github.com/as/shiny@v0.8.2/driver/gldriver/cocoa.go (about)

     1  // Copyright 2015 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 386 amd64
     7  // +build !ios
     8  
     9  package gldriver
    10  
    11  /*
    12  #cgo CFLAGS: -x objective-c
    13  #cgo LDFLAGS: -framework Cocoa -framework OpenGL
    14  #include <OpenGL/gl3.h>
    15  #import <Carbon/Carbon.h> // for HIToolbox/Events.h
    16  #import <Cocoa/Cocoa.h>
    17  #include <pthread.h>
    18  #include <stdint.h>
    19  #include <stdlib.h>
    20  
    21  void startDriver();
    22  void stopDriver();
    23  void makeCurrentContext(uintptr_t ctx);
    24  void flushContext(uintptr_t ctx);
    25  uintptr_t doNewWindow(int width, int height, char* title);
    26  void doShowWindow(uintptr_t id);
    27  void doCloseWindow(uintptr_t id);
    28  uint64_t threadID();
    29  */
    30  import "C"
    31  
    32  import (
    33  	"errors"
    34  	"fmt"
    35  	"log"
    36  	"runtime"
    37  	"unsafe"
    38  
    39  	"github.com/as/shiny/driver/internal/lifecycler"
    40  	"github.com/as/shiny/event/key"
    41  	"github.com/as/shiny/event/lifecycle"
    42  	"github.com/as/shiny/event/mouse"
    43  	"github.com/as/shiny/event/paint"
    44  	"github.com/as/shiny/event/size"
    45  	"github.com/as/shiny/geom"
    46  	"github.com/as/shiny/gl"
    47  	"github.com/as/shiny/screen"
    48  )
    49  
    50  var initThreadID C.uint64_t
    51  
    52  func newWindow(opts *screen.NewWindowOptions) (uintptr, error) {
    53  	width, height := optsSize(opts)
    54  
    55  	title := C.CString(opts.GetTitle())
    56  	defer C.free(unsafe.Pointer(title))
    57  
    58  	return uintptr(C.doNewWindow(C.int(width), C.int(height), title)), nil
    59  }
    60  
    61  func initWindow(w *windowImpl) {
    62  	w.glctx, w.worker = gl.NewContext()
    63  }
    64  
    65  func showWindow(w *windowImpl) {
    66  	C.doShowWindow(C.uintptr_t(w.id))
    67  }
    68  
    69  //export preparedOpenGL
    70  func preparedOpenGL(id, ctx, vba uintptr) {
    71  	w := theScreen.window
    72  	w.ctx = ctx
    73  	go drawLoop(w, vba)
    74  }
    75  
    76  func closeWindow(id uintptr) {
    77  	C.doCloseWindow(C.uintptr_t(id))
    78  }
    79  
    80  var mainCallback func(screen.Screen)
    81  
    82  func main(f func(screen.Screen)) error {
    83  	initThreadID = C.threadID()
    84  	mainCallback = f
    85  	runtime.LockOSThread()
    86  	C.startDriver()
    87  	return nil
    88  }
    89  
    90  //export driverStarted
    91  func driverStarted() {
    92  	go func() {
    93  		mainCallback(theScreen)
    94  		C.stopDriver()
    95  	}()
    96  }
    97  
    98  //export drawgl
    99  func drawgl(id uintptr) {
   100  	w := theScreen.window
   101  	if w == nil {
   102  		panic("dead")
   103  		return // closing window
   104  	}
   105  	// TODO: is this necessary?
   106  	screen.SendPaint(paint.Event{External: true})
   107  	<-w.drawDone
   108  }
   109  
   110  // drawLoop is the primary drawing loop.
   111  //
   112  // After Cocoa has created an NSWindow and called prepareOpenGL,
   113  // it starts drawLoop on a locked goroutine to handle OpenGL calls.
   114  //
   115  // The screen is drawn every time a paint.Event is received, which can be
   116  // triggered either by the user or by Cocoa via drawgl (for example, when
   117  // the window is resized).
   118  func drawLoop(w *windowImpl, vba uintptr) {
   119  	runtime.LockOSThread()
   120  	C.makeCurrentContext(C.uintptr_t(w.ctx.(uintptr)))
   121  
   122  	// Starting in OS X 10.11 (El Capitan), the vertex array is
   123  	// occasionally getting unbound when the context changes threads.
   124  	//
   125  	// Avoid this by binding it again.
   126  	C.glBindVertexArray(C.GLuint(vba))
   127  	if errno := C.glGetError(); errno != 0 {
   128  		panic(fmt.Sprintf("gldriver: glBindVertexArray failed: %d", errno))
   129  	}
   130  
   131  	workAvailable := w.worker.WorkAvailable()
   132  
   133  	// TODO(crawshaw): exit this goroutine on Release.
   134  	for {
   135  		select {
   136  		case <-workAvailable:
   137  			w.worker.DoWork()
   138  		case <-w.publish:
   139  		loop:
   140  			for {
   141  				select {
   142  				case <-workAvailable:
   143  					w.worker.DoWork()
   144  				default:
   145  					break loop
   146  				}
   147  			}
   148  			C.flushContext(C.uintptr_t(w.ctx.(uintptr)))
   149  			C.flushContext(C.uintptr_t(w.ctx.(uintptr)))
   150  			w.publishDone <- screen.PublishResult{}
   151  		}
   152  	}
   153  }
   154  
   155  //export setGeom
   156  func setGeom(id uintptr, ppp float32, widthPx, heightPx int) {
   157  	w := theScreen.window
   158  	if w == nil {
   159  		panic("closing window")
   160  		return // closing window
   161  	}
   162  
   163  	w.sz = size.Event{
   164  		WidthPx:     widthPx,
   165  		HeightPx:    heightPx,
   166  		WidthPt:     geom.Pt(float32(widthPx) / ppp),
   167  		HeightPt:    geom.Pt(float32(heightPx) / ppp),
   168  		PixelsPerPt: ppp,
   169  	}
   170  	screen.SendSize(w.sz)
   171  }
   172  
   173  //export windowClosing
   174  func windowClosing(id uintptr) {
   175  	screen.SendLifecycle(lifecycle.Event{To: lifecycle.StageDead})
   176  	//sendLifecycle(id, (*lifecycler.State).SetDead, true)
   177  }
   178  
   179  func sendWindowEvent(id uintptr, e interface{}) {
   180  	log.Printf("sendWindowEvent: %#v, %#T\n", id, e)
   181  	//w.Send(e)
   182  }
   183  
   184  var mods = [...]struct {
   185  	flags uint32
   186  	code  uint16
   187  	mod   key.Modifiers
   188  }{
   189  	// Left and right variants of modifier keys have their own masks,
   190  	// but they are not documented. These were determined empirically.
   191  	{1<<17 | 0x102, C.kVK_Shift, key.ModShift},
   192  	{1<<17 | 0x104, C.kVK_RightShift, key.ModShift},
   193  	{1<<18 | 0x101, C.kVK_Control, key.ModControl},
   194  	// TODO key.ControlRight
   195  	{1<<19 | 0x120, C.kVK_Option, key.ModAlt},
   196  	{1<<19 | 0x140, C.kVK_RightOption, key.ModAlt},
   197  	{1<<20 | 0x108, C.kVK_Command, key.ModMeta},
   198  	{1<<20 | 0x110, C.kVK_Command, key.ModMeta}, // TODO: missing kVK_RightCommand
   199  }
   200  
   201  func cocoaMods(flags uint32) (m key.Modifiers) {
   202  	for _, mod := range mods {
   203  		if flags&mod.flags == mod.flags {
   204  			m |= mod.mod
   205  		}
   206  	}
   207  	return m
   208  }
   209  
   210  func cocoaMouseDir(ty int32) mouse.Direction {
   211  	switch ty {
   212  	case C.NSLeftMouseDown, C.NSRightMouseDown, C.NSOtherMouseDown:
   213  		return mouse.DirPress
   214  	case C.NSLeftMouseUp, C.NSRightMouseUp, C.NSOtherMouseUp:
   215  		return mouse.DirRelease
   216  	default: // dragged
   217  		return mouse.DirNone
   218  	}
   219  }
   220  
   221  func cocoaMouseButton(button int32) mouse.Button {
   222  	switch button {
   223  	case 0:
   224  		return mouse.ButtonLeft
   225  	case 1:
   226  		return mouse.ButtonRight
   227  	case 2:
   228  		return mouse.ButtonMiddle
   229  	default:
   230  		return mouse.ButtonNone
   231  	}
   232  }
   233  
   234  func scrollEvent(id uintptr, x, y, dx, dy float32, ty, buttonDeleteMe int32, flags uint32) {
   235  	button := mouse.ButtonWheelUp
   236  	if dy < 0 {
   237  		dy = -dy
   238  		button = mouse.ButtonWheelDown
   239  	}
   240  	screen.SendScroll(mouse.Event{
   241  		X:         x,
   242  		Y:         y,
   243  		Button:    button,
   244  		Direction: mouse.DirStep,
   245  		Modifiers: cocoaMods(flags),
   246  	})
   247  	return
   248  }
   249  
   250  //export mouseEvent
   251  func mouseEvent(id uintptr, x, y, dx, dy float32, ty, button int32, flags uint32) {
   252  	if ty == C.NSScrollWheel {
   253  		scrollEvent(id, x, y, dx, dy, ty, button, flags)
   254  		return
   255  	}
   256  
   257  	cmButton := mouse.ButtonNone
   258  	switch ty {
   259  	default:
   260  		cmButton = cocoaMouseButton(button)
   261  	case C.NSMouseMoved,
   262  		C.NSLeftMouseDragged,
   263  		C.NSRightMouseDragged,
   264  		C.NSOtherMouseDragged:
   265  		// No-op.
   266  	}
   267  	screen.SendMouse(mouse.Event{
   268  		X:         x,
   269  		Y:         y,
   270  		Button:    cmButton,
   271  		Direction: cocoaMouseDir(ty),
   272  		Modifiers: cocoaMods(flags),
   273  	})
   274  }
   275  
   276  //export keyEvent
   277  func keyEvent(id uintptr, runeVal rune, dir uint8, code uint16, flags uint32) {
   278  	screen.SendKey(key.Event{
   279  		Rune:      cocoaRune(runeVal),
   280  		Direction: key.Direction(dir),
   281  		Code:      cocoaKeyCode(code),
   282  		Modifiers: cocoaMods(flags),
   283  	})
   284  }
   285  
   286  //export flagEvent
   287  func flagEvent(id uintptr, flags uint32) {
   288  	for _, mod := range mods {
   289  		if flags&mod.flags == mod.flags && lastFlags&mod.flags != mod.flags {
   290  			keyEvent(id, -1, C.NSKeyDown, mod.code, flags)
   291  		}
   292  		if lastFlags&mod.flags == mod.flags && flags&mod.flags != mod.flags {
   293  			keyEvent(id, -1, C.NSKeyUp, mod.code, flags)
   294  		}
   295  	}
   296  	lastFlags = flags
   297  }
   298  
   299  var lastFlags uint32
   300  
   301  func sendLifecycle(id uintptr, setter func(*lifecycler.State, bool), val bool) {
   302  
   303  }
   304  
   305  func sendLifecycleAll(dead bool) {
   306  	screen.SendLifecycle(lifecycle.Event{To: lifecycle.StageFocused})
   307  	screen.SendLifecycle(lifecycle.Event{To: lifecycle.StageVisible})
   308  }
   309  
   310  //export lifecycleDeadAll
   311  func lifecycleDeadAll() { sendLifecycleAll(true) }
   312  
   313  //export lifecycleHideAll
   314  func lifecycleHideAll() { sendLifecycleAll(false) }
   315  
   316  //export lifecycleVisible
   317  func lifecycleVisible(id uintptr, val bool) {
   318  	screen.SendLifecycle(lifecycle.Event{To: lifecycle.StageVisible})
   319  }
   320  
   321  //export lifecycleFocused
   322  func lifecycleFocused(id uintptr, val bool) {
   323  	screen.SendLifecycle(lifecycle.Event{To: lifecycle.StageFocused})
   324  }
   325  
   326  // cocoaRune marks the Carbon/Cocoa private-range unicode rune representing
   327  // a non-unicode key event to -1, used for Rune in the key package.
   328  //
   329  // http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/CORPCHAR.TXT
   330  func cocoaRune(r rune) rune {
   331  	if '\uE000' <= r && r <= '\uF8FF' {
   332  		return -1
   333  	}
   334  	return r
   335  }
   336  
   337  // cocoaKeyCode converts a Carbon/Cocoa virtual key code number
   338  // into the standard keycodes used by the key package.
   339  //
   340  // To get a sense of the key map, see the diagram on
   341  //	http://boredzo.org/blog/archives/2007-05-22/virtual-key-codes
   342  func cocoaKeyCode(vkcode uint16) key.Code {
   343  	switch vkcode {
   344  	case C.kVK_ANSI_A:
   345  		return key.CodeA
   346  	case C.kVK_ANSI_B:
   347  		return key.CodeB
   348  	case C.kVK_ANSI_C:
   349  		return key.CodeC
   350  	case C.kVK_ANSI_D:
   351  		return key.CodeD
   352  	case C.kVK_ANSI_E:
   353  		return key.CodeE
   354  	case C.kVK_ANSI_F:
   355  		return key.CodeF
   356  	case C.kVK_ANSI_G:
   357  		return key.CodeG
   358  	case C.kVK_ANSI_H:
   359  		return key.CodeH
   360  	case C.kVK_ANSI_I:
   361  		return key.CodeI
   362  	case C.kVK_ANSI_J:
   363  		return key.CodeJ
   364  	case C.kVK_ANSI_K:
   365  		return key.CodeK
   366  	case C.kVK_ANSI_L:
   367  		return key.CodeL
   368  	case C.kVK_ANSI_M:
   369  		return key.CodeM
   370  	case C.kVK_ANSI_N:
   371  		return key.CodeN
   372  	case C.kVK_ANSI_O:
   373  		return key.CodeO
   374  	case C.kVK_ANSI_P:
   375  		return key.CodeP
   376  	case C.kVK_ANSI_Q:
   377  		return key.CodeQ
   378  	case C.kVK_ANSI_R:
   379  		return key.CodeR
   380  	case C.kVK_ANSI_S:
   381  		return key.CodeS
   382  	case C.kVK_ANSI_T:
   383  		return key.CodeT
   384  	case C.kVK_ANSI_U:
   385  		return key.CodeU
   386  	case C.kVK_ANSI_V:
   387  		return key.CodeV
   388  	case C.kVK_ANSI_W:
   389  		return key.CodeW
   390  	case C.kVK_ANSI_X:
   391  		return key.CodeX
   392  	case C.kVK_ANSI_Y:
   393  		return key.CodeY
   394  	case C.kVK_ANSI_Z:
   395  		return key.CodeZ
   396  	case C.kVK_ANSI_1:
   397  		return key.Code1
   398  	case C.kVK_ANSI_2:
   399  		return key.Code2
   400  	case C.kVK_ANSI_3:
   401  		return key.Code3
   402  	case C.kVK_ANSI_4:
   403  		return key.Code4
   404  	case C.kVK_ANSI_5:
   405  		return key.Code5
   406  	case C.kVK_ANSI_6:
   407  		return key.Code6
   408  	case C.kVK_ANSI_7:
   409  		return key.Code7
   410  	case C.kVK_ANSI_8:
   411  		return key.Code8
   412  	case C.kVK_ANSI_9:
   413  		return key.Code9
   414  	case C.kVK_ANSI_0:
   415  		return key.Code0
   416  	// TODO: move the rest of these codes to constants in key.go
   417  	// if we are happy with them.
   418  	case C.kVK_Return:
   419  		return key.CodeReturnEnter
   420  	case C.kVK_Escape:
   421  		return key.CodeEscape
   422  	case C.kVK_Delete:
   423  		return key.CodeDeleteBackspace
   424  	case C.kVK_Tab:
   425  		return key.CodeTab
   426  	case C.kVK_Space:
   427  		return key.CodeSpacebar
   428  	case C.kVK_ANSI_Minus:
   429  		return key.CodeHyphenMinus
   430  	case C.kVK_ANSI_Equal:
   431  		return key.CodeEqualSign
   432  	case C.kVK_ANSI_LeftBracket:
   433  		return key.CodeLeftSquareBracket
   434  	case C.kVK_ANSI_RightBracket:
   435  		return key.CodeRightSquareBracket
   436  	case C.kVK_ANSI_Backslash:
   437  		return key.CodeBackslash
   438  	// 50: Keyboard Non-US "#" and ~
   439  	case C.kVK_ANSI_Semicolon:
   440  		return key.CodeSemicolon
   441  	case C.kVK_ANSI_Quote:
   442  		return key.CodeApostrophe
   443  	case C.kVK_ANSI_Grave:
   444  		return key.CodeGraveAccent
   445  	case C.kVK_ANSI_Comma:
   446  		return key.CodeComma
   447  	case C.kVK_ANSI_Period:
   448  		return key.CodeFullStop
   449  	case C.kVK_ANSI_Slash:
   450  		return key.CodeSlash
   451  	case C.kVK_CapsLock:
   452  		return key.CodeCapsLock
   453  	case C.kVK_F1:
   454  		return key.CodeF1
   455  	case C.kVK_F2:
   456  		return key.CodeF2
   457  	case C.kVK_F3:
   458  		return key.CodeF3
   459  	case C.kVK_F4:
   460  		return key.CodeF4
   461  	case C.kVK_F5:
   462  		return key.CodeF5
   463  	case C.kVK_F6:
   464  		return key.CodeF6
   465  	case C.kVK_F7:
   466  		return key.CodeF7
   467  	case C.kVK_F8:
   468  		return key.CodeF8
   469  	case C.kVK_F9:
   470  		return key.CodeF9
   471  	case C.kVK_F10:
   472  		return key.CodeF10
   473  	case C.kVK_F11:
   474  		return key.CodeF11
   475  	case C.kVK_F12:
   476  		return key.CodeF12
   477  	// 70: PrintScreen
   478  	// 71: Scroll Lock
   479  	// 72: Pause
   480  	// 73: Insert
   481  	case C.kVK_Home:
   482  		return key.CodeHome
   483  	case C.kVK_PageUp:
   484  		return key.CodePageUp
   485  	case C.kVK_ForwardDelete:
   486  		return key.CodeDeleteForward
   487  	case C.kVK_End:
   488  		return key.CodeEnd
   489  	case C.kVK_PageDown:
   490  		return key.CodePageDown
   491  	case C.kVK_RightArrow:
   492  		return key.CodeRightArrow
   493  	case C.kVK_LeftArrow:
   494  		return key.CodeLeftArrow
   495  	case C.kVK_DownArrow:
   496  		return key.CodeDownArrow
   497  	case C.kVK_UpArrow:
   498  		return key.CodeUpArrow
   499  	case C.kVK_ANSI_KeypadClear:
   500  		return key.CodeKeypadNumLock
   501  	case C.kVK_ANSI_KeypadDivide:
   502  		return key.CodeKeypadSlash
   503  	case C.kVK_ANSI_KeypadMultiply:
   504  		return key.CodeKeypadAsterisk
   505  	case C.kVK_ANSI_KeypadMinus:
   506  		return key.CodeKeypadHyphenMinus
   507  	case C.kVK_ANSI_KeypadPlus:
   508  		return key.CodeKeypadPlusSign
   509  	case C.kVK_ANSI_KeypadEnter:
   510  		return key.CodeKeypadEnter
   511  	case C.kVK_ANSI_Keypad1:
   512  		return key.CodeKeypad1
   513  	case C.kVK_ANSI_Keypad2:
   514  		return key.CodeKeypad2
   515  	case C.kVK_ANSI_Keypad3:
   516  		return key.CodeKeypad3
   517  	case C.kVK_ANSI_Keypad4:
   518  		return key.CodeKeypad4
   519  	case C.kVK_ANSI_Keypad5:
   520  		return key.CodeKeypad5
   521  	case C.kVK_ANSI_Keypad6:
   522  		return key.CodeKeypad6
   523  	case C.kVK_ANSI_Keypad7:
   524  		return key.CodeKeypad7
   525  	case C.kVK_ANSI_Keypad8:
   526  		return key.CodeKeypad8
   527  	case C.kVK_ANSI_Keypad9:
   528  		return key.CodeKeypad9
   529  	case C.kVK_ANSI_Keypad0:
   530  		return key.CodeKeypad0
   531  	case C.kVK_ANSI_KeypadDecimal:
   532  		return key.CodeKeypadFullStop
   533  	case C.kVK_ANSI_KeypadEquals:
   534  		return key.CodeKeypadEqualSign
   535  	case C.kVK_F13:
   536  		return key.CodeF13
   537  	case C.kVK_F14:
   538  		return key.CodeF14
   539  	case C.kVK_F15:
   540  		return key.CodeF15
   541  	case C.kVK_F16:
   542  		return key.CodeF16
   543  	case C.kVK_F17:
   544  		return key.CodeF17
   545  	case C.kVK_F18:
   546  		return key.CodeF18
   547  	case C.kVK_F19:
   548  		return key.CodeF19
   549  	case C.kVK_F20:
   550  		return key.CodeF20
   551  	// 116: Keyboard Execute
   552  	case C.kVK_Help:
   553  		return key.CodeHelp
   554  	// 118: Keyboard Menu
   555  	// 119: Keyboard Select
   556  	// 120: Keyboard Stop
   557  	// 121: Keyboard Again
   558  	// 122: Keyboard Undo
   559  	// 123: Keyboard Cut
   560  	// 124: Keyboard Copy
   561  	// 125: Keyboard Paste
   562  	// 126: Keyboard Find
   563  	case C.kVK_Mute:
   564  		return key.CodeMute
   565  	case C.kVK_VolumeUp:
   566  		return key.CodeVolumeUp
   567  	case C.kVK_VolumeDown:
   568  		return key.CodeVolumeDown
   569  	// 130: Keyboard Locking Caps Lock
   570  	// 131: Keyboard Locking Num Lock
   571  	// 132: Keyboard Locking Scroll Lock
   572  	// 133: Keyboard Comma
   573  	// 134: Keyboard Equal Sign
   574  	// ...: Bunch of stuff
   575  	case C.kVK_Control:
   576  		return key.CodeLeftControl
   577  	case C.kVK_Shift:
   578  		return key.CodeLeftShift
   579  	case C.kVK_Option:
   580  		return key.CodeLeftAlt
   581  	case C.kVK_Command:
   582  		return key.CodeLeftGUI
   583  	case C.kVK_RightControl:
   584  		return key.CodeRightControl
   585  	case C.kVK_RightShift:
   586  		return key.CodeRightShift
   587  	case C.kVK_RightOption:
   588  		return key.CodeRightAlt
   589  	// TODO key.CodeRightGUI
   590  	default:
   591  		return key.CodeUnknown
   592  	}
   593  }
   594  
   595  func surfaceCreate() error {
   596  	return errors.New("gldriver: surface creation not implemented on darwin")
   597  }