diff options
Diffstat (limited to 'Userland/Services/WindowServer/Screen.cpp')
-rw-r--r-- | Userland/Services/WindowServer/Screen.cpp | 155 |
1 files changed, 115 insertions, 40 deletions
diff --git a/Userland/Services/WindowServer/Screen.cpp b/Userland/Services/WindowServer/Screen.cpp index bc94fa5a7f..e6b7716e9e 100644 --- a/Userland/Services/WindowServer/Screen.cpp +++ b/Userland/Services/WindowServer/Screen.cpp @@ -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); |