Add vendor

Specifically because I want to modify a dependency
This commit is contained in:
2024-08-26 01:00:50 +02:00
parent 64061af4c8
commit 3c5e991ec5
704 changed files with 291662 additions and 0 deletions

27
vendor/golang.org/x/exp/shiny/LICENSE generated vendored Normal file
View 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.

View 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
View 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
View 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];
});
}

View 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
View 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"
}

View 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
View 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
View 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
}

View 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
View 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
View 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
View 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
View 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
}

View 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)
}

View 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 }

View 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()
}

View 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{})
}

View 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
}

View 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

View 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

View 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
}

View 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
}

File diff suppressed because it is too large Load Diff

View 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
View 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)?
}