├── README.md ├── go.mod ├── shell.nix ├── generate.sh ├── wlproto └── wlproto.go ├── go.sum ├── LICENSE ├── cmd ├── dump-registry │ └── main.go ├── wayland-scanner │ ├── parse.go │ └── main.go └── simple-shm │ └── main.go ├── wlshared └── wlshared.go ├── wlserver ├── interface.go ├── protocol.go └── protocols │ └── xdg-shell │ └── xdg-shell.go └── wlclient ├── protocol.go └── protocols └── xdg-shell └── xdg-shell.go /README.md: -------------------------------------------------------------------------------- 1 | This repository contains very incomplete pure Go bindings for Wayland. 2 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module honnef.co/go/wayland 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a 7 | golang.org/x/sys v0.0.0-20220317061510-51cd9980dadf 8 | golang.org/x/tools v0.1.10 9 | ) 10 | 11 | require ( 12 | golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect 13 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect 14 | ) 15 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import {} }: 2 | pkgs.mkShell { 3 | buildInputs = with pkgs; [ 4 | libGL 5 | libdrm 6 | libinput 7 | pkg-config 8 | wayland 9 | wayland.debug # debug symbols 10 | wayland-protocols 11 | wayland-utils 12 | hello-wayland # simple test client 13 | 14 | # example clients to debug with 15 | (weston.overrideAttrs (old: { 16 | mesonFlags = old.mesonFlags ++ ["-Ddemo-clients=true" "-Dsimple-clients=all"]; 17 | })) 18 | ]; 19 | } 20 | -------------------------------------------------------------------------------- /generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | go run ./cmd/wayland-scanner -prefix=wl_ $(pkg-config --variable=pkgdatadir wayland-server)/wayland.xml > wlclient/protocols/wayland/wayland.go 3 | go run ./cmd/wayland-scanner -mode=server -prefix=wl_ $(pkg-config --variable=pkgdatadir wayland-server)/wayland.xml > wlserver/protocols/wayland/wayland.go 4 | 5 | go run ./cmd/wayland-scanner -prefix=xdg_ -i ./wlclient/protocols/wayland $(pkg-config --variable=pkgdatadir wayland-protocols)/stable/xdg-shell/xdg-shell.xml > ./wlclient/protocols/xdg-shell/xdg-shell.go 6 | go run ./cmd/wayland-scanner -mode=server -prefix=xdg_ -i ./wlclient/protocols/wayland $(pkg-config --variable=pkgdatadir wayland-protocols)/stable/xdg-shell/xdg-shell.xml > ./wlserver/protocols/xdg-shell/xdg-shell.go 7 | -------------------------------------------------------------------------------- /wlproto/wlproto.go: -------------------------------------------------------------------------------- 1 | package wlproto 2 | 3 | import "reflect" 4 | 5 | type Interface struct { 6 | Name string 7 | Version uint32 8 | Type reflect.Type 9 | Requests []Request 10 | Events []Event 11 | } 12 | 13 | type Arg struct { 14 | Type ArgType 15 | Aux reflect.Type 16 | } 17 | 18 | type ArgType byte 19 | 20 | const ( 21 | ArgTypeInt ArgType = iota + 1 22 | ArgTypeUint 23 | ArgTypeFixed 24 | ArgTypeString 25 | ArgTypeObject 26 | ArgTypeNewID 27 | ArgTypeArray 28 | ArgTypeFd 29 | ) 30 | 31 | type Request struct { 32 | Name string 33 | Method reflect.Value 34 | Type string 35 | Since uint32 36 | Args []Arg 37 | } 38 | 39 | type Event struct { 40 | Name string 41 | Since uint32 42 | Args []Arg 43 | } 44 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a h1:KikTa6HtAK8cS1qjvUvvq4QO21QnwC+EfvB+OAuZ/ZU= 2 | github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg= 3 | golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o= 4 | golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= 5 | golang.org/x/sys v0.0.0-20220317061510-51cd9980dadf h1:Fm4IcnUL803i92qDlmB0obyHmosDrxZWxJL3gIeNqOw= 6 | golang.org/x/sys v0.0.0-20220317061510-51cd9980dadf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 7 | golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20= 8 | golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= 9 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 10 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 Dominik Honnef 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /cmd/dump-registry/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net" 6 | 7 | "honnef.co/go/wayland/wlclient" 8 | "honnef.co/go/wayland/wlclient/protocols/wayland" 9 | ) 10 | 11 | func roundtrip(dsp *wayland.Display) { 12 | queue := wlclient.NewEventQueue() 13 | cb := dsp.WithQueue(queue).Sync() 14 | var done bool 15 | cb.AddListener(wayland.CallbackEvents{ 16 | Done: func(obj *wayland.Callback, _ uint32) { 17 | log.Println("callback fired") 18 | done = true 19 | cb.Destroy() 20 | }, 21 | }) 22 | for !done { 23 | queue.Dispatch() 24 | } 25 | } 26 | 27 | func main() { 28 | uc, err := net.Dial("unix", "/run/user/1000/wayland-0") 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | c := wlclient.NewConn(uc.(*net.UnixConn)) 33 | 34 | dsp := wayland.GetDisplay(c) 35 | 36 | registry := dsp.GetRegistry() 37 | registry.AddListener(wayland.RegistryEvents{ 38 | Global: func(obj *wayland.Registry, name uint32, interface_ string, version uint32) { 39 | log.Println(obj, name, interface_, version) 40 | }, 41 | }) 42 | 43 | // wait until we've received the initial batch of registry events 44 | roundtrip(dsp) 45 | // call our callbacks 46 | dsp.Queue().Dispatch() 47 | } 48 | -------------------------------------------------------------------------------- /cmd/wayland-scanner/parse.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "go/ast" 5 | "go/token" 6 | "log" 7 | 8 | "golang.org/x/tools/go/packages" 9 | ) 10 | 11 | type Package struct { 12 | Path string 13 | Name string 14 | Interfaces map[string]string 15 | } 16 | 17 | func loadInterfaces(paths []string) map[string]*Package { 18 | cfg := &packages.Config{ 19 | Mode: packages.NeedName | packages.NeedSyntax, 20 | Tests: false, 21 | } 22 | pkgs, err := packages.Load(cfg, paths...) 23 | if err != nil { 24 | log.Fatal(err) 25 | } 26 | 27 | // map from package name (not path) to package 28 | m := map[string]*Package{} 29 | 30 | invalidPackage: 31 | for _, pkg := range pkgs { 32 | if len(pkg.Errors) != 0 { 33 | log.Fatal(pkg.Errors) 34 | } 35 | 36 | if _, ok := m[pkg.Name]; ok { 37 | log.Fatalf("duplicate package name %q", pkg.Name) 38 | } 39 | 40 | ifaces :=map[string]string{} 41 | for _, f := range pkg.Syntax { 42 | for _, decl := range f.Decls { 43 | if decl, ok := decl.(*ast.GenDecl); ok && decl.Tok == token.VAR { 44 | wrongSpec: 45 | for _, spec := range decl.Specs { 46 | spec := spec.(*ast.ValueSpec) 47 | if len(spec.Names) != 1 { 48 | continue wrongSpec 49 | } 50 | if spec.Names[0].Name != "interfaceNames" { 51 | continue wrongSpec 52 | } 53 | if len(spec.Values) != 1 { 54 | continue invalidPackage 55 | } 56 | lit, ok := spec.Values[0].(*ast.CompositeLit) 57 | if !ok { 58 | continue invalidPackage 59 | } 60 | for _, elt := range lit.Elts { 61 | kv, ok := elt.(*ast.KeyValueExpr) 62 | if !ok { 63 | continue invalidPackage 64 | } 65 | key, ok := kv.Key.(*ast.BasicLit) 66 | if !ok || key.Kind != token.STRING { 67 | continue invalidPackage 68 | } 69 | value, ok := kv.Value.(*ast.BasicLit) 70 | if !ok || value.Kind != token.STRING { 71 | continue invalidPackage 72 | } 73 | ifaces[key.Value[1:len(key.Value)-1]] = value.Value[1:len(value.Value)-1] 74 | } 75 | } 76 | } 77 | } 78 | } 79 | 80 | m[pkg.Name] = &Package{ 81 | Path: pkg.PkgPath, 82 | Name: pkg.Name, 83 | Interfaces: ifaces, 84 | } 85 | } 86 | 87 | out := map[string]*Package{} 88 | for _, pkg := range m { 89 | for iface := range pkg.Interfaces { 90 | if _, ok := out[iface]; ok { 91 | log.Fatalf("multiple packages define interface %q", iface) 92 | } 93 | out[iface] = pkg 94 | } 95 | } 96 | 97 | return out 98 | } 99 | -------------------------------------------------------------------------------- /wlshared/wlshared.go: -------------------------------------------------------------------------------- 1 | package wlshared 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "reflect" 7 | "syscall" 8 | "unsafe" 9 | 10 | "honnef.co/go/wayland/wlproto" 11 | ) 12 | 13 | var byteOrder binary.ByteOrder 14 | 15 | func init() { 16 | var x uint32 = 0x01020304 17 | if *(*byte)(unsafe.Pointer(&x)) == 0x01 { 18 | byteOrder = binary.BigEndian 19 | } else { 20 | byteOrder = binary.LittleEndian 21 | } 22 | } 23 | 24 | type Fixed uint32 25 | 26 | func (f Fixed) Float64() float64 { 27 | panic("XXX") 28 | } 29 | 30 | func FromFloat64(f float64) Fixed { 31 | panic("XXX") 32 | } 33 | 34 | type ObjectID uint32 35 | type NewID uint32 36 | 37 | type Object interface { 38 | ID() ObjectID 39 | } 40 | 41 | func EncodeRequest(buf []byte, source ObjectID, request int, args []interface{}) (data []byte, oob []byte) { 42 | buf = append(buf, 0, 0, 0, 0, 0, 0, 0, 0) 43 | var scratch [4]byte 44 | 45 | var fds []int 46 | for _, arg := range args { 47 | if v, ok := arg.(Object); ok { 48 | id := v.ID() 49 | byteOrder.PutUint32(scratch[:], uint32(id)) 50 | buf = append(buf, scratch[:]...) 51 | } else { 52 | v := reflect.ValueOf(arg) 53 | switch v.Type().Kind() { 54 | case reflect.Int32: 55 | byteOrder.PutUint32(scratch[:], uint32(v.Int())) 56 | buf = append(buf, scratch[:]...) 57 | case reflect.Uint32: 58 | byteOrder.PutUint32(scratch[:], uint32(v.Uint())) 59 | buf = append(buf, scratch[:]...) 60 | case reflect.String: 61 | str := v.String() 62 | byteOrder.PutUint32(scratch[:], uint32(len(str)+1)) 63 | buf = append(buf, scratch[:]...) 64 | buf = append(buf, str...) 65 | buf = append(buf, 0) 66 | m := len(str) + 1 67 | n := (m + 3) & ^3 68 | for i := n - m; i > 0; i-- { 69 | buf = append(buf, 0) 70 | } 71 | // XXX array 72 | case reflect.Uintptr: 73 | fds = append(fds, int(v.Uint())) 74 | default: 75 | panic(fmt.Sprintf("internal error: unhandled type %T", arg)) 76 | } 77 | } 78 | } 79 | byteOrder.PutUint32(buf[0:4], uint32(source)) 80 | byteOrder.PutUint16(buf[4:6], uint16(request)) 81 | byteOrder.PutUint16(buf[6:8], uint16(len(buf))) 82 | 83 | if len(fds) > 0 { 84 | // OPT(dh): we send file descriptors so rarely that allocating 85 | // here isn't an issue. 86 | oob = syscall.UnixRights(fds...) 87 | } 88 | return buf, oob 89 | } 90 | 91 | func ParseArgument(arg wlproto.Arg, d []byte, off int) (newOff int, v interface{}) { 92 | var num uint32 93 | if arg.Type != wlproto.ArgTypeFd { 94 | num = byteOrder.Uint32(d[off:]) 95 | off += 4 96 | } 97 | var out interface{} 98 | switch arg.Type { 99 | case wlproto.ArgTypeInt: 100 | out = int32(num) 101 | case wlproto.ArgTypeUint: 102 | if arg.Aux != nil { 103 | out = reflect.ValueOf(uint32(num)).Convert(arg.Aux).Interface() 104 | } else { 105 | out = uint32(num) 106 | } 107 | case wlproto.ArgTypeFixed: 108 | out = Fixed(num) 109 | case wlproto.ArgTypeString: 110 | out = string(d[off : off+int(num)-1]) 111 | off += int(num) 112 | off = (off + 3) & ^3 113 | case wlproto.ArgTypeObject: 114 | out = ObjectID(num) 115 | case wlproto.ArgTypeArray: 116 | // XXX copy out the data, probably 117 | out = d[off : off+int(num)] 118 | off += int(num) 119 | off = (off + 3) &^ 3 120 | case wlproto.ArgTypeFd: 121 | out = nil 122 | case wlproto.ArgTypeNewID: 123 | out = ObjectID(num) 124 | default: 125 | panic("unreachable") 126 | } 127 | 128 | return off, out 129 | } 130 | -------------------------------------------------------------------------------- /wlserver/interface.go: -------------------------------------------------------------------------------- 1 | package wlserver 2 | 3 | // This file contains an unexported copy of ./protocols/wayland, to break the circular dependency between wlserver and 4 | // ./protocols/wayland. 5 | 6 | import ( 7 | "reflect" 8 | 9 | "honnef.co/go/wayland/wlproto" 10 | "honnef.co/go/wayland/wlshared" 11 | ) 12 | 13 | type displayError uint32 14 | 15 | const ( 16 | displayErrorInvalidObject displayError = 0 17 | displayErrorInvalidMethod displayError = 1 18 | displayErrorNoMemory displayError = 2 19 | displayErrorImplementation displayError = 3 20 | ) 21 | 22 | var displayInterface = &wlproto.Interface{ 23 | Name: "wl_display", 24 | Version: 1, 25 | Type: reflect.TypeOf(displayResource{}), 26 | Requests: []wlproto.Request{ 27 | { 28 | Name: "sync", 29 | Type: "", 30 | Since: 1, 31 | Method: reflect.ValueOf(displayImplementation.Sync), 32 | Args: []wlproto.Arg{ 33 | {Type: wlproto.ArgTypeNewID, Aux: reflect.TypeOf(callbackResource{})}, 34 | }, 35 | }, 36 | { 37 | Name: "get_registry", 38 | Type: "", 39 | Since: 1, 40 | Method: reflect.ValueOf(displayImplementation.GetRegistry), 41 | Args: []wlproto.Arg{ 42 | {Type: wlproto.ArgTypeNewID, Aux: reflect.TypeOf(registryResource{})}, 43 | }, 44 | }, 45 | }, 46 | Events: []wlproto.Event{ 47 | { 48 | Name: "error", 49 | Since: 1, 50 | Args: []wlproto.Arg{ 51 | {Type: wlproto.ArgTypeObject}, 52 | {Type: wlproto.ArgTypeUint}, 53 | {Type: wlproto.ArgTypeString}, 54 | }, 55 | }, 56 | { 57 | Name: "delete_id", 58 | Since: 1, 59 | Args: []wlproto.Arg{ 60 | {Type: wlproto.ArgTypeUint}, 61 | }, 62 | }, 63 | }, 64 | } 65 | 66 | type displayResource struct{ Resource } 67 | 68 | func (displayResource) Interface() *wlproto.Interface { return displayInterface } 69 | 70 | type displayImplementation interface { 71 | Sync(obj displayResource, callback callbackResource) callbackImplementation 72 | GetRegistry(obj displayResource, registry registryResource) registryImplementation 73 | } 74 | 75 | func (obj displayResource) Error(objectId Object, code uint32, message string) { 76 | obj.Conn().SendEvent(obj, 0, objectId, code, message) 77 | } 78 | 79 | func (obj displayResource) DeleteID(id uint32) { 80 | obj.Conn().SendEvent(obj, 1, id) 81 | } 82 | 83 | var registryInterface = &wlproto.Interface{ 84 | Name: "wl_registry", 85 | Version: 1, 86 | Type: reflect.TypeOf(registryResource{}), 87 | Requests: []wlproto.Request{ 88 | { 89 | Name: "bind", 90 | Type: "", 91 | Since: 1, 92 | Method: reflect.ValueOf(registryImplementation.Bind), 93 | Args: []wlproto.Arg{ 94 | {Type: wlproto.ArgTypeUint}, 95 | {Type: wlproto.ArgTypeString}, {Type: wlproto.ArgTypeUint}, {Type: wlproto.ArgTypeNewID}, 96 | }, 97 | }, 98 | }, 99 | Events: []wlproto.Event{ 100 | { 101 | Name: "global", 102 | Since: 1, 103 | Args: []wlproto.Arg{ 104 | {Type: wlproto.ArgTypeUint}, 105 | {Type: wlproto.ArgTypeString}, 106 | {Type: wlproto.ArgTypeUint}, 107 | }, 108 | }, 109 | { 110 | Name: "global_remove", 111 | Since: 1, 112 | Args: []wlproto.Arg{ 113 | {Type: wlproto.ArgTypeUint}, 114 | }, 115 | }, 116 | }, 117 | } 118 | 119 | type registryResource struct{ Resource } 120 | 121 | func (registryResource) Interface() *wlproto.Interface { return registryInterface } 122 | 123 | type registryImplementation interface { 124 | Bind(obj registryResource, name uint32, idName string, idVersion uint32, id wlshared.ObjectID) ResourceImplementation 125 | } 126 | 127 | func (obj registryResource) Global(name uint32, interface_ string, version uint32) { 128 | obj.Conn().SendEvent(obj, 0, name, interface_, version) 129 | } 130 | 131 | func (obj registryResource) GlobalRemove(name uint32) { 132 | obj.Conn().SendEvent(obj, 1, name) 133 | } 134 | 135 | var callbackInterface = &wlproto.Interface{ 136 | Name: "wl_callback", 137 | Version: 1, 138 | Type: reflect.TypeOf(callbackResource{}), 139 | Requests: []wlproto.Request{}, 140 | Events: []wlproto.Event{ 141 | { 142 | Name: "done", 143 | Since: 1, 144 | Args: []wlproto.Arg{ 145 | {Type: wlproto.ArgTypeUint}, 146 | }, 147 | }, 148 | }, 149 | } 150 | 151 | type callbackResource struct{ Resource } 152 | 153 | func (callbackResource) Interface() *wlproto.Interface { return callbackInterface } 154 | 155 | type callbackImplementation interface{} 156 | 157 | func (obj callbackResource) Done(callbackData uint32) { 158 | obj.Conn().SendEvent(obj, 0, callbackData) 159 | } 160 | -------------------------------------------------------------------------------- /cmd/simple-shm/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "math/rand" 6 | "net" 7 | "syscall" 8 | 9 | "golang.org/x/sys/unix" 10 | "honnef.co/go/wayland/wlclient" 11 | "honnef.co/go/wayland/wlclient/protocols/wayland" 12 | xdgShell "honnef.co/go/wayland/wlclient/protocols/xdg-shell" 13 | ) 14 | 15 | type Display struct { 16 | display *wayland.Display 17 | registry *wayland.Registry 18 | compositor *wayland.Compositor 19 | shm *wayland.Shm 20 | wmBase *xdgShell.WmBase 21 | hasXRGB bool 22 | } 23 | 24 | type Window struct { 25 | display *Display 26 | width int32 27 | height int32 28 | surface *wayland.Surface 29 | xdgSurface *xdgShell.Surface 30 | xdgToplevel *xdgShell.Toplevel 31 | waitForConfigure bool 32 | buffers [2]Buffer 33 | callback *wayland.Callback 34 | } 35 | 36 | type Buffer struct { 37 | buffer *wayland.Buffer 38 | shmData []byte 39 | busy bool 40 | } 41 | 42 | func roundtrip(dsp *wayland.Display) { 43 | queue := wlclient.NewEventQueue() 44 | cb := dsp.WithQueue(queue).Sync() 45 | var done bool 46 | cb.AddListener(wayland.CallbackEvents{ 47 | Done: func(obj *wayland.Callback, _ uint32) { 48 | log.Println("callback fired") 49 | done = true 50 | cb.Destroy() 51 | }, 52 | }) 53 | for !done { 54 | queue.Dispatch() 55 | } 56 | } 57 | 58 | func createDisplay(c *wlclient.Conn) *Display { 59 | dsp := &Display{ 60 | display: wayland.GetDisplay(c), 61 | } 62 | dsp.registry = dsp.display.GetRegistry() 63 | dsp.registry.AddListener(wayland.RegistryEvents{ 64 | Global: func(_ *wayland.Registry, name uint32, iface string, version uint32) { 65 | switch iface { 66 | case "wl_compositor": 67 | dsp.compositor = &wayland.Compositor{} 68 | dsp.registry.Bind(name, dsp.compositor, 1) 69 | case "xdg_wm_base": 70 | dsp.wmBase = &xdgShell.WmBase{} 71 | dsp.registry.Bind(name, dsp.wmBase, 1) 72 | case "wl_shm": 73 | dsp.shm = &wayland.Shm{} 74 | dsp.registry.Bind(name, dsp.shm, 1) 75 | dsp.shm.AddListener(wayland.ShmEvents{ 76 | Format: func(obj *wayland.Shm, format wayland.ShmFormat) { 77 | if format == wayland.ShmFormatXrgb8888 { 78 | dsp.hasXRGB = true 79 | } 80 | }, 81 | }) 82 | } 83 | }, 84 | }) 85 | 86 | // make sure the server has processed all requests and sent out 87 | // all events, so that we have the full initial state of the 88 | // registry 89 | roundtrip(dsp.display) 90 | dsp.display.Queue().Dispatch() 91 | 92 | if dsp.shm == nil { 93 | log.Fatal("no SHM") 94 | } 95 | if dsp.wmBase == nil { 96 | log.Fatal("no XDG") 97 | } 98 | 99 | // this time make sure that we've processed all initial Shm events 100 | roundtrip(dsp.display) 101 | dsp.display.Queue().Dispatch() 102 | 103 | if !dsp.hasXRGB { 104 | log.Fatal("no XRGB8888") 105 | } 106 | 107 | return dsp 108 | } 109 | 110 | func createWindow(dsp *Display, width, height int32) *Window { 111 | win := &Window{ 112 | display: dsp, 113 | width: width, 114 | height: height, 115 | surface: dsp.compositor.CreateSurface(), 116 | } 117 | 118 | win.xdgSurface = dsp.wmBase.GetXdgSurface(win.surface) 119 | win.xdgSurface.AddListener(xdgShell.SurfaceEvents{ 120 | Configure: func(_ *xdgShell.Surface, serial uint32) { 121 | win.xdgSurface.AckConfigure(serial) 122 | if win.waitForConfigure { 123 | redraw(win, nil, 0) 124 | win.waitForConfigure = false 125 | } 126 | }, 127 | }) 128 | 129 | win.xdgToplevel = win.xdgSurface.GetToplevel() 130 | win.xdgToplevel.SetTitle("simple-shm") 131 | win.surface.Commit() 132 | win.waitForConfigure = true 133 | return win 134 | } 135 | 136 | func redraw(win *Window, callback *wayland.Callback, time uint32) { 137 | buf := windowNextBuffer(win) 138 | 139 | for i := range buf.shmData { 140 | buf.shmData[i] = byte(rand.Int()) 141 | } 142 | 143 | win.surface.Attach(buf.buffer, 0, 0) 144 | win.surface.Damage(0, 0, win.width, win.height) 145 | if callback != nil { 146 | callback.Destroy() 147 | } 148 | win.callback = win.surface.Frame() 149 | win.callback.AddListener(wayland.CallbackEvents{ 150 | Done: func(_ *wayland.Callback, data uint32) { 151 | redraw(win, win.callback, data) 152 | }, 153 | }) 154 | buf.busy = true 155 | win.surface.Commit() 156 | } 157 | 158 | func paintPixels() { 159 | 160 | } 161 | 162 | func windowNextBuffer(win *Window) *Buffer { 163 | var buf *Buffer 164 | if !win.buffers[0].busy { 165 | buf = &win.buffers[0] 166 | } else { 167 | buf = &win.buffers[1] 168 | } 169 | 170 | if buf.buffer == nil { 171 | createShmBuffer(win.display, buf, win.width, win.height, wayland.ShmFormatXrgb8888) 172 | } 173 | 174 | return buf 175 | } 176 | 177 | func createShmBuffer(dsp *Display, buf *Buffer, width, height int32, format wayland.ShmFormat) { 178 | stride := width * 4 179 | size := stride * height 180 | fd, err := unix.MemfdCreate("", 0) 181 | if err != nil { 182 | log.Fatal(err) 183 | } 184 | unix.Ftruncate(fd, int64(size)) 185 | data, err := syscall.Mmap(fd, 0, int(size), syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED) 186 | if err != nil { 187 | log.Fatal(err) 188 | } 189 | 190 | pool := dsp.shm.CreatePool(uintptr(fd), size) 191 | buf.buffer = pool.CreateBuffer(0, width, height, stride, format) 192 | buf.buffer.AddListener(wayland.BufferEvents{ 193 | Release: func(_ *wayland.Buffer) { 194 | buf.busy = false 195 | }, 196 | }) 197 | pool.Destroy() 198 | unix.Close(fd) 199 | 200 | buf.shmData = data 201 | } 202 | 203 | func main() { 204 | uc, err := net.Dial("unix", "/run/user/1000/wayland-0") 205 | if err != nil { 206 | log.Fatal(err) 207 | } 208 | c := wlclient.NewConn(uc.(*net.UnixConn)) 209 | 210 | dsp := createDisplay(c) 211 | createWindow(dsp, 250, 250) 212 | for { 213 | dsp.display.Queue().Dispatch() 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /wlclient/protocol.go: -------------------------------------------------------------------------------- 1 | // Package wlclient provides a low-level client and runtime for the 2 | // Wayland protocol. 3 | // 4 | // This package provides the interfaces and low-level functions for 5 | // establishing Wayland client connections, as well as runtime support 6 | // for code-generated protocol implementations. 7 | // 8 | // It is loosely based on libwayland-client, but designed around Go's 9 | // type system. 10 | // 11 | // 12 | // The Proxy type 13 | // 14 | // Similar to libwayland-client, we rely heavily on the concept of a 15 | // proxy. In the Wayland protocol, the client can associate numeric 16 | // IDs with server-side resources. The Proxy type encapsulates this 17 | // ID, and provides all the internal state necessary for using 18 | // requests and receiving events. 19 | // 20 | // Code generated by our wayland-scanner will generate wrapper types 21 | // for Wayland interfaces (such as wl_buffer) that provide a strongly 22 | // typed abstraction on top of proxies. Code that operates on 23 | // arbitrary objects will accept the Object interface, as opposed to 24 | // instances of *Proxy. 25 | // 26 | // 27 | // Event loop 28 | // 29 | // TODO explain how events work 30 | // 31 | // 32 | // Code generation 33 | // 34 | // TODO explain code generation, mega-packages, and converting types 35 | // between packages. 36 | package wlclient 37 | 38 | // Object deletion and races 39 | // 40 | // Wayland is an asynchronous protocol. The client may decide to 41 | // delete an object while the server is still sending events for it. 42 | // When we receive an event for a object we don't know about, we drop 43 | // the event. 44 | // 45 | // Some objects have methods that receive file descriptors and we need 46 | // to consume these fds even for events we're dropping. Thus, when 47 | // deleting such an object, we create a "zombie" that contains just 48 | // enough information to consume fds. It is assumed that any such 49 | // objects have a server-side destructor, so that we will eventually 50 | // receive a delete_id event and can remove the zombie. 51 | // 52 | // Objects can also be used as arguments in events, where the same 53 | // race can apply. Thus, when an argument fails to resolve to an 54 | // object, we set the argument to nil. This is arguably gnarly, 55 | // because every event handler will have to check that its arguments 56 | // aren't nil. 57 | // 58 | // Some objects get destroyed by the server, not the client (most 59 | // famously wl_callback), so we mustn't create zombies if the object 60 | // has already been deleted, i.e. if our call to Destroy happens after 61 | // we have processed the server's Destroy. 62 | 63 | import ( 64 | "encoding/binary" 65 | "net" 66 | "reflect" 67 | "sync" 68 | "syscall" 69 | "unsafe" 70 | 71 | "honnef.co/go/wayland/wlproto" 72 | "honnef.co/go/wayland/wlshared" 73 | ) 74 | 75 | type Object interface { 76 | wlshared.Object 77 | Conn() *Conn 78 | Interface() *wlproto.Interface 79 | GetProxy() *Proxy 80 | Queue() *EventQueue 81 | EventHandlers() []interface{} 82 | } 83 | 84 | var byteOrder binary.ByteOrder 85 | 86 | func init() { 87 | var x uint32 = 0x01020304 88 | if *(*byte)(unsafe.Pointer(&x)) == 0x01 { 89 | byteOrder = binary.BigEndian 90 | } else { 91 | byteOrder = binary.LittleEndian 92 | } 93 | } 94 | 95 | // Proxy encapsulates all the internal state of client-side objects. 96 | // 97 | // Conceptually, a proxy is an ID that is associated with some 98 | // server-side resource. Concretely, a proxy contains all state 99 | // necessary for requests and events to function correctly. 100 | // 101 | // Code generation will create strongly typed wrapper structs for 102 | // Proxy, and user-level code will primarily interact with these 103 | // wrapper types. 104 | type Proxy struct { 105 | id wlshared.ObjectID 106 | conn *Conn 107 | eventHandlers []interface{} 108 | queue *EventQueue 109 | } 110 | 111 | // GetProxy returns p. It helps objects implement the Object interface. 112 | func (p *Proxy) GetProxy() *Proxy { return p } 113 | 114 | // ID returns the object's ID. 115 | func (p *Proxy) ID() wlshared.ObjectID { return p.id } 116 | 117 | // Conn returns the connection the object belongs to. 118 | func (p *Proxy) Conn() *Conn { return p.conn } 119 | 120 | // Queue returns the object's event queue. 121 | func (p *Proxy) Queue() *EventQueue { return p.queue } 122 | 123 | func (p *Proxy) EventHandlers() []interface{} { return p.eventHandlers } 124 | 125 | // SetListeners sets the event callbacks associated with the proxy. 126 | // 127 | // This is a function provided for use by generated code. 128 | // User-level code should use generated AddListener methods instead. 129 | func (p *Proxy) SetListeners(listeners ...interface{}) { 130 | copy(p.eventHandlers, listeners) 131 | } 132 | 133 | type object struct { 134 | kind uint8 135 | fds []int 136 | obj Object 137 | } 138 | 139 | const ( 140 | objectKindZombie = iota + 1 141 | ) 142 | 143 | type EventQueue struct { 144 | // signals the availability of events 145 | ch chan struct{} 146 | 147 | mu sync.Mutex 148 | events []Event 149 | } 150 | 151 | func NewEventQueue() *EventQueue { 152 | return &EventQueue{ 153 | ch: make(chan struct{}, 1), 154 | } 155 | } 156 | 157 | func (q *EventQueue) Push(ev Event) { 158 | q.mu.Lock() 159 | defer q.mu.Unlock() 160 | q.events = append(q.events, ev) 161 | select { 162 | case q.ch <- struct{}{}: 163 | default: 164 | } 165 | } 166 | 167 | func (q *EventQueue) Dispatch() { 168 | <-q.ch 169 | q.mu.Lock() 170 | defer q.mu.Unlock() 171 | for _, ev := range q.events { 172 | handlers := ev.Obj.EventHandlers() 173 | cb := handlers[ev.Ev] 174 | if cb != nil { 175 | args := []reflect.Value{reflect.ValueOf(ev.Obj)} 176 | args = append(args, ev.Args...) 177 | reflect.ValueOf(cb).Call(args) 178 | } 179 | } 180 | q.events = q.events[:0] 181 | } 182 | 183 | type Event struct { 184 | Obj Object 185 | Ev int 186 | Args []reflect.Value 187 | } 188 | 189 | type Conn struct { 190 | debug bool 191 | defaultQueue *EventQueue 192 | 193 | mu sync.Mutex 194 | rw *net.UnixConn 195 | objects map[wlshared.ObjectID]object 196 | maxID wlshared.ObjectID 197 | sendBuf []byte 198 | 199 | data []byte 200 | fds []uintptr 201 | } 202 | 203 | func NewConn(rw *net.UnixConn) *Conn { 204 | c := &Conn{ 205 | rw: rw, 206 | objects: map[wlshared.ObjectID]object{}, 207 | debug: true, 208 | defaultQueue: NewEventQueue(), 209 | maxID: 1, 210 | } 211 | go c.readLoop() 212 | return c 213 | } 214 | 215 | // NewWrapper initializes the proxy in wrapper with a copy of obj's 216 | // proxy, but with a different queue. 217 | // 218 | // This is a function provided for use by generated code. 219 | // User-level code should use generated WithQueue methods instead. 220 | func (c *Conn) NewWrapper(obj Object, wrapper Object, queue *EventQueue) { 221 | p := obj.GetProxy() 222 | *wrapper.GetProxy() = Proxy{ 223 | id: p.ID(), 224 | conn: c, 225 | queue: queue, 226 | } 227 | } 228 | 229 | // NewProxy initialized the proxy in obj with the given id and queue. 230 | // If queue is nil, the connection's default queue will be used. 231 | func (c *Conn) NewProxy(id wlshared.ObjectID, obj Object, queue *EventQueue) { 232 | c.mu.Lock() 233 | defer c.mu.Unlock() 234 | 235 | if queue == nil { 236 | queue = c.defaultQueue 237 | } 238 | p := obj.GetProxy() 239 | *p = Proxy{ 240 | id: id, 241 | conn: c, 242 | eventHandlers: make([]interface{}, len(obj.Interface().Events)), 243 | queue: queue, 244 | } 245 | if id != 0 { 246 | c.objects[id] = object{obj: obj} 247 | } 248 | } 249 | 250 | // Destroy destroys obj's proxy. 251 | // 252 | // This is a function provided for use by generated code. 253 | // User-level code should use generated destructors instead. 254 | func (c *Conn) Destroy(obj Object) { 255 | c.mu.Lock() 256 | defer c.mu.Unlock() 257 | delete(c.objects, obj.ID()) 258 | } 259 | 260 | func (c *Conn) SendDestructor(obj Object, request int, args ...interface{}) { 261 | c.mu.Lock() 262 | defer c.mu.Unlock() 263 | c.sendRequest(obj, request, args...) 264 | 265 | if _, ok := c.objects[obj.ID()]; !ok { 266 | // the server has already destroyed the object before we 267 | // decided to destroy it 268 | return 269 | } 270 | 271 | // OPT(dh): cache this computation 272 | var fds []int 273 | for i, ev := range obj.Interface().Events { 274 | var nfds int 275 | for _, arg := range ev.Args { 276 | if arg.Type == wlproto.ArgTypeFd { 277 | nfds++ 278 | } 279 | } 280 | if nfds > 0 { 281 | if fds == nil { 282 | fds = make([]int, len(obj.Interface().Events)) 283 | fds[i] = nfds 284 | } 285 | } 286 | } 287 | 288 | c.objects[obj.ID()] = object{ 289 | kind: objectKindZombie, 290 | fds: fds, 291 | obj: obj, 292 | } 293 | } 294 | 295 | func (c *Conn) SendRequest(source Object, request int, args ...interface{}) { 296 | c.mu.Lock() 297 | defer c.mu.Unlock() 298 | c.sendRequest(source, request, args...) 299 | } 300 | 301 | func (c *Conn) sendRequest(source Object, request int, args ...interface{}) { 302 | for _, arg := range args { 303 | if arg, ok := arg.(Object); ok { 304 | id := arg.ID() 305 | if id == 0 { 306 | c.maxID++ 307 | id = c.maxID 308 | 309 | p := arg.GetProxy() 310 | if p.conn == nil { 311 | *p = Proxy{ 312 | id: id, 313 | conn: c, 314 | eventHandlers: make([]interface{}, len(arg.Interface().Events)), 315 | queue: c.defaultQueue, 316 | } 317 | } else { 318 | p.id = id 319 | } 320 | c.objects[id] = object{obj: arg} 321 | } 322 | } 323 | } 324 | 325 | buf := c.sendBuf[:0] 326 | var oob []byte 327 | buf, oob = wlshared.EncodeRequest(buf, source.ID(), request, args) 328 | 329 | c.rw.WriteMsgUnix(buf, oob, nil) 330 | 331 | c.sendBuf = buf[:0] 332 | } 333 | 334 | func (c *Conn) read() { 335 | b := make([]byte, 1<<16) 336 | // XXX can there be more than one SCM per message? 337 | // XXX in general, be more robust in handling SCM 338 | oob := make([]byte, 24) 339 | n, oobn, _, _, err := c.rw.ReadMsgUnix(b, oob) 340 | if err != nil { 341 | panic(err) 342 | } 343 | c.data = append(c.data, b[:n]...) 344 | if oobn == 24 { 345 | scm, err := syscall.ParseSocketControlMessage(oob[:oobn]) 346 | if err != nil { 347 | panic(err) 348 | } 349 | fds, err := syscall.ParseUnixRights(&scm[0]) 350 | if err != nil { 351 | panic(err) 352 | } 353 | c.fds = append(c.fds, uintptr(fds[0])) 354 | } 355 | } 356 | 357 | func (c *Conn) readAtLeast(n int) { 358 | for len(c.data) < n { 359 | c.read() 360 | } 361 | } 362 | 363 | func (c *Conn) readLoop() { 364 | for { 365 | c.readAtLeast(8) 366 | sender := wlshared.ObjectID(byteOrder.Uint32(c.data[0:4])) 367 | h := byteOrder.Uint32(c.data[4:8]) 368 | size := (h & 0xFFFF0000) >> 16 369 | if size < 8 { 370 | // XXX invalid size 371 | } 372 | size -= 8 373 | // XXX guard against invalid opcodes 374 | opcode := h & 0x0000FFFF 375 | c.data = c.data[8:] 376 | c.readAtLeast(int(size)) 377 | 378 | d := c.data[:size] 379 | c.data = c.data[size:] 380 | 381 | c.mu.Lock() 382 | objw, ok := c.objects[sender] 383 | if !ok { 384 | // we don't know the object, we don't even have a zombie 385 | // for it -> ignore the message. 386 | c.mu.Unlock() 387 | continue 388 | } 389 | if objw.kind == objectKindZombie { 390 | // this object has been deleted, see if we need to consume 391 | // fds. 392 | if fds := objw.fds[opcode]; fds > 0 { 393 | copy(c.fds, c.fds[fds:]) 394 | c.fds = c.fds[:len(c.fds)-fds] 395 | } 396 | c.mu.Unlock() 397 | continue 398 | } 399 | obj := objw.obj 400 | c.mu.Unlock() 401 | off := 0 402 | sig := obj.Interface().Events[opcode].Args 403 | args := make([]reflect.Value, len(sig)) 404 | for i, arg := range sig { 405 | newOff, argv := wlshared.ParseArgument(arg, d, off) 406 | off = newOff 407 | 408 | switch arg.Type { 409 | case wlproto.ArgTypeObject: 410 | c.mu.Lock() 411 | // XXX guard against invalid object id 412 | args[i] = reflect.ValueOf(c.objects[argv.(wlshared.ObjectID)]) 413 | c.mu.Unlock() 414 | case wlproto.ArgTypeFd: 415 | fd := c.fds[0] 416 | copy(c.fds, c.fds[1:]) 417 | c.fds = c.fds[:len(c.fds)-1] 418 | args[i] = reflect.ValueOf(uintptr(fd)) 419 | case wlproto.ArgTypeNewID: 420 | // XXX verify that the new ID isn't already in use 421 | // XXX verify that the new ID is in the server's ID space 422 | v := reflect.New(arg.Aux.Elem()).Interface().(Object) 423 | c.NewProxy(argv.(wlshared.ObjectID), v, obj.Queue()) 424 | c.mu.Lock() 425 | c.objects[argv.(wlshared.ObjectID)] = object{obj: v} 426 | c.mu.Unlock() 427 | default: 428 | args[i] = reflect.ValueOf(argv) 429 | } 430 | } 431 | 432 | if sender == 1 && opcode == 1 { 433 | // Special case for the delete_id event on wl_display. 434 | // Primarily to clean up zombies, but this event may also 435 | // fire without us having called a server-side destructor. 436 | // For example, wl_callback gets destroyed by the server 437 | // once it has fired. 438 | c.mu.Lock() 439 | id := wlshared.ObjectID(args[0].Uint()) 440 | delete(c.objects, id) 441 | c.mu.Unlock() 442 | } 443 | obj.GetProxy().queue.Push(Event{obj, int(opcode), args}) 444 | } 445 | } 446 | -------------------------------------------------------------------------------- /wlserver/protocol.go: -------------------------------------------------------------------------------- 1 | package wlserver 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "io" 7 | "math" 8 | "net" 9 | "reflect" 10 | "sync" 11 | "sync/atomic" 12 | "syscall" 13 | "unsafe" 14 | 15 | "honnef.co/go/wayland/wlproto" 16 | "honnef.co/go/wayland/wlshared" 17 | ) 18 | 19 | // TODO implement removal of globals, take the race documented in 20 | // https://gitlab.freedesktop.org/wayland/wayland/-/issues/10 into consideration 21 | 22 | var byteOrder binary.ByteOrder 23 | 24 | func init() { 25 | var x uint32 = 0x01020304 26 | if *(*byte)(unsafe.Pointer(&x)) == 0x01 { 27 | byteOrder = binary.BigEndian 28 | } else { 29 | byteOrder = binary.LittleEndian 30 | } 31 | } 32 | 33 | type Display struct { 34 | l *net.UnixListener 35 | clientID uint64 36 | 37 | clients map[*Client]struct{} 38 | globalsID uint32 39 | globals map[uint32]global 40 | 41 | newConns chan net.Conn 42 | messages chan Message 43 | disconnects chan Disconnect 44 | } 45 | 46 | type Disconnect struct { 47 | Client *Client 48 | Err error 49 | } 50 | 51 | func NewDisplay(l *net.UnixListener) *Display { 52 | return &Display{ 53 | l: l, 54 | clients: make(map[*Client]struct{}), 55 | globals: map[uint32]global{}, 56 | newConns: make(chan net.Conn), 57 | messages: make(chan Message), 58 | disconnects: make(chan Disconnect), 59 | } 60 | } 61 | 62 | func (dsp *Display) NewConns() <-chan net.Conn { 63 | return dsp.newConns 64 | } 65 | 66 | func (dsp *Display) Messages() <-chan Message { 67 | return dsp.messages 68 | } 69 | 70 | func (dsp *Display) Disconnects() <-chan Disconnect { 71 | return dsp.disconnects 72 | } 73 | 74 | type ProtocolError struct { 75 | Object Object 76 | Code uint32 77 | Message string 78 | } 79 | 80 | func (err *ProtocolError) Error() string { 81 | return fmt.Sprintf("protocol error with code %d for object %s: %s", err.Code, objectString(err.Object), err.Message) 82 | } 83 | 84 | func (dsp *Display) Error(obj Object, code uint32, message string) { 85 | obj.Conn().objects[1].(displayResource).Error(obj, code, message) 86 | err := error(&ProtocolError{obj, code, message}) 87 | obj.Conn().err.CompareAndSwap(nil, &err) 88 | obj.Conn().rw.Close() 89 | } 90 | 91 | type global struct { 92 | iface *wlproto.Interface 93 | version int 94 | bind func(Object) ResourceImplementation 95 | } 96 | 97 | func (dsp *Display) AddGlobal(iface *wlproto.Interface, version int, bind func(Object) ResourceImplementation) uint32 { 98 | dsp.globalsID++ 99 | name := dsp.globalsID 100 | if dsp.globalsID == math.MaxUint32 { 101 | // XXX reclaim names used by deleted globals 102 | panic("global counter overflow") 103 | } 104 | dsp.globals[name] = global{iface, version, bind} 105 | 106 | for c := range dsp.clients { 107 | for _, obj := range c.registries { 108 | obj.Global(name, iface.Name, uint32(version)) 109 | } 110 | } 111 | 112 | return name 113 | } 114 | 115 | func (dsp *Display) RemoveGlobal(name uint32) { 116 | delete(dsp.globals, name) 117 | 118 | for c := range dsp.clients { 119 | for _, obj := range c.registries { 120 | obj.GlobalRemove(name) 121 | } 122 | } 123 | } 124 | 125 | type Message struct { 126 | Client *Client 127 | Sender wlshared.ObjectID 128 | Opcode uint32 129 | Data []byte 130 | } 131 | 132 | func (dsp *Display) AddClient(conn net.Conn) *Client { 133 | client := &Client{ 134 | dsp: dsp, 135 | id: dsp.clientID, 136 | rw: conn.(*net.UnixConn), 137 | objects: map[wlshared.ObjectID]Object{}, 138 | implementations: map[wlshared.ObjectID]ResourceImplementation{}, 139 | registries: map[wlshared.ObjectID]registryResource{}, 140 | } 141 | 142 | client.objects[1] = displayResource{ 143 | Resource: Resource{ 144 | id: 1, 145 | conn: client, 146 | version: 1, 147 | }, 148 | } 149 | client.implementations[1] = displayImplementation(displaySingleton{dsp}) 150 | 151 | dsp.clientID++ 152 | dsp.clients[client] = struct{}{} 153 | go func() { 154 | err := client.readLoop(dsp.messages) 155 | client.rw.Close() 156 | if werr, ok := client.err.Load().(*error); ok { 157 | // favour the write or protocol error over the read error 158 | err = *werr 159 | } 160 | // XXX destroy all resources before officially disconnecting the client 161 | dsp.disconnects <- Disconnect{client, err} 162 | }() 163 | return client 164 | } 165 | 166 | func (dsp *Display) RemoveClient(client *Client) { 167 | // XXX properly disconnect the client if it isn't already disconnected 168 | delete(dsp.clients, client) 169 | } 170 | 171 | type buf []byte 172 | 173 | func (b *buf) uint32() uint32 { 174 | n := byteOrder.Uint32(*b) 175 | *b = (*b)[4:] 176 | return n 177 | } 178 | 179 | func (b *buf) string() string { 180 | n := byteOrder.Uint32(*b) 181 | // -1 to skip terminating null byte 182 | data := (*b)[4 : 4+n-1] 183 | // strings are padded to 32-bit boundary 184 | n = (n + 3) &^ 0x03 185 | *b = (*b)[4+n:] 186 | return string(data) 187 | } 188 | 189 | type displaySingleton struct { 190 | dsp *Display 191 | } 192 | 193 | func (dsp displaySingleton) GetRegistry(obj displayResource, registry registryResource) registryImplementation { 194 | obj.conn.registries[registry.ID()] = registry 195 | for name, g := range dsp.dsp.globals { 196 | registry.Global(name, g.iface.Name, uint32(g.version)) 197 | } 198 | return dsp 199 | } 200 | 201 | func (dsp displaySingleton) Sync(obj displayResource, cb callbackResource) callbackImplementation { 202 | // XXX how… how do we destroy the callback after calling done? answer: by fixing wayland-scanner; the event has a destructor type 203 | // XXX "The callback_data passed in the callback is the event serial." 204 | cb.Done(0) 205 | // wl_callback has no requests, it doesn't matter what we return here, except that it has to be non-nil 206 | return dsp 207 | } 208 | 209 | func (dsp displaySingleton) Bind(reg registryResource, name uint32, idName string, idVersion uint32, id wlshared.ObjectID) ResourceImplementation { 210 | g, ok := dsp.dsp.globals[name] 211 | if !ok { 212 | if name > dsp.dsp.globalsID { 213 | // XXX the client tried to bind to a global that has never existed and we should kill the client 214 | } else { 215 | // XXX the global has been removed and the client's bind raced with the removal. we should set up a 216 | // tombstone that later gets destroyed by the client. 217 | } 218 | } 219 | 220 | res := Resource{ 221 | conn: reg.conn, 222 | id: id, 223 | } 224 | rv := reflect.New(g.iface.Type).Elem() 225 | rv.Field(0).Set(reflect.ValueOf(res)) 226 | v := rv.Interface().(Object) 227 | reg.conn.objects[id] = v 228 | 229 | // XXX verify that idName matches the global's interface 230 | 231 | return g.bind(v) 232 | // TODO(dh): we should verify that bind returned the correct implementation, e.g. a global with 233 | // wayland.SeatInterface returns an implementation that implements wayland.SeatImplementation 234 | } 235 | 236 | func (dsp *Display) ProcessMessage(msg Message) { 237 | c := msg.Client 238 | 239 | // XXX make sure there aren't other places that also need this check 240 | if _, ok := msg.Client.err.Load().(*error); ok { 241 | // don't process message if the client has already failed 242 | return 243 | } 244 | 245 | d := buf(msg.Data) 246 | opcode := msg.Opcode 247 | sender := msg.Sender 248 | obj, ok := c.objects[sender] 249 | if !ok { 250 | // TODO(dh): is it okay for objects to be unknown when we're the server, or should we kill the client? 251 | 252 | // unknown object 253 | return 254 | } 255 | off := 0 256 | // XXX guard against invalid opcodes 257 | sig := obj.Interface().Requests[opcode].Args 258 | allArgs := make([]reflect.Value, len(sig)+2) 259 | impl := c.implementations[sender] 260 | allArgs[0] = reflect.ValueOf(impl) 261 | allArgs[1] = reflect.ValueOf(obj) 262 | args := allArgs[2:] 263 | // XXX guard against provided arguments not matching signature in protocol spec 264 | for i, arg := range sig { 265 | // XXX guard against not enough arguments being provided 266 | newOff, argv := wlshared.ParseArgument(arg, d, off) 267 | off = newOff 268 | 269 | switch arg.Type { 270 | case wlproto.ArgTypeObject: 271 | // XXX guard against invalid object id 272 | args[i] = reflect.ValueOf(c.objects[argv.(wlshared.ObjectID)]) 273 | case wlproto.ArgTypeFd: 274 | fd := c.fds[0] 275 | copy(c.fds, c.fds[1:]) 276 | c.fds = c.fds[:len(c.fds)-1] 277 | args[i] = reflect.ValueOf(uintptr(fd)) 278 | case wlproto.ArgTypeNewID: 279 | // XXX verify that the new ID isn't already in use 280 | // XXX verify that the new ID is in the client's ID space 281 | num := argv.(wlshared.ObjectID) 282 | // XXX handle new_id with no specified interface, and thus no Aux 283 | if arg.Aux == nil { 284 | args[i] = reflect.ValueOf(argv) 285 | } else { 286 | // XXX set the resource's version. it will be tied to the version of the resource to which the request is being sent 287 | res := Resource{ 288 | conn: c, 289 | id: wlshared.ObjectID(num), 290 | } 291 | rv := reflect.New(arg.Aux).Elem() 292 | rv.Field(0).Set(reflect.ValueOf(res)) 293 | v := rv.Interface().(Object) 294 | c.objects[wlshared.ObjectID(num)] = v 295 | args[i] = rv 296 | } 297 | default: 298 | args[i] = reflect.ValueOf(argv) 299 | } 300 | } 301 | 302 | // XXX guard against opcodes that don't exist in our version of the protocol 303 | meth := obj.Interface().Requests[opcode].Method 304 | results := meth.Call(allArgs) 305 | 306 | n := 0 307 | for i, arg := range sig { 308 | if arg.Type == wlproto.ArgTypeNewID { 309 | var obj Object 310 | if arg.Aux == nil { 311 | id := args[i].Interface().(wlshared.ObjectID) 312 | obj = c.objects[id] 313 | } else { 314 | obj = args[i].Interface().(Object) 315 | } 316 | obj.GetResource().SetImplementation(results[n].Interface().(ResourceImplementation)) 317 | n++ 318 | } 319 | } 320 | 321 | if obj.Interface().Requests[opcode].Type == "destructor" { 322 | delete(c.objects, obj.ID()) 323 | delete(c.implementations, obj.ID()) 324 | c.objects[1].(displayResource).DeleteID(uint32(obj.ID())) 325 | } 326 | } 327 | 328 | func objectString(obj Object) string { 329 | return fmt.Sprintf("%s@%d", obj.Interface().Name, obj.ID()) 330 | } 331 | 332 | func (dsp *Display) Run() { 333 | for { 334 | conn, err := dsp.l.Accept() 335 | if err != nil { 336 | // XXX 337 | panic(err) 338 | return 339 | } 340 | dsp.newConns <- conn 341 | } 342 | } 343 | 344 | type Client struct { 345 | dsp *Display 346 | id uint64 347 | rw *net.UnixConn 348 | 349 | // TODO merge objects and implementations maps 350 | objects map[wlshared.ObjectID]Object 351 | // registries tracks the client's registry resources for faster access. these objects are also present in the 352 | // objects map. 353 | registries map[wlshared.ObjectID]registryResource 354 | implementations map[wlshared.ObjectID]ResourceImplementation 355 | 356 | err atomic.Value 357 | 358 | fds []uintptr 359 | 360 | sendMu sync.RWMutex 361 | sendBuf []byte 362 | } 363 | 364 | func (c *Client) ID() uint64 { return c.id } 365 | 366 | func (c *Client) read(b []byte) (int, error) { 367 | // XXX can there be more than one SCM per message? 368 | // XXX in general, be more robust in handling SCM 369 | oob := make([]byte, 24) 370 | n, oobn, _, _, err := c.rw.ReadMsgUnix(b, oob) 371 | if err != nil { 372 | return n, err 373 | } 374 | if oobn == 24 { 375 | scm, err := syscall.ParseSocketControlMessage(oob[:oobn]) 376 | if err != nil { 377 | return n, err 378 | } 379 | fds, err := syscall.ParseUnixRights(&scm[0]) 380 | if err != nil { 381 | return n, err 382 | } 383 | c.fds = append(c.fds, uintptr(fds[0])) 384 | } 385 | return n, nil 386 | } 387 | 388 | func (c *Client) readFull(buf []byte) (n int, err error) { 389 | for n < len(buf) && err == nil { 390 | var nn int 391 | nn, err = c.read(buf[n:]) 392 | n += nn 393 | } 394 | if n == len(buf) { 395 | err = nil 396 | } else if n > 0 && err == io.EOF { 397 | err = io.ErrUnexpectedEOF 398 | } 399 | return 400 | } 401 | 402 | func (c *Client) readLoop(msgs chan<- Message) error { 403 | // We are the server, thus we are reading requests 404 | hdr := make([]byte, 8) 405 | for { 406 | if _, err := c.readFull(hdr); err != nil { 407 | return err 408 | } 409 | sender := wlshared.ObjectID(byteOrder.Uint32(hdr[0:4])) 410 | h := byteOrder.Uint32(hdr[4:8]) 411 | size := (h & 0xFFFF0000) >> 16 412 | if size < 8 { 413 | // XXX invalid size 414 | } 415 | size -= 8 416 | opcode := h & 0x0000FFFF 417 | 418 | buf := make([]byte, int(size)) 419 | if _, err := c.readFull(buf); err != nil { 420 | return err 421 | } 422 | 423 | msgs <- Message{ 424 | Client: c, 425 | Sender: sender, 426 | Opcode: opcode, 427 | Data: buf, 428 | } 429 | } 430 | } 431 | 432 | type Resource struct { 433 | id wlshared.ObjectID 434 | conn *Client 435 | version uint32 436 | } 437 | 438 | type ResourceImplementation interface{} 439 | 440 | func (p Resource) SetImplementation(impl ResourceImplementation) { 441 | p.conn.implementations[p.id] = impl 442 | } 443 | 444 | func (p Resource) GetResource() Resource { return p } 445 | func (p Resource) Conn() *Client { return p.conn } 446 | func (p Resource) ID() wlshared.ObjectID { return p.id } 447 | func (p Resource) Version() uint32 { return p.version } 448 | 449 | type Object interface { 450 | ID() wlshared.ObjectID 451 | Conn() *Client 452 | Interface() *wlproto.Interface 453 | GetResource() Resource 454 | } 455 | 456 | func (c *Client) SendEvent(source wlshared.Object, event int, args ...interface{}) { 457 | c.sendMu.Lock() 458 | defer c.sendMu.Unlock() 459 | 460 | buf := c.sendBuf[:0] 461 | var oob []byte 462 | buf, oob = wlshared.EncodeRequest(buf, source.ID(), event, args) 463 | _, _, err := c.rw.WriteMsgUnix(buf, oob, nil) 464 | if err != nil { 465 | // Set c.err if it hasn't been set yet 466 | c.err.CompareAndSwap(nil, &err) 467 | c.rw.Close() 468 | } 469 | 470 | c.sendBuf = buf[:0] 471 | } 472 | -------------------------------------------------------------------------------- /cmd/wayland-scanner/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/xml" 6 | "flag" 7 | "fmt" 8 | "go/format" 9 | "io" 10 | "log" 11 | "os" 12 | "strings" 13 | 14 | "github.com/shurcooL/graphql/ident" 15 | ) 16 | 17 | type elProtocol struct { 18 | Name string `xml:"name,attr"` 19 | 20 | Copyright elCopyright `xml:"copyright"` 21 | Description elDescription `xml:"description"` 22 | Interfaces []elInterface `xml:"interface"` 23 | } 24 | 25 | type elCopyright struct { 26 | Text string `xml:",cdata"` 27 | } 28 | 29 | type elInterface struct { 30 | Name string `xml:"name,attr"` 31 | Version string `xml:"version,attr"` 32 | 33 | Description elDescription `xml:"description"` 34 | Requests []elRequest `xml:"request"` 35 | Events []elEvent `xml:"event"` 36 | Enums []elEnum `xml:"enum"` 37 | } 38 | 39 | type elRequest struct { 40 | Name string `xml:"name,attr"` 41 | Type string `xml:"type,attr"` 42 | Since string `xml:"since,attr"` 43 | 44 | Description elDescription `xml:"description"` 45 | Args []elArg `xml:"arg"` 46 | } 47 | 48 | type elEvent struct { 49 | Name string `xml:"name,attr"` 50 | Since string `xml:"since,attr"` 51 | 52 | Description elDescription `xml:"description"` 53 | Args []elArg `xml:"arg"` 54 | } 55 | 56 | type elEnum struct { 57 | Name string `xml:"name,attr"` 58 | Since string `xml:"since,attr"` 59 | Bitfield string `xml:"bitfield,attr"` 60 | 61 | Description elDescription `xml:"description"` 62 | Entries []elEntry `xml:"entry"` 63 | } 64 | 65 | type elEntry struct { 66 | Name string `xml:"name,attr"` 67 | Value string `xml:"value,attr"` 68 | Summary string `xml:"summary,attr"` 69 | Since string `xml:"since,attr"` 70 | 71 | Description elDescription `xml:"description"` 72 | } 73 | 74 | type elArg struct { 75 | Name string `xml:"name,attr"` 76 | Type string `xml:"type,attr"` 77 | Summary string `xml:"summary,attr"` 78 | Interface string `xml:"interface,attr"` 79 | AllowNull string `xml:"allow-null,attr"` 80 | Enum string `xml:"enum,attr"` 81 | 82 | Description elDescription `xml:"description"` 83 | } 84 | 85 | type elDescription struct { 86 | Summary string `xml:"summary,attr"` 87 | 88 | Text string `xml:",cdata"` 89 | } 90 | 91 | func (b *Builder) trimPrefix(name string) string { 92 | return strings.TrimPrefix(name, b.Prefix) 93 | } 94 | 95 | func (b *Builder) typeName(name string) string { 96 | name = b.trimPrefix(name) 97 | if len(name) == 0 { 98 | // XXX 99 | } 100 | return exportedGoIdentifier(name) 101 | } 102 | 103 | func (b *Builder) eventsTypeName(iface elInterface) string { 104 | if b.ServerMode { 105 | return fmt.Sprintf("%sImplementation", b.typeName(iface.Name)) 106 | } else { 107 | return fmt.Sprintf("%sEvents", b.typeName(iface.Name)) 108 | } 109 | } 110 | 111 | // XXX check for reserved names 112 | 113 | func exportedGoIdentifier(name string) string { 114 | name = strings.TrimSuffix(name, "_") 115 | return ident.Name(strings.Split(name, "_")).ToMixedCaps() 116 | } 117 | 118 | func goIdentifier(name string) string { 119 | name = strings.TrimSuffix(name, "_") 120 | name = ident.Name(strings.Split(name, "_")).ToLowerCamelCase() 121 | return mapReserved(name) 122 | } 123 | 124 | func mapReserved(name string) string { 125 | switch name { 126 | case "interface": 127 | return "interface_" 128 | default: 129 | return name 130 | } 131 | } 132 | 133 | func (b *Builder) wlprotoInterfaceName(iface elInterface) string { 134 | name := b.trimPrefix(iface.Name) 135 | if len(name) == 0 { 136 | // XXX 137 | } 138 | return exportedGoIdentifier(name) + "Interface" 139 | } 140 | 141 | func (b *Builder) wlprotoArg(arg elArg, ctx elInterface) string { 142 | if arg.Type == "new_id" && arg.Interface == "" { 143 | // new_id without an interface expands to stringified name, version, id 144 | return "{Type: wlproto.ArgTypeString}, {Type: wlproto.ArgTypeUint}, {Type: wlproto.ArgTypeNewID}" 145 | } 146 | 147 | var typ string 148 | switch arg.Type { 149 | case "int": 150 | typ = "ArgTypeInt" 151 | case "uint": 152 | typ = "ArgTypeUint" 153 | case "fixed": 154 | typ = "ArgTypeFixed" 155 | case "string": 156 | typ = "ArgTypeString" 157 | case "object": 158 | typ = "ArgTypeObject" 159 | case "new_id": 160 | typ = "ArgTypeNewID" 161 | case "array": 162 | typ = "ArgTypeArray" 163 | case "fd": 164 | typ = "ArgTypeFd" 165 | default: 166 | panic("XXX") 167 | } 168 | if arg.Interface == "" { 169 | if arg.Enum == "" { 170 | return fmt.Sprintf("{Type: wlproto.%s}", typ) 171 | } else { 172 | return fmt.Sprintf("{Type: wlproto.%s, Aux: reflect.TypeOf(%s(0))}", typ, b.goTypeFromWlType(arg, ctx)) 173 | } 174 | } else { 175 | if b.ServerMode { 176 | return fmt.Sprintf("{Type: wlproto.%s, Aux: reflect.TypeOf(%s{})}", typ, b.qualifyTypeName(arg.Interface)) 177 | } else { 178 | return fmt.Sprintf("{Type: wlproto.%s, Aux: reflect.TypeOf((*%s)(nil))}", typ, b.qualifyTypeName(arg.Interface)) 179 | } 180 | } 181 | } 182 | 183 | func (b *Builder) goTypeFromWlType(arg elArg, ctx elInterface) string { 184 | typ := arg.Type 185 | iface := arg.Interface 186 | if arg.Enum != "" { 187 | if strings.ContainsRune(arg.Enum, '.') { 188 | parts := strings.SplitN(arg.Enum, ".", 2) 189 | return b.typeName(parts[0]) + exportedGoIdentifier(parts[1]) 190 | } else { 191 | return b.typeName(ctx.Name) + exportedGoIdentifier(arg.Enum) 192 | } 193 | } 194 | switch typ { 195 | case "int": 196 | return "int32" 197 | case "uint": 198 | return "uint32" 199 | case "fixed": 200 | return "wlshared.Fixed" 201 | case "string": 202 | return "string" 203 | case "object": 204 | if iface != "" { 205 | if b.ServerMode { 206 | return b.qualifyTypeName(iface) 207 | } else { 208 | return "*" + b.qualifyTypeName(iface) 209 | } 210 | } else { 211 | if b.ServerMode { 212 | return "wlserver.Object" 213 | } else { 214 | return "wlclient.Object" 215 | } 216 | } 217 | case "new_id": 218 | if iface != "" { 219 | if b.ServerMode { 220 | return b.qualifyTypeName(iface) 221 | } else { 222 | return "*" + b.qualifyTypeName(iface) 223 | } 224 | } else { 225 | if b.ServerMode { 226 | return "wlserver.Object" 227 | } else { 228 | return "wlclient.Object" 229 | } 230 | } 231 | case "array": 232 | return "[]byte" 233 | case "fd": 234 | return "uintptr" 235 | default: 236 | return "UNKNOWN_TYPE_" + typ 237 | } 238 | } 239 | 240 | func docString(docs elDescription) string { 241 | text := docs.Text 242 | if text == "" { 243 | text = docs.Summary 244 | if text == "" { 245 | return "" 246 | } 247 | } 248 | 249 | text = strings.TrimSpace(text) 250 | lines := strings.Split(text, "\n") 251 | for i, line := range lines { 252 | lines[i] = "// " + strings.TrimSpace(line) 253 | } 254 | return strings.Join(lines, "\n") 255 | } 256 | 257 | func enumEntryDocString(entry elEntry) string { 258 | if entry.Description.Text != "" || entry.Description.Summary != "" { 259 | return docString(entry.Description) 260 | } 261 | if entry.Summary == "" { 262 | return "" 263 | } 264 | return "// " + fixNewlineInSummaryAttribute(entry.Summary) 265 | } 266 | 267 | func fixNewlineInSummaryAttribute(s string) string { 268 | // The xdg-shell protocol contains this: 269 | // 270 | // 272 | // 273 | // Remove the newline and indentation. 274 | if !strings.Contains(s, "\n") { 275 | return s 276 | } 277 | lines := strings.Split(s, "\n") 278 | for i := range lines { 279 | lines[i] = strings.TrimLeft(lines[i], " ") 280 | } 281 | return strings.Join(lines, " ") 282 | } 283 | 284 | func (b *Builder) qualifyTypeName(name string) string { 285 | pkg, ok := b.Interfaces[name] 286 | if !ok { 287 | panic(fmt.Sprintf("unknown interface %s", name)) 288 | } 289 | if pkg != nil { 290 | b.Imports[pkg.Path] = true 291 | return pkg.Name + "." + pkg.Interfaces[name] 292 | } 293 | return b.typeName(name) 294 | } 295 | 296 | type Builder struct { 297 | Spec elProtocol 298 | Imports map[string]bool 299 | Interfaces map[string]*Package 300 | Code bytes.Buffer 301 | Prefix string 302 | ServerMode bool 303 | } 304 | 305 | func (b *Builder) Write(data []byte) (int, error) { 306 | return b.Code.Write(data) 307 | } 308 | 309 | func (b *Builder) printSpecs(out io.Writer) { 310 | printPackage := func() { 311 | pkg := goIdentifier(b.Spec.Name) 312 | fmt.Fprintln(out, "// Code generated by wayland-scanner; DO NOT EDIT.") 313 | fmt.Fprintln(out) 314 | fmt.Fprintf(out, "// Package %s contains generated definitions of the %s Wayland protocol.\n", pkg, b.Spec.Name) 315 | if b.Spec.Description.Text != "" { 316 | fmt.Fprintln(out, "//") 317 | seenNonEmpty := false 318 | lines := strings.Split(b.Spec.Description.Text, "\n") 319 | for _, line := range lines { 320 | line = strings.TrimSpace(line) 321 | if line == "" && !seenNonEmpty { 322 | continue 323 | } 324 | seenNonEmpty = true 325 | fmt.Fprintf(out, "// %s\n", line) 326 | } 327 | } 328 | fmt.Fprintf(out, "package %s\n", pkg) 329 | 330 | } 331 | 332 | printImports := func() { 333 | fmt.Fprintln(out, "import (") 334 | for imp := range b.Imports { 335 | fmt.Fprintf(out, "%q\n", imp) 336 | } 337 | fmt.Fprintln(out, ")") 338 | fmt.Fprintln(out, "var _ wlshared.Fixed\n") // make sure the import isn't unused 339 | } 340 | 341 | printMaps := func() { 342 | fmt.Fprintln(b, "var interfaceNames = map[string]string{") 343 | for _, iface := range b.Spec.Interfaces { 344 | fmt.Fprintf(b, "%q: %q,\n", iface.Name, b.typeName(iface.Name)) 345 | } 346 | fmt.Fprint(b, "}\n\n") 347 | 348 | fmt.Fprintln(b, "var Interfaces = map[string]*wlproto.Interface{") 349 | for _, iface := range b.Spec.Interfaces { 350 | fmt.Fprintf(b, "%q: %s,\n", iface.Name, b.wlprotoInterfaceName(iface)) 351 | } 352 | fmt.Fprint(b, "}\n\n") 353 | 354 | fmt.Fprintln(b, "var Requests = map[string]*wlproto.Request{") 355 | for _, iface := range b.Spec.Interfaces { 356 | for ireq, req := range iface.Requests { 357 | fmt.Fprintf(b, "\"%s_%s\": &%s.Requests[%d],\n", iface.Name, req.Name, b.wlprotoInterfaceName(iface), ireq) 358 | } 359 | } 360 | fmt.Fprint(b, "}\n\n") 361 | 362 | fmt.Fprintln(b, "var Events = map[string]*wlproto.Event{") 363 | for _, iface := range b.Spec.Interfaces { 364 | for iev, ev := range iface.Events { 365 | fmt.Fprintf(b, "\"%s_%s\": &%s.Events[%d],\n", iface.Name, ev.Name, b.wlprotoInterfaceName(iface), iev) 366 | } 367 | } 368 | fmt.Fprintln(b, "}") 369 | } 370 | 371 | printInterface := func(iface elInterface) { 372 | printEnums := func() { 373 | for _, enum := range iface.Enums { 374 | fmt.Fprintln(b, docString(enum.Description)) 375 | fmt.Fprintf(b, "type %s%s uint32\n\n", b.typeName(iface.Name), exportedGoIdentifier(enum.Name)) 376 | fmt.Fprintln(b, "const (") 377 | for _, entry := range enum.Entries { 378 | doc := enumEntryDocString(entry) 379 | if doc != "" { 380 | fmt.Fprintf(b, "%s\n", doc) 381 | } 382 | fmt.Fprintf(b, "%[1]s%[2]s%[3]s %[1]s%[2]s = %[4]s\n", b.typeName(iface.Name), exportedGoIdentifier(enum.Name), exportedGoIdentifier(entry.Name), entry.Value) 383 | } 384 | fmt.Fprintln(b, ")") 385 | } 386 | } 387 | 388 | printInterfaceVar := func() { 389 | fmt.Fprintf(b, "var %s = &wlproto.Interface{\n", b.wlprotoInterfaceName(iface)) 390 | fmt.Fprintf(b, "Name: %q,\n", iface.Name) 391 | fmt.Fprintf(b, "Version: %s,\n", iface.Version) 392 | if b.ServerMode { 393 | fmt.Fprintf(b, "Type: reflect.TypeOf(%s{}),\n", b.typeName(iface.Name)) 394 | } 395 | 396 | fmt.Fprintln(b, "Requests: []wlproto.Request{") 397 | for _, req := range iface.Requests { 398 | if req.Since == "" { 399 | req.Since = "1" 400 | } 401 | fmt.Fprintln(b, "{") 402 | fmt.Fprintf(b, "Name: %q,\n", req.Name) 403 | fmt.Fprintf(b, "Type: %q,\n", req.Type) 404 | fmt.Fprintf(b, "Since: %s,\n", req.Since) 405 | 406 | if b.ServerMode { 407 | fmt.Fprintf(b, "Method: reflect.ValueOf(%s.%s),\n", b.eventsTypeName(iface), exportedGoIdentifier(req.Name)) 408 | } 409 | 410 | fmt.Fprintln(b, "Args: []wlproto.Arg{") 411 | for _, arg := range req.Args { 412 | fmt.Fprintf(b, "%s,\n", b.wlprotoArg(arg, iface)) 413 | } 414 | fmt.Fprintln(b, "},") 415 | 416 | fmt.Fprintln(b, "},") 417 | } 418 | fmt.Fprintln(b, "},") 419 | 420 | fmt.Fprintln(b, "Events: []wlproto.Event{") 421 | for _, ev := range iface.Events { 422 | if ev.Since == "" { 423 | ev.Since = "1" 424 | } 425 | fmt.Fprintln(b, "{") 426 | fmt.Fprintf(b, "Name: %q,\n", ev.Name) 427 | fmt.Fprintf(b, "Since: %s,\n", ev.Since) 428 | 429 | fmt.Fprintln(b, "Args: []wlproto.Arg{") 430 | for _, arg := range ev.Args { 431 | fmt.Fprintf(b, "%s,\n", b.wlprotoArg(arg, iface)) 432 | } 433 | fmt.Fprintln(b, "},") 434 | 435 | fmt.Fprintln(b, "},") 436 | } 437 | fmt.Fprintln(b, "},") 438 | 439 | fmt.Fprintln(b, "}") 440 | } 441 | 442 | printInterfaceEventsType := func() { 443 | if b.ServerMode { 444 | fmt.Fprintf(b, "type %s interface {\n", b.eventsTypeName(iface)) 445 | for _, req := range iface.Requests { 446 | fmt.Fprintf(b, "%s(obj %s,", exportedGoIdentifier(req.Name), b.typeName(iface.Name)) 447 | 448 | var rets []string 449 | for _, arg := range req.Args { 450 | if arg.Type == "new_id" && arg.Interface == "" { 451 | name := goIdentifier(arg.Name) 452 | fmt.Fprintf(b, "%sName string, %sVersion uint32, %s wlshared.ObjectID,", name, name, name) 453 | } else { 454 | fmt.Fprintf(b, "%s %s,", goIdentifier(arg.Name), b.goTypeFromWlType(arg, iface)) 455 | } 456 | if arg.Type == "new_id" { 457 | if arg.Interface == "" { 458 | rets = append(rets, "wlserver.ResourceImplementation") 459 | } else { 460 | rets = append(rets, b.goTypeFromWlType(arg, iface)+"Implementation") 461 | } 462 | } 463 | } 464 | fmt.Fprintf(b, ") (%s)\n", strings.Join(rets, ",")) 465 | } 466 | fmt.Fprint(b, "}\n\n") 467 | 468 | fmt.Fprintf(b, "func Add%sGlobal(dsp *wlserver.Display, version int, bind func(res %s) %s) {\n", 469 | b.typeName(iface.Name), b.typeName(iface.Name), b.eventsTypeName(iface)) 470 | fmt.Fprintf(b, "dsp.AddGlobal(%s, version, func(res wlserver.Object) wlserver.ResourceImplementation { return bind(res.(%s))})\n", b.wlprotoInterfaceName(iface), b.typeName(iface.Name)) 471 | fmt.Fprint(b, "}\n\n") 472 | } else { 473 | fmt.Fprintf(b, "type %s struct {\n", b.eventsTypeName(iface)) 474 | for _, ev := range iface.Events { 475 | fmt.Fprintf(b, "%s func(obj *%s,", exportedGoIdentifier(ev.Name), b.typeName(iface.Name)) 476 | for _, arg := range ev.Args { 477 | fmt.Fprintf(b, "%s %s,", goIdentifier(arg.Name), b.goTypeFromWlType(arg, iface)) 478 | } 479 | fmt.Fprintln(b, ")") 480 | } 481 | fmt.Fprint(b, "}\n\n") 482 | } 483 | } 484 | 485 | printInterfaceType := func() { 486 | fmt.Fprintln(b, docString(iface.Description)) 487 | if b.ServerMode { 488 | fmt.Fprintf(b, "type %s struct { wlserver.Resource }\n\n", b.typeName(iface.Name)) 489 | fmt.Fprintf(b, "func (%s) Interface() *wlproto.Interface { return %s }\n\n", b.typeName(iface.Name), b.wlprotoInterfaceName(iface)) 490 | } else { 491 | fmt.Fprintf(b, "type %s struct { wlclient.Proxy }\n\n", b.typeName(iface.Name)) 492 | fmt.Fprintf(b, "func (*%s) Interface() *wlproto.Interface { return %s }\n\n", b.typeName(iface.Name), b.wlprotoInterfaceName(iface)) 493 | } 494 | 495 | // basic methods 496 | 497 | if !b.ServerMode { 498 | fmt.Fprintf(b, "func (obj *%[1]s) WithQueue(queue *wlclient.EventQueue) *%[1]s {\n", b.typeName(iface.Name)) 499 | fmt.Fprintf(b, "wobj := &%s{}\n", b.typeName(iface.Name)) 500 | fmt.Fprintf(b, "obj.Conn().NewWrapper(obj, wobj, queue)\n") 501 | fmt.Fprintf(b, "return wobj\n") 502 | fmt.Fprintf(b, "}\n\n") 503 | } 504 | 505 | printInterfaceEventsType() 506 | if !b.ServerMode { 507 | fmt.Fprintf(b, "func (obj *%s) AddListener(listeners %s) {\n", b.typeName(iface.Name), b.eventsTypeName(iface)) 508 | fmt.Fprint(b, "obj.Proxy.SetListeners(") 509 | for _, ev := range iface.Events { 510 | fmt.Fprintf(b, "listeners.%s,", exportedGoIdentifier(ev.Name)) 511 | } 512 | fmt.Fprintln(b, ")") 513 | fmt.Fprint(b, "}\n\n") 514 | } 515 | } 516 | 517 | printMethod := func(ireq int, desc elDescription, name string, args []elArg, typ string) { 518 | var ctor elArg 519 | fmt.Fprintln(b, docString(desc)) 520 | if b.ServerMode { 521 | fmt.Fprintf(b, "func (obj %s) %s(", b.typeName(iface.Name), exportedGoIdentifier(name)) 522 | } else { 523 | fmt.Fprintf(b, "func (obj *%s) %s(", b.typeName(iface.Name), exportedGoIdentifier(name)) 524 | } 525 | for _, arg := range args { 526 | if arg.Type == "new_id" { 527 | ctor = arg 528 | if arg.Interface == "" { 529 | fmt.Fprintf(b, "%s %s, version uint32,", goIdentifier(arg.Name), b.goTypeFromWlType(arg, iface)) 530 | } else if b.ServerMode { 531 | fmt.Fprintf(b, "%s %s,", goIdentifier(arg.Name), b.goTypeFromWlType(arg, iface)) 532 | } 533 | } else { 534 | fmt.Fprintf(b, "%s %s,", goIdentifier(arg.Name), b.goTypeFromWlType(arg, iface)) 535 | } 536 | } 537 | fmt.Fprint(b, ")") 538 | 539 | if !b.ServerMode { 540 | if ctor.Interface != "" { 541 | fmt.Fprintf(b, "*%s", b.typeName(ctor.Interface)) 542 | } 543 | } 544 | 545 | fmt.Fprintln(b, "{") 546 | if b.ServerMode { 547 | fmt.Fprintf(b, "obj.Conn().SendEvent(obj, %d, ", ireq) 548 | for _, arg := range args { 549 | if arg.Type == "new_id" { 550 | if ctor.Interface == "" { 551 | fmt.Fprintf(b, "%[1]s.Interface().Name, version, %[1]s,", goIdentifier(ctor.Name)) 552 | } else { 553 | fmt.Fprintf(b, "%s,", goIdentifier(arg.Name)) 554 | } 555 | } else { 556 | fmt.Fprintf(b, "%s,", goIdentifier(arg.Name)) 557 | } 558 | } 559 | fmt.Fprintln(b, ")") 560 | } else { 561 | if ctor.Interface != "" { 562 | fmt.Fprintf(b, "_ret := &%s{}\n", b.typeName(ctor.Interface)) 563 | fmt.Fprintln(b, "obj.Conn().NewProxy(0, _ret, obj.Queue())") 564 | } 565 | 566 | if typ == "destructor" { 567 | fmt.Fprintf(b, "obj.Conn().SendDestructor(obj, %d, ", ireq) 568 | } else { 569 | fmt.Fprintf(b, "obj.Conn().SendRequest(obj, %d, ", ireq) 570 | } 571 | for _, arg := range args { 572 | if arg.Type == "new_id" { 573 | if ctor.Interface == "" { 574 | fmt.Fprintf(b, "%[1]s.Interface().Name, version, %[1]s,", goIdentifier(ctor.Name)) 575 | } else { 576 | fmt.Fprint(b, "_ret,") 577 | } 578 | } else { 579 | fmt.Fprintf(b, "%s,", goIdentifier(arg.Name)) 580 | } 581 | } 582 | fmt.Fprintln(b, ")") 583 | 584 | if ctor.Interface != "" { 585 | fmt.Fprintln(b, "return _ret") 586 | } 587 | } 588 | 589 | fmt.Fprint(b, "}\n\n") 590 | } 591 | 592 | printRequest := func(ireq int, req elRequest) { 593 | printMethod(ireq, req.Description, req.Name, req.Args, req.Type) 594 | } 595 | 596 | printEvent := func(ireq int, ev elEvent) { 597 | printMethod(ireq, ev.Description, ev.Name, ev.Args, "") 598 | } 599 | 600 | printEnums() 601 | printInterfaceVar() 602 | printInterfaceType() 603 | 604 | if b.ServerMode { 605 | for iev, ev := range iface.Events { 606 | printEvent(iev, ev) 607 | } 608 | } else { 609 | hasDestroy := false 610 | for ireq, req := range iface.Requests { 611 | if req.Name == "destroy" { 612 | hasDestroy = true 613 | } 614 | printRequest(ireq, req) 615 | } 616 | if !hasDestroy { 617 | fmt.Fprintf(b, "func (obj *%s) Destroy() { obj.Conn().Destroy(obj) }\n\n", b.typeName(iface.Name)) 618 | } 619 | } 620 | } 621 | 622 | for _, iface := range b.Spec.Interfaces { 623 | b.Interfaces[iface.Name] = nil 624 | } 625 | 626 | printMaps() 627 | for _, iface := range b.Spec.Interfaces { 628 | printInterface(iface) 629 | } 630 | 631 | if b.Spec.Name == "wayland" && !b.ServerMode { 632 | fmt.Fprintln(b, "func GetDisplay(conn *wlclient.Conn) *Display { _ret := &Display{}; conn.NewProxy(1, _ret, nil); return _ret }") 633 | } 634 | 635 | printPackage() 636 | printImports() 637 | 638 | out.Write(b.Code.Bytes()) 639 | } 640 | 641 | type imports []string 642 | 643 | func (imps *imports) String() string { 644 | return strings.Join([]string(*imps), ",") 645 | } 646 | 647 | func (imps *imports) Set(s string) error { 648 | *imps = append(*imps, s) 649 | return nil 650 | } 651 | 652 | func Build(file string, imports []string, prefix string, out io.Writer, serverMode bool) { 653 | b := &Builder{ 654 | Imports: map[string]bool{ 655 | "reflect": true, 656 | "honnef.co/go/wayland/wlshared": true, 657 | "honnef.co/go/wayland/wlproto": true, 658 | }, 659 | Prefix: prefix, 660 | ServerMode: serverMode, 661 | } 662 | if b.ServerMode { 663 | b.Imports["honnef.co/go/wayland/wlserver"] = true 664 | } else { 665 | b.Imports["honnef.co/go/wayland/wlclient"] = true 666 | } 667 | 668 | if len(imports) > 0 { 669 | b.Interfaces = loadInterfaces(imports) 670 | } else { 671 | b.Interfaces = map[string]*Package{} 672 | } 673 | 674 | f, err := os.OpenFile(flag.Args()[0], os.O_RDONLY, 0) 675 | if err != nil { 676 | log.Fatal(err) 677 | } 678 | 679 | dec := xml.NewDecoder(f) 680 | if err := dec.Decode(&b.Spec); err != nil { 681 | log.Fatal(err) 682 | } 683 | f.Close() 684 | 685 | var buf bytes.Buffer 686 | b.printSpecs(&buf) 687 | d, err := format.Source(buf.Bytes()) 688 | if err != nil { 689 | out.Write(buf.Bytes()) 690 | log.Fatal(err) 691 | } 692 | out.Write(d) 693 | } 694 | 695 | func main() { 696 | var imps imports 697 | flag.Var(&imps, "i", "XXX") 698 | prefix := flag.String("prefix", "", "XXX") 699 | mode := flag.String("mode", "client", "client or server") 700 | flag.Parse() 701 | 702 | Build(flag.Args()[0], imps, *prefix, os.Stdout, *mode == "server") 703 | } 704 | -------------------------------------------------------------------------------- /wlserver/protocols/xdg-shell/xdg-shell.go: -------------------------------------------------------------------------------- 1 | // Code generated by wayland-scanner; DO NOT EDIT. 2 | 3 | // Package xdgShell contains generated definitions of the xdg_shell Wayland protocol. 4 | package xdgShell 5 | 6 | import ( 7 | "honnef.co/go/wayland/wlclient/protocols/wayland" 8 | "honnef.co/go/wayland/wlproto" 9 | "honnef.co/go/wayland/wlserver" 10 | "honnef.co/go/wayland/wlshared" 11 | "reflect" 12 | ) 13 | 14 | var _ wlshared.Fixed 15 | 16 | var interfaceNames = map[string]string{ 17 | "xdg_wm_base": "WmBase", 18 | "xdg_positioner": "Positioner", 19 | "xdg_surface": "Surface", 20 | "xdg_toplevel": "Toplevel", 21 | "xdg_popup": "Popup", 22 | } 23 | 24 | var Interfaces = map[string]*wlproto.Interface{ 25 | "xdg_wm_base": WmBaseInterface, 26 | "xdg_positioner": PositionerInterface, 27 | "xdg_surface": SurfaceInterface, 28 | "xdg_toplevel": ToplevelInterface, 29 | "xdg_popup": PopupInterface, 30 | } 31 | 32 | var Requests = map[string]*wlproto.Request{ 33 | "xdg_wm_base_destroy": &WmBaseInterface.Requests[0], 34 | "xdg_wm_base_create_positioner": &WmBaseInterface.Requests[1], 35 | "xdg_wm_base_get_xdg_surface": &WmBaseInterface.Requests[2], 36 | "xdg_wm_base_pong": &WmBaseInterface.Requests[3], 37 | "xdg_positioner_destroy": &PositionerInterface.Requests[0], 38 | "xdg_positioner_set_size": &PositionerInterface.Requests[1], 39 | "xdg_positioner_set_anchor_rect": &PositionerInterface.Requests[2], 40 | "xdg_positioner_set_anchor": &PositionerInterface.Requests[3], 41 | "xdg_positioner_set_gravity": &PositionerInterface.Requests[4], 42 | "xdg_positioner_set_constraint_adjustment": &PositionerInterface.Requests[5], 43 | "xdg_positioner_set_offset": &PositionerInterface.Requests[6], 44 | "xdg_positioner_set_reactive": &PositionerInterface.Requests[7], 45 | "xdg_positioner_set_parent_size": &PositionerInterface.Requests[8], 46 | "xdg_positioner_set_parent_configure": &PositionerInterface.Requests[9], 47 | "xdg_surface_destroy": &SurfaceInterface.Requests[0], 48 | "xdg_surface_get_toplevel": &SurfaceInterface.Requests[1], 49 | "xdg_surface_get_popup": &SurfaceInterface.Requests[2], 50 | "xdg_surface_set_window_geometry": &SurfaceInterface.Requests[3], 51 | "xdg_surface_ack_configure": &SurfaceInterface.Requests[4], 52 | "xdg_toplevel_destroy": &ToplevelInterface.Requests[0], 53 | "xdg_toplevel_set_parent": &ToplevelInterface.Requests[1], 54 | "xdg_toplevel_set_title": &ToplevelInterface.Requests[2], 55 | "xdg_toplevel_set_app_id": &ToplevelInterface.Requests[3], 56 | "xdg_toplevel_show_window_menu": &ToplevelInterface.Requests[4], 57 | "xdg_toplevel_move": &ToplevelInterface.Requests[5], 58 | "xdg_toplevel_resize": &ToplevelInterface.Requests[6], 59 | "xdg_toplevel_set_max_size": &ToplevelInterface.Requests[7], 60 | "xdg_toplevel_set_min_size": &ToplevelInterface.Requests[8], 61 | "xdg_toplevel_set_maximized": &ToplevelInterface.Requests[9], 62 | "xdg_toplevel_unset_maximized": &ToplevelInterface.Requests[10], 63 | "xdg_toplevel_set_fullscreen": &ToplevelInterface.Requests[11], 64 | "xdg_toplevel_unset_fullscreen": &ToplevelInterface.Requests[12], 65 | "xdg_toplevel_set_minimized": &ToplevelInterface.Requests[13], 66 | "xdg_popup_destroy": &PopupInterface.Requests[0], 67 | "xdg_popup_grab": &PopupInterface.Requests[1], 68 | "xdg_popup_reposition": &PopupInterface.Requests[2], 69 | } 70 | 71 | var Events = map[string]*wlproto.Event{ 72 | "xdg_wm_base_ping": &WmBaseInterface.Events[0], 73 | "xdg_surface_configure": &SurfaceInterface.Events[0], 74 | "xdg_toplevel_configure": &ToplevelInterface.Events[0], 75 | "xdg_toplevel_close": &ToplevelInterface.Events[1], 76 | "xdg_toplevel_configure_bounds": &ToplevelInterface.Events[2], 77 | "xdg_popup_configure": &PopupInterface.Events[0], 78 | "xdg_popup_popup_done": &PopupInterface.Events[1], 79 | "xdg_popup_repositioned": &PopupInterface.Events[2], 80 | } 81 | 82 | type WmBaseError uint32 83 | 84 | const ( 85 | // given wl_surface has another role 86 | WmBaseErrorRole WmBaseError = 0 87 | // xdg_wm_base was destroyed before children 88 | WmBaseErrorDefunctSurfaces WmBaseError = 1 89 | // the client tried to map or destroy a non-topmost popup 90 | WmBaseErrorNotTheTopmostPopup WmBaseError = 2 91 | // the client specified an invalid popup parent surface 92 | WmBaseErrorInvalidPopupParent WmBaseError = 3 93 | // the client provided an invalid surface state 94 | WmBaseErrorInvalidSurfaceState WmBaseError = 4 95 | // the client provided an invalid positioner 96 | WmBaseErrorInvalidPositioner WmBaseError = 5 97 | ) 98 | 99 | var WmBaseInterface = &wlproto.Interface{ 100 | Name: "xdg_wm_base", 101 | Version: 4, 102 | Type: reflect.TypeOf(WmBase{}), 103 | Requests: []wlproto.Request{ 104 | { 105 | Name: "destroy", 106 | Type: "destructor", 107 | Since: 1, 108 | Method: reflect.ValueOf(WmBaseImplementation.Destroy), 109 | Args: []wlproto.Arg{}, 110 | }, 111 | { 112 | Name: "create_positioner", 113 | Type: "", 114 | Since: 1, 115 | Method: reflect.ValueOf(WmBaseImplementation.CreatePositioner), 116 | Args: []wlproto.Arg{ 117 | {Type: wlproto.ArgTypeNewID, Aux: reflect.TypeOf(Positioner{})}, 118 | }, 119 | }, 120 | { 121 | Name: "get_xdg_surface", 122 | Type: "", 123 | Since: 1, 124 | Method: reflect.ValueOf(WmBaseImplementation.GetXdgSurface), 125 | Args: []wlproto.Arg{ 126 | {Type: wlproto.ArgTypeNewID, Aux: reflect.TypeOf(Surface{})}, 127 | {Type: wlproto.ArgTypeObject, Aux: reflect.TypeOf(wayland.Surface{})}, 128 | }, 129 | }, 130 | { 131 | Name: "pong", 132 | Type: "", 133 | Since: 1, 134 | Method: reflect.ValueOf(WmBaseImplementation.Pong), 135 | Args: []wlproto.Arg{ 136 | {Type: wlproto.ArgTypeUint}, 137 | }, 138 | }, 139 | }, 140 | Events: []wlproto.Event{ 141 | { 142 | Name: "ping", 143 | Since: 1, 144 | Args: []wlproto.Arg{ 145 | {Type: wlproto.ArgTypeUint}, 146 | }, 147 | }, 148 | }, 149 | } 150 | 151 | // The xdg_wm_base interface is exposed as a global object enabling clients 152 | // to turn their wl_surfaces into windows in a desktop environment. It 153 | // defines the basic functionality needed for clients and the compositor to 154 | // create windows that can be dragged, resized, maximized, etc, as well as 155 | // creating transient windows such as popup menus. 156 | type WmBase struct{ wlserver.Resource } 157 | 158 | func (WmBase) Interface() *wlproto.Interface { return WmBaseInterface } 159 | 160 | type WmBaseImplementation interface { 161 | Destroy(obj WmBase) 162 | CreatePositioner(obj WmBase, id Positioner) PositionerImplementation 163 | GetXdgSurface(obj WmBase, id Surface, surface wayland.Surface) SurfaceImplementation 164 | Pong(obj WmBase, serial uint32) 165 | } 166 | 167 | func AddWmBaseGlobal(dsp *wlserver.Display, version int, bind func(res WmBase) WmBaseImplementation) { 168 | dsp.AddGlobal(WmBaseInterface, version, func(res wlserver.Object) wlserver.ResourceImplementation { return bind(res.(WmBase)) }) 169 | } 170 | 171 | // The ping event asks the client if it's still alive. Pass the 172 | // serial specified in the event back to the compositor by sending 173 | // a "pong" request back with the specified serial. See xdg_wm_base.pong. 174 | // 175 | // Compositors can use this to determine if the client is still 176 | // alive. It's unspecified what will happen if the client doesn't 177 | // respond to the ping request, or in what timeframe. Clients should 178 | // try to respond in a reasonable amount of time. 179 | // 180 | // A compositor is free to ping in any way it wants, but a client must 181 | // always respond to any xdg_wm_base object it created. 182 | func (obj WmBase) Ping(serial uint32) { 183 | obj.Conn().SendEvent(obj, 0, serial) 184 | } 185 | 186 | type PositionerError uint32 187 | 188 | const ( 189 | // invalid input provided 190 | PositionerErrorInvalidInput PositionerError = 0 191 | ) 192 | 193 | type PositionerAnchor uint32 194 | 195 | const ( 196 | PositionerAnchorNone PositionerAnchor = 0 197 | PositionerAnchorTop PositionerAnchor = 1 198 | PositionerAnchorBottom PositionerAnchor = 2 199 | PositionerAnchorLeft PositionerAnchor = 3 200 | PositionerAnchorRight PositionerAnchor = 4 201 | PositionerAnchorTopLeft PositionerAnchor = 5 202 | PositionerAnchorBottomLeft PositionerAnchor = 6 203 | PositionerAnchorTopRight PositionerAnchor = 7 204 | PositionerAnchorBottomRight PositionerAnchor = 8 205 | ) 206 | 207 | type PositionerGravity uint32 208 | 209 | const ( 210 | PositionerGravityNone PositionerGravity = 0 211 | PositionerGravityTop PositionerGravity = 1 212 | PositionerGravityBottom PositionerGravity = 2 213 | PositionerGravityLeft PositionerGravity = 3 214 | PositionerGravityRight PositionerGravity = 4 215 | PositionerGravityTopLeft PositionerGravity = 5 216 | PositionerGravityBottomLeft PositionerGravity = 6 217 | PositionerGravityTopRight PositionerGravity = 7 218 | PositionerGravityBottomRight PositionerGravity = 8 219 | ) 220 | 221 | // The constraint adjustment value define ways the compositor will adjust 222 | // the position of the surface, if the unadjusted position would result 223 | // in the surface being partly constrained. 224 | // 225 | // Whether a surface is considered 'constrained' is left to the compositor 226 | // to determine. For example, the surface may be partly outside the 227 | // compositor's defined 'work area', thus necessitating the child surface's 228 | // position be adjusted until it is entirely inside the work area. 229 | // 230 | // The adjustments can be combined, according to a defined precedence: 1) 231 | // Flip, 2) Slide, 3) Resize. 232 | type PositionerConstraintAdjustment uint32 233 | 234 | const ( 235 | // Don't alter the surface position even if it is constrained on some 236 | // axis, for example partially outside the edge of an output. 237 | PositionerConstraintAdjustmentNone PositionerConstraintAdjustment = 0 238 | // Slide the surface along the x axis until it is no longer constrained. 239 | // 240 | // First try to slide towards the direction of the gravity on the x axis 241 | // until either the edge in the opposite direction of the gravity is 242 | // unconstrained or the edge in the direction of the gravity is 243 | // constrained. 244 | // 245 | // Then try to slide towards the opposite direction of the gravity on the 246 | // x axis until either the edge in the direction of the gravity is 247 | // unconstrained or the edge in the opposite direction of the gravity is 248 | // constrained. 249 | PositionerConstraintAdjustmentSlideX PositionerConstraintAdjustment = 1 250 | // Slide the surface along the y axis until it is no longer constrained. 251 | // 252 | // First try to slide towards the direction of the gravity on the y axis 253 | // until either the edge in the opposite direction of the gravity is 254 | // unconstrained or the edge in the direction of the gravity is 255 | // constrained. 256 | // 257 | // Then try to slide towards the opposite direction of the gravity on the 258 | // y axis until either the edge in the direction of the gravity is 259 | // unconstrained or the edge in the opposite direction of the gravity is 260 | // constrained. 261 | PositionerConstraintAdjustmentSlideY PositionerConstraintAdjustment = 2 262 | // Invert the anchor and gravity on the x axis if the surface is 263 | // constrained on the x axis. For example, if the left edge of the 264 | // surface is constrained, the gravity is 'left' and the anchor is 265 | // 'left', change the gravity to 'right' and the anchor to 'right'. 266 | // 267 | // If the adjusted position also ends up being constrained, the resulting 268 | // position of the flip_x adjustment will be the one before the 269 | // adjustment. 270 | PositionerConstraintAdjustmentFlipX PositionerConstraintAdjustment = 4 271 | // Invert the anchor and gravity on the y axis if the surface is 272 | // constrained on the y axis. For example, if the bottom edge of the 273 | // surface is constrained, the gravity is 'bottom' and the anchor is 274 | // 'bottom', change the gravity to 'top' and the anchor to 'top'. 275 | // 276 | // The adjusted position is calculated given the original anchor 277 | // rectangle and offset, but with the new flipped anchor and gravity 278 | // values. 279 | // 280 | // If the adjusted position also ends up being constrained, the resulting 281 | // position of the flip_y adjustment will be the one before the 282 | // adjustment. 283 | PositionerConstraintAdjustmentFlipY PositionerConstraintAdjustment = 8 284 | // Resize the surface horizontally so that it is completely 285 | // unconstrained. 286 | PositionerConstraintAdjustmentResizeX PositionerConstraintAdjustment = 16 287 | // Resize the surface vertically so that it is completely unconstrained. 288 | PositionerConstraintAdjustmentResizeY PositionerConstraintAdjustment = 32 289 | ) 290 | 291 | var PositionerInterface = &wlproto.Interface{ 292 | Name: "xdg_positioner", 293 | Version: 4, 294 | Type: reflect.TypeOf(Positioner{}), 295 | Requests: []wlproto.Request{ 296 | { 297 | Name: "destroy", 298 | Type: "destructor", 299 | Since: 1, 300 | Method: reflect.ValueOf(PositionerImplementation.Destroy), 301 | Args: []wlproto.Arg{}, 302 | }, 303 | { 304 | Name: "set_size", 305 | Type: "", 306 | Since: 1, 307 | Method: reflect.ValueOf(PositionerImplementation.SetSize), 308 | Args: []wlproto.Arg{ 309 | {Type: wlproto.ArgTypeInt}, 310 | {Type: wlproto.ArgTypeInt}, 311 | }, 312 | }, 313 | { 314 | Name: "set_anchor_rect", 315 | Type: "", 316 | Since: 1, 317 | Method: reflect.ValueOf(PositionerImplementation.SetAnchorRect), 318 | Args: []wlproto.Arg{ 319 | {Type: wlproto.ArgTypeInt}, 320 | {Type: wlproto.ArgTypeInt}, 321 | {Type: wlproto.ArgTypeInt}, 322 | {Type: wlproto.ArgTypeInt}, 323 | }, 324 | }, 325 | { 326 | Name: "set_anchor", 327 | Type: "", 328 | Since: 1, 329 | Method: reflect.ValueOf(PositionerImplementation.SetAnchor), 330 | Args: []wlproto.Arg{ 331 | {Type: wlproto.ArgTypeUint, Aux: reflect.TypeOf(PositionerAnchor(0))}, 332 | }, 333 | }, 334 | { 335 | Name: "set_gravity", 336 | Type: "", 337 | Since: 1, 338 | Method: reflect.ValueOf(PositionerImplementation.SetGravity), 339 | Args: []wlproto.Arg{ 340 | {Type: wlproto.ArgTypeUint, Aux: reflect.TypeOf(PositionerGravity(0))}, 341 | }, 342 | }, 343 | { 344 | Name: "set_constraint_adjustment", 345 | Type: "", 346 | Since: 1, 347 | Method: reflect.ValueOf(PositionerImplementation.SetConstraintAdjustment), 348 | Args: []wlproto.Arg{ 349 | {Type: wlproto.ArgTypeUint}, 350 | }, 351 | }, 352 | { 353 | Name: "set_offset", 354 | Type: "", 355 | Since: 1, 356 | Method: reflect.ValueOf(PositionerImplementation.SetOffset), 357 | Args: []wlproto.Arg{ 358 | {Type: wlproto.ArgTypeInt}, 359 | {Type: wlproto.ArgTypeInt}, 360 | }, 361 | }, 362 | { 363 | Name: "set_reactive", 364 | Type: "", 365 | Since: 3, 366 | Method: reflect.ValueOf(PositionerImplementation.SetReactive), 367 | Args: []wlproto.Arg{}, 368 | }, 369 | { 370 | Name: "set_parent_size", 371 | Type: "", 372 | Since: 3, 373 | Method: reflect.ValueOf(PositionerImplementation.SetParentSize), 374 | Args: []wlproto.Arg{ 375 | {Type: wlproto.ArgTypeInt}, 376 | {Type: wlproto.ArgTypeInt}, 377 | }, 378 | }, 379 | { 380 | Name: "set_parent_configure", 381 | Type: "", 382 | Since: 3, 383 | Method: reflect.ValueOf(PositionerImplementation.SetParentConfigure), 384 | Args: []wlproto.Arg{ 385 | {Type: wlproto.ArgTypeUint}, 386 | }, 387 | }, 388 | }, 389 | Events: []wlproto.Event{}, 390 | } 391 | 392 | // The xdg_positioner provides a collection of rules for the placement of a 393 | // child surface relative to a parent surface. Rules can be defined to ensure 394 | // the child surface remains within the visible area's borders, and to 395 | // specify how the child surface changes its position, such as sliding along 396 | // an axis, or flipping around a rectangle. These positioner-created rules are 397 | // constrained by the requirement that a child surface must intersect with or 398 | // be at least partially adjacent to its parent surface. 399 | // 400 | // See the various requests for details about possible rules. 401 | // 402 | // At the time of the request, the compositor makes a copy of the rules 403 | // specified by the xdg_positioner. Thus, after the request is complete the 404 | // xdg_positioner object can be destroyed or reused; further changes to the 405 | // object will have no effect on previous usages. 406 | // 407 | // For an xdg_positioner object to be considered complete, it must have a 408 | // non-zero size set by set_size, and a non-zero anchor rectangle set by 409 | // set_anchor_rect. Passing an incomplete xdg_positioner object when 410 | // positioning a surface raises an error. 411 | type Positioner struct{ wlserver.Resource } 412 | 413 | func (Positioner) Interface() *wlproto.Interface { return PositionerInterface } 414 | 415 | type PositionerImplementation interface { 416 | Destroy(obj Positioner) 417 | SetSize(obj Positioner, width int32, height int32) 418 | SetAnchorRect(obj Positioner, x int32, y int32, width int32, height int32) 419 | SetAnchor(obj Positioner, anchor PositionerAnchor) 420 | SetGravity(obj Positioner, gravity PositionerGravity) 421 | SetConstraintAdjustment(obj Positioner, constraintAdjustment uint32) 422 | SetOffset(obj Positioner, x int32, y int32) 423 | SetReactive(obj Positioner) 424 | SetParentSize(obj Positioner, parentWidth int32, parentHeight int32) 425 | SetParentConfigure(obj Positioner, serial uint32) 426 | } 427 | 428 | func AddPositionerGlobal(dsp *wlserver.Display, version int, bind func(res Positioner) PositionerImplementation) { 429 | dsp.AddGlobal(PositionerInterface, version, func(res wlserver.Object) wlserver.ResourceImplementation { return bind(res.(Positioner)) }) 430 | } 431 | 432 | type SurfaceError uint32 433 | 434 | const ( 435 | SurfaceErrorNotConstructed SurfaceError = 1 436 | SurfaceErrorAlreadyConstructed SurfaceError = 2 437 | SurfaceErrorUnconfiguredBuffer SurfaceError = 3 438 | ) 439 | 440 | var SurfaceInterface = &wlproto.Interface{ 441 | Name: "xdg_surface", 442 | Version: 4, 443 | Type: reflect.TypeOf(Surface{}), 444 | Requests: []wlproto.Request{ 445 | { 446 | Name: "destroy", 447 | Type: "destructor", 448 | Since: 1, 449 | Method: reflect.ValueOf(SurfaceImplementation.Destroy), 450 | Args: []wlproto.Arg{}, 451 | }, 452 | { 453 | Name: "get_toplevel", 454 | Type: "", 455 | Since: 1, 456 | Method: reflect.ValueOf(SurfaceImplementation.GetToplevel), 457 | Args: []wlproto.Arg{ 458 | {Type: wlproto.ArgTypeNewID, Aux: reflect.TypeOf(Toplevel{})}, 459 | }, 460 | }, 461 | { 462 | Name: "get_popup", 463 | Type: "", 464 | Since: 1, 465 | Method: reflect.ValueOf(SurfaceImplementation.GetPopup), 466 | Args: []wlproto.Arg{ 467 | {Type: wlproto.ArgTypeNewID, Aux: reflect.TypeOf(Popup{})}, 468 | {Type: wlproto.ArgTypeObject, Aux: reflect.TypeOf(Surface{})}, 469 | {Type: wlproto.ArgTypeObject, Aux: reflect.TypeOf(Positioner{})}, 470 | }, 471 | }, 472 | { 473 | Name: "set_window_geometry", 474 | Type: "", 475 | Since: 1, 476 | Method: reflect.ValueOf(SurfaceImplementation.SetWindowGeometry), 477 | Args: []wlproto.Arg{ 478 | {Type: wlproto.ArgTypeInt}, 479 | {Type: wlproto.ArgTypeInt}, 480 | {Type: wlproto.ArgTypeInt}, 481 | {Type: wlproto.ArgTypeInt}, 482 | }, 483 | }, 484 | { 485 | Name: "ack_configure", 486 | Type: "", 487 | Since: 1, 488 | Method: reflect.ValueOf(SurfaceImplementation.AckConfigure), 489 | Args: []wlproto.Arg{ 490 | {Type: wlproto.ArgTypeUint}, 491 | }, 492 | }, 493 | }, 494 | Events: []wlproto.Event{ 495 | { 496 | Name: "configure", 497 | Since: 1, 498 | Args: []wlproto.Arg{ 499 | {Type: wlproto.ArgTypeUint}, 500 | }, 501 | }, 502 | }, 503 | } 504 | 505 | // An interface that may be implemented by a wl_surface, for 506 | // implementations that provide a desktop-style user interface. 507 | // 508 | // It provides a base set of functionality required to construct user 509 | // interface elements requiring management by the compositor, such as 510 | // toplevel windows, menus, etc. The types of functionality are split into 511 | // xdg_surface roles. 512 | // 513 | // Creating an xdg_surface does not set the role for a wl_surface. In order 514 | // to map an xdg_surface, the client must create a role-specific object 515 | // using, e.g., get_toplevel, get_popup. The wl_surface for any given 516 | // xdg_surface can have at most one role, and may not be assigned any role 517 | // not based on xdg_surface. 518 | // 519 | // A role must be assigned before any other requests are made to the 520 | // xdg_surface object. 521 | // 522 | // The client must call wl_surface.commit on the corresponding wl_surface 523 | // for the xdg_surface state to take effect. 524 | // 525 | // Creating an xdg_surface from a wl_surface which has a buffer attached or 526 | // committed is a client error, and any attempts by a client to attach or 527 | // manipulate a buffer prior to the first xdg_surface.configure call must 528 | // also be treated as errors. 529 | // 530 | // After creating a role-specific object and setting it up, the client must 531 | // perform an initial commit without any buffer attached. The compositor 532 | // will reply with an xdg_surface.configure event. The client must 533 | // acknowledge it and is then allowed to attach a buffer to map the surface. 534 | // 535 | // Mapping an xdg_surface-based role surface is defined as making it 536 | // possible for the surface to be shown by the compositor. Note that 537 | // a mapped surface is not guaranteed to be visible once it is mapped. 538 | // 539 | // For an xdg_surface to be mapped by the compositor, the following 540 | // conditions must be met: 541 | // (1) the client has assigned an xdg_surface-based role to the surface 542 | // (2) the client has set and committed the xdg_surface state and the 543 | // role-dependent state to the surface 544 | // (3) the client has committed a buffer to the surface 545 | // 546 | // A newly-unmapped surface is considered to have met condition (1) out 547 | // of the 3 required conditions for mapping a surface if its role surface 548 | // has not been destroyed, i.e. the client must perform the initial commit 549 | // again before attaching a buffer. 550 | type Surface struct{ wlserver.Resource } 551 | 552 | func (Surface) Interface() *wlproto.Interface { return SurfaceInterface } 553 | 554 | type SurfaceImplementation interface { 555 | Destroy(obj Surface) 556 | GetToplevel(obj Surface, id Toplevel) ToplevelImplementation 557 | GetPopup(obj Surface, id Popup, parent Surface, positioner Positioner) PopupImplementation 558 | SetWindowGeometry(obj Surface, x int32, y int32, width int32, height int32) 559 | AckConfigure(obj Surface, serial uint32) 560 | } 561 | 562 | func AddSurfaceGlobal(dsp *wlserver.Display, version int, bind func(res Surface) SurfaceImplementation) { 563 | dsp.AddGlobal(SurfaceInterface, version, func(res wlserver.Object) wlserver.ResourceImplementation { return bind(res.(Surface)) }) 564 | } 565 | 566 | // The configure event marks the end of a configure sequence. A configure 567 | // sequence is a set of one or more events configuring the state of the 568 | // xdg_surface, including the final xdg_surface.configure event. 569 | // 570 | // Where applicable, xdg_surface surface roles will during a configure 571 | // sequence extend this event as a latched state sent as events before the 572 | // xdg_surface.configure event. Such events should be considered to make up 573 | // a set of atomically applied configuration states, where the 574 | // xdg_surface.configure commits the accumulated state. 575 | // 576 | // Clients should arrange their surface for the new states, and then send 577 | // an ack_configure request with the serial sent in this configure event at 578 | // some point before committing the new surface. 579 | // 580 | // If the client receives multiple configure events before it can respond 581 | // to one, it is free to discard all but the last event it received. 582 | func (obj Surface) Configure(serial uint32) { 583 | obj.Conn().SendEvent(obj, 0, serial) 584 | } 585 | 586 | type ToplevelError uint32 587 | 588 | const ( 589 | // provided value is not a valid variant of the resize_edge enum 590 | ToplevelErrorInvalidResizeEdge ToplevelError = 0 591 | ) 592 | 593 | // These values are used to indicate which edge of a surface 594 | // is being dragged in a resize operation. 595 | type ToplevelResizeEdge uint32 596 | 597 | const ( 598 | ToplevelResizeEdgeNone ToplevelResizeEdge = 0 599 | ToplevelResizeEdgeTop ToplevelResizeEdge = 1 600 | ToplevelResizeEdgeBottom ToplevelResizeEdge = 2 601 | ToplevelResizeEdgeLeft ToplevelResizeEdge = 4 602 | ToplevelResizeEdgeTopLeft ToplevelResizeEdge = 5 603 | ToplevelResizeEdgeBottomLeft ToplevelResizeEdge = 6 604 | ToplevelResizeEdgeRight ToplevelResizeEdge = 8 605 | ToplevelResizeEdgeTopRight ToplevelResizeEdge = 9 606 | ToplevelResizeEdgeBottomRight ToplevelResizeEdge = 10 607 | ) 608 | 609 | // The different state values used on the surface. This is designed for 610 | // state values like maximized, fullscreen. It is paired with the 611 | // configure event to ensure that both the client and the compositor 612 | // setting the state can be synchronized. 613 | // 614 | // States set in this way are double-buffered. They will get applied on 615 | // the next commit. 616 | type ToplevelState uint32 617 | 618 | const ( 619 | // The surface is maximized. The window geometry specified in the configure 620 | // event must be obeyed by the client. 621 | // 622 | // The client should draw without shadow or other 623 | // decoration outside of the window geometry. 624 | ToplevelStateMaximized ToplevelState = 1 625 | // The surface is fullscreen. The window geometry specified in the 626 | // configure event is a maximum; the client cannot resize beyond it. For 627 | // a surface to cover the whole fullscreened area, the geometry 628 | // dimensions must be obeyed by the client. For more details, see 629 | // xdg_toplevel.set_fullscreen. 630 | ToplevelStateFullscreen ToplevelState = 2 631 | // The surface is being resized. The window geometry specified in the 632 | // configure event is a maximum; the client cannot resize beyond it. 633 | // Clients that have aspect ratio or cell sizing configuration can use 634 | // a smaller size, however. 635 | ToplevelStateResizing ToplevelState = 3 636 | // Client window decorations should be painted as if the window is 637 | // active. Do not assume this means that the window actually has 638 | // keyboard or pointer focus. 639 | ToplevelStateActivated ToplevelState = 4 640 | // The window is currently in a tiled layout and the left edge is 641 | // considered to be adjacent to another part of the tiling grid. 642 | ToplevelStateTiledLeft ToplevelState = 5 643 | // The window is currently in a tiled layout and the right edge is 644 | // considered to be adjacent to another part of the tiling grid. 645 | ToplevelStateTiledRight ToplevelState = 6 646 | // The window is currently in a tiled layout and the top edge is 647 | // considered to be adjacent to another part of the tiling grid. 648 | ToplevelStateTiledTop ToplevelState = 7 649 | // The window is currently in a tiled layout and the bottom edge is 650 | // considered to be adjacent to another part of the tiling grid. 651 | ToplevelStateTiledBottom ToplevelState = 8 652 | ) 653 | 654 | var ToplevelInterface = &wlproto.Interface{ 655 | Name: "xdg_toplevel", 656 | Version: 4, 657 | Type: reflect.TypeOf(Toplevel{}), 658 | Requests: []wlproto.Request{ 659 | { 660 | Name: "destroy", 661 | Type: "destructor", 662 | Since: 1, 663 | Method: reflect.ValueOf(ToplevelImplementation.Destroy), 664 | Args: []wlproto.Arg{}, 665 | }, 666 | { 667 | Name: "set_parent", 668 | Type: "", 669 | Since: 1, 670 | Method: reflect.ValueOf(ToplevelImplementation.SetParent), 671 | Args: []wlproto.Arg{ 672 | {Type: wlproto.ArgTypeObject, Aux: reflect.TypeOf(Toplevel{})}, 673 | }, 674 | }, 675 | { 676 | Name: "set_title", 677 | Type: "", 678 | Since: 1, 679 | Method: reflect.ValueOf(ToplevelImplementation.SetTitle), 680 | Args: []wlproto.Arg{ 681 | {Type: wlproto.ArgTypeString}, 682 | }, 683 | }, 684 | { 685 | Name: "set_app_id", 686 | Type: "", 687 | Since: 1, 688 | Method: reflect.ValueOf(ToplevelImplementation.SetAppID), 689 | Args: []wlproto.Arg{ 690 | {Type: wlproto.ArgTypeString}, 691 | }, 692 | }, 693 | { 694 | Name: "show_window_menu", 695 | Type: "", 696 | Since: 1, 697 | Method: reflect.ValueOf(ToplevelImplementation.ShowWindowMenu), 698 | Args: []wlproto.Arg{ 699 | {Type: wlproto.ArgTypeObject, Aux: reflect.TypeOf(wayland.Seat{})}, 700 | {Type: wlproto.ArgTypeUint}, 701 | {Type: wlproto.ArgTypeInt}, 702 | {Type: wlproto.ArgTypeInt}, 703 | }, 704 | }, 705 | { 706 | Name: "move", 707 | Type: "", 708 | Since: 1, 709 | Method: reflect.ValueOf(ToplevelImplementation.Move), 710 | Args: []wlproto.Arg{ 711 | {Type: wlproto.ArgTypeObject, Aux: reflect.TypeOf(wayland.Seat{})}, 712 | {Type: wlproto.ArgTypeUint}, 713 | }, 714 | }, 715 | { 716 | Name: "resize", 717 | Type: "", 718 | Since: 1, 719 | Method: reflect.ValueOf(ToplevelImplementation.Resize), 720 | Args: []wlproto.Arg{ 721 | {Type: wlproto.ArgTypeObject, Aux: reflect.TypeOf(wayland.Seat{})}, 722 | {Type: wlproto.ArgTypeUint}, 723 | {Type: wlproto.ArgTypeUint, Aux: reflect.TypeOf(ToplevelResizeEdge(0))}, 724 | }, 725 | }, 726 | { 727 | Name: "set_max_size", 728 | Type: "", 729 | Since: 1, 730 | Method: reflect.ValueOf(ToplevelImplementation.SetMaxSize), 731 | Args: []wlproto.Arg{ 732 | {Type: wlproto.ArgTypeInt}, 733 | {Type: wlproto.ArgTypeInt}, 734 | }, 735 | }, 736 | { 737 | Name: "set_min_size", 738 | Type: "", 739 | Since: 1, 740 | Method: reflect.ValueOf(ToplevelImplementation.SetMinSize), 741 | Args: []wlproto.Arg{ 742 | {Type: wlproto.ArgTypeInt}, 743 | {Type: wlproto.ArgTypeInt}, 744 | }, 745 | }, 746 | { 747 | Name: "set_maximized", 748 | Type: "", 749 | Since: 1, 750 | Method: reflect.ValueOf(ToplevelImplementation.SetMaximized), 751 | Args: []wlproto.Arg{}, 752 | }, 753 | { 754 | Name: "unset_maximized", 755 | Type: "", 756 | Since: 1, 757 | Method: reflect.ValueOf(ToplevelImplementation.UnsetMaximized), 758 | Args: []wlproto.Arg{}, 759 | }, 760 | { 761 | Name: "set_fullscreen", 762 | Type: "", 763 | Since: 1, 764 | Method: reflect.ValueOf(ToplevelImplementation.SetFullscreen), 765 | Args: []wlproto.Arg{ 766 | {Type: wlproto.ArgTypeObject, Aux: reflect.TypeOf(wayland.Output{})}, 767 | }, 768 | }, 769 | { 770 | Name: "unset_fullscreen", 771 | Type: "", 772 | Since: 1, 773 | Method: reflect.ValueOf(ToplevelImplementation.UnsetFullscreen), 774 | Args: []wlproto.Arg{}, 775 | }, 776 | { 777 | Name: "set_minimized", 778 | Type: "", 779 | Since: 1, 780 | Method: reflect.ValueOf(ToplevelImplementation.SetMinimized), 781 | Args: []wlproto.Arg{}, 782 | }, 783 | }, 784 | Events: []wlproto.Event{ 785 | { 786 | Name: "configure", 787 | Since: 1, 788 | Args: []wlproto.Arg{ 789 | {Type: wlproto.ArgTypeInt}, 790 | {Type: wlproto.ArgTypeInt}, 791 | {Type: wlproto.ArgTypeArray}, 792 | }, 793 | }, 794 | { 795 | Name: "close", 796 | Since: 1, 797 | Args: []wlproto.Arg{}, 798 | }, 799 | { 800 | Name: "configure_bounds", 801 | Since: 4, 802 | Args: []wlproto.Arg{ 803 | {Type: wlproto.ArgTypeInt}, 804 | {Type: wlproto.ArgTypeInt}, 805 | }, 806 | }, 807 | }, 808 | } 809 | 810 | // This interface defines an xdg_surface role which allows a surface to, 811 | // among other things, set window-like properties such as maximize, 812 | // fullscreen, and minimize, set application-specific metadata like title and 813 | // id, and well as trigger user interactive operations such as interactive 814 | // resize and move. 815 | // 816 | // Unmapping an xdg_toplevel means that the surface cannot be shown 817 | // by the compositor until it is explicitly mapped again. 818 | // All active operations (e.g., move, resize) are canceled and all 819 | // attributes (e.g. title, state, stacking, ...) are discarded for 820 | // an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to 821 | // the state it had right after xdg_surface.get_toplevel. The client 822 | // can re-map the toplevel by perfoming a commit without any buffer 823 | // attached, waiting for a configure event and handling it as usual (see 824 | // xdg_surface description). 825 | // 826 | // Attaching a null buffer to a toplevel unmaps the surface. 827 | type Toplevel struct{ wlserver.Resource } 828 | 829 | func (Toplevel) Interface() *wlproto.Interface { return ToplevelInterface } 830 | 831 | type ToplevelImplementation interface { 832 | Destroy(obj Toplevel) 833 | SetParent(obj Toplevel, parent Toplevel) 834 | SetTitle(obj Toplevel, title string) 835 | SetAppID(obj Toplevel, appId string) 836 | ShowWindowMenu(obj Toplevel, seat wayland.Seat, serial uint32, x int32, y int32) 837 | Move(obj Toplevel, seat wayland.Seat, serial uint32) 838 | Resize(obj Toplevel, seat wayland.Seat, serial uint32, edges ToplevelResizeEdge) 839 | SetMaxSize(obj Toplevel, width int32, height int32) 840 | SetMinSize(obj Toplevel, width int32, height int32) 841 | SetMaximized(obj Toplevel) 842 | UnsetMaximized(obj Toplevel) 843 | SetFullscreen(obj Toplevel, output wayland.Output) 844 | UnsetFullscreen(obj Toplevel) 845 | SetMinimized(obj Toplevel) 846 | } 847 | 848 | func AddToplevelGlobal(dsp *wlserver.Display, version int, bind func(res Toplevel) ToplevelImplementation) { 849 | dsp.AddGlobal(ToplevelInterface, version, func(res wlserver.Object) wlserver.ResourceImplementation { return bind(res.(Toplevel)) }) 850 | } 851 | 852 | // This configure event asks the client to resize its toplevel surface or 853 | // to change its state. The configured state should not be applied 854 | // immediately. See xdg_surface.configure for details. 855 | // 856 | // The width and height arguments specify a hint to the window 857 | // about how its surface should be resized in window geometry 858 | // coordinates. See set_window_geometry. 859 | // 860 | // If the width or height arguments are zero, it means the client 861 | // should decide its own window dimension. This may happen when the 862 | // compositor needs to configure the state of the surface but doesn't 863 | // have any information about any previous or expected dimension. 864 | // 865 | // The states listed in the event specify how the width/height 866 | // arguments should be interpreted, and possibly how it should be 867 | // drawn. 868 | // 869 | // Clients must send an ack_configure in response to this event. See 870 | // xdg_surface.configure and xdg_surface.ack_configure for details. 871 | func (obj Toplevel) Configure(width int32, height int32, states []byte) { 872 | obj.Conn().SendEvent(obj, 0, width, height, states) 873 | } 874 | 875 | // The close event is sent by the compositor when the user 876 | // wants the surface to be closed. This should be equivalent to 877 | // the user clicking the close button in client-side decorations, 878 | // if your application has any. 879 | // 880 | // This is only a request that the user intends to close the 881 | // window. The client may choose to ignore this request, or show 882 | // a dialog to ask the user to save their data, etc. 883 | func (obj Toplevel) Close() { 884 | obj.Conn().SendEvent(obj, 1) 885 | } 886 | 887 | // The configure_bounds event may be sent prior to a xdg_toplevel.configure 888 | // event to communicate the bounds a window geometry size is recommended 889 | // to constrain to. 890 | // 891 | // The passed width and height are in surface coordinate space. If width 892 | // and height are 0, it means bounds is unknown and equivalent to as if no 893 | // configure_bounds event was ever sent for this surface. 894 | // 895 | // The bounds can for example correspond to the size of a monitor excluding 896 | // any panels or other shell components, so that a surface isn't created in 897 | // a way that it cannot fit. 898 | // 899 | // The bounds may change at any point, and in such a case, a new 900 | // xdg_toplevel.configure_bounds will be sent, followed by 901 | // xdg_toplevel.configure and xdg_surface.configure. 902 | func (obj Toplevel) ConfigureBounds(width int32, height int32) { 903 | obj.Conn().SendEvent(obj, 2, width, height) 904 | } 905 | 906 | type PopupError uint32 907 | 908 | const ( 909 | // tried to grab after being mapped 910 | PopupErrorInvalidGrab PopupError = 0 911 | ) 912 | 913 | var PopupInterface = &wlproto.Interface{ 914 | Name: "xdg_popup", 915 | Version: 4, 916 | Type: reflect.TypeOf(Popup{}), 917 | Requests: []wlproto.Request{ 918 | { 919 | Name: "destroy", 920 | Type: "destructor", 921 | Since: 1, 922 | Method: reflect.ValueOf(PopupImplementation.Destroy), 923 | Args: []wlproto.Arg{}, 924 | }, 925 | { 926 | Name: "grab", 927 | Type: "", 928 | Since: 1, 929 | Method: reflect.ValueOf(PopupImplementation.Grab), 930 | Args: []wlproto.Arg{ 931 | {Type: wlproto.ArgTypeObject, Aux: reflect.TypeOf(wayland.Seat{})}, 932 | {Type: wlproto.ArgTypeUint}, 933 | }, 934 | }, 935 | { 936 | Name: "reposition", 937 | Type: "", 938 | Since: 3, 939 | Method: reflect.ValueOf(PopupImplementation.Reposition), 940 | Args: []wlproto.Arg{ 941 | {Type: wlproto.ArgTypeObject, Aux: reflect.TypeOf(Positioner{})}, 942 | {Type: wlproto.ArgTypeUint}, 943 | }, 944 | }, 945 | }, 946 | Events: []wlproto.Event{ 947 | { 948 | Name: "configure", 949 | Since: 1, 950 | Args: []wlproto.Arg{ 951 | {Type: wlproto.ArgTypeInt}, 952 | {Type: wlproto.ArgTypeInt}, 953 | {Type: wlproto.ArgTypeInt}, 954 | {Type: wlproto.ArgTypeInt}, 955 | }, 956 | }, 957 | { 958 | Name: "popup_done", 959 | Since: 1, 960 | Args: []wlproto.Arg{}, 961 | }, 962 | { 963 | Name: "repositioned", 964 | Since: 3, 965 | Args: []wlproto.Arg{ 966 | {Type: wlproto.ArgTypeUint}, 967 | }, 968 | }, 969 | }, 970 | } 971 | 972 | // A popup surface is a short-lived, temporary surface. It can be used to 973 | // implement for example menus, popovers, tooltips and other similar user 974 | // interface concepts. 975 | // 976 | // A popup can be made to take an explicit grab. See xdg_popup.grab for 977 | // details. 978 | // 979 | // When the popup is dismissed, a popup_done event will be sent out, and at 980 | // the same time the surface will be unmapped. See the xdg_popup.popup_done 981 | // event for details. 982 | // 983 | // Explicitly destroying the xdg_popup object will also dismiss the popup and 984 | // unmap the surface. Clients that want to dismiss the popup when another 985 | // surface of their own is clicked should dismiss the popup using the destroy 986 | // request. 987 | // 988 | // A newly created xdg_popup will be stacked on top of all previously created 989 | // xdg_popup surfaces associated with the same xdg_toplevel. 990 | // 991 | // The parent of an xdg_popup must be mapped (see the xdg_surface 992 | // description) before the xdg_popup itself. 993 | // 994 | // The client must call wl_surface.commit on the corresponding wl_surface 995 | // for the xdg_popup state to take effect. 996 | type Popup struct{ wlserver.Resource } 997 | 998 | func (Popup) Interface() *wlproto.Interface { return PopupInterface } 999 | 1000 | type PopupImplementation interface { 1001 | Destroy(obj Popup) 1002 | Grab(obj Popup, seat wayland.Seat, serial uint32) 1003 | Reposition(obj Popup, positioner Positioner, token uint32) 1004 | } 1005 | 1006 | func AddPopupGlobal(dsp *wlserver.Display, version int, bind func(res Popup) PopupImplementation) { 1007 | dsp.AddGlobal(PopupInterface, version, func(res wlserver.Object) wlserver.ResourceImplementation { return bind(res.(Popup)) }) 1008 | } 1009 | 1010 | // This event asks the popup surface to configure itself given the 1011 | // configuration. The configured state should not be applied immediately. 1012 | // See xdg_surface.configure for details. 1013 | // 1014 | // The x and y arguments represent the position the popup was placed at 1015 | // given the xdg_positioner rule, relative to the upper left corner of the 1016 | // window geometry of the parent surface. 1017 | // 1018 | // For version 2 or older, the configure event for an xdg_popup is only 1019 | // ever sent once for the initial configuration. Starting with version 3, 1020 | // it may be sent again if the popup is setup with an xdg_positioner with 1021 | // set_reactive requested, or in response to xdg_popup.reposition requests. 1022 | func (obj Popup) Configure(x int32, y int32, width int32, height int32) { 1023 | obj.Conn().SendEvent(obj, 0, x, y, width, height) 1024 | } 1025 | 1026 | // The popup_done event is sent out when a popup is dismissed by the 1027 | // compositor. The client should destroy the xdg_popup object at this 1028 | // point. 1029 | func (obj Popup) PopupDone() { 1030 | obj.Conn().SendEvent(obj, 1) 1031 | } 1032 | 1033 | // The repositioned event is sent as part of a popup configuration 1034 | // sequence, together with xdg_popup.configure and lastly 1035 | // xdg_surface.configure to notify the completion of a reposition request. 1036 | // 1037 | // The repositioned event is to notify about the completion of a 1038 | // xdg_popup.reposition request. The token argument is the token passed 1039 | // in the xdg_popup.reposition request. 1040 | // 1041 | // Immediately after this event is emitted, xdg_popup.configure and 1042 | // xdg_surface.configure will be sent with the updated size and position, 1043 | // as well as a new configure serial. 1044 | // 1045 | // The client should optionally update the content of the popup, but must 1046 | // acknowledge the new popup configuration for the new position to take 1047 | // effect. See xdg_surface.ack_configure for details. 1048 | func (obj Popup) Repositioned(token uint32) { 1049 | obj.Conn().SendEvent(obj, 2, token) 1050 | } 1051 | -------------------------------------------------------------------------------- /wlclient/protocols/xdg-shell/xdg-shell.go: -------------------------------------------------------------------------------- 1 | // Code generated by wayland-scanner; DO NOT EDIT. 2 | 3 | // Package xdgShell contains generated definitions of the xdg_shell Wayland protocol. 4 | package xdgShell 5 | 6 | import ( 7 | "honnef.co/go/wayland/wlclient" 8 | "honnef.co/go/wayland/wlclient/protocols/wayland" 9 | "honnef.co/go/wayland/wlproto" 10 | "honnef.co/go/wayland/wlshared" 11 | "reflect" 12 | ) 13 | 14 | var _ wlshared.Fixed 15 | 16 | var interfaceNames = map[string]string{ 17 | "xdg_wm_base": "WmBase", 18 | "xdg_positioner": "Positioner", 19 | "xdg_surface": "Surface", 20 | "xdg_toplevel": "Toplevel", 21 | "xdg_popup": "Popup", 22 | } 23 | 24 | var Interfaces = map[string]*wlproto.Interface{ 25 | "xdg_wm_base": WmBaseInterface, 26 | "xdg_positioner": PositionerInterface, 27 | "xdg_surface": SurfaceInterface, 28 | "xdg_toplevel": ToplevelInterface, 29 | "xdg_popup": PopupInterface, 30 | } 31 | 32 | var Requests = map[string]*wlproto.Request{ 33 | "xdg_wm_base_destroy": &WmBaseInterface.Requests[0], 34 | "xdg_wm_base_create_positioner": &WmBaseInterface.Requests[1], 35 | "xdg_wm_base_get_xdg_surface": &WmBaseInterface.Requests[2], 36 | "xdg_wm_base_pong": &WmBaseInterface.Requests[3], 37 | "xdg_positioner_destroy": &PositionerInterface.Requests[0], 38 | "xdg_positioner_set_size": &PositionerInterface.Requests[1], 39 | "xdg_positioner_set_anchor_rect": &PositionerInterface.Requests[2], 40 | "xdg_positioner_set_anchor": &PositionerInterface.Requests[3], 41 | "xdg_positioner_set_gravity": &PositionerInterface.Requests[4], 42 | "xdg_positioner_set_constraint_adjustment": &PositionerInterface.Requests[5], 43 | "xdg_positioner_set_offset": &PositionerInterface.Requests[6], 44 | "xdg_positioner_set_reactive": &PositionerInterface.Requests[7], 45 | "xdg_positioner_set_parent_size": &PositionerInterface.Requests[8], 46 | "xdg_positioner_set_parent_configure": &PositionerInterface.Requests[9], 47 | "xdg_surface_destroy": &SurfaceInterface.Requests[0], 48 | "xdg_surface_get_toplevel": &SurfaceInterface.Requests[1], 49 | "xdg_surface_get_popup": &SurfaceInterface.Requests[2], 50 | "xdg_surface_set_window_geometry": &SurfaceInterface.Requests[3], 51 | "xdg_surface_ack_configure": &SurfaceInterface.Requests[4], 52 | "xdg_toplevel_destroy": &ToplevelInterface.Requests[0], 53 | "xdg_toplevel_set_parent": &ToplevelInterface.Requests[1], 54 | "xdg_toplevel_set_title": &ToplevelInterface.Requests[2], 55 | "xdg_toplevel_set_app_id": &ToplevelInterface.Requests[3], 56 | "xdg_toplevel_show_window_menu": &ToplevelInterface.Requests[4], 57 | "xdg_toplevel_move": &ToplevelInterface.Requests[5], 58 | "xdg_toplevel_resize": &ToplevelInterface.Requests[6], 59 | "xdg_toplevel_set_max_size": &ToplevelInterface.Requests[7], 60 | "xdg_toplevel_set_min_size": &ToplevelInterface.Requests[8], 61 | "xdg_toplevel_set_maximized": &ToplevelInterface.Requests[9], 62 | "xdg_toplevel_unset_maximized": &ToplevelInterface.Requests[10], 63 | "xdg_toplevel_set_fullscreen": &ToplevelInterface.Requests[11], 64 | "xdg_toplevel_unset_fullscreen": &ToplevelInterface.Requests[12], 65 | "xdg_toplevel_set_minimized": &ToplevelInterface.Requests[13], 66 | "xdg_popup_destroy": &PopupInterface.Requests[0], 67 | "xdg_popup_grab": &PopupInterface.Requests[1], 68 | "xdg_popup_reposition": &PopupInterface.Requests[2], 69 | } 70 | 71 | var Events = map[string]*wlproto.Event{ 72 | "xdg_wm_base_ping": &WmBaseInterface.Events[0], 73 | "xdg_surface_configure": &SurfaceInterface.Events[0], 74 | "xdg_toplevel_configure": &ToplevelInterface.Events[0], 75 | "xdg_toplevel_close": &ToplevelInterface.Events[1], 76 | "xdg_toplevel_configure_bounds": &ToplevelInterface.Events[2], 77 | "xdg_popup_configure": &PopupInterface.Events[0], 78 | "xdg_popup_popup_done": &PopupInterface.Events[1], 79 | "xdg_popup_repositioned": &PopupInterface.Events[2], 80 | } 81 | 82 | type WmBaseError uint32 83 | 84 | const ( 85 | // given wl_surface has another role 86 | WmBaseErrorRole WmBaseError = 0 87 | // xdg_wm_base was destroyed before children 88 | WmBaseErrorDefunctSurfaces WmBaseError = 1 89 | // the client tried to map or destroy a non-topmost popup 90 | WmBaseErrorNotTheTopmostPopup WmBaseError = 2 91 | // the client specified an invalid popup parent surface 92 | WmBaseErrorInvalidPopupParent WmBaseError = 3 93 | // the client provided an invalid surface state 94 | WmBaseErrorInvalidSurfaceState WmBaseError = 4 95 | // the client provided an invalid positioner 96 | WmBaseErrorInvalidPositioner WmBaseError = 5 97 | ) 98 | 99 | var WmBaseInterface = &wlproto.Interface{ 100 | Name: "xdg_wm_base", 101 | Version: 4, 102 | Requests: []wlproto.Request{ 103 | { 104 | Name: "destroy", 105 | Type: "destructor", 106 | Since: 1, 107 | Args: []wlproto.Arg{}, 108 | }, 109 | { 110 | Name: "create_positioner", 111 | Type: "", 112 | Since: 1, 113 | Args: []wlproto.Arg{ 114 | {Type: wlproto.ArgTypeNewID, Aux: reflect.TypeOf((*Positioner)(nil))}, 115 | }, 116 | }, 117 | { 118 | Name: "get_xdg_surface", 119 | Type: "", 120 | Since: 1, 121 | Args: []wlproto.Arg{ 122 | {Type: wlproto.ArgTypeNewID, Aux: reflect.TypeOf((*Surface)(nil))}, 123 | {Type: wlproto.ArgTypeObject, Aux: reflect.TypeOf((*wayland.Surface)(nil))}, 124 | }, 125 | }, 126 | { 127 | Name: "pong", 128 | Type: "", 129 | Since: 1, 130 | Args: []wlproto.Arg{ 131 | {Type: wlproto.ArgTypeUint}, 132 | }, 133 | }, 134 | }, 135 | Events: []wlproto.Event{ 136 | { 137 | Name: "ping", 138 | Since: 1, 139 | Args: []wlproto.Arg{ 140 | {Type: wlproto.ArgTypeUint}, 141 | }, 142 | }, 143 | }, 144 | } 145 | 146 | // The xdg_wm_base interface is exposed as a global object enabling clients 147 | // to turn their wl_surfaces into windows in a desktop environment. It 148 | // defines the basic functionality needed for clients and the compositor to 149 | // create windows that can be dragged, resized, maximized, etc, as well as 150 | // creating transient windows such as popup menus. 151 | type WmBase struct{ wlclient.Proxy } 152 | 153 | func (*WmBase) Interface() *wlproto.Interface { return WmBaseInterface } 154 | 155 | func (obj *WmBase) WithQueue(queue *wlclient.EventQueue) *WmBase { 156 | wobj := &WmBase{} 157 | obj.Conn().NewWrapper(obj, wobj, queue) 158 | return wobj 159 | } 160 | 161 | type WmBaseEvents struct { 162 | Ping func(obj *WmBase, serial uint32) 163 | } 164 | 165 | func (obj *WmBase) AddListener(listeners WmBaseEvents) { 166 | obj.Proxy.SetListeners(listeners.Ping) 167 | } 168 | 169 | // Destroy this xdg_wm_base object. 170 | // 171 | // Destroying a bound xdg_wm_base object while there are surfaces 172 | // still alive created by this xdg_wm_base object instance is illegal 173 | // and will result in a protocol error. 174 | func (obj *WmBase) Destroy() { 175 | obj.Conn().SendDestructor(obj, 0) 176 | } 177 | 178 | // Create a positioner object. A positioner object is used to position 179 | // surfaces relative to some parent surface. See the interface description 180 | // and xdg_surface.get_popup for details. 181 | func (obj *WmBase) CreatePositioner() *Positioner { 182 | _ret := &Positioner{} 183 | obj.Conn().NewProxy(0, _ret, obj.Queue()) 184 | obj.Conn().SendRequest(obj, 1, _ret) 185 | return _ret 186 | } 187 | 188 | // This creates an xdg_surface for the given surface. While xdg_surface 189 | // itself is not a role, the corresponding surface may only be assigned 190 | // a role extending xdg_surface, such as xdg_toplevel or xdg_popup. It is 191 | // illegal to create an xdg_surface for a wl_surface which already has an 192 | // assigned role and this will result in a protocol error. 193 | // 194 | // This creates an xdg_surface for the given surface. An xdg_surface is 195 | // used as basis to define a role to a given surface, such as xdg_toplevel 196 | // or xdg_popup. It also manages functionality shared between xdg_surface 197 | // based surface roles. 198 | // 199 | // See the documentation of xdg_surface for more details about what an 200 | // xdg_surface is and how it is used. 201 | func (obj *WmBase) GetXdgSurface(surface *wayland.Surface) *Surface { 202 | _ret := &Surface{} 203 | obj.Conn().NewProxy(0, _ret, obj.Queue()) 204 | obj.Conn().SendRequest(obj, 2, _ret, surface) 205 | return _ret 206 | } 207 | 208 | // A client must respond to a ping event with a pong request or 209 | // the client may be deemed unresponsive. See xdg_wm_base.ping. 210 | func (obj *WmBase) Pong(serial uint32) { 211 | obj.Conn().SendRequest(obj, 3, serial) 212 | } 213 | 214 | type PositionerError uint32 215 | 216 | const ( 217 | // invalid input provided 218 | PositionerErrorInvalidInput PositionerError = 0 219 | ) 220 | 221 | type PositionerAnchor uint32 222 | 223 | const ( 224 | PositionerAnchorNone PositionerAnchor = 0 225 | PositionerAnchorTop PositionerAnchor = 1 226 | PositionerAnchorBottom PositionerAnchor = 2 227 | PositionerAnchorLeft PositionerAnchor = 3 228 | PositionerAnchorRight PositionerAnchor = 4 229 | PositionerAnchorTopLeft PositionerAnchor = 5 230 | PositionerAnchorBottomLeft PositionerAnchor = 6 231 | PositionerAnchorTopRight PositionerAnchor = 7 232 | PositionerAnchorBottomRight PositionerAnchor = 8 233 | ) 234 | 235 | type PositionerGravity uint32 236 | 237 | const ( 238 | PositionerGravityNone PositionerGravity = 0 239 | PositionerGravityTop PositionerGravity = 1 240 | PositionerGravityBottom PositionerGravity = 2 241 | PositionerGravityLeft PositionerGravity = 3 242 | PositionerGravityRight PositionerGravity = 4 243 | PositionerGravityTopLeft PositionerGravity = 5 244 | PositionerGravityBottomLeft PositionerGravity = 6 245 | PositionerGravityTopRight PositionerGravity = 7 246 | PositionerGravityBottomRight PositionerGravity = 8 247 | ) 248 | 249 | // The constraint adjustment value define ways the compositor will adjust 250 | // the position of the surface, if the unadjusted position would result 251 | // in the surface being partly constrained. 252 | // 253 | // Whether a surface is considered 'constrained' is left to the compositor 254 | // to determine. For example, the surface may be partly outside the 255 | // compositor's defined 'work area', thus necessitating the child surface's 256 | // position be adjusted until it is entirely inside the work area. 257 | // 258 | // The adjustments can be combined, according to a defined precedence: 1) 259 | // Flip, 2) Slide, 3) Resize. 260 | type PositionerConstraintAdjustment uint32 261 | 262 | const ( 263 | // Don't alter the surface position even if it is constrained on some 264 | // axis, for example partially outside the edge of an output. 265 | PositionerConstraintAdjustmentNone PositionerConstraintAdjustment = 0 266 | // Slide the surface along the x axis until it is no longer constrained. 267 | // 268 | // First try to slide towards the direction of the gravity on the x axis 269 | // until either the edge in the opposite direction of the gravity is 270 | // unconstrained or the edge in the direction of the gravity is 271 | // constrained. 272 | // 273 | // Then try to slide towards the opposite direction of the gravity on the 274 | // x axis until either the edge in the direction of the gravity is 275 | // unconstrained or the edge in the opposite direction of the gravity is 276 | // constrained. 277 | PositionerConstraintAdjustmentSlideX PositionerConstraintAdjustment = 1 278 | // Slide the surface along the y axis until it is no longer constrained. 279 | // 280 | // First try to slide towards the direction of the gravity on the y axis 281 | // until either the edge in the opposite direction of the gravity is 282 | // unconstrained or the edge in the direction of the gravity is 283 | // constrained. 284 | // 285 | // Then try to slide towards the opposite direction of the gravity on the 286 | // y axis until either the edge in the direction of the gravity is 287 | // unconstrained or the edge in the opposite direction of the gravity is 288 | // constrained. 289 | PositionerConstraintAdjustmentSlideY PositionerConstraintAdjustment = 2 290 | // Invert the anchor and gravity on the x axis if the surface is 291 | // constrained on the x axis. For example, if the left edge of the 292 | // surface is constrained, the gravity is 'left' and the anchor is 293 | // 'left', change the gravity to 'right' and the anchor to 'right'. 294 | // 295 | // If the adjusted position also ends up being constrained, the resulting 296 | // position of the flip_x adjustment will be the one before the 297 | // adjustment. 298 | PositionerConstraintAdjustmentFlipX PositionerConstraintAdjustment = 4 299 | // Invert the anchor and gravity on the y axis if the surface is 300 | // constrained on the y axis. For example, if the bottom edge of the 301 | // surface is constrained, the gravity is 'bottom' and the anchor is 302 | // 'bottom', change the gravity to 'top' and the anchor to 'top'. 303 | // 304 | // The adjusted position is calculated given the original anchor 305 | // rectangle and offset, but with the new flipped anchor and gravity 306 | // values. 307 | // 308 | // If the adjusted position also ends up being constrained, the resulting 309 | // position of the flip_y adjustment will be the one before the 310 | // adjustment. 311 | PositionerConstraintAdjustmentFlipY PositionerConstraintAdjustment = 8 312 | // Resize the surface horizontally so that it is completely 313 | // unconstrained. 314 | PositionerConstraintAdjustmentResizeX PositionerConstraintAdjustment = 16 315 | // Resize the surface vertically so that it is completely unconstrained. 316 | PositionerConstraintAdjustmentResizeY PositionerConstraintAdjustment = 32 317 | ) 318 | 319 | var PositionerInterface = &wlproto.Interface{ 320 | Name: "xdg_positioner", 321 | Version: 4, 322 | Requests: []wlproto.Request{ 323 | { 324 | Name: "destroy", 325 | Type: "destructor", 326 | Since: 1, 327 | Args: []wlproto.Arg{}, 328 | }, 329 | { 330 | Name: "set_size", 331 | Type: "", 332 | Since: 1, 333 | Args: []wlproto.Arg{ 334 | {Type: wlproto.ArgTypeInt}, 335 | {Type: wlproto.ArgTypeInt}, 336 | }, 337 | }, 338 | { 339 | Name: "set_anchor_rect", 340 | Type: "", 341 | Since: 1, 342 | Args: []wlproto.Arg{ 343 | {Type: wlproto.ArgTypeInt}, 344 | {Type: wlproto.ArgTypeInt}, 345 | {Type: wlproto.ArgTypeInt}, 346 | {Type: wlproto.ArgTypeInt}, 347 | }, 348 | }, 349 | { 350 | Name: "set_anchor", 351 | Type: "", 352 | Since: 1, 353 | Args: []wlproto.Arg{ 354 | {Type: wlproto.ArgTypeUint, Aux: reflect.TypeOf(PositionerAnchor(0))}, 355 | }, 356 | }, 357 | { 358 | Name: "set_gravity", 359 | Type: "", 360 | Since: 1, 361 | Args: []wlproto.Arg{ 362 | {Type: wlproto.ArgTypeUint, Aux: reflect.TypeOf(PositionerGravity(0))}, 363 | }, 364 | }, 365 | { 366 | Name: "set_constraint_adjustment", 367 | Type: "", 368 | Since: 1, 369 | Args: []wlproto.Arg{ 370 | {Type: wlproto.ArgTypeUint}, 371 | }, 372 | }, 373 | { 374 | Name: "set_offset", 375 | Type: "", 376 | Since: 1, 377 | Args: []wlproto.Arg{ 378 | {Type: wlproto.ArgTypeInt}, 379 | {Type: wlproto.ArgTypeInt}, 380 | }, 381 | }, 382 | { 383 | Name: "set_reactive", 384 | Type: "", 385 | Since: 3, 386 | Args: []wlproto.Arg{}, 387 | }, 388 | { 389 | Name: "set_parent_size", 390 | Type: "", 391 | Since: 3, 392 | Args: []wlproto.Arg{ 393 | {Type: wlproto.ArgTypeInt}, 394 | {Type: wlproto.ArgTypeInt}, 395 | }, 396 | }, 397 | { 398 | Name: "set_parent_configure", 399 | Type: "", 400 | Since: 3, 401 | Args: []wlproto.Arg{ 402 | {Type: wlproto.ArgTypeUint}, 403 | }, 404 | }, 405 | }, 406 | Events: []wlproto.Event{}, 407 | } 408 | 409 | // The xdg_positioner provides a collection of rules for the placement of a 410 | // child surface relative to a parent surface. Rules can be defined to ensure 411 | // the child surface remains within the visible area's borders, and to 412 | // specify how the child surface changes its position, such as sliding along 413 | // an axis, or flipping around a rectangle. These positioner-created rules are 414 | // constrained by the requirement that a child surface must intersect with or 415 | // be at least partially adjacent to its parent surface. 416 | // 417 | // See the various requests for details about possible rules. 418 | // 419 | // At the time of the request, the compositor makes a copy of the rules 420 | // specified by the xdg_positioner. Thus, after the request is complete the 421 | // xdg_positioner object can be destroyed or reused; further changes to the 422 | // object will have no effect on previous usages. 423 | // 424 | // For an xdg_positioner object to be considered complete, it must have a 425 | // non-zero size set by set_size, and a non-zero anchor rectangle set by 426 | // set_anchor_rect. Passing an incomplete xdg_positioner object when 427 | // positioning a surface raises an error. 428 | type Positioner struct{ wlclient.Proxy } 429 | 430 | func (*Positioner) Interface() *wlproto.Interface { return PositionerInterface } 431 | 432 | func (obj *Positioner) WithQueue(queue *wlclient.EventQueue) *Positioner { 433 | wobj := &Positioner{} 434 | obj.Conn().NewWrapper(obj, wobj, queue) 435 | return wobj 436 | } 437 | 438 | type PositionerEvents struct { 439 | } 440 | 441 | func (obj *Positioner) AddListener(listeners PositionerEvents) { 442 | obj.Proxy.SetListeners() 443 | } 444 | 445 | // Notify the compositor that the xdg_positioner will no longer be used. 446 | func (obj *Positioner) Destroy() { 447 | obj.Conn().SendDestructor(obj, 0) 448 | } 449 | 450 | // Set the size of the surface that is to be positioned with the positioner 451 | // object. The size is in surface-local coordinates and corresponds to the 452 | // window geometry. See xdg_surface.set_window_geometry. 453 | // 454 | // If a zero or negative size is set the invalid_input error is raised. 455 | func (obj *Positioner) SetSize(width int32, height int32) { 456 | obj.Conn().SendRequest(obj, 1, width, height) 457 | } 458 | 459 | // Specify the anchor rectangle within the parent surface that the child 460 | // surface will be placed relative to. The rectangle is relative to the 461 | // window geometry as defined by xdg_surface.set_window_geometry of the 462 | // parent surface. 463 | // 464 | // When the xdg_positioner object is used to position a child surface, the 465 | // anchor rectangle may not extend outside the window geometry of the 466 | // positioned child's parent surface. 467 | // 468 | // If a negative size is set the invalid_input error is raised. 469 | func (obj *Positioner) SetAnchorRect(x int32, y int32, width int32, height int32) { 470 | obj.Conn().SendRequest(obj, 2, x, y, width, height) 471 | } 472 | 473 | // Defines the anchor point for the anchor rectangle. The specified anchor 474 | // is used derive an anchor point that the child surface will be 475 | // positioned relative to. If a corner anchor is set (e.g. 'top_left' or 476 | // 'bottom_right'), the anchor point will be at the specified corner; 477 | // otherwise, the derived anchor point will be centered on the specified 478 | // edge, or in the center of the anchor rectangle if no edge is specified. 479 | func (obj *Positioner) SetAnchor(anchor PositionerAnchor) { 480 | obj.Conn().SendRequest(obj, 3, anchor) 481 | } 482 | 483 | // Defines in what direction a surface should be positioned, relative to 484 | // the anchor point of the parent surface. If a corner gravity is 485 | // specified (e.g. 'bottom_right' or 'top_left'), then the child surface 486 | // will be placed towards the specified gravity; otherwise, the child 487 | // surface will be centered over the anchor point on any axis that had no 488 | // gravity specified. 489 | func (obj *Positioner) SetGravity(gravity PositionerGravity) { 490 | obj.Conn().SendRequest(obj, 4, gravity) 491 | } 492 | 493 | // Specify how the window should be positioned if the originally intended 494 | // position caused the surface to be constrained, meaning at least 495 | // partially outside positioning boundaries set by the compositor. The 496 | // adjustment is set by constructing a bitmask describing the adjustment to 497 | // be made when the surface is constrained on that axis. 498 | // 499 | // If no bit for one axis is set, the compositor will assume that the child 500 | // surface should not change its position on that axis when constrained. 501 | // 502 | // If more than one bit for one axis is set, the order of how adjustments 503 | // are applied is specified in the corresponding adjustment descriptions. 504 | // 505 | // The default adjustment is none. 506 | func (obj *Positioner) SetConstraintAdjustment(constraintAdjustment uint32) { 507 | obj.Conn().SendRequest(obj, 5, constraintAdjustment) 508 | } 509 | 510 | // Specify the surface position offset relative to the position of the 511 | // anchor on the anchor rectangle and the anchor on the surface. For 512 | // example if the anchor of the anchor rectangle is at (x, y), the surface 513 | // has the gravity bottom|right, and the offset is (ox, oy), the calculated 514 | // surface position will be (x + ox, y + oy). The offset position of the 515 | // surface is the one used for constraint testing. See 516 | // set_constraint_adjustment. 517 | // 518 | // An example use case is placing a popup menu on top of a user interface 519 | // element, while aligning the user interface element of the parent surface 520 | // with some user interface element placed somewhere in the popup surface. 521 | func (obj *Positioner) SetOffset(x int32, y int32) { 522 | obj.Conn().SendRequest(obj, 6, x, y) 523 | } 524 | 525 | // When set reactive, the surface is reconstrained if the conditions used 526 | // for constraining changed, e.g. the parent window moved. 527 | // 528 | // If the conditions changed and the popup was reconstrained, an 529 | // xdg_popup.configure event is sent with updated geometry, followed by an 530 | // xdg_surface.configure event. 531 | func (obj *Positioner) SetReactive() { 532 | obj.Conn().SendRequest(obj, 7) 533 | } 534 | 535 | // Set the parent window geometry the compositor should use when 536 | // positioning the popup. The compositor may use this information to 537 | // determine the future state the popup should be constrained using. If 538 | // this doesn't match the dimension of the parent the popup is eventually 539 | // positioned against, the behavior is undefined. 540 | // 541 | // The arguments are given in the surface-local coordinate space. 542 | func (obj *Positioner) SetParentSize(parentWidth int32, parentHeight int32) { 543 | obj.Conn().SendRequest(obj, 8, parentWidth, parentHeight) 544 | } 545 | 546 | // Set the serial of an xdg_surface.configure event this positioner will be 547 | // used in response to. The compositor may use this information together 548 | // with set_parent_size to determine what future state the popup should be 549 | // constrained using. 550 | func (obj *Positioner) SetParentConfigure(serial uint32) { 551 | obj.Conn().SendRequest(obj, 9, serial) 552 | } 553 | 554 | type SurfaceError uint32 555 | 556 | const ( 557 | SurfaceErrorNotConstructed SurfaceError = 1 558 | SurfaceErrorAlreadyConstructed SurfaceError = 2 559 | SurfaceErrorUnconfiguredBuffer SurfaceError = 3 560 | ) 561 | 562 | var SurfaceInterface = &wlproto.Interface{ 563 | Name: "xdg_surface", 564 | Version: 4, 565 | Requests: []wlproto.Request{ 566 | { 567 | Name: "destroy", 568 | Type: "destructor", 569 | Since: 1, 570 | Args: []wlproto.Arg{}, 571 | }, 572 | { 573 | Name: "get_toplevel", 574 | Type: "", 575 | Since: 1, 576 | Args: []wlproto.Arg{ 577 | {Type: wlproto.ArgTypeNewID, Aux: reflect.TypeOf((*Toplevel)(nil))}, 578 | }, 579 | }, 580 | { 581 | Name: "get_popup", 582 | Type: "", 583 | Since: 1, 584 | Args: []wlproto.Arg{ 585 | {Type: wlproto.ArgTypeNewID, Aux: reflect.TypeOf((*Popup)(nil))}, 586 | {Type: wlproto.ArgTypeObject, Aux: reflect.TypeOf((*Surface)(nil))}, 587 | {Type: wlproto.ArgTypeObject, Aux: reflect.TypeOf((*Positioner)(nil))}, 588 | }, 589 | }, 590 | { 591 | Name: "set_window_geometry", 592 | Type: "", 593 | Since: 1, 594 | Args: []wlproto.Arg{ 595 | {Type: wlproto.ArgTypeInt}, 596 | {Type: wlproto.ArgTypeInt}, 597 | {Type: wlproto.ArgTypeInt}, 598 | {Type: wlproto.ArgTypeInt}, 599 | }, 600 | }, 601 | { 602 | Name: "ack_configure", 603 | Type: "", 604 | Since: 1, 605 | Args: []wlproto.Arg{ 606 | {Type: wlproto.ArgTypeUint}, 607 | }, 608 | }, 609 | }, 610 | Events: []wlproto.Event{ 611 | { 612 | Name: "configure", 613 | Since: 1, 614 | Args: []wlproto.Arg{ 615 | {Type: wlproto.ArgTypeUint}, 616 | }, 617 | }, 618 | }, 619 | } 620 | 621 | // An interface that may be implemented by a wl_surface, for 622 | // implementations that provide a desktop-style user interface. 623 | // 624 | // It provides a base set of functionality required to construct user 625 | // interface elements requiring management by the compositor, such as 626 | // toplevel windows, menus, etc. The types of functionality are split into 627 | // xdg_surface roles. 628 | // 629 | // Creating an xdg_surface does not set the role for a wl_surface. In order 630 | // to map an xdg_surface, the client must create a role-specific object 631 | // using, e.g., get_toplevel, get_popup. The wl_surface for any given 632 | // xdg_surface can have at most one role, and may not be assigned any role 633 | // not based on xdg_surface. 634 | // 635 | // A role must be assigned before any other requests are made to the 636 | // xdg_surface object. 637 | // 638 | // The client must call wl_surface.commit on the corresponding wl_surface 639 | // for the xdg_surface state to take effect. 640 | // 641 | // Creating an xdg_surface from a wl_surface which has a buffer attached or 642 | // committed is a client error, and any attempts by a client to attach or 643 | // manipulate a buffer prior to the first xdg_surface.configure call must 644 | // also be treated as errors. 645 | // 646 | // After creating a role-specific object and setting it up, the client must 647 | // perform an initial commit without any buffer attached. The compositor 648 | // will reply with an xdg_surface.configure event. The client must 649 | // acknowledge it and is then allowed to attach a buffer to map the surface. 650 | // 651 | // Mapping an xdg_surface-based role surface is defined as making it 652 | // possible for the surface to be shown by the compositor. Note that 653 | // a mapped surface is not guaranteed to be visible once it is mapped. 654 | // 655 | // For an xdg_surface to be mapped by the compositor, the following 656 | // conditions must be met: 657 | // (1) the client has assigned an xdg_surface-based role to the surface 658 | // (2) the client has set and committed the xdg_surface state and the 659 | // role-dependent state to the surface 660 | // (3) the client has committed a buffer to the surface 661 | // 662 | // A newly-unmapped surface is considered to have met condition (1) out 663 | // of the 3 required conditions for mapping a surface if its role surface 664 | // has not been destroyed, i.e. the client must perform the initial commit 665 | // again before attaching a buffer. 666 | type Surface struct{ wlclient.Proxy } 667 | 668 | func (*Surface) Interface() *wlproto.Interface { return SurfaceInterface } 669 | 670 | func (obj *Surface) WithQueue(queue *wlclient.EventQueue) *Surface { 671 | wobj := &Surface{} 672 | obj.Conn().NewWrapper(obj, wobj, queue) 673 | return wobj 674 | } 675 | 676 | type SurfaceEvents struct { 677 | Configure func(obj *Surface, serial uint32) 678 | } 679 | 680 | func (obj *Surface) AddListener(listeners SurfaceEvents) { 681 | obj.Proxy.SetListeners(listeners.Configure) 682 | } 683 | 684 | // Destroy the xdg_surface object. An xdg_surface must only be destroyed 685 | // after its role object has been destroyed. 686 | func (obj *Surface) Destroy() { 687 | obj.Conn().SendDestructor(obj, 0) 688 | } 689 | 690 | // This creates an xdg_toplevel object for the given xdg_surface and gives 691 | // the associated wl_surface the xdg_toplevel role. 692 | // 693 | // See the documentation of xdg_toplevel for more details about what an 694 | // xdg_toplevel is and how it is used. 695 | func (obj *Surface) GetToplevel() *Toplevel { 696 | _ret := &Toplevel{} 697 | obj.Conn().NewProxy(0, _ret, obj.Queue()) 698 | obj.Conn().SendRequest(obj, 1, _ret) 699 | return _ret 700 | } 701 | 702 | // This creates an xdg_popup object for the given xdg_surface and gives 703 | // the associated wl_surface the xdg_popup role. 704 | // 705 | // If null is passed as a parent, a parent surface must be specified using 706 | // some other protocol, before committing the initial state. 707 | // 708 | // See the documentation of xdg_popup for more details about what an 709 | // xdg_popup is and how it is used. 710 | func (obj *Surface) GetPopup(parent *Surface, positioner *Positioner) *Popup { 711 | _ret := &Popup{} 712 | obj.Conn().NewProxy(0, _ret, obj.Queue()) 713 | obj.Conn().SendRequest(obj, 2, _ret, parent, positioner) 714 | return _ret 715 | } 716 | 717 | // The window geometry of a surface is its "visible bounds" from the 718 | // user's perspective. Client-side decorations often have invisible 719 | // portions like drop-shadows which should be ignored for the 720 | // purposes of aligning, placing and constraining windows. 721 | // 722 | // The window geometry is double buffered, and will be applied at the 723 | // time wl_surface.commit of the corresponding wl_surface is called. 724 | // 725 | // When maintaining a position, the compositor should treat the (x, y) 726 | // coordinate of the window geometry as the top left corner of the window. 727 | // A client changing the (x, y) window geometry coordinate should in 728 | // general not alter the position of the window. 729 | // 730 | // Once the window geometry of the surface is set, it is not possible to 731 | // unset it, and it will remain the same until set_window_geometry is 732 | // called again, even if a new subsurface or buffer is attached. 733 | // 734 | // If never set, the value is the full bounds of the surface, 735 | // including any subsurfaces. This updates dynamically on every 736 | // commit. This unset is meant for extremely simple clients. 737 | // 738 | // The arguments are given in the surface-local coordinate space of 739 | // the wl_surface associated with this xdg_surface. 740 | // 741 | // The width and height must be greater than zero. Setting an invalid size 742 | // will raise an error. When applied, the effective window geometry will be 743 | // the set window geometry clamped to the bounding rectangle of the 744 | // combined geometry of the surface of the xdg_surface and the associated 745 | // subsurfaces. 746 | func (obj *Surface) SetWindowGeometry(x int32, y int32, width int32, height int32) { 747 | obj.Conn().SendRequest(obj, 3, x, y, width, height) 748 | } 749 | 750 | // When a configure event is received, if a client commits the 751 | // surface in response to the configure event, then the client 752 | // must make an ack_configure request sometime before the commit 753 | // request, passing along the serial of the configure event. 754 | // 755 | // For instance, for toplevel surfaces the compositor might use this 756 | // information to move a surface to the top left only when the client has 757 | // drawn itself for the maximized or fullscreen state. 758 | // 759 | // If the client receives multiple configure events before it 760 | // can respond to one, it only has to ack the last configure event. 761 | // 762 | // A client is not required to commit immediately after sending 763 | // an ack_configure request - it may even ack_configure several times 764 | // before its next surface commit. 765 | // 766 | // A client may send multiple ack_configure requests before committing, but 767 | // only the last request sent before a commit indicates which configure 768 | // event the client really is responding to. 769 | func (obj *Surface) AckConfigure(serial uint32) { 770 | obj.Conn().SendRequest(obj, 4, serial) 771 | } 772 | 773 | type ToplevelError uint32 774 | 775 | const ( 776 | // provided value is not a valid variant of the resize_edge enum 777 | ToplevelErrorInvalidResizeEdge ToplevelError = 0 778 | ) 779 | 780 | // These values are used to indicate which edge of a surface 781 | // is being dragged in a resize operation. 782 | type ToplevelResizeEdge uint32 783 | 784 | const ( 785 | ToplevelResizeEdgeNone ToplevelResizeEdge = 0 786 | ToplevelResizeEdgeTop ToplevelResizeEdge = 1 787 | ToplevelResizeEdgeBottom ToplevelResizeEdge = 2 788 | ToplevelResizeEdgeLeft ToplevelResizeEdge = 4 789 | ToplevelResizeEdgeTopLeft ToplevelResizeEdge = 5 790 | ToplevelResizeEdgeBottomLeft ToplevelResizeEdge = 6 791 | ToplevelResizeEdgeRight ToplevelResizeEdge = 8 792 | ToplevelResizeEdgeTopRight ToplevelResizeEdge = 9 793 | ToplevelResizeEdgeBottomRight ToplevelResizeEdge = 10 794 | ) 795 | 796 | // The different state values used on the surface. This is designed for 797 | // state values like maximized, fullscreen. It is paired with the 798 | // configure event to ensure that both the client and the compositor 799 | // setting the state can be synchronized. 800 | // 801 | // States set in this way are double-buffered. They will get applied on 802 | // the next commit. 803 | type ToplevelState uint32 804 | 805 | const ( 806 | // The surface is maximized. The window geometry specified in the configure 807 | // event must be obeyed by the client. 808 | // 809 | // The client should draw without shadow or other 810 | // decoration outside of the window geometry. 811 | ToplevelStateMaximized ToplevelState = 1 812 | // The surface is fullscreen. The window geometry specified in the 813 | // configure event is a maximum; the client cannot resize beyond it. For 814 | // a surface to cover the whole fullscreened area, the geometry 815 | // dimensions must be obeyed by the client. For more details, see 816 | // xdg_toplevel.set_fullscreen. 817 | ToplevelStateFullscreen ToplevelState = 2 818 | // The surface is being resized. The window geometry specified in the 819 | // configure event is a maximum; the client cannot resize beyond it. 820 | // Clients that have aspect ratio or cell sizing configuration can use 821 | // a smaller size, however. 822 | ToplevelStateResizing ToplevelState = 3 823 | // Client window decorations should be painted as if the window is 824 | // active. Do not assume this means that the window actually has 825 | // keyboard or pointer focus. 826 | ToplevelStateActivated ToplevelState = 4 827 | // The window is currently in a tiled layout and the left edge is 828 | // considered to be adjacent to another part of the tiling grid. 829 | ToplevelStateTiledLeft ToplevelState = 5 830 | // The window is currently in a tiled layout and the right edge is 831 | // considered to be adjacent to another part of the tiling grid. 832 | ToplevelStateTiledRight ToplevelState = 6 833 | // The window is currently in a tiled layout and the top edge is 834 | // considered to be adjacent to another part of the tiling grid. 835 | ToplevelStateTiledTop ToplevelState = 7 836 | // The window is currently in a tiled layout and the bottom edge is 837 | // considered to be adjacent to another part of the tiling grid. 838 | ToplevelStateTiledBottom ToplevelState = 8 839 | ) 840 | 841 | var ToplevelInterface = &wlproto.Interface{ 842 | Name: "xdg_toplevel", 843 | Version: 4, 844 | Requests: []wlproto.Request{ 845 | { 846 | Name: "destroy", 847 | Type: "destructor", 848 | Since: 1, 849 | Args: []wlproto.Arg{}, 850 | }, 851 | { 852 | Name: "set_parent", 853 | Type: "", 854 | Since: 1, 855 | Args: []wlproto.Arg{ 856 | {Type: wlproto.ArgTypeObject, Aux: reflect.TypeOf((*Toplevel)(nil))}, 857 | }, 858 | }, 859 | { 860 | Name: "set_title", 861 | Type: "", 862 | Since: 1, 863 | Args: []wlproto.Arg{ 864 | {Type: wlproto.ArgTypeString}, 865 | }, 866 | }, 867 | { 868 | Name: "set_app_id", 869 | Type: "", 870 | Since: 1, 871 | Args: []wlproto.Arg{ 872 | {Type: wlproto.ArgTypeString}, 873 | }, 874 | }, 875 | { 876 | Name: "show_window_menu", 877 | Type: "", 878 | Since: 1, 879 | Args: []wlproto.Arg{ 880 | {Type: wlproto.ArgTypeObject, Aux: reflect.TypeOf((*wayland.Seat)(nil))}, 881 | {Type: wlproto.ArgTypeUint}, 882 | {Type: wlproto.ArgTypeInt}, 883 | {Type: wlproto.ArgTypeInt}, 884 | }, 885 | }, 886 | { 887 | Name: "move", 888 | Type: "", 889 | Since: 1, 890 | Args: []wlproto.Arg{ 891 | {Type: wlproto.ArgTypeObject, Aux: reflect.TypeOf((*wayland.Seat)(nil))}, 892 | {Type: wlproto.ArgTypeUint}, 893 | }, 894 | }, 895 | { 896 | Name: "resize", 897 | Type: "", 898 | Since: 1, 899 | Args: []wlproto.Arg{ 900 | {Type: wlproto.ArgTypeObject, Aux: reflect.TypeOf((*wayland.Seat)(nil))}, 901 | {Type: wlproto.ArgTypeUint}, 902 | {Type: wlproto.ArgTypeUint, Aux: reflect.TypeOf(ToplevelResizeEdge(0))}, 903 | }, 904 | }, 905 | { 906 | Name: "set_max_size", 907 | Type: "", 908 | Since: 1, 909 | Args: []wlproto.Arg{ 910 | {Type: wlproto.ArgTypeInt}, 911 | {Type: wlproto.ArgTypeInt}, 912 | }, 913 | }, 914 | { 915 | Name: "set_min_size", 916 | Type: "", 917 | Since: 1, 918 | Args: []wlproto.Arg{ 919 | {Type: wlproto.ArgTypeInt}, 920 | {Type: wlproto.ArgTypeInt}, 921 | }, 922 | }, 923 | { 924 | Name: "set_maximized", 925 | Type: "", 926 | Since: 1, 927 | Args: []wlproto.Arg{}, 928 | }, 929 | { 930 | Name: "unset_maximized", 931 | Type: "", 932 | Since: 1, 933 | Args: []wlproto.Arg{}, 934 | }, 935 | { 936 | Name: "set_fullscreen", 937 | Type: "", 938 | Since: 1, 939 | Args: []wlproto.Arg{ 940 | {Type: wlproto.ArgTypeObject, Aux: reflect.TypeOf((*wayland.Output)(nil))}, 941 | }, 942 | }, 943 | { 944 | Name: "unset_fullscreen", 945 | Type: "", 946 | Since: 1, 947 | Args: []wlproto.Arg{}, 948 | }, 949 | { 950 | Name: "set_minimized", 951 | Type: "", 952 | Since: 1, 953 | Args: []wlproto.Arg{}, 954 | }, 955 | }, 956 | Events: []wlproto.Event{ 957 | { 958 | Name: "configure", 959 | Since: 1, 960 | Args: []wlproto.Arg{ 961 | {Type: wlproto.ArgTypeInt}, 962 | {Type: wlproto.ArgTypeInt}, 963 | {Type: wlproto.ArgTypeArray}, 964 | }, 965 | }, 966 | { 967 | Name: "close", 968 | Since: 1, 969 | Args: []wlproto.Arg{}, 970 | }, 971 | { 972 | Name: "configure_bounds", 973 | Since: 4, 974 | Args: []wlproto.Arg{ 975 | {Type: wlproto.ArgTypeInt}, 976 | {Type: wlproto.ArgTypeInt}, 977 | }, 978 | }, 979 | }, 980 | } 981 | 982 | // This interface defines an xdg_surface role which allows a surface to, 983 | // among other things, set window-like properties such as maximize, 984 | // fullscreen, and minimize, set application-specific metadata like title and 985 | // id, and well as trigger user interactive operations such as interactive 986 | // resize and move. 987 | // 988 | // Unmapping an xdg_toplevel means that the surface cannot be shown 989 | // by the compositor until it is explicitly mapped again. 990 | // All active operations (e.g., move, resize) are canceled and all 991 | // attributes (e.g. title, state, stacking, ...) are discarded for 992 | // an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to 993 | // the state it had right after xdg_surface.get_toplevel. The client 994 | // can re-map the toplevel by perfoming a commit without any buffer 995 | // attached, waiting for a configure event and handling it as usual (see 996 | // xdg_surface description). 997 | // 998 | // Attaching a null buffer to a toplevel unmaps the surface. 999 | type Toplevel struct{ wlclient.Proxy } 1000 | 1001 | func (*Toplevel) Interface() *wlproto.Interface { return ToplevelInterface } 1002 | 1003 | func (obj *Toplevel) WithQueue(queue *wlclient.EventQueue) *Toplevel { 1004 | wobj := &Toplevel{} 1005 | obj.Conn().NewWrapper(obj, wobj, queue) 1006 | return wobj 1007 | } 1008 | 1009 | type ToplevelEvents struct { 1010 | Configure func(obj *Toplevel, width int32, height int32, states []byte) 1011 | Close func(obj *Toplevel) 1012 | ConfigureBounds func(obj *Toplevel, width int32, height int32) 1013 | } 1014 | 1015 | func (obj *Toplevel) AddListener(listeners ToplevelEvents) { 1016 | obj.Proxy.SetListeners(listeners.Configure, listeners.Close, listeners.ConfigureBounds) 1017 | } 1018 | 1019 | // This request destroys the role surface and unmaps the surface; 1020 | // see "Unmapping" behavior in interface section for details. 1021 | func (obj *Toplevel) Destroy() { 1022 | obj.Conn().SendDestructor(obj, 0) 1023 | } 1024 | 1025 | // Set the "parent" of this surface. This surface should be stacked 1026 | // above the parent surface and all other ancestor surfaces. 1027 | // 1028 | // Parent windows should be set on dialogs, toolboxes, or other 1029 | // "auxiliary" surfaces, so that the parent is raised when the dialog 1030 | // is raised. 1031 | // 1032 | // Setting a null parent for a child window removes any parent-child 1033 | // relationship for the child. Setting a null parent for a window which 1034 | // currently has no parent is a no-op. 1035 | // 1036 | // If the parent is unmapped then its children are managed as 1037 | // though the parent of the now-unmapped parent has become the 1038 | // parent of this surface. If no parent exists for the now-unmapped 1039 | // parent then the children are managed as though they have no 1040 | // parent surface. 1041 | func (obj *Toplevel) SetParent(parent *Toplevel) { 1042 | obj.Conn().SendRequest(obj, 1, parent) 1043 | } 1044 | 1045 | // Set a short title for the surface. 1046 | // 1047 | // This string may be used to identify the surface in a task bar, 1048 | // window list, or other user interface elements provided by the 1049 | // compositor. 1050 | // 1051 | // The string must be encoded in UTF-8. 1052 | func (obj *Toplevel) SetTitle(title string) { 1053 | obj.Conn().SendRequest(obj, 2, title) 1054 | } 1055 | 1056 | // Set an application identifier for the surface. 1057 | // 1058 | // The app ID identifies the general class of applications to which 1059 | // the surface belongs. The compositor can use this to group multiple 1060 | // surfaces together, or to determine how to launch a new application. 1061 | // 1062 | // For D-Bus activatable applications, the app ID is used as the D-Bus 1063 | // service name. 1064 | // 1065 | // The compositor shell will try to group application surfaces together 1066 | // by their app ID. As a best practice, it is suggested to select app 1067 | // ID's that match the basename of the application's .desktop file. 1068 | // For example, "org.freedesktop.FooViewer" where the .desktop file is 1069 | // "org.freedesktop.FooViewer.desktop". 1070 | // 1071 | // Like other properties, a set_app_id request can be sent after the 1072 | // xdg_toplevel has been mapped to update the property. 1073 | // 1074 | // See the desktop-entry specification [0] for more details on 1075 | // application identifiers and how they relate to well-known D-Bus 1076 | // names and .desktop files. 1077 | // 1078 | // [0] http://standards.freedesktop.org/desktop-entry-spec/ 1079 | func (obj *Toplevel) SetAppID(appId string) { 1080 | obj.Conn().SendRequest(obj, 3, appId) 1081 | } 1082 | 1083 | // Clients implementing client-side decorations might want to show 1084 | // a context menu when right-clicking on the decorations, giving the 1085 | // user a menu that they can use to maximize or minimize the window. 1086 | // 1087 | // This request asks the compositor to pop up such a window menu at 1088 | // the given position, relative to the local surface coordinates of 1089 | // the parent surface. There are no guarantees as to what menu items 1090 | // the window menu contains. 1091 | // 1092 | // This request must be used in response to some sort of user action 1093 | // like a button press, key press, or touch down event. 1094 | func (obj *Toplevel) ShowWindowMenu(seat *wayland.Seat, serial uint32, x int32, y int32) { 1095 | obj.Conn().SendRequest(obj, 4, seat, serial, x, y) 1096 | } 1097 | 1098 | // Start an interactive, user-driven move of the surface. 1099 | // 1100 | // This request must be used in response to some sort of user action 1101 | // like a button press, key press, or touch down event. The passed 1102 | // serial is used to determine the type of interactive move (touch, 1103 | // pointer, etc). 1104 | // 1105 | // The server may ignore move requests depending on the state of 1106 | // the surface (e.g. fullscreen or maximized), or if the passed serial 1107 | // is no longer valid. 1108 | // 1109 | // If triggered, the surface will lose the focus of the device 1110 | // (wl_pointer, wl_touch, etc) used for the move. It is up to the 1111 | // compositor to visually indicate that the move is taking place, such as 1112 | // updating a pointer cursor, during the move. There is no guarantee 1113 | // that the device focus will return when the move is completed. 1114 | func (obj *Toplevel) Move(seat *wayland.Seat, serial uint32) { 1115 | obj.Conn().SendRequest(obj, 5, seat, serial) 1116 | } 1117 | 1118 | // Start a user-driven, interactive resize of the surface. 1119 | // 1120 | // This request must be used in response to some sort of user action 1121 | // like a button press, key press, or touch down event. The passed 1122 | // serial is used to determine the type of interactive resize (touch, 1123 | // pointer, etc). 1124 | // 1125 | // The server may ignore resize requests depending on the state of 1126 | // the surface (e.g. fullscreen or maximized). 1127 | // 1128 | // If triggered, the client will receive configure events with the 1129 | // "resize" state enum value and the expected sizes. See the "resize" 1130 | // enum value for more details about what is required. The client 1131 | // must also acknowledge configure events using "ack_configure". After 1132 | // the resize is completed, the client will receive another "configure" 1133 | // event without the resize state. 1134 | // 1135 | // If triggered, the surface also will lose the focus of the device 1136 | // (wl_pointer, wl_touch, etc) used for the resize. It is up to the 1137 | // compositor to visually indicate that the resize is taking place, 1138 | // such as updating a pointer cursor, during the resize. There is no 1139 | // guarantee that the device focus will return when the resize is 1140 | // completed. 1141 | // 1142 | // The edges parameter specifies how the surface should be resized, and 1143 | // is one of the values of the resize_edge enum. Values not matching 1144 | // a variant of the enum will cause a protocol error. The compositor 1145 | // may use this information to update the surface position for example 1146 | // when dragging the top left corner. The compositor may also use 1147 | // this information to adapt its behavior, e.g. choose an appropriate 1148 | // cursor image. 1149 | func (obj *Toplevel) Resize(seat *wayland.Seat, serial uint32, edges ToplevelResizeEdge) { 1150 | obj.Conn().SendRequest(obj, 6, seat, serial, edges) 1151 | } 1152 | 1153 | // Set a maximum size for the window. 1154 | // 1155 | // The client can specify a maximum size so that the compositor does 1156 | // not try to configure the window beyond this size. 1157 | // 1158 | // The width and height arguments are in window geometry coordinates. 1159 | // See xdg_surface.set_window_geometry. 1160 | // 1161 | // Values set in this way are double-buffered. They will get applied 1162 | // on the next commit. 1163 | // 1164 | // The compositor can use this information to allow or disallow 1165 | // different states like maximize or fullscreen and draw accurate 1166 | // animations. 1167 | // 1168 | // Similarly, a tiling window manager may use this information to 1169 | // place and resize client windows in a more effective way. 1170 | // 1171 | // The client should not rely on the compositor to obey the maximum 1172 | // size. The compositor may decide to ignore the values set by the 1173 | // client and request a larger size. 1174 | // 1175 | // If never set, or a value of zero in the request, means that the 1176 | // client has no expected maximum size in the given dimension. 1177 | // As a result, a client wishing to reset the maximum size 1178 | // to an unspecified state can use zero for width and height in the 1179 | // request. 1180 | // 1181 | // Requesting a maximum size to be smaller than the minimum size of 1182 | // a surface is illegal and will result in a protocol error. 1183 | // 1184 | // The width and height must be greater than or equal to zero. Using 1185 | // strictly negative values for width and height will result in a 1186 | // protocol error. 1187 | func (obj *Toplevel) SetMaxSize(width int32, height int32) { 1188 | obj.Conn().SendRequest(obj, 7, width, height) 1189 | } 1190 | 1191 | // Set a minimum size for the window. 1192 | // 1193 | // The client can specify a minimum size so that the compositor does 1194 | // not try to configure the window below this size. 1195 | // 1196 | // The width and height arguments are in window geometry coordinates. 1197 | // See xdg_surface.set_window_geometry. 1198 | // 1199 | // Values set in this way are double-buffered. They will get applied 1200 | // on the next commit. 1201 | // 1202 | // The compositor can use this information to allow or disallow 1203 | // different states like maximize or fullscreen and draw accurate 1204 | // animations. 1205 | // 1206 | // Similarly, a tiling window manager may use this information to 1207 | // place and resize client windows in a more effective way. 1208 | // 1209 | // The client should not rely on the compositor to obey the minimum 1210 | // size. The compositor may decide to ignore the values set by the 1211 | // client and request a smaller size. 1212 | // 1213 | // If never set, or a value of zero in the request, means that the 1214 | // client has no expected minimum size in the given dimension. 1215 | // As a result, a client wishing to reset the minimum size 1216 | // to an unspecified state can use zero for width and height in the 1217 | // request. 1218 | // 1219 | // Requesting a minimum size to be larger than the maximum size of 1220 | // a surface is illegal and will result in a protocol error. 1221 | // 1222 | // The width and height must be greater than or equal to zero. Using 1223 | // strictly negative values for width and height will result in a 1224 | // protocol error. 1225 | func (obj *Toplevel) SetMinSize(width int32, height int32) { 1226 | obj.Conn().SendRequest(obj, 8, width, height) 1227 | } 1228 | 1229 | // Maximize the surface. 1230 | // 1231 | // After requesting that the surface should be maximized, the compositor 1232 | // will respond by emitting a configure event. Whether this configure 1233 | // actually sets the window maximized is subject to compositor policies. 1234 | // The client must then update its content, drawing in the configured 1235 | // state. The client must also acknowledge the configure when committing 1236 | // the new content (see ack_configure). 1237 | // 1238 | // It is up to the compositor to decide how and where to maximize the 1239 | // surface, for example which output and what region of the screen should 1240 | // be used. 1241 | // 1242 | // If the surface was already maximized, the compositor will still emit 1243 | // a configure event with the "maximized" state. 1244 | // 1245 | // If the surface is in a fullscreen state, this request has no direct 1246 | // effect. It may alter the state the surface is returned to when 1247 | // unmaximized unless overridden by the compositor. 1248 | func (obj *Toplevel) SetMaximized() { 1249 | obj.Conn().SendRequest(obj, 9) 1250 | } 1251 | 1252 | // Unmaximize the surface. 1253 | // 1254 | // After requesting that the surface should be unmaximized, the compositor 1255 | // will respond by emitting a configure event. Whether this actually 1256 | // un-maximizes the window is subject to compositor policies. 1257 | // If available and applicable, the compositor will include the window 1258 | // geometry dimensions the window had prior to being maximized in the 1259 | // configure event. The client must then update its content, drawing it in 1260 | // the configured state. The client must also acknowledge the configure 1261 | // when committing the new content (see ack_configure). 1262 | // 1263 | // It is up to the compositor to position the surface after it was 1264 | // unmaximized; usually the position the surface had before maximizing, if 1265 | // applicable. 1266 | // 1267 | // If the surface was already not maximized, the compositor will still 1268 | // emit a configure event without the "maximized" state. 1269 | // 1270 | // If the surface is in a fullscreen state, this request has no direct 1271 | // effect. It may alter the state the surface is returned to when 1272 | // unmaximized unless overridden by the compositor. 1273 | func (obj *Toplevel) UnsetMaximized() { 1274 | obj.Conn().SendRequest(obj, 10) 1275 | } 1276 | 1277 | // Make the surface fullscreen. 1278 | // 1279 | // After requesting that the surface should be fullscreened, the 1280 | // compositor will respond by emitting a configure event. Whether the 1281 | // client is actually put into a fullscreen state is subject to compositor 1282 | // policies. The client must also acknowledge the configure when 1283 | // committing the new content (see ack_configure). 1284 | // 1285 | // The output passed by the request indicates the client's preference as 1286 | // to which display it should be set fullscreen on. If this value is NULL, 1287 | // it's up to the compositor to choose which display will be used to map 1288 | // this surface. 1289 | // 1290 | // If the surface doesn't cover the whole output, the compositor will 1291 | // position the surface in the center of the output and compensate with 1292 | // with border fill covering the rest of the output. The content of the 1293 | // border fill is undefined, but should be assumed to be in some way that 1294 | // attempts to blend into the surrounding area (e.g. solid black). 1295 | // 1296 | // If the fullscreened surface is not opaque, the compositor must make 1297 | // sure that other screen content not part of the same surface tree (made 1298 | // up of subsurfaces, popups or similarly coupled surfaces) are not 1299 | // visible below the fullscreened surface. 1300 | func (obj *Toplevel) SetFullscreen(output *wayland.Output) { 1301 | obj.Conn().SendRequest(obj, 11, output) 1302 | } 1303 | 1304 | // Make the surface no longer fullscreen. 1305 | // 1306 | // After requesting that the surface should be unfullscreened, the 1307 | // compositor will respond by emitting a configure event. 1308 | // Whether this actually removes the fullscreen state of the client is 1309 | // subject to compositor policies. 1310 | // 1311 | // Making a surface unfullscreen sets states for the surface based on the following: 1312 | // * the state(s) it may have had before becoming fullscreen 1313 | // * any state(s) decided by the compositor 1314 | // * any state(s) requested by the client while the surface was fullscreen 1315 | // 1316 | // The compositor may include the previous window geometry dimensions in 1317 | // the configure event, if applicable. 1318 | // 1319 | // The client must also acknowledge the configure when committing the new 1320 | // content (see ack_configure). 1321 | func (obj *Toplevel) UnsetFullscreen() { 1322 | obj.Conn().SendRequest(obj, 12) 1323 | } 1324 | 1325 | // Request that the compositor minimize your surface. There is no 1326 | // way to know if the surface is currently minimized, nor is there 1327 | // any way to unset minimization on this surface. 1328 | // 1329 | // If you are looking to throttle redrawing when minimized, please 1330 | // instead use the wl_surface.frame event for this, as this will 1331 | // also work with live previews on windows in Alt-Tab, Expose or 1332 | // similar compositor features. 1333 | func (obj *Toplevel) SetMinimized() { 1334 | obj.Conn().SendRequest(obj, 13) 1335 | } 1336 | 1337 | type PopupError uint32 1338 | 1339 | const ( 1340 | // tried to grab after being mapped 1341 | PopupErrorInvalidGrab PopupError = 0 1342 | ) 1343 | 1344 | var PopupInterface = &wlproto.Interface{ 1345 | Name: "xdg_popup", 1346 | Version: 4, 1347 | Requests: []wlproto.Request{ 1348 | { 1349 | Name: "destroy", 1350 | Type: "destructor", 1351 | Since: 1, 1352 | Args: []wlproto.Arg{}, 1353 | }, 1354 | { 1355 | Name: "grab", 1356 | Type: "", 1357 | Since: 1, 1358 | Args: []wlproto.Arg{ 1359 | {Type: wlproto.ArgTypeObject, Aux: reflect.TypeOf((*wayland.Seat)(nil))}, 1360 | {Type: wlproto.ArgTypeUint}, 1361 | }, 1362 | }, 1363 | { 1364 | Name: "reposition", 1365 | Type: "", 1366 | Since: 3, 1367 | Args: []wlproto.Arg{ 1368 | {Type: wlproto.ArgTypeObject, Aux: reflect.TypeOf((*Positioner)(nil))}, 1369 | {Type: wlproto.ArgTypeUint}, 1370 | }, 1371 | }, 1372 | }, 1373 | Events: []wlproto.Event{ 1374 | { 1375 | Name: "configure", 1376 | Since: 1, 1377 | Args: []wlproto.Arg{ 1378 | {Type: wlproto.ArgTypeInt}, 1379 | {Type: wlproto.ArgTypeInt}, 1380 | {Type: wlproto.ArgTypeInt}, 1381 | {Type: wlproto.ArgTypeInt}, 1382 | }, 1383 | }, 1384 | { 1385 | Name: "popup_done", 1386 | Since: 1, 1387 | Args: []wlproto.Arg{}, 1388 | }, 1389 | { 1390 | Name: "repositioned", 1391 | Since: 3, 1392 | Args: []wlproto.Arg{ 1393 | {Type: wlproto.ArgTypeUint}, 1394 | }, 1395 | }, 1396 | }, 1397 | } 1398 | 1399 | // A popup surface is a short-lived, temporary surface. It can be used to 1400 | // implement for example menus, popovers, tooltips and other similar user 1401 | // interface concepts. 1402 | // 1403 | // A popup can be made to take an explicit grab. See xdg_popup.grab for 1404 | // details. 1405 | // 1406 | // When the popup is dismissed, a popup_done event will be sent out, and at 1407 | // the same time the surface will be unmapped. See the xdg_popup.popup_done 1408 | // event for details. 1409 | // 1410 | // Explicitly destroying the xdg_popup object will also dismiss the popup and 1411 | // unmap the surface. Clients that want to dismiss the popup when another 1412 | // surface of their own is clicked should dismiss the popup using the destroy 1413 | // request. 1414 | // 1415 | // A newly created xdg_popup will be stacked on top of all previously created 1416 | // xdg_popup surfaces associated with the same xdg_toplevel. 1417 | // 1418 | // The parent of an xdg_popup must be mapped (see the xdg_surface 1419 | // description) before the xdg_popup itself. 1420 | // 1421 | // The client must call wl_surface.commit on the corresponding wl_surface 1422 | // for the xdg_popup state to take effect. 1423 | type Popup struct{ wlclient.Proxy } 1424 | 1425 | func (*Popup) Interface() *wlproto.Interface { return PopupInterface } 1426 | 1427 | func (obj *Popup) WithQueue(queue *wlclient.EventQueue) *Popup { 1428 | wobj := &Popup{} 1429 | obj.Conn().NewWrapper(obj, wobj, queue) 1430 | return wobj 1431 | } 1432 | 1433 | type PopupEvents struct { 1434 | Configure func(obj *Popup, x int32, y int32, width int32, height int32) 1435 | PopupDone func(obj *Popup) 1436 | Repositioned func(obj *Popup, token uint32) 1437 | } 1438 | 1439 | func (obj *Popup) AddListener(listeners PopupEvents) { 1440 | obj.Proxy.SetListeners(listeners.Configure, listeners.PopupDone, listeners.Repositioned) 1441 | } 1442 | 1443 | // This destroys the popup. Explicitly destroying the xdg_popup 1444 | // object will also dismiss the popup, and unmap the surface. 1445 | // 1446 | // If this xdg_popup is not the "topmost" popup, a protocol error 1447 | // will be sent. 1448 | func (obj *Popup) Destroy() { 1449 | obj.Conn().SendDestructor(obj, 0) 1450 | } 1451 | 1452 | // This request makes the created popup take an explicit grab. An explicit 1453 | // grab will be dismissed when the user dismisses the popup, or when the 1454 | // client destroys the xdg_popup. This can be done by the user clicking 1455 | // outside the surface, using the keyboard, or even locking the screen 1456 | // through closing the lid or a timeout. 1457 | // 1458 | // If the compositor denies the grab, the popup will be immediately 1459 | // dismissed. 1460 | // 1461 | // This request must be used in response to some sort of user action like a 1462 | // button press, key press, or touch down event. The serial number of the 1463 | // event should be passed as 'serial'. 1464 | // 1465 | // The parent of a grabbing popup must either be an xdg_toplevel surface or 1466 | // another xdg_popup with an explicit grab. If the parent is another 1467 | // xdg_popup it means that the popups are nested, with this popup now being 1468 | // the topmost popup. 1469 | // 1470 | // Nested popups must be destroyed in the reverse order they were created 1471 | // in, e.g. the only popup you are allowed to destroy at all times is the 1472 | // topmost one. 1473 | // 1474 | // When compositors choose to dismiss a popup, they may dismiss every 1475 | // nested grabbing popup as well. When a compositor dismisses popups, it 1476 | // will follow the same dismissing order as required from the client. 1477 | // 1478 | // The parent of a grabbing popup must either be another xdg_popup with an 1479 | // active explicit grab, or an xdg_popup or xdg_toplevel, if there are no 1480 | // explicit grabs already taken. 1481 | // 1482 | // If the topmost grabbing popup is destroyed, the grab will be returned to 1483 | // the parent of the popup, if that parent previously had an explicit grab. 1484 | // 1485 | // If the parent is a grabbing popup which has already been dismissed, this 1486 | // popup will be immediately dismissed. If the parent is a popup that did 1487 | // not take an explicit grab, an error will be raised. 1488 | // 1489 | // During a popup grab, the client owning the grab will receive pointer 1490 | // and touch events for all their surfaces as normal (similar to an 1491 | // "owner-events" grab in X11 parlance), while the top most grabbing popup 1492 | // will always have keyboard focus. 1493 | func (obj *Popup) Grab(seat *wayland.Seat, serial uint32) { 1494 | obj.Conn().SendRequest(obj, 1, seat, serial) 1495 | } 1496 | 1497 | // Reposition an already-mapped popup. The popup will be placed given the 1498 | // details in the passed xdg_positioner object, and a 1499 | // xdg_popup.repositioned followed by xdg_popup.configure and 1500 | // xdg_surface.configure will be emitted in response. Any parameters set 1501 | // by the previous positioner will be discarded. 1502 | // 1503 | // The passed token will be sent in the corresponding 1504 | // xdg_popup.repositioned event. The new popup position will not take 1505 | // effect until the corresponding configure event is acknowledged by the 1506 | // client. See xdg_popup.repositioned for details. The token itself is 1507 | // opaque, and has no other special meaning. 1508 | // 1509 | // If multiple reposition requests are sent, the compositor may skip all 1510 | // but the last one. 1511 | // 1512 | // If the popup is repositioned in response to a configure event for its 1513 | // parent, the client should send an xdg_positioner.set_parent_configure 1514 | // and possibly an xdg_positioner.set_parent_size request to allow the 1515 | // compositor to properly constrain the popup. 1516 | // 1517 | // If the popup is repositioned together with a parent that is being 1518 | // resized, but not in response to a configure event, the client should 1519 | // send an xdg_positioner.set_parent_size request. 1520 | func (obj *Popup) Reposition(positioner *Positioner, token uint32) { 1521 | obj.Conn().SendRequest(obj, 2, positioner, token) 1522 | } 1523 | --------------------------------------------------------------------------------