#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include HashMap>* s_connections; void WSClientConnection::for_each_client(Function callback) { if (!s_connections) return; for (auto& it : *s_connections) { callback(*it.value); } } WSClientConnection* WSClientConnection::from_client_id(int client_id) { if (!s_connections) return nullptr; auto it = s_connections->find(client_id); if (it == s_connections->end()) return nullptr; return (*it).value.ptr(); } WSClientConnection::WSClientConnection(CLocalSocket& client_socket, int client_id) : IClientConnection(*this, client_socket, client_id) { if (!s_connections) s_connections = new HashMap>; s_connections->set(client_id, *this); } WSClientConnection::~WSClientConnection() { auto windows = move(m_windows); } void WSClientConnection::die() { s_connections->remove(client_id()); } void WSClientConnection::notify_about_new_screen_rect(const Rect& rect) { post_message(WindowClient::ScreenRectChanged(rect)); } void WSClientConnection::notify_about_clipboard_contents_changed() { post_message(WindowClient::ClipboardContentsChanged(WSClipboard::the().data_type())); } OwnPtr WSClientConnection::handle(const WindowServer::CreateMenubar&) { int menubar_id = m_next_menubar_id++; auto menubar = make(*this, menubar_id); m_menubars.set(menubar_id, move(menubar)); return make(menubar_id); } OwnPtr WSClientConnection::handle(const WindowServer::DestroyMenubar& message) { int menubar_id = message.menubar_id(); auto it = m_menubars.find(menubar_id); if (it == m_menubars.end()) { did_misbehave("DestroyMenubar: Bad menubar ID"); return nullptr; } auto& menubar = *(*it).value; WSWindowManager::the().close_menubar(menubar); m_menubars.remove(it); return make(); } OwnPtr WSClientConnection::handle(const WindowServer::CreateMenu& message) { int menu_id = m_next_menu_id++; auto menu = WSMenu::construct(this, menu_id, message.menu_title()); m_menus.set(menu_id, move(menu)); return make(menu_id); } OwnPtr WSClientConnection::handle(const WindowServer::DestroyMenu& message) { int menu_id = message.menu_id(); auto it = m_menus.find(menu_id); if (it == m_menus.end()) { did_misbehave("DestroyMenu: Bad menu ID"); return nullptr; } auto& menu = *(*it).value; menu.close(); m_menus.remove(it); remove_child(menu); return make(); } OwnPtr WSClientConnection::handle(const WindowServer::SetApplicationMenubar& message) { int menubar_id = message.menubar_id(); auto it = m_menubars.find(menubar_id); if (it == m_menubars.end()) { did_misbehave("SetApplicationMenubar: Bad menubar ID"); return nullptr; } auto& menubar = *(*it).value; m_app_menubar = menubar.make_weak_ptr(); WSWindowManager::the().notify_client_changed_app_menubar(*this); return make(); } OwnPtr WSClientConnection::handle(const WindowServer::AddMenuToMenubar& message) { int menubar_id = message.menubar_id(); int menu_id = message.menu_id(); auto it = m_menubars.find(menubar_id); auto jt = m_menus.find(menu_id); if (it == m_menubars.end()) { did_misbehave("AddMenuToMenubar: Bad menubar ID"); return nullptr; } if (jt == m_menus.end()) { did_misbehave("AddMenuToMenubar: Bad menu ID"); return nullptr; } auto& menubar = *(*it).value; auto& menu = *(*jt).value; menubar.add_menu(menu); return make(); } OwnPtr WSClientConnection::handle(const WindowServer::AddMenuItem& message) { int menu_id = message.menu_id(); unsigned identifier = message.identifier(); auto it = m_menus.find(menu_id); if (it == m_menus.end()) { dbg() << "AddMenuItem: Bad menu ID: " << menu_id; return nullptr; } auto& menu = *(*it).value; auto menu_item = make(menu, identifier, message.text(), message.shortcut(), message.enabled(), message.checkable(), message.checked()); if (message.icon_buffer_id() != -1) { auto icon_buffer = SharedBuffer::create_from_shared_buffer_id(message.icon_buffer_id()); if (!icon_buffer) return nullptr; // FIXME: Verify that the icon buffer can accomodate a 16x16 bitmap view. auto shared_icon = GraphicsBitmap::create_with_shared_buffer(GraphicsBitmap::Format::RGBA32, icon_buffer.release_nonnull(), { 16, 16 }); menu_item->set_icon(shared_icon); } menu_item->set_submenu_id(message.submenu_id()); menu.add_item(move(menu_item)); return make(); } OwnPtr WSClientConnection::handle(const WindowServer::PopupMenu& message) { int menu_id = message.menu_id(); auto position = message.screen_position(); auto it = m_menus.find(menu_id); if (it == m_menus.end()) { did_misbehave("PopupMenu: Bad menu ID"); return nullptr; } auto& menu = *(*it).value; menu.popup(position); return make(); } OwnPtr WSClientConnection::handle(const WindowServer::DismissMenu& message) { int menu_id = message.menu_id(); auto it = m_menus.find(menu_id); if (it == m_menus.end()) { did_misbehave("DismissMenu: Bad menu ID"); return nullptr; } auto& menu = *(*it).value; menu.close(); return make(); } OwnPtr WSClientConnection::handle(const WindowServer::UpdateMenuItem& message) { int menu_id = message.menu_id(); auto it = m_menus.find(menu_id); if (it == m_menus.end()) { did_misbehave("UpdateMenuItem: Bad menu ID"); return nullptr; } auto& menu = *(*it).value; auto* menu_item = menu.item_with_identifier(message.identifier()); if (!menu_item) { did_misbehave("UpdateMenuItem: Bad menu item identifier"); return nullptr; } menu_item->set_text(message.text()); menu_item->set_shortcut_text(message.shortcut()); menu_item->set_enabled(message.enabled()); menu_item->set_checkable(message.checkable()); if (message.checkable()) menu_item->set_checked(message.checked()); return make(); } OwnPtr WSClientConnection::handle(const WindowServer::AddMenuSeparator& message) { int menu_id = message.menu_id(); auto it = m_menus.find(menu_id); if (it == m_menus.end()) { did_misbehave("AddMenuSeparator: Bad menu ID"); return nullptr; } auto& menu = *(*it).value; menu.add_item(make(menu, WSMenuItem::Separator)); return make(); } OwnPtr WSClientConnection::handle(const WindowServer::MoveWindowToFront& message) { auto it = m_windows.find(message.window_id()); if (it == m_windows.end()) { did_misbehave("MoveWindowToFront: Bad window ID"); return nullptr; } WSWindowManager::the().move_to_front_and_make_active(*(*it).value); return make(); } OwnPtr WSClientConnection::handle(const WindowServer::SetFullscreen& message) { auto it = m_windows.find(message.window_id()); if (it == m_windows.end()) { did_misbehave("SetFullscreen: Bad window ID"); return nullptr; } it->value->set_fullscreen(message.fullscreen()); return make(); } OwnPtr WSClientConnection::handle(const WindowServer::SetWindowOpacity& message) { auto it = m_windows.find(message.window_id()); if (it == m_windows.end()) { did_misbehave("SetWindowOpacity: Bad window ID"); return nullptr; } it->value->set_opacity(message.opacity()); return make(); } void WSClientConnection::handle(const WindowServer::AsyncSetWallpaper& message) { WSCompositor::the().set_wallpaper(message.path(), [&](bool success) { post_message(WindowClient::AsyncSetWallpaperFinished(success)); }); } OwnPtr WSClientConnection::handle(const WindowServer::GetWallpaper&) { return make(WSCompositor::the().wallpaper_path()); } OwnPtr WSClientConnection::handle(const WindowServer::SetResolution& message) { WSWindowManager::the().set_resolution(message.resolution().width(), message.resolution().height()); return make(); } OwnPtr WSClientConnection::handle(const WindowServer::SetWindowTitle& message) { auto it = m_windows.find(message.window_id()); if (it == m_windows.end()) { did_misbehave("SetWindowTitle: Bad window ID"); return nullptr; } it->value->set_title(message.title()); return make(); } OwnPtr WSClientConnection::handle(const WindowServer::GetWindowTitle& message) { auto it = m_windows.find(message.window_id()); if (it == m_windows.end()) { did_misbehave("GetWindowTitle: Bad window ID"); return nullptr; } return make(it->value->title()); } OwnPtr WSClientConnection::handle(const WindowServer::SetWindowIconBitmap& message) { auto it = m_windows.find(message.window_id()); if (it == m_windows.end()) { did_misbehave("SetWindowIconBitmap: Bad window ID"); return nullptr; } auto& window = *(*it).value; auto icon_buffer = SharedBuffer::create_from_shared_buffer_id(message.icon_buffer_id()); if (!icon_buffer) { window.set_default_icon(); } else { window.set_icon(GraphicsBitmap::create_with_shared_buffer(GraphicsBitmap::Format::RGBA32, *icon_buffer, message.icon_size())); } window.frame().invalidate_title_bar(); WSWindowManager::the().tell_wm_listeners_window_icon_changed(window); return make(); } OwnPtr WSClientConnection::handle(const WindowServer::SetWindowRect& message) { int window_id = message.window_id(); auto it = m_windows.find(window_id); if (it == m_windows.end()) { did_misbehave("SetWindowRect: Bad window ID"); return nullptr; } auto& window = *(*it).value; if (window.is_fullscreen()) { dbg() << "WSClientConnection: Ignoring SetWindowRect request for fullscreen window"; return nullptr; } window.set_rect(message.rect()); window.request_update(message.rect()); return make(); } OwnPtr WSClientConnection::handle(const WindowServer::GetWindowRect& message) { int window_id = message.window_id(); auto it = m_windows.find(window_id); if (it == m_windows.end()) { did_misbehave("GetWindowRect: Bad window ID"); return nullptr; } return make(it->value->rect()); } OwnPtr WSClientConnection::handle(const WindowServer::SetClipboardContents& message) { auto shared_buffer = SharedBuffer::create_from_shared_buffer_id(message.shared_buffer_id()); if (!shared_buffer) { did_misbehave("SetClipboardContents: Bad shared buffer ID"); return nullptr; } WSClipboard::the().set_data(*shared_buffer, message.content_size(), message.content_type()); return make(); } OwnPtr WSClientConnection::handle(const WindowServer::GetClipboardContents&) { auto& clipboard = WSClipboard::the(); i32 shared_buffer_id = -1; if (clipboard.size()) { // FIXME: Optimize case where an app is copy/pasting within itself. // We can just reuse the SharedBuffer then, since it will have the same peer PID. // It would be even nicer if a SharedBuffer could have an arbitrary number of clients.. RefPtr shared_buffer = SharedBuffer::create_with_size(clipboard.size()); ASSERT(shared_buffer); memcpy(shared_buffer->data(), clipboard.data(), clipboard.size()); shared_buffer->seal(); shared_buffer->share_with(client_pid()); shared_buffer_id = shared_buffer->shared_buffer_id(); // FIXME: This is a workaround for the fact that SharedBuffers will go away if neither side is retaining them. // After we respond to GetClipboardContents, we have to wait for the client to ref the buffer on his side. m_last_sent_clipboard_content = move(shared_buffer); } return make(shared_buffer_id, clipboard.size(), clipboard.data_type()); } OwnPtr WSClientConnection::handle(const WindowServer::CreateWindow& message) { int window_id = m_next_window_id++; auto window = WSWindow::construct(*this, (WSWindowType)message.type(), window_id, message.modal(), message.resizable(), message.fullscreen()); window->set_background_color(message.background_color()); window->set_has_alpha_channel(message.has_alpha_channel()); window->set_title(message.title()); if (!message.fullscreen()) window->set_rect(message.rect()); window->set_show_titlebar(message.show_titlebar()); window->set_opacity(message.opacity()); window->set_size_increment(message.size_increment()); window->set_base_size(message.base_size()); window->invalidate(); m_windows.set(window_id, move(window)); return make(window_id); } OwnPtr WSClientConnection::handle(const WindowServer::DestroyWindow& message) { auto it = m_windows.find(message.window_id()); if (it == m_windows.end()) { did_misbehave("DestroyWindow: Bad window ID"); return nullptr; } auto& window = *(*it).value; WSWindowManager::the().invalidate(window); remove_child(window); ASSERT(it->value.ptr() == &window); m_windows.remove(message.window_id()); return make(); } void WSClientConnection::post_paint_message(WSWindow& window) { auto rect_set = window.take_pending_paint_rects(); if (window.is_minimized()) return; Vector rects; rects.ensure_capacity(rect_set.size()); for (auto& r : rect_set.rects()) { rects.append(r); } post_message(WindowClient::Paint(window.window_id(), window.size(), rects)); } void WSClientConnection::handle(const WindowServer::InvalidateRect& message) { auto it = m_windows.find(message.window_id()); if (it == m_windows.end()) { did_misbehave("InvalidateRect: Bad window ID"); return; } auto& window = *(*it).value; for (int i = 0; i < message.rects().size(); ++i) window.request_update(message.rects()[i].intersected({ {}, window.size() })); } void WSClientConnection::handle(const WindowServer::DidFinishPainting& message) { int window_id = message.window_id(); auto it = m_windows.find(window_id); if (it == m_windows.end()) { did_misbehave("DidFinishPainting: Bad window ID"); return; } auto& window = *(*it).value; for (auto& rect : message.rects()) WSWindowManager::the().invalidate(window, rect); WSWindowSwitcher::the().refresh_if_needed(); } OwnPtr WSClientConnection::handle(const WindowServer::SetWindowBackingStore& message) { int window_id = message.window_id(); auto it = m_windows.find(window_id); if (it == m_windows.end()) { did_misbehave("SetWindowBackingStore: Bad window ID"); return nullptr; } auto& window = *(*it).value; if (window.last_backing_store() && window.last_backing_store()->shared_buffer_id() == message.shared_buffer_id()) { window.swap_backing_stores(); } else { auto shared_buffer = SharedBuffer::create_from_shared_buffer_id(message.shared_buffer_id()); if (!shared_buffer) return make(); auto backing_store = GraphicsBitmap::create_with_shared_buffer( message.has_alpha_channel() ? GraphicsBitmap::Format::RGBA32 : GraphicsBitmap::Format::RGB32, *shared_buffer, message.size()); window.set_backing_store(move(backing_store)); } if (message.flush_immediately()) window.invalidate(); return make(); } OwnPtr WSClientConnection::handle(const WindowServer::SetGlobalCursorTracking& message) { int window_id = message.window_id(); auto it = m_windows.find(window_id); if (it == m_windows.end()) { did_misbehave("SetGlobalCursorTracking: Bad window ID"); return nullptr; } it->value->set_global_cursor_tracking_enabled(message.enabled()); return make(); } OwnPtr WSClientConnection::handle(const WindowServer::SetWindowOverrideCursor& message) { auto it = m_windows.find(message.window_id()); if (it == m_windows.end()) { did_misbehave("SetWindowOverrideCursor: Bad window ID"); return nullptr; } auto& window = *(*it).value; window.set_override_cursor(WSCursor::create((WSStandardCursor)message.cursor_type())); return make(); } OwnPtr WSClientConnection::handle(const WindowServer::SetWindowHasAlphaChannel& message) { auto it = m_windows.find(message.window_id()); if (it == m_windows.end()) { did_misbehave("SetWindowHasAlphaChannel: Bad window ID"); return nullptr; } it->value->set_has_alpha_channel(message.has_alpha_channel()); return make(); } void WSClientConnection::handle(const WindowServer::WM_SetActiveWindow& message) { auto* client = WSClientConnection::from_client_id(message.client_id()); if (!client) { did_misbehave("WM_SetActiveWindow: Bad client ID"); return; } auto it = client->m_windows.find(message.window_id()); if (it == client->m_windows.end()) { did_misbehave("WM_SetActiveWindow: Bad window ID"); return; } auto& window = *(*it).value; window.set_minimized(false); WSWindowManager::the().move_to_front_and_make_active(window); } void WSClientConnection::handle(const WindowServer::WM_PopupWindowMenu& message) { auto* client = WSClientConnection::from_client_id(message.client_id()); if (!client) { did_misbehave("WM_PopupWindowMenu: Bad client ID"); return; } auto it = client->m_windows.find(message.window_id()); if (it == client->m_windows.end()) { did_misbehave("WM_PopupWindowMenu: Bad window ID"); return; } auto& window = *(*it).value; window.popup_window_menu(message.screen_position()); } void WSClientConnection::handle(const WindowServer::WM_StartWindowResize& request) { auto* client = WSClientConnection::from_client_id(request.client_id()); if (!client) { did_misbehave("WM_StartWindowResize: Bad client ID"); return; } auto it = client->m_windows.find(request.window_id()); if (it == client->m_windows.end()) { did_misbehave("WM_StartWindowResize: Bad window ID"); return; } 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? WSWindowManager::the().start_window_resize(window, WSScreen::the().cursor_location(), MouseButton::Left); } void WSClientConnection::handle(const WindowServer::WM_SetWindowMinimized& message) { auto* client = WSClientConnection::from_client_id(message.client_id()); if (!client) { did_misbehave("WM_SetWindowMinimized: Bad client ID"); return; } auto it = client->m_windows.find(message.window_id()); if (it == client->m_windows.end()) { did_misbehave("WM_SetWindowMinimized: Bad window ID"); return; } auto& window = *(*it).value; window.set_minimized(message.minimized()); } OwnPtr WSClientConnection::handle(const WindowServer::Greet&) { return make(client_id(), WSScreen::the().rect()); } bool WSClientConnection::is_showing_modal_window() const { for (auto& it : m_windows) { auto& window = *it.value; if (window.is_visible() && window.is_modal()) return true; } return false; } void WSClientConnection::handle(const WindowServer::WM_SetWindowTaskbarRect& message) { auto* client = WSClientConnection::from_client_id(message.client_id()); if (!client) { did_misbehave("WM_SetWindowTaskbarRect: Bad client ID"); return; } auto it = client->m_windows.find(message.window_id()); if (it == client->m_windows.end()) { did_misbehave("WM_SetWindowTaskbarRect: Bad window ID"); return; } auto& window = *(*it).value; window.set_taskbar_rect(message.rect()); } OwnPtr WSClientConnection::handle(const WindowServer::CreateMenuApplet& message) { auto applet = make(message.size()); auto applet_id = applet->applet_id(); WSWindowManager::the().menu_manager().add_applet(*applet); m_menu_applets.set(applet_id, move(applet)); return make(applet_id); } OwnPtr WSClientConnection::handle(const WindowServer::DestroyMenuApplet& message) { auto it = m_menu_applets.find(message.applet_id()); if (it == m_menu_applets.end()) { did_misbehave("DestroyApplet: Invalid applet ID"); return nullptr; } WSWindowManager::the().menu_manager().remove_applet(*it->value); m_menu_applets.remove(message.applet_id()); return make(); } OwnPtr WSClientConnection::handle(const WindowServer::SetMenuAppletBackingStore& message) { auto it = m_menu_applets.find(message.applet_id()); if (it == m_menu_applets.end()) { did_misbehave("SetAppletBackingStore: Invalid applet ID"); return nullptr; } auto shared_buffer = SharedBuffer::create_from_shared_buffer_id(message.shared_buffer_id()); ssize_t size_in_bytes = it->value->size().area() * sizeof(RGBA32); if (size_in_bytes > shared_buffer->size()) { did_misbehave("SetAppletBackingStore: Shared buffer is too small for applet size"); return nullptr; } auto bitmap = GraphicsBitmap::create_with_shared_buffer(GraphicsBitmap::Format::RGBA32, *shared_buffer, it->value->size()); it->value->set_bitmap(bitmap); return make(); } OwnPtr WSClientConnection::handle(const WindowServer::InvalidateMenuAppletRect& message) { auto it = m_menu_applets.find(message.applet_id()); if (it == m_menu_applets.end()) { did_misbehave("InvalidateAppletRect: Invalid applet ID"); return nullptr; } it->value->invalidate(message.rect()); return make(); }