shim.c 106 KB

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