Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement keyboard-shortcuts-inhibitor-v1 #1969

Merged
merged 3 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions metadata/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ install_data('workarounds.xml', install_dir: conf_data.get('PLUGIN_XML_DIR'))
install_data('wrot.xml', install_dir: conf_data.get('PLUGIN_XML_DIR'))
install_data('zoom.xml', install_dir: conf_data.get('PLUGIN_XML_DIR'))
install_data('scale-title-filter.xml', install_dir: conf_data.get('PLUGIN_XML_DIR'))
install_data('shortcuts-inhibit.xml', install_dir: conf_data.get('PLUGIN_XML_DIR'))
install_data('wsets.xml', install_dir: conf_data.get('PLUGIN_XML_DIR'))
install_data('wayfire-shell.xml', install_dir: conf_data.get('PLUGIN_XML_DIR'))
install_data('xdg-activation.xml', install_dir: conf_data.get('PLUGIN_XML_DIR'))
20 changes: 20 additions & 0 deletions metadata/shortcuts-inhibit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0"?>
<wayfire>
<plugin name="shortcuts-inhibit">
<_short>Keyboard Shortcuts Inhibit Protocol</_short>
<_long>An implementation of the keyboard-shortcuts-inhibit-v1 protocol.</_long>
<category>Utility</category>

<option name="inhibit_by_default" type="string">
<_short>Inhibit by default</_short>
<_long>Criteria for which apps to inhibit input for even though they do not request it via the protocol.</_long>
<default>none</default>
</option>

<option name="break_grab" type="key">
<_short>Break current inhibitor</_short>
<_long>A keybinding to remove the currently active inhibitor temporary.</_long>
<default>disabled</default>
</option>
</plugin>
</wayfire>
2 changes: 1 addition & 1 deletion plugins/protocols/meson.build
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
protocol_plugins = [
'foreign-toplevel', 'gtk-shell', 'wayfire-shell', 'xdg-activation',
'foreign-toplevel', 'gtk-shell', 'wayfire-shell', 'xdg-activation', 'shortcuts-inhibit'
]

all_include_dirs = [wayfire_api_inc, wayfire_conf_inc, plugins_common_inc]
Expand Down
180 changes: 180 additions & 0 deletions plugins/protocols/shortcuts-inhibit.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
#include "wayfire/core.hpp"
#include "wayfire/option-wrapper.hpp"
#include "wayfire/plugins/common/shared-core-data.hpp"
#include "wayfire/signal-definitions.hpp"
#include "wayfire/signal-provider.hpp"
#include "wayfire/util.hpp"
#include "wayfire/seat.hpp"
#include "wayfire/view.hpp"
#include "wayfire/matcher.hpp"
#include "wayfire/bindings-repository.hpp"
#include <wayfire/nonstd/wlroots-full.hpp>
#include <wayfire/plugin.hpp>
#include <wayland-server-protocol.h>

