logo

inaban

Unnamed repository; edit this file 'description' to name the repository.

inaban.c (31086B)


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