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:
parent
7fc079bd86
commit
b8d6a56fa3
21 changed files with 1 additions and 1 deletions
1
Userland/MenuApplets/Audio/.gitignore
vendored
Normal file
1
Userland/MenuApplets/Audio/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
Audio.MenuApplet
|
6
Userland/MenuApplets/Audio/CMakeLists.txt
Normal file
6
Userland/MenuApplets/Audio/CMakeLists.txt
Normal 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)
|
248
Userland/MenuApplets/Audio/main.cpp
Normal file
248
Userland/MenuApplets/Audio/main.cpp
Normal 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();
|
||||
}
|
6
Userland/MenuApplets/CMakeLists.txt
Normal file
6
Userland/MenuApplets/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
add_subdirectory(Audio)
|
||||
add_subdirectory(ClipboardHistory)
|
||||
add_subdirectory(Clock)
|
||||
add_subdirectory(Network)
|
||||
add_subdirectory(ResourceGraph)
|
||||
add_subdirectory(UserName)
|
7
Userland/MenuApplets/ClipboardHistory/CMakeLists.txt
Normal file
7
Userland/MenuApplets/ClipboardHistory/CMakeLists.txt
Normal 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)
|
129
Userland/MenuApplets/ClipboardHistory/ClipboardHistoryModel.cpp
Normal file
129
Userland/MenuApplets/ClipboardHistory/ClipboardHistoryModel.cpp
Normal 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);
|
||||
}
|
|
@ -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 };
|
||||
};
|
106
Userland/MenuApplets/ClipboardHistory/main.cpp
Normal file
106
Userland/MenuApplets/ClipboardHistory/main.cpp
Normal 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
1
Userland/MenuApplets/Clock/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
Clock.MenuApplet
|
6
Userland/MenuApplets/Clock/CMakeLists.txt
Normal file
6
Userland/MenuApplets/Clock/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
set(SOURCES
|
||||
main.cpp
|
||||
)
|
||||
|
||||
serenity_bin(Clock.MenuApplet)
|
||||
target_link_libraries(Clock.MenuApplet LibGUI LibCore LibGfx)
|
317
Userland/MenuApplets/Clock/main.cpp
Normal file
317
Userland/MenuApplets/Clock/main.cpp
Normal 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();
|
||||
}
|
6
Userland/MenuApplets/Network/CMakeLists.txt
Normal file
6
Userland/MenuApplets/Network/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
set(SOURCES
|
||||
main.cpp
|
||||
)
|
||||
|
||||
serenity_app(Network.MenuApplet ICON network)
|
||||
target_link_libraries(Network.MenuApplet LibGUI LibCore LibGfx)
|
230
Userland/MenuApplets/Network/main.cpp
Normal file
230
Userland/MenuApplets/Network/main.cpp
Normal 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();
|
||||
}
|
1
Userland/MenuApplets/ResourceGraph/.gitignore
vendored
Normal file
1
Userland/MenuApplets/ResourceGraph/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
ResourceGraph.MenuApplet
|
6
Userland/MenuApplets/ResourceGraph/CMakeLists.txt
Normal file
6
Userland/MenuApplets/ResourceGraph/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
set(SOURCES
|
||||
main.cpp
|
||||
)
|
||||
|
||||
serenity_bin(ResourceGraph.MenuApplet)
|
||||
target_link_libraries(ResourceGraph.MenuApplet LibGUI LibCore LibGfx)
|
292
Userland/MenuApplets/ResourceGraph/main.cpp
Normal file
292
Userland/MenuApplets/ResourceGraph/main.cpp
Normal 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();
|
||||
}
|
1
Userland/MenuApplets/UserName/.gitignore
vendored
Normal file
1
Userland/MenuApplets/UserName/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
UserName.MenuApplet
|
6
Userland/MenuApplets/UserName/CMakeLists.txt
Normal file
6
Userland/MenuApplets/UserName/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
set(SOURCES
|
||||
main.cpp
|
||||
)
|
||||
|
||||
serenity_bin(UserName.MenuApplet)
|
||||
target_link_libraries(UserName.MenuApplet LibGUI LibGfx)
|
101
Userland/MenuApplets/UserName/main.cpp
Normal file
101
Userland/MenuApplets/UserName/main.cpp
Normal 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();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue