mirror of
https://github.com/rad4day/Waybar.git
synced 2023-12-21 10:22:59 +01:00
Merge branch 'master' into keyboard_state
This commit is contained in:
commit
445ad22580
@ -3,5 +3,5 @@
|
|||||||
FROM debian:sid
|
FROM debian:sid
|
||||||
|
|
||||||
RUN apt-get update && \
|
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
|
apt-get clean
|
||||||
|
@ -3,5 +3,7 @@
|
|||||||
FROM opensuse/tumbleweed:latest
|
FROM opensuse/tumbleweed:latest
|
||||||
|
|
||||||
RUN zypper -n up && \
|
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 -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 <libdbusmenu-gtk/dbusmenu-gtk.h>
|
||||||
#include <sigc++/trackable.h>
|
#include <sigc++/trackable.h>
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
namespace waybar::modules::SNI {
|
namespace waybar::modules::SNI {
|
||||||
|
|
||||||
|
struct ToolTip {
|
||||||
|
Glib::ustring icon_name;
|
||||||
|
Glib::ustring text;
|
||||||
|
};
|
||||||
|
|
||||||
class Item : public sigc::trackable {
|
class Item : public sigc::trackable {
|
||||||
public:
|
public:
|
||||||
Item(const std::string&, const std::string&, const Json::Value&);
|
Item(const std::string&, const std::string&, const Json::Value&);
|
||||||
@ -27,10 +35,8 @@ class Item : public sigc::trackable {
|
|||||||
Gtk::EventBox event_box;
|
Gtk::EventBox event_box;
|
||||||
std::string category;
|
std::string category;
|
||||||
std::string id;
|
std::string id;
|
||||||
std::string status;
|
|
||||||
|
|
||||||
std::string title;
|
std::string title;
|
||||||
int32_t window_id;
|
|
||||||
std::string icon_name;
|
std::string icon_name;
|
||||||
Glib::RefPtr<Gdk::Pixbuf> icon_pixmap;
|
Glib::RefPtr<Gdk::Pixbuf> icon_pixmap;
|
||||||
Glib::RefPtr<Gtk::IconTheme> icon_theme;
|
Glib::RefPtr<Gtk::IconTheme> icon_theme;
|
||||||
@ -39,6 +45,7 @@ class Item : public sigc::trackable {
|
|||||||
std::string attention_movie_name;
|
std::string attention_movie_name;
|
||||||
std::string icon_theme_path;
|
std::string icon_theme_path;
|
||||||
std::string menu;
|
std::string menu;
|
||||||
|
ToolTip tooltip;
|
||||||
DbusmenuGtkMenu* dbus_menu = nullptr;
|
DbusmenuGtkMenu* dbus_menu = nullptr;
|
||||||
Gtk::Menu* gtk_menu = nullptr;
|
Gtk::Menu* gtk_menu = nullptr;
|
||||||
/**
|
/**
|
||||||
@ -51,6 +58,7 @@ class Item : public sigc::trackable {
|
|||||||
private:
|
private:
|
||||||
void proxyReady(Glib::RefPtr<Gio::AsyncResult>& result);
|
void proxyReady(Glib::RefPtr<Gio::AsyncResult>& result);
|
||||||
void setProperty(const Glib::ustring& name, Glib::VariantBase& value);
|
void setProperty(const Glib::ustring& name, Glib::VariantBase& value);
|
||||||
|
void setStatus(const Glib::ustring& value);
|
||||||
void getUpdatedProperties();
|
void getUpdatedProperties();
|
||||||
void processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& result);
|
void processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& result);
|
||||||
void onSignal(const Glib::ustring& sender_name, const Glib::ustring& signal_name,
|
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);
|
static void onMenuDestroyed(Item* self, GObject* old_menu_pointer);
|
||||||
void makeMenu();
|
void makeMenu();
|
||||||
bool handleClick(GdkEventButton* const& /*ev*/);
|
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::DBus::Proxy> proxy_;
|
||||||
Glib::RefPtr<Gio::Cancellable> cancellable_;
|
Glib::RefPtr<Gio::Cancellable> cancellable_;
|
||||||
bool update_pending_;
|
std::set<std::string_view> update_pending_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules::SNI
|
} // namespace waybar::modules::SNI
|
||||||
|
@ -29,4 +29,8 @@ enum ipc_command_type {
|
|||||||
IPC_EVENT_BINDING = ((1 << 31) | 5),
|
IPC_EVENT_BINDING = ((1 << 31) | 5),
|
||||||
IPC_EVENT_SHUTDOWN = ((1 << 31) | 6),
|
IPC_EVENT_SHUTDOWN = ((1 << 31) | 6),
|
||||||
IPC_EVENT_TICK = ((1 << 31) | 7),
|
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
|
#pragma once
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
#include <xkbcommon/xkbregistry.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "ALabel.hpp"
|
#include "ALabel.hpp"
|
||||||
#include "bar.hpp"
|
#include "bar.hpp"
|
||||||
#include "client.hpp"
|
#include "client.hpp"
|
||||||
@ -16,10 +21,37 @@ class Language : public ALabel, public sigc::trackable {
|
|||||||
auto update() -> void;
|
auto update() -> void;
|
||||||
|
|
||||||
private:
|
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 onEvent(const struct Ipc::ipc_response&);
|
||||||
void onCmd(const struct Ipc::ipc_response&);
|
void onCmd(const struct Ipc::ipc_response&);
|
||||||
|
|
||||||
std::string lang_;
|
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;
|
||||||
|
|
||||||
|
Layout layout_;
|
||||||
|
std::map<std::string, Layout> layouts_map_;
|
||||||
|
XKBContext xkb_context_;
|
||||||
|
bool is_variant_displayed;
|
||||||
|
|
||||||
util::JsonParser parser_;
|
util::JsonParser parser_;
|
||||||
std::mutex mutex_;
|
std::mutex mutex_;
|
||||||
Ipc ipc_;
|
Ipc ipc_;
|
||||||
|
15
include/util/string.hpp
Normal file
15
include/util/string.hpp
Normal file
@ -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*: ++
|
*format*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
default: {} ++
|
default: {} ++
|
||||||
The format, how information should be displayed. On {} data gets inserted.
|
The format, how layout should be displayed.
|
||||||
|
|
||||||
*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.
|
|
||||||
|
|
||||||
*tooltip*: ++
|
*tooltip*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
default: true ++
|
default: true ++
|
||||||
Option to disable tooltip on hover.
|
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
|
# EXAMPLES
|
||||||
|
|
||||||
```
|
```
|
||||||
"sway/language": {
|
"sway/language": {
|
||||||
"format": "{}",
|
"format": "{}",
|
||||||
"max-length": 50
|
},
|
||||||
|
|
||||||
|
"sway/language": {
|
||||||
|
"format": "{short} {variant}",
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -16,6 +16,15 @@ Addressed by *tray*
|
|||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
Defines the size of the tray icons.
|
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*: ++
|
*spacing*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
Defines the spacing between the tray icons.
|
Defines the spacing between the tray icons.
|
||||||
@ -37,3 +46,6 @@ Addressed by *tray*
|
|||||||
# STYLE
|
# STYLE
|
||||||
|
|
||||||
- *#tray*
|
- *#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'))
|
libudev = dependency('libudev', required: get_option('libudev'))
|
||||||
libevdev = dependency('libevdev', required: get_option('libevdev'))
|
libevdev = dependency('libevdev', required: get_option('libevdev'))
|
||||||
libmpdclient = dependency('libmpdclient', required: get_option('mpd'))
|
libmpdclient = dependency('libmpdclient', required: get_option('mpd'))
|
||||||
|
xkbregistry = dependency('xkbregistry')
|
||||||
|
|
||||||
libsndio = compiler.find_library('sndio', required: get_option('sndio'))
|
libsndio = compiler.find_library('sndio', required: get_option('sndio'))
|
||||||
if libsndio.found()
|
if libsndio.found()
|
||||||
@ -279,7 +280,8 @@ executable(
|
|||||||
libevdev,
|
libevdev,
|
||||||
gtk_layer_shell,
|
gtk_layer_shell,
|
||||||
libsndio,
|
libsndio,
|
||||||
tz_dep
|
tz_dep,
|
||||||
|
xkbregistry
|
||||||
],
|
],
|
||||||
include_directories: [include_directories('include')],
|
include_directories: [include_directories('include')],
|
||||||
install: true,
|
install: true,
|
||||||
|
@ -195,6 +195,15 @@ label:focus {
|
|||||||
background-color: #2980b9;
|
background-color: #2980b9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#tray > .passive {
|
||||||
|
-gtk-icon-effect: dim;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tray > .needs-attention {
|
||||||
|
-gtk-icon-effect: highlight;
|
||||||
|
background-color: #eb4d4b;
|
||||||
|
}
|
||||||
|
|
||||||
#idle_inhibitor {
|
#idle_inhibitor {
|
||||||
background-color: #2d3436;
|
background-color: #2d3436;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
#include "modules/sni/item.hpp"
|
#include "modules/sni/item.hpp"
|
||||||
|
|
||||||
|
#include <gdkmm/general.h>
|
||||||
#include <glibmm/main.h>
|
#include <glibmm/main.h>
|
||||||
|
#include <gtkmm/tooltip.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct fmt::formatter<Glib::ustring> : formatter<std::string> {
|
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),
|
object_path(op),
|
||||||
icon_size(16),
|
icon_size(16),
|
||||||
effective_icon_size(0),
|
effective_icon_size(0),
|
||||||
icon_theme(Gtk::IconTheme::create()),
|
icon_theme(Gtk::IconTheme::create()) {
|
||||||
update_pending_(false) {
|
|
||||||
if (config["icon-size"].isUInt()) {
|
if (config["icon-size"].isUInt()) {
|
||||||
icon_size = config["icon-size"].asUInt();
|
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(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_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();
|
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));
|
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);
|
spdlog::error("Invalid Status Notifier Item: {}, {}", bus_name, object_path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->updateImage();
|
this->updateImage();
|
||||||
// this->event_box.set_tooltip_text(this->title);
|
|
||||||
|
|
||||||
} catch (const Glib::Error& err) {
|
} catch (const Glib::Error& err) {
|
||||||
spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what());
|
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>
|
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();
|
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) {
|
void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
|
||||||
try {
|
try {
|
||||||
spdlog::trace("Set tray item property: {}.{} = {}", id.empty() ? bus_name : id, name, value);
|
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);
|
id = get_variant<std::string>(value);
|
||||||
} else if (name == "Title") {
|
} else if (name == "Title") {
|
||||||
title = get_variant<std::string>(value);
|
title = get_variant<std::string>(value);
|
||||||
|
if (tooltip.text.empty()) {
|
||||||
|
event_box.set_tooltip_markup(title);
|
||||||
|
}
|
||||||
} else if (name == "Status") {
|
} else if (name == "Status") {
|
||||||
status = get_variant<std::string>(value);
|
setStatus(get_variant<Glib::ustring>(value));
|
||||||
} else if (name == "WindowId") {
|
|
||||||
window_id = get_variant<int32_t>(value);
|
|
||||||
} else if (name == "IconName") {
|
} else if (name == "IconName") {
|
||||||
icon_name = get_variant<std::string>(value);
|
icon_name = get_variant<std::string>(value);
|
||||||
} else if (name == "IconPixmap") {
|
} else if (name == "IconPixmap") {
|
||||||
@ -121,7 +148,10 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
|
|||||||
} else if (name == "AttentionMovieName") {
|
} else if (name == "AttentionMovieName") {
|
||||||
attention_movie_name = get_variant<std::string>(value);
|
attention_movie_name = get_variant<std::string>(value);
|
||||||
} else if (name == "ToolTip") {
|
} 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") {
|
} else if (name == "IconThemePath") {
|
||||||
icon_theme_path = get_variant<std::string>(value);
|
icon_theme_path = get_variant<std::string>(value);
|
||||||
if (!icon_theme_path.empty()) {
|
if (!icon_theme_path.empty()) {
|
||||||
@ -148,9 +178,22 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Item::getUpdatedProperties() {
|
void Item::setStatus(const Glib::ustring& value) {
|
||||||
update_pending_ = false;
|
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(
|
auto params = Glib::VariantContainerBase::create_tuple(
|
||||||
{Glib::Variant<Glib::ustring>::create(SNI_INTERFACE_NAME)});
|
{Glib::Variant<Glib::ustring>::create(SNI_INTERFACE_NAME)});
|
||||||
proxy_->call("org.freedesktop.DBus.Properties.GetAll",
|
proxy_->call("org.freedesktop.DBus.Properties.GetAll",
|
||||||
@ -167,34 +210,49 @@ void Item::processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& _result) {
|
|||||||
auto properties = properties_variant.get();
|
auto properties = properties_variant.get();
|
||||||
|
|
||||||
for (const auto& [name, value] : properties) {
|
for (const auto& [name, value] : properties) {
|
||||||
Glib::VariantBase old_value;
|
if (update_pending_.count(name.raw())) {
|
||||||
proxy_->get_cached_property(old_value, name);
|
|
||||||
if (!old_value || !value.equal(old_value)) {
|
|
||||||
proxy_->set_cached_property(name, value);
|
|
||||||
setProperty(name, const_cast<Glib::VariantBase&>(value));
|
setProperty(name, const_cast<Glib::VariantBase&>(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->updateImage();
|
this->updateImage();
|
||||||
// this->event_box.set_tooltip_text(this->title);
|
|
||||||
} catch (const Glib::Error& err) {
|
} catch (const Glib::Error& err) {
|
||||||
spdlog::warn("Failed to update properties: {}", err.what());
|
spdlog::warn("Failed to update properties: {}", err.what());
|
||||||
} catch (const std::exception& err) {
|
} catch (const std::exception& err) {
|
||||||
spdlog::warn("Failed to update properties: {}", err.what());
|
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,
|
void Item::onSignal(const Glib::ustring& sender_name, const Glib::ustring& signal_name,
|
||||||
const Glib::VariantContainerBase& arguments) {
|
const Glib::VariantContainerBase& arguments) {
|
||||||
spdlog::trace("Tray item '{}' got signal {}", id, signal_name);
|
spdlog::trace("Tray item '{}' got signal {}", id, signal_name);
|
||||||
if (!update_pending_ && signal_name.compare(0, 3, "New") == 0) {
|
auto changed = signal2props.find(signal_name.raw());
|
||||||
|
if (changed != signal2props.end()) {
|
||||||
|
if (update_pending_.empty()) {
|
||||||
/* Debounce signals and schedule update of all properties.
|
/* Debounce signals and schedule update of all properties.
|
||||||
* Based on behavior of Plasma dataengine for StatusNotifierItem.
|
* Based on behavior of Plasma dataengine for StatusNotifierItem.
|
||||||
*/
|
*/
|
||||||
update_pending_ = true;
|
|
||||||
Glib::signal_timeout().connect_once(sigc::mem_fun(*this, &Item::getUpdatedProperties),
|
Glib::signal_timeout().connect_once(sigc::mem_fun(*this, &Item::getUpdatedProperties),
|
||||||
UPDATE_DEBOUNCE_TIME);
|
UPDATE_DEBOUNCE_TIME);
|
||||||
}
|
}
|
||||||
|
update_pending_.insert(changed->second.begin(), changed->second.end());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pixbuf_data_deleter(const guint8* data) { g_free((void*)data); }
|
static void pixbuf_data_deleter(const guint8* data) { g_free((void*)data); }
|
||||||
@ -252,33 +310,42 @@ Glib::RefPtr<Gdk::Pixbuf> Item::extractPixBuf(GVariant* variant) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Item::updateImage() {
|
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_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()) {
|
if (!icon_name.empty()) {
|
||||||
try {
|
try {
|
||||||
// Try to find icons specified by path and filename
|
// Try to find icons specified by path and filename
|
||||||
std::ifstream temp(icon_name);
|
std::ifstream temp(icon_name);
|
||||||
if (temp.is_open()) {
|
if (temp.is_open()) {
|
||||||
auto pixbuf = Gdk::Pixbuf::create_from_file(icon_name);
|
auto pixbuf = Gdk::Pixbuf::create_from_file(icon_name);
|
||||||
|
|
||||||
if (pixbuf->gobj() != nullptr) {
|
if (pixbuf->gobj() != nullptr) {
|
||||||
// An icon specified by path and filename may be the wrong size for
|
// An icon specified by path and filename may be the wrong size for
|
||||||
// the tray
|
// 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
|
// 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);
|
pixbuf = pixbuf->scale_simple(width, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR);
|
||||||
image.set(pixbuf);
|
|
||||||
|
auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, 0, image.get_window());
|
||||||
|
image.set(surface);
|
||||||
}
|
}
|
||||||
} else {
|
} 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) {
|
} catch (Glib::Error& e) {
|
||||||
spdlog::error("Item '{}': {}", id, static_cast<std::string>(e.what()));
|
spdlog::error("Item '{}': {}", id, static_cast<std::string>(e.what()));
|
||||||
}
|
}
|
||||||
} else if (icon_pixmap) {
|
} else if (icon_pixmap) {
|
||||||
// An icon extracted may be the wrong size for the tray
|
// 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);
|
image.set(icon_pixmap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -360,4 +427,52 @@ bool Item::handleClick(GdkEventButton* const& ev) {
|
|||||||
return false;
|
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
|
} // namespace waybar::modules::SNI
|
||||||
|
@ -1,10 +1,24 @@
|
|||||||
#include "modules/sway/language.hpp"
|
#include "modules/sway/language.hpp"
|
||||||
|
|
||||||
|
#include <fmt/core.h>
|
||||||
#include <spdlog/spdlog.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 {
|
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)
|
Language::Language(const std::string& id, const Json::Value& config)
|
||||||
: ALabel(config, "language", id, "{}", 0, true) {
|
: ALabel(config, "language", id, "{}", 0, true) {
|
||||||
|
is_variant_displayed = format_.find("{variant}") != std::string::npos;
|
||||||
ipc_.subscribe(R"(["input"])");
|
ipc_.subscribe(R"(["input"])");
|
||||||
ipc_.signal_event.connect(sigc::mem_fun(*this, &Language::onEvent));
|
ipc_.signal_event.connect(sigc::mem_fun(*this, &Language::onEvent));
|
||||||
ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Language::onCmd));
|
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) {
|
void Language::onCmd(const struct Ipc::ipc_response& res) {
|
||||||
|
if (res.type != static_cast<uint32_t>(IPC_GET_INPUTS)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
auto payload = parser_.parse(res.payload);
|
auto payload = parser_.parse(res.payload);
|
||||||
//Display current layout of a device with a maximum count of layouts, expecting that all will be OK
|
std::vector<std::string> used_layouts;
|
||||||
Json::Value::ArrayIndex maxId = 0, max = 0;
|
// Display current layout of a device with a maximum count of layouts, expecting that all will
|
||||||
for(Json::Value::ArrayIndex i = 0; i < payload.size(); i++) {
|
// be OK
|
||||||
if(payload[i]["xkb_layout_names"].size() > max) {
|
Json::ArrayIndex max_id = 0, max = 0;
|
||||||
max = payload[i]["xkb_layout_names"].size();
|
for (Json::ArrayIndex i = 0; i < payload.size(); i++) {
|
||||||
maxId = 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();
|
dp.emit();
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
spdlog::error("Language: {}", e.what());
|
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) {
|
void Language::onEvent(const struct Ipc::ipc_response& res) {
|
||||||
|
if (res.type != static_cast<uint32_t>(IPC_EVENT_INPUT)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
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") {
|
if (payload["type"].asString() == "keyboard") {
|
||||||
auto layout_name = payload["xkb_active_layout_name"].asString().substr(0,2);
|
set_current_layout(payload[XKB_ACTIVE_LAYOUT_NAME_KEY].asString());
|
||||||
if (!layout_name.empty()) {
|
|
||||||
lang_ = Glib::Markup::escape_text(layout_name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
dp.emit();
|
dp.emit();
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
@ -56,17 +84,93 @@ void Language::onEvent(const struct Ipc::ipc_response& res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto Language::update() -> void {
|
auto Language::update() -> void {
|
||||||
if (lang_.empty()) {
|
auto display_layout = trim(fmt::format(format_,
|
||||||
event_box_.hide();
|
fmt::arg("short", layout_.short_name),
|
||||||
} else {
|
fmt::arg("long", layout_.full_name),
|
||||||
label_.set_markup(fmt::format(format_, lang_));
|
fmt::arg("variant", layout_.variant)));
|
||||||
|
label_.set_markup(display_layout);
|
||||||
if (tooltipEnabled()) {
|
if (tooltipEnabled()) {
|
||||||
label_.set_tooltip_text(lang_);
|
label_.set_tooltip_markup(display_layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
event_box_.show();
|
event_box_.show();
|
||||||
}
|
|
||||||
// Call parent update
|
// Call parent update
|
||||||
ALabel::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
|
} // namespace waybar::modules::sway
|
||||||
|
Loading…
Reference in New Issue
Block a user