From 6bf7e095cf23b570c1fa0e295f9d950404867a22 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Mon, 12 Jun 2017 22:33:55 -0400 Subject: [PATCH] Flesh out keyboard support in examples --- example/rotation.c | 157 ++++++++++++++++++++++++++++++++++++++++----- example/simple.c | 10 --- 2 files changed, 140 insertions(+), 27 deletions(-) diff --git a/example/rotation.c b/example/rotation.c index 12778f8e..24c9f494 100644 --- a/example/rotation.c +++ b/example/rotation.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -18,10 +19,19 @@ #include "cat.h" struct state { + struct wl_list config; + struct xkb_keymap *keymap; + struct xkb_state *xkb_state; + bool exit; + + struct wl_list keyboards; + struct wl_listener input_add; + struct wl_listener input_remove; + + struct wl_list outputs; struct wl_listener output_add; struct wl_listener output_remove; - struct wl_list outputs; - struct wl_list config; + struct wlr_renderer *renderer; struct wlr_surface *cat_texture; }; @@ -33,6 +43,7 @@ struct output_state { struct state *state; struct wl_listener frame; float x_offs, y_offs; + float x_vel, y_vel; }; struct output_config { @@ -41,6 +52,13 @@ struct output_config { struct wl_list link; }; +struct keyboard_state { + struct state *state; + struct wlr_input_device *device; + struct wl_listener key; + struct wl_list link; +}; + static void output_frame(struct wl_listener *listener, void *data) { struct output_state *ostate = wl_container_of(listener, ostate, frame); struct wlr_output *output = ostate->output; @@ -68,8 +86,8 @@ static void output_frame(struct wl_listener *listener, void *data) { (now.tv_nsec - ostate->last_frame.tv_nsec) / 1000000; float seconds = ms / 1000.0f; - ostate->x_offs += 128 * seconds; - ostate->y_offs += 128 * seconds; + ostate->x_offs += ostate->x_vel * seconds; + ostate->y_offs += ostate->y_vel * seconds; if (ostate->x_offs > 128) ostate->x_offs = 0; if (ostate->y_offs > 128) ostate->y_offs = 0; ostate->last_frame = now; @@ -89,6 +107,7 @@ static void output_add(struct wl_listener *listener, void *data) { ostate->state = state; ostate->frame.notify = output_frame; ostate->x_offs = ostate->y_offs = 0; + ostate->x_vel = ostate->y_vel = 128; struct output_config *conf; wl_list_for_each(conf, &state->config, link) { @@ -118,9 +137,91 @@ static void output_remove(struct wl_listener *listener, void *data) { } } -static int timer_done(void *data) { - *(bool *)data = true; - return 1; +static void update_velocities(struct state *state, float x_diff, float y_diff) { + struct output_state *ostate; + wl_list_for_each(ostate, &state->outputs, link) { + ostate->x_vel += x_diff; + ostate->y_vel += y_diff; + } +} + +static void handle_keysym(struct state *state, xkb_keysym_t sym, + enum wlr_key_state key_state) { + char name[64]; + int l = xkb_keysym_get_name(sym, name, sizeof(name)); + if (l != -1 && l != sizeof(name)) { + fprintf(stderr, "Key event: %s %s\n", name, + key_state == WLR_KEY_PRESSED ? "pressed" : "released"); + } + // NOTE: It may be better to simply refer to our key state during each frame + // and make this change in pixels/sec^2 + if (key_state == WLR_KEY_PRESSED) { + switch (sym) { + case XKB_KEY_Escape: + state->exit = true; + break; + case XKB_KEY_Left: + update_velocities(state, -16, 0); + break; + case XKB_KEY_Right: + update_velocities(state, 16, 0); + break; + case XKB_KEY_Up: + update_velocities(state, 0, -16); + break; + case XKB_KEY_Down: + update_velocities(state, 0, 16); + break; + } + } +} + +static void keyboard_key(struct wl_listener *listener, void *data) { + struct wlr_keyboard_key *event = data; + struct keyboard_state *kbstate = wl_container_of(listener, kbstate, key); + uint32_t keycode = event->keycode + 8; + const xkb_keysym_t *syms; + int nsyms = xkb_state_key_get_syms(kbstate->state->xkb_state, keycode, &syms); + for (int i = 0; i < nsyms; ++i) { + handle_keysym(kbstate->state, syms[i], event->state); + } + xkb_state_update_key(kbstate->state->xkb_state, keycode, + event->state == WLR_KEY_PRESSED ? XKB_KEY_DOWN : XKB_KEY_UP); +} + +void input_add(struct wl_listener *listener, void *data) { + struct wlr_input_device *device = data; + struct state *state = wl_container_of(listener, state, input_add); + if (device->type != WLR_INPUT_DEVICE_KEYBOARD) { + return; + } + struct keyboard_state *kbstate = calloc(sizeof(struct keyboard_state), 1); + kbstate->device = device; + kbstate->state = state; + wl_list_init(&kbstate->key.link); + kbstate->key.notify = keyboard_key; + wl_signal_add(&device->keyboard->events.key, &kbstate->key); + wl_list_insert(&state->keyboards, &kbstate->link); +} + +void input_remove(struct wl_listener *listener, void *data) { + struct wlr_input_device *device = data; + struct state *state = wl_container_of(listener, state, input_add); + if (device->type != WLR_INPUT_DEVICE_KEYBOARD) { + return; + } + struct keyboard_state *kbstate = NULL, *_kbstate; + wl_list_for_each(_kbstate, &state->keyboards, link) { + if (_kbstate->device == device) { + kbstate = kbstate; + break; + } + } + if (!kbstate) { + return; // We are unfamiliar with this keyboard + } + wl_list_remove(&kbstate->link); + wl_list_remove(&kbstate->key.link); } static void usage(const char *name, int ret) { @@ -191,10 +292,38 @@ static void parse_args(int argc, char *argv[], struct wl_list *config) { int main(int argc, char *argv[]) { struct state state = { + .exit = false, + .input_add = { .notify = input_add }, + .input_remove = { .notify = input_remove }, .output_add = { .notify = output_add }, .output_remove = { .notify = output_remove } }; + struct xkb_rule_names rules; + memset(&rules, 0, sizeof(rules)); + rules.rules = getenv("XKB_DEFAULT_RULES"); + rules.model = getenv("XKB_DEFAULT_MODEL"); + rules.layout = getenv("XKB_DEFAULT_LAYOUT"); + rules.variant = getenv("XKB_DEFAULT_VARIANT"); + rules.options = getenv("XKB_DEFAULT_OPTIONS"); + struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if (!context) { + fprintf(stderr, "Failed to create XKB context\n"); + return 1; + } + state.keymap = + xkb_map_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); + if (!state.keymap) { + fprintf(stderr, "Failed to create XKB keymap\n"); + return 1; + } + xkb_context_unref(context); + state.xkb_state = xkb_state_new(state.keymap); + + wl_list_init(&state.keyboards); + wl_list_init(&state.input_add.link); + wl_list_init(&state.input_remove.link); + wl_list_init(&state.outputs); wl_list_init(&state.config); wl_list_init(&state.output_add.link); @@ -215,10 +344,11 @@ int main(int argc, char *argv[]) { return 1; } + wl_signal_add(&wlr->events.input_add, &state.input_add); + wl_signal_add(&wlr->events.input_remove, &state.input_remove); wl_signal_add(&wlr->events.output_add, &state.output_add); wl_signal_add(&wlr->events.output_remove, &state.output_remove); - - if (!wlr_backend_init(wlr)) { + if (!wlr || !wlr_backend_init(wlr)) { printf("Failed to initialize backend, bailing out\n"); return 1; } @@ -228,17 +358,10 @@ int main(int argc, char *argv[]) { wlr_surface_attach_pixels(state.cat_texture, GL_RGB, cat_tex.width, cat_tex.height, cat_tex.pixel_data); - bool done = false; - struct wl_event_source *timer = wl_event_loop_add_timer(event_loop, - timer_done, &done); - - wl_event_source_timer_update(timer, 30000); - - while (!done) { + while (!state.exit) { wl_event_loop_dispatch(event_loop, 0); } - wl_event_source_remove(timer); wlr_backend_destroy(wlr); wlr_session_finish(session); wlr_surface_destroy(state.cat_texture); diff --git a/example/simple.c b/example/simple.c index 89f74330..331a0d08 100644 --- a/example/simple.c +++ b/example/simple.c @@ -162,12 +162,6 @@ void input_remove(struct wl_listener *listener, void *data) { wl_list_remove(&kbstate->key.link); } -static int timer_done(void *data) { - struct state *state = data; - state->exit = true; - return 1; -} - int main() { struct state state = { .color = { 1.0, 0.0, 0.0 }, @@ -225,10 +219,6 @@ int main() { if (!wlr || !wlr_backend_init(wlr)) { return 1; } - struct wl_event_source *timer = wl_event_loop_add_timer(event_loop, - timer_done, &state); - - wl_event_source_timer_update(timer, 30000); while (!state.exit) { wl_event_loop_dispatch(event_loop, 0);