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