gioui.org/ui@v0.0.0-20190926171558-ce74bc0cbaea/app/os_wayland.go (about)

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