324 lines
8.0 KiB
C
324 lines
8.0 KiB
C
#include <hikari/cursor.h>
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
|
|
#include <wlr/types/wlr_seat.h>
|
|
#include <wlr/types/wlr_xcursor_manager.h>
|
|
|
|
#include <hikari/binding.h>
|
|
#include <hikari/binding_config.h>
|
|
#include <hikari/memory.h>
|
|
#include <hikari/output.h>
|
|
#include <hikari/server.h>
|
|
|
|
static void
|
|
motion_absolute_handler(struct wl_listener *listener, void *data);
|
|
|
|
static void
|
|
frame_handler(struct wl_listener *listener, void *data);
|
|
|
|
static void
|
|
motion_handler(struct wl_listener *listener, void *data);
|
|
|
|
static void
|
|
button_handler(struct wl_listener *listener, void *data);
|
|
|
|
static void
|
|
axis_handler(struct wl_listener *listener, void *data);
|
|
|
|
static void
|
|
request_set_cursor_handler(struct wl_listener *listener, void *data);
|
|
|
|
static void
|
|
surface_destroy_handler(struct wl_listener *listener, void *data);
|
|
|
|
static unsigned long
|
|
get_cursor_size(void)
|
|
{
|
|
const char *cursor_size = getenv("XCURSOR_SIZE");
|
|
|
|
if (cursor_size != NULL && strlen(cursor_size) > 0) {
|
|
errno = 0;
|
|
char *end;
|
|
unsigned long size = strtoul(cursor_size, &end, 10);
|
|
if (*end == '\0' && errno == 0) {
|
|
return size;
|
|
}
|
|
}
|
|
|
|
setenv("XCURSOR_SIZE", "16", 1);
|
|
|
|
return 16;
|
|
}
|
|
|
|
static const char *
|
|
get_cursor_theme(void)
|
|
{
|
|
char *cursor_theme = getenv("XCURSOR_THEME");
|
|
|
|
if (cursor_theme != NULL && strlen(cursor_theme) > 0) {
|
|
return cursor_theme;
|
|
}
|
|
|
|
setenv("XCURSOR_THEME", "Adwaita", 1);
|
|
|
|
return "Adwaita";
|
|
}
|
|
|
|
static void
|
|
configure_bindings(struct hikari_cursor *cursor, struct wl_list *bindings)
|
|
{
|
|
int nr[256] = { 0 };
|
|
struct hikari_binding_config *binding_config;
|
|
wl_list_for_each (binding_config, bindings, link) {
|
|
nr[binding_config->key.modifiers]++;
|
|
}
|
|
|
|
for (int mask = 0; mask < 256; mask++) {
|
|
cursor->bindings[mask].nbindings = nr[mask];
|
|
if (nr[mask] != 0) {
|
|
cursor->bindings[mask].bindings =
|
|
hikari_calloc(nr[mask], sizeof(struct hikari_binding));
|
|
} else {
|
|
cursor->bindings[mask].bindings = NULL;
|
|
}
|
|
|
|
nr[mask] = 0;
|
|
}
|
|
|
|
wl_list_for_each (binding_config, bindings, link) {
|
|
uint8_t mask = binding_config->key.modifiers;
|
|
struct hikari_binding *binding = &cursor->bindings[mask].bindings[nr[mask]];
|
|
|
|
binding->action = &binding_config->action;
|
|
|
|
switch (binding_config->key.type) {
|
|
case HIKARI_ACTION_BINDING_KEY_KEYCODE:
|
|
binding->keycode = binding_config->key.value.keycode;
|
|
break;
|
|
|
|
case HIKARI_ACTION_BINDING_KEY_KEYSYM:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
nr[mask]++;
|
|
}
|
|
}
|
|
|
|
void
|
|
hikari_cursor_init(
|
|
struct hikari_cursor *cursor, struct wlr_output_layout *output_layout)
|
|
{
|
|
struct wlr_cursor *wlr_cursor = wlr_cursor_create();
|
|
|
|
wlr_cursor_attach_output_layout(wlr_cursor, output_layout);
|
|
|
|
const char *cursor_theme = get_cursor_theme();
|
|
unsigned long cursor_size = get_cursor_size();
|
|
|
|
cursor->cursor_mgr = wlr_xcursor_manager_create(cursor_theme, cursor_size);
|
|
wlr_xcursor_manager_load(cursor->cursor_mgr, 1);
|
|
|
|
cursor->wlr_cursor = wlr_cursor;
|
|
|
|
wl_list_init(&cursor->surface_destroy.link);
|
|
hikari_binding_group_init(cursor->bindings);
|
|
}
|
|
|
|
void
|
|
hikari_cursor_configure_bindings(
|
|
struct hikari_cursor *cursor, struct wl_list *bindings)
|
|
{
|
|
hikari_binding_group_fini(cursor->bindings);
|
|
hikari_binding_group_init(cursor->bindings);
|
|
configure_bindings(cursor, bindings);
|
|
}
|
|
|
|
void
|
|
hikari_cursor_fini(struct hikari_cursor *cursor)
|
|
{
|
|
hikari_binding_group_fini(cursor->bindings);
|
|
hikari_cursor_deactivate(cursor);
|
|
|
|
wlr_xcursor_manager_destroy(cursor->cursor_mgr);
|
|
}
|
|
|
|
void
|
|
hikari_cursor_activate(struct hikari_cursor *cursor)
|
|
{
|
|
struct wlr_cursor *wlr_cursor = cursor->wlr_cursor;
|
|
|
|
cursor->motion_absolute.notify = motion_absolute_handler;
|
|
wl_signal_add(&wlr_cursor->events.motion_absolute, &cursor->motion_absolute);
|
|
|
|
cursor->frame.notify = frame_handler;
|
|
wl_signal_add(&wlr_cursor->events.frame, &cursor->frame);
|
|
|
|
cursor->motion.notify = motion_handler;
|
|
wl_signal_add(&wlr_cursor->events.motion, &cursor->motion);
|
|
|
|
cursor->button.notify = button_handler;
|
|
wl_signal_add(&wlr_cursor->events.button, &cursor->button);
|
|
|
|
cursor->axis.notify = axis_handler;
|
|
wl_signal_add(&wlr_cursor->events.axis, &cursor->axis);
|
|
|
|
cursor->request_set_cursor.notify = request_set_cursor_handler;
|
|
wl_signal_add(&hikari_server.seat->events.request_set_cursor,
|
|
&cursor->request_set_cursor);
|
|
|
|
hikari_cursor_reset_image(cursor);
|
|
}
|
|
|
|
void
|
|
hikari_cursor_deactivate(struct hikari_cursor *cursor)
|
|
{
|
|
wl_list_remove(&cursor->motion_absolute.link);
|
|
wl_list_remove(&cursor->frame.link);
|
|
wl_list_remove(&cursor->motion.link);
|
|
wl_list_remove(&cursor->button.link);
|
|
wl_list_remove(&cursor->axis.link);
|
|
wl_list_remove(&cursor->request_set_cursor.link);
|
|
|
|
hikari_cursor_set_image(cursor, NULL);
|
|
}
|
|
|
|
void
|
|
hikari_cursor_set_image(struct hikari_cursor *cursor, const char *path)
|
|
{
|
|
wl_list_remove(&cursor->surface_destroy.link);
|
|
wl_list_init(&cursor->surface_destroy.link);
|
|
|
|
if (path != NULL) {
|
|
wlr_xcursor_manager_set_cursor_image(
|
|
cursor->cursor_mgr, path, cursor->wlr_cursor);
|
|
} else {
|
|
wlr_cursor_set_image(cursor->wlr_cursor, NULL, 0, 0, 0, 0, 0, 0);
|
|
}
|
|
}
|
|
|
|
void
|
|
hikari_cursor_center(struct hikari_cursor *cursor,
|
|
struct hikari_output *output,
|
|
struct wlr_box *geometry)
|
|
{
|
|
int x = output->geometry.x + geometry->x + geometry->width / 2;
|
|
int y = output->geometry.y + geometry->y + geometry->height / 2;
|
|
|
|
hikari_cursor_warp(cursor, x, y);
|
|
}
|
|
|
|
static void
|
|
motion_absolute_handler(struct wl_listener *listener, void *data)
|
|
{
|
|
struct hikari_cursor *cursor =
|
|
wl_container_of(listener, cursor, motion_absolute);
|
|
|
|
assert(!hikari_server_in_lock_mode());
|
|
|
|
struct wlr_event_pointer_motion_absolute *event = data;
|
|
|
|
wlr_cursor_warp_absolute(
|
|
cursor->wlr_cursor, event->device, event->x, event->y);
|
|
|
|
hikari_server.mode->cursor_move(event->time_msec);
|
|
}
|
|
|
|
static void
|
|
frame_handler(struct wl_listener *listener, void *data)
|
|
{
|
|
assert(!hikari_server_in_lock_mode());
|
|
|
|
wlr_seat_pointer_notify_frame(hikari_server.seat);
|
|
}
|
|
|
|
static void
|
|
motion_handler(struct wl_listener *listener, void *data)
|
|
{
|
|
struct hikari_cursor *cursor = wl_container_of(listener, cursor, motion);
|
|
|
|
assert(!hikari_server_in_lock_mode());
|
|
|
|
struct wlr_event_pointer_motion *event = data;
|
|
|
|
wlr_cursor_move(
|
|
cursor->wlr_cursor, event->device, event->delta_x, event->delta_y);
|
|
|
|
hikari_server.mode->cursor_move(event->time_msec);
|
|
}
|
|
|
|
static void
|
|
button_handler(struct wl_listener *listener, void *data)
|
|
{
|
|
assert(!hikari_server_in_lock_mode());
|
|
|
|
struct hikari_cursor *cursor = wl_container_of(listener, cursor, button);
|
|
struct wlr_event_pointer_button *event = data;
|
|
|
|
hikari_server.mode->button_handler(cursor, event);
|
|
}
|
|
|
|
static void
|
|
axis_handler(struct wl_listener *listener, void *data)
|
|
{
|
|
assert(!hikari_server_in_lock_mode());
|
|
|
|
struct wlr_event_pointer_axis *event = data;
|
|
|
|
wlr_seat_pointer_notify_axis(hikari_server.seat,
|
|
event->time_msec,
|
|
event->orientation,
|
|
event->delta,
|
|
event->delta_discrete,
|
|
event->source);
|
|
}
|
|
|
|
static void
|
|
request_set_cursor_handler(struct wl_listener *listener, void *data)
|
|
{
|
|
if (!hikari_server_in_normal_mode()) {
|
|
return;
|
|
}
|
|
|
|
struct hikari_cursor *cursor =
|
|
wl_container_of(listener, cursor, request_set_cursor);
|
|
|
|
struct hikari_server *server = &hikari_server;
|
|
struct wlr_seat_pointer_request_set_cursor_event *event = data;
|
|
struct wlr_seat *seat = server->seat;
|
|
|
|
struct wl_client *focused_client = NULL;
|
|
struct wlr_surface *focused_surface = seat->pointer_state.focused_surface;
|
|
if (focused_surface != NULL) {
|
|
focused_client = wl_resource_get_client(focused_surface->resource);
|
|
}
|
|
|
|
if (focused_client == NULL || event->seat_client->client != focused_client) {
|
|
return;
|
|
}
|
|
|
|
struct wlr_surface *surface = event->surface;
|
|
|
|
wl_list_remove(&cursor->surface_destroy.link);
|
|
if (surface != NULL) {
|
|
cursor->surface_destroy.notify = surface_destroy_handler;
|
|
wl_signal_add(&surface->events.destroy, &cursor->surface_destroy);
|
|
} else {
|
|
wl_list_init(&cursor->surface_destroy.link);
|
|
}
|
|
|
|
wlr_cursor_set_surface(
|
|
cursor->wlr_cursor, surface, event->hotspot_x, event->hotspot_y);
|
|
}
|
|
|
|
static void
|
|
surface_destroy_handler(struct wl_listener *listener, void *data)
|
|
{
|
|
struct hikari_cursor *cursor =
|
|
wl_container_of(listener, cursor, surface_destroy);
|
|
|
|
hikari_cursor_reset_image(cursor);
|
|
}
|