Skip to content

Commit

Permalink
Merge pull request #1159 from Anakael/pr/anakael/sway-language-impr
Browse files Browse the repository at this point in the history
[sway/language] Improve sway/language
  • Loading branch information
Alexays authored Jul 23, 2021
2 parents b33be38 + b47705a commit 3f3f2d9
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 73 deletions.
4 changes: 4 additions & 0 deletions include/modules/sway/ipc/ipc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,8 @@ enum ipc_command_type {
IPC_EVENT_BINDING = ((1 << 31) | 5),
IPC_EVENT_SHUTDOWN = ((1 << 31) | 6),
IPC_EVENT_TICK = ((1 << 31) | 7),

// sway-specific event types
IPC_EVENT_BAR_STATE_UPDATE = ((1<<31) | 20),
IPC_EVENT_INPUT = ((1<<31) | 21),
};
40 changes: 36 additions & 4 deletions include/modules/sway/language.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
#pragma once

#include <fmt/format.h>
#include <xkbcommon/xkbregistry.h>

#include <map>
#include <string>

#include "ALabel.hpp"
#include "bar.hpp"
#include "client.hpp"
Expand All @@ -16,13 +21,40 @@ class Language : public ALabel, public sigc::trackable {
auto update() -> void;

private:
struct Layout {
std::string full_name;
std::string short_name;
std::string variant;
};

class XKBContext {
public:
XKBContext();
~XKBContext();
auto next_layout() -> Layout*;
private:
rxkb_context* context_ = nullptr;
rxkb_layout* xkb_layout_ = nullptr;
Layout* layout_ = nullptr;
};

void onEvent(const struct Ipc::ipc_response&);
void onCmd(const struct Ipc::ipc_response&);

auto set_current_layout(std::string current_layout) -> void;
auto init_layouts_map(const std::vector<std::string>& used_layouts) -> void;

const static std::string XKB_LAYOUT_NAMES_KEY;
const static std::string XKB_ACTIVE_LAYOUT_NAME_KEY;

std::string lang_;
util::JsonParser parser_;
std::mutex mutex_;
Ipc ipc_;
Layout layout_;
std::map<std::string, Layout> layouts_map_;
XKBContext xkb_context_;
bool is_variant_displayed;

util::JsonParser parser_;
std::mutex mutex_;
Ipc ipc_;
};

} // namespace waybar::modules::sway
15 changes: 15 additions & 0 deletions include/util/string.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include <string>

const std::string WHITESPACE = " \n\r\t\f\v";

std::string ltrim(const std::string s) {
size_t begin = s.find_first_not_of(WHITESPACE);
return (begin == std::string::npos) ? "" : s.substr(begin);
}

std::string rtrim(const std::string s) {
size_t end = s.find_last_not_of(WHITESPACE);
return (end == std::string::npos) ? "" : s.substr(0, end + 1);
}

std::string trim(const std::string& s) { return rtrim(ltrim(s)); }
59 changes: 13 additions & 46 deletions man/waybar-sway-language.5.scd
Original file line number Diff line number Diff line change
Expand Up @@ -15,63 +15,30 @@ Addressed by *sway/language*
*format*: ++
typeof: string ++
default: {} ++
The format, how information should be displayed. On {} data gets inserted.

*rotate*: ++
typeof: integer ++
Positive value to rotate the text label.

*max-length*: ++
typeof: integer ++
The maximum length in character the module should display.

*min-length*: ++
typeof: integer ++
The minimum length in characters the module should take up.

*align*: ++
typeof: float ++
The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text.

*on-click*: ++
typeof: string ++
Command to execute when clicked on the module.

*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.

*on-click-right*: ++
typeof: string ++
Command to execute when you right clicked on the module.

*on-update*: ++
typeof: string ++
Command to execute when the module is updated.

*on-scroll-up*: ++
typeof: string ++
Command to execute when scrolling up on the module.

*on-scroll-down*: ++
typeof: string ++
Command to execute when scrolling down on the module.

*smooth-scrolling-threshold*: ++
typeof: double ++
Threshold to be used when scrolling.
The format, how layout should be displayed.

