github.com/cybriq/giocore@v0.0.7-0.20210703034601-cfb9cb5f3900/app/internal/wm/os_ios.go (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  // +build darwin,ios
     4  
     5  package wm
     6  
     7  /*
     8  #cgo CFLAGS: -DGLES_SILENCE_DEPRECATION -Werror -Wno-deprecated-declarations -fmodules -fobjc-arc -x objective-c
     9  
    10  #include <CoreGraphics/CoreGraphics.h>
    11  #include <UIKit/UIKit.h>
    12  #include <stdint.h>
    13  
    14  struct drawParams {
    15  	CGFloat dpi, sdpi;
    16  	CGFloat width, height;
    17  	CGFloat top, right, bottom, left;
    18  };
    19  
    20  __attribute__ ((visibility ("hidden"))) void gio_showTextInput(CFTypeRef viewRef);
    21  __attribute__ ((visibility ("hidden"))) void gio_hideTextInput(CFTypeRef viewRef);
    22  __attribute__ ((visibility ("hidden"))) void gio_addLayerToView(CFTypeRef viewRef, CFTypeRef layerRef);
    23  __attribute__ ((visibility ("hidden"))) void gio_updateView(CFTypeRef viewRef, CFTypeRef layerRef);
    24  __attribute__ ((visibility ("hidden"))) void gio_removeLayer(CFTypeRef layerRef);
    25  __attribute__ ((visibility ("hidden"))) struct drawParams gio_viewDrawParams(CFTypeRef viewRef);
    26  __attribute__ ((visibility ("hidden"))) CFTypeRef gio_readClipboard(void);
    27  __attribute__ ((visibility ("hidden"))) void gio_writeClipboard(unichar *chars, NSUInteger length);
    28  */
    29  import "C"
    30  
    31  import (
    32  	"image"
    33  	"runtime"
    34  	"runtime/debug"
    35  	"sync/atomic"
    36  	"time"
    37  	"unicode/utf16"
    38  	"unsafe"
    39  
    40  	"github.com/cybriq/giocore/f32"
    41  	"github.com/cybriq/giocore/io/clipboard"
    42  	"github.com/cybriq/giocore/io/key"
    43  	"github.com/cybriq/giocore/io/pointer"
    44  	"github.com/cybriq/giocore/io/system"
    45  	"github.com/cybriq/giocore/unit"
    46  )
    47  
    48  type ViewEvent struct{}
    49  
    50  type window struct {
    51  	view        C.CFTypeRef
    52  	w           Callbacks
    53  	displayLink *displayLink
    54  
    55  	layer   C.CFTypeRef
    56  	visible atomic.Value
    57  	cursor  pointer.CursorName
    58  
    59  	pointerMap []C.CFTypeRef
    60  }
    61  
    62  var mainWindow = newWindowRendezvous()
    63  
    64  var layerFactory func() uintptr
    65  
    66  var views = make(map[C.CFTypeRef]*window)
    67  
    68  func init() {
    69  	// Darwin requires UI operations happen on the main thread only.
    70  	runtime.LockOSThread()
    71  }
    72  
    73  //export onCreate
    74  func onCreate(view C.CFTypeRef) {
    75  	w := &window{
    76  		view: view,
    77  	}
    78  	dl, err := NewDisplayLink(func() {
    79  		w.draw(false)
    80  	})
    81  	if err != nil {
    82  		panic(err)
    83  	}
    84  	w.displayLink = dl
    85  	wopts := <-mainWindow.out
    86  	w.w = wopts.window
    87  	w.w.SetDriver(w)
    88  	w.visible.Store(false)
    89  	w.layer = C.CFTypeRef(layerFactory())
    90  	C.gio_addLayerToView(view, w.layer)
    91  	views[view] = w
    92  	w.w.Event(system.StageEvent{Stage: system.StagePaused})
    93  }
    94  
    95  //export gio_onDraw
    96  func gio_onDraw(view C.CFTypeRef) {
    97  	w := views[view]
    98  	w.draw(true)
    99  }
   100  
   101  func (w *window) draw(sync bool) {
   102  	params := C.gio_viewDrawParams(w.view)
   103  	if params.width == 0 || params.height == 0 {
   104  		return
   105  	}
   106  	wasVisible := w.isVisible()
   107  	w.visible.Store(true)
   108  	C.gio_updateView(w.view, w.layer)
   109  	if !wasVisible {
   110  		w.w.Event(system.StageEvent{Stage: system.StageRunning})
   111  	}
   112  	const inchPrDp = 1.0 / 163
   113  	w.w.Event(FrameEvent{
   114  		FrameEvent: system.FrameEvent{
   115  			Now: time.Now(),
   116  			Size: image.Point{
   117  				X: int(params.width + .5),
   118  				Y: int(params.height + .5),
   119  			},
   120  			Insets: system.Insets{
   121  				Top:    unit.Px(float32(params.top)),
   122  				Right:  unit.Px(float32(params.right)),
   123  				Bottom: unit.Px(float32(params.bottom)),
   124  				Left:   unit.Px(float32(params.left)),
   125  			},
   126  			Metric: unit.Metric{
   127  				PxPerDp: float32(params.dpi) * inchPrDp,
   128  				PxPerSp: float32(params.sdpi) * inchPrDp,
   129  			},
   130  		},
   131  		Sync: sync,
   132  	})
   133  }
   134  
   135  //export onStop
   136  func onStop(view C.CFTypeRef) {
   137  	w := views[view]
   138  	w.visible.Store(false)
   139  	w.w.Event(system.StageEvent{Stage: system.StagePaused})
   140  }
   141  
   142  //export onDestroy
   143  func onDestroy(view C.CFTypeRef) {
   144  	w := views[view]
   145  	delete(views, view)
   146  	w.w.Event(system.DestroyEvent{})
   147  	w.displayLink.Close()
   148  	C.gio_removeLayer(w.layer)
   149  	C.CFRelease(w.layer)
   150  	w.layer = 0
   151  	w.view = 0
   152  }
   153  
   154  //export onFocus
   155  func onFocus(view C.CFTypeRef, focus int) {
   156  	w := views[view]
   157  	w.w.Event(key.FocusEvent{Focus: focus != 0})
   158  }
   159  
   160  //export onLowMemory
   161  func onLowMemory() {
   162  	runtime.GC()
   163  	debug.FreeOSMemory()
   164  }
   165  
   166  //export onUpArrow
   167  func onUpArrow(view C.CFTypeRef) {
   168  	views[view].onKeyCommand(key.NameUpArrow)
   169  }
   170  
   171  //export onDownArrow
   172  func onDownArrow(view C.CFTypeRef) {
   173  	views[view].onKeyCommand(key.NameDownArrow)
   174  }
   175  
   176  //export onLeftArrow
   177  func onLeftArrow(view C.CFTypeRef) {
   178  	views[view].onKeyCommand(key.NameLeftArrow)
   179  }
   180  
   181  //export onRightArrow
   182  func onRightArrow(view C.CFTypeRef) {
   183  	views[view].onKeyCommand(key.NameRightArrow)
   184  }
   185  
   186  //export onDeleteBackward
   187  func onDeleteBackward(view C.CFTypeRef) {
   188  	views[view].onKeyCommand(key.NameDeleteBackward)
   189  }
   190  
   191  //export onText
   192  func onText(view C.CFTypeRef, str *C.char) {
   193  	w := views[view]
   194  	w.w.Event(key.EditEvent{
   195  		Text: C.GoString(str),
   196  	})
   197  }
   198  
   199  //export onTouch
   200  func onTouch(last C.int, view, touchRef C.CFTypeRef, phase C.NSInteger, x, y C.CGFloat, ti C.double) {
   201  	var typ pointer.Type
   202  	switch phase {
   203  	case C.UITouchPhaseBegan:
   204  		typ = pointer.Press
   205  	case C.UITouchPhaseMoved:
   206  		typ = pointer.Move
   207  	case C.UITouchPhaseEnded:
   208  		typ = pointer.Release
   209  	case C.UITouchPhaseCancelled:
   210  		typ = pointer.Cancel
   211  	default:
   212  		return
   213  	}
   214  	w := views[view]
   215  	t := time.Duration(float64(ti) * float64(time.Second))
   216  	p := f32.Point{X: float32(x), Y: float32(y)}
   217  	w.w.Event(pointer.Event{
   218  		Type:      typ,
   219  		Source:    pointer.Touch,
   220  		PointerID: w.lookupTouch(last != 0, touchRef),
   221  		Position:  p,
   222  		Time:      t,
   223  	})
   224  }
   225  
   226  func (w *window) ReadClipboard() {
   227  	content := nsstringToString(C.gio_readClipboard())
   228  	go w.w.Event(clipboard.Event{Text: content})
   229  }
   230  
   231  func (w *window) WriteClipboard(s string) {
   232  	u16 := utf16.Encode([]rune(s))
   233  	var chars *C.unichar
   234  	if len(u16) > 0 {
   235  		chars = (*C.unichar)(unsafe.Pointer(&u16[0]))
   236  	}
   237  	C.gio_writeClipboard(chars, C.NSUInteger(len(u16)))
   238  }
   239  
   240  func (w *window) Option(opts *Options) {}
   241  
   242  func (w *window) SetAnimating(anim bool) {
   243  	v := w.view
   244  	if v == 0 {
   245  		return
   246  	}
   247  	if anim {
   248  		w.displayLink.Start()
   249  	} else {
   250  		w.displayLink.Stop()
   251  	}
   252  }
   253  
   254  func (w *window) SetCursor(name pointer.CursorName) {
   255  	w.cursor = windowSetCursor(w.cursor, name)
   256  }
   257  
   258  func (w *window) onKeyCommand(name string) {
   259  	w.w.Event(key.Event{
   260  		Name: name,
   261  	})
   262  }
   263  
   264  // lookupTouch maps an UITouch pointer value to an index. If
   265  // last is set, the map is cleared.
   266  func (w *window) lookupTouch(last bool, touch C.CFTypeRef) pointer.ID {
   267  	id := -1
   268  	for i, ref := range w.pointerMap {
   269  		if ref == touch {
   270  			id = i
   271  			break
   272  		}
   273  	}
   274  	if id == -1 {
   275  		id = len(w.pointerMap)
   276  		w.pointerMap = append(w.pointerMap, touch)
   277  	}
   278  	if last {
   279  		w.pointerMap = w.pointerMap[:0]
   280  	}
   281  	return pointer.ID(id)
   282  }
   283  
   284  func (w *window) contextLayer() uintptr {
   285  	return uintptr(w.layer)
   286  }
   287  
   288  func (w *window) isVisible() bool {
   289  	return w.visible.Load().(bool)
   290  }
   291  
   292  func (w *window) ShowTextInput(show bool) {
   293  	if show {
   294  		C.gio_showTextInput(w.view)
   295  	} else {
   296  		C.gio_hideTextInput(w.view)
   297  	}
   298  }
   299  
   300  func (w *window) SetInputHint(_ key.InputHint) {}
   301  
   302  // Close the window. Not implemented for iOS.
   303  func (w *window) Close() {}
   304  
   305  func NewWindow(win Callbacks, opts *Options) error {
   306  	mainWindow.in <- windowAndOptions{win, opts}
   307  	return <-mainWindow.errs
   308  }
   309  
   310  func Main() {
   311  }
   312  
   313  //export gio_runMain
   314  func gio_runMain() {
   315  	runMain()
   316  }
   317  
   318  func (_ ViewEvent) ImplementsEvent() {}