i3lock-color

The world's most popular non-default computer lockscreen.
Index Commits Files Refs README LICENSE
xcb.c (20220B)
   1 /*
   2  * vim:ts=4:sw=4:expandtab
   3  *
   4  * © 2010 Michael Stapelberg
   5  * © 2021 Raymond Li
   6  *
   7  * xcb.c: contains all functions which use XCB to talk to X11. Mostly wrappers
   8  *        around the rather complicated/ugly parts of the XCB API.
   9  *
  10  */
  11 #include <xcb/xcb.h>
  12 #include <xcb/xcb_image.h>
  13 #include <xcb/xcb_atom.h>
  14 #include <xcb/xcb_aux.h>
  15 #include <xcb/composite.h>
  16 #include <xcb/xkb.h>
  17 #include <xkbcommon/xkbcommon.h>
  18 #include <xkbcommon/xkbcommon-x11.h>
  19 #include <stdio.h>
  20 #include <stdlib.h>
  21 #include <stdbool.h>
  22 #include <string.h>
  23 #include <unistd.h>
  24 #include <assert.h>
  25 #include <err.h>
  26 #include <time.h>
  27 #include <sys/time.h>
  28 
  29 #include "cursors.h"
  30 #include "i3lock.h"
  31 #include "xcb.h"
  32 #include "unlock_indicator.h"
  33 
  34 extern auth_state_t auth_state;
  35 extern bool composite;
  36 extern bool debug_mode;
  37 
  38 xcb_connection_t *conn;
  39 xcb_screen_t *screen;
  40 
  41 static xcb_atom_t _NET_WM_BYPASS_COMPOSITOR = XCB_NONE;
  42 void _init_net_wm_bypass_compositor(xcb_connection_t *conn) {
  43     if (_NET_WM_BYPASS_COMPOSITOR != XCB_NONE) {
  44         /* already initialized */
  45         return;
  46     }
  47     xcb_generic_error_t *err;
  48     xcb_intern_atom_reply_t *atom_reply = xcb_intern_atom_reply(
  49         conn,
  50         xcb_intern_atom(conn, 0, strlen("_NET_WM_BYPASS_COMPOSITOR"), "_NET_WM_BYPASS_COMPOSITOR"),
  51         &err);
  52     if (atom_reply == NULL) {
  53         fprintf(stderr, "X11 Error %d\n", err->error_code);
  54         free(err);
  55         return;
  56     }
  57     _NET_WM_BYPASS_COMPOSITOR = atom_reply->atom;
  58     free(atom_reply);
  59 }
  60 
  61 #define curs_invisible_width 8
  62 #define curs_invisible_height 8
  63 
  64 static unsigned char curs_invisible_bits[] = {
  65     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  66 
  67 #define curs_windows_width 11
  68 #define curs_windows_height 19
  69 
  70 static unsigned char curs_windows_bits[] = {
  71     0xfe, 0x07, 0xfc, 0x07, 0xfa, 0x07, 0xf6, 0x07, 0xee, 0x07, 0xde, 0x07,
  72     0xbe, 0x07, 0x7e, 0x07, 0xfe, 0x06, 0xfe, 0x05, 0x3e, 0x00, 0xb6, 0x07,
  73     0x6a, 0x07, 0x6c, 0x07, 0xde, 0x06, 0xdf, 0x06, 0xbf, 0x05, 0xbf, 0x05,
  74     0x7f, 0x06};
  75 
  76 #define mask_windows_width 11
  77 #define mask_windows_height 19
  78 
  79 static unsigned char mask_windows_bits[] = {
  80     0x01, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x3f, 0x00,
  81     0x7f, 0x00, 0xff, 0x00, 0xff, 0x01, 0xff, 0x03, 0xff, 0x07, 0x7f, 0x00,
  82     0xf7, 0x00, 0xf3, 0x00, 0xe1, 0x01, 0xe0, 0x01, 0xc0, 0x03, 0xc0, 0x03,
  83     0x80, 0x01};
  84 
  85 static uint32_t get_colorpixel(char *hex) {
  86     char strgroups[4][3] = {{hex[0], hex[1], '\0'},
  87                             {hex[2], hex[3], '\0'},
  88                             {hex[4], hex[5], '\0'},
  89                             {hex[6], hex[4], '\0'}};
  90     uint32_t rgb16[4] = {(strtol(strgroups[0], NULL, 16)),
  91                          (strtol(strgroups[1], NULL, 16)),
  92                          (strtol(strgroups[2], NULL, 16)),
  93                          (strtol(strgroups[3], NULL, 16))};
  94 
  95     return (rgb16[3] << 24) + (rgb16[0] << 16) + (rgb16[1] << 8) + rgb16[2];
  96 }
  97 
  98 xcb_visualtype_t *get_visualtype_by_depth(uint16_t depth, xcb_screen_t *root_screen) {
  99     xcb_depth_iterator_t depth_iter;
 100 
 101     depth_iter = xcb_screen_allowed_depths_iterator(root_screen);
 102     for (; depth_iter.rem; xcb_depth_next(&depth_iter)) {
 103         if (depth_iter.data->depth != depth)
 104             continue;
 105 
 106         xcb_visualtype_iterator_t visual_iter;
 107 
 108         visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
 109         if (!visual_iter.rem)
 110             continue;
 111         return visual_iter.data;
 112     }
 113     return NULL;
 114 }
 115 
 116 
 117 xcb_visualtype_t *get_root_visual_type(xcb_screen_t *screen) {
 118     xcb_visualtype_t *visual_type = NULL;
 119     xcb_depth_iterator_t depth_iter;
 120     xcb_visualtype_iterator_t visual_iter;
 121 
 122     for (depth_iter = xcb_screen_allowed_depths_iterator(screen);
 123          depth_iter.rem;
 124          xcb_depth_next(&depth_iter)) {
 125         for (visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
 126              visual_iter.rem;
 127              xcb_visualtype_next(&visual_iter)) {
 128             if (screen->root_visual != visual_iter.data->visual_id)
 129                 continue;
 130 
 131             visual_type = visual_iter.data;
 132             return visual_type;
 133         }
 134     }
 135 
 136     return NULL;
 137 }
 138 
 139 xcb_pixmap_t create_bg_pixmap(xcb_connection_t *conn, xcb_drawable_t win, u_int32_t *resolution, char *color) {
 140     xcb_pixmap_t bg_pixmap = xcb_generate_id(conn);
 141     xcb_create_pixmap(conn, 32, bg_pixmap, win, resolution[0], resolution[1]);
 142 
 143     /* Generate a Graphics Context and fill the pixmap with background color
 144      * (for images that are smaller than your screen) */
 145     xcb_gcontext_t gc = xcb_generate_id(conn);
 146     uint32_t values[] = {get_colorpixel(color)};
 147     xcb_create_gc(conn, gc, bg_pixmap, XCB_GC_FOREGROUND, values);
 148     xcb_rectangle_t rect = {0, 0, resolution[0], resolution[1]};
 149     xcb_poly_fill_rectangle(conn, bg_pixmap, gc, 1, &rect);
 150     xcb_free_gc(conn, gc);
 151 
 152     return bg_pixmap;
 153 }
 154 
 155 xcb_window_t open_fullscreen_window(xcb_connection_t *conn, xcb_screen_t *scr, char *color) {
 156     uint32_t mask = 0;
 157     uint32_t values[5];
 158     xcb_window_t win = xcb_generate_id(conn);
 159     xcb_window_t parent_win = scr->root;
 160 
 161     if (composite) {
 162         /* Check whether the composite extension is available */
 163         const xcb_query_extension_reply_t *extension_query = NULL;
 164         xcb_generic_error_t *error = NULL;
 165         xcb_composite_get_overlay_window_cookie_t cookie;
 166         xcb_composite_get_overlay_window_reply_t *composite_reply = NULL;
 167 
 168         extension_query = xcb_get_extension_data(conn, &xcb_composite_id);
 169         if (extension_query && extension_query->present) {
 170             /* When composition is used, we need to use the composite overlay
 171              * window instead of the normal root window to be able to cover
 172              * composited windows */
 173             cookie = xcb_composite_get_overlay_window(conn, scr->root);
 174             composite_reply = xcb_composite_get_overlay_window_reply(conn, cookie, &error);
 175 
 176             if (!error && composite_reply) {
 177                 parent_win = composite_reply->overlay_win;
 178             }
 179 
 180             free(composite_reply);
 181             free(error);
 182         }
 183     }
 184 
 185 
 186     xcb_visualid_t visual = get_visualtype_by_depth(32, scr)->visual_id;
 187     xcb_colormap_t win_colormap = xcb_generate_id(conn);
 188     xcb_create_colormap(conn, XCB_COLORMAP_ALLOC_NONE, win_colormap, scr->root, visual);
 189 
 190     mask |= XCB_CW_BACK_PIXEL;
 191     values[0] = get_colorpixel(color);
 192 
 193     mask |= XCB_CW_BORDER_PIXEL;
 194     values[1] = 0x00000000;
 195 
 196     mask |= XCB_CW_OVERRIDE_REDIRECT;
 197     values[2] = 1;
 198 
 199     mask |= XCB_CW_EVENT_MASK;
 200     values[3] = XCB_EVENT_MASK_EXPOSURE |
 201                 XCB_EVENT_MASK_KEY_PRESS |
 202                 XCB_EVENT_MASK_KEY_RELEASE |
 203                 XCB_EVENT_MASK_VISIBILITY_CHANGE |
 204                 XCB_EVENT_MASK_STRUCTURE_NOTIFY;
 205 
 206     mask |= XCB_CW_COLORMAP;
 207     values[4] = win_colormap;
 208 
 209     xcb_create_window(conn,
 210                       32,
 211                       win, /* the window id */
 212                       parent_win,
 213                       0, 0,
 214                       scr->width_in_pixels,
 215                       scr->height_in_pixels, /* dimensions */
 216                       0,                     /* border = 0, we draw our own */
 217                       XCB_WINDOW_CLASS_INPUT_OUTPUT,
 218                       visual, /* copy visual from parent */
 219                       mask,
 220                       values);
 221 
 222     char *name = "i3lock";
 223     xcb_change_property(conn,
 224                         XCB_PROP_MODE_REPLACE,
 225                         win,
 226                         XCB_ATOM_WM_NAME,
 227                         XCB_ATOM_STRING,
 228                         8,
 229                         strlen(name),
 230                         name);
 231 
 232     xcb_change_property(conn,
 233                         XCB_PROP_MODE_REPLACE,
 234                         win,
 235                         XCB_ATOM_WM_CLASS,
 236                         XCB_ATOM_STRING,
 237                         8,
 238                         2 * (strlen("i3lock") + 1),
 239                         "i3lock\0i3lock\0");
 240 
 241     const uint32_t bypass_compositor = 1; /* disable compositing */
 242     _init_net_wm_bypass_compositor(conn);
 243     xcb_change_property(conn,
 244                         XCB_PROP_MODE_REPLACE,
 245                         win,
 246                         _NET_WM_BYPASS_COMPOSITOR,
 247                         XCB_ATOM_CARDINAL,
 248                         32,
 249                         1,
 250                         &bypass_compositor);
 251 
 252     /* Map the window (= make it visible) */
 253     xcb_map_window(conn, win);
 254 
 255     /* Raise window (put it on top) */
 256     values[0] = XCB_STACK_MODE_ABOVE;
 257     xcb_configure_window(conn, win, XCB_CONFIG_WINDOW_STACK_MODE, values);
 258 
 259     /* Ensure that the window is created and set up before returning */
 260     xcb_aux_sync(conn);
 261 
 262     return win;
 263 }
 264 
 265 /*
 266  * Repeatedly tries to grab pointer and keyboard (up to the specified number of
 267  * tries).
 268  *
 269  * Returns true if the grab succeeded, false if not.
 270  *
 271  */
 272 bool grab_pointer_and_keyboard(xcb_connection_t *conn, xcb_screen_t *screen, xcb_cursor_t cursor, int tries) {
 273     xcb_grab_pointer_cookie_t pcookie;
 274     xcb_grab_pointer_reply_t *preply;
 275 
 276     xcb_grab_keyboard_cookie_t kcookie;
 277     xcb_grab_keyboard_reply_t *kreply;
 278 
 279     const suseconds_t screen_redraw_timeout = 100000; /* 100ms */
 280 
 281     /* Using few variables to trigger a redraw_screen() if too many tries */
 282     bool redrawn = false;
 283     struct timeval start;
 284     if (gettimeofday(&start, NULL) == -1) {
 285         err(EXIT_FAILURE, "gettimeofday");
 286     }
 287 
 288     while (tries-- > 0) {
 289         pcookie = xcb_grab_pointer(
 290             conn,
 291             false,               /* get all pointer events specified by the following mask */
 292             screen->root,        /* grab the root window */
 293             XCB_NONE,            /* which events to let through */
 294             XCB_GRAB_MODE_ASYNC, /* pointer events should continue as normal */
 295             XCB_GRAB_MODE_ASYNC, /* keyboard mode */
 296             XCB_NONE,            /* confine_to = in which window should the cursor stay */
 297             cursor,              /* we change the cursor to whatever the user wanted */
 298             XCB_CURRENT_TIME);
 299 
 300         if ((preply = xcb_grab_pointer_reply(conn, pcookie, NULL)) &&
 301             preply->status == XCB_GRAB_STATUS_SUCCESS) {
 302             free(preply);
 303             break;
 304         }
 305 
 306         /* In case the grab failed, we still need to free the reply */
 307         free(preply);
 308 
 309         /* Make this quite a bit slower */
 310         usleep(50);
 311 
 312         struct timeval now;
 313         if (gettimeofday(&now, NULL) == -1) {
 314             err(EXIT_FAILURE, "gettimeofday");
 315         }
 316 
 317         struct timeval elapsed;
 318         timersub(&now, &start, &elapsed);
 319 
 320         if (!redrawn &&
 321             (tries % 100) == 0 &&
 322             elapsed.tv_usec >= screen_redraw_timeout) {
 323             redraw_screen();
 324             redrawn = true;
 325         }
 326     }
 327 
 328     while (tries-- > 0) {
 329         kcookie = xcb_grab_keyboard(
 330             conn,
 331             true,         /* report events */
 332             screen->root, /* grab the root window */
 333             XCB_CURRENT_TIME,
 334             XCB_GRAB_MODE_ASYNC, /* process events as normal, do not require sync */
 335             XCB_GRAB_MODE_ASYNC);
 336 
 337         if ((kreply = xcb_grab_keyboard_reply(conn, kcookie, NULL)) &&
 338             kreply->status == XCB_GRAB_STATUS_SUCCESS) {
 339             free(kreply);
 340             break;
 341         }
 342 
 343         /* In case the grab failed, we still need to free the reply */
 344         free(kreply);
 345 
 346         /* Make this quite a bit slower */
 347         usleep(50);
 348 
 349         struct timeval now;
 350         if (gettimeofday(&now, NULL) == -1) {
 351             err(EXIT_FAILURE, "gettimeofday");
 352         }
 353 
 354         struct timeval elapsed;
 355         timersub(&now, &start, &elapsed);
 356 
 357         /* Trigger a screen redraw if 100ms elapsed */
 358         if (!redrawn &&
 359             (tries % 100) == 0 &&
 360             elapsed.tv_usec >= screen_redraw_timeout) {
 361             redraw_screen();
 362             redrawn = true;
 363         }
 364     }
 365 
 366     return (tries > 0);
 367 }
 368 
 369 xcb_cursor_t create_cursor(xcb_connection_t *conn, xcb_screen_t *screen, xcb_window_t win, int choice) {
 370     xcb_pixmap_t bitmap;
 371     xcb_pixmap_t mask;
 372     xcb_cursor_t cursor;
 373 
 374     unsigned char *curs_bits;
 375     unsigned char *mask_bits;
 376     int curs_w, curs_h;
 377 
 378     switch (choice) {
 379         case CURS_NONE:
 380             curs_bits = curs_invisible_bits;
 381             mask_bits = curs_invisible_bits;
 382             curs_w = curs_invisible_width;
 383             curs_h = curs_invisible_height;
 384             break;
 385         case CURS_WIN:
 386             curs_bits = curs_windows_bits;
 387             mask_bits = mask_windows_bits;
 388             curs_w = curs_windows_width;
 389             curs_h = curs_windows_height;
 390             break;
 391         case CURS_DEFAULT:
 392         default:
 393             return XCB_NONE; /* XCB_NONE is xcb's way of saying "don't change the cursor" */
 394     }
 395 
 396     bitmap = xcb_create_pixmap_from_bitmap_data(conn,
 397                                                 win,
 398                                                 curs_bits,
 399                                                 curs_w,
 400                                                 curs_h,
 401                                                 1,
 402                                                 screen->white_pixel,
 403                                                 screen->black_pixel,
 404                                                 NULL);
 405 
 406     mask = xcb_create_pixmap_from_bitmap_data(conn,
 407                                               win,
 408                                               mask_bits,
 409                                               curs_w,
 410                                               curs_h,
 411                                               1,
 412                                               screen->white_pixel,
 413                                               screen->black_pixel,
 414                                               NULL);
 415 
 416     cursor = xcb_generate_id(conn);
 417 
 418     xcb_create_cursor(conn,
 419                       cursor,
 420                       bitmap,
 421                       mask,
 422                       65535, 65535, 65535,
 423                       0, 0, 0,
 424                       0, 0);
 425 
 426     xcb_free_pixmap(conn, bitmap);
 427     xcb_free_pixmap(conn, mask);
 428 
 429     return cursor;
 430 }
 431 
 432 static xcb_atom_t _NET_ACTIVE_WINDOW = XCB_NONE;
 433 void _init_net_active_window(xcb_connection_t *conn) {
 434     if (_NET_ACTIVE_WINDOW != XCB_NONE) {
 435         /* already initialized */
 436         return;
 437     }
 438     xcb_generic_error_t *err;
 439     xcb_intern_atom_reply_t *atom_reply = xcb_intern_atom_reply(
 440         conn,
 441         xcb_intern_atom(conn, 0, strlen("_NET_ACTIVE_WINDOW"), "_NET_ACTIVE_WINDOW"),
 442         &err);
 443     if (atom_reply == NULL) {
 444         fprintf(stderr, "X11 Error %d\n", err->error_code);
 445         free(err);
 446         return;
 447     }
 448     _NET_ACTIVE_WINDOW = atom_reply->atom;
 449     free(atom_reply);
 450 }
 451 
 452 xcb_window_t find_focused_window(xcb_connection_t *conn, const xcb_window_t root) {
 453     xcb_window_t result = XCB_NONE;
 454 
 455     _init_net_active_window(conn);
 456 
 457     xcb_get_property_reply_t *prop_reply = xcb_get_property_reply(
 458         conn,
 459         xcb_get_property_unchecked(
 460             conn, false, root, _NET_ACTIVE_WINDOW, XCB_GET_PROPERTY_TYPE_ANY, 0, 1 /* word */),
 461         NULL);
 462     if (prop_reply == NULL) {
 463         goto out;
 464     }
 465     if (xcb_get_property_value_length(prop_reply) == 0) {
 466         goto out_prop;
 467     }
 468     if (prop_reply->type != XCB_ATOM_WINDOW) {
 469         goto out_prop;
 470     }
 471 
 472     result = *((xcb_window_t *)xcb_get_property_value(prop_reply));
 473 
 474 out_prop:
 475     free(prop_reply);
 476 out:
 477     return result;
 478 }
 479 
 480 void set_focused_window(xcb_connection_t *conn, const xcb_window_t root, const xcb_window_t window) {
 481     xcb_client_message_event_t ev;
 482     memset(&ev, '\0', sizeof(xcb_client_message_event_t));
 483 
 484     _init_net_active_window(conn);
 485 
 486     ev.response_type = XCB_CLIENT_MESSAGE;
 487     ev.window = window;
 488     ev.type = _NET_ACTIVE_WINDOW;
 489     ev.format = 32;
 490     ev.data.data32[0] = 2; /* 2 = pager */
 491 
 492     xcb_send_event(conn, false, root, XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (char *)&ev);
 493     xcb_flush(conn);
 494 }
 495 
 496 xcb_pixmap_t capture_bg_pixmap(xcb_connection_t *conn, xcb_screen_t *scr, u_int32_t * resolution) {
 497     xcb_pixmap_t bg_pixmap = xcb_generate_id(conn);
 498     xcb_create_pixmap(conn, scr->root_depth, bg_pixmap, scr->root, resolution[0], resolution[1]);
 499     xcb_gcontext_t gc = xcb_generate_id(conn);
 500     uint32_t values[] = { scr->black_pixel, 1};
 501     xcb_create_gc(conn, gc, bg_pixmap, XCB_GC_FOREGROUND | XCB_GC_SUBWINDOW_MODE, values);
 502     xcb_rectangle_t rect = { 0, 0, resolution[0], resolution[1] };
 503     xcb_poly_fill_rectangle(conn, bg_pixmap, gc, 1, &rect);
 504     xcb_copy_area(conn, scr->root, bg_pixmap, gc, 0, 0, 0, 0, resolution[0], resolution[1]);
 505     xcb_flush(conn);
 506     xcb_free_gc(conn, gc);
 507     return bg_pixmap;
 508 }
 509 
 510 static char * get_atom_name(xcb_connection_t* conn, xcb_atom_t atom) {
 511     xcb_get_atom_name_reply_t *reply = NULL;
 512     char *name;
 513     int length;
 514     char* answer = NULL;
 515 
 516     if (atom == 0)
 517         return "<empty>";
 518 
 519         xcb_get_atom_name_cookie_t cookie;
 520         xcb_generic_error_t *error = NULL;
 521 
 522         cookie = xcb_get_atom_name(conn, atom);
 523 
 524         reply = xcb_get_atom_name_reply(conn, cookie, &error);
 525         if (!reply || error)
 526                 return "<invalid>";
 527 
 528     length = xcb_get_atom_name_name_length(reply);
 529     name = xcb_get_atom_name_name(reply);
 530 
 531     answer = malloc(sizeof(char) * (length + 1));
 532     strncpy(answer, name, length);
 533     answer[length] = '\0';
 534     free(error);
 535     free(reply);
 536     return answer;
 537 }
 538 
 539 
 540 uint8_t xcb_get_key_group_index(xcb_connection_t *conn) {
 541     xcb_xkb_get_state_cookie_t cookie;
 542     xcb_xkb_get_state_reply_t* reply = NULL;
 543     cookie = xcb_xkb_get_state(conn, XCB_XKB_ID_USE_CORE_KBD);
 544     reply = xcb_xkb_get_state_reply(conn, cookie, NULL);
 545     uint8_t ans = reply->group;
 546     free(reply);
 547     return ans;
 548 }
 549 
 550 
 551 char* xcb_get_key_group_names(xcb_connection_t *conn) {
 552     uint8_t xkb_base_event;
 553     uint8_t xkb_base_error;
 554     if (xkb_x11_setup_xkb_extension(conn,
 555                                     XKB_X11_MIN_MAJOR_XKB_VERSION,
 556                                     XKB_X11_MIN_MINOR_XKB_VERSION,
 557                                     0,
 558                                     NULL,
 559                                     NULL,
 560                                     &xkb_base_event,
 561                                     &xkb_base_error) != 1)
 562     errx(EXIT_FAILURE, "Could not setup XKB extension.");
 563 
 564 
 565     xcb_xkb_get_names_reply_t *reply = NULL;
 566 
 567 
 568     xcb_generic_error_t *error = NULL;
 569     xcb_xkb_get_names_cookie_t cookie;
 570 
 571     cookie = xcb_xkb_get_names(conn,
 572                                  XCB_XKB_ID_USE_CORE_KBD,
 573                                  all_name_details);
 574 
 575     reply = xcb_xkb_get_names_reply(conn, cookie, &error);
 576     if (!reply || error)
 577             errx(1, "couldn't get reply for get_names");
 578 
 579     xcb_xkb_get_names_value_list_t list;
 580 
 581     void *buffer;
 582 
 583     buffer = xcb_xkb_get_names_value_list(reply);
 584     xcb_xkb_get_names_value_list_unpack(buffer,
 585                                         reply->nTypes,
 586                                         reply->indicators,
 587                                         reply->virtualMods,
 588                                         reply->groupNames,
 589                                         reply->nKeys,
 590                                         reply->nKeyAliases,
 591                                         reply->nRadioGroups,
 592                                         reply->which,
 593                                         &list);
 594 
 595     /* dump group names. */
 596 
 597     int length;
 598     xcb_atom_t *iter;
 599     uint8_t index;
 600     char* answer = NULL;
 601     length = xcb_xkb_get_names_value_list_groups_length(reply, &list);
 602     iter = xcb_xkb_get_names_value_list_groups(&list);
 603     index = xcb_get_key_group_index(conn);
 604 
 605     for (int i = 0; i < length; i++) {
 606             xcb_atom_t group_name = *iter;
 607             char* name = get_atom_name(conn, group_name);
 608             DEBUG("group_name %d: %s\n", i, name);
 609             if (i == index) {
 610                 answer = name;
 611             } else {
 612                 free(name);
 613             }
 614             iter++;
 615     }
 616     free(reply);
 617     free(error);
 618     return answer;
 619 }