github.com/gop9/olt@v0.0.0-20200202132135-d956aad50b08/gio/app/internal/window/os_wayland.go (about)

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