mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
7a259e68f7
Packages that provide support for external events such as pointer, key and system are only the beginning. Future packages are expected for clipboard access, drag and drop, gps positions and so on. To keep the number of top-level packages under control, move such I/O packages to the new `io` directory. The `system` package name was the previous solution to keeping the number of top-level packages under control: I named it `system` instead of the narrower `profile` because I expected to put all the less common events into it, turning `system` into a "package util" smell. With `io`, package system can be renamed to `profile`. Signed-off-by: Elias Naur <mail@eliasnaur.com>
256 lines
4.7 KiB
Go
256 lines
4.7 KiB
Go
// SPDX-License-Identifier: Unlicense OR MIT
|
|
|
|
// +build darwin,ios
|
|
|
|
package app
|
|
|
|
/*
|
|
#cgo CFLAGS: -fmodules -fobjc-arc -x objective-c
|
|
|
|
#include <CoreGraphics/CoreGraphics.h>
|
|
#include <UIKit/UIKit.h>
|
|
#include <stdint.h>
|
|
#include "os_ios.h"
|
|
|
|
*/
|
|
import "C"
|
|
|
|
import (
|
|
"image"
|
|
"runtime"
|
|
"runtime/debug"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"gioui.org/f32"
|
|
"gioui.org/io/key"
|
|
"gioui.org/io/pointer"
|
|
"gioui.org/ui"
|
|
)
|
|
|
|
type window struct {
|
|
view C.CFTypeRef
|
|
w *Window
|
|
|
|
layer C.CFTypeRef
|
|
visible atomic.Value
|
|
|
|
pointerMap []C.CFTypeRef
|
|
}
|
|
|
|
var mainWindow = newWindowRendezvous()
|
|
|
|
var layerFactory func() uintptr
|
|
|
|
var views = make(map[C.CFTypeRef]*window)
|
|
|
|
func init() {
|
|
// Darwin requires UI operations happen on the main thread only.
|
|
runtime.LockOSThread()
|
|
}
|
|
|
|
//export onCreate
|
|
func onCreate(view C.CFTypeRef) {
|
|
w := &window{
|
|
view: view,
|
|
}
|
|
wopts := <-mainWindow.out
|
|
w.w = wopts.window
|
|
w.w.setDriver(w)
|
|
w.visible.Store(false)
|
|
w.layer = C.CFTypeRef(layerFactory())
|
|
C.gio_addLayerToView(view, w.layer)
|
|
views[view] = w
|
|
w.w.event(StageEvent{StagePaused})
|
|
}
|
|
|
|
//export onDraw
|
|
func onDraw(view C.CFTypeRef, dpi, sdpi, width, height C.CGFloat, sync C.int, top, right, bottom, left C.CGFloat) {
|
|
if width == 0 || height == 0 {
|
|
return
|
|
}
|
|
w := views[view]
|
|
wasVisible := w.isVisible()
|
|
w.visible.Store(true)
|
|
C.gio_updateView(view, w.layer)
|
|
if !wasVisible {
|
|
w.w.event(StageEvent{StageRunning})
|
|
}
|
|
isSync := false
|
|
if sync != 0 {
|
|
isSync = true
|
|
}
|
|
w.w.event(UpdateEvent{
|
|
Size: image.Point{
|
|
X: int(width + .5),
|
|
Y: int(height + .5),
|
|
},
|
|
Insets: Insets{
|
|
Top: ui.Px(float32(top)),
|
|
Right: ui.Px(float32(right)),
|
|
Bottom: ui.Px(float32(bottom)),
|
|
Left: ui.Px(float32(left)),
|
|
},
|
|
Config: Config{
|
|
pxPerDp: float32(dpi) * inchPrDp,
|
|
pxPerSp: float32(sdpi) * inchPrDp,
|
|
now: time.Now(),
|
|
},
|
|
sync: isSync,
|
|
})
|
|
}
|
|
|
|
//export onStop
|
|
func onStop(view C.CFTypeRef) {
|
|
w := views[view]
|
|
w.visible.Store(false)
|
|
w.w.event(StageEvent{StagePaused})
|
|
}
|
|
|
|
//export onDestroy
|
|
func onDestroy(view C.CFTypeRef) {
|
|
w := views[view]
|
|
delete(views, view)
|
|
w.w.event(DestroyEvent{})
|
|
C.gio_removeLayer(w.layer)
|
|
C.CFRelease(w.layer)
|
|
w.layer = 0
|
|
w.view = 0
|
|
}
|
|
|
|
//export onFocus
|
|
func onFocus(view C.CFTypeRef, focus int) {
|
|
w := views[view]
|
|
w.w.event(key.FocusEvent{Focus: focus != 0})
|
|
}
|
|
|
|
//export onLowMemory
|
|
func onLowMemory() {
|
|
runtime.GC()
|
|
debug.FreeOSMemory()
|
|
}
|
|
|
|
//export onUpArrow
|
|
func onUpArrow(view C.CFTypeRef) {
|
|
views[view].onKeyCommand(key.NameUpArrow)
|
|
}
|
|
|
|
//export onDownArrow
|
|
func onDownArrow(view C.CFTypeRef) {
|
|
views[view].onKeyCommand(key.NameDownArrow)
|
|
}
|
|
|
|
//export onLeftArrow
|
|
func onLeftArrow(view C.CFTypeRef) {
|
|
views[view].onKeyCommand(key.NameLeftArrow)
|
|
}
|
|
|
|
//export onRightArrow
|
|
func onRightArrow(view C.CFTypeRef) {
|
|
views[view].onKeyCommand(key.NameRightArrow)
|
|
}
|
|
|
|
//export onDeleteBackward
|
|
func onDeleteBackward(view C.CFTypeRef) {
|
|
views[view].onKeyCommand(key.NameDeleteBackward)
|
|
}
|
|
|
|
//export onText
|
|
func onText(view C.CFTypeRef, str *C.char) {
|
|
w := views[view]
|
|
w.w.event(key.EditEvent{
|
|
Text: C.GoString(str),
|
|
})
|
|
}
|
|
|
|
//export onTouch
|
|
func onTouch(last C.int, view, touchRef C.CFTypeRef, phase C.NSInteger, x, y C.CGFloat, ti C.double) {
|
|
var typ pointer.Type
|
|
switch phase {
|
|
case C.UITouchPhaseBegan:
|
|
typ = pointer.Press
|
|
case C.UITouchPhaseMoved:
|
|
typ = pointer.Move
|
|
case C.UITouchPhaseEnded:
|
|
typ = pointer.Release
|
|
case C.UITouchPhaseCancelled:
|
|
typ = pointer.Cancel
|
|
default:
|
|
return
|
|
}
|
|
w := views[view]
|
|
t := time.Duration(float64(ti) * float64(time.Second))
|
|
p := f32.Point{X: float32(x), Y: float32(y)}
|
|
w.w.event(pointer.Event{
|
|
Type: typ,
|
|
Source: pointer.Touch,
|
|
PointerID: w.lookupTouch(last != 0, touchRef),
|
|
Position: p,
|
|
Time: t,
|
|
})
|
|
}
|
|
|
|
func (w *window) setAnimating(anim bool) {
|
|
if w.view == 0 {
|
|
return
|
|
}
|
|
var animi C.int
|
|
if anim {
|
|
animi = 1
|
|
}
|
|
C.gio_setAnimating(w.view, animi)
|
|
}
|
|
|
|
func (w *window) onKeyCommand(name rune) {
|
|
w.w.event(key.Event{
|
|
Name: name,
|
|
})
|
|
}
|
|
|
|
// lookupTouch maps an UITouch pointer value to an index. If
|
|
// last is set, the map is cleared.
|
|
func (w *window) lookupTouch(last bool, touch C.CFTypeRef) pointer.ID {
|
|
id := -1
|
|
for i, ref := range w.pointerMap {
|
|
if ref == touch {
|
|
id = i
|
|
break
|
|
}
|
|
}
|
|
if id == -1 {
|
|
id = len(w.pointerMap)
|
|
w.pointerMap = append(w.pointerMap, touch)
|
|
}
|
|
if last {
|
|
w.pointerMap = w.pointerMap[:0]
|
|
}
|
|
return pointer.ID(id)
|
|
}
|
|
|
|
func (w *window) contextLayer() uintptr {
|
|
return uintptr(w.layer)
|
|
}
|
|
|
|
func (w *window) isVisible() bool {
|
|
return w.visible.Load().(bool)
|
|
}
|
|
|
|
func (w *window) showTextInput(show bool) {
|
|
if w.view == 0 {
|
|
return
|
|
}
|
|
if show {
|
|
C.gio_showTextInput(w.view)
|
|
} else {
|
|
C.gio_hideTextInput(w.view)
|
|
}
|
|
}
|
|
|
|
func createWindow(win *Window, opts *windowOptions) error {
|
|
mainWindow.in <- windowAndOptions{win, opts}
|
|
return <-mainWindow.errs
|
|
}
|
|
|
|
func main() {
|
|
}
|