make neko follow cursor

This commit is contained in:
Leon Henrik Plickat 2023-10-10 19:48:54 +02:00
commit a4f7f31a37
2 changed files with 208 additions and 16 deletions

View file

@ -21,6 +21,7 @@ wayneko \- Neko on Wayland
.SH DESCRIPTION .SH DESCRIPTION
.P .P
Display an animated neko cat on the bottom of an output. Display an animated neko cat on the bottom of an output.
Responds to the pointer being near the bottom edge of that output.
.P .P
Requires the Wayland server to implement \fBzwlr-layer-shell-unstable-v1\fR. Requires the Wayland server to implement \fBzwlr-layer-shell-unstable-v1\fR.
. .

223
wayneko.c
View file

@ -63,6 +63,16 @@ const uint16_t animation_timeout = 200;
size_t animation_ticks_until_next_frame = 10; size_t animation_ticks_until_next_frame = 10;
enum Neko current_neko = NEKO_STARE; enum Neko current_neko = NEKO_STARE;
struct Seat
{
struct wl_list link;
struct wl_seat *wl_seat;
struct wl_pointer *wl_pointer;
uint32_t global_name;
bool on_surface;
uint32_t surface_x;
};
struct Buffer struct Buffer
{ {
struct wl_list link; struct wl_list link;
@ -101,6 +111,8 @@ struct wl_shm *wl_shm = NULL;
struct zwlr_layer_shell_v1 *layer_shell = NULL; struct zwlr_layer_shell_v1 *layer_shell = NULL;
struct timespec last_tick; struct timespec last_tick;
struct wl_list seats;
/* The amount of buffers per surface we consider the reasonable upper limit. /* The amount of buffers per surface we consider the reasonable upper limit.
* Some compositors sometimes tripple-buffer, so three seems to be ok. * Some compositors sometimes tripple-buffer, so three seems to be ok.
* Note that we can absolutely work with higher buffer numbers if needed, * Note that we can absolutely work with higher buffer numbers if needed,
@ -457,6 +469,121 @@ static void buffer_pool_destroy_all_buffers (void)
} }
} }
/**********
* *
* Seat *
* *
**********/
static struct Seat *seat_from_global_name (uint32_t name)
{
struct Seat *seat;
wl_list_for_each(seat, &seats, link)
if ( seat->global_name == name )
return seat;
return NULL;
}
static void seat_release_pointer (struct Seat *seat)
{
seat->on_surface = false;
if (seat->wl_pointer)
{
wl_pointer_release(seat->wl_pointer);
seat->wl_pointer = NULL;
}
}
static void pointer_handle_enter (void *data, struct wl_pointer *wl_pointer,
uint32_t serial, struct wl_surface *wl_surface,
wl_fixed_t x, wl_fixed_t y)
{
struct Seat *seat = (struct Seat *)data;
assert(wl_surface == surface.wl_surface);
seat->on_surface = true;
seat->surface_x = (uint32_t)wl_fixed_to_int(x);
/* Abort current animation frame. */
animation_ticks_until_next_frame = 0;
}
static void pointer_handle_leave (void *data, struct wl_pointer *wl_pointer,
uint32_t serial, struct wl_surface *wl_surface)
{
struct Seat *seat = (struct Seat *)data;
assert(wl_surface == surface.wl_surface);
assert(seat->on_surface == true);
seat->on_surface = false;
}
static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
uint32_t time, wl_fixed_t x, wl_fixed_t y)
{
struct Seat *seat = (struct Seat *)data;
assert(seat->on_surface == true);
seat->surface_x = (uint32_t)wl_fixed_to_int(x);
}
static const struct wl_pointer_listener pointer_listener = {
.enter = pointer_handle_enter,
.leave = pointer_handle_leave,
.motion = pointer_handle_motion,
.axis_discrete = noop,
.axis = noop,
.axis_source = noop,
.axis_stop = noop,
.button = noop,
.frame = noop,
};
static void seat_bind_pointer (struct Seat *seat)
{
seat->wl_pointer = wl_seat_get_pointer(seat->wl_seat);
wl_pointer_add_listener(seat->wl_pointer, &pointer_listener, seat);
}
static void seat_handle_capabilities (void *data, struct wl_seat *wl_seat,
uint32_t capabilities)
{
struct Seat *seat = (struct Seat *)data;
if ( capabilities & WL_SEAT_CAPABILITY_POINTER )
seat_bind_pointer(seat);
else
seat_release_pointer(seat);
}
static const struct wl_seat_listener seat_listener = {
.capabilities = seat_handle_capabilities,
.name = noop,
};
static void seat_new (struct wl_seat *wl_seat, uint32_t name)
{
struct Seat *seat = calloc(1, sizeof(struct Seat));
if ( seat == NULL )
{
fprintf(stderr, "ERROR: calloc(): %s\n", strerror(errno));
return;
}
seat->wl_seat = wl_seat;
seat->global_name = name;
seat->on_surface = false;
wl_seat_set_user_data(seat->wl_seat, seat);
wl_list_insert(&seats, &seat->link);
wl_seat_add_listener(wl_seat, &seat_listener, seat);
}
static void seat_destroy (struct Seat *seat)
{
seat_release_pointer(seat);
wl_seat_destroy(seat->wl_seat);
wl_list_remove(&seat->link);
free(seat);
}
/*********** /***********
* * * *
* Atlas * * Atlas *
@ -618,14 +745,54 @@ static void animation_neko_do_scratch (void)
} }
/** Returns true if new frame is needed. */ /** Returns true if new frame is needed. */
static bool animation_next_state (void) static bool animation_next_state_with_hotspot (uint32_t x)
{ {
if ( animation_ticks_until_next_frame > 0 ) switch (current_neko)
{ {
animation_ticks_until_next_frame--; case NEKO_SHOCK:
return false; case NEKO_RUN_RIGHT_1:
} case NEKO_RUN_RIGHT_2:
case NEKO_RUN_LEFT_1:
case NEKO_RUN_LEFT_2:
bool need_frame = current_neko != NEKO_STARE;
if ( x < surface.neko_x )
{
if (!animation_can_run_left())
{
animation_neko_do_stare(true);
return need_frame;
}
animation_neko_advance_left();
animation_neko_do_run_left();
return true;
}
else if ( x > surface.neko_x + neko_size )
{
if (!animation_can_run_right())
{
animation_neko_do_stare(true);
return need_frame;
}
animation_neko_advance_right();
animation_neko_do_run_right();
return true;
}
else
{
animation_neko_do_stare(true);
return need_frame;
}
default:
animation_neko_do_shock();
return true;
}
}
/** Returns true if new frame is needed. */
static bool animation_next_state_normal (void)
{
switch (current_neko) switch (current_neko)
{ {
case NEKO_STARE: case NEKO_STARE:
@ -701,7 +868,6 @@ static bool animation_next_state (void)
animation_neko_do_sleep(); animation_neko_do_sleep();
return true; return true;
case NEKO_SCRATCH_1: case NEKO_SCRATCH_1:
case NEKO_SCRATCH_2: case NEKO_SCRATCH_2:
if ( rand() % 4 == 0 ) if ( rand() % 4 == 0 )
@ -711,14 +877,14 @@ static bool animation_next_state (void)
return true; return true;
case NEKO_THINK: case NEKO_THINK:
if ( rand() %2 == 0 ) if ( rand() % 2 == 0 )
animation_neko_do_stare(false); animation_neko_do_stare(false);
else else
animation_neko_do_shock(); animation_neko_do_shock();
return true; return true;
case NEKO_YAWN: case NEKO_YAWN:
if ( rand() %2 == 0 ) if ( rand() % 2 == 0 )
animation_neko_do_stare(false); animation_neko_do_stare(false);
else else
animation_neko_do_sleep(); animation_neko_do_sleep();
@ -733,6 +899,24 @@ static bool animation_next_state (void)
return false; return false;
} }
/** Returns true if new frame is needed. */
static bool animation_next_state (void)
{
if ( animation_ticks_until_next_frame > 0 )
{
animation_ticks_until_next_frame--;
return false;
}
struct Seat *seat;
wl_list_for_each(seat, &seats, link)
{
if (seat->on_surface)
return animation_next_state_with_hotspot(seat->surface_x);
}
return animation_next_state_normal();
}
/************* /*************
* * * *
* Surface * * Surface *
@ -835,11 +1019,6 @@ static void surface_create (void)
ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT
); );
/* Empty input region. */
struct wl_region *region = wl_compositor_create_region(wl_compositor);
wl_surface_set_input_region(surface.wl_surface, region);
wl_region_destroy(region);
wl_surface_commit(surface.wl_surface); wl_surface_commit(surface.wl_surface);
} }
@ -857,13 +1036,24 @@ static void registry_handle_global (void *data, struct wl_registry *registry,
wl_compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4); wl_compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4);
else if ( strcmp(interface, wl_shm_interface.name) == 0 ) else if ( strcmp(interface, wl_shm_interface.name) == 0 )
wl_shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); wl_shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
else if ( strcmp(interface, wl_seat_interface.name) == 0 )
{
struct wl_seat *wl_seat = wl_registry_bind(registry, name, &wl_seat_interface, 7);
seat_new(wl_seat, name);
}
}
static void registry_handle_global_remove (void *data, struct wl_registry *registry,
uint32_t name)
{
struct Seat *seat = seat_from_global_name(name);
if ( seat != NULL )
seat_destroy(seat);
} }
static const struct wl_registry_listener registry_listener = { static const struct wl_registry_listener registry_listener = {
.global = registry_handle_global, .global = registry_handle_global,
.global_remove = registry_handle_global_remove,
/* We do not bind interfaces that - realistically - will ever disappear. */
.global_remove = noop,
}; };
static char *check_for_interfaces (void) static char *check_for_interfaces (void)
@ -978,6 +1168,7 @@ int main (int argc, char *argv[])
} }
} }
wl_list_init(&seats);
wl_list_init(&buffer_pool); wl_list_init(&buffer_pool);
srand((unsigned int)time(0)); srand((unsigned int)time(0));