diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..8b032d56 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "subprojects/gtk-layer-shell"] + path = subprojects/gtk-layer-shell + url = https://github.com/wmww/gtk-layer-shell diff --git a/meson.build b/meson.build index d876858e..271a1f9c 100644 --- a/meson.build +++ b/meson.build @@ -6,7 +6,7 @@ project( license: 'MIT', meson_version: '>=0.43.0', default_options: [ - 'cpp_std=c++11', + 'cpp_std=c++14', 'c_std=c11', 'warning_level=2', 'werror=false', @@ -17,6 +17,7 @@ wayland_client = dependency('wayland-client') wayland_protos = dependency('wayland-protocols') gtkmm = dependency('gtkmm-3.0') wfconfig = dependency('wf-config') #TODO fallback submodule +gtklayershell = dependency('gtk-layer-shell-0', fallback: ['gtk-layer-shell', 'gtk_layer_shell_dep']) needs_libinotify = ['freebsd', 'dragonfly'].contains(host_machine.system()) libinotify = dependency('libinotify', required: needs_libinotify) @@ -24,7 +25,7 @@ libinotify = dependency('libinotify', required: needs_libinotify) add_project_arguments(['-Wno-pedantic', '-Wno-unused-parameter', '-Wno-parentheses', '-Wno-cast-function-type'], language: 'cpp') icon_dir = join_paths(get_option('prefix'), 'share', 'wayfire', 'icons') -add_global_arguments('-DICONDIR="' + icon_dir + '"', language : 'cpp') +add_project_arguments('-DICONDIR="' + icon_dir + '"', language : 'cpp') subdir('proto') subdir('data') diff --git a/proto/meson.build b/proto/meson.build index 03f9bd10..e357a0d9 100644 --- a/proto/meson.build +++ b/proto/meson.build @@ -15,10 +15,8 @@ wayland_scanner_client = generator( ) client_protocols = [ - [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], - [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], - 'wayfire-shell.xml', - 'wlr-foreign-toplevel-management-unstable-v1.xml' + 'wlr-foreign-toplevel-management-unstable-v1.xml', + 'wayfire-shell-unstable-v2.xml', ] wl_protos_src = [] diff --git a/proto/wayfire-shell-unstable-v2.xml b/proto/wayfire-shell-unstable-v2.xml new file mode 100644 index 00000000..ffdabd50 --- /dev/null +++ b/proto/wayfire-shell-unstable-v2.xml @@ -0,0 +1,110 @@ + + + + This protocol provides additional events and requests for special DE + clients like panels, docks, etc. + + It is meant as an addition for protocols like wlr-layer-shell. + + + + + + + + + + + + + + + + + + Represents a single output. + Each output is managed independently from the others. + + + + + Emitted when a window gets fullscreened on the given output. In this + mode, windows in the TOP layer are not visible. + + There will be no two consecutive enter_fullscreen calls, i.e. if + fullscreen mode is entered it will be exited before going into this mode + again. + + + + + + Emitted when the output is no longer in fullscreen mode. Each + leave_fullscreen has a corresponding enter_fullscreen before it. + + + + + + Request the compositor to not render the output, so the output usually + is cleared to black color. To enable output rendering again, call + inhibit_output_done. + + + + + + Stop inhibiting the output. This must be called as many times as + inhibit_output was called to actually uninhibit rendering. + + The inhibit/inhibit_done requests can be called multiple times, even + from different apps, so don't assume that a call to inhibit_done would + always mean actually starting the rendering process. + + + + + + + + + + + + + A hotspot on the output is an edge or a corner region of the + output where the mouse has been residing for a given amount of time. + + The hotspot can be used for example for autohiding panels, where the + panel is shown when the mouse hovers on the edge of the output for a + specific amount of time. + + + + + + + + + + + + + + + Means that the mouse was inside the indicated hotspot for the given + amount of time. + + Emitted at most once for each entry of the mouse inside the hotspot. + + + + + + + + + + + + diff --git a/proto/wayfire-shell.xml b/proto/wayfire-shell.xml deleted file mode 100644 index d1d4f82f..00000000 --- a/proto/wayfire-shell.xml +++ /dev/null @@ -1,168 +0,0 @@ - - - - The purpose of this protocol is to enable the creation of different - desktop-interface windows like panels, backgrounds, docks, - lockscreens, etc. It also aims to allow the creation of full-blown - DEs using Wayfire. - - Note that in contrast to some other efforts to create a similar - protocol, such as wlr-layer-shell, this isn't a new "shell" for - giving a role to wl_surfaces. This protocol can be used with any - type of toplevel surface (xdg_toplevel, xdg_toplevel_v6, etc.) - to give them to the corresponding WM role. - - - - - - - - - - - Assign the given role to the given surface and add it to the - given output. A client can specify a null output, in which case - the compositor will assign the surface to the focused output, - if any such output. - - The role cannot be changed later, and neither can the surface be - moved to a different output, except by the compositor. - - - - - - - - - - - - Represents a single output. - Each output is managed independently from the others. - - - - Panels are always rendered on top, even above fullscreen windows. - If autohide is 1, the event indicates that the panels should hide - itself, by for example unmapping or sliding outside of the output. - If autohide is 0, this means that the reason for the last request - with autohide == 1 is no longer valid, i.e the panels can show - themselves. - - The output_hide_panels can be called multiple times with - autohide = 1, and the panel should show itself only when - it has received a matching number of events with autohide = 0 - - - - - - - - Request the compositor to not render the output, so - the output usually is cleared to black color. - To enable output rendering again, call inhibit_output_done - - - - - - Stop inhibiting the output. This must be called as many times - as inhibit_output was called to actually uninhibit rendering. - - The inhibit/inhibit_done requests can be called multiple times, - even from different apps, so don't assume that a call to - inhibit_done would always mean actually starting the rendering process. - - - - - - - Represents a surface with a specific WM role. - It belongs to the output which it was created for. - - - - - - - - - - - - - - - - - - - - - - - - - - Sets the position on the screen where the compositor should - position the view. Can be reset by specifying anchor 0. If not - set, the compositor will assume manual positioning via the - configure request. - - If one anchor edge is provided, the wm surface is "stuck" to - that edge. - If two anchor edges are provided, the wm surface is considered - anchored to the corner of the screen between them. - - Any other anchor edge configuration is considered invalid. - - - - - - - Set the offset from the anchored edges to the wm surface. This - is an alternative to the configure request. Using both will - result in undefined results. - - Margin has effect only for edges the wm surface is anchored to. - - - - - - - - - - - - - - - - - Sets how the wm surface will interact with keyboard focus. - Setting no_focus means that the surface will never receive - keyboard focus, click_to_focus means normal focus semantics (i.e - what you expect from "normal" windows), and exclusive focus means - that no other window can get keyboard focus. - - - - - - - Request the compositor to reserve the given amount of pixels - for the wm surface(like STRUTS in X11). This has effect only - if the surface is anchored to a single edge. Margin doesn't - affect exclusive zone in any way. - - - - - diff --git a/src/meson.build b/src/meson.build index 483b9bce..6396e53f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,5 +1,5 @@ subdir('util') -subdir('panel') -subdir('background') -subdir('dock') +#subdir('panel') +#subdir('background') +#subdir('dock') diff --git a/src/util/display.cpp b/src/util/display.cpp deleted file mode 100644 index 09dc7863..00000000 --- a/src/util/display.cpp +++ /dev/null @@ -1,146 +0,0 @@ -#include "display.hpp" -#include -#include -#include -#include - -// listeners -static void registry_add_object(void *data, struct wl_registry *registry, uint32_t name, - const char *interface, uint32_t version) -{ - auto display = static_cast (data); - if (strcmp(interface, zwf_shell_manager_v1_interface.name) == 0) - { - display->zwf_shell_manager = - (zwf_shell_manager_v1*) wl_registry_bind(registry, name, - &zwf_shell_manager_v1_interface, - std::min(version, 1u)); - } - - if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) - { - display->zxdg_output_manager = (zxdg_output_manager_v1*) - wl_registry_bind(registry, name, &zxdg_output_manager_v1_interface, - std::min(version, 2u)); - } - - if (strcmp(interface, wl_output_interface.name) == 0) - { - auto output = (wl_output*) wl_registry_bind(registry, name, &wl_output_interface, - std::min(version, 1u)); - display->name_to_wayfire_output[name] = new WayfireOutput(display, output); - } - - if (strcmp(interface, wl_seat_interface.name) == 0 && !display->default_seat) - { - display->default_seat = (wl_seat*) wl_registry_bind(registry, name, - &wl_seat_interface, std::min(version, 1u)); - } -} - -static void registry_remove_object(void *data, struct wl_registry *registry, uint32_t name) -{ - auto display = (WayfireDisplay*) data; - if (display->name_to_wayfire_output.count(name)) - { - delete display->name_to_wayfire_output[name]; - display->name_to_wayfire_output.erase(name); - } -} - -static struct wl_registry_listener registry_listener = -{ - ®istry_add_object, - ®istry_remove_object -}; - -/* WayfireDisplay implementation */ -WayfireDisplay::WayfireDisplay(std::function new_output_cb) -{ - this->new_output_callback = new_output_cb; - - auto gdk_display = gdk_display_get_default(); - auto display = gdk_wayland_display_get_wl_display(gdk_display); - - if (!display) - { - std::cerr << "Failed to connect to wayland display!" - << " Are you sure you are running a wayland compositor?" << std::endl; - std::exit(-1); - } - - wl_registry *registry = wl_display_get_registry(display); - wl_registry_add_listener(registry, ®istry_listener, this); - wl_display_roundtrip(display); - - if (!this->zxdg_output_manager) - { - std::cerr << "xdg_output not available" << std::endl; - std::exit(-1); - } - - if (!this->zwf_shell_manager) - { - std::cerr << "wayfire-shell not available" << std::endl; - std::exit(-1); - } -} - -WayfireDisplay::~WayfireDisplay() -{ - zxdg_output_manager_v1_destroy(zxdg_output_manager); - - // TODO: we should also destroy all kinds of shells, - // registry, etc. here - wl_display_disconnect(display); -} - -/* zxdg_output impl */ -static void zxdg_output_logical_position(void *data, struct zxdg_output_v1 *zxdg_output_v1, - int32_t x, int32_t y) -{ } - -static void zxdg_output_logical_size(void *data, struct zxdg_output_v1 *zxdg_output_v1, - int32_t width, int32_t height) -{ - auto wo = (WayfireOutput*) data; - if (width > 0 && height > 0 && wo->resized_callback) - wo->resized_callback(wo, width, height); -} - -static void zxdg_output_done(void *data, struct zxdg_output_v1 *zxdg_output_v1) { } -static void zxdg_output_name(void *data, struct zxdg_output_v1 *zxdg_output_v1, const char *name) {} -static void zxdg_output_description(void *data, struct zxdg_output_v1 *zxdg_output_v1, const char *description) {} - -const struct zxdg_output_v1_listener zxdg_output_v1_impl = -{ - zxdg_output_logical_position, - zxdg_output_logical_size, - zxdg_output_done, - zxdg_output_name, - zxdg_output_description -}; - -/* WayfireOutput implementation */ -WayfireOutput::WayfireOutput(WayfireDisplay *display, wl_output *output) -{ - this->display = display; - this->handle = output; - - zxdg_output = zxdg_output_manager_v1_get_xdg_output(display->zxdg_output_manager, handle); - zxdg_output_v1_add_listener(zxdg_output, &zxdg_output_v1_impl, this); - - if (display->zwf_shell_manager) - zwf = zwf_shell_manager_v1_get_wf_output(display->zwf_shell_manager, output); - - if (display->new_output_callback) - display->new_output_callback(this); -} - -WayfireOutput::~WayfireOutput() -{ - if (destroyed_callback) - destroyed_callback(this); - - zxdg_output_v1_destroy(zxdg_output); -} diff --git a/src/util/display.hpp b/src/util/display.hpp deleted file mode 100644 index 528e75c4..00000000 --- a/src/util/display.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef DISPLAY_HPP -#define DISPLAY_HPP - -#include -#include -#include - -#include "xdg-output-unstable-v1-client-protocol.h" -#include "wayfire-shell-client-protocol.h" - -struct WayfireOutput; -using output_callback = std::function; -struct WayfireDisplay -{ - wl_display *display = nullptr; - wl_seat *default_seat = nullptr; - - zwf_shell_manager_v1 *zwf_shell_manager = nullptr; - zxdg_output_manager_v1 *zxdg_output_manager = nullptr; - - WayfireDisplay(output_callback new_output_cb); - ~WayfireDisplay(); - - std::map name_to_wayfire_output; - output_callback new_output_callback; -}; - -struct WayfireOutput -{ - WayfireDisplay *display; - - wl_output *handle = nullptr; - zxdg_output_v1 *zxdg_output = nullptr; - zwf_output_v1 *zwf = nullptr; - - std::function destroyed_callback; - std::function resized_callback; - - WayfireOutput(WayfireDisplay *display, wl_output *); - ~WayfireOutput(); -}; - -#endif /* end of include guard: DISPLAY_HPP */ diff --git a/src/util/meson.build b/src/util/meson.build index 2aa370ec..9962bd9b 100644 --- a/src/util/meson.build +++ b/src/util/meson.build @@ -1,5 +1,5 @@ -util = static_library('util', ['display.cpp', 'gtk-utils.cpp', 'wf-shell-app.cpp', 'wf-autohide-window.cpp'], - dependencies: [wf_protos, wayland_client, gtkmm, wfconfig, libinotify]) +util = static_library('util', ['gtk-utils.cpp', 'wf-shell-app.cpp', 'wf-autohide-window.cpp'], + dependencies: [wf_protos, wayland_client, gtkmm, wfconfig, libinotify, gtklayershell]) util_includes = include_directories('.') libutil = declare_dependency( diff --git a/src/util/wf-autohide-window.cpp b/src/util/wf-autohide-window.cpp index 5f8532e2..a522c3b3 100644 --- a/src/util/wf-autohide-window.cpp +++ b/src/util/wf-autohide-window.cpp @@ -1,33 +1,28 @@ #include "wf-autohide-window.hpp" -#include "display.hpp" + +#include +#include + #include #include #include -WayfireAutohidingWindow::WayfireAutohidingWindow(int width, int height, - WayfireOutput *output, zwf_wm_surface_v1_role role) +WayfireAutohidingWindow::WayfireAutohidingWindow(WayfireOutput *output) { - this->set_size_request(width, height); this->set_decorated(false); this->set_resizable(false); + this->realize(); - this->show_all(); - - auto surface = this->get_wl_surface(); - if (!surface) - { - std::cerr << "Error: created window was not a wayland surface" << std::endl; - std::exit(-1); - } + gtk_layer_init_for_window(this->gobj()); - wm_surface = zwf_shell_manager_v1_get_wm_surface( - output->display->zwf_shell_manager, surface, role, output->handle); this->m_position_changed = [=] () {this->update_position();}; - this->signal_draw().connect_notify( [=] (const Cairo::RefPtr&) { update_margin(); }); + this->signal_size_allocate().connect_notify( - [=] (Gtk::Allocation&) {this->update_exclusive_zone();}); + [=] (Gtk::Allocation&) { + this->set_auto_exclusive_zone(this->has_auto_exclusive_zone); + }); this->signal_enter_notify_event().connect_notify( sigc::mem_fun(this, &WayfireAutohidingWindow::on_enter)); @@ -43,27 +38,30 @@ wl_surface* WayfireAutohidingWindow::get_wl_surface() const return gdk_wayland_window_get_wl_surface(gdk_window); } -zwf_wm_surface_v1* WayfireAutohidingWindow::get_wm_surface() const +static GtkLayerShellEdge get_anchor_edge(std::string position) { - return this->wm_surface; + if (position == WF_WINDOW_POSITION_TOP) { + return GTK_LAYER_SHELL_EDGE_TOP; + } else if (position == WF_WINDOW_POSITION_BOTTOM) { + return GTK_LAYER_SHELL_EDGE_BOTTOM; + } + + std::cerr << "Bad position in config file, defaulting to top" << std::endl; + return GTK_LAYER_SHELL_EDGE_TOP; } void WayfireAutohidingWindow::update_position() { - uint32_t anchor = 0; - if (this->m_position->as_string() == WF_WINDOW_POSITION_TOP) { - anchor = ZWF_WM_SURFACE_V1_ANCHOR_EDGE_TOP; - } else if (this->m_position->as_string() == WF_WINDOW_POSITION_BOTTOM) { - anchor = ZWF_WM_SURFACE_V1_ANCHOR_EDGE_BOTTOM; - } else { - std::cerr << "Bad position in config file, defaulting to top" << std::endl; - anchor = ZWF_WM_SURFACE_V1_ANCHOR_EDGE_TOP; - } + /* Reset old anchors */ + gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_TOP, false); + gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_BOTTOM, false); - zwf_wm_surface_v1_set_anchor(wm_surface, anchor); + /* Set new anchor */ + GtkLayerShellEdge anchor = get_anchor_edge(m_position->as_string()); + gtk_layer_set_anchor(this->gobj(), anchor, true); /* When the position changes, show an animation from the new edge. */ - transition.start_value = transition.end_value = get_hidden_y(); + transition.start_value = transition.end_value = -this->get_allocated_height(); schedule_show(0); /* And don't forget to hide the window afterwards, if autohide is enabled */ if (is_autohide()) @@ -82,16 +80,6 @@ void WayfireAutohidingWindow::set_position(wf_option position) update_position(); } -void WayfireAutohidingWindow::set_hidden_height(int hidden_height) -{ - this->m_hidden_height = hidden_height; -} - -int WayfireAutohidingWindow::get_hidden_y() const -{ - return m_hidden_height - get_allocated_height(); -} - void WayfireAutohidingWindow::set_animation_duration(wf_option duration) { /* Make sure we do not lose progress */ @@ -102,25 +90,18 @@ void WayfireAutohidingWindow::set_animation_duration(wf_option duration) this->transition.start(current, end); } -void WayfireAutohidingWindow::update_exclusive_zone() +void WayfireAutohidingWindow::set_auto_exclusive_zone(bool has_zone) { - int height = this->get_allocated_height(); - if (!this->has_exclusive_zone) - height = 0; + this->has_auto_exclusive_zone = has_zone; + int target_zone = has_zone ? get_allocated_height() : 0; - if (height != exclusive_zone) + if (this->last_zone != target_zone) { - exclusive_zone = height; - zwf_wm_surface_v1_set_exclusive_zone(wm_surface, exclusive_zone); + gtk_layer_set_exclusive_zone(this->gobj(), target_zone); + last_zone = target_zone; } } -void WayfireAutohidingWindow::set_exclusive_zone(bool exclusive) -{ - this->has_exclusive_zone = exclusive;; - update_exclusive_zone(); -} - void WayfireAutohidingWindow::increase_autohide() { ++autohide_counter; @@ -143,7 +124,7 @@ bool WayfireAutohidingWindow::is_autohide() const bool WayfireAutohidingWindow::m_do_hide() { int start = transition.progress(); - transition.start(start, get_hidden_y()); + transition.start(start, -get_allocated_height()); update_margin(); return false; // disconnect } @@ -190,16 +171,14 @@ void WayfireAutohidingWindow::schedule_show(int delay) bool WayfireAutohidingWindow::update_margin() { - if (animation_running || transition.running()) + if (transition.running()) { int target_y = std::round(transition.progress()); + gtk_layer_set_margin(this->gobj(), + get_anchor_edge(m_position->as_string()), target_y); - // takes effect only for anchored edges - zwf_wm_surface_v1_set_margin(wm_surface, - target_y, target_y, target_y, target_y); - - this->queue_draw(); - return (animation_running = transition.running()); + this->queue_draw(); // XXX: is this necessary? + return true; } return false; @@ -225,9 +204,3 @@ void WayfireAutohidingWindow::on_leave(GdkEventCrossing *cross) if (autohide_counter) schedule_hide(500); } - -void WayfireAutohidingWindow::set_keyboard_mode( - zwf_wm_surface_v1_keyboard_focus_mode mode) -{ - zwf_wm_surface_v1_set_keyboard_mode(wm_surface, mode); -} diff --git a/src/util/wf-autohide-window.hpp b/src/util/wf-autohide-window.hpp index 9f37730b..98b02b1d 100644 --- a/src/util/wf-autohide-window.hpp +++ b/src/util/wf-autohide-window.hpp @@ -6,30 +6,56 @@ #include #include -#include "wayfire-shell-client-protocol.h" struct WayfireOutput; #define WF_WINDOW_POSITION_TOP "top" #define WF_WINDOW_POSITION_BOTTOM "bottom" -/* A window which is anchored to an edge of the screen, and can autohide. */ +/** + * A window which is anchored to an edge of the screen, and can autohide. + * + * Autohide mode requires that the compositor supports wayfire-shell. + */ class WayfireAutohidingWindow : public Gtk::Window { - zwf_wm_surface_v1 *wm_surface = nullptr; - + public: + WayfireAutohidingWindow(WayfireOutput *output); + wl_surface* get_wl_surface() const; + + /* Sets the edge of the screen where the window is */ + void set_position(wf_option position); + + /* Sets the time for hiding/showing animation */ + void set_animation_duration(wf_option duration); + + /* Add one more autohide request */ + void increase_autohide(); + /* Remove one autohide request */ + void decrease_autohide(); + /* Returns true if the window is in autohide mode */ + bool is_autohide() const; + + /* Hide or show the panel after delay milliseconds, if nothing happens + * in the meantime */ + void schedule_hide(int delay); + void schedule_show(int delay); + + /** When auto exclusive zone is set, the window will adjust its exclusive + * zone based on the window size. + * + * Note that autohide margin isn't taken into account. */ + void set_auto_exclusive_zone(bool has_zone = false); + + private: wf_option_callback m_position_changed; wf_option m_position; void update_position(); - bool animation_running = false; - int m_hidden_height = 1; wf_duration transition; - int get_hidden_y() const; bool update_margin(); - bool has_exclusive_zone = false; - int exclusive_zone = 0; - void update_exclusive_zone(); + bool has_auto_exclusive_zone = false; + int last_zone = 0; sigc::connection pending_show, pending_hide; bool m_do_show(); @@ -38,38 +64,6 @@ class WayfireAutohidingWindow : public Gtk::Window void on_enter(GdkEventCrossing *cross); void on_leave(GdkEventCrossing *cross); - - public: - WayfireAutohidingWindow(int width, int height, - WayfireOutput *output, zwf_wm_surface_v1_role role); - wl_surface* get_wl_surface() const; - zwf_wm_surface_v1* get_wm_surface() const; - - /* Sets the edge of the screen where the window is */ - void set_position(wf_option position); - - /* Note: the functions below have effect only if the position is set */ - /* Sets the amount of pixels visible when the window is hidden */ - void set_hidden_height(int hidden_height = 1); - /* Sets the time for hiding/showing animation */ - void set_animation_duration(wf_option duration); - /* Sets that the window should have exclusive zone */ - void set_exclusive_zone(bool exclusive = true); - - /* Add one more autohide request */ - void increase_autohide(); - /* Remove one autohide request */ - void decrease_autohide(); - /* Returns true if the window is in autohide mode */ - bool is_autohide() const; - - /* Hide or show the panel after delay milliseconds, if nothing happens - * in the meantime */ - void schedule_hide(int delay); - void schedule_show(int delay); - - /* Set keyboard mode */ - void set_keyboard_mode(zwf_wm_surface_v1_keyboard_focus_mode mode); }; diff --git a/src/util/wf-shell-app.cpp b/src/util/wf-shell-app.cpp index 948cc238..784ca733 100644 --- a/src/util/wf-shell-app.cpp +++ b/src/util/wf-shell-app.cpp @@ -1,6 +1,9 @@ #include "wf-shell-app.hpp" #include #include +#include +#include +#include #include @@ -14,6 +17,7 @@ std::string WayfireShellApp::get_config_file() #define INOT_BUF_SIZE (1024 * sizeof(inotify_event)) char buf[INOT_BUF_SIZE]; +/* Reload file and add next inotify watch */ static void do_reload_config(WayfireShellApp *app) { app->config->reload_config(); @@ -21,6 +25,7 @@ static void do_reload_config(WayfireShellApp *app) inotify_add_watch(app->inotify_fd, app->get_config_file().c_str(), IN_MODIFY); } +/* Handle inotify event */ static bool handle_inotify_event(WayfireShellApp *app, Glib::IOCondition cond) { /* read, but don't use */ @@ -30,10 +35,43 @@ static bool handle_inotify_event(WayfireShellApp *app, Glib::IOCondition cond) return true; } +static void registry_add_object(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) +{ + auto app = static_cast (data); + if (strcmp(interface, zwf_shell_manager_v2_interface.name) == 0) + { + app->manager = (zwf_shell_manager_v2*) wl_registry_bind(registry, name, + &zwf_shell_manager_v2_interface, std::min(version, 1u)); + } +} +static void registry_remove_object(void *data, struct wl_registry *registry, + uint32_t name) {} + +static struct wl_registry_listener registry_listener = +{ + ®istry_add_object, + ®istry_remove_object +}; + void WayfireShellApp::on_activate() { app->hold(); + // load wf-shell if available + auto gdk_display = gdk_display_get_default(); + auto wl_display = gdk_wayland_display_get_wl_display(gdk_display); + if (!wl_display) + { + std::cerr << "Failed to connect to wayland display!" + << " Are you sure you are running a wayland compositor?" << std::endl; + std::exit(-1); + } + + wl_registry *registry = wl_display_get_registry(wl_display); + wl_registry_add_listener(registry, ®istry_listener, this); + wl_display_roundtrip(wl_display); + // setup config config = std::unique_ptr ( new wayfire_config(get_config_file())); @@ -45,16 +83,36 @@ void WayfireShellApp::on_activate() sigc::bind<0>(&handle_inotify_event, this), inotify_fd, Glib::IO_IN | Glib::IO_HUP); - // setup display - auto handle_new_output = [=] (WayfireOutput *output) - { - output->destroyed_callback = [=] (WayfireOutput *output) { - on_output_removed(output); - }; + // Hook up monitor tracking + auto display = Gdk::Display::get_default(); + display->signal_monitor_added().connect_notify( + [=] (const GMonitor& monitor) { this->add_output(monitor); }); + display->signal_monitor_removed().connect_notify( + [=] (const GMonitor& monitor) { this->rem_output(monitor); }); - on_new_output(output); - }; - display = std::unique_ptr (new WayfireDisplay(handle_new_output)); + // initial monitors + int num_monitors = display->get_n_monitors(); + for (int i = 0; i < num_monitors; i++) + add_output(display->get_monitor(i)); +} + +void WayfireShellApp::add_output(GMonitor monitor) +{ + monitors.push_back( + std::make_unique (monitor, this->manager)); + handle_new_output(monitors.back().get()); +} + +void WayfireShellApp::rem_output(GMonitor monitor) +{ + auto it = std::remove_if(monitors.begin(), monitors.end(), + [monitor] (auto& output) { return output->monitor == monitor; }); + + if (it != monitors.end()) + { + handle_output_removed(it->get()); + monitors.erase(it, monitors.end()); + } } WayfireShellApp::WayfireShellApp(int argc, char **argv) @@ -68,3 +126,26 @@ void WayfireShellApp::run() { app->run(); } + +/* -------------------------- WayfireOutput --------------------------------- */ +WayfireOutput::WayfireOutput(const GMonitor& monitor, + zwf_shell_manager_v2 *zwf_manager) +{ + this->monitor = monitor; + this->wo = gdk_wayland_monitor_get_wl_output(monitor->gobj()); + + if (zwf_manager) + { + this->output = + zwf_shell_manager_v2_get_wf_output(zwf_manager, this->wo); + } else + { + this->output = nullptr; + } +} + +WayfireOutput::~WayfireOutput() +{ + if (this->output) + zwf_output_v2_destroy(this->output); +} diff --git a/src/util/wf-shell-app.hpp b/src/util/wf-shell-app.hpp index 80a9a96d..ffe7bf5a 100644 --- a/src/util/wf-shell-app.hpp +++ b/src/util/wf-shell-app.hpp @@ -1,37 +1,64 @@ #ifndef WF_SHELL_APP_HPP #define WF_SHELL_APP_HPP +#include #include #include #include +#include -#include "display.hpp" +#include "wayfire-shell-unstable-v2-client-protocol.h" -/* A base class for applications like backgrounds, panels, docks, etc */ +using GMonitor = Glib::RefPtr; +/** + * Represents a single output + */ +struct WayfireOutput +{ + GMonitor monitor; + wl_output *wo; + zwf_output_v2 *output; + + WayfireOutput(const GMonitor& monitor, zwf_shell_manager_v2 *zwf_manager); + ~WayfireOutput(); +}; + +/** + * A basic shell application. + * + * It is suitable for applications that need to show one or more windows + * per monitor. + */ class WayfireShellApp { - protected: - Glib::RefPtr app; - virtual ~WayfireShellApp() {}; + private: + std::vector> monitors; + + protected: + Glib::RefPtr app; + virtual ~WayfireShellApp() {}; + + virtual void on_activate(); - virtual void on_activate(); + virtual void add_output(GMonitor monitor); + virtual void rem_output(GMonitor monitor); - public: - int inotify_fd; + public: - std::unique_ptr config; - std::unique_ptr display; + int inotify_fd; + std::unique_ptr config; + zwf_shell_manager_v2 *manager = nullptr; - WayfireShellApp(int argc, char **argv); + WayfireShellApp(int argc, char **argv); - virtual std::string get_config_file(); - virtual void on_config_reload() {} + virtual std::string get_config_file(); + virtual void on_config_reload() {} - virtual void on_new_output(WayfireOutput *output) {} - virtual void on_output_removed(WayfireOutput *output) {} + virtual void handle_new_output(WayfireOutput *output) {} + virtual void handle_output_removed(WayfireOutput *output) {} - virtual void run(); + virtual void run(); }; #endif /* end of include guard: WF_SHELL_APP_HPP */ diff --git a/subprojects/gtk-layer-shell b/subprojects/gtk-layer-shell new file mode 160000 index 00000000..da4a346f --- /dev/null +++ b/subprojects/gtk-layer-shell @@ -0,0 +1 @@ +Subproject commit da4a346f5700a79e22a3c3312a8947a26745511e