diff --git a/Applications/DisplayProperties/DisplayProperties.cpp b/Applications/DisplayProperties/DisplayProperties.cpp new file mode 100644 index 00000000000..74a1dacb7f0 --- /dev/null +++ b/Applications/DisplayProperties/DisplayProperties.cpp @@ -0,0 +1,156 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "DisplayProperties.h" +#include "ItemListModel.h" + +DisplayPropertiesWidget::DisplayPropertiesWidget() + : m_wm_config(CConfigFile::get_for_app("WindowManager")) +{ + create_root_widget(); + create_frame(); + create_resolution_list(); + create_wallpaper_list(); +} + +void DisplayPropertiesWidget::create_resolution_list() +{ + // TODO: Find a better way to get the default resolution + m_resolutions.append({ 640, 480 }); + m_resolutions.append({ 800, 600 }); + m_resolutions.append({ 1024, 768 }); + m_resolutions.append({ 1280, 1024 }); + m_resolutions.append({ 1366, 768 }); + m_resolutions.append({ 1440, 900 }); + m_resolutions.append({ 1600, 900 }); + m_resolutions.append({ 1920, 1080 }); + m_resolutions.append({ 2560, 1080 }); + + m_selected_resolution = m_resolutions.at(0); +} + +void DisplayPropertiesWidget::create_root_widget() +{ + m_root_widget = new GWidget; + m_root_widget->set_layout(make(Orientation::Vertical)); + m_root_widget->set_fill_with_background_color(true); + m_root_widget->layout()->set_margins({ 4, 4, 4, 16 }); +} + +void DisplayPropertiesWidget::create_wallpaper_list() +{ + CDirIterator iterator("/res/wallpapers/", CDirIterator::Flags::SkipDots); + + while (iterator.has_next()) + m_wallpapers.append(iterator.next_path()); +} + +void DisplayPropertiesWidget::create_frame() +{ + auto* tab_widget = new GTabWidget(m_root_widget); + + // First, let's create the "Background" tab + auto* background_splitter = new GSplitter(Orientation::Vertical, nullptr); + tab_widget->add_widget("Wallpaper", background_splitter); + + auto* background_content = new GWidget(background_splitter); + background_content->set_layout(make(Orientation::Vertical)); + background_content->layout()->add_spacer(); + background_content->layout()->set_margins({ 4, 4, 4, 4 }); + + auto* wallpaper_list = new GListView(background_content); + wallpaper_list->set_background_color(Color::White); + wallpaper_list->set_model(*ItemListModel::create(m_wallpapers)); + wallpaper_list->horizontal_scrollbar().set_visible(false); + wallpaper_list->on_selection = [this](auto& index) { + m_selected_wallpaper = m_wallpapers.at(index.row()); + }; + + // Let's add the settings tab + auto* settings_splitter = new GSplitter(Orientation::Vertical, nullptr); + tab_widget->add_widget("Settings", settings_splitter); + + auto* settings_content = new GWidget(settings_splitter); + settings_content->set_layout(make(Orientation::Vertical)); + settings_content->layout()->add_spacer(); + settings_content->layout()->set_margins({ 4, 4, 4, 4 }); + + auto* resolution_list = new GListView(settings_content); + resolution_list->set_background_color(Color::White); + resolution_list->set_model(*ItemListModel::create(m_resolutions)); + resolution_list->horizontal_scrollbar().set_visible(false); + resolution_list->on_selection = [this](auto& index) { + m_selected_resolution = m_resolutions.at(index.row()); + }; + + // Add the apply and cancel buttons + auto* bottom_widget = new GWidget(m_root_widget); + bottom_widget->set_layout(make(Orientation::Horizontal)); + bottom_widget->layout()->add_spacer(); + bottom_widget->set_size_policy(Orientation::Vertical, SizePolicy::Fixed); + bottom_widget->set_preferred_size(1, 22); + + auto* apply_button = new GButton(bottom_widget); + apply_button->set_text("Apply"); + apply_button->set_size_policy(Orientation::Vertical, SizePolicy::Fixed); + apply_button->set_size_policy(Orientation::Horizontal, SizePolicy::Fixed); + apply_button->set_preferred_size(60, 22); + apply_button->on_click = [this, tab_widget](GButton&) { + send_settings_to_window_server(tab_widget->get_active_tab()); + }; + + auto* ok_button = new GButton(bottom_widget); + ok_button->set_text("OK"); + ok_button->set_size_policy(Orientation::Vertical, SizePolicy::Fixed); + ok_button->set_size_policy(Orientation::Horizontal, SizePolicy::Fixed); + ok_button->set_preferred_size(60, 22); + ok_button->on_click = [this, tab_widget](GButton&) { + send_settings_to_window_server(tab_widget->get_active_tab()); + GApplication::the().quit(); + }; + + auto* cancel_button = new GButton(bottom_widget); + cancel_button->set_text("Cancel"); + cancel_button->set_size_policy(Orientation::Vertical, SizePolicy::Fixed); + cancel_button->set_size_policy(Orientation::Horizontal, SizePolicy::Fixed); + cancel_button->set_preferred_size(60, 22); + cancel_button->on_click = [this](GButton&) { + GApplication::the().quit(); + }; +} + +void DisplayPropertiesWidget::send_settings_to_window_server(int tab_index) +{ + if (tab_index == TabIndices::Wallpaper) { + StringBuilder builder; + builder.append("/res/wallpapers/"); + builder.append(m_selected_wallpaper); + GDesktop::the().set_wallpaper(builder.to_string()); + } else if (tab_index == TabIndices::Settings) { + WSAPI_ClientMessage request; + request.type = WSAPI_ClientMessage::Type::SetResolution; + dbg() << "Attempting to set resolution " << m_selected_resolution; + request.wm_conf.resolution = { m_selected_resolution.width(), m_selected_resolution.height() }; + auto response = GWindowServerConnection::the().sync_request(request, WSAPI_ServerMessage::Type::DidSetResolution); + ASSERT(response.value == 1); + } else { + dbg() << "Invalid tab index " << tab_index; + } +} diff --git a/Applications/DisplayProperties/DisplayProperties.h b/Applications/DisplayProperties/DisplayProperties.h new file mode 100644 index 00000000000..91fbc57609b --- /dev/null +++ b/Applications/DisplayProperties/DisplayProperties.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +class DisplayPropertiesWidget final { +public: + enum class ButtonOperations { + Ok, + Apply, + Cancel, + }; + + enum TabIndices { + Wallpaper, + Settings + }; + +public: + DisplayPropertiesWidget(); + + // Apply the settings to the Window Server + void send_settings_to_window_server(int tabIndex); + void create_frame(); + + inline GWidget* get_root_widget() const { return m_root_widget; } + +private: + void create_wallpaper_list(); + void create_resolution_list(); + void create_root_widget(); + +private: + String m_wallpaper_path; + RefPtr m_wm_config; + GWidget* m_root_widget { nullptr }; + Vector m_resolutions; + Vector m_wallpapers; + + Size m_selected_resolution; + String m_selected_wallpaper; +}; diff --git a/Applications/DisplayProperties/ItemListModel.h b/Applications/DisplayProperties/ItemListModel.h new file mode 100644 index 00000000000..ea92a43a427 --- /dev/null +++ b/Applications/DisplayProperties/ItemListModel.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include + +template +class ItemListModel final : public GModel { +public: + static NonnullRefPtr create(Vector& data) { return adopt(*new ItemListModel(data)); } + + virtual ~ItemListModel() override {} + + virtual int row_count(const GModelIndex&) const override + { + return m_data.size(); + } + + virtual int column_count(const GModelIndex&) const override + { + return 1; + } + + virtual String column_name(int) const override + { + return "Data"; + } + + virtual ColumnMetadata column_metadata(int) const override + { + return { 70, TextAlignment::CenterLeft }; + } + + virtual GVariant data(const GModelIndex& index, Role role = Role::Display) const override + { + if (role == Role::Display) + return m_data.at(index.row()); + + return {}; + } + + virtual void update() override + { + did_update(); + } + +private: + explicit ItemListModel(Vector& data) + : m_data(data) + { + } + + Vector& m_data; +}; diff --git a/Applications/DisplayProperties/Makefile b/Applications/DisplayProperties/Makefile new file mode 100644 index 00000000000..a3d7d9462be --- /dev/null +++ b/Applications/DisplayProperties/Makefile @@ -0,0 +1,9 @@ +include ../../Makefile.common + +OBJS = \ + DisplayProperties.o \ + main.o \ + +APP = DisplayProperties + +include ../Makefile.common diff --git a/Applications/DisplayProperties/main.cpp b/Applications/DisplayProperties/main.cpp new file mode 100644 index 00000000000..5e0148c8053 --- /dev/null +++ b/Applications/DisplayProperties/main.cpp @@ -0,0 +1,22 @@ +#include "DisplayProperties.h" +#include +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + GApplication app(argc, argv); + DisplayPropertiesWidget instance; + + auto* window = new GWindow(); + window->set_title("Display Properties"); + window->resize(400, 448); + window->set_resizable(false); + window->set_main_widget(instance.get_root_widget()); + window->set_icon(load_png("/res/icons/16x16/app-display-properties.png")); + + window->show(); + return app.exec(); +} diff --git a/Base/res/icons/16x16/app-display-properties.png b/Base/res/icons/16x16/app-display-properties.png new file mode 100644 index 00000000000..6564dabede0 Binary files /dev/null and b/Base/res/icons/16x16/app-display-properties.png differ diff --git a/Base/res/icons/32x32/app-display-properties.png b/Base/res/icons/32x32/app-display-properties.png new file mode 100644 index 00000000000..a4def59cd30 Binary files /dev/null and b/Base/res/icons/32x32/app-display-properties.png differ diff --git a/Kernel/build-root-filesystem.sh b/Kernel/build-root-filesystem.sh index 8979c047e56..52a007ff15c 100755 --- a/Kernel/build-root-filesystem.sh +++ b/Kernel/build-root-filesystem.sh @@ -86,6 +86,7 @@ cp ../Applications/SystemDialog/SystemDialog mnt/bin/SystemDialog cp ../Applications/ChanViewer/ChanViewer mnt/bin/ChanViewer cp ../Applications/Calculator/Calculator mnt/bin/Calculator cp ../Applications/SoundPlayer/SoundPlayer mnt/bin/SoundPlayer +cp ../Applications/DisplayProperties/DisplayProperties mnt/bin/DisplayProperties cp ../Demos/HelloWorld/HelloWorld mnt/bin/HelloWorld cp ../Demos/HelloWorld2/HelloWorld2 mnt/bin/HelloWorld2 cp ../Demos/RetroFetch/RetroFetch mnt/bin/RetroFetch diff --git a/Kernel/makeall.sh b/Kernel/makeall.sh index e87cd3db113..4553530947b 100755 --- a/Kernel/makeall.sh +++ b/Kernel/makeall.sh @@ -42,6 +42,7 @@ build_targets="$build_targets ../Libraries/LibVT" build_targets="$build_targets ../Applications/About" build_targets="$build_targets ../Applications/Calculator" build_targets="$build_targets ../Applications/ChanViewer" +build_targets="$build_targets ../Applications/DisplayProperties" build_targets="$build_targets ../Applications/Downloader" build_targets="$build_targets ../Applications/FileManager" build_targets="$build_targets ../Applications/FontEditor" diff --git a/Libraries/LibGUI/GTabWidget.cpp b/Libraries/LibGUI/GTabWidget.cpp index d0663c5b7e7..180ddb51383 100644 --- a/Libraries/LibGUI/GTabWidget.cpp +++ b/Libraries/LibGUI/GTabWidget.cpp @@ -1,7 +1,7 @@ +#include #include #include #include -#include GTabWidget::GTabWidget(GWidget* parent) : GWidget(parent) diff --git a/Servers/WindowServer/WSAPITypes.h b/Servers/WindowServer/WSAPITypes.h index 4d5e7574db0..fcc4c66c29f 100644 --- a/Servers/WindowServer/WSAPITypes.h +++ b/Servers/WindowServer/WSAPITypes.h @@ -109,6 +109,7 @@ struct WSAPI_ServerMessage { DidSetWindowBackingStore, DidSetWallpaper, DidGetWallpaper, + DidSetResolution, DidSetWindowHasAlphaChannel, ScreenRectChanged, @@ -224,6 +225,7 @@ struct WSAPI_ClientMessage { Greeting, SetWallpaper, GetWallpaper, + SetResolution, SetWindowOverrideCursor, WM_SetActiveWindow, WM_SetWindowMinimized, @@ -260,6 +262,9 @@ struct WSAPI_ClientMessage { bool minimized; WSAPI_Point position; } wm; + struct { + WSAPI_Size resolution; + } wm_conf; struct { int menubar_id; int menu_id; diff --git a/Servers/WindowServer/WSClientConnection.cpp b/Servers/WindowServer/WSClientConnection.cpp index c71215473a1..b2105ec9309 100644 --- a/Servers/WindowServer/WSClientConnection.cpp +++ b/Servers/WindowServer/WSClientConnection.cpp @@ -289,6 +289,9 @@ bool WSClientConnection::handle_message(const WSAPI_ClientMessage& message, cons case WSAPI_ClientMessage::Type::GetWallpaper: CEventLoop::current().post_event(*this, make(client_id())); break; + case WSAPI_ClientMessage::Type::SetResolution: + CEventLoop::current().post_event(*this, make(client_id(), message.wm_conf.resolution.width, message.wm_conf.resolution.height)); + break; case WSAPI_ClientMessage::Type::SetWindowOverrideCursor: CEventLoop::current().post_event(*this, make(client_id(), message.window_id, (WSStandardCursor)message.cursor.cursor)); break; @@ -557,6 +560,15 @@ void WSClientConnection::handle_request(const WSAPIGetWallpaperRequest&) post_message(response); } +void WSClientConnection::handle_request(const WSAPISetResolutionRequest& request) +{ + WSWindowManager::the().set_resolution(request.resolution().width(), request.resolution().height()); + WSAPI_ServerMessage response; + response.type = WSAPI_ServerMessage::Type::DidSetResolution; + response.value = true; + post_message(response); +} + void WSClientConnection::handle_request(const WSAPISetWindowTitleRequest& request) { int window_id = request.window_id(); @@ -984,6 +996,8 @@ void WSClientConnection::on_request(const WSAPIClientRequest& request) return handle_request(static_cast(request)); case WSEvent::APIGetWallpaperRequest: return handle_request(static_cast(request)); + case WSEvent::APISetResolutionRequest: + return handle_request(static_cast(request)); case WSEvent::APISetWindowOverrideCursorRequest: return handle_request(static_cast(request)); case WSEvent::WMAPISetActiveWindowRequest: diff --git a/Servers/WindowServer/WSClientConnection.h b/Servers/WindowServer/WSClientConnection.h index 16770464f71..bc2e12b22e4 100644 --- a/Servers/WindowServer/WSClientConnection.h +++ b/Servers/WindowServer/WSClientConnection.h @@ -73,6 +73,7 @@ private: void handle_request(const WSAPISetWindowOpacityRequest&); void handle_request(const WSAPISetWallpaperRequest&); void handle_request(const WSAPIGetWallpaperRequest&); + void handle_request(const WSAPISetResolutionRequest&); void handle_request(const WSAPISetWindowOverrideCursorRequest&); void handle_request(const WSWMAPISetActiveWindowRequest&); void handle_request(const WSWMAPISetWindowMinimizedRequest&); diff --git a/Servers/WindowServer/WSCompositor.cpp b/Servers/WindowServer/WSCompositor.cpp index 23096361d69..a9e7548f0cf 100644 --- a/Servers/WindowServer/WSCompositor.cpp +++ b/Servers/WindowServer/WSCompositor.cpp @@ -300,6 +300,8 @@ void WSCompositor::set_resolution(int desired_width, int desired_height) return; m_wallpaper_path = {}; m_wallpaper = nullptr; + // Make sure it's impossible to set an invalid resolution + ASSERT(desired_width >= 640 && desired_height >= 480); WSScreen::the().set_resolution(desired_width, desired_height); init_bitmaps(); compose(); diff --git a/Servers/WindowServer/WSEvent.h b/Servers/WindowServer/WSEvent.h index b96c78beaa6..4391d3f3649 100644 --- a/Servers/WindowServer/WSEvent.h +++ b/Servers/WindowServer/WSEvent.h @@ -7,6 +7,7 @@ #include #include #include +#include #include class WSEvent : public CEvent { @@ -60,6 +61,7 @@ public: APIGetClipboardContentsRequest, APISetWallpaperRequest, APIGetWallpaperRequest, + APISetResolutionRequest, APISetWindowOverrideCursorRequest, APISetWindowHasAlphaChannelRequest, APIMoveWindowToFrontRequest, @@ -441,6 +443,20 @@ public: } }; +class WSAPISetResolutionRequest final : public WSAPIClientRequest { +public: + explicit WSAPISetResolutionRequest(int client_id, int width, int height) + : WSAPIClientRequest(WSEvent::APISetResolutionRequest, client_id), + m_resolution(width, height) + { + } + + Size resolution() const { return m_resolution; } + +private: + Size m_resolution; +}; + class WSAPISetWindowTitleRequest final : public WSAPIClientRequest { public: explicit WSAPISetWindowTitleRequest(int client_id, int window_id, const String& title)