forked from godotengine/godot
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Wayland: Split WaylandThread implementation into multiple files
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
Showing
17 changed files
with
2,947 additions
and
2,484 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
Oops, something went wrong.