github.com/gop9/olt@v0.0.0-20200202132135-d956aad50b08/gio/app/internal/window/os_wayland.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 // +build linux,!android,!nowayland freebsd 4 5 package window 6 7 import ( 8 "bytes" 9 "errors" 10 "fmt" 11 "image" 12 "math" 13 "os/exec" 14 "strconv" 15 "sync" 16 "time" 17 "unsafe" 18 19 "github.com/gop9/olt/gio/app/internal/xkb" 20 "github.com/gop9/olt/gio/f32" 21 "github.com/gop9/olt/gio/internal/fling" 22 "github.com/gop9/olt/gio/io/key" 23 "github.com/gop9/olt/gio/io/pointer" 24 "github.com/gop9/olt/gio/io/system" 25 syscall "golang.org/x/sys/unix" 26 ) 27 28 // Use wayland-scanner to generate glue code for the xdg-shell and xdg-decoration extensions. 29 //go:generate wayland-scanner client-header /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml wayland_xdg_shell.h 30 //go:generate wayland-scanner private-code /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml wayland_xdg_shell.c 31 32 //go:generate wayland-scanner client-header /usr/share/wayland-protocols/unstable/text-input/text-input-unstable-v3.xml wayland_text_input.h 33 //go:generate wayland-scanner private-code /usr/share/wayland-protocols/unstable/text-input/text-input-unstable-v3.xml wayland_text_input.c 34 35 //go:generate wayland-scanner client-header /usr/share/wayland-protocols/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml wayland_xdg_decoration.h 36 //go:generate wayland-scanner private-code /usr/share/wayland-protocols/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml wayland_xdg_decoration.c 37 38 //go:generate sed -i "1s;^;// +build linux,!android,!nowayland freebsd\\n\\n;" wayland_xdg_shell.c 39 //go:generate sed -i "1s;^;// +build linux,!android,!nowayland freebsd\\n\\n;" wayland_xdg_decoration.c 40 //go:generate sed -i "1s;^;// +build linux,!android,!nowayland freebsd\\n\\n;" wayland_text_input.c 41 42 /* 43 #cgo LDFLAGS: -lwayland-client -lwayland-cursor 44 #cgo freebsd CFLAGS: -I/usr/local/include 45 #cgo freebsd LDFLAGS: -L/usr/local/lib 46 47 #include <stdlib.h> 48 #include <wayland-client.h> 49 #include <wayland-cursor.h> 50 #include "wayland_text_input.h" 51 #include "wayland_xdg_shell.h" 52 #include "wayland_xdg_decoration.h" 53 #include "os_wayland.h" 54 */ 55 import "C" 56 57 type wlConn struct { 58 disp *C.struct_wl_display 59 compositor *C.struct_wl_compositor 60 wm *C.struct_xdg_wm_base 61 imm *C.struct_zwp_text_input_manager_v3 62 im *C.struct_zwp_text_input_v3 63 shm *C.struct_wl_shm 64 cursor struct { 65 theme *C.struct_wl_cursor_theme 66 cursor *C.struct_wl_cursor 67 surf *C.struct_wl_surface 68 } 69 decor *C.struct_zxdg_decoration_manager_v1 70 seat *C.struct_wl_seat 71 seatName C.uint32_t 72 pointer *C.struct_wl_pointer 73 touch *C.struct_wl_touch 74 keyboard *C.struct_wl_keyboard 75 xkb *xkb.Context 76 77 repeat repeatState 78 } 79 80 type repeatState struct { 81 rate int 82 delay time.Duration 83 84 key uint32 85 win Callbacks 86 stopC chan struct{} 87 88 start time.Duration 89 last time.Duration 90 mu sync.Mutex 91 now time.Duration 92 } 93 94 type window struct { 95 w Callbacks 96 disp *C.struct_wl_display 97 surf *C.struct_wl_surface 98 wmSurf *C.struct_xdg_surface 99 topLvl *C.struct_xdg_toplevel 100 decor *C.struct_zxdg_toplevel_decoration_v1 101 // Notification pipe fds. 102 notify struct { 103 read, write int 104 } 105 ppdp, ppsp float32 106 scroll struct { 107 time time.Duration 108 steps image.Point 109 dist f32.Point 110 } 111 pointerBtns pointer.Buttons 112 lastPos f32.Point 113 lastTouch f32.Point 114 115 fling struct { 116 yExtrapolation fling.Extrapolation 117 xExtrapolation fling.Extrapolation 118 anim fling.Animation 119 start bool 120 dir f32.Point 121 } 122 123 stage system.Stage 124 dead bool 125 pendingErr error 126 lastFrameCallback *C.struct_wl_callback 127 128 mu sync.Mutex 129 animating bool 130 needAck bool 131 // The last configure serial waiting to be ack'ed. 132 serial C.uint32_t 133 width int 134 height int 135 newScale bool 136 scale int 137 } 138 139 type wlOutput struct { 140 width int 141 height int 142 physWidth int 143 physHeight int 144 transform C.int32_t 145 scale int 146 windows []*window 147 } 148 149 var connMu sync.Mutex 150 var conn *wlConn 151 152 var ( 153 winMap = make(map[interface{}]*window) 154 outputMap = make(map[C.uint32_t]*C.struct_wl_output) 155 outputConfig = make(map[*C.struct_wl_output]*wlOutput) 156 ) 157 158 func init() { 159 wlDriver = newWLWindow 160 } 161 162 func newWLWindow(window Callbacks, opts *Options) error { 163 connMu.Lock() 164 defer connMu.Unlock() 165 if len(winMap) > 0 { 166 return errors.New("multiple windows are not supported") 167 } 168 if err := waylandConnect(); err != nil { 169 return err 170 } 171 w, err := createNativeWindow(opts) 172 if err != nil { 173 conn.destroy() 174 return err 175 } 176 w.w = window 177 go func() { 178 w.w.SetDriver(w) 179 w.loop() 180 w.destroy() 181 conn.destroy() 182 close(mainDone) 183 }() 184 return nil 185 } 186 187 func createNativeWindow(opts *Options) (*window, error) { 188 pipe := make([]int, 2) 189 if err := syscall.Pipe2(pipe, syscall.O_NONBLOCK|syscall.O_CLOEXEC); err != nil { 190 return nil, fmt.Errorf("createNativeWindow: failed to create pipe: %v", err) 191 } 192 193 var scale int 194 for _, conf := range outputConfig { 195 if s := conf.scale; s > scale { 196 scale = s 197 } 198 } 199 ppdp := detectUIScale() 200 201 w := &window{ 202 disp: conn.disp, 203 scale: scale, 204 newScale: scale != 1, 205 ppdp: ppdp, 206 ppsp: ppdp, 207 } 208 w.notify.read = pipe[0] 209 w.notify.write = pipe[1] 210 w.surf = C.wl_compositor_create_surface(conn.compositor) 211 if w.surf == nil { 212 w.destroy() 213 return nil, errors.New("wayland: wl_compositor_create_surface failed") 214 } 215 w.wmSurf = C.xdg_wm_base_get_xdg_surface(conn.wm, w.surf) 216 if w.wmSurf == nil { 217 w.destroy() 218 return nil, errors.New("wayland: xdg_wm_base_get_xdg_surface failed") 219 } 220 w.topLvl = C.xdg_surface_get_toplevel(w.wmSurf) 221 if w.topLvl == nil { 222 w.destroy() 223 return nil, errors.New("wayland: xdg_surface_get_toplevel failed") 224 } 225 C.gio_xdg_wm_base_add_listener(conn.wm) 226 C.gio_wl_surface_add_listener(w.surf) 227 C.gio_xdg_surface_add_listener(w.wmSurf) 228 C.gio_xdg_toplevel_add_listener(w.topLvl) 229 title := C.CString(opts.Title) 230 C.xdg_toplevel_set_title(w.topLvl, title) 231 C.free(unsafe.Pointer(title)) 232 233 _, _, cfg := w.config() 234 w.width = cfg.Px(opts.Width) 235 w.height = cfg.Px(opts.Height) 236 if conn.decor != nil { 237 // Request server side decorations. 238 w.decor = C.zxdg_decoration_manager_v1_get_toplevel_decoration(conn.decor, w.topLvl) 239 C.zxdg_toplevel_decoration_v1_set_mode(w.decor, C.ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE) 240 } 241 w.updateOpaqueRegion() 242 C.wl_surface_commit(w.surf) 243 winMap[w.topLvl] = w 244 winMap[w.surf] = w 245 winMap[w.wmSurf] = w 246 return w, nil 247 } 248 249 //export gio_onSeatCapabilities 250 func gio_onSeatCapabilities(data unsafe.Pointer, seat *C.struct_wl_seat, caps C.uint32_t) { 251 if seat != conn.seat { 252 panic("unexpected seat") 253 } 254 if conn.im == nil && conn.imm != nil { 255 conn.im = C.zwp_text_input_manager_v3_get_text_input(conn.imm, conn.seat) 256 C.gio_zwp_text_input_v3_add_listener(conn.im) 257 } 258 switch { 259 case conn.pointer == nil && caps&C.WL_SEAT_CAPABILITY_POINTER != 0: 260 conn.pointer = C.wl_seat_get_pointer(seat) 261 C.gio_wl_pointer_add_listener(conn.pointer) 262 case conn.pointer != nil && caps&C.WL_SEAT_CAPABILITY_POINTER == 0: 263 C.wl_pointer_release(conn.pointer) 264 conn.pointer = nil 265 } 266 switch { 267 case conn.touch == nil && caps&C.WL_SEAT_CAPABILITY_TOUCH != 0: 268 conn.touch = C.wl_seat_get_touch(seat) 269 C.gio_wl_touch_add_listener(conn.touch) 270 case conn.touch != nil && caps&C.WL_SEAT_CAPABILITY_TOUCH == 0: 271 C.wl_touch_release(conn.touch) 272 conn.touch = nil 273 } 274 switch { 275 case conn.keyboard == nil && caps&C.WL_SEAT_CAPABILITY_KEYBOARD != 0: 276 conn.keyboard = C.wl_seat_get_keyboard(seat) 277 C.gio_wl_keyboard_add_listener(conn.keyboard) 278 case conn.keyboard != nil && caps&C.WL_SEAT_CAPABILITY_KEYBOARD == 0: 279 C.wl_keyboard_release(conn.keyboard) 280 conn.keyboard = nil 281 } 282 } 283 284 //export gio_onSeatName 285 func gio_onSeatName(data unsafe.Pointer, seat *C.struct_wl_seat, name *C.char) { 286 } 287 288 //export gio_onXdgSurfaceConfigure 289 func gio_onXdgSurfaceConfigure(data unsafe.Pointer, wmSurf *C.struct_xdg_surface, serial C.uint32_t) { 290 w := winMap[wmSurf] 291 w.mu.Lock() 292 w.serial = serial 293 w.needAck = true 294 w.mu.Unlock() 295 w.setStage(system.StageRunning) 296 w.draw(true) 297 } 298 299 //export gio_onToplevelClose 300 func gio_onToplevelClose(data unsafe.Pointer, topLvl *C.struct_xdg_toplevel) { 301 w := winMap[topLvl] 302 w.dead = true 303 } 304 305 //export gio_onToplevelConfigure 306 func gio_onToplevelConfigure(data unsafe.Pointer, topLvl *C.struct_xdg_toplevel, width, height C.int32_t, states *C.struct_wl_array) { 307 w := winMap[topLvl] 308 if width != 0 && height != 0 { 309 w.mu.Lock() 310 defer w.mu.Unlock() 311 w.width = int(width) 312 w.height = int(height) 313 w.updateOpaqueRegion() 314 } 315 } 316 317 //export gio_onOutputMode 318 func gio_onOutputMode(data unsafe.Pointer, output *C.struct_wl_output, flags C.uint32_t, width, height, refresh C.int32_t) { 319 if flags&C.WL_OUTPUT_MODE_CURRENT == 0 { 320 return 321 } 322 c := outputConfig[output] 323 c.width = int(width) 324 c.height = int(height) 325 } 326 327 //export gio_onOutputGeometry 328 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) { 329 c := outputConfig[output] 330 c.transform = transform 331 c.physWidth = int(physWidth) 332 c.physHeight = int(physHeight) 333 } 334 335 //export gio_onOutputScale 336 func gio_onOutputScale(data unsafe.Pointer, output *C.struct_wl_output, scale C.int32_t) { 337 c := outputConfig[output] 338 c.scale = int(scale) 339 } 340 341 //export gio_onOutputDone 342 func gio_onOutputDone(data unsafe.Pointer, output *C.struct_wl_output) { 343 conf := outputConfig[output] 344 for _, w := range conf.windows { 345 w.draw(true) 346 } 347 } 348 349 //export gio_onSurfaceEnter 350 func gio_onSurfaceEnter(data unsafe.Pointer, surf *C.struct_wl_surface, output *C.struct_wl_output) { 351 w := winMap[surf] 352 conf := outputConfig[output] 353 var found bool 354 for _, w2 := range conf.windows { 355 if w2 == w { 356 found = true 357 break 358 } 359 } 360 if !found { 361 conf.windows = append(conf.windows, w) 362 } 363 w.updateOutputs() 364 } 365 366 //export gio_onSurfaceLeave 367 func gio_onSurfaceLeave(data unsafe.Pointer, surf *C.struct_wl_surface, output *C.struct_wl_output) { 368 w := winMap[surf] 369 conf := outputConfig[output] 370 for i, w2 := range conf.windows { 371 if w2 == w { 372 conf.windows = append(conf.windows[:i], conf.windows[i+1:]...) 373 break 374 } 375 } 376 w.updateOutputs() 377 } 378 379 //export gio_onRegistryGlobal 380 func gio_onRegistryGlobal(data unsafe.Pointer, reg *C.struct_wl_registry, name C.uint32_t, cintf *C.char, version C.uint32_t) { 381 switch C.GoString(cintf) { 382 case "wl_compositor": 383 conn.compositor = (*C.struct_wl_compositor)(C.wl_registry_bind(reg, name, &C.wl_compositor_interface, 3)) 384 case "wl_output": 385 output := (*C.struct_wl_output)(C.wl_registry_bind(reg, name, &C.wl_output_interface, 2)) 386 C.gio_wl_output_add_listener(output) 387 outputMap[name] = output 388 outputConfig[output] = new(wlOutput) 389 case "wl_seat": 390 if conn.seat == nil { 391 conn.seatName = name 392 conn.seat = (*C.struct_wl_seat)(C.wl_registry_bind(reg, name, &C.wl_seat_interface, 5)) 393 C.gio_wl_seat_add_listener(conn.seat) 394 } 395 case "wl_shm": 396 conn.shm = (*C.struct_wl_shm)(C.wl_registry_bind(reg, name, &C.wl_shm_interface, 1)) 397 case "xdg_wm_base": 398 conn.wm = (*C.struct_xdg_wm_base)(C.wl_registry_bind(reg, name, &C.xdg_wm_base_interface, 1)) 399 case "zxdg_decoration_manager_v1": 400 conn.decor = (*C.struct_zxdg_decoration_manager_v1)(C.wl_registry_bind(reg, name, &C.zxdg_decoration_manager_v1_interface, 1)) 401 // TODO: Implement and test text-input support. 402 /*case "zwp_text_input_manager_v3": 403 conn.imm = (*C.struct_zwp_text_input_manager_v3)(C.wl_registry_bind(reg, name, &C.zwp_text_input_manager_v3_interface, 1))*/ 404 } 405 } 406 407 //export gio_onRegistryGlobalRemove 408 func gio_onRegistryGlobalRemove(data unsafe.Pointer, reg *C.struct_wl_registry, name C.uint32_t) { 409 if conn.seat != nil && name == conn.seatName { 410 if conn.im != nil { 411 C.zwp_text_input_v3_destroy(conn.im) 412 conn.im = nil 413 } 414 if conn.pointer != nil { 415 delete(winMap, conn.pointer) 416 } 417 if conn.touch != nil { 418 delete(winMap, conn.touch) 419 } 420 if conn.keyboard != nil { 421 delete(winMap, conn.keyboard) 422 } 423 C.wl_seat_release(conn.seat) 424 conn.seat = nil 425 } 426 if output, exists := outputMap[name]; exists { 427 C.wl_output_destroy(output) 428 delete(outputMap, name) 429 delete(outputConfig, output) 430 } 431 } 432 433 //export gio_onTouchDown 434 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) { 435 w := winMap[surf] 436 winMap[touch] = w 437 w.lastTouch = f32.Point{ 438 X: fromFixed(x) * float32(w.scale), 439 Y: fromFixed(y) * float32(w.scale), 440 } 441 w.w.Event(pointer.Event{ 442 Type: pointer.Press, 443 Source: pointer.Touch, 444 Position: w.lastTouch, 445 PointerID: pointer.ID(id), 446 Time: time.Duration(t) * time.Millisecond, 447 }) 448 } 449 450 //export gio_onTouchUp 451 func gio_onTouchUp(data unsafe.Pointer, touch *C.struct_wl_touch, serial, t C.uint32_t, id C.int32_t) { 452 w := winMap[touch] 453 w.w.Event(pointer.Event{ 454 Type: pointer.Release, 455 Source: pointer.Touch, 456 Position: w.lastTouch, 457 PointerID: pointer.ID(id), 458 Time: time.Duration(t) * time.Millisecond, 459 }) 460 } 461 462 //export gio_onTouchMotion 463 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) { 464 w := winMap[touch] 465 w.lastTouch = f32.Point{ 466 X: fromFixed(x) * float32(w.scale), 467 Y: fromFixed(y) * float32(w.scale), 468 } 469 w.w.Event(pointer.Event{ 470 Type: pointer.Move, 471 Position: w.lastTouch, 472 Source: pointer.Touch, 473 PointerID: pointer.ID(id), 474 Time: time.Duration(t) * time.Millisecond, 475 }) 476 } 477 478 //export gio_onTouchFrame 479 func gio_onTouchFrame(data unsafe.Pointer, touch *C.struct_wl_touch) { 480 } 481 482 //export gio_onTouchCancel 483 func gio_onTouchCancel(data unsafe.Pointer, touch *C.struct_wl_touch) { 484 w := winMap[touch] 485 w.w.Event(pointer.Event{ 486 Type: pointer.Cancel, 487 Source: pointer.Touch, 488 }) 489 } 490 491 //export gio_onPointerEnter 492 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) { 493 // Get images[0]. 494 img := *conn.cursor.cursor.images 495 buf := C.wl_cursor_image_get_buffer(img) 496 if buf == nil { 497 return 498 } 499 C.wl_pointer_set_cursor(pointer, serial, conn.cursor.surf, C.int32_t(img.hotspot_x), C.int32_t(img.hotspot_y)) 500 C.wl_surface_attach(conn.cursor.surf, buf, 0, 0) 501 C.wl_surface_damage(conn.cursor.surf, 0, 0, C.int32_t(img.width), C.int32_t(img.height)) 502 C.wl_surface_commit(conn.cursor.surf) 503 w := winMap[surf] 504 winMap[pointer] = w 505 w.lastPos = f32.Point{X: fromFixed(x), Y: fromFixed(y)} 506 } 507 508 //export gio_onPointerLeave 509 func gio_onPointerLeave(data unsafe.Pointer, p *C.struct_wl_pointer, serial C.uint32_t, surface *C.struct_wl_surface) { 510 } 511 512 //export gio_onPointerMotion 513 func gio_onPointerMotion(data unsafe.Pointer, p *C.struct_wl_pointer, t C.uint32_t, x, y C.wl_fixed_t) { 514 w := winMap[p] 515 w.resetFling() 516 w.onPointerMotion(x, y, t) 517 } 518 519 //export gio_onPointerButton 520 func gio_onPointerButton(data unsafe.Pointer, p *C.struct_wl_pointer, serial, t, wbtn, state C.uint32_t) { 521 w := winMap[p] 522 // From linux-event-codes.h. 523 const ( 524 BTN_LEFT = 0x110 525 BTN_RIGHT = 0x111 526 BTN_MIDDLE = 0x112 527 ) 528 var btn pointer.Buttons 529 switch wbtn { 530 case BTN_LEFT: 531 btn = pointer.ButtonLeft 532 case BTN_RIGHT: 533 btn = pointer.ButtonRight 534 case BTN_MIDDLE: 535 btn = pointer.ButtonMiddle 536 default: 537 return 538 } 539 var typ pointer.Type 540 switch state { 541 case 0: 542 w.pointerBtns &^= btn 543 typ = pointer.Release 544 case 1: 545 w.pointerBtns |= btn 546 typ = pointer.Press 547 } 548 w.flushScroll() 549 w.resetFling() 550 w.w.Event(pointer.Event{ 551 Type: typ, 552 Source: pointer.Mouse, 553 Buttons: w.pointerBtns, 554 Position: w.lastPos, 555 Time: time.Duration(t) * time.Millisecond, 556 }) 557 } 558 559 //export gio_onPointerAxis 560 func gio_onPointerAxis(data unsafe.Pointer, ptr *C.struct_wl_pointer, t, axis C.uint32_t, value C.wl_fixed_t) { 561 w := winMap[ptr] 562 v := fromFixed(value) 563 w.resetFling() 564 if w.scroll.dist == (f32.Point{}) { 565 w.scroll.time = time.Duration(t) * time.Millisecond 566 } 567 switch axis { 568 case C.WL_POINTER_AXIS_HORIZONTAL_SCROLL: 569 w.scroll.dist.X += v 570 case C.WL_POINTER_AXIS_VERTICAL_SCROLL: 571 w.scroll.dist.Y += v 572 } 573 } 574 575 //export gio_onPointerFrame 576 func gio_onPointerFrame(data unsafe.Pointer, pointer *C.struct_wl_pointer) { 577 w := winMap[pointer] 578 w.flushScroll() 579 w.flushFling() 580 } 581 582 func (w *window) flushFling() { 583 if !w.fling.start { 584 return 585 } 586 w.fling.start = false 587 estx, esty := w.fling.xExtrapolation.Estimate(), w.fling.yExtrapolation.Estimate() 588 w.fling.xExtrapolation = fling.Extrapolation{} 589 w.fling.yExtrapolation = fling.Extrapolation{} 590 vel := float32(math.Sqrt(float64(estx.Velocity*estx.Velocity + esty.Velocity*esty.Velocity))) 591 _, _, c := w.config() 592 if !w.fling.anim.Start(&c, time.Now(), vel) { 593 return 594 } 595 invDist := 1 / vel 596 w.fling.dir.X = estx.Velocity * invDist 597 w.fling.dir.Y = esty.Velocity * invDist 598 // Wake up the window loop. 599 w.wakeup() 600 } 601 602 //export gio_onPointerAxisSource 603 func gio_onPointerAxisSource(data unsafe.Pointer, pointer *C.struct_wl_pointer, source C.uint32_t) { 604 } 605 606 //export gio_onPointerAxisStop 607 func gio_onPointerAxisStop(data unsafe.Pointer, ptr *C.struct_wl_pointer, t, axis C.uint32_t) { 608 w := winMap[ptr] 609 w.fling.start = true 610 } 611 612 //export gio_onPointerAxisDiscrete 613 func gio_onPointerAxisDiscrete(data unsafe.Pointer, pointer *C.struct_wl_pointer, axis C.uint32_t, discrete C.int32_t) { 614 w := winMap[pointer] 615 w.resetFling() 616 switch axis { 617 case C.WL_POINTER_AXIS_HORIZONTAL_SCROLL: 618 w.scroll.steps.X += int(discrete) 619 case C.WL_POINTER_AXIS_VERTICAL_SCROLL: 620 w.scroll.steps.Y += int(discrete) 621 } 622 } 623 624 func (w *window) resetFling() { 625 w.fling.start = false 626 w.fling.anim = fling.Animation{} 627 } 628 629 //export gio_onKeyboardKeymap 630 func gio_onKeyboardKeymap(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, format C.uint32_t, fd C.int32_t, size C.uint32_t) { 631 defer syscall.Close(int(fd)) 632 conn.repeat.Stop(0) 633 conn.xkb.DestroyKeymapState() 634 if format != C.WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1 { 635 return 636 } 637 if err := conn.xkb.LoadKeymap(int(format), int(fd), int(size)); err != nil { 638 // TODO: Do better. 639 panic(err) 640 } 641 } 642 643 //export gio_onKeyboardEnter 644 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) { 645 conn.repeat.Stop(0) 646 w := winMap[surf] 647 winMap[keyboard] = w 648 w.w.Event(key.FocusEvent{Focus: true}) 649 } 650 651 //export gio_onKeyboardLeave 652 func gio_onKeyboardLeave(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, serial C.uint32_t, surf *C.struct_wl_surface) { 653 conn.repeat.Stop(0) 654 w := winMap[keyboard] 655 w.w.Event(key.FocusEvent{Focus: false}) 656 } 657 658 //export gio_onKeyboardKey 659 func gio_onKeyboardKey(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, serial, timestamp, keyCode, state C.uint32_t) { 660 t := time.Duration(timestamp) * time.Millisecond 661 w := winMap[keyboard] 662 w.resetFling() 663 conn.repeat.Stop(t) 664 if state != C.WL_KEYBOARD_KEY_STATE_PRESSED { 665 return 666 } 667 kc := mapXKBKeycode(uint32(keyCode)) 668 for _, e := range conn.xkb.DispatchKey(kc) { 669 w.w.Event(e) 670 } 671 if conn.xkb.IsRepeatKey(kc) { 672 conn.repeat.Start(w, kc, t) 673 } 674 } 675 676 func mapXKBKeycode(keyCode uint32) uint32 { 677 // According to the xkb_v1 spec: "to determine the xkb keycode, clients must add 8 to the key event keycode." 678 return keyCode + 8 679 } 680 681 func (r *repeatState) Start(w *window, keyCode uint32, t time.Duration) { 682 if r.rate <= 0 { 683 return 684 } 685 stopC := make(chan struct{}) 686 r.start = t 687 r.last = 0 688 r.now = 0 689 r.stopC = stopC 690 r.key = keyCode 691 r.win = w.w 692 rate, delay := r.rate, r.delay 693 go func() { 694 timer := time.NewTimer(delay) 695 for { 696 select { 697 case <-timer.C: 698 case <-stopC: 699 close(stopC) 700 return 701 } 702 r.Advance(delay) 703 w.wakeup() 704 delay = time.Second / time.Duration(rate) 705 timer.Reset(delay) 706 } 707 }() 708 } 709 710 func (r *repeatState) Stop(t time.Duration) { 711 if r.stopC == nil { 712 return 713 } 714 r.stopC <- struct{}{} 715 <-r.stopC 716 r.stopC = nil 717 t -= r.start 718 if r.now > t { 719 r.now = t 720 } 721 } 722 723 func (r *repeatState) Advance(dt time.Duration) { 724 r.mu.Lock() 725 defer r.mu.Unlock() 726 r.now += dt 727 } 728 729 func (r *repeatState) Repeat() { 730 if r.rate <= 0 { 731 return 732 } 733 r.mu.Lock() 734 now := r.now 735 r.mu.Unlock() 736 for { 737 var delay time.Duration 738 if r.last < r.delay { 739 delay = r.delay 740 } else { 741 delay = time.Second / time.Duration(r.rate) 742 } 743 if r.last+delay > now { 744 break 745 } 746 for _, e := range conn.xkb.DispatchKey(r.key) { 747 r.win.Event(e) 748 } 749 r.last += delay 750 } 751 } 752 753 //export gio_onFrameDone 754 func gio_onFrameDone(data unsafe.Pointer, callback *C.struct_wl_callback, t C.uint32_t) { 755 C.wl_callback_destroy(callback) 756 surf := (*C.struct_wl_surface)(data) 757 w := winMap[surf] 758 if w.lastFrameCallback == callback { 759 w.lastFrameCallback = nil 760 w.draw(false) 761 } 762 } 763 764 func (w *window) loop() { 765 dispfd := C.wl_display_get_fd(conn.disp) 766 // Poll for events and notifications. 767 pollfds := []syscall.PollFd{ 768 {Fd: int32(dispfd), Events: syscall.POLLIN | syscall.POLLERR}, 769 {Fd: int32(w.notify.read), Events: syscall.POLLIN | syscall.POLLERR}, 770 } 771 dispFd := &pollfds[0] 772 // Plenty of room for a backlog of notifications. 773 var buf = make([]byte, 100) 774 loop: 775 for { 776 C.wl_display_dispatch_pending(conn.disp) 777 dispFd.Events &^= syscall.POLLOUT 778 if _, err := C.wl_display_flush(conn.disp); err != nil { 779 if err != syscall.EAGAIN { 780 break 781 } 782 // EAGAIN means the output buffer was full. Poll for 783 // POLLOUT to know when we can write again. 784 dispFd.Events |= syscall.POLLOUT 785 } 786 if w.dead { 787 w.w.Event(system.DestroyEvent{Err: w.pendingErr}) 788 break 789 } 790 // Clear poll events. 791 dispFd.Revents = 0 792 if _, err := syscall.Poll(pollfds, -1); err != nil && err != syscall.EINTR { 793 panic(fmt.Errorf("poll failed: %v", err)) 794 } 795 redraw := false 796 // Clear notifications. 797 for { 798 _, err := syscall.Read(w.notify.read, buf) 799 if err == syscall.EAGAIN { 800 break 801 } 802 if err != nil { 803 panic(fmt.Errorf("read from notify pipe failed: %v", err)) 804 } 805 redraw = true 806 } 807 // Handle events 808 switch { 809 case dispFd.Revents&syscall.POLLIN != 0: 810 if ret := C.wl_display_dispatch(conn.disp); ret < 0 { 811 break loop 812 } 813 case dispFd.Revents&(syscall.POLLERR|syscall.POLLHUP) != 0: 814 break loop 815 } 816 conn.repeat.Repeat() 817 if redraw { 818 w.draw(false) 819 } 820 } 821 } 822 823 func (w *window) SetAnimating(anim bool) { 824 w.mu.Lock() 825 w.animating = anim 826 animating := w.isAnimating() 827 w.mu.Unlock() 828 if animating { 829 w.wakeup() 830 } 831 } 832 833 // Wakeup wakes up the event loop through the notification pipe. 834 func (w *window) wakeup() { 835 oneByte := make([]byte, 1) 836 if _, err := syscall.Write(w.notify.write, oneByte); err != nil && err != syscall.EAGAIN { 837 panic(fmt.Errorf("failed to write to pipe: %v", err)) 838 } 839 } 840 841 func (w *window) destroy() { 842 if w.notify.write != 0 { 843 syscall.Close(w.notify.write) 844 w.notify.write = 0 845 } 846 if w.notify.read != 0 { 847 syscall.Close(w.notify.read) 848 w.notify.read = 0 849 } 850 if w.topLvl != nil { 851 delete(winMap, w.topLvl) 852 C.xdg_toplevel_destroy(w.topLvl) 853 } 854 if w.surf != nil { 855 delete(winMap, w.surf) 856 C.wl_surface_destroy(w.surf) 857 } 858 if w.wmSurf != nil { 859 delete(winMap, w.wmSurf) 860 C.xdg_surface_destroy(w.wmSurf) 861 } 862 if w.decor != nil { 863 C.zxdg_toplevel_decoration_v1_destroy(w.decor) 864 } 865 } 866 867 //export gio_onKeyboardModifiers 868 func gio_onKeyboardModifiers(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, serial, depressed, latched, locked, group C.uint32_t) { 869 conn.repeat.Stop(0) 870 if conn.xkb == nil { 871 return 872 } 873 conn.xkb.UpdateMask(uint32(depressed), uint32(latched), uint32(locked), uint32(group), uint32(group), uint32(group)) 874 } 875 876 //export gio_onKeyboardRepeatInfo 877 func gio_onKeyboardRepeatInfo(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, rate, delay C.int32_t) { 878 conn.repeat.Stop(0) 879 conn.repeat.rate = int(rate) 880 conn.repeat.delay = time.Duration(delay) * time.Millisecond 881 } 882 883 //export gio_onTextInputEnter 884 func gio_onTextInputEnter(data unsafe.Pointer, im *C.struct_zwp_text_input_v3, surf *C.struct_wl_surface) { 885 } 886 887 //export gio_onTextInputLeave 888 func gio_onTextInputLeave(data unsafe.Pointer, im *C.struct_zwp_text_input_v3, surf *C.struct_wl_surface) { 889 } 890 891 //export gio_onTextInputPreeditString 892 func gio_onTextInputPreeditString(data unsafe.Pointer, im *C.struct_zwp_text_input_v3, ctxt *C.char, begin, end C.int32_t) { 893 } 894 895 //export gio_onTextInputCommitString 896 func gio_onTextInputCommitString(data unsafe.Pointer, im *C.struct_zwp_text_input_v3, ctxt *C.char) { 897 } 898 899 //export gio_onTextInputDeleteSurroundingText 900 func gio_onTextInputDeleteSurroundingText(data unsafe.Pointer, im *C.struct_zwp_text_input_v3, before, after C.uint32_t) { 901 } 902 903 //export gio_onTextInputDone 904 func gio_onTextInputDone(data unsafe.Pointer, im *C.struct_zwp_text_input_v3, serial C.uint32_t) { 905 } 906 907 func (w *window) flushScroll() { 908 var fling f32.Point 909 if w.fling.anim.Active() { 910 dist := float32(w.fling.anim.Tick(time.Now())) 911 fling = w.fling.dir.Mul(dist) 912 } 913 // The Wayland reported scroll distance for 914 // discrete scroll axes is only 10 pixels, where 915 // 100 seems more appropriate. 916 const discreteScale = 10 917 if w.scroll.steps.X != 0 { 918 w.scroll.dist.X *= discreteScale 919 } 920 if w.scroll.steps.Y != 0 { 921 w.scroll.dist.Y *= discreteScale 922 } 923 total := w.scroll.dist.Add(fling) 924 if total == (f32.Point{}) { 925 return 926 } 927 w.w.Event(pointer.Event{ 928 Type: pointer.Move, 929 Source: pointer.Mouse, 930 Buttons: w.pointerBtns, 931 Position: w.lastPos, 932 Scroll: total, 933 Time: w.scroll.time, 934 }) 935 if w.scroll.steps == (image.Point{}) { 936 w.fling.xExtrapolation.SampleDelta(w.scroll.time, -w.scroll.dist.X) 937 w.fling.yExtrapolation.SampleDelta(w.scroll.time, -w.scroll.dist.Y) 938 } 939 w.scroll.dist = f32.Point{} 940 w.scroll.steps = image.Point{} 941 } 942 943 func (w *window) onPointerMotion(x, y C.wl_fixed_t, t C.uint32_t) { 944 w.flushScroll() 945 w.lastPos = f32.Point{ 946 X: fromFixed(x) * float32(w.scale), 947 Y: fromFixed(y) * float32(w.scale), 948 } 949 w.w.Event(pointer.Event{ 950 Type: pointer.Move, 951 Position: w.lastPos, 952 Buttons: w.pointerBtns, 953 Source: pointer.Mouse, 954 Time: time.Duration(t) * time.Millisecond, 955 }) 956 } 957 958 func (w *window) updateOpaqueRegion() { 959 reg := C.wl_compositor_create_region(conn.compositor) 960 C.wl_region_add(reg, 0, 0, C.int32_t(w.width), C.int32_t(w.height)) 961 C.wl_surface_set_opaque_region(w.surf, reg) 962 C.wl_region_destroy(reg) 963 } 964 965 func (w *window) updateOutputs() { 966 scale := 1 967 var found bool 968 for _, conf := range outputConfig { 969 for _, w2 := range conf.windows { 970 if w2 == w { 971 found = true 972 if conf.scale > scale { 973 scale = conf.scale 974 } 975 } 976 } 977 } 978 w.mu.Lock() 979 if found && scale != w.scale { 980 w.scale = scale 981 w.newScale = true 982 } 983 w.mu.Unlock() 984 if !found { 985 w.setStage(system.StagePaused) 986 } else { 987 w.setStage(system.StageRunning) 988 w.draw(true) 989 } 990 } 991 992 func (w *window) config() (int, int, config) { 993 width, height := w.width*w.scale, w.height*w.scale 994 return width, height, config{ 995 pxPerDp: w.ppdp * float32(w.scale), 996 pxPerSp: w.ppsp * float32(w.scale), 997 } 998 } 999 1000 func (w *window) isAnimating() bool { 1001 return w.animating || w.fling.anim.Active() 1002 } 1003 1004 func (w *window) kill(err error) { 1005 if w.pendingErr == nil { 1006 w.pendingErr = err 1007 } 1008 w.dead = true 1009 } 1010 1011 func (w *window) draw(sync bool) { 1012 w.flushScroll() 1013 w.mu.Lock() 1014 animating := w.isAnimating() 1015 dead := w.dead 1016 w.mu.Unlock() 1017 if dead || (!animating && !sync) { 1018 return 1019 } 1020 width, height, cfg := w.config() 1021 if cfg == (config{}) { 1022 return 1023 } 1024 if animating && w.lastFrameCallback == nil { 1025 w.lastFrameCallback = C.wl_surface_frame(w.surf) 1026 // Use the surface as listener data for gio_onFrameDone. 1027 C.gio_wl_callback_add_listener(w.lastFrameCallback, unsafe.Pointer(w.surf)) 1028 } 1029 cfg.now = time.Now() 1030 w.w.Event(FrameEvent{ 1031 FrameEvent: system.FrameEvent{ 1032 Size: image.Point{ 1033 X: width, 1034 Y: height, 1035 }, 1036 Config: &cfg, 1037 }, 1038 Sync: sync, 1039 }) 1040 } 1041 1042 func (w *window) setStage(s system.Stage) { 1043 if s == w.stage { 1044 return 1045 } 1046 w.stage = s 1047 w.w.Event(system.StageEvent{s}) 1048 } 1049 1050 func (w *window) display() *C.struct_wl_display { 1051 return w.disp 1052 } 1053 1054 func (w *window) surface() (*C.struct_wl_surface, int, int) { 1055 if w.needAck { 1056 C.xdg_surface_ack_configure(w.wmSurf, w.serial) 1057 w.needAck = false 1058 } 1059 width, height, scale := w.width, w.height, w.scale 1060 if w.newScale { 1061 C.wl_surface_set_buffer_scale(w.surf, C.int32_t(scale)) 1062 w.newScale = false 1063 } 1064 return w.surf, width * scale, height * scale 1065 } 1066 1067 func (w *window) ShowTextInput(show bool) {} 1068 1069 // detectUIScale reports the system UI scale, or 1.0 if it fails. 1070 func detectUIScale() float32 { 1071 // TODO: What about other window environments? 1072 out, err := exec.Command("gsettings", "get", "org.gnome.desktop.interface", "text-scaling-factor").Output() 1073 if err != nil { 1074 return 1.0 1075 } 1076 scale, err := strconv.ParseFloat(string(bytes.TrimSpace(out)), 32) 1077 if err != nil { 1078 return 1.0 1079 } 1080 return float32(scale) 1081 } 1082 1083 func waylandConnect() error { 1084 c := new(wlConn) 1085 conn = c 1086 xkb, err := xkb.New() 1087 if err != nil { 1088 c.destroy() 1089 return fmt.Errorf("wayland: %v", err) 1090 } 1091 c.xkb = xkb 1092 c.disp, err = C.wl_display_connect(nil) 1093 if c.disp == nil { 1094 c.destroy() 1095 return fmt.Errorf("wayland: wl_display_connect failed: %v", err) 1096 } 1097 reg := C.wl_display_get_registry(c.disp) 1098 if reg == nil { 1099 c.destroy() 1100 return errors.New("wayland: wl_display_get_registry failed") 1101 } 1102 C.gio_wl_registry_add_listener(reg) 1103 // Wait for the server to register all its globals to the 1104 // registry listener (gio_onRegistryGlobal). 1105 C.wl_display_roundtrip(c.disp) 1106 // Configuration listeners are added to outputs by gio_onRegistryGlobal. 1107 // We need another roundtrip to get the initial output configurations 1108 // through the gio_onOutput* callbacks. 1109 C.wl_display_roundtrip(c.disp) 1110 if c.compositor == nil { 1111 c.destroy() 1112 return errors.New("wayland: no compositor available") 1113 } 1114 if c.wm == nil { 1115 c.destroy() 1116 return errors.New("wayland: no xdg_wm_base available") 1117 } 1118 if c.shm == nil { 1119 c.destroy() 1120 return errors.New("wayland: no wl_shm available") 1121 } 1122 if len(outputMap) == 0 { 1123 c.destroy() 1124 return errors.New("wayland: no outputs available") 1125 } 1126 c.cursor.theme = C.wl_cursor_theme_load(nil, 32, c.shm) 1127 if c.cursor.theme == nil { 1128 c.destroy() 1129 return errors.New("wayland: wl_cursor_theme_load failed") 1130 } 1131 cname := C.CString("left_ptr") 1132 defer C.free(unsafe.Pointer(cname)) 1133 c.cursor.cursor = C.wl_cursor_theme_get_cursor(c.cursor.theme, cname) 1134 if c.cursor.cursor == nil { 1135 c.destroy() 1136 return errors.New("wayland: wl_cursor_theme_get_cursor failed") 1137 } 1138 c.cursor.surf = C.wl_compositor_create_surface(conn.compositor) 1139 if c.cursor.surf == nil { 1140 c.destroy() 1141 return errors.New("wayland: wl_compositor_create_surface failed") 1142 } 1143 return nil 1144 } 1145 1146 func (c *wlConn) destroy() { 1147 c.repeat.Stop(0) 1148 if c.xkb != nil { 1149 c.xkb.Destroy() 1150 c.xkb = nil 1151 } 1152 if c.cursor.surf != nil { 1153 C.wl_surface_destroy(c.cursor.surf) 1154 } 1155 if c.cursor.theme != nil { 1156 C.wl_cursor_theme_destroy(c.cursor.theme) 1157 } 1158 if c.keyboard != nil { 1159 C.wl_keyboard_release(c.keyboard) 1160 } 1161 if c.pointer != nil { 1162 C.wl_pointer_release(c.pointer) 1163 } 1164 if c.touch != nil { 1165 C.wl_touch_release(c.touch) 1166 } 1167 if c.im != nil { 1168 C.zwp_text_input_v3_destroy(c.im) 1169 } 1170 if c.imm != nil { 1171 C.zwp_text_input_manager_v3_destroy(c.imm) 1172 } 1173 if c.seat != nil { 1174 C.wl_seat_release(c.seat) 1175 } 1176 if c.decor != nil { 1177 C.zxdg_decoration_manager_v1_destroy(c.decor) 1178 } 1179 if c.shm != nil { 1180 C.wl_shm_destroy(c.shm) 1181 } 1182 if c.compositor != nil { 1183 C.wl_compositor_destroy(c.compositor) 1184 } 1185 if c.wm != nil { 1186 C.xdg_wm_base_destroy(c.wm) 1187 } 1188 for _, output := range outputMap { 1189 C.wl_output_destroy(output) 1190 } 1191 if c.disp != nil { 1192 C.wl_display_disconnect(c.disp) 1193 } 1194 } 1195 1196 // fromFixed converts a Wayland wl_fixed_t 23.8 number to float32. 1197 func fromFixed(v C.wl_fixed_t) float32 { 1198 // Convert to float64 to avoid overflow. 1199 // From wayland-util.h. 1200 b := ((1023 + 44) << 52) + (1 << 51) + uint64(v) 1201 f := math.Float64frombits(b) - (3 << 43) 1202 return float32(f) 1203 }