class wayfire_shortcuts_inhibit : public wf::plugin_interface_t
{
public:
void init() override
{
inhibit_manager = wlr_keyboard_shortcuts_inhibit_v1_create(wf::get_core().display);

keyboard_inhibit_new.set_callback([&] (void *data)
{
auto wlr_inhibitor = (struct wlr_keyboard_shortcuts_inhibitor_v1*)data;
if (inhibitors.count(wlr_inhibitor->surface))
{
LOGE("Duplicate inhibitors for one surface not supported!");
return;
}

inhibitors[wlr_inhibitor->surface] = std::make_unique<inhibitor_t>();
auto& inhibitor = inhibitors[wlr_inhibitor->surface];

inhibitor->inhibitor = wlr_inhibitor;
inhibitor->on_destroy.set_callback([=] (auto)
{
deactivate_for_surface(wlr_inhibitor->surface);
this->inhibitors.erase(wlr_inhibitor->surface);
});
inhibitor->on_destroy.connect(&wlr_inhibitor->events.destroy);
check_inhibit(wf::get_core().seat->get_active_node());
});
keyboard_inhibit_new.connect(&inhibit_manager->events.new_inhibitor);
wf::get_core().connect(&on_kb_focus_change);
wf::get_core().connect(&on_view_mapped);
wf::get_core().connect(&on_key_press);
}

void fini() override
{}

void check_inhibit(wf::scene::node_ptr focus)
{
auto focus_view = focus ? wf::node_to_view(focus) : nullptr;
wlr_surface *new_focus = focus_view ? focus_view->get_keyboard_focus_surface() : nullptr;
if (!inhibitors.count(new_focus))
{
new_focus = nullptr;
}

if (new_focus == last_focus)
{
return;
}

deactivate_for_surface(last_focus);
activate_for_surface(new_focus);
}

bool is_unloadable() override
{
return false;
}

private:
wlr_keyboard_shortcuts_inhibit_manager_v1 *inhibit_manager;
wf::wl_listener_wrapper keyboard_inhibit_new;
wf::view_matcher_t inhibit_by_default{"shortcuts-inhibit/inhibit_by_default"};

struct inhibitor_t
{
bool active = false;
wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor;
wf::wl_listener_wrapper on_destroy;
};

std::map<wlr_surface*, std::unique_ptr<inhibitor_t>> inhibitors;
wlr_surface *last_focus = nullptr;

void activate_for_surface(wlr_surface *surface)
{
if (!surface)
{
return;
}

auto& inhibitor = inhibitors[surface];
if (!inhibitor->active)
{
LOGD("Activating inhibitor for surface ", surface);
wf::get_core().bindings->set_enabled(false);

if (inhibitor->inhibitor)
{
wlr_keyboard_shortcuts_inhibitor_v1_activate(inhibitor->inhibitor);
}

inhibitor->active = true;
}

last_focus = surface;
}

void deactivate_for_surface(wlr_surface *surface)
{
if (!surface)
{
return;
}

auto& inhibitor = inhibitors[surface];
if (inhibitor->active)
{
LOGD("Deactivating inhibitor for surface ", surface);
wf::get_core().bindings->set_enabled(true);

if (inhibitor->inhibitor)
{
wlr_keyboard_shortcuts_inhibitor_v1_deactivate(inhibitor->inhibitor);
}

inhibitor->active = false;
}

last_focus = nullptr;
}

wf::signal::connection_t<wf::keyboard_focus_changed_signal> on_kb_focus_change =
[=] (wf::keyboard_focus_changed_signal *ev)
{
check_inhibit(ev->new_focus);
};

wf::signal::connection_t<wf::view_mapped_signal> on_view_mapped = [=] (wf::view_mapped_signal *ev)
{
if (inhibit_by_default.matches(ev->view) && ev->view->get_keyboard_focus_surface())
{
auto surface = ev->view->get_keyboard_focus_surface();
inhibitors[surface] = std::make_unique<inhibitor_t>();
auto& inhibitor = inhibitors[surface];

inhibitor->inhibitor = nullptr;
inhibitor->on_destroy.set_callback([this, surface] (auto)
{
deactivate_for_surface(surface);
this->inhibitors.erase(surface);
});
inhibitor->on_destroy.connect(&surface->events.destroy);
check_inhibit(wf::get_core().seat->get_active_node());
}
};

wf::option_wrapper_t<wf::keybinding_t> break_grab_key{"shortcuts-inhibit/break_grab"};

wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key_press =
[=] (wf::input_event_signal<wlr_keyboard_key_event> *ev)
{
auto break_key = break_grab_key.value();

if ((ev->event->state == WL_KEYBOARD_KEY_STATE_PRESSED) &&
(wf::get_core().seat->get_keyboard_modifiers() == break_key.get_modifiers()) &&
(ev->event->keycode == break_key.get_key()))
{
LOGD("Force-break active inhibitor");
deactivate_for_surface(last_focus);
}
};
};

