Add vendor
Specifically because I want to modify a dependency
This commit is contained in:
27
vendor/golang.org/x/exp/shiny/LICENSE
generated
vendored
Normal file
27
vendor/golang.org/x/exp/shiny/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright 2009 The Go Authors.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google LLC nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
32
vendor/golang.org/x/exp/shiny/driver/gldriver/buffer.go
generated
vendored
Normal file
32
vendor/golang.org/x/exp/shiny/driver/gldriver/buffer.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gldriver
|
||||
|
||||
import "image"
|
||||
|
||||
type bufferImpl struct {
|
||||
// buf should always be equal to (i.e. the same ptr, len, cap as) rgba.Pix.
|
||||
// It is a separate, redundant field in order to detect modifications to
|
||||
// the rgba field that are invalid as per the screen.Buffer documentation.
|
||||
buf []byte
|
||||
rgba image.RGBA
|
||||
size image.Point
|
||||
}
|
||||
|
||||
func (b *bufferImpl) Release() {}
|
||||
func (b *bufferImpl) Size() image.Point { return b.size }
|
||||
func (b *bufferImpl) Bounds() image.Rectangle { return image.Rectangle{Max: b.size} }
|
||||
func (b *bufferImpl) RGBA() *image.RGBA { return &b.rgba }
|
||||
|
||||
func (b *bufferImpl) preUpload() {
|
||||
// Check that the program hasn't tried to modify the rgba field via the
|
||||
// pointer returned by the bufferImpl.RGBA method. This check doesn't catch
|
||||
// 100% of all cases; it simply tries to detect some invalid uses of a
|
||||
// screen.Buffer such as:
|
||||
// *buffer.RGBA() = anotherImageRGBA
|
||||
if len(b.buf) != 0 && len(b.rgba.Pix) != 0 && &b.buf[0] != &b.rgba.Pix[0] {
|
||||
panic("gldriver: invalid Buffer.RGBA modification")
|
||||
}
|
||||
}
|
||||
670
vendor/golang.org/x/exp/shiny/driver/gldriver/cocoa.go
generated
vendored
Normal file
670
vendor/golang.org/x/exp/shiny/driver/gldriver/cocoa.go
generated
vendored
Normal file
@@ -0,0 +1,670 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build darwin && !ios
|
||||
|
||||
package gldriver
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c -DGL_SILENCE_DEPRECATION
|
||||
#cgo LDFLAGS: -framework Cocoa -framework OpenGL
|
||||
#include <OpenGL/gl3.h>
|
||||
#import <Carbon/Carbon.h> // for HIToolbox/Events.h
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void startDriver();
|
||||
void stopDriver();
|
||||
void makeCurrentContext(uintptr_t ctx);
|
||||
void flushContext(uintptr_t ctx);
|
||||
uintptr_t doNewWindow(int width, int height, char* title);
|
||||
void doShowWindow(uintptr_t id);
|
||||
void doCloseWindow(uintptr_t id);
|
||||
uint64_t threadID();
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/exp/shiny/driver/internal/lifecycler"
|
||||
"golang.org/x/exp/shiny/screen"
|
||||
"golang.org/x/mobile/event/key"
|
||||
"golang.org/x/mobile/event/mouse"
|
||||
"golang.org/x/mobile/event/paint"
|
||||
"golang.org/x/mobile/event/size"
|
||||
"golang.org/x/mobile/geom"
|
||||
"golang.org/x/mobile/gl"
|
||||
)
|
||||
|
||||
const useLifecycler = true
|
||||
|
||||
// TODO: change this to true, after manual testing on OS X.
|
||||
const handleSizeEventsAtChannelReceive = false
|
||||
|
||||
var initThreadID C.uint64_t
|
||||
|
||||
func init() {
|
||||
// Lock the goroutine responsible for initialization to an OS thread.
|
||||
// This means the goroutine running main (and calling startDriver below)
|
||||
// is locked to the OS thread that started the program. This is
|
||||
// necessary for the correct delivery of Cocoa events to the process.
|
||||
//
|
||||
// A discussion on this topic:
|
||||
// https://groups.google.com/forum/#!msg/golang-nuts/IiWZ2hUuLDA/SNKYYZBelsYJ
|
||||
runtime.LockOSThread()
|
||||
initThreadID = C.threadID()
|
||||
}
|
||||
|
||||
func newWindow(opts *screen.NewWindowOptions) (uintptr, error) {
|
||||
width, height := optsSize(opts)
|
||||
|
||||
title := C.CString(opts.GetTitle())
|
||||
defer C.free(unsafe.Pointer(title))
|
||||
|
||||
return uintptr(C.doNewWindow(C.int(width), C.int(height), title)), nil
|
||||
}
|
||||
|
||||
func initWindow(w *windowImpl) {
|
||||
w.glctx, w.worker = gl.NewContext()
|
||||
}
|
||||
|
||||
func showWindow(w *windowImpl) {
|
||||
C.doShowWindow(C.uintptr_t(w.id))
|
||||
}
|
||||
|
||||
//export preparedOpenGL
|
||||
func preparedOpenGL(id, ctx, vba uintptr) {
|
||||
theScreen.mu.Lock()
|
||||
w := theScreen.windows[id]
|
||||
theScreen.mu.Unlock()
|
||||
|
||||
w.ctx = ctx
|
||||
go drawLoop(w, vba)
|
||||
}
|
||||
|
||||
func closeWindow(id uintptr) {
|
||||
C.doCloseWindow(C.uintptr_t(id))
|
||||
}
|
||||
|
||||
var mainCallback func(screen.Screen)
|
||||
|
||||
func main(f func(screen.Screen)) error {
|
||||
if tid := C.threadID(); tid != initThreadID {
|
||||
log.Fatalf("gldriver.Main called on thread %d, but gldriver.init ran on %d", tid, initThreadID)
|
||||
}
|
||||
|
||||
mainCallback = f
|
||||
C.startDriver()
|
||||
return nil
|
||||
}
|
||||
|
||||
//export driverStarted
|
||||
func driverStarted() {
|
||||
go func() {
|
||||
mainCallback(theScreen)
|
||||
C.stopDriver()
|
||||
}()
|
||||
}
|
||||
|
||||
//export drawgl
|
||||
func drawgl(id uintptr) {
|
||||
theScreen.mu.Lock()
|
||||
w := theScreen.windows[id]
|
||||
theScreen.mu.Unlock()
|
||||
|
||||
if w == nil {
|
||||
return // closing window
|
||||
}
|
||||
|
||||
// TODO: is this necessary?
|
||||
w.lifecycler.SetVisible(true)
|
||||
w.lifecycler.SendEvent(w, w.glctx)
|
||||
|
||||
w.Send(paint.Event{External: true})
|
||||
<-w.drawDone
|
||||
}
|
||||
|
||||
// drawLoop is the primary drawing loop.
|
||||
//
|
||||
// After Cocoa has created an NSWindow and called prepareOpenGL,
|
||||
// it starts drawLoop on a locked goroutine to handle OpenGL calls.
|
||||
//
|
||||
// The screen is drawn every time a paint.Event is received, which can be
|
||||
// triggered either by the user or by Cocoa via drawgl (for example, when
|
||||
// the window is resized).
|
||||
func drawLoop(w *windowImpl, vba uintptr) {
|
||||
runtime.LockOSThread()
|
||||
C.makeCurrentContext(C.uintptr_t(w.ctx.(uintptr)))
|
||||
|
||||
// Starting in OS X 10.11 (El Capitan), the vertex array is
|
||||
// occasionally getting unbound when the context changes threads.
|
||||
//
|
||||
// Avoid this by binding it again.
|
||||
C.glBindVertexArray(C.GLuint(vba))
|
||||
if errno := C.glGetError(); errno != 0 {
|
||||
panic(fmt.Sprintf("gldriver: glBindVertexArray failed: %d", errno))
|
||||
}
|
||||
|
||||
workAvailable := w.worker.WorkAvailable()
|
||||
|
||||
// TODO(crawshaw): exit this goroutine on Release.
|
||||
for {
|
||||
select {
|
||||
case <-workAvailable:
|
||||
w.worker.DoWork()
|
||||
case <-w.publish:
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case <-workAvailable:
|
||||
w.worker.DoWork()
|
||||
default:
|
||||
break loop
|
||||
}
|
||||
}
|
||||
C.flushContext(C.uintptr_t(w.ctx.(uintptr)))
|
||||
w.publishDone <- screen.PublishResult{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//export setGeom
|
||||
func setGeom(id uintptr, ppp float32, widthPx, heightPx int) {
|
||||
theScreen.mu.Lock()
|
||||
w := theScreen.windows[id]
|
||||
theScreen.mu.Unlock()
|
||||
|
||||
if w == nil {
|
||||
return // closing window
|
||||
}
|
||||
|
||||
sz := size.Event{
|
||||
WidthPx: widthPx,
|
||||
HeightPx: heightPx,
|
||||
WidthPt: geom.Pt(float32(widthPx) / ppp),
|
||||
HeightPt: geom.Pt(float32(heightPx) / ppp),
|
||||
PixelsPerPt: ppp,
|
||||
}
|
||||
|
||||
if !handleSizeEventsAtChannelReceive {
|
||||
w.szMu.Lock()
|
||||
w.sz = sz
|
||||
w.szMu.Unlock()
|
||||
}
|
||||
|
||||
w.Send(sz)
|
||||
}
|
||||
|
||||
//export windowClosing
|
||||
func windowClosing(id uintptr) {
|
||||
sendLifecycle(id, (*lifecycler.State).SetDead, true)
|
||||
}
|
||||
|
||||
func sendWindowEvent(id uintptr, e interface{}) {
|
||||
theScreen.mu.Lock()
|
||||
w := theScreen.windows[id]
|
||||
theScreen.mu.Unlock()
|
||||
|
||||
if w == nil {
|
||||
return // closing window
|
||||
}
|
||||
w.Send(e)
|
||||
}
|
||||
|
||||
var mods = [...]struct {
|
||||
flags uint32
|
||||
code uint16
|
||||
mod key.Modifiers
|
||||
}{
|
||||
// Left and right variants of modifier keys have their own masks,
|
||||
// but they are not documented. These were determined empirically.
|
||||
{1<<17 | 0x102, C.kVK_Shift, key.ModShift},
|
||||
{1<<17 | 0x104, C.kVK_RightShift, key.ModShift},
|
||||
{1<<18 | 0x101, C.kVK_Control, key.ModControl},
|
||||
{33<<13 | 0x100, C.kVK_RightControl, key.ModControl},
|
||||
{1<<19 | 0x120, C.kVK_Option, key.ModAlt},
|
||||
{1<<19 | 0x140, C.kVK_RightOption, key.ModAlt},
|
||||
{1<<20 | 0x108, C.kVK_Command, key.ModMeta},
|
||||
{1<<20 | 0x110, 0x36 /* kVK_RightCommand */, key.ModMeta},
|
||||
}
|
||||
|
||||
func cocoaMods(flags uint32) (m key.Modifiers) {
|
||||
for _, mod := range mods {
|
||||
if flags&mod.flags == mod.flags {
|
||||
m |= mod.mod
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func cocoaMouseDir(ty int32) mouse.Direction {
|
||||
switch ty {
|
||||
case C.NSLeftMouseDown, C.NSRightMouseDown, C.NSOtherMouseDown:
|
||||
return mouse.DirPress
|
||||
case C.NSLeftMouseUp, C.NSRightMouseUp, C.NSOtherMouseUp:
|
||||
return mouse.DirRelease
|
||||
default: // dragged
|
||||
return mouse.DirNone
|
||||
}
|
||||
}
|
||||
|
||||
func cocoaMouseButton(button int32) mouse.Button {
|
||||
switch button {
|
||||
case 0:
|
||||
return mouse.ButtonLeft
|
||||
case 1:
|
||||
return mouse.ButtonRight
|
||||
case 2:
|
||||
return mouse.ButtonMiddle
|
||||
default:
|
||||
return mouse.ButtonNone
|
||||
}
|
||||
}
|
||||
|
||||
//export mouseEvent
|
||||
func mouseEvent(id uintptr, x, y, dx, dy float32, ty, button int32, flags uint32) {
|
||||
cmButton := mouse.ButtonNone
|
||||
switch ty {
|
||||
default:
|
||||
cmButton = cocoaMouseButton(button)
|
||||
case C.NSMouseMoved, C.NSLeftMouseDragged, C.NSRightMouseDragged, C.NSOtherMouseDragged:
|
||||
// No-op.
|
||||
case C.NSScrollWheel:
|
||||
// Note that the direction of scrolling is inverted by default
|
||||
// on OS X by the "natural scrolling" setting. At the Cocoa
|
||||
// level this inversion is applied to trackpads and mice behind
|
||||
// the scenes, and the value of dy goes in the direction the OS
|
||||
// wants scrolling to go.
|
||||
//
|
||||
// This means the same trackpad/mouse motion on OS X and Linux
|
||||
// can produce wheel events in opposite directions, but the
|
||||
// direction matches what other programs on the OS do.
|
||||
//
|
||||
// If we wanted to expose the physical device motion in the
|
||||
// event we could use [NSEvent isDirectionInvertedFromDevice]
|
||||
// to know if "natural scrolling" is enabled.
|
||||
//
|
||||
// TODO: On a trackpad, a scroll can be a drawn-out affair with a
|
||||
// distinct beginning and end. Should the intermediate events be
|
||||
// DirNone?
|
||||
//
|
||||
// TODO: handle horizontal scrolling
|
||||
button := mouse.ButtonWheelUp
|
||||
if dy < 0 {
|
||||
dy = -dy
|
||||
button = mouse.ButtonWheelDown
|
||||
}
|
||||
e := mouse.Event{
|
||||
X: x,
|
||||
Y: y,
|
||||
Button: button,
|
||||
Direction: mouse.DirStep,
|
||||
Modifiers: cocoaMods(flags),
|
||||
}
|
||||
for delta := int(dy); delta != 0; delta-- {
|
||||
sendWindowEvent(id, e)
|
||||
}
|
||||
return
|
||||
}
|
||||
sendWindowEvent(id, mouse.Event{
|
||||
X: x,
|
||||
Y: y,
|
||||
Button: cmButton,
|
||||
Direction: cocoaMouseDir(ty),
|
||||
Modifiers: cocoaMods(flags),
|
||||
})
|
||||
}
|
||||
|
||||
//export keyEvent
|
||||
func keyEvent(id uintptr, runeVal rune, dir uint8, code uint16, flags uint32) {
|
||||
sendWindowEvent(id, key.Event{
|
||||
Rune: cocoaRune(runeVal),
|
||||
Direction: key.Direction(dir),
|
||||
Code: cocoaKeyCode(code),
|
||||
Modifiers: cocoaMods(flags),
|
||||
})
|
||||
}
|
||||
|
||||
//export flagEvent
|
||||
func flagEvent(id uintptr, flags uint32) {
|
||||
for _, mod := range mods {
|
||||
if flags&mod.flags == mod.flags && lastFlags&mod.flags != mod.flags {
|
||||
keyEvent(id, -1, C.NSKeyDown, mod.code, flags)
|
||||
}
|
||||
if lastFlags&mod.flags == mod.flags && flags&mod.flags != mod.flags {
|
||||
keyEvent(id, -1, C.NSKeyUp, mod.code, flags)
|
||||
}
|
||||
}
|
||||
lastFlags = flags
|
||||
}
|
||||
|
||||
var lastFlags uint32
|
||||
|
||||
func sendLifecycle(id uintptr, setter func(*lifecycler.State, bool), val bool) {
|
||||
theScreen.mu.Lock()
|
||||
w := theScreen.windows[id]
|
||||
theScreen.mu.Unlock()
|
||||
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
setter(&w.lifecycler, val)
|
||||
w.lifecycler.SendEvent(w, w.glctx)
|
||||
}
|
||||
|
||||
func sendLifecycleAll(dead bool) {
|
||||
windows := []*windowImpl{}
|
||||
|
||||
theScreen.mu.Lock()
|
||||
for _, w := range theScreen.windows {
|
||||
windows = append(windows, w)
|
||||
}
|
||||
theScreen.mu.Unlock()
|
||||
|
||||
for _, w := range windows {
|
||||
w.lifecycler.SetFocused(false)
|
||||
w.lifecycler.SetVisible(false)
|
||||
if dead {
|
||||
w.lifecycler.SetDead(true)
|
||||
}
|
||||
w.lifecycler.SendEvent(w, w.glctx)
|
||||
}
|
||||
}
|
||||
|
||||
//export lifecycleDeadAll
|
||||
func lifecycleDeadAll() { sendLifecycleAll(true) }
|
||||
|
||||
//export lifecycleHideAll
|
||||
func lifecycleHideAll() { sendLifecycleAll(false) }
|
||||
|
||||
//export lifecycleVisible
|
||||
func lifecycleVisible(id uintptr, val bool) {
|
||||
sendLifecycle(id, (*lifecycler.State).SetVisible, val)
|
||||
}
|
||||
|
||||
//export lifecycleFocused
|
||||
func lifecycleFocused(id uintptr, val bool) {
|
||||
sendLifecycle(id, (*lifecycler.State).SetFocused, val)
|
||||
}
|
||||
|
||||
// cocoaRune marks the Carbon/Cocoa private-range unicode rune representing
|
||||
// a non-unicode key event to -1, used for Rune in the key package.
|
||||
//
|
||||
// http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/CORPCHAR.TXT
|
||||
func cocoaRune(r rune) rune {
|
||||
if '\uE000' <= r && r <= '\uF8FF' {
|
||||
return -1
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// cocoaKeyCode converts a Carbon/Cocoa virtual key code number
|
||||
// into the standard keycodes used by the key package.
|
||||
//
|
||||
// To get a sense of the key map, see the diagram on
|
||||
//
|
||||
// http://boredzo.org/blog/archives/2007-05-22/virtual-key-codes
|
||||
func cocoaKeyCode(vkcode uint16) key.Code {
|
||||
switch vkcode {
|
||||
case C.kVK_ANSI_A:
|
||||
return key.CodeA
|
||||
case C.kVK_ANSI_B:
|
||||
return key.CodeB
|
||||
case C.kVK_ANSI_C:
|
||||
return key.CodeC
|
||||
case C.kVK_ANSI_D:
|
||||
return key.CodeD
|
||||
case C.kVK_ANSI_E:
|
||||
return key.CodeE
|
||||
case C.kVK_ANSI_F:
|
||||
return key.CodeF
|
||||
case C.kVK_ANSI_G:
|
||||
return key.CodeG
|
||||
case C.kVK_ANSI_H:
|
||||
return key.CodeH
|
||||
case C.kVK_ANSI_I:
|
||||
return key.CodeI
|
||||
case C.kVK_ANSI_J:
|
||||
return key.CodeJ
|
||||
case C.kVK_ANSI_K:
|
||||
return key.CodeK
|
||||
case C.kVK_ANSI_L:
|
||||
return key.CodeL
|
||||
case C.kVK_ANSI_M:
|
||||
return key.CodeM
|
||||
case C.kVK_ANSI_N:
|
||||
return key.CodeN
|
||||
case C.kVK_ANSI_O:
|
||||
return key.CodeO
|
||||
case C.kVK_ANSI_P:
|
||||
return key.CodeP
|
||||
case C.kVK_ANSI_Q:
|
||||
return key.CodeQ
|
||||
case C.kVK_ANSI_R:
|
||||
return key.CodeR
|
||||
case C.kVK_ANSI_S:
|
||||
return key.CodeS
|
||||
case C.kVK_ANSI_T:
|
||||
return key.CodeT
|
||||
case C.kVK_ANSI_U:
|
||||
return key.CodeU
|
||||
case C.kVK_ANSI_V:
|
||||
return key.CodeV
|
||||
case C.kVK_ANSI_W:
|
||||
return key.CodeW
|
||||
case C.kVK_ANSI_X:
|
||||
return key.CodeX
|
||||
case C.kVK_ANSI_Y:
|
||||
return key.CodeY
|
||||
case C.kVK_ANSI_Z:
|
||||
return key.CodeZ
|
||||
case C.kVK_ANSI_1:
|
||||
return key.Code1
|
||||
case C.kVK_ANSI_2:
|
||||
return key.Code2
|
||||
case C.kVK_ANSI_3:
|
||||
return key.Code3
|
||||
case C.kVK_ANSI_4:
|
||||
return key.Code4
|
||||
case C.kVK_ANSI_5:
|
||||
return key.Code5
|
||||
case C.kVK_ANSI_6:
|
||||
return key.Code6
|
||||
case C.kVK_ANSI_7:
|
||||
return key.Code7
|
||||
case C.kVK_ANSI_8:
|
||||
return key.Code8
|
||||
case C.kVK_ANSI_9:
|
||||
return key.Code9
|
||||
case C.kVK_ANSI_0:
|
||||
return key.Code0
|
||||
// TODO: move the rest of these codes to constants in key.go
|
||||
// if we are happy with them.
|
||||
case C.kVK_Return:
|
||||
return key.CodeReturnEnter
|
||||
case C.kVK_Escape:
|
||||
return key.CodeEscape
|
||||
case C.kVK_Delete:
|
||||
return key.CodeDeleteBackspace
|
||||
case C.kVK_Tab:
|
||||
return key.CodeTab
|
||||
case C.kVK_Space:
|
||||
return key.CodeSpacebar
|
||||
case C.kVK_ANSI_Minus:
|
||||
return key.CodeHyphenMinus
|
||||
case C.kVK_ANSI_Equal:
|
||||
return key.CodeEqualSign
|
||||
case C.kVK_ANSI_LeftBracket:
|
||||
return key.CodeLeftSquareBracket
|
||||
case C.kVK_ANSI_RightBracket:
|
||||
return key.CodeRightSquareBracket
|
||||
case C.kVK_ANSI_Backslash:
|
||||
return key.CodeBackslash
|
||||
// 50: Keyboard Non-US "#" and ~
|
||||
case C.kVK_ANSI_Semicolon:
|
||||
return key.CodeSemicolon
|
||||
case C.kVK_ANSI_Quote:
|
||||
return key.CodeApostrophe
|
||||
case C.kVK_ANSI_Grave:
|
||||
return key.CodeGraveAccent
|
||||
case C.kVK_ANSI_Comma:
|
||||
return key.CodeComma
|
||||
case C.kVK_ANSI_Period:
|
||||
return key.CodeFullStop
|
||||
case C.kVK_ANSI_Slash:
|
||||
return key.CodeSlash
|
||||
case C.kVK_CapsLock:
|
||||
return key.CodeCapsLock
|
||||
case C.kVK_F1:
|
||||
return key.CodeF1
|
||||
case C.kVK_F2:
|
||||
return key.CodeF2
|
||||
case C.kVK_F3:
|
||||
return key.CodeF3
|
||||
case C.kVK_F4:
|
||||
return key.CodeF4
|
||||
case C.kVK_F5:
|
||||
return key.CodeF5
|
||||
case C.kVK_F6:
|
||||
return key.CodeF6
|
||||
case C.kVK_F7:
|
||||
return key.CodeF7
|
||||
case C.kVK_F8:
|
||||
return key.CodeF8
|
||||
case C.kVK_F9:
|
||||
return key.CodeF9
|
||||
case C.kVK_F10:
|
||||
return key.CodeF10
|
||||
case C.kVK_F11:
|
||||
return key.CodeF11
|
||||
case C.kVK_F12:
|
||||
return key.CodeF12
|
||||
// 70: PrintScreen
|
||||
// 71: Scroll Lock
|
||||
// 72: Pause
|
||||
// 73: Insert
|
||||
case C.kVK_Home:
|
||||
return key.CodeHome
|
||||
case C.kVK_PageUp:
|
||||
return key.CodePageUp
|
||||
case C.kVK_ForwardDelete:
|
||||
return key.CodeDeleteForward
|
||||
case C.kVK_End:
|
||||
return key.CodeEnd
|
||||
case C.kVK_PageDown:
|
||||
return key.CodePageDown
|
||||
case C.kVK_RightArrow:
|
||||
return key.CodeRightArrow
|
||||
case C.kVK_LeftArrow:
|
||||
return key.CodeLeftArrow
|
||||
case C.kVK_DownArrow:
|
||||
return key.CodeDownArrow
|
||||
case C.kVK_UpArrow:
|
||||
return key.CodeUpArrow
|
||||
case C.kVK_ANSI_KeypadClear:
|
||||
return key.CodeKeypadNumLock
|
||||
case C.kVK_ANSI_KeypadDivide:
|
||||
return key.CodeKeypadSlash
|
||||
case C.kVK_ANSI_KeypadMultiply:
|
||||
return key.CodeKeypadAsterisk
|
||||
case C.kVK_ANSI_KeypadMinus:
|
||||
return key.CodeKeypadHyphenMinus
|
||||
case C.kVK_ANSI_KeypadPlus:
|
||||
return key.CodeKeypadPlusSign
|
||||
case C.kVK_ANSI_KeypadEnter:
|
||||
return key.CodeKeypadEnter
|
||||
case C.kVK_ANSI_Keypad1:
|
||||
return key.CodeKeypad1
|
||||
case C.kVK_ANSI_Keypad2:
|
||||
return key.CodeKeypad2
|
||||
case C.kVK_ANSI_Keypad3:
|
||||
return key.CodeKeypad3
|
||||
case C.kVK_ANSI_Keypad4:
|
||||
return key.CodeKeypad4
|
||||
case C.kVK_ANSI_Keypad5:
|
||||
return key.CodeKeypad5
|
||||
case C.kVK_ANSI_Keypad6:
|
||||
return key.CodeKeypad6
|
||||
case C.kVK_ANSI_Keypad7:
|
||||
return key.CodeKeypad7
|
||||
case C.kVK_ANSI_Keypad8:
|
||||
return key.CodeKeypad8
|
||||
case C.kVK_ANSI_Keypad9:
|
||||
return key.CodeKeypad9
|
||||
case C.kVK_ANSI_Keypad0:
|
||||
return key.CodeKeypad0
|
||||
case C.kVK_ANSI_KeypadDecimal:
|
||||
return key.CodeKeypadFullStop
|
||||
case C.kVK_ANSI_KeypadEquals:
|
||||
return key.CodeKeypadEqualSign
|
||||
case C.kVK_F13:
|
||||
return key.CodeF13
|
||||
case C.kVK_F14:
|
||||
return key.CodeF14
|
||||
case C.kVK_F15:
|
||||
return key.CodeF15
|
||||
case C.kVK_F16:
|
||||
return key.CodeF16
|
||||
case C.kVK_F17:
|
||||
return key.CodeF17
|
||||
case C.kVK_F18:
|
||||
return key.CodeF18
|
||||
case C.kVK_F19:
|
||||
return key.CodeF19
|
||||
case C.kVK_F20:
|
||||
return key.CodeF20
|
||||
// 116: Keyboard Execute
|
||||
case C.kVK_Help:
|
||||
return key.CodeHelp
|
||||
// 118: Keyboard Menu
|
||||
// 119: Keyboard Select
|
||||
// 120: Keyboard Stop
|
||||
// 121: Keyboard Again
|
||||
// 122: Keyboard Undo
|
||||
// 123: Keyboard Cut
|
||||
// 124: Keyboard Copy
|
||||
// 125: Keyboard Paste
|
||||
// 126: Keyboard Find
|
||||
case C.kVK_Mute:
|
||||
return key.CodeMute
|
||||
case C.kVK_VolumeUp:
|
||||
return key.CodeVolumeUp
|
||||
case C.kVK_VolumeDown:
|
||||
return key.CodeVolumeDown
|
||||
// 130: Keyboard Locking Caps Lock
|
||||
// 131: Keyboard Locking Num Lock
|
||||
// 132: Keyboard Locking Scroll Lock
|
||||
// 133: Keyboard Comma
|
||||
// 134: Keyboard Equal Sign
|
||||
// ...: Bunch of stuff
|
||||
case C.kVK_Control:
|
||||
return key.CodeLeftControl
|
||||
case C.kVK_Shift:
|
||||
return key.CodeLeftShift
|
||||
case C.kVK_Option:
|
||||
return key.CodeLeftAlt
|
||||
case C.kVK_Command:
|
||||
return key.CodeLeftGUI
|
||||
case C.kVK_RightControl:
|
||||
return key.CodeRightControl
|
||||
case C.kVK_RightShift:
|
||||
return key.CodeRightShift
|
||||
case C.kVK_RightOption:
|
||||
return key.CodeRightAlt
|
||||
// TODO key.CodeRightGUI
|
||||
default:
|
||||
return key.CodeUnknown
|
||||
}
|
||||
}
|
||||
|
||||
func surfaceCreate() error {
|
||||
return errors.New("gldriver: surface creation not implemented on darwin")
|
||||
}
|
||||
332
vendor/golang.org/x/exp/shiny/driver/gldriver/cocoa.m
generated
vendored
Normal file
332
vendor/golang.org/x/exp/shiny/driver/gldriver/cocoa.m
generated
vendored
Normal file
@@ -0,0 +1,332 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build darwin
|
||||
// +build !ios
|
||||
|
||||
#include "_cgo_export.h"
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <OpenGL/gl3.h>
|
||||
|
||||
// The variables did not exist on older OS X releases,
|
||||
// we use the old variables deprecated on macOS to define them.
|
||||
#if __MAC_OS_X_VERSION_MAX_ALLOWED < 101200
|
||||
enum
|
||||
{
|
||||
NSEventTypeScrollWheel = NSScrollWheel,
|
||||
NSEventTypeKeyDown = NSKeyDown
|
||||
};
|
||||
enum
|
||||
{
|
||||
NSWindowStyleMaskTitled = NSTitledWindowMask,
|
||||
NSWindowStyleMaskResizable = NSResizableWindowMask,
|
||||
NSWindowStyleMaskMiniaturizable = NSMiniaturizableWindowMask,
|
||||
NSWindowStyleMaskClosable = NSClosableWindowMask
|
||||
};
|
||||
#endif
|
||||
|
||||
void makeCurrentContext(uintptr_t context) {
|
||||
NSOpenGLContext* ctx = (NSOpenGLContext*)context;
|
||||
[ctx makeCurrentContext];
|
||||
}
|
||||
|
||||
void flushContext(uintptr_t context) {
|
||||
NSOpenGLContext* ctx = (NSOpenGLContext*)context;
|
||||
[ctx flushBuffer];
|
||||
}
|
||||
|
||||
uint64 threadID() {
|
||||
uint64 id;
|
||||
if (pthread_threadid_np(pthread_self(), &id)) {
|
||||
abort();
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
@interface ScreenGLView : NSOpenGLView<NSWindowDelegate>
|
||||
{
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation ScreenGLView
|
||||
- (void)prepareOpenGL {
|
||||
[super prepareOpenGL];
|
||||
|
||||
[self setWantsBestResolutionOpenGLSurface:YES];
|
||||
GLint swapInt = 1;
|
||||
NSOpenGLContext *ctx = [self openGLContext];
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
[ctx setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
// Using attribute arrays in OpenGL 3.3 requires the use of a VBA.
|
||||
// But VBAs don't exist in ES 2. So we bind a default one.
|
||||
GLuint vba;
|
||||
glGenVertexArrays(1, &vba);
|
||||
glBindVertexArray(vba);
|
||||
|
||||
preparedOpenGL((GoUintptr)self, (GoUintptr)ctx, (GoUintptr)vba);
|
||||
}
|
||||
|
||||
- (void)callSetGeom {
|
||||
// Calculate screen PPI.
|
||||
//
|
||||
// Note that the backingScaleFactor converts from logical
|
||||
// pixels to actual pixels, but both of these units vary
|
||||
// independently from real world size. E.g.
|
||||
//
|
||||
// 13" Retina Macbook Pro, 2560x1600, 227ppi, backingScaleFactor=2, scale=3.15
|
||||
// 15" Retina Macbook Pro, 2880x1800, 220ppi, backingScaleFactor=2, scale=3.06
|
||||
// 27" iMac, 2560x1440, 109ppi, backingScaleFactor=1, scale=1.51
|
||||
// 27" Retina iMac, 5120x2880, 218ppi, backingScaleFactor=2, scale=3.03
|
||||
NSScreen *screen = self.window.screen;
|
||||
double screenPixW = [screen frame].size.width * [screen backingScaleFactor];
|
||||
|
||||
CGDirectDisplayID display = (CGDirectDisplayID)[[[screen deviceDescription] valueForKey:@"NSScreenNumber"] intValue];
|
||||
CGSize screenSizeMM = CGDisplayScreenSize(display); // in millimeters
|
||||
float ppi = 25.4 * screenPixW / screenSizeMM.width;
|
||||
float pixelsPerPt = ppi/72.0;
|
||||
|
||||
// The width and height reported to the geom package are the
|
||||
// bounds of the OpenGL view. Several steps are necessary.
|
||||
// First, [self bounds] gives us the number of logical pixels
|
||||
// in the view. Multiplying this by the backingScaleFactor
|
||||
// gives us the number of actual pixels.
|
||||
NSRect r = [self bounds];
|
||||
int w = r.size.width * [screen backingScaleFactor];
|
||||
int h = r.size.height * [screen backingScaleFactor];
|
||||
|
||||
setGeom((GoUintptr)self, pixelsPerPt, w, h);
|
||||
}
|
||||
|
||||
- (void)reshape {
|
||||
[super reshape];
|
||||
[self callSetGeom];
|
||||
}
|
||||
|
||||
- (void)drawRect:(NSRect)theRect {
|
||||
// Called during resize. Do an extra draw if we are visible.
|
||||
// This gets rid of flicker when resizing.
|
||||
drawgl((GoUintptr)self);
|
||||
}
|
||||
|
||||
- (void)mouseEventNS:(NSEvent *)theEvent {
|
||||
NSPoint p = [theEvent locationInWindow];
|
||||
double h = self.frame.size.height;
|
||||
|
||||
// Both h and p are measured in Cocoa pixels, which are a fraction of
|
||||
// physical pixels, so we multiply by backingScaleFactor.
|
||||
double scale = [self.window.screen backingScaleFactor];
|
||||
|
||||
double x = p.x * scale;
|
||||
double y = (h - p.y) * scale - 1; // flip origin from bottom-left to top-left.
|
||||
|
||||
double dx, dy;
|
||||
if (theEvent.type == NSEventTypeScrollWheel) {
|
||||
dx = theEvent.scrollingDeltaX;
|
||||
dy = theEvent.scrollingDeltaY;
|
||||
}
|
||||
|
||||
mouseEvent((GoUintptr)self, x, y, dx, dy, theEvent.type, theEvent.buttonNumber, theEvent.modifierFlags);
|
||||
}
|
||||
|
||||
- (void)mouseMoved:(NSEvent *)theEvent { [self mouseEventNS:theEvent]; }
|
||||
- (void)mouseDown:(NSEvent *)theEvent { [self mouseEventNS:theEvent]; }
|
||||
- (void)mouseUp:(NSEvent *)theEvent { [self mouseEventNS:theEvent]; }
|
||||
- (void)mouseDragged:(NSEvent *)theEvent { [self mouseEventNS:theEvent]; }
|
||||
- (void)rightMouseDown:(NSEvent *)theEvent { [self mouseEventNS:theEvent]; }
|
||||
- (void)rightMouseUp:(NSEvent *)theEvent { [self mouseEventNS:theEvent]; }
|
||||
- (void)rightMouseDragged:(NSEvent *)theEvent { [self mouseEventNS:theEvent]; }
|
||||
- (void)otherMouseDown:(NSEvent *)theEvent { [self mouseEventNS:theEvent]; }
|
||||
- (void)otherMouseUp:(NSEvent *)theEvent { [self mouseEventNS:theEvent]; }
|
||||
- (void)otherMouseDragged:(NSEvent *)theEvent { [self mouseEventNS:theEvent]; }
|
||||
- (void)scrollWheel:(NSEvent *)theEvent { [self mouseEventNS:theEvent]; }
|
||||
|
||||
// raw modifier key presses
|
||||
- (void)flagsChanged:(NSEvent *)theEvent {
|
||||
flagEvent((GoUintptr)self, theEvent.modifierFlags);
|
||||
}
|
||||
|
||||
// overrides special handling of escape and tab
|
||||
- (BOOL)performKeyEquivalent:(NSEvent *)theEvent {
|
||||
[self key:theEvent];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)keyDown:(NSEvent *)theEvent { [self key:theEvent]; }
|
||||
- (void)keyUp:(NSEvent *)theEvent { [self key:theEvent]; }
|
||||
|
||||
- (void)key:(NSEvent *)theEvent {
|
||||
NSRange range = [theEvent.characters rangeOfComposedCharacterSequenceAtIndex:0];
|
||||
|
||||
uint8_t buf[4] = {0, 0, 0, 0};
|
||||
if (![theEvent.characters getBytes:buf
|
||||
maxLength:4
|
||||
usedLength:nil
|
||||
encoding:NSUTF32LittleEndianStringEncoding
|
||||
options:NSStringEncodingConversionAllowLossy
|
||||
range:range
|
||||
remainingRange:nil]) {
|
||||
NSLog(@"failed to read key event %@", theEvent);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t rune = (uint32_t)buf[0]<<0 | (uint32_t)buf[1]<<8 | (uint32_t)buf[2]<<16 | (uint32_t)buf[3]<<24;
|
||||
|
||||
uint8_t direction;
|
||||
if ([theEvent isARepeat]) {
|
||||
direction = 0;
|
||||
} else if (theEvent.type == NSEventTypeKeyDown) {
|
||||
direction = 1;
|
||||
} else {
|
||||
direction = 2;
|
||||
}
|
||||
keyEvent((GoUintptr)self, (int32_t)rune, direction, theEvent.keyCode, theEvent.modifierFlags);
|
||||
}
|
||||
|
||||
- (void)windowDidChangeScreenProfile:(NSNotification *)notification {
|
||||
[self callSetGeom];
|
||||
}
|
||||
|
||||
// TODO: catch windowDidMiniaturize?
|
||||
|
||||
- (void)windowDidExpose:(NSNotification *)notification {
|
||||
lifecycleVisible((GoUintptr)self, true);
|
||||
}
|
||||
|
||||
- (void)windowDidBecomeKey:(NSNotification *)notification {
|
||||
lifecycleFocused((GoUintptr)self, true);
|
||||
}
|
||||
|
||||
- (void)windowDidResignKey:(NSNotification *)notification {
|
||||
lifecycleFocused((GoUintptr)self, false);
|
||||
if ([NSApp isHidden]) {
|
||||
lifecycleVisible((GoUintptr)self, false);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)windowWillClose:(NSNotification *)notification {
|
||||
// TODO: is this right? Closing a window via the top-left red button
|
||||
// seems to return early without ever calling windowClosing.
|
||||
if (self.window.nextResponder == NULL) {
|
||||
return; // already called close
|
||||
}
|
||||
|
||||
windowClosing((GoUintptr)self);
|
||||
[self.window.nextResponder release];
|
||||
self.window.nextResponder = NULL;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface AppDelegate : NSObject<NSApplicationDelegate>
|
||||
{
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation AppDelegate
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
|
||||
driverStarted();
|
||||
[[NSRunningApplication currentApplication] activateWithOptions:NSApplicationActivateAllWindows];
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(NSNotification *)aNotification {
|
||||
lifecycleDeadAll();
|
||||
}
|
||||
|
||||
- (void)applicationWillHide:(NSNotification *)aNotification {
|
||||
lifecycleHideAll();
|
||||
}
|
||||
@end
|
||||
|
||||
uintptr_t doNewWindow(int width, int height, char* title) {
|
||||
NSScreen *screen = [NSScreen mainScreen];
|
||||
double w = (double)width / [screen backingScaleFactor];
|
||||
double h = (double)height / [screen backingScaleFactor];
|
||||
__block ScreenGLView* view = NULL;
|
||||
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
id menuBar = [NSMenu new];
|
||||
id menuItem = [NSMenuItem new];
|
||||
[menuBar addItem:menuItem];
|
||||
[NSApp setMainMenu:menuBar];
|
||||
|
||||
id menu = [NSMenu new];
|
||||
NSString* name = [[NSString alloc] initWithUTF8String:title];
|
||||
|
||||
id hideMenuItem = [[NSMenuItem alloc] initWithTitle:@"Hide"
|
||||
action:@selector(hide:) keyEquivalent:@"h"];
|
||||
[menu addItem:hideMenuItem];
|
||||
|
||||
id quitMenuItem = [[NSMenuItem alloc] initWithTitle:@"Quit"
|
||||
action:@selector(terminate:) keyEquivalent:@"q"];
|
||||
[menu addItem:quitMenuItem];
|
||||
[menuItem setSubmenu:menu];
|
||||
|
||||
NSRect rect = NSMakeRect(0, 0, w, h);
|
||||
|
||||
NSWindow* window = [[NSWindow alloc] initWithContentRect:rect
|
||||
styleMask:NSWindowStyleMaskTitled
|
||||
backing:NSBackingStoreBuffered
|
||||
defer:NO];
|
||||
window.styleMask |= NSWindowStyleMaskResizable;
|
||||
window.styleMask |= NSWindowStyleMaskMiniaturizable;
|
||||
window.styleMask |= NSWindowStyleMaskClosable;
|
||||
window.title = name;
|
||||
window.displaysWhenScreenProfileChanges = YES;
|
||||
[window cascadeTopLeftFromPoint:NSMakePoint(20,20)];
|
||||
[window setAcceptsMouseMovedEvents:YES];
|
||||
|
||||
NSOpenGLPixelFormatAttribute attr[] = {
|
||||
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
|
||||
NSOpenGLPFAColorSize, 24,
|
||||
NSOpenGLPFAAlphaSize, 8,
|
||||
NSOpenGLPFADepthSize, 16,
|
||||
NSOpenGLPFADoubleBuffer,
|
||||
NSOpenGLPFAAllowOfflineRenderers,
|
||||
0
|
||||
};
|
||||
id pixFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
|
||||
view = [[ScreenGLView alloc] initWithFrame:rect pixelFormat:pixFormat];
|
||||
[window setContentView:view];
|
||||
[window setDelegate:view];
|
||||
[window makeFirstResponder:view];
|
||||
});
|
||||
|
||||
return (uintptr_t)view;
|
||||
}
|
||||
|
||||
void doShowWindow(uintptr_t viewID) {
|
||||
ScreenGLView* view = (ScreenGLView*)viewID;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[view.window makeKeyAndOrderFront:view.window];
|
||||
});
|
||||
}
|
||||
|
||||
void doCloseWindow(uintptr_t viewID) {
|
||||
ScreenGLView* view = (ScreenGLView*)viewID;
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
[view.window performClose:view];
|
||||
});
|
||||
}
|
||||
|
||||
void startDriver() {
|
||||
[NSAutoreleasePool new];
|
||||
[NSApplication sharedApplication];
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
AppDelegate* delegate = [[AppDelegate alloc] init];
|
||||
[NSApp setDelegate:delegate];
|
||||
[NSApp run];
|
||||
}
|
||||
|
||||
void stopDriver() {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[NSApp terminate:nil];
|
||||
});
|
||||
}
|
||||
37
vendor/golang.org/x/exp/shiny/driver/gldriver/context.go
generated
vendored
Normal file
37
vendor/golang.org/x/exp/shiny/driver/gldriver/context.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !android
|
||||
|
||||
package gldriver
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/mobile/gl"
|
||||
)
|
||||
|
||||
// NewContext creates an OpenGL ES context with a dedicated processing thread.
|
||||
func NewContext() (gl.Context, error) {
|
||||
glctx, worker := gl.NewContext()
|
||||
|
||||
errCh := make(chan error)
|
||||
workAvailable := worker.WorkAvailable()
|
||||
go func() {
|
||||
runtime.LockOSThread()
|
||||
err := surfaceCreate()
|
||||
errCh <- err
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for range workAvailable {
|
||||
worker.DoWork()
|
||||
}
|
||||
}()
|
||||
if err := <-errCh; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return glctx, nil
|
||||
}
|
||||
106
vendor/golang.org/x/exp/shiny/driver/gldriver/egl.go
generated
vendored
Normal file
106
vendor/golang.org/x/exp/shiny/driver/gldriver/egl.go
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gldriver
|
||||
|
||||
// These constants match the values found in the EGL 1.4 headers,
|
||||
// egl.h, eglext.h, and eglplatform.h.
|
||||
const (
|
||||
_EGL_DONT_CARE = -1
|
||||
|
||||
_EGL_NO_SURFACE = 0
|
||||
_EGL_NO_CONTEXT = 0
|
||||
_EGL_NO_DISPLAY = 0
|
||||
|
||||
_EGL_OPENGL_ES2_BIT = 0x04 // EGL_RENDERABLE_TYPE mask
|
||||
_EGL_WINDOW_BIT = 0x04 // EGL_SURFACE_TYPE mask
|
||||
|
||||
_EGL_OPENGL_ES_API = 0x30A0
|
||||
_EGL_RENDERABLE_TYPE = 0x3040
|
||||
_EGL_SURFACE_TYPE = 0x3033
|
||||
_EGL_BUFFER_SIZE = 0x3020
|
||||
_EGL_ALPHA_SIZE = 0x3021
|
||||
_EGL_BLUE_SIZE = 0x3022
|
||||
_EGL_GREEN_SIZE = 0x3023
|
||||
_EGL_RED_SIZE = 0x3024
|
||||
_EGL_DEPTH_SIZE = 0x3025
|
||||
_EGL_STENCIL_SIZE = 0x3026
|
||||
_EGL_SAMPLE_BUFFERS = 0x3032
|
||||
_EGL_CONFIG_CAVEAT = 0x3027
|
||||
_EGL_NONE = 0x3038
|
||||
|
||||
_EGL_CONTEXT_CLIENT_VERSION = 0x3098
|
||||
)
|
||||
|
||||
// ANGLE specific options found in eglext.h
|
||||
const (
|
||||
_EGL_PLATFORM_ANGLE_ANGLE = 0x3202
|
||||
_EGL_PLATFORM_ANGLE_TYPE_ANGLE = 0x3203
|
||||
_EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE = 0x3204
|
||||
_EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE = 0x3205
|
||||
_EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE = 0x3206
|
||||
|
||||
_EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE = 0x3207
|
||||
_EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE = 0x3208
|
||||
_EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE = 0x3209
|
||||
_EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE = 0x320A
|
||||
_EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE = 0x320B
|
||||
|
||||
_EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE = 0x320D
|
||||
_EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE = 0x320E
|
||||
)
|
||||
|
||||
const (
|
||||
_EGL_SUCCESS = 0x3000
|
||||
_EGL_NOT_INITIALIZED = 0x3001
|
||||
_EGL_BAD_ACCESS = 0x3002
|
||||
_EGL_BAD_ALLOC = 0x3003
|
||||
_EGL_BAD_ATTRIBUTE = 0x3004
|
||||
_EGL_BAD_CONFIG = 0x3005
|
||||
_EGL_BAD_CONTEXT = 0x3006
|
||||
_EGL_BAD_CURRENT_SURFACE = 0x3007
|
||||
_EGL_BAD_DISPLAY = 0x3008
|
||||
_EGL_BAD_MATCH = 0x3009
|
||||
_EGL_BAD_NATIVE_PIXMAP = 0x300A
|
||||
_EGL_BAD_NATIVE_WINDOW = 0x300B
|
||||
_EGL_BAD_PARAMETER = 0x300C
|
||||
_EGL_BAD_SURFACE = 0x300D
|
||||
_EGL_CONTEXT_LOST = 0x300E
|
||||
)
|
||||
|
||||
func eglErrString(errno uintptr) string {
|
||||
switch errno {
|
||||
case _EGL_SUCCESS:
|
||||
return "EGL_SUCCESS"
|
||||
case _EGL_NOT_INITIALIZED:
|
||||
return "EGL_NOT_INITIALIZED"
|
||||
case _EGL_BAD_ACCESS:
|
||||
return "EGL_BAD_ACCESS"
|
||||
case _EGL_BAD_ALLOC:
|
||||
return "EGL_BAD_ALLOC"
|
||||
case _EGL_BAD_ATTRIBUTE:
|
||||
return "EGL_BAD_ATTRIBUTE"
|
||||
case _EGL_BAD_CONFIG:
|
||||
return "EGL_BAD_CONFIG"
|
||||
case _EGL_BAD_CONTEXT:
|
||||
return "EGL_BAD_CONTEXT"
|
||||
case _EGL_BAD_CURRENT_SURFACE:
|
||||
return "EGL_BAD_CURRENT_SURFACE"
|
||||
case _EGL_BAD_DISPLAY:
|
||||
return "EGL_BAD_DISPLAY"
|
||||
case _EGL_BAD_MATCH:
|
||||
return "EGL_BAD_MATCH"
|
||||
case _EGL_BAD_NATIVE_PIXMAP:
|
||||
return "EGL_BAD_NATIVE_PIXMAP"
|
||||
case _EGL_BAD_NATIVE_WINDOW:
|
||||
return "EGL_BAD_NATIVE_WINDOW"
|
||||
case _EGL_BAD_PARAMETER:
|
||||
return "EGL_BAD_PARAMETER"
|
||||
case _EGL_BAD_SURFACE:
|
||||
return "EGL_BAD_SURFACE"
|
||||
case _EGL_CONTEXT_LOST:
|
||||
return "EGL_CONTEXT_LOST"
|
||||
}
|
||||
return "EGL: unknown error"
|
||||
}
|
||||
133
vendor/golang.org/x/exp/shiny/driver/gldriver/gldriver.go
generated
vendored
Normal file
133
vendor/golang.org/x/exp/shiny/driver/gldriver/gldriver.go
generated
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package gldriver provides an OpenGL driver for accessing a screen.
|
||||
package gldriver // import "golang.org/x/exp/shiny/driver/gldriver"
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"golang.org/x/exp/shiny/driver/internal/errscreen"
|
||||
"golang.org/x/exp/shiny/screen"
|
||||
"golang.org/x/image/math/f64"
|
||||
"golang.org/x/mobile/gl"
|
||||
)
|
||||
|
||||
// Main is called by the program's main function to run the graphical
|
||||
// application.
|
||||
//
|
||||
// It calls f on the Screen, possibly in a separate goroutine, as some OS-
|
||||
// specific libraries require being on 'the main thread'. It returns when f
|
||||
// returns.
|
||||
func Main(f func(screen.Screen)) {
|
||||
if err := main(f); err != nil {
|
||||
f(errscreen.Stub(err))
|
||||
}
|
||||
}
|
||||
|
||||
func mul(a, b f64.Aff3) f64.Aff3 {
|
||||
return f64.Aff3{
|
||||
a[0]*b[0] + a[1]*b[3],
|
||||
a[0]*b[1] + a[1]*b[4],
|
||||
a[0]*b[2] + a[1]*b[5] + a[2],
|
||||
|
||||
a[3]*b[0] + a[4]*b[3],
|
||||
a[3]*b[1] + a[4]*b[4],
|
||||
a[3]*b[2] + a[4]*b[5] + a[5],
|
||||
}
|
||||
}
|
||||
|
||||
// writeAff3 must only be called while holding windowImpl.glctxMu.
|
||||
func writeAff3(glctx gl.Context, u gl.Uniform, a f64.Aff3) {
|
||||
var m [9]float32
|
||||
m[0*3+0] = float32(a[0*3+0])
|
||||
m[0*3+1] = float32(a[1*3+0])
|
||||
m[0*3+2] = 0
|
||||
m[1*3+0] = float32(a[0*3+1])
|
||||
m[1*3+1] = float32(a[1*3+1])
|
||||
m[1*3+2] = 0
|
||||
m[2*3+0] = float32(a[0*3+2])
|
||||
m[2*3+1] = float32(a[1*3+2])
|
||||
m[2*3+2] = 1
|
||||
glctx.UniformMatrix3fv(u, m[:])
|
||||
}
|
||||
|
||||
// f32Bytes returns the byte representation of float32 values in the given byte
|
||||
// order. byteOrder must be either binary.BigEndian or binary.LittleEndian.
|
||||
func f32Bytes(byteOrder binary.ByteOrder, values ...float32) []byte {
|
||||
le := false
|
||||
switch byteOrder {
|
||||
case binary.BigEndian:
|
||||
case binary.LittleEndian:
|
||||
le = true
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid byte order %v", byteOrder))
|
||||
}
|
||||
|
||||
b := make([]byte, 4*len(values))
|
||||
for i, v := range values {
|
||||
u := math.Float32bits(v)
|
||||
if le {
|
||||
b[4*i+0] = byte(u >> 0)
|
||||
b[4*i+1] = byte(u >> 8)
|
||||
b[4*i+2] = byte(u >> 16)
|
||||
b[4*i+3] = byte(u >> 24)
|
||||
} else {
|
||||
b[4*i+0] = byte(u >> 24)
|
||||
b[4*i+1] = byte(u >> 16)
|
||||
b[4*i+2] = byte(u >> 8)
|
||||
b[4*i+3] = byte(u >> 0)
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// compileProgram must only be called while holding windowImpl.glctxMu.
|
||||
func compileProgram(glctx gl.Context, vSrc, fSrc string) (gl.Program, error) {
|
||||
program := glctx.CreateProgram()
|
||||
if program.Value == 0 {
|
||||
return gl.Program{}, fmt.Errorf("gldriver: no programs available")
|
||||
}
|
||||
|
||||
vertexShader, err := compileShader(glctx, gl.VERTEX_SHADER, vSrc)
|
||||
if err != nil {
|
||||
return gl.Program{}, err
|
||||
}
|
||||
fragmentShader, err := compileShader(glctx, gl.FRAGMENT_SHADER, fSrc)
|
||||
if err != nil {
|
||||
glctx.DeleteShader(vertexShader)
|
||||
return gl.Program{}, err
|
||||
}
|
||||
|
||||
glctx.AttachShader(program, vertexShader)
|
||||
glctx.AttachShader(program, fragmentShader)
|
||||
glctx.LinkProgram(program)
|
||||
|
||||
// Flag shaders for deletion when program is unlinked.
|
||||
glctx.DeleteShader(vertexShader)
|
||||
glctx.DeleteShader(fragmentShader)
|
||||
|
||||
if glctx.GetProgrami(program, gl.LINK_STATUS) == 0 {
|
||||
defer glctx.DeleteProgram(program)
|
||||
return gl.Program{}, fmt.Errorf("gldriver: program compile: %s", glctx.GetProgramInfoLog(program))
|
||||
}
|
||||
return program, nil
|
||||
}
|
||||
|
||||
// compileShader must only be called while holding windowImpl.glctxMu.
|
||||
func compileShader(glctx gl.Context, shaderType gl.Enum, src string) (gl.Shader, error) {
|
||||
shader := glctx.CreateShader(shaderType)
|
||||
if shader.Value == 0 {
|
||||
return gl.Shader{}, fmt.Errorf("gldriver: could not create shader (type %v)", shaderType)
|
||||
}
|
||||
glctx.ShaderSource(shader, src)
|
||||
glctx.CompileShader(shader)
|
||||
if glctx.GetShaderi(shader, gl.COMPILE_STATUS) == 0 {
|
||||
defer glctx.DeleteShader(shader)
|
||||
return gl.Shader{}, fmt.Errorf("gldriver: shader compile: %s", glctx.GetShaderInfoLog(shader))
|
||||
}
|
||||
return shader, nil
|
||||
}
|
||||
29
vendor/golang.org/x/exp/shiny/driver/gldriver/other.go
generated
vendored
Normal file
29
vendor/golang.org/x/exp/shiny/driver/gldriver/other.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (!darwin || ios || !cgo) && (!linux || android || !cgo) && (!openbsd || !cgo) && !windows
|
||||
|
||||
package gldriver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/exp/shiny/screen"
|
||||
)
|
||||
|
||||
const useLifecycler = true
|
||||
const handleSizeEventsAtChannelReceive = true
|
||||
|
||||
var errUnsupported = fmt.Errorf("gldriver: unsupported GOOS/GOARCH %s/%s or cgo not enabled", runtime.GOOS, runtime.GOARCH)
|
||||
|
||||
func newWindow(opts *screen.NewWindowOptions) (uintptr, error) { return 0, errUnsupported }
|
||||
|
||||
func initWindow(id *windowImpl) {}
|
||||
func showWindow(id *windowImpl) {}
|
||||
func closeWindow(id uintptr) {}
|
||||
func drawLoop(w *windowImpl) {}
|
||||
|
||||
func surfaceCreate() error { return errUnsupported }
|
||||
func main(f func(screen.Screen)) error { return errUnsupported }
|
||||
149
vendor/golang.org/x/exp/shiny/driver/gldriver/screen.go
generated
vendored
Normal file
149
vendor/golang.org/x/exp/shiny/driver/gldriver/screen.go
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gldriver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/exp/shiny/screen"
|
||||
"golang.org/x/mobile/gl"
|
||||
)
|
||||
|
||||
var theScreen = &screenImpl{
|
||||
windows: make(map[uintptr]*windowImpl),
|
||||
}
|
||||
|
||||
type screenImpl struct {
|
||||
texture struct {
|
||||
program gl.Program
|
||||
pos gl.Attrib
|
||||
mvp gl.Uniform
|
||||
uvp gl.Uniform
|
||||
inUV gl.Attrib
|
||||
sample gl.Uniform
|
||||
quad gl.Buffer
|
||||
}
|
||||
fill struct {
|
||||
program gl.Program
|
||||
pos gl.Attrib
|
||||
mvp gl.Uniform
|
||||
color gl.Uniform
|
||||
quad gl.Buffer
|
||||
}
|
||||
|
||||
mu sync.Mutex
|
||||
windows map[uintptr]*windowImpl
|
||||
}
|
||||
|
||||
func (s *screenImpl) NewBuffer(size image.Point) (retBuf screen.Buffer, retErr error) {
|
||||
m := image.NewRGBA(image.Rectangle{Max: size})
|
||||
return &bufferImpl{
|
||||
buf: m.Pix,
|
||||
rgba: *m,
|
||||
size: size,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *screenImpl) NewTexture(size image.Point) (screen.Texture, error) {
|
||||
// TODO: can we compile these programs eagerly instead of lazily?
|
||||
|
||||
// Find a GL context for this texture.
|
||||
// TODO: this might be correct. Some GL objects can be shared
|
||||
// across contexts. But this needs a review of the spec to make
|
||||
// sure it's correct, and some testing would be nice.
|
||||
var w *windowImpl
|
||||
|
||||
s.mu.Lock()
|
||||
for _, window := range s.windows {
|
||||
w = window
|
||||
break
|
||||
}
|
||||
s.mu.Unlock()
|
||||
|
||||
if w == nil {
|
||||
return nil, fmt.Errorf("gldriver: no window available")
|
||||
}
|
||||
|
||||
w.glctxMu.Lock()
|
||||
defer w.glctxMu.Unlock()
|
||||
glctx := w.glctx
|
||||
if glctx == nil {
|
||||
return nil, fmt.Errorf("gldriver: no GL context available")
|
||||
}
|
||||
|
||||
if !glctx.IsProgram(s.texture.program) {
|
||||
p, err := compileProgram(glctx, textureVertexSrc, textureFragmentSrc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.texture.program = p
|
||||
s.texture.pos = glctx.GetAttribLocation(p, "pos")
|
||||
s.texture.mvp = glctx.GetUniformLocation(p, "mvp")
|
||||
s.texture.uvp = glctx.GetUniformLocation(p, "uvp")
|
||||
s.texture.inUV = glctx.GetAttribLocation(p, "inUV")
|
||||
s.texture.sample = glctx.GetUniformLocation(p, "sample")
|
||||
s.texture.quad = glctx.CreateBuffer()
|
||||
|
||||
glctx.BindBuffer(gl.ARRAY_BUFFER, s.texture.quad)
|
||||
glctx.BufferData(gl.ARRAY_BUFFER, quadCoords, gl.STATIC_DRAW)
|
||||
}
|
||||
|
||||
t := &textureImpl{
|
||||
w: w,
|
||||
id: glctx.CreateTexture(),
|
||||
size: size,
|
||||
}
|
||||
|
||||
glctx.BindTexture(gl.TEXTURE_2D, t.id)
|
||||
glctx.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, size.X, size.Y, gl.RGBA, gl.UNSIGNED_BYTE, nil)
|
||||
glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
|
||||
glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
|
||||
glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
|
||||
glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func optsSize(opts *screen.NewWindowOptions) (width, height int) {
|
||||
width, height = 1024, 768
|
||||
if opts != nil {
|
||||
if opts.Width > 0 {
|
||||
width = opts.Width
|
||||
}
|
||||
if opts.Height > 0 {
|
||||
height = opts.Height
|
||||
}
|
||||
}
|
||||
return width, height
|
||||
}
|
||||
|
||||
func (s *screenImpl) NewWindow(opts *screen.NewWindowOptions) (screen.Window, error) {
|
||||
id, err := newWindow(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
w := &windowImpl{
|
||||
s: s,
|
||||
id: id,
|
||||
publish: make(chan struct{}),
|
||||
publishDone: make(chan screen.PublishResult),
|
||||
drawDone: make(chan struct{}),
|
||||
}
|
||||
initWindow(w)
|
||||
|
||||
s.mu.Lock()
|
||||
s.windows[id] = w
|
||||
s.mu.Unlock()
|
||||
|
||||
if useLifecycler {
|
||||
w.lifecycler.SendEvent(w, nil)
|
||||
}
|
||||
|
||||
showWindow(w)
|
||||
|
||||
return w, nil
|
||||
}
|
||||
160
vendor/golang.org/x/exp/shiny/driver/gldriver/texture.go
generated
vendored
Normal file
160
vendor/golang.org/x/exp/shiny/driver/gldriver/texture.go
generated
vendored
Normal file
@@ -0,0 +1,160 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gldriver
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
|
||||
"golang.org/x/exp/shiny/screen"
|
||||
"golang.org/x/mobile/gl"
|
||||
)
|
||||
|
||||
type textureImpl struct {
|
||||
w *windowImpl
|
||||
id gl.Texture
|
||||
fb gl.Framebuffer
|
||||
size image.Point
|
||||
}
|
||||
|
||||
func (t *textureImpl) Size() image.Point { return t.size }
|
||||
func (t *textureImpl) Bounds() image.Rectangle { return image.Rectangle{Max: t.size} }
|
||||
|
||||
func (t *textureImpl) Release() {
|
||||
t.w.glctxMu.Lock()
|
||||
defer t.w.glctxMu.Unlock()
|
||||
|
||||
if t.fb.Value != 0 {
|
||||
t.w.glctx.DeleteFramebuffer(t.fb)
|
||||
t.fb = gl.Framebuffer{}
|
||||
}
|
||||
t.w.glctx.DeleteTexture(t.id)
|
||||
t.id = gl.Texture{}
|
||||
}
|
||||
|
||||
func (t *textureImpl) Upload(dp image.Point, src screen.Buffer, sr image.Rectangle) {
|
||||
buf := src.(*bufferImpl)
|
||||
buf.preUpload()
|
||||
|
||||
// src2dst is added to convert from the src coordinate space to the dst
|
||||
// coordinate space. It is subtracted to convert the other way.
|
||||
src2dst := dp.Sub(sr.Min)
|
||||
|
||||
// Clip to the source.
|
||||
sr = sr.Intersect(buf.Bounds())
|
||||
|
||||
// Clip to the destination.
|
||||
dr := sr.Add(src2dst)
|
||||
dr = dr.Intersect(t.Bounds())
|
||||
if dr.Empty() {
|
||||
return
|
||||
}
|
||||
|
||||
// Bring dr.Min in dst-space back to src-space to get the pixel buffer offset.
|
||||
pix := buf.rgba.Pix[buf.rgba.PixOffset(dr.Min.X-src2dst.X, dr.Min.Y-src2dst.Y):]
|
||||
|
||||
t.w.glctxMu.Lock()
|
||||
defer t.w.glctxMu.Unlock()
|
||||
|
||||
t.w.glctx.BindTexture(gl.TEXTURE_2D, t.id)
|
||||
|
||||
width := dr.Dx()
|
||||
if width*4 == buf.rgba.Stride {
|
||||
t.w.glctx.TexSubImage2D(gl.TEXTURE_2D, 0, dr.Min.X, dr.Min.Y, width, dr.Dy(), gl.RGBA, gl.UNSIGNED_BYTE, pix)
|
||||
return
|
||||
}
|
||||
// TODO: can we use GL_UNPACK_ROW_LENGTH with glPixelStorei for stride in
|
||||
// ES 3.0, instead of uploading the pixels row-by-row?
|
||||
for y, p := dr.Min.Y, 0; y < dr.Max.Y; y++ {
|
||||
t.w.glctx.TexSubImage2D(gl.TEXTURE_2D, 0, dr.Min.X, y, width, 1, gl.RGBA, gl.UNSIGNED_BYTE, pix[p:])
|
||||
p += buf.rgba.Stride
|
||||
}
|
||||
}
|
||||
|
||||
func (t *textureImpl) Fill(dr image.Rectangle, src color.Color, op draw.Op) {
|
||||
minX := float64(dr.Min.X)
|
||||
minY := float64(dr.Min.Y)
|
||||
maxX := float64(dr.Max.X)
|
||||
maxY := float64(dr.Max.Y)
|
||||
mvp := calcMVP(
|
||||
t.size.X, t.size.Y,
|
||||
minX, minY,
|
||||
maxX, minY,
|
||||
minX, maxY,
|
||||
)
|
||||
|
||||
glctx := t.w.glctx
|
||||
|
||||
t.w.glctxMu.Lock()
|
||||
defer t.w.glctxMu.Unlock()
|
||||
|
||||
create := t.fb.Value == 0
|
||||
if create {
|
||||
t.fb = glctx.CreateFramebuffer()
|
||||
}
|
||||
glctx.BindFramebuffer(gl.FRAMEBUFFER, t.fb)
|
||||
if create {
|
||||
glctx.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, t.id, 0)
|
||||
}
|
||||
|
||||
glctx.Viewport(0, 0, t.size.X, t.size.Y)
|
||||
doFill(t.w.s, t.w.glctx, mvp, src, op)
|
||||
|
||||
// We can't restore the GL state (i.e. bind the back buffer, also known as
|
||||
// gl.Framebuffer{Value: 0}) right away, since we don't necessarily know
|
||||
// the right viewport size yet. It is valid to call textureImpl.Fill before
|
||||
// we've gotten our first size.Event. We bind it lazily instead.
|
||||
t.w.backBufferBound = false
|
||||
}
|
||||
|
||||
var quadCoords = f32Bytes(binary.LittleEndian,
|
||||
0, 0, // top left
|
||||
1, 0, // top right
|
||||
0, 1, // bottom left
|
||||
1, 1, // bottom right
|
||||
)
|
||||
|
||||
const textureVertexSrc = `#version 100
|
||||
uniform mat3 mvp;
|
||||
uniform mat3 uvp;
|
||||
attribute vec3 pos;
|
||||
attribute vec2 inUV;
|
||||
varying vec2 uv;
|
||||
void main() {
|
||||
vec3 p = pos;
|
||||
p.z = 1.0;
|
||||
gl_Position = vec4(mvp * p, 1);
|
||||
uv = (uvp * vec3(inUV, 1)).xy;
|
||||
}
|
||||
`
|
||||
|
||||
const textureFragmentSrc = `#version 100
|
||||
precision mediump float;
|
||||
varying vec2 uv;
|
||||
uniform sampler2D sample;
|
||||
void main() {
|
||||
gl_FragColor = texture2D(sample, uv);
|
||||
}
|
||||
`
|
||||
|
||||
const fillVertexSrc = `#version 100
|
||||
uniform mat3 mvp;
|
||||
attribute vec3 pos;
|
||||
void main() {
|
||||
vec3 p = pos;
|
||||
p.z = 1.0;
|
||||
gl_Position = vec4(mvp * p, 1);
|
||||
}
|
||||
`
|
||||
|
||||
const fillFragmentSrc = `#version 100
|
||||
precision mediump float;
|
||||
uniform vec4 color;
|
||||
void main() {
|
||||
gl_FragColor = color;
|
||||
}
|
||||
`
|
||||
357
vendor/golang.org/x/exp/shiny/driver/gldriver/win32.go
generated
vendored
Normal file
357
vendor/golang.org/x/exp/shiny/driver/gldriver/win32.go
generated
vendored
Normal file
@@ -0,0 +1,357 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package gldriver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/exp/shiny/driver/internal/win32"
|
||||
"golang.org/x/exp/shiny/screen"
|
||||
"golang.org/x/mobile/event/key"
|
||||
"golang.org/x/mobile/event/lifecycle"
|
||||
"golang.org/x/mobile/event/mouse"
|
||||
"golang.org/x/mobile/event/paint"
|
||||
"golang.org/x/mobile/event/size"
|
||||
"golang.org/x/mobile/gl"
|
||||
)
|
||||
|
||||
// TODO: change this to true, after manual testing on Win32.
|
||||
const useLifecycler = false
|
||||
|
||||
// TODO: change this to true, after manual testing on Win32.
|
||||
const handleSizeEventsAtChannelReceive = false
|
||||
|
||||
func main(f func(screen.Screen)) error {
|
||||
return win32.Main(func() { f(theScreen) })
|
||||
}
|
||||
|
||||
var (
|
||||
eglGetPlatformDisplayEXT = gl.LibEGL.NewProc("eglGetPlatformDisplayEXT")
|
||||
eglInitialize = gl.LibEGL.NewProc("eglInitialize")
|
||||
eglChooseConfig = gl.LibEGL.NewProc("eglChooseConfig")
|
||||
eglGetError = gl.LibEGL.NewProc("eglGetError")
|
||||
eglBindAPI = gl.LibEGL.NewProc("eglBindAPI")
|
||||
eglCreateWindowSurface = gl.LibEGL.NewProc("eglCreateWindowSurface")
|
||||
eglCreateContext = gl.LibEGL.NewProc("eglCreateContext")
|
||||
eglMakeCurrent = gl.LibEGL.NewProc("eglMakeCurrent")
|
||||
eglSwapInterval = gl.LibEGL.NewProc("eglSwapInterval")
|
||||
eglDestroySurface = gl.LibEGL.NewProc("eglDestroySurface")
|
||||
eglSwapBuffers = gl.LibEGL.NewProc("eglSwapBuffers")
|
||||
)
|
||||
|
||||
type eglConfig uintptr // void*
|
||||
|
||||
type eglInt int32
|
||||
|
||||
var rgb888 = [...]eglInt{
|
||||
_EGL_RENDERABLE_TYPE, _EGL_OPENGL_ES2_BIT,
|
||||
_EGL_SURFACE_TYPE, _EGL_WINDOW_BIT,
|
||||
_EGL_BLUE_SIZE, 8,
|
||||
_EGL_GREEN_SIZE, 8,
|
||||
_EGL_RED_SIZE, 8,
|
||||
_EGL_DEPTH_SIZE, 16,
|
||||
_EGL_STENCIL_SIZE, 8,
|
||||
_EGL_NONE,
|
||||
}
|
||||
|
||||
type ctxWin32 struct {
|
||||
ctx uintptr
|
||||
display uintptr // EGLDisplay
|
||||
surface uintptr // EGLSurface
|
||||
}
|
||||
|
||||
func newWindow(opts *screen.NewWindowOptions) (uintptr, error) {
|
||||
w, err := win32.NewWindow(opts)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uintptr(w), nil
|
||||
}
|
||||
|
||||
func initWindow(w *windowImpl) {
|
||||
w.glctx, w.worker = gl.NewContext()
|
||||
}
|
||||
|
||||
func showWindow(w *windowImpl) {
|
||||
// Show makes an initial call to sizeEvent (via win32.SizeEvent), where
|
||||
// we setup the EGL surface and GL context.
|
||||
win32.Show(syscall.Handle(w.id))
|
||||
}
|
||||
|
||||
func closeWindow(id uintptr) {} // TODO
|
||||
|
||||
func drawLoop(w *windowImpl) {
|
||||
runtime.LockOSThread()
|
||||
|
||||
display := w.ctx.(ctxWin32).display
|
||||
surface := w.ctx.(ctxWin32).surface
|
||||
ctx := w.ctx.(ctxWin32).ctx
|
||||
|
||||
if ret, _, _ := eglMakeCurrent.Call(display, surface, surface, ctx); ret == 0 {
|
||||
panic(fmt.Sprintf("eglMakeCurrent failed: %v", eglErr()))
|
||||
}
|
||||
|
||||
// TODO(crawshaw): exit this goroutine on Release.
|
||||
workAvailable := w.worker.WorkAvailable()
|
||||
for {
|
||||
select {
|
||||
case <-workAvailable:
|
||||
w.worker.DoWork()
|
||||
case <-w.publish:
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case <-workAvailable:
|
||||
w.worker.DoWork()
|
||||
default:
|
||||
break loop
|
||||
}
|
||||
}
|
||||
if ret, _, _ := eglSwapBuffers.Call(display, surface); ret == 0 {
|
||||
panic(fmt.Sprintf("eglSwapBuffers failed: %v", eglErr()))
|
||||
}
|
||||
w.publishDone <- screen.PublishResult{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
win32.SizeEvent = sizeEvent
|
||||
win32.PaintEvent = paintEvent
|
||||
win32.MouseEvent = mouseEvent
|
||||
win32.KeyEvent = keyEvent
|
||||
win32.LifecycleEvent = lifecycleEvent
|
||||
}
|
||||
|
||||
func lifecycleEvent(hwnd syscall.Handle, to lifecycle.Stage) {
|
||||
theScreen.mu.Lock()
|
||||
w := theScreen.windows[uintptr(hwnd)]
|
||||
theScreen.mu.Unlock()
|
||||
|
||||
if w.lifecycleStage == to {
|
||||
return
|
||||
}
|
||||
w.Send(lifecycle.Event{
|
||||
From: w.lifecycleStage,
|
||||
To: to,
|
||||
DrawContext: w.glctx,
|
||||
})
|
||||
w.lifecycleStage = to
|
||||
}
|
||||
|
||||
func mouseEvent(hwnd syscall.Handle, e mouse.Event) {
|
||||
theScreen.mu.Lock()
|
||||
w := theScreen.windows[uintptr(hwnd)]
|
||||
theScreen.mu.Unlock()
|
||||
|
||||
w.Send(e)
|
||||
}
|
||||
|
||||
func keyEvent(hwnd syscall.Handle, e key.Event) {
|
||||
theScreen.mu.Lock()
|
||||
w := theScreen.windows[uintptr(hwnd)]
|
||||
theScreen.mu.Unlock()
|
||||
|
||||
w.Send(e)
|
||||
}
|
||||
|
||||
func paintEvent(hwnd syscall.Handle, e paint.Event) {
|
||||
theScreen.mu.Lock()
|
||||
w := theScreen.windows[uintptr(hwnd)]
|
||||
theScreen.mu.Unlock()
|
||||
|
||||
if w.ctx == nil {
|
||||
// Sometimes a paint event comes in before initial
|
||||
// window size is set. Ignore it.
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: the paint.Event should have External: true.
|
||||
w.Send(paint.Event{})
|
||||
}
|
||||
|
||||
func sizeEvent(hwnd syscall.Handle, e size.Event) {
|
||||
theScreen.mu.Lock()
|
||||
w := theScreen.windows[uintptr(hwnd)]
|
||||
theScreen.mu.Unlock()
|
||||
|
||||
if w.ctx == nil {
|
||||
// This is the initial size event on window creation.
|
||||
// Create an EGL surface and spin up a GL context.
|
||||
if err := createEGLSurface(hwnd, w); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
go drawLoop(w)
|
||||
}
|
||||
|
||||
if !handleSizeEventsAtChannelReceive {
|
||||
w.szMu.Lock()
|
||||
w.sz = e
|
||||
w.szMu.Unlock()
|
||||
}
|
||||
|
||||
w.Send(e)
|
||||
|
||||
if handleSizeEventsAtChannelReceive {
|
||||
return
|
||||
}
|
||||
|
||||
// Screen is dirty, generate a paint event.
|
||||
//
|
||||
// The sizeEvent function is called on the goroutine responsible for
|
||||
// calling the GL worker.DoWork. When compiling with -tags gldebug,
|
||||
// these GL calls are blocking (so we can read the error message), so
|
||||
// to make progress they need to happen on another goroutine.
|
||||
go func() {
|
||||
// TODO: this call to Viewport is not right, but is very hard to
|
||||
// do correctly with our async events channel model. We want
|
||||
// the call to Viewport to be made the instant before the
|
||||
// paint.Event is received.
|
||||
w.glctxMu.Lock()
|
||||
w.glctx.Viewport(0, 0, e.WidthPx, e.HeightPx)
|
||||
w.glctx.ClearColor(0, 0, 0, 1)
|
||||
w.glctx.Clear(gl.COLOR_BUFFER_BIT)
|
||||
w.glctxMu.Unlock()
|
||||
|
||||
w.Send(paint.Event{})
|
||||
}()
|
||||
}
|
||||
|
||||
func eglErr() error {
|
||||
if ret, _, _ := eglGetError.Call(); ret != _EGL_SUCCESS {
|
||||
return errors.New(eglErrString(ret))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createEGLSurface(hwnd syscall.Handle, w *windowImpl) error {
|
||||
var displayAttribPlatforms = [][]eglInt{
|
||||
// Default
|
||||
[]eglInt{
|
||||
_EGL_PLATFORM_ANGLE_TYPE_ANGLE,
|
||||
_EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE,
|
||||
_EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, _EGL_DONT_CARE,
|
||||
_EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, _EGL_DONT_CARE,
|
||||
_EGL_NONE,
|
||||
},
|
||||
// Direct3D 11
|
||||
[]eglInt{
|
||||
_EGL_PLATFORM_ANGLE_TYPE_ANGLE,
|
||||
_EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
|
||||
_EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, _EGL_DONT_CARE,
|
||||
_EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, _EGL_DONT_CARE,
|
||||
_EGL_NONE,
|
||||
},
|
||||
// Direct3D 9
|
||||
[]eglInt{
|
||||
_EGL_PLATFORM_ANGLE_TYPE_ANGLE,
|
||||
_EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE,
|
||||
_EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, _EGL_DONT_CARE,
|
||||
_EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, _EGL_DONT_CARE,
|
||||
_EGL_NONE,
|
||||
},
|
||||
// Direct3D 11 with WARP
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/gg615082.aspx
|
||||
[]eglInt{
|
||||
_EGL_PLATFORM_ANGLE_TYPE_ANGLE,
|
||||
_EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
|
||||
_EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE,
|
||||
_EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE,
|
||||
_EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, _EGL_DONT_CARE,
|
||||
_EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, _EGL_DONT_CARE,
|
||||
_EGL_NONE,
|
||||
},
|
||||
}
|
||||
|
||||
dc, err := win32.GetDC(hwnd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("win32.GetDC failed: %v", err)
|
||||
}
|
||||
|
||||
var display uintptr = _EGL_NO_DISPLAY
|
||||
for i, displayAttrib := range displayAttribPlatforms {
|
||||
lastTry := i == len(displayAttribPlatforms)-1
|
||||
|
||||
display, _, _ = eglGetPlatformDisplayEXT.Call(
|
||||
_EGL_PLATFORM_ANGLE_ANGLE,
|
||||
uintptr(dc),
|
||||
uintptr(unsafe.Pointer(&displayAttrib[0])),
|
||||
)
|
||||
|
||||
if display == _EGL_NO_DISPLAY {
|
||||
if !lastTry {
|
||||
continue
|
||||
}
|
||||
return fmt.Errorf("eglGetPlatformDisplayEXT failed: %v", eglErr())
|
||||
}
|
||||
|
||||
if ret, _, _ := eglInitialize.Call(display, 0, 0); ret == 0 {
|
||||
if !lastTry {
|
||||
continue
|
||||
}
|
||||
return fmt.Errorf("eglInitialize failed: %v", eglErr())
|
||||
}
|
||||
}
|
||||
|
||||
eglBindAPI.Call(_EGL_OPENGL_ES_API)
|
||||
if err := eglErr(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var numConfigs eglInt
|
||||
var config eglConfig
|
||||
ret, _, _ := eglChooseConfig.Call(
|
||||
display,
|
||||
uintptr(unsafe.Pointer(&rgb888[0])),
|
||||
uintptr(unsafe.Pointer(&config)),
|
||||
1,
|
||||
uintptr(unsafe.Pointer(&numConfigs)),
|
||||
)
|
||||
if ret == 0 {
|
||||
return fmt.Errorf("eglChooseConfig failed: %v", eglErr())
|
||||
}
|
||||
if numConfigs <= 0 {
|
||||
return errors.New("eglChooseConfig found no valid config")
|
||||
}
|
||||
|
||||
surface, _, _ := eglCreateWindowSurface.Call(display, uintptr(config), uintptr(hwnd), 0, 0)
|
||||
if surface == _EGL_NO_SURFACE {
|
||||
return fmt.Errorf("eglCreateWindowSurface failed: %v", eglErr())
|
||||
}
|
||||
|
||||
contextAttribs := [...]eglInt{
|
||||
_EGL_CONTEXT_CLIENT_VERSION, 2,
|
||||
_EGL_NONE,
|
||||
}
|
||||
context, _, _ := eglCreateContext.Call(
|
||||
display,
|
||||
uintptr(config),
|
||||
_EGL_NO_CONTEXT,
|
||||
uintptr(unsafe.Pointer(&contextAttribs[0])),
|
||||
)
|
||||
if context == _EGL_NO_CONTEXT {
|
||||
return fmt.Errorf("eglCreateContext failed: %v", eglErr())
|
||||
}
|
||||
|
||||
eglSwapInterval.Call(display, 1)
|
||||
|
||||
w.ctx = ctxWin32{
|
||||
ctx: context,
|
||||
display: display,
|
||||
surface: surface,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func surfaceCreate() error {
|
||||
return errors.New("gldriver: surface creation not implemented on windows")
|
||||
}
|
||||
389
vendor/golang.org/x/exp/shiny/driver/gldriver/window.go
generated
vendored
Normal file
389
vendor/golang.org/x/exp/shiny/driver/gldriver/window.go
generated
vendored
Normal file
@@ -0,0 +1,389 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gldriver
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/exp/shiny/driver/internal/drawer"
|
||||
"golang.org/x/exp/shiny/driver/internal/event"
|
||||
"golang.org/x/exp/shiny/driver/internal/lifecycler"
|
||||
"golang.org/x/exp/shiny/screen"
|
||||
"golang.org/x/image/math/f64"
|
||||
"golang.org/x/mobile/event/lifecycle"
|
||||
"golang.org/x/mobile/event/size"
|
||||
"golang.org/x/mobile/gl"
|
||||
)
|
||||
|
||||
type windowImpl struct {
|
||||
s *screenImpl
|
||||
|
||||
// id is an OS-specific data structure for the window.
|
||||
// - Cocoa: ScreenGLView*
|
||||
// - X11: Window
|
||||
// - Windows: win32.HWND
|
||||
id uintptr
|
||||
|
||||
// ctx is a C data structure for the GL context.
|
||||
// - Cocoa: uintptr holding a NSOpenGLContext*.
|
||||
// - X11: uintptr holding an EGLSurface.
|
||||
// - Windows: ctxWin32
|
||||
ctx interface{}
|
||||
|
||||
lifecycler lifecycler.State
|
||||
// TODO: Delete the field below (and the useLifecycler constant), and use
|
||||
// the field above for cocoa and win32.
|
||||
lifecycleStage lifecycle.Stage // current stage
|
||||
|
||||
event.Deque
|
||||
publish chan struct{}
|
||||
publishDone chan screen.PublishResult
|
||||
drawDone chan struct{}
|
||||
|
||||
// glctxMu is a mutex that enforces the atomicity of methods like
|
||||
// Texture.Upload or Window.Draw that are conceptually one operation
|
||||
// but are implemented by multiple OpenGL calls. OpenGL is a stateful
|
||||
// API, so interleaving OpenGL calls from separate higher-level
|
||||
// operations causes inconsistencies.
|
||||
glctxMu sync.Mutex
|
||||
glctx gl.Context
|
||||
worker gl.Worker
|
||||
// backBufferBound is whether the default Framebuffer, with ID 0, also
|
||||
// known as the back buffer or the window's Framebuffer, is bound and its
|
||||
// viewport is known to equal the window size. It can become false when we
|
||||
// bind to a texture's Framebuffer or when the window size changes.
|
||||
backBufferBound bool
|
||||
|
||||
// szMu protects only sz. If you need to hold both glctxMu and szMu, the
|
||||
// lock ordering is to lock glctxMu first (and unlock it last).
|
||||
szMu sync.Mutex
|
||||
sz size.Event
|
||||
}
|
||||
|
||||
// NextEvent implements the screen.EventDeque interface.
|
||||
func (w *windowImpl) NextEvent() interface{} {
|
||||
e := w.Deque.NextEvent()
|
||||
if handleSizeEventsAtChannelReceive {
|
||||
if sz, ok := e.(size.Event); ok {
|
||||
w.glctxMu.Lock()
|
||||
w.backBufferBound = false
|
||||
w.szMu.Lock()
|
||||
w.sz = sz
|
||||
w.szMu.Unlock()
|
||||
w.glctxMu.Unlock()
|
||||
}
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func (w *windowImpl) Release() {
|
||||
// There are two ways a window can be closed: the Operating System or
|
||||
// Desktop Environment can initiate (e.g. in response to a user clicking a
|
||||
// red button), or the Go app can programatically close the window (by
|
||||
// calling Window.Release).
|
||||
//
|
||||
// When the OS closes a window:
|
||||
// - Cocoa: Obj-C's windowWillClose calls Go's windowClosing.
|
||||
// - X11: the X11 server sends a WM_DELETE_WINDOW message.
|
||||
// - Windows: TODO: implement and document this.
|
||||
//
|
||||
// This should send a lifecycle event (To: StageDead) to the Go app's event
|
||||
// loop, which should respond by calling Window.Release (this method).
|
||||
// Window.Release is where system resources are actually cleaned up.
|
||||
//
|
||||
// When Window.Release is called, the closeWindow call below:
|
||||
// - Cocoa: calls Obj-C's performClose, which emulates the red button
|
||||
// being clicked. (TODO: document how this actually cleans up
|
||||
// resources??)
|
||||
// - X11: calls C's XDestroyWindow.
|
||||
// - Windows: TODO: implement and document this.
|
||||
//
|
||||
// On Cocoa, if these two approaches race, experiments suggest that the
|
||||
// race is won by performClose (which is called serially on the main
|
||||
// thread). Even if that isn't true, the windowWillClose handler is
|
||||
// idempotent.
|
||||
|
||||
theScreen.mu.Lock()
|
||||
delete(theScreen.windows, w.id)
|
||||
theScreen.mu.Unlock()
|
||||
|
||||
closeWindow(w.id)
|
||||
}
|
||||
|
||||
func (w *windowImpl) Upload(dp image.Point, src screen.Buffer, sr image.Rectangle) {
|
||||
originalSRMin := sr.Min
|
||||
sr = sr.Intersect(src.Bounds())
|
||||
if sr.Empty() {
|
||||
return
|
||||
}
|
||||
dp = dp.Add(sr.Min.Sub(originalSRMin))
|
||||
// TODO: keep a texture around for this purpose?
|
||||
t, err := w.s.NewTexture(sr.Size())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
t.Upload(image.Point{}, src, sr)
|
||||
w.Draw(f64.Aff3{
|
||||
1, 0, float64(dp.X),
|
||||
0, 1, float64(dp.Y),
|
||||
}, t, t.Bounds(), draw.Src, nil)
|
||||
t.Release()
|
||||
}
|
||||
|
||||
func useOp(glctx gl.Context, op draw.Op) {
|
||||
if op == draw.Over {
|
||||
glctx.Enable(gl.BLEND)
|
||||
glctx.BlendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)
|
||||
} else {
|
||||
glctx.Disable(gl.BLEND)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *windowImpl) bindBackBuffer() {
|
||||
w.szMu.Lock()
|
||||
sz := w.sz
|
||||
w.szMu.Unlock()
|
||||
|
||||
w.backBufferBound = true
|
||||
w.glctx.BindFramebuffer(gl.FRAMEBUFFER, gl.Framebuffer{Value: 0})
|
||||
w.glctx.Viewport(0, 0, sz.WidthPx, sz.HeightPx)
|
||||
}
|
||||
|
||||
func (w *windowImpl) fill(mvp f64.Aff3, src color.Color, op draw.Op) {
|
||||
w.glctxMu.Lock()
|
||||
defer w.glctxMu.Unlock()
|
||||
|
||||
if !w.backBufferBound {
|
||||
w.bindBackBuffer()
|
||||
}
|
||||
|
||||
doFill(w.s, w.glctx, mvp, src, op)
|
||||
}
|
||||
|
||||
func doFill(s *screenImpl, glctx gl.Context, mvp f64.Aff3, src color.Color, op draw.Op) {
|
||||
useOp(glctx, op)
|
||||
if !glctx.IsProgram(s.fill.program) {
|
||||
p, err := compileProgram(glctx, fillVertexSrc, fillFragmentSrc)
|
||||
if err != nil {
|
||||
// TODO: initialize this somewhere else we can better handle the error.
|
||||
panic(err.Error())
|
||||
}
|
||||
s.fill.program = p
|
||||
s.fill.pos = glctx.GetAttribLocation(p, "pos")
|
||||
s.fill.mvp = glctx.GetUniformLocation(p, "mvp")
|
||||
s.fill.color = glctx.GetUniformLocation(p, "color")
|
||||
s.fill.quad = glctx.CreateBuffer()
|
||||
|
||||
glctx.BindBuffer(gl.ARRAY_BUFFER, s.fill.quad)
|
||||
glctx.BufferData(gl.ARRAY_BUFFER, quadCoords, gl.STATIC_DRAW)
|
||||
}
|
||||
glctx.UseProgram(s.fill.program)
|
||||
|
||||
writeAff3(glctx, s.fill.mvp, mvp)
|
||||
|
||||
r, g, b, a := src.RGBA()
|
||||
glctx.Uniform4f(
|
||||
s.fill.color,
|
||||
float32(r)/65535,
|
||||
float32(g)/65535,
|
||||
float32(b)/65535,
|
||||
float32(a)/65535,
|
||||
)
|
||||
|
||||
glctx.BindBuffer(gl.ARRAY_BUFFER, s.fill.quad)
|
||||
glctx.EnableVertexAttribArray(s.fill.pos)
|
||||
glctx.VertexAttribPointer(s.fill.pos, 2, gl.FLOAT, false, 0, 0)
|
||||
|
||||
glctx.DrawArrays(gl.TRIANGLE_STRIP, 0, 4)
|
||||
|
||||
glctx.DisableVertexAttribArray(s.fill.pos)
|
||||
}
|
||||
|
||||
func (w *windowImpl) Fill(dr image.Rectangle, src color.Color, op draw.Op) {
|
||||
minX := float64(dr.Min.X)
|
||||
minY := float64(dr.Min.Y)
|
||||
maxX := float64(dr.Max.X)
|
||||
maxY := float64(dr.Max.Y)
|
||||
w.fill(w.mvp(
|
||||
minX, minY,
|
||||
maxX, minY,
|
||||
minX, maxY,
|
||||
), src, op)
|
||||
}
|
||||
|
||||
func (w *windowImpl) DrawUniform(src2dst f64.Aff3, src color.Color, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) {
|
||||
minX := float64(sr.Min.X)
|
||||
minY := float64(sr.Min.Y)
|
||||
maxX := float64(sr.Max.X)
|
||||
maxY := float64(sr.Max.Y)
|
||||
w.fill(w.mvp(
|
||||
src2dst[0]*minX+src2dst[1]*minY+src2dst[2],
|
||||
src2dst[3]*minX+src2dst[4]*minY+src2dst[5],
|
||||
src2dst[0]*maxX+src2dst[1]*minY+src2dst[2],
|
||||
src2dst[3]*maxX+src2dst[4]*minY+src2dst[5],
|
||||
src2dst[0]*minX+src2dst[1]*maxY+src2dst[2],
|
||||
src2dst[3]*minX+src2dst[4]*maxY+src2dst[5],
|
||||
), src, op)
|
||||
}
|
||||
|
||||
func (w *windowImpl) Draw(src2dst f64.Aff3, src screen.Texture, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) {
|
||||
t := src.(*textureImpl)
|
||||
sr = sr.Intersect(t.Bounds())
|
||||
if sr.Empty() {
|
||||
return
|
||||
}
|
||||
|
||||
w.glctxMu.Lock()
|
||||
defer w.glctxMu.Unlock()
|
||||
|
||||
if !w.backBufferBound {
|
||||
w.bindBackBuffer()
|
||||
}
|
||||
|
||||
useOp(w.glctx, op)
|
||||
w.glctx.UseProgram(w.s.texture.program)
|
||||
|
||||
// Start with src-space left, top, right and bottom.
|
||||
srcL := float64(sr.Min.X)
|
||||
srcT := float64(sr.Min.Y)
|
||||
srcR := float64(sr.Max.X)
|
||||
srcB := float64(sr.Max.Y)
|
||||
// Transform to dst-space via the src2dst matrix, then to a MVP matrix.
|
||||
writeAff3(w.glctx, w.s.texture.mvp, w.mvp(
|
||||
src2dst[0]*srcL+src2dst[1]*srcT+src2dst[2],
|
||||
src2dst[3]*srcL+src2dst[4]*srcT+src2dst[5],
|
||||
src2dst[0]*srcR+src2dst[1]*srcT+src2dst[2],
|
||||
src2dst[3]*srcR+src2dst[4]*srcT+src2dst[5],
|
||||
src2dst[0]*srcL+src2dst[1]*srcB+src2dst[2],
|
||||
src2dst[3]*srcL+src2dst[4]*srcB+src2dst[5],
|
||||
))
|
||||
|
||||
// OpenGL's fragment shaders' UV coordinates run from (0,0)-(1,1),
|
||||
// unlike vertex shaders' XY coordinates running from (-1,+1)-(+1,-1).
|
||||
//
|
||||
// We are drawing a rectangle PQRS, defined by two of its
|
||||
// corners, onto the entire texture. The two quads may actually
|
||||
// be equal, but in the general case, PQRS can be smaller.
|
||||
//
|
||||
// (0,0) +---------------+ (1,0)
|
||||
// | P +-----+ Q |
|
||||
// | | | |
|
||||
// | S +-----+ R |
|
||||
// (0,1) +---------------+ (1,1)
|
||||
//
|
||||
// The PQRS quad is always axis-aligned. First of all, convert
|
||||
// from pixel space to texture space.
|
||||
tw := float64(t.size.X)
|
||||
th := float64(t.size.Y)
|
||||
px := float64(sr.Min.X-0) / tw
|
||||
py := float64(sr.Min.Y-0) / th
|
||||
qx := float64(sr.Max.X-0) / tw
|
||||
sy := float64(sr.Max.Y-0) / th
|
||||
// Due to axis alignment, qy = py and sx = px.
|
||||
//
|
||||
// The simultaneous equations are:
|
||||
// 0 + 0 + a02 = px
|
||||
// 0 + 0 + a12 = py
|
||||
// a00 + 0 + a02 = qx
|
||||
// a10 + 0 + a12 = qy = py
|
||||
// 0 + a01 + a02 = sx = px
|
||||
// 0 + a11 + a12 = sy
|
||||
writeAff3(w.glctx, w.s.texture.uvp, f64.Aff3{
|
||||
qx - px, 0, px,
|
||||
0, sy - py, py,
|
||||
})
|
||||
|
||||
w.glctx.ActiveTexture(gl.TEXTURE0)
|
||||
w.glctx.BindTexture(gl.TEXTURE_2D, t.id)
|
||||
w.glctx.Uniform1i(w.s.texture.sample, 0)
|
||||
|
||||
w.glctx.BindBuffer(gl.ARRAY_BUFFER, w.s.texture.quad)
|
||||
w.glctx.EnableVertexAttribArray(w.s.texture.pos)
|
||||
w.glctx.VertexAttribPointer(w.s.texture.pos, 2, gl.FLOAT, false, 0, 0)
|
||||
|
||||
w.glctx.BindBuffer(gl.ARRAY_BUFFER, w.s.texture.quad)
|
||||
w.glctx.EnableVertexAttribArray(w.s.texture.inUV)
|
||||
w.glctx.VertexAttribPointer(w.s.texture.inUV, 2, gl.FLOAT, false, 0, 0)
|
||||
|
||||
w.glctx.DrawArrays(gl.TRIANGLE_STRIP, 0, 4)
|
||||
|
||||
w.glctx.DisableVertexAttribArray(w.s.texture.pos)
|
||||
w.glctx.DisableVertexAttribArray(w.s.texture.inUV)
|
||||
}
|
||||
|
||||
func (w *windowImpl) Copy(dp image.Point, src screen.Texture, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) {
|
||||
drawer.Copy(w, dp, src, sr, op, opts)
|
||||
}
|
||||
|
||||
func (w *windowImpl) Scale(dr image.Rectangle, src screen.Texture, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) {
|
||||
drawer.Scale(w, dr, src, sr, op, opts)
|
||||
}
|
||||
|
||||
func (w *windowImpl) mvp(tlx, tly, trx, try, blx, bly float64) f64.Aff3 {
|
||||
w.szMu.Lock()
|
||||
sz := w.sz
|
||||
w.szMu.Unlock()
|
||||
|
||||
return calcMVP(sz.WidthPx, sz.HeightPx, tlx, tly, trx, try, blx, bly)
|
||||
}
|
||||
|
||||
// calcMVP returns the Model View Projection matrix that maps the quadCoords
|
||||
// unit square, (0, 0) to (1, 1), to a quad QV, such that QV in vertex shader
|
||||
// space corresponds to the quad QP in pixel space, where QP is defined by
|
||||
// three of its four corners - the arguments to this function. The three
|
||||
// corners are nominally the top-left, top-right and bottom-left, but there is
|
||||
// no constraint that e.g. tlx < trx.
|
||||
//
|
||||
// In pixel space, the window ranges from (0, 0) to (widthPx, heightPx). The
|
||||
// Y-axis points downwards.
|
||||
//
|
||||
// In vertex shader space, the window ranges from (-1, +1) to (+1, -1), which
|
||||
// is a 2-unit by 2-unit square. The Y-axis points upwards.
|
||||
func calcMVP(widthPx, heightPx int, tlx, tly, trx, try, blx, bly float64) f64.Aff3 {
|
||||
// Convert from pixel coords to vertex shader coords.
|
||||
invHalfWidth := +2 / float64(widthPx)
|
||||
invHalfHeight := -2 / float64(heightPx)
|
||||
tlx = tlx*invHalfWidth - 1
|
||||
tly = tly*invHalfHeight + 1
|
||||
trx = trx*invHalfWidth - 1
|
||||
try = try*invHalfHeight + 1
|
||||
blx = blx*invHalfWidth - 1
|
||||
bly = bly*invHalfHeight + 1
|
||||
|
||||
// The resultant affine matrix:
|
||||
// - maps (0, 0) to (tlx, tly).
|
||||
// - maps (1, 0) to (trx, try).
|
||||
// - maps (0, 1) to (blx, bly).
|
||||
return f64.Aff3{
|
||||
trx - tlx, blx - tlx, tlx,
|
||||
try - tly, bly - tly, tly,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *windowImpl) Publish() screen.PublishResult {
|
||||
// gl.Flush is a lightweight (on modern GL drivers) blocking call
|
||||
// that ensures all GL functions pending in the gl package have
|
||||
// been passed onto the GL driver before the app package attempts
|
||||
// to swap the screen buffer.
|
||||
//
|
||||
// This enforces that the final receive (for this paint cycle) on
|
||||
// gl.WorkAvailable happens before the send on publish.
|
||||
w.glctxMu.Lock()
|
||||
w.glctx.Flush()
|
||||
w.glctxMu.Unlock()
|
||||
|
||||
w.publish <- struct{}{}
|
||||
res := <-w.publishDone
|
||||
|
||||
select {
|
||||
case w.drawDone <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
330
vendor/golang.org/x/exp/shiny/driver/gldriver/x11.c
generated
vendored
Normal file
330
vendor/golang.org/x/exp/shiny/driver/gldriver/x11.c
generated
vendored
Normal file
@@ -0,0 +1,330 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux,!android openbsd
|
||||
|
||||
#include "_cgo_export.h"
|
||||
#include <EGL/egl.h>
|
||||
#include <X11/Xlib.h> // for Atom, Colormap, Display, Window
|
||||
#include <X11/Xutil.h> // for XVisualInfo
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
Atom net_wm_name;
|
||||
Atom utf8_string;
|
||||
Atom wm_delete_window;
|
||||
Atom wm_protocols;
|
||||
Atom wm_take_focus;
|
||||
|
||||
EGLConfig e_config;
|
||||
EGLContext e_ctx;
|
||||
EGLDisplay e_dpy;
|
||||
Colormap x_colormap;
|
||||
Display *x_dpy;
|
||||
XVisualInfo *x_visual_info;
|
||||
Window x_root;
|
||||
|
||||
// TODO: share code with eglErrString
|
||||
char *
|
||||
eglGetErrorStr() {
|
||||
switch (eglGetError()) {
|
||||
case EGL_SUCCESS:
|
||||
return "EGL_SUCCESS";
|
||||
case EGL_NOT_INITIALIZED:
|
||||
return "EGL_NOT_INITIALIZED";
|
||||
case EGL_BAD_ACCESS:
|
||||
return "EGL_BAD_ACCESS";
|
||||
case EGL_BAD_ALLOC:
|
||||
return "EGL_BAD_ALLOC";
|
||||
case EGL_BAD_ATTRIBUTE:
|
||||
return "EGL_BAD_ATTRIBUTE";
|
||||
case EGL_BAD_CONFIG:
|
||||
return "EGL_BAD_CONFIG";
|
||||
case EGL_BAD_CONTEXT:
|
||||
return "EGL_BAD_CONTEXT";
|
||||
case EGL_BAD_CURRENT_SURFACE:
|
||||
return "EGL_BAD_CURRENT_SURFACE";
|
||||
case EGL_BAD_DISPLAY:
|
||||
return "EGL_BAD_DISPLAY";
|
||||
case EGL_BAD_MATCH:
|
||||
return "EGL_BAD_MATCH";
|
||||
case EGL_BAD_NATIVE_PIXMAP:
|
||||
return "EGL_BAD_NATIVE_PIXMAP";
|
||||
case EGL_BAD_NATIVE_WINDOW:
|
||||
return "EGL_BAD_NATIVE_WINDOW";
|
||||
case EGL_BAD_PARAMETER:
|
||||
return "EGL_BAD_PARAMETER";
|
||||
case EGL_BAD_SURFACE:
|
||||
return "EGL_BAD_SURFACE";
|
||||
case EGL_CONTEXT_LOST:
|
||||
return "EGL_CONTEXT_LOST";
|
||||
}
|
||||
return "unknown EGL error";
|
||||
}
|
||||
|
||||
void
|
||||
startDriver() {
|
||||
x_dpy = XOpenDisplay(NULL);
|
||||
if (!x_dpy) {
|
||||
fprintf(stderr, "XOpenDisplay failed\n");
|
||||
exit(1);
|
||||
}
|
||||
e_dpy = eglGetDisplay(x_dpy);
|
||||
if (!e_dpy) {
|
||||
fprintf(stderr, "eglGetDisplay failed: %s\n", eglGetErrorStr());
|
||||
exit(1);
|
||||
}
|
||||
EGLint e_major, e_minor;
|
||||
if (!eglInitialize(e_dpy, &e_major, &e_minor)) {
|
||||
fprintf(stderr, "eglInitialize failed: %s\n", eglGetErrorStr());
|
||||
exit(1);
|
||||
}
|
||||
if (!eglBindAPI(EGL_OPENGL_ES_API)) {
|
||||
fprintf(stderr, "eglBindAPI failed: %s\n", eglGetErrorStr());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static const EGLint attribs[] = {
|
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||||
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||||
EGL_BLUE_SIZE, 8,
|
||||
EGL_GREEN_SIZE, 8,
|
||||
EGL_RED_SIZE, 8,
|
||||
EGL_DEPTH_SIZE, 16,
|
||||
EGL_CONFIG_CAVEAT, EGL_NONE,
|
||||
EGL_NONE
|
||||
};
|
||||
EGLint num_configs;
|
||||
if (!eglChooseConfig(e_dpy, attribs, &e_config, 1, &num_configs)) {
|
||||
fprintf(stderr, "eglChooseConfig failed: %s\n", eglGetErrorStr());
|
||||
exit(1);
|
||||
}
|
||||
EGLint vid;
|
||||
if (!eglGetConfigAttrib(e_dpy, e_config, EGL_NATIVE_VISUAL_ID, &vid)) {
|
||||
fprintf(stderr, "eglGetConfigAttrib failed: %s\n", eglGetErrorStr());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
XVisualInfo visTemplate;
|
||||
visTemplate.visualid = vid;
|
||||
int num_visuals;
|
||||
x_visual_info = XGetVisualInfo(x_dpy, VisualIDMask, &visTemplate, &num_visuals);
|
||||
if (!x_visual_info) {
|
||||
fprintf(stderr, "XGetVisualInfo failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
x_root = RootWindow(x_dpy, DefaultScreen(x_dpy));
|
||||
x_colormap = XCreateColormap(x_dpy, x_root, x_visual_info->visual, AllocNone);
|
||||
if (!x_colormap) {
|
||||
fprintf(stderr, "XCreateColormap failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static const EGLint ctx_attribs[] = {
|
||||
EGL_CONTEXT_CLIENT_VERSION, 3,
|
||||
EGL_NONE
|
||||
};
|
||||
e_ctx = eglCreateContext(e_dpy, e_config, EGL_NO_CONTEXT, ctx_attribs);
|
||||
if (!e_ctx) {
|
||||
fprintf(stderr, "eglCreateContext failed: %s\n", eglGetErrorStr());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
net_wm_name = XInternAtom(x_dpy, "_NET_WM_NAME", False);
|
||||
utf8_string = XInternAtom(x_dpy, "UTF8_STRING", False);
|
||||
wm_delete_window = XInternAtom(x_dpy, "WM_DELETE_WINDOW", False);
|
||||
wm_protocols = XInternAtom(x_dpy, "WM_PROTOCOLS", False);
|
||||
wm_take_focus = XInternAtom(x_dpy, "WM_TAKE_FOCUS", False);
|
||||
|
||||
const int key_lo = 8;
|
||||
const int key_hi = 255;
|
||||
int keysyms_per_keycode;
|
||||
KeySym *keysyms = XGetKeyboardMapping(x_dpy, key_lo, key_hi-key_lo+1, &keysyms_per_keycode);
|
||||
if (keysyms_per_keycode < 2) {
|
||||
fprintf(stderr, "XGetKeyboardMapping returned too few keysyms per keycode: %d\n", keysyms_per_keycode);
|
||||
exit(1);
|
||||
}
|
||||
int k;
|
||||
for (k = key_lo; k <= key_hi; k++) {
|
||||
onKeysym(k,
|
||||
keysyms[(k-key_lo)*keysyms_per_keycode + 0],
|
||||
keysyms[(k-key_lo)*keysyms_per_keycode + 1]);
|
||||
}
|
||||
//TODO: use GetModifierMapping to figure out which modifier is the numlock modifier.
|
||||
}
|
||||
|
||||
void
|
||||
processEvents() {
|
||||
while (XPending(x_dpy)) {
|
||||
XEvent ev;
|
||||
XNextEvent(x_dpy, &ev);
|
||||
switch (ev.type) {
|
||||
case KeyPress:
|
||||
case KeyRelease:
|
||||
onKey(ev.xkey.window, ev.xkey.state, ev.xkey.keycode, ev.type == KeyPress ? 1 : 2);
|
||||
break;
|
||||
case ButtonPress:
|
||||
case ButtonRelease:
|
||||
onMouse(ev.xbutton.window, ev.xbutton.x, ev.xbutton.y, ev.xbutton.state, ev.xbutton.button,
|
||||
ev.type == ButtonPress ? 1 : 2);
|
||||
break;
|
||||
case MotionNotify:
|
||||
onMouse(ev.xmotion.window, ev.xmotion.x, ev.xmotion.y, ev.xmotion.state, 0, 0);
|
||||
break;
|
||||
case FocusIn:
|
||||
case FocusOut:
|
||||
onFocus(ev.xmotion.window, ev.type == FocusIn);
|
||||
break;
|
||||
case Expose:
|
||||
// A non-zero Count means that there are more expose events coming. For
|
||||
// example, a non-rectangular exposure (e.g. from a partially overlapped
|
||||
// window) will result in multiple expose events whose dirty rectangles
|
||||
// combine to define the dirty region. Go's paint events do not provide
|
||||
// dirty regions, so we only pass on the final X11 expose event.
|
||||
if (ev.xexpose.count == 0) {
|
||||
onExpose(ev.xexpose.window);
|
||||
}
|
||||
break;
|
||||
case ConfigureNotify:
|
||||
onConfigure(ev.xconfigure.window, ev.xconfigure.x, ev.xconfigure.y,
|
||||
ev.xconfigure.width, ev.xconfigure.height,
|
||||
DisplayWidth(x_dpy, DefaultScreen(x_dpy)),
|
||||
DisplayWidthMM(x_dpy, DefaultScreen(x_dpy)));
|
||||
break;
|
||||
case ClientMessage:
|
||||
if ((ev.xclient.message_type != wm_protocols) || (ev.xclient.format != 32)) {
|
||||
break;
|
||||
}
|
||||
Atom a = ev.xclient.data.l[0];
|
||||
if (a == wm_delete_window) {
|
||||
onDeleteWindow(ev.xclient.window);
|
||||
} else if (a == wm_take_focus) {
|
||||
XSetInputFocus(x_dpy, ev.xclient.window, RevertToParent, ev.xclient.data.l[1]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
makeCurrent(uintptr_t surface) {
|
||||
EGLSurface surf = (EGLSurface)(surface);
|
||||
if (!eglMakeCurrent(e_dpy, surf, surf, e_ctx)) {
|
||||
fprintf(stderr, "eglMakeCurrent failed: %s\n", eglGetErrorStr());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
swapBuffers(uintptr_t surface) {
|
||||
EGLSurface surf = (EGLSurface)(surface);
|
||||
if (!eglSwapBuffers(e_dpy, surf)) {
|
||||
fprintf(stderr, "eglSwapBuffers failed: %s\n", eglGetErrorStr());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
doCloseWindow(uintptr_t id) {
|
||||
Window win = (Window)(id);
|
||||
XDestroyWindow(x_dpy, win);
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
doNewWindow(int width, int height, char* title, int title_len) {
|
||||
XSetWindowAttributes attr;
|
||||
attr.colormap = x_colormap;
|
||||
attr.event_mask =
|
||||
KeyPressMask |
|
||||
KeyReleaseMask |
|
||||
ButtonPressMask |
|
||||
ButtonReleaseMask |
|
||||
PointerMotionMask |
|
||||
ExposureMask |
|
||||
StructureNotifyMask |
|
||||
FocusChangeMask;
|
||||
|
||||
Window win = XCreateWindow(
|
||||
x_dpy, x_root, 0, 0, width, height, 0, x_visual_info->depth, InputOutput,
|
||||
x_visual_info->visual, CWColormap | CWEventMask, &attr);
|
||||
|
||||
XSizeHints sizehints;
|
||||
sizehints.width = width;
|
||||
sizehints.height = height;
|
||||
sizehints.flags = USSize;
|
||||
XSetNormalHints(x_dpy, win, &sizehints);
|
||||
|
||||
Atom atoms[2];
|
||||
atoms[0] = wm_delete_window;
|
||||
atoms[1] = wm_take_focus;
|
||||
XSetWMProtocols(x_dpy, win, atoms, 2);
|
||||
|
||||
XSetStandardProperties(x_dpy, win, "", "App", None, (char **)NULL, 0, &sizehints);
|
||||
XChangeProperty(x_dpy, win, net_wm_name, utf8_string, 8, PropModeReplace, title, title_len);
|
||||
|
||||
return win;
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
doShowWindow(uintptr_t id) {
|
||||
Window win = (Window)(id);
|
||||
XMapWindow(x_dpy, win);
|
||||
EGLSurface surf = eglCreateWindowSurface(e_dpy, e_config, win, NULL);
|
||||
if (!surf) {
|
||||
fprintf(stderr, "eglCreateWindowSurface failed: %s\n", eglGetErrorStr());
|
||||
exit(1);
|
||||
}
|
||||
return (uintptr_t)(surf);
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
surfaceCreate() {
|
||||
static const EGLint ctx_attribs[] = {
|
||||
EGL_CONTEXT_CLIENT_VERSION, 3,
|
||||
EGL_NONE
|
||||
};
|
||||
EGLContext ctx = eglCreateContext(e_dpy, e_config, EGL_NO_CONTEXT, ctx_attribs);
|
||||
if (!ctx) {
|
||||
fprintf(stderr, "surface eglCreateContext failed: %s\n", eglGetErrorStr());
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const EGLint cfg_attribs[] = {
|
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||||
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
|
||||
EGL_BLUE_SIZE, 8,
|
||||
EGL_GREEN_SIZE, 8,
|
||||
EGL_RED_SIZE, 8,
|
||||
EGL_DEPTH_SIZE, 16,
|
||||
EGL_CONFIG_CAVEAT, EGL_NONE,
|
||||
EGL_NONE
|
||||
};
|
||||
EGLConfig cfg;
|
||||
EGLint num_configs;
|
||||
if (!eglChooseConfig(e_dpy, cfg_attribs, &cfg, 1, &num_configs)) {
|
||||
fprintf(stderr, "gldriver: surface eglChooseConfig failed: %s\n", eglGetErrorStr());
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: use the size of the monitor as a bound for texture size.
|
||||
static const EGLint attribs[] = {
|
||||
EGL_WIDTH, 4096,
|
||||
EGL_HEIGHT, 3072,
|
||||
EGL_NONE
|
||||
};
|
||||
EGLSurface surface = eglCreatePbufferSurface(e_dpy, cfg, attribs);
|
||||
if (!surface) {
|
||||
fprintf(stderr, "gldriver: surface eglCreatePbufferSurface failed: %s\n", eglGetErrorStr());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!eglMakeCurrent(e_dpy, surface, surface, ctx)) {
|
||||
fprintf(stderr, "gldriver: surface eglMakeCurrent failed: %s\n", eglGetErrorStr());
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (uintptr_t)surface;
|
||||
}
|
||||
318
vendor/golang.org/x/exp/shiny/driver/gldriver/x11.go
generated
vendored
Normal file
318
vendor/golang.org/x/exp/shiny/driver/gldriver/x11.go
generated
vendored
Normal file
@@ -0,0 +1,318 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (linux && !android) || openbsd
|
||||
|
||||
package gldriver
|
||||
|
||||
/*
|
||||
#cgo linux LDFLAGS: -lEGL -lGLESv2 -lX11
|
||||
#cgo openbsd LDFLAGS: -L/usr/X11R6/lib/ -lEGL -lGLESv2 -lX11
|
||||
|
||||
#cgo openbsd CFLAGS: -I/usr/X11R6/include/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
char *eglGetErrorStr();
|
||||
void startDriver();
|
||||
void processEvents();
|
||||
void makeCurrent(uintptr_t ctx);
|
||||
void swapBuffers(uintptr_t ctx);
|
||||
void doCloseWindow(uintptr_t id);
|
||||
uintptr_t doNewWindow(int width, int height, char* title, int title_len);
|
||||
uintptr_t doShowWindow(uintptr_t id);
|
||||
uintptr_t surfaceCreate();
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"errors"
|
||||
"runtime"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/exp/shiny/driver/internal/x11key"
|
||||
"golang.org/x/exp/shiny/screen"
|
||||
"golang.org/x/mobile/event/key"
|
||||
"golang.org/x/mobile/event/mouse"
|
||||
"golang.org/x/mobile/event/paint"
|
||||
"golang.org/x/mobile/event/size"
|
||||
"golang.org/x/mobile/geom"
|
||||
"golang.org/x/mobile/gl"
|
||||
)
|
||||
|
||||
const useLifecycler = true
|
||||
|
||||
const handleSizeEventsAtChannelReceive = true
|
||||
|
||||
var theKeysyms x11key.KeysymTable
|
||||
|
||||
func init() {
|
||||
// It might not be necessary, but it probably doesn't hurt to try to make
|
||||
// 'the main thread' be 'the X11 / OpenGL thread'.
|
||||
runtime.LockOSThread()
|
||||
}
|
||||
|
||||
func newWindow(opts *screen.NewWindowOptions) (uintptr, error) {
|
||||
width, height := optsSize(opts)
|
||||
|
||||
title := opts.GetTitle()
|
||||
ctitle := C.CString(title)
|
||||
defer C.free(unsafe.Pointer(ctitle))
|
||||
|
||||
retc := make(chan uintptr)
|
||||
uic <- uiClosure{
|
||||
f: func() uintptr {
|
||||
return uintptr(C.doNewWindow(C.int(width), C.int(height), ctitle, C.int(len(title))))
|
||||
},
|
||||
retc: retc,
|
||||
}
|
||||
return <-retc, nil
|
||||
}
|
||||
|
||||
func initWindow(w *windowImpl) {
|
||||
w.glctx, w.worker = glctx, worker
|
||||
}
|
||||
|
||||
func showWindow(w *windowImpl) {
|
||||
retc := make(chan uintptr)
|
||||
uic <- uiClosure{
|
||||
f: func() uintptr {
|
||||
return uintptr(C.doShowWindow(C.uintptr_t(w.id)))
|
||||
},
|
||||
retc: retc,
|
||||
}
|
||||
w.ctx = <-retc
|
||||
go drawLoop(w)
|
||||
}
|
||||
|
||||
func closeWindow(id uintptr) {
|
||||
uic <- uiClosure{
|
||||
f: func() uintptr {
|
||||
C.doCloseWindow(C.uintptr_t(id))
|
||||
return 0
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func drawLoop(w *windowImpl) {
|
||||
glcontextc <- w.ctx.(uintptr)
|
||||
go func() {
|
||||
for range w.publish {
|
||||
publishc <- w
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
var (
|
||||
glcontextc = make(chan uintptr)
|
||||
publishc = make(chan *windowImpl)
|
||||
uic = make(chan uiClosure)
|
||||
|
||||
// TODO: don't assume that there is only one window, and hence only
|
||||
// one (global) GL context.
|
||||
//
|
||||
// TODO: should we be able to make a shiny.Texture before having a
|
||||
// shiny.Window's GL context? Should something like gl.IsProgram be a
|
||||
// method instead of a function, and have each shiny.Window have its own
|
||||
// gl.Context?
|
||||
glctx gl.Context
|
||||
worker gl.Worker
|
||||
)
|
||||
|
||||
// uiClosure is a closure to be run on C's UI thread.
|
||||
type uiClosure struct {
|
||||
f func() uintptr
|
||||
retc chan uintptr
|
||||
}
|
||||
|
||||
func main(f func(screen.Screen)) error {
|
||||
if gl.Version() == "GL_ES_2_0" {
|
||||
return errors.New("gldriver: ES 3 required on X11")
|
||||
}
|
||||
C.startDriver()
|
||||
glctx, worker = gl.NewContext()
|
||||
|
||||
closec := make(chan struct{})
|
||||
go func() {
|
||||
f(theScreen)
|
||||
close(closec)
|
||||
}()
|
||||
|
||||
// heartbeat is a channel that, at regular intervals, directs the select
|
||||
// below to also consider X11 events, not just Go events (channel
|
||||
// communications).
|
||||
//
|
||||
// TODO: select instead of poll. Note that knowing whether to call
|
||||
// C.processEvents needs to select on a file descriptor, and the other
|
||||
// cases below select on Go channels.
|
||||
heartbeat := time.NewTicker(time.Second / 60)
|
||||
workAvailable := worker.WorkAvailable()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-closec:
|
||||
return nil
|
||||
case ctx := <-glcontextc:
|
||||
// TODO: do we need to synchronize with seeing a size event for
|
||||
// this window's context before or after calling makeCurrent?
|
||||
// Otherwise, are we racing with the gl.Viewport call? I've
|
||||
// occasionally seen a stale viewport, if the window manager sets
|
||||
// the window width and height to something other than that
|
||||
// requested by XCreateWindow, but it's not easily reproducible.
|
||||
C.makeCurrent(C.uintptr_t(ctx))
|
||||
case w := <-publishc:
|
||||
C.swapBuffers(C.uintptr_t(w.ctx.(uintptr)))
|
||||
w.publishDone <- screen.PublishResult{}
|
||||
case req := <-uic:
|
||||
ret := req.f()
|
||||
if req.retc != nil {
|
||||
req.retc <- ret
|
||||
}
|
||||
case <-heartbeat.C:
|
||||
C.processEvents()
|
||||
case <-workAvailable:
|
||||
worker.DoWork()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//export onExpose
|
||||
func onExpose(id uintptr) {
|
||||
theScreen.mu.Lock()
|
||||
w := theScreen.windows[id]
|
||||
theScreen.mu.Unlock()
|
||||
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
|
||||
w.Send(paint.Event{External: true})
|
||||
}
|
||||
|
||||
//export onKeysym
|
||||
func onKeysym(k, unshifted, shifted uint32) {
|
||||
theKeysyms.Table[k][0] = unshifted
|
||||
theKeysyms.Table[k][1] = shifted
|
||||
}
|
||||
|
||||
//export onKey
|
||||
func onKey(id uintptr, state uint16, detail, dir uint8) {
|
||||
theScreen.mu.Lock()
|
||||
w := theScreen.windows[id]
|
||||
theScreen.mu.Unlock()
|
||||
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
|
||||
r, c := theKeysyms.Lookup(detail, state)
|
||||
w.Send(key.Event{
|
||||
Rune: r,
|
||||
Code: c,
|
||||
Modifiers: x11key.KeyModifiers(state),
|
||||
Direction: key.Direction(dir),
|
||||
})
|
||||
}
|
||||
|
||||
//export onMouse
|
||||
func onMouse(id uintptr, x, y int32, state uint16, button, dir uint8) {
|
||||
theScreen.mu.Lock()
|
||||
w := theScreen.windows[id]
|
||||
theScreen.mu.Unlock()
|
||||
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: should a mouse.Event have a separate MouseModifiers field, for
|
||||
// which buttons are pressed during a mouse move?
|
||||
btn := mouse.Button(button)
|
||||
switch btn {
|
||||
case 4:
|
||||
btn = mouse.ButtonWheelUp
|
||||
case 5:
|
||||
btn = mouse.ButtonWheelDown
|
||||
case 6:
|
||||
btn = mouse.ButtonWheelLeft
|
||||
case 7:
|
||||
btn = mouse.ButtonWheelRight
|
||||
}
|
||||
if btn.IsWheel() {
|
||||
if dir != uint8(mouse.DirPress) {
|
||||
return
|
||||
}
|
||||
dir = uint8(mouse.DirStep)
|
||||
}
|
||||
w.Send(mouse.Event{
|
||||
X: float32(x),
|
||||
Y: float32(y),
|
||||
Button: btn,
|
||||
Modifiers: x11key.KeyModifiers(state),
|
||||
Direction: mouse.Direction(dir),
|
||||
})
|
||||
}
|
||||
|
||||
//export onFocus
|
||||
func onFocus(id uintptr, focused bool) {
|
||||
theScreen.mu.Lock()
|
||||
w := theScreen.windows[id]
|
||||
theScreen.mu.Unlock()
|
||||
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
|
||||
w.lifecycler.SetFocused(focused)
|
||||
w.lifecycler.SendEvent(w, w.glctx)
|
||||
}
|
||||
|
||||
//export onConfigure
|
||||
func onConfigure(id uintptr, x, y, width, height, displayWidth, displayWidthMM int32) {
|
||||
theScreen.mu.Lock()
|
||||
w := theScreen.windows[id]
|
||||
theScreen.mu.Unlock()
|
||||
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
|
||||
w.lifecycler.SetVisible(x+width > 0 && y+height > 0)
|
||||
w.lifecycler.SendEvent(w, w.glctx)
|
||||
|
||||
const (
|
||||
mmPerInch = 25.4
|
||||
ptPerInch = 72
|
||||
)
|
||||
pixelsPerMM := float32(displayWidth) / float32(displayWidthMM)
|
||||
w.Send(size.Event{
|
||||
WidthPx: int(width),
|
||||
HeightPx: int(height),
|
||||
WidthPt: geom.Pt(width),
|
||||
HeightPt: geom.Pt(height),
|
||||
PixelsPerPt: pixelsPerMM * mmPerInch / ptPerInch,
|
||||
})
|
||||
}
|
||||
|
||||
//export onDeleteWindow
|
||||
func onDeleteWindow(id uintptr) {
|
||||
theScreen.mu.Lock()
|
||||
w := theScreen.windows[id]
|
||||
theScreen.mu.Unlock()
|
||||
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
|
||||
w.lifecycler.SetDead(true)
|
||||
w.lifecycler.SendEvent(w, w.glctx)
|
||||
}
|
||||
|
||||
func surfaceCreate() error {
|
||||
if C.surfaceCreate() == 0 {
|
||||
return errors.New("gldriver: surface creation failed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
34
vendor/golang.org/x/exp/shiny/driver/internal/drawer/drawer.go
generated
vendored
Normal file
34
vendor/golang.org/x/exp/shiny/driver/internal/drawer/drawer.go
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package drawer provides functions that help implement screen.Drawer methods.
|
||||
package drawer // import "golang.org/x/exp/shiny/driver/internal/drawer"
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/draw"
|
||||
|
||||
"golang.org/x/exp/shiny/screen"
|
||||
"golang.org/x/image/math/f64"
|
||||
)
|
||||
|
||||
// Copy implements the Copy method of the screen.Drawer interface by calling
|
||||
// the Draw method of that same interface.
|
||||
func Copy(dst screen.Drawer, dp image.Point, src screen.Texture, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) {
|
||||
dst.Draw(f64.Aff3{
|
||||
1, 0, float64(dp.X - sr.Min.X),
|
||||
0, 1, float64(dp.Y - sr.Min.Y),
|
||||
}, src, sr, op, opts)
|
||||
}
|
||||
|
||||
// Scale implements the Scale method of the screen.Drawer interface by calling
|
||||
// the Draw method of that same interface.
|
||||
func Scale(dst screen.Drawer, dr image.Rectangle, src screen.Texture, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) {
|
||||
rx := float64(dr.Dx()) / float64(sr.Dx())
|
||||
ry := float64(dr.Dy()) / float64(sr.Dy())
|
||||
dst.Draw(f64.Aff3{
|
||||
rx, 0, float64(dr.Min.X) - rx*float64(sr.Min.X),
|
||||
0, ry, float64(dr.Min.Y) - ry*float64(sr.Min.Y),
|
||||
}, src, sr, op, opts)
|
||||
}
|
||||
25
vendor/golang.org/x/exp/shiny/driver/internal/errscreen/errscreen.go
generated
vendored
Normal file
25
vendor/golang.org/x/exp/shiny/driver/internal/errscreen/errscreen.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package errscreen provides a stub Screen implementation.
|
||||
package errscreen // import "golang.org/x/exp/shiny/driver/internal/errscreen"
|
||||
|
||||
import (
|
||||
"image"
|
||||
|
||||
"golang.org/x/exp/shiny/screen"
|
||||
)
|
||||
|
||||
// Stub returns a Screen whose methods all return the given error.
|
||||
func Stub(err error) screen.Screen {
|
||||
return stub{err}
|
||||
}
|
||||
|
||||
type stub struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (s stub) NewBuffer(size image.Point) (screen.Buffer, error) { return nil, s.err }
|
||||
func (s stub) NewTexture(size image.Point) (screen.Texture, error) { return nil, s.err }
|
||||
func (s stub) NewWindow(opts *screen.NewWindowOptions) (screen.Window, error) { return nil, s.err }
|
||||
68
vendor/golang.org/x/exp/shiny/driver/internal/event/event.go
generated
vendored
Normal file
68
vendor/golang.org/x/exp/shiny/driver/internal/event/event.go
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package event provides an infinitely buffered double-ended queue of events.
|
||||
package event // import "golang.org/x/exp/shiny/driver/internal/event"
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Deque is an infinitely buffered double-ended queue of events. The zero value
|
||||
// is usable, but a Deque value must not be copied.
|
||||
type Deque struct {
|
||||
mu sync.Mutex
|
||||
cond sync.Cond // cond.L is lazily initialized to &Deque.mu.
|
||||
back []interface{} // FIFO.
|
||||
front []interface{} // LIFO.
|
||||
}
|
||||
|
||||
func (q *Deque) lockAndInit() {
|
||||
q.mu.Lock()
|
||||
if q.cond.L == nil {
|
||||
q.cond.L = &q.mu
|
||||
}
|
||||
}
|
||||
|
||||
// NextEvent implements the screen.EventDeque interface.
|
||||
func (q *Deque) NextEvent() interface{} {
|
||||
q.lockAndInit()
|
||||
defer q.mu.Unlock()
|
||||
|
||||
for {
|
||||
if n := len(q.front); n > 0 {
|
||||
e := q.front[n-1]
|
||||
q.front[n-1] = nil
|
||||
q.front = q.front[:n-1]
|
||||
return e
|
||||
}
|
||||
|
||||
if n := len(q.back); n > 0 {
|
||||
e := q.back[0]
|
||||
q.back[0] = nil
|
||||
q.back = q.back[1:]
|
||||
return e
|
||||
}
|
||||
|
||||
q.cond.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
// Send implements the screen.EventDeque interface.
|
||||
func (q *Deque) Send(event interface{}) {
|
||||
q.lockAndInit()
|
||||
defer q.mu.Unlock()
|
||||
|
||||
q.back = append(q.back, event)
|
||||
q.cond.Signal()
|
||||
}
|
||||
|
||||
// SendFirst implements the screen.EventDeque interface.
|
||||
func (q *Deque) SendFirst(event interface{}) {
|
||||
q.lockAndInit()
|
||||
defer q.mu.Unlock()
|
||||
|
||||
q.front = append(q.front, event)
|
||||
q.cond.Signal()
|
||||
}
|
||||
80
vendor/golang.org/x/exp/shiny/driver/internal/lifecycler/lifecycler.go
generated
vendored
Normal file
80
vendor/golang.org/x/exp/shiny/driver/internal/lifecycler/lifecycler.go
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package lifecycler tracks a window's lifecycle state.
|
||||
//
|
||||
// It eliminates sending redundant lifecycle events, ones where the From and To
|
||||
// stages are equal. For example, moving a window from one part of the screen
|
||||
// to another should not send multiple events from StageVisible to
|
||||
// StageVisible, even though the underlying window system's message might only
|
||||
// hold the new position, and not whether the window was previously visible.
|
||||
package lifecycler // import "golang.org/x/exp/shiny/driver/internal/lifecycler"
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"golang.org/x/mobile/event/lifecycle"
|
||||
)
|
||||
|
||||
// State is a window's lifecycle state.
|
||||
type State struct {
|
||||
mu sync.Mutex
|
||||
stage lifecycle.Stage
|
||||
dead bool
|
||||
focused bool
|
||||
visible bool
|
||||
}
|
||||
|
||||
func (s *State) SetDead(b bool) {
|
||||
s.mu.Lock()
|
||||
s.dead = b
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s *State) SetFocused(b bool) {
|
||||
s.mu.Lock()
|
||||
s.focused = b
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s *State) SetVisible(b bool) {
|
||||
s.mu.Lock()
|
||||
s.visible = b
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s *State) SendEvent(r Sender, drawContext interface{}) {
|
||||
s.mu.Lock()
|
||||
from, to := s.stage, lifecycle.StageAlive
|
||||
// The order of these if's is important. For example, once a window becomes
|
||||
// StageDead, it should never change stage again.
|
||||
//
|
||||
// Similarly, focused trumps visible. It's hard to imagine a situation
|
||||
// where a window is focused and not visible on screen, but in that
|
||||
// unlikely case, StageFocused seems the most appropriate stage.
|
||||
if s.dead {
|
||||
to = lifecycle.StageDead
|
||||
} else if s.focused {
|
||||
to = lifecycle.StageFocused
|
||||
} else if s.visible {
|
||||
to = lifecycle.StageVisible
|
||||
}
|
||||
s.stage = to
|
||||
s.mu.Unlock()
|
||||
|
||||
if from != to {
|
||||
r.Send(lifecycle.Event{
|
||||
From: from,
|
||||
To: to,
|
||||
|
||||
// TODO: does shiny use this at all?
|
||||
DrawContext: drawContext,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Sender is who to send the lifecycle event to.
|
||||
type Sender interface {
|
||||
Send(event interface{})
|
||||
}
|
||||
350
vendor/golang.org/x/exp/shiny/driver/internal/win32/key.go
generated
vendored
Normal file
350
vendor/golang.org/x/exp/shiny/driver/internal/win32/key.go
generated
vendored
Normal file
@@ -0,0 +1,350 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package win32
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unicode/utf16"
|
||||
|
||||
"golang.org/x/mobile/event/key"
|
||||
)
|
||||
|
||||
// convVirtualKeyCode converts a Win32 virtual key code number
|
||||
// into the standard keycodes used by the key package.
|
||||
func convVirtualKeyCode(vKey uint32) key.Code {
|
||||
switch vKey {
|
||||
case 0x01: // VK_LBUTTON left mouse button
|
||||
case 0x02: // VK_RBUTTON right mouse button
|
||||
case 0x03: // VK_CANCEL control-break processing
|
||||
case 0x04: // VK_MBUTTON middle mouse button
|
||||
case 0x05: // VK_XBUTTON1 X1 mouse button
|
||||
case 0x06: // VK_XBUTTON2 X2 mouse button
|
||||
case 0x08: // VK_BACK
|
||||
return key.CodeDeleteBackspace
|
||||
case 0x09: // VK_TAB
|
||||
return key.CodeTab
|
||||
case 0x0C: // VK_CLEAR
|
||||
case 0x0D: // VK_RETURN
|
||||
return key.CodeReturnEnter
|
||||
case 0x10: // VK_SHIFT
|
||||
return key.CodeLeftShift
|
||||
case 0x11: // VK_CONTROL
|
||||
return key.CodeLeftControl
|
||||
case 0x12: // VK_MENU
|
||||
return key.CodeLeftAlt
|
||||
case 0x13: // VK_PAUSE
|
||||
case 0x14: // VK_CAPITAL
|
||||
return key.CodeCapsLock
|
||||
case 0x15: // VK_KANA, VK_HANGUEL, VK_HANGUL
|
||||
case 0x17: // VK_JUNJA
|
||||
case 0x18: // VK_FINA, L
|
||||
case 0x19: // VK_HANJA, VK_KANJI
|
||||
case 0x1B: // VK_ESCAPE
|
||||
return key.CodeEscape
|
||||
case 0x1C: // VK_CONVERT
|
||||
case 0x1D: // VK_NONCONVERT
|
||||
case 0x1E: // VK_ACCEPT
|
||||
case 0x1F: // VK_MODECHANGE
|
||||
case 0x20: // VK_SPACE
|
||||
return key.CodeSpacebar
|
||||
case 0x21: // VK_PRIOR
|
||||
return key.CodePageUp
|
||||
case 0x22: // VK_NEXT
|
||||
return key.CodePageDown
|
||||
case 0x23: // VK_END
|
||||
return key.CodeEnd
|
||||
case 0x24: // VK_HOME
|
||||
return key.CodeHome
|
||||
case 0x25: // VK_LEFT
|
||||
return key.CodeLeftArrow
|
||||
case 0x26: // VK_UP
|
||||
return key.CodeUpArrow
|
||||
case 0x27: // VK_RIGHT
|
||||
return key.CodeRightArrow
|
||||
case 0x28: // VK_DOWN
|
||||
return key.CodeDownArrow
|
||||
case 0x29: // VK_SELECT
|
||||
case 0x2A: // VK_PRINT
|
||||
case 0x2B: // VK_EXECUTE
|
||||
case 0x2C: // VK_SNAPSHOT
|
||||
case 0x2D: // VK_INSERT
|
||||
case 0x2E: // VK_DELETE
|
||||
return key.CodeDeleteForward
|
||||
case 0x2F: // VK_HELP
|
||||
return key.CodeHelp
|
||||
case 0x30:
|
||||
return key.Code0
|
||||
case 0x31:
|
||||
return key.Code1
|
||||
case 0x32:
|
||||
return key.Code2
|
||||
case 0x33:
|
||||
return key.Code3
|
||||
case 0x34:
|
||||
return key.Code4
|
||||
case 0x35:
|
||||
return key.Code5
|
||||
case 0x36:
|
||||
return key.Code6
|
||||
case 0x37:
|
||||
return key.Code7
|
||||
case 0x38:
|
||||
return key.Code8
|
||||
case 0x39:
|
||||
return key.Code9
|
||||
case 0x41:
|
||||
return key.CodeA
|
||||
case 0x42:
|
||||
return key.CodeB
|
||||
case 0x43:
|
||||
return key.CodeC
|
||||
case 0x44:
|
||||
return key.CodeD
|
||||
case 0x45:
|
||||
return key.CodeE
|
||||
case 0x46:
|
||||
return key.CodeF
|
||||
case 0x47:
|
||||
return key.CodeG
|
||||
case 0x48:
|
||||
return key.CodeH
|
||||
case 0x49:
|
||||
return key.CodeI
|
||||
case 0x4A:
|
||||
return key.CodeJ
|
||||
case 0x4B:
|
||||
return key.CodeK
|
||||
case 0x4C:
|
||||
return key.CodeL
|
||||
case 0x4D:
|
||||
return key.CodeM
|
||||
case 0x4E:
|
||||
return key.CodeN
|
||||
case 0x4F:
|
||||
return key.CodeO
|
||||
case 0x50:
|
||||
return key.CodeP
|
||||
case 0x51:
|
||||
return key.CodeQ
|
||||
case 0x52:
|
||||
return key.CodeR
|
||||
case 0x53:
|
||||
return key.CodeS
|
||||
case 0x54:
|
||||
return key.CodeT
|
||||
case 0x55:
|
||||
return key.CodeU
|
||||
case 0x56:
|
||||
return key.CodeV
|
||||
case 0x57:
|
||||
return key.CodeW
|
||||
case 0x58:
|
||||
return key.CodeX
|
||||
case 0x59:
|
||||
return key.CodeY
|
||||
case 0x5A:
|
||||
return key.CodeZ
|
||||
case 0x5B: // VK_LWIN
|
||||
return key.CodeLeftGUI
|
||||
case 0x5C: // VK_RWIN
|
||||
return key.CodeRightGUI
|
||||
case 0x5D: // VK_APPS
|
||||
case 0x5F: // VK_SLEEP
|
||||
case 0x60: // VK_NUMPAD0
|
||||
return key.CodeKeypad0
|
||||
case 0x61: // VK_NUMPAD1
|
||||
return key.CodeKeypad1
|
||||
case 0x62: // VK_NUMPAD2
|
||||
return key.CodeKeypad2
|
||||
case 0x63: // VK_NUMPAD3
|
||||
return key.CodeKeypad3
|
||||
case 0x64: // VK_NUMPAD4
|
||||
return key.CodeKeypad4
|
||||
case 0x65: // VK_NUMPAD5
|
||||
return key.CodeKeypad5
|
||||
case 0x66: // VK_NUMPAD6
|
||||
return key.CodeKeypad6
|
||||
case 0x67: // VK_NUMPAD7
|
||||
return key.CodeKeypad7
|
||||
case 0x68: // VK_NUMPAD8
|
||||
return key.CodeKeypad8
|
||||
case 0x69: // VK_NUMPAD9
|
||||
return key.CodeKeypad9
|
||||
case 0x6A: // VK_MULTIPLY
|
||||
return key.CodeKeypadAsterisk
|
||||
case 0x6B: // VK_ADD
|
||||
return key.CodeKeypadPlusSign
|
||||
case 0x6C: // VK_SEPARATOR
|
||||
case 0x6D: // VK_SUBTRACT
|
||||
return key.CodeKeypadHyphenMinus
|
||||
case 0x6E: // VK_DECIMAL
|
||||
return key.CodeFullStop
|
||||
case 0x6F: // VK_DIVIDE
|
||||
return key.CodeKeypadSlash
|
||||
case 0x70: // VK_F1
|
||||
return key.CodeF1
|
||||
case 0x71: // VK_F2
|
||||
return key.CodeF2
|
||||
case 0x72: // VK_F3
|
||||
return key.CodeF3
|
||||
case 0x73: // VK_F4
|
||||
return key.CodeF4
|
||||
case 0x74: // VK_F5
|
||||
return key.CodeF5
|
||||
case 0x75: // VK_F6
|
||||
return key.CodeF6
|
||||
case 0x76: // VK_F7
|
||||
return key.CodeF7
|
||||
case 0x77: // VK_F8
|
||||
return key.CodeF8
|
||||
case 0x78: // VK_F9
|
||||
return key.CodeF9
|
||||
case 0x79: // VK_F10
|
||||
return key.CodeF10
|
||||
case 0x7A: // VK_F11
|
||||
return key.CodeF11
|
||||
case 0x7B: // VK_F12
|
||||
return key.CodeF12
|
||||
case 0x7C: // VK_F13
|
||||
return key.CodeF13
|
||||
case 0x7D: // VK_F14
|
||||
return key.CodeF14
|
||||
case 0x7E: // VK_F15
|
||||
return key.CodeF15
|
||||
case 0x7F: // VK_F16
|
||||
return key.CodeF16
|
||||
case 0x80: // VK_F17
|
||||
return key.CodeF17
|
||||
case 0x81: // VK_F18
|
||||
return key.CodeF18
|
||||
case 0x82: // VK_F19
|
||||
return key.CodeF19
|
||||
case 0x83: // VK_F20
|
||||
return key.CodeF20
|
||||
case 0x84: // VK_F21
|
||||
return key.CodeF21
|
||||
case 0x85: // VK_F22
|
||||
return key.CodeF22
|
||||
case 0x86: // VK_F23
|
||||
return key.CodeF23
|
||||
case 0x87: // VK_F24
|
||||
return key.CodeF24
|
||||
case 0x90: // VK_NUMLOCK
|
||||
return key.CodeKeypadNumLock
|
||||
case 0x91: // VK_SCROLL
|
||||
case 0xA0: // VK_LSHIFT
|
||||
return key.CodeLeftShift
|
||||
case 0xA1: // VK_RSHIFT
|
||||
return key.CodeRightShift
|
||||
case 0xA2: // VK_LCONTROL
|
||||
return key.CodeLeftControl
|
||||
case 0xA3: // VK_RCONTROL
|
||||
return key.CodeRightControl
|
||||
case 0xA4: // VK_LMENU
|
||||
case 0xA5: // VK_RMENU
|
||||
case 0xA6: // VK_BROWSER_BACK
|
||||
case 0xA7: // VK_BROWSER_FORWARD
|
||||
case 0xA8: // VK_BROWSER_REFRESH
|
||||
case 0xA9: // VK_BROWSER_STOP
|
||||
case 0xAA: // VK_BROWSER_SEARCH
|
||||
case 0xAB: // VK_BROWSER_FAVORITES
|
||||
case 0xAC: // VK_BROWSER_HOME
|
||||
case 0xAD: // VK_VOLUME_MUTE
|
||||
return key.CodeMute
|
||||
case 0xAE: // VK_VOLUME_DOWN
|
||||
return key.CodeVolumeDown
|
||||
case 0xAF: // VK_VOLUME_UP
|
||||
return key.CodeVolumeUp
|
||||
case 0xB0: // VK_MEDIA_NEXT_TRACK
|
||||
case 0xB1: // VK_MEDIA_PREV_TRACK
|
||||
case 0xB2: // VK_MEDIA_STOP
|
||||
case 0xB3: // VK_MEDIA_PLAY_PAUSE
|
||||
case 0xB4: // VK_LAUNCH_MAIL
|
||||
case 0xB5: // VK_LAUNCH_MEDIA_SELECT
|
||||
case 0xB6: // VK_LAUNCH_APP1
|
||||
case 0xB7: // VK_LAUNCH_APP2
|
||||
case 0xBA: // VK_OEM_1 ';:'
|
||||
return key.CodeSemicolon
|
||||
case 0xBB: // VK_OEM_PLUS '+'
|
||||
return key.CodeEqualSign
|
||||
case 0xBC: // VK_OEM_COMMA ','
|
||||
return key.CodeComma
|
||||
case 0xBD: // VK_OEM_MINUS '-'
|
||||
return key.CodeHyphenMinus
|
||||
case 0xBE: // VK_OEM_PERIOD '.'
|
||||
return key.CodeFullStop
|
||||
case 0xBF: // VK_OEM_2 '/?'
|
||||
return key.CodeSlash
|
||||
case 0xC0: // VK_OEM_3 '`~'
|
||||
return key.CodeGraveAccent
|
||||
case 0xDB: // VK_OEM_4 '[{'
|
||||
return key.CodeLeftSquareBracket
|
||||
case 0xDC: // VK_OEM_5 '\|'
|
||||
return key.CodeBackslash
|
||||
case 0xDD: // VK_OEM_6 ']}'
|
||||
return key.CodeRightSquareBracket
|
||||
case 0xDE: // VK_OEM_7 'single-quote/double-quote'
|
||||
return key.CodeApostrophe
|
||||
case 0xDF: // VK_OEM_8
|
||||
return key.CodeUnknown
|
||||
case 0xE2: // VK_OEM_102
|
||||
case 0xE5: // VK_PROCESSKEY
|
||||
case 0xE7: // VK_PACKET
|
||||
case 0xF6: // VK_ATTN
|
||||
case 0xF7: // VK_CRSEL
|
||||
case 0xF8: // VK_EXSEL
|
||||
case 0xF9: // VK_EREOF
|
||||
case 0xFA: // VK_PLAY
|
||||
case 0xFB: // VK_ZOOM
|
||||
case 0xFC: // VK_NONAME
|
||||
case 0xFD: // VK_PA1
|
||||
case 0xFE: // VK_OEM_CLEAR
|
||||
}
|
||||
return key.CodeUnknown
|
||||
}
|
||||
|
||||
func readRune(vKey uint32, scanCode uint8) rune {
|
||||
var (
|
||||
keystate [256]byte
|
||||
buf [4]uint16
|
||||
)
|
||||
if err := _GetKeyboardState(&keystate[0]); err != nil {
|
||||
panic(fmt.Sprintf("win32: %v", err))
|
||||
}
|
||||
// TODO: cache GetKeyboardLayout result, update on WM_INPUTLANGCHANGE
|
||||
layout := _GetKeyboardLayout(0)
|
||||
ret := _ToUnicodeEx(vKey, uint32(scanCode), &keystate[0], &buf[0], int32(len(buf)), 0, layout)
|
||||
if ret < 1 {
|
||||
return -1
|
||||
}
|
||||
return utf16.Decode(buf[:ret])[0]
|
||||
}
|
||||
|
||||
func sendKeyEvent(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) (lResult uintptr) {
|
||||
e := key.Event{
|
||||
Rune: readRune(uint32(wParam), uint8(lParam>>16)),
|
||||
Code: convVirtualKeyCode(uint32(wParam)),
|
||||
Modifiers: keyModifiers(),
|
||||
}
|
||||
switch uMsg {
|
||||
case _WM_KEYDOWN, _WM_SYSKEYDOWN:
|
||||
const prevMask = 1 << 30
|
||||
if repeat := lParam&prevMask == prevMask; repeat {
|
||||
e.Direction = key.DirNone
|
||||
} else {
|
||||
e.Direction = key.DirPress
|
||||
}
|
||||
case _WM_KEYUP, _WM_SYSKEYUP:
|
||||
e.Direction = key.DirRelease
|
||||
default:
|
||||
panic(fmt.Sprintf("win32: unexpected key message: %d", uMsg))
|
||||
}
|
||||
|
||||
KeyEvent(hwnd, e)
|
||||
return 0
|
||||
}
|
||||
7
vendor/golang.org/x/exp/shiny/driver/internal/win32/syscall.go
generated
vendored
Normal file
7
vendor/golang.org/x/exp/shiny/driver/internal/win32/syscall.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go
|
||||
|
||||
package win32
|
||||
186
vendor/golang.org/x/exp/shiny/driver/internal/win32/syscall_windows.go
generated
vendored
Normal file
186
vendor/golang.org/x/exp/shiny/driver/internal/win32/syscall_windows.go
generated
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package win32
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type _COLORREF uint32
|
||||
|
||||
func _RGB(r, g, b byte) _COLORREF {
|
||||
return _COLORREF(r) | _COLORREF(g)<<8 | _COLORREF(b)<<16
|
||||
}
|
||||
|
||||
type _POINT struct {
|
||||
X int32
|
||||
Y int32
|
||||
}
|
||||
|
||||
type _RECT struct {
|
||||
Left int32
|
||||
Top int32
|
||||
Right int32
|
||||
Bottom int32
|
||||
}
|
||||
|
||||
type _MSG struct {
|
||||
HWND syscall.Handle
|
||||
Message uint32
|
||||
Wparam uintptr
|
||||
Lparam uintptr
|
||||
Time uint32
|
||||
Pt _POINT
|
||||
}
|
||||
|
||||
type _WNDCLASS struct {
|
||||
Style uint32
|
||||
LpfnWndProc uintptr
|
||||
CbClsExtra int32
|
||||
CbWndExtra int32
|
||||
HInstance syscall.Handle
|
||||
HIcon syscall.Handle
|
||||
HCursor syscall.Handle
|
||||
HbrBackground syscall.Handle
|
||||
LpszMenuName *uint16
|
||||
LpszClassName *uint16
|
||||
}
|
||||
|
||||
type _WINDOWPOS struct {
|
||||
HWND syscall.Handle
|
||||
HWNDInsertAfter syscall.Handle
|
||||
X int32
|
||||
Y int32
|
||||
Cx int32
|
||||
Cy int32
|
||||
Flags uint32
|
||||
}
|
||||
|
||||
const (
|
||||
_WM_SETFOCUS = 7
|
||||
_WM_KILLFOCUS = 8
|
||||
_WM_PAINT = 15
|
||||
_WM_CLOSE = 16
|
||||
_WM_WINDOWPOSCHANGED = 71
|
||||
_WM_KEYDOWN = 256
|
||||
_WM_KEYUP = 257
|
||||
_WM_SYSKEYDOWN = 260
|
||||
_WM_SYSKEYUP = 261
|
||||
_WM_MOUSEMOVE = 512
|
||||
_WM_MOUSEWHEEL = 522
|
||||
_WM_LBUTTONDOWN = 513
|
||||
_WM_LBUTTONUP = 514
|
||||
_WM_RBUTTONDOWN = 516
|
||||
_WM_RBUTTONUP = 517
|
||||
_WM_MBUTTONDOWN = 519
|
||||
_WM_MBUTTONUP = 520
|
||||
_WM_USER = 0x0400
|
||||
)
|
||||
|
||||
const (
|
||||
_WS_OVERLAPPED = 0x00000000
|
||||
_WS_CAPTION = 0x00C00000
|
||||
_WS_SYSMENU = 0x00080000
|
||||
_WS_THICKFRAME = 0x00040000
|
||||
_WS_MINIMIZEBOX = 0x00020000
|
||||
_WS_MAXIMIZEBOX = 0x00010000
|
||||
_WS_OVERLAPPEDWINDOW = _WS_OVERLAPPED | _WS_CAPTION | _WS_SYSMENU | _WS_THICKFRAME | _WS_MINIMIZEBOX | _WS_MAXIMIZEBOX
|
||||
)
|
||||
|
||||
const (
|
||||
_VK_SHIFT = 16
|
||||
_VK_CONTROL = 17
|
||||
_VK_MENU = 18
|
||||
_VK_LWIN = 0x5B
|
||||
_VK_RWIN = 0x5C
|
||||
)
|
||||
|
||||
const (
|
||||
_MK_LBUTTON = 0x0001
|
||||
_MK_MBUTTON = 0x0010
|
||||
_MK_RBUTTON = 0x0002
|
||||
)
|
||||
|
||||
const (
|
||||
_COLOR_BTNFACE = 15
|
||||
)
|
||||
|
||||
const (
|
||||
_IDI_APPLICATION = 32512
|
||||
_IDC_ARROW = 32512
|
||||
)
|
||||
|
||||
const (
|
||||
_CW_USEDEFAULT = 0x80000000 - 0x100000000
|
||||
|
||||
_SW_SHOWDEFAULT = 10
|
||||
|
||||
_HWND_MESSAGE = syscall.Handle(^uintptr(2)) // -3
|
||||
|
||||
_SWP_NOSIZE = 0x0001
|
||||
)
|
||||
|
||||
const (
|
||||
_BI_RGB = 0
|
||||
_DIB_RGB_COLORS = 0
|
||||
|
||||
_AC_SRC_OVER = 0x00
|
||||
_AC_SRC_ALPHA = 0x01
|
||||
|
||||
_SRCCOPY = 0x00cc0020
|
||||
|
||||
_WHEEL_DELTA = 120
|
||||
)
|
||||
|
||||
func _GET_X_LPARAM(lp uintptr) int32 {
|
||||
return int32(_LOWORD(lp))
|
||||
}
|
||||
|
||||
func _GET_Y_LPARAM(lp uintptr) int32 {
|
||||
return int32(_HIWORD(lp))
|
||||
}
|
||||
|
||||
func _GET_WHEEL_DELTA_WPARAM(lp uintptr) int16 {
|
||||
return int16(_HIWORD(lp))
|
||||
}
|
||||
|
||||
func _LOWORD(l uintptr) uint16 {
|
||||
return uint16(uint32(l))
|
||||
}
|
||||
|
||||
func _HIWORD(l uintptr) uint16 {
|
||||
return uint16(uint32(l >> 16))
|
||||
}
|
||||
|
||||
// notes to self
|
||||
// UINT = uint32
|
||||
// callbacks = uintptr
|
||||
// strings = *uint16
|
||||
|
||||
//sys GetDC(hwnd syscall.Handle) (dc syscall.Handle, err error) = user32.GetDC
|
||||
//sys ReleaseDC(hwnd syscall.Handle, dc syscall.Handle) (err error) = user32.ReleaseDC
|
||||
//sys sendMessage(hwnd syscall.Handle, uMsg uint32, wParam uintptr, lParam uintptr) (lResult uintptr) = user32.SendMessageW
|
||||
|
||||
//sys _CreateWindowEx(exstyle uint32, className *uint16, windowText *uint16, style uint32, x int32, y int32, width int32, height int32, parent syscall.Handle, menu syscall.Handle, hInstance syscall.Handle, lpParam uintptr) (hwnd syscall.Handle, err error) = user32.CreateWindowExW
|
||||
//sys _DefWindowProc(hwnd syscall.Handle, uMsg uint32, wParam uintptr, lParam uintptr) (lResult uintptr) = user32.DefWindowProcW
|
||||
//sys _DestroyWindow(hwnd syscall.Handle) (err error) = user32.DestroyWindow
|
||||
//sys _DispatchMessage(msg *_MSG) (ret int32) = user32.DispatchMessageW
|
||||
//sys _GetClientRect(hwnd syscall.Handle, rect *_RECT) (err error) = user32.GetClientRect
|
||||
//sys _GetWindowRect(hwnd syscall.Handle, rect *_RECT) (err error) = user32.GetWindowRect
|
||||
//sys _GetKeyboardLayout(threadID uint32) (locale syscall.Handle) = user32.GetKeyboardLayout
|
||||
//sys _GetKeyboardState(lpKeyState *byte) (err error) = user32.GetKeyboardState
|
||||
//sys _GetKeyState(virtkey int32) (keystatus int16) = user32.GetKeyState
|
||||
//sys _GetMessage(msg *_MSG, hwnd syscall.Handle, msgfiltermin uint32, msgfiltermax uint32) (ret int32, err error) [failretval==-1] = user32.GetMessageW
|
||||
//sys _LoadCursor(hInstance syscall.Handle, cursorName uintptr) (cursor syscall.Handle, err error) = user32.LoadCursorW
|
||||
//sys _LoadIcon(hInstance syscall.Handle, iconName uintptr) (icon syscall.Handle, err error) = user32.LoadIconW
|
||||
//sys _MoveWindow(hwnd syscall.Handle, x int32, y int32, w int32, h int32, repaint bool) (err error) = user32.MoveWindow
|
||||
//sys _PostMessage(hwnd syscall.Handle, uMsg uint32, wParam uintptr, lParam uintptr) (lResult bool) = user32.PostMessageW
|
||||
//sys _PostQuitMessage(exitCode int32) = user32.PostQuitMessage
|
||||
//sys _RegisterClass(wc *_WNDCLASS) (atom uint16, err error) = user32.RegisterClassW
|
||||
//sys _ShowWindow(hwnd syscall.Handle, cmdshow int32) (wasvisible bool) = user32.ShowWindow
|
||||
//sys _ScreenToClient(hwnd syscall.Handle, lpPoint *_POINT) (ok bool) = user32.ScreenToClient
|
||||
//sys _ToUnicodeEx(wVirtKey uint32, wScanCode uint32, lpKeyState *byte, pwszBuff *uint16, cchBuff int32, wFlags uint32, dwhkl syscall.Handle) (ret int32) = user32.ToUnicodeEx
|
||||
//sys _TranslateMessage(msg *_MSG) (done bool) = user32.TranslateMessage
|
||||
//sys _UnregisterClass(lpClassName *uint16, hInstance syscall.Handle) (done bool) = user32.UnregisterClassW
|
||||
519
vendor/golang.org/x/exp/shiny/driver/internal/win32/win32.go
generated
vendored
Normal file
519
vendor/golang.org/x/exp/shiny/driver/internal/win32/win32.go
generated
vendored
Normal file
@@ -0,0 +1,519 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows
|
||||
|
||||
// Package win32 implements a partial shiny screen driver using the Win32 API.
|
||||
// It provides window, lifecycle, key, and mouse management, but no drawing.
|
||||
// That is left to windriver (using GDI) or gldriver (using DirectX via ANGLE).
|
||||
package win32 // import "golang.org/x/exp/shiny/driver/internal/win32"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/exp/shiny/screen"
|
||||
"golang.org/x/mobile/event/key"
|
||||
"golang.org/x/mobile/event/lifecycle"
|
||||
"golang.org/x/mobile/event/mouse"
|
||||
"golang.org/x/mobile/event/paint"
|
||||
"golang.org/x/mobile/event/size"
|
||||
"golang.org/x/mobile/geom"
|
||||
)
|
||||
|
||||
// screenHWND is the handle to the "Screen window".
|
||||
// The Screen window encapsulates all screen.Screen operations
|
||||
// in an actual Windows window so they all run on the main thread.
|
||||
// Since any messages sent to a window will be executed on the
|
||||
// main thread, we can safely use the messages below.
|
||||
var screenHWND syscall.Handle
|
||||
|
||||
const (
|
||||
msgCreateWindow = _WM_USER + iota
|
||||
msgMainCallback
|
||||
msgShow
|
||||
msgQuit
|
||||
msgLast
|
||||
)
|
||||
|
||||
// userWM is used to generate private (WM_USER and above) window message IDs
|
||||
// for use by screenWindowWndProc and windowWndProc.
|
||||
type userWM struct {
|
||||
sync.Mutex
|
||||
id uint32
|
||||
}
|
||||
|
||||
func (m *userWM) next() uint32 {
|
||||
m.Lock()
|
||||
if m.id == 0 {
|
||||
m.id = msgLast
|
||||
}
|
||||
r := m.id
|
||||
m.id++
|
||||
m.Unlock()
|
||||
return r
|
||||
}
|
||||
|
||||
var currentUserWM userWM
|
||||
|
||||
func newWindow(opts *screen.NewWindowOptions) (syscall.Handle, error) {
|
||||
// TODO(brainman): convert windowClass to *uint16 once (in initWindowClass)
|
||||
wcname, err := syscall.UTF16PtrFromString(windowClass)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
title, err := syscall.UTF16PtrFromString(opts.GetTitle())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
hwnd, err := _CreateWindowEx(0,
|
||||
wcname, title,
|
||||
_WS_OVERLAPPEDWINDOW,
|
||||
_CW_USEDEFAULT, _CW_USEDEFAULT,
|
||||
_CW_USEDEFAULT, _CW_USEDEFAULT,
|
||||
0, 0, hThisInstance, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// TODO(andlabs): use proper nCmdShow
|
||||
// TODO(andlabs): call UpdateWindow()
|
||||
|
||||
return hwnd, nil
|
||||
}
|
||||
|
||||
// ResizeClientRect makes hwnd client rectangle opts.Width by opts.Height in size.
|
||||
func ResizeClientRect(hwnd syscall.Handle, opts *screen.NewWindowOptions) error {
|
||||
if opts == nil || opts.Width <= 0 || opts.Height <= 0 {
|
||||
return nil
|
||||
}
|
||||
var cr, wr _RECT
|
||||
err := _GetClientRect(hwnd, &cr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = _GetWindowRect(hwnd, &wr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w := (wr.Right - wr.Left) - (cr.Right - int32(opts.Width))
|
||||
h := (wr.Bottom - wr.Top) - (cr.Bottom - int32(opts.Height))
|
||||
return _MoveWindow(hwnd, wr.Left, wr.Top, w, h, false)
|
||||
}
|
||||
|
||||
// Show shows a newly created window.
|
||||
// It sends the appropriate lifecycle events, makes the window appear
|
||||
// on the screen, and sends an initial size event.
|
||||
//
|
||||
// This is a separate step from NewWindow to give the driver a chance
|
||||
// to setup its internal state for a window before events start being
|
||||
// delivered.
|
||||
func Show(hwnd syscall.Handle) {
|
||||
SendMessage(hwnd, msgShow, 0, 0)
|
||||
}
|
||||
|
||||
func Release(hwnd syscall.Handle) {
|
||||
SendMessage(hwnd, _WM_CLOSE, 0, 0)
|
||||
}
|
||||
|
||||
func sendFocus(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) (lResult uintptr) {
|
||||
switch uMsg {
|
||||
case _WM_SETFOCUS:
|
||||
LifecycleEvent(hwnd, lifecycle.StageFocused)
|
||||
case _WM_KILLFOCUS:
|
||||
LifecycleEvent(hwnd, lifecycle.StageVisible)
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected focus message: %d", uMsg))
|
||||
}
|
||||
return _DefWindowProc(hwnd, uMsg, wParam, lParam)
|
||||
}
|
||||
|
||||
func sendShow(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) (lResult uintptr) {
|
||||
LifecycleEvent(hwnd, lifecycle.StageVisible)
|
||||
_ShowWindow(hwnd, _SW_SHOWDEFAULT)
|
||||
sendSize(hwnd)
|
||||
return 0
|
||||
}
|
||||
|
||||
func sendSizeEvent(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) (lResult uintptr) {
|
||||
wp := (*_WINDOWPOS)(unsafe.Pointer(lParam))
|
||||
if wp.Flags&_SWP_NOSIZE != 0 {
|
||||
return 0
|
||||
}
|
||||
sendSize(hwnd)
|
||||
return 0
|
||||
}
|
||||
|
||||
func sendSize(hwnd syscall.Handle) {
|
||||
var r _RECT
|
||||
if err := _GetClientRect(hwnd, &r); err != nil {
|
||||
panic(err) // TODO(andlabs)
|
||||
}
|
||||
|
||||
width := int(r.Right - r.Left)
|
||||
height := int(r.Bottom - r.Top)
|
||||
|
||||
// TODO(andlabs): don't assume that PixelsPerPt == 1
|
||||
SizeEvent(hwnd, size.Event{
|
||||
WidthPx: width,
|
||||
HeightPx: height,
|
||||
WidthPt: geom.Pt(width),
|
||||
HeightPt: geom.Pt(height),
|
||||
PixelsPerPt: 1,
|
||||
})
|
||||
}
|
||||
|
||||
func sendClose(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) (lResult uintptr) {
|
||||
// TODO(ktye): DefWindowProc calls DestroyWindow by default.
|
||||
// To intercept destruction of the window, return 0 and call
|
||||
// DestroyWindow when appropriate.
|
||||
LifecycleEvent(hwnd, lifecycle.StageDead)
|
||||
return _DefWindowProc(hwnd, uMsg, wParam, lParam)
|
||||
}
|
||||
|
||||
func sendMouseEvent(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) (lResult uintptr) {
|
||||
e := mouse.Event{
|
||||
X: float32(_GET_X_LPARAM(lParam)),
|
||||
Y: float32(_GET_Y_LPARAM(lParam)),
|
||||
Modifiers: keyModifiers(),
|
||||
}
|
||||
|
||||
switch uMsg {
|
||||
case _WM_MOUSEMOVE:
|
||||
e.Direction = mouse.DirNone
|
||||
case _WM_LBUTTONDOWN, _WM_MBUTTONDOWN, _WM_RBUTTONDOWN:
|
||||
e.Direction = mouse.DirPress
|
||||
case _WM_LBUTTONUP, _WM_MBUTTONUP, _WM_RBUTTONUP:
|
||||
e.Direction = mouse.DirRelease
|
||||
case _WM_MOUSEWHEEL:
|
||||
// TODO: On a trackpad, a scroll can be a drawn-out affair with a
|
||||
// distinct beginning and end. Should the intermediate events be
|
||||
// DirNone?
|
||||
e.Direction = mouse.DirStep
|
||||
|
||||
// Convert from screen to window coordinates.
|
||||
p := _POINT{
|
||||
int32(e.X),
|
||||
int32(e.Y),
|
||||
}
|
||||
_ScreenToClient(hwnd, &p)
|
||||
e.X = float32(p.X)
|
||||
e.Y = float32(p.Y)
|
||||
default:
|
||||
panic("sendMouseEvent() called on non-mouse message")
|
||||
}
|
||||
|
||||
switch uMsg {
|
||||
case _WM_MOUSEMOVE:
|
||||
// No-op.
|
||||
case _WM_LBUTTONDOWN, _WM_LBUTTONUP:
|
||||
e.Button = mouse.ButtonLeft
|
||||
case _WM_MBUTTONDOWN, _WM_MBUTTONUP:
|
||||
e.Button = mouse.ButtonMiddle
|
||||
case _WM_RBUTTONDOWN, _WM_RBUTTONUP:
|
||||
e.Button = mouse.ButtonRight
|
||||
case _WM_MOUSEWHEEL:
|
||||
// TODO: handle horizontal scrolling
|
||||
delta := _GET_WHEEL_DELTA_WPARAM(wParam) / _WHEEL_DELTA
|
||||
switch {
|
||||
case delta > 0:
|
||||
e.Button = mouse.ButtonWheelUp
|
||||
case delta < 0:
|
||||
e.Button = mouse.ButtonWheelDown
|
||||
delta = -delta
|
||||
default:
|
||||
return
|
||||
}
|
||||
for delta > 0 {
|
||||
MouseEvent(hwnd, e)
|
||||
delta--
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
MouseEvent(hwnd, e)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// Precondition: this is called in immediate response to the message that triggered the event (so not after w.Send).
|
||||
func keyModifiers() (m key.Modifiers) {
|
||||
down := func(x int32) bool {
|
||||
// GetKeyState gets the key state at the time of the message, so this is what we want.
|
||||
return _GetKeyState(x)&0x80 != 0
|
||||
}
|
||||
|
||||
if down(_VK_CONTROL) {
|
||||
m |= key.ModControl
|
||||
}
|
||||
if down(_VK_MENU) {
|
||||
m |= key.ModAlt
|
||||
}
|
||||
if down(_VK_SHIFT) {
|
||||
m |= key.ModShift
|
||||
}
|
||||
if down(_VK_LWIN) || down(_VK_RWIN) {
|
||||
m |= key.ModMeta
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
var (
|
||||
MouseEvent func(hwnd syscall.Handle, e mouse.Event)
|
||||
PaintEvent func(hwnd syscall.Handle, e paint.Event)
|
||||
SizeEvent func(hwnd syscall.Handle, e size.Event)
|
||||
KeyEvent func(hwnd syscall.Handle, e key.Event)
|
||||
LifecycleEvent func(hwnd syscall.Handle, e lifecycle.Stage)
|
||||
|
||||
// TODO: use the golang.org/x/exp/shiny/driver/internal/lifecycler package
|
||||
// instead of or together with the LifecycleEvent callback?
|
||||
)
|
||||
|
||||
func sendPaint(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) (lResult uintptr) {
|
||||
PaintEvent(hwnd, paint.Event{})
|
||||
return _DefWindowProc(hwnd, uMsg, wParam, lParam)
|
||||
}
|
||||
|
||||
var screenMsgs = map[uint32]func(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) (lResult uintptr){}
|
||||
|
||||
func AddScreenMsg(fn func(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr)) uint32 {
|
||||
uMsg := currentUserWM.next()
|
||||
screenMsgs[uMsg] = func(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) uintptr {
|
||||
fn(hwnd, uMsg, wParam, lParam)
|
||||
return 0
|
||||
}
|
||||
return uMsg
|
||||
}
|
||||
|
||||
func screenWindowWndProc(hwnd syscall.Handle, uMsg uint32, wParam uintptr, lParam uintptr) (lResult uintptr) {
|
||||
switch uMsg {
|
||||
case msgCreateWindow:
|
||||
p := (*newWindowParams)(unsafe.Pointer(lParam))
|
||||
p.w, p.err = newWindow(p.opts)
|
||||
case msgMainCallback:
|
||||
go func() {
|
||||
mainCallback()
|
||||
SendScreenMessage(msgQuit, 0, 0)
|
||||
}()
|
||||
case msgQuit:
|
||||
_PostQuitMessage(0)
|
||||
}
|
||||
fn := screenMsgs[uMsg]
|
||||
if fn != nil {
|
||||
return fn(hwnd, uMsg, wParam, lParam)
|
||||
}
|
||||
return _DefWindowProc(hwnd, uMsg, wParam, lParam)
|
||||
}
|
||||
|
||||
//go:uintptrescapes
|
||||
|
||||
func SendScreenMessage(uMsg uint32, wParam uintptr, lParam uintptr) (lResult uintptr) {
|
||||
return SendMessage(screenHWND, uMsg, wParam, lParam)
|
||||
}
|
||||
|
||||
var windowMsgs = map[uint32]func(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) (lResult uintptr){
|
||||
_WM_SETFOCUS: sendFocus,
|
||||
_WM_KILLFOCUS: sendFocus,
|
||||
_WM_PAINT: sendPaint,
|
||||
msgShow: sendShow,
|
||||
_WM_WINDOWPOSCHANGED: sendSizeEvent,
|
||||
_WM_CLOSE: sendClose,
|
||||
|
||||
_WM_LBUTTONDOWN: sendMouseEvent,
|
||||
_WM_LBUTTONUP: sendMouseEvent,
|
||||
_WM_MBUTTONDOWN: sendMouseEvent,
|
||||
_WM_MBUTTONUP: sendMouseEvent,
|
||||
_WM_RBUTTONDOWN: sendMouseEvent,
|
||||
_WM_RBUTTONUP: sendMouseEvent,
|
||||
_WM_MOUSEMOVE: sendMouseEvent,
|
||||
_WM_MOUSEWHEEL: sendMouseEvent,
|
||||
|
||||
_WM_KEYDOWN: sendKeyEvent,
|
||||
_WM_KEYUP: sendKeyEvent,
|
||||
_WM_SYSKEYDOWN: sendKeyEvent,
|
||||
_WM_SYSKEYUP: sendKeyEvent,
|
||||
}
|
||||
|
||||
func AddWindowMsg(fn func(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr)) uint32 {
|
||||
uMsg := currentUserWM.next()
|
||||
windowMsgs[uMsg] = func(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) uintptr {
|
||||
fn(hwnd, uMsg, wParam, lParam)
|
||||
return 0
|
||||
}
|
||||
return uMsg
|
||||
}
|
||||
|
||||
func windowWndProc(hwnd syscall.Handle, uMsg uint32, wParam uintptr, lParam uintptr) (lResult uintptr) {
|
||||
fn := windowMsgs[uMsg]
|
||||
if fn != nil {
|
||||
return fn(hwnd, uMsg, wParam, lParam)
|
||||
}
|
||||
return _DefWindowProc(hwnd, uMsg, wParam, lParam)
|
||||
}
|
||||
|
||||
type newWindowParams struct {
|
||||
opts *screen.NewWindowOptions
|
||||
w syscall.Handle
|
||||
err error
|
||||
}
|
||||
|
||||
func NewWindow(opts *screen.NewWindowOptions) (syscall.Handle, error) {
|
||||
var p newWindowParams
|
||||
p.opts = opts
|
||||
SendScreenMessage(msgCreateWindow, 0, uintptr(unsafe.Pointer(&p)))
|
||||
return p.w, p.err
|
||||
}
|
||||
|
||||
const windowClass = "shiny_Window"
|
||||
const screenWindowClass = "shiny_ScreenWindow"
|
||||
|
||||
func initWindowClass() (err error) {
|
||||
wcname, err := syscall.UTF16PtrFromString(windowClass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = _RegisterClass(&_WNDCLASS{
|
||||
LpszClassName: wcname,
|
||||
LpfnWndProc: syscall.NewCallback(windowWndProc),
|
||||
HIcon: hDefaultIcon,
|
||||
HCursor: hDefaultCursor,
|
||||
HInstance: hThisInstance,
|
||||
// TODO(andlabs): change this to something else? NULL? the hollow brush?
|
||||
HbrBackground: syscall.Handle(_COLOR_BTNFACE + 1),
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func closeWindowClass() (err error) {
|
||||
wcname, err := syscall.UTF16PtrFromString(windowClass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_UnregisterClass(wcname, hThisInstance)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func initScreenWindow() (err error) {
|
||||
swc, err := syscall.UTF16PtrFromString(screenWindowClass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
emptyString, err := syscall.UTF16PtrFromString("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wc := _WNDCLASS{
|
||||
LpszClassName: swc,
|
||||
LpfnWndProc: syscall.NewCallback(screenWindowWndProc),
|
||||
HIcon: hDefaultIcon,
|
||||
HCursor: hDefaultCursor,
|
||||
HInstance: hThisInstance,
|
||||
HbrBackground: syscall.Handle(_COLOR_BTNFACE + 1),
|
||||
}
|
||||
_, err = _RegisterClass(&wc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
screenHWND, err = _CreateWindowEx(0,
|
||||
swc, emptyString,
|
||||
_WS_OVERLAPPEDWINDOW,
|
||||
_CW_USEDEFAULT, _CW_USEDEFAULT,
|
||||
_CW_USEDEFAULT, _CW_USEDEFAULT,
|
||||
_HWND_MESSAGE, 0, hThisInstance, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func closeScreenWindow() (err error) {
|
||||
// first destroy window
|
||||
_DestroyWindow(screenHWND)
|
||||
|
||||
// then unregister class
|
||||
swc, err := syscall.UTF16PtrFromString(screenWindowClass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_UnregisterClass(swc, hThisInstance)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
hDefaultIcon syscall.Handle
|
||||
hDefaultCursor syscall.Handle
|
||||
hThisInstance syscall.Handle
|
||||
)
|
||||
|
||||
func initCommon() (err error) {
|
||||
hDefaultIcon, err = _LoadIcon(0, _IDI_APPLICATION)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hDefaultCursor, err = _LoadCursor(0, _IDC_ARROW)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO(andlabs) hThisInstance
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:uintptrescapes
|
||||
|
||||
func SendMessage(hwnd syscall.Handle, uMsg uint32, wParam uintptr, lParam uintptr) (lResult uintptr) {
|
||||
return sendMessage(hwnd, uMsg, wParam, lParam)
|
||||
}
|
||||
|
||||
var mainCallback func()
|
||||
|
||||
func Main(f func()) (retErr error) {
|
||||
// It does not matter which OS thread we are on.
|
||||
// All that matters is that we confine all UI operations
|
||||
// to the thread that created the respective window.
|
||||
runtime.LockOSThread()
|
||||
|
||||
if err := initCommon(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := initScreenWindow(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
// TODO(andlabs): log an error if this fails?
|
||||
closeScreenWindow()
|
||||
}()
|
||||
|
||||
if err := initWindowClass(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
// TODO(andlabs): log an error if this fails?
|
||||
closeWindowClass()
|
||||
}()
|
||||
|
||||
// Prime the pump.
|
||||
mainCallback = f
|
||||
_PostMessage(screenHWND, msgMainCallback, 0, 0)
|
||||
|
||||
// Main message pump.
|
||||
var m _MSG
|
||||
for {
|
||||
done, err := _GetMessage(&m, 0, 0, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("win32 GetMessage failed: %v", err)
|
||||
}
|
||||
if done == 0 { // WM_QUIT
|
||||
break
|
||||
}
|
||||
_TranslateMessage(&m)
|
||||
_DispatchMessage(&m)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
293
vendor/golang.org/x/exp/shiny/driver/internal/win32/zsyscall_windows.go
generated
vendored
Normal file
293
vendor/golang.org/x/exp/shiny/driver/internal/win32/zsyscall_windows.go
generated
vendored
Normal file
@@ -0,0 +1,293 @@
|
||||
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
|
||||
|
||||
package win32
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
// Do the interface allocations only once for common
|
||||
// Errno values.
|
||||
const (
|
||||
errnoERROR_IO_PENDING = 997
|
||||
)
|
||||
|
||||
var (
|
||||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||
)
|
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
// allocations at runtime.
|
||||
func errnoErr(e syscall.Errno) error {
|
||||
switch e {
|
||||
case 0:
|
||||
return nil
|
||||
case errnoERROR_IO_PENDING:
|
||||
return errERROR_IO_PENDING
|
||||
}
|
||||
// TODO: add more here, after collecting data on the common
|
||||
// error values see on Windows. (perhaps when running
|
||||
// all.bat?)
|
||||
return e
|
||||
}
|
||||
|
||||
var (
|
||||
moduser32 = windows.NewLazySystemDLL("user32.dll")
|
||||
|
||||
procGetDC = moduser32.NewProc("GetDC")
|
||||
procReleaseDC = moduser32.NewProc("ReleaseDC")
|
||||
procSendMessageW = moduser32.NewProc("SendMessageW")
|
||||
procCreateWindowExW = moduser32.NewProc("CreateWindowExW")
|
||||
procDefWindowProcW = moduser32.NewProc("DefWindowProcW")
|
||||
procDestroyWindow = moduser32.NewProc("DestroyWindow")
|
||||
procDispatchMessageW = moduser32.NewProc("DispatchMessageW")
|
||||
procGetClientRect = moduser32.NewProc("GetClientRect")
|
||||
procGetWindowRect = moduser32.NewProc("GetWindowRect")
|
||||
procGetKeyboardLayout = moduser32.NewProc("GetKeyboardLayout")
|
||||
procGetKeyboardState = moduser32.NewProc("GetKeyboardState")
|
||||
procGetKeyState = moduser32.NewProc("GetKeyState")
|
||||
procGetMessageW = moduser32.NewProc("GetMessageW")
|
||||
procLoadCursorW = moduser32.NewProc("LoadCursorW")
|
||||
procLoadIconW = moduser32.NewProc("LoadIconW")
|
||||
procMoveWindow = moduser32.NewProc("MoveWindow")
|
||||
procPostMessageW = moduser32.NewProc("PostMessageW")
|
||||
procPostQuitMessage = moduser32.NewProc("PostQuitMessage")
|
||||
procRegisterClassW = moduser32.NewProc("RegisterClassW")
|
||||
procShowWindow = moduser32.NewProc("ShowWindow")
|
||||
procScreenToClient = moduser32.NewProc("ScreenToClient")
|
||||
procToUnicodeEx = moduser32.NewProc("ToUnicodeEx")
|
||||
procTranslateMessage = moduser32.NewProc("TranslateMessage")
|
||||
procUnregisterClassW = moduser32.NewProc("UnregisterClassW")
|
||||
)
|
||||
|
||||
func GetDC(hwnd syscall.Handle) (dc syscall.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procGetDC.Addr(), 1, uintptr(hwnd), 0, 0)
|
||||
dc = syscall.Handle(r0)
|
||||
if dc == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ReleaseDC(hwnd syscall.Handle, dc syscall.Handle) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procReleaseDC.Addr(), 2, uintptr(hwnd), uintptr(dc), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func sendMessage(hwnd syscall.Handle, uMsg uint32, wParam uintptr, lParam uintptr) (lResult uintptr) {
|
||||
r0, _, _ := syscall.Syscall6(procSendMessageW.Addr(), 4, uintptr(hwnd), uintptr(uMsg), uintptr(wParam), uintptr(lParam), 0, 0)
|
||||
lResult = uintptr(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func _CreateWindowEx(exstyle uint32, className *uint16, windowText *uint16, style uint32, x int32, y int32, width int32, height int32, parent syscall.Handle, menu syscall.Handle, hInstance syscall.Handle, lpParam uintptr) (hwnd syscall.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall12(procCreateWindowExW.Addr(), 12, uintptr(exstyle), uintptr(unsafe.Pointer(className)), uintptr(unsafe.Pointer(windowText)), uintptr(style), uintptr(x), uintptr(y), uintptr(width), uintptr(height), uintptr(parent), uintptr(menu), uintptr(hInstance), uintptr(lpParam))
|
||||
hwnd = syscall.Handle(r0)
|
||||
if hwnd == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _DefWindowProc(hwnd syscall.Handle, uMsg uint32, wParam uintptr, lParam uintptr) (lResult uintptr) {
|
||||
r0, _, _ := syscall.Syscall6(procDefWindowProcW.Addr(), 4, uintptr(hwnd), uintptr(uMsg), uintptr(wParam), uintptr(lParam), 0, 0)
|
||||
lResult = uintptr(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func _DestroyWindow(hwnd syscall.Handle) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procDestroyWindow.Addr(), 1, uintptr(hwnd), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _DispatchMessage(msg *_MSG) (ret int32) {
|
||||
r0, _, _ := syscall.Syscall(procDispatchMessageW.Addr(), 1, uintptr(unsafe.Pointer(msg)), 0, 0)
|
||||
ret = int32(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func _GetClientRect(hwnd syscall.Handle, rect *_RECT) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procGetClientRect.Addr(), 2, uintptr(hwnd), uintptr(unsafe.Pointer(rect)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _GetWindowRect(hwnd syscall.Handle, rect *_RECT) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procGetWindowRect.Addr(), 2, uintptr(hwnd), uintptr(unsafe.Pointer(rect)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _GetKeyboardLayout(threadID uint32) (locale syscall.Handle) {
|
||||
r0, _, _ := syscall.Syscall(procGetKeyboardLayout.Addr(), 1, uintptr(threadID), 0, 0)
|
||||
locale = syscall.Handle(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func _GetKeyboardState(lpKeyState *byte) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procGetKeyboardState.Addr(), 1, uintptr(unsafe.Pointer(lpKeyState)), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _GetKeyState(virtkey int32) (keystatus int16) {
|
||||
r0, _, _ := syscall.Syscall(procGetKeyState.Addr(), 1, uintptr(virtkey), 0, 0)
|
||||
keystatus = int16(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func _GetMessage(msg *_MSG, hwnd syscall.Handle, msgfiltermin uint32, msgfiltermax uint32) (ret int32, err error) {
|
||||
r0, _, e1 := syscall.Syscall6(procGetMessageW.Addr(), 4, uintptr(unsafe.Pointer(msg)), uintptr(hwnd), uintptr(msgfiltermin), uintptr(msgfiltermax), 0, 0)
|
||||
ret = int32(r0)
|
||||
if ret == -1 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _LoadCursor(hInstance syscall.Handle, cursorName uintptr) (cursor syscall.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procLoadCursorW.Addr(), 2, uintptr(hInstance), uintptr(cursorName), 0)
|
||||
cursor = syscall.Handle(r0)
|
||||
if cursor == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _LoadIcon(hInstance syscall.Handle, iconName uintptr) (icon syscall.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procLoadIconW.Addr(), 2, uintptr(hInstance), uintptr(iconName), 0)
|
||||
icon = syscall.Handle(r0)
|
||||
if icon == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _MoveWindow(hwnd syscall.Handle, x int32, y int32, w int32, h int32, repaint bool) (err error) {
|
||||
var _p0 uint32
|
||||
if repaint {
|
||||
_p0 = 1
|
||||
} else {
|
||||
_p0 = 0
|
||||
}
|
||||
r1, _, e1 := syscall.Syscall6(procMoveWindow.Addr(), 6, uintptr(hwnd), uintptr(x), uintptr(y), uintptr(w), uintptr(h), uintptr(_p0))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _PostMessage(hwnd syscall.Handle, uMsg uint32, wParam uintptr, lParam uintptr) (lResult bool) {
|
||||
r0, _, _ := syscall.Syscall6(procPostMessageW.Addr(), 4, uintptr(hwnd), uintptr(uMsg), uintptr(wParam), uintptr(lParam), 0, 0)
|
||||
lResult = r0 != 0
|
||||
return
|
||||
}
|
||||
|
||||
func _PostQuitMessage(exitCode int32) {
|
||||
syscall.Syscall(procPostQuitMessage.Addr(), 1, uintptr(exitCode), 0, 0)
|
||||
return
|
||||
}
|
||||
|
||||
func _RegisterClass(wc *_WNDCLASS) (atom uint16, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procRegisterClassW.Addr(), 1, uintptr(unsafe.Pointer(wc)), 0, 0)
|
||||
atom = uint16(r0)
|
||||
if atom == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _ShowWindow(hwnd syscall.Handle, cmdshow int32) (wasvisible bool) {
|
||||
r0, _, _ := syscall.Syscall(procShowWindow.Addr(), 2, uintptr(hwnd), uintptr(cmdshow), 0)
|
||||
wasvisible = r0 != 0
|
||||
return
|
||||
}
|
||||
|
||||
func _ScreenToClient(hwnd syscall.Handle, lpPoint *_POINT) (ok bool) {
|
||||
r0, _, _ := syscall.Syscall(procScreenToClient.Addr(), 2, uintptr(hwnd), uintptr(unsafe.Pointer(lpPoint)), 0)
|
||||
ok = r0 != 0
|
||||
return
|
||||
}
|
||||
|
||||
func _ToUnicodeEx(wVirtKey uint32, wScanCode uint32, lpKeyState *byte, pwszBuff *uint16, cchBuff int32, wFlags uint32, dwhkl syscall.Handle) (ret int32) {
|
||||
r0, _, _ := syscall.Syscall9(procToUnicodeEx.Addr(), 7, uintptr(wVirtKey), uintptr(wScanCode), uintptr(unsafe.Pointer(lpKeyState)), uintptr(unsafe.Pointer(pwszBuff)), uintptr(cchBuff), uintptr(wFlags), uintptr(dwhkl), 0, 0)
|
||||
ret = int32(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func _TranslateMessage(msg *_MSG) (done bool) {
|
||||
r0, _, _ := syscall.Syscall(procTranslateMessage.Addr(), 1, uintptr(unsafe.Pointer(msg)), 0, 0)
|
||||
done = r0 != 0
|
||||
return
|
||||
}
|
||||
|
||||
func _UnregisterClass(lpClassName *uint16, hInstance syscall.Handle) (done bool) {
|
||||
r0, _, _ := syscall.Syscall(procUnregisterClassW.Addr(), 2, uintptr(unsafe.Pointer(lpClassName)), uintptr(hInstance), 0)
|
||||
done = r0 != 0
|
||||
return
|
||||
}
|
||||
1577
vendor/golang.org/x/exp/shiny/driver/internal/x11key/table.go
generated
vendored
Normal file
1577
vendor/golang.org/x/exp/shiny/driver/internal/x11key/table.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
323
vendor/golang.org/x/exp/shiny/driver/internal/x11key/x11key.go
generated
vendored
Normal file
323
vendor/golang.org/x/exp/shiny/driver/internal/x11key/x11key.go
generated
vendored
Normal file
@@ -0,0 +1,323 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:generate go run gen.go
|
||||
|
||||
// x11key contains X11 numeric codes for the keyboard and mouse.
|
||||
package x11key // import "golang.org/x/exp/shiny/driver/internal/x11key"
|
||||
|
||||
import (
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/mobile/event/key"
|
||||
)
|
||||
|
||||
// These constants come from /usr/include/X11/X.h
|
||||
const (
|
||||
ShiftMask = 1 << 0
|
||||
LockMask = 1 << 1
|
||||
ControlMask = 1 << 2
|
||||
Mod1Mask = 1 << 3
|
||||
Mod2Mask = 1 << 4
|
||||
Mod3Mask = 1 << 5
|
||||
Mod4Mask = 1 << 6
|
||||
Mod5Mask = 1 << 7
|
||||
Button1Mask = 1 << 8
|
||||
Button2Mask = 1 << 9
|
||||
Button3Mask = 1 << 10
|
||||
Button4Mask = 1 << 11
|
||||
Button5Mask = 1 << 12
|
||||
)
|
||||
|
||||
type KeysymTable struct {
|
||||
Table [256][6]uint32
|
||||
|
||||
NumLockMod, ModeSwitchMod, ISOLevel3ShiftMod uint16
|
||||
}
|
||||
|
||||
func (t *KeysymTable) Lookup(detail uint8, state uint16) (rune, key.Code) {
|
||||
te := t.Table[detail][0:2]
|
||||
if state&t.ModeSwitchMod != 0 {
|
||||
te = t.Table[detail][2:4]
|
||||
}
|
||||
if state&t.ISOLevel3ShiftMod != 0 {
|
||||
te = t.Table[detail][4:6]
|
||||
}
|
||||
|
||||
// The key event's rune depends on whether the shift key is down.
|
||||
unshifted := rune(te[0])
|
||||
r := unshifted
|
||||
if state&t.NumLockMod != 0 && isKeypad(te[1]) {
|
||||
if state&ShiftMask == 0 {
|
||||
r = rune(te[1])
|
||||
}
|
||||
} else if state&ShiftMask != 0 {
|
||||
r = rune(te[1])
|
||||
// In X11, a zero keysym when shift is down means to use what the
|
||||
// keysym is when shift is up.
|
||||
if r == 0 {
|
||||
r = unshifted
|
||||
}
|
||||
}
|
||||
|
||||
// The key event's code is independent of whether the shift key is down.
|
||||
var c key.Code
|
||||
if 0 <= unshifted && unshifted < 0x80 {
|
||||
c = asciiKeycodes[unshifted]
|
||||
if state&LockMask != 0 {
|
||||
r = unicode.ToUpper(r)
|
||||
}
|
||||
} else if kk, isKeypad := keypadKeysyms[r]; isKeypad {
|
||||
r, c = kk.rune, kk.code
|
||||
} else if nuk := nonUnicodeKeycodes[unshifted]; nuk != key.CodeUnknown {
|
||||
r, c = -1, nuk
|
||||
} else {
|
||||
r = keysymCodePoints[r]
|
||||
if state&LockMask != 0 {
|
||||
r = unicode.ToUpper(r)
|
||||
}
|
||||
}
|
||||
|
||||
return r, c
|
||||
}
|
||||
|
||||
func isKeypad(keysym uint32) bool {
|
||||
return keysym >= 0xff80 && keysym <= 0xffbd
|
||||
}
|
||||
|
||||
func KeyModifiers(state uint16) (m key.Modifiers) {
|
||||
if state&ShiftMask != 0 {
|
||||
m |= key.ModShift
|
||||
}
|
||||
if state&ControlMask != 0 {
|
||||
m |= key.ModControl
|
||||
}
|
||||
if state&Mod1Mask != 0 {
|
||||
m |= key.ModAlt
|
||||
}
|
||||
if state&Mod4Mask != 0 {
|
||||
m |= key.ModMeta
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// These constants come from /usr/include/X11/{keysymdef,XF86keysym}.h
|
||||
const (
|
||||
xkISOLeftTab = 0xfe20
|
||||
xkBackSpace = 0xff08
|
||||
xkTab = 0xff09
|
||||
xkReturn = 0xff0d
|
||||
xkEscape = 0xff1b
|
||||
xkMultiKey = 0xff20
|
||||
xkHome = 0xff50
|
||||
xkLeft = 0xff51
|
||||
xkUp = 0xff52
|
||||
xkRight = 0xff53
|
||||
xkDown = 0xff54
|
||||
xkPageUp = 0xff55
|
||||
xkPageDown = 0xff56
|
||||
xkEnd = 0xff57
|
||||
xkInsert = 0xff63
|
||||
xkMenu = 0xff67
|
||||
xkHelp = 0xff6a
|
||||
|
||||
xkNumLock = 0xff7f
|
||||
xkKeypadEnter = 0xff8d
|
||||
xkKeypadHome = 0xff95
|
||||
xkKeypadLeft = 0xff96
|
||||
xkKeypadUp = 0xff97
|
||||
xkKeypadRight = 0xff98
|
||||
xkKeypadDown = 0xff99
|
||||
xkKeypadPageUp = 0xff9a
|
||||
xkKeypadPageDown = 0xff9b
|
||||
xkKeypadEnd = 0xff9c
|
||||
xkKeypadInsert = 0xff9e
|
||||
xkKeypadDelete = 0xff9f
|
||||
xkKeypadEqual = 0xffbd
|
||||
xkKeypadMultiply = 0xffaa
|
||||
xkKeypadAdd = 0xffab
|
||||
xkKeypadSubtract = 0xffad
|
||||
xkKeypadDecimal = 0xffae
|
||||
xkKeypadDivide = 0xffaf
|
||||
xkKeypad0 = 0xffb0
|
||||
xkKeypad1 = 0xffb1
|
||||
xkKeypad2 = 0xffb2
|
||||
xkKeypad3 = 0xffb3
|
||||
xkKeypad4 = 0xffb4
|
||||
xkKeypad5 = 0xffb5
|
||||
xkKeypad6 = 0xffb6
|
||||
xkKeypad7 = 0xffb7
|
||||
xkKeypad8 = 0xffb8
|
||||
xkKeypad9 = 0xffb9
|
||||
|
||||
xkF1 = 0xffbe
|
||||
xkF2 = 0xffbf
|
||||
xkF3 = 0xffc0
|
||||
xkF4 = 0xffc1
|
||||
xkF5 = 0xffc2
|
||||
xkF6 = 0xffc3
|
||||
xkF7 = 0xffc4
|
||||
xkF8 = 0xffc5
|
||||
xkF9 = 0xffc6
|
||||
xkF10 = 0xffc7
|
||||
xkF11 = 0xffc8
|
||||
xkF12 = 0xffc9
|
||||
xkShiftL = 0xffe1
|
||||
xkShiftR = 0xffe2
|
||||
xkControlL = 0xffe3
|
||||
xkControlR = 0xffe4
|
||||
xkCapsLock = 0xffe5
|
||||
xkAltL = 0xffe9
|
||||
xkAltR = 0xffea
|
||||
xkSuperL = 0xffeb
|
||||
xkSuperR = 0xffec
|
||||
xkDelete = 0xffff
|
||||
|
||||
xf86xkAudioLowerVolume = 0x1008ff11
|
||||
xf86xkAudioMute = 0x1008ff12
|
||||
xf86xkAudioRaiseVolume = 0x1008ff13
|
||||
)
|
||||
|
||||
// nonUnicodeKeycodes maps from those xproto.Keysym values (converted to runes)
|
||||
// that do not correspond to a Unicode code point, such as "Page Up", "F1" or
|
||||
// "Left Shift", to key.Code values.
|
||||
var nonUnicodeKeycodes = map[rune]key.Code{
|
||||
xkISOLeftTab: key.CodeTab,
|
||||
xkBackSpace: key.CodeDeleteBackspace,
|
||||
xkTab: key.CodeTab,
|
||||
xkReturn: key.CodeReturnEnter,
|
||||
xkEscape: key.CodeEscape,
|
||||
xkHome: key.CodeHome,
|
||||
xkLeft: key.CodeLeftArrow,
|
||||
xkUp: key.CodeUpArrow,
|
||||
xkRight: key.CodeRightArrow,
|
||||
xkDown: key.CodeDownArrow,
|
||||
xkPageUp: key.CodePageUp,
|
||||
xkPageDown: key.CodePageDown,
|
||||
xkEnd: key.CodeEnd,
|
||||
xkInsert: key.CodeInsert,
|
||||
xkMenu: key.CodeRightGUI, // TODO: CodeRightGUI or CodeMenu??
|
||||
xkHelp: key.CodeHelp,
|
||||
xkNumLock: key.CodeKeypadNumLock,
|
||||
xkMultiKey: key.CodeCompose,
|
||||
|
||||
xkKeypadEnter: key.CodeKeypadEnter,
|
||||
xkKeypadHome: key.CodeHome,
|
||||
xkKeypadLeft: key.CodeLeftArrow,
|
||||
xkKeypadUp: key.CodeUpArrow,
|
||||
xkKeypadRight: key.CodeRightArrow,
|
||||
xkKeypadDown: key.CodeDownArrow,
|
||||
xkKeypadPageUp: key.CodePageUp,
|
||||
xkKeypadPageDown: key.CodePageDown,
|
||||
xkKeypadEnd: key.CodeEnd,
|
||||
xkKeypadInsert: key.CodeInsert,
|
||||
xkKeypadDelete: key.CodeDeleteForward,
|
||||
|
||||
xkF1: key.CodeF1,
|
||||
xkF2: key.CodeF2,
|
||||
xkF3: key.CodeF3,
|
||||
xkF4: key.CodeF4,
|
||||
xkF5: key.CodeF5,
|
||||
xkF6: key.CodeF6,
|
||||
xkF7: key.CodeF7,
|
||||
xkF8: key.CodeF8,
|
||||
xkF9: key.CodeF9,
|
||||
xkF10: key.CodeF10,
|
||||
xkF11: key.CodeF11,
|
||||
xkF12: key.CodeF12,
|
||||
|
||||
xkShiftL: key.CodeLeftShift,
|
||||
xkShiftR: key.CodeRightShift,
|
||||
xkControlL: key.CodeLeftControl,
|
||||
xkControlR: key.CodeRightControl,
|
||||
xkCapsLock: key.CodeCapsLock,
|
||||
xkAltL: key.CodeLeftAlt,
|
||||
xkAltR: key.CodeRightAlt,
|
||||
xkSuperL: key.CodeLeftGUI,
|
||||
xkSuperR: key.CodeRightGUI,
|
||||
|
||||
xkDelete: key.CodeDeleteForward,
|
||||
|
||||
xf86xkAudioRaiseVolume: key.CodeVolumeUp,
|
||||
xf86xkAudioLowerVolume: key.CodeVolumeDown,
|
||||
xf86xkAudioMute: key.CodeMute,
|
||||
}
|
||||
|
||||
// asciiKeycodes maps lower-case ASCII runes to key.Code values.
|
||||
var asciiKeycodes = [0x80]key.Code{
|
||||
'a': key.CodeA,
|
||||
'b': key.CodeB,
|
||||
'c': key.CodeC,
|
||||
'd': key.CodeD,
|
||||
'e': key.CodeE,
|
||||
'f': key.CodeF,
|
||||
'g': key.CodeG,
|
||||
'h': key.CodeH,
|
||||
'i': key.CodeI,
|
||||
'j': key.CodeJ,
|
||||
'k': key.CodeK,
|
||||
'l': key.CodeL,
|
||||
'm': key.CodeM,
|
||||
'n': key.CodeN,
|
||||
'o': key.CodeO,
|
||||
'p': key.CodeP,
|
||||
'q': key.CodeQ,
|
||||
'r': key.CodeR,
|
||||
's': key.CodeS,
|
||||
't': key.CodeT,
|
||||
'u': key.CodeU,
|
||||
'v': key.CodeV,
|
||||
'w': key.CodeW,
|
||||
'x': key.CodeX,
|
||||
'y': key.CodeY,
|
||||
'z': key.CodeZ,
|
||||
|
||||
'1': key.Code1,
|
||||
'2': key.Code2,
|
||||
'3': key.Code3,
|
||||
'4': key.Code4,
|
||||
'5': key.Code5,
|
||||
'6': key.Code6,
|
||||
'7': key.Code7,
|
||||
'8': key.Code8,
|
||||
'9': key.Code9,
|
||||
'0': key.Code0,
|
||||
|
||||
' ': key.CodeSpacebar,
|
||||
'-': key.CodeHyphenMinus,
|
||||
'=': key.CodeEqualSign,
|
||||
'[': key.CodeLeftSquareBracket,
|
||||
']': key.CodeRightSquareBracket,
|
||||
'\\': key.CodeBackslash,
|
||||
';': key.CodeSemicolon,
|
||||
'\'': key.CodeApostrophe,
|
||||
'`': key.CodeGraveAccent,
|
||||
',': key.CodeComma,
|
||||
'.': key.CodeFullStop,
|
||||
'/': key.CodeSlash,
|
||||
}
|
||||
|
||||
type keypadKeysym struct {
|
||||
rune rune
|
||||
code key.Code
|
||||
}
|
||||
|
||||
var keypadKeysyms = map[rune]keypadKeysym{
|
||||
xkKeypadEqual: {'=', key.CodeKeypadEqualSign},
|
||||
xkKeypadMultiply: {'*', key.CodeKeypadAsterisk},
|
||||
xkKeypadAdd: {'+', key.CodeKeypadPlusSign},
|
||||
xkKeypadSubtract: {'-', key.CodeKeypadHyphenMinus},
|
||||
xkKeypadDecimal: {'.', key.CodeKeypadFullStop},
|
||||
xkKeypadDivide: {'/', key.CodeKeypadSlash},
|
||||
xkKeypad0: {'0', key.CodeKeypad0},
|
||||
xkKeypad1: {'1', key.CodeKeypad1},
|
||||
xkKeypad2: {'2', key.CodeKeypad2},
|
||||
xkKeypad3: {'3', key.CodeKeypad3},
|
||||
xkKeypad4: {'4', key.CodeKeypad4},
|
||||
xkKeypad5: {'5', key.CodeKeypad5},
|
||||
xkKeypad6: {'6', key.CodeKeypad6},
|
||||
xkKeypad7: {'7', key.CodeKeypad7},
|
||||
xkKeypad8: {'8', key.CodeKeypad8},
|
||||
xkKeypad9: {'9', key.CodeKeypad9},
|
||||
}
|
||||
354
vendor/golang.org/x/exp/shiny/screen/screen.go
generated
vendored
Normal file
354
vendor/golang.org/x/exp/shiny/screen/screen.go
generated
vendored
Normal file
@@ -0,0 +1,354 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package screen provides interfaces for portable two-dimensional graphics and
|
||||
// input events.
|
||||
//
|
||||
// Screens are not created directly. Instead, driver packages provide access to
|
||||
// the screen through a Main function that is designed to be called by the
|
||||
// program's main function. The golang.org/x/exp/shiny/driver package provides
|
||||
// the default driver for the system, such as the X11 driver for desktop Linux,
|
||||
// but other drivers, such as the OpenGL driver, can be explicitly invoked by
|
||||
// calling that driver's Main function. To use the default driver:
|
||||
//
|
||||
// package main
|
||||
//
|
||||
// import (
|
||||
// "golang.org/x/exp/shiny/driver"
|
||||
// "golang.org/x/exp/shiny/screen"
|
||||
// "golang.org/x/mobile/event/lifecycle"
|
||||
// )
|
||||
//
|
||||
// func main() {
|
||||
// driver.Main(func(s screen.Screen) {
|
||||
// w, err := s.NewWindow(nil)
|
||||
// if err != nil {
|
||||
// handleError(err)
|
||||
// return
|
||||
// }
|
||||
// defer w.Release()
|
||||
//
|
||||
// for {
|
||||
// switch e := w.NextEvent().(type) {
|
||||
// case lifecycle.Event:
|
||||
// if e.To == lifecycle.StageDead {
|
||||
// return
|
||||
// }
|
||||
// etc
|
||||
// case etc:
|
||||
// etc
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// Complete examples can be found in the shiny/example directory.
|
||||
//
|
||||
// Each driver package provides Screen, Buffer, Texture and Window
|
||||
// implementations that work together. Such types are interface types because
|
||||
// this package is driver-independent, but those interfaces aren't expected to
|
||||
// be implemented outside of drivers. For example, a driver's Window
|
||||
// implementation will generally work only with that driver's Buffer
|
||||
// implementation, and will not work with an arbitrary type that happens to
|
||||
// implement the Buffer methods.
|
||||
package screen // import "golang.org/x/exp/shiny/screen"
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/image/math/f64"
|
||||
)
|
||||
|
||||
// TODO: specify image format (Alpha or Gray, not just RGBA) for NewBuffer
|
||||
// and/or NewTexture?
|
||||
|
||||
// Screen creates Buffers, Textures and Windows.
|
||||
type Screen interface {
|
||||
// NewBuffer returns a new Buffer for this screen.
|
||||
NewBuffer(size image.Point) (Buffer, error)
|
||||
|
||||
// NewTexture returns a new Texture for this screen.
|
||||
NewTexture(size image.Point) (Texture, error)
|
||||
|
||||
// NewWindow returns a new Window for this screen.
|
||||
//
|
||||
// A nil opts is valid and means to use the default option values.
|
||||
NewWindow(opts *NewWindowOptions) (Window, error)
|
||||
}
|
||||
|
||||
// TODO: rename Buffer to Image, to be less confusing with a Window's back and
|
||||
// front buffers.
|
||||
|
||||
// Buffer is an in-memory pixel buffer. Its pixels can be modified by any Go
|
||||
// code that takes an *image.RGBA, such as the standard library's image/draw
|
||||
// package. A Buffer is essentially an *image.RGBA, but not all *image.RGBA
|
||||
// values (including those returned by image.NewRGBA) are valid Buffers, as a
|
||||
// driver may assume that the memory backing a Buffer's pixels are specially
|
||||
// allocated.
|
||||
//
|
||||
// To see a Buffer's contents on a screen, upload it to a Texture (and then
|
||||
// draw the Texture on a Window) or upload it directly to a Window.
|
||||
//
|
||||
// When specifying a sub-Buffer via Upload, a Buffer's top-left pixel is always
|
||||
// (0, 0) in its own coordinate space.
|
||||
type Buffer interface {
|
||||
// Release releases the Buffer's resources, after all pending uploads and
|
||||
// draws resolve.
|
||||
//
|
||||
// The behavior of the Buffer after Release, whether calling its methods or
|
||||
// passing it as an argument, is undefined.
|
||||
Release()
|
||||
|
||||
// Size returns the size of the Buffer's image.
|
||||
Size() image.Point
|
||||
|
||||
// Bounds returns the bounds of the Buffer's image. It is equal to
|
||||
// image.Rectangle{Max: b.Size()}.
|
||||
Bounds() image.Rectangle
|
||||
|
||||
// RGBA returns the pixel buffer as an *image.RGBA.
|
||||
//
|
||||
// Its contents should not be accessed while the Buffer is uploading.
|
||||
//
|
||||
// The contents of the returned *image.RGBA's Pix field (of type []byte)
|
||||
// can be modified at other times, but that Pix slice itself (i.e. its
|
||||
// underlying pointer, length and capacity) should not be modified at any
|
||||
// time.
|
||||
//
|
||||
// The following is valid:
|
||||
// m := buffer.RGBA()
|
||||
// if len(m.Pix) >= 4 {
|
||||
// m.Pix[0] = 0xff
|
||||
// m.Pix[1] = 0x00
|
||||
// m.Pix[2] = 0x00
|
||||
// m.Pix[3] = 0xff
|
||||
// }
|
||||
// or, equivalently:
|
||||
// m := buffer.RGBA()
|
||||
// m.SetRGBA(m.Rect.Min.X, m.Rect.Min.Y, color.RGBA{0xff, 0x00, 0x00, 0xff})
|
||||
// and using the standard library's image/draw package is also valid:
|
||||
// dst := buffer.RGBA()
|
||||
// draw.Draw(dst, dst.Bounds(), etc)
|
||||
// but the following is invalid:
|
||||
// m := buffer.RGBA()
|
||||
// m.Pix = anotherByteSlice
|
||||
// and so is this:
|
||||
// *buffer.RGBA() = anotherImageRGBA
|
||||
RGBA() *image.RGBA
|
||||
}
|
||||
|
||||
// Texture is a pixel buffer, but not one that is directly accessible as a
|
||||
// []byte. Conceptually, it could live on a GPU, in another process or even be
|
||||
// across a network, instead of on a CPU in this process.
|
||||
//
|
||||
// Buffers can be uploaded to Textures, and Textures can be drawn on Windows.
|
||||
//
|
||||
// When specifying a sub-Texture via Draw, a Texture's top-left pixel is always
|
||||
// (0, 0) in its own coordinate space.
|
||||
type Texture interface {
|
||||
// Release releases the Texture's resources, after all pending uploads and
|
||||
// draws resolve.
|
||||
//
|
||||
// The behavior of the Texture after Release, whether calling its methods
|
||||
// or passing it as an argument, is undefined.
|
||||
Release()
|
||||
|
||||
// Size returns the size of the Texture's image.
|
||||
Size() image.Point
|
||||
|
||||
// Bounds returns the bounds of the Texture's image. It is equal to
|
||||
// image.Rectangle{Max: t.Size()}.
|
||||
Bounds() image.Rectangle
|
||||
|
||||
Uploader
|
||||
|
||||
// TODO: also implement Drawer? If so, merge the Uploader and Drawer
|
||||
// interfaces??
|
||||
}
|
||||
|
||||
// EventDeque is an infinitely buffered double-ended queue of events.
|
||||
type EventDeque interface {
|
||||
// Send adds an event to the end of the deque. They are returned by
|
||||
// NextEvent in FIFO order.
|
||||
Send(event interface{})
|
||||
|
||||
// SendFirst adds an event to the start of the deque. They are returned by
|
||||
// NextEvent in LIFO order, and have priority over events sent via Send.
|
||||
SendFirst(event interface{})
|
||||
|
||||
// NextEvent returns the next event in the deque. It blocks until such an
|
||||
// event has been sent.
|
||||
//
|
||||
// Typical event types include:
|
||||
// - lifecycle.Event
|
||||
// - size.Event
|
||||
// - paint.Event
|
||||
// - key.Event
|
||||
// - mouse.Event
|
||||
// - touch.Event
|
||||
// from the golang.org/x/mobile/event/... packages. Other packages may send
|
||||
// events, of those types above or of other types, via Send or SendFirst.
|
||||
NextEvent() interface{}
|
||||
|
||||
// TODO: LatestLifecycleEvent? Is that still worth it if the
|
||||
// lifecycle.Event struct type loses its DrawContext field?
|
||||
|
||||
// TODO: LatestSizeEvent?
|
||||
}
|
||||
|
||||
// Window is a top-level, double-buffered GUI window.
|
||||
type Window interface {
|
||||
// Release closes the window.
|
||||
//
|
||||
// The behavior of the Window after Release, whether calling its methods or
|
||||
// passing it as an argument, is undefined.
|
||||
Release()
|
||||
|
||||
EventDeque
|
||||
|
||||
Uploader
|
||||
|
||||
Drawer
|
||||
|
||||
// Publish flushes any pending Upload and Draw calls to the window, and
|
||||
// swaps the back buffer to the front.
|
||||
Publish() PublishResult
|
||||
}
|
||||
|
||||
// PublishResult is the result of an Window.Publish call.
|
||||
type PublishResult struct {
|
||||
// BackBufferPreserved is whether the contents of the back buffer was
|
||||
// preserved. If false, the contents are undefined.
|
||||
BackBufferPreserved bool
|
||||
}
|
||||
|
||||
// NewWindowOptions are optional arguments to NewWindow.
|
||||
type NewWindowOptions struct {
|
||||
// Width and Height specify the dimensions of the new window. If Width
|
||||
// or Height are zero, a driver-dependent default will be used for each
|
||||
// zero value dimension.
|
||||
Width, Height int
|
||||
|
||||
// Title specifies the window title.
|
||||
Title string
|
||||
|
||||
// TODO: fullscreen, icon, cursorHidden?
|
||||
}
|
||||
|
||||
// GetTitle returns a sanitized form of o.Title. In particular, its length will
|
||||
// not exceed 4096, and it may be further truncated so that it is valid UTF-8
|
||||
// and will not contain the NUL byte.
|
||||
//
|
||||
// o may be nil, in which case "" is returned.
|
||||
func (o *NewWindowOptions) GetTitle() string {
|
||||
if o == nil {
|
||||
return ""
|
||||
}
|
||||
return sanitizeUTF8(o.Title, 4096)
|
||||
}
|
||||
|
||||
func sanitizeUTF8(s string, n int) string {
|
||||
if n < len(s) {
|
||||
s = s[:n]
|
||||
}
|
||||
i := 0
|
||||
for i < len(s) {
|
||||
r, n := utf8.DecodeRuneInString(s[i:])
|
||||
if r == 0 || (r == utf8.RuneError && n == 1) {
|
||||
break
|
||||
}
|
||||
i += n
|
||||
}
|
||||
return s[:i]
|
||||
}
|
||||
|
||||
// Uploader is something you can upload a Buffer to.
|
||||
type Uploader interface {
|
||||
// Upload uploads the sub-Buffer defined by src and sr to the destination
|
||||
// (the method receiver), such that sr.Min in src-space aligns with dp in
|
||||
// dst-space. The destination's contents are overwritten; the draw operator
|
||||
// is implicitly draw.Src.
|
||||
//
|
||||
// It is valid to upload a Buffer while another upload of the same Buffer
|
||||
// is in progress, but a Buffer's image.RGBA pixel contents should not be
|
||||
// accessed while it is uploading. A Buffer is re-usable, in that its pixel
|
||||
// contents can be further modified, once all outstanding calls to Upload
|
||||
// have returned.
|
||||
//
|
||||
// TODO: make it optional that a Buffer's contents is preserved after
|
||||
// Upload? Undoing a swizzle is a non-trivial amount of work, and can be
|
||||
// redundant if the next paint cycle starts by clearing the buffer.
|
||||
//
|
||||
// When uploading to a Window, there will not be any visible effect until
|
||||
// Publish is called.
|
||||
Upload(dp image.Point, src Buffer, sr image.Rectangle)
|
||||
|
||||
// Fill fills that part of the destination (the method receiver) defined by
|
||||
// dr with the given color.
|
||||
//
|
||||
// When filling a Window, there will not be any visible effect until
|
||||
// Publish is called.
|
||||
Fill(dr image.Rectangle, src color.Color, op draw.Op)
|
||||
}
|
||||
|
||||
// TODO: have a Downloader interface? Not every graphical app needs to be
|
||||
// interactive or involve a window. You could use the GPU for hardware-
|
||||
// accelerated image manipulation: upload a buffer, do some texture ops, then
|
||||
// download the result.
|
||||
|
||||
// Drawer is something you can draw Textures on.
|
||||
//
|
||||
// Draw is the most general purpose of this interface's methods. It supports
|
||||
// arbitrary affine transformations, such as translations, scales and
|
||||
// rotations.
|
||||
//
|
||||
// Copy and Scale are more specific versions of Draw. The affected dst pixels
|
||||
// are an axis-aligned rectangle, quantized to the pixel grid. Copy copies
|
||||
// pixels in a 1:1 manner, Scale is more general. They have simpler parameters
|
||||
// than Draw, using ints instead of float64s.
|
||||
//
|
||||
// When drawing on a Window, there will not be any visible effect until Publish
|
||||
// is called.
|
||||
type Drawer interface {
|
||||
// Draw draws the sub-Texture defined by src and sr to the destination (the
|
||||
// method receiver). src2dst defines how to transform src coordinates to
|
||||
// dst coordinates. For example, if src2dst is the matrix
|
||||
//
|
||||
// m00 m01 m02
|
||||
// m10 m11 m12
|
||||
//
|
||||
// then the src-space point (sx, sy) maps to the dst-space point
|
||||
// (m00*sx + m01*sy + m02, m10*sx + m11*sy + m12).
|
||||
Draw(src2dst f64.Aff3, src Texture, sr image.Rectangle, op draw.Op, opts *DrawOptions)
|
||||
|
||||
// DrawUniform is like Draw except that the src is a uniform color instead
|
||||
// of a Texture.
|
||||
DrawUniform(src2dst f64.Aff3, src color.Color, sr image.Rectangle, op draw.Op, opts *DrawOptions)
|
||||
|
||||
// Copy copies the sub-Texture defined by src and sr to the destination
|
||||
// (the method receiver), such that sr.Min in src-space aligns with dp in
|
||||
// dst-space.
|
||||
Copy(dp image.Point, src Texture, sr image.Rectangle, op draw.Op, opts *DrawOptions)
|
||||
|
||||
// Scale scales the sub-Texture defined by src and sr to the destination
|
||||
// (the method receiver), such that sr in src-space is mapped to dr in
|
||||
// dst-space.
|
||||
Scale(dr image.Rectangle, src Texture, sr image.Rectangle, op draw.Op, opts *DrawOptions)
|
||||
}
|
||||
|
||||
// These draw.Op constants are provided so that users of this package don't
|
||||
// have to explicitly import "image/draw".
|
||||
const (
|
||||
Over = draw.Over
|
||||
Src = draw.Src
|
||||
)
|
||||
|
||||
// DrawOptions are optional arguments to Draw.
|
||||
type DrawOptions struct {
|
||||
// TODO: transparency in [0x0000, 0xffff]?
|
||||
// TODO: scaler (nearest neighbor vs linear)?
|
||||
}
|
||||
Reference in New Issue
Block a user