merge: extension protocol bindings (Veya P3)
24 files · +7095 -2
M README.md +6 -0
| @@ -17,6 +17,12 @@ WLTurbo provides the foundational Wayland client infrastructure: | |||
| 17 | 17 | ||
| 18 | 18 | Higher-level protocol implementations (virtual input devices, output management, etc.) are intentionally left to specialized libraries that build on top of WLTurbo. | |
| 19 | 19 | ||
| 20 | + | ## Generated protocols | |
| 21 | + | ||
| 22 | + | `protocol/core` provides core Wayland (including data-device); `protocol/xdgshell` provides xdg-shell; `protocol/linuxdmabuf` provides linux-dmabuf v4 feedback; `protocol/drmsyncobj` provides linux-drm-syncobj v1; `protocol/viewporter` provides viewporter; `protocol/fractionalscale` provides fractional-scale v1; and `protocol/textinput` provides text-input v3. Bindings handle wire messages and object lifecycle; applications decide compositor policy. | |
| 23 | + | ||
| 24 | + | After `display.Roundtrip()` discovers globals, call `display.Registry().BindNegotiated(interfaceName, supportedVersion, proxy)` to bind at the lesser of the advertised and supported versions. It returns the negotiated version; use `errors.Is(err, wlturbo.ErrGlobalNotFound)` to handle absent optional globals. | |
| 25 | + | ||
| 20 | 26 | ## Features | |
| 21 | 27 | ||
| 22 | 28 | ### Performance Optimizations | |
M client.go +22 -0
| @@ -789,6 +789,28 @@ func (r *Registry) AddHandler(iface string, handler GlobalHandler) { | |||
| 789 | 789 | r.handlers[iface] = handler | |
| 790 | 790 | } | |
| 791 | 791 | ||
| 792 | + | // ErrGlobalNotFound reports that the compositor does not announce a global. | |
| 793 | + | var ErrGlobalNotFound = errors.New("wlturbo: global not announced") | |
| 794 | + | ||
| 795 | + | // BindNegotiated binds the announced global for iface at | |
| 796 | + | // min(announced version, supported) and returns that version. It returns | |
| 797 | + | // ErrGlobalNotFound when the global is absent, so callers can decide whether | |
| 798 | + | // the capability is optional. | |
| 799 | + | func (r *Registry) BindNegotiated(iface string, supported uint32, proxy Proxy) (uint32, error) { | |
| 800 | + | if supported == 0 { | |
| 801 | + | return 0, fmt.Errorf("wlturbo: supported version for %s must be at least 1", iface) | |
| 802 | + | } | |
| 803 | + | g, ok := r.FindGlobal(iface) | |
| 804 | + | if !ok || g.Version == 0 { | |
| 805 | + | return 0, fmt.Errorf("%w: %s", ErrGlobalNotFound, iface) | |
| 806 | + | } | |
| 807 | + | version := min(g.Version, supported) | |
| 808 | + | if err := r.Bind(g.Name, iface, version, proxy); err != nil { | |
| 809 | + | return 0, err | |
| 810 | + | } | |
| 811 | + | return version, nil | |
| 812 | + | } | |
| 813 | + | ||
| 792 | 814 | // Bind binds to a global object and returns a typed proxy | |
| 793 | 815 | func (r *Registry) Bind(name uint32, iface string, version uint32, proxy Proxy) error { | |
| 794 | 816 | // Set the ID if not already set | |
M consumer_test.go +20 -1
| @@ -35,7 +35,26 @@ func TestExternalConsumer(t *testing.T) { | |||
| 35 | 35 | if err := os.WriteFile(filepath.Join(temp, "consumer_test.go"), source, 0600); err != nil { | |
| 36 | 36 | t.Fatal(err) | |
| 37 | 37 | } | |
| 38 | - | cmd := exec.Command(goBin, "test", "-run", "TestGeneratedCoreOverSocketpair|TestBufferReleaseAndOfferChild", "./...") | |
| 38 | + | // Keep the extension fake server in a separate package: both fixtures | |
| 39 | + | // independently own their socketpair helpers. | |
| 40 | + | extension, err := os.ReadFile(filepath.Join(root, "protocol/extensions_test.go")) | |
| 41 | + | if err != nil { | |
| 42 | + | t.Fatal(err) | |
| 43 | + | } | |
| 44 | + | if err := os.Mkdir(filepath.Join(temp, "extensions"), 0700); err != nil { | |
| 45 | + | t.Fatal(err) | |
| 46 | + | } | |
| 47 | + | if err := os.WriteFile(filepath.Join(temp, "extensions", "extensions_test.go"), extension, 0600); err != nil { | |
| 48 | + | t.Fatal(err) | |
| 49 | + | } | |
| 50 | + | negotiation, err := os.ReadFile(filepath.Join(root, "protocol/negotiation_test.go")) | |
| 51 | + | if err != nil { | |
| 52 | + | t.Fatal(err) | |
| 53 | + | } | |
| 54 | + | if err := os.WriteFile(filepath.Join(temp, "extensions", "negotiation_test.go"), negotiation, 0600); err != nil { | |
| 55 | + | t.Fatal(err) | |
| 56 | + | } | |
| 57 | + | cmd := exec.Command(goBin, "test", "-run", "TestGeneratedCoreOverSocketpair|TestBufferReleaseAndOfferChild|TestExtensionsOverSocketpair|TestAnnouncedVersionNegotiation", "./...") | |
| 39 | 58 | cmd.Dir = temp | |
| 40 | 59 | cmd.Env = append(os.Environ(), "GOWORK=off", "GOFLAGS=-mod=mod") | |
| 41 | 60 | if output, err := cmd.CombinedOutput(); err != nil { | |
M internal/scanner/scanner.go +2 -1
| @@ -265,7 +265,8 @@ func (s *Scanner) prepareTemplateData(packageName string) (templateData, error) | |||
| 265 | 265 | } | |
| 266 | 266 | for _, ev := range candidate.Events { | |
| 267 | 267 | for _, arg := range ev.Args { | |
| 268 | - | if arg.Type == "object" || arg.Type == "new_id" { | |
| 268 | + | // Event object references are decoded as IDs, not imported proxies. | |
| 269 | + | if arg.Type == "new_id" { | |
| 269 | 270 | usedCross[arg.Interface] = true | |
| 270 | 271 | } | |
| 271 | 272 | } | |
M protocol/SOURCE.md +16 -0
| @@ -5,3 +5,19 @@ | |||
| 5 | 5 | `87cc8a8728a923fc57938faa81ba0e74f34ecdc7`. | |
| 6 | 6 | SHA-256: `cc860987e54f8d85c940e97fa1270c69b6e4ad31fbcf5a7f00107ce1157f5e07`. | |
| 7 | 7 | License: MIT (full copyright and grant in `wayland.xml` `<copyright>`). | |
| 8 | + | ||
| 9 | + | ## Required extensions | |
| 10 | + | ||
| 11 | + | The following XML files come from `https://gitlab.freedesktop.org/wayland/wayland-protocols`, | |
| 12 | + | tag `1.49` (peeled commit `ee78491a237eaff9389a0ccf8680521d074407d3`). | |
| 13 | + | Each file includes its upstream `<copyright>` and MIT-style license grant. | |
| 14 | + | SHA-256 (of the vendored XML bytes): | |
| 15 | + | ||
| 16 | + | | Upstream path (under tag 1.49) | Vendored path | SHA-256 | | |
| 17 | + | |---|---|---| | |
| 18 | + | | `stable/xdg-shell/xdg-shell.xml` | `xdgshell/xdg-shell.xml` | `7ba7f9c8473deee674cb1f154a18abd0bb0cc072604fc055b0c15e459fc4c7df` | | |
| 19 | + | | `stable/linux-dmabuf/linux-dmabuf-v1.xml` | `linuxdmabuf/linux-dmabuf-v1.xml` | `2735bb4589cbb364dfcb6a821dd3abbf0c0257956b716b9691718ddfd453df4c` | | |
| 20 | + | | `staging/linux-drm-syncobj/linux-drm-syncobj-v1.xml` | `drmsyncobj/linux-drm-syncobj-v1.xml` | `5ede41a56eced6aa635caa7ab2996066bff9fd211620dbf9f73aafab740bec71` | | |
| 21 | + | | `stable/viewporter/viewporter.xml` | `viewporter/viewporter.xml` | `dcb12279a03746301fe490aaed4b38a403485a925abfce2ccfceb644e104fe71` | | |
| 22 | + | | `staging/fractional-scale/fractional-scale-v1.xml` | `fractionalscale/fractional-scale-v1.xml` | `5941de5d28f427ecdadddc8623a6f6af0a30b0ab4726847236ba7a7652b81316` | | |
| 23 | + | | `unstable/text-input/text-input-unstable-v3.xml` | `textinput/text-input-unstable-v3.xml` | `160815cda13c285a30df1971d8f0a5b3aac07071537df59916d2f748b18e4f59` | | |
A protocol/drmsyncobj/linux-drm-syncobj-v1.xml +261 -0
| @@ -0,0 +1,261 @@ | |||
| 1 | + | <?xml version="1.0" encoding="UTF-8"?> | |
| 2 | + | <protocol name="linux_drm_syncobj_v1"> | |
| 3 | + | <copyright> | |
| 4 | + | Copyright 2016 The Chromium Authors. | |
| 5 | + | Copyright 2017 Intel Corporation | |
| 6 | + | Copyright 2018 Collabora, Ltd | |
| 7 | + | Copyright 2021 Simon Ser | |
| 8 | + | ||
| 9 | + | Permission is hereby granted, free of charge, to any person obtaining a | |
| 10 | + | copy of this software and associated documentation files (the "Software"), | |
| 11 | + | to deal in the Software without restriction, including without limitation | |
| 12 | + | the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 13 | + | and/or sell copies of the Software, and to permit persons to whom the | |
| 14 | + | Software is furnished to do so, subject to the following conditions: | |
| 15 | + | ||
| 16 | + | The above copyright notice and this permission notice (including the next | |
| 17 | + | paragraph) shall be included in all copies or substantial portions of the | |
| 18 | + | Software. | |
| 19 | + | ||
| 20 | + | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 21 | + | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 22 | + | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 23 | + | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 24 | + | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
| 25 | + | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
| 26 | + | DEALINGS IN THE SOFTWARE. | |
| 27 | + | </copyright> | |
| 28 | + | ||
| 29 | + | <description summary="protocol for providing explicit synchronization"> | |
| 30 | + | This protocol allows clients to request explicit synchronization for | |
| 31 | + | buffers. It is tied to the Linux DRM synchronization object framework. | |
| 32 | + | ||
| 33 | + | Synchronization refers to co-ordination of pipelined operations performed | |
| 34 | + | on buffers. Most GPU clients will schedule an asynchronous operation to | |
| 35 | + | render to the buffer, then immediately send the buffer to the compositor | |
| 36 | + | to be attached to a surface. | |
| 37 | + | ||
| 38 | + | With implicit synchronization, ensuring that the rendering operation is | |
| 39 | + | complete before the compositor displays the buffer is an implementation | |
| 40 | + | detail handled by either the kernel or userspace graphics driver. | |
| 41 | + | ||
| 42 | + | By contrast, with explicit synchronization, DRM synchronization object | |
| 43 | + | timeline points mark when the asynchronous operations are complete. When | |
| 44 | + | submitting a buffer, the client provides a timeline point which will be | |
| 45 | + | waited on before the compositor accesses the buffer, and another timeline | |
| 46 | + | point that the compositor will signal when it no longer needs to access the | |
| 47 | + | buffer contents for the purposes of the surface commit. | |
| 48 | + | ||
| 49 | + | Linux DRM synchronization objects are documented at: | |
| 50 | + | https://dri.freedesktop.org/docs/drm/gpu/drm-mm.html#drm-sync-objects | |
| 51 | + | ||
| 52 | + | Warning! The protocol described in this file is currently in the testing | |
| 53 | + | phase. Backward compatible changes may be added together with the | |
| 54 | + | corresponding interface version bump. Backward incompatible changes can | |
| 55 | + | only be done by creating a new major version of the extension. | |
| 56 | + | </description> | |
| 57 | + | ||
| 58 | + | <interface name="wp_linux_drm_syncobj_manager_v1" version="1"> | |
| 59 | + | <description summary="global for providing explicit synchronization"> | |
| 60 | + | This global is a factory interface, allowing clients to request | |
| 61 | + | explicit synchronization for buffers on a per-surface basis. | |
| 62 | + | ||
| 63 | + | See wp_linux_drm_syncobj_surface_v1 for more information. | |
| 64 | + | </description> | |
| 65 | + | ||
| 66 | + | <request name="destroy" type="destructor"> | |
| 67 | + | <description summary="destroy explicit synchronization factory object"> | |
| 68 | + | Destroy this explicit synchronization factory object. Other objects | |
| 69 | + | shall not be affected by this request. | |
| 70 | + | </description> | |
| 71 | + | </request> | |
| 72 | + | ||
| 73 | + | <enum name="error"> | |
| 74 | + | <entry name="surface_exists" value="0" | |
| 75 | + | summary="the surface already has a synchronization object associated"/> | |
| 76 | + | <entry name="invalid_timeline" value="1" | |
| 77 | + | summary="the timeline object could not be imported"/> | |
| 78 | + | </enum> | |
| 79 | + | ||
| 80 | + | <request name="get_surface"> | |
| 81 | + | <description summary="extend surface interface for explicit synchronization"> | |
| 82 | + | Instantiate an interface extension for the given wl_surface to provide | |
| 83 | + | explicit synchronization. | |
| 84 | + | ||
| 85 | + | If the given wl_surface already has an explicit synchronization object | |
| 86 | + | associated, the surface_exists protocol error is raised. | |
| 87 | + | ||
| 88 | + | Graphics APIs, like EGL or Vulkan, that manage the buffer queue and | |
| 89 | + | commits of a wl_surface themselves, are likely to be using this | |
| 90 | + | extension internally. If a client is using such an API for a | |
| 91 | + | wl_surface, it should not directly use this extension on that surface, | |
| 92 | + | to avoid raising a surface_exists protocol error. | |
| 93 | + | </description> | |
| 94 | + | <arg name="id" type="new_id" interface="wp_linux_drm_syncobj_surface_v1" | |
| 95 | + | summary="the new synchronization surface object id"/> | |
| 96 | + | <arg name="surface" type="object" interface="wl_surface" | |
| 97 | + | summary="the surface"/> | |
| 98 | + | </request> | |
| 99 | + | ||
| 100 | + | <request name="import_timeline"> | |
| 101 | + | <description summary="import a DRM syncobj timeline"> | |
| 102 | + | Import a DRM synchronization object timeline. | |
| 103 | + | ||
| 104 | + | If the FD cannot be imported, the invalid_timeline error is raised. | |
| 105 | + | </description> | |
| 106 | + | <arg name="id" type="new_id" interface="wp_linux_drm_syncobj_timeline_v1"/> | |
| 107 | + | <arg name="fd" type="fd" summary="drm_syncobj file descriptor"/> | |
| 108 | + | </request> | |
| 109 | + | </interface> | |
| 110 | + | ||
| 111 | + | <interface name="wp_linux_drm_syncobj_timeline_v1" version="1"> | |
| 112 | + | <description summary="synchronization object timeline"> | |
| 113 | + | This object represents an explicit synchronization object timeline | |
| 114 | + | imported by the client to the compositor. | |
| 115 | + | </description> | |
| 116 | + | ||
| 117 | + | <request name="destroy" type="destructor"> | |
| 118 | + | <description summary="destroy the timeline"> | |
| 119 | + | Destroy the synchronization object timeline. Other objects are not | |
| 120 | + | affected by this request, in particular timeline points set by | |
| 121 | + | set_acquire_point and set_release_point are not unset. | |
| 122 | + | </description> | |
| 123 | + | </request> | |
| 124 | + | </interface> | |
| 125 | + | ||
| 126 | + | <interface name="wp_linux_drm_syncobj_surface_v1" version="1"> | |
| 127 | + | <description summary="per-surface explicit synchronization"> | |
| 128 | + | This object is an add-on interface for wl_surface to enable explicit | |
| 129 | + | synchronization. | |
| 130 | + | ||
| 131 | + | Each surface can be associated with only one object of this interface at | |
| 132 | + | any time. | |
| 133 | + | ||
| 134 | + | Explicit synchronization is guaranteed to be supported for buffers | |
| 135 | + | created with any version of the linux-dmabuf protocol. Compositors are | |
| 136 | + | free to support explicit synchronization for additional buffer types. | |
| 137 | + | If at surface commit time the attached buffer does not support explicit | |
| 138 | + | synchronization, an unsupported_buffer error is raised. | |
| 139 | + | ||
| 140 | + | As long as the wp_linux_drm_syncobj_surface_v1 object is alive, the | |
| 141 | + | compositor may ignore implicit synchronization for buffers attached and | |
| 142 | + | committed to the wl_surface. The delivery of wl_buffer.release events | |
| 143 | + | for buffers attached to the surface becomes undefined. | |
| 144 | + | ||
| 145 | + | Clients must set both acquire and release points if and only if a | |
| 146 | + | non-null buffer is attached in the same surface commit. See the | |
| 147 | + | no_buffer, no_acquire_point and no_release_point protocol errors. | |
| 148 | + | ||
| 149 | + | If at surface commit time the acquire and release DRM syncobj timelines | |
| 150 | + | are identical, the acquire point value must be strictly less than the | |
| 151 | + | release point value, or else the conflicting_points protocol error is | |
| 152 | + | raised. | |
| 153 | + | </description> | |
| 154 | + | ||
| 155 | + | <request name="destroy" type="destructor"> | |
| 156 | + | <description summary="destroy the surface synchronization object"> | |
| 157 | + | Destroy this surface synchronization object. | |
| 158 | + | ||
| 159 | + | Any timeline point set by this object with set_acquire_point or | |
| 160 | + | set_release_point since the last commit may be discarded by the | |
| 161 | + | compositor. Any timeline point set by this object before the last | |
| 162 | + | commit will not be affected. | |
| 163 | + | </description> | |
| 164 | + | </request> | |
| 165 | + | ||
| 166 | + | <enum name="error"> | |
| 167 | + | <entry name="no_surface" value="1" | |
| 168 | + | summary="the associated wl_surface was destroyed"/> | |
| 169 | + | <entry name="unsupported_buffer" value="2" | |
| 170 | + | summary="the buffer does not support explicit synchronization"/> | |
| 171 | + | <entry name="no_buffer" value="3" summary="no buffer was attached"/> | |
| 172 | + | <entry name="no_acquire_point" value="4" | |
| 173 | + | summary="no acquire timeline point was set"/> | |
| 174 | + | <entry name="no_release_point" value="5" | |
| 175 | + | summary="no release timeline point was set"/> | |
| 176 | + | <entry name="conflicting_points" value="6" | |
| 177 | + | summary="acquire and release timeline points are in conflict"/> | |
| 178 | + | </enum> | |
| 179 | + | ||
| 180 | + | <request name="set_acquire_point"> | |
| 181 | + | <description summary="set the acquire timeline point"> | |
| 182 | + | Set the timeline point that must be signalled before the compositor may | |
| 183 | + | sample from the buffer attached with wl_surface.attach. | |
| 184 | + | ||
| 185 | + | The 64-bit unsigned value combined from point_hi and point_lo is the | |
| 186 | + | point value. | |
| 187 | + | ||
| 188 | + | The acquire point is double-buffered state, and will be applied on the | |
| 189 | + | next wl_surface.commit request for the associated surface. Thus, it | |
| 190 | + | applies only to the buffer that is attached to the surface at commit | |
| 191 | + | time. | |
| 192 | + | ||
| 193 | + | If an acquire point has already been attached during the same commit | |
| 194 | + | cycle, the new point replaces the old one. | |
| 195 | + | ||
| 196 | + | If the associated wl_surface was destroyed, a no_surface error is | |
| 197 | + | raised. | |
| 198 | + | ||
| 199 | + | If at surface commit time there is a pending acquire timeline point set | |
| 200 | + | but no pending buffer attached, a no_buffer error is raised. If at | |
| 201 | + | surface commit time there is a pending buffer attached but no pending | |
| 202 | + | acquire timeline point set, the no_acquire_point protocol error is | |
| 203 | + | raised. | |
| 204 | + | </description> | |
| 205 | + | <arg name="timeline" type="object" interface="wp_linux_drm_syncobj_timeline_v1"/> | |
| 206 | + | <arg name="point_hi" type="uint" summary="high 32 bits of the point value"/> | |
| 207 | + | <arg name="point_lo" type="uint" summary="low 32 bits of the point value"/> | |
| 208 | + | </request> | |
| 209 | + | ||
| 210 | + | <request name="set_release_point"> | |
| 211 | + | <description summary="set the release timeline point"> | |
| 212 | + | Set the timeline point that must be signalled by the compositor when it | |
| 213 | + | has finished its usage of the buffer attached with wl_surface.attach | |
| 214 | + | for the relevant commit. | |
| 215 | + | ||
| 216 | + | Once the timeline point is signaled, and assuming the associated buffer | |
| 217 | + | is not pending release from other wl_surface.commit requests, no | |
| 218 | + | additional explicit or implicit synchronization with the compositor is | |
| 219 | + | required to safely re-use the buffer. | |
| 220 | + | ||
| 221 | + | Note that clients cannot rely on the release point being always | |
| 222 | + | signaled after the acquire point: compositors may release buffers | |
| 223 | + | without ever reading from them. In addition, the compositor may use | |
| 224 | + | different presentation paths for different commits, which may have | |
| 225 | + | different release behavior. As a result, the compositor may signal the | |
| 226 | + | release points in a different order than the client committed them. | |
| 227 | + | ||
| 228 | + | Because signaling a timeline point also signals every previous point, | |
| 229 | + | it is generally not safe to use the same timeline object for the | |
| 230 | + | release points of multiple buffers. The out-of-order signaling | |
| 231 | + | described above may lead to a release point being signaled before the | |
| 232 | + | compositor has finished reading. To avoid this, it is strongly | |
| 233 | + | recommended that each buffer should use a separate timeline for its | |
| 234 | + | release points. | |
| 235 | + | ||
| 236 | + | The 64-bit unsigned value combined from point_hi and point_lo is the | |
| 237 | + | point value. | |
| 238 | + | ||
| 239 | + | The release point is double-buffered state, and will be applied on the | |
| 240 | + | next wl_surface.commit request for the associated surface. Thus, it | |
| 241 | + | applies only to the buffer that is attached to the surface at commit | |
| 242 | + | time. | |
| 243 | + | ||
| 244 | + | If a release point has already been attached during the same commit | |
| 245 | + | cycle, the new point replaces the old one. | |
| 246 | + | ||
| 247 | + | If the associated wl_surface was destroyed, a no_surface error is | |
| 248 | + | raised. | |
| 249 | + | ||
| 250 | + | If at surface commit time there is a pending release timeline point set | |
| 251 | + | but no pending buffer attached, a no_buffer error is raised. If at | |
| 252 | + | surface commit time there is a pending buffer attached but no pending | |
| 253 | + | release timeline point set, the no_release_point protocol error is | |
| 254 | + | raised. | |
| 255 | + | </description> | |
| 256 | + | <arg name="timeline" type="object" interface="wp_linux_drm_syncobj_timeline_v1"/> | |
| 257 | + | <arg name="point_hi" type="uint" summary="high 32 bits of the point value"/> | |
| 258 | + | <arg name="point_lo" type="uint" summary="low 32 bits of the point value"/> | |
| 259 | + | </request> | |
| 260 | + | </interface> | |
| 261 | + | </protocol> | |
A protocol/drmsyncobj/linux-drm-syncobj-v1_generated.go +227 -0
| @@ -0,0 +1,227 @@ | |||
| 1 | + | // Code generated by wlturbo-scanner. DO NOT EDIT. | |
| 2 | + | // Protocol: linux_drm_syncobj_v1 | |
| 3 | + | // Source: linux-drm-syncobj-v1.xml | |
| 4 | + | ||
| 5 | + | package drmsyncobj | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | cross_wl_surface "github.com/bnema/wlturbo/protocol/core" | |
| 9 | + | "github.com/bnema/wlturbo/wl" | |
| 10 | + | ) | |
| 11 | + | ||
| 12 | + | // ERROR_SURFACE_EXISTS is the error surface_exists value. | |
| 13 | + | const ERROR_SURFACE_EXISTS = 0 | |
| 14 | + | ||
| 15 | + | // ERROR_INVALID_TIMELINE is the error invalid_timeline value. | |
| 16 | + | const ERROR_INVALID_TIMELINE = 1 | |
| 17 | + | ||
| 18 | + | // ERROR_NO_SURFACE is the error no_surface value. | |
| 19 | + | const ERROR_NO_SURFACE = 1 | |
| 20 | + | ||
| 21 | + | // ERROR_UNSUPPORTED_BUFFER is the error unsupported_buffer value. | |
| 22 | + | const ERROR_UNSUPPORTED_BUFFER = 2 | |
| 23 | + | ||
| 24 | + | // ERROR_NO_BUFFER is the error no_buffer value. | |
| 25 | + | const ERROR_NO_BUFFER = 3 | |
| 26 | + | ||
| 27 | + | // ERROR_NO_ACQUIRE_POINT is the error no_acquire_point value. | |
| 28 | + | const ERROR_NO_ACQUIRE_POINT = 4 | |
| 29 | + | ||
| 30 | + | // ERROR_NO_RELEASE_POINT is the error no_release_point value. | |
| 31 | + | const ERROR_NO_RELEASE_POINT = 5 | |
| 32 | + | ||
| 33 | + | // ERROR_CONFLICTING_POINTS is the error conflicting_points value. | |
| 34 | + | const ERROR_CONFLICTING_POINTS = 6 | |
| 35 | + | ||
| 36 | + | // WpLinuxDrmSyncobjManagerInterface is the protocol interface name. | |
| 37 | + | const WpLinuxDrmSyncobjManagerInterface = "wp_linux_drm_syncobj_manager_v1" | |
| 38 | + | ||
| 39 | + | // WpLinuxDrmSyncobjManager is the generated binding for wp_linux_drm_syncobj_manager_v1 version 1. | |
| 40 | + | type WpLinuxDrmSyncobjManager struct { | |
| 41 | + | wl.BaseProxy | |
| 42 | + | } | |
| 43 | + | ||
| 44 | + | // NewWpLinuxDrmSyncobjManager returns an unbound WpLinuxDrmSyncobjManager. | |
| 45 | + | func NewWpLinuxDrmSyncobjManager(ctx *wl.Context) *WpLinuxDrmSyncobjManager { | |
| 46 | + | obj := &WpLinuxDrmSyncobjManager{} | |
| 47 | + | obj.SetContext(ctx) | |
| 48 | + | ctx.Register(obj) | |
| 49 | + | return obj | |
| 50 | + | } | |
| 51 | + | ||
| 52 | + | // Destroy this explicit synchronization factory object. Other objects shall not be affected by this request. | |
| 53 | + | func (o *WpLinuxDrmSyncobjManager) Destroy() error { | |
| 54 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 55 | + | return err | |
| 56 | + | } | |
| 57 | + | if err := o.Context().SendDestructor(o, 0); err != nil { | |
| 58 | + | return err | |
| 59 | + | } | |
| 60 | + | return nil | |
| 61 | + | } | |
| 62 | + | ||
| 63 | + | // Instantiate an interface extension for the given wl_surface to provide explicit synchronization. If the given wl_surface already has an explicit synchronization object associated, the surface_exists protocol error is raised. Graphics APIs, like EGL or Vulkan, that manage the buffer queue and commits of a wl_surface themselves, are likely to be using this extension internally. If a client is using such an API for a wl_surface, it should not directly use this extension on that surface, to avoid raising a surface_exists protocol error. | |
| 64 | + | func (o *WpLinuxDrmSyncobjManager) GetSurface(surface *cross_wl_surface.Surface) (*WpLinuxDrmSyncobjSurface, error) { | |
| 65 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 66 | + | return nil, err | |
| 67 | + | } | |
| 68 | + | var arg1 wl.Object | |
| 69 | + | if surface != nil { | |
| 70 | + | arg1 = surface | |
| 71 | + | } | |
| 72 | + | child := &WpLinuxDrmSyncobjSurface{} | |
| 73 | + | child.SetContext(o.Context()) | |
| 74 | + | child.SetID(o.Context().AllocateID()) | |
| 75 | + | o.Context().Register(child) | |
| 76 | + | if err := o.Context().SendRequest(o, 1, child, arg1); err != nil { | |
| 77 | + | o.Context().Unregister(child) | |
| 78 | + | return nil, err | |
| 79 | + | } | |
| 80 | + | return child, nil | |
| 81 | + | } | |
| 82 | + | ||
| 83 | + | // Import a DRM synchronization object timeline. If the FD cannot be imported, the invalid_timeline error is raised. | |
| 84 | + | func (o *WpLinuxDrmSyncobjManager) ImportTimeline(fd int) (*WpLinuxDrmSyncobjTimeline, error) { | |
| 85 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 86 | + | return nil, err | |
| 87 | + | } | |
| 88 | + | child := &WpLinuxDrmSyncobjTimeline{} | |
| 89 | + | child.SetContext(o.Context()) | |
| 90 | + | child.SetID(o.Context().AllocateID()) | |
| 91 | + | o.Context().Register(child) | |
| 92 | + | if err := o.Context().SendRequestWithFDs(o, 2, []int{fd}, child, uintptr(fd)); err != nil { | |
| 93 | + | o.Context().Unregister(child) | |
| 94 | + | return nil, err | |
| 95 | + | } | |
| 96 | + | _ = wl.CloseSentFD(fd) | |
| 97 | + | return child, nil | |
| 98 | + | } | |
| 99 | + | ||
| 100 | + | // EventSignature returns the declared event argument types in wire order. | |
| 101 | + | func (o *WpLinuxDrmSyncobjManager) EventSignature(opcode uint16) (string, bool) { | |
| 102 | + | switch opcode { | |
| 103 | + | default: | |
| 104 | + | return "", false | |
| 105 | + | } | |
| 106 | + | } | |
| 107 | + | ||
| 108 | + | // Dispatch decodes one event on wp_linux_drm_syncobj_manager_v1 and calls the registered handlers. | |
| 109 | + | func (o *WpLinuxDrmSyncobjManager) Dispatch(event *wl.Event) { | |
| 110 | + | switch event.Opcode { | |
| 111 | + | default: | |
| 112 | + | } | |
| 113 | + | } | |
| 114 | + | ||
| 115 | + | // WpLinuxDrmSyncobjTimelineInterface is the protocol interface name. | |
| 116 | + | const WpLinuxDrmSyncobjTimelineInterface = "wp_linux_drm_syncobj_timeline_v1" | |
| 117 | + | ||
| 118 | + | // WpLinuxDrmSyncobjTimeline is the generated binding for wp_linux_drm_syncobj_timeline_v1 version 1. | |
| 119 | + | type WpLinuxDrmSyncobjTimeline struct { | |
| 120 | + | wl.BaseProxy | |
| 121 | + | } | |
| 122 | + | ||
| 123 | + | // NewWpLinuxDrmSyncobjTimeline returns an unbound WpLinuxDrmSyncobjTimeline. | |
| 124 | + | func NewWpLinuxDrmSyncobjTimeline(ctx *wl.Context) *WpLinuxDrmSyncobjTimeline { | |
| 125 | + | obj := &WpLinuxDrmSyncobjTimeline{} | |
| 126 | + | obj.SetContext(ctx) | |
| 127 | + | ctx.Register(obj) | |
| 128 | + | return obj | |
| 129 | + | } | |
| 130 | + | ||
| 131 | + | // Destroy the synchronization object timeline. Other objects are not affected by this request, in particular timeline points set by set_acquire_point and set_release_point are not unset. | |
| 132 | + | func (o *WpLinuxDrmSyncobjTimeline) Destroy() error { | |
| 133 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 134 | + | return err | |
| 135 | + | } | |
| 136 | + | if err := o.Context().SendDestructor(o, 0); err != nil { | |
| 137 | + | return err | |
| 138 | + | } | |
| 139 | + | return nil | |
| 140 | + | } | |
| 141 | + | ||
| 142 | + | // EventSignature returns the declared event argument types in wire order. | |
| 143 | + | func (o *WpLinuxDrmSyncobjTimeline) EventSignature(opcode uint16) (string, bool) { | |
| 144 | + | switch opcode { | |
| 145 | + | default: | |
| 146 | + | return "", false | |
| 147 | + | } | |
| 148 | + | } | |
| 149 | + | ||
| 150 | + | // Dispatch decodes one event on wp_linux_drm_syncobj_timeline_v1 and calls the registered handlers. | |
| 151 | + | func (o *WpLinuxDrmSyncobjTimeline) Dispatch(event *wl.Event) { | |
| 152 | + | switch event.Opcode { | |
| 153 | + | default: | |
| 154 | + | } | |
| 155 | + | } | |
| 156 | + | ||
| 157 | + | // WpLinuxDrmSyncobjSurfaceInterface is the protocol interface name. | |
| 158 | + | const WpLinuxDrmSyncobjSurfaceInterface = "wp_linux_drm_syncobj_surface_v1" | |
| 159 | + | ||
| 160 | + | // WpLinuxDrmSyncobjSurface is the generated binding for wp_linux_drm_syncobj_surface_v1 version 1. | |
| 161 | + | type WpLinuxDrmSyncobjSurface struct { | |
| 162 | + | wl.BaseProxy | |
| 163 | + | } | |
| 164 | + | ||
| 165 | + | // NewWpLinuxDrmSyncobjSurface returns an unbound WpLinuxDrmSyncobjSurface. | |
| 166 | + | func NewWpLinuxDrmSyncobjSurface(ctx *wl.Context) *WpLinuxDrmSyncobjSurface { | |
| 167 | + | obj := &WpLinuxDrmSyncobjSurface{} | |
| 168 | + | obj.SetContext(ctx) | |
| 169 | + | ctx.Register(obj) | |
| 170 | + | return obj | |
| 171 | + | } | |
| 172 | + | ||
| 173 | + | // Destroy this surface synchronization object. Any timeline point set by this object with set_acquire_point or set_release_point since the last commit may be discarded by the compositor. Any timeline point set by this object before the last commit will not be affected. | |
| 174 | + | func (o *WpLinuxDrmSyncobjSurface) Destroy() error { | |
| 175 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 176 | + | return err | |
| 177 | + | } | |
| 178 | + | if err := o.Context().SendDestructor(o, 0); err != nil { | |
| 179 | + | return err | |
| 180 | + | } | |
| 181 | + | return nil | |
| 182 | + | } | |
| 183 | + | ||
| 184 | + | // Set the timeline point that must be signalled before the compositor may sample from the buffer attached with wl_surface.attach. The 64-bit unsigned value combined from point_hi and point_lo is the point value. The acquire point is double-buffered state, and will be applied on the next wl_surface.commit request for the associated surface. Thus, it applies only to the buffer that is attached to the surface at commit time. If an acquire point has already been attached during the same commit cycle, the new point replaces the old one. If the associated wl_surface was destroyed, a no_surface error is raised. If at surface commit time there is a pending acquire timeline point set but no pending buffer attached, a no_buffer error is raised. If at surface commit time there is a pending buffer attached but no pending acquire timeline point set, the no_acquire_point protocol error is raised. | |
| 185 | + | func (o *WpLinuxDrmSyncobjSurface) SetAcquirePoint(timeline *WpLinuxDrmSyncobjTimeline, pointHi uint32, pointLo uint32) error { | |
| 186 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 187 | + | return err | |
| 188 | + | } | |
| 189 | + | var arg0 wl.Object | |
| 190 | + | if timeline != nil { | |
| 191 | + | arg0 = timeline | |
| 192 | + | } | |
| 193 | + | if err := o.Context().SendRequest(o, 1, arg0, pointHi, pointLo); err != nil { | |
| 194 | + | return err | |
| 195 | + | } | |
| 196 | + | return nil | |
| 197 | + | } | |
| 198 | + | ||
| 199 | + | // Set the timeline point that must be signalled by the compositor when it has finished its usage of the buffer attached with wl_surface.attach for the relevant commit. Once the timeline point is signaled, and assuming the associated buffer is not pending release from other wl_surface.commit requests, no additional explicit or implicit synchronization with the compositor is required to safely re-use the buffer. Note that clients cannot rely on the release point being always signaled after the acquire point: compositors may release buffers without ever reading from them. In addition, the compositor may use different presentation paths for different commits, which may have different release behavior. As a result, the compositor may signal the release points in a different order than the client committed them. Because signaling a timeline point also signals every previous point, it is generally not safe to use the same timeline object for the release points of multiple buffers. The out-of-order signaling described above may lead to a release point being signaled before the compositor has finished reading. To avoid this, it is strongly recommended that each buffer should use a separate timeline for its release points. The 64-bit unsigned value combined from point_hi and point_lo is the point value. The release point is double-buffered state, and will be applied on the next wl_surface.commit request for the associated surface. Thus, it applies only to the buffer that is attached to the surface at commit time. If a release point has already been attached during the same commit cycle, the new point replaces the old one. If the associated wl_surface was destroyed, a no_surface error is raised. If at surface commit time there is a pending release timeline point set but no pending buffer attached, a no_buffer error is raised. If at surface commit time there is a pending buffer attached but no pending release timeline point set, the no_release_point protocol error is raised. | |
| 200 | + | func (o *WpLinuxDrmSyncobjSurface) SetReleasePoint(timeline *WpLinuxDrmSyncobjTimeline, pointHi uint32, pointLo uint32) error { | |
| 201 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 202 | + | return err | |
| 203 | + | } | |
| 204 | + | var arg0 wl.Object | |
| 205 | + | if timeline != nil { | |
| 206 | + | arg0 = timeline | |
| 207 | + | } | |
| 208 | + | if err := o.Context().SendRequest(o, 2, arg0, pointHi, pointLo); err != nil { | |
| 209 | + | return err | |
| 210 | + | } | |
| 211 | + | return nil | |
| 212 | + | } | |
| 213 | + | ||
| 214 | + | // EventSignature returns the declared event argument types in wire order. | |
| 215 | + | func (o *WpLinuxDrmSyncobjSurface) EventSignature(opcode uint16) (string, bool) { | |
| 216 | + | switch opcode { | |
| 217 | + | default: | |
| 218 | + | return "", false | |
| 219 | + | } | |
| 220 | + | } | |
| 221 | + | ||
| 222 | + | // Dispatch decodes one event on wp_linux_drm_syncobj_surface_v1 and calls the registered handlers. | |
| 223 | + | func (o *WpLinuxDrmSyncobjSurface) Dispatch(event *wl.Event) { | |
| 224 | + | switch event.Opcode { | |
| 225 | + | default: | |
| 226 | + | } | |
| 227 | + | } | |
A protocol/extensions_test.go +478 -0
| @@ -0,0 +1,478 @@ | |||
| 1 | + | //go:build linux | |
| 2 | + | ||
| 3 | + | package protocol_test | |
| 4 | + | ||
| 5 | + | import ( | |
| 6 | + | "encoding/binary" | |
| 7 | + | "net" | |
| 8 | + | "os" | |
| 9 | + | "strings" | |
| 10 | + | "testing" | |
| 11 | + | "time" | |
| 12 | + | ||
| 13 | + | "github.com/bnema/wlturbo" | |
| 14 | + | "github.com/bnema/wlturbo/protocol/core" | |
| 15 | + | "github.com/bnema/wlturbo/protocol/drmsyncobj" | |
| 16 | + | "github.com/bnema/wlturbo/protocol/fractionalscale" | |
| 17 | + | "github.com/bnema/wlturbo/protocol/linuxdmabuf" | |
| 18 | + | "github.com/bnema/wlturbo/protocol/textinput" | |
| 19 | + | "github.com/bnema/wlturbo/protocol/viewporter" | |
| 20 | + | "github.com/bnema/wlturbo/protocol/xdgshell" | |
| 21 | + | "golang.org/x/sys/unix" | |
| 22 | + | ) | |
| 23 | + | ||
| 24 | + | func pair(t *testing.T) (*net.UnixConn, *net.UnixConn) { | |
| 25 | + | t.Helper() | |
| 26 | + | fds, e := unix.Socketpair(unix.AF_UNIX, unix.SOCK_STREAM|unix.SOCK_CLOEXEC, 0) | |
| 27 | + | if e != nil { | |
| 28 | + | t.Fatal(e) | |
| 29 | + | } | |
| 30 | + | a := os.NewFile(uintptr(fds[0]), "a") | |
| 31 | + | b := os.NewFile(uintptr(fds[1]), "b") | |
| 32 | + | ca, e := net.FileConn(a) | |
| 33 | + | a.Close() | |
| 34 | + | if e != nil { | |
| 35 | + | t.Fatal(e) | |
| 36 | + | } | |
| 37 | + | cb, e := net.FileConn(b) | |
| 38 | + | b.Close() | |
| 39 | + | if e != nil { | |
| 40 | + | t.Fatal(e) | |
| 41 | + | } | |
| 42 | + | t.Cleanup(func() { ca.Close(); cb.Close() }) | |
| 43 | + | return ca.(*net.UnixConn), cb.(*net.UnixConn) | |
| 44 | + | } | |
| 45 | + | func msg(id uint32, op uint16, body ...uint32) []byte { | |
| 46 | + | b := make([]byte, 8+4*len(body)) | |
| 47 | + | binary.NativeEndian.PutUint32(b, id) | |
| 48 | + | binary.NativeEndian.PutUint32(b[4:], uint32(len(b))<<16|uint32(op)) | |
| 49 | + | for i, v := range body { | |
| 50 | + | binary.NativeEndian.PutUint32(b[8+i*4:], v) | |
| 51 | + | } | |
| 52 | + | return b | |
| 53 | + | } | |
| 54 | + | func payload(id uint32, op uint16, b ...byte) []byte { | |
| 55 | + | p := make([]byte, 8+len(b)) | |
| 56 | + | copy(p, msg(id, op)) | |
| 57 | + | copy(p[8:], b) | |
| 58 | + | binary.NativeEndian.PutUint32(p[4:], uint32(len(p))<<16|uint32(op)) | |
| 59 | + | return p | |
| 60 | + | } | |
| 61 | + | func arr(b []byte) []byte { | |
| 62 | + | out := make([]byte, 4+(len(b)+3)&^3) | |
| 63 | + | binary.NativeEndian.PutUint32(out, uint32(len(b))) | |
| 64 | + | copy(out[4:], b) | |
| 65 | + | return out | |
| 66 | + | } | |
| 67 | + | func str(s string) []byte { return arr(append([]byte(s), 0)) } | |
| 68 | + | func send(t *testing.T, p *net.UnixConn, b []byte, fd ...int) { | |
| 69 | + | t.Helper() | |
| 70 | + | var rights []byte | |
| 71 | + | if len(fd) > 0 { | |
| 72 | + | rights = unix.UnixRights(fd...) | |
| 73 | + | } | |
| 74 | + | if _, _, e := p.WriteMsgUnix(b, rights, nil); e != nil { | |
| 75 | + | t.Fatal(e) | |
| 76 | + | } | |
| 77 | + | } | |
| 78 | + | func request(t *testing.T, p *net.UnixConn) (uint32, uint16, []byte) { | |
| 79 | + | t.Helper() | |
| 80 | + | p.SetReadDeadline(time.Now().Add(2 * time.Second)) | |
| 81 | + | b := make([]byte, 4096) | |
| 82 | + | n, e := p.Read(b) | |
| 83 | + | if e != nil { | |
| 84 | + | t.Fatal(e) | |
| 85 | + | } | |
| 86 | + | if n < 8 { | |
| 87 | + | t.Fatal("short request") | |
| 88 | + | } | |
| 89 | + | return binary.NativeEndian.Uint32(b), uint16(binary.NativeEndian.Uint32(b[4:])), b[8:n] | |
| 90 | + | } | |
| 91 | + | func noerr(t *testing.T, e error) { | |
| 92 | + | t.Helper() | |
| 93 | + | if e != nil { | |
| 94 | + | t.Fatal(e) | |
| 95 | + | } | |
| 96 | + | } | |
| 97 | + | func bind(t *testing.T, d *wlturbo.Display, p *net.UnixConn, name uint32, iface string, announced uint32, supported uint32, proxy wlturbo.Proxy) { | |
| 98 | + | t.Helper() | |
| 99 | + | // A fake server must advertise a global before clients can negotiate it. | |
| 100 | + | announcement := append(msg(0, 0, name)[8:], str(iface)...) | |
| 101 | + | announcement = append(announcement, msg(0, 0, announced)[8:]...) | |
| 102 | + | send(t, p, payload(d.Registry().ID(), 0, announcement...)) | |
| 103 | + | noerr(t, d.Dispatch()) | |
| 104 | + | v, err := d.Registry().BindNegotiated(iface, supported, proxy) | |
| 105 | + | noerr(t, err) | |
| 106 | + | want := min(announced, supported) | |
| 107 | + | _, op, b := request(t, p) | |
| 108 | + | if op != 0 || v != want || binary.NativeEndian.Uint32(b) != name || binary.NativeEndian.Uint32(b[len(b)-8:]) != want || binary.NativeEndian.Uint32(b[len(b)-4:]) != proxy.ID() { | |
| 109 | + | t.Fatalf("bad bind %s announced=%d supported=%d negotiated=%d wire=%x", iface, announced, supported, v, b) | |
| 110 | + | } | |
| 111 | + | } | |
| 112 | + | func TestExtensionsOverSocketpair(t *testing.T) { | |
| 113 | + | c, p := pair(t) | |
| 114 | + | d, e := wlturbo.ConnectFromConn(c) | |
| 115 | + | noerr(t, e) | |
| 116 | + | defer d.Close() | |
| 117 | + | request(t, p) | |
| 118 | + | // A registry announcement is the only input to optional-global discovery. | |
| 119 | + | send(t, p, payload(d.Registry().ID(), 0, append(msg(0, 0, 11)[8:12], append(str(fractionalscale.WpFractionalScaleManagerInterface), msg(0, 0, 1)[8:12]...)...)...)) | |
| 120 | + | noerr(t, d.Dispatch()) | |
| 121 | + | if g, ok := d.Registry().FindGlobal(fractionalscale.WpFractionalScaleManagerInterface); !ok || g.Version != 1 { | |
| 122 | + | t.Fatal("announced fractional scale missing", g, ok) | |
| 123 | + | } | |
| 124 | + | if _, ok := d.Registry().FindGlobal(textinput.TextInputManagerV3Interface); ok { | |
| 125 | + | t.Fatal("absent text input") | |
| 126 | + | } | |
| 127 | + | // Remove the discovery-only announcement before the bind matrix assigns | |
| 128 | + | // its own global name to this interface. | |
| 129 | + | send(t, p, msg(d.Registry().ID(), 1, 11)) | |
| 130 | + | noerr(t, d.Dispatch()) | |
| 131 | + | surface := core.NewSurface(d.Context()) | |
| 132 | + | surface.SetID(d.AllocateID()) | |
| 133 | + | d.Context().Register(surface) | |
| 134 | + | seat := core.NewSeat(d.Context()) | |
| 135 | + | seat.SetID(d.AllocateID()) | |
| 136 | + | d.Context().Register(seat) | |
| 137 | + | wm := xdgshell.NewXdgWmBase(d.Context()) | |
| 138 | + | bind(t, d, p, 1, xdgshell.XdgWmBaseInterface, 6, 7, wm) | |
| 139 | + | xs, e := wm.GetXdgSurface(surface) | |
| 140 | + | noerr(t, e) | |
| 141 | + | request(t, p) | |
| 142 | + | top, e := xs.GetToplevel() | |
| 143 | + | noerr(t, e) | |
| 144 | + | request(t, p) | |
| 145 | + | var sequence []string | |
| 146 | + | top.OnConfigure(func(w, h int32, states []byte) { | |
| 147 | + | if w != 800 || h != 600 { | |
| 148 | + | t.Fatalf("size %d,%d", w, h) | |
| 149 | + | } | |
| 150 | + | sequence = append(sequence, "top") | |
| 151 | + | }) | |
| 152 | + | xs.OnConfigure(func(serial uint32) { sequence = append(sequence, "surface"); noerr(t, xs.AckConfigure(serial)) }) | |
| 153 | + | send(t, p, payload(top.ID(), 0, append(msg(0, 0, 800, 600)[8:], arr(nil)...)...)) | |
| 154 | + | noerr(t, d.Dispatch()) | |
| 155 | + | send(t, p, msg(xs.ID(), 0, 9)) | |
| 156 | + | noerr(t, d.Dispatch()) | |
| 157 | + | _, op, b := request(t, p) | |
| 158 | + | if op != 4 || binary.NativeEndian.Uint32(b) != 9 || strings.Join(sequence, ",") != "top,surface" { | |
| 159 | + | t.Fatalf("xdg %d %x %v", op, b, sequence) | |
| 160 | + | } | |
| 161 | + | noerr(t, top.Destroy()) | |
| 162 | + | request(t, p) | |
| 163 | + | noerr(t, xs.Destroy()) | |
| 164 | + | request(t, p) | |
| 165 | + | noerr(t, wm.Destroy()) | |
| 166 | + | request(t, p) | |
| 167 | + | dm := linuxdmabuf.NewLinuxDmabuf(d.Context()) | |
| 168 | + | bind(t, d, p, 2, linuxdmabuf.LinuxDmabufInterface, 6, 4, dm) | |
| 169 | + | fb, e := dm.GetDefaultFeedback() | |
| 170 | + | noerr(t, e) | |
| 171 | + | request(t, p) | |
| 172 | + | table, e := os.CreateTemp(t.TempDir(), "table") | |
| 173 | + | noerr(t, e) | |
| 174 | + | var bytes [32]byte | |
| 175 | + | binary.NativeEndian.PutUint32(bytes[:], 0x34325258) | |
| 176 | + | binary.NativeEndian.PutUint64(bytes[8:], 19) | |
| 177 | + | binary.NativeEndian.PutUint32(bytes[16:], 0x34325241) | |
| 178 | + | binary.NativeEndian.PutUint64(bytes[24:], 42) | |
| 179 | + | _, e = table.Write(bytes[:]) | |
| 180 | + | noerr(t, e) | |
| 181 | + | defer table.Close() | |
| 182 | + | var entries []linuxdmabuf.FormatEntry | |
| 183 | + | var indices []uint16 | |
| 184 | + | var feedback []string | |
| 185 | + | fb.OnMainDevice(func(dev []byte) { | |
| 186 | + | if len(dev) != 8 { | |
| 187 | + | t.Fatalf("device %x", dev) | |
| 188 | + | } | |
| 189 | + | feedback = append(feedback, "main") | |
| 190 | + | }) | |
| 191 | + | fb.OnFormatTable(func(fd *wlturbo.OwnedFD, size uint32) { | |
| 192 | + | entries, e = linuxdmabuf.ReadFormatTable(fd, size) | |
| 193 | + | noerr(t, e) | |
| 194 | + | feedback = append(feedback, "table") | |
| 195 | + | }) | |
| 196 | + | fb.OnTrancheTargetDevice(func(dev []byte) { feedback = append(feedback, "target") }) | |
| 197 | + | fb.OnTrancheFormats(func(data []byte) { | |
| 198 | + | indices, e = linuxdmabuf.TrancheIndices(data, len(entries)) | |
| 199 | + | noerr(t, e) | |
| 200 | + | feedback = append(feedback, "formats") | |
| 201 | + | }) | |
| 202 | + | fb.OnTrancheFlags(func(flags uint32) { | |
| 203 | + | if flags != 1 { | |
| 204 | + | t.Fatal(flags) | |
| 205 | + | } | |
| 206 | + | feedback = append(feedback, "flags") | |
| 207 | + | }) | |
| 208 | + | fb.OnTrancheDone(func() { feedback = append(feedback, "tranche") }) | |
| 209 | + | fb.OnDone(func() { feedback = append(feedback, "done") }) | |
| 210 | + | send(t, p, payload(fb.ID(), 2, arr(make([]byte, 8))...)) | |
| 211 | + | noerr(t, d.Dispatch()) | |
| 212 | + | send(t, p, msg(fb.ID(), 1, 32), int(table.Fd())) | |
| 213 | + | noerr(t, d.Dispatch()) | |
| 214 | + | send(t, p, payload(fb.ID(), 4, arr(make([]byte, 8))...)) | |
| 215 | + | noerr(t, d.Dispatch()) | |
| 216 | + | send(t, p, payload(fb.ID(), 5, arr([]byte{0, 0, 1, 0})...)) | |
| 217 | + | noerr(t, d.Dispatch()) | |
| 218 | + | send(t, p, msg(fb.ID(), 6, 1)) | |
| 219 | + | noerr(t, d.Dispatch()) | |
| 220 | + | send(t, p, msg(fb.ID(), 3)) | |
| 221 | + | noerr(t, d.Dispatch()) | |
| 222 | + | send(t, p, msg(fb.ID(), 0)) | |
| 223 | + | noerr(t, d.Dispatch()) | |
| 224 | + | if len(entries) != 2 || entries[1].Modifier != 42 || len(indices) != 2 || indices[1] != 1 || strings.Join(feedback, ",") != "main,table,target,formats,flags,tranche,done" { | |
| 225 | + | t.Fatalf("feedback %v %v %v", entries, indices, feedback) | |
| 226 | + | } | |
| 227 | + | noerr(t, fb.Destroy()) | |
| 228 | + | request(t, p) | |
| 229 | + | params, e := dm.CreateParams() | |
| 230 | + | noerr(t, e) | |
| 231 | + | request(t, p) | |
| 232 | + | f, e := os.Open("/dev/null") | |
| 233 | + | noerr(t, e) | |
| 234 | + | noerr(t, params.Add(int(f.Fd()), 0, 0, 4, 0, 0)) | |
| 235 | + | request(t, p) | |
| 236 | + | if _, e := unix.FcntlInt(f.Fd(), unix.F_GETFD, 0); e == nil { | |
| 237 | + | t.Fatal("sent fd not closed") | |
| 238 | + | } | |
| 239 | + | f.Close() | |
| 240 | + | var buffer *core.Buffer | |
| 241 | + | params.OnCreated(func(b *core.Buffer) { buffer = b }) | |
| 242 | + | params.OnFailed(func() { sequence = append(sequence, "failed") }) | |
| 243 | + | noerr(t, params.Create(1, 1, 0x34325258, 0)) | |
| 244 | + | request(t, p) | |
| 245 | + | send(t, p, msg(params.ID(), 0, 999)) | |
| 246 | + | noerr(t, d.Dispatch()) | |
| 247 | + | if buffer == nil || buffer.ID() != 999 { | |
| 248 | + | t.Fatal("typed buffer missing") | |
| 249 | + | } | |
| 250 | + | noerr(t, buffer.Destroy()) | |
| 251 | + | request(t, p) | |
| 252 | + | noerr(t, params.Destroy()) | |
| 253 | + | request(t, p) | |
| 254 | + | failedParams, e := dm.CreateParams() | |
| 255 | + | noerr(t, e) | |
| 256 | + | request(t, p) | |
| 257 | + | failedParams.OnFailed(func() { sequence = append(sequence, "failed") }) | |
| 258 | + | noerr(t, failedParams.Create(1, 1, 0x34325258, 0)) | |
| 259 | + | request(t, p) | |
| 260 | + | send(t, p, msg(failedParams.ID(), 1)) | |
| 261 | + | noerr(t, d.Dispatch()) | |
| 262 | + | if sequence[len(sequence)-1] != "failed" { | |
| 263 | + | t.Fatal(sequence) | |
| 264 | + | } | |
| 265 | + | noerr(t, failedParams.Destroy()) | |
| 266 | + | request(t, p) | |
| 267 | + | immedParams, e := dm.CreateParams() | |
| 268 | + | noerr(t, e) | |
| 269 | + | request(t, p) | |
| 270 | + | immediate, e := immedParams.CreateImmed(1, 1, 0x34325258, 0) | |
| 271 | + | noerr(t, e) | |
| 272 | + | request(t, p) | |
| 273 | + | noerr(t, immediate.Destroy()) | |
| 274 | + | request(t, p) | |
| 275 | + | noerr(t, immedParams.Destroy()) | |
| 276 | + | request(t, p) | |
| 277 | + | noerr(t, dm.Destroy()) | |
| 278 | + | request(t, p) | |
| 279 | + | sync := drmsyncobj.NewWpLinuxDrmSyncobjManager(d.Context()) | |
| 280 | + | bind(t, d, p, 3, drmsyncobj.WpLinuxDrmSyncobjManagerInterface, 1, 1, sync) | |
| 281 | + | ss, e := sync.GetSurface(surface) | |
| 282 | + | noerr(t, e) | |
| 283 | + | request(t, p) | |
| 284 | + | f, e = os.Open("/dev/null") | |
| 285 | + | noerr(t, e) | |
| 286 | + | tl, e := sync.ImportTimeline(int(f.Fd())) | |
| 287 | + | noerr(t, e) | |
| 288 | + | request(t, p) | |
| 289 | + | if _, e := unix.FcntlInt(f.Fd(), unix.F_GETFD, 0); e == nil { | |
| 290 | + | t.Fatal("timeline fd not closed") | |
| 291 | + | } | |
| 292 | + | f.Close() | |
| 293 | + | noerr(t, ss.SetAcquirePoint(tl, 0x123, 0x456)) | |
| 294 | + | _, op, b = request(t, p) | |
| 295 | + | if op != 1 || binary.NativeEndian.Uint32(b[4:]) != 0x123 || binary.NativeEndian.Uint32(b[8:]) != 0x456 { | |
| 296 | + | t.Fatalf("acquire %d %x", op, b) | |
| 297 | + | } | |
| 298 | + | noerr(t, ss.SetReleasePoint(tl, 0x789, 0xabc)) | |
| 299 | + | _, op, b = request(t, p) | |
| 300 | + | if op != 2 || binary.NativeEndian.Uint32(b[4:]) != 0x789 { | |
| 301 | + | t.Fatalf("release %d %x", op, b) | |
| 302 | + | } | |
| 303 | + | noerr(t, ss.Destroy()) | |
| 304 | + | request(t, p) | |
| 305 | + | noerr(t, tl.Destroy()) | |
| 306 | + | request(t, p) | |
| 307 | + | noerr(t, sync.Destroy()) | |
| 308 | + | request(t, p) | |
| 309 | + | vp := viewporter.NewWpViewporter(d.Context()) | |
| 310 | + | bind(t, d, p, 4, viewporter.WpViewporterInterface, 1, 1, vp) | |
| 311 | + | view, e := vp.GetViewport(surface) | |
| 312 | + | noerr(t, e) | |
| 313 | + | request(t, p) | |
| 314 | + | noerr(t, view.Destroy()) | |
| 315 | + | request(t, p) | |
| 316 | + | noerr(t, vp.Destroy()) | |
| 317 | + | request(t, p) | |
| 318 | + | fs := fractionalscale.NewWpFractionalScaleManager(d.Context()) | |
| 319 | + | bind(t, d, p, 5, fractionalscale.WpFractionalScaleManagerInterface, 1, 1, fs) | |
| 320 | + | scale, e := fs.GetFractionalScale(surface) | |
| 321 | + | noerr(t, e) | |
| 322 | + | request(t, p) | |
| 323 | + | var preferred uint32 | |
| 324 | + | scale.OnPreferredScale(func(v uint32) { preferred = v }) | |
| 325 | + | send(t, p, msg(scale.ID(), 0, 150)) | |
| 326 | + | noerr(t, d.Dispatch()) | |
| 327 | + | if preferred != 150 { | |
| 328 | + | t.Fatal(preferred) | |
| 329 | + | } | |
| 330 | + | noerr(t, scale.Destroy()) | |
| 331 | + | request(t, p) | |
| 332 | + | noerr(t, fs.Destroy()) | |
| 333 | + | request(t, p) | |
| 334 | + | ti := textinput.NewTextInputManagerV3(d.Context()) | |
| 335 | + | bind(t, d, p, 6, textinput.TextInputManagerV3Interface, 2, 1, ti) | |
| 336 | + | input, e := ti.GetTextInput(seat) | |
| 337 | + | noerr(t, e) | |
| 338 | + | request(t, p) | |
| 339 | + | noerr(t, input.Enable()) | |
| 340 | + | request(t, p) | |
| 341 | + | noerr(t, input.SetSurroundingText("hello", 5, 5)) | |
| 342 | + | request(t, p) | |
| 343 | + | noerr(t, input.Commit()) | |
| 344 | + | request(t, p) | |
| 345 | + | var events []string | |
| 346 | + | input.OnPreeditString(func(s string, _, _ int32) { events = append(events, "preedit:"+s) }) | |
| 347 | + | input.OnCommitString(func(s string) { events = append(events, "commit:"+s) }) | |
| 348 | + | input.OnDeleteSurroundingText(func(_, _ uint32) { events = append(events, "delete") }) | |
| 349 | + | input.OnDone(func(uint32) { events = append(events, "done") }) | |
| 350 | + | send(t, p, payload(input.ID(), 2, append(str("é"), msg(0, 0, 0, 0)[8:]...)...)) | |
| 351 | + | noerr(t, d.Dispatch()) | |
| 352 | + | send(t, p, payload(input.ID(), 3, str("hi")...)) | |
| 353 | + | noerr(t, d.Dispatch()) | |
| 354 | + | send(t, p, msg(input.ID(), 4, 1, 2)) | |
| 355 | + | noerr(t, d.Dispatch()) | |
| 356 | + | send(t, p, msg(input.ID(), 5, 1)) | |
| 357 | + | noerr(t, d.Dispatch()) | |
| 358 | + | if strings.Join(events, ",") != "preedit:é,commit:hi,delete,done" { | |
| 359 | + | t.Fatal(events) | |
| 360 | + | } | |
| 361 | + | noerr(t, input.Disable()) | |
| 362 | + | request(t, p) | |
| 363 | + | noerr(t, input.Commit()) | |
| 364 | + | request(t, p) | |
| 365 | + | noerr(t, input.Destroy()) | |
| 366 | + | request(t, p) | |
| 367 | + | noerr(t, ti.Destroy()) | |
| 368 | + | request(t, p) | |
| 369 | + | // Core data-device remains in protocol/core; verify its typed child and | |
| 370 | + | // both directions of clipboard descriptor ownership. | |
| 371 | + | mgr := core.NewDataDeviceManager(d.Context()) | |
| 372 | + | bind(t, d, p, 7, core.DataDeviceManagerInterface, 3, 4, mgr) | |
| 373 | + | device, e := mgr.GetDataDevice(seat) | |
| 374 | + | noerr(t, e) | |
| 375 | + | request(t, p) | |
| 376 | + | source, e := mgr.CreateDataSource() | |
| 377 | + | noerr(t, e) | |
| 378 | + | request(t, p) | |
| 379 | + | var offer *core.DataOffer | |
| 380 | + | device.OnDataOffer(func(o *core.DataOffer) { offer = o }) | |
| 381 | + | send(t, p, msg(device.ID(), 0, 998)) | |
| 382 | + | noerr(t, d.Dispatch()) | |
| 383 | + | if offer == nil || offer.ID() != 998 { | |
| 384 | + | t.Fatal("offer not typed") | |
| 385 | + | } | |
| 386 | + | read, write, e := os.Pipe() | |
| 387 | + | noerr(t, e) | |
| 388 | + | defer read.Close() | |
| 389 | + | noerr(t, offer.Receive("text/plain", int(write.Fd()))) | |
| 390 | + | request(t, p) | |
| 391 | + | if _, e := unix.FcntlInt(write.Fd(), unix.F_GETFD, 0); e == nil { | |
| 392 | + | t.Fatal("clipboard sent fd retained") | |
| 393 | + | } | |
| 394 | + | write.Close() | |
| 395 | + | source.OnSend(func(mime string, fd *wlturbo.OwnedFD) { | |
| 396 | + | if mime != "text/plain" { | |
| 397 | + | t.Fatal(mime) | |
| 398 | + | } | |
| 399 | + | n, e := fd.Take() | |
| 400 | + | noerr(t, e) | |
| 401 | + | unix.Close(n) | |
| 402 | + | }) | |
| 403 | + | received, e := os.Open("/dev/null") | |
| 404 | + | noerr(t, e) | |
| 405 | + | send(t, p, payload(source.ID(), 1, str("text/plain")...), int(received.Fd())) | |
| 406 | + | noerr(t, d.Dispatch()) | |
| 407 | + | received.Close() | |
| 408 | + | noerr(t, offer.Destroy()) | |
| 409 | + | request(t, p) | |
| 410 | + | noerr(t, source.Destroy()) | |
| 411 | + | request(t, p) | |
| 412 | + | noerr(t, device.Release()) | |
| 413 | + | request(t, p) | |
| 414 | + | noerr(t, mgr.Release()) | |
| 415 | + | request(t, p) | |
| 416 | + | ||
| 417 | + | } | |
| 418 | + | ||
| 419 | + | func TestLiveFeedback(t *testing.T) { | |
| 420 | + | if os.Getenv("WLTURBO_LIVE") != "1" { | |
| 421 | + | t.Skip("set WLTURBO_LIVE=1") | |
| 422 | + | } | |
| 423 | + | d, e := wlturbo.Connect("") | |
| 424 | + | noerr(t, e) | |
| 425 | + | defer d.Close() | |
| 426 | + | noerr(t, d.Roundtrip()) | |
| 427 | + | for _, iface := range []string{xdgshell.XdgWmBaseInterface, linuxdmabuf.LinuxDmabufInterface, drmsyncobj.WpLinuxDrmSyncobjManagerInterface, viewporter.WpViewporterInterface, fractionalscale.WpFractionalScaleManagerInterface, textinput.TextInputManagerV3Interface} { | |
| 428 | + | g, ok := d.Registry().FindGlobal(iface) | |
| 429 | + | if !ok { | |
| 430 | + | t.Logf("%s absent", iface) | |
| 431 | + | continue | |
| 432 | + | } | |
| 433 | + | v := g.Version | |
| 434 | + | cap := uint32(1) | |
| 435 | + | if iface == xdgshell.XdgWmBaseInterface { | |
| 436 | + | cap = 7 | |
| 437 | + | } | |
| 438 | + | if iface == linuxdmabuf.LinuxDmabufInterface { | |
| 439 | + | cap = 4 | |
| 440 | + | } | |
| 441 | + | if v > cap { | |
| 442 | + | v = cap | |
| 443 | + | } | |
| 444 | + | t.Logf("%s negotiated=%d (server=%d)", iface, v, g.Version) | |
| 445 | + | if iface != linuxdmabuf.LinuxDmabufInterface { | |
| 446 | + | continue | |
| 447 | + | } | |
| 448 | + | if v < 4 { | |
| 449 | + | t.Fatal("dmabuf feedback requires v4") | |
| 450 | + | } | |
| 451 | + | dm := linuxdmabuf.NewLinuxDmabuf(d.Context()) | |
| 452 | + | negotiated, err := d.Registry().BindNegotiated(iface, 4, dm) | |
| 453 | + | noerr(t, err) | |
| 454 | + | if negotiated != v { | |
| 455 | + | t.Fatalf("dmabuf negotiated=%d want=%d", negotiated, v) | |
| 456 | + | } | |
| 457 | + | fb, e := dm.GetDefaultFeedback() | |
| 458 | + | noerr(t, e) | |
| 459 | + | done := false | |
| 460 | + | fb.OnMainDevice(func(b []byte) { t.Logf("main_device=%x", b) }) | |
| 461 | + | fb.OnFormatTable(func(fd *wlturbo.OwnedFD, size uint32) { | |
| 462 | + | entries, err := linuxdmabuf.ReadFormatTable(fd, size) | |
| 463 | + | if err != nil { | |
| 464 | + | t.Errorf("table: %v", err) | |
| 465 | + | } | |
| 466 | + | t.Logf("format-table entries=%d", len(entries)) | |
| 467 | + | }) | |
| 468 | + | fb.OnDone(func() { done = true }) | |
| 469 | + | for i := 0; i < 100 && !done; i++ { | |
| 470 | + | noerr(t, d.Dispatch()) | |
| 471 | + | } | |
| 472 | + | if !done { | |
| 473 | + | t.Fatal("no feedback done") | |
| 474 | + | } | |
| 475 | + | noerr(t, fb.Destroy()) | |
| 476 | + | noerr(t, dm.Destroy()) | |
| 477 | + | } | |
| 478 | + | } | |
A protocol/format_table_test.go +71 -0
| @@ -0,0 +1,71 @@ | |||
| 1 | + | //go:build linux | |
| 2 | + | ||
| 3 | + | package protocol_test | |
| 4 | + | ||
| 5 | + | import ( | |
| 6 | + | "errors" | |
| 7 | + | "github.com/bnema/wlturbo" | |
| 8 | + | "github.com/bnema/wlturbo/protocol/linuxdmabuf" | |
| 9 | + | "golang.org/x/sys/unix" | |
| 10 | + | "io" | |
| 11 | + | "os" | |
| 12 | + | "testing" | |
| 13 | + | ) | |
| 14 | + | ||
| 15 | + | // Exercise format_table through generated event dispatch so the received FD | |
| 16 | + | // has real transport ownership; verify closing by inspecting the taken FD. | |
| 17 | + | func TestMalformedFormatTables(t *testing.T) { | |
| 18 | + | for _, tc := range []struct { | |
| 19 | + | name string | |
| 20 | + | contents []byte | |
| 21 | + | size uint32 | |
| 22 | + | wantErr bool | |
| 23 | + | }{ | |
| 24 | + | {"unaligned", make([]byte, 16), 17, true}, | |
| 25 | + | {"truncated", make([]byte, 16), 32, true}, | |
| 26 | + | {"zero", nil, 0, false}, | |
| 27 | + | {"oversize", nil, (16 << 20) + 16, true}, | |
| 28 | + | } { | |
| 29 | + | t.Run(tc.name, func(t *testing.T) { | |
| 30 | + | c, p := pair(t) | |
| 31 | + | d, e := wlturbo.ConnectFromConn(c) | |
| 32 | + | noerr(t, e) | |
| 33 | + | defer d.Close() | |
| 34 | + | request(t, p) | |
| 35 | + | fb := linuxdmabuf.NewLinuxDmabufFeedback(d.Context()) | |
| 36 | + | fb.SetID(d.AllocateID()) | |
| 37 | + | d.Context().Register(fb) | |
| 38 | + | file, e := os.CreateTemp(t.TempDir(), "table") | |
| 39 | + | noerr(t, e) | |
| 40 | + | defer file.Close() | |
| 41 | + | _, e = file.Write(tc.contents) | |
| 42 | + | noerr(t, e) | |
| 43 | + | called := false | |
| 44 | + | fb.OnFormatTable(func(fd *wlturbo.OwnedFD, size uint32) { | |
| 45 | + | called = true | |
| 46 | + | entries, err := linuxdmabuf.ReadFormatTable(fd, size) | |
| 47 | + | if (err != nil) != tc.wantErr { | |
| 48 | + | t.Fatalf("size=%d entries=%v err=%v", size, entries, err) | |
| 49 | + | } | |
| 50 | + | if tc.name == "truncated" && !errors.Is(err, io.ErrUnexpectedEOF) { | |
| 51 | + | t.Fatalf("short read: %v", err) | |
| 52 | + | } | |
| 53 | + | // Take/Close must be idempotent, including after a failed size check. | |
| 54 | + | if _, err = fd.Take(); err == nil { | |
| 55 | + | t.Fatal("already consumed fd still takeable") | |
| 56 | + | } | |
| 57 | + | noerr(t, fd.Close()) | |
| 58 | + | noerr(t, fd.Close()) | |
| 59 | + | }) | |
| 60 | + | send(t, p, msg(fb.ID(), 1, tc.size), int(file.Fd())) | |
| 61 | + | noerr(t, d.Dispatch()) | |
| 62 | + | if !called { | |
| 63 | + | t.Fatal("no callback") | |
| 64 | + | } | |
| 65 | + | // The transport FD was a duplicate: caller's descriptor is still open. | |
| 66 | + | if _, e = unix.FcntlInt(file.Fd(), unix.F_GETFD, 0); e != nil { | |
| 67 | + | t.Fatalf("sender fd closed: %v", e) | |
| 68 | + | } | |
| 69 | + | }) | |
| 70 | + | } | |
| 71 | + | } | |
A protocol/fractionalscale/fractional-scale-v1.xml +102 -0
| @@ -0,0 +1,102 @@ | |||
| 1 | + | <?xml version="1.0" encoding="UTF-8"?> | |
| 2 | + | <protocol name="fractional_scale_v1"> | |
| 3 | + | <copyright> | |
| 4 | + | Copyright © 2022 Kenny Levinsen | |
| 5 | + | ||
| 6 | + | Permission is hereby granted, free of charge, to any person obtaining a | |
| 7 | + | copy of this software and associated documentation files (the "Software"), | |
| 8 | + | to deal in the Software without restriction, including without limitation | |
| 9 | + | the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 10 | + | and/or sell copies of the Software, and to permit persons to whom the | |
| 11 | + | Software is furnished to do so, subject to the following conditions: | |
| 12 | + | ||
| 13 | + | The above copyright notice and this permission notice (including the next | |
| 14 | + | paragraph) shall be included in all copies or substantial portions of the | |
| 15 | + | Software. | |
| 16 | + | ||
| 17 | + | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 18 | + | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 19 | + | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 20 | + | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 21 | + | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
| 22 | + | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
| 23 | + | DEALINGS IN THE SOFTWARE. | |
| 24 | + | </copyright> | |
| 25 | + | ||
| 26 | + | <description summary="Protocol for requesting fractional surface scales"> | |
| 27 | + | This protocol allows a compositor to suggest for surfaces to render at | |
| 28 | + | fractional scales. | |
| 29 | + | ||
| 30 | + | A client can submit scaled content by utilizing wp_viewport. This is done by | |
| 31 | + | creating a wp_viewport object for the surface and setting the destination | |
| 32 | + | rectangle to the surface size before the scale factor is applied. | |
| 33 | + | ||
| 34 | + | The buffer size is calculated by multiplying the surface size by the | |
| 35 | + | intended scale. | |
| 36 | + | ||
| 37 | + | The wl_surface buffer scale should remain set to 1. | |
| 38 | + | ||
| 39 | + | If a surface has a surface-local size of 100 px by 50 px and wishes to | |
| 40 | + | submit buffers with a scale of 1.5, then a buffer of 150px by 75 px should | |
| 41 | + | be used and the wp_viewport destination rectangle should be 100 px by 50 px. | |
| 42 | + | ||
| 43 | + | For toplevel surfaces, the size is rounded halfway away from zero. The | |
| 44 | + | rounding algorithm for subsurface position and size is not defined. | |
| 45 | + | </description> | |
| 46 | + | ||
| 47 | + | <interface name="wp_fractional_scale_manager_v1" version="1"> | |
| 48 | + | <description summary="fractional surface scale information"> | |
| 49 | + | A global interface for requesting surfaces to use fractional scales. | |
| 50 | + | </description> | |
| 51 | + | ||
| 52 | + | <request name="destroy" type="destructor"> | |
| 53 | + | <description summary="unbind the fractional surface scale interface"> | |
| 54 | + | Informs the server that the client will not be using this protocol | |
| 55 | + | object anymore. This does not affect any other objects, | |
| 56 | + | wp_fractional_scale_v1 objects included. | |
| 57 | + | </description> | |
| 58 | + | </request> | |
| 59 | + | ||
| 60 | + | <enum name="error"> | |
| 61 | + | <entry name="fractional_scale_exists" value="0" | |
| 62 | + | summary="the surface already has a fractional_scale object associated"/> | |
| 63 | + | </enum> | |
| 64 | + | ||
| 65 | + | <request name="get_fractional_scale"> | |
| 66 | + | <description summary="extend surface interface for scale information"> | |
| 67 | + | Create an add-on object for the the wl_surface to let the compositor | |
| 68 | + | request fractional scales. If the given wl_surface already has a | |
| 69 | + | wp_fractional_scale_v1 object associated, the fractional_scale_exists | |
| 70 | + | protocol error is raised. | |
| 71 | + | </description> | |
| 72 | + | <arg name="id" type="new_id" interface="wp_fractional_scale_v1" | |
| 73 | + | summary="the new surface scale info interface id"/> | |
| 74 | + | <arg name="surface" type="object" interface="wl_surface" | |
| 75 | + | summary="the surface"/> | |
| 76 | + | </request> | |
| 77 | + | </interface> | |
| 78 | + | ||
| 79 | + | <interface name="wp_fractional_scale_v1" version="1"> | |
| 80 | + | <description summary="fractional scale interface to a wl_surface"> | |
| 81 | + | An additional interface to a wl_surface object which allows the compositor | |
| 82 | + | to inform the client of the preferred scale. | |
| 83 | + | </description> | |
| 84 | + | ||
| 85 | + | <request name="destroy" type="destructor"> | |
| 86 | + | <description summary="remove surface scale information for surface"> | |
| 87 | + | Destroy the fractional scale object. When this object is destroyed, | |
| 88 | + | preferred_scale events will no longer be sent. | |
| 89 | + | </description> | |
| 90 | + | </request> | |
| 91 | + | ||
| 92 | + | <event name="preferred_scale"> | |
| 93 | + | <description summary="notify of new preferred scale"> | |
| 94 | + | Notification of a new preferred scale for this surface that the | |
| 95 | + | compositor suggests that the client should use. | |
| 96 | + | ||
| 97 | + | The sent scale is the numerator of a fraction with a denominator of 120. | |
| 98 | + | </description> | |
| 99 | + | <arg name="scale" type="uint" summary="the new preferred scale"/> | |
| 100 | + | </event> | |
| 101 | + | </interface> | |
| 102 | + | </protocol> | |
A protocol/fractionalscale/fractional-scale-v1_generated.go +154 -0
| @@ -0,0 +1,154 @@ | |||
| 1 | + | // Code generated by wlturbo-scanner. DO NOT EDIT. | |
| 2 | + | // Protocol: fractional_scale_v1 | |
| 3 | + | // Source: fractional-scale-v1.xml | |
| 4 | + | ||
| 5 | + | package fractionalscale | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "sync" | |
| 9 | + | ||
| 10 | + | cross_wl_surface "github.com/bnema/wlturbo/protocol/core" | |
| 11 | + | "github.com/bnema/wlturbo/wl" | |
| 12 | + | ) | |
| 13 | + | ||
| 14 | + | // ERROR_FRACTIONAL_SCALE_EXISTS is the error fractional_scale_exists value. | |
| 15 | + | const ERROR_FRACTIONAL_SCALE_EXISTS = 0 | |
| 16 | + | ||
| 17 | + | // WpFractionalScaleManagerInterface is the protocol interface name. | |
| 18 | + | const WpFractionalScaleManagerInterface = "wp_fractional_scale_manager_v1" | |
| 19 | + | ||
| 20 | + | // WpFractionalScaleManager is the generated binding for wp_fractional_scale_manager_v1 version 1. | |
| 21 | + | type WpFractionalScaleManager struct { | |
| 22 | + | wl.BaseProxy | |
| 23 | + | } | |
| 24 | + | ||
| 25 | + | // NewWpFractionalScaleManager returns an unbound WpFractionalScaleManager. | |
| 26 | + | func NewWpFractionalScaleManager(ctx *wl.Context) *WpFractionalScaleManager { | |
| 27 | + | obj := &WpFractionalScaleManager{} | |
| 28 | + | obj.SetContext(ctx) | |
| 29 | + | ctx.Register(obj) | |
| 30 | + | return obj | |
| 31 | + | } | |
| 32 | + | ||
| 33 | + | // Informs the server that the client will not be using this protocol object anymore. This does not affect any other objects, wp_fractional_scale_v1 objects included. | |
| 34 | + | func (o *WpFractionalScaleManager) Destroy() error { | |
| 35 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 36 | + | return err | |
| 37 | + | } | |
| 38 | + | if err := o.Context().SendDestructor(o, 0); err != nil { | |
| 39 | + | return err | |
| 40 | + | } | |
| 41 | + | return nil | |
| 42 | + | } | |
| 43 | + | ||
| 44 | + | // Create an add-on object for the the wl_surface to let the compositor request fractional scales. If the given wl_surface already has a wp_fractional_scale_v1 object associated, the fractional_scale_exists protocol error is raised. | |
| 45 | + | func (o *WpFractionalScaleManager) GetFractionalScale(surface *cross_wl_surface.Surface) (*WpFractionalScale, error) { | |
| 46 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 47 | + | return nil, err | |
| 48 | + | } | |
| 49 | + | var arg1 wl.Object | |
| 50 | + | if surface != nil { | |
| 51 | + | arg1 = surface | |
| 52 | + | } | |
| 53 | + | child := &WpFractionalScale{} | |
| 54 | + | child.SetContext(o.Context()) | |
| 55 | + | child.SetID(o.Context().AllocateID()) | |
| 56 | + | o.Context().Register(child) | |
| 57 | + | if err := o.Context().SendRequest(o, 1, child, arg1); err != nil { | |
| 58 | + | o.Context().Unregister(child) | |
| 59 | + | return nil, err | |
| 60 | + | } | |
| 61 | + | return child, nil | |
| 62 | + | } | |
| 63 | + | ||
| 64 | + | // EventSignature returns the declared event argument types in wire order. | |
| 65 | + | func (o *WpFractionalScaleManager) EventSignature(opcode uint16) (string, bool) { | |
| 66 | + | switch opcode { | |
| 67 | + | default: | |
| 68 | + | return "", false | |
| 69 | + | } | |
| 70 | + | } | |
| 71 | + | ||
| 72 | + | // Dispatch decodes one event on wp_fractional_scale_manager_v1 and calls the registered handlers. | |
| 73 | + | func (o *WpFractionalScaleManager) Dispatch(event *wl.Event) { | |
| 74 | + | switch event.Opcode { | |
| 75 | + | default: | |
| 76 | + | } | |
| 77 | + | } | |
| 78 | + | ||
| 79 | + | // WpFractionalScaleInterface is the protocol interface name. | |
| 80 | + | const WpFractionalScaleInterface = "wp_fractional_scale_v1" | |
| 81 | + | ||
| 82 | + | // WpFractionalScale is the generated binding for wp_fractional_scale_v1 version 1. | |
| 83 | + | type WpFractionalScale struct { | |
| 84 | + | wl.BaseProxy | |
| 85 | + | // handlersMu guards the handler slices: a client may register or replace | |
| 86 | + | // handlers while another goroutine dispatches events. | |
| 87 | + | handlersMu sync.Mutex | |
| 88 | + | onPreferredScale []func(scale uint32) | |
| 89 | + | } | |
| 90 | + | ||
| 91 | + | // NewWpFractionalScale returns an unbound WpFractionalScale. | |
| 92 | + | func NewWpFractionalScale(ctx *wl.Context) *WpFractionalScale { | |
| 93 | + | obj := &WpFractionalScale{} | |
| 94 | + | obj.SetContext(ctx) | |
| 95 | + | ctx.Register(obj) | |
| 96 | + | return obj | |
| 97 | + | } | |
| 98 | + | ||
| 99 | + | // Destroy the fractional scale object. When this object is destroyed, preferred_scale events will no longer be sent. | |
| 100 | + | func (o *WpFractionalScale) Destroy() error { | |
| 101 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 102 | + | return err | |
| 103 | + | } | |
| 104 | + | if err := o.Context().SendDestructor(o, 0); err != nil { | |
| 105 | + | return err | |
| 106 | + | } | |
| 107 | + | return nil | |
| 108 | + | } | |
| 109 | + | ||
| 110 | + | // OnPreferredScale registers a handler for the preferred_scale event. | |
| 111 | + | // | |
| 112 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 113 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 114 | + | func (o *WpFractionalScale) OnPreferredScale(handler func(scale uint32)) { | |
| 115 | + | if handler == nil { | |
| 116 | + | return | |
| 117 | + | } | |
| 118 | + | o.handlersMu.Lock() | |
| 119 | + | defer o.handlersMu.Unlock() | |
| 120 | + | o.onPreferredScale = append(o.onPreferredScale, handler) | |
| 121 | + | } | |
| 122 | + | ||
| 123 | + | // handlersForPreferredScale returns the handlers registered for preferred_scale, taken | |
| 124 | + | // under the lock so a handler may register another handler while events are | |
| 125 | + | // being dispatched. | |
| 126 | + | func (o *WpFractionalScale) handlersForPreferredScale() []func(scale uint32) { | |
| 127 | + | o.handlersMu.Lock() | |
| 128 | + | defer o.handlersMu.Unlock() | |
| 129 | + | return o.onPreferredScale | |
| 130 | + | } | |
| 131 | + | ||
| 132 | + | // EventSignature returns the declared event argument types in wire order. | |
| 133 | + | func (o *WpFractionalScale) EventSignature(opcode uint16) (string, bool) { | |
| 134 | + | switch opcode { | |
| 135 | + | case 0: | |
| 136 | + | return "uint,", true | |
| 137 | + | default: | |
| 138 | + | return "", false | |
| 139 | + | } | |
| 140 | + | } | |
| 141 | + | ||
| 142 | + | // Dispatch decodes one event on wp_fractional_scale_v1 and calls the registered handlers. | |
| 143 | + | func (o *WpFractionalScale) Dispatch(event *wl.Event) { | |
| 144 | + | switch event.Opcode { | |
| 145 | + | case 0: // preferred_scale | |
| 146 | + | scale := event.Uint32() | |
| 147 | + | ||
| 148 | + | for i, handler := range o.handlersForPreferredScale() { | |
| 149 | + | _ = i | |
| 150 | + | handler(scale) | |
| 151 | + | } | |
| 152 | + | default: | |
| 153 | + | } | |
| 154 | + | } | |
M protocol/generate.go +6 -0
| @@ -1,3 +1,9 @@ | |||
| 1 | 1 | package protocol | |
| 2 | 2 | ||
| 3 | 3 | //go:generate go run ../cmd/wlturbo-scanner -p core -o core/wayland_generated.go wayland.xml | |
| 4 | + | //go:generate go run ../cmd/wlturbo-scanner -p xdgshell -o xdgshell/xdg-shell_generated.go -import wl_surface=github.com/bnema/wlturbo/protocol/core -import wl_seat=github.com/bnema/wlturbo/protocol/core -import wl_output=github.com/bnema/wlturbo/protocol/core xdgshell/xdg-shell.xml | |
| 5 | + | //go:generate go run ../cmd/wlturbo-scanner -p linuxdmabuf -o linuxdmabuf/linux-dmabuf-v1_generated.go -import wl_surface=github.com/bnema/wlturbo/protocol/core -import wl_buffer=github.com/bnema/wlturbo/protocol/core linuxdmabuf/linux-dmabuf-v1.xml | |
| 6 | + | //go:generate go run ../cmd/wlturbo-scanner -p drmsyncobj -o drmsyncobj/linux-drm-syncobj-v1_generated.go -import wl_surface=github.com/bnema/wlturbo/protocol/core drmsyncobj/linux-drm-syncobj-v1.xml | |
| 7 | + | //go:generate go run ../cmd/wlturbo-scanner -p viewporter -o viewporter/viewporter_generated.go -import wl_surface=github.com/bnema/wlturbo/protocol/core viewporter/viewporter.xml | |
| 8 | + | //go:generate go run ../cmd/wlturbo-scanner -p fractionalscale -o fractionalscale/fractional-scale-v1_generated.go -import wl_surface=github.com/bnema/wlturbo/protocol/core fractionalscale/fractional-scale-v1.xml | |
| 9 | + | //go:generate go run ../cmd/wlturbo-scanner -p textinput -o textinput/text-input-unstable-v3_generated.go -import wl_surface=github.com/bnema/wlturbo/protocol/core -import wl_seat=github.com/bnema/wlturbo/protocol/core textinput/text-input-unstable-v3.xml | |
A protocol/headless_test.go +180 -0
| @@ -0,0 +1,180 @@ | |||
| 1 | + | //go:build linux | |
| 2 | + | ||
| 3 | + | package protocol_test | |
| 4 | + | ||
| 5 | + | import ( | |
| 6 | + | "context" | |
| 7 | + | "fmt" | |
| 8 | + | "net" | |
| 9 | + | "os" | |
| 10 | + | "os/exec" | |
| 11 | + | "path/filepath" | |
| 12 | + | "strings" | |
| 13 | + | "syscall" | |
| 14 | + | "testing" | |
| 15 | + | "time" | |
| 16 | + | ||
| 17 | + | "github.com/bnema/wlturbo" | |
| 18 | + | "github.com/bnema/wlturbo/protocol/core" | |
| 19 | + | "github.com/bnema/wlturbo/protocol/drmsyncobj" | |
| 20 | + | "github.com/bnema/wlturbo/protocol/fractionalscale" | |
| 21 | + | "github.com/bnema/wlturbo/protocol/linuxdmabuf" | |
| 22 | + | "github.com/bnema/wlturbo/protocol/viewporter" | |
| 23 | + | "github.com/bnema/wlturbo/protocol/xdgshell" | |
| 24 | + | ) | |
| 25 | + | ||
| 26 | + | func TestHeadlessNeferWL(t *testing.T) { | |
| 27 | + | binary := os.Getenv("WLTURBO_HEADLESS") | |
| 28 | + | if binary == "" { | |
| 29 | + | t.Skip("set WLTURBO_HEADLESS to a NeferWL binary") | |
| 30 | + | } | |
| 31 | + | binary, e := filepath.Abs(binary) | |
| 32 | + | noerr(t, e) | |
| 33 | + | runtimeDir := filepath.Join(t.TempDir(), "run") | |
| 34 | + | noerr(t, os.Mkdir(runtimeDir, 0700)) | |
| 35 | + | configDir := filepath.Join(t.TempDir(), "config") | |
| 36 | + | noerr(t, os.Mkdir(configDir, 0700)) | |
| 37 | + | ctx, cancel := context.WithTimeout(context.Background(), 18*time.Second) | |
| 38 | + | defer cancel() | |
| 39 | + | cmd := exec.Command(binary, "--backend=headless", "--no-terminal", "--no-xwayland", "--size", "640x480", "--timeout", "20s") | |
| 40 | + | cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} | |
| 41 | + | cmd.Env = append(cleanDisplayEnv(os.Environ()), "XDG_RUNTIME_DIR="+runtimeDir, "XDG_CONFIG_HOME="+configDir) | |
| 42 | + | log, e := os.Create(filepath.Join(t.TempDir(), "neferwl.log")) | |
| 43 | + | noerr(t, e) | |
| 44 | + | defer log.Close() | |
| 45 | + | cmd.Stdout = log | |
| 46 | + | cmd.Stderr = log | |
| 47 | + | noerr(t, cmd.Start()) | |
| 48 | + | t.Cleanup(func() { _ = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL); _ = cmd.Wait() }) | |
| 49 | + | // A failed startup must still terminate its process group and reap it. | |
| 50 | + | socket := "" | |
| 51 | + | for socket == "" { | |
| 52 | + | if e := ctx.Err(); e != nil { | |
| 53 | + | t.Fatalf("headless socket timeout: %v\n%s", e, headlessLog(log)) | |
| 54 | + | } | |
| 55 | + | paths, _ := filepath.Glob(filepath.Join(runtimeDir, "wayland-*")) | |
| 56 | + | for _, path := range paths { | |
| 57 | + | info, e := os.Stat(path) | |
| 58 | + | if e == nil && info.Mode()&os.ModeSocket != 0 { | |
| 59 | + | socket = path | |
| 60 | + | break | |
| 61 | + | } | |
| 62 | + | } | |
| 63 | + | if socket == "" { | |
| 64 | + | time.Sleep(20 * time.Millisecond) | |
| 65 | + | } | |
| 66 | + | } | |
| 67 | + | conn, e := net.DialTimeout("unix", socket, time.Second) | |
| 68 | + | noerr(t, e) | |
| 69 | + | d, e := wlturbo.ConnectFromConn(conn) | |
| 70 | + | noerr(t, e) | |
| 71 | + | defer d.Close() | |
| 72 | + | // Close the socket when the test deadline expires so blocking Dispatch exits. | |
| 73 | + | stop := context.AfterFunc(ctx, func() { _ = d.Close() }) | |
| 74 | + | defer stop() | |
| 75 | + | noerr(t, d.Roundtrip()) | |
| 76 | + | bind := func(iface string, supported uint32, proxy wlturbo.Proxy) uint32 { | |
| 77 | + | t.Helper() | |
| 78 | + | v, err := d.Registry().BindNegotiated(iface, supported, proxy) | |
| 79 | + | if err != nil { | |
| 80 | + | t.Fatalf("bind %s: %v\n%s", iface, err, headlessLog(log)) | |
| 81 | + | } | |
| 82 | + | t.Logf("%s negotiated=%d", iface, v) | |
| 83 | + | return v | |
| 84 | + | } | |
| 85 | + | compositor := core.NewCompositor(d.Context()) | |
| 86 | + | bind(core.CompositorInterface, 6, compositor) | |
| 87 | + | wm := xdgshell.NewXdgWmBase(d.Context()) | |
| 88 | + | bind(xdgshell.XdgWmBaseInterface, 7, wm) | |
| 89 | + | dm := linuxdmabuf.NewLinuxDmabuf(d.Context()) | |
| 90 | + | if bind(linuxdmabuf.LinuxDmabufInterface, 4, dm) < 4 { | |
| 91 | + | t.Fatal("dmabuf feedback requires v4") | |
| 92 | + | } | |
| 93 | + | syncManager := drmsyncobj.NewWpLinuxDrmSyncobjManager(d.Context()) | |
| 94 | + | bind(drmsyncobj.WpLinuxDrmSyncobjManagerInterface, 1, syncManager) | |
| 95 | + | dataManager := core.NewDataDeviceManager(d.Context()) | |
| 96 | + | bind(core.DataDeviceManagerInterface, 4, dataManager) | |
| 97 | + | vp := viewporter.NewWpViewporter(d.Context()) | |
| 98 | + | bind(viewporter.WpViewporterInterface, 1, vp) | |
| 99 | + | if g, ok := d.Registry().FindGlobal(fractionalscale.WpFractionalScaleManagerInterface); ok { | |
| 100 | + | fs := fractionalscale.NewWpFractionalScaleManager(d.Context()) | |
| 101 | + | v := bind(g.Interface, 1, fs) | |
| 102 | + | if v != 1 { | |
| 103 | + | t.Fatalf("fractional scale version %d", v) | |
| 104 | + | } | |
| 105 | + | noerr(t, fs.Destroy()) | |
| 106 | + | } else { | |
| 107 | + | t.Log("fractional scale absent") | |
| 108 | + | } | |
| 109 | + | surface, e := compositor.CreateSurface() | |
| 110 | + | noerr(t, e) | |
| 111 | + | xs, e := wm.GetXdgSurface(surface) | |
| 112 | + | noerr(t, e) | |
| 113 | + | top, e := xs.GetToplevel() | |
| 114 | + | noerr(t, e) | |
| 115 | + | feedback, e := dm.GetSurfaceFeedback(surface) | |
| 116 | + | noerr(t, e) | |
| 117 | + | ss, e := syncManager.GetSurface(surface) | |
| 118 | + | noerr(t, e) | |
| 119 | + | configured, done, mainDevice, tableOK := false, false, false, false | |
| 120 | + | top.OnConfigure(func(w, h int32, states []byte) { t.Logf("xdg_toplevel.configure %dx%d", w, h) }) | |
| 121 | + | xs.OnConfigure(func(serial uint32) { | |
| 122 | + | noerr(t, xs.AckConfigure(serial)) | |
| 123 | + | configured = true | |
| 124 | + | t.Logf("xdg_surface.configure ack=%d", serial) | |
| 125 | + | }) | |
| 126 | + | feedback.OnMainDevice(func(b []byte) { mainDevice = len(b) == 8; t.Logf("main_device=%x", b) }) | |
| 127 | + | feedback.OnFormatTable(func(fd *wlturbo.OwnedFD, size uint32) { | |
| 128 | + | entries, e := linuxdmabuf.ReadFormatTable(fd, size) | |
| 129 | + | if e != nil { | |
| 130 | + | t.Errorf("format table: %v", e) | |
| 131 | + | } else { | |
| 132 | + | tableOK = len(entries) > 0 | |
| 133 | + | t.Logf("format-table entries=%d", len(entries)) | |
| 134 | + | } | |
| 135 | + | }) | |
| 136 | + | feedback.OnDone(func() { done = true }) | |
| 137 | + | // Initial empty commit requests xdg configure; no buffer or window is shown. | |
| 138 | + | noerr(t, surface.Commit()) | |
| 139 | + | for (!configured || !done) && ctx.Err() == nil { | |
| 140 | + | if e := d.Dispatch(); e != nil { | |
| 141 | + | t.Fatalf("dispatch: %v\n%s", e, headlessLog(log)) | |
| 142 | + | } | |
| 143 | + | } | |
| 144 | + | if !configured || !done || !mainDevice || !tableOK { | |
| 145 | + | t.Fatalf("configure=%t feedback=%t main_device=%t table=%t: %v\n%s", configured, done, mainDevice, tableOK, ctx.Err(), headlessLog(log)) | |
| 146 | + | } | |
| 147 | + | noerr(t, surface.Commit()) | |
| 148 | + | // Drop all child objects while their parents are still alive. | |
| 149 | + | noerr(t, ss.Destroy()) | |
| 150 | + | noerr(t, feedback.Destroy()) | |
| 151 | + | noerr(t, top.Destroy()) | |
| 152 | + | noerr(t, xs.Destroy()) | |
| 153 | + | noerr(t, surface.Destroy()) | |
| 154 | + | noerr(t, vp.Destroy()) | |
| 155 | + | noerr(t, dataManager.Release()) | |
| 156 | + | noerr(t, syncManager.Destroy()) | |
| 157 | + | noerr(t, dm.Destroy()) | |
| 158 | + | noerr(t, wm.Destroy()) | |
| 159 | + | noerr(t, d.Close()) | |
| 160 | + | } | |
| 161 | + | func cleanDisplayEnv(in []string) []string { | |
| 162 | + | out := make([]string, 0, len(in)) | |
| 163 | + | for _, e := range in { | |
| 164 | + | if strings.HasPrefix(e, "DISPLAY=") || strings.HasPrefix(e, "WAYLAND_DISPLAY=") || strings.HasPrefix(e, "XDG_RUNTIME_DIR=") || strings.HasPrefix(e, "XDG_CONFIG_HOME=") { | |
| 165 | + | continue | |
| 166 | + | } | |
| 167 | + | out = append(out, e) | |
| 168 | + | } | |
| 169 | + | return out | |
| 170 | + | } | |
| 171 | + | func headlessLog(f *os.File) string { | |
| 172 | + | b, e := os.ReadFile(f.Name()) | |
| 173 | + | if e != nil { | |
| 174 | + | return fmt.Sprint(e) | |
| 175 | + | } | |
| 176 | + | if len(b) > 8192 { | |
| 177 | + | b = b[len(b)-8192:] | |
| 178 | + | } | |
| 179 | + | return string(b) | |
| 180 | + | } | |
A protocol/linuxdmabuf/linux-dmabuf-v1.xml +623 -0
| @@ -0,0 +1,623 @@ | |||
| 1 | + | <?xml version="1.0" encoding="UTF-8"?> | |
| 2 | + | <protocol name="linux_dmabuf_v1"> | |
| 3 | + | ||
| 4 | + | <copyright> | |
| 5 | + | Copyright © 2014, 2015 Collabora, Ltd. | |
| 6 | + | ||
| 7 | + | Permission is hereby granted, free of charge, to any person obtaining a | |
| 8 | + | copy of this software and associated documentation files (the "Software"), | |
| 9 | + | to deal in the Software without restriction, including without limitation | |
| 10 | + | the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 11 | + | and/or sell copies of the Software, and to permit persons to whom the | |
| 12 | + | Software is furnished to do so, subject to the following conditions: | |
| 13 | + | ||
| 14 | + | The above copyright notice and this permission notice (including the next | |
| 15 | + | paragraph) shall be included in all copies or substantial portions of the | |
| 16 | + | Software. | |
| 17 | + | ||
| 18 | + | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 19 | + | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 20 | + | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 21 | + | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 22 | + | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
| 23 | + | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
| 24 | + | DEALINGS IN THE SOFTWARE. | |
| 25 | + | </copyright> | |
| 26 | + | ||
| 27 | + | <interface name="zwp_linux_dmabuf_v1" version="6"> | |
| 28 | + | <description summary="factory for creating dmabuf-based wl_buffers"> | |
| 29 | + | This interface offers ways to create generic dmabuf-based wl_buffers. | |
| 30 | + | ||
| 31 | + | For more information about dmabuf, see: | |
| 32 | + | https://www.kernel.org/doc/html/next/userspace-api/dma-buf-alloc-exchange.html | |
| 33 | + | ||
| 34 | + | Clients can use the get_surface_feedback request to get dmabuf feedback | |
| 35 | + | for a particular surface. If the client wants to retrieve feedback not | |
| 36 | + | tied to a surface, they can use the get_default_feedback request. | |
| 37 | + | ||
| 38 | + | The following are required from clients: | |
| 39 | + | ||
| 40 | + | - Clients must ensure that either all data in the dma-buf is | |
| 41 | + | coherent for all subsequent read access or that coherency is | |
| 42 | + | correctly handled by the underlying kernel-side dma-buf | |
| 43 | + | implementation. | |
| 44 | + | ||
| 45 | + | - Don't make any more attachments after sending the buffer to the | |
| 46 | + | compositor. Making more attachments later increases the risk of | |
| 47 | + | the compositor not being able to use (re-import) an existing | |
| 48 | + | dmabuf-based wl_buffer. | |
| 49 | + | ||
| 50 | + | The underlying graphics stack must ensure the following: | |
| 51 | + | ||
| 52 | + | - The dmabuf file descriptors relayed to the server will stay valid | |
| 53 | + | for the whole lifetime of the wl_buffer. This means the server may | |
| 54 | + | at any time use those fds to import the dmabuf into any kernel | |
| 55 | + | sub-system that might accept it. | |
| 56 | + | ||
| 57 | + | However, when the underlying graphics stack fails to deliver the | |
| 58 | + | promise, because of e.g. a device hot-unplug which raises internal | |
| 59 | + | errors, after the wl_buffer has been successfully created the | |
| 60 | + | compositor must not raise protocol errors to the client when dmabuf | |
| 61 | + | import later fails. | |
| 62 | + | ||
| 63 | + | To create a wl_buffer from one or more dmabufs, a client creates a | |
| 64 | + | zwp_linux_buffer_params_v1 object with a zwp_linux_dmabuf_v1.create_params | |
| 65 | + | request. All planes required by the intended format are added with | |
| 66 | + | the 'add' request. Finally, a 'create' or 'create_immed' request is | |
| 67 | + | issued, which has the following outcome depending on the import success. | |
| 68 | + | ||
| 69 | + | The 'create' request, | |
| 70 | + | - on success, triggers a 'created' event which provides the final | |
| 71 | + | wl_buffer to the client. | |
| 72 | + | - on failure, triggers a 'failed' event to convey that the server | |
| 73 | + | cannot use the dmabufs received from the client. | |
| 74 | + | ||
| 75 | + | For the 'create_immed' request, | |
| 76 | + | - on success, the server immediately imports the added dmabufs to | |
| 77 | + | create a wl_buffer. No event is sent from the server in this case. | |
| 78 | + | - on failure, the server can choose to either: | |
| 79 | + | - terminate the client by raising a fatal error. | |
| 80 | + | - mark the wl_buffer as failed, and send a 'failed' event to the | |
| 81 | + | client. If the client uses a failed wl_buffer as an argument to any | |
| 82 | + | request, the behaviour is compositor implementation-defined. | |
| 83 | + | ||
| 84 | + | For all DRM formats and unless specified in another protocol extension, | |
| 85 | + | pre-multiplied alpha is used for pixel values. | |
| 86 | + | ||
| 87 | + | Unless specified otherwise in another protocol extension, implicit | |
| 88 | + | synchronization is used. In other words, compositors and clients must | |
| 89 | + | wait and signal fences implicitly passed via the DMA-BUF's reservation | |
| 90 | + | mechanism. | |
| 91 | + | </description> | |
| 92 | + | ||
| 93 | + | <request name="destroy" type="destructor"> | |
| 94 | + | <description summary="unbind the factory"> | |
| 95 | + | Objects created through this interface, especially wl_buffers, will | |
| 96 | + | remain valid. | |
| 97 | + | </description> | |
| 98 | + | </request> | |
| 99 | + | ||
| 100 | + | <request name="create_params"> | |
| 101 | + | <description summary="create a temporary object for buffer parameters"> | |
| 102 | + | This temporary object is used to collect multiple dmabuf handles into | |
| 103 | + | a single batch to create a wl_buffer. It can only be used once and | |
| 104 | + | should be destroyed after a 'created' or 'failed' event has been | |
| 105 | + | received. | |
| 106 | + | </description> | |
| 107 | + | <arg name="params_id" type="new_id" interface="zwp_linux_buffer_params_v1" | |
| 108 | + | summary="id for the newly created zwp_linux_buffer_params_v1"/> | |
| 109 | + | </request> | |
| 110 | + | ||
| 111 | + | <event name="format" deprecated-since="4"> | |
| 112 | + | <description summary="supported buffer format"> | |
| 113 | + | This event advertises one buffer format that the server supports. | |
| 114 | + | All the supported formats are advertised once when the client | |
| 115 | + | binds to this interface. A roundtrip after binding guarantees | |
| 116 | + | that the client has received all supported formats. | |
| 117 | + | ||
| 118 | + | For the definition of the format codes, see the | |
| 119 | + | zwp_linux_buffer_params_v1::create request. | |
| 120 | + | ||
| 121 | + | Starting version 4, the format event is deprecated and must not be | |
| 122 | + | sent by compositors. Instead, use get_default_feedback or | |
| 123 | + | get_surface_feedback. | |
| 124 | + | </description> | |
| 125 | + | <arg name="format" type="uint" summary="DRM_FORMAT code"/> | |
| 126 | + | </event> | |
| 127 | + | ||
| 128 | + | <event name="modifier" since="3" deprecated-since="4"> | |
| 129 | + | <description summary="supported buffer format modifier"> | |
| 130 | + | This event advertises the formats that the server supports, along with | |
| 131 | + | the modifiers supported for each format. All the supported modifiers | |
| 132 | + | for all the supported formats are advertised once when the client | |
| 133 | + | binds to this interface. A roundtrip after binding guarantees that | |
| 134 | + | the client has received all supported format-modifier pairs. | |
| 135 | + | ||
| 136 | + | For legacy support, DRM_FORMAT_MOD_INVALID (that is, modifier_hi == | |
| 137 | + | 0x00ffffff and modifier_lo == 0xffffffff) is allowed in this event. | |
| 138 | + | It indicates that the server can support the format with an implicit | |
| 139 | + | modifier. When a plane has DRM_FORMAT_MOD_INVALID as its modifier, it | |
| 140 | + | is as if no explicit modifier is specified. The effective modifier | |
| 141 | + | will be derived from the dmabuf. | |
| 142 | + | ||
| 143 | + | A compositor that sends valid modifiers and DRM_FORMAT_MOD_INVALID for | |
| 144 | + | a given format supports both explicit modifiers and implicit modifiers. | |
| 145 | + | ||
| 146 | + | For the definition of the format and modifier codes, see the | |
| 147 | + | zwp_linux_buffer_params_v1::create and zwp_linux_buffer_params_v1::add | |
| 148 | + | requests. | |
| 149 | + | ||
| 150 | + | Starting version 4, the modifier event is deprecated and must not be | |
| 151 | + | sent by compositors. Instead, use get_default_feedback or | |
| 152 | + | get_surface_feedback. | |
| 153 | + | </description> | |
| 154 | + | <arg name="format" type="uint" summary="DRM_FORMAT code"/> | |
| 155 | + | <arg name="modifier_hi" type="uint" | |
| 156 | + | summary="high 32 bits of layout modifier"/> | |
| 157 | + | <arg name="modifier_lo" type="uint" | |
| 158 | + | summary="low 32 bits of layout modifier"/> | |
| 159 | + | </event> | |
| 160 | + | ||
| 161 | + | <!-- Version 4 additions --> | |
| 162 | + | ||
| 163 | + | <request name="get_default_feedback" since="4"> | |
| 164 | + | <description summary="get default feedback"> | |
| 165 | + | This request creates a new zwp_linux_dmabuf_feedback_v1 object not bound | |
| 166 | + | to a particular surface. This object will deliver feedback about dmabuf | |
| 167 | + | parameters to use if the client doesn't support per-surface feedback | |
| 168 | + | (see get_surface_feedback). | |
| 169 | + | </description> | |
| 170 | + | <arg name="id" type="new_id" interface="zwp_linux_dmabuf_feedback_v1"/> | |
| 171 | + | </request> | |
| 172 | + | ||
| 173 | + | <request name="get_surface_feedback" since="4"> | |
| 174 | + | <description summary="get feedback for a surface"> | |
| 175 | + | This request creates a new zwp_linux_dmabuf_feedback_v1 object for the | |
| 176 | + | specified wl_surface. This object will deliver feedback about dmabuf | |
| 177 | + | parameters to use for buffers attached to this surface. | |
| 178 | + | ||
| 179 | + | If the surface is destroyed before the zwp_linux_dmabuf_feedback_v1 object, | |
| 180 | + | the feedback object becomes inert. | |
| 181 | + | </description> | |
| 182 | + | <arg name="id" type="new_id" interface="zwp_linux_dmabuf_feedback_v1"/> | |
| 183 | + | <arg name="surface" type="object" interface="wl_surface"/> | |
| 184 | + | </request> | |
| 185 | + | </interface> | |
| 186 | + | ||
| 187 | + | <interface name="zwp_linux_buffer_params_v1" version="6"> | |
| 188 | + | <description summary="parameters for creating a dmabuf-based wl_buffer"> | |
| 189 | + | This temporary object is a collection of dmabufs and other | |
| 190 | + | parameters that together form a single logical buffer. The temporary | |
| 191 | + | object may eventually create one wl_buffer unless cancelled by | |
| 192 | + | destroying it before requesting 'create'. | |
| 193 | + | ||
| 194 | + | Single-planar formats only require one dmabuf, however | |
| 195 | + | multi-planar formats may require more than one dmabuf. For all | |
| 196 | + | formats, an 'add' request must be called once per plane (even if the | |
| 197 | + | underlying dmabuf fd is identical). | |
| 198 | + | ||
| 199 | + | You must use consecutive plane indices ('plane_idx' argument for 'add') | |
| 200 | + | from zero to the number of planes used by the drm_fourcc format code. | |
| 201 | + | All planes required by the format must be given exactly once, but can | |
| 202 | + | be given in any order. Each plane index can only be set once; subsequent | |
| 203 | + | calls with a plane index which has already been set will result in a | |
| 204 | + | plane_set error being generated. | |
| 205 | + | </description> | |
| 206 | + | ||
| 207 | + | <enum name="error"> | |
| 208 | + | <entry name="already_used" value="0" | |
| 209 | + | summary="the zwp_linux_buffer_params_v1 object has already been used to create a wl_buffer"/> | |
| 210 | + | <entry name="plane_idx" value="1" | |
| 211 | + | summary="plane index out of bounds"/> | |
| 212 | + | <entry name="plane_set" value="2" | |
| 213 | + | summary="the plane index was already set"/> | |
| 214 | + | <entry name="incomplete" value="3" | |
| 215 | + | summary="missing or too many planes to create a buffer"/> | |
| 216 | + | <entry name="invalid_format" value="4" | |
| 217 | + | summary="format not supported"/> | |
| 218 | + | <entry name="invalid_dimensions" value="5" | |
| 219 | + | summary="invalid width or height"/> | |
| 220 | + | <entry name="out_of_bounds" value="6" | |
| 221 | + | summary="offset + stride * height goes out of dmabuf bounds"/> | |
| 222 | + | <entry name="invalid_wl_buffer" value="7" | |
| 223 | + | summary="invalid wl_buffer resulted from importing dmabufs via | |
| 224 | + | the create_immed request on given buffer_params"/> | |
| 225 | + | <entry name="invalid_dev_t_size" value="8" | |
| 226 | + | summary="an array with mismatching size for a dev_t was used"/> | |
| 227 | + | </enum> | |
| 228 | + | ||
| 229 | + | <request name="destroy" type="destructor"> | |
| 230 | + | <description summary="delete this object, used or not"> | |
| 231 | + | Cleans up the temporary data sent to the server for dmabuf-based | |
| 232 | + | wl_buffer creation. | |
| 233 | + | </description> | |
| 234 | + | </request> | |
| 235 | + | ||
| 236 | + | <request name="add"> | |
| 237 | + | <description summary="add a dmabuf to the temporary set"> | |
| 238 | + | This request adds one dmabuf to the set in this | |
| 239 | + | zwp_linux_buffer_params_v1. | |
| 240 | + | ||
| 241 | + | The 64-bit unsigned value combined from modifier_hi and modifier_lo | |
| 242 | + | is the dmabuf layout modifier. DRM AddFB2 ioctl calls this the | |
| 243 | + | fb modifier, which is defined in drm_mode.h of Linux UAPI. | |
| 244 | + | This is an opaque token. Drivers use this token to express tiling, | |
| 245 | + | compression, etc. driver-specific modifications to the base format | |
| 246 | + | defined by the DRM fourcc code. | |
| 247 | + | ||
| 248 | + | Starting from version 4, the invalid_format protocol error is sent if | |
| 249 | + | the format + modifier pair was not advertised as supported. | |
| 250 | + | ||
| 251 | + | Starting from version 5, the invalid_format protocol error is sent if | |
| 252 | + | all planes don't use the same modifier. | |
| 253 | + | ||
| 254 | + | This request raises the PLANE_IDX error if plane_idx is too large. | |
| 255 | + | The error PLANE_SET is raised if attempting to set a plane that | |
| 256 | + | was already set. | |
| 257 | + | </description> | |
| 258 | + | <arg name="fd" type="fd" summary="dmabuf fd"/> | |
| 259 | + | <arg name="plane_idx" type="uint" summary="plane index"/> | |
| 260 | + | <arg name="offset" type="uint" summary="offset in bytes"/> | |
| 261 | + | <arg name="stride" type="uint" summary="stride in bytes"/> | |
| 262 | + | <arg name="modifier_hi" type="uint" | |
| 263 | + | summary="high 32 bits of layout modifier"/> | |
| 264 | + | <arg name="modifier_lo" type="uint" | |
| 265 | + | summary="low 32 bits of layout modifier"/> | |
| 266 | + | </request> | |
| 267 | + | ||
| 268 | + | <enum name="flags" bitfield="true"> | |
| 269 | + | <entry name="y_invert" value="1" summary="contents are y-inverted"/> | |
| 270 | + | <entry name="interlaced" value="2" summary="content is interlaced"/> | |
| 271 | + | <entry name="bottom_first" value="4" summary="bottom field first"/> | |
| 272 | + | </enum> | |
| 273 | + | ||
| 274 | + | <request name="create"> | |
| 275 | + | <description summary="create a wl_buffer from the given dmabufs"> | |
| 276 | + | This asks for creation of a wl_buffer from the added dmabuf | |
| 277 | + | buffers. The wl_buffer is not created immediately but returned via | |
| 278 | + | the 'created' event if the dmabuf sharing succeeds. The sharing | |
| 279 | + | may fail at runtime for reasons a client cannot predict, in | |
| 280 | + | which case the 'failed' event is triggered. | |
| 281 | + | ||
| 282 | + | The 'format' argument is a DRM_FORMAT code, as defined by the | |
| 283 | + | libdrm's drm_fourcc.h. The Linux kernel's DRM sub-system is the | |
| 284 | + | authoritative source on how the format codes should work. | |
| 285 | + | ||
| 286 | + | The 'flags' is a bitfield of the flags defined in enum "flags". | |
| 287 | + | 'y_invert' means that the image needs to be y-flipped. | |
| 288 | + | ||
| 289 | + | Flag 'interlaced' means that the frame in the buffer is not | |
| 290 | + | progressive as usual, but interlaced. An interlaced buffer as | |
| 291 | + | supported here must always contain both top and bottom fields. | |
| 292 | + | The top field always begins on the first pixel row. The temporal | |
| 293 | + | ordering between the two fields is top field first, unless | |
| 294 | + | 'bottom_first' is specified. It is undefined whether 'bottom_first' | |
| 295 | + | is ignored if 'interlaced' is not set. | |
| 296 | + | ||
| 297 | + | This protocol does not convey any information about field rate, | |
| 298 | + | duration, or timing, other than the relative ordering between the | |
| 299 | + | two fields in one buffer. A compositor may have to estimate the | |
| 300 | + | intended field rate from the incoming buffer rate. It is undefined | |
| 301 | + | whether the time of receiving wl_surface.commit with a new buffer | |
| 302 | + | attached, applying the wl_surface state, wl_surface.frame callback | |
| 303 | + | trigger, presentation, or any other point in the compositor cycle | |
| 304 | + | is used to measure the frame or field times. There is no support | |
| 305 | + | for detecting missed or late frames/fields/buffers either, and | |
| 306 | + | there is no support whatsoever for cooperating with interlaced | |
| 307 | + | compositor output. | |
| 308 | + | ||
| 309 | + | The composited image quality resulting from the use of interlaced | |
| 310 | + | buffers is explicitly undefined. A compositor may use elaborate | |
| 311 | + | hardware features or software to deinterlace and create progressive | |
| 312 | + | output frames from a sequence of interlaced input buffers, or it | |
| 313 | + | may produce substandard image quality. However, compositors that | |
| 314 | + | cannot guarantee reasonable image quality in all cases are recommended | |
| 315 | + | to just reject all interlaced buffers. | |
| 316 | + | ||
| 317 | + | Any argument errors, including non-positive width or height, | |
| 318 | + | mismatch between the number of planes and the format, bad | |
| 319 | + | format, bad offset or stride, may be indicated by fatal protocol | |
| 320 | + | errors: INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS, | |
| 321 | + | OUT_OF_BOUNDS. | |
| 322 | + | ||
| 323 | + | Dmabuf import errors in the server that are not obvious client | |
| 324 | + | bugs are returned via the 'failed' event as non-fatal. This | |
| 325 | + | allows attempting dmabuf sharing and falling back in the client | |
| 326 | + | if it fails. | |
| 327 | + | ||
| 328 | + | This request can be sent only once in the object's lifetime, after | |
| 329 | + | which the only legal request is destroy. This object should be | |
| 330 | + | destroyed after issuing a 'create' request. Attempting to use this | |
| 331 | + | object after issuing 'create' raises the ALREADY_USED protocol error. | |
| 332 | + | ||
| 333 | + | It is not mandatory to issue 'create'. If a client wants to | |
| 334 | + | cancel the buffer creation, it can just destroy this object. | |
| 335 | + | </description> | |
| 336 | + | <arg name="width" type="int" summary="base plane width in pixels"/> | |
| 337 | + | <arg name="height" type="int" summary="base plane height in pixels"/> | |
| 338 | + | <arg name="format" type="uint" summary="DRM_FORMAT code"/> | |
| 339 | + | <arg name="flags" type="uint" enum="flags" summary="see enum flags"/> | |
| 340 | + | </request> | |
| 341 | + | ||
| 342 | + | <event name="created"> | |
| 343 | + | <description summary="buffer creation succeeded"> | |
| 344 | + | This event indicates that the attempted buffer creation was | |
| 345 | + | successful. It provides the new wl_buffer referencing the dmabuf(s). | |
| 346 | + | ||
| 347 | + | Upon receiving this event, the client should destroy the | |
| 348 | + | zwp_linux_buffer_params_v1 object. | |
| 349 | + | </description> | |
| 350 | + | <arg name="buffer" type="new_id" interface="wl_buffer" | |
| 351 | + | summary="id for the the newly created wl_buffer"/> | |
| 352 | + | </event> | |
| 353 | + | ||
| 354 | + | <event name="failed"> | |
| 355 | + | <description summary="buffer creation failed"> | |
| 356 | + | This event indicates that the attempted buffer creation has | |
| 357 | + | failed. It usually means that one of the dmabuf constraints | |
| 358 | + | has not been fulfilled. | |
| 359 | + | ||
| 360 | + | Upon receiving this event, the client should destroy the | |
| 361 | + | zwp_linux_buffer_params_v1 object. | |
| 362 | + | </description> | |
| 363 | + | </event> | |
| 364 | + | ||
| 365 | + | <request name="create_immed" since="2"> | |
| 366 | + | <description summary="immediately create a wl_buffer from the given | |
| 367 | + | dmabufs"> | |
| 368 | + | This asks for immediate creation of a wl_buffer by importing the | |
| 369 | + | added dmabufs. | |
| 370 | + | ||
| 371 | + | In case of import success, no event is sent from the server, and the | |
| 372 | + | wl_buffer is ready to be used by the client. | |
| 373 | + | ||
| 374 | + | Upon import failure, either of the following may happen, as seen fit | |
| 375 | + | by the implementation: | |
| 376 | + | - the client is terminated with one of the following fatal protocol | |
| 377 | + | errors: | |
| 378 | + | - INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS, OUT_OF_BOUNDS, | |
| 379 | + | in case of argument errors such as mismatch between the number | |
| 380 | + | of planes and the format, bad format, non-positive width or | |
| 381 | + | height, or bad offset or stride. | |
| 382 | + | - INVALID_WL_BUFFER, in case the cause for failure is unknown or | |
| 383 | + | platform specific. | |
| 384 | + | - the server creates an invalid wl_buffer, marks it as failed and | |
| 385 | + | sends a 'failed' event to the client. The result of using this | |
| 386 | + | invalid wl_buffer as an argument in any request by the client is | |
| 387 | + | defined by the compositor implementation. | |
| 388 | + | ||
| 389 | + | This takes the same arguments as a 'create' request, and obeys the | |
| 390 | + | same restrictions. | |
| 391 | + | </description> | |
| 392 | + | <arg name="buffer_id" type="new_id" interface="wl_buffer" | |
| 393 | + | summary="id for the newly created wl_buffer"/> | |
| 394 | + | <arg name="width" type="int" summary="base plane width in pixels"/> | |
| 395 | + | <arg name="height" type="int" summary="base plane height in pixels"/> | |
| 396 | + | <arg name="format" type="uint" summary="DRM_FORMAT code"/> | |
| 397 | + | <arg name="flags" type="uint" enum="flags" summary="see enum flags"/> | |
| 398 | + | </request> | |
| 399 | + | ||
| 400 | + | <request name="set_sampling_device" since="6"> | |
| 401 | + | <description summary="set the target device of the wl_buffer"> | |
| 402 | + | Set the device the compositor should import the dmabufs to for sampling | |
| 403 | + | in the next create or create_immed request. | |
| 404 | + | ||
| 405 | + | To avoid race conditions when the compositor removes a device from the | |
| 406 | + | tranches, it is not a protocol error if the device hasn't been advertised | |
| 407 | + | by the compositor in a tranche with the sampling flag, but the import is | |
| 408 | + | likely to fail in that case. | |
| 409 | + | ||
| 410 | + | If the client doesn't know a suitable target device, it shouldn't set one, | |
| 411 | + | and the compositor should attempt import on all devices it supports. | |
| 412 | + | ||
| 413 | + | If the array is too small to contain a dev_t or larger than required, the | |
| 414 | + | invalid_dev_t_size error will be emitted. | |
| 415 | + | </description> | |
| 416 | + | <arg name="device" type="array" summary="device dev_t value"/> | |
| 417 | + | </request> | |
| 418 | + | </interface> | |
| 419 | + | ||
| 420 | + | <interface name="zwp_linux_dmabuf_feedback_v1" version="6"> | |
| 421 | + | <description summary="dmabuf feedback"> | |
| 422 | + | This object advertises dmabuf parameters feedback. This includes the | |
| 423 | + | preferred devices and the supported formats/modifiers. | |
| 424 | + | ||
| 425 | + | The parameters are sent once when this object is created and whenever they | |
| 426 | + | change. The done event is always sent once after all parameters have been | |
| 427 | + | sent. When a single parameter changes, all parameters are re-sent by the | |
| 428 | + | compositor. | |
| 429 | + | ||
| 430 | + | Compositors can re-send the parameters when the current client buffer | |
| 431 | + | allocations are sub-optimal. Compositors should not re-send the | |
| 432 | + | parameters if re-allocating the buffers would not result in a more optimal | |
| 433 | + | configuration. In particular, compositors should avoid sending the exact | |
| 434 | + | same parameters multiple times in a row. | |
| 435 | + | ||
| 436 | + | The tranche_target_device and tranche_formats events are grouped by | |
| 437 | + | tranches of preference. For each tranche, a tranche_target_device, one | |
| 438 | + | tranche_flags and one or more tranche_formats events are sent, followed | |
| 439 | + | by a tranche_done event finishing the list. The tranches are sent in | |
| 440 | + | descending order of preference. All formats and modifiers in the same | |
| 441 | + | tranche have the same preference. | |
| 442 | + | ||
| 443 | + | To send parameters, the compositor sends one main_device event (unless | |
| 444 | + | the client bound version 6 or above), tranches (each consisting of one | |
| 445 | + | tranche_target_device event, one tranche_flags event, tranche_formats | |
| 446 | + | events and then a tranche_done event), then one done event. | |
| 447 | + | ||
| 448 | + | With version 6 and above, the compositor must always advertise at least | |
| 449 | + | one tranche with the sampling flag set. | |
| 450 | + | </description> | |
| 451 | + | ||
| 452 | + | <request name="destroy" type="destructor"> | |
| 453 | + | <description summary="destroy the feedback object"> | |
| 454 | + | Using this request a client can tell the server that it is not going to | |
| 455 | + | use the zwp_linux_dmabuf_feedback_v1 object anymore. | |
| 456 | + | </description> | |
| 457 | + | </request> | |
| 458 | + | ||
| 459 | + | <event name="done"> | |
| 460 | + | <description summary="all feedback has been sent"> | |
| 461 | + | This event is sent after all parameters of a zwp_linux_dmabuf_feedback_v1 | |
| 462 | + | object have been sent. | |
| 463 | + | ||
| 464 | + | This allows changes to the zwp_linux_dmabuf_feedback_v1 parameters to be | |
| 465 | + | seen as atomic, even if they happen via multiple events. | |
| 466 | + | </description> | |
| 467 | + | </event> | |
| 468 | + | ||
| 469 | + | <event name="format_table"> | |
| 470 | + | <description summary="format and modifier table"> | |
| 471 | + | This event provides a file descriptor which can be memory-mapped to | |
| 472 | + | access the format and modifier table. | |
| 473 | + | ||
| 474 | + | The table contains a tightly packed array of consecutive format + | |
| 475 | + | modifier pairs. Each pair is 16 bytes wide. It contains a format as a | |
| 476 | + | 32-bit unsigned integer, followed by 4 bytes of unused padding, and a | |
| 477 | + | modifier as a 64-bit unsigned integer. The native endianness is used. | |
| 478 | + | ||
| 479 | + | The client must map the file descriptor in read-only private mode. | |
| 480 | + | ||
| 481 | + | Compositors are not allowed to mutate the table file contents once this | |
| 482 | + | event has been sent. Instead, compositors must create a new, separate | |
| 483 | + | table file and re-send feedback parameters. Compositors are allowed to | |
| 484 | + | store duplicate format + modifier pairs in the table. | |
| 485 | + | </description> | |
| 486 | + | <arg name="fd" type="fd" summary="table file descriptor"/> | |
| 487 | + | <arg name="size" type="uint" summary="table size, in bytes"/> | |
| 488 | + | </event> | |
| 489 | + | ||
| 490 | + | <event name="main_device" deprecated-since="6"> | |
| 491 | + | <description summary="preferred main device"> | |
| 492 | + | This event advertises the main device that the server prefers to use | |
| 493 | + | when direct scan-out to the target device isn't possible. The | |
| 494 | + | advertised main device may be different for each | |
| 495 | + | zwp_linux_dmabuf_feedback_v1 object, and may change over time. | |
| 496 | + | ||
| 497 | + | There is exactly one main device. The compositor must send at least | |
| 498 | + | one preference tranche with tranche_target_device equal to main_device. | |
| 499 | + | ||
| 500 | + | Clients need to create buffers that the main device can import and | |
| 501 | + | read from, otherwise creating the dmabuf wl_buffer will fail (see the | |
| 502 | + | zwp_linux_buffer_params_v1.create and create_immed requests for details). | |
| 503 | + | The main device will also likely be kept active by the compositor, | |
| 504 | + | so clients can use it instead of waking up another device for power | |
| 505 | + | savings. | |
| 506 | + | ||
| 507 | + | In general the device is a DRM node. The DRM node type (primary vs. | |
| 508 | + | render) is unspecified. Clients must not rely on the compositor sending | |
| 509 | + | a particular node type. Clients cannot check two devices for equality | |
| 510 | + | by comparing the dev_t value. | |
| 511 | + | ||
| 512 | + | If explicit modifiers are not supported and the client performs buffer | |
| 513 | + | allocations on a different device than the main device, then the client | |
| 514 | + | must force the buffer to have a linear layout. | |
| 515 | + | ||
| 516 | + | With version 6 and above, this event is no longer sent. Clients should | |
| 517 | + | use a device with the sampling flag in the tranches instead. | |
| 518 | + | </description> | |
| 519 | + | <arg name="device" type="array" summary="device dev_t value"/> | |
| 520 | + | </event> | |
| 521 | + | ||
| 522 | + | <event name="tranche_done"> | |
| 523 | + | <description summary="a preference tranche has been sent"> | |
| 524 | + | This event splits tranche_target_device and tranche_formats events into | |
| 525 | + | preference tranches. It is sent after a set of tranche_target_device | |
| 526 | + | and tranche_formats events; it represents the end of a tranche. The | |
| 527 | + | next tranche will have a lower preference. | |
| 528 | + | </description> | |
| 529 | + | </event> | |
| 530 | + | ||
| 531 | + | <event name="tranche_target_device"> | |
| 532 | + | <description summary="target device"> | |
| 533 | + | This event advertises the target device that the server prefers to use | |
| 534 | + | for a buffer created given this tranche. The advertised target device | |
| 535 | + | may be different for each preference tranche, and may change over time. | |
| 536 | + | ||
| 537 | + | There is exactly one target device per tranche. | |
| 538 | + | ||
| 539 | + | The target device may be a scan-out device, for example if the | |
| 540 | + | compositor prefers to directly scan-out a buffer created given this | |
| 541 | + | tranche. The target device may be a rendering device, for example if | |
| 542 | + | the compositor prefers to texture from said buffer. | |
| 543 | + | ||
| 544 | + | The client can use this hint to allocate the buffer in a way that makes | |
| 545 | + | it accessible from the target device, ideally directly. The buffer must | |
| 546 | + | still be accessible from a device with the sampling flag, either through | |
| 547 | + | direct import or a potentially more expensive fallback path. If the | |
| 548 | + | buffer can't be directly imported for sampling, then clients must be | |
| 549 | + | prepared for the compositor changing the tranche priority or making | |
| 550 | + | wl_buffer creation fail (see the zwp_linux_buffer_params_v1.create and | |
| 551 | + | create_immed requests for details). | |
| 552 | + | ||
| 553 | + | If the device is a DRM node, the DRM node type (primary vs. render) is | |
| 554 | + | unspecified. Clients must not rely on the compositor sending a | |
| 555 | + | particular node type. Clients cannot check two devices for equality by | |
| 556 | + | comparing the dev_t value. | |
| 557 | + | ||
| 558 | + | This event is tied to a preference tranche, see the tranche_done event. | |
| 559 | + | </description> | |
| 560 | + | <arg name="device" type="array" summary="device dev_t value"/> | |
| 561 | + | </event> | |
| 562 | + | ||
| 563 | + | <event name="tranche_formats"> | |
| 564 | + | <description summary="supported buffer format modifiers"> | |
| 565 | + | This event advertises the format + modifier combinations that the | |
| 566 | + | compositor supports. | |
| 567 | + | ||
| 568 | + | It carries an array of indices, each referring to a format + modifier | |
| 569 | + | pair in the last received format table (see the format_table event). | |
| 570 | + | Each index is a 16-bit unsigned integer in native endianness. | |
| 571 | + | ||
| 572 | + | For legacy support, DRM_FORMAT_MOD_INVALID is an allowed modifier. | |
| 573 | + | It indicates that the server can support the format with an implicit | |
| 574 | + | modifier. When a buffer has DRM_FORMAT_MOD_INVALID as its modifier, it | |
| 575 | + | is as if no explicit modifier is specified. The effective modifier | |
| 576 | + | will be derived from the dmabuf. | |
| 577 | + | ||
| 578 | + | A compositor that sends valid modifiers and DRM_FORMAT_MOD_INVALID for | |
| 579 | + | a given format supports both explicit modifiers and implicit modifiers. | |
| 580 | + | ||
| 581 | + | Compositors must not send duplicate format + modifier pairs within the | |
| 582 | + | same tranche or across two different tranches with the same target | |
| 583 | + | device and flags. | |
| 584 | + | ||
| 585 | + | This event is tied to a preference tranche, see the tranche_done event. | |
| 586 | + | ||
| 587 | + | For the definition of the format and modifier codes, see the | |
| 588 | + | zwp_linux_buffer_params_v1.create request. | |
| 589 | + | </description> | |
| 590 | + | <arg name="indices" type="array" summary="array of 16-bit indexes"/> | |
| 591 | + | </event> | |
| 592 | + | ||
| 593 | + | <enum name="tranche_flags" bitfield="true"> | |
| 594 | + | <entry name="scanout" value="1" since="4"> | |
| 595 | + | <description summary="direct scan-out tranche"> | |
| 596 | + | The scanout flag is a hint that direct scan-out may be attempted by | |
| 597 | + | the compositor on the target device if the client appropriately | |
| 598 | + | allocates a buffer. How to allocate a buffer that can be scanned out | |
| 599 | + | on the target device is implementation-defined. | |
| 600 | + | </description> | |
| 601 | + | </entry> | |
| 602 | + | <entry name="sampling" value="2" since="6"> | |
| 603 | + | <description summary="sampling tranche"> | |
| 604 | + | The sampling flag describes that the compositor is able to efficiently | |
| 605 | + | sample from buffers imported to the target device if the client | |
| 606 | + | appropriately allocates a buffer. How to allocate a buffer that can be | |
| 607 | + | efficiently sampled on the target device is implementation defined. | |
| 608 | + | </description> | |
| 609 | + | </entry> | |
| 610 | + | </enum> | |
| 611 | + | ||
| 612 | + | <event name="tranche_flags"> | |
| 613 | + | <description summary="tranche flags"> | |
| 614 | + | This event sets tranche-specific flags. This event is tied to a | |
| 615 | + | preference tranche, see the tranche_done event. | |
| 616 | + | With version 6 and above, the compositor must set at least one flag | |
| 617 | + | in each tranche. | |
| 618 | + | </description> | |
| 619 | + | <arg name="flags" type="uint" enum="tranche_flags" summary="tranche flags"/> | |
| 620 | + | </event> | |
| 621 | + | </interface> | |
| 622 | + | ||
| 623 | + | </protocol> | |
A protocol/linuxdmabuf/linux-dmabuf-v1_generated.go +650 -0
| @@ -0,0 +1,650 @@ | |||
| 1 | + | // Code generated by wlturbo-scanner. DO NOT EDIT. | |
| 2 | + | // Protocol: linux_dmabuf_v1 | |
| 3 | + | // Source: linux-dmabuf-v1.xml | |
| 4 | + | ||
| 5 | + | package linuxdmabuf | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "sync" | |
| 9 | + | ||
| 10 | + | cross_wl_buffer "github.com/bnema/wlturbo/protocol/core" | |
| 11 | + | cross_wl_surface "github.com/bnema/wlturbo/protocol/core" | |
| 12 | + | "github.com/bnema/wlturbo/wl" | |
| 13 | + | ) | |
| 14 | + | ||
| 15 | + | // ERROR_ALREADY_USED is the error already_used value. | |
| 16 | + | const ERROR_ALREADY_USED = 0 | |
| 17 | + | ||
| 18 | + | // ERROR_PLANE_IDX is the error plane_idx value. | |
| 19 | + | const ERROR_PLANE_IDX = 1 | |
| 20 | + | ||
| 21 | + | // ERROR_PLANE_SET is the error plane_set value. | |
| 22 | + | const ERROR_PLANE_SET = 2 | |
| 23 | + | ||
| 24 | + | // ERROR_INCOMPLETE is the error incomplete value. | |
| 25 | + | const ERROR_INCOMPLETE = 3 | |
| 26 | + | ||
| 27 | + | // ERROR_INVALID_FORMAT is the error invalid_format value. | |
| 28 | + | const ERROR_INVALID_FORMAT = 4 | |
| 29 | + | ||
| 30 | + | // ERROR_INVALID_DIMENSIONS is the error invalid_dimensions value. | |
| 31 | + | const ERROR_INVALID_DIMENSIONS = 5 | |
| 32 | + | ||
| 33 | + | // ERROR_OUT_OF_BOUNDS is the error out_of_bounds value. | |
| 34 | + | const ERROR_OUT_OF_BOUNDS = 6 | |
| 35 | + | ||
| 36 | + | // ERROR_INVALID_WL_BUFFER is the error invalid_wl_buffer value. | |
| 37 | + | const ERROR_INVALID_WL_BUFFER = 7 | |
| 38 | + | ||
| 39 | + | // ERROR_INVALID_DEV_T_SIZE is the error invalid_dev_t_size value. | |
| 40 | + | const ERROR_INVALID_DEV_T_SIZE = 8 | |
| 41 | + | ||
| 42 | + | // FLAGS_Y_INVERT is the flags y_invert value. | |
| 43 | + | const FLAGS_Y_INVERT = 1 | |
| 44 | + | ||
| 45 | + | // FLAGS_INTERLACED is the flags interlaced value. | |
| 46 | + | const FLAGS_INTERLACED = 2 | |
| 47 | + | ||
| 48 | + | // FLAGS_BOTTOM_FIRST is the flags bottom_first value. | |
| 49 | + | const FLAGS_BOTTOM_FIRST = 4 | |
| 50 | + | ||
| 51 | + | // TRANCHE_FLAGS_SCANOUT is the tranche_flags scanout value. | |
| 52 | + | const TRANCHE_FLAGS_SCANOUT = 1 | |
| 53 | + | ||
| 54 | + | // TRANCHE_FLAGS_SAMPLING is the tranche_flags sampling value. | |
| 55 | + | const TRANCHE_FLAGS_SAMPLING = 2 | |
| 56 | + | ||
| 57 | + | // LinuxDmabufInterface is the protocol interface name. | |
| 58 | + | const LinuxDmabufInterface = "zwp_linux_dmabuf_v1" | |
| 59 | + | ||
| 60 | + | // LinuxDmabuf is the generated binding for zwp_linux_dmabuf_v1 version 6. | |
| 61 | + | type LinuxDmabuf struct { | |
| 62 | + | wl.BaseProxy | |
| 63 | + | // handlersMu guards the handler slices: a client may register or replace | |
| 64 | + | // handlers while another goroutine dispatches events. | |
| 65 | + | handlersMu sync.Mutex | |
| 66 | + | onFormat []func(format uint32) | |
| 67 | + | onModifier []func(format uint32, modifierHi uint32, modifierLo uint32) | |
| 68 | + | } | |
| 69 | + | ||
| 70 | + | // NewLinuxDmabuf returns an unbound LinuxDmabuf. | |
| 71 | + | func NewLinuxDmabuf(ctx *wl.Context) *LinuxDmabuf { | |
| 72 | + | obj := &LinuxDmabuf{} | |
| 73 | + | obj.SetContext(ctx) | |
| 74 | + | ctx.Register(obj) | |
| 75 | + | return obj | |
| 76 | + | } | |
| 77 | + | ||
| 78 | + | // Objects created through this interface, especially wl_buffers, will remain valid. | |
| 79 | + | func (o *LinuxDmabuf) Destroy() error { | |
| 80 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 81 | + | return err | |
| 82 | + | } | |
| 83 | + | if err := o.Context().SendDestructor(o, 0); err != nil { | |
| 84 | + | return err | |
| 85 | + | } | |
| 86 | + | return nil | |
| 87 | + | } | |
| 88 | + | ||
| 89 | + | // This temporary object is used to collect multiple dmabuf handles into a single batch to create a wl_buffer. It can only be used once and should be destroyed after a 'created' or 'failed' event has been received. | |
| 90 | + | func (o *LinuxDmabuf) CreateParams() (*LinuxBufferParams, error) { | |
| 91 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 92 | + | return nil, err | |
| 93 | + | } | |
| 94 | + | child := &LinuxBufferParams{} | |
| 95 | + | child.SetContext(o.Context()) | |
| 96 | + | child.SetID(o.Context().AllocateID()) | |
| 97 | + | o.Context().Register(child) | |
| 98 | + | if err := o.Context().SendRequest(o, 1, child); err != nil { | |
| 99 | + | o.Context().Unregister(child) | |
| 100 | + | return nil, err | |
| 101 | + | } | |
| 102 | + | return child, nil | |
| 103 | + | } | |
| 104 | + | ||
| 105 | + | // This request creates a new zwp_linux_dmabuf_feedback_v1 object not bound to a particular surface. This object will deliver feedback about dmabuf parameters to use if the client doesn't support per-surface feedback (see get_surface_feedback). | |
| 106 | + | func (o *LinuxDmabuf) GetDefaultFeedback() (*LinuxDmabufFeedback, error) { | |
| 107 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 108 | + | return nil, err | |
| 109 | + | } | |
| 110 | + | child := &LinuxDmabufFeedback{} | |
| 111 | + | child.SetContext(o.Context()) | |
| 112 | + | child.SetID(o.Context().AllocateID()) | |
| 113 | + | o.Context().Register(child) | |
| 114 | + | if err := o.Context().SendRequest(o, 2, child); err != nil { | |
| 115 | + | o.Context().Unregister(child) | |
| 116 | + | return nil, err | |
| 117 | + | } | |
| 118 | + | return child, nil | |
| 119 | + | } | |
| 120 | + | ||
| 121 | + | // This request creates a new zwp_linux_dmabuf_feedback_v1 object for the specified wl_surface. This object will deliver feedback about dmabuf parameters to use for buffers attached to this surface. If the surface is destroyed before the zwp_linux_dmabuf_feedback_v1 object, the feedback object becomes inert. | |
| 122 | + | func (o *LinuxDmabuf) GetSurfaceFeedback(surface *cross_wl_surface.Surface) (*LinuxDmabufFeedback, error) { | |
| 123 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 124 | + | return nil, err | |
| 125 | + | } | |
| 126 | + | var arg1 wl.Object | |
| 127 | + | if surface != nil { | |
| 128 | + | arg1 = surface | |
| 129 | + | } | |
| 130 | + | child := &LinuxDmabufFeedback{} | |
| 131 | + | child.SetContext(o.Context()) | |
| 132 | + | child.SetID(o.Context().AllocateID()) | |
| 133 | + | o.Context().Register(child) | |
| 134 | + | if err := o.Context().SendRequest(o, 3, child, arg1); err != nil { | |
| 135 | + | o.Context().Unregister(child) | |
| 136 | + | return nil, err | |
| 137 | + | } | |
| 138 | + | return child, nil | |
| 139 | + | } | |
| 140 | + | ||
| 141 | + | // OnFormat registers a handler for the format event. | |
| 142 | + | // | |
| 143 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 144 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 145 | + | func (o *LinuxDmabuf) OnFormat(handler func(format uint32)) { | |
| 146 | + | if handler == nil { | |
| 147 | + | return | |
| 148 | + | } | |
| 149 | + | o.handlersMu.Lock() | |
| 150 | + | defer o.handlersMu.Unlock() | |
| 151 | + | o.onFormat = append(o.onFormat, handler) | |
| 152 | + | } | |
| 153 | + | ||
| 154 | + | // handlersForFormat returns the handlers registered for format, taken | |
| 155 | + | // under the lock so a handler may register another handler while events are | |
| 156 | + | // being dispatched. | |
| 157 | + | func (o *LinuxDmabuf) handlersForFormat() []func(format uint32) { | |
| 158 | + | o.handlersMu.Lock() | |
| 159 | + | defer o.handlersMu.Unlock() | |
| 160 | + | return o.onFormat | |
| 161 | + | } | |
| 162 | + | ||
| 163 | + | // OnModifier registers a handler for the modifier event. | |
| 164 | + | // | |
| 165 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 166 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 167 | + | func (o *LinuxDmabuf) OnModifier(handler func(format uint32, modifierHi uint32, modifierLo uint32)) { | |
| 168 | + | if handler == nil { | |
| 169 | + | return | |
| 170 | + | } | |
| 171 | + | o.handlersMu.Lock() | |
| 172 | + | defer o.handlersMu.Unlock() | |
| 173 | + | o.onModifier = append(o.onModifier, handler) | |
| 174 | + | } | |
| 175 | + | ||
| 176 | + | // handlersForModifier returns the handlers registered for modifier, taken | |
| 177 | + | // under the lock so a handler may register another handler while events are | |
| 178 | + | // being dispatched. | |
| 179 | + | func (o *LinuxDmabuf) handlersForModifier() []func(format uint32, modifierHi uint32, modifierLo uint32) { | |
| 180 | + | o.handlersMu.Lock() | |
| 181 | + | defer o.handlersMu.Unlock() | |
| 182 | + | return o.onModifier | |
| 183 | + | } | |
| 184 | + | ||
| 185 | + | // EventSignature returns the declared event argument types in wire order. | |
| 186 | + | func (o *LinuxDmabuf) EventSignature(opcode uint16) (string, bool) { | |
| 187 | + | switch opcode { | |
| 188 | + | case 0: | |
| 189 | + | return "uint,", true | |
| 190 | + | case 1: | |
| 191 | + | return "uint,uint,uint,", true | |
| 192 | + | default: | |
| 193 | + | return "", false | |
| 194 | + | } | |
| 195 | + | } | |
| 196 | + | ||
| 197 | + | // Dispatch decodes one event on zwp_linux_dmabuf_v1 and calls the registered handlers. | |
| 198 | + | func (o *LinuxDmabuf) Dispatch(event *wl.Event) { | |
| 199 | + | switch event.Opcode { | |
| 200 | + | case 0: // format | |
| 201 | + | format := event.Uint32() | |
| 202 | + | ||
| 203 | + | for i, handler := range o.handlersForFormat() { | |
| 204 | + | _ = i | |
| 205 | + | handler(format) | |
| 206 | + | } | |
| 207 | + | case 1: // modifier | |
| 208 | + | format := event.Uint32() | |
| 209 | + | modifierHi := event.Uint32() | |
| 210 | + | modifierLo := event.Uint32() | |
| 211 | + | ||
| 212 | + | for i, handler := range o.handlersForModifier() { | |
| 213 | + | _ = i | |
| 214 | + | handler(format, modifierHi, modifierLo) | |
| 215 | + | } | |
| 216 | + | default: | |
| 217 | + | } | |
| 218 | + | } | |
| 219 | + | ||
| 220 | + | // LinuxBufferParamsInterface is the protocol interface name. | |
| 221 | + | const LinuxBufferParamsInterface = "zwp_linux_buffer_params_v1" | |
| 222 | + | ||
| 223 | + | // LinuxBufferParams is the generated binding for zwp_linux_buffer_params_v1 version 6. | |
| 224 | + | type LinuxBufferParams struct { | |
| 225 | + | wl.BaseProxy | |
| 226 | + | // handlersMu guards the handler slices: a client may register or replace | |
| 227 | + | // handlers while another goroutine dispatches events. | |
| 228 | + | handlersMu sync.Mutex | |
| 229 | + | onCreated []func(bufferObject *cross_wl_buffer.Buffer) | |
| 230 | + | onFailed []func() | |
| 231 | + | } | |
| 232 | + | ||
| 233 | + | // NewLinuxBufferParams returns an unbound LinuxBufferParams. | |
| 234 | + | func NewLinuxBufferParams(ctx *wl.Context) *LinuxBufferParams { | |
| 235 | + | obj := &LinuxBufferParams{} | |
| 236 | + | obj.SetContext(ctx) | |
| 237 | + | ctx.Register(obj) | |
| 238 | + | return obj | |
| 239 | + | } | |
| 240 | + | ||
| 241 | + | // Cleans up the temporary data sent to the server for dmabuf-based wl_buffer creation. | |
| 242 | + | func (o *LinuxBufferParams) Destroy() error { | |
| 243 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 244 | + | return err | |
| 245 | + | } | |
| 246 | + | if err := o.Context().SendDestructor(o, 0); err != nil { | |
| 247 | + | return err | |
| 248 | + | } | |
| 249 | + | return nil | |
| 250 | + | } | |
| 251 | + | ||
| 252 | + | // This request adds one dmabuf to the set in this zwp_linux_buffer_params_v1. The 64-bit unsigned value combined from modifier_hi and modifier_lo is the dmabuf layout modifier. DRM AddFB2 ioctl calls this the fb modifier, which is defined in drm_mode.h of Linux UAPI. This is an opaque token. Drivers use this token to express tiling, compression, etc. driver-specific modifications to the base format defined by the DRM fourcc code. Starting from version 4, the invalid_format protocol error is sent if the format + modifier pair was not advertised as supported. Starting from version 5, the invalid_format protocol error is sent if all planes don't use the same modifier. This request raises the PLANE_IDX error if plane_idx is too large. The error PLANE_SET is raised if attempting to set a plane that was already set. | |
| 253 | + | func (o *LinuxBufferParams) Add(fd int, planeIdx uint32, offset uint32, stride uint32, modifierHi uint32, modifierLo uint32) error { | |
| 254 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 255 | + | return err | |
| 256 | + | } | |
| 257 | + | if err := o.Context().SendRequestWithFDs(o, 1, []int{fd}, uintptr(fd), planeIdx, offset, stride, modifierHi, modifierLo); err != nil { | |
| 258 | + | return err | |
| 259 | + | } | |
| 260 | + | _ = wl.CloseSentFD(fd) | |
| 261 | + | return nil | |
| 262 | + | } | |
| 263 | + | ||
| 264 | + | // This asks for creation of a wl_buffer from the added dmabuf buffers. The wl_buffer is not created immediately but returned via the 'created' event if the dmabuf sharing succeeds. The sharing may fail at runtime for reasons a client cannot predict, in which case the 'failed' event is triggered. The 'format' argument is a DRM_FORMAT code, as defined by the libdrm's drm_fourcc.h. The Linux kernel's DRM sub-system is the authoritative source on how the format codes should work. The 'flags' is a bitfield of the flags defined in enum "flags". 'y_invert' means that the image needs to be y-flipped. Flag 'interlaced' means that the frame in the buffer is not progressive as usual, but interlaced. An interlaced buffer as supported here must always contain both top and bottom fields. The top field always begins on the first pixel row. The temporal ordering between the two fields is top field first, unless 'bottom_first' is specified. It is undefined whether 'bottom_first' is ignored if 'interlaced' is not set. This protocol does not convey any information about field rate, duration, or timing, other than the relative ordering between the two fields in one buffer. A compositor may have to estimate the intended field rate from the incoming buffer rate. It is undefined whether the time of receiving wl_surface.commit with a new buffer attached, applying the wl_surface state, wl_surface.frame callback trigger, presentation, or any other point in the compositor cycle is used to measure the frame or field times. There is no support for detecting missed or late frames/fields/buffers either, and there is no support whatsoever for cooperating with interlaced compositor output. The composited image quality resulting from the use of interlaced buffers is explicitly undefined. A compositor may use elaborate hardware features or software to deinterlace and create progressive output frames from a sequence of interlaced input buffers, or it may produce substandard image quality. However, compositors that cannot guarantee reasonable image quality in all cases are recommended to just reject all interlaced buffers. Any argument errors, including non-positive width or height, mismatch between the number of planes and the format, bad format, bad offset or stride, may be indicated by fatal protocol errors: INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS, OUT_OF_BOUNDS. Dmabuf import errors in the server that are not obvious client bugs are returned via the 'failed' event as non-fatal. This allows attempting dmabuf sharing and falling back in the client if it fails. This request can be sent only once in the object's lifetime, after which the only legal request is destroy. This object should be destroyed after issuing a 'create' request. Attempting to use this object after issuing 'create' raises the ALREADY_USED protocol error. It is not mandatory to issue 'create'. If a client wants to cancel the buffer creation, it can just destroy this object. | |
| 265 | + | func (o *LinuxBufferParams) Create(width int32, height int32, format uint32, flags uint32) error { | |
| 266 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 267 | + | return err | |
| 268 | + | } | |
| 269 | + | if err := o.Context().SendRequest(o, 2, width, height, format, flags); err != nil { | |
| 270 | + | return err | |
| 271 | + | } | |
| 272 | + | return nil | |
| 273 | + | } | |
| 274 | + | ||
| 275 | + | // This asks for immediate creation of a wl_buffer by importing the added dmabufs. In case of import success, no event is sent from the server, and the wl_buffer is ready to be used by the client. Upon import failure, either of the following may happen, as seen fit by the implementation: - the client is terminated with one of the following fatal protocol errors: - INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS, OUT_OF_BOUNDS, in case of argument errors such as mismatch between the number of planes and the format, bad format, non-positive width or height, or bad offset or stride. - INVALID_WL_BUFFER, in case the cause for failure is unknown or platform specific. - the server creates an invalid wl_buffer, marks it as failed and sends a 'failed' event to the client. The result of using this invalid wl_buffer as an argument in any request by the client is defined by the compositor implementation. This takes the same arguments as a 'create' request, and obeys the same restrictions. | |
| 276 | + | func (o *LinuxBufferParams) CreateImmed(width int32, height int32, format uint32, flags uint32) (*cross_wl_buffer.Buffer, error) { | |
| 277 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 278 | + | return nil, err | |
| 279 | + | } | |
| 280 | + | child := cross_wl_buffer.NewBuffer(o.Context()) | |
| 281 | + | child.SetID(o.Context().AllocateID()) | |
| 282 | + | o.Context().Register(child) | |
| 283 | + | if err := o.Context().SendRequest(o, 3, child, width, height, format, flags); err != nil { | |
| 284 | + | o.Context().Unregister(child) | |
| 285 | + | return nil, err | |
| 286 | + | } | |
| 287 | + | return child, nil | |
| 288 | + | } | |
| 289 | + | ||
| 290 | + | // Set the device the compositor should import the dmabufs to for sampling in the next create or create_immed request. To avoid race conditions when the compositor removes a device from the tranches, it is not a protocol error if the device hasn't been advertised by the compositor in a tranche with the sampling flag, but the import is likely to fail in that case. If the client doesn't know a suitable target device, it shouldn't set one, and the compositor should attempt import on all devices it supports. If the array is too small to contain a dev_t or larger than required, the invalid_dev_t_size error will be emitted. | |
| 291 | + | func (o *LinuxBufferParams) SetSamplingDevice(device []byte) error { | |
| 292 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 293 | + | return err | |
| 294 | + | } | |
| 295 | + | if err := o.Context().SendRequest(o, 4, device); err != nil { | |
| 296 | + | return err | |
| 297 | + | } | |
| 298 | + | return nil | |
| 299 | + | } | |
| 300 | + | ||
| 301 | + | // OnCreated registers a handler for the created event. | |
| 302 | + | // | |
| 303 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 304 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 305 | + | func (o *LinuxBufferParams) OnCreated(handler func(bufferObject *cross_wl_buffer.Buffer)) { | |
| 306 | + | if handler == nil { | |
| 307 | + | return | |
| 308 | + | } | |
| 309 | + | o.handlersMu.Lock() | |
| 310 | + | defer o.handlersMu.Unlock() | |
| 311 | + | o.onCreated = append(o.onCreated, handler) | |
| 312 | + | } | |
| 313 | + | ||
| 314 | + | // handlersForCreated returns the handlers registered for created, taken | |
| 315 | + | // under the lock so a handler may register another handler while events are | |
| 316 | + | // being dispatched. | |
| 317 | + | func (o *LinuxBufferParams) handlersForCreated() []func(bufferObject *cross_wl_buffer.Buffer) { | |
| 318 | + | o.handlersMu.Lock() | |
| 319 | + | defer o.handlersMu.Unlock() | |
| 320 | + | return o.onCreated | |
| 321 | + | } | |
| 322 | + | ||
| 323 | + | // OnFailed registers a handler for the failed event. | |
| 324 | + | // | |
| 325 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 326 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 327 | + | func (o *LinuxBufferParams) OnFailed(handler func()) { | |
| 328 | + | if handler == nil { | |
| 329 | + | return | |
| 330 | + | } | |
| 331 | + | o.handlersMu.Lock() | |
| 332 | + | defer o.handlersMu.Unlock() | |
| 333 | + | o.onFailed = append(o.onFailed, handler) | |
| 334 | + | } | |
| 335 | + | ||
| 336 | + | // handlersForFailed returns the handlers registered for failed, taken | |
| 337 | + | // under the lock so a handler may register another handler while events are | |
| 338 | + | // being dispatched. | |
| 339 | + | func (o *LinuxBufferParams) handlersForFailed() []func() { | |
| 340 | + | o.handlersMu.Lock() | |
| 341 | + | defer o.handlersMu.Unlock() | |
| 342 | + | return o.onFailed | |
| 343 | + | } | |
| 344 | + | ||
| 345 | + | // EventSignature returns the declared event argument types in wire order. | |
| 346 | + | func (o *LinuxBufferParams) EventSignature(opcode uint16) (string, bool) { | |
| 347 | + | switch opcode { | |
| 348 | + | case 0: | |
| 349 | + | return "new_id,", true | |
| 350 | + | case 1: | |
| 351 | + | return "", true | |
| 352 | + | default: | |
| 353 | + | return "", false | |
| 354 | + | } | |
| 355 | + | } | |
| 356 | + | ||
| 357 | + | // Dispatch decodes one event on zwp_linux_buffer_params_v1 and calls the registered handlers. | |
| 358 | + | func (o *LinuxBufferParams) Dispatch(event *wl.Event) { | |
| 359 | + | switch event.Opcode { | |
| 360 | + | case 0: // created | |
| 361 | + | bufferID := event.Uint32() | |
| 362 | + | bufferObject := cross_wl_buffer.NewBuffer(o.Context()) | |
| 363 | + | bufferObject.SetID(bufferID) | |
| 364 | + | o.Context().Register(bufferObject) | |
| 365 | + | ||
| 366 | + | for i, handler := range o.handlersForCreated() { | |
| 367 | + | _ = i | |
| 368 | + | handler(bufferObject) | |
| 369 | + | } | |
| 370 | + | case 1: // failed | |
| 371 | + | ||
| 372 | + | for i, handler := range o.handlersForFailed() { | |
| 373 | + | _ = i | |
| 374 | + | handler() | |
| 375 | + | } | |
| 376 | + | default: | |
| 377 | + | } | |
| 378 | + | } | |
| 379 | + | ||
| 380 | + | // LinuxDmabufFeedbackInterface is the protocol interface name. | |
| 381 | + | const LinuxDmabufFeedbackInterface = "zwp_linux_dmabuf_feedback_v1" | |
| 382 | + | ||
| 383 | + | // LinuxDmabufFeedback is the generated binding for zwp_linux_dmabuf_feedback_v1 version 6. | |
| 384 | + | type LinuxDmabufFeedback struct { | |
| 385 | + | wl.BaseProxy | |
| 386 | + | // handlersMu guards the handler slices: a client may register or replace | |
| 387 | + | // handlers while another goroutine dispatches events. | |
| 388 | + | handlersMu sync.Mutex | |
| 389 | + | onDone []func() | |
| 390 | + | onFormatTable []func(fd *wl.OwnedFD, size uint32) | |
| 391 | + | onMainDevice []func(device []byte) | |
| 392 | + | onTrancheDone []func() | |
| 393 | + | onTrancheTargetDevice []func(device []byte) | |
| 394 | + | onTrancheFormats []func(indices []byte) | |
| 395 | + | onTrancheFlags []func(flags uint32) | |
| 396 | + | } | |
| 397 | + | ||
| 398 | + | // NewLinuxDmabufFeedback returns an unbound LinuxDmabufFeedback. | |
| 399 | + | func NewLinuxDmabufFeedback(ctx *wl.Context) *LinuxDmabufFeedback { | |
| 400 | + | obj := &LinuxDmabufFeedback{} | |
| 401 | + | obj.SetContext(ctx) | |
| 402 | + | ctx.Register(obj) | |
| 403 | + | return obj | |
| 404 | + | } | |
| 405 | + | ||
| 406 | + | // Using this request a client can tell the server that it is not going to use the zwp_linux_dmabuf_feedback_v1 object anymore. | |
| 407 | + | func (o *LinuxDmabufFeedback) Destroy() error { | |
| 408 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 409 | + | return err | |
| 410 | + | } | |
| 411 | + | if err := o.Context().SendDestructor(o, 0); err != nil { | |
| 412 | + | return err | |
| 413 | + | } | |
| 414 | + | return nil | |
| 415 | + | } | |
| 416 | + | ||
| 417 | + | // OnDone registers a handler for the done event. | |
| 418 | + | // | |
| 419 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 420 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 421 | + | func (o *LinuxDmabufFeedback) OnDone(handler func()) { | |
| 422 | + | if handler == nil { | |
| 423 | + | return | |
| 424 | + | } | |
| 425 | + | o.handlersMu.Lock() | |
| 426 | + | defer o.handlersMu.Unlock() | |
| 427 | + | o.onDone = append(o.onDone, handler) | |
| 428 | + | } | |
| 429 | + | ||
| 430 | + | // handlersForDone returns the handlers registered for done, taken | |
| 431 | + | // under the lock so a handler may register another handler while events are | |
| 432 | + | // being dispatched. | |
| 433 | + | func (o *LinuxDmabufFeedback) handlersForDone() []func() { | |
| 434 | + | o.handlersMu.Lock() | |
| 435 | + | defer o.handlersMu.Unlock() | |
| 436 | + | return o.onDone | |
| 437 | + | } | |
| 438 | + | ||
| 439 | + | // OnFormatTable registers a handler for the format_table event. | |
| 440 | + | // | |
| 441 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 442 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 443 | + | func (o *LinuxDmabufFeedback) OnFormatTable(handler func(fd *wl.OwnedFD, size uint32)) { | |
| 444 | + | if handler == nil { | |
| 445 | + | return | |
| 446 | + | } | |
| 447 | + | o.handlersMu.Lock() | |
| 448 | + | defer o.handlersMu.Unlock() | |
| 449 | + | o.onFormatTable = append(o.onFormatTable, handler) | |
| 450 | + | } | |
| 451 | + | ||
| 452 | + | // handlersForFormatTable returns the handlers registered for format_table, taken | |
| 453 | + | // under the lock so a handler may register another handler while events are | |
| 454 | + | // being dispatched. | |
| 455 | + | func (o *LinuxDmabufFeedback) handlersForFormatTable() []func(fd *wl.OwnedFD, size uint32) { | |
| 456 | + | o.handlersMu.Lock() | |
| 457 | + | defer o.handlersMu.Unlock() | |
| 458 | + | return o.onFormatTable | |
| 459 | + | } | |
| 460 | + | ||
| 461 | + | // OnMainDevice registers a handler for the main_device event. | |
| 462 | + | // | |
| 463 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 464 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 465 | + | func (o *LinuxDmabufFeedback) OnMainDevice(handler func(device []byte)) { | |
| 466 | + | if handler == nil { | |
| 467 | + | return | |
| 468 | + | } | |
| 469 | + | o.handlersMu.Lock() | |
| 470 | + | defer o.handlersMu.Unlock() | |
| 471 | + | o.onMainDevice = append(o.onMainDevice, handler) | |
| 472 | + | } | |
| 473 | + | ||
| 474 | + | // handlersForMainDevice returns the handlers registered for main_device, taken | |
| 475 | + | // under the lock so a handler may register another handler while events are | |
| 476 | + | // being dispatched. | |
| 477 | + | func (o *LinuxDmabufFeedback) handlersForMainDevice() []func(device []byte) { | |
| 478 | + | o.handlersMu.Lock() | |
| 479 | + | defer o.handlersMu.Unlock() | |
| 480 | + | return o.onMainDevice | |
| 481 | + | } | |
| 482 | + | ||
| 483 | + | // OnTrancheDone registers a handler for the tranche_done event. | |
| 484 | + | // | |
| 485 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 486 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 487 | + | func (o *LinuxDmabufFeedback) OnTrancheDone(handler func()) { | |
| 488 | + | if handler == nil { | |
| 489 | + | return | |
| 490 | + | } | |
| 491 | + | o.handlersMu.Lock() | |
| 492 | + | defer o.handlersMu.Unlock() | |
| 493 | + | o.onTrancheDone = append(o.onTrancheDone, handler) | |
| 494 | + | } | |
| 495 | + | ||
| 496 | + | // handlersForTrancheDone returns the handlers registered for tranche_done, taken | |
| 497 | + | // under the lock so a handler may register another handler while events are | |
| 498 | + | // being dispatched. | |
| 499 | + | func (o *LinuxDmabufFeedback) handlersForTrancheDone() []func() { | |
| 500 | + | o.handlersMu.Lock() | |
| 501 | + | defer o.handlersMu.Unlock() | |
| 502 | + | return o.onTrancheDone | |
| 503 | + | } | |
| 504 | + | ||
| 505 | + | // OnTrancheTargetDevice registers a handler for the tranche_target_device event. | |
| 506 | + | // | |
| 507 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 508 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 509 | + | func (o *LinuxDmabufFeedback) OnTrancheTargetDevice(handler func(device []byte)) { | |
| 510 | + | if handler == nil { | |
| 511 | + | return | |
| 512 | + | } | |
| 513 | + | o.handlersMu.Lock() | |
| 514 | + | defer o.handlersMu.Unlock() | |
| 515 | + | o.onTrancheTargetDevice = append(o.onTrancheTargetDevice, handler) | |
| 516 | + | } | |
| 517 | + | ||
| 518 | + | // handlersForTrancheTargetDevice returns the handlers registered for tranche_target_device, taken | |
| 519 | + | // under the lock so a handler may register another handler while events are | |
| 520 | + | // being dispatched. | |
| 521 | + | func (o *LinuxDmabufFeedback) handlersForTrancheTargetDevice() []func(device []byte) { | |
| 522 | + | o.handlersMu.Lock() | |
| 523 | + | defer o.handlersMu.Unlock() | |
| 524 | + | return o.onTrancheTargetDevice | |
| 525 | + | } | |
| 526 | + | ||
| 527 | + | // OnTrancheFormats registers a handler for the tranche_formats event. | |
| 528 | + | // | |
| 529 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 530 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 531 | + | func (o *LinuxDmabufFeedback) OnTrancheFormats(handler func(indices []byte)) { | |
| 532 | + | if handler == nil { | |
| 533 | + | return | |
| 534 | + | } | |
| 535 | + | o.handlersMu.Lock() | |
| 536 | + | defer o.handlersMu.Unlock() | |
| 537 | + | o.onTrancheFormats = append(o.onTrancheFormats, handler) | |
| 538 | + | } | |
| 539 | + | ||
| 540 | + | // handlersForTrancheFormats returns the handlers registered for tranche_formats, taken | |
| 541 | + | // under the lock so a handler may register another handler while events are | |
| 542 | + | // being dispatched. | |
| 543 | + | func (o *LinuxDmabufFeedback) handlersForTrancheFormats() []func(indices []byte) { | |
| 544 | + | o.handlersMu.Lock() | |
| 545 | + | defer o.handlersMu.Unlock() | |
| 546 | + | return o.onTrancheFormats | |
| 547 | + | } | |
| 548 | + | ||
| 549 | + | // OnTrancheFlags registers a handler for the tranche_flags event. | |
| 550 | + | // | |
| 551 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 552 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 553 | + | func (o *LinuxDmabufFeedback) OnTrancheFlags(handler func(flags uint32)) { | |
| 554 | + | if handler == nil { | |
| 555 | + | return | |
| 556 | + | } | |
| 557 | + | o.handlersMu.Lock() | |
| 558 | + | defer o.handlersMu.Unlock() | |
| 559 | + | o.onTrancheFlags = append(o.onTrancheFlags, handler) | |
| 560 | + | } | |
| 561 | + | ||
| 562 | + | // handlersForTrancheFlags returns the handlers registered for tranche_flags, taken | |
| 563 | + | // under the lock so a handler may register another handler while events are | |
| 564 | + | // being dispatched. | |
| 565 | + | func (o *LinuxDmabufFeedback) handlersForTrancheFlags() []func(flags uint32) { | |
| 566 | + | o.handlersMu.Lock() | |
| 567 | + | defer o.handlersMu.Unlock() | |
| 568 | + | return o.onTrancheFlags | |
| 569 | + | } | |
| 570 | + | ||
| 571 | + | // EventSignature returns the declared event argument types in wire order. | |
| 572 | + | func (o *LinuxDmabufFeedback) EventSignature(opcode uint16) (string, bool) { | |
| 573 | + | switch opcode { | |
| 574 | + | case 0: | |
| 575 | + | return "", true | |
| 576 | + | case 1: | |
| 577 | + | return "fd,uint,", true | |
| 578 | + | case 2: | |
| 579 | + | return "array,", true | |
| 580 | + | case 3: | |
| 581 | + | return "", true | |
| 582 | + | case 4: | |
| 583 | + | return "array,", true | |
| 584 | + | case 5: | |
| 585 | + | return "array,", true | |
| 586 | + | case 6: | |
| 587 | + | return "uint,", true | |
| 588 | + | default: | |
| 589 | + | return "", false | |
| 590 | + | } | |
| 591 | + | } | |
| 592 | + | ||
| 593 | + | // Dispatch decodes one event on zwp_linux_dmabuf_feedback_v1 and calls the registered handlers. | |
| 594 | + | func (o *LinuxDmabufFeedback) Dispatch(event *wl.Event) { | |
| 595 | + | switch event.Opcode { | |
| 596 | + | case 0: // done | |
| 597 | + | ||
| 598 | + | for i, handler := range o.handlersForDone() { | |
| 599 | + | _ = i | |
| 600 | + | handler() | |
| 601 | + | } | |
| 602 | + | case 1: // format_table | |
| 603 | + | fd := event.FD() | |
| 604 | + | size := event.Uint32() | |
| 605 | + | ownedfd := fd | |
| 606 | + | defer ownedfd.Close() | |
| 607 | + | ||
| 608 | + | for i, handler := range o.handlersForFormatTable() { | |
| 609 | + | if i > 0 { | |
| 610 | + | fd = nil | |
| 611 | + | } | |
| 612 | + | handler(fd, size) | |
| 613 | + | } | |
| 614 | + | case 2: // main_device | |
| 615 | + | device := event.Array() | |
| 616 | + | ||
| 617 | + | for i, handler := range o.handlersForMainDevice() { | |
| 618 | + | _ = i | |
| 619 | + | handler(device) | |
| 620 | + | } | |
| 621 | + | case 3: // tranche_done | |
| 622 | + | ||
| 623 | + | for i, handler := range o.handlersForTrancheDone() { | |
| 624 | + | _ = i | |
| 625 | + | handler() | |
| 626 | + | } | |
| 627 | + | case 4: // tranche_target_device | |
| 628 | + | device := event.Array() | |
| 629 | + | ||
| 630 | + | for i, handler := range o.handlersForTrancheTargetDevice() { | |
| 631 | + | _ = i | |
| 632 | + | handler(device) | |
| 633 | + | } | |
| 634 | + | case 5: // tranche_formats | |
| 635 | + | indices := event.Array() | |
| 636 | + | ||
| 637 | + | for i, handler := range o.handlersForTrancheFormats() { | |
| 638 | + | _ = i | |
| 639 | + | handler(indices) | |
| 640 | + | } | |
| 641 | + | case 6: // tranche_flags | |
| 642 | + | flags := event.Uint32() | |
| 643 | + | ||
| 644 | + | for i, handler := range o.handlersForTrancheFlags() { | |
| 645 | + | _ = i | |
| 646 | + | handler(flags) | |
| 647 | + | } | |
| 648 | + | default: | |
| 649 | + | } | |
| 650 | + | } | |
A protocol/linuxdmabuf/table.go +68 -0
| @@ -0,0 +1,68 @@ | |||
| 1 | + | package linuxdmabuf | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "encoding/binary" | |
| 5 | + | "fmt" | |
| 6 | + | "io" | |
| 7 | + | ||
| 8 | + | "github.com/bnema/wlturbo/wl" | |
| 9 | + | "golang.org/x/sys/unix" | |
| 10 | + | ) | |
| 11 | + | ||
| 12 | + | // FormatEntry is one 16-byte format-table record. Padding is ignored. | |
| 13 | + | type FormatEntry struct { | |
| 14 | + | Format uint32 | |
| 15 | + | Modifier uint64 | |
| 16 | + | } | |
| 17 | + | ||
| 18 | + | // ReadFormatTable consumes an owned format_table descriptor, even on error. | |
| 19 | + | // It reads a bounded snapshot with pread rather than mapping compositor-owned | |
| 20 | + | // memory, avoiding SIGBUS if the backing file is unexpectedly truncated. | |
| 21 | + | func ReadFormatTable(fd *wl.OwnedFD, size uint32) ([]FormatEntry, error) { | |
| 22 | + | if fd == nil { | |
| 23 | + | return nil, fmt.Errorf("missing format table descriptor") | |
| 24 | + | } | |
| 25 | + | n, err := fd.Take() | |
| 26 | + | if err != nil { | |
| 27 | + | return nil, err | |
| 28 | + | } | |
| 29 | + | defer unix.Close(n) | |
| 30 | + | if size%16 != 0 || size > 16<<20 { | |
| 31 | + | return nil, fmt.Errorf("invalid format table size %d", size) | |
| 32 | + | } | |
| 33 | + | b := make([]byte, size) | |
| 34 | + | for off := 0; off < len(b); { | |
| 35 | + | read, e := unix.Pread(n, b[off:], int64(off)) | |
| 36 | + | off += read | |
| 37 | + | if e == unix.EINTR { | |
| 38 | + | continue | |
| 39 | + | } | |
| 40 | + | if e != nil { | |
| 41 | + | return nil, e | |
| 42 | + | } | |
| 43 | + | if read == 0 { | |
| 44 | + | return nil, io.ErrUnexpectedEOF | |
| 45 | + | } | |
| 46 | + | } | |
| 47 | + | entries := make([]FormatEntry, size/16) | |
| 48 | + | for i := range entries { | |
| 49 | + | off := i * 16 | |
| 50 | + | entries[i] = FormatEntry{binary.NativeEndian.Uint32(b[off:]), binary.NativeEndian.Uint64(b[off+8:])} | |
| 51 | + | } | |
| 52 | + | return entries, nil | |
| 53 | + | } | |
| 54 | + | ||
| 55 | + | // TrancheIndices decodes the uint16 indices into a format table. | |
| 56 | + | func TrancheIndices(data []byte, count int) ([]uint16, error) { | |
| 57 | + | if len(data)%2 != 0 { | |
| 58 | + | return nil, fmt.Errorf("odd tranche indices length") | |
| 59 | + | } | |
| 60 | + | indices := make([]uint16, len(data)/2) | |
| 61 | + | for i := range indices { | |
| 62 | + | indices[i] = binary.NativeEndian.Uint16(data[i*2:]) | |
| 63 | + | if int(indices[i]) >= count { | |
| 64 | + | return nil, fmt.Errorf("format table index %d out of range", indices[i]) | |
| 65 | + | } | |
| 66 | + | } | |
| 67 | + | return indices, nil | |
| 68 | + | } | |
A protocol/linuxdmabuf/table_test.go +28 -0
| @@ -0,0 +1,28 @@ | |||
| 1 | + | //go:build linux | |
| 2 | + | ||
| 3 | + | package linuxdmabuf_test | |
| 4 | + | ||
| 5 | + | import ( | |
| 6 | + | "encoding/binary" | |
| 7 | + | "github.com/bnema/wlturbo/protocol/linuxdmabuf" | |
| 8 | + | "testing" | |
| 9 | + | ) | |
| 10 | + | ||
| 11 | + | func TestTrancheIndices(t *testing.T) { | |
| 12 | + | b := make([]byte, 4) | |
| 13 | + | binary.NativeEndian.PutUint16(b, 1) | |
| 14 | + | binary.NativeEndian.PutUint16(b[2:], 0) | |
| 15 | + | got, e := linuxdmabuf.TrancheIndices(b, 2) | |
| 16 | + | if e != nil || len(got) != 2 || got[0] != 1 || got[1] != 0 { | |
| 17 | + | t.Fatalf("indices %v %v", got, e) | |
| 18 | + | } | |
| 19 | + | if _, e = linuxdmabuf.TrancheIndices(b, 1); e == nil { | |
| 20 | + | t.Fatal("out of range accepted") | |
| 21 | + | } | |
| 22 | + | if _, e = linuxdmabuf.TrancheIndices(b[:3], 2); e == nil { | |
| 23 | + | t.Fatal("odd length accepted") | |
| 24 | + | } | |
| 25 | + | if _, e = linuxdmabuf.ReadFormatTable(nil, 0); e == nil { | |
| 26 | + | t.Fatal("missing fd accepted") | |
| 27 | + | } | |
| 28 | + | } | |
A protocol/negotiation_test.go +50 -0
| @@ -0,0 +1,50 @@ | |||
| 1 | + | //go:build linux | |
| 2 | + | ||
| 3 | + | package protocol_test | |
| 4 | + | ||
| 5 | + | import ( | |
| 6 | + | "encoding/binary" | |
| 7 | + | "errors" | |
| 8 | + | "testing" | |
| 9 | + | ||
| 10 | + | "github.com/bnema/wlturbo" | |
| 11 | + | "github.com/bnema/wlturbo/protocol/xdgshell" | |
| 12 | + | ) | |
| 13 | + | ||
| 14 | + | func TestAnnouncedVersionNegotiation(t *testing.T) { | |
| 15 | + | c, p := pair(t) | |
| 16 | + | d, e := wlturbo.ConnectFromConn(c) | |
| 17 | + | noerr(t, e) | |
| 18 | + | defer d.Close() | |
| 19 | + | request(t, p) | |
| 20 | + | iface := xdgshell.XdgWmBaseInterface | |
| 21 | + | if v, e := d.Registry().BindNegotiated(iface, 6, xdgshell.NewXdgWmBase(d.Context())); v != 0 || !errors.Is(e, wlturbo.ErrGlobalNotFound) { | |
| 22 | + | t.Fatalf("missing: %d %v", v, e) | |
| 23 | + | } | |
| 24 | + | if v, e := d.Registry().BindNegotiated(iface, 0, xdgshell.NewXdgWmBase(d.Context())); v != 0 || e == nil || errors.Is(e, wlturbo.ErrGlobalNotFound) { | |
| 25 | + | t.Fatalf("unsupported zero: %d %v", v, e) | |
| 26 | + | } | |
| 27 | + | for i, version := range []uint32{2, 9} { | |
| 28 | + | name := uint32(30 + i) | |
| 29 | + | announcement := append(msg(0, 0, name)[8:], str(iface)...) | |
| 30 | + | announcement = append(announcement, msg(0, 0, version)[8:]...) | |
| 31 | + | send(t, p, payload(d.Registry().ID(), 0, announcement...)) | |
| 32 | + | noerr(t, d.Dispatch()) | |
| 33 | + | proxy := xdgshell.NewXdgWmBase(d.Context()) | |
| 34 | + | if v, e := d.Registry().BindNegotiated(iface, 0, proxy); v != 0 || e == nil || errors.Is(e, wlturbo.ErrGlobalNotFound) { | |
| 35 | + | t.Fatalf("supported zero with global: %d %v", v, e) | |
| 36 | + | } | |
| 37 | + | negotiated, e := d.Registry().BindNegotiated(iface, 6, proxy) | |
| 38 | + | noerr(t, e) | |
| 39 | + | want := version | |
| 40 | + | if want > 6 { | |
| 41 | + | want = 6 | |
| 42 | + | } | |
| 43 | + | _, op, body := request(t, p) | |
| 44 | + | if op != 0 || negotiated != want || binary.NativeEndian.Uint32(body) != name || binary.NativeEndian.Uint32(body[len(body)-8:]) != want || binary.NativeEndian.Uint32(body[len(body)-4:]) != proxy.ID() { | |
| 45 | + | t.Fatalf("server=%d negotiated=%d wire=%x", version, negotiated, body) | |
| 46 | + | } | |
| 47 | + | send(t, p, msg(d.Registry().ID(), 1, name)) | |
| 48 | + | noerr(t, d.Dispatch()) | |
| 49 | + | } | |
| 50 | + | } | |
A protocol/textinput/text-input-unstable-v3.xml +604 -0
| @@ -0,0 +1,604 @@ | |||
| 1 | + | <?xml version="1.0" encoding="UTF-8"?> | |
| 2 | + | ||
| 3 | + | <protocol name="text_input_unstable_v3"> | |
| 4 | + | <copyright> | |
| 5 | + | Copyright © 2012, 2013 Intel Corporation | |
| 6 | + | Copyright © 2015, 2016 Jan Arne Petersen | |
| 7 | + | Copyright © 2017, 2018 Red Hat, Inc. | |
| 8 | + | Copyright © 2018 Purism SPC | |
| 9 | + | ||
| 10 | + | Permission to use, copy, modify, distribute, and sell this | |
| 11 | + | software and its documentation for any purpose is hereby granted | |
| 12 | + | without fee, provided that the above copyright notice appear in | |
| 13 | + | all copies and that both that copyright notice and this permission | |
| 14 | + | notice appear in supporting documentation, and that the name of | |
| 15 | + | the copyright holders not be used in advertising or publicity | |
| 16 | + | pertaining to distribution of the software without specific, | |
| 17 | + | written prior permission. The copyright holders make no | |
| 18 | + | representations about the suitability of this software for any | |
| 19 | + | purpose. It is provided "as is" without express or implied | |
| 20 | + | warranty. | |
| 21 | + | ||
| 22 | + | THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS | |
| 23 | + | SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND | |
| 24 | + | FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY | |
| 25 | + | SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
| 26 | + | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN | |
| 27 | + | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, | |
| 28 | + | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF | |
| 29 | + | THIS SOFTWARE. | |
| 30 | + | </copyright> | |
| 31 | + | ||
| 32 | + | <description summary="Protocol for composing text"> | |
| 33 | + | This protocol allows compositors to act as input methods and to send text | |
| 34 | + | to applications. A text input object is used to manage state of what are | |
| 35 | + | typically text entry fields in the application. | |
| 36 | + | ||
| 37 | + | This document adheres to the RFC 2119 when using words like "must", | |
| 38 | + | "should", "may", etc. | |
| 39 | + | ||
| 40 | + | Warning! The protocol described in this file is experimental and | |
| 41 | + | backward incompatible changes may be made. Backward compatible changes | |
| 42 | + | may be added together with the corresponding interface version bump. | |
| 43 | + | Backward incompatible changes are done by bumping the version number in | |
| 44 | + | the protocol and interface names and resetting the interface version. | |
| 45 | + | Once the protocol is to be declared stable, the 'z' prefix and the | |
| 46 | + | version number in the protocol and interface names are removed and the | |
| 47 | + | interface version number is reset. | |
| 48 | + | </description> | |
| 49 | + | ||
| 50 | + | <interface name="zwp_text_input_v3" version="2"> | |
| 51 | + | <description summary="text input"> | |
| 52 | + | The zwp_text_input_v3 interface represents text input and input methods | |
| 53 | + | associated with a seat. It provides enter/leave events to follow the | |
| 54 | + | text input focus for a seat. | |
| 55 | + | ||
| 56 | + | Requests are used to enable/disable the text-input object and set | |
| 57 | + | state information like surrounding and selected text or the content type. | |
| 58 | + | The information about the entered text is sent to the text-input object | |
| 59 | + | via the preedit_string and commit_string events. | |
| 60 | + | ||
| 61 | + | Text is valid UTF-8 encoded, indices and lengths are in bytes. Indices | |
| 62 | + | must not point to middle bytes inside a code point: they must either | |
| 63 | + | point to the first byte of a code point or to the end of the buffer. | |
| 64 | + | Lengths must be measured between two valid indices. | |
| 65 | + | ||
| 66 | + | Focus moving throughout surfaces will result in the emission of | |
| 67 | + | zwp_text_input_v3.enter and zwp_text_input_v3.leave events. The focused | |
| 68 | + | surface must commit zwp_text_input_v3.enable and | |
| 69 | + | zwp_text_input_v3.disable requests as the keyboard focus moves across | |
| 70 | + | editable and non-editable elements of the UI. Those two requests are not | |
| 71 | + | expected to be paired with each other, the compositor must be able to | |
| 72 | + | handle consecutive series of the same request. | |
| 73 | + | ||
| 74 | + | State is sent by the state requests (set_surrounding_text, | |
| 75 | + | set_content_type and set_cursor_rectangle) and a commit request. After an | |
| 76 | + | enter event or disable request all state information is invalidated and | |
| 77 | + | needs to be resent by the client. | |
| 78 | + | </description> | |
| 79 | + | ||
| 80 | + | <request name="destroy" type="destructor"> | |
| 81 | + | <description summary="Destroy the wp_text_input"> | |
| 82 | + | Destroy the wp_text_input object. Also disables all surfaces enabled | |
| 83 | + | through this wp_text_input object. | |
| 84 | + | </description> | |
| 85 | + | </request> | |
| 86 | + | ||
| 87 | + | <request name="enable"> | |
| 88 | + | <description summary="Request text input to be enabled"> | |
| 89 | + | Requests text input on the surface previously obtained from the enter | |
| 90 | + | event. | |
| 91 | + | ||
| 92 | + | This request must be issued every time the focused text input changes | |
| 93 | + | to a new one, including within the current surface. Use | |
| 94 | + | zwp_text_input_v3.disable when there is no longer any input focus on | |
| 95 | + | the current surface. | |
| 96 | + | ||
| 97 | + | Clients must not enable more than one text input on the single seat | |
| 98 | + | and should disable the current text input before enabling the new one. | |
| 99 | + | Requests to enable a text input when another text input is enabled | |
| 100 | + | on the same seat must be ignored by compositor. | |
| 101 | + | ||
| 102 | + | This request resets all state associated with previous enable, disable, | |
| 103 | + | set_surrounding_text, set_text_change_cause, set_content_type, and | |
| 104 | + | set_cursor_rectangle requests, as well as the state associated with | |
| 105 | + | preedit_string, commit_string, and delete_surrounding_text events. | |
| 106 | + | ||
| 107 | + | The set_surrounding_text, set_content_type and set_cursor_rectangle | |
| 108 | + | requests must follow if the text input supports the necessary | |
| 109 | + | functionality. | |
| 110 | + | ||
| 111 | + | State set with this request is double-buffered. It will get applied on | |
| 112 | + | the next zwp_text_input_v3.commit request, and stay valid until the | |
| 113 | + | next committed enable or disable request. | |
| 114 | + | ||
| 115 | + | The changes must be applied by the compositor after issuing a | |
| 116 | + | zwp_text_input_v3.commit request. | |
| 117 | + | </description> | |
| 118 | + | </request> | |
| 119 | + | ||
| 120 | + | <request name="disable"> | |
| 121 | + | <description summary="Disable text input on a surface"> | |
| 122 | + | Explicitly disable text input on the current surface (typically when | |
| 123 | + | there is no focus on any text entry inside the surface). | |
| 124 | + | ||
| 125 | + | State set with this request is double-buffered. It will get applied on | |
| 126 | + | the next zwp_text_input_v3.commit request. | |
| 127 | + | </description> | |
| 128 | + | </request> | |
| 129 | + | ||
| 130 | + | <request name="set_surrounding_text"> | |
| 131 | + | <description summary="sets the surrounding text"> | |
| 132 | + | Sets the surrounding plain text around the input, excluding the preedit | |
| 133 | + | text. | |
| 134 | + | ||
| 135 | + | The client should notify the compositor of any changes in any of the | |
| 136 | + | values carried with this request, including changes caused by handling | |
| 137 | + | incoming text-input events as well as changes caused by other | |
| 138 | + | mechanisms like keyboard typing. | |
| 139 | + | ||
| 140 | + | If the client is unaware of the text around the cursor, it should not | |
| 141 | + | issue this request, to signify lack of support to the compositor. | |
| 142 | + | ||
| 143 | + | Text is UTF-8 encoded, and should include the cursor position, the | |
| 144 | + | complete selection and additional characters before and after them. | |
| 145 | + | There is a maximum length of wayland messages, so text can not be | |
| 146 | + | longer than 4000 bytes. | |
| 147 | + | ||
| 148 | + | Cursor is the byte offset of the cursor within text buffer. | |
| 149 | + | ||
| 150 | + | Anchor is the byte offset of the selection anchor within text buffer. | |
| 151 | + | If there is no selected text, anchor is the same as cursor. | |
| 152 | + | ||
| 153 | + | If any preedit text is present, it is replaced with a cursor for the | |
| 154 | + | purpose of this event. | |
| 155 | + | ||
| 156 | + | Values set with this request are double-buffered. They will get applied | |
| 157 | + | on the next zwp_text_input_v3.commit request, and stay valid until the | |
| 158 | + | next committed enable or disable request. | |
| 159 | + | ||
| 160 | + | The initial state for affected fields is empty, meaning that the text | |
| 161 | + | input does not support sending surrounding text. If the empty values | |
| 162 | + | get applied, subsequent attempts to change them may have no effect. | |
| 163 | + | </description> | |
| 164 | + | <arg name="text" type="string"/> | |
| 165 | + | <arg name="cursor" type="int"/> | |
| 166 | + | <arg name="anchor" type="int"/> | |
| 167 | + | </request> | |
| 168 | + | ||
| 169 | + | <enum name="change_cause"> | |
| 170 | + | <description summary="text change reason"> | |
| 171 | + | Reason for the change of surrounding text or cursor posision. | |
| 172 | + | </description> | |
| 173 | + | <entry name="input_method" value="0" summary="input method caused the change"/> | |
| 174 | + | <entry name="other" value="1" summary="something else than the input method caused the change"/> | |
| 175 | + | </enum> | |
| 176 | + | ||
| 177 | + | <request name="set_text_change_cause"> | |
| 178 | + | <description summary="indicates the cause of surrounding text change"> | |
| 179 | + | Tells the compositor why the text surrounding the cursor changed. | |
| 180 | + | ||
| 181 | + | Whenever the client detects an external change in text, cursor, or | |
| 182 | + | anchor posision, it must issue this request to the compositor. This | |
| 183 | + | request is intended to give the input method a chance to update the | |
| 184 | + | preedit text in an appropriate way, e.g. by removing it when the user | |
| 185 | + | starts typing with a keyboard. | |
| 186 | + | ||
| 187 | + | cause describes the source of the change. | |
| 188 | + | ||
| 189 | + | The value set with this request is double-buffered. It must be applied | |
| 190 | + | and reset to initial at the next zwp_text_input_v3.commit request. | |
| 191 | + | ||
| 192 | + | The initial value of cause is input_method. | |
| 193 | + | </description> | |
| 194 | + | <arg name="cause" type="uint" enum="change_cause"/> | |
| 195 | + | </request> | |
| 196 | + | ||
| 197 | + | <enum name="content_hint" bitfield="true"> | |
| 198 | + | <description summary="content hint"> | |
| 199 | + | Content hint is a bitmask to allow to modify the behavior of the text | |
| 200 | + | input. | |
| 201 | + | </description> | |
| 202 | + | <entry name="none" value="0x0" summary="no special behavior"/> | |
| 203 | + | <entry name="completion" value="0x1" summary="suggest word completions"/> | |
| 204 | + | <entry name="spellcheck" value="0x2" summary="suggest word corrections"/> | |
| 205 | + | <entry name="auto_capitalization" value="0x4" summary="switch to uppercase letters at the start of a sentence"/> | |
| 206 | + | <entry name="lowercase" value="0x8" summary="prefer lowercase letters"/> | |
| 207 | + | <entry name="uppercase" value="0x10" summary="prefer uppercase letters"/> | |
| 208 | + | <entry name="titlecase" value="0x20" summary="prefer casing for titles and headings (can be language dependent)"/> | |
| 209 | + | <entry name="hidden_text" value="0x40" summary="characters should be hidden"/> | |
| 210 | + | <entry name="sensitive_data" value="0x80" summary="typed text should not be stored"/> | |
| 211 | + | <entry name="latin" value="0x100" summary="just Latin characters should be entered"/> | |
| 212 | + | <entry name="multiline" value="0x200" summary="the text input is multiline"/> | |
| 213 | + | <entry | |
| 214 | + | name="on_screen_input_provided" value="0x400" since="2" | |
| 215 | + | summary="an on-screen way to fill in the input is already provided by the client"/> | |
| 216 | + | <entry | |
| 217 | + | name="no_emoji" value="0x800" since="2" | |
| 218 | + | summary="prefer not offering emoji support"/> | |
| 219 | + | <entry | |
| 220 | + | name="preedit_shown" value="0x1000" since="2" | |
| 221 | + | summary="the text input will display preedit text in place"/> | |
| 222 | + | </enum> | |
| 223 | + | ||
| 224 | + | <enum name="content_purpose"> | |
| 225 | + | <description summary="content purpose"> | |
| 226 | + | The content purpose allows to specify the primary purpose of a text | |
| 227 | + | input. | |
| 228 | + | ||
| 229 | + | This allows an input method to show special purpose input panels with | |
| 230 | + | extra characters or to disallow some characters. | |
| 231 | + | </description> | |
| 232 | + | <entry name="normal" value="0" summary="default input, allowing all characters"/> | |
| 233 | + | <entry name="alpha" value="1" summary="allow only alphabetic characters"/> | |
| 234 | + | <entry name="digits" value="2" summary="allow only digits"/> | |
| 235 | + | <entry name="number" value="3" summary="input a number (including decimal separator and sign)"/> | |
| 236 | + | <entry name="phone" value="4" summary="input a phone number"/> | |
| 237 | + | <entry name="url" value="5" summary="input an URL"/> | |
| 238 | + | <entry name="email" value="6" summary="input an email address"/> | |
| 239 | + | <entry name="name" value="7" summary="input a name of a person"/> | |
| 240 | + | <entry name="password" value="8" summary="input a password (combine with sensitive_data hint)"/> | |
| 241 | + | <entry name="pin" value="9" summary="input is a numeric password (combine with sensitive_data hint)"/> | |
| 242 | + | <entry name="date" value="10" summary="input a date"/> | |
| 243 | + | <entry name="time" value="11" summary="input a time"/> | |
| 244 | + | <entry name="datetime" value="12" summary="input a date and time"/> | |
| 245 | + | <entry name="terminal" value="13" summary="input for a terminal"/> | |
| 246 | + | </enum> | |
| 247 | + | ||
| 248 | + | <request name="set_content_type"> | |
| 249 | + | <description summary="set content purpose and hint"> | |
| 250 | + | Sets the content purpose and content hint. While the purpose is the | |
| 251 | + | basic purpose of an input field, the hint flags allow to modify some of | |
| 252 | + | the behavior. | |
| 253 | + | ||
| 254 | + | Values set with this request are double-buffered. They will get applied | |
| 255 | + | on the next zwp_text_input_v3.commit request. | |
| 256 | + | Subsequent attempts to update them may have no effect. The values | |
| 257 | + | remain valid until the next committed enable or disable request. | |
| 258 | + | ||
| 259 | + | The initial value for hint is none, and the initial value for purpose | |
| 260 | + | is normal. | |
| 261 | + | </description> | |
| 262 | + | <arg name="hint" type="uint" enum="content_hint"/> | |
| 263 | + | <arg name="purpose" type="uint" enum="content_purpose"/> | |
| 264 | + | </request> | |
| 265 | + | ||
| 266 | + | <request name="set_cursor_rectangle"> | |
| 267 | + | <description summary="set cursor position"> | |
| 268 | + | Marks an area around the cursor as a x, y, width, height rectangle in | |
| 269 | + | surface local coordinates. | |
| 270 | + | ||
| 271 | + | Allows the compositor to put a window with word suggestions near the | |
| 272 | + | cursor, without obstructing the text being input. | |
| 273 | + | ||
| 274 | + | If the client is unaware of the position of edited text, it should not | |
| 275 | + | issue this request, to signify lack of support to the compositor. | |
| 276 | + | ||
| 277 | + | Values set with this request are double-buffered. They will get applied | |
| 278 | + | on the next zwp_text_input_v3.commit request, and stay valid until the | |
| 279 | + | next committed enable or disable request. | |
| 280 | + | ||
| 281 | + | The initial values describing a cursor rectangle are empty. That means | |
| 282 | + | the text input does not support describing the cursor area. If the | |
| 283 | + | empty values get applied, subsequent attempts to change them may have | |
| 284 | + | no effect. | |
| 285 | + | ||
| 286 | + | As of version 2, the zwp_text_input_v3.commit request does not apply | |
| 287 | + | values sent with this request. Instead, it stores them in a separate | |
| 288 | + | "committed" area. The committed values, if still valid, get applied on | |
| 289 | + | the next wl_surface.commit request on the surface with text-input focus. | |
| 290 | + | Both committed and applied values get invalidated on: | |
| 291 | + | ||
| 292 | + | - the next committed enable or disable request, or | |
| 293 | + | - a change of the focused surface of the text-input (leave or enter events). | |
| 294 | + | ||
| 295 | + | This double stage application allows the compositor to position | |
| 296 | + | the input method popup in the same frame as the contents | |
| 297 | + | of the text on the surface are updated. | |
| 298 | + | </description> | |
| 299 | + | <arg name="x" type="int"/> | |
| 300 | + | <arg name="y" type="int"/> | |
| 301 | + | <arg name="width" type="int"/> | |
| 302 | + | <arg name="height" type="int"/> | |
| 303 | + | </request> | |
| 304 | + | ||
| 305 | + | <request name="commit"> | |
| 306 | + | <description summary="commit state"> | |
| 307 | + | Atomically applies state changes recently sent to the compositor. | |
| 308 | + | ||
| 309 | + | The commit request establishes and updates the state of the client, and | |
| 310 | + | must be issued after any changes to apply them. | |
| 311 | + | ||
| 312 | + | Text input state (enabled status, content purpose, content hint, | |
| 313 | + | surrounding text and change cause, cursor rectangle) is conceptually | |
| 314 | + | double-buffered within the context of a text input, i.e. between a | |
| 315 | + | committed enable request and the following committed enable or disable | |
| 316 | + | request. | |
| 317 | + | ||
| 318 | + | Protocol requests modify the pending state, as opposed to the current | |
| 319 | + | state in use by the input method. A commit request atomically applies | |
| 320 | + | all pending state, replacing the current state. After commit, the new | |
| 321 | + | pending state is as documented for each related request. | |
| 322 | + | ||
| 323 | + | Requests are applied in the order of arrival. | |
| 324 | + | ||
| 325 | + | Neither current nor pending state are modified unless noted otherwise. | |
| 326 | + | ||
| 327 | + | The compositor must count the number of commit requests coming from | |
| 328 | + | each zwp_text_input_v3 object and use the count as the serial in done | |
| 329 | + | events. | |
| 330 | + | </description> | |
| 331 | + | </request> | |
| 332 | + | ||
| 333 | + | <event name="enter"> | |
| 334 | + | <description summary="enter event"> | |
| 335 | + | Notification that this seat's text-input focus is on a certain surface. | |
| 336 | + | ||
| 337 | + | If client has created multiple text input objects, compositor must send | |
| 338 | + | this event to all of them. | |
| 339 | + | ||
| 340 | + | When the seat has the keyboard capability the text-input focus follows | |
| 341 | + | the keyboard focus. This event sets the current surface for the | |
| 342 | + | text-input object. | |
| 343 | + | </description> | |
| 344 | + | <arg name="surface" type="object" interface="wl_surface"/> | |
| 345 | + | </event> | |
| 346 | + | ||
| 347 | + | <event name="leave"> | |
| 348 | + | <description summary="leave event"> | |
| 349 | + | Notification that this seat's text-input focus is no longer on a | |
| 350 | + | certain surface. The client should reset any preedit string previously | |
| 351 | + | set. | |
| 352 | + | ||
| 353 | + | The leave notification clears the current surface. It is sent before | |
| 354 | + | the enter notification for the new focus. After leave event, compositor | |
| 355 | + | must ignore requests from any text input instances until next enter | |
| 356 | + | event. | |
| 357 | + | ||
| 358 | + | When the seat has the keyboard capability the text-input focus follows | |
| 359 | + | the keyboard focus. | |
| 360 | + | </description> | |
| 361 | + | <arg name="surface" type="object" interface="wl_surface"/> | |
| 362 | + | </event> | |
| 363 | + | ||
| 364 | + | <event name="preedit_string"> | |
| 365 | + | <description summary="pre-edit"> | |
| 366 | + | Notify when a new composing text (pre-edit) should be set at the | |
| 367 | + | current cursor position. Any previously set composing text must be | |
| 368 | + | removed. Any previously existing selected text must be removed. | |
| 369 | + | ||
| 370 | + | The argument text contains the pre-edit string buffer. | |
| 371 | + | ||
| 372 | + | The parameters cursor_begin and cursor_end are counted in bytes | |
| 373 | + | relative to the beginning of the submitted text buffer. Cursor should | |
| 374 | + | be hidden when both are equal to -1. | |
| 375 | + | ||
| 376 | + | They could be represented by the client as a line if both values are | |
| 377 | + | the same, or as a text highlight otherwise. | |
| 378 | + | ||
| 379 | + | Values set with this event are double-buffered. They must be applied | |
| 380 | + | and reset to initial on the next zwp_text_input_v3.done event. | |
| 381 | + | ||
| 382 | + | The initial value of text is an empty string, and cursor_begin, | |
| 383 | + | cursor_end and cursor_hidden are all 0. | |
| 384 | + | </description> | |
| 385 | + | <arg name="text" type="string" allow-null="true"/> | |
| 386 | + | <arg name="cursor_begin" type="int"/> | |
| 387 | + | <arg name="cursor_end" type="int"/> | |
| 388 | + | </event> | |
| 389 | + | ||
| 390 | + | <event name="commit_string"> | |
| 391 | + | <description summary="text commit"> | |
| 392 | + | Notify when text should be inserted into the editor widget. The text to | |
| 393 | + | commit could be either just a single character after a key press or the | |
| 394 | + | result of some composing (pre-edit). | |
| 395 | + | ||
| 396 | + | Values set with this event are double-buffered. They must be applied | |
| 397 | + | and reset to initial on the next zwp_text_input_v3.done event. | |
| 398 | + | ||
| 399 | + | The initial value of text is an empty string. | |
| 400 | + | </description> | |
| 401 | + | <arg name="text" type="string" allow-null="true"/> | |
| 402 | + | </event> | |
| 403 | + | ||
| 404 | + | <event name="delete_surrounding_text"> | |
| 405 | + | <description summary="delete surrounding text"> | |
| 406 | + | Notify when the text around the current cursor position should be | |
| 407 | + | deleted. | |
| 408 | + | ||
| 409 | + | Before_length and after_length are the number of bytes before and after | |
| 410 | + | the current cursor index (excluding the selection) to delete. | |
| 411 | + | ||
| 412 | + | If a preedit text is present, in effect before_length is counted from | |
| 413 | + | the beginning of it, and after_length from its end (see done event | |
| 414 | + | sequence). | |
| 415 | + | ||
| 416 | + | Values set with this event are double-buffered. They must be applied | |
| 417 | + | and reset to initial on the next zwp_text_input_v3.done event. | |
| 418 | + | ||
| 419 | + | The initial values of both before_length and after_length are 0. | |
| 420 | + | </description> | |
| 421 | + | <arg name="before_length" type="uint" summary="length of text before current cursor position"/> | |
| 422 | + | <arg name="after_length" type="uint" summary="length of text after current cursor position"/> | |
| 423 | + | </event> | |
| 424 | + | ||
| 425 | + | <event name="done"> | |
| 426 | + | <description summary="apply changes"> | |
| 427 | + | Instruct the application to apply changes to state requested by the | |
| 428 | + | preedit_string, commit_string delete_surrounding_text, and action | |
| 429 | + | events. | |
| 430 | + | ||
| 431 | + | The state relating to these events is double-buffered, and each one | |
| 432 | + | modifies the pending state. This event replaces the current state with | |
| 433 | + | the pending state. | |
| 434 | + | ||
| 435 | + | The application must proceed by evaluating the changes in the following | |
| 436 | + | order: | |
| 437 | + | ||
| 438 | + | 1. Replace existing preedit string with the cursor. | |
| 439 | + | 2. Delete requested surrounding text. | |
| 440 | + | 3. Insert commit string with the cursor at its end. | |
| 441 | + | 4. Calculate surrounding text to send. | |
| 442 | + | 5. Insert new preedit text in cursor position. | |
| 443 | + | 6. Place cursor inside preedit text. | |
| 444 | + | 7. Perform the requested action. | |
| 445 | + | ||
| 446 | + | The serial number reflects the last state of the zwp_text_input_v3 | |
| 447 | + | object known to the compositor. The value of the serial argument must | |
| 448 | + | be equal to the number of commit requests already issued on that object. | |
| 449 | + | ||
| 450 | + | When the client receives a done event with a serial different than the | |
| 451 | + | number of past commit requests, it must proceed with evaluating and | |
| 452 | + | applying the changes as normal, except it should not change the current | |
| 453 | + | state of the zwp_text_input_v3 object. All pending state requests | |
| 454 | + | (set_surrounding_text, set_content_type and set_cursor_rectangle) on | |
| 455 | + | the zwp_text_input_v3 object should be sent and committed after | |
| 456 | + | receiving a zwp_text_input_v3.done event with a matching serial. | |
| 457 | + | </description> | |
| 458 | + | <arg name="serial" type="uint"/> | |
| 459 | + | </event> | |
| 460 | + | ||
| 461 | + | <!-- Version 2 additions --> | |
| 462 | + | <enum name="error" since="2"> | |
| 463 | + | <entry name="invalid_action" value="0" summary="an invalid or duplicate action was specified"/> | |
| 464 | + | </enum> | |
| 465 | + | ||
| 466 | + | <enum name="action" since="2"> | |
| 467 | + | <description summary="action"> | |
| 468 | + | A possible action to perform on a text input. | |
| 469 | + | ||
| 470 | + | The submit action is intended for input entries that expect some sort of | |
| 471 | + | activation after user interaction, e.g. the URL entry in a browser. | |
| 472 | + | </description> | |
| 473 | + | <entry name="none" value="0" summary="no action"/> | |
| 474 | + | <entry name="submit" value="1" summary="the action is submitted"/> | |
| 475 | + | </enum> | |
| 476 | + | ||
| 477 | + | <event name="action" since="2"> | |
| 478 | + | <description summary="action performed"> | |
| 479 | + | An action was performed on this text input. | |
| 480 | + | ||
| 481 | + | Values set with this event are double-buffered. They must be applied | |
| 482 | + | and reset to initial on the next zwp_text_input_v3.done event. | |
| 483 | + | ||
| 484 | + | The initial value of action is none. | |
| 485 | + | </description> | |
| 486 | + | <arg name="action" type="uint" enum="action" summary="action performed"/> | |
| 487 | + | <arg name="serial" type="uint" summary="serial number of the action event"/> | |
| 488 | + | </event> | |
| 489 | + | ||
| 490 | + | <event name="language" since="2"> | |
| 491 | + | <description summary="notify of language selection"> | |
| 492 | + | Notify the application of language used by the input method. | |
| 493 | + | ||
| 494 | + | This event will be sent on creation if known and for all subsequent changes. | |
| 495 | + | ||
| 496 | + | The language should be specified as an IETF BCP 47 tag. | |
| 497 | + | Setting an empty string will reset any known language back to the default unknown state. | |
| 498 | + | </description> | |
| 499 | + | <arg name="language" type="string" summary="new language set by IME"/> | |
| 500 | + | </event> | |
| 501 | + | ||
| 502 | + | <request name="set_available_actions" since="2"> | |
| 503 | + | <description summary="set the available actions"> | |
| 504 | + | Set the actions available for this text input. | |
| 505 | + | ||
| 506 | + | Values set with this request are double-buffered. They will get applied | |
| 507 | + | on the next zwp_text_input_v3.commit request. | |
| 508 | + | ||
| 509 | + | If the available_actions array contains the none action, or contains the | |
| 510 | + | same action multiple times, the compositor must raise the invalid_action | |
| 511 | + | protocol error. | |
| 512 | + | ||
| 513 | + | Initially, no actions are available. | |
| 514 | + | </description> | |
| 515 | + | <arg name="available_actions" type="array" summary="available actions"/> | |
| 516 | + | </request> | |
| 517 | + | ||
| 518 | + | <request name="show_input_panel" since="2"> | |
| 519 | + | <description summary="show input panel"> | |
| 520 | + | Requests an input panel to be shown (e.g. a on-screen keyboard). | |
| 521 | + | ||
| 522 | + | This request only hints the desired interaction pattern from the | |
| 523 | + | client side, and its effect may be ignored by compositors given | |
| 524 | + | other environmental factors. Repeated calls will be ignored. | |
| 525 | + | </description> | |
| 526 | + | </request> | |
| 527 | + | ||
| 528 | + | <request name="hide_input_panel" since="2"> | |
| 529 | + | <description summary="hide input panel"> | |
| 530 | + | Requests an input panel to be hidden. | |
| 531 | + | ||
| 532 | + | This request only hints the desired interaction pattern from the | |
| 533 | + | client side, and its effect may be ignored by compositors given | |
| 534 | + | other environmental factors. Repeated calls will be ignored. | |
| 535 | + | </description> | |
| 536 | + | </request> | |
| 537 | + | ||
| 538 | + | <enum name="preedit_hint"> | |
| 539 | + | <description summary="preedit style hint"> | |
| 540 | + | Style hints for the preedit string. | |
| 541 | + | </description> | |
| 542 | + | <entry name="whole" value="1" summary="simple pre-edit text style, typically underlined"/> | |
| 543 | + | <entry name="selection" value="2" | |
| 544 | + | summary="hint for a selected piece of text, e.g. per-character navigation and composition"/> | |
| 545 | + | <entry name="prediction" value="3" summary="predicted text, not typed by the user"/> | |
| 546 | + | <entry name="prefix" value="4" | |
| 547 | + | summary="prefixed text not being currently edited, e.g. prior to a 'selection' section"/> | |
| 548 | + | <entry name="suffix" value="5" | |
| 549 | + | summary="suffixed text not being currently edited, e.g. after a 'selection' section"/> | |
| 550 | + | <entry name="spelling_error" value="6" summary="spelling error"/> | |
| 551 | + | <entry name="compose_error" value="7" | |
| 552 | + | summary="wrong composition, e.g. user input that can not be transliterated"/> | |
| 553 | + | </enum> | |
| 554 | + | ||
| 555 | + | <event name="preedit_hint" since="2"> | |
| 556 | + | <description summary="pre-edit"> | |
| 557 | + | Notify of contextual hints for the pre-edit string. This | |
| 558 | + | event is always sent together with a zwp_text_input_v3.preedit_string | |
| 559 | + | event. | |
| 560 | + | ||
| 561 | + | The parameters start and end are counted in bytes relative to the | |
| 562 | + | beginning of the text buffer submitted through | |
| 563 | + | zwp_text_input_v3.preedit_string, and represent the substring in the | |
| 564 | + | pre-edit text affected by the hint. | |
| 565 | + | ||
| 566 | + | Multiple events may be submitted if the preedit string has different | |
| 567 | + | sections. The extent of hints may overlap. The parts of the preedit | |
| 568 | + | string that are not covered by any zwp_text_input_v3.preedit_hint event, | |
| 569 | + | the text will be considered unhinted. This is also the case if no | |
| 570 | + | preedit_hint event is sent. | |
| 571 | + | ||
| 572 | + | Clients should provide recognizable visuals to these hints. if they are | |
| 573 | + | unable to comply with this requisition, it may be preferable for them | |
| 574 | + | keep the preedit_shown content hint disabled. | |
| 575 | + | ||
| 576 | + | Values set with this event are double-buffered. They must be applied | |
| 577 | + | and reset on the next zwp_text_input_v3.done event. | |
| 578 | + | </description> | |
| 579 | + | <arg name="start" type="uint" summary="starting point of the affected substring"/> | |
| 580 | + | <arg name="end" type="uint" summary="end point of the affected substring"/> | |
| 581 | + | <arg name="hint" type="uint" enum="preedit_hint" summary="hint to apply"/> | |
| 582 | + | </event> | |
| 583 | + | </interface> | |
| 584 | + | ||
| 585 | + | <interface name="zwp_text_input_manager_v3" version="2"> | |
| 586 | + | <description summary="text input manager"> | |
| 587 | + | A factory for text-input objects. This object is a global singleton. | |
| 588 | + | </description> | |
| 589 | + | ||
| 590 | + | <request name="destroy" type="destructor"> | |
| 591 | + | <description summary="Destroy the wp_text_input_manager"> | |
| 592 | + | Destroy the wp_text_input_manager object. | |
| 593 | + | </description> | |
| 594 | + | </request> | |
| 595 | + | ||
| 596 | + | <request name="get_text_input"> | |
| 597 | + | <description summary="create a new text input object"> | |
| 598 | + | Creates a new text-input object for a given seat. | |
| 599 | + | </description> | |
| 600 | + | <arg name="id" type="new_id" interface="zwp_text_input_v3"/> | |
| 601 | + | <arg name="seat" type="object" interface="wl_seat"/> | |
| 602 | + | </request> | |
| 603 | + | </interface> | |
| 604 | + | </protocol> | |
A protocol/textinput/text-input-unstable-v3_generated.go +643 -0
| @@ -0,0 +1,643 @@ | |||
| 1 | + | // Code generated by wlturbo-scanner. DO NOT EDIT. | |
| 2 | + | // Protocol: text_input_unstable_v3 | |
| 3 | + | // Source: text-input-unstable-v3.xml | |
| 4 | + | ||
| 5 | + | package textinput | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "sync" | |
| 9 | + | ||
| 10 | + | cross_wl_seat "github.com/bnema/wlturbo/protocol/core" | |
| 11 | + | "github.com/bnema/wlturbo/wl" | |
| 12 | + | ) | |
| 13 | + | ||
| 14 | + | // CHANGE_CAUSE_INPUT_METHOD is the change_cause input_method value. | |
| 15 | + | const CHANGE_CAUSE_INPUT_METHOD = 0 | |
| 16 | + | ||
| 17 | + | // CHANGE_CAUSE_OTHER is the change_cause other value. | |
| 18 | + | const CHANGE_CAUSE_OTHER = 1 | |
| 19 | + | ||
| 20 | + | // CONTENT_HINT_NONE is the content_hint none value. | |
| 21 | + | const CONTENT_HINT_NONE = 0x0 | |
| 22 | + | ||
| 23 | + | // CONTENT_HINT_COMPLETION is the content_hint completion value. | |
| 24 | + | const CONTENT_HINT_COMPLETION = 0x1 | |
| 25 | + | ||
| 26 | + | // CONTENT_HINT_SPELLCHECK is the content_hint spellcheck value. | |
| 27 | + | const CONTENT_HINT_SPELLCHECK = 0x2 | |
| 28 | + | ||
| 29 | + | // CONTENT_HINT_AUTO_CAPITALIZATION is the content_hint auto_capitalization value. | |
| 30 | + | const CONTENT_HINT_AUTO_CAPITALIZATION = 0x4 | |
| 31 | + | ||
| 32 | + | // CONTENT_HINT_LOWERCASE is the content_hint lowercase value. | |
| 33 | + | const CONTENT_HINT_LOWERCASE = 0x8 | |
| 34 | + | ||
| 35 | + | // CONTENT_HINT_UPPERCASE is the content_hint uppercase value. | |
| 36 | + | const CONTENT_HINT_UPPERCASE = 0x10 | |
| 37 | + | ||
| 38 | + | // CONTENT_HINT_TITLECASE is the content_hint titlecase value. | |
| 39 | + | const CONTENT_HINT_TITLECASE = 0x20 | |
| 40 | + | ||
| 41 | + | // CONTENT_HINT_HIDDEN_TEXT is the content_hint hidden_text value. | |
| 42 | + | const CONTENT_HINT_HIDDEN_TEXT = 0x40 | |
| 43 | + | ||
| 44 | + | // CONTENT_HINT_SENSITIVE_DATA is the content_hint sensitive_data value. | |
| 45 | + | const CONTENT_HINT_SENSITIVE_DATA = 0x80 | |
| 46 | + | ||
| 47 | + | // CONTENT_HINT_LATIN is the content_hint latin value. | |
| 48 | + | const CONTENT_HINT_LATIN = 0x100 | |
| 49 | + | ||
| 50 | + | // CONTENT_HINT_MULTILINE is the content_hint multiline value. | |
| 51 | + | const CONTENT_HINT_MULTILINE = 0x200 | |
| 52 | + | ||
| 53 | + | // CONTENT_HINT_ON_SCREEN_INPUT_PROVIDED is the content_hint on_screen_input_provided value. | |
| 54 | + | const CONTENT_HINT_ON_SCREEN_INPUT_PROVIDED = 0x400 | |
| 55 | + | ||
| 56 | + | // CONTENT_HINT_NO_EMOJI is the content_hint no_emoji value. | |
| 57 | + | const CONTENT_HINT_NO_EMOJI = 0x800 | |
| 58 | + | ||
| 59 | + | // CONTENT_HINT_PREEDIT_SHOWN is the content_hint preedit_shown value. | |
| 60 | + | const CONTENT_HINT_PREEDIT_SHOWN = 0x1000 | |
| 61 | + | ||
| 62 | + | // CONTENT_PURPOSE_NORMAL is the content_purpose normal value. | |
| 63 | + | const CONTENT_PURPOSE_NORMAL = 0 | |
| 64 | + | ||
| 65 | + | // CONTENT_PURPOSE_ALPHA is the content_purpose alpha value. | |
| 66 | + | const CONTENT_PURPOSE_ALPHA = 1 | |
| 67 | + | ||
| 68 | + | // CONTENT_PURPOSE_DIGITS is the content_purpose digits value. | |
| 69 | + | const CONTENT_PURPOSE_DIGITS = 2 | |
| 70 | + | ||
| 71 | + | // CONTENT_PURPOSE_NUMBER is the content_purpose number value. | |
| 72 | + | const CONTENT_PURPOSE_NUMBER = 3 | |
| 73 | + | ||
| 74 | + | // CONTENT_PURPOSE_PHONE is the content_purpose phone value. | |
| 75 | + | const CONTENT_PURPOSE_PHONE = 4 | |
| 76 | + | ||
| 77 | + | // CONTENT_PURPOSE_URL is the content_purpose url value. | |
| 78 | + | const CONTENT_PURPOSE_URL = 5 | |
| 79 | + | ||
| 80 | + | // CONTENT_PURPOSE_EMAIL is the content_purpose email value. | |
| 81 | + | const CONTENT_PURPOSE_EMAIL = 6 | |
| 82 | + | ||
| 83 | + | // CONTENT_PURPOSE_NAME is the content_purpose name value. | |
| 84 | + | const CONTENT_PURPOSE_NAME = 7 | |
| 85 | + | ||
| 86 | + | // CONTENT_PURPOSE_PASSWORD is the content_purpose password value. | |
| 87 | + | const CONTENT_PURPOSE_PASSWORD = 8 | |
| 88 | + | ||
| 89 | + | // CONTENT_PURPOSE_PIN is the content_purpose pin value. | |
| 90 | + | const CONTENT_PURPOSE_PIN = 9 | |
| 91 | + | ||
| 92 | + | // CONTENT_PURPOSE_DATE is the content_purpose date value. | |
| 93 | + | const CONTENT_PURPOSE_DATE = 10 | |
| 94 | + | ||
| 95 | + | // CONTENT_PURPOSE_TIME is the content_purpose time value. | |
| 96 | + | const CONTENT_PURPOSE_TIME = 11 | |
| 97 | + | ||
| 98 | + | // CONTENT_PURPOSE_DATETIME is the content_purpose datetime value. | |
| 99 | + | const CONTENT_PURPOSE_DATETIME = 12 | |
| 100 | + | ||
| 101 | + | // CONTENT_PURPOSE_TERMINAL is the content_purpose terminal value. | |
| 102 | + | const CONTENT_PURPOSE_TERMINAL = 13 | |
| 103 | + | ||
| 104 | + | // ERROR_INVALID_ACTION is the error invalid_action value. | |
| 105 | + | const ERROR_INVALID_ACTION = 0 | |
| 106 | + | ||
| 107 | + | // ACTION_NONE is the action none value. | |
| 108 | + | const ACTION_NONE = 0 | |
| 109 | + | ||
| 110 | + | // ACTION_SUBMIT is the action submit value. | |
| 111 | + | const ACTION_SUBMIT = 1 | |
| 112 | + | ||
| 113 | + | // PREEDIT_HINT_WHOLE is the preedit_hint whole value. | |
| 114 | + | const PREEDIT_HINT_WHOLE = 1 | |
| 115 | + | ||
| 116 | + | // PREEDIT_HINT_SELECTION is the preedit_hint selection value. | |
| 117 | + | const PREEDIT_HINT_SELECTION = 2 | |
| 118 | + | ||
| 119 | + | // PREEDIT_HINT_PREDICTION is the preedit_hint prediction value. | |
| 120 | + | const PREEDIT_HINT_PREDICTION = 3 | |
| 121 | + | ||
| 122 | + | // PREEDIT_HINT_PREFIX is the preedit_hint prefix value. | |
| 123 | + | const PREEDIT_HINT_PREFIX = 4 | |
| 124 | + | ||
| 125 | + | // PREEDIT_HINT_SUFFIX is the preedit_hint suffix value. | |
| 126 | + | const PREEDIT_HINT_SUFFIX = 5 | |
| 127 | + | ||
| 128 | + | // PREEDIT_HINT_SPELLING_ERROR is the preedit_hint spelling_error value. | |
| 129 | + | const PREEDIT_HINT_SPELLING_ERROR = 6 | |
| 130 | + | ||
| 131 | + | // PREEDIT_HINT_COMPOSE_ERROR is the preedit_hint compose_error value. | |
| 132 | + | const PREEDIT_HINT_COMPOSE_ERROR = 7 | |
| 133 | + | ||
| 134 | + | // TextInputV3Interface is the protocol interface name. | |
| 135 | + | const TextInputV3Interface = "zwp_text_input_v3" | |
| 136 | + | ||
| 137 | + | // TextInputV3 is the generated binding for zwp_text_input_v3 version 2. | |
| 138 | + | type TextInputV3 struct { | |
| 139 | + | wl.BaseProxy | |
| 140 | + | // handlersMu guards the handler slices: a client may register or replace | |
| 141 | + | // handlers while another goroutine dispatches events. | |
| 142 | + | handlersMu sync.Mutex | |
| 143 | + | onEnter []func(surfaceID uint32) | |
| 144 | + | onLeave []func(surfaceID uint32) | |
| 145 | + | onPreeditString []func(text string, cursorBegin int32, cursorEnd int32) | |
| 146 | + | onCommitString []func(text string) | |
| 147 | + | onDeleteSurroundingText []func(beforeLength uint32, afterLength uint32) | |
| 148 | + | onDone []func(serial uint32) | |
| 149 | + | onAction []func(action uint32, serial uint32) | |
| 150 | + | onLanguage []func(language string) | |
| 151 | + | onPreeditHint []func(start uint32, end uint32, hint uint32) | |
| 152 | + | } | |
| 153 | + | ||
| 154 | + | // NewTextInputV3 returns an unbound TextInputV3. | |
| 155 | + | func NewTextInputV3(ctx *wl.Context) *TextInputV3 { | |
| 156 | + | obj := &TextInputV3{} | |
| 157 | + | obj.SetContext(ctx) | |
| 158 | + | ctx.Register(obj) | |
| 159 | + | return obj | |
| 160 | + | } | |
| 161 | + | ||
| 162 | + | // Destroy the wp_text_input object. Also disables all surfaces enabled through this wp_text_input object. | |
| 163 | + | func (o *TextInputV3) Destroy() error { | |
| 164 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 165 | + | return err | |
| 166 | + | } | |
| 167 | + | if err := o.Context().SendDestructor(o, 0); err != nil { | |
| 168 | + | return err | |
| 169 | + | } | |
| 170 | + | return nil | |
| 171 | + | } | |
| 172 | + | ||
| 173 | + | // Requests text input on the surface previously obtained from the enter event. This request must be issued every time the focused text input changes to a new one, including within the current surface. Use zwp_text_input_v3.disable when there is no longer any input focus on the current surface. Clients must not enable more than one text input on the single seat and should disable the current text input before enabling the new one. Requests to enable a text input when another text input is enabled on the same seat must be ignored by compositor. This request resets all state associated with previous enable, disable, set_surrounding_text, set_text_change_cause, set_content_type, and set_cursor_rectangle requests, as well as the state associated with preedit_string, commit_string, and delete_surrounding_text events. The set_surrounding_text, set_content_type and set_cursor_rectangle requests must follow if the text input supports the necessary functionality. State set with this request is double-buffered. It will get applied on the next zwp_text_input_v3.commit request, and stay valid until the next committed enable or disable request. The changes must be applied by the compositor after issuing a zwp_text_input_v3.commit request. | |
| 174 | + | func (o *TextInputV3) Enable() error { | |
| 175 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 176 | + | return err | |
| 177 | + | } | |
| 178 | + | if err := o.Context().SendRequest(o, 1); err != nil { | |
| 179 | + | return err | |
| 180 | + | } | |
| 181 | + | return nil | |
| 182 | + | } | |
| 183 | + | ||
| 184 | + | // Explicitly disable text input on the current surface (typically when there is no focus on any text entry inside the surface). State set with this request is double-buffered. It will get applied on the next zwp_text_input_v3.commit request. | |
| 185 | + | func (o *TextInputV3) Disable() error { | |
| 186 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 187 | + | return err | |
| 188 | + | } | |
| 189 | + | if err := o.Context().SendRequest(o, 2); err != nil { | |
| 190 | + | return err | |
| 191 | + | } | |
| 192 | + | return nil | |
| 193 | + | } | |
| 194 | + | ||
| 195 | + | // Sets the surrounding plain text around the input, excluding the preedit text. The client should notify the compositor of any changes in any of the values carried with this request, including changes caused by handling incoming text-input events as well as changes caused by other mechanisms like keyboard typing. If the client is unaware of the text around the cursor, it should not issue this request, to signify lack of support to the compositor. Text is UTF-8 encoded, and should include the cursor position, the complete selection and additional characters before and after them. There is a maximum length of wayland messages, so text can not be longer than 4000 bytes. Cursor is the byte offset of the cursor within text buffer. Anchor is the byte offset of the selection anchor within text buffer. If there is no selected text, anchor is the same as cursor. If any preedit text is present, it is replaced with a cursor for the purpose of this event. Values set with this request are double-buffered. They will get applied on the next zwp_text_input_v3.commit request, and stay valid until the next committed enable or disable request. The initial state for affected fields is empty, meaning that the text input does not support sending surrounding text. If the empty values get applied, subsequent attempts to change them may have no effect. | |
| 196 | + | func (o *TextInputV3) SetSurroundingText(text string, cursor int32, anchor int32) error { | |
| 197 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 198 | + | return err | |
| 199 | + | } | |
| 200 | + | if err := o.Context().SendRequest(o, 3, text, cursor, anchor); err != nil { | |
| 201 | + | return err | |
| 202 | + | } | |
| 203 | + | return nil | |
| 204 | + | } | |
| 205 | + | ||
| 206 | + | // Tells the compositor why the text surrounding the cursor changed. Whenever the client detects an external change in text, cursor, or anchor posision, it must issue this request to the compositor. This request is intended to give the input method a chance to update the preedit text in an appropriate way, e.g. by removing it when the user starts typing with a keyboard. cause describes the source of the change. The value set with this request is double-buffered. It must be applied and reset to initial at the next zwp_text_input_v3.commit request. The initial value of cause is input_method. | |
| 207 | + | func (o *TextInputV3) SetTextChangeCause(cause uint32) error { | |
| 208 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 209 | + | return err | |
| 210 | + | } | |
| 211 | + | if err := o.Context().SendRequest(o, 4, cause); err != nil { | |
| 212 | + | return err | |
| 213 | + | } | |
| 214 | + | return nil | |
| 215 | + | } | |
| 216 | + | ||
| 217 | + | // Sets the content purpose and content hint. While the purpose is the basic purpose of an input field, the hint flags allow to modify some of the behavior. Values set with this request are double-buffered. They will get applied on the next zwp_text_input_v3.commit request. Subsequent attempts to update them may have no effect. The values remain valid until the next committed enable or disable request. The initial value for hint is none, and the initial value for purpose is normal. | |
| 218 | + | func (o *TextInputV3) SetContentType(hint uint32, purpose uint32) error { | |
| 219 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 220 | + | return err | |
| 221 | + | } | |
| 222 | + | if err := o.Context().SendRequest(o, 5, hint, purpose); err != nil { | |
| 223 | + | return err | |
| 224 | + | } | |
| 225 | + | return nil | |
| 226 | + | } | |
| 227 | + | ||
| 228 | + | // Marks an area around the cursor as a x, y, width, height rectangle in surface local coordinates. Allows the compositor to put a window with word suggestions near the cursor, without obstructing the text being input. If the client is unaware of the position of edited text, it should not issue this request, to signify lack of support to the compositor. Values set with this request are double-buffered. They will get applied on the next zwp_text_input_v3.commit request, and stay valid until the next committed enable or disable request. The initial values describing a cursor rectangle are empty. That means the text input does not support describing the cursor area. If the empty values get applied, subsequent attempts to change them may have no effect. As of version 2, the zwp_text_input_v3.commit request does not apply values sent with this request. Instead, it stores them in a separate "committed" area. The committed values, if still valid, get applied on the next wl_surface.commit request on the surface with text-input focus. Both committed and applied values get invalidated on: - the next committed enable or disable request, or - a change of the focused surface of the text-input (leave or enter events). This double stage application allows the compositor to position the input method popup in the same frame as the contents of the text on the surface are updated. | |
| 229 | + | func (o *TextInputV3) SetCursorRectangle(x int32, y int32, width int32, height int32) error { | |
| 230 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 231 | + | return err | |
| 232 | + | } | |
| 233 | + | if err := o.Context().SendRequest(o, 6, x, y, width, height); err != nil { | |
| 234 | + | return err | |
| 235 | + | } | |
| 236 | + | return nil | |
| 237 | + | } | |
| 238 | + | ||
| 239 | + | // Atomically applies state changes recently sent to the compositor. The commit request establishes and updates the state of the client, and must be issued after any changes to apply them. Text input state (enabled status, content purpose, content hint, surrounding text and change cause, cursor rectangle) is conceptually double-buffered within the context of a text input, i.e. between a committed enable request and the following committed enable or disable request. Protocol requests modify the pending state, as opposed to the current state in use by the input method. A commit request atomically applies all pending state, replacing the current state. After commit, the new pending state is as documented for each related request. Requests are applied in the order of arrival. Neither current nor pending state are modified unless noted otherwise. The compositor must count the number of commit requests coming from each zwp_text_input_v3 object and use the count as the serial in done events. | |
| 240 | + | func (o *TextInputV3) Commit() error { | |
| 241 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 242 | + | return err | |
| 243 | + | } | |
| 244 | + | if err := o.Context().SendRequest(o, 7); err != nil { | |
| 245 | + | return err | |
| 246 | + | } | |
| 247 | + | return nil | |
| 248 | + | } | |
| 249 | + | ||
| 250 | + | // Set the actions available for this text input. Values set with this request are double-buffered. They will get applied on the next zwp_text_input_v3.commit request. If the available_actions array contains the none action, or contains the same action multiple times, the compositor must raise the invalid_action protocol error. Initially, no actions are available. | |
| 251 | + | func (o *TextInputV3) SetAvailableActions(availableActions []byte) error { | |
| 252 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 253 | + | return err | |
| 254 | + | } | |
| 255 | + | if err := o.Context().SendRequest(o, 8, availableActions); err != nil { | |
| 256 | + | return err | |
| 257 | + | } | |
| 258 | + | return nil | |
| 259 | + | } | |
| 260 | + | ||
| 261 | + | // Requests an input panel to be shown (e.g. a on-screen keyboard). This request only hints the desired interaction pattern from the client side, and its effect may be ignored by compositors given other environmental factors. Repeated calls will be ignored. | |
| 262 | + | func (o *TextInputV3) ShowInputPanel() error { | |
| 263 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 264 | + | return err | |
| 265 | + | } | |
| 266 | + | if err := o.Context().SendRequest(o, 9); err != nil { | |
| 267 | + | return err | |
| 268 | + | } | |
| 269 | + | return nil | |
| 270 | + | } | |
| 271 | + | ||
| 272 | + | // Requests an input panel to be hidden. This request only hints the desired interaction pattern from the client side, and its effect may be ignored by compositors given other environmental factors. Repeated calls will be ignored. | |
| 273 | + | func (o *TextInputV3) HideInputPanel() error { | |
| 274 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 275 | + | return err | |
| 276 | + | } | |
| 277 | + | if err := o.Context().SendRequest(o, 10); err != nil { | |
| 278 | + | return err | |
| 279 | + | } | |
| 280 | + | return nil | |
| 281 | + | } | |
| 282 | + | ||
| 283 | + | // OnEnter registers a handler for the enter event. | |
| 284 | + | // | |
| 285 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 286 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 287 | + | func (o *TextInputV3) OnEnter(handler func(surfaceID uint32)) { | |
| 288 | + | if handler == nil { | |
| 289 | + | return | |
| 290 | + | } | |
| 291 | + | o.handlersMu.Lock() | |
| 292 | + | defer o.handlersMu.Unlock() | |
| 293 | + | o.onEnter = append(o.onEnter, handler) | |
| 294 | + | } | |
| 295 | + | ||
| 296 | + | // handlersForEnter returns the handlers registered for enter, taken | |
| 297 | + | // under the lock so a handler may register another handler while events are | |
| 298 | + | // being dispatched. | |
| 299 | + | func (o *TextInputV3) handlersForEnter() []func(surfaceID uint32) { | |
| 300 | + | o.handlersMu.Lock() | |
| 301 | + | defer o.handlersMu.Unlock() | |
| 302 | + | return o.onEnter | |
| 303 | + | } | |
| 304 | + | ||
| 305 | + | // OnLeave registers a handler for the leave event. | |
| 306 | + | // | |
| 307 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 308 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 309 | + | func (o *TextInputV3) OnLeave(handler func(surfaceID uint32)) { | |
| 310 | + | if handler == nil { | |
| 311 | + | return | |
| 312 | + | } | |
| 313 | + | o.handlersMu.Lock() | |
| 314 | + | defer o.handlersMu.Unlock() | |
| 315 | + | o.onLeave = append(o.onLeave, handler) | |
| 316 | + | } | |
| 317 | + | ||
| 318 | + | // handlersForLeave returns the handlers registered for leave, taken | |
| 319 | + | // under the lock so a handler may register another handler while events are | |
| 320 | + | // being dispatched. | |
| 321 | + | func (o *TextInputV3) handlersForLeave() []func(surfaceID uint32) { | |
| 322 | + | o.handlersMu.Lock() | |
| 323 | + | defer o.handlersMu.Unlock() | |
| 324 | + | return o.onLeave | |
| 325 | + | } | |
| 326 | + | ||
| 327 | + | // OnPreeditString registers a handler for the preedit_string event. | |
| 328 | + | // | |
| 329 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 330 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 331 | + | func (o *TextInputV3) OnPreeditString(handler func(text string, cursorBegin int32, cursorEnd int32)) { | |
| 332 | + | if handler == nil { | |
| 333 | + | return | |
| 334 | + | } | |
| 335 | + | o.handlersMu.Lock() | |
| 336 | + | defer o.handlersMu.Unlock() | |
| 337 | + | o.onPreeditString = append(o.onPreeditString, handler) | |
| 338 | + | } | |
| 339 | + | ||
| 340 | + | // handlersForPreeditString returns the handlers registered for preedit_string, taken | |
| 341 | + | // under the lock so a handler may register another handler while events are | |
| 342 | + | // being dispatched. | |
| 343 | + | func (o *TextInputV3) handlersForPreeditString() []func(text string, cursorBegin int32, cursorEnd int32) { | |
| 344 | + | o.handlersMu.Lock() | |
| 345 | + | defer o.handlersMu.Unlock() | |
| 346 | + | return o.onPreeditString | |
| 347 | + | } | |
| 348 | + | ||
| 349 | + | // OnCommitString registers a handler for the commit_string event. | |
| 350 | + | // | |
| 351 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 352 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 353 | + | func (o *TextInputV3) OnCommitString(handler func(text string)) { | |
| 354 | + | if handler == nil { | |
| 355 | + | return | |
| 356 | + | } | |
| 357 | + | o.handlersMu.Lock() | |
| 358 | + | defer o.handlersMu.Unlock() | |
| 359 | + | o.onCommitString = append(o.onCommitString, handler) | |
| 360 | + | } | |
| 361 | + | ||
| 362 | + | // handlersForCommitString returns the handlers registered for commit_string, taken | |
| 363 | + | // under the lock so a handler may register another handler while events are | |
| 364 | + | // being dispatched. | |
| 365 | + | func (o *TextInputV3) handlersForCommitString() []func(text string) { | |
| 366 | + | o.handlersMu.Lock() | |
| 367 | + | defer o.handlersMu.Unlock() | |
| 368 | + | return o.onCommitString | |
| 369 | + | } | |
| 370 | + | ||
| 371 | + | // OnDeleteSurroundingText registers a handler for the delete_surrounding_text event. | |
| 372 | + | // | |
| 373 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 374 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 375 | + | func (o *TextInputV3) OnDeleteSurroundingText(handler func(beforeLength uint32, afterLength uint32)) { | |
| 376 | + | if handler == nil { | |
| 377 | + | return | |
| 378 | + | } | |
| 379 | + | o.handlersMu.Lock() | |
| 380 | + | defer o.handlersMu.Unlock() | |
| 381 | + | o.onDeleteSurroundingText = append(o.onDeleteSurroundingText, handler) | |
| 382 | + | } | |
| 383 | + | ||
| 384 | + | // handlersForDeleteSurroundingText returns the handlers registered for delete_surrounding_text, taken | |
| 385 | + | // under the lock so a handler may register another handler while events are | |
| 386 | + | // being dispatched. | |
| 387 | + | func (o *TextInputV3) handlersForDeleteSurroundingText() []func(beforeLength uint32, afterLength uint32) { | |
| 388 | + | o.handlersMu.Lock() | |
| 389 | + | defer o.handlersMu.Unlock() | |
| 390 | + | return o.onDeleteSurroundingText | |
| 391 | + | } | |
| 392 | + | ||
| 393 | + | // OnDone registers a handler for the done event. | |
| 394 | + | // | |
| 395 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 396 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 397 | + | func (o *TextInputV3) OnDone(handler func(serial uint32)) { | |
| 398 | + | if handler == nil { | |
| 399 | + | return | |
| 400 | + | } | |
| 401 | + | o.handlersMu.Lock() | |
| 402 | + | defer o.handlersMu.Unlock() | |
| 403 | + | o.onDone = append(o.onDone, handler) | |
| 404 | + | } | |
| 405 | + | ||
| 406 | + | // handlersForDone returns the handlers registered for done, taken | |
| 407 | + | // under the lock so a handler may register another handler while events are | |
| 408 | + | // being dispatched. | |
| 409 | + | func (o *TextInputV3) handlersForDone() []func(serial uint32) { | |
| 410 | + | o.handlersMu.Lock() | |
| 411 | + | defer o.handlersMu.Unlock() | |
| 412 | + | return o.onDone | |
| 413 | + | } | |
| 414 | + | ||
| 415 | + | // OnAction registers a handler for the action event. | |
| 416 | + | // | |
| 417 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 418 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 419 | + | func (o *TextInputV3) OnAction(handler func(action uint32, serial uint32)) { | |
| 420 | + | if handler == nil { | |
| 421 | + | return | |
| 422 | + | } | |
| 423 | + | o.handlersMu.Lock() | |
| 424 | + | defer o.handlersMu.Unlock() | |
| 425 | + | o.onAction = append(o.onAction, handler) | |
| 426 | + | } | |
| 427 | + | ||
| 428 | + | // handlersForAction returns the handlers registered for action, taken | |
| 429 | + | // under the lock so a handler may register another handler while events are | |
| 430 | + | // being dispatched. | |
| 431 | + | func (o *TextInputV3) handlersForAction() []func(action uint32, serial uint32) { | |
| 432 | + | o.handlersMu.Lock() | |
| 433 | + | defer o.handlersMu.Unlock() | |
| 434 | + | return o.onAction | |
| 435 | + | } | |
| 436 | + | ||
| 437 | + | // OnLanguage registers a handler for the language event. | |
| 438 | + | // | |
| 439 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 440 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 441 | + | func (o *TextInputV3) OnLanguage(handler func(language string)) { | |
| 442 | + | if handler == nil { | |
| 443 | + | return | |
| 444 | + | } | |
| 445 | + | o.handlersMu.Lock() | |
| 446 | + | defer o.handlersMu.Unlock() | |
| 447 | + | o.onLanguage = append(o.onLanguage, handler) | |
| 448 | + | } | |
| 449 | + | ||
| 450 | + | // handlersForLanguage returns the handlers registered for language, taken | |
| 451 | + | // under the lock so a handler may register another handler while events are | |
| 452 | + | // being dispatched. | |
| 453 | + | func (o *TextInputV3) handlersForLanguage() []func(language string) { | |
| 454 | + | o.handlersMu.Lock() | |
| 455 | + | defer o.handlersMu.Unlock() | |
| 456 | + | return o.onLanguage | |
| 457 | + | } | |
| 458 | + | ||
| 459 | + | // OnPreeditHint registers a handler for the preedit_hint event. | |
| 460 | + | // | |
| 461 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 462 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 463 | + | func (o *TextInputV3) OnPreeditHint(handler func(start uint32, end uint32, hint uint32)) { | |
| 464 | + | if handler == nil { | |
| 465 | + | return | |
| 466 | + | } | |
| 467 | + | o.handlersMu.Lock() | |
| 468 | + | defer o.handlersMu.Unlock() | |
| 469 | + | o.onPreeditHint = append(o.onPreeditHint, handler) | |
| 470 | + | } | |
| 471 | + | ||
| 472 | + | // handlersForPreeditHint returns the handlers registered for preedit_hint, taken | |
| 473 | + | // under the lock so a handler may register another handler while events are | |
| 474 | + | // being dispatched. | |
| 475 | + | func (o *TextInputV3) handlersForPreeditHint() []func(start uint32, end uint32, hint uint32) { | |
| 476 | + | o.handlersMu.Lock() | |
| 477 | + | defer o.handlersMu.Unlock() | |
| 478 | + | return o.onPreeditHint | |
| 479 | + | } | |
| 480 | + | ||
| 481 | + | // EventSignature returns the declared event argument types in wire order. | |
| 482 | + | func (o *TextInputV3) EventSignature(opcode uint16) (string, bool) { | |
| 483 | + | switch opcode { | |
| 484 | + | case 0: | |
| 485 | + | return "object,", true | |
| 486 | + | case 1: | |
| 487 | + | return "object,", true | |
| 488 | + | case 2: | |
| 489 | + | return "string,int,int,", true | |
| 490 | + | case 3: | |
| 491 | + | return "string,", true | |
| 492 | + | case 4: | |
| 493 | + | return "uint,uint,", true | |
| 494 | + | case 5: | |
| 495 | + | return "uint,", true | |
| 496 | + | case 6: | |
| 497 | + | return "uint,uint,", true | |
| 498 | + | case 7: | |
| 499 | + | return "string,", true | |
| 500 | + | case 8: | |
| 501 | + | return "uint,uint,uint,", true | |
| 502 | + | default: | |
| 503 | + | return "", false | |
| 504 | + | } | |
| 505 | + | } | |
| 506 | + | ||
| 507 | + | // Dispatch decodes one event on zwp_text_input_v3 and calls the registered handlers. | |
| 508 | + | func (o *TextInputV3) Dispatch(event *wl.Event) { | |
| 509 | + | switch event.Opcode { | |
| 510 | + | case 0: // enter | |
| 511 | + | surfaceID := event.Uint32() | |
| 512 | + | ||
| 513 | + | for i, handler := range o.handlersForEnter() { | |
| 514 | + | _ = i | |
| 515 | + | handler(surfaceID) | |
| 516 | + | } | |
| 517 | + | case 1: // leave | |
| 518 | + | surfaceID := event.Uint32() | |
| 519 | + | ||
| 520 | + | for i, handler := range o.handlersForLeave() { | |
| 521 | + | _ = i | |
| 522 | + | handler(surfaceID) | |
| 523 | + | } | |
| 524 | + | case 2: // preedit_string | |
| 525 | + | text := event.String() | |
| 526 | + | cursorBegin := event.Int32() | |
| 527 | + | cursorEnd := event.Int32() | |
| 528 | + | ||
| 529 | + | for i, handler := range o.handlersForPreeditString() { | |
| 530 | + | _ = i | |
| 531 | + | handler(text, cursorBegin, cursorEnd) | |
| 532 | + | } | |
| 533 | + | case 3: // commit_string | |
| 534 | + | text := event.String() | |
| 535 | + | ||
| 536 | + | for i, handler := range o.handlersForCommitString() { | |
| 537 | + | _ = i | |
| 538 | + | handler(text) | |
| 539 | + | } | |
| 540 | + | case 4: // delete_surrounding_text | |
| 541 | + | beforeLength := event.Uint32() | |
| 542 | + | afterLength := event.Uint32() | |
| 543 | + | ||
| 544 | + | for i, handler := range o.handlersForDeleteSurroundingText() { | |
| 545 | + | _ = i | |
| 546 | + | handler(beforeLength, afterLength) | |
| 547 | + | } | |
| 548 | + | case 5: // done | |
| 549 | + | serial := event.Uint32() | |
| 550 | + | ||
| 551 | + | for i, handler := range o.handlersForDone() { | |
| 552 | + | _ = i | |
| 553 | + | handler(serial) | |
| 554 | + | } | |
| 555 | + | case 6: // action | |
| 556 | + | action := event.Uint32() | |
| 557 | + | serial := event.Uint32() | |
| 558 | + | ||
| 559 | + | for i, handler := range o.handlersForAction() { | |
| 560 | + | _ = i | |
| 561 | + | handler(action, serial) | |
| 562 | + | } | |
| 563 | + | case 7: // language | |
| 564 | + | language := event.String() | |
| 565 | + | ||
| 566 | + | for i, handler := range o.handlersForLanguage() { | |
| 567 | + | _ = i | |
| 568 | + | handler(language) | |
| 569 | + | } | |
| 570 | + | case 8: // preedit_hint | |
| 571 | + | start := event.Uint32() | |
| 572 | + | end := event.Uint32() | |
| 573 | + | hint := event.Uint32() | |
| 574 | + | ||
| 575 | + | for i, handler := range o.handlersForPreeditHint() { | |
| 576 | + | _ = i | |
| 577 | + | handler(start, end, hint) | |
| 578 | + | } | |
| 579 | + | default: | |
| 580 | + | } | |
| 581 | + | } | |
| 582 | + | ||
| 583 | + | // TextInputManagerV3Interface is the protocol interface name. | |
| 584 | + | const TextInputManagerV3Interface = "zwp_text_input_manager_v3" | |
| 585 | + | ||
| 586 | + | // TextInputManagerV3 is the generated binding for zwp_text_input_manager_v3 version 2. | |
| 587 | + | type TextInputManagerV3 struct { | |
| 588 | + | wl.BaseProxy | |
| 589 | + | } | |
| 590 | + | ||
| 591 | + | // NewTextInputManagerV3 returns an unbound TextInputManagerV3. | |
| 592 | + | func NewTextInputManagerV3(ctx *wl.Context) *TextInputManagerV3 { | |
| 593 | + | obj := &TextInputManagerV3{} | |
| 594 | + | obj.SetContext(ctx) | |
| 595 | + | ctx.Register(obj) | |
| 596 | + | return obj | |
| 597 | + | } | |
| 598 | + | ||
| 599 | + | // Destroy the wp_text_input_manager object. | |
| 600 | + | func (o *TextInputManagerV3) Destroy() error { | |
| 601 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 602 | + | return err | |
| 603 | + | } | |
| 604 | + | if err := o.Context().SendDestructor(o, 0); err != nil { | |
| 605 | + | return err | |
| 606 | + | } | |
| 607 | + | return nil | |
| 608 | + | } | |
| 609 | + | ||
| 610 | + | // Creates a new text-input object for a given seat. | |
| 611 | + | func (o *TextInputManagerV3) GetTextInput(seat *cross_wl_seat.Seat) (*TextInputV3, error) { | |
| 612 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 613 | + | return nil, err | |
| 614 | + | } | |
| 615 | + | var arg1 wl.Object | |
| 616 | + | if seat != nil { | |
| 617 | + | arg1 = seat | |
| 618 | + | } | |
| 619 | + | child := &TextInputV3{} | |
| 620 | + | child.SetContext(o.Context()) | |
| 621 | + | child.SetID(o.Context().AllocateID()) | |
| 622 | + | o.Context().Register(child) | |
| 623 | + | if err := o.Context().SendRequest(o, 1, child, arg1); err != nil { | |
| 624 | + | o.Context().Unregister(child) | |
| 625 | + | return nil, err | |
| 626 | + | } | |
| 627 | + | return child, nil | |
| 628 | + | } | |
| 629 | + | ||
| 630 | + | // EventSignature returns the declared event argument types in wire order. | |
| 631 | + | func (o *TextInputManagerV3) EventSignature(opcode uint16) (string, bool) { | |
| 632 | + | switch opcode { | |
| 633 | + | default: | |
| 634 | + | return "", false | |
| 635 | + | } | |
| 636 | + | } | |
| 637 | + | ||
| 638 | + | // Dispatch decodes one event on zwp_text_input_manager_v3 and calls the registered handlers. | |
| 639 | + | func (o *TextInputManagerV3) Dispatch(event *wl.Event) { | |
| 640 | + | switch event.Opcode { | |
| 641 | + | default: | |
| 642 | + | } | |
| 643 | + | } | |
A protocol/viewporter/viewporter.xml +177 -0
| @@ -0,0 +1,177 @@ | |||
| 1 | + | <?xml version="1.0" encoding="UTF-8"?> | |
| 2 | + | <protocol name="viewporter"> | |
| 3 | + | ||
| 4 | + | <copyright> | |
| 5 | + | Copyright © 2013-2016 Collabora, Ltd. | |
| 6 | + | ||
| 7 | + | Permission is hereby granted, free of charge, to any person obtaining a | |
| 8 | + | copy of this software and associated documentation files (the "Software"), | |
| 9 | + | to deal in the Software without restriction, including without limitation | |
| 10 | + | the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 11 | + | and/or sell copies of the Software, and to permit persons to whom the | |
| 12 | + | Software is furnished to do so, subject to the following conditions: | |
| 13 | + | ||
| 14 | + | The above copyright notice and this permission notice (including the next | |
| 15 | + | paragraph) shall be included in all copies or substantial portions of the | |
| 16 | + | Software. | |
| 17 | + | ||
| 18 | + | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 19 | + | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 20 | + | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 21 | + | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 22 | + | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
| 23 | + | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
| 24 | + | DEALINGS IN THE SOFTWARE. | |
| 25 | + | </copyright> | |
| 26 | + | ||
| 27 | + | <interface name="wp_viewporter" version="1"> | |
| 28 | + | <description summary="surface cropping and scaling"> | |
| 29 | + | The global interface exposing surface cropping and scaling | |
| 30 | + | capabilities is used to instantiate an interface extension for a | |
| 31 | + | wl_surface object. This extended interface will then allow | |
| 32 | + | cropping and scaling the surface contents, effectively | |
| 33 | + | disconnecting the direct relationship between the buffer and the | |
| 34 | + | surface size. | |
| 35 | + | </description> | |
| 36 | + | ||
| 37 | + | <request name="destroy" type="destructor"> | |
| 38 | + | <description summary="unbind from the cropping and scaling interface"> | |
| 39 | + | Informs the server that the client will not be using this | |
| 40 | + | protocol object anymore. This does not affect any other objects, | |
| 41 | + | wp_viewport objects included. | |
| 42 | + | </description> | |
| 43 | + | </request> | |
| 44 | + | ||
| 45 | + | <enum name="error"> | |
| 46 | + | <entry name="viewport_exists" value="0" | |
| 47 | + | summary="the surface already has a viewport object associated"/> | |
| 48 | + | </enum> | |
| 49 | + | ||
| 50 | + | <request name="get_viewport"> | |
| 51 | + | <description summary="extend surface interface for crop and scale"> | |
| 52 | + | Instantiate an interface extension for the given wl_surface to | |
| 53 | + | crop and scale its content. If the given wl_surface already has | |
| 54 | + | a wp_viewport object associated, the viewport_exists | |
| 55 | + | protocol error is raised. | |
| 56 | + | </description> | |
| 57 | + | <arg name="id" type="new_id" interface="wp_viewport" | |
| 58 | + | summary="the new viewport interface id"/> | |
| 59 | + | <arg name="surface" type="object" interface="wl_surface" | |
| 60 | + | summary="the surface"/> | |
| 61 | + | </request> | |
| 62 | + | </interface> | |
| 63 | + | ||
| 64 | + | <interface name="wp_viewport" version="1"> | |
| 65 | + | <description summary="crop and scale interface to a wl_surface"> | |
| 66 | + | An additional interface to a wl_surface object, which allows the | |
| 67 | + | client to specify the cropping and scaling of the surface | |
| 68 | + | contents. | |
| 69 | + | ||
| 70 | + | This interface works with two concepts: the source rectangle (src_x, | |
| 71 | + | src_y, src_width, src_height), and the destination size (dst_width, | |
| 72 | + | dst_height). The contents of the source rectangle are scaled to the | |
| 73 | + | destination size, and content outside the source rectangle is ignored. | |
| 74 | + | This state is double-buffered, see wl_surface.commit. | |
| 75 | + | ||
| 76 | + | The two parts of crop and scale state are independent: the source | |
| 77 | + | rectangle, and the destination size. Initially both are unset, that | |
| 78 | + | is, no scaling is applied. The whole of the current wl_buffer is | |
| 79 | + | used as the source, and the surface size is as defined in | |
| 80 | + | wl_surface.attach. | |
| 81 | + | ||
| 82 | + | If the destination size is set, it causes the surface size to become | |
| 83 | + | dst_width, dst_height. The source (rectangle) is scaled to exactly | |
| 84 | + | this size. This overrides whatever the attached wl_buffer size is, | |
| 85 | + | unless the wl_buffer is NULL. If the wl_buffer is NULL, the surface | |
| 86 | + | has no content and therefore no size. Otherwise, the size is always | |
| 87 | + | at least 1x1 in surface local coordinates. | |
| 88 | + | ||
| 89 | + | If the source rectangle is set, it defines what area of the wl_buffer is | |
| 90 | + | taken as the source. If the source rectangle is set and the destination | |
| 91 | + | size is not set, then src_width and src_height must be integers, and the | |
| 92 | + | surface size becomes the source rectangle size. This results in cropping | |
| 93 | + | without scaling. If src_width or src_height are not integers and | |
| 94 | + | destination size is not set, the bad_size protocol error is raised when | |
| 95 | + | the surface state is applied. | |
| 96 | + | ||
| 97 | + | The coordinate transformations from buffer pixel coordinates up to | |
| 98 | + | the surface-local coordinates happen in the following order: | |
| 99 | + | 1. buffer_transform (wl_surface.set_buffer_transform) | |
| 100 | + | 2. buffer_scale (wl_surface.set_buffer_scale) | |
| 101 | + | 3. crop and scale (wp_viewport.set*) | |
| 102 | + | This means, that the source rectangle coordinates of crop and scale | |
| 103 | + | are given in the coordinates after the buffer transform and scale, | |
| 104 | + | i.e. in the coordinates that would be the surface-local coordinates | |
| 105 | + | if the crop and scale was not applied. | |
| 106 | + | ||
| 107 | + | If src_x or src_y are negative, the bad_value protocol error is raised. | |
| 108 | + | Otherwise, if the source rectangle is partially or completely outside of | |
| 109 | + | the non-NULL wl_buffer, then the out_of_buffer protocol error is raised | |
| 110 | + | when the surface state is applied. A NULL wl_buffer does not raise the | |
| 111 | + | out_of_buffer error. | |
| 112 | + | ||
| 113 | + | If the wl_surface associated with the wp_viewport is destroyed, | |
| 114 | + | all wp_viewport requests except 'destroy' raise the protocol error | |
| 115 | + | no_surface. | |
| 116 | + | ||
| 117 | + | If the wp_viewport object is destroyed, the crop and scale | |
| 118 | + | state is removed from the wl_surface. The change will be applied | |
| 119 | + | on the next wl_surface.commit. | |
| 120 | + | </description> | |
| 121 | + | ||
| 122 | + | <request name="destroy" type="destructor"> | |
| 123 | + | <description summary="remove scaling and cropping from the surface"> | |
| 124 | + | The associated wl_surface's crop and scale state is removed. | |
| 125 | + | The change is applied on the next wl_surface.commit. | |
| 126 | + | </description> | |
| 127 | + | </request> | |
| 128 | + | ||
| 129 | + | <enum name="error"> | |
| 130 | + | <entry name="bad_value" value="0" | |
| 131 | + | summary="negative or zero values in width or height"/> | |
| 132 | + | <entry name="bad_size" value="1" | |
| 133 | + | summary="destination size is not integer"/> | |
| 134 | + | <entry name="out_of_buffer" value="2" | |
| 135 | + | summary="source rectangle extends outside of the content area"/> | |
| 136 | + | <entry name="no_surface" value="3" | |
| 137 | + | summary="the wl_surface was destroyed"/> | |
| 138 | + | </enum> | |
| 139 | + | ||
| 140 | + | <request name="set_source"> | |
| 141 | + | <description summary="set the source rectangle for cropping"> | |
| 142 | + | Set the source rectangle of the associated wl_surface. See | |
| 143 | + | wp_viewport for the description, and relation to the wl_buffer | |
| 144 | + | size. | |
| 145 | + | ||
| 146 | + | If all of x, y, width and height are -1.0, the source rectangle is | |
| 147 | + | unset instead. Any other set of values where width or height are zero | |
| 148 | + | or negative, or x or y are negative, raise the bad_value protocol | |
| 149 | + | error. | |
| 150 | + | ||
| 151 | + | The crop and scale state is double-buffered, see wl_surface.commit. | |
| 152 | + | </description> | |
| 153 | + | <arg name="x" type="fixed" summary="source rectangle x"/> | |
| 154 | + | <arg name="y" type="fixed" summary="source rectangle y"/> | |
| 155 | + | <arg name="width" type="fixed" summary="source rectangle width"/> | |
| 156 | + | <arg name="height" type="fixed" summary="source rectangle height"/> | |
| 157 | + | </request> | |
| 158 | + | ||
| 159 | + | <request name="set_destination"> | |
| 160 | + | <description summary="set the surface size for scaling"> | |
| 161 | + | Set the destination size of the associated wl_surface. See | |
| 162 | + | wp_viewport for the description, and relation to the wl_buffer | |
| 163 | + | size. | |
| 164 | + | ||
| 165 | + | If width is -1 and height is -1, the destination size is unset | |
| 166 | + | instead. Any other pair of values for width and height that | |
| 167 | + | contains zero or negative values raises the bad_value protocol | |
| 168 | + | error. | |
| 169 | + | ||
| 170 | + | The crop and scale state is double-buffered, see wl_surface.commit. | |
| 171 | + | </description> | |
| 172 | + | <arg name="width" type="int" summary="surface width"/> | |
| 173 | + | <arg name="height" type="int" summary="surface height"/> | |
| 174 | + | </request> | |
| 175 | + | </interface> | |
| 176 | + | ||
| 177 | + | </protocol> | |
A protocol/viewporter/viewporter_generated.go +151 -0
| @@ -0,0 +1,151 @@ | |||
| 1 | + | // Code generated by wlturbo-scanner. DO NOT EDIT. | |
| 2 | + | // Protocol: viewporter | |
| 3 | + | // Source: viewporter.xml | |
| 4 | + | ||
| 5 | + | package viewporter | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | cross_wl_surface "github.com/bnema/wlturbo/protocol/core" | |
| 9 | + | "github.com/bnema/wlturbo/wl" | |
| 10 | + | ) | |
| 11 | + | ||
| 12 | + | // ERROR_VIEWPORT_EXISTS is the error viewport_exists value. | |
| 13 | + | const ERROR_VIEWPORT_EXISTS = 0 | |
| 14 | + | ||
| 15 | + | // ERROR_BAD_VALUE is the error bad_value value. | |
| 16 | + | const ERROR_BAD_VALUE = 0 | |
| 17 | + | ||
| 18 | + | // ERROR_BAD_SIZE is the error bad_size value. | |
| 19 | + | const ERROR_BAD_SIZE = 1 | |
| 20 | + | ||
| 21 | + | // ERROR_OUT_OF_BUFFER is the error out_of_buffer value. | |
| 22 | + | const ERROR_OUT_OF_BUFFER = 2 | |
| 23 | + | ||
| 24 | + | // ERROR_NO_SURFACE is the error no_surface value. | |
| 25 | + | const ERROR_NO_SURFACE = 3 | |
| 26 | + | ||
| 27 | + | // WpViewporterInterface is the protocol interface name. | |
| 28 | + | const WpViewporterInterface = "wp_viewporter" | |
| 29 | + | ||
| 30 | + | // WpViewporter is the generated binding for wp_viewporter version 1. | |
| 31 | + | type WpViewporter struct { | |
| 32 | + | wl.BaseProxy | |
| 33 | + | } | |
| 34 | + | ||
| 35 | + | // NewWpViewporter returns an unbound WpViewporter. | |
| 36 | + | func NewWpViewporter(ctx *wl.Context) *WpViewporter { | |
| 37 | + | obj := &WpViewporter{} | |
| 38 | + | obj.SetContext(ctx) | |
| 39 | + | ctx.Register(obj) | |
| 40 | + | return obj | |
| 41 | + | } | |
| 42 | + | ||
| 43 | + | // Informs the server that the client will not be using this protocol object anymore. This does not affect any other objects, wp_viewport objects included. | |
| 44 | + | func (o *WpViewporter) Destroy() error { | |
| 45 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 46 | + | return err | |
| 47 | + | } | |
| 48 | + | if err := o.Context().SendDestructor(o, 0); err != nil { | |
| 49 | + | return err | |
| 50 | + | } | |
| 51 | + | return nil | |
| 52 | + | } | |
| 53 | + | ||
| 54 | + | // Instantiate an interface extension for the given wl_surface to crop and scale its content. If the given wl_surface already has a wp_viewport object associated, the viewport_exists protocol error is raised. | |
| 55 | + | func (o *WpViewporter) GetViewport(surface *cross_wl_surface.Surface) (*WpViewport, error) { | |
| 56 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 57 | + | return nil, err | |
| 58 | + | } | |
| 59 | + | var arg1 wl.Object | |
| 60 | + | if surface != nil { | |
| 61 | + | arg1 = surface | |
| 62 | + | } | |
| 63 | + | child := &WpViewport{} | |
| 64 | + | child.SetContext(o.Context()) | |
| 65 | + | child.SetID(o.Context().AllocateID()) | |
| 66 | + | o.Context().Register(child) | |
| 67 | + | if err := o.Context().SendRequest(o, 1, child, arg1); err != nil { | |
| 68 | + | o.Context().Unregister(child) | |
| 69 | + | return nil, err | |
| 70 | + | } | |
| 71 | + | return child, nil | |
| 72 | + | } | |
| 73 | + | ||
| 74 | + | // EventSignature returns the declared event argument types in wire order. | |
| 75 | + | func (o *WpViewporter) EventSignature(opcode uint16) (string, bool) { | |
| 76 | + | switch opcode { | |
| 77 | + | default: | |
| 78 | + | return "", false | |
| 79 | + | } | |
| 80 | + | } | |
| 81 | + | ||
| 82 | + | // Dispatch decodes one event on wp_viewporter and calls the registered handlers. | |
| 83 | + | func (o *WpViewporter) Dispatch(event *wl.Event) { | |
| 84 | + | switch event.Opcode { | |
| 85 | + | default: | |
| 86 | + | } | |
| 87 | + | } | |
| 88 | + | ||
| 89 | + | // WpViewportInterface is the protocol interface name. | |
| 90 | + | const WpViewportInterface = "wp_viewport" | |
| 91 | + | ||
| 92 | + | // WpViewport is the generated binding for wp_viewport version 1. | |
| 93 | + | type WpViewport struct { | |
| 94 | + | wl.BaseProxy | |
| 95 | + | } | |
| 96 | + | ||
| 97 | + | // NewWpViewport returns an unbound WpViewport. | |
| 98 | + | func NewWpViewport(ctx *wl.Context) *WpViewport { | |
| 99 | + | obj := &WpViewport{} | |
| 100 | + | obj.SetContext(ctx) | |
| 101 | + | ctx.Register(obj) | |
| 102 | + | return obj | |
| 103 | + | } | |
| 104 | + | ||
| 105 | + | // The associated wl_surface's crop and scale state is removed. The change is applied on the next wl_surface.commit. | |
| 106 | + | func (o *WpViewport) Destroy() error { | |
| 107 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 108 | + | return err | |
| 109 | + | } | |
| 110 | + | if err := o.Context().SendDestructor(o, 0); err != nil { | |
| 111 | + | return err | |
| 112 | + | } | |
| 113 | + | return nil | |
| 114 | + | } | |
| 115 | + | ||
| 116 | + | // Set the source rectangle of the associated wl_surface. See wp_viewport for the description, and relation to the wl_buffer size. If all of x, y, width and height are -1.0, the source rectangle is unset instead. Any other set of values where width or height are zero or negative, or x or y are negative, raise the bad_value protocol error. The crop and scale state is double-buffered, see wl_surface.commit. | |
| 117 | + | func (o *WpViewport) SetSource(x wl.Fixed, y wl.Fixed, width wl.Fixed, height wl.Fixed) error { | |
| 118 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 119 | + | return err | |
| 120 | + | } | |
| 121 | + | if err := o.Context().SendRequest(o, 1, x, y, width, height); err != nil { | |
| 122 | + | return err | |
| 123 | + | } | |
| 124 | + | return nil | |
| 125 | + | } | |
| 126 | + | ||
| 127 | + | // Set the destination size of the associated wl_surface. See wp_viewport for the description, and relation to the wl_buffer size. If width is -1 and height is -1, the destination size is unset instead. Any other pair of values for width and height that contains zero or negative values raises the bad_value protocol error. The crop and scale state is double-buffered, see wl_surface.commit. | |
| 128 | + | func (o *WpViewport) SetDestination(width int32, height int32) error { | |
| 129 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 130 | + | return err | |
| 131 | + | } | |
| 132 | + | if err := o.Context().SendRequest(o, 2, width, height); err != nil { | |
| 133 | + | return err | |
| 134 | + | } | |
| 135 | + | return nil | |
| 136 | + | } | |
| 137 | + | ||
| 138 | + | // EventSignature returns the declared event argument types in wire order. | |
| 139 | + | func (o *WpViewport) EventSignature(opcode uint16) (string, bool) { | |
| 140 | + | switch opcode { | |
| 141 | + | default: | |
| 142 | + | return "", false | |
| 143 | + | } | |
| 144 | + | } | |
| 145 | + | ||
| 146 | + | // Dispatch decodes one event on wp_viewport and calls the registered handlers. | |
| 147 | + | func (o *WpViewport) Dispatch(event *wl.Event) { | |
| 148 | + | switch event.Opcode { | |
| 149 | + | default: | |
| 150 | + | } | |
| 151 | + | } | |
A protocol/xdgshell/xdg-shell.xml +1420 -0
| @@ -0,0 +1,1420 @@ | |||
| 1 | + | <?xml version="1.0" encoding="UTF-8"?> | |
| 2 | + | <protocol name="xdg_shell"> | |
| 3 | + | ||
| 4 | + | <copyright> | |
| 5 | + | Copyright © 2008-2013 Kristian Høgsberg | |
| 6 | + | Copyright © 2013 Rafael Antognolli | |
| 7 | + | Copyright © 2013 Jasper St. Pierre | |
| 8 | + | Copyright © 2010-2013 Intel Corporation | |
| 9 | + | Copyright © 2015-2017 Samsung Electronics Co., Ltd | |
| 10 | + | Copyright © 2015-2017 Red Hat Inc. | |
| 11 | + | ||
| 12 | + | Permission is hereby granted, free of charge, to any person obtaining a | |
| 13 | + | copy of this software and associated documentation files (the "Software"), | |
| 14 | + | to deal in the Software without restriction, including without limitation | |
| 15 | + | the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 16 | + | and/or sell copies of the Software, and to permit persons to whom the | |
| 17 | + | Software is furnished to do so, subject to the following conditions: | |
| 18 | + | ||
| 19 | + | The above copyright notice and this permission notice (including the next | |
| 20 | + | paragraph) shall be included in all copies or substantial portions of the | |
| 21 | + | Software. | |
| 22 | + | ||
| 23 | + | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 24 | + | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 25 | + | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 26 | + | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 27 | + | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
| 28 | + | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
| 29 | + | DEALINGS IN THE SOFTWARE. | |
| 30 | + | </copyright> | |
| 31 | + | ||
| 32 | + | <interface name="xdg_wm_base" version="7"> | |
| 33 | + | <description summary="create desktop-style surfaces"> | |
| 34 | + | The xdg_wm_base interface is exposed as a global object enabling clients | |
| 35 | + | to turn their wl_surfaces into windows in a desktop environment. It | |
| 36 | + | defines the basic functionality needed for clients and the compositor to | |
| 37 | + | create windows that can be dragged, resized, maximized, etc, as well as | |
| 38 | + | creating transient windows such as popup menus. | |
| 39 | + | </description> | |
| 40 | + | ||
| 41 | + | <enum name="error"> | |
| 42 | + | <entry name="role" value="0" summary="given wl_surface has another role"/> | |
| 43 | + | <entry name="defunct_surfaces" value="1" | |
| 44 | + | summary="xdg_wm_base was destroyed before children"/> | |
| 45 | + | <entry name="not_the_topmost_popup" value="2" | |
| 46 | + | summary="the client tried to map or destroy a non-topmost popup"/> | |
| 47 | + | <entry name="invalid_popup_parent" value="3" | |
| 48 | + | summary="the client specified an invalid popup parent surface"/> | |
| 49 | + | <entry name="invalid_surface_state" value="4" | |
| 50 | + | summary="the client provided an invalid surface state"/> | |
| 51 | + | <entry name="invalid_positioner" value="5" | |
| 52 | + | summary="the client provided an invalid positioner"/> | |
| 53 | + | <entry name="unresponsive" value="6" | |
| 54 | + | summary="the client didn’t respond to a ping event in time"/> | |
| 55 | + | </enum> | |
| 56 | + | ||
| 57 | + | <request name="destroy" type="destructor"> | |
| 58 | + | <description summary="destroy xdg_wm_base"> | |
| 59 | + | Destroy this xdg_wm_base object. | |
| 60 | + | ||
| 61 | + | Destroying a bound xdg_wm_base object while there are surfaces | |
| 62 | + | still alive created by this xdg_wm_base object instance is illegal | |
| 63 | + | and will result in a defunct_surfaces error. | |
| 64 | + | </description> | |
| 65 | + | </request> | |
| 66 | + | ||
| 67 | + | <request name="create_positioner"> | |
| 68 | + | <description summary="create a positioner object"> | |
| 69 | + | Create a positioner object. A positioner object is used to position | |
| 70 | + | surfaces relative to some parent surface. See the interface description | |
| 71 | + | and xdg_surface.get_popup for details. | |
| 72 | + | </description> | |
| 73 | + | <arg name="id" type="new_id" interface="xdg_positioner"/> | |
| 74 | + | </request> | |
| 75 | + | ||
| 76 | + | <request name="get_xdg_surface"> | |
| 77 | + | <description summary="create a shell surface from a surface"> | |
| 78 | + | This creates an xdg_surface for the given surface. An xdg_surface is | |
| 79 | + | used as basis to define a role to a given surface, such as xdg_toplevel | |
| 80 | + | or xdg_popup. It also manages functionality shared between xdg_surface | |
| 81 | + | based surface roles. | |
| 82 | + | ||
| 83 | + | While xdg_surface itself is not a role, the corresponding surface may | |
| 84 | + | only be assigned a role extending xdg_surface, such as xdg_toplevel or | |
| 85 | + | xdg_popup. It is illegal to create an xdg_surface for a wl_surface which | |
| 86 | + | already has anassigned role and this will result in a role error. | |
| 87 | + | ||
| 88 | + | See the documentation of xdg_surface for more details about what an | |
| 89 | + | xdg_surface is and how it is used. | |
| 90 | + | </description> | |
| 91 | + | <arg name="id" type="new_id" interface="xdg_surface"/> | |
| 92 | + | <arg name="surface" type="object" interface="wl_surface"/> | |
| 93 | + | </request> | |
| 94 | + | ||
| 95 | + | <request name="pong"> | |
| 96 | + | <description summary="respond to a ping event"> | |
| 97 | + | A client must respond to a ping event with a pong request or | |
| 98 | + | the client may be deemed unresponsive. See xdg_wm_base.ping | |
| 99 | + | and xdg_wm_base.error.unresponsive. | |
| 100 | + | </description> | |
| 101 | + | <arg name="serial" type="uint" summary="serial of the ping event"/> | |
| 102 | + | </request> | |
| 103 | + | ||
| 104 | + | <event name="ping"> | |
| 105 | + | <description summary="check if the client is alive"> | |
| 106 | + | The ping event asks the client if it's still alive. Pass the | |
| 107 | + | serial specified in the event back to the compositor by sending | |
| 108 | + | a "pong" request back with the specified serial. See xdg_wm_base.pong. | |
| 109 | + | ||
| 110 | + | Compositors can use this to determine if the client is still | |
| 111 | + | alive. It's unspecified what will happen if the client doesn't | |
| 112 | + | respond to the ping request, or in what timeframe. Clients should | |
| 113 | + | try to respond in a reasonable amount of time. The “unresponsive” | |
| 114 | + | error is provided for compositors that wish to disconnect unresponsive | |
| 115 | + | clients. | |
| 116 | + | ||
| 117 | + | A compositor is free to ping in any way it wants, but a client must | |
| 118 | + | always respond to any xdg_wm_base object it created. | |
| 119 | + | </description> | |
| 120 | + | <arg name="serial" type="uint" summary="pass this to the pong request"/> | |
| 121 | + | </event> | |
| 122 | + | </interface> | |
| 123 | + | ||
| 124 | + | <interface name="xdg_positioner" version="7"> | |
| 125 | + | <description summary="child surface positioner"> | |
| 126 | + | The xdg_positioner provides a collection of rules for the placement of a | |
| 127 | + | child surface relative to a parent surface. Rules can be defined to ensure | |
| 128 | + | the child surface remains within the visible area's borders, and to | |
| 129 | + | specify how the child surface changes its position, such as sliding along | |
| 130 | + | an axis, or flipping around a rectangle. These positioner-created rules are | |
| 131 | + | constrained by the requirement that a child surface must intersect with or | |
| 132 | + | be at least partially adjacent to its parent surface. | |
| 133 | + | ||
| 134 | + | See the various requests for details about possible rules. | |
| 135 | + | ||
| 136 | + | At the time of the request, the compositor makes a copy of the rules | |
| 137 | + | specified by the xdg_positioner. Thus, after the request is complete the | |
| 138 | + | xdg_positioner object can be destroyed or reused; further changes to the | |
| 139 | + | object will have no effect on previous usages. | |
| 140 | + | ||
| 141 | + | For an xdg_positioner object to be considered complete, it must have a | |
| 142 | + | non-zero size set by set_size, and a non-zero anchor rectangle set by | |
| 143 | + | set_anchor_rect. Passing an incomplete xdg_positioner object when | |
| 144 | + | positioning a surface raises an invalid_positioner error. | |
| 145 | + | </description> | |
| 146 | + | ||
| 147 | + | <enum name="error"> | |
| 148 | + | <entry name="invalid_input" value="0" summary="invalid input provided"/> | |
| 149 | + | </enum> | |
| 150 | + | ||
| 151 | + | <request name="destroy" type="destructor"> | |
| 152 | + | <description summary="destroy the xdg_positioner object"> | |
| 153 | + | Notify the compositor that the xdg_positioner will no longer be used. | |
| 154 | + | </description> | |
| 155 | + | </request> | |
| 156 | + | ||
| 157 | + | <request name="set_size"> | |
| 158 | + | <description summary="set the size of the to-be positioned rectangle"> | |
| 159 | + | Set the size of the surface that is to be positioned with the positioner | |
| 160 | + | object. The size is in surface-local coordinates and corresponds to the | |
| 161 | + | window geometry. See xdg_surface.set_window_geometry. | |
| 162 | + | ||
| 163 | + | If a zero or negative size is set the invalid_input error is raised. | |
| 164 | + | </description> | |
| 165 | + | <arg name="width" type="int" summary="width of positioned rectangle"/> | |
| 166 | + | <arg name="height" type="int" summary="height of positioned rectangle"/> | |
| 167 | + | </request> | |
| 168 | + | ||
| 169 | + | <request name="set_anchor_rect"> | |
| 170 | + | <description summary="set the anchor rectangle within the parent surface"> | |
| 171 | + | Specify the anchor rectangle within the parent surface that the child | |
| 172 | + | surface will be placed relative to. The rectangle is relative to the | |
| 173 | + | window geometry as defined by xdg_surface.set_window_geometry of the | |
| 174 | + | parent surface. | |
| 175 | + | ||
| 176 | + | When the xdg_positioner object is used to position a child surface, the | |
| 177 | + | anchor rectangle may not extend outside the window geometry of the | |
| 178 | + | positioned child's parent surface. | |
| 179 | + | ||
| 180 | + | If a negative size is set the invalid_input error is raised. | |
| 181 | + | </description> | |
| 182 | + | <arg name="x" type="int" summary="x position of anchor rectangle"/> | |
| 183 | + | <arg name="y" type="int" summary="y position of anchor rectangle"/> | |
| 184 | + | <arg name="width" type="int" summary="width of anchor rectangle"/> | |
| 185 | + | <arg name="height" type="int" summary="height of anchor rectangle"/> | |
| 186 | + | </request> | |
| 187 | + | ||
| 188 | + | <enum name="anchor"> | |
| 189 | + | <entry name="none" value="0"/> | |
| 190 | + | <entry name="top" value="1"/> | |
| 191 | + | <entry name="bottom" value="2"/> | |
| 192 | + | <entry name="left" value="3"/> | |
| 193 | + | <entry name="right" value="4"/> | |
| 194 | + | <entry name="top_left" value="5"/> | |
| 195 | + | <entry name="bottom_left" value="6"/> | |
| 196 | + | <entry name="top_right" value="7"/> | |
| 197 | + | <entry name="bottom_right" value="8"/> | |
| 198 | + | </enum> | |
| 199 | + | ||
| 200 | + | <request name="set_anchor"> | |
| 201 | + | <description summary="set anchor rectangle anchor"> | |
| 202 | + | Defines the anchor point for the anchor rectangle. The specified anchor | |
| 203 | + | is used to derive an anchor point that the child surface will be | |
| 204 | + | positioned relative to. If a corner anchor is set (e.g. 'top_left' or | |
| 205 | + | 'bottom_right'), the anchor point will be at the specified corner; | |
| 206 | + | otherwise, the derived anchor point will be centered on the specified | |
| 207 | + | edge, or in the center of the anchor rectangle if no edge is specified. | |
| 208 | + | </description> | |
| 209 | + | <arg name="anchor" type="uint" enum="anchor" | |
| 210 | + | summary="anchor point"/> | |
| 211 | + | </request> | |
| 212 | + | ||
| 213 | + | <enum name="gravity"> | |
| 214 | + | <entry name="none" value="0"/> | |
| 215 | + | <entry name="top" value="1"/> | |
| 216 | + | <entry name="bottom" value="2"/> | |
| 217 | + | <entry name="left" value="3"/> | |
| 218 | + | <entry name="right" value="4"/> | |
| 219 | + | <entry name="top_left" value="5"/> | |
| 220 | + | <entry name="bottom_left" value="6"/> | |
| 221 | + | <entry name="top_right" value="7"/> | |
| 222 | + | <entry name="bottom_right" value="8"/> | |
| 223 | + | </enum> | |
| 224 | + | ||
| 225 | + | <request name="set_gravity"> | |
| 226 | + | <description summary="set child surface gravity"> | |
| 227 | + | Defines in what direction a surface should be positioned, relative to | |
| 228 | + | the anchor point of the parent surface. If a corner gravity is | |
| 229 | + | specified (e.g. 'bottom_right' or 'top_left'), then the child surface | |
| 230 | + | will be placed towards the specified gravity; otherwise, the child | |
| 231 | + | surface will be centered over the anchor point on any axis that had no | |
| 232 | + | gravity specified. If the gravity is not in the ‘gravity’ enum, an | |
| 233 | + | invalid_input error is raised. | |
| 234 | + | </description> | |
| 235 | + | <arg name="gravity" type="uint" enum="gravity" | |
| 236 | + | summary="gravity direction"/> | |
| 237 | + | </request> | |
| 238 | + | ||
| 239 | + | <enum name="constraint_adjustment" bitfield="true"> | |
| 240 | + | <description summary="constraint adjustments"> | |
| 241 | + | The constraint adjustment value define ways the compositor will adjust | |
| 242 | + | the position of the surface, if the unadjusted position would result | |
| 243 | + | in the surface being partly constrained. | |
| 244 | + | ||
| 245 | + | Whether a surface is considered 'constrained' is left to the compositor | |
| 246 | + | to determine. For example, the surface may be partly outside the | |
| 247 | + | compositor's defined 'work area', thus necessitating the child surface's | |
| 248 | + | position be adjusted until it is entirely inside the work area. | |
| 249 | + | ||
| 250 | + | The adjustments can be combined, according to a defined precedence: 1) | |
| 251 | + | Flip, 2) Slide, 3) Resize. | |
| 252 | + | </description> | |
| 253 | + | <entry name="none" value="0"> | |
| 254 | + | <description summary="don't move the child surface when constrained"> | |
| 255 | + | Don't alter the surface position even if it is constrained on some | |
| 256 | + | axis, for example partially outside the edge of an output. | |
| 257 | + | </description> | |
| 258 | + | </entry> | |
| 259 | + | <entry name="slide_x" value="1"> | |
| 260 | + | <description summary="move along the x axis until unconstrained"> | |
| 261 | + | Slide the surface along the x axis until it is no longer constrained. | |
| 262 | + | ||
| 263 | + | First try to slide towards the direction of the gravity on the x axis | |
| 264 | + | until either the edge in the opposite direction of the gravity is | |
| 265 | + | unconstrained or the edge in the direction of the gravity is | |
| 266 | + | constrained. | |
| 267 | + | ||
| 268 | + | Then try to slide towards the opposite direction of the gravity on the | |
| 269 | + | x axis until either the edge in the direction of the gravity is | |
| 270 | + | unconstrained or the edge in the opposite direction of the gravity is | |
| 271 | + | constrained. | |
| 272 | + | </description> | |
| 273 | + | </entry> | |
| 274 | + | <entry name="slide_y" value="2"> | |
| 275 | + | <description summary="move along the y axis until unconstrained"> | |
| 276 | + | Slide the surface along the y axis until it is no longer constrained. | |
| 277 | + | ||
| 278 | + | First try to slide towards the direction of the gravity on the y axis | |
| 279 | + | until either the edge in the opposite direction of the gravity is | |
| 280 | + | unconstrained or the edge in the direction of the gravity is | |
| 281 | + | constrained. | |
| 282 | + | ||
| 283 | + | Then try to slide towards the opposite direction of the gravity on the | |
| 284 | + | y axis until either the edge in the direction of the gravity is | |
| 285 | + | unconstrained or the edge in the opposite direction of the gravity is | |
| 286 | + | constrained. | |
| 287 | + | </description> | |
| 288 | + | </entry> | |
| 289 | + | <entry name="flip_x" value="4"> | |
| 290 | + | <description summary="invert the anchor and gravity on the x axis"> | |
| 291 | + | Invert the anchor and gravity on the x axis if the surface is | |
| 292 | + | constrained on the x axis. For example, if the left edge of the | |
| 293 | + | surface is constrained, the gravity is 'left' and the anchor is | |
| 294 | + | 'left', change the gravity to 'right' and the anchor to 'right'. | |
| 295 | + | ||
| 296 | + | If the adjusted position also ends up being constrained, the resulting | |
| 297 | + | position of the flip_x adjustment will be the one before the | |
| 298 | + | adjustment. | |
| 299 | + | </description> | |
| 300 | + | </entry> | |
| 301 | + | <entry name="flip_y" value="8"> | |
| 302 | + | <description summary="invert the anchor and gravity on the y axis"> | |
| 303 | + | Invert the anchor and gravity on the y axis if the surface is | |
| 304 | + | constrained on the y axis. For example, if the bottom edge of the | |
| 305 | + | surface is constrained, the gravity is 'bottom' and the anchor is | |
| 306 | + | 'bottom', change the gravity to 'top' and the anchor to 'top'. | |
| 307 | + | ||
| 308 | + | The adjusted position is calculated given the original anchor | |
| 309 | + | rectangle and offset, but with the new flipped anchor and gravity | |
| 310 | + | values. | |
| 311 | + | ||
| 312 | + | If the adjusted position also ends up being constrained, the resulting | |
| 313 | + | position of the flip_y adjustment will be the one before the | |
| 314 | + | adjustment. | |
| 315 | + | </description> | |
| 316 | + | </entry> | |
| 317 | + | <entry name="resize_x" value="16"> | |
| 318 | + | <description summary="horizontally resize the surface"> | |
| 319 | + | Resize the surface horizontally so that it is completely | |
| 320 | + | unconstrained. | |
| 321 | + | </description> | |
| 322 | + | </entry> | |
| 323 | + | <entry name="resize_y" value="32"> | |
| 324 | + | <description summary="vertically resize the surface"> | |
| 325 | + | Resize the surface vertically so that it is completely unconstrained. | |
| 326 | + | </description> | |
| 327 | + | </entry> | |
| 328 | + | </enum> | |
| 329 | + | ||
| 330 | + | <request name="set_constraint_adjustment"> | |
| 331 | + | <description summary="set the adjustment to be done when constrained"> | |
| 332 | + | Specify how the window should be positioned if the originally intended | |
| 333 | + | position caused the surface to be constrained, meaning at least | |
| 334 | + | partially outside positioning boundaries set by the compositor. The | |
| 335 | + | adjustment is set by constructing a bitmask describing the adjustment to | |
| 336 | + | be made when the surface is constrained on that axis. | |
| 337 | + | ||
| 338 | + | If no bit for one axis is set, the compositor will assume that the child | |
| 339 | + | surface should not change its position on that axis when constrained. | |
| 340 | + | ||
| 341 | + | If more than one bit for one axis is set, the order of how adjustments | |
| 342 | + | are applied is specified in the corresponding adjustment descriptions. | |
| 343 | + | ||
| 344 | + | The default adjustment is none. | |
| 345 | + | </description> | |
| 346 | + | <arg name="constraint_adjustment" type="uint" enum="constraint_adjustment" | |
| 347 | + | summary="bit mask of constraint adjustments"/> | |
| 348 | + | </request> | |
| 349 | + | ||
| 350 | + | <request name="set_offset"> | |
| 351 | + | <description summary="set surface position offset"> | |
| 352 | + | Specify the surface position offset relative to the position of the | |
| 353 | + | anchor on the anchor rectangle and the anchor on the surface. For | |
| 354 | + | example if the anchor of the anchor rectangle is at (x, y), the surface | |
| 355 | + | has the gravity bottom|right, and the offset is (ox, oy), the calculated | |
| 356 | + | surface position will be (x + ox, y + oy). The offset position of the | |
| 357 | + | surface is the one used for constraint testing. See | |
| 358 | + | set_constraint_adjustment. | |
| 359 | + | ||
| 360 | + | An example use case is placing a popup menu on top of a user interface | |
| 361 | + | element, while aligning the user interface element of the parent surface | |
| 362 | + | with some user interface element placed somewhere in the popup surface. | |
| 363 | + | </description> | |
| 364 | + | <arg name="x" type="int" summary="surface position x offset"/> | |
| 365 | + | <arg name="y" type="int" summary="surface position y offset"/> | |
| 366 | + | </request> | |
| 367 | + | ||
| 368 | + | <!-- Version 3 additions --> | |
| 369 | + | ||
| 370 | + | <request name="set_reactive" since="3"> | |
| 371 | + | <description summary="continuously reconstrain the surface"> | |
| 372 | + | When set reactive, the surface is reconstrained if the conditions used | |
| 373 | + | for constraining changed, e.g. the parent window moved. | |
| 374 | + | ||
| 375 | + | If the conditions changed and the popup was reconstrained, an | |
| 376 | + | xdg_popup.configure event is sent with updated geometry, followed by an | |
| 377 | + | xdg_surface.configure event. | |
| 378 | + | </description> | |
| 379 | + | </request> | |
| 380 | + | ||
| 381 | + | <request name="set_parent_size" since="3"> | |
| 382 | + | <description summary="set parent size"> | |
| 383 | + | Set the parent window geometry the compositor should use when | |
| 384 | + | positioning the popup. The compositor may use this information to | |
| 385 | + | determine the future state the popup should be constrained using. If | |
| 386 | + | this doesn't match the dimension of the parent the popup is eventually | |
| 387 | + | positioned against, the behavior is undefined. | |
| 388 | + | ||
| 389 | + | The arguments are given in the surface-local coordinate space. | |
| 390 | + | </description> | |
| 391 | + | <arg name="parent_width" type="int" | |
| 392 | + | summary="future window geometry width of parent"/> | |
| 393 | + | <arg name="parent_height" type="int" | |
| 394 | + | summary="future window geometry height of parent"/> | |
| 395 | + | </request> | |
| 396 | + | ||
| 397 | + | <request name="set_parent_configure" since="3"> | |
| 398 | + | <description summary="set parent configure this is a response to"> | |
| 399 | + | Set the serial of an xdg_surface.configure event this positioner will be | |
| 400 | + | used in response to. The compositor may use this information together | |
| 401 | + | with set_parent_size to determine what future state the popup should be | |
| 402 | + | constrained using. | |
| 403 | + | </description> | |
| 404 | + | <arg name="serial" type="uint" | |
| 405 | + | summary="serial of parent configure event"/> | |
| 406 | + | </request> | |
| 407 | + | </interface> | |
| 408 | + | ||
| 409 | + | <interface name="xdg_surface" version="7"> | |
| 410 | + | <description summary="desktop user interface surface base interface"> | |
| 411 | + | An interface that may be implemented by a wl_surface, for | |
| 412 | + | implementations that provide a desktop-style user interface. | |
| 413 | + | ||
| 414 | + | It provides a base set of functionality required to construct user | |
| 415 | + | interface elements requiring management by the compositor, such as | |
| 416 | + | toplevel windows, menus, etc. The types of functionality are split into | |
| 417 | + | xdg_surface roles. | |
| 418 | + | ||
| 419 | + | Creating an xdg_surface does not set the role for a wl_surface. In order | |
| 420 | + | to map an xdg_surface, the client must create a role-specific object | |
| 421 | + | using, e.g., get_toplevel, get_popup. The wl_surface for any given | |
| 422 | + | xdg_surface can have at most one role, and may not be assigned any role | |
| 423 | + | not based on xdg_surface. | |
| 424 | + | ||
| 425 | + | A role must be assigned before any other requests are made to the | |
| 426 | + | xdg_surface object. | |
| 427 | + | ||
| 428 | + | The client must call wl_surface.commit on the corresponding wl_surface | |
| 429 | + | for the xdg_surface state to take effect. | |
| 430 | + | ||
| 431 | + | Creating an xdg_surface from a wl_surface which has a buffer attached or | |
| 432 | + | committed is a client error, and any attempts by a client to attach or | |
| 433 | + | manipulate a buffer prior to the first xdg_surface.configure call must | |
| 434 | + | also be treated as errors. | |
| 435 | + | ||
| 436 | + | After creating a role-specific object and setting it up (e.g. by sending | |
| 437 | + | the title, app ID, size constraints, parent, etc), the client must | |
| 438 | + | perform an initial commit without any buffer attached. The compositor | |
| 439 | + | will reply with initial wl_surface state such as | |
| 440 | + | wl_surface.preferred_buffer_scale followed by an xdg_surface.configure | |
| 441 | + | event. The client must acknowledge it and is then allowed to attach a | |
| 442 | + | buffer to map the surface. | |
| 443 | + | ||
| 444 | + | Mapping an xdg_surface-based role surface is defined as making it | |
| 445 | + | possible for the surface to be shown by the compositor. Note that | |
| 446 | + | a mapped surface is not guaranteed to be visible once it is mapped. | |
| 447 | + | ||
| 448 | + | For an xdg_surface to be mapped by the compositor, the following | |
| 449 | + | conditions must be met: | |
| 450 | + | (1) the client has assigned an xdg_surface-based role to the surface | |
| 451 | + | (2) the client has set and committed the xdg_surface state and the | |
| 452 | + | role-dependent state to the surface | |
| 453 | + | (3) the client has committed a buffer to the surface | |
| 454 | + | ||
| 455 | + | A newly-unmapped surface is considered to have met condition (1) out | |
| 456 | + | of the 3 required conditions for mapping a surface if its role surface | |
| 457 | + | has not been destroyed, i.e. the client must perform the initial commit | |
| 458 | + | again before attaching a buffer. | |
| 459 | + | </description> | |
| 460 | + | ||
| 461 | + | <enum name="error"> | |
| 462 | + | <entry name="not_constructed" value="1" | |
| 463 | + | summary="Surface was not fully constructed"/> | |
| 464 | + | <entry name="already_constructed" value="2" | |
| 465 | + | summary="Surface was already constructed"/> | |
| 466 | + | <entry name="unconfigured_buffer" value="3" | |
| 467 | + | summary="Attaching a buffer to an unconfigured surface"/> | |
| 468 | + | <entry name="invalid_serial" value="4" | |
| 469 | + | summary="Invalid serial number when acking a configure event"/> | |
| 470 | + | <entry name="invalid_size" value="5" | |
| 471 | + | summary="Width or height was zero or negative"/> | |
| 472 | + | <entry name="defunct_role_object" value="6" | |
| 473 | + | summary="Surface was destroyed before its role object"/> | |
| 474 | + | </enum> | |
| 475 | + | ||
| 476 | + | <request name="destroy" type="destructor"> | |
| 477 | + | <description summary="destroy the xdg_surface"> | |
| 478 | + | Destroy the xdg_surface object. An xdg_surface must only be destroyed | |
| 479 | + | after its role object has been destroyed, otherwise | |
| 480 | + | a defunct_role_object error is raised. | |
| 481 | + | </description> | |
| 482 | + | </request> | |
| 483 | + | ||
| 484 | + | <request name="get_toplevel"> | |
| 485 | + | <description summary="assign the xdg_toplevel surface role"> | |
| 486 | + | This creates an xdg_toplevel object for the given xdg_surface and gives | |
| 487 | + | the associated wl_surface the xdg_toplevel role. | |
| 488 | + | ||
| 489 | + | See the documentation of xdg_toplevel for more details about what an | |
| 490 | + | xdg_toplevel is and how it is used. | |
| 491 | + | </description> | |
| 492 | + | <arg name="id" type="new_id" interface="xdg_toplevel"/> | |
| 493 | + | </request> | |
| 494 | + | ||
| 495 | + | <request name="get_popup"> | |
| 496 | + | <description summary="assign the xdg_popup surface role"> | |
| 497 | + | This creates an xdg_popup object for the given xdg_surface and gives | |
| 498 | + | the associated wl_surface the xdg_popup role. | |
| 499 | + | ||
| 500 | + | If null is passed as a parent, a parent surface must be specified using | |
| 501 | + | some other protocol, before committing the initial state. | |
| 502 | + | ||
| 503 | + | See the documentation of xdg_popup for more details about what an | |
| 504 | + | xdg_popup is and how it is used. | |
| 505 | + | </description> | |
| 506 | + | <arg name="id" type="new_id" interface="xdg_popup"/> | |
| 507 | + | <arg name="parent" type="object" interface="xdg_surface" allow-null="true" summary="parent surface for this popup"/> | |
| 508 | + | <arg name="positioner" type="object" interface="xdg_positioner" summary="positioner for this popup"/> | |
| 509 | + | </request> | |
| 510 | + | ||
| 511 | + | <request name="set_window_geometry"> | |
| 512 | + | <description summary="set the new window geometry"> | |
| 513 | + | The window geometry of a surface is its "visible bounds" from the | |
| 514 | + | user's perspective. Client-side decorations often have invisible | |
| 515 | + | portions like drop-shadows which should be ignored for the | |
| 516 | + | purposes of aligning, placing and constraining windows. Note that | |
| 517 | + | in some situations, compositors may clip rendering to the window | |
| 518 | + | geometry, so the client should avoid putting functional elements | |
| 519 | + | outside of it. | |
| 520 | + | ||
| 521 | + | The window geometry is double-buffered state, see wl_surface.commit. | |
| 522 | + | ||
| 523 | + | When maintaining a position, the compositor should treat the (x, y) | |
| 524 | + | coordinate of the window geometry as the top left corner of the window. | |
| 525 | + | A client changing the (x, y) window geometry coordinate should in | |
| 526 | + | general not alter the position of the window. | |
| 527 | + | ||
| 528 | + | Once the window geometry of the surface is set, it is not possible to | |
| 529 | + | unset it, and it will remain the same until set_window_geometry is | |
| 530 | + | called again, even if a new subsurface or buffer is attached. | |
| 531 | + | ||
| 532 | + | If never set, the value is the full bounds of the surface, | |
| 533 | + | including any subsurfaces. This updates dynamically on every | |
| 534 | + | commit. This unset is meant for extremely simple clients. | |
| 535 | + | ||
| 536 | + | The arguments are given in the surface-local coordinate space of | |
| 537 | + | the wl_surface associated with this xdg_surface, and may extend outside | |
| 538 | + | of the wl_surface itself to mark parts of the subsurface tree as part of | |
| 539 | + | the window geometry. | |
| 540 | + | ||
| 541 | + | When applied, the effective window geometry will be the set window | |
| 542 | + | geometry clamped to the bounding rectangle of the combined | |
| 543 | + | geometry of the surface of the xdg_surface and the associated | |
| 544 | + | subsurfaces. | |
| 545 | + | ||
| 546 | + | The effective geometry will not be recalculated unless a new call to | |
| 547 | + | set_window_geometry is done and the new pending surface state is | |
| 548 | + | subsequently applied. | |
| 549 | + | ||
| 550 | + | The width and height of the effective window geometry must be | |
| 551 | + | greater than zero. Setting an invalid size will raise an | |
| 552 | + | invalid_size error. | |
| 553 | + | </description> | |
| 554 | + | <arg name="x" type="int" summary="x coordinate of the top-left corner of the window inside this surface"/> | |
| 555 | + | <arg name="y" type="int" summary="y coordinate of the top-left corner of the window inside this surface"/> | |
| 556 | + | <arg name="width" type="int" summary="width of the window"/> | |
| 557 | + | <arg name="height" type="int" summary="height of the window"/> | |
| 558 | + | </request> | |
| 559 | + | ||
| 560 | + | <request name="ack_configure"> | |
| 561 | + | <description summary="ack a configure event"> | |
| 562 | + | When a configure event is received, if a client commits the | |
| 563 | + | surface in response to the configure event, then the client | |
| 564 | + | must make an ack_configure request sometime before the commit | |
| 565 | + | request, passing along the serial of the configure event. | |
| 566 | + | ||
| 567 | + | For instance, for toplevel surfaces the compositor might use this | |
| 568 | + | information to move a surface to the top left only when the client has | |
| 569 | + | drawn itself for the maximized or fullscreen state. | |
| 570 | + | ||
| 571 | + | If the client receives multiple configure events before it | |
| 572 | + | can respond to one, it only has to ack the last configure event. | |
| 573 | + | Acking a configure event that was never sent raises an invalid_serial | |
| 574 | + | error. | |
| 575 | + | ||
| 576 | + | A client is not required to commit immediately after sending | |
| 577 | + | an ack_configure request - it may even ack_configure several times | |
| 578 | + | before its next surface commit. | |
| 579 | + | ||
| 580 | + | A client may send multiple ack_configure requests before committing, but | |
| 581 | + | only the last request sent before a commit indicates which configure | |
| 582 | + | event the client really is responding to. | |
| 583 | + | ||
| 584 | + | Sending an ack_configure request consumes the serial number sent with | |
| 585 | + | the request, as well as serial numbers sent by all configure events | |
| 586 | + | sent on this xdg_surface prior to the configure event referenced by | |
| 587 | + | the committed serial. | |
| 588 | + | ||
| 589 | + | It is an error to issue multiple ack_configure requests referencing a | |
| 590 | + | serial from the same configure event, or to issue an ack_configure | |
| 591 | + | request referencing a serial from a configure event issued before the | |
| 592 | + | event identified by the last ack_configure request for the same | |
| 593 | + | xdg_surface. Doing so will raise an invalid_serial error. | |
| 594 | + | </description> | |
| 595 | + | <arg name="serial" type="uint" summary="the serial from the configure event"/> | |
| 596 | + | </request> | |
| 597 | + | ||
| 598 | + | <event name="configure"> | |
| 599 | + | <description summary="suggest a surface change"> | |
| 600 | + | The configure event marks the end of a configure sequence. A configure | |
| 601 | + | sequence is a set of one or more events configuring the state of the | |
| 602 | + | xdg_surface, including the final xdg_surface.configure event. | |
| 603 | + | ||
| 604 | + | Where applicable, xdg_surface surface roles will during a configure | |
| 605 | + | sequence extend this event as a latched state sent as events before the | |
| 606 | + | xdg_surface.configure event. Such events should be considered to make up | |
| 607 | + | a set of atomically applied configuration states, where the | |
| 608 | + | xdg_surface.configure commits the accumulated state. | |
| 609 | + | ||
| 610 | + | Clients should arrange their surface for the new states, and then send | |
| 611 | + | an ack_configure request with the serial sent in this configure event at | |
| 612 | + | some point before committing the new surface. | |
| 613 | + | ||
| 614 | + | If the client receives multiple configure events before it can respond | |
| 615 | + | to one, it is free to discard all but the last event it received. | |
| 616 | + | </description> | |
| 617 | + | <arg name="serial" type="uint" summary="serial of the configure event"/> | |
| 618 | + | </event> | |
| 619 | + | ||
| 620 | + | </interface> | |
| 621 | + | ||
| 622 | + | <interface name="xdg_toplevel" version="7"> | |
| 623 | + | <description summary="toplevel surface"> | |
| 624 | + | This interface defines an xdg_surface role which allows a surface to, | |
| 625 | + | among other things, set window-like properties such as maximize, | |
| 626 | + | fullscreen, and minimize, set application-specific metadata like title and | |
| 627 | + | id, and well as trigger user interactive operations such as interactive | |
| 628 | + | resize and move. | |
| 629 | + | ||
| 630 | + | An xdg_toplevel by default is responsible for providing the full intended | |
| 631 | + | visual representation of the toplevel, which depending on the window | |
| 632 | + | state, may mean things like a title bar, window controls and drop shadow. | |
| 633 | + | ||
| 634 | + | Unmapping an xdg_toplevel means that the surface cannot be shown | |
| 635 | + | by the compositor until it is explicitly mapped again. | |
| 636 | + | All active operations (e.g., move, resize) are canceled and all | |
| 637 | + | attributes (e.g. title, state, stacking, ...) are discarded for | |
| 638 | + | an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to | |
| 639 | + | the state it had right after xdg_surface.get_toplevel. The client | |
| 640 | + | can re-map the toplevel by performing a commit without any buffer | |
| 641 | + | attached, waiting for a configure event and handling it as usual (see | |
| 642 | + | xdg_surface description). | |
| 643 | + | ||
| 644 | + | Attaching a null buffer to a toplevel unmaps the surface. | |
| 645 | + | </description> | |
| 646 | + | ||
| 647 | + | <request name="destroy" type="destructor"> | |
| 648 | + | <description summary="destroy the xdg_toplevel"> | |
| 649 | + | This request destroys the role surface and unmaps the surface; | |
| 650 | + | see "Unmapping" behavior in interface section for details. | |
| 651 | + | </description> | |
| 652 | + | </request> | |
| 653 | + | ||
| 654 | + | <enum name="error"> | |
| 655 | + | <entry name="invalid_resize_edge" value="0" summary="provided value is | |
| 656 | + | not a valid variant of the resize_edge enum"/> | |
| 657 | + | <entry name="invalid_parent" value="1" | |
| 658 | + | summary="invalid parent toplevel"/> | |
| 659 | + | <entry name="invalid_size" value="2" | |
| 660 | + | summary="client provided an invalid min or max size"/> | |
| 661 | + | </enum> | |
| 662 | + | ||
| 663 | + | <request name="set_parent"> | |
| 664 | + | <description summary="set the parent of this surface"> | |
| 665 | + | Set the "parent" of this surface. This surface should be stacked | |
| 666 | + | above the parent surface and all other ancestor surfaces. | |
| 667 | + | ||
| 668 | + | Parent surfaces should be set on dialogs, toolboxes, or other | |
| 669 | + | "auxiliary" surfaces, so that the parent is raised when the dialog | |
| 670 | + | is raised. | |
| 671 | + | ||
| 672 | + | Setting a null parent for a child surface unsets its parent. Setting | |
| 673 | + | a null parent for a surface which currently has no parent is a no-op. | |
| 674 | + | ||
| 675 | + | Only mapped surfaces can have child surfaces. Setting a parent which | |
| 676 | + | is not mapped is equivalent to setting a null parent. If a surface | |
| 677 | + | becomes unmapped, its children's parent is set to the parent of | |
| 678 | + | the now-unmapped surface. If the now-unmapped surface has no parent, | |
| 679 | + | its children's parent is unset. If the now-unmapped surface becomes | |
| 680 | + | mapped again, its parent-child relationship is not restored. | |
| 681 | + | ||
| 682 | + | The parent toplevel must not be one of the child toplevel's | |
| 683 | + | descendants, and the parent must be different from the child toplevel, | |
| 684 | + | otherwise the invalid_parent protocol error is raised. | |
| 685 | + | </description> | |
| 686 | + | <arg name="parent" type="object" interface="xdg_toplevel" allow-null="true" summary="parent surface for this surface"/> | |
| 687 | + | </request> | |
| 688 | + | ||
| 689 | + | <request name="set_title"> | |
| 690 | + | <description summary="set surface title"> | |
| 691 | + | Set a short title for the surface. | |
| 692 | + | ||
| 693 | + | This string may be used to identify the surface in a task bar, | |
| 694 | + | window list, or other user interface elements provided by the | |
| 695 | + | compositor. | |
| 696 | + | ||
| 697 | + | The string must be encoded in UTF-8. | |
| 698 | + | </description> | |
| 699 | + | <arg name="title" type="string" summary="title of the surface"/> | |
| 700 | + | </request> | |
| 701 | + | ||
| 702 | + | <request name="set_app_id"> | |
| 703 | + | <description summary="set application ID"> | |
| 704 | + | Set an application identifier for the surface. | |
| 705 | + | ||
| 706 | + | The app ID identifies the general class of applications to which | |
| 707 | + | the surface belongs. The compositor can use this to group multiple | |
| 708 | + | surfaces together, or to determine how to launch a new application. | |
| 709 | + | ||
| 710 | + | For D-Bus activatable applications, the app ID is used as the D-Bus | |
| 711 | + | service name. | |
| 712 | + | ||
| 713 | + | The compositor shell will try to group application surfaces together | |
| 714 | + | by their app ID. As a best practice, it is suggested to select app | |
| 715 | + | ID's that match the basename of the application's .desktop file. | |
| 716 | + | For example, "org.freedesktop.FooViewer" where the .desktop file is | |
| 717 | + | "org.freedesktop.FooViewer.desktop". | |
| 718 | + | ||
| 719 | + | Like other properties, a set_app_id request can be sent after the | |
| 720 | + | xdg_toplevel has been mapped to update the property. | |
| 721 | + | ||
| 722 | + | See the desktop-entry specification [0] for more details on | |
| 723 | + | application identifiers and how they relate to well-known D-Bus | |
| 724 | + | names and .desktop files. | |
| 725 | + | ||
| 726 | + | [0] https://standards.freedesktop.org/desktop-entry-spec/ | |
| 727 | + | </description> | |
| 728 | + | <arg name="app_id" type="string" summary="application identifier surface belongs to"/> | |
| 729 | + | </request> | |
| 730 | + | ||
| 731 | + | <request name="show_window_menu"> | |
| 732 | + | <description summary="show the window menu"> | |
| 733 | + | Clients implementing client-side decorations might want to show | |
| 734 | + | a context menu when right-clicking on the decorations, giving the | |
| 735 | + | user a menu that they can use to maximize or minimize the window. | |
| 736 | + | ||
| 737 | + | This request asks the compositor to pop up such a window menu at | |
| 738 | + | the given position, relative to the local surface coordinates of | |
| 739 | + | the parent surface. There are no guarantees as to what menu items | |
| 740 | + | the window menu contains, or even if a window menu will be drawn | |
| 741 | + | at all. | |
| 742 | + | ||
| 743 | + | This request must be used in response to some sort of user action | |
| 744 | + | like a button press, key press, or touch down event. | |
| 745 | + | </description> | |
| 746 | + | <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat of the user event"/> | |
| 747 | + | <arg name="serial" type="uint" summary="the serial of the user event"/> | |
| 748 | + | <arg name="x" type="int" summary="the x position to pop up the window menu at"/> | |
| 749 | + | <arg name="y" type="int" summary="the y position to pop up the window menu at"/> | |
| 750 | + | </request> | |
| 751 | + | ||
| 752 | + | <request name="move"> | |
| 753 | + | <description summary="start an interactive move"> | |
| 754 | + | Start an interactive, user-driven move of the surface. | |
| 755 | + | ||
| 756 | + | This request must be used in response to some sort of user action | |
| 757 | + | like a button press, key press, or touch down event. The passed | |
| 758 | + | serial is used to determine the type of interactive move (touch, | |
| 759 | + | pointer, etc). | |
| 760 | + | ||
| 761 | + | The server may ignore move requests depending on the state of | |
| 762 | + | the surface (e.g. fullscreen or maximized), or if the passed serial | |
| 763 | + | is no longer valid. | |
| 764 | + | ||
| 765 | + | If triggered, the surface will lose the focus of the device | |
| 766 | + | (wl_pointer, wl_touch, etc) used for the move. It is up to the | |
| 767 | + | compositor to visually indicate that the move is taking place, such as | |
| 768 | + | updating a pointer cursor, during the move. There is no guarantee | |
| 769 | + | that the device focus will return when the move is completed. | |
| 770 | + | </description> | |
| 771 | + | <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat of the user event"/> | |
| 772 | + | <arg name="serial" type="uint" summary="the serial of the user event"/> | |
| 773 | + | </request> | |
| 774 | + | ||
| 775 | + | <enum name="resize_edge"> | |
| 776 | + | <description summary="edge values for resizing"> | |
| 777 | + | These values are used to indicate which edge of a surface | |
| 778 | + | is being dragged in a resize operation. | |
| 779 | + | </description> | |
| 780 | + | <entry name="none" value="0"/> | |
| 781 | + | <entry name="top" value="1"/> | |
| 782 | + | <entry name="bottom" value="2"/> | |
| 783 | + | <entry name="left" value="4"/> | |
| 784 | + | <entry name="top_left" value="5"/> | |
| 785 | + | <entry name="bottom_left" value="6"/> | |
| 786 | + | <entry name="right" value="8"/> | |
| 787 | + | <entry name="top_right" value="9"/> | |
| 788 | + | <entry name="bottom_right" value="10"/> | |
| 789 | + | </enum> | |
| 790 | + | ||
| 791 | + | <request name="resize"> | |
| 792 | + | <description summary="start an interactive resize"> | |
| 793 | + | Start a user-driven, interactive resize of the surface. | |
| 794 | + | ||
| 795 | + | This request must be used in response to some sort of user action | |
| 796 | + | like a button press, key press, or touch down event. The passed | |
| 797 | + | serial is used to determine the type of interactive resize (touch, | |
| 798 | + | pointer, etc). | |
| 799 | + | ||
| 800 | + | The server may ignore resize requests depending on the state of | |
| 801 | + | the surface (e.g. fullscreen or maximized). | |
| 802 | + | ||
| 803 | + | If triggered, the client will receive configure events with the | |
| 804 | + | "resize" state enum value and the expected sizes. See the "resize" | |
| 805 | + | enum value for more details about what is required. The client | |
| 806 | + | must also acknowledge configure events using "ack_configure". After | |
| 807 | + | the resize is completed, the client will receive another "configure" | |
| 808 | + | event without the resize state. | |
| 809 | + | ||
| 810 | + | If triggered, the surface also will lose the focus of the device | |
| 811 | + | (wl_pointer, wl_touch, etc) used for the resize. It is up to the | |
| 812 | + | compositor to visually indicate that the resize is taking place, | |
| 813 | + | such as updating a pointer cursor, during the resize. There is no | |
| 814 | + | guarantee that the device focus will return when the resize is | |
| 815 | + | completed. | |
| 816 | + | ||
| 817 | + | The edges parameter specifies how the surface should be resized, and | |
| 818 | + | is one of the values of the resize_edge enum. Values not matching | |
| 819 | + | a variant of the enum will cause the invalid_resize_edge protocol error. | |
| 820 | + | The compositor may use this information to update the surface position | |
| 821 | + | for example when dragging the top left corner. The compositor may also | |
| 822 | + | use this information to adapt its behavior, e.g. choose an appropriate | |
| 823 | + | cursor image. | |
| 824 | + | </description> | |
| 825 | + | <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat of the user event"/> | |
| 826 | + | <arg name="serial" type="uint" summary="the serial of the user event"/> | |
| 827 | + | <arg name="edges" type="uint" enum="resize_edge" summary="which edge or corner is being dragged"/> | |
| 828 | + | </request> | |
| 829 | + | ||
| 830 | + | <enum name="state"> | |
| 831 | + | <description summary="types of state on the surface"> | |
| 832 | + | The different state values used on the surface. This is designed for | |
| 833 | + | state values like maximized, fullscreen. It is paired with the | |
| 834 | + | configure event to ensure that both the client and the compositor | |
| 835 | + | setting the state can be synchronized. | |
| 836 | + | ||
| 837 | + | States set in this way are double-buffered, see wl_surface.commit. | |
| 838 | + | </description> | |
| 839 | + | <entry name="maximized" value="1" summary="the surface is maximized"> | |
| 840 | + | <description summary="the surface is maximized"> | |
| 841 | + | The surface is maximized. The window geometry specified in the configure | |
| 842 | + | event must be obeyed by the client, or the xdg_wm_base.invalid_surface_state | |
| 843 | + | error is raised. | |
| 844 | + | ||
| 845 | + | The client should draw without shadow or other | |
| 846 | + | decoration outside of the window geometry. | |
| 847 | + | </description> | |
| 848 | + | </entry> | |
| 849 | + | <entry name="fullscreen" value="2" summary="the surface is fullscreen"> | |
| 850 | + | <description summary="the surface is fullscreen"> | |
| 851 | + | The surface is fullscreen. The window geometry specified in the | |
| 852 | + | configure event is a maximum; the client cannot resize beyond it. For | |
| 853 | + | a surface to cover the whole fullscreened area, the geometry | |
| 854 | + | dimensions must be obeyed by the client. For more details, see | |
| 855 | + | xdg_toplevel.set_fullscreen. | |
| 856 | + | </description> | |
| 857 | + | </entry> | |
| 858 | + | <entry name="resizing" value="3" summary="the surface is being resized"> | |
| 859 | + | <description summary="the surface is being resized"> | |
| 860 | + | The surface is being resized. The window geometry specified in the | |
| 861 | + | configure event is a maximum; the client cannot resize beyond it. | |
| 862 | + | Clients that have aspect ratio or cell sizing configuration can use | |
| 863 | + | a smaller size, however. | |
| 864 | + | </description> | |
| 865 | + | </entry> | |
| 866 | + | <entry name="activated" value="4" summary="the surface is now activated"> | |
| 867 | + | <description summary="the surface is now activated"> | |
| 868 | + | Client window decorations should be painted as if the window is | |
| 869 | + | active. Do not assume this means that the window actually has | |
| 870 | + | keyboard or pointer focus. | |
| 871 | + | </description> | |
| 872 | + | </entry> | |
| 873 | + | <entry name="tiled_left" value="5" since="2"> | |
| 874 | + | <description summary="the surface’s left edge is tiled"> | |
| 875 | + | The window is currently in a tiled layout and the left edge is | |
| 876 | + | considered to be adjacent to another part of the tiling grid. | |
| 877 | + | ||
| 878 | + | The client should draw without shadow or other decoration outside of | |
| 879 | + | the window geometry on the left edge. | |
| 880 | + | </description> | |
| 881 | + | </entry> | |
| 882 | + | <entry name="tiled_right" value="6" since="2"> | |
| 883 | + | <description summary="the surface’s right edge is tiled"> | |
| 884 | + | The window is currently in a tiled layout and the right edge is | |
| 885 | + | considered to be adjacent to another part of the tiling grid. | |
| 886 | + | ||
| 887 | + | The client should draw without shadow or other decoration outside of | |
| 888 | + | the window geometry on the right edge. | |
| 889 | + | </description> | |
| 890 | + | </entry> | |
| 891 | + | <entry name="tiled_top" value="7" since="2"> | |
| 892 | + | <description summary="the surface’s top edge is tiled"> | |
| 893 | + | The window is currently in a tiled layout and the top edge is | |
| 894 | + | considered to be adjacent to another part of the tiling grid. | |
| 895 | + | ||
| 896 | + | The client should draw without shadow or other decoration outside of | |
| 897 | + | the window geometry on the top edge. | |
| 898 | + | </description> | |
| 899 | + | </entry> | |
| 900 | + | <entry name="tiled_bottom" value="8" since="2"> | |
| 901 | + | <description summary="the surface’s bottom edge is tiled"> | |
| 902 | + | The window is currently in a tiled layout and the bottom edge is | |
| 903 | + | considered to be adjacent to another part of the tiling grid. | |
| 904 | + | ||
| 905 | + | The client should draw without shadow or other decoration outside of | |
| 906 | + | the window geometry on the bottom edge. | |
| 907 | + | </description> | |
| 908 | + | </entry> | |
| 909 | + | <entry name="suspended" value="9" since="6"> | |
| 910 | + | <description summary="surface repaint is suspended"> | |
| 911 | + | The surface is currently not ordinarily being repainted; for | |
| 912 | + | example because its content is occluded by another window, or its | |
| 913 | + | outputs are switched off due to screen locking. | |
| 914 | + | </description> | |
| 915 | + | </entry> | |
| 916 | + | <entry name="constrained_left" value="10" since="7"> | |
| 917 | + | <description summary="the surface’s left edge is constrained"> | |
| 918 | + | The left edge of the window is currently constrained, meaning it | |
| 919 | + | shouldn't attempt to resize from that edge. It can for example mean | |
| 920 | + | it's tiled next to a monitor edge on the constrained side of the | |
| 921 | + | window. | |
| 922 | + | </description> | |
| 923 | + | </entry> | |
| 924 | + | <entry name="constrained_right" value="11" since="7"> | |
| 925 | + | <description summary="the surface’s right edge is constrained"> | |
| 926 | + | The right edge of the window is currently constrained, meaning it | |
| 927 | + | shouldn't attempt to resize from that edge. It can for example mean | |
| 928 | + | it's tiled next to a monitor edge on the constrained side of the | |
| 929 | + | window. | |
| 930 | + | </description> | |
| 931 | + | </entry> | |
| 932 | + | <entry name="constrained_top" value="12" since="7"> | |
| 933 | + | <description summary="the surface’s top edge is constrained"> | |
| 934 | + | The top edge of the window is currently constrained, meaning it | |
| 935 | + | shouldn't attempt to resize from that edge. It can for example mean | |
| 936 | + | it's tiled next to a monitor edge on the constrained side of the | |
| 937 | + | window. | |
| 938 | + | </description> | |
| 939 | + | </entry> | |
| 940 | + | <entry name="constrained_bottom" value="13" since="7"> | |
| 941 | + | <description summary="the surface’s bottom edge is constrained"> | |
| 942 | + | The bottom edge of the window is currently constrained, meaning it | |
| 943 | + | shouldn't attempt to resize from that edge. It can for example mean | |
| 944 | + | it's tiled next to a monitor edge on the constrained side of the | |
| 945 | + | window. | |
| 946 | + | </description> | |
| 947 | + | </entry> | |
| 948 | + | </enum> | |
| 949 | + | ||
| 950 | + | <request name="set_max_size"> | |
| 951 | + | <description summary="set the maximum size"> | |
| 952 | + | Set a maximum size for the window. | |
| 953 | + | ||
| 954 | + | The client can specify a maximum size so that the compositor does | |
| 955 | + | not try to configure the window beyond this size. | |
| 956 | + | ||
| 957 | + | The width and height arguments are in window geometry coordinates. | |
| 958 | + | See xdg_surface.set_window_geometry. | |
| 959 | + | ||
| 960 | + | Values set in this way are double-buffered, see wl_surface.commit. | |
| 961 | + | ||
| 962 | + | The compositor can use this information to allow or disallow | |
| 963 | + | different states like maximize or fullscreen and draw accurate | |
| 964 | + | animations. | |
| 965 | + | ||
| 966 | + | Similarly, a tiling window manager may use this information to | |
| 967 | + | place and resize client windows in a more effective way. | |
| 968 | + | ||
| 969 | + | The client should not rely on the compositor to obey the maximum | |
| 970 | + | size. The compositor may decide to ignore the values set by the | |
| 971 | + | client and request a larger size. | |
| 972 | + | ||
| 973 | + | If never set, or a value of zero in the request, means that the | |
| 974 | + | client has no expected maximum size in the given dimension. | |
| 975 | + | As a result, a client wishing to reset the maximum size | |
| 976 | + | to an unspecified state can use zero for width and height in the | |
| 977 | + | request. | |
| 978 | + | ||
| 979 | + | Requesting a maximum size to be smaller than the minimum size of | |
| 980 | + | a surface is illegal and will result in an invalid_size error. | |
| 981 | + | ||
| 982 | + | The width and height must be greater than or equal to zero. Using | |
| 983 | + | strictly negative values for width or height will result in an | |
| 984 | + | invalid_size error. | |
| 985 | + | </description> | |
| 986 | + | <arg name="width" type="int" summary="maximum width of the window"/> | |
| 987 | + | <arg name="height" type="int" summary="maximum height of the window"/> | |
| 988 | + | </request> | |
| 989 | + | ||
| 990 | + | <request name="set_min_size"> | |
| 991 | + | <description summary="set the minimum size"> | |
| 992 | + | Set a minimum size for the window. | |
| 993 | + | ||
| 994 | + | The client can specify a minimum size so that the compositor does | |
| 995 | + | not try to configure the window below this size. | |
| 996 | + | ||
| 997 | + | The width and height arguments are in window geometry coordinates. | |
| 998 | + | See xdg_surface.set_window_geometry. | |
| 999 | + | ||
| 1000 | + | Values set in this way are double-buffered, see wl_surface.commit. | |
| 1001 | + | ||
| 1002 | + | The compositor can use this information to allow or disallow | |
| 1003 | + | different states like maximize or fullscreen and draw accurate | |
| 1004 | + | animations. | |
| 1005 | + | ||
| 1006 | + | Similarly, a tiling window manager may use this information to | |
| 1007 | + | place and resize client windows in a more effective way. | |
| 1008 | + | ||
| 1009 | + | The client should not rely on the compositor to obey the minimum | |
| 1010 | + | size. The compositor may decide to ignore the values set by the | |
| 1011 | + | client and request a smaller size. | |
| 1012 | + | ||
| 1013 | + | If never set, or a value of zero in the request, means that the | |
| 1014 | + | client has no expected minimum size in the given dimension. | |
| 1015 | + | As a result, a client wishing to reset the minimum size | |
| 1016 | + | to an unspecified state can use zero for width and height in the | |
| 1017 | + | request. | |
| 1018 | + | ||
| 1019 | + | Requesting a minimum size to be larger than the maximum size of | |
| 1020 | + | a surface is illegal and will result in an invalid_size error. | |
| 1021 | + | ||
| 1022 | + | The width and height must be greater than or equal to zero. Using | |
| 1023 | + | strictly negative values for width and height will result in an | |
| 1024 | + | invalid_size error. | |
| 1025 | + | </description> | |
| 1026 | + | <arg name="width" type="int" summary="minimum width of the window"/> | |
| 1027 | + | <arg name="height" type="int" summary="minimum height of the window"/> | |
| 1028 | + | </request> | |
| 1029 | + | ||
| 1030 | + | <request name="set_maximized"> | |
| 1031 | + | <description summary="maximize the window"> | |
| 1032 | + | Maximize the surface. | |
| 1033 | + | ||
| 1034 | + | After requesting that the surface should be maximized, the compositor | |
| 1035 | + | will respond by emitting a configure event. Whether this configure | |
| 1036 | + | actually sets the window maximized is subject to compositor policies. | |
| 1037 | + | The client must then update its content, drawing in the configured | |
| 1038 | + | state. The client must also acknowledge the configure when committing | |
| 1039 | + | the new content (see ack_configure). | |
| 1040 | + | ||
| 1041 | + | It is up to the compositor to decide how and where to maximize the | |
| 1042 | + | surface, for example which output and what region of the screen should | |
| 1043 | + | be used. | |
| 1044 | + | ||
| 1045 | + | If the surface was already maximized, the compositor will still emit | |
| 1046 | + | a configure event with the "maximized" state. | |
| 1047 | + | ||
| 1048 | + | If the surface is in a fullscreen state, this request has no direct | |
| 1049 | + | effect. It may alter the state the surface is returned to when | |
| 1050 | + | unmaximized unless overridden by the compositor. | |
| 1051 | + | </description> | |
| 1052 | + | </request> | |
| 1053 | + | ||
| 1054 | + | <request name="unset_maximized"> | |
| 1055 | + | <description summary="unmaximize the window"> | |
| 1056 | + | Unmaximize the surface. | |
| 1057 | + | ||
| 1058 | + | After requesting that the surface should be unmaximized, the compositor | |
| 1059 | + | will respond by emitting a configure event. Whether this actually | |
| 1060 | + | un-maximizes the window is subject to compositor policies. | |
| 1061 | + | If available and applicable, the compositor will include the window | |
| 1062 | + | geometry dimensions the window had prior to being maximized in the | |
| 1063 | + | configure event. The client must then update its content, drawing it in | |
| 1064 | + | the configured state. The client must also acknowledge the configure | |
| 1065 | + | when committing the new content (see ack_configure). | |
| 1066 | + | ||
| 1067 | + | It is up to the compositor to position the surface after it was | |
| 1068 | + | unmaximized; usually the position the surface had before maximizing, if | |
| 1069 | + | applicable. | |
| 1070 | + | ||
| 1071 | + | If the surface was already not maximized, the compositor will still | |
| 1072 | + | emit a configure event without the "maximized" state. | |
| 1073 | + | ||
| 1074 | + | If the surface is in a fullscreen state, this request has no direct | |
| 1075 | + | effect. It may alter the state the surface is returned to when | |
| 1076 | + | unmaximized unless overridden by the compositor. | |
| 1077 | + | </description> | |
| 1078 | + | </request> | |
| 1079 | + | ||
| 1080 | + | <request name="set_fullscreen"> | |
| 1081 | + | <description summary="set the window as fullscreen on an output"> | |
| 1082 | + | Make the surface fullscreen. | |
| 1083 | + | ||
| 1084 | + | After requesting that the surface should be fullscreened, the | |
| 1085 | + | compositor will respond by emitting a configure event. Whether the | |
| 1086 | + | client is actually put into a fullscreen state is subject to compositor | |
| 1087 | + | policies. The client must also acknowledge the configure when | |
| 1088 | + | committing the new content (see ack_configure). | |
| 1089 | + | ||
| 1090 | + | The output passed by the request indicates the client's preference as | |
| 1091 | + | to which display it should be set fullscreen on. If this value is NULL, | |
| 1092 | + | it's up to the compositor to choose which display will be used to map | |
| 1093 | + | this surface. | |
| 1094 | + | ||
| 1095 | + | If the surface doesn't cover the whole output, the compositor will | |
| 1096 | + | position the surface in the center of the output and compensate with | |
| 1097 | + | border fill covering the rest of the output. The content of the | |
| 1098 | + | border fill is undefined, but should be assumed to be in some way that | |
| 1099 | + | attempts to blend into the surrounding area (e.g. solid black). | |
| 1100 | + | ||
| 1101 | + | If the fullscreened surface is not opaque, the compositor must make | |
| 1102 | + | sure that other screen content not part of the same surface tree (made | |
| 1103 | + | up of subsurfaces, popups or similarly coupled surfaces) are not | |
| 1104 | + | visible below the fullscreened surface. | |
| 1105 | + | </description> | |
| 1106 | + | <arg name="output" type="object" interface="wl_output" allow-null="true" summary="preferred output to place surface on"/> | |
| 1107 | + | </request> | |
| 1108 | + | ||
| 1109 | + | <request name="unset_fullscreen"> | |
| 1110 | + | <description summary="unset the window as fullscreen"> | |
| 1111 | + | Make the surface no longer fullscreen. | |
| 1112 | + | ||
| 1113 | + | After requesting that the surface should be unfullscreened, the | |
| 1114 | + | compositor will respond by emitting a configure event. | |
| 1115 | + | Whether this actually removes the fullscreen state of the client is | |
| 1116 | + | subject to compositor policies. | |
| 1117 | + | ||
| 1118 | + | Making a surface unfullscreen sets states for the surface based on the following: | |
| 1119 | + | * the state(s) it may have had before becoming fullscreen | |
| 1120 | + | * any state(s) decided by the compositor | |
| 1121 | + | * any state(s) requested by the client while the surface was fullscreen | |
| 1122 | + | ||
| 1123 | + | The compositor may include the previous window geometry dimensions in | |
| 1124 | + | the configure event, if applicable. | |
| 1125 | + | ||
| 1126 | + | The client must also acknowledge the configure when committing the new | |
| 1127 | + | content (see ack_configure). | |
| 1128 | + | </description> | |
| 1129 | + | </request> | |
| 1130 | + | ||
| 1131 | + | <request name="set_minimized"> | |
| 1132 | + | <description summary="set the window as minimized"> | |
| 1133 | + | Request that the compositor minimize your surface. There is no | |
| 1134 | + | way to know if the surface is currently minimized, nor is there | |
| 1135 | + | any way to unset minimization on this surface. | |
| 1136 | + | ||
| 1137 | + | If you are looking to throttle redrawing when minimized, please | |
| 1138 | + | instead use the wl_surface.frame event for this, as this will | |
| 1139 | + | also work with live previews on windows in Alt-Tab, Expose or | |
| 1140 | + | similar compositor features. | |
| 1141 | + | </description> | |
| 1142 | + | </request> | |
| 1143 | + | ||
| 1144 | + | <event name="configure"> | |
| 1145 | + | <description summary="suggest a surface change"> | |
| 1146 | + | This configure event asks the client to resize its toplevel surface or | |
| 1147 | + | to change its state. The configured state should not be applied | |
| 1148 | + | immediately. See xdg_surface.configure for details. | |
| 1149 | + | ||
| 1150 | + | The width and height arguments specify a hint to the window | |
| 1151 | + | about how its surface should be resized in window geometry | |
| 1152 | + | coordinates. See set_window_geometry. | |
| 1153 | + | ||
| 1154 | + | If the width or height arguments are zero, it means the client | |
| 1155 | + | should decide its own window dimension. This may happen when the | |
| 1156 | + | compositor needs to configure the state of the surface but doesn't | |
| 1157 | + | have any information about any previous or expected dimension. | |
| 1158 | + | ||
| 1159 | + | The states listed in the event specify how the width/height | |
| 1160 | + | arguments should be interpreted, and possibly how it should be | |
| 1161 | + | drawn. | |
| 1162 | + | ||
| 1163 | + | The states are sent as an array of 32-bit unsigned integers in | |
| 1164 | + | native endianness. State values are defined in the state enum. | |
| 1165 | + | ||
| 1166 | + | Clients must send an ack_configure in response to this event. See | |
| 1167 | + | xdg_surface.configure and xdg_surface.ack_configure for details. | |
| 1168 | + | </description> | |
| 1169 | + | <arg name="width" type="int" summary="suggested width of window"/> | |
| 1170 | + | <arg name="height" type="int" summary="suggested height of window"/> | |
| 1171 | + | <arg name="states" type="array" summary="suggested states of the window"/> | |
| 1172 | + | </event> | |
| 1173 | + | ||
| 1174 | + | <event name="close"> | |
| 1175 | + | <description summary="surface wants to be closed"> | |
| 1176 | + | The close event is sent by the compositor when the user | |
| 1177 | + | wants the surface to be closed. This should be equivalent to | |
| 1178 | + | the user clicking the close button in client-side decorations, | |
| 1179 | + | if your application has any. | |
| 1180 | + | ||
| 1181 | + | This is only a request that the user intends to close the | |
| 1182 | + | window. The client may choose to ignore this request, or show | |
| 1183 | + | a dialog to ask the user to save their data, etc. | |
| 1184 | + | </description> | |
| 1185 | + | </event> | |
| 1186 | + | ||
| 1187 | + | <!-- Version 4 additions --> | |
| 1188 | + | ||
| 1189 | + | <event name="configure_bounds" since="4"> | |
| 1190 | + | <description summary="recommended window geometry bounds"> | |
| 1191 | + | The configure_bounds event may be sent prior to a xdg_toplevel.configure | |
| 1192 | + | event to communicate the bounds a window geometry size is recommended | |
| 1193 | + | to constrain to. | |
| 1194 | + | ||
| 1195 | + | The passed width and height are in surface coordinate space. If width | |
| 1196 | + | and height are 0, it means bounds is unknown and equivalent to as if no | |
| 1197 | + | configure_bounds event was ever sent for this surface. | |
| 1198 | + | ||
| 1199 | + | The bounds can for example correspond to the size of a monitor excluding | |
| 1200 | + | any panels or other shell components, so that a surface isn't created in | |
| 1201 | + | a way that it cannot fit. | |
| 1202 | + | ||
| 1203 | + | The bounds may change at any point, and in such a case, a new | |
| 1204 | + | xdg_toplevel.configure_bounds will be sent, followed by | |
| 1205 | + | xdg_toplevel.configure and xdg_surface.configure. | |
| 1206 | + | </description> | |
| 1207 | + | <arg name="width" type="int" summary="suggested maximum width of surface"/> | |
| 1208 | + | <arg name="height" type="int" summary="suggested maximum height of surface"/> | |
| 1209 | + | </event> | |
| 1210 | + | ||
| 1211 | + | <!-- Version 5 additions --> | |
| 1212 | + | ||
| 1213 | + | <enum name="wm_capabilities" since="5"> | |
| 1214 | + | <entry name="window_menu" value="1" summary="show_window_menu is available"/> | |
| 1215 | + | <entry name="maximize" value="2" summary="set_maximized and unset_maximized are available"/> | |
| 1216 | + | <entry name="fullscreen" value="3" summary="set_fullscreen and unset_fullscreen are available"/> | |
| 1217 | + | <entry name="minimize" value="4" summary="set_minimized is available"/> | |
| 1218 | + | </enum> | |
| 1219 | + | ||
| 1220 | + | <event name="wm_capabilities" since="5"> | |
| 1221 | + | <description summary="compositor capabilities"> | |
| 1222 | + | This event advertises the capabilities supported by the compositor. If | |
| 1223 | + | a capability isn't supported, clients should hide or disable the UI | |
| 1224 | + | elements that expose this functionality. For instance, if the | |
| 1225 | + | compositor doesn't advertise support for minimized toplevels, a button | |
| 1226 | + | triggering the set_minimized request should not be displayed. | |
| 1227 | + | ||
| 1228 | + | The compositor will ignore requests it doesn't support. For instance, | |
| 1229 | + | a compositor which doesn't advertise support for minimized will ignore | |
| 1230 | + | set_minimized requests. | |
| 1231 | + | ||
| 1232 | + | Compositors must send this event once before the first | |
| 1233 | + | xdg_surface.configure event. When the capabilities change, compositors | |
| 1234 | + | must send this event again and then send an xdg_surface.configure | |
| 1235 | + | event. | |
| 1236 | + | ||
| 1237 | + | The configured state should not be applied immediately. See | |
| 1238 | + | xdg_surface.configure for details. | |
| 1239 | + | ||
| 1240 | + | The capabilities are sent as an array of 32-bit unsigned integers in | |
| 1241 | + | native endianness. Capability values are defined in the wm_capabilities enum. | |
| 1242 | + | </description> | |
| 1243 | + | <arg name="capabilities" type="array" summary="array of 32-bit capabilities"/> | |
| 1244 | + | </event> | |
| 1245 | + | </interface> | |
| 1246 | + | ||
| 1247 | + | <interface name="xdg_popup" version="7"> | |
| 1248 | + | <description summary="short-lived, popup surfaces for menus"> | |
| 1249 | + | A popup surface is a short-lived, temporary surface. It can be used to | |
| 1250 | + | implement for example menus, popovers, tooltips and other similar user | |
| 1251 | + | interface concepts. | |
| 1252 | + | ||
| 1253 | + | A popup can be made to take an explicit grab. See xdg_popup.grab for | |
| 1254 | + | details. | |
| 1255 | + | ||
| 1256 | + | When the popup is dismissed, a popup_done event will be sent out, and at | |
| 1257 | + | the same time the surface will be unmapped. See the xdg_popup.popup_done | |
| 1258 | + | event for details. | |
| 1259 | + | ||
| 1260 | + | Explicitly destroying the xdg_popup object will also dismiss the popup and | |
| 1261 | + | unmap the surface. Clients that want to dismiss the popup when another | |
| 1262 | + | surface of their own is clicked should dismiss the popup using the destroy | |
| 1263 | + | request. | |
| 1264 | + | ||
| 1265 | + | A newly created xdg_popup will be stacked on top of all previously created | |
| 1266 | + | xdg_popup surfaces associated with the same xdg_toplevel. | |
| 1267 | + | ||
| 1268 | + | The parent of an xdg_popup must be mapped (see the xdg_surface | |
| 1269 | + | description) before the xdg_popup itself. | |
| 1270 | + | ||
| 1271 | + | The client must call wl_surface.commit on the corresponding wl_surface | |
| 1272 | + | for the xdg_popup state to take effect. | |
| 1273 | + | </description> | |
| 1274 | + | ||
| 1275 | + | <enum name="error"> | |
| 1276 | + | <entry name="invalid_grab" value="0" | |
| 1277 | + | summary="tried to grab after being mapped"/> | |
| 1278 | + | </enum> | |
| 1279 | + | ||
| 1280 | + | <request name="destroy" type="destructor"> | |
| 1281 | + | <description summary="remove xdg_popup interface"> | |
| 1282 | + | This destroys the popup. Explicitly destroying the xdg_popup | |
| 1283 | + | object will also dismiss the popup, and unmap the surface. | |
| 1284 | + | ||
| 1285 | + | If this xdg_popup is not the "topmost" popup, the | |
| 1286 | + | xdg_wm_base.not_the_topmost_popup protocol error will be sent. | |
| 1287 | + | </description> | |
| 1288 | + | </request> | |
| 1289 | + | ||
| 1290 | + | <request name="grab"> | |
| 1291 | + | <description summary="make the popup take an explicit grab"> | |
| 1292 | + | This request makes the created popup take an explicit grab. An explicit | |
| 1293 | + | grab will be dismissed when the user dismisses the popup, or when the | |
| 1294 | + | client destroys the xdg_popup. This can be done by the user clicking | |
| 1295 | + | outside the surface, using the keyboard, or even locking the screen | |
| 1296 | + | through closing the lid or a timeout. | |
| 1297 | + | ||
| 1298 | + | If the compositor denies the grab, the popup will be immediately | |
| 1299 | + | dismissed. | |
| 1300 | + | ||
| 1301 | + | This request must be used in response to some sort of user action like a | |
| 1302 | + | button press, key press, or touch down event. The serial number of the | |
| 1303 | + | event should be passed as 'serial'. | |
| 1304 | + | ||
| 1305 | + | The parent of a grabbing popup must either be an xdg_toplevel surface or | |
| 1306 | + | another xdg_popup with an explicit grab. If the parent is another | |
| 1307 | + | xdg_popup it means that the popups are nested, with this popup now being | |
| 1308 | + | the topmost popup. | |
| 1309 | + | ||
| 1310 | + | Nested popups must be destroyed in the reverse order they were created | |
| 1311 | + | in, e.g. the only popup you are allowed to destroy at all times is the | |
| 1312 | + | topmost one. | |
| 1313 | + | ||
| 1314 | + | When compositors choose to dismiss a popup, they may dismiss every | |
| 1315 | + | nested grabbing popup as well. When a compositor dismisses popups, it | |
| 1316 | + | will follow the same dismissing order as required from the client. | |
| 1317 | + | ||
| 1318 | + | If the topmost grabbing popup is destroyed, the grab will be returned to | |
| 1319 | + | the parent of the popup, if that parent previously had an explicit grab. | |
| 1320 | + | ||
| 1321 | + | If the parent is a grabbing popup which has already been dismissed, this | |
| 1322 | + | popup will be immediately dismissed. If the parent is a popup that did | |
| 1323 | + | not take an explicit grab, an error will be raised. | |
| 1324 | + | ||
| 1325 | + | During a popup grab, the client owning the grab will receive pointer | |
| 1326 | + | and touch events for all their surfaces as normal (similar to an | |
| 1327 | + | "owner-events" grab in X11 parlance), while the top most grabbing popup | |
| 1328 | + | will always have keyboard focus. | |
| 1329 | + | </description> | |
| 1330 | + | <arg name="seat" type="object" interface="wl_seat" | |
| 1331 | + | summary="the wl_seat of the user event"/> | |
| 1332 | + | <arg name="serial" type="uint" summary="the serial of the user event"/> | |
| 1333 | + | </request> | |
| 1334 | + | ||
| 1335 | + | <event name="configure"> | |
| 1336 | + | <description summary="configure the popup surface"> | |
| 1337 | + | This event asks the popup surface to configure itself given the | |
| 1338 | + | configuration. The configured state should not be applied immediately. | |
| 1339 | + | See xdg_surface.configure for details. | |
| 1340 | + | ||
| 1341 | + | The x and y arguments represent the position the popup was placed at | |
| 1342 | + | given the xdg_positioner rule, relative to the upper left corner of the | |
| 1343 | + | window geometry of the parent surface. | |
| 1344 | + | ||
| 1345 | + | For version 2 or older, the configure event for an xdg_popup is only | |
| 1346 | + | ever sent once for the initial configuration. Starting with version 3, | |
| 1347 | + | it may be sent again if the popup is setup with an xdg_positioner with | |
| 1348 | + | set_reactive requested, or in response to xdg_popup.reposition requests. | |
| 1349 | + | </description> | |
| 1350 | + | <arg name="x" type="int" | |
| 1351 | + | summary="x position relative to parent surface window geometry"/> | |
| 1352 | + | <arg name="y" type="int" | |
| 1353 | + | summary="y position relative to parent surface window geometry"/> | |
| 1354 | + | <arg name="width" type="int" summary="window geometry width"/> | |
| 1355 | + | <arg name="height" type="int" summary="window geometry height"/> | |
| 1356 | + | </event> | |
| 1357 | + | ||
| 1358 | + | <event name="popup_done"> | |
| 1359 | + | <description summary="popup interaction is done"> | |
| 1360 | + | The popup_done event is sent out when a popup is dismissed by the | |
| 1361 | + | compositor. The client should destroy the xdg_popup object at this | |
| 1362 | + | point. | |
| 1363 | + | </description> | |
| 1364 | + | </event> | |
| 1365 | + | ||
| 1366 | + | <!-- Version 3 additions --> | |
| 1367 | + | ||
| 1368 | + | <request name="reposition" since="3"> | |
| 1369 | + | <description summary="recalculate the popup's location"> | |
| 1370 | + | Reposition an already-mapped popup. The popup will be placed given the | |
| 1371 | + | details in the passed xdg_positioner object, and a | |
| 1372 | + | xdg_popup.repositioned followed by xdg_popup.configure and | |
| 1373 | + | xdg_surface.configure will be emitted in response. Any parameters set | |
| 1374 | + | by the previous positioner will be discarded. | |
| 1375 | + | ||
| 1376 | + | The passed token will be sent in the corresponding | |
| 1377 | + | xdg_popup.repositioned event. The new popup position will not take | |
| 1378 | + | effect until the corresponding configure event is acknowledged by the | |
| 1379 | + | client. See xdg_popup.repositioned for details. The token itself is | |
| 1380 | + | opaque, and has no other special meaning. | |
| 1381 | + | ||
| 1382 | + | If multiple reposition requests are sent, the compositor may skip all | |
| 1383 | + | but the last one. | |
| 1384 | + | ||
| 1385 | + | If the popup is repositioned in response to a configure event for its | |
| 1386 | + | parent, the client should send an xdg_positioner.set_parent_configure | |
| 1387 | + | and possibly an xdg_positioner.set_parent_size request to allow the | |
| 1388 | + | compositor to properly constrain the popup. | |
| 1389 | + | ||
| 1390 | + | If the popup is repositioned together with a parent that is being | |
| 1391 | + | resized, but not in response to a configure event, the client should | |
| 1392 | + | send an xdg_positioner.set_parent_size request. | |
| 1393 | + | </description> | |
| 1394 | + | <arg name="positioner" type="object" interface="xdg_positioner"/> | |
| 1395 | + | <arg name="token" type="uint" summary="reposition request token"/> | |
| 1396 | + | </request> | |
| 1397 | + | ||
| 1398 | + | <event name="repositioned" since="3"> | |
| 1399 | + | <description summary="signal the completion of a repositioned request"> | |
| 1400 | + | The repositioned event is sent as part of a popup configuration | |
| 1401 | + | sequence, together with xdg_popup.configure and lastly | |
| 1402 | + | xdg_surface.configure to notify the completion of a reposition request. | |
| 1403 | + | ||
| 1404 | + | The repositioned event is to notify about the completion of a | |
| 1405 | + | xdg_popup.reposition request. The token argument is the token passed | |
| 1406 | + | in the xdg_popup.reposition request. | |
| 1407 | + | ||
| 1408 | + | Immediately after this event is emitted, xdg_popup.configure and | |
| 1409 | + | xdg_surface.configure will be sent with the updated size and position, | |
| 1410 | + | as well as a new configure serial. | |
| 1411 | + | ||
| 1412 | + | The client should optionally update the content of the popup, but must | |
| 1413 | + | acknowledge the new popup configuration for the new position to take | |
| 1414 | + | effect. See xdg_surface.ack_configure for details. | |
| 1415 | + | </description> | |
| 1416 | + | <arg name="token" type="uint" summary="reposition request token"/> | |
| 1417 | + | </event> | |
| 1418 | + | ||
| 1419 | + | </interface> | |
| 1420 | + | </protocol> | |
A protocol/xdgshell/xdg-shell_generated.go +1136 -0
| @@ -0,0 +1,1136 @@ | |||
| 1 | + | // Code generated by wlturbo-scanner. DO NOT EDIT. | |
| 2 | + | // Protocol: xdg_shell | |
| 3 | + | // Source: xdg-shell.xml | |
| 4 | + | ||
| 5 | + | package xdgshell | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "sync" | |
| 9 | + | ||
| 10 | + | cross_wl_output "github.com/bnema/wlturbo/protocol/core" | |
| 11 | + | cross_wl_seat "github.com/bnema/wlturbo/protocol/core" | |
| 12 | + | cross_wl_surface "github.com/bnema/wlturbo/protocol/core" | |
| 13 | + | "github.com/bnema/wlturbo/wl" | |
| 14 | + | ) | |
| 15 | + | ||
| 16 | + | // ERROR_ROLE is the error role value. | |
| 17 | + | const ERROR_ROLE = 0 | |
| 18 | + | ||
| 19 | + | // ERROR_DEFUNCT_SURFACES is the error defunct_surfaces value. | |
| 20 | + | const ERROR_DEFUNCT_SURFACES = 1 | |
| 21 | + | ||
| 22 | + | // ERROR_NOT_THE_TOPMOST_POPUP is the error not_the_topmost_popup value. | |
| 23 | + | const ERROR_NOT_THE_TOPMOST_POPUP = 2 | |
| 24 | + | ||
| 25 | + | // ERROR_INVALID_POPUP_PARENT is the error invalid_popup_parent value. | |
| 26 | + | const ERROR_INVALID_POPUP_PARENT = 3 | |
| 27 | + | ||
| 28 | + | // ERROR_INVALID_SURFACE_STATE is the error invalid_surface_state value. | |
| 29 | + | const ERROR_INVALID_SURFACE_STATE = 4 | |
| 30 | + | ||
| 31 | + | // ERROR_INVALID_POSITIONER is the error invalid_positioner value. | |
| 32 | + | const ERROR_INVALID_POSITIONER = 5 | |
| 33 | + | ||
| 34 | + | // ERROR_UNRESPONSIVE is the error unresponsive value. | |
| 35 | + | const ERROR_UNRESPONSIVE = 6 | |
| 36 | + | ||
| 37 | + | // ERROR_INVALID_INPUT is the error invalid_input value. | |
| 38 | + | const ERROR_INVALID_INPUT = 0 | |
| 39 | + | ||
| 40 | + | // ANCHOR_NONE is the anchor none value. | |
| 41 | + | const ANCHOR_NONE = 0 | |
| 42 | + | ||
| 43 | + | // ANCHOR_TOP is the anchor top value. | |
| 44 | + | const ANCHOR_TOP = 1 | |
| 45 | + | ||
| 46 | + | // ANCHOR_BOTTOM is the anchor bottom value. | |
| 47 | + | const ANCHOR_BOTTOM = 2 | |
| 48 | + | ||
| 49 | + | // ANCHOR_LEFT is the anchor left value. | |
| 50 | + | const ANCHOR_LEFT = 3 | |
| 51 | + | ||
| 52 | + | // ANCHOR_RIGHT is the anchor right value. | |
| 53 | + | const ANCHOR_RIGHT = 4 | |
| 54 | + | ||
| 55 | + | // ANCHOR_TOP_LEFT is the anchor top_left value. | |
| 56 | + | const ANCHOR_TOP_LEFT = 5 | |
| 57 | + | ||
| 58 | + | // ANCHOR_BOTTOM_LEFT is the anchor bottom_left value. | |
| 59 | + | const ANCHOR_BOTTOM_LEFT = 6 | |
| 60 | + | ||
| 61 | + | // ANCHOR_TOP_RIGHT is the anchor top_right value. | |
| 62 | + | const ANCHOR_TOP_RIGHT = 7 | |
| 63 | + | ||
| 64 | + | // ANCHOR_BOTTOM_RIGHT is the anchor bottom_right value. | |
| 65 | + | const ANCHOR_BOTTOM_RIGHT = 8 | |
| 66 | + | ||
| 67 | + | // GRAVITY_NONE is the gravity none value. | |
| 68 | + | const GRAVITY_NONE = 0 | |
| 69 | + | ||
| 70 | + | // GRAVITY_TOP is the gravity top value. | |
| 71 | + | const GRAVITY_TOP = 1 | |
| 72 | + | ||
| 73 | + | // GRAVITY_BOTTOM is the gravity bottom value. | |
| 74 | + | const GRAVITY_BOTTOM = 2 | |
| 75 | + | ||
| 76 | + | // GRAVITY_LEFT is the gravity left value. | |
| 77 | + | const GRAVITY_LEFT = 3 | |
| 78 | + | ||
| 79 | + | // GRAVITY_RIGHT is the gravity right value. | |
| 80 | + | const GRAVITY_RIGHT = 4 | |
| 81 | + | ||
| 82 | + | // GRAVITY_TOP_LEFT is the gravity top_left value. | |
| 83 | + | const GRAVITY_TOP_LEFT = 5 | |
| 84 | + | ||
| 85 | + | // GRAVITY_BOTTOM_LEFT is the gravity bottom_left value. | |
| 86 | + | const GRAVITY_BOTTOM_LEFT = 6 | |
| 87 | + | ||
| 88 | + | // GRAVITY_TOP_RIGHT is the gravity top_right value. | |
| 89 | + | const GRAVITY_TOP_RIGHT = 7 | |
| 90 | + | ||
| 91 | + | // GRAVITY_BOTTOM_RIGHT is the gravity bottom_right value. | |
| 92 | + | const GRAVITY_BOTTOM_RIGHT = 8 | |
| 93 | + | ||
| 94 | + | // CONSTRAINT_ADJUSTMENT_NONE is the constraint_adjustment none value. | |
| 95 | + | const CONSTRAINT_ADJUSTMENT_NONE = 0 | |
| 96 | + | ||
| 97 | + | // CONSTRAINT_ADJUSTMENT_SLIDE_X is the constraint_adjustment slide_x value. | |
| 98 | + | const CONSTRAINT_ADJUSTMENT_SLIDE_X = 1 | |
| 99 | + | ||
| 100 | + | // CONSTRAINT_ADJUSTMENT_SLIDE_Y is the constraint_adjustment slide_y value. | |
| 101 | + | const CONSTRAINT_ADJUSTMENT_SLIDE_Y = 2 | |
| 102 | + | ||
| 103 | + | // CONSTRAINT_ADJUSTMENT_FLIP_X is the constraint_adjustment flip_x value. | |
| 104 | + | const CONSTRAINT_ADJUSTMENT_FLIP_X = 4 | |
| 105 | + | ||
| 106 | + | // CONSTRAINT_ADJUSTMENT_FLIP_Y is the constraint_adjustment flip_y value. | |
| 107 | + | const CONSTRAINT_ADJUSTMENT_FLIP_Y = 8 | |
| 108 | + | ||
| 109 | + | // CONSTRAINT_ADJUSTMENT_RESIZE_X is the constraint_adjustment resize_x value. | |
| 110 | + | const CONSTRAINT_ADJUSTMENT_RESIZE_X = 16 | |
| 111 | + | ||
| 112 | + | // CONSTRAINT_ADJUSTMENT_RESIZE_Y is the constraint_adjustment resize_y value. | |
| 113 | + | const CONSTRAINT_ADJUSTMENT_RESIZE_Y = 32 | |
| 114 | + | ||
| 115 | + | // ERROR_NOT_CONSTRUCTED is the error not_constructed value. | |
| 116 | + | const ERROR_NOT_CONSTRUCTED = 1 | |
| 117 | + | ||
| 118 | + | // ERROR_ALREADY_CONSTRUCTED is the error already_constructed value. | |
| 119 | + | const ERROR_ALREADY_CONSTRUCTED = 2 | |
| 120 | + | ||
| 121 | + | // ERROR_UNCONFIGURED_BUFFER is the error unconfigured_buffer value. | |
| 122 | + | const ERROR_UNCONFIGURED_BUFFER = 3 | |
| 123 | + | ||
| 124 | + | // ERROR_INVALID_SERIAL is the error invalid_serial value. | |
| 125 | + | const ERROR_INVALID_SERIAL = 4 | |
| 126 | + | ||
| 127 | + | // XDG_SURFACE_ERROR_INVALID_SIZE is the error invalid_size value. | |
| 128 | + | const XDG_SURFACE_ERROR_INVALID_SIZE = 5 | |
| 129 | + | ||
| 130 | + | // ERROR_DEFUNCT_ROLE_OBJECT is the error defunct_role_object value. | |
| 131 | + | const ERROR_DEFUNCT_ROLE_OBJECT = 6 | |
| 132 | + | ||
| 133 | + | // ERROR_INVALID_RESIZE_EDGE is the error invalid_resize_edge value. | |
| 134 | + | const ERROR_INVALID_RESIZE_EDGE = 0 | |
| 135 | + | ||
| 136 | + | // ERROR_INVALID_PARENT is the error invalid_parent value. | |
| 137 | + | const ERROR_INVALID_PARENT = 1 | |
| 138 | + | ||
| 139 | + | // XDG_TOPLEVEL_ERROR_INVALID_SIZE is the error invalid_size value. | |
| 140 | + | const XDG_TOPLEVEL_ERROR_INVALID_SIZE = 2 | |
| 141 | + | ||
| 142 | + | // RESIZE_EDGE_NONE is the resize_edge none value. | |
| 143 | + | const RESIZE_EDGE_NONE = 0 | |
| 144 | + | ||
| 145 | + | // RESIZE_EDGE_TOP is the resize_edge top value. | |
| 146 | + | const RESIZE_EDGE_TOP = 1 | |
| 147 | + | ||
| 148 | + | // RESIZE_EDGE_BOTTOM is the resize_edge bottom value. | |
| 149 | + | const RESIZE_EDGE_BOTTOM = 2 | |
| 150 | + | ||
| 151 | + | // RESIZE_EDGE_LEFT is the resize_edge left value. | |
| 152 | + | const RESIZE_EDGE_LEFT = 4 | |
| 153 | + | ||
| 154 | + | // RESIZE_EDGE_TOP_LEFT is the resize_edge top_left value. | |
| 155 | + | const RESIZE_EDGE_TOP_LEFT = 5 | |
| 156 | + | ||
| 157 | + | // RESIZE_EDGE_BOTTOM_LEFT is the resize_edge bottom_left value. | |
| 158 | + | const RESIZE_EDGE_BOTTOM_LEFT = 6 | |
| 159 | + | ||
| 160 | + | // RESIZE_EDGE_RIGHT is the resize_edge right value. | |
| 161 | + | const RESIZE_EDGE_RIGHT = 8 | |
| 162 | + | ||
| 163 | + | // RESIZE_EDGE_TOP_RIGHT is the resize_edge top_right value. | |
| 164 | + | const RESIZE_EDGE_TOP_RIGHT = 9 | |
| 165 | + | ||
| 166 | + | // RESIZE_EDGE_BOTTOM_RIGHT is the resize_edge bottom_right value. | |
| 167 | + | const RESIZE_EDGE_BOTTOM_RIGHT = 10 | |
| 168 | + | ||
| 169 | + | // STATE_MAXIMIZED is the state maximized value. | |
| 170 | + | const STATE_MAXIMIZED = 1 | |
| 171 | + | ||
| 172 | + | // STATE_FULLSCREEN is the state fullscreen value. | |
| 173 | + | const STATE_FULLSCREEN = 2 | |
| 174 | + | ||
| 175 | + | // STATE_RESIZING is the state resizing value. | |
| 176 | + | const STATE_RESIZING = 3 | |
| 177 | + | ||
| 178 | + | // STATE_ACTIVATED is the state activated value. | |
| 179 | + | const STATE_ACTIVATED = 4 | |
| 180 | + | ||
| 181 | + | // STATE_TILED_LEFT is the state tiled_left value. | |
| 182 | + | const STATE_TILED_LEFT = 5 | |
| 183 | + | ||
| 184 | + | // STATE_TILED_RIGHT is the state tiled_right value. | |
| 185 | + | const STATE_TILED_RIGHT = 6 | |
| 186 | + | ||
| 187 | + | // STATE_TILED_TOP is the state tiled_top value. | |
| 188 | + | const STATE_TILED_TOP = 7 | |
| 189 | + | ||
| 190 | + | // STATE_TILED_BOTTOM is the state tiled_bottom value. | |
| 191 | + | const STATE_TILED_BOTTOM = 8 | |
| 192 | + | ||
| 193 | + | // STATE_SUSPENDED is the state suspended value. | |
| 194 | + | const STATE_SUSPENDED = 9 | |
| 195 | + | ||
| 196 | + | // STATE_CONSTRAINED_LEFT is the state constrained_left value. | |
| 197 | + | const STATE_CONSTRAINED_LEFT = 10 | |
| 198 | + | ||
| 199 | + | // STATE_CONSTRAINED_RIGHT is the state constrained_right value. | |
| 200 | + | const STATE_CONSTRAINED_RIGHT = 11 | |
| 201 | + | ||
| 202 | + | // STATE_CONSTRAINED_TOP is the state constrained_top value. | |
| 203 | + | const STATE_CONSTRAINED_TOP = 12 | |
| 204 | + | ||
| 205 | + | // STATE_CONSTRAINED_BOTTOM is the state constrained_bottom value. | |
| 206 | + | const STATE_CONSTRAINED_BOTTOM = 13 | |
| 207 | + | ||
| 208 | + | // WM_CAPABILITIES_WINDOW_MENU is the wm_capabilities window_menu value. | |
| 209 | + | const WM_CAPABILITIES_WINDOW_MENU = 1 | |
| 210 | + | ||
| 211 | + | // WM_CAPABILITIES_MAXIMIZE is the wm_capabilities maximize value. | |
| 212 | + | const WM_CAPABILITIES_MAXIMIZE = 2 | |
| 213 | + | ||
| 214 | + | // WM_CAPABILITIES_FULLSCREEN is the wm_capabilities fullscreen value. | |
| 215 | + | const WM_CAPABILITIES_FULLSCREEN = 3 | |
| 216 | + | ||
| 217 | + | // WM_CAPABILITIES_MINIMIZE is the wm_capabilities minimize value. | |
| 218 | + | const WM_CAPABILITIES_MINIMIZE = 4 | |
| 219 | + | ||
| 220 | + | // ERROR_INVALID_GRAB is the error invalid_grab value. | |
| 221 | + | const ERROR_INVALID_GRAB = 0 | |
| 222 | + | ||
| 223 | + | // XdgWmBaseInterface is the protocol interface name. | |
| 224 | + | const XdgWmBaseInterface = "xdg_wm_base" | |
| 225 | + | ||
| 226 | + | // XdgWmBase is the generated binding for xdg_wm_base version 7. | |
| 227 | + | type XdgWmBase struct { | |
| 228 | + | wl.BaseProxy | |
| 229 | + | // handlersMu guards the handler slices: a client may register or replace | |
| 230 | + | // handlers while another goroutine dispatches events. | |
| 231 | + | handlersMu sync.Mutex | |
| 232 | + | onPing []func(serial uint32) | |
| 233 | + | } | |
| 234 | + | ||
| 235 | + | // NewXdgWmBase returns an unbound XdgWmBase. | |
| 236 | + | func NewXdgWmBase(ctx *wl.Context) *XdgWmBase { | |
| 237 | + | obj := &XdgWmBase{} | |
| 238 | + | obj.SetContext(ctx) | |
| 239 | + | ctx.Register(obj) | |
| 240 | + | return obj | |
| 241 | + | } | |
| 242 | + | ||
| 243 | + | // Destroy this xdg_wm_base object. Destroying a bound xdg_wm_base object while there are surfaces still alive created by this xdg_wm_base object instance is illegal and will result in a defunct_surfaces error. | |
| 244 | + | func (o *XdgWmBase) Destroy() error { | |
| 245 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 246 | + | return err | |
| 247 | + | } | |
| 248 | + | if err := o.Context().SendDestructor(o, 0); err != nil { | |
| 249 | + | return err | |
| 250 | + | } | |
| 251 | + | return nil | |
| 252 | + | } | |
| 253 | + | ||
| 254 | + | // Create a positioner object. A positioner object is used to position surfaces relative to some parent surface. See the interface description and xdg_surface.get_popup for details. | |
| 255 | + | func (o *XdgWmBase) CreatePositioner() (*XdgPositioner, error) { | |
| 256 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 257 | + | return nil, err | |
| 258 | + | } | |
| 259 | + | child := &XdgPositioner{} | |
| 260 | + | child.SetContext(o.Context()) | |
| 261 | + | child.SetID(o.Context().AllocateID()) | |
| 262 | + | o.Context().Register(child) | |
| 263 | + | if err := o.Context().SendRequest(o, 1, child); err != nil { | |
| 264 | + | o.Context().Unregister(child) | |
| 265 | + | return nil, err | |
| 266 | + | } | |
| 267 | + | return child, nil | |
| 268 | + | } | |
| 269 | + | ||
| 270 | + | // This creates an xdg_surface for the given surface. An xdg_surface is used as basis to define a role to a given surface, such as xdg_toplevel or xdg_popup. It also manages functionality shared between xdg_surface based surface roles. While xdg_surface itself is not a role, the corresponding surface may only be assigned a role extending xdg_surface, such as xdg_toplevel or xdg_popup. It is illegal to create an xdg_surface for a wl_surface which already has anassigned role and this will result in a role error. See the documentation of xdg_surface for more details about what an xdg_surface is and how it is used. | |
| 271 | + | func (o *XdgWmBase) GetXdgSurface(surface *cross_wl_surface.Surface) (*XdgSurface, error) { | |
| 272 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 273 | + | return nil, err | |
| 274 | + | } | |
| 275 | + | var arg1 wl.Object | |
| 276 | + | if surface != nil { | |
| 277 | + | arg1 = surface | |
| 278 | + | } | |
| 279 | + | child := &XdgSurface{} | |
| 280 | + | child.SetContext(o.Context()) | |
| 281 | + | child.SetID(o.Context().AllocateID()) | |
| 282 | + | o.Context().Register(child) | |
| 283 | + | if err := o.Context().SendRequest(o, 2, child, arg1); err != nil { | |
| 284 | + | o.Context().Unregister(child) | |
| 285 | + | return nil, err | |
| 286 | + | } | |
| 287 | + | return child, nil | |
| 288 | + | } | |
| 289 | + | ||
| 290 | + | // A client must respond to a ping event with a pong request or the client may be deemed unresponsive. See xdg_wm_base.ping and xdg_wm_base.error.unresponsive. | |
| 291 | + | func (o *XdgWmBase) Pong(serial uint32) error { | |
| 292 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 293 | + | return err | |
| 294 | + | } | |
| 295 | + | if err := o.Context().SendRequest(o, 3, serial); err != nil { | |
| 296 | + | return err | |
| 297 | + | } | |
| 298 | + | return nil | |
| 299 | + | } | |
| 300 | + | ||
| 301 | + | // OnPing registers a handler for the ping event. | |
| 302 | + | // | |
| 303 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 304 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 305 | + | func (o *XdgWmBase) OnPing(handler func(serial uint32)) { | |
| 306 | + | if handler == nil { | |
| 307 | + | return | |
| 308 | + | } | |
| 309 | + | o.handlersMu.Lock() | |
| 310 | + | defer o.handlersMu.Unlock() | |
| 311 | + | o.onPing = append(o.onPing, handler) | |
| 312 | + | } | |
| 313 | + | ||
| 314 | + | // handlersForPing returns the handlers registered for ping, taken | |
| 315 | + | // under the lock so a handler may register another handler while events are | |
| 316 | + | // being dispatched. | |
| 317 | + | func (o *XdgWmBase) handlersForPing() []func(serial uint32) { | |
| 318 | + | o.handlersMu.Lock() | |
| 319 | + | defer o.handlersMu.Unlock() | |
| 320 | + | return o.onPing | |
| 321 | + | } | |
| 322 | + | ||
| 323 | + | // EventSignature returns the declared event argument types in wire order. | |
| 324 | + | func (o *XdgWmBase) EventSignature(opcode uint16) (string, bool) { | |
| 325 | + | switch opcode { | |
| 326 | + | case 0: | |
| 327 | + | return "uint,", true | |
| 328 | + | default: | |
| 329 | + | return "", false | |
| 330 | + | } | |
| 331 | + | } | |
| 332 | + | ||
| 333 | + | // Dispatch decodes one event on xdg_wm_base and calls the registered handlers. | |
| 334 | + | func (o *XdgWmBase) Dispatch(event *wl.Event) { | |
| 335 | + | switch event.Opcode { | |
| 336 | + | case 0: // ping | |
| 337 | + | serial := event.Uint32() | |
| 338 | + | ||
| 339 | + | for i, handler := range o.handlersForPing() { | |
| 340 | + | _ = i | |
| 341 | + | handler(serial) | |
| 342 | + | } | |
| 343 | + | default: | |
| 344 | + | } | |
| 345 | + | } | |
| 346 | + | ||
| 347 | + | // XdgPositionerInterface is the protocol interface name. | |
| 348 | + | const XdgPositionerInterface = "xdg_positioner" | |
| 349 | + | ||
| 350 | + | // XdgPositioner is the generated binding for xdg_positioner version 7. | |
| 351 | + | type XdgPositioner struct { | |
| 352 | + | wl.BaseProxy | |
| 353 | + | } | |
| 354 | + | ||
| 355 | + | // NewXdgPositioner returns an unbound XdgPositioner. | |
| 356 | + | func NewXdgPositioner(ctx *wl.Context) *XdgPositioner { | |
| 357 | + | obj := &XdgPositioner{} | |
| 358 | + | obj.SetContext(ctx) | |
| 359 | + | ctx.Register(obj) | |
| 360 | + | return obj | |
| 361 | + | } | |
| 362 | + | ||
| 363 | + | // Notify the compositor that the xdg_positioner will no longer be used. | |
| 364 | + | func (o *XdgPositioner) Destroy() error { | |
| 365 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 366 | + | return err | |
| 367 | + | } | |
| 368 | + | if err := o.Context().SendDestructor(o, 0); err != nil { | |
| 369 | + | return err | |
| 370 | + | } | |
| 371 | + | return nil | |
| 372 | + | } | |
| 373 | + | ||
| 374 | + | // Set the size of the surface that is to be positioned with the positioner object. The size is in surface-local coordinates and corresponds to the window geometry. See xdg_surface.set_window_geometry. If a zero or negative size is set the invalid_input error is raised. | |
| 375 | + | func (o *XdgPositioner) SetSize(width int32, height int32) error { | |
| 376 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 377 | + | return err | |
| 378 | + | } | |
| 379 | + | if err := o.Context().SendRequest(o, 1, width, height); err != nil { | |
| 380 | + | return err | |
| 381 | + | } | |
| 382 | + | return nil | |
| 383 | + | } | |
| 384 | + | ||
| 385 | + | // Specify the anchor rectangle within the parent surface that the child surface will be placed relative to. The rectangle is relative to the window geometry as defined by xdg_surface.set_window_geometry of the parent surface. When the xdg_positioner object is used to position a child surface, the anchor rectangle may not extend outside the window geometry of the positioned child's parent surface. If a negative size is set the invalid_input error is raised. | |
| 386 | + | func (o *XdgPositioner) SetAnchorRect(x int32, y int32, width int32, height int32) error { | |
| 387 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 388 | + | return err | |
| 389 | + | } | |
| 390 | + | if err := o.Context().SendRequest(o, 2, x, y, width, height); err != nil { | |
| 391 | + | return err | |
| 392 | + | } | |
| 393 | + | return nil | |
| 394 | + | } | |
| 395 | + | ||
| 396 | + | // Defines the anchor point for the anchor rectangle. The specified anchor is used to derive an anchor point that the child surface will be positioned relative to. If a corner anchor is set (e.g. 'top_left' or 'bottom_right'), the anchor point will be at the specified corner; otherwise, the derived anchor point will be centered on the specified edge, or in the center of the anchor rectangle if no edge is specified. | |
| 397 | + | func (o *XdgPositioner) SetAnchor(anchor uint32) error { | |
| 398 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 399 | + | return err | |
| 400 | + | } | |
| 401 | + | if err := o.Context().SendRequest(o, 3, anchor); err != nil { | |
| 402 | + | return err | |
| 403 | + | } | |
| 404 | + | return nil | |
| 405 | + | } | |
| 406 | + | ||
| 407 | + | // Defines in what direction a surface should be positioned, relative to the anchor point of the parent surface. If a corner gravity is specified (e.g. 'bottom_right' or 'top_left'), then the child surface will be placed towards the specified gravity; otherwise, the child surface will be centered over the anchor point on any axis that had no gravity specified. If the gravity is not in the ‘gravity’ enum, an invalid_input error is raised. | |
| 408 | + | func (o *XdgPositioner) SetGravity(gravity uint32) error { | |
| 409 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 410 | + | return err | |
| 411 | + | } | |
| 412 | + | if err := o.Context().SendRequest(o, 4, gravity); err != nil { | |
| 413 | + | return err | |
| 414 | + | } | |
| 415 | + | return nil | |
| 416 | + | } | |
| 417 | + | ||
| 418 | + | // Specify how the window should be positioned if the originally intended position caused the surface to be constrained, meaning at least partially outside positioning boundaries set by the compositor. The adjustment is set by constructing a bitmask describing the adjustment to be made when the surface is constrained on that axis. If no bit for one axis is set, the compositor will assume that the child surface should not change its position on that axis when constrained. If more than one bit for one axis is set, the order of how adjustments are applied is specified in the corresponding adjustment descriptions. The default adjustment is none. | |
| 419 | + | func (o *XdgPositioner) SetConstraintAdjustment(constraintAdjustment uint32) error { | |
| 420 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 421 | + | return err | |
| 422 | + | } | |
| 423 | + | if err := o.Context().SendRequest(o, 5, constraintAdjustment); err != nil { | |
| 424 | + | return err | |
| 425 | + | } | |
| 426 | + | return nil | |
| 427 | + | } | |
| 428 | + | ||
| 429 | + | // Specify the surface position offset relative to the position of the anchor on the anchor rectangle and the anchor on the surface. For example if the anchor of the anchor rectangle is at (x, y), the surface has the gravity bottom|right, and the offset is (ox, oy), the calculated surface position will be (x + ox, y + oy). The offset position of the surface is the one used for constraint testing. See set_constraint_adjustment. An example use case is placing a popup menu on top of a user interface element, while aligning the user interface element of the parent surface with some user interface element placed somewhere in the popup surface. | |
| 430 | + | func (o *XdgPositioner) SetOffset(x int32, y int32) error { | |
| 431 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 432 | + | return err | |
| 433 | + | } | |
| 434 | + | if err := o.Context().SendRequest(o, 6, x, y); err != nil { | |
| 435 | + | return err | |
| 436 | + | } | |
| 437 | + | return nil | |
| 438 | + | } | |
| 439 | + | ||
| 440 | + | // When set reactive, the surface is reconstrained if the conditions used for constraining changed, e.g. the parent window moved. If the conditions changed and the popup was reconstrained, an xdg_popup.configure event is sent with updated geometry, followed by an xdg_surface.configure event. | |
| 441 | + | func (o *XdgPositioner) SetReactive() error { | |
| 442 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 443 | + | return err | |
| 444 | + | } | |
| 445 | + | if err := o.Context().SendRequest(o, 7); err != nil { | |
| 446 | + | return err | |
| 447 | + | } | |
| 448 | + | return nil | |
| 449 | + | } | |
| 450 | + | ||
| 451 | + | // Set the parent window geometry the compositor should use when positioning the popup. The compositor may use this information to determine the future state the popup should be constrained using. If this doesn't match the dimension of the parent the popup is eventually positioned against, the behavior is undefined. The arguments are given in the surface-local coordinate space. | |
| 452 | + | func (o *XdgPositioner) SetParentSize(parentWidth int32, parentHeight int32) error { | |
| 453 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 454 | + | return err | |
| 455 | + | } | |
| 456 | + | if err := o.Context().SendRequest(o, 8, parentWidth, parentHeight); err != nil { | |
| 457 | + | return err | |
| 458 | + | } | |
| 459 | + | return nil | |
| 460 | + | } | |
| 461 | + | ||
| 462 | + | // Set the serial of an xdg_surface.configure event this positioner will be used in response to. The compositor may use this information together with set_parent_size to determine what future state the popup should be constrained using. | |
| 463 | + | func (o *XdgPositioner) SetParentConfigure(serial uint32) error { | |
| 464 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 465 | + | return err | |
| 466 | + | } | |
| 467 | + | if err := o.Context().SendRequest(o, 9, serial); err != nil { | |
| 468 | + | return err | |
| 469 | + | } | |
| 470 | + | return nil | |
| 471 | + | } | |
| 472 | + | ||
| 473 | + | // EventSignature returns the declared event argument types in wire order. | |
| 474 | + | func (o *XdgPositioner) EventSignature(opcode uint16) (string, bool) { | |
| 475 | + | switch opcode { | |
| 476 | + | default: | |
| 477 | + | return "", false | |
| 478 | + | } | |
| 479 | + | } | |
| 480 | + | ||
| 481 | + | // Dispatch decodes one event on xdg_positioner and calls the registered handlers. | |
| 482 | + | func (o *XdgPositioner) Dispatch(event *wl.Event) { | |
| 483 | + | switch event.Opcode { | |
| 484 | + | default: | |
| 485 | + | } | |
| 486 | + | } | |
| 487 | + | ||
| 488 | + | // XdgSurfaceInterface is the protocol interface name. | |
| 489 | + | const XdgSurfaceInterface = "xdg_surface" | |
| 490 | + | ||
| 491 | + | // XdgSurface is the generated binding for xdg_surface version 7. | |
| 492 | + | type XdgSurface struct { | |
| 493 | + | wl.BaseProxy | |
| 494 | + | // handlersMu guards the handler slices: a client may register or replace | |
| 495 | + | // handlers while another goroutine dispatches events. | |
| 496 | + | handlersMu sync.Mutex | |
| 497 | + | onConfigure []func(serial uint32) | |
| 498 | + | } | |
| 499 | + | ||
| 500 | + | // NewXdgSurface returns an unbound XdgSurface. | |
| 501 | + | func NewXdgSurface(ctx *wl.Context) *XdgSurface { | |
| 502 | + | obj := &XdgSurface{} | |
| 503 | + | obj.SetContext(ctx) | |
| 504 | + | ctx.Register(obj) | |
| 505 | + | return obj | |
| 506 | + | } | |
| 507 | + | ||
| 508 | + | // Destroy the xdg_surface object. An xdg_surface must only be destroyed after its role object has been destroyed, otherwise a defunct_role_object error is raised. | |
| 509 | + | func (o *XdgSurface) Destroy() error { | |
| 510 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 511 | + | return err | |
| 512 | + | } | |
| 513 | + | if err := o.Context().SendDestructor(o, 0); err != nil { | |
| 514 | + | return err | |
| 515 | + | } | |
| 516 | + | return nil | |
| 517 | + | } | |
| 518 | + | ||
| 519 | + | // This creates an xdg_toplevel object for the given xdg_surface and gives the associated wl_surface the xdg_toplevel role. See the documentation of xdg_toplevel for more details about what an xdg_toplevel is and how it is used. | |
| 520 | + | func (o *XdgSurface) GetToplevel() (*XdgToplevel, error) { | |
| 521 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 522 | + | return nil, err | |
| 523 | + | } | |
| 524 | + | child := &XdgToplevel{} | |
| 525 | + | child.SetContext(o.Context()) | |
| 526 | + | child.SetID(o.Context().AllocateID()) | |
| 527 | + | o.Context().Register(child) | |
| 528 | + | if err := o.Context().SendRequest(o, 1, child); err != nil { | |
| 529 | + | o.Context().Unregister(child) | |
| 530 | + | return nil, err | |
| 531 | + | } | |
| 532 | + | return child, nil | |
| 533 | + | } | |
| 534 | + | ||
| 535 | + | // This creates an xdg_popup object for the given xdg_surface and gives the associated wl_surface the xdg_popup role. If null is passed as a parent, a parent surface must be specified using some other protocol, before committing the initial state. See the documentation of xdg_popup for more details about what an xdg_popup is and how it is used. | |
| 536 | + | func (o *XdgSurface) GetPopup(parent *XdgSurface, positioner *XdgPositioner) (*XdgPopup, error) { | |
| 537 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 538 | + | return nil, err | |
| 539 | + | } | |
| 540 | + | var arg1 wl.Object | |
| 541 | + | if parent != nil { | |
| 542 | + | arg1 = parent | |
| 543 | + | } | |
| 544 | + | var arg2 wl.Object | |
| 545 | + | if positioner != nil { | |
| 546 | + | arg2 = positioner | |
| 547 | + | } | |
| 548 | + | child := &XdgPopup{} | |
| 549 | + | child.SetContext(o.Context()) | |
| 550 | + | child.SetID(o.Context().AllocateID()) | |
| 551 | + | o.Context().Register(child) | |
| 552 | + | if err := o.Context().SendRequest(o, 2, child, arg1, arg2); err != nil { | |
| 553 | + | o.Context().Unregister(child) | |
| 554 | + | return nil, err | |
| 555 | + | } | |
| 556 | + | return child, nil | |
| 557 | + | } | |
| 558 | + | ||
| 559 | + | // The window geometry of a surface is its "visible bounds" from the user's perspective. Client-side decorations often have invisible portions like drop-shadows which should be ignored for the purposes of aligning, placing and constraining windows. Note that in some situations, compositors may clip rendering to the window geometry, so the client should avoid putting functional elements outside of it. The window geometry is double-buffered state, see wl_surface.commit. When maintaining a position, the compositor should treat the (x, y) coordinate of the window geometry as the top left corner of the window. A client changing the (x, y) window geometry coordinate should in general not alter the position of the window. Once the window geometry of the surface is set, it is not possible to unset it, and it will remain the same until set_window_geometry is called again, even if a new subsurface or buffer is attached. If never set, the value is the full bounds of the surface, including any subsurfaces. This updates dynamically on every commit. This unset is meant for extremely simple clients. The arguments are given in the surface-local coordinate space of the wl_surface associated with this xdg_surface, and may extend outside of the wl_surface itself to mark parts of the subsurface tree as part of the window geometry. When applied, the effective window geometry will be the set window geometry clamped to the bounding rectangle of the combined geometry of the surface of the xdg_surface and the associated subsurfaces. The effective geometry will not be recalculated unless a new call to set_window_geometry is done and the new pending surface state is subsequently applied. The width and height of the effective window geometry must be greater than zero. Setting an invalid size will raise an invalid_size error. | |
| 560 | + | func (o *XdgSurface) SetWindowGeometry(x int32, y int32, width int32, height int32) error { | |
| 561 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 562 | + | return err | |
| 563 | + | } | |
| 564 | + | if err := o.Context().SendRequest(o, 3, x, y, width, height); err != nil { | |
| 565 | + | return err | |
| 566 | + | } | |
| 567 | + | return nil | |
| 568 | + | } | |
| 569 | + | ||
| 570 | + | // When a configure event is received, if a client commits the surface in response to the configure event, then the client must make an ack_configure request sometime before the commit request, passing along the serial of the configure event. For instance, for toplevel surfaces the compositor might use this information to move a surface to the top left only when the client has drawn itself for the maximized or fullscreen state. If the client receives multiple configure events before it can respond to one, it only has to ack the last configure event. Acking a configure event that was never sent raises an invalid_serial error. A client is not required to commit immediately after sending an ack_configure request - it may even ack_configure several times before its next surface commit. A client may send multiple ack_configure requests before committing, but only the last request sent before a commit indicates which configure event the client really is responding to. Sending an ack_configure request consumes the serial number sent with the request, as well as serial numbers sent by all configure events sent on this xdg_surface prior to the configure event referenced by the committed serial. It is an error to issue multiple ack_configure requests referencing a serial from the same configure event, or to issue an ack_configure request referencing a serial from a configure event issued before the event identified by the last ack_configure request for the same xdg_surface. Doing so will raise an invalid_serial error. | |
| 571 | + | func (o *XdgSurface) AckConfigure(serial uint32) error { | |
| 572 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 573 | + | return err | |
| 574 | + | } | |
| 575 | + | if err := o.Context().SendRequest(o, 4, serial); err != nil { | |
| 576 | + | return err | |
| 577 | + | } | |
| 578 | + | return nil | |
| 579 | + | } | |
| 580 | + | ||
| 581 | + | // OnConfigure registers a handler for the configure event. | |
| 582 | + | // | |
| 583 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 584 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 585 | + | func (o *XdgSurface) OnConfigure(handler func(serial uint32)) { | |
| 586 | + | if handler == nil { | |
| 587 | + | return | |
| 588 | + | } | |
| 589 | + | o.handlersMu.Lock() | |
| 590 | + | defer o.handlersMu.Unlock() | |
| 591 | + | o.onConfigure = append(o.onConfigure, handler) | |
| 592 | + | } | |
| 593 | + | ||
| 594 | + | // handlersForConfigure returns the handlers registered for configure, taken | |
| 595 | + | // under the lock so a handler may register another handler while events are | |
| 596 | + | // being dispatched. | |
| 597 | + | func (o *XdgSurface) handlersForConfigure() []func(serial uint32) { | |
| 598 | + | o.handlersMu.Lock() | |
| 599 | + | defer o.handlersMu.Unlock() | |
| 600 | + | return o.onConfigure | |
| 601 | + | } | |
| 602 | + | ||
| 603 | + | // EventSignature returns the declared event argument types in wire order. | |
| 604 | + | func (o *XdgSurface) EventSignature(opcode uint16) (string, bool) { | |
| 605 | + | switch opcode { | |
| 606 | + | case 0: | |
| 607 | + | return "uint,", true | |
| 608 | + | default: | |
| 609 | + | return "", false | |
| 610 | + | } | |
| 611 | + | } | |
| 612 | + | ||
| 613 | + | // Dispatch decodes one event on xdg_surface and calls the registered handlers. | |
| 614 | + | func (o *XdgSurface) Dispatch(event *wl.Event) { | |
| 615 | + | switch event.Opcode { | |
| 616 | + | case 0: // configure | |
| 617 | + | serial := event.Uint32() | |
| 618 | + | ||
| 619 | + | for i, handler := range o.handlersForConfigure() { | |
| 620 | + | _ = i | |
| 621 | + | handler(serial) | |
| 622 | + | } | |
| 623 | + | default: | |
| 624 | + | } | |
| 625 | + | } | |
| 626 | + | ||
| 627 | + | // XdgToplevelInterface is the protocol interface name. | |
| 628 | + | const XdgToplevelInterface = "xdg_toplevel" | |
| 629 | + | ||
| 630 | + | // XdgToplevel is the generated binding for xdg_toplevel version 7. | |
| 631 | + | type XdgToplevel struct { | |
| 632 | + | wl.BaseProxy | |
| 633 | + | // handlersMu guards the handler slices: a client may register or replace | |
| 634 | + | // handlers while another goroutine dispatches events. | |
| 635 | + | handlersMu sync.Mutex | |
| 636 | + | onConfigure []func(width int32, height int32, states []byte) | |
| 637 | + | onClose []func() | |
| 638 | + | onConfigureBounds []func(width int32, height int32) | |
| 639 | + | onWmCapabilities []func(capabilities []byte) | |
| 640 | + | } | |
| 641 | + | ||
| 642 | + | // NewXdgToplevel returns an unbound XdgToplevel. | |
| 643 | + | func NewXdgToplevel(ctx *wl.Context) *XdgToplevel { | |
| 644 | + | obj := &XdgToplevel{} | |
| 645 | + | obj.SetContext(ctx) | |
| 646 | + | ctx.Register(obj) | |
| 647 | + | return obj | |
| 648 | + | } | |
| 649 | + | ||
| 650 | + | // This request destroys the role surface and unmaps the surface; see "Unmapping" behavior in interface section for details. | |
| 651 | + | func (o *XdgToplevel) Destroy() error { | |
| 652 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 653 | + | return err | |
| 654 | + | } | |
| 655 | + | if err := o.Context().SendDestructor(o, 0); err != nil { | |
| 656 | + | return err | |
| 657 | + | } | |
| 658 | + | return nil | |
| 659 | + | } | |
| 660 | + | ||
| 661 | + | // Set the "parent" of this surface. This surface should be stacked above the parent surface and all other ancestor surfaces. Parent surfaces should be set on dialogs, toolboxes, or other "auxiliary" surfaces, so that the parent is raised when the dialog is raised. Setting a null parent for a child surface unsets its parent. Setting a null parent for a surface which currently has no parent is a no-op. Only mapped surfaces can have child surfaces. Setting a parent which is not mapped is equivalent to setting a null parent. If a surface becomes unmapped, its children's parent is set to the parent of the now-unmapped surface. If the now-unmapped surface has no parent, its children's parent is unset. If the now-unmapped surface becomes mapped again, its parent-child relationship is not restored. The parent toplevel must not be one of the child toplevel's descendants, and the parent must be different from the child toplevel, otherwise the invalid_parent protocol error is raised. | |
| 662 | + | func (o *XdgToplevel) SetParent(parent *XdgToplevel) error { | |
| 663 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 664 | + | return err | |
| 665 | + | } | |
| 666 | + | var arg0 wl.Object | |
| 667 | + | if parent != nil { | |
| 668 | + | arg0 = parent | |
| 669 | + | } | |
| 670 | + | if err := o.Context().SendRequest(o, 1, arg0); err != nil { | |
| 671 | + | return err | |
| 672 | + | } | |
| 673 | + | return nil | |
| 674 | + | } | |
| 675 | + | ||
| 676 | + | // Set a short title for the surface. This string may be used to identify the surface in a task bar, window list, or other user interface elements provided by the compositor. The string must be encoded in UTF-8. | |
| 677 | + | func (o *XdgToplevel) SetTitle(title string) error { | |
| 678 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 679 | + | return err | |
| 680 | + | } | |
| 681 | + | if err := o.Context().SendRequest(o, 2, title); err != nil { | |
| 682 | + | return err | |
| 683 | + | } | |
| 684 | + | return nil | |
| 685 | + | } | |
| 686 | + | ||
| 687 | + | // Set an application identifier for the surface. The app ID identifies the general class of applications to which the surface belongs. The compositor can use this to group multiple surfaces together, or to determine how to launch a new application. For D-Bus activatable applications, the app ID is used as the D-Bus service name. The compositor shell will try to group application surfaces together by their app ID. As a best practice, it is suggested to select app ID's that match the basename of the application's .desktop file. For example, "org.freedesktop.FooViewer" where the .desktop file is "org.freedesktop.FooViewer.desktop". Like other properties, a set_app_id request can be sent after the xdg_toplevel has been mapped to update the property. See the desktop-entry specification [0] for more details on application identifiers and how they relate to well-known D-Bus names and .desktop files. [0] https://standards.freedesktop.org/desktop-entry-spec/ | |
| 688 | + | func (o *XdgToplevel) SetAppId(appId string) error { | |
| 689 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 690 | + | return err | |
| 691 | + | } | |
| 692 | + | if err := o.Context().SendRequest(o, 3, appId); err != nil { | |
| 693 | + | return err | |
| 694 | + | } | |
| 695 | + | return nil | |
| 696 | + | } | |
| 697 | + | ||
| 698 | + | // Clients implementing client-side decorations might want to show a context menu when right-clicking on the decorations, giving the user a menu that they can use to maximize or minimize the window. This request asks the compositor to pop up such a window menu at the given position, relative to the local surface coordinates of the parent surface. There are no guarantees as to what menu items the window menu contains, or even if a window menu will be drawn at all. This request must be used in response to some sort of user action like a button press, key press, or touch down event. | |
| 699 | + | func (o *XdgToplevel) ShowWindowMenu(seat *cross_wl_seat.Seat, serial uint32, x int32, y int32) error { | |
| 700 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 701 | + | return err | |
| 702 | + | } | |
| 703 | + | var arg0 wl.Object | |
| 704 | + | if seat != nil { | |
| 705 | + | arg0 = seat | |
| 706 | + | } | |
| 707 | + | if err := o.Context().SendRequest(o, 4, arg0, serial, x, y); err != nil { | |
| 708 | + | return err | |
| 709 | + | } | |
| 710 | + | return nil | |
| 711 | + | } | |
| 712 | + | ||
| 713 | + | // Start an interactive, user-driven move of the surface. This request must be used in response to some sort of user action like a button press, key press, or touch down event. The passed serial is used to determine the type of interactive move (touch, pointer, etc). The server may ignore move requests depending on the state of the surface (e.g. fullscreen or maximized), or if the passed serial is no longer valid. If triggered, the surface will lose the focus of the device (wl_pointer, wl_touch, etc) used for the move. It is up to the compositor to visually indicate that the move is taking place, such as updating a pointer cursor, during the move. There is no guarantee that the device focus will return when the move is completed. | |
| 714 | + | func (o *XdgToplevel) Move(seat *cross_wl_seat.Seat, serial uint32) error { | |
| 715 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 716 | + | return err | |
| 717 | + | } | |
| 718 | + | var arg0 wl.Object | |
| 719 | + | if seat != nil { | |
| 720 | + | arg0 = seat | |
| 721 | + | } | |
| 722 | + | if err := o.Context().SendRequest(o, 5, arg0, serial); err != nil { | |
| 723 | + | return err | |
| 724 | + | } | |
| 725 | + | return nil | |
| 726 | + | } | |
| 727 | + | ||
| 728 | + | // Start a user-driven, interactive resize of the surface. This request must be used in response to some sort of user action like a button press, key press, or touch down event. The passed serial is used to determine the type of interactive resize (touch, pointer, etc). The server may ignore resize requests depending on the state of the surface (e.g. fullscreen or maximized). If triggered, the client will receive configure events with the "resize" state enum value and the expected sizes. See the "resize" enum value for more details about what is required. The client must also acknowledge configure events using "ack_configure". After the resize is completed, the client will receive another "configure" event without the resize state. If triggered, the surface also will lose the focus of the device (wl_pointer, wl_touch, etc) used for the resize. It is up to the compositor to visually indicate that the resize is taking place, such as updating a pointer cursor, during the resize. There is no guarantee that the device focus will return when the resize is completed. The edges parameter specifies how the surface should be resized, and is one of the values of the resize_edge enum. Values not matching a variant of the enum will cause the invalid_resize_edge protocol error. The compositor may use this information to update the surface position for example when dragging the top left corner. The compositor may also use this information to adapt its behavior, e.g. choose an appropriate cursor image. | |
| 729 | + | func (o *XdgToplevel) Resize(seat *cross_wl_seat.Seat, serial uint32, edges uint32) error { | |
| 730 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 731 | + | return err | |
| 732 | + | } | |
| 733 | + | var arg0 wl.Object | |
| 734 | + | if seat != nil { | |
| 735 | + | arg0 = seat | |
| 736 | + | } | |
| 737 | + | if err := o.Context().SendRequest(o, 6, arg0, serial, edges); err != nil { | |
| 738 | + | return err | |
| 739 | + | } | |
| 740 | + | return nil | |
| 741 | + | } | |
| 742 | + | ||
| 743 | + | // Set a maximum size for the window. The client can specify a maximum size so that the compositor does not try to configure the window beyond this size. The width and height arguments are in window geometry coordinates. See xdg_surface.set_window_geometry. Values set in this way are double-buffered, see wl_surface.commit. The compositor can use this information to allow or disallow different states like maximize or fullscreen and draw accurate animations. Similarly, a tiling window manager may use this information to place and resize client windows in a more effective way. The client should not rely on the compositor to obey the maximum size. The compositor may decide to ignore the values set by the client and request a larger size. If never set, or a value of zero in the request, means that the client has no expected maximum size in the given dimension. As a result, a client wishing to reset the maximum size to an unspecified state can use zero for width and height in the request. Requesting a maximum size to be smaller than the minimum size of a surface is illegal and will result in an invalid_size error. The width and height must be greater than or equal to zero. Using strictly negative values for width or height will result in an invalid_size error. | |
| 744 | + | func (o *XdgToplevel) SetMaxSize(width int32, height int32) error { | |
| 745 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 746 | + | return err | |
| 747 | + | } | |
| 748 | + | if err := o.Context().SendRequest(o, 7, width, height); err != nil { | |
| 749 | + | return err | |
| 750 | + | } | |
| 751 | + | return nil | |
| 752 | + | } | |
| 753 | + | ||
| 754 | + | // Set a minimum size for the window. The client can specify a minimum size so that the compositor does not try to configure the window below this size. The width and height arguments are in window geometry coordinates. See xdg_surface.set_window_geometry. Values set in this way are double-buffered, see wl_surface.commit. The compositor can use this information to allow or disallow different states like maximize or fullscreen and draw accurate animations. Similarly, a tiling window manager may use this information to place and resize client windows in a more effective way. The client should not rely on the compositor to obey the minimum size. The compositor may decide to ignore the values set by the client and request a smaller size. If never set, or a value of zero in the request, means that the client has no expected minimum size in the given dimension. As a result, a client wishing to reset the minimum size to an unspecified state can use zero for width and height in the request. Requesting a minimum size to be larger than the maximum size of a surface is illegal and will result in an invalid_size error. The width and height must be greater than or equal to zero. Using strictly negative values for width and height will result in an invalid_size error. | |
| 755 | + | func (o *XdgToplevel) SetMinSize(width int32, height int32) error { | |
| 756 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 757 | + | return err | |
| 758 | + | } | |
| 759 | + | if err := o.Context().SendRequest(o, 8, width, height); err != nil { | |
| 760 | + | return err | |
| 761 | + | } | |
| 762 | + | return nil | |
| 763 | + | } | |
| 764 | + | ||
| 765 | + | // Maximize the surface. After requesting that the surface should be maximized, the compositor will respond by emitting a configure event. Whether this configure actually sets the window maximized is subject to compositor policies. The client must then update its content, drawing in the configured state. The client must also acknowledge the configure when committing the new content (see ack_configure). It is up to the compositor to decide how and where to maximize the surface, for example which output and what region of the screen should be used. If the surface was already maximized, the compositor will still emit a configure event with the "maximized" state. If the surface is in a fullscreen state, this request has no direct effect. It may alter the state the surface is returned to when unmaximized unless overridden by the compositor. | |
| 766 | + | func (o *XdgToplevel) SetMaximized() error { | |
| 767 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 768 | + | return err | |
| 769 | + | } | |
| 770 | + | if err := o.Context().SendRequest(o, 9); err != nil { | |
| 771 | + | return err | |
| 772 | + | } | |
| 773 | + | return nil | |
| 774 | + | } | |
| 775 | + | ||
| 776 | + | // Unmaximize the surface. After requesting that the surface should be unmaximized, the compositor will respond by emitting a configure event. Whether this actually un-maximizes the window is subject to compositor policies. If available and applicable, the compositor will include the window geometry dimensions the window had prior to being maximized in the configure event. The client must then update its content, drawing it in the configured state. The client must also acknowledge the configure when committing the new content (see ack_configure). It is up to the compositor to position the surface after it was unmaximized; usually the position the surface had before maximizing, if applicable. If the surface was already not maximized, the compositor will still emit a configure event without the "maximized" state. If the surface is in a fullscreen state, this request has no direct effect. It may alter the state the surface is returned to when unmaximized unless overridden by the compositor. | |
| 777 | + | func (o *XdgToplevel) UnsetMaximized() error { | |
| 778 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 779 | + | return err | |
| 780 | + | } | |
| 781 | + | if err := o.Context().SendRequest(o, 10); err != nil { | |
| 782 | + | return err | |
| 783 | + | } | |
| 784 | + | return nil | |
| 785 | + | } | |
| 786 | + | ||
| 787 | + | // Make the surface fullscreen. After requesting that the surface should be fullscreened, the compositor will respond by emitting a configure event. Whether the client is actually put into a fullscreen state is subject to compositor policies. The client must also acknowledge the configure when committing the new content (see ack_configure). The output passed by the request indicates the client's preference as to which display it should be set fullscreen on. If this value is NULL, it's up to the compositor to choose which display will be used to map this surface. If the surface doesn't cover the whole output, the compositor will position the surface in the center of the output and compensate with border fill covering the rest of the output. The content of the border fill is undefined, but should be assumed to be in some way that attempts to blend into the surrounding area (e.g. solid black). If the fullscreened surface is not opaque, the compositor must make sure that other screen content not part of the same surface tree (made up of subsurfaces, popups or similarly coupled surfaces) are not visible below the fullscreened surface. | |
| 788 | + | func (o *XdgToplevel) SetFullscreen(output *cross_wl_output.Output) error { | |
| 789 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 790 | + | return err | |
| 791 | + | } | |
| 792 | + | var arg0 wl.Object | |
| 793 | + | if output != nil { | |
| 794 | + | arg0 = output | |
| 795 | + | } | |
| 796 | + | if err := o.Context().SendRequest(o, 11, arg0); err != nil { | |
| 797 | + | return err | |
| 798 | + | } | |
| 799 | + | return nil | |
| 800 | + | } | |
| 801 | + | ||
| 802 | + | // Make the surface no longer fullscreen. After requesting that the surface should be unfullscreened, the compositor will respond by emitting a configure event. Whether this actually removes the fullscreen state of the client is subject to compositor policies. Making a surface unfullscreen sets states for the surface based on the following: * the state(s) it may have had before becoming fullscreen * any state(s) decided by the compositor * any state(s) requested by the client while the surface was fullscreen The compositor may include the previous window geometry dimensions in the configure event, if applicable. The client must also acknowledge the configure when committing the new content (see ack_configure). | |
| 803 | + | func (o *XdgToplevel) UnsetFullscreen() error { | |
| 804 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 805 | + | return err | |
| 806 | + | } | |
| 807 | + | if err := o.Context().SendRequest(o, 12); err != nil { | |
| 808 | + | return err | |
| 809 | + | } | |
| 810 | + | return nil | |
| 811 | + | } | |
| 812 | + | ||
| 813 | + | // Request that the compositor minimize your surface. There is no way to know if the surface is currently minimized, nor is there any way to unset minimization on this surface. If you are looking to throttle redrawing when minimized, please instead use the wl_surface.frame event for this, as this will also work with live previews on windows in Alt-Tab, Expose or similar compositor features. | |
| 814 | + | func (o *XdgToplevel) SetMinimized() error { | |
| 815 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 816 | + | return err | |
| 817 | + | } | |
| 818 | + | if err := o.Context().SendRequest(o, 13); err != nil { | |
| 819 | + | return err | |
| 820 | + | } | |
| 821 | + | return nil | |
| 822 | + | } | |
| 823 | + | ||
| 824 | + | // OnConfigure registers a handler for the configure event. | |
| 825 | + | // | |
| 826 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 827 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 828 | + | func (o *XdgToplevel) OnConfigure(handler func(width int32, height int32, states []byte)) { | |
| 829 | + | if handler == nil { | |
| 830 | + | return | |
| 831 | + | } | |
| 832 | + | o.handlersMu.Lock() | |
| 833 | + | defer o.handlersMu.Unlock() | |
| 834 | + | o.onConfigure = append(o.onConfigure, handler) | |
| 835 | + | } | |
| 836 | + | ||
| 837 | + | // handlersForConfigure returns the handlers registered for configure, taken | |
| 838 | + | // under the lock so a handler may register another handler while events are | |
| 839 | + | // being dispatched. | |
| 840 | + | func (o *XdgToplevel) handlersForConfigure() []func(width int32, height int32, states []byte) { | |
| 841 | + | o.handlersMu.Lock() | |
| 842 | + | defer o.handlersMu.Unlock() | |
| 843 | + | return o.onConfigure | |
| 844 | + | } | |
| 845 | + | ||
| 846 | + | // OnClose registers a handler for the close event. | |
| 847 | + | // | |
| 848 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 849 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 850 | + | func (o *XdgToplevel) OnClose(handler func()) { | |
| 851 | + | if handler == nil { | |
| 852 | + | return | |
| 853 | + | } | |
| 854 | + | o.handlersMu.Lock() | |
| 855 | + | defer o.handlersMu.Unlock() | |
| 856 | + | o.onClose = append(o.onClose, handler) | |
| 857 | + | } | |
| 858 | + | ||
| 859 | + | // handlersForClose returns the handlers registered for close, taken | |
| 860 | + | // under the lock so a handler may register another handler while events are | |
| 861 | + | // being dispatched. | |
| 862 | + | func (o *XdgToplevel) handlersForClose() []func() { | |
| 863 | + | o.handlersMu.Lock() | |
| 864 | + | defer o.handlersMu.Unlock() | |
| 865 | + | return o.onClose | |
| 866 | + | } | |
| 867 | + | ||
| 868 | + | // OnConfigureBounds registers a handler for the configure_bounds event. | |
| 869 | + | // | |
| 870 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 871 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 872 | + | func (o *XdgToplevel) OnConfigureBounds(handler func(width int32, height int32)) { | |
| 873 | + | if handler == nil { | |
| 874 | + | return | |
| 875 | + | } | |
| 876 | + | o.handlersMu.Lock() | |
| 877 | + | defer o.handlersMu.Unlock() | |
| 878 | + | o.onConfigureBounds = append(o.onConfigureBounds, handler) | |
| 879 | + | } | |
| 880 | + | ||
| 881 | + | // handlersForConfigureBounds returns the handlers registered for configure_bounds, taken | |
| 882 | + | // under the lock so a handler may register another handler while events are | |
| 883 | + | // being dispatched. | |
| 884 | + | func (o *XdgToplevel) handlersForConfigureBounds() []func(width int32, height int32) { | |
| 885 | + | o.handlersMu.Lock() | |
| 886 | + | defer o.handlersMu.Unlock() | |
| 887 | + | return o.onConfigureBounds | |
| 888 | + | } | |
| 889 | + | ||
| 890 | + | // OnWmCapabilities registers a handler for the wm_capabilities event. | |
| 891 | + | // | |
| 892 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 893 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 894 | + | func (o *XdgToplevel) OnWmCapabilities(handler func(capabilities []byte)) { | |
| 895 | + | if handler == nil { | |
| 896 | + | return | |
| 897 | + | } | |
| 898 | + | o.handlersMu.Lock() | |
| 899 | + | defer o.handlersMu.Unlock() | |
| 900 | + | o.onWmCapabilities = append(o.onWmCapabilities, handler) | |
| 901 | + | } | |
| 902 | + | ||
| 903 | + | // handlersForWmCapabilities returns the handlers registered for wm_capabilities, taken | |
| 904 | + | // under the lock so a handler may register another handler while events are | |
| 905 | + | // being dispatched. | |
| 906 | + | func (o *XdgToplevel) handlersForWmCapabilities() []func(capabilities []byte) { | |
| 907 | + | o.handlersMu.Lock() | |
| 908 | + | defer o.handlersMu.Unlock() | |
| 909 | + | return o.onWmCapabilities | |
| 910 | + | } | |
| 911 | + | ||
| 912 | + | // EventSignature returns the declared event argument types in wire order. | |
| 913 | + | func (o *XdgToplevel) EventSignature(opcode uint16) (string, bool) { | |
| 914 | + | switch opcode { | |
| 915 | + | case 0: | |
| 916 | + | return "int,int,array,", true | |
| 917 | + | case 1: | |
| 918 | + | return "", true | |
| 919 | + | case 2: | |
| 920 | + | return "int,int,", true | |
| 921 | + | case 3: | |
| 922 | + | return "array,", true | |
| 923 | + | default: | |
| 924 | + | return "", false | |
| 925 | + | } | |
| 926 | + | } | |
| 927 | + | ||
| 928 | + | // Dispatch decodes one event on xdg_toplevel and calls the registered handlers. | |
| 929 | + | func (o *XdgToplevel) Dispatch(event *wl.Event) { | |
| 930 | + | switch event.Opcode { | |
| 931 | + | case 0: // configure | |
| 932 | + | width := event.Int32() | |
| 933 | + | height := event.Int32() | |
| 934 | + | states := event.Array() | |
| 935 | + | ||
| 936 | + | for i, handler := range o.handlersForConfigure() { | |
| 937 | + | _ = i | |
| 938 | + | handler(width, height, states) | |
| 939 | + | } | |
| 940 | + | case 1: // close | |
| 941 | + | ||
| 942 | + | for i, handler := range o.handlersForClose() { | |
| 943 | + | _ = i | |
| 944 | + | handler() | |
| 945 | + | } | |
| 946 | + | case 2: // configure_bounds | |
| 947 | + | width := event.Int32() | |
| 948 | + | height := event.Int32() | |
| 949 | + | ||
| 950 | + | for i, handler := range o.handlersForConfigureBounds() { | |
| 951 | + | _ = i | |
| 952 | + | handler(width, height) | |
| 953 | + | } | |
| 954 | + | case 3: // wm_capabilities | |
| 955 | + | capabilities := event.Array() | |
| 956 | + | ||
| 957 | + | for i, handler := range o.handlersForWmCapabilities() { | |
| 958 | + | _ = i | |
| 959 | + | handler(capabilities) | |
| 960 | + | } | |
| 961 | + | default: | |
| 962 | + | } | |
| 963 | + | } | |
| 964 | + | ||
| 965 | + | // XdgPopupInterface is the protocol interface name. | |
| 966 | + | const XdgPopupInterface = "xdg_popup" | |
| 967 | + | ||
| 968 | + | // XdgPopup is the generated binding for xdg_popup version 7. | |
| 969 | + | type XdgPopup struct { | |
| 970 | + | wl.BaseProxy | |
| 971 | + | // handlersMu guards the handler slices: a client may register or replace | |
| 972 | + | // handlers while another goroutine dispatches events. | |
| 973 | + | handlersMu sync.Mutex | |
| 974 | + | onConfigure []func(x int32, y int32, width int32, height int32) | |
| 975 | + | onPopupDone []func() | |
| 976 | + | onRepositioned []func(token uint32) | |
| 977 | + | } | |
| 978 | + | ||
| 979 | + | // NewXdgPopup returns an unbound XdgPopup. | |
| 980 | + | func NewXdgPopup(ctx *wl.Context) *XdgPopup { | |
| 981 | + | obj := &XdgPopup{} | |
| 982 | + | obj.SetContext(ctx) | |
| 983 | + | ctx.Register(obj) | |
| 984 | + | return obj | |
| 985 | + | } | |
| 986 | + | ||
| 987 | + | // This destroys the popup. Explicitly destroying the xdg_popup object will also dismiss the popup, and unmap the surface. If this xdg_popup is not the "topmost" popup, the xdg_wm_base.not_the_topmost_popup protocol error will be sent. | |
| 988 | + | func (o *XdgPopup) Destroy() error { | |
| 989 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 990 | + | return err | |
| 991 | + | } | |
| 992 | + | if err := o.Context().SendDestructor(o, 0); err != nil { | |
| 993 | + | return err | |
| 994 | + | } | |
| 995 | + | return nil | |
| 996 | + | } | |
| 997 | + | ||
| 998 | + | // This request makes the created popup take an explicit grab. An explicit grab will be dismissed when the user dismisses the popup, or when the client destroys the xdg_popup. This can be done by the user clicking outside the surface, using the keyboard, or even locking the screen through closing the lid or a timeout. If the compositor denies the grab, the popup will be immediately dismissed. This request must be used in response to some sort of user action like a button press, key press, or touch down event. The serial number of the event should be passed as 'serial'. The parent of a grabbing popup must either be an xdg_toplevel surface or another xdg_popup with an explicit grab. If the parent is another xdg_popup it means that the popups are nested, with this popup now being the topmost popup. Nested popups must be destroyed in the reverse order they were created in, e.g. the only popup you are allowed to destroy at all times is the topmost one. When compositors choose to dismiss a popup, they may dismiss every nested grabbing popup as well. When a compositor dismisses popups, it will follow the same dismissing order as required from the client. If the topmost grabbing popup is destroyed, the grab will be returned to the parent of the popup, if that parent previously had an explicit grab. If the parent is a grabbing popup which has already been dismissed, this popup will be immediately dismissed. If the parent is a popup that did not take an explicit grab, an error will be raised. During a popup grab, the client owning the grab will receive pointer and touch events for all their surfaces as normal (similar to an "owner-events" grab in X11 parlance), while the top most grabbing popup will always have keyboard focus. | |
| 999 | + | func (o *XdgPopup) Grab(seat *cross_wl_seat.Seat, serial uint32) error { | |
| 1000 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 1001 | + | return err | |
| 1002 | + | } | |
| 1003 | + | var arg0 wl.Object | |
| 1004 | + | if seat != nil { | |
| 1005 | + | arg0 = seat | |
| 1006 | + | } | |
| 1007 | + | if err := o.Context().SendRequest(o, 1, arg0, serial); err != nil { | |
| 1008 | + | return err | |
| 1009 | + | } | |
| 1010 | + | return nil | |
| 1011 | + | } | |
| 1012 | + | ||
| 1013 | + | // Reposition an already-mapped popup. The popup will be placed given the details in the passed xdg_positioner object, and a xdg_popup.repositioned followed by xdg_popup.configure and xdg_surface.configure will be emitted in response. Any parameters set by the previous positioner will be discarded. The passed token will be sent in the corresponding xdg_popup.repositioned event. The new popup position will not take effect until the corresponding configure event is acknowledged by the client. See xdg_popup.repositioned for details. The token itself is opaque, and has no other special meaning. If multiple reposition requests are sent, the compositor may skip all but the last one. If the popup is repositioned in response to a configure event for its parent, the client should send an xdg_positioner.set_parent_configure and possibly an xdg_positioner.set_parent_size request to allow the compositor to properly constrain the popup. If the popup is repositioned together with a parent that is being resized, but not in response to a configure event, the client should send an xdg_positioner.set_parent_size request. | |
| 1014 | + | func (o *XdgPopup) Reposition(positioner *XdgPositioner, token uint32) error { | |
| 1015 | + | if err := o.Context().CheckProxy(o); err != nil { | |
| 1016 | + | return err | |
| 1017 | + | } | |
| 1018 | + | var arg0 wl.Object | |
| 1019 | + | if positioner != nil { | |
| 1020 | + | arg0 = positioner | |
| 1021 | + | } | |
| 1022 | + | if err := o.Context().SendRequest(o, 2, arg0, token); err != nil { | |
| 1023 | + | return err | |
| 1024 | + | } | |
| 1025 | + | return nil | |
| 1026 | + | } | |
| 1027 | + | ||
| 1028 | + | // OnConfigure registers a handler for the configure event. | |
| 1029 | + | // | |
| 1030 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 1031 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 1032 | + | func (o *XdgPopup) OnConfigure(handler func(x int32, y int32, width int32, height int32)) { | |
| 1033 | + | if handler == nil { | |
| 1034 | + | return | |
| 1035 | + | } | |
| 1036 | + | o.handlersMu.Lock() | |
| 1037 | + | defer o.handlersMu.Unlock() | |
| 1038 | + | o.onConfigure = append(o.onConfigure, handler) | |
| 1039 | + | } | |
| 1040 | + | ||
| 1041 | + | // handlersForConfigure returns the handlers registered for configure, taken | |
| 1042 | + | // under the lock so a handler may register another handler while events are | |
| 1043 | + | // being dispatched. | |
| 1044 | + | func (o *XdgPopup) handlersForConfigure() []func(x int32, y int32, width int32, height int32) { | |
| 1045 | + | o.handlersMu.Lock() | |
| 1046 | + | defer o.handlersMu.Unlock() | |
| 1047 | + | return o.onConfigure | |
| 1048 | + | } | |
| 1049 | + | ||
| 1050 | + | // OnPopupDone registers a handler for the popup_done event. | |
| 1051 | + | // | |
| 1052 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 1053 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 1054 | + | func (o *XdgPopup) OnPopupDone(handler func()) { | |
| 1055 | + | if handler == nil { | |
| 1056 | + | return | |
| 1057 | + | } | |
| 1058 | + | o.handlersMu.Lock() | |
| 1059 | + | defer o.handlersMu.Unlock() | |
| 1060 | + | o.onPopupDone = append(o.onPopupDone, handler) | |
| 1061 | + | } | |
| 1062 | + | ||
| 1063 | + | // handlersForPopupDone returns the handlers registered for popup_done, taken | |
| 1064 | + | // under the lock so a handler may register another handler while events are | |
| 1065 | + | // being dispatched. | |
| 1066 | + | func (o *XdgPopup) handlersForPopupDone() []func() { | |
| 1067 | + | o.handlersMu.Lock() | |
| 1068 | + | defer o.handlersMu.Unlock() | |
| 1069 | + | return o.onPopupDone | |
| 1070 | + | } | |
| 1071 | + | ||
| 1072 | + | // OnRepositioned registers a handler for the repositioned event. | |
| 1073 | + | // | |
| 1074 | + | // Handlers are appended, so a second call adds another handler rather than | |
| 1075 | + | // replacing the first. Registration is safe while another goroutine dispatches. | |
| 1076 | + | func (o *XdgPopup) OnRepositioned(handler func(token uint32)) { | |
| 1077 | + | if handler == nil { | |
| 1078 | + | return | |
| 1079 | + | } | |
| 1080 | + | o.handlersMu.Lock() | |
| 1081 | + | defer o.handlersMu.Unlock() | |
| 1082 | + | o.onRepositioned = append(o.onRepositioned, handler) | |
| 1083 | + | } | |
| 1084 | + | ||
| 1085 | + | // handlersForRepositioned returns the handlers registered for repositioned, taken | |
| 1086 | + | // under the lock so a handler may register another handler while events are | |
| 1087 | + | // being dispatched. | |
| 1088 | + | func (o *XdgPopup) handlersForRepositioned() []func(token uint32) { | |
| 1089 | + | o.handlersMu.Lock() | |
| 1090 | + | defer o.handlersMu.Unlock() | |
| 1091 | + | return o.onRepositioned | |
| 1092 | + | } | |
| 1093 | + | ||
| 1094 | + | // EventSignature returns the declared event argument types in wire order. | |
| 1095 | + | func (o *XdgPopup) EventSignature(opcode uint16) (string, bool) { | |
| 1096 | + | switch opcode { | |
| 1097 | + | case 0: | |
| 1098 | + | return "int,int,int,int,", true | |
| 1099 | + | case 1: | |
| 1100 | + | return "", true | |
| 1101 | + | case 2: | |
| 1102 | + | return "uint,", true | |
| 1103 | + | default: | |
| 1104 | + | return "", false | |
| 1105 | + | } | |
| 1106 | + | } | |
| 1107 | + | ||
| 1108 | + | // Dispatch decodes one event on xdg_popup and calls the registered handlers. | |
| 1109 | + | func (o *XdgPopup) Dispatch(event *wl.Event) { | |
| 1110 | + | switch event.Opcode { | |
| 1111 | + | case 0: // configure | |
| 1112 | + | x := event.Int32() | |
| 1113 | + | y := event.Int32() | |
| 1114 | + | width := event.Int32() | |
| 1115 | + | height := event.Int32() | |
| 1116 | + | ||
| 1117 | + | for i, handler := range o.handlersForConfigure() { | |
| 1118 | + | _ = i | |
| 1119 | + | handler(x, y, width, height) | |
| 1120 | + | } | |
| 1121 | + | case 1: // popup_done | |
| 1122 | + | ||
| 1123 | + | for i, handler := range o.handlersForPopupDone() { | |
| 1124 | + | _ = i | |
| 1125 | + | handler() | |
| 1126 | + | } | |
| 1127 | + | case 2: // repositioned | |
| 1128 | + | token := event.Uint32() | |
| 1129 | + | ||
| 1130 | + | for i, handler := range o.handlersForRepositioned() { | |
| 1131 | + | _ = i | |
| 1132 | + | handler(token) | |
| 1133 | + | } | |
| 1134 | + | default: | |
| 1135 | + | } | |
| 1136 | + | } | |