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