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