Skip to content

Commit

Permalink
Wayland: Split WaylandThread implementation into multiple files
Browse files Browse the repository at this point in the history
Wayland is an asynchronous object-oriented API. This means that most
code in WaylandThread is made of event handlers for each kind of
object. Over time it all became quite unwieldy.

This patch moves the implementation for all the handlers into separate
files, one for each protocol. The core protocol got further split into
files as it's quite chunky.

Note that this relates only to the _implementation_ of the
`WaylandThread` class (into which the handlers are currently located)
and does no further attempt at splitting everything up (e.g. our custom
object state structs), since that would require a lot more work.

Personally I think that this is definitely clearer, given how simple
this change is, and that potentially we might go even further over time.

Perhaps we could use a "wayland" namespace for all the state objects and
get rid of them from the `WaylandThread` class? Just thinking out loud
:P
  • Loading branch information
Riteo committed Sep 12, 2024
1 parent 83d54ab commit 3c7a258
Show file tree
Hide file tree
Showing 17 changed files with 2,947 additions and 2,484 deletions.
19 changes: 19 additions & 0 deletions platform/linuxbsd/wayland/SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,27 @@ source_files = [
"protocol/idle_inhibit.gen.c",
"protocol/tablet.gen.c",
"protocol/text_input.gen.c",

"display_server_wayland.cpp",

"wayland_thread.cpp",

"thread/core/data.cpp",
"thread/core/keyboard.cpp",
"thread/core/pointer.cpp",
"thread/core/registry.cpp",
"thread/core/output.cpp",
"thread/core/seat.cpp",
"thread/core/surface.cpp",

"thread/libdecor.cpp",
"thread/primary-selection.cpp",
"thread/tablet.cpp",
"thread/text-input.cpp",
"thread/util.cpp",
"thread/xdg-activation.cpp",
"thread/xdg-shell.cpp",

"key_mapping_xkb.cpp",
"detect_prime_egl.cpp",
]
Expand Down
188 changes: 188 additions & 0 deletions platform/linuxbsd/wayland/thread/core/data.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/**************************************************************************/
/* data.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/

#include "wayland/wayland_thread.h"

#include <unistd.h>

// NOTE: Don't forget to `memfree` the offer's state.
void WaylandThread::_wl_data_device_on_data_offer(void *data, struct wl_data_device *wl_data_device, struct wl_data_offer *id) {
wl_proxy_tag_godot((struct wl_proxy *)id);
wl_data_offer_add_listener(id, &wl_data_offer_listener, memnew(OfferState));
}

void WaylandThread::_wl_data_device_on_enter(void *data, struct wl_data_device *wl_data_device, uint32_t serial, struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y, struct wl_data_offer *id) {
SeatState *ss = (SeatState *)data;
ERR_FAIL_NULL(ss);

ss->dnd_enter_serial = serial;
ss->wl_data_offer_dnd = id;

// Godot only supports DnD file copying for now.
wl_data_offer_accept(id, serial, "text/uri-list");
wl_data_offer_set_actions(id, WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY, WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY);
}

void WaylandThread::_wl_data_device_on_leave(void *data, struct wl_data_device *wl_data_device) {
SeatState *ss = (SeatState *)data;
ERR_FAIL_NULL(ss);

if (ss->wl_data_offer_dnd) {
memdelete(wl_data_offer_get_offer_state(ss->wl_data_offer_dnd));
wl_data_offer_destroy(ss->wl_data_offer_dnd);
ss->wl_data_offer_dnd = nullptr;
}
}

void WaylandThread::_wl_data_device_on_motion(void *data, struct wl_data_device *wl_data_device, uint32_t time, wl_fixed_t x, wl_fixed_t y) {
}

void WaylandThread::_wl_data_device_on_drop(void *data, struct wl_data_device *wl_data_device) {
SeatState *ss = (SeatState *)data;
ERR_FAIL_NULL(ss);

WaylandThread *wayland_thread = ss->wayland_thread;
ERR_FAIL_NULL(wayland_thread);

OfferState *os = wl_data_offer_get_offer_state(ss->wl_data_offer_dnd);
ERR_FAIL_NULL(os);

if (os) {
Ref<DropFilesEventMessage> msg;
msg.instantiate();

Vector<uint8_t> list_data = _wl_data_offer_read(wayland_thread->wl_display, "text/uri-list", ss->wl_data_offer_dnd);

msg->files = String::utf8((const char *)list_data.ptr(), list_data.size()).split("\r\n", false);
for (int i = 0; i < msg->files.size(); i++) {
msg->files.write[i] = msg->files[i].replace("file://", "").uri_decode();
}

wayland_thread->push_message(msg);

wl_data_offer_finish(ss->wl_data_offer_dnd);
}

memdelete(wl_data_offer_get_offer_state(ss->wl_data_offer_dnd));
wl_data_offer_destroy(ss->wl_data_offer_dnd);
ss->wl_data_offer_dnd = nullptr;
}

void WaylandThread::_wl_data_device_on_selection(void *data, struct wl_data_device *wl_data_device, struct wl_data_offer *id) {
SeatState *ss = (SeatState *)data;
ERR_FAIL_NULL(ss);

if (ss->wl_data_offer_selection) {
memdelete(wl_data_offer_get_offer_state(ss->wl_data_offer_selection));
wl_data_offer_destroy(ss->wl_data_offer_selection);
}

ss->wl_data_offer_selection = id;
}

void WaylandThread::_wl_data_offer_on_offer(void *data, struct wl_data_offer *wl_data_offer, const char *mime_type) {
OfferState *os = (OfferState *)data;
ERR_FAIL_NULL(os);

if (os) {
os->mime_types.insert(String::utf8(mime_type));
}
}

void WaylandThread::_wl_data_offer_on_source_actions(void *data, struct wl_data_offer *wl_data_offer, uint32_t source_actions) {
}

void WaylandThread::_wl_data_offer_on_action(void *data, struct wl_data_offer *wl_data_offer, uint32_t dnd_action) {
}

void WaylandThread::_wl_data_source_on_target(void *data, struct wl_data_source *wl_data_source, const char *mime_type) {
}

void WaylandThread::_wl_data_source_on_send(void *data, struct wl_data_source *wl_data_source, const char *mime_type, int32_t fd) {
SeatState *ss = (SeatState *)data;
ERR_FAIL_NULL(ss);

Vector<uint8_t> *data_to_send = nullptr;

if (wl_data_source == ss->wl_data_source_selection) {
data_to_send = &ss->selection_data;
DEBUG_LOG_WAYLAND_THREAD("Clipboard: requested selection.");
}

if (data_to_send) {
ssize_t written_bytes = 0;

bool valid_mime = false;

if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) {
valid_mime = true;
} else if (strcmp(mime_type, "text/plain") == 0) {
valid_mime = true;
}

if (valid_mime) {
written_bytes = write(fd, data_to_send->ptr(), data_to_send->size());
}

if (written_bytes > 0) {
DEBUG_LOG_WAYLAND_THREAD(vformat("Clipboard: sent %d bytes.", written_bytes));
} else if (written_bytes == 0) {
DEBUG_LOG_WAYLAND_THREAD("Clipboard: no bytes sent.");
} else {
ERR_PRINT(vformat("Clipboard: write error %d.", errno));
}
}

close(fd);
}

void WaylandThread::_wl_data_source_on_cancelled(void *data, struct wl_data_source *wl_data_source) {
SeatState *ss = (SeatState *)data;
ERR_FAIL_NULL(ss);

wl_data_source_destroy(wl_data_source);

if (wl_data_source == ss->wl_data_source_selection) {
ss->wl_data_source_selection = nullptr;
ss->selection_data.clear();

DEBUG_LOG_WAYLAND_THREAD("Clipboard: selection set by another program.");
return;
}
}

void WaylandThread::_wl_data_source_on_dnd_drop_performed(void *data, struct wl_data_source *wl_data_source) {
}

void WaylandThread::_wl_data_source_on_dnd_finished(void *data, struct wl_data_source *wl_data_source) {
}

void WaylandThread::_wl_data_source_on_action(void *data, struct wl_data_source *wl_data_source, uint32_t dnd_action) {
}
145 changes: 145 additions & 0 deletions platform/linuxbsd/wayland/thread/core/keyboard.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/**************************************************************************/
/* keyboard.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/

#include "wayland/wayland_thread.h"

#include <sys/mman.h>

void WaylandThread::_wl_keyboard_on_keymap(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size) {
ERR_FAIL_COND_MSG(format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, "Unsupported keymap format announced from the Wayland compositor.");

SeatState *ss = (SeatState *)data;
ERR_FAIL_NULL(ss);

if (ss->keymap_buffer) {
// We have already a mapped buffer, so we unmap it. There's no need to reset
// its pointer or size, as we're gonna set them below.
munmap((void *)ss->keymap_buffer, ss->keymap_buffer_size);
ss->keymap_buffer = nullptr;
}

ss->keymap_buffer = (const char *)mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
ss->keymap_buffer_size = size;

xkb_keymap_unref(ss->xkb_keymap);
ss->xkb_keymap = xkb_keymap_new_from_string(ss->xkb_context, ss->keymap_buffer,
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);

xkb_state_unref(ss->xkb_state);
ss->xkb_state = xkb_state_new(ss->xkb_keymap);
}

void WaylandThread::_wl_keyboard_on_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
SeatState *ss = (SeatState *)data;
ERR_FAIL_NULL(ss);

WaylandThread *wayland_thread = ss->wayland_thread;
ERR_FAIL_NULL(wayland_thread);

wayland_thread->_set_current_seat(ss->wl_seat);

Ref<WindowEventMessage> msg;
msg.instantiate();
msg->event = DisplayServer::WINDOW_EVENT_FOCUS_IN;
wayland_thread->push_message(msg);
}

void WaylandThread::_wl_keyboard_on_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface) {
SeatState *ss = (SeatState *)data;
ERR_FAIL_NULL(ss);

WaylandThread *wayland_thread = ss->wayland_thread;
ERR_FAIL_NULL(wayland_thread);

ss->repeating_keycode = XKB_KEYCODE_INVALID;

Ref<WindowEventMessage> msg;
msg.instantiate();
msg->event = DisplayServer::WINDOW_EVENT_FOCUS_OUT;
wayland_thread->push_message(msg);
}

void WaylandThread::_wl_keyboard_on_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
SeatState *ss = (SeatState *)data;
ERR_FAIL_NULL(ss);

WaylandThread *wayland_thread = ss->wayland_thread;
ERR_FAIL_NULL(wayland_thread);

// We have to add 8 to the scancode to get an XKB-compatible keycode.
xkb_keycode_t xkb_keycode = key + 8;

bool pressed = state & WL_KEYBOARD_KEY_STATE_PRESSED;

if (pressed) {
if (xkb_keymap_key_repeats(ss->xkb_keymap, xkb_keycode)) {
ss->last_repeat_start_msec = OS::get_singleton()->get_ticks_msec();
ss->repeating_keycode = xkb_keycode;
}

ss->last_key_pressed_serial = serial;
} else if (ss->repeating_keycode == xkb_keycode) {
ss->repeating_keycode = XKB_KEYCODE_INVALID;
}

Ref<InputEventKey> k;
k.instantiate();

if (!_seat_state_configure_key_event(*ss, k, xkb_keycode, pressed)) {
return;
}

Ref<InputEventMessage> msg;
msg.instantiate();
msg->event = k;
wayland_thread->push_message(msg);
}

void WaylandThread::_wl_keyboard_on_modifiers(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) {
SeatState *ss = (SeatState *)data;
ERR_FAIL_NULL(ss);

xkb_state_update_mask(ss->xkb_state, mods_depressed, mods_latched, mods_locked, ss->current_layout_index, ss->current_layout_index, group);

ss->shift_pressed = xkb_state_mod_name_is_active(ss->xkb_state, XKB_MOD_NAME_SHIFT, XKB_STATE_MODS_DEPRESSED);
ss->ctrl_pressed = xkb_state_mod_name_is_active(ss->xkb_state, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_DEPRESSED);
ss->alt_pressed = xkb_state_mod_name_is_active(ss->xkb_state, XKB_MOD_NAME_ALT, XKB_STATE_MODS_DEPRESSED);
ss->meta_pressed = xkb_state_mod_name_is_active(ss->xkb_state, XKB_MOD_NAME_LOGO, XKB_STATE_MODS_DEPRESSED);

ss->current_layout_index = group;
}

void WaylandThread::_wl_keyboard_on_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay) {
SeatState *ss = (SeatState *)data;
ERR_FAIL_NULL(ss);

ss->repeat_key_delay_msec = 1000 / rate;
ss->repeat_start_delay_msec = delay;
}
Loading

0 comments on commit 3c7a258

Please sign in to comment.