From 5b7302b46dfaf770832ada283ad2be7d823e5599 Mon Sep 17 00:00:00 2001 From: bbedward Date: Thu, 4 Dec 2025 00:08:26 -0500 Subject: [PATCH] color picker: use shortcuts inhibitor when active --- core/internal/colorpicker/picker.go | 49 +++ .../keyboard_shortcuts_inhibit.go | 284 ++++++++++++++++++ ...keyboard-shortcuts-inhibit-unstable-v1.xml | 143 +++++++++ 3 files changed, 476 insertions(+) create mode 100644 core/internal/proto/keyboard_shortcuts_inhibit/keyboard_shortcuts_inhibit.go create mode 100644 core/internal/proto/xml/keyboard-shortcuts-inhibit-unstable-v1.xml diff --git a/core/internal/colorpicker/picker.go b/core/internal/colorpicker/picker.go index b871f304..7216a895 100644 --- a/core/internal/colorpicker/picker.go +++ b/core/internal/colorpicker/picker.go @@ -6,6 +6,7 @@ import ( "sync" "github.com/AvengeMedia/DankMaterialShell/core/internal/log" + "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/keyboard_shortcuts_inhibit" "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wlr_layer_shell" "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wlr_screencopy" "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wp_viewporter" @@ -58,6 +59,9 @@ type Picker struct { screencopy *wlr_screencopy.ZwlrScreencopyManagerV1 viewporter *wp_viewporter.WpViewporter + shortcutsInhibitMgr *keyboard_shortcuts_inhibit.ZwpKeyboardShortcutsInhibitManagerV1 + shortcutsInhibitor *keyboard_shortcuts_inhibit.ZwpKeyboardShortcutsInhibitorV1 + outputs map[uint32]*Output outputsMu sync.Mutex @@ -266,6 +270,12 @@ func (p *Picker) handleGlobal(e client.RegistryGlobalEvent) { if err := p.registry.Bind(e.Name, e.Interface, e.Version, viewporter); err == nil { p.viewporter = viewporter } + + case keyboard_shortcuts_inhibit.ZwpKeyboardShortcutsInhibitManagerV1InterfaceName: + mgr := keyboard_shortcuts_inhibit.NewZwpKeyboardShortcutsInhibitManagerV1(p.ctx) + if err := p.registry.Bind(e.Name, e.Interface, e.Version, mgr); err == nil { + p.shortcutsInhibitMgr = mgr + } } } @@ -390,6 +400,9 @@ func (p *Picker) createLayerSurface(output *Output) (*LayerSurface, error) { } else { p.redrawSurface(ls) } + + // Request shortcut inhibition once surface is configured + p.ensureShortcutsInhibitor(ls) }) layerSurf.SetClosedHandler(func(e wlr_layer_shell.ZwlrLayerSurfaceV1ClosedEvent) { @@ -415,6 +428,28 @@ func (p *Picker) computeSurfaceScale(ls *LayerSurface) int32 { return scale } +func (p *Picker) ensureShortcutsInhibitor(ls *LayerSurface) { + if p.shortcutsInhibitMgr == nil || p.seat == nil || p.shortcutsInhibitor != nil { + return + } + + inhibitor, err := p.shortcutsInhibitMgr.InhibitShortcuts(ls.wlSurface, p.seat) + if err != nil { + log.Debug("failed to create shortcuts inhibitor", "err", err) + return + } + + p.shortcutsInhibitor = inhibitor + + inhibitor.SetActiveHandler(func(e keyboard_shortcuts_inhibit.ZwpKeyboardShortcutsInhibitorV1ActiveEvent) { + log.Debug("shortcuts inhibitor active") + }) + + inhibitor.SetInactiveHandler(func(e keyboard_shortcuts_inhibit.ZwpKeyboardShortcutsInhibitorV1InactiveEvent) { + log.Debug("shortcuts inhibitor deactivated by compositor") + }) +} + func (p *Picker) captureForSurface(ls *LayerSurface) { frame, err := p.screencopy.CaptureOutput(0, ls.output.wlOutput) if err != nil { @@ -631,6 +666,20 @@ func (p *Picker) cleanup() { } } + if p.shortcutsInhibitor != nil { + if err := p.shortcutsInhibitor.Destroy(); err != nil { + log.Debug("failed to destroy shortcuts inhibitor", "err", err) + } + p.shortcutsInhibitor = nil + } + + if p.shortcutsInhibitMgr != nil { + if err := p.shortcutsInhibitMgr.Destroy(); err != nil { + log.Debug("failed to destroy shortcuts inhibit manager", "err", err) + } + p.shortcutsInhibitMgr = nil + } + if p.viewporter != nil { p.viewporter.Destroy() } diff --git a/core/internal/proto/keyboard_shortcuts_inhibit/keyboard_shortcuts_inhibit.go b/core/internal/proto/keyboard_shortcuts_inhibit/keyboard_shortcuts_inhibit.go new file mode 100644 index 00000000..1c701880 --- /dev/null +++ b/core/internal/proto/keyboard_shortcuts_inhibit/keyboard_shortcuts_inhibit.go @@ -0,0 +1,284 @@ +// Generated by go-wayland-scanner +// https://github.com/yaslama/go-wayland/cmd/go-wayland-scanner +// XML file : internal/proto/xml/keyboard-shortcuts-inhibit-unstable-v1.xml +// +// keyboard_shortcuts_inhibit_unstable_v1 Protocol 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. + +package keyboard_shortcuts_inhibit + +import "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" + +// ZwpKeyboardShortcutsInhibitManagerV1InterfaceName is the name of the interface as it appears in the [client.Registry]. +// It can be used to match the [client.RegistryGlobalEvent.Interface] in the +// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. +const ZwpKeyboardShortcutsInhibitManagerV1InterfaceName = "zwp_keyboard_shortcuts_inhibit_manager_v1" + +// ZwpKeyboardShortcutsInhibitManagerV1 : context object for keyboard grab_manager +// +// A global interface used for inhibiting the compositor keyboard shortcuts. +type ZwpKeyboardShortcutsInhibitManagerV1 struct { + client.BaseProxy +} + +// NewZwpKeyboardShortcutsInhibitManagerV1 : context object for keyboard grab_manager +// +// A global interface used for inhibiting the compositor keyboard shortcuts. +func NewZwpKeyboardShortcutsInhibitManagerV1(ctx *client.Context) *ZwpKeyboardShortcutsInhibitManagerV1 { + zwpKeyboardShortcutsInhibitManagerV1 := &ZwpKeyboardShortcutsInhibitManagerV1{} + ctx.Register(zwpKeyboardShortcutsInhibitManagerV1) + return zwpKeyboardShortcutsInhibitManagerV1 +} + +// Destroy : destroy the keyboard shortcuts inhibitor object +// +// Destroy the keyboard shortcuts inhibitor manager. +func (i *ZwpKeyboardShortcutsInhibitManagerV1) Destroy() error { + defer i.Context().Unregister(i) + const opcode = 0 + const _reqBufLen = 8 + var _reqBuf [_reqBufLen]byte + l := 0 + client.PutUint32(_reqBuf[l:4], i.ID()) + l += 4 + client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) + l += 4 + err := i.Context().WriteMsg(_reqBuf[:], nil) + return err +} + +// InhibitShortcuts : 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. +// +// surface: the surface that inhibits the keyboard shortcuts behavior +// seat: the wl_seat for which keyboard shortcuts should be disabled +func (i *ZwpKeyboardShortcutsInhibitManagerV1) InhibitShortcuts(surface *client.Surface, seat *client.Seat) (*ZwpKeyboardShortcutsInhibitorV1, error) { + id := NewZwpKeyboardShortcutsInhibitorV1(i.Context()) + const opcode = 1 + const _reqBufLen = 8 + 4 + 4 + 4 + var _reqBuf [_reqBufLen]byte + l := 0 + client.PutUint32(_reqBuf[l:4], i.ID()) + l += 4 + client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) + l += 4 + client.PutUint32(_reqBuf[l:l+4], id.ID()) + l += 4 + client.PutUint32(_reqBuf[l:l+4], surface.ID()) + l += 4 + client.PutUint32(_reqBuf[l:l+4], seat.ID()) + l += 4 + err := i.Context().WriteMsg(_reqBuf[:], nil) + return id, err +} + +type ZwpKeyboardShortcutsInhibitManagerV1Error uint32 + +// ZwpKeyboardShortcutsInhibitManagerV1Error : +const ( + // ZwpKeyboardShortcutsInhibitManagerV1ErrorAlreadyInhibited : the shortcuts are already inhibited for this surface + ZwpKeyboardShortcutsInhibitManagerV1ErrorAlreadyInhibited ZwpKeyboardShortcutsInhibitManagerV1Error = 0 +) + +func (e ZwpKeyboardShortcutsInhibitManagerV1Error) Name() string { + switch e { + case ZwpKeyboardShortcutsInhibitManagerV1ErrorAlreadyInhibited: + return "already_inhibited" + default: + return "" + } +} + +func (e ZwpKeyboardShortcutsInhibitManagerV1Error) Value() string { + switch e { + case ZwpKeyboardShortcutsInhibitManagerV1ErrorAlreadyInhibited: + return "0" + default: + return "" + } +} + +func (e ZwpKeyboardShortcutsInhibitManagerV1Error) String() string { + return e.Name() + "=" + e.Value() +} + +// ZwpKeyboardShortcutsInhibitorV1InterfaceName is the name of the interface as it appears in the [client.Registry]. +// It can be used to match the [client.RegistryGlobalEvent.Interface] in the +// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. +const ZwpKeyboardShortcutsInhibitorV1InterfaceName = "zwp_keyboard_shortcuts_inhibitor_v1" + +// ZwpKeyboardShortcutsInhibitorV1 : 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. +type ZwpKeyboardShortcutsInhibitorV1 struct { + client.BaseProxy + activeHandler ZwpKeyboardShortcutsInhibitorV1ActiveHandlerFunc + inactiveHandler ZwpKeyboardShortcutsInhibitorV1InactiveHandlerFunc +} + +// NewZwpKeyboardShortcutsInhibitorV1 : 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. +func NewZwpKeyboardShortcutsInhibitorV1(ctx *client.Context) *ZwpKeyboardShortcutsInhibitorV1 { + zwpKeyboardShortcutsInhibitorV1 := &ZwpKeyboardShortcutsInhibitorV1{} + ctx.Register(zwpKeyboardShortcutsInhibitorV1) + return zwpKeyboardShortcutsInhibitorV1 +} + +// Destroy : destroy the keyboard shortcuts inhibitor object +// +// Remove the keyboard shortcuts inhibitor from the associated wl_surface. +func (i *ZwpKeyboardShortcutsInhibitorV1) Destroy() error { + defer i.Context().Unregister(i) + const opcode = 0 + const _reqBufLen = 8 + var _reqBuf [_reqBufLen]byte + l := 0 + client.PutUint32(_reqBuf[l:4], i.ID()) + l += 4 + client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) + l += 4 + err := i.Context().WriteMsg(_reqBuf[:], nil) + return err +} + +// ZwpKeyboardShortcutsInhibitorV1ActiveEvent : 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. +type ZwpKeyboardShortcutsInhibitorV1ActiveEvent struct{} +type ZwpKeyboardShortcutsInhibitorV1ActiveHandlerFunc func(ZwpKeyboardShortcutsInhibitorV1ActiveEvent) + +// SetActiveHandler : sets handler for ZwpKeyboardShortcutsInhibitorV1ActiveEvent +func (i *ZwpKeyboardShortcutsInhibitorV1) SetActiveHandler(f ZwpKeyboardShortcutsInhibitorV1ActiveHandlerFunc) { + i.activeHandler = f +} + +// ZwpKeyboardShortcutsInhibitorV1InactiveEvent : shortcuts are restored +// +// This event indicates that the shortcuts inhibitor is inactive, +// normal shortcuts processing is restored by the compositor. +type ZwpKeyboardShortcutsInhibitorV1InactiveEvent struct{} +type ZwpKeyboardShortcutsInhibitorV1InactiveHandlerFunc func(ZwpKeyboardShortcutsInhibitorV1InactiveEvent) + +// SetInactiveHandler : sets handler for ZwpKeyboardShortcutsInhibitorV1InactiveEvent +func (i *ZwpKeyboardShortcutsInhibitorV1) SetInactiveHandler(f ZwpKeyboardShortcutsInhibitorV1InactiveHandlerFunc) { + i.inactiveHandler = f +} + +func (i *ZwpKeyboardShortcutsInhibitorV1) Dispatch(opcode uint32, fd int, data []byte) { + switch opcode { + case 0: + if i.activeHandler == nil { + return + } + var e ZwpKeyboardShortcutsInhibitorV1ActiveEvent + + i.activeHandler(e) + case 1: + if i.inactiveHandler == nil { + return + } + var e ZwpKeyboardShortcutsInhibitorV1InactiveEvent + + i.inactiveHandler(e) + } +} diff --git a/core/internal/proto/xml/keyboard-shortcuts-inhibit-unstable-v1.xml b/core/internal/proto/xml/keyboard-shortcuts-inhibit-unstable-v1.xml new file mode 100644 index 00000000..27748764 --- /dev/null +++ b/core/internal/proto/xml/keyboard-shortcuts-inhibit-unstable-v1.xml @@ -0,0 +1,143 @@ + + + + + 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. + + + + 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. + + + + + A global interface used for inhibiting the compositor keyboard shortcuts. + + + + + Destroy the keyboard shortcuts inhibitor manager. + + + + + + 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. + + + + + + + + + + + + + + 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. + + + + + Remove the keyboard shortcuts inhibitor from the associated wl_surface. + + + + + + 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. + + + + + + This event indicates that the shortcuts inhibitor is inactive, + normal shortcuts processing is restored by the compositor. + + + +