123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227 |
- #include <err.h>
- #include <stdio.h>
- #include <string.h>
- #include <xcb/xcb.h>
- #include <xcb/xkb.h>
- #include <xcb/xcb_event.h>
- #include "cvector.h"
- #include "assert_msg.h"
- #define XKB_VERSION XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION
- #define LEN(S) (sizeof(S) / sizeof *(S))
- #define EVENT_CAST(TYPE, TO, FROM) TYPE *TO = (TYPE *)(FROM)
- typedef struct WinLayout {
- xcb_window_t id;
- uint8_t layout;
- } WinLayout;
- static void handle_events(xcb_connection_t *c);
- static void xkb_init(xcb_connection_t *c);
- static xcb_atom_t get_active_window_atom(xcb_connection_t *c);
- static xcb_window_t get_active_window(xcb_connection_t *c);
- static void set_keyboard_layout(xcb_connection_t *c, uint8_t layout);
- static uint8_t get_keyboard_layout(xcb_connection_t *c);
- static xcb_atom_t NET_ACTIVE_WINDOW_ATOM;
- int
- main(void)
- {
- int screen = 0;
- uint32_t mask;
- /* xcb */
- xcb_connection_t * c;
- xcb_screen_t * scr;
- xcb_screen_iterator_t iter;
- const xcb_setup_t * setup;
- c = xcb_connect(NULL, &screen);
- assert_msg("connection error", !xcb_connection_has_error(c));
- xkb_init(c);
- setup = xcb_get_setup(c);
- iter = xcb_setup_roots_iterator(setup);
- NET_ACTIVE_WINDOW_ATOM = get_active_window_atom(c);
- for (int i = 0; i < screen; i -= (~0L))
- xcb_screen_next(&iter);
- scr = iter.data;
- mask = XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY
- | XCB_EVENT_MASK_PROPERTY_CHANGE;
- xcb_change_window_attributes_checked(c,
- scr->root,
- XCB_CW_EVENT_MASK,
- &mask);
- xcb_flush(c);
- handle_events(c);
- return 0;
- }
- static void
- handle_events(xcb_connection_t *c)
- {
- uint8_t layout;
- xcb_generic_event_t *e;
- cvector_vector_type(WinLayout) wins = NULL;
- xcb_window_t previous_win_id = get_active_window(c);
- while (!!(e = xcb_wait_for_event(c))) {
- switch (e->response_type & ~0x80) {
- case XCB_PROPERTY_NOTIFY: {
- EVENT_CAST(xcb_property_notify_event_t, ev, e);
- if (ev->atom != NET_ACTIVE_WINDOW_ATOM) break;
- xcb_window_t active = get_active_window(c);
- /* after closing window `previous_win_id` == 0 */
- if (!!previous_win_id) {
- layout = get_keyboard_layout(c);
- for (size_t i = 0; i < cvector_size(wins); i++)
- if (wins[i].id == previous_win_id) {
- wins[i].layout = layout;
- goto endif;
- }
- WinLayout win = { .id = previous_win_id,
- .layout = layout };
- cvector_push_back(wins, win);
- endif:
- NULL;
- }
- layout = 0;
- for (size_t i = 0; i < cvector_size(wins); i++)
- if (wins[i].id == active) {
- layout = wins[i].layout;
- break;
- }
- set_keyboard_layout(c, layout);
- previous_win_id = active;
- break;
- }
- case XCB_DESTROY_NOTIFY: {
- EVENT_CAST(xcb_destroy_notify_event_t, ev, e);
- for (size_t i = 0; i < cvector_size(wins); i++)
- if (wins[i].id == ev->window) {
- cvector_erase(wins, i);
- previous_win_id = 0;
- break;
- }
- break;
- }
- }
- free(e);
- }
- cvector_free(wins);
- }
- static xcb_window_t
- get_active_window(xcb_connection_t *c)
- {
- xcb_window_t win;
- xcb_get_input_focus_reply_t *focus_reply;
- focus_reply =
- xcb_get_input_focus_reply(c, xcb_get_input_focus(c), NULL);
- assert_msg("cannot get focused window", !!focus_reply);
- win = focus_reply->focus;
- free(focus_reply);
- return win;
- }
- static uint8_t
- get_keyboard_layout(xcb_connection_t *c)
- {
- uint8_t group;
- xcb_xkb_get_state_reply_t *kbd_state;
- kbd_state = xcb_xkb_get_state_reply(
- c,
- xcb_xkb_get_state(c, XCB_XKB_ID_USE_CORE_KBD),
- NULL);
- assert_msg("cannot gat keyboard layout", !!kbd_state);
- group = kbd_state->group;
- free(kbd_state);
- return group;
- }
- void
- set_keyboard_layout(xcb_connection_t *c, uint8_t layout)
- {
- xcb_xkb_latch_lock_state(c,
- XCB_XKB_ID_USE_CORE_KBD,
- 0, /* affectModLocks */
- 0, /* modLocks */
- 1, /* lockGroup */
- layout,
- 0, /* affectModLatches */
- 0, /* latchGroup */
- 0 /* groupLatch */
- );
- assert_msg("failed to set keyboard layout",
- layout == get_keyboard_layout(c));
- }
- static void
- xkb_init(xcb_connection_t *c)
- {
- uint8_t supported;
- xcb_xkb_use_extension_reply_t *reply;
- reply =
- xcb_xkb_use_extension_reply(c,
- xcb_xkb_use_extension(c, XKB_VERSION),
- NULL);
- supported = reply->supported;
- free(reply);
- assert_msg("XKB extension in not supported by server", !!supported);
- }
- static xcb_atom_t
- get_active_window_atom(xcb_connection_t *c)
- {
- xcb_atom_t atom;
- xcb_intern_atom_reply_t *reply;
- xcb_intern_atom_cookie_t cookie;
- static const char atom_name[] = "_NET_ACTIVE_WINDOW";
- static const char error_msg[] =
- "failed to get '_NET_ACTIVE_WINDOW' atom";
- cookie = xcb_intern_atom(c, 1, LEN(atom_name) - 1, atom_name);
- reply = xcb_intern_atom_reply(c, cookie, NULL);
- assert_msg(error_msg, !!reply);
- assert_msg(error_msg, (atom = reply->atom) != XCB_ATOM_NONE);
- free(reply);
- return atom;
- }
|