zorldo

Goofing around with Ebiten
git clone git://bsandro.tech/zorldo
Log | Files | Refs | README

darwin_desktop.go (12005B)


      1 // Copyright 2014 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 //go:build darwin && !ios
      6 // +build darwin,!ios
      7 
      8 package app
      9 
     10 // Simple on-screen app debugging for OS X. Not an officially supported
     11 // development target for apps, as screens with mice are very different
     12 // than screens with touch panels.
     13 
     14 /*
     15 #cgo CFLAGS: -x objective-c -DGL_SILENCE_DEPRECATION
     16 #cgo LDFLAGS: -framework Cocoa -framework OpenGL
     17 #import <Carbon/Carbon.h> // for HIToolbox/Events.h
     18 #import <Cocoa/Cocoa.h>
     19 #include <pthread.h>
     20 
     21 void runApp(void);
     22 void stopApp(void);
     23 void makeCurrentContext(GLintptr);
     24 uint64 threadID();
     25 */
     26 import "C"
     27 import (
     28 	"log"
     29 	"runtime"
     30 	"sync"
     31 
     32 	"golang.org/x/mobile/event/key"
     33 	"golang.org/x/mobile/event/lifecycle"
     34 	"golang.org/x/mobile/event/paint"
     35 	"golang.org/x/mobile/event/size"
     36 	"golang.org/x/mobile/event/touch"
     37 	"golang.org/x/mobile/geom"
     38 )
     39 
     40 var initThreadID uint64
     41 
     42 func init() {
     43 	// Lock the goroutine responsible for initialization to an OS thread.
     44 	// This means the goroutine running main (and calling runApp below)
     45 	// is locked to the OS thread that started the program. This is
     46 	// necessary for the correct delivery of Cocoa events to the process.
     47 	//
     48 	// A discussion on this topic:
     49 	// https://groups.google.com/forum/#!msg/golang-nuts/IiWZ2hUuLDA/SNKYYZBelsYJ
     50 	runtime.LockOSThread()
     51 	initThreadID = uint64(C.threadID())
     52 }
     53 
     54 func main(f func(App)) {
     55 	if tid := uint64(C.threadID()); tid != initThreadID {
     56 		log.Fatalf("app.Main called on thread %d, but app.init ran on %d", tid, initThreadID)
     57 	}
     58 
     59 	go func() {
     60 		f(theApp)
     61 		C.stopApp()
     62 		// TODO(crawshaw): trigger runApp to return
     63 	}()
     64 
     65 	C.runApp()
     66 }
     67 
     68 // loop is the primary drawing loop.
     69 //
     70 // After Cocoa has captured the initial OS thread for processing Cocoa
     71 // events in runApp, it starts loop on another goroutine. It is locked
     72 // to an OS thread for its OpenGL context.
     73 //
     74 // The loop processes GL calls until a publish event appears.
     75 // Then it runs any remaining GL calls and flushes the screen.
     76 //
     77 // As NSOpenGLCPSwapInterval is set to 1, the call to CGLFlushDrawable
     78 // blocks until the screen refresh.
     79 func (a *app) loop(ctx C.GLintptr) {
     80 	runtime.LockOSThread()
     81 	C.makeCurrentContext(ctx)
     82 
     83 	workAvailable := a.worker.WorkAvailable()
     84 
     85 	for {
     86 		select {
     87 		case <-workAvailable:
     88 			a.worker.DoWork()
     89 		case <-theApp.publish:
     90 		loop1:
     91 			for {
     92 				select {
     93 				case <-workAvailable:
     94 					a.worker.DoWork()
     95 				default:
     96 					break loop1
     97 				}
     98 			}
     99 			C.CGLFlushDrawable(C.CGLGetCurrentContext())
    100 			theApp.publishResult <- PublishResult{}
    101 			select {
    102 			case drawDone <- struct{}{}:
    103 			default:
    104 			}
    105 		}
    106 	}
    107 }
    108 
    109 var drawDone = make(chan struct{})
    110 
    111 // drawgl is used by Cocoa to occasionally request screen updates.
    112 //
    113 //export drawgl
    114 func drawgl() {
    115 	switch theApp.lifecycleStage {
    116 	case lifecycle.StageFocused, lifecycle.StageVisible:
    117 		theApp.Send(paint.Event{
    118 			External: true,
    119 		})
    120 		<-drawDone
    121 	}
    122 }
    123 
    124 //export startloop
    125 func startloop(ctx C.GLintptr) {
    126 	go theApp.loop(ctx)
    127 }
    128 
    129 var windowHeightPx float32
    130 
    131 //export setGeom
    132 func setGeom(pixelsPerPt float32, widthPx, heightPx int) {
    133 	windowHeightPx = float32(heightPx)
    134 	theApp.eventsIn <- size.Event{
    135 		WidthPx:     widthPx,
    136 		HeightPx:    heightPx,
    137 		WidthPt:     geom.Pt(float32(widthPx) / pixelsPerPt),
    138 		HeightPt:    geom.Pt(float32(heightPx) / pixelsPerPt),
    139 		PixelsPerPt: pixelsPerPt,
    140 	}
    141 }
    142 
    143 var touchEvents struct {
    144 	sync.Mutex
    145 	pending []touch.Event
    146 }
    147 
    148 func sendTouch(t touch.Type, x, y float32) {
    149 	theApp.eventsIn <- touch.Event{
    150 		X:        x,
    151 		Y:        windowHeightPx - y,
    152 		Sequence: 0,
    153 		Type:     t,
    154 	}
    155 }
    156 
    157 //export eventMouseDown
    158 func eventMouseDown(x, y float32) { sendTouch(touch.TypeBegin, x, y) }
    159 
    160 //export eventMouseDragged
    161 func eventMouseDragged(x, y float32) { sendTouch(touch.TypeMove, x, y) }
    162 
    163 //export eventMouseEnd
    164 func eventMouseEnd(x, y float32) { sendTouch(touch.TypeEnd, x, y) }
    165 
    166 //export lifecycleDead
    167 func lifecycleDead() { theApp.sendLifecycle(lifecycle.StageDead) }
    168 
    169 //export eventKey
    170 func eventKey(runeVal int32, direction uint8, code uint16, flags uint32) {
    171 	var modifiers key.Modifiers
    172 	for _, mod := range mods {
    173 		if flags&mod.flags == mod.flags {
    174 			modifiers |= mod.mod
    175 		}
    176 	}
    177 
    178 	theApp.eventsIn <- key.Event{
    179 		Rune:      convRune(rune(runeVal)),
    180 		Code:      convVirtualKeyCode(code),
    181 		Modifiers: modifiers,
    182 		Direction: key.Direction(direction),
    183 	}
    184 }
    185 
    186 //export eventFlags
    187 func eventFlags(flags uint32) {
    188 	for _, mod := range mods {
    189 		if flags&mod.flags == mod.flags && lastFlags&mod.flags != mod.flags {
    190 			eventKey(-1, uint8(key.DirPress), mod.code, flags)
    191 		}
    192 		if lastFlags&mod.flags == mod.flags && flags&mod.flags != mod.flags {
    193 			eventKey(-1, uint8(key.DirRelease), mod.code, flags)
    194 		}
    195 	}
    196 	lastFlags = flags
    197 }
    198 
    199 var lastFlags uint32
    200 
    201 var mods = [...]struct {
    202 	flags uint32
    203 	code  uint16
    204 	mod   key.Modifiers
    205 }{
    206 	// Left and right variants of modifier keys have their own masks,
    207 	// but they are not documented. These were determined empirically.
    208 	{1<<17 | 0x102, C.kVK_Shift, key.ModShift},
    209 	{1<<17 | 0x104, C.kVK_RightShift, key.ModShift},
    210 	{1<<18 | 0x101, C.kVK_Control, key.ModControl},
    211 	// TODO key.ControlRight
    212 	{1<<19 | 0x120, C.kVK_Option, key.ModAlt},
    213 	{1<<19 | 0x140, C.kVK_RightOption, key.ModAlt},
    214 	{1<<20 | 0x108, C.kVK_Command, key.ModMeta},
    215 	{1<<20 | 0x110, C.kVK_Command, key.ModMeta}, // TODO: missing kVK_RightCommand
    216 }
    217 
    218 //export lifecycleAlive
    219 func lifecycleAlive() { theApp.sendLifecycle(lifecycle.StageAlive) }
    220 
    221 //export lifecycleVisible
    222 func lifecycleVisible() {
    223 	theApp.sendLifecycle(lifecycle.StageVisible)
    224 }
    225 
    226 //export lifecycleFocused
    227 func lifecycleFocused() { theApp.sendLifecycle(lifecycle.StageFocused) }
    228 
    229 // convRune marks the Carbon/Cocoa private-range unicode rune representing
    230 // a non-unicode key event to -1, used for Rune in the key package.
    231 //
    232 // http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/CORPCHAR.TXT
    233 func convRune(r rune) rune {
    234 	if '\uE000' <= r && r <= '\uF8FF' {
    235 		return -1
    236 	}
    237 	return r
    238 }
    239 
    240 // convVirtualKeyCode converts a Carbon/Cocoa virtual key code number
    241 // into the standard keycodes used by the key package.
    242 //
    243 // To get a sense of the key map, see the diagram on
    244 //	http://boredzo.org/blog/archives/2007-05-22/virtual-key-codes
    245 func convVirtualKeyCode(vkcode uint16) key.Code {
    246 	switch vkcode {
    247 	case C.kVK_ANSI_A:
    248 		return key.CodeA
    249 	case C.kVK_ANSI_B:
    250 		return key.CodeB
    251 	case C.kVK_ANSI_C:
    252 		return key.CodeC
    253 	case C.kVK_ANSI_D:
    254 		return key.CodeD
    255 	case C.kVK_ANSI_E:
    256 		return key.CodeE
    257 	case C.kVK_ANSI_F:
    258 		return key.CodeF
    259 	case C.kVK_ANSI_G:
    260 		return key.CodeG
    261 	case C.kVK_ANSI_H:
    262 		return key.CodeH
    263 	case C.kVK_ANSI_I:
    264 		return key.CodeI
    265 	case C.kVK_ANSI_J:
    266 		return key.CodeJ
    267 	case C.kVK_ANSI_K:
    268 		return key.CodeK
    269 	case C.kVK_ANSI_L:
    270 		return key.CodeL
    271 	case C.kVK_ANSI_M:
    272 		return key.CodeM
    273 	case C.kVK_ANSI_N:
    274 		return key.CodeN
    275 	case C.kVK_ANSI_O:
    276 		return key.CodeO
    277 	case C.kVK_ANSI_P:
    278 		return key.CodeP
    279 	case C.kVK_ANSI_Q:
    280 		return key.CodeQ
    281 	case C.kVK_ANSI_R:
    282 		return key.CodeR
    283 	case C.kVK_ANSI_S:
    284 		return key.CodeS
    285 	case C.kVK_ANSI_T:
    286 		return key.CodeT
    287 	case C.kVK_ANSI_U:
    288 		return key.CodeU
    289 	case C.kVK_ANSI_V:
    290 		return key.CodeV
    291 	case C.kVK_ANSI_W:
    292 		return key.CodeW
    293 	case C.kVK_ANSI_X:
    294 		return key.CodeX
    295 	case C.kVK_ANSI_Y:
    296 		return key.CodeY
    297 	case C.kVK_ANSI_Z:
    298 		return key.CodeZ
    299 	case C.kVK_ANSI_1:
    300 		return key.Code1
    301 	case C.kVK_ANSI_2:
    302 		return key.Code2
    303 	case C.kVK_ANSI_3:
    304 		return key.Code3
    305 	case C.kVK_ANSI_4:
    306 		return key.Code4
    307 	case C.kVK_ANSI_5:
    308 		return key.Code5
    309 	case C.kVK_ANSI_6:
    310 		return key.Code6
    311 	case C.kVK_ANSI_7:
    312 		return key.Code7
    313 	case C.kVK_ANSI_8:
    314 		return key.Code8
    315 	case C.kVK_ANSI_9:
    316 		return key.Code9
    317 	case C.kVK_ANSI_0:
    318 		return key.Code0
    319 	// TODO: move the rest of these codes to constants in key.go
    320 	// if we are happy with them.
    321 	case C.kVK_Return:
    322 		return key.CodeReturnEnter
    323 	case C.kVK_Escape:
    324 		return key.CodeEscape
    325 	case C.kVK_Delete:
    326 		return key.CodeDeleteBackspace
    327 	case C.kVK_Tab:
    328 		return key.CodeTab
    329 	case C.kVK_Space:
    330 		return key.CodeSpacebar
    331 	case C.kVK_ANSI_Minus:
    332 		return key.CodeHyphenMinus
    333 	case C.kVK_ANSI_Equal:
    334 		return key.CodeEqualSign
    335 	case C.kVK_ANSI_LeftBracket:
    336 		return key.CodeLeftSquareBracket
    337 	case C.kVK_ANSI_RightBracket:
    338 		return key.CodeRightSquareBracket
    339 	case C.kVK_ANSI_Backslash:
    340 		return key.CodeBackslash
    341 	// 50: Keyboard Non-US "#" and ~
    342 	case C.kVK_ANSI_Semicolon:
    343 		return key.CodeSemicolon
    344 	case C.kVK_ANSI_Quote:
    345 		return key.CodeApostrophe
    346 	case C.kVK_ANSI_Grave:
    347 		return key.CodeGraveAccent
    348 	case C.kVK_ANSI_Comma:
    349 		return key.CodeComma
    350 	case C.kVK_ANSI_Period:
    351 		return key.CodeFullStop
    352 	case C.kVK_ANSI_Slash:
    353 		return key.CodeSlash
    354 	case C.kVK_CapsLock:
    355 		return key.CodeCapsLock
    356 	case C.kVK_F1:
    357 		return key.CodeF1
    358 	case C.kVK_F2:
    359 		return key.CodeF2
    360 	case C.kVK_F3:
    361 		return key.CodeF3
    362 	case C.kVK_F4:
    363 		return key.CodeF4
    364 	case C.kVK_F5:
    365 		return key.CodeF5
    366 	case C.kVK_F6:
    367 		return key.CodeF6
    368 	case C.kVK_F7:
    369 		return key.CodeF7
    370 	case C.kVK_F8:
    371 		return key.CodeF8
    372 	case C.kVK_F9:
    373 		return key.CodeF9
    374 	case C.kVK_F10:
    375 		return key.CodeF10
    376 	case C.kVK_F11:
    377 		return key.CodeF11
    378 	case C.kVK_F12:
    379 		return key.CodeF12
    380 	// 70: PrintScreen
    381 	// 71: Scroll Lock
    382 	// 72: Pause
    383 	// 73: Insert
    384 	case C.kVK_Home:
    385 		return key.CodeHome
    386 	case C.kVK_PageUp:
    387 		return key.CodePageUp
    388 	case C.kVK_ForwardDelete:
    389 		return key.CodeDeleteForward
    390 	case C.kVK_End:
    391 		return key.CodeEnd
    392 	case C.kVK_PageDown:
    393 		return key.CodePageDown
    394 	case C.kVK_RightArrow:
    395 		return key.CodeRightArrow
    396 	case C.kVK_LeftArrow:
    397 		return key.CodeLeftArrow
    398 	case C.kVK_DownArrow:
    399 		return key.CodeDownArrow
    400 	case C.kVK_UpArrow:
    401 		return key.CodeUpArrow
    402 	case C.kVK_ANSI_KeypadClear:
    403 		return key.CodeKeypadNumLock
    404 	case C.kVK_ANSI_KeypadDivide:
    405 		return key.CodeKeypadSlash
    406 	case C.kVK_ANSI_KeypadMultiply:
    407 		return key.CodeKeypadAsterisk
    408 	case C.kVK_ANSI_KeypadMinus:
    409 		return key.CodeKeypadHyphenMinus
    410 	case C.kVK_ANSI_KeypadPlus:
    411 		return key.CodeKeypadPlusSign
    412 	case C.kVK_ANSI_KeypadEnter:
    413 		return key.CodeKeypadEnter
    414 	case C.kVK_ANSI_Keypad1:
    415 		return key.CodeKeypad1
    416 	case C.kVK_ANSI_Keypad2:
    417 		return key.CodeKeypad2
    418 	case C.kVK_ANSI_Keypad3:
    419 		return key.CodeKeypad3
    420 	case C.kVK_ANSI_Keypad4:
    421 		return key.CodeKeypad4
    422 	case C.kVK_ANSI_Keypad5:
    423 		return key.CodeKeypad5
    424 	case C.kVK_ANSI_Keypad6:
    425 		return key.CodeKeypad6
    426 	case C.kVK_ANSI_Keypad7:
    427 		return key.CodeKeypad7
    428 	case C.kVK_ANSI_Keypad8:
    429 		return key.CodeKeypad8
    430 	case C.kVK_ANSI_Keypad9:
    431 		return key.CodeKeypad9
    432 	case C.kVK_ANSI_Keypad0:
    433 		return key.CodeKeypad0
    434 	case C.kVK_ANSI_KeypadDecimal:
    435 		return key.CodeKeypadFullStop
    436 	case C.kVK_ANSI_KeypadEquals:
    437 		return key.CodeKeypadEqualSign
    438 	case C.kVK_F13:
    439 		return key.CodeF13
    440 	case C.kVK_F14:
    441 		return key.CodeF14
    442 	case C.kVK_F15:
    443 		return key.CodeF15
    444 	case C.kVK_F16:
    445 		return key.CodeF16
    446 	case C.kVK_F17:
    447 		return key.CodeF17
    448 	case C.kVK_F18:
    449 		return key.CodeF18
    450 	case C.kVK_F19:
    451 		return key.CodeF19
    452 	case C.kVK_F20:
    453 		return key.CodeF20
    454 	// 116: Keyboard Execute
    455 	case C.kVK_Help:
    456 		return key.CodeHelp
    457 	// 118: Keyboard Menu
    458 	// 119: Keyboard Select
    459 	// 120: Keyboard Stop
    460 	// 121: Keyboard Again
    461 	// 122: Keyboard Undo
    462 	// 123: Keyboard Cut
    463 	// 124: Keyboard Copy
    464 	// 125: Keyboard Paste
    465 	// 126: Keyboard Find
    466 	case C.kVK_Mute:
    467 		return key.CodeMute
    468 	case C.kVK_VolumeUp:
    469 		return key.CodeVolumeUp
    470 	case C.kVK_VolumeDown:
    471 		return key.CodeVolumeDown
    472 	// 130: Keyboard Locking Caps Lock
    473 	// 131: Keyboard Locking Num Lock
    474 	// 132: Keyboard Locking Scroll Lock
    475 	// 133: Keyboard Comma
    476 	// 134: Keyboard Equal Sign
    477 	// ...: Bunch of stuff
    478 	case C.kVK_Control:
    479 		return key.CodeLeftControl
    480 	case C.kVK_Shift:
    481 		return key.CodeLeftShift
    482 	case C.kVK_Option:
    483 		return key.CodeLeftAlt
    484 	case C.kVK_Command:
    485 		return key.CodeLeftGUI
    486 	case C.kVK_RightControl:
    487 		return key.CodeRightControl
    488 	case C.kVK_RightShift:
    489 		return key.CodeRightShift
    490 	case C.kVK_RightOption:
    491 		return key.CodeRightAlt
    492 	// TODO key.CodeRightGUI
    493 	default:
    494 		return key.CodeUnknown
    495 	}
    496 }