diff --git a/src/plugin/wayfire-kill-view.cpp b/src/plugin/wayfire-kill-view.cpp index 8753a26..3c657b7 100644 --- a/src/plugin/wayfire-kill-view.cpp +++ b/src/plugin/wayfire-kill-view.cpp @@ -24,6 +24,8 @@ #include +#include +#include #include #include #include @@ -44,70 +46,144 @@ wf::plugin_activation_data_t grab_interface{ static void bind_manager(wl_client *client, void *data, uint32_t version, uint32_t id); -static pid_t get_pid_for_view(wayfire_view view) +void wf_kill_view::close_view(wayfire_view view, wl_resource *resource) { + /* Compositor-owned views (e.g. the mag plugin) have no client to kill. */ + wl_client *view_client = view ? view->get_client() : nullptr; + if (!view_client) + { + send_cancel(resource); + return; + } + pid_t pid = 0; - wlr_surface *wlr_surface = view->get_wlr_surface(); #if WF_HAS_XWAYLAND - if (auto xwayland_surface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface)) + wlr_surface *wlr_surface = view->get_wlr_surface(); + if (auto xwayland_surface = wlr_surface ? + wlr_xwayland_surface_try_from_wlr_surface(wlr_surface) : nullptr) { pid = xwayland_surface->pid; } else #endif { - wl_client_get_credentials(view->get_client(), &pid, 0, 0); + wl_client_get_credentials(view_client, &pid, 0, 0); } - return pid; -} - -void wf_kill_view::send_view_info() -{ - wayfire_view view = wf::get_core().get_cursor_focus_view(); - if (!view) + if (pid == getpid()) { + send_cancel(resource); return; } - pid_t pid = get_pid_for_view(view); view->close(); + wf_kill_view_base_send_view_pid(resource, pid); +} - for (auto r : client_resources) +void wf_kill_view::send_view_info(wl_resource *resource) +{ + close_view(wf::get_core().get_cursor_focus_view(), resource); +} + +void wf_kill_view::send_cancel(wl_resource *resource) +{ + wf_kill_view_base_send_cancel(resource); +} + +void wf_kill_view::release_grabs() +{ + for (auto& [o, grab] : input_grabs) { - wf_kill_view_base_send_view_pid(r, pid); + grab->ungrab_input(); + o->deactivate_plugin(&grab_interface); } + + input_grabs.clear(); + grab_active = false; } -void wf_kill_view::send_cancel() +void wf_kill_view::start_grab(wl_resource *resource) { - for (auto r : client_resources) + /* One selection at a time; replacing a live grab orphans its scene node. */ + if (grab_active || requester) + { + send_cancel(resource); + return; + } + + for (auto& o : wf::get_core().output_layout->get_outputs()) + { + if (!o->activate_plugin(&grab_interface)) + { + continue; + } + + auto grab = std::make_unique(grab_interface.name, o, nullptr, base, nullptr); + grab->grab_input(wf::scene::layer::OVERLAY); + input_grabs[o] = std::move(grab); + } + + if (input_grabs.empty()) { - wf_kill_view_base_send_cancel(r); + send_cancel(resource); + return; } + + grab_active = true; + requester = resource; + idle_set_cursor.run_once([] () + { + wf::get_core().set_cursor("pirate"); + }); } void wf_kill_view::deactivate(uint32_t b) { - for (auto& o : wf::get_core().output_layout->get_outputs()) + if (!grab_active) { - o->deactivate_plugin(&grab_interface); - input_grabs[o]->ungrab_input(); - input_grabs[o].reset(); + return; } + release_grabs(); + idle_set_cursor.run_once([=] () { - if (b == BTN_LEFT) + /* The requester may have disconnected before the idle callback. */ + auto resource = requester; + requester = nullptr; + if (!resource) { - send_view_info(); + return; } - else + + if (b == BTN_LEFT) { - send_cancel(); + send_view_info(resource); + } else + { + send_cancel(resource); } }); } +void wf_kill_view::client_destroyed(wl_resource *resource) +{ + client_resources.erase(std::remove(client_resources.begin(), + client_resources.end(), resource), client_resources.end()); + + if (resource == requester) + { + /* Without its client the selection must not close anything. */ + if (grab_active) + { + release_grabs(); + idle_set_cursor.disconnect(); + wf::get_core().set_cursor("default"); + } + + requester = nullptr; + } +} + void wf_kill_view::end_grab(uint32_t b) { deactivate(b); @@ -128,83 +204,95 @@ wf_kill_view::wf_kill_view() LOGE("Failed to create wf_kill_view interface"); return; } + + on_output_pre_remove = [=] (wf::output_pre_remove_signal *ev) + { + auto it = input_grabs.find(ev->output); + if (it == input_grabs.end()) + { + return; + } + + it->second->ungrab_input(); + ev->output->deactivate_plugin(&grab_interface); + input_grabs.erase(it); + if (input_grabs.empty() && grab_active) + { + grab_active = false; + idle_set_cursor.disconnect(); + if (requester) + { + send_cancel(requester); + requester = nullptr; + } + } + }; + wf::get_core().output_layout->connect(&on_output_pre_remove); } wf_kill_view::~wf_kill_view() { - wl_global_destroy(manager); - - for (auto& o : wf::get_core().output_layout->get_outputs()) + if (manager) { - input_grabs[o].reset(); + wl_global_destroy(manager); } -} - -static void view_kill(struct wl_client *client, struct wl_resource *resource) -{ - wf_kill_view *wd = (wf_kill_view*)wl_resource_get_user_data(resource); - bool set_cursor = false; - for (auto& o : wf::get_core().output_layout->get_outputs()) + if (grab_active) { - wd->input_grabs[o] = std::make_unique (grab_interface.name, o, nullptr, wd->base, nullptr); - - if (!o->activate_plugin(&grab_interface)) - { - continue; - } - - wd->input_grabs[o]->grab_input(wf::scene::layer::OVERLAY); + release_grabs(); + wf::get_core().set_cursor("default"); + } - set_cursor = true; + if (requester) + { + send_cancel(requester); } - if (set_cursor) + /* Resources outlive the plugin; detach them so later requests and + * destructors do not touch freed memory. */ + for (auto r : client_resources) { - wd->idle_set_cursor.run_once([wd] () - { - wf::get_core().set_cursor("pirate"); - }); + wl_resource_set_user_data(r, nullptr); } } -static void top_view_kill(struct wl_client *client, struct wl_resource *resource) +static void view_kill(struct wl_client *client, struct wl_resource *resource) { wf_kill_view *wd = (wf_kill_view*)wl_resource_get_user_data(resource); - auto view = wf::get_core().seat->get_active_view(); - if (!view) + if (!wd) { + wf_kill_view_base_send_cancel(resource); return; } - pid_t pid = get_pid_for_view(view); - view->close(); + wd->start_grab(resource); +} - for (auto r : wd->client_resources) +static void top_view_kill(struct wl_client *client, struct wl_resource *resource) +{ + wf_kill_view *wd = (wf_kill_view*)wl_resource_get_user_data(resource); + if (!wd || wd->grab_active || wd->requester) { - wf_kill_view_base_send_view_pid(r, pid); + wf_kill_view_base_send_cancel(resource); + return; } + + wd->close_view(wf::get_core().seat->get_active_view(), resource); } static const struct wf_kill_view_base_interface wf_kill_view_impl = { - .view_kill = view_kill, + .view_kill = view_kill, .top_view_kill = top_view_kill, }; static void destroy_client(wl_resource *resource) { wf_kill_view *wd = (wf_kill_view*)wl_resource_get_user_data(resource); - - for (auto& r : wd->client_resources) + if (wd) { - if (r == resource) - { - r = nullptr; - } + wd->client_destroyed(resource); } - wd->client_resources.erase(std::remove(wd->client_resources.begin(), - wd->client_resources.end(), nullptr), wd->client_resources.end()); } static void bind_manager(wl_client *client, void *data, @@ -214,8 +302,13 @@ static void bind_manager(wl_client *client, void *data, auto resource = wl_resource_create(client, &wf_kill_view_base_interface, 1, id); + if (!resource) + { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &wf_kill_view_impl, data, destroy_client); wd->client_resources.push_back(resource); - } diff --git a/src/plugin/wayfire-kill-view.hpp b/src/plugin/wayfire-kill-view.hpp index f2ed946..b7e5696 100644 --- a/src/plugin/wayfire-kill-view.hpp +++ b/src/plugin/wayfire-kill-view.hpp @@ -25,6 +25,8 @@ #pragma once #include +#include +#include class wf_kill_view { @@ -34,11 +36,19 @@ class wf_kill_view wf::pointer_interaction_t *base; void set_base_ptr(wf::pointer_interaction_t *base); std::vector client_resources; - void send_view_info(); - void send_cancel(); + /* Client that issued the pending view_kill; replies go only to it. */ + wl_resource *requester = nullptr; + bool grab_active = false; + void start_grab(wl_resource *resource); + void release_grabs(); + void client_destroyed(wl_resource *resource); + void close_view(wayfire_view view, wl_resource *resource); + void send_view_info(wl_resource *resource); + void send_cancel(wl_resource *resource); void deactivate(uint32_t b); wf::wl_idle_call idle_set_cursor; std::map> input_grabs; + wf::signal::connection_t on_output_pre_remove; void end_grab(uint32_t b); wf_kill_view(); ~wf_kill_view();