feat(xkeyboard): Customizable indicators and layouts (#1559)
* `layout-icon-*` list that maps layouts to icons. * `indicator-icon-*` list that maps indicators to off and on icons * `label-indicator-off` * `label-indicator-on` which replaces the now deprecated `label-indicator` * `label-indicator-[on|off]-*` for each indicator. Overrides `label-indicator-on` and `label-indicator-off` Fixes #1558 Closes #1048 * add icon support for xkeyboard layouts * removed unneeded #include * add sperate %icon% token that can be used in <label-layout> * removed unneeded #include * added caps lock indicator (was mentioned in wiki, but not actually implememnted) and support for indicator icons * a few more fixes to make sure existing user configs are not broken * ready to go * Added an option to replace xkb indicator names * Added labels for each indicator state * Removed print left on accident * Fixed review comments * Update src/modules/xkeyboard.cpp Co-Authored-By: Gilnaa <gilad@naaman.io>
This commit is contained in:
parent
7d4e62f6d3
commit
db9a83a83b
4 changed files with 126 additions and 27 deletions
|
@ -42,7 +42,9 @@ namespace modules {
|
|||
static constexpr const char* TAG_LABEL_LAYOUT{"<label-layout>"};
|
||||
static constexpr const char* TAG_LABEL_INDICATOR{"<label-indicator>"};
|
||||
static constexpr const char* FORMAT_DEFAULT{"<label-layout> <label-indicator>"};
|
||||
|
||||
static constexpr const char* DEFAULT_LAYOUT_ICON{"layout-icon-default"};
|
||||
static constexpr const char* DEFAULT_INDICATOR_ICON{"indicator-icon-default"};
|
||||
|
||||
static constexpr const char* EVENT_SWITCH{"xkeyboard/switch"};
|
||||
|
||||
connection& m_connection;
|
||||
|
@ -52,10 +54,16 @@ namespace modules {
|
|||
unique_ptr<keyboard> m_keyboard;
|
||||
|
||||
label_t m_layout;
|
||||
label_t m_indicator;
|
||||
label_t m_indicator_state_on;
|
||||
label_t m_indicator_state_off;
|
||||
map<keyboard::indicator::type, label_t> m_indicators;
|
||||
map<keyboard::indicator::type, label_t> m_indicator_on_labels;
|
||||
map<keyboard::indicator::type, label_t> m_indicator_off_labels;
|
||||
|
||||
vector<string> m_blacklist;
|
||||
iconset_t m_layout_icons;
|
||||
iconset_t m_indicator_icons_on;
|
||||
iconset_t m_indicator_icons_off;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ namespace evt {
|
|||
class keyboard {
|
||||
public:
|
||||
struct indicator {
|
||||
enum class type { NONE = 0U, CAPS_LOCK, NUM_LOCK };
|
||||
enum class type { NONE = 0U, CAPS_LOCK, NUM_LOCK, SCROLL_LOCK };
|
||||
xcb_atom_t atom{};
|
||||
unsigned char mask{0};
|
||||
string name{};
|
||||
|
|
|
@ -12,13 +12,48 @@ POLYBAR_NS
|
|||
namespace modules {
|
||||
template class module<xkeyboard_module>;
|
||||
|
||||
// clang-format off
|
||||
static const keyboard::indicator::type INDICATOR_TYPES[] {
|
||||
keyboard::indicator::type::CAPS_LOCK,
|
||||
keyboard::indicator::type::NUM_LOCK,
|
||||
keyboard::indicator::type::SCROLL_LOCK
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
/**
|
||||
* Construct module
|
||||
*/
|
||||
xkeyboard_module::xkeyboard_module(const bar_settings& bar, string name_)
|
||||
: static_module<xkeyboard_module>(bar, move(name_)), m_connection(connection::make()) {
|
||||
|
||||
|
||||
// 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_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();
|
||||
|
||||
// Load config values
|
||||
m_blacklist = m_conf.get_list(name(), "blacklist", {});
|
||||
|
||||
// load layout icons
|
||||
m_layout_icons = factory_util::shared<iconset>();
|
||||
m_layout_icons->add(DEFAULT_LAYOUT_ICON, load_optional_label(m_conf, name(), DEFAULT_LAYOUT_ICON, ""s));
|
||||
|
||||
for (const auto& it : m_conf.get_list<string>(name(), "layout-icon", {})) {
|
||||
auto vec = string_util::split(it, ';');
|
||||
if (vec.size() == 2) {
|
||||
m_layout_icons->add(vec[0], factory_util::shared<label>(vec[1]));
|
||||
}
|
||||
}
|
||||
|
||||
// Add formats and elements
|
||||
m_formatter->add(DEFAULT_FORMAT, FORMAT_DEFAULT, {TAG_LABEL_LAYOUT, TAG_LABEL_INDICATOR});
|
||||
|
@ -26,19 +61,60 @@ namespace modules {
|
|||
if (m_formatter->has(TAG_LABEL_LAYOUT)) {
|
||||
m_layout = load_optional_label(m_conf, name(), TAG_LABEL_LAYOUT, "%layout%");
|
||||
}
|
||||
|
||||
|
||||
if (m_formatter->has(TAG_LABEL_INDICATOR)) {
|
||||
m_indicator = load_optional_label(m_conf, name(), TAG_LABEL_INDICATOR, "%name%");
|
||||
label_t indicator = load_optional_label(m_conf, name(), TAG_LABEL_INDICATOR, "%name%"s);
|
||||
m_conf.warn_deprecated(name(), TAG_LABEL_INDICATOR, "label-indicator-on");
|
||||
|
||||
// load an empty label if 'label-indicator-off' is not explicitly specified so
|
||||
// no existing user configs are broken (who expect nothing to be shown when indicator is off)
|
||||
m_indicator_state_off = load_optional_label(m_conf, name(), "label-indicator-off"s, ""s);
|
||||
|
||||
if (m_conf.has(name(), "label-indicator-on"s)) {
|
||||
m_indicator_state_on = load_optional_label(m_conf, name(), "label-indicator-on"s, "%name%"s);
|
||||
} else {
|
||||
// if 'label-indicator-on' is not explicitly specified, use 'label-indicator'
|
||||
// as to not break existing user configs
|
||||
m_indicator_state_off = std::move(indicator);
|
||||
}
|
||||
|
||||
// load indicator icons
|
||||
m_indicator_icons_off = factory_util::shared<iconset>();
|
||||
m_indicator_icons_on = factory_util::shared<iconset>();
|
||||
|
||||
auto icon_pair = string_util::split(m_conf.get(name(), DEFAULT_INDICATOR_ICON, ""s), ';');
|
||||
if(icon_pair.size() == 2) {
|
||||
m_indicator_icons_off->add(DEFAULT_INDICATOR_ICON, factory_util::shared<label>(icon_pair[0]));
|
||||
m_indicator_icons_on->add(DEFAULT_INDICATOR_ICON, factory_util::shared<label>(icon_pair[1]));
|
||||
} else {
|
||||
m_indicator_icons_off->add(DEFAULT_INDICATOR_ICON, factory_util::shared<label>(""s));
|
||||
m_indicator_icons_on->add(DEFAULT_INDICATOR_ICON, factory_util::shared<label>(""s));
|
||||
}
|
||||
|
||||
for (const auto& it : m_conf.get_list<string>(name(), "indicator-icon", {})) {
|
||||
auto icon_triple = string_util::split(it, ';');
|
||||
if (icon_triple.size() == 3) {
|
||||
auto const indicator_str = string_util::lower(icon_triple[0]);
|
||||
m_indicator_icons_off->add(indicator_str, factory_util::shared<label>(icon_triple[1]));
|
||||
m_indicator_icons_on->add(indicator_str, factory_util::shared<label>(icon_triple[2]));
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it : INDICATOR_TYPES) {
|
||||
const auto& indicator_str = m_keyboard->indicator_name(it);
|
||||
auto key_name = string_util::replace(string_util::lower(indicator_str), " "s, ""s);
|
||||
const auto indicator_key_on = "label-indicator-on-"s + key_name;
|
||||
const auto indicator_key_off = "label-indicator-off-"s + key_name;
|
||||
|
||||
if (m_conf.has(name(), indicator_key_on)) {
|
||||
m_indicator_on_labels.emplace(it, load_label(m_conf, name(), indicator_key_on));
|
||||
}
|
||||
if (m_conf.has(name(), indicator_key_off)) {
|
||||
m_indicator_off_labels.emplace(it, load_label(m_conf, name(), indicator_key_off));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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_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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -48,28 +124,41 @@ namespace modules {
|
|||
if (m_layout) {
|
||||
m_layout->reset_tokens();
|
||||
m_layout->replace_token("%name%", m_keyboard->group_name(m_keyboard->current()));
|
||||
m_layout->replace_token("%layout%", m_keyboard->layout_name(m_keyboard->current()));
|
||||
|
||||
auto const current_layout = m_keyboard->layout_name(m_keyboard->current());
|
||||
auto icon = m_layout_icons->get(current_layout, DEFAULT_LAYOUT_ICON);
|
||||
|
||||
m_layout->replace_token("%icon%", icon->get());
|
||||
m_layout->replace_token("%layout%", current_layout);
|
||||
m_layout->replace_token("%number%", to_string(m_keyboard->current()));
|
||||
}
|
||||
|
||||
if (m_indicator) {
|
||||
m_indicators.clear();
|
||||
m_indicators.clear();
|
||||
|
||||
const auto& caps = keyboard::indicator::type::CAPS_LOCK;
|
||||
const auto& caps_str = m_keyboard->indicator_name(caps);
|
||||
for (auto it : INDICATOR_TYPES) {
|
||||
const auto& indicator_str = m_keyboard->indicator_name(it);
|
||||
|
||||
if (!blacklisted(caps_str) && m_keyboard->on(caps)) {
|
||||
m_indicators[caps] = m_indicator->clone();
|
||||
m_indicators[caps]->replace_token("%name%", caps_str);
|
||||
if (blacklisted(indicator_str)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& num = keyboard::indicator::type::NUM_LOCK;
|
||||
const auto& num_str = m_keyboard->indicator_name(num);
|
||||
auto indicator_on = m_keyboard->on(it);
|
||||
auto &indicator_labels = indicator_on ? m_indicator_on_labels : m_indicator_off_labels;
|
||||
auto &indicator_icons = indicator_on ? m_indicator_icons_on : m_indicator_icons_off;
|
||||
auto &indicator_state = indicator_on ? m_indicator_state_on : m_indicator_state_off;
|
||||
|
||||
if (!blacklisted(num_str) && m_keyboard->on(num)) {
|
||||
m_indicators[num] = m_indicator->clone();
|
||||
m_indicators[num]->replace_token("%name%", num_str);
|
||||
label_t indicator;
|
||||
if (indicator_labels.find(it) != indicator_labels.end()) {
|
||||
indicator = indicator_labels[it]->clone();
|
||||
} else {
|
||||
indicator = indicator_state->clone();
|
||||
}
|
||||
|
||||
auto icon = indicator_icons->get(string_util::lower(indicator_str), DEFAULT_INDICATOR_ICON);
|
||||
|
||||
indicator->replace_token("%name%", indicator_str);
|
||||
indicator->replace_token("%icon%", icon->get());
|
||||
m_indicators.emplace(it, move(indicator));
|
||||
}
|
||||
|
||||
// Trigger redraw
|
||||
|
|
|
@ -181,6 +181,8 @@ namespace xkb_util {
|
|||
type = keyboard::indicator::type::CAPS_LOCK;
|
||||
} else if (string_util::compare(name, "num lock")) {
|
||||
type = keyboard::indicator::type::NUM_LOCK;
|
||||
} else if (string_util::compare(name, "scroll lock")) {
|
||||
type = keyboard::indicator::type::SCROLL_LOCK;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue