inaban.c 30 KB

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