wl_client_side_decorations.c 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874
  1. /*
  2. * wl_client_side_decorations.c
  3. * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>
  4. *
  5. * Distributed under terms of the GPL3 license.
  6. */
  7. #include "wl_client_side_decorations.h"
  8. #include "backend_utils.h"
  9. #include <sys/mman.h>
  10. #include <errno.h>
  11. #include <string.h>
  12. #include <stdlib.h>
  13. #define decs window->wl.decorations
  14. #define debug debug_rendering
  15. #define ARGB(a, r, g, b) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
  16. #define A(x) (((x) >> 24) & 0xff)
  17. #define R(x) (((x) >> 16) & 0xff)
  18. #define G(x) (((x) >> 8) & 0xff)
  19. #define B(x) ((x) & 0xff)
  20. #define SWAP(x, y) do { __typeof__(x) SWAP = x; x = y; y = SWAP; } while (0)
  21. // shadow tile {{{
  22. typedef float kernel_type;
  23. static void
  24. build_blur_kernel(kernel_type *blur_kernel, const size_t size, kernel_type sigma) {
  25. // 1D Normalized Gaussian
  26. const kernel_type half = size / (kernel_type)2;
  27. kernel_type sum = 0;
  28. for (size_t i = 0; i < size; i++) {
  29. kernel_type f = (i - half);
  30. blur_kernel[i] = (kernel_type)exp(- f * f / sigma);
  31. sum += blur_kernel[i];
  32. }
  33. for (size_t i = 0; i < size; i++) blur_kernel[i] /= sum;
  34. }
  35. static void
  36. blur_mask(kernel_type *image_data, ssize_t width, ssize_t height, ssize_t kernel_size, kernel_type sigma, kernel_type *scratch, kernel_type *blur_kernel, ssize_t margin) {
  37. (void)margin;
  38. build_blur_kernel(blur_kernel, kernel_size, sigma);
  39. const size_t half = kernel_size / 2;
  40. for (ssize_t y = 0; y < height; y++) {
  41. kernel_type *s = image_data + y * width, *d = scratch + y * width;
  42. for (ssize_t x = 0; x < width; x++) {
  43. kernel_type a = 0;
  44. for (ssize_t k = 0; k < kernel_size; k++) {
  45. const ssize_t px = x + k - half;
  46. if (0 <= px && px < width) a += s[px] * blur_kernel[k];
  47. }
  48. d[x] = a;
  49. }
  50. }
  51. for (ssize_t y = 0; y < height; y++) {
  52. kernel_type *d = image_data + y * width;
  53. for (ssize_t x = 0; x < width; x++) {
  54. kernel_type a = 0;
  55. for (ssize_t k = 0; k < kernel_size; k++) {
  56. const ssize_t py = y + k - half;
  57. if (0 <= py && py < height) {
  58. kernel_type *s = scratch + py * width;
  59. a += s[x] * blur_kernel[k];
  60. }
  61. }
  62. d[x] = a;
  63. }
  64. }
  65. }
  66. static kernel_type*
  67. create_shadow_mask(size_t width, size_t height, size_t margin, size_t kernel_size, kernel_type base_alpha, kernel_type sigma) {
  68. kernel_type *mask = calloc(2 * width * height + kernel_size, sizeof(kernel_type));
  69. if (!mask) return NULL;
  70. for (size_t y = margin; y < height - margin; y++) {
  71. kernel_type *row = mask + y * width;
  72. for (size_t x = margin; x < width - margin; x++) row[x] = base_alpha;
  73. }
  74. blur_mask(mask, width, height, kernel_size, sigma, mask + width * height, (kernel_type*)(mask + 2 * width * height), margin);
  75. return mask;
  76. }
  77. #define st decs.shadow_tile
  78. static size_t
  79. create_shadow_tile(_GLFWwindow *window) {
  80. const size_t margin = (size_t)round(decs.metrics.width * decs.for_window_state.fscale);
  81. if (st.data && st.for_decoration_size == margin) return margin;
  82. st.for_decoration_size = margin;
  83. free(st.data);
  84. st.segments = 7;
  85. st.stride = st.segments * margin;
  86. st.corner_size = margin * (st.segments - 1) / 2;
  87. kernel_type* mask = create_shadow_mask(st.stride, st.stride, margin, 2 * margin + 1, (kernel_type)0.7, 32 * margin);
  88. st.data = malloc(sizeof(uint32_t) * st.stride * st.stride);
  89. if (st.data) for (size_t i = 0; i < st.stride * st.stride; i++) st.data[i] = ((uint8_t)(mask[i] * 255)) << 24;
  90. free(mask);
  91. return margin;
  92. }
  93. // }}}
  94. static bool window_needs_shadows(_GLFWwindow *w) { return !(w->wl.current.toplevel_states & TOPLEVEL_STATE_DOCKED); }
  95. static void
  96. swap_buffers(_GLFWWaylandBufferPair *pair) {
  97. SWAP(pair->front, pair->back);
  98. SWAP(pair->data.front, pair->data.back);
  99. }
  100. static size_t
  101. init_buffer_pair(_GLFWWaylandBufferPair *pair, size_t width, size_t height, double scale) {
  102. memset(pair, 0, sizeof(_GLFWWaylandBufferPair));
  103. pair->width = (int)round(width * scale);
  104. pair->height = (int)round(height * scale);
  105. pair->viewport_width = width; pair->viewport_height = height;
  106. pair->stride = 4 * pair->width;
  107. pair->size_in_bytes = pair->stride * pair->height;
  108. return 2 * pair->size_in_bytes;
  109. }
  110. #define all_shadow_surfaces(Q) Q(shadow_left); Q(shadow_top); Q(shadow_right); Q(shadow_bottom); \
  111. Q(shadow_upper_left); Q(shadow_upper_right); Q(shadow_lower_left); Q(shadow_lower_right);
  112. #define all_surfaces(Q) Q(titlebar); all_shadow_surfaces(Q);
  113. static bool
  114. window_has_buffer(_GLFWwindow *window, struct wl_buffer *q) {
  115. #define Q(which) if (decs.which.buffer.a == q) { decs.which.buffer.a_needs_to_be_destroyed = false; return true; } if (decs.which.buffer.b == q) { decs.which.buffer.b_needs_to_be_destroyed = false; return true; }
  116. all_surfaces(Q);
  117. #undef Q
  118. return false;
  119. }
  120. static void
  121. buffer_release_event(void *data, struct wl_buffer *buffer) {
  122. wl_buffer_destroy(buffer);
  123. _GLFWwindow *window = _glfwWindowForId((uintptr_t)data);
  124. if (window && window_has_buffer(window, buffer)) decs.buffer_destroyed = true;
  125. }
  126. static struct wl_buffer_listener handle_buffer_events = {.release = buffer_release_event};
  127. static void
  128. alloc_buffer_pair(uintptr_t window_id, _GLFWWaylandBufferPair *pair, struct wl_shm_pool *pool, uint8_t *data, size_t *offset) {
  129. pair->data.a = data + *offset;
  130. pair->a = wl_shm_pool_create_buffer(pool, *offset, pair->width, pair->height, pair->stride, WL_SHM_FORMAT_ARGB8888);
  131. pair->a_needs_to_be_destroyed = true;
  132. wl_buffer_add_listener(pair->a, &handle_buffer_events, (void*)window_id);
  133. *offset += pair->size_in_bytes;
  134. pair->data.b = data + *offset;
  135. pair->b = wl_shm_pool_create_buffer(pool, *offset, pair->width, pair->height, pair->stride, WL_SHM_FORMAT_ARGB8888);
  136. pair->b_needs_to_be_destroyed = true;
  137. wl_buffer_add_listener(pair->b, &handle_buffer_events, (void*)window_id);
  138. *offset += pair->size_in_bytes;
  139. pair->front = pair->a; pair->back = pair->b;
  140. pair->data.front = pair->data.a; pair->data.back = pair->data.b;
  141. }
  142. void
  143. csd_initialize_metrics(_GLFWwindow *window) {
  144. decs.metrics.width = 12;
  145. decs.metrics.top = 36;
  146. decs.metrics.visible_titlebar_height = decs.metrics.top - decs.metrics.width;
  147. decs.metrics.horizontal = 2 * decs.metrics.width;
  148. decs.metrics.vertical = decs.metrics.width + decs.metrics.top;
  149. }
  150. static void
  151. patch_titlebar_with_alpha_mask(uint32_t *dest, uint8_t *src, unsigned height, unsigned dest_stride, unsigned src_width, unsigned dest_left, uint32_t bg, uint32_t fg) {
  152. for (unsigned y = 0; y < height; y++, src += src_width, dest += dest_stride) {
  153. uint32_t *d = dest + dest_left;
  154. for (unsigned i = 0; i < src_width; i++) {
  155. const uint8_t alpha = src[i], calpha = 255 - alpha;
  156. // Blend the red and blue components
  157. uint32_t ans = ((bg & 0xff00ff) * calpha + (fg & 0xff00ff) * alpha) & 0xff00ff00;
  158. // Blend the green component
  159. ans += ((bg & 0xff00) * calpha + (fg & 0xff00) * alpha) & 0xff0000;
  160. ans >>= 8;
  161. d[i] = ans | 0xff000000;
  162. }
  163. }
  164. }
  165. static void
  166. render_hline(uint8_t *out, unsigned width, unsigned thickness, unsigned bottom, unsigned left, unsigned right) {
  167. for (unsigned y = bottom - thickness; y < bottom; y++) {
  168. uint8_t *dest = out + width * y;
  169. for (unsigned x = left; x < right; x++) dest[x] = 255;
  170. }
  171. }
  172. static void
  173. render_vline(uint8_t *out, unsigned width, unsigned thickness, unsigned left, unsigned top, unsigned bottom) {
  174. for (unsigned y = top; y < bottom; y++) {
  175. uint8_t *dest = out + width * y;
  176. for (unsigned x = left; x < left + thickness; x++) dest[x] = 255;
  177. }
  178. }
  179. static int
  180. scale(unsigned thickness, float factor) {
  181. return (unsigned)(roundf(thickness * factor));
  182. }
  183. static void
  184. render_minimize(uint8_t *out, unsigned width, unsigned height) {
  185. memset(out, 0, width * height);
  186. unsigned thickness = height / 12;
  187. unsigned baseline = height - thickness * 2;
  188. unsigned side_margin = scale(thickness, 3.8f);
  189. if (!thickness || width <= side_margin || height < baseline + 2 * thickness) return;
  190. render_hline(out, width, thickness, baseline, side_margin, width - side_margin);
  191. }
  192. static void
  193. render_maximize(uint8_t *out, unsigned width, unsigned height) {
  194. memset(out, 0, width * height);
  195. unsigned thickness = height / 12, half_thickness = thickness / 2;
  196. unsigned baseline = height - thickness * 2;
  197. unsigned side_margin = scale(thickness, 3.0f);
  198. unsigned top = 4 * thickness;
  199. if (!half_thickness || width <= side_margin || height < baseline + 2 * thickness || top >= baseline) return;
  200. render_hline(out, width, half_thickness, baseline, side_margin, width - side_margin);
  201. render_hline(out, width, thickness, top + thickness, side_margin, width - side_margin);
  202. render_vline(out, width, half_thickness, side_margin, top, baseline);
  203. render_vline(out, width, half_thickness, width - side_margin, top, baseline);
  204. }
  205. static void
  206. render_restore(uint8_t *out, unsigned width, unsigned height) {
  207. memset(out, 0, width * height);
  208. unsigned thickness = height / 12, half_thickness = thickness / 2;
  209. unsigned baseline = height - thickness * 2;
  210. unsigned side_margin = scale(thickness, 3.0f);
  211. unsigned top = 4 * thickness;
  212. if (!half_thickness || width <= side_margin || height < baseline + 2 * thickness || top >= baseline) return;
  213. unsigned box_height = ((baseline - top) * 3) / 4;
  214. if (box_height < 2*thickness) return;
  215. unsigned box_width = ((width - 2 * side_margin) * 3) / 4;
  216. // bottom box
  217. unsigned box_top = baseline - box_height, left = side_margin, right = side_margin + box_width, bottom = baseline;
  218. render_hline(out, width, thickness, box_top + thickness, left, right);
  219. render_hline(out, width, half_thickness, bottom, left, right);
  220. render_vline(out, width, half_thickness, left, box_top, bottom);
  221. render_vline(out, width, half_thickness, side_margin + box_width, baseline - box_height, baseline);
  222. // top box
  223. unsigned box_x_shift = 2 * thickness, box_y_shift = 2 * thickness;
  224. box_x_shift = MIN(width - right, box_x_shift);
  225. box_y_shift = MIN(box_top, box_y_shift);
  226. unsigned left2 = left + box_x_shift, right2 = right + box_x_shift, top2 = box_top - box_y_shift, bottom2 = bottom - box_y_shift;
  227. render_hline(out, width, thickness, top2 + thickness, left2, right2);
  228. render_vline(out, width, half_thickness, right2, top2, bottom2);
  229. render_hline(out, width, half_thickness, bottom2, right, right2);
  230. render_vline(out, width, half_thickness, left2, top2, box_top);
  231. }
  232. static void
  233. render_line(uint8_t *buf, unsigned width, unsigned height, unsigned thickness, int x1, int y1, int x2, int y2) {
  234. float m = (y2 - y1) / (float)(x2 - x1);
  235. float c = y1 - m * x1;
  236. unsigned delta = thickness / 2, extra = thickness % 2;
  237. for (int x = MAX(0, MIN(x1, x2)); x < MIN((int)width, MAX(x1, x2) + 1); x++) {
  238. float ly = m * x + c;
  239. for (int y = MAX(0, (int)(ly - delta)); y < MIN((int)height, (int)(ly + delta + extra + 1)); y++) buf[x + y * width] = 255;
  240. }
  241. for (int y = MAX(0, MIN(y1, y2)); y < MIN((int)height, MAX(y1, y2) + 1); y++) {
  242. float lx = (y - c) / m;
  243. for (int x = MAX(0, (int)(lx - delta)); x < MIN((int)width, (int)(lx + delta + extra + 1)); x++) buf[x + y * width] = 255;
  244. }
  245. }
  246. static void
  247. render_close(uint8_t *out, unsigned width, unsigned height) {
  248. memset(out, 0, width * height);
  249. unsigned thickness = height / 12;
  250. unsigned baseline = height - thickness * 2;
  251. unsigned side_margin = scale(thickness, 3.3f);
  252. int top = baseline - (width - 2 * side_margin);
  253. if (top <= 0) return;
  254. unsigned line_thickness = scale(thickness, 1.5f);
  255. render_line(out, width, height, line_thickness, side_margin, top, width - side_margin, baseline);
  256. render_line(out, width, height, line_thickness, side_margin, baseline, width - side_margin, top);
  257. }
  258. static uint32_t
  259. average_intensity_in_src(uint8_t *src, unsigned src_width, unsigned src_x, unsigned src_y, unsigned factor) {
  260. uint32_t ans = 0;
  261. for (unsigned y = src_y; y < src_y + factor; y++) {
  262. uint8_t *s = src + src_width * y;
  263. for (unsigned x = src_x; x < src_x + factor; x++) ans += s[x];
  264. }
  265. return ans / (factor * factor);
  266. }
  267. static void
  268. downsample(uint8_t *dest, uint8_t *src, unsigned dest_width, unsigned dest_height, unsigned factor) {
  269. unsigned src_width = factor * dest_width;
  270. for (unsigned y = 0; y < dest_height; y++) {
  271. uint8_t *d = dest + dest_width * y;
  272. for (unsigned x = 0; x < dest_width; x++) {
  273. d[x] = MIN(255u, (uint32_t)d[x] + average_intensity_in_src(src, src_width, x * factor, y * factor, factor));
  274. }
  275. }
  276. }
  277. static void
  278. render_button(void(*which)(uint8_t *, unsigned, unsigned), bool antialias, uint32_t *dest, uint8_t *src, unsigned height, unsigned dest_stride, unsigned src_width, unsigned dest_left, uint32_t bg, uint32_t fg) {
  279. if (antialias) {
  280. static const unsigned factor = 4;
  281. uint8_t *big_src = malloc(factor * factor * height * src_width);
  282. if (big_src) {
  283. which(big_src, src_width * factor, height * factor);
  284. memset(src, 0, src_width * height);
  285. downsample(src, big_src, src_width, height, factor);
  286. free(big_src);
  287. } else which(src, src_width, height);
  288. } else which(src, src_width, height);
  289. patch_titlebar_with_alpha_mask(dest, src, height, dest_stride, src_width, dest_left, bg, fg);
  290. }
  291. static void
  292. render_title_bar(_GLFWwindow *window, bool to_front_buffer) {
  293. const bool is_focused = window->id == _glfw.focusedWindowId;
  294. const bool is_maximized = window->wl.current.toplevel_states & TOPLEVEL_STATE_MAXIMIZED;
  295. const uint32_t light_fg = is_focused ? 0xff444444 : 0xff888888, light_bg = is_focused ? 0xffdddad6 : 0xffeeeeee;
  296. const uint32_t dark_fg = is_focused ? 0xffffffff : 0xffcccccc, dark_bg = is_focused ? 0xff303030 : 0xff242424;
  297. static const uint32_t hover_dark_bg = 0xff444444, hover_light_bg = 0xffbbbbbb;
  298. uint32_t bg_color = light_bg, fg_color = light_fg, hover_bg = hover_light_bg;
  299. GLFWColorScheme appearance = glfwGetCurrentSystemColorTheme();
  300. bool is_dark = false;
  301. if (decs.use_custom_titlebar_color || appearance == GLFW_COLOR_SCHEME_NO_PREFERENCE) {
  302. bg_color = 0xff000000 | (decs.titlebar_color & 0xffffff);
  303. double red = ((bg_color >> 16) & 0xFF) / 255.0;
  304. double green = ((bg_color >> 8) & 0xFF) / 255.0;
  305. double blue = (bg_color & 0xFF) / 255.0;
  306. double luma = 0.2126 * red + 0.7152 * green + 0.0722 * blue;
  307. if (luma < 0.5) { fg_color = dark_fg; hover_bg = hover_dark_bg; is_dark = true; }
  308. if (!decs.use_custom_titlebar_color) bg_color = luma < 0.5 ? dark_bg : light_bg;
  309. } else if (appearance == GLFW_COLOR_SCHEME_DARK) { bg_color = dark_bg; fg_color = dark_fg; hover_bg = hover_dark_bg; is_dark = true; }
  310. uint8_t *output = to_front_buffer ? decs.titlebar.buffer.data.front : decs.titlebar.buffer.data.back;
  311. // render text part
  312. int button_size = decs.titlebar.buffer.height;
  313. int num_buttons = 1;
  314. if (window->wl.wm_capabilities.maximize) num_buttons++;
  315. if (window->wl.wm_capabilities.minimize) num_buttons++;
  316. if (window->wl.title && window->wl.title[0] && _glfw.callbacks.draw_text) {
  317. if (_glfw.callbacks.draw_text((GLFWwindow*)window, window->wl.title, fg_color, bg_color, output, decs.titlebar.buffer.width, decs.titlebar.buffer.height, 0, 0, num_buttons * button_size, false)) goto render_buttons;
  318. }
  319. // rendering of text failed, blank the buffer
  320. for (uint32_t *px = (uint32_t*)output, *end = (uint32_t*)(output + decs.titlebar.buffer.size_in_bytes); px < end; px++) *px = bg_color;
  321. render_buttons:
  322. decs.maximize.width = 0; decs.minimize.width = 0; decs.close.width = 0;
  323. if (!button_size) return;
  324. uint8_t *alpha_mask = malloc(button_size * button_size);
  325. int left = decs.titlebar.buffer.width - num_buttons * button_size;
  326. if (!alpha_mask || left <= 0) return;
  327. #define drawb(which, antialias, func, hover_bg) { \
  328. render_button(func, antialias, (uint32_t*)output, alpha_mask, button_size, decs.titlebar.buffer.width, button_size, left, decs.which.hovered ? hover_bg : bg_color, fg_color); decs.which.left = left; decs.which.width = button_size; left += button_size; }
  329. if (window->wl.wm_capabilities.minimize) drawb(minimize, false, render_minimize, hover_bg);
  330. if (window->wl.wm_capabilities.maximize) {
  331. if (is_maximized) { drawb(maximize, false, render_restore, hover_bg); } else { drawb(maximize, false, render_maximize, hover_bg); }
  332. }
  333. drawb(close, true, render_close, is_dark ? 0xff880000: 0xffc80000);
  334. free(alpha_mask);
  335. #undef drawb
  336. }
  337. static void
  338. update_title_bar(_GLFWwindow *window) {
  339. render_title_bar(window, false);
  340. swap_buffers(&decs.titlebar.buffer);
  341. }
  342. static void
  343. render_horizontal_shadow(_GLFWwindow *window, ssize_t scaled_shadow_size, ssize_t src_y_offset, ssize_t y, _GLFWWaylandBufferPair *buf) {
  344. // left region
  345. ssize_t src_y = src_y_offset + y;
  346. const ssize_t src_leftover_corner = st.corner_size - scaled_shadow_size;
  347. uint32_t *src = st.data + st.stride * src_y + scaled_shadow_size;
  348. uint32_t *d_start = (uint32_t*)(buf->data.front + y * buf->stride);
  349. uint32_t *d_end = (uint32_t*)(buf->data.front + (y+1) * buf->stride);
  350. uint32_t *left_region_end = d_start + MIN(d_end - d_start, src_leftover_corner);
  351. memcpy(d_start, src, sizeof(uint32_t) * (left_region_end - d_start));
  352. // right region
  353. uint32_t *right_region_start = MAX(d_start, d_end - src_leftover_corner);
  354. src = st.data + st.stride * (src_y+1) - st.corner_size;
  355. memcpy(right_region_start, src, sizeof(uint32_t) * MIN(src_leftover_corner, d_end - right_region_start));
  356. src = st.data + st.stride * src_y + st.corner_size;
  357. // middle region
  358. for (uint32_t *d = left_region_end; d < right_region_start; d += scaled_shadow_size)
  359. memcpy(d, src, sizeof(uint32_t) * MIN(right_region_start - d, scaled_shadow_size));
  360. }
  361. static void
  362. copy_vertical_region(
  363. _GLFWwindow *window, ssize_t src_y_start, ssize_t src_y_limit,
  364. ssize_t y_start, ssize_t y_limit, ssize_t src_x_offset, _GLFWWaylandBufferPair *buf
  365. ) {
  366. for (ssize_t dy = y_start, sy = src_y_start; dy < y_limit && sy < src_y_limit; dy++, sy++)
  367. memcpy(buf->data.front + dy * buf->stride, st.data + sy * st.stride + src_x_offset, sizeof(uint32_t) * buf->width);
  368. }
  369. static void
  370. render_shadows(_GLFWwindow *window) {
  371. if (!window_needs_shadows(window)) return;
  372. const ssize_t scaled_shadow_size = create_shadow_tile(window);
  373. if (!st.data || !scaled_shadow_size) return; // out of memory
  374. // upper and lower shadows
  375. for (ssize_t y = 0; y < scaled_shadow_size; y++) {
  376. _GLFWWaylandBufferPair *buf = &decs.shadow_upper_left.buffer;
  377. uint32_t *src = st.data + st.stride * y;
  378. uint32_t *d = (uint32_t*)(buf->data.front + y * buf->stride);
  379. memcpy(d, src, sizeof(uint32_t) * scaled_shadow_size);
  380. buf = &decs.shadow_upper_right.buffer;
  381. src += st.stride - scaled_shadow_size;
  382. d = (uint32_t*)(buf->data.front + y * buf->stride);
  383. memcpy(d, src, sizeof(uint32_t) * scaled_shadow_size);
  384. const size_t tile_bottom_start = st.stride - scaled_shadow_size;
  385. buf = &decs.shadow_lower_left.buffer;
  386. src = st.data + (tile_bottom_start + y) * st.stride;
  387. d = (uint32_t*)(buf->data.front + y * buf->stride);
  388. memcpy(d, src, sizeof(uint32_t) * scaled_shadow_size);
  389. buf = &decs.shadow_lower_right.buffer;
  390. src += st.stride - scaled_shadow_size;
  391. d = (uint32_t*)(buf->data.front + y * buf->stride);
  392. memcpy(d, src, sizeof(uint32_t) * scaled_shadow_size);
  393. render_horizontal_shadow(window, scaled_shadow_size, 0, y, &decs.shadow_top.buffer);
  394. render_horizontal_shadow(window, scaled_shadow_size, st.stride - scaled_shadow_size, y, &decs.shadow_bottom.buffer);
  395. }
  396. // side shadows
  397. // top region
  398. const ssize_t src_leftover_corner = st.corner_size - scaled_shadow_size;
  399. ssize_t y_start = 0, y_end = decs.shadow_left.buffer.height, top_end = MIN(y_end, src_leftover_corner);
  400. ssize_t right_src_start = st.stride - scaled_shadow_size;
  401. #define c(src_y_start, src_y_limit, dest_y_start, dest_y_limit) { \
  402. copy_vertical_region(window, src_y_start, src_y_limit, dest_y_start, dest_y_limit, 0, &decs.shadow_left.buffer); \
  403. copy_vertical_region(window, src_y_start, src_y_limit, dest_y_start, dest_y_limit, right_src_start, &decs.shadow_right.buffer); \
  404. }
  405. c(scaled_shadow_size, st.corner_size, y_start, top_end);
  406. // bottom region
  407. ssize_t bottom_start = MAX(0, y_end - src_leftover_corner);
  408. c(st.stride - st.corner_size, st.stride - scaled_shadow_size, bottom_start, y_end);
  409. // middle region
  410. for (ssize_t dest_y = top_end; dest_y < bottom_start; dest_y += scaled_shadow_size)
  411. c(st.corner_size, st.corner_size + scaled_shadow_size, dest_y, MIN(dest_y + scaled_shadow_size, bottom_start));
  412. #undef c
  413. #define copy(which) for (uint32_t *src = (uint32_t*)decs.which.buffer.data.front, *dest = (uint32_t*)decs.which.buffer.data.back; src < (uint32_t*)(decs.which.buffer.data.front + decs.which.buffer.size_in_bytes); src++, dest++) *dest = (A(*src) / 2 ) << 24;
  414. all_shadow_surfaces(copy);
  415. #undef copy
  416. }
  417. #undef st
  418. static bool
  419. create_shm_buffers(_GLFWwindow* window) {
  420. const double scale = _glfwWaylandWindowScale(window);
  421. decs.mapping.size = 0;
  422. #define bp(which, width, height) decs.mapping.size += init_buffer_pair(&decs.which.buffer, width, height, scale);
  423. bp(titlebar, window->wl.width, decs.metrics.visible_titlebar_height);
  424. bp(shadow_top, window->wl.width, decs.metrics.width);
  425. bp(shadow_bottom, window->wl.width, decs.metrics.width);
  426. bp(shadow_left, decs.metrics.width, window->wl.height + decs.metrics.visible_titlebar_height);
  427. bp(shadow_right, decs.metrics.width, window->wl.height + decs.metrics.visible_titlebar_height);
  428. bp(shadow_upper_left, decs.metrics.width, decs.metrics.width);
  429. bp(shadow_upper_right, decs.metrics.width, decs.metrics.width);
  430. bp(shadow_lower_left, decs.metrics.width, decs.metrics.width);
  431. bp(shadow_lower_right, decs.metrics.width, decs.metrics.width);
  432. #undef bp
  433. int fd = createAnonymousFile(decs.mapping.size);
  434. if (fd < 0) {
  435. _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Creating a buffer file for %zu B failed: %s",
  436. decs.mapping.size, strerror(errno));
  437. return false;
  438. }
  439. decs.mapping.data = mmap(NULL, decs.mapping.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  440. if (decs.mapping.data == MAP_FAILED) {
  441. _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: mmap failed: %s", strerror(errno));
  442. close(fd);
  443. return false;
  444. }
  445. struct wl_shm_pool* pool = wl_shm_create_pool(_glfw.wl.shm, fd, decs.mapping.size);
  446. close(fd);
  447. size_t offset = 0;
  448. #define Q(which) alloc_buffer_pair(window->id, &decs.which.buffer, pool, decs.mapping.data, &offset)
  449. all_surfaces(Q);
  450. #undef Q
  451. wl_shm_pool_destroy(pool);
  452. render_title_bar(window, true);
  453. render_shadows(window);
  454. debug("Created decoration buffers at scale: %f\n", scale);
  455. return true;
  456. }
  457. static void
  458. free_csd_surface(_GLFWWaylandCSDSurface *s) {
  459. if (s->subsurface) wl_subsurface_destroy(s->subsurface);
  460. s->subsurface = NULL;
  461. if (s->wp_viewport) wp_viewport_destroy(s->wp_viewport);
  462. s->wp_viewport = NULL;
  463. if (s->surface) wl_surface_destroy(s->surface);
  464. s->surface = NULL;
  465. }
  466. static void
  467. free_csd_surfaces(_GLFWwindow *window) {
  468. #define Q(which) free_csd_surface(&decs.which)
  469. all_surfaces(Q);
  470. #undef Q
  471. }
  472. static void
  473. free_csd_buffers(_GLFWwindow *window) {
  474. #define Q(which) { \
  475. if (decs.which.buffer.a_needs_to_be_destroyed && decs.which.buffer.a) wl_buffer_destroy(decs.which.buffer.a); \
  476. if (decs.which.buffer.b_needs_to_be_destroyed && decs.which.buffer.b) wl_buffer_destroy(decs.which.buffer.b); \
  477. memset(&decs.which.buffer, 0, sizeof(_GLFWWaylandBufferPair)); \
  478. }
  479. all_surfaces(Q);
  480. #undef Q
  481. if (decs.mapping.data) munmap(decs.mapping.data, decs.mapping.size);
  482. decs.mapping.data = NULL; decs.mapping.size = 0;
  483. }
  484. static void
  485. position_csd_surface(_GLFWWaylandCSDSurface *s, int x, int y) {
  486. if (s->surface) {
  487. wl_surface_set_buffer_scale(s->surface, 1);
  488. s->x = x; s->y = y;
  489. wl_subsurface_set_position(s->subsurface, s->x, s->y);
  490. }
  491. }
  492. static void
  493. create_csd_surfaces(_GLFWwindow *window, _GLFWWaylandCSDSurface *s) {
  494. if (s->surface) wl_surface_destroy(s->surface);
  495. s->surface = wl_compositor_create_surface(_glfw.wl.compositor);
  496. wl_surface_set_user_data(s->surface, window);
  497. if (s->subsurface) wl_subsurface_destroy(s->subsurface);
  498. s->subsurface = wl_subcompositor_get_subsurface(_glfw.wl.subcompositor, s->surface, window->wl.surface);
  499. if (_glfw.wl.wp_viewporter) {
  500. if (s->wp_viewport) wp_viewport_destroy(s->wp_viewport);
  501. s->wp_viewport = wp_viewporter_get_viewport(_glfw.wl.wp_viewporter, s->surface);
  502. }
  503. }
  504. #define damage_csd(which, xbuffer) if (decs.which.surface) { \
  505. wl_surface_attach(decs.which.surface, (xbuffer), 0, 0); \
  506. if (decs.which.wp_viewport) wp_viewport_set_destination(decs.which.wp_viewport, decs.which.buffer.viewport_width, decs.which.buffer.viewport_height); \
  507. wl_surface_damage(decs.which.surface, 0, 0, decs.which.buffer.width, decs.which.buffer.height); \
  508. wl_surface_commit(decs.which.surface); \
  509. if (decs.which.buffer.a == (xbuffer)) { decs.which.buffer.a_needs_to_be_destroyed = false; } else { decs.which.buffer.b_needs_to_be_destroyed = false; }}
  510. static bool
  511. window_is_csd_capable(_GLFWwindow *window) {
  512. return window->decorated && !decs.serverSide && window->wl.xdg.toplevel;
  513. }
  514. static bool
  515. ensure_csd_resources(_GLFWwindow *window) {
  516. if (!window_is_csd_capable(window)) return false;
  517. const bool is_focused = window->id == _glfw.focusedWindowId;
  518. const bool focus_changed = is_focused != decs.for_window_state.focused;
  519. const bool needs_shadow = window_needs_shadows(window);
  520. const bool size_changed = (
  521. decs.for_window_state.width != window->wl.width ||
  522. decs.for_window_state.height != window->wl.height ||
  523. decs.for_window_state.fscale != _glfwWaylandWindowScale(window) ||
  524. !decs.mapping.data
  525. );
  526. const bool state_changed = decs.for_window_state.toplevel_states != window->wl.current.toplevel_states;
  527. const bool needs_update = focus_changed || size_changed || !decs.titlebar.surface || decs.buffer_destroyed || state_changed;
  528. const bool shadow_changed = needs_shadow != decs.for_window_state.needs_shadow;
  529. debug("CSD: old.size: %dx%d new.size: %dx%d needs_update: %d shadow_changed: %d size_changed: %d state_changed: %d buffer_destroyed: %d\n",
  530. decs.for_window_state.width, decs.for_window_state.height, window->wl.width, window->wl.height, needs_update, shadow_changed,
  531. size_changed, state_changed, decs.buffer_destroyed);
  532. if (!needs_update) return false;
  533. if (size_changed || decs.buffer_destroyed || shadow_changed) {
  534. free_csd_buffers(window);
  535. if (!create_shm_buffers(window)) return false;
  536. decs.buffer_destroyed = false;
  537. }
  538. #define setup_surface(which, x, y) \
  539. if (!decs.which.surface) create_csd_surfaces(window, &decs.which); \
  540. position_csd_surface(&decs.which, x, y);
  541. setup_surface(titlebar, 0, -decs.metrics.visible_titlebar_height);
  542. if (needs_shadow) {
  543. setup_surface(shadow_top, decs.titlebar.x, decs.titlebar.y - decs.metrics.width);
  544. setup_surface(shadow_bottom, decs.titlebar.x, window->wl.height);
  545. setup_surface(shadow_left, -decs.metrics.width, decs.titlebar.y);
  546. setup_surface(shadow_right, window->wl.width, decs.shadow_left.y);
  547. setup_surface(shadow_upper_left, decs.shadow_left.x, decs.shadow_top.y);
  548. setup_surface(shadow_upper_right, decs.shadow_right.x, decs.shadow_top.y);
  549. setup_surface(shadow_lower_left, decs.shadow_left.x, decs.shadow_bottom.y);
  550. setup_surface(shadow_lower_right, decs.shadow_right.x, decs.shadow_bottom.y);
  551. } else {
  552. #define d(which) free_csd_surface(&decs.which);
  553. all_shadow_surfaces(d)
  554. #undef d
  555. }
  556. if (focus_changed || state_changed) update_title_bar(window);
  557. damage_csd(titlebar, decs.titlebar.buffer.front);
  558. #define d(which) damage_csd(which, is_focused ? decs.which.buffer.front : decs.which.buffer.back);
  559. d(shadow_left); d(shadow_right); d(shadow_top); d(shadow_bottom);
  560. d(shadow_upper_left); d(shadow_upper_right); d(shadow_lower_left); d(shadow_lower_right);
  561. #undef d
  562. decs.for_window_state.width = window->wl.width;
  563. decs.for_window_state.height = window->wl.height;
  564. decs.for_window_state.fscale = _glfwWaylandWindowScale(window);
  565. decs.for_window_state.focused = is_focused;
  566. decs.for_window_state.toplevel_states = window->wl.current.toplevel_states;
  567. decs.for_window_state.needs_shadow = needs_shadow;
  568. return true;
  569. }
  570. void
  571. csd_set_visible(_GLFWwindow *window, bool visible) {
  572. // When setting to visible will only take effect if window currently has
  573. // CSD and will also ensure CSD is of correct size and type for current window.
  574. // When hiding CSD simply destroys all CSD surfaces.
  575. if (visible) ensure_csd_resources(window); else free_csd_surfaces(window);
  576. }
  577. void
  578. csd_free_all_resources(_GLFWwindow *window) {
  579. free_csd_surfaces(window);
  580. free_csd_buffers(window);
  581. if (decs.shadow_tile.data) free(decs.shadow_tile.data);
  582. decs.shadow_tile.data = NULL;
  583. }
  584. bool
  585. csd_change_title(_GLFWwindow *window) {
  586. if (!window_is_csd_capable(window)) return false;
  587. if (ensure_csd_resources(window)) return true; // CSD were re-rendered for other reasons
  588. if (decs.titlebar.surface) {
  589. update_title_bar(window);
  590. damage_csd(titlebar, decs.titlebar.buffer.front);
  591. return true;
  592. }
  593. return false;
  594. }
  595. void
  596. csd_set_window_geometry(_GLFWwindow *window, int32_t *width, int32_t *height) {
  597. bool has_csd = window_is_csd_capable(window) && decs.titlebar.surface && !(window->wl.current.toplevel_states & TOPLEVEL_STATE_FULLSCREEN);
  598. bool size_specified_by_compositor = *width > 0 && *height > 0;
  599. if (!size_specified_by_compositor) {
  600. *width = window->wl.user_requested_content_size.width;
  601. *height = window->wl.user_requested_content_size.height;
  602. if (window->wl.xdg.top_level_bounds.width > 0) *width = MIN(*width, window->wl.xdg.top_level_bounds.width);
  603. if (window->wl.xdg.top_level_bounds.height > 0) *height = MIN(*height, window->wl.xdg.top_level_bounds.height);
  604. if (has_csd) *height += decs.metrics.visible_titlebar_height;
  605. }
  606. decs.geometry.x = 0; decs.geometry.y = 0;
  607. decs.geometry.width = *width; decs.geometry.height = *height;
  608. if (has_csd) {
  609. decs.geometry.y = -decs.metrics.visible_titlebar_height;
  610. *height -= decs.metrics.visible_titlebar_height;
  611. }
  612. }
  613. bool
  614. csd_set_titlebar_color(_GLFWwindow *window, uint32_t color, bool use_system_color) {
  615. bool use_custom_color = !use_system_color;
  616. decs.use_custom_titlebar_color = use_custom_color;
  617. decs.titlebar_color = color;
  618. return csd_change_title(window);
  619. }
  620. #define x window->wl.allCursorPosX
  621. #define y window->wl.allCursorPosY
  622. static void
  623. set_cursor(GLFWCursorShape shape, _GLFWwindow* window)
  624. {
  625. if (_glfw.wl.wp_cursor_shape_device_v1) {
  626. wayland_cursor_shape s = glfw_cursor_shape_to_wayland_cursor_shape(shape);
  627. if (s.which > -1) {
  628. debug("Changing cursor shape to: %s with serial: %u\n", s.name, _glfw.wl.pointer_enter_serial);
  629. wp_cursor_shape_device_v1_set_shape(_glfw.wl.wp_cursor_shape_device_v1, _glfw.wl.pointer_enter_serial, (uint32_t)s.which);
  630. return;
  631. }
  632. }
  633. struct wl_buffer* buffer;
  634. struct wl_cursor* cursor;
  635. struct wl_cursor_image* image;
  636. struct wl_surface* surface = _glfw.wl.cursorSurface;
  637. const int scale = _glfwWaylandIntegerWindowScale(window);
  638. struct wl_cursor_theme *theme = glfw_wlc_theme_for_scale(scale);
  639. if (!theme) return;
  640. cursor = _glfwLoadCursor(shape, theme);
  641. if (!cursor) return;
  642. image = cursor->images[0];
  643. if (!image) return;
  644. if (image->width % scale || image->height % scale) {
  645. static uint32_t warned_width = 0, warned_height = 0;
  646. if (warned_width != image->width || warned_height != image->height) {
  647. _glfwInputError(GLFW_PLATFORM_ERROR, "WARNING: Cursor image size: %dx%d is not a multiple of window scale: %d. This will"
  648. " cause some compositors such as GNOME to crash. See https://github.com/kovidgoyal/kitty/issues/4878", image->width, image->height, scale);
  649. warned_width = image->width; warned_height = image->height;
  650. }
  651. }
  652. buffer = wl_cursor_image_get_buffer(image);
  653. if (!buffer) return;
  654. debug("Calling wl_pointer_set_cursor in set_cursor with surface: %p\n", (void*)surface);
  655. wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial,
  656. surface,
  657. image->hotspot_x / scale,
  658. image->hotspot_y / scale);
  659. wl_surface_set_buffer_scale(surface, scale);
  660. wl_surface_attach(surface, buffer, 0, 0);
  661. wl_surface_damage(surface, 0, 0,
  662. image->width, image->height);
  663. wl_surface_commit(surface);
  664. _glfw.wl.cursorPreviousShape = shape;
  665. }
  666. static bool
  667. update_hovered_button(_GLFWwindow *window) {
  668. bool has_hovered_button = false;
  669. int scaled_x = (int)round(decs.for_window_state.fscale * x);
  670. #define c(which) \
  671. if (decs.which.left <= scaled_x && scaled_x < decs.which.left + decs.which.width) { \
  672. has_hovered_button = true; \
  673. if (!decs.which.hovered) { decs.titlebar_needs_update = true; decs.which.hovered = true; } \
  674. } else if (decs.which.hovered) { decs.titlebar_needs_update = true; decs.which.hovered = false; }
  675. c(minimize); c(maximize); c(close);
  676. #undef c
  677. update_title_bar(window);
  678. return has_hovered_button;
  679. }
  680. static bool
  681. has_hovered_button(_GLFWwindow *window) {
  682. return decs.minimize.hovered || decs.maximize.hovered || decs.close.hovered;
  683. }
  684. static void
  685. handle_pointer_leave(_GLFWwindow *window, struct wl_surface *surface) {
  686. #define c(which) if (decs.which.hovered) { decs.titlebar_needs_update = true; decs.which.hovered = false; }
  687. if (surface == decs.titlebar.surface) {
  688. c(minimize); c(maximize); c(close);
  689. }
  690. #undef c
  691. decs.focus = CENTRAL_WINDOW;
  692. }
  693. static void
  694. handle_pointer_move(_GLFWwindow *window) {
  695. GLFWCursorShape cursorShape = GLFW_DEFAULT_CURSOR;
  696. switch (decs.focus)
  697. {
  698. case CENTRAL_WINDOW: break;
  699. case CSD_titlebar: if (update_hovered_button(window)) cursorShape = GLFW_POINTER_CURSOR; break;
  700. case CSD_shadow_top: cursorShape = GLFW_N_RESIZE_CURSOR; break;
  701. case CSD_shadow_bottom: cursorShape = GLFW_S_RESIZE_CURSOR; break;
  702. case CSD_shadow_left: cursorShape = GLFW_W_RESIZE_CURSOR; break;
  703. case CSD_shadow_right: cursorShape = GLFW_E_RESIZE_CURSOR; break;
  704. case CSD_shadow_upper_left: cursorShape = GLFW_NW_RESIZE_CURSOR; break;
  705. case CSD_shadow_upper_right: cursorShape = GLFW_NE_RESIZE_CURSOR; break;
  706. case CSD_shadow_lower_left: cursorShape = GLFW_SW_RESIZE_CURSOR; break;
  707. case CSD_shadow_lower_right: cursorShape = GLFW_SE_RESIZE_CURSOR; break;
  708. }
  709. if (_glfw.wl.cursorPreviousShape != cursorShape) set_cursor(cursorShape, window);
  710. }
  711. static void
  712. handle_pointer_enter(_GLFWwindow *window, struct wl_surface *surface) {
  713. #define Q(which) if (decs.which.surface == surface) { \
  714. decs.focus = CSD_##which; handle_pointer_move(window); return; } // enter is also a move
  715. all_surfaces(Q)
  716. #undef Q
  717. decs.focus = CENTRAL_WINDOW;
  718. }
  719. static void
  720. handle_pointer_button(_GLFWwindow *window, uint32_t button, uint32_t state) {
  721. uint32_t edges = XDG_TOPLEVEL_RESIZE_EDGE_NONE;
  722. if (button == BTN_LEFT) {
  723. switch (decs.focus) {
  724. case CENTRAL_WINDOW: break;
  725. case CSD_titlebar:
  726. if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
  727. monotonic_t last_click_at = decs.last_click_on_top_decoration_at;
  728. decs.last_click_on_top_decoration_at = monotonic();
  729. if (decs.last_click_on_top_decoration_at - last_click_at <= _glfwPlatformGetDoubleClickInterval(window)) {
  730. decs.last_click_on_top_decoration_at = 0;
  731. if (window->wl.current.toplevel_states & TOPLEVEL_STATE_MAXIMIZED) _glfwPlatformRestoreWindow(window);
  732. else _glfwPlatformMaximizeWindow(window);
  733. return;
  734. }
  735. } else {
  736. if (decs.minimize.hovered) _glfwPlatformIconifyWindow(window);
  737. else if (decs.maximize.hovered) {
  738. if (window->wl.current.toplevel_states & TOPLEVEL_STATE_MAXIMIZED) _glfwPlatformRestoreWindow(window);
  739. else _glfwPlatformMaximizeWindow(window);
  740. // hack otherwise on GNOME maximize button remains hovered sometimes
  741. decs.maximize.hovered = false; decs.titlebar_needs_update = true;
  742. } else if (decs.close.hovered) _glfwInputWindowCloseRequest(window);
  743. }
  744. if (!has_hovered_button(window)) {
  745. if (window->wl.xdg.toplevel) xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, _glfw.wl.pointer_serial);
  746. }
  747. break;
  748. case CSD_shadow_left: edges = XDG_TOPLEVEL_RESIZE_EDGE_LEFT; break;
  749. case CSD_shadow_upper_left: edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; break;
  750. case CSD_shadow_right: edges = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT; break;
  751. case CSD_shadow_upper_right: edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; break;
  752. case CSD_shadow_top: edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP; break;
  753. case CSD_shadow_lower_left: edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; break;
  754. case CSD_shadow_bottom: edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM; break;
  755. case CSD_shadow_lower_right: edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; break;
  756. }
  757. if (edges != XDG_TOPLEVEL_RESIZE_EDGE_NONE) xdg_toplevel_resize(window->wl.xdg.toplevel, _glfw.wl.seat, _glfw.wl.pointer_serial, edges);
  758. }
  759. else if (button == BTN_RIGHT) {
  760. if (decs.focus == CSD_titlebar && window->wl.xdg.toplevel)
  761. {
  762. if (window->wl.wm_capabilities.window_menu) xdg_toplevel_show_window_menu(
  763. window->wl.xdg.toplevel, _glfw.wl.seat, _glfw.wl.pointer_serial, (int32_t)x, (int32_t)y - decs.metrics.top);
  764. else
  765. _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland compositor does not support showing wndow menu");
  766. return;
  767. }
  768. }
  769. }
  770. void
  771. csd_handle_pointer_event(_GLFWwindow *window, int button, int state, struct wl_surface *surface) {
  772. if (!window_is_csd_capable(window)) return;
  773. decs.titlebar_needs_update = false;
  774. switch (button) {
  775. case -1: handle_pointer_move(window); break;
  776. case -2: handle_pointer_enter(window, surface); break;
  777. case -3: handle_pointer_leave(window, surface); break;
  778. default: handle_pointer_button(window, button, state); break;
  779. }
  780. if (decs.titlebar_needs_update) {
  781. csd_change_title(window);
  782. if (!window->wl.waiting_for_swap_to_commit) wl_surface_commit(window->wl.surface);
  783. }
  784. }
  785. #undef x
  786. #undef y