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