| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495 |
- // Copyright 2014 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- //go:build darwin && !ios
- package app
- // Simple on-screen app debugging for OS X. Not an officially supported
- // development target for apps, as screens with mice are very different
- // than screens with touch panels.
- /*
- #cgo CFLAGS: -x objective-c -DGL_SILENCE_DEPRECATION
- #cgo LDFLAGS: -framework Cocoa -framework OpenGL
- #import <Carbon/Carbon.h> // for HIToolbox/Events.h
- #import <Cocoa/Cocoa.h>
- #include <pthread.h>
- void runApp(void);
- void stopApp(void);
- void makeCurrentContext(GLintptr);
- uint64 threadID();
- */
- import "C"
- import (
- "log"
- "runtime"
- "sync"
- "golang.org/x/mobile/event/key"
- "golang.org/x/mobile/event/lifecycle"
- "golang.org/x/mobile/event/paint"
- "golang.org/x/mobile/event/size"
- "golang.org/x/mobile/event/touch"
- "golang.org/x/mobile/geom"
- )
- var initThreadID uint64
- func init() {
- // Lock the goroutine responsible for initialization to an OS thread.
- // This means the goroutine running main (and calling runApp below)
- // is locked to the OS thread that started the program. This is
- // necessary for the correct delivery of Cocoa events to the process.
- //
- // A discussion on this topic:
- // https://groups.google.com/forum/#!msg/golang-nuts/IiWZ2hUuLDA/SNKYYZBelsYJ
- runtime.LockOSThread()
- initThreadID = uint64(C.threadID())
- }
- func main(f func(App)) {
- if tid := uint64(C.threadID()); tid != initThreadID {
- log.Fatalf("app.Main called on thread %d, but app.init ran on %d", tid, initThreadID)
- }
- go func() {
- f(theApp)
- C.stopApp()
- }()
- C.runApp()
- }
- // loop is the primary drawing loop.
- //
- // After Cocoa has captured the initial OS thread for processing Cocoa
- // events in runApp, it starts loop on another goroutine. It is locked
- // to an OS thread for its OpenGL context.
- //
- // The loop processes GL calls until a publish event appears.
- // Then it runs any remaining GL calls and flushes the screen.
- //
- // As NSOpenGLCPSwapInterval is set to 1, the call to CGLFlushDrawable
- // blocks until the screen refresh.
- func (a *app) loop(ctx C.GLintptr) {
- runtime.LockOSThread()
- C.makeCurrentContext(ctx)
- workAvailable := a.worker.WorkAvailable()
- for {
- select {
- case <-workAvailable:
- a.worker.DoWork()
- case <-theApp.publish:
- loop1:
- for {
- select {
- case <-workAvailable:
- a.worker.DoWork()
- default:
- break loop1
- }
- }
- C.CGLFlushDrawable(C.CGLGetCurrentContext())
- theApp.publishResult <- PublishResult{}
- select {
- case drawDone <- struct{}{}:
- default:
- }
- }
- }
- }
- var drawDone = make(chan struct{})
- // drawgl is used by Cocoa to occasionally request screen updates.
- //
- //export drawgl
- func drawgl() {
- switch theApp.lifecycleStage {
- case lifecycle.StageFocused, lifecycle.StageVisible:
- theApp.Send(paint.Event{
- External: true,
- })
- <-drawDone
- }
- }
- //export startloop
- func startloop(ctx C.GLintptr) {
- go theApp.loop(ctx)
- }
- var windowHeightPx float32
- //export setGeom
- func setGeom(pixelsPerPt float32, widthPx, heightPx int) {
- windowHeightPx = float32(heightPx)
- theApp.eventsIn <- size.Event{
- WidthPx: widthPx,
- HeightPx: heightPx,
- WidthPt: geom.Pt(float32(widthPx) / pixelsPerPt),
- HeightPt: geom.Pt(float32(heightPx) / pixelsPerPt),
- PixelsPerPt: pixelsPerPt,
- }
- }
- var touchEvents struct {
- sync.Mutex
- pending []touch.Event
- }
- func sendTouch(t touch.Type, x, y float32) {
- theApp.eventsIn <- touch.Event{
- X: x,
- Y: windowHeightPx - y,
- Sequence: 0,
- Type: t,
- }
- }
- //export eventMouseDown
- func eventMouseDown(x, y float32) { sendTouch(touch.TypeBegin, x, y) }
- //export eventMouseDragged
- func eventMouseDragged(x, y float32) { sendTouch(touch.TypeMove, x, y) }
- //export eventMouseEnd
- func eventMouseEnd(x, y float32) { sendTouch(touch.TypeEnd, x, y) }
- //export lifecycleDead
- func lifecycleDead() { theApp.sendLifecycle(lifecycle.StageDead) }
- //export eventKey
- func eventKey(runeVal int32, direction uint8, code uint16, flags uint32) {
- var modifiers key.Modifiers
- for _, mod := range mods {
- if flags&mod.flags == mod.flags {
- modifiers |= mod.mod
- }
- }
- theApp.eventsIn <- key.Event{
- Rune: convRune(rune(runeVal)),
- Code: convVirtualKeyCode(code),
- Modifiers: modifiers,
- Direction: key.Direction(direction),
- }
- }
- //export eventFlags
- func eventFlags(flags uint32) {
- for _, mod := range mods {
- if flags&mod.flags == mod.flags && lastFlags&mod.flags != mod.flags {
- eventKey(-1, uint8(key.DirPress), mod.code, flags)
- }
- if lastFlags&mod.flags == mod.flags && flags&mod.flags != mod.flags {
- eventKey(-1, uint8(key.DirRelease), mod.code, flags)
- }
- }
- lastFlags = flags
- }
- var lastFlags uint32
- var mods = [...]struct {
- flags uint32
- code uint16
- mod key.Modifiers
- }{
- // Left and right variants of modifier keys have their own masks,
- // but they are not documented. These were determined empirically.
- {1<<17 | 0x102, C.kVK_Shift, key.ModShift},
- {1<<17 | 0x104, C.kVK_RightShift, key.ModShift},
- {1<<18 | 0x101, C.kVK_Control, key.ModControl},
- // TODO key.ControlRight
- {1<<19 | 0x120, C.kVK_Option, key.ModAlt},
- {1<<19 | 0x140, C.kVK_RightOption, key.ModAlt},
- {1<<20 | 0x108, C.kVK_Command, key.ModMeta},
- {1<<20 | 0x110, C.kVK_Command, key.ModMeta}, // TODO: missing kVK_RightCommand
- }
- //export lifecycleAlive
- func lifecycleAlive() { theApp.sendLifecycle(lifecycle.StageAlive) }
- //export lifecycleVisible
- func lifecycleVisible() {
- theApp.sendLifecycle(lifecycle.StageVisible)
- }
- //export lifecycleFocused
- func lifecycleFocused() { theApp.sendLifecycle(lifecycle.StageFocused) }
- // convRune marks the Carbon/Cocoa private-range unicode rune representing
- // a non-unicode key event to -1, used for Rune in the key package.
- //
- // http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/CORPCHAR.TXT
- func convRune(r rune) rune {
- if '\uE000' <= r && r <= '\uF8FF' {
- return -1
- }
- return r
- }
- // convVirtualKeyCode converts a Carbon/Cocoa virtual key code number
- // into the standard keycodes used by the key package.
- //
- // To get a sense of the key map, see the diagram on
- //
- // http://boredzo.org/blog/archives/2007-05-22/virtual-key-codes
- func convVirtualKeyCode(vkcode uint16) key.Code {
- switch vkcode {
- case C.kVK_ANSI_A:
- return key.CodeA
- case C.kVK_ANSI_B:
- return key.CodeB
- case C.kVK_ANSI_C:
- return key.CodeC
- case C.kVK_ANSI_D:
- return key.CodeD
- case C.kVK_ANSI_E:
- return key.CodeE
- case C.kVK_ANSI_F:
- return key.CodeF
- case C.kVK_ANSI_G:
- return key.CodeG
- case C.kVK_ANSI_H:
- return key.CodeH
- case C.kVK_ANSI_I:
- return key.CodeI
- case C.kVK_ANSI_J:
- return key.CodeJ
- case C.kVK_ANSI_K:
- return key.CodeK
- case C.kVK_ANSI_L:
- return key.CodeL
- case C.kVK_ANSI_M:
- return key.CodeM
- case C.kVK_ANSI_N:
- return key.CodeN
- case C.kVK_ANSI_O:
- return key.CodeO
- case C.kVK_ANSI_P:
- return key.CodeP
- case C.kVK_ANSI_Q:
- return key.CodeQ
- case C.kVK_ANSI_R:
- return key.CodeR
- case C.kVK_ANSI_S:
- return key.CodeS
- case C.kVK_ANSI_T:
- return key.CodeT
- case C.kVK_ANSI_U:
- return key.CodeU
- case C.kVK_ANSI_V:
- return key.CodeV
- case C.kVK_ANSI_W:
- return key.CodeW
- case C.kVK_ANSI_X:
- return key.CodeX
- case C.kVK_ANSI_Y:
- return key.CodeY
- case C.kVK_ANSI_Z:
- return key.CodeZ
- case C.kVK_ANSI_1:
- return key.Code1
- case C.kVK_ANSI_2:
- return key.Code2
- case C.kVK_ANSI_3:
- return key.Code3
- case C.kVK_ANSI_4:
- return key.Code4
- case C.kVK_ANSI_5:
- return key.Code5
- case C.kVK_ANSI_6:
- return key.Code6
- case C.kVK_ANSI_7:
- return key.Code7
- case C.kVK_ANSI_8:
- return key.Code8
- case C.kVK_ANSI_9:
- return key.Code9
- case C.kVK_ANSI_0:
- return key.Code0
- // TODO: move the rest of these codes to constants in key.go
- // if we are happy with them.
- case C.kVK_Return:
- return key.CodeReturnEnter
- case C.kVK_Escape:
- return key.CodeEscape
- case C.kVK_Delete:
- return key.CodeDeleteBackspace
- case C.kVK_Tab:
- return key.CodeTab
- case C.kVK_Space:
- return key.CodeSpacebar
- case C.kVK_ANSI_Minus:
- return key.CodeHyphenMinus
- case C.kVK_ANSI_Equal:
- return key.CodeEqualSign
- case C.kVK_ANSI_LeftBracket:
- return key.CodeLeftSquareBracket
- case C.kVK_ANSI_RightBracket:
- return key.CodeRightSquareBracket
- case C.kVK_ANSI_Backslash:
- return key.CodeBackslash
- // 50: Keyboard Non-US "#" and ~
- case C.kVK_ANSI_Semicolon:
- return key.CodeSemicolon
- case C.kVK_ANSI_Quote:
- return key.CodeApostrophe
- case C.kVK_ANSI_Grave:
- return key.CodeGraveAccent
- case C.kVK_ANSI_Comma:
- return key.CodeComma
- case C.kVK_ANSI_Period:
- return key.CodeFullStop
- case C.kVK_ANSI_Slash:
- return key.CodeSlash
- case C.kVK_CapsLock:
- return key.CodeCapsLock
- case C.kVK_F1:
- return key.CodeF1
- case C.kVK_F2:
- return key.CodeF2
- case C.kVK_F3:
- return key.CodeF3
- case C.kVK_F4:
- return key.CodeF4
- case C.kVK_F5:
- return key.CodeF5
- case C.kVK_F6:
- return key.CodeF6
- case C.kVK_F7:
- return key.CodeF7
- case C.kVK_F8:
- return key.CodeF8
- case C.kVK_F9:
- return key.CodeF9
- case C.kVK_F10:
- return key.CodeF10
- case C.kVK_F11:
- return key.CodeF11
- case C.kVK_F12:
- return key.CodeF12
- // 70: PrintScreen
- // 71: Scroll Lock
- // 72: Pause
- // 73: Insert
- case C.kVK_Home:
- return key.CodeHome
- case C.kVK_PageUp:
- return key.CodePageUp
- case C.kVK_ForwardDelete:
- return key.CodeDeleteForward
- case C.kVK_End:
- return key.CodeEnd
- case C.kVK_PageDown:
- return key.CodePageDown
- case C.kVK_RightArrow:
- return key.CodeRightArrow
- case C.kVK_LeftArrow:
- return key.CodeLeftArrow
- case C.kVK_DownArrow:
- return key.CodeDownArrow
- case C.kVK_UpArrow:
- return key.CodeUpArrow
- case C.kVK_ANSI_KeypadClear:
- return key.CodeKeypadNumLock
- case C.kVK_ANSI_KeypadDivide:
- return key.CodeKeypadSlash
- case C.kVK_ANSI_KeypadMultiply:
- return key.CodeKeypadAsterisk
- case C.kVK_ANSI_KeypadMinus:
- return key.CodeKeypadHyphenMinus
- case C.kVK_ANSI_KeypadPlus:
- return key.CodeKeypadPlusSign
- case C.kVK_ANSI_KeypadEnter:
- return key.CodeKeypadEnter
- case C.kVK_ANSI_Keypad1:
- return key.CodeKeypad1
- case C.kVK_ANSI_Keypad2:
- return key.CodeKeypad2
- case C.kVK_ANSI_Keypad3:
- return key.CodeKeypad3
- case C.kVK_ANSI_Keypad4:
- return key.CodeKeypad4
- case C.kVK_ANSI_Keypad5:
- return key.CodeKeypad5
- case C.kVK_ANSI_Keypad6:
- return key.CodeKeypad6
- case C.kVK_ANSI_Keypad7:
- return key.CodeKeypad7
- case C.kVK_ANSI_Keypad8:
- return key.CodeKeypad8
- case C.kVK_ANSI_Keypad9:
- return key.CodeKeypad9
- case C.kVK_ANSI_Keypad0:
- return key.CodeKeypad0
- case C.kVK_ANSI_KeypadDecimal:
- return key.CodeKeypadFullStop
- case C.kVK_ANSI_KeypadEquals:
- return key.CodeKeypadEqualSign
- case C.kVK_F13:
- return key.CodeF13
- case C.kVK_F14:
- return key.CodeF14
- case C.kVK_F15:
- return key.CodeF15
- case C.kVK_F16:
- return key.CodeF16
- case C.kVK_F17:
- return key.CodeF17
- case C.kVK_F18:
- return key.CodeF18
- case C.kVK_F19:
- return key.CodeF19
- case C.kVK_F20:
- return key.CodeF20
- // 116: Keyboard Execute
- case C.kVK_Help:
- return key.CodeHelp
- // 118: Keyboard Menu
- // 119: Keyboard Select
- // 120: Keyboard Stop
- // 121: Keyboard Again
- // 122: Keyboard Undo
- // 123: Keyboard Cut
- // 124: Keyboard Copy
- // 125: Keyboard Paste
- // 126: Keyboard Find
- case C.kVK_Mute:
- return key.CodeMute
- case C.kVK_VolumeUp:
- return key.CodeVolumeUp
- case C.kVK_VolumeDown:
- return key.CodeVolumeDown
- // 130: Keyboard Locking Caps Lock
- // 131: Keyboard Locking Num Lock
- // 132: Keyboard Locking Scroll Lock
- // 133: Keyboard Comma
- // 134: Keyboard Equal Sign
- // ...: Bunch of stuff
- case C.kVK_Control:
- return key.CodeLeftControl
- case C.kVK_Shift:
- return key.CodeLeftShift
- case C.kVK_Option:
- return key.CodeLeftAlt
- case C.kVK_Command:
- return key.CodeLeftGUI
- case C.kVK_RightControl:
- return key.CodeRightControl
- case C.kVK_RightShift:
- return key.CodeRightShift
- case C.kVK_RightOption:
- return key.CodeRightAlt
- // TODO key.CodeRightGUI
- default:
- return key.CodeUnknown
- }
- }
|