forked from external/yambar
module/xkb: implement indicator state
For now, we only expose caps-, num- and scroll-lock, as booleans.
This commit is contained in:
parent
7bb849bdf4
commit
925dfb736a
1 changed files with 208 additions and 42 deletions
250
modules/xkb.c
250
modules/xkb.c
|
@ -21,32 +21,51 @@ struct layout {
|
|||
};
|
||||
|
||||
struct layouts {
|
||||
ssize_t count;
|
||||
size_t count;
|
||||
struct layout *layouts;
|
||||
};
|
||||
|
||||
struct indicators {
|
||||
size_t count;
|
||||
char **names;
|
||||
};
|
||||
|
||||
struct private {
|
||||
struct particle *label;
|
||||
struct indicators indicators;
|
||||
struct layouts layouts;
|
||||
size_t current;
|
||||
|
||||
bool caps_lock;
|
||||
bool num_lock;
|
||||
bool scroll_lock;
|
||||
};
|
||||
|
||||
static void
|
||||
free_layouts(struct layouts layouts)
|
||||
{
|
||||
for (ssize_t i = 0; i < layouts.count; i++) {
|
||||
for (size_t i = 0; i < layouts.count; i++) {
|
||||
free(layouts.layouts[i].name);
|
||||
free(layouts.layouts[i].symbol);
|
||||
}
|
||||
free(layouts.layouts);
|
||||
}
|
||||
|
||||
static void
|
||||
free_indicators(struct indicators indicators)
|
||||
{
|
||||
for (size_t i = 0; i < indicators.count; i++)
|
||||
free(indicators.names[i]);
|
||||
free(indicators.names);
|
||||
}
|
||||
|
||||
static void
|
||||
destroy(struct module *mod)
|
||||
{
|
||||
struct private *m = mod->private;
|
||||
m->label->destroy(m->label);
|
||||
free_layouts(m->layouts);
|
||||
free_indicators(m->indicators);
|
||||
free(m);
|
||||
module_default_destroy(mod);
|
||||
}
|
||||
|
@ -70,8 +89,11 @@ content(struct module *mod)
|
|||
.tags = (struct tag *[]){
|
||||
tag_new_string(mod, "name", name),
|
||||
tag_new_string(mod, "symbol", symbol),
|
||||
tag_new_bool(mod, "caps_lock", m->caps_lock),
|
||||
tag_new_bool(mod, "num_lock", m->num_lock),
|
||||
tag_new_bool(mod, "scroll_lock", m->scroll_lock),
|
||||
},
|
||||
.count = 2,
|
||||
.count = 5,
|
||||
};
|
||||
|
||||
mtx_unlock(&mod->lock);
|
||||
|
@ -92,8 +114,19 @@ xkb_enable(xcb_connection_t *conn)
|
|||
xcb_xkb_use_extension_reply_t *reply = xcb_xkb_use_extension_reply(
|
||||
conn, cookie, &err);
|
||||
|
||||
assert(err == NULL && "xcb_xkb_use_extension() failed");
|
||||
assert(reply->supported && "XKB extension not supported");
|
||||
if (err != NULL) {
|
||||
LOG_ERR("failed to query for XKB extension: %s", xcb_error(err));
|
||||
free(err);
|
||||
free(reply);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!reply->supported) {
|
||||
LOG_ERR("XKB extension is not supported");
|
||||
free(reply);
|
||||
return false;
|
||||
}
|
||||
|
||||
free(reply);
|
||||
return true;
|
||||
}
|
||||
|
@ -117,25 +150,25 @@ get_xkb_event_base(xcb_connection_t *conn)
|
|||
return reply->first_event;
|
||||
}
|
||||
|
||||
static struct layouts
|
||||
get_layouts(xcb_connection_t *conn)
|
||||
static bool
|
||||
get_layouts_and_indicators(xcb_connection_t *conn, struct layouts *layouts,
|
||||
struct indicators *indicators)
|
||||
{
|
||||
struct layouts ret;
|
||||
xcb_generic_error_t *err;
|
||||
|
||||
xcb_xkb_get_names_cookie_t cookie = xcb_xkb_get_names(
|
||||
conn,
|
||||
XCB_XKB_ID_USE_CORE_KBD,
|
||||
XCB_XKB_NAME_DETAIL_GROUP_NAMES |
|
||||
XCB_XKB_NAME_DETAIL_SYMBOLS);
|
||||
XCB_XKB_NAME_DETAIL_SYMBOLS |
|
||||
XCB_XKB_NAME_DETAIL_INDICATOR_NAMES);
|
||||
|
||||
xcb_xkb_get_names_reply_t *reply = xcb_xkb_get_names_reply(
|
||||
conn, cookie, &err);
|
||||
|
||||
if (err != NULL) {
|
||||
LOG_ERR("failed to get group names and symbols: %s", xcb_error(err));
|
||||
LOG_ERR("failed to get layouts and indicators: %s", xcb_error(err));
|
||||
free(err);
|
||||
return (struct layouts){.count = -1};
|
||||
return false;
|
||||
}
|
||||
|
||||
xcb_xkb_get_names_value_list_t vlist;
|
||||
|
@ -146,47 +179,78 @@ get_layouts(xcb_connection_t *conn)
|
|||
reply->nRadioGroups, reply->which, &vlist);
|
||||
|
||||
/* Number of groups (aka layouts) */
|
||||
ret.count = xcb_xkb_get_names_value_list_groups_length(reply, &vlist);
|
||||
ret.layouts = calloc(ret.count, sizeof(ret.layouts[0]));
|
||||
layouts->count = xcb_xkb_get_names_value_list_groups_length(reply, &vlist);
|
||||
layouts->layouts = calloc(layouts->count, sizeof(layouts->layouts[0]));
|
||||
|
||||
/* Number of indicators */
|
||||
indicators->count = xcb_xkb_get_names_value_list_indicator_names_length(
|
||||
reply, &vlist);
|
||||
indicators->names = calloc(indicators->count, sizeof(indicators->names[0]));
|
||||
|
||||
xcb_get_atom_name_cookie_t symbols_name_cookie = xcb_get_atom_name(
|
||||
conn, vlist.symbolsName);
|
||||
|
||||
xcb_get_atom_name_cookie_t group_name_cookies[ret.count];
|
||||
for (ssize_t i = 0; i < ret.count; i++)
|
||||
xcb_get_atom_name_cookie_t group_name_cookies[layouts->count];
|
||||
for (size_t i = 0; i < layouts->count; i++)
|
||||
group_name_cookies[i] = xcb_get_atom_name(conn, vlist.groups[i]);
|
||||
|
||||
xcb_get_atom_name_cookie_t indicator_cookies[indicators->count];
|
||||
for (size_t i = 0; i < indicators->count; i++)
|
||||
indicator_cookies[i] = xcb_get_atom_name(conn, vlist.indicatorNames[i]);
|
||||
|
||||
char *symbols = NULL;
|
||||
|
||||
/* Get layout short names (e.g. "us") */
|
||||
xcb_get_atom_name_reply_t *atom_name = xcb_get_atom_name_reply(
|
||||
conn, symbols_name_cookie, &err);
|
||||
if (err != NULL) {
|
||||
LOG_ERR("failed to get atom name: %s", xcb_error(err));
|
||||
LOG_ERR("failed to get 'symbols' atom name: %s", xcb_error(err));
|
||||
free(err);
|
||||
goto err;
|
||||
}
|
||||
|
||||
symbols = strndup(xcb_get_atom_name_name(atom_name),
|
||||
xcb_get_atom_name_name_length(atom_name));
|
||||
symbols = strndup(
|
||||
xcb_get_atom_name_name(atom_name),
|
||||
xcb_get_atom_name_name_length(atom_name));
|
||||
LOG_DBG("symbols: %s", symbols);
|
||||
free(atom_name);
|
||||
|
||||
/* Get layout long names (e.g. "English (US)") */
|
||||
for (ssize_t i = 0; i < ret.count; i++) {
|
||||
for (size_t i = 0; i < layouts->count; i++) {
|
||||
atom_name = xcb_get_atom_name_reply(conn, group_name_cookies[i], &err);
|
||||
if (err != NULL) {
|
||||
LOG_ERR("failed to get atom name: %s", xcb_error(err));
|
||||
LOG_ERR("failed to get 'group' atom name: %s", xcb_error(err));
|
||||
free(err);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret.layouts[i].name = strndup(xcb_get_atom_name_name(atom_name),
|
||||
xcb_get_atom_name_name_length(atom_name));
|
||||
layouts->layouts[i].name = strndup(
|
||||
xcb_get_atom_name_name(atom_name),
|
||||
xcb_get_atom_name_name_length(atom_name));
|
||||
|
||||
LOG_DBG("layout #%zd: long name: %s", i, layouts->layouts[i].name);
|
||||
free(atom_name);
|
||||
}
|
||||
|
||||
/* Indicator names e.g. "Caps Lock", "Num Lock" */
|
||||
for (size_t i = 0; i < indicators->count; i++) {
|
||||
atom_name = xcb_get_atom_name_reply(conn, indicator_cookies[i], &err);
|
||||
if (err != NULL) {
|
||||
LOG_ERR("failed to get 'indicator' atom name: %s", xcb_error(err));
|
||||
free(err);
|
||||
goto err;
|
||||
}
|
||||
|
||||
indicators->names[i] = strndup(
|
||||
xcb_get_atom_name_name(atom_name),
|
||||
xcb_get_atom_name_name_length(atom_name));
|
||||
|
||||
LOG_DBG("indicator #%zd: %s", i, indicators->names[i]);
|
||||
free(atom_name);
|
||||
}
|
||||
|
||||
/* e.g. pc+us+inet(evdev)+group(..) */
|
||||
ssize_t layout_idx = 0;
|
||||
size_t layout_idx = 0;
|
||||
for (char *tok_ctx = NULL, *tok = strtok_r(symbols, "+", &tok_ctx);
|
||||
tok != NULL;
|
||||
tok = strtok_r(NULL, "+", &tok_ctx)) {
|
||||
|
@ -206,32 +270,33 @@ get_layouts(xcb_connection_t *conn)
|
|||
if (strcmp(tok, "pc") == 0)
|
||||
continue;
|
||||
|
||||
if (layout_idx >= ret.count) {
|
||||
if (layout_idx >= layouts->count) {
|
||||
LOG_ERR("layout vs group name count mismatch: %zd > %zd",
|
||||
layout_idx + 1, ret.count);
|
||||
layout_idx + 1, layouts->count);
|
||||
goto err;
|
||||
}
|
||||
|
||||
char *sym = strdup(fname);
|
||||
ret.layouts[layout_idx++].symbol = sym;
|
||||
layouts->layouts[layout_idx++].symbol = sym;
|
||||
LOG_DBG("layout #%zd: short name: %s", layout_idx - 1, sym);
|
||||
}
|
||||
|
||||
if (layout_idx != ret.count) {
|
||||
if (layout_idx != layouts->count) {
|
||||
LOG_ERR("layout vs group name count mismatch: %zd != %zd",
|
||||
layout_idx, ret.count);
|
||||
layout_idx, layouts->count);
|
||||
goto err;
|
||||
}
|
||||
|
||||
free(symbols);
|
||||
free(reply);
|
||||
|
||||
return ret;
|
||||
return true;
|
||||
|
||||
err:
|
||||
free(symbols);
|
||||
free(reply);
|
||||
free_layouts(ret);
|
||||
return (struct layouts){.count = -1};
|
||||
free_layouts(*layouts);
|
||||
free_indicators(*indicators);
|
||||
return false;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -255,6 +320,28 @@ get_current_layout(xcb_connection_t *conn)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
get_indicator_state(xcb_connection_t *conn)
|
||||
{
|
||||
xcb_generic_error_t *err;
|
||||
xcb_xkb_get_indicator_state_cookie_t cookie = xcb_xkb_get_indicator_state(
|
||||
conn, XCB_XKB_ID_USE_CORE_KBD);
|
||||
xcb_xkb_get_indicator_state_reply_t *reply = xcb_xkb_get_indicator_state_reply(
|
||||
conn, cookie, &err);
|
||||
|
||||
if (err != NULL) {
|
||||
LOG_ERR("failed to get indicator state: %s", xcb_error(err));
|
||||
free(err);
|
||||
return (uint32_t)-1;
|
||||
}
|
||||
|
||||
uint32_t state = reply->state;
|
||||
LOG_DBG("indicator state: 0x%08x", state);
|
||||
|
||||
free(reply);
|
||||
return state;
|
||||
}
|
||||
|
||||
static bool
|
||||
register_for_events(xcb_connection_t *conn)
|
||||
{
|
||||
|
@ -345,8 +432,9 @@ event_loop(struct module *mod, xcb_connection_t *conn, int xkb_event_base)
|
|||
break;
|
||||
}
|
||||
|
||||
struct layouts layouts = get_layouts(conn);
|
||||
if (layouts.count == -1) {
|
||||
struct layouts layouts;
|
||||
struct indicators indicators;
|
||||
if (!get_layouts_and_indicators(conn, &layouts, &indicators)) {
|
||||
has_error = true;
|
||||
break;
|
||||
}
|
||||
|
@ -354,15 +442,15 @@ event_loop(struct module *mod, xcb_connection_t *conn, int xkb_event_base)
|
|||
if (current < layouts.count) {
|
||||
mtx_lock(&mod->lock);
|
||||
free_layouts(m->layouts);
|
||||
m->layouts = layouts;
|
||||
m->current = current;
|
||||
m->layouts = layouts;
|
||||
m->indicators = indicators;
|
||||
mtx_unlock(&mod->lock);
|
||||
bar->refresh(bar);
|
||||
} else {
|
||||
/* Can happen while transitioning to a new map */
|
||||
mtx_lock(&mod->lock);
|
||||
free_layouts(layouts);
|
||||
mtx_unlock(&mod->lock);
|
||||
free_indicators(indicators);
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -386,10 +474,50 @@ event_loop(struct module *mod, xcb_connection_t *conn, int xkb_event_base)
|
|||
LOG_WARN("map event unimplemented");
|
||||
break;
|
||||
|
||||
case XCB_XKB_INDICATOR_STATE_NOTIFY:
|
||||
LOG_WARN("indicator state event unimplemented");
|
||||
case XCB_XKB_INDICATOR_STATE_NOTIFY: {
|
||||
const xcb_xkb_indicator_state_notify_event_t *evt =
|
||||
(const xcb_xkb_indicator_state_notify_event_t *)_evt;
|
||||
|
||||
#if 0
|
||||
size_t idx = __builtin_ctz(evt->stateChanged);
|
||||
LOG_ERR("%zu", idx);
|
||||
if (idx < m->indicators.count)
|
||||
LOG_ERR("%s", m->indicators.names[idx]);
|
||||
#endif
|
||||
|
||||
/* TODO: bit count evt->stateChanged instead */
|
||||
bool need_refresh = false;
|
||||
for (size_t i = 0; i < m->indicators.count; i++) {
|
||||
bool changed = (evt->stateChanged >> i) & 1;
|
||||
if (!changed)
|
||||
continue;
|
||||
|
||||
bool enabled = (evt->state >> i) & 1;
|
||||
LOG_DBG("%s: %s", m->indicators.names[i],
|
||||
enabled ? "enabled" : "disabled");
|
||||
|
||||
const char *name = m->indicators.names[i];
|
||||
bool is_caps = strcasecmp(name, "caps lock") == 0;
|
||||
bool is_num = strcasecmp(name, "num lock") == 0;
|
||||
|
||||
if (is_caps || is_num) {
|
||||
mtx_lock(&mod->lock);
|
||||
|
||||
if (is_caps)
|
||||
m->caps_lock = enabled;
|
||||
else if (is_num)
|
||||
m->num_lock = enabled;
|
||||
|
||||
mtx_unlock(&mod->lock);
|
||||
need_refresh = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (need_refresh)
|
||||
bar->refresh(bar);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(_evt);
|
||||
}
|
||||
|
@ -417,19 +545,54 @@ talk_to_xkb(struct module *mod, xcb_connection_t *conn)
|
|||
if (current == -1)
|
||||
return false;
|
||||
|
||||
struct layouts layouts = get_layouts(conn);
|
||||
if (layouts.count == -1)
|
||||
/* Bitmask, one bit for every indicator available */
|
||||
uint32_t indicator_state = get_indicator_state(conn);
|
||||
if (indicator_state == (uint32_t)-1)
|
||||
return false;
|
||||
|
||||
struct layouts layouts;
|
||||
struct indicators indicators;
|
||||
if (!get_layouts_and_indicators(conn, &layouts, &indicators))
|
||||
return false;
|
||||
|
||||
if (current >= layouts.count) {
|
||||
LOG_ERR("current layout index: %d >= %zd", current, layouts.count);
|
||||
free_layouts(layouts);
|
||||
free_indicators(indicators);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool caps_lock = false, num_lock = false, scroll_lock = false;
|
||||
|
||||
/* Lookup initial state of caps-, num- and scroll lock */
|
||||
for (size_t i = 0; i < indicators.count; i++) {
|
||||
const char *name = indicators.names[i];
|
||||
bool enabled = (indicator_state >> i) & 1;
|
||||
|
||||
bool is_caps = strcasecmp(name, "caps lock") == 0;
|
||||
bool is_num = strcasecmp(name, "num lock") == 0;
|
||||
bool is_scroll = strcasecmp(name, "scroll lock") == 0;
|
||||
|
||||
if (!(is_caps || is_num || is_scroll))
|
||||
continue;
|
||||
|
||||
if (is_caps)
|
||||
caps_lock = enabled;
|
||||
else if (is_num)
|
||||
num_lock = enabled;
|
||||
else if (is_scroll)
|
||||
scroll_lock = enabled;
|
||||
|
||||
LOG_DBG("%s: %s", name, enabled ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
mtx_lock(&mod->lock);
|
||||
m->layouts = layouts;
|
||||
m->current = current;
|
||||
m->indicators = indicators;
|
||||
m->caps_lock = caps_lock;
|
||||
m->num_lock = num_lock;
|
||||
m->scroll_lock = scroll_lock;
|
||||
mtx_unlock(&mod->lock);
|
||||
mod->bar->refresh(mod->bar);
|
||||
|
||||
|
@ -459,6 +622,9 @@ xkb_new(struct particle *label)
|
|||
m->current = 0;
|
||||
m->layouts.count = 0;
|
||||
m->layouts.layouts = NULL;
|
||||
m->caps_lock = false;
|
||||
m->num_lock = false;
|
||||
m->scroll_lock = false;
|
||||
|
||||
struct module *mod = module_common_new();
|
||||
mod->private = m;
|
||||
|
|
Loading…
Add table
Reference in a new issue