1 #include <glibmm/main.h>
2 #include <gtkmm/window.h>
3 #include <gtkmm/headerbar.h>
4 #include <gtkmm/hvbox.h>
5 #include <gtkmm/application.h>
6 #include <gdk/gdkwayland.h>
7 #include <gtk-layer-shell.h>
8 
9 #include <stdio.h>
10 #include <iostream>
11 #include <sstream>
12 
13 #include <map>
14 
15 #include "panel.hpp"
16 #include "../util/gtk-utils.hpp"
17 
18 #include "widgets/battery.hpp"
19 #include "widgets/menu.hpp"
20 #include "widgets/clock.hpp"
21 #include "widgets/launchers.hpp"
22 #include "widgets/network.hpp"
23 #include "widgets/spacing.hpp"
24 #ifdef HAVE_PULSE
25 #include "widgets/volume.hpp"
26 #endif
27 #include "widgets/window-list/window-list.hpp"
28 
29 #include "wf-autohide-window.hpp"
30 
31 struct WayfirePanelZwfOutputCallbacks
32 {
33     std::function<void()> enter_fullscreen;
34     std::function<void()> leave_fullscreen;
35 };
36 
handle_zwf_output_enter_fullscreen(void * dd,zwf_output_v2 * zwf_output_v2)37 static void handle_zwf_output_enter_fullscreen(void* dd,
38     zwf_output_v2 *zwf_output_v2)
39 {
40     auto data = (WayfirePanelZwfOutputCallbacks*)
41         zwf_output_v2_get_user_data(zwf_output_v2);
42 
43     if (data)
44         data->enter_fullscreen();
45 }
46 
handle_zwf_output_leave_fullscreen(void *,zwf_output_v2 * zwf_output_v2)47 static void handle_zwf_output_leave_fullscreen(void *,
48     zwf_output_v2 *zwf_output_v2)
49 {
50     auto data = (WayfirePanelZwfOutputCallbacks*)
51         zwf_output_v2_get_user_data(zwf_output_v2);
52 
53     if (data)
54         data->leave_fullscreen();
55 }
56 
57 static struct zwf_output_v2_listener output_impl = {
58     .enter_fullscreen = handle_zwf_output_enter_fullscreen,
59     .leave_fullscreen = handle_zwf_output_leave_fullscreen,
60 };
61 
62 class WayfirePanel::impl
63 {
64     std::unique_ptr<WayfireAutohidingWindow> window;
65 
66     Gtk::HBox content_box;
67     Gtk::HBox left_box, center_box, right_box;
68 
69     using Widget = std::unique_ptr<WayfireWidget>;
70     using WidgetContainer = std::vector<Widget>;
71     WidgetContainer left_widgets, center_widgets, right_widgets;
72 
73     WayfireOutput *output;
74 
75     int last_autohide_value = -1;
76     WfOption<bool> autohide_opt{"panel/autohide"};
77     std::function<void()> autohide_opt_updated = [=] ()
__anond7d839cc0102() 78     {
79         if (autohide_opt == last_autohide_value)
80             return;
81 
82         if (autohide_opt) {
83             this->window->increase_autohide();
84         } else if (last_autohide_value == 1) {
85             this->window->decrease_autohide();
86         }
87 
88         last_autohide_value = autohide_opt;
89         window->set_auto_exclusive_zone(!autohide_opt);
90     };
91 
92     WfOption<std::string> bg_color{"panel/background_color"};
93     std::function<void()> on_window_color_updated = [=] ()
__anond7d839cc0202() 94     {
95         if ((std::string)bg_color == "gtk_default")
96             return window->unset_background_color();
97 
98         Gdk::RGBA rgba;
99         if ((std::string)bg_color == "gtk_headerbar")
100         {
101             Gtk::HeaderBar headerbar;
102             rgba = headerbar.get_style_context()->get_background_color();
103         } else {
104             auto color = wf::option_type::from_string<wf::color_t> (
105                 ((wf::option_sptr_t<std::string>)bg_color)->get_value_str());
106             if (!color) {
107                 std::cerr << "Invalid panel background color in"
108                     " config file" << std::endl;
109                 return;
110             }
111 
112             rgba.set_red(color.value().r);
113             rgba.set_green(color.value().g);
114             rgba.set_blue(color.value().b);
115             rgba.set_alpha(color.value().a);
116         }
117 
118         window->override_background_color(rgba);
119     };
120 
121     WfOption<std::string> panel_layer{"panel/layer"};
122     std::function<void()> set_panel_layer = [=] ()
__anond7d839cc0302() 123     {
124         if ((std::string)panel_layer == "overlay")
125             gtk_layer_set_layer(window->gobj(), GTK_LAYER_SHELL_LAYER_OVERLAY);
126         if ((std::string)panel_layer == "top")
127             gtk_layer_set_layer(window->gobj(), GTK_LAYER_SHELL_LAYER_TOP);
128         if ((std::string)panel_layer == "bottom")
129             gtk_layer_set_layer(window->gobj(), GTK_LAYER_SHELL_LAYER_BOTTOM);
130         if ((std::string)panel_layer == "background")
131             gtk_layer_set_layer(window->gobj(), GTK_LAYER_SHELL_LAYER_BACKGROUND);
132     };
133 
134     WfOption<int> minimal_panel_height{"panel/minimal_height"};
135     WfOption<std::string> css_path{"panel/css_path"};
136 
create_window()137     void create_window()
138     {
139         window = std::make_unique<WayfireAutohidingWindow> (output, "panel");
140         window->set_size_request(1, minimal_panel_height);
141         panel_layer.set_callback(set_panel_layer);
142         set_panel_layer(); // initial setting
143 
144         gtk_layer_set_anchor(window->gobj(), GTK_LAYER_SHELL_EDGE_LEFT, true);
145         gtk_layer_set_anchor(window->gobj(), GTK_LAYER_SHELL_EDGE_RIGHT, true);
146 
147         bg_color.set_callback(on_window_color_updated);
148         on_window_color_updated(); // set initial color
149 
150         autohide_opt.set_callback(autohide_opt_updated);
151         autohide_opt_updated(); // set initial autohide status
152 
153         if ((std::string)css_path != "")
154         {
155             auto css = load_css_from_path(css_path);
156             if (css)
157             {
158                 auto screen = Gdk::Screen::get_default();
159                 auto style_context = Gtk::StyleContext::create();
160                 style_context->add_provider_for_screen(
161                     screen, css, GTK_STYLE_PROVIDER_PRIORITY_USER);
162             }
163         }
164 
165         window->show_all();
166         init_widgets();
167         init_layout();
168 
169         window->signal_delete_event().connect(
170             sigc::mem_fun(this, &WayfirePanel::impl::on_delete));
171     }
172 
on_delete(GdkEventAny * ev)173     bool on_delete(GdkEventAny *ev)
174     {
175         /* We ignore close events, because the panel's lifetime is bound to
176          * the lifetime of the output */
177         return true;
178     }
179 
init_layout()180     void init_layout()
181     {
182         content_box.pack_start(left_box, false, false);
183         std::vector<Gtk::Widget*> center_children = center_box.get_children();
184         if (center_children.size() > 0)
185             content_box.set_center_widget(center_box);
186         content_box.pack_end(right_box, false, false);
187         window->add(content_box);
188         window->show_all();
189     }
190 
widget_from_name(std::string name)191     Widget widget_from_name(std::string name)
192     {
193         if (name == "menu")
194             return Widget(new WayfireMenu());
195         if (name == "launchers")
196             return Widget(new WayfireLaunchers());
197         if (name == "clock")
198             return Widget(new WayfireClock());
199         if (name == "network")
200             return Widget(new WayfireNetworkInfo());
201         if (name == "battery")
202             return Widget(new WayfireBatteryInfo());
203         if (name == "volume") {
204 #ifdef HAVE_PULSE
205             return Widget(new WayfireVolume());
206 #else
207 #warning "Pulse not found, volume widget will not be available."
208         std::cerr << "Built without pulse support, volume widget "
209             " is not available." << std::endl;
210 #endif
211         }
212         if (name == "window-list")
213             return Widget(new WayfireWindowList(output));
214 
215         std::string spacing = "spacing";
216         if (name.find(spacing) == 0)
217         {
218             auto pixel_str = name.substr(spacing.size());
219             int pixel = std::atoi(pixel_str.c_str());
220 
221             if (pixel <= 0)
222             {
223                 std::cerr << "Invalid spacing, " << pixel << std::endl;
224                 return nullptr;
225             }
226 
227             return Widget(new WayfireSpacing(pixel));
228         }
229 
230         if (name != "none")
231             std::cerr << "Invalid widget: " << name << std::endl;
232         return nullptr;
233     }
234 
tokenize(std::string list)235     static std::vector<std::string> tokenize(std::string list)
236     {
237         std::string token;
238         std::istringstream stream(list);
239         std::vector<std::string> result;
240 
241         while(stream >> token)
242         {
243             if (token.size())
244                 result.push_back(token);
245         }
246 
247         return result;
248     }
249 
reload_widgets(std::string list,WidgetContainer & container,Gtk::HBox & box)250     void reload_widgets(std::string list, WidgetContainer& container,
251                         Gtk::HBox& box)
252     {
253         container.clear();
254         auto widgets = tokenize(list);
255         for (auto widget_name : widgets)
256         {
257             auto widget = widget_from_name(widget_name);
258             if (!widget)
259                 continue;
260 
261             widget->widget_name = widget_name;
262             widget->init(&box);
263             container.push_back(std::move(widget));
264         }
265     }
266 
267     WfOption<std::string> left_widgets_opt{"panel/widgets_left"};
268     WfOption<std::string> right_widgets_opt{"panel/widgets_right"};
269     WfOption<std::string> center_widgets_opt{"panel/widgets_center"};
init_widgets()270     void init_widgets()
271     {
272         left_widgets_opt.set_callback([=] () {
273             reload_widgets((std::string)left_widgets_opt, left_widgets, left_box);
274         });
275         right_widgets_opt.set_callback([=] () {
276             reload_widgets((std::string)right_widgets_opt, right_widgets, right_box);
277         });
278         center_widgets_opt.set_callback([=] () {
279             reload_widgets((std::string)center_widgets_opt, center_widgets, center_box);
280         });
281 
282         reload_widgets((std::string)left_widgets_opt, left_widgets, left_box);
283         reload_widgets((std::string)right_widgets_opt, right_widgets, right_box);
284         reload_widgets((std::string)center_widgets_opt, center_widgets, center_box);
285     }
286 
287     WayfirePanelZwfOutputCallbacks callbacks;
288 
289     public:
impl(WayfireOutput * output)290     impl(WayfireOutput *output)
291     {
292         this->output = output;
293         create_window();
294 
295         if (output->output)
296         {
297             callbacks.enter_fullscreen = [=]() { window->increase_autohide(); };
298             callbacks.leave_fullscreen = [=]() { window->decrease_autohide(); };
299             zwf_output_v2_add_listener(output->output, &output_impl, NULL);
300             zwf_output_v2_set_user_data(output->output, &callbacks);
301         }
302     }
303 
~impl()304     ~impl()
305     {
306         if (output->output)
307             zwf_output_v2_set_user_data(output->output, NULL);
308     }
309 
get_wl_surface()310     wl_surface *get_wl_surface()
311     {
312         return window->get_wl_surface();
313     }
314 
get_window()315     Gtk::Window& get_window()
316     {
317         return *window;
318     }
319 
handle_config_reload()320     void handle_config_reload()
321     {
322         for (auto& w : left_widgets)
323             w->handle_config_reload();
324         for (auto& w : right_widgets)
325             w->handle_config_reload();
326         for (auto& w : center_widgets)
327             w->handle_config_reload();
328     }
329 };
330 
WayfirePanel(WayfireOutput * output)331 WayfirePanel::WayfirePanel(WayfireOutput *output) : pimpl(new impl(output)) { }
get_wl_surface()332 wl_surface *WayfirePanel::get_wl_surface() { return pimpl->get_wl_surface(); }
get_window()333 Gtk::Window& WayfirePanel::get_window() { return pimpl->get_window(); }
handle_config_reload()334 void WayfirePanel::handle_config_reload() { return pimpl->handle_config_reload(); }
335 
336 class WayfirePanelApp::impl
337 {
338   public:
339     std::map<WayfireOutput*, std::unique_ptr<WayfirePanel> > panels;
340 };
341 
on_config_reload()342 void WayfirePanelApp::on_config_reload()
343 {
344     for (auto& p : priv->panels)
345         p.second->handle_config_reload();
346 }
347 
handle_new_output(WayfireOutput * output)348 void WayfirePanelApp::handle_new_output(WayfireOutput *output)
349 {
350     priv->panels[output] = std::unique_ptr<WayfirePanel> (
351         new WayfirePanel(output));
352 }
353 
panel_for_wl_output(wl_output * output)354 WayfirePanel* WayfirePanelApp::panel_for_wl_output(wl_output *output)
355 {
356     for (auto& p : priv->panels)
357     {
358         if (p.first->wo == output)
359             return p.second.get();
360     }
361 
362     return nullptr;
363 }
364 
handle_output_removed(WayfireOutput * output)365 void WayfirePanelApp::handle_output_removed(WayfireOutput *output)
366 {
367     priv->panels.erase(output);
368 }
369 
get()370 WayfirePanelApp& WayfirePanelApp::get()
371 {
372     if (!instance)
373         throw std::logic_error("Calling WayfirePanelApp::get() before starting app!");
374     return dynamic_cast<WayfirePanelApp&>(*instance.get());
375 }
376 
create(int argc,char ** argv)377 void WayfirePanelApp::create(int argc, char **argv)
378 {
379     if (instance)
380         throw std::logic_error("Running WayfirePanelApp twice!");
381 
382     instance = std::unique_ptr<WayfireShellApp>(new WayfirePanelApp{argc, argv});
383     instance->run();
384 }
385 
386 WayfirePanelApp::~WayfirePanelApp() = default;
WayfirePanelApp(int argc,char ** argv)387 WayfirePanelApp::WayfirePanelApp(int argc, char **argv)
388     : WayfireShellApp(argc, argv), priv(new impl()) { }
389 
main(int argc,char ** argv)390 int main(int argc, char **argv)
391 {
392     WayfirePanelApp::create(argc, argv);
393     return 0;
394 }
395