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