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:
parent
6f6c5b7459
commit
608519363d
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -5,4 +5,4 @@
|
||||
[submodule "lib/xpp"]
|
||||
path = lib/xpp
|
||||
url = https://github.com/jaagr/xpp
|
||||
branch = 1.3.1
|
||||
branch = 1.3.3
|
||||
|
@ -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 {{{
|
||||
|
@ -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 "--------------------------")
|
||||
|
@ -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})
|
||||
|
@ -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);
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
53
include/modules/xkeyboard.hpp
Normal file
53
include/modules/xkeyboard.hpp
Normal 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
|
@ -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:
|
||||
|
22
include/x11/extensions.hpp
Normal file
22
include/x11/extensions.hpp
Normal 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
|
39
include/x11/extensions_fwd.hpp
Normal file
39
include/x11/extensions_fwd.hpp
Normal 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
28
include/x11/registry.hpp
Normal 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
|
@ -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
82
include/x11/xkb.hpp
Normal 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
|
2
lib/xpp
2
lib/xpp
@ -1 +1 @@
|
||||
Subproject commit 3d6925ad2e1ec7cd2c98ecdc0667c3e6e2d0baaf
|
||||
Subproject commit 34d0cf14e928270e3dd2ff3aab97f2208d409bff
|
@ -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)
|
||||
|
@ -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)...");
|
||||
}
|
||||
|
||||
|
@ -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") {
|
||||
|
@ -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
164
src/modules/xkeyboard.cpp
Normal 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
|
@ -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
9
src/x11/registry.cpp
Normal 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
150
src/x11/xkb.cpp
Normal 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
|
Loading…
Reference in New Issue
Block a user