summaryrefslogtreecommitdiff
path: root/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg')
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/dbusutil/variant.go95
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/dbusutil/variant_test.go155
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/AUTHORS3
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/LICENSE24
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/README.md25
-rwxr-xr-xraveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/generate4
-rwxr-xr-xraveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/generatep9
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/client.go7542
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/common.go55
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/context.go143
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/context_test.go111
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/display.go37
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/doc.go6
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/event.go120
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/request.go44
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/util.go24
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/stable/xdg-shell/xdg_shell.go13
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/CREDITS.MD5
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/LICENSE201
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/adapter-http.go132
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/adapter.go9
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/attribute.go528
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/constants.go450
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/cups-client.go313
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/error.go31
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/ipp-client.go329
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/request.go299
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/response.go383
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/utils.go28
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/syncmap/LICENSE28
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/syncmap/syncmap.go537
31 files changed, 11683 insertions, 0 deletions
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/dbusutil/variant.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/dbusutil/variant.go
new file mode 100644
index 0000000..4080b66
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/dbusutil/variant.go
@@ -0,0 +1,95 @@
+package dbusutil
+
+import "github.com/godbus/dbus/v5"
+
+func As[T any](v dbus.Variant) (T, bool) {
+ val, ok := v.Value().(T)
+ return val, ok
+}
+
+func AsOr[T any](v dbus.Variant, def T) T {
+ if val, ok := v.Value().(T); ok {
+ return val
+ }
+ return def
+}
+
+func Get[T any](m map[string]dbus.Variant, key string) (T, bool) {
+ v, ok := m[key]
+ if !ok {
+ var zero T
+ return zero, false
+ }
+ return As[T](v)
+}
+
+func GetOr[T any](m map[string]dbus.Variant, key string, def T) T {
+ v, ok := m[key]
+ if !ok {
+ return def
+ }
+ return AsOr(v, def)
+}
+
+func Normalize(v any) any {
+ switch val := v.(type) {
+ case dbus.Variant:
+ return Normalize(val.Value())
+ case dbus.ObjectPath:
+ return string(val)
+ case []dbus.ObjectPath:
+ result := make([]string, len(val))
+ for i, p := range val {
+ result[i] = string(p)
+ }
+ return result
+ case map[string]dbus.Variant:
+ result := make(map[string]any)
+ for k, vv := range val {
+ result[k] = Normalize(vv.Value())
+ }
+ return result
+ case map[string]any:
+ result := make(map[string]any)
+ for k, vv := range val {
+ result[k] = Normalize(vv)
+ }
+ return result
+ case map[dbus.ObjectPath]map[string]map[string]dbus.Variant:
+ result := make(map[string]any)
+ for path, ifaces := range val {
+ ifaceMap := make(map[string]any)
+ for ifaceName, props := range ifaces {
+ propMap := make(map[string]any)
+ for propName, propVal := range props {
+ propMap[propName] = Normalize(propVal.Value())
+ }
+ ifaceMap[ifaceName] = propMap
+ }
+ result[string(path)] = ifaceMap
+ }
+ return result
+ case []any:
+ result := make([]any, len(val))
+ for i, item := range val {
+ result[i] = Normalize(item)
+ }
+ return result
+ case []dbus.Variant:
+ result := make([]any, len(val))
+ for i, item := range val {
+ result[i] = Normalize(item.Value())
+ }
+ return result
+ default:
+ return v
+ }
+}
+
+func NormalizeSlice(values []any) []any {
+ result := make([]any, len(values))
+ for i, v := range values {
+ result[i] = Normalize(v)
+ }
+ return result
+}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/dbusutil/variant_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/dbusutil/variant_test.go
new file mode 100644
index 0000000..a927e54
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/dbusutil/variant_test.go
@@ -0,0 +1,155 @@
+package dbusutil
+
+import (
+ "testing"
+
+ "github.com/godbus/dbus/v5"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestAs(t *testing.T) {
+ t.Run("string", func(t *testing.T) {
+ v := dbus.MakeVariant("hello")
+ val, ok := As[string](v)
+ assert.True(t, ok)
+ assert.Equal(t, "hello", val)
+ })
+
+ t.Run("bool", func(t *testing.T) {
+ v := dbus.MakeVariant(true)
+ val, ok := As[bool](v)
+ assert.True(t, ok)
+ assert.True(t, val)
+ })
+
+ t.Run("int32", func(t *testing.T) {
+ v := dbus.MakeVariant(int32(42))
+ val, ok := As[int32](v)
+ assert.True(t, ok)
+ assert.Equal(t, int32(42), val)
+ })
+
+ t.Run("wrong type", func(t *testing.T) {
+ v := dbus.MakeVariant("hello")
+ _, ok := As[int](v)
+ assert.False(t, ok)
+ })
+}
+
+func TestAsOr(t *testing.T) {
+ t.Run("exists", func(t *testing.T) {
+ v := dbus.MakeVariant("hello")
+ val := AsOr(v, "default")
+ assert.Equal(t, "hello", val)
+ })
+
+ t.Run("wrong type uses default", func(t *testing.T) {
+ v := dbus.MakeVariant(123)
+ val := AsOr(v, "default")
+ assert.Equal(t, "default", val)
+ })
+}
+
+func TestGet(t *testing.T) {
+ m := map[string]dbus.Variant{
+ "name": dbus.MakeVariant("test"),
+ "enabled": dbus.MakeVariant(true),
+ "count": dbus.MakeVariant(int32(5)),
+ }
+
+ t.Run("exists", func(t *testing.T) {
+ val, ok := Get[string](m, "name")
+ assert.True(t, ok)
+ assert.Equal(t, "test", val)
+ })
+
+ t.Run("missing key", func(t *testing.T) {
+ _, ok := Get[string](m, "missing")
+ assert.False(t, ok)
+ })
+
+ t.Run("wrong type", func(t *testing.T) {
+ _, ok := Get[int](m, "name")
+ assert.False(t, ok)
+ })
+}
+
+func TestGetOr(t *testing.T) {
+ m := map[string]dbus.Variant{
+ "name": dbus.MakeVariant("test"),
+ }
+
+ t.Run("exists", func(t *testing.T) {
+ val := GetOr(m, "name", "default")
+ assert.Equal(t, "test", val)
+ })
+
+ t.Run("missing uses default", func(t *testing.T) {
+ val := GetOr(m, "missing", "default")
+ assert.Equal(t, "default", val)
+ })
+
+ t.Run("wrong type uses default", func(t *testing.T) {
+ val := GetOr(m, "name", 42)
+ assert.Equal(t, 42, val)
+ })
+}
+
+func TestNormalize(t *testing.T) {
+ t.Run("variant unwrap", func(t *testing.T) {
+ v := dbus.MakeVariant("hello")
+ result := Normalize(v)
+ assert.Equal(t, "hello", result)
+ })
+
+ t.Run("nested variant", func(t *testing.T) {
+ v := dbus.MakeVariant(dbus.MakeVariant("nested"))
+ result := Normalize(v)
+ assert.Equal(t, "nested", result)
+ })
+
+ t.Run("object path", func(t *testing.T) {
+ v := dbus.ObjectPath("/org/test")
+ result := Normalize(v)
+ assert.Equal(t, "/org/test", result)
+ })
+
+ t.Run("object path slice", func(t *testing.T) {
+ v := []dbus.ObjectPath{"/org/a", "/org/b"}
+ result := Normalize(v)
+ assert.Equal(t, []string{"/org/a", "/org/b"}, result)
+ })
+
+ t.Run("variant map", func(t *testing.T) {
+ v := map[string]dbus.Variant{
+ "key": dbus.MakeVariant("value"),
+ }
+ result := Normalize(v)
+ expected := map[string]any{"key": "value"}
+ assert.Equal(t, expected, result)
+ })
+
+ t.Run("any slice", func(t *testing.T) {
+ v := []any{dbus.MakeVariant("a"), dbus.ObjectPath("/b")}
+ result := Normalize(v)
+ expected := []any{"a", "/b"}
+ assert.Equal(t, expected, result)
+ })
+
+ t.Run("passthrough primitives", func(t *testing.T) {
+ assert.Equal(t, "hello", Normalize("hello"))
+ assert.Equal(t, 42, Normalize(42))
+ assert.Equal(t, true, Normalize(true))
+ })
+}
+
+func TestNormalizeSlice(t *testing.T) {
+ input := []any{
+ dbus.MakeVariant("a"),
+ dbus.ObjectPath("/b"),
+ "c",
+ }
+ result := NormalizeSlice(input)
+ expected := []any{"a", "/b", "c"}
+ assert.Equal(t, expected, result)
+}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/AUTHORS b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/AUTHORS
new file mode 100644
index 0000000..7b24eab
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/AUTHORS
@@ -0,0 +1,3 @@
+// Keep this sorted
+
+rajveermalviya
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/LICENSE b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/LICENSE
new file mode 100644
index 0000000..4da3300
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/LICENSE
@@ -0,0 +1,24 @@
+Copyright 2021 go-wayland authors
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/README.md b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/README.md
new file mode 100644
index 0000000..9e52d99
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/README.md
@@ -0,0 +1,25 @@
+# Wayland implementation in Go
+
+[![Go Reference](https://pkg.go.dev/badge/github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland.svg)](https://pkg.go.dev/github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland)
+
+This module contains pure Go implementation of the Wayland protocol.
+Currently only wayland-client functionality is supported.
+
+Go code is generated from protocol XML files using
+[`go-wayland-scanner`](cmd/go-wayland-scanner/scanner.go).
+
+To load cursor, minimal port of `wayland-cursor` & `xcursor` in pure Go
+is located at [`wayland/cursor`](wayland/cursor) & [`wayland/cursor/xcursor`](wayland/cursor/xcursor)
+respectively.
+
+To demonstrate the functionality of this module
+[`examples/imageviewer`](examples/imageviewer) contains a simple image
+viewer. It demos displaying a top-level window, resizing of window,
+cursor themes, pointer and keyboard. Because it's in pure Go, it can be
+compiled without CGO. You can try it using the following commands:
+
+```sh
+CGO_ENABLED=0 go install github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/examples/imageviewer@latest
+
+imageviewer file.jpg
+```
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/generate b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/generate
new file mode 100755
index 0000000..02ad975
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/generate
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd ./wayland
+go generate -x ./...
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/generatep b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/generatep
new file mode 100755
index 0000000..bd660d7
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/generatep
@@ -0,0 +1,9 @@
+#!/usr/bin/env sh
+
+# Runs go generate for each directory, but in parallel. Any arguments are appended to the
+# go generate command.
+# Usage: $ ./generatep [go generate arguments]
+# Print all generate commands: $ ./generatep -x
+
+cd ./wayland || exit 1
+find . -type f -name '*.go' -exec dirname {} \; | sort -u | parallel -j 0 go generate "$1" {}/.
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/client.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/client.go
new file mode 100644
index 0000000..fd49c20
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/client.go
@@ -0,0 +1,7542 @@
+// Generated by go-wayland-scanner
+// https://github.com/yaslama/go-wayland/cmd/go-wayland-scanner
+// XML file : https://gitlab.freedesktop.org/wayland/wayland/-/raw/1.24.0/protocol/wayland.xml?ref_type=tags
+//
+// wayland Protocol Copyright:
+//
+// Copyright © 2008-2011 Kristian Høgsberg
+// Copyright © 2010-2011 Intel Corporation
+// Copyright © 2012-2013 Collabora, Ltd.
+//
+// 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 client
+
+import "golang.org/x/sys/unix"
+
+// DisplayInterfaceName 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 DisplayInterfaceName = "wl_display"
+
+// Display : core global object
+//
+// The core global object. This is a special singleton object. It
+// is used for internal Wayland protocol features.
+type Display struct {
+ BaseProxy
+ errorHandler DisplayErrorHandlerFunc
+ deleteIdHandler DisplayDeleteIdHandlerFunc
+}
+
+// NewDisplay : core global object
+//
+// The core global object. This is a special singleton object. It
+// is used for internal Wayland protocol features.
+func NewDisplay(ctx *Context) *Display {
+ wlDisplay := &Display{}
+ ctx.Register(wlDisplay)
+ return wlDisplay
+}
+
+// Sync : asynchronous roundtrip
+//
+// The sync request asks the server to emit the 'done' event
+// on the returned wl_callback object. Since requests are
+// handled in-order and events are delivered in-order, this can
+// be used as a barrier to ensure all previous requests and the
+// resulting events have been handled.
+//
+// The object returned by this request will be destroyed by the
+// compositor after the callback is fired and as such the client must not
+// attempt to use it after that point.
+//
+// The callback_data passed in the callback is undefined and should be ignored.
+func (i *Display) Sync() (*Callback, error) {
+ callback := NewCallback(i.Context())
+ const opcode = 0
+ const _reqBufLen = 8 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], callback.ID())
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return callback, err
+}
+
+// GetRegistry : get global registry object
+//
+// This request creates a registry object that allows the client
+// to list and bind the global objects available from the
+// compositor.
+//
+// It should be noted that the server side resources consumed in
+// response to a get_registry request can only be released when the
+// client disconnects, not when the client side proxy is destroyed.
+// Therefore, clients should invoke get_registry as infrequently as
+// possible to avoid wasting memory.
+func (i *Display) GetRegistry() (*Registry, error) {
+ registry := NewRegistry(i.Context())
+ const opcode = 1
+ const _reqBufLen = 8 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], registry.ID())
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return registry, err
+}
+
+func (i *Display) Destroy() error {
+ i.MarkZombie()
+ return nil
+}
+
+type DisplayError uint32
+
+// DisplayError : global error values
+//
+// These errors are global and can be emitted in response to any
+// server request.
+const (
+ // DisplayErrorInvalidObject : server couldn't find object
+ DisplayErrorInvalidObject DisplayError = 0
+ // DisplayErrorInvalidMethod : method doesn't exist on the specified interface or malformed request
+ DisplayErrorInvalidMethod DisplayError = 1
+ // DisplayErrorNoMemory : server is out of memory
+ DisplayErrorNoMemory DisplayError = 2
+ // DisplayErrorImplementation : implementation error in compositor
+ DisplayErrorImplementation DisplayError = 3
+)
+
+func (e DisplayError) Name() string {
+ switch e {
+ case DisplayErrorInvalidObject:
+ return "invalid_object"
+ case DisplayErrorInvalidMethod:
+ return "invalid_method"
+ case DisplayErrorNoMemory:
+ return "no_memory"
+ case DisplayErrorImplementation:
+ return "implementation"
+ default:
+ return ""
+ }
+}
+
+func (e DisplayError) Value() string {
+ switch e {
+ case DisplayErrorInvalidObject:
+ return "0"
+ case DisplayErrorInvalidMethod:
+ return "1"
+ case DisplayErrorNoMemory:
+ return "2"
+ case DisplayErrorImplementation:
+ return "3"
+ default:
+ return ""
+ }
+}
+
+func (e DisplayError) String() string {
+ return e.Name() + "=" + e.Value()
+}
+
+// DisplayErrorEvent : fatal error event
+//
+// The error event is sent out when a fatal (non-recoverable)
+// error has occurred. The object_id argument is the object
+// where the error occurred, most often in response to a request
+// to that object. The code identifies the error and is defined
+// by the object interface. As such, each interface defines its
+// own set of error codes. The message is a brief description
+// of the error, for (debugging) convenience.
+type DisplayErrorEvent struct {
+ ObjectId Proxy
+ Code uint32
+ Message string
+}
+type DisplayErrorHandlerFunc func(DisplayErrorEvent)
+
+// SetErrorHandler : sets handler for DisplayErrorEvent
+func (i *Display) SetErrorHandler(f DisplayErrorHandlerFunc) {
+ i.errorHandler = f
+}
+
+// DisplayDeleteIdEvent : acknowledge object ID deletion
+//
+// This event is used internally by the object ID management
+// logic. When a client deletes an object that it had created,
+// the server will send this event to acknowledge that it has
+// seen the delete request. When the client receives this event,
+// it will know that it can safely reuse the object ID.
+type DisplayDeleteIdEvent struct {
+ Id uint32
+}
+type DisplayDeleteIdHandlerFunc func(DisplayDeleteIdEvent)
+
+// SetDeleteIdHandler : sets handler for DisplayDeleteIdEvent
+func (i *Display) SetDeleteIdHandler(f DisplayDeleteIdHandlerFunc) {
+ i.deleteIdHandler = f
+}
+
+func (i *Display) Dispatch(opcode uint32, fd int, data []byte) {
+ switch opcode {
+ case 0:
+ if i.errorHandler == nil {
+ return
+ }
+ var e DisplayErrorEvent
+ l := 0
+ e.ObjectId = i.Context().GetProxy(Uint32(data[l : l+4]))
+ l += 4
+ e.Code = Uint32(data[l : l+4])
+ l += 4
+ messageLen := PaddedLen(int(Uint32(data[l : l+4])))
+ l += 4
+ e.Message = String(data[l : l+messageLen])
+
+ i.errorHandler(e)
+ case 1:
+ var e DisplayDeleteIdEvent
+ l := 0
+ e.Id = Uint32(data[l : l+4])
+ l += 4
+
+ i.Context().DeleteID(e.Id)
+
+ if i.deleteIdHandler != nil {
+ i.deleteIdHandler(e)
+ }
+ }
+}
+
+// RegistryInterfaceName 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 RegistryInterfaceName = "wl_registry"
+
+// Registry : global registry object
+//
+// The singleton global registry object. The server has a number of
+// global objects that are available to all clients. These objects
+// typically represent an actual object in the server (for example,
+// an input device) or they are singleton objects that provide
+// extension functionality.
+//
+// When a client creates a registry object, the registry object
+// will emit a global event for each global currently in the
+// registry. Globals come and go as a result of device or
+// monitor hotplugs, reconfiguration or other events, and the
+// registry will send out global and global_remove events to
+// keep the client up to date with the changes. To mark the end
+// of the initial burst of events, the client can use the
+// wl_display.sync request immediately after calling
+// wl_display.get_registry.
+//
+// A client can bind to a global object by using the bind
+// request. This creates a client-side handle that lets the object
+// emit events to the client and lets the client invoke requests on
+// the object.
+type Registry struct {
+ BaseProxy
+ globalHandler RegistryGlobalHandlerFunc
+ globalRemoveHandler RegistryGlobalRemoveHandlerFunc
+}
+
+// NewRegistry : global registry object
+//
+// The singleton global registry object. The server has a number of
+// global objects that are available to all clients. These objects
+// typically represent an actual object in the server (for example,
+// an input device) or they are singleton objects that provide
+// extension functionality.
+//
+// When a client creates a registry object, the registry object
+// will emit a global event for each global currently in the
+// registry. Globals come and go as a result of device or
+// monitor hotplugs, reconfiguration or other events, and the
+// registry will send out global and global_remove events to
+// keep the client up to date with the changes. To mark the end
+// of the initial burst of events, the client can use the
+// wl_display.sync request immediately after calling
+// wl_display.get_registry.
+//
+// A client can bind to a global object by using the bind
+// request. This creates a client-side handle that lets the object
+// emit events to the client and lets the client invoke requests on
+// the object.
+func NewRegistry(ctx *Context) *Registry {
+ wlRegistry := &Registry{}
+ ctx.Register(wlRegistry)
+ return wlRegistry
+}
+
+// Bind : bind an object to the display
+//
+// Binds a new, client-created object to the server using the
+// specified name as the identifier.
+//
+// name: unique numeric name of the object
+func (i *Registry) Bind(name uint32, iface string, version uint32, id Proxy) error {
+ const opcode = 0
+ ifaceLen := PaddedLen(len(iface) + 1)
+ _reqBufLen := 8 + 4 + (4 + ifaceLen) + 4 + 4
+ _reqBuf := make([]byte, _reqBufLen)
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(name))
+ l += 4
+ PutString(_reqBuf[l:l+(4+ifaceLen)], iface)
+ l += (4 + ifaceLen)
+ PutUint32(_reqBuf[l:l+4], uint32(version))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], id.ID())
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf, nil)
+ return err
+}
+
+func (i *Registry) Destroy() error {
+ i.MarkZombie()
+ return nil
+}
+
+// RegistryGlobalEvent : announce global object
+//
+// Notify the client of global objects.
+//
+// The event notifies the client that a global object with
+// the given name is now available, and it implements the
+// given version of the given interface.
+type RegistryGlobalEvent struct {
+ Name uint32
+ Interface string
+ Version uint32
+}
+type RegistryGlobalHandlerFunc func(RegistryGlobalEvent)
+
+// SetGlobalHandler : sets handler for RegistryGlobalEvent
+func (i *Registry) SetGlobalHandler(f RegistryGlobalHandlerFunc) {
+ i.globalHandler = f
+}
+
+// RegistryGlobalRemoveEvent : announce removal of global object
+//
+// Notify the client of removed global objects.
+//
+// This event notifies the client that the global identified
+// by name is no longer available. If the client bound to
+// the global using the bind request, the client should now
+// destroy that object.
+//
+// The object remains valid and requests to the object will be
+// ignored until the client destroys it, to avoid races between
+// the global going away and a client sending a request to it.
+type RegistryGlobalRemoveEvent struct {
+ Name uint32
+}
+type RegistryGlobalRemoveHandlerFunc func(RegistryGlobalRemoveEvent)
+
+// SetGlobalRemoveHandler : sets handler for RegistryGlobalRemoveEvent
+func (i *Registry) SetGlobalRemoveHandler(f RegistryGlobalRemoveHandlerFunc) {
+ i.globalRemoveHandler = f
+}
+
+func (i *Registry) Dispatch(opcode uint32, fd int, data []byte) {
+ switch opcode {
+ case 0:
+ if i.globalHandler == nil {
+ return
+ }
+ var e RegistryGlobalEvent
+ l := 0
+ e.Name = Uint32(data[l : l+4])
+ l += 4
+ interfaceLen := PaddedLen(int(Uint32(data[l : l+4])))
+ l += 4
+ e.Interface = String(data[l : l+interfaceLen])
+ l += interfaceLen
+ e.Version = Uint32(data[l : l+4])
+ l += 4
+
+ i.globalHandler(e)
+ case 1:
+ if i.globalRemoveHandler == nil {
+ return
+ }
+ var e RegistryGlobalRemoveEvent
+ l := 0
+ e.Name = Uint32(data[l : l+4])
+ l += 4
+
+ i.globalRemoveHandler(e)
+ }
+}
+
+// CallbackInterfaceName 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 CallbackInterfaceName = "wl_callback"
+
+// Callback : callback object
+//
+// Clients can handle the 'done' event to get notified when
+// the related request is done.
+//
+// Note, because wl_callback objects are created from multiple independent
+// factory interfaces, the wl_callback interface is frozen at version 1.
+type Callback struct {
+ BaseProxy
+ doneHandler CallbackDoneHandlerFunc
+}
+
+// NewCallback : callback object
+//
+// Clients can handle the 'done' event to get notified when
+// the related request is done.
+//
+// Note, because wl_callback objects are created from multiple independent
+// factory interfaces, the wl_callback interface is frozen at version 1.
+func NewCallback(ctx *Context) *Callback {
+ wlCallback := &Callback{}
+ ctx.Register(wlCallback)
+ return wlCallback
+}
+
+func (i *Callback) Destroy() error {
+ i.MarkZombie()
+ return nil
+}
+
+// CallbackDoneEvent : done event
+//
+// Notify the client when the related request is done.
+type CallbackDoneEvent struct {
+ CallbackData uint32
+}
+type CallbackDoneHandlerFunc func(CallbackDoneEvent)
+
+// SetDoneHandler : sets handler for CallbackDoneEvent
+func (i *Callback) SetDoneHandler(f CallbackDoneHandlerFunc) {
+ i.doneHandler = f
+}
+
+func (i *Callback) Dispatch(opcode uint32, fd int, data []byte) {
+ switch opcode {
+ case 0:
+ if i.doneHandler == nil {
+ return
+ }
+ var e CallbackDoneEvent
+ l := 0
+ e.CallbackData = Uint32(data[l : l+4])
+ l += 4
+
+ i.doneHandler(e)
+ }
+}
+
+// CompositorInterfaceName 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 CompositorInterfaceName = "wl_compositor"
+
+// Compositor : the compositor singleton
+//
+// A compositor. This object is a singleton global. The
+// compositor is in charge of combining the contents of multiple
+// surfaces into one displayable output.
+type Compositor struct {
+ BaseProxy
+}
+
+// NewCompositor : the compositor singleton
+//
+// A compositor. This object is a singleton global. The
+// compositor is in charge of combining the contents of multiple
+// surfaces into one displayable output.
+func NewCompositor(ctx *Context) *Compositor {
+ wlCompositor := &Compositor{}
+ ctx.Register(wlCompositor)
+ return wlCompositor
+}
+
+// CreateSurface : create new surface
+//
+// Ask the compositor to create a new surface.
+func (i *Compositor) CreateSurface() (*Surface, error) {
+ id := NewSurface(i.Context())
+ const opcode = 0
+ const _reqBufLen = 8 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], id.ID())
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return id, err
+}
+
+// CreateRegion : create new region
+//
+// Ask the compositor to create a new region.
+func (i *Compositor) CreateRegion() (*Region, error) {
+ id := NewRegion(i.Context())
+ const opcode = 1
+ const _reqBufLen = 8 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], id.ID())
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return id, err
+}
+
+func (i *Compositor) Destroy() error {
+ i.MarkZombie()
+ return nil
+}
+
+// ShmPoolInterfaceName 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 ShmPoolInterfaceName = "wl_shm_pool"
+
+// ShmPool : a shared memory pool
+//
+// The wl_shm_pool object encapsulates a piece of memory shared
+// between the compositor and client. Through the wl_shm_pool
+// object, the client can allocate shared memory wl_buffer objects.
+// All objects created through the same pool share the same
+// underlying mapped memory. Reusing the mapped memory avoids the
+// setup/teardown overhead and is useful when interactively resizing
+// a surface or for many small buffers.
+type ShmPool struct {
+ BaseProxy
+}
+
+// NewShmPool : a shared memory pool
+//
+// The wl_shm_pool object encapsulates a piece of memory shared
+// between the compositor and client. Through the wl_shm_pool
+// object, the client can allocate shared memory wl_buffer objects.
+// All objects created through the same pool share the same
+// underlying mapped memory. Reusing the mapped memory avoids the
+// setup/teardown overhead and is useful when interactively resizing
+// a surface or for many small buffers.
+func NewShmPool(ctx *Context) *ShmPool {
+ wlShmPool := &ShmPool{}
+ ctx.Register(wlShmPool)
+ return wlShmPool
+}
+
+// CreateBuffer : create a buffer from the pool
+//
+// Create a wl_buffer object from the pool.
+//
+// The buffer is created offset bytes into the pool and has
+// width and height as specified. The stride argument specifies
+// the number of bytes from the beginning of one row to the beginning
+// of the next. The format is the pixel format of the buffer and
+// must be one of those advertised through the wl_shm.format event.
+//
+// A buffer will keep a reference to the pool it was created from
+// so it is valid to destroy the pool immediately after creating
+// a buffer from it.
+//
+// offset: buffer byte offset within the pool
+// width: buffer width, in pixels
+// height: buffer height, in pixels
+// stride: number of bytes from the beginning of one row to the beginning of the next row
+// format: buffer pixel format
+func (i *ShmPool) CreateBuffer(offset, width, height, stride int32, format uint32) (*Buffer, error) {
+ id := NewBuffer(i.Context())
+ const opcode = 0
+ const _reqBufLen = 8 + 4 + 4 + 4 + 4 + 4 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], id.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(offset))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(width))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(height))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(stride))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(format))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return id, err
+}
+
+// Destroy : destroy the pool
+//
+// Destroy the shared memory pool.
+//
+// The mmapped memory will be released when all
+// buffers that have been created from this pool
+// are gone.
+func (i *ShmPool) Destroy() error {
+ defer i.MarkZombie()
+ const opcode = 1
+ const _reqBufLen = 8
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// Resize : change the size of the pool mapping
+//
+// This request will cause the server to remap the backing memory
+// for the pool from the file descriptor passed when the pool was
+// created, but using the new size. This request can only be
+// used to make the pool bigger.
+//
+// This request only changes the amount of bytes that are mmapped
+// by the server and does not touch the file corresponding to the
+// file descriptor passed at creation time. It is the client's
+// responsibility to ensure that the file is at least as big as
+// the new pool size.
+//
+// size: new size of the pool, in bytes
+func (i *ShmPool) Resize(size int32) error {
+ const opcode = 2
+ const _reqBufLen = 8 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(size))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// ShmInterfaceName 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 ShmInterfaceName = "wl_shm"
+
+// Shm : shared memory support
+//
+// A singleton global object that provides support for shared
+// memory.
+//
+// Clients can create wl_shm_pool objects using the create_pool
+// request.
+//
+// On binding the wl_shm object one or more format events
+// are emitted to inform clients about the valid pixel formats
+// that can be used for buffers.
+type Shm struct {
+ BaseProxy
+ formatHandler ShmFormatHandlerFunc
+}
+
+// NewShm : shared memory support
+//
+// A singleton global object that provides support for shared
+// memory.
+//
+// Clients can create wl_shm_pool objects using the create_pool
+// request.
+//
+// On binding the wl_shm object one or more format events
+// are emitted to inform clients about the valid pixel formats
+// that can be used for buffers.
+func NewShm(ctx *Context) *Shm {
+ wlShm := &Shm{}
+ ctx.Register(wlShm)
+ return wlShm
+}
+
+// CreatePool : create a shm pool
+//
+// Create a new wl_shm_pool object.
+//
+// The pool can be used to create shared memory based buffer
+// objects. The server will mmap size bytes of the passed file
+// descriptor, to use as backing memory for the pool.
+//
+// fd: file descriptor for the pool
+// size: pool size, in bytes
+func (i *Shm) CreatePool(fd int, size int32) (*ShmPool, error) {
+ id := NewShmPool(i.Context())
+ const opcode = 0
+ const _reqBufLen = 8 + 4 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], id.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(size))
+ l += 4
+ oob := unix.UnixRights(int(fd))
+ err := i.Context().WriteMsg(_reqBuf[:], oob)
+ return id, err
+}
+
+// Release : release the shm object
+//
+// Using this request a client can tell the server that it is not going to
+// use the shm object anymore.
+//
+// Objects created via this interface remain unaffected.
+func (i *Shm) Release() error {
+ defer i.MarkZombie()
+ const opcode = 1
+ const _reqBufLen = 8
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+type ShmError uint32
+
+// ShmError : wl_shm error values
+//
+// These errors can be emitted in response to wl_shm requests.
+const (
+ // ShmErrorInvalidFormat : buffer format is not known
+ ShmErrorInvalidFormat ShmError = 0
+ // ShmErrorInvalidStride : invalid size or stride during pool or buffer creation
+ ShmErrorInvalidStride ShmError = 1
+ // ShmErrorInvalidFd : mmapping the file descriptor failed
+ ShmErrorInvalidFd ShmError = 2
+)
+
+func (e ShmError) Name() string {
+ switch e {
+ case ShmErrorInvalidFormat:
+ return "invalid_format"
+ case ShmErrorInvalidStride:
+ return "invalid_stride"
+ case ShmErrorInvalidFd:
+ return "invalid_fd"
+ default:
+ return ""
+ }
+}
+
+func (e ShmError) Value() string {
+ switch e {
+ case ShmErrorInvalidFormat:
+ return "0"
+ case ShmErrorInvalidStride:
+ return "1"
+ case ShmErrorInvalidFd:
+ return "2"
+ default:
+ return ""
+ }
+}
+
+func (e ShmError) String() string {
+ return e.Name() + "=" + e.Value()
+}
+
+type ShmFormat uint32
+
+// ShmFormat : pixel formats
+//
+// This describes the memory layout of an individual pixel.
+//
+// All renderers should support argb8888 and xrgb8888 but any other
+// formats are optional and may not be supported by the particular
+// renderer in use.
+//
+// The drm format codes match the macros defined in drm_fourcc.h, except
+// argb8888 and xrgb8888. The formats actually supported by the compositor
+// will be reported by the format event.
+//
+// For all wl_shm formats and unless specified in another protocol
+// extension, pre-multiplied alpha is used for pixel values.
+const (
+ // ShmFormatArgb8888 : 32-bit ARGB format, [31:0] A:R:G:B 8:8:8:8 little endian
+ ShmFormatArgb8888 ShmFormat = 0
+ // ShmFormatXrgb8888 : 32-bit RGB format, [31:0] x:R:G:B 8:8:8:8 little endian
+ ShmFormatXrgb8888 ShmFormat = 1
+ // ShmFormatC8 : 8-bit color index format, [7:0] C
+ ShmFormatC8 ShmFormat = 0x20203843
+ // ShmFormatRgb332 : 8-bit RGB format, [7:0] R:G:B 3:3:2
+ ShmFormatRgb332 ShmFormat = 0x38424752
+ // ShmFormatBgr233 : 8-bit BGR format, [7:0] B:G:R 2:3:3
+ ShmFormatBgr233 ShmFormat = 0x38524742
+ // ShmFormatXrgb4444 : 16-bit xRGB format, [15:0] x:R:G:B 4:4:4:4 little endian
+ ShmFormatXrgb4444 ShmFormat = 0x32315258
+ // ShmFormatXbgr4444 : 16-bit xBGR format, [15:0] x:B:G:R 4:4:4:4 little endian
+ ShmFormatXbgr4444 ShmFormat = 0x32314258
+ // ShmFormatRgbx4444 : 16-bit RGBx format, [15:0] R:G:B:x 4:4:4:4 little endian
+ ShmFormatRgbx4444 ShmFormat = 0x32315852
+ // ShmFormatBgrx4444 : 16-bit BGRx format, [15:0] B:G:R:x 4:4:4:4 little endian
+ ShmFormatBgrx4444 ShmFormat = 0x32315842
+ // ShmFormatArgb4444 : 16-bit ARGB format, [15:0] A:R:G:B 4:4:4:4 little endian
+ ShmFormatArgb4444 ShmFormat = 0x32315241
+ // ShmFormatAbgr4444 : 16-bit ABGR format, [15:0] A:B:G:R 4:4:4:4 little endian
+ ShmFormatAbgr4444 ShmFormat = 0x32314241
+ // ShmFormatRgba4444 : 16-bit RBGA format, [15:0] R:G:B:A 4:4:4:4 little endian
+ ShmFormatRgba4444 ShmFormat = 0x32314152
+ // ShmFormatBgra4444 : 16-bit BGRA format, [15:0] B:G:R:A 4:4:4:4 little endian
+ ShmFormatBgra4444 ShmFormat = 0x32314142
+ // ShmFormatXrgb1555 : 16-bit xRGB format, [15:0] x:R:G:B 1:5:5:5 little endian
+ ShmFormatXrgb1555 ShmFormat = 0x35315258
+ // ShmFormatXbgr1555 : 16-bit xBGR 1555 format, [15:0] x:B:G:R 1:5:5:5 little endian
+ ShmFormatXbgr1555 ShmFormat = 0x35314258
+ // ShmFormatRgbx5551 : 16-bit RGBx 5551 format, [15:0] R:G:B:x 5:5:5:1 little endian
+ ShmFormatRgbx5551 ShmFormat = 0x35315852
+ // ShmFormatBgrx5551 : 16-bit BGRx 5551 format, [15:0] B:G:R:x 5:5:5:1 little endian
+ ShmFormatBgrx5551 ShmFormat = 0x35315842
+ // ShmFormatArgb1555 : 16-bit ARGB 1555 format, [15:0] A:R:G:B 1:5:5:5 little endian
+ ShmFormatArgb1555 ShmFormat = 0x35315241
+ // ShmFormatAbgr1555 : 16-bit ABGR 1555 format, [15:0] A:B:G:R 1:5:5:5 little endian
+ ShmFormatAbgr1555 ShmFormat = 0x35314241
+ // ShmFormatRgba5551 : 16-bit RGBA 5551 format, [15:0] R:G:B:A 5:5:5:1 little endian
+ ShmFormatRgba5551 ShmFormat = 0x35314152
+ // ShmFormatBgra5551 : 16-bit BGRA 5551 format, [15:0] B:G:R:A 5:5:5:1 little endian
+ ShmFormatBgra5551 ShmFormat = 0x35314142
+ // ShmFormatRgb565 : 16-bit RGB 565 format, [15:0] R:G:B 5:6:5 little endian
+ ShmFormatRgb565 ShmFormat = 0x36314752
+ // ShmFormatBgr565 : 16-bit BGR 565 format, [15:0] B:G:R 5:6:5 little endian
+ ShmFormatBgr565 ShmFormat = 0x36314742
+ // ShmFormatRgb888 : 24-bit RGB format, [23:0] R:G:B little endian
+ ShmFormatRgb888 ShmFormat = 0x34324752
+ // ShmFormatBgr888 : 24-bit BGR format, [23:0] B:G:R little endian
+ ShmFormatBgr888 ShmFormat = 0x34324742
+ // ShmFormatXbgr8888 : 32-bit xBGR format, [31:0] x:B:G:R 8:8:8:8 little endian
+ ShmFormatXbgr8888 ShmFormat = 0x34324258
+ // ShmFormatRgbx8888 : 32-bit RGBx format, [31:0] R:G:B:x 8:8:8:8 little endian
+ ShmFormatRgbx8888 ShmFormat = 0x34325852
+ // ShmFormatBgrx8888 : 32-bit BGRx format, [31:0] B:G:R:x 8:8:8:8 little endian
+ ShmFormatBgrx8888 ShmFormat = 0x34325842
+ // ShmFormatAbgr8888 : 32-bit ABGR format, [31:0] A:B:G:R 8:8:8:8 little endian
+ ShmFormatAbgr8888 ShmFormat = 0x34324241
+ // ShmFormatRgba8888 : 32-bit RGBA format, [31:0] R:G:B:A 8:8:8:8 little endian
+ ShmFormatRgba8888 ShmFormat = 0x34324152
+ // ShmFormatBgra8888 : 32-bit BGRA format, [31:0] B:G:R:A 8:8:8:8 little endian
+ ShmFormatBgra8888 ShmFormat = 0x34324142
+ // ShmFormatXrgb2101010 : 32-bit xRGB format, [31:0] x:R:G:B 2:10:10:10 little endian
+ ShmFormatXrgb2101010 ShmFormat = 0x30335258
+ // ShmFormatXbgr2101010 : 32-bit xBGR format, [31:0] x:B:G:R 2:10:10:10 little endian
+ ShmFormatXbgr2101010 ShmFormat = 0x30334258
+ // ShmFormatRgbx1010102 : 32-bit RGBx format, [31:0] R:G:B:x 10:10:10:2 little endian
+ ShmFormatRgbx1010102 ShmFormat = 0x30335852
+ // ShmFormatBgrx1010102 : 32-bit BGRx format, [31:0] B:G:R:x 10:10:10:2 little endian
+ ShmFormatBgrx1010102 ShmFormat = 0x30335842
+ // ShmFormatArgb2101010 : 32-bit ARGB format, [31:0] A:R:G:B 2:10:10:10 little endian
+ ShmFormatArgb2101010 ShmFormat = 0x30335241
+ // ShmFormatAbgr2101010 : 32-bit ABGR format, [31:0] A:B:G:R 2:10:10:10 little endian
+ ShmFormatAbgr2101010 ShmFormat = 0x30334241
+ // ShmFormatRgba1010102 : 32-bit RGBA format, [31:0] R:G:B:A 10:10:10:2 little endian
+ ShmFormatRgba1010102 ShmFormat = 0x30334152
+ // ShmFormatBgra1010102 : 32-bit BGRA format, [31:0] B:G:R:A 10:10:10:2 little endian
+ ShmFormatBgra1010102 ShmFormat = 0x30334142
+ // ShmFormatYuyv : packed YCbCr format, [31:0] Cr0:Y1:Cb0:Y0 8:8:8:8 little endian
+ ShmFormatYuyv ShmFormat = 0x56595559
+ // ShmFormatYvyu : packed YCbCr format, [31:0] Cb0:Y1:Cr0:Y0 8:8:8:8 little endian
+ ShmFormatYvyu ShmFormat = 0x55595659
+ // ShmFormatUyvy : packed YCbCr format, [31:0] Y1:Cr0:Y0:Cb0 8:8:8:8 little endian
+ ShmFormatUyvy ShmFormat = 0x59565955
+ // ShmFormatVyuy : packed YCbCr format, [31:0] Y1:Cb0:Y0:Cr0 8:8:8:8 little endian
+ ShmFormatVyuy ShmFormat = 0x59555956
+ // ShmFormatAyuv : packed AYCbCr format, [31:0] A:Y:Cb:Cr 8:8:8:8 little endian
+ ShmFormatAyuv ShmFormat = 0x56555941
+ // ShmFormatNv12 : 2 plane YCbCr Cr:Cb format, 2x2 subsampled Cr:Cb plane
+ ShmFormatNv12 ShmFormat = 0x3231564e
+ // ShmFormatNv21 : 2 plane YCbCr Cb:Cr format, 2x2 subsampled Cb:Cr plane
+ ShmFormatNv21 ShmFormat = 0x3132564e
+ // ShmFormatNv16 : 2 plane YCbCr Cr:Cb format, 2x1 subsampled Cr:Cb plane
+ ShmFormatNv16 ShmFormat = 0x3631564e
+ // ShmFormatNv61 : 2 plane YCbCr Cb:Cr format, 2x1 subsampled Cb:Cr plane
+ ShmFormatNv61 ShmFormat = 0x3136564e
+ // ShmFormatYuv410 : 3 plane YCbCr format, 4x4 subsampled Cb (1) and Cr (2) planes
+ ShmFormatYuv410 ShmFormat = 0x39565559
+ // ShmFormatYvu410 : 3 plane YCbCr format, 4x4 subsampled Cr (1) and Cb (2) planes
+ ShmFormatYvu410 ShmFormat = 0x39555659
+ // ShmFormatYuv411 : 3 plane YCbCr format, 4x1 subsampled Cb (1) and Cr (2) planes
+ ShmFormatYuv411 ShmFormat = 0x31315559
+ // ShmFormatYvu411 : 3 plane YCbCr format, 4x1 subsampled Cr (1) and Cb (2) planes
+ ShmFormatYvu411 ShmFormat = 0x31315659
+ // ShmFormatYuv420 : 3 plane YCbCr format, 2x2 subsampled Cb (1) and Cr (2) planes
+ ShmFormatYuv420 ShmFormat = 0x32315559
+ // ShmFormatYvu420 : 3 plane YCbCr format, 2x2 subsampled Cr (1) and Cb (2) planes
+ ShmFormatYvu420 ShmFormat = 0x32315659
+ // ShmFormatYuv422 : 3 plane YCbCr format, 2x1 subsampled Cb (1) and Cr (2) planes
+ ShmFormatYuv422 ShmFormat = 0x36315559
+ // ShmFormatYvu422 : 3 plane YCbCr format, 2x1 subsampled Cr (1) and Cb (2) planes
+ ShmFormatYvu422 ShmFormat = 0x36315659
+ // ShmFormatYuv444 : 3 plane YCbCr format, non-subsampled Cb (1) and Cr (2) planes
+ ShmFormatYuv444 ShmFormat = 0x34325559
+ // ShmFormatYvu444 : 3 plane YCbCr format, non-subsampled Cr (1) and Cb (2) planes
+ ShmFormatYvu444 ShmFormat = 0x34325659
+ // ShmFormatR8 : [7:0] R
+ ShmFormatR8 ShmFormat = 0x20203852
+ // ShmFormatR16 : [15:0] R little endian
+ ShmFormatR16 ShmFormat = 0x20363152
+ // ShmFormatRg88 : [15:0] R:G 8:8 little endian
+ ShmFormatRg88 ShmFormat = 0x38384752
+ // ShmFormatGr88 : [15:0] G:R 8:8 little endian
+ ShmFormatGr88 ShmFormat = 0x38385247
+ // ShmFormatRg1616 : [31:0] R:G 16:16 little endian
+ ShmFormatRg1616 ShmFormat = 0x32334752
+ // ShmFormatGr1616 : [31:0] G:R 16:16 little endian
+ ShmFormatGr1616 ShmFormat = 0x32335247
+ // ShmFormatXrgb16161616F : [63:0] x:R:G:B 16:16:16:16 little endian
+ ShmFormatXrgb16161616F ShmFormat = 0x48345258
+ // ShmFormatXbgr16161616F : [63:0] x:B:G:R 16:16:16:16 little endian
+ ShmFormatXbgr16161616F ShmFormat = 0x48344258
+ // ShmFormatArgb16161616F : [63:0] A:R:G:B 16:16:16:16 little endian
+ ShmFormatArgb16161616F ShmFormat = 0x48345241
+ // ShmFormatAbgr16161616F : [63:0] A:B:G:R 16:16:16:16 little endian
+ ShmFormatAbgr16161616F ShmFormat = 0x48344241
+ // ShmFormatXyuv8888 : [31:0] X:Y:Cb:Cr 8:8:8:8 little endian
+ ShmFormatXyuv8888 ShmFormat = 0x56555958
+ // ShmFormatVuy888 : [23:0] Cr:Cb:Y 8:8:8 little endian
+ ShmFormatVuy888 ShmFormat = 0x34325556
+ // ShmFormatVuy101010 : Y followed by U then V, 10:10:10.
+ ShmFormatVuy101010 ShmFormat = 0x30335556
+ // ShmFormatY210 : [63:0] Cr0:0:Y1:0:Cb0:0:Y0:0 10:6:10:6:10:6:10:6 little endian per 2 Y pixels
+ ShmFormatY210 ShmFormat = 0x30313259
+ // ShmFormatY212 : [63:0] Cr0:0:Y1:0:Cb0:0:Y0:0 12:4:12:4:12:4:12:4 little endian per 2 Y pixels
+ ShmFormatY212 ShmFormat = 0x32313259
+ // ShmFormatY216 : [63:0] Cr0:Y1:Cb0:Y0 16:16:16:16 little endian per 2 Y pixels
+ ShmFormatY216 ShmFormat = 0x36313259
+ // ShmFormatY410 : [31:0] A:Cr:Y:Cb 2:10:10:10 little endian
+ ShmFormatY410 ShmFormat = 0x30313459
+ // ShmFormatY412 : [63:0] A:0:Cr:0:Y:0:Cb:0 12:4:12:4:12:4:12:4 little endian
+ ShmFormatY412 ShmFormat = 0x32313459
+ // ShmFormatY416 : [63:0] A:Cr:Y:Cb 16:16:16:16 little endian
+ ShmFormatY416 ShmFormat = 0x36313459
+ // ShmFormatXvyu2101010 : [31:0] X:Cr:Y:Cb 2:10:10:10 little endian
+ ShmFormatXvyu2101010 ShmFormat = 0x30335658
+ // ShmFormatXvyu1216161616 : [63:0] X:0:Cr:0:Y:0:Cb:0 12:4:12:4:12:4:12:4 little endian
+ ShmFormatXvyu1216161616 ShmFormat = 0x36335658
+ // ShmFormatXvyu16161616 : [63:0] X:Cr:Y:Cb 16:16:16:16 little endian
+ ShmFormatXvyu16161616 ShmFormat = 0x38345658
+ // ShmFormatY0L0 : [63:0] A3:A2:Y3:0:Cr0:0:Y2:0:A1:A0:Y1:0:Cb0:0:Y0:0 1:1:8:2:8:2:8:2:1:1:8:2:8:2:8:2 little endian
+ ShmFormatY0L0 ShmFormat = 0x304c3059
+ // ShmFormatX0L0 : [63:0] X3:X2:Y3:0:Cr0:0:Y2:0:X1:X0:Y1:0:Cb0:0:Y0:0 1:1:8:2:8:2:8:2:1:1:8:2:8:2:8:2 little endian
+ ShmFormatX0L0 ShmFormat = 0x304c3058
+ // ShmFormatY0L2 : [63:0] A3:A2:Y3:Cr0:Y2:A1:A0:Y1:Cb0:Y0 1:1:10:10:10:1:1:10:10:10 little endian
+ ShmFormatY0L2 ShmFormat = 0x324c3059
+ // ShmFormatX0L2 : [63:0] X3:X2:Y3:Cr0:Y2:X1:X0:Y1:Cb0:Y0 1:1:10:10:10:1:1:10:10:10 little endian
+ ShmFormatX0L2 ShmFormat = 0x324c3058
+ ShmFormatYuv4208Bit ShmFormat = 0x38305559
+ ShmFormatYuv42010Bit ShmFormat = 0x30315559
+ ShmFormatXrgb8888A8 ShmFormat = 0x38415258
+ ShmFormatXbgr8888A8 ShmFormat = 0x38414258
+ ShmFormatRgbx8888A8 ShmFormat = 0x38415852
+ ShmFormatBgrx8888A8 ShmFormat = 0x38415842
+ ShmFormatRgb888A8 ShmFormat = 0x38413852
+ ShmFormatBgr888A8 ShmFormat = 0x38413842
+ ShmFormatRgb565A8 ShmFormat = 0x38413552
+ ShmFormatBgr565A8 ShmFormat = 0x38413542
+ // ShmFormatNv24 : non-subsampled Cr:Cb plane
+ ShmFormatNv24 ShmFormat = 0x3432564e
+ // ShmFormatNv42 : non-subsampled Cb:Cr plane
+ ShmFormatNv42 ShmFormat = 0x3234564e
+ // ShmFormatP210 : 2x1 subsampled Cr:Cb plane, 10 bit per channel
+ ShmFormatP210 ShmFormat = 0x30313250
+ // ShmFormatP010 : 2x2 subsampled Cr:Cb plane 10 bits per channel
+ ShmFormatP010 ShmFormat = 0x30313050
+ // ShmFormatP012 : 2x2 subsampled Cr:Cb plane 12 bits per channel
+ ShmFormatP012 ShmFormat = 0x32313050
+ // ShmFormatP016 : 2x2 subsampled Cr:Cb plane 16 bits per channel
+ ShmFormatP016 ShmFormat = 0x36313050
+ // ShmFormatAxbxgxrx106106106106 : [63:0] A:x:B:x:G:x:R:x 10:6:10:6:10:6:10:6 little endian
+ ShmFormatAxbxgxrx106106106106 ShmFormat = 0x30314241
+ // ShmFormatNv15 : 2x2 subsampled Cr:Cb plane
+ ShmFormatNv15 ShmFormat = 0x3531564e
+ ShmFormatQ410 ShmFormat = 0x30313451
+ ShmFormatQ401 ShmFormat = 0x31303451
+ // ShmFormatXrgb16161616 : [63:0] x:R:G:B 16:16:16:16 little endian
+ ShmFormatXrgb16161616 ShmFormat = 0x38345258
+ // ShmFormatXbgr16161616 : [63:0] x:B:G:R 16:16:16:16 little endian
+ ShmFormatXbgr16161616 ShmFormat = 0x38344258
+ // ShmFormatArgb16161616 : [63:0] A:R:G:B 16:16:16:16 little endian
+ ShmFormatArgb16161616 ShmFormat = 0x38345241
+ // ShmFormatAbgr16161616 : [63:0] A:B:G:R 16:16:16:16 little endian
+ ShmFormatAbgr16161616 ShmFormat = 0x38344241
+ // ShmFormatC1 : [7:0] C0:C1:C2:C3:C4:C5:C6:C7 1:1:1:1:1:1:1:1 eight pixels/byte
+ ShmFormatC1 ShmFormat = 0x20203143
+ // ShmFormatC2 : [7:0] C0:C1:C2:C3 2:2:2:2 four pixels/byte
+ ShmFormatC2 ShmFormat = 0x20203243
+ // ShmFormatC4 : [7:0] C0:C1 4:4 two pixels/byte
+ ShmFormatC4 ShmFormat = 0x20203443
+ // ShmFormatD1 : [7:0] D0:D1:D2:D3:D4:D5:D6:D7 1:1:1:1:1:1:1:1 eight pixels/byte
+ ShmFormatD1 ShmFormat = 0x20203144
+ // ShmFormatD2 : [7:0] D0:D1:D2:D3 2:2:2:2 four pixels/byte
+ ShmFormatD2 ShmFormat = 0x20203244
+ // ShmFormatD4 : [7:0] D0:D1 4:4 two pixels/byte
+ ShmFormatD4 ShmFormat = 0x20203444
+ // ShmFormatD8 : [7:0] D
+ ShmFormatD8 ShmFormat = 0x20203844
+ // ShmFormatR1 : [7:0] R0:R1:R2:R3:R4:R5:R6:R7 1:1:1:1:1:1:1:1 eight pixels/byte
+ ShmFormatR1 ShmFormat = 0x20203152
+ // ShmFormatR2 : [7:0] R0:R1:R2:R3 2:2:2:2 four pixels/byte
+ ShmFormatR2 ShmFormat = 0x20203252
+ // ShmFormatR4 : [7:0] R0:R1 4:4 two pixels/byte
+ ShmFormatR4 ShmFormat = 0x20203452
+ // ShmFormatR10 : [15:0] x:R 6:10 little endian
+ ShmFormatR10 ShmFormat = 0x20303152
+ // ShmFormatR12 : [15:0] x:R 4:12 little endian
+ ShmFormatR12 ShmFormat = 0x20323152
+ // ShmFormatAvuy8888 : [31:0] A:Cr:Cb:Y 8:8:8:8 little endian
+ ShmFormatAvuy8888 ShmFormat = 0x59555641
+ // ShmFormatXvuy8888 : [31:0] X:Cr:Cb:Y 8:8:8:8 little endian
+ ShmFormatXvuy8888 ShmFormat = 0x59555658
+ // ShmFormatP030 : 2x2 subsampled Cr:Cb plane 10 bits per channel packed
+ ShmFormatP030 ShmFormat = 0x30333050
+)
+
+func (e ShmFormat) Name() string {
+ switch e {
+ case ShmFormatArgb8888:
+ return "argb8888"
+ case ShmFormatXrgb8888:
+ return "xrgb8888"
+ case ShmFormatC8:
+ return "c8"
+ case ShmFormatRgb332:
+ return "rgb332"
+ case ShmFormatBgr233:
+ return "bgr233"
+ case ShmFormatXrgb4444:
+ return "xrgb4444"
+ case ShmFormatXbgr4444:
+ return "xbgr4444"
+ case ShmFormatRgbx4444:
+ return "rgbx4444"
+ case ShmFormatBgrx4444:
+ return "bgrx4444"
+ case ShmFormatArgb4444:
+ return "argb4444"
+ case ShmFormatAbgr4444:
+ return "abgr4444"
+ case ShmFormatRgba4444:
+ return "rgba4444"
+ case ShmFormatBgra4444:
+ return "bgra4444"
+ case ShmFormatXrgb1555:
+ return "xrgb1555"
+ case ShmFormatXbgr1555:
+ return "xbgr1555"
+ case ShmFormatRgbx5551:
+ return "rgbx5551"
+ case ShmFormatBgrx5551:
+ return "bgrx5551"
+ case ShmFormatArgb1555:
+ return "argb1555"
+ case ShmFormatAbgr1555:
+ return "abgr1555"
+ case ShmFormatRgba5551:
+ return "rgba5551"
+ case ShmFormatBgra5551:
+ return "bgra5551"
+ case ShmFormatRgb565:
+ return "rgb565"
+ case ShmFormatBgr565:
+ return "bgr565"
+ case ShmFormatRgb888:
+ return "rgb888"
+ case ShmFormatBgr888:
+ return "bgr888"
+ case ShmFormatXbgr8888:
+ return "xbgr8888"
+ case ShmFormatRgbx8888:
+ return "rgbx8888"
+ case ShmFormatBgrx8888:
+ return "bgrx8888"
+ case ShmFormatAbgr8888:
+ return "abgr8888"
+ case ShmFormatRgba8888:
+ return "rgba8888"
+ case ShmFormatBgra8888:
+ return "bgra8888"
+ case ShmFormatXrgb2101010:
+ return "xrgb2101010"
+ case ShmFormatXbgr2101010:
+ return "xbgr2101010"
+ case ShmFormatRgbx1010102:
+ return "rgbx1010102"
+ case ShmFormatBgrx1010102:
+ return "bgrx1010102"
+ case ShmFormatArgb2101010:
+ return "argb2101010"
+ case ShmFormatAbgr2101010:
+ return "abgr2101010"
+ case ShmFormatRgba1010102:
+ return "rgba1010102"
+ case ShmFormatBgra1010102:
+ return "bgra1010102"
+ case ShmFormatYuyv:
+ return "yuyv"
+ case ShmFormatYvyu:
+ return "yvyu"
+ case ShmFormatUyvy:
+ return "uyvy"
+ case ShmFormatVyuy:
+ return "vyuy"
+ case ShmFormatAyuv:
+ return "ayuv"
+ case ShmFormatNv12:
+ return "nv12"
+ case ShmFormatNv21:
+ return "nv21"
+ case ShmFormatNv16:
+ return "nv16"
+ case ShmFormatNv61:
+ return "nv61"
+ case ShmFormatYuv410:
+ return "yuv410"
+ case ShmFormatYvu410:
+ return "yvu410"
+ case ShmFormatYuv411:
+ return "yuv411"
+ case ShmFormatYvu411:
+ return "yvu411"
+ case ShmFormatYuv420:
+ return "yuv420"
+ case ShmFormatYvu420:
+ return "yvu420"
+ case ShmFormatYuv422:
+ return "yuv422"
+ case ShmFormatYvu422:
+ return "yvu422"
+ case ShmFormatYuv444:
+ return "yuv444"
+ case ShmFormatYvu444:
+ return "yvu444"
+ case ShmFormatR8:
+ return "r8"
+ case ShmFormatR16:
+ return "r16"
+ case ShmFormatRg88:
+ return "rg88"
+ case ShmFormatGr88:
+ return "gr88"
+ case ShmFormatRg1616:
+ return "rg1616"
+ case ShmFormatGr1616:
+ return "gr1616"
+ case ShmFormatXrgb16161616F:
+ return "xrgb16161616f"
+ case ShmFormatXbgr16161616F:
+ return "xbgr16161616f"
+ case ShmFormatArgb16161616F:
+ return "argb16161616f"
+ case ShmFormatAbgr16161616F:
+ return "abgr16161616f"
+ case ShmFormatXyuv8888:
+ return "xyuv8888"
+ case ShmFormatVuy888:
+ return "vuy888"
+ case ShmFormatVuy101010:
+ return "vuy101010"
+ case ShmFormatY210:
+ return "y210"
+ case ShmFormatY212:
+ return "y212"
+ case ShmFormatY216:
+ return "y216"
+ case ShmFormatY410:
+ return "y410"
+ case ShmFormatY412:
+ return "y412"
+ case ShmFormatY416:
+ return "y416"
+ case ShmFormatXvyu2101010:
+ return "xvyu2101010"
+ case ShmFormatXvyu1216161616:
+ return "xvyu12_16161616"
+ case ShmFormatXvyu16161616:
+ return "xvyu16161616"
+ case ShmFormatY0L0:
+ return "y0l0"
+ case ShmFormatX0L0:
+ return "x0l0"
+ case ShmFormatY0L2:
+ return "y0l2"
+ case ShmFormatX0L2:
+ return "x0l2"
+ case ShmFormatYuv4208Bit:
+ return "yuv420_8bit"
+ case ShmFormatYuv42010Bit:
+ return "yuv420_10bit"
+ case ShmFormatXrgb8888A8:
+ return "xrgb8888_a8"
+ case ShmFormatXbgr8888A8:
+ return "xbgr8888_a8"
+ case ShmFormatRgbx8888A8:
+ return "rgbx8888_a8"
+ case ShmFormatBgrx8888A8:
+ return "bgrx8888_a8"
+ case ShmFormatRgb888A8:
+ return "rgb888_a8"
+ case ShmFormatBgr888A8:
+ return "bgr888_a8"
+ case ShmFormatRgb565A8:
+ return "rgb565_a8"
+ case ShmFormatBgr565A8:
+ return "bgr565_a8"
+ case ShmFormatNv24:
+ return "nv24"
+ case ShmFormatNv42:
+ return "nv42"
+ case ShmFormatP210:
+ return "p210"
+ case ShmFormatP010:
+ return "p010"
+ case ShmFormatP012:
+ return "p012"
+ case ShmFormatP016:
+ return "p016"
+ case ShmFormatAxbxgxrx106106106106:
+ return "axbxgxrx106106106106"
+ case ShmFormatNv15:
+ return "nv15"
+ case ShmFormatQ410:
+ return "q410"
+ case ShmFormatQ401:
+ return "q401"
+ case ShmFormatXrgb16161616:
+ return "xrgb16161616"
+ case ShmFormatXbgr16161616:
+ return "xbgr16161616"
+ case ShmFormatArgb16161616:
+ return "argb16161616"
+ case ShmFormatAbgr16161616:
+ return "abgr16161616"
+ case ShmFormatC1:
+ return "c1"
+ case ShmFormatC2:
+ return "c2"
+ case ShmFormatC4:
+ return "c4"
+ case ShmFormatD1:
+ return "d1"
+ case ShmFormatD2:
+ return "d2"
+ case ShmFormatD4:
+ return "d4"
+ case ShmFormatD8:
+ return "d8"
+ case ShmFormatR1:
+ return "r1"
+ case ShmFormatR2:
+ return "r2"
+ case ShmFormatR4:
+ return "r4"
+ case ShmFormatR10:
+ return "r10"
+ case ShmFormatR12:
+ return "r12"
+ case ShmFormatAvuy8888:
+ return "avuy8888"
+ case ShmFormatXvuy8888:
+ return "xvuy8888"
+ case ShmFormatP030:
+ return "p030"
+ default:
+ return ""
+ }
+}
+
+func (e ShmFormat) Value() string {
+ switch e {
+ case ShmFormatArgb8888:
+ return "0"
+ case ShmFormatXrgb8888:
+ return "1"
+ case ShmFormatC8:
+ return "0x20203843"
+ case ShmFormatRgb332:
+ return "0x38424752"
+ case ShmFormatBgr233:
+ return "0x38524742"
+ case ShmFormatXrgb4444:
+ return "0x32315258"
+ case ShmFormatXbgr4444:
+ return "0x32314258"
+ case ShmFormatRgbx4444:
+ return "0x32315852"
+ case ShmFormatBgrx4444:
+ return "0x32315842"
+ case ShmFormatArgb4444:
+ return "0x32315241"
+ case ShmFormatAbgr4444:
+ return "0x32314241"
+ case ShmFormatRgba4444:
+ return "0x32314152"
+ case ShmFormatBgra4444:
+ return "0x32314142"
+ case ShmFormatXrgb1555:
+ return "0x35315258"
+ case ShmFormatXbgr1555:
+ return "0x35314258"
+ case ShmFormatRgbx5551:
+ return "0x35315852"
+ case ShmFormatBgrx5551:
+ return "0x35315842"
+ case ShmFormatArgb1555:
+ return "0x35315241"
+ case ShmFormatAbgr1555:
+ return "0x35314241"
+ case ShmFormatRgba5551:
+ return "0x35314152"
+ case ShmFormatBgra5551:
+ return "0x35314142"
+ case ShmFormatRgb565:
+ return "0x36314752"
+ case ShmFormatBgr565:
+ return "0x36314742"
+ case ShmFormatRgb888:
+ return "0x34324752"
+ case ShmFormatBgr888:
+ return "0x34324742"
+ case ShmFormatXbgr8888:
+ return "0x34324258"
+ case ShmFormatRgbx8888:
+ return "0x34325852"
+ case ShmFormatBgrx8888:
+ return "0x34325842"
+ case ShmFormatAbgr8888:
+ return "0x34324241"
+ case ShmFormatRgba8888:
+ return "0x34324152"
+ case ShmFormatBgra8888:
+ return "0x34324142"
+ case ShmFormatXrgb2101010:
+ return "0x30335258"
+ case ShmFormatXbgr2101010:
+ return "0x30334258"
+ case ShmFormatRgbx1010102:
+ return "0x30335852"
+ case ShmFormatBgrx1010102:
+ return "0x30335842"
+ case ShmFormatArgb2101010:
+ return "0x30335241"
+ case ShmFormatAbgr2101010:
+ return "0x30334241"
+ case ShmFormatRgba1010102:
+ return "0x30334152"
+ case ShmFormatBgra1010102:
+ return "0x30334142"
+ case ShmFormatYuyv:
+ return "0x56595559"
+ case ShmFormatYvyu:
+ return "0x55595659"
+ case ShmFormatUyvy:
+ return "0x59565955"
+ case ShmFormatVyuy:
+ return "0x59555956"
+ case ShmFormatAyuv:
+ return "0x56555941"
+ case ShmFormatNv12:
+ return "0x3231564e"
+ case ShmFormatNv21:
+ return "0x3132564e"
+ case ShmFormatNv16:
+ return "0x3631564e"
+ case ShmFormatNv61:
+ return "0x3136564e"
+ case ShmFormatYuv410:
+ return "0x39565559"
+ case ShmFormatYvu410:
+ return "0x39555659"
+ case ShmFormatYuv411:
+ return "0x31315559"
+ case ShmFormatYvu411:
+ return "0x31315659"
+ case ShmFormatYuv420:
+ return "0x32315559"
+ case ShmFormatYvu420:
+ return "0x32315659"
+ case ShmFormatYuv422:
+ return "0x36315559"
+ case ShmFormatYvu422:
+ return "0x36315659"
+ case ShmFormatYuv444:
+ return "0x34325559"
+ case ShmFormatYvu444:
+ return "0x34325659"
+ case ShmFormatR8:
+ return "0x20203852"
+ case ShmFormatR16:
+ return "0x20363152"
+ case ShmFormatRg88:
+ return "0x38384752"
+ case ShmFormatGr88:
+ return "0x38385247"
+ case ShmFormatRg1616:
+ return "0x32334752"
+ case ShmFormatGr1616:
+ return "0x32335247"
+ case ShmFormatXrgb16161616F:
+ return "0x48345258"
+ case ShmFormatXbgr16161616F:
+ return "0x48344258"
+ case ShmFormatArgb16161616F:
+ return "0x48345241"
+ case ShmFormatAbgr16161616F:
+ return "0x48344241"
+ case ShmFormatXyuv8888:
+ return "0x56555958"
+ case ShmFormatVuy888:
+ return "0x34325556"
+ case ShmFormatVuy101010:
+ return "0x30335556"
+ case ShmFormatY210:
+ return "0x30313259"
+ case ShmFormatY212:
+ return "0x32313259"
+ case ShmFormatY216:
+ return "0x36313259"
+ case ShmFormatY410:
+ return "0x30313459"
+ case ShmFormatY412:
+ return "0x32313459"
+ case ShmFormatY416:
+ return "0x36313459"
+ case ShmFormatXvyu2101010:
+ return "0x30335658"
+ case ShmFormatXvyu1216161616:
+ return "0x36335658"
+ case ShmFormatXvyu16161616:
+ return "0x38345658"
+ case ShmFormatY0L0:
+ return "0x304c3059"
+ case ShmFormatX0L0:
+ return "0x304c3058"
+ case ShmFormatY0L2:
+ return "0x324c3059"
+ case ShmFormatX0L2:
+ return "0x324c3058"
+ case ShmFormatYuv4208Bit:
+ return "0x38305559"
+ case ShmFormatYuv42010Bit:
+ return "0x30315559"
+ case ShmFormatXrgb8888A8:
+ return "0x38415258"
+ case ShmFormatXbgr8888A8:
+ return "0x38414258"
+ case ShmFormatRgbx8888A8:
+ return "0x38415852"
+ case ShmFormatBgrx8888A8:
+ return "0x38415842"
+ case ShmFormatRgb888A8:
+ return "0x38413852"
+ case ShmFormatBgr888A8:
+ return "0x38413842"
+ case ShmFormatRgb565A8:
+ return "0x38413552"
+ case ShmFormatBgr565A8:
+ return "0x38413542"
+ case ShmFormatNv24:
+ return "0x3432564e"
+ case ShmFormatNv42:
+ return "0x3234564e"
+ case ShmFormatP210:
+ return "0x30313250"
+ case ShmFormatP010:
+ return "0x30313050"
+ case ShmFormatP012:
+ return "0x32313050"
+ case ShmFormatP016:
+ return "0x36313050"
+ case ShmFormatAxbxgxrx106106106106:
+ return "0x30314241"
+ case ShmFormatNv15:
+ return "0x3531564e"
+ case ShmFormatQ410:
+ return "0x30313451"
+ case ShmFormatQ401:
+ return "0x31303451"
+ case ShmFormatXrgb16161616:
+ return "0x38345258"
+ case ShmFormatXbgr16161616:
+ return "0x38344258"
+ case ShmFormatArgb16161616:
+ return "0x38345241"
+ case ShmFormatAbgr16161616:
+ return "0x38344241"
+ case ShmFormatC1:
+ return "0x20203143"
+ case ShmFormatC2:
+ return "0x20203243"
+ case ShmFormatC4:
+ return "0x20203443"
+ case ShmFormatD1:
+ return "0x20203144"
+ case ShmFormatD2:
+ return "0x20203244"
+ case ShmFormatD4:
+ return "0x20203444"
+ case ShmFormatD8:
+ return "0x20203844"
+ case ShmFormatR1:
+ return "0x20203152"
+ case ShmFormatR2:
+ return "0x20203252"
+ case ShmFormatR4:
+ return "0x20203452"
+ case ShmFormatR10:
+ return "0x20303152"
+ case ShmFormatR12:
+ return "0x20323152"
+ case ShmFormatAvuy8888:
+ return "0x59555641"
+ case ShmFormatXvuy8888:
+ return "0x59555658"
+ case ShmFormatP030:
+ return "0x30333050"
+ default:
+ return ""
+ }
+}
+
+func (e ShmFormat) String() string {
+ return e.Name() + "=" + e.Value()
+}
+
+// ShmFormatEvent : pixel format description
+//
+// Informs the client about a valid pixel format that
+// can be used for buffers. Known formats include
+// argb8888 and xrgb8888.
+type ShmFormatEvent struct {
+ Format uint32
+}
+type ShmFormatHandlerFunc func(ShmFormatEvent)
+
+// SetFormatHandler : sets handler for ShmFormatEvent
+func (i *Shm) SetFormatHandler(f ShmFormatHandlerFunc) {
+ i.formatHandler = f
+}
+
+func (i *Shm) Dispatch(opcode uint32, fd int, data []byte) {
+ switch opcode {
+ case 0:
+ if i.formatHandler == nil {
+ return
+ }
+ var e ShmFormatEvent
+ l := 0
+ e.Format = Uint32(data[l : l+4])
+ l += 4
+
+ i.formatHandler(e)
+ }
+}
+
+// BufferInterfaceName 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 BufferInterfaceName = "wl_buffer"
+
+// Buffer : content for a wl_surface
+//
+// A buffer provides the content for a wl_surface. Buffers are
+// created through factory interfaces such as wl_shm, wp_linux_buffer_params
+// (from the linux-dmabuf protocol extension) or similar. It has a width and
+// a height and can be attached to a wl_surface, but the mechanism by which a
+// client provides and updates the contents is defined by the buffer factory
+// interface.
+//
+// Color channels are assumed to be electrical rather than optical (in other
+// words, encoded with a transfer function) unless otherwise specified. If
+// the buffer uses a format that has an alpha channel, the alpha channel is
+// assumed to be premultiplied into the electrical color channel values
+// (after transfer function encoding) unless otherwise specified.
+//
+// Note, because wl_buffer objects are created from multiple independent
+// factory interfaces, the wl_buffer interface is frozen at version 1.
+type Buffer struct {
+ BaseProxy
+ releaseHandler BufferReleaseHandlerFunc
+}
+
+// NewBuffer : content for a wl_surface
+//
+// A buffer provides the content for a wl_surface. Buffers are
+// created through factory interfaces such as wl_shm, wp_linux_buffer_params
+// (from the linux-dmabuf protocol extension) or similar. It has a width and
+// a height and can be attached to a wl_surface, but the mechanism by which a
+// client provides and updates the contents is defined by the buffer factory
+// interface.
+//
+// Color channels are assumed to be electrical rather than optical (in other
+// words, encoded with a transfer function) unless otherwise specified. If
+// the buffer uses a format that has an alpha channel, the alpha channel is
+// assumed to be premultiplied into the electrical color channel values
+// (after transfer function encoding) unless otherwise specified.
+//
+// Note, because wl_buffer objects are created from multiple independent
+// factory interfaces, the wl_buffer interface is frozen at version 1.
+func NewBuffer(ctx *Context) *Buffer {
+ wlBuffer := &Buffer{}
+ ctx.Register(wlBuffer)
+ return wlBuffer
+}
+
+// Destroy : destroy a buffer
+//
+// Destroy a buffer. If and how you need to release the backing
+// storage is defined by the buffer factory interface.
+//
+// For possible side-effects to a surface, see wl_surface.attach.
+func (i *Buffer) Destroy() error {
+ defer i.MarkZombie()
+ const opcode = 0
+ const _reqBufLen = 8
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// BufferReleaseEvent : compositor releases buffer
+//
+// Sent when this wl_buffer is no longer used by the compositor.
+//
+// For more information on when release events may or may not be sent,
+// and what consequences it has, please see the description of
+// wl_surface.attach.
+//
+// If a client receives a release event before the frame callback
+// requested in the same wl_surface.commit that attaches this
+// wl_buffer to a surface, then the client is immediately free to
+// reuse the buffer and its backing storage, and does not need a
+// second buffer for the next surface content update. Typically
+// this is possible, when the compositor maintains a copy of the
+// wl_surface contents, e.g. as a GL texture. This is an important
+// optimization for GL(ES) compositors with wl_shm clients.
+type BufferReleaseEvent struct{}
+type BufferReleaseHandlerFunc func(BufferReleaseEvent)
+
+// SetReleaseHandler : sets handler for BufferReleaseEvent
+func (i *Buffer) SetReleaseHandler(f BufferReleaseHandlerFunc) {
+ i.releaseHandler = f
+}
+
+func (i *Buffer) Dispatch(opcode uint32, fd int, data []byte) {
+ switch opcode {
+ case 0:
+ if i.releaseHandler == nil {
+ return
+ }
+ var e BufferReleaseEvent
+
+ i.releaseHandler(e)
+ }
+}
+
+// DataOfferInterfaceName 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 DataOfferInterfaceName = "wl_data_offer"
+
+// DataOffer : offer to transfer data
+//
+// A wl_data_offer represents a piece of data offered for transfer
+// by another client (the source client). It is used by the
+// copy-and-paste and drag-and-drop mechanisms. The offer
+// describes the different mime types that the data can be
+// converted to and provides the mechanism for transferring the
+// data directly from the source client.
+type DataOffer struct {
+ BaseProxy
+ offerHandler DataOfferOfferHandlerFunc
+ sourceActionsHandler DataOfferSourceActionsHandlerFunc
+ actionHandler DataOfferActionHandlerFunc
+}
+
+// NewDataOffer : offer to transfer data
+//
+// A wl_data_offer represents a piece of data offered for transfer
+// by another client (the source client). It is used by the
+// copy-and-paste and drag-and-drop mechanisms. The offer
+// describes the different mime types that the data can be
+// converted to and provides the mechanism for transferring the
+// data directly from the source client.
+func NewDataOffer(ctx *Context) *DataOffer {
+ wlDataOffer := &DataOffer{}
+ ctx.Register(wlDataOffer)
+ return wlDataOffer
+}
+
+// Accept : accept one of the offered mime types
+//
+// Indicate that the client can accept the given mime type, or
+// NULL for not accepted.
+//
+// For objects of version 2 or older, this request is used by the
+// client to give feedback whether the client can receive the given
+// mime type, or NULL if none is accepted; the feedback does not
+// determine whether the drag-and-drop operation succeeds or not.
+//
+// For objects of version 3 or newer, this request determines the
+// final result of the drag-and-drop operation. If the end result
+// is that no mime types were accepted, the drag-and-drop operation
+// will be cancelled and the corresponding drag source will receive
+// wl_data_source.cancelled. Clients may still use this event in
+// conjunction with wl_data_source.action for feedback.
+//
+// serial: serial number of the accept request
+// mimeType: mime type accepted by the client
+func (i *DataOffer) Accept(serial uint32, mimeType string) error {
+ const opcode = 0
+ mimeTypeLen := PaddedLen(len(mimeType) + 1)
+ _reqBufLen := 8 + 4 + (4 + mimeTypeLen)
+ _reqBuf := make([]byte, _reqBufLen)
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(serial))
+ l += 4
+ PutString(_reqBuf[l:l+(4+mimeTypeLen)], mimeType)
+ l += (4 + mimeTypeLen)
+ err := i.Context().WriteMsg(_reqBuf, nil)
+ return err
+}
+
+// Receive : request that the data is transferred
+//
+// To transfer the offered data, the client issues this request
+// and indicates the mime type it wants to receive. The transfer
+// happens through the passed file descriptor (typically created
+// with the pipe system call). The source client writes the data
+// in the mime type representation requested and then closes the
+// file descriptor.
+//
+// The receiving client reads from the read end of the pipe until
+// EOF and then closes its end, at which point the transfer is
+// complete.
+//
+// This request may happen multiple times for different mime types,
+// both before and after wl_data_device.drop. Drag-and-drop destination
+// clients may preemptively fetch data or examine it more closely to
+// determine acceptance.
+//
+// mimeType: mime type desired by receiver
+// fd: file descriptor for data transfer
+func (i *DataOffer) Receive(mimeType string, fd int) error {
+ const opcode = 1
+ mimeTypeLen := PaddedLen(len(mimeType) + 1)
+ _reqBufLen := 8 + (4 + mimeTypeLen)
+ _reqBuf := make([]byte, _reqBufLen)
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutString(_reqBuf[l:l+(4+mimeTypeLen)], mimeType)
+ l += (4 + mimeTypeLen)
+ oob := unix.UnixRights(int(fd))
+ err := i.Context().WriteMsg(_reqBuf, oob)
+ return err
+}
+
+// Destroy : destroy data offer
+//
+// Destroy the data offer.
+func (i *DataOffer) Destroy() error {
+ defer i.MarkZombie()
+ const opcode = 2
+ const _reqBufLen = 8
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// Finish : the offer will no longer be used
+//
+// Notifies the compositor that the drag destination successfully
+// finished the drag-and-drop operation.
+//
+// Upon receiving this request, the compositor will emit
+// wl_data_source.dnd_finished on the drag source client.
+//
+// It is a client error to perform other requests than
+// wl_data_offer.destroy after this one. It is also an error to perform
+// this request after a NULL mime type has been set in
+// wl_data_offer.accept or no action was received through
+// wl_data_offer.action.
+//
+// If wl_data_offer.finish request is received for a non drag and drop
+// operation, the invalid_finish protocol error is raised.
+func (i *DataOffer) Finish() error {
+ const opcode = 3
+ const _reqBufLen = 8
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// SetActions : set the available/preferred drag-and-drop actions
+//
+// Sets the actions that the destination side client supports for
+// this operation. This request may trigger the emission of
+// wl_data_source.action and wl_data_offer.action events if the compositor
+// needs to change the selected action.
+//
+// This request can be called multiple times throughout the
+// drag-and-drop operation, typically in response to wl_data_device.enter
+// or wl_data_device.motion events.
+//
+// This request determines the final result of the drag-and-drop
+// operation. If the end result is that no action is accepted,
+// the drag source will receive wl_data_source.cancelled.
+//
+// The dnd_actions argument must contain only values expressed in the
+// wl_data_device_manager.dnd_actions enum, and the preferred_action
+// argument must only contain one of those values set, otherwise it
+// will result in a protocol error.
+//
+// While managing an "ask" action, the destination drag-and-drop client
+// may perform further wl_data_offer.receive requests, and is expected
+// to perform one last wl_data_offer.set_actions request with a preferred
+// action other than "ask" (and optionally wl_data_offer.accept) before
+// requesting wl_data_offer.finish, in order to convey the action selected
+// by the user. If the preferred action is not in the
+// wl_data_offer.source_actions mask, an error will be raised.
+//
+// If the "ask" action is dismissed (e.g. user cancellation), the client
+// is expected to perform wl_data_offer.destroy right away.
+//
+// This request can only be made on drag-and-drop offers, a protocol error
+// will be raised otherwise.
+//
+// dndActions: actions supported by the destination client
+// preferredAction: action preferred by the destination client
+func (i *DataOffer) SetActions(dndActions, preferredAction uint32) error {
+ const opcode = 4
+ const _reqBufLen = 8 + 4 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(dndActions))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(preferredAction))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+type DataOfferError uint32
+
+// DataOfferError :
+const (
+ // DataOfferErrorInvalidFinish : finish request was called untimely
+ DataOfferErrorInvalidFinish DataOfferError = 0
+ // DataOfferErrorInvalidActionMask : action mask contains invalid values
+ DataOfferErrorInvalidActionMask DataOfferError = 1
+ // DataOfferErrorInvalidAction : action argument has an invalid value
+ DataOfferErrorInvalidAction DataOfferError = 2
+ // DataOfferErrorInvalidOffer : offer doesn't accept this request
+ DataOfferErrorInvalidOffer DataOfferError = 3
+)
+
+func (e DataOfferError) Name() string {
+ switch e {
+ case DataOfferErrorInvalidFinish:
+ return "invalid_finish"
+ case DataOfferErrorInvalidActionMask:
+ return "invalid_action_mask"
+ case DataOfferErrorInvalidAction:
+ return "invalid_action"
+ case DataOfferErrorInvalidOffer:
+ return "invalid_offer"
+ default:
+ return ""
+ }
+}
+
+func (e DataOfferError) Value() string {
+ switch e {
+ case DataOfferErrorInvalidFinish:
+ return "0"
+ case DataOfferErrorInvalidActionMask:
+ return "1"
+ case DataOfferErrorInvalidAction:
+ return "2"
+ case DataOfferErrorInvalidOffer:
+ return "3"
+ default:
+ return ""
+ }
+}
+
+func (e DataOfferError) String() string {
+ return e.Name() + "=" + e.Value()
+}
+
+// DataOfferOfferEvent : advertise offered mime type
+//
+// Sent immediately after creating the wl_data_offer object. One
+// event per offered mime type.
+type DataOfferOfferEvent struct {
+ MimeType string
+}
+type DataOfferOfferHandlerFunc func(DataOfferOfferEvent)
+
+// SetOfferHandler : sets handler for DataOfferOfferEvent
+func (i *DataOffer) SetOfferHandler(f DataOfferOfferHandlerFunc) {
+ i.offerHandler = f
+}
+
+// DataOfferSourceActionsEvent : notify the source-side available actions
+//
+// This event indicates the actions offered by the data source. It
+// will be sent immediately after creating the wl_data_offer object,
+// or anytime the source side changes its offered actions through
+// wl_data_source.set_actions.
+type DataOfferSourceActionsEvent struct {
+ SourceActions uint32
+}
+type DataOfferSourceActionsHandlerFunc func(DataOfferSourceActionsEvent)
+
+// SetSourceActionsHandler : sets handler for DataOfferSourceActionsEvent
+func (i *DataOffer) SetSourceActionsHandler(f DataOfferSourceActionsHandlerFunc) {
+ i.sourceActionsHandler = f
+}
+
+// DataOfferActionEvent : notify the selected action
+//
+// This event indicates the action selected by the compositor after
+// matching the source/destination side actions. Only one action (or
+// none) will be offered here.
+//
+// This event can be emitted multiple times during the drag-and-drop
+// operation in response to destination side action changes through
+// wl_data_offer.set_actions.
+//
+// This event will no longer be emitted after wl_data_device.drop
+// happened on the drag-and-drop destination, the client must
+// honor the last action received, or the last preferred one set
+// through wl_data_offer.set_actions when handling an "ask" action.
+//
+// Compositors may also change the selected action on the fly, mainly
+// in response to keyboard modifier changes during the drag-and-drop
+// operation.
+//
+// The most recent action received is always the valid one. Prior to
+// receiving wl_data_device.drop, the chosen action may change (e.g.
+// due to keyboard modifiers being pressed). At the time of receiving
+// wl_data_device.drop the drag-and-drop destination must honor the
+// last action received.
+//
+// Action changes may still happen after wl_data_device.drop,
+// especially on "ask" actions, where the drag-and-drop destination
+// may choose another action afterwards. Action changes happening
+// at this stage are always the result of inter-client negotiation, the
+// compositor shall no longer be able to induce a different action.
+//
+// Upon "ask" actions, it is expected that the drag-and-drop destination
+// may potentially choose a different action and/or mime type,
+// based on wl_data_offer.source_actions and finally chosen by the
+// user (e.g. popping up a menu with the available options). The
+// final wl_data_offer.set_actions and wl_data_offer.accept requests
+// must happen before the call to wl_data_offer.finish.
+type DataOfferActionEvent struct {
+ DndAction uint32
+}
+type DataOfferActionHandlerFunc func(DataOfferActionEvent)
+
+// SetActionHandler : sets handler for DataOfferActionEvent
+func (i *DataOffer) SetActionHandler(f DataOfferActionHandlerFunc) {
+ i.actionHandler = f
+}
+
+func (i *DataOffer) Dispatch(opcode uint32, fd int, data []byte) {
+ switch opcode {
+ case 0:
+ if i.offerHandler == nil {
+ return
+ }
+ var e DataOfferOfferEvent
+ l := 0
+ mimeTypeLen := PaddedLen(int(Uint32(data[l : l+4])))
+ l += 4
+ e.MimeType = String(data[l : l+mimeTypeLen])
+ l += mimeTypeLen
+
+ i.offerHandler(e)
+ case 1:
+ if i.sourceActionsHandler == nil {
+ return
+ }
+ var e DataOfferSourceActionsEvent
+ l := 0
+ e.SourceActions = Uint32(data[l : l+4])
+ l += 4
+
+ i.sourceActionsHandler(e)
+ case 2:
+ if i.actionHandler == nil {
+ return
+ }
+ var e DataOfferActionEvent
+ l := 0
+ e.DndAction = Uint32(data[l : l+4])
+ l += 4
+
+ i.actionHandler(e)
+ }
+}
+
+// DataSourceInterfaceName 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 DataSourceInterfaceName = "wl_data_source"
+
+// DataSource : offer to transfer data
+//
+// The wl_data_source object is the source side of a wl_data_offer.
+// It is created by the source client in a data transfer and
+// provides a way to describe the offered data and a way to respond
+// to requests to transfer the data.
+type DataSource struct {
+ BaseProxy
+ targetHandler DataSourceTargetHandlerFunc
+ sendHandler DataSourceSendHandlerFunc
+ cancelledHandler DataSourceCancelledHandlerFunc
+ dndDropPerformedHandler DataSourceDndDropPerformedHandlerFunc
+ dndFinishedHandler DataSourceDndFinishedHandlerFunc
+ actionHandler DataSourceActionHandlerFunc
+}
+
+// NewDataSource : offer to transfer data
+//
+// The wl_data_source object is the source side of a wl_data_offer.
+// It is created by the source client in a data transfer and
+// provides a way to describe the offered data and a way to respond
+// to requests to transfer the data.
+func NewDataSource(ctx *Context) *DataSource {
+ wlDataSource := &DataSource{}
+ ctx.Register(wlDataSource)
+ return wlDataSource
+}
+
+// Offer : add an offered mime type
+//
+// This request adds a mime type to the set of mime types
+// advertised to targets. Can be called several times to offer
+// multiple types.
+//
+// mimeType: mime type offered by the data source
+func (i *DataSource) Offer(mimeType string) error {
+ const opcode = 0
+ mimeTypeLen := PaddedLen(len(mimeType) + 1)
+ _reqBufLen := 8 + (4 + mimeTypeLen)
+ _reqBuf := make([]byte, _reqBufLen)
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutString(_reqBuf[l:l+(4+mimeTypeLen)], mimeType)
+ l += (4 + mimeTypeLen)
+ err := i.Context().WriteMsg(_reqBuf, nil)
+ return err
+}
+
+// Destroy : destroy the data source
+//
+// Destroy the data source.
+func (i *DataSource) Destroy() error {
+ defer i.MarkZombie()
+ const opcode = 1
+ const _reqBufLen = 8
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// SetActions : set the available drag-and-drop actions
+//
+// Sets the actions that the source side client supports for this
+// operation. This request may trigger wl_data_source.action and
+// wl_data_offer.action events if the compositor needs to change the
+// selected action.
+//
+// The dnd_actions argument must contain only values expressed in the
+// wl_data_device_manager.dnd_actions enum, otherwise it will result
+// in a protocol error.
+//
+// This request must be made once only, and can only be made on sources
+// used in drag-and-drop, so it must be performed before
+// wl_data_device.start_drag. Attempting to use the source other than
+// for drag-and-drop will raise a protocol error.
+//
+// dndActions: actions supported by the data source
+func (i *DataSource) SetActions(dndActions uint32) error {
+ const opcode = 2
+ const _reqBufLen = 8 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(dndActions))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+type DataSourceError uint32
+
+// DataSourceError :
+const (
+ // DataSourceErrorInvalidActionMask : action mask contains invalid values
+ DataSourceErrorInvalidActionMask DataSourceError = 0
+ // DataSourceErrorInvalidSource : source doesn't accept this request
+ DataSourceErrorInvalidSource DataSourceError = 1
+)
+
+func (e DataSourceError) Name() string {
+ switch e {
+ case DataSourceErrorInvalidActionMask:
+ return "invalid_action_mask"
+ case DataSourceErrorInvalidSource:
+ return "invalid_source"
+ default:
+ return ""
+ }
+}
+
+func (e DataSourceError) Value() string {
+ switch e {
+ case DataSourceErrorInvalidActionMask:
+ return "0"
+ case DataSourceErrorInvalidSource:
+ return "1"
+ default:
+ return ""
+ }
+}
+
+func (e DataSourceError) String() string {
+ return e.Name() + "=" + e.Value()
+}
+
+// DataSourceTargetEvent : a target accepts an offered mime type
+//
+// Sent when a target accepts pointer_focus or motion events. If
+// a target does not accept any of the offered types, type is NULL.
+//
+// Used for feedback during drag-and-drop.
+type DataSourceTargetEvent struct {
+ MimeType string
+}
+type DataSourceTargetHandlerFunc func(DataSourceTargetEvent)
+
+// SetTargetHandler : sets handler for DataSourceTargetEvent
+func (i *DataSource) SetTargetHandler(f DataSourceTargetHandlerFunc) {
+ i.targetHandler = f
+}
+
+// DataSourceSendEvent : send the data
+//
+// Request for data from the client. Send the data as the
+// specified mime type over the passed file descriptor, then
+// close it.
+type DataSourceSendEvent struct {
+ MimeType string
+ Fd int
+}
+type DataSourceSendHandlerFunc func(DataSourceSendEvent)
+
+// SetSendHandler : sets handler for DataSourceSendEvent
+func (i *DataSource) SetSendHandler(f DataSourceSendHandlerFunc) {
+ i.sendHandler = f
+}
+
+// DataSourceCancelledEvent : selection was cancelled
+//
+// This data source is no longer valid. There are several reasons why
+// this could happen:
+//
+// - The data source has been replaced by another data source.
+// - The drag-and-drop operation was performed, but the drop destination
+// did not accept any of the mime types offered through
+// wl_data_source.target.
+// - The drag-and-drop operation was performed, but the drop destination
+// did not select any of the actions present in the mask offered through
+// wl_data_source.action.
+// - The drag-and-drop operation was performed but didn't happen over a
+// surface.
+// - The compositor cancelled the drag-and-drop operation (e.g. compositor
+// dependent timeouts to avoid stale drag-and-drop transfers).
+//
+// The client should clean up and destroy this data source.
+//
+// For objects of version 2 or older, wl_data_source.cancelled will
+// only be emitted if the data source was replaced by another data
+// source.
+type DataSourceCancelledEvent struct{}
+type DataSourceCancelledHandlerFunc func(DataSourceCancelledEvent)
+
+// SetCancelledHandler : sets handler for DataSourceCancelledEvent
+func (i *DataSource) SetCancelledHandler(f DataSourceCancelledHandlerFunc) {
+ i.cancelledHandler = f
+}
+
+// DataSourceDndDropPerformedEvent : the drag-and-drop operation physically finished
+//
+// The user performed the drop action. This event does not indicate
+// acceptance, wl_data_source.cancelled may still be emitted afterwards
+// if the drop destination does not accept any mime type.
+//
+// However, this event might however not be received if the compositor
+// cancelled the drag-and-drop operation before this event could happen.
+//
+// Note that the data_source may still be used in the future and should
+// not be destroyed here.
+type DataSourceDndDropPerformedEvent struct{}
+type DataSourceDndDropPerformedHandlerFunc func(DataSourceDndDropPerformedEvent)
+
+// SetDndDropPerformedHandler : sets handler for DataSourceDndDropPerformedEvent
+func (i *DataSource) SetDndDropPerformedHandler(f DataSourceDndDropPerformedHandlerFunc) {
+ i.dndDropPerformedHandler = f
+}
+
+// DataSourceDndFinishedEvent : the drag-and-drop operation concluded
+//
+// The drop destination finished interoperating with this data
+// source, so the client is now free to destroy this data source and
+// free all associated data.
+//
+// If the action used to perform the operation was "move", the
+// source can now delete the transferred data.
+type DataSourceDndFinishedEvent struct{}
+type DataSourceDndFinishedHandlerFunc func(DataSourceDndFinishedEvent)
+
+// SetDndFinishedHandler : sets handler for DataSourceDndFinishedEvent
+func (i *DataSource) SetDndFinishedHandler(f DataSourceDndFinishedHandlerFunc) {
+ i.dndFinishedHandler = f
+}
+
+// DataSourceActionEvent : notify the selected action
+//
+// This event indicates the action selected by the compositor after
+// matching the source/destination side actions. Only one action (or
+// none) will be offered here.
+//
+// This event can be emitted multiple times during the drag-and-drop
+// operation, mainly in response to destination side changes through
+// wl_data_offer.set_actions, and as the data device enters/leaves
+// surfaces.
+//
+// It is only possible to receive this event after
+// wl_data_source.dnd_drop_performed if the drag-and-drop operation
+// ended in an "ask" action, in which case the final wl_data_source.action
+// event will happen immediately before wl_data_source.dnd_finished.
+//
+// Compositors may also change the selected action on the fly, mainly
+// in response to keyboard modifier changes during the drag-and-drop
+// operation.
+//
+// The most recent action received is always the valid one. The chosen
+// action may change alongside negotiation (e.g. an "ask" action can turn
+// into a "move" operation), so the effects of the final action must
+// always be applied in wl_data_offer.dnd_finished.
+//
+// Clients can trigger cursor surface changes from this point, so
+// they reflect the current action.
+type DataSourceActionEvent struct {
+ DndAction uint32
+}
+type DataSourceActionHandlerFunc func(DataSourceActionEvent)
+
+// SetActionHandler : sets handler for DataSourceActionEvent
+func (i *DataSource) SetActionHandler(f DataSourceActionHandlerFunc) {
+ i.actionHandler = f
+}
+
+func (i *DataSource) Dispatch(opcode uint32, fd int, data []byte) {
+ switch opcode {
+ case 0:
+ if i.targetHandler == nil {
+ return
+ }
+ var e DataSourceTargetEvent
+ l := 0
+ mimeTypeLen := PaddedLen(int(Uint32(data[l : l+4])))
+ l += 4
+ e.MimeType = String(data[l : l+mimeTypeLen])
+ l += mimeTypeLen
+
+ i.targetHandler(e)
+ case 1:
+ if i.sendHandler == nil {
+ if fd != -1 {
+ unix.Close(fd)
+ }
+ return
+ }
+ var e DataSourceSendEvent
+ l := 0
+ mimeTypeLen := PaddedLen(int(Uint32(data[l : l+4])))
+ l += 4
+ e.MimeType = String(data[l : l+mimeTypeLen])
+ l += mimeTypeLen
+ e.Fd = fd
+
+ i.sendHandler(e)
+ case 2:
+ if i.cancelledHandler == nil {
+ return
+ }
+ var e DataSourceCancelledEvent
+
+ i.cancelledHandler(e)
+ case 3:
+ if i.dndDropPerformedHandler == nil {
+ return
+ }
+ var e DataSourceDndDropPerformedEvent
+
+ i.dndDropPerformedHandler(e)
+ case 4:
+ if i.dndFinishedHandler == nil {
+ return
+ }
+ var e DataSourceDndFinishedEvent
+
+ i.dndFinishedHandler(e)
+ case 5:
+ if i.actionHandler == nil {
+ return
+ }
+ var e DataSourceActionEvent
+ l := 0
+ e.DndAction = Uint32(data[l : l+4])
+ l += 4
+
+ i.actionHandler(e)
+ }
+}
+
+// DataDeviceInterfaceName 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 DataDeviceInterfaceName = "wl_data_device"
+
+// DataDevice : data transfer device
+//
+// There is one wl_data_device per seat which can be obtained
+// from the global wl_data_device_manager singleton.
+//
+// A wl_data_device provides access to inter-client data transfer
+// mechanisms such as copy-and-paste and drag-and-drop.
+type DataDevice struct {
+ BaseProxy
+ dataOfferHandler DataDeviceDataOfferHandlerFunc
+ enterHandler DataDeviceEnterHandlerFunc
+ leaveHandler DataDeviceLeaveHandlerFunc
+ motionHandler DataDeviceMotionHandlerFunc
+ dropHandler DataDeviceDropHandlerFunc
+ selectionHandler DataDeviceSelectionHandlerFunc
+}
+
+// NewDataDevice : data transfer device
+//
+// There is one wl_data_device per seat which can be obtained
+// from the global wl_data_device_manager singleton.
+//
+// A wl_data_device provides access to inter-client data transfer
+// mechanisms such as copy-and-paste and drag-and-drop.
+func NewDataDevice(ctx *Context) *DataDevice {
+ wlDataDevice := &DataDevice{}
+ ctx.Register(wlDataDevice)
+ return wlDataDevice
+}
+
+// StartDrag : start drag-and-drop operation
+//
+// This request asks the compositor to start a drag-and-drop
+// operation on behalf of the client.
+//
+// The source argument is the data source that provides the data
+// for the eventual data transfer. If source is NULL, enter, leave
+// and motion events are sent only to the client that initiated the
+// drag and the client is expected to handle the data passing
+// internally. If source is destroyed, the drag-and-drop session will be
+// cancelled.
+//
+// The origin surface is the surface where the drag originates and
+// the client must have an active implicit grab that matches the
+// serial.
+//
+// The icon surface is an optional (can be NULL) surface that
+// provides an icon to be moved around with the cursor. Initially,
+// the top-left corner of the icon surface is placed at the cursor
+// hotspot, but subsequent wl_surface.offset requests can move the
+// relative position. Attach requests must be confirmed with
+// wl_surface.commit as usual. The icon surface is given the role of
+// a drag-and-drop icon. If the icon surface already has another role,
+// it raises a protocol error.
+//
+// The input region is ignored for wl_surfaces with the role of a
+// drag-and-drop icon.
+//
+// The given source may not be used in any further set_selection or
+// start_drag requests. Attempting to reuse a previously-used source
+// may send a used_source error.
+//
+// source: data source for the eventual transfer
+// origin: surface where the drag originates
+// icon: drag-and-drop icon surface
+// serial: serial number of the implicit grab on the origin
+func (i *DataDevice) StartDrag(source *DataSource, origin, icon *Surface, serial uint32) error {
+ const opcode = 0
+ const _reqBufLen = 8 + 4 + 4 + 4 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ if source == nil {
+ PutUint32(_reqBuf[l:l+4], 0)
+ l += 4
+ } else {
+ PutUint32(_reqBuf[l:l+4], source.ID())
+ l += 4
+ }
+ PutUint32(_reqBuf[l:l+4], origin.ID())
+ l += 4
+ if icon == nil {
+ PutUint32(_reqBuf[l:l+4], 0)
+ l += 4
+ } else {
+ PutUint32(_reqBuf[l:l+4], icon.ID())
+ l += 4
+ }
+ PutUint32(_reqBuf[l:l+4], uint32(serial))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// SetSelection : copy data to the selection
+//
+// This request asks the compositor to set the selection
+// to the data from the source on behalf of the client.
+//
+// To unset the selection, set the source to NULL.
+//
+// The given source may not be used in any further set_selection or
+// start_drag requests. Attempting to reuse a previously-used source
+// may send a used_source error.
+//
+// source: data source for the selection
+// serial: serial number of the event that triggered this request
+func (i *DataDevice) SetSelection(source *DataSource, serial uint32) error {
+ const opcode = 1
+ const _reqBufLen = 8 + 4 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ if source == nil {
+ PutUint32(_reqBuf[l:l+4], 0)
+ l += 4
+ } else {
+ PutUint32(_reqBuf[l:l+4], source.ID())
+ l += 4
+ }
+ PutUint32(_reqBuf[l:l+4], uint32(serial))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// Release : destroy data device
+//
+// This request destroys the data device.
+func (i *DataDevice) Release() error {
+ defer i.MarkZombie()
+ const opcode = 2
+ const _reqBufLen = 8
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+type DataDeviceError uint32
+
+// DataDeviceError :
+const (
+ // DataDeviceErrorRole : given wl_surface has another role
+ DataDeviceErrorRole DataDeviceError = 0
+ // DataDeviceErrorUsedSource : source has already been used
+ DataDeviceErrorUsedSource DataDeviceError = 1
+)
+
+func (e DataDeviceError) Name() string {
+ switch e {
+ case DataDeviceErrorRole:
+ return "role"
+ case DataDeviceErrorUsedSource:
+ return "used_source"
+ default:
+ return ""
+ }
+}
+
+func (e DataDeviceError) Value() string {
+ switch e {
+ case DataDeviceErrorRole:
+ return "0"
+ case DataDeviceErrorUsedSource:
+ return "1"
+ default:
+ return ""
+ }
+}
+
+func (e DataDeviceError) String() string {
+ return e.Name() + "=" + e.Value()
+}
+
+// DataDeviceDataOfferEvent : introduce a new wl_data_offer
+//
+// The data_offer event introduces a new wl_data_offer object,
+// which will subsequently be used in either the
+// data_device.enter event (for drag-and-drop) or the
+// data_device.selection event (for selections). Immediately
+// following the data_device.data_offer event, the new data_offer
+// object will send out data_offer.offer events to describe the
+// mime types it offers.
+type DataDeviceDataOfferEvent struct {
+ Id *DataOffer
+}
+type DataDeviceDataOfferHandlerFunc func(DataDeviceDataOfferEvent)
+
+// SetDataOfferHandler : sets handler for DataDeviceDataOfferEvent
+func (i *DataDevice) SetDataOfferHandler(f DataDeviceDataOfferHandlerFunc) {
+ i.dataOfferHandler = f
+}
+
+// DataDeviceEnterEvent : initiate drag-and-drop session
+//
+// This event is sent when an active drag-and-drop pointer enters
+// a surface owned by the client. The position of the pointer at
+// enter time is provided by the x and y arguments, in surface-local
+// coordinates.
+type DataDeviceEnterEvent struct {
+ Serial uint32
+ Surface *Surface
+ X float64
+ Y float64
+ Id *DataOffer
+}
+type DataDeviceEnterHandlerFunc func(DataDeviceEnterEvent)
+
+// SetEnterHandler : sets handler for DataDeviceEnterEvent
+func (i *DataDevice) SetEnterHandler(f DataDeviceEnterHandlerFunc) {
+ i.enterHandler = f
+}
+
+// DataDeviceLeaveEvent : end drag-and-drop session
+//
+// This event is sent when the drag-and-drop pointer leaves the
+// surface and the session ends. The client must destroy the
+// wl_data_offer introduced at enter time at this point.
+type DataDeviceLeaveEvent struct{}
+type DataDeviceLeaveHandlerFunc func(DataDeviceLeaveEvent)
+
+// SetLeaveHandler : sets handler for DataDeviceLeaveEvent
+func (i *DataDevice) SetLeaveHandler(f DataDeviceLeaveHandlerFunc) {
+ i.leaveHandler = f
+}
+
+// DataDeviceMotionEvent : drag-and-drop session motion
+//
+// This event is sent when the drag-and-drop pointer moves within
+// the currently focused surface. The new position of the pointer
+// is provided by the x and y arguments, in surface-local
+// coordinates.
+type DataDeviceMotionEvent struct {
+ Time uint32
+ X float64
+ Y float64
+}
+type DataDeviceMotionHandlerFunc func(DataDeviceMotionEvent)
+
+// SetMotionHandler : sets handler for DataDeviceMotionEvent
+func (i *DataDevice) SetMotionHandler(f DataDeviceMotionHandlerFunc) {
+ i.motionHandler = f
+}
+
+// DataDeviceDropEvent : end drag-and-drop session successfully
+//
+// The event is sent when a drag-and-drop operation is ended
+// because the implicit grab is removed.
+//
+// The drag-and-drop destination is expected to honor the last action
+// received through wl_data_offer.action, if the resulting action is
+// "copy" or "move", the destination can still perform
+// wl_data_offer.receive requests, and is expected to end all
+// transfers with a wl_data_offer.finish request.
+//
+// If the resulting action is "ask", the action will not be considered
+// final. The drag-and-drop destination is expected to perform one last
+// wl_data_offer.set_actions request, or wl_data_offer.destroy in order
+// to cancel the operation.
+type DataDeviceDropEvent struct{}
+type DataDeviceDropHandlerFunc func(DataDeviceDropEvent)
+
+// SetDropHandler : sets handler for DataDeviceDropEvent
+func (i *DataDevice) SetDropHandler(f DataDeviceDropHandlerFunc) {
+ i.dropHandler = f
+}
+
+// DataDeviceSelectionEvent : advertise new selection
+//
+// The selection event is sent out to notify the client of a new
+// wl_data_offer for the selection for this device. The
+// data_device.data_offer and the data_offer.offer events are
+// sent out immediately before this event to introduce the data
+// offer object. The selection event is sent to a client
+// immediately before receiving keyboard focus and when a new
+// selection is set while the client has keyboard focus. The
+// data_offer is valid until a new data_offer or NULL is received
+// or until the client loses keyboard focus. Switching surface with
+// keyboard focus within the same client doesn't mean a new selection
+// will be sent. The client must destroy the previous selection
+// data_offer, if any, upon receiving this event.
+type DataDeviceSelectionEvent struct {
+ Id *DataOffer
+}
+type DataDeviceSelectionHandlerFunc func(DataDeviceSelectionEvent)
+
+// SetSelectionHandler : sets handler for DataDeviceSelectionEvent
+func (i *DataDevice) SetSelectionHandler(f DataDeviceSelectionHandlerFunc) {
+ i.selectionHandler = f
+}
+
+func (i *DataDevice) Dispatch(opcode uint32, fd int, data []byte) {
+ switch opcode {
+ case 0:
+ if i.dataOfferHandler == nil {
+ return
+ }
+ var e DataDeviceDataOfferEvent
+ l := 0
+ e.Id = i.Context().GetProxy(Uint32(data[l : l+4])).(*DataOffer)
+ l += 4
+
+ i.dataOfferHandler(e)
+ case 1:
+ if i.enterHandler == nil {
+ return
+ }
+ var e DataDeviceEnterEvent
+ l := 0
+ e.Serial = Uint32(data[l : l+4])
+ l += 4
+ e.Surface = i.Context().GetProxy(Uint32(data[l : l+4])).(*Surface)
+ l += 4
+ e.X = Fixed(data[l : l+4])
+ l += 4
+ e.Y = Fixed(data[l : l+4])
+ l += 4
+ e.Id = i.Context().GetProxy(Uint32(data[l : l+4])).(*DataOffer)
+ l += 4
+
+ i.enterHandler(e)
+ case 2:
+ if i.leaveHandler == nil {
+ return
+ }
+ var e DataDeviceLeaveEvent
+
+ i.leaveHandler(e)
+ case 3:
+ if i.motionHandler == nil {
+ return
+ }
+ var e DataDeviceMotionEvent
+ l := 0
+ e.Time = Uint32(data[l : l+4])
+ l += 4
+ e.X = Fixed(data[l : l+4])
+ l += 4
+ e.Y = Fixed(data[l : l+4])
+ l += 4
+
+ i.motionHandler(e)
+ case 4:
+ if i.dropHandler == nil {
+ return
+ }
+ var e DataDeviceDropEvent
+
+ i.dropHandler(e)
+ case 5:
+ if i.selectionHandler == nil {
+ return
+ }
+ var e DataDeviceSelectionEvent
+ l := 0
+ e.Id = i.Context().GetProxy(Uint32(data[l : l+4])).(*DataOffer)
+ l += 4
+
+ i.selectionHandler(e)
+ }
+}
+
+// DataDeviceManagerInterfaceName 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 DataDeviceManagerInterfaceName = "wl_data_device_manager"
+
+// DataDeviceManager : data transfer interface
+//
+// The wl_data_device_manager is a singleton global object that
+// provides access to inter-client data transfer mechanisms such as
+// copy-and-paste and drag-and-drop. These mechanisms are tied to
+// a wl_seat and this interface lets a client get a wl_data_device
+// corresponding to a wl_seat.
+//
+// Depending on the version bound, the objects created from the bound
+// wl_data_device_manager object will have different requirements for
+// functioning properly. See wl_data_source.set_actions,
+// wl_data_offer.accept and wl_data_offer.finish for details.
+type DataDeviceManager struct {
+ BaseProxy
+}
+
+// NewDataDeviceManager : data transfer interface
+//
+// The wl_data_device_manager is a singleton global object that
+// provides access to inter-client data transfer mechanisms such as
+// copy-and-paste and drag-and-drop. These mechanisms are tied to
+// a wl_seat and this interface lets a client get a wl_data_device
+// corresponding to a wl_seat.
+//
+// Depending on the version bound, the objects created from the bound
+// wl_data_device_manager object will have different requirements for
+// functioning properly. See wl_data_source.set_actions,
+// wl_data_offer.accept and wl_data_offer.finish for details.
+func NewDataDeviceManager(ctx *Context) *DataDeviceManager {
+ wlDataDeviceManager := &DataDeviceManager{}
+ ctx.Register(wlDataDeviceManager)
+ return wlDataDeviceManager
+}
+
+// CreateDataSource : create a new data source
+//
+// Create a new data source.
+func (i *DataDeviceManager) CreateDataSource() (*DataSource, error) {
+ id := NewDataSource(i.Context())
+ const opcode = 0
+ const _reqBufLen = 8 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], id.ID())
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return id, err
+}
+
+// GetDataDevice : create a new data device
+//
+// Create a new data device for a given seat.
+//
+// seat: seat associated with the data device
+func (i *DataDeviceManager) GetDataDevice(seat *Seat) (*DataDevice, error) {
+ id := NewDataDevice(i.Context())
+ const opcode = 1
+ const _reqBufLen = 8 + 4 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], id.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], seat.ID())
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return id, err
+}
+
+func (i *DataDeviceManager) Destroy() error {
+ i.MarkZombie()
+ return nil
+}
+
+type DataDeviceManagerDndAction uint32
+
+// DataDeviceManagerDndAction : drag and drop actions
+//
+// This is a bitmask of the available/preferred actions in a
+// drag-and-drop operation.
+//
+// In the compositor, the selected action is a result of matching the
+// actions offered by the source and destination sides. "action" events
+// with a "none" action will be sent to both source and destination if
+// there is no match. All further checks will effectively happen on
+// (source actions ∩ destination actions).
+//
+// In addition, compositors may also pick different actions in
+// reaction to key modifiers being pressed. One common design that
+// is used in major toolkits (and the behavior recommended for
+// compositors) is:
+//
+// - If no modifiers are pressed, the first match (in bit order)
+// will be used.
+// - Pressing Shift selects "move", if enabled in the mask.
+// - Pressing Control selects "copy", if enabled in the mask.
+//
+// Behavior beyond that is considered implementation-dependent.
+// Compositors may for example bind other modifiers (like Alt/Meta)
+// or drags initiated with other buttons than BTN_LEFT to specific
+// actions (e.g. "ask").
+const (
+ // DataDeviceManagerDndActionNone : no action
+ DataDeviceManagerDndActionNone DataDeviceManagerDndAction = 0
+ // DataDeviceManagerDndActionCopy : copy action
+ DataDeviceManagerDndActionCopy DataDeviceManagerDndAction = 1
+ // DataDeviceManagerDndActionMove : move action
+ DataDeviceManagerDndActionMove DataDeviceManagerDndAction = 2
+ // DataDeviceManagerDndActionAsk : ask action
+ DataDeviceManagerDndActionAsk DataDeviceManagerDndAction = 4
+)
+
+func (e DataDeviceManagerDndAction) Name() string {
+ switch e {
+ case DataDeviceManagerDndActionNone:
+ return "none"
+ case DataDeviceManagerDndActionCopy:
+ return "copy"
+ case DataDeviceManagerDndActionMove:
+ return "move"
+ case DataDeviceManagerDndActionAsk:
+ return "ask"
+ default:
+ return ""
+ }
+}
+
+func (e DataDeviceManagerDndAction) Value() string {
+ switch e {
+ case DataDeviceManagerDndActionNone:
+ return "0"
+ case DataDeviceManagerDndActionCopy:
+ return "1"
+ case DataDeviceManagerDndActionMove:
+ return "2"
+ case DataDeviceManagerDndActionAsk:
+ return "4"
+ default:
+ return ""
+ }
+}
+
+func (e DataDeviceManagerDndAction) String() string {
+ return e.Name() + "=" + e.Value()
+}
+
+// ShellInterfaceName 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 ShellInterfaceName = "wl_shell"
+
+// Shell : create desktop-style surfaces
+//
+// This interface is implemented by servers that provide
+// desktop-style user interfaces.
+//
+// It allows clients to associate a wl_shell_surface with
+// a basic surface.
+//
+// Note! This protocol is deprecated and not intended for production use.
+// For desktop-style user interfaces, use xdg_shell. Compositors and clients
+// should not implement this interface.
+type Shell struct {
+ BaseProxy
+}
+
+// NewShell : create desktop-style surfaces
+//
+// This interface is implemented by servers that provide
+// desktop-style user interfaces.
+//
+// It allows clients to associate a wl_shell_surface with
+// a basic surface.
+//
+// Note! This protocol is deprecated and not intended for production use.
+// For desktop-style user interfaces, use xdg_shell. Compositors and clients
+// should not implement this interface.
+func NewShell(ctx *Context) *Shell {
+ wlShell := &Shell{}
+ ctx.Register(wlShell)
+ return wlShell
+}
+
+// GetShellSurface : create a shell surface from a surface
+//
+// Create a shell surface for an existing surface. This gives
+// the wl_surface the role of a shell surface. If the wl_surface
+// already has another role, it raises a protocol error.
+//
+// Only one shell surface can be associated with a given surface.
+//
+// surface: surface to be given the shell surface role
+func (i *Shell) GetShellSurface(surface *Surface) (*ShellSurface, error) {
+ id := NewShellSurface(i.Context())
+ const opcode = 0
+ const _reqBufLen = 8 + 4 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], id.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], surface.ID())
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return id, err
+}
+
+func (i *Shell) Destroy() error {
+ i.MarkZombie()
+ return nil
+}
+
+type ShellError uint32
+
+// ShellError :
+const (
+ // ShellErrorRole : given wl_surface has another role
+ ShellErrorRole ShellError = 0
+)
+
+func (e ShellError) Name() string {
+ switch e {
+ case ShellErrorRole:
+ return "role"
+ default:
+ return ""
+ }
+}
+
+func (e ShellError) Value() string {
+ switch e {
+ case ShellErrorRole:
+ return "0"
+ default:
+ return ""
+ }
+}
+
+func (e ShellError) String() string {
+ return e.Name() + "=" + e.Value()
+}
+
+// ShellSurfaceInterfaceName 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 ShellSurfaceInterfaceName = "wl_shell_surface"
+
+// ShellSurface : desktop-style metadata interface
+//
+// An interface that may be implemented by a wl_surface, for
+// implementations that provide a desktop-style user interface.
+//
+// It provides requests to treat surfaces like toplevel, fullscreen
+// or popup windows, move, resize or maximize them, associate
+// metadata like title and class, etc.
+//
+// On the server side the object is automatically destroyed when
+// the related wl_surface is destroyed. On the client side,
+// wl_shell_surface_destroy() must be called before destroying
+// the wl_surface object.
+type ShellSurface struct {
+ BaseProxy
+ pingHandler ShellSurfacePingHandlerFunc
+ configureHandler ShellSurfaceConfigureHandlerFunc
+ popupDoneHandler ShellSurfacePopupDoneHandlerFunc
+}
+
+// NewShellSurface : desktop-style metadata interface
+//
+// An interface that may be implemented by a wl_surface, for
+// implementations that provide a desktop-style user interface.
+//
+// It provides requests to treat surfaces like toplevel, fullscreen
+// or popup windows, move, resize or maximize them, associate
+// metadata like title and class, etc.
+//
+// On the server side the object is automatically destroyed when
+// the related wl_surface is destroyed. On the client side,
+// wl_shell_surface_destroy() must be called before destroying
+// the wl_surface object.
+func NewShellSurface(ctx *Context) *ShellSurface {
+ wlShellSurface := &ShellSurface{}
+ ctx.Register(wlShellSurface)
+ return wlShellSurface
+}
+
+// Pong : respond to a ping event
+//
+// A client must respond to a ping event with a pong request or
+// the client may be deemed unresponsive.
+//
+// serial: serial number of the ping event
+func (i *ShellSurface) Pong(serial uint32) error {
+ const opcode = 0
+ const _reqBufLen = 8 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(serial))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// Move : start an interactive move
+//
+// Start a pointer-driven move of the surface.
+//
+// This request must be used in response to a button press event.
+// The server may ignore move requests depending on the state of
+// the surface (e.g. fullscreen or maximized).
+//
+// seat: seat whose pointer is used
+// serial: serial number of the implicit grab on the pointer
+func (i *ShellSurface) Move(seat *Seat, serial uint32) error {
+ const opcode = 1
+ const _reqBufLen = 8 + 4 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], seat.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(serial))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// Resize : start an interactive resize
+//
+// Start a pointer-driven resizing of the surface.
+//
+// This request must be used in response to a button press event.
+// The server may ignore resize requests depending on the state of
+// the surface (e.g. fullscreen or maximized).
+//
+// seat: seat whose pointer is used
+// serial: serial number of the implicit grab on the pointer
+// edges: which edge or corner is being dragged
+func (i *ShellSurface) Resize(seat *Seat, serial, edges uint32) error {
+ const opcode = 2
+ const _reqBufLen = 8 + 4 + 4 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], seat.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(serial))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(edges))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// SetToplevel : make the surface a toplevel surface
+//
+// Map the surface as a toplevel surface.
+//
+// A toplevel surface is not fullscreen, maximized or transient.
+func (i *ShellSurface) SetToplevel() error {
+ const opcode = 3
+ const _reqBufLen = 8
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// SetTransient : make the surface a transient surface
+//
+// Map the surface relative to an existing surface.
+//
+// The x and y arguments specify the location of the upper left
+// corner of the surface relative to the upper left corner of the
+// parent surface, in surface-local coordinates.
+//
+// The flags argument controls details of the transient behaviour.
+//
+// parent: parent surface
+// x: surface-local x coordinate
+// y: surface-local y coordinate
+// flags: transient surface behavior
+func (i *ShellSurface) SetTransient(parent *Surface, x, y int32, flags uint32) error {
+ const opcode = 4
+ const _reqBufLen = 8 + 4 + 4 + 4 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], parent.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(x))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(y))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(flags))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// SetFullscreen : make the surface a fullscreen surface
+//
+// Map the surface as a fullscreen surface.
+//
+// If an output parameter is given then the surface will be made
+// fullscreen on that output. If the client does not specify the
+// output then the compositor will apply its policy - usually
+// choosing the output on which the surface has the biggest surface
+// area.
+//
+// The client may specify a method to resolve a size conflict
+// between the output size and the surface size - this is provided
+// through the method parameter.
+//
+// The framerate parameter is used only when the method is set
+// to "driver", to indicate the preferred framerate. A value of 0
+// indicates that the client does not care about framerate. The
+// framerate is specified in mHz, that is framerate of 60000 is 60Hz.
+//
+// A method of "scale" or "driver" implies a scaling operation of
+// the surface, either via a direct scaling operation or a change of
+// the output mode. This will override any kind of output scaling, so
+// that mapping a surface with a buffer size equal to the mode can
+// fill the screen independent of buffer_scale.
+//
+// A method of "fill" means we don't scale up the buffer, however
+// any output scale is applied. This means that you may run into
+// an edge case where the application maps a buffer with the same
+// size of the output mode but buffer_scale 1 (thus making a
+// surface larger than the output). In this case it is allowed to
+// downscale the results to fit the screen.
+//
+// The compositor must reply to this request with a configure event
+// with the dimensions for the output on which the surface will
+// be made fullscreen.
+//
+// method: method for resolving size conflict
+// framerate: framerate in mHz
+// output: output on which the surface is to be fullscreen
+func (i *ShellSurface) SetFullscreen(method, framerate uint32, output *Output) error {
+ const opcode = 5
+ const _reqBufLen = 8 + 4 + 4 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(method))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(framerate))
+ l += 4
+ if output == nil {
+ PutUint32(_reqBuf[l:l+4], 0)
+ l += 4
+ } else {
+ PutUint32(_reqBuf[l:l+4], output.ID())
+ l += 4
+ }
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// SetPopup : make the surface a popup surface
+//
+// Map the surface as a popup.
+//
+// A popup surface is a transient surface with an added pointer
+// grab.
+//
+// An existing implicit grab will be changed to owner-events mode,
+// and the popup grab will continue after the implicit grab ends
+// (i.e. releasing the mouse button does not cause the popup to
+// be unmapped).
+//
+// The popup grab continues until the window is destroyed or a
+// mouse button is pressed in any other client's window. A click
+// in any of the client's surfaces is reported as normal, however,
+// clicks in other clients' surfaces will be discarded and trigger
+// the callback.
+//
+// The x and y arguments specify the location of the upper left
+// corner of the surface relative to the upper left corner of the
+// parent surface, in surface-local coordinates.
+//
+// seat: seat whose pointer is used
+// serial: serial number of the implicit grab on the pointer
+// parent: parent surface
+// x: surface-local x coordinate
+// y: surface-local y coordinate
+// flags: transient surface behavior
+func (i *ShellSurface) SetPopup(seat *Seat, serial uint32, parent *Surface, x, y int32, flags uint32) error {
+ const opcode = 6
+ const _reqBufLen = 8 + 4 + 4 + 4 + 4 + 4 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], seat.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(serial))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], parent.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(x))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(y))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(flags))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// SetMaximized : make the surface a maximized surface
+//
+// Map the surface as a maximized surface.
+//
+// If an output parameter is given then the surface will be
+// maximized on that output. If the client does not specify the
+// output then the compositor will apply its policy - usually
+// choosing the output on which the surface has the biggest surface
+// area.
+//
+// The compositor will reply with a configure event telling
+// the expected new surface size. The operation is completed
+// on the next buffer attach to this surface.
+//
+// A maximized surface typically fills the entire output it is
+// bound to, except for desktop elements such as panels. This is
+// the main difference between a maximized shell surface and a
+// fullscreen shell surface.
+//
+// The details depend on the compositor implementation.
+//
+// output: output on which the surface is to be maximized
+func (i *ShellSurface) SetMaximized(output *Output) error {
+ const opcode = 7
+ const _reqBufLen = 8 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ if output == nil {
+ PutUint32(_reqBuf[l:l+4], 0)
+ l += 4
+ } else {
+ PutUint32(_reqBuf[l:l+4], output.ID())
+ l += 4
+ }
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// SetTitle : set surface title
+//
+// Set a short title for the surface.
+//
+// This string may be used to identify the surface in a task bar,
+// window list, or other user interface elements provided by the
+// compositor.
+//
+// The string must be encoded in UTF-8.
+//
+// title: surface title
+func (i *ShellSurface) SetTitle(title string) error {
+ const opcode = 8
+ titleLen := PaddedLen(len(title) + 1)
+ _reqBufLen := 8 + (4 + titleLen)
+ _reqBuf := make([]byte, _reqBufLen)
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutString(_reqBuf[l:l+(4+titleLen)], title)
+ l += (4 + titleLen)
+ err := i.Context().WriteMsg(_reqBuf, nil)
+ return err
+}
+
+// SetClass : set surface class
+//
+// Set a class for the surface.
+//
+// The surface class identifies the general class of applications
+// to which the surface belongs. A common convention is to use the
+// file name (or the full path if it is a non-standard location) of
+// the application's .desktop file as the class.
+//
+// class: surface class
+func (i *ShellSurface) SetClass(class string) error {
+ const opcode = 9
+ classLen := PaddedLen(len(class) + 1)
+ _reqBufLen := 8 + (4 + classLen)
+ _reqBuf := make([]byte, _reqBufLen)
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutString(_reqBuf[l:l+(4+classLen)], class)
+ l += (4 + classLen)
+ err := i.Context().WriteMsg(_reqBuf, nil)
+ return err
+}
+
+func (i *ShellSurface) Destroy() error {
+ i.MarkZombie()
+ return nil
+}
+
+type ShellSurfaceResize uint32
+
+// ShellSurfaceResize : edge values for resizing
+//
+// These values are used to indicate which edge of a surface
+// is being dragged in a resize operation. The server may
+// use this information to adapt its behavior, e.g. choose
+// an appropriate cursor image.
+const (
+ // ShellSurfaceResizeNone : no edge
+ ShellSurfaceResizeNone ShellSurfaceResize = 0
+ // ShellSurfaceResizeTop : top edge
+ ShellSurfaceResizeTop ShellSurfaceResize = 1
+ // ShellSurfaceResizeBottom : bottom edge
+ ShellSurfaceResizeBottom ShellSurfaceResize = 2
+ // ShellSurfaceResizeLeft : left edge
+ ShellSurfaceResizeLeft ShellSurfaceResize = 4
+ // ShellSurfaceResizeTopLeft : top and left edges
+ ShellSurfaceResizeTopLeft ShellSurfaceResize = 5
+ // ShellSurfaceResizeBottomLeft : bottom and left edges
+ ShellSurfaceResizeBottomLeft ShellSurfaceResize = 6
+ // ShellSurfaceResizeRight : right edge
+ ShellSurfaceResizeRight ShellSurfaceResize = 8
+ // ShellSurfaceResizeTopRight : top and right edges
+ ShellSurfaceResizeTopRight ShellSurfaceResize = 9
+ // ShellSurfaceResizeBottomRight : bottom and right edges
+ ShellSurfaceResizeBottomRight ShellSurfaceResize = 10
+)
+
+func (e ShellSurfaceResize) Name() string {
+ switch e {
+ case ShellSurfaceResizeNone:
+ return "none"
+ case ShellSurfaceResizeTop:
+ return "top"
+ case ShellSurfaceResizeBottom:
+ return "bottom"
+ case ShellSurfaceResizeLeft:
+ return "left"
+ case ShellSurfaceResizeTopLeft:
+ return "top_left"
+ case ShellSurfaceResizeBottomLeft:
+ return "bottom_left"
+ case ShellSurfaceResizeRight:
+ return "right"
+ case ShellSurfaceResizeTopRight:
+ return "top_right"
+ case ShellSurfaceResizeBottomRight:
+ return "bottom_right"
+ default:
+ return ""
+ }
+}
+
+func (e ShellSurfaceResize) Value() string {
+ switch e {
+ case ShellSurfaceResizeNone:
+ return "0"
+ case ShellSurfaceResizeTop:
+ return "1"
+ case ShellSurfaceResizeBottom:
+ return "2"
+ case ShellSurfaceResizeLeft:
+ return "4"
+ case ShellSurfaceResizeTopLeft:
+ return "5"
+ case ShellSurfaceResizeBottomLeft:
+ return "6"
+ case ShellSurfaceResizeRight:
+ return "8"
+ case ShellSurfaceResizeTopRight:
+ return "9"
+ case ShellSurfaceResizeBottomRight:
+ return "10"
+ default:
+ return ""
+ }
+}
+
+func (e ShellSurfaceResize) String() string {
+ return e.Name() + "=" + e.Value()
+}
+
+type ShellSurfaceTransient uint32
+
+// ShellSurfaceTransient : details of transient behaviour
+//
+// These flags specify details of the expected behaviour
+// of transient surfaces. Used in the set_transient request.
+const (
+ // ShellSurfaceTransientInactive : do not set keyboard focus
+ ShellSurfaceTransientInactive ShellSurfaceTransient = 0x1
+)
+
+func (e ShellSurfaceTransient) Name() string {
+ switch e {
+ case ShellSurfaceTransientInactive:
+ return "inactive"
+ default:
+ return ""
+ }
+}
+
+func (e ShellSurfaceTransient) Value() string {
+ switch e {
+ case ShellSurfaceTransientInactive:
+ return "0x1"
+ default:
+ return ""
+ }
+}
+
+func (e ShellSurfaceTransient) String() string {
+ return e.Name() + "=" + e.Value()
+}
+
+type ShellSurfaceFullscreenMethod uint32
+
+// ShellSurfaceFullscreenMethod : different method to set the surface fullscreen
+//
+// Hints to indicate to the compositor how to deal with a conflict
+// between the dimensions of the surface and the dimensions of the
+// output. The compositor is free to ignore this parameter.
+const (
+ // ShellSurfaceFullscreenMethodDefault : no preference, apply default policy
+ ShellSurfaceFullscreenMethodDefault ShellSurfaceFullscreenMethod = 0
+ // ShellSurfaceFullscreenMethodScale : scale, preserve the surface's aspect ratio and center on output
+ ShellSurfaceFullscreenMethodScale ShellSurfaceFullscreenMethod = 1
+ // ShellSurfaceFullscreenMethodDriver : switch output mode to the smallest mode that can fit the surface, add black borders to compensate size mismatch
+ ShellSurfaceFullscreenMethodDriver ShellSurfaceFullscreenMethod = 2
+ // ShellSurfaceFullscreenMethodFill : no upscaling, center on output and add black borders to compensate size mismatch
+ ShellSurfaceFullscreenMethodFill ShellSurfaceFullscreenMethod = 3
+)
+
+func (e ShellSurfaceFullscreenMethod) Name() string {
+ switch e {
+ case ShellSurfaceFullscreenMethodDefault:
+ return "default"
+ case ShellSurfaceFullscreenMethodScale:
+ return "scale"
+ case ShellSurfaceFullscreenMethodDriver:
+ return "driver"
+ case ShellSurfaceFullscreenMethodFill:
+ return "fill"
+ default:
+ return ""
+ }
+}
+
+func (e ShellSurfaceFullscreenMethod) Value() string {
+ switch e {
+ case ShellSurfaceFullscreenMethodDefault:
+ return "0"
+ case ShellSurfaceFullscreenMethodScale:
+ return "1"
+ case ShellSurfaceFullscreenMethodDriver:
+ return "2"
+ case ShellSurfaceFullscreenMethodFill:
+ return "3"
+ default:
+ return ""
+ }
+}
+
+func (e ShellSurfaceFullscreenMethod) String() string {
+ return e.Name() + "=" + e.Value()
+}
+
+// ShellSurfacePingEvent : ping client
+//
+// Ping a client to check if it is receiving events and sending
+// requests. A client is expected to reply with a pong request.
+type ShellSurfacePingEvent struct {
+ Serial uint32
+}
+type ShellSurfacePingHandlerFunc func(ShellSurfacePingEvent)
+
+// SetPingHandler : sets handler for ShellSurfacePingEvent
+func (i *ShellSurface) SetPingHandler(f ShellSurfacePingHandlerFunc) {
+ i.pingHandler = f
+}
+
+// ShellSurfaceConfigureEvent : suggest resize
+//
+// The configure event asks the client to resize its surface.
+//
+// The size is a hint, in the sense that the client is free to
+// ignore it if it doesn't resize, pick a smaller size (to
+// satisfy aspect ratio or resize in steps of NxM pixels).
+//
+// The edges parameter provides a hint about how the surface
+// was resized. The client may use this information to decide
+// how to adjust its content to the new size (e.g. a scrolling
+// area might adjust its content position to leave the viewable
+// content unmoved).
+//
+// The client is free to dismiss all but the last configure
+// event it received.
+//
+// The width and height arguments specify the size of the window
+// in surface-local coordinates.
+type ShellSurfaceConfigureEvent struct {
+ Edges uint32
+ Width int32
+ Height int32
+}
+type ShellSurfaceConfigureHandlerFunc func(ShellSurfaceConfigureEvent)
+
+// SetConfigureHandler : sets handler for ShellSurfaceConfigureEvent
+func (i *ShellSurface) SetConfigureHandler(f ShellSurfaceConfigureHandlerFunc) {
+ i.configureHandler = f
+}
+
+// ShellSurfacePopupDoneEvent : popup interaction is done
+//
+// The popup_done event is sent out when a popup grab is broken,
+// that is, when the user clicks a surface that doesn't belong
+// to the client owning the popup surface.
+type ShellSurfacePopupDoneEvent struct{}
+type ShellSurfacePopupDoneHandlerFunc func(ShellSurfacePopupDoneEvent)
+
+// SetPopupDoneHandler : sets handler for ShellSurfacePopupDoneEvent
+func (i *ShellSurface) SetPopupDoneHandler(f ShellSurfacePopupDoneHandlerFunc) {
+ i.popupDoneHandler = f
+}
+
+func (i *ShellSurface) Dispatch(opcode uint32, fd int, data []byte) {
+ switch opcode {
+ case 0:
+ if i.pingHandler == nil {
+ return
+ }
+ var e ShellSurfacePingEvent
+ l := 0
+ e.Serial = Uint32(data[l : l+4])
+ l += 4
+
+ i.pingHandler(e)
+ case 1:
+ if i.configureHandler == nil {
+ return
+ }
+ var e ShellSurfaceConfigureEvent
+ l := 0
+ e.Edges = Uint32(data[l : l+4])
+ l += 4
+ e.Width = int32(Uint32(data[l : l+4]))
+ l += 4
+ e.Height = int32(Uint32(data[l : l+4]))
+ l += 4
+
+ i.configureHandler(e)
+ case 2:
+ if i.popupDoneHandler == nil {
+ return
+ }
+ var e ShellSurfacePopupDoneEvent
+
+ i.popupDoneHandler(e)
+ }
+}
+
+// SurfaceInterfaceName 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 SurfaceInterfaceName = "wl_surface"
+
+// Surface : an onscreen surface
+//
+// A surface is a rectangular area that may be displayed on zero
+// or more outputs, and shown any number of times at the compositor's
+// discretion. They can present wl_buffers, receive user input, and
+// define a local coordinate system.
+//
+// The size of a surface (and relative positions on it) is described
+// in surface-local coordinates, which may differ from the buffer
+// coordinates of the pixel content, in case a buffer_transform
+// or a buffer_scale is used.
+//
+// A surface without a "role" is fairly useless: a compositor does
+// not know where, when or how to present it. The role is the
+// purpose of a wl_surface. Examples of roles are a cursor for a
+// pointer (as set by wl_pointer.set_cursor), a drag icon
+// (wl_data_device.start_drag), a sub-surface
+// (wl_subcompositor.get_subsurface), and a window as defined by a
+// shell protocol (e.g. wl_shell.get_shell_surface).
+//
+// A surface can have only one role at a time. Initially a
+// wl_surface does not have a role. Once a wl_surface is given a
+// role, it is set permanently for the whole lifetime of the
+// wl_surface object. Giving the current role again is allowed,
+// unless explicitly forbidden by the relevant interface
+// specification.
+//
+// Surface roles are given by requests in other interfaces such as
+// wl_pointer.set_cursor. The request should explicitly mention
+// that this request gives a role to a wl_surface. Often, this
+// request also creates a new protocol object that represents the
+// role and adds additional functionality to wl_surface. When a
+// client wants to destroy a wl_surface, they must destroy this role
+// object before the wl_surface, otherwise a defunct_role_object error is
+// sent.
+//
+// Destroying the role object does not remove the role from the
+// wl_surface, but it may stop the wl_surface from "playing the role".
+// For instance, if a wl_subsurface object is destroyed, the wl_surface
+// it was created for will be unmapped and forget its position and
+// z-order. It is allowed to create a wl_subsurface for the same
+// wl_surface again, but it is not allowed to use the wl_surface as
+// a cursor (cursor is a different role than sub-surface, and role
+// switching is not allowed).
+type Surface struct {
+ BaseProxy
+ enterHandler SurfaceEnterHandlerFunc
+ leaveHandler SurfaceLeaveHandlerFunc
+ preferredBufferScaleHandler SurfacePreferredBufferScaleHandlerFunc
+ preferredBufferTransformHandler SurfacePreferredBufferTransformHandlerFunc
+}
+
+// NewSurface : an onscreen surface
+//
+// A surface is a rectangular area that may be displayed on zero
+// or more outputs, and shown any number of times at the compositor's
+// discretion. They can present wl_buffers, receive user input, and
+// define a local coordinate system.
+//
+// The size of a surface (and relative positions on it) is described
+// in surface-local coordinates, which may differ from the buffer
+// coordinates of the pixel content, in case a buffer_transform
+// or a buffer_scale is used.
+//
+// A surface without a "role" is fairly useless: a compositor does
+// not know where, when or how to present it. The role is the
+// purpose of a wl_surface. Examples of roles are a cursor for a
+// pointer (as set by wl_pointer.set_cursor), a drag icon
+// (wl_data_device.start_drag), a sub-surface
+// (wl_subcompositor.get_subsurface), and a window as defined by a
+// shell protocol (e.g. wl_shell.get_shell_surface).
+//
+// A surface can have only one role at a time. Initially a
+// wl_surface does not have a role. Once a wl_surface is given a
+// role, it is set permanently for the whole lifetime of the
+// wl_surface object. Giving the current role again is allowed,
+// unless explicitly forbidden by the relevant interface
+// specification.
+//
+// Surface roles are given by requests in other interfaces such as
+// wl_pointer.set_cursor. The request should explicitly mention
+// that this request gives a role to a wl_surface. Often, this
+// request also creates a new protocol object that represents the
+// role and adds additional functionality to wl_surface. When a
+// client wants to destroy a wl_surface, they must destroy this role
+// object before the wl_surface, otherwise a defunct_role_object error is
+// sent.
+//
+// Destroying the role object does not remove the role from the
+// wl_surface, but it may stop the wl_surface from "playing the role".
+// For instance, if a wl_subsurface object is destroyed, the wl_surface
+// it was created for will be unmapped and forget its position and
+// z-order. It is allowed to create a wl_subsurface for the same
+// wl_surface again, but it is not allowed to use the wl_surface as
+// a cursor (cursor is a different role than sub-surface, and role
+// switching is not allowed).
+func NewSurface(ctx *Context) *Surface {
+ wlSurface := &Surface{}
+ ctx.Register(wlSurface)
+ return wlSurface
+}
+
+// Destroy : delete surface
+//
+// Deletes the surface and invalidates its object ID.
+func (i *Surface) Destroy() error {
+ defer i.MarkZombie()
+ const opcode = 0
+ const _reqBufLen = 8
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// Attach : set the surface contents
+//
+// Set a buffer as the content of this surface.
+//
+// The new size of the surface is calculated based on the buffer
+// size transformed by the inverse buffer_transform and the
+// inverse buffer_scale. This means that at commit time the supplied
+// buffer size must be an integer multiple of the buffer_scale. If
+// that's not the case, an invalid_size error is sent.
+//
+// The x and y arguments specify the location of the new pending
+// buffer's upper left corner, relative to the current buffer's upper
+// left corner, in surface-local coordinates. In other words, the
+// x and y, combined with the new surface size define in which
+// directions the surface's size changes. Setting anything other than 0
+// as x and y arguments is discouraged, and should instead be replaced
+// with using the separate wl_surface.offset request.
+//
+// When the bound wl_surface version is 5 or higher, passing any
+// non-zero x or y is a protocol violation, and will result in an
+// 'invalid_offset' error being raised. The x and y arguments are ignored
+// and do not change the pending state. To achieve equivalent semantics,
+// use wl_surface.offset.
+//
+// Surface contents are double-buffered state, see wl_surface.commit.
+//
+// The initial surface contents are void; there is no content.
+// wl_surface.attach assigns the given wl_buffer as the pending
+// wl_buffer. wl_surface.commit makes the pending wl_buffer the new
+// surface contents, and the size of the surface becomes the size
+// calculated from the wl_buffer, as described above. After commit,
+// there is no pending buffer until the next attach.
+//
+// Committing a pending wl_buffer allows the compositor to read the
+// pixels in the wl_buffer. The compositor may access the pixels at
+// any time after the wl_surface.commit request. When the compositor
+// will not access the pixels anymore, it will send the
+// wl_buffer.release event. Only after receiving wl_buffer.release,
+// the client may reuse the wl_buffer. A wl_buffer that has been
+// attached and then replaced by another attach instead of committed
+// will not receive a release event, and is not used by the
+// compositor.
+//
+// If a pending wl_buffer has been committed to more than one wl_surface,
+// the delivery of wl_buffer.release events becomes undefined. A well
+// behaved client should not rely on wl_buffer.release events in this
+// case. Alternatively, a client could create multiple wl_buffer objects
+// from the same backing storage or use a protocol extension providing
+// per-commit release notifications.
+//
+// Destroying the wl_buffer after wl_buffer.release does not change
+// the surface contents. Destroying the wl_buffer before wl_buffer.release
+// is allowed as long as the underlying buffer storage isn't re-used (this
+// can happen e.g. on client process termination). However, if the client
+// destroys the wl_buffer before receiving the wl_buffer.release event and
+// mutates the underlying buffer storage, the surface contents become
+// undefined immediately.
+//
+// If wl_surface.attach is sent with a NULL wl_buffer, the
+// following wl_surface.commit will remove the surface content.
+//
+// If a pending wl_buffer has been destroyed, the result is not specified.
+// Many compositors are known to remove the surface content on the following
+// wl_surface.commit, but this behaviour is not universal. Clients seeking to
+// maximise compatibility should not destroy pending buffers and should
+// ensure that they explicitly remove content from surfaces, even after
+// destroying buffers.
+//
+// buffer: buffer of surface contents
+// x: surface-local x coordinate
+// y: surface-local y coordinate
+func (i *Surface) Attach(buffer *Buffer, x, y int32) error {
+ const opcode = 1
+ const _reqBufLen = 8 + 4 + 4 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ if buffer == nil {
+ PutUint32(_reqBuf[l:l+4], 0)
+ l += 4
+ } else {
+ PutUint32(_reqBuf[l:l+4], buffer.ID())
+ l += 4
+ }
+ PutUint32(_reqBuf[l:l+4], uint32(x))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(y))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// Damage : mark part of the surface damaged
+//
+// This request is used to describe the regions where the pending
+// buffer is different from the current surface contents, and where
+// the surface therefore needs to be repainted. The compositor
+// ignores the parts of the damage that fall outside of the surface.
+//
+// Damage is double-buffered state, see wl_surface.commit.
+//
+// The damage rectangle is specified in surface-local coordinates,
+// where x and y specify the upper left corner of the damage rectangle.
+//
+// The initial value for pending damage is empty: no damage.
+// wl_surface.damage adds pending damage: the new pending damage
+// is the union of old pending damage and the given rectangle.
+//
+// wl_surface.commit assigns pending damage as the current damage,
+// and clears pending damage. The server will clear the current
+// damage as it repaints the surface.
+//
+// Note! New clients should not use this request. Instead damage can be
+// posted with wl_surface.damage_buffer which uses buffer coordinates
+// instead of surface coordinates.
+//
+// x: surface-local x coordinate
+// y: surface-local y coordinate
+// width: width of damage rectangle
+// height: height of damage rectangle
+func (i *Surface) Damage(x, y, width, height int32) error {
+ const opcode = 2
+ const _reqBufLen = 8 + 4 + 4 + 4 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(x))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(y))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(width))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(height))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// Frame : request a frame throttling hint
+//
+// Request a notification when it is a good time to start drawing a new
+// frame, by creating a frame callback. This is useful for throttling
+// redrawing operations, and driving animations.
+//
+// When a client is animating on a wl_surface, it can use the 'frame'
+// request to get notified when it is a good time to draw and commit the
+// next frame of animation. If the client commits an update earlier than
+// that, it is likely that some updates will not make it to the display,
+// and the client is wasting resources by drawing too often.
+//
+// The frame request will take effect on the next wl_surface.commit.
+// The notification will only be posted for one frame unless
+// requested again. For a wl_surface, the notifications are posted in
+// the order the frame requests were committed.
+//
+// The server must send the notifications so that a client
+// will not send excessive updates, while still allowing
+// the highest possible update rate for clients that wait for the reply
+// before drawing again. The server should give some time for the client
+// to draw and commit after sending the frame callback events to let it
+// hit the next output refresh.
+//
+// A server should avoid signaling the frame callbacks if the
+// surface is not visible in any way, e.g. the surface is off-screen,
+// or completely obscured by other opaque surfaces.
+//
+// The object returned by this request will be destroyed by the
+// compositor after the callback is fired and as such the client must not
+// attempt to use it after that point.
+//
+// The callback_data passed in the callback is the current time, in
+// milliseconds, with an undefined base.
+func (i *Surface) Frame() (*Callback, error) {
+ callback := NewCallback(i.Context())
+ const opcode = 3
+ const _reqBufLen = 8 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], callback.ID())
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return callback, err
+}
+
+// SetOpaqueRegion : set opaque region
+//
+// This request sets the region of the surface that contains
+// opaque content.
+//
+// The opaque region is an optimization hint for the compositor
+// that lets it optimize the redrawing of content behind opaque
+// regions. Setting an opaque region is not required for correct
+// behaviour, but marking transparent content as opaque will result
+// in repaint artifacts.
+//
+// The opaque region is specified in surface-local coordinates.
+//
+// The compositor ignores the parts of the opaque region that fall
+// outside of the surface.
+//
+// Opaque region is double-buffered state, see wl_surface.commit.
+//
+// wl_surface.set_opaque_region changes the pending opaque region.
+// wl_surface.commit copies the pending region to the current region.
+// Otherwise, the pending and current regions are never changed.
+//
+// The initial value for an opaque region is empty. Setting the pending
+// opaque region has copy semantics, and the wl_region object can be
+// destroyed immediately. A NULL wl_region causes the pending opaque
+// region to be set to empty.
+//
+// region: opaque region of the surface
+func (i *Surface) SetOpaqueRegion(region *Region) error {
+ const opcode = 4
+ const _reqBufLen = 8 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ if region == nil {
+ PutUint32(_reqBuf[l:l+4], 0)
+ l += 4
+ } else {
+ PutUint32(_reqBuf[l:l+4], region.ID())
+ l += 4
+ }
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// SetInputRegion : set input region
+//
+// This request sets the region of the surface that can receive
+// pointer and touch events.
+//
+// Input events happening outside of this region will try the next
+// surface in the server surface stack. The compositor ignores the
+// parts of the input region that fall outside of the surface.
+//
+// The input region is specified in surface-local coordinates.
+//
+// Input region is double-buffered state, see wl_surface.commit.
+//
+// wl_surface.set_input_region changes the pending input region.
+// wl_surface.commit copies the pending region to the current region.
+// Otherwise the pending and current regions are never changed,
+// except cursor and icon surfaces are special cases, see
+// wl_pointer.set_cursor and wl_data_device.start_drag.
+//
+// The initial value for an input region is infinite. That means the
+// whole surface will accept input. Setting the pending input region
+// has copy semantics, and the wl_region object can be destroyed
+// immediately. A NULL wl_region causes the input region to be set
+// to infinite.
+//
+// region: input region of the surface
+func (i *Surface) SetInputRegion(region *Region) error {
+ const opcode = 5
+ const _reqBufLen = 8 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ if region == nil {
+ PutUint32(_reqBuf[l:l+4], 0)
+ l += 4
+ } else {
+ PutUint32(_reqBuf[l:l+4], region.ID())
+ l += 4
+ }
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// Commit : commit pending surface state
+//
+// Surface state (input, opaque, and damage regions, attached buffers,
+// etc.) is double-buffered. Protocol requests modify the pending state,
+// as opposed to the active state in use by the compositor.
+//
+// A commit request atomically creates a content update from the pending
+// state, even if the pending state has not been touched. The content
+// update is placed in a queue until it becomes active. After commit, the
+// new pending state is as documented for each related request.
+//
+// When the content update is applied, the wl_buffer is applied before all
+// other state. This means that all coordinates in double-buffered state
+// are relative to the newly attached wl_buffers, except for
+// wl_surface.attach itself. If there is no newly attached wl_buffer, the
+// coordinates are relative to the previous content update.
+//
+// All requests that need a commit to become effective are documented
+// to affect double-buffered state.
+//
+// Other interfaces may add further double-buffered surface state.
+func (i *Surface) Commit() error {
+ const opcode = 6
+ const _reqBufLen = 8
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// SetBufferTransform : sets the buffer transformation
+//
+// This request sets the transformation that the client has already applied
+// to the content of the buffer. The accepted values for the transform
+// parameter are the values for wl_output.transform.
+//
+// The compositor applies the inverse of this transformation whenever it
+// uses the buffer contents.
+//
+// Buffer transform is double-buffered state, see wl_surface.commit.
+//
+// A newly created surface has its buffer transformation set to normal.
+//
+// wl_surface.set_buffer_transform changes the pending buffer
+// transformation. wl_surface.commit copies the pending buffer
+// transformation to the current one. Otherwise, the pending and current
+// values are never changed.
+//
+// The purpose of this request is to allow clients to render content
+// according to the output transform, thus permitting the compositor to
+// use certain optimizations even if the display is rotated. Using
+// hardware overlays and scanning out a client buffer for fullscreen
+// surfaces are examples of such optimizations. Those optimizations are
+// highly dependent on the compositor implementation, so the use of this
+// request should be considered on a case-by-case basis.
+//
+// Note that if the transform value includes 90 or 270 degree rotation,
+// the width of the buffer will become the surface height and the height
+// of the buffer will become the surface width.
+//
+// If transform is not one of the values from the
+// wl_output.transform enum the invalid_transform protocol error
+// is raised.
+//
+// transform: transform for interpreting buffer contents
+func (i *Surface) SetBufferTransform(transform int32) error {
+ const opcode = 7
+ const _reqBufLen = 8 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(transform))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// SetBufferScale : sets the buffer scaling factor
+//
+// This request sets an optional scaling factor on how the compositor
+// interprets the contents of the buffer attached to the window.
+//
+// Buffer scale is double-buffered state, see wl_surface.commit.
+//
+// A newly created surface has its buffer scale set to 1.
+//
+// wl_surface.set_buffer_scale changes the pending buffer scale.
+// wl_surface.commit copies the pending buffer scale to the current one.
+// Otherwise, the pending and current values are never changed.
+//
+// The purpose of this request is to allow clients to supply higher
+// resolution buffer data for use on high resolution outputs. It is
+// intended that you pick the same buffer scale as the scale of the
+// output that the surface is displayed on. This means the compositor
+// can avoid scaling when rendering the surface on that output.
+//
+// Note that if the scale is larger than 1, then you have to attach
+// a buffer that is larger (by a factor of scale in each dimension)
+// than the desired surface size.
+//
+// If scale is not greater than 0 the invalid_scale protocol error is
+// raised.
+//
+// scale: scale for interpreting buffer contents
+func (i *Surface) SetBufferScale(scale int32) error {
+ const opcode = 8
+ const _reqBufLen = 8 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(scale))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// DamageBuffer : mark part of the surface damaged using buffer coordinates
+//
+// This request is used to describe the regions where the pending
+// buffer is different from the current surface contents, and where
+// the surface therefore needs to be repainted. The compositor
+// ignores the parts of the damage that fall outside of the surface.
+//
+// Damage is double-buffered state, see wl_surface.commit.
+//
+// The damage rectangle is specified in buffer coordinates,
+// where x and y specify the upper left corner of the damage rectangle.
+//
+// The initial value for pending damage is empty: no damage.
+// wl_surface.damage_buffer adds pending damage: the new pending
+// damage is the union of old pending damage and the given rectangle.
+//
+// wl_surface.commit assigns pending damage as the current damage,
+// and clears pending damage. The server will clear the current
+// damage as it repaints the surface.
+//
+// This request differs from wl_surface.damage in only one way - it
+// takes damage in buffer coordinates instead of surface-local
+// coordinates. While this generally is more intuitive than surface
+// coordinates, it is especially desirable when using wp_viewport
+// or when a drawing library (like EGL) is unaware of buffer scale
+// and buffer transform.
+//
+// Note: Because buffer transformation changes and damage requests may
+// be interleaved in the protocol stream, it is impossible to determine
+// the actual mapping between surface and buffer damage until
+// wl_surface.commit time. Therefore, compositors wishing to take both
+// kinds of damage into account will have to accumulate damage from the
+// two requests separately and only transform from one to the other
+// after receiving the wl_surface.commit.
+//
+// x: buffer-local x coordinate
+// y: buffer-local y coordinate
+// width: width of damage rectangle
+// height: height of damage rectangle
+func (i *Surface) DamageBuffer(x, y, width, height int32) error {
+ const opcode = 9
+ const _reqBufLen = 8 + 4 + 4 + 4 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(x))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(y))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(width))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(height))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// Offset : set the surface contents offset
+//
+// The x and y arguments specify the location of the new pending
+// buffer's upper left corner, relative to the current buffer's upper
+// left corner, in surface-local coordinates. In other words, the
+// x and y, combined with the new surface size define in which
+// directions the surface's size changes.
+//
+// The exact semantics of wl_surface.offset are role-specific. Refer to
+// the documentation of specific roles for more information.
+//
+// Surface location offset is double-buffered state, see
+// wl_surface.commit.
+//
+// This request is semantically equivalent to and the replaces the x and y
+// arguments in the wl_surface.attach request in wl_surface versions prior
+// to 5. See wl_surface.attach for details.
+//
+// x: surface-local x coordinate
+// y: surface-local y coordinate
+func (i *Surface) Offset(x, y int32) error {
+ const opcode = 10
+ const _reqBufLen = 8 + 4 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(x))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(y))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+type SurfaceError uint32
+
+// SurfaceError : wl_surface error values
+//
+// These errors can be emitted in response to wl_surface requests.
+const (
+ // SurfaceErrorInvalidScale : buffer scale value is invalid
+ SurfaceErrorInvalidScale SurfaceError = 0
+ // SurfaceErrorInvalidTransform : buffer transform value is invalid
+ SurfaceErrorInvalidTransform SurfaceError = 1
+ // SurfaceErrorInvalidSize : buffer size is invalid
+ SurfaceErrorInvalidSize SurfaceError = 2
+ // SurfaceErrorInvalidOffset : buffer offset is invalid
+ SurfaceErrorInvalidOffset SurfaceError = 3
+ // SurfaceErrorDefunctRoleObject : surface was destroyed before its role object
+ SurfaceErrorDefunctRoleObject SurfaceError = 4
+)
+
+func (e SurfaceError) Name() string {
+ switch e {
+ case SurfaceErrorInvalidScale:
+ return "invalid_scale"
+ case SurfaceErrorInvalidTransform:
+ return "invalid_transform"
+ case SurfaceErrorInvalidSize:
+ return "invalid_size"
+ case SurfaceErrorInvalidOffset:
+ return "invalid_offset"
+ case SurfaceErrorDefunctRoleObject:
+ return "defunct_role_object"
+ default:
+ return ""
+ }
+}
+
+func (e SurfaceError) Value() string {
+ switch e {
+ case SurfaceErrorInvalidScale:
+ return "0"
+ case SurfaceErrorInvalidTransform:
+ return "1"
+ case SurfaceErrorInvalidSize:
+ return "2"
+ case SurfaceErrorInvalidOffset:
+ return "3"
+ case SurfaceErrorDefunctRoleObject:
+ return "4"
+ default:
+ return ""
+ }
+}
+
+func (e SurfaceError) String() string {
+ return e.Name() + "=" + e.Value()
+}
+
+// SurfaceEnterEvent : surface enters an output
+//
+// This is emitted whenever a surface's creation, movement, or resizing
+// results in some part of it being within the scanout region of an
+// output.
+//
+// Note that a surface may be overlapping with zero or more outputs.
+type SurfaceEnterEvent struct {
+ Output *Output
+}
+type SurfaceEnterHandlerFunc func(SurfaceEnterEvent)
+
+// SetEnterHandler : sets handler for SurfaceEnterEvent
+func (i *Surface) SetEnterHandler(f SurfaceEnterHandlerFunc) {
+ i.enterHandler = f
+}
+
+// SurfaceLeaveEvent : surface leaves an output
+//
+// This is emitted whenever a surface's creation, movement, or resizing
+// results in it no longer having any part of it within the scanout region
+// of an output.
+//
+// Clients should not use the number of outputs the surface is on for frame
+// throttling purposes. The surface might be hidden even if no leave event
+// has been sent, and the compositor might expect new surface content
+// updates even if no enter event has been sent. The frame event should be
+// used instead.
+type SurfaceLeaveEvent struct {
+ Output *Output
+}
+type SurfaceLeaveHandlerFunc func(SurfaceLeaveEvent)
+
+// SetLeaveHandler : sets handler for SurfaceLeaveEvent
+func (i *Surface) SetLeaveHandler(f SurfaceLeaveHandlerFunc) {
+ i.leaveHandler = f
+}
+
+// SurfacePreferredBufferScaleEvent : preferred buffer scale for the surface
+//
+// This event indicates the preferred buffer scale for this surface. It is
+// sent whenever the compositor's preference changes.
+//
+// Before receiving this event the preferred buffer scale for this surface
+// is 1.
+//
+// It is intended that scaling aware clients use this event to scale their
+// content and use wl_surface.set_buffer_scale to indicate the scale they
+// have rendered with. This allows clients to supply a higher detail
+// buffer.
+//
+// The compositor shall emit a scale value greater than 0.
+type SurfacePreferredBufferScaleEvent struct {
+ Factor int32
+}
+type SurfacePreferredBufferScaleHandlerFunc func(SurfacePreferredBufferScaleEvent)
+
+// SetPreferredBufferScaleHandler : sets handler for SurfacePreferredBufferScaleEvent
+func (i *Surface) SetPreferredBufferScaleHandler(f SurfacePreferredBufferScaleHandlerFunc) {
+ i.preferredBufferScaleHandler = f
+}
+
+// SurfacePreferredBufferTransformEvent : preferred buffer transform for the surface
+//
+// This event indicates the preferred buffer transform for this surface.
+// It is sent whenever the compositor's preference changes.
+//
+// Before receiving this event the preferred buffer transform for this
+// surface is normal.
+//
+// Applying this transformation to the surface buffer contents and using
+// wl_surface.set_buffer_transform might allow the compositor to use the
+// surface buffer more efficiently.
+type SurfacePreferredBufferTransformEvent struct {
+ Transform uint32
+}
+type SurfacePreferredBufferTransformHandlerFunc func(SurfacePreferredBufferTransformEvent)
+
+// SetPreferredBufferTransformHandler : sets handler for SurfacePreferredBufferTransformEvent
+func (i *Surface) SetPreferredBufferTransformHandler(f SurfacePreferredBufferTransformHandlerFunc) {
+ i.preferredBufferTransformHandler = f
+}
+
+func (i *Surface) Dispatch(opcode uint32, fd int, data []byte) {
+ switch opcode {
+ case 0:
+ if i.enterHandler == nil {
+ return
+ }
+ var e SurfaceEnterEvent
+ l := 0
+ e.Output = i.Context().GetProxy(Uint32(data[l : l+4])).(*Output)
+ l += 4
+
+ i.enterHandler(e)
+ case 1:
+ if i.leaveHandler == nil {
+ return
+ }
+ var e SurfaceLeaveEvent
+ l := 0
+ e.Output = i.Context().GetProxy(Uint32(data[l : l+4])).(*Output)
+ l += 4
+
+ i.leaveHandler(e)
+ case 2:
+ if i.preferredBufferScaleHandler == nil {
+ return
+ }
+ var e SurfacePreferredBufferScaleEvent
+ l := 0
+ e.Factor = int32(Uint32(data[l : l+4]))
+ l += 4
+
+ i.preferredBufferScaleHandler(e)
+ case 3:
+ if i.preferredBufferTransformHandler == nil {
+ return
+ }
+ var e SurfacePreferredBufferTransformEvent
+ l := 0
+ e.Transform = Uint32(data[l : l+4])
+ l += 4
+
+ i.preferredBufferTransformHandler(e)
+ }
+}
+
+// SeatInterfaceName 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 SeatInterfaceName = "wl_seat"
+
+// Seat : group of input devices
+//
+// A seat is a group of keyboards, pointer and touch devices. This
+// object is published as a global during start up, or when such a
+// device is hot plugged. A seat typically has a pointer and
+// maintains a keyboard focus and a pointer focus.
+type Seat struct {
+ BaseProxy
+ capabilitiesHandler SeatCapabilitiesHandlerFunc
+ nameHandler SeatNameHandlerFunc
+}
+
+// NewSeat : group of input devices
+//
+// A seat is a group of keyboards, pointer and touch devices. This
+// object is published as a global during start up, or when such a
+// device is hot plugged. A seat typically has a pointer and
+// maintains a keyboard focus and a pointer focus.
+func NewSeat(ctx *Context) *Seat {
+ wlSeat := &Seat{}
+ ctx.Register(wlSeat)
+ return wlSeat
+}
+
+// GetPointer : return pointer object
+//
+// The ID provided will be initialized to the wl_pointer interface
+// for this seat.
+//
+// This request only takes effect if the seat has the pointer
+// capability, or has had the pointer capability in the past.
+// It is a protocol violation to issue this request on a seat that has
+// never had the pointer capability. The missing_capability error will
+// be sent in this case.
+func (i *Seat) GetPointer() (*Pointer, error) {
+ id := NewPointer(i.Context())
+ const opcode = 0
+ const _reqBufLen = 8 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], id.ID())
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return id, err
+}
+
+// GetKeyboard : return keyboard object
+//
+// The ID provided will be initialized to the wl_keyboard interface
+// for this seat.
+//
+// This request only takes effect if the seat has the keyboard
+// capability, or has had the keyboard capability in the past.
+// It is a protocol violation to issue this request on a seat that has
+// never had the keyboard capability. The missing_capability error will
+// be sent in this case.
+func (i *Seat) GetKeyboard() (*Keyboard, error) {
+ id := NewKeyboard(i.Context())
+ const opcode = 1
+ const _reqBufLen = 8 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], id.ID())
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return id, err
+}
+
+// GetTouch : return touch object
+//
+// The ID provided will be initialized to the wl_touch interface
+// for this seat.
+//
+// This request only takes effect if the seat has the touch
+// capability, or has had the touch capability in the past.
+// It is a protocol violation to issue this request on a seat that has
+// never had the touch capability. The missing_capability error will
+// be sent in this case.
+func (i *Seat) GetTouch() (*Touch, error) {
+ id := NewTouch(i.Context())
+ const opcode = 2
+ const _reqBufLen = 8 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], id.ID())
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return id, err
+}
+
+// Release : release the seat object
+//
+// Using this request a client can tell the server that it is not going to
+// use the seat object anymore.
+func (i *Seat) Release() error {
+ defer i.MarkZombie()
+ const opcode = 3
+ const _reqBufLen = 8
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+type SeatCapability uint32
+
+// SeatCapability : seat capability bitmask
+//
+// This is a bitmask of capabilities this seat has; if a member is
+// set, then it is present on the seat.
+const (
+ // SeatCapabilityPointer : the seat has pointer devices
+ SeatCapabilityPointer SeatCapability = 1
+ // SeatCapabilityKeyboard : the seat has one or more keyboards
+ SeatCapabilityKeyboard SeatCapability = 2
+ // SeatCapabilityTouch : the seat has touch devices
+ SeatCapabilityTouch SeatCapability = 4
+)
+
+func (e SeatCapability) Name() string {
+ switch e {
+ case SeatCapabilityPointer:
+ return "pointer"
+ case SeatCapabilityKeyboard:
+ return "keyboard"
+ case SeatCapabilityTouch:
+ return "touch"
+ default:
+ return ""
+ }
+}
+
+func (e SeatCapability) Value() string {
+ switch e {
+ case SeatCapabilityPointer:
+ return "1"
+ case SeatCapabilityKeyboard:
+ return "2"
+ case SeatCapabilityTouch:
+ return "4"
+ default:
+ return ""
+ }
+}
+
+func (e SeatCapability) String() string {
+ return e.Name() + "=" + e.Value()
+}
+
+type SeatError uint32
+
+// SeatError : wl_seat error values
+//
+// These errors can be emitted in response to wl_seat requests.
+const (
+ // SeatErrorMissingCapability : get_pointer, get_keyboard or get_touch called on seat without the matching capability
+ SeatErrorMissingCapability SeatError = 0
+)
+
+func (e SeatError) Name() string {
+ switch e {
+ case SeatErrorMissingCapability:
+ return "missing_capability"
+ default:
+ return ""
+ }
+}
+
+func (e SeatError) Value() string {
+ switch e {
+ case SeatErrorMissingCapability:
+ return "0"
+ default:
+ return ""
+ }
+}
+
+func (e SeatError) String() string {
+ return e.Name() + "=" + e.Value()
+}
+
+// SeatCapabilitiesEvent : seat capabilities changed
+//
+// This is sent on binding to the seat global or whenever a seat gains
+// or loses the pointer, keyboard or touch capabilities.
+// The argument is a capability enum containing the complete set of
+// capabilities this seat has.
+//
+// When the pointer capability is added, a client may create a
+// wl_pointer object using the wl_seat.get_pointer request. This object
+// will receive pointer events until the capability is removed in the
+// future.
+//
+// When the pointer capability is removed, a client should destroy the
+// wl_pointer objects associated with the seat where the capability was
+// removed, using the wl_pointer.release request. No further pointer
+// events will be received on these objects.
+//
+// In some compositors, if a seat regains the pointer capability and a
+// client has a previously obtained wl_pointer object of version 4 or
+// less, that object may start sending pointer events again. This
+// behavior is considered a misinterpretation of the intended behavior
+// and must not be relied upon by the client. wl_pointer objects of
+// version 5 or later must not send events if created before the most
+// recent event notifying the client of an added pointer capability.
+//
+// The above behavior also applies to wl_keyboard and wl_touch with the
+// keyboard and touch capabilities, respectively.
+type SeatCapabilitiesEvent struct {
+ Capabilities uint32
+}
+type SeatCapabilitiesHandlerFunc func(SeatCapabilitiesEvent)
+
+// SetCapabilitiesHandler : sets handler for SeatCapabilitiesEvent
+func (i *Seat) SetCapabilitiesHandler(f SeatCapabilitiesHandlerFunc) {
+ i.capabilitiesHandler = f
+}
+
+// SeatNameEvent : unique identifier for this seat
+//
+// In a multi-seat configuration the seat name can be used by clients to
+// help identify which physical devices the seat represents.
+//
+// The seat name is a UTF-8 string with no convention defined for its
+// contents. Each name is unique among all wl_seat globals. The name is
+// only guaranteed to be unique for the current compositor instance.
+//
+// The same seat names are used for all clients. Thus, the name can be
+// shared across processes to refer to a specific wl_seat global.
+//
+// The name event is sent after binding to the seat global, and should be sent
+// before announcing capabilities. This event only sent once per seat object,
+// and the name does not change over the lifetime of the wl_seat global.
+//
+// Compositors may re-use the same seat name if the wl_seat global is
+// destroyed and re-created later.
+type SeatNameEvent struct {
+ Name string
+}
+type SeatNameHandlerFunc func(SeatNameEvent)
+
+// SetNameHandler : sets handler for SeatNameEvent
+func (i *Seat) SetNameHandler(f SeatNameHandlerFunc) {
+ i.nameHandler = f
+}
+
+func (i *Seat) Dispatch(opcode uint32, fd int, data []byte) {
+ switch opcode {
+ case 0:
+ if i.capabilitiesHandler == nil {
+ return
+ }
+ var e SeatCapabilitiesEvent
+ l := 0
+ e.Capabilities = Uint32(data[l : l+4])
+ l += 4
+
+ i.capabilitiesHandler(e)
+ case 1:
+ if i.nameHandler == nil {
+ return
+ }
+ var e SeatNameEvent
+ l := 0
+ nameLen := PaddedLen(int(Uint32(data[l : l+4])))
+ l += 4
+ e.Name = String(data[l : l+nameLen])
+ l += nameLen
+
+ i.nameHandler(e)
+ }
+}
+
+// PointerInterfaceName 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 PointerInterfaceName = "wl_pointer"
+
+// Pointer : pointer input device
+//
+// The wl_pointer interface represents one or more input devices,
+// such as mice, which control the pointer location and pointer_focus
+// of a seat.
+//
+// The wl_pointer interface generates motion, enter and leave
+// events for the surfaces that the pointer is located over,
+// and button and axis events for button presses, button releases
+// and scrolling.
+type Pointer struct {
+ BaseProxy
+ enterHandler PointerEnterHandlerFunc
+ leaveHandler PointerLeaveHandlerFunc
+ motionHandler PointerMotionHandlerFunc
+ buttonHandler PointerButtonHandlerFunc
+ axisHandler PointerAxisHandlerFunc
+ frameHandler PointerFrameHandlerFunc
+ axisSourceHandler PointerAxisSourceHandlerFunc
+ axisStopHandler PointerAxisStopHandlerFunc
+ axisDiscreteHandler PointerAxisDiscreteHandlerFunc
+ axisValue120Handler PointerAxisValue120HandlerFunc
+ axisRelativeDirectionHandler PointerAxisRelativeDirectionHandlerFunc
+}
+
+// NewPointer : pointer input device
+//
+// The wl_pointer interface represents one or more input devices,
+// such as mice, which control the pointer location and pointer_focus
+// of a seat.
+//
+// The wl_pointer interface generates motion, enter and leave
+// events for the surfaces that the pointer is located over,
+// and button and axis events for button presses, button releases
+// and scrolling.
+func NewPointer(ctx *Context) *Pointer {
+ wlPointer := &Pointer{}
+ ctx.Register(wlPointer)
+ return wlPointer
+}
+
+// SetCursor : set the pointer surface
+//
+// Set the pointer surface, i.e., the surface that contains the
+// pointer image (cursor). This request gives the surface the role
+// of a cursor. If the surface already has another role, it raises
+// a protocol error.
+//
+// The cursor actually changes only if the pointer
+// focus for this device is one of the requesting client's surfaces
+// or the surface parameter is the current pointer surface. If
+// there was a previous surface set with this request it is
+// replaced. If surface is NULL, the pointer image is hidden.
+//
+// The parameters hotspot_x and hotspot_y define the position of
+// the pointer surface relative to the pointer location. Its
+// top-left corner is always at (x, y) - (hotspot_x, hotspot_y),
+// where (x, y) are the coordinates of the pointer location, in
+// surface-local coordinates.
+//
+// On wl_surface.offset requests to the pointer surface, hotspot_x
+// and hotspot_y are decremented by the x and y parameters
+// passed to the request. The offset must be applied by
+// wl_surface.commit as usual.
+//
+// The hotspot can also be updated by passing the currently set
+// pointer surface to this request with new values for hotspot_x
+// and hotspot_y.
+//
+// The input region is ignored for wl_surfaces with the role of
+// a cursor. When the use as a cursor ends, the wl_surface is
+// unmapped.
+//
+// The serial parameter must match the latest wl_pointer.enter
+// serial number sent to the client. Otherwise the request will be
+// ignored.
+//
+// serial: serial number of the enter event
+// surface: pointer surface
+// hotspotX: surface-local x coordinate
+// hotspotY: surface-local y coordinate
+func (i *Pointer) SetCursor(serial uint32, surface *Surface, hotspotX, hotspotY int32) error {
+ const opcode = 0
+ const _reqBufLen = 8 + 4 + 4 + 4 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(serial))
+ l += 4
+ if surface == nil {
+ PutUint32(_reqBuf[l:l+4], 0)
+ l += 4
+ } else {
+ PutUint32(_reqBuf[l:l+4], surface.ID())
+ l += 4
+ }
+ PutUint32(_reqBuf[l:l+4], uint32(hotspotX))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(hotspotY))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// Release : release the pointer object
+//
+// Using this request a client can tell the server that it is not going to
+// use the pointer object anymore.
+//
+// This request destroys the pointer proxy object, so clients must not call
+// wl_pointer_destroy() after using this request.
+func (i *Pointer) Release() error {
+ defer i.MarkZombie()
+ const opcode = 1
+ const _reqBufLen = 8
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+type PointerError uint32
+
+// PointerError :
+const (
+ // PointerErrorRole : given wl_surface has another role
+ PointerErrorRole PointerError = 0
+)
+
+func (e PointerError) Name() string {
+ switch e {
+ case PointerErrorRole:
+ return "role"
+ default:
+ return ""
+ }
+}
+
+func (e PointerError) Value() string {
+ switch e {
+ case PointerErrorRole:
+ return "0"
+ default:
+ return ""
+ }
+}
+
+func (e PointerError) String() string {
+ return e.Name() + "=" + e.Value()
+}
+
+type PointerButtonState uint32
+
+// PointerButtonState : physical button state
+//
+// Describes the physical state of a button that produced the button
+// event.
+const (
+ // PointerButtonStateReleased : the button is not pressed
+ PointerButtonStateReleased PointerButtonState = 0
+ // PointerButtonStatePressed : the button is pressed
+ PointerButtonStatePressed PointerButtonState = 1
+)
+
+func (e PointerButtonState) Name() string {
+ switch e {
+ case PointerButtonStateReleased:
+ return "released"
+ case PointerButtonStatePressed:
+ return "pressed"
+ default:
+ return ""
+ }
+}
+
+func (e PointerButtonState) Value() string {
+ switch e {
+ case PointerButtonStateReleased:
+ return "0"
+ case PointerButtonStatePressed:
+ return "1"
+ default:
+ return ""
+ }
+}
+
+func (e PointerButtonState) String() string {
+ return e.Name() + "=" + e.Value()
+}
+
+type PointerAxis uint32
+
+// PointerAxis : axis types
+//
+// Describes the axis types of scroll events.
+const (
+ // PointerAxisVerticalScroll : vertical axis
+ PointerAxisVerticalScroll PointerAxis = 0
+ // PointerAxisHorizontalScroll : horizontal axis
+ PointerAxisHorizontalScroll PointerAxis = 1
+)
+
+func (e PointerAxis) Name() string {
+ switch e {
+ case PointerAxisVerticalScroll:
+ return "vertical_scroll"
+ case PointerAxisHorizontalScroll:
+ return "horizontal_scroll"
+ default:
+ return ""
+ }
+}
+
+func (e PointerAxis) Value() string {
+ switch e {
+ case PointerAxisVerticalScroll:
+ return "0"
+ case PointerAxisHorizontalScroll:
+ return "1"
+ default:
+ return ""
+ }
+}
+
+func (e PointerAxis) String() string {
+ return e.Name() + "=" + e.Value()
+}
+
+type PointerAxisSource uint32
+
+// PointerAxisSource : axis source types
+//
+// Describes the source types for axis events. This indicates to the
+// client how an axis event was physically generated; a client may
+// adjust the user interface accordingly. For example, scroll events
+// from a "finger" source may be in a smooth coordinate space with
+// kinetic scrolling whereas a "wheel" source may be in discrete steps
+// of a number of lines.
+//
+// The "continuous" axis source is a device generating events in a
+// continuous coordinate space, but using something other than a
+// finger. One example for this source is button-based scrolling where
+// the vertical motion of a device is converted to scroll events while
+// a button is held down.
+//
+// The "wheel tilt" axis source indicates that the actual device is a
+// wheel but the scroll event is not caused by a rotation but a
+// (usually sideways) tilt of the wheel.
+const (
+ // PointerAxisSourceWheel : a physical wheel rotation
+ PointerAxisSourceWheel PointerAxisSource = 0
+ // PointerAxisSourceFinger : finger on a touch surface
+ PointerAxisSourceFinger PointerAxisSource = 1
+ // PointerAxisSourceContinuous : continuous coordinate space
+ PointerAxisSourceContinuous PointerAxisSource = 2
+ // PointerAxisSourceWheelTilt : a physical wheel tilt
+ PointerAxisSourceWheelTilt PointerAxisSource = 3
+)
+
+func (e PointerAxisSource) Name() string {
+ switch e {
+ case PointerAxisSourceWheel:
+ return "wheel"
+ case PointerAxisSourceFinger:
+ return "finger"
+ case PointerAxisSourceContinuous:
+ return "continuous"
+ case PointerAxisSourceWheelTilt:
+ return "wheel_tilt"
+ default:
+ return ""
+ }
+}
+
+func (e PointerAxisSource) Value() string {
+ switch e {
+ case PointerAxisSourceWheel:
+ return "0"
+ case PointerAxisSourceFinger:
+ return "1"
+ case PointerAxisSourceContinuous:
+ return "2"
+ case PointerAxisSourceWheelTilt:
+ return "3"
+ default:
+ return ""
+ }
+}
+
+func (e PointerAxisSource) String() string {
+ return e.Name() + "=" + e.Value()
+}
+
+type PointerAxisRelativeDirection uint32
+
+// PointerAxisRelativeDirection : axis relative direction
+//
+// This specifies the direction of the physical motion that caused a
+// wl_pointer.axis event, relative to the wl_pointer.axis direction.
+const (
+ // PointerAxisRelativeDirectionIdentical : physical motion matches axis direction
+ PointerAxisRelativeDirectionIdentical PointerAxisRelativeDirection = 0
+ // PointerAxisRelativeDirectionInverted : physical motion is the inverse of the axis direction
+ PointerAxisRelativeDirectionInverted PointerAxisRelativeDirection = 1
+)
+
+func (e PointerAxisRelativeDirection) Name() string {
+ switch e {
+ case PointerAxisRelativeDirectionIdentical:
+ return "identical"
+ case PointerAxisRelativeDirectionInverted:
+ return "inverted"
+ default:
+ return ""
+ }
+}
+
+func (e PointerAxisRelativeDirection) Value() string {
+ switch e {
+ case PointerAxisRelativeDirectionIdentical:
+ return "0"
+ case PointerAxisRelativeDirectionInverted:
+ return "1"
+ default:
+ return ""
+ }
+}
+
+func (e PointerAxisRelativeDirection) String() string {
+ return e.Name() + "=" + e.Value()
+}
+
+// PointerEnterEvent : enter event
+//
+// Notification that this seat's pointer is focused on a certain
+// surface.
+//
+// When a seat's focus enters a surface, the pointer image
+// is undefined and a client should respond to this event by setting
+// an appropriate pointer image with the set_cursor request.
+type PointerEnterEvent struct {
+ Serial uint32
+ Surface *Surface
+ SurfaceX float64
+ SurfaceY float64
+}
+type PointerEnterHandlerFunc func(PointerEnterEvent)
+
+// SetEnterHandler : sets handler for PointerEnterEvent
+func (i *Pointer) SetEnterHandler(f PointerEnterHandlerFunc) {
+ i.enterHandler = f
+}
+
+// PointerLeaveEvent : leave event
+//
+// Notification that this seat's pointer is no longer focused on
+// a certain surface.
+//
+// The leave notification is sent before the enter notification
+// for the new focus.
+type PointerLeaveEvent struct {
+ Serial uint32
+ Surface *Surface
+}
+type PointerLeaveHandlerFunc func(PointerLeaveEvent)
+
+// SetLeaveHandler : sets handler for PointerLeaveEvent
+func (i *Pointer) SetLeaveHandler(f PointerLeaveHandlerFunc) {
+ i.leaveHandler = f
+}
+
+// PointerMotionEvent : pointer motion event
+//
+// Notification of pointer location change. The arguments
+// surface_x and surface_y are the location relative to the
+// focused surface.
+type PointerMotionEvent struct {
+ Time uint32
+ SurfaceX float64
+ SurfaceY float64
+}
+type PointerMotionHandlerFunc func(PointerMotionEvent)
+
+// SetMotionHandler : sets handler for PointerMotionEvent
+func (i *Pointer) SetMotionHandler(f PointerMotionHandlerFunc) {
+ i.motionHandler = f
+}
+
+// PointerButtonEvent : pointer button event
+//
+// Mouse button click and release notifications.
+//
+// The location of the click is given by the last motion or
+// enter event.
+// The time argument is a timestamp with millisecond
+// granularity, with an undefined base.
+//
+// The button is a button code as defined in the Linux kernel's
+// linux/input-event-codes.h header file, e.g. BTN_LEFT.
+//
+// Any 16-bit button code value is reserved for future additions to the
+// kernel's event code list. All other button codes above 0xFFFF are
+// currently undefined but may be used in future versions of this
+// protocol.
+type PointerButtonEvent struct {
+ Serial uint32
+ Time uint32
+ Button uint32
+ State uint32
+}
+type PointerButtonHandlerFunc func(PointerButtonEvent)
+
+// SetButtonHandler : sets handler for PointerButtonEvent
+func (i *Pointer) SetButtonHandler(f PointerButtonHandlerFunc) {
+ i.buttonHandler = f
+}
+
+// PointerAxisEvent : axis event
+//
+// Scroll and other axis notifications.
+//
+// For scroll events (vertical and horizontal scroll axes), the
+// value parameter is the length of a vector along the specified
+// axis in a coordinate space identical to those of motion events,
+// representing a relative movement along the specified axis.
+//
+// For devices that support movements non-parallel to axes multiple
+// axis events will be emitted.
+//
+// When applicable, for example for touch pads, the server can
+// choose to emit scroll events where the motion vector is
+// equivalent to a motion event vector.
+//
+// When applicable, a client can transform its content relative to the
+// scroll distance.
+type PointerAxisEvent struct {
+ Time uint32
+ Axis uint32
+ Value float64
+}
+type PointerAxisHandlerFunc func(PointerAxisEvent)
+
+// SetAxisHandler : sets handler for PointerAxisEvent
+func (i *Pointer) SetAxisHandler(f PointerAxisHandlerFunc) {
+ i.axisHandler = f
+}
+
+// PointerFrameEvent : end of a pointer event sequence
+//
+// Indicates the end of a set of events that logically belong together.
+// A client is expected to accumulate the data in all events within the
+// frame before proceeding.
+//
+// All wl_pointer events before a wl_pointer.frame event belong
+// logically together. For example, in a diagonal scroll motion the
+// compositor will send an optional wl_pointer.axis_source event, two
+// wl_pointer.axis events (horizontal and vertical) and finally a
+// wl_pointer.frame event. The client may use this information to
+// calculate a diagonal vector for scrolling.
+//
+// When multiple wl_pointer.axis events occur within the same frame,
+// the motion vector is the combined motion of all events.
+// When a wl_pointer.axis and a wl_pointer.axis_stop event occur within
+// the same frame, this indicates that axis movement in one axis has
+// stopped but continues in the other axis.
+// When multiple wl_pointer.axis_stop events occur within the same
+// frame, this indicates that these axes stopped in the same instance.
+//
+// A wl_pointer.frame event is sent for every logical event group,
+// even if the group only contains a single wl_pointer event.
+// Specifically, a client may get a sequence: motion, frame, button,
+// frame, axis, frame, axis_stop, frame.
+//
+// The wl_pointer.enter and wl_pointer.leave events are logical events
+// generated by the compositor and not the hardware. These events are
+// also grouped by a wl_pointer.frame. When a pointer moves from one
+// surface to another, a compositor should group the
+// wl_pointer.leave event within the same wl_pointer.frame.
+// However, a client must not rely on wl_pointer.leave and
+// wl_pointer.enter being in the same wl_pointer.frame.
+// Compositor-specific policies may require the wl_pointer.leave and
+// wl_pointer.enter event being split across multiple wl_pointer.frame
+// groups.
+type PointerFrameEvent struct{}
+type PointerFrameHandlerFunc func(PointerFrameEvent)
+
+// SetFrameHandler : sets handler for PointerFrameEvent
+func (i *Pointer) SetFrameHandler(f PointerFrameHandlerFunc) {
+ i.frameHandler = f
+}
+
+// PointerAxisSourceEvent : axis source event
+//
+// Source information for scroll and other axes.
+//
+// This event does not occur on its own. It is sent before a
+// wl_pointer.frame event and carries the source information for
+// all events within that frame.
+//
+// The source specifies how this event was generated. If the source is
+// wl_pointer.axis_source.finger, a wl_pointer.axis_stop event will be
+// sent when the user lifts the finger off the device.
+//
+// If the source is wl_pointer.axis_source.wheel,
+// wl_pointer.axis_source.wheel_tilt or
+// wl_pointer.axis_source.continuous, a wl_pointer.axis_stop event may
+// or may not be sent. Whether a compositor sends an axis_stop event
+// for these sources is hardware-specific and implementation-dependent;
+// clients must not rely on receiving an axis_stop event for these
+// scroll sources and should treat scroll sequences from these scroll
+// sources as unterminated by default.
+//
+// This event is optional. If the source is unknown for a particular
+// axis event sequence, no event is sent.
+// Only one wl_pointer.axis_source event is permitted per frame.
+//
+// The order of wl_pointer.axis_discrete and wl_pointer.axis_source is
+// not guaranteed.
+type PointerAxisSourceEvent struct {
+ AxisSource uint32
+}
+type PointerAxisSourceHandlerFunc func(PointerAxisSourceEvent)
+
+// SetAxisSourceHandler : sets handler for PointerAxisSourceEvent
+func (i *Pointer) SetAxisSourceHandler(f PointerAxisSourceHandlerFunc) {
+ i.axisSourceHandler = f
+}
+
+// PointerAxisStopEvent : axis stop event
+//
+// Stop notification for scroll and other axes.
+//
+// For some wl_pointer.axis_source types, a wl_pointer.axis_stop event
+// is sent to notify a client that the axis sequence has terminated.
+// This enables the client to implement kinetic scrolling.
+// See the wl_pointer.axis_source documentation for information on when
+// this event may be generated.
+//
+// Any wl_pointer.axis events with the same axis_source after this
+// event should be considered as the start of a new axis motion.
+//
+// The timestamp is to be interpreted identical to the timestamp in the
+// wl_pointer.axis event. The timestamp value may be the same as a
+// preceding wl_pointer.axis event.
+type PointerAxisStopEvent struct {
+ Time uint32
+ Axis uint32
+}
+type PointerAxisStopHandlerFunc func(PointerAxisStopEvent)
+
+// SetAxisStopHandler : sets handler for PointerAxisStopEvent
+func (i *Pointer) SetAxisStopHandler(f PointerAxisStopHandlerFunc) {
+ i.axisStopHandler = f
+}
+
+// PointerAxisDiscreteEvent : axis click event
+//
+// Discrete step information for scroll and other axes.
+//
+// This event carries the axis value of the wl_pointer.axis event in
+// discrete steps (e.g. mouse wheel clicks).
+//
+// This event is deprecated with wl_pointer version 8 - this event is not
+// sent to clients supporting version 8 or later.
+//
+// This event does not occur on its own, it is coupled with a
+// wl_pointer.axis event that represents this axis value on a
+// continuous scale. The protocol guarantees that each axis_discrete
+// event is always followed by exactly one axis event with the same
+// axis number within the same wl_pointer.frame. Note that the protocol
+// allows for other events to occur between the axis_discrete and
+// its coupled axis event, including other axis_discrete or axis
+// events. A wl_pointer.frame must not contain more than one axis_discrete
+// event per axis type.
+//
+// This event is optional; continuous scrolling devices
+// like two-finger scrolling on touchpads do not have discrete
+// steps and do not generate this event.
+//
+// The discrete value carries the directional information. e.g. a value
+// of -2 is two steps towards the negative direction of this axis.
+//
+// The axis number is identical to the axis number in the associated
+// axis event.
+//
+// The order of wl_pointer.axis_discrete and wl_pointer.axis_source is
+// not guaranteed.
+type PointerAxisDiscreteEvent struct {
+ Axis uint32
+ Discrete int32
+}
+type PointerAxisDiscreteHandlerFunc func(PointerAxisDiscreteEvent)
+
+// SetAxisDiscreteHandler : sets handler for PointerAxisDiscreteEvent
+func (i *Pointer) SetAxisDiscreteHandler(f PointerAxisDiscreteHandlerFunc) {
+ i.axisDiscreteHandler = f
+}
+
+// PointerAxisValue120Event : axis high-resolution scroll event
+//
+// Discrete high-resolution scroll information.
+//
+// This event carries high-resolution wheel scroll information,
+// with each multiple of 120 representing one logical scroll step
+// (a wheel detent). For example, an axis_value120 of 30 is one quarter of
+// a logical scroll step in the positive direction, a value120 of
+// -240 are two logical scroll steps in the negative direction within the
+// same hardware event.
+// Clients that rely on discrete scrolling should accumulate the
+// value120 to multiples of 120 before processing the event.
+//
+// The value120 must not be zero.
+//
+// This event replaces the wl_pointer.axis_discrete event in clients
+// supporting wl_pointer version 8 or later.
+//
+// Where a wl_pointer.axis_source event occurs in the same
+// wl_pointer.frame, the axis source applies to this event.
+//
+// The order of wl_pointer.axis_value120 and wl_pointer.axis_source is
+// not guaranteed.
+type PointerAxisValue120Event struct {
+ Axis uint32
+ Value120 int32
+}
+type PointerAxisValue120HandlerFunc func(PointerAxisValue120Event)
+
+// SetAxisValue120Handler : sets handler for PointerAxisValue120Event
+func (i *Pointer) SetAxisValue120Handler(f PointerAxisValue120HandlerFunc) {
+ i.axisValue120Handler = f
+}
+
+// PointerAxisRelativeDirectionEvent : axis relative physical direction event
+//
+// Relative directional information of the entity causing the axis
+// motion.
+//
+// For a wl_pointer.axis event, the wl_pointer.axis_relative_direction
+// event specifies the movement direction of the entity causing the
+// wl_pointer.axis event. For example:
+// - if a user's fingers on a touchpad move down and this
+// causes a wl_pointer.axis vertical_scroll down event, the physical
+// direction is 'identical'
+// - if a user's fingers on a touchpad move down and this causes a
+// wl_pointer.axis vertical_scroll up scroll up event ('natural
+// scrolling'), the physical direction is 'inverted'.
+//
+// A client may use this information to adjust scroll motion of
+// components. Specifically, enabling natural scrolling causes the
+// content to change direction compared to traditional scrolling.
+// Some widgets like volume control sliders should usually match the
+// physical direction regardless of whether natural scrolling is
+// active. This event enables clients to match the scroll direction of
+// a widget to the physical direction.
+//
+// This event does not occur on its own, it is coupled with a
+// wl_pointer.axis event that represents this axis value.
+// The protocol guarantees that each axis_relative_direction event is
+// always followed by exactly one axis event with the same
+// axis number within the same wl_pointer.frame. Note that the protocol
+// allows for other events to occur between the axis_relative_direction
+// and its coupled axis event.
+//
+// The axis number is identical to the axis number in the associated
+// axis event.
+//
+// The order of wl_pointer.axis_relative_direction,
+// wl_pointer.axis_discrete and wl_pointer.axis_source is not
+// guaranteed.
+type PointerAxisRelativeDirectionEvent struct {
+ Axis uint32
+ Direction uint32
+}
+type PointerAxisRelativeDirectionHandlerFunc func(PointerAxisRelativeDirectionEvent)
+
+// SetAxisRelativeDirectionHandler : sets handler for PointerAxisRelativeDirectionEvent
+func (i *Pointer) SetAxisRelativeDirectionHandler(f PointerAxisRelativeDirectionHandlerFunc) {
+ i.axisRelativeDirectionHandler = f
+}
+
+func (i *Pointer) Dispatch(opcode uint32, fd int, data []byte) {
+ switch opcode {
+ case 0:
+ if i.enterHandler == nil {
+ return
+ }
+ var e PointerEnterEvent
+ l := 0
+ e.Serial = Uint32(data[l : l+4])
+ l += 4
+ e.Surface = i.Context().GetProxy(Uint32(data[l : l+4])).(*Surface)
+ l += 4
+ e.SurfaceX = Fixed(data[l : l+4])
+ l += 4
+ e.SurfaceY = Fixed(data[l : l+4])
+ l += 4
+
+ i.enterHandler(e)
+ case 1:
+ if i.leaveHandler == nil {
+ return
+ }
+ var e PointerLeaveEvent
+ l := 0
+ e.Serial = Uint32(data[l : l+4])
+ l += 4
+ e.Surface = i.Context().GetProxy(Uint32(data[l : l+4])).(*Surface)
+ l += 4
+
+ i.leaveHandler(e)
+ case 2:
+ if i.motionHandler == nil {
+ return
+ }
+ var e PointerMotionEvent
+ l := 0
+ e.Time = Uint32(data[l : l+4])
+ l += 4
+ e.SurfaceX = Fixed(data[l : l+4])
+ l += 4
+ e.SurfaceY = Fixed(data[l : l+4])
+ l += 4
+
+ i.motionHandler(e)
+ case 3:
+ if i.buttonHandler == nil {
+ return
+ }
+ var e PointerButtonEvent
+ l := 0
+ e.Serial = Uint32(data[l : l+4])
+ l += 4
+ e.Time = Uint32(data[l : l+4])
+ l += 4
+ e.Button = Uint32(data[l : l+4])
+ l += 4
+ e.State = Uint32(data[l : l+4])
+ l += 4
+
+ i.buttonHandler(e)
+ case 4:
+ if i.axisHandler == nil {
+ return
+ }
+ var e PointerAxisEvent
+ l := 0
+ e.Time = Uint32(data[l : l+4])
+ l += 4
+ e.Axis = Uint32(data[l : l+4])
+ l += 4
+ e.Value = Fixed(data[l : l+4])
+ l += 4
+
+ i.axisHandler(e)
+ case 5:
+ if i.frameHandler == nil {
+ return
+ }
+ var e PointerFrameEvent
+
+ i.frameHandler(e)
+ case 6:
+ if i.axisSourceHandler == nil {
+ return
+ }
+ var e PointerAxisSourceEvent
+ l := 0
+ e.AxisSource = Uint32(data[l : l+4])
+ l += 4
+
+ i.axisSourceHandler(e)
+ case 7:
+ if i.axisStopHandler == nil {
+ return
+ }
+ var e PointerAxisStopEvent
+ l := 0
+ e.Time = Uint32(data[l : l+4])
+ l += 4
+ e.Axis = Uint32(data[l : l+4])
+ l += 4
+
+ i.axisStopHandler(e)
+ case 8:
+ if i.axisDiscreteHandler == nil {
+ return
+ }
+ var e PointerAxisDiscreteEvent
+ l := 0
+ e.Axis = Uint32(data[l : l+4])
+ l += 4
+ e.Discrete = int32(Uint32(data[l : l+4]))
+ l += 4
+
+ i.axisDiscreteHandler(e)
+ case 9:
+ if i.axisValue120Handler == nil {
+ return
+ }
+ var e PointerAxisValue120Event
+ l := 0
+ e.Axis = Uint32(data[l : l+4])
+ l += 4
+ e.Value120 = int32(Uint32(data[l : l+4]))
+ l += 4
+
+ i.axisValue120Handler(e)
+ case 10:
+ if i.axisRelativeDirectionHandler == nil {
+ return
+ }
+ var e PointerAxisRelativeDirectionEvent
+ l := 0
+ e.Axis = Uint32(data[l : l+4])
+ l += 4
+ e.Direction = Uint32(data[l : l+4])
+ l += 4
+
+ i.axisRelativeDirectionHandler(e)
+ }
+}
+
+// KeyboardInterfaceName 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 KeyboardInterfaceName = "wl_keyboard"
+
+// Keyboard : keyboard input device
+//
+// The wl_keyboard interface represents one or more keyboards
+// associated with a seat.
+//
+// Each wl_keyboard has the following logical state:
+//
+// - an active surface (possibly null),
+// - the keys currently logically down,
+// - the active modifiers,
+// - the active group.
+//
+// By default, the active surface is null, the keys currently logically down
+// are empty, the active modifiers and the active group are 0.
+type Keyboard struct {
+ BaseProxy
+ keymapHandler KeyboardKeymapHandlerFunc
+ enterHandler KeyboardEnterHandlerFunc
+ leaveHandler KeyboardLeaveHandlerFunc
+ keyHandler KeyboardKeyHandlerFunc
+ modifiersHandler KeyboardModifiersHandlerFunc
+ repeatInfoHandler KeyboardRepeatInfoHandlerFunc
+}
+
+// NewKeyboard : keyboard input device
+//
+// The wl_keyboard interface represents one or more keyboards
+// associated with a seat.
+//
+// Each wl_keyboard has the following logical state:
+//
+// - an active surface (possibly null),
+// - the keys currently logically down,
+// - the active modifiers,
+// - the active group.
+//
+// By default, the active surface is null, the keys currently logically down
+// are empty, the active modifiers and the active group are 0.
+func NewKeyboard(ctx *Context) *Keyboard {
+ wlKeyboard := &Keyboard{}
+ ctx.Register(wlKeyboard)
+ return wlKeyboard
+}
+
+// Release : release the keyboard object
+func (i *Keyboard) Release() error {
+ defer i.MarkZombie()
+ const opcode = 0
+ const _reqBufLen = 8
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+type KeyboardKeymapFormat uint32
+
+// KeyboardKeymapFormat : keyboard mapping format
+//
+// This specifies the format of the keymap provided to the
+// client with the wl_keyboard.keymap event.
+const (
+ // KeyboardKeymapFormatNoKeymap : no keymap; client must understand how to interpret the raw keycode
+ KeyboardKeymapFormatNoKeymap KeyboardKeymapFormat = 0
+ // KeyboardKeymapFormatXkbV1 : libxkbcommon compatible, null-terminated string; to determine the xkb keycode, clients must add 8 to the key event keycode
+ KeyboardKeymapFormatXkbV1 KeyboardKeymapFormat = 1
+)
+
+func (e KeyboardKeymapFormat) Name() string {
+ switch e {
+ case KeyboardKeymapFormatNoKeymap:
+ return "no_keymap"
+ case KeyboardKeymapFormatXkbV1:
+ return "xkb_v1"
+ default:
+ return ""
+ }
+}
+
+func (e KeyboardKeymapFormat) Value() string {
+ switch e {
+ case KeyboardKeymapFormatNoKeymap:
+ return "0"
+ case KeyboardKeymapFormatXkbV1:
+ return "1"
+ default:
+ return ""
+ }
+}
+
+func (e KeyboardKeymapFormat) String() string {
+ return e.Name() + "=" + e.Value()
+}
+
+type KeyboardKeyState uint32
+
+// KeyboardKeyState : physical key state
+//
+// Describes the physical state of a key that produced the key event.
+//
+// Since version 10, the key can be in a "repeated" pseudo-state which
+// means the same as "pressed", but is used to signal repetition in the
+// key event.
+//
+// The key may only enter the repeated state after entering the pressed
+// state and before entering the released state. This event may be
+// generated multiple times while the key is down.
+const (
+ // KeyboardKeyStateReleased : key is not pressed
+ KeyboardKeyStateReleased KeyboardKeyState = 0
+ // KeyboardKeyStatePressed : key is pressed
+ KeyboardKeyStatePressed KeyboardKeyState = 1
+ // KeyboardKeyStateRepeated : key was repeated
+ KeyboardKeyStateRepeated KeyboardKeyState = 2
+)
+
+func (e KeyboardKeyState) Name() string {
+ switch e {
+ case KeyboardKeyStateReleased:
+ return "released"
+ case KeyboardKeyStatePressed:
+ return "pressed"
+ case KeyboardKeyStateRepeated:
+ return "repeated"
+ default:
+ return ""
+ }
+}
+
+func (e KeyboardKeyState) Value() string {
+ switch e {
+ case KeyboardKeyStateReleased:
+ return "0"
+ case KeyboardKeyStatePressed:
+ return "1"
+ case KeyboardKeyStateRepeated:
+ return "2"
+ default:
+ return ""
+ }
+}
+
+func (e KeyboardKeyState) String() string {
+ return e.Name() + "=" + e.Value()
+}
+
+// KeyboardKeymapEvent : keyboard mapping
+//
+// This event provides a file descriptor to the client which can be
+// memory-mapped in read-only mode to provide a keyboard mapping
+// description.
+//
+// From version 7 onwards, the fd must be mapped with MAP_PRIVATE by
+// the recipient, as MAP_SHARED may fail.
+type KeyboardKeymapEvent struct {
+ Format uint32
+ Fd int
+ Size uint32
+}
+type KeyboardKeymapHandlerFunc func(KeyboardKeymapEvent)
+
+// SetKeymapHandler : sets handler for KeyboardKeymapEvent
+func (i *Keyboard) SetKeymapHandler(f KeyboardKeymapHandlerFunc) {
+ i.keymapHandler = f
+}
+
+// KeyboardEnterEvent : enter event
+//
+// Notification that this seat's keyboard focus is on a certain
+// surface.
+//
+// The compositor must send the wl_keyboard.modifiers event after this
+// event.
+//
+// In the wl_keyboard logical state, this event sets the active surface to
+// the surface argument and the keys currently logically down to the keys
+// in the keys argument. The compositor must not send this event if the
+// wl_keyboard already had an active surface immediately before this event.
+//
+// Clients should not use the list of pressed keys to emulate key-press
+// events. The order of keys in the list is unspecified.
+type KeyboardEnterEvent struct {
+ Serial uint32
+ Surface *Surface
+ Keys []byte
+}
+type KeyboardEnterHandlerFunc func(KeyboardEnterEvent)
+
+// SetEnterHandler : sets handler for KeyboardEnterEvent
+func (i *Keyboard) SetEnterHandler(f KeyboardEnterHandlerFunc) {
+ i.enterHandler = f
+}
+
+// KeyboardLeaveEvent : leave event
+//
+// Notification that this seat's keyboard focus is no longer on
+// a certain surface.
+//
+// The leave notification is sent before the enter notification
+// for the new focus.
+//
+// In the wl_keyboard logical state, this event resets all values to their
+// defaults. The compositor must not send this event if the active surface
+// of the wl_keyboard was not equal to the surface argument immediately
+// before this event.
+type KeyboardLeaveEvent struct {
+ Serial uint32
+ Surface *Surface
+}
+type KeyboardLeaveHandlerFunc func(KeyboardLeaveEvent)
+
+// SetLeaveHandler : sets handler for KeyboardLeaveEvent
+func (i *Keyboard) SetLeaveHandler(f KeyboardLeaveHandlerFunc) {
+ i.leaveHandler = f
+}
+
+// KeyboardKeyEvent : key event
+//
+// A key was pressed or released.
+// The time argument is a timestamp with millisecond
+// granularity, with an undefined base.
+//
+// The key is a platform-specific key code that can be interpreted
+// by feeding it to the keyboard mapping (see the keymap event).
+//
+// If this event produces a change in modifiers, then the resulting
+// wl_keyboard.modifiers event must be sent after this event.
+//
+// In the wl_keyboard logical state, this event adds the key to the keys
+// currently logically down (if the state argument is pressed) or removes
+// the key from the keys currently logically down (if the state argument is
+// released). The compositor must not send this event if the wl_keyboard
+// did not have an active surface immediately before this event. The
+// compositor must not send this event if state is pressed (resp. released)
+// and the key was already logically down (resp. was not logically down)
+// immediately before this event.
+//
+// Since version 10, compositors may send key events with the "repeated"
+// key state when a wl_keyboard.repeat_info event with a rate argument of
+// 0 has been received. This allows the compositor to take over the
+// responsibility of key repetition.
+type KeyboardKeyEvent struct {
+ Serial uint32
+ Time uint32
+ Key uint32
+ State uint32
+}
+type KeyboardKeyHandlerFunc func(KeyboardKeyEvent)
+
+// SetKeyHandler : sets handler for KeyboardKeyEvent
+func (i *Keyboard) SetKeyHandler(f KeyboardKeyHandlerFunc) {
+ i.keyHandler = f
+}
+
+// KeyboardModifiersEvent : modifier and group state
+//
+// Notifies clients that the modifier and/or group state has
+// changed, and it should update its local state.
+//
+// The compositor may send this event without a surface of the client
+// having keyboard focus, for example to tie modifier information to
+// pointer focus instead. If a modifier event with pressed modifiers is sent
+// without a prior enter event, the client can assume the modifier state is
+// valid until it receives the next wl_keyboard.modifiers event. In order to
+// reset the modifier state again, the compositor can send a
+// wl_keyboard.modifiers event with no pressed modifiers.
+//
+// In the wl_keyboard logical state, this event updates the modifiers and
+// group.
+type KeyboardModifiersEvent struct {
+ Serial uint32
+ ModsDepressed uint32
+ ModsLatched uint32
+ ModsLocked uint32
+ Group uint32
+}
+type KeyboardModifiersHandlerFunc func(KeyboardModifiersEvent)
+
+// SetModifiersHandler : sets handler for KeyboardModifiersEvent
+func (i *Keyboard) SetModifiersHandler(f KeyboardModifiersHandlerFunc) {
+ i.modifiersHandler = f
+}
+
+// KeyboardRepeatInfoEvent : repeat rate and delay
+//
+// Informs the client about the keyboard's repeat rate and delay.
+//
+// This event is sent as soon as the wl_keyboard object has been created,
+// and is guaranteed to be received by the client before any key press
+// event.
+//
+// Negative values for either rate or delay are illegal. A rate of zero
+// will disable any repeating (regardless of the value of delay).
+//
+// This event can be sent later on as well with a new value if necessary,
+// so clients should continue listening for the event past the creation
+// of wl_keyboard.
+type KeyboardRepeatInfoEvent struct {
+ Rate int32
+ Delay int32
+}
+type KeyboardRepeatInfoHandlerFunc func(KeyboardRepeatInfoEvent)
+
+// SetRepeatInfoHandler : sets handler for KeyboardRepeatInfoEvent
+func (i *Keyboard) SetRepeatInfoHandler(f KeyboardRepeatInfoHandlerFunc) {
+ i.repeatInfoHandler = f
+}
+
+func (i *Keyboard) Dispatch(opcode uint32, fd int, data []byte) {
+ switch opcode {
+ case 0:
+ if i.keymapHandler == nil {
+ if fd != -1 {
+ unix.Close(fd)
+ }
+ return
+ }
+ var e KeyboardKeymapEvent
+ l := 0
+ e.Format = Uint32(data[l : l+4])
+ l += 4
+ e.Fd = fd
+ e.Size = Uint32(data[l : l+4])
+ l += 4
+
+ i.keymapHandler(e)
+ case 1:
+ if i.enterHandler == nil {
+ return
+ }
+ var e KeyboardEnterEvent
+ l := 0
+ e.Serial = Uint32(data[l : l+4])
+ l += 4
+ e.Surface = i.Context().GetProxy(Uint32(data[l : l+4])).(*Surface)
+ l += 4
+ keysLen := int(Uint32(data[l : l+4]))
+ l += 4
+ e.Keys = make([]byte, keysLen)
+ copy(e.Keys, data[l:l+keysLen])
+ l += keysLen
+
+ i.enterHandler(e)
+ case 2:
+ if i.leaveHandler == nil {
+ return
+ }
+ var e KeyboardLeaveEvent
+ l := 0
+ e.Serial = Uint32(data[l : l+4])
+ l += 4
+ e.Surface = i.Context().GetProxy(Uint32(data[l : l+4])).(*Surface)
+ l += 4
+
+ i.leaveHandler(e)
+ case 3:
+ if i.keyHandler == nil {
+ return
+ }
+ var e KeyboardKeyEvent
+ l := 0
+ e.Serial = Uint32(data[l : l+4])
+ l += 4
+ e.Time = Uint32(data[l : l+4])
+ l += 4
+ e.Key = Uint32(data[l : l+4])
+ l += 4
+ e.State = Uint32(data[l : l+4])
+ l += 4
+
+ i.keyHandler(e)
+ case 4:
+ if i.modifiersHandler == nil {
+ return
+ }
+ var e KeyboardModifiersEvent
+ l := 0
+ e.Serial = Uint32(data[l : l+4])
+ l += 4
+ e.ModsDepressed = Uint32(data[l : l+4])
+ l += 4
+ e.ModsLatched = Uint32(data[l : l+4])
+ l += 4
+ e.ModsLocked = Uint32(data[l : l+4])
+ l += 4
+ e.Group = Uint32(data[l : l+4])
+ l += 4
+
+ i.modifiersHandler(e)
+ case 5:
+ if i.repeatInfoHandler == nil {
+ return
+ }
+ var e KeyboardRepeatInfoEvent
+ l := 0
+ e.Rate = int32(Uint32(data[l : l+4]))
+ l += 4
+ e.Delay = int32(Uint32(data[l : l+4]))
+ l += 4
+
+ i.repeatInfoHandler(e)
+ }
+}
+
+// TouchInterfaceName 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 TouchInterfaceName = "wl_touch"
+
+// Touch : touchscreen input device
+//
+// The wl_touch interface represents a touchscreen
+// associated with a seat.
+//
+// Touch interactions can consist of one or more contacts.
+// For each contact, a series of events is generated, starting
+// with a down event, followed by zero or more motion events,
+// and ending with an up event. Events relating to the same
+// contact point can be identified by the ID of the sequence.
+type Touch struct {
+ BaseProxy
+ downHandler TouchDownHandlerFunc
+ upHandler TouchUpHandlerFunc
+ motionHandler TouchMotionHandlerFunc
+ frameHandler TouchFrameHandlerFunc
+ cancelHandler TouchCancelHandlerFunc
+ shapeHandler TouchShapeHandlerFunc
+ orientationHandler TouchOrientationHandlerFunc
+}
+
+// NewTouch : touchscreen input device
+//
+// The wl_touch interface represents a touchscreen
+// associated with a seat.
+//
+// Touch interactions can consist of one or more contacts.
+// For each contact, a series of events is generated, starting
+// with a down event, followed by zero or more motion events,
+// and ending with an up event. Events relating to the same
+// contact point can be identified by the ID of the sequence.
+func NewTouch(ctx *Context) *Touch {
+ wlTouch := &Touch{}
+ ctx.Register(wlTouch)
+ return wlTouch
+}
+
+// Release : release the touch object
+func (i *Touch) Release() error {
+ defer i.MarkZombie()
+ const opcode = 0
+ const _reqBufLen = 8
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// TouchDownEvent : touch down event and beginning of a touch sequence
+//
+// A new touch point has appeared on the surface. This touch point is
+// assigned a unique ID. Future events from this touch point reference
+// this ID. The ID ceases to be valid after a touch up event and may be
+// reused in the future.
+type TouchDownEvent struct {
+ Serial uint32
+ Time uint32
+ Surface *Surface
+ Id int32
+ X float64
+ Y float64
+}
+type TouchDownHandlerFunc func(TouchDownEvent)
+
+// SetDownHandler : sets handler for TouchDownEvent
+func (i *Touch) SetDownHandler(f TouchDownHandlerFunc) {
+ i.downHandler = f
+}
+
+// TouchUpEvent : end of a touch event sequence
+//
+// The touch point has disappeared. No further events will be sent for
+// this touch point and the touch point's ID is released and may be
+// reused in a future touch down event.
+type TouchUpEvent struct {
+ Serial uint32
+ Time uint32
+ Id int32
+}
+type TouchUpHandlerFunc func(TouchUpEvent)
+
+// SetUpHandler : sets handler for TouchUpEvent
+func (i *Touch) SetUpHandler(f TouchUpHandlerFunc) {
+ i.upHandler = f
+}
+
+// TouchMotionEvent : update of touch point coordinates
+//
+// A touch point has changed coordinates.
+type TouchMotionEvent struct {
+ Time uint32
+ Id int32
+ X float64
+ Y float64
+}
+type TouchMotionHandlerFunc func(TouchMotionEvent)
+
+// SetMotionHandler : sets handler for TouchMotionEvent
+func (i *Touch) SetMotionHandler(f TouchMotionHandlerFunc) {
+ i.motionHandler = f
+}
+
+// TouchFrameEvent : end of touch frame event
+//
+// Indicates the end of a set of events that logically belong together.
+// A client is expected to accumulate the data in all events within the
+// frame before proceeding.
+//
+// A wl_touch.frame terminates at least one event but otherwise no
+// guarantee is provided about the set of events within a frame. A client
+// must assume that any state not updated in a frame is unchanged from the
+// previously known state.
+type TouchFrameEvent struct{}
+type TouchFrameHandlerFunc func(TouchFrameEvent)
+
+// SetFrameHandler : sets handler for TouchFrameEvent
+func (i *Touch) SetFrameHandler(f TouchFrameHandlerFunc) {
+ i.frameHandler = f
+}
+
+// TouchCancelEvent : touch session cancelled
+//
+// Sent if the compositor decides the touch stream is a global
+// gesture. No further events are sent to the clients from that
+// particular gesture. Touch cancellation applies to all touch points
+// currently active on this client's surface. The client is
+// responsible for finalizing the touch points, future touch points on
+// this surface may reuse the touch point ID.
+//
+// No frame event is required after the cancel event.
+type TouchCancelEvent struct{}
+type TouchCancelHandlerFunc func(TouchCancelEvent)
+
+// SetCancelHandler : sets handler for TouchCancelEvent
+func (i *Touch) SetCancelHandler(f TouchCancelHandlerFunc) {
+ i.cancelHandler = f
+}
+
+// TouchShapeEvent : update shape of touch point
+//
+// Sent when a touchpoint has changed its shape.
+//
+// This event does not occur on its own. It is sent before a
+// wl_touch.frame event and carries the new shape information for
+// any previously reported, or new touch points of that frame.
+//
+// Other events describing the touch point such as wl_touch.down,
+// wl_touch.motion or wl_touch.orientation may be sent within the
+// same wl_touch.frame. A client should treat these events as a single
+// logical touch point update. The order of wl_touch.shape,
+// wl_touch.orientation and wl_touch.motion is not guaranteed.
+// A wl_touch.down event is guaranteed to occur before the first
+// wl_touch.shape event for this touch ID but both events may occur within
+// the same wl_touch.frame.
+//
+// A touchpoint shape is approximated by an ellipse through the major and
+// minor axis length. The major axis length describes the longer diameter
+// of the ellipse, while the minor axis length describes the shorter
+// diameter. Major and minor are orthogonal and both are specified in
+// surface-local coordinates. The center of the ellipse is always at the
+// touchpoint location as reported by wl_touch.down or wl_touch.move.
+//
+// This event is only sent by the compositor if the touch device supports
+// shape reports. The client has to make reasonable assumptions about the
+// shape if it did not receive this event.
+type TouchShapeEvent struct {
+ Id int32
+ Major float64
+ Minor float64
+}
+type TouchShapeHandlerFunc func(TouchShapeEvent)
+
+// SetShapeHandler : sets handler for TouchShapeEvent
+func (i *Touch) SetShapeHandler(f TouchShapeHandlerFunc) {
+ i.shapeHandler = f
+}
+
+// TouchOrientationEvent : update orientation of touch point
+//
+// Sent when a touchpoint has changed its orientation.
+//
+// This event does not occur on its own. It is sent before a
+// wl_touch.frame event and carries the new shape information for
+// any previously reported, or new touch points of that frame.
+//
+// Other events describing the touch point such as wl_touch.down,
+// wl_touch.motion or wl_touch.shape may be sent within the
+// same wl_touch.frame. A client should treat these events as a single
+// logical touch point update. The order of wl_touch.shape,
+// wl_touch.orientation and wl_touch.motion is not guaranteed.
+// A wl_touch.down event is guaranteed to occur before the first
+// wl_touch.orientation event for this touch ID but both events may occur
+// within the same wl_touch.frame.
+//
+// The orientation describes the clockwise angle of a touchpoint's major
+// axis to the positive surface y-axis and is normalized to the -180 to
+// +180 degree range. The granularity of orientation depends on the touch
+// device, some devices only support binary rotation values between 0 and
+// 90 degrees.
+//
+// This event is only sent by the compositor if the touch device supports
+// orientation reports.
+type TouchOrientationEvent struct {
+ Id int32
+ Orientation float64
+}
+type TouchOrientationHandlerFunc func(TouchOrientationEvent)
+
+// SetOrientationHandler : sets handler for TouchOrientationEvent
+func (i *Touch) SetOrientationHandler(f TouchOrientationHandlerFunc) {
+ i.orientationHandler = f
+}
+
+func (i *Touch) Dispatch(opcode uint32, fd int, data []byte) {
+ switch opcode {
+ case 0:
+ if i.downHandler == nil {
+ return
+ }
+ var e TouchDownEvent
+ l := 0
+ e.Serial = Uint32(data[l : l+4])
+ l += 4
+ e.Time = Uint32(data[l : l+4])
+ l += 4
+ e.Surface = i.Context().GetProxy(Uint32(data[l : l+4])).(*Surface)
+ l += 4
+ e.Id = int32(Uint32(data[l : l+4]))
+ l += 4
+ e.X = Fixed(data[l : l+4])
+ l += 4
+ e.Y = Fixed(data[l : l+4])
+ l += 4
+
+ i.downHandler(e)
+ case 1:
+ if i.upHandler == nil {
+ return
+ }
+ var e TouchUpEvent
+ l := 0
+ e.Serial = Uint32(data[l : l+4])
+ l += 4
+ e.Time = Uint32(data[l : l+4])
+ l += 4
+ e.Id = int32(Uint32(data[l : l+4]))
+ l += 4
+
+ i.upHandler(e)
+ case 2:
+ if i.motionHandler == nil {
+ return
+ }
+ var e TouchMotionEvent
+ l := 0
+ e.Time = Uint32(data[l : l+4])
+ l += 4
+ e.Id = int32(Uint32(data[l : l+4]))
+ l += 4
+ e.X = Fixed(data[l : l+4])
+ l += 4
+ e.Y = Fixed(data[l : l+4])
+ l += 4
+
+ i.motionHandler(e)
+ case 3:
+ if i.frameHandler == nil {
+ return
+ }
+ var e TouchFrameEvent
+
+ i.frameHandler(e)
+ case 4:
+ if i.cancelHandler == nil {
+ return
+ }
+ var e TouchCancelEvent
+
+ i.cancelHandler(e)
+ case 5:
+ if i.shapeHandler == nil {
+ return
+ }
+ var e TouchShapeEvent
+ l := 0
+ e.Id = int32(Uint32(data[l : l+4]))
+ l += 4
+ e.Major = Fixed(data[l : l+4])
+ l += 4
+ e.Minor = Fixed(data[l : l+4])
+ l += 4
+
+ i.shapeHandler(e)
+ case 6:
+ if i.orientationHandler == nil {
+ return
+ }
+ var e TouchOrientationEvent
+ l := 0
+ e.Id = int32(Uint32(data[l : l+4]))
+ l += 4
+ e.Orientation = Fixed(data[l : l+4])
+ l += 4
+
+ i.orientationHandler(e)
+ }
+}
+
+// OutputInterfaceName 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 OutputInterfaceName = "wl_output"
+
+// Output : compositor output region
+//
+// An output describes part of the compositor geometry. The
+// compositor works in the 'compositor coordinate system' and an
+// output corresponds to a rectangular area in that space that is
+// actually visible. This typically corresponds to a monitor that
+// displays part of the compositor space. This object is published
+// as global during start up, or when a monitor is hotplugged.
+type Output struct {
+ BaseProxy
+ geometryHandler OutputGeometryHandlerFunc
+ modeHandler OutputModeHandlerFunc
+ doneHandler OutputDoneHandlerFunc
+ scaleHandler OutputScaleHandlerFunc
+ nameHandler OutputNameHandlerFunc
+ descriptionHandler OutputDescriptionHandlerFunc
+}
+
+// NewOutput : compositor output region
+//
+// An output describes part of the compositor geometry. The
+// compositor works in the 'compositor coordinate system' and an
+// output corresponds to a rectangular area in that space that is
+// actually visible. This typically corresponds to a monitor that
+// displays part of the compositor space. This object is published
+// as global during start up, or when a monitor is hotplugged.
+func NewOutput(ctx *Context) *Output {
+ wlOutput := &Output{}
+ ctx.Register(wlOutput)
+ return wlOutput
+}
+
+// Release : release the output object
+//
+// Using this request a client can tell the server that it is not going to
+// use the output object anymore.
+func (i *Output) Release() error {
+ defer i.MarkZombie()
+ const opcode = 0
+ const _reqBufLen = 8
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+type OutputSubpixel uint32
+
+// OutputSubpixel : subpixel geometry information
+//
+// This enumeration describes how the physical
+// pixels on an output are laid out.
+const (
+ // OutputSubpixelUnknown : unknown geometry
+ OutputSubpixelUnknown OutputSubpixel = 0
+ // OutputSubpixelNone : no geometry
+ OutputSubpixelNone OutputSubpixel = 1
+ // OutputSubpixelHorizontalRgb : horizontal RGB
+ OutputSubpixelHorizontalRgb OutputSubpixel = 2
+ // OutputSubpixelHorizontalBgr : horizontal BGR
+ OutputSubpixelHorizontalBgr OutputSubpixel = 3
+ // OutputSubpixelVerticalRgb : vertical RGB
+ OutputSubpixelVerticalRgb OutputSubpixel = 4
+ // OutputSubpixelVerticalBgr : vertical BGR
+ OutputSubpixelVerticalBgr OutputSubpixel = 5
+)
+
+func (e OutputSubpixel) Name() string {
+ switch e {
+ case OutputSubpixelUnknown:
+ return "unknown"
+ case OutputSubpixelNone:
+ return "none"
+ case OutputSubpixelHorizontalRgb:
+ return "horizontal_rgb"
+ case OutputSubpixelHorizontalBgr:
+ return "horizontal_bgr"
+ case OutputSubpixelVerticalRgb:
+ return "vertical_rgb"
+ case OutputSubpixelVerticalBgr:
+ return "vertical_bgr"
+ default:
+ return ""
+ }
+}
+
+func (e OutputSubpixel) Value() string {
+ switch e {
+ case OutputSubpixelUnknown:
+ return "0"
+ case OutputSubpixelNone:
+ return "1"
+ case OutputSubpixelHorizontalRgb:
+ return "2"
+ case OutputSubpixelHorizontalBgr:
+ return "3"
+ case OutputSubpixelVerticalRgb:
+ return "4"
+ case OutputSubpixelVerticalBgr:
+ return "5"
+ default:
+ return ""
+ }
+}
+
+func (e OutputSubpixel) String() string {
+ return e.Name() + "=" + e.Value()
+}
+
+type OutputTransform uint32
+
+// OutputTransform : transformation applied to buffer contents
+//
+// This describes transformations that clients and compositors apply to
+// buffer contents.
+//
+// The flipped values correspond to an initial flip around a
+// vertical axis followed by rotation.
+//
+// The purpose is mainly to allow clients to render accordingly and
+// tell the compositor, so that for fullscreen surfaces, the
+// compositor will still be able to scan out directly from client
+// surfaces.
+const (
+ // OutputTransformNormal : no transform
+ OutputTransformNormal OutputTransform = 0
+ // OutputTransform90 : 90 degrees counter-clockwise
+ OutputTransform90 OutputTransform = 1
+ // OutputTransform180 : 180 degrees counter-clockwise
+ OutputTransform180 OutputTransform = 2
+ // OutputTransform270 : 270 degrees counter-clockwise
+ OutputTransform270 OutputTransform = 3
+ // OutputTransformFlipped : 180 degree flip around a vertical axis
+ OutputTransformFlipped OutputTransform = 4
+ // OutputTransformFlipped90 : flip and rotate 90 degrees counter-clockwise
+ OutputTransformFlipped90 OutputTransform = 5
+ // OutputTransformFlipped180 : flip and rotate 180 degrees counter-clockwise
+ OutputTransformFlipped180 OutputTransform = 6
+ // OutputTransformFlipped270 : flip and rotate 270 degrees counter-clockwise
+ OutputTransformFlipped270 OutputTransform = 7
+)
+
+func (e OutputTransform) Name() string {
+ switch e {
+ case OutputTransformNormal:
+ return "normal"
+ case OutputTransform90:
+ return "90"
+ case OutputTransform180:
+ return "180"
+ case OutputTransform270:
+ return "270"
+ case OutputTransformFlipped:
+ return "flipped"
+ case OutputTransformFlipped90:
+ return "flipped_90"
+ case OutputTransformFlipped180:
+ return "flipped_180"
+ case OutputTransformFlipped270:
+ return "flipped_270"
+ default:
+ return ""
+ }
+}
+
+func (e OutputTransform) Value() string {
+ switch e {
+ case OutputTransformNormal:
+ return "0"
+ case OutputTransform90:
+ return "1"
+ case OutputTransform180:
+ return "2"
+ case OutputTransform270:
+ return "3"
+ case OutputTransformFlipped:
+ return "4"
+ case OutputTransformFlipped90:
+ return "5"
+ case OutputTransformFlipped180:
+ return "6"
+ case OutputTransformFlipped270:
+ return "7"
+ default:
+ return ""
+ }
+}
+
+func (e OutputTransform) String() string {
+ return e.Name() + "=" + e.Value()
+}
+
+type OutputMode uint32
+
+// OutputMode : mode information
+//
+// These flags describe properties of an output mode.
+// They are used in the flags bitfield of the mode event.
+const (
+ // OutputModeCurrent : indicates this is the current mode
+ OutputModeCurrent OutputMode = 0x1
+ // OutputModePreferred : indicates this is the preferred mode
+ OutputModePreferred OutputMode = 0x2
+)
+
+func (e OutputMode) Name() string {
+ switch e {
+ case OutputModeCurrent:
+ return "current"
+ case OutputModePreferred:
+ return "preferred"
+ default:
+ return ""
+ }
+}
+
+func (e OutputMode) Value() string {
+ switch e {
+ case OutputModeCurrent:
+ return "0x1"
+ case OutputModePreferred:
+ return "0x2"
+ default:
+ return ""
+ }
+}
+
+func (e OutputMode) String() string {
+ return e.Name() + "=" + e.Value()
+}
+
+// OutputGeometryEvent : properties of the output
+//
+// The geometry event describes geometric properties of the output.
+// The event is sent when binding to the output object and whenever
+// any of the properties change.
+//
+// The physical size can be set to zero if it doesn't make sense for this
+// output (e.g. for projectors or virtual outputs).
+//
+// The geometry event will be followed by a done event (starting from
+// version 2).
+//
+// Clients should use wl_surface.preferred_buffer_transform instead of the
+// transform advertised by this event to find the preferred buffer
+// transform to use for a surface.
+//
+// Note: wl_output only advertises partial information about the output
+// position and identification. Some compositors, for instance those not
+// implementing a desktop-style output layout or those exposing virtual
+// outputs, might fake this information. Instead of using x and y, clients
+// should use xdg_output.logical_position. Instead of using make and model,
+// clients should use name and description.
+type OutputGeometryEvent struct {
+ X int32
+ Y int32
+ PhysicalWidth int32
+ PhysicalHeight int32
+ Subpixel int32
+ Make string
+ Model string
+ Transform int32
+}
+type OutputGeometryHandlerFunc func(OutputGeometryEvent)
+
+// SetGeometryHandler : sets handler for OutputGeometryEvent
+func (i *Output) SetGeometryHandler(f OutputGeometryHandlerFunc) {
+ i.geometryHandler = f
+}
+
+// OutputModeEvent : advertise available modes for the output
+//
+// The mode event describes an available mode for the output.
+//
+// The event is sent when binding to the output object and there
+// will always be one mode, the current mode. The event is sent
+// again if an output changes mode, for the mode that is now
+// current. In other words, the current mode is always the last
+// mode that was received with the current flag set.
+//
+// Non-current modes are deprecated. A compositor can decide to only
+// advertise the current mode and never send other modes. Clients
+// should not rely on non-current modes.
+//
+// The size of a mode is given in physical hardware units of
+// the output device. This is not necessarily the same as
+// the output size in the global compositor space. For instance,
+// the output may be scaled, as described in wl_output.scale,
+// or transformed, as described in wl_output.transform. Clients
+// willing to retrieve the output size in the global compositor
+// space should use xdg_output.logical_size instead.
+//
+// The vertical refresh rate can be set to zero if it doesn't make
+// sense for this output (e.g. for virtual outputs).
+//
+// The mode event will be followed by a done event (starting from
+// version 2).
+//
+// Clients should not use the refresh rate to schedule frames. Instead,
+// they should use the wl_surface.frame event or the presentation-time
+// protocol.
+//
+// Note: this information is not always meaningful for all outputs. Some
+// compositors, such as those exposing virtual outputs, might fake the
+// refresh rate or the size.
+type OutputModeEvent struct {
+ Flags uint32
+ Width int32
+ Height int32
+ Refresh int32
+}
+type OutputModeHandlerFunc func(OutputModeEvent)
+
+// SetModeHandler : sets handler for OutputModeEvent
+func (i *Output) SetModeHandler(f OutputModeHandlerFunc) {
+ i.modeHandler = f
+}
+
+// OutputDoneEvent : sent all information about output
+//
+// This event is sent after all other properties have been
+// sent after binding to the output object and after any
+// other property changes done after that. This allows
+// changes to the output properties to be seen as
+// atomic, even if they happen via multiple events.
+type OutputDoneEvent struct{}
+type OutputDoneHandlerFunc func(OutputDoneEvent)
+
+// SetDoneHandler : sets handler for OutputDoneEvent
+func (i *Output) SetDoneHandler(f OutputDoneHandlerFunc) {
+ i.doneHandler = f
+}
+
+// OutputScaleEvent : output scaling properties
+//
+// This event contains scaling geometry information
+// that is not in the geometry event. It may be sent after
+// binding the output object or if the output scale changes
+// later. The compositor will emit a non-zero, positive
+// value for scale. If it is not sent, the client should
+// assume a scale of 1.
+//
+// A scale larger than 1 means that the compositor will
+// automatically scale surface buffers by this amount
+// when rendering. This is used for very high resolution
+// displays where applications rendering at the native
+// resolution would be too small to be legible.
+//
+// Clients should use wl_surface.preferred_buffer_scale
+// instead of this event to find the preferred buffer
+// scale to use for a surface.
+//
+// The scale event will be followed by a done event.
+type OutputScaleEvent struct {
+ Factor int32
+}
+type OutputScaleHandlerFunc func(OutputScaleEvent)
+
+// SetScaleHandler : sets handler for OutputScaleEvent
+func (i *Output) SetScaleHandler(f OutputScaleHandlerFunc) {
+ i.scaleHandler = f
+}
+
+// OutputNameEvent : name of this output
+//
+// Many compositors will assign user-friendly names to their outputs, show
+// them to the user, allow the user to refer to an output, etc. The client
+// may wish to know this name as well to offer the user similar behaviors.
+//
+// The name is a UTF-8 string with no convention defined for its contents.
+// Each name is unique among all wl_output globals. The name is only
+// guaranteed to be unique for the compositor instance.
+//
+// The same output name is used for all clients for a given wl_output
+// global. Thus, the name can be shared across processes to refer to a
+// specific wl_output global.
+//
+// The name is not guaranteed to be persistent across sessions, thus cannot
+// be used to reliably identify an output in e.g. configuration files.
+//
+// Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc. However, do
+// not assume that the name is a reflection of an underlying DRM connector,
+// X11 connection, etc.
+//
+// The name event is sent after binding the output object. This event is
+// only sent once per output object, and the name does not change over the
+// lifetime of the wl_output global.
+//
+// Compositors may re-use the same output name if the wl_output global is
+// destroyed and re-created later. Compositors should avoid re-using the
+// same name if possible.
+//
+// The name event will be followed by a done event.
+type OutputNameEvent struct {
+ Name string
+}
+type OutputNameHandlerFunc func(OutputNameEvent)
+
+// SetNameHandler : sets handler for OutputNameEvent
+func (i *Output) SetNameHandler(f OutputNameHandlerFunc) {
+ i.nameHandler = f
+}
+
+// OutputDescriptionEvent : human-readable description of this output
+//
+// Many compositors can produce human-readable descriptions of their
+// outputs. The client may wish to know this description as well, e.g. for
+// output selection purposes.
+//
+// The description is a UTF-8 string with no convention defined for its
+// contents. The description is not guaranteed to be unique among all
+// wl_output globals. Examples might include 'Foocorp 11" Display' or
+// 'Virtual X11 output via :1'.
+//
+// The description event is sent after binding the output object and
+// whenever the description changes. The description is optional, and may
+// not be sent at all.
+//
+// The description event will be followed by a done event.
+type OutputDescriptionEvent struct {
+ Description string
+}
+type OutputDescriptionHandlerFunc func(OutputDescriptionEvent)
+
+// SetDescriptionHandler : sets handler for OutputDescriptionEvent
+func (i *Output) SetDescriptionHandler(f OutputDescriptionHandlerFunc) {
+ i.descriptionHandler = f
+}
+
+func (i *Output) Dispatch(opcode uint32, fd int, data []byte) {
+ switch opcode {
+ case 0:
+ if i.geometryHandler == nil {
+ return
+ }
+ var e OutputGeometryEvent
+ l := 0
+ e.X = int32(Uint32(data[l : l+4]))
+ l += 4
+ e.Y = int32(Uint32(data[l : l+4]))
+ l += 4
+ e.PhysicalWidth = int32(Uint32(data[l : l+4]))
+ l += 4
+ e.PhysicalHeight = int32(Uint32(data[l : l+4]))
+ l += 4
+ e.Subpixel = int32(Uint32(data[l : l+4]))
+ l += 4
+ makeLen := PaddedLen(int(Uint32(data[l : l+4])))
+ l += 4
+ e.Make = String(data[l : l+makeLen])
+ l += makeLen
+ modelLen := PaddedLen(int(Uint32(data[l : l+4])))
+ l += 4
+ e.Model = String(data[l : l+modelLen])
+ l += modelLen
+ e.Transform = int32(Uint32(data[l : l+4]))
+ l += 4
+
+ i.geometryHandler(e)
+ case 1:
+ if i.modeHandler == nil {
+ return
+ }
+ var e OutputModeEvent
+ l := 0
+ e.Flags = Uint32(data[l : l+4])
+ l += 4
+ e.Width = int32(Uint32(data[l : l+4]))
+ l += 4
+ e.Height = int32(Uint32(data[l : l+4]))
+ l += 4
+ e.Refresh = int32(Uint32(data[l : l+4]))
+ l += 4
+
+ i.modeHandler(e)
+ case 2:
+ if i.doneHandler == nil {
+ return
+ }
+ var e OutputDoneEvent
+
+ i.doneHandler(e)
+ case 3:
+ if i.scaleHandler == nil {
+ return
+ }
+ var e OutputScaleEvent
+ l := 0
+ e.Factor = int32(Uint32(data[l : l+4]))
+ l += 4
+
+ i.scaleHandler(e)
+ case 4:
+ if i.nameHandler == nil {
+ return
+ }
+ var e OutputNameEvent
+ l := 0
+ nameLen := PaddedLen(int(Uint32(data[l : l+4])))
+ l += 4
+ e.Name = String(data[l : l+nameLen])
+ l += nameLen
+
+ i.nameHandler(e)
+ case 5:
+ if i.descriptionHandler == nil {
+ return
+ }
+ var e OutputDescriptionEvent
+ l := 0
+ descriptionLen := PaddedLen(int(Uint32(data[l : l+4])))
+ l += 4
+ e.Description = String(data[l : l+descriptionLen])
+ l += descriptionLen
+
+ i.descriptionHandler(e)
+ }
+}
+
+// RegionInterfaceName 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 RegionInterfaceName = "wl_region"
+
+// Region : region interface
+//
+// A region object describes an area.
+//
+// Region objects are used to describe the opaque and input
+// regions of a surface.
+type Region struct {
+ BaseProxy
+}
+
+// NewRegion : region interface
+//
+// A region object describes an area.
+//
+// Region objects are used to describe the opaque and input
+// regions of a surface.
+func NewRegion(ctx *Context) *Region {
+ wlRegion := &Region{}
+ ctx.Register(wlRegion)
+ return wlRegion
+}
+
+// Destroy : destroy region
+//
+// Destroy the region. This will invalidate the object ID.
+func (i *Region) Destroy() error {
+ defer i.MarkZombie()
+ const opcode = 0
+ const _reqBufLen = 8
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// Add : add rectangle to region
+//
+// Add the specified rectangle to the region.
+//
+// x: region-local x coordinate
+// y: region-local y coordinate
+// width: rectangle width
+// height: rectangle height
+func (i *Region) Add(x, y, width, height int32) error {
+ const opcode = 1
+ const _reqBufLen = 8 + 4 + 4 + 4 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(x))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(y))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(width))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(height))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// Subtract : subtract rectangle from region
+//
+// Subtract the specified rectangle from the region.
+//
+// x: region-local x coordinate
+// y: region-local y coordinate
+// width: rectangle width
+// height: rectangle height
+func (i *Region) Subtract(x, y, width, height int32) error {
+ const opcode = 2
+ const _reqBufLen = 8 + 4 + 4 + 4 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(x))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(y))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(width))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(height))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// SubcompositorInterfaceName 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 SubcompositorInterfaceName = "wl_subcompositor"
+
+// Subcompositor : sub-surface compositing
+//
+// The global interface exposing sub-surface compositing capabilities.
+// A wl_surface, that has sub-surfaces associated, is called the
+// parent surface. Sub-surfaces can be arbitrarily nested and create
+// a tree of sub-surfaces.
+//
+// The root surface in a tree of sub-surfaces is the main
+// surface. The main surface cannot be a sub-surface, because
+// sub-surfaces must always have a parent.
+//
+// A main surface with its sub-surfaces forms a (compound) window.
+// For window management purposes, this set of wl_surface objects is
+// to be considered as a single window, and it should also behave as
+// such.
+//
+// The aim of sub-surfaces is to offload some of the compositing work
+// within a window from clients to the compositor. A prime example is
+// a video player with decorations and video in separate wl_surface
+// objects. This should allow the compositor to pass YUV video buffer
+// processing to dedicated overlay hardware when possible.
+type Subcompositor struct {
+ BaseProxy
+}
+
+// NewSubcompositor : sub-surface compositing
+//
+// The global interface exposing sub-surface compositing capabilities.
+// A wl_surface, that has sub-surfaces associated, is called the
+// parent surface. Sub-surfaces can be arbitrarily nested and create
+// a tree of sub-surfaces.
+//
+// The root surface in a tree of sub-surfaces is the main
+// surface. The main surface cannot be a sub-surface, because
+// sub-surfaces must always have a parent.
+//
+// A main surface with its sub-surfaces forms a (compound) window.
+// For window management purposes, this set of wl_surface objects is
+// to be considered as a single window, and it should also behave as
+// such.
+//
+// The aim of sub-surfaces is to offload some of the compositing work
+// within a window from clients to the compositor. A prime example is
+// a video player with decorations and video in separate wl_surface
+// objects. This should allow the compositor to pass YUV video buffer
+// processing to dedicated overlay hardware when possible.
+func NewSubcompositor(ctx *Context) *Subcompositor {
+ wlSubcompositor := &Subcompositor{}
+ ctx.Register(wlSubcompositor)
+ return wlSubcompositor
+}
+
+// Destroy : unbind from the subcompositor interface
+//
+// Informs the server that the client will not be using this
+// protocol object anymore. This does not affect any other
+// objects, wl_subsurface objects included.
+func (i *Subcompositor) Destroy() error {
+ defer i.MarkZombie()
+ const opcode = 0
+ const _reqBufLen = 8
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// GetSubsurface : give a surface the role sub-surface
+//
+// Create a sub-surface interface for the given surface, and
+// associate it with the given parent surface. This turns a
+// plain wl_surface into a sub-surface.
+//
+// The to-be sub-surface must not already have another role, and it
+// must not have an existing wl_subsurface object. Otherwise the
+// bad_surface protocol error is raised.
+//
+// Adding sub-surfaces to a parent is a double-buffered operation on the
+// parent (see wl_surface.commit). The effect of adding a sub-surface
+// becomes visible on the next time the state of the parent surface is
+// applied.
+//
+// The parent surface must not be one of the child surface's descendants,
+// and the parent must be different from the child surface, otherwise the
+// bad_parent protocol error is raised.
+//
+// This request modifies the behaviour of wl_surface.commit request on
+// the sub-surface, see the documentation on wl_subsurface interface.
+//
+// surface: the surface to be turned into a sub-surface
+// parent: the parent surface
+func (i *Subcompositor) GetSubsurface(surface, parent *Surface) (*Subsurface, error) {
+ id := NewSubsurface(i.Context())
+ const opcode = 1
+ const _reqBufLen = 8 + 4 + 4 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], id.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], surface.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], parent.ID())
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return id, err
+}
+
+type SubcompositorError uint32
+
+// SubcompositorError :
+const (
+ // SubcompositorErrorBadSurface : the to-be sub-surface is invalid
+ SubcompositorErrorBadSurface SubcompositorError = 0
+ // SubcompositorErrorBadParent : the to-be sub-surface parent is invalid
+ SubcompositorErrorBadParent SubcompositorError = 1
+)
+
+func (e SubcompositorError) Name() string {
+ switch e {
+ case SubcompositorErrorBadSurface:
+ return "bad_surface"
+ case SubcompositorErrorBadParent:
+ return "bad_parent"
+ default:
+ return ""
+ }
+}
+
+func (e SubcompositorError) Value() string {
+ switch e {
+ case SubcompositorErrorBadSurface:
+ return "0"
+ case SubcompositorErrorBadParent:
+ return "1"
+ default:
+ return ""
+ }
+}
+
+func (e SubcompositorError) String() string {
+ return e.Name() + "=" + e.Value()
+}
+
+// SubsurfaceInterfaceName 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 SubsurfaceInterfaceName = "wl_subsurface"
+
+// Subsurface : sub-surface interface to a wl_surface
+//
+// An additional interface to a wl_surface object, which has been
+// made a sub-surface. A sub-surface has one parent surface. A
+// sub-surface's size and position are not limited to that of the parent.
+// Particularly, a sub-surface is not automatically clipped to its
+// parent's area.
+//
+// A sub-surface becomes mapped, when a non-NULL wl_buffer is applied
+// and the parent surface is mapped. The order of which one happens
+// first is irrelevant. A sub-surface is hidden if the parent becomes
+// hidden, or if a NULL wl_buffer is applied. These rules apply
+// recursively through the tree of surfaces.
+//
+// The behaviour of a wl_surface.commit request on a sub-surface
+// depends on the sub-surface's mode. The possible modes are
+// synchronized and desynchronized, see methods
+// wl_subsurface.set_sync and wl_subsurface.set_desync. Synchronized
+// mode caches the wl_surface state to be applied when the parent's
+// state gets applied, and desynchronized mode applies the pending
+// wl_surface state directly. A sub-surface is initially in the
+// synchronized mode.
+//
+// Sub-surfaces also have another kind of state, which is managed by
+// wl_subsurface requests, as opposed to wl_surface requests. This
+// state includes the sub-surface position relative to the parent
+// surface (wl_subsurface.set_position), and the stacking order of
+// the parent and its sub-surfaces (wl_subsurface.place_above and
+// .place_below). This state is applied when the parent surface's
+// wl_surface state is applied, regardless of the sub-surface's mode.
+// As the exception, set_sync and set_desync are effective immediately.
+//
+// The main surface can be thought to be always in desynchronized mode,
+// since it does not have a parent in the sub-surfaces sense.
+//
+// Even if a sub-surface is in desynchronized mode, it will behave as
+// in synchronized mode, if its parent surface behaves as in
+// synchronized mode. This rule is applied recursively throughout the
+// tree of surfaces. This means, that one can set a sub-surface into
+// synchronized mode, and then assume that all its child and grand-child
+// sub-surfaces are synchronized, too, without explicitly setting them.
+//
+// Destroying a sub-surface takes effect immediately. If you need to
+// synchronize the removal of a sub-surface to the parent surface update,
+// unmap the sub-surface first by attaching a NULL wl_buffer, update parent,
+// and then destroy the sub-surface.
+//
+// If the parent wl_surface object is destroyed, the sub-surface is
+// unmapped.
+//
+// A sub-surface never has the keyboard focus of any seat.
+//
+// The wl_surface.offset request is ignored: clients must use set_position
+// instead to move the sub-surface.
+type Subsurface struct {
+ BaseProxy
+}
+
+// NewSubsurface : sub-surface interface to a wl_surface
+//
+// An additional interface to a wl_surface object, which has been
+// made a sub-surface. A sub-surface has one parent surface. A
+// sub-surface's size and position are not limited to that of the parent.
+// Particularly, a sub-surface is not automatically clipped to its
+// parent's area.
+//
+// A sub-surface becomes mapped, when a non-NULL wl_buffer is applied
+// and the parent surface is mapped. The order of which one happens
+// first is irrelevant. A sub-surface is hidden if the parent becomes
+// hidden, or if a NULL wl_buffer is applied. These rules apply
+// recursively through the tree of surfaces.
+//
+// The behaviour of a wl_surface.commit request on a sub-surface
+// depends on the sub-surface's mode. The possible modes are
+// synchronized and desynchronized, see methods
+// wl_subsurface.set_sync and wl_subsurface.set_desync. Synchronized
+// mode caches the wl_surface state to be applied when the parent's
+// state gets applied, and desynchronized mode applies the pending
+// wl_surface state directly. A sub-surface is initially in the
+// synchronized mode.
+//
+// Sub-surfaces also have another kind of state, which is managed by
+// wl_subsurface requests, as opposed to wl_surface requests. This
+// state includes the sub-surface position relative to the parent
+// surface (wl_subsurface.set_position), and the stacking order of
+// the parent and its sub-surfaces (wl_subsurface.place_above and
+// .place_below). This state is applied when the parent surface's
+// wl_surface state is applied, regardless of the sub-surface's mode.
+// As the exception, set_sync and set_desync are effective immediately.
+//
+// The main surface can be thought to be always in desynchronized mode,
+// since it does not have a parent in the sub-surfaces sense.
+//
+// Even if a sub-surface is in desynchronized mode, it will behave as
+// in synchronized mode, if its parent surface behaves as in
+// synchronized mode. This rule is applied recursively throughout the
+// tree of surfaces. This means, that one can set a sub-surface into
+// synchronized mode, and then assume that all its child and grand-child
+// sub-surfaces are synchronized, too, without explicitly setting them.
+//
+// Destroying a sub-surface takes effect immediately. If you need to
+// synchronize the removal of a sub-surface to the parent surface update,
+// unmap the sub-surface first by attaching a NULL wl_buffer, update parent,
+// and then destroy the sub-surface.
+//
+// If the parent wl_surface object is destroyed, the sub-surface is
+// unmapped.
+//
+// A sub-surface never has the keyboard focus of any seat.
+//
+// The wl_surface.offset request is ignored: clients must use set_position
+// instead to move the sub-surface.
+func NewSubsurface(ctx *Context) *Subsurface {
+ wlSubsurface := &Subsurface{}
+ ctx.Register(wlSubsurface)
+ return wlSubsurface
+}
+
+// Destroy : remove sub-surface interface
+//
+// The sub-surface interface is removed from the wl_surface object
+// that was turned into a sub-surface with a
+// wl_subcompositor.get_subsurface request. The wl_surface's association
+// to the parent is deleted. The wl_surface is unmapped immediately.
+func (i *Subsurface) Destroy() error {
+ defer i.MarkZombie()
+ const opcode = 0
+ const _reqBufLen = 8
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// SetPosition : reposition the sub-surface
+//
+// This schedules a sub-surface position change.
+// The sub-surface will be moved so that its origin (top left
+// corner pixel) will be at the location x, y of the parent surface
+// coordinate system. The coordinates are not restricted to the parent
+// surface area. Negative values are allowed.
+//
+// The scheduled coordinates will take effect whenever the state of the
+// parent surface is applied.
+//
+// If more than one set_position request is invoked by the client before
+// the commit of the parent surface, the position of a new request always
+// replaces the scheduled position from any previous request.
+//
+// The initial position is 0, 0.
+//
+// x: x coordinate in the parent surface
+// y: y coordinate in the parent surface
+func (i *Subsurface) SetPosition(x, y int32) error {
+ const opcode = 1
+ const _reqBufLen = 8 + 4 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(x))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(y))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// PlaceAbove : restack the sub-surface
+//
+// This sub-surface is taken from the stack, and put back just
+// above the reference surface, changing the z-order of the sub-surfaces.
+// The reference surface must be one of the sibling surfaces, or the
+// parent surface. Using any other surface, including this sub-surface,
+// will cause a protocol error.
+//
+// The z-order is double-buffered. Requests are handled in order and
+// applied immediately to a pending state. The final pending state is
+// copied to the active state the next time the state of the parent
+// surface is applied.
+//
+// A new sub-surface is initially added as the top-most in the stack
+// of its siblings and parent.
+//
+// sibling: the reference surface
+func (i *Subsurface) PlaceAbove(sibling *Surface) error {
+ const opcode = 2
+ const _reqBufLen = 8 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], sibling.ID())
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// PlaceBelow : restack the sub-surface
+//
+// The sub-surface is placed just below the reference surface.
+// See wl_subsurface.place_above.
+//
+// sibling: the reference surface
+func (i *Subsurface) PlaceBelow(sibling *Surface) error {
+ const opcode = 3
+ const _reqBufLen = 8 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], sibling.ID())
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// SetSync : set sub-surface to synchronized mode
+//
+// Change the commit behaviour of the sub-surface to synchronized
+// mode, also described as the parent dependent mode.
+//
+// In synchronized mode, wl_surface.commit on a sub-surface will
+// accumulate the committed state in a cache, but the state will
+// not be applied and hence will not change the compositor output.
+// The cached state is applied to the sub-surface immediately after
+// the parent surface's state is applied. This ensures atomic
+// updates of the parent and all its synchronized sub-surfaces.
+// Applying the cached state will invalidate the cache, so further
+// parent surface commits do not (re-)apply old state.
+//
+// See wl_subsurface for the recursive effect of this mode.
+func (i *Subsurface) SetSync() error {
+ const opcode = 4
+ const _reqBufLen = 8
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// SetDesync : set sub-surface to desynchronized mode
+//
+// Change the commit behaviour of the sub-surface to desynchronized
+// mode, also described as independent or freely running mode.
+//
+// In desynchronized mode, wl_surface.commit on a sub-surface will
+// apply the pending state directly, without caching, as happens
+// normally with a wl_surface. Calling wl_surface.commit on the
+// parent surface has no effect on the sub-surface's wl_surface
+// state. This mode allows a sub-surface to be updated on its own.
+//
+// If cached state exists when wl_surface.commit is called in
+// desynchronized mode, the pending state is added to the cached
+// state, and applied as a whole. This invalidates the cache.
+//
+// Note: even if a sub-surface is set to desynchronized, a parent
+// sub-surface may override it to behave as synchronized. For details,
+// see wl_subsurface.
+//
+// If a surface's parent surface behaves as desynchronized, then
+// the cached state is applied on set_desync.
+func (i *Subsurface) SetDesync() error {
+ const opcode = 5
+ const _reqBufLen = 8
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+type SubsurfaceError uint32
+
+// SubsurfaceError :
+const (
+ // SubsurfaceErrorBadSurface : wl_surface is not a sibling or the parent
+ SubsurfaceErrorBadSurface SubsurfaceError = 0
+)
+
+func (e SubsurfaceError) Name() string {
+ switch e {
+ case SubsurfaceErrorBadSurface:
+ return "bad_surface"
+ default:
+ return ""
+ }
+}
+
+func (e SubsurfaceError) Value() string {
+ switch e {
+ case SubsurfaceErrorBadSurface:
+ return "0"
+ default:
+ return ""
+ }
+}
+
+func (e SubsurfaceError) String() string {
+ return e.Name() + "=" + e.Value()
+}
+
+// FixesInterfaceName 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 FixesInterfaceName = "wl_fixes"
+
+// Fixes : wayland protocol fixes
+//
+// This global fixes problems with other core-protocol interfaces that
+// cannot be fixed in these interfaces themselves.
+type Fixes struct {
+ BaseProxy
+}
+
+// NewFixes : wayland protocol fixes
+//
+// This global fixes problems with other core-protocol interfaces that
+// cannot be fixed in these interfaces themselves.
+func NewFixes(ctx *Context) *Fixes {
+ wlFixes := &Fixes{}
+ ctx.Register(wlFixes)
+ return wlFixes
+}
+
+// Destroy : destroys this object
+func (i *Fixes) Destroy() error {
+ defer i.MarkZombie()
+ const opcode = 0
+ const _reqBufLen = 8
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
+
+// DestroyRegistry : destroy a wl_registry
+//
+// This request destroys a wl_registry object.
+//
+// The client should no longer use the wl_registry after making this
+// request.
+//
+// The compositor will emit a wl_display.delete_id event with the object ID
+// of the registry and will no longer emit any events on the registry. The
+// client should re-use the object ID once it receives the
+// wl_display.delete_id event.
+//
+// registry: the registry to destroy
+func (i *Fixes) DestroyRegistry(registry *Registry) error {
+ const opcode = 1
+ const _reqBufLen = 8 + 4
+ var _reqBuf [_reqBufLen]byte
+ l := 0
+ PutUint32(_reqBuf[l:4], i.ID())
+ l += 4
+ PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
+ l += 4
+ PutUint32(_reqBuf[l:l+4], registry.ID())
+ l += 4
+ err := i.Context().WriteMsg(_reqBuf[:], nil)
+ return err
+}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/common.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/common.go
new file mode 100644
index 0000000..b97d1b8
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/common.go
@@ -0,0 +1,55 @@
+package client
+
+import "sync/atomic"
+
+type Dispatcher interface {
+ Dispatch(opcode uint32, fd int, data []byte)
+}
+
+type Proxy interface {
+ Context() *Context
+ SetContext(ctx *Context)
+ ID() uint32
+ SetID(id uint32)
+ IsZombie() bool
+ MarkZombie()
+}
+
+type WaylandDisplay interface {
+ Context() *Context
+ GetRegistry() (*Registry, error)
+ Roundtrip() error
+ Destroy() error
+}
+
+var _ WaylandDisplay = (*Display)(nil)
+
+type BaseProxy struct {
+ ctx *Context
+ id uint32
+ zombie atomic.Bool
+}
+
+func (p *BaseProxy) ID() uint32 {
+ return p.id
+}
+
+func (p *BaseProxy) SetID(id uint32) {
+ p.id = id
+}
+
+func (p *BaseProxy) Context() *Context {
+ return p.ctx
+}
+
+func (p *BaseProxy) SetContext(ctx *Context) {
+ p.ctx = ctx
+}
+
+func (p *BaseProxy) IsZombie() bool {
+ return p.zombie.Load()
+}
+
+func (p *BaseProxy) MarkZombie() {
+ p.zombie.Store(true)
+}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/context.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/context.go
new file mode 100644
index 0000000..4eee2cb
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/context.go
@@ -0,0 +1,143 @@
+package client
+
+import (
+ "errors"
+ "fmt"
+ "net"
+ "os"
+ "sync"
+ "time"
+
+ "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
+)
+
+type Context struct {
+ conn *net.UnixConn
+ objects syncmap.Map[uint32, Proxy] // map[uint32]Proxy - thread-safe concurrent map
+ currentID uint32
+ idMu sync.Mutex // protects currentID increment
+}
+
+func (ctx *Context) Register(p Proxy) {
+ ctx.idMu.Lock()
+ ctx.currentID++
+ id := ctx.currentID
+ ctx.idMu.Unlock()
+
+ p.SetID(id)
+ p.SetContext(ctx)
+ ctx.objects.Store(id, p)
+}
+
+func (ctx *Context) RegisterWithID(p Proxy, id uint32) {
+ p.SetID(id)
+ p.SetContext(ctx)
+ ctx.objects.Store(id, p)
+}
+
+func (ctx *Context) Unregister(p Proxy) {
+ ctx.objects.Delete(p.ID())
+}
+
+func (ctx *Context) DeleteID(id uint32) {
+ ctx.objects.Delete(id)
+}
+
+func (ctx *Context) GetProxy(id uint32) Proxy {
+ if val, ok := ctx.objects.Load(id); ok {
+ return val
+ }
+ return nil
+}
+
+func (ctx *Context) Close() error {
+ return ctx.conn.Close()
+}
+
+func (ctx *Context) SetReadDeadline(t time.Time) error {
+ return ctx.conn.SetReadDeadline(t)
+}
+
+func (ctx *Context) Fd() int {
+ rawConn, err := ctx.conn.SyscallConn()
+ if err != nil {
+ return -1
+ }
+ var fd int
+ rawConn.Control(func(f uintptr) {
+ fd = int(f)
+ })
+ return fd
+}
+
+// Dispatch reads and processes incoming messages and calls [client.Dispatcher.Dispatch] on the
+// respective wayland protocol.
+// Dispatch must be called on the same goroutine as other interactions with the Context.
+// If a multi goroutine approach is desired, use [Context.GetDispatch] instead.
+// Dispatch blocks if there are no incoming messages.
+// A Dispatch loop is usually used to handle incoming messages.
+func (ctx *Context) Dispatch() error {
+ return ctx.GetDispatch()()
+}
+
+var ErrDispatchSenderNotFound = errors.New("dispatch: unable to find sender")
+var ErrDispatchSenderUnsupported = errors.New("dispatch: sender does not implement Dispatch method")
+var ErrDispatchUnableToReadMsg = errors.New("dispatch: unable to read msg")
+
+// GetDispatch reads incoming messages and returns the dispatch function which calls
+// [client.Dispatcher.Dispatch] on the respective wayland protocol.
+// This function is now thread-safe and can be called from multiple goroutines.
+// GetDispatch blocks if there are no incoming messages.
+func (ctx *Context) GetDispatch() func() error {
+ senderID, opcode, fd, data, err := ctx.ReadMsg() // Blocks if there are no incoming messages
+ if err != nil {
+ return func() error {
+ return fmt.Errorf("%w: %w", ErrDispatchUnableToReadMsg, err)
+ }
+ }
+
+ return func() error {
+ proxy, ok := ctx.objects.Load(senderID)
+ if !ok {
+ return nil // Proxy already deleted via delete_id, silently ignore
+ }
+
+ if proxy.IsZombie() {
+ return nil // Zombie proxy, discard late events
+ }
+
+ sender, ok := proxy.(Dispatcher)
+ if !ok {
+ return fmt.Errorf("%w (senderID=%d)", ErrDispatchSenderUnsupported, senderID)
+ }
+
+ sender.Dispatch(opcode, fd, data)
+ return nil
+ }
+}
+
+func Connect(addr string) (*Display, error) {
+ if addr == "" {
+ runtimeDir := os.Getenv("XDG_RUNTIME_DIR")
+ if runtimeDir == "" {
+ return nil, errors.New("env XDG_RUNTIME_DIR not set")
+ }
+ if addr == "" {
+ addr = os.Getenv("WAYLAND_DISPLAY")
+ }
+ if addr == "" {
+ addr = "wayland-0"
+ }
+ addr = runtimeDir + "/" + addr
+ }
+
+ ctx := &Context{}
+
+ conn, err := net.DialUnix("unix", nil, &net.UnixAddr{Name: addr, Net: "unix"})
+ if err != nil {
+ return nil, err
+ }
+ ctx.conn = conn
+
+ return NewDisplay(ctx), nil
+}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/context_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/context_test.go
new file mode 100644
index 0000000..380d7da
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/context_test.go
@@ -0,0 +1,111 @@
+package client_test
+
+import (
+ "errors"
+ "fmt"
+ "log"
+
+ "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client"
+)
+
+// Shows a dispatch loop that will block the goroutine.
+// This approach has no risk of data races but the loop blocks the goroutine when no messages are
+// received. This can be a valid approach if there are no more changes that need to be made after
+// setting up and starting the loop.
+// For a multi goroutine approach, use [client.Context.GetDispatch].
+func ExampleContext_Dispatch() {
+ display, err := client.Connect("")
+ if err != nil {
+ log.Fatalf("Error connecting to Wayland server: %v", err)
+ }
+
+ registry, err := display.GetRegistry()
+ if err != nil {
+ log.Fatalf("Error getting Wayland registry: %v", err)
+ }
+
+ var seat *client.Seat
+ registry.SetGlobalHandler(func(e client.RegistryGlobalEvent) {
+ switch e.Interface {
+ case client.SeatInterfaceName:
+ seat = client.NewSeat(display.Context())
+ err := registry.Bind(e.Name, e.Interface, e.Version, seat)
+ if err != nil {
+ log.Fatalf("unable to bind %s interface: %v", client.SeatInterfaceName, err)
+ }
+ }
+ })
+ display.Roundtrip()
+ display.Roundtrip()
+
+ keyboard, err := seat.GetKeyboard()
+ if err != nil {
+ log.Printf("Error getting keyboard: %v", err)
+ }
+ log.Printf("Got keyboard: %v\n", keyboard)
+
+ for {
+ err := display.Context().Dispatch()
+ if err != nil {
+ log.Printf("Dispatch error: %v\n", err)
+ }
+ }
+}
+
+// Shows how the dispatch loop can be done in another goroutine.
+// This prevents the goroutine from being blocked and allows making changes to wayland objects while
+// the dispatch loop is blocking another goroutine.
+func ExampleContext_GetDispatch() {
+ display, err := client.Connect("")
+ if err != nil {
+ log.Fatalf("Error connecting to Wayland server: %v", err)
+ }
+
+ registry, err := display.GetRegistry()
+ if err != nil {
+ log.Fatalf("Error getting Wayland registry: %v", err)
+ }
+
+ var seat *client.Seat
+ registry.SetGlobalHandler(func(e client.RegistryGlobalEvent) {
+ switch e.Interface {
+ case client.SeatInterfaceName:
+ seat = client.NewSeat(display.Context())
+ err := registry.Bind(e.Name, e.Interface, e.Version, seat)
+ if err != nil {
+ log.Fatalf("unable to bind %s interface: %v", client.SeatInterfaceName, err)
+ }
+ }
+ })
+ display.Roundtrip()
+ display.Roundtrip()
+ dispatchQueue := make(chan func() error)
+
+ go func() {
+ for {
+ dispatchQueue <- display.Context().GetDispatch()
+ }
+ }()
+
+ keyboard, err := seat.GetKeyboard()
+ if err != nil {
+ log.Printf("Error getting keyboard: %v", err)
+ }
+ log.Printf("Got keyboard: %v\n", keyboard)
+
+ err = errors.Join(keyboard.Release(), seat.Release(), display.Context().Close())
+ if err != nil {
+ fmt.Printf("Error cleaning up: %v\n", err)
+ }
+
+ for {
+ select {
+ // Add other cases here to do other things
+ case dispatchFunc := <-dispatchQueue:
+ err := dispatchFunc()
+ if err != nil {
+ log.Printf("Dispatch error: %v\n", err)
+ }
+ }
+ }
+}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/display.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/display.go
new file mode 100644
index 0000000..fa2af6f
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/display.go
@@ -0,0 +1,37 @@
+package client
+
+import (
+ "fmt"
+ "log"
+)
+
+// Roundtrip blocks until all pending request are processed by the server.
+// It is the implementation of [wl_display_roundtrip].
+//
+// [wl_display_roundtrip]: https://wayland.freedesktop.org/docs/html/apb.html#Client-classwl__display_1ab60f38c2f80980ac84f347e932793390
+func (i *Display) Roundtrip() error {
+ callback, err := i.Sync()
+ if err != nil {
+ return fmt.Errorf("unable to get sync callback: %w", err)
+ }
+ defer func() {
+ if err2 := callback.Destroy(); err2 != nil {
+ log.Printf("unable to destroy callback: %v\n", err2)
+ }
+ }()
+
+ done := false
+ callback.SetDoneHandler(func(_ CallbackDoneEvent) {
+ done = true
+ })
+
+ // Wait for callback to return
+ for !done {
+ err := i.Context().GetDispatch()()
+ if err != nil {
+ return fmt.Errorf("roundtrip: failed to dispatch: %w", err)
+ }
+ }
+
+ return nil
+}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/doc.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/doc.go
new file mode 100644
index 0000000..91137f9
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/doc.go
@@ -0,0 +1,6 @@
+// Package client is Go port of wayland-client library
+// for writing pure Go GUI software for wayland supported
+// platforms.
+package client
+
+//go:generate go run github.com/yaslama/go-wayland/cmd/go-wayland-scanner -pkg client -prefix wl -o client.go -i https://gitlab.freedesktop.org/wayland/wayland/-/raw/1.23.0/protocol/wayland.xml?ref_type=tags
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/event.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/event.go
new file mode 100644
index 0000000..e7b5cb8
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/event.go
@@ -0,0 +1,120 @@
+package client
+
+import (
+ "bytes"
+ "fmt"
+ "unsafe"
+
+ "golang.org/x/sys/unix"
+
+ _ "unsafe"
+)
+
+var oobSpace = unix.CmsgSpace(4)
+
+func (ctx *Context) ReadMsg() (senderID uint32, opcode uint32, fd int, msg []byte, err error) {
+ fd = -1
+
+ oob := make([]byte, oobSpace)
+ header := make([]byte, 8)
+
+ n, oobn, _, _, err := ctx.conn.ReadMsgUnix(header, oob)
+ if err != nil {
+ return senderID, opcode, fd, msg, err
+ }
+ if n != 8 {
+ return senderID, opcode, fd, msg, fmt.Errorf("ctx.ReadMsg: incorrect number of bytes read for header (n=%d)", n)
+ }
+
+ if oobn > 0 {
+ fds, err := getFdsFromOob(oob, oobn, "header")
+ if err != nil {
+ return senderID, opcode, fd, msg, fmt.Errorf("ctx.ReadMsg: %w", err)
+ }
+
+ if len(fds) > 0 {
+ fd = fds[0]
+ }
+ }
+
+ senderID = Uint32(header[:4])
+ opcodeAndSize := Uint32(header[4:8])
+ opcode = opcodeAndSize & 0xffff
+ size := opcodeAndSize >> 16
+
+ msgSize := int(size) - 8
+ if msgSize == 0 {
+ return senderID, opcode, fd, nil, nil
+ }
+
+ msg = make([]byte, msgSize)
+
+ if fd == -1 {
+ // if something was read before, then zero it out
+ if oobn > 0 {
+ oob = make([]byte, oobSpace)
+ }
+
+ n, oobn, _, _, err = ctx.conn.ReadMsgUnix(msg, oob)
+ } else {
+ n, err = ctx.conn.Read(msg)
+ }
+ if err != nil {
+ return senderID, opcode, fd, msg, fmt.Errorf("ctx.ReadMsg: %w", err)
+ }
+ if n != msgSize {
+ return senderID, opcode, fd, msg, fmt.Errorf("ctx.ReadMsg: incorrect number of bytes read for msg (n=%d, msgSize=%d)", n, msgSize)
+ }
+
+ if fd == -1 && oobn > 0 {
+ fds, err := getFdsFromOob(oob, oobn, "msg")
+ if err != nil {
+ return senderID, opcode, fd, msg, fmt.Errorf("ctx.ReadMsg: %w", err)
+ }
+
+ if len(fds) > 0 {
+ fd = fds[0]
+ }
+ }
+
+ return senderID, opcode, fd, msg, nil
+}
+
+func getFdsFromOob(oob []byte, oobn int, source string) ([]int, error) {
+ if oobn > len(oob) {
+ return nil, fmt.Errorf("getFdsFromOob: incorrect number of bytes read from %s for oob (oobn=%d)", source, oobn)
+ }
+ scms, err := unix.ParseSocketControlMessage(oob)
+ if err != nil {
+ return nil, fmt.Errorf("getFdsFromOob: unable to parse control message from %s: %w", source, err)
+ }
+
+ var fdsRet []int
+ for _, scm := range scms {
+ fds, err := unix.ParseUnixRights(&scm)
+ if err != nil {
+ return nil, fmt.Errorf("getFdsFromOob: unable to parse unix rights from %s: %w", source, err)
+ }
+
+ fdsRet = append(fdsRet, fds...)
+ }
+
+ return fdsRet, nil
+}
+
+func Uint32(src []byte) uint32 {
+ _ = src[3]
+ return *(*uint32)(unsafe.Pointer(&src[0]))
+}
+
+func String(src []byte) string {
+ idx := bytes.IndexByte(src, 0)
+ src = src[:idx:idx]
+ return *(*string)(unsafe.Pointer(&src))
+}
+
+func Fixed(src []byte) float64 {
+ _ = src[3]
+ fx := *(*int32)(unsafe.Pointer(&src[0]))
+ return fixedToFloat64(fx)
+}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/request.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/request.go
new file mode 100644
index 0000000..3ca2317
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/request.go
@@ -0,0 +1,44 @@
+package client
+
+import (
+ "fmt"
+ "unsafe"
+)
+
+func (ctx *Context) WriteMsg(b []byte, oob []byte) error {
+ n, oobn, err := ctx.conn.WriteMsgUnix(b, oob, nil)
+ if err != nil {
+ return err
+ }
+ if n != len(b) || oobn != len(oob) {
+ return fmt.Errorf("ctx.WriteMsg: incorrect number of bytes written (n=%d oobn=%d)", n, oobn)
+ }
+
+ return nil
+}
+
+func PutUint32(dst []byte, v uint32) {
+ _ = dst[3]
+ *(*uint32)(unsafe.Pointer(&dst[0])) = v
+}
+
+func PutFixed(dst []byte, f float64) {
+ fx := fixedFromfloat64(f)
+ _ = dst[3]
+ *(*int32)(unsafe.Pointer(&dst[0])) = fx
+}
+
+// PutString places a string in Wayland's wire format on the destination buffer.
+// It first places the length of the string (plus one for the null terminator) and then the string
+// followed by a null byte.
+// The length of dst must be equal to, or greater than, len(v) + 5.
+func PutString(dst []byte, v string) {
+ PutUint32(dst[:4], uint32(len(v)+1))
+ copy(dst[4:], v)
+ dst[4+len(v)] = '\x00' // To cause panic if dst is not large enough
+}
+
+func PutArray(dst []byte, a []byte) {
+ PutUint32(dst[:4], uint32(len(a)))
+ copy(dst[4:], a)
+}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/util.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/util.go
new file mode 100644
index 0000000..ba3d43a
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/util.go
@@ -0,0 +1,24 @@
+package client
+
+import "math"
+
+// From wayland/wayland-util.h
+
+func fixedToFloat64(f int32) float64 {
+ u_i := (1023+44)<<52 + (1 << 51) + int64(f)
+ u_d := math.Float64frombits(uint64(u_i))
+ return u_d - (3 << 43)
+}
+
+func fixedFromfloat64(d float64) int32 {
+ u_d := d + (3 << (51 - 8))
+ u_i := int64(math.Float64bits(u_d))
+ return int32(u_i)
+}
+
+func PaddedLen(l int) int {
+ if (l & 0x3) != 0 {
+ return l + (4 - (l & 0x3))
+ }
+ return l
+}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/stable/xdg-shell/xdg_shell.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/stable/xdg-shell/xdg_shell.go
new file mode 100644
index 0000000..19a203b
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/stable/xdg-shell/xdg_shell.go
@@ -0,0 +1,13 @@
+package xdg_shell
+
+import "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client"
+
+type Popup struct {
+ client.BaseProxy
+}
+
+func NewPopup(ctx *client.Context) *Popup {
+ p := &Popup{}
+ ctx.Register(p)
+ return p
+}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/CREDITS.MD b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/CREDITS.MD
new file mode 100644
index 0000000..b7d28cc
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/CREDITS.MD
@@ -0,0 +1,5 @@
+# Credits
+
+* Original socket adapter code is mostly taken from [korylprince/printer-manager-cups](https://github.com/korylprince/printer-manager-cups)
+([MIT](https://github.com/korylprince/printer-manager-cups/blob/v1.0.9/LICENSE) licensed):
+[conn.go](https://github.com/korylprince/printer-manager-cups/blob/v1.0.9/cups/conn.go)
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/LICENSE b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/adapter-http.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/adapter-http.go
new file mode 100644
index 0000000..56ababb
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/adapter-http.go
@@ -0,0 +1,132 @@
+package ipp
+
+import (
+ "bytes"
+ "crypto/tls"
+ "fmt"
+ "io"
+ "net"
+ "net/http"
+ "strconv"
+ "time"
+)
+
+type HttpAdapter struct {
+ host string
+ port int
+ username string
+ password string
+ useTLS bool
+ client *http.Client
+}
+
+func NewHttpAdapter(host string, port int, username, password string, useTLS bool) *HttpAdapter {
+ httpClient := http.Client{
+ Timeout: 0,
+ Transport: &http.Transport{
+ TLSClientConfig: &tls.Config{
+ InsecureSkipVerify: true,
+ },
+ ResponseHeaderTimeout: 90 * time.Second,
+ IdleConnTimeout: 120 * time.Second,
+ },
+ }
+
+ return &HttpAdapter{
+ host: host,
+ port: port,
+ username: username,
+ password: password,
+ useTLS: useTLS,
+ client: &httpClient,
+ }
+}
+
+func (h *HttpAdapter) SendRequest(url string, req *Request, additionalResponseData io.Writer) (*Response, error) {
+ payload, err := req.Encode()
+ if err != nil {
+ return nil, err
+ }
+
+ size := len(payload)
+ var body io.Reader
+ if req.File != nil && req.FileSize != -1 {
+ size += req.FileSize
+ body = io.MultiReader(bytes.NewBuffer(payload), req.File)
+ } else {
+ body = bytes.NewBuffer(payload)
+ }
+
+ httpReq, err := http.NewRequest("POST", url, body)
+ if err != nil {
+ return nil, err
+ }
+
+ httpReq.Header.Set("Content-Length", strconv.Itoa(size))
+ httpReq.Header.Set("Content-Type", ContentTypeIPP)
+
+ if h.username != "" && h.password != "" {
+ httpReq.SetBasicAuth(h.username, h.password)
+ }
+
+ httpResp, err := h.client.Do(httpReq)
+ if err != nil {
+ return nil, err
+ }
+ defer httpResp.Body.Close()
+
+ if httpResp.StatusCode != 200 {
+ return nil, HTTPError{
+ Code: httpResp.StatusCode,
+ }
+ }
+
+ // buffer response to avoid read issues
+ buf := new(bytes.Buffer)
+ if httpResp.ContentLength > 0 {
+ buf.Grow(int(httpResp.ContentLength))
+ }
+ if _, err := io.Copy(buf, httpResp.Body); err != nil {
+ return nil, fmt.Errorf("unable to buffer response: %w", err)
+ }
+
+ ippResp, err := NewResponseDecoder(buf).Decode(additionalResponseData)
+ if err != nil {
+ return nil, err
+ }
+
+ if err = ippResp.CheckForErrors(); err != nil {
+ return nil, fmt.Errorf("received error IPP response: %w", err)
+ }
+
+ return ippResp, nil
+}
+
+func (h *HttpAdapter) GetHttpUri(namespace string, object any) string {
+ proto := "http"
+ if h.useTLS {
+ proto = "https"
+ }
+
+ uri := fmt.Sprintf("%s://%s:%d", proto, h.host, h.port)
+
+ if namespace != "" {
+ uri = fmt.Sprintf("%s/%s", uri, namespace)
+ }
+
+ if object != nil {
+ uri = fmt.Sprintf("%s/%v", uri, object)
+ }
+
+ return uri
+}
+
+func (h *HttpAdapter) TestConnection() error {
+ conn, err := net.Dial("tcp", net.JoinHostPort(h.host, fmt.Sprintf("%d", h.port)))
+ if err != nil {
+ return err
+ }
+ conn.Close()
+
+ return nil
+}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/adapter.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/adapter.go
new file mode 100644
index 0000000..8af0520
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/adapter.go
@@ -0,0 +1,9 @@
+package ipp
+
+import "io"
+
+type Adapter interface {
+ SendRequest(url string, req *Request, additionalResponseData io.Writer) (*Response, error)
+ GetHttpUri(namespace string, object any) string
+ TestConnection() error
+}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/attribute.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/attribute.go
new file mode 100644
index 0000000..295f521
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/attribute.go
@@ -0,0 +1,528 @@
+package ipp
+
+import (
+ "encoding/binary"
+ "fmt"
+ "io"
+)
+
+const (
+ sizeInteger = int16(4)
+ sizeBoolean = int16(1)
+)
+
+// AttributeEncoder encodes attribute to a io.Writer
+type AttributeEncoder struct {
+ writer io.Writer
+}
+
+// NewAttributeEncoder returns a new encoder that writes to w
+func NewAttributeEncoder(w io.Writer) *AttributeEncoder {
+ return &AttributeEncoder{w}
+}
+
+// Encode encodes a attribute and its value to a io.Writer
+// the tag is determined by the AttributeTagMapping map
+func (e *AttributeEncoder) Encode(attribute string, value any) error {
+ tag, ok := AttributeTagMapping[attribute]
+ if !ok {
+ return fmt.Errorf("cannot get tag of attribute %s", attribute)
+ }
+
+ switch v := value.(type) {
+ case int:
+ if tag != TagInteger && tag != TagEnum {
+ return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
+ }
+
+ if err := e.encodeTag(tag); err != nil {
+ return err
+ }
+
+ if err := e.encodeString(attribute); err != nil {
+ return err
+ }
+
+ if err := e.encodeInteger(int32(v)); err != nil {
+ return err
+ }
+ case int16:
+ if tag != TagInteger && tag != TagEnum {
+ return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
+ }
+
+ if err := e.encodeTag(tag); err != nil {
+ return err
+ }
+
+ if err := e.encodeString(attribute); err != nil {
+ return err
+ }
+
+ if err := e.encodeInteger(int32(v)); err != nil {
+ return err
+ }
+ case int8:
+ if tag != TagInteger && tag != TagEnum {
+ return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
+ }
+
+ if err := e.encodeTag(tag); err != nil {
+ return err
+ }
+
+ if err := e.encodeString(attribute); err != nil {
+ return err
+ }
+
+ if err := e.encodeInteger(int32(v)); err != nil {
+ return err
+ }
+ case int32:
+ if tag != TagInteger && tag != TagEnum {
+ return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
+ }
+
+ if err := e.encodeTag(tag); err != nil {
+ return err
+ }
+
+ if err := e.encodeString(attribute); err != nil {
+ return err
+ }
+
+ if err := e.encodeInteger(v); err != nil {
+ return err
+ }
+ case int64:
+ if tag != TagInteger && tag != TagEnum {
+ return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
+ }
+
+ if err := e.encodeTag(tag); err != nil {
+ return err
+ }
+
+ if err := e.encodeString(attribute); err != nil {
+ return err
+ }
+
+ if err := e.encodeInteger(int32(v)); err != nil {
+ return err
+ }
+ case []int:
+ if tag != TagInteger && tag != TagEnum {
+ return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
+ }
+
+ for index, val := range v {
+ if err := e.encodeTag(tag); err != nil {
+ return err
+ }
+
+ if index == 0 {
+ if err := e.encodeString(attribute); err != nil {
+ return err
+ }
+ } else {
+ if err := e.writeNullByte(); err != nil {
+ return err
+ }
+ }
+
+ if err := e.encodeInteger(int32(val)); err != nil {
+ return err
+ }
+ }
+ case []int16:
+ if tag != TagInteger && tag != TagEnum {
+ return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
+ }
+
+ for index, val := range v {
+ if err := e.encodeTag(tag); err != nil {
+ return err
+ }
+
+ if index == 0 {
+ if err := e.encodeString(attribute); err != nil {
+ return err
+ }
+ } else {
+ if err := e.writeNullByte(); err != nil {
+ return err
+ }
+ }
+
+ if err := e.encodeInteger(int32(val)); err != nil {
+ return err
+ }
+ }
+ case []int8:
+ if tag != TagInteger && tag != TagEnum {
+ return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
+ }
+
+ for index, val := range v {
+ if err := e.encodeTag(tag); err != nil {
+ return err
+ }
+
+ if index == 0 {
+ if err := e.encodeString(attribute); err != nil {
+ return err
+ }
+ } else {
+ if err := e.writeNullByte(); err != nil {
+ return err
+ }
+ }
+
+ if err := e.encodeInteger(int32(val)); err != nil {
+ return err
+ }
+ }
+ case []int32:
+ if tag != TagInteger && tag != TagEnum {
+ return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
+ }
+
+ for index, val := range v {
+ if err := e.encodeTag(tag); err != nil {
+ return err
+ }
+
+ if index == 0 {
+ if err := e.encodeString(attribute); err != nil {
+ return err
+ }
+ } else {
+ if err := e.writeNullByte(); err != nil {
+ return err
+ }
+ }
+
+ if err := e.encodeInteger(val); err != nil {
+ return err
+ }
+ }
+ case []int64:
+ if tag != TagInteger && tag != TagEnum {
+ return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
+ }
+
+ for index, val := range v {
+ if err := e.encodeTag(tag); err != nil {
+ return err
+ }
+
+ if index == 0 {
+ if err := e.encodeString(attribute); err != nil {
+ return err
+ }
+ } else {
+ if err := e.writeNullByte(); err != nil {
+ return err
+ }
+ }
+
+ if err := e.encodeInteger(int32(val)); err != nil {
+ return err
+ }
+ }
+ case bool:
+ if tag != TagBoolean {
+ return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
+ }
+
+ if err := e.encodeTag(tag); err != nil {
+ return err
+ }
+
+ if err := e.encodeString(attribute); err != nil {
+ return err
+ }
+
+ if err := e.encodeBoolean(v); err != nil {
+ return err
+ }
+ case []bool:
+ if tag != TagBoolean {
+ return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
+ }
+
+ for index, val := range v {
+ if err := e.encodeTag(tag); err != nil {
+ return err
+ }
+
+ if index == 0 {
+ if err := e.encodeString(attribute); err != nil {
+ return err
+ }
+ } else {
+ if err := e.writeNullByte(); err != nil {
+ return err
+ }
+ }
+
+ if err := e.encodeBoolean(val); err != nil {
+ return err
+ }
+ }
+ case string:
+ if err := e.encodeTag(tag); err != nil {
+ return err
+ }
+
+ if err := e.encodeString(attribute); err != nil {
+ return err
+ }
+
+ if err := e.encodeString(v); err != nil {
+ return err
+ }
+ case []string:
+ for index, val := range v {
+ if err := e.encodeTag(tag); err != nil {
+ return err
+ }
+
+ if index == 0 {
+ if err := e.encodeString(attribute); err != nil {
+ return err
+ }
+ } else {
+ if err := e.writeNullByte(); err != nil {
+ return err
+ }
+ }
+
+ if err := e.encodeString(val); err != nil {
+ return err
+ }
+ }
+ default:
+ return fmt.Errorf("type %T is not supported", value)
+ }
+
+ return nil
+}
+
+func (e *AttributeEncoder) encodeString(s string) error {
+ if err := binary.Write(e.writer, binary.BigEndian, int16(len(s))); err != nil {
+ return err
+ }
+
+ _, err := e.writer.Write([]byte(s))
+ return err
+}
+
+func (e *AttributeEncoder) encodeInteger(i int32) error {
+ if err := binary.Write(e.writer, binary.BigEndian, sizeInteger); err != nil {
+ return err
+ }
+
+ return binary.Write(e.writer, binary.BigEndian, i)
+}
+
+func (e *AttributeEncoder) encodeBoolean(b bool) error {
+ if err := binary.Write(e.writer, binary.BigEndian, sizeBoolean); err != nil {
+ return err
+ }
+
+ return binary.Write(e.writer, binary.BigEndian, b)
+}
+
+func (e *AttributeEncoder) encodeTag(t int8) error {
+ return binary.Write(e.writer, binary.BigEndian, t)
+}
+
+func (e *AttributeEncoder) writeNullByte() error {
+ return binary.Write(e.writer, binary.BigEndian, int16(0))
+}
+
+// Attribute defines an ipp attribute
+type Attribute struct {
+ Tag int8
+ Name string
+ Value any
+}
+
+// Resolution defines the resolution attribute
+type Resolution struct {
+ Height int32
+ Width int32
+ Depth int8
+}
+
+// AttributeDecoder reads and decodes ipp from an input stream
+type AttributeDecoder struct {
+ reader io.Reader
+}
+
+// NewAttributeDecoder returns a new decoder that reads from r
+func NewAttributeDecoder(r io.Reader) *AttributeDecoder {
+ return &AttributeDecoder{r}
+}
+
+// Decode reads the next ipp attribute into a attribute struct. the type is identified by a tag passed as an argument
+func (d *AttributeDecoder) Decode(tag int8) (*Attribute, error) {
+ attr := Attribute{Tag: tag}
+
+ name, err := d.decodeString()
+ if err != nil {
+ return nil, err
+ }
+ attr.Name = name
+
+ switch attr.Tag {
+ case TagEnum, TagInteger:
+ val, err := d.decodeInteger()
+ if err != nil {
+ return nil, err
+ }
+ attr.Value = val
+ case TagBoolean:
+ val, err := d.decodeBool()
+ if err != nil {
+ return nil, err
+ }
+ attr.Value = val
+ case TagDate:
+ val, err := d.decodeDate()
+ if err != nil {
+ return nil, err
+ }
+ attr.Value = val
+ case TagRange:
+ val, err := d.decodeRange()
+ if err != nil {
+ return nil, err
+ }
+ attr.Value = val
+ case TagResolution:
+ val, err := d.decodeResolution()
+ if err != nil {
+ return nil, err
+ }
+ attr.Value = val
+ default:
+ val, err := d.decodeString()
+ if err != nil {
+ return nil, err
+ }
+ attr.Value = val
+ }
+
+ return &attr, nil
+}
+
+func (d *AttributeDecoder) decodeBool() (b bool, err error) {
+ if _, err = d.readValueLength(); err != nil {
+ return
+ }
+
+ if err = binary.Read(d.reader, binary.BigEndian, &b); err != nil {
+ return
+ }
+
+ return
+}
+
+func (d *AttributeDecoder) decodeInteger() (i int, err error) {
+ if _, err = d.readValueLength(); err != nil {
+ return
+ }
+
+ var reti int32
+ if err = binary.Read(d.reader, binary.BigEndian, &reti); err != nil {
+ return
+ }
+
+ return int(reti), nil
+}
+
+func (d *AttributeDecoder) decodeString() (string, error) {
+ length, err := d.readValueLength()
+ if err != nil {
+ return "", err
+ }
+
+ if length == 0 {
+ return "", nil
+ }
+
+ bs := make([]byte, length)
+ if _, err := d.reader.Read(bs); err != nil {
+ return "", nil
+ }
+
+ return string(bs), nil
+}
+
+func (d *AttributeDecoder) decodeDate() ([]int, error) {
+ length, err := d.readValueLength()
+ if err != nil {
+ return nil, err
+ }
+
+ is := make([]int, length)
+ var ti int8
+
+ for i := int16(0); i < length; i++ {
+ if err = binary.Read(d.reader, binary.BigEndian, &ti); err != nil {
+ return nil, err
+ }
+ is[i] = int(ti)
+ }
+
+ return is, nil
+}
+
+func (d *AttributeDecoder) decodeRange() ([]int32, error) {
+ length, err := d.readValueLength()
+ if err != nil {
+ return nil, err
+ }
+
+ // initialize range element count (c) and range slice (r)
+ c := length / 4
+ r := make([]int32, c)
+
+ for i := int16(0); i < c; i++ {
+ var ti int32
+ if err = binary.Read(d.reader, binary.BigEndian, &ti); err != nil {
+ return nil, err
+ }
+ r[i] = ti
+ }
+
+ return r, nil
+}
+
+func (d *AttributeDecoder) decodeResolution() (res Resolution, err error) {
+ _, err = d.readValueLength()
+ if err != nil {
+ return
+ }
+
+ if err = binary.Read(d.reader, binary.BigEndian, &res.Height); err != nil {
+ return
+ }
+
+ if err = binary.Read(d.reader, binary.BigEndian, &res.Width); err != nil {
+ return
+ }
+
+ if err = binary.Read(d.reader, binary.BigEndian, &res.Depth); err != nil {
+ return
+ }
+
+ return
+}
+
+func (d *AttributeDecoder) readValueLength() (length int16, err error) {
+ err = binary.Read(d.reader, binary.BigEndian, &length)
+ return
+}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/constants.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/constants.go
new file mode 100644
index 0000000..83bf41b
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/constants.go
@@ -0,0 +1,450 @@
+package ipp
+
+// ipp status codes
+const (
+ StatusCupsInvalid int16 = -1
+ StatusOk int16 = 0x0000
+ StatusOkIgnoredOrSubstituted int16 = 0x0001
+ StatusOkConflicting int16 = 0x0002
+ StatusOkIgnoredSubscriptions int16 = 0x0003
+ StatusOkIgnoredNotifications int16 = 0x0004
+ StatusOkTooManyEvents int16 = 0x0005
+ StatusOkButCancelSubscription int16 = 0x0006
+ StatusOkEventsComplete int16 = 0x0007
+ StatusRedirectionOtherSite int16 = 0x0200
+ StatusCupsSeeOther int16 = 0x0280
+ StatusErrorBadRequest int16 = 0x0400
+ StatusErrorForbidden int16 = 0x0401
+ StatusErrorNotAuthenticated int16 = 0x0402
+ StatusErrorNotAuthorized int16 = 0x0403
+ StatusErrorNotPossible int16 = 0x0404
+ StatusErrorTimeout int16 = 0x0405
+ StatusErrorNotFound int16 = 0x0406
+ StatusErrorGone int16 = 0x0407
+ StatusErrorRequestEntity int16 = 0x0408
+ StatusErrorRequestValue int16 = 0x0409
+ StatusErrorDocumentFormatNotSupported int16 = 0x040a
+ StatusErrorAttributesOrValues int16 = 0x040b
+ StatusErrorUriScheme int16 = 0x040c
+ StatusErrorCharset int16 = 0x040d
+ StatusErrorConflicting int16 = 0x040e
+ StatusErrorCompressionError int16 = 0x040f
+ StatusErrorDocumentFormatError int16 = 0x0410
+ StatusErrorDocumentAccess int16 = 0x0411
+ StatusErrorAttributesNotSettable int16 = 0x0412
+ StatusErrorIgnoredAllSubscriptions int16 = 0x0413
+ StatusErrorTooManySubscriptions int16 = 0x0414
+ StatusErrorIgnoredAllNotifications int16 = 0x0415
+ StatusErrorPrintSupportFileNotFound int16 = 0x0416
+ StatusErrorDocumentPassword int16 = 0x0417
+ StatusErrorDocumentPermission int16 = 0x0418
+ StatusErrorDocumentSecurity int16 = 0x0419
+ StatusErrorDocumentUnprintable int16 = 0x041a
+ StatusErrorAccountInfoNeeded int16 = 0x041b
+ StatusErrorAccountClosed int16 = 0x041c
+ StatusErrorAccountLimitReached int16 = 0x041d
+ StatusErrorAccountAuthorizationFailed int16 = 0x041e
+ StatusErrorNotFetchable int16 = 0x041f
+ StatusErrorCupsAccountInfoNeeded int16 = 0x049C
+ StatusErrorCupsAccountClosed int16 = 0x049d
+ StatusErrorCupsAccountLimitReached int16 = 0x049e
+ StatusErrorCupsAccountAuthorizationFailed int16 = 0x049f
+ StatusErrorInternal int16 = 0x0500
+ StatusErrorOperationNotSupported int16 = 0x0501
+ StatusErrorServiceUnavailable int16 = 0x0502
+ StatusErrorVersionNotSupported int16 = 0x0503
+ StatusErrorDevice int16 = 0x0504
+ StatusErrorTemporary int16 = 0x0505
+ StatusErrorNotAcceptingJobs int16 = 0x0506
+ StatusErrorBusy int16 = 0x0507
+ StatusErrorJobCanceled int16 = 0x0508
+ StatusErrorMultipleJobsNotSupported int16 = 0x0509
+ StatusErrorPrinterIsDeactivated int16 = 0x050a
+ StatusErrorTooManyJobs int16 = 0x050b
+ StatusErrorTooManyDocuments int16 = 0x050c
+ StatusErrorCupsAuthenticationCanceled int16 = 0x1000
+ StatusErrorCupsPki int16 = 0x1001
+ StatusErrorCupsUpgradeRequired int16 = 0x1002
+)
+
+// ipp operations
+const (
+ OperationCupsInvalid int16 = -0x0001
+ OperationCupsNone int16 = 0x0000
+ OperationPrintJob int16 = 0x0002
+ OperationPrintUri int16 = 0x0003
+ OperationValidateJob int16 = 0x0004
+ OperationCreateJob int16 = 0x0005
+ OperationSendDocument int16 = 0x0006
+ OperationSendUri int16 = 0x0007
+ OperationCancelJob int16 = 0x0008
+ OperationGetJobAttributes int16 = 0x0009
+ OperationGetJobs int16 = 0x000a
+ OperationGetPrinterAttributes int16 = 0x000b
+ OperationHoldJob int16 = 0x000c
+ OperationReleaseJob int16 = 0x000d
+ OperationRestartJob int16 = 0x000e
+ OperationPausePrinter int16 = 0x0010
+ OperationResumePrinter int16 = 0x0011
+ OperationPurgeJobs int16 = 0x0012
+ OperationSetPrinterAttributes int16 = 0x0013
+ OperationSetJobAttributes int16 = 0x0014
+ OperationGetPrinterSupportedValues int16 = 0x0015
+ OperationCreatePrinterSubscriptions int16 = 0x0016
+ OperationCreateJobSubscriptions int16 = 0x0017
+ OperationGetSubscriptionAttributes int16 = 0x0018
+ OperationGetSubscriptions int16 = 0x0019
+ OperationRenewSubscription int16 = 0x001a
+ OperationCancelSubscription int16 = 0x001b
+ OperationGetNotifications int16 = 0x001c
+ OperationSendNotifications int16 = 0x001d
+ OperationGetResourceAttributes int16 = 0x001e
+ OperationGetResourceData int16 = 0x001f
+ OperationGetResources int16 = 0x0020
+ OperationGetPrintSupportFiles int16 = 0x0021
+ OperationEnablePrinter int16 = 0x0022
+ OperationDisablePrinter int16 = 0x0023
+ OperationPausePrinterAfterCurrentJob int16 = 0x0024
+ OperationHoldNewJobs int16 = 0x0025
+ OperationReleaseHeldNewJobs int16 = 0x0026
+ OperationDeactivatePrinter int16 = 0x0027
+ OperationActivatePrinter int16 = 0x0028
+ OperationRestartPrinter int16 = 0x0029
+ OperationShutdownPrinter int16 = 0x002a
+ OperationStartupPrinter int16 = 0x002b
+ OperationReprocessJob int16 = 0x002c
+ OperationCancelCurrentJob int16 = 0x002d
+ OperationSuspendCurrentJob int16 = 0x002e
+ OperationResumeJob int16 = 0x002f
+ OperationOperationPromoteJob int16 = 0x0030
+ OperationScheduleJobAfter int16 = 0x0031
+ OperationCancelDocument int16 = 0x0033
+ OperationGetDocumentAttributes int16 = 0x0034
+ OperationGetDocuments int16 = 0x0035
+ OperationDeleteDocument int16 = 0x0036
+ OperationSetDocumentAttributes int16 = 0x0037
+ OperationCancelJobs int16 = 0x0038
+ OperationCancelMyJobs int16 = 0x0039
+ OperationResubmitJob int16 = 0x003a
+ OperationCloseJob int16 = 0x003b
+ OperationIdentifyPrinter int16 = 0x003c
+ OperationValidateDocument int16 = 0x003d
+ OperationAddDocumentImages int16 = 0x003e
+ OperationAcknowledgeDocument int16 = 0x003f
+ OperationAcknowledgeIdentifyPrinter int16 = 0x0040
+ OperationAcknowledgeJob int16 = 0x0041
+ OperationFetchDocument int16 = 0x0042
+ OperationFetchJob int16 = 0x0043
+ OperationGetOutputDeviceAttributes int16 = 0x0044
+ OperationUpdateActiveJobs int16 = 0x0045
+ OperationDeregisterOutputDevice int16 = 0x0046
+ OperationUpdateDocumentStatus int16 = 0x0047
+ OperationUpdateJobStatus int16 = 0x0048
+ OperationUpdateOutputDeviceAttributes int16 = 0x0049
+ OperationGetNextDocumentData int16 = 0x004a
+ OperationAllocatePrinterResources int16 = 0x004b
+ OperationCreatePrinter int16 = 0x004c
+ OperationDeallocatePrinterResources int16 = 0x004d
+ OperationDeletePrinter int16 = 0x004e
+ OperationGetPrinters int16 = 0x004f
+ OperationShutdownOnePrinter int16 = 0x0050
+ OperationStartupOnePrinter int16 = 0x0051
+ OperationCancelResource int16 = 0x0052
+ OperationCreateResource int16 = 0x0053
+ OperationInstallResource int16 = 0x0054
+ OperationSendResourceData int16 = 0x0055
+ OperationSetResourceAttributes int16 = 0x0056
+ OperationCreateResourceSubscriptions int16 = 0x0057
+ OperationCreateSystemSubscriptions int16 = 0x0058
+ OperationDisableAllPrinters int16 = 0x0059
+ OperationEnableAllPrinters int16 = 0x005a
+ OperationGetSystemAttributes int16 = 0x005b
+ OperationGetSystemSupportedValues int16 = 0x005c
+ OperationPauseAllPrinters int16 = 0x005d
+ OperationPauseAllPrintersAfterCurrentJob int16 = 0x005e
+ OperationRegisterOutputDevice int16 = 0x005f
+ OperationRestartSystem int16 = 0x0060
+ OperationResumeAllPrinters int16 = 0x0061
+ OperationSetSystemAttributes int16 = 0x0062
+ OperationShutdownAllPrinter int16 = 0x0063
+ OperationStartupAllPrinters int16 = 0x0064
+ OperationPrivate int16 = 0x4000
+ OperationCupsGetDefault int16 = 0x4001
+ OperationCupsGetPrinters int16 = 0x4002
+ OperationCupsAddModifyPrinter int16 = 0x4003
+ OperationCupsDeletePrinter int16 = 0x4004
+ OperationCupsGetClasses int16 = 0x4005
+ OperationCupsAddModifyClass int16 = 0x4006
+ OperationCupsDeleteClass int16 = 0x4007
+ OperationCupsAcceptJobs int16 = 0x4008
+ OperationCupsRejectJobs int16 = 0x4009
+ OperationCupsSetDefault int16 = 0x400a
+ OperationCupsGetDevices int16 = 0x400b
+ OperationCupsGetPPDs int16 = 0x400c
+ OperationCupsMoveJob int16 = 0x400d
+ OperationCupsAuthenticateJob int16 = 0x400e
+ OperationCupsGetPpd int16 = 0x400f
+ OperationCupsGetDocument int16 = 0x4027
+ OperationCupsCreateLocalPrinter int16 = 0x4028
+)
+
+// ipp tags
+const (
+ TagCupsInvalid int8 = -1
+ TagZero int8 = 0x00
+ TagOperation int8 = 0x01
+ TagJob int8 = 0x02
+ TagEnd int8 = 0x03
+ TagPrinter int8 = 0x04
+ TagUnsupportedGroup int8 = 0x05
+ TagSubscription int8 = 0x06
+ TagEventNotification int8 = 0x07
+ TagResource int8 = 0x08
+ TagDocument int8 = 0x09
+ TagSystem int8 = 0x0a
+ TagUnsupportedValue int8 = 0x10
+ TagDefault int8 = 0x11
+ TagUnknown int8 = 0x12
+ TagNoValue int8 = 0x13
+ TagNotSettable int8 = 0x15
+ TagDeleteAttr int8 = 0x16
+ TagAdminDefine int8 = 0x17
+ TagInteger int8 = 0x21
+ TagBoolean int8 = 0x22
+ TagEnum int8 = 0x23
+ TagString int8 = 0x30
+ TagDate int8 = 0x31
+ TagResolution int8 = 0x32
+ TagRange int8 = 0x33
+ TagBeginCollection int8 = 0x34
+ TagTextLang int8 = 0x35
+ TagNameLang int8 = 0x36
+ TagEndCollection int8 = 0x37
+ TagText int8 = 0x41
+ TagName int8 = 0x42
+ TagReservedString int8 = 0x43
+ TagKeyword int8 = 0x44
+ TagUri int8 = 0x45
+ TagUriScheme int8 = 0x46
+ TagCharset int8 = 0x47
+ TagLanguage int8 = 0x48
+ TagMimeType int8 = 0x49
+ TagMemberName int8 = 0x4a
+ TagExtension int8 = 0x7f
+)
+
+// job states
+const (
+ JobStatePending int8 = 0x03
+ JobStateHeld int8 = 0x04
+ JobStateProcessing int8 = 0x05
+ JobStateStopped int8 = 0x06
+ JobStateCanceled int8 = 0x07
+ JobStateAborted int8 = 0x08
+ JobStateCompleted int8 = 0x09
+)
+
+// document states
+const (
+ DocumentStatePending int8 = 0x03
+ DocumentStateProcessing int8 = 0x05
+ DocumentStateCanceled int8 = 0x07
+ DocumentStateAborted int8 = 0x08
+ DocumentStateCompleted int8 = 0x08
+)
+
+// printer states
+const (
+ PrinterStateIdle int8 = 0x0003
+ PrinterStateProcessing int8 = 0x0004
+ PrinterStateStopped int8 = 0x0005
+)
+
+// job state filter
+const (
+ JobStateFilterNotCompleted = "not-completed"
+ JobStateFilterCompleted = "completed"
+ JobStateFilterAll = "all"
+)
+
+// error policies
+const (
+ ErrorPolicyRetryJob = "retry-job"
+ ErrorPolicyAbortJob = "abort-job"
+ ErrorPolicyRetryCurrentJob = "retry-current-job"
+ ErrorPolicyStopPrinter = "stop-printer"
+)
+
+// ipp defaults
+const (
+ CharsetLanguage = "en-US"
+ Charset = "utf-8"
+ ProtocolVersionMajor = int8(2)
+ ProtocolVersionMinor = int8(0)
+
+ DefaultJobPriority = 50
+)
+
+// useful mime types for ipp
+const (
+ MimeTypePostscript = "application/postscript"
+ MimeTypePDF = "application/pdf"
+ MimeTypeOctetStream = "application/octet-stream"
+)
+
+// ipp content types
+const (
+ ContentTypeIPP = "application/ipp"
+)
+
+// known ipp attributes
+const (
+ AttributeCopies = "copies"
+ AttributeDocumentFormat = "document-format"
+ AttributeDocumentName = "document-name"
+ AttributeJobID = "job-id"
+ AttributeJobName = "job-name"
+ AttributeJobPriority = "job-priority"
+ AttributeJobURI = "job-uri"
+ AttributeLastDocument = "last-document"
+ AttributeMyJobs = "my-jobs"
+ AttributePPDName = "ppd-name"
+ AttributePPDMakeAndModel = "ppd-make-and-model"
+ AttributePrinterIsShared = "printer-is-shared"
+ AttributePrinterIsTemporary = "printer-is-temporary"
+ AttributePrinterURI = "printer-uri"
+ AttributePurgeJobs = "purge-jobs"
+ AttributeRequestedAttributes = "requested-attributes"
+ AttributeRequestingUserName = "requesting-user-name"
+ AttributeWhichJobs = "which-jobs"
+ AttributeFirstJobID = "first-job-id"
+ AttributeLimit = "limit"
+ AttributeStatusMessage = "status-message"
+ AttributeCharset = "attributes-charset"
+ AttributeNaturalLanguage = "attributes-natural-language"
+ AttributeDeviceURI = "device-uri"
+ AttributeHoldJobUntil = "job-hold-until"
+ AttributePrinterErrorPolicy = "printer-error-policy"
+ AttributePrinterInfo = "printer-info"
+ AttributePrinterLocation = "printer-location"
+ AttributePrinterName = "printer-name"
+ AttributePrinterStateReasons = "printer-state-reasons"
+ AttributeJobPrinterURI = "job-printer-uri"
+ AttributeMemberURIs = "member-uris"
+ AttributeDocumentNumber = "document-number"
+ AttributeDocumentState = "document-state"
+ AttributeFinishings = "finishings"
+ AttributeJobHoldUntil = "hold-job-until"
+ AttributeJobSheets = "job-sheets"
+ AttributeJobState = "job-state"
+ AttributeJobStateReason = "job-state-reason"
+ AttributeMedia = "media"
+ AttributeSides = "sides"
+ AttributeNumberUp = "number-up"
+ AttributeOrientationRequested = "orientation-requested"
+ AttributePrintQuality = "print-quality"
+ AttributePrinterIsAcceptingJobs = "printer-is-accepting-jobs"
+ AttributePrinterResolution = "printer-resolution"
+ AttributePrinterState = "printer-state"
+ AttributeMemberNames = "member-names"
+ AttributePrinterType = "printer-type"
+ AttributePrinterMakeAndModel = "printer-make-and-model"
+ AttributePrinterStateMessage = "printer-state-message"
+ AttributePrinterUriSupported = "printer-uri-supported"
+ AttributeJobMediaProgress = "job-media-progress"
+ AttributeJobKilobyteOctets = "job-k-octets"
+ AttributeNumberOfDocuments = "number-of-documents"
+ AttributeJobOriginatingUserName = "job-originating-user-name"
+ AttributeOutputOrder = "outputorder"
+ AttributeJobStateReasons = "job-state-reasons"
+ AttributeJobStateMessage = "job-state-message"
+ AttributeJobPrinterStateReasons = "job-printer-state-reasons"
+ AttributeJobPrinterStateMessage = "job-printer-state-message"
+ AttributeJobImpressionsCompleted = "job-impressions-completed"
+ AttributePrintScaling = "print-scaling"
+)
+
+// Default attributes
+var (
+ DefaultClassAttributes = []string{AttributePrinterName, AttributeMemberNames}
+ DefaultPrinterAttributes = []string{
+ AttributePrinterName, AttributePrinterType, AttributePrinterLocation, AttributePrinterInfo,
+ AttributePrinterMakeAndModel, AttributePrinterState, AttributePrinterStateMessage, AttributePrinterStateReasons,
+ AttributePrinterUriSupported, AttributeDeviceURI, AttributePrinterIsShared,
+ }
+ DefaultJobAttributes = []string{
+ AttributeJobID, AttributeJobName, AttributePrinterURI, AttributeJobState, AttributeJobStateReason,
+ AttributeJobHoldUntil, AttributeJobMediaProgress, AttributeJobKilobyteOctets, AttributeNumberOfDocuments, AttributeCopies,
+ AttributeJobOriginatingUserName,
+ }
+)
+
+// Attribute to tag mapping
+var (
+ AttributeTagMapping = map[string]int8{
+ AttributeCharset: TagCharset,
+ AttributeNaturalLanguage: TagLanguage,
+ AttributeCopies: TagInteger,
+ AttributeDeviceURI: TagUri,
+ AttributeDocumentFormat: TagMimeType,
+ AttributeDocumentName: TagName,
+ AttributeDocumentNumber: TagInteger,
+ AttributeDocumentState: TagEnum,
+ AttributeFinishings: TagEnum,
+ AttributeJobHoldUntil: TagKeyword,
+ AttributeHoldJobUntil: TagKeyword,
+ AttributeJobID: TagInteger,
+ AttributeJobName: TagName,
+ AttributeJobPrinterURI: TagUri,
+ AttributeJobPriority: TagInteger,
+ AttributeJobSheets: TagName,
+ AttributeJobState: TagEnum,
+ AttributeJobStateReason: TagKeyword,
+ AttributeJobURI: TagUri,
+ AttributeLastDocument: TagBoolean,
+ AttributeMedia: TagKeyword,
+ AttributeSides: TagKeyword,
+ AttributeMemberURIs: TagUri,
+ AttributeMyJobs: TagBoolean,
+ AttributeNumberUp: TagInteger,
+ AttributeOrientationRequested: TagEnum,
+ AttributePPDName: TagName,
+ AttributePPDMakeAndModel: TagText,
+ AttributeNumberOfDocuments: TagInteger,
+ AttributePrintQuality: TagEnum,
+ AttributePrinterErrorPolicy: TagName,
+ AttributePrinterInfo: TagText,
+ AttributePrinterIsAcceptingJobs: TagBoolean,
+ AttributePrinterIsShared: TagBoolean,
+ AttributePrinterIsTemporary: TagBoolean,
+ AttributePrinterName: TagName,
+ AttributePrinterLocation: TagText,
+ AttributePrinterResolution: TagResolution,
+ AttributePrinterState: TagEnum,
+ AttributePrinterStateReasons: TagKeyword,
+ AttributePrinterURI: TagUri,
+ AttributePurgeJobs: TagBoolean,
+ AttributeRequestedAttributes: TagKeyword,
+ AttributeRequestingUserName: TagName,
+ AttributeWhichJobs: TagKeyword,
+ AttributeFirstJobID: TagInteger,
+ AttributeStatusMessage: TagText,
+ AttributeLimit: TagInteger,
+ AttributeOutputOrder: TagName,
+ AttributeJobStateReasons: TagString,
+ AttributeJobStateMessage: TagString,
+ AttributeJobPrinterStateReasons: TagString,
+ AttributeJobPrinterStateMessage: TagString,
+ AttributeJobImpressionsCompleted: TagInteger,
+ AttributePrintScaling: TagKeyword,
+ // IPP Subscription/Notification attributes (added for dankdots)
+ "notify-events": TagKeyword,
+ "notify-pull-method": TagKeyword,
+ "notify-lease-duration": TagInteger,
+ "notify-subscription-id": TagInteger,
+ "notify-subscription-ids": TagInteger,
+ "notify-sequence-numbers": TagInteger,
+ "notify-wait": TagBoolean,
+ "notify-recipient-uri": TagUri,
+ }
+)
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/cups-client.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/cups-client.go
new file mode 100644
index 0000000..68d60ce
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/cups-client.go
@@ -0,0 +1,313 @@
+package ipp
+
+import (
+ "io"
+ "strings"
+)
+
+// CUPSClient implements a ipp client with specific cups operations
+type CUPSClient struct {
+ *IPPClient
+}
+
+// NewCUPSClient creates a new cups ipp client (used HttpAdapter internally)
+func NewCUPSClient(host string, port int, username, password string, useTLS bool) *CUPSClient {
+ ippClient := NewIPPClient(host, port, username, password, useTLS)
+ return &CUPSClient{ippClient}
+}
+
+// NewCUPSClientWithAdapter creates a new cups ipp client with given Adapter
+func NewCUPSClientWithAdapter(username string, adapter Adapter) *CUPSClient {
+ ippClient := NewIPPClientWithAdapter(username, adapter)
+ return &CUPSClient{ippClient}
+}
+
+// GetDevices returns a map of device uris and printer attributes
+func (c *CUPSClient) GetDevices() (map[string]Attributes, error) {
+ req := NewRequest(OperationCupsGetDevices, 1)
+
+ resp, err := c.SendRequest(c.adapter.GetHttpUri("", nil), req, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ printerNameMap := make(map[string]Attributes)
+
+ for _, printerAttributes := range resp.PrinterAttributes {
+ printerNameMap[printerAttributes[AttributeDeviceURI][0].Value.(string)] = printerAttributes
+ }
+
+ return printerNameMap, nil
+}
+
+// MoveJob moves a job to a other printer
+func (c *CUPSClient) MoveJob(jobID int, destPrinter string) error {
+ req := NewRequest(OperationCupsMoveJob, 1)
+ req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID)
+ req.PrinterAttributes[AttributeJobPrinterURI] = c.getPrinterUri(destPrinter)
+
+ _, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil)
+ return err
+}
+
+// MoveAllJob moves all job from a printer to a other printer
+func (c *CUPSClient) MoveAllJob(srcPrinter, destPrinter string) error {
+ req := NewRequest(OperationCupsMoveJob, 1)
+ req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(srcPrinter)
+ req.PrinterAttributes[AttributeJobPrinterURI] = c.getPrinterUri(destPrinter)
+
+ _, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil)
+ return err
+}
+
+// GetPPDs returns a map of ppd names and attributes
+func (c *CUPSClient) GetPPDs() (map[string]Attributes, error) {
+ req := NewRequest(OperationCupsGetPPDs, 1)
+
+ resp, err := c.SendRequest(c.adapter.GetHttpUri("", nil), req, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ ppdNameMap := make(map[string]Attributes)
+
+ for _, printerAttributes := range resp.PrinterAttributes {
+ ppdNameMap[printerAttributes[AttributePPDName][0].Value.(string)] = printerAttributes
+ }
+
+ return ppdNameMap, nil
+}
+
+// AcceptJobs lets a printer accept jobs again
+func (c *CUPSClient) AcceptJobs(printer string) error {
+ req := NewRequest(OperationCupsAcceptJobs, 1)
+ req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
+
+ _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
+ return err
+}
+
+// RejectJobs does not let a printer accept jobs
+func (c *CUPSClient) RejectJobs(printer string) error {
+ req := NewRequest(OperationCupsRejectJobs, 1)
+ req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
+
+ _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
+ return err
+}
+
+// AddPrinterToClass adds a printer to a class, if the class does not exists it will be created
+func (c *CUPSClient) AddPrinterToClass(class, printer string) error {
+ attributes, err := c.GetPrinterAttributes(class, []string{AttributeMemberURIs})
+ if err != nil && !IsNotExistsError(err) {
+ return err
+ }
+
+ memberURIList := make([]string, 0)
+
+ if !IsNotExistsError(err) {
+ for _, member := range attributes[AttributeMemberURIs] {
+ memberString := strings.Split(member.Value.(string), "/")
+ printerName := memberString[len(memberString)-1]
+
+ if printerName == printer {
+ return nil
+ }
+
+ memberURIList = append(memberURIList, member.Value.(string))
+ }
+ }
+
+ memberURIList = append(memberURIList, c.getPrinterUri(printer))
+
+ req := NewRequest(OperationCupsAddModifyClass, 1)
+ req.OperationAttributes[AttributePrinterURI] = c.getClassUri(class)
+ req.PrinterAttributes[AttributeMemberURIs] = memberURIList
+
+ _, err = c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
+ return err
+}
+
+// DeletePrinterFromClass removes a printer from a class, if a class has no more printer it will be deleted
+func (c *CUPSClient) DeletePrinterFromClass(class, printer string) error {
+ attributes, err := c.GetPrinterAttributes(class, []string{AttributeMemberURIs})
+ if err != nil {
+ return err
+ }
+
+ memberURIList := make([]string, 0)
+
+ for _, member := range attributes[AttributeMemberURIs] {
+ memberString := strings.Split(member.Value.(string), "/")
+ printerName := memberString[len(memberString)-1]
+
+ if printerName != printer {
+ memberURIList = append(memberURIList, member.Value.(string))
+ }
+ }
+
+ if len(memberURIList) == 0 {
+ return c.DeleteClass(class)
+ }
+
+ req := NewRequest(OperationCupsAddModifyClass, 1)
+ req.OperationAttributes[AttributePrinterURI] = c.getClassUri(class)
+ req.PrinterAttributes[AttributeMemberURIs] = memberURIList
+
+ _, err = c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
+ return err
+}
+
+// DeleteClass deletes a class
+func (c *CUPSClient) DeleteClass(class string) error {
+ req := NewRequest(OperationCupsDeleteClass, 1)
+ req.OperationAttributes[AttributePrinterURI] = c.getClassUri(class)
+
+ _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
+ return err
+}
+
+// CreatePrinter creates a new printer
+func (c *CUPSClient) CreatePrinter(name, deviceURI, ppd string, shared bool, errorPolicy string, information, location string) error {
+ req := NewRequest(OperationCupsAddModifyPrinter, 1)
+ req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(name)
+ req.OperationAttributes[AttributePPDName] = ppd
+ req.OperationAttributes[AttributePrinterIsShared] = shared
+ req.PrinterAttributes[AttributePrinterStateReasons] = "none"
+ req.PrinterAttributes[AttributeDeviceURI] = deviceURI
+ req.PrinterAttributes[AttributePrinterInfo] = information
+ req.PrinterAttributes[AttributePrinterLocation] = location
+ req.PrinterAttributes[AttributePrinterErrorPolicy] = errorPolicy
+
+ _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
+ return err
+}
+
+// SetPrinterPPD sets the ppd for a printer
+func (c *CUPSClient) SetPrinterPPD(printer, ppd string) error {
+ req := NewRequest(OperationCupsAddModifyPrinter, 1)
+ req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
+ req.OperationAttributes[AttributePPDName] = ppd
+
+ _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
+ return err
+}
+
+// SetPrinterDeviceURI sets the device uri for a printer
+func (c *CUPSClient) SetPrinterDeviceURI(printer, deviceURI string) error {
+ req := NewRequest(OperationCupsAddModifyPrinter, 1)
+ req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
+ req.PrinterAttributes[AttributeDeviceURI] = deviceURI
+
+ _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
+ return err
+}
+
+// SetPrinterIsShared shares or unshares a printer in the network
+func (c *CUPSClient) SetPrinterIsShared(printer string, shared bool) error {
+ req := NewRequest(OperationCupsAddModifyPrinter, 1)
+ req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
+ req.OperationAttributes[AttributePrinterIsShared] = shared
+
+ _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
+ return err
+}
+
+// SetPrinterErrorPolicy sets the error policy for a printer
+func (c *CUPSClient) SetPrinterErrorPolicy(printer string, errorPolicy string) error {
+ req := NewRequest(OperationCupsAddModifyPrinter, 1)
+ req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
+ req.PrinterAttributes[AttributePrinterErrorPolicy] = errorPolicy
+
+ _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
+ return err
+}
+
+// SetPrinterInformation sets general printer information
+func (c *CUPSClient) SetPrinterInformation(printer, information string) error {
+ req := NewRequest(OperationCupsAddModifyPrinter, 1)
+ req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
+ req.PrinterAttributes[AttributePrinterInfo] = information
+
+ _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
+ return err
+}
+
+// SetPrinterLocation sets the printer location
+func (c *CUPSClient) SetPrinterLocation(printer, location string) error {
+ req := NewRequest(OperationCupsAddModifyPrinter, 1)
+ req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
+ req.PrinterAttributes[AttributePrinterLocation] = location
+
+ _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
+ return err
+}
+
+// DeletePrinter deletes a printer
+func (c *CUPSClient) DeletePrinter(printer string) error {
+ req := NewRequest(OperationCupsDeletePrinter, 1)
+ req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
+
+ _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
+ return err
+}
+
+// GetPrinters returns a map of printer names and attributes
+func (c *CUPSClient) GetPrinters(attributes []string) (map[string]Attributes, error) {
+ req := NewRequest(OperationCupsGetPrinters, 1)
+
+ if attributes == nil {
+ req.OperationAttributes[AttributeRequestedAttributes] = DefaultPrinterAttributes
+ } else {
+ req.OperationAttributes[AttributeRequestedAttributes] = append(attributes, AttributePrinterName)
+ }
+
+ resp, err := c.SendRequest(c.adapter.GetHttpUri("", nil), req, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ printerNameMap := make(map[string]Attributes)
+
+ for _, printerAttributes := range resp.PrinterAttributes {
+ printerNameMap[printerAttributes[AttributePrinterName][0].Value.(string)] = printerAttributes
+ }
+
+ return printerNameMap, nil
+}
+
+// GetClasses returns a map of class names and attributes
+func (c *CUPSClient) GetClasses(attributes []string) (map[string]Attributes, error) {
+ req := NewRequest(OperationCupsGetClasses, 1)
+
+ if attributes == nil {
+ req.OperationAttributes[AttributeRequestedAttributes] = DefaultClassAttributes
+ } else {
+ req.OperationAttributes[AttributeRequestedAttributes] = append(attributes, AttributePrinterName)
+ }
+
+ resp, err := c.SendRequest(c.adapter.GetHttpUri("", nil), req, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ printerNameMap := make(map[string]Attributes)
+
+ for _, printerAttributes := range resp.PrinterAttributes {
+ printerNameMap[printerAttributes[AttributePrinterName][0].Value.(string)] = printerAttributes
+ }
+
+ return printerNameMap, nil
+}
+
+// PrintTestPage prints a test page using the provided PDF data
+func (c *CUPSClient) PrintTestPage(printer string, testPageData io.Reader, size int) (int, error) {
+ return c.PrintJob(Document{
+ Document: testPageData,
+ Name: "Test Page",
+ Size: size,
+ MimeType: MimeTypePDF,
+ }, printer, map[string]any{
+ AttributeJobName: "Test Page",
+ })
+}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/error.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/error.go
new file mode 100644
index 0000000..e4edf5e
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/error.go
@@ -0,0 +1,31 @@
+package ipp
+
+import "fmt"
+
+// IsNotExistsError checks a given error whether a printer or class does not exist
+func IsNotExistsError(err error) bool {
+ if err == nil {
+ return false
+ }
+
+ return err.Error() == "The printer or class does not exist."
+}
+
+// IPPError used for non ok ipp status codes
+type IPPError struct {
+ Status int16
+ Message string
+}
+
+func (e IPPError) Error() string {
+ return fmt.Sprintf("ipp status: %d, message: %s", e.Status, e.Message)
+}
+
+// HTTPError used for non 200 http codes
+type HTTPError struct {
+ Code int
+}
+
+func (e HTTPError) Error() string {
+ return fmt.Sprintf("got http code %d", e.Code)
+}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/ipp-client.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/ipp-client.go
new file mode 100644
index 0000000..a21dd54
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/ipp-client.go
@@ -0,0 +1,329 @@
+package ipp
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "path"
+)
+
+// Document wraps an io.Reader with more information, needed for encoding
+type Document struct {
+ Document io.Reader
+ Size int
+ Name string
+ MimeType string
+}
+
+// IPPClient implements a generic ipp client
+type IPPClient struct {
+ username string
+ adapter Adapter
+}
+
+// NewIPPClient creates a new generic ipp client (used HttpAdapter internally)
+func NewIPPClient(host string, port int, username, password string, useTLS bool) *IPPClient {
+ adapter := NewHttpAdapter(host, port, username, password, useTLS)
+
+ return &IPPClient{
+ username: username,
+ adapter: adapter,
+ }
+}
+
+// NewIPPClientWithAdapter creates a new generic ipp client with given Adapter
+func NewIPPClientWithAdapter(username string, adapter Adapter) *IPPClient {
+ return &IPPClient{
+ username: username,
+ adapter: adapter,
+ }
+}
+
+func (c *IPPClient) getPrinterUri(printer string) string {
+ return fmt.Sprintf("ipp://localhost/printers/%s", printer)
+}
+
+func (c *IPPClient) getJobUri(jobID int) string {
+ return fmt.Sprintf("ipp://localhost/jobs/%d", jobID)
+}
+
+func (c *IPPClient) getClassUri(printer string) string {
+ return fmt.Sprintf("ipp://localhost/classes/%s", printer)
+}
+
+// SendRequest sends a request to a remote uri end returns the response
+func (c *IPPClient) SendRequest(url string, req *Request, additionalResponseData io.Writer) (*Response, error) {
+ if _, ok := req.OperationAttributes[AttributeRequestingUserName]; !ok {
+ req.OperationAttributes[AttributeRequestingUserName] = c.username
+ }
+
+ return c.adapter.SendRequest(url, req, additionalResponseData)
+}
+
+// PrintDocuments prints one or more documents using a Create-Job operation followed by one or more Send-Document operation(s). custom job settings can be specified via the jobAttributes parameter
+func (c *IPPClient) PrintDocuments(docs []Document, printer string, jobAttributes map[string]any) (int, error) {
+ printerURI := c.getPrinterUri(printer)
+
+ req := NewRequest(OperationCreateJob, 1)
+ req.OperationAttributes[AttributePrinterURI] = printerURI
+ req.OperationAttributes[AttributeRequestingUserName] = c.username
+
+ // set defaults for some attributes, may get overwritten
+ req.OperationAttributes[AttributeJobName] = docs[0].Name
+ req.OperationAttributes[AttributeCopies] = 1
+ req.OperationAttributes[AttributeJobPriority] = DefaultJobPriority
+
+ for key, value := range jobAttributes {
+ req.JobAttributes[key] = value
+ }
+
+ resp, err := c.SendRequest(c.adapter.GetHttpUri("printers", printer), req, nil)
+ if err != nil {
+ return -1, err
+ }
+
+ if len(resp.JobAttributes) == 0 {
+ return 0, errors.New("server doesn't returned a job id")
+ }
+
+ jobID := resp.JobAttributes[0][AttributeJobID][0].Value.(int)
+
+ documentCount := len(docs) - 1
+
+ for docID, doc := range docs {
+ req = NewRequest(OperationSendDocument, 2)
+ req.OperationAttributes[AttributePrinterURI] = printerURI
+ req.OperationAttributes[AttributeRequestingUserName] = c.username
+ req.OperationAttributes[AttributeJobID] = jobID
+ req.OperationAttributes[AttributeDocumentName] = doc.Name
+ req.OperationAttributes[AttributeDocumentFormat] = doc.MimeType
+ req.OperationAttributes[AttributeLastDocument] = docID == documentCount
+ req.File = doc.Document
+ req.FileSize = doc.Size
+
+ _, err = c.SendRequest(c.adapter.GetHttpUri("printers", printer), req, nil)
+ if err != nil {
+ return -1, err
+ }
+ }
+
+ return jobID, nil
+}
+
+// PrintJob prints a document using a Print-Job operation. custom job settings can be specified via the jobAttributes parameter
+func (c *IPPClient) PrintJob(doc Document, printer string, jobAttributes map[string]any) (int, error) {
+ printerURI := c.getPrinterUri(printer)
+
+ req := NewRequest(OperationPrintJob, 1)
+ req.OperationAttributes[AttributePrinterURI] = printerURI
+ req.OperationAttributes[AttributeRequestingUserName] = c.username
+ req.OperationAttributes[AttributeJobName] = doc.Name
+ req.OperationAttributes[AttributeDocumentFormat] = doc.MimeType
+
+ // set defaults for some attributes, may get overwritten
+ req.OperationAttributes[AttributeCopies] = 1
+ req.OperationAttributes[AttributeJobPriority] = DefaultJobPriority
+
+ for key, value := range jobAttributes {
+ req.JobAttributes[key] = value
+ }
+
+ req.File = doc.Document
+ req.FileSize = doc.Size
+
+ resp, err := c.SendRequest(c.adapter.GetHttpUri("printers", printer), req, nil)
+ if err != nil {
+ return -1, err
+ }
+
+ if len(resp.JobAttributes) == 0 {
+ return 0, errors.New("server doesn't returned a job id")
+ }
+
+ jobID := resp.JobAttributes[0][AttributeJobID][0].Value.(int)
+
+ return jobID, nil
+}
+
+// PrintFile prints a local file on the file system. custom job settings can be specified via the jobAttributes parameter
+func (c *IPPClient) PrintFile(filePath, printer string, jobAttributes map[string]any) (int, error) {
+ fileStats, err := os.Stat(filePath)
+ if os.IsNotExist(err) {
+ return -1, err
+ }
+
+ fileName := path.Base(filePath)
+
+ document, err := os.Open(filePath)
+ if err != nil {
+ return 0, err
+ }
+ defer document.Close()
+
+ jobAttributes[AttributeJobName] = fileName
+
+ return c.PrintDocuments([]Document{
+ {
+ Document: document,
+ Name: fileName,
+ Size: int(fileStats.Size()),
+ MimeType: MimeTypeOctetStream,
+ },
+ }, printer, jobAttributes)
+}
+
+// GetPrinterAttributes returns the requested attributes for the specified printer, if attributes is nil the default attributes will be used
+func (c *IPPClient) GetPrinterAttributes(printer string, attributes []string) (Attributes, error) {
+ req := NewRequest(OperationGetPrinterAttributes, 1)
+ req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
+ req.OperationAttributes[AttributeRequestingUserName] = c.username
+
+ if attributes == nil {
+ req.OperationAttributes[AttributeRequestedAttributes] = DefaultPrinterAttributes
+ } else {
+ req.OperationAttributes[AttributeRequestedAttributes] = attributes
+ }
+
+ resp, err := c.SendRequest(c.adapter.GetHttpUri("printers", printer), req, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ if len(resp.PrinterAttributes) == 0 {
+ return nil, errors.New("server doesn't return any printer attributes")
+ }
+
+ return resp.PrinterAttributes[0], nil
+}
+
+// ResumePrinter resumes a printer
+func (c *IPPClient) ResumePrinter(printer string) error {
+ req := NewRequest(OperationResumePrinter, 1)
+ req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
+
+ _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
+ return err
+}
+
+// PausePrinter pauses a printer
+func (c *IPPClient) PausePrinter(printer string) error {
+ req := NewRequest(OperationPausePrinter, 1)
+ req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
+
+ _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
+ return err
+}
+
+// GetJobAttributes returns the requested attributes for the specified job, if attributes is nil the default job will be used
+func (c *IPPClient) GetJobAttributes(jobID int, attributes []string) (Attributes, error) {
+ req := NewRequest(OperationGetJobAttributes, 1)
+ req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID)
+
+ if attributes == nil {
+ req.OperationAttributes[AttributeRequestedAttributes] = DefaultJobAttributes
+ } else {
+ req.OperationAttributes[AttributeRequestedAttributes] = attributes
+ }
+
+ resp, err := c.SendRequest(c.adapter.GetHttpUri("jobs", jobID), req, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ if len(resp.JobAttributes) == 0 {
+ return nil, errors.New("server doesn't return any job attributes")
+ }
+
+ return resp.JobAttributes[0], nil
+}
+
+// GetJobs returns jobs from a printer or class
+func (c *IPPClient) GetJobs(printer, class string, whichJobs string, myJobs bool, firstJobId, limit int, attributes []string) (map[int]Attributes, error) {
+ req := NewRequest(OperationGetJobs, 1)
+ req.OperationAttributes[AttributeWhichJobs] = whichJobs
+ req.OperationAttributes[AttributeMyJobs] = myJobs
+
+ if printer != "" {
+ req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
+ } else if class != "" {
+ req.OperationAttributes[AttributePrinterURI] = c.getClassUri(printer)
+ } else {
+ req.OperationAttributes[AttributePrinterURI] = "ipp://localhost/"
+ }
+
+ if firstJobId > 0 {
+ req.OperationAttributes[AttributeFirstJobID] = firstJobId
+ }
+
+ if limit > 0 {
+ req.OperationAttributes[AttributeLimit] = limit
+ }
+
+ if myJobs {
+ req.OperationAttributes[AttributeRequestingUserName] = c.username
+ }
+
+ if attributes == nil {
+ req.OperationAttributes[AttributeRequestedAttributes] = DefaultJobAttributes
+ } else {
+ req.OperationAttributes[AttributeRequestedAttributes] = append(attributes, AttributeJobID)
+ }
+
+ resp, err := c.SendRequest(c.adapter.GetHttpUri("", nil), req, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ jobIDMap := make(map[int]Attributes)
+
+ for _, jobAttributes := range resp.JobAttributes {
+ jobIDMap[jobAttributes[AttributeJobID][0].Value.(int)] = jobAttributes
+ }
+
+ return jobIDMap, nil
+}
+
+// CancelJob cancels a job. if purge is true, the job will also be removed
+func (c *IPPClient) CancelJob(jobID int, purge bool) error {
+ req := NewRequest(OperationCancelJob, 1)
+ req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID)
+ req.OperationAttributes[AttributePurgeJobs] = purge
+
+ _, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil)
+ return err
+}
+
+// CancelAllJob cancels all jobs for a specified printer. if purge is true, the jobs will also be removed
+func (c *IPPClient) CancelAllJob(printer string, purge bool) error {
+ req := NewRequest(OperationCancelJobs, 1)
+ req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
+ req.OperationAttributes[AttributePurgeJobs] = purge
+
+ _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
+ return err
+}
+
+// RestartJob restarts a job
+func (c *IPPClient) RestartJob(jobID int) error {
+ req := NewRequest(OperationRestartJob, 1)
+ req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID)
+
+ _, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil)
+ return err
+}
+
+// HoldJobUntil holds a job
+func (c *IPPClient) HoldJobUntil(jobID int, holdUntil string) error {
+ req := NewRequest(OperationRestartJob, 1)
+ req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID)
+ req.JobAttributes[AttributeHoldJobUntil] = holdUntil
+
+ _, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil)
+ return err
+}
+
+// TestConnection tests if a tcp connection to the remote server is possible
+func (c *IPPClient) TestConnection() error {
+ return c.adapter.TestConnection()
+}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/request.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/request.go
new file mode 100644
index 0000000..3207038
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/request.go
@@ -0,0 +1,299 @@
+package ipp
+
+import (
+ "bytes"
+ "encoding/binary"
+ "io"
+)
+
+// Request defines a ipp request
+type Request struct {
+ ProtocolVersionMajor int8
+ ProtocolVersionMinor int8
+
+ Operation int16
+ RequestId int32
+
+ OperationAttributes map[string]any
+ JobAttributes map[string]any
+ PrinterAttributes map[string]any
+ SubscriptionAttributes map[string]any // Added for subscription operations
+
+ File io.Reader
+ FileSize int
+}
+
+// NewRequest creates a new ipp request
+func NewRequest(op int16, reqID int32) *Request {
+ return &Request{
+ ProtocolVersionMajor: ProtocolVersionMajor,
+ ProtocolVersionMinor: ProtocolVersionMinor,
+ Operation: op,
+ RequestId: reqID,
+ OperationAttributes: make(map[string]any),
+ JobAttributes: make(map[string]any),
+ PrinterAttributes: make(map[string]any),
+ SubscriptionAttributes: make(map[string]any),
+ File: nil,
+ FileSize: -1,
+ }
+}
+
+// Encode encodes the request to a byte slice
+func (r *Request) Encode() ([]byte, error) {
+ buf := new(bytes.Buffer)
+ enc := NewAttributeEncoder(buf)
+
+ if err := binary.Write(buf, binary.BigEndian, r.ProtocolVersionMajor); err != nil {
+ return nil, err
+ }
+
+ if err := binary.Write(buf, binary.BigEndian, r.ProtocolVersionMinor); err != nil {
+ return nil, err
+ }
+
+ if err := binary.Write(buf, binary.BigEndian, r.Operation); err != nil {
+ return nil, err
+ }
+
+ if err := binary.Write(buf, binary.BigEndian, r.RequestId); err != nil {
+ return nil, err
+ }
+
+ if err := binary.Write(buf, binary.BigEndian, TagOperation); err != nil {
+ return nil, err
+ }
+
+ if r.OperationAttributes == nil {
+ r.OperationAttributes = make(map[string]any, 2)
+ }
+
+ if _, found := r.OperationAttributes[AttributeCharset]; !found {
+ r.OperationAttributes[AttributeCharset] = Charset
+ }
+
+ if _, found := r.OperationAttributes[AttributeNaturalLanguage]; !found {
+ r.OperationAttributes[AttributeNaturalLanguage] = CharsetLanguage
+ }
+
+ if err := r.encodeOperationAttributes(enc); err != nil {
+ return nil, err
+ }
+
+ if len(r.JobAttributes) > 0 {
+ if err := binary.Write(buf, binary.BigEndian, TagJob); err != nil {
+ return nil, err
+ }
+ for attr, value := range r.JobAttributes {
+ if err := enc.Encode(attr, value); err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ if len(r.PrinterAttributes) > 0 {
+ if err := binary.Write(buf, binary.BigEndian, TagPrinter); err != nil {
+ return nil, err
+ }
+ for attr, value := range r.PrinterAttributes {
+ if err := enc.Encode(attr, value); err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ if len(r.SubscriptionAttributes) > 0 {
+ if err := binary.Write(buf, binary.BigEndian, TagSubscription); err != nil {
+ return nil, err
+ }
+ if err := r.encodeSubscriptionAttributes(enc); err != nil {
+ return nil, err
+ }
+ }
+
+ if err := binary.Write(buf, binary.BigEndian, TagEnd); err != nil {
+ return nil, err
+ }
+
+ return buf.Bytes(), nil
+}
+
+func (r *Request) encodeOperationAttributes(enc *AttributeEncoder) error {
+ ordered := []string{
+ AttributeCharset,
+ AttributeNaturalLanguage,
+ AttributePrinterURI,
+ AttributeJobID,
+ }
+
+ for _, attr := range ordered {
+ if value, ok := r.OperationAttributes[attr]; ok {
+ delete(r.OperationAttributes, attr)
+ if err := enc.Encode(attr, value); err != nil {
+ return err
+ }
+ }
+ }
+
+ for attr, value := range r.OperationAttributes {
+ if err := enc.Encode(attr, value); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (r *Request) encodeSubscriptionAttributes(enc *AttributeEncoder) error {
+ // Encode subscription attributes in proper order
+ // notify-pull-method and notify-lease-duration must come before notify-events
+ ordered := []string{
+ "notify-pull-method",
+ "notify-lease-duration",
+ "notify-events",
+ }
+
+ for _, attr := range ordered {
+ if value, ok := r.SubscriptionAttributes[attr]; ok {
+ delete(r.SubscriptionAttributes, attr)
+ if err := enc.Encode(attr, value); err != nil {
+ return err
+ }
+ }
+ }
+
+ // Encode any remaining subscription attributes
+ for attr, value := range r.SubscriptionAttributes {
+ if err := enc.Encode(attr, value); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// RequestDecoder reads and decodes a request from a stream
+type RequestDecoder struct {
+ reader io.Reader
+}
+
+// NewRequestDecoder returns a new decoder that reads from r
+func NewRequestDecoder(r io.Reader) *RequestDecoder {
+ return &RequestDecoder{
+ reader: r,
+ }
+}
+
+// Decode decodes a ipp request into a request struct. additional data will be written to an io.Writer if data is not nil
+func (d *RequestDecoder) Decode(data io.Writer) (*Request, error) {
+ req := new(Request)
+
+ if err := binary.Read(d.reader, binary.BigEndian, &req.ProtocolVersionMajor); err != nil {
+ return nil, err
+ }
+
+ if err := binary.Read(d.reader, binary.BigEndian, &req.ProtocolVersionMinor); err != nil {
+ return nil, err
+ }
+
+ if err := binary.Read(d.reader, binary.BigEndian, &req.Operation); err != nil {
+ return nil, err
+ }
+
+ if err := binary.Read(d.reader, binary.BigEndian, &req.RequestId); err != nil {
+ return nil, err
+ }
+
+ startByteSlice := make([]byte, 1)
+
+ tag := TagCupsInvalid
+ previousAttributeName := ""
+ tagSet := false
+
+ attribDecoder := NewAttributeDecoder(d.reader)
+
+ // decode attribute buffer
+ for {
+ if _, err := d.reader.Read(startByteSlice); err != nil {
+ // when we read from a stream, we may get an EOF if we want to read the end tag
+ // all data should be read and we can ignore the error
+ if err == io.EOF {
+ break
+ }
+ return nil, err
+ }
+
+ startByte := int8(startByteSlice[0])
+
+ // check if attributes are completed
+ if startByte == TagEnd {
+ break
+ }
+
+ if startByte == TagOperation {
+ if req.OperationAttributes == nil {
+ req.OperationAttributes = make(map[string]any)
+ }
+
+ tag = TagOperation
+ tagSet = true
+
+ }
+
+ if startByte == TagJob {
+ if req.JobAttributes == nil {
+ req.JobAttributes = make(map[string]any)
+ }
+ tag = TagJob
+ tagSet = true
+ }
+
+ if startByte == TagPrinter {
+ if req.PrinterAttributes == nil {
+ req.PrinterAttributes = make(map[string]any)
+ }
+ tag = TagPrinter
+ tagSet = true
+ }
+
+ if tagSet {
+ if _, err := d.reader.Read(startByteSlice); err != nil {
+ return nil, err
+ }
+ startByte = int8(startByteSlice[0])
+ }
+
+ attrib, err := attribDecoder.Decode(startByte)
+ if err != nil {
+ return nil, err
+ }
+
+ if attrib.Name != "" {
+ appendAttributeToRequest(req, tag, attrib.Name, attrib.Value)
+ previousAttributeName = attrib.Name
+ } else {
+ appendAttributeToRequest(req, tag, previousAttributeName, attrib.Value)
+ }
+
+ tagSet = false
+ }
+
+ if data != nil {
+ if _, err := io.Copy(data, d.reader); err != nil {
+ return nil, err
+ }
+ }
+
+ return req, nil
+}
+
+func appendAttributeToRequest(req *Request, tag int8, name string, value any) {
+ switch tag {
+ case TagOperation:
+ req.OperationAttributes[name] = value
+ case TagPrinter:
+ req.PrinterAttributes[name] = value
+ case TagJob:
+ req.JobAttributes[name] = value
+ }
+}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/response.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/response.go
new file mode 100644
index 0000000..e7cbd75
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/response.go
@@ -0,0 +1,383 @@
+package ipp
+
+import (
+ "bytes"
+ "encoding/binary"
+ "io"
+)
+
+// Attributes is a wrapper for a set of attributes
+type Attributes map[string][]Attribute
+
+// Response defines a ipp response
+type Response struct {
+ ProtocolVersionMajor int8
+ ProtocolVersionMinor int8
+
+ StatusCode int16
+ RequestId int32
+
+ OperationAttributes Attributes
+ PrinterAttributes []Attributes
+ JobAttributes []Attributes
+ SubscriptionAttributes []Attributes // Added for subscription responses
+}
+
+// CheckForErrors checks the status code and returns a error if it is not zero. it also returns the status message if provided by the server
+func (r *Response) CheckForErrors() error {
+ if r.StatusCode != StatusOk {
+ err := IPPError{
+ Status: r.StatusCode,
+ Message: "no status message returned",
+ }
+
+ if len(r.OperationAttributes["status-message"]) > 0 {
+ err.Message = r.OperationAttributes["status-message"][0].Value.(string)
+ }
+
+ return err
+ }
+
+ return nil
+}
+
+// NewResponse creates a new ipp response
+func NewResponse(statusCode int16, reqID int32) *Response {
+ return &Response{
+ ProtocolVersionMajor: ProtocolVersionMajor,
+ ProtocolVersionMinor: ProtocolVersionMinor,
+ StatusCode: statusCode,
+ RequestId: reqID,
+ OperationAttributes: make(Attributes),
+ PrinterAttributes: make([]Attributes, 0),
+ JobAttributes: make([]Attributes, 0),
+ }
+}
+
+// Encode encodes the response to a byte slice
+func (r *Response) Encode() ([]byte, error) {
+ buf := new(bytes.Buffer)
+ enc := NewAttributeEncoder(buf)
+
+ if err := binary.Write(buf, binary.BigEndian, r.ProtocolVersionMajor); err != nil {
+ return nil, err
+ }
+
+ if err := binary.Write(buf, binary.BigEndian, r.ProtocolVersionMinor); err != nil {
+ return nil, err
+ }
+
+ if err := binary.Write(buf, binary.BigEndian, r.StatusCode); err != nil {
+ return nil, err
+ }
+
+ if err := binary.Write(buf, binary.BigEndian, r.RequestId); err != nil {
+ return nil, err
+ }
+
+ if err := binary.Write(buf, binary.BigEndian, TagOperation); err != nil {
+ return nil, err
+ }
+
+ if r.OperationAttributes == nil {
+ r.OperationAttributes = make(Attributes, 0)
+ }
+
+ if _, found := r.OperationAttributes[AttributeCharset]; !found {
+ r.OperationAttributes[AttributeCharset] = []Attribute{
+ {
+ Value: Charset,
+ },
+ }
+ }
+
+ if _, found := r.OperationAttributes[AttributeNaturalLanguage]; !found {
+ r.OperationAttributes[AttributeNaturalLanguage] = []Attribute{
+ {
+ Value: CharsetLanguage,
+ },
+ }
+ }
+
+ if err := r.encodeOperationAttributes(enc); err != nil {
+ return nil, err
+ }
+
+ if len(r.PrinterAttributes) > 0 {
+ for _, printerAttr := range r.PrinterAttributes {
+ if err := binary.Write(buf, binary.BigEndian, TagPrinter); err != nil {
+ return nil, err
+ }
+
+ for name, attr := range printerAttr {
+ if len(attr) == 0 {
+ continue
+ }
+
+ values := make([]any, len(attr))
+ for i, v := range attr {
+ values[i] = v.Value
+ }
+
+ if len(values) == 1 {
+ if err := enc.Encode(name, values[0]); err != nil {
+ return nil, err
+ }
+ } else {
+ if err := enc.Encode(name, values); err != nil {
+ return nil, err
+ }
+ }
+ }
+ }
+ }
+
+ if len(r.JobAttributes) > 0 {
+ for _, jobAttr := range r.JobAttributes {
+ if err := binary.Write(buf, binary.BigEndian, TagJob); err != nil {
+ return nil, err
+ }
+
+ for name, attr := range jobAttr {
+ if len(attr) == 0 {
+ continue
+ }
+
+ values := make([]any, len(attr))
+ for i, v := range attr {
+ values[i] = v.Value
+ }
+
+ if len(values) == 1 {
+ if err := enc.Encode(name, values[0]); err != nil {
+ return nil, err
+ }
+ } else {
+ if err := enc.Encode(name, values); err != nil {
+ return nil, err
+ }
+ }
+ }
+ }
+ }
+
+ if err := binary.Write(buf, binary.BigEndian, TagEnd); err != nil {
+ return nil, err
+ }
+
+ return buf.Bytes(), nil
+}
+
+func (r *Response) encodeOperationAttributes(enc *AttributeEncoder) error {
+ ordered := []string{
+ AttributeCharset,
+ AttributeNaturalLanguage,
+ AttributePrinterURI,
+ AttributeJobID,
+ }
+
+ for _, name := range ordered {
+ if attr, ok := r.OperationAttributes[name]; ok {
+ delete(r.OperationAttributes, name)
+ if err := encodeOperationAttribute(enc, name, attr); err != nil {
+ return err
+ }
+ }
+ }
+
+ for name, attr := range r.OperationAttributes {
+ if err := encodeOperationAttribute(enc, name, attr); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func encodeOperationAttribute(enc *AttributeEncoder, name string, attr []Attribute) error {
+ if len(attr) == 0 {
+ return nil
+ }
+
+ values := make([]any, len(attr))
+ for i, v := range attr {
+ values[i] = v.Value
+ }
+
+ if len(values) == 1 {
+ return enc.Encode(name, values[0])
+ }
+
+ return enc.Encode(name, values)
+}
+
+// ResponseDecoder reads and decodes a response from a stream
+type ResponseDecoder struct {
+ reader io.Reader
+}
+
+// NewResponseDecoder returns a new decoder that reads from r
+func NewResponseDecoder(r io.Reader) *ResponseDecoder {
+ return &ResponseDecoder{
+ reader: r,
+ }
+}
+
+// Decode decodes a ipp response into a response struct. additional data will be written to an io.Writer if data is not nil
+func (d *ResponseDecoder) Decode(data io.Writer) (*Response, error) {
+ /*
+ 1 byte: Protocol Major Version - b
+ 1 byte: Protocol Minor Version - b
+ 2 byte: Status ID - h
+ 4 byte: Request ID - i
+ 1 byte: Operation Attribute Byte (\0x01)
+ N times: Attributes
+ 1 byte: Attribute End Byte (\0x03)
+ */
+
+ resp := new(Response)
+
+ // wrap the reader so we have more functionality
+ // reader := bufio.NewReader(d.reader)
+
+ if err := binary.Read(d.reader, binary.BigEndian, &resp.ProtocolVersionMajor); err != nil {
+ return nil, err
+ }
+
+ if err := binary.Read(d.reader, binary.BigEndian, &resp.ProtocolVersionMinor); err != nil {
+ return nil, err
+ }
+
+ if err := binary.Read(d.reader, binary.BigEndian, &resp.StatusCode); err != nil {
+ return nil, err
+ }
+
+ if err := binary.Read(d.reader, binary.BigEndian, &resp.RequestId); err != nil {
+ return nil, err
+ }
+
+ startByteSlice := make([]byte, 1)
+
+ tag := TagCupsInvalid
+ previousAttributeName := ""
+ tempAttributes := make(Attributes)
+ tagSet := false
+
+ attribDecoder := NewAttributeDecoder(d.reader)
+
+ // decode attribute buffer
+ for {
+ if _, err := d.reader.Read(startByteSlice); err != nil {
+ // when we read from a stream, we may get an EOF if we want to read the end tag
+ // all data should be read and we can ignore the error
+ if err == io.EOF {
+ break
+ }
+ return nil, err
+ }
+
+ startByte := int8(startByteSlice[0])
+
+ // check if attributes are completed
+ if startByte == TagEnd {
+ break
+ }
+
+ if startByte == TagOperation {
+ if len(tempAttributes) > 0 && tag != TagCupsInvalid {
+ appendAttributeToResponse(resp, tag, tempAttributes)
+ tempAttributes = make(Attributes)
+ }
+
+ tag = TagOperation
+ tagSet = true
+ }
+
+ if startByte == TagJob {
+ if len(tempAttributes) > 0 && tag != TagCupsInvalid {
+ appendAttributeToResponse(resp, tag, tempAttributes)
+ tempAttributes = make(Attributes)
+ }
+
+ tag = TagJob
+ tagSet = true
+ }
+
+ if startByte == TagPrinter {
+ if len(tempAttributes) > 0 && tag != TagCupsInvalid {
+ appendAttributeToResponse(resp, tag, tempAttributes)
+ tempAttributes = make(Attributes)
+ }
+
+ tag = TagPrinter
+ tagSet = true
+ }
+
+ if startByte == TagSubscription {
+ if len(tempAttributes) > 0 && tag != TagCupsInvalid {
+ appendAttributeToResponse(resp, tag, tempAttributes)
+ tempAttributes = make(Attributes)
+ }
+
+ tag = TagSubscription
+ tagSet = true
+ }
+
+ if startByte == TagEventNotification {
+ if len(tempAttributes) > 0 && tag != TagCupsInvalid {
+ appendAttributeToResponse(resp, tag, tempAttributes)
+ tempAttributes = make(Attributes)
+ }
+
+ tag = TagEventNotification
+ tagSet = true
+ }
+
+ if tagSet {
+ if _, err := d.reader.Read(startByteSlice); err != nil {
+ return nil, err
+ }
+ startByte = int8(startByteSlice[0])
+ }
+
+ attrib, err := attribDecoder.Decode(startByte)
+ if err != nil {
+ return nil, err
+ }
+
+ if attrib.Name != "" {
+ tempAttributes[attrib.Name] = append(tempAttributes[attrib.Name], *attrib)
+ previousAttributeName = attrib.Name
+ } else {
+ tempAttributes[previousAttributeName] = append(tempAttributes[previousAttributeName], *attrib)
+ }
+
+ tagSet = false
+ }
+
+ if len(tempAttributes) > 0 && tag != TagCupsInvalid {
+ appendAttributeToResponse(resp, tag, tempAttributes)
+ }
+
+ if data != nil {
+ if _, err := io.Copy(data, d.reader); err != nil {
+ return nil, err
+ }
+ }
+
+ return resp, nil
+}
+
+func appendAttributeToResponse(resp *Response, tag int8, attr map[string][]Attribute) {
+ switch tag {
+ case TagOperation:
+ resp.OperationAttributes = attr
+ case TagPrinter:
+ resp.PrinterAttributes = append(resp.PrinterAttributes, attr)
+ case TagJob:
+ resp.JobAttributes = append(resp.JobAttributes, attr)
+ case TagSubscription, TagEventNotification:
+ // Both subscription and event notification attributes go to SubscriptionAttributes
+ resp.SubscriptionAttributes = append(resp.SubscriptionAttributes, attr)
+ }
+}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/utils.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/utils.go
new file mode 100644
index 0000000..62689a3
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/utils.go
@@ -0,0 +1,28 @@
+package ipp
+
+import (
+ "fmt"
+ "os"
+ "path"
+)
+
+// ParseControlFile reads and decodes a cups control file into a response
+func ParseControlFile(jobID int, spoolDirectory string) (*Response, error) {
+ if spoolDirectory == "" {
+ spoolDirectory = "/var/spool/cups"
+ }
+
+ controlFilePath := path.Join(spoolDirectory, fmt.Sprintf("c%d", jobID))
+
+ if _, err := os.Stat(controlFilePath); err != nil {
+ return nil, err
+ }
+
+ controlFile, err := os.Open(controlFilePath)
+ if err != nil {
+ return nil, err
+ }
+ defer controlFile.Close()
+
+ return NewResponseDecoder(controlFile).Decode(nil)
+}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/syncmap/LICENSE b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/syncmap/LICENSE
new file mode 100644
index 0000000..edba275
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/syncmap/LICENSE
@@ -0,0 +1,28 @@
+Copyright 2009 The Go Authors.
+Copyright 2024 Zachary Olstein.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google LLC nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/syncmap/syncmap.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/syncmap/syncmap.go
new file mode 100644
index 0000000..27d9458
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/syncmap/syncmap.go
@@ -0,0 +1,537 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package syncmap
+
+import (
+ "sync"
+ "sync/atomic"
+ "unsafe"
+)
+
+// Map is like a Go map[K]V but is safe for concurrent use
+// by multiple goroutines without additional locking or coordination.
+// Loads, stores, and deletes run in amortized constant time.
+//
+// The Map type is specialized. Most code should use a plain Go map instead,
+// with separate locking or coordination, for better type safety and to make it
+// easier to maintain other invariants along with the map content.
+//
+// The Map type is optimized for two common use cases: (1) when the entry for a given
+// key is only ever written once but read many times, as in caches that only grow,
+// or (2) when multiple goroutines read, write, and overwrite entries for disjoint
+// sets of keys. In these two cases, use of a Map may significantly reduce lock
+// contention compared to a Go map paired with a separate [Mutex] or [RWMutex].
+//
+// The zero Map is empty and ready for use. A Map must not be copied after first use.
+//
+// In the terminology of [the Go memory model], Map arranges that a write operation
+// “synchronizes before” any read operation that observes the effect of the write, where
+// read and write operations are defined as follows.
+// [Map.Load], [Map.LoadAndDelete], [Map.LoadOrStore], and [Map.Swap] are read operations;
+// [Map.Delete], [Map.LoadAndDelete], [Map.Store], and [Map.Swap] are write operations;
+// [Map.LoadOrStore] is a write operation when it returns loaded set to false.
+//
+// [the Go memory model]: https://go.dev/ref/mem
+type Map[K comparable, V any] struct {
+ mu sync.Mutex
+
+ // read contains the portion of the map's contents that are safe for
+ // concurrent access (with or without mu held).
+ //
+ // The read field itself is always safe to load, but must only be stored with
+ // mu held.
+ //
+ // Entries stored in read may be updated concurrently without mu, but updating
+ // a previously-expunged entry requires that the entry be copied to the dirty
+ // map and unexpunged with mu held.
+ read atomic.Pointer[readOnly[K, V]]
+
+ // dirty contains the portion of the map's contents that require mu to be
+ // held. To ensure that the dirty map can be promoted to the read map quickly,
+ // it also includes all of the non-expunged entries in the read map.
+ //
+ // Expunged entries are not stored in the dirty map. An expunged entry in the
+ // clean map must be unexpunged and added to the dirty map before a new value
+ // can be stored to it.
+ //
+ // If the dirty map is nil, the next write to the map will initialize it by
+ // making a shallow copy of the clean map, omitting stale entries.
+ dirty map[K]*entry[V]
+
+ // misses counts the number of loads since the read map was last updated that
+ // needed to lock mu to determine whether the key was present.
+ //
+ // Once enough misses have occurred to cover the cost of copying the dirty
+ // map, the dirty map will be promoted to the read map (in the unamended
+ // state) and the next store to the map will make a new dirty copy.
+ misses int
+}
+
+// readOnly is an immutable struct stored atomically in the Map.read field.
+type readOnly[K comparable, V any] struct {
+ m map[K]*entry[V]
+ amended bool // true if the dirty map contains some key not in m.
+}
+
+// expunged is an arbitrary pointer that marks entries which have been deleted
+// from the dirty map.
+// Because the same expunged pointer is used regardless of the Map's value type,
+// value pointers read from the map must be compared against expunged BEFORE
+// casting the pointer to *V.
+var expunged = unsafe.Pointer(new(int))
+
+// An entry is a slot in the map corresponding to a particular key.
+type entry[V any] struct {
+ // p points to the value stored for the entry.
+ //
+ // If p == nil, the entry has been deleted, and either m.dirty == nil or
+ // m.dirty[key] is e.
+ //
+ // If p == expunged, the entry has been deleted, m.dirty != nil, and the entry
+ // is missing from m.dirty.
+ //
+ // Otherwise, the entry is valid and recorded in m.read.m[key] and, if m.dirty
+ // != nil, in m.dirty[key].
+ //
+ // If p != expunged, it is always safe to cast it to (*V).
+ //
+ // An entry can be deleted by atomic replacement with nil: when m.dirty is
+ // next created, it will atomically replace nil with expunged and leave
+ // m.dirty[key] unset.
+ //
+ // An entry's associated value can be updated by atomic replacement, provided
+ // p != expunged. If p == expunged, an entry's associated value can be updated
+ // only after first setting m.dirty[key] = e so that lookups using the dirty
+ // map find the entry.
+ p unsafe.Pointer
+}
+
+func newEntry[V any](i V) *entry[V] {
+ e := &entry[V]{}
+ atomic.StorePointer(&e.p, unsafe.Pointer(&i))
+ return e
+}
+
+func (m *Map[K, V]) loadReadOnly() readOnly[K, V] {
+ if p := m.read.Load(); p != nil {
+ return *p
+ }
+ return readOnly[K, V]{}
+}
+
+// Load returns the value stored in the map for a key, or nil if no
+// value is present.
+// The ok result indicates whether value was found in the map.
+func (m *Map[K, V]) Load(key K) (value V, ok bool) {
+ read := m.loadReadOnly()
+ e, ok := read.m[key]
+ if !ok && read.amended {
+ m.mu.Lock()
+ // Avoid reporting a spurious miss if m.dirty got promoted while we were
+ // blocked on m.mu. (If further loads of the same key will not miss, it's
+ // not worth copying the dirty map for this key.)
+ read = m.loadReadOnly()
+ e, ok = read.m[key]
+ if !ok && read.amended {
+ e, ok = m.dirty[key]
+ // Regardless of whether the entry was present, record a miss: this key
+ // will take the slow path until the dirty map is promoted to the read
+ // map.
+ m.missLocked()
+ }
+ m.mu.Unlock()
+ }
+ if !ok {
+ return value, false
+ }
+ return e.load()
+}
+
+func (e *entry[V]) load() (value V, ok bool) {
+ p := atomic.LoadPointer(&e.p)
+ if p == nil || p == expunged {
+ return value, false
+ }
+ return *(*V)(p), true
+}
+
+// Store sets the value for a key.
+func (m *Map[K, V]) Store(key K, value V) {
+ _, _ = m.Swap(key, value)
+}
+
+// unexpungeLocked ensures that the entry is not marked as expunged.
+//
+// If the entry was previously expunged, it must be added to the dirty map
+// before m.mu is unlocked.
+func (e *entry[V]) unexpungeLocked() (wasExpunged bool) {
+ return atomic.CompareAndSwapPointer(&e.p, expunged, nil)
+}
+
+// swapLocked unconditionally swaps a value into the entry.
+//
+// The entry must be known not to be expunged.
+func (e *entry[V]) swapLocked(i *V) *V {
+ return (*V)(atomic.SwapPointer(&e.p, unsafe.Pointer(i)))
+}
+
+// LoadOrStore returns the existing value for the key if present.
+// Otherwise, it stores and returns the given value.
+// The loaded result is true if the value was loaded, false if stored.
+func (m *Map[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) {
+ // Avoid locking if it's a clean hit.
+ read := m.loadReadOnly()
+ if e, ok := read.m[key]; ok {
+ actual, loaded, ok := e.tryLoadOrStore(value)
+ if ok {
+ return actual, loaded
+ }
+ }
+
+ m.mu.Lock()
+ read = m.loadReadOnly()
+ if e, ok := read.m[key]; ok {
+ if e.unexpungeLocked() {
+ m.dirty[key] = e
+ }
+ actual, loaded, _ = e.tryLoadOrStore(value)
+ } else if e, ok := m.dirty[key]; ok {
+ actual, loaded, _ = e.tryLoadOrStore(value)
+ m.missLocked()
+ } else {
+ if !read.amended {
+ // We're adding the first new key to the dirty map.
+ // Make sure it is allocated and mark the read-only map as incomplete.
+ m.dirtyLocked()
+ m.read.Store(&readOnly[K, V]{m: read.m, amended: true})
+ }
+ m.dirty[key] = newEntry(value)
+ actual, loaded = value, false
+ }
+ m.mu.Unlock()
+
+ return actual, loaded
+}
+
+// tryLoadOrStore atomically loads or stores a value if the entry is not
+// expunged.
+//
+// If the entry is expunged, tryLoadOrStore leaves the entry unchanged and
+// returns with ok==false.
+func (e *entry[V]) tryLoadOrStore(i V) (actual V, loaded, ok bool) {
+ ptr := atomic.LoadPointer(&e.p)
+ if ptr == expunged {
+ return actual, false, false
+ }
+ p := (*V)(ptr)
+ if p != nil {
+ return *p, true, true
+ }
+
+ // Copy the interface after the first load to make this method more amenable
+ // to escape analysis: if we hit the "load" path or the entry is expunged, we
+ // shouldn't bother heap-allocating.
+ ic := i
+ for {
+ if atomic.CompareAndSwapPointer(&e.p, nil, unsafe.Pointer(&ic)) {
+ return i, false, true
+ }
+ ptr = atomic.LoadPointer(&e.p)
+ if ptr == expunged {
+ return actual, false, false
+ }
+ p = (*V)(ptr)
+ if p != nil {
+ return *p, true, true
+ }
+ }
+}
+
+// LoadAndDelete deletes the value for a key, returning the previous value if any.
+// The loaded result reports whether the key was present.
+func (m *Map[K, V]) LoadAndDelete(key K) (value V, loaded bool) {
+ read := m.loadReadOnly()
+ e, ok := read.m[key]
+ if !ok && read.amended {
+ m.mu.Lock()
+ read = m.loadReadOnly()
+ e, ok = read.m[key]
+ if !ok && read.amended {
+ e, ok = m.dirty[key]
+ delete(m.dirty, key)
+ // Regardless of whether the entry was present, record a miss: this key
+ // will take the slow path until the dirty map is promoted to the read
+ // map.
+ m.missLocked()
+ }
+ m.mu.Unlock()
+ }
+ if ok {
+ return e.delete()
+ }
+ return value, false
+}
+
+// Delete deletes the value for a key.
+func (m *Map[K, V]) Delete(key K) {
+ m.LoadAndDelete(key)
+}
+
+func (e *entry[V]) delete() (value V, ok bool) {
+ for {
+ p := atomic.LoadPointer(&e.p)
+ if p == nil || p == expunged {
+ return value, false
+ }
+ if atomic.CompareAndSwapPointer(&e.p, p, nil) {
+ return *(*V)(p), true
+ }
+ }
+}
+
+// trySwap swaps a value if the entry has not been expunged.
+//
+// If the entry is expunged, trySwap returns false and leaves the entry
+// unchanged.
+func (e *entry[V]) trySwap(i *V) (*V, bool) {
+ for {
+ p := atomic.LoadPointer(&e.p)
+ if p == expunged {
+ return nil, false
+ }
+ if atomic.CompareAndSwapPointer(&e.p, p, unsafe.Pointer(i)) {
+ return (*V)(p), true
+ }
+ }
+}
+
+// Swap swaps the value for a key and returns the previous value if any.
+// The loaded result reports whether the key was present.
+func (m *Map[K, V]) Swap(key K, value V) (previous V, loaded bool) {
+ read := m.loadReadOnly()
+ if e, ok := read.m[key]; ok {
+ if v, ok := e.trySwap(&value); ok {
+ if v == nil {
+ return previous, false
+ }
+ return *v, true
+ }
+ }
+
+ m.mu.Lock()
+ read = m.loadReadOnly()
+ if e, ok := read.m[key]; ok {
+ if e.unexpungeLocked() {
+ // The entry was previously expunged, which implies that there is a
+ // non-nil dirty map and this entry is not in it.
+ m.dirty[key] = e
+ }
+ if v := e.swapLocked(&value); v != nil {
+ loaded = true
+ previous = *v
+ }
+ } else if e, ok := m.dirty[key]; ok {
+ if v := e.swapLocked(&value); v != nil {
+ loaded = true
+ previous = *v
+ }
+ } else {
+ if !read.amended {
+ // We're adding the first new key to the dirty map.
+ // Make sure it is allocated and mark the read-only map as incomplete.
+ m.dirtyLocked()
+ m.read.Store(&readOnly[K, V]{m: read.m, amended: true})
+ }
+ m.dirty[key] = newEntry(value)
+ }
+ m.mu.Unlock()
+ return previous, loaded
+}
+
+// Range calls f sequentially for each key and value present in the map.
+// If f returns false, range stops the iteration.
+//
+// Range does not necessarily correspond to any consistent snapshot of the Map's
+// contents: no key will be visited more than once, but if the value for any key
+// is stored or deleted concurrently (including by f), Range may reflect any
+// mapping for that key from any point during the Range call. Range does not
+// block other methods on the receiver; even f itself may call any method on m.
+//
+// Range may be O(N) with the number of elements in the map even if f returns
+// false after a constant number of calls.
+func (m *Map[K, V]) Range(f func(key K, value V) bool) {
+ // We need to be able to iterate over all of the keys that were already
+ // present at the start of the call to Range.
+ // If read.amended is false, then read.m satisfies that property without
+ // requiring us to hold m.mu for a long time.
+ read := m.loadReadOnly()
+ if read.amended {
+ // m.dirty contains keys not in read.m. Fortunately, Range is already O(N)
+ // (assuming the caller does not break out early), so a call to Range
+ // amortizes an entire copy of the map: we can promote the dirty copy
+ // immediately!
+ m.mu.Lock()
+ read = m.loadReadOnly()
+ if read.amended {
+ read = readOnly[K, V]{m: m.dirty}
+ copyRead := read
+ m.read.Store(&copyRead)
+ m.dirty = nil
+ m.misses = 0
+ }
+ m.mu.Unlock()
+ }
+
+ for k, e := range read.m {
+ v, ok := e.load()
+ if !ok {
+ continue
+ }
+ if !f(k, v) {
+ break
+ }
+ }
+}
+
+// CompareAndSwap swaps the old and new values for key
+// if the value stored in the map is equal to old.
+// The old value must be of a comparable type.
+func CompareAndSwap[K comparable, V comparable](m *Map[K, V], key K, old, new V) (swapped bool) {
+ read := m.loadReadOnly()
+ if e, ok := read.m[key]; ok {
+ return tryCompareAndSwap(e, old, new)
+ } else if !read.amended {
+ return false // No existing value for key.
+ }
+
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ read = m.loadReadOnly()
+ swapped = false
+ if e, ok := read.m[key]; ok {
+ swapped = tryCompareAndSwap(e, old, new)
+ } else if e, ok := m.dirty[key]; ok {
+ swapped = tryCompareAndSwap(e, old, new)
+ // We needed to lock mu in order to load the entry for key,
+ // and the operation didn't change the set of keys in the map
+ // (so it would be made more efficient by promoting the dirty
+ // map to read-only).
+ // Count it as a miss so that we will eventually switch to the
+ // more efficient steady state.
+ m.missLocked()
+ }
+ return swapped
+}
+
+// CompareAndDelete deletes the entry for key if its value is equal to old.
+// The old value must be of a comparable type.
+//
+// If there is no current value for key in the map, CompareAndDelete
+// returns false (even if the old value is the zero value of V).
+func CompareAndDelete[K comparable, V comparable](m *Map[K, V], key K, old V) (deleted bool) {
+ read := m.loadReadOnly()
+ e, ok := read.m[key]
+ if !ok && read.amended {
+ m.mu.Lock()
+ read = m.loadReadOnly()
+ e, ok = read.m[key]
+ if !ok && read.amended {
+ e, ok = m.dirty[key]
+ // Don't delete key from m.dirty: we still need to do the “compare” part
+ // of the operation. The entry will eventually be expunged when the
+ // dirty map is promoted to the read map.
+ //
+ // Regardless of whether the entry was present, record a miss: this key
+ // will take the slow path until the dirty map is promoted to the read
+ // map.
+ m.missLocked()
+ }
+ m.mu.Unlock()
+ }
+ for ok {
+ ptr := atomic.LoadPointer(&e.p)
+ if ptr == nil || ptr == expunged {
+ return false
+ }
+ p := (*V)(ptr)
+ if *p != old {
+ return false
+ }
+ if atomic.CompareAndSwapPointer(&e.p, ptr, nil) {
+ return true
+ }
+ }
+ return false
+}
+
+// tryCompareAndSwap compare the entry with the given old value and swaps
+// it with a new value if the entry is equal to the old value, and the entry
+// has not been expunged.
+//
+// If the entry is expunged, tryCompareAndSwap returns false and leaves
+// the entry unchanged.
+func tryCompareAndSwap[V comparable](e *entry[V], old, new V) bool {
+ ptr := atomic.LoadPointer(&e.p)
+ if ptr == nil || ptr == expunged {
+ return false
+ }
+ p := (*V)(ptr)
+ if *p != old {
+ return false
+ }
+
+ // Copy the interface after the first load to make this method more amenable
+ // to escape analysis: if the comparison fails from the start, we shouldn't
+ // bother heap-allocating an interface value to store.
+ nc := new
+ for {
+ if atomic.CompareAndSwapPointer(&e.p, ptr, unsafe.Pointer(&nc)) {
+ return true
+ }
+ ptr = atomic.LoadPointer(&e.p)
+ if ptr == nil || ptr == expunged {
+ return false
+ }
+ p = (*V)(ptr)
+ if *p != old {
+ return false
+ }
+ }
+}
+
+func (m *Map[K, V]) missLocked() {
+ m.misses++
+ if m.misses < len(m.dirty) {
+ return
+ }
+ m.read.Store(&readOnly[K, V]{m: m.dirty})
+ m.dirty = nil
+ m.misses = 0
+}
+
+func (m *Map[K, V]) dirtyLocked() {
+ if m.dirty != nil {
+ return
+ }
+
+ read := m.loadReadOnly()
+ m.dirty = make(map[K]*entry[V], len(read.m))
+ for k, e := range read.m {
+ if !e.tryExpungeLocked() {
+ m.dirty[k] = e
+ }
+ }
+}
+
+func (e *entry[V]) tryExpungeLocked() (isExpunged bool) {
+ p := atomic.LoadPointer(&e.p)
+ for p == nil {
+ if atomic.CompareAndSwapPointer(&e.p, nil, expunged) {
+ return true
+ }
+ p = atomic.LoadPointer(&e.p)
+ }
+ return p == expunged
+}