Serenity Operating System
at hosted 348 lines 14 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <AK/SharedBuffer.h> 28#include <LibCore/EventLoop.h> 29#include <LibCore/MimeData.h> 30#include <LibGUI/Action.h> 31#include <LibGUI/Application.h> 32#include <LibGUI/Clipboard.h> 33#include <LibGUI/Desktop.h> 34#include <LibGUI/DisplayLink.h> 35#include <LibGUI/DragOperation.h> 36#include <LibGUI/Event.h> 37#include <LibGUI/Menu.h> 38#include <LibGUI/Widget.h> 39#include <LibGUI/Window.h> 40#include <LibGUI/WindowServerConnection.h> 41#include <LibGfx/Bitmap.h> 42#include <LibGfx/Palette.h> 43#include <LibGfx/SystemTheme.h> 44 45//#define GEVENTLOOP_DEBUG 46 47namespace GUI { 48 49WindowServerConnection& WindowServerConnection::the() 50{ 51 static WindowServerConnection* s_connection = nullptr; 52 if (!s_connection) 53 s_connection = new WindowServerConnection; 54 return *s_connection; 55} 56 57static void set_system_theme_from_shbuf_id(int id) 58{ 59 auto system_theme = SharedBuffer::create_from_shbuf_id(id); 60 ASSERT(system_theme); 61 Gfx::set_system_theme(*system_theme); 62 Application::the().set_system_palette(*system_theme); 63} 64 65void WindowServerConnection::handshake() 66{ 67 auto response = send_sync<Messages::WindowServer::Greet>(); 68 set_my_client_id(response->client_id()); 69 set_system_theme_from_shbuf_id(response->system_theme_buffer_id()); 70 Desktop::the().did_receive_screen_rect({}, response->screen_rect()); 71} 72 73void WindowServerConnection::handle(const Messages::WindowClient::UpdateSystemTheme& message) 74{ 75 set_system_theme_from_shbuf_id(message.system_theme_buffer_id()); 76 Window::update_all_windows({}); 77 Window::for_each_window({}, [](auto& window) { 78 Core::EventLoop::current().post_event(window, make<ThemeChangeEvent>()); 79 }); 80} 81 82void WindowServerConnection::handle(const Messages::WindowClient::Paint& message) 83{ 84#ifdef GEVENTLOOP_DEBUG 85 dbgprintf("WID=%d Paint\n", message.window_id()); 86#endif 87 if (auto* window = Window::from_window_id(message.window_id())) 88 Core::EventLoop::current().post_event(*window, make<MultiPaintEvent>(message.rects(), message.window_size())); 89} 90 91void WindowServerConnection::handle(const Messages::WindowClient::WindowResized& message) 92{ 93 if (auto* window = Window::from_window_id(message.window_id())) { 94 Core::EventLoop::current().post_event(*window, make<ResizeEvent>(message.old_rect().size(), message.new_rect().size())); 95 } 96} 97 98void WindowServerConnection::handle(const Messages::WindowClient::WindowActivated& message) 99{ 100#ifdef GEVENTLOOP_DEBUG 101 dbgprintf("(%d) WID=%d WindowActivated\n", getpid(), message.window_id()); 102#endif 103 if (auto* window = Window::from_window_id(message.window_id())) 104 Core::EventLoop::current().post_event(*window, make<Event>(Event::WindowBecameActive)); 105} 106 107void WindowServerConnection::handle(const Messages::WindowClient::WindowDeactivated& message) 108{ 109#ifdef GEVENTLOOP_DEBUG 110 dbgprintf("(%d) WID=%d WindowDeactivated\n", getpid(), message.window_id()); 111#endif 112 if (auto* window = Window::from_window_id(message.window_id())) 113 Core::EventLoop::current().post_event(*window, make<Event>(Event::WindowBecameInactive)); 114} 115 116void WindowServerConnection::handle(const Messages::WindowClient::WindowCloseRequest& message) 117{ 118 if (auto* window = Window::from_window_id(message.window_id())) 119 Core::EventLoop::current().post_event(*window, make<Event>(Event::WindowCloseRequest)); 120} 121 122void WindowServerConnection::handle(const Messages::WindowClient::WindowEntered& message) 123{ 124 if (auto* window = Window::from_window_id(message.window_id())) 125 Core::EventLoop::current().post_event(*window, make<Event>(Event::WindowEntered)); 126} 127 128void WindowServerConnection::handle(const Messages::WindowClient::WindowLeft& message) 129{ 130 if (auto* window = Window::from_window_id(message.window_id())) 131 Core::EventLoop::current().post_event(*window, make<Event>(Event::WindowLeft)); 132} 133 134void WindowServerConnection::handle(const Messages::WindowClient::KeyDown& message) 135{ 136#ifdef GEVENTLOOP_DEBUG 137 dbgprintf("WID=%d KeyDown character=0x%02x\n", message.window_id(), message.character()); 138#endif 139 auto* window = Window::from_window_id(message.window_id()); 140 if (!window) 141 return; 142 143 auto key_event = make<KeyEvent>(Event::KeyDown, message.key(), message.modifiers()); 144 if (message.character() != '\0') { 145 char ch = message.character(); 146 key_event->m_text = String(&ch, 1); 147 } 148 149 Action* action = nullptr; 150 151 if (auto* focused_widget = window->focused_widget()) 152 action = focused_widget->action_for_key_event(*key_event); 153 154 if (!action) 155 action = window->action_for_key_event(*key_event); 156 157 if (!action) 158 action = Application::the().action_for_key_event(*key_event); 159 160 if (action && action->is_enabled()) { 161 action->activate(); 162 return; 163 } 164 Core::EventLoop::current().post_event(*window, move(key_event)); 165} 166 167void WindowServerConnection::handle(const Messages::WindowClient::KeyUp& message) 168{ 169#ifdef GEVENTLOOP_DEBUG 170 dbgprintf("WID=%d KeyUp character=0x%02x\n", message.window_id(), message.character()); 171#endif 172 auto* window = Window::from_window_id(message.window_id()); 173 if (!window) 174 return; 175 176 auto key_event = make<KeyEvent>(Event::KeyUp, message.key(), message.modifiers()); 177 if (message.character() != '\0') { 178 char ch = message.character(); 179 key_event->m_text = String(&ch, 1); 180 } 181 182 Core::EventLoop::current().post_event(*window, move(key_event)); 183} 184 185MouseButton to_gmousebutton(u32 button) 186{ 187 switch (button) { 188 case 0: 189 return MouseButton::None; 190 case 1: 191 return MouseButton::Left; 192 case 2: 193 return MouseButton::Right; 194 case 4: 195 return MouseButton::Middle; 196 default: 197 ASSERT_NOT_REACHED(); 198 break; 199 } 200} 201 202void WindowServerConnection::handle(const Messages::WindowClient::MouseDown& message) 203{ 204#ifdef GEVENTLOOP_DEBUG 205 dbgprintf("WID=%d MouseDown %d,%d,%d\n", message.window_id(), message.mouse_position().x(), message.mouse_position().y(), message.wheel_delta(); 206#endif 207 208 if (auto* window = Window::from_window_id(message.window_id())) 209 Core::EventLoop::current().post_event(*window, make<MouseEvent>(Event::MouseDown, message.mouse_position(), message.buttons(), to_gmousebutton(message.button()), message.modifiers(), message.wheel_delta())); 210} 211 212void WindowServerConnection::handle(const Messages::WindowClient::MouseUp& message) 213{ 214#ifdef GEVENTLOOP_DEBUG 215 dbgprintf("WID=%d MouseUp %d,%d,%d\n", message.window_id(), message.mouse_position().x(), message.mouse_position().y(), message.wheel_delta(); 216#endif 217 218 if (auto* window = Window::from_window_id(message.window_id())) 219 Core::EventLoop::current().post_event(*window, make<MouseEvent>(Event::MouseUp, message.mouse_position(), message.buttons(), to_gmousebutton(message.button()), message.modifiers(), message.wheel_delta())); 220} 221 222void WindowServerConnection::handle(const Messages::WindowClient::MouseMove& message) 223{ 224#ifdef GEVENTLOOP_DEBUG 225 dbgprintf("WID=%d MouseMove %d,%d,%d\n", message.window_id(), message.mouse_position().x(), message.mouse_position().y(), message.wheel_delta(); 226#endif 227 228 if (auto* window = Window::from_window_id(message.window_id())) { 229 if (message.is_drag()) 230 Core::EventLoop::current().post_event(*window, make<DragEvent>(Event::DragMove, message.mouse_position(), message.drag_data_type())); 231 else 232 Core::EventLoop::current().post_event(*window, make<MouseEvent>(Event::MouseMove, message.mouse_position(), message.buttons(), to_gmousebutton(message.button()), message.modifiers(), message.wheel_delta())); 233 } 234} 235 236void WindowServerConnection::handle(const Messages::WindowClient::MouseDoubleClick& message) 237{ 238#ifdef GEVENTLOOP_DEBUG 239 dbgprintf("WID=%d MouseDoubleClick %d,%d,%d\n", message.window_id(), message.mouse_position().x(), message.mouse_position().y(), message.wheel_delta(); 240#endif 241 242 if (auto* window = Window::from_window_id(message.window_id())) 243 Core::EventLoop::current().post_event(*window, make<MouseEvent>(Event::MouseDoubleClick, message.mouse_position(), message.buttons(), to_gmousebutton(message.button()), message.modifiers(), message.wheel_delta())); 244} 245 246void WindowServerConnection::handle(const Messages::WindowClient::MouseWheel& message) 247{ 248#ifdef GEVENTLOOP_DEBUG 249 dbgprintf("WID=%d MouseWheel %d,%d,%d\n", message.window_id(), message.mouse_position().x(), message.mouse_position().y(), message.wheel_delta(); 250#endif 251 252 if (auto* window = Window::from_window_id(message.window_id())) 253 Core::EventLoop::current().post_event(*window, make<MouseEvent>(Event::MouseWheel, message.mouse_position(), message.buttons(), to_gmousebutton(message.button()), message.modifiers(), message.wheel_delta())); 254} 255 256void WindowServerConnection::handle(const Messages::WindowClient::MenuItemActivated& message) 257{ 258 auto* menu = Menu::from_menu_id(message.menu_id()); 259 if (!menu) { 260 dbgprintf("EventLoop received event for invalid menu ID %d\n", message.menu_id()); 261 return; 262 } 263 if (auto* action = menu->action_at(message.identifier())) 264 action->activate(menu); 265} 266 267void WindowServerConnection::handle(const Messages::WindowClient::WM_WindowStateChanged& message) 268{ 269#ifdef GEVENTLOOP_DEBUG 270 dbgprintf("EventLoop: handle_wm_event: %d\n", (int)event.type); 271#endif 272 if (auto* window = Window::from_window_id(message.wm_id())) 273 Core::EventLoop::current().post_event(*window, make<WMWindowStateChangedEvent>(message.client_id(), message.window_id(), message.title(), message.rect(), message.is_active(), static_cast<WindowType>(message.window_type()), message.is_minimized())); 274} 275 276void WindowServerConnection::handle(const Messages::WindowClient::WM_WindowRectChanged& message) 277{ 278#ifdef GEVENTLOOP_DEBUG 279 dbgprintf("EventLoop: handle_wm_event: %d\n", (int)event.type); 280#endif 281 if (auto* window = Window::from_window_id(message.wm_id())) 282 Core::EventLoop::current().post_event(*window, make<WMWindowRectChangedEvent>(message.client_id(), message.window_id(), message.rect())); 283} 284 285void WindowServerConnection::handle(const Messages::WindowClient::WM_WindowIconBitmapChanged& message) 286{ 287#ifdef GEVENTLOOP_DEBUG 288 dbgprintf("EventLoop: handle_wm_event: %d\n", (int)event.type); 289#endif 290 if (auto* window = Window::from_window_id(message.wm_id())) 291 Core::EventLoop::current().post_event(*window, make<WMWindowIconBitmapChangedEvent>(message.client_id(), message.window_id(), message.icon_buffer_id(), message.icon_size())); 292} 293 294void WindowServerConnection::handle(const Messages::WindowClient::WM_WindowRemoved& message) 295{ 296#ifdef GEVENTLOOP_DEBUG 297 dbgprintf("EventLoop: handle_wm_event: %d\n", (int)event.type); 298#endif 299 if (auto* window = Window::from_window_id(message.wm_id())) 300 Core::EventLoop::current().post_event(*window, make<WMWindowRemovedEvent>(message.client_id(), message.window_id())); 301} 302 303void WindowServerConnection::handle(const Messages::WindowClient::ScreenRectChanged& message) 304{ 305 Desktop::the().did_receive_screen_rect({}, message.rect()); 306} 307 308void WindowServerConnection::handle(const Messages::WindowClient::ClipboardContentsChanged& message) 309{ 310 Clipboard::the().did_receive_clipboard_contents_changed({}, message.content_type()); 311} 312 313void WindowServerConnection::handle(const Messages::WindowClient::AsyncSetWallpaperFinished&) 314{ 315 // This is handled manually by Desktop::set_wallpaper(). 316} 317 318void WindowServerConnection::handle(const Messages::WindowClient::DragDropped& message) 319{ 320 if (auto* window = Window::from_window_id(message.window_id())) { 321 auto mime_data = Core::MimeData::construct(); 322 mime_data->set_data(message.data_type(), message.data().to_byte_buffer()); 323 Core::EventLoop::current().post_event(*window, make<DropEvent>(message.mouse_position(), message.text(), mime_data)); 324 } 325} 326 327void WindowServerConnection::handle(const Messages::WindowClient::DragAccepted&) 328{ 329 DragOperation::notify_accepted({}); 330} 331 332void WindowServerConnection::handle(const Messages::WindowClient::DragCancelled&) 333{ 334 DragOperation::notify_cancelled({}); 335} 336 337void WindowServerConnection::handle(const Messages::WindowClient::WindowStateChanged& message) 338{ 339 if (auto* window = Window::from_window_id(message.window_id())) 340 window->notify_state_changed({}, message.minimized(), message.occluded()); 341} 342 343void WindowServerConnection::handle(const Messages::WindowClient::DisplayLinkNotification&) 344{ 345 DisplayLink::notify({}); 346} 347 348}