Add module for keyboard layout

Update list of modules in README
Add layout class to sway modules
update default config and style
pull/556/head
Matej Novota 2020-01-15 00:56:14 +01:00
parent b9cd51a9cc
commit 87b56a31cb
No known key found for this signature in database
GPG Key ID: 95FBA0E5BFA35C2E
8 changed files with 168 additions and 3 deletions

View File

@ -6,7 +6,7 @@
> *Waybar [examples](https://github.com/Alexays/Waybar/wiki/Examples)* > *Waybar [examples](https://github.com/Alexays/Waybar/wiki/Examples)*
**Current features** **Current features**
- Sway (Workspaces, Binding mode, Focused window name) - Sway (Workspaces, Binding mode, Focused window name, Keyboard Layout)
- Tray [#21](https://github.com/Alexays/Waybar/issues/21) - Tray [#21](https://github.com/Alexays/Waybar/issues/21)
- Local time - Local time
- Battery - Battery

View File

@ -6,6 +6,7 @@
#include "modules/sway/mode.hpp" #include "modules/sway/mode.hpp"
#include "modules/sway/window.hpp" #include "modules/sway/window.hpp"
#include "modules/sway/workspaces.hpp" #include "modules/sway/workspaces.hpp"
#include "modules/sway/layout.hpp"
#endif #endif
#ifndef NO_FILESYSTEM #ifndef NO_FILESYSTEM
#include "modules/battery.hpp" #include "modules/battery.hpp"

View File

@ -0,0 +1,54 @@
#pragma once
#ifdef FILESYSTEM_EXPERIMENTAL
#include <experimental/filesystem>
#else
#include <filesystem>
#endif
#include <fmt/format.h>
#include <fstream>
#include <regex>
#include "ALabel.hpp"
#include "bar.hpp"
#include "client.hpp"
#include "modules/sway/ipc/client.hpp"
#include "util/json.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules::sway {
#ifdef FILESYSTEM_EXPERIMENTAL
namespace fs = std::experimental::filesystem;
#else
namespace fs = std::filesystem;
#endif
class Layout : public ALabel, public sigc::trackable {
public:
Layout(const std::string&, const Json::Value&);
~Layout() = default;
auto update() -> void;
private:
static inline const fs::path xbk_file_ = "/usr/share/X11/xkb/rules/evdev.xml";
void onCmd(const struct Ipc::ipc_response&);
void onEvent(const struct Ipc::ipc_response&);
void worker();
void shortName();
inline std::string sanitize(const std::string& text) {
std::regex specialChars {R"([-[\]{}()*+?.,\^$|#\s])"};
return std::regex_replace(text, specialChars, R"(\$&)");
}
std::string layout_;
std::string short_description_;
std::string short_variant_;
util::JsonParser parser_;
std::mutex mutex_;
util::SleeperThread thread_;
Ipc ipc_;
};
} // namespace waybar::modules::sway

View File

@ -105,7 +105,8 @@ if true # find_program('sway', required : false).found()
'src/modules/sway/ipc/client.cpp', 'src/modules/sway/ipc/client.cpp',
'src/modules/sway/mode.cpp', 'src/modules/sway/mode.cpp',
'src/modules/sway/window.cpp', 'src/modules/sway/window.cpp',
'src/modules/sway/workspaces.cpp' 'src/modules/sway/workspaces.cpp',
'src/modules/sway/layout.cpp'
] ]
endif endif

View File

