/* * Copyright (c) 2019-2020, Jesse Buhagiar * Copyright (c) 2020-2021, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #include "MonitorSettingsWidget.h" #include #include #include #include #include #include #include #include #include #include #include namespace DisplaySettings { MonitorSettingsWidget::MonitorSettingsWidget() { create_resolution_list(); create_frame(); load_current_settings(); } void MonitorSettingsWidget::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, 720 }); m_resolutions.append({ 1280, 768 }); m_resolutions.append({ 1280, 960 }); m_resolutions.append({ 1280, 1024 }); m_resolutions.append({ 1360, 768 }); m_resolutions.append({ 1368, 768 }); m_resolutions.append({ 1440, 900 }); m_resolutions.append({ 1600, 900 }); m_resolutions.append({ 1600, 1200 }); m_resolutions.append({ 1920, 1080 }); m_resolutions.append({ 2048, 1152 }); m_resolutions.append({ 2560, 1080 }); m_resolutions.append({ 2560, 1440 }); m_resolutions.append({ 3440, 1440 }); } void MonitorSettingsWidget::create_frame() { load_from_gml(monitor_settings_window_gml); m_monitor_widget = *find_descendant_of_type_named("monitor_widget"); m_screen_combo = *find_descendant_of_type_named("screen_combo"); m_screen_combo->set_only_allow_values_from_model(true); m_screen_combo->set_model(*GUI::ItemListModel::create(m_screens)); m_screen_combo->on_change = [this](auto&, const GUI::ModelIndex& index) { m_selected_screen_index = index.row(); selected_screen_index_or_resolution_changed(); }; m_resolution_combo = *find_descendant_of_type_named("resolution_combo"); m_resolution_combo->set_only_allow_values_from_model(true); m_resolution_combo->set_model(*GUI::ItemListModel::create(m_resolutions)); m_resolution_combo->on_change = [this](auto&, const GUI::ModelIndex& index) { auto& selected_screen = m_screen_layout.screens[m_selected_screen_index]; selected_screen.resolution = m_resolutions.at(index.row()); // Try to auto re-arrange things if there are overlaps or disconnected screens m_screen_layout.normalize(); selected_screen_index_or_resolution_changed(); }; m_display_scale_radio_1x = *find_descendant_of_type_named("scale_1x"); m_display_scale_radio_1x->on_checked = [this](bool checked) { if (checked) { auto& selected_screen = m_screen_layout.screens[m_selected_screen_index]; selected_screen.scale_factor = 1; // Try to auto re-arrange things if there are overlaps or disconnected screens m_screen_layout.normalize(); m_monitor_widget->set_desktop_scale_factor(1); m_monitor_widget->update(); } }; m_display_scale_radio_2x = *find_descendant_of_type_named("scale_2x"); m_display_scale_radio_2x->on_checked = [this](bool checked) { if (checked) { auto& selected_screen = m_screen_layout.screens[m_selected_screen_index]; selected_screen.scale_factor = 2; // Try to auto re-arrange things if there are overlaps or disconnected screens m_screen_layout.normalize(); m_monitor_widget->set_desktop_scale_factor(2); m_monitor_widget->update(); } }; m_dpi_label = *find_descendant_of_type_named("display_dpi"); } void MonitorSettingsWidget::load_current_settings() { m_screen_layout = GUI::WindowServerConnection::the().get_screen_layout(); m_screens.clear(); for (size_t i = 0; i < m_screen_layout.screens.size(); i++) { if (i == m_screen_layout.main_screen_index) m_screens.append(String::formatted("{}: {} (main screen)", i + 1, m_screen_layout.screens[i].device)); else m_screens.append(String::formatted("{}: {}", i + 1, m_screen_layout.screens[i].device)); } m_selected_screen_index = m_screen_layout.main_screen_index; m_screen_combo->set_selected_index(m_selected_screen_index); selected_screen_index_or_resolution_changed(); } void MonitorSettingsWidget::selected_screen_index_or_resolution_changed() { auto& screen = m_screen_layout.screens[m_selected_screen_index]; // Let's attempt to find the current resolution based on the screen layout settings auto index = m_resolutions.find_first_index(screen.resolution).value_or(0); Gfx::IntSize current_resolution = m_resolutions.at(index); Optional screen_dpi; String screen_dpi_tooltip; if (auto edid = EDID::Parser::from_framebuffer_device(screen.device, 0); !edid.is_error()) { // TODO: multihead if (auto screen_size = edid.value().screen_size(); screen_size.has_value()) { float x_cm = screen_size.value().horizontal_cm(); float y_cm = screen_size.value().vertical_cm(); float diagonal_inch = sqrtf(x_cm * x_cm + y_cm * y_cm) / 2.54f; float diagonal_pixels = sqrtf(current_resolution.width() * current_resolution.width() + current_resolution.height() * current_resolution.height()); if (diagonal_pixels != 0.0f) { screen_dpi = diagonal_pixels / diagonal_inch; screen_dpi_tooltip = String::formatted("{} inch display ({}cm x {}cm)", roundf(diagonal_inch), x_cm, y_cm); } } } else { dbgln("Error getting EDID from device {}: {}", screen.device, edid.error()); } if (screen_dpi.has_value()) { m_dpi_label->set_tooltip(screen_dpi_tooltip); m_dpi_label->set_text(String::formatted("{} dpi", screen_dpi.value())); m_dpi_label->set_visible(true); } else { m_dpi_label->set_visible(false); } if (screen.scale_factor != 1 && screen.scale_factor != 2) { dbgln("unexpected ScaleFactor {}, setting to 1", screen.scale_factor); screen.scale_factor = 1; } (screen.scale_factor == 1 ? m_display_scale_radio_1x : m_display_scale_radio_2x)->set_checked(true); m_monitor_widget->set_desktop_scale_factor(screen.scale_factor); // Select the current selected resolution as it may differ m_monitor_widget->set_desktop_resolution(current_resolution); m_resolution_combo->set_selected_index(index); m_monitor_widget->update(); } void MonitorSettingsWidget::apply_settings() { // Fetch the latest configuration again, in case it has been changed by someone else. // This isn't technically race free, but if the user automates changing settings we can't help... auto current_layout = GUI::WindowServerConnection::the().get_screen_layout(); if (m_screen_layout != current_layout) { auto result = GUI::WindowServerConnection::the().set_screen_layout(m_screen_layout, false); if (result.success()) { load_current_settings(); // Refresh auto box = GUI::MessageBox::construct(window(), String::formatted("Do you want to keep the new settings? They will be reverted after 10 seconds."), "Apply new screen layout", GUI::MessageBox::Type::Question, GUI::MessageBox::InputType::YesNo); box->set_icon(window()->icon()); // If after 10 seconds the user doesn't close the message box, just close it. auto timer = Core::Timer::construct(10000, [&] { box->close(); }); // If the user selects "No", closes the window or the window gets closed by the 10 seconds timer, revert the changes. if (box->exec() == GUI::MessageBox::ExecYes) { auto save_result = GUI::WindowServerConnection::the().save_screen_layout(); if (!save_result.success()) { GUI::MessageBox::show(window(), String::formatted("Error saving settings: {}", save_result.error_msg()), "Unable to save setting", GUI::MessageBox::Type::Error); } } else { auto restore_result = GUI::WindowServerConnection::the().set_screen_layout(current_layout, false); if (!restore_result.success()) { GUI::MessageBox::show(window(), String::formatted("Error restoring settings: {}", restore_result.error_msg()), "Unable to restore setting", GUI::MessageBox::Type::Error); } else { load_current_settings(); } } } else { GUI::MessageBox::show(window(), String::formatted("Error setting screen layout: {}", result.error_msg()), "Unable to apply changes", GUI::MessageBox::Type::Error); } } } void MonitorSettingsWidget::show_screen_numbers(bool show) { if (m_showing_screen_numbers == show) return; m_showing_screen_numbers = show; GUI::WindowServerConnection::the().async_show_screen_numbers(show); } void MonitorSettingsWidget::show_event(GUI::ShowEvent&) { show_screen_numbers(true); } void MonitorSettingsWidget::hide_event(GUI::HideEvent&) { show_screen_numbers(false); } }