wayland.c - wmenu - [fork] efficient dynamic menu for wayland
 (HTM) git clone https://git.drkhsh.at/wmenu.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       wayland.c (15754B)
       ---
            1 #define _POSIX_C_SOURCE 200809L
            2 #include <assert.h>
            3 #include <errno.h>
            4 #include <poll.h>
            5 #include <stdbool.h>
            6 #include <stdio.h>
            7 #include <stdlib.h>
            8 #include <string.h>
            9 #include <strings.h>
           10 #include <time.h>
           11 #include <unistd.h>
           12 #include <sys/mman.h>
           13 #include <sys/timerfd.h>
           14 #include <wayland-client.h>
           15 #include <wayland-client-protocol.h>
           16 #include <xkbcommon/xkbcommon.h>
           17 
           18 #include "menu.h"
           19 #include "pool-buffer.h"
           20 #include "render.h"
           21 #include "wayland.h"
           22 #include "xdg-activation-v1-client-protocol.h"
           23 #include "wlr-layer-shell-unstable-v1-client-protocol.h"
           24 
           25 // A Wayland output.
           26 struct output {
           27         struct wl_context *context;
           28         struct wl_output *output;
           29         const char *name;    // output name
           30         int32_t scale;       // output scale
           31         struct output *next; // next output
           32 };
           33 
           34 // Creates and returns a new output.
           35 static struct output *output_create(struct wl_context *context, struct wl_output *wl_output) {
           36         struct output *output = calloc(1, sizeof(struct output));
           37         output->context = context;
           38         output->output = wl_output;
           39         output->scale = 1;
           40         return output;
           41 }
           42 
           43 // Keyboard state.
           44 struct keyboard {
           45         struct menu *menu;
           46         struct wl_keyboard *keyboard;
           47         struct xkb_context *context;
           48         struct xkb_keymap *keymap;
           49         struct xkb_state *state;
           50 
           51         int repeat_timer;
           52         int repeat_delay;
           53         int repeat_period;
           54         enum wl_keyboard_key_state repeat_key_state;
           55         xkb_keysym_t repeat_sym;
           56 };
           57 
           58 // Creates and returns a new keyboard.
           59 static struct keyboard *keyboard_create(struct menu *menu, struct wl_keyboard *wl_keyboard) {
           60         struct keyboard *keyboard = calloc(1, sizeof(struct keyboard));
           61         keyboard->menu = menu;
           62         keyboard->keyboard = wl_keyboard;
           63         keyboard->context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
           64         assert(keyboard->context != NULL);
           65         keyboard->repeat_timer = timerfd_create(CLOCK_MONOTONIC, 0);
           66         assert(keyboard->repeat_timer != -1);
           67         return keyboard;
           68 }
           69 
           70 // Frees the keyboard.
           71 static void free_keyboard(struct keyboard *keyboard) {
           72         wl_keyboard_release(keyboard->keyboard);
           73         xkb_state_unref(keyboard->state);
           74         xkb_keymap_unref(keyboard->keymap);
           75         xkb_context_unref(keyboard->context);
           76         free(keyboard);
           77 }
           78 
           79 // Wayland context.
           80 struct wl_context {
           81         struct menu *menu;
           82 
           83         struct wl_display *display;
           84         struct wl_registry *registry;
           85         struct wl_compositor *compositor;
           86         struct wl_shm *shm;
           87         struct wl_seat *seat;
           88         struct wl_data_device_manager *data_device_manager;
           89         struct zwlr_layer_shell_v1 *layer_shell;
           90         struct output *output_list;
           91         struct xdg_activation_v1 *activation;
           92 
           93         struct keyboard *keyboard;
           94         struct wl_data_device *data_device;
           95         struct wl_surface *surface;
           96         struct zwlr_layer_surface_v1 *layer_surface;
           97         struct wl_data_offer *data_offer;
           98         struct output *output;
           99 
          100         struct pool_buffer buffers[2];
          101         struct pool_buffer *current;
          102 };
          103 
          104 // Returns the current output_scale.
          105 int context_get_scale(struct wl_context *context) {
          106         return context->output ? context->output->scale : 1;
          107 }
          108 
          109 // Returns the current buffer from the pool.
          110 struct pool_buffer *context_get_current_buffer(struct wl_context *context) {
          111         return context->current;
          112 }
          113 
          114 // Returns the next buffer from the pool.
          115 struct pool_buffer *context_get_next_buffer(struct wl_context *context, int scale) {
          116         struct menu *menu = context->menu;
          117         context->current = get_next_buffer(context->shm, context->buffers, menu->width, menu->height, scale);
          118         return context->current;
          119 }
          120 
          121 // Returns the Wayland surface for the context.
          122 struct wl_surface *context_get_surface(struct wl_context *context) {
          123         return context->surface;
          124 }
          125 
          126 // Returns the XKB state for the context.
          127 struct xkb_state *context_get_xkb_state(struct wl_context *context) {
          128         return context->keyboard->state;
          129 }
          130 
          131 // Returns the XDG activation object for the context.
          132 struct xdg_activation_v1 *context_get_xdg_activation(struct wl_context *context) {
          133         return context->activation;
          134 }
          135 
          136 // Retrieves pasted text from a Wayland data offer.
          137 bool context_paste(struct wl_context *context) {
          138         if (!context->data_offer) {
          139                 return false;
          140         }
          141 
          142         int fds[2];
          143         if (pipe(fds) == -1) {
          144                 // Pipe failed
          145                 return false;
          146         }
          147         wl_data_offer_receive(context->data_offer, "text/plain", fds[1]);
          148         close(fds[1]);
          149 
          150         wl_display_roundtrip(context->display);
          151 
          152         while (true) {
          153                 char buf[1024];
          154                 ssize_t n = read(fds[0], buf, sizeof(buf));
          155                 if (n <= 0) {
          156                         break;
          157                 }
          158                 menu_paste(context->menu, buf, n);
          159         }
          160         close(fds[0]);
          161 
          162         wl_data_offer_destroy(context->data_offer);
          163         context->data_offer = NULL;
          164         return true;
          165 }
          166 
          167 // Adds an output to the output list.
          168 static void context_add_output(struct wl_context *context, struct output *output) {
          169         output->next = context->output_list;
          170         context->output_list = output;
          171 }
          172 
          173 // Frees the outputs.
          174 static void free_outputs(struct wl_context *context) {
          175         struct output *next = context->output_list;
          176         while (next) {
          177                 struct output *output = next;
          178                 next = output->next;
          179                 wl_output_destroy(output->output);
          180                 free(output);
          181         }
          182 }
          183 
          184 // Destroys the Wayland context, freeing memory associated with it.
          185 static void context_destroy(struct wl_context *context) {
          186         wl_registry_destroy(context->registry);
          187         wl_compositor_destroy(context->compositor);
          188         wl_shm_destroy(context->shm);
          189         wl_seat_destroy(context->seat);
          190         wl_data_device_manager_destroy(context->data_device_manager);
          191         zwlr_layer_shell_v1_destroy(context->layer_shell);
          192         free_outputs(context);
          193 
          194         free_keyboard(context->keyboard);
          195         wl_data_device_destroy(context->data_device);
          196         wl_surface_destroy(context->surface);
          197         zwlr_layer_surface_v1_destroy(context->layer_surface);
          198         xdg_activation_v1_destroy(context->activation);
          199 
          200         wl_display_disconnect(context->display);
          201         free(context);
          202 }
          203 
          204 static void noop() {
          205         // Do nothing
          206 }
          207 
          208 static void surface_enter(void *data, struct wl_surface *surface, struct wl_output *wl_output) {
          209         struct wl_context *context = data;
          210         context->output = wl_output_get_user_data(wl_output);
          211         menu_invalidate(context->menu);
          212 }
          213 
          214 static const struct wl_surface_listener surface_listener = {
          215         .enter = surface_enter,
          216         .leave = noop,
          217 };
          218 
          219 static void layer_surface_configure(void *data,
          220                 struct zwlr_layer_surface_v1 *surface,
          221                 uint32_t serial, uint32_t width, uint32_t height) {
          222         struct wl_context *context = data;
          223         if (context->menu->customwidth > 0) {
          224                 context->menu->width = context->menu->customwidth;
          225         } else {
          226                 context->menu->width = width;
          227         }
          228         context->menu->height = height;
          229         zwlr_layer_surface_v1_ack_configure(surface, serial);
          230 }
          231 
          232 static void layer_surface_closed(void *data, struct zwlr_layer_surface_v1 *surface) {
          233         struct wl_context *context = data;
          234         context->menu->exit = true;
          235 }
          236 
          237 static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
          238         .configure = layer_surface_configure,
          239         .closed = layer_surface_closed,
          240 };
          241 
          242 static void output_scale(void *data, struct wl_output *wl_output, int32_t factor) {
          243         struct output *output = data;
          244         output->scale = factor;
          245 }
          246 
          247 static void output_name(void *data, struct wl_output *wl_output, const char *name) {
          248         struct output *output = data;
          249         output->name = name;
          250 
          251         struct wl_context *context = output->context;
          252         if (context->menu->output_name && strcmp(context->menu->output_name, name) == 0) {
          253                 context->output = output;
          254         }
          255 }
          256 
          257 static const struct wl_output_listener output_listener = {
          258         .geometry = noop,
          259         .mode = noop,
          260         .done = noop,
          261         .scale = output_scale,
          262         .name = output_name,
          263         .description = noop,
          264 };
          265 
          266 static void keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
          267                 uint32_t format, int32_t fd, uint32_t size) {
          268         struct keyboard *keyboard = data;
          269         assert(format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1);
          270 
          271         char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
          272         assert(map_shm != MAP_FAILED);
          273 
          274         keyboard->keymap = xkb_keymap_new_from_string(keyboard->context,
          275                 map_shm, XKB_KEYMAP_FORMAT_TEXT_V1, 0);
          276         munmap(map_shm, size);
          277         close(fd);
          278 
          279         keyboard->state = xkb_state_new(keyboard->keymap);
          280 }
          281 
          282 static void keyboard_repeat(struct keyboard *keyboard) {
          283         menu_keypress(keyboard->menu, keyboard->repeat_key_state, keyboard->repeat_sym);
          284         struct itimerspec spec = { 0 };
          285         spec.it_value.tv_sec = keyboard->repeat_period / 1000;
          286         spec.it_value.tv_nsec = (keyboard->repeat_period % 1000) * 1000000l;
          287         timerfd_settime(keyboard->repeat_timer, 0, &spec, NULL);
          288 }
          289 
          290 static void keyboard_key(void *data, struct wl_keyboard *wl_keyboard,
          291                 uint32_t serial, uint32_t time, uint32_t key, uint32_t _key_state) {
          292         struct keyboard *keyboard = data;
          293 
          294         enum wl_keyboard_key_state key_state = _key_state;
          295         xkb_keysym_t sym = xkb_state_key_get_one_sym(keyboard->state, key + 8);
          296         menu_keypress(keyboard->menu, key_state, sym);
          297 
          298         if (key_state == WL_KEYBOARD_KEY_STATE_PRESSED && keyboard->repeat_period >= 0) {
          299                 keyboard->repeat_key_state = key_state;
          300                 keyboard->repeat_sym = sym;
          301 
          302                 struct itimerspec spec = { 0 };
          303                 spec.it_value.tv_sec = keyboard->repeat_delay / 1000;
          304                 spec.it_value.tv_nsec = (keyboard->repeat_delay % 1000) * 1000000l;
          305                 timerfd_settime(keyboard->repeat_timer, 0, &spec, NULL);
          306         } else if (key_state == WL_KEYBOARD_KEY_STATE_RELEASED) {
          307                 struct itimerspec spec = { 0 };
          308                 timerfd_settime(keyboard->repeat_timer, 0, &spec, NULL);
          309         }
          310 }
          311 
          312 static void keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
          313                 int32_t rate, int32_t delay) {
          314         struct keyboard *keyboard = data;
          315         keyboard->repeat_delay = delay;
          316         if (rate > 0) {
          317                 keyboard->repeat_period = 1000 / rate;
          318         } else {
          319                 keyboard->repeat_period = -1;
          320         }
          321 }
          322 
          323 static void keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard,
          324                 uint32_t serial, uint32_t mods_depressed,
          325                 uint32_t mods_latched, uint32_t mods_locked,
          326                 uint32_t group) {
          327         struct keyboard *keyboard = data;
          328         xkb_state_update_mask(keyboard->state, mods_depressed, mods_latched,
          329                         mods_locked, 0, 0, group);
          330 }
          331 
          332 static const struct wl_keyboard_listener keyboard_listener = {
          333         .keymap = keyboard_keymap,
          334         .enter = noop,
          335         .leave = noop,
          336         .key = keyboard_key,
          337         .modifiers = keyboard_modifiers,
          338         .repeat_info = keyboard_repeat_info,
          339 };
          340 
          341 static void seat_capabilities(void *data, struct wl_seat *seat,
          342                 enum wl_seat_capability caps) {
          343         struct wl_context *context = data;
          344         if (caps & WL_SEAT_CAPABILITY_KEYBOARD) {
          345                 struct wl_keyboard *wl_keyboard = wl_seat_get_keyboard(seat);
          346                 struct keyboard *keyboard = keyboard_create(context->menu, wl_keyboard);
          347                 wl_keyboard_add_listener(wl_keyboard, &keyboard_listener, keyboard);
          348                 context->keyboard = keyboard;
          349         }
          350 }
          351 
          352 static const struct wl_seat_listener seat_listener = {
          353         .capabilities = seat_capabilities,
          354         .name = noop,
          355 };
          356 
          357 static void data_device_selection(void *data, struct wl_data_device *data_device,
          358                 struct wl_data_offer *data_offer) {
          359         struct wl_context *context = data;
          360         context->data_offer = data_offer;
          361 }
          362 
          363 static const struct wl_data_device_listener data_device_listener = {
          364         .data_offer = noop,
          365         .enter = noop,
          366         .leave = noop,
          367         .motion = noop,
          368         .drop = noop,
          369         .selection = data_device_selection,
          370 };
          371 
          372 static void handle_global(void *data, struct wl_registry *registry,
          373                 uint32_t name, const char *interface, uint32_t version) {
          374         struct wl_context *context = data;
          375         if (strcmp(interface, wl_compositor_interface.name) == 0) {
          376                 context->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4);
          377         } else if (strcmp(interface, wl_shm_interface.name) == 0) {
          378                 context->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
          379         } else if (strcmp(interface, wl_seat_interface.name) == 0) {
          380                 context->seat = wl_registry_bind(registry, name, &wl_seat_interface, 4);
          381                 wl_seat_add_listener(context->seat, &seat_listener, data);
          382         } else if (strcmp(interface, wl_data_device_manager_interface.name) == 0) {
          383                 context->data_device_manager = wl_registry_bind(registry, name, &wl_data_device_manager_interface, 3);
          384         } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
          385                 context->layer_shell = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 1);
          386         } else if (strcmp(interface, wl_output_interface.name) == 0) {
          387                 struct wl_output *wl_output = wl_registry_bind(registry, name, &wl_output_interface, 4);
          388                 struct output *output = output_create(context, wl_output);
          389                 wl_output_set_user_data(wl_output, output);
          390                 wl_output_add_listener(wl_output, &output_listener, output);
          391                 context_add_output(context, output);
          392         } else if (strcmp(interface, xdg_activation_v1_interface.name) == 0) {
          393                 context->activation = wl_registry_bind(registry, name, &xdg_activation_v1_interface, 1);
          394         }
          395 }
          396 
          397 static const struct wl_registry_listener registry_listener = {
          398         .global = handle_global,
          399         .global_remove = noop,
          400 };
          401 
          402 // Connect to the Wayland display and run the menu.
          403 int menu_run(struct menu *menu) {
          404         struct wl_context *context = calloc(1, sizeof(struct wl_context));
          405         context->menu = menu;
          406         menu->context = context;
          407 
          408         context->display = wl_display_connect(NULL);
          409         if (!context->display) {
          410                 fprintf(stderr, "Failed to connect to display.\n");
          411                 exit(EXIT_FAILURE);
          412         }
          413 
          414         struct wl_registry *registry = wl_display_get_registry(context->display);
          415         wl_registry_add_listener(registry, &registry_listener, context);
          416         wl_display_roundtrip(context->display);
          417         assert(context->compositor != NULL);
          418         assert(context->shm != NULL);
          419         assert(context->seat != NULL);
          420         assert(context->data_device_manager != NULL);
          421         assert(context->layer_shell != NULL);
          422         assert(context->activation != NULL);
          423         context->registry = registry;
          424 
          425         // Get data device for seat
          426         struct wl_data_device *data_device = wl_data_device_manager_get_data_device(
          427                         context->data_device_manager, context->seat);
          428         wl_data_device_add_listener(data_device, &data_device_listener, context);
          429         context->data_device = data_device;
          430 
          431         // Second roundtrip for seat and output listeners
          432         wl_display_roundtrip(context->display);
          433         assert(context->keyboard != NULL);
          434 
          435         if (menu->output_name && !context->output) {
          436                 fprintf(stderr, "Output %s not found\n", menu->output_name);
          437                 exit(EXIT_FAILURE);
          438         }
          439 
          440         context->surface = wl_compositor_create_surface(context->compositor);
          441         wl_surface_add_listener(context->surface, &surface_listener, context);
          442 
          443         struct zwlr_layer_surface_v1 *layer_surface = zwlr_layer_shell_v1_get_layer_surface(
          444                 context->layer_shell,
          445                 context->surface,
          446                 context->output ? context->output->output : NULL,
          447                 ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
          448                 "menu"
          449         );
          450         assert(layer_surface != NULL);
          451         context->layer_surface = layer_surface;
          452 
          453         uint32_t anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
          454                 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
          455         if (menu->bottom) {
          456                 anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
          457         } else {
          458                 anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
          459         }
          460 
          461         zwlr_layer_surface_v1_set_anchor(layer_surface, anchor);
          462         zwlr_layer_surface_v1_set_size(layer_surface, 0, menu->height);
          463         zwlr_layer_surface_v1_set_exclusive_zone(layer_surface, -1);
          464         zwlr_layer_surface_v1_set_keyboard_interactivity(layer_surface, true);
          465         zwlr_layer_surface_v1_add_listener(layer_surface, &layer_surface_listener, context);
          466 
          467         wl_surface_commit(context->surface);
          468         wl_display_roundtrip(context->display);
          469         menu_render_items(menu);
          470 
          471         struct pollfd fds[] = {
          472                 { wl_display_get_fd(context->display), POLLIN },
          473                 { context->keyboard->repeat_timer, POLLIN },
          474         };
          475         const size_t nfds = sizeof(fds) / sizeof(*fds);
          476 
          477         while (!menu->exit) {
          478                 errno = 0;
          479                 do {
          480                         if (wl_display_flush(context->display) == -1 && errno != EAGAIN) {
          481                                 fprintf(stderr, "wl_display_flush: %s\n", strerror(errno));
          482                                 break;
          483                         }
          484                 } while (errno == EAGAIN);
          485 
          486                 if (poll(fds, nfds, -1) < 0) {
          487                         fprintf(stderr, "poll: %s\n", strerror(errno));
          488                         break;
          489                 }
          490 
          491                 if (fds[0].revents & POLLIN) {
          492                         if (wl_display_dispatch(context->display) < 0) {
          493                                 menu->exit = true;
          494                         }
          495                 }
          496 
          497                 if (fds[1].revents & POLLIN) {
          498                         keyboard_repeat(context->keyboard);
          499                 }
          500 
          501                 // Render the menu if necessary
          502                 if (!menu->rendered) {
          503                         render_menu(menu);
          504                 }
          505         }
          506 
          507         context_destroy(context);
          508         menu->context = NULL;
          509 
          510         if (menu->failure) {
          511                 return EXIT_FAILURE;
          512         }
          513         return EXIT_SUCCESS;
          514 }