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  }