darwin_desktop.go 12 KB

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