rofi 1.7.7
xcb.c
Go to the documentation of this file.
1/*
2 * rofi
3 *
4 * MIT/X11 License
5 * Copyright © 2012 Sean Pringle <sean.pringle@gmail.com>
6 * Copyright © 2013-2023 Qball Cow <qball@gmpclient.org>
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining
9 * a copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sublicense, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be
17 * included in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 *
27 */
28
30#define G_LOG_DOMAIN "X11Helper"
31
32#include "config.h"
33#ifdef XCB_IMDKIT
34#include <xcb-imdkit/encoding.h>
35#include <xcb/xcb_keysyms.h>
36#endif
37#include <cairo-xcb.h>
38#include <cairo.h>
39#include <glib.h>
40#include <math.h>
41#include <stdint.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <unistd.h>
46#include <xcb/randr.h>
47#include <xcb/xcb.h>
48#include <xcb/xcb_aux.h>
49#include <xcb/xcb_cursor.h>
50#include <xcb/xcb_ewmh.h>
51#include <xcb/xinerama.h>
52#include <xcb/xkb.h>
53#include <xcb/xproto.h>
54#include <xkbcommon/xkbcommon-x11.h>
55#include <xkbcommon/xkbcommon.h>
57#define SN_API_NOT_YET_FROZEN
60#define sn_launcher_context_set_application_id sn_launcher_set_application_id
61#include "display.h"
62#include "helper.h"
63#include "rofi-types.h"
64#include "settings.h"
65#include "timings.h"
66#include "xcb-internal.h"
67#include "xcb.h"
68#include <libsn/sn.h>
69#include <stdbool.h>
70
71#include "mode.h"
72#include "modes/window.h"
73
74#include <rofi.h>
75
77#define RANDR_PREF_MAJOR_VERSION 1
79#define RANDR_PREF_MINOR_VERSION 5
80
82#define INTERSECT(x, y, x1, y1, w1, h1) \
83 ((((x) >= (x1)) && ((x) < (x1 + w1))) && (((y) >= (y1)) && ((y) < (y1 + h1))))
84
86
90struct _xcb_stuff xcb_int = {.connection = NULL,
91 .screen = NULL,
92#ifdef XCB_IMDKIT
93 .im = NULL,
94 .syms = NULL,
95#endif
96 .screen_nbr = -1,
97 .sndisplay = NULL,
98 .sncontext = NULL,
99 .monitors = NULL,
100 .clipboard = NULL};
102
106xcb_depth_t *depth = NULL;
107xcb_visualtype_t *visual = NULL;
108xcb_colormap_t map = XCB_COLORMAP_NONE;
112static xcb_visualtype_t *root_visual = NULL;
115
119xcb_cursor_t cursors[NUM_CURSORS] = {XCB_CURSOR_NONE, XCB_CURSOR_NONE,
120 XCB_CURSOR_NONE};
121
123const struct {
125 const char *css_name;
127 const char *traditional_name;
128} cursor_names[] = {
129 {"default", "left_ptr"}, {"pointer", "hand"}, {"text", "xterm"}};
130
131static xcb_visualtype_t *lookup_visual(xcb_screen_t *s, xcb_visualid_t vis) {
132 xcb_depth_iterator_t d;
133 d = xcb_screen_allowed_depths_iterator(s);
134 for (; d.rem; xcb_depth_next(&d)) {
135 xcb_visualtype_iterator_t v = xcb_depth_visuals_iterator(d.data);
136 for (; v.rem; xcb_visualtype_next(&v)) {
137 if (v.data->visual_id == vis) {
138 return v.data;
139 }
140 }
141 }
142 return 0;
143}
144
145/* This blur function was originally created my MacSlow and published on his
146 * website: http://macslow.thepimp.net. I'm not entirely sure he's proud of it,
147 * but it has proved immeasurably useful for me. */
148
149static uint32_t *create_kernel(double radius, double deviation,
150 uint32_t *sum2) {
151 int size = 2 * (int)(radius) + 1;
152 uint32_t *kernel = (uint32_t *)(g_malloc(sizeof(uint32_t) * (size + 1)));
153 double radiusf = fabs(radius) + 1.0;
154 double value = -radius;
155 double sum = 0.0;
156 int i;
157
158 if (deviation == 0.0) {
159 deviation = sqrt(-(radiusf * radiusf) / (2.0 * log(1.0 / 255.0)));
160 }
161
162 kernel[0] = size;
163
164 for (i = 0; i < size; i++) {
165 kernel[1 + i] = INT16_MAX / (2.506628275 * deviation) *
166 exp(-((value * value) / (2.0 * (deviation * deviation))));
167
168 sum += kernel[1 + i];
169 value += 1.0;
170 }
171
172 *sum2 = sum;
173
174 return kernel;
175}
176
177void cairo_image_surface_blur(cairo_surface_t *surface, double radius,
178 double deviation) {
179 uint32_t *horzBlur;
180 uint32_t *kernel = 0;
181 cairo_format_t format;
182 unsigned int channels;
183
184 if (cairo_surface_status(surface)) {
185 return;
186 }
187
188 uint8_t *data = cairo_image_surface_get_data(surface);
189 format = cairo_image_surface_get_format(surface);
190 const int width = cairo_image_surface_get_width(surface);
191 const int height = cairo_image_surface_get_height(surface);
192 const int stride = cairo_image_surface_get_stride(surface);
193
194 if (format == CAIRO_FORMAT_ARGB32) {
195 channels = 4;
196 } else {
197 return;
198 }
199
200 horzBlur = (uint32_t *)(g_malloc(sizeof(uint32_t) * height * stride));
201 TICK();
202 uint32_t sum = 0;
203 kernel = create_kernel(radius, deviation, &sum);
204 TICK_N("BLUR: kernel");
205
206 /* Horizontal pass. */
207 uint32_t *horzBlur_ptr = horzBlur;
208 for (int iY = 0; iY < height; iY++) {
209 const int iYs = iY * stride;
210 for (int iX = 0; iX < width; iX++) {
211 uint32_t red = 0;
212 uint32_t green = 0;
213 uint32_t blue = 0;
214 uint32_t alpha = 0;
215 int offset = (int)(kernel[0]) / -2;
216
217 for (int i = 0; i < (int)(kernel[0]); i++) {
218 int x = iX + offset;
219
220 if (x < 0 || x >= width) {
221 offset++;
222 continue;
223 }
224
225 uint8_t *dataPtr = &data[iYs + x * channels];
226 const uint32_t kernip1 = kernel[i + 1];
227
228 blue += kernip1 * dataPtr[0];
229 green += kernip1 * dataPtr[1];
230 red += kernip1 * dataPtr[2];
231 alpha += kernip1 * dataPtr[3];
232 offset++;
233 }
234
235 *horzBlur_ptr++ = blue / sum;
236 *horzBlur_ptr++ = green / sum;
237 *horzBlur_ptr++ = red / sum;
238 *horzBlur_ptr++ = alpha / sum;
239 }
240 }
241 TICK_N("BLUR: hori");
242
243 /* Vertical pass. */
244 for (int iY = 0; iY < height; iY++) {
245 for (int iX = 0; iX < width; iX++) {
246 uint32_t red = 0;
247 uint32_t green = 0;
248 uint32_t blue = 0;
249 uint32_t alpha = 0;
250 int offset = (int)(kernel[0]) / -2;
251
252 const int iXs = iX * channels;
253 for (int i = 0; i < (int)(kernel[0]); i++) {
254 int y = iY + offset;
255
256 if (y < 0 || y >= height) {
257 offset++;
258 continue;
259 }
260
261 uint32_t *dataPtr = &horzBlur[y * stride + iXs];
262 const uint32_t kernip1 = kernel[i + 1];
263
264 blue += kernip1 * dataPtr[0];
265 green += kernip1 * dataPtr[1];
266 red += kernip1 * dataPtr[2];
267 alpha += kernip1 * dataPtr[3];
268
269 offset++;
270 }
271
272 *data++ = blue / sum;
273 *data++ = green / sum;
274 *data++ = red / sum;
275 *data++ = alpha / sum;
276 }
277 }
278 TICK_N("BLUR: vert");
279
280 free(kernel);
281 free(horzBlur);
282
283 return;
284}
285
286cairo_surface_t *x11_helper_get_screenshot_surface_window(xcb_window_t window,
287 int size) {
288 xcb_get_geometry_cookie_t cookie;
289 xcb_get_geometry_reply_t *reply;
290
291 cookie = xcb_get_geometry(xcb->connection, window);
292 reply = xcb_get_geometry_reply(xcb->connection, cookie, NULL);
293 if (reply == NULL) {
294 return NULL;
295 }
296
297 xcb_get_window_attributes_cookie_t attributesCookie =
298 xcb_get_window_attributes(xcb->connection, window);
299 xcb_get_window_attributes_reply_t *attributes =
300 xcb_get_window_attributes_reply(xcb->connection, attributesCookie, NULL);
301 if (attributes == NULL || (attributes->map_state != XCB_MAP_STATE_VIEWABLE)) {
302 free(reply);
303 if (attributes) {
304 free(attributes);
305 }
306 return NULL;
307 }
308 // Create a cairo surface for the window.
309 xcb_visualtype_t *vt = lookup_visual(xcb->screen, attributes->visual);
310 free(attributes);
311
312 cairo_surface_t *t = cairo_xcb_surface_create(xcb->connection, window, vt,
313 reply->width, reply->height);
314
315 if (cairo_surface_status(t) != CAIRO_STATUS_SUCCESS) {
316 cairo_surface_destroy(t);
317 free(reply);
318 return NULL;
319 }
320
321 // Scale the image, as we don't want to keep large one around.
322 int max = MAX(reply->width, reply->height);
323 double scale = (double)size / max;
324
325 cairo_surface_t *s2 = cairo_surface_create_similar_image(
326 t, CAIRO_FORMAT_ARGB32, reply->width * scale, reply->height * scale);
327 free(reply);
328
329 if (cairo_surface_status(s2) != CAIRO_STATUS_SUCCESS) {
330 cairo_surface_destroy(t);
331 return NULL;
332 }
333 // Paint it in.
334 cairo_t *d = cairo_create(s2);
335 cairo_scale(d, scale, scale);
336 cairo_set_source_surface(d, t, 0, 0);
337 cairo_paint(d);
338 cairo_destroy(d);
339
340 cairo_surface_destroy(t);
341 return s2;
342}
343
347cairo_surface_t *x11_helper_get_screenshot_surface(void) {
348 return cairo_xcb_surface_create(xcb->connection, xcb_stuff_get_root_window(),
349 root_visual, xcb->screen->width_in_pixels,
350 xcb->screen->height_in_pixels);
351}
352
353static xcb_pixmap_t get_root_pixmap(xcb_connection_t *c, xcb_screen_t *screen,
354 xcb_atom_t atom) {
355 xcb_get_property_cookie_t cookie;
356 xcb_get_property_reply_t *reply;
357 xcb_pixmap_t rootpixmap = XCB_NONE;
358
359 cookie = xcb_get_property(c, 0, screen->root, atom, XCB_ATOM_PIXMAP, 0, 1);
360
361 reply = xcb_get_property_reply(c, cookie, NULL);
362
363 if (reply) {
364 if (xcb_get_property_value_length(reply) == sizeof(xcb_pixmap_t)) {
365 memcpy(&rootpixmap, xcb_get_property_value(reply), sizeof(xcb_pixmap_t));
366 }
367 free(reply);
368 }
369
370 return rootpixmap;
371}
372
373cairo_surface_t *x11_helper_get_bg_surface(void) {
374 xcb_pixmap_t pm =
375 get_root_pixmap(xcb->connection, xcb->screen, netatoms[ESETROOT_PMAP_ID]);
376 if (pm == XCB_NONE) {
377 return NULL;
378 }
379 return cairo_xcb_surface_create(xcb->connection, pm, root_visual,
380 xcb->screen->width_in_pixels,
381 xcb->screen->height_in_pixels);
382}
383
384// retrieve a text property from a window
385// technically we could use window_get_prop(), but this is better for character
386// set support
387char *window_get_text_prop(xcb_window_t w, xcb_atom_t atom) {
388 xcb_get_property_cookie_t c = xcb_get_property(
389 xcb->connection, 0, w, atom, XCB_GET_PROPERTY_TYPE_ANY, 0, UINT_MAX);
390 xcb_get_property_reply_t *r =
391 xcb_get_property_reply(xcb->connection, c, NULL);
392 if (r) {
393 if (xcb_get_property_value_length(r) > 0) {
394 char *str = NULL;
395 if (r->type == netatoms[UTF8_STRING]) {
396 str = g_strndup(xcb_get_property_value(r),
397 xcb_get_property_value_length(r));
398 } else if (r->type == netatoms[STRING]) {
399 str = rofi_latin_to_utf8_strdup(xcb_get_property_value(r),
400 xcb_get_property_value_length(r));
401 } else {
402 str = g_strdup("Invalid encoding.");
403 }
404
405 free(r);
406 return str;
407 }
408 free(r);
409 }
410 return NULL;
411}
412
413void window_set_atom_prop(xcb_window_t w, xcb_atom_t prop, xcb_atom_t *atoms,
414 int count) {
415 xcb_change_property(xcb->connection, XCB_PROP_MODE_REPLACE, w, prop,
416 XCB_ATOM_ATOM, 32, count, atoms);
417}
418
419/****
420 * Code used to get monitor layout.
421 */
422
426static void x11_monitor_free(workarea *m) {
427 g_free(m->name);
428 g_free(m);
429}
430
431static void x11_monitors_free(void) {
432 while (xcb->monitors != NULL) {
433 workarea *m = xcb->monitors;
434 xcb->monitors = m->next;
436 }
437}
438
445 double ratio_res = w->w / (double)w->h;
446 double ratio_size = w->mw / (double)w->mh;
447
448 if ((ratio_res < 1.0 && ratio_size > 1.0) ||
449 (ratio_res > 1.0 && ratio_size < 1.0)) {
450 // Oposite ratios, swap them.
451 int nh = w->mw;
452 w->mw = w->mh;
453 w->mh = nh;
454 }
455}
456
459static workarea *x11_get_monitor_from_output(xcb_randr_output_t out) {
460 xcb_randr_get_output_info_reply_t *op_reply;
461 xcb_randr_get_crtc_info_reply_t *crtc_reply;
462 xcb_randr_get_output_info_cookie_t it =
463 xcb_randr_get_output_info(xcb->connection, out, XCB_CURRENT_TIME);
464 op_reply = xcb_randr_get_output_info_reply(xcb->connection, it, NULL);
465 if (op_reply->crtc == XCB_NONE) {
466 free(op_reply);
467 return NULL;
468 }
469 xcb_randr_get_crtc_info_cookie_t ct = xcb_randr_get_crtc_info(
470 xcb->connection, op_reply->crtc, XCB_CURRENT_TIME);
471 crtc_reply = xcb_randr_get_crtc_info_reply(xcb->connection, ct, NULL);
472 if (!crtc_reply) {
473 free(op_reply);
474 return NULL;
475 }
476 workarea *retv = g_malloc0(sizeof(workarea));
477 retv->x = crtc_reply->x;
478 retv->y = crtc_reply->y;
479 retv->w = crtc_reply->width;
480 retv->h = crtc_reply->height;
481
482 retv->mw = op_reply->mm_width;
483 retv->mh = op_reply->mm_height;
485
486 char *tname = (char *)xcb_randr_get_output_info_name(op_reply);
487 int tname_len = xcb_randr_get_output_info_name_length(op_reply);
488
489 retv->name = g_malloc0((tname_len + 1) * sizeof(char));
490 memcpy(retv->name, tname, tname_len);
491
492 free(crtc_reply);
493 free(op_reply);
494 return retv;
495}
496
497#if (((XCB_RANDR_MAJOR_VERSION >= RANDR_PREF_MAJOR_VERSION) && \
498 (XCB_RANDR_MINOR_VERSION >= RANDR_PREF_MINOR_VERSION)) || \
499 XCB_RANDR_MAJOR_VERSION > RANDR_PREF_MAJOR_VERSION)
507static workarea *
508x11_get_monitor_from_randr_monitor(xcb_randr_monitor_info_t *mon) {
509 // Query to the name of the monitor.
510 xcb_generic_error_t *err;
511 xcb_get_atom_name_cookie_t anc =
512 xcb_get_atom_name(xcb->connection, mon->name);
513 xcb_get_atom_name_reply_t *atom_reply =
514 xcb_get_atom_name_reply(xcb->connection, anc, &err);
515 if (err != NULL) {
516 g_warning("Could not get RandR monitor name: X11 error code %d\n",
517 err->error_code);
518 free(err);
519 return NULL;
520 }
521 workarea *retv = g_malloc0(sizeof(workarea));
522
523 // Is primary monitor.
524 retv->primary = mon->primary;
525
526 // Position and size.
527 retv->x = mon->x;
528 retv->y = mon->y;
529 retv->w = mon->width;
530 retv->h = mon->height;
531
532 // Physical
533 retv->mw = mon->width_in_millimeters;
534 retv->mh = mon->height_in_millimeters;
536
537 // Name
538 retv->name =
539 g_strdup_printf("%.*s", xcb_get_atom_name_name_length(atom_reply),
540 xcb_get_atom_name_name(atom_reply));
541
542 // Free name atom.
543 free(atom_reply);
544
545 return retv;
546}
547#endif
548
549static int x11_is_extension_present(const char *extension) {
550 xcb_query_extension_cookie_t randr_cookie =
551 xcb_query_extension(xcb->connection, strlen(extension), extension);
552
553 xcb_query_extension_reply_t *randr_reply =
554 xcb_query_extension_reply(xcb->connection, randr_cookie, NULL);
555
556 int present = randr_reply->present;
557
558 free(randr_reply);
559
560 return present;
561}
562
564 xcb_xinerama_query_screens_cookie_t screens_cookie =
565 xcb_xinerama_query_screens_unchecked(xcb->connection);
566
567 xcb_xinerama_query_screens_reply_t *screens_reply =
568 xcb_xinerama_query_screens_reply(xcb->connection, screens_cookie, NULL);
569
570 xcb_xinerama_screen_info_iterator_t screens_iterator =
571 xcb_xinerama_query_screens_screen_info_iterator(screens_reply);
572
573 for (; screens_iterator.rem > 0;
574 xcb_xinerama_screen_info_next(&screens_iterator)) {
575 workarea *w = g_malloc0(sizeof(workarea));
576
577 w->x = screens_iterator.data->x_org;
578 w->y = screens_iterator.data->y_org;
579 w->w = screens_iterator.data->width;
580 w->h = screens_iterator.data->height;
581
582 w->next = xcb->monitors;
583 xcb->monitors = w;
584 }
585
586 int index = 0;
587 for (workarea *iter = xcb->monitors; iter; iter = iter->next) {
588 iter->monitor_id = index++;
589 }
590
591 free(screens_reply);
592}
593
594static void x11_build_monitor_layout(void) {
595 if (xcb->monitors) {
596 return;
597 }
598 // If RANDR is not available, try Xinerama
599 if (!x11_is_extension_present("RANDR")) {
600 // Check if xinerama is available.
601 if (x11_is_extension_present("XINERAMA")) {
602 g_debug("Query XINERAMA for monitor layout.");
604 return;
605 }
606 g_debug("No RANDR or Xinerama available for getting monitor layout.");
607 return;
608 }
609 g_debug("Query RANDR for monitor layout.");
610
611 g_debug("Randr XCB api version: %d.%d.", XCB_RANDR_MAJOR_VERSION,
612 XCB_RANDR_MINOR_VERSION);
613#if (((XCB_RANDR_MAJOR_VERSION == RANDR_PREF_MAJOR_VERSION) && \
614 (XCB_RANDR_MINOR_VERSION >= RANDR_PREF_MINOR_VERSION)) || \
615 XCB_RANDR_MAJOR_VERSION > RANDR_PREF_MAJOR_VERSION)
616 xcb_randr_query_version_cookie_t cversion = xcb_randr_query_version(
618 xcb_randr_query_version_reply_t *rversion =
619 xcb_randr_query_version_reply(xcb->connection, cversion, NULL);
620 if (rversion) {
621 g_debug("Found randr version: %d.%d", rversion->major_version,
622 rversion->minor_version);
623 // Check if we are 1.5 and up.
624 if (((rversion->major_version == RANDR_PREF_MAJOR_VERSION) &&
625 (rversion->minor_version >= RANDR_PREF_MINOR_VERSION)) ||
626 (rversion->major_version > RANDR_PREF_MAJOR_VERSION)) {
627 xcb_randr_get_monitors_cookie_t t =
628 xcb_randr_get_monitors(xcb->connection, xcb->screen->root, 1);
629 xcb_randr_get_monitors_reply_t *mreply =
630 xcb_randr_get_monitors_reply(xcb->connection, t, NULL);
631 if (mreply) {
632 xcb_randr_monitor_info_iterator_t iter =
633 xcb_randr_get_monitors_monitors_iterator(mreply);
634 while (iter.rem > 0) {
635 workarea *w = x11_get_monitor_from_randr_monitor(iter.data);
636 if (w) {
637 w->next = xcb->monitors;
638 xcb->monitors = w;
639 }
640 xcb_randr_monitor_info_next(&iter);
641 }
642 free(mreply);
643 }
644 }
645 free(rversion);
646 }
647#endif
648
649 // If no monitors found.
650 if (xcb->monitors == NULL) {
651 xcb_randr_get_screen_resources_current_reply_t *res_reply;
652 xcb_randr_get_screen_resources_current_cookie_t src;
653 src = xcb_randr_get_screen_resources_current(xcb->connection,
654 xcb->screen->root);
655 res_reply = xcb_randr_get_screen_resources_current_reply(xcb->connection,
656 src, NULL);
657 if (!res_reply) {
658 return; // just report error
659 }
660 int mon_num =
661 xcb_randr_get_screen_resources_current_outputs_length(res_reply);
662 xcb_randr_output_t *ops =
663 xcb_randr_get_screen_resources_current_outputs(res_reply);
664
665 // Get primary.
666 xcb_randr_get_output_primary_cookie_t pc =
667 xcb_randr_get_output_primary(xcb->connection, xcb->screen->root);
668 xcb_randr_get_output_primary_reply_t *pc_rep =
669 xcb_randr_get_output_primary_reply(xcb->connection, pc, NULL);
670
671 for (int i = mon_num - 1; i >= 0; i--) {
673 if (w) {
674 w->next = xcb->monitors;
675 xcb->monitors = w;
676 if (pc_rep && pc_rep->output == ops[i]) {
677 w->primary = TRUE;
678 }
679 }
680 }
681 // If exists, free primary output reply.
682 if (pc_rep) {
683 free(pc_rep);
684 }
685 free(res_reply);
686 }
687
688 // Number monitor
689 int index = 0;
690 for (workarea *iter = xcb->monitors; iter; iter = iter->next) {
691 iter->monitor_id = index++;
692 }
693}
694
696 int is_term = isatty(fileno(stdout));
697 printf("Monitor layout:\n");
698 for (workarea *iter = xcb->monitors; iter; iter = iter->next) {
699 printf("%s ID%s: %d", (is_term) ? color_bold : "",
700 is_term ? color_reset : "", iter->monitor_id);
701 if (iter->primary) {
702 printf(" (primary)");
703 }
704 printf("\n");
705 printf("%s name%s: %s\n", (is_term) ? color_bold : "",
706 is_term ? color_reset : "", iter->name);
707 printf("%s position%s: %d,%d\n", (is_term) ? color_bold : "",
708 is_term ? color_reset : "", iter->x, iter->y);
709 printf("%s size%s: %d,%d\n", (is_term) ? color_bold : "",
710 is_term ? color_reset : "", iter->w, iter->h);
711 if (iter->mw > 0 && iter->mh > 0) {
712 printf("%s size%s: %dmm,%dmm dpi: %.0f,%.0f\n",
713 (is_term) ? color_bold : "", is_term ? color_reset : "", iter->mw,
714 iter->mh, iter->w * 25.4 / (double)iter->mw,
715 iter->h * 25.4 / (double)iter->mh);
716 }
717 printf("\n");
718 }
719}
720
722 GSpawnChildSetupFunc *child_setup,
723 gpointer *user_data) {
724 if (context == NULL) {
725 return;
726 }
727
728 SnLauncherContext *sncontext;
729
730 sncontext = sn_launcher_context_new(xcb->sndisplay, xcb->screen_nbr);
731
732 sn_launcher_context_set_name(sncontext, context->name);
733 sn_launcher_context_set_description(sncontext, context->description);
734 if (context->binary != NULL) {
735 sn_launcher_context_set_binary_name(sncontext, context->binary);
736 }
737 if (context->icon != NULL) {
738 sn_launcher_context_set_icon_name(sncontext, context->icon);
739 }
740 if (context->app_id != NULL) {
742 }
743 if (context->wmclass != NULL) {
744 sn_launcher_context_set_wmclass(sncontext, context->wmclass);
745 }
746
747 xcb_get_property_cookie_t c;
748 unsigned int current_desktop = 0;
749
750 c = xcb_ewmh_get_current_desktop(&xcb->ewmh, xcb->screen_nbr);
751 if (xcb_ewmh_get_current_desktop_reply(&xcb->ewmh, c, &current_desktop,
752 NULL)) {
753 sn_launcher_context_set_workspace(sncontext, current_desktop);
754 }
755
756 sn_launcher_context_initiate(sncontext, "rofi", context->command,
757 xcb->last_timestamp);
758
759 *child_setup = (GSpawnChildSetupFunc)sn_launcher_context_setup_child_process;
760 *user_data = sncontext;
761}
762
763static int monitor_get_dimension(int monitor_id, workarea *mon) {
764 memset(mon, 0, sizeof(workarea));
765 mon->w = xcb->screen->width_in_pixels;
766 mon->h = xcb->screen->height_in_pixels;
767
768 workarea *iter = NULL;
769 for (iter = xcb->monitors; iter; iter = iter->next) {
770 if (iter->monitor_id == monitor_id) {
771 *mon = *iter;
772 return TRUE;
773 }
774 }
775 return FALSE;
776}
777// find the dimensions of the monitor displaying point x,y
778static void monitor_dimensions(int x, int y, workarea *mon) {
779 if (mon == NULL) {
780 g_error("%s: mon == NULL", __func__);
781 return;
782 }
783 memset(mon, 0, sizeof(workarea));
784 mon->w = xcb->screen->width_in_pixels;
785 mon->h = xcb->screen->height_in_pixels;
786
787 for (workarea *iter = xcb->monitors; iter; iter = iter->next) {
788 if (INTERSECT(x, y, iter->x, iter->y, iter->w, iter->h)) {
789 *mon = *iter;
790 break;
791 }
792 }
793}
794
805static int pointer_get(xcb_window_t root, int *x, int *y) {
806 *x = 0;
807 *y = 0;
808 xcb_query_pointer_cookie_t c = xcb_query_pointer(xcb->connection, root);
809 xcb_query_pointer_reply_t *r =
810 xcb_query_pointer_reply(xcb->connection, c, NULL);
811 if (r) {
812 *x = r->root_x;
813 *y = r->root_y;
814 free(r);
815 return TRUE;
816 }
817
818 return FALSE;
819}
820
821static int monitor_active_from_winid(xcb_drawable_t id, workarea *mon) {
822 if (mon == NULL) {
823 g_error("%s: mon == NULL", __func__);
824 return FALSE;
825 }
826 xcb_window_t root = xcb->screen->root;
827 xcb_get_geometry_cookie_t c = xcb_get_geometry(xcb->connection, id);
828 xcb_get_geometry_reply_t *r =
829 xcb_get_geometry_reply(xcb->connection, c, NULL);
830 if (r) {
831 xcb_translate_coordinates_cookie_t ct =
832 xcb_translate_coordinates(xcb->connection, id, root, r->x, r->y);
833 xcb_translate_coordinates_reply_t *t =
834 xcb_translate_coordinates_reply(xcb->connection, ct, NULL);
835 if (t) {
836 // place the menu above the window
837 // if some window is focused, place menu above window, else fall
838 // back to selected monitor.
839 mon->x = t->dst_x - r->x;
840 mon->y = t->dst_y - r->y;
841 mon->w = r->width;
842 mon->h = r->height;
843 free(r);
844 free(t);
845 return TRUE;
846 }
847 free(r);
848 }
849 return FALSE;
850}
852 int retv = FALSE;
853 xcb_window_t active_window;
854 xcb_get_property_cookie_t awc;
855 if (mon == NULL) {
856 g_error("%s: mon == NULL", __func__);
857 return retv;
858 }
859 awc = xcb_ewmh_get_active_window(&xcb->ewmh, xcb->screen_nbr);
860 if (!xcb_ewmh_get_active_window_reply(&xcb->ewmh, awc, &active_window,
861 NULL)) {
862 g_debug(
863 "Failed to get active window, falling back to mouse location (-5).");
864 return retv;
865 }
866 xcb_query_tree_cookie_t tree_cookie =
867 xcb_query_tree(xcb->connection, active_window);
868 xcb_query_tree_reply_t *tree_reply =
869 xcb_query_tree_reply(xcb->connection, tree_cookie, NULL);
870 if (!tree_reply) {
871 g_debug(
872 "Failed to get parent window, falling back to mouse location (-5).");
873 return retv;
874 }
875 // get geometry.
876 xcb_get_geometry_cookie_t c =
877 xcb_get_geometry(xcb->connection, active_window);
878 xcb_get_geometry_reply_t *r =
879 xcb_get_geometry_reply(xcb->connection, c, NULL);
880 if (!r) {
881 g_debug("Failed to get geometry of active window, falling back to mouse "
882 "location (-5).");
883 free(tree_reply);
884 return retv;
885 }
886 if (tree_reply->parent != r->root) {
887 xcb_translate_coordinates_cookie_t ct = xcb_translate_coordinates(
888 xcb->connection, tree_reply->parent, r->root, r->x, r->y);
889 xcb_translate_coordinates_reply_t *t =
890 xcb_translate_coordinates_reply(xcb->connection, ct, NULL);
891 if (t) {
892 r->x = t->dst_x;
893 r->y = t->dst_y;
894 free(t);
895 } else {
896 g_debug("Failed to get translate position of active window, falling back "
897 "to mouse location (-5).");
898 free(r);
899 free(tree_reply);
900 return retv;
901 }
902 }
903 if (mon_id == -2) {
904 // place the menu above the window
905 // if some window is focused, place menu above window, else fall
906 // back to selected monitor.
907 mon->x = r->x + r->border_width;
908 mon->y = r->y + r->border_width;
909 mon->w = r->width;
910 mon->h = r->height;
911 retv = TRUE;
912 } else if (mon_id == -4) {
913 g_debug("Find monitor at location: %d %d", r->x, r->y);
914 monitor_dimensions(r->x, r->y, mon);
915 retv = TRUE;
916 }
917 g_debug("mon pos: %d %d %d-%d", mon->x, mon->y, mon->w, mon->h);
918 free(r);
919 free(tree_reply);
920 return retv;
921}
922static int monitor_active_from_id(int mon_id, workarea *mon) {
923 xcb_window_t root = xcb->screen->root;
924 int x, y;
925 if (mon == NULL) {
926 g_error("%s: mon == NULL", __func__);
927 return FALSE;
928 }
929 g_debug("Monitor id: %d", mon_id);
930 // At mouse position.
931 if (mon_id == -3) {
932 if (pointer_get(root, &x, &y)) {
933 monitor_dimensions(x, y, mon);
934 mon->x = x;
935 mon->y = y;
936 return TRUE;
937 }
938 }
939 // Focused monitor
940 else if (mon_id == -1) {
941 g_debug("rofi on current monitor");
942 // Get the current desktop.
943 unsigned int current_desktop = 0;
944 xcb_get_property_cookie_t gcdc;
945 gcdc = xcb_ewmh_get_current_desktop(&xcb->ewmh, xcb->screen_nbr);
946 if (xcb_ewmh_get_current_desktop_reply(&xcb->ewmh, gcdc, &current_desktop,
947 NULL)) {
948 g_debug("Found current desktop: %u", current_desktop);
949 xcb_get_property_cookie_t c =
950 xcb_ewmh_get_desktop_viewport(&xcb->ewmh, xcb->screen_nbr);
951 xcb_ewmh_get_desktop_viewport_reply_t vp;
952 if (xcb_ewmh_get_desktop_viewport_reply(&xcb->ewmh, c, &vp, NULL)) {
953 g_debug("Found %d number of desktops", vp.desktop_viewport_len);
954 if (current_desktop < vp.desktop_viewport_len) {
955 g_debug("Found viewport for desktop: %d %d",
956 vp.desktop_viewport[current_desktop].x,
957 vp.desktop_viewport[current_desktop].y);
958 monitor_dimensions(vp.desktop_viewport[current_desktop].x,
959 vp.desktop_viewport[current_desktop].y, mon);
960 g_debug("Found monitor @: %d %d %dx%d", mon->x, mon->y, mon->w,
961 mon->h);
962 xcb_ewmh_get_desktop_viewport_reply_wipe(&vp);
963 return TRUE;
964 }
965 g_debug("Viewport does not exist for current desktop: %d, falling "
966 "back to mouse location (-5)",
967 current_desktop);
968 xcb_ewmh_get_desktop_viewport_reply_wipe(&vp);
969 } else {
970 g_debug("Failed to get viewport for current desktop: %d, falling back "
971 "to mouse location (-5).",
972 current_desktop);
973 }
974 } else {
975 g_debug("Failed to get current desktop, falling back to mouse location "
976 "(-5).");
977 }
978 } else if (mon_id == -2 || mon_id == -4) {
979 if (monitor_active_from_id_focused(mon_id, mon)) {
980 return TRUE;
981 }
982 }
983 // Monitor that has mouse pointer.
984 else if (mon_id == -5) {
985 if (pointer_get(root, &x, &y)) {
986 monitor_dimensions(x, y, mon);
987 return TRUE;
988 }
989 // This is our give up point.
990 return FALSE;
991 }
992 g_debug("Failed to find monitor, fall back to monitor showing mouse.");
993 return monitor_active_from_id(-5, mon);
994}
995
996// determine which monitor holds the active window, or failing that the mouse
997// pointer
999gboolean mon_set = FALSE;
1002 0,
1003};
1005 if (mon == NULL) {
1006 g_error("%s: mon == NULL", __func__);
1007 return FALSE;
1008 }
1009 g_debug("Monitor active");
1010 if (mon_set) {
1011 *mon = mon_cache;
1012 return TRUE;
1013 }
1014 if (config.monitor != NULL) {
1015 g_debug("Monitor lookup by name : %s", config.monitor);
1016 for (workarea *iter = xcb->monitors; iter; iter = iter->next) {
1017 if (g_strcmp0(config.monitor, iter->name) == 0) {
1018 *mon = *iter;
1019 mon_cache = *mon;
1020 mon_set = TRUE;
1021 return TRUE;
1022 }
1023 }
1024 }
1025 g_debug("Monitor lookup by name failed: %s", config.monitor);
1026 // Grab primary.
1027 if (g_strcmp0(config.monitor, "primary") == 0) {
1028 for (workarea *iter = xcb->monitors; iter; iter = iter->next) {
1029 if (iter->primary) {
1030 *mon = *iter;
1031 mon_cache = *mon;
1032 mon_set = TRUE;
1033 return TRUE;
1034 }
1035 }
1036 }
1037 if (g_str_has_prefix(config.monitor, "wid:")) {
1038 char *end = NULL;
1039 xcb_drawable_t win = g_ascii_strtoll(config.monitor + 4, &end, 0);
1040 if (end != config.monitor) {
1041 if (monitor_active_from_winid(win, mon)) {
1042 mon_cache = *mon;
1043 mon_set = TRUE;
1044 return TRUE;
1045 }
1046 }
1047 }
1048 {
1049 // IF fail, fall back to classic mode.
1050 char *end = NULL;
1051 gint64 mon_id = g_ascii_strtoll(config.monitor, &end, 0);
1052 if (end != config.monitor) {
1053 if (mon_id >= 0) {
1054 if (monitor_get_dimension(mon_id, mon)) {
1055 mon_cache = *mon;
1056 mon_set = TRUE;
1057 return TRUE;
1058 }
1059 g_warning("Failed to find selected monitor.");
1060 } else {
1061 int val = monitor_active_from_id(mon_id, mon);
1062 mon_cache = *mon;
1063 mon_set = TRUE;
1064 return val;
1065 }
1066 }
1067 }
1068 // Fallback.
1069 monitor_dimensions(0, 0, mon);
1070 mon_cache = *mon;
1071 mon_set = TRUE;
1072 return FALSE;
1073}
1074
1075static bool get_atom_name(xcb_connection_t *conn, xcb_atom_t atom, char **out) {
1076 xcb_get_atom_name_cookie_t cookie;
1077 xcb_get_atom_name_reply_t *reply;
1078 int length;
1079 char *name;
1080
1081 if (atom == 0) {
1082 *out = NULL;
1083 return true;
1084 }
1085
1086 cookie = xcb_get_atom_name(conn, atom);
1087 reply = xcb_get_atom_name_reply(conn, cookie, NULL);
1088 if (!reply)
1089 return false;
1090
1091 length = xcb_get_atom_name_name_length(reply);
1092 name = xcb_get_atom_name_name(reply);
1093
1094 (*out) = g_strndup(name, length);
1095 if (!(*out)) {
1096 free(reply);
1097 return false;
1098 }
1099
1100 free(reply);
1101 return true;
1102}
1103
1111 xcb_selection_notify_event_t *xse) {
1112 if (xse->property == XCB_ATOM_NONE) {
1113 g_debug("Failed to convert selection");
1114 } else if (xse->property == xcb->ewmh.UTF8_STRING) {
1115 gchar *text = window_get_text_prop(xse->requestor, xcb->ewmh.UTF8_STRING);
1116 if (text != NULL && text[0] != '\0') {
1117 unsigned int dl = strlen(text);
1118 // Strip new line
1119 for (unsigned int i = 0; i < dl; i++) {
1120 if (text[i] == '\n') {
1121 text[i] = '\0';
1122 }
1123 }
1124 rofi_view_handle_text(state, text);
1125 }
1126 g_free(text);
1127 } else {
1128 char *out = NULL;
1129 if (get_atom_name(xcb->connection, xse->property, &out)) {
1130 g_debug("rofi_view_paste: Got unknown atom: %s", out);
1131 g_free(out);
1132 } else {
1133 g_debug("rofi_view_paste: Got unknown, unnamed: %s", out);
1134 }
1135 }
1136}
1137
1138static gboolean
1140 NkBindingsMouseButton *button) {
1141 switch (x11_button) {
1142 case 1:
1143 *button = NK_BINDINGS_MOUSE_BUTTON_PRIMARY;
1144 break;
1145 case 3:
1146 *button = NK_BINDINGS_MOUSE_BUTTON_SECONDARY;
1147 break;
1148 case 2:
1149 *button = NK_BINDINGS_MOUSE_BUTTON_MIDDLE;
1150 break;
1151 case 8:
1152 *button = NK_BINDINGS_MOUSE_BUTTON_BACK;
1153 break;
1154 case 9:
1155 *button = NK_BINDINGS_MOUSE_BUTTON_FORWARD;
1156 break;
1157 case 4:
1158 case 5:
1159 case 6:
1160 case 7:
1161 return FALSE;
1162 default:
1163 *button = NK_BINDINGS_MOUSE_BUTTON_EXTRA + x11_button;
1164 }
1165 return TRUE;
1166}
1167
1168static gboolean x11_button_to_nk_bindings_scroll(guint32 x11_button,
1169 NkBindingsScrollAxis *axis,
1170 gint32 *steps) {
1171 *steps = 1;
1172 switch (x11_button) {
1173 case 4:
1174 *steps = -1;
1175 /* fallthrough */
1176 case 5:
1177 *axis = NK_BINDINGS_SCROLL_AXIS_VERTICAL;
1178 break;
1179 case 6:
1180 *steps = -1;
1181 /* fallthrough */
1182 case 7:
1183 *axis = NK_BINDINGS_SCROLL_AXIS_HORIZONTAL;
1184 break;
1185 default:
1186 return FALSE;
1187 }
1188 return TRUE;
1189}
1190
1191static void rofi_key_press_event_handler(xcb_key_press_event_t *xkpe,
1192 RofiViewState *state) {
1193 gchar *text;
1194 g_log("IMDKit", G_LOG_LEVEL_DEBUG, "press handler %d", xkpe->detail);
1195
1196 xcb->last_timestamp = xkpe->time;
1197 if (config.xserver_i300_workaround) {
1198 text = nk_bindings_seat_handle_key_with_modmask(
1199 xcb->bindings_seat, NULL, xkpe->state, xkpe->detail,
1200 NK_BINDINGS_KEY_STATE_PRESS);
1201 } else {
1202 text = nk_bindings_seat_handle_key(xcb->bindings_seat, NULL, xkpe->detail,
1203 NK_BINDINGS_KEY_STATE_PRESS);
1204 }
1205 if (text != NULL) {
1206 rofi_view_handle_text(state, text);
1207 g_free(text);
1208 }
1209}
1210
1211static void rofi_key_release_event_handler(xcb_key_release_event_t *xkre,
1212 G_GNUC_UNUSED RofiViewState *state) {
1213 g_log("IMDKit", G_LOG_LEVEL_DEBUG, "release handler %d", xkre->detail);
1214 xcb->last_timestamp = xkre->time;
1215 nk_bindings_seat_handle_key(xcb->bindings_seat, NULL, xkre->detail,
1216 NK_BINDINGS_KEY_STATE_RELEASE);
1217}
1218
1222static void main_loop_x11_event_handler_view(xcb_generic_event_t *event) {
1224 if (state == NULL) {
1225 return;
1226 }
1227
1228 switch (event->response_type & ~0x80) {
1229 case XCB_CLIENT_MESSAGE: {
1230 xcb_client_message_event_t *cme = (xcb_client_message_event_t *)event;
1231 xcb_atom_t atom = cme->data.data32[0];
1232 xcb_timestamp_t time = cme->data.data32[1];
1233 if (atom == netatoms[WM_TAKE_FOCUS]) {
1234 xcb_set_input_focus(xcb->connection, XCB_INPUT_FOCUS_NONE, cme->window,
1235 time);
1236 xcb_flush(xcb->connection);
1237 }
1238 break;
1239 }
1240 case XCB_DESTROY_NOTIFY: {
1241 xcb_window_t win = ((xcb_destroy_notify_event_t *)event)->window;
1242 if (win != rofi_view_get_window()) {
1243#ifdef WINDOW_MODE
1244 window_client_handle_signal(win, FALSE);
1245#endif
1246 } else {
1247 g_main_loop_quit(xcb->main_loop);
1248 }
1249 break;
1250 }
1251 case XCB_CREATE_NOTIFY: {
1252 xcb_window_t win = ((xcb_create_notify_event_t *)event)->window;
1253 if (win != rofi_view_get_window()) {
1254#ifdef WINDOW_MODE
1255 window_client_handle_signal(win, TRUE);
1256#endif
1257 }
1258 break;
1259 }
1260 case XCB_EXPOSE:
1262 break;
1263 case XCB_CONFIGURE_NOTIFY: {
1264 xcb_configure_notify_event_t *xce = (xcb_configure_notify_event_t *)event;
1266 break;
1267 }
1268 case XCB_MOTION_NOTIFY: {
1269 xcb_motion_notify_event_t *xme = (xcb_motion_notify_event_t *)event;
1270 gboolean button_mask = xme->state & XCB_EVENT_MASK_BUTTON_1_MOTION;
1271 rofi_view_handle_mouse_motion(state, xme->event_x, xme->event_y,
1272 !button_mask && config.hover_select);
1273 break;
1274 }
1275 case XCB_BUTTON_PRESS: {
1276 xcb_button_press_event_t *bpe = (xcb_button_press_event_t *)event;
1277 NkBindingsMouseButton button;
1278 NkBindingsScrollAxis axis;
1279 gint32 steps;
1280
1281 xcb->last_timestamp = bpe->time;
1282 rofi_view_handle_mouse_motion(state, bpe->event_x, bpe->event_y, FALSE);
1283 if (x11_button_to_nk_bindings_button(bpe->detail, &button)) {
1284 nk_bindings_seat_handle_button(xcb->bindings_seat, NULL, button,
1285 NK_BINDINGS_BUTTON_STATE_PRESS, bpe->time);
1286 } else if (x11_button_to_nk_bindings_scroll(bpe->detail, &axis, &steps)) {
1287 nk_bindings_seat_handle_scroll(xcb->bindings_seat, NULL, axis, steps);
1288 }
1289 xcb->mouse_seen++;
1290 break;
1291 }
1292 case XCB_SELECTION_CLEAR: {
1293 g_debug("Selection Clear.");
1295 } break;
1296 case XCB_SELECTION_REQUEST: {
1297 g_debug("Selection Request.");
1298 xcb_selection_request_event_t *req = (xcb_selection_request_event_t *)event;
1299 if (req->selection == netatoms[CLIPBOARD]) {
1300 xcb_atom_t targets[2];
1301 xcb_selection_notify_event_t selection_notify = {
1302 .response_type = XCB_SELECTION_NOTIFY,
1303 .sequence = 0,
1304 .time = req->time,
1305 .requestor = req->requestor,
1306 .selection = req->selection,
1307 .target = req->target,
1308 .property = XCB_ATOM_NONE,
1309 };
1310 // If no clipboard, we return NONE.
1311 if (xcb->clipboard) {
1312 // Request for UTF-8
1313 if (req->target == netatoms[UTF8_STRING]) {
1314 g_debug("Selection Request UTF-8.");
1315 xcb_change_property(xcb->connection, XCB_PROP_MODE_REPLACE,
1316 req->requestor, req->property,
1317 netatoms[UTF8_STRING], 8,
1318 strlen(xcb->clipboard) + 1, xcb->clipboard);
1319 selection_notify.property = req->property;
1320 } else if (req->target == netatoms[TARGETS]) {
1321 g_debug("Selection Request Targets.");
1322 // We currently only support UTF8 from clipboard. So indicate this.
1323 targets[0] = netatoms[UTF8_STRING];
1324 xcb_change_property(xcb->connection, XCB_PROP_MODE_REPLACE,
1325 req->requestor, req->property, XCB_ATOM_ATOM, 32,
1326 1, targets);
1327 selection_notify.property = req->property;
1328 }
1329 }
1330
1331 xcb_send_event(xcb->connection,
1332 0, // propagate
1333 req->requestor, XCB_EVENT_MASK_NO_EVENT,
1334 (const char *)&selection_notify);
1335 xcb_flush(xcb->connection);
1336 }
1337 } break;
1338 case XCB_BUTTON_RELEASE: {
1339 xcb_button_release_event_t *bre = (xcb_button_release_event_t *)event;
1340 NkBindingsMouseButton button;
1341
1342 xcb->last_timestamp = bre->time;
1343 if (x11_button_to_nk_bindings_button(bre->detail, &button)) {
1344 nk_bindings_seat_handle_button(xcb->bindings_seat, NULL, button,
1345 NK_BINDINGS_BUTTON_STATE_RELEASE,
1346 bre->time);
1347 }
1348 if (config.click_to_exit == TRUE) {
1349 if (!xcb->mouse_seen) {
1350 rofi_view_temp_click_to_exit(state, bre->event);
1351 }
1352 xcb->mouse_seen--;
1353 }
1354 break;
1355 }
1356 // Paste event.
1357 case XCB_SELECTION_NOTIFY:
1358 rofi_view_paste(state, (xcb_selection_notify_event_t *)event);
1359 break;
1360 case XCB_KEYMAP_NOTIFY: {
1361 xcb_keymap_notify_event_t *kne = (xcb_keymap_notify_event_t *)event;
1362 for (gint32 by = 0; by < 31; ++by) {
1363 for (gint8 bi = 0; bi < 7; ++bi) {
1364 if (kne->keys[by] & (1 << bi)) {
1365 // X11 keycodes starts at 8
1366 nk_bindings_seat_handle_key(xcb->bindings_seat, NULL,
1367 (8 * by + bi) + 8,
1368 NK_BINDINGS_KEY_STATE_PRESSED);
1369 }
1370 }
1371 }
1372 break;
1373 }
1374 case XCB_KEY_PRESS: {
1375 xcb_key_press_event_t *xkpe = (xcb_key_press_event_t *)event;
1376#ifdef XCB_IMDKIT
1377 if (xcb->ic) {
1378 g_log("IMDKit", G_LOG_LEVEL_DEBUG, "press key %d to xim", xkpe->detail);
1379 xcb_xim_forward_event(xcb->im, xcb->ic, xkpe);
1380 return;
1381 } else
1382#endif
1383 {
1384 rofi_key_press_event_handler(xkpe, state);
1385 }
1386 break;
1387 }
1388 case XCB_KEY_RELEASE: {
1389 xcb_key_release_event_t *xkre = (xcb_key_release_event_t *)event;
1390#ifdef XCB_IMDKIT
1391 if (xcb->ic) {
1392 g_log("IMDKit", G_LOG_LEVEL_DEBUG, "release key %d to xim", xkre->detail);
1393
1394 // Check if the keysym is a modifier key (e.g., Shift, Ctrl, Alt). If it
1395 // is, sleep for 5 milliseconds as a workaround for XCB XIM limitation.
1396 // This sleep helps to ensure that XCB XIM can properly handle subsequent
1397 // key events that may occur rapidly after a modifier key is pressed.
1398 xcb_keysym_t sym = xcb_key_press_lookup_keysym(xcb->syms, xkre, 0);
1399 if (xcb_is_modifier_key(sym)) {
1400 struct timespec five_millis = {.tv_sec = 0, .tv_nsec = 5000000};
1401 nanosleep(&five_millis, NULL);
1402 }
1403 xcb_xim_forward_event(xcb->im, xcb->ic, xkre);
1404 return;
1405 } else
1406#endif
1407 {
1408 rofi_key_release_event_handler(xkre, state);
1409 }
1410 break;
1411 }
1412 default:
1413 break;
1414 }
1416}
1417
1418#ifdef XCB_IMDKIT
1419void x11_event_handler_fowarding(G_GNUC_UNUSED xcb_xim_t *im,
1420 G_GNUC_UNUSED xcb_xic_t ic,
1421 xcb_key_press_event_t *event,
1422 G_GNUC_UNUSED void *user_data) {
1424 if (state == NULL) {
1425 return;
1426 }
1427
1428 uint8_t type = event->response_type & ~0x80;
1429 if (type == XCB_KEY_PRESS) {
1430 rofi_key_press_event_handler(event, state);
1431 } else if (type == XCB_KEY_RELEASE) {
1432 xcb_key_release_event_t *xkre = (xcb_key_release_event_t *)event;
1433 rofi_key_release_event_handler(xkre, state);
1434 }
1436}
1437#endif
1438
1439static gboolean main_loop_x11_event_handler(xcb_generic_event_t *ev,
1440 G_GNUC_UNUSED gpointer user_data) {
1441 if (ev == NULL) {
1442 int status = xcb_connection_has_error(xcb->connection);
1443 if (status > 0) {
1444 g_warning("The XCB connection to X server had a fatal error: %d", status);
1445 g_main_loop_quit(xcb->main_loop);
1446 return G_SOURCE_REMOVE;
1447 }
1448 // DD: it seems this handler often gets dispatched while the queue in GWater
1449 // is empty. resulting in a NULL for ev. This seems not an error.
1450 // g_warning("main_loop_x11_event_handler: ev == NULL, status == %d",
1451 // status);
1452 return G_SOURCE_CONTINUE;
1453 }
1454
1455#ifdef XCB_IMDKIT
1456 if (xcb->im && xcb_xim_filter_event(xcb->im, ev))
1457 return G_SOURCE_CONTINUE;
1458#endif
1459
1460 uint8_t type = ev->response_type & ~0x80;
1461 if (type == xcb->xkb.first_event) {
1462 switch (ev->pad0) {
1463 case XCB_XKB_MAP_NOTIFY: {
1464 struct xkb_keymap *keymap = xkb_x11_keymap_new_from_device(
1465 nk_bindings_seat_get_context(xcb->bindings_seat), xcb->connection,
1466 xcb->xkb.device_id, 0);
1467 struct xkb_state *state = xkb_x11_state_new_from_device(
1468 keymap, xcb->connection, xcb->xkb.device_id);
1469 nk_bindings_seat_update_keymap(xcb->bindings_seat, keymap, state);
1470 xkb_keymap_unref(keymap);
1471 xkb_state_unref(state);
1472 break;
1473 }
1474 case XCB_XKB_STATE_NOTIFY: {
1475 xcb_xkb_state_notify_event_t *ksne = (xcb_xkb_state_notify_event_t *)ev;
1476 nk_bindings_seat_update_mask(xcb->bindings_seat, NULL, ksne->baseMods,
1477 ksne->latchedMods, ksne->lockedMods,
1478 ksne->baseGroup, ksne->latchedGroup,
1479 ksne->lockedGroup);
1481 break;
1482 }
1483 }
1484 return G_SOURCE_CONTINUE;
1485 }
1486 if (xcb->sndisplay != NULL) {
1487 sn_xcb_display_process_event(xcb->sndisplay, ev);
1488 }
1489
1491 return G_SOURCE_CONTINUE;
1492}
1493
1494void rofi_xcb_set_input_focus(xcb_window_t w) {
1495 if (config.steal_focus != TRUE) {
1496 xcb->focus_revert = 0;
1497 return;
1498 }
1499 xcb_generic_error_t *error;
1500 xcb_get_input_focus_reply_t *freply;
1501 xcb_get_input_focus_cookie_t fcookie = xcb_get_input_focus(xcb->connection);
1502 freply = xcb_get_input_focus_reply(xcb->connection, fcookie, &error);
1503 if (error != NULL) {
1504 g_warning("Could not get input focus (error %d), will revert focus to best "
1505 "effort",
1506 error->error_code);
1507 free(error);
1508 xcb->focus_revert = 0;
1509 } else {
1510 xcb->focus_revert = freply->focus;
1511 }
1512 xcb_set_input_focus(xcb->connection, XCB_INPUT_FOCUS_POINTER_ROOT, w,
1513 XCB_CURRENT_TIME);
1514 xcb_flush(xcb->connection);
1515}
1516
1518 if (xcb->focus_revert == 0) {
1519 return;
1520 }
1521
1522 xcb_set_input_focus(xcb->connection, XCB_INPUT_FOCUS_POINTER_ROOT,
1523 xcb->focus_revert, XCB_CURRENT_TIME);
1524 xcb_flush(xcb->connection);
1525}
1526
1527static int take_pointer(xcb_window_t w, int iters) {
1528 int i = 0;
1529 while (TRUE) {
1530 if (xcb_connection_has_error(xcb->connection)) {
1531 g_warning("Connection has error");
1532 exit(EXIT_FAILURE);
1533 }
1534 xcb_grab_pointer_cookie_t cc =
1535 xcb_grab_pointer(xcb->connection, 1, w, XCB_EVENT_MASK_BUTTON_RELEASE,
1536 XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, w, XCB_NONE,
1537 XCB_CURRENT_TIME);
1538 xcb_grab_pointer_reply_t *r =
1539 xcb_grab_pointer_reply(xcb->connection, cc, NULL);
1540 if (r) {
1541 if (r->status == XCB_GRAB_STATUS_SUCCESS) {
1542 free(r);
1543 return 1;
1544 }
1545 free(r);
1546 }
1547 if ((++i) > iters) {
1548 break;
1549 }
1550 struct timespec del = {.tv_sec = 0, .tv_nsec = 1000000};
1551 nanosleep(&del, NULL);
1552 }
1553 return 0;
1554}
1555
1556static int take_keyboard(xcb_window_t w, int iters) {
1557 int i = 0;
1558 while (TRUE) {
1559 if (xcb_connection_has_error(xcb->connection)) {
1560 g_warning("Connection has error");
1561 exit(EXIT_FAILURE);
1562 }
1563 xcb_grab_keyboard_cookie_t cc =
1564 xcb_grab_keyboard(xcb->connection, 1, w, XCB_CURRENT_TIME,
1565 XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
1566 xcb_grab_keyboard_reply_t *r =
1567 xcb_grab_keyboard_reply(xcb->connection, cc, NULL);
1568 if (r) {
1569 if (r->status == XCB_GRAB_STATUS_SUCCESS) {
1570 free(r);
1571 return 1;
1572 }
1573 free(r);
1574 }
1575 if ((++i) > iters) {
1576 break;
1577 }
1578 struct timespec del = {.tv_sec = 0, .tv_nsec = 1000000};
1579 nanosleep(&del, NULL);
1580 }
1581 return 0;
1582}
1583
1584static void release_keyboard(void) {
1585 xcb_ungrab_keyboard(xcb->connection, XCB_CURRENT_TIME);
1586}
1587static void release_pointer(void) {
1588 xcb_ungrab_pointer(xcb->connection, XCB_CURRENT_TIME);
1589}
1590
1592static int error_trap_depth = 0;
1593static void error_trap_push(G_GNUC_UNUSED SnDisplay *display,
1594 G_GNUC_UNUSED xcb_connection_t *xdisplay) {
1596}
1597
1598static void error_trap_pop(G_GNUC_UNUSED SnDisplay *display,
1599 xcb_connection_t *xdisplay) {
1600 if (error_trap_depth == 0) {
1601 g_warning("Error trap underflow!");
1602 exit(EXIT_FAILURE);
1603 }
1604
1605 xcb_flush(xdisplay);
1607}
1608
1613 // X atom values
1614 for (int i = 0; i < NUM_NETATOMS; i++) {
1615 xcb_intern_atom_cookie_t cc = xcb_intern_atom(
1616 xcb->connection, 0, strlen(netatom_names[i]), netatom_names[i]);
1617 xcb_intern_atom_reply_t *r =
1618 xcb_intern_atom_reply(xcb->connection, cc, NULL);
1619 if (r) {
1620 netatoms[i] = r->atom;
1621 free(r);
1622 }
1623 }
1624}
1625
1627 char *retv = NULL;
1628 xcb_window_t wm_win = 0;
1629 xcb_get_property_cookie_t cc = xcb_ewmh_get_supporting_wm_check_unchecked(
1630 &xcb->ewmh, xcb_stuff_get_root_window());
1631
1632 if (xcb_ewmh_get_supporting_wm_check_reply(&xcb->ewmh, cc, &wm_win, NULL)) {
1633 xcb_ewmh_get_utf8_strings_reply_t wtitle;
1634 xcb_get_property_cookie_t cookie =
1635 xcb_ewmh_get_wm_name_unchecked(&(xcb->ewmh), wm_win);
1636 if (xcb_ewmh_get_wm_name_reply(&(xcb->ewmh), cookie, &wtitle, (void *)0)) {
1637 if (wtitle.strings_len > 0) {
1638 retv = g_strndup(wtitle.strings, wtitle.strings_len);
1639 }
1640 xcb_ewmh_get_utf8_strings_reply_wipe(&wtitle);
1641 }
1642 }
1643 return retv;
1644}
1645
1647 xcb_window_t wm_win = 0;
1648 xcb_get_property_cookie_t cc = xcb_ewmh_get_supporting_wm_check_unchecked(
1649 &xcb->ewmh, xcb_stuff_get_root_window());
1650
1651 if (xcb_ewmh_get_supporting_wm_check_reply(&xcb->ewmh, cc, &wm_win, NULL)) {
1652 xcb_ewmh_get_utf8_strings_reply_t wtitle;
1653 xcb_get_property_cookie_t cookie =
1654 xcb_ewmh_get_wm_name_unchecked(&(xcb->ewmh), wm_win);
1655 if (xcb_ewmh_get_wm_name_reply(&(xcb->ewmh), cookie, &wtitle, (void *)0)) {
1656 if (wtitle.strings_len > 0) {
1657 // Copy the string and add terminating '\0'.
1658 char *str = g_strndup(wtitle.strings, wtitle.strings_len);
1659 g_debug("Found window manager: |%s|", str);
1660 if (g_strcmp0(str, "i3") == 0) {
1663 }
1664 g_free(str);
1665 }
1666 xcb_ewmh_get_utf8_strings_reply_wipe(&wtitle);
1667 }
1668 }
1669}
1670
1671gboolean display_setup(GMainLoop *main_loop, NkBindings *bindings) {
1672 // Get DISPLAY, first env, then argument.
1673 // We never modify display_str content.
1674 char *display_str = (char *)g_getenv("DISPLAY");
1675 find_arg_str("-display", &display_str);
1676
1677 xcb->main_loop = main_loop;
1678#ifdef XCB_IMDKIT
1679 xcb_compound_text_init();
1680#endif
1681 xcb->source = g_water_xcb_source_new(g_main_loop_get_context(xcb->main_loop),
1682 display_str, &xcb->screen_nbr,
1683 main_loop_x11_event_handler, NULL, NULL);
1684 if (xcb->source == NULL) {
1685 g_warning("Failed to open display: %s", display_str);
1686 return FALSE;
1687 }
1688 xcb->connection = g_water_xcb_source_get_connection(xcb->source);
1689#ifdef XCB_IMDKIT
1690 xcb->im = xcb_xim_create(xcb->connection, xcb->screen_nbr, NULL);
1691 xcb->syms = xcb_key_symbols_alloc(xcb->connection);
1692#endif
1693
1694#ifdef XCB_IMDKIT
1695#ifndef XCB_IMDKIT_1_0_3_LOWER
1696 xcb_xim_set_use_compound_text(xcb->im, true);
1697 xcb_xim_set_use_utf8_string(xcb->im, true);
1698#endif
1699#endif
1700
1701 TICK_N("Open Display");
1702
1703 xcb->screen = xcb_aux_get_screen(xcb->connection, xcb->screen_nbr);
1704
1706
1707 xcb_intern_atom_cookie_t *ac =
1708 xcb_ewmh_init_atoms(xcb->connection, &xcb->ewmh);
1709 xcb_generic_error_t *errors = NULL;
1710 xcb_ewmh_init_atoms_replies(&xcb->ewmh, ac, &errors);
1711 if (errors) {
1712 g_warning("Failed to create EWMH atoms");
1713 free(errors);
1714 }
1715 // Discover the current active window manager.
1717 TICK_N("Setup XCB");
1718
1719 if (xkb_x11_setup_xkb_extension(
1720 xcb->connection, XKB_X11_MIN_MAJOR_XKB_VERSION,
1721 XKB_X11_MIN_MINOR_XKB_VERSION, XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS,
1722 NULL, NULL, &xcb->xkb.first_event, NULL) < 0) {
1723 g_warning("cannot setup XKB extension!");
1724 return FALSE;
1725 }
1726
1727 xcb->xkb.device_id = xkb_x11_get_core_keyboard_device_id(xcb->connection);
1728
1729 enum {
1730 required_events =
1731 (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY |
1732 XCB_XKB_EVENT_TYPE_MAP_NOTIFY | XCB_XKB_EVENT_TYPE_STATE_NOTIFY),
1733
1734 required_nkn_details = (XCB_XKB_NKN_DETAIL_KEYCODES),
1735
1736 required_map_parts =
1737 (XCB_XKB_MAP_PART_KEY_TYPES | XCB_XKB_MAP_PART_KEY_SYMS |
1738 XCB_XKB_MAP_PART_MODIFIER_MAP | XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS |
1739 XCB_XKB_MAP_PART_KEY_ACTIONS | XCB_XKB_MAP_PART_VIRTUAL_MODS |
1740 XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP),
1741
1742 required_state_details =
1743 (XCB_XKB_STATE_PART_MODIFIER_BASE | XCB_XKB_STATE_PART_MODIFIER_LATCH |
1744 XCB_XKB_STATE_PART_MODIFIER_LOCK | XCB_XKB_STATE_PART_GROUP_BASE |
1745 XCB_XKB_STATE_PART_GROUP_LATCH | XCB_XKB_STATE_PART_GROUP_LOCK),
1746 };
1747
1748 static const xcb_xkb_select_events_details_t details = {
1749 .affectNewKeyboard = required_nkn_details,
1750 .newKeyboardDetails = required_nkn_details,
1751 .affectState = required_state_details,
1752 .stateDetails = required_state_details,
1753 };
1754 xcb_xkb_select_events(xcb->connection, xcb->xkb.device_id,
1755 required_events, /* affectWhich */
1756 0, /* clear */
1757 required_events, /* selectAll */
1758 required_map_parts, /* affectMap */
1759 required_map_parts, /* map */
1760 &details);
1761
1762 xcb->bindings_seat = nk_bindings_seat_new(bindings, XKB_CONTEXT_NO_FLAGS);
1763 struct xkb_keymap *keymap = xkb_x11_keymap_new_from_device(
1764 nk_bindings_seat_get_context(xcb->bindings_seat), xcb->connection,
1765 xcb->xkb.device_id, XKB_KEYMAP_COMPILE_NO_FLAGS);
1766 if (keymap == NULL) {
1767 g_warning("Failed to get Keymap for current keyboard device.");
1768 return FALSE;
1769 }
1770 struct xkb_state *state = xkb_x11_state_new_from_device(
1771 keymap, xcb->connection, xcb->xkb.device_id);
1772 if (state == NULL) {
1773 g_warning("Failed to get state object for current keyboard device.");
1774 return FALSE;
1775 }
1776
1777 nk_bindings_seat_update_keymap(xcb->bindings_seat, keymap, state);
1778 xkb_state_unref(state);
1779 xkb_keymap_unref(keymap);
1780
1781 // determine numlock mask so we can bind on keys with and without it
1783
1784 if (xcb_connection_has_error(xcb->connection)) {
1785 g_warning("Connection has error");
1786 return FALSE;
1787 }
1788
1789 uint32_t val[] = {XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY};
1790
1791 xcb_change_window_attributes(xcb->connection, xcb_stuff_get_root_window(),
1792 XCB_CW_EVENT_MASK, val);
1793
1794 // startup not.
1795 xcb->sndisplay =
1796 sn_xcb_display_new(xcb->connection, error_trap_push, error_trap_pop);
1797 if (xcb_connection_has_error(xcb->connection)) {
1798 g_warning("Connection has error");
1799 return FALSE;
1800 }
1801
1802 if (xcb->sndisplay != NULL) {
1803 xcb->sncontext = sn_launchee_context_new_from_environment(xcb->sndisplay,
1804 xcb->screen_nbr);
1805 }
1806 if (xcb_connection_has_error(xcb->connection)) {
1807 g_warning("Connection has error");
1808 return FALSE;
1809 }
1810
1811 return TRUE;
1812}
1813
1815 xcb_depth_t *root_depth = NULL;
1816 xcb_depth_iterator_t depth_iter;
1817 for (depth_iter = xcb_screen_allowed_depths_iterator(xcb->screen);
1818 depth_iter.rem; xcb_depth_next(&depth_iter)) {
1819 xcb_depth_t *d = depth_iter.data;
1820
1821 xcb_visualtype_iterator_t visual_iter;
1822 for (visual_iter = xcb_depth_visuals_iterator(d); visual_iter.rem;
1823 xcb_visualtype_next(&visual_iter)) {
1824 xcb_visualtype_t *v = visual_iter.data;
1825 if ((v->bits_per_rgb_value == 8) && (d->depth == 32) &&
1826 (v->_class == XCB_VISUAL_CLASS_TRUE_COLOR)) {
1827 depth = d;
1828 visual = v;
1829 }
1830 if (xcb->screen->root_visual == v->visual_id) {
1831 root_depth = d;
1832 root_visual = v;
1833 }
1834 }
1835 }
1836 if (visual != NULL) {
1837 xcb_void_cookie_t c;
1838 xcb_generic_error_t *e;
1839 map = xcb_generate_id(xcb->connection);
1840 c = xcb_create_colormap_checked(xcb->connection, XCB_COLORMAP_ALLOC_NONE,
1841 map, xcb->screen->root, visual->visual_id);
1842 e = xcb_request_check(xcb->connection, c);
1843 if (e) {
1844 depth = NULL;
1845 visual = NULL;
1846 free(e);
1847 }
1848 }
1849
1850 if (visual == NULL) {
1851 depth = root_depth;
1853 map = xcb->screen->default_colormap;
1854 }
1855}
1856
1857static void x11_lookup_cursors(void) {
1858 xcb_cursor_context_t *ctx;
1859
1860 if (xcb_cursor_context_new(xcb->connection, xcb->screen, &ctx) < 0) {
1861 return;
1862 }
1863
1864 for (int i = 0; i < NUM_CURSORS; ++i) {
1865 cursors[i] = xcb_cursor_load_cursor(ctx, cursor_names[i].css_name);
1866
1867 if (cursors[i] == XCB_CURSOR_NONE) {
1868 cursors[i] =
1869 xcb_cursor_load_cursor(ctx, cursor_names[i].traditional_name);
1870 }
1871 }
1872
1873 xcb_cursor_context_free(ctx);
1874}
1875
1880static gboolean lazy_grab_pointer(G_GNUC_UNUSED gpointer data) {
1881 // After 5 sec.
1882 if (lazy_grab_retry_count_pt > (5 * 1000)) {
1883 g_warning("Failed to grab pointer after %u times. Giving up.",
1885 return G_SOURCE_REMOVE;
1886 }
1888 return G_SOURCE_REMOVE;
1889 }
1891 return G_SOURCE_CONTINUE;
1892}
1893static gboolean lazy_grab_keyboard(G_GNUC_UNUSED gpointer data) {
1894 // After 5 sec.
1895 if (lazy_grab_retry_count_kb > (5 * 1000)) {
1896 g_warning("Failed to grab keyboard after %u times. Giving up.",
1898 g_main_loop_quit(xcb->main_loop);
1899 return G_SOURCE_REMOVE;
1900 }
1902 return G_SOURCE_REMOVE;
1903 }
1905 return G_SOURCE_CONTINUE;
1906}
1907
1908gboolean display_late_setup(void) {
1910
1912
1916 // Try to grab the keyboard as early as possible.
1917 // We grab this using the rootwindow (as dmenu does it).
1918 // this seems to result in the smallest delay for most people.
1919 if (find_arg("-normal-window") >= 0 || find_arg("-transient-window") >= 0) {
1920 return TRUE;
1921 }
1922 if (find_arg("-no-lazy-grab") >= 0) {
1924 g_warning("Failed to grab keyboard, even after %d uS.", 500 * 1000);
1925 return FALSE;
1926 }
1928 g_warning("Failed to grab mouse pointer, even after %d uS.", 100 * 1000);
1929 }
1930 } else {
1932 g_timeout_add(1, lazy_grab_keyboard, NULL);
1933 }
1935 g_timeout_add(1, lazy_grab_pointer, NULL);
1936 }
1937 }
1938 return TRUE;
1939}
1940
1941xcb_window_t xcb_stuff_get_root_window(void) { return xcb->screen->root; }
1942
1946 xcb_flush(xcb->connection);
1947}
1948
1950 if (xcb->connection == NULL) {
1951 return;
1952 }
1953
1954 g_debug("Cleaning up XCB and XKB");
1955
1956 nk_bindings_seat_free(xcb->bindings_seat);
1957 if (xcb->sncontext != NULL) {
1958 sn_launchee_context_unref(xcb->sncontext);
1959 xcb->sncontext = NULL;
1960 }
1961 if (xcb->sndisplay != NULL) {
1962 sn_display_unref(xcb->sndisplay);
1963 xcb->sndisplay = NULL;
1964 }
1966 xcb_ewmh_connection_wipe(&(xcb->ewmh));
1967 xcb_flush(xcb->connection);
1968 xcb_aux_sync(xcb->connection);
1969#ifdef XCB_IMDKIT
1970 xcb_xim_close(xcb->im);
1971 xcb_xim_destroy(xcb->im);
1972 xcb->im = NULL;
1973#endif
1974 g_water_xcb_source_free(xcb->source);
1975 xcb->source = NULL;
1976 xcb->connection = NULL;
1977 xcb->screen = NULL;
1978 xcb->screen_nbr = 0;
1979}
1980
1981void x11_disable_decoration(xcb_window_t window) {
1982 // Flag used to indicate we are setting the decoration type.
1983 const uint32_t MWM_HINTS_DECORATIONS = (1 << 1);
1984 // Motif property data structure
1985 struct MotifWMHints {
1986 uint32_t flags;
1987 uint32_t functions;
1988 uint32_t decorations;
1989 int32_t inputMode;
1990 uint32_t state;
1991 };
1992
1993 struct MotifWMHints hints;
1994 hints.flags = MWM_HINTS_DECORATIONS;
1995 hints.decorations = 0;
1996 hints.functions = 0;
1997 hints.inputMode = 0;
1998 hints.state = 0;
1999
2000 xcb_atom_t ha = netatoms[_MOTIF_WM_HINTS];
2001 xcb_change_property(xcb->connection, XCB_PROP_MODE_REPLACE, window, ha, ha,
2002 32, 5, &hints);
2003}
2004
2005void x11_set_cursor(xcb_window_t window, X11CursorType type) {
2006 if (type < 0 || type >= NUM_CURSORS) {
2007 return;
2008 }
2009
2010 if (cursors[type] == XCB_CURSOR_NONE) {
2011 return;
2012 }
2013
2014 xcb_change_window_attributes(xcb->connection, window, XCB_CW_CURSOR,
2015 &(cursors[type]));
2016}
2017void xcb_stuff_set_clipboard(char *data) {
2018 g_free(xcb->clipboard);
2019 xcb->clipboard = data;
2020}
char * rofi_latin_to_utf8_strdup(const char *input, gssize length)
Definition helper.c:806
int find_arg_str(const char *const key, char **val)
Definition helper.c:308
int find_arg(const char *const key)
Definition helper.c:299
#define color_reset
Definition rofi.h:115
#define color_bold
Definition rofi.h:117
#define TICK()
Definition timings.h:64
#define TICK_N(a)
Definition timings.h:69
xcb_window_t rofi_view_get_window(void)
Definition view.c:2793
RofiViewState * rofi_view_get_active(void)
Definition view.c:607
void rofi_view_handle_text(RofiViewState *state, char *text)
Definition view.c:2019
void rofi_view_handle_mouse_motion(RofiViewState *state, gint x, gint y, gboolean find_mouse_target)
Definition view.c:2061
void rofi_view_temp_click_to_exit(RofiViewState *state, xcb_window_t target)
Definition view.c:2150
void rofi_view_temp_configure_notify(RofiViewState *state, xcb_configure_notify_event_t *xce)
Definition view.c:2116
void rofi_view_frame_callback(void)
Definition view.c:2159
void rofi_view_maybe_update(RofiViewState *state)
Definition view.c:2089
NkBindings * bindings
Definition rofi.c:138
GMainLoop * main_loop
Definition rofi.c:141
Settings config
const gchar * binary
Definition helper.h:290
const gchar * wmclass
Definition helper.h:298
const gchar * app_id
Definition helper.h:296
const gchar * description
Definition helper.h:292
const gchar * name
Definition helper.h:288
const gchar * icon
Definition helper.h:294
const gchar * command
Definition helper.h:300
int w
Definition xcb.h:114
int x
Definition xcb.h:110
int monitor_id
Definition xcb.h:106
char * name
Definition xcb.h:119
int mh
Definition xcb.h:117
struct _workarea * next
Definition xcb.h:121
int h
Definition xcb.h:116
int mw
Definition xcb.h:117
int primary
Definition xcb.h:108
int y
Definition xcb.h:112
GTimer * time
Definition view.c:290
workarea mon
Definition view.c:131
MenuFlags flags
Definition view.c:127
unsigned long long count
Definition view.c:147
xcb_colormap_t map
Definition xcb.c:108
gboolean display_late_setup(void)
Definition xcb.c:1908
static int take_pointer(xcb_window_t w, int iters)
Definition xcb.c:1527
int monitor_active(workarea *mon)
Definition xcb.c:1004
#define RANDR_PREF_MAJOR_VERSION
Definition xcb.c:77
const char * traditional_name
Definition xcb.c:127
void display_early_cleanup(void)
Definition xcb.c:1943
static int x11_is_extension_present(const char *extension)
Definition xcb.c:549
static gboolean lazy_grab_pointer(G_GNUC_UNUSED gpointer data)
Definition xcb.c:1880
static void x11_workarea_fix_rotation(workarea *w)
Definition xcb.c:444
void display_startup_notification(RofiHelperExecuteContext *context, GSpawnChildSetupFunc *child_setup, gpointer *user_data)
Definition xcb.c:721
cairo_surface_t * x11_helper_get_screenshot_surface(void)
Definition xcb.c:347
static void x11_build_monitor_layout(void)
Definition xcb.c:594
char * window_get_text_prop(xcb_window_t w, xcb_atom_t atom)
Definition xcb.c:387
void display_cleanup(void)
Definition xcb.c:1949
xcb_stuff * xcb
Definition xcb.c:101
static xcb_pixmap_t get_root_pixmap(xcb_connection_t *c, xcb_screen_t *screen, xcb_atom_t atom)
Definition xcb.c:353
static void release_pointer(void)
Definition xcb.c:1587
static bool get_atom_name(xcb_connection_t *conn, xcb_atom_t atom, char **out)
Definition xcb.c:1075
struct _xcb_stuff xcb_int
Definition xcb.c:90
xcb_depth_t * depth
Definition xcb.c:106
static uint32_t * create_kernel(double radius, double deviation, uint32_t *sum2)
Definition xcb.c:149
static int monitor_active_from_winid(xcb_drawable_t id, workarea *mon)
Definition xcb.c:821
static void x11_create_visual_and_colormap(void)
Definition xcb.c:1814
static gboolean x11_button_to_nk_bindings_scroll(guint32 x11_button, NkBindingsScrollAxis *axis, gint32 *steps)
Definition xcb.c:1168
static void monitor_dimensions(int x, int y, workarea *mon)
Definition xcb.c:778
void rofi_xcb_revert_input_focus(void)
Definition xcb.c:1517
cairo_surface_t * x11_helper_get_bg_surface(void)
Definition xcb.c:373
static void x11_lookup_cursors(void)
Definition xcb.c:1857
void rofi_xcb_set_input_focus(xcb_window_t w)
Definition xcb.c:1494
static gboolean lazy_grab_keyboard(G_GNUC_UNUSED gpointer data)
Definition xcb.c:1893
#define INTERSECT(x, y, x1, y1, w1, h1)
Definition xcb.c:82
static workarea * x11_get_monitor_from_output(xcb_randr_output_t out)
Definition xcb.c:459
static void error_trap_pop(G_GNUC_UNUSED SnDisplay *display, xcb_connection_t *xdisplay)
Definition xcb.c:1598
void x11_set_cursor(xcb_window_t window, X11CursorType type)
Definition xcb.c:2005
workarea mon_cache
Definition xcb.c:1001
static void x11_build_monitor_layout_xinerama(void)
Definition xcb.c:563
static xcb_visualtype_t * lookup_visual(xcb_screen_t *s, xcb_visualid_t vis)
Definition xcb.c:131
unsigned int lazy_grab_retry_count_pt
Definition xcb.c:1879
static xcb_visualtype_t * root_visual
Definition xcb.c:112
char * x11_helper_get_window_manager(void)
Definition xcb.c:1626
static void x11_create_frequently_used_atoms(void)
Definition xcb.c:1612
static int monitor_active_from_id_focused(int mon_id, workarea *mon)
Definition xcb.c:851
const struct @161120043102063226230375010005116241233005203005 cursor_names[]
static gboolean x11_button_to_nk_bindings_button(guint32 x11_button, NkBindingsMouseButton *button)
Definition xcb.c:1139
xcb_cursor_t cursors[NUM_CURSORS]
Definition xcb.c:119
static void x11_monitor_free(workarea *m)
Definition xcb.c:426
static void x11_helper_discover_window_manager(void)
Definition xcb.c:1646
static int monitor_get_dimension(int monitor_id, workarea *mon)
Definition xcb.c:763
static int take_keyboard(xcb_window_t w, int iters)
Definition xcb.c:1556
xcb_window_t xcb_stuff_get_root_window(void)
Definition xcb.c:1941
gboolean mon_set
Definition xcb.c:999
unsigned int lazy_grab_retry_count_kb
Definition xcb.c:1877
static void rofi_view_paste(RofiViewState *state, xcb_selection_notify_event_t *xse)
Definition xcb.c:1110
const char * css_name
Definition xcb.c:125
void window_set_atom_prop(xcb_window_t w, xcb_atom_t prop, xcb_atom_t *atoms, int count)
Definition xcb.c:413
const char * netatom_names[]
Definition xcb.c:114
void cairo_image_surface_blur(cairo_surface_t *surface, double radius, double deviation)
Definition xcb.c:177
static int monitor_active_from_id(int mon_id, workarea *mon)
Definition xcb.c:922
static void x11_monitors_free(void)
Definition xcb.c:431
gboolean display_setup(GMainLoop *main_loop, NkBindings *bindings)
Definition xcb.c:1671
void display_dump_monitor_layout(void)
Definition xcb.c:695
static void error_trap_push(G_GNUC_UNUSED SnDisplay *display, G_GNUC_UNUSED xcb_connection_t *xdisplay)
Definition xcb.c:1593
WindowManagerQuirk current_window_manager
Definition xcb.c:85
static int pointer_get(xcb_window_t root, int *x, int *y)
Definition xcb.c:805
#define RANDR_PREF_MINOR_VERSION
Definition xcb.c:79
#define sn_launcher_context_set_application_id
Definition xcb.c:60
static void rofi_key_press_event_handler(xcb_key_press_event_t *xkpe, RofiViewState *state)
Definition xcb.c:1191
static void rofi_key_release_event_handler(xcb_key_release_event_t *xkre, G_GNUC_UNUSED RofiViewState *state)
Definition xcb.c:1211
void xcb_stuff_set_clipboard(char *data)
Definition xcb.c:2017
static int error_trap_depth
Definition xcb.c:1592
static void release_keyboard(void)
Definition xcb.c:1584
static gboolean main_loop_x11_event_handler(xcb_generic_event_t *ev, G_GNUC_UNUSED gpointer user_data)
Definition xcb.c:1439
void x11_disable_decoration(xcb_window_t window)
Definition xcb.c:1981
static void main_loop_x11_event_handler_view(xcb_generic_event_t *event)
Definition xcb.c:1222
xcb_atom_t netatoms[NUM_NETATOMS]
Definition xcb.c:113
xcb_visualtype_t * visual
Definition xcb.c:107
cairo_surface_t * x11_helper_get_screenshot_surface_window(xcb_window_t window, int size)
Definition xcb.c:286
struct _xcb_stuff xcb_stuff
Definition xcb.h:40
#define EWMH_ATOMS(X)
Definition xcb.h:89
#define ATOM_CHAR(x)
Definition xcb.h:86
@ NUM_NETATOMS
Definition xcb.h:95
WindowManagerQuirk
Definition xcb.h:205
@ WM_PANGO_WORKSPACE_NAMES
Definition xcb.h:211
@ WM_DO_NOT_CHANGE_CURRENT_DESKTOP
Definition xcb.h:209
@ WM_EWHM
Definition xcb.h:207
struct _workarea workarea
X11CursorType
Definition xcb.h:184
@ NUM_CURSORS
Definition xcb.h:191