diff --git a/metadata/background-view.xml b/metadata/background-view.xml deleted file mode 100644 index 1bd4a65..0000000 --- a/metadata/background-view.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - <_short>Background View - Desktop - - - - - - diff --git a/metadata/meson.build b/metadata/meson.build index e2c7e9a..db9ce8f 100644 --- a/metadata/meson.build +++ b/metadata/meson.build @@ -1,6 +1,5 @@ install_data('annotate.xml', install_dir: wayfire.get_variable(pkgconfig: 'metadatadir')) install_data('autorotate-iio.xml', install_dir: wayfire.get_variable(pkgconfig: 'metadatadir')) -install_data('background-view.xml', install_dir: wayfire.get_variable(pkgconfig: 'metadatadir')) install_data('bench.xml', install_dir: wayfire.get_variable(pkgconfig: 'metadatadir')) install_data('crosshair.xml', install_dir: wayfire.get_variable(pkgconfig: 'metadatadir')) install_data('focus-change.xml', install_dir: wayfire.get_variable(pkgconfig: 'metadatadir')) diff --git a/src/background-view.cpp b/src/background-view.cpp deleted file mode 100644 index 3de74dd..0000000 --- a/src/background-view.cpp +++ /dev/null @@ -1,496 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2023 Scott Moreau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -class wayfire_bgview_set_pointer_interaction : public wf::pointer_interaction_t -{ - public: - void handle_pointer_enter(wf::pointf_t position) override - { - wf::get_core().set_cursor("default"); - } -}; - -// A main node for the view. It allows or denies keyboard and pointer focus according to inhibit_output. -class wayfire_background_view_root_node : public wf::scene::translation_node_t -{ - std::weak_ptr _view; - wf::option_wrapper_t inhibit_input{"background-view/inhibit_input"}; - std::unique_ptr wlr_kb_interaction; - - public: - wayfire_background_view_root_node(wf::view_interface_t *_view) : translation_node_t(false) - { - this->_view = _view->weak_from_this(); - this->wlr_kb_interaction = std::make_unique(_view); - } - - std::optional find_node_at(const wf::pointf_t& point) override - { - if (inhibit_input) - { - // Prohibit pointer/touch input, but need to set the pointer on enter => we grab the input to this - // node in those cases, and the pointer_interaction.enter() sets the cursor to default - return wf::scene::input_node_t{ - .node = {this}, - .local_coords = point, - }; - } - - // Propagate event to the actual view node - return floating_inner_node_t::find_node_at(point); - } - - wf::keyboard_focus_node_t keyboard_refocus(wf::output_t *output) override - { - auto view = _view.lock(); - if (inhibit_input || !view) - { - // Prohibit keyboard input - return wf::keyboard_focus_node_t{}; - } - - // Take input only if the user explicitly clicks on the view => in this case, the last focus timestamp - // on the seat will be equal to our focus timestamp. - if (output != view->get_output()) - { - return wf::keyboard_focus_node_t{}; - } - - const uint64_t last_ts = wf::get_core().seat->get_last_focus_timestamp(); - const uint64_t our_ts = keyboard_interaction().last_focus_timestamp; - if (our_ts == last_ts) - { - return wf::keyboard_focus_node_t{this, wf::focus_importance::REGULAR}; - } - - return wf::keyboard_focus_node_t{}; - } - - wf::pointer_interaction_t& pointer_interaction() override - { - static wayfire_bgview_set_pointer_interaction itr; - return itr; - } - - wf::keyboard_interaction_t& keyboard_interaction() override - { - return *wlr_kb_interaction; - } - - std::string stringify() const override - { - return "background-view node " + stringify_flags(); - } -}; - -// The view type which background-views use. -// It derives from wf::view_interface_t directly instead of deriving from wf::toplevel_view_interface_t. -// This makes it so that the view appears similarly to a layer-shell view in the eyes of other plugins. -class unmappable_view_t : public virtual wf::view_interface_t -{ - public: - virtual void bg_view_unmap() - {} - wlr_surface *get_keyboard_focus_surface() - { - return nullptr; - } - - wf::wl_listener_wrapper on_unmap; - std::shared_ptr root_node; - - template - static std::shared_ptr create( - WlrObject *toplevel, wf::output_t *output) - { - auto new_view = wf::view_interface_t::create(toplevel); - - new_view->role = wf::VIEW_ROLE_DESKTOP_ENVIRONMENT; - new_view->root_node = std::make_shared(new_view.get()); - - // Pin the view to (0, 0) on its output: backgrounds always span the whole output - new_view->root_node->set_offset({0, 0}); - new_view->set_surface_root_node(new_view->root_node); - - new_view->set_output(output); - wf::scene::add_front(output->node_for_layer(wf::scene::layer::BACKGROUND), new_view->get_root_node()); - - // Directly map the view, we know that the old_view comes from a pre_map signal, so it is already - // mapped on the wlroots side - new_view->map(); - wf::view_implementation::emit_view_map_signal({new_view.get()}, true); - return new_view; - } -}; - -// An adapter so that xdg-shell views can be used as background views -class wayfire_background_view_xdg : public wf::xdg_toplevel_view_base_t, public unmappable_view_t -{ - public: - wayfire_background_view_xdg(wlr_xdg_toplevel *toplevel) : xdg_toplevel_view_base_t(toplevel, true) - {} - wlr_surface *get_keyboard_focus_surface() - { - return nullptr; - } - - void bg_view_unmap() override - { - wf::xdg_toplevel_view_base_t::unmap(); - } -}; - -#if WF_HAS_XWAYLAND -// An adapter so that xwayland views can be used as background views -class wayfire_background_view_xwl : public wf::xwayland_view_base_t, public unmappable_view_t -{ - wf::option_wrapper_t inhibit_input{"background-view/inhibit_input"}; - - public: - wayfire_background_view_xwl(wlr_xwayland_surface *xw) : xwayland_view_base_t(xw) - { - this->kb_focus_enabled = !inhibit_input; - } - - wlr_surface *get_keyboard_focus_surface() - { - return nullptr; - } - - void map() - { - do_map(xw->surface, true); - } - - void bg_view_unmap() override - { - do_unmap(); - } -}; -#endif - -struct background_view -{ - std::shared_ptr view; - pid_t pid; -}; - -class wayfire_background_view : public wf::plugin_interface_t -{ - /* The command option should be set to a client like mpv, projectM or - * a single xscreensaver. - */ - wf::option_wrapper_t command{"background-view/command"}; - - /* The file option is for convenience when using wcm. If file is set, - * it will be appended to the command, wrapped in double quotes. - */ - wf::option_wrapper_t file{"background-view/file"}; - - /* The app-id option is used to identify the application. If app-id - * is not set or does not match the launched app-id, the plugin will - * not be able to set the client surface as the background. - */ - wf::option_wrapper_t app_id{"background-view/app_id"}; - - // List of all background views assigned to an output - std::map views; - - wf::wl_listener_wrapper on_new_inhibitor; - wf::wl_idle_call idle_cleanup_inhibitors; - size_t output_count; - - public: - void init() override - { - command.set_callback(option_changed); - file.set_callback(option_changed); - option_changed(); - - on_new_inhibitor.set_callback([=] (auto) { remove_idle_inhibitors(); }); - on_new_inhibitor.connect(&wf::get_core().protocols.idle_inhibit->events.new_inhibitor); - } - - /* Borrowed from sway */ - pid_t get_parent_pid(pid_t child) - { - pid_t parent = -1; - char file_name[100]; - char *buffer = NULL; - const char *sep = " "; - FILE *stat = NULL; - size_t buf_size = 0; - - snprintf(file_name, sizeof(file_name), "/proc/%d/stat", child); - - if ((stat = fopen(file_name, "r"))) - { - if (getline(&buffer, &buf_size, stat) != -1) - { - strtok(buffer, sep); // pid - strtok(NULL, sep); // executable name - strtok(NULL, sep); // state - char *token = strtok(NULL, sep); // parent pid - parent = strtol(token, NULL, 10); - } - - free(buffer); - fclose(stat); - } - - if (parent) - { - return (parent == child) ? -1 : parent; - } - - return -1; - } - - void close_all_views() - { - for (auto& [output, bg_view] : views) - { - if (!bg_view.view) - { - continue; - } - - bg_view.view->close(); - bg_view.view->on_unmap.disconnect(); - bg_view.view->bg_view_unmap(); - } - - views.clear(); - } - - wf::config::option_base_t::updated_callback_t option_changed = [=] () - { - close_all_views(); - if (std::string(command).empty()) - { - return; - } - - if (!on_view_pre_map.is_connected()) - { - wf::get_core().connect(&on_view_pre_map); - } - - output_count = 0; - for (auto & o : wf::get_core().output_layout->get_outputs()) - { - views[o].pid = wf::get_core().run(std::string(command) + add_arg_if_not_empty(file)); - } - }; - - void set_view_for_output(wayfire_toplevel_view view, wlr_surface *surface, wf::output_t *o) - { - std::shared_ptr new_view; - if (wlr_xdg_surface *surf = wlr_xdg_surface_try_from_wlr_surface(surface)) - { - auto toplevel = surf->toplevel; - wlr_xdg_toplevel_set_size(toplevel, o->get_screen_size().width, o->get_screen_size().height); - new_view = unmappable_view_t::create(toplevel, o); - new_view->on_unmap.connect(&toplevel->base->surface->events.unmap); - } - -#if WF_HAS_XWAYLAND - else if (wlr_xwayland_surface *surf = wlr_xwayland_surface_try_from_wlr_surface(surface)) - { - wlr_xwayland_surface_configure(surf, o->get_layout_geometry().x, o->get_layout_geometry().y, - o->get_layout_geometry().width, o->get_layout_geometry().height); - new_view = unmappable_view_t::create(surf, o); - new_view->on_unmap.connect(&surf->surface->events.unmap); - } -#endif - else - { - LOGE("Failed to set background view: the view is neither xdg-toplevel nor a xwayland surface!"); - return; - } - - new_view->on_unmap.set_callback([this, o] (auto) - { - views[o].view->bg_view_unmap(); - views.erase(o); - }); - views[o].view = new_view; - - /* Remove any idle inhibitors which were already set */ - remove_idle_inhibitors(); - /* Disconnect the pre_map handler in case the view goes away, - * we don't want to background-view the wrong view. */ - if (++output_count == wf::get_core().output_layout->get_outputs().size()) - { - on_view_pre_map.disconnect(); - } - } - - wf::signal::connection_t on_view_pre_map = [=] (wf::view_pre_map_signal *ev) - { - pid_t pid = 0; -#if WF_HAS_XWAYLAND - pid_t x_pid = 0; -#endif - pid_t wl_pid = 0; - auto view = ev->view; - if (!view) - { - return; - } - -#if WF_HAS_XWAYLAND - wlr_surface *wlr_surface = ev->surface; - wlr_xwayland_surface *xwayland_surface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface); - - if (xwayland_surface) - { - /* Get pid for xwayland view */ - x_pid = xwayland_surface->pid; - } else -#endif - if (ev->surface) - { - /* Get pid for native view */ - wl_client_get_credentials(wl_resource_get_client(ev->surface->resource), &wl_pid, 0, 0); - } - - for (auto& o : wf::get_core().output_layout->get_outputs()) - { - if (views[o].view) - { - continue; - } - - /* First try to match pid */ - if ((views[o].pid == wl_pid) -#if WF_HAS_XWAYLAND - || (views[o].pid == x_pid) -#endif - ) - { - set_view_for_output(wf::toplevel_cast(view), ev->surface, o); - ev->override_implementation = true; - return; - } - -#if WF_HAS_XWAYLAND - if (xwayland_surface) - { - pid = get_parent_pid(x_pid); - } else -#endif - { - pid = get_parent_pid(wl_pid); - } - - do { - if (views[o].pid == pid) - { - set_view_for_output(wf::toplevel_cast(view), ev->surface, o); - ev->override_implementation = true; - return; - } - } while ((pid = get_parent_pid(pid)) != -1); - - /* Next, try to match application identifier */ - if (std::string(app_id) == view->get_app_id()) - { - set_view_for_output(wf::toplevel_cast(view), ev->surface, o); - ev->override_implementation = true; - return; - } - } - }; - - std::string add_arg_if_not_empty(std::string in) - { - if (in.empty()) - { - return in; - } else - { - return " \"" + in + "\""; - } - } - - void remove_idle_inhibitors() - { - idle_cleanup_inhibitors.run_once([=] () - { - auto& mgr = wf::get_core().protocols.idle_inhibit; - wlr_idle_inhibitor_v1 *inhibitor; - - wl_list_for_each(inhibitor, &mgr->inhibitors, link) - { - for (auto& [_, bg_view] : views) - { - if (bg_view.view && (inhibitor->surface == bg_view.view->get_wlr_surface())) - { - // Tell core that the inhibitor was destroyed. It was not really destroyed, but core - // will adjust its internal state as if the inhibitor wasn't there. - wl_signal_emit(&inhibitor->events.destroy, inhibitor->surface); - break; - } - } - } - }); - } - - void fini() override - { - close_all_views(); - wf::get_core().disconnect(&on_view_pre_map); - } -}; - -DECLARE_WAYFIRE_PLUGIN(wayfire_background_view); diff --git a/src/meson.build b/src/meson.build index d09b4b8..a47041a 100644 --- a/src/meson.build +++ b/src/meson.build @@ -8,10 +8,6 @@ autorotate = shared_module('autorotate-iio', 'autorotate-iio.cpp', install: true, install_dir: join_paths(get_option('libdir'), 'wayfire')) endif -background_view = shared_module('background-view', 'background-view.cpp', - dependencies: [wayfire, wf_protos], - install: true, install_dir: join_paths(get_option('libdir'), 'wayfire')) - benchmark = shared_module('bench', 'bench.cpp', dependencies: [wayfire], install: true, install_dir: join_paths(get_option('libdir'), 'wayfire'))