gioui.org/ui@v0.0.0-20190926171558-ce74bc0cbaea/app/os_ios.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 // +build darwin,ios 4 5 package app 6 7 /* 8 #cgo CFLAGS: -fmodules -fobjc-arc -x objective-c 9 10 #include <CoreGraphics/CoreGraphics.h> 11 #include <UIKit/UIKit.h> 12 #include <stdint.h> 13 #include "os_ios.h" 14 15 */ 16 import "C" 17 18 import ( 19 "image" 20 "runtime" 21 "runtime/debug" 22 "sync/atomic" 23 "time" 24 25 "gioui.org/ui" 26 "gioui.org/ui/f32" 27 "gioui.org/ui/key" 28 "gioui.org/ui/pointer" 29 ) 30 31 type window struct { 32 view C.CFTypeRef 33 w *Window 34 35 layer C.CFTypeRef 36 visible atomic.Value 37 38 pointerMap []C.CFTypeRef 39 } 40 41 var mainWindow = newWindowRendezvous() 42 43 var layerFactory func() uintptr 44 45 var views = make(map[C.CFTypeRef]*window) 46 47 func init() { 48 // Darwin requires UI operations happen on the main thread only. 49 runtime.LockOSThread() 50 } 51 52 //export onCreate 53 func onCreate(view C.CFTypeRef) { 54 w := &window{ 55 view: view, 56 } 57 wopts := <-mainWindow.out 58 w.w = wopts.window 59 w.w.setDriver(w) 60 w.visible.Store(false) 61 w.layer = C.CFTypeRef(layerFactory()) 62 C.gio_addLayerToView(view, w.layer) 63 views[view] = w 64 w.w.event(StageEvent{StagePaused}) 65 } 66 67 //export onDraw 68 func onDraw(view C.CFTypeRef, dpi, sdpi, width, height C.CGFloat, sync C.int, top, right, bottom, left C.CGFloat) { 69 if width == 0 || height == 0 { 70 return 71 } 72 w := views[view] 73 wasVisible := w.isVisible() 74 w.visible.Store(true) 75 C.gio_updateView(view, w.layer) 76 if !wasVisible { 77 w.w.event(StageEvent{StageRunning}) 78 } 79 isSync := false 80 if sync != 0 { 81 isSync = true 82 } 83 w.w.event(UpdateEvent{ 84 Size: image.Point{ 85 X: int(width + .5), 86 Y: int(height + .5), 87 }, 88 Insets: Insets{ 89 Top: ui.Px(float32(top)), 90 Right: ui.Px(float32(right)), 91 Bottom: ui.Px(float32(bottom)), 92 Left: ui.Px(float32(left)), 93 }, 94 Config: Config{ 95 pxPerDp: float32(dpi) * inchPrDp, 96 pxPerSp: float32(sdpi) * inchPrDp, 97 now: time.Now(), 98 }, 99 sync: isSync, 100 }) 101 } 102 103 //export onStop 104 func onStop(view C.CFTypeRef) { 105 w := views[view] 106 w.visible.Store(false) 107 w.w.event(StageEvent{StagePaused}) 108 } 109 110 //export onDestroy 111 func onDestroy(view C.CFTypeRef) { 112 w := views[view] 113 delete(views, view) 114 w.w.event(DestroyEvent{}) 115 C.gio_removeLayer(w.layer) 116 C.CFRelease(w.layer) 117 w.layer = 0 118 w.view = 0 119 } 120 121 //export onFocus 122 func onFocus(view C.CFTypeRef, focus int) { 123 w := views[view] 124 w.w.event(key.FocusEvent{Focus: focus != 0}) 125 } 126 127 //export onLowMemory 128 func onLowMemory() { 129 runtime.GC() 130 debug.FreeOSMemory() 131 } 132 133 //export onUpArrow 134 func onUpArrow(view C.CFTypeRef) { 135 views[view].onKeyCommand(key.NameUpArrow) 136 } 137 138 //export onDownArrow 139 func onDownArrow(view C.CFTypeRef) { 140 views[view].onKeyCommand(key.NameDownArrow) 141 } 142 143 //export onLeftArrow 144 func onLeftArrow(view C.CFTypeRef) { 145 views[view].onKeyCommand(key.NameLeftArrow) 146 } 147 148 //export onRightArrow 149 func onRightArrow(view C.CFTypeRef) { 150 views[view].onKeyCommand(key.NameRightArrow) 151 } 152 153 //export onDeleteBackward 154 func onDeleteBackward(view C.CFTypeRef) { 155 views[view].onKeyCommand(key.NameDeleteBackward) 156 } 157 158 //export onText 159 func onText(view C.CFTypeRef, str *C.char) { 160 w := views[view] 161 w.w.event(key.EditEvent{ 162 Text: C.GoString(str), 163 }) 164 } 165 166 //export onTouch 167 func onTouch(last C.int, view, touchRef C.CFTypeRef, phase C.NSInteger, x, y C.CGFloat, ti C.double) { 168 var typ pointer.Type 169 switch phase { 170 case C.UITouchPhaseBegan: 171 typ = pointer.Press 172 case C.UITouchPhaseMoved: 173 typ = pointer.Move 174 case C.UITouchPhaseEnded: 175 typ = pointer.Release 176 case C.UITouchPhaseCancelled: 177 typ = pointer.Cancel 178 default: 179 return 180 } 181 w := views[view] 182 t := time.Duration(float64(ti) * float64(time.Second)) 183 p := f32.Point{X: float32(x), Y: float32(y)} 184 w.w.event(pointer.Event{ 185 Type: typ, 186 Source: pointer.Touch, 187 PointerID: w.lookupTouch(last != 0, touchRef), 188 Position: p, 189 Time: t, 190 }) 191 } 192 193 func (w *window) setAnimating(anim bool) { 194 if w.view == 0 { 195 return 196 } 197 var animi C.int 198 if anim { 199 animi = 1 200 } 201 C.gio_setAnimating(w.view, animi) 202 } 203 204 func (w *window) onKeyCommand(name rune) { 205 w.w.event(key.Event{ 206 Name: name, 207 }) 208 } 209 210 // lookupTouch maps an UITouch pointer value to an index. If 211 // last is set, the map is cleared. 212 func (w *window) lookupTouch(last bool, touch C.CFTypeRef) pointer.ID { 213 id := -1 214 for i, ref := range w.pointerMap { 215 if ref == touch { 216 id = i 217 break 218 } 219 } 220 if id == -1 { 221 id = len(w.pointerMap) 222 w.pointerMap = append(w.pointerMap, touch) 223 } 224 if last { 225 w.pointerMap = w.pointerMap[:0] 226 } 227 return pointer.ID(id) 228 } 229 230 func (w *window) contextLayer() uintptr { 231 return uintptr(w.layer) 232 } 233 234 func (w *window) isVisible() bool { 235 return w.visible.Load().(bool) 236 } 237 238 func (w *window) showTextInput(show bool) { 239 if w.view == 0 { 240 return 241 } 242 if show { 243 C.gio_showTextInput(w.view) 244 } else { 245 C.gio_hideTextInput(w.view) 246 } 247 } 248 249 func createWindow(win *Window, opts *windowOptions) error { 250 mainWindow.in <- windowAndOptions{win, opts} 251 return <-mainWindow.errs 252 } 253 254 func main() { 255 }