github.com/rajveermalviya/gamen@v0.1.2-0.20220930195403-9be15877c1aa/internal/wayland/pointer.go (about)

     1  //go:build linux && !android
     2  
     3  package wayland
     4  
     5  /*
     6  
     7  #include <stdlib.h>
     8  #include "wayland-util.h"
     9  #include "wayland-cursor.h"
    10  
    11  */
    12  import "C"
    13  
    14  import (
    15  	"runtime/cgo"
    16  	"sync"
    17  	"time"
    18  	"unsafe"
    19  
    20  	"github.com/rajveermalviya/gamen/events"
    21  )
    22  
    23  type Pointer struct {
    24  	d  *Display
    25  	mu sync.Mutex
    26  
    27  	pointer *C.struct_wl_pointer
    28  
    29  	serial uint32
    30  	focus  *C.struct_wl_surface
    31  
    32  	pixelDeltaVertical   float64
    33  	pixelDeltaHorizontal float64
    34  
    35  	lineDeltaVertical   float64
    36  	lineDeltaHorizontal float64
    37  
    38  	currentCursor                   *C.struct_wl_cursor
    39  	currentCursorAnimationStartTime time.Time
    40  	cursorThemes                    map[uint32]*C.struct_wl_cursor_theme
    41  	cursorSurface                   *C.struct_wl_surface
    42  	cursorSurfaceFrameCallback      *C.struct_wl_callback
    43  }
    44  
    45  func (p *Pointer) destroy() {
    46  	if p.currentCursor != nil {
    47  		p.currentCursor = nil
    48  	}
    49  
    50  	if p.cursorSurfaceFrameCallback != nil {
    51  		p.d.l.wl_callback_destroy(p.cursorSurfaceFrameCallback)
    52  		p.cursorSurfaceFrameCallback = nil
    53  	}
    54  
    55  	if p.cursorSurface != nil {
    56  		p.d.l.wl_surface_destroy(p.cursorSurface)
    57  		p.cursorSurface = nil
    58  	}
    59  
    60  	if p.cursorThemes != nil {
    61  		for _, theme := range p.cursorThemes {
    62  			p.d.l.wl_cursor_theme_destroy(theme)
    63  		}
    64  		p.cursorThemes = nil
    65  	}
    66  
    67  	if p.pointer != nil {
    68  		p.d.l.wl_pointer_destroy(p.pointer)
    69  		p.pointer = nil
    70  	}
    71  }
    72  
    73  func (p *Pointer) loadCursor(name string, size uint32, scaleFactor float64) *C.struct_wl_cursor {
    74  	p.mu.Lock()
    75  	defer p.mu.Unlock()
    76  
    77  	size = size * uint32(scaleFactor)
    78  
    79  	theme, ok := p.cursorThemes[size]
    80  	if !ok {
    81  		theme = p.d.l.wl_cursor_theme_load(nil, C.int(size), p.d.shm)
    82  		p.cursorThemes[size] = theme
    83  	}
    84  
    85  	nameStr := C.CString(name)
    86  	defer C.free(unsafe.Pointer(nameStr))
    87  
    88  	cursor := p.d.l.wl_cursor_theme_get_cursor(theme, nameStr)
    89  	if cursor == nil {
    90  		return nil
    91  	}
    92  	if cursor.image_count == 0 {
    93  		return nil
    94  	}
    95  
    96  	return cursor
    97  }
    98  
    99  func (p *Pointer) setCursor(cursor *C.struct_wl_cursor, name string, scaleFactor float64) {
   100  	p.mu.Lock()
   101  	defer p.mu.Unlock()
   102  
   103  	// hide cursor
   104  	if cursor == nil {
   105  		p.d.l.wl_pointer_set_cursor(p.pointer, C.uint32_t(p.serial), nil, 0, 0)
   106  		p.currentCursor = nil
   107  		return
   108  	}
   109  
   110  	if p.cursorSurfaceFrameCallback != nil {
   111  		p.d.l.wl_callback_destroy(p.cursorSurfaceFrameCallback)
   112  		p.cursorSurfaceFrameCallback = nil
   113  	}
   114  
   115  	imageSlice := unsafe.Slice(cursor.images, cursor.image_count)
   116  	image := imageSlice[0]
   117  	cursorBuffer := p.d.l.wl_cursor_image_get_buffer(image)
   118  
   119  	if p.cursorSurface == nil {
   120  		p.cursorSurface = p.d.l.wl_compositor_create_surface(p.d.compositor)
   121  	}
   122  
   123  	p.d.l.wl_surface_set_buffer_scale(p.cursorSurface, C.int32_t(scaleFactor))
   124  	p.d.l.wl_surface_attach(p.cursorSurface, cursorBuffer, 0, 0)
   125  	p.d.l.wl_surface_damage_buffer(p.cursorSurface, 0, 0, C.int32_t(image.width), C.int32_t(image.height))
   126  	p.d.l.wl_surface_commit(p.cursorSurface)
   127  
   128  	p.d.l.wl_pointer_set_cursor(
   129  		p.pointer,
   130  		C.uint32_t(p.serial),
   131  		p.cursorSurface,
   132  		C.int32_t(float64(image.hotspot_x)/scaleFactor),
   133  		C.int32_t(float64(image.hotspot_y)/scaleFactor),
   134  	)
   135  	p.currentCursor = cursor
   136  
   137  	if cursor.image_count > 1 {
   138  		p.currentCursorAnimationStartTime = time.Now()
   139  		p.startAnimatingCursor()
   140  	}
   141  }
   142  
   143  func (p *Pointer) startAnimatingCursor() {
   144  	var fn func()
   145  	fn = func() {
   146  		p.mu.Lock()
   147  		defer p.mu.Unlock()
   148  
   149  		if p.cursorSurfaceFrameCallback != nil {
   150  			p.d.l.wl_callback_destroy(p.cursorSurfaceFrameCallback)
   151  			p.cursorSurfaceFrameCallback = nil
   152  		}
   153  
   154  		if p.currentCursor == nil {
   155  			return
   156  		}
   157  
   158  		imageIdx := p.d.l.wl_cursor_frame_and_duration(
   159  			p.currentCursor,
   160  			C.uint32_t(p.currentCursorAnimationStartTime.UnixMilli()),
   161  			nil,
   162  		)
   163  
   164  		imageSlice := unsafe.Slice(p.currentCursor.images, p.currentCursor.image_count)
   165  		image := imageSlice[imageIdx]
   166  		cursorBuffer := p.d.l.wl_cursor_image_get_buffer(image)
   167  
   168  		p.d.l.wl_surface_attach(p.cursorSurface, cursorBuffer, 0, 0)
   169  		p.d.l.wl_surface_damage_buffer(p.cursorSurface, 0, 0, C.int32_t(image.width), C.int32_t(image.height))
   170  
   171  		p.cursorSurfaceFrameCallback = p.d.l.wl_surface_frame(p.cursorSurface)
   172  		p.d.setCallbackListener(p.cursorSurfaceFrameCallback, fn)
   173  		p.d.l.wl_surface_commit(p.cursorSurface)
   174  
   175  		p.currentCursorAnimationStartTime = time.Now()
   176  	}
   177  
   178  	p.cursorSurfaceFrameCallback = p.d.l.wl_surface_frame(p.cursorSurface)
   179  	p.d.setCallbackListener(p.cursorSurfaceFrameCallback, fn)
   180  	p.d.l.wl_surface_commit(p.cursorSurface)
   181  }
   182  
   183  //export pointerHandleEnter
   184  func pointerHandleEnter(data unsafe.Pointer, wl_pointer *C.struct_wl_pointer, serial C.uint32_t, surface *C.struct_wl_surface, surface_x C.wl_fixed_t, surface_y C.wl_fixed_t) {
   185  	if surface == nil {
   186  		return
   187  	}
   188  
   189  	d, ok := (*cgo.Handle)(data).Value().(*Display)
   190  	if !ok {
   191  		return
   192  	}
   193  
   194  	d.pointer.mu.Lock()
   195  	d.pointer.serial = uint32(serial)
   196  	d.pointer.focus = surface
   197  	d.pointer.mu.Unlock()
   198  
   199  	w, ok := d.windows[surface]
   200  	if !ok {
   201  		return
   202  	}
   203  
   204  	w.mu.Lock()
   205  	currentCursorIcon := w.currentCursorIcon
   206  	scaleFactor := w.scaleFactor
   207  	w.mu.Unlock()
   208  
   209  	// user can call window.SetCursor when window is not in focus
   210  	// so we just store the state so when pointer enters window
   211  	// we set cursor to how the window's state requires it
   212  	if currentCursorIcon == "" {
   213  		d.pointer.setCursor(nil, "", 0)
   214  	} else {
   215  		cursor := d.pointer.loadCursor(currentCursorIcon, 24, scaleFactor)
   216  		if cursor != nil {
   217  			d.pointer.setCursor(cursor, currentCursorIcon, scaleFactor)
   218  		}
   219  	}
   220  
   221  	if cb := w.cursorEnteredCb.Load(); cb != nil {
   222  		if cb := (*cb); cb != nil {
   223  			cb()
   224  		}
   225  	}
   226  }
   227  
   228  //export pointerHandleLeave
   229  func pointerHandleLeave(data unsafe.Pointer, wl_pointer *C.struct_wl_pointer, serial C.uint32_t, surface *C.struct_wl_surface) {
   230  	if surface == nil {
   231  		return
   232  	}
   233  
   234  	d, ok := (*cgo.Handle)(data).Value().(*Display)
   235  	if !ok {
   236  		return
   237  	}
   238  
   239  	d.pointer.mu.Lock()
   240  	d.pointer.serial = uint32(serial)
   241  	d.pointer.focus = nil
   242  	d.pointer.mu.Unlock()
   243  
   244  	d.pointer.setCursor(nil, "", 0)
   245  
   246  	w, ok := d.windows[surface]
   247  	if !ok {
   248  		return
   249  	}
   250  
   251  	if cb := w.cursorLeftCb.Load(); cb != nil {
   252  		if cb := (*cb); cb != nil {
   253  			cb()
   254  		}
   255  	}
   256  }
   257  
   258  //export pointerHandleMotion
   259  func pointerHandleMotion(data unsafe.Pointer, wl_pointer *C.struct_wl_pointer, time C.uint32_t, surface_x C.double, surface_y C.double) {
   260  	d, ok := (*cgo.Handle)(data).Value().(*Display)
   261  	if !ok {
   262  		return
   263  	}
   264  
   265  	d.pointer.mu.Lock()
   266  	focus := d.pointer.focus
   267  	d.pointer.mu.Unlock()
   268  
   269  	if focus == nil {
   270  		return
   271  	}
   272  
   273  	w, ok := d.windows[focus]
   274  	if !ok {
   275  		return
   276  	}
   277  
   278  	if cb := w.cursorMovedCb.Load(); cb != nil {
   279  		if cb := (*cb); cb != nil {
   280  			cb(float64(surface_x), float64(surface_y))
   281  		}
   282  	}
   283  }
   284  
   285  //export pointerHandleButton
   286  func pointerHandleButton(data unsafe.Pointer, wl_pointer *C.struct_wl_pointer, serial C.uint32_t, time C.uint32_t, button C.uint32_t, state enum_wl_pointer_button_state) {
   287  	const (
   288  		BTN_LEFT   = 272
   289  		BTN_RIGHT  = 273
   290  		BTN_MIDDLE = 274
   291  	)
   292  
   293  	d, ok := (*cgo.Handle)(data).Value().(*Display)
   294  	if !ok {
   295  		return
   296  	}
   297  
   298  	d.pointer.mu.Lock()
   299  	d.pointer.serial = uint32(serial)
   300  	focus := d.pointer.focus
   301  	d.pointer.mu.Unlock()
   302  
   303  	if focus == nil {
   304  		return
   305  	}
   306  
   307  	w, ok := d.windows[focus]
   308  	if !ok {
   309  		return
   310  	}
   311  
   312  	if cb := w.mouseInputCb.Load(); cb != nil {
   313  		if cb := (*cb); cb != nil {
   314  			var s events.ButtonState
   315  			switch state {
   316  			case WL_POINTER_BUTTON_STATE_PRESSED:
   317  				s = events.ButtonStatePressed
   318  			case WL_POINTER_BUTTON_STATE_RELEASED:
   319  				s = events.ButtonStateReleased
   320  			}
   321  
   322  			var b events.MouseButton
   323  			switch button {
   324  			case BTN_LEFT:
   325  				b = events.MouseButtonLeft
   326  			case BTN_RIGHT:
   327  				b = events.MouseButtonRight
   328  			case BTN_MIDDLE:
   329  				b = events.MouseButtonMiddle
   330  			default:
   331  				b = events.MouseButton(button)
   332  			}
   333  
   334  			cb(s, b)
   335  		}
   336  	}
   337  }
   338  
   339  //export pointerHandleAxis
   340  func pointerHandleAxis(data unsafe.Pointer, wl_pointer *C.struct_wl_pointer, time C.uint32_t, axis enum_wl_pointer_axis, value C.double) {
   341  	d, ok := (*cgo.Handle)(data).Value().(*Display)
   342  	if !ok {
   343  		return
   344  	}
   345  
   346  	// we call callbacks on frame event
   347  	switch axis {
   348  	case WL_POINTER_AXIS_VERTICAL_SCROLL:
   349  		d.pointer.pixelDeltaVertical -= float64(value)
   350  
   351  	case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
   352  		d.pointer.pixelDeltaHorizontal -= float64(value)
   353  	}
   354  }
   355  
   356  //export pointerHandleAxisDiscrete
   357  func pointerHandleAxisDiscrete(data unsafe.Pointer, wl_pointer *C.struct_wl_pointer, axis enum_wl_pointer_axis, discrete C.int32_t) {
   358  	d, ok := (*cgo.Handle)(data).Value().(*Display)
   359  	if !ok {
   360  		return
   361  	}
   362  
   363  	// we call callbacks on frame event
   364  	switch axis {
   365  	case WL_POINTER_AXIS_VERTICAL_SCROLL:
   366  		d.pointer.lineDeltaVertical -= float64(discrete)
   367  
   368  	case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
   369  		d.pointer.lineDeltaHorizontal -= float64(discrete)
   370  	}
   371  }
   372  
   373  //export pointerHandleFrame
   374  func pointerHandleFrame(data unsafe.Pointer, wl_pointer *C.struct_wl_pointer) {
   375  	d, ok := (*cgo.Handle)(data).Value().(*Display)
   376  	if !ok {
   377  		return
   378  	}
   379  
   380  	d.pointer.mu.Lock()
   381  	focus := d.pointer.focus
   382  	d.pointer.mu.Unlock()
   383  
   384  	if focus == nil {
   385  		return
   386  	}
   387  
   388  	w, ok := d.windows[focus]
   389  	if !ok || w == nil {
   390  		return
   391  	}
   392  
   393  	if d.pointer.lineDeltaVertical != 0 {
   394  		if cb := w.mouseWheelCb.Load(); cb != nil {
   395  			if cb := (*cb); cb != nil {
   396  				cb(
   397  					events.MouseScrollDeltaLine,
   398  					events.MouseScrollAxisVertical,
   399  					d.pointer.lineDeltaVertical,
   400  				)
   401  			}
   402  		}
   403  
   404  		d.pointer.lineDeltaVertical = 0
   405  	} else if d.pointer.pixelDeltaVertical != 0 {
   406  		if cb := w.mouseWheelCb.Load(); cb != nil {
   407  			if cb := (*cb); cb != nil {
   408  				cb(
   409  					events.MouseScrollDeltaPixel,
   410  					events.MouseScrollAxisVertical,
   411  					d.pointer.pixelDeltaVertical,
   412  				)
   413  			}
   414  		}
   415  
   416  		d.pointer.pixelDeltaVertical = 0
   417  	} else if d.pointer.lineDeltaHorizontal != 0 {
   418  		if cb := w.mouseWheelCb.Load(); cb != nil {
   419  			if cb := (*cb); cb != nil {
   420  				cb(
   421  					events.MouseScrollDeltaLine,
   422  					events.MouseScrollAxisHorizontal,
   423  					d.pointer.lineDeltaHorizontal,
   424  				)
   425  			}
   426  		}
   427  
   428  		d.pointer.lineDeltaHorizontal = 0
   429  	} else if d.pointer.pixelDeltaHorizontal != 0 {
   430  		if cb := w.mouseWheelCb.Load(); cb != nil {
   431  			if cb := (*cb); cb != nil {
   432  				cb(
   433  					events.MouseScrollDeltaPixel,
   434  					events.MouseScrollAxisHorizontal,
   435  					d.pointer.pixelDeltaHorizontal,
   436  				)
   437  			}
   438  		}
   439  
   440  		d.pointer.pixelDeltaHorizontal = 0
   441  	}
   442  }