gioui.org@v0.6.1-0.20240506124620-7a9ce51988ce/app/os_wayland.go (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  //go:build ((linux && !android) || freebsd) && !nowayland
     4  // +build linux,!android freebsd
     5  // +build !nowayland
     6  
     7  package app
     8  
     9  import (
    10  	"bytes"
    11  	"errors"
    12  	"fmt"
    13  	"image"
    14  	"io"
    15  	"math"
    16  	"os"
    17  	"os/exec"
    18  	"runtime"
    19  	"strconv"
    20  	"sync"
    21  	"time"
    22  	"unsafe"
    23  
    24  	syscall "golang.org/x/sys/unix"
    25  
    26  	"gioui.org/app/internal/xkb"
    27  	"gioui.org/f32"
    28  	"gioui.org/internal/fling"
    29  	"gioui.org/io/event"
    30  	"gioui.org/io/key"
    31  	"gioui.org/io/pointer"
    32  	"gioui.org/io/system"
    33  	"gioui.org/io/transfer"
    34  	"gioui.org/op"
    35  	"gioui.org/unit"
    36  )
    37  
    38  // Use wayland-scanner to generate glue code for the xdg-shell and xdg-decoration extensions.
    39  //go:generate wayland-scanner client-header /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml wayland_xdg_shell.h
    40  //go:generate wayland-scanner private-code /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml wayland_xdg_shell.c
    41  
    42  //go:generate wayland-scanner client-header /usr/share/wayland-protocols/unstable/text-input/text-input-unstable-v3.xml wayland_text_input.h
    43  //go:generate wayland-scanner private-code /usr/share/wayland-protocols/unstable/text-input/text-input-unstable-v3.xml wayland_text_input.c
    44  
    45  //go:generate wayland-scanner client-header /usr/share/wayland-protocols/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml wayland_xdg_decoration.h
    46  //go:generate wayland-scanner private-code /usr/share/wayland-protocols/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml wayland_xdg_decoration.c
    47  
    48  //go:generate sed -i "1s;^;//go:build ((linux \\&\\& !android) || freebsd) \\&\\& !nowayland\\n// +build linux,!android freebsd\\n// +build !nowayland\\n\\n;" wayland_xdg_shell.c
    49  //go:generate sed -i "1s;^;//go:build ((linux \\&\\& !android) || freebsd) \\&\\& !nowayland\\n// +build linux,!android freebsd\\n// +build !nowayland\\n\\n;" wayland_xdg_decoration.c
    50  //go:generate sed -i "1s;^;//go:build ((linux \\&\\& !android) || freebsd) \\&\\& !nowayland\\n// +build linux,!android freebsd\\n// +build !nowayland\\n\\n;" wayland_text_input.c
    51  
    52  /*
    53  #cgo linux pkg-config: wayland-client wayland-cursor
    54  #cgo freebsd openbsd LDFLAGS: -lwayland-client -lwayland-cursor
    55  #cgo freebsd CFLAGS: -I/usr/local/include
    56  #cgo freebsd LDFLAGS: -L/usr/local/lib
    57  
    58  #include <stdlib.h>
    59  #include <wayland-client.h>
    60  #include <wayland-cursor.h>
    61  #include "wayland_text_input.h"
    62  #include "wayland_xdg_shell.h"
    63  #include "wayland_xdg_decoration.h"
    64  
    65  extern const struct wl_registry_listener gio_registry_listener;
    66  extern const struct wl_surface_listener gio_surface_listener;
    67  extern const struct xdg_surface_listener gio_xdg_surface_listener;
    68  extern const struct xdg_toplevel_listener gio_xdg_toplevel_listener;
    69  extern const struct zxdg_toplevel_decoration_v1_listener gio_zxdg_toplevel_decoration_v1_listener;
    70  extern const struct xdg_wm_base_listener gio_xdg_wm_base_listener;
    71  extern const struct wl_callback_listener gio_callback_listener;
    72  extern const struct wl_output_listener gio_output_listener;
    73  extern const struct wl_seat_listener gio_seat_listener;
    74  extern const struct wl_pointer_listener gio_pointer_listener;
    75  extern const struct wl_touch_listener gio_touch_listener;
    76  extern const struct wl_keyboard_listener gio_keyboard_listener;
    77  extern const struct zwp_text_input_v3_listener gio_zwp_text_input_v3_listener;
    78  extern const struct wl_data_device_listener gio_data_device_listener;
    79  extern const struct wl_data_offer_listener gio_data_offer_listener;
    80  extern const struct wl_data_source_listener gio_data_source_listener;
    81  */
    82  import "C"
    83  
    84  type wlDisplay struct {
    85  	disp              *C.struct_wl_display
    86  	reg               *C.struct_wl_registry
    87  	compositor        *C.struct_wl_compositor
    88  	wm                *C.struct_xdg_wm_base
    89  	imm               *C.struct_zwp_text_input_manager_v3
    90  	shm               *C.struct_wl_shm
    91  	dataDeviceManager *C.struct_wl_data_device_manager
    92  	decor             *C.struct_zxdg_decoration_manager_v1
    93  	seat              *wlSeat
    94  	xkb               *xkb.Context
    95  	outputMap         map[C.uint32_t]*C.struct_wl_output
    96  	outputConfig      map[*C.struct_wl_output]*wlOutput
    97  
    98  	// Notification pipe fds.
    99  	notify struct {
   100  		read, write int
   101  	}
   102  
   103  	repeat        repeatState
   104  	poller        poller
   105  	readClipClose chan struct{}
   106  }
   107  
   108  type wlSeat struct {
   109  	disp     *wlDisplay
   110  	seat     *C.struct_wl_seat
   111  	name     C.uint32_t
   112  	pointer  *C.struct_wl_pointer
   113  	touch    *C.struct_wl_touch
   114  	keyboard *C.struct_wl_keyboard
   115  	im       *C.struct_zwp_text_input_v3
   116  
   117  	// The most recent input serial.
   118  	serial C.uint32_t
   119  
   120  	pointerFocus  *window
   121  	keyboardFocus *window
   122  	touchFoci     map[C.int32_t]*window
   123  
   124  	// Clipboard support.
   125  	dataDev *C.struct_wl_data_device
   126  	// offers is a map from active wl_data_offers to
   127  	// the list of mime types they support.
   128  	offers map[*C.struct_wl_data_offer][]string
   129  	// clipboard is the wl_data_offer for the clipboard.
   130  	clipboard *C.struct_wl_data_offer
   131  	// mimeType is the chosen mime type of clipboard.
   132  	mimeType string
   133  	// source represents the clipboard content of the most recent
   134  	// clipboard write, if any.
   135  	source *C.struct_wl_data_source
   136  	// content is the data belonging to source.
   137  	content []byte
   138  }
   139  
   140  type repeatState struct {
   141  	rate  int
   142  	delay time.Duration
   143  
   144  	key   uint32
   145  	win   *window
   146  	stopC chan struct{}
   147  
   148  	start time.Duration
   149  	last  time.Duration
   150  	mu    sync.Mutex
   151  	now   time.Duration
   152  }
   153  
   154  type window struct {
   155  	w          *callbacks
   156  	disp       *wlDisplay
   157  	seat       *wlSeat
   158  	surf       *C.struct_wl_surface
   159  	wmSurf     *C.struct_xdg_surface
   160  	topLvl     *C.struct_xdg_toplevel
   161  	decor      *C.struct_zxdg_toplevel_decoration_v1
   162  	ppdp, ppsp float32
   163  	scroll     struct {
   164  		time  time.Duration
   165  		steps image.Point
   166  		dist  f32.Point
   167  	}
   168  	pointerBtns pointer.Buttons
   169  	lastPos     f32.Point
   170  	lastTouch   f32.Point
   171  
   172  	cursor struct {
   173  		theme  *C.struct_wl_cursor_theme
   174  		cursor *C.struct_wl_cursor
   175  		// system is the active cursor for system gestures
   176  		// such as border resizes and window moves. It
   177  		// is nil if the pointer is not in a system gesture
   178  		// area.
   179  		system  *C.struct_wl_cursor
   180  		surf    *C.struct_wl_surface
   181  		cursors struct {
   182  			pointer         *C.struct_wl_cursor
   183  			resizeNorth     *C.struct_wl_cursor
   184  			resizeSouth     *C.struct_wl_cursor
   185  			resizeWest      *C.struct_wl_cursor
   186  			resizeEast      *C.struct_wl_cursor
   187  			resizeNorthWest *C.struct_wl_cursor
   188  			resizeNorthEast *C.struct_wl_cursor
   189  			resizeSouthWest *C.struct_wl_cursor
   190  			resizeSouthEast *C.struct_wl_cursor
   191  		}
   192  	}
   193  
   194  	fling struct {
   195  		yExtrapolation fling.Extrapolation
   196  		xExtrapolation fling.Extrapolation
   197  		anim           fling.Animation
   198  		start          bool
   199  		dir            f32.Point
   200  	}
   201  
   202  	configured        bool
   203  	lastFrameCallback *C.struct_wl_callback
   204  
   205  	animating bool
   206  	// The most recent configure serial waiting to be ack'ed.
   207  	serial C.uint32_t
   208  	scale  int
   209  	// size is the unscaled window size (unlike config.Size which is scaled).
   210  	size         image.Point
   211  	config       Config
   212  	wsize        image.Point // window config size before going fullscreen or maximized
   213  	inCompositor bool        // window is moving or being resized
   214  
   215  	clipReads chan transfer.DataEvent
   216  
   217  	wakeups chan struct{}
   218  
   219  	closing bool
   220  
   221  	// invMu avoids the race between the destruction of disp and
   222  	// Invalidate waking it up.
   223  	invMu sync.Mutex
   224  }
   225  
   226  type poller struct {
   227  	pollfds [2]syscall.PollFd
   228  	// buf is scratch space for draining the notification pipe.
   229  	buf [100]byte
   230  }
   231  
   232  type wlOutput struct {
   233  	width      int
   234  	height     int
   235  	physWidth  int
   236  	physHeight int
   237  	transform  C.int32_t
   238  	scale      int
   239  	windows    []*window
   240  }
   241  
   242  // callbackMap maps Wayland native handles to corresponding Go
   243  // references. It is necessary because the Wayland client API
   244  // forces the use of callbacks and storing pointers to Go values
   245  // in C is forbidden.
   246  var callbackMap sync.Map
   247  
   248  // clipboardMimeTypes is a list of supported clipboard mime types, in
   249  // order of preference.
   250  var clipboardMimeTypes = []string{"text/plain;charset=utf8", "UTF8_STRING", "text/plain", "TEXT", "STRING"}
   251  
   252  var (
   253  	newWaylandEGLContext    func(w *window) (context, error)
   254  	newWaylandVulkanContext func(w *window) (context, error)
   255  )
   256  
   257  func init() {
   258  	wlDriver = newWLWindow
   259  }
   260  
   261  func newWLWindow(callbacks *callbacks, options []Option) error {
   262  	d, err := newWLDisplay()
   263  	if err != nil {
   264  		return err
   265  	}
   266  	w, err := d.createNativeWindow(options)
   267  	if err != nil {
   268  		d.destroy()
   269  		return err
   270  	}
   271  	w.w = callbacks
   272  	w.w.SetDriver(w)
   273  
   274  	// Finish and commit setup from createNativeWindow.
   275  	w.Configure(options)
   276  	w.draw(true)
   277  	C.wl_surface_commit(w.surf)
   278  
   279  	w.ProcessEvent(WaylandViewEvent{
   280  		Display: unsafe.Pointer(w.display()),
   281  		Surface: unsafe.Pointer(w.surf),
   282  	})
   283  	return nil
   284  }
   285  
   286  func (d *wlDisplay) writeClipboard(content []byte) error {
   287  	s := d.seat
   288  	if s == nil {
   289  		return nil
   290  	}
   291  	// Clear old offer.
   292  	if s.source != nil {
   293  		C.wl_data_source_destroy(s.source)
   294  		s.source = nil
   295  		s.content = nil
   296  	}
   297  	if d.dataDeviceManager == nil || s.dataDev == nil {
   298  		return nil
   299  	}
   300  	s.content = content
   301  	s.source = C.wl_data_device_manager_create_data_source(d.dataDeviceManager)
   302  	C.wl_data_source_add_listener(s.source, &C.gio_data_source_listener, unsafe.Pointer(s.seat))
   303  	for _, mime := range clipboardMimeTypes {
   304  		C.wl_data_source_offer(s.source, C.CString(mime))
   305  	}
   306  	C.wl_data_device_set_selection(s.dataDev, s.source, s.serial)
   307  	return nil
   308  }
   309  
   310  func (d *wlDisplay) readClipboard() (io.ReadCloser, error) {
   311  	s := d.seat
   312  	if s == nil {
   313  		return nil, nil
   314  	}
   315  	if s.clipboard == nil {
   316  		return nil, nil
   317  	}
   318  	r, w, err := os.Pipe()
   319  	if err != nil {
   320  		return nil, err
   321  	}
   322  	// wl_data_offer_receive performs and implicit dup(2) of the write end
   323  	// of the pipe. Close our version.
   324  	defer w.Close()
   325  	cmimeType := C.CString(s.mimeType)
   326  	defer C.free(unsafe.Pointer(cmimeType))
   327  	C.wl_data_offer_receive(s.clipboard, cmimeType, C.int(w.Fd()))
   328  	return r, nil
   329  }
   330  
   331  func (d *wlDisplay) createNativeWindow(options []Option) (*window, error) {
   332  	if d.compositor == nil {
   333  		return nil, errors.New("wayland: no compositor available")
   334  	}
   335  	if d.wm == nil {
   336  		return nil, errors.New("wayland: no xdg_wm_base available")
   337  	}
   338  	if d.shm == nil {
   339  		return nil, errors.New("wayland: no wl_shm available")
   340  	}
   341  	if len(d.outputMap) == 0 {
   342  		return nil, errors.New("wayland: no outputs available")
   343  	}
   344  	var scale int
   345  	for _, conf := range d.outputConfig {
   346  		if s := conf.scale; s > scale {
   347  			scale = s
   348  		}
   349  	}
   350  	ppdp := detectUIScale()
   351  
   352  	w := &window{
   353  		disp:      d,
   354  		scale:     scale,
   355  		ppdp:      ppdp,
   356  		ppsp:      ppdp,
   357  		wakeups:   make(chan struct{}, 1),
   358  		clipReads: make(chan transfer.DataEvent, 1),
   359  	}
   360  	w.surf = C.wl_compositor_create_surface(d.compositor)
   361  	if w.surf == nil {
   362  		w.destroy()
   363  		return nil, errors.New("wayland: wl_compositor_create_surface failed")
   364  	}
   365  	C.wl_surface_set_buffer_scale(w.surf, C.int32_t(w.scale))
   366  	callbackStore(unsafe.Pointer(w.surf), w)
   367  	w.wmSurf = C.xdg_wm_base_get_xdg_surface(d.wm, w.surf)
   368  	if w.wmSurf == nil {
   369  		w.destroy()
   370  		return nil, errors.New("wayland: xdg_wm_base_get_xdg_surface failed")
   371  	}
   372  	w.topLvl = C.xdg_surface_get_toplevel(w.wmSurf)
   373  	if w.topLvl == nil {
   374  		w.destroy()
   375  		return nil, errors.New("wayland: xdg_surface_get_toplevel failed")
   376  	}
   377  
   378  	id := C.CString(ID)
   379  	defer C.free(unsafe.Pointer(id))
   380  	C.xdg_toplevel_set_app_id(w.topLvl, id)
   381  
   382  	cursorTheme := C.CString(os.Getenv("XCURSOR_THEME"))
   383  	defer C.free(unsafe.Pointer(cursorTheme))
   384  	cursorSize := 32
   385  	if envSize, ok := os.LookupEnv("XCURSOR_SIZE"); ok && envSize != "" {
   386  		size, err := strconv.Atoi(envSize)
   387  		if err == nil {
   388  			cursorSize = size
   389  		}
   390  	}
   391  
   392  	w.cursor.theme = C.wl_cursor_theme_load(cursorTheme, C.int(cursorSize*w.scale), d.shm)
   393  	if w.cursor.theme == nil {
   394  		w.destroy()
   395  		return nil, errors.New("wayland: wl_cursor_theme_load failed")
   396  	}
   397  	w.loadCursors()
   398  	w.cursor.cursor = w.cursor.cursors.pointer
   399  	if w.cursor.cursor == nil {
   400  		w.destroy()
   401  		return nil, errors.New("wayland: wl_cursor_theme_get_cursor failed")
   402  	}
   403  	w.cursor.surf = C.wl_compositor_create_surface(d.compositor)
   404  	if w.cursor.surf == nil {
   405  		w.destroy()
   406  		return nil, errors.New("wayland: wl_compositor_create_surface failed")
   407  	}
   408  	C.wl_surface_set_buffer_scale(w.cursor.surf, C.int32_t(w.scale))
   409  	C.xdg_wm_base_add_listener(d.wm, &C.gio_xdg_wm_base_listener, unsafe.Pointer(w.surf))
   410  	C.wl_surface_add_listener(w.surf, &C.gio_surface_listener, unsafe.Pointer(w.surf))
   411  	C.xdg_surface_add_listener(w.wmSurf, &C.gio_xdg_surface_listener, unsafe.Pointer(w.surf))
   412  	C.xdg_toplevel_add_listener(w.topLvl, &C.gio_xdg_toplevel_listener, unsafe.Pointer(w.surf))
   413  
   414  	if d.decor != nil {
   415  		w.decor = C.zxdg_decoration_manager_v1_get_toplevel_decoration(d.decor, w.topLvl)
   416  		C.zxdg_toplevel_decoration_v1_add_listener(w.decor, &C.gio_zxdg_toplevel_decoration_v1_listener, unsafe.Pointer(w.surf))
   417  	}
   418  	w.updateOpaqueRegion()
   419  	return w, nil
   420  }
   421  
   422  func (w *window) loadCursors() {
   423  	w.cursor.cursors.pointer = w.loadCursor(pointer.CursorDefault)
   424  	w.cursor.cursors.resizeNorth = w.loadCursor(pointer.CursorNorthResize)
   425  	w.cursor.cursors.resizeSouth = w.loadCursor(pointer.CursorSouthResize)
   426  	w.cursor.cursors.resizeWest = w.loadCursor(pointer.CursorWestResize)
   427  	w.cursor.cursors.resizeEast = w.loadCursor(pointer.CursorEastResize)
   428  	w.cursor.cursors.resizeSouthWest = w.loadCursor(pointer.CursorSouthWestResize)
   429  	w.cursor.cursors.resizeSouthEast = w.loadCursor(pointer.CursorSouthEastResize)
   430  	w.cursor.cursors.resizeNorthWest = w.loadCursor(pointer.CursorNorthWestResize)
   431  	w.cursor.cursors.resizeNorthEast = w.loadCursor(pointer.CursorNorthEastResize)
   432  }
   433  
   434  func (w *window) loadCursor(name pointer.Cursor) *C.struct_wl_cursor {
   435  	if name == pointer.CursorNone {
   436  		return nil
   437  	}
   438  	xcursor := xCursor[name]
   439  	cname := C.CString(xcursor)
   440  	defer C.free(unsafe.Pointer(cname))
   441  	c := C.wl_cursor_theme_get_cursor(w.cursor.theme, cname)
   442  	if c == nil {
   443  		// Fall back to default cursor.
   444  		c = w.cursor.cursors.pointer
   445  	}
   446  	return c
   447  }
   448  
   449  func callbackDelete(k unsafe.Pointer) {
   450  	callbackMap.Delete(k)
   451  }
   452  
   453  func callbackStore(k unsafe.Pointer, v interface{}) {
   454  	callbackMap.Store(k, v)
   455  }
   456  
   457  func callbackLoad(k unsafe.Pointer) interface{} {
   458  	v, exists := callbackMap.Load(k)
   459  	if !exists {
   460  		panic("missing callback entry")
   461  	}
   462  	return v
   463  }
   464  
   465  //export gio_onSeatCapabilities
   466  func gio_onSeatCapabilities(data unsafe.Pointer, seat *C.struct_wl_seat, caps C.uint32_t) {
   467  	s := callbackLoad(data).(*wlSeat)
   468  	s.updateCaps(caps)
   469  }
   470  
   471  // flushOffers remove all wl_data_offers that isn't the clipboard
   472  // content.
   473  func (s *wlSeat) flushOffers() {
   474  	for o := range s.offers {
   475  		if o == s.clipboard {
   476  			continue
   477  		}
   478  		// We're only interested in clipboard offers.
   479  		delete(s.offers, o)
   480  		callbackDelete(unsafe.Pointer(o))
   481  		C.wl_data_offer_destroy(o)
   482  	}
   483  }
   484  
   485  func (s *wlSeat) destroy() {
   486  	if s.source != nil {
   487  		C.wl_data_source_destroy(s.source)
   488  		s.source = nil
   489  	}
   490  	if s.im != nil {
   491  		C.zwp_text_input_v3_destroy(s.im)
   492  		s.im = nil
   493  	}
   494  	if s.pointer != nil {
   495  		C.wl_pointer_release(s.pointer)
   496  	}
   497  	if s.touch != nil {
   498  		C.wl_touch_release(s.touch)
   499  	}
   500  	if s.keyboard != nil {
   501  		C.wl_keyboard_release(s.keyboard)
   502  	}
   503  	s.clipboard = nil
   504  	s.flushOffers()
   505  	if s.dataDev != nil {
   506  		C.wl_data_device_release(s.dataDev)
   507  	}
   508  	if s.seat != nil {
   509  		callbackDelete(unsafe.Pointer(s.seat))
   510  		C.wl_seat_release(s.seat)
   511  	}
   512  }
   513  
   514  func (s *wlSeat) updateCaps(caps C.uint32_t) {
   515  	if s.im == nil && s.disp.imm != nil {
   516  		s.im = C.zwp_text_input_manager_v3_get_text_input(s.disp.imm, s.seat)
   517  		C.zwp_text_input_v3_add_listener(s.im, &C.gio_zwp_text_input_v3_listener, unsafe.Pointer(s.seat))
   518  	}
   519  	switch {
   520  	case s.pointer == nil && caps&C.WL_SEAT_CAPABILITY_POINTER != 0:
   521  		s.pointer = C.wl_seat_get_pointer(s.seat)
   522  		C.wl_pointer_add_listener(s.pointer, &C.gio_pointer_listener, unsafe.Pointer(s.seat))
   523  	case s.pointer != nil && caps&C.WL_SEAT_CAPABILITY_POINTER == 0:
   524  		C.wl_pointer_release(s.pointer)
   525  		s.pointer = nil
   526  	}
   527  	switch {
   528  	case s.touch == nil && caps&C.WL_SEAT_CAPABILITY_TOUCH != 0:
   529  		s.touch = C.wl_seat_get_touch(s.seat)
   530  		C.wl_touch_add_listener(s.touch, &C.gio_touch_listener, unsafe.Pointer(s.seat))
   531  	case s.touch != nil && caps&C.WL_SEAT_CAPABILITY_TOUCH == 0:
   532  		C.wl_touch_release(s.touch)
   533  		s.touch = nil
   534  	}
   535  	switch {
   536  	case s.keyboard == nil && caps&C.WL_SEAT_CAPABILITY_KEYBOARD != 0:
   537  		s.keyboard = C.wl_seat_get_keyboard(s.seat)
   538  		C.wl_keyboard_add_listener(s.keyboard, &C.gio_keyboard_listener, unsafe.Pointer(s.seat))
   539  	case s.keyboard != nil && caps&C.WL_SEAT_CAPABILITY_KEYBOARD == 0:
   540  		C.wl_keyboard_release(s.keyboard)
   541  		s.keyboard = nil
   542  	}
   543  }
   544  
   545  //export gio_onSeatName
   546  func gio_onSeatName(data unsafe.Pointer, seat *C.struct_wl_seat, name *C.char) {
   547  }
   548  
   549  //export gio_onXdgSurfaceConfigure
   550  func gio_onXdgSurfaceConfigure(data unsafe.Pointer, wmSurf *C.struct_xdg_surface, serial C.uint32_t) {
   551  	w := callbackLoad(data).(*window)
   552  	w.serial = serial
   553  	C.xdg_surface_ack_configure(wmSurf, serial)
   554  	w.configured = true
   555  	w.draw(true)
   556  }
   557  
   558  //export gio_onToplevelClose
   559  func gio_onToplevelClose(data unsafe.Pointer, topLvl *C.struct_xdg_toplevel) {
   560  	w := callbackLoad(data).(*window)
   561  	w.closing = true
   562  }
   563  
   564  //export gio_onToplevelConfigure
   565  func gio_onToplevelConfigure(data unsafe.Pointer, topLvl *C.struct_xdg_toplevel, width, height C.int32_t, states *C.struct_wl_array) {
   566  	w := callbackLoad(data).(*window)
   567  	if width != 0 && height != 0 {
   568  		w.size = image.Pt(int(width), int(height))
   569  		w.updateOpaqueRegion()
   570  	}
   571  }
   572  
   573  //export gio_onToplevelDecorationConfigure
   574  func gio_onToplevelDecorationConfigure(data unsafe.Pointer, deco *C.struct_zxdg_toplevel_decoration_v1, mode C.uint32_t) {
   575  	w := callbackLoad(data).(*window)
   576  	decorated := w.config.Decorated
   577  	switch mode {
   578  	case C.ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE:
   579  		w.config.Decorated = false
   580  	case C.ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE:
   581  		w.config.Decorated = true
   582  	}
   583  	if decorated != w.config.Decorated {
   584  		w.setWindowConstraints()
   585  		if w.config.Decorated {
   586  			w.size.Y -= int(w.config.decoHeight)
   587  		} else {
   588  			w.size.Y += int(w.config.decoHeight)
   589  		}
   590  		w.ProcessEvent(ConfigEvent{Config: w.config})
   591  		w.draw(true)
   592  	}
   593  }
   594  
   595  //export gio_onOutputMode
   596  func gio_onOutputMode(data unsafe.Pointer, output *C.struct_wl_output, flags C.uint32_t, width, height, refresh C.int32_t) {
   597  	if flags&C.WL_OUTPUT_MODE_CURRENT == 0 {
   598  		return
   599  	}
   600  	d := callbackLoad(data).(*wlDisplay)
   601  	c := d.outputConfig[output]
   602  	c.width = int(width)
   603  	c.height = int(height)
   604  }
   605  
   606  //export gio_onOutputGeometry
   607  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) {
   608  	d := callbackLoad(data).(*wlDisplay)
   609  	c := d.outputConfig[output]
   610  	c.transform = transform
   611  	c.physWidth = int(physWidth)
   612  	c.physHeight = int(physHeight)
   613  }
   614  
   615  //export gio_onOutputScale
   616  func gio_onOutputScale(data unsafe.Pointer, output *C.struct_wl_output, scale C.int32_t) {
   617  	d := callbackLoad(data).(*wlDisplay)
   618  	c := d.outputConfig[output]
   619  	c.scale = int(scale)
   620  }
   621  
   622  //export gio_onOutputDone
   623  func gio_onOutputDone(data unsafe.Pointer, output *C.struct_wl_output) {
   624  	d := callbackLoad(data).(*wlDisplay)
   625  	conf := d.outputConfig[output]
   626  	for _, w := range conf.windows {
   627  		w.updateOutputs()
   628  	}
   629  }
   630  
   631  //export gio_onSurfaceEnter
   632  func gio_onSurfaceEnter(data unsafe.Pointer, surf *C.struct_wl_surface, output *C.struct_wl_output) {
   633  	w := callbackLoad(data).(*window)
   634  	conf := w.disp.outputConfig[output]
   635  	var found bool
   636  	for _, w2 := range conf.windows {
   637  		if w2 == w {
   638  			found = true
   639  			break
   640  		}
   641  	}
   642  	if !found {
   643  		conf.windows = append(conf.windows, w)
   644  	}
   645  	w.updateOutputs()
   646  	if w.config.Mode == Minimized {
   647  		// Minimized window got brought back up: it is no longer so.
   648  		w.config.Mode = Windowed
   649  		w.ProcessEvent(ConfigEvent{Config: w.config})
   650  	}
   651  }
   652  
   653  //export gio_onSurfaceLeave
   654  func gio_onSurfaceLeave(data unsafe.Pointer, surf *C.struct_wl_surface, output *C.struct_wl_output) {
   655  	w := callbackLoad(data).(*window)
   656  	conf := w.disp.outputConfig[output]
   657  	for i, w2 := range conf.windows {
   658  		if w2 == w {
   659  			conf.windows = append(conf.windows[:i], conf.windows[i+1:]...)
   660  			break
   661  		}
   662  	}
   663  	w.updateOutputs()
   664  }
   665  
   666  //export gio_onRegistryGlobal
   667  func gio_onRegistryGlobal(data unsafe.Pointer, reg *C.struct_wl_registry, name C.uint32_t, cintf *C.char, version C.uint32_t) {
   668  	d := callbackLoad(data).(*wlDisplay)
   669  	switch C.GoString(cintf) {
   670  	case "wl_compositor":
   671  		d.compositor = (*C.struct_wl_compositor)(C.wl_registry_bind(reg, name, &C.wl_compositor_interface, 3))
   672  	case "wl_output":
   673  		output := (*C.struct_wl_output)(C.wl_registry_bind(reg, name, &C.wl_output_interface, 2))
   674  		C.wl_output_add_listener(output, &C.gio_output_listener, unsafe.Pointer(d.disp))
   675  		d.outputMap[name] = output
   676  		d.outputConfig[output] = new(wlOutput)
   677  	case "wl_seat":
   678  		if d.seat != nil {
   679  			break
   680  		}
   681  		s := (*C.struct_wl_seat)(C.wl_registry_bind(reg, name, &C.wl_seat_interface, 5))
   682  		if s == nil {
   683  			// No support for v5 protocol.
   684  			break
   685  		}
   686  		d.seat = &wlSeat{
   687  			disp:      d,
   688  			name:      name,
   689  			seat:      s,
   690  			offers:    make(map[*C.struct_wl_data_offer][]string),
   691  			touchFoci: make(map[C.int32_t]*window),
   692  		}
   693  		callbackStore(unsafe.Pointer(s), d.seat)
   694  		C.wl_seat_add_listener(s, &C.gio_seat_listener, unsafe.Pointer(s))
   695  		d.bindDataDevice()
   696  	case "wl_shm":
   697  		d.shm = (*C.struct_wl_shm)(C.wl_registry_bind(reg, name, &C.wl_shm_interface, 1))
   698  	case "xdg_wm_base":
   699  		d.wm = (*C.struct_xdg_wm_base)(C.wl_registry_bind(reg, name, &C.xdg_wm_base_interface, 1))
   700  	case "zxdg_decoration_manager_v1":
   701  		d.decor = (*C.struct_zxdg_decoration_manager_v1)(C.wl_registry_bind(reg, name, &C.zxdg_decoration_manager_v1_interface, 1))
   702  		// TODO: Implement and test text-input support.
   703  		/*case "zwp_text_input_manager_v3":
   704  		d.imm = (*C.struct_zwp_text_input_manager_v3)(C.wl_registry_bind(reg, name, &C.zwp_text_input_manager_v3_interface, 1))*/
   705  	case "wl_data_device_manager":
   706  		d.dataDeviceManager = (*C.struct_wl_data_device_manager)(C.wl_registry_bind(reg, name, &C.wl_data_device_manager_interface, 3))
   707  		d.bindDataDevice()
   708  	}
   709  }
   710  
   711  //export gio_onDataOfferOffer
   712  func gio_onDataOfferOffer(data unsafe.Pointer, offer *C.struct_wl_data_offer, mime *C.char) {
   713  	s := callbackLoad(data).(*wlSeat)
   714  	s.offers[offer] = append(s.offers[offer], C.GoString(mime))
   715  }
   716  
   717  //export gio_onDataOfferSourceActions
   718  func gio_onDataOfferSourceActions(data unsafe.Pointer, offer *C.struct_wl_data_offer, acts C.uint32_t) {
   719  }
   720  
   721  //export gio_onDataOfferAction
   722  func gio_onDataOfferAction(data unsafe.Pointer, offer *C.struct_wl_data_offer, act C.uint32_t) {
   723  }
   724  
   725  //export gio_onDataDeviceOffer
   726  func gio_onDataDeviceOffer(data unsafe.Pointer, dataDev *C.struct_wl_data_device, id *C.struct_wl_data_offer) {
   727  	s := callbackLoad(data).(*wlSeat)
   728  	callbackStore(unsafe.Pointer(id), s)
   729  	C.wl_data_offer_add_listener(id, &C.gio_data_offer_listener, unsafe.Pointer(id))
   730  	s.offers[id] = nil
   731  }
   732  
   733  //export gio_onDataDeviceEnter
   734  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) {
   735  	s := callbackLoad(data).(*wlSeat)
   736  	s.serial = serial
   737  	s.flushOffers()
   738  }
   739  
   740  //export gio_onDataDeviceLeave
   741  func gio_onDataDeviceLeave(data unsafe.Pointer, dataDev *C.struct_wl_data_device) {
   742  }
   743  
   744  //export gio_onDataDeviceMotion
   745  func gio_onDataDeviceMotion(data unsafe.Pointer, dataDev *C.struct_wl_data_device, t C.uint32_t, x, y C.wl_fixed_t) {
   746  }
   747  
   748  //export gio_onDataDeviceDrop
   749  func gio_onDataDeviceDrop(data unsafe.Pointer, dataDev *C.struct_wl_data_device) {
   750  }
   751  
   752  //export gio_onDataDeviceSelection
   753  func gio_onDataDeviceSelection(data unsafe.Pointer, dataDev *C.struct_wl_data_device, id *C.struct_wl_data_offer) {
   754  	s := callbackLoad(data).(*wlSeat)
   755  	defer s.flushOffers()
   756  	s.clipboard = nil
   757  loop:
   758  	for _, want := range clipboardMimeTypes {
   759  		for _, got := range s.offers[id] {
   760  			if want != got {
   761  				continue
   762  			}
   763  			s.clipboard = id
   764  			s.mimeType = got
   765  			break loop
   766  		}
   767  	}
   768  }
   769  
   770  //export gio_onRegistryGlobalRemove
   771  func gio_onRegistryGlobalRemove(data unsafe.Pointer, reg *C.struct_wl_registry, name C.uint32_t) {
   772  	d := callbackLoad(data).(*wlDisplay)
   773  	if s := d.seat; s != nil && name == s.name {
   774  		s.destroy()
   775  		d.seat = nil
   776  	}
   777  	if output, exists := d.outputMap[name]; exists {
   778  		C.wl_output_destroy(output)
   779  		delete(d.outputMap, name)
   780  		delete(d.outputConfig, output)
   781  	}
   782  }
   783  
   784  //export gio_onTouchDown
   785  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) {
   786  	s := callbackLoad(data).(*wlSeat)
   787  	s.serial = serial
   788  	w := callbackLoad(unsafe.Pointer(surf)).(*window)
   789  	s.touchFoci[id] = w
   790  	w.lastTouch = f32.Point{
   791  		X: fromFixed(x) * float32(w.scale),
   792  		Y: fromFixed(y) * float32(w.scale),
   793  	}
   794  	w.ProcessEvent(pointer.Event{
   795  		Kind:      pointer.Press,
   796  		Source:    pointer.Touch,
   797  		Position:  w.lastTouch,
   798  		PointerID: pointer.ID(id),
   799  		Time:      time.Duration(t) * time.Millisecond,
   800  		Modifiers: w.disp.xkb.Modifiers(),
   801  	})
   802  }
   803  
   804  //export gio_onTouchUp
   805  func gio_onTouchUp(data unsafe.Pointer, touch *C.struct_wl_touch, serial, t C.uint32_t, id C.int32_t) {
   806  	s := callbackLoad(data).(*wlSeat)
   807  	s.serial = serial
   808  	w := s.touchFoci[id]
   809  	delete(s.touchFoci, id)
   810  	w.ProcessEvent(pointer.Event{
   811  		Kind:      pointer.Release,
   812  		Source:    pointer.Touch,
   813  		Position:  w.lastTouch,
   814  		PointerID: pointer.ID(id),
   815  		Time:      time.Duration(t) * time.Millisecond,
   816  		Modifiers: w.disp.xkb.Modifiers(),
   817  	})
   818  }
   819  
   820  //export gio_onTouchMotion
   821  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) {
   822  	s := callbackLoad(data).(*wlSeat)
   823  	w := s.touchFoci[id]
   824  	w.lastTouch = f32.Point{
   825  		X: fromFixed(x) * float32(w.scale),
   826  		Y: fromFixed(y) * float32(w.scale),
   827  	}
   828  	w.ProcessEvent(pointer.Event{
   829  		Kind:      pointer.Move,
   830  		Position:  w.lastTouch,
   831  		Source:    pointer.Touch,
   832  		PointerID: pointer.ID(id),
   833  		Time:      time.Duration(t) * time.Millisecond,
   834  		Modifiers: w.disp.xkb.Modifiers(),
   835  	})
   836  }
   837  
   838  //export gio_onTouchFrame
   839  func gio_onTouchFrame(data unsafe.Pointer, touch *C.struct_wl_touch) {
   840  }
   841  
   842  //export gio_onTouchCancel
   843  func gio_onTouchCancel(data unsafe.Pointer, touch *C.struct_wl_touch) {
   844  	s := callbackLoad(data).(*wlSeat)
   845  	for id, w := range s.touchFoci {
   846  		delete(s.touchFoci, id)
   847  		w.ProcessEvent(pointer.Event{
   848  			Kind:   pointer.Cancel,
   849  			Source: pointer.Touch,
   850  		})
   851  	}
   852  }
   853  
   854  //export gio_onPointerEnter
   855  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) {
   856  	s := callbackLoad(data).(*wlSeat)
   857  	s.serial = serial
   858  	w := callbackLoad(unsafe.Pointer(surf)).(*window)
   859  	w.seat = s
   860  	s.pointerFocus = w
   861  	w.setCursor(pointer, serial)
   862  	w.lastPos = f32.Point{X: fromFixed(x), Y: fromFixed(y)}
   863  }
   864  
   865  //export gio_onPointerLeave
   866  func gio_onPointerLeave(data unsafe.Pointer, p *C.struct_wl_pointer, serial C.uint32_t, surf *C.struct_wl_surface) {
   867  	w := callbackLoad(unsafe.Pointer(surf)).(*window)
   868  	w.seat = nil
   869  	s := callbackLoad(data).(*wlSeat)
   870  	s.serial = serial
   871  	if w.inCompositor {
   872  		w.inCompositor = false
   873  		w.ProcessEvent(pointer.Event{Kind: pointer.Cancel})
   874  	}
   875  }
   876  
   877  //export gio_onPointerMotion
   878  func gio_onPointerMotion(data unsafe.Pointer, p *C.struct_wl_pointer, t C.uint32_t, x, y C.wl_fixed_t) {
   879  	s := callbackLoad(data).(*wlSeat)
   880  	w := s.pointerFocus
   881  	w.resetFling()
   882  	w.onPointerMotion(x, y, t)
   883  }
   884  
   885  //export gio_onPointerButton
   886  func gio_onPointerButton(data unsafe.Pointer, p *C.struct_wl_pointer, serial, t, wbtn, state C.uint32_t) {
   887  	s := callbackLoad(data).(*wlSeat)
   888  	s.serial = serial
   889  	w := s.pointerFocus
   890  	// From linux-event-codes.h.
   891  	const (
   892  		BTN_LEFT   = 0x110
   893  		BTN_RIGHT  = 0x111
   894  		BTN_MIDDLE = 0x112
   895  	)
   896  	var btn pointer.Buttons
   897  	switch wbtn {
   898  	case BTN_LEFT:
   899  		btn = pointer.ButtonPrimary
   900  	case BTN_RIGHT:
   901  		btn = pointer.ButtonSecondary
   902  	case BTN_MIDDLE:
   903  		btn = pointer.ButtonTertiary
   904  	default:
   905  		return
   906  	}
   907  	if state == 1 && btn == pointer.ButtonPrimary {
   908  		if _, edge := w.systemGesture(); edge != 0 {
   909  			w.resize(serial, edge)
   910  			return
   911  		}
   912  		act, ok := w.w.ActionAt(w.lastPos)
   913  		if ok && w.config.Mode == Windowed {
   914  			switch act {
   915  			case system.ActionMove:
   916  				w.move(serial)
   917  				return
   918  			}
   919  		}
   920  	}
   921  	var kind pointer.Kind
   922  	switch state {
   923  	case 0:
   924  		w.pointerBtns &^= btn
   925  		kind = pointer.Release
   926  		// Move or resize gestures no longer applies.
   927  		w.inCompositor = false
   928  	case 1:
   929  		w.pointerBtns |= btn
   930  		kind = pointer.Press
   931  	}
   932  	w.flushScroll()
   933  	w.resetFling()
   934  	w.ProcessEvent(pointer.Event{
   935  		Kind:      kind,
   936  		Source:    pointer.Mouse,
   937  		Buttons:   w.pointerBtns,
   938  		Position:  w.lastPos,
   939  		Time:      time.Duration(t) * time.Millisecond,
   940  		Modifiers: w.disp.xkb.Modifiers(),
   941  	})
   942  }
   943  
   944  //export gio_onPointerAxis
   945  func gio_onPointerAxis(data unsafe.Pointer, p *C.struct_wl_pointer, t, axis C.uint32_t, value C.wl_fixed_t) {
   946  	s := callbackLoad(data).(*wlSeat)
   947  	w := s.pointerFocus
   948  	v := fromFixed(value)
   949  	w.resetFling()
   950  	if w.scroll.dist == (f32.Point{}) {
   951  		w.scroll.time = time.Duration(t) * time.Millisecond
   952  	}
   953  	switch axis {
   954  	case C.WL_POINTER_AXIS_HORIZONTAL_SCROLL:
   955  		w.scroll.dist.X += v
   956  	case C.WL_POINTER_AXIS_VERTICAL_SCROLL:
   957  		// horizontal scroll if shift + mousewheel(up/down) pressed.
   958  		if w.disp.xkb.Modifiers() == key.ModShift {
   959  			w.scroll.dist.X += v
   960  		} else {
   961  			w.scroll.dist.Y += v
   962  		}
   963  	}
   964  }
   965  
   966  //export gio_onPointerFrame
   967  func gio_onPointerFrame(data unsafe.Pointer, p *C.struct_wl_pointer) {
   968  	s := callbackLoad(data).(*wlSeat)
   969  	w := s.pointerFocus
   970  	w.flushScroll()
   971  	w.flushFling()
   972  }
   973  
   974  func (w *window) flushFling() {
   975  	if !w.fling.start {
   976  		return
   977  	}
   978  	w.fling.start = false
   979  	estx, esty := w.fling.xExtrapolation.Estimate(), w.fling.yExtrapolation.Estimate()
   980  	w.fling.xExtrapolation = fling.Extrapolation{}
   981  	w.fling.yExtrapolation = fling.Extrapolation{}
   982  	vel := float32(math.Sqrt(float64(estx.Velocity*estx.Velocity + esty.Velocity*esty.Velocity)))
   983  	_, c := w.getConfig()
   984  	if !w.fling.anim.Start(c, time.Now(), vel) {
   985  		return
   986  	}
   987  	invDist := 1 / vel
   988  	w.fling.dir.X = estx.Velocity * invDist
   989  	w.fling.dir.Y = esty.Velocity * invDist
   990  }
   991  
   992  //export gio_onPointerAxisSource
   993  func gio_onPointerAxisSource(data unsafe.Pointer, pointer *C.struct_wl_pointer, source C.uint32_t) {
   994  }
   995  
   996  //export gio_onPointerAxisStop
   997  func gio_onPointerAxisStop(data unsafe.Pointer, p *C.struct_wl_pointer, t, axis C.uint32_t) {
   998  	s := callbackLoad(data).(*wlSeat)
   999  	w := s.pointerFocus
  1000  	w.fling.start = true
  1001  }
  1002  
  1003  //export gio_onPointerAxisDiscrete
  1004  func gio_onPointerAxisDiscrete(data unsafe.Pointer, p *C.struct_wl_pointer, axis C.uint32_t, discrete C.int32_t) {
  1005  	s := callbackLoad(data).(*wlSeat)
  1006  	w := s.pointerFocus
  1007  	w.resetFling()
  1008  	switch axis {
  1009  	case C.WL_POINTER_AXIS_HORIZONTAL_SCROLL:
  1010  		w.scroll.steps.X += int(discrete)
  1011  	case C.WL_POINTER_AXIS_VERTICAL_SCROLL:
  1012  		// horizontal scroll if shift + mousewheel(up/down) pressed.
  1013  		if w.disp.xkb.Modifiers() == key.ModShift {
  1014  			w.scroll.steps.X += int(discrete)
  1015  		} else {
  1016  			w.scroll.steps.Y += int(discrete)
  1017  		}
  1018  	}
  1019  }
  1020  
  1021  func (w *window) ReadClipboard() {
  1022  	if w.disp.readClipClose != nil {
  1023  		return
  1024  	}
  1025  	w.disp.readClipClose = make(chan struct{})
  1026  	r, err := w.disp.readClipboard()
  1027  	if r == nil || err != nil {
  1028  		return
  1029  	}
  1030  	// Don't let slow clipboard transfers block event loop.
  1031  	go func() {
  1032  		defer r.Close()
  1033  		data, _ := io.ReadAll(r)
  1034  		e := transfer.DataEvent{
  1035  			Type: "application/text",
  1036  			Open: func() io.ReadCloser {
  1037  				return io.NopCloser(bytes.NewReader(data))
  1038  			},
  1039  		}
  1040  		select {
  1041  		case w.clipReads <- e:
  1042  			w.disp.wakeup()
  1043  		case <-w.disp.readClipClose:
  1044  		}
  1045  	}()
  1046  }
  1047  
  1048  func (w *window) WriteClipboard(mime string, s []byte) {
  1049  	w.disp.writeClipboard(s)
  1050  }
  1051  
  1052  func (w *window) Configure(options []Option) {
  1053  	_, cfg := w.getConfig()
  1054  	prev := w.config
  1055  	cnf := w.config
  1056  	cnf.apply(cfg, options)
  1057  	w.config.decoHeight = cnf.decoHeight
  1058  
  1059  	switch cnf.Mode {
  1060  	case Fullscreen:
  1061  		switch prev.Mode {
  1062  		case Minimized, Fullscreen:
  1063  		default:
  1064  			w.config.Mode = Fullscreen
  1065  			w.wsize = w.config.Size
  1066  			C.xdg_toplevel_set_fullscreen(w.topLvl, nil)
  1067  		}
  1068  	case Minimized:
  1069  		switch prev.Mode {
  1070  		case Minimized, Fullscreen:
  1071  		default:
  1072  			w.config.Mode = Minimized
  1073  			C.xdg_toplevel_set_minimized(w.topLvl)
  1074  		}
  1075  	case Maximized:
  1076  		switch prev.Mode {
  1077  		case Minimized, Fullscreen:
  1078  		default:
  1079  			w.config.Mode = Maximized
  1080  			w.wsize = w.config.Size
  1081  			C.xdg_toplevel_set_maximized(w.topLvl)
  1082  			w.setTitle(prev, cnf)
  1083  		}
  1084  	case Windowed:
  1085  		switch prev.Mode {
  1086  		case Fullscreen:
  1087  			w.config.Mode = Windowed
  1088  			w.size = w.wsize.Div(w.scale)
  1089  			C.xdg_toplevel_unset_fullscreen(w.topLvl)
  1090  		case Minimized:
  1091  			w.config.Mode = Windowed
  1092  		case Maximized:
  1093  			w.config.Mode = Windowed
  1094  			w.size = w.wsize.Div(w.scale)
  1095  			C.xdg_toplevel_unset_maximized(w.topLvl)
  1096  		}
  1097  		w.setTitle(prev, cnf)
  1098  		if prev.Size != cnf.Size {
  1099  			w.config.Size = cnf.Size
  1100  			w.config.Size.Y += int(w.decoHeight()) * w.scale
  1101  			w.size = w.config.Size.Div(w.scale)
  1102  		}
  1103  		w.config.MinSize = cnf.MinSize
  1104  		w.config.MaxSize = cnf.MaxSize
  1105  		w.setWindowConstraints()
  1106  	}
  1107  	w.ProcessEvent(ConfigEvent{Config: w.config})
  1108  }
  1109  
  1110  func (w *window) setWindowConstraints() {
  1111  	decoHeight := w.decoHeight()
  1112  	if scaled := w.config.MinSize.Div(w.scale); scaled != (image.Point{}) {
  1113  		C.xdg_toplevel_set_min_size(w.topLvl, C.int32_t(scaled.X), C.int32_t(scaled.Y+decoHeight))
  1114  	}
  1115  	if scaled := w.config.MaxSize.Div(w.scale); scaled != (image.Point{}) {
  1116  		C.xdg_toplevel_set_max_size(w.topLvl, C.int32_t(scaled.X), C.int32_t(scaled.Y+decoHeight))
  1117  	}
  1118  }
  1119  
  1120  // decoHeight returns the adjustment for client-side decorations, if applicable.
  1121  // The unit is in surface-local coordinates.
  1122  func (w *window) decoHeight() int {
  1123  	if !w.config.Decorated {
  1124  		return int(w.config.decoHeight)
  1125  	}
  1126  	return 0
  1127  }
  1128  
  1129  func (w *window) setTitle(prev, cnf Config) {
  1130  	if prev.Title != cnf.Title {
  1131  		w.config.Title = cnf.Title
  1132  		title := C.CString(cnf.Title)
  1133  		C.xdg_toplevel_set_title(w.topLvl, title)
  1134  		C.free(unsafe.Pointer(title))
  1135  	}
  1136  }
  1137  
  1138  func (w *window) Perform(actions system.Action) {
  1139  	// NB. there is no way for a minimized window to be unminimized.
  1140  	// https://wayland.app/protocols/xdg-shell#xdg_toplevel:request:set_minimized
  1141  	walkActions(actions, func(action system.Action) {
  1142  		switch action {
  1143  		case system.ActionClose:
  1144  			w.closing = true
  1145  		}
  1146  	})
  1147  }
  1148  
  1149  func (w *window) move(serial C.uint32_t) {
  1150  	s := w.seat
  1151  	if !w.inCompositor && s != nil {
  1152  		w.inCompositor = true
  1153  		C.xdg_toplevel_move(w.topLvl, s.seat, serial)
  1154  	}
  1155  }
  1156  
  1157  func (w *window) resize(serial, edge C.uint32_t) {
  1158  	s := w.seat
  1159  	if w.inCompositor || s == nil {
  1160  		return
  1161  	}
  1162  	w.inCompositor = true
  1163  	C.xdg_toplevel_resize(w.topLvl, s.seat, serial, edge)
  1164  }
  1165  
  1166  func (w *window) SetCursor(cursor pointer.Cursor) {
  1167  	w.cursor.cursor = w.loadCursor(cursor)
  1168  	w.updateCursor()
  1169  }
  1170  
  1171  func (w *window) updateCursor() {
  1172  	ptr := w.disp.seat.pointer
  1173  	if ptr == nil {
  1174  		return
  1175  	}
  1176  	w.setCursor(ptr, w.serial)
  1177  }
  1178  
  1179  func (w *window) setCursor(pointer *C.struct_wl_pointer, serial C.uint32_t) {
  1180  	c := w.cursor.system
  1181  	if c == nil {
  1182  		c = w.cursor.cursor
  1183  	}
  1184  	if c == nil {
  1185  		C.wl_pointer_set_cursor(pointer, w.serial, nil, 0, 0)
  1186  		return
  1187  	}
  1188  	// Get images[0].
  1189  	img := *c.images
  1190  	buf := C.wl_cursor_image_get_buffer(img)
  1191  	if buf == nil {
  1192  		return
  1193  	}
  1194  	C.wl_pointer_set_cursor(pointer, serial, w.cursor.surf, C.int32_t(img.hotspot_x/C.uint(w.scale)), C.int32_t(img.hotspot_y/C.uint(w.scale)))
  1195  	C.wl_surface_attach(w.cursor.surf, buf, 0, 0)
  1196  	C.wl_surface_damage(w.cursor.surf, 0, 0, C.int32_t(img.width), C.int32_t(img.height))
  1197  	C.wl_surface_commit(w.cursor.surf)
  1198  }
  1199  
  1200  func (w *window) resetFling() {
  1201  	w.fling.start = false
  1202  	w.fling.anim = fling.Animation{}
  1203  }
  1204  
  1205  //export gio_onKeyboardKeymap
  1206  func gio_onKeyboardKeymap(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, format C.uint32_t, fd C.int32_t, size C.uint32_t) {
  1207  	defer syscall.Close(int(fd))
  1208  	s := callbackLoad(data).(*wlSeat)
  1209  	s.disp.repeat.Stop(0)
  1210  	s.disp.xkb.DestroyKeymapState()
  1211  	if format != C.WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1 {
  1212  		return
  1213  	}
  1214  	if err := s.disp.xkb.LoadKeymap(int(format), int(fd), int(size)); err != nil {
  1215  		// TODO: Do better.
  1216  		panic(err)
  1217  	}
  1218  }
  1219  
  1220  //export gio_onKeyboardEnter
  1221  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) {
  1222  	s := callbackLoad(data).(*wlSeat)
  1223  	s.serial = serial
  1224  	w := callbackLoad(unsafe.Pointer(surf)).(*window)
  1225  	s.keyboardFocus = w
  1226  	s.disp.repeat.Stop(0)
  1227  	w.config.Focused = true
  1228  	w.ProcessEvent(ConfigEvent{Config: w.config})
  1229  }
  1230  
  1231  //export gio_onKeyboardLeave
  1232  func gio_onKeyboardLeave(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, serial C.uint32_t, surf *C.struct_wl_surface) {
  1233  	s := callbackLoad(data).(*wlSeat)
  1234  	s.serial = serial
  1235  	s.disp.repeat.Stop(0)
  1236  	w := s.keyboardFocus
  1237  	w.config.Focused = false
  1238  	w.ProcessEvent(ConfigEvent{Config: w.config})
  1239  }
  1240  
  1241  //export gio_onKeyboardKey
  1242  func gio_onKeyboardKey(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, serial, timestamp, keyCode, state C.uint32_t) {
  1243  	s := callbackLoad(data).(*wlSeat)
  1244  	s.serial = serial
  1245  	w := s.keyboardFocus
  1246  	t := time.Duration(timestamp) * time.Millisecond
  1247  	s.disp.repeat.Stop(t)
  1248  	w.resetFling()
  1249  	kc := mapXKBKeycode(uint32(keyCode))
  1250  	ks := mapXKBKeyState(uint32(state))
  1251  	for _, e := range w.disp.xkb.DispatchKey(kc, ks) {
  1252  		if ee, ok := e.(key.EditEvent); ok {
  1253  			// There's no support for IME yet.
  1254  			w.w.EditorInsert(ee.Text)
  1255  		} else {
  1256  			w.ProcessEvent(e)
  1257  		}
  1258  	}
  1259  	if state != C.WL_KEYBOARD_KEY_STATE_PRESSED {
  1260  		return
  1261  	}
  1262  	if w.disp.xkb.IsRepeatKey(kc) {
  1263  		w.disp.repeat.Start(w, kc, t)
  1264  	}
  1265  }
  1266  
  1267  func mapXKBKeycode(keyCode uint32) uint32 {
  1268  	// According to the xkb_v1 spec: "to determine the xkb keycode, clients must add 8 to the key event keycode."
  1269  	return keyCode + 8
  1270  }
  1271  
  1272  func mapXKBKeyState(state uint32) key.State {
  1273  	switch state {
  1274  	case C.WL_KEYBOARD_KEY_STATE_RELEASED:
  1275  		return key.Release
  1276  	default:
  1277  		return key.Press
  1278  	}
  1279  }
  1280  
  1281  func (r *repeatState) Start(w *window, keyCode uint32, t time.Duration) {
  1282  	if r.rate <= 0 {
  1283  		return
  1284  	}
  1285  	stopC := make(chan struct{})
  1286  	r.start = t
  1287  	r.last = 0
  1288  	r.now = 0
  1289  	r.stopC = stopC
  1290  	r.key = keyCode
  1291  	r.win = w
  1292  	rate, delay := r.rate, r.delay
  1293  	go func() {
  1294  		timer := time.NewTimer(delay)
  1295  		for {
  1296  			select {
  1297  			case <-timer.C:
  1298  			case <-stopC:
  1299  				close(stopC)
  1300  				return
  1301  			}
  1302  			r.Advance(delay)
  1303  			w.disp.wakeup()
  1304  			delay = time.Second / time.Duration(rate)
  1305  			timer.Reset(delay)
  1306  		}
  1307  	}()
  1308  }
  1309  
  1310  func (r *repeatState) Stop(t time.Duration) {
  1311  	if r.stopC == nil {
  1312  		return
  1313  	}
  1314  	r.stopC <- struct{}{}
  1315  	<-r.stopC
  1316  	r.stopC = nil
  1317  	t -= r.start
  1318  	if r.now > t {
  1319  		r.now = t
  1320  	}
  1321  }
  1322  
  1323  func (r *repeatState) Advance(dt time.Duration) {
  1324  	r.mu.Lock()
  1325  	defer r.mu.Unlock()
  1326  	r.now += dt
  1327  }
  1328  
  1329  func (r *repeatState) Repeat(d *wlDisplay) {
  1330  	if r.rate <= 0 {
  1331  		return
  1332  	}
  1333  	r.mu.Lock()
  1334  	now := r.now
  1335  	r.mu.Unlock()
  1336  	for {
  1337  		var delay time.Duration
  1338  		if r.last < r.delay {
  1339  			delay = r.delay
  1340  		} else {
  1341  			delay = time.Second / time.Duration(r.rate)
  1342  		}
  1343  		if r.last+delay > now {
  1344  			break
  1345  		}
  1346  		for _, e := range d.xkb.DispatchKey(r.key, key.Press) {
  1347  			if ee, ok := e.(key.EditEvent); ok {
  1348  				// There's no support for IME yet.
  1349  				r.win.w.EditorInsert(ee.Text)
  1350  			} else {
  1351  				r.win.ProcessEvent(e)
  1352  			}
  1353  		}
  1354  		r.last += delay
  1355  	}
  1356  }
  1357  
  1358  //export gio_onFrameDone
  1359  func gio_onFrameDone(data unsafe.Pointer, callback *C.struct_wl_callback, t C.uint32_t) {
  1360  	C.wl_callback_destroy(callback)
  1361  	w := callbackLoad(data).(*window)
  1362  	if w.lastFrameCallback == callback {
  1363  		w.lastFrameCallback = nil
  1364  		w.draw(false)
  1365  	}
  1366  }
  1367  
  1368  func (w *window) close(err error) {
  1369  	w.ProcessEvent(WaylandViewEvent{})
  1370  	w.ProcessEvent(DestroyEvent{Err: err})
  1371  	w.destroy()
  1372  	w.invMu.Lock()
  1373  	w.disp.destroy()
  1374  	w.disp = nil
  1375  	w.invMu.Unlock()
  1376  }
  1377  
  1378  func (w *window) dispatch() {
  1379  	if w.disp == nil {
  1380  		<-w.wakeups
  1381  		w.w.Invalidate()
  1382  		return
  1383  	}
  1384  	if err := w.disp.dispatch(); err != nil || w.closing {
  1385  		w.close(err)
  1386  		return
  1387  	}
  1388  	select {
  1389  	case e := <-w.clipReads:
  1390  		w.disp.readClipClose = nil
  1391  		w.ProcessEvent(e)
  1392  	case <-w.wakeups:
  1393  		w.w.Invalidate()
  1394  	default:
  1395  	}
  1396  }
  1397  
  1398  func (w *window) ProcessEvent(e event.Event) {
  1399  	w.w.ProcessEvent(e)
  1400  }
  1401  
  1402  func (w *window) Event() event.Event {
  1403  	for {
  1404  		evt, ok := w.w.nextEvent()
  1405  		if !ok {
  1406  			w.dispatch()
  1407  			continue
  1408  		}
  1409  		return evt
  1410  	}
  1411  }
  1412  
  1413  func (w *window) Invalidate() {
  1414  	select {
  1415  	case w.wakeups <- struct{}{}:
  1416  	default:
  1417  		return
  1418  	}
  1419  	w.invMu.Lock()
  1420  	defer w.invMu.Unlock()
  1421  	if w.disp != nil {
  1422  		w.disp.wakeup()
  1423  	}
  1424  }
  1425  
  1426  func (w *window) Run(f func()) {
  1427  	f()
  1428  }
  1429  
  1430  func (w *window) Frame(frame *op.Ops) {
  1431  	w.w.ProcessFrame(frame, nil)
  1432  }
  1433  
  1434  // bindDataDevice initializes the dataDev field if and only if both
  1435  // the seat and dataDeviceManager fields are initialized.
  1436  func (d *wlDisplay) bindDataDevice() {
  1437  	if d.seat != nil && d.dataDeviceManager != nil {
  1438  		d.seat.dataDev = C.wl_data_device_manager_get_data_device(d.dataDeviceManager, d.seat.seat)
  1439  		if d.seat.dataDev == nil {
  1440  			return
  1441  		}
  1442  		callbackStore(unsafe.Pointer(d.seat.dataDev), d.seat)
  1443  		C.wl_data_device_add_listener(d.seat.dataDev, &C.gio_data_device_listener, unsafe.Pointer(d.seat.dataDev))
  1444  	}
  1445  }
  1446  
  1447  func (d *wlDisplay) dispatch() error {
  1448  	// wl_display_prepare_read records the current thread for
  1449  	// use in wl_display_read_events or wl_display_cancel_events.
  1450  	runtime.LockOSThread()
  1451  	defer runtime.UnlockOSThread()
  1452  
  1453  	dispfd := C.wl_display_get_fd(d.disp)
  1454  	// Poll for events and notifications.
  1455  	pollfds := append(d.poller.pollfds[:0],
  1456  		syscall.PollFd{Fd: int32(dispfd), Events: syscall.POLLIN | syscall.POLLERR},
  1457  		syscall.PollFd{Fd: int32(d.notify.read), Events: syscall.POLLIN | syscall.POLLERR},
  1458  	)
  1459  	for C.wl_display_prepare_read(d.disp) != 0 {
  1460  		C.wl_display_dispatch_pending(d.disp)
  1461  	}
  1462  	dispFd := &pollfds[0]
  1463  	if ret, err := C.wl_display_flush(d.disp); ret < 0 {
  1464  		if err != syscall.EAGAIN {
  1465  			return fmt.Errorf("wayland: wl_display_flush failed: %v", err)
  1466  		}
  1467  		// EAGAIN means the output buffer was full. Poll for
  1468  		// POLLOUT to know when we can write again.
  1469  		dispFd.Events |= syscall.POLLOUT
  1470  	}
  1471  	if _, err := syscall.Poll(pollfds, -1); err != nil && err != syscall.EINTR {
  1472  		C.wl_display_cancel_read(d.disp)
  1473  		return fmt.Errorf("wayland: poll failed: %v", err)
  1474  	}
  1475  	if dispFd.Revents&(syscall.POLLERR|syscall.POLLHUP) != 0 {
  1476  		C.wl_display_cancel_read(d.disp)
  1477  		return errors.New("wayland: display file descriptor gone")
  1478  	}
  1479  	// Handle events.
  1480  	if dispFd.Revents&syscall.POLLIN != 0 {
  1481  		if ret, err := C.wl_display_read_events(d.disp); ret < 0 {
  1482  			return fmt.Errorf("wayland: wl_display_read_events failed: %v", err)
  1483  		}
  1484  		C.wl_display_dispatch_pending(d.disp)
  1485  	} else {
  1486  		C.wl_display_cancel_read(d.disp)
  1487  	}
  1488  	// Clear notifications.
  1489  	for {
  1490  		_, err := syscall.Read(d.notify.read, d.poller.buf[:])
  1491  		if err == syscall.EAGAIN {
  1492  			break
  1493  		}
  1494  		if err != nil {
  1495  			return fmt.Errorf("wayland: read from notify pipe failed: %v", err)
  1496  		}
  1497  	}
  1498  	d.repeat.Repeat(d)
  1499  	return nil
  1500  }
  1501  
  1502  func (w *window) SetAnimating(anim bool) {
  1503  	w.animating = anim
  1504  	if anim {
  1505  		w.draw(false)
  1506  	}
  1507  }
  1508  
  1509  // Wakeup wakes up the event loop through the notification pipe.
  1510  func (d *wlDisplay) wakeup() {
  1511  	oneByte := make([]byte, 1)
  1512  	if _, err := syscall.Write(d.notify.write, oneByte); err != nil && err != syscall.EAGAIN {
  1513  		panic(fmt.Errorf("failed to write to pipe: %v", err))
  1514  	}
  1515  }
  1516  
  1517  func (w *window) destroy() {
  1518  	if w.lastFrameCallback != nil {
  1519  		C.wl_callback_destroy(w.lastFrameCallback)
  1520  		w.lastFrameCallback = nil
  1521  	}
  1522  	if w.cursor.surf != nil {
  1523  		C.wl_surface_destroy(w.cursor.surf)
  1524  	}
  1525  	if w.cursor.theme != nil {
  1526  		C.wl_cursor_theme_destroy(w.cursor.theme)
  1527  	}
  1528  	if w.topLvl != nil {
  1529  		C.xdg_toplevel_destroy(w.topLvl)
  1530  	}
  1531  	if w.surf != nil {
  1532  		C.wl_surface_destroy(w.surf)
  1533  	}
  1534  	if w.wmSurf != nil {
  1535  		C.xdg_surface_destroy(w.wmSurf)
  1536  	}
  1537  	if w.decor != nil {
  1538  		C.zxdg_toplevel_decoration_v1_destroy(w.decor)
  1539  	}
  1540  	callbackDelete(unsafe.Pointer(w.surf))
  1541  }
  1542  
  1543  //export gio_onKeyboardModifiers
  1544  func gio_onKeyboardModifiers(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, serial, depressed, latched, locked, group C.uint32_t) {
  1545  	s := callbackLoad(data).(*wlSeat)
  1546  	s.serial = serial
  1547  	d := s.disp
  1548  	d.repeat.Stop(0)
  1549  	if d.xkb == nil {
  1550  		return
  1551  	}
  1552  	d.xkb.UpdateMask(uint32(depressed), uint32(latched), uint32(locked), uint32(group), uint32(group), uint32(group))
  1553  }
  1554  
  1555  //export gio_onKeyboardRepeatInfo
  1556  func gio_onKeyboardRepeatInfo(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, rate, delay C.int32_t) {
  1557  	s := callbackLoad(data).(*wlSeat)
  1558  	d := s.disp
  1559  	d.repeat.Stop(0)
  1560  	d.repeat.rate = int(rate)
  1561  	d.repeat.delay = time.Duration(delay) * time.Millisecond
  1562  }
  1563  
  1564  //export gio_onTextInputEnter
  1565  func gio_onTextInputEnter(data unsafe.Pointer, im *C.struct_zwp_text_input_v3, surf *C.struct_wl_surface) {
  1566  }
  1567  
  1568  //export gio_onTextInputLeave
  1569  func gio_onTextInputLeave(data unsafe.Pointer, im *C.struct_zwp_text_input_v3, surf *C.struct_wl_surface) {
  1570  }
  1571  
  1572  //export gio_onTextInputPreeditString
  1573  func gio_onTextInputPreeditString(data unsafe.Pointer, im *C.struct_zwp_text_input_v3, ctxt *C.char, begin, end C.int32_t) {
  1574  }
  1575  
  1576  //export gio_onTextInputCommitString
  1577  func gio_onTextInputCommitString(data unsafe.Pointer, im *C.struct_zwp_text_input_v3, ctxt *C.char) {
  1578  }
  1579  
  1580  //export gio_onTextInputDeleteSurroundingText
  1581  func gio_onTextInputDeleteSurroundingText(data unsafe.Pointer, im *C.struct_zwp_text_input_v3, before, after C.uint32_t) {
  1582  }
  1583  
  1584  //export gio_onTextInputDone
  1585  func gio_onTextInputDone(data unsafe.Pointer, im *C.struct_zwp_text_input_v3, serial C.uint32_t) {
  1586  	s := callbackLoad(data).(*wlSeat)
  1587  	s.serial = serial
  1588  }
  1589  
  1590  //export gio_onDataSourceTarget
  1591  func gio_onDataSourceTarget(data unsafe.Pointer, source *C.struct_wl_data_source, mime *C.char) {
  1592  }
  1593  
  1594  //export gio_onDataSourceSend
  1595  func gio_onDataSourceSend(data unsafe.Pointer, source *C.struct_wl_data_source, mime *C.char, fd C.int32_t) {
  1596  	s := callbackLoad(data).(*wlSeat)
  1597  	content := s.content
  1598  	go func() {
  1599  		defer syscall.Close(int(fd))
  1600  		syscall.Write(int(fd), content)
  1601  	}()
  1602  }
  1603  
  1604  //export gio_onDataSourceCancelled
  1605  func gio_onDataSourceCancelled(data unsafe.Pointer, source *C.struct_wl_data_source) {
  1606  	s := callbackLoad(data).(*wlSeat)
  1607  	if s.source == source {
  1608  		s.content = nil
  1609  		s.source = nil
  1610  	}
  1611  	C.wl_data_source_destroy(source)
  1612  }
  1613  
  1614  //export gio_onDataSourceDNDDropPerformed
  1615  func gio_onDataSourceDNDDropPerformed(data unsafe.Pointer, source *C.struct_wl_data_source) {
  1616  }
  1617  
  1618  //export gio_onDataSourceDNDFinished
  1619  func gio_onDataSourceDNDFinished(data unsafe.Pointer, source *C.struct_wl_data_source) {
  1620  }
  1621  
  1622  //export gio_onDataSourceAction
  1623  func gio_onDataSourceAction(data unsafe.Pointer, source *C.struct_wl_data_source, act C.uint32_t) {
  1624  }
  1625  
  1626  func (w *window) flushScroll() {
  1627  	var fling f32.Point
  1628  	if w.fling.anim.Active() {
  1629  		dist := float32(w.fling.anim.Tick(time.Now()))
  1630  		fling = w.fling.dir.Mul(dist)
  1631  	}
  1632  	// The Wayland reported scroll distance for
  1633  	// discrete scroll axes is only 10 pixels, where
  1634  	// 100 seems more appropriate.
  1635  	const discreteScale = 10
  1636  	if w.scroll.steps.X != 0 {
  1637  		w.scroll.dist.X *= discreteScale
  1638  	}
  1639  	if w.scroll.steps.Y != 0 {
  1640  		w.scroll.dist.Y *= discreteScale
  1641  	}
  1642  	total := w.scroll.dist.Add(fling)
  1643  	if total == (f32.Point{}) {
  1644  		return
  1645  	}
  1646  	w.ProcessEvent(pointer.Event{
  1647  		Kind:      pointer.Scroll,
  1648  		Source:    pointer.Mouse,
  1649  		Buttons:   w.pointerBtns,
  1650  		Position:  w.lastPos,
  1651  		Scroll:    total,
  1652  		Time:      w.scroll.time,
  1653  		Modifiers: w.disp.xkb.Modifiers(),
  1654  	})
  1655  	if w.scroll.steps == (image.Point{}) {
  1656  		w.fling.xExtrapolation.SampleDelta(w.scroll.time, -w.scroll.dist.X)
  1657  		w.fling.yExtrapolation.SampleDelta(w.scroll.time, -w.scroll.dist.Y)
  1658  	}
  1659  	w.scroll.dist = f32.Point{}
  1660  	w.scroll.steps = image.Point{}
  1661  }
  1662  
  1663  func (w *window) onPointerMotion(x, y C.wl_fixed_t, t C.uint32_t) {
  1664  	w.flushScroll()
  1665  	w.lastPos = f32.Point{
  1666  		X: fromFixed(x) * float32(w.scale),
  1667  		Y: fromFixed(y) * float32(w.scale),
  1668  	}
  1669  	w.ProcessEvent(pointer.Event{
  1670  		Kind:      pointer.Move,
  1671  		Position:  w.lastPos,
  1672  		Buttons:   w.pointerBtns,
  1673  		Source:    pointer.Mouse,
  1674  		Time:      time.Duration(t) * time.Millisecond,
  1675  		Modifiers: w.disp.xkb.Modifiers(),
  1676  	})
  1677  	c, _ := w.systemGesture()
  1678  	if c != w.cursor.system {
  1679  		w.cursor.system = c
  1680  		w.updateCursor()
  1681  	}
  1682  }
  1683  
  1684  // updateCursor updates the system gesture cursor according to the pointer
  1685  // position.
  1686  func (w *window) systemGesture() (*C.struct_wl_cursor, C.uint32_t) {
  1687  	if w.config.Mode != Windowed || w.config.Decorated {
  1688  		return nil, 0
  1689  	}
  1690  	_, cfg := w.getConfig()
  1691  	border := cfg.Dp(3)
  1692  	x, y, size := int(w.lastPos.X), int(w.lastPos.Y), w.config.Size
  1693  	north := y <= border
  1694  	south := y >= size.Y-border
  1695  	west := x <= border
  1696  	east := x >= size.X-border
  1697  
  1698  	switch {
  1699  	default:
  1700  		fallthrough
  1701  	case !north && !south && !west && !east:
  1702  		return nil, 0
  1703  	case north && west:
  1704  		return w.cursor.cursors.resizeNorthWest, C.XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT
  1705  	case north && east:
  1706  		return w.cursor.cursors.resizeNorthEast, C.XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT
  1707  	case south && west:
  1708  		return w.cursor.cursors.resizeSouthWest, C.XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT
  1709  	case south && east:
  1710  		return w.cursor.cursors.resizeSouthEast, C.XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT
  1711  	case north:
  1712  		return w.cursor.cursors.resizeNorth, C.XDG_TOPLEVEL_RESIZE_EDGE_TOP
  1713  	case south:
  1714  		return w.cursor.cursors.resizeSouth, C.XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM
  1715  	case west:
  1716  		return w.cursor.cursors.resizeWest, C.XDG_TOPLEVEL_RESIZE_EDGE_LEFT
  1717  	case east:
  1718  		return w.cursor.cursors.resizeEast, C.XDG_TOPLEVEL_RESIZE_EDGE_RIGHT
  1719  	}
  1720  }
  1721  
  1722  func (w *window) updateOpaqueRegion() {
  1723  	reg := C.wl_compositor_create_region(w.disp.compositor)
  1724  	C.wl_region_add(reg, 0, 0, C.int32_t(w.size.X), C.int32_t(w.size.Y))
  1725  	C.wl_surface_set_opaque_region(w.surf, reg)
  1726  	C.wl_region_destroy(reg)
  1727  }
  1728  
  1729  func (w *window) updateOutputs() {
  1730  	scale := 1
  1731  	var found bool
  1732  	for _, conf := range w.disp.outputConfig {
  1733  		for _, w2 := range conf.windows {
  1734  			if w2 == w {
  1735  				found = true
  1736  				if conf.scale > scale {
  1737  					scale = conf.scale
  1738  				}
  1739  			}
  1740  		}
  1741  	}
  1742  	if found && scale != w.scale {
  1743  		w.scale = scale
  1744  		C.wl_surface_set_buffer_scale(w.surf, C.int32_t(w.scale))
  1745  		w.draw(true)
  1746  	}
  1747  	if found {
  1748  		w.draw(true)
  1749  	}
  1750  }
  1751  
  1752  func (w *window) getConfig() (image.Point, unit.Metric) {
  1753  	size := w.size.Mul(w.scale)
  1754  	return size, unit.Metric{
  1755  		PxPerDp: w.ppdp * float32(w.scale),
  1756  		PxPerSp: w.ppsp * float32(w.scale),
  1757  	}
  1758  }
  1759  
  1760  func (w *window) draw(sync bool) {
  1761  	if !w.configured {
  1762  		return
  1763  	}
  1764  	w.flushScroll()
  1765  	size, cfg := w.getConfig()
  1766  	if cfg == (unit.Metric{}) {
  1767  		return
  1768  	}
  1769  	if size != w.config.Size {
  1770  		w.config.Size = size
  1771  		w.ProcessEvent(ConfigEvent{Config: w.config})
  1772  	}
  1773  	anim := w.animating || w.fling.anim.Active()
  1774  	// Draw animation only when not waiting for frame callback.
  1775  	redrawAnim := anim && w.lastFrameCallback == nil
  1776  	if !redrawAnim && !sync {
  1777  		return
  1778  	}
  1779  	if anim {
  1780  		w.lastFrameCallback = C.wl_surface_frame(w.surf)
  1781  		// Use the surface as listener data for gio_onFrameDone.
  1782  		C.wl_callback_add_listener(w.lastFrameCallback, &C.gio_callback_listener, unsafe.Pointer(w.surf))
  1783  	}
  1784  	w.ProcessEvent(frameEvent{
  1785  		FrameEvent: FrameEvent{
  1786  			Now:    time.Now(),
  1787  			Size:   w.config.Size,
  1788  			Metric: cfg,
  1789  		},
  1790  		Sync: sync,
  1791  	})
  1792  }
  1793  
  1794  func (w *window) display() *C.struct_wl_display {
  1795  	return w.disp.disp
  1796  }
  1797  
  1798  func (w *window) surface() (*C.struct_wl_surface, int, int) {
  1799  	sz, _ := w.getConfig()
  1800  	return w.surf, sz.X, sz.Y
  1801  }
  1802  
  1803  func (w *window) ShowTextInput(show bool) {}
  1804  
  1805  func (w *window) SetInputHint(_ key.InputHint) {}
  1806  
  1807  func (w *window) EditorStateChanged(old, new editorState) {}
  1808  
  1809  func (w *window) NewContext() (context, error) {
  1810  	var firstErr error
  1811  	if f := newWaylandEGLContext; f != nil {
  1812  		c, err := f(w)
  1813  		if err == nil {
  1814  			return c, nil
  1815  		}
  1816  		firstErr = err
  1817  	}
  1818  	if f := newWaylandVulkanContext; f != nil {
  1819  		c, err := f(w)
  1820  		if err == nil {
  1821  			return c, nil
  1822  		}
  1823  		firstErr = err
  1824  	}
  1825  	if firstErr != nil {
  1826  		return nil, firstErr
  1827  	}
  1828  	return nil, errors.New("wayland: no available GPU backends")
  1829  }
  1830  
  1831  // detectUIScale reports the system UI scale, or 1.0 if it fails.
  1832  func detectUIScale() float32 {
  1833  	// TODO: What about other window environments?
  1834  	out, err := exec.Command("gsettings", "get", "org.gnome.desktop.interface", "text-scaling-factor").Output()
  1835  	if err != nil {
  1836  		return 1.0
  1837  	}
  1838  	scale, err := strconv.ParseFloat(string(bytes.TrimSpace(out)), 32)
  1839  	if err != nil {
  1840  		return 1.0
  1841  	}
  1842  	return float32(scale)
  1843  }
  1844  
  1845  func newWLDisplay() (*wlDisplay, error) {
  1846  	d := &wlDisplay{
  1847  		outputMap:    make(map[C.uint32_t]*C.struct_wl_output),
  1848  		outputConfig: make(map[*C.struct_wl_output]*wlOutput),
  1849  	}
  1850  	pipe := make([]int, 2)
  1851  	if err := syscall.Pipe2(pipe, syscall.O_NONBLOCK|syscall.O_CLOEXEC); err != nil {
  1852  		return nil, fmt.Errorf("wayland: failed to create pipe: %v", err)
  1853  	}
  1854  	d.notify.read = pipe[0]
  1855  	d.notify.write = pipe[1]
  1856  	xkb, err := xkb.New()
  1857  	if err != nil {
  1858  		d.destroy()
  1859  		return nil, fmt.Errorf("wayland: %v", err)
  1860  	}
  1861  	d.xkb = xkb
  1862  	d.disp, err = C.wl_display_connect(nil)
  1863  	if d.disp == nil {
  1864  		d.destroy()
  1865  		return nil, fmt.Errorf("wayland: wl_display_connect failed: %v", err)
  1866  	}
  1867  	callbackMap.Store(unsafe.Pointer(d.disp), d)
  1868  	d.reg = C.wl_display_get_registry(d.disp)
  1869  	if d.reg == nil {
  1870  		d.destroy()
  1871  		return nil, errors.New("wayland: wl_display_get_registry failed")
  1872  	}
  1873  	C.wl_registry_add_listener(d.reg, &C.gio_registry_listener, unsafe.Pointer(d.disp))
  1874  	// Wait for the server to register all its globals to the
  1875  	// registry listener (gio_onRegistryGlobal).
  1876  	C.wl_display_roundtrip(d.disp)
  1877  	// Configuration listeners are added to outputs by gio_onRegistryGlobal.
  1878  	// We need another roundtrip to get the initial output configurations
  1879  	// through the gio_onOutput* callbacks.
  1880  	C.wl_display_roundtrip(d.disp)
  1881  	return d, nil
  1882  }
  1883  
  1884  func (d *wlDisplay) destroy() {
  1885  	if d.readClipClose != nil {
  1886  		close(d.readClipClose)
  1887  		d.readClipClose = nil
  1888  	}
  1889  	if d.notify.write != 0 {
  1890  		syscall.Close(d.notify.write)
  1891  		d.notify.write = 0
  1892  	}
  1893  	if d.notify.read != 0 {
  1894  		syscall.Close(d.notify.read)
  1895  		d.notify.read = 0
  1896  	}
  1897  	d.repeat.Stop(0)
  1898  	if d.xkb != nil {
  1899  		d.xkb.Destroy()
  1900  		d.xkb = nil
  1901  	}
  1902  	if d.seat != nil {
  1903  		d.seat.destroy()
  1904  		d.seat = nil
  1905  	}
  1906  	if d.imm != nil {
  1907  		C.zwp_text_input_manager_v3_destroy(d.imm)
  1908  	}
  1909  	if d.decor != nil {
  1910  		C.zxdg_decoration_manager_v1_destroy(d.decor)
  1911  	}
  1912  	if d.shm != nil {
  1913  		C.wl_shm_destroy(d.shm)
  1914  	}
  1915  	if d.compositor != nil {
  1916  		C.wl_compositor_destroy(d.compositor)
  1917  	}
  1918  	if d.wm != nil {
  1919  		C.xdg_wm_base_destroy(d.wm)
  1920  	}
  1921  	for _, output := range d.outputMap {
  1922  		C.wl_output_destroy(output)
  1923  	}
  1924  	if d.reg != nil {
  1925  		C.wl_registry_destroy(d.reg)
  1926  	}
  1927  	if d.disp != nil {
  1928  		C.wl_display_disconnect(d.disp)
  1929  		callbackDelete(unsafe.Pointer(d.disp))
  1930  		d.disp = nil
  1931  	}
  1932  }
  1933  
  1934  // fromFixed converts a Wayland wl_fixed_t 23.8 number to float32.
  1935  func fromFixed(v C.wl_fixed_t) float32 {
  1936  	// Convert to float64 to avoid overflow.
  1937  	// From wayland-util.h.
  1938  	b := ((1023 + 44) << 52) + (1 << 51) + uint64(v)
  1939  	f := math.Float64frombits(b) - (3 << 43)
  1940  	return float32(f)
  1941  }