os_wayland.go raw
1 // SPDX-License-Identifier: Unlicense OR MIT
2
3 // +build linux,!android,!nowayland freebsd
4
5 package wm
6
7 import (
8 "bytes"
9 "errors"
10 "fmt"
11 "image"
12 "io"
13 "io/ioutil"
14 "math"
15 "os"
16 "os/exec"
17 "strconv"
18 "sync"
19 "time"
20 "unsafe"
21
22 syscall "golang.org/x/sys/unix"
23
24 "github.com/p9c/p9/pkg/gel/gio/app/internal/xkb"
25 "github.com/p9c/p9/pkg/gel/gio/f32"
26 "github.com/p9c/p9/pkg/gel/gio/internal/fling"
27 "github.com/p9c/p9/pkg/gel/gio/io/clipboard"
28 "github.com/p9c/p9/pkg/gel/gio/io/key"
29 "github.com/p9c/p9/pkg/gel/gio/io/pointer"
30 "github.com/p9c/p9/pkg/gel/gio/io/system"
31 "github.com/p9c/p9/pkg/gel/gio/unit"
32 )
33
34 // Use wayland-scanner to generate glue code for the xdg-shell and xdg-decoration extensions.
35 //go:generate wayland-scanner client-header /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml wayland_xdg_shell.h
36 //go:generate wayland-scanner private-code /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml wayland_xdg_shell.c
37
38 //go:generate wayland-scanner client-header /usr/share/wayland-protocols/unstable/text-input/text-input-unstable-v3.xml wayland_text_input.h
39 //go:generate wayland-scanner private-code /usr/share/wayland-protocols/unstable/text-input/text-input-unstable-v3.xml wayland_text_input.c
40
41 //go:generate wayland-scanner client-header /usr/share/wayland-protocols/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml wayland_xdg_decoration.h
42 //go:generate wayland-scanner private-code /usr/share/wayland-protocols/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml wayland_xdg_decoration.c
43
44 //go:generate sed -i "1s;^;// +build linux,!android,!nowayland freebsd\\n\\n;" wayland_xdg_shell.c
45 //go:generate sed -i "1s;^;// +build linux,!android,!nowayland freebsd\\n\\n;" wayland_xdg_decoration.c
46 //go:generate sed -i "1s;^;// +build linux,!android,!nowayland freebsd\\n\\n;" wayland_text_input.c
47
48 /*
49 #cgo linux pkg-config: wayland-client wayland-cursor
50 #cgo freebsd openbsd LDFLAGS: -lwayland-client -lwayland-cursor
51 #cgo freebsd CFLAGS: -I/usr/local/include
52 #cgo freebsd LDFLAGS: -L/usr/local/lib
53
54 #include <stdlib.h>
55 #include <wayland-client.h>
56 #include <wayland-cursor.h>
57 #include "wayland_text_input.h"
58 #include "wayland_xdg_shell.h"
59 #include "wayland_xdg_decoration.h"
60
61 extern const struct wl_registry_listener gio_registry_listener;
62 extern const struct wl_surface_listener gio_surface_listener;
63 extern const struct xdg_surface_listener gio_xdg_surface_listener;
64 extern const struct xdg_toplevel_listener gio_xdg_toplevel_listener;
65 extern const struct xdg_wm_base_listener gio_xdg_wm_base_listener;
66 extern const struct wl_callback_listener gio_callback_listener;
67 extern const struct wl_output_listener gio_output_listener;
68 extern const struct wl_seat_listener gio_seat_listener;
69 extern const struct wl_pointer_listener gio_pointer_listener;
70 extern const struct wl_touch_listener gio_touch_listener;
71 extern const struct wl_keyboard_listener gio_keyboard_listener;
72 extern const struct zwp_text_input_v3_listener gio_zwp_text_input_v3_listener;
73 extern const struct wl_data_device_listener gio_data_device_listener;
74 extern const struct wl_data_offer_listener gio_data_offer_listener;
75 extern const struct wl_data_source_listener gio_data_source_listener;
76 */
77 import "C"
78
79 type wlDisplay struct {
80 disp *C.struct_wl_display
81 reg *C.struct_wl_registry
82 compositor *C.struct_wl_compositor
83 wm *C.struct_xdg_wm_base
84 imm *C.struct_zwp_text_input_manager_v3
85 shm *C.struct_wl_shm
86 dataDeviceManager *C.struct_wl_data_device_manager
87 decor *C.struct_zxdg_decoration_manager_v1
88 seat *wlSeat
89 xkb *xkb.Context
90 outputMap map[C.uint32_t]*C.struct_wl_output
91 outputConfig map[*C.struct_wl_output]*wlOutput
92
93 // Notification pipe fds.
94 notify struct {
95 read, write int
96 }
97
98 repeat repeatState
99 }
100
101 type wlSeat struct {
102 disp *wlDisplay
103 seat *C.struct_wl_seat
104 name C.uint32_t
105 pointer *C.struct_wl_pointer
106 touch *C.struct_wl_touch
107 keyboard *C.struct_wl_keyboard
108 im *C.struct_zwp_text_input_v3
109
110 // The most recent input serial.
111 serial C.uint32_t
112
113 pointerFocus *window
114 keyboardFocus *window
115 touchFoci map[C.int32_t]*window
116
117 // Clipboard support.
118 dataDev *C.struct_wl_data_device
119 // offers is a map from active wl_data_offers to
120 // the list of mime types they support.
121 offers map[*C.struct_wl_data_offer][]string
122 // clipboard is the wl_data_offer for the clipboard.
123 clipboard *C.struct_wl_data_offer
124 // mimeType is the chosen mime type of clipboard.
125 mimeType string
126 // source represents the clipboard content of the most recent
127 // clipboard write, if any.
128 source *C.struct_wl_data_source
129 // content is the data belonging to source.
130 content []byte
131 }
132
133 type repeatState struct {
134 rate int
135 delay time.Duration
136
137 key uint32
138 win Callbacks
139 stopC chan struct{}
140
141 start time.Duration
142 last time.Duration
143 mu sync.Mutex
144 now time.Duration
145 }
146
147 type window struct {
148 w Callbacks
149 disp *wlDisplay
150 surf *C.struct_wl_surface
151 wmSurf *C.struct_xdg_surface
152 topLvl *C.struct_xdg_toplevel
153 decor *C.struct_zxdg_toplevel_decoration_v1
154 ppdp, ppsp float32
155 scroll struct {
156 time time.Duration
157 steps image.Point
158 dist f32.Point
159 }
160 pointerBtns pointer.Buttons
161 lastPos f32.Point
162 lastTouch f32.Point
163
164 cursor struct {
165 theme *C.struct_wl_cursor_theme
166 cursor *C.struct_wl_cursor
167 surf *C.struct_wl_surface
168 }
169
170 fling struct {
171 yExtrapolation fling.Extrapolation
172 xExtrapolation fling.Extrapolation
173 anim fling.Animation
174 start bool
175 dir f32.Point
176 }
177
178 stage system.Stage
179 dead bool
180 lastFrameCallback *C.struct_wl_callback
181
182 mu sync.Mutex
183 animating bool
184 opts *Options
185 needAck bool
186 // The most recent configure serial waiting to be ack'ed.
187 serial C.uint32_t
188 width int
189 height int
190 newScale bool
191 scale int
192 // readClipboard tracks whether a ClipboardEvent is requested.
193 readClipboard bool
194 // writeClipboard is set whenever a clipboard write is requested.
195 writeClipboard *string
196 }
197
198 type poller struct {
199 pollfds [2]syscall.PollFd
200 // buf is scratch space for draining the notification pipe.
201 buf [100]byte
202 }
203
204 type wlOutput struct {
205 width int
206 height int
207 physWidth int
208 physHeight int
209 transform C.int32_t
210 scale int
211 windows []*window
212 }
213
214 // callbackMap maps Wayland native handles to corresponding Go
215 // references. It is necessary because the the Wayland client API
216 // forces the use of callbacks and storing pointers to Go values
217 // in C is forbidden.
218 var callbackMap sync.Map
219
220 // clipboardMimeTypes is a list of supported clipboard mime types, in
221 // order of preference.
222 var clipboardMimeTypes = []string{"text/plain;charset=utf8", "UTF8_STRING", "text/plain", "TEXT", "STRING"}
223
224 func init() {
225 wlDriver = newWLWindow
226 }
227
228 func newWLWindow(window Callbacks, opts *Options) error {
229 d, err := newWLDisplay()
230 if err != nil {
231 return err
232 }
233 w, err := d.createNativeWindow(opts)
234 if err != nil {
235 d.destroy()
236 return err
237 }
238 w.w = window
239 go func() {
240 defer d.destroy()
241 defer w.destroy()
242 w.w.SetDriver(w)
243 if err := w.loop(); err != nil {
244 panic(err)
245 }
246 }()
247 return nil
248 }
249
250 func (d *wlDisplay) writeClipboard(content []byte) error {
251 s := d.seat
252 if s == nil {
253 return nil
254 }
255 // Clear old offer.
256 if s.source != nil {
257 C.wl_data_source_destroy(s.source)
258 s.source = nil
259 s.content = nil
260 }
261 if d.dataDeviceManager == nil || s.dataDev == nil {
262 return nil
263 }
264 s.content = content
265 s.source = C.wl_data_device_manager_create_data_source(d.dataDeviceManager)
266 C.wl_data_source_add_listener(s.source, &C.gio_data_source_listener, unsafe.Pointer(s.seat))
267 for _, mime := range clipboardMimeTypes {
268 C.wl_data_source_offer(s.source, C.CString(mime))
269 }
270 C.wl_data_device_set_selection(s.dataDev, s.source, s.serial)
271 return nil
272 }
273
274 func (d *wlDisplay) readClipboard() (io.ReadCloser, error) {
275 s := d.seat
276 if s == nil {
277 return nil, nil
278 }
279 if s.clipboard == nil {
280 return nil, nil
281 }
282 r, w, err := os.Pipe()
283 if err != nil {
284 return nil, err
285 }
286 // wl_data_offer_receive performs and implicit dup(2) of the write end
287 // of the pipe. Close our version.
288 defer w.Close()
289 cmimeType := C.CString(s.mimeType)
290 defer C.free(unsafe.Pointer(cmimeType))
291 C.wl_data_offer_receive(s.clipboard, cmimeType, C.int(w.Fd()))
292 return r, nil
293 }
294
295 func (d *wlDisplay) createNativeWindow(opts *Options) (*window, error) {
296 if d.compositor == nil {
297 return nil, errors.New("wayland: no compositor available")
298 }
299 if d.wm == nil {
300 return nil, errors.New("wayland: no xdg_wm_base available")
301 }
302 if d.shm == nil {
303 return nil, errors.New("wayland: no wl_shm available")
304 }
305 if len(d.outputMap) == 0 {
306 return nil, errors.New("wayland: no outputs available")
307 }
308 var scale int
309 for _, conf := range d.outputConfig {
310 if s := conf.scale; s > scale {
311 scale = s
312 }
313 }
314 ppdp := detectUIScale()
315
316 w := &window{
317 disp: d,
318 scale: scale,
319 newScale: scale != 1,
320 ppdp: ppdp,
321 ppsp: ppdp,
322 }
323 w.surf = C.wl_compositor_create_surface(d.compositor)
324 if w.surf == nil {
325 w.destroy()
326 return nil, errors.New("wayland: wl_compositor_create_surface failed")
327 }
328 callbackStore(unsafe.Pointer(w.surf), w)
329 w.wmSurf = C.xdg_wm_base_get_xdg_surface(d.wm, w.surf)
330 if w.wmSurf == nil {
331 w.destroy()
332 return nil, errors.New("wayland: xdg_wm_base_get_xdg_surface failed")
333 }
334 w.topLvl = C.xdg_surface_get_toplevel(w.wmSurf)
335 if w.topLvl == nil {
336 w.destroy()
337 return nil, errors.New("wayland: xdg_surface_get_toplevel failed")
338 }
339 w.cursor.theme = C.wl_cursor_theme_load(nil, 32, d.shm)
340 if w.cursor.theme == nil {
341 w.destroy()
342 return nil, errors.New("wayland: wl_cursor_theme_load failed")
343 }
344 cname := C.CString("left_ptr")
345 defer C.free(unsafe.Pointer(cname))
346 w.cursor.cursor = C.wl_cursor_theme_get_cursor(w.cursor.theme, cname)
347 if w.cursor.cursor == nil {
348 w.destroy()
349 return nil, errors.New("wayland: wl_cursor_theme_get_cursor failed")
350 }
351 w.cursor.surf = C.wl_compositor_create_surface(d.compositor)
352 if w.cursor.surf == nil {
353 w.destroy()
354 return nil, errors.New("wayland: wl_compositor_create_surface failed")
355 }
356 C.xdg_wm_base_add_listener(d.wm, &C.gio_xdg_wm_base_listener, unsafe.Pointer(w.surf))
357 C.wl_surface_add_listener(w.surf, &C.gio_surface_listener, unsafe.Pointer(w.surf))
358 C.xdg_surface_add_listener(w.wmSurf, &C.gio_xdg_surface_listener, unsafe.Pointer(w.surf))
359 C.xdg_toplevel_add_listener(w.topLvl, &C.gio_xdg_toplevel_listener, unsafe.Pointer(w.surf))
360
361 w.setOptions(opts)
362
363 if d.decor != nil {
364 // Request server side decorations.
365 w.decor = C.zxdg_decoration_manager_v1_get_toplevel_decoration(d.decor, w.topLvl)
366 C.zxdg_toplevel_decoration_v1_set_mode(w.decor, C.ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE)
367 }
368 w.updateOpaqueRegion()
369 C.wl_surface_commit(w.surf)
370 return w, nil
371 }
372
373 func callbackDelete(k unsafe.Pointer) {
374 callbackMap.Delete(k)
375 }
376
377 func callbackStore(k unsafe.Pointer, v interface{}) {
378 callbackMap.Store(k, v)
379 }
380
381 func callbackLoad(k unsafe.Pointer) interface{} {
382 v, exists := callbackMap.Load(k)
383 if !exists {
384 panic("missing callback entry")
385 }
386 return v
387 }
388
389 //export gio_onSeatCapabilities
390 func gio_onSeatCapabilities(data unsafe.Pointer, seat *C.struct_wl_seat, caps C.uint32_t) {
391 s := callbackLoad(data).(*wlSeat)
392 s.updateCaps(caps)
393 }
394
395 // flushOffers remove all wl_data_offers that isn't the clipboard
396 // content.
397 func (s *wlSeat) flushOffers() {
398 for o := range s.offers {
399 if o == s.clipboard {
400 continue
401 }
402 // We're only interested in clipboard offers.
403 delete(s.offers, o)
404 callbackDelete(unsafe.Pointer(o))
405 C.wl_data_offer_destroy(o)
406 }
407 }
408
409 func (s *wlSeat) destroy() {
410 if s.source != nil {
411 C.wl_data_source_destroy(s.source)
412 s.source = nil
413 }
414 if s.im != nil {
415 C.zwp_text_input_v3_destroy(s.im)
416 s.im = nil
417 }
418 if s.pointer != nil {
419 C.wl_pointer_release(s.pointer)
420 }
421 if s.touch != nil {
422 C.wl_touch_release(s.touch)
423 }
424 if s.keyboard != nil {
425 C.wl_keyboard_release(s.keyboard)
426 }
427 s.clipboard = nil
428 s.flushOffers()
429 if s.dataDev != nil {
430 C.wl_data_device_release(s.dataDev)
431 }
432 if s.seat != nil {
433 callbackDelete(unsafe.Pointer(s.seat))
434 C.wl_seat_release(s.seat)
435 }
436 }
437
438 func (s *wlSeat) updateCaps(caps C.uint32_t) {
439 if s.im == nil && s.disp.imm != nil {
440 s.im = C.zwp_text_input_manager_v3_get_text_input(s.disp.imm, s.seat)
441 C.zwp_text_input_v3_add_listener(s.im, &C.gio_zwp_text_input_v3_listener, unsafe.Pointer(s.seat))
442 }
443 switch {
444 case s.pointer == nil && caps&C.WL_SEAT_CAPABILITY_POINTER != 0:
445 s.pointer = C.wl_seat_get_pointer(s.seat)
446 C.wl_pointer_add_listener(s.pointer, &C.gio_pointer_listener, unsafe.Pointer(s.seat))
447 case s.pointer != nil && caps&C.WL_SEAT_CAPABILITY_POINTER == 0:
448 C.wl_pointer_release(s.pointer)
449 s.pointer = nil
450 }
451 switch {
452 case s.touch == nil && caps&C.WL_SEAT_CAPABILITY_TOUCH != 0:
453 s.touch = C.wl_seat_get_touch(s.seat)
454 C.wl_touch_add_listener(s.touch, &C.gio_touch_listener, unsafe.Pointer(s.seat))
455 case s.touch != nil && caps&C.WL_SEAT_CAPABILITY_TOUCH == 0:
456 C.wl_touch_release(s.touch)
457 s.touch = nil
458 }
459 switch {
460 case s.keyboard == nil && caps&C.WL_SEAT_CAPABILITY_KEYBOARD != 0:
461 s.keyboard = C.wl_seat_get_keyboard(s.seat)
462 C.wl_keyboard_add_listener(s.keyboard, &C.gio_keyboard_listener, unsafe.Pointer(s.seat))
463 case s.keyboard != nil && caps&C.WL_SEAT_CAPABILITY_KEYBOARD == 0:
464 C.wl_keyboard_release(s.keyboard)
465 s.keyboard = nil
466 }
467 }
468
469 //export gio_onSeatName
470 func gio_onSeatName(data unsafe.Pointer, seat *C.struct_wl_seat, name *C.char) {
471 }
472
473 //export gio_onXdgSurfaceConfigure
474 func gio_onXdgSurfaceConfigure(data unsafe.Pointer, wmSurf *C.struct_xdg_surface, serial C.uint32_t) {
475 w := callbackLoad(data).(*window)
476 w.mu.Lock()
477 w.serial = serial
478 w.needAck = true
479 w.mu.Unlock()
480 w.setStage(system.StageRunning)
481 w.draw(true)
482 }
483
484 //export gio_onToplevelClose
485 func gio_onToplevelClose(data unsafe.Pointer, topLvl *C.struct_xdg_toplevel) {
486 w := callbackLoad(data).(*window)
487 w.dead = true
488 }
489
490 //export gio_onToplevelConfigure
491 func gio_onToplevelConfigure(data unsafe.Pointer, topLvl *C.struct_xdg_toplevel, width, height C.int32_t, states *C.struct_wl_array) {
492 w := callbackLoad(data).(*window)
493 if width != 0 && height != 0 {
494 w.mu.Lock()
495 defer w.mu.Unlock()
496 w.width = int(width)
497 w.height = int(height)
498 w.updateOpaqueRegion()
499 }
500 }
501
502 //export gio_onOutputMode
503 func gio_onOutputMode(data unsafe.Pointer, output *C.struct_wl_output, flags C.uint32_t, width, height, refresh C.int32_t) {
504 if flags&C.WL_OUTPUT_MODE_CURRENT == 0 {
505 return
506 }
507 d := callbackLoad(data).(*wlDisplay)
508 c := d.outputConfig[output]
509 c.width = int(width)
510 c.height = int(height)
511 }
512
513 //export gio_onOutputGeometry
514 func gio_onOutputGeometry(data unsafe.Pointer, output *C.struct_wl_output, x, y, physWidth, physHeight, subpixel C.int32_t, make, model *C.char, transform C.int32_t) {
515 d := callbackLoad(data).(*wlDisplay)
516 c := d.outputConfig[output]
517 c.transform = transform
518 c.physWidth = int(physWidth)
519 c.physHeight = int(physHeight)
520 }
521
522 //export gio_onOutputScale
523 func gio_onOutputScale(data unsafe.Pointer, output *C.struct_wl_output, scale C.int32_t) {
524 d := callbackLoad(data).(*wlDisplay)
525 c := d.outputConfig[output]
526 c.scale = int(scale)
527 }
528
529 //export gio_onOutputDone
530 func gio_onOutputDone(data unsafe.Pointer, output *C.struct_wl_output) {
531 d := callbackLoad(data).(*wlDisplay)
532 conf := d.outputConfig[output]
533 for _, w := range conf.windows {
534 w.draw(true)
535 }
536 }
537
538 //export gio_onSurfaceEnter
539 func gio_onSurfaceEnter(data unsafe.Pointer, surf *C.struct_wl_surface, output *C.struct_wl_output) {
540 w := callbackLoad(data).(*window)
541 conf := w.disp.outputConfig[output]
542 var found bool
543 for _, w2 := range conf.windows {
544 if w2 == w {
545 found = true
546 break
547 }
548 }
549 if !found {
550 conf.windows = append(conf.windows, w)
551 }
552 w.updateOutputs()
553 }
554
555 //export gio_onSurfaceLeave
556 func gio_onSurfaceLeave(data unsafe.Pointer, surf *C.struct_wl_surface, output *C.struct_wl_output) {
557 w := callbackLoad(data).(*window)
558 conf := w.disp.outputConfig[output]
559 for i, w2 := range conf.windows {
560 if w2 == w {
561 conf.windows = append(conf.windows[:i], conf.windows[i+1:]...)
562 break
563 }
564 }
565 w.updateOutputs()
566 }
567
568 //export gio_onRegistryGlobal
569 func gio_onRegistryGlobal(data unsafe.Pointer, reg *C.struct_wl_registry, name C.uint32_t, cintf *C.char, version C.uint32_t) {
570 d := callbackLoad(data).(*wlDisplay)
571 switch C.GoString(cintf) {
572 case "wl_compositor":
573 d.compositor = (*C.struct_wl_compositor)(C.wl_registry_bind(reg, name, &C.wl_compositor_interface, 3))
574 case "wl_output":
575 output := (*C.struct_wl_output)(C.wl_registry_bind(reg, name, &C.wl_output_interface, 2))
576 C.wl_output_add_listener(output, &C.gio_output_listener, unsafe.Pointer(d.disp))
577 d.outputMap[name] = output
578 d.outputConfig[output] = new(wlOutput)
579 case "wl_seat":
580 if d.seat != nil {
581 break
582 }
583 s := (*C.struct_wl_seat)(C.wl_registry_bind(reg, name, &C.wl_seat_interface, 5))
584 if s == nil {
585 // No support for v5 protocol.
586 break
587 }
588 d.seat = &wlSeat{
589 disp: d,
590 name: name,
591 seat: s,
592 offers: make(map[*C.struct_wl_data_offer][]string),
593 touchFoci: make(map[C.int32_t]*window),
594 }
595 callbackStore(unsafe.Pointer(s), d.seat)
596 C.wl_seat_add_listener(s, &C.gio_seat_listener, unsafe.Pointer(s))
597 if d.dataDeviceManager == nil {
598 break
599 }
600 d.seat.dataDev = C.wl_data_device_manager_get_data_device(d.dataDeviceManager, s)
601 if d.seat.dataDev == nil {
602 break
603 }
604 callbackStore(unsafe.Pointer(d.seat.dataDev), d.seat)
605 C.wl_data_device_add_listener(d.seat.dataDev, &C.gio_data_device_listener, unsafe.Pointer(d.seat.dataDev))
606 case "wl_shm":
607 d.shm = (*C.struct_wl_shm)(C.wl_registry_bind(reg, name, &C.wl_shm_interface, 1))
608 case "xdg_wm_base":
609 d.wm = (*C.struct_xdg_wm_base)(C.wl_registry_bind(reg, name, &C.xdg_wm_base_interface, 1))
610 case "zxdg_decoration_manager_v1":
611 d.decor = (*C.struct_zxdg_decoration_manager_v1)(C.wl_registry_bind(reg, name, &C.zxdg_decoration_manager_v1_interface, 1))
612 // TODO: Implement and test text-input support.
613 /*case "zwp_text_input_manager_v3":
614 d.imm = (*C.struct_zwp_text_input_manager_v3)(C.wl_registry_bind(reg, name, &C.zwp_text_input_manager_v3_interface, 1))*/
615 case "wl_data_device_manager":
616 d.dataDeviceManager = (*C.struct_wl_data_device_manager)(C.wl_registry_bind(reg, name, &C.wl_data_device_manager_interface, 3))
617 }
618 }
619
620 //export gio_onDataOfferOffer
621 func gio_onDataOfferOffer(data unsafe.Pointer, offer *C.struct_wl_data_offer, mime *C.char) {
622 s := callbackLoad(data).(*wlSeat)
623 s.offers[offer] = append(s.offers[offer], C.GoString(mime))
624 }
625
626 //export gio_onDataOfferSourceActions
627 func gio_onDataOfferSourceActions(data unsafe.Pointer, offer *C.struct_wl_data_offer, acts C.uint32_t) {
628 }
629
630 //export gio_onDataOfferAction
631 func gio_onDataOfferAction(data unsafe.Pointer, offer *C.struct_wl_data_offer, act C.uint32_t) {
632 }
633
634 //export gio_onDataDeviceOffer
635 func gio_onDataDeviceOffer(data unsafe.Pointer, dataDev *C.struct_wl_data_device, id *C.struct_wl_data_offer) {
636 s := callbackLoad(data).(*wlSeat)
637 callbackStore(unsafe.Pointer(id), s)
638 C.wl_data_offer_add_listener(id, &C.gio_data_offer_listener, unsafe.Pointer(id))
639 s.offers[id] = nil
640 }
641
642 //export gio_onDataDeviceEnter
643 func gio_onDataDeviceEnter(data unsafe.Pointer, dataDev *C.struct_wl_data_device, serial C.uint32_t, surf *C.struct_wl_surface, x, y C.wl_fixed_t, id *C.struct_wl_data_offer) {
644 s := callbackLoad(data).(*wlSeat)
645 s.serial = serial
646 s.flushOffers()
647 }
648
649 //export gio_onDataDeviceLeave
650 func gio_onDataDeviceLeave(data unsafe.Pointer, dataDev *C.struct_wl_data_device) {
651 }
652
653 //export gio_onDataDeviceMotion
654 func gio_onDataDeviceMotion(data unsafe.Pointer, dataDev *C.struct_wl_data_device, t C.uint32_t, x, y C.wl_fixed_t) {
655 }
656
657 //export gio_onDataDeviceDrop
658 func gio_onDataDeviceDrop(data unsafe.Pointer, dataDev *C.struct_wl_data_device) {
659 }
660
661 //export gio_onDataDeviceSelection
662 func gio_onDataDeviceSelection(data unsafe.Pointer, dataDev *C.struct_wl_data_device, id *C.struct_wl_data_offer) {
663 s := callbackLoad(data).(*wlSeat)
664 defer s.flushOffers()
665 s.clipboard = nil
666 loop:
667 for _, want := range clipboardMimeTypes {
668 for _, got := range s.offers[id] {
669 if want != got {
670 continue
671 }
672 s.clipboard = id
673 s.mimeType = got
674 break loop
675 }
676 }
677 }
678
679 //export gio_onRegistryGlobalRemove
680 func gio_onRegistryGlobalRemove(data unsafe.Pointer, reg *C.struct_wl_registry, name C.uint32_t) {
681 d := callbackLoad(data).(*wlDisplay)
682 if s := d.seat; s != nil && name == s.name {
683 s.destroy()
684 d.seat = nil
685 }
686 if output, exists := d.outputMap[name]; exists {
687 C.wl_output_destroy(output)
688 delete(d.outputMap, name)
689 delete(d.outputConfig, output)
690 }
691 }
692
693 //export gio_onTouchDown
694 func gio_onTouchDown(data unsafe.Pointer, touch *C.struct_wl_touch, serial, t C.uint32_t, surf *C.struct_wl_surface, id C.int32_t, x, y C.wl_fixed_t) {
695 s := callbackLoad(data).(*wlSeat)
696 s.serial = serial
697 w := callbackLoad(unsafe.Pointer(surf)).(*window)
698 s.touchFoci[id] = w
699 w.lastTouch = f32.Point{
700 X: fromFixed(x) * float32(w.scale),
701 Y: fromFixed(y) * float32(w.scale),
702 }
703 w.w.Event(pointer.Event{
704 Type: pointer.Press,
705 Source: pointer.Touch,
706 Position: w.lastTouch,
707 PointerID: pointer.ID(id),
708 Time: time.Duration(t) * time.Millisecond,
709 Modifiers: w.disp.xkb.Modifiers(),
710 })
711 }
712
713 //export gio_onTouchUp
714 func gio_onTouchUp(data unsafe.Pointer, touch *C.struct_wl_touch, serial, t C.uint32_t, id C.int32_t) {
715 s := callbackLoad(data).(*wlSeat)
716 s.serial = serial
717 w := s.touchFoci[id]
718 delete(s.touchFoci, id)
719 w.w.Event(pointer.Event{
720 Type: pointer.Release,
721 Source: pointer.Touch,
722 Position: w.lastTouch,
723 PointerID: pointer.ID(id),
724 Time: time.Duration(t) * time.Millisecond,
725 Modifiers: w.disp.xkb.Modifiers(),
726 })
727 }
728
729 //export gio_onTouchMotion
730 func gio_onTouchMotion(data unsafe.Pointer, touch *C.struct_wl_touch, t C.uint32_t, id C.int32_t, x, y C.wl_fixed_t) {
731 s := callbackLoad(data).(*wlSeat)
732 w := s.touchFoci[id]
733 w.lastTouch = f32.Point{
734 X: fromFixed(x) * float32(w.scale),
735 Y: fromFixed(y) * float32(w.scale),
736 }
737 w.w.Event(pointer.Event{
738 Type: pointer.Move,
739 Position: w.lastTouch,
740 Source: pointer.Touch,
741 PointerID: pointer.ID(id),
742 Time: time.Duration(t) * time.Millisecond,
743 Modifiers: w.disp.xkb.Modifiers(),
744 })
745 }
746
747 //export gio_onTouchFrame
748 func gio_onTouchFrame(data unsafe.Pointer, touch *C.struct_wl_touch) {
749 }
750
751 //export gio_onTouchCancel
752 func gio_onTouchCancel(data unsafe.Pointer, touch *C.struct_wl_touch) {
753 s := callbackLoad(data).(*wlSeat)
754 for id, w := range s.touchFoci {
755 delete(s.touchFoci, id)
756 w.w.Event(pointer.Event{
757 Type: pointer.Cancel,
758 Source: pointer.Touch,
759 })
760 }
761 }
762
763 //export gio_onPointerEnter
764 func gio_onPointerEnter(data unsafe.Pointer, pointer *C.struct_wl_pointer, serial C.uint32_t, surf *C.struct_wl_surface, x, y C.wl_fixed_t) {
765 s := callbackLoad(data).(*wlSeat)
766 s.serial = serial
767 w := callbackLoad(unsafe.Pointer(surf)).(*window)
768 s.pointerFocus = w
769 w.setCursor(pointer, serial)
770 w.lastPos = f32.Point{X: fromFixed(x), Y: fromFixed(y)}
771 }
772
773 //export gio_onPointerLeave
774 func gio_onPointerLeave(data unsafe.Pointer, p *C.struct_wl_pointer, serial C.uint32_t, surface *C.struct_wl_surface) {
775 s := callbackLoad(data).(*wlSeat)
776 s.serial = serial
777 }
778
779 //export gio_onPointerMotion
780 func gio_onPointerMotion(data unsafe.Pointer, p *C.struct_wl_pointer, t C.uint32_t, x, y C.wl_fixed_t) {
781 s := callbackLoad(data).(*wlSeat)
782 w := s.pointerFocus
783 w.resetFling()
784 w.onPointerMotion(x, y, t)
785 }
786
787 //export gio_onPointerButton
788 func gio_onPointerButton(data unsafe.Pointer, p *C.struct_wl_pointer, serial, t, wbtn, state C.uint32_t) {
789 s := callbackLoad(data).(*wlSeat)
790 s.serial = serial
791 w := s.pointerFocus
792 // From linux-event-codes.h.
793 const (
794 BTN_LEFT = 0x110
795 BTN_RIGHT = 0x111
796 BTN_MIDDLE = 0x112
797 )
798 var btn pointer.Buttons
799 switch wbtn {
800 case BTN_LEFT:
801 btn = pointer.ButtonPrimary
802 case BTN_RIGHT:
803 btn = pointer.ButtonSecondary
804 case BTN_MIDDLE:
805 btn = pointer.ButtonTertiary
806 default:
807 return
808 }
809 var typ pointer.Type
810 switch state {
811 case 0:
812 w.pointerBtns &^= btn
813 typ = pointer.Release
814 case 1:
815 w.pointerBtns |= btn
816 typ = pointer.Press
817 }
818 w.flushScroll()
819 w.resetFling()
820 w.w.Event(pointer.Event{
821 Type: typ,
822 Source: pointer.Mouse,
823 Buttons: w.pointerBtns,
824 Position: w.lastPos,
825 Time: time.Duration(t) * time.Millisecond,
826 Modifiers: w.disp.xkb.Modifiers(),
827 })
828 }
829
830 //export gio_onPointerAxis
831 func gio_onPointerAxis(data unsafe.Pointer, p *C.struct_wl_pointer, t, axis C.uint32_t, value C.wl_fixed_t) {
832 s := callbackLoad(data).(*wlSeat)
833 w := s.pointerFocus
834 v := fromFixed(value)
835 w.resetFling()
836 if w.scroll.dist == (f32.Point{}) {
837 w.scroll.time = time.Duration(t) * time.Millisecond
838 }
839 switch axis {
840 case C.WL_POINTER_AXIS_HORIZONTAL_SCROLL:
841 w.scroll.dist.X += v
842 case C.WL_POINTER_AXIS_VERTICAL_SCROLL:
843 w.scroll.dist.Y += v
844 }
845 }
846
847 //export gio_onPointerFrame
848 func gio_onPointerFrame(data unsafe.Pointer, p *C.struct_wl_pointer) {
849 s := callbackLoad(data).(*wlSeat)
850 w := s.pointerFocus
851 w.flushScroll()
852 w.flushFling()
853 }
854
855 func (w *window) flushFling() {
856 if !w.fling.start {
857 return
858 }
859 w.fling.start = false
860 estx, esty := w.fling.xExtrapolation.Estimate(), w.fling.yExtrapolation.Estimate()
861 w.fling.xExtrapolation = fling.Extrapolation{}
862 w.fling.yExtrapolation = fling.Extrapolation{}
863 vel := float32(math.Sqrt(float64(estx.Velocity*estx.Velocity + esty.Velocity*esty.Velocity)))
864 _, _, c := w.config()
865 if !w.fling.anim.Start(c, time.Now(), vel) {
866 return
867 }
868 invDist := 1 / vel
869 w.fling.dir.X = estx.Velocity * invDist
870 w.fling.dir.Y = esty.Velocity * invDist
871 // Wake up the window loop.
872 w.disp.wakeup()
873 }
874
875 //export gio_onPointerAxisSource
876 func gio_onPointerAxisSource(data unsafe.Pointer, pointer *C.struct_wl_pointer, source C.uint32_t) {
877 }
878
879 //export gio_onPointerAxisStop
880 func gio_onPointerAxisStop(data unsafe.Pointer, p *C.struct_wl_pointer, t, axis C.uint32_t) {
881 s := callbackLoad(data).(*wlSeat)
882 w := s.pointerFocus
883 w.fling.start = true
884 }
885
886 //export gio_onPointerAxisDiscrete
887 func gio_onPointerAxisDiscrete(data unsafe.Pointer, p *C.struct_wl_pointer, axis C.uint32_t, discrete C.int32_t) {
888 s := callbackLoad(data).(*wlSeat)
889 w := s.pointerFocus
890 w.resetFling()
891 switch axis {
892 case C.WL_POINTER_AXIS_HORIZONTAL_SCROLL:
893 w.scroll.steps.X += int(discrete)
894 case C.WL_POINTER_AXIS_VERTICAL_SCROLL:
895 w.scroll.steps.Y += int(discrete)
896 }
897 }
898
899 func (w *window) ReadClipboard() {
900 w.mu.Lock()
901 w.readClipboard = true
902 w.mu.Unlock()
903 w.disp.wakeup()
904 }
905
906 func (w *window) WriteClipboard(s string) {
907 w.mu.Lock()
908 w.writeClipboard = &s
909 w.mu.Unlock()
910 w.disp.wakeup()
911 }
912
913 func (w *window) Option(opts *Options) {
914 w.mu.Lock()
915 w.opts = opts
916 w.mu.Unlock()
917 w.disp.wakeup()
918 }
919
920 func (w *window) setOptions(opts *Options) {
921 _, _, cfg := w.config()
922 if o := opts.Size; o != nil {
923 w.width = cfg.Px(o.Width)
924 w.height = cfg.Px(o.Height)
925 }
926 if o := opts.Title; o != nil {
927 title := C.CString(*o)
928 C.xdg_toplevel_set_title(w.topLvl, title)
929 C.free(unsafe.Pointer(title))
930 }
931 }
932
933 func (w *window) SetCursor(name pointer.CursorName) {
934 if name == pointer.CursorNone {
935 C.wl_pointer_set_cursor(w.disp.seat.pointer, w.serial, nil, 0, 0)
936 return
937 }
938 switch name {
939 default:
940 fallthrough
941 case pointer.CursorDefault:
942 name = "left_ptr"
943 case pointer.CursorText:
944 name = "xterm"
945 case pointer.CursorPointer:
946 name = "hand1"
947 case pointer.CursorCrossHair:
948 name = "crosshair"
949 case pointer.CursorRowResize:
950 name = "top_side"
951 case pointer.CursorColResize:
952 name = "left_side"
953 case pointer.CursorGrab:
954 name = "hand1"
955 }
956 cname := C.CString(string(name))
957 defer C.free(unsafe.Pointer(cname))
958 c := C.wl_cursor_theme_get_cursor(w.cursor.theme, cname)
959 if c == nil {
960 return
961 }
962 w.cursor.cursor = c
963 w.setCursor(w.disp.seat.pointer, w.serial)
964 }
965
966 func (w *window) setCursor(pointer *C.struct_wl_pointer, serial C.uint32_t) {
967 // Get images[0].
968 img := *w.cursor.cursor.images
969 buf := C.wl_cursor_image_get_buffer(img)
970 if buf == nil {
971 return
972 }
973 C.wl_pointer_set_cursor(pointer, serial, w.cursor.surf, C.int32_t(img.hotspot_x), C.int32_t(img.hotspot_y))
974 C.wl_surface_attach(w.cursor.surf, buf, 0, 0)
975 C.wl_surface_damage(w.cursor.surf, 0, 0, C.int32_t(img.width), C.int32_t(img.height))
976 C.wl_surface_commit(w.cursor.surf)
977 }
978
979 func (w *window) resetFling() {
980 w.fling.start = false
981 w.fling.anim = fling.Animation{}
982 }
983
984 //export gio_onKeyboardKeymap
985 func gio_onKeyboardKeymap(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, format C.uint32_t, fd C.int32_t, size C.uint32_t) {
986 defer syscall.Close(int(fd))
987 s := callbackLoad(data).(*wlSeat)
988 s.disp.repeat.Stop(0)
989 s.disp.xkb.DestroyKeymapState()
990 if format != C.WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1 {
991 return
992 }
993 if err := s.disp.xkb.LoadKeymap(int(format), int(fd), int(size)); err != nil {
994 // TODO: Do better.
995 panic(err)
996 }
997 }
998
999 //export gio_onKeyboardEnter
1000 func gio_onKeyboardEnter(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, serial C.uint32_t, surf *C.struct_wl_surface, keys *C.struct_wl_array) {
1001 s := callbackLoad(data).(*wlSeat)
1002 s.serial = serial
1003 w := callbackLoad(unsafe.Pointer(surf)).(*window)
1004 s.keyboardFocus = w
1005 s.disp.repeat.Stop(0)
1006 w.w.Event(key.FocusEvent{Focus: true})
1007 }
1008
1009 //export gio_onKeyboardLeave
1010 func gio_onKeyboardLeave(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, serial C.uint32_t, surf *C.struct_wl_surface) {
1011 s := callbackLoad(data).(*wlSeat)
1012 s.serial = serial
1013 s.disp.repeat.Stop(0)
1014 w := s.keyboardFocus
1015 w.w.Event(key.FocusEvent{Focus: false})
1016 }
1017
1018 //export gio_onKeyboardKey
1019 func gio_onKeyboardKey(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, serial, timestamp, keyCode, state C.uint32_t) {
1020 s := callbackLoad(data).(*wlSeat)
1021 s.serial = serial
1022 w := s.keyboardFocus
1023 t := time.Duration(timestamp) * time.Millisecond
1024 s.disp.repeat.Stop(t)
1025 w.resetFling()
1026 kc := mapXKBKeycode(uint32(keyCode))
1027 ks := mapXKBKeyState(uint32(state))
1028 for _, e := range w.disp.xkb.DispatchKey(kc, ks) {
1029 w.w.Event(e)
1030 }
1031 if state != C.WL_KEYBOARD_KEY_STATE_PRESSED {
1032 return
1033 }
1034 if w.disp.xkb.IsRepeatKey(kc) {
1035 w.disp.repeat.Start(w, kc, t)
1036 }
1037 }
1038
1039 func mapXKBKeycode(keyCode uint32) uint32 {
1040 // According to the xkb_v1 spec: "to determine the xkb keycode, clients must add 8 to the key event keycode."
1041 return keyCode + 8
1042 }
1043
1044 func mapXKBKeyState(state uint32) key.State {
1045 switch state {
1046 case C.WL_KEYBOARD_KEY_STATE_RELEASED:
1047 return key.Release
1048 default:
1049 return key.Press
1050 }
1051 }
1052
1053 func (r *repeatState) Start(w *window, keyCode uint32, t time.Duration) {
1054 if r.rate <= 0 {
1055 return
1056 }
1057 stopC := make(chan struct{})
1058 r.start = t
1059 r.last = 0
1060 r.now = 0
1061 r.stopC = stopC
1062 r.key = keyCode
1063 r.win = w.w
1064 rate, delay := r.rate, r.delay
1065 go func() {
1066 timer := time.NewTimer(delay)
1067 for {
1068 select {
1069 case <-timer.C:
1070 case <-stopC:
1071 close(stopC)
1072 return
1073 }
1074 r.Advance(delay)
1075 w.disp.wakeup()
1076 delay = time.Second / time.Duration(rate)
1077 timer.Reset(delay)
1078 }
1079 }()
1080 }
1081
1082 func (r *repeatState) Stop(t time.Duration) {
1083 if r.stopC == nil {
1084 return
1085 }
1086 r.stopC <- struct{}{}
1087 <-r.stopC
1088 r.stopC = nil
1089 t -= r.start
1090 if r.now > t {
1091 r.now = t
1092 }
1093 }
1094
1095 func (r *repeatState) Advance(dt time.Duration) {
1096 r.mu.Lock()
1097 defer r.mu.Unlock()
1098 r.now += dt
1099 }
1100
1101 func (r *repeatState) Repeat(d *wlDisplay) {
1102 if r.rate <= 0 {
1103 return
1104 }
1105 r.mu.Lock()
1106 now := r.now
1107 r.mu.Unlock()
1108 for {
1109 var delay time.Duration
1110 if r.last < r.delay {
1111 delay = r.delay
1112 } else {
1113 delay = time.Second / time.Duration(r.rate)
1114 }
1115 if r.last+delay > now {
1116 break
1117 }
1118 for _, e := range d.xkb.DispatchKey(r.key, key.Press) {
1119 r.win.Event(e)
1120 }
1121 r.last += delay
1122 }
1123 }
1124
1125 //export gio_onFrameDone
1126 func gio_onFrameDone(data unsafe.Pointer, callback *C.struct_wl_callback, t C.uint32_t) {
1127 C.wl_callback_destroy(callback)
1128 w := callbackLoad(data).(*window)
1129 if w.lastFrameCallback == callback {
1130 w.lastFrameCallback = nil
1131 w.draw(false)
1132 }
1133 }
1134
1135 func (w *window) loop() error {
1136 var p poller
1137 for {
1138 if err := w.disp.dispatch(&p); err != nil {
1139 return err
1140 }
1141 if w.dead {
1142 w.w.Event(system.DestroyEvent{})
1143 break
1144 }
1145 w.process()
1146 }
1147 return nil
1148 }
1149
1150 func (w *window) process() {
1151 w.mu.Lock()
1152 readClipboard := w.readClipboard
1153 writeClipboard := w.writeClipboard
1154 opts := w.opts
1155 w.readClipboard = false
1156 w.writeClipboard = nil
1157 w.opts = nil
1158 w.mu.Unlock()
1159 if readClipboard {
1160 r, err := w.disp.readClipboard()
1161 // Send empty responses on unavailable clipboards or errors.
1162 if r == nil || err != nil {
1163 w.w.Event(clipboard.Event{})
1164 return
1165 }
1166 // Don't let slow clipboard transfers block event loop.
1167 go func() {
1168 defer r.Close()
1169 data, _ := ioutil.ReadAll(r)
1170 w.w.Event(clipboard.Event{Text: string(data)})
1171 }()
1172 }
1173 if writeClipboard != nil {
1174 w.disp.writeClipboard([]byte(*writeClipboard))
1175 }
1176 if opts != nil {
1177 w.setOptions(opts)
1178 }
1179 // pass false to skip unnecessary drawing.
1180 w.draw(false)
1181 }
1182
1183 func (d *wlDisplay) dispatch(p *poller) error {
1184 dispfd := C.wl_display_get_fd(d.disp)
1185 // Poll for events and notifications.
1186 pollfds := append(p.pollfds[:0],
1187 syscall.PollFd{Fd: int32(dispfd), Events: syscall.POLLIN | syscall.POLLERR},
1188 syscall.PollFd{Fd: int32(d.notify.read), Events: syscall.POLLIN | syscall.POLLERR},
1189 )
1190 dispFd := &pollfds[0]
1191 if ret, err := C.wl_display_flush(d.disp); ret < 0 {
1192 if err != syscall.EAGAIN {
1193 return fmt.Errorf("wayland: wl_display_flush failed: %v", err)
1194 }
1195 // EAGAIN means the output buffer was full. Poll for
1196 // POLLOUT to know when we can write again.
1197 dispFd.Events |= syscall.POLLOUT
1198 }
1199 if _, err := syscall.Poll(pollfds, -1); err != nil && err != syscall.EINTR {
1200 return fmt.Errorf("wayland: poll failed: %v", err)
1201 }
1202 // Clear notifications.
1203 for {
1204 _, err := syscall.Read(d.notify.read, p.buf[:])
1205 if err == syscall.EAGAIN {
1206 break
1207 }
1208 if err != nil {
1209 return fmt.Errorf("wayland: read from notify pipe failed: %v", err)
1210 }
1211 }
1212 // Handle events
1213 switch {
1214 case dispFd.Revents&syscall.POLLIN != 0:
1215 if ret, err := C.wl_display_dispatch(d.disp); ret < 0 {
1216 return fmt.Errorf("wayland: wl_display_dispatch failed: %v", err)
1217 }
1218 case dispFd.Revents&(syscall.POLLERR|syscall.POLLHUP) != 0:
1219 return errors.New("wayland: display file descriptor gone")
1220 }
1221 d.repeat.Repeat(d)
1222 return nil
1223 }
1224
1225 func (w *window) SetAnimating(anim bool) {
1226 w.mu.Lock()
1227 w.animating = anim
1228 w.mu.Unlock()
1229 w.disp.wakeup()
1230 }
1231
1232 // Wakeup wakes up the event loop through the notification pipe.
1233 func (d *wlDisplay) wakeup() {
1234 oneByte := make([]byte, 1)
1235 if _, err := syscall.Write(d.notify.write, oneByte); err != nil && err != syscall.EAGAIN {
1236 panic(fmt.Errorf("failed to write to pipe: %v", err))
1237 }
1238 }
1239
1240 func (w *window) destroy() {
1241 if w.cursor.surf != nil {
1242 C.wl_surface_destroy(w.cursor.surf)
1243 }
1244 if w.cursor.theme != nil {
1245 C.wl_cursor_theme_destroy(w.cursor.theme)
1246 }
1247 if w.topLvl != nil {
1248 C.xdg_toplevel_destroy(w.topLvl)
1249 }
1250 if w.surf != nil {
1251 C.wl_surface_destroy(w.surf)
1252 }
1253 if w.wmSurf != nil {
1254 C.xdg_surface_destroy(w.wmSurf)
1255 }
1256 if w.decor != nil {
1257 C.zxdg_toplevel_decoration_v1_destroy(w.decor)
1258 }
1259 callbackDelete(unsafe.Pointer(w.surf))
1260 }
1261
1262 //export gio_onKeyboardModifiers
1263 func gio_onKeyboardModifiers(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, serial, depressed, latched, locked, group C.uint32_t) {
1264 s := callbackLoad(data).(*wlSeat)
1265 s.serial = serial
1266 d := s.disp
1267 d.repeat.Stop(0)
1268 if d.xkb == nil {
1269 return
1270 }
1271 d.xkb.UpdateMask(uint32(depressed), uint32(latched), uint32(locked), uint32(group), uint32(group), uint32(group))
1272 }
1273
1274 //export gio_onKeyboardRepeatInfo
1275 func gio_onKeyboardRepeatInfo(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, rate, delay C.int32_t) {
1276 s := callbackLoad(data).(*wlSeat)
1277 d := s.disp
1278 d.repeat.Stop(0)
1279 d.repeat.rate = int(rate)
1280 d.repeat.delay = time.Duration(delay) * time.Millisecond
1281 }
1282
1283 //export gio_onTextInputEnter
1284 func gio_onTextInputEnter(data unsafe.Pointer, im *C.struct_zwp_text_input_v3, surf *C.struct_wl_surface) {
1285 }
1286
1287 //export gio_onTextInputLeave
1288 func gio_onTextInputLeave(data unsafe.Pointer, im *C.struct_zwp_text_input_v3, surf *C.struct_wl_surface) {
1289 }
1290
1291 //export gio_onTextInputPreeditString
1292 func gio_onTextInputPreeditString(data unsafe.Pointer, im *C.struct_zwp_text_input_v3, ctxt *C.char, begin, end C.int32_t) {
1293 }
1294
1295 //export gio_onTextInputCommitString
1296 func gio_onTextInputCommitString(data unsafe.Pointer, im *C.struct_zwp_text_input_v3, ctxt *C.char) {
1297 }
1298
1299 //export gio_onTextInputDeleteSurroundingText
1300 func gio_onTextInputDeleteSurroundingText(data unsafe.Pointer, im *C.struct_zwp_text_input_v3, before, after C.uint32_t) {
1301 }
1302
1303 //export gio_onTextInputDone
1304 func gio_onTextInputDone(data unsafe.Pointer, im *C.struct_zwp_text_input_v3, serial C.uint32_t) {
1305 s := callbackLoad(data).(*wlSeat)
1306 s.serial = serial
1307 }
1308
1309 //export gio_onDataSourceTarget
1310 func gio_onDataSourceTarget(data unsafe.Pointer, source *C.struct_wl_data_source, mime *C.char) {
1311 }
1312
1313 //export gio_onDataSourceSend
1314 func gio_onDataSourceSend(data unsafe.Pointer, source *C.struct_wl_data_source, mime *C.char, fd C.int32_t) {
1315 s := callbackLoad(data).(*wlSeat)
1316 content := s.content
1317 go func() {
1318 defer syscall.Close(int(fd))
1319 syscall.Write(int(fd), content)
1320 }()
1321 }
1322
1323 //export gio_onDataSourceCancelled
1324 func gio_onDataSourceCancelled(data unsafe.Pointer, source *C.struct_wl_data_source) {
1325 s := callbackLoad(data).(*wlSeat)
1326 if s.source == source {
1327 s.content = nil
1328 s.source = nil
1329 }
1330 C.wl_data_source_destroy(source)
1331 }
1332
1333 //export gio_onDataSourceDNDDropPerformed
1334 func gio_onDataSourceDNDDropPerformed(data unsafe.Pointer, source *C.struct_wl_data_source) {
1335 }
1336
1337 //export gio_onDataSourceDNDFinished
1338 func gio_onDataSourceDNDFinished(data unsafe.Pointer, source *C.struct_wl_data_source) {
1339 }
1340
1341 //export gio_onDataSourceAction
1342 func gio_onDataSourceAction(data unsafe.Pointer, source *C.struct_wl_data_source, act C.uint32_t) {
1343 }
1344
1345 func (w *window) flushScroll() {
1346 var fling f32.Point
1347 if w.fling.anim.Active() {
1348 dist := float32(w.fling.anim.Tick(time.Now()))
1349 fling = w.fling.dir.Mul(dist)
1350 }
1351 // The Wayland reported scroll distance for
1352 // discrete scroll axes is only 10 pixels, where
1353 // 100 seems more appropriate.
1354 const discreteScale = 10
1355 if w.scroll.steps.X != 0 {
1356 w.scroll.dist.X *= discreteScale
1357 }
1358 if w.scroll.steps.Y != 0 {
1359 w.scroll.dist.Y *= discreteScale
1360 }
1361 total := w.scroll.dist.Add(fling)
1362 if total == (f32.Point{}) {
1363 return
1364 }
1365 w.w.Event(pointer.Event{
1366 Type: pointer.Scroll,
1367 Source: pointer.Mouse,
1368 Buttons: w.pointerBtns,
1369 Position: w.lastPos,
1370 Scroll: total,
1371 Time: w.scroll.time,
1372 Modifiers: w.disp.xkb.Modifiers(),
1373 })
1374 if w.scroll.steps == (image.Point{}) {
1375 w.fling.xExtrapolation.SampleDelta(w.scroll.time, -w.scroll.dist.X)
1376 w.fling.yExtrapolation.SampleDelta(w.scroll.time, -w.scroll.dist.Y)
1377 }
1378 w.scroll.dist = f32.Point{}
1379 w.scroll.steps = image.Point{}
1380 }
1381
1382 func (w *window) onPointerMotion(x, y C.wl_fixed_t, t C.uint32_t) {
1383 w.flushScroll()
1384 w.lastPos = f32.Point{
1385 X: fromFixed(x) * float32(w.scale),
1386 Y: fromFixed(y) * float32(w.scale),
1387 }
1388 w.w.Event(pointer.Event{
1389 Type: pointer.Move,
1390 Position: w.lastPos,
1391 Buttons: w.pointerBtns,
1392 Source: pointer.Mouse,
1393 Time: time.Duration(t) * time.Millisecond,
1394 Modifiers: w.disp.xkb.Modifiers(),
1395 })
1396 }
1397
1398 func (w *window) updateOpaqueRegion() {
1399 reg := C.wl_compositor_create_region(w.disp.compositor)
1400 C.wl_region_add(reg, 0, 0, C.int32_t(w.width), C.int32_t(w.height))
1401 C.wl_surface_set_opaque_region(w.surf, reg)
1402 C.wl_region_destroy(reg)
1403 }
1404
1405 func (w *window) updateOutputs() {
1406 scale := 1
1407 var found bool
1408 for _, conf := range w.disp.outputConfig {
1409 for _, w2 := range conf.windows {
1410 if w2 == w {
1411 found = true
1412 if conf.scale > scale {
1413 scale = conf.scale
1414 }
1415 }
1416 }
1417 }
1418 w.mu.Lock()
1419 if found && scale != w.scale {
1420 w.scale = scale
1421 w.newScale = true
1422 }
1423 w.mu.Unlock()
1424 if !found {
1425 w.setStage(system.StagePaused)
1426 } else {
1427 w.setStage(system.StageRunning)
1428 w.draw(true)
1429 }
1430 }
1431
1432 func (w *window) config() (int, int, unit.Metric) {
1433 width, height := w.width*w.scale, w.height*w.scale
1434 return width, height, unit.Metric{
1435 PxPerDp: w.ppdp * float32(w.scale),
1436 PxPerSp: w.ppsp * float32(w.scale),
1437 }
1438 }
1439
1440 func (w *window) draw(sync bool) {
1441 w.flushScroll()
1442 w.mu.Lock()
1443 anim := w.animating || w.fling.anim.Active()
1444 dead := w.dead
1445 w.mu.Unlock()
1446 if dead || (!anim && !sync) {
1447 return
1448 }
1449 width, height, cfg := w.config()
1450 if cfg == (unit.Metric{}) {
1451 return
1452 }
1453 if anim && w.lastFrameCallback == nil {
1454 w.lastFrameCallback = C.wl_surface_frame(w.surf)
1455 // Use the surface as listener data for gio_onFrameDone.
1456 C.wl_callback_add_listener(w.lastFrameCallback, &C.gio_callback_listener, unsafe.Pointer(w.surf))
1457 }
1458 w.w.Event(FrameEvent{
1459 FrameEvent: system.FrameEvent{
1460 Now: time.Now(),
1461 Size: image.Point{
1462 X: width,
1463 Y: height,
1464 },
1465 Metric: cfg,
1466 },
1467 Sync: sync,
1468 })
1469 }
1470
1471 func (w *window) setStage(s system.Stage) {
1472 if s == w.stage {
1473 return
1474 }
1475 w.stage = s
1476 w.w.Event(system.StageEvent{Stage: s})
1477 }
1478
1479 func (w *window) display() *C.struct_wl_display {
1480 return w.disp.disp
1481 }
1482
1483 func (w *window) surface() (*C.struct_wl_surface, int, int) {
1484 if w.needAck {
1485 C.xdg_surface_ack_configure(w.wmSurf, w.serial)
1486 w.needAck = false
1487 }
1488 width, height, scale := w.width, w.height, w.scale
1489 if w.newScale {
1490 C.wl_surface_set_buffer_scale(w.surf, C.int32_t(scale))
1491 w.newScale = false
1492 }
1493 return w.surf, width * scale, height * scale
1494 }
1495
1496 func (w *window) ShowTextInput(show bool) {}
1497
1498 // Close the window. Not implemented for Wayland.
1499 func (w *window) Close() {}
1500
1501 // detectUIScale reports the system UI scale, or 1.0 if it fails.
1502 func detectUIScale() float32 {
1503 // TODO: What about other window environments?
1504 out, err := exec.Command("gsettings", "get", "org.gnome.desktop.interface", "text-scaling-factor").Output()
1505 if err != nil {
1506 return 1.0
1507 }
1508 scale, err := strconv.ParseFloat(string(bytes.TrimSpace(out)), 32)
1509 if err != nil {
1510 return 1.0
1511 }
1512 return float32(scale)
1513 }
1514
1515 func newWLDisplay() (*wlDisplay, error) {
1516 d := &wlDisplay{
1517 outputMap: make(map[C.uint32_t]*C.struct_wl_output),
1518 outputConfig: make(map[*C.struct_wl_output]*wlOutput),
1519 }
1520 pipe := make([]int, 2)
1521 if err := syscall.Pipe2(pipe, syscall.O_NONBLOCK|syscall.O_CLOEXEC); err != nil {
1522 return nil, fmt.Errorf("wayland: failed to create pipe: %v", err)
1523 }
1524 d.notify.read = pipe[0]
1525 d.notify.write = pipe[1]
1526 xkb, err := xkb.New()
1527 if err != nil {
1528 d.destroy()
1529 return nil, fmt.Errorf("wayland: %v", err)
1530 }
1531 d.xkb = xkb
1532 d.disp, err = C.wl_display_connect(nil)
1533 if d.disp == nil {
1534 d.destroy()
1535 return nil, fmt.Errorf("wayland: wl_display_connect failed: %v", err)
1536 }
1537 callbackMap.Store(unsafe.Pointer(d.disp), d)
1538 d.reg = C.wl_display_get_registry(d.disp)
1539 if d.reg == nil {
1540 d.destroy()
1541 return nil, errors.New("wayland: wl_display_get_registry failed")
1542 }
1543 C.wl_registry_add_listener(d.reg, &C.gio_registry_listener, unsafe.Pointer(d.disp))
1544 // Wait for the server to register all its globals to the
1545 // registry listener (gio_onRegistryGlobal).
1546 C.wl_display_roundtrip(d.disp)
1547 // Configuration listeners are added to outputs by gio_onRegistryGlobal.
1548 // We need another roundtrip to get the initial output configurations
1549 // through the gio_onOutput* callbacks.
1550 C.wl_display_roundtrip(d.disp)
1551 return d, nil
1552 }
1553
1554 func (d *wlDisplay) destroy() {
1555 if d.notify.write != 0 {
1556 syscall.Close(d.notify.write)
1557 d.notify.write = 0
1558 }
1559 if d.notify.read != 0 {
1560 syscall.Close(d.notify.read)
1561 d.notify.read = 0
1562 }
1563 d.repeat.Stop(0)
1564 if d.xkb != nil {
1565 d.xkb.Destroy()
1566 d.xkb = nil
1567 }
1568 if d.seat != nil {
1569 d.seat.destroy()
1570 d.seat = nil
1571 }
1572 if d.imm != nil {
1573 C.zwp_text_input_manager_v3_destroy(d.imm)
1574 }
1575 if d.decor != nil {
1576 C.zxdg_decoration_manager_v1_destroy(d.decor)
1577 }
1578 if d.shm != nil {
1579 C.wl_shm_destroy(d.shm)
1580 }
1581 if d.compositor != nil {
1582 C.wl_compositor_destroy(d.compositor)
1583 }
1584 if d.wm != nil {
1585 C.xdg_wm_base_destroy(d.wm)
1586 }
1587 for _, output := range d.outputMap {
1588 C.wl_output_destroy(output)
1589 }
1590 if d.reg != nil {
1591 C.wl_registry_destroy(d.reg)
1592 }
1593 if d.disp != nil {
1594 C.wl_display_disconnect(d.disp)
1595 callbackDelete(unsafe.Pointer(d.disp))
1596 }
1597 }
1598
1599 // fromFixed converts a Wayland wl_fixed_t 23.8 number to float32.
1600 func fromFixed(v C.wl_fixed_t) float32 {
1601 // Convert to float64 to avoid overflow.
1602 // From wayland-util.h.
1603 b := ((1023 + 44) << 52) + (1 << 51) + uint64(v)
1604 f := math.Float64frombits(b) - (3 << 43)
1605 return float32(f)
1606 }
1607