tsp:kp.c - spkp - Stacking wayland compositor
(HTM) git clone git://git.z3bra.org/spkp.git
(DIR) Log
(DIR) Files
(DIR) Refs
---
tsp:kp.c (32874B)
---
1 #include <signal.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <time.h>
5 #include <unistd.h>
6
7 #include <sys/types.h>
8
9 #include <wayland-server.h>
10 #include <wlr/backend.h>
11 #include <wlr/render/wlr_renderer.h>
12 #include <wlr/types/wlr_compositor.h>
13 #include <wlr/types/wlr_cursor.h>
14 #include <wlr/types/wlr_data_control_v1.h>
15 #include <wlr/types/wlr_data_device.h>
16 #include <wlr/types/wlr_gamma_control_v1.h>
17 #include <wlr/types/wlr_idle.h>
18 #include <wlr/types/wlr_keyboard.h>
19 #include <wlr/types/wlr_matrix.h>
20 #include <wlr/types/wlr_output.h>
21 #include <wlr/types/wlr_output_layout.h>
22 #include <wlr/types/wlr_primary_selection_v1.h>
23 #include <wlr/types/wlr_server_decoration.h>
24 #include <wlr/types/wlr_screencopy_v1.h>
25 #include <wlr/types/wlr_xcursor_manager.h>
26 #include <wlr/types/wlr_xdg_decoration_v1.h>
27 #include <wlr/types/wlr_xdg_output_v1.h>
28 #include <wlr/types/wlr_xdg_shell.h>
29
30 #include <linux/input-event-codes.h>
31
32 #include "arg.h"
33
34 #define MAX(a,b) ((a)>(b)?(a):(b))
35 #define MIN(a,b) ((a)<(b)?(a):(b))
36 #define DELTA(a,b) ((a)>(b)?(a)-(b):(b)-(a))
37
38 #define CLEANMASK(mask) (mask & ~(WLR_MODIFIER_CAPS|WLR_MODIFIER_SHIFT))
39
40 enum {
41 NORMAL,
42 UNGRAB,
43 MOVE,
44 RESIZE,
45 };
46
47 /* Internal compositor state */
48 struct spkp {
49 struct wl_display *dpy;
50 struct wlr_backend *backend;
51 struct wlr_renderer *renderer;
52 struct wlr_xdg_shell *shell;
53 struct wlr_output_layout *layout;
54
55 struct wlr_seat *seat;
56
57 int desktop; /* wether desktop is visible */
58
59 struct {
60 int mode;
61 struct wlr_box box;
62 struct window *window;
63 } grab;
64
65 struct wlr_cursor *cursor;
66 struct wlr_xcursor_manager *cursor_mgr;
67 struct wlr_xdg_decoration_manager_v1 *chrome_mgr;
68
69 struct wl_listener new_input;
70 struct wl_listener new_output;
71 struct wl_listener new_window;
72 struct wl_listener new_chrome;
73 struct wl_listener copy;
74
75 /* cursor related events */
76 struct wl_listener req_cursor;
77 struct wl_listener motion_relative;
78 struct wl_listener motion_absolute;
79 struct wl_listener click;
80 struct wl_listener scroll;
81 struct wl_listener paint_cursor;
82
83 struct wl_list outputs;
84 struct wl_list windows;
85 struct wl_list keyboards;
86 };
87
88 /* Display output (usually a monitor) */
89 struct output {
90 struct spkp *server;
91 struct wlr_output *wlr_output;
92
93 struct wl_listener destroy;
94 struct wl_listener frame;
95 struct wl_listener window;
96
97 /* pointer to the next output */
98 struct wl_list link;
99 };
100
101 /* Client surface window */
102 struct window {
103 struct spkp *server;
104 struct wlr_surface *surface;
105 struct wlr_xdg_surface *xdg;
106 struct wlr_xdg_toplevel_decoration_v1 decoration;
107 double x, y;
108
109 double sx, sy;
110
111 struct wl_listener map;
112 struct wl_listener destroy;
113
114 /* pointer to the next client */
115 struct wl_list link;
116 };
117
118 struct keyboard {
119 struct spkp *server;
120 struct wlr_input_device *device;
121 struct wl_listener destroy;
122
123 struct wl_listener key;
124 struct wl_listener modifiers;
125
126 /* pointer to the next keyboard */
127 struct wl_list link;
128 };
129
130 struct chrome {
131 struct spkp *server;
132 struct wlr_xdg_toplevel_decoration_v1 *decoration;
133
134 struct wl_listener destroy;
135 struct wl_listener mode;
136 };
137
138 /* Optional argument to pass down to a keybinding function */
139 union keyarg {
140 int i;
141 float f;
142 const void *v;
143 };
144
145 /* Keybindig to function association, see config.h */
146 struct key {
147 uint32_t mod;
148 uint32_t key;
149 void (*func)(struct spkp *, union keyarg *);
150 union keyarg arg;
151 };
152
153 /* Rendering data, used to render window's surfaces */
154 struct rdata {
155 struct wlr_output *output;
156 struct window *window;
157 struct wlr_renderer *renderer;
158 struct timespec when;
159 };
160
161
162 static void usage(char *);
163
164 /* callback functions triggered when a new event occur */
165 static void cb_new_input(struct wl_listener *, void *);
166 static void cb_new_output(struct wl_listener *, void *);
167 static void cb_destroy_output(struct wl_listener *, void *);
168 static void cb_frame_output(struct wl_listener *, void *);
169 static void cb_new_window(struct wl_listener *, void *);
170 static void cb_new_decoration(struct wl_listener *, void *);
171 static void cb_copy(struct wl_listener *, void *);
172
173 static void cb_map_window(struct wl_listener *, void *);
174 static void cb_destroy_window(struct wl_listener *, void *);
175 static void cb_set_chrome(struct wl_listener *, void *);
176 static void cb_destroy_chrome(struct wl_listener *, void *);
177
178 static void cb_kb_mod(struct wl_listener *, void *);
179 static void cb_kb_key(struct wl_listener *, void *);
180
181 static void cb_motion(struct spkp *, uint32_t);
182 static void cb_motion_relative(struct wl_listener *, void *);
183 static void cb_motion_absolute(struct wl_listener *, void *);
184 static void cb_click(struct wl_listener *, void *);
185 static void cb_click_press(struct spkp *, struct wlr_event_pointer_button *);
186 static void cb_click_release(struct spkp *, struct wlr_event_pointer_button *);
187 static void cb_scroll(struct wl_listener *, void *);
188 static void cb_paint_cursor(struct wl_listener *, void *);
189
190 /* helper functions, used inside callbacks */
191 static void add_keyboard(struct spkp *, struct wlr_input_device *);
192 static void add_pointer(struct spkp *, struct wlr_input_device *);
193 static void render(struct wlr_surface *, int, int, void *);
194 static void render_border(struct wlr_box *, struct rdata *, float *);
195 static void focus(struct window *);
196 static void grab(struct window *, int, const char *);
197
198 static int dropprivilege();
199 static int keybinding(struct spkp *, uint32_t, uint32_t, enum wlr_key_state);
200 static struct window *underneath(struct spkp *, double, double);
201
202 /* keybinding functions, see config.h */
203 static void kb_terminate(struct spkp *, union keyarg *);
204 static void kb_alttab(struct spkp *, union keyarg *);
205 static void kb_exec(struct spkp *, union keyarg *);
206 static void kb_desktop(struct spkp *, union keyarg *);
207
208 #include "config.h"
209
210 void
211 usage(char *pgm)
212 {
213 fprintf(stderr, "usage: %s [-h]\n", pgm);
214 }
215
216 /*
217 * A new input device is available. Each new input type is added to the
218 * capabilities of the seat.
219 */
220 void
221 cb_new_input(struct wl_listener *listener, void *data)
222 {
223 uint32_t cap = 0;
224 struct spkp *server;
225 struct wlr_input_device *device;
226
227 server = wl_container_of(listener, server, new_input);
228 device = data;
229
230 cap = server->seat->capabilities;
231
232 switch(device->type) {
233 case WLR_INPUT_DEVICE_KEYBOARD:
234 cap |= WL_SEAT_CAPABILITY_KEYBOARD;
235 add_keyboard(server, device);
236 break;
237 case WLR_INPUT_DEVICE_POINTER:
238 cap |= WL_SEAT_CAPABILITY_POINTER;
239 add_pointer(server, device);
240 break;
241 default:
242 break;
243 }
244
245 wlr_seat_set_capabilities(server->seat, cap);
246 }
247
248 /*
249 * Triggered whenever a new output (eg, monitor) is connected to the
250 * compositor. This output is configured (set "mode" if needed) and
251 * added to the outputs list.
252 * Listeners are setup on this output for when stuff needs to get
253 * rendered, or when output is disconnected.
254 */
255 void
256 cb_new_output(struct wl_listener *listener, void *data)
257 {
258 struct spkp *server;
259 struct output *output;
260 struct wlr_output *wlr_output;
261 struct wlr_output_mode *mode;
262
263 server = wl_container_of(listener, server, new_output);
264 wlr_output = data;
265
266 /*
267 * Configure output mode when advertized. This is only needed
268 * for DRM backend, as other backends don't have such thing as an
269 * "output" or "mode". They simply run within a window, and thus
270 * have nothing to configure on their own.
271 */
272 if (!wl_list_empty(&wlr_output->modes)) {
273 mode = wl_container_of(wlr_output->modes.prev, mode, link);
274 wlr_output_set_mode(wlr_output, mode);
275 wlr_output_enable(wlr_output, true);
276 if (!wlr_output_commit(wlr_output))
277 return;
278 }
279
280 output = calloc(1, sizeof(*output));
281 output->server = server;
282 output->wlr_output = wlr_output;
283
284 wl_list_insert(&server->outputs, &output->link);
285
286 /* setup callbacks for our output */
287 output->destroy.notify = cb_destroy_output;
288 output->frame.notify = cb_frame_output;
289
290 /* setup signals for above notifies */
291 wl_signal_add(&wlr_output->events.destroy, &output->destroy);
292 wl_signal_add(&wlr_output->events.frame, &output->frame);
293
294 /* create global and arrange outputs from left to right */
295 wlr_output_layout_add_auto(server->layout, wlr_output);
296 }
297
298 /*
299 * Output gets disconnected. Remove it from the list and release memory.
300 */
301 void
302 cb_destroy_output(struct wl_listener *listener, void *data)
303 {
304 (void)data;
305 struct output *output;
306
307 output = wl_container_of(listener, output, destroy);
308
309 wl_list_remove(&output->link);
310 wl_list_remove(&output->destroy.link);
311 wl_list_remove(&output->frame.link);
312
313 free(output);
314 }
315
316 /*
317 * Output is displaying a frame. This would be called 60 times per
318 * seconds on a 60Hz monitor.
319 * Windows will be rendered on top of each others, starting from the
320 * bottom of the window list.
321 *
322 * TODO: damage tracking
323 */
324 void
325 cb_frame_output(struct wl_listener *listener, void *data)
326 {
327 struct spkp *server;
328 struct output *output;
329 struct window *window;
330 struct rdata rdata;
331 struct wlr_output *wlr_output;
332 struct wlr_renderer *renderer;
333
334 output = wl_container_of(listener, output, frame);
335 server = output->server;
336 renderer = output->server->renderer;
337 wlr_output = data;
338
339 if (!wlr_output->enabled)
340 return;
341
342 if (!wlr_output_attach_render(wlr_output, NULL))
343 return;
344
345 /* Update output resolution with current values */
346 wlr_output_effective_resolution(wlr_output, &wlr_output->width, &wlr_output->height);
347
348 /* Paint whole output surface with the background color */
349 wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
350 wlr_renderer_clear(renderer, background);
351
352 rdata.output = wlr_output;
353 rdata.renderer = renderer;
354
355 wl_list_for_each_reverse(window, &output->server->windows, link) {
356 if (!window->xdg->mapped)
357 continue;
358
359 rdata.window = window;
360 clock_gettime(CLOCK_MONOTONIC, &rdata.when);
361 wlr_xdg_surface_for_each_surface(window->xdg, render, &rdata);
362 }
363
364 if (server->grab.mode != NORMAL)
365 render_border(&server->grab.box, &rdata, drawcolor);
366
367 /* render cursor by software in case the GPU doesn't handle it */
368 wlr_output_render_software_cursors(wlr_output, NULL);
369
370 /* commit changes made to output */
371 wlr_renderer_end(renderer);
372 wlr_output_commit(wlr_output);
373 }
374
375 /*
376 * A new application window is created. Only top-level windows are
377 * supported and rendered.
378 *
379 * TODO: better window placement
380 * TODO: client size/position hint support
381 */
382 void
383 cb_new_window(struct wl_listener *listener, void *data)
384 {
385 struct spkp *server;
386 struct wlr_xdg_surface *surface;
387 struct window *window;
388
389 server = wl_container_of(listener, server, new_window);
390 surface = data;
391
392 /* only register top level windows */
393 if (surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL)
394 return;
395
396 window = calloc(1, sizeof(*window));
397 window->server = server;
398 window->xdg = surface;
399
400 window->x = -1;
401 window->y = -1;
402
403 window->map.notify = cb_map_window;
404 window->destroy.notify = cb_destroy_window;
405
406 /* have the client consider itself "tiled", to constrain it */
407 wlr_xdg_toplevel_set_tiled(window->xdg,
408 WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | WLR_EDGE_BOTTOM);
409
410
411 wl_signal_add(&surface->events.map, &window->map);
412 wl_signal_add(&surface->events.destroy, &window->destroy);
413
414 wl_list_insert(&server->windows, &window->link);
415 }
416
417 /*
418 * Top level window request to add decorations
419 */
420 void
421 cb_new_decoration(struct wl_listener *listener, void *data)
422 {
423 struct spkp *server;
424 struct chrome *chrome;
425 struct wlr_xdg_toplevel_decoration_v1 *decoration;
426
427 server = wl_container_of(listener, server, new_chrome);
428 decoration = data;
429
430 chrome = calloc(1, sizeof(*chrome));
431 if (!chrome)
432 return;
433
434 chrome->decoration = decoration;
435 chrome->server = server;
436
437 chrome->mode.notify = cb_set_chrome;
438 chrome->destroy.notify = cb_destroy_chrome;
439
440 wl_signal_add(&decoration->events.request_mode, &chrome->mode);
441 wl_signal_add(&decoration->events.destroy, &chrome->destroy);
442 }
443
444 /*
445 * Request setting selection from the client. This happens when the user
446 * copies something from a client window.
447 */
448 void
449 cb_copy(struct wl_listener *listener, void *data)
450 {
451 struct spkp *server;
452 struct wlr_seat_request_set_selection_event *ev;
453
454 server = wl_container_of(listener, server, copy);
455 ev = data;
456 wlr_seat_set_selection(server->seat, ev->source, ev->serial);
457 }
458
459 /*
460 * Request from a client to be mapped on-screen.
461 */
462 void
463 cb_map_window(struct wl_listener *listener, void *data)
464 {
465 (void)data;
466 int ow, oh;
467 struct window *w;
468 struct wlr_output *output;
469 struct spkp *server;
470 struct wlr_output_layout_output *layer;
471 struct wlr_box geom;
472
473 w = wl_container_of(listener, w, map);
474 server = w->server;
475
476 /*
477 * New windows are created with no specific position on
478 * screen. In this case, they're put in the middle of the output,
479 * and eventually resized if they're bigger than said output.
480 */
481 if (w->x < 0 || w->y < 0) {
482 output = wlr_output_layout_output_at(server->layout,
483 server->cursor->x, server->cursor->y);
484 layer = wlr_output_layout_get(server->layout, output);
485
486 wlr_output_effective_resolution(output, &ow, &oh);
487 wlr_xdg_surface_get_geometry(w->xdg, &geom);
488
489 if (geom.width > ow || geom.height > oh) {
490 geom.width = MIN(ow - 2 * bordersize, geom.width);
491 geom.height = MIN(oh - 2 * bordersize, geom.height);
492 wlr_xdg_toplevel_set_size(w->xdg, geom.width, geom.height);
493 }
494
495 w->x = layer->x + ow/2 - geom.width/2;
496 w->y = layer->y + oh/2 - geom.height/2;
497
498 focus(w);
499 }
500 }
501
502 /*
503 * Release all resources associated to a window.
504 */
505 void
506 cb_destroy_window(struct wl_listener *listener, void *data)
507 {
508 (void)data;
509 struct window *w;
510
511 w = wl_container_of(listener, w, destroy);
512 wl_list_remove(&w->link);
513 free(w);
514 }
515
516 /*
517 * Ask wether to use client-side or server-side decorations.
518 * We always to server-side.
519 */
520 void
521 cb_set_chrome(struct wl_listener *listener, void *data)
522 {
523 (void)data;
524 struct chrome *chrome;
525
526 chrome = wl_container_of(listener, chrome, mode);
527 wlr_xdg_toplevel_decoration_v1_set_mode(chrome->decoration,
528 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
529 }
530
531 /*
532 * Destroy chrome associated to a window
533 */
534 void
535 cb_destroy_chrome(struct wl_listener *listener, void *data)
536 {
537 (void)data;
538 struct chrome *chrome;
539
540 chrome = wl_container_of(listener, chrome, destroy);
541
542 wl_list_remove(&chrome->destroy.link);
543 wl_list_remove(&chrome->mode.link);
544 free(chrome);
545 }
546
547 /*
548 * A modifier key is pressed, or depressed.
549 */
550 void
551 cb_kb_mod(struct wl_listener *listener, void *data)
552 {
553 (void)data;
554 struct keyboard *kb;
555
556 kb = wl_container_of(listener, kb, modifiers);
557
558 wlr_seat_set_keyboard(kb->server->seat, kb->device);
559 wlr_seat_keyboard_notify_modifiers(kb->server->seat, &kb->device->keyboard->modifiers);
560 }
561
562 /*
563 * A key is pressed, or depressed.
564 */
565 void
566 cb_kb_key(struct wl_listener *listener, void *data)
567 {
568 struct spkp *server;
569 struct keyboard *kb;
570 struct wlr_event_keyboard_key *ev;
571 struct wlr_seat *seat;
572
573 int nsym;
574 uint32_t kc, mod;
575 xkb_layout_index_t layout;
576 struct xkb_keymap *keymap;
577 const xkb_keysym_t *syms;
578
579 ev = data;
580 kb = wl_container_of(listener, kb, key);
581 server = kb->server;
582 seat = server->seat;
583
584 /* translate libinput keycode to xkbcommon */
585 kc = ev->keycode + 8;
586
587 /* get keysym as if no modifier was held (level 0) */
588 keymap = xkb_state_get_keymap(kb->device->keyboard->xkb_state);
589 layout = xkb_state_key_get_layout(kb->device->keyboard->xkb_state, kc);
590 nsym = xkb_keymap_key_get_syms_by_level(keymap, kc, layout, 0, &syms);
591
592 mod = wlr_keyboard_get_modifiers(kb->device->keyboard);
593
594 for (int i = 0; i < nsym; i++) {
595 if (keybinding(server, mod, syms[i], ev->state))
596 continue;
597
598 /* pass it onto the underlying client */
599 wlr_seat_set_keyboard(seat, kb->device);
600 wlr_seat_keyboard_notify_key(seat, ev->time_msec, ev->keycode, ev->state);
601 }
602 }
603
604 /*
605 * Client request to change the cursor frame.
606 */
607 void
608 cb_req_cursor(struct wl_listener *listener, void *data)
609 {
610 struct spkp *server;
611 struct wlr_seat_pointer_request_set_cursor_event *ev;
612 struct wlr_seat_client *focus;
613
614 server = wl_container_of(listener, server, req_cursor);
615 ev = data;
616
617 focus = server->seat->pointer_state.focused_client;
618 if (focus == ev->seat_client)
619 wlr_cursor_set_surface(server->cursor, ev->surface, ev->hotspot_x, ev->hotspot_y);
620 }
621
622 /*
623 * Generic callback for pointer motion (relative or absolute).
624 * Depending on the server grab mode, windows will be moved or resized.
625 * If no grab mode is set, motion events are simply passed down to
626 * the client underneath the pointer.
627 */
628 void
629 cb_motion(struct spkp *server, uint32_t time)
630 {
631 struct window *w;
632 struct wlr_seat *seat;
633
634 seat = server->seat;
635
636 /*
637 * reset focus and cursor image when no window is under the
638 * pointer
639 */
640 switch(server->grab.mode) {
641 case MOVE:
642 server->grab.box.x = server->cursor->x - server->grab.window->sx;
643 server->grab.box.y = server->cursor->y - server->grab.window->sy;
644 break;
645 case RESIZE:
646 server->grab.box.width = MAX(server->cursor->x - server->grab.box.x, 1);
647 server->grab.box.height = MAX(server->cursor->y - server->grab.box.y, 1);
648 break;
649 case NORMAL:
650 wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
651 "left_ptr", server->cursor);
652 w = underneath(server, server->cursor->x, server->cursor->y);
653 if (!w) {
654 wlr_seat_pointer_clear_focus(seat);
655 return;
656 }
657
658 /* focus pointer when moving to a different surface */
659 if (seat->pointer_state.focused_surface != w->surface)
660 wlr_seat_pointer_notify_enter(seat, w->surface, w->sx, w->sy);
661
662 wlr_seat_pointer_notify_motion(seat, time, w->sx, w->sy);
663 }
664 }
665
666 /*
667 * Move the cursor according to input device request. This event is
668 * triggered when the cursor is moving from a relative offset from its
669 * current position.
670 */
671 void
672 cb_motion_relative(struct wl_listener *listener, void *data)
673 {
674 struct spkp *server;
675 struct wlr_event_pointer_motion *ev;
676
677 server = wl_container_of(listener, server, motion_relative);
678 ev = data;
679
680 wlr_cursor_move(server->cursor, ev->device, ev->delta_x, ev->delta_y);
681 cb_motion(server, ev->time_msec);
682 }
683
684 /*
685 * Move the cursor according to input device request. This event is
686 * triggered when the cursor is given an absolute position to move to.
687 */
688 void
689 cb_motion_absolute(struct wl_listener *listener, void *data)
690 {
691 struct spkp *server;
692 struct wlr_event_pointer_motion_absolute *ev;
693
694 server = wl_container_of(listener, server, motion_absolute);
695 ev = data;
696
697 wlr_cursor_warp_absolute(server->cursor, ev->device, ev->x, ev->y);
698 cb_motion(server, ev->time_msec);
699 }
700
701 /*
702 * A mouse button is pressed, or released. If the given modifiers are
703 * pressed wilst pressing the button, the underlying window (if any!) will
704 * be moved or resized.
705 * Any mouse event will be ignored until the button is released.
706 *
707 * TODO: refactor grab mode
708 */
709 void
710 cb_click(struct wl_listener *listener, void *data)
711 {
712 struct spkp *server;
713 struct wlr_event_pointer_button *ev;
714
715 server = wl_container_of(listener, server, click);
716 ev = data;
717
718 switch (ev->state) {
719 case WLR_BUTTON_RELEASED:
720 cb_click_release(server, ev);
721 break;
722 case WLR_BUTTON_PRESSED:
723 cb_click_press(server, ev);
724 break;
725 }
726 }
727
728 /*
729 * Mouse button was released. There are only 2 different use cases:
730 * either we where grabbing a window to move/resize it, or not.
731 * In the first case, it means the operation is over, so me must
732 * move/resize the window.
733 * When it's not, the event is simply passed down to the focused window.
734 */
735 void
736 cb_click_release(struct spkp *server, struct wlr_event_pointer_button *ev)
737 {
738 switch(server->grab.mode) {
739 case RESIZE:
740 wlr_xdg_toplevel_set_size(server->grab.window->xdg,
741 server->grab.box.width,
742 server->grab.box.height);
743 break;
744 case MOVE:
745 server->grab.window->x = server->grab.box.x;
746 server->grab.window->y = server->grab.box.y;
747 break;
748 default:
749 /* pass down event when not grabbing a window */
750 wlr_seat_pointer_notify_button(server->seat,
751 ev->time_msec, ev->button, ev->state);
752 return;
753 }
754
755 if (server->grab.mode != NORMAL)
756 grab(server->grab.window, UNGRAB, "left_ptr");
757 }
758
759 /*
760 * Mouse button was pressed. If the modifier key is held down, we enter
761 * the "grab mode", which will move and/or resize the clicked or focused
762 * window, and will terminate when the button is released.
763 */
764 void
765 cb_click_press(struct spkp *server, struct wlr_event_pointer_button *ev)
766 {
767 struct window *w;
768 struct wlr_keyboard *kb;
769
770 /* Ignore all press events when a window is grabbed */
771 if (server->grab.mode != NORMAL)
772 return;
773
774 kb = wlr_seat_get_keyboard(server->seat);
775
776 w = underneath(server, server->cursor->x, server->cursor->y);
777 if (!w)
778 return;
779
780 /* bring window to the front */
781 wl_list_remove(&w->link);
782 wl_list_insert(&server->windows, &w->link);
783
784 focus(w);
785
786 if (kb->modifiers.depressed != mousemod) {
787 wlr_seat_pointer_notify_button(server->seat,
788 ev->time_msec, ev->button, ev->state);
789
790 return;
791 }
792
793 switch (ev->button) {
794 case BTN_LEFT:
795 grab(w, MOVE, "hand2");
796 break;
797 case BTN_RIGHT:
798 grab(w, RESIZE, "bottom_right_corner");
799 break;
800 }
801 }
802
803 /*
804 * Mouse scroll. Passed down to the underlying client.
805 */
806 void
807 cb_scroll(struct wl_listener *listener, void *data)
808 {
809 struct spkp *server;
810 struct wlr_event_pointer_axis *ev;
811
812
813 server = wl_container_of(listener, server, scroll);
814 ev = data;
815
816 /* Ignore all scrolling events when a window is grabbed */
817 if (server->grab.mode != NORMAL)
818 return;
819
820 wlr_seat_pointer_notify_axis(server->seat, ev->time_msec,
821 ev->orientation, ev->delta, ev->delta_discrete, ev->source);
822 }
823
824 /*
825 * Request to re-paint the cursor.
826 */
827 void
828 cb_paint_cursor(struct wl_listener *listener, void *data)
829 {
830 (void)data;
831 struct spkp *server;
832
833 server = wl_container_of(listener, server, paint_cursor);
834
835 wlr_seat_pointer_notify_frame(server->seat);
836 }
837
838 /*
839 * Configure a newly added keyboard.
840 */
841 void
842 add_keyboard(struct spkp *server, struct wlr_input_device *device)
843 {
844 struct keyboard *kb;
845 struct xkb_context *context;
846 struct xkb_keymap *keymap;
847 struct xkb_rule_names rules = { 0 };
848
849 kb = calloc(1, sizeof(*kb));
850
851 kb->server = server;
852 kb->device = device;
853
854 rules.rules = getenv("XKB_DEFAULT_RULES");
855 rules.model = getenv("XKB_DEFAULT_MODEL");
856 rules.layout = getenv("XKB_DEFAULT_LAYOUT");
857 rules.variant = getenv("XKB_DEFAULT_VARIANT");
858 rules.options = getenv("XKB_DEFAULT_OPTIONS");
859
860 context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
861 keymap = xkb_map_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
862
863 wlr_keyboard_set_keymap(device->keyboard, keymap);
864 xkb_keymap_unref(keymap);
865 xkb_context_unref(context);
866 wlr_keyboard_set_repeat_info(device->keyboard, 25, 600);
867
868 kb->key.notify = cb_kb_key;
869 kb->modifiers.notify = cb_kb_mod;
870
871 wl_signal_add(&device->keyboard->events.key, &kb->key);
872 wl_signal_add(&device->keyboard->events.modifiers, &kb->modifiers);
873
874 wlr_seat_set_keyboard(server->seat, device);
875
876 wl_list_insert(&server->keyboards, &kb->link);
877 }
878
879 /*
880 * Configure a newly added pointer device.
881 */
882 void
883 add_pointer(struct spkp *server, struct wlr_input_device *device)
884 {
885 /* no special configuration required (eg, accel, …) */
886 wlr_cursor_attach_input_device(server->cursor, device);
887 }
888
889 /*
890 * Render a full surface on-screen, at the given position.
891 */
892 void
893 render(struct wlr_surface *surface, int x, int y, void *data)
894 {
895 float matrix[9];
896 struct rdata *rdata;
897 struct window *window;
898 struct wlr_output *output;
899 struct wlr_surface *focus;
900 struct wlr_texture *texture;
901 struct wlr_box box;
902 enum wl_output_transform transform;
903
904 rdata = data;
905 window = rdata->window;
906 output = rdata->output;
907
908 texture = wlr_surface_get_texture(surface);
909 if (!texture)
910 return;
911
912 transform = wlr_output_transform_invert(surface->current.transform);
913
914 box.x = window->x + x;
915 box.y = window->y + y;
916 box.width = surface->current.width;
917 box.height = surface->current.height;
918
919 focus = window->server->seat->keyboard_state.focused_surface;
920 if (surface == window->xdg->surface)
921 render_border(&box, rdata, (surface == focus) ? activecolor : bordercolor);
922
923 wlr_matrix_project_box(matrix, &box, transform, 0, output->transform_matrix);
924 wlr_render_texture_with_matrix(rdata->renderer, texture, matrix, 1);
925 wlr_surface_send_frame_done(surface, &rdata->when);
926 }
927
928 void
929 render_border(struct wlr_box *content, struct rdata *rdata, float *color)
930 {
931 struct wlr_box box;
932
933 /* top */
934 box.x = content->x - bordersize;
935 box.y = content->y - bordersize;
936 box.width = bordersize * 2 + content->width;
937 box.height = bordersize;
938 wlr_render_rect(rdata->renderer, &box, color, rdata->output->transform_matrix);
939
940 /* bottom */
941 box.x = content->x - bordersize;
942 box.y = content->y + content->height;
943 box.width = bordersize * 2 + content->width;
944 box.height = bordersize;
945 wlr_render_rect(rdata->renderer, &box, color, rdata->output->transform_matrix);
946
947 /* left */
948 box.x = content->x - bordersize;
949 box.y = content->y;
950 box.width = bordersize;
951 box.height = content->height;
952 wlr_render_rect(rdata->renderer, &box, color, rdata->output->transform_matrix);
953
954 /* right */
955 box.x = content->x + content->width;
956 box.y = content->y;
957 box.width = bordersize;
958 box.height = content->height;
959 wlr_render_rect(rdata->renderer, &box, color, rdata->output->transform_matrix);
960 }
961
962 /*
963 * Set keyboard focus on the given top-level window.
964 */
965 void
966 focus(struct window *window)
967 {
968 struct spkp *server;
969 struct wlr_seat *seat;
970 struct wlr_keyboard *kb;
971 struct wlr_surface *focus;
972 struct wlr_xdg_surface *toplevel;
973
974 server = window->server;
975 seat = server->seat;
976 kb = wlr_seat_get_keyboard(seat);
977 focus = seat->keyboard_state.focused_surface;
978
979 /* skip if window is already focused */
980 if (focus == window->surface)
981 return;
982
983 /* deactivate currently focused window */
984 if (focus) {
985 toplevel = wlr_xdg_surface_from_wlr_surface(focus);
986 wlr_xdg_toplevel_set_activated(toplevel, false);
987 }
988
989 /* give keyboard focus to the toplevel window */
990 wlr_seat_keyboard_notify_enter(seat, window->xdg->surface,
991 kb->keycodes, kb->num_keycodes, &kb->modifiers);
992 }
993
994 void
995 grab(struct window *window, int mode, const char *cursor)
996 {
997 struct spkp *server;
998
999 server = window->server;
1000
1001 if (mode == UNGRAB) {
1002 server->grab.mode = NORMAL;
1003 server->grab.window = NULL;
1004 wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
1005 cursor, server->cursor);
1006 return;
1007 }
1008
1009 if (server->grab.mode != NORMAL)
1010 return;
1011
1012 server->grab.mode = mode;
1013 server->grab.window = window;
1014 wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
1015 cursor, server->cursor);
1016
1017 switch (mode) {
1018 case MOVE:
1019 wlr_xdg_surface_get_geometry(window->xdg, &(server->grab.box));
1020 server->grab.box.x = window->x;
1021 server->grab.box.y = window->y;
1022 break;
1023 case RESIZE:
1024 server->grab.box.x = window->x;
1025 server->grab.box.y = window->y;
1026 break;
1027 }
1028 }
1029
1030 /*
1031 * Drop current privileges to run as current user.
1032 */
1033 int
1034 dropprivilege()
1035 {
1036 if (getuid() == geteuid() && getgid() == getegid())
1037 return 1;
1038
1039 if (!setgid(getgid()) && !setuid(getuid()))
1040 return 1;
1041
1042 return 0;
1043 }
1044
1045
1046 /*
1047 * Execute specific functions when an modifier/key combination is pressed.
1048 */
1049 int
1050 keybinding(struct spkp *server, uint32_t mod, uint32_t key, enum wlr_key_state state)
1051 {
1052 ssize_t i, nkey;
1053
1054 nkey = sizeof(keys)/sizeof(*keys);
1055
1056 for (i=0; i<nkey; i++) {
1057 if (keys[i].mod == CLEANMASK(mod) && keys[i].key == key) {
1058 if (state == WLR_KEY_PRESSED)
1059 keys[i].func(server, &(keys[i].arg));
1060
1061 return 1;
1062 }
1063 }
1064
1065 return 0;
1066 }
1067
1068 /*
1069 * Returns a pointer to the toplevel window at the given position.
1070 * If a toplevel window is found, the topmost surface pertaining to this
1071 * toplevel window will be saved, as well as the pointer coordinates
1072 * relative to that surface.
1073 * These informations are needed to relay pointer events to the window.
1074 */
1075 struct window *
1076 underneath(struct spkp *server, double x, double y)
1077 {
1078 struct window *w;
1079
1080 wl_list_for_each(w, &server->windows, link) {
1081 /*
1082 * retrieve the subsurface at the given coordinates.
1083 * coordinates are given relatively to the top level
1084 * window coordinates on the output. Subsurfaces can
1085 * however exist outside the bounds of the top level
1086 * window, and thus have negative coordinates for example
1087 *
1088 * sx, sy will be updated with the pointer coordinates
1089 * within the subsurface (always positive).
1090 */
1091 w->surface = wlr_xdg_surface_surface_at(w->xdg,
1092 x - w->x, y - w->y, &w->sx, &w->sy);
1093
1094 if (w->surface)
1095 return w;
1096 }
1097
1098 return NULL;
1099 }
1100
1101 /*
1102 * Keybind: Terminate the wayland compositor
1103 */
1104 void
1105 kb_terminate(struct spkp *server, union keyarg *arg)
1106 {
1107 (void)arg;
1108 wl_display_terminate(server->dpy);
1109 }
1110
1111 /*
1112 * Cycle windows.
1113 */
1114 void
1115 kb_alttab(struct spkp *server, union keyarg *arg)
1116 {
1117 int reverse;
1118 struct window *w;
1119 struct wl_list *list, *elm;
1120
1121 if (wl_list_empty(&server->windows))
1122 return;
1123
1124 reverse = arg->i;
1125
1126 list = &server->windows;
1127 elm = list->prev;
1128
1129 if (reverse) {
1130 elm = list->next;
1131 list = list->prev;
1132 }
1133
1134 wl_list_remove(elm);
1135 wl_list_insert(list, elm);
1136
1137 w = wl_container_of(elm, w, link);
1138
1139 focus(w);
1140 }
1141
1142 /*
1143 * Run the given command
1144 */
1145 void
1146 kb_exec(struct spkp *server, union keyarg *arg)
1147 {
1148 (void)server;
1149 if (!fork()) {
1150 setsid();
1151 sigset_t set;
1152 sigemptyset(&set);
1153 sigprocmask(SIG_SETMASK, &set, NULL);
1154 close(1);
1155 close(2);
1156 execl("/bin/sh", "/bin/sh", "-c", (char *)arg->v, (void *)NULL);
1157 exit(1); /* NOTREACHED */
1158 }
1159 }
1160
1161 /*
1162 * Hide/show all windows on the desktop
1163 */
1164 void
1165 kb_desktop(struct spkp *server, union keyarg *arg)
1166 {
1167 (void)arg;
1168 struct window *w;
1169
1170 server->desktop = !server->desktop;
1171 wl_list_for_each(w, &server->windows, link)
1172 w->xdg->mapped = !server->desktop;
1173
1174 }
1175
1176 int
1177 main(int argc, char *argv[])
1178 {
1179 char *argv0;
1180 const char *socket;
1181 struct spkp server;
1182 struct wlr_server_decoration_manager *decorations;
1183
1184 ARGBEGIN {
1185 case 'h':
1186 default:
1187 usage(argv0);
1188 return -1;
1189 break; /* NOTREACHED */
1190 } ARGEND;
1191
1192 /*
1193 * create server side resources
1194 */
1195 server.dpy = wl_display_create();
1196 server.backend = wlr_backend_autocreate(server.dpy, NULL);
1197
1198 if (!dropprivilege())
1199 return -1;
1200
1201 server.seat = wlr_seat_create(server.dpy, "seat0");
1202 server.shell = wlr_xdg_shell_create(server.dpy);
1203 server.layout = wlr_output_layout_create();
1204 server.renderer = wlr_backend_get_renderer(server.backend);
1205 server.chrome_mgr = wlr_xdg_decoration_manager_v1_create(server.dpy);
1206
1207 server.cursor = wlr_cursor_create();
1208 server.cursor_mgr = wlr_xcursor_manager_create(NULL, 24);
1209 server.grab.mode = NORMAL;
1210
1211 wl_list_init(&server.outputs);
1212 wl_list_init(&server.windows);
1213 wl_list_init(&server.keyboards);
1214
1215 wl_display_init_shm(server.dpy);
1216 wlr_renderer_init_wl_display(server.renderer, server.dpy);
1217
1218 wlr_xcursor_manager_load(server.cursor_mgr, 1);
1219 wlr_cursor_attach_output_layout(server.cursor, server.layout);
1220
1221 wlr_compositor_create(server.dpy, server.renderer);
1222 wlr_data_device_manager_create(server.dpy);
1223
1224 wlr_gamma_control_manager_v1_create(server.dpy);
1225 wlr_screencopy_manager_v1_create(server.dpy);
1226 wlr_primary_selection_v1_device_manager_create(server.dpy);
1227 wlr_xdg_output_manager_v1_create(server.dpy, server.layout);
1228 wlr_idle_create(server.dpy);
1229
1230 /*
1231 * Legacy protocol to negotiate decorations. Still used by GTK…
1232 */
1233 decorations = wlr_server_decoration_manager_create(server.dpy);
1234 wlr_server_decoration_manager_set_default_mode(decorations,
1235 WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
1236
1237 /*
1238 * Setup callbacks for server-side events
1239 */
1240 server.new_window.notify = cb_new_window;
1241 server.new_input.notify = cb_new_input;
1242 server.new_output.notify = cb_new_output;
1243 server.new_chrome.notify = cb_new_decoration;
1244 server.copy.notify = cb_copy;
1245
1246 server.click.notify = cb_click;
1247 server.scroll.notify = cb_scroll;
1248 server.paint_cursor.notify = cb_paint_cursor;
1249 server.motion_relative.notify = cb_motion_relative;
1250 server.motion_absolute.notify = cb_motion_absolute;
1251 server.req_cursor.notify = cb_req_cursor;
1252
1253 wl_signal_add(&server.backend->events.new_output, &server.new_output);
1254 wl_signal_add(&server.shell->events.new_surface, &server.new_window);
1255 wl_signal_add(&server.backend->events.new_input, &server.new_input);
1256 wl_signal_add(&server.chrome_mgr->events.new_toplevel_decoration, &server.new_chrome);
1257 wl_signal_add(&server.seat->events.request_set_selection, &server.copy);
1258
1259 wl_signal_add(&server.cursor->events.button, &server.click);
1260 wl_signal_add(&server.cursor->events.axis, &server.scroll);
1261 wl_signal_add(&server.cursor->events.frame, &server.paint_cursor);
1262 wl_signal_add(&server.cursor->events.motion, &server.motion_relative);
1263 wl_signal_add(&server.cursor->events.motion_absolute, &server.motion_absolute);
1264 wl_signal_add(&server.seat->events.request_set_cursor, &server.req_cursor);
1265
1266 socket = wl_display_add_socket_auto(server.dpy);
1267 setenv("WAYLAND_DISPLAY", socket, 1);
1268
1269 if (!wlr_backend_start(server.backend)) {
1270 fprintf(stderr, "Failed to start backend\n");
1271 wlr_backend_destroy(server.backend);
1272 wl_display_destroy(server.dpy);
1273 return -1;
1274 }
1275
1276 /* all these interfaces are managed internally by wlroots */
1277 /* trigger internal event loop */
1278 wl_display_run(server.dpy);
1279 wl_display_destroy_clients(server.dpy);
1280 wl_display_destroy(server.dpy);
1281
1282 return 0;
1283 }