@ -6,7 +6,7 @@
// Choose the order of the modules // Choose the order of the modules
"modules-left": ["sway/workspaces", "sway/mode", "custom/media"], "modules-left": ["sway/workspaces", "sway/mode", "custom/media"],
"modules-center": ["sway/window"], "modules-center": ["sway/window"],
"modules-right": ["mpd", "idle_inhibitor", "pulseaudio", "network", "cpu", "memory", "temperature", "backlight", "battery", "battery#bat2", "clock", "tray"], "modules-right": ["sway/layout", "mpd", "idle_inhibitor", "pulseaudio", "network", "cpu", "memory", "temperature", "backlight", "battery", "battery#bat2", "clock", "tray"],
// Modules configuration // Modules configuration
// "sway/workspaces": { // "sway/workspaces": {
// "disable-scroll": true, // "disable-scroll": true,
@ -26,6 +26,10 @@
"sway/mode": { "sway/mode": {
"format": "<span style=\"italic\">{}</span>" "format": "<span style=\"italic\">{}</span>"
}, },
"sway/layout": {
"tooltip-format": "{long}",
"format": "{short} "
},
"mpd": { "mpd": {
"format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ", "format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ",
"format-disconnected": "Disconnected ", "format-disconnected": "Disconnected ",

View File

@ -77,6 +77,7 @@ window#waybar.chromium {
#tray, #tray,
#mode, #mode,
#idle_inhibitor, #idle_inhibitor,
#layout,
#mpd { #mpd {
padding: 0 10px; padding: 0 10px;
margin: 0 4px; margin: 0 4px;

View File

@ -22,6 +22,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
if (ref == "sway/window") { if (ref == "sway/window") {
return new waybar::modules::sway::Window(id, bar_, config_[name]); return new waybar::modules::sway::Window(id, bar_, config_[name]);
} }
if (ref == "sway/layout") {
return new waybar::modules::sway::Layout(id, config_[name]);
}
#endif #endif
if (ref == "idle_inhibitor") { if (ref == "idle_inhibitor") {
return new waybar::modules::IdleInhibitor(id, bar_, config_[name]); return new waybar::modules::IdleInhibitor(id, bar_, config_[name]);

View File

@ -0,0 +1,101 @@
#include "modules/sway/layout.hpp"
#include <spdlog/spdlog.h>
namespace waybar::modules::sway {
Layout::Layout(const std::string& id, const Json::Value& config)
: ALabel(config, "layout", id, "{}", 0) {
ipc_.subscribe(R"(["input"])");
ipc_.signal_event.connect(sigc::mem_fun(*this, &Layout::onEvent));
ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Layout::onCmd));
ipc_.sendCmd(IPC_GET_INPUTS);
shortName();
// Launch worker
worker();
dp.emit();
}
void Layout::onEvent(const struct Ipc::ipc_response& res) {
try {
std::lock_guard<std::mutex> lock(mutex_);
auto payload = parser_.parse(res.payload);
if (payload["change"] == "xkb_layout") {
layout_ = payload["input"]["xkb_active_layout_name"].asString();
}
dp.emit();
} catch (const std::exception& e) {
spdlog::error("Layout: {}", e.what());
}
}
void Layout::onCmd(const struct Ipc::ipc_response &res) {
if (res.type == IPC_GET_INPUTS) {
try {
std::lock_guard<std::mutex> lock(mutex_);
auto payload = parser_.parse(res.payload);
for (auto keyboard : payload) {
if (keyboard["identifier"] == "1:1:AT_Translated_Set_2_keyboard") {
layout_ = keyboard["xkb_active_layout_name"].asString();
}
}
dp.emit();
} catch (const std::exception& e) {
spdlog::error("Layout: {}", e.what());
}
}
}
void Layout::worker() {
thread_ = [this] {
try {
ipc_.handleEvent();
} catch (const std::exception& e) {
spdlog::error("Layout: {}", e.what());
}
};
}
auto Layout::update() -> void {
shortName();
if (layout_.empty()) {
event_box_.hide();
} else {
label_.set_markup(fmt::format(format_,
fmt::arg("long", layout_),
fmt::arg("short", short_description_),
fmt::arg("variant", short_variant_)));
if (tooltipEnabled()) {
if (config_["tooltip-format"].isString()) {
auto tooltip_format = config_["tooltip-format"].asString();
label_.set_tooltip_text(fmt::format(tooltip_format,
fmt::arg("long", layout_),
fmt::arg("short", short_description_),
fmt::arg("variant", short_variant_)));
} else {
label_.set_tooltip_text(layout_);
}
}
event_box_.show();
}
}
void Layout::shortName() {
std::string line;
std::ifstream file (xbk_file_);
std::regex e1 (".*<shortDescription>(.*)</shortDescription>.*");
std::regex e2 (".*<name>(.*)</name>.*");
while (getline(file,line)) {
if(std::regex_match(line, e1)) {
short_description_ = std::regex_replace(line, e1, "$1");
} else if(std::regex_match(line, e2)) {
short_variant_ = std::regex_replace(line, e2, "$1");
} else if(std::regex_match(line, std::regex(".*<description>"+sanitize(layout_)+"</description>.*"))){
break;
}
}
}
} // namespace waybar::modules::sway