darwin_desktop.go 12 KB

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