feat(xkeyboard): New module

New module that uses the X keyboard extension
to show keyboard layout and indicators.

Ref #84, #200
This commit is contained in:
Michael Carlberg 2016-11-30 10:06:16 +01:00
parent 6f6c5b7459
commit 608519363d
22 changed files with 617 additions and 132 deletions

2
.gitmodules vendored
View File

@ -5,4 +5,4 @@
[submodule "lib/xpp"]
path = lib/xpp
url = https://github.com/jaagr/xpp
branch = 1.3.1
branch = 1.3.3

View File

@ -56,6 +56,7 @@ option(ENABLE_RENDER_EXT "Enable Render X extension" OFF)
option(ENABLE_DAMAGE_EXT "Enable Damage X extension" OFF)
option(ENABLE_SYNC_EXT "Enable Sync X extension" OFF)
option(ENABLE_COMPOSITE_EXT "Enable Sync X extension" OFF)
option(ENABLE_XKB_EXT "Enable Sync X extension" ON)
# }}}
# Set cache vars {{{

View File

@ -60,4 +60,5 @@ colored_option(STATUS " Enable X Render ${ENABLE_RENDER_EXT}" ENABLE_RENDER
colored_option(STATUS " Enable X Damage ${ENABLE_DAMAGE_EXT}" ENABLE_DAMAGE_EXT "32;1" "37;2")
colored_option(STATUS " Enable X Sync ${ENABLE_SYNC_EXT}" ENABLE_SYNC_EXT "32;1" "37;2")
colored_option(STATUS " Enable X Composite ${ENABLE_COMPOSITE_EXT}" ENABLE_COMPOSITE_EXT "32;1" "37;2")
colored_option(STATUS " Enable X Xkb ${ENABLE_XKB_EXT}" ENABLE_XKB_EXT "32;1" "37;2")
message(STATUS "--------------------------")

View File

@ -54,7 +54,8 @@ set(knownComponents XCB
XFIXES
XTEST
XV
XINERAMA)
XINERAMA
XKB)
unset(unknownComponents)
@ -114,6 +115,8 @@ foreach(comp ${comps})
list(APPEND pkgConfigModules "xcb-xv")
elseif("${comp}" STREQUAL "XINERAMA")
list(APPEND pkgConfigModules "xcb-xinerama")
elseif("${comp}" STREQUAL "XKB")
list(APPEND pkgConfigModules "xcb-xkb")
endif()
endif()
endforeach()
@ -193,6 +196,9 @@ macro(_XCB_HANDLE_COMPONENT _comp)
elseif("${_comp}" STREQUAL "XINERAMA")
set(_header "xcb/xinerama.h")
set(_lib "xcb-xinerama")
elseif("${_comp}" STREQUAL "XKB")
set(_header "xcb/xkb.h")
set(_lib "xcb-xkb")
endif()
find_path(XCB_${_comp}_INCLUDE_DIR NAMES ${_header} HINTS ${PKG_XCB_INCLUDE_DIRS})

View File

