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() {}