mirror of
https://github.com/fergalmoran/ladybird.git
synced 2025-12-31 21:59:21 +00:00
Previously we were rendering the whole menubar on every compose(), even if nothing changed about it. Now it's in its own window and can be invalidated and painted separately.
388 lines
15 KiB
C++
388 lines
15 KiB
C++
#include <Kernel/KeyCode.h>
|
|
#include <Kernel/MousePacket.h>
|
|
#include <LibCore/CObject.h>
|
|
#include <WindowServer/WSAPITypes.h>
|
|
#include <WindowServer/WSClientConnection.h>
|
|
#include <WindowServer/WSCursor.h>
|
|
#include <WindowServer/WSEvent.h>
|
|
#include <WindowServer/WSEventLoop.h>
|
|
#include <WindowServer/WSScreen.h>
|
|
#include <WindowServer/WSWindowManager.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <sys/select.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/time.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
//#define WSMESSAGELOOP_DEBUG
|
|
|
|
WSEventLoop::WSEventLoop()
|
|
{
|
|
m_keyboard_fd = open("/dev/keyboard", O_RDONLY | O_NONBLOCK | O_CLOEXEC);
|
|
m_mouse_fd = open("/dev/psaux", O_RDONLY | O_NONBLOCK | O_CLOEXEC);
|
|
|
|
unlink("/tmp/wsportal");
|
|
|
|
m_server_fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
|
ASSERT(m_server_fd >= 0);
|
|
sockaddr_un address;
|
|
address.sun_family = AF_LOCAL;
|
|
strcpy(address.sun_path, "/tmp/wsportal");
|
|
int rc = bind(m_server_fd, (const sockaddr*)&address, sizeof(address));
|
|
ASSERT(rc == 0);
|
|
rc = listen(m_server_fd, 5);
|
|
ASSERT(rc == 0);
|
|
|
|
ASSERT(m_keyboard_fd >= 0);
|
|
ASSERT(m_mouse_fd >= 0);
|
|
}
|
|
|
|
WSEventLoop::~WSEventLoop()
|
|
{
|
|
}
|
|
|
|
void WSEventLoop::drain_server()
|
|
{
|
|
sockaddr_un address;
|
|
socklen_t address_size = sizeof(address);
|
|
int client_fd = accept(m_server_fd, (sockaddr*)&address, &address_size);
|
|
if (client_fd < 0) {
|
|
dbgprintf("WindowServer: accept() failed: %s\n", strerror(errno));
|
|
} else {
|
|
new WSClientConnection(client_fd);
|
|
}
|
|
}
|
|
|
|
void WSEventLoop::drain_mouse()
|
|
{
|
|
auto& screen = WSScreen::the();
|
|
unsigned prev_buttons = screen.mouse_button_state();
|
|
int dx = 0;
|
|
int dy = 0;
|
|
int dz = 0;
|
|
unsigned buttons = prev_buttons;
|
|
for (;;) {
|
|
MousePacket packet;
|
|
ssize_t nread = read(m_mouse_fd, &packet, sizeof(MousePacket));
|
|
if (nread == 0)
|
|
break;
|
|
ASSERT(nread == sizeof(packet));
|
|
buttons = packet.buttons;
|
|
|
|
dx += packet.dx;
|
|
dy += -packet.dy;
|
|
dz += packet.dz;
|
|
if (buttons != prev_buttons) {
|
|
screen.on_receive_mouse_data(dx, dy, dz, buttons);
|
|
dx = 0;
|
|
dy = 0;
|
|
dz = 0;
|
|
prev_buttons = buttons;
|
|
}
|
|
}
|
|
if (dx || dy || dz)
|
|
screen.on_receive_mouse_data(dx, dy, dz, buttons);
|
|
}
|
|
|
|
void WSEventLoop::drain_keyboard()
|
|
{
|
|
auto& screen = WSScreen::the();
|
|
for (;;) {
|
|
KeyEvent event;
|
|
ssize_t nread = read(m_keyboard_fd, (byte*)&event, sizeof(KeyEvent));
|
|
if (nread == 0)
|
|
break;
|
|
ASSERT(nread == sizeof(KeyEvent));
|
|
screen.on_receive_keyboard_data(event);
|
|
}
|
|
}
|
|
|
|
static Vector<Rect, 32> get_rects(const WSAPI_ClientMessage& message, const ByteBuffer& extra_data)
|
|
{
|
|
Vector<Rect, 32> rects;
|
|
if (message.rect_count > (WSAPI_ClientMessage::max_inline_rect_count + extra_data.size() / sizeof(WSAPI_Rect))) {
|
|
return {};
|
|
}
|
|
for (int i = 0; i < min(WSAPI_ClientMessage::max_inline_rect_count, message.rect_count); ++i)
|
|
rects.append(message.rects[i]);
|
|
if (!extra_data.is_empty()) {
|
|
auto* extra_rects = reinterpret_cast<const WSAPI_Rect*>(extra_data.data());
|
|
for (int i = 0; i < (extra_data.size() / sizeof(WSAPI_Rect)); ++i)
|
|
rects.append(extra_rects[i]);
|
|
}
|
|
return rects;
|
|
}
|
|
|
|
bool WSEventLoop::on_receive_from_client(int client_id, const WSAPI_ClientMessage& message, ByteBuffer&& extra_data)
|
|
{
|
|
WSClientConnection& client = *WSClientConnection::from_client_id(client_id);
|
|
switch (message.type) {
|
|
case WSAPI_ClientMessage::Type::Greeting:
|
|
client.set_client_pid(message.greeting.client_pid);
|
|
break;
|
|
case WSAPI_ClientMessage::Type::CreateMenubar:
|
|
post_event(client, make<WSAPICreateMenubarRequest>(client_id));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::DestroyMenubar:
|
|
post_event(client, make<WSAPIDestroyMenubarRequest>(client_id, message.menu.menubar_id));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::SetApplicationMenubar:
|
|
post_event(client, make<WSAPISetApplicationMenubarRequest>(client_id, message.menu.menubar_id));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::AddMenuToMenubar:
|
|
post_event(client, make<WSAPIAddMenuToMenubarRequest>(client_id, message.menu.menubar_id, message.menu.menu_id));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::CreateMenu:
|
|
if (message.text_length > (int)sizeof(message.text)) {
|
|
client.did_misbehave();
|
|
return false;
|
|
}
|
|
post_event(client, make<WSAPICreateMenuRequest>(client_id, String(message.text, message.text_length)));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::PopupMenu:
|
|
post_event(client, make<WSAPIPopupMenuRequest>(client_id, message.menu.menu_id, message.menu.position));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::DismissMenu:
|
|
post_event(client, make<WSAPIDismissMenuRequest>(client_id, message.menu.menu_id));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::SetWindowIcon:
|
|
if (message.text_length > (int)sizeof(message.text)) {
|
|
client.did_misbehave();
|
|
return false;
|
|
}
|
|
post_event(client, make<WSAPISetWindowIconRequest>(client_id, message.window_id, String(message.text, message.text_length)));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::DestroyMenu:
|
|
post_event(client, make<WSAPIDestroyMenuRequest>(client_id, message.menu.menu_id));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::AddMenuItem:
|
|
if (message.text_length > (int)sizeof(message.text)) {
|
|
client.did_misbehave();
|
|
return false;
|
|
}
|
|
if (message.menu.shortcut_text_length > (int)sizeof(message.menu.shortcut_text)) {
|
|
client.did_misbehave();
|
|
return false;
|
|
}
|
|
post_event(client, make<WSAPIAddMenuItemRequest>(client_id, message.menu.menu_id, message.menu.identifier, String(message.text, message.text_length), String(message.menu.shortcut_text, message.menu.shortcut_text_length), message.menu.enabled, message.menu.checkable, message.menu.checked));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::UpdateMenuItem:
|
|
if (message.text_length > (int)sizeof(message.text)) {
|
|
client.did_misbehave();
|
|
return false;
|
|
}
|
|
if (message.menu.shortcut_text_length > (int)sizeof(message.menu.shortcut_text)) {
|
|
client.did_misbehave();
|
|
return false;
|
|
}
|
|
post_event(client, make<WSAPIUpdateMenuItemRequest>(client_id, message.menu.menu_id, message.menu.identifier, String(message.text, message.text_length), String(message.menu.shortcut_text, message.menu.shortcut_text_length), message.menu.enabled, message.menu.checkable, message.menu.checked));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::AddMenuSeparator:
|
|
post_event(client, make<WSAPIAddMenuSeparatorRequest>(client_id, message.menu.menu_id));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::CreateWindow: {
|
|
if (message.text_length > (int)sizeof(message.text)) {
|
|
client.did_misbehave();
|
|
return false;
|
|
}
|
|
|
|
auto ws_window_type = WSWindowType::Invalid;
|
|
switch (message.window.type) {
|
|
case WSAPI_WindowType::Normal:
|
|
ws_window_type = WSWindowType::Normal;
|
|
break;
|
|
case WSAPI_WindowType::Menu:
|
|
ws_window_type = WSWindowType::Menu;
|
|
break;
|
|
case WSAPI_WindowType::WindowSwitcher:
|
|
ws_window_type = WSWindowType::WindowSwitcher;
|
|
break;
|
|
case WSAPI_WindowType::Taskbar:
|
|
ws_window_type = WSWindowType::Taskbar;
|
|
break;
|
|
case WSAPI_WindowType::Tooltip:
|
|
ws_window_type = WSWindowType::Tooltip;
|
|
break;
|
|
case WSAPI_WindowType::Menubar:
|
|
ws_window_type = WSWindowType::Menubar;
|
|
break;
|
|
case WSAPI_WindowType::Invalid:
|
|
break; // handled below
|
|
}
|
|
|
|
if (ws_window_type == WSWindowType::Invalid) {
|
|
dbgprintf("Unknown WSAPI_WindowType: %d\n", message.window.type);
|
|
client.did_misbehave();
|
|
return false;
|
|
}
|
|
|
|
post_event(client,
|
|
make<WSAPICreateWindowRequest>(client_id,
|
|
message.window.rect,
|
|
String(message.text, message.text_length),
|
|
message.window.has_alpha_channel,
|
|
message.window.modal,
|
|
message.window.resizable,
|
|
message.window.fullscreen,
|
|
message.window.show_titlebar,
|
|
message.window.opacity,
|
|
message.window.base_size,
|
|
message.window.size_increment,
|
|
ws_window_type,
|
|
Color::from_rgba(message.window.background_color)));
|
|
break;
|
|
}
|
|
case WSAPI_ClientMessage::Type::DestroyWindow:
|
|
post_event(client, make<WSAPIDestroyWindowRequest>(client_id, message.window_id));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::SetWindowTitle:
|
|
if (message.text_length > (int)sizeof(message.text)) {
|
|
client.did_misbehave();
|
|
return false;
|
|
}
|
|
post_event(client, make<WSAPISetWindowTitleRequest>(client_id, message.window_id, String(message.text, message.text_length)));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::GetWindowTitle:
|
|
post_event(client, make<WSAPIGetWindowTitleRequest>(client_id, message.window_id));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::SetWindowRect:
|
|
post_event(client, make<WSAPISetWindowRectRequest>(client_id, message.window_id, message.window.rect));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::GetWindowRect:
|
|
post_event(client, make<WSAPIGetWindowRectRequest>(client_id, message.window_id));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::SetClipboardContents:
|
|
post_event(client, make<WSAPISetClipboardContentsRequest>(client_id, message.clipboard.shared_buffer_id, message.clipboard.contents_size));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::GetClipboardContents:
|
|
post_event(client, make<WSAPIGetClipboardContentsRequest>(client_id));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::InvalidateRect: {
|
|
auto rects = get_rects(message, extra_data);
|
|
if (rects.is_empty()) {
|
|
client.did_misbehave();
|
|
return false;
|
|
}
|
|
post_event(client, make<WSAPIInvalidateRectRequest>(client_id, message.window_id, rects));
|
|
break;
|
|
}
|
|
case WSAPI_ClientMessage::Type::DidFinishPainting: {
|
|
auto rects = get_rects(message, extra_data);
|
|
if (rects.is_empty()) {
|
|
client.did_misbehave();
|
|
return false;
|
|
}
|
|
post_event(client, make<WSAPIDidFinishPaintingNotification>(client_id, message.window_id, rects));
|
|
break;
|
|
}
|
|
case WSAPI_ClientMessage::Type::GetWindowBackingStore:
|
|
post_event(client, make<WSAPIGetWindowBackingStoreRequest>(client_id, message.window_id));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::SetWindowBackingStore:
|
|
post_event(client, make<WSAPISetWindowBackingStoreRequest>(client_id, message.window_id, message.backing.shared_buffer_id, message.backing.size, message.backing.bpp, message.backing.pitch, message.backing.has_alpha_channel, message.backing.flush_immediately));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::SetGlobalCursorTracking:
|
|
post_event(client, make<WSAPISetGlobalCursorTrackingRequest>(client_id, message.window_id, message.value));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::SetWallpaper:
|
|
if (message.text_length > (int)sizeof(message.text)) {
|
|
client.did_misbehave();
|
|
return false;
|
|
}
|
|
post_event(client, make<WSAPISetWallpaperRequest>(client_id, String(message.text, message.text_length)));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::GetWallpaper:
|
|
post_event(client, make<WSAPIGetWallpaperRequest>(client_id));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::SetWindowOverrideCursor:
|
|
post_event(client, make<WSAPISetWindowOverrideCursorRequest>(client_id, message.window_id, (WSStandardCursor)message.cursor.cursor));
|
|
break;
|
|
case WSAPI_ClientMessage::SetWindowHasAlphaChannel:
|
|
post_event(client, make<WSAPISetWindowHasAlphaChannelRequest>(client_id, message.window_id, message.value));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::WM_SetActiveWindow:
|
|
post_event(client, make<WSWMAPISetActiveWindowRequest>(client_id, message.wm.client_id, message.wm.window_id));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::WM_SetWindowMinimized:
|
|
post_event(client, make<WSWMAPISetWindowMinimizedRequest>(client_id, message.wm.client_id, message.wm.window_id, message.wm.minimized));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::WM_StartWindowResize:
|
|
post_event(client, make<WSWMAPIStartWindowResizeRequest>(client_id, message.wm.client_id, message.wm.window_id));
|
|
break;
|
|
case WSAPI_ClientMessage::Type::MoveWindowToFront:
|
|
post_event(client, make<WSAPIMoveWindowToFrontRequest>(client_id, message.window_id));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void WSEventLoop::add_file_descriptors_for_select(fd_set& fds, int& max_fd_added)
|
|
{
|
|
auto add_fd_to_set = [&max_fd_added](int fd, auto& set) {
|
|
FD_SET(fd, &set);
|
|
if (fd > max_fd_added)
|
|
max_fd_added = fd;
|
|
};
|
|
add_fd_to_set(m_keyboard_fd, fds);
|
|
add_fd_to_set(m_mouse_fd, fds);
|
|
add_fd_to_set(m_server_fd, fds);
|
|
WSClientConnection::for_each_client([&](WSClientConnection& client) {
|
|
add_fd_to_set(client.fd(), fds);
|
|
});
|
|
}
|
|
|
|
void WSEventLoop::process_file_descriptors_after_select(const fd_set& fds)
|
|
{
|
|
if (FD_ISSET(m_server_fd, &fds))
|
|
drain_server();
|
|
if (FD_ISSET(m_keyboard_fd, &fds))
|
|
drain_keyboard();
|
|
if (FD_ISSET(m_mouse_fd, &fds))
|
|
drain_mouse();
|
|
WSClientConnection::for_each_client([&](WSClientConnection& client) {
|
|
if (FD_ISSET(client.fd(), &fds))
|
|
drain_client(client);
|
|
});
|
|
}
|
|
|
|
void WSEventLoop::drain_client(WSClientConnection& client)
|
|
{
|
|
unsigned messages_received = 0;
|
|
for (;;) {
|
|
WSAPI_ClientMessage message;
|
|
// FIXME: Don't go one message at a time, that's so much context switching, oof.
|
|
ssize_t nread = recv(client.fd(), &message, sizeof(WSAPI_ClientMessage), MSG_DONTWAIT);
|
|
if (nread == 0 || (nread == -1 && errno == EAGAIN)) {
|
|
if (!messages_received)
|
|
post_event(client, make<WSClientDisconnectedNotification>(client.client_id()));
|
|
break;
|
|
}
|
|
if (nread < 0) {
|
|
perror("recv");
|
|
ASSERT_NOT_REACHED();
|
|
}
|
|
ByteBuffer extra_data;
|
|
if (message.extra_size) {
|
|
if (message.extra_size >= 32768) {
|
|
dbgprintf("message.extra_size is way too large\n");
|
|
return client.did_misbehave();
|
|
}
|
|
extra_data = ByteBuffer::create_uninitialized(message.extra_size);
|
|
// FIXME: We should allow this to time out. Maybe use a socket timeout?
|
|
int extra_nread = read(client.fd(), extra_data.data(), extra_data.size());
|
|
if (extra_nread != message.extra_size) {
|
|
dbgprintf("extra_nread(%d) != extra_size(%d)\n", extra_nread, extra_data.size());
|
|
return client.did_misbehave();
|
|
}
|
|
}
|
|
if (!on_receive_from_client(client.client_id(), message, move(extra_data)))
|
|
return;
|
|
++messages_received;
|
|
}
|
|
}
|