/* * Copyright (c) 2018-2020, Andreas Kling * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace GUI { WindowServerConnection& WindowServerConnection::the() { static WindowServerConnection* s_connection = nullptr; if (!s_connection) s_connection = new WindowServerConnection; return *s_connection; } static void set_system_theme_from_shbuf_id(int id) { auto system_theme = SharedBuffer::create_from_shbuf_id(id); ASSERT(system_theme); Gfx::set_system_theme(*system_theme); Application::the().set_system_palette(*system_theme); } void WindowServerConnection::handshake() { auto response = send_sync(); set_my_client_id(response->client_id()); set_system_theme_from_shbuf_id(response->system_theme_buffer_id()); Desktop::the().did_receive_screen_rect({}, response->screen_rect()); } void WindowServerConnection::handle(const Messages::WindowClient::UpdateSystemTheme& message) { set_system_theme_from_shbuf_id(message.system_theme_buffer_id()); Window::update_all_windows({}); Window::for_each_window({}, [](auto& window) { Core::EventLoop::current().post_event(window, make()); }); } void WindowServerConnection::handle(const Messages::WindowClient::Paint& message) { if (auto* window = Window::from_window_id(message.window_id())) Core::EventLoop::current().post_event(*window, make(message.rects(), message.window_size())); } void WindowServerConnection::handle(const Messages::WindowClient::WindowResized& message) { if (auto* window = Window::from_window_id(message.window_id())) { Core::EventLoop::current().post_event(*window, make(message.old_rect().size(), message.new_rect().size())); } } void WindowServerConnection::handle(const Messages::WindowClient::WindowActivated& message) { if (auto* window = Window::from_window_id(message.window_id())) Core::EventLoop::current().post_event(*window, make(Event::WindowBecameActive)); } void WindowServerConnection::handle(const Messages::WindowClient::WindowDeactivated& message) { if (auto* window = Window::from_window_id(message.window_id())) Core::EventLoop::current().post_event(*window, make(Event::WindowBecameInactive)); } void WindowServerConnection::handle(const Messages::WindowClient::WindowCloseRequest& message) { if (auto* window = Window::from_window_id(message.window_id())) Core::EventLoop::current().post_event(*window, make(Event::WindowCloseRequest)); } void WindowServerConnection::handle(const Messages::WindowClient::WindowEntered& message) { if (auto* window = Window::from_window_id(message.window_id())) Core::EventLoop::current().post_event(*window, make(Event::WindowEntered)); } void WindowServerConnection::handle(const Messages::WindowClient::WindowLeft& message) { if (auto* window = Window::from_window_id(message.window_id())) Core::EventLoop::current().post_event(*window, make(Event::WindowLeft)); } void WindowServerConnection::handle(const Messages::WindowClient::KeyDown& message) { auto* window = Window::from_window_id(message.window_id()); if (!window) return; auto key_event = make(Event::KeyDown, message.key(), message.modifiers()); if (message.character() != '\0') { char ch = message.character(); key_event->m_text = String(&ch, 1); } Action* action = nullptr; if (auto* focused_widget = window->focused_widget()) { for (auto* widget = focused_widget; widget && !action; widget = widget->parent_widget()) action = focused_widget->action_for_key_event(*key_event); } if (!action) action = window->action_for_key_event(*key_event); if (!action) action = Application::the().action_for_key_event(*key_event); if (action && action->is_enabled()) { action->activate(); return; } Core::EventLoop::current().post_event(*window, move(key_event)); } void WindowServerConnection::handle(const Messages::WindowClient::KeyUp& message) { auto* window = Window::from_window_id(message.window_id()); if (!window) return; auto key_event = make(Event::KeyUp, message.key(), message.modifiers()); if (message.character() != '\0') { char ch = message.character(); key_event->m_text = String(&ch, 1); } Core::EventLoop::current().post_event(*window, move(key_event)); } MouseButton to_gmousebutton(u32 button) { switch (button) { case 0: return MouseButton::None; case 1: return MouseButton::Left; case 2: return MouseButton::Right; case 4: return MouseButton::Middle; case 8: return MouseButton::Back; case 16: return MouseButton::Forward; default: ASSERT_NOT_REACHED(); break; } } void WindowServerConnection::handle(const Messages::WindowClient::MouseDown& message) { if (auto* window = Window::from_window_id(message.window_id())) Core::EventLoop::current().post_event(*window, make(Event::MouseDown, message.mouse_position(), message.buttons(), to_gmousebutton(message.button()), message.modifiers(), message.wheel_delta())); } void WindowServerConnection::handle(const Messages::WindowClient::MouseUp& message) { if (auto* window = Window::from_window_id(message.window_id())) Core::EventLoop::current().post_event(*window, make(Event::MouseUp, message.mouse_position(), message.buttons(), to_gmousebutton(message.button()), message.modifiers(), message.wheel_delta())); } void WindowServerConnection::handle(const Messages::WindowClient::MouseMove& message) { if (auto* window = Window::from_window_id(message.window_id())) { if (message.is_drag()) Core::EventLoop::current().post_event(*window, make(Event::DragMove, message.mouse_position(), message.drag_data_type())); else Core::EventLoop::current().post_event(*window, make(Event::MouseMove, message.mouse_position(), message.buttons(), to_gmousebutton(message.button()), message.modifiers(), message.wheel_delta())); } } void WindowServerConnection::handle(const Messages::WindowClient::MouseDoubleClick& message) { if (auto* window = Window::from_window_id(message.window_id())) Core::EventLoop::current().post_event(*window, make(Event::MouseDoubleClick, message.mouse_position(), message.buttons(), to_gmousebutton(message.button()), message.modifiers(), message.wheel_delta())); } void WindowServerConnection::handle(const Messages::WindowClient::MouseWheel& message) { if (auto* window = Window::from_window_id(message.window_id())) Core::EventLoop::current().post_event(*window, make(Event::MouseWheel, message.mouse_position(), message.buttons(), to_gmousebutton(message.button()), message.modifiers(), message.wheel_delta())); } void WindowServerConnection::handle(const Messages::WindowClient::MenuItemActivated& message) { auto* menu = Menu::from_menu_id(message.menu_id()); if (!menu) { dbgprintf("EventLoop received event for invalid menu ID %d\n", message.menu_id()); return; } if (auto* action = menu->action_at(message.identifier())) action->activate(menu); } void WindowServerConnection::handle(const Messages::WindowClient::WM_WindowStateChanged& message) { if (auto* window = Window::from_window_id(message.wm_id())) Core::EventLoop::current().post_event(*window, make(message.client_id(), message.window_id(), message.title(), message.rect(), message.is_active(), static_cast(message.window_type()), message.is_minimized(), message.is_frameless())); } void WindowServerConnection::handle(const Messages::WindowClient::WM_WindowRectChanged& message) { if (auto* window = Window::from_window_id(message.wm_id())) Core::EventLoop::current().post_event(*window, make(message.client_id(), message.window_id(), message.rect())); } void WindowServerConnection::handle(const Messages::WindowClient::WM_WindowIconBitmapChanged& message) { if (auto* window = Window::from_window_id(message.wm_id())) Core::EventLoop::current().post_event(*window, make(message.client_id(), message.window_id(), message.icon_buffer_id(), message.icon_size())); } void WindowServerConnection::handle(const Messages::WindowClient::WM_WindowRemoved& message) { if (auto* window = Window::from_window_id(message.wm_id())) Core::EventLoop::current().post_event(*window, make(message.client_id(), message.window_id())); } void WindowServerConnection::handle(const Messages::WindowClient::ScreenRectChanged& message) { Desktop::the().did_receive_screen_rect({}, message.rect()); } void WindowServerConnection::handle(const Messages::WindowClient::ClipboardContentsChanged& message) { Clipboard::the().did_receive_clipboard_contents_changed({}, message.content_type()); } void WindowServerConnection::handle(const Messages::WindowClient::AsyncSetWallpaperFinished&) { // This is handled manually by Desktop::set_wallpaper(). } void WindowServerConnection::handle(const Messages::WindowClient::DragDropped& message) { if (auto* window = Window::from_window_id(message.window_id())) { auto mime_data = Core::MimeData::construct(); mime_data->set_data(message.data_type(), message.data().to_byte_buffer()); Core::EventLoop::current().post_event(*window, make(message.mouse_position(), message.text(), mime_data)); } } void WindowServerConnection::handle(const Messages::WindowClient::DragAccepted&) { DragOperation::notify_accepted({}); } void WindowServerConnection::handle(const Messages::WindowClient::DragCancelled&) { DragOperation::notify_cancelled({}); } void WindowServerConnection::handle(const Messages::WindowClient::WindowStateChanged& message) { if (auto* window = Window::from_window_id(message.window_id())) window->notify_state_changed({}, message.minimized(), message.occluded()); } void WindowServerConnection::handle(const Messages::WindowClient::DisplayLinkNotification&) { if (m_display_link_notification_pending) return; m_display_link_notification_pending = true; deferred_invoke([this](auto&) { DisplayLink::notify({}); m_display_link_notification_pending = false; }); } }