shim.c 94 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557
  1. /*
  2. * Copyright (c) Milan Jurkulák 2026.
  3. * Contact:
  4. * e: mimoccc@gmail.com
  5. * e: mj@mjdev.org
  6. * w: https://mjdev.org
  7. *
  8. * wlroots 0.18 based compositor core, exposed to Kotlin/Native
  9. * through the flat API in shim.h. Policy (layers, focus order,
  10. * IPC) lives on the Kotlin side.
  11. */
  12. #define _POSIX_C_SOURCE 200809L
  13. #include <assert.h>
  14. #include <signal.h>
  15. #include <stdbool.h>
  16. #include <stdint.h>
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <sys/wait.h>
  21. #include <time.h>
  22. #include <unistd.h>
  23. #include <wayland-server-core.h>
  24. #include <wlr/backend.h>
  25. #include <wlr/backend/session.h>
  26. #include <wlr/render/allocator.h>
  27. #include <wlr/render/wlr_renderer.h>
  28. #include <wlr/render/gles2.h>
  29. #include <wlr/render/egl.h>
  30. #include <EGL/egl.h>
  31. #include <GLES2/gl2.h>
  32. #include <wlr/types/wlr_compositor.h>
  33. #include <wlr/types/wlr_subcompositor.h>
  34. #include <wlr/types/wlr_cursor.h>
  35. #include <wlr/types/wlr_data_device.h>
  36. #include <wlr/types/wlr_input_device.h>
  37. #include <wlr/types/wlr_keyboard.h>
  38. #include <wlr/types/wlr_output.h>
  39. #include <wlr/types/wlr_output_layout.h>
  40. #include <wlr/types/wlr_pointer.h>
  41. #include <wlr/types/wlr_scene.h>
  42. #include <wlr/types/wlr_seat.h>
  43. #include <wlr/types/wlr_xcursor_manager.h>
  44. #include <wlr/types/wlr_xdg_decoration_v1.h>
  45. #include <wlr/types/wlr_xdg_shell.h>
  46. #include <wlr/util/log.h>
  47. #include <wlr/xwayland.h>
  48. #include <xkbcommon/xkbcommon.h>
  49. /* server-side window decorations: rendered with cairo into an image surface that
  50. * is wrapped in a custom wlr_buffer and shown via a wlr_scene_buffer node */
  51. #include <math.h>
  52. #include <cairo.h>
  53. #include <drm_fourcc.h>
  54. #include <wlr/interfaces/wlr_buffer.h>
  55. /* _POSIX_C_SOURCE hides the XOPEN/GNU math constants from <math.h> */
  56. #ifndef M_PI
  57. #define M_PI 3.14159265358979323846
  58. #endif
  59. #include "shim.h"
  60. /* GNOME-style decoration geometry (logical px) */
  61. #define MJC_DECO_TITLEBAR_H 36
  62. #define MJC_DECO_RADIUS 10
  63. #define MJC_DECO_BORDER 3
  64. #define MJC_DECO_BTN_R 9 /* button circle radius */
  65. #define MJC_DECO_BTN_GAP 8 /* gap between button circles */
  66. #define MJC_DECO_PAD 14 /* horizontal padding (title left, buttons right) */
  67. #define MJC_DECO_RESIZE 6 /* edge/corner resize hotzone half-width (px) */
  68. enum mjc_cursor_mode {
  69. MJC_CURSOR_PASSTHROUGH,
  70. MJC_CURSOR_MOVE,
  71. MJC_CURSOR_RESIZE,
  72. };
  73. struct mjc_server {
  74. struct wl_display *display;
  75. struct wl_event_loop *loop;
  76. struct wlr_backend *backend;
  77. struct wlr_session *session;
  78. struct wlr_renderer *renderer;
  79. struct wlr_allocator *allocator;
  80. struct wlr_compositor *compositor;
  81. struct wlr_scene *scene;
  82. struct wlr_scene_output_layout *scene_layout;
  83. struct wlr_output_layout *output_layout;
  84. struct wlr_scene_tree *layers[5];
  85. struct wlr_xdg_shell *xdg_shell;
  86. struct wlr_xdg_decoration_manager_v1 *xdg_decoration_mgr;
  87. struct wlr_xwayland *xwayland;
  88. struct wlr_cursor *cursor;
  89. struct wlr_xcursor_manager *cursor_mgr;
  90. struct wlr_seat *seat;
  91. struct wl_list views; /* mjc_view.link */
  92. struct wl_list keyboards; /* mjc_keyboard.link */
  93. struct wl_list fd_sources;/* mjc_fd_source.link */
  94. struct mjc_view *focused_view;
  95. mjc_callbacks cbs;
  96. void *ud;
  97. const char *socket;
  98. uint64_t next_view_id;
  99. /* nested-output size (16:9 default); used when the backend has no mode list
  100. * (wayland/headless) and to lock the window against host-driven resizes */
  101. int output_w, output_h;
  102. /* throttle timestamp (msec) for the pointer callback */
  103. uint32_t last_pointer_ms;
  104. /* injection feedback marker (Android "show touches" style): a small white square in
  105. * the overlay layer that appears at the injected pointer and fades out after a timeout */
  106. struct wlr_scene_rect *inject_marker;
  107. struct wl_event_source *inject_fade_timer;
  108. float inject_alpha;
  109. enum mjc_cursor_mode cursor_mode;
  110. struct mjc_view *grabbed_view;
  111. double grab_x, grab_y;
  112. struct wlr_box grab_geobox;
  113. uint32_t resize_edges;
  114. struct wl_event_source *sigchld_source;
  115. /* pids spawned via mjc_spawn; sigchld must reap only these,
  116. * other children (Xwayland) belong to wlroots */
  117. pid_t spawned_pids[64];
  118. int spawned_count;
  119. /* server-side decoration theme (straight-alpha colors, 0..1) derived from the
  120. * shell background; the compositor paints every normal-layer window's frame with it */
  121. float deco_bg[3];
  122. float deco_fg[3];
  123. struct wl_listener new_output;
  124. struct wl_listener new_xdg_toplevel;
  125. struct wl_listener new_xdg_popup;
  126. struct wl_listener new_toplevel_decoration;
  127. struct wl_listener new_xwayland_surface;
  128. struct wl_listener xwayland_ready;
  129. struct wl_listener cursor_motion;
  130. struct wl_listener cursor_motion_absolute;
  131. struct wl_listener cursor_button;
  132. struct wl_listener cursor_axis;
  133. struct wl_listener cursor_frame;
  134. struct wl_listener new_input;
  135. struct wl_listener request_cursor;
  136. struct wl_listener request_set_selection;
  137. };
  138. struct mjc_view {
  139. struct wl_list link;
  140. struct mjc_server *server;
  141. uint64_t id;
  142. bool is_xwayland;
  143. bool mapped;
  144. bool minimized;
  145. bool maximized;
  146. /* re-center once the client commits its post-unmaximize size */
  147. bool pending_center;
  148. bool focusable;
  149. mjc_layer layer;
  150. struct wlr_scene_tree *scene_tree;
  151. /* floating geometry remembered while maximized (x/y are scene coords) */
  152. struct wlr_box saved_geo;
  153. /* server-side decoration (only for normal-layer app windows). The titlebar is a
  154. * cairo buffer node, the borders are scene rects; all are children of scene_tree so
  155. * they move/raise/minimize with the window automatically. */
  156. bool decorated;
  157. bool deco_disabled; /* chromeless: never draw a server frame */
  158. bool is_chrome; /* shell surface (wallpaper/panel/menu): no frame */
  159. struct wlr_scene_buffer *deco_titlebar;
  160. struct wlr_scene_rect *deco_border[3]; /* left, right, bottom */
  161. int deco_w, deco_h; /* last rendered content size */
  162. int hovered_button; /* -1 none, 0 min, 1 max, 2 close */
  163. /* xdg shell */
  164. struct wlr_xdg_toplevel *xdg_toplevel;
  165. /* xwayland */
  166. struct wlr_xwayland_surface *xsurface;
  167. struct wl_listener map;
  168. struct wl_listener unmap;
  169. struct wl_listener commit;
  170. struct wl_listener destroy;
  171. struct wl_listener request_move;
  172. struct wl_listener request_resize;
  173. struct wl_listener request_maximize;
  174. struct wl_listener request_minimize;
  175. struct wl_listener request_fullscreen;
  176. struct wl_listener set_title;
  177. struct wl_listener set_app_id; /* set_class for xwayland */
  178. struct wl_listener associate;
  179. struct wl_listener dissociate;
  180. struct wl_listener request_configure;
  181. struct wl_listener set_geometry;
  182. };
  183. struct mjc_popup {
  184. struct wlr_xdg_popup *xdg_popup;
  185. struct wl_listener commit;
  186. struct wl_listener destroy;
  187. };
  188. struct mjc_decoration {
  189. struct wlr_xdg_toplevel_decoration_v1 *decoration;
  190. struct mjc_view *view;
  191. struct wl_listener request_mode;
  192. struct wl_listener destroy;
  193. };
  194. struct mjc_keyboard {
  195. struct wl_list link;
  196. struct mjc_server *server;
  197. struct wlr_keyboard *wlr_keyboard;
  198. struct wl_listener modifiers;
  199. struct wl_listener key;
  200. struct wl_listener destroy;
  201. };
  202. struct mjc_output {
  203. struct wl_list link;
  204. struct mjc_server *server;
  205. struct wlr_output *wlr_output;
  206. struct wl_listener frame;
  207. struct wl_listener request_state;
  208. struct wl_listener destroy;
  209. };
  210. struct mjc_fd_source {
  211. struct wl_list link;
  212. struct mjc_server *server;
  213. int fd;
  214. struct wl_event_source *source;
  215. };
  216. /* ------------------------------------------------------------------ */
  217. /* helpers */
  218. /* ------------------------------------------------------------------ */
  219. static struct wlr_surface *view_surface(struct mjc_view *view) {
  220. if (view->is_xwayland) {
  221. return view->xsurface != NULL ? view->xsurface->surface : NULL;
  222. }
  223. return view->xdg_toplevel->base->surface;
  224. }
  225. /* re-render a window's titlebar bitmap in place (focus/hover/title changes) */
  226. static void deco_repaint(struct mjc_view *view);
  227. static void emit_focus_change(struct mjc_server *server) {
  228. if (server->cbs.focus_change != NULL) {
  229. server->cbs.focus_change(server->ud, server->focused_view);
  230. }
  231. }
  232. static void view_deactivate(struct mjc_view *view) {
  233. if (view->is_xwayland) {
  234. if (view->xsurface != NULL) {
  235. wlr_xwayland_surface_activate(view->xsurface, false);
  236. }
  237. } else {
  238. wlr_xdg_toplevel_set_activated(view->xdg_toplevel, false);
  239. }
  240. }
  241. static void view_focus_internal(struct mjc_view *view, bool raise) {
  242. if (view == NULL || !view->focusable || view->minimized) {
  243. return;
  244. }
  245. struct mjc_server *server = view->server;
  246. struct wlr_seat *seat = server->seat;
  247. if (server->focused_view == view) {
  248. return;
  249. }
  250. struct mjc_view *prev = server->focused_view;
  251. if (server->focused_view != NULL) {
  252. view_deactivate(server->focused_view);
  253. }
  254. /* focus-follows-mouse must not restack windows (no autoraise); only an explicit
  255. * focus (click / activate / unminimize) raises the view to the top */
  256. if (raise && view->scene_tree != NULL) {
  257. wlr_scene_node_raise_to_top(&view->scene_tree->node);
  258. }
  259. if (view->is_xwayland) {
  260. wlr_xwayland_surface_activate(view->xsurface, true);
  261. wlr_xwayland_surface_restack(view->xsurface, NULL, XCB_STACK_MODE_ABOVE);
  262. } else {
  263. wlr_xdg_toplevel_set_activated(view->xdg_toplevel, true);
  264. }
  265. struct wlr_surface *surface = view_surface(view);
  266. struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat);
  267. if (surface != NULL) {
  268. if (keyboard != NULL) {
  269. wlr_seat_keyboard_notify_enter(seat, surface,
  270. keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers);
  271. } else {
  272. wlr_seat_keyboard_notify_enter(seat, surface, NULL, 0, NULL);
  273. }
  274. }
  275. server->focused_view = view;
  276. /* refresh active/inactive titlebar styling on both windows */
  277. if (prev != NULL) {
  278. deco_repaint(prev);
  279. }
  280. deco_repaint(view);
  281. emit_focus_change(server);
  282. }
  283. void mjc_view_focus(struct mjc_view *view) {
  284. view_focus_internal(view, true);
  285. }
  286. static void clear_focus_if(struct mjc_server *server, struct mjc_view *view) {
  287. if (server->focused_view == view) {
  288. server->focused_view = NULL;
  289. emit_focus_change(server);
  290. }
  291. if (server->grabbed_view == view) {
  292. server->grabbed_view = NULL;
  293. server->cursor_mode = MJC_CURSOR_PASSTHROUGH;
  294. }
  295. }
  296. static struct mjc_view *desktop_view_at(struct mjc_server *server,
  297. double lx, double ly, struct wlr_surface **surface,
  298. double *sx, double *sy) {
  299. struct wlr_scene_node *node =
  300. wlr_scene_node_at(&server->scene->tree.node, lx, ly, sx, sy);
  301. if (node == NULL || node->type != WLR_SCENE_NODE_BUFFER) {
  302. return NULL;
  303. }
  304. struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
  305. struct wlr_scene_surface *scene_surface =
  306. wlr_scene_surface_try_from_buffer(scene_buffer);
  307. if (scene_surface == NULL) {
  308. return NULL;
  309. }
  310. *surface = scene_surface->surface;
  311. struct wlr_scene_tree *tree = node->parent;
  312. while (tree != NULL && tree->node.data == NULL) {
  313. tree = tree->node.parent;
  314. }
  315. return tree != NULL ? tree->node.data : NULL;
  316. }
  317. /* ------------------------------------------------------------------ */
  318. /* outputs */
  319. /* ------------------------------------------------------------------ */
  320. static void output_frame(struct wl_listener *listener, void *data) {
  321. (void) data;
  322. struct mjc_output *output = wl_container_of(listener, output, frame);
  323. struct wlr_scene_output *scene_output = wlr_scene_get_scene_output(
  324. output->server->scene, output->wlr_output);
  325. if (scene_output == NULL) {
  326. return;
  327. }
  328. wlr_scene_output_commit(scene_output, NULL);
  329. struct timespec now;
  330. clock_gettime(CLOCK_MONOTONIC, &now);
  331. wlr_scene_output_send_frame_done(scene_output, &now);
  332. }
  333. static void output_request_state(struct wl_listener *listener, void *data) {
  334. struct mjc_output *output = wl_container_of(listener, output, request_state);
  335. const struct wlr_output_event_request_state *event = data;
  336. /* honor host-driven resizes (the user resizing the nested x11/wayland window) so the
  337. * desktop relayouts to the new size; commit the requested state and remember the new
  338. * dimensions, which the shell reads back via the wl_output geometry */
  339. wlr_output_commit_state(output->wlr_output, event->state);
  340. output->server->output_w = output->wlr_output->width;
  341. output->server->output_h = output->wlr_output->height;
  342. }
  343. static void output_destroy(struct wl_listener *listener, void *data) {
  344. (void) data;
  345. struct mjc_output *output = wl_container_of(listener, output, destroy);
  346. struct mjc_server *server = output->server;
  347. wl_list_remove(&output->frame.link);
  348. wl_list_remove(&output->request_state.link);
  349. wl_list_remove(&output->destroy.link);
  350. wl_list_remove(&output->link);
  351. free(output);
  352. /* nested backends (x11/wayland) have no session; closing the host window
  353. * destroys the output, so quit the compositor and let the run action finish.
  354. * a real (drm) session keeps running when an output is unplugged. */
  355. if (server->session == NULL) {
  356. wl_display_terminate(server->display);
  357. }
  358. }
  359. static void server_new_output(struct wl_listener *listener, void *data) {
  360. struct mjc_server *server = wl_container_of(listener, server, new_output);
  361. struct wlr_output *wlr_output = data;
  362. wlr_output_init_render(wlr_output, server->allocator, server->renderer);
  363. struct wlr_output_state state;
  364. wlr_output_state_init(&state);
  365. wlr_output_state_set_enabled(&state, true);
  366. struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
  367. if (mode != NULL) {
  368. wlr_output_state_set_mode(&state, mode);
  369. } else {
  370. /* wayland / headless backends expose no mode list, so preferred_mode is
  371. * NULL; without a mode the output has no dimensions and renders black.
  372. * pin it to the configured 16:9 size. */
  373. wlr_output_state_set_custom_mode(&state,
  374. server->output_w, server->output_h, 0);
  375. }
  376. wlr_output_commit_state(wlr_output, &state);
  377. wlr_output_state_finish(&state);
  378. struct mjc_output *output = calloc(1, sizeof(*output));
  379. output->wlr_output = wlr_output;
  380. output->server = server;
  381. output->frame.notify = output_frame;
  382. wl_signal_add(&wlr_output->events.frame, &output->frame);
  383. output->request_state.notify = output_request_state;
  384. wl_signal_add(&wlr_output->events.request_state, &output->request_state);
  385. output->destroy.notify = output_destroy;
  386. wl_signal_add(&wlr_output->events.destroy, &output->destroy);
  387. wl_list_init(&output->link);
  388. struct wlr_output_layout_output *l_output =
  389. wlr_output_layout_add_auto(server->output_layout, wlr_output);
  390. struct wlr_scene_output *scene_output =
  391. wlr_scene_output_create(server->scene, wlr_output);
  392. wlr_scene_output_layout_add_output(server->scene_layout, l_output, scene_output);
  393. }
  394. /* ------------------------------------------------------------------ */
  395. /* keyboard & input */
  396. /* ------------------------------------------------------------------ */
  397. static void keyboard_handle_modifiers(struct wl_listener *listener, void *data) {
  398. (void) data;
  399. struct mjc_keyboard *keyboard = wl_container_of(listener, keyboard, modifiers);
  400. wlr_seat_set_keyboard(keyboard->server->seat, keyboard->wlr_keyboard);
  401. wlr_seat_keyboard_notify_modifiers(keyboard->server->seat,
  402. &keyboard->wlr_keyboard->modifiers);
  403. }
  404. static void keyboard_handle_key(struct wl_listener *listener, void *data) {
  405. struct mjc_keyboard *keyboard = wl_container_of(listener, keyboard, key);
  406. struct mjc_server *server = keyboard->server;
  407. struct wlr_keyboard_key_event *event = data;
  408. uint32_t keycode = event->keycode + 8;
  409. const xkb_keysym_t *syms;
  410. int nsyms = xkb_state_key_get_syms(
  411. keyboard->wlr_keyboard->xkb_state, keycode, &syms);
  412. uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->wlr_keyboard);
  413. bool pressed = event->state == WL_KEYBOARD_KEY_STATE_PRESSED;
  414. bool handled = false;
  415. for (int i = 0; i < nsyms; i++) {
  416. /* VT switching always works */
  417. if (pressed && syms[i] >= XKB_KEY_XF86Switch_VT_1 &&
  418. syms[i] <= XKB_KEY_XF86Switch_VT_12) {
  419. if (server->session != NULL) {
  420. wlr_session_change_vt(server->session,
  421. syms[i] - XKB_KEY_XF86Switch_VT_1 + 1);
  422. }
  423. handled = true;
  424. continue;
  425. }
  426. if (server->cbs.key != NULL &&
  427. server->cbs.key(server->ud, syms[i], modifiers, pressed)) {
  428. handled = true;
  429. }
  430. }
  431. if (!handled) {
  432. wlr_seat_set_keyboard(server->seat, keyboard->wlr_keyboard);
  433. wlr_seat_keyboard_notify_key(server->seat,
  434. event->time_msec, event->keycode, event->state);
  435. }
  436. }
  437. static void keyboard_handle_destroy(struct wl_listener *listener, void *data) {
  438. (void) data;
  439. struct mjc_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
  440. wl_list_remove(&keyboard->modifiers.link);
  441. wl_list_remove(&keyboard->key.link);
  442. wl_list_remove(&keyboard->destroy.link);
  443. wl_list_remove(&keyboard->link);
  444. free(keyboard);
  445. }
  446. static void server_new_keyboard(struct mjc_server *server,
  447. struct wlr_input_device *device) {
  448. struct wlr_keyboard *wlr_keyboard = wlr_keyboard_from_input_device(device);
  449. struct mjc_keyboard *keyboard = calloc(1, sizeof(*keyboard));
  450. keyboard->server = server;
  451. keyboard->wlr_keyboard = wlr_keyboard;
  452. struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
  453. struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL,
  454. XKB_KEYMAP_COMPILE_NO_FLAGS);
  455. wlr_keyboard_set_keymap(wlr_keyboard, keymap);
  456. xkb_keymap_unref(keymap);
  457. xkb_context_unref(context);
  458. wlr_keyboard_set_repeat_info(wlr_keyboard, 25, 600);
  459. keyboard->modifiers.notify = keyboard_handle_modifiers;
  460. wl_signal_add(&wlr_keyboard->events.modifiers, &keyboard->modifiers);
  461. keyboard->key.notify = keyboard_handle_key;
  462. wl_signal_add(&wlr_keyboard->events.key, &keyboard->key);
  463. keyboard->destroy.notify = keyboard_handle_destroy;
  464. wl_signal_add(&device->events.destroy, &keyboard->destroy);
  465. wlr_seat_set_keyboard(server->seat, wlr_keyboard);
  466. wl_list_insert(&server->keyboards, &keyboard->link);
  467. }
  468. static void server_new_pointer(struct mjc_server *server,
  469. struct wlr_input_device *device) {
  470. wlr_cursor_attach_input_device(server->cursor, device);
  471. }
  472. static void server_new_input(struct wl_listener *listener, void *data) {
  473. struct mjc_server *server = wl_container_of(listener, server, new_input);
  474. struct wlr_input_device *device = data;
  475. switch (device->type) {
  476. case WLR_INPUT_DEVICE_KEYBOARD:
  477. server_new_keyboard(server, device);
  478. break;
  479. case WLR_INPUT_DEVICE_POINTER:
  480. server_new_pointer(server, device);
  481. break;
  482. default:
  483. break;
  484. }
  485. uint32_t caps = WL_SEAT_CAPABILITY_POINTER;
  486. if (!wl_list_empty(&server->keyboards)) {
  487. caps |= WL_SEAT_CAPABILITY_KEYBOARD;
  488. }
  489. wlr_seat_set_capabilities(server->seat, caps);
  490. }
  491. static void seat_request_cursor(struct wl_listener *listener, void *data) {
  492. struct mjc_server *server = wl_container_of(listener, server, request_cursor);
  493. struct wlr_seat_pointer_request_set_cursor_event *event = data;
  494. struct wlr_seat_client *focused_client =
  495. server->seat->pointer_state.focused_client;
  496. if (focused_client == event->seat_client) {
  497. wlr_cursor_set_surface(server->cursor, event->surface,
  498. event->hotspot_x, event->hotspot_y);
  499. }
  500. }
  501. static void seat_request_set_selection(struct wl_listener *listener, void *data) {
  502. struct mjc_server *server =
  503. wl_container_of(listener, server, request_set_selection);
  504. struct wlr_seat_request_set_selection_event *event = data;
  505. wlr_seat_set_selection(server->seat, event->source, event->serial);
  506. }
  507. /* ------------------------------------------------------------------ */
  508. /* cursor */
  509. /* ------------------------------------------------------------------ */
  510. static void view_current_geometry(struct mjc_view *view, struct wlr_box *box);
  511. /* server-side decoration helpers (defined after the interactive-grab section) */
  512. static void view_decoration_update(struct mjc_view *view);
  513. static void view_decoration_destroy(struct mjc_view *view);
  514. /* returns true when the press/release was consumed by a window's decoration */
  515. static bool deco_handle_button(struct mjc_server *server,
  516. double lx, double ly, bool pressed);
  517. /* recompute which titlebar button (if any) the pointer hovers, re-rendering on change */
  518. static void deco_update_hover(struct mjc_server *server);
  519. /* layout-space top-left and width of a decorated view's titlebar; false if undecorated */
  520. static bool deco_titlebar_box(struct mjc_view *view,
  521. double *tx, double *ty, int *w);
  522. /* resize-edge mask under (lx,ly) for a decorated window (0 = not on an edge) */
  523. static uint32_t deco_resize_edges(struct mjc_server *server,
  524. double lx, double ly, struct mjc_view **out);
  525. /* xcursor name for a resize-edge mask */
  526. static const char *deco_resize_cursor(uint32_t edges);
  527. static void process_cursor_move(struct mjc_server *server) {
  528. struct mjc_view *view = server->grabbed_view;
  529. if (view == NULL || view->scene_tree == NULL) {
  530. return;
  531. }
  532. int x = (int)(server->cursor->x - server->grab_x);
  533. int y = (int)(server->cursor->y - server->grab_y);
  534. wlr_scene_node_set_position(&view->scene_tree->node, x, y);
  535. if (view->is_xwayland && view->xsurface != NULL) {
  536. wlr_xwayland_surface_configure(view->xsurface,
  537. (int16_t) x, (int16_t) y,
  538. (uint16_t) view->xsurface->width, (uint16_t) view->xsurface->height);
  539. }
  540. }
  541. static void process_cursor_resize(struct mjc_server *server) {
  542. struct mjc_view *view = server->grabbed_view;
  543. if (view == NULL || view->scene_tree == NULL) {
  544. return;
  545. }
  546. double border_x = server->cursor->x - server->grab_x;
  547. double border_y = server->cursor->y - server->grab_y;
  548. int new_left = server->grab_geobox.x;
  549. int new_right = server->grab_geobox.x + server->grab_geobox.width;
  550. int new_top = server->grab_geobox.y;
  551. int new_bottom = server->grab_geobox.y + server->grab_geobox.height;
  552. if (server->resize_edges & WLR_EDGE_TOP) {
  553. new_top = (int) border_y;
  554. if (new_top >= new_bottom) {
  555. new_top = new_bottom - 1;
  556. }
  557. } else if (server->resize_edges & WLR_EDGE_BOTTOM) {
  558. new_bottom = (int) border_y;
  559. if (new_bottom <= new_top) {
  560. new_bottom = new_top + 1;
  561. }
  562. }
  563. if (server->resize_edges & WLR_EDGE_LEFT) {
  564. new_left = (int) border_x;
  565. if (new_left >= new_right) {
  566. new_left = new_right - 1;
  567. }
  568. } else if (server->resize_edges & WLR_EDGE_RIGHT) {
  569. new_right = (int) border_x;
  570. if (new_right <= new_left) {
  571. new_right = new_left + 1;
  572. }
  573. }
  574. int new_width = new_right - new_left;
  575. int new_height = new_bottom - new_top;
  576. if (view->is_xwayland && view->xsurface != NULL) {
  577. wlr_scene_node_set_position(&view->scene_tree->node, new_left, new_top);
  578. wlr_xwayland_surface_configure(view->xsurface,
  579. (int16_t) new_left, (int16_t) new_top,
  580. (uint16_t) new_width, (uint16_t) new_height);
  581. } else {
  582. struct wlr_box geo_box;
  583. wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &geo_box);
  584. wlr_scene_node_set_position(&view->scene_tree->node,
  585. new_left - geo_box.x, new_top - geo_box.y);
  586. wlr_xdg_toplevel_set_size(view->xdg_toplevel, new_width, new_height);
  587. }
  588. }
  589. /* 1 = focus-follows-mouse (focusable view under the pointer gets focus). The focus-clear
  590. * over the desktop is intentionally NOT done here: it made the dock reveal jam after a couple
  591. * of cycles. The dock autohide must instead be driven by pointer-leave on the shell side. */
  592. #define MJC_FOCUS_FOLLOWS_MOUSE 1
  593. static void process_cursor_motion(struct mjc_server *server, uint32_t time) {
  594. if (server->cursor_mode == MJC_CURSOR_MOVE) {
  595. process_cursor_move(server);
  596. return;
  597. } else if (server->cursor_mode == MJC_CURSOR_RESIZE) {
  598. process_cursor_resize(server);
  599. return;
  600. }
  601. double sx, sy;
  602. struct wlr_seat *seat = server->seat;
  603. struct wlr_surface *surface = NULL;
  604. struct mjc_view *view = desktop_view_at(server, server->cursor->x,
  605. server->cursor->y, &surface, &sx, &sy);
  606. /* server-side decorations own the pointer over the frame: refresh button hover
  607. * and pick the cursor (resize on edges/corners, hand over buttons, arrow over the
  608. * draggable titlebar). When the frame owns the pointer the client gets no focus. */
  609. deco_update_hover(server);
  610. struct mjc_view *rview = NULL;
  611. uint32_t redges = deco_resize_edges(server, server->cursor->x,
  612. server->cursor->y, &rview);
  613. bool deco_owns = false;
  614. const char *deco_cursor = NULL;
  615. if (redges != 0) {
  616. deco_cursor = deco_resize_cursor(redges);
  617. deco_owns = true;
  618. } else {
  619. struct mjc_view *tv;
  620. wl_list_for_each(tv, &server->views, link) {
  621. double tx, ty;
  622. int w;
  623. if (!deco_titlebar_box(tv, &tx, &ty, &w)) {
  624. continue;
  625. }
  626. if (server->cursor->x >= tx && server->cursor->x < tx + w &&
  627. server->cursor->y >= ty &&
  628. server->cursor->y < ty + MJC_DECO_TITLEBAR_H) {
  629. deco_cursor = (tv->hovered_button >= 0) ? "pointer" : "default";
  630. deco_owns = true;
  631. break;
  632. }
  633. }
  634. }
  635. if (deco_owns) {
  636. wlr_cursor_set_xcursor(server->cursor, server->cursor_mgr, deco_cursor);
  637. wlr_seat_pointer_clear_focus(seat);
  638. } else if (surface == NULL) {
  639. wlr_cursor_set_xcursor(server->cursor, server->cursor_mgr, "default");
  640. wlr_seat_pointer_clear_focus(seat);
  641. } else {
  642. wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
  643. wlr_seat_pointer_notify_motion(seat, time, sx, sy);
  644. /* focus-follows-mouse: focusable view under the pointer gets keyboard focus (no
  645. * restack). Do NOT clear focus over non-focusable surfaces — that churns focus and
  646. * jams the dock reveal; autohide is handled by pointer-leave on the shell side. */
  647. if (MJC_FOCUS_FOLLOWS_MOUSE && view != NULL && view->focusable) {
  648. view_focus_internal(view, false);
  649. }
  650. }
  651. // broadcast the pointer position to ipc clients (throttled), so the shell can do
  652. // edge detection without the AWT global pointer which is unreliable under nested XWayland
  653. if (server->cbs.pointer != NULL && (time - server->last_pointer_ms) >= 30u) {
  654. server->last_pointer_ms = time;
  655. server->cbs.pointer(server->ud,
  656. (int) server->cursor->x, (int) server->cursor->y);
  657. }
  658. }
  659. static void server_cursor_motion(struct wl_listener *listener, void *data) {
  660. struct mjc_server *server = wl_container_of(listener, server, cursor_motion);
  661. struct wlr_pointer_motion_event *event = data;
  662. wlr_cursor_move(server->cursor, &event->pointer->base,
  663. event->delta_x, event->delta_y);
  664. process_cursor_motion(server, event->time_msec);
  665. }
  666. static void server_cursor_motion_absolute(struct wl_listener *listener, void *data) {
  667. struct mjc_server *server =
  668. wl_container_of(listener, server, cursor_motion_absolute);
  669. struct wlr_pointer_motion_absolute_event *event = data;
  670. wlr_cursor_warp_absolute(server->cursor, &event->pointer->base,
  671. event->x, event->y);
  672. process_cursor_motion(server, event->time_msec);
  673. }
  674. static void server_cursor_button(struct wl_listener *listener, void *data) {
  675. struct mjc_server *server = wl_container_of(listener, server, cursor_button);
  676. struct wlr_pointer_button_event *event = data;
  677. wlr_seat_pointer_notify_button(server->seat,
  678. event->time_msec, event->button, event->state);
  679. if (event->state == WL_POINTER_BUTTON_STATE_RELEASED) {
  680. server->cursor_mode = MJC_CURSOR_PASSTHROUGH;
  681. server->grabbed_view = NULL;
  682. } else {
  683. /* a press over a window's titlebar/buttons is handled by the decoration
  684. * (focus + move grab, or a min/max/close action) and must not fall through
  685. * to client focus */
  686. if (deco_handle_button(server, server->cursor->x, server->cursor->y, true)) {
  687. return;
  688. }
  689. double sx, sy;
  690. struct wlr_surface *surface = NULL;
  691. struct mjc_view *view = desktop_view_at(server,
  692. server->cursor->x, server->cursor->y, &surface, &sx, &sy);
  693. if (view != NULL && view->focusable) {
  694. mjc_view_focus(view);
  695. }
  696. }
  697. }
  698. static void server_cursor_axis(struct wl_listener *listener, void *data) {
  699. struct mjc_server *server = wl_container_of(listener, server, cursor_axis);
  700. struct wlr_pointer_axis_event *event = data;
  701. wlr_seat_pointer_notify_axis(server->seat,
  702. event->time_msec, event->orientation, event->delta,
  703. event->delta_discrete, event->source, event->relative_direction);
  704. }
  705. static void server_cursor_frame(struct wl_listener *listener, void *data) {
  706. (void) data;
  707. struct mjc_server *server = wl_container_of(listener, server, cursor_frame);
  708. wlr_seat_pointer_notify_frame(server->seat);
  709. }
  710. /* ------------------------------------------------------------------ */
  711. /* interactive move/resize */
  712. /* ------------------------------------------------------------------ */
  713. static void view_current_geometry(struct mjc_view *view, struct wlr_box *box) {
  714. if (view->is_xwayland && view->xsurface != NULL) {
  715. box->x = view->scene_tree != NULL ? view->scene_tree->node.x : view->xsurface->x;
  716. box->y = view->scene_tree != NULL ? view->scene_tree->node.y : view->xsurface->y;
  717. box->width = view->xsurface->width;
  718. box->height = view->xsurface->height;
  719. } else {
  720. struct wlr_box geo;
  721. wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &geo);
  722. box->x = view->scene_tree->node.x + geo.x;
  723. box->y = view->scene_tree->node.y + geo.y;
  724. box->width = geo.width;
  725. box->height = geo.height;
  726. }
  727. }
  728. static void begin_interactive(struct mjc_view *view,
  729. enum mjc_cursor_mode mode, uint32_t edges) {
  730. struct mjc_server *server = view->server;
  731. if (view_surface(view) !=
  732. server->seat->pointer_state.focused_surface) {
  733. /* only react to requests from the focused client */
  734. return;
  735. }
  736. server->grabbed_view = view;
  737. server->cursor_mode = mode;
  738. struct wlr_box geo_box;
  739. view_current_geometry(view, &geo_box);
  740. if (mode == MJC_CURSOR_MOVE) {
  741. server->grab_x = server->cursor->x - view->scene_tree->node.x;
  742. server->grab_y = server->cursor->y - view->scene_tree->node.y;
  743. } else {
  744. double border_x = geo_box.x +
  745. ((edges & WLR_EDGE_RIGHT) ? geo_box.width : 0);
  746. double border_y = geo_box.y +
  747. ((edges & WLR_EDGE_BOTTOM) ? geo_box.height : 0);
  748. server->grab_x = server->cursor->x - border_x;
  749. server->grab_y = server->cursor->y - border_y;
  750. server->grab_geobox = geo_box;
  751. server->resize_edges = edges;
  752. }
  753. }
  754. /* ------------------------------------------------------------------ */
  755. /* server-side decorations */
  756. /* ------------------------------------------------------------------ */
  757. /* a wlr_buffer backed by a cairo ARGB32 image surface. cairo ARGB32 is
  758. * premultiplied and laid out as B,G,R,A on little-endian, which matches
  759. * DRM_FORMAT_ARGB8888 — so the scene can upload it directly as a texture. */
  760. struct mjc_cairo_buffer {
  761. struct wlr_buffer base;
  762. cairo_surface_t *surface;
  763. };
  764. static void cairo_buffer_destroy(struct wlr_buffer *wlr_buffer) {
  765. struct mjc_cairo_buffer *buf = wl_container_of(wlr_buffer, buf, base);
  766. cairo_surface_destroy(buf->surface);
  767. free(buf);
  768. }
  769. static bool cairo_buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer,
  770. uint32_t flags, void **data, uint32_t *format, size_t *stride) {
  771. struct mjc_cairo_buffer *buf = wl_container_of(wlr_buffer, buf, base);
  772. if (flags & WLR_BUFFER_DATA_PTR_ACCESS_WRITE) {
  773. return false;
  774. }
  775. *data = cairo_image_surface_get_data(buf->surface);
  776. *format = DRM_FORMAT_ARGB8888;
  777. *stride = (size_t) cairo_image_surface_get_stride(buf->surface);
  778. return true;
  779. }
  780. static void cairo_buffer_end_data_ptr_access(struct wlr_buffer *wlr_buffer) {
  781. (void) wlr_buffer;
  782. }
  783. static const struct wlr_buffer_impl cairo_buffer_impl = {
  784. .destroy = cairo_buffer_destroy,
  785. .begin_data_ptr_access = cairo_buffer_begin_data_ptr_access,
  786. .end_data_ptr_access = cairo_buffer_end_data_ptr_access,
  787. };
  788. static struct mjc_cairo_buffer *cairo_buffer_create(int width, int height) {
  789. struct mjc_cairo_buffer *buf = calloc(1, sizeof(*buf));
  790. if (buf == NULL) {
  791. return NULL;
  792. }
  793. buf->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
  794. if (cairo_surface_status(buf->surface) != CAIRO_STATUS_SUCCESS) {
  795. cairo_surface_destroy(buf->surface);
  796. free(buf);
  797. return NULL;
  798. }
  799. wlr_buffer_init(&buf->base, &cairo_buffer_impl, width, height);
  800. return buf;
  801. }
  802. /* content-surface offset inside the view's scene_tree (xdg windows carry a
  803. * geometry offset for their CSD margins; xwayland surfaces sit at 0,0) */
  804. static void view_geo_offset(struct mjc_view *view, int *ox, int *oy) {
  805. if (view->is_xwayland) {
  806. *ox = 0;
  807. *oy = 0;
  808. } else {
  809. struct wlr_box geo;
  810. wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &geo);
  811. *ox = geo.x;
  812. *oy = geo.y;
  813. }
  814. }
  815. static void view_content_size(struct mjc_view *view, int *w, int *h) {
  816. struct wlr_box box;
  817. view_current_geometry(view, &box);
  818. *w = box.width;
  819. *h = box.height;
  820. }
  821. /* center x of titlebar button i (0=min, 1=max, 2=close), right-aligned GNOME order */
  822. static int deco_button_cx(int width, int i) {
  823. int step = 2 * MJC_DECO_BTN_R + MJC_DECO_BTN_GAP;
  824. int close_cx = width - MJC_DECO_PAD - MJC_DECO_BTN_R;
  825. return close_cx - (2 - i) * step;
  826. }
  827. static bool view_should_decorate(struct mjc_view *view) {
  828. if (!view->mapped || view->scene_tree == NULL) {
  829. return false;
  830. }
  831. /* chrome (shell wallpaper/panels/menus) and chromeless windows get no frame;
  832. * everything else is a normal app window and is decorated in any layer, so
  833. * always-on-top / always-on-bottom apps keep their titlebar */
  834. if (view->is_chrome || view->deco_disabled) {
  835. return false;
  836. }
  837. if (view->is_xwayland && view->xsurface != NULL &&
  838. view->xsurface->override_redirect) {
  839. return false;
  840. }
  841. return true;
  842. }
  843. /* a path with rounded top corners and square bottom corners */
  844. static void deco_rounded_top(cairo_t *cr, double x, double y,
  845. double w, double h, double r) {
  846. cairo_new_sub_path(cr);
  847. cairo_arc(cr, x + r, y + r, r, M_PI, 1.5 * M_PI);
  848. cairo_arc(cr, x + w - r, y + r, r, 1.5 * M_PI, 2.0 * M_PI);
  849. cairo_line_to(cr, x + w, y + h);
  850. cairo_line_to(cr, x, y + h);
  851. cairo_close_path(cr);
  852. }
  853. /* render the titlebar of `width` px into a fresh cairo buffer and hand it to the
  854. * scene node (the scene takes a ref; we drop ours) */
  855. static void deco_render(struct mjc_view *view, int width) {
  856. if (view->deco_titlebar == NULL || width <= 0) {
  857. return;
  858. }
  859. struct mjc_server *server = view->server;
  860. int h = MJC_DECO_TITLEBAR_H;
  861. struct mjc_cairo_buffer *buf = cairo_buffer_create(width, h);
  862. if (buf == NULL) {
  863. return;
  864. }
  865. cairo_t *cr = cairo_create(buf->surface);
  866. cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
  867. cairo_paint(cr);
  868. cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
  869. const float *bg = server->deco_bg;
  870. const float *fg = server->deco_fg;
  871. bool active = (server->focused_view == view);
  872. double dim = active ? 0.0 : 0.10;
  873. /* titlebar fill: soft vertical gradient derived from the theme bg */
  874. cairo_pattern_t *grad = cairo_pattern_create_linear(0, 0, 0, h);
  875. cairo_pattern_add_color_stop_rgb(grad, 0.0,
  876. fmin(bg[0] + 0.10 - dim, 1.0), fmin(bg[1] + 0.10 - dim, 1.0),
  877. fmin(bg[2] + 0.10 - dim, 1.0));
  878. cairo_pattern_add_color_stop_rgb(grad, 1.0,
  879. fmax(bg[0] - dim, 0.0), fmax(bg[1] - dim, 0.0), fmax(bg[2] - dim, 0.0));
  880. deco_rounded_top(cr, 0, 0, width, h, MJC_DECO_RADIUS);
  881. cairo_set_source(cr, grad);
  882. cairo_fill(cr);
  883. cairo_pattern_destroy(grad);
  884. /* hairline top highlight for depth */
  885. cairo_set_source_rgba(cr, 1, 1, 1, active ? 0.10 : 0.05);
  886. cairo_set_line_width(cr, 1.0);
  887. deco_rounded_top(cr, 0.5, 0.5, width - 1, h, MJC_DECO_RADIUS);
  888. cairo_stroke(cr);
  889. /* title text (cairo toy font api, no pango dependency) */
  890. const char *title = mjc_view_title(view);
  891. if (title != NULL && title[0] != '\0') {
  892. cairo_select_font_face(cr, "Sans",
  893. CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
  894. cairo_set_font_size(cr, 13.0);
  895. cairo_font_extents_t fe;
  896. cairo_font_extents(cr, &fe);
  897. double clip_r = deco_button_cx(width, 0) - MJC_DECO_BTN_R - 8;
  898. cairo_save(cr);
  899. cairo_rectangle(cr, 0, 0, clip_r > 0 ? clip_r : 0, h);
  900. cairo_clip(cr);
  901. cairo_set_source_rgba(cr, fg[0], fg[1], fg[2], active ? 0.95 : 0.6);
  902. cairo_move_to(cr, MJC_DECO_PAD, (h + fe.ascent - fe.descent) / 2.0);
  903. cairo_show_text(cr, title);
  904. cairo_restore(cr);
  905. }
  906. /* three GNOME-style circular buttons: min, max, close */
  907. double cy = h / 2.0;
  908. for (int i = 0; i < 3; i++) {
  909. double cx = deco_button_cx(width, i);
  910. bool hov = (view->hovered_button == i);
  911. bool is_close = (i == 2);
  912. if (is_close && hov) {
  913. cairo_set_source_rgba(cr, 0.86, 0.22, 0.20, 1.0);
  914. } else {
  915. double a = hov ? 0.22 : 0.12;
  916. cairo_set_source_rgba(cr, fg[0], fg[1], fg[2], active ? a : a * 0.6);
  917. }
  918. cairo_arc(cr, cx, cy, MJC_DECO_BTN_R, 0, 2 * M_PI);
  919. cairo_fill(cr);
  920. if (is_close && hov) {
  921. cairo_set_source_rgba(cr, 1, 1, 1, 1.0);
  922. } else {
  923. cairo_set_source_rgba(cr, fg[0], fg[1], fg[2], active ? 0.92 : 0.55);
  924. }
  925. cairo_set_line_width(cr, 1.4);
  926. double g = 4.0;
  927. if (i == 0) { /* minimize: bottom dash */
  928. cairo_move_to(cr, cx - g, cy + 2.5);
  929. cairo_line_to(cr, cx + g, cy + 2.5);
  930. cairo_stroke(cr);
  931. } else if (i == 1) { /* maximize: square */
  932. cairo_rectangle(cr, cx - g, cy - g, 2 * g, 2 * g);
  933. cairo_stroke(cr);
  934. } else { /* close: x */
  935. cairo_move_to(cr, cx - g, cy - g);
  936. cairo_line_to(cr, cx + g, cy + g);
  937. cairo_move_to(cr, cx + g, cy - g);
  938. cairo_line_to(cr, cx - g, cy + g);
  939. cairo_stroke(cr);
  940. }
  941. }
  942. cairo_destroy(cr);
  943. cairo_surface_flush(buf->surface);
  944. wlr_scene_buffer_set_buffer(view->deco_titlebar, &buf->base);
  945. wlr_buffer_drop(&buf->base);
  946. }
  947. static void deco_repaint(struct mjc_view *view) {
  948. if (view->decorated && view->deco_w > 0) {
  949. deco_render(view, view->deco_w);
  950. }
  951. }
  952. static void view_decoration_destroy(struct mjc_view *view) {
  953. if (view->deco_titlebar != NULL) {
  954. wlr_scene_node_destroy(&view->deco_titlebar->node);
  955. view->deco_titlebar = NULL;
  956. }
  957. for (int i = 0; i < 3; i++) {
  958. if (view->deco_border[i] != NULL) {
  959. wlr_scene_node_destroy(&view->deco_border[i]->node);
  960. view->deco_border[i] = NULL;
  961. }
  962. }
  963. view->decorated = false;
  964. view->deco_w = 0;
  965. view->deco_h = 0;
  966. view->hovered_button = -1;
  967. }
  968. /* create/refresh/remove a window's decoration to match its current state */
  969. static void view_decoration_update(struct mjc_view *view) {
  970. if (!view_should_decorate(view)) {
  971. view_decoration_destroy(view);
  972. return;
  973. }
  974. int w, h, ox, oy;
  975. view_content_size(view, &w, &h);
  976. view_geo_offset(view, &ox, &oy);
  977. if (w <= 0 || h <= 0) {
  978. return;
  979. }
  980. struct mjc_server *server = view->server;
  981. int tb = MJC_DECO_TITLEBAR_H;
  982. int b = MJC_DECO_BORDER;
  983. if (view->deco_titlebar == NULL) {
  984. view->deco_titlebar = wlr_scene_buffer_create(view->scene_tree, NULL);
  985. const float border_col[4] = {
  986. server->deco_bg[0] * 0.6f, server->deco_bg[1] * 0.6f,
  987. server->deco_bg[2] * 0.6f, 1.0f,
  988. };
  989. for (int i = 0; i < 3; i++) {
  990. view->deco_border[i] =
  991. wlr_scene_rect_create(view->scene_tree, 1, 1, border_col);
  992. }
  993. view->hovered_button = -1;
  994. view->decorated = true;
  995. view->deco_w = 0;
  996. }
  997. if (w != view->deco_w) {
  998. deco_render(view, w);
  999. }
  1000. view->deco_w = w;
  1001. view->deco_h = h;
  1002. /* place relative to the content surface inside scene_tree; the titlebar sits
  1003. * directly above the content, the borders frame the sides and bottom */
  1004. wlr_scene_node_set_position(&view->deco_titlebar->node, ox, oy - tb);
  1005. wlr_scene_rect_set_size(view->deco_border[0], b, tb + h);
  1006. wlr_scene_node_set_position(&view->deco_border[0]->node, ox - b, oy - tb);
  1007. wlr_scene_rect_set_size(view->deco_border[1], b, tb + h);
  1008. wlr_scene_node_set_position(&view->deco_border[1]->node, ox + w, oy - tb);
  1009. wlr_scene_rect_set_size(view->deco_border[2], w + 2 * b, b);
  1010. wlr_scene_node_set_position(&view->deco_border[2]->node, ox - b, oy + h);
  1011. }
  1012. /* layout-space top-left and size of a decorated view's titlebar */
  1013. static bool deco_titlebar_box(struct mjc_view *view,
  1014. double *tx, double *ty, int *w) {
  1015. if (!view->decorated || view->deco_titlebar == NULL) {
  1016. return false;
  1017. }
  1018. int ox, oy, h;
  1019. view_geo_offset(view, &ox, &oy);
  1020. view_content_size(view, w, &h);
  1021. *tx = view->scene_tree->node.x + ox;
  1022. *ty = view->scene_tree->node.y + oy - MJC_DECO_TITLEBAR_H;
  1023. return true;
  1024. }
  1025. /* button index (0..2) under (lx,ly), or -1 if over the titlebar but not a button */
  1026. static int deco_button_index(double tx, double ty, int w, double lx, double ly) {
  1027. double cy = ty + MJC_DECO_TITLEBAR_H / 2.0;
  1028. for (int i = 0; i < 3; i++) {
  1029. double cx = tx + deco_button_cx(w, i);
  1030. double dx = lx - cx, dy = ly - cy;
  1031. double rr = (MJC_DECO_BTN_R + 2) * (MJC_DECO_BTN_R + 2);
  1032. if (dx * dx + dy * dy <= rr) {
  1033. return i;
  1034. }
  1035. }
  1036. return -1;
  1037. }
  1038. /* resize-edge mask under (lx,ly) for the first decorated, non-maximized window
  1039. * whose frame border zone the pointer is in; 0 when the pointer is not on an edge */
  1040. static uint32_t deco_resize_edges(struct mjc_server *server,
  1041. double lx, double ly, struct mjc_view **out) {
  1042. struct mjc_view *view;
  1043. wl_list_for_each(view, &server->views, link) {
  1044. if (!view->decorated || view->scene_tree == NULL || view->maximized) {
  1045. continue;
  1046. }
  1047. int ox, oy, w, h;
  1048. view_geo_offset(view, &ox, &oy);
  1049. view_content_size(view, &w, &h);
  1050. double x0 = view->scene_tree->node.x + ox - MJC_DECO_BORDER;
  1051. double y0 = view->scene_tree->node.y + oy - MJC_DECO_TITLEBAR_H;
  1052. double x1 = view->scene_tree->node.x + ox + w + MJC_DECO_BORDER;
  1053. double y1 = view->scene_tree->node.y + oy + h + MJC_DECO_BORDER;
  1054. double r = MJC_DECO_RESIZE;
  1055. if (lx < x0 - r || lx > x1 + r || ly < y0 - r || ly > y1 + r) {
  1056. continue;
  1057. }
  1058. uint32_t edges = 0;
  1059. if (ly <= y0 + r) {
  1060. edges |= WLR_EDGE_TOP;
  1061. } else if (ly >= y1 - r) {
  1062. edges |= WLR_EDGE_BOTTOM;
  1063. }
  1064. if (lx <= x0 + r) {
  1065. edges |= WLR_EDGE_LEFT;
  1066. } else if (lx >= x1 - r) {
  1067. edges |= WLR_EDGE_RIGHT;
  1068. }
  1069. if (edges != 0) {
  1070. if (out != NULL) {
  1071. *out = view;
  1072. }
  1073. return edges;
  1074. }
  1075. }
  1076. return 0;
  1077. }
  1078. static const char *deco_resize_cursor(uint32_t edges) {
  1079. bool top = edges & WLR_EDGE_TOP, bottom = edges & WLR_EDGE_BOTTOM;
  1080. bool left = edges & WLR_EDGE_LEFT, right = edges & WLR_EDGE_RIGHT;
  1081. if (top && left) return "nw-resize";
  1082. if (top && right) return "ne-resize";
  1083. if (bottom && left) return "sw-resize";
  1084. if (bottom && right) return "se-resize";
  1085. if (top) return "n-resize";
  1086. if (bottom) return "s-resize";
  1087. if (left) return "w-resize";
  1088. if (right) return "e-resize";
  1089. return "default";
  1090. }
  1091. static void deco_update_hover(struct mjc_server *server) {
  1092. struct mjc_view *view;
  1093. wl_list_for_each(view, &server->views, link) {
  1094. double tx, ty;
  1095. int w;
  1096. if (!deco_titlebar_box(view, &tx, &ty, &w)) {
  1097. continue;
  1098. }
  1099. int want = -1;
  1100. if (server->cursor->x >= tx && server->cursor->x < tx + w &&
  1101. server->cursor->y >= ty &&
  1102. server->cursor->y < ty + MJC_DECO_TITLEBAR_H) {
  1103. want = deco_button_index(tx, ty, w, server->cursor->x, server->cursor->y);
  1104. }
  1105. if (want != view->hovered_button) {
  1106. view->hovered_button = want;
  1107. deco_repaint(view);
  1108. }
  1109. }
  1110. }
  1111. static bool deco_handle_button(struct mjc_server *server,
  1112. double lx, double ly, bool pressed) {
  1113. if (!pressed) {
  1114. return false;
  1115. }
  1116. /* edge/corner of a window frame => start an interactive resize. Mirrors the
  1117. * RESIZE branch of begin_interactive but without the focused-surface guard
  1118. * (the pointer is over the decoration, not the client surface). */
  1119. struct mjc_view *rv = NULL;
  1120. uint32_t redges = deco_resize_edges(server, lx, ly, &rv);
  1121. if (redges != 0 && rv != NULL) {
  1122. if (rv->focusable) {
  1123. mjc_view_focus(rv);
  1124. }
  1125. struct wlr_box geo_box;
  1126. view_current_geometry(rv, &geo_box);
  1127. double border_x = geo_box.x +
  1128. ((redges & WLR_EDGE_RIGHT) ? geo_box.width : 0);
  1129. double border_y = geo_box.y +
  1130. ((redges & WLR_EDGE_BOTTOM) ? geo_box.height : 0);
  1131. server->grabbed_view = rv;
  1132. server->cursor_mode = MJC_CURSOR_RESIZE;
  1133. server->grab_x = server->cursor->x - border_x;
  1134. server->grab_y = server->cursor->y - border_y;
  1135. server->grab_geobox = geo_box;
  1136. server->resize_edges = redges;
  1137. return true;
  1138. }
  1139. struct mjc_view *view;
  1140. wl_list_for_each(view, &server->views, link) {
  1141. double tx, ty;
  1142. int w;
  1143. if (!deco_titlebar_box(view, &tx, &ty, &w)) {
  1144. continue;
  1145. }
  1146. if (lx < tx || lx >= tx + w || ly < ty ||
  1147. ly >= ty + MJC_DECO_TITLEBAR_H) {
  1148. continue;
  1149. }
  1150. int btn = deco_button_index(tx, ty, w, lx, ly);
  1151. if (view->focusable) {
  1152. mjc_view_focus(view);
  1153. }
  1154. if (btn == 0) {
  1155. mjc_view_set_minimized(view, true);
  1156. } else if (btn == 1) {
  1157. mjc_view_set_maximized(view, !view->maximized);
  1158. } else if (btn == 2) {
  1159. mjc_view_close(view);
  1160. } else {
  1161. /* drag the titlebar => interactive move (no focused-surface guard
  1162. * because the pointer is over the decoration, not the client) */
  1163. server->grabbed_view = view;
  1164. server->cursor_mode = MJC_CURSOR_MOVE;
  1165. server->grab_x = server->cursor->x - view->scene_tree->node.x;
  1166. server->grab_y = server->cursor->y - view->scene_tree->node.y;
  1167. }
  1168. return true;
  1169. }
  1170. return false;
  1171. }
  1172. /* ------------------------------------------------------------------ */
  1173. /* view common */
  1174. /* ------------------------------------------------------------------ */
  1175. /* places a freshly mapped floating view in the middle of the output */
  1176. static void view_center(struct mjc_view *view) {
  1177. struct mjc_server *server = view->server;
  1178. if (view->scene_tree == NULL || view->maximized) {
  1179. return;
  1180. }
  1181. struct wlr_box out;
  1182. wlr_output_layout_get_box(server->output_layout, NULL, &out);
  1183. struct wlr_box geo;
  1184. view_current_geometry(view, &geo);
  1185. if (out.width <= 0 || geo.width <= 0) {
  1186. return;
  1187. }
  1188. int x = out.x + (out.width - geo.width) / 2;
  1189. int y = out.y + (out.height - geo.height) / 2;
  1190. if (x < out.x) x = out.x;
  1191. if (y < out.y) y = out.y;
  1192. if (view->is_xwayland) {
  1193. mjc_view_set_position(view, x, y);
  1194. } else {
  1195. struct wlr_box off;
  1196. wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &off);
  1197. wlr_scene_node_set_position(&view->scene_tree->node, x - off.x, y - off.y);
  1198. }
  1199. }
  1200. static void view_emit_new(struct mjc_view *view) {
  1201. if (view->server->cbs.view_new != NULL) {
  1202. view->server->cbs.view_new(view->server->ud, view, view->is_xwayland);
  1203. }
  1204. }
  1205. static void view_emit_map(struct mjc_view *view) {
  1206. if (view->server->cbs.view_map != NULL) {
  1207. view->server->cbs.view_map(view->server->ud, view);
  1208. }
  1209. }
  1210. static void view_emit_unmap(struct mjc_view *view) {
  1211. if (view->server->cbs.view_unmap != NULL) {
  1212. view->server->cbs.view_unmap(view->server->ud, view);
  1213. }
  1214. }
  1215. static void view_emit_destroy(struct mjc_view *view) {
  1216. if (view->server->cbs.view_destroy != NULL) {
  1217. view->server->cbs.view_destroy(view->server->ud, view);
  1218. }
  1219. }
  1220. static struct mjc_view *view_create(struct mjc_server *server, bool is_xwayland) {
  1221. struct mjc_view *view = calloc(1, sizeof(*view));
  1222. view->server = server;
  1223. view->id = ++server->next_view_id;
  1224. view->is_xwayland = is_xwayland;
  1225. view->focusable = true;
  1226. view->layer = MJC_LAYER_NORMAL;
  1227. wl_list_insert(&server->views, &view->link);
  1228. return view;
  1229. }
  1230. static void view_free(struct mjc_view *view) {
  1231. clear_focus_if(view->server, view);
  1232. view_emit_destroy(view);
  1233. wl_list_remove(&view->link);
  1234. free(view);
  1235. }
  1236. /* ------------------------------------------------------------------ */
  1237. /* xdg shell views */
  1238. /* ------------------------------------------------------------------ */
  1239. static void xdg_toplevel_map(struct wl_listener *listener, void *data) {
  1240. (void) data;
  1241. struct mjc_view *view = wl_container_of(listener, view, map);
  1242. view->mapped = true;
  1243. view_center(view);
  1244. view_emit_map(view); /* policy assigns the layer here (synchronous) */
  1245. mjc_view_focus(view);
  1246. view_decoration_update(view);
  1247. }
  1248. static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) {
  1249. (void) data;
  1250. struct mjc_view *view = wl_container_of(listener, view, unmap);
  1251. view->mapped = false;
  1252. clear_focus_if(view->server, view);
  1253. view_decoration_destroy(view);
  1254. view_emit_unmap(view);
  1255. }
  1256. static void xdg_toplevel_commit(struct wl_listener *listener, void *data) {
  1257. (void) data;
  1258. struct mjc_view *view = wl_container_of(listener, view, commit);
  1259. if (view->xdg_toplevel->base->initial_commit) {
  1260. if (view->xdg_toplevel->requested.maximized) {
  1261. mjc_view_set_maximized(view, true);
  1262. } else {
  1263. /* 0x0 lets the client pick its own (or remembered) size */
  1264. wlr_xdg_toplevel_set_size(view->xdg_toplevel, 0, 0);
  1265. }
  1266. return;
  1267. }
  1268. if (view->pending_center && view->mapped && !view->maximized &&
  1269. !view->xdg_toplevel->current.maximized) {
  1270. struct wlr_box geo;
  1271. wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &geo);
  1272. if (geo.width > 0) {
  1273. view->pending_center = false;
  1274. if (view->saved_geo.width > 0 && view->scene_tree != NULL) {
  1275. /* saved coords are visible coords, geo.x/y the offsets */
  1276. wlr_scene_node_set_position(&view->scene_tree->node,
  1277. view->saved_geo.x - geo.x, view->saved_geo.y - geo.y);
  1278. } else {
  1279. view_center(view);
  1280. }
  1281. }
  1282. }
  1283. /* keep the frame sized/placed to the (possibly just-resized) window */
  1284. if (view->decorated || view_should_decorate(view)) {
  1285. view_decoration_update(view);
  1286. }
  1287. }
  1288. static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) {
  1289. (void) data;
  1290. struct mjc_view *view = wl_container_of(listener, view, destroy);
  1291. wl_list_remove(&view->map.link);
  1292. wl_list_remove(&view->unmap.link);
  1293. wl_list_remove(&view->commit.link);
  1294. wl_list_remove(&view->destroy.link);
  1295. wl_list_remove(&view->request_move.link);
  1296. wl_list_remove(&view->request_resize.link);
  1297. wl_list_remove(&view->request_maximize.link);
  1298. wl_list_remove(&view->request_minimize.link);
  1299. wl_list_remove(&view->request_fullscreen.link);
  1300. wl_list_remove(&view->set_title.link);
  1301. wl_list_remove(&view->set_app_id.link);
  1302. view_free(view);
  1303. }
  1304. static void xdg_toplevel_request_move(struct wl_listener *listener, void *data) {
  1305. (void) data;
  1306. struct mjc_view *view = wl_container_of(listener, view, request_move);
  1307. begin_interactive(view, MJC_CURSOR_MOVE, 0);
  1308. }
  1309. static void xdg_toplevel_request_resize(struct wl_listener *listener, void *data) {
  1310. struct wlr_xdg_toplevel_resize_event *event = data;
  1311. struct mjc_view *view = wl_container_of(listener, view, request_resize);
  1312. begin_interactive(view, MJC_CURSOR_RESIZE, event->edges);
  1313. }
  1314. static void xdg_toplevel_request_maximize(struct wl_listener *listener, void *data) {
  1315. (void) data;
  1316. struct mjc_view *view = wl_container_of(listener, view, request_maximize);
  1317. if (view->xdg_toplevel->base->initialized) {
  1318. mjc_view_set_maximized(view, view->xdg_toplevel->requested.maximized);
  1319. wlr_xdg_surface_schedule_configure(view->xdg_toplevel->base);
  1320. }
  1321. }
  1322. static void xdg_toplevel_request_minimize(struct wl_listener *listener, void *data) {
  1323. (void) data;
  1324. struct mjc_view *view = wl_container_of(listener, view, request_minimize);
  1325. mjc_view_set_minimized(view, true);
  1326. }
  1327. static void xdg_toplevel_request_fullscreen(struct wl_listener *listener, void *data) {
  1328. (void) data;
  1329. struct mjc_view *view = wl_container_of(listener, view, request_fullscreen);
  1330. if (view->xdg_toplevel->base->initialized) {
  1331. wlr_xdg_surface_schedule_configure(view->xdg_toplevel->base);
  1332. }
  1333. }
  1334. static void xdg_toplevel_set_title(struct wl_listener *listener, void *data) {
  1335. (void) data;
  1336. struct mjc_view *view = wl_container_of(listener, view, set_title);
  1337. deco_repaint(view);
  1338. if (view->server->cbs.view_title != NULL) {
  1339. view->server->cbs.view_title(view->server->ud, view,
  1340. view->xdg_toplevel->title);
  1341. }
  1342. }
  1343. static void xdg_toplevel_set_app_id(struct wl_listener *listener, void *data) {
  1344. (void) data;
  1345. struct mjc_view *view = wl_container_of(listener, view, set_app_id);
  1346. if (view->server->cbs.view_app_id != NULL) {
  1347. view->server->cbs.view_app_id(view->server->ud, view,
  1348. view->xdg_toplevel->app_id);
  1349. }
  1350. }
  1351. static void server_new_xdg_toplevel(struct wl_listener *listener, void *data) {
  1352. struct mjc_server *server =
  1353. wl_container_of(listener, server, new_xdg_toplevel);
  1354. struct wlr_xdg_toplevel *xdg_toplevel = data;
  1355. struct mjc_view *view = view_create(server, false);
  1356. view->xdg_toplevel = xdg_toplevel;
  1357. /* wayland toplevels default to frameless: a client that draws its own CSD (GTK)
  1358. * never negotiates xdg-decoration, and adding a server frame would double it up.
  1359. * The frame is enabled only when the client requests SERVER_SIDE (Qt, libdecor,
  1360. * SDL…) in decoration_request_mode. xwayland/X11 windows have no CSD and are
  1361. * always decorated. */
  1362. view->deco_disabled = true;
  1363. view->scene_tree = wlr_scene_xdg_surface_create(
  1364. server->layers[MJC_LAYER_NORMAL], xdg_toplevel->base);
  1365. view->scene_tree->node.data = view;
  1366. xdg_toplevel->base->data = view->scene_tree;
  1367. view->map.notify = xdg_toplevel_map;
  1368. wl_signal_add(&xdg_toplevel->base->surface->events.map, &view->map);
  1369. view->unmap.notify = xdg_toplevel_unmap;
  1370. wl_signal_add(&xdg_toplevel->base->surface->events.unmap, &view->unmap);
  1371. view->commit.notify = xdg_toplevel_commit;
  1372. wl_signal_add(&xdg_toplevel->base->surface->events.commit, &view->commit);
  1373. view->destroy.notify = xdg_toplevel_destroy;
  1374. wl_signal_add(&xdg_toplevel->events.destroy, &view->destroy);
  1375. view->request_move.notify = xdg_toplevel_request_move;
  1376. wl_signal_add(&xdg_toplevel->events.request_move, &view->request_move);
  1377. view->request_resize.notify = xdg_toplevel_request_resize;
  1378. wl_signal_add(&xdg_toplevel->events.request_resize, &view->request_resize);
  1379. view->request_maximize.notify = xdg_toplevel_request_maximize;
  1380. wl_signal_add(&xdg_toplevel->events.request_maximize, &view->request_maximize);
  1381. view->request_minimize.notify = xdg_toplevel_request_minimize;
  1382. wl_signal_add(&xdg_toplevel->events.request_minimize, &view->request_minimize);
  1383. view->request_fullscreen.notify = xdg_toplevel_request_fullscreen;
  1384. wl_signal_add(&xdg_toplevel->events.request_fullscreen, &view->request_fullscreen);
  1385. view->set_title.notify = xdg_toplevel_set_title;
  1386. wl_signal_add(&xdg_toplevel->events.set_title, &view->set_title);
  1387. view->set_app_id.notify = xdg_toplevel_set_app_id;
  1388. wl_signal_add(&xdg_toplevel->events.set_app_id, &view->set_app_id);
  1389. view_emit_new(view);
  1390. }
  1391. /* popups */
  1392. static void xdg_popup_commit(struct wl_listener *listener, void *data) {
  1393. (void) data;
  1394. struct mjc_popup *popup = wl_container_of(listener, popup, commit);
  1395. if (popup->xdg_popup->base->initial_commit) {
  1396. wlr_xdg_surface_schedule_configure(popup->xdg_popup->base);
  1397. }
  1398. }
  1399. static void xdg_popup_destroy(struct wl_listener *listener, void *data) {
  1400. (void) data;
  1401. struct mjc_popup *popup = wl_container_of(listener, popup, destroy);
  1402. wl_list_remove(&popup->commit.link);
  1403. wl_list_remove(&popup->destroy.link);
  1404. free(popup);
  1405. }
  1406. static void server_new_xdg_popup(struct wl_listener *listener, void *data) {
  1407. (void) listener;
  1408. struct wlr_xdg_popup *xdg_popup = data;
  1409. struct wlr_xdg_surface *parent =
  1410. wlr_xdg_surface_try_from_wlr_surface(xdg_popup->parent);
  1411. if (parent == NULL || parent->data == NULL) {
  1412. return;
  1413. }
  1414. struct wlr_scene_tree *parent_tree = parent->data;
  1415. xdg_popup->base->data =
  1416. wlr_scene_xdg_surface_create(parent_tree, xdg_popup->base);
  1417. struct mjc_popup *popup = calloc(1, sizeof(*popup));
  1418. popup->xdg_popup = xdg_popup;
  1419. popup->commit.notify = xdg_popup_commit;
  1420. wl_signal_add(&xdg_popup->base->surface->events.commit, &popup->commit);
  1421. popup->destroy.notify = xdg_popup_destroy;
  1422. wl_signal_add(&xdg_popup->events.destroy, &popup->destroy);
  1423. }
  1424. /* decorations - the compositor draws GNOME-style server-side frames. We grant
  1425. * SERVER_SIDE unless the client explicitly demands client-side decorations, in
  1426. * which case we honor it and leave that window chromeless (no server frame). */
  1427. static void decoration_request_mode(struct wl_listener *listener, void *data) {
  1428. (void) data;
  1429. struct mjc_decoration *deco = wl_container_of(listener, deco, request_mode);
  1430. if (!deco->decoration->toplevel->base->initialized) {
  1431. return;
  1432. }
  1433. bool client_wants_csd = deco->decoration->requested_mode ==
  1434. WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
  1435. wlr_xdg_toplevel_decoration_v1_set_mode(deco->decoration, client_wants_csd
  1436. ? WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE
  1437. : WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
  1438. if (deco->view != NULL) {
  1439. deco->view->deco_disabled = client_wants_csd;
  1440. view_decoration_update(deco->view);
  1441. }
  1442. }
  1443. static void decoration_destroy(struct wl_listener *listener, void *data) {
  1444. (void) data;
  1445. struct mjc_decoration *deco = wl_container_of(listener, deco, destroy);
  1446. wl_list_remove(&deco->request_mode.link);
  1447. wl_list_remove(&deco->destroy.link);
  1448. free(deco);
  1449. }
  1450. static void server_new_toplevel_decoration(struct wl_listener *listener,
  1451. void *data) {
  1452. (void) listener;
  1453. struct wlr_xdg_toplevel_decoration_v1 *decoration = data;
  1454. struct mjc_decoration *deco = calloc(1, sizeof(*deco));
  1455. deco->decoration = decoration;
  1456. /* the view was stashed on the xdg surface in server_new_xdg_toplevel */
  1457. struct wlr_scene_tree *tree = decoration->toplevel->base->data;
  1458. deco->view = tree != NULL ? tree->node.data : NULL;
  1459. deco->request_mode.notify = decoration_request_mode;
  1460. wl_signal_add(&decoration->events.request_mode, &deco->request_mode);
  1461. deco->destroy.notify = decoration_destroy;
  1462. wl_signal_add(&decoration->events.destroy, &deco->destroy);
  1463. decoration_request_mode(&deco->request_mode, NULL);
  1464. }
  1465. /* ------------------------------------------------------------------ */
  1466. /* xwayland views */
  1467. /* ------------------------------------------------------------------ */
  1468. static void xwayland_surface_map(struct wl_listener *listener, void *data) {
  1469. (void) data;
  1470. struct mjc_view *view = wl_container_of(listener, view, map);
  1471. struct mjc_server *server = view->server;
  1472. struct wlr_xwayland_surface *xsurface = view->xsurface;
  1473. mjc_layer layer = xsurface->override_redirect
  1474. ? MJC_LAYER_OVERLAY : view->layer;
  1475. view->scene_tree = wlr_scene_tree_create(server->layers[layer]);
  1476. view->scene_tree->node.data = view;
  1477. wlr_scene_surface_create(view->scene_tree, xsurface->surface);
  1478. wlr_scene_node_set_position(&view->scene_tree->node,
  1479. xsurface->x, xsurface->y);
  1480. view->mapped = true;
  1481. /* X clients that did not place themselves land at 0,0 - center those */
  1482. if (!xsurface->override_redirect && xsurface->x == 0 && xsurface->y == 0) {
  1483. view_center(view);
  1484. }
  1485. view_emit_map(view);
  1486. if (!xsurface->override_redirect) {
  1487. mjc_view_focus(view);
  1488. }
  1489. view_decoration_update(view);
  1490. }
  1491. static void xwayland_surface_unmap(struct wl_listener *listener, void *data) {
  1492. (void) data;
  1493. struct mjc_view *view = wl_container_of(listener, view, unmap);
  1494. view->mapped = false;
  1495. clear_focus_if(view->server, view);
  1496. /* emit while the scene node still exists so the policy side can
  1497. * read the last window geometry */
  1498. view_emit_unmap(view);
  1499. /* tear down the frame before the scene_tree (its children) is destroyed */
  1500. view_decoration_destroy(view);
  1501. if (view->scene_tree != NULL) {
  1502. wlr_scene_node_destroy(&view->scene_tree->node);
  1503. view->scene_tree = NULL;
  1504. }
  1505. }
  1506. static void xwayland_surface_associate(struct wl_listener *listener, void *data) {
  1507. (void) data;
  1508. struct mjc_view *view = wl_container_of(listener, view, associate);
  1509. view->map.notify = xwayland_surface_map;
  1510. wl_signal_add(&view->xsurface->surface->events.map, &view->map);
  1511. view->unmap.notify = xwayland_surface_unmap;
  1512. wl_signal_add(&view->xsurface->surface->events.unmap, &view->unmap);
  1513. }
  1514. static void xwayland_surface_dissociate(struct wl_listener *listener, void *data) {
  1515. (void) data;
  1516. struct mjc_view *view = wl_container_of(listener, view, dissociate);
  1517. wl_list_remove(&view->map.link);
  1518. wl_list_remove(&view->unmap.link);
  1519. }
  1520. static void xwayland_surface_destroy(struct wl_listener *listener, void *data) {
  1521. (void) data;
  1522. struct mjc_view *view = wl_container_of(listener, view, destroy);
  1523. wl_list_remove(&view->associate.link);
  1524. wl_list_remove(&view->dissociate.link);
  1525. wl_list_remove(&view->destroy.link);
  1526. wl_list_remove(&view->request_configure.link);
  1527. wl_list_remove(&view->request_move.link);
  1528. wl_list_remove(&view->request_resize.link);
  1529. wl_list_remove(&view->request_minimize.link);
  1530. wl_list_remove(&view->request_maximize.link);
  1531. wl_list_remove(&view->set_title.link);
  1532. wl_list_remove(&view->set_app_id.link);
  1533. wl_list_remove(&view->set_geometry.link);
  1534. view_decoration_destroy(view);
  1535. if (view->scene_tree != NULL) {
  1536. wlr_scene_node_destroy(&view->scene_tree->node);
  1537. view->scene_tree = NULL;
  1538. }
  1539. view_free(view);
  1540. }
  1541. static void xwayland_surface_request_configure(struct wl_listener *listener,
  1542. void *data) {
  1543. struct mjc_view *view = wl_container_of(listener, view, request_configure);
  1544. struct wlr_xwayland_surface_configure_event *event = data;
  1545. wlr_xwayland_surface_configure(view->xsurface,
  1546. event->x, event->y, event->width, event->height);
  1547. if (view->scene_tree != NULL) {
  1548. wlr_scene_node_set_position(&view->scene_tree->node, event->x, event->y);
  1549. }
  1550. if (view->decorated) {
  1551. view_decoration_update(view);
  1552. }
  1553. }
  1554. static void xwayland_surface_set_geometry(struct wl_listener *listener,
  1555. void *data) {
  1556. (void) data;
  1557. struct mjc_view *view = wl_container_of(listener, view, set_geometry);
  1558. if (view->scene_tree != NULL && view->xsurface != NULL) {
  1559. wlr_scene_node_set_position(&view->scene_tree->node,
  1560. view->xsurface->x, view->xsurface->y);
  1561. }
  1562. if (view->decorated) {
  1563. view_decoration_update(view);
  1564. }
  1565. }
  1566. static void xwayland_surface_request_move(struct wl_listener *listener,
  1567. void *data) {
  1568. (void) data;
  1569. struct mjc_view *view = wl_container_of(listener, view, request_move);
  1570. begin_interactive(view, MJC_CURSOR_MOVE, 0);
  1571. }
  1572. static void xwayland_surface_request_resize(struct wl_listener *listener,
  1573. void *data) {
  1574. struct mjc_view *view = wl_container_of(listener, view, request_resize);
  1575. struct wlr_xwayland_resize_event *event = data;
  1576. begin_interactive(view, MJC_CURSOR_RESIZE, event->edges);
  1577. }
  1578. static void xwayland_surface_request_minimize(struct wl_listener *listener,
  1579. void *data) {
  1580. (void) data;
  1581. struct mjc_view *view = wl_container_of(listener, view, request_minimize);
  1582. mjc_view_set_minimized(view, true);
  1583. }
  1584. static void xwayland_surface_request_maximize(struct wl_listener *listener,
  1585. void *data) {
  1586. (void) data;
  1587. struct mjc_view *view = wl_container_of(listener, view, request_maximize);
  1588. struct wlr_xwayland_surface *xsurface = view->xsurface;
  1589. if (xsurface == NULL) {
  1590. return;
  1591. }
  1592. /* the client announces the state it wants via _NET_WM_STATE */
  1593. mjc_view_set_maximized(view,
  1594. xsurface->maximized_vert && xsurface->maximized_horz);
  1595. }
  1596. static void xwayland_surface_set_title(struct wl_listener *listener, void *data) {
  1597. (void) data;
  1598. struct mjc_view *view = wl_container_of(listener, view, set_title);
  1599. deco_repaint(view);
  1600. if (view->server->cbs.view_title != NULL) {
  1601. view->server->cbs.view_title(view->server->ud, view,
  1602. view->xsurface->title);
  1603. }
  1604. }
  1605. static void xwayland_surface_set_class(struct wl_listener *listener, void *data) {
  1606. (void) data;
  1607. struct mjc_view *view = wl_container_of(listener, view, set_app_id);
  1608. if (view->server->cbs.view_app_id != NULL) {
  1609. view->server->cbs.view_app_id(view->server->ud, view,
  1610. view->xsurface->class);
  1611. }
  1612. }
  1613. static void server_new_xwayland_surface(struct wl_listener *listener, void *data) {
  1614. struct mjc_server *server =
  1615. wl_container_of(listener, server, new_xwayland_surface);
  1616. struct wlr_xwayland_surface *xsurface = data;
  1617. struct mjc_view *view = view_create(server, true);
  1618. view->xsurface = xsurface;
  1619. if (xsurface->override_redirect) {
  1620. view->focusable = false;
  1621. }
  1622. xsurface->data = view;
  1623. view->associate.notify = xwayland_surface_associate;
  1624. wl_signal_add(&xsurface->events.associate, &view->associate);
  1625. view->dissociate.notify = xwayland_surface_dissociate;
  1626. wl_signal_add(&xsurface->events.dissociate, &view->dissociate);
  1627. view->destroy.notify = xwayland_surface_destroy;
  1628. wl_signal_add(&xsurface->events.destroy, &view->destroy);
  1629. view->request_configure.notify = xwayland_surface_request_configure;
  1630. wl_signal_add(&xsurface->events.request_configure, &view->request_configure);
  1631. view->request_move.notify = xwayland_surface_request_move;
  1632. wl_signal_add(&xsurface->events.request_move, &view->request_move);
  1633. view->request_resize.notify = xwayland_surface_request_resize;
  1634. wl_signal_add(&xsurface->events.request_resize, &view->request_resize);
  1635. view->request_minimize.notify = xwayland_surface_request_minimize;
  1636. wl_signal_add(&xsurface->events.request_minimize, &view->request_minimize);
  1637. view->request_maximize.notify = xwayland_surface_request_maximize;
  1638. wl_signal_add(&xsurface->events.request_maximize, &view->request_maximize);
  1639. view->set_title.notify = xwayland_surface_set_title;
  1640. wl_signal_add(&xsurface->events.set_title, &view->set_title);
  1641. view->set_app_id.notify = xwayland_surface_set_class;
  1642. wl_signal_add(&xsurface->events.set_class, &view->set_app_id);
  1643. view->set_geometry.notify = xwayland_surface_set_geometry;
  1644. wl_signal_add(&xsurface->events.set_geometry, &view->set_geometry);
  1645. view_emit_new(view);
  1646. }
  1647. static void server_xwayland_ready(struct wl_listener *listener, void *data) {
  1648. (void) data;
  1649. struct mjc_server *server = wl_container_of(listener, server, xwayland_ready);
  1650. wlr_xwayland_set_seat(server->xwayland, server->seat);
  1651. }
  1652. /* ------------------------------------------------------------------ */
  1653. /* sigchld */
  1654. /* ------------------------------------------------------------------ */
  1655. static int handle_sigchld(int signal_number, void *data) {
  1656. (void) signal_number;
  1657. struct mjc_server *server = data;
  1658. for (int i = 0; i < server->spawned_count;) {
  1659. int status;
  1660. pid_t pid = waitpid(server->spawned_pids[i], &status, WNOHANG);
  1661. if (pid > 0) {
  1662. server->spawned_pids[i] =
  1663. server->spawned_pids[--server->spawned_count];
  1664. if (server->cbs.child_exit != NULL) {
  1665. server->cbs.child_exit(server->ud, (int) pid, status);
  1666. }
  1667. } else {
  1668. i++;
  1669. }
  1670. }
  1671. return 0;
  1672. }
  1673. /* True when the GLES2 renderer ended up on software rendering (llvmpipe/swrast) — i.e. there is
  1674. * no usable hardware GL. In that case the Compose shell (Skiko) cannot create an OpenGL/GLX
  1675. * context on the (also software) XWayland and must use its CPU raster backend, so the compositor
  1676. * exports SKIKO_RENDER_API=SOFTWARE for the shell. On a real GPU it returns false and the shell
  1677. * uses hardware GL. Queries GL_RENDERER via the renderer's own EGL context (surfaceless). */
  1678. static bool mjc_renderer_is_software(struct wlr_renderer *renderer) {
  1679. if (!wlr_renderer_is_gles2(renderer)) {
  1680. return false;
  1681. }
  1682. struct wlr_egl *egl = wlr_gles2_renderer_get_egl(renderer);
  1683. if (egl == NULL) {
  1684. return false;
  1685. }
  1686. EGLDisplay dpy = wlr_egl_get_display(egl);
  1687. EGLContext ctx = wlr_egl_get_context(egl);
  1688. if (dpy == EGL_NO_DISPLAY || ctx == EGL_NO_CONTEXT) {
  1689. return false;
  1690. }
  1691. if (!eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx)) {
  1692. return false;
  1693. }
  1694. const char *gl_renderer = (const char *) glGetString(GL_RENDERER);
  1695. bool software = gl_renderer != NULL && (
  1696. strstr(gl_renderer, "llvmpipe") != NULL ||
  1697. strstr(gl_renderer, "softpipe") != NULL ||
  1698. strstr(gl_renderer, "swrast") != NULL ||
  1699. strstr(gl_renderer, "SWR") != NULL ||
  1700. strstr(gl_renderer, "Software") != NULL);
  1701. eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
  1702. return software;
  1703. }
  1704. /* ------------------------------------------------------------------ */
  1705. /* public API */
  1706. /* ------------------------------------------------------------------ */
  1707. mjc_server *mjc_create(void) {
  1708. struct mjc_server *server = calloc(1, sizeof(*server));
  1709. wl_list_init(&server->views);
  1710. wl_list_init(&server->keyboards);
  1711. wl_list_init(&server->fd_sources);
  1712. return server;
  1713. }
  1714. bool mjc_start(mjc_server *server, const mjc_callbacks *callbacks, void *userdata) {
  1715. server->cbs = *callbacks;
  1716. server->ud = userdata;
  1717. /* default decoration theme (dark slate + near-white); the shell overrides this
  1718. * from its live background palette via mjc_set_decoration_theme over IPC */
  1719. server->deco_bg[0] = 0.16f;
  1720. server->deco_bg[1] = 0.17f;
  1721. server->deco_bg[2] = 0.20f;
  1722. server->deco_fg[0] = 0.92f;
  1723. server->deco_fg[1] = 0.93f;
  1724. server->deco_fg[2] = 0.95f;
  1725. wlr_log_init(WLR_INFO, NULL);
  1726. /* nested-output size, 16:9 default; override with MJDEVC_OUTPUT=WIDTHxHEIGHT */
  1727. server->output_w = 1280;
  1728. server->output_h = 720;
  1729. const char *out_env = getenv("MJDEVC_OUTPUT");
  1730. if (out_env != NULL) {
  1731. int w = 0, h = 0;
  1732. if (sscanf(out_env, "%dx%d", &w, &h) == 2 && w > 0 && h > 0) {
  1733. server->output_w = w;
  1734. server->output_h = h;
  1735. }
  1736. }
  1737. server->display = wl_display_create();
  1738. server->loop = wl_display_get_event_loop(server->display);
  1739. server->backend = wlr_backend_autocreate(server->loop, &server->session);
  1740. if (server->backend == NULL) {
  1741. fprintf(stderr, "mjdevc: failed to create wlr_backend\n");
  1742. return false;
  1743. }
  1744. server->renderer = wlr_renderer_autocreate(server->backend);
  1745. if (server->renderer == NULL) {
  1746. fprintf(stderr, "mjdevc: failed to create wlr_renderer\n");
  1747. return false;
  1748. }
  1749. wlr_renderer_init_wl_display(server->renderer, server->display);
  1750. server->allocator = wlr_allocator_autocreate(server->backend, server->renderer);
  1751. if (server->allocator == NULL) {
  1752. fprintf(stderr, "mjdevc: failed to create wlr_allocator\n");
  1753. return false;
  1754. }
  1755. server->compositor = wlr_compositor_create(server->display, 5, server->renderer);
  1756. wlr_subcompositor_create(server->display);
  1757. wlr_data_device_manager_create(server->display);
  1758. server->output_layout = wlr_output_layout_create(server->display);
  1759. server->new_output.notify = server_new_output;
  1760. wl_signal_add(&server->backend->events.new_output, &server->new_output);
  1761. server->scene = wlr_scene_create();
  1762. server->scene_layout =
  1763. wlr_scene_attach_output_layout(server->scene, server->output_layout);
  1764. for (int i = 0; i < 5; i++) {
  1765. server->layers[i] = wlr_scene_tree_create(&server->scene->tree);
  1766. }
  1767. /* injection feedback marker: a 16x16 white square in the overlay layer, hidden until
  1768. * input is injected, then shown at the pointer and faded out (Android show-touches style) */
  1769. const float marker_hidden[4] = {0.0f, 0.0f, 0.0f, 0.0f};
  1770. server->inject_marker = wlr_scene_rect_create(
  1771. server->layers[MJC_LAYER_OVERLAY], 16, 16, marker_hidden);
  1772. wlr_scene_node_set_enabled(&server->inject_marker->node, false);
  1773. server->xdg_shell = wlr_xdg_shell_create(server->display, 3);
  1774. server->new_xdg_toplevel.notify = server_new_xdg_toplevel;
  1775. wl_signal_add(&server->xdg_shell->events.new_toplevel, &server->new_xdg_toplevel);
  1776. server->new_xdg_popup.notify = server_new_xdg_popup;
  1777. wl_signal_add(&server->xdg_shell->events.new_popup, &server->new_xdg_popup);
  1778. server->xdg_decoration_mgr = wlr_xdg_decoration_manager_v1_create(server->display);
  1779. server->new_toplevel_decoration.notify = server_new_toplevel_decoration;
  1780. wl_signal_add(&server->xdg_decoration_mgr->events.new_toplevel_decoration,
  1781. &server->new_toplevel_decoration);
  1782. server->cursor = wlr_cursor_create();
  1783. wlr_cursor_attach_output_layout(server->cursor, server->output_layout);
  1784. server->cursor_mgr = wlr_xcursor_manager_create(NULL, 24);
  1785. server->cursor_mode = MJC_CURSOR_PASSTHROUGH;
  1786. server->cursor_motion.notify = server_cursor_motion;
  1787. wl_signal_add(&server->cursor->events.motion, &server->cursor_motion);
  1788. server->cursor_motion_absolute.notify = server_cursor_motion_absolute;
  1789. wl_signal_add(&server->cursor->events.motion_absolute,
  1790. &server->cursor_motion_absolute);
  1791. server->cursor_button.notify = server_cursor_button;
  1792. wl_signal_add(&server->cursor->events.button, &server->cursor_button);
  1793. server->cursor_axis.notify = server_cursor_axis;
  1794. wl_signal_add(&server->cursor->events.axis, &server->cursor_axis);
  1795. server->cursor_frame.notify = server_cursor_frame;
  1796. wl_signal_add(&server->cursor->events.frame, &server->cursor_frame);
  1797. server->new_input.notify = server_new_input;
  1798. wl_signal_add(&server->backend->events.new_input, &server->new_input);
  1799. server->seat = wlr_seat_create(server->display, "seat0");
  1800. server->request_cursor.notify = seat_request_cursor;
  1801. wl_signal_add(&server->seat->events.request_set_cursor, &server->request_cursor);
  1802. server->request_set_selection.notify = seat_request_set_selection;
  1803. wl_signal_add(&server->seat->events.request_set_selection,
  1804. &server->request_set_selection);
  1805. const char *socket = wl_display_add_socket_auto(server->display);
  1806. if (socket == NULL) {
  1807. fprintf(stderr, "mjdevc: failed to create wayland socket\n");
  1808. return false;
  1809. }
  1810. server->socket = socket;
  1811. server->xwayland = wlr_xwayland_create(server->display, server->compositor, true);
  1812. if (server->xwayland != NULL) {
  1813. server->new_xwayland_surface.notify = server_new_xwayland_surface;
  1814. wl_signal_add(&server->xwayland->events.new_surface,
  1815. &server->new_xwayland_surface);
  1816. server->xwayland_ready.notify = server_xwayland_ready;
  1817. wl_signal_add(&server->xwayland->events.ready, &server->xwayland_ready);
  1818. }
  1819. server->sigchld_source = wl_event_loop_add_signal(server->loop,
  1820. SIGCHLD, handle_sigchld, server);
  1821. if (!wlr_backend_start(server->backend)) {
  1822. fprintf(stderr, "mjdevc: failed to start backend\n");
  1823. return false;
  1824. }
  1825. setenv("WAYLAND_DISPLAY", socket, true);
  1826. if (server->xwayland != NULL) {
  1827. setenv("DISPLAY", server->xwayland->display_name, true);
  1828. }
  1829. /* GPU vs software decision for the Compose shell (Skiko), exported into its environment
  1830. * before it is spawned. Software GL (VM / no GPU) -> Skiko CPU raster (else it crashes with
  1831. * "Cannot create Linux GL context"); real GPU -> leave unset so Skiko uses hardware GL. */
  1832. if (mjc_renderer_is_software(server->renderer)) {
  1833. setenv("SKIKO_RENDER_API", "SOFTWARE", true);
  1834. fprintf(stderr, "mjdevc: software GL renderer -> SKIKO_RENDER_API=SOFTWARE for the shell\n");
  1835. } else {
  1836. unsetenv("SKIKO_RENDER_API");
  1837. fprintf(stderr, "mjdevc: hardware GL renderer -> shell uses Skiko OpenGL\n");
  1838. }
  1839. if (server->cbs.ready != NULL) {
  1840. server->cbs.ready(server->ud);
  1841. }
  1842. return true;
  1843. }
  1844. void mjc_run(mjc_server *server) {
  1845. wl_display_run(server->display);
  1846. }
  1847. void mjc_terminate(mjc_server *server) {
  1848. wl_display_terminate(server->display);
  1849. }
  1850. void mjc_destroy(mjc_server *server) {
  1851. if (server->xwayland != NULL) {
  1852. wlr_xwayland_destroy(server->xwayland);
  1853. }
  1854. wl_display_destroy_clients(server->display);
  1855. wlr_scene_node_destroy(&server->scene->tree.node);
  1856. wlr_xcursor_manager_destroy(server->cursor_mgr);
  1857. wlr_cursor_destroy(server->cursor);
  1858. wlr_allocator_destroy(server->allocator);
  1859. wlr_renderer_destroy(server->renderer);
  1860. wlr_backend_destroy(server->backend);
  1861. wl_display_destroy(server->display);
  1862. free(server);
  1863. }
  1864. const char *mjc_socket_name(mjc_server *server) {
  1865. return server->socket;
  1866. }
  1867. const char *mjc_xwayland_display(mjc_server *server) {
  1868. return server->xwayland != NULL ? server->xwayland->display_name : NULL;
  1869. }
  1870. void mjc_output_size(mjc_server *server, int *width, int *height) {
  1871. struct wlr_box box;
  1872. wlr_output_layout_get_box(server->output_layout, NULL, &box);
  1873. *width = box.width;
  1874. *height = box.height;
  1875. }
  1876. int mjc_spawn(mjc_server *server, const char *command) {
  1877. pid_t pid = fork();
  1878. if (pid < 0) {
  1879. return -1;
  1880. }
  1881. if (pid == 0) {
  1882. setsid();
  1883. sigset_t set;
  1884. sigemptyset(&set);
  1885. sigprocmask(SIG_SETMASK, &set, NULL);
  1886. execl("/bin/sh", "/bin/sh", "-c", command, (void *) NULL);
  1887. _exit(127);
  1888. }
  1889. if (server->spawned_count <
  1890. (int) (sizeof(server->spawned_pids) / sizeof(server->spawned_pids[0]))) {
  1891. server->spawned_pids[server->spawned_count++] = pid;
  1892. }
  1893. return (int) pid;
  1894. }
  1895. /* unix socket helpers */
  1896. #include <fcntl.h>
  1897. #include <sys/socket.h>
  1898. #include <sys/un.h>
  1899. int mjc_unix_listen(const char *path, int backlog) {
  1900. int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
  1901. if (fd < 0) {
  1902. return -1;
  1903. }
  1904. struct sockaddr_un addr = {0};
  1905. addr.sun_family = AF_UNIX;
  1906. if (strlen(path) >= sizeof(addr.sun_path)) {
  1907. close(fd);
  1908. return -1;
  1909. }
  1910. strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
  1911. unlink(path);
  1912. if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) != 0 ||
  1913. listen(fd, backlog) != 0) {
  1914. close(fd);
  1915. return -1;
  1916. }
  1917. return fd;
  1918. }
  1919. int mjc_unix_accept(int server_fd) {
  1920. int fd = accept(server_fd, NULL, NULL);
  1921. if (fd >= 0) {
  1922. fcntl(fd, F_SETFL, O_NONBLOCK);
  1923. }
  1924. return fd;
  1925. }
  1926. /* fd integration */
  1927. static int handle_fd_event(int fd, uint32_t mask, void *data) {
  1928. struct mjc_fd_source *source = data;
  1929. if (source->server->cbs.fd_event != NULL) {
  1930. return source->server->cbs.fd_event(source->server->ud, fd, mask);
  1931. }
  1932. return 0;
  1933. }
  1934. bool mjc_loop_add_fd(mjc_server *server, int fd, uint32_t mask) {
  1935. struct mjc_fd_source *source = calloc(1, sizeof(*source));
  1936. source->server = server;
  1937. source->fd = fd;
  1938. source->source = wl_event_loop_add_fd(server->loop, fd, mask,
  1939. handle_fd_event, source);
  1940. if (source->source == NULL) {
  1941. free(source);
  1942. return false;
  1943. }
  1944. wl_list_insert(&server->fd_sources, &source->link);
  1945. return true;
  1946. }
  1947. void mjc_loop_remove_fd(mjc_server *server, int fd) {
  1948. struct mjc_fd_source *source, *tmp;
  1949. wl_list_for_each_safe(source, tmp, &server->fd_sources, link) {
  1950. if (source->fd == fd) {
  1951. wl_event_source_remove(source->source);
  1952. wl_list_remove(&source->link);
  1953. free(source);
  1954. return;
  1955. }
  1956. }
  1957. }
  1958. /* views */
  1959. uint64_t mjc_view_id(mjc_view *view) {
  1960. return view->id;
  1961. }
  1962. int mjc_view_pid(mjc_view *view) {
  1963. if (view->is_xwayland) {
  1964. return view->xsurface != NULL ? (int) view->xsurface->pid : 0;
  1965. }
  1966. pid_t pid = 0;
  1967. struct wlr_surface *surface = view_surface(view);
  1968. if (surface != NULL) {
  1969. struct wl_client *client = wl_resource_get_client(surface->resource);
  1970. wl_client_get_credentials(client, &pid, NULL, NULL);
  1971. }
  1972. return (int) pid;
  1973. }
  1974. const char *mjc_view_title(mjc_view *view) {
  1975. if (view->is_xwayland) {
  1976. return view->xsurface != NULL ? view->xsurface->title : NULL;
  1977. }
  1978. return view->xdg_toplevel->title;
  1979. }
  1980. const char *mjc_view_app_id(mjc_view *view) {
  1981. if (view->is_xwayland) {
  1982. return view->xsurface != NULL ? view->xsurface->class : NULL;
  1983. }
  1984. return view->xdg_toplevel->app_id;
  1985. }
  1986. bool mjc_view_is_xwayland(mjc_view *view) {
  1987. return view->is_xwayland;
  1988. }
  1989. bool mjc_view_is_mapped(mjc_view *view) {
  1990. return view->mapped;
  1991. }
  1992. bool mjc_view_is_minimized(mjc_view *view) {
  1993. return view->minimized;
  1994. }
  1995. bool mjc_view_is_maximized(mjc_view *view) {
  1996. return view->maximized;
  1997. }
  1998. bool mjc_view_is_focused(mjc_view *view) {
  1999. return view->server->focused_view == view;
  2000. }
  2001. void mjc_view_get_geometry(mjc_view *view, int *x, int *y, int *width, int *height) {
  2002. struct wlr_box box = {0};
  2003. if (view->scene_tree != NULL || !view->is_xwayland) {
  2004. view_current_geometry(view, &box);
  2005. }
  2006. *x = box.x;
  2007. *y = box.y;
  2008. *width = box.width;
  2009. *height = box.height;
  2010. }
  2011. void mjc_view_close(mjc_view *view) {
  2012. if (view->is_xwayland) {
  2013. if (view->xsurface != NULL) {
  2014. wlr_xwayland_surface_close(view->xsurface);
  2015. }
  2016. } else {
  2017. wlr_xdg_toplevel_send_close(view->xdg_toplevel);
  2018. }
  2019. }
  2020. void mjc_view_set_minimized(mjc_view *view, bool minimized) {
  2021. if (view->minimized == minimized) {
  2022. return;
  2023. }
  2024. view->minimized = minimized;
  2025. if (view->scene_tree != NULL) {
  2026. wlr_scene_node_set_enabled(&view->scene_tree->node, !minimized);
  2027. }
  2028. if (view->is_xwayland && view->xsurface != NULL) {
  2029. wlr_xwayland_surface_set_minimized(view->xsurface, minimized);
  2030. }
  2031. if (minimized) {
  2032. clear_focus_if(view->server, view);
  2033. } else {
  2034. mjc_view_focus(view);
  2035. }
  2036. }
  2037. void mjc_view_set_maximized(mjc_view *view, bool maximized) {
  2038. struct mjc_server *server = view->server;
  2039. if (view->maximized == maximized) {
  2040. return;
  2041. }
  2042. struct wlr_box box;
  2043. wlr_output_layout_get_box(server->output_layout, NULL, &box);
  2044. if (maximized && view->mapped && view->scene_tree != NULL) {
  2045. /* remember the floating geometry (visible coords) for restore */
  2046. view_current_geometry(view, &view->saved_geo);
  2047. }
  2048. view->maximized = maximized;
  2049. if (view->is_xwayland) {
  2050. if (view->xsurface == NULL) {
  2051. return;
  2052. }
  2053. wlr_xwayland_surface_set_maximized(view->xsurface, maximized);
  2054. if (maximized) {
  2055. wlr_xwayland_surface_configure(view->xsurface,
  2056. (int16_t) box.x, (int16_t) box.y,
  2057. (uint16_t) box.width, (uint16_t) box.height);
  2058. if (view->scene_tree != NULL) {
  2059. wlr_scene_node_set_position(&view->scene_tree->node, box.x, box.y);
  2060. }
  2061. } else if (view->saved_geo.width > 0) {
  2062. wlr_xwayland_surface_configure(view->xsurface,
  2063. (int16_t) view->saved_geo.x, (int16_t) view->saved_geo.y,
  2064. (uint16_t) view->saved_geo.width, (uint16_t) view->saved_geo.height);
  2065. if (view->scene_tree != NULL) {
  2066. wlr_scene_node_set_position(&view->scene_tree->node,
  2067. view->saved_geo.x, view->saved_geo.y);
  2068. }
  2069. }
  2070. } else {
  2071. wlr_xdg_toplevel_set_maximized(view->xdg_toplevel, maximized);
  2072. if (maximized) {
  2073. wlr_xdg_toplevel_set_size(view->xdg_toplevel, box.width, box.height);
  2074. if (view->scene_tree != NULL) {
  2075. wlr_scene_node_set_position(&view->scene_tree->node, box.x, box.y);
  2076. }
  2077. } else {
  2078. /* 0x0 when no remembered size - the client picks its own;
  2079. * placement happens on the commit that carries the new size,
  2080. * when the decoration offsets are known again */
  2081. wlr_xdg_toplevel_set_size(view->xdg_toplevel,
  2082. view->saved_geo.width, view->saved_geo.height);
  2083. view->pending_center = true;
  2084. }
  2085. }
  2086. /* xwayland sizes itself synchronously here (no commit round-trip), so refresh
  2087. * the frame now; xdg follows up on its next commit */
  2088. if (view->decorated) {
  2089. view_decoration_update(view);
  2090. }
  2091. }
  2092. void mjc_view_set_layer(mjc_view *view, mjc_layer layer) {
  2093. view->layer = layer;
  2094. if (view->scene_tree != NULL) {
  2095. wlr_scene_node_reparent(&view->scene_tree->node,
  2096. view->server->layers[layer]);
  2097. }
  2098. /* only normal-layer app windows get a server-side frame; shell surfaces
  2099. * (wallpaper, panels, menus) live in other layers and stay undecorated */
  2100. view_decoration_update(view);
  2101. }
  2102. void mjc_view_set_focusable(mjc_view *view, bool focusable) {
  2103. view->focusable = focusable;
  2104. if (!focusable) {
  2105. clear_focus_if(view->server, view);
  2106. }
  2107. }
  2108. void mjc_view_set_chrome(mjc_view *view, bool chrome) {
  2109. view->is_chrome = chrome;
  2110. view_decoration_update(view);
  2111. }
  2112. void mjc_view_set_decorated(mjc_view *view, bool decorated) {
  2113. view->deco_disabled = !decorated;
  2114. view_decoration_update(view);
  2115. }
  2116. void mjc_set_decoration_theme(mjc_server *server,
  2117. float bg_r, float bg_g, float bg_b,
  2118. float fg_r, float fg_g, float fg_b) {
  2119. server->deco_bg[0] = bg_r;
  2120. server->deco_bg[1] = bg_g;
  2121. server->deco_bg[2] = bg_b;
  2122. server->deco_fg[0] = fg_r;
  2123. server->deco_fg[1] = fg_g;
  2124. server->deco_fg[2] = fg_b;
  2125. /* re-skin every currently decorated window with the new palette */
  2126. struct mjc_view *view;
  2127. wl_list_for_each(view, &server->views, link) {
  2128. if (view->decorated) {
  2129. view->deco_w = 0; /* force a re-render at the same width */
  2130. view_decoration_update(view);
  2131. /* refresh the border tint too */
  2132. const float border_col[4] = {
  2133. bg_r * 0.6f, bg_g * 0.6f, bg_b * 0.6f, 1.0f,
  2134. };
  2135. for (int i = 0; i < 3; i++) {
  2136. if (view->deco_border[i] != NULL) {
  2137. wlr_scene_rect_set_color(view->deco_border[i], border_col);
  2138. }
  2139. }
  2140. }
  2141. }
  2142. }
  2143. void mjc_view_set_position(mjc_view *view, int x, int y) {
  2144. if (view->scene_tree != NULL) {
  2145. wlr_scene_node_set_position(&view->scene_tree->node, x, y);
  2146. }
  2147. if (view->is_xwayland && view->xsurface != NULL) {
  2148. wlr_xwayland_surface_configure(view->xsurface,
  2149. (int16_t) x, (int16_t) y,
  2150. (uint16_t) view->xsurface->width, (uint16_t) view->xsurface->height);
  2151. }
  2152. }
  2153. void mjc_view_set_geometry(mjc_view *view, int x, int y, int width, int height) {
  2154. if (width <= 0 || height <= 0) {
  2155. return;
  2156. }
  2157. if (view->maximized) {
  2158. /* keep it as the restore target for the next unmaximize */
  2159. view->saved_geo.x = x;
  2160. view->saved_geo.y = y;
  2161. view->saved_geo.width = width;
  2162. view->saved_geo.height = height;
  2163. return;
  2164. }
  2165. if (view->is_xwayland) {
  2166. if (view->xsurface != NULL) {
  2167. wlr_xwayland_surface_configure(view->xsurface,
  2168. (int16_t) x, (int16_t) y, (uint16_t) width, (uint16_t) height);
  2169. }
  2170. if (view->scene_tree != NULL) {
  2171. wlr_scene_node_set_position(&view->scene_tree->node, x, y);
  2172. }
  2173. } else {
  2174. wlr_xdg_toplevel_set_size(view->xdg_toplevel, width, height);
  2175. if (view->scene_tree != NULL) {
  2176. struct wlr_box off;
  2177. wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &off);
  2178. wlr_scene_node_set_position(&view->scene_tree->node,
  2179. x - off.x, y - off.y);
  2180. }
  2181. }
  2182. if (view->decorated) {
  2183. view_decoration_update(view);
  2184. }
  2185. }
  2186. /* ------------------------------------------------------------------ */
  2187. /* input injection (ipc-driven, for automated/headless testing) */
  2188. /* ------------------------------------------------------------------ */
  2189. static uint32_t mjc_now_msec(void) {
  2190. struct timespec now;
  2191. clock_gettime(CLOCK_MONOTONIC, &now);
  2192. return (uint32_t) (now.tv_sec * 1000 + now.tv_nsec / 1000000);
  2193. }
  2194. #define MJC_INJECT_MARKER_SIZE 16
  2195. #define MJC_INJECT_FADE_STEP 0.08f
  2196. #define MJC_INJECT_FADE_INTERVAL_MS 40
  2197. /* step the injection marker's alpha down; disables it once fully faded */
  2198. static int inject_marker_fade(void *data) {
  2199. struct mjc_server *server = data;
  2200. if (server->inject_marker == NULL) {
  2201. return 0;
  2202. }
  2203. server->inject_alpha -= MJC_INJECT_FADE_STEP;
  2204. if (server->inject_alpha <= 0.0f) {
  2205. server->inject_alpha = 0.0f;
  2206. wlr_scene_node_set_enabled(&server->inject_marker->node, false);
  2207. return 0;
  2208. }
  2209. float a = server->inject_alpha;
  2210. const float color[4] = {a, a, a, a}; /* premultiplied white */
  2211. wlr_scene_rect_set_color(server->inject_marker, color);
  2212. if (server->inject_fade_timer != NULL) {
  2213. wl_event_source_timer_update(server->inject_fade_timer,
  2214. MJC_INJECT_FADE_INTERVAL_MS);
  2215. }
  2216. return 0;
  2217. }
  2218. /* show the injection marker at the current cursor position at full opacity and (re)arm
  2219. * the fade timer; called on every injected pointer event so it stays bright while moving */
  2220. static void inject_marker_show(struct mjc_server *server) {
  2221. if (server->inject_marker == NULL) {
  2222. return;
  2223. }
  2224. wlr_scene_node_set_position(&server->inject_marker->node,
  2225. (int) server->cursor->x - MJC_INJECT_MARKER_SIZE / 2,
  2226. (int) server->cursor->y - MJC_INJECT_MARKER_SIZE / 2);
  2227. server->inject_alpha = 1.0f;
  2228. const float color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
  2229. wlr_scene_rect_set_color(server->inject_marker, color);
  2230. wlr_scene_node_raise_to_top(&server->inject_marker->node);
  2231. wlr_scene_node_set_enabled(&server->inject_marker->node, true);
  2232. /* keep the marker persistently visible for now (behaves like a mouse cursor); the
  2233. * inactivity fadeout is disabled until we want it back */
  2234. }
  2235. void mjc_pointer_move(mjc_server *server, int x, int y) {
  2236. if (server == NULL) {
  2237. return;
  2238. }
  2239. /* warp to the closest valid layout point, then run the normal motion path so
  2240. * pointer focus, focus-follows-mouse and the ipc pointer broadcast all fire */
  2241. wlr_cursor_warp_closest(server->cursor, NULL, (double) x, (double) y);
  2242. process_cursor_motion(server, mjc_now_msec());
  2243. inject_marker_show(server);
  2244. }
  2245. void mjc_pointer_button(mjc_server *server, uint32_t button, bool pressed) {
  2246. if (server == NULL) {
  2247. return;
  2248. }
  2249. enum wl_pointer_button_state state = pressed
  2250. ? WL_POINTER_BUTTON_STATE_PRESSED
  2251. : WL_POINTER_BUTTON_STATE_RELEASED;
  2252. wlr_seat_pointer_notify_button(server->seat, mjc_now_msec(), button, state);
  2253. if (!pressed) {
  2254. server->cursor_mode = MJC_CURSOR_PASSTHROUGH;
  2255. server->grabbed_view = NULL;
  2256. } else {
  2257. double sx, sy;
  2258. struct wlr_surface *surface = NULL;
  2259. struct mjc_view *view = desktop_view_at(server,
  2260. server->cursor->x, server->cursor->y, &surface, &sx, &sy);
  2261. if (view != NULL && view->focusable) {
  2262. mjc_view_focus(view);
  2263. }
  2264. }
  2265. wlr_seat_pointer_notify_frame(server->seat);
  2266. inject_marker_show(server);
  2267. }
  2268. void mjc_key(mjc_server *server, uint32_t keycode, bool pressed) {
  2269. if (server == NULL) {
  2270. return;
  2271. }
  2272. struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(server->seat);
  2273. if (keyboard == NULL) {
  2274. if (wl_list_empty(&server->keyboards)) {
  2275. return;
  2276. }
  2277. struct mjc_keyboard *kb =
  2278. wl_container_of(server->keyboards.next, kb, link);
  2279. keyboard = kb->wlr_keyboard;
  2280. }
  2281. enum wl_keyboard_key_state state = pressed
  2282. ? WL_KEYBOARD_KEY_STATE_PRESSED
  2283. : WL_KEYBOARD_KEY_STATE_RELEASED;
  2284. /* mirror keyboard_handle_key: offer the keysym to the shell global-key hook first
  2285. * (Super/Escape toggles), and only forward to the focused client when unhandled */
  2286. uint32_t xkb_keycode = keycode + 8;
  2287. const xkb_keysym_t *syms;
  2288. int nsyms = xkb_state_key_get_syms(keyboard->xkb_state, xkb_keycode, &syms);
  2289. uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard);
  2290. bool handled = false;
  2291. if (pressed) {
  2292. for (int i = 0; i < nsyms; i++) {
  2293. if (server->cbs.key != NULL &&
  2294. server->cbs.key(server->ud, syms[i], modifiers, true)) {
  2295. handled = true;
  2296. }
  2297. }
  2298. }
  2299. if (!handled) {
  2300. wlr_seat_set_keyboard(server->seat, keyboard);
  2301. wlr_seat_keyboard_notify_key(server->seat, mjc_now_msec(), keycode, state);
  2302. }
  2303. }