mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 20:47:45 +00:00
WindowServer: Add initial support for rendering on multiple screens
This allows WindowServer to use multiple framebuffer devices and compose the desktop with any arbitrary layout. Currently, it is assumed that it is configured contiguous and non-overlapping, but this should eventually be enforced. To make rendering efficient, each window now also tracks on which screens it needs to be rendered. This way we don't have to iterate all the windows for each screen but instead use the same rendering loop and then only render to the screen (or screens) that the window actually uses.
This commit is contained in:
parent
499c33ae0c
commit
4392da970a
42 changed files with 1127 additions and 563 deletions
|
@ -1,4 +1,10 @@
|
|||
[Screen]
|
||||
[Screens]
|
||||
MainScreen=0
|
||||
|
||||
[Screen0]
|
||||
Device=/dev/fb0
|
||||
Left=0
|
||||
Top=0
|
||||
Width=1024
|
||||
Height=768
|
||||
ScaleFactor=1
|
||||
|
|
|
@ -116,7 +116,8 @@ void MonitorSettingsWidget::apply_settings()
|
|||
}
|
||||
|
||||
if (current_resolution != m_monitor_widget->desktop_resolution() || current_scale_factor != m_monitor_widget->desktop_scale_factor()) {
|
||||
auto result = GUI::WindowServerConnection::the().set_resolution(m_monitor_widget->desktop_resolution(), m_monitor_widget->desktop_scale_factor());
|
||||
u32 display_index = 0; // TODO: implement multiple display support
|
||||
auto result = GUI::WindowServerConnection::the().set_resolution(display_index, m_monitor_widget->desktop_resolution(), m_monitor_widget->desktop_scale_factor());
|
||||
if (!result.success()) {
|
||||
GUI::MessageBox::show(nullptr, String::formatted("Reverting to resolution {}x{} @ {}x", result.resolution().width(), result.resolution().height(), result.scale_factor()),
|
||||
"Unable to set resolution", GUI::MessageBox::Type::Error);
|
||||
|
@ -134,7 +135,7 @@ void MonitorSettingsWidget::apply_settings()
|
|||
|
||||
// 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) {
|
||||
result = GUI::WindowServerConnection::the().set_resolution(current_resolution, current_scale_factor);
|
||||
result = GUI::WindowServerConnection::the().set_resolution(display_index, current_resolution, current_scale_factor);
|
||||
if (!result.success()) {
|
||||
GUI::MessageBox::show(nullptr, String::formatted("Reverting to resolution {}x{} @ {}x", result.resolution().width(), result.resolution().height(), result.scale_factor()),
|
||||
"Unable to set resolution", GUI::MessageBox::Type::Error);
|
||||
|
|
|
@ -25,11 +25,17 @@ Desktop::Desktop()
|
|||
{
|
||||
}
|
||||
|
||||
void Desktop::did_receive_screen_rect(Badge<WindowServerConnection>, const Gfx::IntRect& rect)
|
||||
void Desktop::did_receive_screen_rects(Badge<WindowServerConnection>, const Vector<Gfx::IntRect, 4>& rects, size_t main_screen_index)
|
||||
{
|
||||
if (m_rect == rect)
|
||||
return;
|
||||
m_rect = rect;
|
||||
m_main_screen_index = main_screen_index;
|
||||
m_rects = rects;
|
||||
if (!m_rects.is_empty()) {
|
||||
m_bounding_rect = m_rects[0];
|
||||
for (size_t i = 1; i < m_rects.size(); i++)
|
||||
m_bounding_rect = m_bounding_rect.united(m_rects[i]);
|
||||
} else {
|
||||
m_bounding_rect = {};
|
||||
}
|
||||
}
|
||||
|
||||
void Desktop::set_background_color(const StringView& background_color)
|
||||
|
|
|
@ -26,14 +26,18 @@ public:
|
|||
String wallpaper() const;
|
||||
bool set_wallpaper(const StringView& path, bool save_config = true);
|
||||
|
||||
Gfx::IntRect rect() const { return m_rect; }
|
||||
Gfx::IntRect rect() const { return m_bounding_rect; }
|
||||
const Vector<Gfx::IntRect, 4>& rects() const { return m_rects; }
|
||||
size_t main_screen_index() const { return m_main_screen_index; }
|
||||
|
||||
int taskbar_height() const { return TaskbarWindow::taskbar_height(); }
|
||||
|
||||
void did_receive_screen_rect(Badge<WindowServerConnection>, const Gfx::IntRect&);
|
||||
void did_receive_screen_rects(Badge<WindowServerConnection>, const Vector<Gfx::IntRect, 4>&, size_t);
|
||||
|
||||
private:
|
||||
Gfx::IntRect m_rect;
|
||||
Vector<Gfx::IntRect, 4> m_rects;
|
||||
size_t m_main_screen_index { 0 };
|
||||
Gfx::IntRect m_bounding_rect;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ public:
|
|||
DragMove,
|
||||
Drop,
|
||||
ThemeChange,
|
||||
ScreenRectChange,
|
||||
ScreenRectsChange,
|
||||
ActionEnter,
|
||||
ActionLeave,
|
||||
|
||||
|
@ -392,18 +392,21 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
class ScreenRectChangeEvent final : public Event {
|
||||
class ScreenRectsChangeEvent final : public Event {
|
||||
public:
|
||||
explicit ScreenRectChangeEvent(const Gfx::IntRect& rect)
|
||||
: Event(Type::ScreenRectChange)
|
||||
, m_rect(rect)
|
||||
explicit ScreenRectsChangeEvent(const Vector<Gfx::IntRect, 4>& rects, size_t main_screen_index)
|
||||
: Event(Type::ScreenRectsChange)
|
||||
, m_rects(rects)
|
||||
, m_main_screen_index(main_screen_index)
|
||||
{
|
||||
}
|
||||
|
||||
const Gfx::IntRect& rect() const { return m_rect; }
|
||||
const Vector<Gfx::IntRect, 4>& rects() const { return m_rects; }
|
||||
size_t main_screen_index() const { return m_main_screen_index; }
|
||||
|
||||
private:
|
||||
Gfx::IntRect m_rect;
|
||||
Vector<Gfx::IntRect, 4> m_rects;
|
||||
size_t m_main_screen_index;
|
||||
};
|
||||
|
||||
class FocusEvent final : public Event {
|
||||
|
|
|
@ -52,7 +52,7 @@ class Painter;
|
|||
class RadioButton;
|
||||
class ResizeCorner;
|
||||
class ResizeEvent;
|
||||
class ScreenRectChangeEvent;
|
||||
class ScreenRectsChangeEvent;
|
||||
class Scrollbar;
|
||||
class AbstractScrollableWidget;
|
||||
class Slider;
|
||||
|
|
|
@ -506,7 +506,7 @@ void Widget::theme_change_event(ThemeChangeEvent&)
|
|||
{
|
||||
}
|
||||
|
||||
void Widget::screen_rect_change_event(ScreenRectChangeEvent&)
|
||||
void Widget::screen_rects_change_event(ScreenRectsChangeEvent&)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -312,7 +312,7 @@ protected:
|
|||
virtual void drag_leave_event(Event&);
|
||||
virtual void drop_event(DropEvent&);
|
||||
virtual void theme_change_event(ThemeChangeEvent&);
|
||||
virtual void screen_rect_change_event(ScreenRectChangeEvent&);
|
||||
virtual void screen_rects_change_event(ScreenRectsChangeEvent&);
|
||||
|
||||
virtual void did_begin_inspection() override;
|
||||
virtual void did_end_inspection() override;
|
||||
|
|
|
@ -495,11 +495,11 @@ void Window::handle_theme_change_event(ThemeChangeEvent& event)
|
|||
dispatch_theme_change(*m_main_widget.ptr(), dispatch_theme_change);
|
||||
}
|
||||
|
||||
void Window::handle_screen_rect_change_event(ScreenRectChangeEvent& event)
|
||||
void Window::handle_screen_rects_change_event(ScreenRectsChangeEvent& event)
|
||||
{
|
||||
if (!m_main_widget)
|
||||
return;
|
||||
auto dispatch_screen_rect_change = [&](auto& widget, auto recursive) {
|
||||
auto dispatch_screen_rects_change = [&](auto& widget, auto recursive) {
|
||||
widget.dispatch_event(event, this);
|
||||
widget.for_each_child_widget([&](auto& widget) -> IterationDecision {
|
||||
widget.dispatch_event(event, this);
|
||||
|
@ -507,8 +507,8 @@ void Window::handle_screen_rect_change_event(ScreenRectChangeEvent& event)
|
|||
return IterationDecision::Continue;
|
||||
});
|
||||
};
|
||||
dispatch_screen_rect_change(*m_main_widget.ptr(), dispatch_screen_rect_change);
|
||||
screen_rect_change_event(event);
|
||||
dispatch_screen_rects_change(*m_main_widget.ptr(), dispatch_screen_rects_change);
|
||||
screen_rects_change_event(event);
|
||||
}
|
||||
|
||||
void Window::handle_drag_move_event(DragEvent& event)
|
||||
|
@ -578,8 +578,8 @@ void Window::event(Core::Event& event)
|
|||
if (event.type() == Event::ThemeChange)
|
||||
return handle_theme_change_event(static_cast<ThemeChangeEvent&>(event));
|
||||
|
||||
if (event.type() == Event::ScreenRectChange)
|
||||
return handle_screen_rect_change_event(static_cast<ScreenRectChangeEvent&>(event));
|
||||
if (event.type() == Event::ScreenRectsChange)
|
||||
return handle_screen_rects_change_event(static_cast<ScreenRectsChangeEvent&>(event));
|
||||
|
||||
Core::Object::event(event);
|
||||
}
|
||||
|
@ -811,7 +811,7 @@ void Window::wm_event(WMEvent&)
|
|||
{
|
||||
}
|
||||
|
||||
void Window::screen_rect_change_event(ScreenRectChangeEvent&)
|
||||
void Window::screen_rects_change_event(ScreenRectsChangeEvent&)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -203,7 +203,7 @@ public:
|
|||
protected:
|
||||
Window(Core::Object* parent = nullptr);
|
||||
virtual void wm_event(WMEvent&);
|
||||
virtual void screen_rect_change_event(ScreenRectChangeEvent&);
|
||||
virtual void screen_rects_change_event(ScreenRectsChangeEvent&);
|
||||
|
||||
private:
|
||||
void update_cursor();
|
||||
|
@ -218,7 +218,7 @@ private:
|
|||
void handle_became_active_or_inactive_event(Core::Event&);
|
||||
void handle_close_request();
|
||||
void handle_theme_change_event(ThemeChangeEvent&);
|
||||
void handle_screen_rect_change_event(ScreenRectChangeEvent&);
|
||||
void handle_screen_rects_change_event(ScreenRectsChangeEvent&);
|
||||
void handle_drag_move_event(DragEvent&);
|
||||
void handle_left_event();
|
||||
|
||||
|
|
|
@ -48,12 +48,12 @@ WindowServerConnection::WindowServerConnection()
|
|||
// All we have to do is wait for it to arrive. This avoids a round-trip during application startup.
|
||||
auto message = wait_for_specific_message<Messages::WindowClient::FastGreet>();
|
||||
set_system_theme_from_anonymous_buffer(message->theme_buffer());
|
||||
Desktop::the().did_receive_screen_rect({}, message->screen_rect());
|
||||
Desktop::the().did_receive_screen_rects({}, message->screen_rects(), message->main_screen_index());
|
||||
Gfx::FontDatabase::set_default_font_query(message->default_font_query());
|
||||
Gfx::FontDatabase::set_fixed_width_font_query(message->fixed_width_font_query());
|
||||
}
|
||||
|
||||
void WindowServerConnection::fast_greet(Gfx::IntRect const&, Core::AnonymousBuffer const&, String const&, String const&)
|
||||
void WindowServerConnection::fast_greet(Vector<Gfx::IntRect> const&, u32, Core::AnonymousBuffer const&, String const&, String const&)
|
||||
{
|
||||
// NOTE: This message is handled in the constructor.
|
||||
}
|
||||
|
@ -311,11 +311,11 @@ void WindowServerConnection::menu_item_left(i32 menu_id, u32 identifier)
|
|||
Core::EventLoop::current().post_event(*app, make<ActionEvent>(GUI::Event::ActionLeave, *action));
|
||||
}
|
||||
|
||||
void WindowServerConnection::screen_rect_changed(Gfx::IntRect const& rect)
|
||||
void WindowServerConnection::screen_rects_changed(Vector<Gfx::IntRect> const& rects, u32 main_screen_index)
|
||||
{
|
||||
Desktop::the().did_receive_screen_rect({}, rect);
|
||||
Window::for_each_window({}, [rect](auto& window) {
|
||||
Core::EventLoop::current().post_event(window, make<ScreenRectChangeEvent>(rect));
|
||||
Desktop::the().did_receive_screen_rects({}, rects, main_screen_index);
|
||||
Window::for_each_window({}, [&](auto& window) {
|
||||
Core::EventLoop::current().post_event(window, make<ScreenRectsChangeEvent>(rects, main_screen_index));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ public:
|
|||
private:
|
||||
WindowServerConnection();
|
||||
|
||||
virtual void fast_greet(Gfx::IntRect const&, Core::AnonymousBuffer const&, String const&, String const&) override;
|
||||
virtual void fast_greet(Vector<Gfx::IntRect> const&, u32, Core::AnonymousBuffer const&, String const&, String const&) override;
|
||||
virtual void paint(i32, Gfx::IntSize const&, Vector<Gfx::IntRect> const&) override;
|
||||
virtual void mouse_move(i32, Gfx::IntPoint const&, u32, u32, u32, i32, bool, Vector<String> const&) override;
|
||||
virtual void mouse_down(i32, Gfx::IntPoint const&, u32, u32, u32, i32) override;
|
||||
|
@ -43,7 +43,7 @@ private:
|
|||
virtual void menu_item_entered(i32, u32) override;
|
||||
virtual void menu_item_left(i32, u32) override;
|
||||
virtual void menu_visibility_did_change(i32, bool) override;
|
||||
virtual void screen_rect_changed(Gfx::IntRect const&) override;
|
||||
virtual void screen_rects_changed(Vector<Gfx::IntRect> const&, u32) override;
|
||||
virtual void set_wallpaper_finished(bool) override;
|
||||
virtual void drag_dropped(i32, Gfx::IntPoint const&, String const&, HashMap<String, ByteBuffer> const&) override;
|
||||
virtual void drag_accepted() override;
|
||||
|
|
|
@ -71,7 +71,7 @@ void OutOfProcessWebView::create_client()
|
|||
|
||||
client().async_update_system_theme(Gfx::current_system_theme_buffer());
|
||||
client().async_update_system_fonts(Gfx::FontDatabase::default_font_query(), Gfx::FontDatabase::fixed_width_font_query());
|
||||
client().async_update_screen_rect(GUI::Desktop::the().rect());
|
||||
client().async_update_screen_rects(GUI::Desktop::the().rects(), GUI::Desktop::the().main_screen_index());
|
||||
}
|
||||
|
||||
void OutOfProcessWebView::load(const URL& url)
|
||||
|
@ -192,9 +192,9 @@ void OutOfProcessWebView::theme_change_event(GUI::ThemeChangeEvent& event)
|
|||
request_repaint();
|
||||
}
|
||||
|
||||
void OutOfProcessWebView::screen_rect_change_event(GUI::ScreenRectChangeEvent& event)
|
||||
void OutOfProcessWebView::screen_rects_change_event(GUI::ScreenRectsChangeEvent& event)
|
||||
{
|
||||
client().async_update_screen_rect(event.rect());
|
||||
client().async_update_screen_rects(event.rects(), event.main_screen_index());
|
||||
}
|
||||
|
||||
void OutOfProcessWebView::notify_server_did_paint(Badge<WebContentClient>, i32 bitmap_id)
|
||||
|
|
|
@ -74,7 +74,7 @@ private:
|
|||
virtual void mousewheel_event(GUI::MouseEvent&) override;
|
||||
virtual void keydown_event(GUI::KeyEvent&) override;
|
||||
virtual void theme_change_event(GUI::ThemeChangeEvent&) override;
|
||||
virtual void screen_rect_change_event(GUI::ScreenRectChangeEvent&) override;
|
||||
virtual void screen_rects_change_event(GUI::ScreenRectsChangeEvent&) override;
|
||||
|
||||
// ^AbstractScrollableWidget
|
||||
virtual void did_scroll() override;
|
||||
|
|
|
@ -129,9 +129,9 @@ void NotificationWindow::set_image(const Gfx::ShareableBitmap& image)
|
|||
}
|
||||
}
|
||||
|
||||
void NotificationWindow::screen_rect_change_event(GUI::ScreenRectChangeEvent& event)
|
||||
void NotificationWindow::screen_rects_change_event(GUI::ScreenRectsChangeEvent& event)
|
||||
{
|
||||
update_notification_window_locations(event.rect());
|
||||
update_notification_window_locations(event.rects()[event.main_screen_index()]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ public:
|
|||
private:
|
||||
NotificationWindow(i32 client_id, const String& text, const String& title, const Gfx::ShareableBitmap&);
|
||||
|
||||
virtual void screen_rect_change_event(GUI::ScreenRectChangeEvent&) override;
|
||||
virtual void screen_rects_change_event(GUI::ScreenRectsChangeEvent&) override;
|
||||
|
||||
Gfx::IntRect m_original_rect;
|
||||
i32 m_id;
|
||||
|
|
|
@ -58,7 +58,7 @@ TaskbarWindow::TaskbarWindow(NonnullRefPtr<GUI::Menu> start_menu)
|
|||
set_window_type(GUI::WindowType::Taskbar);
|
||||
set_title("Taskbar");
|
||||
|
||||
on_screen_rect_change(GUI::Desktop::the().rect());
|
||||
on_screen_rects_change(GUI::Desktop::the().rects(), GUI::Desktop::the().main_screen_index());
|
||||
|
||||
auto& main_widget = set_main_widget<TaskbarWidget>();
|
||||
main_widget.set_layout<GUI::HorizontalBoxLayout>();
|
||||
|
@ -148,8 +148,9 @@ void TaskbarWindow::create_quick_launch_bar()
|
|||
quick_launch_bar.set_fixed_size(total_width, 24);
|
||||
}
|
||||
|
||||
void TaskbarWindow::on_screen_rect_change(const Gfx::IntRect& rect)
|
||||
void TaskbarWindow::on_screen_rects_change(const Vector<Gfx::IntRect, 4>& rects, size_t main_screen_index)
|
||||
{
|
||||
const auto& rect = rects[main_screen_index];
|
||||
Gfx::IntRect new_rect { rect.x(), rect.bottom() - taskbar_height() + 1, rect.width(), taskbar_height() };
|
||||
set_rect(new_rect);
|
||||
update_applet_area();
|
||||
|
@ -332,7 +333,7 @@ void TaskbarWindow::wm_event(GUI::WMEvent& event)
|
|||
}
|
||||
}
|
||||
|
||||
void TaskbarWindow::screen_rect_change_event(GUI::ScreenRectChangeEvent& event)
|
||||
void TaskbarWindow::screen_rects_change_event(GUI::ScreenRectsChangeEvent& event)
|
||||
{
|
||||
on_screen_rect_change(event.rect());
|
||||
on_screen_rects_change(event.rects(), event.main_screen_index());
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ public:
|
|||
private:
|
||||
explicit TaskbarWindow(NonnullRefPtr<GUI::Menu> start_menu);
|
||||
void create_quick_launch_bar();
|
||||
void on_screen_rect_change(const Gfx::IntRect&);
|
||||
void on_screen_rects_change(const Vector<Gfx::IntRect, 4>&, size_t);
|
||||
NonnullRefPtr<GUI::Button> create_button(const WindowIdentifier&);
|
||||
void add_window_button(::Window&, const WindowIdentifier&);
|
||||
void remove_window_button(::Window&, bool);
|
||||
|
@ -29,7 +29,7 @@ private:
|
|||
::Window* find_window_owner(::Window&) const;
|
||||
|
||||
virtual void wm_event(GUI::WMEvent&) override;
|
||||
virtual void screen_rect_change_event(GUI::ScreenRectChangeEvent&) override;
|
||||
virtual void screen_rects_change_event(GUI::ScreenRectsChangeEvent&) override;
|
||||
|
||||
void update_applet_area();
|
||||
|
||||
|
|
|
@ -72,9 +72,9 @@ void ClientConnection::update_system_fonts(String const& default_font_query, Str
|
|||
Gfx::FontDatabase::set_fixed_width_font_query(fixed_width_font_query);
|
||||
}
|
||||
|
||||
void ClientConnection::update_screen_rect(const Gfx::IntRect& rect)
|
||||
void ClientConnection::update_screen_rects(const Vector<Gfx::IntRect>& rects, u32 main_screen)
|
||||
{
|
||||
m_page_host->set_screen_rect(rect);
|
||||
m_page_host->set_screen_rects(rects, main_screen);
|
||||
}
|
||||
|
||||
void ClientConnection::load_url(const URL& url)
|
||||
|
|
|
@ -34,7 +34,7 @@ private:
|
|||
|
||||
virtual void update_system_theme(Core::AnonymousBuffer const&) override;
|
||||
virtual void update_system_fonts(String const&, String const&) override;
|
||||
virtual void update_screen_rect(Gfx::IntRect const&) override;
|
||||
virtual void update_screen_rects(Vector<Gfx::IntRect> const&, u32) override;
|
||||
virtual void load_url(URL const&) override;
|
||||
virtual void load_html(String const&, URL const&) override;
|
||||
virtual void paint(Gfx::IntRect const&, i32) override;
|
||||
|
|
|
@ -28,7 +28,7 @@ public:
|
|||
|
||||
void set_palette_impl(const Gfx::PaletteImpl&);
|
||||
void set_viewport_rect(const Gfx::IntRect&);
|
||||
void set_screen_rect(const Gfx::IntRect& rect) { m_screen_rect = rect; };
|
||||
void set_screen_rects(const Vector<Gfx::IntRect, 4>& rects, size_t main_screen_index) { m_screen_rect = rects[main_screen_index]; };
|
||||
|
||||
void set_should_show_line_box_borders(bool b) { m_should_show_line_box_borders = b; }
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ endpoint WebContentServer
|
|||
{
|
||||
update_system_theme(Core::AnonymousBuffer theme_buffer) =|
|
||||
update_system_fonts(String default_font_query, String fixed_width_font_query) =|
|
||||
update_screen_rect(Gfx::IntRect rect) =|
|
||||
update_screen_rects(Vector<Gfx::IntRect> rects, u32 main_screen_index) =|
|
||||
|
||||
load_url(URL url) =|
|
||||
load_html(String html, URL url) =|
|
||||
|
|
|
@ -53,7 +53,7 @@ ClientConnection::ClientConnection(NonnullRefPtr<Core::LocalSocket> client_socke
|
|||
s_connections = new HashMap<int, NonnullRefPtr<ClientConnection>>;
|
||||
s_connections->set(client_id, *this);
|
||||
|
||||
async_fast_greet(Screen::the().rect(), Gfx::current_system_theme_buffer(), Gfx::FontDatabase::default_font_query(), Gfx::FontDatabase::fixed_width_font_query());
|
||||
async_fast_greet(Screen::rects(), Screen::main().index(), Gfx::current_system_theme_buffer(), Gfx::FontDatabase::default_font_query(), Gfx::FontDatabase::fixed_width_font_query());
|
||||
}
|
||||
|
||||
ClientConnection::~ClientConnection()
|
||||
|
@ -77,9 +77,9 @@ void ClientConnection::die()
|
|||
});
|
||||
}
|
||||
|
||||
void ClientConnection::notify_about_new_screen_rect(Gfx::IntRect const& rect)
|
||||
void ClientConnection::notify_about_new_screen_rects(Vector<Gfx::IntRect, 4> const& rects, size_t main_screen_index)
|
||||
{
|
||||
async_screen_rect_changed(rect);
|
||||
async_screen_rects_changed(rects, main_screen_index);
|
||||
}
|
||||
|
||||
void ClientConnection::create_menubar(i32 menubar_id)
|
||||
|
@ -297,9 +297,14 @@ Messages::WindowServer::GetWallpaperResponse ClientConnection::get_wallpaper()
|
|||
return Compositor::the().wallpaper_path();
|
||||
}
|
||||
|
||||
Messages::WindowServer::SetResolutionResponse ClientConnection::set_resolution(Gfx::IntSize const& resolution, int scale_factor)
|
||||
Messages::WindowServer::SetResolutionResponse ClientConnection::set_resolution(u32 screen_index, Gfx::IntSize const& resolution, int scale_factor)
|
||||
{
|
||||
return { WindowManager::the().set_resolution(resolution.width(), resolution.height(), scale_factor), WindowManager::the().resolution(), WindowManager::the().scale_factor() };
|
||||
if (auto* screen = Screen::find_by_index(screen_index)) {
|
||||
bool success = WindowManager::the().set_resolution(*screen, resolution.width(), resolution.height(), scale_factor);
|
||||
return { success, screen->size(), screen->scale_factor() };
|
||||
}
|
||||
dbgln("Setting resolution: Invalid screen index {}", screen_index);
|
||||
return { false, {}, 0 };
|
||||
}
|
||||
|
||||
void ClientConnection::set_window_title(i32 window_id, String const& title)
|
||||
|
@ -384,7 +389,7 @@ Messages::WindowServer::SetWindowRectResponse ClientConnection::set_window_rect(
|
|||
auto new_rect = rect;
|
||||
window.apply_minimum_size(new_rect);
|
||||
window.set_rect(new_rect);
|
||||
window.nudge_into_desktop();
|
||||
window.nudge_into_desktop(nullptr);
|
||||
window.request_update(window.rect());
|
||||
return window.rect();
|
||||
}
|
||||
|
@ -419,7 +424,7 @@ void ClientConnection::set_window_minimum_size(i32 window_id, Gfx::IntSize const
|
|||
auto new_rect = window.rect();
|
||||
bool did_size_clamp = window.apply_minimum_size(new_rect);
|
||||
window.set_rect(new_rect);
|
||||
window.nudge_into_desktop();
|
||||
window.nudge_into_desktop(nullptr);
|
||||
window.request_update(window.rect());
|
||||
|
||||
if (did_size_clamp)
|
||||
|
@ -498,13 +503,13 @@ void ClientConnection::create_window(i32 window_id, Gfx::IntRect const& rect,
|
|||
window->set_minimum_size(minimum_size);
|
||||
bool did_size_clamp = window->apply_minimum_size(new_rect);
|
||||
window->set_rect(new_rect);
|
||||
window->nudge_into_desktop();
|
||||
window->nudge_into_desktop(nullptr);
|
||||
|
||||
if (did_size_clamp)
|
||||
window->refresh_client_size();
|
||||
}
|
||||
if (window->type() == WindowType::Desktop) {
|
||||
window->set_rect(WindowManager::the().desktop_rect());
|
||||
window->set_rect(Screen::bounding_rect());
|
||||
window->recalculate_rect();
|
||||
}
|
||||
window->set_opacity(opacity);
|
||||
|
@ -706,7 +711,7 @@ void ClientConnection::start_window_resize(i32 window_id)
|
|||
}
|
||||
// FIXME: We are cheating a bit here by using the current cursor location and hard-coding the left button.
|
||||
// Maybe the client should be allowed to specify what initiated this request?
|
||||
WindowManager::the().start_window_resize(window, Screen::the().cursor_location(), MouseButton::Left);
|
||||
WindowManager::the().start_window_resize(window, ScreenInput::the().cursor_location(), MouseButton::Left);
|
||||
}
|
||||
|
||||
Messages::WindowServer::StartDragResponse ClientConnection::start_drag(String const& text, HashMap<String, ByteBuffer> const& mime_data, Gfx::ShareableBitmap const& drag_bitmap)
|
||||
|
@ -830,7 +835,7 @@ void ClientConnection::pong()
|
|||
|
||||
Messages::WindowServer::GetGlobalCursorPositionResponse ClientConnection::get_global_cursor_position()
|
||||
{
|
||||
return Screen::the().cursor_location();
|
||||
return ScreenInput::the().cursor_location();
|
||||
}
|
||||
|
||||
void ClientConnection::set_mouse_acceleration(float factor)
|
||||
|
@ -845,7 +850,7 @@ void ClientConnection::set_mouse_acceleration(float factor)
|
|||
|
||||
Messages::WindowServer::GetMouseAccelerationResponse ClientConnection::get_mouse_acceleration()
|
||||
{
|
||||
return Screen::the().acceleration_factor();
|
||||
return ScreenInput::the().acceleration_factor();
|
||||
}
|
||||
|
||||
void ClientConnection::set_scroll_step_size(u32 step_size)
|
||||
|
@ -859,7 +864,7 @@ void ClientConnection::set_scroll_step_size(u32 step_size)
|
|||
|
||||
Messages::WindowServer::GetScrollStepSizeResponse ClientConnection::get_scroll_step_size()
|
||||
{
|
||||
return Screen::the().scroll_step_size();
|
||||
return ScreenInput::the().scroll_step_size();
|
||||
}
|
||||
|
||||
void ClientConnection::set_double_click_speed(i32 speed)
|
||||
|
@ -909,25 +914,27 @@ void ClientConnection::did_become_responsive()
|
|||
|
||||
Messages::WindowServer::GetScreenBitmapResponse ClientConnection::get_screen_bitmap(Optional<Gfx::IntRect> const& rect)
|
||||
{
|
||||
auto& screen = Screen::main(); // TODO: implement screenshots from other screens or areas spanning multiple screens
|
||||
if (rect.has_value()) {
|
||||
auto bitmap = Compositor::the().front_bitmap_for_screenshot({}).cropped(rect.value());
|
||||
auto bitmap = Compositor::the().front_bitmap_for_screenshot({}, screen).cropped(rect.value());
|
||||
return bitmap->to_shareable_bitmap();
|
||||
}
|
||||
auto& bitmap = Compositor::the().front_bitmap_for_screenshot({});
|
||||
auto& bitmap = Compositor::the().front_bitmap_for_screenshot({}, screen);
|
||||
return bitmap.to_shareable_bitmap();
|
||||
}
|
||||
|
||||
Messages::WindowServer::GetScreenBitmapAroundCursorResponse ClientConnection::get_screen_bitmap_around_cursor(Gfx::IntSize const& size)
|
||||
{
|
||||
auto scale_factor = WindowManager::the().scale_factor();
|
||||
auto cursor_location = Screen::the().cursor_location();
|
||||
auto& screen = Screen::main(); // TODO: implement getting screen bitmaps from other screens or areas spanning multiple screens
|
||||
auto scale_factor = screen.scale_factor();
|
||||
auto cursor_location = ScreenInput::the().cursor_location();
|
||||
Gfx::Rect rect { (cursor_location.x() * scale_factor) - (size.width() / 2), (cursor_location.y() * scale_factor) - (size.height() / 2), size.width(), size.height() };
|
||||
|
||||
// Recompose the screen to make sure the cursor is painted in the location we think it is.
|
||||
// FIXME: This is rather wasteful. We can probably think of a way to avoid this.
|
||||
Compositor::the().compose();
|
||||
|
||||
auto bitmap = Compositor::the().front_bitmap_for_screenshot({}).cropped(rect);
|
||||
auto bitmap = Compositor::the().front_bitmap_for_screenshot({}, screen).cropped(rect);
|
||||
return bitmap->to_shareable_bitmap();
|
||||
}
|
||||
|
||||
|
@ -942,9 +949,12 @@ Messages::WindowServer::IsWindowModifiedResponse ClientConnection::is_window_mod
|
|||
return window.is_modified();
|
||||
}
|
||||
|
||||
Messages::WindowServer::GetDesktopDisplayScaleResponse ClientConnection::get_desktop_display_scale()
|
||||
Messages::WindowServer::GetDesktopDisplayScaleResponse ClientConnection::get_desktop_display_scale(u32 screen_index)
|
||||
{
|
||||
return WindowManager::the().scale_factor();
|
||||
if (auto* screen = Screen::find_by_index(screen_index))
|
||||
return screen->scale_factor();
|
||||
dbgln("GetDesktopDisplayScale: Screen {} does not exist", screen_index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ClientConnection::set_window_modified(i32 window_id, bool modified)
|
||||
|
|
|
@ -40,7 +40,7 @@ public:
|
|||
static ClientConnection* from_client_id(int client_id);
|
||||
static void for_each_client(Function<void(ClientConnection&)>);
|
||||
|
||||
void notify_about_new_screen_rect(const Gfx::IntRect&);
|
||||
void notify_about_new_screen_rects(const Vector<Gfx::IntRect, 4>&, size_t);
|
||||
void post_paint_message(Window&, bool ignore_occlusion = false);
|
||||
|
||||
Menu* find_menu_by_id(int menu_id)
|
||||
|
@ -125,7 +125,7 @@ private:
|
|||
virtual void set_background_color(String const&) override;
|
||||
virtual void set_wallpaper_mode(String const&) override;
|
||||
virtual Messages::WindowServer::GetWallpaperResponse get_wallpaper() override;
|
||||
virtual Messages::WindowServer::SetResolutionResponse set_resolution(Gfx::IntSize const&, int) override;
|
||||
virtual Messages::WindowServer::SetResolutionResponse set_resolution(u32, Gfx::IntSize const&, int) override;
|
||||
virtual void set_window_cursor(i32, i32) override;
|
||||
virtual void set_window_custom_cursor(i32, Gfx::ShareableBitmap const&) override;
|
||||
virtual void popup_menu(i32, Gfx::IntPoint const&) override;
|
||||
|
@ -153,7 +153,7 @@ private:
|
|||
virtual Messages::WindowServer::GetDoubleClickSpeedResponse get_double_click_speed() override;
|
||||
virtual void set_window_modified(i32, bool) override;
|
||||
virtual Messages::WindowServer::IsWindowModifiedResponse is_window_modified(i32) override;
|
||||
virtual Messages::WindowServer::GetDesktopDisplayScaleResponse get_desktop_display_scale() override;
|
||||
virtual Messages::WindowServer::GetDesktopDisplayScaleResponse get_desktop_display_scale(u32) override;
|
||||
|
||||
Window* window_from_id(i32 window_id);
|
||||
|
||||
|
|
|
@ -61,28 +61,44 @@ Compositor::Compositor()
|
|||
},
|
||||
this);
|
||||
|
||||
m_screen_can_set_buffer = Screen::the().can_set_buffer();
|
||||
init_bitmaps();
|
||||
}
|
||||
|
||||
void Compositor::init_bitmaps()
|
||||
const Gfx::Bitmap& Compositor::front_bitmap_for_screenshot(Badge<ClientConnection>, Screen& screen) const
|
||||
{
|
||||
return *m_screen_data[screen.index()].m_front_bitmap;
|
||||
}
|
||||
|
||||
void Compositor::ScreenData::init_bitmaps(Screen& screen)
|
||||
{
|
||||
auto& screen = Screen::the();
|
||||
auto size = screen.size();
|
||||
|
||||
m_front_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::BGRx8888, size, screen.scale_factor(), screen.pitch(), screen.scanline(0));
|
||||
m_front_painter = make<Gfx::Painter>(*m_front_bitmap);
|
||||
m_front_painter->translate(-screen.rect().location());
|
||||
|
||||
if (m_screen_can_set_buffer)
|
||||
if (screen.can_set_buffer())
|
||||
m_back_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::BGRx8888, size, screen.scale_factor(), screen.pitch(), screen.scanline(screen.physical_height()));
|
||||
else
|
||||
m_back_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, size, screen.scale_factor());
|
||||
m_back_painter = make<Gfx::Painter>(*m_back_bitmap);
|
||||
m_back_painter->translate(-screen.rect().location());
|
||||
|
||||
m_temp_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, size, screen.scale_factor());
|
||||
m_temp_painter = make<Gfx::Painter>(*m_temp_bitmap);
|
||||
m_temp_painter->translate(-screen.rect().location());
|
||||
|
||||
m_buffers_are_flipped = false;
|
||||
m_screen_can_set_buffer = screen.can_set_buffer();
|
||||
}
|
||||
|
||||
void Compositor::init_bitmaps()
|
||||
{
|
||||
m_screen_data.resize(Screen::count());
|
||||
Screen::for_each([&](auto& screen) {
|
||||
m_screen_data[screen.index()].init_bitmaps(screen);
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
invalidate_screen();
|
||||
}
|
||||
|
@ -101,7 +117,6 @@ void Compositor::did_construct_window_manager(Badge<WindowManager>)
|
|||
void Compositor::compose()
|
||||
{
|
||||
auto& wm = WindowManager::the();
|
||||
auto& ws = Screen::the();
|
||||
|
||||
{
|
||||
auto& current_cursor = wm.active_cursor();
|
||||
|
@ -122,11 +137,26 @@ void Compositor::compose()
|
|||
}
|
||||
|
||||
auto dirty_screen_rects = move(m_dirty_screen_rects);
|
||||
dirty_screen_rects.add(m_last_geometry_label_damage_rect.intersected(ws.rect()));
|
||||
dirty_screen_rects.add(m_last_dnd_rect.intersected(ws.rect()));
|
||||
if (m_invalidated_cursor) {
|
||||
if (wm.dnd_client())
|
||||
dirty_screen_rects.add(wm.dnd_rect().intersected(ws.rect()));
|
||||
auto* dnd_client = wm.dnd_client();
|
||||
if (!m_last_geometry_label_damage_rect.is_empty() || !m_last_dnd_rect.is_empty() || (m_invalidated_cursor && dnd_client)) {
|
||||
Screen::for_each([&](auto& screen) {
|
||||
if (!m_last_geometry_label_damage_rect.is_empty()) {
|
||||
auto rect = m_last_geometry_label_damage_rect.intersected(screen.rect());
|
||||
if (!rect.is_empty())
|
||||
dirty_screen_rects.add(rect);
|
||||
}
|
||||
if (!m_last_dnd_rect.is_empty()) {
|
||||
auto rect = m_last_dnd_rect.intersected(screen.rect());
|
||||
if (!rect.is_empty())
|
||||
dirty_screen_rects.add(rect);
|
||||
}
|
||||
if (m_invalidated_cursor && dnd_client) {
|
||||
auto rect = wm.dnd_rect().intersected(screen.rect());
|
||||
if (!rect.is_empty())
|
||||
dirty_screen_rects.add(rect);
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
// Mark window regions as dirty that need to be re-rendered
|
||||
|
@ -180,61 +210,81 @@ void Compositor::compose()
|
|||
dbgln("dirty screen: {}", r);
|
||||
}
|
||||
|
||||
Gfx::DisjointRectSet flush_rects;
|
||||
Gfx::DisjointRectSet flush_transparent_rects;
|
||||
Gfx::DisjointRectSet flush_special_rects;
|
||||
auto& cursor_screen = ScreenInput::the().cursor_location_screen();
|
||||
|
||||
for (auto& screen_data : m_screen_data) {
|
||||
screen_data.m_flush_rects.clear_with_capacity();
|
||||
screen_data.m_flush_transparent_rects.clear_with_capacity();
|
||||
screen_data.m_flush_special_rects.clear_with_capacity();
|
||||
}
|
||||
|
||||
auto cursor_rect = current_cursor_rect();
|
||||
|
||||
bool need_to_draw_cursor = false;
|
||||
|
||||
auto back_painter = *m_back_painter;
|
||||
auto temp_painter = *m_temp_painter;
|
||||
|
||||
auto check_restore_cursor_back = [&](const Gfx::IntRect& rect) {
|
||||
if (!need_to_draw_cursor && rect.intersects(cursor_rect)) {
|
||||
Gfx::IntRect previous_cursor_rect;
|
||||
Screen* previous_cursor_screen = nullptr;
|
||||
auto check_restore_cursor_back = [&](Screen& screen, const Gfx::IntRect& rect) {
|
||||
if (&screen == &cursor_screen && !previous_cursor_screen && !need_to_draw_cursor && rect.intersects(cursor_rect)) {
|
||||
// Restore what's behind the cursor if anything touches the area of the cursor
|
||||
need_to_draw_cursor = true;
|
||||
restore_cursor_back();
|
||||
auto& screen_data = m_screen_data[cursor_screen.index()];
|
||||
if (screen_data.restore_cursor_back(cursor_screen, previous_cursor_rect))
|
||||
previous_cursor_screen = &screen;
|
||||
}
|
||||
};
|
||||
|
||||
auto prepare_rect = [&](const Gfx::IntRect& rect) {
|
||||
if (&cursor_screen != m_current_cursor_screen) {
|
||||
// Cursor moved to another screen, restore on the cursor's background on the previous screen
|
||||
need_to_draw_cursor = true;
|
||||
if (m_current_cursor_screen) {
|
||||
auto& screen_data = m_screen_data[m_current_cursor_screen->index()];
|
||||
if (screen_data.restore_cursor_back(*m_current_cursor_screen, previous_cursor_rect))
|
||||
previous_cursor_screen = m_current_cursor_screen;
|
||||
}
|
||||
m_current_cursor_screen = &cursor_screen;
|
||||
}
|
||||
|
||||
auto prepare_rect = [&](Screen& screen, const Gfx::IntRect& rect) {
|
||||
auto& screen_data = m_screen_data[screen.index()];
|
||||
dbgln_if(COMPOSE_DEBUG, " -> flush opaque: {}", rect);
|
||||
VERIFY(!flush_rects.intersects(rect));
|
||||
VERIFY(!flush_transparent_rects.intersects(rect));
|
||||
flush_rects.add(rect);
|
||||
check_restore_cursor_back(rect);
|
||||
VERIFY(!screen_data.m_flush_rects.intersects(rect));
|
||||
VERIFY(!screen_data.m_flush_transparent_rects.intersects(rect));
|
||||
screen_data.m_flush_rects.add(rect);
|
||||
check_restore_cursor_back(screen, rect);
|
||||
};
|
||||
|
||||
auto prepare_transparency_rect = [&](const Gfx::IntRect& rect) {
|
||||
auto prepare_transparency_rect = [&](Screen& screen, const Gfx::IntRect& rect) {
|
||||
auto& screen_data = m_screen_data[screen.index()];
|
||||
dbgln_if(COMPOSE_DEBUG, " -> flush transparent: {}", rect);
|
||||
VERIFY(!flush_rects.intersects(rect));
|
||||
for (auto& r : flush_transparent_rects.rects()) {
|
||||
VERIFY(!screen_data.m_flush_rects.intersects(rect));
|
||||
for (auto& r : screen_data.m_flush_transparent_rects.rects()) {
|
||||
if (r == rect)
|
||||
return;
|
||||
}
|
||||
|
||||
flush_transparent_rects.add(rect);
|
||||
check_restore_cursor_back(rect);
|
||||
screen_data.m_flush_transparent_rects.add(rect);
|
||||
check_restore_cursor_back(screen, rect);
|
||||
};
|
||||
|
||||
if (!m_cursor_back_bitmap || m_invalidated_cursor)
|
||||
check_restore_cursor_back(cursor_rect);
|
||||
if (!m_screen_data[cursor_screen.index()].m_cursor_back_bitmap || m_invalidated_cursor)
|
||||
check_restore_cursor_back(cursor_screen, cursor_rect);
|
||||
|
||||
auto paint_wallpaper = [&](Gfx::Painter& painter, const Gfx::IntRect& rect) {
|
||||
auto paint_wallpaper = [&](Screen& screen, Gfx::Painter& painter, const Gfx::IntRect& rect, const Gfx::IntRect& screen_rect) {
|
||||
// FIXME: If the wallpaper is opaque and covers the whole rect, no need to fill with color!
|
||||
painter.fill_rect(rect, background_color);
|
||||
if (m_wallpaper) {
|
||||
if (m_wallpaper_mode == WallpaperMode::Center) {
|
||||
Gfx::IntPoint offset { (ws.width() - m_wallpaper->width()) / 2, (ws.height() - m_wallpaper->height()) / 2 };
|
||||
painter.blit_offset(rect.location(), *m_wallpaper, rect, offset);
|
||||
Gfx::IntPoint offset { (screen.width() - m_wallpaper->width()) / 2, (screen.height() - m_wallpaper->height()) / 2 };
|
||||
painter.blit_offset(rect.location(), *m_wallpaper, rect.translated(-screen_rect.location()), offset);
|
||||
} else if (m_wallpaper_mode == WallpaperMode::Tile) {
|
||||
painter.draw_tiled_bitmap(rect, *m_wallpaper);
|
||||
} else if (m_wallpaper_mode == WallpaperMode::Stretch) {
|
||||
float hscale = (float)m_wallpaper->width() / (float)ws.width();
|
||||
float vscale = (float)m_wallpaper->height() / (float)ws.height();
|
||||
float hscale = (float)m_wallpaper->width() / (float)screen.width();
|
||||
float vscale = (float)m_wallpaper->height() / (float)screen.height();
|
||||
|
||||
// TODO: this may look ugly, we should scale to a backing bitmap and then blit
|
||||
auto src_rect = Gfx::FloatRect { rect.x() * hscale, rect.y() * vscale, rect.width() * hscale, rect.height() * vscale };
|
||||
auto relative_rect = rect.translated(-screen_rect.location());
|
||||
auto src_rect = Gfx::FloatRect { relative_rect.x() * hscale, relative_rect.y() * vscale, relative_rect.width() * hscale, relative_rect.height() * vscale };
|
||||
painter.draw_scaled_bitmap(rect, *m_wallpaper, src_rect);
|
||||
} else {
|
||||
VERIFY_NOT_REACHED();
|
||||
|
@ -243,29 +293,39 @@ void Compositor::compose()
|
|||
};
|
||||
|
||||
m_opaque_wallpaper_rects.for_each_intersected(dirty_screen_rects, [&](const Gfx::IntRect& render_rect) {
|
||||
dbgln_if(COMPOSE_DEBUG, " render wallpaper opaque: {}", render_rect);
|
||||
prepare_rect(render_rect);
|
||||
paint_wallpaper(back_painter, render_rect);
|
||||
Screen::for_each([&](auto& screen) {
|
||||
auto screen_rect = screen.rect();
|
||||
auto screen_render_rect = screen_rect.intersected(render_rect);
|
||||
if (!screen_render_rect.is_empty()) {
|
||||
auto& back_painter = *m_screen_data[screen.index()].m_back_painter;
|
||||
dbgln_if(COMPOSE_DEBUG, " render wallpaper opaque: {} on screen #{}", screen_render_rect, screen.index());
|
||||
prepare_rect(screen, render_rect);
|
||||
paint_wallpaper(screen, back_painter, render_rect, screen_rect);
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
auto compose_window = [&](Window& window) -> IterationDecision {
|
||||
auto frame_rect = window.frame().render_rect();
|
||||
if (!frame_rect.intersects(ws.rect()))
|
||||
if (window.screens().is_empty()) {
|
||||
// This window doesn't intersect with any screens, so there's nothing to render
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
auto frame_rect = window.frame().render_rect();
|
||||
auto window_rect = window.rect();
|
||||
auto frame_rects = frame_rect.shatter(window_rect);
|
||||
|
||||
dbgln_if(COMPOSE_DEBUG, " window {} frame rect: {}", window.title(), frame_rect);
|
||||
|
||||
RefPtr<Gfx::Bitmap> backing_store = window.backing_store();
|
||||
auto compose_window_rect = [&](Gfx::Painter& painter, const Gfx::IntRect& rect) {
|
||||
auto compose_window_rect = [&](Screen& screen, Gfx::Painter& painter, const Gfx::IntRect& rect) {
|
||||
if (!window.is_fullscreen()) {
|
||||
rect.for_each_intersected(frame_rects, [&](const Gfx::IntRect& intersected_rect) {
|
||||
Gfx::PainterStateSaver saver(painter);
|
||||
painter.add_clip_rect(intersected_rect);
|
||||
dbgln_if(COMPOSE_DEBUG, " render frame: {}", intersected_rect);
|
||||
window.frame().paint(painter, intersected_rect);
|
||||
window.frame().paint(screen, painter, intersected_rect);
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
@ -359,12 +419,18 @@ void Compositor::compose()
|
|||
auto& opaque_rects = window.opaque_rects();
|
||||
if (!opaque_rects.is_empty()) {
|
||||
opaque_rects.for_each_intersected(dirty_rects, [&](const Gfx::IntRect& render_rect) {
|
||||
dbgln_if(COMPOSE_DEBUG, " render opaque: {}", render_rect);
|
||||
for (auto* screen : window.screens()) {
|
||||
auto screen_render_rect = render_rect.intersected(screen->rect());
|
||||
if (screen_render_rect.is_empty())
|
||||
continue;
|
||||
dbgln_if(COMPOSE_DEBUG, " render opaque: {} on screen #{}", screen_render_rect, screen->index());
|
||||
|
||||
prepare_rect(render_rect);
|
||||
prepare_rect(*screen, screen_render_rect);
|
||||
auto& back_painter = *m_screen_data[screen->index()].m_back_painter;
|
||||
Gfx::PainterStateSaver saver(back_painter);
|
||||
back_painter.add_clip_rect(render_rect);
|
||||
compose_window_rect(back_painter, render_rect);
|
||||
back_painter.add_clip_rect(screen_render_rect);
|
||||
compose_window_rect(*screen, back_painter, screen_render_rect);
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
@ -374,22 +440,36 @@ void Compositor::compose()
|
|||
auto& transparency_wallpaper_rects = window.transparency_wallpaper_rects();
|
||||
if (!transparency_wallpaper_rects.is_empty()) {
|
||||
transparency_wallpaper_rects.for_each_intersected(dirty_rects, [&](const Gfx::IntRect& render_rect) {
|
||||
dbgln_if(COMPOSE_DEBUG, " render wallpaper: {}", render_rect);
|
||||
for (auto* screen : window.screens()) {
|
||||
auto screen_rect = screen->rect();
|
||||
auto screen_render_rect = render_rect.intersected(screen_rect);
|
||||
if (screen_render_rect.is_empty())
|
||||
continue;
|
||||
dbgln_if(COMPOSE_DEBUG, " render wallpaper: {} on screen #{}", screen_render_rect, screen->index());
|
||||
|
||||
prepare_transparency_rect(render_rect);
|
||||
paint_wallpaper(temp_painter, render_rect);
|
||||
auto& temp_painter = *m_screen_data[screen->index()].m_temp_painter;
|
||||
prepare_transparency_rect(*screen, screen_render_rect);
|
||||
paint_wallpaper(*screen, temp_painter, screen_render_rect, screen_rect);
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
auto& transparency_rects = window.transparency_rects();
|
||||
if (!transparency_rects.is_empty()) {
|
||||
transparency_rects.for_each_intersected(dirty_rects, [&](const Gfx::IntRect& render_rect) {
|
||||
dbgln_if(COMPOSE_DEBUG, " render transparent: {}", render_rect);
|
||||
for (auto* screen : window.screens()) {
|
||||
auto screen_rect = screen->rect();
|
||||
auto screen_render_rect = render_rect.intersected(screen_rect);
|
||||
if (screen_render_rect.is_empty())
|
||||
continue;
|
||||
dbgln_if(COMPOSE_DEBUG, " render transparent: {} on screen #{}", screen_render_rect, screen->index());
|
||||
|
||||
prepare_transparency_rect(render_rect);
|
||||
prepare_transparency_rect(*screen, screen_render_rect);
|
||||
auto& temp_painter = *m_screen_data[screen->index()].m_temp_painter;
|
||||
Gfx::PainterStateSaver saver(temp_painter);
|
||||
temp_painter.add_clip_rect(render_rect);
|
||||
compose_window_rect(temp_painter, render_rect);
|
||||
temp_painter.add_clip_rect(screen_render_rect);
|
||||
compose_window_rect(*screen, temp_painter, screen_render_rect);
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
@ -411,24 +491,35 @@ void Compositor::compose()
|
|||
|
||||
// Check that there are no overlapping transparent and opaque flush rectangles
|
||||
VERIFY(![&]() {
|
||||
bool is_overlapping = false;
|
||||
Screen::for_each([&](auto& screen) {
|
||||
auto& screen_data = m_screen_data[screen.index()];
|
||||
auto& flush_transparent_rects = screen_data.m_flush_transparent_rects;
|
||||
auto& flush_rects = screen_data.m_flush_rects;
|
||||
for (auto& rect_transparent : flush_transparent_rects.rects()) {
|
||||
for (auto& rect_opaque : flush_rects.rects()) {
|
||||
if (rect_opaque.intersects(rect_transparent)) {
|
||||
dbgln("Transparent rect {} overlaps opaque rect: {}: {}", rect_transparent, rect_opaque, rect_opaque.intersected(rect_transparent));
|
||||
return true;
|
||||
is_overlapping = true;
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
return is_overlapping;
|
||||
}());
|
||||
|
||||
// Copy anything rendered to the temporary buffer to the back buffer
|
||||
for (auto& rect : flush_transparent_rects.rects())
|
||||
back_painter.blit(rect.location(), *m_temp_bitmap, rect);
|
||||
Screen::for_each([&](auto& screen) {
|
||||
auto screen_rect = screen.rect();
|
||||
auto& screen_data = m_screen_data[screen.index()];
|
||||
for (auto& rect : screen_data.m_flush_transparent_rects.rects())
|
||||
screen_data.m_back_painter->blit(rect.location(), *screen_data.m_temp_bitmap, rect.translated(-screen_rect.location()));
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
Gfx::IntRect geometry_label_damage_rect;
|
||||
if (draw_geometry_label(geometry_label_damage_rect))
|
||||
flush_special_rects.add(geometry_label_damage_rect);
|
||||
draw_geometry_label(cursor_screen);
|
||||
}
|
||||
|
||||
m_invalidated_any = false;
|
||||
|
@ -438,6 +529,14 @@ void Compositor::compose()
|
|||
if (wm.dnd_client()) {
|
||||
auto dnd_rect = wm.dnd_rect();
|
||||
|
||||
Screen::for_each([&](auto& screen) {
|
||||
auto screen_rect = screen.rect();
|
||||
auto render_dnd_rect = screen_rect.intersected(dnd_rect);
|
||||
if (render_dnd_rect.is_empty())
|
||||
return IterationDecision::Continue;
|
||||
auto& screen_data = m_screen_data[screen.index()];
|
||||
auto& back_painter = *screen_data.m_back_painter;
|
||||
|
||||
// TODO: render once into a backing bitmap, then just blit...
|
||||
auto render_dnd = [&]() {
|
||||
back_painter.fill_rect(dnd_rect, wm.palette().selection().with_alpha(200));
|
||||
|
@ -454,18 +553,26 @@ void Compositor::compose()
|
|||
};
|
||||
|
||||
dirty_screen_rects.for_each_intersected(dnd_rect, [&](const Gfx::IntRect& render_rect) {
|
||||
auto screen_render_rect = render_rect.intersected(screen_rect);
|
||||
if (screen_render_rect.is_empty())
|
||||
return IterationDecision::Continue;
|
||||
Gfx::PainterStateSaver saver(back_painter);
|
||||
back_painter.add_clip_rect(render_rect);
|
||||
back_painter.add_clip_rect(screen_render_rect);
|
||||
render_dnd();
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
flush_transparent_rects.for_each_intersected(dnd_rect, [&](const Gfx::IntRect& render_rect) {
|
||||
screen_data.m_flush_transparent_rects.for_each_intersected(dnd_rect, [&](const Gfx::IntRect& render_rect) {
|
||||
auto screen_render_rect = render_rect.intersected(screen_rect);
|
||||
if (screen_render_rect.is_empty())
|
||||
return IterationDecision::Continue;
|
||||
Gfx::PainterStateSaver saver(back_painter);
|
||||
back_painter.add_clip_rect(render_rect);
|
||||
back_painter.add_clip_rect(screen_render_rect);
|
||||
render_dnd();
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
m_last_dnd_rect = dnd_rect;
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
} else {
|
||||
if (!m_last_dnd_rect.is_empty()) {
|
||||
invalidate_screen(m_last_dnd_rect);
|
||||
|
@ -473,42 +580,55 @@ void Compositor::compose()
|
|||
}
|
||||
}
|
||||
|
||||
run_animations(flush_special_rects);
|
||||
bool did_render_animation = false;
|
||||
Screen::for_each([&](auto& screen) {
|
||||
auto& screen_data = m_screen_data[screen.index()];
|
||||
did_render_animation |= render_animation_frame(screen, screen_data.m_flush_special_rects);
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
if (need_to_draw_cursor) {
|
||||
flush_rects.add(cursor_rect);
|
||||
if (cursor_rect != m_last_cursor_rect)
|
||||
flush_rects.add(m_last_cursor_rect);
|
||||
draw_cursor(cursor_rect);
|
||||
auto& screen_data = m_screen_data[cursor_screen.index()];
|
||||
screen_data.draw_cursor(cursor_screen, cursor_rect);
|
||||
screen_data.m_flush_rects.add(cursor_rect);
|
||||
if (previous_cursor_screen && cursor_rect != previous_cursor_rect)
|
||||
m_screen_data[previous_cursor_screen->index()].m_flush_rects.add(previous_cursor_rect);
|
||||
}
|
||||
|
||||
if (m_flash_flush) {
|
||||
for (auto& rect : flush_rects.rects())
|
||||
m_front_painter->fill_rect(rect, Color::Yellow);
|
||||
}
|
||||
Screen::for_each([&](auto& screen) {
|
||||
flush(screen);
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
if (m_screen_can_set_buffer)
|
||||
flip_buffers();
|
||||
|
||||
for (auto& rect : flush_rects.rects())
|
||||
flush(rect);
|
||||
for (auto& rect : flush_transparent_rects.rects())
|
||||
flush(rect);
|
||||
for (auto& rect : flush_special_rects.rects())
|
||||
flush(rect);
|
||||
if (did_render_animation)
|
||||
step_animations();
|
||||
}
|
||||
|
||||
void Compositor::flush(const Gfx::IntRect& a_rect)
|
||||
void Compositor::flush(Screen& screen)
|
||||
{
|
||||
auto rect = Gfx::IntRect::intersection(a_rect, Screen::the().rect());
|
||||
auto& screen_data = m_screen_data[screen.index()];
|
||||
if (m_flash_flush) {
|
||||
for (auto& rect : screen_data.m_flush_rects.rects())
|
||||
screen_data.m_front_painter->fill_rect(rect, Color::Yellow);
|
||||
}
|
||||
|
||||
if (screen_data.m_screen_can_set_buffer)
|
||||
screen_data.flip_buffers(screen);
|
||||
|
||||
auto screen_rect = screen.rect();
|
||||
auto do_flush = [&](const Gfx::IntRect& a_rect) {
|
||||
auto rect = Gfx::IntRect::intersection(a_rect, screen_rect);
|
||||
if (rect.is_empty())
|
||||
return;
|
||||
rect.translate_by(-screen_rect.location());
|
||||
|
||||
// Almost everything in Compositor is in logical coordinates, with the painters having
|
||||
// a scale applied. But this routine accesses the backbuffer pixels directly, so it
|
||||
// must work in physical coordinates.
|
||||
rect = rect * Screen::the().scale_factor();
|
||||
Gfx::RGBA32* front_ptr = m_front_bitmap->scanline(rect.y()) + rect.x();
|
||||
Gfx::RGBA32* back_ptr = m_back_bitmap->scanline(rect.y()) + rect.x();
|
||||
size_t pitch = m_back_bitmap->pitch();
|
||||
rect = rect * screen.scale_factor();
|
||||
Gfx::RGBA32* front_ptr = screen_data.m_front_bitmap->scanline(rect.y()) + rect.x();
|
||||
Gfx::RGBA32* back_ptr = screen_data.m_back_bitmap->scanline(rect.y()) + rect.x();
|
||||
size_t pitch = screen_data.m_back_bitmap->pitch();
|
||||
|
||||
// NOTE: The meaning of a flush depends on whether we can flip buffers or not.
|
||||
//
|
||||
|
@ -522,7 +642,7 @@ void Compositor::flush(const Gfx::IntRect& a_rect)
|
|||
Gfx::RGBA32* to_ptr;
|
||||
const Gfx::RGBA32* from_ptr;
|
||||
|
||||
if (m_screen_can_set_buffer) {
|
||||
if (screen_data.m_screen_can_set_buffer) {
|
||||
to_ptr = back_ptr;
|
||||
from_ptr = front_ptr;
|
||||
} else {
|
||||
|
@ -535,16 +655,23 @@ void Compositor::flush(const Gfx::IntRect& a_rect)
|
|||
from_ptr = (const Gfx::RGBA32*)((const u8*)from_ptr + pitch);
|
||||
to_ptr = (Gfx::RGBA32*)((u8*)to_ptr + pitch);
|
||||
}
|
||||
};
|
||||
for (auto& rect : screen_data.m_flush_rects.rects())
|
||||
do_flush(rect);
|
||||
for (auto& rect : screen_data.m_flush_transparent_rects.rects())
|
||||
do_flush(rect);
|
||||
for (auto& rect : screen_data.m_flush_special_rects.rects())
|
||||
do_flush(rect);
|
||||
}
|
||||
|
||||
void Compositor::invalidate_screen()
|
||||
{
|
||||
invalidate_screen(Screen::the().rect());
|
||||
invalidate_screen(Screen::bounding_rect());
|
||||
}
|
||||
|
||||
void Compositor::invalidate_screen(const Gfx::IntRect& screen_rect)
|
||||
{
|
||||
m_dirty_screen_rects.add(screen_rect.intersected(Screen::the().rect()));
|
||||
m_dirty_screen_rects.add(screen_rect.intersected(Screen::bounding_rect()));
|
||||
|
||||
if (m_invalidated_any)
|
||||
return;
|
||||
|
@ -623,19 +750,21 @@ bool Compositor::set_wallpaper(const String& path, Function<void(bool)>&& callba
|
|||
return true;
|
||||
}
|
||||
|
||||
void Compositor::flip_buffers()
|
||||
void Compositor::ScreenData::flip_buffers(Screen& screen)
|
||||
{
|
||||
VERIFY(m_screen_can_set_buffer);
|
||||
swap(m_front_bitmap, m_back_bitmap);
|
||||
swap(m_front_painter, m_back_painter);
|
||||
Screen::the().set_buffer(m_buffers_are_flipped ? 0 : 1);
|
||||
screen.set_buffer(m_buffers_are_flipped ? 0 : 1);
|
||||
m_buffers_are_flipped = !m_buffers_are_flipped;
|
||||
}
|
||||
|
||||
void Compositor::run_animations(Gfx::DisjointRectSet& flush_rects)
|
||||
static const int minimize_animation_steps = 10;
|
||||
|
||||
bool Compositor::render_animation_frame(Screen& screen, Gfx::DisjointRectSet& flush_rects)
|
||||
{
|
||||
static const int minimize_animation_steps = 10;
|
||||
auto& painter = *m_back_painter;
|
||||
bool did_render_any = false;
|
||||
auto& painter = *m_screen_data[screen.index()].m_back_painter;
|
||||
Gfx::PainterStateSaver saver(painter);
|
||||
painter.set_draw_op(Gfx::Painter::DrawOp::Invert);
|
||||
|
||||
|
@ -658,12 +787,24 @@ void Compositor::run_animations(Gfx::DisjointRectSet& flush_rects)
|
|||
from_rect.height() - (int)(height_delta_per_step * animation_index)
|
||||
};
|
||||
|
||||
dbgln_if(MINIMIZE_ANIMATION_DEBUG, "Minimize animation from {} to {} frame# {} {}", from_rect, to_rect, animation_index, rect);
|
||||
dbgln_if(MINIMIZE_ANIMATION_DEBUG, "Minimize animation from {} to {} frame# {} {} on screen #{}", from_rect, to_rect, animation_index, rect, screen.index());
|
||||
|
||||
painter.draw_rect(rect, Color::Transparent); // Color doesn't matter, we draw inverted
|
||||
flush_rects.add(rect);
|
||||
invalidate_screen(rect);
|
||||
|
||||
did_render_any = true;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
return did_render_any;
|
||||
}
|
||||
|
||||
void Compositor::step_animations()
|
||||
{
|
||||
WindowManager::the().window_stack().for_each_window([&](Window& window) {
|
||||
if (window.in_minimize_animation()) {
|
||||
window.step_minimize_animation();
|
||||
if (window.minimize_animation_index() >= minimize_animation_steps)
|
||||
window.end_minimize_animation();
|
||||
|
@ -672,33 +813,18 @@ void Compositor::run_animations(Gfx::DisjointRectSet& flush_rects)
|
|||
});
|
||||
}
|
||||
|
||||
bool Compositor::set_resolution(int desired_width, int desired_height, int scale_factor)
|
||||
void Compositor::screen_resolution_changed()
|
||||
{
|
||||
auto screen_rect = Screen::the().rect();
|
||||
if (screen_rect.width() == desired_width && screen_rect.height() == desired_height && Screen::the().scale_factor() == scale_factor)
|
||||
return true;
|
||||
|
||||
// Make sure it's impossible to set an invalid resolution
|
||||
if (!(desired_width >= 640 && desired_height >= 480 && scale_factor >= 1)) {
|
||||
dbgln("Compositor: Tried to set invalid resolution: {}x{}", desired_width, desired_height);
|
||||
return false;
|
||||
}
|
||||
|
||||
int old_scale_factor = Screen::the().scale_factor();
|
||||
bool success = Screen::the().set_resolution(desired_width, desired_height, scale_factor);
|
||||
if (success && old_scale_factor != scale_factor)
|
||||
WindowManager::the().reload_icon_bitmaps_after_scale_change();
|
||||
init_bitmaps();
|
||||
invalidate_occlusions();
|
||||
compose();
|
||||
return success;
|
||||
}
|
||||
|
||||
Gfx::IntRect Compositor::current_cursor_rect() const
|
||||
{
|
||||
auto& wm = WindowManager::the();
|
||||
auto& current_cursor = m_current_cursor ? *m_current_cursor : wm.active_cursor();
|
||||
return { Screen::the().cursor_location().translated(-current_cursor.params().hotspot()), current_cursor.size() };
|
||||
return { ScreenInput::the().cursor_location().translated(-current_cursor.params().hotspot()), current_cursor.size() };
|
||||
}
|
||||
|
||||
void Compositor::invalidate_cursor(bool compose_immediately)
|
||||
|
@ -714,13 +840,13 @@ void Compositor::invalidate_cursor(bool compose_immediately)
|
|||
start_compose_async_timer();
|
||||
}
|
||||
|
||||
bool Compositor::draw_geometry_label(Gfx::IntRect& geometry_label_damage_rect)
|
||||
void Compositor::draw_geometry_label(Screen& screen)
|
||||
{
|
||||
auto& wm = WindowManager::the();
|
||||
auto* window_being_moved_or_resized = wm.m_move_window ? wm.m_move_window.ptr() : (wm.m_resize_window ? wm.m_resize_window.ptr() : nullptr);
|
||||
if (!window_being_moved_or_resized) {
|
||||
m_last_geometry_label_damage_rect = {};
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
auto geometry_string = window_being_moved_or_resized->rect().to_string();
|
||||
if (!window_being_moved_or_resized->size_increment().is_null()) {
|
||||
|
@ -731,7 +857,7 @@ bool Compositor::draw_geometry_label(Gfx::IntRect& geometry_label_damage_rect)
|
|||
|
||||
auto geometry_label_rect = Gfx::IntRect { 0, 0, wm.font().width(geometry_string) + 16, wm.font().glyph_height() + 10 };
|
||||
geometry_label_rect.center_within(window_being_moved_or_resized->rect());
|
||||
auto desktop_rect = wm.desktop_rect();
|
||||
auto desktop_rect = wm.desktop_rect(screen);
|
||||
if (geometry_label_rect.left() < desktop_rect.left())
|
||||
geometry_label_rect.set_left(desktop_rect.left());
|
||||
if (geometry_label_rect.top() < desktop_rect.top())
|
||||
|
@ -741,14 +867,17 @@ bool Compositor::draw_geometry_label(Gfx::IntRect& geometry_label_damage_rect)
|
|||
if (geometry_label_rect.bottom() > desktop_rect.bottom())
|
||||
geometry_label_rect.set_bottom_without_resize(desktop_rect.bottom());
|
||||
|
||||
auto& back_painter = *m_back_painter;
|
||||
auto& screen_data = m_screen_data[screen.index()];
|
||||
auto& back_painter = *screen_data.m_back_painter;
|
||||
auto geometry_label_damage_rect = geometry_label_rect.inflated(2, 2);
|
||||
Gfx::PainterStateSaver saver(back_painter);
|
||||
back_painter.add_clip_rect(geometry_label_damage_rect);
|
||||
|
||||
back_painter.fill_rect(geometry_label_rect.translated(1, 1), Color(Color::Black).with_alpha(80));
|
||||
Gfx::StylePainter::paint_button(back_painter, geometry_label_rect.translated(-1, -1), wm.palette(), Gfx::ButtonStyle::Normal, false);
|
||||
back_painter.draw_text(geometry_label_rect.translated(-1, -1), geometry_string, Gfx::TextAlignment::Center, wm.palette().window_text());
|
||||
|
||||
geometry_label_damage_rect = geometry_label_rect.inflated(2, 2);
|
||||
m_last_geometry_label_damage_rect = geometry_label_damage_rect;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Compositor::change_cursor(const Cursor* cursor)
|
||||
|
@ -774,28 +903,34 @@ void Compositor::change_cursor(const Cursor* cursor)
|
|||
}
|
||||
}
|
||||
|
||||
void Compositor::draw_cursor(const Gfx::IntRect& cursor_rect)
|
||||
void Compositor::ScreenData::draw_cursor(Screen& screen, const Gfx::IntRect& cursor_rect)
|
||||
{
|
||||
auto& wm = WindowManager::the();
|
||||
|
||||
if (!m_cursor_back_bitmap || m_cursor_back_bitmap->size() != cursor_rect.size() || m_cursor_back_bitmap->scale() != Screen::the().scale_factor()) {
|
||||
m_cursor_back_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, cursor_rect.size(), Screen::the().scale_factor());
|
||||
if (!m_cursor_back_bitmap || m_cursor_back_bitmap->size() != cursor_rect.size() || m_cursor_back_bitmap->scale() != screen.scale_factor()) {
|
||||
m_cursor_back_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, cursor_rect.size(), screen.scale_factor());
|
||||
m_cursor_back_painter = make<Gfx::Painter>(*m_cursor_back_bitmap);
|
||||
}
|
||||
|
||||
auto& current_cursor = m_current_cursor ? *m_current_cursor : wm.active_cursor();
|
||||
m_cursor_back_painter->blit({ 0, 0 }, *m_back_bitmap, current_cursor.rect().translated(cursor_rect.location()).intersected(Screen::the().rect()));
|
||||
m_back_painter->blit(cursor_rect.location(), current_cursor.bitmap(), current_cursor.source_rect(m_current_cursor_frame));
|
||||
auto& compositor = Compositor::the();
|
||||
auto& current_cursor = compositor.m_current_cursor ? *compositor.m_current_cursor : wm.active_cursor();
|
||||
auto screen_rect = screen.rect();
|
||||
m_cursor_back_painter->blit({ 0, 0 }, *m_back_bitmap, current_cursor.rect().translated(cursor_rect.location()).intersected(screen_rect).translated(-screen_rect.location()));
|
||||
m_back_painter->blit(cursor_rect.location(), current_cursor.bitmap(), current_cursor.source_rect(compositor.m_current_cursor_frame));
|
||||
m_last_cursor_rect = cursor_rect;
|
||||
VERIFY(compositor.m_current_cursor_screen == &screen);
|
||||
m_cursor_back_is_valid = true;
|
||||
}
|
||||
|
||||
void Compositor::restore_cursor_back()
|
||||
bool Compositor::ScreenData::restore_cursor_back(Screen& screen, Gfx::IntRect& last_cursor_rect)
|
||||
{
|
||||
if (!m_cursor_back_bitmap || m_cursor_back_bitmap->scale() != m_back_bitmap->scale())
|
||||
return;
|
||||
if (!m_cursor_back_is_valid || !m_cursor_back_bitmap || m_cursor_back_bitmap->scale() != m_back_bitmap->scale())
|
||||
return false;
|
||||
|
||||
auto last_cursor_rect = m_last_cursor_rect.intersected(Screen::the().rect());
|
||||
last_cursor_rect = m_last_cursor_rect.intersected(screen.rect());
|
||||
m_back_painter->blit(last_cursor_rect.location(), *m_cursor_back_bitmap, { { 0, 0 }, last_cursor_rect.size() });
|
||||
m_cursor_back_is_valid = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Compositor::notify_display_links()
|
||||
|
@ -863,14 +998,17 @@ void Compositor::recompute_occlusions()
|
|||
|
||||
dbgln_if(OCCLUSIONS_DEBUG, "OCCLUSIONS:");
|
||||
|
||||
auto screen_rect = Screen::the().rect();
|
||||
|
||||
auto& main_screen = Screen::main();
|
||||
if (auto* fullscreen_window = wm.active_fullscreen_window()) {
|
||||
// TODO: support fullscreen windows on all screens
|
||||
auto screen_rect = main_screen.rect();
|
||||
WindowManager::the().window_stack().for_each_visible_window_from_front_to_back([&](Window& w) {
|
||||
auto& visible_opaque = w.opaque_rects();
|
||||
auto& transparency_rects = w.transparency_rects();
|
||||
auto& transparency_wallpaper_rects = w.transparency_wallpaper_rects();
|
||||
w.screens().clear_with_capacity();
|
||||
if (&w == fullscreen_window) {
|
||||
w.screens().append(&main_screen);
|
||||
if (w.is_opaque()) {
|
||||
visible_opaque = screen_rect;
|
||||
transparency_rects.clear();
|
||||
|
@ -890,28 +1028,39 @@ void Compositor::recompute_occlusions()
|
|||
|
||||
m_opaque_wallpaper_rects.clear();
|
||||
} else {
|
||||
Gfx::DisjointRectSet visible_rects(screen_rect);
|
||||
Gfx::DisjointRectSet visible_rects;
|
||||
visible_rects.add_many(Screen::rects());
|
||||
bool have_transparent = false;
|
||||
WindowManager::the().window_stack().for_each_visible_window_from_front_to_back([&](Window& w) {
|
||||
w.transparency_wallpaper_rects().clear();
|
||||
auto& visible_opaque = w.opaque_rects();
|
||||
visible_opaque.clear();
|
||||
auto& transparency_rects = w.transparency_rects();
|
||||
if (w.is_minimized()) {
|
||||
visible_opaque.clear();
|
||||
transparency_rects.clear();
|
||||
w.screens().clear_with_capacity();
|
||||
if (w.is_minimized())
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
|
||||
auto transparent_render_rects = w.frame().transparent_render_rects().intersected(screen_rect);
|
||||
auto opaque_render_rects = w.frame().opaque_render_rects().intersected(screen_rect);
|
||||
if (transparent_render_rects.is_empty() && opaque_render_rects.is_empty()) {
|
||||
visible_opaque.clear();
|
||||
transparency_rects.clear();
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
|
||||
visible_opaque = visible_rects.intersected(opaque_render_rects);
|
||||
auto transparent_frame_render_rects = w.frame().transparent_render_rects();
|
||||
auto opaque_frame_render_rects = w.frame().opaque_render_rects();
|
||||
Gfx::DisjointRectSet visible_opaque_rects;
|
||||
Screen::for_each([&](auto& screen) {
|
||||
auto screen_rect = screen.rect();
|
||||
if (auto transparent_render_rects = transparent_frame_render_rects.intersected(screen_rect); !transparent_render_rects.is_empty()) {
|
||||
if (transparency_rects.is_empty())
|
||||
transparency_rects = move(transparent_render_rects);
|
||||
else
|
||||
transparency_rects.add(transparent_render_rects);
|
||||
}
|
||||
if (auto opaque_render_rects = opaque_frame_render_rects.intersected(screen_rect); !opaque_render_rects.is_empty()) {
|
||||
if (visible_opaque_rects.is_empty())
|
||||
visible_opaque_rects = move(opaque_render_rects);
|
||||
else
|
||||
visible_opaque_rects.add(opaque_render_rects);
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
visible_opaque = visible_rects.intersected(visible_opaque_rects);
|
||||
|
||||
auto render_rect = w.frame().render_rect();
|
||||
|
||||
|
@ -967,8 +1116,31 @@ void Compositor::recompute_occlusions()
|
|||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
bool have_opaque = !visible_opaque.is_empty();
|
||||
if (!transparency_rects.is_empty())
|
||||
have_transparent = true;
|
||||
if (have_transparent || have_opaque) {
|
||||
// Figure out what screens this window is rendered on
|
||||
// We gather this information so we can more quickly
|
||||
// render the window on each of the screens that it
|
||||
// needs to be rendered on.
|
||||
Screen::for_each([&](auto& screen) {
|
||||
auto screen_rect = screen.rect();
|
||||
for (auto& r : visible_opaque.rects()) {
|
||||
if (r.intersects(screen_rect)) {
|
||||
w.screens().append(&screen);
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
}
|
||||
for (auto& r : transparency_rects.rects()) {
|
||||
if (r.intersects(screen_rect)) {
|
||||
w.screens().append(&screen);
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
VERIFY(!visible_opaque.intersects(transparency_rects));
|
||||
|
||||
|
@ -1008,12 +1180,14 @@ void Compositor::recompute_occlusions()
|
|||
}
|
||||
|
||||
wm.window_stack().for_each_visible_window_from_back_to_front([&](Window& w) {
|
||||
auto window_frame_rect = w.frame().render_rect().intersected(screen_rect);
|
||||
if (w.is_minimized() || window_frame_rect.is_empty())
|
||||
auto window_frame_rect = w.frame().render_rect();
|
||||
if (w.is_minimized() || window_frame_rect.is_empty() || w.screens().is_empty())
|
||||
return IterationDecision::Continue;
|
||||
|
||||
if constexpr (OCCLUSIONS_DEBUG) {
|
||||
dbgln(" Window {} frame rect: {}", w.title(), window_frame_rect);
|
||||
dbgln(" Window {} frame rect: {} rendered on screens: {}", w.title(), window_frame_rect, w.screens().size());
|
||||
for (auto& s : w.screens())
|
||||
dbgln(" screen: #{}", s->index());
|
||||
for (auto& r : w.opaque_rects().rects())
|
||||
dbgln(" opaque: {}", r);
|
||||
for (auto& r : w.transparency_wallpaper_rects().rects())
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <LibCore/Object.h>
|
||||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/DisjointRectSet.h>
|
||||
#include <WindowServer/Screen.h>
|
||||
|
||||
namespace WindowServer {
|
||||
|
||||
|
@ -36,7 +37,7 @@ public:
|
|||
void invalidate_screen();
|
||||
void invalidate_screen(const Gfx::IntRect&);
|
||||
|
||||
bool set_resolution(int desired_width, int desired_height, int scale_factor);
|
||||
void screen_resolution_changed();
|
||||
|
||||
bool set_background_color(const String& background_color);
|
||||
|
||||
|
@ -55,46 +56,58 @@ public:
|
|||
|
||||
void did_construct_window_manager(Badge<WindowManager>);
|
||||
|
||||
const Gfx::Bitmap& front_bitmap_for_screenshot(Badge<ClientConnection>) const { return *m_front_bitmap; }
|
||||
const Gfx::Bitmap& front_bitmap_for_screenshot(Badge<ClientConnection>, Screen&) const;
|
||||
|
||||
private:
|
||||
Compositor();
|
||||
void init_bitmaps();
|
||||
void flip_buffers();
|
||||
void flush(const Gfx::IntRect&);
|
||||
void run_animations(Gfx::DisjointRectSet&);
|
||||
bool render_animation_frame(Screen&, Gfx::DisjointRectSet&);
|
||||
void step_animations();
|
||||
void notify_display_links();
|
||||
void start_compose_async_timer();
|
||||
void recompute_occlusions();
|
||||
bool any_opaque_window_above_this_one_contains_rect(const Window&, const Gfx::IntRect&);
|
||||
void change_cursor(const Cursor*);
|
||||
void draw_cursor(const Gfx::IntRect&);
|
||||
void restore_cursor_back();
|
||||
bool draw_geometry_label(Gfx::IntRect&);
|
||||
void draw_geometry_label(Screen&);
|
||||
void flush(Screen&);
|
||||
|
||||
RefPtr<Core::Timer> m_compose_timer;
|
||||
RefPtr<Core::Timer> m_immediate_compose_timer;
|
||||
bool m_flash_flush { false };
|
||||
bool m_buffers_are_flipped { false };
|
||||
bool m_screen_can_set_buffer { false };
|
||||
bool m_occlusions_dirty { true };
|
||||
bool m_invalidated_any { true };
|
||||
bool m_invalidated_window { false };
|
||||
bool m_invalidated_cursor { false };
|
||||
|
||||
struct ScreenData {
|
||||
RefPtr<Gfx::Bitmap> m_front_bitmap;
|
||||
RefPtr<Gfx::Bitmap> m_back_bitmap;
|
||||
RefPtr<Gfx::Bitmap> m_temp_bitmap;
|
||||
OwnPtr<Gfx::Painter> m_back_painter;
|
||||
OwnPtr<Gfx::Painter> m_front_painter;
|
||||
OwnPtr<Gfx::Painter> m_temp_painter;
|
||||
RefPtr<Gfx::Bitmap> m_cursor_back_bitmap;
|
||||
OwnPtr<Gfx::Painter> m_cursor_back_painter;
|
||||
Gfx::IntRect m_last_cursor_rect;
|
||||
bool m_buffers_are_flipped { false };
|
||||
bool m_screen_can_set_buffer { false };
|
||||
bool m_cursor_back_is_valid { false };
|
||||
|
||||
Gfx::DisjointRectSet m_flush_rects;
|
||||
Gfx::DisjointRectSet m_flush_transparent_rects;
|
||||
Gfx::DisjointRectSet m_flush_special_rects;
|
||||
|
||||
void init_bitmaps(Screen&);
|
||||
void flip_buffers(Screen&);
|
||||
void draw_cursor(Screen&, const Gfx::IntRect&);
|
||||
bool restore_cursor_back(Screen&, Gfx::IntRect&);
|
||||
};
|
||||
friend class ScreenData;
|
||||
Vector<ScreenData, default_screen_count> m_screen_data;
|
||||
|
||||
Gfx::DisjointRectSet m_dirty_screen_rects;
|
||||
Gfx::DisjointRectSet m_opaque_wallpaper_rects;
|
||||
|
||||
RefPtr<Gfx::Bitmap> m_cursor_back_bitmap;
|
||||
OwnPtr<Gfx::Painter> m_cursor_back_painter;
|
||||
Gfx::IntRect m_last_cursor_rect;
|
||||
Gfx::IntRect m_last_dnd_rect;
|
||||
Gfx::IntRect m_last_geometry_label_damage_rect;
|
||||
|
||||
|
@ -103,6 +116,7 @@ private:
|
|||
RefPtr<Gfx::Bitmap> m_wallpaper;
|
||||
|
||||
const Cursor* m_current_cursor { nullptr };
|
||||
Screen* m_current_cursor_screen { nullptr };
|
||||
unsigned m_current_cursor_frame { 0 };
|
||||
RefPtr<Core::Timer> m_cursor_timer;
|
||||
|
||||
|
|
|
@ -81,9 +81,9 @@ EventLoop::~EventLoop()
|
|||
|
||||
void EventLoop::drain_mouse()
|
||||
{
|
||||
auto& screen = Screen::the();
|
||||
auto& screen_input = ScreenInput::the();
|
||||
MousePacket state;
|
||||
state.buttons = screen.mouse_button_state();
|
||||
state.buttons = screen_input.mouse_button_state();
|
||||
unsigned buttons = state.buttons;
|
||||
MousePacket packets[32];
|
||||
|
||||
|
@ -114,7 +114,7 @@ void EventLoop::drain_mouse()
|
|||
if (buttons != state.buttons) {
|
||||
state.buttons = buttons;
|
||||
dbgln_if(WSMESSAGELOOP_DEBUG, "EventLoop: Mouse Button Event");
|
||||
screen.on_receive_mouse_data(state);
|
||||
screen_input.on_receive_mouse_data(state);
|
||||
if (state.is_relative) {
|
||||
state.x = 0;
|
||||
state.y = 0;
|
||||
|
@ -123,21 +123,21 @@ void EventLoop::drain_mouse()
|
|||
}
|
||||
}
|
||||
if (state.is_relative && (state.x || state.y || state.z))
|
||||
screen.on_receive_mouse_data(state);
|
||||
screen_input.on_receive_mouse_data(state);
|
||||
if (!state.is_relative)
|
||||
screen.on_receive_mouse_data(state);
|
||||
screen_input.on_receive_mouse_data(state);
|
||||
}
|
||||
|
||||
void EventLoop::drain_keyboard()
|
||||
{
|
||||
auto& screen = Screen::the();
|
||||
auto& screen_input = ScreenInput::the();
|
||||
for (;;) {
|
||||
::KeyEvent event;
|
||||
ssize_t nread = read(m_keyboard_fd, (u8*)&event, sizeof(::KeyEvent));
|
||||
if (nread == 0)
|
||||
break;
|
||||
VERIFY(nread == sizeof(::KeyEvent));
|
||||
screen.on_receive_keyboard_data(event);
|
||||
screen_input.on_receive_keyboard_data(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -136,7 +136,7 @@ Window& Menu::ensure_menu_window()
|
|||
next_item_location.translate_by(0, height);
|
||||
}
|
||||
|
||||
int window_height_available = Screen::the().height() - frame_thickness() * 2;
|
||||
int window_height_available = Screen::main().height() - frame_thickness() * 2; // TODO: we don't know yet on what screen!
|
||||
int max_window_height = (window_height_available / item_height()) * item_height() + frame_thickness() * 2;
|
||||
int content_height = m_items.is_empty() ? 0 : (m_items.last().rect().bottom() + 1) + frame_thickness();
|
||||
int window_height = min(max_window_height, content_height);
|
||||
|
@ -584,14 +584,15 @@ void Menu::do_popup(const Gfx::IntPoint& position, bool make_input, bool as_subm
|
|||
redraw_if_theme_changed();
|
||||
|
||||
const int margin = 30;
|
||||
auto& screen = Screen::closest_to_location(position);
|
||||
Gfx::IntPoint adjusted_pos = position;
|
||||
|
||||
if (adjusted_pos.x() + window.width() >= Screen::the().width() - margin) {
|
||||
if (adjusted_pos.x() + window.width() >= screen.width() - margin) {
|
||||
adjusted_pos = adjusted_pos.translated(-window.width(), 0);
|
||||
} else {
|
||||
adjusted_pos.set_x(adjusted_pos.x() + 1);
|
||||
}
|
||||
if (adjusted_pos.y() + window.height() >= Screen::the().height() - margin) {
|
||||
if (adjusted_pos.y() + window.height() >= screen.height() - margin) {
|
||||
adjusted_pos = adjusted_pos.translated(0, -min(window.height(), adjusted_pos.y()));
|
||||
if (as_submenu)
|
||||
adjusted_pos = adjusted_pos.translated(0, item_height());
|
||||
|
|
|
@ -19,21 +19,37 @@
|
|||
|
||||
namespace WindowServer {
|
||||
|
||||
static Screen* s_the;
|
||||
NonnullOwnPtrVector<Screen, default_screen_count> Screen::s_screens;
|
||||
Screen* Screen::s_main_screen { nullptr };
|
||||
Gfx::IntRect Screen::s_bounding_screens_rect {};
|
||||
|
||||
Screen& Screen::the()
|
||||
ScreenInput& ScreenInput::the()
|
||||
{
|
||||
VERIFY(s_the);
|
||||
return *s_the;
|
||||
static ScreenInput s_the;
|
||||
return s_the;
|
||||
}
|
||||
|
||||
Screen::Screen(unsigned desired_width, unsigned desired_height, int scale_factor)
|
||||
Screen& ScreenInput::cursor_location_screen()
|
||||
{
|
||||
VERIFY(!s_the);
|
||||
s_the = this;
|
||||
m_framebuffer_fd = open("/dev/fb0", O_RDWR | O_CLOEXEC);
|
||||
auto* screen = Screen::find_by_location(m_cursor_location);
|
||||
VERIFY(screen);
|
||||
return *screen;
|
||||
}
|
||||
|
||||
const Screen& ScreenInput::cursor_location_screen() const
|
||||
{
|
||||
auto* screen = Screen::find_by_location(m_cursor_location);
|
||||
VERIFY(screen);
|
||||
return *screen;
|
||||
}
|
||||
|
||||
Screen::Screen(const String& device, const Gfx::IntRect& virtual_rect, int scale_factor)
|
||||
: m_virtual_rect(virtual_rect)
|
||||
, m_scale_factor(scale_factor)
|
||||
{
|
||||
m_framebuffer_fd = open(device.characters(), O_RDWR | O_CLOEXEC);
|
||||
if (m_framebuffer_fd < 0) {
|
||||
perror("failed to open /dev/fb0");
|
||||
perror(String::formatted("failed to open {}", device).characters());
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
|
@ -41,8 +57,10 @@ Screen::Screen(unsigned desired_width, unsigned desired_height, int scale_factor
|
|||
m_can_set_buffer = true;
|
||||
}
|
||||
|
||||
set_resolution(desired_width, desired_height, scale_factor);
|
||||
m_physical_cursor_location = physical_rect().center();
|
||||
// If the cursor is not in a valid screen (yet), force it into one
|
||||
dbgln("Screen() current physical cursor location: {} rect: {}", ScreenInput::the().cursor_location(), rect());
|
||||
if (!find_by_location(ScreenInput::the().cursor_location()))
|
||||
ScreenInput::the().set_cursor_location(rect().center());
|
||||
}
|
||||
|
||||
Screen::~Screen()
|
||||
|
@ -50,35 +68,84 @@ Screen::~Screen()
|
|||
close(m_framebuffer_fd);
|
||||
}
|
||||
|
||||
bool Screen::set_resolution(int width, int height, int new_scale_factor)
|
||||
void Screen::init()
|
||||
{
|
||||
do_set_resolution(true, m_virtual_rect.width(), m_virtual_rect.height(), m_scale_factor);
|
||||
}
|
||||
|
||||
Screen& Screen::closest_to_rect(const Gfx::IntRect& rect)
|
||||
{
|
||||
Screen* best_screen = nullptr;
|
||||
int best_area = 0;
|
||||
for (auto& screen : s_screens) {
|
||||
auto r = screen.rect().intersected(rect);
|
||||
int area = r.width() * r.height();
|
||||
if (!best_screen || area > best_area) {
|
||||
best_screen = &screen;
|
||||
best_area = area;
|
||||
}
|
||||
}
|
||||
if (!best_screen) {
|
||||
// TODO: try to find the best screen in close proximity
|
||||
best_screen = &Screen::main();
|
||||
}
|
||||
return *best_screen;
|
||||
}
|
||||
|
||||
Screen& Screen::closest_to_location(const Gfx::IntPoint& point)
|
||||
{
|
||||
for (auto& screen : s_screens) {
|
||||
if (screen.rect().contains(point))
|
||||
return screen;
|
||||
}
|
||||
// TODO: guess based on how close the point is to the next screen rectangle
|
||||
return Screen::main();
|
||||
}
|
||||
|
||||
void Screen::update_bounding_rect()
|
||||
{
|
||||
if (!s_screens.is_empty()) {
|
||||
s_bounding_screens_rect = s_screens[0].rect();
|
||||
for (size_t i = 1; i < s_screens.size(); i++)
|
||||
s_bounding_screens_rect = s_bounding_screens_rect.united(s_screens[i].rect());
|
||||
} else {
|
||||
s_bounding_screens_rect = {};
|
||||
}
|
||||
}
|
||||
|
||||
bool Screen::do_set_resolution(bool initial, int width, int height, int new_scale_factor)
|
||||
{
|
||||
// Remember the screen that the cursor is on. Make sure it stays on the same screen if we change its resolution...
|
||||
auto& screen_with_cursor = ScreenInput::the().cursor_location_screen();
|
||||
|
||||
int new_physical_width = width * new_scale_factor;
|
||||
int new_physical_height = height * new_scale_factor;
|
||||
if (physical_width() == new_physical_width && physical_height() == new_physical_height) {
|
||||
VERIFY(scale_factor() != new_scale_factor);
|
||||
on_change_resolution(m_pitch, physical_width(), physical_height(), new_scale_factor);
|
||||
if (!initial && physical_width() == new_physical_width && physical_height() == new_physical_height) {
|
||||
VERIFY(initial || scale_factor() != new_scale_factor);
|
||||
on_change_resolution(initial, m_pitch, physical_width(), physical_height(), new_scale_factor, screen_with_cursor);
|
||||
return true;
|
||||
}
|
||||
|
||||
FBResolution physical_resolution { 0, (unsigned)new_physical_width, (unsigned)new_physical_height };
|
||||
int rc = fb_set_resolution(m_framebuffer_fd, &physical_resolution);
|
||||
dbgln_if(WSSCREEN_DEBUG, "fb_set_resolution() - return code {}", rc);
|
||||
dbgln_if(WSSCREEN_DEBUG, "Screen #{}: fb_set_resolution() - return code {}", index(), rc);
|
||||
|
||||
if (rc == 0) {
|
||||
on_change_resolution(physical_resolution.pitch, physical_resolution.width, physical_resolution.height, new_scale_factor);
|
||||
on_change_resolution(initial, physical_resolution.pitch, physical_resolution.width, physical_resolution.height, new_scale_factor, screen_with_cursor);
|
||||
return true;
|
||||
}
|
||||
if (rc == -1) {
|
||||
dbgln("Invalid resolution {}x{}", width, height);
|
||||
on_change_resolution(physical_resolution.pitch, physical_resolution.width, physical_resolution.height, new_scale_factor);
|
||||
int err = errno;
|
||||
dbgln("Screen #{}: Failed to set resolution {}x{}: {}", index(), width, height, strerror(err));
|
||||
on_change_resolution(initial, physical_resolution.pitch, physical_resolution.width, physical_resolution.height, new_scale_factor, screen_with_cursor);
|
||||
return false;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
void Screen::on_change_resolution(int pitch, int new_physical_width, int new_physical_height, int new_scale_factor)
|
||||
void Screen::on_change_resolution(bool initial, int pitch, int new_physical_width, int new_physical_height, int new_scale_factor, Screen& screen_with_cursor)
|
||||
{
|
||||
if (physical_width() != new_physical_width || physical_height() != new_physical_height) {
|
||||
if (initial || physical_width() != new_physical_width || physical_height() != new_physical_height) {
|
||||
if (m_framebuffer) {
|
||||
size_t previous_size_in_bytes = m_size_in_bytes;
|
||||
int rc = munmap(m_framebuffer, previous_size_in_bytes);
|
||||
|
@ -93,11 +160,15 @@ void Screen::on_change_resolution(int pitch, int new_physical_width, int new_phy
|
|||
}
|
||||
|
||||
m_pitch = pitch;
|
||||
m_width = new_physical_width / new_scale_factor;
|
||||
m_height = new_physical_height / new_scale_factor;
|
||||
m_virtual_rect.set_width(new_physical_width / new_scale_factor);
|
||||
m_virtual_rect.set_height(new_physical_height / new_scale_factor);
|
||||
m_scale_factor = new_scale_factor;
|
||||
update_bounding_rect();
|
||||
|
||||
m_physical_cursor_location.constrain(physical_rect());
|
||||
if (this == &screen_with_cursor) {
|
||||
auto& screen_input = ScreenInput::the();
|
||||
screen_input.set_cursor_location(screen_input.cursor_location().constrained(rect()));
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::set_buffer(int index)
|
||||
|
@ -107,31 +178,35 @@ void Screen::set_buffer(int index)
|
|||
VERIFY(rc == 0);
|
||||
}
|
||||
|
||||
void Screen::set_acceleration_factor(double factor)
|
||||
void ScreenInput::set_acceleration_factor(double factor)
|
||||
{
|
||||
VERIFY(factor >= mouse_accel_min && factor <= mouse_accel_max);
|
||||
m_acceleration_factor = factor;
|
||||
}
|
||||
|
||||
void Screen::set_scroll_step_size(unsigned step_size)
|
||||
void ScreenInput::set_scroll_step_size(unsigned step_size)
|
||||
{
|
||||
VERIFY(step_size >= scroll_step_size_min);
|
||||
m_scroll_step_size = step_size;
|
||||
}
|
||||
|
||||
void Screen::on_receive_mouse_data(const MousePacket& packet)
|
||||
void ScreenInput::on_receive_mouse_data(const MousePacket& packet)
|
||||
{
|
||||
auto prev_location = m_physical_cursor_location / m_scale_factor;
|
||||
auto& current_screen = cursor_location_screen();
|
||||
auto prev_location = m_cursor_location;
|
||||
if (packet.is_relative) {
|
||||
m_physical_cursor_location.translate_by(packet.x * m_acceleration_factor, packet.y * m_acceleration_factor);
|
||||
dbgln_if(WSSCREEN_DEBUG, "Screen: New Relative mouse point @ {}", m_physical_cursor_location);
|
||||
m_cursor_location.translate_by(packet.x * m_acceleration_factor, packet.y * m_acceleration_factor);
|
||||
dbgln_if(WSSCREEN_DEBUG, "Screen: New Relative mouse point @ {}", m_cursor_location);
|
||||
} else {
|
||||
m_physical_cursor_location = { packet.x * physical_width() / 0xffff, packet.y * physical_height() / 0xffff };
|
||||
dbgln_if(WSSCREEN_DEBUG, "Screen: New Absolute mouse point @ {}", m_physical_cursor_location);
|
||||
m_cursor_location = { packet.x * current_screen.physical_width() / 0xffff, packet.y * current_screen.physical_height() / 0xffff };
|
||||
dbgln_if(WSSCREEN_DEBUG, "Screen: New Absolute mouse point @ {}", m_cursor_location);
|
||||
}
|
||||
|
||||
m_physical_cursor_location.constrain(physical_rect());
|
||||
auto new_location = m_physical_cursor_location / m_scale_factor;
|
||||
auto* moved_to_screen = Screen::find_by_location(m_cursor_location);
|
||||
if (!moved_to_screen) {
|
||||
m_cursor_location = m_cursor_location.constrained(current_screen.rect());
|
||||
moved_to_screen = ¤t_screen;
|
||||
}
|
||||
|
||||
unsigned buttons = packet.buttons;
|
||||
unsigned prev_buttons = m_mouse_button_state;
|
||||
|
@ -140,7 +215,7 @@ void Screen::on_receive_mouse_data(const MousePacket& packet)
|
|||
auto post_mousedown_or_mouseup_if_needed = [&](MouseButton button) {
|
||||
if (!(changed_buttons & (unsigned)button))
|
||||
return;
|
||||
auto message = make<MouseEvent>(buttons & (unsigned)button ? Event::MouseDown : Event::MouseUp, new_location, buttons, button, m_modifiers);
|
||||
auto message = make<MouseEvent>(buttons & (unsigned)button ? Event::MouseDown : Event::MouseUp, m_cursor_location, buttons, button, m_modifiers);
|
||||
Core::EventLoop::current().post_event(WindowManager::the(), move(message));
|
||||
};
|
||||
post_mousedown_or_mouseup_if_needed(MouseButton::Left);
|
||||
|
@ -148,23 +223,23 @@ void Screen::on_receive_mouse_data(const MousePacket& packet)
|
|||
post_mousedown_or_mouseup_if_needed(MouseButton::Middle);
|
||||
post_mousedown_or_mouseup_if_needed(MouseButton::Back);
|
||||
post_mousedown_or_mouseup_if_needed(MouseButton::Forward);
|
||||
if (new_location != prev_location) {
|
||||
auto message = make<MouseEvent>(Event::MouseMove, new_location, buttons, MouseButton::None, m_modifiers);
|
||||
if (m_cursor_location != prev_location) {
|
||||
auto message = make<MouseEvent>(Event::MouseMove, m_cursor_location, buttons, MouseButton::None, m_modifiers);
|
||||
if (WindowManager::the().dnd_client())
|
||||
message->set_mime_data(WindowManager::the().dnd_mime_data());
|
||||
Core::EventLoop::current().post_event(WindowManager::the(), move(message));
|
||||
}
|
||||
|
||||
if (packet.z) {
|
||||
auto message = make<MouseEvent>(Event::MouseWheel, new_location, buttons, MouseButton::None, m_modifiers, packet.z * m_scroll_step_size);
|
||||
auto message = make<MouseEvent>(Event::MouseWheel, m_cursor_location, buttons, MouseButton::None, m_modifiers, packet.z * m_scroll_step_size);
|
||||
Core::EventLoop::current().post_event(WindowManager::the(), move(message));
|
||||
}
|
||||
|
||||
if (new_location != prev_location)
|
||||
if (m_cursor_location != prev_location)
|
||||
Compositor::the().invalidate_cursor();
|
||||
}
|
||||
|
||||
void Screen::on_receive_keyboard_data(::KeyEvent kernel_event)
|
||||
void ScreenInput::on_receive_keyboard_data(::KeyEvent kernel_event)
|
||||
{
|
||||
m_modifiers = kernel_event.modifiers();
|
||||
auto message = make<KeyEvent>(kernel_event.is_press() ? Event::KeyDown : Event::KeyUp, kernel_event.key, kernel_event.code_point, kernel_event.modifiers(), kernel_event.scancode);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/NonnullOwnPtrVector.h>
|
||||
#include <Kernel/API/KeyCode.h>
|
||||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
|
@ -19,34 +20,17 @@ constexpr double mouse_accel_max = 3.5;
|
|||
constexpr double mouse_accel_min = 0.5;
|
||||
constexpr unsigned scroll_step_size_min = 1;
|
||||
|
||||
class Screen {
|
||||
// Most people will probably have 4 screens or less
|
||||
constexpr size_t default_screen_count = 4;
|
||||
|
||||
class Screen;
|
||||
|
||||
class ScreenInput {
|
||||
public:
|
||||
Screen(unsigned width, unsigned height, int scale_factor);
|
||||
~Screen();
|
||||
static ScreenInput& the();
|
||||
|
||||
bool set_resolution(int width, int height, int scale_factor);
|
||||
bool can_set_buffer() { return m_can_set_buffer; }
|
||||
void set_buffer(int index);
|
||||
|
||||
int physical_width() const { return width() * scale_factor(); }
|
||||
int physical_height() const { return height() * scale_factor(); }
|
||||
size_t pitch() const { return m_pitch; }
|
||||
|
||||
int width() const { return m_width; }
|
||||
int height() const { return m_height; }
|
||||
int scale_factor() const { return m_scale_factor; }
|
||||
|
||||
Gfx::RGBA32* scanline(int y);
|
||||
|
||||
static Screen& the();
|
||||
|
||||
Gfx::IntSize physical_size() const { return { physical_width(), physical_height() }; }
|
||||
Gfx::IntRect physical_rect() const { return { { 0, 0 }, physical_size() }; }
|
||||
|
||||
Gfx::IntSize size() const { return { width(), height() }; }
|
||||
Gfx::IntRect rect() const { return { { 0, 0 }, size() }; }
|
||||
|
||||
Gfx::IntPoint cursor_location() const { return m_physical_cursor_location / m_scale_factor; }
|
||||
Screen& cursor_location_screen();
|
||||
const Screen& cursor_location_screen() const;
|
||||
unsigned mouse_button_state() const { return m_mouse_button_state; }
|
||||
|
||||
double acceleration_factor() const { return m_acceleration_factor; }
|
||||
|
@ -58,8 +42,130 @@ public:
|
|||
void on_receive_mouse_data(const MousePacket&);
|
||||
void on_receive_keyboard_data(::KeyEvent);
|
||||
|
||||
Gfx::IntPoint cursor_location() const { return m_cursor_location; }
|
||||
void set_cursor_location(const Gfx::IntPoint point) { m_cursor_location = point; }
|
||||
|
||||
private:
|
||||
void on_change_resolution(int pitch, int physical_width, int physical_height, int scale_factor);
|
||||
Gfx::IntPoint m_cursor_location;
|
||||
unsigned m_mouse_button_state { 0 };
|
||||
unsigned m_modifiers { 0 };
|
||||
double m_acceleration_factor { 1.0 };
|
||||
unsigned m_scroll_step_size { 1 };
|
||||
};
|
||||
|
||||
class Screen {
|
||||
public:
|
||||
template<typename... Args>
|
||||
static Screen* create(Args&&... args)
|
||||
{
|
||||
auto screen = adopt_own(*new Screen(forward<Args>(args)...));
|
||||
if (!screen->is_opened())
|
||||
return nullptr;
|
||||
auto* screen_ptr = screen.ptr();
|
||||
s_screens.append(move(screen));
|
||||
update_indices();
|
||||
update_bounding_rect();
|
||||
if (!s_main_screen)
|
||||
s_main_screen = screen_ptr;
|
||||
screen_ptr->init();
|
||||
return screen_ptr;
|
||||
}
|
||||
~Screen();
|
||||
|
||||
static Screen& main()
|
||||
{
|
||||
VERIFY(s_main_screen);
|
||||
return *s_main_screen;
|
||||
}
|
||||
|
||||
static Screen& closest_to_rect(const Gfx::IntRect&);
|
||||
static Screen& closest_to_location(const Gfx::IntPoint&);
|
||||
|
||||
static Screen* find_by_index(size_t index)
|
||||
{
|
||||
if (index >= s_screens.size())
|
||||
return nullptr;
|
||||
return &s_screens[index];
|
||||
}
|
||||
|
||||
static Vector<Gfx::IntRect, 4> rects()
|
||||
{
|
||||
Vector<Gfx::IntRect, 4> rects;
|
||||
for (auto& screen : s_screens)
|
||||
rects.append(screen.rect());
|
||||
return rects;
|
||||
}
|
||||
|
||||
static Screen* find_by_location(const Gfx::IntPoint& point)
|
||||
{
|
||||
for (auto& screen : s_screens) {
|
||||
if (screen.rect().contains(point))
|
||||
return &screen;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static const Gfx::IntRect& bounding_rect() { return s_bounding_screens_rect; }
|
||||
|
||||
static size_t count() { return s_screens.size(); }
|
||||
size_t index() const { return m_index; }
|
||||
|
||||
template<typename F>
|
||||
static IterationDecision for_each(F f)
|
||||
{
|
||||
for (auto& screen : s_screens) {
|
||||
IterationDecision decision = f(screen);
|
||||
if (decision != IterationDecision::Continue)
|
||||
return decision;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
|
||||
void make_main_screen() { s_main_screen = this; }
|
||||
bool is_main_screen() const { return s_main_screen == this; }
|
||||
|
||||
template<typename... Args>
|
||||
bool set_resolution(Args&&... args)
|
||||
{
|
||||
return do_set_resolution(false, forward<Args>(args)...);
|
||||
}
|
||||
bool can_set_buffer() { return m_can_set_buffer; }
|
||||
void set_buffer(int index);
|
||||
|
||||
int physical_width() const { return width() * scale_factor(); }
|
||||
int physical_height() const { return height() * scale_factor(); }
|
||||
size_t pitch() const { return m_pitch; }
|
||||
|
||||
int width() const { return m_virtual_rect.width(); }
|
||||
int height() const { return m_virtual_rect.height(); }
|
||||
int scale_factor() const { return m_scale_factor; }
|
||||
|
||||
Gfx::RGBA32* scanline(int y);
|
||||
|
||||
Gfx::IntSize physical_size() const { return { physical_width(), physical_height() }; }
|
||||
|
||||
Gfx::IntSize size() const { return { m_virtual_rect.width(), m_virtual_rect.height() }; }
|
||||
Gfx::IntRect rect() const { return m_virtual_rect; }
|
||||
|
||||
private:
|
||||
Screen(const String& device, const Gfx::IntRect& virtual_rect, int scale_factor);
|
||||
void init();
|
||||
bool do_set_resolution(bool initial, int width, int height, int scale_factor);
|
||||
static void update_indices()
|
||||
{
|
||||
for (size_t i = 0; i < s_screens.size(); i++)
|
||||
s_screens[i].m_index = i;
|
||||
}
|
||||
static void update_bounding_rect();
|
||||
|
||||
bool is_opened() const { return m_framebuffer_fd >= 0; }
|
||||
|
||||
void on_change_resolution(bool initial, int pitch, int physical_width, int physical_height, int scale_factor, Screen& screen_with_cursor);
|
||||
|
||||
static NonnullOwnPtrVector<Screen, default_screen_count> s_screens;
|
||||
static Screen* s_main_screen;
|
||||
static Gfx::IntRect s_bounding_screens_rect;
|
||||
size_t m_index { 0 };
|
||||
|
||||
size_t m_size_in_bytes;
|
||||
|
||||
|
@ -67,16 +173,9 @@ private:
|
|||
bool m_can_set_buffer { false };
|
||||
|
||||
int m_pitch { 0 };
|
||||
int m_width { 0 };
|
||||
int m_height { 0 };
|
||||
Gfx::IntRect m_virtual_rect;
|
||||
int m_framebuffer_fd { -1 };
|
||||
int m_scale_factor { 1 };
|
||||
|
||||
Gfx::IntPoint m_physical_cursor_location;
|
||||
unsigned m_mouse_button_state { 0 };
|
||||
unsigned m_modifiers { 0 };
|
||||
double m_acceleration_factor { 1.0 };
|
||||
unsigned m_scroll_step_size { 1 };
|
||||
};
|
||||
|
||||
inline Gfx::RGBA32* Screen::scanline(int y)
|
||||
|
|
|
@ -96,7 +96,7 @@ void WMClientConnection::start_window_resize(i32 client_id, i32 window_id)
|
|||
auto& window = *(*it).value;
|
||||
// FIXME: We are cheating a bit here by using the current cursor location and hard-coding the left button.
|
||||
// Maybe the client should be allowed to specify what initiated this request?
|
||||
WindowManager::the().start_window_resize(window, Screen::the().cursor_location(), MouseButton::Left);
|
||||
WindowManager::the().start_window_resize(window, ScreenInput::the().cursor_location(), MouseButton::Left);
|
||||
}
|
||||
|
||||
void WMClientConnection::set_window_minimized(i32 client_id, i32 window_id, bool minimized)
|
||||
|
|
|
@ -174,12 +174,25 @@ bool Window::apply_minimum_size(Gfx::IntRect& rect)
|
|||
return did_size_clamp;
|
||||
}
|
||||
|
||||
void Window::nudge_into_desktop(bool force_titlebar_visible)
|
||||
void Window::nudge_into_desktop(Screen* target_screen, bool force_titlebar_visible)
|
||||
{
|
||||
Gfx::IntRect arena = WindowManager::the().arena_rect_for_type(type());
|
||||
if (!target_screen) {
|
||||
// If no explicit target screen was supplied,
|
||||
// guess based on the current frame rectangle
|
||||
target_screen = &Screen::closest_to_rect(rect());
|
||||
}
|
||||
Gfx::IntRect arena = WindowManager::the().arena_rect_for_type(*target_screen, type());
|
||||
auto min_visible = 1;
|
||||
if (type() == WindowType::Normal)
|
||||
switch (type()) {
|
||||
case WindowType::Normal:
|
||||
min_visible = 30;
|
||||
break;
|
||||
case WindowType::Desktop:
|
||||
set_rect(arena);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Push the frame around such that at least `min_visible` pixels of the *frame* are in the desktop rect.
|
||||
auto old_frame_rect = frame().rect();
|
||||
|
@ -203,6 +216,7 @@ void Window::nudge_into_desktop(bool force_titlebar_visible)
|
|||
width(),
|
||||
height(),
|
||||
};
|
||||
|
||||
set_rect(new_window_rect);
|
||||
}
|
||||
|
||||
|
@ -684,7 +698,7 @@ void Window::handle_window_menu_action(WindowMenuAction action)
|
|||
WindowManager::the().move_to_front_and_make_active(*this);
|
||||
break;
|
||||
case WindowMenuAction::Move:
|
||||
WindowManager::the().start_window_move(*this, Screen::the().cursor_location());
|
||||
WindowManager::the().start_window_move(*this, ScreenInput::the().cursor_location());
|
||||
break;
|
||||
case WindowMenuAction::Close:
|
||||
request_close();
|
||||
|
@ -746,7 +760,7 @@ void Window::set_fullscreen(bool fullscreen)
|
|||
Gfx::IntRect new_window_rect = m_rect;
|
||||
if (m_fullscreen) {
|
||||
m_saved_nonfullscreen_rect = m_rect;
|
||||
new_window_rect = Screen::the().rect();
|
||||
new_window_rect = Screen::main().rect(); // TODO: We should support fullscreen on any screen
|
||||
} else if (!m_saved_nonfullscreen_rect.is_empty()) {
|
||||
new_window_rect = m_saved_nonfullscreen_rect;
|
||||
}
|
||||
|
@ -755,56 +769,73 @@ void Window::set_fullscreen(bool fullscreen)
|
|||
set_rect(new_window_rect);
|
||||
}
|
||||
|
||||
Gfx::IntRect Window::tiled_rect(WindowTileType tiled) const
|
||||
Gfx::IntRect Window::tiled_rect(Screen* target_screen, WindowTileType tiled) const
|
||||
{
|
||||
if (!target_screen) {
|
||||
// If no explicit target screen was supplied,
|
||||
// guess based on the current frame rectangle
|
||||
target_screen = &Screen::closest_to_rect(frame().rect());
|
||||
}
|
||||
|
||||
VERIFY(tiled != WindowTileType::None);
|
||||
|
||||
int frame_width = (m_frame.rect().width() - m_rect.width()) / 2;
|
||||
int titlebar_height = m_frame.titlebar_rect().height();
|
||||
int menu_height = WindowManager::the().maximized_window_rect(*this).y();
|
||||
int max_height = WindowManager::the().maximized_window_rect(*this).height();
|
||||
auto maximized_rect_relative_to_window_screen = WindowManager::the().maximized_window_rect(*this, true);
|
||||
int menu_height = maximized_rect_relative_to_window_screen.y();
|
||||
int max_height = maximized_rect_relative_to_window_screen.height();
|
||||
|
||||
auto& screen = *target_screen;
|
||||
auto screen_location = screen.rect().location();
|
||||
switch (tiled) {
|
||||
case WindowTileType::Left:
|
||||
return Gfx::IntRect(0,
|
||||
menu_height,
|
||||
Screen::the().width() / 2 - frame_width,
|
||||
max_height);
|
||||
screen.width() / 2 - frame_width,
|
||||
max_height)
|
||||
.translated(screen_location);
|
||||
case WindowTileType::Right:
|
||||
return Gfx::IntRect(Screen::the().width() / 2 + frame_width,
|
||||
return Gfx::IntRect(screen.width() / 2 + frame_width,
|
||||
menu_height,
|
||||
Screen::the().width() / 2 - frame_width,
|
||||
max_height);
|
||||
screen.width() / 2 - frame_width,
|
||||
max_height)
|
||||
.translated(screen_location);
|
||||
case WindowTileType::Top:
|
||||
return Gfx::IntRect(0,
|
||||
menu_height,
|
||||
Screen::the().width(),
|
||||
(max_height - titlebar_height) / 2 - frame_width);
|
||||
screen.width(),
|
||||
(max_height - titlebar_height) / 2 - frame_width)
|
||||
.translated(screen_location);
|
||||
case WindowTileType::Bottom:
|
||||
return Gfx::IntRect(0,
|
||||
menu_height + (titlebar_height + max_height) / 2 + frame_width,
|
||||
Screen::the().width(),
|
||||
(max_height - titlebar_height) / 2 - frame_width);
|
||||
screen.width(),
|
||||
(max_height - titlebar_height) / 2 - frame_width)
|
||||
.translated(screen_location);
|
||||
case WindowTileType::TopLeft:
|
||||
return Gfx::IntRect(0,
|
||||
menu_height,
|
||||
Screen::the().width() / 2 - frame_width,
|
||||
(max_height - titlebar_height) / 2 - frame_width);
|
||||
screen.width() / 2 - frame_width,
|
||||
(max_height - titlebar_height) / 2 - frame_width)
|
||||
.translated(screen_location);
|
||||
case WindowTileType::TopRight:
|
||||
return Gfx::IntRect(Screen::the().width() / 2 + frame_width,
|
||||
return Gfx::IntRect(screen.width() / 2 + frame_width,
|
||||
menu_height,
|
||||
Screen::the().width() / 2 - frame_width,
|
||||
(max_height - titlebar_height) / 2 - frame_width);
|
||||
screen.width() / 2 - frame_width,
|
||||
(max_height - titlebar_height) / 2 - frame_width)
|
||||
.translated(screen_location);
|
||||
case WindowTileType::BottomLeft:
|
||||
return Gfx::IntRect(0,
|
||||
menu_height + (titlebar_height + max_height) / 2 + frame_width,
|
||||
Screen::the().width() / 2 - frame_width,
|
||||
(max_height - titlebar_height) / 2 - frame_width);
|
||||
screen.width() / 2 - frame_width,
|
||||
(max_height - titlebar_height) / 2 - frame_width)
|
||||
.translated(screen_location);
|
||||
case WindowTileType::BottomRight:
|
||||
return Gfx::IntRect(Screen::the().width() / 2 + frame_width,
|
||||
return Gfx::IntRect(screen.width() / 2 + frame_width,
|
||||
menu_height + (titlebar_height + max_height) / 2 + frame_width,
|
||||
Screen::the().width() / 2 - frame_width,
|
||||
(max_height - titlebar_height) / 2 - frame_width);
|
||||
screen.width() / 2 - frame_width,
|
||||
(max_height - titlebar_height) / 2 - frame_width)
|
||||
.translated(screen_location);
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
@ -831,7 +862,7 @@ bool Window::set_untiled(Optional<Gfx::IntPoint> fixed_point)
|
|||
return true;
|
||||
}
|
||||
|
||||
void Window::set_tiled(WindowTileType tiled)
|
||||
void Window::set_tiled(Screen* screen, WindowTileType tiled)
|
||||
{
|
||||
VERIFY(tiled != WindowTileType::None);
|
||||
|
||||
|
@ -845,7 +876,7 @@ void Window::set_tiled(WindowTileType tiled)
|
|||
m_untiled_rect = m_rect;
|
||||
m_tiled = tiled;
|
||||
|
||||
set_rect(tiled_rect(tiled));
|
||||
set_rect(tiled_rect(screen, tiled));
|
||||
Core::EventLoop::current().post_event(*this, make<ResizeEvent>(m_rect));
|
||||
}
|
||||
|
||||
|
@ -861,11 +892,11 @@ void Window::recalculate_rect()
|
|||
|
||||
bool send_event = true;
|
||||
if (m_tiled != WindowTileType::None) {
|
||||
set_rect(tiled_rect(m_tiled));
|
||||
set_rect(tiled_rect(nullptr, m_tiled));
|
||||
} else if (is_maximized()) {
|
||||
set_rect(WindowManager::the().maximized_window_rect(*this));
|
||||
} else if (type() == WindowType::Desktop) {
|
||||
set_rect(WindowManager::the().desktop_rect());
|
||||
set_rect(WindowManager::the().arena_rect_for_type(Screen::main(), WindowType::Desktop));
|
||||
} else {
|
||||
send_event = false;
|
||||
}
|
||||
|
@ -953,7 +984,7 @@ bool Window::is_descendant_of(Window& window) const
|
|||
return false;
|
||||
}
|
||||
|
||||
Optional<HitTestResult> Window::hit_test(Gfx::IntPoint const& position, bool include_frame) const
|
||||
Optional<HitTestResult> Window::hit_test(Gfx::IntPoint const& position, bool include_frame)
|
||||
{
|
||||
if (!m_hit_testing_enabled)
|
||||
return {};
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <LibGfx/DisjointRectSet.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <WindowServer/Cursor.h>
|
||||
#include <WindowServer/Screen.h>
|
||||
#include <WindowServer/WindowFrame.h>
|
||||
#include <WindowServer/WindowType.h>
|
||||
|
||||
|
@ -97,7 +98,7 @@ public:
|
|||
void set_fullscreen(bool);
|
||||
|
||||
WindowTileType tiled() const { return m_tiled; }
|
||||
void set_tiled(WindowTileType);
|
||||
void set_tiled(Screen*, WindowTileType);
|
||||
bool set_untiled(Optional<Gfx::IntPoint> fixed_point = {});
|
||||
|
||||
bool is_occluded() const { return m_occluded; }
|
||||
|
@ -139,7 +140,8 @@ public:
|
|||
{
|
||||
m_alpha_hit_threshold = threshold;
|
||||
}
|
||||
Optional<HitTestResult> hit_test(const Gfx::IntPoint&, bool include_frame = true) const;
|
||||
|
||||
Optional<HitTestResult> hit_test(const Gfx::IntPoint&, bool include_frame = true);
|
||||
|
||||
int x() const { return m_rect.x(); }
|
||||
int y() const { return m_rect.y(); }
|
||||
|
@ -159,7 +161,7 @@ public:
|
|||
void set_rect(int x, int y, int width, int height) { set_rect({ x, y, width, height }); }
|
||||
void set_rect_without_repaint(const Gfx::IntRect&);
|
||||
bool apply_minimum_size(Gfx::IntRect&);
|
||||
void nudge_into_desktop(bool force_titlebar_visible = true);
|
||||
void nudge_into_desktop(Screen*, bool force_titlebar_visible = true);
|
||||
|
||||
Gfx::IntSize minimum_size() const { return m_minimum_size; }
|
||||
void set_minimum_size(const Gfx::IntSize&);
|
||||
|
@ -260,7 +262,7 @@ public:
|
|||
void start_minimize_animation();
|
||||
void end_minimize_animation() { m_minimize_animation_step = -1; }
|
||||
|
||||
Gfx::IntRect tiled_rect(WindowTileType) const;
|
||||
Gfx::IntRect tiled_rect(Screen*, WindowTileType) const;
|
||||
void recalculate_rect();
|
||||
|
||||
IntrusiveListNode<Window> m_list_node;
|
||||
|
@ -319,6 +321,14 @@ public:
|
|||
WindowStack const* outer_stack() const { return m_outer_stack; }
|
||||
void set_outer_stack(Badge<WindowStack>, WindowStack* stack) { m_outer_stack = stack; }
|
||||
|
||||
const Vector<Screen*, default_screen_count>& screens() const { return m_screens; }
|
||||
Vector<Screen*, default_screen_count>& screens() { return m_screens; }
|
||||
|
||||
void did_construct()
|
||||
{
|
||||
frame().window_was_constructed({});
|
||||
}
|
||||
|
||||
private:
|
||||
Window(ClientConnection&, WindowType, int window_id, bool modal, bool minimizable, bool frameless, bool resizable, bool fullscreen, bool accessory, Window* parent_window = nullptr);
|
||||
Window(Core::Object&, WindowType);
|
||||
|
@ -344,6 +354,7 @@ private:
|
|||
Gfx::IntRect m_rect;
|
||||
Gfx::IntRect m_saved_nonfullscreen_rect;
|
||||
Gfx::IntRect m_taskbar_rect;
|
||||
Vector<Screen*, default_screen_count> m_screens;
|
||||
Gfx::DisjointRectSet m_dirty_rects;
|
||||
Gfx::DisjointRectSet m_opaque_rects;
|
||||
Gfx::DisjointRectSet m_transparency_rects;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
endpoint WindowClient
|
||||
{
|
||||
fast_greet(Gfx::IntRect screen_rect, Core::AnonymousBuffer theme_buffer, String default_font_query, String fixed_width_font_query) =|
|
||||
fast_greet(Vector<Gfx::IntRect> screen_rects, u32 main_screen_index, Core::AnonymousBuffer theme_buffer, String default_font_query, String fixed_width_font_query) =|
|
||||
|
||||
paint(i32 window_id, Gfx::IntSize window_size, Vector<Gfx::IntRect> rects) =|
|
||||
mouse_move(i32 window_id, Gfx::IntPoint mouse_position, u32 button, u32 buttons, u32 modifiers, i32 wheel_delta, bool is_drag, Vector<String> mime_types) =|
|
||||
|
@ -25,7 +25,7 @@ endpoint WindowClient
|
|||
menu_item_left(i32 menu_id, u32 identifier) =|
|
||||
menu_visibility_did_change(i32 menu_id, bool visible) =|
|
||||
|
||||
screen_rect_changed(Gfx::IntRect rect) =|
|
||||
screen_rects_changed(Vector<Gfx::IntRect> rects, u32 main_screen_index) =|
|
||||
|
||||
set_wallpaper_finished(bool success) =|
|
||||
|
||||
|
|
|
@ -64,6 +64,14 @@ static Gfx::IntRect frame_rect_for_window(Window& window, const Gfx::IntRect& re
|
|||
|
||||
WindowFrame::WindowFrame(Window& window)
|
||||
: m_window(window)
|
||||
{
|
||||
// Because Window constructs a WindowFrame during its construction, we need
|
||||
// to be careful and defer doing initialization that assumes a fully
|
||||
// constructed Window. It is fully constructed when Window notifies us with
|
||||
// a call to WindowFrame::window_was_constructed.
|
||||
}
|
||||
|
||||
void WindowFrame::window_was_constructed(Badge<Window>)
|
||||
{
|
||||
{
|
||||
auto button = make<Button>(*this, [this](auto&) {
|
||||
|
@ -73,7 +81,7 @@ WindowFrame::WindowFrame(Window& window)
|
|||
m_buttons.append(move(button));
|
||||
}
|
||||
|
||||
if (window.is_resizable()) {
|
||||
if (m_window.is_resizable()) {
|
||||
auto button = make<Button>(*this, [this](auto&) {
|
||||
m_window.handle_window_menu_action(WindowMenuAction::MaximizeOrRestore);
|
||||
});
|
||||
|
@ -84,7 +92,7 @@ WindowFrame::WindowFrame(Window& window)
|
|||
m_buttons.append(move(button));
|
||||
}
|
||||
|
||||
if (window.is_minimizable()) {
|
||||
if (m_window.is_minimizable()) {
|
||||
auto button = make<Button>(*this, [this](auto&) {
|
||||
m_window.handle_window_menu_action(WindowMenuAction::MinimizeOrUnminimize);
|
||||
});
|
||||
|
@ -93,6 +101,8 @@ WindowFrame::WindowFrame(Window& window)
|
|||
}
|
||||
|
||||
set_button_icons();
|
||||
|
||||
m_has_alpha_channel = Gfx::WindowTheme::current().frame_uses_alpha(window_state_for_theme(), WindowManager::the().palette());
|
||||
}
|
||||
|
||||
WindowFrame::~WindowFrame()
|
||||
|
@ -101,7 +111,7 @@ WindowFrame::~WindowFrame()
|
|||
|
||||
void WindowFrame::set_button_icons()
|
||||
{
|
||||
m_dirty = true;
|
||||
set_dirty();
|
||||
if (m_window.is_frameless())
|
||||
return;
|
||||
|
||||
|
@ -115,7 +125,7 @@ void WindowFrame::set_button_icons()
|
|||
void WindowFrame::reload_config()
|
||||
{
|
||||
String icons_path = WindowManager::the().palette().title_button_icons_path();
|
||||
int icons_scale = WindowManager::the().compositor_icon_scale();
|
||||
int icons_scale = WindowManager::the().compositor_icon_scale(); // TODO: We'll need to load icons for all scales in use!
|
||||
|
||||
StringBuilder full_path;
|
||||
if (!s_minimize_icon || s_last_title_button_icons_path != icons_path || s_last_title_button_icons_scale != icons_scale) {
|
||||
|
@ -301,27 +311,30 @@ void WindowFrame::paint_normal_frame(Gfx::Painter& painter)
|
|||
paint_menubar(painter);
|
||||
}
|
||||
|
||||
void WindowFrame::paint(Gfx::Painter& painter, const Gfx::IntRect& rect)
|
||||
void WindowFrame::paint(Screen& screen, Gfx::Painter& painter, const Gfx::IntRect& rect)
|
||||
{
|
||||
render_to_cache();
|
||||
|
||||
auto frame_rect = render_rect();
|
||||
auto window_rect = m_window.rect();
|
||||
if (auto* cached = render_to_cache(screen))
|
||||
cached->paint(*this, painter, rect);
|
||||
}
|
||||
|
||||
void WindowFrame::RenderedCache::paint(WindowFrame& frame, Gfx::Painter& painter, const Gfx::IntRect& rect)
|
||||
{
|
||||
auto frame_rect = frame.render_rect();
|
||||
auto window_rect = frame.window().rect();
|
||||
if (m_top_bottom) {
|
||||
auto top_bottom_height = frame_rect.height() - window_rect.height();
|
||||
if (m_bottom_y > 0) {
|
||||
// We have a top piece
|
||||
auto src_rect = rect.intersected({ frame_rect.location(), { frame_rect.width(), m_bottom_y } });
|
||||
if (!src_rect.is_empty())
|
||||
painter.blit(src_rect.location(), *m_top_bottom, src_rect.translated(-frame_rect.location()), m_opacity);
|
||||
painter.blit(src_rect.location(), *m_top_bottom, src_rect.translated(-frame_rect.location()), frame.opacity());
|
||||
}
|
||||
if (m_bottom_y < top_bottom_height) {
|
||||
// We have a bottom piece
|
||||
Gfx::IntRect rect_in_frame { frame_rect.x(), window_rect.bottom() + 1, frame_rect.width(), top_bottom_height - m_bottom_y };
|
||||
auto src_rect = rect.intersected(rect_in_frame);
|
||||
if (!src_rect.is_empty())
|
||||
painter.blit(src_rect.location(), *m_top_bottom, src_rect.translated(-rect_in_frame.x(), -rect_in_frame.y() + m_bottom_y), m_opacity);
|
||||
painter.blit(src_rect.location(), *m_top_bottom, src_rect.translated(-rect_in_frame.x(), -rect_in_frame.y() + m_bottom_y), frame.opacity());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -332,19 +345,19 @@ void WindowFrame::paint(Gfx::Painter& painter, const Gfx::IntRect& rect)
|
|||
Gfx::IntRect rect_in_frame { frame_rect.x(), window_rect.y(), m_right_x, window_rect.height() };
|
||||
auto src_rect = rect.intersected(rect_in_frame);
|
||||
if (!src_rect.is_empty())
|
||||
painter.blit(src_rect.location(), *m_left_right, src_rect.translated(-rect_in_frame.location()), m_opacity);
|
||||
painter.blit(src_rect.location(), *m_left_right, src_rect.translated(-rect_in_frame.location()), frame.opacity());
|
||||
}
|
||||
if (m_right_x < left_right_width) {
|
||||
// We have a right piece
|
||||
Gfx::IntRect rect_in_frame { window_rect.right() + 1, window_rect.y(), left_right_width - m_right_x, window_rect.height() };
|
||||
auto src_rect = rect.intersected(rect_in_frame);
|
||||
if (!src_rect.is_empty())
|
||||
painter.blit(src_rect.location(), *m_left_right, src_rect.translated(-rect_in_frame.x() + m_right_x, -rect_in_frame.y()), m_opacity);
|
||||
painter.blit(src_rect.location(), *m_left_right, src_rect.translated(-rect_in_frame.x() + m_right_x, -rect_in_frame.y()), frame.opacity());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WindowFrame::render(Gfx::Painter& painter)
|
||||
void WindowFrame::render(Screen&, Gfx::Painter& painter)
|
||||
{
|
||||
if (m_window.is_frameless())
|
||||
return;
|
||||
|
@ -365,10 +378,7 @@ void WindowFrame::render(Gfx::Painter& painter)
|
|||
|
||||
void WindowFrame::theme_changed()
|
||||
{
|
||||
m_dirty = m_shadow_dirty = true;
|
||||
m_top_bottom = nullptr;
|
||||
m_left_right = nullptr;
|
||||
m_bottom_y = m_right_x = 0;
|
||||
m_rendered_cache = {};
|
||||
|
||||
layout_buttons();
|
||||
set_button_icons();
|
||||
|
@ -376,19 +386,34 @@ void WindowFrame::theme_changed()
|
|||
m_has_alpha_channel = Gfx::WindowTheme::current().frame_uses_alpha(window_state_for_theme(), WindowManager::the().palette());
|
||||
}
|
||||
|
||||
void WindowFrame::render_to_cache()
|
||||
auto WindowFrame::render_to_cache(Screen& screen) -> RenderedCache*
|
||||
{
|
||||
auto scale = screen.scale_factor();
|
||||
RenderedCache* rendered_cache;
|
||||
auto cached_it = m_rendered_cache.find(scale);
|
||||
if (cached_it == m_rendered_cache.end()) {
|
||||
auto new_rendered_cache = make<RenderedCache>();
|
||||
rendered_cache = new_rendered_cache.ptr();
|
||||
m_rendered_cache.set(scale, move(new_rendered_cache));
|
||||
} else {
|
||||
rendered_cache = cached_it->value.ptr();
|
||||
}
|
||||
rendered_cache->render(*this, screen);
|
||||
return rendered_cache;
|
||||
}
|
||||
|
||||
void WindowFrame::RenderedCache::render(WindowFrame& frame, Screen& screen)
|
||||
{
|
||||
if (!m_dirty)
|
||||
return;
|
||||
m_dirty = false;
|
||||
|
||||
m_has_alpha_channel = Gfx::WindowTheme::current().frame_uses_alpha(window_state_for_theme(), WindowManager::the().palette());
|
||||
auto scale = screen.scale_factor();
|
||||
|
||||
static RefPtr<Gfx::Bitmap> s_tmp_bitmap;
|
||||
auto frame_rect = rect();
|
||||
auto frame_rect = frame.rect();
|
||||
|
||||
auto frame_rect_including_shadow = frame_rect;
|
||||
auto* shadow_bitmap = this->shadow_bitmap();
|
||||
auto* shadow_bitmap = frame.shadow_bitmap();
|
||||
Gfx::IntPoint shadow_offset;
|
||||
|
||||
if (shadow_bitmap) {
|
||||
|
@ -398,19 +423,35 @@ void WindowFrame::render_to_cache()
|
|||
shadow_offset = { offset, offset };
|
||||
}
|
||||
|
||||
auto window_rect = m_window.rect();
|
||||
auto scale = Screen::the().scale_factor();
|
||||
if (!s_tmp_bitmap || !s_tmp_bitmap->size().contains(frame_rect_including_shadow.size()) || s_tmp_bitmap->scale() != scale) {
|
||||
auto window_rect = frame.window().rect();
|
||||
|
||||
// TODO: if we stop using a scaling factor we should clear cached bitmaps from this map
|
||||
static HashMap<int, RefPtr<Gfx::Bitmap>> s_tmp_bitmap_cache;
|
||||
Gfx::Bitmap* tmp_bitmap;
|
||||
{
|
||||
auto tmp_it = s_tmp_bitmap_cache.find(scale);
|
||||
if (tmp_it == s_tmp_bitmap_cache.end() || !tmp_it->value->size().contains(frame_rect_including_shadow.size())) {
|
||||
// Explicitly clear the old bitmap first so this works on machines with very little memory
|
||||
s_tmp_bitmap = nullptr;
|
||||
s_tmp_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, frame_rect_including_shadow.size(), scale);
|
||||
if (!s_tmp_bitmap) {
|
||||
if (tmp_it != s_tmp_bitmap_cache.end())
|
||||
tmp_it->value = nullptr;
|
||||
|
||||
auto bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, frame_rect_including_shadow.size(), scale);
|
||||
if (!bitmap) {
|
||||
s_tmp_bitmap_cache.remove(scale);
|
||||
dbgln("Could not create bitmap of size {}", frame_rect_including_shadow.size());
|
||||
return;
|
||||
}
|
||||
tmp_bitmap = bitmap.ptr();
|
||||
if (tmp_it != s_tmp_bitmap_cache.end())
|
||||
tmp_it->value = bitmap.release_nonnull();
|
||||
else
|
||||
s_tmp_bitmap_cache.set(scale, bitmap.release_nonnull());
|
||||
} else {
|
||||
tmp_bitmap = tmp_it->value.ptr();
|
||||
}
|
||||
}
|
||||
|
||||
VERIFY(s_tmp_bitmap);
|
||||
VERIFY(tmp_bitmap);
|
||||
|
||||
auto top_bottom_height = frame_rect_including_shadow.height() - window_rect.height();
|
||||
auto left_right_width = frame_rect_including_shadow.width() - window_rect.width();
|
||||
|
@ -433,19 +474,19 @@ void WindowFrame::render_to_cache()
|
|||
auto& frame_rect_to_update = m_shadow_dirty ? frame_rect_including_shadow : frame_rect;
|
||||
Gfx::IntPoint update_location(m_shadow_dirty ? Gfx::IntPoint { 0, 0 } : shadow_offset);
|
||||
|
||||
Gfx::Painter painter(*s_tmp_bitmap);
|
||||
Gfx::Painter painter(*tmp_bitmap);
|
||||
|
||||
// Clear the frame area, not including the window content area, which we don't care about
|
||||
for (auto& rect : frame_rect_to_update.shatter(window_rect))
|
||||
painter.clear_rect({ rect.location() - frame_rect_to_update.location(), rect.size() }, { 255, 255, 255, 0 });
|
||||
|
||||
if (m_shadow_dirty && shadow_bitmap)
|
||||
paint_simple_rect_shadow(painter, { { 0, 0 }, frame_rect_including_shadow.size() }, *shadow_bitmap);
|
||||
frame.paint_simple_rect_shadow(painter, { { 0, 0 }, frame_rect_including_shadow.size() }, *shadow_bitmap);
|
||||
|
||||
{
|
||||
Gfx::PainterStateSaver save(painter);
|
||||
painter.translate(shadow_offset);
|
||||
render(painter);
|
||||
frame.render(screen, painter);
|
||||
}
|
||||
|
||||
if (m_top_bottom && top_bottom_height > 0) {
|
||||
|
@ -455,9 +496,9 @@ void WindowFrame::render_to_cache()
|
|||
Gfx::Painter top_bottom_painter(*m_top_bottom);
|
||||
top_bottom_painter.add_clip_rect({ update_location, { frame_rect_to_update.width(), top_bottom_height - update_location.y() - (frame_rect_including_shadow.bottom() - frame_rect_to_update.bottom()) } });
|
||||
if (m_bottom_y > 0)
|
||||
top_bottom_painter.blit({ 0, 0 }, *s_tmp_bitmap, { 0, 0, frame_rect_including_shadow.width(), m_bottom_y }, 1.0, false);
|
||||
top_bottom_painter.blit({ 0, 0 }, *tmp_bitmap, { 0, 0, frame_rect_including_shadow.width(), m_bottom_y }, 1.0, false);
|
||||
if (m_bottom_y < top_bottom_height)
|
||||
top_bottom_painter.blit({ 0, m_bottom_y }, *s_tmp_bitmap, { 0, frame_rect_including_shadow.height() - (frame_rect_including_shadow.bottom() - window_rect.bottom()), frame_rect_including_shadow.width(), top_bottom_height - m_bottom_y }, 1.0, false);
|
||||
top_bottom_painter.blit({ 0, m_bottom_y }, *tmp_bitmap, { 0, frame_rect_including_shadow.height() - (frame_rect_including_shadow.bottom() - window_rect.bottom()), frame_rect_including_shadow.width(), top_bottom_height - m_bottom_y }, 1.0, false);
|
||||
} else {
|
||||
m_bottom_y = 0;
|
||||
}
|
||||
|
@ -469,9 +510,9 @@ void WindowFrame::render_to_cache()
|
|||
Gfx::Painter left_right_painter(*m_left_right);
|
||||
left_right_painter.add_clip_rect({ update_location, { left_right_width - update_location.x() - (frame_rect_including_shadow.right() - frame_rect_to_update.right()), window_rect.height() } });
|
||||
if (m_right_x > 0)
|
||||
left_right_painter.blit({ 0, 0 }, *s_tmp_bitmap, { 0, m_bottom_y, m_right_x, window_rect.height() }, 1.0, false);
|
||||
left_right_painter.blit({ 0, 0 }, *tmp_bitmap, { 0, m_bottom_y, m_right_x, window_rect.height() }, 1.0, false);
|
||||
if (m_right_x < left_right_width)
|
||||
left_right_painter.blit({ m_right_x, 0 }, *s_tmp_bitmap, { (window_rect.right() - frame_rect_including_shadow.x()) + 1, m_bottom_y, frame_rect_including_shadow.width() - (frame_rect_including_shadow.right() - window_rect.right()), window_rect.height() }, 1.0, false);
|
||||
left_right_painter.blit({ m_right_x, 0 }, *tmp_bitmap, { (window_rect.right() - frame_rect_including_shadow.x()) + 1, m_bottom_y, frame_rect_including_shadow.width() - (frame_rect_including_shadow.right() - window_rect.right()), window_rect.height() }, 1.0, false);
|
||||
} else {
|
||||
m_right_x = 0;
|
||||
}
|
||||
|
@ -545,7 +586,7 @@ Gfx::DisjointRectSet WindowFrame::transparent_render_rects() const
|
|||
|
||||
void WindowFrame::invalidate_titlebar()
|
||||
{
|
||||
m_dirty = true;
|
||||
set_dirty();
|
||||
invalidate(titlebar_rect());
|
||||
}
|
||||
|
||||
|
@ -561,7 +602,7 @@ void WindowFrame::invalidate(Gfx::IntRect relative_rect)
|
|||
auto frame_rect = rect();
|
||||
auto window_rect = m_window.rect();
|
||||
relative_rect.translate_by(frame_rect.x() - window_rect.x(), frame_rect.y() - window_rect.y());
|
||||
m_dirty = true;
|
||||
set_dirty();
|
||||
m_window.invalidate(relative_rect, true);
|
||||
}
|
||||
|
||||
|
@ -572,7 +613,7 @@ void WindowFrame::notify_window_rect_changed(const Gfx::IntRect& old_rect, const
|
|||
auto old_frame_rect = inflated_for_shadow(frame_rect_for_window(m_window, old_rect));
|
||||
auto new_frame_rect = inflated_for_shadow(frame_rect_for_window(m_window, new_rect));
|
||||
if (old_frame_rect.size() != new_frame_rect.size())
|
||||
m_dirty = m_shadow_dirty = true;
|
||||
set_dirty(true);
|
||||
auto& compositor = Compositor::the();
|
||||
for (auto& dirty : old_frame_rect.shatter(new_frame_rect))
|
||||
compositor.invalidate_screen(dirty);
|
||||
|
@ -591,7 +632,7 @@ void WindowFrame::layout_buttons()
|
|||
m_buttons[i].set_relative_rect(button_rects[i]);
|
||||
}
|
||||
|
||||
Optional<HitTestResult> WindowFrame::hit_test(Gfx::IntPoint const& position) const
|
||||
Optional<HitTestResult> WindowFrame::hit_test(Gfx::IntPoint const& position)
|
||||
{
|
||||
if (m_window.is_frameless())
|
||||
return {};
|
||||
|
@ -602,19 +643,32 @@ Optional<HitTestResult> WindowFrame::hit_test(Gfx::IntPoint const& position) con
|
|||
if (window_rect.contains(position))
|
||||
return {};
|
||||
|
||||
auto* screen = Screen::find_by_location(position);
|
||||
if (!screen)
|
||||
return {};
|
||||
auto* cached = render_to_cache(*screen);
|
||||
if (!cached)
|
||||
return {};
|
||||
|
||||
auto window_relative_position = position.translated(-render_rect().location());
|
||||
return cached->hit_test(*this, position, window_relative_position);
|
||||
}
|
||||
|
||||
Optional<HitTestResult> WindowFrame::RenderedCache::hit_test(WindowFrame& frame, Gfx::IntPoint const& position, Gfx::IntPoint const& window_relative_position)
|
||||
{
|
||||
HitTestResult result {
|
||||
.window = m_window,
|
||||
.window = frame.window(),
|
||||
.screen_position = position,
|
||||
.window_relative_position = window_relative_position,
|
||||
.is_frame_hit = true,
|
||||
};
|
||||
|
||||
u8 alpha_threshold = Gfx::WindowTheme::current().frame_alpha_hit_threshold(window_state_for_theme()) * 255;
|
||||
u8 alpha_threshold = Gfx::WindowTheme::current().frame_alpha_hit_threshold(frame.window_state_for_theme()) * 255;
|
||||
if (alpha_threshold == 0)
|
||||
return result;
|
||||
u8 alpha = 0xff;
|
||||
|
||||
auto window_rect = frame.window().rect();
|
||||
if (position.y() < window_rect.y()) {
|
||||
if (m_top_bottom) {
|
||||
auto scaled_relative_point = window_relative_position * m_top_bottom->scale();
|
||||
|
|
|
@ -20,21 +20,46 @@ class Button;
|
|||
class Menu;
|
||||
class MouseEvent;
|
||||
class Window;
|
||||
class Screen;
|
||||
|
||||
class WindowFrame {
|
||||
public:
|
||||
class RenderedCache {
|
||||
friend class WindowFrame;
|
||||
|
||||
public:
|
||||
void paint(WindowFrame&, Gfx::Painter&, const Gfx::IntRect&);
|
||||
void render(WindowFrame&, Screen&);
|
||||
Optional<HitTestResult> hit_test(WindowFrame&, Gfx::IntPoint const&, Gfx::IntPoint const&);
|
||||
|
||||
private:
|
||||
RefPtr<Gfx::Bitmap> m_top_bottom;
|
||||
RefPtr<Gfx::Bitmap> m_left_right;
|
||||
int m_bottom_y { 0 }; // y-offset in m_top_bottom for the bottom half
|
||||
int m_right_x { 0 }; // x-offset in m_left_right for the right half
|
||||
bool m_shadow_dirty { true };
|
||||
bool m_dirty { true };
|
||||
};
|
||||
friend class RenderedCache;
|
||||
|
||||
static void reload_config();
|
||||
|
||||
explicit WindowFrame(Window&);
|
||||
~WindowFrame();
|
||||
|
||||
void window_was_constructed(Badge<Window>);
|
||||
|
||||
Window& window() { return m_window; }
|
||||
const Window& window() const { return m_window; }
|
||||
|
||||
Gfx::IntRect rect() const;
|
||||
Gfx::IntRect render_rect() const;
|
||||
Gfx::DisjointRectSet opaque_render_rects() const;
|
||||
Gfx::DisjointRectSet transparent_render_rects() const;
|
||||
void paint(Gfx::Painter&, const Gfx::IntRect&);
|
||||
void render(Gfx::Painter&);
|
||||
void render_to_cache();
|
||||
|
||||
void paint(Screen&, Gfx::Painter&, const Gfx::IntRect&);
|
||||
void render(Screen&, Gfx::Painter&);
|
||||
RenderedCache* render_to_cache(Screen&);
|
||||
|
||||
void handle_mouse_event(MouseEvent const&);
|
||||
void handle_titlebar_mouse_event(MouseEvent const&);
|
||||
|
@ -78,13 +103,16 @@ public:
|
|||
|
||||
void set_dirty(bool re_render_shadow = false)
|
||||
{
|
||||
m_dirty = true;
|
||||
m_shadow_dirty |= re_render_shadow;
|
||||
for (auto& it : m_rendered_cache) {
|
||||
auto& cached = *it.value;
|
||||
cached.m_dirty = true;
|
||||
cached.m_shadow_dirty |= re_render_shadow;
|
||||
}
|
||||
}
|
||||
|
||||
void theme_changed();
|
||||
|
||||
Optional<HitTestResult> hit_test(Gfx::IntPoint const&) const;
|
||||
Optional<HitTestResult> hit_test(Gfx::IntPoint const&);
|
||||
|
||||
void open_menubar_menu(Menu&);
|
||||
|
||||
|
@ -109,17 +137,12 @@ private:
|
|||
Button* m_maximize_button { nullptr };
|
||||
Button* m_minimize_button { nullptr };
|
||||
|
||||
RefPtr<Gfx::Bitmap> m_top_bottom;
|
||||
RefPtr<Gfx::Bitmap> m_left_right;
|
||||
int m_bottom_y { 0 }; // y-offset in m_top_bottom for the bottom half
|
||||
int m_right_x { 0 }; // x-offset in m_left_right for the right half
|
||||
HashMap<int, NonnullOwnPtr<RenderedCache>> m_rendered_cache;
|
||||
|
||||
RefPtr<Core::Timer> m_flash_timer;
|
||||
size_t m_flash_counter { 0 };
|
||||
float m_opacity { 1 };
|
||||
bool m_has_alpha_channel { false };
|
||||
bool m_shadow_dirty { false };
|
||||
bool m_dirty { false };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -93,11 +93,27 @@ Gfx::Font const& WindowManager::window_title_font() const
|
|||
return Gfx::FontDatabase::default_font().bold_variant();
|
||||
}
|
||||
|
||||
bool WindowManager::set_resolution(int width, int height, int scale)
|
||||
bool WindowManager::set_resolution(Screen& screen, int width, int height, int scale)
|
||||
{
|
||||
bool success = Compositor::the().set_resolution(width, height, scale);
|
||||
auto screen_rect = screen.rect();
|
||||
if (screen_rect.width() == width && screen_rect.height() == height && screen.scale_factor() == scale)
|
||||
return true;
|
||||
|
||||
// Make sure it's impossible to set an invalid resolution
|
||||
if (!(width >= 640 && height >= 480 && scale >= 1)) {
|
||||
dbgln("Compositor: Tried to set invalid resolution: {}x{}", width, height);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto old_scale_factor = screen.scale_factor();
|
||||
bool success = screen.set_resolution(width, height, scale);
|
||||
if (success && old_scale_factor != scale)
|
||||
reload_icon_bitmaps_after_scale_change();
|
||||
|
||||
Compositor::the().screen_resolution_changed();
|
||||
|
||||
ClientConnection::for_each_client([&](ClientConnection& client) {
|
||||
client.notify_about_new_screen_rect(Screen::the().rect());
|
||||
client.notify_about_new_screen_rects(Screen::rects(), Screen::main().index());
|
||||
});
|
||||
if (success) {
|
||||
m_window_stack.for_each_window([](Window& window) {
|
||||
|
@ -113,9 +129,9 @@ bool WindowManager::set_resolution(int width, int height, int scale)
|
|||
m_config->write_num_entry("Screen", "ScaleFactor", scale);
|
||||
m_config->sync();
|
||||
} else {
|
||||
dbgln("Saving fallback resolution: {} @1x to config file at {}", resolution(), m_config->filename());
|
||||
m_config->write_num_entry("Screen", "Width", resolution().width());
|
||||
m_config->write_num_entry("Screen", "Height", resolution().height());
|
||||
dbgln("Saving fallback resolution: {} @1x to config file at {}", screen.size(), m_config->filename());
|
||||
m_config->write_num_entry("Screen", "Width", screen.size().width());
|
||||
m_config->write_num_entry("Screen", "Height", screen.size().height());
|
||||
m_config->write_num_entry("Screen", "ScaleFactor", 1);
|
||||
m_config->sync();
|
||||
}
|
||||
|
@ -123,14 +139,9 @@ bool WindowManager::set_resolution(int width, int height, int scale)
|
|||
return success;
|
||||
}
|
||||
|
||||
Gfx::IntSize WindowManager::resolution() const
|
||||
{
|
||||
return Screen::the().size();
|
||||
}
|
||||
|
||||
void WindowManager::set_acceleration_factor(double factor)
|
||||
{
|
||||
Screen::the().set_acceleration_factor(factor);
|
||||
ScreenInput::the().set_acceleration_factor(factor);
|
||||
dbgln("Saving acceleration factor {} to config file at {}", factor, m_config->filename());
|
||||
m_config->write_entry("Mouse", "AccelerationFactor", String::formatted("{}", factor));
|
||||
m_config->sync();
|
||||
|
@ -138,7 +149,7 @@ void WindowManager::set_acceleration_factor(double factor)
|
|||
|
||||
void WindowManager::set_scroll_step_size(unsigned step_size)
|
||||
{
|
||||
Screen::the().set_scroll_step_size(step_size);
|
||||
ScreenInput::the().set_scroll_step_size(step_size);
|
||||
dbgln("Saving scroll step size {} to config file at {}", step_size, m_config->filename());
|
||||
m_config->write_entry("Mouse", "ScrollStepSize", String::number(step_size));
|
||||
m_config->sync();
|
||||
|
@ -158,11 +169,6 @@ int WindowManager::double_click_speed() const
|
|||
return m_double_click_speed;
|
||||
}
|
||||
|
||||
int WindowManager::scale_factor() const
|
||||
{
|
||||
return Screen::the().scale_factor();
|
||||
}
|
||||
|
||||
void WindowManager::add_window(Window& window)
|
||||
{
|
||||
bool is_first_window = m_window_stack.is_empty();
|
||||
|
@ -170,8 +176,9 @@ void WindowManager::add_window(Window& window)
|
|||
m_window_stack.add(window);
|
||||
|
||||
if (window.is_fullscreen()) {
|
||||
Core::EventLoop::current().post_event(window, make<ResizeEvent>(Screen::the().rect()));
|
||||
window.set_rect(Screen::the().rect());
|
||||
auto& screen = Screen::main(); // TODO: support fullscreen windows on other screens!
|
||||
Core::EventLoop::current().post_event(window, make<ResizeEvent>(screen.rect()));
|
||||
window.set_rect(screen.rect());
|
||||
}
|
||||
|
||||
if (window.type() != WindowType::Desktop || is_first_window)
|
||||
|
@ -558,8 +565,9 @@ bool WindowManager::process_ongoing_window_move(MouseEvent& event)
|
|||
|
||||
const int tiling_deadzone = 10;
|
||||
const int secondary_deadzone = 2;
|
||||
auto desktop = desktop_rect();
|
||||
|
||||
auto& cursor_screen = Screen::closest_to_location(event.position());
|
||||
auto desktop = desktop_rect(cursor_screen);
|
||||
auto desktop_relative_to_screen = desktop.translated(-cursor_screen.rect().location());
|
||||
if (m_move_window->is_maximized()) {
|
||||
auto pixels_moved_from_start = event.position().pixels_moved(m_move_origin);
|
||||
if (pixels_moved_from_start > 5) {
|
||||
|
@ -574,31 +582,32 @@ bool WindowManager::process_ongoing_window_move(MouseEvent& event)
|
|||
bool is_resizable = m_move_window->is_resizable();
|
||||
auto pixels_moved_from_start = event.position().pixels_moved(m_move_origin);
|
||||
|
||||
if (is_resizable && event.x() <= tiling_deadzone) {
|
||||
if (event.y() <= tiling_deadzone + desktop.top())
|
||||
m_move_window->set_tiled(WindowTileType::TopLeft);
|
||||
else if (event.y() >= desktop.height() - tiling_deadzone)
|
||||
m_move_window->set_tiled(WindowTileType::BottomLeft);
|
||||
auto event_location_relative_to_screen = event.position().translated(-cursor_screen.rect().location());
|
||||
if (is_resizable && event_location_relative_to_screen.x() <= tiling_deadzone) {
|
||||
if (event_location_relative_to_screen.y() <= tiling_deadzone + desktop_relative_to_screen.top())
|
||||
m_move_window->set_tiled(&cursor_screen, WindowTileType::TopLeft);
|
||||
else if (event_location_relative_to_screen.y() >= desktop_relative_to_screen.height() - tiling_deadzone)
|
||||
m_move_window->set_tiled(&cursor_screen, WindowTileType::BottomLeft);
|
||||
else
|
||||
m_move_window->set_tiled(WindowTileType::Left);
|
||||
} else if (is_resizable && event.x() >= Screen::the().width() - tiling_deadzone) {
|
||||
if (event.y() <= tiling_deadzone + desktop.top())
|
||||
m_move_window->set_tiled(WindowTileType::TopRight);
|
||||
else if (event.y() >= desktop.height() - tiling_deadzone)
|
||||
m_move_window->set_tiled(WindowTileType::BottomRight);
|
||||
m_move_window->set_tiled(&cursor_screen, WindowTileType::Left);
|
||||
} else if (is_resizable && event_location_relative_to_screen.x() >= cursor_screen.width() - tiling_deadzone) {
|
||||
if (event_location_relative_to_screen.y() <= tiling_deadzone + desktop.top())
|
||||
m_move_window->set_tiled(&cursor_screen, WindowTileType::TopRight);
|
||||
else if (event_location_relative_to_screen.y() >= desktop_relative_to_screen.height() - tiling_deadzone)
|
||||
m_move_window->set_tiled(&cursor_screen, WindowTileType::BottomRight);
|
||||
else
|
||||
m_move_window->set_tiled(WindowTileType::Right);
|
||||
} else if (is_resizable && event.y() <= secondary_deadzone + desktop.top()) {
|
||||
m_move_window->set_tiled(WindowTileType::Top);
|
||||
} else if (is_resizable && event.y() >= desktop.bottom() - secondary_deadzone) {
|
||||
m_move_window->set_tiled(WindowTileType::Bottom);
|
||||
m_move_window->set_tiled(&cursor_screen, WindowTileType::Right);
|
||||
} else if (is_resizable && event_location_relative_to_screen.y() <= secondary_deadzone + desktop_relative_to_screen.top()) {
|
||||
m_move_window->set_tiled(&cursor_screen, WindowTileType::Top);
|
||||
} else if (is_resizable && event_location_relative_to_screen.y() >= desktop_relative_to_screen.bottom() - secondary_deadzone) {
|
||||
m_move_window->set_tiled(&cursor_screen, WindowTileType::Bottom);
|
||||
} else if (m_move_window->tiled() == WindowTileType::None) {
|
||||
Gfx::IntPoint pos = m_move_window_origin.translated(event.position() - m_move_origin);
|
||||
m_move_window->set_position_without_repaint(pos);
|
||||
// "Bounce back" the window if it would end up too far outside the screen.
|
||||
// If the user has let go of Mod_Super, maybe they didn't intentionally press it to begin with. Therefore, refuse to go into a state where knowledge about super-drags is necessary.
|
||||
bool force_titlebar_visible = !(m_keyboard_modifiers & Mod_Super);
|
||||
m_move_window->nudge_into_desktop(force_titlebar_visible);
|
||||
m_move_window->nudge_into_desktop(&cursor_screen, force_titlebar_visible);
|
||||
} else if (pixels_moved_from_start > 5) {
|
||||
m_move_window->set_untiled(event.position());
|
||||
m_move_origin = event.position();
|
||||
|
@ -1067,7 +1076,7 @@ void WindowManager::reevaluate_hovered_window(Window* updated_window)
|
|||
if (m_dnd_client || m_resize_window || m_move_window || m_cursor_tracking_button || MenuManager::the().has_open_menu())
|
||||
return;
|
||||
|
||||
auto cursor_location = Screen::the().cursor_location();
|
||||
auto cursor_location = ScreenInput::the().cursor_location();
|
||||
auto* currently_hovered = hovered_window();
|
||||
if (updated_window) {
|
||||
if (!(updated_window == currently_hovered || updated_window->frame().rect().contains(cursor_location) || (currently_hovered && currently_hovered->frame().rect().contains(cursor_location))))
|
||||
|
@ -1110,32 +1119,31 @@ void WindowManager::clear_resize_candidate()
|
|||
m_resize_candidate = nullptr;
|
||||
}
|
||||
|
||||
Gfx::IntRect WindowManager::desktop_rect() const
|
||||
Gfx::IntRect WindowManager::desktop_rect(Screen& screen) const
|
||||
{
|
||||
if (active_fullscreen_window())
|
||||
return Screen::the().rect();
|
||||
return {
|
||||
0,
|
||||
0,
|
||||
Screen::the().width(),
|
||||
Screen::the().height() - 28
|
||||
};
|
||||
return Screen::main().rect(); // TODO: we should support fullscreen windows on any screen
|
||||
auto screen_rect = screen.rect();
|
||||
if (screen.is_main_screen())
|
||||
screen_rect.set_height(screen.height() - 28);
|
||||
return screen_rect;
|
||||
}
|
||||
|
||||
Gfx::IntRect WindowManager::arena_rect_for_type(WindowType type) const
|
||||
Gfx::IntRect WindowManager::arena_rect_for_type(Screen& screen, WindowType type) const
|
||||
{
|
||||
switch (type) {
|
||||
case WindowType::Desktop:
|
||||
return Screen::bounding_rect();
|
||||
case WindowType::Normal:
|
||||
case WindowType::ToolWindow:
|
||||
return desktop_rect();
|
||||
return desktop_rect(screen);
|
||||
case WindowType::Menu:
|
||||
case WindowType::WindowSwitcher:
|
||||
case WindowType::Taskbar:
|
||||
case WindowType::Tooltip:
|
||||
case WindowType::Applet:
|
||||
case WindowType::Notification:
|
||||
return Screen::the().rect();
|
||||
return screen.rect();
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
@ -1233,7 +1241,7 @@ void WindowManager::process_key_event(KeyEvent& event)
|
|||
}
|
||||
if (m_active_input_window->is_maximized())
|
||||
maximize_windows(*m_active_input_window, false);
|
||||
m_active_input_window->set_tiled(WindowTileType::Left);
|
||||
m_active_input_window->set_tiled(nullptr, WindowTileType::Left);
|
||||
return;
|
||||
}
|
||||
if (event.key() == Key_Right) {
|
||||
|
@ -1245,7 +1253,7 @@ void WindowManager::process_key_event(KeyEvent& event)
|
|||
}
|
||||
if (m_active_input_window->is_maximized())
|
||||
maximize_windows(*m_active_input_window, false);
|
||||
m_active_input_window->set_tiled(WindowTileType::Right);
|
||||
m_active_input_window->set_tiled(nullptr, WindowTileType::Right);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1448,24 +1456,29 @@ ResizeDirection WindowManager::resize_direction_of_window(Window const& window)
|
|||
return m_resize_direction;
|
||||
}
|
||||
|
||||
Gfx::IntRect WindowManager::maximized_window_rect(Window const& window) const
|
||||
Gfx::IntRect WindowManager::maximized_window_rect(Window const& window, bool relative_to_window_screen) const
|
||||
{
|
||||
Gfx::IntRect rect = Screen::the().rect();
|
||||
auto& screen = Screen::closest_to_rect(window.frame().rect());
|
||||
Gfx::IntRect rect = screen.rect();
|
||||
|
||||
// Subtract window title bar (leaving the border)
|
||||
rect.set_y(rect.y() + window.frame().titlebar_rect().height() + window.frame().menubar_rect().height());
|
||||
rect.set_height(rect.height() - window.frame().titlebar_rect().height() - window.frame().menubar_rect().height());
|
||||
|
||||
if (screen.is_main_screen()) {
|
||||
// Subtract taskbar window height if present
|
||||
const_cast<WindowManager*>(this)->m_window_stack.for_each_visible_window_of_type_from_back_to_front(WindowType::Taskbar, [&rect](Window& taskbar_window) {
|
||||
rect.set_height(rect.height() - taskbar_window.height());
|
||||
return IterationDecision::Break;
|
||||
});
|
||||
}
|
||||
|
||||
constexpr int tasteful_space_above_maximized_window = 1;
|
||||
rect.set_y(rect.y() + tasteful_space_above_maximized_window);
|
||||
rect.set_height(rect.height() - tasteful_space_above_maximized_window);
|
||||
|
||||
if (relative_to_window_screen)
|
||||
rect.translate_by(-screen.rect().location());
|
||||
return rect;
|
||||
}
|
||||
|
||||
|
@ -1577,9 +1590,10 @@ Gfx::IntPoint WindowManager::get_recommended_window_position(Gfx::IntPoint const
|
|||
|
||||
Gfx::IntPoint point;
|
||||
if (overlap_window) {
|
||||
auto& screen = Screen::closest_to_location(desired);
|
||||
point = overlap_window->position() + shift;
|
||||
point = { point.x() % Screen::the().width(),
|
||||
(point.y() >= (Screen::the().height() - taskbar_height))
|
||||
point = { point.x() % screen.width(),
|
||||
(point.y() >= (screen.height() - (screen.is_main_screen() ? taskbar_height : 0)))
|
||||
? Gfx::WindowTheme::current().titlebar_height(Gfx::WindowTheme::WindowType::Normal, palette())
|
||||
: point.y() };
|
||||
} else {
|
||||
|
@ -1593,7 +1607,7 @@ int WindowManager::compositor_icon_scale() const
|
|||
{
|
||||
if (!m_allow_hidpi_icons)
|
||||
return 1;
|
||||
return scale_factor();
|
||||
return Screen::main().scale_factor(); // TODO: There is no *one* scale factor...
|
||||
}
|
||||
|
||||
void WindowManager::reload_icon_bitmaps_after_scale_change(bool allow_hidpi_icons)
|
||||
|
|
|
@ -79,7 +79,7 @@ public:
|
|||
void notify_progress_changed(Window&);
|
||||
void notify_modified_changed(Window&);
|
||||
|
||||
Gfx::IntRect maximized_window_rect(Window const&) const;
|
||||
Gfx::IntRect maximized_window_rect(Window const&, bool relative_to_window_screen = false) const;
|
||||
|
||||
ClientConnection const* dnd_client() const { return m_dnd_client.ptr(); }
|
||||
String const& dnd_text() const { return m_dnd_text; }
|
||||
|
@ -105,8 +105,8 @@ public:
|
|||
|
||||
void move_to_front_and_make_active(Window&);
|
||||
|
||||
Gfx::IntRect desktop_rect() const;
|
||||
Gfx::IntRect arena_rect_for_type(WindowType) const;
|
||||
Gfx::IntRect desktop_rect(Screen&) const;
|
||||
Gfx::IntRect arena_rect_for_type(Screen&, WindowType) const;
|
||||
|
||||
Cursor const& active_cursor() const;
|
||||
Cursor const& hidden_cursor() const { return *m_hidden_cursor; }
|
||||
|
@ -129,9 +129,7 @@ public:
|
|||
Gfx::Font const& font() const;
|
||||
Gfx::Font const& window_title_font() const;
|
||||
|
||||
bool set_resolution(int width, int height, int scale);
|
||||
Gfx::IntSize resolution() const;
|
||||
int scale_factor() const;
|
||||
bool set_resolution(Screen&, int width, int height, int scale);
|
||||
|
||||
void set_acceleration_factor(double);
|
||||
void set_scroll_step_size(unsigned);
|
||||
|
|
|
@ -93,7 +93,7 @@ endpoint WindowServer
|
|||
set_background_color(String background_color) =|
|
||||
set_wallpaper_mode(String mode) =|
|
||||
|
||||
set_resolution(Gfx::IntSize resolution, int scale_factor) => (bool success, Gfx::IntSize resolution, int scale_factor)
|
||||
set_resolution(u32 screen_index, Gfx::IntSize resolution, int scale_factor) => (bool success, Gfx::IntSize resolution, int scale_factor)
|
||||
set_window_icon_bitmap(i32 window_id, Gfx::ShareableBitmap icon) =|
|
||||
|
||||
get_wallpaper() => (String path)
|
||||
|
@ -130,5 +130,5 @@ endpoint WindowServer
|
|||
set_double_click_speed(int speed) => ()
|
||||
get_double_click_speed() => (int speed)
|
||||
|
||||
get_desktop_display_scale() => (int desktop_display_scale)
|
||||
get_desktop_display_scale(u32 screen_index) => (int desktop_display_scale)
|
||||
}
|
||||
|
|
|
@ -221,7 +221,7 @@ void WindowSwitcher::refresh()
|
|||
int space_for_window_rect = 180;
|
||||
m_rect.set_width(thumbnail_width() + longest_title_width + space_for_window_rect + padding() * 2 + item_padding() * 2);
|
||||
m_rect.set_height(window_count * item_height() + padding() * 2);
|
||||
m_rect.center_within(Screen::the().rect());
|
||||
m_rect.center_within(Screen::main().rect());
|
||||
if (!m_switcher_window)
|
||||
m_switcher_window = Window::construct(*this, WindowType::WindowSwitcher);
|
||||
m_switcher_window->set_rect(m_rect);
|
||||
|
|
|
@ -75,10 +75,47 @@ int main(int, char**)
|
|||
return 1;
|
||||
}
|
||||
|
||||
int scale = wm_config->read_num_entry("Screen", "ScaleFactor", 1);
|
||||
WindowServer::Screen screen(wm_config->read_num_entry("Screen", "Width", 1024 / scale), wm_config->read_num_entry("Screen", "Height", 768 / scale), scale);
|
||||
screen.set_acceleration_factor(atof(wm_config->read_entry("Mouse", "AccelerationFactor", "1.0").characters()));
|
||||
screen.set_scroll_step_size(wm_config->read_num_entry("Mouse", "ScrollStepSize", 4));
|
||||
// First check which screens are explicitly configured
|
||||
AK::HashTable<String> fb_devices_configured;
|
||||
int main_screen_index = wm_config->read_num_entry("Screens", "MainScreen", 0);
|
||||
for (int screen_index = 0;; screen_index++) {
|
||||
auto group_name = String::formatted("Screen{}", screen_index);
|
||||
if (!wm_config->has_group(group_name))
|
||||
break;
|
||||
|
||||
int scale = wm_config->read_num_entry(group_name, "ScaleFactor", 1);
|
||||
auto device_path = wm_config->read_entry(group_name, "Device", {});
|
||||
if (device_path.is_null() || device_path.is_empty()) {
|
||||
dbgln("Screen {} misses Device setting", screen_index);
|
||||
break;
|
||||
}
|
||||
|
||||
Gfx::IntRect virtual_rect {
|
||||
wm_config->read_num_entry(group_name, "Left", 0 / scale),
|
||||
wm_config->read_num_entry(group_name, "Top", 0 / scale),
|
||||
wm_config->read_num_entry(group_name, "Width", 1024 / scale),
|
||||
wm_config->read_num_entry("Screen", "Height", 768 / scale)
|
||||
};
|
||||
auto* screen = WindowServer::Screen::create(device_path, virtual_rect, scale);
|
||||
if (!screen) {
|
||||
dbgln("Screen {} failed to be created", screen_index);
|
||||
break;
|
||||
}
|
||||
|
||||
if (main_screen_index == screen_index)
|
||||
screen->make_main_screen();
|
||||
|
||||
// Remember that we used this device for a screen already
|
||||
fb_devices_configured.set(device_path);
|
||||
}
|
||||
|
||||
// TODO: Enumerate the /dev/fbX devices and set up any ones we find that we haven't already used
|
||||
|
||||
auto& screen_input = WindowServer::ScreenInput::the();
|
||||
screen_input.set_cursor_location(WindowServer::Screen::main().rect().center());
|
||||
screen_input.set_acceleration_factor(atof(wm_config->read_entry("Mouse", "AccelerationFactor", "1.0").characters()));
|
||||
screen_input.set_scroll_step_size(wm_config->read_num_entry("Mouse", "ScrollStepSize", 4));
|
||||
|
||||
WindowServer::Compositor::the();
|
||||
auto wm = WindowServer::WindowManager::construct(*palette);
|
||||
auto am = WindowServer::AppletManager::construct();
|
||||
|
|
|
@ -10,12 +10,14 @@
|
|||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int screen = -1;
|
||||
int width = -1;
|
||||
int height = -1;
|
||||
int scale = 1;
|
||||
|
||||
Core::ArgsParser args_parser;
|
||||
args_parser.set_general_help("Change the screen resolution.");
|
||||
args_parser.add_positional_argument(screen, "Screen", "screen");
|
||||
args_parser.add_positional_argument(width, "Width", "width");
|
||||
args_parser.add_positional_argument(height, "Height", "height");
|
||||
args_parser.add_positional_argument(scale, "Scale Factor", "scale", Core::ArgsParser::Required::No);
|
||||
|
@ -24,7 +26,7 @@ int main(int argc, char** argv)
|
|||
// A Core::EventLoop is all we need, but WindowServerConnection needs a full Application object.
|
||||
char* dummy_argv[] = { argv[0] };
|
||||
auto app = GUI::Application::construct(1, dummy_argv);
|
||||
auto result = GUI::WindowServerConnection::the().set_resolution(Gfx::IntSize { width, height }, scale);
|
||||
auto result = GUI::WindowServerConnection::the().set_resolution(screen, Gfx::IntSize { width, height }, scale);
|
||||
if (!result.success()) {
|
||||
warnln("failed to set resolution");
|
||||
return 1;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue