diff --git a/metadata/panel.xml b/metadata/panel.xml
index 6da3f7202..b7fb81201 100644
--- a/metadata/panel.xml
+++ b/metadata/panel.xml
@@ -235,6 +235,18 @@ If full_span is off, both sides of the panel will take the same amount of space,
+ <_short>Power profiles
+
+ <_short>Battery Icon Size
+ 0
+ 0
+
+
+ <_short>Battery Font
+ default
+
+
+
<_short>Network
<_short>Clock Font
diff --git a/src/dock/dock.cpp b/src/dock/dock.cpp
index ce3c924d8..f5698a80d 100644
--- a/src/dock/dock.cpp
+++ b/src/dock/dock.cpp
@@ -11,13 +11,9 @@
#include "dock.hpp"
#include "../util/gtk-utils.hpp"
#include "gtkmm/flowbox.h"
-#include "network/manager.hpp"
-#include "network/network-widget.hpp"
-#include "network/network.hpp"
#include "wf-popover.hpp"
#include
-
class WfDock::impl
{
WayfireOutput *output;
@@ -25,8 +21,6 @@ class WfDock::impl
wl_surface *_wl_surface;
Gtk::FlowBox box;
std::unique_ptr network_image;
- std::unique_ptr network_control;
- std::shared_ptr network_manager;
WfOption css_path{"dock/css_path"};
WfOption entries_per_line{"dock/max_per_line"};
diff --git a/src/locker/plugin/battery.cpp b/src/locker/plugin/battery.cpp
index e7c4d2d2d..f3f8a53f5 100644
--- a/src/locker/plugin/battery.cpp
+++ b/src/locker/plugin/battery.cpp
@@ -1,285 +1,5 @@
-#include
-#include
-#include
-#include
-#include
-
-#include "lockergrid.hpp"
-#include "timedrevealer.hpp"
#include "battery.hpp"
-
-static const std::string BATTERY_STATUS_ICON = "icon"; // icon
-static const std::string BATTERY_STATUS_PERCENT = "percentage"; // icon + percentage
-static const std::string BATTERY_STATUS_FULL = "full"; // icon + percentage + TimeToFull/TimeToEmpty
-
-static bool is_charging(uint32_t state)
-{
- return (state == 1) || (state == 5);
-}
-
-static bool is_discharging(uint32_t state)
-{
- return (state == 2) || (state == 6);
-}
-
-static std::string state_descriptions[] = {
- "Unknown", // 0
- "Charging", // 1
- "Discharging", // 2
- "Empty", // 3
- "Fully charged", // 4
- "Pending charge", // 5
- "Pending discharge", // 6
-};
-
-static std::string get_device_type_description(uint32_t type)
-{
- if (type == 2)
- {
- return "Battery ";
- }
-
- if (type == 3)
- {
- return "UPS ";
- }
-
- return "";
-}
-
-static std::string format_digit(int digit)
-{
- return digit <= 9 ? ("0" + std::to_string(digit)) :
- std::to_string(digit);
-}
-
-static std::string uint_to_time(int64_t time)
-{
- int hrs = time / 3600;
- int min = (time / 60) % 60;
-
- return format_digit(hrs) + ":" + format_digit(min);
-}
-
-void WayfireLockerBatteryPlugin::update_percentages(std::string text)
-{
- for (auto& it : widgets)
- {
- it.second->label.set_label(text);
- }
-}
-
-void WayfireLockerBatteryPlugin::update_descriptions(std::string text)
-{
- for (auto& it : widgets)
- {
- it.second->subtext.set_label(text);
- }
-}
-
-void WayfireLockerBatteryPlugin::update_images()
-{
- Glib::Variant icon_name;
- display_device->get_cached_property(icon_name, ICON);
- for (auto& it : widgets)
- {
- it.second->image.set_from_icon_name(icon_name.get());
- }
-}
-
-WayfireLockerBatteryPluginWidget::WayfireLockerBatteryPluginWidget() :
- WayfireLockerTimedRevealer("locker/battery_always")
-{
- set_child(grid);
- label.add_css_class("battery-percent");
- subtext.add_css_class("battery-description");
- image.add_css_class("battery-image");
-
- grid.attach(image, 0, 0);
- grid.attach(label, 1, 0);
- grid.attach(subtext, 0, 1, 2, 1);
-}
-
-void WayfireLockerBatteryPlugin::add_output(std::string id, std::shared_ptr grid)
-{
- widgets.emplace(id, new WayfireLockerBatteryPluginWidget());
- auto widget = widgets[id];
-
- if (!show_state)
- {
- widget->hide();
- }
-
- grid->attach(*widget, (std::string)position);
-
- update_details();
-}
-
-void WayfireLockerBatteryPlugin::remove_output(std::string id, std::shared_ptr grid)
-{
- grid->remove(*widgets[id]);
- widgets.erase(id);
-}
-
-void WayfireLockerBatteryPlugin::init()
-{
- if (!setup_dbus())
- {
- hide();
- }
-}
-
-void WayfireLockerBatteryPlugin::deinit()
-{
- if (signal)
- {
- signal.disconnect();
- }
-
- display_device = nullptr;
- upower_proxy = nullptr;
- connection = nullptr;
-}
-
-bool WayfireLockerBatteryPlugin::setup_dbus()
-{
- auto cancellable = Gio::Cancellable::create();
- connection = Gio::DBus::Connection::get_sync(Gio::DBus::BusType::SYSTEM, cancellable);
- if (!connection)
- {
- std::cerr << "Failed to connect to dbus" << std::endl;
- return false;
- }
-
- upower_proxy = Gio::DBus::Proxy::create_sync(connection, UPOWER_NAME,
- "/org/freedesktop/UPower",
- "org.freedesktop.UPower");
- if (!upower_proxy)
- {
- std::cerr << "Failed to connect to UPower" << std::endl;
- return false;
- }
-
- display_device = Gio::DBus::Proxy::create_sync(connection,
- UPOWER_NAME,
- DISPLAY_DEVICE,
- "org.freedesktop.UPower.Device");
- if (!display_device)
- {
- return false;
- }
-
- Glib::Variant present;
- display_device->get_cached_property(present, SHOULD_DISPLAY);
- if (present.get())
- {
- signal = display_device->signal_properties_changed().connect(
- sigc::mem_fun(*this, &WayfireLockerBatteryPlugin::on_properties_changed));
-
- return true;
- }
-
- return false;
-}
-
-void WayfireLockerBatteryPlugin::on_properties_changed(
- const Gio::DBus::Proxy::MapChangedProperties& properties,
- const std::vector& invalidated)
-{
- bool invalid_icon = false, invalid_details = false;
- for (auto& prop : properties)
- {
- if (prop.first == ICON)
- {
- invalid_icon = true;
- }
-
- if ((prop.first == TYPE) || (prop.first == STATE) || (prop.first == PERCENTAGE) ||
- (prop.first == TIMETOFULL) || (prop.first == TIMETOEMPTY))
- {
- invalid_details = true;
- }
-
- if (prop.first == SHOULD_DISPLAY)
- {}
- }
-
- if (invalid_icon)
- {
- update_images();
- }
-
- if (invalid_details)
- {
- update_details();
- }
-}
-
-void WayfireLockerBatteryPlugin::update_details()
-{
- if (display_device == nullptr)
- {
- std::cout << "No battery proxy!" << std::endl;
- return;
- }
-
- Glib::Variant type;
- display_device->get_cached_property(type, TYPE);
-
- Glib::Variant vstate;
- display_device->get_cached_property(vstate, STATE);
- uint32_t state = vstate.get();
-
- Glib::Variant vpercentage;
- display_device->get_cached_property(vpercentage, PERCENTAGE);
- auto percentage_string = std::to_string((int)vpercentage.get()) + "%";
-
- Glib::Variant time_to_full;
- display_device->get_cached_property(time_to_full, TIMETOFULL);
-
- Glib::Variant time_to_empty;
- display_device->get_cached_property(time_to_empty, TIMETOEMPTY);
-
- std::string description = state_descriptions[state];
- if (is_charging(state))
- {
- description += ", " + uint_to_time(time_to_full.get()) + " until full";
- } else if (is_discharging(state))
- {
- description += ", " + uint_to_time(time_to_empty.get()) + " remaining";
- }
-
- if (state == 0) /* Unknown */
- {
- hide();
- return;
- }
-
- show();
- update_descriptions(get_device_type_description(type.get()) + description);
- update_percentages(percentage_string);
- update_images();
-}
-
-void WayfireLockerBatteryPlugin::hide()
-{
- show_state = false;
- for (auto& it : widgets)
- {
- it.second->hide();
- }
-}
-
-void WayfireLockerBatteryPlugin::show()
-{
- show_state = true;
- for (auto& it : widgets)
- {
- it.second->show();
- }
-}
-
WayfireLockerBatteryPlugin::WayfireLockerBatteryPlugin() :
- WayfireLockerPlugin("locker/battery")
+ WayfireLockerMultiOutputPlugin("locker/battery")
{}
diff --git a/src/locker/plugin/battery.hpp b/src/locker/plugin/battery.hpp
index fadb7d682..5e531799d 100644
--- a/src/locker/plugin/battery.hpp
+++ b/src/locker/plugin/battery.hpp
@@ -1,65 +1,28 @@
#pragma once
-#include
-#include
-#include
-#include
-#include
-#include
-#include "plugin.hpp"
-#include "lockergrid.hpp"
-#include "timedrevealer.hpp"
+#include "multi-output-timed-plugin.hpp"
+#include "widget-utils/battery.hpp"
-using DBusConnection = Glib::RefPtr;
-using DBusProxy = Glib::RefPtr;
-/* Shamelessly copied in from battery in panel */
-
-#define UPOWER_NAME "org.freedesktop.UPower"
-#define DISPLAY_DEVICE "/org/freedesktop/UPower/devices/DisplayDevice"
-
-#define ICON "IconName"
-#define TYPE "Type"
-#define STATE "State"
-#define PERCENTAGE "Percentage"
-#define TIMETOFULL "TimeToFull"
-#define TIMETOEMPTY "TimeToEmpty"
-#define SHOULD_DISPLAY "IsPresent"
-
-class WayfireLockerBatteryPluginWidget : public WayfireLockerTimedRevealer
+class WayfireLockerBatteryPluginWidget : public WayfireLockerTimedWidget
{
public:
- Gtk::Grid grid;
- Gtk::Image image;
- Gtk::Label label, subtext;
- WayfireLockerBatteryPluginWidget();
+ WayfireLockerBatteryPluginWidget() :
+ WayfireLockerTimedWidget("locker/battery_always")
+ {}
};
-class WayfireLockerBatteryPlugin : public WayfireLockerPlugin
+class WayfireLockerBatteryPlugin :
+ public WayfireLockerMultiOutputPlugin
{
private:
- DBusConnection connection;
- DBusProxy upower_proxy, display_device;
- void on_properties_changed(
- const Gio::DBus::Proxy::MapChangedProperties& properties,
- const std::vector& invalidated);
- bool setup_dbus();
sigc::connection signal;
+ protected:
+ std::shared_ptr create_widget() override
+ {
+ return std::make_shared();
+ }
+
public:
WayfireLockerBatteryPlugin();
- void add_output(std::string id, std::shared_ptr grid) override;
- void remove_output(std::string id, std::shared_ptr grid) override;
- void init() override;
- void deinit() override;
- void hide();
- void show();
- bool show_state = true;
-
- void update_percentages(std::string text);
- void update_descriptions(std::string text);
- void update_images();
- void update_details();
-
-
- std::map> widgets;
};
diff --git a/src/locker/plugin/clock.cpp b/src/locker/plugin/clock.cpp
index 7b0e71985..6dd318e98 100644
--- a/src/locker/plugin/clock.cpp
+++ b/src/locker/plugin/clock.cpp
@@ -1,80 +1,5 @@
-#include
-#include
-#include
-
-#include "lockergrid.hpp"
-#include "timedrevealer.hpp"
#include "clock.hpp"
-
-void WayfireLockerClockPlugin::update_labels(std::string text)
-{
- for (auto& it : widgets)
- {
- it.second->label.set_markup(text);
- }
-
- label_contents = text;
-}
-
-WayfireLockerClockPluginWidget::WayfireLockerClockPluginWidget(std::string contents) :
- WayfireLockerTimedRevealer("locker/clock_always")
-{
- set_child(label);
- label.add_css_class("clock");
- label.set_markup(contents);
- label.set_justify(Gtk::Justification::CENTER);
-}
-
-void WayfireLockerClockPlugin::add_output(std::string id, std::shared_ptr grid)
-{
- widgets.emplace(id, new WayfireLockerClockPluginWidget(label_contents));
- auto widget = widgets[id];
-
- grid->attach(*widget, position);
-}
-
-void WayfireLockerClockPlugin::remove_output(std::string id, std::shared_ptr grid)
-{
- grid->remove(*widgets[id]);
- widgets.erase(id);
-}
-
-void WayfireLockerClockPlugin::update_time()
-{
- auto time = Glib::DateTime::create_now_local();
- auto text = time.format((std::string)format);
-
- /* Sometimes GLib::DateTime will add leading spaces. This results in
- * unevenly balanced padding around the text, which looks quite bad.
- *
- * This could be circumvented with the modifiers the user passes to the
- * format string, * but to remove the requirement that the user does
- * something fancy, we just remove any leading spaces. */
- int i = 0;
- while (i < (int)text.length() && text[i] == ' ')
- {
- i++;
- }
-
- this->update_labels(text.substr(i));
-}
-
WayfireLockerClockPlugin::WayfireLockerClockPlugin() :
- WayfireLockerPlugin("locker/clock")
+ WayfireLockerMultiOutputPlugin("locker/clock")
{}
-
-void WayfireLockerClockPlugin::init()
-{
- timeout = Glib::signal_timeout().connect_seconds(
- [this] ()
- {
- this->update_time();
- return G_SOURCE_CONTINUE;
- }, 1);
-}
-
-void WayfireLockerClockPlugin::deinit()
-{
- timeout.disconnect();
-}
diff --git a/src/locker/plugin/clock.hpp b/src/locker/plugin/clock.hpp
index 4f27fd40d..88a184683 100644
--- a/src/locker/plugin/clock.hpp
+++ b/src/locker/plugin/clock.hpp
@@ -1,34 +1,25 @@
#pragma once
-#include
-#include
-#include "plugin.hpp"
-#include "timedrevealer.hpp"
-#include "wf-option-wrap.hpp"
-#include "lockergrid.hpp"
+#include "multi-output-timed-plugin.hpp"
+#include "widget-utils/clock.hpp"
-class WayfireLockerClockPluginWidget : public WayfireLockerTimedRevealer
+class WayfireLockerClockPluginWidget : public WayfireLockerTimedWidget
{
public:
- Gtk::Label label;
- WayfireLockerClockPluginWidget(std::string contents);
+ WayfireLockerClockPluginWidget() :
+ WayfireLockerTimedWidget("locker/clock_always", "clock")
+ {}
};
-class WayfireLockerClockPlugin : public WayfireLockerPlugin
+class WayfireLockerClockPlugin :
+ public WayfireLockerMultiOutputPlugin
{
+ protected:
+ std::shared_ptr create_widget() override
+ {
+ return std::make_shared();
+ }
+
public:
WayfireLockerClockPlugin();
- void add_output(std::string id, std::shared_ptr grid) override;
- void remove_output(std::string id, std::shared_ptr grid) override;
- void init() override;
- void deinit() override;
-
- WfOption format{"locker/clock_format"};
-
- sigc::connection timeout;
- void update_labels(std::string text);
- void update_time();
-
- std::unordered_map> widgets;
- std::string label_contents = "";
};
diff --git a/src/locker/plugin/multi-output-timed-plugin.hpp b/src/locker/plugin/multi-output-timed-plugin.hpp
new file mode 100644
index 000000000..93e6a5ec9
--- /dev/null
+++ b/src/locker/plugin/multi-output-timed-plugin.hpp
@@ -0,0 +1,89 @@
+#pragma once
+
+#include
+#include
+
+#include "plugin.hpp"
+#include "timedrevealer.hpp"
+#include "lockergrid.hpp"
+
+template
+class WayfireLockerMultiOutputPlugin : public WayfireLockerPlugin
+{
+ protected:
+ std::unordered_map> widgets;
+ bool shown = true;
+
+ virtual std::shared_ptr create_widget() = 0;
+
+ public:
+ WayfireLockerMultiOutputPlugin(std::string prefix) :
+ WayfireLockerPlugin(prefix)
+ {}
+
+ void add_output(std::string id, std::shared_ptr grid) override
+ {
+ auto widget = create_widget();
+ widgets.emplace(id, widget);
+
+ if (!shown)
+ {
+ widget->hide();
+ }
+
+ grid->attach(*widget, position);
+ }
+
+ void remove_output(std::string id, std::shared_ptr grid) override
+ {
+ auto it = widgets.find(id);
+ if (it == widgets.end())
+ {
+ return;
+ }
+
+ grid->remove(*it->second);
+ widgets.erase(it);
+ }
+
+ void hide()
+ {
+ shown = false;
+ for (auto& it : widgets)
+ {
+ it.second->hide();
+ }
+ }
+
+ void show()
+ {
+ shown = true;
+ for (auto& it : widgets)
+ {
+ it.second->show();
+ }
+ }
+
+ void init() override
+ {}
+ void deinit() override
+ {}
+};
+
+template
+class WayfireLockerTimedWidget : public WayfireLockerTimedRevealer
+{
+ public:
+ InnerWidgetT inner;
+
+ WayfireLockerTimedWidget(std::string always_option, const char* css_class = nullptr) :
+ WayfireLockerTimedRevealer(always_option),
+ inner("locker")
+ {
+ set_child(inner);
+ if (css_class != nullptr)
+ {
+ inner.add_css_class(css_class);
+ }
+ }
+};
diff --git a/src/locker/plugin/network.hpp b/src/locker/plugin/network.hpp
index 567a305d4..acdef424f 100644
--- a/src/locker/plugin/network.hpp
+++ b/src/locker/plugin/network.hpp
@@ -4,11 +4,9 @@
#include
#include
#include "lockergrid.hpp"
-#include "network/network.hpp"
+#include "widget-utils/network/network.hpp"
#include "plugin.hpp"
-#include "../../util/network/manager.hpp"
-
-
+#include "widget-utils/network/manager.hpp"
class WayfireLockerNetworkPluginWidget : public WayfireLockerTimedRevealer
{
diff --git a/src/locker/plugin/weather.cpp b/src/locker/plugin/weather.cpp
index f87873d89..6750a2b6c 100644
--- a/src/locker/plugin/weather.cpp
+++ b/src/locker/plugin/weather.cpp
@@ -1,165 +1,5 @@
-#include
-#include
-#include
-#include
-
-#include "gtkmm/enums.h"
-#include "lockergrid.hpp"
#include "weather.hpp"
-#include "timedrevealer.hpp"
-#include "wayfire/nonstd/json.hpp"
-
-
-void WayfireLockerWeatherPlugin::update_labels(std::string text)
-{
- for (auto& it : weather_widgets)
- {
- it.second->label.set_markup(text);
- }
-
- label_contents = text;
-}
-
-void WayfireLockerWeatherPlugin::update_icons(std::string path)
-{
- for (auto& it : weather_widgets)
- {
- it.second->image.set(path);
- }
-
- icon_path = path;
-}
-
-WayfireLockerWeatherPluginWidget::WayfireLockerWeatherPluginWidget(std::string contents,
- std::string icon_path) :
- WayfireLockerTimedRevealer("locker/weather_always")
-{
- set_child(box);
- label.add_css_class("weather");
- label.set_markup(contents);
- label.set_justify(Gtk::Justification::CENTER);
- box.prepend(label);
-
- image.add_css_class("weather");
- image.set(icon_path);
- box.append(image);
-}
-
-void WayfireLockerWeatherPlugin::add_output(std::string id, std::shared_ptr grid)
-{
- weather_widgets.emplace(id, new WayfireLockerWeatherPluginWidget(label_contents, icon_path));
-
- auto weather_widget = weather_widgets[id];
- if (!shown)
- {
- weather_widget->hide();
- }
-
- grid->attach(*weather_widget, position);
-}
-
-void WayfireLockerWeatherPlugin::remove_output(std::string id, std::shared_ptr grid)
-{
- grid->remove(*weather_widgets[id]);
- weather_widgets.erase(id);
-}
-
-bool WayfireLockerWeatherPlugin::handle_inotify_event(Glib::IOCondition cond)
-{
- if (cond == Glib::IOCondition::IO_HUP)
- {
- return false;
- }
-
- char buf[1024 * sizeof(inotify_event)];
- read(inotify_fd, buf, sizeof(buf));
-
- update_weather();
-
- return true;
-}
-
-void WayfireLockerWeatherPlugin::update_weather()
-{
- std::ifstream input_file(weather_data_path);
-
- if (!input_file)
- {
- std::cerr << "Error reading json file " << weather_data_path << std::endl;
- hide();
- return;
- }
-
- std::stringstream buf;
- buf << input_file.rdbuf();
-
- wf::json_t json_data;
-
- auto err = wf::json_t::parse_string(buf.str(), json_data);
-
- if (err.has_value())
- {
- std::cerr << "Error parsing json data " << weather_data_path << ": " << *err << std::endl;
- hide();
- return;
- }
-
- if (!json_data.has_member("temp") || !json_data.has_member("icon"))
- {
- std::cerr << "Unexpected weather json data in " << weather_data_path << std::endl;
- hide();
- return;
- }
-
- update_labels(json_data["temp"]);
- update_icons(json_data["icon"]);
-
- show();
-}
WayfireLockerWeatherPlugin::WayfireLockerWeatherPlugin() :
- WayfireLockerPlugin("locker/weather")
+ WayfireLockerMultiOutputPlugin("locker/weather")
{}
-
-void WayfireLockerWeatherPlugin::init()
-{
- weather_data_path = std::string(getenv("HOME")) + "/.local/share/owf/data/data.json";
-
- inotify_fd = inotify_init();
-
- inotify_add_watch(inotify_fd,
- weather_data_path.c_str(),
- IN_CLOSE_WRITE);
-
- inotify_connection = Glib::signal_io().connect(
- sigc::mem_fun(*this, &WayfireLockerWeatherPlugin::handle_inotify_event),
- inotify_fd, Glib::IOCondition::IO_IN | Glib::IOCondition::IO_HUP);
-
- update_weather();
-}
-
-void WayfireLockerWeatherPlugin::deinit()
-{
- inotify_connection.disconnect();
- close(inotify_fd);
-}
-
-void WayfireLockerWeatherPlugin::hide()
-{
- for (auto& it : weather_widgets)
- {
- it.second->hide();
- }
-
- shown = false;
-}
-
-void WayfireLockerWeatherPlugin::show()
-{
- for (auto& it : weather_widgets)
- {
- it.second->show();
- }
-
- shown = true;
-}
diff --git a/src/locker/plugin/weather.hpp b/src/locker/plugin/weather.hpp
index 863e2fa21..031b42a6f 100644
--- a/src/locker/plugin/weather.hpp
+++ b/src/locker/plugin/weather.hpp
@@ -1,47 +1,25 @@
#pragma once
-#include
-#include
-#include
-#include
-#include
-#include "plugin.hpp"
-#include "timedrevealer.hpp"
-#include "wf-option-wrap.hpp"
-#include "lockergrid.hpp"
+#include "multi-output-timed-plugin.hpp"
+#include "widget-utils/weather.hpp"
-class WayfireLockerWeatherPluginWidget : public WayfireLockerTimedRevealer
+class WayfireLockerWeatherPluginWidget : public WayfireLockerTimedWidget
{
public:
- Gtk::Box box;
- Gtk::Label label;
- Gtk::Image image;
- WayfireLockerWeatherPluginWidget(std::string contents, std::string icon_path);
+ WayfireLockerWeatherPluginWidget() :
+ WayfireLockerTimedWidget("locker/weather_always", "weather")
+ {}
};
-class WayfireLockerWeatherPlugin : public WayfireLockerPlugin
+class WayfireLockerWeatherPlugin :
+ public WayfireLockerMultiOutputPlugin
{
+ protected:
+ std::shared_ptr create_widget() override
+ {
+ return std::make_shared();
+ }
+
public:
WayfireLockerWeatherPlugin();
- void add_output(std::string id, std::shared_ptr grid) override;
- void remove_output(std::string id, std::shared_ptr grid) override;
- void init() override;
- void deinit() override;
-
-
- int inotify_fd;
- sigc::connection inotify_connection;
- std::string weather_data_path;
- bool handle_inotify_event(Glib::IOCondition cond);
- void update_weather();
- void update_labels(std::string text);
- void update_icons(std::string path);
-
- void hide();
- void show();
-
- std::unordered_map> weather_widgets;
- std::string label_contents = "";
- std::string icon_path = "";
- bool shown;
};
diff --git a/src/panel/meson.build b/src/panel/meson.build
index ee51c0a0a..f8a238640 100644
--- a/src/panel/meson.build
+++ b/src/panel/meson.build
@@ -1,5 +1,6 @@
widget_sources = [
'widgets/battery.cpp',
+ 'widgets/power-profiles.cpp',
'widgets/menu/menu.cpp',
'widgets/menu/layout.cpp',
'widgets/menu/logout.cpp',
@@ -92,4 +93,4 @@ executable(
['panel.cpp'] + widget_sources,
dependencies: deps,
install: true,
-)
\ No newline at end of file
+)
diff --git a/src/panel/panel.cpp b/src/panel/panel.cpp
index eef0910ee..c5280f71b 100644
--- a/src/panel/panel.cpp
+++ b/src/panel/panel.cpp
@@ -14,8 +14,8 @@
#include
#include "panel.hpp"
-#include "network/manager.hpp"
#include "widgets/battery.hpp"
+#include "widgets/power-profiles.hpp"
#include "widgets/command-output.hpp"
#include "widgets/language.hpp"
#include "widgets/menu/menu.hpp"
@@ -232,6 +232,11 @@ class WayfirePanel::impl
return Widget(new WayfireBatteryInfo());
}
+ if (name == "power-profiles")
+ {
+ return Widget(new WayfirePowerProfiles());
+ }
+
if (name == "volume")
{
#ifdef HAVE_PULSE
@@ -606,6 +611,7 @@ void WayfirePanelApp::on_activate()
{"panel/menu_category_icon_size", ".app-category .default-icon"},
{"panel/launchers_size", ".launcher.widget-icon"},
{"panel/battery_icon_size", ".battery image.widget-icon"},
+ {"panel/power_profiles_icon_size", ".power-profiles image.widget-icon"},
{"panel/network_icon_size", ".network .widget-icon"},
{"panel/volume_icon_size", ".volume .widget-icon"},
{"panel/mixer_icon_size", ".mixer .widget-icon"},
@@ -628,6 +634,7 @@ void WayfirePanelApp::on_activate()
new CssFromConfigBool("panel/network_icon_invert_color", ".network-icon{filter:invert(100%);}", "");
new CssFromConfigFont("panel/battery_font", ".battery {", "}");
+ new CssFromConfigFont("panel/power_profiles_font", ".power_profiles {", "}");
new CssFromConfigFont("panel/clock_font", ".clock {", "}");
new CssFromConfigFont("panel/weather_font", ".weather {", "}");
}
diff --git a/src/panel/widgets/battery.cpp b/src/panel/widgets/battery.cpp
index 41f56be71..e2684ef75 100644
--- a/src/panel/widgets/battery.cpp
+++ b/src/panel/widgets/battery.cpp
@@ -1,280 +1,6 @@
#include
-#include
#include "battery.hpp"
-#define POWER_PROFILE_PATH "/org/freedesktop/UPower/PowerProfiles"
-#define POWER_PROFILE_NAME "org.freedesktop.UPower.PowerProfiles"
-#define UPOWER_NAME "org.freedesktop.UPower"
-#define DISPLAY_DEVICE "/org/freedesktop/UPower/devices/DisplayDevice"
-
-#define ICON "IconName"
-#define TYPE "Type"
-#define STATE "State"
-#define PERCENTAGE "Percentage"
-#define TIMETOFULL "TimeToFull"
-#define TIMETOEMPTY "TimeToEmpty"
-#define SHOULD_DISPLAY "IsPresent"
-
-#define DEGRADED "PerformanceDegraded"
-#define PROFILES "Profiles"
-#define ACTIVE_PROFILE "ActiveProfile"
-
-static std::string get_device_type_description(uint32_t type)
-{
- if (type == 2)
- {
- return "Battery ";
- }
-
- if (type == 3)
- {
- return "UPS ";
- }
-
- return "";
-}
-
-void WayfireBatteryInfo::on_properties_changed(
- const Gio::DBus::Proxy::MapChangedProperties& properties,
- const std::vector& invalidated)
-{
- bool invalid_icon = false, invalid_details = false;
- bool invalid_state = false;
- for (auto& prop : properties)
- {
- if (prop.first == ICON)
- {
- invalid_icon = true;
- }
-
- if ((prop.first == TYPE) || (prop.first == STATE) || (prop.first == PERCENTAGE) ||
- (prop.first == TIMETOFULL) || (prop.first == TIMETOEMPTY))
- {
- invalid_details = true;
- }
-
- if (prop.first == SHOULD_DISPLAY)
- {
- invalid_state = true;
- }
- }
-
- if (invalid_icon)
- {
- update_icon();
- }
-
- if (invalid_details)
- {
- update_details();
- }
-
- if (invalid_state)
- {
- update_state();
- }
-}
-
-void WayfireBatteryInfo::update_icon()
-{
- if (!feat_bat && feat_modes)
- {
- icon.set_from_icon_name("power-profile-" + power_mode);
- return;
- }
-
- Glib::Variant icon_name;
- display_device->get_cached_property(icon_name, ICON);
- icon.set_from_icon_name(icon_name.get());
-}
-
-static std::string state_descriptions[] = {
- "Unknown", // 0
- "Charging", // 1
- "Discharging", // 2
- "Empty", // 3
- "Fully charged", // 4
- "Pending charge", // 5
- "Pending discharge", // 6
-};
-
-static bool is_charging(uint32_t state)
-{
- return (state == 1) || (state == 5);
-}
-
-static bool is_discharging(uint32_t state)
-{
- return (state == 2) || (state == 6);
-}
-
-static std::string format_digit(int digit)
-{
- return digit <= 9 ? ("0" + std::to_string(digit)) :
- std::to_string(digit);
-}
-
-static std::string uint_to_time(int64_t time)
-{
- int hrs = time / 3600;
- int min = (time / 60) % 60;
-
- return format_digit(hrs) + ":" + format_digit(min);
-}
-
-void WayfireBatteryInfo::update_details()
-{
- Glib::Variant type;
- display_device->get_cached_property(type, TYPE);
-
- Glib::Variant vstate;
- display_device->get_cached_property(vstate, STATE);
- uint32_t state = vstate.get();
-
- Glib::Variant vpercentage;
- display_device->get_cached_property(vpercentage, PERCENTAGE);
- auto percentage_string = std::to_string((int)vpercentage.get()) + "%";
-
- Glib::Variant time_to_full;
- display_device->get_cached_property(time_to_full, TIMETOFULL);
-
- Glib::Variant time_to_empty;
- display_device->get_cached_property(time_to_empty, TIMETOEMPTY);
-
- std::string description = percentage_string + ", " + state_descriptions[state];
- if (is_charging(state))
- {
- description += ", " + uint_to_time(time_to_full.get()) + " until full";
- } else if (is_discharging(state))
- {
- description += ", " + uint_to_time(time_to_empty.get()) + " remaining";
- }
-
- box->set_tooltip_text(
- get_device_type_description(type.get()) + description);
-
- if (status_opt.value() == BATTERY_STATUS_PERCENT)
- {
- label.set_text(percentage_string);
- overlay.remove_overlay(label);
- box->set_child(label);
- } else if (status_opt.value() == BATTERY_STATUS_FULL)
- {
- label.set_text(description);
- auto children = overlay.get_children();
- if (std::count(children.begin(), children.end(), &label))
- {
- overlay.remove_overlay(label);
- }
-
- box->set_child(label);
- } else if (status_opt.value() == BATTERY_STATUS_OVERLAY)
- {
- label.set_text(percentage_string);
- auto children = box->get_children();
- if (std::count(children.begin(), children.end(), &label))
- {
- box->remove(label);
- }
-
- overlay.add_overlay(label);
- }
-
- if (status_opt.value() == BATTERY_STATUS_ICON)
- {
- label.hide();
- } else
- {
- label.show();
- }
-}
-
-void WayfireBatteryInfo::update_state()
-{
- std::cout << "unimplemented reached, in battery.cpp: "
- "\n\tWayfireBatteryInfo::update_state()" << std::endl;
-}
-
-bool WayfireBatteryInfo::setup_dbus_power_modes()
-{
- auto cancellable = Gio::Cancellable::create();
- connection = Gio::DBus::Connection::get_sync(Gio::DBus::BusType::SYSTEM, cancellable);
- if (!connection)
- {
- std::cerr << "Failed to connect to dbus" << std::endl;
- return false;
- }
-
- powerprofile_proxy = Gio::DBus::Proxy::create_sync(connection, POWER_PROFILE_NAME,
- POWER_PROFILE_PATH,
- POWER_PROFILE_NAME);
- if (!powerprofile_proxy)
- {
- std::cout << "Unable to conect to Power Profiles. Continuing" << std::endl;
- } else
- {
- powerprofile_proxy->signal_properties_changed().connect(
- sigc::mem_fun(*this, &WayfireBatteryInfo::on_upower_properties_changed));
- Glib::Variant current_profile;
- Glib::Variant>> profiles;
- powerprofile_proxy->get_cached_property(current_profile, ACTIVE_PROFILE);
- powerprofile_proxy->get_cached_property(profiles, PROFILES);
-
- if (profiles && current_profile)
- {
- setup_profiles(profiles.get());
- set_current_profile(current_profile.get());
- return true;
- } else
- {
- std::cout << "Unable to conect to Power Profiles. Continuing" << std::endl;
- return false;
- }
- }
-
- return false;
-}
-
-bool WayfireBatteryInfo::setup_dbus_battery()
-{
- auto cancellable = Gio::Cancellable::create();
- connection = Gio::DBus::Connection::get_sync(Gio::DBus::BusType::SYSTEM, cancellable);
- if (!connection)
- {
- std::cerr << "Failed to connect to dbus" << std::endl;
- return false;
- }
-
- upower_proxy = Gio::DBus::Proxy::create_sync(connection, UPOWER_NAME,
- "/org/freedesktop/UPower",
- "org.freedesktop.UPower");
- if (!upower_proxy)
- {
- std::cerr << "Failed to connect to UPower" << std::endl;
- return false;
- }
-
- display_device = Gio::DBus::Proxy::create_sync(connection,
- UPOWER_NAME,
- DISPLAY_DEVICE,
- "org.freedesktop.UPower.Device");
- if (!display_device)
- {
- return false;
- }
-
- Glib::Variant present;
- display_device->get_cached_property(present, SHOULD_DISPLAY);
- if (present.get())
- {
- disp_dev_sig = display_device->signal_properties_changed().connect(
- sigc::mem_fun(*this, &WayfireBatteryInfo::on_properties_changed));
-
- return true;
- }
-
- return false;
-}
void WayfireBatteryInfo::update_layout()
{
@@ -282,10 +8,10 @@ void WayfireBatteryInfo::update_layout()
if (panel_position.value() == PANEL_POSITION_LEFT or panel_position.value() == PANEL_POSITION_RIGHT)
{
- box->set_orientation(Gtk::Orientation::VERTICAL);
+ battery.set_orientation(Gtk::Orientation::VERTICAL);
} else
{
- box->set_orientation(Gtk::Orientation::HORIZONTAL);
+ battery.set_orientation(Gtk::Orientation::HORIZONTAL);
}
}
@@ -294,139 +20,8 @@ void WayfireBatteryInfo::handle_config_reload()
update_layout();
}
-// TODO: simplify config loading
-
void WayfireBatteryInfo::init(Gtk::Box *container)
{
- profiles_menu = Gio::Menu::create();
- state_action = Gio::SimpleAction::create_radio_string("set_profile", "");
-
- // the two features are battery displaying and power modes
- feat_bat = setup_dbus_battery();
- feat_modes = setup_dbus_power_modes();
-
- // ignore if we can’t do either
- if (!feat_bat && !feat_modes)
- {
- return;
- }
-
- box = std::make_unique("panel", "battery");
-
- box->set_child(overlay);
- overlay.set_child(icon);
- icon.add_css_class("widget-icon");
-
- if (feat_bat)
- {
- status_opt.set_callback([=] () { update_details(); });
- update_details();
- }
-
- update_icon();
-
- if (feat_modes)
- {
- auto actions = Gio::SimpleActionGroup::create();
-
- state_action->signal_activate().connect([=] (Glib::VariantBase vb)
- {
- // User has requested a change of state. Don't change the UI choice,
- // let the dbus roundtrip happen to be sure.
- if (vb.is_of_type(Glib::VariantType("s")))
- {
- // Couldn't seem to make proxy send property back, so this will have to do
- Glib::VariantContainerBase params = Glib::Variant>::create({POWER_PROFILE_NAME, ACTIVE_PROFILE, vb});
-
- connection->call_sync(
- POWER_PROFILE_PATH,
- "org.freedesktop.DBus.Properties",
- "Set",
- params,
- NULL,
- POWER_PROFILE_NAME,
- -1,
- Gio::DBus::CallFlags::NONE,
- {});
- }
- });
-
- actions->add_action(state_action);
-
- box->open_on(1);
- box->insert_action_group("actions", actions);
- box->set_spacing(5);
- box->set_menu_model(profiles_menu);
- }
-
- container->append(*box);
-
- icon.property_scale_factor().signal_changed()
- .connect(sigc::mem_fun(*this, &WayfireBatteryInfo::update_icon));
-
+ container->append(battery);
update_layout();
}
-
-void WayfireBatteryInfo::on_upower_properties_changed(
- const Gio::DBus::Proxy::MapChangedProperties& properties,
- const std::vector& invalidated)
-{
- for (auto& prop : properties)
- {
- if (prop.first == ACTIVE_PROFILE)
- {
- if (prop.second.is_of_type(Glib::VariantType("s")))
- {
- auto value_string =
- Glib::VariantBase::cast_dynamic>(prop.second).get();
- set_current_profile(value_string);
- }
- } else if (prop.first == PROFILES)
- {
- // I've been unable to find a way to change possible profiles on the fly, so cannot confirm this
- // works at all.
- auto value = Glib::VariantBase::cast_dynamic>>>(prop.second);
- setup_profiles(value.get());
- }
-
- // TODO Consider watching for "Performance Degraded" events too, but we currently have no way to
- // output this additional information
- }
-}
-
-void WayfireBatteryInfo::set_current_profile(Glib::ustring profile)
-{
- power_mode = profile;
- state_action->set_state(Glib::Variant::create(profile));
- update_icon();
-}
-
-void WayfireBatteryInfo::setup_profiles(std::vector> profiles)
-{
- profiles_menu->remove_all();
- for (auto profile : profiles)
- {
- if (profile.count("Profile") == 1)
- {
- Glib::VariantBase value = profile.at("Profile");
- if (value.is_of_type(Glib::VariantType("s")))
- {
- auto value_string =
- Glib::VariantBase::cast_dynamic>(value).get();
- auto item = Gio::MenuItem::create(value_string, "noactionyet");
-
- item->set_action_and_target("actions.set_profile",
- Glib::Variant::create(value_string));
- profiles_menu->append_item(item);
- }
- }
- }
-}
-
-WayfireBatteryInfo::~WayfireBatteryInfo()
-{
- btn_sig.disconnect();
- disp_dev_sig.disconnect();
-}
diff --git a/src/panel/widgets/battery.hpp b/src/panel/widgets/battery.hpp
index df85e4a88..cdd5d3888 100644
--- a/src/panel/widgets/battery.hpp
+++ b/src/panel/widgets/battery.hpp
@@ -1,70 +1,19 @@
#pragma once
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-
#include
#include "widget.hpp"
-#include "wf-popover.hpp"
-
-using DBusConnection = Glib::RefPtr;
-using DBusProxy = Glib::RefPtr;
-
-static const std::string BATTERY_STATUS_ICON = "icon"; // icon
-static const std::string BATTERY_STATUS_PERCENT = "percentage"; // icon + percentage
-static const std::string BATTERY_STATUS_FULL = "full"; // icon + percentage + TimeToFull/TimeToEmpty
-static const std::string BATTERY_STATUS_OVERLAY = "percentage_overlay";
+#include "widget-utils/battery.hpp"
-class wayfire_config;
class WayfireBatteryInfo : public WayfireWidget
{
- WfOption status_opt{"panel/battery_status"};
-
- sigc::connection btn_sig, disp_dev_sig;
-
- Gtk::Label label;
- std::unique_ptr box;
- Gtk::Overlay overlay;
- std::shared_ptr profiles_menu;
- Gtk::Image icon;
-
- DBusConnection connection;
- DBusProxy upower_proxy, powerprofile_proxy, display_device;
- std::string power_mode = "";
-
- bool feat_bat, feat_modes; // the available features when running
- bool setup_dbus_battery();
- bool setup_dbus_power_modes();
-
- void update_icon();
- void update_details();
- void update_state();
+ ShellBattery battery;
void update_layout();
void handle_config_reload();
- void on_properties_changed(
- const Gio::DBus::Proxy::MapChangedProperties& properties,
- const std::vector& invalidated);
-
- void on_upower_properties_changed(
- const Gio::DBus::Proxy::MapChangedProperties& properties,
- const std::vector& invalidated);
-
- void set_current_profile(Glib::ustring profile);
- void setup_profiles(std::vector> profiles);
-
- std::shared_ptr state_action;
-
public:
+ WayfireBatteryInfo() : battery("panel")
+ {}
virtual void init(Gtk::Box *container);
- virtual ~WayfireBatteryInfo();
};
diff --git a/src/panel/widgets/clock.cpp b/src/panel/widgets/clock.cpp
index 2ea16546f..c983b4b29 100644
--- a/src/panel/widgets/clock.cpp
+++ b/src/panel/widgets/clock.cpp
@@ -1,26 +1,21 @@
#include
#include "clock.hpp"
+
void WayfireClock::init(Gtk::Box *container)
{
button = std::make_unique("panel", "clock");
button->add_css_class("clock");
button->set_child(label);
button->open_on(1);
- label.set_justify(Gtk::Justification::CENTER);
label.show();
- update_label();
-
calendar.show();
button->set_popup_child(calendar);
btn_sig = button->signal_popup().connect(
sigc::mem_fun(*this, &WayfireClock::on_calendar_shown));
container->append(*button);
-
- timeout = Glib::signal_timeout().connect_seconds(
- sigc::mem_fun(*this, &WayfireClock::update_label), 1);
}
void WayfireClock::on_calendar_shown()
@@ -32,29 +27,7 @@ void WayfireClock::on_calendar_shown()
calendar.select_day(now);
}
-bool WayfireClock::update_label()
-{
- auto time = Glib::DateTime::create_now_local();
- auto text = time.format(format.value());
-
- /* Sometimes GLib::DateTime will add leading spaces. This results in
- * unevenly balanced padding around the text, which looks quite bad.
- *
- * This could be circumvented with the modifiers the user passes to the
- * format string, * but to remove the requirement that the user does
- * something fancy, we just remove any leading spaces. */
- size_t i = 0;
- while (i < text.length() && text[i] == ' ')
- {
- i++;
- }
-
- label.set_text(text.substr(i));
- return 1;
-}
-
WayfireClock::~WayfireClock()
{
btn_sig.disconnect();
- timeout.disconnect();
}
diff --git a/src/panel/widgets/clock.hpp b/src/panel/widgets/clock.hpp
index 858f842f8..70b8dc79b 100644
--- a/src/panel/widgets/clock.hpp
+++ b/src/panel/widgets/clock.hpp
@@ -1,23 +1,21 @@
#pragma once
+#include
+
#include "../widget.hpp"
#include "wf-popover.hpp"
-#include
-#include
+#include "widget-utils/clock.hpp"
class WayfireClock : public WayfireWidget
{
- Gtk::Label label;
+ ShellClock label{"panel/clock_format"};
Gtk::Calendar calendar;
std::unique_ptr button;
- sigc::connection timeout, btn_sig;
- WfOption format{"panel/clock_format"};
-
+ sigc::connection btn_sig;
void on_calendar_shown();
public:
void init(Gtk::Box *container) override;
- bool update_label();
~WayfireClock();
};
diff --git a/src/panel/widgets/network.cpp b/src/panel/widgets/network.cpp
index 68eb7126e..909e3a82f 100644
--- a/src/panel/widgets/network.cpp
+++ b/src/panel/widgets/network.cpp
@@ -2,7 +2,6 @@
#include "gtkmm/gesture.h"
#include "gtkmm/gestureclick.h"
#include "gtkmm/gesturelongpress.h"
-#include "network/network.hpp"
#include "wf-popover.hpp"
#include
#include
diff --git a/src/panel/widgets/network.hpp b/src/panel/widgets/network.hpp
index 70e6b3792..bb930fdc6 100644
--- a/src/panel/widgets/network.hpp
+++ b/src/panel/widgets/network.hpp
@@ -10,8 +10,8 @@
#include "wf-popover.hpp"
#include "../widget.hpp"
-#include "../../util/network/manager.hpp"
-#include "network/network-widget.hpp"
+#include "widget-utils/network/manager.hpp"
+#include "widget-utils/network/network-widget.hpp"
class WayfireNetworkInfo : public WayfireWidget
{
diff --git a/src/panel/widgets/power-profiles.cpp b/src/panel/widgets/power-profiles.cpp
new file mode 100644
index 000000000..843670e7b
--- /dev/null
+++ b/src/panel/widgets/power-profiles.cpp
@@ -0,0 +1,157 @@
+#include
+#include
+
+#include "power-profiles.hpp"
+
+#define POWER_PROFILE_PATH "/org/freedesktop/UPower/PowerProfiles"
+#define POWER_PROFILE_NAME "org.freedesktop.UPower.PowerProfiles"
+
+#define PROFILES "Profiles"
+#define ACTIVE_PROFILE "ActiveProfile"
+
+void WayfirePowerProfiles::update_icon()
+{
+ icon.set_from_icon_name("power-profile-" + power_mode);
+}
+
+void WayfirePowerProfiles::set_current_profile(Glib::ustring profile)
+{
+ power_mode = profile;
+ state_action->set_state(Glib::Variant::create(profile));
+ update_icon();
+}
+
+void WayfirePowerProfiles::setup_profiles(std::vector> profiles)
+{
+ profiles_menu->remove_all();
+ for (auto profile : profiles)
+ {
+ if (profile.count("Profile") == 1)
+ {
+ Glib::VariantBase value = profile.at("Profile");
+ if (value.is_of_type(Glib::VariantType("s")))
+ {
+ auto value_string =
+ Glib::VariantBase::cast_dynamic>(value).get();
+ auto item = Gio::MenuItem::create(value_string, "noactionyet");
+
+ item->set_action_and_target("actions.set_profile",
+ Glib::Variant::create(value_string));
+ profiles_menu->append_item(item);
+ }
+ }
+ }
+}
+
+void WayfirePowerProfiles::on_properties_changed(
+ const Gio::DBus::Proxy::MapChangedProperties& properties,
+ const std::vector& invalidated)
+{
+ for (auto& prop : properties)
+ {
+ if (prop.first == ACTIVE_PROFILE)
+ {
+ if (prop.second.is_of_type(Glib::VariantType("s")))
+ {
+ auto value_string =
+ Glib::VariantBase::cast_dynamic>(prop.second).get();
+ set_current_profile(value_string);
+ }
+ } else if (prop.first == PROFILES)
+ {
+ auto value = Glib::VariantBase::cast_dynamic>>>(prop.second);
+ setup_profiles(value.get());
+ }
+ }
+}
+
+bool WayfirePowerProfiles::setup_dbus_power_modes()
+{
+ auto cancellable = Gio::Cancellable::create();
+ connection = Gio::DBus::Connection::get_sync(Gio::DBus::BusType::SYSTEM, cancellable);
+ if (!connection)
+ {
+ std::cerr << "Failed to connect to dbus" << std::endl;
+ return false;
+ }
+
+ powerprofile_proxy = Gio::DBus::Proxy::create_sync(connection, POWER_PROFILE_NAME,
+ POWER_PROFILE_PATH,
+ POWER_PROFILE_NAME);
+ if (!powerprofile_proxy)
+ {
+ std::cout << "Unable to conect to Power Profiles. Continuing" << std::endl;
+ return false;
+ }
+
+ powerprofile_proxy->signal_properties_changed().connect(
+ sigc::mem_fun(*this, &WayfirePowerProfiles::on_properties_changed));
+
+ Glib::Variant current_profile;
+ Glib::Variant>> profiles;
+ powerprofile_proxy->get_cached_property(current_profile, ACTIVE_PROFILE);
+ powerprofile_proxy->get_cached_property(profiles, PROFILES);
+
+ if (profiles && current_profile)
+ {
+ setup_profiles(profiles.get());
+ set_current_profile(current_profile.get());
+ return true;
+ }
+
+ std::cout << "Unable to conect to Power Profiles. Continuing" << std::endl;
+ return false;
+}
+
+void WayfirePowerProfiles::init(Gtk::Box *container)
+{
+ profiles_menu = Gio::Menu::create();
+ state_action = Gio::SimpleAction::create_radio_string("set_profile", "");
+
+ if (!setup_dbus_power_modes())
+ {
+ return;
+ }
+
+ box = std::make_unique("panel", "power-profiles");
+
+ box->set_child(icon);
+ icon.add_css_class("widget-icon");
+
+ auto actions = Gio::SimpleActionGroup::create();
+
+ state_action->signal_activate().connect([=] (Glib::VariantBase vb)
+ {
+ if (vb.is_of_type(Glib::VariantType("s")))
+ {
+ Glib::VariantContainerBase params = Glib::Variant>::create({POWER_PROFILE_NAME, ACTIVE_PROFILE, vb});
+
+ connection->call_sync(
+ POWER_PROFILE_PATH,
+ "org.freedesktop.DBus.Properties",
+ "Set",
+ params,
+ NULL,
+ POWER_PROFILE_NAME,
+ -1,
+ Gio::DBus::CallFlags::NONE,
+ {});
+ }
+ });
+
+ actions->add_action(state_action);
+
+ box->open_on(1);
+ box->insert_action_group("actions", actions);
+ box->set_spacing(5);
+ box->set_menu_model(profiles_menu);
+
+ container->append(*box);
+
+ icon.property_scale_factor().signal_changed()
+ .connect(sigc::mem_fun(*this, &WayfirePowerProfiles::update_icon));
+
+ update_icon();
+}
diff --git a/src/panel/widgets/power-profiles.hpp b/src/panel/widgets/power-profiles.hpp
new file mode 100644
index 000000000..225a63181
--- /dev/null
+++ b/src/panel/widgets/power-profiles.hpp
@@ -0,0 +1,39 @@
+#pragma once
+
+#include
+
+#include
+#include
+#include
+
+#include "widget.hpp"
+#include "wf-popover.hpp"
+
+using DBusConnection = Glib::RefPtr;
+using DBusProxy = Glib::RefPtr;
+
+class WayfirePowerProfiles : public WayfireWidget
+{
+ Gtk::Image icon;
+ std::unique_ptr box;
+ std::shared_ptr profiles_menu;
+ std::shared_ptr state_action;
+
+ DBusConnection connection;
+ DBusProxy powerprofile_proxy;
+ std::string power_mode = "";
+
+ bool setup_dbus_power_modes();
+
+ void update_icon();
+ void set_current_profile(Glib::ustring profile);
+ void setup_profiles(std::vector> profiles);
+
+ void on_properties_changed(
+ const Gio::DBus::Proxy::MapChangedProperties& properties,
+ const std::vector& invalidated);
+
+ public:
+ virtual void init(Gtk::Box *container);
+ virtual ~WayfirePowerProfiles() = default;
+};
diff --git a/src/panel/widgets/weather.cpp b/src/panel/widgets/weather.cpp
index 369777114..1acdeed78 100644
--- a/src/panel/widgets/weather.cpp
+++ b/src/panel/widgets/weather.cpp
@@ -1,93 +1,9 @@
-#include
-#include
-#include
-#include
#include "weather.hpp"
-#include "wayfire/nonstd/json.hpp"
void WayfireWeather::init(Gtk::Box *container)
{
- label.set_justify(Gtk::Justification::CENTER);
- box.get_style_context()->add_class("weather");
- box.prepend(label);
- box.append(icon);
- container->append(box);
-
- weather_data_path = std::string(getenv("HOME")) + "/.local/share/owf/data/data.json";
-
- inotify_fd = inotify_init();
-
- inotify_add_watch(inotify_fd,
- weather_data_path.c_str(),
- IN_CLOSE_WRITE);
-
- inotify_connection = Glib::signal_io().connect(
- sigc::mem_fun(*this, &WayfireWeather::handle_inotify_event),
- inotify_fd, Glib::IOCondition::IO_IN | Glib::IOCondition::IO_HUP);
-
- update_weather();
+ weather.add_css_class("weather");
+ container->append(weather);
}
-bool WayfireWeather::handle_inotify_event(Glib::IOCondition cond)
-{
- if (cond == Glib::IOCondition::IO_HUP)
- {
- return false;
- }
-
- char buf[1024 * sizeof(inotify_event)];
- read(inotify_fd, buf, sizeof(buf));
-
- update_weather();
-
- return true;
-}
-
-void WayfireWeather::update_weather()
-{
- std::ifstream input_file(weather_data_path);
-
- if (!input_file)
- {
- std::cerr << "Error reading json file " << weather_data_path << std::endl;
- return;
- }
-
- std::stringstream buffer;
- buffer << input_file.rdbuf();
-
- wf::json_t json_data;
-
- auto err = wf::json_t::parse_string(buffer.str(), json_data);
-
- if (err.has_value())
- {
- std::cerr << "Error parsing json data " << weather_data_path << ": " << *err << std::endl;
- return;
- }
-
- if (!json_data.has_member("temp") || !json_data.has_member("icon"))
- {
- std::cerr << "Unexpected weather json data in " << weather_data_path << std::endl;
- return;
- }
-
- update_label(json_data["temp"]);
- update_icon(json_data["icon"]);
-}
-
-void WayfireWeather::update_label(std::string temperature)
-{
- label.set_text(temperature);
-}
-
-void WayfireWeather::update_icon(std::string path)
-{
- icon.set(path);
-}
-
-WayfireWeather::~WayfireWeather()
-{
- inotify_connection.disconnect();
- close(inotify_fd);
-}
+WayfireWeather::~WayfireWeather() = default;
diff --git a/src/panel/widgets/weather.hpp b/src/panel/widgets/weather.hpp
index f71267e07..7aa8170b4 100644
--- a/src/panel/widgets/weather.hpp
+++ b/src/panel/widgets/weather.hpp
@@ -1,25 +1,13 @@
#pragma once
#include "../widget.hpp"
-#include "wf-popover.hpp"
-#include
-#include
+#include "widget-utils/weather.hpp"
class WayfireWeather : public WayfireWidget
{
- Gtk::Label label;
- Gtk::Image icon;
- Gtk::Box box;
-
- int inotify_fd;
- sigc::connection inotify_connection;
- std::string weather_data_path;
+ ShellWeather weather{"panel"};
public:
void init(Gtk::Box *container) override;
- bool handle_inotify_event(Glib::IOCondition cond);
- void update_weather();
- void update_label(std::string);
- void update_icon(std::string);
~WayfireWeather();
};
diff --git a/src/util/meson.build b/src/util/meson.build
index d53e3680e..6566d24a0 100644
--- a/src/util/meson.build
+++ b/src/util/meson.build
@@ -1,26 +1,36 @@
+sources = [
+ 'gtk-utils.cpp',
+ 'icon-select.cpp',
+ 'wf-shell-app.cpp',
+ 'wf-autohide-window.cpp',
+ 'wf-popover.cpp',
+ 'css-config.cpp',
+ 'wf-ipc.cpp',
+ 'animated-scale.cpp',
+ 'widget-utils/battery.cpp',
+ 'widget-utils/clock.cpp',
+ 'widget-utils/network/manager.cpp',
+ 'widget-utils/network/wifi.cpp',
+ 'widget-utils/network/wired.cpp',
+ 'widget-utils/network/network-widget.cpp',
+ 'widget-utils/network/wifi-ap.cpp',
+ 'widget-utils/network/network.cpp',
+ 'widget-utils/network/modem.cpp',
+ 'widget-utils/network/settings.cpp',
+ 'widget-utils/network/connection.cpp',
+ 'background-gl.cpp',
+ 'icon-select.cpp'
+]
+
+if get_option('weather') == true
+ sources += [
+ 'widget-utils/weather.cpp',
+ ]
+endif
+
util = static_library(
'util',
- [
- 'gtk-utils.cpp',
- 'icon-select.cpp',
- 'wf-shell-app.cpp',
- 'wf-autohide-window.cpp',
- 'wf-popover.cpp',
- 'css-config.cpp',
- 'wf-ipc.cpp',
- 'animated-scale.cpp',
- 'network/manager.cpp',
- 'network/wifi.cpp',
- 'network/wired.cpp',
- 'network/network-widget.cpp',
- 'network/wifi-ap.cpp',
- 'network/network.cpp',
- 'network/modem.cpp',
- 'network/settings.cpp',
- 'network/connection.cpp',
- 'background-gl.cpp',
- 'icon-select.cpp'
- ],
+ sources,
dependencies: [
wf_protos,
gtklayershell,
diff --git a/src/util/widget-utils/battery.cpp b/src/util/widget-utils/battery.cpp
new file mode 100644
index 000000000..b44644773
--- /dev/null
+++ b/src/util/widget-utils/battery.cpp
@@ -0,0 +1,261 @@
+#include
+#include
+
+#include "battery.hpp"
+#define UPOWER_NAME "org.freedesktop.UPower"
+#define DISPLAY_DEVICE "/org/freedesktop/UPower/devices/DisplayDevice"
+
+#define ICON "IconName"
+#define TYPE "Type"
+#define STATE "State"
+#define PERCENTAGE "Percentage"
+#define TIMETOFULL "TimeToFull"
+#define TIMETOEMPTY "TimeToEmpty"
+#define SHOULD_DISPLAY "IsPresent"
+
+static std::string get_device_type_description(uint32_t type)
+{
+ if (type == 2)
+ {
+ return "Battery ";
+ }
+
+ if (type == 3)
+ {
+ return "UPS ";
+ }
+
+ return "";
+}
+
+ShellBattery::ShellBattery(const std::string& section) :
+ status_opt{section + "/battery_status"}
+{
+ // replace by a generic power icon if unavailable
+ if (!setup())
+ {
+ upower_avail = false;
+ } else
+ {
+ upower_avail = true;
+ status_opt.set_callback([=] () { update_details(); });
+ update_details();
+ }
+
+ append(overlay);
+ overlay.set_child(icon);
+ icon.add_css_class("widget-icon");
+ add_css_class("battery");
+
+ update_icon();
+
+ icon.property_scale_factor().signal_changed()
+ .connect(sigc::mem_fun(*this, &ShellBattery::update_icon));
+}
+
+void ShellBattery::on_properties_changed(
+ const Gio::DBus::Proxy::MapChangedProperties& properties,
+ const std::vector& invalidated)
+{
+ bool invalid_icon = false, invalid_details = false;
+ bool invalid_state = false;
+ for (auto& prop : properties)
+ {
+ if (prop.first == ICON)
+ {
+ invalid_icon = true;
+ }
+
+ if ((prop.first == TYPE) || (prop.first == STATE) || (prop.first == PERCENTAGE) ||
+ (prop.first == TIMETOFULL) || (prop.first == TIMETOEMPTY))
+ {
+ invalid_details = true;
+ }
+
+ if (prop.first == SHOULD_DISPLAY)
+ {
+ invalid_state = true;
+ }
+ }
+
+ if (invalid_icon)
+ {
+ update_icon();
+ }
+
+ if (invalid_details)
+ {
+ update_details();
+ }
+
+ if (invalid_state)
+ {
+ update_state();
+ }
+}
+
+void ShellBattery::update_icon()
+{
+ if (!upower_avail)
+ {
+ icon.set_from_icon_name("power-profile-");
+ return;
+ }
+
+ Glib::Variant icon_name;
+ display_device->get_cached_property(icon_name, ICON);
+ icon.set_from_icon_name(icon_name.get());
+}
+
+static std::string state_descriptions[] = {
+ "Unknown", // 0
+ "Charging", // 1
+ "Discharging", // 2
+ "Empty", // 3
+ "Fully charged", // 4
+ "Pending charge", // 5
+ "Pending discharge", // 6
+};
+
+static bool is_charging(uint32_t state)
+{
+ return (state == 1) || (state == 5);
+}
+
+static bool is_discharging(uint32_t state)
+{
+ return (state == 2) || (state == 6);
+}
+
+static std::string format_digit(int digit)
+{
+ return digit <= 9 ? ("0" + std::to_string(digit)) :
+ std::to_string(digit);
+}
+
+static std::string uint_to_time(int64_t time)
+{
+ int hrs = time / 3600;
+ int min = (time / 60) % 60;
+
+ return format_digit(hrs) + ":" + format_digit(min);
+}
+
+void ShellBattery::update_details()
+{
+ Glib::Variant type;
+ display_device->get_cached_property(type, TYPE);
+
+ Glib::Variant vstate;
+ display_device->get_cached_property(vstate, STATE);
+ uint32_t state = vstate.get();
+
+ Glib::Variant vpercentage;
+ display_device->get_cached_property(vpercentage, PERCENTAGE);
+ auto percentage_string = std::to_string((int)vpercentage.get()) + "%";
+
+ Glib::Variant time_to_full;
+ display_device->get_cached_property(time_to_full, TIMETOFULL);
+
+ Glib::Variant time_to_empty;
+ display_device->get_cached_property(time_to_empty, TIMETOEMPTY);
+
+ std::string description = percentage_string + ", " + state_descriptions[state];
+ if (is_charging(state))
+ {
+ description += ", " + uint_to_time(time_to_full.get()) + " until full";
+ } else if (is_discharging(state))
+ {
+ description += ", " + uint_to_time(time_to_empty.get()) + " remaining";
+ }
+
+ set_tooltip_text(
+ get_device_type_description(type.get()) + description);
+
+ if (status_opt.value() == BATTERY_STATUS_PERCENT)
+ {
+ label.set_text(percentage_string);
+ overlay.remove_overlay(label);
+ append(label);
+ } else if (status_opt.value() == BATTERY_STATUS_FULL)
+ {
+ label.set_text(description);
+ auto children = overlay.get_children();
+ if (std::count(children.begin(), children.end(), &label))
+ {
+ overlay.remove_overlay(label);
+ }
+
+ append(label);
+ } else if (status_opt.value() == BATTERY_STATUS_OVERLAY)
+ {
+ label.set_text(percentage_string);
+ auto children = get_children();
+ if (std::count(children.begin(), children.end(), &label))
+ {
+ remove(label);
+ }
+
+ overlay.add_overlay(label);
+ }
+
+ if (status_opt.value() == BATTERY_STATUS_ICON)
+ {
+ label.hide();
+ } else
+ {
+ label.show();
+ }
+}
+
+void ShellBattery::update_state()
+{
+ std::cout << "unimplemented reached, in battery.cpp: "
+ "\n\tWayfireBatteryInfo::update_state()" << std::endl;
+}
+
+bool ShellBattery::setup()
+{
+ auto cancellable = Gio::Cancellable::create();
+ connection = Gio::DBus::Connection::get_sync(Gio::DBus::BusType::SYSTEM, cancellable);
+ if (!connection)
+ {
+ std::cerr << "Failed to connect to dbus" << std::endl;
+ return false;
+ }
+
+ upower_proxy = Gio::DBus::Proxy::create_sync(connection, UPOWER_NAME,
+ "/org/freedesktop/UPower",
+ "org.freedesktop.UPower");
+ if (!upower_proxy)
+ {
+ std::cerr << "Failed to connect to UPower" << std::endl;
+ return false;
+ }
+
+ display_device = Gio::DBus::Proxy::create_sync(connection,
+ UPOWER_NAME,
+ DISPLAY_DEVICE,
+ "org.freedesktop.UPower.Device");
+ if (!display_device)
+ {
+ return false;
+ }
+
+ Glib::Variant present;
+ display_device->get_cached_property(present, SHOULD_DISPLAY);
+ if (present.get())
+ {
+ disp_dev_sig = display_device->signal_properties_changed().connect(
+ sigc::mem_fun(*this, &ShellBattery::on_properties_changed));
+
+ return true;
+ }
+
+ return false;
+}
+
+ShellBattery::~ShellBattery()
+{
+ disp_dev_sig.disconnect();
+}
diff --git a/src/util/widget-utils/battery.hpp b/src/util/widget-utils/battery.hpp
new file mode 100644
index 000000000..f1a227441
--- /dev/null
+++ b/src/util/widget-utils/battery.hpp
@@ -0,0 +1,54 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+
+#include
+
+using DBusConnection = Glib::RefPtr;
+using DBusProxy = Glib::RefPtr;
+
+static const std::string BATTERY_STATUS_ICON = "icon"; // icon
+static const std::string BATTERY_STATUS_PERCENT = "percentage"; // icon + percentage
+static const std::string BATTERY_STATUS_FULL = "full"; // icon + percentage + TimeToFull/TimeToEmpty
+static const std::string BATTERY_STATUS_OVERLAY = "percentage_overlay";
+
+class ShellBattery : public Gtk::Box
+{
+ WfOption status_opt;
+
+ sigc::connection disp_dev_sig;
+
+ Gtk::Label label;
+ Gtk::Overlay overlay;
+ Gtk::Image icon;
+
+ DBusConnection connection;
+ DBusProxy upower_proxy, display_device;
+
+ bool upower_avail;
+ bool setup();
+
+ void update_icon();
+ void update_details();
+ void update_state();
+
+ void on_properties_changed(
+ const Gio::DBus::Proxy::MapChangedProperties& properties,
+ const std::vector& invalidated);
+
+ public:
+ ShellBattery(const std::string& section);
+
+ bool get_upower_avail();
+
+ ~ShellBattery();
+};
diff --git a/src/util/widget-utils/clock.cpp b/src/util/widget-utils/clock.cpp
new file mode 100644
index 000000000..28511c329
--- /dev/null
+++ b/src/util/widget-utils/clock.cpp
@@ -0,0 +1,40 @@
+#include "clock.hpp"
+
+ShellClock::ShellClock(const std::string& section) :
+ format_opt(section)
+{
+ set_justify(Gtk::Justification::CENTER);
+ update_time();
+
+ timeout = Glib::signal_timeout().connect_seconds(
+ [this] ()
+ {
+ update_time();
+ return G_SOURCE_CONTINUE;
+ }, 1);
+}
+
+ShellClock::~ShellClock()
+{
+ timeout.disconnect();
+}
+
+void ShellClock::update_time()
+{
+ auto time = Glib::DateTime::create_now_local();
+ auto text = time.format(format_opt.value());
+
+ /* Sometimes GLib::DateTime will add leading spaces. This results in
+ * unevenly balanced padding around the text, which looks quite bad.
+ *
+ * This could be circumvented with the modifiers the user passes to the
+ * format string, but to remove the requirement that the user does
+ * something fancy, we just remove any leading spaces. */
+ size_t i = 0;
+ while (i < text.length() && text[i] == ' ')
+ {
+ i++;
+ }
+
+ set_text(text.substr(i));
+}
diff --git a/src/util/widget-utils/clock.hpp b/src/util/widget-utils/clock.hpp
new file mode 100644
index 000000000..61176e547
--- /dev/null
+++ b/src/util/widget-utils/clock.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include
+#include
+#include
+
+#include "wf-option-wrap.hpp"
+
+class ShellClock : public Gtk::Label
+{
+ public:
+ ShellClock(const std::string& section);
+ ~ShellClock();
+
+ void update_time();
+
+ private:
+ WfOption format_opt;
+ sigc::connection timeout;
+};
diff --git a/src/util/network/bluetooth.hpp b/src/util/widget-utils/network/bluetooth.hpp
similarity index 100%
rename from src/util/network/bluetooth.hpp
rename to src/util/widget-utils/network/bluetooth.hpp
diff --git a/src/util/network/connection.cpp b/src/util/widget-utils/network/connection.cpp
similarity index 98%
rename from src/util/network/connection.cpp
rename to src/util/widget-utils/network/connection.cpp
index 81bb9d3d8..1f7fd4e60 100644
--- a/src/util/network/connection.cpp
+++ b/src/util/widget-utils/network/connection.cpp
@@ -1,7 +1,7 @@
#include
#include
#include "connection.hpp"
-#include "network/manager.hpp"
+#include "widget-utils/network/manager.hpp"
Connection::Connection() :
Network("/", nullptr), connection_proxy(nullptr), devices({})
diff --git a/src/util/network/connection.hpp b/src/util/widget-utils/network/connection.hpp
similarity index 100%
rename from src/util/network/connection.hpp
rename to src/util/widget-utils/network/connection.hpp
diff --git a/src/util/network/manager.cpp b/src/util/widget-utils/network/manager.cpp
similarity index 99%
rename from src/util/network/manager.cpp
rename to src/util/widget-utils/network/manager.cpp
index 2be92a40c..6c5784014 100644
--- a/src/util/network/manager.cpp
+++ b/src/util/widget-utils/network/manager.cpp
@@ -10,7 +10,7 @@
#include "connection.hpp"
#include "gtkmm/enums.h"
#include "network.hpp"
-#include "network/settings.hpp"
+#include "widget-utils/network/settings.hpp"
#include "sigc++/functors/mem_fun.h"
#include "vpn.hpp"
#include "wifi.hpp"
diff --git a/src/util/network/manager.hpp b/src/util/widget-utils/network/manager.hpp
similarity index 100%
rename from src/util/network/manager.hpp
rename to src/util/widget-utils/network/manager.hpp
diff --git a/src/util/network/modem.cpp b/src/util/widget-utils/network/modem.cpp
similarity index 100%
rename from src/util/network/modem.cpp
rename to src/util/widget-utils/network/modem.cpp
diff --git a/src/util/network/modem.hpp b/src/util/widget-utils/network/modem.hpp
similarity index 100%
rename from src/util/network/modem.hpp
rename to src/util/widget-utils/network/modem.hpp
diff --git a/src/util/network/network-widget.cpp b/src/util/widget-utils/network/network-widget.cpp
similarity index 100%
rename from src/util/network/network-widget.cpp
rename to src/util/widget-utils/network/network-widget.cpp
diff --git a/src/util/network/network-widget.hpp b/src/util/widget-utils/network/network-widget.hpp
similarity index 100%
rename from src/util/network/network-widget.hpp
rename to src/util/widget-utils/network/network-widget.hpp
diff --git a/src/util/network/network.cpp b/src/util/widget-utils/network/network.cpp
similarity index 100%
rename from src/util/network/network.cpp
rename to src/util/widget-utils/network/network.cpp
diff --git a/src/util/network/network.hpp b/src/util/widget-utils/network/network.hpp
similarity index 100%
rename from src/util/network/network.hpp
rename to src/util/widget-utils/network/network.hpp
diff --git a/src/util/network/null.hpp b/src/util/widget-utils/network/null.hpp
similarity index 100%
rename from src/util/network/null.hpp
rename to src/util/widget-utils/network/null.hpp
diff --git a/src/util/network/settings.cpp b/src/util/widget-utils/network/settings.cpp
similarity index 100%
rename from src/util/network/settings.cpp
rename to src/util/widget-utils/network/settings.cpp
diff --git a/src/util/network/settings.hpp b/src/util/widget-utils/network/settings.hpp
similarity index 100%
rename from src/util/network/settings.hpp
rename to src/util/widget-utils/network/settings.hpp
diff --git a/src/util/network/vpn.hpp b/src/util/widget-utils/network/vpn.hpp
similarity index 100%
rename from src/util/network/vpn.hpp
rename to src/util/widget-utils/network/vpn.hpp
diff --git a/src/util/network/wifi-ap.cpp b/src/util/widget-utils/network/wifi-ap.cpp
similarity index 100%
rename from src/util/network/wifi-ap.cpp
rename to src/util/widget-utils/network/wifi-ap.cpp
diff --git a/src/util/network/wifi-ap.hpp b/src/util/widget-utils/network/wifi-ap.hpp
similarity index 92%
rename from src/util/network/wifi-ap.hpp
rename to src/util/widget-utils/network/wifi-ap.hpp
index e53ffe780..da0396324 100644
--- a/src/util/network/wifi-ap.hpp
+++ b/src/util/widget-utils/network/wifi-ap.hpp
@@ -1,6 +1,6 @@
#pragma once
-#include "network/network.hpp"
-#include "network/settings.hpp"
+#include "widget-utils/network/network.hpp"
+#include "widget-utils/network/settings.hpp"
#include "sigc++/connection.h"
#include
#include
diff --git a/src/util/network/wifi.cpp b/src/util/widget-utils/network/wifi.cpp
similarity index 100%
rename from src/util/network/wifi.cpp
rename to src/util/widget-utils/network/wifi.cpp
diff --git a/src/util/network/wifi.hpp b/src/util/widget-utils/network/wifi.hpp
similarity index 100%
rename from src/util/network/wifi.hpp
rename to src/util/widget-utils/network/wifi.hpp
diff --git a/src/util/network/wired.cpp b/src/util/widget-utils/network/wired.cpp
similarity index 100%
rename from src/util/network/wired.cpp
rename to src/util/widget-utils/network/wired.cpp
diff --git a/src/util/network/wired.hpp b/src/util/widget-utils/network/wired.hpp
similarity index 100%
rename from src/util/network/wired.hpp
rename to src/util/widget-utils/network/wired.hpp
diff --git a/src/util/widget-utils/weather.cpp b/src/util/widget-utils/weather.cpp
new file mode 100644
index 000000000..6ca47a0d4
--- /dev/null
+++ b/src/util/widget-utils/weather.cpp
@@ -0,0 +1,113 @@
+#include
+#include
+#include
+#include
+
+#include
+
+#include "wayfire/nonstd/json.hpp"
+#include "weather.hpp"
+
+
+ShellWeather::ShellWeather(const std::string& section)
+{
+ set_orientation(Gtk::Orientation::HORIZONTAL);
+
+ label.set_justify(Gtk::Justification::CENTER);
+ prepend(label);
+ append(icon);
+
+ weather_data_path = std::string(getenv("HOME")) + "/.local/share/owf/data/data.json";
+
+ inotify_fd = inotify_init();
+ if (inotify_fd < 0)
+ {
+ std::cerr << "Error creating inotify instance" << std::endl;
+ notify_visibility(false);
+ return;
+ }
+
+ if (inotify_add_watch(inotify_fd, weather_data_path.c_str(), IN_CLOSE_WRITE) < 0)
+ {
+ std::cerr << "Error watching json file " << weather_data_path << std::endl;
+ notify_visibility(false);
+ return;
+ }
+
+ inotify_connection = Glib::signal_io().connect(
+ sigc::mem_fun(*this, &ShellWeather::handle_inotify_event),
+ inotify_fd, Glib::IOCondition::IO_IN | Glib::IOCondition::IO_HUP);
+
+ update_weather();
+}
+
+ShellWeather::~ShellWeather()
+{
+ inotify_connection.disconnect();
+ if (inotify_fd >= 0)
+ {
+ close(inotify_fd);
+ }
+}
+
+void ShellWeather::set_visibility_callback(std::function callback)
+{
+ visibility_callback = std::move(callback);
+}
+
+void ShellWeather::notify_visibility(bool visible)
+{
+ if (visibility_callback)
+ {
+ visibility_callback(visible);
+ }
+}
+
+bool ShellWeather::handle_inotify_event(Glib::IOCondition cond)
+{
+ if (cond == Glib::IOCondition::IO_HUP)
+ {
+ notify_visibility(false);
+ return false;
+ }
+
+ char buf[1024 * sizeof(inotify_event)];
+ read(inotify_fd, buf, sizeof(buf));
+
+ update_weather();
+ return true;
+}
+
+void ShellWeather::update_weather()
+{
+ std::ifstream input_file(weather_data_path);
+ if (!input_file)
+ {
+ std::cerr << "Error reading json file " << weather_data_path << std::endl;
+ notify_visibility(false);
+ return;
+ }
+
+ std::stringstream buffer;
+ buffer << input_file.rdbuf();
+
+ wf::json_t json_data;
+ auto err = wf::json_t::parse_string(buffer.str(), json_data);
+ if (err.has_value())
+ {
+ std::cerr << "Error parsing json data " << weather_data_path << ": " << *err << std::endl;
+ notify_visibility(false);
+ return;
+ }
+
+ if (!json_data.has_member("temp") || !json_data.has_member("icon"))
+ {
+ std::cerr << "Unexpected weather json data in " << weather_data_path << std::endl;
+ notify_visibility(false);
+ return;
+ }
+
+ label.set_text(json_data["temp"].as_string());
+ icon.set(json_data["icon"].as_string());
+ notify_visibility(true);
+}
diff --git a/src/util/widget-utils/weather.hpp b/src/util/widget-utils/weather.hpp
new file mode 100644
index 000000000..904e252af
--- /dev/null
+++ b/src/util/widget-utils/weather.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+class ShellWeather : public Gtk::Box
+{
+ public:
+ ShellWeather(const std::string& section);
+ ~ShellWeather() override;
+
+ void set_visibility_callback(std::function callback);
+
+ private:
+ Gtk::Label label;
+ Gtk::Image icon;
+
+ int inotify_fd = -1;
+ sigc::connection inotify_connection;
+ std::string weather_data_path;
+ std::function visibility_callback;
+
+ bool handle_inotify_event(Glib::IOCondition cond);
+ void update_weather();
+ void notify_visibility(bool visible);
+};