@ -135,16 +135,17 @@ struct action_block {
};
struct event_timer {
xcb_timestamp_t event{0};
uint32_t delay_ms{0U};
xcb_timestamp_t event{0L};
xcb_timestamp_t offset{1L};
bool throttle(xcb_timestamp_t evt) {
if (evt > event + delay_ms) {
event = evt;
return false;
} else {
return true;
}
bool allow(xcb_timestamp_t time) {
bool pass = time >= event + offset;
event = time;
return pass;
};
bool deny(xcb_timestamp_t time) {
return !allow(time);
};
};

View File

@ -22,6 +22,8 @@
#cmakedefine01 ENABLE_DAMAGE_EXT
#cmakedefine01 ENABLE_SYNC_EXT
#cmakedefine01 ENABLE_COMPOSITE_EXT
#cmakedefine01 ENABLE_XKB_EXT
#cmakedefine XPP_EXTENSION_LIST @XPP_EXTENSION_LIST@
#cmakedefine DEBUG_LOGGER
#cmakedefine VERBOSE_TRACELOG

View File

@ -0,0 +1,53 @@
#pragma once
#include "common.hpp"
#include "components/config.hpp"
#include "components/types.hpp"
#include "modules/meta/static_module.hpp"
#include "x11/events.hpp"
#include "x11/window.hpp"
#include "x11/xkb.hpp"
POLYBAR_NS
class connection;
namespace modules {
/**
* Keyboard module using the X keyboard extension
*/
class xkeyboard_module : public static_module<xkeyboard_module>,
public xpp::event::sink<evt::xkb_new_keyboard_notify, evt::xkb_indicator_state_notify> {
public:
xkeyboard_module(const bar_settings& bar, const logger& logger, const config& config, string name);
void setup();
void teardown();
void update();
bool build(builder* builder, const string& tag) const;
protected:
bool query_keyboard();
bool blacklisted(const string& indicator_name);
void handle(const evt::xkb_new_keyboard_notify& evt);
void handle(const evt::xkb_indicator_state_notify& evt);
private:
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>"};
connection& m_connection;
event_timer m_xkbnotify{};
unique_ptr<keyboard> m_keyboard;
label_t m_layout;
label_t m_indicator;
map<keyboard::indicator::type, label_t> m_indicators;
vector<string> m_blacklist;
};
}
POLYBAR_NS_END

View File

@ -8,55 +8,13 @@
#include <xpp/xpp.hpp>
#include "common.hpp"
#include "x11/extensions.hpp"
#include "x11/registry.hpp"
#include "x11/types.hpp"
#if ENABLE_DAMAGE_EXT
#include "x11/damage.hpp"
#endif
#if ENABLE_RENDER_EXT
#include "x11/render.hpp"
#endif
#if ENABLE_RANDR_EXT
#include "x11/randr.hpp"
#endif
#if ENABLE_SYNC_EXT
#include "x11/sync.hpp"
#endif
#if ENABLE_COMPOSITE_EXT
#include "x11/composite.hpp"
#endif
POLYBAR_NS
using xpp_connection = xpp::connection<
#if ENABLE_DAMAGE_EXT
xpp::damage::extension
#endif
#if ENABLE_RANDR_EXT
#if ENABLE_DAMAGE_EXT
,
#endif
xpp::randr::extension
#endif
#if ENABLE_RENDER_EXT
#if (ENABLE_RANDR_EXT || ENABLE_DAMAGE_EXT)
,
#endif
xpp::render::extension
#endif
#if ENABLE_SYNC_EXT
#if (ENABLE_RANDR_EXT || ENABLE_DAMAGE_EXT || ENABLE_RENDER_EXT)
,
#endif
xpp::sync::extension
#endif
#if ENABLE_COMPOSITE_EXT
#if (ENABLE_RANDR_EXT || ENABLE_DAMAGE_EXT || ENABLE_RENDER_EXT || ENABLE_SYNC_EXT)
,
#endif
xpp::composite::extension
#endif
>;
using xpp_connection = xpp::connection<XPP_EXTENSION_LIST>;
class connection : public xpp_connection {
public:

View File

@ -0,0 +1,22 @@
#pragma once
#include "config.hpp"
#if ENABLE_DAMAGE_EXT
#include "x11/damage.hpp"
#endif
#if ENABLE_RENDER_EXT
#include "x11/render.hpp"
#endif
#if ENABLE_RANDR_EXT
#include "x11/randr.hpp"
#endif
#if ENABLE_SYNC_EXT
#include "x11/sync.hpp"
#endif
#if ENABLE_COMPOSITE_EXT
#include "x11/composite.hpp"
#endif
#if ENABLE_XKB_EXT
#include "x11/xkb.hpp"
#endif

View File

@ -0,0 +1,39 @@
#pragma once
#include "common.hpp"
namespace xpp {
#if ENABLE_DAMAGE_EXT
namespace damage {
class extension;
}
#endif
#if ENABLE_RANDR_EXT
namespace randr {
class extension;
}
#endif
#if ENABLE_SYNC_EXT
namespace sync {
class extension;
}
#endif
#if ENABLE_RENDER_EXT
namespace render {
class extension;
}
#endif
#if ENABLE_COMPOSITE_EXT
namespace composite {
class extension;
}
#endif
}
POLYBAR_NS
#if ENABLE_XKB_EXT
class xkb_extension;
#endif
POLYBAR_NS_END

28
include/x11/registry.hpp Normal file
View File

@ -0,0 +1,28 @@
#pragma once
#include <xcb/xcb.h>
#include "common.hpp"
#include "x11/extensions_fwd.hpp"
// fwd
namespace xpp {
namespace event {
template <typename Connection, typename... Extensions>
class registry;
}
}
POLYBAR_NS
// fwd
class connection;
using xpp_registry = xpp::event::registry<connection&, XPP_EXTENSION_LIST>;
class registry : public xpp_registry {
public:
explicit registry(connection& conn);
};
POLYBAR_NS_END

View File

@ -1,36 +1,5 @@
#pragma once
#include "config.hpp"
// fwd
namespace xpp {
#if ENABLE_DAMAGE_EXT
namespace damage {
class extension;
}
#endif
#if ENABLE_RANDR_EXT
namespace randr {
class extension;
}
#endif
#if ENABLE_SYNC_EXT
namespace sync {
class extension;
}
#endif
#if ENABLE_RENDER_EXT
namespace render {
class extension;
}
#endif
#if ENABLE_COMPOSITE_EXT
namespace composite {
class extension;
}
#endif
}
#include <xpp/xpp.hpp>
#include "common.hpp"
@ -38,6 +7,7 @@ namespace xpp {
POLYBAR_NS
class connection;
class registry;
using gcontext = xpp::gcontext<connection&>;
using pixmap = xpp::pixmap<connection&>;
@ -47,34 +17,10 @@ using atom = xpp::atom<connection&>;
using font = xpp::font<connection&>;
using cursor = xpp::cursor<connection&>;
using registry = xpp::event::registry<connection&,
#if ENABLE_DAMAGE_EXT
xpp::damage::extension
#endif
#if ENABLE_RANDR_EXT
#if ENABLE_DAMAGE_EXT
,
#endif
xpp::randr::extension
#endif
#if ENABLE_RENDER_EXT
#if (ENABLE_RANDR_EXT || ENABLE_DAMAGE_EXT)
,
#endif
xpp::render::extension
#endif
#if ENABLE_SYNC_EXT
#if (ENABLE_RANDR_EXT || ENABLE_DAMAGE_EXT || ENABLE_RENDER_EXT)
,
#endif
xpp::sync::extension
#endif
#if ENABLE_COMPOSITE_EXT
#if (ENABLE_RANDR_EXT || ENABLE_DAMAGE_EXT || ENABLE_RENDER_EXT || ENABLE_COMPOSITE_EXT)
,
#endif
xpp::composite::extension
#endif
>;
namespace reply_checked = xpp::x::reply::checked;
namespace reply_unchecked = xpp::x::reply::unchecked;
namespace reply {
using get_atom_name = reply_checked::get_atom_name<connection&>;
}
POLYBAR_NS_END

82
include/x11/xkb.hpp Normal file
View File

@ -0,0 +1,82 @@
#pragma once
#include "config.hpp"
#if not ENABLE_XKB_EXT
#error "X xkb extension is disabled..."
#endif
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wkeyword-macro"
#endif
#define explicit mask_cxx_explicit_keyword
#include <xcb/xkb.h>
#include <xpp/proto/xkb.hpp>
#undef explicit
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
#include "common.hpp"
POLYBAR_NS
// fwd
class connection;
namespace evt {
using xkb_new_keyboard_notify = xpp::xkb::event::new_keyboard_notify<connection&>;
using xkb_map_notify = xpp::xkb::event::map_notify<connection&>;
using xkb_state_notify = xpp::xkb::event::state_notify<connection&>;
using xkb_controls_notify = xpp::xkb::event::controls_notify<connection&>;
using xkb_indicator_state_notify = xpp::xkb::event::indicator_state_notify<connection&>;
using xkb_indicator_map_notify = xpp::xkb::event::indicator_map_notify<connection&>;
using xkb_names_notify = xpp::xkb::event::names_notify<connection&>;
using xkb_compat_map_notify = xpp::xkb::event::compat_map_notify<connection&>;
using xkb_bell_notify = xpp::xkb::event::bell_notify<connection&>;
using xkb_action_message = xpp::xkb::event::action_message<connection&>;
using xkb_access_x_notify = xpp::xkb::event::access_x_notify<connection&>;
using xkb_extension_device_notify = xpp::xkb::event::extension_device_notify<connection&>;
}
class keyboard {
public:
struct indicator {
enum class type { NONE = 0U, CAPS_LOCK, NUM_LOCK };
xcb_atom_t atom{};
uint8_t mask{0};
string name;
bool enabled{false};
};
struct layout {
string group_name;
vector<string> symbols;
};
explicit keyboard(vector<layout>&& layouts_, map<indicator::type, indicator>&& indicators_)
: layouts(forward<decltype(layouts)>(layouts_)), indicators(forward<decltype(indicators)>(indicators_)) {}
const indicator& get(const indicator::type& i) const;
void set(uint32_t indicator_state);
bool on(const indicator::type&) 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;
private:
vector<layout> layouts;
map<indicator::type, indicator> indicators;
};
namespace xkb_util {
static constexpr const char* LAYOUT_SYMBOL_BLACKLIST{";group;inet;pc;"};
string parse_layout_symbol(string&& name);
vector<keyboard::layout> get_layouts(connection& conn, xcb_xkb_device_spec_t device);
map<keyboard::indicator::type, keyboard::indicator> get_indicators(connection& conn, xcb_xkb_device_spec_t device);
}
POLYBAR_NS_END

@ -1 +1 @@
Subproject commit 3d6925ad2e1ec7cd2c98ecdc0667c3e6e2d0baaf
Subproject commit 34d0cf14e928270e3dd2ff3aab97f2208d409bff

View File

@ -2,16 +2,6 @@
# Create executable
#
execute_process(COMMAND git describe --tags --dirty=-git
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
OUTPUT_VARIABLE APP_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
configure_file(
${PROJECT_SOURCE_DIR}/include/config.hpp.cmake
${CMAKE_SOURCE_DIR}/include/config.hpp
ESCAPE_QUOTES @ONLY)
# Generate source tree {{{
file(GLOB_RECURSE SOURCES RELATIVE ${PROJECT_SOURCE_DIR}/src *.c[p]*)
@ -55,20 +45,33 @@ set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR})
set(XCB_PROTOS xproto)
if(ENABLE_RANDR_EXT)
set(XCB_PROTOS "${XCB_PROTOS}" randr)
set(XPP_EXTENSION_LIST ${XPP_EXTENSION_LIST} xpp::randr::extension)
endif()
if(ENABLE_RENDER_EXT)
set(XCB_PROTOS "${XCB_PROTOS}" render)
set(XPP_EXTENSION_LIST ${XPP_EXTENSION_LIST} xpp::render::extension)
endif()
if(ENABLE_DAMAGE_EXT)
set(XCB_PROTOS "${XCB_PROTOS}" damage)
set(XPP_EXTENSION_LIST ${XPP_EXTENSION_LIST} xpp::damage::extension)
endif()
if(ENABLE_SYNC_EXT)
set(XCB_PROTOS "${XCB_PROTOS}" sync)
set(XPP_EXTENSION_LIST ${XPP_EXTENSION_LIST} xpp::sync::extension)
endif()
if(ENABLE_COMPOSITE_EXT)
set(XCB_PROTOS "${XCB_PROTOS}" composite)
set(XPP_EXTENSION_LIST ${XPP_EXTENSION_LIST} xpp::composite::extension)
endif()
if(ENABLE_XKB_EXT)
set(XCB_PROTOS "${XCB_PROTOS}" xkb)
set(XPP_EXTENSION_LIST ${XPP_EXTENSION_LIST} xpp::xkb::extension)
endif()
string(REPLACE ";" ", " XPP_EXTENSION_LIST "${XPP_EXTENSION_LIST}")
add_subdirectory(${PROJECT_SOURCE_DIR}/lib/xpp ${PROJECT_BINARY_DIR}/lib/xpp)
set(APP_LIBRARIES ${APP_LIBRARIES} ${XPP_LIBRARIES})
set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} ${XPP_INCLUDE_DIRS})
@ -132,3 +135,13 @@ set(APP_LIBRARIES ${APP_LIBRARIES} PARENT_SCOPE)
set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} PARENT_SCOPE)
# }}}
execute_process(COMMAND git describe --tags --dirty=-git
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
OUTPUT_VARIABLE APP_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
configure_file(
${PROJECT_SOURCE_DIR}/include/config.hpp.cmake
${CMAKE_SOURCE_DIR}/include/config.hpp
ESCAPE_QUOTES @ONLY)

View File

@ -88,7 +88,7 @@ void bar::bootstrap(bool nodraw) {
m_opts.strut.top = m_conf.get<int>("global/wm", "margin-top", 0);
m_opts.strut.bottom = m_conf.get<int>("global/wm", "margin-bottom", 0);
m_buttonpress.delay_ms = xutils::event_timer_ms(m_conf, xcb_button_press_event_t{});
m_buttonpress.offset = xutils::event_timer_ms(m_conf, xcb_button_press_event_t{});
}
m_log.trace("bar: Load color values");
@ -584,7 +584,7 @@ void bar::handle(const evt::button_press& evt) {
std::lock_guard<std::mutex> guard(m_mutex, std::adopt_lock);
if (m_buttonpress.throttle(evt->time)) {
if (m_buttonpress.deny(evt->time)) {
return m_log.trace_x("bar: Ignoring button press (throttled)...");
}

View File

@ -23,6 +23,7 @@
#include "modules/temperature.hpp"
#include "modules/text.hpp"
#include "modules/xbacklight.hpp"
#include "modules/xkeyboard.hpp"
#include "modules/xwindow.hpp"
#include "modules/xworkspaces.hpp"
#include "utils/process.hpp"
@ -400,6 +401,8 @@ void controller::bootstrap_modules() {
module.reset(new temperature_module(bar, m_log, m_conf, module_name));
} else if (type == "internal/xbacklight") {
module.reset(new xbacklight_module(bar, m_log, m_conf, module_name));
} else if (type == "internal/xkeyboard") {
module.reset(new xkeyboard_module(bar, m_log, m_conf, module_name));
} else if (type == "internal/xwindow") {
module.reset(new xwindow_module(bar, m_log, m_conf, module_name));
} else if (type == "internal/xworkspaces") {

View File

@ -61,7 +61,7 @@ namespace modules {
}
// Get the throttle time
m_randrnotify.delay_ms = xutils::event_timer_ms(m_conf, xcb_randr_notify_event_t{});
m_randrnotify.offset = xutils::event_timer_ms(m_conf, xcb_randr_notify_event_t{});
// Connect with the event registry and make sure we get
// notified when a RandR output property gets modified
@ -106,7 +106,7 @@ namespace modules {
return;
} else if (evt->u.op.atom != m_output->backlight.atom) {
return;
} else if (m_randrnotify.throttle(evt->u.op.timestamp)) {
} else if (m_randrnotify.deny(evt->u.op.timestamp)) {
return m_log.trace_x("%s: Ignoring randr notify (throttled)...", name());
} else {
update();

164
src/modules/xkeyboard.cpp Normal file
View File

@ -0,0 +1,164 @@
#include "modules/xkeyboard.hpp"
#include "drawtypes/iconset.hpp"
#include "drawtypes/label.hpp"
#include "x11/atoms.hpp"
#include "x11/connection.hpp"
#include "modules/meta/base.inl"
#include "modules/meta/static_module.inl"
POLYBAR_NS
namespace modules {
template class module<xkeyboard_module>;
template class static_module<xkeyboard_module>;
/**
* Construct module
*/
xkeyboard_module::xkeyboard_module(const bar_settings& bar, const logger& logger, const config& config, string name)
: static_module<xkeyboard_module>(bar, logger, config, name)
, m_connection(configure_connection().create<connection&>()) {}
/**
* Bootstrap the module
*/
void xkeyboard_module::setup() {
// Load config values
m_blacklist = m_conf.get_list<string>(name(), "blacklist", {});
// Add formats and elements
m_formatter->add(DEFAULT_FORMAT, FORMAT_DEFAULT, {TAG_LABEL_LAYOUT, TAG_LABEL_INDICATOR});
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%");
}
// Connect to the event registry
m_connection.attach_sink(this, 3);
// Setup extension
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);
// Create keyboard object
query_keyboard();
update();
}
/**
* Disconnect from the event registry
*/
void xkeyboard_module::teardown() {
m_connection.detach_sink(this, 3);
}
/**
* Update labels with extension data
*/
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());
}
if (m_indicator) {
m_indicators.clear();
const auto& caps = keyboard::indicator::type::CAPS_LOCK;
const auto& caps_str = m_keyboard->indicator_name(caps);
if (!blacklisted(caps_str) && m_keyboard->on(caps)) {
m_indicators[caps] = m_indicator->clone();
m_indicators[caps]->replace_token("%name%", m_keyboard->indicator_name(caps));
}
const auto& num = keyboard::indicator::type::NUM_LOCK;
const auto& num_str = m_keyboard->indicator_name(num);
if (!blacklisted(num_str) && m_keyboard->on(num)) {
m_indicators[num] = m_indicator->clone();
m_indicators[num]->replace_token("%name%", num_str);
}
}
// Trigger redraw
broadcast();
}
/**
* Map format tags to content
*/
bool xkeyboard_module::build(builder* builder, const string& tag) const {
if (tag == TAG_LABEL_LAYOUT) {
builder->node(m_layout);
} else if (tag == TAG_LABEL_INDICATOR) {
for (auto&& indicator : m_indicators) {
if (indicator != *m_indicators.begin()) {
builder->space(m_formatter->get(DEFAULT_FORMAT)->spacing);
}
builder->node(indicator.second);
}
} else {
return false;
}
return true;
}
/**
* Create keyboard object by querying current extension data
*/
bool xkeyboard_module::query_keyboard() {
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<keyboard>(move(layouts), move(indicators));
return true;
} catch (const exception& err) {
throw module_error("Failed to query keyboard, err: " + string{err.what()});
}
return false;
}
/**
* Check if the indicator has been blacklisted by the user
*/
bool xkeyboard_module::blacklisted(const string& indicator_name) {
for (auto&& i : m_blacklist) {
if (string_util::compare(i, indicator_name)) {
return true;
}
}
return false;
}
/**
* Handler for XCB_XKB_NEW_KEYBOARD_NOTIFY events
*/
void xkeyboard_module::handle(const evt::xkb_new_keyboard_notify& evt) {
if (evt->changed & XCB_XKB_NKN_DETAIL_KEYCODES && m_xkbnotify.allow(evt->time)) {
query_keyboard();
update();
}
}
/**
* Handler for XCB_XKB_INDICATOR_STATE_NOTIFY events
*/
void xkeyboard_module::handle(const evt::xkb_indicator_state_notify& evt) {
if (m_xkbnotify.allow(evt->time)) {
m_keyboard->set(m_connection.xkb().get_state(XCB_XKB_ID_USE_CORE_KBD)->lockedMods);
update();
}
}
}
POLYBAR_NS_END

