github.com/c-darwin/mobile@v0.0.0-20160313183840-ff625c46f7c9/app/darwin_amd64.go (about)

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