#include "x11/connection.hpp" #include "errors.hpp" #include "utils/string.hpp" #include "x11/xkb.hpp" POLYBAR_NS /** * Get indicator name */ const keyboard::indicator& keyboard::get(const indicator::type& i) const { return indicators.at(i); } /** * Update indicator states */ void keyboard::set(uint32_t state) { for (auto& i : indicators) { i.second.enabled = state & i.second.mask; } } /** * Get state for the given class */ bool keyboard::on(const indicator::type& i) const { return indicators.at(i).enabled; } /** * Set current group number */ void keyboard::current(uint8_t group) { current_group = group; } /** * Get current group number */ uint8_t keyboard::current() const { return current_group; } /** * Get current group name */ const string keyboard::group_name(size_t index) const { if (!layouts.empty() && index < layouts.size()) { return layouts[index].group_name; } return ""; } /** * Get current layout name */ const string keyboard::layout_name(size_t index) const { if (index >= layouts.size() || index >= layouts[index].symbols.size()) { return ""; } return layouts[index].symbols[index]; } /** * Get indicator name */ const string keyboard::indicator_name(const indicator::type& i) const { return indicators.at(i).name; } namespace xkb_util { /** * Get current group number */ uint8_t get_current_group(connection& conn, xcb_xkb_device_spec_t device) { auto reply = xcb_xkb_get_state_reply(conn, xcb_xkb_get_state(conn, device), nullptr); return reply ? reply->group : 0; } /** * Get keyboard layouts */ vector get_layouts(connection& conn, xcb_xkb_device_spec_t device) { auto mask = XCB_XKB_NAME_DETAIL_GROUP_NAMES | XCB_XKB_NAME_DETAIL_SYMBOLS; auto reply = xcb_xkb_get_names_reply(conn, xcb_xkb_get_names(conn, device, mask), nullptr); xcb_xkb_get_names_value_list_t values; xcb_xkb_get_names_value_list_unpack(xcb_xkb_get_names_value_list(reply), reply->nTypes, reply->indicators, reply->virtualMods, reply->groupNames, reply->nKeys, reply->nKeyAliases, reply->nRadioGroups, reply->which, &values); auto len = xcb_xkb_get_names_value_list_groups_length(reply, &values); free(reply); vector replies; for (int i = 0; i < len; i++) { replies.emplace_back(xpp::x::get_atom_name(conn, values.groups[i])); } vector results; for (const auto& reply : replies) { vector sym_names; for (auto&& sym : string_util::split(conn.get_atom_name(values.symbolsName).name(), '+')) { if (!(sym = parse_layout_symbol(move(sym))).empty()) { sym_names.emplace_back(move(sym)); } } results.emplace_back(keyboard::layout{static_cast(reply).name(), sym_names}); } return results; } /** * Get keyboard indicators */ map get_indicators(connection& conn, xcb_xkb_device_spec_t device) { auto mask = XCB_XKB_NAME_DETAIL_INDICATOR_NAMES; auto reply = xcb_xkb_get_names_reply(conn, xcb_xkb_get_names(conn, device, mask), nullptr); xcb_xkb_get_names_value_list_t values; xcb_xkb_get_names_value_list_unpack(xcb_xkb_get_names_value_list(reply), reply->nTypes, reply->indicators, reply->virtualMods, reply->groupNames, reply->nKeys, reply->nKeyAliases, reply->nRadioGroups, reply->which, &values); auto len = xcb_xkb_get_names_value_list_indicator_names_length(reply, &values); free(reply); map entries; for (int i = 0; i < len; i++) { entries.emplace(values.indicatorNames[i], xpp::x::get_atom_name(conn, values.indicatorNames[i])); } map results; for (const auto& entry : entries) { auto name = static_cast(entry.second).name(); auto type = keyboard::indicator::type::NONE; if (string_util::compare(name, "caps lock")) { type = keyboard::indicator::type::CAPS_LOCK; } else if (string_util::compare(name, "num lock")) { type = keyboard::indicator::type::NUM_LOCK; } else { continue; } auto data = conn.xkb().get_named_indicator(device, 0, 0, entry.first); auto mask = (*conn.xkb().get_indicator_map(device, 1 << data->ndx).maps().begin()).mods; auto enabled = static_cast(data->on); results.emplace(type, keyboard::indicator{entry.first, mask, name, enabled}); } return results; } /** * Parse symbol name and exclude entries blacklisted entries */ string parse_layout_symbol(string&& name) { size_t pos; while ((pos = name.find_first_of({'(', ':'})) != string::npos) { name.erase(pos); } if (string_util::contains(LAYOUT_SYMBOL_BLACKLIST, ";" + name + ";")) { return ""; } return name; } } POLYBAR_NS_END