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  }