DECLARE_WAYFIRE_PLUGIN(wayfire_shortcuts_inhibit);
1 change: 1 addition & 0 deletions proto/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ server_protocols = [
[wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/relative-pointer/relative-pointer-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/tablet/tablet-unstable-v2.xml'],
[wl_protocol_dir, 'unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml'],
'wayfire-shell-unstable-v2.xml',
'gtk-shell.xml',
'wlr-layer-shell-unstable-v1.xml',
Expand Down
5 changes: 5 additions & 0 deletions src/api/wayfire/bindings-repository.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ class bindings_repository_t
/** Erase binding of any type by callback */
void rem_binding(void *callback);

/**
* Enable or disable the repository. The state is reference-counted and starts at 1 (enabled).
*/
void set_enabled(bool enabled);

struct impl;
std::unique_ptr<impl> priv;
};
Expand Down
1 change: 1 addition & 0 deletions src/api/wayfire/nonstd/wlroots-full.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ extern "C"
#include <wlr/util/region.h>
#include <wlr/types/wlr_screencopy_v1.h>
#include <wlr/types/wlr_export_dmabuf_v1.h>
#include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h>

// Shells
#if __has_include(<xdg-shell-protocol.h>)
Expand Down
2 changes: 1 addition & 1 deletion src/api/wayfire/plugin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ class plugin_interface_t
using wayfire_plugin_load_func = wf::plugin_interface_t * (*)();

/** The version of Wayfire's API/ABI */
constexpr uint32_t WAYFIRE_API_ABI_VERSION = 2023'10'05;
constexpr uint32_t WAYFIRE_API_ABI_VERSION = 2023'10'21;

/**
* Each plugin must also provide a function which returns the Wayfire API/ABI
Expand Down
5 changes: 5 additions & 0 deletions src/api/wayfire/seat.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ class seat_t
*/
void set_active_node(wf::scene::node_ptr node);

/**
* Get the node which has current keyboard focus.
*/
wf::scene::node_ptr get_active_node();

/**
* Try to focus the given view. This may not work if another view or a node requests a higher focus
* importance.
Expand Down
9 changes: 8 additions & 1 deletion src/core/seat/bindings-repository-impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ struct wf::bindings_repository_t::impl
{
this->idle_recreate_hotspots.run_once([=] ()
{
hotspot_mgr.update_hotspots(activators);
if (enabled > 0)
{
hotspot_mgr.update_hotspots(activators);
} else
{
hotspot_mgr.update_hotspots({});
}
});
}

Expand All @@ -33,4 +39,5 @@ struct wf::bindings_repository_t::impl
};

wf::wl_idle_call idle_recreate_hotspots;
int enabled = 1;
};
26 changes: 26 additions & 0 deletions src/core/seat/bindings-repository.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ void wf::bindings_repository_t::add_activator(
bool wf::bindings_repository_t::handle_key(const wf::keybinding_t& pressed,
uint32_t mod_binding_key)
{
if (!priv->enabled)
{
return false;
}

std::vector<std::function<bool()>> callbacks;
for (auto& binding : this->priv->keys)
{
Expand Down Expand Up @@ -101,6 +106,11 @@ bool wf::bindings_repository_t::handle_key(const wf::keybinding_t& pressed,
bool wf::bindings_repository_t::handle_axis(uint32_t modifiers,
wlr_pointer_axis_event *ev)
{
if (!priv->enabled)
{
return false;
}

std::vector<wf::axis_callback*> callbacks;

for (auto& binding : this->priv->axes)
Expand All @@ -121,6 +131,11 @@ bool wf::bindings_repository_t::handle_axis(uint32_t modifiers,

bool wf::bindings_repository_t::handle_button(const wf::buttonbinding_t& pressed)
{
if (!priv->enabled)
{
return false;
}

std::vector<std::function<bool()>> callbacks;
for (auto& binding : this->priv->buttons)
{
Expand Down Expand Up @@ -165,6 +180,11 @@ bool wf::bindings_repository_t::handle_button(const wf::buttonbinding_t& pressed

void wf::bindings_repository_t::handle_gesture(const wf::touchgesture_t& gesture)
{
if (!priv->enabled)
{
return;
}

std::vector<std::function<void()>> callbacks;
for (auto& binding : this->priv->activators)
{
Expand Down Expand Up @@ -218,3 +238,9 @@ void wf::bindings_repository_t::rem_binding(void *callback)
priv->recreate_hotspots();
}
}

void wf::bindings_repository_t::set_enabled(bool enabled)
{
priv->enabled += (enabled ? 1 : -1);
priv->recreate_hotspots();
}
1 change: 1 addition & 0 deletions src/core/seat/hotspot-manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class hotspot_manager_t
void update_hotspots(const container_t& activators);

private:
bool enabled = true;
std::vector<std::unique_ptr<hotspot_instance_t>> hotspots;
};
}
5 changes: 5 additions & 0 deletions src/core/seat/seat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ void wf::seat_t::set_active_node(wf::scene::node_ptr node)
priv->set_keyboard_focus(focus.node ? focus.node->shared_from_this() : nullptr);
}

wf::scene::node_ptr wf::seat_t::get_active_node()
{
return priv->keyboard_focus;
}

void wf::seat_t::focus_output(wf::output_t *wo)
{
if (priv->active_output == wo)
Expand Down