*tooltip*: ++
typeof: bool ++
default: true ++
Option to disable tooltip on hover.

# FORMAT REPLACEMENTS

*{short}*: Short name of layout (e.g. "en"). Equals to {}.

*{long}*: Long name of layout (e.g. "English (Dvorak)").

*{variant}*: Variant of layout (e.g. "Dvorak").

# EXAMPLES

```
"sway/language": {
"format": "{}",
"max-length": 50
},
"sway/language": {
"format": "{short} {variant}",
}
```

Expand Down
4 changes: 3 additions & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ libnlgen = dependency('libnl-genl-3.0', required: get_option('libnl'))
libpulse = dependency('libpulse', required: get_option('pulseaudio'))
libudev = dependency('libudev', required: get_option('libudev'))
libmpdclient = dependency('libmpdclient', required: get_option('mpd'))
xkbregistry = dependency('xkbregistry')

libsndio = compiler.find_library('sndio', required: get_option('sndio'))
if libsndio.found()
Expand Down Expand Up @@ -272,7 +273,8 @@ executable(
libmpdclient,
gtk_layer_shell,
libsndio,
tz_dep
tz_dep,
xkbregistry
],
include_directories: [include_directories('include')],
install: true,
Expand Down
148 changes: 126 additions & 22 deletions src/modules/sway/language.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
#include "modules/sway/language.hpp"

#include <fmt/core.h>
#include <spdlog/spdlog.h>
#include <xkbcommon/xkbregistry.h>

#include <cstring>
#include <string>
#include <vector>

#include "modules/sway/ipc/ipc.hpp"
#include "util/string.hpp"

namespace waybar::modules::sway {

const std::string Language::XKB_LAYOUT_NAMES_KEY = "xkb_layout_names";
const std::string Language::XKB_ACTIVE_LAYOUT_NAME_KEY = "xkb_active_layout_name";

Language::Language(const std::string& id, const Json::Value& config)
: ALabel(config, "language", id, "{}", 0, true) {
is_variant_displayed = format_.find("{variant}") != std::string::npos;
ipc_.subscribe(R"(["input"])");
ipc_.signal_event.connect(sigc::mem_fun(*this, &Language::onEvent));
ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Language::onCmd));
Expand All @@ -21,33 +35,47 @@ Language::Language(const std::string& id, const Json::Value& config)
}

