1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 23:37:36 +00:00

MenuApplets: Move to Userland/MenuApplets/

This commit is contained in:
Andreas Kling 2021-01-12 12:02:43 +01:00
parent 7fc079bd86
commit b8d6a56fa3
21 changed files with 1 additions and 1 deletions

1
Userland/MenuApplets/Audio/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
Audio.MenuApplet

View file

@ -0,0 +1,6 @@
set(SOURCES
main.cpp
)
serenity_app(Audio.MenuApplet ICON audio-volume-high)
target_link_libraries(Audio.MenuApplet LibGUI LibGfx LibAudio)

View file

@ -0,0 +1,248 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibAudio/ClientConnection.h>
#include <LibGUI/Application.h>
#include <LibGUI/BoxLayout.h>
#include <LibGUI/CheckBox.h>
#include <LibGUI/Label.h>
#include <LibGUI/Painter.h>
#include <LibGUI/Slider.h>
#include <LibGUI/Widget.h>
#include <LibGUI/Window.h>
#include <LibGfx/Bitmap.h>
#include <LibGfx/Font.h>
#include <LibGfx/FontDatabase.h>
#include <LibGfx/Palette.h>
class AudioWidget final : public GUI::Widget {
C_OBJECT(AudioWidget)
public:
AudioWidget()
: m_audio_client(Audio::ClientConnection::construct())
{
m_audio_client->on_muted_state_change = [this](bool muted) {
if (m_audio_muted == muted)
return;
m_mute_box->set_checked(!m_audio_muted);
m_slider->set_enabled(!muted);
m_audio_muted = muted;
update();
};
m_audio_client->on_main_mix_volume_change = [this](int volume) {
m_audio_volume = volume;
if (!m_audio_muted)
update();
};
m_volume_level_bitmaps.append({ 66, Gfx::Bitmap::load_from_file("/res/icons/16x16/audio-volume-high.png") });
m_volume_level_bitmaps.append({ 33, Gfx::Bitmap::load_from_file("/res/icons/16x16/audio-volume-medium.png") });
m_volume_level_bitmaps.append({ 1, Gfx::Bitmap::load_from_file("/res/icons/16x16/audio-volume-low.png") });
m_volume_level_bitmaps.append({ 0, Gfx::Bitmap::load_from_file("/res/icons/16x16/audio-volume-zero.png") });
m_volume_level_bitmaps.append({ 0, Gfx::Bitmap::load_from_file("/res/icons/16x16/audio-volume-muted.png") });
m_slider_window = add<GUI::Window>(window());
m_slider_window->set_frameless(true);
m_slider_window->set_resizable(false);
m_slider_window->set_minimizable(false);
m_slider_window->on_active_input_change = [this](bool is_active_input) {
if (!is_active_input)
close();
};
m_root_container = m_slider_window->set_main_widget<GUI::Label>();
m_root_container->set_fill_with_background_color(true);
m_root_container->set_layout<GUI::VerticalBoxLayout>();
m_root_container->layout()->set_margins({ 0, 4, 0, 4 });
m_root_container->layout()->set_spacing(0);
m_root_container->set_frame_thickness(2);
m_root_container->set_frame_shape(Gfx::FrameShape::Container);
m_root_container->set_frame_shadow(Gfx::FrameShadow::Raised);
m_percent_box = m_root_container->add<GUI::CheckBox>("\xE2\x84\xB9");
m_percent_box->set_fixed_size(27, 16);
m_percent_box->set_checked(false);
m_percent_box->set_tooltip("Show percent");
m_percent_box->on_checked = [&](bool show_percent) {
m_show_percent = show_percent;
if (!m_show_percent) {
window()->resize(16, 16);
m_percent_box->set_tooltip("Show percent");
} else {
window()->resize(44, 16);
m_percent_box->set_tooltip("Hide percent");
}
reposition_slider_window();
GUI::Application::the()->hide_tooltip();
};
m_slider = m_root_container->add<GUI::VerticalSlider>();
m_slider->set_max(20);
m_slider->set_value(0);
m_slider->set_knob_size_mode(GUI::Slider::KnobSizeMode::Proportional);
m_slider->on_change = [&](int value) {
int volume = clamp((20 - value) * 5, 0, 100);
float volume_log = ((volume / 100.0f) * (volume / 100.0f)) * 100.0f;
m_audio_client->set_main_mix_volume(volume_log);
update();
};
m_mute_box = m_root_container->add<GUI::CheckBox>("\xE2\x9D\x8C");
m_mute_box->set_fixed_size(27, 16);
m_mute_box->set_checked(false);
m_mute_box->set_tooltip("Mute");
m_mute_box->on_checked = [&](bool is_muted) {
m_mute_box->set_tooltip(is_muted ? "Unmute" : "Mute");
m_audio_client->set_muted(is_muted);
GUI::Application::the()->hide_tooltip();
};
}
virtual ~AudioWidget() override { }
private:
virtual void mousedown_event(GUI::MouseEvent& event) override
{
if (event.button() == GUI::MouseButton::Left) {
if (!m_slider_window->is_visible())
open();
else
close();
return;
}
if (event.button() == GUI::MouseButton::Right) {
m_audio_client->set_muted(!m_audio_muted);
update();
}
}
virtual void mousewheel_event(GUI::MouseEvent& event) override
{
if (m_audio_muted)
return;
int volume = clamp(m_audio_volume - event.wheel_delta() * 5, 0, 100);
float volume_log = ((volume / 100.0f) * (volume / 100.0f)) * 100.0f;
m_audio_client->set_main_mix_volume(volume_log);
m_slider->set_value(20 - (volume / 5));
update();
}
virtual void paint_event(GUI::PaintEvent& event) override
{
GUI::Painter painter(*this);
painter.add_clip_rect(event.rect());
painter.clear_rect(event.rect(), Color::from_rgba(0));
auto& audio_bitmap = choose_bitmap_from_volume();
painter.blit({}, audio_bitmap, audio_bitmap.rect());
if (m_show_percent) {
auto volume_text = m_audio_muted ? "mute" : String::format("%d%%", m_audio_volume);
painter.draw_text({ 16, 3, 24, 16 }, volume_text, Gfx::FontDatabase::default_fixed_width_font(), Gfx::TextAlignment::TopLeft, palette().window_text());
}
}
void open()
{
reposition_slider_window();
m_slider_window->show();
}
void close()
{
m_slider_window->hide();
}
Gfx::Bitmap& choose_bitmap_from_volume()
{
if (m_audio_muted)
return *m_volume_level_bitmaps.last().bitmap;
for (auto& pair : m_volume_level_bitmaps) {
if (m_audio_volume >= pair.volume_threshold)
return *pair.bitmap;
}
ASSERT_NOT_REACHED();
}
void reposition_slider_window() { m_slider_window->set_rect(window()->rect_in_menubar().x() - 20, 19, 50, 100); }
struct VolumeBitmapPair {
int volume_threshold { 0 };
RefPtr<Gfx::Bitmap> bitmap;
};
NonnullRefPtr<Audio::ClientConnection> m_audio_client;
Vector<VolumeBitmapPair, 5> m_volume_level_bitmaps;
bool m_show_percent { false };
bool m_audio_muted { false };
int m_audio_volume { 100 };
RefPtr<GUI::Slider> m_slider;
RefPtr<GUI::Window> m_slider_window;
RefPtr<GUI::CheckBox> m_mute_box;
RefPtr<GUI::CheckBox> m_percent_box;
RefPtr<GUI::Label> m_root_container;
};
int main(int argc, char** argv)
{
if (pledge("stdio shared_buffer accept rpath unix cpath fattr", nullptr) < 0) {
perror("pledge");
return 1;
}
auto app = GUI::Application::construct(argc, argv);
if (pledge("stdio shared_buffer accept rpath unix", nullptr) < 0) {
perror("pledge");
return 1;
}
auto window = GUI::Window::construct();
window->set_has_alpha_channel(true);
window->set_title("Audio");
window->set_window_type(GUI::WindowType::MenuApplet);
window->resize(16, 16);
window->set_main_widget<AudioWidget>();
window->show();
if (unveil("/res", "r") < 0) {
perror("unveil");
return 1;
}
unveil(nullptr, nullptr);
if (pledge("stdio shared_buffer accept rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
return app->exec();
}

View file

@ -0,0 +1,6 @@
add_subdirectory(Audio)
add_subdirectory(ClipboardHistory)
add_subdirectory(Clock)
add_subdirectory(Network)
add_subdirectory(ResourceGraph)
add_subdirectory(UserName)

View file

@ -0,0 +1,7 @@
set(SOURCES
ClipboardHistoryModel.cpp
main.cpp
)
serenity_app(ClipboardHistory.MenuApplet ICON clipboard)
target_link_libraries(ClipboardHistory.MenuApplet LibGUI LibCore LibGfx)

View file

@ -0,0 +1,129 @@
/*
* Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "ClipboardHistoryModel.h"
#include <AK/NumberFormat.h>
#include <AK/StringBuilder.h>
NonnullRefPtr<ClipboardHistoryModel> ClipboardHistoryModel::create()
{
return adopt(*new ClipboardHistoryModel());
}
ClipboardHistoryModel::~ClipboardHistoryModel()
{
}
String ClipboardHistoryModel::column_name(int column) const
{
switch (column) {
case Column::Data:
return "Data";
case Column::Type:
return "Type";
case Column::Size:
return "Size";
default:
ASSERT_NOT_REACHED();
}
}
static const char* bpp_for_format_resilient(String format)
{
unsigned format_uint = format.to_uint().value_or(static_cast<unsigned>(Gfx::BitmapFormat::Invalid));
// Cannot use Gfx::Bitmap::bpp_for_format here, as we have to accept invalid enum values.
switch (static_cast<Gfx::BitmapFormat>(format_uint)) {
case Gfx::BitmapFormat::Indexed1:
return "1";
case Gfx::BitmapFormat::Indexed2:
return "2";
case Gfx::BitmapFormat::Indexed4:
return "4";
case Gfx::BitmapFormat::Indexed8:
return "8";
case Gfx::BitmapFormat::RGB32:
case Gfx::BitmapFormat::RGBA32:
return "32";
case Gfx::BitmapFormat::Invalid:
/* fall-through */
default:
return "?";
}
}
GUI::Variant ClipboardHistoryModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const
{
if (role != GUI::ModelRole::Display)
return {};
auto& data_and_type = m_history_items[index.row()];
switch (index.column()) {
case Column::Data:
if (data_and_type.mime_type.starts_with("text/"))
return String::copy(data_and_type.data);
if (data_and_type.mime_type == "image/x-serenityos") {
StringBuilder builder;
builder.append("[");
builder.append(data_and_type.metadata.get("width").value_or("?"));
builder.append('x');
builder.append(data_and_type.metadata.get("height").value_or("?"));
builder.append('x');
builder.append(bpp_for_format_resilient(data_and_type.metadata.get("height").value_or("0")));
builder.append(" bitmap");
builder.append("]");
return builder.to_string();
}
return "<...>";
case Column::Type:
return data_and_type.mime_type;
case Column::Size:
return AK::human_readable_size(data_and_type.data.size());
default:
ASSERT_NOT_REACHED();
}
}
void ClipboardHistoryModel::update()
{
did_update();
}
void ClipboardHistoryModel::add_item(const GUI::Clipboard::DataAndType& item)
{
m_history_items.remove_first_matching([&](GUI::Clipboard::DataAndType& existing) {
return existing.data == item.data && existing.mime_type == item.mime_type;
});
if (m_history_items.size() == m_history_limit)
m_history_items.take_last();
m_history_items.prepend(item);
update();
}
void ClipboardHistoryModel::remove_item(int index)
{
m_history_items.remove(index);
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <AK/Vector.h>
#include <LibGUI/Clipboard.h>
#include <LibGUI/Model.h>
class ClipboardHistoryModel final : public GUI::Model {
public:
static NonnullRefPtr<ClipboardHistoryModel> create();
enum Column {
Data,
Type,
Size,
__Count
};
virtual ~ClipboardHistoryModel() override;
const GUI::Clipboard::DataAndType& item_at(int index) const { return m_history_items[index]; }
void add_item(const GUI::Clipboard::DataAndType& item);
void remove_item(int index);
private:
virtual int row_count(const GUI::ModelIndex&) const override { return m_history_items.size(); }
virtual String column_name(int) const override;
virtual int column_count(const GUI::ModelIndex&) const override { return Column::__Count; }
virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override;
virtual void update() override;
Vector<GUI::Clipboard::DataAndType> m_history_items;
size_t m_history_limit { 20 };
};

View file

@ -0,0 +1,106 @@
/*
* Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "ClipboardHistoryModel.h"
#include <LibGUI/Action.h>
#include <LibGUI/Application.h>
#include <LibGUI/ImageWidget.h>
#include <LibGUI/Menu.h>
#include <LibGUI/TableView.h>
#include <LibGUI/Window.h>
#include <stdio.h>
int main(int argc, char* argv[])
{
if (pledge("stdio shared_buffer accept rpath unix cpath fattr", nullptr) < 0) {
perror("pledge");
return 1;
}
auto app = GUI::Application::construct(argc, argv);
if (pledge("stdio shared_buffer accept rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
if (unveil("/res", "r") < 0) {
perror("unveil");
return 1;
}
if (unveil(nullptr, nullptr) < 0) {
perror("unveil");
return 1;
}
auto app_icon = GUI::Icon::default_icon("clipboard");
auto main_window = GUI::Window::construct();
main_window->set_title("Clipboard history");
main_window->set_rect(670, 65, 325, 500);
main_window->set_icon(app_icon.bitmap_for_size(16));
auto& table_view = main_window->set_main_widget<GUI::TableView>();
auto model = ClipboardHistoryModel::create();
table_view.set_model(model);
GUI::Clipboard::the().on_change = [&](const String&) {
auto item = GUI::Clipboard::the().data_and_type();
model->add_item(item);
};
table_view.on_activation = [&](const GUI::ModelIndex& index) {
auto& data_and_type = model->item_at(index.row());
GUI::Clipboard::the().set_data(data_and_type.data, data_and_type.mime_type, data_and_type.metadata);
};
auto delete_action = GUI::CommonActions::make_delete_action([&](const GUI::Action&) {
model->remove_item(table_view.selection().first().row());
});
auto entry_context_menu = GUI::Menu::construct();
entry_context_menu->add_action(delete_action);
table_view.on_context_menu_request = [&](const GUI::ModelIndex&, const GUI::ContextMenuEvent& event) {
delete_action->set_enabled(!table_view.selection().is_empty());
entry_context_menu->popup(event.screen_position());
};
auto applet_window = GUI::Window::construct();
applet_window->set_title("ClipboardHistory");
applet_window->set_window_type(GUI::WindowType::MenuApplet);
auto& icon = applet_window->set_main_widget<GUI::ImageWidget>();
icon.load_from_file("/res/icons/16x16/clipboard.png");
icon.set_fill_with_background_color(true);
icon.on_click = [&main_window = *main_window] {
main_window.show();
main_window.move_to_front();
};
applet_window->resize(16, 16);
applet_window->show();
return app->exec();
}

1
Userland/MenuApplets/Clock/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
Clock.MenuApplet

View file

@ -0,0 +1,6 @@
set(SOURCES
main.cpp
)
serenity_bin(Clock.MenuApplet)
target_link_libraries(Clock.MenuApplet LibGUI LibCore LibGfx)

View file

@ -0,0 +1,317 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/DateTime.h>
#include <LibCore/Timer.h>
#include <LibGUI/Application.h>
#include <LibGUI/BoxLayout.h>
#include <LibGUI/Button.h>
#include <LibGUI/Calendar.h>
#include <LibGUI/Frame.h>
#include <LibGUI/Label.h>
#include <LibGUI/Painter.h>
#include <LibGUI/Widget.h>
#include <LibGUI/Window.h>
#include <LibGfx/Font.h>
#include <LibGfx/FontDatabase.h>
#include <LibGfx/Palette.h>
#include <serenity.h>
#include <spawn.h>
#include <stdio.h>
#include <time.h>
class ClockWidget final : public GUI::Widget {
C_OBJECT(ClockWidget)
public:
ClockWidget()
{
m_time_width = Gfx::FontDatabase::default_bold_font().width("2222-22-22 22:22:22");
m_timer = add<Core::Timer>(1000, [this] {
static time_t last_update_time;
time_t now = time(nullptr);
if (now != last_update_time) {
tick_clock();
last_update_time = now;
}
});
m_calendar_window = add<GUI::Window>(window());
m_calendar_window->set_frameless(true);
m_calendar_window->set_resizable(false);
m_calendar_window->set_minimizable(false);
m_calendar_window->on_active_input_change = [this](bool is_active_input) {
if (!is_active_input)
close();
};
auto& root_container = m_calendar_window->set_main_widget<GUI::Label>();
root_container.set_fill_with_background_color(true);
root_container.set_layout<GUI::VerticalBoxLayout>();
root_container.layout()->set_margins({ 0, 2, 0, 2 });
root_container.layout()->set_spacing(0);
root_container.set_frame_thickness(2);
root_container.set_frame_shape(Gfx::FrameShape::Container);
root_container.set_frame_shadow(Gfx::FrameShadow::Raised);
auto& navigation_container = root_container.add<GUI::Widget>();
navigation_container.set_fixed_height(24);
navigation_container.set_layout<GUI::HorizontalBoxLayout>();
navigation_container.layout()->set_margins({ 2, 2, 3, 2 });
m_prev_date = navigation_container.add<GUI::Button>();
m_prev_date->set_button_style(Gfx::ButtonStyle::CoolBar);
m_prev_date->set_fixed_size(24, 24);
m_prev_date->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/go-back.png"));
m_prev_date->on_click = [&](auto) {
unsigned int target_month = m_calendar->selected_month();
unsigned int target_year = m_calendar->selected_year();
if (m_calendar->mode() == GUI::Calendar::Month) {
target_month--;
if (m_calendar->selected_month() <= 1) {
target_month = 12;
target_year--;
}
} else {
target_year--;
}
m_calendar->update_tiles(target_year, target_month);
m_selected_calendar_button->set_text(m_calendar->selected_calendar_text(GUI::Calendar::LongNames));
};
m_selected_calendar_button = navigation_container.add<GUI::Button>();
m_selected_calendar_button->set_button_style(Gfx::ButtonStyle::CoolBar);
m_selected_calendar_button->set_fixed_height(24);
m_selected_calendar_button->on_click = [&](auto) {
m_calendar->toggle_mode();
m_selected_calendar_button->set_text(m_calendar->selected_calendar_text(GUI::Calendar::LongNames));
};
m_next_date = navigation_container.add<GUI::Button>();
m_next_date->set_button_style(Gfx::ButtonStyle::CoolBar);
m_next_date->set_fixed_size(24, 24);
m_next_date->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"));
m_next_date->on_click = [&](auto) {
unsigned int target_month = m_calendar->selected_month();
unsigned int target_year = m_calendar->selected_year();
if (m_calendar->mode() == GUI::Calendar::Month) {
target_month++;
if (m_calendar->selected_month() >= 12) {
target_month = 1;
target_year++;
}
} else {
target_year++;
}
m_calendar->update_tiles(target_year, target_month);
m_selected_calendar_button->set_text(m_calendar->selected_calendar_text(GUI::Calendar::LongNames));
};
auto& divider1_container = root_container.add<GUI::Widget>();
divider1_container.set_fixed_height(2);
divider1_container.set_layout<GUI::HorizontalBoxLayout>();
divider1_container.layout()->set_margins({ 2, 0, 3, 0 });
auto& divider1 = divider1_container.add<GUI::Frame>();
divider1.set_fixed_height(2);
divider1.set_frame_shape(Gfx::FrameShape::Panel);
auto& calendar_frame_container = root_container.add<GUI::Widget>();
calendar_frame_container.set_layout<GUI::HorizontalBoxLayout>();
calendar_frame_container.layout()->set_margins({ 4, 4, 5, 4 });
auto& calendar_frame = calendar_frame_container.add<GUI::Frame>();
calendar_frame.set_layout<GUI::VerticalBoxLayout>();
calendar_frame.layout()->set_margins({ 2, 2, 2, 2 });
m_calendar = calendar_frame.add<GUI::Calendar>(Core::DateTime::now());
m_selected_calendar_button->set_text(m_calendar->selected_calendar_text(GUI::Calendar::LongNames));
m_calendar->on_calendar_tile_click = [&] {
m_selected_calendar_button->set_text(m_calendar->selected_calendar_text(GUI::Calendar::LongNames));
};
m_calendar->on_month_tile_click = [&] {
m_selected_calendar_button->set_text(m_calendar->selected_calendar_text(GUI::Calendar::LongNames));
};
auto& divider2_container = root_container.add<GUI::Widget>();
divider2_container.set_fixed_height(2);
divider2_container.set_layout<GUI::HorizontalBoxLayout>();
divider2_container.layout()->set_margins({ 2, 0, 3, 0 });
auto& divider2 = divider2_container.add<GUI::Frame>();
divider2.set_fixed_height(2);
divider2.set_frame_shape(Gfx::FrameShape::Panel);
auto& settings_container = root_container.add<GUI::Widget>();
settings_container.set_fixed_height(24);
settings_container.set_layout<GUI::HorizontalBoxLayout>();
settings_container.layout()->set_margins({ 2, 2, 3, 2 });
settings_container.layout()->add_spacer();
m_jump_to_button = settings_container.add<GUI::Button>();
m_jump_to_button->set_button_style(Gfx::ButtonStyle::CoolBar);
m_jump_to_button->set_fixed_size(24, 24);
m_jump_to_button->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/calendar-date.png"));
m_jump_to_button->set_tooltip("Jump to today");
m_jump_to_button->on_click = [this](auto) {
jump_to_current_date();
};
m_calendar_launcher = settings_container.add<GUI::Button>();
m_calendar_launcher->set_button_style(Gfx::ButtonStyle::CoolBar);
m_calendar_launcher->set_fixed_size(24, 24);
m_calendar_launcher->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-calendar.png"));
m_calendar_launcher->set_tooltip("Calendar");
m_calendar_launcher->on_click = [](auto) {
pid_t pid;
const char* argv[] = { "Calendar", nullptr };
if ((errno = posix_spawn(&pid, "/bin/Calendar", nullptr, nullptr, const_cast<char**>(argv), environ))) {
perror("posix_spawn");
} else {
if (disown(pid) < 0)
perror("disown");
}
};
}
virtual ~ClockWidget() override { }
int get_width()
{
return m_time_width + menubar_menu_margin();
}
private:
static int menubar_menu_margin() { return 2; }
virtual void paint_event(GUI::PaintEvent& event) override
{
auto time_text = Core::DateTime::now().to_string();
GUI::Painter painter(*this);
painter.fill_rect(event.rect(), palette().window());
painter.draw_text(event.rect(), time_text, Gfx::FontDatabase::default_font(), Gfx::TextAlignment::Center, palette().window_text());
}
virtual void mousedown_event(GUI::MouseEvent& event) override
{
if (event.button() != GUI::MouseButton::Left) {
return;
} else {
if (!m_calendar_window->is_visible())
open();
else
close();
}
}
void tick_clock()
{
update();
}
void open()
{
position_calendar_window();
jump_to_current_date();
m_calendar_window->show();
}
void close()
{
m_calendar_window->hide();
}
void position_calendar_window()
{
m_calendar_window->set_rect(
window()->rect_in_menubar().x() - ((m_calendar_window->rect().width() - window()->rect().width()) / 2),
19,
153,
180);
}
void jump_to_current_date()
{
if (m_calendar->mode() == GUI::Calendar::Year)
m_calendar->toggle_mode();
m_calendar->set_selected_date(Core::DateTime::now());
m_calendar->update_tiles(Core::DateTime::now().year(), Core::DateTime::now().month());
m_selected_calendar_button->set_text(m_calendar->selected_calendar_text(GUI::Calendar::LongNames));
}
RefPtr<GUI::Window> m_calendar_window;
RefPtr<GUI::Calendar> m_calendar;
RefPtr<GUI::Button> m_next_date;
RefPtr<GUI::Button> m_prev_date;
RefPtr<GUI::Button> m_selected_calendar_button;
RefPtr<GUI::Button> m_jump_to_button;
RefPtr<GUI::Button> m_calendar_launcher;
RefPtr<Core::Timer> m_timer;
int m_time_width;
};
int main(int argc, char** argv)
{
if (pledge("stdio shared_buffer accept rpath unix cpath fattr exec proc", nullptr) < 0) {
perror("pledge");
return 1;
}
auto app = GUI::Application::construct(argc, argv);
if (pledge("stdio shared_buffer accept rpath exec proc", nullptr) < 0) {
perror("pledge");
return 1;
}
auto window = GUI::Window::construct();
window->set_title("Clock");
window->set_window_type(GUI::WindowType::MenuApplet);
auto& widget = window->set_main_widget<ClockWidget>();
window->resize(widget.get_width(), 16);
window->show();
if (unveil("/res", "r") < 0) {
perror("unveil");
return 1;
}
if (unveil("/bin/Calendar", "x") < 0) {
perror("unveil");
return 1;
}
unveil(nullptr, nullptr);
return app->exec();
}

View file

@ -0,0 +1,6 @@
set(SOURCES
main.cpp
)
serenity_app(Network.MenuApplet ICON network)
target_link_libraries(Network.MenuApplet LibGUI LibCore LibGfx)

View file

@ -0,0 +1,230 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/ArgsParser.h>
#include <LibCore/File.h>
#include <LibGUI/Action.h>
#include <LibGUI/Application.h>
#include <LibGUI/ImageWidget.h>
#include <LibGUI/Menu.h>
#include <LibGUI/Notification.h>
#include <LibGUI/Window.h>
#include <LibGfx/Bitmap.h>
#include <serenity.h>
#include <spawn.h>
#include <stdio.h>
class NetworkWidget final : public GUI::ImageWidget {
C_OBJECT(NetworkWidget);
public:
NetworkWidget(bool notifications)
{
m_notifications = notifications;
update_widget();
start_timer(5000);
}
private:
virtual void timer_event(Core::TimerEvent&) override
{
update_widget();
}
virtual void mousedown_event(GUI::MouseEvent& event) override
{
if (event.button() != GUI::MouseButton::Left)
return;
pid_t child_pid;
const char* argv[] = { "SystemMonitor", "-t", "network", nullptr };
if ((errno = posix_spawn(&child_pid, "/bin/SystemMonitor", nullptr, nullptr, const_cast<char**>(argv), environ))) {
perror("posix_spawn");
return;
}
if (disown(child_pid) < 0)
perror("disown");
}
virtual void update_widget()
{
auto adapter_info = get_adapter_info();
if (adapter_info == "") {
set_connected(false);
m_adapter_info = "No network adapters";
} else {
m_adapter_info = adapter_info;
}
set_tooltip(m_adapter_info);
if (m_connected)
NetworkWidget::set_bitmap(m_connected_icon);
else
NetworkWidget::set_bitmap(m_disconnected_icon);
update();
}
virtual void notify_on_connect()
{
if (!m_notifications)
return;
auto notification = GUI::Notification::construct();
notification->set_title("Network");
notification->set_icon(m_connected_icon);
notification->set_text("Network connected");
notification->show();
}
virtual void notify_on_disconnect()
{
if (!m_notifications)
return;
auto notification = GUI::Notification::construct();
notification->set_title("Network");
notification->set_icon(m_disconnected_icon);
notification->set_text("Network disconnected");
notification->show();
}
virtual void set_connected(bool connected)
{
if (m_connected != connected) {
connected ? notify_on_connect() : notify_on_disconnect();
}
m_connected = connected;
}
virtual String get_adapter_info(bool include_loopback = false)
{
StringBuilder adapter_info;
auto file = Core::File::construct("/proc/net/adapters");
if (!file->open(Core::IODevice::ReadOnly)) {
fprintf(stderr, "Error: %s\n", file->error_string());
return adapter_info.to_string();
}
auto file_contents = file->read_all();
auto json = JsonValue::from_string(file_contents);
if (!json.has_value())
return adapter_info.to_string();
int connected_adapters = 0;
json.value().as_array().for_each([&adapter_info, include_loopback, &connected_adapters](auto& value) {
auto if_object = value.as_object();
auto ip_address = if_object.get("ipv4_address").to_string();
auto ifname = if_object.get("name").to_string();
if (!include_loopback)
if (ifname == "loop0")
return;
if (ip_address != "null")
connected_adapters++;
adapter_info.appendf("%s: %s\n", ifname.characters(), ip_address.characters());
});
// show connected icon so long as at least one adapter is connected
connected_adapters ? set_connected(true) : set_connected(false);
return adapter_info.to_string();
}
String m_adapter_info;
bool m_connected = false;
bool m_notifications = true;
RefPtr<Gfx::Bitmap> m_connected_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/network.png");
RefPtr<Gfx::Bitmap> m_disconnected_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/network-disconnected.png");
};
int main(int argc, char* argv[])
{
if (pledge("stdio shared_buffer accept rpath unix cpath fattr unix proc exec", nullptr) < 0) {
perror("pledge");
return 1;
}
auto app = GUI::Application::construct(argc, argv);
if (pledge("stdio shared_buffer accept rpath unix proc exec", nullptr) < 0) {
perror("pledge");
return 1;
}
if (unveil("/res", "r") < 0) {
perror("unveil");
return 1;
}
if (unveil("/tmp/portal/notify", "rw") < 0) {
perror("unveil");
return 1;
}
if (unveil("/proc/net/adapters", "r") < 0) {
perror("unveil");
return 1;
}
if (unveil("/bin/SystemMonitor", "x") < 0) {
perror("unveil");
return 1;
}
if (unveil(nullptr, nullptr) < 0) {
perror("unveil");
return 1;
}
bool display_notifications = false;
const char* name = nullptr;
Core::ArgsParser args_parser;
args_parser.add_option(display_notifications, "Display notifications", "display-notifications", 'd');
args_parser.add_option(name, "Applet name used by WindowServer.ini to set the applet order", "name", 'n', "name");
args_parser.parse(argc, argv);
if (name == nullptr)
name = "Network";
auto window = GUI::Window::construct();
window->set_title(name);
window->set_window_type(GUI::WindowType::MenuApplet);
window->resize(16, 16);
auto& icon = window->set_main_widget<NetworkWidget>(display_notifications);
icon.set_fill_with_background_color(true);
icon.load_from_file("/res/icons/16x16/network.png");
window->resize(16, 16);
window->show();
return app->exec();
}

View file

@ -0,0 +1 @@
ResourceGraph.MenuApplet

View file

@ -0,0 +1,6 @@
set(SOURCES
main.cpp
)
serenity_bin(ResourceGraph.MenuApplet)
target_link_libraries(ResourceGraph.MenuApplet LibGUI LibCore LibGfx)

View file

@ -0,0 +1,292 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/ByteBuffer.h>
#include <AK/CircularQueue.h>
#include <AK/JsonObject.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/File.h>
#include <LibCore/ProcessStatisticsReader.h>
#include <LibGUI/Application.h>
#include <LibGUI/Frame.h>
#include <LibGUI/Painter.h>
#include <LibGUI/Window.h>
#include <LibGfx/Palette.h>
#include <serenity.h>
#include <spawn.h>
#include <stdio.h>
enum class GraphType {
CPU,
Memory,
};
class GraphWidget final : public GUI::Frame {
C_OBJECT(GraphWidget);
public:
static constexpr size_t history_size = 30;
GraphWidget(GraphType graph_type, Optional<Gfx::Color> graph_color, Optional<Gfx::Color> graph_error_color)
: m_graph_type(graph_type)
{
set_frame_thickness(1);
m_graph_color = graph_color.value_or(palette().menu_selection());
m_graph_error_color = graph_error_color.value_or(Color::Red);
start_timer(1000);
}
private:
virtual void timer_event(Core::TimerEvent&) override
{
switch (m_graph_type) {
case GraphType::CPU: {
unsigned busy;
unsigned idle;
if (get_cpu_usage(busy, idle)) {
unsigned busy_diff = busy - m_last_cpu_busy;
unsigned idle_diff = idle - m_last_cpu_idle;
m_last_cpu_busy = busy;
m_last_cpu_idle = idle;
float cpu = (float)busy_diff / (float)(busy_diff + idle_diff);
m_history.enqueue(cpu);
m_tooltip = String::format("CPU usage: %.1f%%", 100 * cpu);
} else {
m_history.enqueue(-1);
m_tooltip = StringView("Unable to determine CPU usage");
}
break;
}
case GraphType::Memory: {
u64 allocated, available;
if (get_memory_usage(allocated, available)) {
double total_memory = allocated + available;
double memory = (double)allocated / total_memory;
m_history.enqueue(memory);
m_tooltip = String::format("Memory: %.1f MiB of %.1f MiB in use", (float)(allocated / MiB), (float)(total_memory / MiB));
} else {
m_history.enqueue(-1);
m_tooltip = StringView("Unable to determine memory usage");
}
break;
}
default:
ASSERT_NOT_REACHED();
}
set_tooltip(m_tooltip);
update();
}
virtual void paint_event(GUI::PaintEvent& event) override
{
GUI::Frame::paint_event(event);
GUI::Painter painter(*this);
painter.add_clip_rect(event.rect());
painter.add_clip_rect(frame_inner_rect());
painter.fill_rect(event.rect(), Color::Black);
int i = m_history.capacity() - m_history.size();
auto rect = frame_inner_rect();
for (auto value : m_history) {
if (value >= 0) {
painter.draw_line(
{ rect.x() + i, rect.bottom() },
{ rect.x() + i, rect.top() + (int)(round(rect.height() - (value * rect.height()))) },
m_graph_color);
} else {
painter.draw_line(
{ rect.x() + i, rect.top() },
{ rect.x() + i, rect.bottom() },
m_graph_error_color);
}
++i;
}
}
virtual void mousedown_event(GUI::MouseEvent& event) override
{
if (event.button() != GUI::MouseButton::Left)
return;
pid_t child_pid;
const char* argv[] = { "SystemMonitor", "-t", "graphs", nullptr };
if ((errno = posix_spawn(&child_pid, "/bin/SystemMonitor", nullptr, nullptr, const_cast<char**>(argv), environ))) {
perror("posix_spawn");
} else {
if (disown(child_pid) < 0)
perror("disown");
}
}
bool get_cpu_usage(unsigned& busy, unsigned& idle)
{
busy = 0;
idle = 0;
auto all_processes = Core::ProcessStatisticsReader::get_all(m_proc_all);
if (!all_processes.has_value() || all_processes.value().is_empty())
return false;
for (auto& it : all_processes.value()) {
for (auto& jt : it.value.threads) {
if (it.value.pid == 0)
idle += jt.ticks_user + jt.ticks_kernel;
else
busy += jt.ticks_user + jt.ticks_kernel;
}
}
return true;
}
bool get_memory_usage(u64& allocated, u64& available)
{
if (m_proc_mem) {
// Seeking to the beginning causes a data refresh!
if (!m_proc_mem->seek(0, Core::File::SeekMode::SetPosition))
return false;
} else {
auto proc_memstat = Core::File::construct("/proc/memstat");
if (!proc_memstat->open(Core::IODevice::OpenMode::ReadOnly))
return false;
m_proc_mem = move(proc_memstat);
}
auto file_contents = m_proc_mem->read_all();
auto json = JsonValue::from_string(file_contents);
ASSERT(json.has_value());
auto& obj = json.value().as_object();
unsigned kmalloc_allocated = obj.get("kmalloc_allocated").to_u32();
unsigned kmalloc_available = obj.get("kmalloc_available").to_u32();
unsigned user_physical_allocated = obj.get("user_physical_allocated").to_u32();
unsigned user_physical_committed = obj.get("user_physical_committed").to_u32();
unsigned user_physical_uncommitted = obj.get("user_physical_uncommitted").to_u32();
unsigned kmalloc_bytes_total = kmalloc_allocated + kmalloc_available;
unsigned kmalloc_pages_total = (kmalloc_bytes_total + PAGE_SIZE - 1) / PAGE_SIZE;
unsigned total_userphysical_and_swappable_pages = kmalloc_pages_total + user_physical_allocated + user_physical_committed + user_physical_uncommitted;
allocated = kmalloc_allocated + ((u64)(user_physical_allocated + user_physical_committed) * PAGE_SIZE);
available = (u64)(total_userphysical_and_swappable_pages * PAGE_SIZE) - allocated;
return true;
}
GraphType m_graph_type;
Gfx::Color m_graph_color;
Gfx::Color m_graph_error_color;
CircularQueue<float, history_size> m_history;
unsigned m_last_cpu_busy { 0 };
unsigned m_last_cpu_idle { 0 };
String m_tooltip;
RefPtr<Core::File> m_proc_all;
RefPtr<Core::File> m_proc_mem;
};
int main(int argc, char** argv)
{
if (pledge("stdio shared_buffer accept proc exec rpath unix cpath fattr", nullptr) < 0) {
perror("pledge");
return 1;
}
auto app = GUI::Application::construct(argc, argv);
if (pledge("stdio shared_buffer accept proc exec rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
bool cpu = false;
bool memory = false;
const char* name = nullptr;
const char* color = nullptr;
const char* error_color = nullptr;
Core::ArgsParser args_parser;
args_parser.add_option(cpu, "Show CPU usage", "cpu", 'C');
args_parser.add_option(memory, "Show memory usage", "memory", 'M');
args_parser.add_option(name, "Applet name used by WindowServer.ini to set the applet order", "name", 'n', "name");
args_parser.add_option(color, "Graph color", "color", 'c', "color");
args_parser.add_option(error_color, "Graph color (error)", "error-color", 'e', "error-color");
args_parser.parse(argc, argv);
if (!cpu && !memory) {
printf("Either --cpu or --memory option must be used");
return 1;
}
if (cpu && memory) {
printf("--cpu and --memory options must not be used together");
return 1;
}
GraphType graph_type;
if (cpu)
graph_type = GraphType::CPU;
if (memory)
graph_type = GraphType::Memory;
if (name == nullptr)
name = "ResourceGraph";
Optional<Gfx::Color> graph_color, graph_error_color;
if (color != nullptr)
graph_color = Gfx::Color::from_string(color);
if (error_color != nullptr)
graph_error_color = Gfx::Color::from_string(error_color);
auto window = GUI::Window::construct();
window->set_title(name);
window->set_window_type(GUI::WindowType::MenuApplet);
window->resize(GraphWidget::history_size + 2, 16);
window->set_main_widget<GraphWidget>(graph_type, graph_color, graph_error_color);
window->show();
if (unveil("/res", "r") < 0) {
perror("unveil");
return 1;
}
// FIXME: This is required by Core::ProcessStatisticsReader.
// It would be good if we didn't depend on that.
if (unveil("/etc/passwd", "r") < 0) {
perror("unveil");
return 1;
}
if (unveil("/proc/all", "r") < 0) {
perror("unveil");
return 1;
}
if (unveil("/proc/memstat", "r") < 0) {
perror("unveil");
return 1;
}
if (unveil("/bin/SystemMonitor", "x") < 0) {
perror("unveil");
return 1;
}
unveil(nullptr, nullptr);
return app->exec();
}

View file

@ -0,0 +1 @@
UserName.MenuApplet

View file

@ -0,0 +1,6 @@
set(SOURCES
main.cpp
)
serenity_bin(UserName.MenuApplet)
target_link_libraries(UserName.MenuApplet LibGUI LibGfx)

View file

@ -0,0 +1,101 @@
/*
* Copyright (c) 2020, Hüseyin Aslıtürk <asliturk@hotmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibGUI/Application.h>
#include <LibGUI/Painter.h>
#include <LibGUI/Widget.h>
#include <LibGUI/Window.h>
#include <LibGfx/Font.h>
#include <LibGfx/FontDatabase.h>
#include <LibGfx/Palette.h>
#include <stdio.h>
class UserNameWidget final : public GUI::Widget {
C_OBJECT(UserNameWidget)
public:
UserNameWidget()
{
m_username = getlogin();
m_username_width = Gfx::FontDatabase::default_bold_font().width(m_username);
}
virtual ~UserNameWidget() override { }
int get_width()
{
return m_username_width + menubar_menu_margin();
}
private:
static int menubar_menu_margin() { return 4; }
virtual void paint_event(GUI::PaintEvent& event) override
{
GUI::Painter painter(*this);
painter.fill_rect(event.rect(), palette().window());
painter.draw_text(event.rect(), m_username, Gfx::FontDatabase::default_bold_font(), Gfx::TextAlignment::Center, palette().window_text());
}
String m_username;
int m_username_width;
};
int main(int argc, char** argv)
{
if (pledge("stdio shared_buffer rpath cpath unix fattr", nullptr) < 0) {
perror("pledge");
return 1;
}
auto app = GUI::Application::construct(argc, argv);
if (pledge("stdio shared_buffer rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
if (unveil("/res", "r") < 0) {
perror("unveil");
return 1;
}
if (unveil("/etc/passwd", "r") < 0) {
perror("unveil");
return 1;
}
unveil(nullptr, nullptr);
auto window = GUI::Window::construct();
window->set_title("UserName");
window->set_window_type(GUI::WindowType::MenuApplet);
auto& widget = window->set_main_widget<UserNameWidget>();
window->resize(widget.get_width(), 16);
window->show();
return app->exec();
}