forked from 12to11/12to11
Check in files for keyboard shortcut inhibition
* keyboard-shortcuts-inhibit-unstable-v1.xml: * keyboard_shortcuts_inhibit.c: New files.
This commit is contained in:
parent
167001689a
commit
f683d8c30f
2 changed files with 522 additions and 0 deletions
143
keyboard-shortcuts-inhibit-unstable-v1.xml
Normal file
143
keyboard-shortcuts-inhibit-unstable-v1.xml
Normal file
|
@ -0,0 +1,143 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="keyboard_shortcuts_inhibit_unstable_v1">
|
||||
|
||||
<copyright>
|
||||
Copyright © 2017 Red Hat Inc.
|
||||
|
||||
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 (including the next
|
||||
paragraph) 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.
|
||||
</copyright>
|
||||
|
||||
<description summary="Protocol for inhibiting the compositor keyboard shortcuts">
|
||||
This protocol specifies a way for a client to request the compositor
|
||||
to ignore its own keyboard shortcuts for a given seat, so that all
|
||||
key events from that seat get forwarded to a surface.
|
||||
|
||||
Warning! The protocol described in this file is experimental and
|
||||
backward incompatible changes may be made. Backward compatible
|
||||
changes may be added together with the corresponding interface
|
||||
version bump.
|
||||
Backward incompatible changes are done by bumping the version
|
||||
number in the protocol and interface names and resetting the
|
||||
interface version. Once the protocol is to be declared stable,
|
||||
the 'z' prefix and the version number in the protocol and
|
||||
interface names are removed and the interface version number is
|
||||
reset.
|
||||
</description>
|
||||
|
||||
<interface name="zwp_keyboard_shortcuts_inhibit_manager_v1" version="1">
|
||||
<description summary="context object for keyboard grab_manager">
|
||||
A global interface used for inhibiting the compositor keyboard shortcuts.
|
||||
</description>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy the keyboard shortcuts inhibitor object">
|
||||
Destroy the keyboard shortcuts inhibitor manager.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="inhibit_shortcuts">
|
||||
<description summary="create a new keyboard shortcuts inhibitor object">
|
||||
Create a new keyboard shortcuts inhibitor object associated with
|
||||
the given surface for the given seat.
|
||||
|
||||
If shortcuts are already inhibited for the specified seat and surface,
|
||||
a protocol error "already_inhibited" is raised by the compositor.
|
||||
</description>
|
||||
<arg name="id" type="new_id" interface="zwp_keyboard_shortcuts_inhibitor_v1"/>
|
||||
<arg name="surface" type="object" interface="wl_surface"
|
||||
summary="the surface that inhibits the keyboard shortcuts behavior"/>
|
||||
<arg name="seat" type="object" interface="wl_seat"
|
||||
summary="the wl_seat for which keyboard shortcuts should be disabled"/>
|
||||
</request>
|
||||
|
||||
<enum name="error">
|
||||
<entry name="already_inhibited"
|
||||
value="0"
|
||||
summary="the shortcuts are already inhibited for this surface"/>
|
||||
</enum>
|
||||
</interface>
|
||||
|
||||
<interface name="zwp_keyboard_shortcuts_inhibitor_v1" version="1">
|
||||
<description summary="context object for keyboard shortcuts inhibitor">
|
||||
A keyboard shortcuts inhibitor instructs the compositor to ignore
|
||||
its own keyboard shortcuts when the associated surface has keyboard
|
||||
focus. As a result, when the surface has keyboard focus on the given
|
||||
seat, it will receive all key events originating from the specified
|
||||
seat, even those which would normally be caught by the compositor for
|
||||
its own shortcuts.
|
||||
|
||||
The Wayland compositor is however under no obligation to disable
|
||||
all of its shortcuts, and may keep some special key combo for its own
|
||||
use, including but not limited to one allowing the user to forcibly
|
||||
restore normal keyboard events routing in the case of an unwilling
|
||||
client. The compositor may also use the same key combo to reactivate
|
||||
an existing shortcut inhibitor that was previously deactivated on
|
||||
user request.
|
||||
|
||||
When the compositor restores its own keyboard shortcuts, an
|
||||
"inactive" event is emitted to notify the client that the keyboard
|
||||
shortcuts inhibitor is not effectively active for the surface and
|
||||
seat any more, and the client should not expect to receive all
|
||||
keyboard events.
|
||||
|
||||
When the keyboard shortcuts inhibitor is inactive, the client has
|
||||
no way to forcibly reactivate the keyboard shortcuts inhibitor.
|
||||
|
||||
The user can chose to re-enable a previously deactivated keyboard
|
||||
shortcuts inhibitor using any mechanism the compositor may offer,
|
||||
in which case the compositor will send an "active" event to notify
|
||||
the client.
|
||||
|
||||
If the surface is destroyed, unmapped, or loses the seat's keyboard
|
||||
focus, the keyboard shortcuts inhibitor becomes irrelevant and the
|
||||
compositor will restore its own keyboard shortcuts but no "inactive"
|
||||
event is emitted in this case.
|
||||
</description>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy the keyboard shortcuts inhibitor object">
|
||||
Remove the keyboard shortcuts inhibitor from the associated wl_surface.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<event name="active">
|
||||
<description summary="shortcuts are inhibited">
|
||||
This event indicates that the shortcut inhibitor is active.
|
||||
|
||||
The compositor sends this event every time compositor shortcuts
|
||||
are inhibited on behalf of the surface. When active, the client
|
||||
may receive input events normally reserved by the compositor
|
||||
(see zwp_keyboard_shortcuts_inhibitor_v1).
|
||||
|
||||
This occurs typically when the initial request "inhibit_shortcuts"
|
||||
first becomes active or when the user instructs the compositor to
|
||||
re-enable and existing shortcuts inhibitor using any mechanism
|
||||
offered by the compositor.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<event name="inactive">
|
||||
<description summary="shortcuts are restored">
|
||||
This event indicates that the shortcuts inhibitor is inactive,
|
||||
normal shortcuts processing is restored by the compositor.
|
||||
</description>
|
||||
</event>
|
||||
</interface>
|
||||
</protocol>
|
379
keyboard_shortcuts_inhibit.c
Normal file
379
keyboard_shortcuts_inhibit.c
Normal file
|
@ -0,0 +1,379 @@
|
|||
/* Wayland compositor running on top of an X server.
|
||||
|
||||
Copyright (C) 2022 to various contributors.
|
||||
|
||||
This file is part of 12to11.
|
||||
|
||||
12to11 is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation, either version 3 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
12to11 is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "compositor.h"
|
||||
#include "keyboard-shortcuts-inhibit-unstable-v1.h"
|
||||
|
||||
typedef struct _ShortcutInhibitDataRecord ShortcutInhibitDataRecord;
|
||||
typedef struct _KeyboardShortcutInhibitor KeyboardShortcutInhibitor;
|
||||
|
||||
enum
|
||||
{
|
||||
IsGrabbed = 1,
|
||||
};
|
||||
|
||||
struct _KeyboardShortcutInhibitor
|
||||
{
|
||||
/* The surface to which the inhibitor applies. */
|
||||
Surface *surface;
|
||||
|
||||
/* The associated struct wl_resource. */
|
||||
struct wl_resource *resource;
|
||||
|
||||
/* The next and last shortcut inhibitors in this list. Not valid if
|
||||
surface is NULL. */
|
||||
KeyboardShortcutInhibitor *next, *last;
|
||||
|
||||
/* The seat. */
|
||||
Seat *seat;
|
||||
|
||||
/* The seat destruction key. */
|
||||
void *seat_key;
|
||||
|
||||
/* Some flags. */
|
||||
int flags;
|
||||
};
|
||||
|
||||
struct _ShortcutInhibitDataRecord
|
||||
{
|
||||
/* List of all keyboard shortcut inhibitors. */
|
||||
KeyboardShortcutInhibitor inhibitors;
|
||||
};
|
||||
|
||||
/* The zwp_keyboard_shortcuts_inhibit_manager_v1 global. */
|
||||
struct wl_global *inhibit_manager_global;
|
||||
|
||||
static void
|
||||
Destroy (struct wl_client *client, struct wl_resource *resource)
|
||||
{
|
||||
wl_resource_destroy (resource);
|
||||
}
|
||||
|
||||
static void
|
||||
FreeShortcutInhibitData (void *data)
|
||||
{
|
||||
ShortcutInhibitDataRecord *record;
|
||||
KeyboardShortcutInhibitor *inhibitor;
|
||||
|
||||
record = data;
|
||||
|
||||
/* Clear the surface of every attached keyboard shortcut
|
||||
inhibitor. */
|
||||
inhibitor = record->inhibitors.next;
|
||||
XLAssert (inhibitor != NULL);
|
||||
|
||||
while (inhibitor != &record->inhibitors)
|
||||
{
|
||||
inhibitor->surface = NULL;
|
||||
|
||||
/* Move to the next inhibitor. */
|
||||
inhibitor = inhibitor->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
InitShortcutInhibitData (ShortcutInhibitDataRecord *data)
|
||||
{
|
||||
/* If data is already initialized, do nothing. */
|
||||
if (data->inhibitors.next)
|
||||
return;
|
||||
|
||||
/* Otherwise, initialize the list of inhibitors. */
|
||||
data->inhibitors.next = &data->inhibitors;
|
||||
data->inhibitors.last = &data->inhibitors;
|
||||
}
|
||||
|
||||
static KeyboardShortcutInhibitor *
|
||||
FindKeyboardShortcutInhibitor (Surface *surface, Seat *seat)
|
||||
{
|
||||
ShortcutInhibitDataRecord *data;
|
||||
KeyboardShortcutInhibitor *inhibitor;
|
||||
|
||||
data = XLSurfaceFindClientData (surface, ShortcutInhibitData);
|
||||
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
inhibitor = data->inhibitors.next;
|
||||
while (inhibitor != &data->inhibitors)
|
||||
{
|
||||
if (inhibitor->seat == seat)
|
||||
return inhibitor;
|
||||
|
||||
inhibitor = data->inhibitors.next;
|
||||
}
|
||||
|
||||
/* There is no inhibitor for this seat on the given surface. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
DestroyKeyboardShortcutsInhibitor (struct wl_client *client,
|
||||
struct wl_resource *resource)
|
||||
{
|
||||
wl_resource_destroy (resource);
|
||||
}
|
||||
|
||||
static struct zwp_keyboard_shortcuts_inhibitor_v1_interface inhibitor_impl =
|
||||
{
|
||||
.destroy = DestroyKeyboardShortcutsInhibitor,
|
||||
};
|
||||
|
||||
static void
|
||||
HandleResourceDestroy (struct wl_resource *resource)
|
||||
{
|
||||
KeyboardShortcutInhibitor *inhibitor;
|
||||
|
||||
inhibitor = wl_resource_get_user_data (resource);
|
||||
|
||||
if (inhibitor->surface)
|
||||
{
|
||||
/* Unlink the inhibitor from its surroundings. */
|
||||
inhibitor->next->last = inhibitor->last;
|
||||
inhibitor->last->next = inhibitor->next;
|
||||
}
|
||||
|
||||
if (inhibitor->seat)
|
||||
{
|
||||
/* Cancel the seat destruction callback. */
|
||||
XLSeatCancelDestroyListener (inhibitor->seat_key);
|
||||
|
||||
/* Ungrab the keyboard if it is grabbed. */
|
||||
if (inhibitor->flags & IsGrabbed)
|
||||
XLSeatCancelExternalGrab (inhibitor->seat);
|
||||
}
|
||||
|
||||
/* Free the inhibitor. */
|
||||
XLFree (inhibitor);
|
||||
}
|
||||
|
||||
static void
|
||||
HandleSeatDestroy (void *data)
|
||||
{
|
||||
KeyboardShortcutInhibitor *inhibitor;
|
||||
|
||||
inhibitor = data;
|
||||
|
||||
/* The seat was destroyed. Unlink the inhibitor, then remove the
|
||||
seat. */
|
||||
if (inhibitor->surface)
|
||||
{
|
||||
/* Unlink the inhibitor from its surroundings. */
|
||||
inhibitor->next->last = inhibitor->last;
|
||||
inhibitor->last->next = inhibitor->next;
|
||||
}
|
||||
|
||||
/* Clear the seat. */
|
||||
inhibitor->seat = NULL;
|
||||
inhibitor->seat_key = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
InhibitShortcuts (struct wl_client *client, struct wl_resource *resource,
|
||||
uint32_t id, struct wl_resource *surface_resource,
|
||||
struct wl_resource *seat_resource)
|
||||
{
|
||||
ShortcutInhibitDataRecord *record;
|
||||
Surface *surface;
|
||||
Seat *seat;
|
||||
KeyboardShortcutInhibitor *inhibitor;
|
||||
struct wl_resource *dummy_resource;
|
||||
|
||||
surface = wl_resource_get_user_data (surface_resource);
|
||||
seat = wl_resource_get_user_data (seat_resource);
|
||||
|
||||
/* If the seat is inert, return an empty inhibitor. */
|
||||
if (XLSeatIsInert (seat))
|
||||
{
|
||||
dummy_resource
|
||||
= wl_resource_create (client,
|
||||
&zwp_keyboard_shortcuts_inhibitor_v1_interface,
|
||||
wl_resource_get_version (resource), id);
|
||||
|
||||
if (!dummy_resource)
|
||||
wl_resource_post_no_memory (resource);
|
||||
else
|
||||
wl_resource_set_implementation (dummy_resource, &inhibitor_impl,
|
||||
NULL, NULL);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check that there is no keyboard shortcut inhibitor already
|
||||
present. */
|
||||
|
||||
#define AlreadyInhibited \
|
||||
ZWP_KEYBOARD_SHORTCUTS_INHIBIT_MANAGER_V1_ERROR_ALREADY_INHIBITED
|
||||
|
||||
if (FindKeyboardShortcutInhibitor (surface, seat))
|
||||
{
|
||||
wl_resource_post_error (resource, AlreadyInhibited,
|
||||
"inhibitor already attached to surface and seat");
|
||||
return;
|
||||
}
|
||||
|
||||
#undef AlreadyInhibited
|
||||
|
||||
record = XLSurfaceGetClientData (surface, ShortcutInhibitData,
|
||||
sizeof *record,
|
||||
FreeShortcutInhibitData);
|
||||
InitShortcutInhibitData (record);
|
||||
|
||||
/* Allocate a new keyboard shortcut inhibitor. */
|
||||
inhibitor = XLSafeMalloc (sizeof *inhibitor);
|
||||
|
||||
if (!inhibitor)
|
||||
{
|
||||
wl_resource_post_no_memory (resource);
|
||||
return;
|
||||
}
|
||||
|
||||
memset (inhibitor, 0, sizeof *inhibitor);
|
||||
inhibitor->resource
|
||||
= wl_resource_create (client,
|
||||
&zwp_keyboard_shortcuts_inhibitor_v1_interface,
|
||||
wl_resource_get_version (resource), id);
|
||||
|
||||
if (!inhibitor->resource)
|
||||
{
|
||||
wl_resource_post_no_memory (resource);
|
||||
XLFree (inhibitor);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Link the inhibitor onto the list. */
|
||||
inhibitor->next = record->inhibitors.next;
|
||||
inhibitor->last = &record->inhibitors;
|
||||
record->inhibitors.next->last = inhibitor;
|
||||
record->inhibitors.next = inhibitor;
|
||||
|
||||
/* Attach the surface. */
|
||||
inhibitor->surface = surface;
|
||||
|
||||
/* And the seat. */
|
||||
inhibitor->seat = seat;
|
||||
inhibitor->seat_key
|
||||
= XLSeatRunOnDestroy (seat, HandleSeatDestroy, inhibitor);
|
||||
|
||||
/* Attach the resource implementation. */
|
||||
wl_resource_set_implementation (inhibitor->resource, &inhibitor_impl,
|
||||
inhibitor, HandleResourceDestroy);
|
||||
|
||||
/* If the given surface is the seat's focus, try to apply the grab
|
||||
now. */
|
||||
if (surface == XLSeatGetFocus (seat)
|
||||
&& XLSeatApplyExternalGrab (seat, surface))
|
||||
{
|
||||
/* The external grab is now active, so send the active
|
||||
signal. */
|
||||
zwp_keyboard_shortcuts_inhibitor_v1_send_active (inhibitor->resource);
|
||||
|
||||
/* Mark the inhibitor as active. */
|
||||
inhibitor->flags |= IsGrabbed;
|
||||
}
|
||||
}
|
||||
|
||||
static struct zwp_keyboard_shortcuts_inhibit_manager_v1_interface manager_impl =
|
||||
{
|
||||
.inhibit_shortcuts = InhibitShortcuts,
|
||||
.destroy = Destroy,
|
||||
};
|
||||
|
||||
static void
|
||||
HandleBind (struct wl_client *client, void *data, uint32_t version,
|
||||
uint32_t id)
|
||||
{
|
||||
struct wl_resource *resource;
|
||||
|
||||
resource
|
||||
= wl_resource_create (client,
|
||||
&zwp_keyboard_shortcuts_inhibit_manager_v1_interface,
|
||||
version, id);
|
||||
|
||||
if (!resource)
|
||||
{
|
||||
wl_client_post_no_memory (client);
|
||||
return;
|
||||
}
|
||||
|
||||
wl_resource_set_implementation (resource, &manager_impl, NULL, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
XLInitKeyboardShortcutsInhibit (void)
|
||||
{
|
||||
inhibit_manager_global
|
||||
= wl_global_create (compositor.wl_display,
|
||||
&zwp_keyboard_shortcuts_inhibit_manager_v1_interface,
|
||||
1, NULL, HandleBind);
|
||||
}
|
||||
|
||||
void
|
||||
XLCheckShortcutInhibition (Seat *seat, Surface *surface)
|
||||
{
|
||||
KeyboardShortcutInhibitor *inhibitor;
|
||||
|
||||
/* If SURFACE has a shortcut inhibitor, inhibit shortcuts and send
|
||||
the active signal. */
|
||||
|
||||
inhibitor = FindKeyboardShortcutInhibitor (surface, seat);
|
||||
|
||||
if (!inhibitor)
|
||||
return;
|
||||
|
||||
/* Try to apply an external grab. */
|
||||
if (XLSeatApplyExternalGrab (seat, surface))
|
||||
{
|
||||
/* The external grab is now active, so send the active
|
||||
signal. */
|
||||
zwp_keyboard_shortcuts_inhibitor_v1_send_active (inhibitor->resource);
|
||||
|
||||
/* Mark the inhibitor as active. */
|
||||
inhibitor->flags |= IsGrabbed;
|
||||
}
|
||||
else if (inhibitor->flags & IsGrabbed)
|
||||
{
|
||||
/* The grab failed, and inhibitor was already grabbed (can that
|
||||
even happen?) */
|
||||
inhibitor->flags &= ~IsGrabbed;
|
||||
zwp_keyboard_shortcuts_inhibitor_v1_send_inactive (inhibitor->resource);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
XLReleaseShortcutInhibition (Seat *seat, Surface *surface)
|
||||
{
|
||||
KeyboardShortcutInhibitor *inhibitor;
|
||||
|
||||
inhibitor = FindKeyboardShortcutInhibitor (surface, seat);
|
||||
|
||||
if (!inhibitor || !(inhibitor->flags & IsGrabbed))
|
||||
return;
|
||||
|
||||
/* Cancel the grab. */
|
||||
XLSeatCancelExternalGrab (seat);
|
||||
|
||||
/* Mark the inhibitor as no longer holding a grab. */
|
||||
inhibitor->flags &= IsGrabbed;
|
||||
zwp_keyboard_shortcuts_inhibitor_v1_send_inactive (inhibitor->resource);
|
||||
}
|
Loading…
Add table
Reference in a new issue