void Language::onCmd(const struct Ipc::ipc_response& res) {
if (res.type != static_cast<uint32_t>(IPC_GET_INPUTS)) {
return;
}

try {
auto payload = parser_.parse(res.payload);
//Display current layout of a device with a maximum count of layouts, expecting that all will be OK
Json::Value::ArrayIndex maxId = 0, max = 0;
for(Json::Value::ArrayIndex i = 0; i < payload.size(); i++) {
if(payload[i]["xkb_layout_names"].size() > max) {
max = payload[i]["xkb_layout_names"].size();
maxId = i;
std::lock_guard<std::mutex> lock(mutex_);
auto payload = parser_.parse(res.payload);
std::vector<std::string> used_layouts;
// Display current layout of a device with a maximum count of layouts, expecting that all will
// be OK
Json::ArrayIndex max_id = 0, max = 0;
for (Json::ArrayIndex i = 0; i < payload.size(); i++) {
auto size = payload[i][XKB_LAYOUT_NAMES_KEY].size();
if (size > max) {
max = size;
max_id = i;
}
}
auto layout_name = payload[maxId]["xkb_active_layout_name"].asString().substr(0,2);
lang_ = Glib::Markup::escape_text(layout_name);

for (const auto& layout : payload[max_id][XKB_LAYOUT_NAMES_KEY]) {
used_layouts.push_back(layout.asString());
}

init_layouts_map(used_layouts);
set_current_layout(payload[max_id][XKB_ACTIVE_LAYOUT_NAME_KEY].asString());
dp.emit();
} catch (const std::exception& e) {
spdlog::error("Language: {}", e.what());
}
}

void Language::onEvent(const struct Ipc::ipc_response& res) {
if (res.type != static_cast<uint32_t>(IPC_EVENT_INPUT)) {
return;
}

try {
std::lock_guard<std::mutex> lock(mutex_);
auto payload = parser_.parse(res.payload)["input"];
auto payload = parser_.parse(res.payload)["input"];
if (payload["type"].asString() == "keyboard") {
auto layout_name = payload["xkb_active_layout_name"].asString().substr(0,2);
if (!layout_name.empty()) {
lang_ = Glib::Markup::escape_text(layout_name);
}
set_current_layout(payload[XKB_ACTIVE_LAYOUT_NAME_KEY].asString());
}
dp.emit();
} catch (const std::exception& e) {
Expand All @@ -56,17 +84,93 @@ void Language::onEvent(const struct Ipc::ipc_response& res) {
}

auto Language::update() -> void {
if (lang_.empty()) {
event_box_.hide();
} else {
label_.set_markup(fmt::format(format_, lang_));
if (tooltipEnabled()) {
label_.set_tooltip_text(lang_);
}
event_box_.show();
auto display_layout = trim(fmt::format(format_,
fmt::arg("short", layout_.short_name),
fmt::arg("long", layout_.full_name),
fmt::arg("variant", layout_.variant)));
label_.set_markup(display_layout);
if (tooltipEnabled()) {
label_.set_tooltip_markup(display_layout);
}

event_box_.show();

// Call parent update
ALabel::update();
}

auto Language::set_current_layout(std::string current_layout) -> void {
layout_ = layouts_map_[current_layout];
}

auto Language::init_layouts_map(const std::vector<std::string>& used_layouts) -> void {
std::map<std::string, std::vector<Layout*>> found_by_short_names;
auto layout = xkb_context_.next_layout();
for (; layout != nullptr; layout = xkb_context_.next_layout()) {
if (std::find(used_layouts.begin(), used_layouts.end(), layout->full_name) ==
used_layouts.end()) {
continue;
}

if (!is_variant_displayed) {
auto short_name = layout->short_name;
if (found_by_short_names.count(short_name) > 0) {
found_by_short_names[short_name].push_back(layout);
} else {
found_by_short_names[short_name] = {layout};
}
}

layouts_map_.emplace(layout->full_name, *layout);
}

if (is_variant_displayed || found_by_short_names.size() == 0) {
return;
}

std::map<std::string, int> short_name_to_number_map;
for (const auto& used_layout_name : used_layouts) {
auto used_layout = &layouts_map_.find(used_layout_name)->second;
auto layouts_with_same_name_list = found_by_short_names[used_layout->short_name];
spdlog::info("SIZE: " + std::to_string(layouts_with_same_name_list.size()));
if (layouts_with_same_name_list.size() < 2) {
continue;
}

if (short_name_to_number_map.count(used_layout->short_name) == 0) {
short_name_to_number_map[used_layout->short_name] = 1;
}

used_layout->short_name =
used_layout->short_name + std::to_string(short_name_to_number_map[used_layout->short_name]++);
}
}

Language::XKBContext::XKBContext() {
context_ = rxkb_context_new(RXKB_CONTEXT_NO_DEFAULT_INCLUDES);
rxkb_context_include_path_append_default(context_);
rxkb_context_parse_default_ruleset(context_);
}

auto Language::XKBContext::next_layout() -> Layout* {
if (xkb_layout_ == nullptr) {
xkb_layout_ = rxkb_layout_first(context_);
} else {
xkb_layout_ = rxkb_layout_next(xkb_layout_);
}

if (xkb_layout_ == nullptr) {
return nullptr;
}

auto description = std::string(rxkb_layout_get_description(xkb_layout_));
auto name = std::string(rxkb_layout_get_name(xkb_layout_));
auto variant_ = rxkb_layout_get_variant(xkb_layout_);
std::string variant = variant_ == nullptr ? "" : std::string(variant_);

layout_ = new Layout{description, name, variant};
return layout_;
}

Language::XKBContext::~XKBContext() { rxkb_context_unref(context_); }
} // namespace waybar::modules::sway

0 comments on commit 3f3f2d9

Please sign in to comment.