github.com/rajveermalviya/gamen@v0.1.2-0.20220930195403-9be15877c1aa/internal/wayland/display.go (about) 1 //go:build linux && !android 2 3 package wayland 4 5 /* 6 7 #include <stdlib.h> 8 #include "wayland-client-protocol.h" 9 #include "xdg-shell-client-protocol.h" 10 #include "xdg-decoration-unstable-v1-client-protocol.h" 11 12 extern const struct wl_registry_listener gamen_wl_registry_listener; 13 extern const struct wl_output_listener gamen_wl_output_listener; 14 extern const struct xdg_wm_base_listener gamen_xdg_wm_base_listener; 15 extern const struct wl_seat_listener gamen_wl_seat_listener; 16 extern const struct wl_pointer_listener gamen_wl_pointer_listener; 17 extern const struct wl_keyboard_listener gamen_wl_keyboard_listener; 18 extern const struct wl_callback_listener gamen_wl_callback_listener; 19 20 */ 21 import "C" 22 23 import ( 24 "errors" 25 "log" 26 "runtime/cgo" 27 "time" 28 "unsafe" 29 30 "github.com/rajveermalviya/gamen/internal/common/atomicx" 31 "github.com/rajveermalviya/gamen/internal/common/mathx" 32 "github.com/rajveermalviya/gamen/internal/xkbcommon" 33 "golang.org/x/sys/unix" 34 ) 35 36 type Display struct { 37 l *wl_library 38 39 // handle for Display to be passed between cgo callbacks 40 handle *cgo.Handle 41 // we allow destroy function to be called multiple 42 // times, but in reality we run it once 43 destroyRequested atomicx.Bool 44 destroyed atomicx.Bool 45 46 // wayland objects 47 display *C.struct_wl_display 48 registry *C.struct_wl_registry 49 compositor *C.struct_wl_compositor 50 shm *C.struct_wl_shm 51 xdgWmBase *C.struct_xdg_wm_base 52 seat *C.struct_wl_seat 53 xdgDecorationManager *C.struct_zxdg_decoration_manager_v1 54 55 // wayland seats 56 pointer *Pointer 57 keyboard *Keyboard 58 59 // we use xkbcommon to parse keymaps and 60 // handle compose sequences 61 xkb *xkbcommon.Xkb 62 63 outputs map[*C.struct_wl_output]*Output 64 windows map[*C.struct_wl_surface]*Window 65 } 66 67 func NewDisplay() (*Display, error) { 68 l, err := open_wl_library() 69 if err != nil { 70 return nil, err 71 } 72 73 // connect to wayland server 74 display := l.wl_display_connect( 75 /* name of socket */ nil, // use default path 76 ) 77 if display == nil { 78 return nil, errors.New("failed to connect to wayland server") 79 } 80 81 d := &Display{ 82 l: l, 83 display: display, 84 windows: make(map[*C.struct_wl_surface]*Window), 85 outputs: make(map[*C.struct_wl_output]*Output), 86 } 87 handle := cgo.NewHandle(d) 88 d.handle = &handle 89 90 // register all interfaces 91 d.registry = l.wl_display_get_registry(d.display) 92 l.wl_registry_add_listener(d.registry, &C.gamen_wl_registry_listener, unsafe.Pointer(d.handle)) 93 94 // wait for interface register callbacks 95 l.wl_display_roundtrip(d.display) 96 // wait for initial interface events 97 l.wl_display_roundtrip(d.display) 98 99 // initialize xkbcommon 100 xkb, err := xkbcommon.New() 101 if err != nil { 102 log.Printf("unable to inititalize xkbcommon: %v\n", err) 103 } 104 d.xkb = xkb 105 106 return d, nil 107 } 108 109 func (d *Display) Destroy() { 110 d.destroyRequested.Store(true) 111 } 112 113 func (d *Display) destroy() { 114 // destroy all the windows 115 for _, w := range d.windows { 116 w.Destroy() 117 } 118 119 if d.keyboard != nil { 120 d.keyboard.destroy() 121 d.keyboard = nil 122 } 123 124 if d.pointer != nil && d.pointer.pointer != nil { 125 d.pointer.destroy() 126 d.pointer = nil 127 } 128 129 if d.xkb != nil { 130 d.xkb.Destroy() 131 d.xkb = nil 132 } 133 134 if d.seat != nil { 135 d.l.wl_seat_destroy(d.seat) 136 d.seat = nil 137 } 138 139 if d.xdgDecorationManager != nil { 140 d.l.zxdg_decoration_manager_v1_destroy(d.xdgDecorationManager) 141 d.xdgDecorationManager = nil 142 } 143 144 if d.xdgWmBase != nil { 145 d.l.xdg_wm_base_destroy(d.xdgWmBase) 146 d.xdgWmBase = nil 147 } 148 149 for output := range d.outputs { 150 d.l.wl_output_destroy(output) 151 d.outputs[output] = nil 152 delete(d.outputs, output) 153 } 154 155 if d.shm != nil { 156 d.l.wl_shm_destroy(d.shm) 157 d.shm = nil 158 } 159 160 if d.compositor != nil { 161 d.l.wl_compositor_destroy(d.compositor) 162 d.compositor = nil 163 } 164 165 if d.registry != nil { 166 d.l.wl_registry_destroy(d.registry) 167 d.registry = nil 168 } 169 170 if d.display != nil { 171 d.l.wl_display_disconnect(d.display) 172 d.display = nil 173 } 174 175 if d.handle != nil { 176 d.handle.Delete() 177 d.handle = nil 178 } 179 180 if d.l != nil { 181 d.l.close() 182 d.l = nil 183 } 184 185 d.destroyed.Store(true) 186 } 187 188 // wayland defers key repeat to clients 189 func (d *Display) handleRepeatKeyFromPoll() { 190 k := d.keyboard 191 if k == nil { 192 return 193 } 194 195 if k.repeatKey == 0 { 196 // there is no key pressed 197 return 198 } 199 200 if k.haveServerRepeat && k.serverRepeatRate == 0 { 201 // server prefers no repeat 202 return 203 } 204 205 rate := k.serverRepeatRate 206 delay := k.serverRepeatDelay 207 if !k.haveServerRepeat { 208 // some default values 209 rate = 33 210 delay = 500 * time.Millisecond 211 } 212 213 // we have to wait for 'delay' duration 214 // until we can start sending key repeat events 215 if time.Since(k.repeatKeyStartTime) < delay { 216 return 217 } 218 219 // interval between two key repeat events 220 interval := time.Second / time.Duration(rate) 221 222 if time.Since(k.repeatKeyLastSendTime) > interval { 223 // send the event as interval has passed 224 k.handleKeyEvent(C.uint32_t(k.repeatKey), WL_KEYBOARD_KEY_STATE_PRESSED) 225 k.repeatKeyLastSendTime = time.Now() 226 } 227 } 228 229 func (d *Display) Poll() bool { 230 d.handleRepeatKeyFromPoll() 231 r := d.pollAndDispatchEvents(0) 232 233 if d.destroyRequested.Load() && !d.destroyed.Load() { 234 d.destroy() 235 return false 236 } 237 238 return r != -1 239 } 240 241 func (d *Display) Wait() bool { 242 // TODO: find a better way to do this 243 // 244 // we switch to Poll if a key is pressed 245 // to handle key repeats 246 k := d.keyboard 247 if k != nil { 248 if k.repeatKey != 0 { 249 return d.Poll() 250 } 251 } 252 253 r := d.pollAndDispatchEvents(-1) 254 255 if d.destroyRequested.Load() && !d.destroyed.Load() { 256 d.destroy() 257 return false 258 } 259 260 return r != -1 261 } 262 263 func (d *Display) WaitTimeout(timeout time.Duration) bool { 264 // TODO: find a better way to do this 265 // 266 // we switch to Poll if a key is pressed 267 // to handle key repeats 268 k := d.keyboard 269 if k != nil { 270 if k.repeatKey != 0 { 271 return d.Poll() 272 } 273 } 274 275 r := d.pollAndDispatchEvents(timeout) 276 277 if d.destroyRequested.Load() && !d.destroyed.Load() { 278 d.destroy() 279 return false 280 } 281 282 return r != -1 283 } 284 285 // schedule's a callback to run on main eventqueue and main thread 286 func (d *Display) scheduleCallback(fn func()) { 287 cb := d.l.wl_display_sync(d.display) 288 d.setCallbackListener(cb, func() { 289 d.l.wl_callback_destroy(cb) 290 fn() 291 }) 292 } 293 294 func (d *Display) setCallbackListener(cb *C.struct_wl_callback, fn func()) { 295 fnHandle := cgo.NewHandle(fn) 296 d.l.wl_callback_add_listener(cb, &C.gamen_wl_callback_listener, unsafe.Pointer(&fnHandle)) 297 } 298 299 //export goWlCallbackDone 300 func goWlCallbackDone(data unsafe.Pointer, wl_callback *C.struct_wl_callback, callback_data C.uint32_t) { 301 fnHandle := (*cgo.Handle)(data) 302 defer fnHandle.Delete() 303 304 fn, ok := fnHandle.Value().(func()) 305 if !ok { 306 return 307 } 308 309 fn() 310 } 311 312 //export registryHandleGlobal 313 func registryHandleGlobal(data unsafe.Pointer, wl_registry *C.struct_wl_registry, name C.uint32_t, iface *C.char, version C.uint32_t) { 314 d, ok := (*cgo.Handle)(data).Value().(*Display) 315 if !ok { 316 return 317 } 318 319 switch C.GoString(iface) { 320 case C.GoString(C.wl_compositor_interface.name): 321 d.compositor = (*C.struct_wl_compositor)(d.l.wl_registry_bind(wl_registry, name, &C.wl_compositor_interface, mathx.Min(5, version))) 322 323 case C.GoString(C.wl_shm_interface.name): 324 d.shm = (*C.struct_wl_shm)(d.l.wl_registry_bind(wl_registry, name, &C.wl_shm_interface, mathx.Min(1, version))) 325 326 case C.GoString(C.zxdg_decoration_manager_v1_interface.name): 327 d.xdgDecorationManager = (*C.struct_zxdg_decoration_manager_v1)(d.l.wl_registry_bind(wl_registry, name, &C.zxdg_decoration_manager_v1_interface, mathx.Min(1, version))) 328 329 case C.GoString(C.wl_output_interface.name): 330 output := (*C.struct_wl_output)(d.l.wl_registry_bind(wl_registry, name, &C.wl_output_interface, mathx.Min(2, version))) 331 d.outputs[output] = &Output{ 332 output: output, 333 name: uint32(name), 334 scaleFactor: 1, 335 } 336 d.l.wl_output_add_listener(output, &C.gamen_wl_output_listener, unsafe.Pointer(d.handle)) 337 338 case C.GoString(C.xdg_wm_base_interface.name): 339 d.xdgWmBase = (*C.struct_xdg_wm_base)(d.l.wl_registry_bind(wl_registry, name, &C.xdg_wm_base_interface, mathx.Min(4, version))) 340 d.l.xdg_wm_base_add_listener(d.xdgWmBase, &C.gamen_xdg_wm_base_listener, unsafe.Pointer(d.handle)) 341 342 case C.GoString(C.wl_seat_interface.name): 343 d.seat = (*C.struct_wl_seat)(d.l.wl_registry_bind(wl_registry, name, &C.wl_seat_interface, mathx.Min(5, version))) 344 d.l.wl_seat_add_listener(d.seat, &C.gamen_wl_seat_listener, unsafe.Pointer(d.handle)) 345 } 346 } 347 348 //export registryHandleGlobalRemove 349 func registryHandleGlobalRemove(data unsafe.Pointer, wl_registry *C.struct_wl_registry, name C.uint32_t) { 350 d, ok := (*cgo.Handle)(data).Value().(*Display) 351 if !ok { 352 return 353 } 354 355 for _, output := range d.outputs { 356 if output.name == uint32(name) { 357 d.l.wl_output_destroy(output.output) 358 d.outputs[output.output] = nil 359 delete(d.outputs, output.output) 360 } 361 } 362 } 363 364 //export xdgWmBaseHandlePing 365 func xdgWmBaseHandlePing(data unsafe.Pointer, xdg_wm_base *C.struct_xdg_wm_base, serial C.uint32_t) { 366 d, ok := (*cgo.Handle)(data).Value().(*Display) 367 if !ok { 368 return 369 } 370 371 d.l.xdg_wm_base_pong(xdg_wm_base, serial) 372 } 373 374 //export seatHandleCapabilities 375 func seatHandleCapabilities(data unsafe.Pointer, wl_seat *C.struct_wl_seat, capabilities enum_wl_seat_capability) { 376 d, ok := (*cgo.Handle)(data).Value().(*Display) 377 if !ok { 378 return 379 } 380 381 if (capabilities&WL_SEAT_CAPABILITY_POINTER) != 0 && d.pointer == nil { 382 pointer := d.l.wl_seat_get_pointer(wl_seat) 383 d.pointer = &Pointer{ 384 d: d, 385 pointer: pointer, 386 cursorThemes: make(map[uint32]*C.struct_wl_cursor_theme), 387 } 388 389 d.l.wl_pointer_add_listener(pointer, &C.gamen_wl_pointer_listener, unsafe.Pointer(d.handle)) 390 } else if (capabilities&WL_SEAT_CAPABILITY_POINTER) == 0 && d.pointer != nil { 391 d.pointer.destroy() 392 d.pointer = nil 393 } 394 395 if (capabilities&WL_SEAT_CAPABILITY_KEYBOARD) != 0 && d.keyboard == nil { 396 keyboard := d.l.wl_seat_get_keyboard(wl_seat) 397 d.keyboard = &Keyboard{ 398 d: d, 399 keyboard: keyboard, 400 } 401 402 d.l.wl_keyboard_add_listener(keyboard, &C.gamen_wl_keyboard_listener, unsafe.Pointer(d.handle)) 403 } else if (capabilities&WL_SEAT_CAPABILITY_KEYBOARD) == 0 && d.keyboard != nil { 404 d.keyboard.destroy() 405 d.keyboard = nil 406 } 407 } 408 409 // helper poll function to correctly handle 410 // timeouts when EINTR occurs 411 func poll(fds []unix.PollFd, timeout time.Duration) bool { 412 switch timeout { 413 case -1: 414 for { 415 result, errno := unix.Ppoll(fds, nil, nil) 416 if result > 0 { 417 return true 418 } else if result == -1 && errno != unix.EINTR && errno != unix.EAGAIN { 419 return false 420 } 421 } 422 423 case 0: 424 for { 425 result, errno := unix.Ppoll(fds, &unix.Timespec{}, nil) 426 if result == -1 && errno != unix.EINTR && errno != unix.EAGAIN { 427 return false 428 } else { 429 return true 430 } 431 } 432 433 default: 434 for { 435 start := time.Now() 436 437 ts := unix.NsecToTimespec(int64(timeout)) 438 result, errno := unix.Ppoll(fds, &ts, nil) 439 440 timeout -= time.Since(start) 441 442 if result > 0 { 443 return true 444 } else if result == -1 && errno != unix.EINTR && errno != unix.EAGAIN { 445 return false 446 } else if timeout <= 0 { 447 return true 448 } 449 } 450 } 451 } 452 453 // loose port of wl_display_dispatch to handle timeouts 454 // TODO: maybe move this to C 455 func (d *Display) pollAndDispatchEvents(timeout time.Duration) (ret C.int) { 456 var errno error 457 if ret = d.l.wl_display_prepare_read(d.display); ret == -1 { 458 ret = d.l.wl_display_dispatch_pending(d.display) 459 return 460 } 461 462 for { 463 ret, errno = d.l.wl_display_flush(d.display) 464 if ret != -1 || errno != unix.EAGAIN { 465 break 466 } 467 468 fds := []unix.PollFd{{ 469 Fd: int32(d.l.wl_display_get_fd(d.display)), 470 Events: unix.POLLOUT, 471 }} 472 473 if r, _ := unix.Ppoll(fds, nil, nil); r == -1 { 474 d.l.wl_display_cancel_read(d.display) 475 return -1 476 } 477 } 478 479 if ret < 0 && errno != unix.EPIPE { 480 d.l.wl_display_cancel_read(d.display) 481 return -1 482 } 483 484 fds := []unix.PollFd{{ 485 Fd: int32(d.l.wl_display_get_fd(d.display)), 486 Events: unix.POLLIN, 487 }} 488 if !poll(fds, timeout) { 489 d.l.wl_display_cancel_read(d.display) 490 return -1 491 } 492 493 if d.l.wl_display_read_events(d.display) == -1 { 494 return -1 495 } 496 497 return d.l.wl_display_dispatch_pending(d.display) 498 }