github.com/champo/mobile@v0.0.0-20190107162257-dc0771356504/app/darwin_desktop.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  // +build !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
    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  }