shim.c 61 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706
  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/types/wlr_compositor.h>
  29. #include <wlr/types/wlr_subcompositor.h>
  30. #include <wlr/types/wlr_cursor.h>
  31. #include <wlr/types/wlr_data_device.h>
  32. #include <wlr/types/wlr_input_device.h>
  33. #include <wlr/types/wlr_keyboard.h>
  34. #include <wlr/types/wlr_output.h>
  35. #include <wlr/types/wlr_output_layout.h>
  36. #include <wlr/types/wlr_pointer.h>
  37. #include <wlr/types/wlr_scene.h>
  38. #include <wlr/types/wlr_seat.h>
  39. #include <wlr/types/wlr_xcursor_manager.h>
  40. #include <wlr/types/wlr_xdg_decoration_v1.h>
  41. #include <wlr/types/wlr_xdg_shell.h>
  42. #include <wlr/util/log.h>
  43. #include <wlr/xwayland.h>
  44. #include <xkbcommon/xkbcommon.h>
  45. #include "shim.h"
  46. enum mjc_cursor_mode {
  47. MJC_CURSOR_PASSTHROUGH,
  48. MJC_CURSOR_MOVE,
  49. MJC_CURSOR_RESIZE,
  50. };
  51. struct mjc_server {
  52. struct wl_display *display;
  53. struct wl_event_loop *loop;
  54. struct wlr_backend *backend;
  55. struct wlr_session *session;
  56. struct wlr_renderer *renderer;
  57. struct wlr_allocator *allocator;
  58. struct wlr_compositor *compositor;
  59. struct wlr_scene *scene;
  60. struct wlr_scene_output_layout *scene_layout;
  61. struct wlr_output_layout *output_layout;
  62. struct wlr_scene_tree *layers[5];
  63. struct wlr_xdg_shell *xdg_shell;
  64. struct wlr_xdg_decoration_manager_v1 *xdg_decoration_mgr;
  65. struct wlr_xwayland *xwayland;
  66. struct wlr_cursor *cursor;
  67. struct wlr_xcursor_manager *cursor_mgr;
  68. struct wlr_seat *seat;
  69. struct wl_list views; /* mjc_view.link */
  70. struct wl_list keyboards; /* mjc_keyboard.link */
  71. struct wl_list fd_sources;/* mjc_fd_source.link */
  72. struct mjc_view *focused_view;
  73. mjc_callbacks cbs;
  74. void *ud;
  75. const char *socket;
  76. uint64_t next_view_id;
  77. /* nested-output size (16:9 default); used when the backend has no mode list
  78. * (wayland/headless) and to lock the window against host-driven resizes */
  79. int output_w, output_h;
  80. /* throttle timestamp (msec) for the pointer callback */
  81. uint32_t last_pointer_ms;
  82. enum mjc_cursor_mode cursor_mode;
  83. struct mjc_view *grabbed_view;
  84. double grab_x, grab_y;
  85. struct wlr_box grab_geobox;
  86. uint32_t resize_edges;
  87. struct wl_event_source *sigchld_source;
  88. /* pids spawned via mjc_spawn; sigchld must reap only these,
  89. * other children (Xwayland) belong to wlroots */
  90. pid_t spawned_pids[64];
  91. int spawned_count;
  92. struct wl_listener new_output;
  93. struct wl_listener new_xdg_toplevel;
  94. struct wl_listener new_xdg_popup;
  95. struct wl_listener new_toplevel_decoration;
  96. struct wl_listener new_xwayland_surface;
  97. struct wl_listener xwayland_ready;
  98. struct wl_listener cursor_motion;
  99. struct wl_listener cursor_motion_absolute;
  100. struct wl_listener cursor_button;
  101. struct wl_listener cursor_axis;
  102. struct wl_listener cursor_frame;
  103. struct wl_listener new_input;
  104. struct wl_listener request_cursor;
  105. struct wl_listener request_set_selection;
  106. };
  107. struct mjc_view {
  108. struct wl_list link;
  109. struct mjc_server *server;
  110. uint64_t id;
  111. bool is_xwayland;
  112. bool mapped;
  113. bool minimized;
  114. bool maximized;
  115. /* re-center once the client commits its post-unmaximize size */
  116. bool pending_center;
  117. bool focusable;
  118. mjc_layer layer;
  119. struct wlr_scene_tree *scene_tree;
  120. /* floating geometry remembered while maximized (x/y are scene coords) */
  121. struct wlr_box saved_geo;
  122. /* xdg shell */
  123. struct wlr_xdg_toplevel *xdg_toplevel;
  124. /* xwayland */
  125. struct wlr_xwayland_surface *xsurface;
  126. struct wl_listener map;
  127. struct wl_listener unmap;
  128. struct wl_listener commit;
  129. struct wl_listener destroy;
  130. struct wl_listener request_move;
  131. struct wl_listener request_resize;
  132. struct wl_listener request_maximize;
  133. struct wl_listener request_minimize;
  134. struct wl_listener request_fullscreen;
  135. struct wl_listener set_title;
  136. struct wl_listener set_app_id; /* set_class for xwayland */
  137. struct wl_listener associate;
  138. struct wl_listener dissociate;
  139. struct wl_listener request_configure;
  140. struct wl_listener set_geometry;
  141. };
  142. struct mjc_popup {
  143. struct wlr_xdg_popup *xdg_popup;
  144. struct wl_listener commit;
  145. struct wl_listener destroy;
  146. };
  147. struct mjc_decoration {
  148. struct wlr_xdg_toplevel_decoration_v1 *decoration;
  149. struct wl_listener request_mode;
  150. struct wl_listener destroy;
  151. };
  152. struct mjc_keyboard {
  153. struct wl_list link;
  154. struct mjc_server *server;
  155. struct wlr_keyboard *wlr_keyboard;
  156. struct wl_listener modifiers;
  157. struct wl_listener key;
  158. struct wl_listener destroy;
  159. };
  160. struct mjc_output {
  161. struct wl_list link;
  162. struct mjc_server *server;
  163. struct wlr_output *wlr_output;
  164. struct wl_listener frame;
  165. struct wl_listener request_state;
  166. struct wl_listener destroy;
  167. };
  168. struct mjc_fd_source {
  169. struct wl_list link;
  170. struct mjc_server *server;
  171. int fd;
  172. struct wl_event_source *source;
  173. };
  174. /* ------------------------------------------------------------------ */
  175. /* helpers */
  176. /* ------------------------------------------------------------------ */
  177. static struct wlr_surface *view_surface(struct mjc_view *view) {
  178. if (view->is_xwayland) {
  179. return view->xsurface != NULL ? view->xsurface->surface : NULL;
  180. }
  181. return view->xdg_toplevel->base->surface;
  182. }
  183. static void emit_focus_change(struct mjc_server *server) {
  184. if (server->cbs.focus_change != NULL) {
  185. server->cbs.focus_change(server->ud, server->focused_view);
  186. }
  187. }
  188. static void view_deactivate(struct mjc_view *view) {
  189. if (view->is_xwayland) {
  190. if (view->xsurface != NULL) {
  191. wlr_xwayland_surface_activate(view->xsurface, false);
  192. }
  193. } else {
  194. wlr_xdg_toplevel_set_activated(view->xdg_toplevel, false);
  195. }
  196. }
  197. void mjc_view_focus(struct mjc_view *view) {
  198. if (view == NULL || !view->focusable || view->minimized) {
  199. return;
  200. }
  201. struct mjc_server *server = view->server;
  202. struct wlr_seat *seat = server->seat;
  203. if (server->focused_view == view) {
  204. return;
  205. }
  206. if (server->focused_view != NULL) {
  207. view_deactivate(server->focused_view);
  208. }
  209. if (view->scene_tree != NULL) {
  210. wlr_scene_node_raise_to_top(&view->scene_tree->node);
  211. }
  212. if (view->is_xwayland) {
  213. wlr_xwayland_surface_activate(view->xsurface, true);
  214. wlr_xwayland_surface_restack(view->xsurface, NULL, XCB_STACK_MODE_ABOVE);
  215. } else {
  216. wlr_xdg_toplevel_set_activated(view->xdg_toplevel, true);
  217. }
  218. struct wlr_surface *surface = view_surface(view);
  219. struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat);
  220. if (surface != NULL) {
  221. if (keyboard != NULL) {
  222. wlr_seat_keyboard_notify_enter(seat, surface,
  223. keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers);
  224. } else {
  225. wlr_seat_keyboard_notify_enter(seat, surface, NULL, 0, NULL);
  226. }
  227. }
  228. server->focused_view = view;
  229. emit_focus_change(server);
  230. }
  231. static void clear_focus_if(struct mjc_server *server, struct mjc_view *view) {
  232. if (server->focused_view == view) {
  233. server->focused_view = NULL;
  234. emit_focus_change(server);
  235. }
  236. if (server->grabbed_view == view) {
  237. server->grabbed_view = NULL;
  238. server->cursor_mode = MJC_CURSOR_PASSTHROUGH;
  239. }
  240. }
  241. static struct mjc_view *desktop_view_at(struct mjc_server *server,
  242. double lx, double ly, struct wlr_surface **surface,
  243. double *sx, double *sy) {
  244. struct wlr_scene_node *node =
  245. wlr_scene_node_at(&server->scene->tree.node, lx, ly, sx, sy);
  246. if (node == NULL || node->type != WLR_SCENE_NODE_BUFFER) {
  247. return NULL;
  248. }
  249. struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
  250. struct wlr_scene_surface *scene_surface =
  251. wlr_scene_surface_try_from_buffer(scene_buffer);
  252. if (scene_surface == NULL) {
  253. return NULL;
  254. }
  255. *surface = scene_surface->surface;
  256. struct wlr_scene_tree *tree = node->parent;
  257. while (tree != NULL && tree->node.data == NULL) {
  258. tree = tree->node.parent;
  259. }
  260. return tree != NULL ? tree->node.data : NULL;
  261. }
  262. /* ------------------------------------------------------------------ */
  263. /* outputs */
  264. /* ------------------------------------------------------------------ */
  265. static void output_frame(struct wl_listener *listener, void *data) {
  266. (void) data;
  267. struct mjc_output *output = wl_container_of(listener, output, frame);
  268. struct wlr_scene_output *scene_output = wlr_scene_get_scene_output(
  269. output->server->scene, output->wlr_output);
  270. if (scene_output == NULL) {
  271. return;
  272. }
  273. wlr_scene_output_commit(scene_output, NULL);
  274. struct timespec now;
  275. clock_gettime(CLOCK_MONOTONIC, &now);
  276. wlr_scene_output_send_frame_done(scene_output, &now);
  277. }
  278. static void output_request_state(struct wl_listener *listener, void *data) {
  279. struct mjc_output *output = wl_container_of(listener, output, request_state);
  280. (void) data; /* ignore the host-requested size: keep the nested output locked
  281. * to its fixed 16:9 mode so the window cannot be resized
  282. * (moving and closing still work via the host decoration) */
  283. struct wlr_output_state state;
  284. wlr_output_state_init(&state);
  285. wlr_output_state_set_custom_mode(&state,
  286. output->server->output_w, output->server->output_h, 0);
  287. wlr_output_commit_state(output->wlr_output, &state);
  288. wlr_output_state_finish(&state);
  289. }
  290. static void output_destroy(struct wl_listener *listener, void *data) {
  291. (void) data;
  292. struct mjc_output *output = wl_container_of(listener, output, destroy);
  293. struct mjc_server *server = output->server;
  294. wl_list_remove(&output->frame.link);
  295. wl_list_remove(&output->request_state.link);
  296. wl_list_remove(&output->destroy.link);
  297. wl_list_remove(&output->link);
  298. free(output);
  299. /* nested backends (x11/wayland) have no session; closing the host window
  300. * destroys the output, so quit the compositor and let the run action finish.
  301. * a real (drm) session keeps running when an output is unplugged. */
  302. if (server->session == NULL) {
  303. wl_display_terminate(server->display);
  304. }
  305. }
  306. static void server_new_output(struct wl_listener *listener, void *data) {
  307. struct mjc_server *server = wl_container_of(listener, server, new_output);
  308. struct wlr_output *wlr_output = data;
  309. wlr_output_init_render(wlr_output, server->allocator, server->renderer);
  310. struct wlr_output_state state;
  311. wlr_output_state_init(&state);
  312. wlr_output_state_set_enabled(&state, true);
  313. struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
  314. if (mode != NULL) {
  315. wlr_output_state_set_mode(&state, mode);
  316. } else {
  317. /* wayland / headless backends expose no mode list, so preferred_mode is
  318. * NULL; without a mode the output has no dimensions and renders black.
  319. * pin it to the configured 16:9 size. */
  320. wlr_output_state_set_custom_mode(&state,
  321. server->output_w, server->output_h, 0);
  322. }
  323. wlr_output_commit_state(wlr_output, &state);
  324. wlr_output_state_finish(&state);
  325. struct mjc_output *output = calloc(1, sizeof(*output));
  326. output->wlr_output = wlr_output;
  327. output->server = server;
  328. output->frame.notify = output_frame;
  329. wl_signal_add(&wlr_output->events.frame, &output->frame);
  330. output->request_state.notify = output_request_state;
  331. wl_signal_add(&wlr_output->events.request_state, &output->request_state);
  332. output->destroy.notify = output_destroy;
  333. wl_signal_add(&wlr_output->events.destroy, &output->destroy);
  334. wl_list_init(&output->link);
  335. struct wlr_output_layout_output *l_output =
  336. wlr_output_layout_add_auto(server->output_layout, wlr_output);
  337. struct wlr_scene_output *scene_output =
  338. wlr_scene_output_create(server->scene, wlr_output);
  339. wlr_scene_output_layout_add_output(server->scene_layout, l_output, scene_output);
  340. }
  341. /* ------------------------------------------------------------------ */
  342. /* keyboard & input */
  343. /* ------------------------------------------------------------------ */
  344. static void keyboard_handle_modifiers(struct wl_listener *listener, void *data) {
  345. (void) data;
  346. struct mjc_keyboard *keyboard = wl_container_of(listener, keyboard, modifiers);
  347. wlr_seat_set_keyboard(keyboard->server->seat, keyboard->wlr_keyboard);
  348. wlr_seat_keyboard_notify_modifiers(keyboard->server->seat,
  349. &keyboard->wlr_keyboard->modifiers);
  350. }
  351. static void keyboard_handle_key(struct wl_listener *listener, void *data) {
  352. struct mjc_keyboard *keyboard = wl_container_of(listener, keyboard, key);
  353. struct mjc_server *server = keyboard->server;
  354. struct wlr_keyboard_key_event *event = data;
  355. uint32_t keycode = event->keycode + 8;
  356. const xkb_keysym_t *syms;
  357. int nsyms = xkb_state_key_get_syms(
  358. keyboard->wlr_keyboard->xkb_state, keycode, &syms);
  359. uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->wlr_keyboard);
  360. bool pressed = event->state == WL_KEYBOARD_KEY_STATE_PRESSED;
  361. bool handled = false;
  362. for (int i = 0; i < nsyms; i++) {
  363. /* VT switching always works */
  364. if (pressed && syms[i] >= XKB_KEY_XF86Switch_VT_1 &&
  365. syms[i] <= XKB_KEY_XF86Switch_VT_12) {
  366. if (server->session != NULL) {
  367. wlr_session_change_vt(server->session,
  368. syms[i] - XKB_KEY_XF86Switch_VT_1 + 1);
  369. }
  370. handled = true;
  371. continue;
  372. }
  373. if (server->cbs.key != NULL &&
  374. server->cbs.key(server->ud, syms[i], modifiers, pressed)) {
  375. handled = true;
  376. }
  377. }
  378. if (!handled) {
  379. wlr_seat_set_keyboard(server->seat, keyboard->wlr_keyboard);
  380. wlr_seat_keyboard_notify_key(server->seat,
  381. event->time_msec, event->keycode, event->state);
  382. }
  383. }
  384. static void keyboard_handle_destroy(struct wl_listener *listener, void *data) {
  385. (void) data;
  386. struct mjc_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
  387. wl_list_remove(&keyboard->modifiers.link);
  388. wl_list_remove(&keyboard->key.link);
  389. wl_list_remove(&keyboard->destroy.link);
  390. wl_list_remove(&keyboard->link);
  391. free(keyboard);
  392. }
  393. static void server_new_keyboard(struct mjc_server *server,
  394. struct wlr_input_device *device) {
  395. struct wlr_keyboard *wlr_keyboard = wlr_keyboard_from_input_device(device);
  396. struct mjc_keyboard *keyboard = calloc(1, sizeof(*keyboard));
  397. keyboard->server = server;
  398. keyboard->wlr_keyboard = wlr_keyboard;
  399. struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
  400. struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL,
  401. XKB_KEYMAP_COMPILE_NO_FLAGS);
  402. wlr_keyboard_set_keymap(wlr_keyboard, keymap);
  403. xkb_keymap_unref(keymap);
  404. xkb_context_unref(context);
  405. wlr_keyboard_set_repeat_info(wlr_keyboard, 25, 600);
  406. keyboard->modifiers.notify = keyboard_handle_modifiers;
  407. wl_signal_add(&wlr_keyboard->events.modifiers, &keyboard->modifiers);
  408. keyboard->key.notify = keyboard_handle_key;
  409. wl_signal_add(&wlr_keyboard->events.key, &keyboard->key);
  410. keyboard->destroy.notify = keyboard_handle_destroy;
  411. wl_signal_add(&device->events.destroy, &keyboard->destroy);
  412. wlr_seat_set_keyboard(server->seat, wlr_keyboard);
  413. wl_list_insert(&server->keyboards, &keyboard->link);
  414. }
  415. static void server_new_pointer(struct mjc_server *server,
  416. struct wlr_input_device *device) {
  417. wlr_cursor_attach_input_device(server->cursor, device);
  418. }
  419. static void server_new_input(struct wl_listener *listener, void *data) {
  420. struct mjc_server *server = wl_container_of(listener, server, new_input);
  421. struct wlr_input_device *device = data;
  422. switch (device->type) {
  423. case WLR_INPUT_DEVICE_KEYBOARD:
  424. server_new_keyboard(server, device);
  425. break;
  426. case WLR_INPUT_DEVICE_POINTER:
  427. server_new_pointer(server, device);
  428. break;
  429. default:
  430. break;
  431. }
  432. uint32_t caps = WL_SEAT_CAPABILITY_POINTER;
  433. if (!wl_list_empty(&server->keyboards)) {
  434. caps |= WL_SEAT_CAPABILITY_KEYBOARD;
  435. }
  436. wlr_seat_set_capabilities(server->seat, caps);
  437. }
  438. static void seat_request_cursor(struct wl_listener *listener, void *data) {
  439. struct mjc_server *server = wl_container_of(listener, server, request_cursor);
  440. struct wlr_seat_pointer_request_set_cursor_event *event = data;
  441. struct wlr_seat_client *focused_client =
  442. server->seat->pointer_state.focused_client;
  443. if (focused_client == event->seat_client) {
  444. wlr_cursor_set_surface(server->cursor, event->surface,
  445. event->hotspot_x, event->hotspot_y);
  446. }
  447. }
  448. static void seat_request_set_selection(struct wl_listener *listener, void *data) {
  449. struct mjc_server *server =
  450. wl_container_of(listener, server, request_set_selection);
  451. struct wlr_seat_request_set_selection_event *event = data;
  452. wlr_seat_set_selection(server->seat, event->source, event->serial);
  453. }
  454. /* ------------------------------------------------------------------ */
  455. /* cursor */
  456. /* ------------------------------------------------------------------ */
  457. static void view_current_geometry(struct mjc_view *view, struct wlr_box *box);
  458. static void process_cursor_move(struct mjc_server *server) {
  459. struct mjc_view *view = server->grabbed_view;
  460. if (view == NULL || view->scene_tree == NULL) {
  461. return;
  462. }
  463. int x = (int)(server->cursor->x - server->grab_x);
  464. int y = (int)(server->cursor->y - server->grab_y);
  465. wlr_scene_node_set_position(&view->scene_tree->node, x, y);
  466. if (view->is_xwayland && view->xsurface != NULL) {
  467. wlr_xwayland_surface_configure(view->xsurface,
  468. (int16_t) x, (int16_t) y,
  469. (uint16_t) view->xsurface->width, (uint16_t) view->xsurface->height);
  470. }
  471. }
  472. static void process_cursor_resize(struct mjc_server *server) {
  473. struct mjc_view *view = server->grabbed_view;
  474. if (view == NULL || view->scene_tree == NULL) {
  475. return;
  476. }
  477. double border_x = server->cursor->x - server->grab_x;
  478. double border_y = server->cursor->y - server->grab_y;
  479. int new_left = server->grab_geobox.x;
  480. int new_right = server->grab_geobox.x + server->grab_geobox.width;
  481. int new_top = server->grab_geobox.y;
  482. int new_bottom = server->grab_geobox.y + server->grab_geobox.height;
  483. if (server->resize_edges & WLR_EDGE_TOP) {
  484. new_top = (int) border_y;
  485. if (new_top >= new_bottom) {
  486. new_top = new_bottom - 1;
  487. }
  488. } else if (server->resize_edges & WLR_EDGE_BOTTOM) {
  489. new_bottom = (int) border_y;
  490. if (new_bottom <= new_top) {
  491. new_bottom = new_top + 1;
  492. }
  493. }
  494. if (server->resize_edges & WLR_EDGE_LEFT) {
  495. new_left = (int) border_x;
  496. if (new_left >= new_right) {
  497. new_left = new_right - 1;
  498. }
  499. } else if (server->resize_edges & WLR_EDGE_RIGHT) {
  500. new_right = (int) border_x;
  501. if (new_right <= new_left) {
  502. new_right = new_left + 1;
  503. }
  504. }
  505. int new_width = new_right - new_left;
  506. int new_height = new_bottom - new_top;
  507. if (view->is_xwayland && view->xsurface != NULL) {
  508. wlr_scene_node_set_position(&view->scene_tree->node, new_left, new_top);
  509. wlr_xwayland_surface_configure(view->xsurface,
  510. (int16_t) new_left, (int16_t) new_top,
  511. (uint16_t) new_width, (uint16_t) new_height);
  512. } else {
  513. struct wlr_box geo_box;
  514. wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &geo_box);
  515. wlr_scene_node_set_position(&view->scene_tree->node,
  516. new_left - geo_box.x, new_top - geo_box.y);
  517. wlr_xdg_toplevel_set_size(view->xdg_toplevel, new_width, new_height);
  518. }
  519. }
  520. static void process_cursor_motion(struct mjc_server *server, uint32_t time) {
  521. if (server->cursor_mode == MJC_CURSOR_MOVE) {
  522. process_cursor_move(server);
  523. return;
  524. } else if (server->cursor_mode == MJC_CURSOR_RESIZE) {
  525. process_cursor_resize(server);
  526. return;
  527. }
  528. double sx, sy;
  529. struct wlr_seat *seat = server->seat;
  530. struct wlr_surface *surface = NULL;
  531. desktop_view_at(server, server->cursor->x, server->cursor->y,
  532. &surface, &sx, &sy);
  533. if (surface == NULL) {
  534. wlr_cursor_set_xcursor(server->cursor, server->cursor_mgr, "default");
  535. }
  536. if (surface != NULL) {
  537. wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
  538. wlr_seat_pointer_notify_motion(seat, time, sx, sy);
  539. } else {
  540. wlr_seat_pointer_clear_focus(seat);
  541. }
  542. // broadcast the pointer position to ipc clients (throttled), so the shell can do
  543. // edge detection without the AWT global pointer which is unreliable under nested XWayland
  544. if (server->cbs.pointer != NULL && (time - server->last_pointer_ms) >= 30u) {
  545. server->last_pointer_ms = time;
  546. server->cbs.pointer(server->ud,
  547. (int) server->cursor->x, (int) server->cursor->y);
  548. }
  549. }
  550. static void server_cursor_motion(struct wl_listener *listener, void *data) {
  551. struct mjc_server *server = wl_container_of(listener, server, cursor_motion);
  552. struct wlr_pointer_motion_event *event = data;
  553. wlr_cursor_move(server->cursor, &event->pointer->base,
  554. event->delta_x, event->delta_y);
  555. process_cursor_motion(server, event->time_msec);
  556. }
  557. static void server_cursor_motion_absolute(struct wl_listener *listener, void *data) {
  558. struct mjc_server *server =
  559. wl_container_of(listener, server, cursor_motion_absolute);
  560. struct wlr_pointer_motion_absolute_event *event = data;
  561. wlr_cursor_warp_absolute(server->cursor, &event->pointer->base,
  562. event->x, event->y);
  563. process_cursor_motion(server, event->time_msec);
  564. }
  565. static void server_cursor_button(struct wl_listener *listener, void *data) {
  566. struct mjc_server *server = wl_container_of(listener, server, cursor_button);
  567. struct wlr_pointer_button_event *event = data;
  568. wlr_seat_pointer_notify_button(server->seat,
  569. event->time_msec, event->button, event->state);
  570. if (event->state == WL_POINTER_BUTTON_STATE_RELEASED) {
  571. server->cursor_mode = MJC_CURSOR_PASSTHROUGH;
  572. server->grabbed_view = NULL;
  573. } else {
  574. double sx, sy;
  575. struct wlr_surface *surface = NULL;
  576. struct mjc_view *view = desktop_view_at(server,
  577. server->cursor->x, server->cursor->y, &surface, &sx, &sy);
  578. if (view != NULL && view->focusable) {
  579. mjc_view_focus(view);
  580. }
  581. }
  582. }
  583. static void server_cursor_axis(struct wl_listener *listener, void *data) {
  584. struct mjc_server *server = wl_container_of(listener, server, cursor_axis);
  585. struct wlr_pointer_axis_event *event = data;
  586. wlr_seat_pointer_notify_axis(server->seat,
  587. event->time_msec, event->orientation, event->delta,
  588. event->delta_discrete, event->source, event->relative_direction);
  589. }
  590. static void server_cursor_frame(struct wl_listener *listener, void *data) {
  591. (void) data;
  592. struct mjc_server *server = wl_container_of(listener, server, cursor_frame);
  593. wlr_seat_pointer_notify_frame(server->seat);
  594. }
  595. /* ------------------------------------------------------------------ */
  596. /* interactive move/resize */
  597. /* ------------------------------------------------------------------ */
  598. static void view_current_geometry(struct mjc_view *view, struct wlr_box *box) {
  599. if (view->is_xwayland && view->xsurface != NULL) {
  600. box->x = view->scene_tree != NULL ? view->scene_tree->node.x : view->xsurface->x;
  601. box->y = view->scene_tree != NULL ? view->scene_tree->node.y : view->xsurface->y;
  602. box->width = view->xsurface->width;
  603. box->height = view->xsurface->height;
  604. } else {
  605. struct wlr_box geo;
  606. wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &geo);
  607. box->x = view->scene_tree->node.x + geo.x;
  608. box->y = view->scene_tree->node.y + geo.y;
  609. box->width = geo.width;
  610. box->height = geo.height;
  611. }
  612. }
  613. static void begin_interactive(struct mjc_view *view,
  614. enum mjc_cursor_mode mode, uint32_t edges) {
  615. struct mjc_server *server = view->server;
  616. if (view_surface(view) !=
  617. server->seat->pointer_state.focused_surface) {
  618. /* only react to requests from the focused client */
  619. return;
  620. }
  621. server->grabbed_view = view;
  622. server->cursor_mode = mode;
  623. struct wlr_box geo_box;
  624. view_current_geometry(view, &geo_box);
  625. if (mode == MJC_CURSOR_MOVE) {
  626. server->grab_x = server->cursor->x - view->scene_tree->node.x;
  627. server->grab_y = server->cursor->y - view->scene_tree->node.y;
  628. } else {
  629. double border_x = geo_box.x +
  630. ((edges & WLR_EDGE_RIGHT) ? geo_box.width : 0);
  631. double border_y = geo_box.y +
  632. ((edges & WLR_EDGE_BOTTOM) ? geo_box.height : 0);
  633. server->grab_x = server->cursor->x - border_x;
  634. server->grab_y = server->cursor->y - border_y;
  635. server->grab_geobox = geo_box;
  636. server->resize_edges = edges;
  637. }
  638. }
  639. /* ------------------------------------------------------------------ */
  640. /* view common */
  641. /* ------------------------------------------------------------------ */
  642. /* places a freshly mapped floating view in the middle of the output */
  643. static void view_center(struct mjc_view *view) {
  644. struct mjc_server *server = view->server;
  645. if (view->scene_tree == NULL || view->maximized) {
  646. return;
  647. }
  648. struct wlr_box out;
  649. wlr_output_layout_get_box(server->output_layout, NULL, &out);
  650. struct wlr_box geo;
  651. view_current_geometry(view, &geo);
  652. if (out.width <= 0 || geo.width <= 0) {
  653. return;
  654. }
  655. int x = out.x + (out.width - geo.width) / 2;
  656. int y = out.y + (out.height - geo.height) / 2;
  657. if (x < out.x) x = out.x;
  658. if (y < out.y) y = out.y;
  659. if (view->is_xwayland) {
  660. mjc_view_set_position(view, x, y);
  661. } else {
  662. struct wlr_box off;
  663. wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &off);
  664. wlr_scene_node_set_position(&view->scene_tree->node, x - off.x, y - off.y);
  665. }
  666. }
  667. static void view_emit_new(struct mjc_view *view) {
  668. if (view->server->cbs.view_new != NULL) {
  669. view->server->cbs.view_new(view->server->ud, view, view->is_xwayland);
  670. }
  671. }
  672. static void view_emit_map(struct mjc_view *view) {
  673. if (view->server->cbs.view_map != NULL) {
  674. view->server->cbs.view_map(view->server->ud, view);
  675. }
  676. }
  677. static void view_emit_unmap(struct mjc_view *view) {
  678. if (view->server->cbs.view_unmap != NULL) {
  679. view->server->cbs.view_unmap(view->server->ud, view);
  680. }
  681. }
  682. static void view_emit_destroy(struct mjc_view *view) {
  683. if (view->server->cbs.view_destroy != NULL) {
  684. view->server->cbs.view_destroy(view->server->ud, view);
  685. }
  686. }
  687. static struct mjc_view *view_create(struct mjc_server *server, bool is_xwayland) {
  688. struct mjc_view *view = calloc(1, sizeof(*view));
  689. view->server = server;
  690. view->id = ++server->next_view_id;
  691. view->is_xwayland = is_xwayland;
  692. view->focusable = true;
  693. view->layer = MJC_LAYER_NORMAL;
  694. wl_list_insert(&server->views, &view->link);
  695. return view;
  696. }
  697. static void view_free(struct mjc_view *view) {
  698. clear_focus_if(view->server, view);
  699. view_emit_destroy(view);
  700. wl_list_remove(&view->link);
  701. free(view);
  702. }
  703. /* ------------------------------------------------------------------ */
  704. /* xdg shell views */
  705. /* ------------------------------------------------------------------ */
  706. static void xdg_toplevel_map(struct wl_listener *listener, void *data) {
  707. (void) data;
  708. struct mjc_view *view = wl_container_of(listener, view, map);
  709. view->mapped = true;
  710. view_center(view);
  711. view_emit_map(view);
  712. mjc_view_focus(view);
  713. }
  714. static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) {
  715. (void) data;
  716. struct mjc_view *view = wl_container_of(listener, view, unmap);
  717. view->mapped = false;
  718. clear_focus_if(view->server, view);
  719. view_emit_unmap(view);
  720. }
  721. static void xdg_toplevel_commit(struct wl_listener *listener, void *data) {
  722. (void) data;
  723. struct mjc_view *view = wl_container_of(listener, view, commit);
  724. if (view->xdg_toplevel->base->initial_commit) {
  725. if (view->xdg_toplevel->requested.maximized) {
  726. mjc_view_set_maximized(view, true);
  727. } else {
  728. /* 0x0 lets the client pick its own (or remembered) size */
  729. wlr_xdg_toplevel_set_size(view->xdg_toplevel, 0, 0);
  730. }
  731. return;
  732. }
  733. if (view->pending_center && view->mapped && !view->maximized &&
  734. !view->xdg_toplevel->current.maximized) {
  735. struct wlr_box geo;
  736. wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &geo);
  737. if (geo.width > 0) {
  738. view->pending_center = false;
  739. if (view->saved_geo.width > 0 && view->scene_tree != NULL) {
  740. /* saved coords are visible coords, geo.x/y the offsets */
  741. wlr_scene_node_set_position(&view->scene_tree->node,
  742. view->saved_geo.x - geo.x, view->saved_geo.y - geo.y);
  743. } else {
  744. view_center(view);
  745. }
  746. }
  747. }
  748. }
  749. static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) {
  750. (void) data;
  751. struct mjc_view *view = wl_container_of(listener, view, destroy);
  752. wl_list_remove(&view->map.link);
  753. wl_list_remove(&view->unmap.link);
  754. wl_list_remove(&view->commit.link);
  755. wl_list_remove(&view->destroy.link);
  756. wl_list_remove(&view->request_move.link);
  757. wl_list_remove(&view->request_resize.link);
  758. wl_list_remove(&view->request_maximize.link);
  759. wl_list_remove(&view->request_minimize.link);
  760. wl_list_remove(&view->request_fullscreen.link);
  761. wl_list_remove(&view->set_title.link);
  762. wl_list_remove(&view->set_app_id.link);
  763. view_free(view);
  764. }
  765. static void xdg_toplevel_request_move(struct wl_listener *listener, void *data) {
  766. (void) data;
  767. struct mjc_view *view = wl_container_of(listener, view, request_move);
  768. begin_interactive(view, MJC_CURSOR_MOVE, 0);
  769. }
  770. static void xdg_toplevel_request_resize(struct wl_listener *listener, void *data) {
  771. struct wlr_xdg_toplevel_resize_event *event = data;
  772. struct mjc_view *view = wl_container_of(listener, view, request_resize);
  773. begin_interactive(view, MJC_CURSOR_RESIZE, event->edges);
  774. }
  775. static void xdg_toplevel_request_maximize(struct wl_listener *listener, void *data) {
  776. (void) data;
  777. struct mjc_view *view = wl_container_of(listener, view, request_maximize);
  778. if (view->xdg_toplevel->base->initialized) {
  779. mjc_view_set_maximized(view, view->xdg_toplevel->requested.maximized);
  780. wlr_xdg_surface_schedule_configure(view->xdg_toplevel->base);
  781. }
  782. }
  783. static void xdg_toplevel_request_minimize(struct wl_listener *listener, void *data) {
  784. (void) data;
  785. struct mjc_view *view = wl_container_of(listener, view, request_minimize);
  786. mjc_view_set_minimized(view, true);
  787. }
  788. static void xdg_toplevel_request_fullscreen(struct wl_listener *listener, void *data) {
  789. (void) data;
  790. struct mjc_view *view = wl_container_of(listener, view, request_fullscreen);
  791. if (view->xdg_toplevel->base->initialized) {
  792. wlr_xdg_surface_schedule_configure(view->xdg_toplevel->base);
  793. }
  794. }
  795. static void xdg_toplevel_set_title(struct wl_listener *listener, void *data) {
  796. (void) data;
  797. struct mjc_view *view = wl_container_of(listener, view, set_title);
  798. if (view->server->cbs.view_title != NULL) {
  799. view->server->cbs.view_title(view->server->ud, view,
  800. view->xdg_toplevel->title);
  801. }
  802. }
  803. static void xdg_toplevel_set_app_id(struct wl_listener *listener, void *data) {
  804. (void) data;
  805. struct mjc_view *view = wl_container_of(listener, view, set_app_id);
  806. if (view->server->cbs.view_app_id != NULL) {
  807. view->server->cbs.view_app_id(view->server->ud, view,
  808. view->xdg_toplevel->app_id);
  809. }
  810. }
  811. static void server_new_xdg_toplevel(struct wl_listener *listener, void *data) {
  812. struct mjc_server *server =
  813. wl_container_of(listener, server, new_xdg_toplevel);
  814. struct wlr_xdg_toplevel *xdg_toplevel = data;
  815. struct mjc_view *view = view_create(server, false);
  816. view->xdg_toplevel = xdg_toplevel;
  817. view->scene_tree = wlr_scene_xdg_surface_create(
  818. server->layers[MJC_LAYER_NORMAL], xdg_toplevel->base);
  819. view->scene_tree->node.data = view;
  820. xdg_toplevel->base->data = view->scene_tree;
  821. view->map.notify = xdg_toplevel_map;
  822. wl_signal_add(&xdg_toplevel->base->surface->events.map, &view->map);
  823. view->unmap.notify = xdg_toplevel_unmap;
  824. wl_signal_add(&xdg_toplevel->base->surface->events.unmap, &view->unmap);
  825. view->commit.notify = xdg_toplevel_commit;
  826. wl_signal_add(&xdg_toplevel->base->surface->events.commit, &view->commit);
  827. view->destroy.notify = xdg_toplevel_destroy;
  828. wl_signal_add(&xdg_toplevel->events.destroy, &view->destroy);
  829. view->request_move.notify = xdg_toplevel_request_move;
  830. wl_signal_add(&xdg_toplevel->events.request_move, &view->request_move);
  831. view->request_resize.notify = xdg_toplevel_request_resize;
  832. wl_signal_add(&xdg_toplevel->events.request_resize, &view->request_resize);
  833. view->request_maximize.notify = xdg_toplevel_request_maximize;
  834. wl_signal_add(&xdg_toplevel->events.request_maximize, &view->request_maximize);
  835. view->request_minimize.notify = xdg_toplevel_request_minimize;
  836. wl_signal_add(&xdg_toplevel->events.request_minimize, &view->request_minimize);
  837. view->request_fullscreen.notify = xdg_toplevel_request_fullscreen;
  838. wl_signal_add(&xdg_toplevel->events.request_fullscreen, &view->request_fullscreen);
  839. view->set_title.notify = xdg_toplevel_set_title;
  840. wl_signal_add(&xdg_toplevel->events.set_title, &view->set_title);
  841. view->set_app_id.notify = xdg_toplevel_set_app_id;
  842. wl_signal_add(&xdg_toplevel->events.set_app_id, &view->set_app_id);
  843. view_emit_new(view);
  844. }
  845. /* popups */
  846. static void xdg_popup_commit(struct wl_listener *listener, void *data) {
  847. (void) data;
  848. struct mjc_popup *popup = wl_container_of(listener, popup, commit);
  849. if (popup->xdg_popup->base->initial_commit) {
  850. wlr_xdg_surface_schedule_configure(popup->xdg_popup->base);
  851. }
  852. }
  853. static void xdg_popup_destroy(struct wl_listener *listener, void *data) {
  854. (void) data;
  855. struct mjc_popup *popup = wl_container_of(listener, popup, destroy);
  856. wl_list_remove(&popup->commit.link);
  857. wl_list_remove(&popup->destroy.link);
  858. free(popup);
  859. }
  860. static void server_new_xdg_popup(struct wl_listener *listener, void *data) {
  861. (void) listener;
  862. struct wlr_xdg_popup *xdg_popup = data;
  863. struct wlr_xdg_surface *parent =
  864. wlr_xdg_surface_try_from_wlr_surface(xdg_popup->parent);
  865. if (parent == NULL || parent->data == NULL) {
  866. return;
  867. }
  868. struct wlr_scene_tree *parent_tree = parent->data;
  869. xdg_popup->base->data =
  870. wlr_scene_xdg_surface_create(parent_tree, xdg_popup->base);
  871. struct mjc_popup *popup = calloc(1, sizeof(*popup));
  872. popup->xdg_popup = xdg_popup;
  873. popup->commit.notify = xdg_popup_commit;
  874. wl_signal_add(&xdg_popup->base->surface->events.commit, &popup->commit);
  875. popup->destroy.notify = xdg_popup_destroy;
  876. wl_signal_add(&xdg_popup->events.destroy, &popup->destroy);
  877. }
  878. /* decorations - clients draw their own buttons (CSD), the compositor
  879. * renders no frames, so server side mode is never granted */
  880. static void decoration_request_mode(struct wl_listener *listener, void *data) {
  881. (void) data;
  882. struct mjc_decoration *deco = wl_container_of(listener, deco, request_mode);
  883. if (deco->decoration->toplevel->base->initialized) {
  884. wlr_xdg_toplevel_decoration_v1_set_mode(deco->decoration,
  885. WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE);
  886. }
  887. }
  888. static void decoration_destroy(struct wl_listener *listener, void *data) {
  889. (void) data;
  890. struct mjc_decoration *deco = wl_container_of(listener, deco, destroy);
  891. wl_list_remove(&deco->request_mode.link);
  892. wl_list_remove(&deco->destroy.link);
  893. free(deco);
  894. }
  895. static void server_new_toplevel_decoration(struct wl_listener *listener,
  896. void *data) {
  897. (void) listener;
  898. struct wlr_xdg_toplevel_decoration_v1 *decoration = data;
  899. struct mjc_decoration *deco = calloc(1, sizeof(*deco));
  900. deco->decoration = decoration;
  901. deco->request_mode.notify = decoration_request_mode;
  902. wl_signal_add(&decoration->events.request_mode, &deco->request_mode);
  903. deco->destroy.notify = decoration_destroy;
  904. wl_signal_add(&decoration->events.destroy, &deco->destroy);
  905. decoration_request_mode(&deco->request_mode, NULL);
  906. }
  907. /* ------------------------------------------------------------------ */
  908. /* xwayland views */
  909. /* ------------------------------------------------------------------ */
  910. static void xwayland_surface_map(struct wl_listener *listener, void *data) {
  911. (void) data;
  912. struct mjc_view *view = wl_container_of(listener, view, map);
  913. struct mjc_server *server = view->server;
  914. struct wlr_xwayland_surface *xsurface = view->xsurface;
  915. mjc_layer layer = xsurface->override_redirect
  916. ? MJC_LAYER_OVERLAY : view->layer;
  917. view->scene_tree = wlr_scene_tree_create(server->layers[layer]);
  918. view->scene_tree->node.data = view;
  919. wlr_scene_surface_create(view->scene_tree, xsurface->surface);
  920. wlr_scene_node_set_position(&view->scene_tree->node,
  921. xsurface->x, xsurface->y);
  922. view->mapped = true;
  923. /* X clients that did not place themselves land at 0,0 - center those */
  924. if (!xsurface->override_redirect && xsurface->x == 0 && xsurface->y == 0) {
  925. view_center(view);
  926. }
  927. view_emit_map(view);
  928. if (!xsurface->override_redirect) {
  929. mjc_view_focus(view);
  930. }
  931. }
  932. static void xwayland_surface_unmap(struct wl_listener *listener, void *data) {
  933. (void) data;
  934. struct mjc_view *view = wl_container_of(listener, view, unmap);
  935. view->mapped = false;
  936. clear_focus_if(view->server, view);
  937. /* emit while the scene node still exists so the policy side can
  938. * read the last window geometry */
  939. view_emit_unmap(view);
  940. if (view->scene_tree != NULL) {
  941. wlr_scene_node_destroy(&view->scene_tree->node);
  942. view->scene_tree = NULL;
  943. }
  944. }
  945. static void xwayland_surface_associate(struct wl_listener *listener, void *data) {
  946. (void) data;
  947. struct mjc_view *view = wl_container_of(listener, view, associate);
  948. view->map.notify = xwayland_surface_map;
  949. wl_signal_add(&view->xsurface->surface->events.map, &view->map);
  950. view->unmap.notify = xwayland_surface_unmap;
  951. wl_signal_add(&view->xsurface->surface->events.unmap, &view->unmap);
  952. }
  953. static void xwayland_surface_dissociate(struct wl_listener *listener, void *data) {
  954. (void) data;
  955. struct mjc_view *view = wl_container_of(listener, view, dissociate);
  956. wl_list_remove(&view->map.link);
  957. wl_list_remove(&view->unmap.link);
  958. }
  959. static void xwayland_surface_destroy(struct wl_listener *listener, void *data) {
  960. (void) data;
  961. struct mjc_view *view = wl_container_of(listener, view, destroy);
  962. wl_list_remove(&view->associate.link);
  963. wl_list_remove(&view->dissociate.link);
  964. wl_list_remove(&view->destroy.link);
  965. wl_list_remove(&view->request_configure.link);
  966. wl_list_remove(&view->request_move.link);
  967. wl_list_remove(&view->request_resize.link);
  968. wl_list_remove(&view->request_minimize.link);
  969. wl_list_remove(&view->request_maximize.link);
  970. wl_list_remove(&view->set_title.link);
  971. wl_list_remove(&view->set_app_id.link);
  972. wl_list_remove(&view->set_geometry.link);
  973. if (view->scene_tree != NULL) {
  974. wlr_scene_node_destroy(&view->scene_tree->node);
  975. view->scene_tree = NULL;
  976. }
  977. view_free(view);
  978. }
  979. static void xwayland_surface_request_configure(struct wl_listener *listener,
  980. void *data) {
  981. struct mjc_view *view = wl_container_of(listener, view, request_configure);
  982. struct wlr_xwayland_surface_configure_event *event = data;
  983. wlr_xwayland_surface_configure(view->xsurface,
  984. event->x, event->y, event->width, event->height);
  985. if (view->scene_tree != NULL) {
  986. wlr_scene_node_set_position(&view->scene_tree->node, event->x, event->y);
  987. }
  988. }
  989. static void xwayland_surface_set_geometry(struct wl_listener *listener,
  990. void *data) {
  991. (void) data;
  992. struct mjc_view *view = wl_container_of(listener, view, set_geometry);
  993. if (view->scene_tree != NULL && view->xsurface != NULL) {
  994. wlr_scene_node_set_position(&view->scene_tree->node,
  995. view->xsurface->x, view->xsurface->y);
  996. }
  997. }
  998. static void xwayland_surface_request_move(struct wl_listener *listener,
  999. void *data) {
  1000. (void) data;
  1001. struct mjc_view *view = wl_container_of(listener, view, request_move);
  1002. begin_interactive(view, MJC_CURSOR_MOVE, 0);
  1003. }
  1004. static void xwayland_surface_request_resize(struct wl_listener *listener,
  1005. void *data) {
  1006. struct mjc_view *view = wl_container_of(listener, view, request_resize);
  1007. struct wlr_xwayland_resize_event *event = data;
  1008. begin_interactive(view, MJC_CURSOR_RESIZE, event->edges);
  1009. }
  1010. static void xwayland_surface_request_minimize(struct wl_listener *listener,
  1011. void *data) {
  1012. (void) data;
  1013. struct mjc_view *view = wl_container_of(listener, view, request_minimize);
  1014. mjc_view_set_minimized(view, true);
  1015. }
  1016. static void xwayland_surface_request_maximize(struct wl_listener *listener,
  1017. void *data) {
  1018. (void) data;
  1019. struct mjc_view *view = wl_container_of(listener, view, request_maximize);
  1020. struct wlr_xwayland_surface *xsurface = view->xsurface;
  1021. if (xsurface == NULL) {
  1022. return;
  1023. }
  1024. /* the client announces the state it wants via _NET_WM_STATE */
  1025. mjc_view_set_maximized(view,
  1026. xsurface->maximized_vert && xsurface->maximized_horz);
  1027. }
  1028. static void xwayland_surface_set_title(struct wl_listener *listener, void *data) {
  1029. (void) data;
  1030. struct mjc_view *view = wl_container_of(listener, view, set_title);
  1031. if (view->server->cbs.view_title != NULL) {
  1032. view->server->cbs.view_title(view->server->ud, view,
  1033. view->xsurface->title);
  1034. }
  1035. }
  1036. static void xwayland_surface_set_class(struct wl_listener *listener, void *data) {
  1037. (void) data;
  1038. struct mjc_view *view = wl_container_of(listener, view, set_app_id);
  1039. if (view->server->cbs.view_app_id != NULL) {
  1040. view->server->cbs.view_app_id(view->server->ud, view,
  1041. view->xsurface->class);
  1042. }
  1043. }
  1044. static void server_new_xwayland_surface(struct wl_listener *listener, void *data) {
  1045. struct mjc_server *server =
  1046. wl_container_of(listener, server, new_xwayland_surface);
  1047. struct wlr_xwayland_surface *xsurface = data;
  1048. struct mjc_view *view = view_create(server, true);
  1049. view->xsurface = xsurface;
  1050. if (xsurface->override_redirect) {
  1051. view->focusable = false;
  1052. }
  1053. xsurface->data = view;
  1054. view->associate.notify = xwayland_surface_associate;
  1055. wl_signal_add(&xsurface->events.associate, &view->associate);
  1056. view->dissociate.notify = xwayland_surface_dissociate;
  1057. wl_signal_add(&xsurface->events.dissociate, &view->dissociate);
  1058. view->destroy.notify = xwayland_surface_destroy;
  1059. wl_signal_add(&xsurface->events.destroy, &view->destroy);
  1060. view->request_configure.notify = xwayland_surface_request_configure;
  1061. wl_signal_add(&xsurface->events.request_configure, &view->request_configure);
  1062. view->request_move.notify = xwayland_surface_request_move;
  1063. wl_signal_add(&xsurface->events.request_move, &view->request_move);
  1064. view->request_resize.notify = xwayland_surface_request_resize;
  1065. wl_signal_add(&xsurface->events.request_resize, &view->request_resize);
  1066. view->request_minimize.notify = xwayland_surface_request_minimize;
  1067. wl_signal_add(&xsurface->events.request_minimize, &view->request_minimize);
  1068. view->request_maximize.notify = xwayland_surface_request_maximize;
  1069. wl_signal_add(&xsurface->events.request_maximize, &view->request_maximize);
  1070. view->set_title.notify = xwayland_surface_set_title;
  1071. wl_signal_add(&xsurface->events.set_title, &view->set_title);
  1072. view->set_app_id.notify = xwayland_surface_set_class;
  1073. wl_signal_add(&xsurface->events.set_class, &view->set_app_id);
  1074. view->set_geometry.notify = xwayland_surface_set_geometry;
  1075. wl_signal_add(&xsurface->events.set_geometry, &view->set_geometry);
  1076. view_emit_new(view);
  1077. }
  1078. static void server_xwayland_ready(struct wl_listener *listener, void *data) {
  1079. (void) data;
  1080. struct mjc_server *server = wl_container_of(listener, server, xwayland_ready);
  1081. wlr_xwayland_set_seat(server->xwayland, server->seat);
  1082. }
  1083. /* ------------------------------------------------------------------ */
  1084. /* sigchld */
  1085. /* ------------------------------------------------------------------ */
  1086. static int handle_sigchld(int signal_number, void *data) {
  1087. (void) signal_number;
  1088. struct mjc_server *server = data;
  1089. for (int i = 0; i < server->spawned_count;) {
  1090. int status;
  1091. pid_t pid = waitpid(server->spawned_pids[i], &status, WNOHANG);
  1092. if (pid > 0) {
  1093. server->spawned_pids[i] =
  1094. server->spawned_pids[--server->spawned_count];
  1095. if (server->cbs.child_exit != NULL) {
  1096. server->cbs.child_exit(server->ud, (int) pid, status);
  1097. }
  1098. } else {
  1099. i++;
  1100. }
  1101. }
  1102. return 0;
  1103. }
  1104. /* ------------------------------------------------------------------ */
  1105. /* public API */
  1106. /* ------------------------------------------------------------------ */
  1107. mjc_server *mjc_create(void) {
  1108. struct mjc_server *server = calloc(1, sizeof(*server));
  1109. wl_list_init(&server->views);
  1110. wl_list_init(&server->keyboards);
  1111. wl_list_init(&server->fd_sources);
  1112. return server;
  1113. }
  1114. bool mjc_start(mjc_server *server, const mjc_callbacks *callbacks, void *userdata) {
  1115. server->cbs = *callbacks;
  1116. server->ud = userdata;
  1117. wlr_log_init(WLR_INFO, NULL);
  1118. /* nested-output size, 16:9 default; override with MJDEVC_OUTPUT=WIDTHxHEIGHT */
  1119. server->output_w = 1280;
  1120. server->output_h = 720;
  1121. const char *out_env = getenv("MJDEVC_OUTPUT");
  1122. if (out_env != NULL) {
  1123. int w = 0, h = 0;
  1124. if (sscanf(out_env, "%dx%d", &w, &h) == 2 && w > 0 && h > 0) {
  1125. server->output_w = w;
  1126. server->output_h = h;
  1127. }
  1128. }
  1129. server->display = wl_display_create();
  1130. server->loop = wl_display_get_event_loop(server->display);
  1131. server->backend = wlr_backend_autocreate(server->loop, &server->session);
  1132. if (server->backend == NULL) {
  1133. fprintf(stderr, "mjdevc: failed to create wlr_backend\n");
  1134. return false;
  1135. }
  1136. server->renderer = wlr_renderer_autocreate(server->backend);
  1137. if (server->renderer == NULL) {
  1138. fprintf(stderr, "mjdevc: failed to create wlr_renderer\n");
  1139. return false;
  1140. }
  1141. wlr_renderer_init_wl_display(server->renderer, server->display);
  1142. server->allocator = wlr_allocator_autocreate(server->backend, server->renderer);
  1143. if (server->allocator == NULL) {
  1144. fprintf(stderr, "mjdevc: failed to create wlr_allocator\n");
  1145. return false;
  1146. }
  1147. server->compositor = wlr_compositor_create(server->display, 5, server->renderer);
  1148. wlr_subcompositor_create(server->display);
  1149. wlr_data_device_manager_create(server->display);
  1150. server->output_layout = wlr_output_layout_create(server->display);
  1151. server->new_output.notify = server_new_output;
  1152. wl_signal_add(&server->backend->events.new_output, &server->new_output);
  1153. server->scene = wlr_scene_create();
  1154. server->scene_layout =
  1155. wlr_scene_attach_output_layout(server->scene, server->output_layout);
  1156. for (int i = 0; i < 5; i++) {
  1157. server->layers[i] = wlr_scene_tree_create(&server->scene->tree);
  1158. }
  1159. server->xdg_shell = wlr_xdg_shell_create(server->display, 3);
  1160. server->new_xdg_toplevel.notify = server_new_xdg_toplevel;
  1161. wl_signal_add(&server->xdg_shell->events.new_toplevel, &server->new_xdg_toplevel);
  1162. server->new_xdg_popup.notify = server_new_xdg_popup;
  1163. wl_signal_add(&server->xdg_shell->events.new_popup, &server->new_xdg_popup);
  1164. server->xdg_decoration_mgr = wlr_xdg_decoration_manager_v1_create(server->display);
  1165. server->new_toplevel_decoration.notify = server_new_toplevel_decoration;
  1166. wl_signal_add(&server->xdg_decoration_mgr->events.new_toplevel_decoration,
  1167. &server->new_toplevel_decoration);
  1168. server->cursor = wlr_cursor_create();
  1169. wlr_cursor_attach_output_layout(server->cursor, server->output_layout);
  1170. server->cursor_mgr = wlr_xcursor_manager_create(NULL, 24);
  1171. server->cursor_mode = MJC_CURSOR_PASSTHROUGH;
  1172. server->cursor_motion.notify = server_cursor_motion;
  1173. wl_signal_add(&server->cursor->events.motion, &server->cursor_motion);
  1174. server->cursor_motion_absolute.notify = server_cursor_motion_absolute;
  1175. wl_signal_add(&server->cursor->events.motion_absolute,
  1176. &server->cursor_motion_absolute);
  1177. server->cursor_button.notify = server_cursor_button;
  1178. wl_signal_add(&server->cursor->events.button, &server->cursor_button);
  1179. server->cursor_axis.notify = server_cursor_axis;
  1180. wl_signal_add(&server->cursor->events.axis, &server->cursor_axis);
  1181. server->cursor_frame.notify = server_cursor_frame;
  1182. wl_signal_add(&server->cursor->events.frame, &server->cursor_frame);
  1183. server->new_input.notify = server_new_input;
  1184. wl_signal_add(&server->backend->events.new_input, &server->new_input);
  1185. server->seat = wlr_seat_create(server->display, "seat0");
  1186. server->request_cursor.notify = seat_request_cursor;
  1187. wl_signal_add(&server->seat->events.request_set_cursor, &server->request_cursor);
  1188. server->request_set_selection.notify = seat_request_set_selection;
  1189. wl_signal_add(&server->seat->events.request_set_selection,
  1190. &server->request_set_selection);
  1191. const char *socket = wl_display_add_socket_auto(server->display);
  1192. if (socket == NULL) {
  1193. fprintf(stderr, "mjdevc: failed to create wayland socket\n");
  1194. return false;
  1195. }
  1196. server->socket = socket;
  1197. server->xwayland = wlr_xwayland_create(server->display, server->compositor, true);
  1198. if (server->xwayland != NULL) {
  1199. server->new_xwayland_surface.notify = server_new_xwayland_surface;
  1200. wl_signal_add(&server->xwayland->events.new_surface,
  1201. &server->new_xwayland_surface);
  1202. server->xwayland_ready.notify = server_xwayland_ready;
  1203. wl_signal_add(&server->xwayland->events.ready, &server->xwayland_ready);
  1204. }
  1205. server->sigchld_source = wl_event_loop_add_signal(server->loop,
  1206. SIGCHLD, handle_sigchld, server);
  1207. if (!wlr_backend_start(server->backend)) {
  1208. fprintf(stderr, "mjdevc: failed to start backend\n");
  1209. return false;
  1210. }
  1211. setenv("WAYLAND_DISPLAY", socket, true);
  1212. if (server->xwayland != NULL) {
  1213. setenv("DISPLAY", server->xwayland->display_name, true);
  1214. }
  1215. if (server->cbs.ready != NULL) {
  1216. server->cbs.ready(server->ud);
  1217. }
  1218. return true;
  1219. }
  1220. void mjc_run(mjc_server *server) {
  1221. wl_display_run(server->display);
  1222. }
  1223. void mjc_terminate(mjc_server *server) {
  1224. wl_display_terminate(server->display);
  1225. }
  1226. void mjc_destroy(mjc_server *server) {
  1227. if (server->xwayland != NULL) {
  1228. wlr_xwayland_destroy(server->xwayland);
  1229. }
  1230. wl_display_destroy_clients(server->display);
  1231. wlr_scene_node_destroy(&server->scene->tree.node);
  1232. wlr_xcursor_manager_destroy(server->cursor_mgr);
  1233. wlr_cursor_destroy(server->cursor);
  1234. wlr_allocator_destroy(server->allocator);
  1235. wlr_renderer_destroy(server->renderer);
  1236. wlr_backend_destroy(server->backend);
  1237. wl_display_destroy(server->display);
  1238. free(server);
  1239. }
  1240. const char *mjc_socket_name(mjc_server *server) {
  1241. return server->socket;
  1242. }
  1243. const char *mjc_xwayland_display(mjc_server *server) {
  1244. return server->xwayland != NULL ? server->xwayland->display_name : NULL;
  1245. }
  1246. void mjc_output_size(mjc_server *server, int *width, int *height) {
  1247. struct wlr_box box;
  1248. wlr_output_layout_get_box(server->output_layout, NULL, &box);
  1249. *width = box.width;
  1250. *height = box.height;
  1251. }
  1252. int mjc_spawn(mjc_server *server, const char *command) {
  1253. pid_t pid = fork();
  1254. if (pid < 0) {
  1255. return -1;
  1256. }
  1257. if (pid == 0) {
  1258. setsid();
  1259. sigset_t set;
  1260. sigemptyset(&set);
  1261. sigprocmask(SIG_SETMASK, &set, NULL);
  1262. execl("/bin/sh", "/bin/sh", "-c", command, (void *) NULL);
  1263. _exit(127);
  1264. }
  1265. if (server->spawned_count <
  1266. (int) (sizeof(server->spawned_pids) / sizeof(server->spawned_pids[0]))) {
  1267. server->spawned_pids[server->spawned_count++] = pid;
  1268. }
  1269. return (int) pid;
  1270. }
  1271. /* unix socket helpers */
  1272. #include <fcntl.h>
  1273. #include <sys/socket.h>
  1274. #include <sys/un.h>
  1275. int mjc_unix_listen(const char *path, int backlog) {
  1276. int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
  1277. if (fd < 0) {
  1278. return -1;
  1279. }
  1280. struct sockaddr_un addr = {0};
  1281. addr.sun_family = AF_UNIX;
  1282. if (strlen(path) >= sizeof(addr.sun_path)) {
  1283. close(fd);
  1284. return -1;
  1285. }
  1286. strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
  1287. unlink(path);
  1288. if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) != 0 ||
  1289. listen(fd, backlog) != 0) {
  1290. close(fd);
  1291. return -1;
  1292. }
  1293. return fd;
  1294. }
  1295. int mjc_unix_accept(int server_fd) {
  1296. int fd = accept(server_fd, NULL, NULL);
  1297. if (fd >= 0) {
  1298. fcntl(fd, F_SETFL, O_NONBLOCK);
  1299. }
  1300. return fd;
  1301. }
  1302. /* fd integration */
  1303. static int handle_fd_event(int fd, uint32_t mask, void *data) {
  1304. struct mjc_fd_source *source = data;
  1305. if (source->server->cbs.fd_event != NULL) {
  1306. return source->server->cbs.fd_event(source->server->ud, fd, mask);
  1307. }
  1308. return 0;
  1309. }
  1310. bool mjc_loop_add_fd(mjc_server *server, int fd, uint32_t mask) {
  1311. struct mjc_fd_source *source = calloc(1, sizeof(*source));
  1312. source->server = server;
  1313. source->fd = fd;
  1314. source->source = wl_event_loop_add_fd(server->loop, fd, mask,
  1315. handle_fd_event, source);
  1316. if (source->source == NULL) {
  1317. free(source);
  1318. return false;
  1319. }
  1320. wl_list_insert(&server->fd_sources, &source->link);
  1321. return true;
  1322. }
  1323. void mjc_loop_remove_fd(mjc_server *server, int fd) {
  1324. struct mjc_fd_source *source, *tmp;
  1325. wl_list_for_each_safe(source, tmp, &server->fd_sources, link) {
  1326. if (source->fd == fd) {
  1327. wl_event_source_remove(source->source);
  1328. wl_list_remove(&source->link);
  1329. free(source);
  1330. return;
  1331. }
  1332. }
  1333. }
  1334. /* views */
  1335. uint64_t mjc_view_id(mjc_view *view) {
  1336. return view->id;
  1337. }
  1338. int mjc_view_pid(mjc_view *view) {
  1339. if (view->is_xwayland) {
  1340. return view->xsurface != NULL ? (int) view->xsurface->pid : 0;
  1341. }
  1342. pid_t pid = 0;
  1343. struct wlr_surface *surface = view_surface(view);
  1344. if (surface != NULL) {
  1345. struct wl_client *client = wl_resource_get_client(surface->resource);
  1346. wl_client_get_credentials(client, &pid, NULL, NULL);
  1347. }
  1348. return (int) pid;
  1349. }
  1350. const char *mjc_view_title(mjc_view *view) {
  1351. if (view->is_xwayland) {
  1352. return view->xsurface != NULL ? view->xsurface->title : NULL;
  1353. }
  1354. return view->xdg_toplevel->title;
  1355. }
  1356. const char *mjc_view_app_id(mjc_view *view) {
  1357. if (view->is_xwayland) {
  1358. return view->xsurface != NULL ? view->xsurface->class : NULL;
  1359. }
  1360. return view->xdg_toplevel->app_id;
  1361. }
  1362. bool mjc_view_is_xwayland(mjc_view *view) {
  1363. return view->is_xwayland;
  1364. }
  1365. bool mjc_view_is_mapped(mjc_view *view) {
  1366. return view->mapped;
  1367. }
  1368. bool mjc_view_is_minimized(mjc_view *view) {
  1369. return view->minimized;
  1370. }
  1371. bool mjc_view_is_maximized(mjc_view *view) {
  1372. return view->maximized;
  1373. }
  1374. bool mjc_view_is_focused(mjc_view *view) {
  1375. return view->server->focused_view == view;
  1376. }
  1377. void mjc_view_get_geometry(mjc_view *view, int *x, int *y, int *width, int *height) {
  1378. struct wlr_box box = {0};
  1379. if (view->scene_tree != NULL || !view->is_xwayland) {
  1380. view_current_geometry(view, &box);
  1381. }
  1382. *x = box.x;
  1383. *y = box.y;
  1384. *width = box.width;
  1385. *height = box.height;
  1386. }
  1387. void mjc_view_close(mjc_view *view) {
  1388. if (view->is_xwayland) {
  1389. if (view->xsurface != NULL) {
  1390. wlr_xwayland_surface_close(view->xsurface);
  1391. }
  1392. } else {
  1393. wlr_xdg_toplevel_send_close(view->xdg_toplevel);
  1394. }
  1395. }
  1396. void mjc_view_set_minimized(mjc_view *view, bool minimized) {
  1397. if (view->minimized == minimized) {
  1398. return;
  1399. }
  1400. view->minimized = minimized;
  1401. if (view->scene_tree != NULL) {
  1402. wlr_scene_node_set_enabled(&view->scene_tree->node, !minimized);
  1403. }
  1404. if (view->is_xwayland && view->xsurface != NULL) {
  1405. wlr_xwayland_surface_set_minimized(view->xsurface, minimized);
  1406. }
  1407. if (minimized) {
  1408. clear_focus_if(view->server, view);
  1409. } else {
  1410. mjc_view_focus(view);
  1411. }
  1412. }
  1413. void mjc_view_set_maximized(mjc_view *view, bool maximized) {
  1414. struct mjc_server *server = view->server;
  1415. if (view->maximized == maximized) {
  1416. return;
  1417. }
  1418. struct wlr_box box;
  1419. wlr_output_layout_get_box(server->output_layout, NULL, &box);
  1420. if (maximized && view->mapped && view->scene_tree != NULL) {
  1421. /* remember the floating geometry (visible coords) for restore */
  1422. view_current_geometry(view, &view->saved_geo);
  1423. }
  1424. view->maximized = maximized;
  1425. if (view->is_xwayland) {
  1426. if (view->xsurface == NULL) {
  1427. return;
  1428. }
  1429. wlr_xwayland_surface_set_maximized(view->xsurface, maximized);
  1430. if (maximized) {
  1431. wlr_xwayland_surface_configure(view->xsurface,
  1432. (int16_t) box.x, (int16_t) box.y,
  1433. (uint16_t) box.width, (uint16_t) box.height);
  1434. if (view->scene_tree != NULL) {
  1435. wlr_scene_node_set_position(&view->scene_tree->node, box.x, box.y);
  1436. }
  1437. } else if (view->saved_geo.width > 0) {
  1438. wlr_xwayland_surface_configure(view->xsurface,
  1439. (int16_t) view->saved_geo.x, (int16_t) view->saved_geo.y,
  1440. (uint16_t) view->saved_geo.width, (uint16_t) view->saved_geo.height);
  1441. if (view->scene_tree != NULL) {
  1442. wlr_scene_node_set_position(&view->scene_tree->node,
  1443. view->saved_geo.x, view->saved_geo.y);
  1444. }
  1445. }
  1446. } else {
  1447. wlr_xdg_toplevel_set_maximized(view->xdg_toplevel, maximized);
  1448. if (maximized) {
  1449. wlr_xdg_toplevel_set_size(view->xdg_toplevel, box.width, box.height);
  1450. if (view->scene_tree != NULL) {
  1451. wlr_scene_node_set_position(&view->scene_tree->node, box.x, box.y);
  1452. }
  1453. } else {
  1454. /* 0x0 when no remembered size - the client picks its own;
  1455. * placement happens on the commit that carries the new size,
  1456. * when the decoration offsets are known again */
  1457. wlr_xdg_toplevel_set_size(view->xdg_toplevel,
  1458. view->saved_geo.width, view->saved_geo.height);
  1459. view->pending_center = true;
  1460. }
  1461. }
  1462. }
  1463. void mjc_view_set_layer(mjc_view *view, mjc_layer layer) {
  1464. view->layer = layer;
  1465. if (view->scene_tree != NULL) {
  1466. wlr_scene_node_reparent(&view->scene_tree->node,
  1467. view->server->layers[layer]);
  1468. }
  1469. }
  1470. void mjc_view_set_focusable(mjc_view *view, bool focusable) {
  1471. view->focusable = focusable;
  1472. if (!focusable) {
  1473. clear_focus_if(view->server, view);
  1474. }
  1475. }
  1476. void mjc_view_set_position(mjc_view *view, int x, int y) {
  1477. if (view->scene_tree != NULL) {
  1478. wlr_scene_node_set_position(&view->scene_tree->node, x, y);
  1479. }
  1480. if (view->is_xwayland && view->xsurface != NULL) {
  1481. wlr_xwayland_surface_configure(view->xsurface,
  1482. (int16_t) x, (int16_t) y,
  1483. (uint16_t) view->xsurface->width, (uint16_t) view->xsurface->height);
  1484. }
  1485. }
  1486. void mjc_view_set_geometry(mjc_view *view, int x, int y, int width, int height) {
  1487. if (width <= 0 || height <= 0) {
  1488. return;
  1489. }
  1490. if (view->maximized) {
  1491. /* keep it as the restore target for the next unmaximize */
  1492. view->saved_geo.x = x;
  1493. view->saved_geo.y = y;
  1494. view->saved_geo.width = width;
  1495. view->saved_geo.height = height;
  1496. return;
  1497. }
  1498. if (view->is_xwayland) {
  1499. if (view->xsurface != NULL) {
  1500. wlr_xwayland_surface_configure(view->xsurface,
  1501. (int16_t) x, (int16_t) y, (uint16_t) width, (uint16_t) height);
  1502. }
  1503. if (view->scene_tree != NULL) {
  1504. wlr_scene_node_set_position(&view->scene_tree->node, x, y);
  1505. }
  1506. } else {
  1507. wlr_xdg_toplevel_set_size(view->xdg_toplevel, width, height);
  1508. if (view->scene_tree != NULL) {
  1509. struct wlr_box off;
  1510. wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &off);
  1511. wlr_scene_node_set_position(&view->scene_tree->node,
  1512. x - off.x, y - off.y);
  1513. }
  1514. }
  1515. }