Merge branch 'master' into keyboard_state
This commit is contained in:
commit
445ad22580
|
@ -3,5 +3,5 @@
|
|||
FROM debian:sid
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon && \
|
||||
apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon-dev libxkbregistry-dev libxkbregistry0 && \
|
||||
apt-get clean
|
||||
|
|
|
@ -3,5 +3,7 @@
|
|||
FROM opensuse/tumbleweed:latest
|
||||
|
||||
RUN zypper -n up && \
|
||||
zypper addrepo https://download.opensuse.org/repositories/X11:Wayland/openSUSE_Tumbleweed/X11:Wayland.repo | echo 'a' && \
|
||||
zypper -n refresh && \
|
||||
zypper -n install -t pattern devel_C_C++ && \
|
||||
zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel scdoc libxkbcommon
|
||||
zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel libxkbregistry-devel scdoc
|
||||
|
|
|
@ -11,8 +11,16 @@
|
|||
#include <libdbusmenu-gtk/dbusmenu-gtk.h>
|
||||
#include <sigc++/trackable.h>
|
||||
|
||||
#include <set>
|
||||
#include <string_view>
|
||||
|
||||
namespace waybar::modules::SNI {
|
||||
|
||||
struct ToolTip {
|
||||
Glib::ustring icon_name;
|
||||
Glib::ustring text;
|
||||
};
|
||||
|
||||
class Item : public sigc::trackable {
|
||||
public:
|
||||
Item(const std::string&, const std::string&, const Json::Value&);
|
||||
|
@ -27,10 +35,8 @@ class Item : public sigc::trackable {
|
|||
Gtk::EventBox event_box;
|
||||
std::string category;
|
||||
std::string id;
|
||||
std::string status;
|
||||
|
||||
std::string title;
|
||||
int32_t window_id;
|
||||
std::string icon_name;
|
||||
Glib::RefPtr<Gdk::Pixbuf> icon_pixmap;
|
||||
Glib::RefPtr<Gtk::IconTheme> icon_theme;
|
||||
|
@ -39,6 +45,7 @@ class Item : public sigc::trackable {
|
|||
std::string attention_movie_name;
|
||||
std::string icon_theme_path;
|
||||
std::string menu;
|
||||
ToolTip tooltip;
|
||||
DbusmenuGtkMenu* dbus_menu = nullptr;
|
||||
Gtk::Menu* gtk_menu = nullptr;
|
||||
/**
|
||||
|
@ -51,6 +58,7 @@ class Item : public sigc::trackable {
|
|||
private:
|
||||
void proxyReady(Glib::RefPtr<Gio::AsyncResult>& result);
|
||||
void setProperty(const Glib::ustring& name, Glib::VariantBase& value);
|
||||
void setStatus(const Glib::ustring& value);
|
||||
void getUpdatedProperties();
|
||||
void processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& result);
|
||||
void onSignal(const Glib::ustring& sender_name, const Glib::ustring& signal_name,
|
||||
|
@ -62,10 +70,18 @@ class Item : public sigc::trackable {
|
|||
static void onMenuDestroyed(Item* self, GObject* old_menu_pointer);
|
||||
void makeMenu();
|
||||
bool handleClick(GdkEventButton* const& /*ev*/);
|
||||
bool handleScroll(GdkEventScroll* const&);
|
||||
|
||||
// smooth scrolling threshold
|
||||
gdouble scroll_threshold_ = 0;
|
||||
gdouble distance_scrolled_x_ = 0;
|
||||
gdouble distance_scrolled_y_ = 0;
|
||||
// visibility of items with Status == Passive
|
||||
bool show_passive_ = false;
|
||||
|
||||
Glib::RefPtr<Gio::DBus::Proxy> proxy_;
|
||||
Glib::RefPtr<Gio::Cancellable> cancellable_;
|
||||
bool update_pending_;
|
||||
std::set<std::string_view> update_pending_;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::SNI
|
||||
|
|
|
@ -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),
|
||||
};
|
||||
|
|
|
@ -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"
|
||||
|
@ -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
|
||||
|
|
|
@ -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)); }
|
|
@ -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}",
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -16,6 +16,15 @@ Addressed by *tray*
|
|||
typeof: integer ++
|
||||
Defines the size of the tray icons.
|
||||
|
||||
*show-passive-items*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
Defines visibility of the tray icons with *Passive* status.
|
||||
|
||||
*smooth-scrolling-threshold*: ++
|
||||
typeof: double ++
|
||||
Threshold to be used when scrolling.
|
||||
|
||||
*spacing*: ++
|
||||
typeof: integer ++
|
||||
Defines the spacing between the tray icons.
|
||||
|
@ -37,3 +46,6 @@ Addressed by *tray*
|
|||
# STYLE
|
||||
|
||||
- *#tray*
|
||||
- *#tray > .passive*
|
||||
- *#tray > .active*
|
||||
- *#tray > .needs-attention*
|
||||
|
|
|
@ -96,6 +96,7 @@ libpulse = dependency('libpulse', required: get_option('pulseaudio'))
|
|||
libudev = dependency('libudev', required: get_option('libudev'))
|
||||
libevdev = dependency('libevdev', required: get_option('libevdev'))
|
||||
libmpdclient = dependency('libmpdclient', required: get_option('mpd'))
|
||||
xkbregistry = dependency('xkbregistry')
|
||||
|
||||
libsndio = compiler.find_library('sndio', required: get_option('sndio'))
|
||||
if libsndio.found()
|
||||
|
@ -279,7 +280,8 @@ executable(
|
|||
libevdev,
|
||||
gtk_layer_shell,
|
||||
libsndio,
|
||||
tz_dep
|
||||
tz_dep,
|
||||
xkbregistry
|
||||
],
|
||||
include_directories: [include_directories('include')],
|
||||
install: true,
|
||||
|
|
|
@ -195,6 +195,15 @@ label:focus {
|
|||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
#tray > .passive {
|
||||
-gtk-icon-effect: dim;
|
||||
}
|
||||
|
||||
#tray > .needs-attention {
|
||||
-gtk-icon-effect: highlight;
|
||||
background-color: #eb4d4b;
|
||||
}
|
||||
|
||||
#idle_inhibitor {
|
||||
background-color: #2d3436;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
#include "modules/sni/item.hpp"
|
||||
|
||||
#include <gdkmm/general.h>
|
||||
#include <glibmm/main.h>
|
||||
#include <gtkmm/tooltip.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Glib::ustring> : formatter<std::string> {
|
||||
|
@ -39,14 +44,22 @@ Item::Item(const std::string& bn, const std::string& op, const Json::Value& conf
|
|||
object_path(op),
|
||||
icon_size(16),
|
||||
effective_icon_size(0),
|
||||
icon_theme(Gtk::IconTheme::create()),
|
||||
update_pending_(false) {
|
||||
icon_theme(Gtk::IconTheme::create()) {
|
||||
if (config["icon-size"].isUInt()) {
|
||||
icon_size = config["icon-size"].asUInt();
|
||||
}
|
||||
if (config["smooth-scrolling-threshold"].isNumeric()) {
|
||||
scroll_threshold_ = config["smooth-scrolling-threshold"].asDouble();
|
||||
}
|
||||
if (config["show-passive-items"].isBool()) {
|
||||
show_passive_ = config["show-passive-items"].asBool();
|
||||
}
|
||||
event_box.add(image);
|
||||
event_box.add_events(Gdk::BUTTON_PRESS_MASK);
|
||||
event_box.add_events(Gdk::BUTTON_PRESS_MASK | Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
|
||||
event_box.signal_button_press_event().connect(sigc::mem_fun(*this, &Item::handleClick));
|
||||
event_box.signal_scroll_event().connect(sigc::mem_fun(*this, &Item::handleScroll));
|
||||
// initial visibility
|
||||
event_box.set_visible(show_passive_);
|
||||
|
||||
cancellable_ = Gio::Cancellable::create();
|
||||
|
||||
|
@ -73,12 +86,11 @@ void Item::proxyReady(Glib::RefPtr<Gio::AsyncResult>& result) {
|
|||
|
||||
this->proxy_->signal_signal().connect(sigc::mem_fun(*this, &Item::onSignal));
|
||||
|
||||
if (this->id.empty() || this->category.empty() || this->status.empty()) {
|
||||
if (this->id.empty() || this->category.empty()) {
|
||||
spdlog::error("Invalid Status Notifier Item: {}, {}", bus_name, object_path);
|
||||
return;
|
||||
}
|
||||
this->updateImage();
|
||||
// this->event_box.set_tooltip_text(this->title);
|
||||
|
||||
} catch (const Glib::Error& err) {
|
||||
spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what());
|
||||
|
@ -88,10 +100,24 @@ void Item::proxyReady(Glib::RefPtr<Gio::AsyncResult>& result) {
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
T get_variant(Glib::VariantBase& value) {
|
||||
T get_variant(const Glib::VariantBase& value) {
|
||||
return Glib::VariantBase::cast_dynamic<Glib::Variant<T>>(value).get();
|
||||
}
|
||||
|
||||
template <>
|
||||
ToolTip get_variant<ToolTip>(const Glib::VariantBase& value) {
|
||||
ToolTip result;
|
||||
// Unwrap (sa(iiay)ss)
|
||||
auto container = value.cast_dynamic<Glib::VariantContainerBase>(value);
|
||||
result.icon_name = get_variant<Glib::ustring>(container.get_child(0));
|
||||
result.text = get_variant<Glib::ustring>(container.get_child(2));
|
||||
auto description = get_variant<Glib::ustring>(container.get_child(3));
|
||||
if (!description.empty()) {
|
||||
result.text = fmt::format("<b>{}</b>\n{}", result.text, description);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
|
||||
try {
|
||||
spdlog::trace("Set tray item property: {}.{} = {}", id.empty() ? bus_name : id, name, value);
|
||||
|
@ -102,10 +128,11 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
|
|||
id = get_variant<std::string>(value);
|
||||
} else if (name == "Title") {
|
||||
title = get_variant<std::string>(value);
|
||||
if (tooltip.text.empty()) {
|
||||
event_box.set_tooltip_markup(title);
|
||||
}
|
||||
} else if (name == "Status") {
|
||||
status = get_variant<std::string>(value);
|
||||
} else if (name == "WindowId") {
|
||||
window_id = get_variant<int32_t>(value);
|
||||
setStatus(get_variant<Glib::ustring>(value));
|
||||
} else if (name == "IconName") {
|
||||
icon_name = get_variant<std::string>(value);
|
||||
} else if (name == "IconPixmap") {
|
||||
|
@ -121,7 +148,10 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
|
|||
} else if (name == "AttentionMovieName") {
|
||||
attention_movie_name = get_variant<std::string>(value);
|
||||
} else if (name == "ToolTip") {
|
||||
// TODO: tooltip
|
||||
tooltip = get_variant<ToolTip>(value);
|
||||
if (!tooltip.text.empty()) {
|
||||
event_box.set_tooltip_markup(tooltip.text);
|
||||
}
|
||||
} else if (name == "IconThemePath") {
|
||||
icon_theme_path = get_variant<std::string>(value);
|
||||
if (!icon_theme_path.empty()) {
|
||||
|
@ -148,9 +178,22 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
|
|||
}
|
||||
}
|
||||
|
||||
void Item::getUpdatedProperties() {
|
||||
update_pending_ = false;
|
||||
void Item::setStatus(const Glib::ustring& value) {
|
||||
Glib::ustring lower = value.lowercase();
|
||||
event_box.set_visible(show_passive_ || lower.compare("passive") != 0);
|
||||
|
||||
auto style = event_box.get_style_context();
|
||||
for (const auto& class_name : style->list_classes()) {
|
||||
style->remove_class(class_name);
|
||||
}
|
||||
if (lower.compare("needsattention") == 0) {
|
||||
// convert status to dash-case for CSS
|
||||
lower = "needs-attention";
|
||||
}
|
||||
style->add_class(lower);
|
||||
}
|
||||
|
||||
void Item::getUpdatedProperties() {
|
||||
auto params = Glib::VariantContainerBase::create_tuple(
|
||||
{Glib::Variant<Glib::ustring>::create(SNI_INTERFACE_NAME)});
|
||||
proxy_->call("org.freedesktop.DBus.Properties.GetAll",
|
||||
|
@ -167,33 +210,48 @@ void Item::processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& _result) {
|
|||
auto properties = properties_variant.get();
|
||||
|
||||
for (const auto& [name, value] : properties) {
|
||||
Glib::VariantBase old_value;
|
||||
proxy_->get_cached_property(old_value, name);
|
||||
if (!old_value || !value.equal(old_value)) {
|
||||
proxy_->set_cached_property(name, value);
|
||||
if (update_pending_.count(name.raw())) {
|
||||
setProperty(name, const_cast<Glib::VariantBase&>(value));
|
||||
}
|
||||
}
|
||||
|
||||
this->updateImage();
|
||||
// this->event_box.set_tooltip_text(this->title);
|
||||
} catch (const Glib::Error& err) {
|
||||
spdlog::warn("Failed to update properties: {}", err.what());
|
||||
} catch (const std::exception& err) {
|
||||
spdlog::warn("Failed to update properties: {}", err.what());
|
||||
}
|
||||
update_pending_.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapping from a signal name to a set of possibly changed properties.
|
||||
* Commented signals are not handled by the tray module at the moment.
|
||||
*/
|
||||
static const std::map<std::string_view, std::set<std::string_view>> signal2props = {
|
||||
{"NewTitle", {"Title"}},
|
||||
{"NewIcon", {"IconName", "IconPixmap"}},
|
||||
// {"NewAttentionIcon", {"AttentionIconName", "AttentionIconPixmap", "AttentionMovieName"}},
|
||||
// {"NewOverlayIcon", {"OverlayIconName", "OverlayIconPixmap"}},
|
||||
{"NewIconThemePath", {"IconThemePath"}},
|
||||
{"NewToolTip", {"ToolTip"}},
|
||||
{"NewStatus", {"Status"}},
|
||||
// {"XAyatanaNewLabel", {"XAyatanaLabel"}},
|
||||
};
|
||||
|
||||
void Item::onSignal(const Glib::ustring& sender_name, const Glib::ustring& signal_name,
|
||||
const Glib::VariantContainerBase& arguments) {
|
||||
spdlog::trace("Tray item '{}' got signal {}", id, signal_name);
|
||||
if (!update_pending_ && signal_name.compare(0, 3, "New") == 0) {
|
||||
/* Debounce signals and schedule update of all properties.
|
||||
* Based on behavior of Plasma dataengine for StatusNotifierItem.
|
||||
*/
|
||||
update_pending_ = true;
|
||||
Glib::signal_timeout().connect_once(sigc::mem_fun(*this, &Item::getUpdatedProperties),
|
||||
UPDATE_DEBOUNCE_TIME);
|
||||
auto changed = signal2props.find(signal_name.raw());
|
||||
if (changed != signal2props.end()) {
|
||||
if (update_pending_.empty()) {
|
||||
/* Debounce signals and schedule update of all properties.
|
||||
* Based on behavior of Plasma dataengine for StatusNotifierItem.
|
||||
*/
|
||||
Glib::signal_timeout().connect_once(sigc::mem_fun(*this, &Item::getUpdatedProperties),
|
||||
UPDATE_DEBOUNCE_TIME);
|
||||
}
|
||||
update_pending_.insert(changed->second.begin(), changed->second.end());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -252,33 +310,42 @@ Glib::RefPtr<Gdk::Pixbuf> Item::extractPixBuf(GVariant* variant) {
|
|||
}
|
||||
|
||||
void Item::updateImage() {
|
||||
auto scale_factor = image.get_scale_factor();
|
||||
auto scaled_icon_size = icon_size * scale_factor;
|
||||
|
||||
image.set_from_icon_name("image-missing", Gtk::ICON_SIZE_MENU);
|
||||
image.set_pixel_size(icon_size);
|
||||
image.set_pixel_size(scaled_icon_size);
|
||||
if (!icon_name.empty()) {
|
||||
try {
|
||||
// Try to find icons specified by path and filename
|
||||
std::ifstream temp(icon_name);
|
||||
if (temp.is_open()) {
|
||||
auto pixbuf = Gdk::Pixbuf::create_from_file(icon_name);
|
||||
|
||||
if (pixbuf->gobj() != nullptr) {
|
||||
// An icon specified by path and filename may be the wrong size for
|
||||
// the tray
|
||||
// Keep the aspect ratio and scale to make the height equal to icon_size
|
||||
// Keep the aspect ratio and scale to make the height equal to scaled_icon_size
|
||||
// If people have non square icons, assume they want it to grow in width not height
|
||||
int width = icon_size * pixbuf->get_width() / pixbuf->get_height();
|
||||
int width = scaled_icon_size * pixbuf->get_width() / pixbuf->get_height();
|
||||
|
||||
pixbuf = pixbuf->scale_simple(width, icon_size, Gdk::InterpType::INTERP_BILINEAR);
|
||||
image.set(pixbuf);
|
||||
pixbuf = pixbuf->scale_simple(width, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR);
|
||||
|
||||
auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, 0, image.get_window());
|
||||
image.set(surface);
|
||||
}
|
||||
} else {
|
||||
image.set(getIconByName(icon_name, icon_size));
|
||||
auto icon_by_name = getIconByName(icon_name, scaled_icon_size);
|
||||
auto surface = Gdk::Cairo::create_surface_from_pixbuf(icon_by_name, 0, image.get_window());
|
||||
image.set(surface);
|
||||
}
|
||||
} catch (Glib::Error& e) {
|
||||
spdlog::error("Item '{}': {}", id, static_cast<std::string>(e.what()));
|
||||
}
|
||||
} else if (icon_pixmap) {
|
||||
// An icon extracted may be the wrong size for the tray
|
||||
icon_pixmap = icon_pixmap->scale_simple(icon_size, icon_size, Gdk::InterpType::INTERP_BILINEAR);
|
||||
icon_pixmap = icon_pixmap->scale_simple(icon_size, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR);
|
||||
auto surface = Gdk::Cairo::create_surface_from_pixbuf(icon_pixmap, 0, image.get_window());
|
||||
image.set(icon_pixmap);
|
||||
}
|
||||
}
|
||||
|
@ -360,4 +427,52 @@ bool Item::handleClick(GdkEventButton* const& ev) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Item::handleScroll(GdkEventScroll* const& ev) {
|
||||
int dx = 0, dy = 0;
|
||||
switch (ev->direction) {
|
||||
case GDK_SCROLL_UP:
|
||||
dy = -1;
|
||||
break;
|
||||
case GDK_SCROLL_DOWN:
|
||||
dy = 1;
|
||||
break;
|
||||
case GDK_SCROLL_LEFT:
|
||||
dx = -1;
|
||||
break;
|
||||
case GDK_SCROLL_RIGHT:
|
||||
dx = 1;
|
||||
break;
|
||||
case GDK_SCROLL_SMOOTH:
|
||||
distance_scrolled_x_ += ev->delta_x;
|
||||
distance_scrolled_y_ += ev->delta_y;
|
||||
// check against the configured threshold and ensure that the absolute value >= 1
|
||||
if (distance_scrolled_x_ > scroll_threshold_) {
|
||||
dx = (int)lround(std::max(distance_scrolled_x_, 1.0));
|
||||
distance_scrolled_x_ = 0;
|
||||
} else if (distance_scrolled_x_ < -scroll_threshold_) {
|
||||
dx = (int)lround(std::min(distance_scrolled_x_, -1.0));
|
||||
distance_scrolled_x_ = 0;
|
||||
}
|
||||
if (distance_scrolled_y_ > scroll_threshold_) {
|
||||
dy = (int)lround(std::max(distance_scrolled_y_, 1.0));
|
||||
distance_scrolled_y_ = 0;
|
||||
} else if (distance_scrolled_y_ < -scroll_threshold_) {
|
||||
dy = (int)lround(std::min(distance_scrolled_y_, -1.0));
|
||||
distance_scrolled_y_ = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (dx != 0) {
|
||||
auto parameters = Glib::VariantContainerBase::create_tuple(
|
||||
{Glib::Variant<int>::create(dx), Glib::Variant<Glib::ustring>::create("horizontal")});
|
||||
proxy_->call("Scroll", parameters);
|
||||
}
|
||||
if (dy != 0) {
|
||||
auto parameters = Glib::VariantContainerBase::create_tuple(
|
||||
{Glib::Variant<int>::create(dy), Glib::Variant<Glib::ustring>::create("vertical")});
|
||||
proxy_->call("Scroll", parameters);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace waybar::modules::SNI
|
||||
|
|
|
@ -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));
|
||||
|
@ -21,18 +35,31 @@ 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());
|
||||
|
@ -40,14 +67,15 @@ void Language::onCmd(const struct Ipc::ipc_response& res) {
|
|||
}
|
||||
|
||||
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) {
|
||||
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue