diff --git a/meson.build b/meson.build index 2b00fec..ec9cedf 100644 --- a/meson.build +++ b/meson.build @@ -239,6 +239,7 @@ drm = dependency('libdrm', version: '>= 2.4.0') gbm = dependency('gbm', version: '>= 24.1.0') libudev = dependency('libudev', version: '>= 256') pixman = dependency('pixman-1', version: '>= 0.43.0') +xkbcommon = dependency('xkbcommon', version: '>= 1.7.0') shoyu_shells = [] diff --git a/protocols/shoyu-shell.xml b/protocols/shoyu-shell.xml index 55b2584..bb6a21d 100644 --- a/protocols/shoyu-shell.xml +++ b/protocols/shoyu-shell.xml @@ -20,6 +20,9 @@ + + + @@ -64,6 +67,8 @@ + + diff --git a/shoyu-compositor/compositor-private.h b/shoyu-compositor/compositor-private.h index 653dc3d..254c370 100644 --- a/shoyu-compositor/compositor-private.h +++ b/shoyu-compositor/compositor-private.h @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include struct _ShoyuCompositor { @@ -30,6 +32,8 @@ struct _ShoyuCompositor { struct wlr_output_layout *output_layout; struct wlr_compositor *wlr_compositor; + struct wlr_seat *wlr_seat; + struct wlr_xcursor_manager *wlr_xcursor_manager; GList *surfaces; struct wl_listener new_surface; @@ -63,6 +67,20 @@ struct _ShoyuCompositorClass { */ GType input_type; + /** + * ShoyuCompositor:input_type_keyboard: + * + * The type to create when a #ShoyuKeyboardInput is being created. + */ + GType input_type_keyboard; + + /** + * ShoyuCompositor:input_type_pointer: + * + * The type to create when a #ShoyuPointerInput is being created. + */ + GType input_type_pointer; + /** * ShoyuCompositor:surface_type: * @@ -229,6 +247,9 @@ struct _ShoyuCompositorClass { void (*started)(ShoyuCompositor *self); }; +ShoyuXdgToplevel * +shoyu_compositor_get_focused_xdg_toplevel(ShoyuCompositor *self); + ShoyuOutput *shoyu_compositor_get_output(ShoyuCompositor *self, struct wlr_output *wlr_output); ShoyuSurface *shoyu_compositor_get_surface(ShoyuCompositor *self, diff --git a/shoyu-compositor/compositor.c b/shoyu-compositor/compositor.c index 7301850..549920d 100644 --- a/shoyu-compositor/compositor.c +++ b/shoyu-compositor/compositor.c @@ -8,6 +8,9 @@ #include "wayland-event-source.h" #include "xdg-toplevel-private.h" +#include "keyboard-input.h" +#include "pointer-input.h" + #include #include #include @@ -184,7 +187,8 @@ static void shoyu_compositor_new_input(struct wl_listener *listener, g_debug("Inputs changed (old: %u, new: %u)", len, new_len); g_assert(new_len > len); - g_debug("Created ShoyuInput#%p", input); + g_debug("Created %s as ShoyuInput#%p", g_type_name(G_OBJECT_TYPE(input)), + input); g_signal_emit(self, shoyu_compositor_sigs[SIG_INPUT_ADDED], 0, input); } @@ -298,6 +302,9 @@ static void shoyu_compositor_finalize(GObject *object) { g_clear_object(&self->shell); + g_clear_pointer(&self->wlr_xcursor_manager, + (GDestroyNotify)wlr_xcursor_manager_destroy); + g_clear_pointer(&self->wlr_seat, (GDestroyNotify)wlr_seat_destroy); g_clear_pointer(&self->wlr_allocator, (GDestroyNotify)wlr_allocator_destroy); g_clear_pointer(&self->wlr_renderer, (GDestroyNotify)wlr_renderer_destroy); g_clear_pointer(&self->wlr_backend, (GDestroyNotify)wlr_backend_destroy); @@ -379,8 +386,26 @@ shoyu_compositor_real_create_input(ShoyuCompositor *self, struct wlr_input_device *input_device) { ShoyuCompositorClass *class = SHOYU_COMPOSITOR_GET_CLASS(self); - ShoyuInput *input = g_object_new(class->input_type, "compositor", self, NULL); + uint32_t caps = self->wlr_seat->capabilities; + GType input_type = class->input_type; + + switch (input_device->type) { + case WLR_INPUT_DEVICE_KEYBOARD: + input_type = class->input_type_keyboard; + caps |= WL_SEAT_CAPABILITY_KEYBOARD; + break; + case WLR_INPUT_DEVICE_POINTER: + input_type = class->input_type_pointer; + caps |= WL_SEAT_CAPABILITY_POINTER; + break; + default: + break; + } + + ShoyuInput *input = g_object_new(input_type, "compositor", self, NULL); g_return_val_if_fail(input != NULL, NULL); + + wlr_seat_set_capabilities(self->wlr_seat, caps); return input; } @@ -415,6 +440,8 @@ static void shoyu_compositor_class_init(ShoyuCompositorClass *class) { class->output_type = SHOYU_TYPE_OUTPUT; class->input_type = SHOYU_TYPE_INPUT; + class->input_type_keyboard = SHOYU_TYPE_KEYBOARD_INPUT; + class->input_type_pointer = SHOYU_TYPE_POINTER_INPUT; class->surface_type = SHOYU_TYPE_SURFACE; class->xdg_toplevel_type = SHOYU_TYPE_XDG_TOPLEVEL; @@ -553,6 +580,12 @@ static void shoyu_compositor_init(ShoyuCompositor *self) { self->output_layout = wlr_output_layout_create(self->wl_display); g_assert(self->output_layout != NULL); + self->wlr_seat = wlr_seat_create(self->wl_display, "seat0"); + g_assert(self->wlr_seat != NULL); + + self->wlr_xcursor_manager = wlr_xcursor_manager_create(NULL, 24); + g_assert(self->wlr_xcursor_manager != NULL); + self->shell = shoyu_shell_new(self); g_assert(self->shell != NULL); @@ -647,6 +680,19 @@ ShoyuShell *shoyu_compositor_get_shell(ShoyuCompositor *self) { return self->shell; } +ShoyuXdgToplevel * +shoyu_compositor_get_focused_xdg_toplevel(ShoyuCompositor *self) { + g_return_val_if_fail(SHOYU_IS_COMPOSITOR(self), NULL); + + for (GList *item = self->xdg_toplevels; item != NULL; item = item->next) { + ShoyuXdgToplevel *xdg_toplevel = SHOYU_XDG_TOPLEVEL(item->data); + if (xdg_toplevel->is_focused) + return xdg_toplevel; + } + + return NULL; +} + ShoyuOutput *shoyu_compositor_get_output(ShoyuCompositor *self, struct wlr_output *wlr_output) { g_return_val_if_fail(SHOYU_IS_COMPOSITOR(self), NULL); diff --git a/shoyu-compositor/keyboard-input-private.h b/shoyu-compositor/keyboard-input-private.h new file mode 100644 index 0000000..a9c83a2 --- /dev/null +++ b/shoyu-compositor/keyboard-input-private.h @@ -0,0 +1,18 @@ +#pragma once + +#include "input-private.h" +#include "keyboard-input.h" + +#include + +struct _ShoyuKeyboardInput { + ShoyuInput parent_instance; + struct xkb_keymap *xkb_keymap; + + struct wl_listener modifiers; + struct wl_listener key; +}; + +struct _ShoyuKeyboardInputClass { + ShoyuInputClass parent_class; +}; diff --git a/shoyu-compositor/keyboard-input.c b/shoyu-compositor/keyboard-input.c new file mode 100644 index 0000000..165148a --- /dev/null +++ b/shoyu-compositor/keyboard-input.c @@ -0,0 +1,94 @@ +#include "compositor-private.h" +#include "keyboard-input-private.h" +#include "output-private.h" +#include "xdg-toplevel-private.h" + +/** + * ShoyuKeyboardInput: + * + * An input which represents a keyboard for #ShoyuCompositor. + */ + +G_DEFINE_TYPE(ShoyuKeyboardInput, shoyu_keyboard_input, SHOYU_TYPE_INPUT) + +static void shoyu_keyboard_input_modifiers(struct wl_listener *listener, + void *data) { + ShoyuKeyboardInput *self = wl_container_of(listener, self, modifiers); + ShoyuInput *input = SHOYU_INPUT(self); + + struct wlr_keyboard *wlr_keyboard = + wlr_keyboard_from_input_device(input->wlr_input_device); + + wlr_seat_set_keyboard(input->compositor->wlr_seat, wlr_keyboard); + wlr_seat_keyboard_notify_modifiers(input->compositor->wlr_seat, + &wlr_keyboard->modifiers); +} + +static void shoyu_keyboard_input_key(struct wl_listener *listener, void *data) { + ShoyuKeyboardInput *self = wl_container_of(listener, self, key); + ShoyuInput *input = SHOYU_INPUT(self); + + struct wlr_keyboard_key_event *event = data; + + struct wlr_keyboard *wlr_keyboard = + wlr_keyboard_from_input_device(input->wlr_input_device); + + wlr_seat_set_keyboard(input->compositor->wlr_seat, wlr_keyboard); + + wlr_seat_keyboard_notify_key(input->compositor->wlr_seat, event->time_msec, + event->keycode, event->state); +} + +static void shoyu_keyboard_input_finalize(GObject *object) { + ShoyuKeyboardInput *self = SHOYU_KEYBOARD_INPUT(object); + + g_clear_pointer(&self->xkb_keymap, (GDestroyNotify)xkb_keymap_unref); + + G_OBJECT_CLASS(shoyu_keyboard_input_parent_class)->finalize(object); +} + +static void +shoyu_keyboard_input_realized(ShoyuInput *input, + struct wlr_input_device *wlr_input_device) { + ShoyuKeyboardInput *self = SHOYU_KEYBOARD_INPUT(input); + + struct wlr_keyboard *wlr_keyboard = + wlr_keyboard_from_input_device(wlr_input_device); + + wlr_keyboard_set_keymap(wlr_keyboard, self->xkb_keymap); + wlr_keyboard_set_repeat_info(wlr_keyboard, 25, 600); + + self->modifiers.notify = shoyu_keyboard_input_modifiers; + wl_signal_add(&wlr_keyboard->events.modifiers, &self->modifiers); + + self->key.notify = shoyu_keyboard_input_key; + wl_signal_add(&wlr_keyboard->events.key, &self->key); + + wlr_seat_set_keyboard(input->compositor->wlr_seat, wlr_keyboard); +} + +static void shoyu_keyboard_input_unrealized(ShoyuInput *input) { + ShoyuKeyboardInput *self = SHOYU_KEYBOARD_INPUT(input); + + wl_list_remove(&self->modifiers.link); + wl_list_remove(&self->key.link); +} + +static void shoyu_keyboard_input_class_init(ShoyuKeyboardInputClass *class) { + GObjectClass *object_class = G_OBJECT_CLASS(class); + ShoyuInputClass *input_class = SHOYU_INPUT_CLASS(class); + + object_class->finalize = shoyu_keyboard_input_finalize; + + input_class->realized = shoyu_keyboard_input_realized; + input_class->unrealized = shoyu_keyboard_input_unrealized; +} + +static void shoyu_keyboard_input_init(ShoyuKeyboardInput *self) { + struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + + self->xkb_keymap = + xkb_keymap_new_from_names(context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS); + + xkb_context_unref(context); +} diff --git a/shoyu-compositor/keyboard-input.h b/shoyu-compositor/keyboard-input.h new file mode 100644 index 0000000..01df4c6 --- /dev/null +++ b/shoyu-compositor/keyboard-input.h @@ -0,0 +1,36 @@ +#pragma once + +#if !defined(__SHOYU_COMPOSITOR_H_INSIDE__) && !defined(SHOYU_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +#ifdef SHOYU_COMPILATION +typedef struct _ShoyuKeyboardInput ShoyuKeyboardInput; +#else +typedef ShoyuInput ShoyuKeyboardInput; +#endif +typedef struct _ShoyuKeyboardInputClass ShoyuKeyboardInputClass; + +#define SHOYU_TYPE_KEYBOARD_INPUT (shoyu_keyboard_input_get_type()) +#define SHOYU_KEYBOARD_INPUT(object) \ + (G_TYPE_CHECK_INSTANCE_CAST((object), SHOYU_TYPE_KEYBOARD_INPUT, \ + ShoyuKeyboardInput)) +#define SHOYU_KEYBOARD_INPUT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), SHOYU_TYPE_KEYBOARD_INPUT, \ + ShoyuKeyboardInputClass)) +#define SHOYU_IS_KEYBOARD_INPUT(object) \ + (G_TYPE_CHECK_INSTANCE_TYPE((object), SHOYU_TYPE_KEYBOARD_INPUT)) +#define SHOYU_IS_KEYBOARD_INPUT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), SHOYU_TYPE_KEYBOARD_INPUT)) +#define SHOYU_KEYBOARD_INPUT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), SHOYU_TYPE_KEYBOARD_INPUT, \ + ShoyuKeyboardInputClass)) + +SHOYU_AVAILABLE_IN_ALL +GType shoyu_keyboard_input_get_type(void) G_GNUC_CONST; + +G_END_DECLS diff --git a/shoyu-compositor/meson.build b/shoyu-compositor/meson.build index e098bdf..3c8a197 100644 --- a/shoyu-compositor/meson.build +++ b/shoyu-compositor/meson.build @@ -15,7 +15,9 @@ libcompositor_private_sources = files([ libcompositor_private_h_sources = files([ 'compositor-private.h', 'input-private.h', + 'keyboard-input-private.h', 'output-private.h', + 'pointer-input-private.h', 'shell-private.h', 'shell-output-private.h', 'shell-toplevel-private.h', @@ -27,8 +29,10 @@ libcompositor_private_h_sources = files([ libcompositor_public_sources = files([ 'compositor.c', 'input.c', + 'keyboard-input.c', 'main.c', 'output.c', + 'pointer-input.c', 'shell.c', 'surface.c', 'version.c', @@ -39,8 +43,10 @@ libcompositor_public_sources = files([ libcompositor_public_headers = files([ 'compositor.h', 'input.h', + 'keyboard-input.h', 'main.h', 'output.h', + 'pointer-input.h', 'shell.h', 'shoyu-compositor.h', 'surface.h', @@ -97,6 +103,7 @@ libcompositor_deps = [ wlroots, wayland_server, pixman, + xkbcommon, ] darwin_versions = [ diff --git a/shoyu-compositor/pointer-input-private.h b/shoyu-compositor/pointer-input-private.h new file mode 100644 index 0000000..88b1ff6 --- /dev/null +++ b/shoyu-compositor/pointer-input-private.h @@ -0,0 +1,23 @@ +#pragma once + +#include "input-private.h" +#include "pointer-input.h" + +#include +#include + +struct _ShoyuPointerInput { + ShoyuInput parent_instance; + + struct wlr_cursor *cursor; + + struct wl_listener cursor_motion; + struct wl_listener cursor_motion_absolute; + struct wl_listener cursor_button; + struct wl_listener cursor_axis; + struct wl_listener cursor_frame; +}; + +struct _ShoyuPointerInputClass { + ShoyuInputClass parent_class; +}; diff --git a/shoyu-compositor/pointer-input.c b/shoyu-compositor/pointer-input.c new file mode 100644 index 0000000..61600f0 --- /dev/null +++ b/shoyu-compositor/pointer-input.c @@ -0,0 +1,205 @@ +#include "compositor-private.h" +#include "output-private.h" +#include "pointer-input-private.h" +#include "xdg-toplevel-private.h" + +/** + * ShoyuPointerInput: + * + * An input which represents a mouse pointer for #ShoyuCompositor. + */ + +G_DEFINE_TYPE(ShoyuPointerInput, shoyu_pointer_input, SHOYU_TYPE_INPUT) + +static void shoyu_pointer_input_process_cursor_motion(ShoyuPointerInput *self, + uint32_t time) { + ShoyuInput *input = SHOYU_INPUT(self); + + ShoyuXdgToplevel *xdg_toplevel = + shoyu_compositor_get_focused_xdg_toplevel(input->compositor); + + struct wl_output *wl_output = wlr_output_layout_output_at( + input->compositor->output_layout, self->cursor->x, self->cursor->y); + g_return_if_fail(wl_output != NULL); + + ShoyuOutput *output = + shoyu_compositor_get_output(input->compositor, wl_output); + g_return_if_fail(output != NULL); + + double sx = 0.0; + double sy = 0.0; + + wlr_output_layout_output_coords(input->compositor->output_layout, wl_output, + &sx, &sy); + + double rx = self->cursor->x - sx; + double ry = self->cursor->y - sy; + + struct wlr_keyboard *wlr_keyboard = + wlr_seat_get_keyboard(input->compositor->wlr_seat); + + if (xdg_toplevel != NULL) { + wlr_seat_pointer_notify_enter(input->compositor->wlr_seat, + xdg_toplevel->wlr_xdg_toplevel->base->surface, + rx, ry); + + wlr_seat_pointer_notify_motion(input->compositor->wlr_seat, time, rx, ry); + + if (wlr_keyboard != NULL) { + wlr_seat_keyboard_notify_enter( + input->compositor->wlr_seat, + xdg_toplevel->wlr_xdg_toplevel->base->surface, wlr_keyboard->keycodes, + wlr_keyboard->num_keycodes, &wlr_keyboard->modifiers); + } + } else { + if (output->wlr_surface != NULL) { + struct wlr_xdg_toplevel *wlr_xdg_toplevel = + wlr_xdg_toplevel_try_from_wlr_surface(output->wlr_surface); + + if (wlr_xdg_toplevel != NULL) { + wlr_xdg_toplevel_set_activated(wlr_xdg_toplevel, TRUE); + } + + wlr_seat_pointer_notify_enter(input->compositor->wlr_seat, + output->wlr_surface, rx, ry); + + wlr_seat_pointer_notify_motion(input->compositor->wlr_seat, time, rx, ry); + + if (wlr_keyboard != NULL) { + wlr_seat_keyboard_notify_enter( + input->compositor->wlr_seat, output->wlr_surface, + wlr_keyboard->keycodes, wlr_keyboard->num_keycodes, + &wlr_keyboard->modifiers); + } + } else { + wlr_seat_pointer_clear_focus(input->compositor->wlr_seat); + } + } +} + +static void shoyu_pointer_input_cursor_motion(struct wl_listener *listener, + void *data) { + ShoyuPointerInput *self = wl_container_of(listener, self, cursor_motion); + struct wlr_pointer_motion_event *event = data; + + wlr_cursor_move(self->cursor, &event->pointer->base, event->delta_x, + event->delta_y); + shoyu_pointer_input_process_cursor_motion(self, event->time_msec); +} + +static void +shoyu_pointer_input_cursor_motion_absolute(struct wl_listener *listener, + void *data) { + ShoyuPointerInput *self = + wl_container_of(listener, self, cursor_motion_absolute); + struct wlr_pointer_motion_absolute_event *event = data; + + wlr_cursor_warp_absolute(self->cursor, &event->pointer->base, event->x, + event->y); + shoyu_pointer_input_process_cursor_motion(self, event->time_msec); +} + +static void shoyu_pointer_input_cursor_button(struct wl_listener *listener, + void *data) { + ShoyuPointerInput *self = wl_container_of(listener, self, cursor_button); + ShoyuInput *input = SHOYU_INPUT(self); + + struct wlr_pointer_button_event *event = data; + + wlr_seat_pointer_notify_button(input->compositor->wlr_seat, event->time_msec, + event->button, event->state); +} + +static void shoyu_pointer_input_cursor_axis(struct wl_listener *listener, + void *data) { + ShoyuPointerInput *self = wl_container_of(listener, self, cursor_axis); + ShoyuInput *input = SHOYU_INPUT(self); + + struct wlr_pointer_axis_event *event = data; + + wlr_seat_pointer_notify_axis(input->compositor->wlr_seat, event->time_msec, + event->orientation, event->delta, + event->delta_discrete, event->source, + event->relative_direction); +} + +static void shoyu_pointer_input_cursor_frame(struct wl_listener *listener, + void *data) { + ShoyuPointerInput *self = wl_container_of(listener, self, cursor_frame); + + ShoyuInput *input = SHOYU_INPUT(self); + + wlr_seat_pointer_notify_frame(input->compositor->wlr_seat); +} + +static void shoyu_pointer_input_constructed(GObject *object) { + G_OBJECT_CLASS(shoyu_pointer_input_parent_class)->constructed(object); + + ShoyuPointerInput *self = SHOYU_POINTER_INPUT(object); + ShoyuInput *input = SHOYU_INPUT(self); + + wlr_cursor_set_xcursor(self->cursor, input->compositor->wlr_xcursor_manager, + "default"); +} + +static void shoyu_pointer_input_finalize(GObject *object) { + ShoyuPointerInput *self = SHOYU_POINTER_INPUT(object); + + g_clear_pointer(&self->cursor, (GDestroyNotify)wlr_cursor_destroy); + + G_OBJECT_CLASS(shoyu_pointer_input_parent_class)->finalize(object); +} + +static void +shoyu_pointer_input_realized(ShoyuInput *input, + struct wlr_input_device *wlr_input_device) { + ShoyuPointerInput *self = SHOYU_POINTER_INPUT(input); + + self->cursor_motion.notify = shoyu_pointer_input_cursor_motion; + wl_signal_add(&self->cursor->events.motion, &self->cursor_motion); + + self->cursor_motion_absolute.notify = + shoyu_pointer_input_cursor_motion_absolute; + wl_signal_add(&self->cursor->events.motion_absolute, + &self->cursor_motion_absolute); + + self->cursor_button.notify = shoyu_pointer_input_cursor_button; + wl_signal_add(&self->cursor->events.button, &self->cursor_button); + + self->cursor_axis.notify = shoyu_pointer_input_cursor_axis; + wl_signal_add(&self->cursor->events.axis, &self->cursor_axis); + + self->cursor_frame.notify = shoyu_pointer_input_cursor_frame; + wl_signal_add(&self->cursor->events.frame, &self->cursor_frame); + + wlr_cursor_attach_input_device(self->cursor, wlr_input_device); + wlr_cursor_attach_output_layout(self->cursor, + input->compositor->output_layout); +} + +static void shoyu_pointer_input_unrealized(ShoyuInput *input) { + ShoyuPointerInput *self = SHOYU_POINTER_INPUT(input); + + wl_list_remove(&self->cursor_motion.link); + wl_list_remove(&self->cursor_motion_absolute.link); + wl_list_remove(&self->cursor_button.link); + wl_list_remove(&self->cursor_axis.link); + wl_list_remove(&self->cursor_frame.link); + + wlr_cursor_detach_input_device(self->cursor, input->wlr_input_device); +} + +static void shoyu_pointer_input_class_init(ShoyuPointerInputClass *class) { + GObjectClass *object_class = G_OBJECT_CLASS(class); + ShoyuInputClass *input_class = SHOYU_INPUT_CLASS(class); + + object_class->constructed = shoyu_pointer_input_constructed; + object_class->finalize = shoyu_pointer_input_finalize; + + input_class->realized = shoyu_pointer_input_realized; + input_class->unrealized = shoyu_pointer_input_unrealized; +} + +static void shoyu_pointer_input_init(ShoyuPointerInput *self) { + self->cursor = wlr_cursor_create(); +} diff --git a/shoyu-compositor/pointer-input.h b/shoyu-compositor/pointer-input.h new file mode 100644 index 0000000..3227047 --- /dev/null +++ b/shoyu-compositor/pointer-input.h @@ -0,0 +1,36 @@ +#pragma once + +#if !defined(__SHOYU_COMPOSITOR_H_INSIDE__) && !defined(SHOYU_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +#ifdef SHOYU_COMPILATION +typedef struct _ShoyuPointerInput ShoyuPointerInput; +#else +typedef ShoyuInput ShoyuPointerInput; +#endif +typedef struct _ShoyuPointerInputClass ShoyuPointerInputClass; + +#define SHOYU_TYPE_POINTER_INPUT (shoyu_pointer_input_get_type()) +#define SHOYU_POINTER_INPUT(object) \ + (G_TYPE_CHECK_INSTANCE_CAST((object), SHOYU_TYPE_POINTER_INPUT, \ + ShoyuPointerInput)) +#define SHOYU_POINTER_INPUT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), SHOYU_TYPE_POINTER_INPUT, \ + ShoyuPointerInputClass)) +#define SHOYU_IS_POINTER_INPUT(object) \ + (G_TYPE_CHECK_INSTANCE_TYPE((object), SHOYU_TYPE_POINTER_INPUT)) +#define SHOYU_IS_POINTER_INPUT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), SHOYU_TYPE_POINTER_INPUT)) +#define SHOYU_POINTER_INPUT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), SHOYU_TYPE_POINTER_INPUT, \ + ShoyuPointerInputClass)) + +SHOYU_AVAILABLE_IN_ALL +GType shoyu_pointer_input_get_type(void) G_GNUC_CONST; + +G_END_DECLS diff --git a/shoyu-compositor/shell-toplevel.c b/shoyu-compositor/shell-toplevel.c index e043e32..4d60b95 100644 --- a/shoyu-compositor/shell-toplevel.c +++ b/shoyu-compositor/shell-toplevel.c @@ -113,9 +113,22 @@ static void shoyu_shell_toplevel_set_geometry(struct wl_client *client, shoyu_xdg_toplevel_set_geometry(xdg_toplevel, x, y, width, height); } +static void shoyu_shell_toplevel_set_focus(struct wl_client *client, + struct wl_resource *resource) { + ShellToplevel *self = wl_resource_get_user_data(resource); + + ShoyuXdgToplevel *xdg_toplevel = shoyu_compositor_get_xdg_toplevel( + self->shell->compositor, self->wlr_xdg_toplevel); + g_return_if_fail(xdg_toplevel != NULL); + g_return_if_fail(SHOYU_XDG_TOPLEVEL(xdg_toplevel)); + + g_object_set(xdg_toplevel, "focus", TRUE, NULL); +} + static const struct shoyu_shell_toplevel_interface shoyu_shell_toplevel_impl = { .capture = shoyu_shell_toplevel_capture, .set_geometry = shoyu_shell_toplevel_set_geometry, + .set_focus = shoyu_shell_toplevel_set_focus, }; static void shoyu_shell_toplevel_destroy(ShellToplevel *self) { diff --git a/shoyu-compositor/shell.c b/shoyu-compositor/shell.c index 1624ca0..3ecfc59 100644 --- a/shoyu-compositor/shell.c +++ b/shoyu-compositor/shell.c @@ -22,8 +22,21 @@ static GParamSpec *shoyu_shell_props[N_PROPERTIES] = { G_DEFINE_TYPE(ShoyuShell, shoyu_shell, G_TYPE_OBJECT) +static void shoyu_shell_clear_focus(struct wl_client *wl_client, + struct wl_resource *shell_resource) { + ShoyuShell *shell = wl_resource_get_user_data(shell_resource); + + for (GList *item = shell->compositor->xdg_toplevels; item != NULL; + item = item->next) { + ShoyuXdgToplevel *xdg_toplevel = SHOYU_XDG_TOPLEVEL(item->data); + + g_object_set(xdg_toplevel, "focus", FALSE, NULL); + } +} + static const struct shoyu_shell_interface shoyu_shell_impl = { .get_output = shoyu_shell_get_output, + .clear_focus = shoyu_shell_clear_focus, }; static void shoyu_shell_resource_destroy(struct wl_resource *resource) { diff --git a/shoyu-compositor/shoyu-compositor.h b/shoyu-compositor/shoyu-compositor.h index 2e0eeaa..2d175e1 100644 --- a/shoyu-compositor/shoyu-compositor.h +++ b/shoyu-compositor/shoyu-compositor.h @@ -5,8 +5,10 @@ #include #include #include +#include #include #include +#include #include #include #include diff --git a/shoyu-compositor/xdg-toplevel-private.h b/shoyu-compositor/xdg-toplevel-private.h index e28015a..e4f7090 100644 --- a/shoyu-compositor/xdg-toplevel-private.h +++ b/shoyu-compositor/xdg-toplevel-private.h @@ -12,6 +12,7 @@ struct _ShoyuXdgToplevel { struct wlr_xdg_toplevel *wlr_xdg_toplevel; bool is_invalidated; + bool is_focused; guint x; guint y; diff --git a/shoyu-compositor/xdg-toplevel.c b/shoyu-compositor/xdg-toplevel.c index c6e5c30..3e9e2d9 100644 --- a/shoyu-compositor/xdg-toplevel.c +++ b/shoyu-compositor/xdg-toplevel.c @@ -6,6 +6,7 @@ enum { PROP_0 = 0, PROP_COMPOSITOR, + PROP_FOCUS, N_PROPERTIES, SIG_DESTROY = 0, @@ -48,6 +49,12 @@ static void shoyu_xdg_toplevel_set_property(GObject *object, guint prop_id, case PROP_COMPOSITOR: self->compositor = g_value_dup_object(value); break; + case PROP_FOCUS: + self->is_focused = g_value_get_boolean(value); + if (!self->is_invalidated && self->wlr_xdg_toplevel != NULL) + wlr_xdg_toplevel_set_activated(self->wlr_xdg_toplevel, + self->is_focused); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -62,6 +69,9 @@ static void shoyu_xdg_toplevel_get_property(GObject *object, guint prop_id, case PROP_COMPOSITOR: g_value_set_object(value, G_OBJECT(self->compositor)); break; + case PROP_FOCUS: + g_value_set_boolean(value, self->is_focused); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -79,6 +89,10 @@ static void shoyu_xdg_toplevel_class_init(ShoyuXdgToplevelClass *class) { "compositor", "Shoyu Compositor", "The compositor the output comes from.", SHOYU_TYPE_COMPOSITOR, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + shoyu_xdg_toplevel_props[PROP_FOCUS] = g_param_spec_boolean( + "focus", "Is toplevel focused", "Whether the toplevel is in focus.", + FALSE, G_PARAM_READWRITE); + g_object_class_install_properties(object_class, N_PROPERTIES, shoyu_xdg_toplevel_props); diff --git a/shoyu-shell-gtk3/display.c b/shoyu-shell-gtk3/display.c index 1fe4ebf..fe6a40d 100644 --- a/shoyu-shell-gtk3/display.c +++ b/shoyu-shell-gtk3/display.c @@ -232,3 +232,9 @@ GListModel *shoyu_shell_gtk_display_get_toplevels(ShoyuShellGtkDisplay *self) { g_return_val_if_fail(SHOYU_SHELL_GTK_IS_DISPLAY(self), NULL); return G_LIST_MODEL(self->toplevels); } + +void shoyu_shell_gtk_display_clear_focus(ShoyuShellGtkDisplay *self) { + g_return_if_fail(SHOYU_SHELL_GTK_IS_DISPLAY(self)); + + shoyu_shell_clear_focus(self->shoyu_shell); +} diff --git a/shoyu-shell-gtk3/display.h b/shoyu-shell-gtk3/display.h index a00c007..6772e0c 100644 --- a/shoyu-shell-gtk3/display.h +++ b/shoyu-shell-gtk3/display.h @@ -42,4 +42,7 @@ ShoyuShellGtkDisplay *shoyu_shell_gtk_display_get(GdkDisplay *display); SHOYU_SHELL_GTK_AVAILABLE_IN_ALL GListModel *shoyu_shell_gtk_display_get_toplevels(ShoyuShellGtkDisplay *self); +SHOYU_SHELL_GTK_AVAILABLE_IN_ALL +void shoyu_shell_gtk_display_clear_focus(ShoyuShellGtkDisplay *self); + G_END_DECLS diff --git a/shoyu-shell-gtk3/toplevel.c b/shoyu-shell-gtk3/toplevel.c index f0cbb53..ec817b3 100644 --- a/shoyu-shell-gtk3/toplevel.c +++ b/shoyu-shell-gtk3/toplevel.c @@ -487,3 +487,10 @@ void shoyu_shell_gtk_toplevel_set_geometry(ShoyuShellGtkToplevel *self, shoyu_shell_toplevel_set_geometry(self->shoyu_shell_toplevel, x, y, width, height); } + +void shoyu_shell_gtk_toplevel_set_focus(ShoyuShellGtkToplevel *self) { + g_return_if_fail(SHOYU_SHELL_GTK_IS_TOPLEVEL(self)); + g_return_if_fail(self->shoyu_shell_toplevel != NULL && !self->is_invalidated); + + shoyu_shell_toplevel_set_focus(self->shoyu_shell_toplevel); +} diff --git a/shoyu-shell-gtk3/toplevel.h b/shoyu-shell-gtk3/toplevel.h index d3cd04c..f271ac5 100644 --- a/shoyu-shell-gtk3/toplevel.h +++ b/shoyu-shell-gtk3/toplevel.h @@ -42,4 +42,7 @@ void shoyu_shell_gtk_toplevel_set_geometry(ShoyuShellGtkToplevel *self, uint32_t x, uint32_t y, uint32_t width, uint32_t height); +SHOYU_SHELL_GTK_AVAILABLE_IN_ALL +void shoyu_shell_gtk_toplevel_set_focus(ShoyuShellGtkToplevel *self); + G_END_DECLS diff --git a/shoyu-shell-gtk4/display.c b/shoyu-shell-gtk4/display.c index 9a3a00f..ac77f6e 100644 --- a/shoyu-shell-gtk4/display.c +++ b/shoyu-shell-gtk4/display.c @@ -232,3 +232,9 @@ GListModel *shoyu_shell_gtk_display_get_toplevels(ShoyuShellGtkDisplay *self) { g_return_val_if_fail(SHOYU_SHELL_GTK_IS_DISPLAY(self), NULL); return G_LIST_MODEL(self->toplevels); } + +void shoyu_shell_gtk_display_clear_focus(ShoyuShellGtkDisplay *self) { + g_return_if_fail(SHOYU_SHELL_GTK_IS_DISPLAY(self)); + + shoyu_shell_clear_focus(self->shoyu_shell); +} diff --git a/shoyu-shell-gtk4/display.h b/shoyu-shell-gtk4/display.h index 70d56a4..d18f212 100644 --- a/shoyu-shell-gtk4/display.h +++ b/shoyu-shell-gtk4/display.h @@ -43,4 +43,7 @@ ShoyuShellGtkDisplay *shoyu_shell_gtk_display_get(GdkDisplay *display); SHOYU_SHELL_GTK_AVAILABLE_IN_ALL GListModel *shoyu_shell_gtk_display_get_toplevels(ShoyuShellGtkDisplay *self); +SHOYU_SHELL_GTK_AVAILABLE_IN_ALL +void shoyu_shell_gtk_display_clear_focus(ShoyuShellGtkDisplay *self); + G_END_DECLS diff --git a/shoyu-shell-gtk4/toplevel.c b/shoyu-shell-gtk4/toplevel.c index aeec5a2..788dd00 100644 --- a/shoyu-shell-gtk4/toplevel.c +++ b/shoyu-shell-gtk4/toplevel.c @@ -454,3 +454,10 @@ void shoyu_shell_gtk_toplevel_set_geometry(ShoyuShellGtkToplevel *self, shoyu_shell_toplevel_set_geometry(self->shoyu_shell_toplevel, x, y, width, height); } + +void shoyu_shell_gtk_toplevel_set_focus(ShoyuShellGtkToplevel *self) { + g_return_if_fail(SHOYU_SHELL_GTK_IS_TOPLEVEL(self)); + g_return_if_fail(self->shoyu_shell_toplevel != NULL && !self->is_invalidated); + + shoyu_shell_toplevel_set_focus(self->shoyu_shell_toplevel); +} diff --git a/shoyu-shell-gtk4/toplevel.h b/shoyu-shell-gtk4/toplevel.h index eea7b35..500136a 100644 --- a/shoyu-shell-gtk4/toplevel.h +++ b/shoyu-shell-gtk4/toplevel.h @@ -42,4 +42,7 @@ void shoyu_shell_gtk_toplevel_set_geometry(ShoyuShellGtkToplevel *self, uint32_t x, uint32_t y, uint32_t width, uint32_t height); +SHOYU_SHELL_GTK_AVAILABLE_IN_ALL +void shoyu_shell_gtk_toplevel_set_focus(ShoyuShellGtkToplevel *self); + G_END_DECLS