Skip to content

Commit

Permalink
protocols: add shortcuts-inhibit
Browse files Browse the repository at this point in the history
  • Loading branch information
ammen99 committed Oct 21, 2023
1 parent caa4534 commit ac93706
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 1 deletion.
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
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

0 comments on commit ac93706

Please sign in to comment.