mirror of
				https://github.com/rad4day/Waybar.git
				synced 2025-11-04 01:32:42 +01:00 
			
		
		
		
	@@ -2,14 +2,10 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include <unistd.h>
 | 
					#include <unistd.h>
 | 
				
			||||||
#include <wordexp.h>
 | 
					#include <wordexp.h>
 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <fmt/format.h>
 | 
					#include <fmt/format.h>
 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <gdk/gdk.h>
 | 
					#include <gdk/gdk.h>
 | 
				
			||||||
#include <wayland-client.h>
 | 
					#include <wayland-client.h>
 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <gdk/gdkwayland.h>
 | 
					#include <gdk/gdkwayland.h>
 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "bar.hpp"
 | 
					#include "bar.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace waybar {
 | 
					namespace waybar {
 | 
				
			||||||
@@ -30,14 +26,14 @@ class Client {
 | 
				
			|||||||
    struct wl_seat *seat = nullptr;
 | 
					    struct wl_seat *seat = nullptr;
 | 
				
			||||||
    std::vector<std::unique_ptr<Bar>> bars;
 | 
					    std::vector<std::unique_ptr<Bar>> bars;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					  private:
 | 
				
			||||||
  void bindInterfaces();
 | 
					    void bindInterfaces();
 | 
				
			||||||
  auto setupCss();
 | 
					    auto setupCss();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static void handleGlobal(void *data, struct wl_registry *registry,
 | 
					    static void handleGlobal(void *data, struct wl_registry *registry,
 | 
				
			||||||
    uint32_t name, const char *interface, uint32_t version);
 | 
					      uint32_t name, const char *interface, uint32_t version);
 | 
				
			||||||
  static void handleGlobalRemove(void *data,
 | 
					    static void handleGlobalRemove(void *data,
 | 
				
			||||||
    struct wl_registry *registry, uint32_t name);
 | 
					      struct wl_registry *registry, uint32_t name);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,7 @@
 | 
				
			|||||||
#include "modules/battery.hpp"
 | 
					#include "modules/battery.hpp"
 | 
				
			||||||
#include "modules/memory.hpp"
 | 
					#include "modules/memory.hpp"
 | 
				
			||||||
#include "modules/cpu.hpp"
 | 
					#include "modules/cpu.hpp"
 | 
				
			||||||
 | 
					#include "modules/sni/tray.hpp"
 | 
				
			||||||
#ifdef HAVE_LIBNL
 | 
					#ifdef HAVE_LIBNL
 | 
				
			||||||
#include "modules/network.hpp"
 | 
					#include "modules/network.hpp"
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										39
									
								
								include/modules/sni/snh.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								include/modules/sni/snh.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <gtkmm.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <tuple>
 | 
				
			||||||
 | 
					#include <dbus-status-notifier-watcher.h>
 | 
				
			||||||
 | 
					#include "modules/sni/sni.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace waybar::modules::SNI {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Host {
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    Host(Glib::Dispatcher*);
 | 
				
			||||||
 | 
					    std::vector<Item> items;
 | 
				
			||||||
 | 
					  private:
 | 
				
			||||||
 | 
					    static void busAcquired(GDBusConnection*, const gchar*, gpointer);
 | 
				
			||||||
 | 
					    static void nameAppeared(GDBusConnection*, const gchar*, const gchar*,
 | 
				
			||||||
 | 
					      gpointer);
 | 
				
			||||||
 | 
					    static void nameVanished(GDBusConnection*, const gchar*, gpointer);
 | 
				
			||||||
 | 
					    static void proxyReady(GObject*, GAsyncResult*, gpointer);
 | 
				
			||||||
 | 
					    static void registerHost(GObject*, GAsyncResult*, gpointer);
 | 
				
			||||||
 | 
					    static void itemRegistered(SnOrgKdeStatusNotifierWatcher*, const gchar*,
 | 
				
			||||||
 | 
					      gpointer);
 | 
				
			||||||
 | 
					    static void itemUnregistered(SnOrgKdeStatusNotifierWatcher*, const gchar*,
 | 
				
			||||||
 | 
					      gpointer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::tuple<std::string, std::string> getBusNameAndObjectPath(const gchar*);
 | 
				
			||||||
 | 
					    void addRegisteredItem(const gchar* service);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    uint32_t bus_name_id_;
 | 
				
			||||||
 | 
					    uint32_t watcher_id_;
 | 
				
			||||||
 | 
					    std::string bus_name_;
 | 
				
			||||||
 | 
					    std::string object_path_;
 | 
				
			||||||
 | 
					    Glib::Dispatcher* dp_;
 | 
				
			||||||
 | 
					    GCancellable* cancellable_ = nullptr;
 | 
				
			||||||
 | 
					    SnOrgKdeStatusNotifierWatcher* watcher_ = nullptr;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										51
									
								
								include/modules/sni/sni.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								include/modules/sni/sni.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <dbus-status-notifier-item.h>
 | 
				
			||||||
 | 
					#include <gtkmm.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace waybar::modules::SNI {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Item {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					  Item(std::string, std::string, Glib::Dispatcher *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  std::string bus_name;
 | 
				
			||||||
 | 
					  std::string object_path;
 | 
				
			||||||
 | 
					  Gtk::EventBox event_box;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  int icon_size;
 | 
				
			||||||
 | 
					  int effective_icon_size;
 | 
				
			||||||
 | 
					  Gtk::Image *image;
 | 
				
			||||||
 | 
					  Gtk::Menu *gtk_menu = nullptr;
 | 
				
			||||||
 | 
					  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;
 | 
				
			||||||
 | 
					  std::string overlay_icon_name;
 | 
				
			||||||
 | 
					  std::string attention_icon_name;
 | 
				
			||||||
 | 
					  std::string attention_movie_name;
 | 
				
			||||||
 | 
					  std::string icon_theme_path;
 | 
				
			||||||
 | 
					  std::string menu;
 | 
				
			||||||
 | 
					  bool item_is_menu;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					  static void proxyReady(GObject *obj, GAsyncResult *res, gpointer data);
 | 
				
			||||||
 | 
					  static void getAll(GObject *obj, GAsyncResult *res, gpointer data);
 | 
				
			||||||
 | 
					  static void handleActivate(GObject *, GAsyncResult *, gpointer);
 | 
				
			||||||
 | 
					  static void handleSecondaryActivate(GObject *, GAsyncResult *, gpointer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void updateImage();
 | 
				
			||||||
 | 
					  Glib::RefPtr<Gdk::Pixbuf> extractPixBuf(GVariant *variant);
 | 
				
			||||||
 | 
					  Glib::RefPtr<Gdk::Pixbuf> getIconByName(std::string name, int size);
 | 
				
			||||||
 | 
					  bool handleClick(GdkEventButton *const & /*ev*/);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Glib::Dispatcher *dp_;
 | 
				
			||||||
 | 
					  GCancellable *cancellable_ = nullptr;
 | 
				
			||||||
 | 
					  SnOrgKdeStatusNotifierItem *proxy_ = nullptr;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace waybar::modules::SNI
 | 
				
			||||||
							
								
								
									
										47
									
								
								include/modules/sni/snw.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								include/modules/sni/snw.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <gtkmm.h>
 | 
				
			||||||
 | 
					#include <dbus-status-notifier-watcher.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace waybar::modules::SNI {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Watcher {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					  Watcher();
 | 
				
			||||||
 | 
					  ~Watcher();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					  typedef enum { GF_WATCH_TYPE_HOST, GF_WATCH_TYPE_ITEM } GfWatchType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  typedef struct {
 | 
				
			||||||
 | 
					    GfWatchType type;
 | 
				
			||||||
 | 
					    Watcher *watcher;
 | 
				
			||||||
 | 
					    gchar *service;
 | 
				
			||||||
 | 
					    gchar *bus_name;
 | 
				
			||||||
 | 
					    gchar *object_path;
 | 
				
			||||||
 | 
					    guint watch_id;
 | 
				
			||||||
 | 
					  } GfWatch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static void busAcquired(GDBusConnection *, const gchar *, gpointer);
 | 
				
			||||||
 | 
					  static gboolean handleRegisterHost(Watcher *, GDBusMethodInvocation *,
 | 
				
			||||||
 | 
					                                     const gchar *);
 | 
				
			||||||
 | 
					  static gboolean handleRegisterItem(Watcher *, GDBusMethodInvocation *,
 | 
				
			||||||
 | 
					                                     const gchar *);
 | 
				
			||||||
 | 
					  static GfWatch *gfWatchFind(GSList *list, const gchar *bus_name,
 | 
				
			||||||
 | 
					                              const gchar *object_path);
 | 
				
			||||||
 | 
					  static GfWatch *gfWatchNew(GfWatchType, const gchar *, const gchar *,
 | 
				
			||||||
 | 
					                             const gchar *, Watcher *);
 | 
				
			||||||
 | 
					  static void nameVanished(GDBusConnection *connection, const char *name,
 | 
				
			||||||
 | 
					                           gpointer data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void updateRegisteredItems(SnOrgKdeStatusNotifierWatcher *obj);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uint32_t bus_name_id_;
 | 
				
			||||||
 | 
					  uint32_t watcher_id_;
 | 
				
			||||||
 | 
					  GSList *hosts_ = nullptr;
 | 
				
			||||||
 | 
					  GSList *items_ = nullptr;
 | 
				
			||||||
 | 
					  SnOrgKdeStatusNotifierWatcher *watcher_ = nullptr;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace waybar::modules::SNI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										25
									
								
								include/modules/sni/tray.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								include/modules/sni/tray.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <fmt/format.h>
 | 
				
			||||||
 | 
					#include <thread>
 | 
				
			||||||
 | 
					#include "util/json.hpp"
 | 
				
			||||||
 | 
					#include "IModule.hpp"
 | 
				
			||||||
 | 
					#include "modules/sni/snw.hpp"
 | 
				
			||||||
 | 
					#include "modules/sni/snh.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace waybar::modules::SNI {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Tray : public IModule {
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    Tray(const Json::Value&);
 | 
				
			||||||
 | 
					    auto update() -> void;
 | 
				
			||||||
 | 
					    operator Gtk::Widget &();
 | 
				
			||||||
 | 
					  private:
 | 
				
			||||||
 | 
					    std::thread thread_;
 | 
				
			||||||
 | 
					    const Json::Value& config_;
 | 
				
			||||||
 | 
					    Gtk::Box box_;
 | 
				
			||||||
 | 
					    SNI::Watcher watcher_ ;
 | 
				
			||||||
 | 
					    SNI::Host host_;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -32,6 +32,7 @@ wayland_cursor = dependency('wayland-cursor')
 | 
				
			|||||||
wayland_protos = dependency('wayland-protocols')
 | 
					wayland_protos = dependency('wayland-protocols')
 | 
				
			||||||
wlroots = dependency('wlroots', fallback: ['wlroots', 'wlroots'])
 | 
					wlroots = dependency('wlroots', fallback: ['wlroots', 'wlroots'])
 | 
				
			||||||
gtkmm = dependency('gtkmm-3.0')
 | 
					gtkmm = dependency('gtkmm-3.0')
 | 
				
			||||||
 | 
					dbusmenu_gtk = dependency('dbusmenu-gtk3-0.4')
 | 
				
			||||||
jsoncpp = dependency('jsoncpp')
 | 
					jsoncpp = dependency('jsoncpp')
 | 
				
			||||||
sigcpp = dependency('sigc++-2.0')
 | 
					sigcpp = dependency('sigc++-2.0')
 | 
				
			||||||
libnl = dependency('libnl-3.0', required: get_option('libnl'))
 | 
					libnl = dependency('libnl-3.0', required: get_option('libnl'))
 | 
				
			||||||
@@ -46,6 +47,10 @@ src_files = files(
 | 
				
			|||||||
    'src/modules/clock.cpp',
 | 
					    'src/modules/clock.cpp',
 | 
				
			||||||
    'src/modules/custom.cpp',
 | 
					    'src/modules/custom.cpp',
 | 
				
			||||||
    'src/modules/cpu.cpp',
 | 
					    'src/modules/cpu.cpp',
 | 
				
			||||||
 | 
					    'src/modules/sni/tray.cpp',
 | 
				
			||||||
 | 
					    'src/modules/sni/snw.cpp',
 | 
				
			||||||
 | 
					    'src/modules/sni/snh.cpp',
 | 
				
			||||||
 | 
					    'src/modules/sni/sni.cpp',
 | 
				
			||||||
    'src/main.cpp',
 | 
					    'src/main.cpp',
 | 
				
			||||||
    'src/bar.cpp',
 | 
					    'src/bar.cpp',
 | 
				
			||||||
    'src/client.cpp'
 | 
					    'src/client.cpp'
 | 
				
			||||||
@@ -86,9 +91,10 @@ executable(
 | 
				
			|||||||
        libinput,
 | 
					        libinput,
 | 
				
			||||||
        wayland_cursor,
 | 
					        wayland_cursor,
 | 
				
			||||||
        gtkmm,
 | 
					        gtkmm,
 | 
				
			||||||
 | 
					        dbusmenu_gtk,
 | 
				
			||||||
        libnl,
 | 
					        libnl,
 | 
				
			||||||
        libnlgen,
 | 
					        libnlgen,
 | 
				
			||||||
        libpulse,
 | 
					        libpulse
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    include_directories: [include_directories('include')],
 | 
					    include_directories: [include_directories('include')],
 | 
				
			||||||
    install: true,
 | 
					    install: true,
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										69
									
								
								protocol/dbus-menu.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								protocol/dbus-menu.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
				
			|||||||
 | 
					<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
 | 
				
			||||||
 | 
					"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
 | 
				
			||||||
 | 
					<node>
 | 
				
			||||||
 | 
					<interface name="com.canonical.dbusmenu">
 | 
				
			||||||
 | 
					    <!-- Properties -->
 | 
				
			||||||
 | 
					    <property name="Version" type="u" access="read" />
 | 
				
			||||||
 | 
					    <property name="TextDirection" type="s" access="read" />
 | 
				
			||||||
 | 
					    <property name="Status" type="s" access="read" />
 | 
				
			||||||
 | 
					    <property name="IconThemePath" type="as" access="read" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Functions -->
 | 
				
			||||||
 | 
					    <method name="GetLayout">
 | 
				
			||||||
 | 
					        <arg type="i" name="parentId" direction="in" />
 | 
				
			||||||
 | 
					        <arg type="i" name="recursionDepth" direction="in" />
 | 
				
			||||||
 | 
					        <arg type="as" name="propertyNames" direction="in" />
 | 
				
			||||||
 | 
					        <arg type="u" name="revision" direction="out" />
 | 
				
			||||||
 | 
					        <arg type="(ia{sv}av)" name="layout" direction="out" />
 | 
				
			||||||
 | 
					    </method>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <method name="GetGroupProperties">
 | 
				
			||||||
 | 
					        <arg type="ai" name="ids" direction="in" />
 | 
				
			||||||
 | 
					        <arg type="as" name="propertyNames" direction="in" />
 | 
				
			||||||
 | 
					        <arg type="a(ia{sv})" name="properties" direction="out" />
 | 
				
			||||||
 | 
					    </method>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <method name="GetProperty">
 | 
				
			||||||
 | 
					        <arg type="i" name="id" direction="in" />
 | 
				
			||||||
 | 
					        <arg type="s" name="name" direction="in" />
 | 
				
			||||||
 | 
					        <arg type="v" name="value" direction="out" />
 | 
				
			||||||
 | 
					    </method>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <method name="Event">
 | 
				
			||||||
 | 
					        <arg type="i" name="id" direction="in" />
 | 
				
			||||||
 | 
					        <arg type="s" name="eventId" direction="in" />
 | 
				
			||||||
 | 
					        <arg type="v" name="data" direction="in" />
 | 
				
			||||||
 | 
					        <arg type="u" name="timestamp" direction="in" />
 | 
				
			||||||
 | 
					    </method>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <method name="EventGroup">
 | 
				
			||||||
 | 
					        <arg type="a(isvu)" name="events" direction="in" />
 | 
				
			||||||
 | 
					        <arg type="ai" name="idErrors" direction="out" />
 | 
				
			||||||
 | 
					    </method>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <method name="AboutToShow">
 | 
				
			||||||
 | 
					        <arg type="i" name="id" direction="in" />
 | 
				
			||||||
 | 
					        <arg type="b" name="needUpdate" direction="out" />
 | 
				
			||||||
 | 
					    </method>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <method name="AboutToShowGroup">
 | 
				
			||||||
 | 
					        <arg type="ai" name="ids" direction="in" />
 | 
				
			||||||
 | 
					        <arg type="ai" name="updatesNeeded" direction="out" />
 | 
				
			||||||
 | 
					        <arg type="ai" name="idErrors" direction="out" />
 | 
				
			||||||
 | 
					    </method>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Signals -->
 | 
				
			||||||
 | 
					    <signal name="ItemsPropertiesUpdated">
 | 
				
			||||||
 | 
					        <arg type="a(ia{sv})" name="updatedProps" direction="out" />
 | 
				
			||||||
 | 
					        <arg type="a(ias)" name="removedProps" direction="out" />
 | 
				
			||||||
 | 
					    </signal>
 | 
				
			||||||
 | 
					    <signal name="LayoutUpdated">
 | 
				
			||||||
 | 
					        <arg type="u" name="revision" direction="out" />
 | 
				
			||||||
 | 
					        <arg type="i" name="parent" direction="out" />
 | 
				
			||||||
 | 
					    </signal>
 | 
				
			||||||
 | 
					    <signal name="ItemActivationRequested">
 | 
				
			||||||
 | 
					        <arg type="i" name="id" direction="out" />
 | 
				
			||||||
 | 
					        <arg type="u" name="timestamp" direction="out" />
 | 
				
			||||||
 | 
					    </signal>
 | 
				
			||||||
 | 
					</interface>
 | 
				
			||||||
 | 
					</node>
 | 
				
			||||||
							
								
								
									
										77
									
								
								protocol/dbus-status-notifier-item.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								protocol/dbus-status-notifier-item.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,77 @@
 | 
				
			|||||||
 | 
					<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
 | 
				
			||||||
 | 
					"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
 | 
				
			||||||
 | 
					<node>
 | 
				
			||||||
 | 
					<interface name="org.kde.StatusNotifierItem">
 | 
				
			||||||
 | 
					    <property name="Category" type="s" access="read"/>
 | 
				
			||||||
 | 
					    <property name="Id" type="s" access="read"/>
 | 
				
			||||||
 | 
					    <property name="Title" type="s" access="read"/>
 | 
				
			||||||
 | 
					    <property name="Status" type="s" access="read"/>
 | 
				
			||||||
 | 
					    <property name="WindowId" type="i" access="read"/>
 | 
				
			||||||
 | 
					    <property name="Menu" type="o" access="read" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- main icon -->
 | 
				
			||||||
 | 
					    <!-- names are preferred over pixmaps -->
 | 
				
			||||||
 | 
					    <property name="IconName" type="s" access="read" />
 | 
				
			||||||
 | 
					    <property name="IconThemePath" type="s" access="read" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- struct containing width, height and image data-->
 | 
				
			||||||
 | 
					    <!-- implementation has been dropped as of now -->
 | 
				
			||||||
 | 
					    <property name="IconPixmap" type="a(iiay)" access="read" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- not used in ayatana code, no test case so far -->
 | 
				
			||||||
 | 
					    <property name="OverlayIconName" type="s" access="read"/>
 | 
				
			||||||
 | 
					    <property name="OverlayIconPixmap" type="a(iiay)" access="read" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Requesting attention icon -->
 | 
				
			||||||
 | 
					    <property name="AttentionIconName" type="s" access="read"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!--same definition as image-->
 | 
				
			||||||
 | 
					    <property name="AttentionIconPixmap" type="a(iiay)" access="read" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- tooltip data -->
 | 
				
			||||||
 | 
					    <!-- unimplemented as of now -->
 | 
				
			||||||
 | 
					    <!--(iiay) is an image-->
 | 
				
			||||||
 | 
					    <property name="ToolTip" type="(sa(iiay)ss)" access="read" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- interaction: actually, we do not use them. -->
 | 
				
			||||||
 | 
					    <method name="Activate">
 | 
				
			||||||
 | 
					        <arg name="x" type="i" direction="in"/>
 | 
				
			||||||
 | 
					        <arg name="y" type="i" direction="in"/>
 | 
				
			||||||
 | 
					    </method>
 | 
				
			||||||
 | 
					    <method name="SecondaryActivate">
 | 
				
			||||||
 | 
					        <arg name="x" type="i" direction="in"/>
 | 
				
			||||||
 | 
					        <arg name="y" type="i" direction="in"/>
 | 
				
			||||||
 | 
					    </method>
 | 
				
			||||||
 | 
					    <method name="Scroll">
 | 
				
			||||||
 | 
					        <arg name="delta" type="i" direction="in"/>
 | 
				
			||||||
 | 
					        <arg name="dir"   type="s" direction="in"/>
 | 
				
			||||||
 | 
					    </method>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Signals: the client wants to change something in the status-->
 | 
				
			||||||
 | 
					    <signal name="NewTitle"></signal>
 | 
				
			||||||
 | 
					    <signal name="NewIcon"></signal>
 | 
				
			||||||
 | 
					    <signal name="NewIconThemePath">
 | 
				
			||||||
 | 
					        <arg type="s" name="icon_theme_path" direction="out" />
 | 
				
			||||||
 | 
					    </signal>
 | 
				
			||||||
 | 
					    <signal name="NewAttentionIcon"></signal>
 | 
				
			||||||
 | 
					    <signal name="NewOverlayIcon"></signal>
 | 
				
			||||||
 | 
					    <signal name="NewToolTip"></signal>
 | 
				
			||||||
 | 
					    <signal name="NewStatus">
 | 
				
			||||||
 | 
					        <arg name="status" type="s" />
 | 
				
			||||||
 | 
					    </signal>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- ayatana labels -->
 | 
				
			||||||
 | 
					    <!-- These are commented out because GDBusProxy would otherwise require them,
 | 
				
			||||||
 | 
					         but they are not available for KDE indicators
 | 
				
			||||||
 | 
					    -->
 | 
				
			||||||
 | 
					    <!--<signal name="XAyatanaNewLabel">
 | 
				
			||||||
 | 
					        <arg type="s" name="label" direction="out" />
 | 
				
			||||||
 | 
					        <arg type="s" name="guide" direction="out" />
 | 
				
			||||||
 | 
					    </signal>
 | 
				
			||||||
 | 
					    <property name="XAyatanaLabel" type="s" access="read" />
 | 
				
			||||||
 | 
					    <property name="XAyatanaLabelGuide" type="s" access="read" />-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</interface>
 | 
				
			||||||
 | 
					</node>
 | 
				
			||||||
							
								
								
									
										42
									
								
								protocol/dbus-status-notifier-watcher.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								protocol/dbus-status-notifier-watcher.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
 | 
				
			||||||
 | 
					<node>
 | 
				
			||||||
 | 
					  <interface name="org.kde.StatusNotifierWatcher">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- methods -->
 | 
				
			||||||
 | 
					    <method name="RegisterStatusNotifierItem">
 | 
				
			||||||
 | 
					       <arg name="service" type="s" direction="in"/>
 | 
				
			||||||
 | 
					    </method>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <method name="RegisterStatusNotifierHost">
 | 
				
			||||||
 | 
					       <arg name="service" type="s" direction="in"/>
 | 
				
			||||||
 | 
					    </method>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- properties -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <property name="RegisteredStatusNotifierItems" type="as" access="read">
 | 
				
			||||||
 | 
					       <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QStringList"/>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <property name="IsStatusNotifierHostRegistered" type="b" access="read"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <property name="ProtocolVersion" type="i" access="read"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- signals -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <signal name="StatusNotifierItemRegistered">
 | 
				
			||||||
 | 
					        <arg type="s"/>
 | 
				
			||||||
 | 
					    </signal>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <signal name="StatusNotifierItemUnregistered">
 | 
				
			||||||
 | 
					        <arg type="s"/>
 | 
				
			||||||
 | 
					    </signal>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <signal name="StatusNotifierHostRegistered">
 | 
				
			||||||
 | 
					    </signal>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <signal name="StatusNotifierHostUnregistered">
 | 
				
			||||||
 | 
					    </signal>
 | 
				
			||||||
 | 
					  </interface>
 | 
				
			||||||
 | 
					</node>
 | 
				
			||||||
@@ -36,10 +36,28 @@ foreach p : client_protocols
 | 
				
			|||||||
	client_protos_headers += wayland_scanner_client.process(xml)
 | 
						client_protos_headers += wayland_scanner_client.process(xml)
 | 
				
			||||||
endforeach
 | 
					endforeach
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					gdbus_code = generator(
 | 
				
			||||||
 | 
					    find_program('gdbus-codegen'),
 | 
				
			||||||
 | 
					    output: '@BASENAME@.c',
 | 
				
			||||||
 | 
					    arguments: ['--c-namespace', 'Sn', '--body', '--output', '@OUTPUT@', '@INPUT@']
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					gdbus_header = generator(
 | 
				
			||||||
 | 
					    find_program('gdbus-codegen'),
 | 
				
			||||||
 | 
					    output: '@BASENAME@.h',
 | 
				
			||||||
 | 
					    arguments: ['--c-namespace', 'Sn', '--header', '--output', '@OUTPUT@', '@INPUT@']
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					client_protos_src += gdbus_code.process('./dbus-status-notifier-watcher.xml')
 | 
				
			||||||
 | 
					client_protos_headers += gdbus_header.process('./dbus-status-notifier-watcher.xml')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					client_protos_src += gdbus_code.process('./dbus-status-notifier-item.xml')
 | 
				
			||||||
 | 
					client_protos_headers += gdbus_header.process('./dbus-status-notifier-item.xml')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
lib_client_protos = static_library(
 | 
					lib_client_protos = static_library(
 | 
				
			||||||
	'client_protos',
 | 
						'client_protos',
 | 
				
			||||||
	client_protos_src + client_protos_headers,
 | 
						client_protos_src + client_protos_headers,
 | 
				
			||||||
	dependencies: [wayland_client]
 | 
						dependencies: [wayland_client, gtkmm]
 | 
				
			||||||
) # for the include directory
 | 
					) # for the include directory
 | 
				
			||||||
 | 
					
 | 
				
			||||||
client_protos = declare_dependency(
 | 
					client_protos = declare_dependency(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
    // Choose the order of the modules
 | 
					    // Choose the order of the modules
 | 
				
			||||||
    "modules-left": ["sway/workspaces", "custom/spotify"],
 | 
					    "modules-left": ["sway/workspaces", "custom/spotify"],
 | 
				
			||||||
    "modules-center": ["sway/window"],
 | 
					    "modules-center": ["sway/window"],
 | 
				
			||||||
    "modules-right": ["pulseaudio", "network", "cpu", "memory", "battery", "clock"],
 | 
					    "modules-right": ["pulseaudio", "network", "cpu", "memory", "battery", "clock", "tray"],
 | 
				
			||||||
    // Modules configuration
 | 
					    // Modules configuration
 | 
				
			||||||
    // "sway/workspaces": {
 | 
					    // "sway/workspaces": {
 | 
				
			||||||
    //     "disable-scroll": true,
 | 
					    //     "disable-scroll": true,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,7 @@
 | 
				
			|||||||
    font-size: 13px;
 | 
					    font-size: 13px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window {
 | 
					window#waybar {
 | 
				
			||||||
    background: rgba(43, 48, 59, 0.5);
 | 
					    background: rgba(43, 48, 59, 0.5);
 | 
				
			||||||
    border-bottom: 3px solid rgba(100, 114, 125, 0.5);
 | 
					    border-bottom: 3px solid rgba(100, 114, 125, 0.5);
 | 
				
			||||||
    color: white;
 | 
					    color: white;
 | 
				
			||||||
@@ -23,7 +23,7 @@ window {
 | 
				
			|||||||
    border-bottom: 3px solid white;
 | 
					    border-bottom: 3px solid white;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#clock, #battery, #cpu, #memory, #network, #pulseaudio, #custom-spotify {
 | 
					#clock, #battery, #cpu, #memory, #network, #pulseaudio, #custom-spotify, #tray {
 | 
				
			||||||
    padding: 0 10px;
 | 
					    padding: 0 10px;
 | 
				
			||||||
    margin: 0 5px;
 | 
					    margin: 0 5px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -90,3 +90,7 @@ window {
 | 
				
			|||||||
    background: #66cc99;
 | 
					    background: #66cc99;
 | 
				
			||||||
    color: #2a5c45;
 | 
					    color: #2a5c45;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#tray {
 | 
				
			||||||
 | 
					    background-color: #2980b9;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,6 +21,7 @@ waybar::Bar::Bar(const Client& client,
 | 
				
			|||||||
	zxdg_output_v1_add_listener(xdg_output_, &xdgOutputListener, this);
 | 
						zxdg_output_v1_add_listener(xdg_output_, &xdgOutputListener, this);
 | 
				
			||||||
  window.set_title("waybar");
 | 
					  window.set_title("waybar");
 | 
				
			||||||
  window.set_decorated(false);
 | 
					  window.set_decorated(false);
 | 
				
			||||||
 | 
					  window.set_name("waybar");
 | 
				
			||||||
  window.set_resizable(false);
 | 
					  window.set_resizable(false);
 | 
				
			||||||
  setupConfig();
 | 
					  setupConfig();
 | 
				
			||||||
  setupCss();
 | 
					  setupCss();
 | 
				
			||||||
@@ -199,14 +200,14 @@ auto waybar::Bar::setupWidgets() -> void
 | 
				
			|||||||
  getModules(factory, "modules-left");
 | 
					  getModules(factory, "modules-left");
 | 
				
			||||||
  getModules(factory, "modules-center");
 | 
					  getModules(factory, "modules-center");
 | 
				
			||||||
  getModules(factory, "modules-right");
 | 
					  getModules(factory, "modules-right");
 | 
				
			||||||
  for (auto& module : modules_left_) {
 | 
					  for (auto const& module : modules_left_) {
 | 
				
			||||||
    left.pack_start(*module, false, true, 0);
 | 
					    left.pack_start(*module, false, true, 0);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  for (auto& module : modules_center_) {
 | 
					  for (auto const& module : modules_center_) {
 | 
				
			||||||
    center.pack_start(*module, true, true, 0);
 | 
					    center.pack_start(*module, true, true, 0);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  std::reverse(modules_right_.begin(), modules_right_.end());
 | 
					  std::reverse(modules_right_.begin(), modules_right_.end());
 | 
				
			||||||
  for (auto& module : modules_right_) {
 | 
					  for (auto const& module : modules_right_) {
 | 
				
			||||||
    right.pack_end(*module, false, false, 0);
 | 
					    right.pack_end(*module, false, false, 0);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,6 +27,9 @@ waybar::IModule* waybar::Factory::makeModule(const std::string &name) const
 | 
				
			|||||||
    if (name == "clock") {
 | 
					    if (name == "clock") {
 | 
				
			||||||
      return new waybar::modules::Clock(config_[name]);
 | 
					      return new waybar::modules::Clock(config_[name]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if (name == "tray") {
 | 
				
			||||||
 | 
					      return new waybar::modules::SNI::Tray(config_[name]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    #ifdef HAVE_LIBNL
 | 
					    #ifdef HAVE_LIBNL
 | 
				
			||||||
    if (name == "network") {
 | 
					    if (name == "network") {
 | 
				
			||||||
      return new waybar::modules::Network(config_[name]);
 | 
					      return new waybar::modules::Network(config_[name]);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@ waybar::modules::Battery::Battery(const Json::Value& config)
 | 
				
			|||||||
  : ALabel(config, "{capacity}%")
 | 
					  : ALabel(config, "{capacity}%")
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    for (auto &node : fs::directory_iterator(data_dir_)) {
 | 
					    for (auto const& node : fs::directory_iterator(data_dir_)) {
 | 
				
			||||||
      if (fs::is_directory(node) && fs::exists(node / "capacity")
 | 
					      if (fs::is_directory(node) && fs::exists(node / "capacity")
 | 
				
			||||||
        && fs::exists(node / "status") && fs::exists(node / "uevent")) {
 | 
					        && fs::exists(node / "status") && fs::exists(node / "uevent")) {
 | 
				
			||||||
        batteries_.push_back(node);
 | 
					        batteries_.push_back(node);
 | 
				
			||||||
@@ -20,7 +20,7 @@ waybar::modules::Battery::Battery(const Json::Value& config)
 | 
				
			|||||||
  if (fd_ == -1) {
 | 
					  if (fd_ == -1) {
 | 
				
			||||||
    throw std::runtime_error("Unable to listen batteries.");
 | 
					    throw std::runtime_error("Unable to listen batteries.");
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  for (auto &bat : batteries_) {
 | 
					  for (auto const& bat : batteries_) {
 | 
				
			||||||
    inotify_add_watch(fd_, (bat / "uevent").c_str(), IN_ACCESS);
 | 
					    inotify_add_watch(fd_, (bat / "uevent").c_str(), IN_ACCESS);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  label_.set_name("battery");
 | 
					  label_.set_name("battery");
 | 
				
			||||||
@@ -51,7 +51,7 @@ auto waybar::modules::Battery::update() -> void
 | 
				
			|||||||
  try {
 | 
					  try {
 | 
				
			||||||
    uint16_t total = 0;
 | 
					    uint16_t total = 0;
 | 
				
			||||||
    std::string status;
 | 
					    std::string status;
 | 
				
			||||||
    for (auto &bat : batteries_) {
 | 
					    for (auto const& bat : batteries_) {
 | 
				
			||||||
      uint16_t capacity;
 | 
					      uint16_t capacity;
 | 
				
			||||||
      std::string _status;
 | 
					      std::string _status;
 | 
				
			||||||
      std::ifstream(bat / "capacity") >> capacity;
 | 
					      std::ifstream(bat / "capacity") >> capacity;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -142,7 +142,7 @@ int waybar::modules::Network::getExternalInterface()
 | 
				
			|||||||
  int ifidx = -1;
 | 
					  int ifidx = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /* Prepare request. */
 | 
					  /* Prepare request. */
 | 
				
			||||||
  uint32_t reqlen = NLMSG_SPACE(sizeof(*rt));
 | 
					  constexpr uint32_t reqlen = NLMSG_SPACE(sizeof(*rt));
 | 
				
			||||||
  char req[reqlen] = {0};
 | 
					  char req[reqlen] = {0};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /* Build the RTM_GETROUTE request. */
 | 
					  /* Build the RTM_GETROUTE request. */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -118,7 +118,7 @@ const std::string waybar::modules::Pulseaudio::getPortIcon() const
 | 
				
			|||||||
    "hifi",
 | 
					    "hifi",
 | 
				
			||||||
    "phone",
 | 
					    "phone",
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
  for (auto port : ports) {
 | 
					  for (auto const& port : ports) {
 | 
				
			||||||
    if (port_name_.find(port) != std::string::npos) {
 | 
					    if (port_name_.find(port) != std::string::npos) {
 | 
				
			||||||
      return port;
 | 
					      return port;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										155
									
								
								src/modules/sni/snh.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								src/modules/sni/snh.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,155 @@
 | 
				
			|||||||
 | 
					#include "modules/sni/snh.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace waybar::modules::SNI;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Host::Host(Glib::Dispatcher* dp)
 | 
				
			||||||
 | 
					: dp_(dp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  GBusNameOwnerFlags flags = static_cast<GBusNameOwnerFlags>(
 | 
				
			||||||
 | 
					    G_BUS_NAME_OWNER_FLAGS_NONE);
 | 
				
			||||||
 | 
					  bus_name_ = "org.kde.StatusNotifierHost-" + std::to_string(getpid());
 | 
				
			||||||
 | 
					  object_path_ = "/StatusNotifierHost";
 | 
				
			||||||
 | 
					  bus_name_id_ = g_bus_own_name(G_BUS_TYPE_SESSION,
 | 
				
			||||||
 | 
					    bus_name_.c_str(), flags,
 | 
				
			||||||
 | 
					    &Host::busAcquired, nullptr, nullptr, this, nullptr);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Host::busAcquired(GDBusConnection* connection,
 | 
				
			||||||
 | 
					  const gchar* name, gpointer data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  auto host = static_cast<SNI::Host *>(data);
 | 
				
			||||||
 | 
					  host->watcher_id_ = g_bus_watch_name(
 | 
				
			||||||
 | 
					    G_BUS_TYPE_SESSION,
 | 
				
			||||||
 | 
					    "org.kde.StatusNotifierWatcher",
 | 
				
			||||||
 | 
					    G_BUS_NAME_WATCHER_FLAGS_NONE,
 | 
				
			||||||
 | 
					    &Host::nameAppeared, &Host::nameVanished, data, nullptr);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Host::nameAppeared(GDBusConnection* connection,
 | 
				
			||||||
 | 
					  const gchar* name, const gchar* name_owner, gpointer data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  auto host = static_cast<SNI::Host *>(data);
 | 
				
			||||||
 | 
					  if (host->cancellable_ != nullptr) {
 | 
				
			||||||
 | 
					    std::cout << "WTF" << std::endl;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  host->cancellable_ = g_cancellable_new();
 | 
				
			||||||
 | 
					  sn_org_kde_status_notifier_watcher_proxy_new(
 | 
				
			||||||
 | 
					    connection,
 | 
				
			||||||
 | 
					    G_DBUS_PROXY_FLAGS_NONE,
 | 
				
			||||||
 | 
					    "org.kde.StatusNotifierWatcher",
 | 
				
			||||||
 | 
					    "/StatusNotifierWatcher",
 | 
				
			||||||
 | 
					    host->cancellable_, &Host::proxyReady, data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Host::nameVanished(GDBusConnection* connection,
 | 
				
			||||||
 | 
					  const gchar* name, gpointer data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  auto host = static_cast<SNI::Host *>(data);
 | 
				
			||||||
 | 
					  g_cancellable_cancel(host->cancellable_);
 | 
				
			||||||
 | 
					  g_clear_object(&host->cancellable_);
 | 
				
			||||||
 | 
					  g_clear_object(&host->watcher_);
 | 
				
			||||||
 | 
					  host->items.clear();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Host::proxyReady(GObject* src, GAsyncResult* res,
 | 
				
			||||||
 | 
					  gpointer data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  GError* error = nullptr;
 | 
				
			||||||
 | 
					  SnOrgKdeStatusNotifierWatcher* watcher =
 | 
				
			||||||
 | 
					    sn_org_kde_status_notifier_watcher_proxy_new_finish(res, &error);
 | 
				
			||||||
 | 
					  if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
 | 
				
			||||||
 | 
					    std::cerr << error->message << std::endl;
 | 
				
			||||||
 | 
					    g_error_free(error);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  auto host = static_cast<SNI::Host *>(data);
 | 
				
			||||||
 | 
					  host->watcher_ = watcher;
 | 
				
			||||||
 | 
					  if (error != nullptr) {
 | 
				
			||||||
 | 
					    std::cerr << error->message << std::endl;
 | 
				
			||||||
 | 
					    g_error_free(error);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  sn_org_kde_status_notifier_watcher_call_register_status_notifier_host(
 | 
				
			||||||
 | 
					    host->watcher_, host->object_path_.c_str(), host->cancellable_,
 | 
				
			||||||
 | 
					    &Host::registerHost, data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Host::registerHost(GObject* src, GAsyncResult* res,
 | 
				
			||||||
 | 
					  gpointer data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  GError* error = nullptr;
 | 
				
			||||||
 | 
					  sn_org_kde_status_notifier_watcher_call_register_status_notifier_host_finish(
 | 
				
			||||||
 | 
					    SN_ORG_KDE_STATUS_NOTIFIER_WATCHER(src), res, &error);
 | 
				
			||||||
 | 
					  if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
 | 
				
			||||||
 | 
					    std::cerr << error->message << std::endl;
 | 
				
			||||||
 | 
					    g_error_free(error);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  auto host = static_cast<SNI::Host *>(data);
 | 
				
			||||||
 | 
					  if (error != nullptr) {
 | 
				
			||||||
 | 
					    std::cerr << error->message << std::endl;
 | 
				
			||||||
 | 
					    g_error_free(error);
 | 
				
			||||||
 | 
					    return; 
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  g_signal_connect(host->watcher_, "status-notifier-item-registered",
 | 
				
			||||||
 | 
					    G_CALLBACK(&Host::itemRegistered), data);
 | 
				
			||||||
 | 
					  g_signal_connect(host->watcher_, "status-notifier-item-unregistered",
 | 
				
			||||||
 | 
					    G_CALLBACK(&Host::itemUnregistered), data);
 | 
				
			||||||
 | 
					  auto items =
 | 
				
			||||||
 | 
					    sn_org_kde_status_notifier_watcher_dup_registered_status_notifier_items(host->watcher_);
 | 
				
			||||||
 | 
					  if (items) {
 | 
				
			||||||
 | 
					    for (uint32_t i = 0; items[i] != nullptr; i += 1) {
 | 
				
			||||||
 | 
					      host->addRegisteredItem(items[i]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  g_strfreev(items);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Host::itemRegistered(
 | 
				
			||||||
 | 
					  SnOrgKdeStatusNotifierWatcher* watcher, const gchar* service, gpointer data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  std::cout << "Item registered" << std::endl;
 | 
				
			||||||
 | 
					  auto host = static_cast<SNI::Host *>(data);
 | 
				
			||||||
 | 
					  host->addRegisteredItem(service);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Host::itemUnregistered(
 | 
				
			||||||
 | 
					  SnOrgKdeStatusNotifierWatcher* watcher, const gchar* service, gpointer data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  auto host = static_cast<SNI::Host *>(data);
 | 
				
			||||||
 | 
					  auto [bus_name, object_path] = host->getBusNameAndObjectPath(service);
 | 
				
			||||||
 | 
					  for (auto it = host->items.begin(); it != host->items.end(); ++it) {
 | 
				
			||||||
 | 
					    if (it->bus_name == bus_name && it->object_path == object_path) {
 | 
				
			||||||
 | 
					      host->items.erase(it);
 | 
				
			||||||
 | 
					      std::cout << "Item Unregistered" << std::endl;
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  host->dp_->emit();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::tuple<std::string, std::string> Host::getBusNameAndObjectPath(
 | 
				
			||||||
 | 
					  const gchar* service)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  std::string bus_name;
 | 
				
			||||||
 | 
					  std::string object_path;
 | 
				
			||||||
 | 
					  gchar* tmp = g_strstr_len(service, -1, "/");
 | 
				
			||||||
 | 
					  if (tmp != nullptr) {
 | 
				
			||||||
 | 
					    gchar** str = g_strsplit(service, "/", 2);
 | 
				
			||||||
 | 
					    bus_name = str[0];
 | 
				
			||||||
 | 
					    object_path = tmp;
 | 
				
			||||||
 | 
					    g_strfreev(str);
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    bus_name = service;
 | 
				
			||||||
 | 
					    object_path = "/StatusNotifierItem";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return { bus_name, object_path };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Host::addRegisteredItem(const gchar* service)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  auto [bus_name, object_path] = getBusNameAndObjectPath(service);
 | 
				
			||||||
 | 
					  items.emplace_back(bus_name, object_path, dp_);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										260
									
								
								src/modules/sni/sni.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										260
									
								
								src/modules/sni/sni.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,260 @@
 | 
				
			|||||||
 | 
					#include "modules/sni/sni.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <libdbusmenu-gtk/dbusmenu-gtk.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					waybar::modules::SNI::Item::Item(std::string bn, std::string op,
 | 
				
			||||||
 | 
					                                 Glib::Dispatcher *dp)
 | 
				
			||||||
 | 
					    : bus_name(bn), object_path(op), event_box(), icon_size(16),
 | 
				
			||||||
 | 
					      effective_icon_size(0), image(Gtk::manage(new Gtk::Image())), dp_(dp) {
 | 
				
			||||||
 | 
					  event_box.add(*image);
 | 
				
			||||||
 | 
					  event_box.add_events(Gdk::BUTTON_PRESS_MASK);
 | 
				
			||||||
 | 
					  event_box.signal_button_press_event().connect(
 | 
				
			||||||
 | 
					      sigc::mem_fun(*this, &Item::handleClick));
 | 
				
			||||||
 | 
					  cancellable_ = g_cancellable_new();
 | 
				
			||||||
 | 
					  sn_org_kde_status_notifier_item_proxy_new_for_bus(
 | 
				
			||||||
 | 
					      G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, bus_name.c_str(),
 | 
				
			||||||
 | 
					      object_path.c_str(), cancellable_, &Item::proxyReady, this);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void waybar::modules::SNI::Item::proxyReady(GObject *obj, GAsyncResult *res,
 | 
				
			||||||
 | 
					                                            gpointer data) {
 | 
				
			||||||
 | 
					  GError *error = nullptr;
 | 
				
			||||||
 | 
					  SnOrgKdeStatusNotifierItem *proxy =
 | 
				
			||||||
 | 
					      sn_org_kde_status_notifier_item_proxy_new_for_bus_finish(res, &error);
 | 
				
			||||||
 | 
					  if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
 | 
				
			||||||
 | 
					    g_error_free(error);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  auto item = static_cast<SNI::Item *>(data);
 | 
				
			||||||
 | 
					  item->proxy_ = proxy;
 | 
				
			||||||
 | 
					  if (error) {
 | 
				
			||||||
 | 
					    std::cerr << error->message << std::endl;
 | 
				
			||||||
 | 
					    g_error_free(error);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  auto conn = g_dbus_proxy_get_connection(G_DBUS_PROXY(proxy));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  g_dbus_connection_call(conn, item->bus_name.c_str(),
 | 
				
			||||||
 | 
					                         item->object_path.c_str(),
 | 
				
			||||||
 | 
					                         "org.freedesktop.DBus.Properties", "GetAll",
 | 
				
			||||||
 | 
					                         g_variant_new("(s)", "org.kde.StatusNotifierItem"),
 | 
				
			||||||
 | 
					                         G_VARIANT_TYPE("(a{sv})"), G_DBUS_CALL_FLAGS_NONE, -1,
 | 
				
			||||||
 | 
					                         item->cancellable_, &Item::getAll, data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void waybar::modules::SNI::Item::getAll(GObject *obj, GAsyncResult *res,
 | 
				
			||||||
 | 
					                                        gpointer data) {
 | 
				
			||||||
 | 
					  GError *error = nullptr;
 | 
				
			||||||
 | 
					  auto conn = G_DBUS_CONNECTION(obj);
 | 
				
			||||||
 | 
					  GVariant *properties = g_dbus_connection_call_finish(conn, res, &error);
 | 
				
			||||||
 | 
					  if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
 | 
				
			||||||
 | 
					    g_error_free(error);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  auto item = static_cast<SNI::Item *>(data);
 | 
				
			||||||
 | 
					  if (error) {
 | 
				
			||||||
 | 
					    std::cerr << error->message << std::endl;
 | 
				
			||||||
 | 
					    g_error_free(error);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  GVariantIter *it = nullptr;
 | 
				
			||||||
 | 
					  g_variant_get(properties, "(a{sv})", &it);
 | 
				
			||||||
 | 
					  gchar *key;
 | 
				
			||||||
 | 
					  GVariant *value;
 | 
				
			||||||
 | 
					  while (g_variant_iter_next(it, "{sv}", &key, &value)) {
 | 
				
			||||||
 | 
					    if (g_strcmp0(key, "Category") == 0) {
 | 
				
			||||||
 | 
					      item->category = g_variant_dup_string(value, nullptr);
 | 
				
			||||||
 | 
					    } else if (g_strcmp0(key, "Id") == 0) {
 | 
				
			||||||
 | 
					      item->id = g_variant_dup_string(value, nullptr);
 | 
				
			||||||
 | 
					    } else if (g_strcmp0(key, "Title") == 0) {
 | 
				
			||||||
 | 
					      item->title = g_variant_dup_string(value, nullptr);
 | 
				
			||||||
 | 
					    } else if (g_strcmp0(key, "Status") == 0) {
 | 
				
			||||||
 | 
					      item->status = g_variant_dup_string(value, nullptr);
 | 
				
			||||||
 | 
					    } else if (g_strcmp0(key, "WindowId") == 0) {
 | 
				
			||||||
 | 
					      item->window_id = g_variant_get_int32(value);
 | 
				
			||||||
 | 
					    } else if (g_strcmp0(key, "IconName") == 0) {
 | 
				
			||||||
 | 
					      item->icon_name = g_variant_dup_string(value, nullptr);
 | 
				
			||||||
 | 
					    } else if (g_strcmp0(key, "IconPixmap") == 0) {
 | 
				
			||||||
 | 
					      item->icon_pixmap = item->extractPixBuf(value);
 | 
				
			||||||
 | 
					    } else if (g_strcmp0(key, "OverlayIconName") == 0) {
 | 
				
			||||||
 | 
					      item->overlay_icon_name = g_variant_dup_string(value, nullptr);
 | 
				
			||||||
 | 
					    } else if (g_strcmp0(key, "OverlayIconPixmap") == 0) {
 | 
				
			||||||
 | 
					      // TODO: overlay_icon_pixmap
 | 
				
			||||||
 | 
					    } else if (g_strcmp0(key, "AttentionIconName") == 0) {
 | 
				
			||||||
 | 
					      item->attention_icon_name = g_variant_dup_string(value, nullptr);
 | 
				
			||||||
 | 
					    } else if (g_strcmp0(key, "AttentionIconPixmap") == 0) {
 | 
				
			||||||
 | 
					      // TODO: attention_icon_pixmap
 | 
				
			||||||
 | 
					    } else if (g_strcmp0(key, "AttentionMovieName") == 0) {
 | 
				
			||||||
 | 
					      item->attention_movie_name = g_variant_dup_string(value, nullptr);
 | 
				
			||||||
 | 
					    } else if (g_strcmp0(key, "ToolTip") == 0) {
 | 
				
			||||||
 | 
					      // TODO: tooltip
 | 
				
			||||||
 | 
					    } else if (g_strcmp0(key, "IconThemePath") == 0) {
 | 
				
			||||||
 | 
					      item->icon_theme_path = g_variant_dup_string(value, nullptr);
 | 
				
			||||||
 | 
					    } else if (g_strcmp0(key, "Menu") == 0) {
 | 
				
			||||||
 | 
					      item->menu = g_variant_dup_string(value, nullptr);
 | 
				
			||||||
 | 
					    } else if (g_strcmp0(key, "ItemIsMenu") == 0) {
 | 
				
			||||||
 | 
					      item->item_is_menu = g_variant_get_boolean(value);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    g_variant_unref(value);
 | 
				
			||||||
 | 
					    g_free(key);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  g_variant_iter_free(it);
 | 
				
			||||||
 | 
					  g_variant_unref(properties);
 | 
				
			||||||
 | 
					  if (item->id.empty() || item->category.empty() || item->status.empty()) {
 | 
				
			||||||
 | 
					    std::cerr << "Invalid Status Notifier Item: " + item->bus_name + "," +
 | 
				
			||||||
 | 
					                     item->object_path
 | 
				
			||||||
 | 
					              << std::endl;
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (!item->icon_theme_path.empty()) {
 | 
				
			||||||
 | 
					    GtkIconTheme *icon_theme = gtk_icon_theme_get_default();
 | 
				
			||||||
 | 
					    gtk_icon_theme_append_search_path(icon_theme,
 | 
				
			||||||
 | 
					                                      item->icon_theme_path.c_str());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  item->updateImage();
 | 
				
			||||||
 | 
					  item->dp_->emit();
 | 
				
			||||||
 | 
					  // TODO: handle change
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Glib::RefPtr<Gdk::Pixbuf>
 | 
				
			||||||
 | 
					waybar::modules::SNI::Item::extractPixBuf(GVariant *variant) {
 | 
				
			||||||
 | 
					  GVariantIter *it;
 | 
				
			||||||
 | 
					  g_variant_get(variant, "a(iiay)", &it);
 | 
				
			||||||
 | 
					  if (it == nullptr) {
 | 
				
			||||||
 | 
					    return Glib::RefPtr<Gdk::Pixbuf>{};
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  GVariant *val;
 | 
				
			||||||
 | 
					  gint lwidth = 0;
 | 
				
			||||||
 | 
					  gint lheight = 0;
 | 
				
			||||||
 | 
					  gint width;
 | 
				
			||||||
 | 
					  gint height;
 | 
				
			||||||
 | 
					  guchar *array = nullptr;
 | 
				
			||||||
 | 
					  while (g_variant_iter_loop(it, "(ii@ay)", &width, &height, &val)) {
 | 
				
			||||||
 | 
					    if (width > 0 && height > 0 && val != nullptr &&
 | 
				
			||||||
 | 
					        width * height > lwidth * lheight) {
 | 
				
			||||||
 | 
					      auto size = g_variant_get_size(val);
 | 
				
			||||||
 | 
					      /* Sanity check */
 | 
				
			||||||
 | 
					      if (size == 4U * width * height) {
 | 
				
			||||||
 | 
					        /* Find the largest image */
 | 
				
			||||||
 | 
					        gconstpointer data = g_variant_get_data(val);
 | 
				
			||||||
 | 
					        if (data != nullptr) {
 | 
				
			||||||
 | 
					          if (array != nullptr) {
 | 
				
			||||||
 | 
					            g_free(array);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          array = static_cast<guchar *>(g_memdup(data, size));
 | 
				
			||||||
 | 
					          lwidth = width;
 | 
				
			||||||
 | 
					          lheight = height;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  g_variant_iter_free(it);
 | 
				
			||||||
 | 
					  if (array != nullptr) {
 | 
				
			||||||
 | 
					    /* argb to rgba */
 | 
				
			||||||
 | 
					    for (uint32_t i = 0; i < 4U * lwidth * lheight; i += 4) {
 | 
				
			||||||
 | 
					      guchar alpha = array[i];
 | 
				
			||||||
 | 
					      array[i] = array[i + 1];
 | 
				
			||||||
 | 
					      array[i + 1] = array[i + 2];
 | 
				
			||||||
 | 
					      array[i + 2] = array[i + 3];
 | 
				
			||||||
 | 
					      array[i + 3] = alpha;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return Gdk::Pixbuf::create_from_data(array, Gdk::Colorspace::COLORSPACE_RGB,
 | 
				
			||||||
 | 
					                                         true, 8, lwidth, lheight, 4 * lwidth);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return Glib::RefPtr<Gdk::Pixbuf>{};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void waybar::modules::SNI::Item::updateImage()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  if (!icon_name.empty()) {
 | 
				
			||||||
 | 
					    auto pixbuf = getIconByName(icon_name, icon_size);
 | 
				
			||||||
 | 
					    if (pixbuf->gobj() == nullptr) {
 | 
				
			||||||
 | 
					      // Try to find icons specified by path and filename
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        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
 | 
				
			||||||
 | 
					          pixbuf->scale_simple(icon_size - 2, icon_size - 2,
 | 
				
			||||||
 | 
					                               Gdk::InterpType::INTERP_BILINEAR);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      } catch (Glib::Error &e) {
 | 
				
			||||||
 | 
					        std::cerr << "Exception: " << e.what() << std::endl;
 | 
				
			||||||
 | 
					        pixbuf = getIconByName("image-missing", icon_size);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (pixbuf->gobj() == nullptr) {
 | 
				
			||||||
 | 
					      pixbuf = getIconByName("image-missing", icon_size);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    image->set(pixbuf);
 | 
				
			||||||
 | 
					  } else if (icon_pixmap) {
 | 
				
			||||||
 | 
					    image->set(icon_pixmap);
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    image->set_from_icon_name("image-missing", Gtk::ICON_SIZE_MENU);
 | 
				
			||||||
 | 
					    image->set_pixel_size(icon_size);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (!menu.empty()) {
 | 
				
			||||||
 | 
					    auto *dbmenu = dbusmenu_gtkmenu_new(bus_name.data(), menu.data());
 | 
				
			||||||
 | 
					    if (dbmenu)
 | 
				
			||||||
 | 
					      gtk_menu = Glib::wrap(GTK_MENU(dbmenu), false);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Glib::RefPtr<Gdk::Pixbuf>
 | 
				
			||||||
 | 
					waybar::modules::SNI::Item::getIconByName(std::string name, int request_size) {
 | 
				
			||||||
 | 
					  int icon_size = 0;
 | 
				
			||||||
 | 
					  Glib::RefPtr<Gtk::IconTheme> icon_theme = Gtk::IconTheme::get_default();
 | 
				
			||||||
 | 
					  icon_theme->rescan_if_needed();
 | 
				
			||||||
 | 
					  auto sizes = icon_theme->get_icon_sizes(name.c_str());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (auto const &size : sizes) {
 | 
				
			||||||
 | 
					    // -1 == scalable
 | 
				
			||||||
 | 
					    if (size == request_size || size == -1) {
 | 
				
			||||||
 | 
					      icon_size = request_size;
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    } else if (size < request_size || size > icon_size) {
 | 
				
			||||||
 | 
					      icon_size = size;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (icon_size == 0) {
 | 
				
			||||||
 | 
					    icon_size = request_size;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return icon_theme->load_icon(name.c_str(), icon_size,
 | 
				
			||||||
 | 
					                               Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void waybar::modules::SNI::Item::handleActivate(GObject *src, GAsyncResult *res,
 | 
				
			||||||
 | 
					                                                gpointer data) {
 | 
				
			||||||
 | 
					  auto item = static_cast<SNI::Item *>(data);
 | 
				
			||||||
 | 
					  sn_org_kde_status_notifier_item_call_activate_finish(item->proxy_, res,
 | 
				
			||||||
 | 
					                                                       nullptr);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void waybar::modules::SNI::Item::handleSecondaryActivate(GObject *src,
 | 
				
			||||||
 | 
					                                                         GAsyncResult *res,
 | 
				
			||||||
 | 
					                                                         gpointer data) {
 | 
				
			||||||
 | 
					  auto item = static_cast<SNI::Item *>(data);
 | 
				
			||||||
 | 
					  sn_org_kde_status_notifier_item_call_secondary_activate_finish(item->proxy_,
 | 
				
			||||||
 | 
					                                                                 res, nullptr);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool waybar::modules::SNI::Item::handleClick(GdkEventButton *const &ev) {
 | 
				
			||||||
 | 
					  if (ev->type == GDK_BUTTON_PRESS) {
 | 
				
			||||||
 | 
					    if (gtk_menu) {
 | 
				
			||||||
 | 
					      if (!gtk_menu->get_attach_widget()) {
 | 
				
			||||||
 | 
					        gtk_menu->attach_to_widget(event_box);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      gtk_menu->popup(ev->button, ev->time);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      sn_org_kde_status_notifier_item_call_activate(
 | 
				
			||||||
 | 
					          proxy_, ev->x, ev->y, nullptr, &Item::handleActivate, this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  } else if (ev->type == GDK_2BUTTON_PRESS) {
 | 
				
			||||||
 | 
					    sn_org_kde_status_notifier_item_call_secondary_activate(
 | 
				
			||||||
 | 
					        proxy_, ev->x, ev->y, nullptr, &Item::handleSecondaryActivate, this);
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										183
									
								
								src/modules/sni/snw.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								src/modules/sni/snw.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,183 @@
 | 
				
			|||||||
 | 
					#include "modules/sni/snw.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace waybar::modules::SNI;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Watcher::Watcher()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  GBusNameOwnerFlags flags = static_cast<GBusNameOwnerFlags>(
 | 
				
			||||||
 | 
					    G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT
 | 
				
			||||||
 | 
					    | G_BUS_NAME_OWNER_FLAGS_REPLACE);
 | 
				
			||||||
 | 
					  bus_name_id_ = g_bus_own_name(G_BUS_TYPE_SESSION,
 | 
				
			||||||
 | 
					    "org.kde.StatusNotifierWatcher", flags,
 | 
				
			||||||
 | 
					    &Watcher::busAcquired, nullptr, nullptr, this, nullptr);
 | 
				
			||||||
 | 
					  watcher_ = sn_org_kde_status_notifier_watcher_skeleton_new();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Watcher::~Watcher()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Watcher::busAcquired(GDBusConnection* connection, const gchar* name,
 | 
				
			||||||
 | 
					  gpointer data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  GError* error = nullptr;
 | 
				
			||||||
 | 
					  auto host = static_cast<SNI::Watcher*>(data);
 | 
				
			||||||
 | 
					  g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(host->watcher_),
 | 
				
			||||||
 | 
					    connection, "/StatusNotifierWatcher", &error);
 | 
				
			||||||
 | 
					  if (error != nullptr) {
 | 
				
			||||||
 | 
					    std::cerr << error->message << std::endl;
 | 
				
			||||||
 | 
					    g_error_free(error);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  g_signal_connect_swapped(host->watcher_,
 | 
				
			||||||
 | 
					    "handle-register-status-notifier-item",
 | 
				
			||||||
 | 
					    G_CALLBACK(&Watcher::handleRegisterItem), data);
 | 
				
			||||||
 | 
					  g_signal_connect_swapped(host->watcher_,
 | 
				
			||||||
 | 
					    "handle-register-status-notifier-host",
 | 
				
			||||||
 | 
					    G_CALLBACK(&Watcher::handleRegisterHost), data);
 | 
				
			||||||
 | 
					  sn_org_kde_status_notifier_watcher_set_protocol_version(host->watcher_, 0);
 | 
				
			||||||
 | 
					  sn_org_kde_status_notifier_watcher_set_is_status_notifier_host_registered(
 | 
				
			||||||
 | 
					    host->watcher_, TRUE);
 | 
				
			||||||
 | 
					  std::cout << "Bus aquired" << std::endl;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					gboolean Watcher::handleRegisterHost(Watcher* obj,
 | 
				
			||||||
 | 
					  GDBusMethodInvocation* invocation, const gchar* service)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  const gchar* bus_name = service;
 | 
				
			||||||
 | 
					  const gchar* object_path = "/StatusNotifierHost";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (*service == '/') {
 | 
				
			||||||
 | 
					    bus_name = g_dbus_method_invocation_get_sender(invocation);
 | 
				
			||||||
 | 
					    object_path = service;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (g_dbus_is_name(bus_name) == FALSE) {
 | 
				
			||||||
 | 
					    g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR,
 | 
				
			||||||
 | 
					      G_DBUS_ERROR_INVALID_ARGS, "D-Bus bus name '%s' is not valid", bus_name);
 | 
				
			||||||
 | 
					    return TRUE;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  auto watch = gfWatchFind(obj->hosts_, bus_name, object_path);
 | 
				
			||||||
 | 
					  if (watch != nullptr) {
 | 
				
			||||||
 | 
					    g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR,
 | 
				
			||||||
 | 
					      G_DBUS_ERROR_INVALID_ARGS, "Status Notifier Host with bus name '%s' and object path '%s' is already registered",
 | 
				
			||||||
 | 
					      bus_name, object_path);
 | 
				
			||||||
 | 
					    return TRUE;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  watch = gfWatchNew(GF_WATCH_TYPE_HOST, service, bus_name, object_path, obj);
 | 
				
			||||||
 | 
					  obj->hosts_ = g_slist_prepend(obj->hosts_, watch);
 | 
				
			||||||
 | 
					  sn_org_kde_status_notifier_watcher_set_is_status_notifier_host_registered(
 | 
				
			||||||
 | 
					    obj->watcher_, TRUE);
 | 
				
			||||||
 | 
					  if (g_slist_length(obj->hosts_)) {
 | 
				
			||||||
 | 
					    sn_org_kde_status_notifier_watcher_emit_status_notifier_host_registered(
 | 
				
			||||||
 | 
					      obj->watcher_);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  sn_org_kde_status_notifier_watcher_complete_register_status_notifier_host(
 | 
				
			||||||
 | 
					    obj->watcher_, invocation);
 | 
				
			||||||
 | 
					  std::cout << "Host registered: " << bus_name << std::endl;
 | 
				
			||||||
 | 
					  return TRUE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					gboolean Watcher::handleRegisterItem(Watcher* obj,
 | 
				
			||||||
 | 
					  GDBusMethodInvocation* invocation, const gchar* service)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  const gchar* bus_name = service;
 | 
				
			||||||
 | 
					  const gchar* object_path = "/StatusNotifierItem";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (*service == '/') {
 | 
				
			||||||
 | 
					    bus_name = g_dbus_method_invocation_get_sender(invocation);
 | 
				
			||||||
 | 
					    object_path = service;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (g_dbus_is_name(bus_name) == FALSE) {
 | 
				
			||||||
 | 
					    g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR,
 | 
				
			||||||
 | 
					      G_DBUS_ERROR_INVALID_ARGS, "D-Bus bus name '%s' is not valid", bus_name);
 | 
				
			||||||
 | 
					    return TRUE;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  auto watch = gfWatchFind(obj->items_, bus_name, object_path);
 | 
				
			||||||
 | 
					  if (watch != nullptr) {
 | 
				
			||||||
 | 
					    g_warning("Status Notifier Item with bus name '%s' and object path '%s' is already registered",
 | 
				
			||||||
 | 
					      bus_name, object_path);
 | 
				
			||||||
 | 
					    sn_org_kde_status_notifier_watcher_complete_register_status_notifier_item(
 | 
				
			||||||
 | 
					      obj->watcher_, invocation);
 | 
				
			||||||
 | 
					    return TRUE;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  watch = gfWatchNew(GF_WATCH_TYPE_ITEM, service, bus_name, object_path, obj);
 | 
				
			||||||
 | 
					  obj->items_ = g_slist_prepend(obj->items_, watch);
 | 
				
			||||||
 | 
					  obj->updateRegisteredItems(obj->watcher_);
 | 
				
			||||||
 | 
					  gchar* tmp = g_strdup_printf("%s%s", bus_name, object_path);
 | 
				
			||||||
 | 
					  sn_org_kde_status_notifier_watcher_emit_status_notifier_item_registered(
 | 
				
			||||||
 | 
					    obj->watcher_, tmp);
 | 
				
			||||||
 | 
					  g_free(tmp);
 | 
				
			||||||
 | 
					  sn_org_kde_status_notifier_watcher_complete_register_status_notifier_item(
 | 
				
			||||||
 | 
					    obj->watcher_, invocation);
 | 
				
			||||||
 | 
					  return TRUE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Watcher::GfWatch* Watcher::gfWatchFind(GSList* list, const gchar* bus_name,
 | 
				
			||||||
 | 
					  const gchar* object_path)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  for (GSList* l = list; l != nullptr; l = g_slist_next (l)) {
 | 
				
			||||||
 | 
					    GfWatch* watch = static_cast<GfWatch*>(l->data);
 | 
				
			||||||
 | 
					    if (g_strcmp0 (watch->bus_name, bus_name) == 0
 | 
				
			||||||
 | 
					      && g_strcmp0 (watch->object_path, object_path) == 0) {
 | 
				
			||||||
 | 
					      return watch;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Watcher::GfWatch* Watcher::gfWatchNew(GfWatchType type, const gchar* service,
 | 
				
			||||||
 | 
					  const gchar* bus_name, const gchar* object_path, Watcher* watcher)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  GfWatch* watch = g_new0(GfWatch, 1);
 | 
				
			||||||
 | 
					  watch->type = type;
 | 
				
			||||||
 | 
					  watch->watcher = watcher;
 | 
				
			||||||
 | 
					  watch->service = g_strdup(service);
 | 
				
			||||||
 | 
					  watch->bus_name = g_strdup(bus_name);
 | 
				
			||||||
 | 
					  watch->object_path = g_strdup(object_path);
 | 
				
			||||||
 | 
					  watch->watch_id = g_bus_watch_name(G_BUS_TYPE_SESSION, bus_name,
 | 
				
			||||||
 | 
					    G_BUS_NAME_WATCHER_FLAGS_NONE, nullptr, &Watcher::nameVanished, watch,
 | 
				
			||||||
 | 
					    nullptr);
 | 
				
			||||||
 | 
					  return watch;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Watcher::nameVanished(GDBusConnection* connection, const char* name,
 | 
				
			||||||
 | 
					  gpointer data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  auto watch = static_cast<GfWatch *>(data);
 | 
				
			||||||
 | 
					  if (watch->type == GF_WATCH_TYPE_HOST) {
 | 
				
			||||||
 | 
					    watch->watcher->hosts_ = g_slist_remove(watch->watcher->hosts_, watch);
 | 
				
			||||||
 | 
					    if (watch->watcher->hosts_ == nullptr)  {
 | 
				
			||||||
 | 
					      sn_org_kde_status_notifier_watcher_set_is_status_notifier_host_registered(
 | 
				
			||||||
 | 
					        watch->watcher->watcher_, FALSE);
 | 
				
			||||||
 | 
					      sn_org_kde_status_notifier_watcher_emit_status_notifier_host_registered(
 | 
				
			||||||
 | 
					        watch->watcher->watcher_);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  } else if (watch->type == GF_WATCH_TYPE_ITEM) {
 | 
				
			||||||
 | 
					    watch->watcher->items_ = g_slist_remove(watch->watcher->items_, watch);
 | 
				
			||||||
 | 
					    watch->watcher->updateRegisteredItems(watch->watcher->watcher_);
 | 
				
			||||||
 | 
					    gchar* tmp = g_strdup_printf("%s%s", watch->bus_name, watch->object_path);
 | 
				
			||||||
 | 
					    sn_org_kde_status_notifier_watcher_emit_status_notifier_item_unregistered(
 | 
				
			||||||
 | 
					      watch->watcher->watcher_, tmp);
 | 
				
			||||||
 | 
					    g_free(tmp);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Watcher::updateRegisteredItems(SnOrgKdeStatusNotifierWatcher* obj)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  GVariantBuilder builder;
 | 
				
			||||||
 | 
					  g_variant_builder_init(&builder, G_VARIANT_TYPE("as")); 
 | 
				
			||||||
 | 
					  for (GSList* l = items_; l != nullptr; l = g_slist_next(l)) {
 | 
				
			||||||
 | 
					    GfWatch* watch = static_cast<GfWatch*>(l->data);
 | 
				
			||||||
 | 
					    gchar* item = g_strdup_printf("%s%s", watch->bus_name, watch->object_path);
 | 
				
			||||||
 | 
					    g_variant_builder_add(&builder, "s", item);
 | 
				
			||||||
 | 
					    g_free(item);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  GVariant* variant = g_variant_builder_end(&builder);
 | 
				
			||||||
 | 
					  const gchar** items = g_variant_get_strv(variant, nullptr);
 | 
				
			||||||
 | 
					  sn_org_kde_status_notifier_watcher_set_registered_status_notifier_items(
 | 
				
			||||||
 | 
					    obj, items);
 | 
				
			||||||
 | 
					  g_variant_unref(variant);
 | 
				
			||||||
 | 
					  g_free(items);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										21
									
								
								src/modules/sni/tray.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/modules/sni/tray.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					#include "modules/sni/tray.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					waybar::modules::SNI::Tray::Tray(const Json::Value &config)
 | 
				
			||||||
 | 
					    : config_(config), watcher_(), host_(&dp) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto waybar::modules::SNI::Tray::update() -> void {
 | 
				
			||||||
 | 
					  for (auto &item : host_.items) {
 | 
				
			||||||
 | 
					    item.event_box.set_tooltip_text(item.title);
 | 
				
			||||||
 | 
					    box_.pack_start(item.event_box);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (box_.get_children().size() > 0) {
 | 
				
			||||||
 | 
					    box_.set_name("tray");
 | 
				
			||||||
 | 
					    box_.show_all();
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    box_.set_name("");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					waybar::modules::SNI::Tray::operator Gtk::Widget &() { return box_; }
 | 
				
			||||||
@@ -44,7 +44,7 @@ auto waybar::modules::sway::Window::update() -> void
 | 
				
			|||||||
std::tuple<int, std::string> waybar::modules::sway::Window::getFocusedNode(
 | 
					std::tuple<int, std::string> waybar::modules::sway::Window::getFocusedNode(
 | 
				
			||||||
  Json::Value nodes)
 | 
					  Json::Value nodes)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  for (auto &node : nodes) {
 | 
					  for (auto const& node : nodes) {
 | 
				
			||||||
    if (node["focused"].asBool() && node["type"] == "con") {
 | 
					    if (node["focused"].asBool() && node["type"] == "con") {
 | 
				
			||||||
      return { node["id"].asInt(), node["name"].asString() };
 | 
					      return { node["id"].asInt(), node["name"].asString() };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,7 +49,7 @@ auto waybar::modules::sway::Workspaces::update() -> void
 | 
				
			|||||||
      ++it;
 | 
					      ++it;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  for (auto node : workspaces_) {
 | 
					  for (auto const& node : workspaces_) {
 | 
				
			||||||
    if (!config_["all-outputs"].asBool()
 | 
					    if (!config_["all-outputs"].asBool()
 | 
				
			||||||
      && bar_.output_name != node["output"].asString()) {
 | 
					      && bar_.output_name != node["output"].asString()) {
 | 
				
			||||||
      continue;
 | 
					      continue;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user