#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) : ConnectionNG(*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::post_error(const String& error_message) { dbgprintf("WSClientConnection::post_error: client_id=%d: %s\n", client_id(), error_message.characters()); did_misbehave(); } 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()) { post_error("WSAPIDestroyMenubarRequest: Bad menubar ID"); return make(); } 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)); dbg() << "Created menu ID " << menu_id << " (" << message.menu_title() << ")"; 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()) { post_error("WSAPIDestroyMenuRequest: Bad menu ID"); return make(); } 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()) { post_error("WSAPISetApplicationMenubarRequest: Bad menubar ID"); return make(); } 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()) { post_error("WSAPIAddMenuToMenubarRequest: Bad menubar ID"); return make(); } if (jt == m_menus.end()) { post_error("WSAPIAddMenuToMenubarRequest: Bad menu ID"); return make(); } 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() << "WSAPIAddMenuItemRequest: Bad menu ID: " << menu_id; return make(); } 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) { did_misbehave(); return make(); } // 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()) { post_error("WSAPIPopupMenuRequest: Bad menu ID"); return make(); } 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()) { post_error("WSAPIDismissMenuRequest: Bad menu ID"); return make(); } 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()) { post_error("WSAPIUpdateMenuItemRequest: Bad menu ID"); return make(); } auto& menu = *(*it).value; auto* menu_item = menu.item_with_identifier(message.identifier()); if (!menu_item) { post_error("WSAPIUpdateMenuItemRequest: Bad menu item identifier"); return make(); } 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()) { post_error("WSAPIAddMenuSeparatorRequest: Bad menu ID"); return make(); } 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()) { post_error("WSAPIMoveWindowToFrontRequest: Bad window ID"); return make(); } 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()) { post_error("WSAPISetFullscreenRequest: Bad window ID"); return make(); } 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()) { post_error("WSAPISetWindowOpacityRequest: Bad window ID"); return make(); } 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()) { post_error("WSAPISetWindowTitleRequest: Bad window ID"); return make(); } 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()) { post_error("WSAPIGetWindowTitleRequest: Bad window ID"); return make(""); } 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()) { post_error("WSAPISetWindowIconBitmapRequest: Bad window ID"); return make(); } 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()) { post_error("WSAPISetWindowRectRequest: Bad window ID"); return make(); } auto& window = *(*it).value; if (window.is_fullscreen()) { dbgprintf("WSClientConnection: Ignoring SetWindowRect request for fullscreen window\n"); return make(); } 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()) { post_error("WSAPIGetWindowRectRequest: Bad window ID"); return make(Rect()); } 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) { post_error("WSAPISetClipboardContentsRequest: Bad shared buffer ID"); return make(); } 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()) { post_error("WSAPIDestroyWindowRequest: Bad window ID"); return make(); } 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()) { post_error("WSAPIInvalidateRectRequest: 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()) { post_error("WSAPIDidFinishPaintingNotification: 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()) { post_error("WSAPISetWindowBackingStoreRequest: Bad window ID"); return make(); } 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()) { post_error("WSAPISetGlobalCursorTrackingRequest: Bad window ID"); return make(); } 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()) { post_error("WSAPISetWindowOverrideCursorRequest: Bad window ID"); return make(); } 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()) { post_error("WSAPISetWindowHasAlphaChannelRequest: Bad window ID"); return make(); } 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) { post_error("WSWMAPISetActiveWindowRequest: Bad client ID"); return; } auto it = client->m_windows.find(message.window_id()); if (it == client->m_windows.end()) { post_error("WSWMAPISetActiveWindowRequest: 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) { post_error("WSWMAPIPopupWindowMenuRequest: Bad client ID"); return; } auto it = client->m_windows.find(message.window_id()); if (it == client->m_windows.end()) { post_error("WSWMAPIPopupWindowMenuRequest: 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) { post_error("WSWMAPIStartWindowResizeRequest: Bad client ID"); return; } auto it = client->m_windows.find(request.window_id()); if (it == client->m_windows.end()) { post_error("WSWMAPIStartWindowResizeRequest: 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) { post_error("WSWMAPISetWindowMinimizedRequest: Bad client ID"); return; } auto it = client->m_windows.find(message.window_id()); if (it == client->m_windows.end()) { post_error("WSWMAPISetWindowMinimizedRequest: Bad window ID"); return; } auto& window = *(*it).value; window.set_minimized(message.minimized()); } OwnPtr WSClientConnection::handle(const WindowServer::Greet& message) { set_client_pid(message.client_pid()); return make(getpid(), 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; }