View File

@ -70,6 +70,7 @@ void connection::query_extensions() {
}
#endif
#if ENABLE_SYNC_EXT
sync().initialize(XCB_SYNC_MAJOR_VERSION, XCB_SYNC_MINOR_VERSION);
if (!extension<xpp::sync::extension>()->present) {
throw application_error("Missing X extension: Sync");
}
@ -77,7 +78,13 @@ void connection::query_extensions() {
#if ENABLE_COMPOSITE_EXT
composite().query_version(XCB_COMPOSITE_MAJOR_VERSION, XCB_COMPOSITE_MINOR_VERSION);
if (!extension<xpp::composite::extension>()->present) {
throw application_error("Missing X extension: RandR");
throw application_error("Missing X extension: Composite");
}
#endif
#if ENABLE_XKB_EXT
xkb().use_extension(XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION);
if (!extension<xpp::xkb::extension>()->present) {
throw application_error("Missing X extension: Xkb");
}
#endif

9
src/x11/registry.cpp Normal file
View File

@ -0,0 +1,9 @@
#include "x11/connection.hpp"
#include "x11/extensions.hpp"
#include "x11/registry.hpp"
POLYBAR_NS
registry::registry(connection& conn) : xpp_registry(conn) {}
POLYBAR_NS_END

150
src/x11/xkb.cpp Normal file
View File

@ -0,0 +1,150 @@
#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;
}
/**
* 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 (!layouts.empty() && index < layouts.size() && !layouts[index].symbols.empty()) {
return layouts[index].symbols[0];
}
return "";
}
/**
* Get indicator name
*/
const string keyboard::indicator_name(const indicator::type& i) const {
return indicators.at(i).name;
}
namespace xkb_util {
/**
* Get keyboard layouts
*/
vector<keyboard::layout> 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<reply::get_atom_name> replies;
for (int i = 0; i < len; i++) {
replies.emplace_back(xpp::x::get_atom_name(conn, values.groups[i]));
}
vector<keyboard::layout> results;
for (const auto& reply : replies) {
vector<string> 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::get_atom_name>(reply).name(), sym_names});
}
return results;
}
/**
* Get keyboard indicators
*/
map<keyboard::indicator::type, keyboard::indicator> 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<xcb_atom_t, reply::get_atom_name> entries;
for (int i = 0; i < len; i++) {
entries.emplace(values.indicatorNames[i], xpp::x::get_atom_name(conn, values.indicatorNames[i]));
}
map<keyboard::indicator::type, keyboard::indicator> results;
for (const auto& entry : entries) {
auto name = static_cast<reply::get_atom_name>(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<bool>(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) {
auto pos = name.find('(');
if (pos != string::npos) {
name.erase(pos);
}
if (string_util::contains(LAYOUT_SYMBOL_BLACKLIST, ";" + name + ";")) {
return "";
}
return name;
}
}
POLYBAR_NS_END