diff --git a/include/modules/xkeyboard.hpp b/include/modules/xkeyboard.hpp index 4a7daf12..bb89cb5e 100644 --- a/include/modules/xkeyboard.hpp +++ b/include/modules/xkeyboard.hpp @@ -17,7 +17,7 @@ namespace modules { * Keyboard module using the X keyboard extension */ class xkeyboard_module : public static_module, - public xpp::event::sink { + public xpp::event::sink { public: xkeyboard_module(const bar_settings& bar, const logger& logger, const config& config, string name); @@ -31,6 +31,7 @@ namespace modules { bool blacklisted(const string& indicator_name); void handle(const evt::xkb_new_keyboard_notify& evt); + void handle(const evt::xkb_state_notify& evt); void handle(const evt::xkb_indicator_state_notify& evt); private: diff --git a/include/x11/xkb.hpp b/include/x11/xkb.hpp index 7989dbba..3c64afe7 100644 --- a/include/x11/xkb.hpp +++ b/include/x11/xkb.hpp @@ -57,13 +57,14 @@ class keyboard { vector symbols; }; - explicit keyboard(vector&& layouts_, map&& indicators_) - : layouts(forward(layouts_)), indicators(forward(indicators_)) {} + explicit keyboard(vector&& layouts_, map&& indicators_, uint8_t group) + : layouts(forward(layouts_)), indicators(forward(indicators_)), current_group(group) {} const indicator& get(const indicator::type& i) const; void set(uint32_t state); bool on(const indicator::type&) const; - + void current(uint8_t group); + uint8_t current() const; const string group_name(size_t index = 0) const; const string layout_name(size_t index = 0) const; const string indicator_name(const indicator::type&) const; @@ -71,14 +72,16 @@ class keyboard { private: vector layouts; map indicators; + uint8_t current_group{0}; }; namespace xkb_util { static constexpr const char* LAYOUT_SYMBOL_BLACKLIST{";group;inet;pc;"}; - string parse_layout_symbol(string&& name); + uint8_t get_current_group(connection& conn, xcb_xkb_device_spec_t device); vector get_layouts(connection& conn, xcb_xkb_device_spec_t device); map get_indicators(connection& conn, xcb_xkb_device_spec_t device); + string parse_layout_symbol(string&& name); } POLYBAR_NS_END diff --git a/src/modules/xkeyboard.cpp b/src/modules/xkeyboard.cpp index fcf98063..a6e4d689 100644 --- a/src/modules/xkeyboard.cpp +++ b/src/modules/xkeyboard.cpp @@ -41,9 +41,11 @@ namespace modules { m_connection.attach_sink(this, SINK_PRIORITY_MODULE); // Setup extension + // clang-format off m_connection.xkb().select_events_checked(XCB_XKB_ID_USE_CORE_KBD, - XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY | XCB_XKB_EVENT_TYPE_INDICATOR_STATE_NOTIFY, 0, - XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY | XCB_XKB_EVENT_TYPE_INDICATOR_STATE_NOTIFY, 0, 0, nullptr); + XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY | XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_INDICATOR_STATE_NOTIFY, 0, + XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY | XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_INDICATOR_STATE_NOTIFY, 0, 0, nullptr); + // clang-format on // Create keyboard object query_keyboard(); @@ -64,8 +66,9 @@ namespace modules { void xkeyboard_module::update() { if (m_layout) { m_layout->reset_tokens(); - m_layout->replace_token("%name%", m_keyboard->group_name()); - m_layout->replace_token("%layout%", m_keyboard->layout_name()); + m_layout->replace_token("%name%", m_keyboard->group_name(m_keyboard->current())); + m_layout->replace_token("%layout%", m_keyboard->layout_name(m_keyboard->current())); + m_layout->replace_token("%number%", to_string(m_keyboard->current())); } if (m_indicator) { @@ -121,7 +124,8 @@ namespace modules { try { auto layouts = xkb_util::get_layouts(m_connection, XCB_XKB_ID_USE_CORE_KBD); auto indicators = xkb_util::get_indicators(m_connection, XCB_XKB_ID_USE_CORE_KBD); - m_keyboard = make_unique(move(layouts), move(indicators)); + auto current_group = xkb_util::get_current_group(m_connection, XCB_XKB_ID_USE_CORE_KBD); + m_keyboard = make_unique(move(layouts), move(indicators), current_group); return true; } catch (const exception& err) { throw module_error("Failed to query keyboard, err: " + string{err.what()}); @@ -152,6 +156,16 @@ namespace modules { } } + /** + * Handler for XCB_XKB_STATE_NOTIFY events + */ + void xkeyboard_module::handle(const evt::xkb_state_notify& evt) { + if (m_keyboard && evt->changed & XCB_XKB_STATE_PART_GROUP_STATE && m_xkbnotify.allow(evt->time)) { + m_keyboard->current(evt->group); + update(); + } + } + /** * Handler for XCB_XKB_INDICATOR_STATE_NOTIFY events */ diff --git a/src/x11/xkb.cpp b/src/x11/xkb.cpp index da2ab1ac..921b8621 100644 --- a/src/x11/xkb.cpp +++ b/src/x11/xkb.cpp @@ -29,6 +29,20 @@ 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 */ @@ -43,10 +57,10 @@ const string keyboard::group_name(size_t index) const { * Get current layout name */ const string keyboard::layout_name(size_t index) const { - if (!layouts.empty() && index < layouts.size() && !layouts[index].symbols.empty()) { - return layouts[index].symbols[0]; + if (index >= layouts.size() || index >= layouts[index].symbols.size()) { + return ""; } - return ""; + return layouts[index].symbols[index]; } /** @@ -57,6 +71,14 @@ const string keyboard::indicator_name(const indicator::type& i) const { } 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 */ @@ -136,8 +158,8 @@ namespace xkb_util { * Parse symbol name and exclude entries blacklisted entries */ string parse_layout_symbol(string&& name) { - auto pos = name.find('('); - if (pos != string::npos) { + size_t pos; + while ((pos = name.find_first_of({'(', ':'})) != string::npos) { name.erase(pos); } if (string_util::contains(LAYOUT_SYMBOL_BLACKLIST, ";" + name + ";")) {