Files
gio/ui/app/os_macos.go
T
Elias Naur 0b6dd4efd9 ui: change events to have "Event" suffixed, not prefixed
Match the Go error naming convention (FooError).

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2019-06-21 17:14:16 +02:00

264 lines
5.0 KiB
Go

// SPDX-License-Identifier: Unlicense OR MIT
// +build darwin,!ios
package app
/*
#cgo CFLAGS: -DGL_SILENCE_DEPRECATION -Werror -fmodules -fobjc-arc -x objective-c
#include <AppKit/AppKit.h>
#include "os_macos.h"
*/
import "C"
import (
"errors"
"image"
"runtime"
"sync"
"time"
"unsafe"
"gioui.org/ui"
"gioui.org/ui/f32"
"gioui.org/ui/key"
"gioui.org/ui/pointer"
)
func init() {
// Darwin requires that UI operations happen on the main thread only.
runtime.LockOSThread()
}
type window struct {
view C.CFTypeRef
w *Window
stage Stage
}
// Only support one main window for now.
var singleWindow struct {
mu sync.Mutex
hasOpts bool
opts *WindowOptions
}
var viewFactory func() C.CFTypeRef
var views = make(map[C.CFTypeRef]*window)
func (w *window) contextView() C.CFTypeRef {
return w.view
}
func (w *window) setTextInput(s key.TextInputState) {}
func (w *window) setAnimating(anim bool) {
var animb C.BOOL
if anim {
animb = 1
}
C.gio_setAnimating(w.view, animb)
}
func (w *window) setStage(stage Stage) {
if stage == w.stage {
return
}
w.stage = stage
w.w.event(StageEvent{stage})
}
//export gio_onFrameCallback
func gio_onFrameCallback(view C.CFTypeRef) {
w := views[view]
w.draw(false)
}
//export gio_onKeys
func gio_onKeys(view C.CFTypeRef, cstr *C.char, ti C.double, mods C.NSUInteger) {
str := C.GoString(cstr)
var kmods key.Modifiers
if mods&C.NSEventModifierFlagCommand != 0 {
kmods |= key.ModCommand
}
if mods&C.NSEventModifierFlagShift != 0 {
kmods |= key.ModShift
}
w := views[view]
for _, k := range str {
if n, ok := convertKey(k); ok {
w.w.event(key.ChordEvent{Name: n, Modifiers: kmods})
}
}
}
//export gio_onText
func gio_onText(view C.CFTypeRef, cstr *C.char) {
str := C.GoString(cstr)
w := views[view]
w.w.event(key.EditEvent{Text: str})
}
//export gio_onMouse
func gio_onMouse(view C.CFTypeRef, cdir C.int, x, y, dx, dy C.CGFloat, ti C.double) {
var typ pointer.Type
switch cdir {
case C.GIO_MOUSE_MOVE:
typ = pointer.Move
case C.GIO_MOUSE_UP:
typ = pointer.Release
case C.GIO_MOUSE_DOWN:
typ = pointer.Press
default:
panic("invalid direction")
}
t := time.Duration(float64(ti)*float64(time.Second) + .5)
w := views[view]
w.w.event(pointer.Event{
Type: typ,
Source: pointer.Mouse,
Time: t,
Position: f32.Point{X: float32(x), Y: float32(y)},
Scroll: f32.Point{X: float32(dx), Y: float32(dy)},
})
}
//export gio_onDraw
func gio_onDraw(view C.CFTypeRef) {
w := views[view]
w.draw(true)
}
//export gio_onFocus
func gio_onFocus(view C.CFTypeRef, focus C.BOOL) {
w := views[view]
w.w.event(key.FocusEvent{Focus: focus == C.YES})
}
func (w *window) draw(sync bool) {
width, height := int(C.gio_viewWidth(w.view)+.5), int(C.gio_viewHeight(w.view)+.5)
if width == 0 || height == 0 {
return
}
cfg := getConfig()
cfg.Now = time.Now()
w.setStage(StageRunning)
w.w.event(DrawEvent{
Size: image.Point{
X: width,
Y: height,
},
Config: cfg,
sync: sync,
})
}
func getConfig() ui.Config {
ppdp := float32(C.gio_getPixelsPerDP())
ppdp *= monitorScale
if ppdp < minDensity {
ppdp = minDensity
}
return ui.Config{
PxPerDp: ppdp,
PxPerSp: ppdp,
}
}
//export gio_onTerminate
func gio_onTerminate(view C.CFTypeRef) {
w := views[view]
delete(views, view)
w.setStage(StageDead)
close(windows)
}
//export gio_onHide
func gio_onHide(view C.CFTypeRef) {
w := views[view]
w.setStage(StagePaused)
}
//export gio_onShow
func gio_onShow(view C.CFTypeRef) {
w := views[view]
w.setStage(StageRunning)
}
//export gio_onCreate
func gio_onCreate(view C.CFTypeRef) {
w := &window{
view: view,
}
ow := newWindow(w)
w.w = ow
views[view] = w
windows <- ow
}
func createWindow(opts *WindowOptions) error {
singleWindow.mu.Lock()
defer singleWindow.mu.Unlock()
if singleWindow.hasOpts {
panic("only one window supported")
}
singleWindow.opts = opts
singleWindow.hasOpts = true
return nil
}
func Main() {
view := viewFactory()
if view == 0 {
// TODO: return this error from CreateWindow.
panic(errors.New("CreateWindow: failed to create view"))
}
cfg := getConfig()
opts := singleWindow.opts
w := cfg.Val(opts.Width)
h := cfg.Val(opts.Height)
title := C.CString(opts.Title)
defer C.free(unsafe.Pointer(title))
C.gio_main(view, title, C.CGFloat(w), C.CGFloat(h))
}
func convertKey(k rune) (rune, bool) {
if '0' <= k && k <= '9' || 'A' <= k && k <= 'Z' {
return k, true
}
if 'a' <= k && k <= 'z' {
return k - 0x20, true
}
var n rune
switch k {
case 0x1b:
n = key.NameEscape
case C.NSLeftArrowFunctionKey:
n = key.NameLeftArrow
case C.NSRightArrowFunctionKey:
n = key.NameRightArrow
case C.NSUpArrowFunctionKey:
n = key.NameUpArrow
case C.NSDownArrowFunctionKey:
n = key.NameDownArrow
case 0xd:
n = key.NameReturn
case C.NSHomeFunctionKey:
n = key.NameHome
case C.NSEndFunctionKey:
n = key.NameEnd
case 0x7f:
n = key.NameDeleteBackward
case C.NSDeleteFunctionKey:
n = key.NameDeleteForward
case C.NSPageUpFunctionKey:
n = key.NamePageUp
case C.NSPageDownFunctionKey:
n = key.NamePageDown
default:
return 0, false
}
return n, true
}