Serenity Operating System
at master 399 lines 16 kB view raw
1/* 2 * Copyright (c) 2018-2022, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/Debug.h> 8#include <AK/StringBuilder.h> 9#include <LibCore/EventLoop.h> 10#include <LibCore/MimeData.h> 11#include <LibGUI/Action.h> 12#include <LibGUI/Application.h> 13#include <LibGUI/CommandPalette.h> 14#include <LibGUI/ConnectionToWindowServer.h> 15#include <LibGUI/Desktop.h> 16#include <LibGUI/DisplayLink.h> 17#include <LibGUI/DragOperation.h> 18#include <LibGUI/EmojiInputDialog.h> 19#include <LibGUI/Event.h> 20#include <LibGUI/Menu.h> 21#include <LibGUI/MouseTracker.h> 22#include <LibGUI/Shortcut.h> 23#include <LibGUI/Window.h> 24#include <LibGfx/Bitmap.h> 25#include <LibGfx/Font/FontDatabase.h> 26#include <LibGfx/Palette.h> 27#include <LibGfx/SystemTheme.h> 28 29namespace GUI { 30 31ConnectionToWindowServer& ConnectionToWindowServer::the() 32{ 33 static RefPtr<ConnectionToWindowServer> s_connection = nullptr; 34 if (!s_connection) 35 s_connection = ConnectionToWindowServer::try_create().release_value_but_fixme_should_propagate_errors(); 36 return *s_connection; 37} 38 39static void set_system_theme_from_anonymous_buffer(Core::AnonymousBuffer buffer) 40{ 41 Gfx::set_system_theme(buffer); 42 Application::the()->set_system_palette(buffer); 43} 44 45ConnectionToWindowServer::ConnectionToWindowServer(NonnullOwnPtr<Core::LocalSocket> socket) 46 : IPC::ConnectionToServer<WindowClientEndpoint, WindowServerEndpoint>(*this, move(socket)) 47{ 48 // NOTE: WindowServer automatically sends a "fast_greet" message to us when we connect. 49 // All we have to do is wait for it to arrive. This avoids a round-trip during application startup. 50 auto message = wait_for_specific_message<Messages::WindowClient::FastGreet>(); 51 set_system_theme_from_anonymous_buffer(message->theme_buffer()); 52 Desktop::the().did_receive_screen_rects({}, message->screen_rects(), message->main_screen_index(), message->workspace_rows(), message->workspace_columns()); 53 Desktop::the().set_system_effects(message->effects()); 54 Gfx::FontDatabase::set_default_font_query(message->default_font_query()); 55 Gfx::FontDatabase::set_fixed_width_font_query(message->fixed_width_font_query()); 56 Gfx::FontDatabase::set_window_title_font_query(message->window_title_font_query()); 57 m_client_id = message->client_id(); 58} 59 60void ConnectionToWindowServer::fast_greet(Vector<Gfx::IntRect> const&, u32, u32, u32, Core::AnonymousBuffer const&, DeprecatedString const&, DeprecatedString const&, DeprecatedString const&, Vector<bool> const&, i32) 61{ 62 // NOTE: This message is handled in the constructor. 63} 64 65void ConnectionToWindowServer::update_system_theme(Core::AnonymousBuffer const& theme_buffer) 66{ 67 set_system_theme_from_anonymous_buffer(theme_buffer); 68 Window::update_all_windows({}); 69 Window::for_each_window({}, [](auto& window) { 70 Core::EventLoop::current().post_event(window, make<ThemeChangeEvent>()); 71 }); 72 73 Application::the()->dispatch_event(*make<ThemeChangeEvent>()); 74} 75 76void ConnectionToWindowServer::update_system_fonts(DeprecatedString const& default_font_query, DeprecatedString const& fixed_width_font_query, DeprecatedString const& window_title_font_query) 77{ 78 Gfx::FontDatabase::set_default_font_query(default_font_query); 79 Gfx::FontDatabase::set_fixed_width_font_query(fixed_width_font_query); 80 Gfx::FontDatabase::set_window_title_font_query(window_title_font_query); 81 Window::update_all_windows({}); 82 Window::for_each_window({}, [](auto& window) { 83 Core::EventLoop::current().post_event(window, make<FontsChangeEvent>()); 84 }); 85} 86 87void ConnectionToWindowServer::update_system_effects(Vector<bool> const& effects) 88{ 89 Desktop::the().set_system_effects(effects); 90} 91 92void ConnectionToWindowServer::paint(i32 window_id, Gfx::IntSize window_size, Vector<Gfx::IntRect> const& rects) 93{ 94 if (auto* window = Window::from_window_id(window_id)) 95 Core::EventLoop::current().post_event(*window, make<MultiPaintEvent>(rects, window_size)); 96} 97 98void ConnectionToWindowServer::window_resized(i32 window_id, Gfx::IntRect const& new_rect) 99{ 100 if (auto* window = Window::from_window_id(window_id)) { 101 Core::EventLoop::current().post_event(*window, make<ResizeEvent>(new_rect.size())); 102 } 103} 104 105void ConnectionToWindowServer::window_moved(i32 window_id, Gfx::IntRect const& new_rect) 106{ 107 if (auto* window = Window::from_window_id(window_id)) 108 Core::EventLoop::current().post_event(*window, make<MoveEvent>(new_rect.location())); 109} 110 111void ConnectionToWindowServer::window_activated(i32 window_id) 112{ 113 if (auto* window = Window::from_window_id(window_id)) 114 Core::EventLoop::current().post_event(*window, make<Event>(Event::WindowBecameActive)); 115} 116 117void ConnectionToWindowServer::window_deactivated(i32 window_id) 118{ 119 if (auto* window = Window::from_window_id(window_id)) 120 Core::EventLoop::current().post_event(*window, make<Event>(Event::WindowBecameInactive)); 121} 122 123void ConnectionToWindowServer::window_input_preempted(i32 window_id) 124{ 125 if (auto* window = Window::from_window_id(window_id)) 126 Core::EventLoop::current().post_event(*window, make<Event>(Event::WindowInputPreempted)); 127} 128 129void ConnectionToWindowServer::window_input_restored(i32 window_id) 130{ 131 if (auto* window = Window::from_window_id(window_id)) 132 Core::EventLoop::current().post_event(*window, make<Event>(Event::WindowInputRestored)); 133} 134 135void ConnectionToWindowServer::window_close_request(i32 window_id) 136{ 137 if (auto* window = Window::from_window_id(window_id)) 138 Core::EventLoop::current().post_event(*window, make<Event>(Event::WindowCloseRequest)); 139} 140 141void ConnectionToWindowServer::window_entered(i32 window_id) 142{ 143 if (auto* window = Window::from_window_id(window_id)) 144 Core::EventLoop::current().post_event(*window, make<Event>(Event::WindowEntered)); 145} 146 147void ConnectionToWindowServer::window_left(i32 window_id) 148{ 149 if (auto* window = Window::from_window_id(window_id)) 150 Core::EventLoop::current().post_event(*window, make<Event>(Event::WindowLeft)); 151} 152 153static Action* action_for_shortcut(Window& window, Shortcut const& shortcut) 154{ 155 if (!shortcut.is_valid()) 156 return nullptr; 157 158 dbgln_if(KEYBOARD_SHORTCUTS_DEBUG, "Looking up action for {}", shortcut.to_deprecated_string()); 159 160 for (auto* widget = window.focused_widget(); widget; widget = widget->parent_widget()) { 161 if (auto* action = widget->action_for_shortcut(shortcut)) { 162 dbgln_if(KEYBOARD_SHORTCUTS_DEBUG, " > Focused widget {} gave action: {} {} (enabled: {}, shortcut: {}, alt-shortcut: {})", *widget, action, action->text(), action->is_enabled(), action->shortcut().to_deprecated_string(), action->alternate_shortcut().to_deprecated_string()); 163 return action; 164 } 165 } 166 167 if (auto* action = window.action_for_shortcut(shortcut)) { 168 dbgln_if(KEYBOARD_SHORTCUTS_DEBUG, " > Asked window {}, got action: {} {} (enabled: {}, shortcut: {}, alt-shortcut: {})", window, action, action->text(), action->is_enabled(), action->shortcut().to_deprecated_string(), action->alternate_shortcut().to_deprecated_string()); 169 return action; 170 } 171 172 // NOTE: Application-global shortcuts are ignored while a blocking modal window is up. 173 if (!window.is_blocking() && !window.is_popup()) { 174 if (auto* action = Application::the()->action_for_shortcut(shortcut)) { 175 dbgln_if(KEYBOARD_SHORTCUTS_DEBUG, " > Asked application, got action: {} {} (enabled: {}, shortcut: {}, alt-shortcut: {})", action, action->text(), action->is_enabled(), action->shortcut().to_deprecated_string(), action->alternate_shortcut().to_deprecated_string()); 176 return action; 177 } 178 } 179 180 return nullptr; 181} 182 183void ConnectionToWindowServer::key_down(i32 window_id, u32 code_point, u32 key, u32 modifiers, u32 scancode) 184{ 185 auto* window = Window::from_window_id(window_id); 186 if (!window) 187 return; 188 189 auto key_event = make<KeyEvent>(Event::KeyDown, (KeyCode)key, modifiers, code_point, scancode); 190 191 bool focused_widget_accepts_emoji_input = window->focused_widget() && window->focused_widget()->on_emoji_input; 192 if (!window->blocks_emoji_input() && focused_widget_accepts_emoji_input && (modifiers == (Mod_Ctrl | Mod_Alt)) && key == Key_Space) { 193 auto emoji_input_dialog = EmojiInputDialog::construct(window); 194 if (emoji_input_dialog->exec() != EmojiInputDialog::ExecResult::OK) 195 return; 196 197 window->focused_widget()->on_emoji_input(emoji_input_dialog->selected_emoji_text()); 198 return; 199 } 200 201 Core::EventLoop::current().post_event(*window, move(key_event)); 202} 203 204void ConnectionToWindowServer::key_up(i32 window_id, u32 code_point, u32 key, u32 modifiers, u32 scancode) 205{ 206 auto* window = Window::from_window_id(window_id); 207 if (!window) 208 return; 209 210 auto key_event = make<KeyEvent>(Event::KeyUp, (KeyCode)key, modifiers, code_point, scancode); 211 Core::EventLoop::current().post_event(*window, move(key_event)); 212} 213 214static MouseButton to_mouse_button(u32 button) 215{ 216 switch (button) { 217 case 0: 218 return MouseButton::None; 219 case 1: 220 return MouseButton::Primary; 221 case 2: 222 return MouseButton::Secondary; 223 case 4: 224 return MouseButton::Middle; 225 case 8: 226 return MouseButton::Backward; 227 case 16: 228 return MouseButton::Forward; 229 default: 230 VERIFY_NOT_REACHED(); 231 break; 232 } 233} 234 235void ConnectionToWindowServer::mouse_down(i32 window_id, Gfx::IntPoint mouse_position, u32 button, u32 buttons, u32 modifiers, i32 wheel_delta_x, i32 wheel_delta_y, i32 wheel_raw_delta_x, i32 wheel_raw_delta_y) 236{ 237 auto* window = Window::from_window_id(window_id); 238 if (!window) 239 return; 240 241 auto mouse_event = make<MouseEvent>(Event::MouseDown, mouse_position, buttons, to_mouse_button(button), modifiers, wheel_delta_x, wheel_delta_y, wheel_raw_delta_x, wheel_raw_delta_y); 242 243 if (auto* action = action_for_shortcut(*window, Shortcut(mouse_event->modifiers(), mouse_event->button()))) { 244 if (action->is_enabled()) { 245 action->flash_menubar_menu(*window); 246 action->activate(); 247 return; 248 } 249 if (action->swallow_key_event_when_disabled()) 250 return; 251 } 252 253 Core::EventLoop::current().post_event(*window, move(mouse_event)); 254} 255 256void ConnectionToWindowServer::mouse_up(i32 window_id, Gfx::IntPoint mouse_position, u32 button, u32 buttons, u32 modifiers, i32 wheel_delta_x, i32 wheel_delta_y, i32 wheel_raw_delta_x, i32 wheel_raw_delta_y) 257{ 258 if (auto* window = Window::from_window_id(window_id)) 259 Core::EventLoop::current().post_event(*window, make<MouseEvent>(Event::MouseUp, mouse_position, buttons, to_mouse_button(button), modifiers, wheel_delta_x, wheel_delta_y, wheel_raw_delta_x, wheel_raw_delta_y)); 260} 261 262void ConnectionToWindowServer::mouse_move(i32 window_id, Gfx::IntPoint mouse_position, u32 button, u32 buttons, u32 modifiers, i32 wheel_delta_x, i32 wheel_delta_y, i32 wheel_raw_delta_x, i32 wheel_raw_delta_y, bool is_drag, Vector<DeprecatedString> const& mime_types) 263{ 264 if (auto* window = Window::from_window_id(window_id)) { 265 if (is_drag) 266 Core::EventLoop::current().post_event(*window, make<DragEvent>(Event::DragMove, mouse_position, mime_types)); 267 else 268 Core::EventLoop::current().post_event(*window, make<MouseEvent>(Event::MouseMove, mouse_position, buttons, to_mouse_button(button), modifiers, wheel_delta_x, wheel_delta_y, wheel_raw_delta_x, wheel_raw_delta_y)); 269 } 270} 271 272void ConnectionToWindowServer::mouse_double_click(i32 window_id, Gfx::IntPoint mouse_position, u32 button, u32 buttons, u32 modifiers, i32 wheel_delta_x, i32 wheel_delta_y, i32 wheel_raw_delta_x, i32 wheel_raw_delta_y) 273{ 274 if (auto* window = Window::from_window_id(window_id)) 275 Core::EventLoop::current().post_event(*window, make<MouseEvent>(Event::MouseDoubleClick, mouse_position, buttons, to_mouse_button(button), modifiers, wheel_delta_x, wheel_delta_y, wheel_raw_delta_x, wheel_raw_delta_y)); 276} 277 278void ConnectionToWindowServer::mouse_wheel(i32 window_id, Gfx::IntPoint mouse_position, u32 button, u32 buttons, u32 modifiers, i32 wheel_delta_x, i32 wheel_delta_y, i32 wheel_raw_delta_x, i32 wheel_raw_delta_y) 279{ 280 if (auto* window = Window::from_window_id(window_id)) 281 Core::EventLoop::current().post_event(*window, make<MouseEvent>(Event::MouseWheel, mouse_position, buttons, to_mouse_button(button), modifiers, wheel_delta_x, wheel_delta_y, wheel_raw_delta_x, wheel_raw_delta_y)); 282} 283 284void ConnectionToWindowServer::menu_visibility_did_change(i32 menu_id, bool visible) 285{ 286 auto* menu = Menu::from_menu_id(menu_id); 287 if (!menu) { 288 dbgln("EventLoop received visibility change event for invalid menu ID {}", menu_id); 289 return; 290 } 291 menu->visibility_did_change({}, visible); 292} 293 294void ConnectionToWindowServer::menu_item_activated(i32 menu_id, u32 identifier) 295{ 296 auto* menu = Menu::from_menu_id(menu_id); 297 if (!menu) { 298 dbgln("EventLoop received event for invalid menu ID {}", menu_id); 299 return; 300 } 301 if (auto* action = menu->action_at(identifier)) 302 action->activate(menu); 303} 304 305void ConnectionToWindowServer::menu_item_entered(i32 menu_id, u32 identifier) 306{ 307 auto* menu = Menu::from_menu_id(menu_id); 308 if (!menu) { 309 dbgln("ConnectionToWindowServer received MenuItemEntered for invalid menu ID {}", menu_id); 310 return; 311 } 312 auto* action = menu->action_at(identifier); 313 if (!action) 314 return; 315 auto* app = Application::the(); 316 if (!app) 317 return; 318 Core::EventLoop::current().post_event(*app, make<ActionEvent>(GUI::Event::ActionEnter, *action)); 319} 320 321void ConnectionToWindowServer::menu_item_left(i32 menu_id, u32 identifier) 322{ 323 auto* menu = Menu::from_menu_id(menu_id); 324 if (!menu) { 325 dbgln("ConnectionToWindowServer received MenuItemLeft for invalid menu ID {}", menu_id); 326 return; 327 } 328 auto* action = menu->action_at(identifier); 329 if (!action) 330 return; 331 auto* app = Application::the(); 332 if (!app) 333 return; 334 Core::EventLoop::current().post_event(*app, make<ActionEvent>(GUI::Event::ActionLeave, *action)); 335} 336 337void ConnectionToWindowServer::screen_rects_changed(Vector<Gfx::IntRect> const& rects, u32 main_screen_index, u32 workspace_rows, u32 workspace_columns) 338{ 339 Desktop::the().did_receive_screen_rects({}, rects, main_screen_index, workspace_rows, workspace_columns); 340 Window::for_each_window({}, [&](auto& window) { 341 Core::EventLoop::current().post_event(window, make<ScreenRectsChangeEvent>(rects, main_screen_index)); 342 }); 343} 344 345void ConnectionToWindowServer::applet_area_rect_changed(Gfx::IntRect const& rect) 346{ 347 Window::for_each_window({}, [&](auto& window) { 348 Core::EventLoop::current().post_event(window, make<AppletAreaRectChangeEvent>(rect)); 349 }); 350} 351 352void ConnectionToWindowServer::drag_dropped(i32 window_id, Gfx::IntPoint mouse_position, DeprecatedString const& text, HashMap<DeprecatedString, ByteBuffer> const& mime_data) 353{ 354 if (auto* window = Window::from_window_id(window_id)) { 355 auto mime_data_obj = Core::MimeData::construct(mime_data); 356 Core::EventLoop::current().post_event(*window, make<DropEvent>(mouse_position, text, mime_data_obj)); 357 } 358} 359 360void ConnectionToWindowServer::drag_accepted() 361{ 362 DragOperation::notify_accepted({}); 363} 364 365void ConnectionToWindowServer::drag_cancelled() 366{ 367 DragOperation::notify_cancelled({}); 368 Application::the()->notify_drag_cancelled({}); 369} 370 371void ConnectionToWindowServer::window_state_changed(i32 window_id, bool minimized, bool maximized, bool occluded) 372{ 373 if (auto* window = Window::from_window_id(window_id)) 374 window->notify_state_changed({}, minimized, maximized, occluded); 375} 376 377void ConnectionToWindowServer::display_link_notification() 378{ 379 if (m_display_link_notification_pending) 380 return; 381 382 m_display_link_notification_pending = true; 383 deferred_invoke([this] { 384 DisplayLink::notify({}); 385 m_display_link_notification_pending = false; 386 }); 387} 388 389void ConnectionToWindowServer::track_mouse_move(Gfx::IntPoint mouse_position) 390{ 391 MouseTracker::track_mouse_move({}, mouse_position); 392} 393 394void ConnectionToWindowServer::ping() 395{ 396 async_pong(); 397} 398 399}