From 70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69 Mon Sep 17 00:00:00 2001 From: AlexanderCurl Date: Sat, 18 Apr 2026 17:46:06 +0100 Subject: Replaced file structures for themes in the correct format --- .../DankMaterialShell/core/pkg/dbusutil/variant.go | 95 + .../core/pkg/dbusutil/variant_test.go | 155 + .../DankMaterialShell/core/pkg/go-wayland/AUTHORS | 3 + .../DankMaterialShell/core/pkg/go-wayland/LICENSE | 24 + .../core/pkg/go-wayland/README.md | 25 + .../DankMaterialShell/core/pkg/go-wayland/generate | 4 + .../core/pkg/go-wayland/generatep | 9 + .../core/pkg/go-wayland/wayland/client/client.go | 7542 ++++++++++++++++++++ .../core/pkg/go-wayland/wayland/client/common.go | 55 + .../core/pkg/go-wayland/wayland/client/context.go | 143 + .../pkg/go-wayland/wayland/client/context_test.go | 111 + .../core/pkg/go-wayland/wayland/client/display.go | 37 + .../core/pkg/go-wayland/wayland/client/doc.go | 6 + .../core/pkg/go-wayland/wayland/client/event.go | 120 + .../core/pkg/go-wayland/wayland/client/request.go | 44 + .../core/pkg/go-wayland/wayland/client/util.go | 24 + .../wayland/stable/xdg-shell/xdg_shell.go | 13 + .../DankMaterialShell/core/pkg/ipp/CREDITS.MD | 5 + .../DankMaterialShell/core/pkg/ipp/LICENSE | 201 + .../DankMaterialShell/core/pkg/ipp/adapter-http.go | 132 + .../DankMaterialShell/core/pkg/ipp/adapter.go | 9 + .../DankMaterialShell/core/pkg/ipp/attribute.go | 528 ++ .../DankMaterialShell/core/pkg/ipp/constants.go | 450 ++ .../DankMaterialShell/core/pkg/ipp/cups-client.go | 313 + .../DankMaterialShell/core/pkg/ipp/error.go | 31 + .../DankMaterialShell/core/pkg/ipp/ipp-client.go | 329 + .../DankMaterialShell/core/pkg/ipp/request.go | 299 + .../DankMaterialShell/core/pkg/ipp/response.go | 383 + .../DankMaterialShell/core/pkg/ipp/utils.go | 28 + .../DankMaterialShell/core/pkg/syncmap/LICENSE | 28 + .../DankMaterialShell/core/pkg/syncmap/syncmap.go | 537 ++ 31 files changed, 11683 insertions(+) create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/dbusutil/variant.go create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/dbusutil/variant_test.go create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/AUTHORS create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/LICENSE create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/README.md create mode 100755 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/generate create mode 100755 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/generatep create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/client.go create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/common.go create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/context.go create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/context_test.go create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/display.go create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/doc.go create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/event.go create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/request.go create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/util.go create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/stable/xdg-shell/xdg_shell.go create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/CREDITS.MD create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/LICENSE create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/adapter-http.go create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/adapter.go create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/attribute.go create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/constants.go create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/cups-client.go create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/error.go create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/ipp-client.go create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/request.go create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/response.go create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/utils.go create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/syncmap/LICENSE create mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/syncmap/syncmap.go (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg') 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(©Read) + 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 +} -- cgit v1.3