main.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. // Copyright 2019-2020 Inaban Authors <https://hacktivis.me/git/inaban>
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. // Based on wlroots's TinyWL which is distributed under CC0
  4. #include "inaban.h"
  5. #include "layers.h"
  6. #include "config.h"
  7. #include <getopt.h>
  8. #include <signal.h> /* signal(), SIGTERM */
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <time.h>
  12. #include <unistd.h> /* execvp() */
  13. #include <unistd.h>
  14. #define LENGTH(X) (sizeof X / sizeof X[0])
  15. struct inaban_server server = {0};
  16. void
  17. spawn(const Arg *arg)
  18. {
  19. if(fork() == 0)
  20. {
  21. setsid();
  22. execvp(((char **)arg->v)[0], (char **)arg->v);
  23. fprintf(stderr, "execvp %s", ((char **)arg->v)[0]);
  24. perror(" failed");
  25. exit(EXIT_SUCCESS);
  26. }
  27. }
  28. static void
  29. focus_view(struct inaban_view *view, struct wlr_surface *surface)
  30. {
  31. /* Note: this function only deals with keyboard focus. */
  32. if(view == NULL) return;
  33. struct inaban_server *server = view->server;
  34. struct wlr_seat *seat = server->seat;
  35. struct wlr_surface *prev_surface = seat->keyboard_state.focused_surface;
  36. if(prev_surface == surface) return; /* Don't re-focus an already focused surface. */
  37. if(prev_surface)
  38. {
  39. /*
  40. * Deactivate the previously focused surface. This lets the client know
  41. * it no longer has focus and the client will repaint accordingly, e.g.
  42. * stop displaying a caret.
  43. */
  44. struct wlr_xdg_surface *previous =
  45. wlr_xdg_surface_from_wlr_surface(seat->keyboard_state.focused_surface);
  46. wlr_xdg_toplevel_set_activated(previous, false);
  47. }
  48. struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat);
  49. /* Move the view to the front */
  50. wl_list_remove(&view->link);
  51. wl_list_insert(&server->views, &view->link);
  52. /* Activate the new surface */
  53. wlr_xdg_toplevel_set_activated(view->xdg_surface, true);
  54. /*
  55. * Tell the seat to have the keyboard enter this surface. wlroots will keep
  56. * track of this and automatically send key events to the appropriate
  57. * clients without additional work on your part.
  58. */
  59. wlr_seat_keyboard_notify_enter(seat,
  60. view->xdg_surface->surface,
  61. keyboard->keycodes,
  62. keyboard->num_keycodes,
  63. &keyboard->modifiers);
  64. }
  65. /* This event is raised when a modifier key, such as shift or alt, is pressed.
  66. * We simply communicate this to the client. */
  67. static void
  68. keyboard_handle_modifiers(struct wl_listener *listener, void *data)
  69. {
  70. (void)data;
  71. struct inaban_keyboard *keyboard = wl_container_of(listener, keyboard, modifiers);
  72. /*
  73. * A seat can only have one keyboard, but this is a limitation of the
  74. * Wayland protocol - not wlroots. We assign all connected keyboards to the
  75. * same seat. You can swap out the underlying wlr_keyboard like this and
  76. * wlr_seat handles this transparently.
  77. */
  78. wlr_seat_set_keyboard(keyboard->server->seat, keyboard->device);
  79. /* Send modifiers to the client. */
  80. wlr_seat_keyboard_notify_modifiers(keyboard->server->seat,
  81. &keyboard->device->keyboard->modifiers);
  82. }
  83. /* This event is raised when a key is pressed or released. */
  84. static void
  85. keyboard_handle_key(struct wl_listener *listener, void *data)
  86. {
  87. struct inaban_keyboard *keyboard = wl_container_of(listener, keyboard, key);
  88. struct inaban_server *server = keyboard->server;
  89. struct wlr_event_keyboard_key *event = data;
  90. struct wlr_seat *seat = server->seat;
  91. /* Translate libinput keycode -> xkbcommon */
  92. uint32_t keycode = event->keycode + 8;
  93. xkb_keysym_t keysym = xkb_state_key_get_one_sym(keyboard->device->keyboard->xkb_state, keycode);
  94. bool handled = false;
  95. uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->device->keyboard);
  96. if(event->state == WLR_KEY_PRESSED)
  97. {
  98. wlr_log(WLR_DEBUG,
  99. "key_pressed: {modifiers: %x, keycode: %x, keysym: %x}",
  100. modifiers,
  101. keycode,
  102. keysym);
  103. for(size_t i = 0; i < LENGTH(shortcuts); i++)
  104. if((keysym == shortcuts[i].keysym) && (modifiers == shortcuts[i].mod) && shortcuts[i].func)
  105. {
  106. shortcuts[i].func(&(shortcuts[i].arg));
  107. handled = true;
  108. }
  109. }
  110. /* Otherwise, we pass it along to the client. */
  111. if(!handled)
  112. {
  113. wlr_seat_set_keyboard(seat, keyboard->device);
  114. wlr_seat_keyboard_notify_key(seat, event->time_msec, event->keycode, event->state);
  115. }
  116. }
  117. static void
  118. server_new_keyboard(struct inaban_server *server, struct wlr_input_device *device)
  119. {
  120. struct inaban_keyboard *keyboard = calloc(1, sizeof(struct inaban_keyboard));
  121. keyboard->server = server;
  122. keyboard->device = device;
  123. /* We need to prepare an XKB keymap and assign it to the keyboard. This
  124. * assumes the defaults (e.g. layout = "us"). */
  125. struct xkb_rule_names rules = {0};
  126. struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
  127. struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
  128. wlr_keyboard_set_keymap(device->keyboard, keymap);
  129. xkb_keymap_unref(keymap);
  130. xkb_context_unref(context);
  131. wlr_keyboard_set_repeat_info(device->keyboard, 25, 600);
  132. /* Here we set up listeners for keyboard events. */
  133. keyboard->modifiers.notify = keyboard_handle_modifiers;
  134. wl_signal_add(&device->keyboard->events.modifiers, &keyboard->modifiers);
  135. keyboard->key.notify = keyboard_handle_key;
  136. wl_signal_add(&device->keyboard->events.key, &keyboard->key);
  137. wlr_seat_set_keyboard(server->seat, device);
  138. /* And add the keyboard to our list of keyboards */
  139. wl_list_insert(&server->keyboards, &keyboard->link);
  140. }
  141. static void
  142. server_new_pointer(struct inaban_server *server, struct wlr_input_device *device)
  143. {
  144. /* We don't do anything special with pointers. All of our pointer handling
  145. * is proxied through wlr_cursor. On another compositor, you might take this
  146. * opportunity to do libinput configuration on the device to set
  147. * acceleration, etc. */
  148. wlr_cursor_attach_input_device(server->cursor, device);
  149. }
  150. static void
  151. server_new_input(struct wl_listener *listener, void *data)
  152. {
  153. /* This event is raised by the backend when a new input device becomes
  154. * available. */
  155. struct inaban_server *server = wl_container_of(listener, server, new_input);
  156. struct wlr_input_device *device = data;
  157. switch(device->type)
  158. {
  159. case WLR_INPUT_DEVICE_KEYBOARD: server_new_keyboard(server, device); break;
  160. case WLR_INPUT_DEVICE_POINTER: server_new_pointer(server, device); break;
  161. default: break;
  162. }
  163. /* We need to let the wlr_seat know what our capabilities are, which is
  164. * communiciated to the client. In TinyWL we always have a cursor, even if
  165. * there are no pointer devices, so we always include that capability. */
  166. uint32_t caps = WL_SEAT_CAPABILITY_POINTER;
  167. if(!wl_list_empty(&server->keyboards)) caps |= WL_SEAT_CAPABILITY_KEYBOARD;
  168. wlr_seat_set_capabilities(server->seat, caps);
  169. }
  170. static void
  171. seat_request_cursor(struct wl_listener *listener, void *data)
  172. {
  173. struct inaban_server *server = wl_container_of(listener, server, request_cursor);
  174. /* This event is rasied by the seat when a client provides a cursor image */
  175. struct wlr_seat_pointer_request_set_cursor_event *event = data;
  176. struct wlr_seat_client *focused_client = server->seat->pointer_state.focused_client;
  177. /* This can be sent by any client, so we check to make sure this one is
  178. * actually has pointer focus first. */
  179. /* Once we've vetted the client, we can tell the cursor to use the
  180. * provided surface as the cursor image. It will set the hardware cursor
  181. * on the output that it's currently on and continue to do so as the
  182. * cursor moves between outputs. */
  183. if(focused_client == event->seat_client)
  184. wlr_cursor_set_surface(server->cursor, event->surface, event->hotspot_x, event->hotspot_y);
  185. }
  186. static bool
  187. view_at(struct inaban_view *view,
  188. double lx,
  189. double ly,
  190. struct wlr_surface **surface,
  191. double *sx,
  192. double *sy)
  193. {
  194. /*
  195. * XDG toplevels may have nested surfaces, such as popup windows for context
  196. * menus or tooltips. This function tests if any of those are underneath the
  197. * coordinates lx and ly (in output Layout Coordinates). If so, it sets the
  198. * surface pointer to that wlr_surface and the sx and sy coordinates to the
  199. * coordinates relative to that surface's top-left corner.
  200. */
  201. double view_sx = lx - view->x;
  202. double view_sy = ly - view->y;
  203. double _sx, _sy;
  204. struct wlr_surface *_surface = NULL;
  205. _surface = wlr_xdg_surface_surface_at(view->xdg_surface, view_sx, view_sy, &_sx, &_sy);
  206. if(_surface != NULL)
  207. {
  208. *sx = _sx;
  209. *sy = _sy;
  210. *surface = _surface;
  211. return true;
  212. }
  213. return false;
  214. }
  215. static struct inaban_view *
  216. desktop_view_at(struct inaban_server *server,
  217. double lx,
  218. double ly,
  219. struct wlr_surface **surface,
  220. double *sx,
  221. double *sy)
  222. {
  223. /* This iterates over all of our surfaces and attempts to find one under the
  224. * cursor. This relies on server->views being ordered from top-to-bottom. */
  225. struct inaban_view *view;
  226. wl_list_for_each(view, &server->views, link)
  227. {
  228. if(view_at(view, lx, ly, surface, sx, sy)) return view;
  229. }
  230. return NULL;
  231. }
  232. static void
  233. process_cursor_motion(struct inaban_server *server, uint32_t time)
  234. {
  235. /* Otherwise, find the view under the pointer and send the event along. */
  236. double sx, sy;
  237. struct wlr_seat *seat = server->seat;
  238. struct wlr_surface *surface = NULL;
  239. struct inaban_view *view =
  240. desktop_view_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy);
  241. /* If there's no view under the cursor, set the cursor image to a
  242. * default. This is what makes the cursor image appear when you move it
  243. * around the screen, not over any views. */
  244. if(!view) wlr_xcursor_manager_set_cursor_image(server->cursor_mgr, "left_ptr", server->cursor);
  245. if(surface)
  246. {
  247. bool focus_changed = seat->pointer_state.focused_surface != surface;
  248. /*
  249. * "Enter" the surface if necessary. This lets the client know that the
  250. * cursor has entered one of its surfaces.
  251. *
  252. * Note that this gives the surface "pointer focus", which is distinct
  253. * from keyboard focus. You get pointer focus by moving the pointer over
  254. * a window.
  255. */
  256. wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
  257. /* The enter event contains coordinates, so we only need to notify
  258. * on motion if the focus did not change. */
  259. if(!focus_changed) wlr_seat_pointer_notify_motion(seat, time, sx, sy);
  260. }
  261. else
  262. {
  263. /* Clear pointer focus so future button events and such are not sent to
  264. * the last client to have the cursor over it. */
  265. wlr_seat_pointer_clear_focus(seat);
  266. }
  267. }
  268. static void
  269. server_cursor_motion(struct wl_listener *listener, void *data)
  270. {
  271. /* This event is forwarded by the cursor when a pointer emits a _relative_
  272. * pointer motion event (i.e. a delta) */
  273. struct inaban_server *server = wl_container_of(listener, server, cursor_motion);
  274. struct wlr_event_pointer_motion *event = data;
  275. /* The cursor doesn't move unless we tell it to. The cursor automatically
  276. * handles constraining the motion to the output layout, as well as any
  277. * special configuration applied for the specific input device which
  278. * generated the event. You can pass NULL for the device if you want to move
  279. * the cursor around without any input. */
  280. wlr_cursor_move(server->cursor, event->device, event->delta_x, event->delta_y);
  281. process_cursor_motion(server, event->time_msec);
  282. }
  283. static void
  284. server_cursor_motion_absolute(struct wl_listener *listener, void *data)
  285. {
  286. /* This event is forwarded by the cursor when a pointer emits an _absolute_
  287. * motion event, from 0..1 on each axis. This happens, for example, when
  288. * wlroots is running under a Wayland window rather than KMS+DRM, and you
  289. * move the mouse over the window. You could enter the window from any edge,
  290. * so we have to warp the mouse there. There is also some hardware which
  291. * emits these events. */
  292. struct inaban_server *server = wl_container_of(listener, server, cursor_motion_absolute);
  293. struct wlr_event_pointer_motion_absolute *event = data;
  294. wlr_cursor_warp_absolute(server->cursor, event->device, event->x, event->y);
  295. process_cursor_motion(server, event->time_msec);
  296. }
  297. static void
  298. server_cursor_button(struct wl_listener *listener, void *data)
  299. {
  300. /* This event is forwarded by the cursor when a pointer emits a button
  301. * event. */
  302. struct inaban_server *server = wl_container_of(listener, server, cursor_button);
  303. struct wlr_event_pointer_button *event = data;
  304. /* Notify the client with pointer focus that a button press has occurred */
  305. wlr_seat_pointer_notify_button(server->seat, event->time_msec, event->button, event->state);
  306. double sx, sy;
  307. struct wlr_surface *surface;
  308. struct inaban_view *view =
  309. desktop_view_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy);
  310. focus_view(view, surface); /* Focus that client if the button was _pressed_ */
  311. }
  312. static void
  313. server_cursor_axis(struct wl_listener *listener, void *data)
  314. {
  315. /* This event is forwarded by the cursor when a pointer emits an axis event,
  316. * for example when you move the scroll wheel. */
  317. struct inaban_server *server = wl_container_of(listener, server, cursor_axis);
  318. struct wlr_event_pointer_axis *event = data;
  319. /* Notify the client with pointer focus of the axis event. */
  320. wlr_seat_pointer_notify_axis(server->seat,
  321. event->time_msec,
  322. event->orientation,
  323. event->delta,
  324. event->delta_discrete,
  325. event->source);
  326. }
  327. static void
  328. server_cursor_frame(struct wl_listener *listener, void *data)
  329. {
  330. (void)data;
  331. /* This event is forwarded by the cursor when a pointer emits an frame
  332. * event. Frame events are sent after regular pointer events to group
  333. * multiple events together. For instance, two axis events may happen at the
  334. * same time, in which case a frame event won't be sent in between. */
  335. struct inaban_server *server = wl_container_of(listener, server, cursor_frame);
  336. /* Notify the client with pointer focus of the frame event. */
  337. wlr_seat_pointer_notify_frame(server->seat);
  338. }
  339. static void
  340. xdg_surface_map(struct wl_listener *listener, void *data)
  341. {
  342. (void)data;
  343. /* Called when the surface is mapped, or ready to display on-screen. */
  344. struct inaban_view *view = wl_container_of(listener, view, map);
  345. view->mapped = true;
  346. focus_view(view, view->xdg_surface->surface);
  347. }
  348. static void
  349. xdg_surface_unmap(struct wl_listener *listener, void *data)
  350. {
  351. (void)data;
  352. /* Called when the surface is unmapped, and should no longer be shown. */
  353. struct inaban_view *view = wl_container_of(listener, view, unmap);
  354. view->mapped = false;
  355. }
  356. static void
  357. xdg_surface_destroy(struct wl_listener *listener, void *data)
  358. {
  359. (void)data;
  360. /* Called when the surface is destroyed and should never be shown again. */
  361. struct inaban_view *view = wl_container_of(listener, view, destroy);
  362. wl_list_remove(&view->link);
  363. free(view);
  364. }
  365. static void
  366. xdg_deny_request(struct wl_listener *listener, void *data)
  367. {
  368. (void)listener;
  369. (void)data;
  370. }
  371. static void
  372. server_new_xdg_surface(struct wl_listener *listener, void *data)
  373. {
  374. /* This event is raised when wlr_xdg_shell receives a new xdg surface from a
  375. * client, either a toplevel (application window) or popup. */
  376. struct inaban_server *server = wl_container_of(listener, server, new_xdg_surface);
  377. struct wlr_xdg_surface *xdg_surface = data;
  378. if(xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) return;
  379. /* Allocate a inaban_view for this surface */
  380. struct inaban_view *view = calloc(1, sizeof(struct inaban_view));
  381. view->server = server;
  382. view->xdg_surface = xdg_surface;
  383. /* Listen to the various events it can emit */
  384. view->map.notify = xdg_surface_map;
  385. wl_signal_add(&xdg_surface->events.map, &view->map);
  386. view->unmap.notify = xdg_surface_unmap;
  387. wl_signal_add(&xdg_surface->events.unmap, &view->unmap);
  388. view->destroy.notify = xdg_surface_destroy;
  389. wl_signal_add(&xdg_surface->events.destroy, &view->destroy);
  390. /* cotd */
  391. struct wlr_xdg_toplevel *toplevel = xdg_surface->toplevel;
  392. view->request_move.notify = xdg_deny_request;
  393. wl_signal_add(&toplevel->events.request_move, &view->request_move);
  394. view->request_resize.notify = xdg_deny_request;
  395. wl_signal_add(&toplevel->events.request_resize, &view->request_resize);
  396. /* Add it to the list of views. */
  397. wl_list_insert(&server->views, &view->link);
  398. }
  399. static bool
  400. drop_permissions(void)
  401. {
  402. if(getuid() != geteuid() || getgid() != getegid())
  403. {
  404. if(setuid(getuid()) != 0 || setgid(getgid()) != 0)
  405. {
  406. wlr_log(WLR_ERROR, "Unable to drop root, refusing to continue");
  407. return false;
  408. }
  409. }
  410. if(setuid(0) != -1)
  411. {
  412. wlr_log(
  413. WLR_ERROR,
  414. "Unable to drop root (we shouldn't be able to restore it after setuid), refusing to start");
  415. return false;
  416. }
  417. return true;
  418. }
  419. void
  420. quit(const Arg *arg)
  421. {
  422. (void)arg;
  423. wl_display_terminate(server.wl_display);
  424. }
  425. void
  426. sigterm_handler(int signal)
  427. {
  428. (void)signal;
  429. wl_display_terminate(server.wl_display);
  430. }
  431. void
  432. usage(char *argv0)
  433. {
  434. printf("Usage: %s [-s startup command]\n", argv0);
  435. }
  436. int
  437. main(int argc, char *argv[])
  438. {
  439. wlr_log_init(WLR_DEBUG, NULL);
  440. char *startup_cmdv[] = {NULL};
  441. int startup_cmdc = 0;
  442. int c;
  443. while((c = getopt(argc, argv, "s:h")) != -1)
  444. {
  445. switch(c)
  446. {
  447. case 's':
  448. startup_cmdv[startup_cmdc] = optarg;
  449. startup_cmdc++;
  450. break;
  451. default: usage(argv[0]); return 0;
  452. }
  453. }
  454. if(optind < argc)
  455. {
  456. usage(argv[0]);
  457. return 0;
  458. }
  459. /* The Wayland display is managed by libwayland. It handles accepting
  460. * clients from the Unix socket, manging Wayland globals, and so on. */
  461. server.wl_display = wl_display_create();
  462. /* The backend is a wlroots feature which abstracts the underlying input and
  463. * output hardware. The autocreate option will choose the most suitable
  464. * backend based on the current environment, such as opening an X11 window
  465. * if an X11 server is running. The NULL argument here optionally allows you
  466. * to pass in a custom renderer if wlr_renderer doesn't meet your needs. The
  467. * backend uses the renderer, for example, to fall back to software cursors
  468. * if the backend does not support hardware cursors (some older GPUs
  469. * don't). */
  470. server.backend = wlr_backend_autocreate(server.wl_display, NULL);
  471. if(!drop_permissions()) abort();
  472. // handle SIGTERM signals
  473. signal(SIGTERM, sigterm_handler);
  474. /* If we don't provide a renderer, autocreate makes a GLES2 renderer for us.
  475. * The renderer is responsible for defining the various pixel formats it
  476. * supports for shared memory, this configures that for clients. */
  477. server.renderer = wlr_backend_get_renderer(server.backend);
  478. wlr_renderer_init_wl_display(server.renderer, server.wl_display);
  479. /* This creates some hands-off wlroots interfaces. The compositor is
  480. * necessary for clients to allocate surfaces and the data device manager
  481. * handles the clipboard. Each of these wlroots interfaces has room for you
  482. * to dig your fingers in and play with their behavior if you want. */
  483. wlr_compositor_create(server.wl_display, server.renderer);
  484. wlr_data_device_manager_create(server.wl_display);
  485. /* Creates an output layout, which a wlroots utility for working with an
  486. * arrangement of screens in a physical layout. */
  487. server.output_layout = wlr_output_layout_create();
  488. /* Configure a listener to be notified when new outputs are available on the
  489. * backend. */
  490. wl_list_init(&server.outputs);
  491. server.new_output.notify = server_new_output;
  492. wl_signal_add(&server.backend->events.new_output, &server.new_output);
  493. /* Set up our list of views and the xdg-shell. The xdg-shell is a Wayland
  494. * protocol which is used for application windows. For more detail on
  495. * shells, refer to my article:
  496. *
  497. * https://drewdevault.com/2018/07/29/Wayland-shells.html
  498. */
  499. wl_list_init(&server.views);
  500. server.xdg_shell = wlr_xdg_shell_create(server.wl_display);
  501. server.new_xdg_surface.notify = server_new_xdg_surface;
  502. wl_signal_add(&server.xdg_shell->events.new_surface, &server.new_xdg_surface);
  503. server.layer_shell = wlr_layer_shell_v1_create(server.wl_display);
  504. server.new_layer_surface.notify = server_new_layer_surface;
  505. wl_signal_add(&server.xdg_shell->events.new_surface, &server.new_layer_surface);
  506. /*
  507. * Creates a cursor, which is a wlroots utility for tracking the cursor
  508. * image shown on screen.
  509. */
  510. server.cursor = wlr_cursor_create();
  511. wlr_cursor_attach_output_layout(server.cursor, server.output_layout);
  512. /* Creates an xcursor manager, another wlroots utility which loads up
  513. * Xcursor themes to source cursor images from and makes sure that cursor
  514. * images are available at all scale factors on the screen (necessary for
  515. * HiDPI support). We add a cursor theme at scale factor 1 to begin with. */
  516. server.cursor_mgr = wlr_xcursor_manager_create(NULL, 24);
  517. wlr_xcursor_manager_load(server.cursor_mgr, 1);
  518. /*
  519. * wlr_cursor *only* displays an image on screen. It does not move around
  520. * when the pointer moves. However, we can attach input devices to it, and
  521. * it will generate aggregate events for all of them. In these events, we
  522. * can choose how we want to process them, forwarding them to clients and
  523. * moving the cursor around. More detail on this process is described in my
  524. * input handling blog post:
  525. *
  526. * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html
  527. *
  528. * And more comments are sprinkled throughout the notify functions above.
  529. */
  530. server.cursor_motion.notify = server_cursor_motion;
  531. wl_signal_add(&server.cursor->events.motion, &server.cursor_motion);
  532. server.cursor_motion_absolute.notify = server_cursor_motion_absolute;
  533. wl_signal_add(&server.cursor->events.motion_absolute, &server.cursor_motion_absolute);
  534. server.cursor_button.notify = server_cursor_button;
  535. wl_signal_add(&server.cursor->events.button, &server.cursor_button);
  536. server.cursor_axis.notify = server_cursor_axis;
  537. wl_signal_add(&server.cursor->events.axis, &server.cursor_axis);
  538. server.cursor_frame.notify = server_cursor_frame;
  539. wl_signal_add(&server.cursor->events.frame, &server.cursor_frame);
  540. /*
  541. * Configures a seat, which is a single "seat" at which a user sits and
  542. * operates the computer. This conceptually includes up to one keyboard,
  543. * pointer, touch, and drawing tablet device. We also rig up a listener to
  544. * let us know when new input devices are available on the backend.
  545. */
  546. wl_list_init(&server.keyboards);
  547. server.new_input.notify = server_new_input;
  548. wl_signal_add(&server.backend->events.new_input, &server.new_input);
  549. server.seat = wlr_seat_create(server.wl_display, "seat0");
  550. server.request_cursor.notify = seat_request_cursor;
  551. wl_signal_add(&server.seat->events.request_set_cursor, &server.request_cursor);
  552. /* Add a Unix socket to the Wayland display. */
  553. const char *socket = wl_display_add_socket_auto(server.wl_display);
  554. if(!socket)
  555. {
  556. wlr_backend_destroy(server.backend);
  557. return 1;
  558. }
  559. /* Start the backend. This will enumerate outputs and inputs, become the DRM
  560. * master, etc */
  561. if(!wlr_backend_start(server.backend))
  562. {
  563. wlr_backend_destroy(server.backend);
  564. wl_display_destroy(server.wl_display);
  565. return 1;
  566. }
  567. /* Set the WAYLAND_DISPLAY environment variable to our socket and run the
  568. * startup command if requested. */
  569. setenv("WAYLAND_DISPLAY", socket, true);
  570. for(int i = 0; i < startup_cmdc; i++)
  571. {
  572. if(fork() == 0)
  573. {
  574. unsetenv("DISPLAY");
  575. execl("/bin/sh", "/bin/sh", "-c", startup_cmdv[i], (void *)NULL);
  576. }
  577. }
  578. /* Run the Wayland event loop. This does not return until you exit the
  579. * compositor. Starting the backend rigged up all of the necessary event
  580. * loop configuration to listen to libinput events, DRM events, generate
  581. * frame events at the refresh rate, and so on. */
  582. wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s", socket);
  583. wl_display_run(server.wl_display);
  584. /* Once wl_display_run returns, we shut down the server. */
  585. wl_display_destroy_clients(server.wl_display);
  586. wl_display_destroy(server.wl_display);
  587. return 0;
  588. }