├── LICENSE ├── README.md ├── TODO ├── driver ├── driver.go ├── driver_darwin.go ├── driver_fallback.go ├── driver_windows.go ├── driver_x11.go ├── gldriver │ ├── buffer.go │ ├── cocoa.go │ ├── cocoa.m │ ├── context.go │ ├── egl.go │ ├── gldriver.go │ ├── other.go │ ├── prog │ │ └── prog.go │ ├── screen.go │ ├── texture.go │ ├── win32.go │ ├── window.go │ ├── x11.c │ └── x11.go ├── internal │ ├── drawer │ │ └── drawer.go │ ├── errscreen │ │ └── errscreen.go │ ├── event │ │ └── event.go │ ├── lifecycler │ │ └── lifecycler.go │ ├── swizzle │ │ ├── swizzle.go │ │ ├── swizzle_amd64.go │ │ ├── swizzle_amd64.s │ │ ├── swizzle_other.go │ │ └── swizzle_test.go │ └── x11key │ │ └── x11key.go ├── win32 │ ├── bitmap.go │ ├── color.go │ ├── gdi.go │ ├── key.go │ ├── lifecycle.go │ ├── mouse.go │ ├── paint.go │ ├── point.go │ ├── rect.go │ ├── screen.go │ ├── scroll.go │ ├── size.go │ ├── syscall_windows.go │ ├── threadmain.go │ ├── window.go │ ├── wm.go │ ├── xform.go │ └── zsyscall_windows.go ├── windriver │ ├── buffer.go │ ├── doc.go │ ├── other.go │ ├── screen.go │ ├── syscall.go │ ├── syscall_windows.go │ ├── texture.go │ ├── window.go │ ├── windraw.go │ └── windriver.go └── x11driver │ ├── buffer.go │ ├── screen.go │ ├── shm_linux_ipc.go │ ├── shm_other.go │ ├── shm_shmopen_syscall.go │ ├── texture.go │ ├── window.go │ └── x11driver.go ├── event ├── key │ ├── code_string.go │ └── key.go ├── lifecycle │ └── lifecycle.go ├── mouse │ └── mouse.go ├── paint │ └── paint.go ├── size │ └── size.go └── touch │ └── touch.go ├── geom └── geom.go ├── gl ├── consts.go ├── dll_windows.go ├── doc.go ├── fn.go ├── gendebug.go ├── gl.go ├── gldebug.go ├── interface.go ├── types_debug.go ├── types_prod.go ├── work.c ├── work.go ├── work.h ├── work_windows.go ├── work_windows_386.s └── work_windows_amd64.s ├── imageutil ├── imageutil.go └── imageutil_test.go ├── math ├── f32 │ └── f32.go ├── f64 │ └── f64.go └── fixed │ ├── fixed.go │ └── fixed_test.go ├── screen ├── screen.go └── screen_test.go ├── sys ├── AUTHORS ├── CONTRIBUTING.md ├── CONTRIBUTORS ├── LICENSE ├── PATENTS ├── README.md └── windows │ ├── asm_windows_386.s │ ├── asm_windows_amd64.s │ ├── dll_windows.go │ ├── env_windows.go │ ├── eventlog.go │ ├── exec_windows.go │ ├── memory_windows.go │ ├── mksyscall.go │ ├── race.go │ ├── race0.go │ ├── registry │ ├── export_test.go │ ├── key.go │ ├── mksyscall.go │ ├── registry_test.go │ ├── syscall.go │ ├── value.go │ └── zsyscall_windows.go │ ├── security_windows.go │ ├── service.go │ ├── str.go │ ├── svc │ ├── debug │ │ ├── log.go │ │ └── service.go │ ├── event.go │ ├── eventlog │ │ ├── install.go │ │ ├── log.go │ │ └── log_test.go │ ├── example │ │ ├── beep.go │ │ ├── install.go │ │ ├── main.go │ │ ├── manage.go │ │ └── service.go │ ├── go12.c │ ├── go12.go │ ├── go13.go │ ├── mgr │ │ ├── config.go │ │ ├── mgr.go │ │ ├── mgr_test.go │ │ └── service.go │ ├── security.go │ ├── service.go │ ├── svc_test.go │ ├── sys_386.s │ └── sys_amd64.s │ ├── syscall.go │ ├── syscall_test.go │ ├── syscall_windows.go │ ├── syscall_windows_test.go │ ├── types_windows.go │ ├── types_windows_386.go │ ├── types_windows_amd64.go │ └── zsyscall_windows.go └── vendor └── github.com └── BurntSushi └── xgb ├── AUTHORS ├── CONTRIBUTORS ├── LICENSE ├── README ├── README.vendor ├── auth.go ├── conn.go ├── cookie.go ├── doc.go ├── help.go ├── render └── render.go ├── shm └── shm.go ├── sync.go ├── xgb.go └── xproto ├── xproto.go └── xproto_test.go /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Shiny 2 | 3 | This is an incompatible fork of Shiny that strips away the event-driven constructs, replacing them with channels and CSP. 4 | 5 | # Changes 6 | 7 | - AVX2 swizzle for amd64 (also in master branch) 8 | - The event pump is gone. (concurrent) 9 | - All events are sent and recieved via channels (concurrent) 10 | - Bare-bones functionality; no widgets (concurrent) 11 | - Only one os window, called the "screen" (concurrent) -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | [ ] Paint event on resize was removed in windows. Remove from linux and mac too. -------------------------------------------------------------------------------- /driver/driver.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package driver provides the default driver for accessing a screen. 6 | package driver // import "github.com/as/shiny/driver" 7 | 8 | // TODO: figure out what to say about the responsibility for users of this 9 | // package to check any implicit dependencies' LICENSEs. For example, the 10 | // driver might use third party software outside of golang.org/x, like an X11 11 | // or OpenGL library. 12 | 13 | import ( 14 | "github.com/as/shiny/screen" 15 | ) 16 | 17 | // Main is called by the program's main function to run the graphical 18 | // application. 19 | // 20 | // It calls f on the Screen, possibly in a separate goroutine, as some OS- 21 | // specific libraries require being on 'the main thread'. It returns when f 22 | // returns. 23 | func Main(f func(screen.Screen)) { 24 | main(f) 25 | } 26 | -------------------------------------------------------------------------------- /driver/driver_darwin.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build darwin 6 | 7 | package driver 8 | 9 | import ( 10 | "github.com/as/shiny/driver/gldriver" 11 | "github.com/as/shiny/screen" 12 | ) 13 | 14 | func main(f func(screen.Screen)) { 15 | gldriver.Main(f) 16 | } 17 | -------------------------------------------------------------------------------- /driver/driver_fallback.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !darwin 6 | // +build !linux android 7 | // +build !windows 8 | // +build !dragonfly 9 | 10 | package driver 11 | 12 | import ( 13 | "errors" 14 | 15 | "github.com/as/shiny/driver/internal/errscreen" 16 | "github.com/as/shiny/screen" 17 | ) 18 | 19 | func main(f func(screen.Screen)) { 20 | f(errscreen.Stub(errors.New("no driver for accessing a screen"))) 21 | } 22 | -------------------------------------------------------------------------------- /driver/driver_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package driver 6 | 7 | import ( 8 | "github.com/as/shiny/driver/windriver" 9 | "github.com/as/shiny/screen" 10 | ) 11 | 12 | func main(f func(screen.Screen)) { 13 | windriver.Main(f) 14 | } 15 | -------------------------------------------------------------------------------- /driver/driver_x11.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build linux,!android dragonfly 6 | 7 | package driver 8 | 9 | import ( 10 | "github.com/as/shiny/driver/x11driver" 11 | "github.com/as/shiny/screen" 12 | ) 13 | 14 | func main(f func(screen.Screen)) { 15 | x11driver.Main(f) 16 | } 17 | -------------------------------------------------------------------------------- /driver/gldriver/buffer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gldriver 6 | 7 | import ( 8 | "image" 9 | 10 | "github.com/as/shiny/screen" 11 | ) 12 | 13 | type bufferImpl struct { 14 | // buf should always be equal to (i.e. the same ptr, len, cap as) rgba.Pix. 15 | // It is a separate, redundant field in order to detect modifications to 16 | // the rgba field that are invalid as per the screen.Buffer documentation. 17 | buf []byte 18 | rgba image.RGBA 19 | size image.Point 20 | 21 | t screen.Texture 22 | } 23 | 24 | func (b *bufferImpl) Release() {} 25 | func (b *bufferImpl) Size() image.Point { return b.size } 26 | func (b *bufferImpl) Bounds() image.Rectangle { return image.Rectangle{Max: b.size} } 27 | func (b *bufferImpl) RGBA() *image.RGBA { return &b.rgba } 28 | -------------------------------------------------------------------------------- /driver/gldriver/context.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gldriver 6 | 7 | import ( 8 | "runtime" 9 | 10 | "github.com/as/shiny/gl" 11 | ) 12 | 13 | // NewContext creates an OpenGL ES context with a dedicated processing thread. 14 | func NewContext() (gl.Context, error) { 15 | glctx, worker := gl.NewContext() 16 | 17 | errCh := make(chan error) 18 | workAvailable := worker.WorkAvailable() 19 | go func() { 20 | runtime.LockOSThread() 21 | err := surfaceCreate() 22 | errCh <- err 23 | if err != nil { 24 | return 25 | } 26 | 27 | for range workAvailable { 28 | worker.DoWork() 29 | } 30 | }() 31 | if err := <-errCh; err != nil { 32 | return nil, err 33 | } 34 | return glctx, nil 35 | } 36 | -------------------------------------------------------------------------------- /driver/gldriver/egl.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gldriver 6 | 7 | // These constants match the values found in the EGL 1.4 headers, 8 | // egl.h, eglext.h, and eglplatform.h. 9 | const ( 10 | _EGL_DONT_CARE = -1 11 | 12 | _EGL_NO_SURFACE = 0 13 | _EGL_NO_CONTEXT = 0 14 | _EGL_NO_DISPLAY = 0 15 | 16 | _EGL_OPENGL_ES2_BIT = 0x04 // EGL_RENDERABLE_TYPE mask 17 | _EGL_WINDOW_BIT = 0x04 // EGL_SURFACE_TYPE mask 18 | 19 | _EGL_OPENGL_ES_API = 0x30A0 20 | _EGL_RENDERABLE_TYPE = 0x3040 21 | _EGL_SURFACE_TYPE = 0x3033 22 | _EGL_BUFFER_SIZE = 0x3020 23 | _EGL_ALPHA_SIZE = 0x3021 24 | _EGL_BLUE_SIZE = 0x3022 25 | _EGL_GREEN_SIZE = 0x3023 26 | _EGL_RED_SIZE = 0x3024 27 | _EGL_DEPTH_SIZE = 0x3025 28 | _EGL_STENCIL_SIZE = 0x3026 29 | _EGL_SAMPLE_BUFFERS = 0x3032 30 | _EGL_CONFIG_CAVEAT = 0x3027 31 | _EGL_NONE = 0x3038 32 | 33 | _EGL_CONTEXT_CLIENT_VERSION = 0x3098 34 | ) 35 | 36 | // ANGLE specific options found in eglext.h 37 | const ( 38 | _EGL_PLATFORM_ANGLE_ANGLE = 0x3202 39 | _EGL_PLATFORM_ANGLE_TYPE_ANGLE = 0x3203 40 | _EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE = 0x3204 41 | _EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE = 0x3205 42 | _EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE = 0x3206 43 | 44 | _EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE = 0x3207 45 | _EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE = 0x3208 46 | _EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE = 0x3209 47 | _EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE = 0x320A 48 | _EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE = 0x320B 49 | 50 | _EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE = 0x320D 51 | _EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE = 0x320E 52 | ) 53 | 54 | const ( 55 | _EGL_SUCCESS = 0x3000 56 | _EGL_NOT_INITIALIZED = 0x3001 57 | _EGL_BAD_ACCESS = 0x3002 58 | _EGL_BAD_ALLOC = 0x3003 59 | _EGL_BAD_ATTRIBUTE = 0x3004 60 | _EGL_BAD_CONFIG = 0x3005 61 | _EGL_BAD_CONTEXT = 0x3006 62 | _EGL_BAD_CURRENT_SURFACE = 0x3007 63 | _EGL_BAD_DISPLAY = 0x3008 64 | _EGL_BAD_MATCH = 0x3009 65 | _EGL_BAD_NATIVE_PIXMAP = 0x300A 66 | _EGL_BAD_NATIVE_WINDOW = 0x300B 67 | _EGL_BAD_PARAMETER = 0x300C 68 | _EGL_BAD_SURFACE = 0x300D 69 | _EGL_CONTEXT_LOST = 0x300E 70 | ) 71 | 72 | func eglErrString(errno uintptr) string { 73 | switch errno { 74 | case _EGL_SUCCESS: 75 | return "EGL_SUCCESS" 76 | case _EGL_NOT_INITIALIZED: 77 | return "EGL_NOT_INITIALIZED" 78 | case _EGL_BAD_ACCESS: 79 | return "EGL_BAD_ACCESS" 80 | case _EGL_BAD_ALLOC: 81 | return "EGL_BAD_ALLOC" 82 | case _EGL_BAD_ATTRIBUTE: 83 | return "EGL_BAD_ATTRIBUTE" 84 | case _EGL_BAD_CONFIG: 85 | return "EGL_BAD_CONFIG" 86 | case _EGL_BAD_CONTEXT: 87 | return "EGL_BAD_CONTEXT" 88 | case _EGL_BAD_CURRENT_SURFACE: 89 | return "EGL_BAD_CURRENT_SURFACE" 90 | case _EGL_BAD_DISPLAY: 91 | return "EGL_BAD_DISPLAY" 92 | case _EGL_BAD_MATCH: 93 | return "EGL_BAD_MATCH" 94 | case _EGL_BAD_NATIVE_PIXMAP: 95 | return "EGL_BAD_NATIVE_PIXMAP" 96 | case _EGL_BAD_NATIVE_WINDOW: 97 | return "EGL_BAD_NATIVE_WINDOW" 98 | case _EGL_BAD_PARAMETER: 99 | return "EGL_BAD_PARAMETER" 100 | case _EGL_BAD_SURFACE: 101 | return "EGL_BAD_SURFACE" 102 | case _EGL_CONTEXT_LOST: 103 | return "EGL_CONTEXT_LOST" 104 | } 105 | return "EGL: unknown error" 106 | } 107 | -------------------------------------------------------------------------------- /driver/gldriver/gldriver.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package gldriver provides an OpenGL driver for accessing a screen. 6 | package gldriver // import "github.com/as/shiny/driver/gldriver" 7 | 8 | import ( 9 | "encoding/binary" 10 | "fmt" 11 | "math" 12 | 13 | "github.com/as/shiny/driver/internal/errscreen" 14 | "github.com/as/shiny/gl" 15 | "github.com/as/shiny/math/f64" 16 | "github.com/as/shiny/screen" 17 | ) 18 | 19 | // Main is called by the program's main function to run the graphical 20 | // application. 21 | // 22 | // It calls f on the Screen, possibly in a separate goroutine, as some OS- 23 | // specific libraries require being on 'the main thread'. It returns when f 24 | // returns. 25 | func Main(f func(screen.Screen)) { 26 | if err := main(f); err != nil { 27 | f(errscreen.Stub(err)) 28 | } 29 | } 30 | 31 | func mul(a, b f64.Aff3) f64.Aff3 { 32 | return f64.Aff3{ 33 | a[0]*b[0] + a[1]*b[3], 34 | a[0]*b[1] + a[1]*b[4], 35 | a[0]*b[2] + a[1]*b[5] + a[2], 36 | 37 | a[3]*b[0] + a[4]*b[3], 38 | a[3]*b[1] + a[4]*b[4], 39 | a[3]*b[2] + a[4]*b[5] + a[5], 40 | } 41 | } 42 | 43 | // writeAff3 must only be called while holding windowImpl.glctxMu. 44 | func writeAff3(glctx gl.Context, u gl.Uniform, a f64.Aff3) { 45 | var m [9]float32 46 | m[0*3+0] = float32(a[0*3+0]) 47 | m[0*3+1] = float32(a[1*3+0]) 48 | m[0*3+2] = 0 49 | m[1*3+0] = float32(a[0*3+1]) 50 | m[1*3+1] = float32(a[1*3+1]) 51 | m[1*3+2] = 0 52 | m[2*3+0] = float32(a[0*3+2]) 53 | m[2*3+1] = float32(a[1*3+2]) 54 | m[2*3+2] = 1 55 | glctx.UniformMatrix3fv(u, m[:]) 56 | } 57 | 58 | // f32Bytes returns the byte representation of float32 values in the given byte 59 | // order. byteOrder must be either binary.BigEndian or binary.LittleEndian. 60 | func f32Bytes(byteOrder binary.ByteOrder, values ...float32) []byte { 61 | le := false 62 | switch byteOrder { 63 | case binary.BigEndian: 64 | case binary.LittleEndian: 65 | le = true 66 | default: 67 | panic(fmt.Sprintf("invalid byte order %v", byteOrder)) 68 | } 69 | 70 | b := make([]byte, 4*len(values)) 71 | for i, v := range values { 72 | u := math.Float32bits(v) 73 | if le { 74 | b[4*i+0] = byte(u >> 0) 75 | b[4*i+1] = byte(u >> 8) 76 | b[4*i+2] = byte(u >> 16) 77 | b[4*i+3] = byte(u >> 24) 78 | } else { 79 | b[4*i+0] = byte(u >> 24) 80 | b[4*i+1] = byte(u >> 16) 81 | b[4*i+2] = byte(u >> 8) 82 | b[4*i+3] = byte(u >> 0) 83 | } 84 | } 85 | return b 86 | } 87 | 88 | // compileProgram must only be called while holding windowImpl.glctxMu. 89 | func compileProgram(glctx gl.Context, vSrc, fSrc string) (gl.Program, error) { 90 | program := glctx.CreateProgram() 91 | if program.Value == 0 { 92 | return gl.Program{}, fmt.Errorf("gldriver: no programs available") 93 | } 94 | 95 | vertexShader, err := compileShader(glctx, gl.VERTEX_SHADER, vSrc) 96 | if err != nil { 97 | return gl.Program{}, err 98 | } 99 | fragmentShader, err := compileShader(glctx, gl.FRAGMENT_SHADER, fSrc) 100 | if err != nil { 101 | glctx.DeleteShader(vertexShader) 102 | return gl.Program{}, err 103 | } 104 | 105 | glctx.AttachShader(program, vertexShader) 106 | glctx.AttachShader(program, fragmentShader) 107 | glctx.LinkProgram(program) 108 | 109 | // Flag shaders for deletion when program is unlinked. 110 | glctx.DeleteShader(vertexShader) 111 | glctx.DeleteShader(fragmentShader) 112 | 113 | if glctx.GetProgrami(program, gl.LINK_STATUS) == 0 { 114 | defer glctx.DeleteProgram(program) 115 | return gl.Program{}, fmt.Errorf("gldriver: program compile: %s", glctx.GetProgramInfoLog(program)) 116 | } 117 | return program, nil 118 | } 119 | 120 | // compileShader must only be called while holding windowImpl.glctxMu. 121 | func compileShader(glctx gl.Context, shaderType gl.Enum, src string) (gl.Shader, error) { 122 | shader := glctx.CreateShader(shaderType) 123 | if shader.Value == 0 { 124 | return gl.Shader{}, fmt.Errorf("gldriver: could not create shader (type %v)", shaderType) 125 | } 126 | glctx.ShaderSource(shader, src) 127 | glctx.CompileShader(shader) 128 | if glctx.GetShaderi(shader, gl.COMPILE_STATUS) == 0 { 129 | defer glctx.DeleteShader(shader) 130 | return gl.Shader{}, fmt.Errorf("gldriver: shader compile: %s", glctx.GetShaderInfoLog(shader)) 131 | } 132 | return shader, nil 133 | } 134 | -------------------------------------------------------------------------------- /driver/gldriver/other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !darwin !386,!amd64 ios 6 | // +build !linux android 7 | // +build !windows 8 | 9 | package gldriver 10 | 11 | import ( 12 | "fmt" 13 | "runtime" 14 | 15 | "github.com/as/shiny/screen" 16 | ) 17 | 18 | func newWindow(opts *screen.NewWindowOptions) (uintptr, error) { return 0, nil } 19 | 20 | func initWindow(id *windowImpl) {} 21 | func showWindow(id *windowImpl) {} 22 | func closeWindow(id uintptr) {} 23 | func drawLoop(w *windowImpl) {} 24 | 25 | func main(f func(screen.Screen)) error { 26 | return fmt.Errorf("gldriver: unsupported GOOS/GOARCH %s/%s", runtime.GOOS, runtime.GOARCH) 27 | } 28 | -------------------------------------------------------------------------------- /driver/gldriver/prog/prog.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "image" 5 | "image/color" 6 | "image/draw" 7 | "log" 8 | 9 | "github.com/as/shiny/event/paint" 10 | "github.com/as/shiny/screen" 11 | "github.com/as/ui" 12 | ) 13 | 14 | func main() { 15 | dev, err := ui.Init(&screen.NewWindowOptions{Width: 1024, Height: 768}) 16 | if err != nil { 17 | panic(err) 18 | } 19 | win := dev.Window() 20 | D := screen.Dev 21 | buf, _ := dev.NewBuffer(image.Pt(512, 512)) 22 | red := image.NewUniform(color.RGBA{255, 0, 0, 255}) 23 | blue := image.NewUniform(color.RGBA{0, 0, 255, 255}) 24 | draw.Draw(buf.RGBA(), buf.RGBA().Bounds(), blue, image.ZP, draw.Src) 25 | for { 26 | select { 27 | case m := <-D.Mouse: 28 | r := image.ZR.Inset(-4).Add(image.Pt(int(m.X), int(m.Y))) 29 | draw.Draw(buf.RGBA(), r, red, image.ZP, draw.Src) 30 | select { 31 | case D.Paint <- paint.Event{}: 32 | log.Println("painted") 33 | default: 34 | log.Println("miss") 35 | } 36 | case <-D.Key: 37 | log.Println("key") 38 | case <-D.Lifecycle: 39 | log.Println("life") 40 | case <-D.Paint: 41 | log.Println("paint") 42 | win.Upload(image.ZP, buf, buf.Bounds()) 43 | win.Publish() 44 | case <-D.Size: 45 | log.Println("size") 46 | buf, _ = dev.NewBuffer(image.Pt(512, 512)) 47 | draw.Draw(buf.RGBA(), buf.RGBA().Bounds(), blue, image.ZP, draw.Src) 48 | 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /driver/gldriver/screen.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gldriver 6 | 7 | import ( 8 | "fmt" 9 | "image" 10 | "sync" 11 | 12 | "github.com/as/shiny/gl" 13 | "github.com/as/shiny/screen" 14 | ) 15 | 16 | var theScreen = &screenImpl{} 17 | 18 | type screenImpl struct { 19 | texture struct { 20 | program gl.Program 21 | pos gl.Attrib 22 | mvp gl.Uniform 23 | uvp gl.Uniform 24 | inUV gl.Attrib 25 | sample gl.Uniform 26 | quad gl.Buffer 27 | } 28 | fill struct { 29 | program gl.Program 30 | pos gl.Attrib 31 | mvp gl.Uniform 32 | color gl.Uniform 33 | quad gl.Buffer 34 | } 35 | 36 | mu sync.Mutex 37 | window *windowImpl 38 | } 39 | 40 | func (s *screenImpl) NewBuffer(size image.Point) (retBuf screen.Buffer, retErr error) { 41 | m := image.NewRGBA(image.Rectangle{Max: size}) 42 | return &bufferImpl{ 43 | buf: m.Pix, 44 | rgba: *m, 45 | size: size, 46 | }, nil 47 | } 48 | 49 | func (s *screenImpl) NewTexture(size image.Point) (screen.Texture, error) { 50 | // TODO: can we compile these programs eagerly instead of lazily? 51 | 52 | // Find a GL context for this texture. 53 | // TODO: this might be correct. Some GL objects can be shared 54 | // across contexts. But this needs a review of the spec to make 55 | // sure it's correct, and some testing would be nice. 56 | w := s.window 57 | if w == nil { 58 | return nil, fmt.Errorf("gldriver: no window available") 59 | } 60 | 61 | w.glctxMu.Lock() 62 | defer w.glctxMu.Unlock() 63 | glctx := w.glctx 64 | if glctx == nil { 65 | return nil, fmt.Errorf("gldriver: no GL context available") 66 | } 67 | 68 | if !glctx.IsProgram(s.texture.program) { 69 | p, err := compileProgram(glctx, textureVertexSrc, textureFragmentSrc) 70 | if err != nil { 71 | return nil, err 72 | } 73 | s.texture.program = p 74 | s.texture.pos = glctx.GetAttribLocation(p, "pos") 75 | s.texture.mvp = glctx.GetUniformLocation(p, "mvp") 76 | s.texture.uvp = glctx.GetUniformLocation(p, "uvp") 77 | s.texture.inUV = glctx.GetAttribLocation(p, "inUV") 78 | s.texture.sample = glctx.GetUniformLocation(p, "sample") 79 | s.texture.quad = glctx.CreateBuffer() 80 | 81 | glctx.BindBuffer(gl.ARRAY_BUFFER, s.texture.quad) 82 | glctx.BufferData(gl.ARRAY_BUFFER, quadCoords, gl.STATIC_DRAW) 83 | } 84 | 85 | t := &textureImpl{ 86 | w: w, 87 | id: glctx.CreateTexture(), 88 | size: size, 89 | } 90 | 91 | glctx.BindTexture(gl.TEXTURE_2D, t.id) 92 | glctx.TexImage2D(gl.TEXTURE_2D, 0, size.X, size.Y, gl.RGBA, gl.UNSIGNED_BYTE, nil) 93 | glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) 94 | glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) 95 | glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) 96 | glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) 97 | 98 | return t, nil 99 | } 100 | 101 | func optsSize(opts *screen.NewWindowOptions) (width, height int) { 102 | width, height = 1024, 768 103 | if opts != nil { 104 | if opts.Width > 0 { 105 | width = opts.Width 106 | } 107 | if opts.Height > 0 { 108 | height = opts.Height 109 | } 110 | } 111 | return width, height 112 | } 113 | 114 | func (s *screenImpl) NewWindow(opts *screen.NewWindowOptions) (screen.Window, error) { 115 | id, err := newWindow(opts) 116 | if err != nil { 117 | return nil, err 118 | } 119 | w := &windowImpl{ 120 | s: s, 121 | id: id, 122 | publish: make(chan struct{}), 123 | publishDone: make(chan screen.PublishResult), 124 | drawDone: make(chan struct{}), 125 | } 126 | initWindow(w) 127 | 128 | if s.window != nil { 129 | panic("newwindow") 130 | } 131 | s.window = w 132 | showWindow(w) 133 | return w, nil 134 | } 135 | -------------------------------------------------------------------------------- /driver/gldriver/texture.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gldriver 6 | 7 | import ( 8 | "encoding/binary" 9 | "image" 10 | "image/color" 11 | "image/draw" 12 | 13 | "github.com/as/shiny/gl" 14 | "github.com/as/shiny/screen" 15 | ) 16 | 17 | type textureImpl struct { 18 | w *windowImpl 19 | id gl.Texture 20 | fb gl.Framebuffer 21 | size image.Point 22 | } 23 | 24 | func (t *textureImpl) Size() image.Point { return t.size } 25 | func (t *textureImpl) Bounds() image.Rectangle { return image.Rectangle{Max: t.size} } 26 | 27 | func (t *textureImpl) Release() { 28 | t.w.glctxMu.Lock() 29 | defer t.w.glctxMu.Unlock() 30 | 31 | if t.fb.Value != 0 { 32 | t.w.glctx.DeleteFramebuffer(t.fb) 33 | t.fb = gl.Framebuffer{} 34 | } 35 | t.w.glctx.DeleteTexture(t.id) 36 | t.id = gl.Texture{} 37 | } 38 | 39 | func (t *textureImpl) Upload(dp image.Point, src screen.Buffer, sr image.Rectangle) { 40 | buf := src.(*bufferImpl) 41 | 42 | // src2dst is added to convert from the src coordinate space to the dst 43 | // coordinate space. It is subtracted to convert the other way. 44 | src2dst := dp.Sub(sr.Min) 45 | 46 | // Clip to the source. 47 | sr = sr.Intersect(buf.Bounds()) 48 | 49 | // Clip to the destination. 50 | dr := sr.Add(src2dst) 51 | dr = dr.Intersect(t.Bounds()) 52 | if dr.Empty() { 53 | return 54 | } 55 | 56 | // Bring dr.Min in dst-space back to src-space to get the pixel buffer offset. 57 | pix := buf.rgba.Pix[buf.rgba.PixOffset(dr.Min.X-src2dst.X, dr.Min.Y-src2dst.Y):] 58 | 59 | t.w.glctxMu.Lock() 60 | defer t.w.glctxMu.Unlock() 61 | 62 | t.w.glctx.BindTexture(gl.TEXTURE_2D, t.id) 63 | 64 | width := dr.Dx() 65 | if width*4 == buf.rgba.Stride { 66 | t.w.glctx.TexSubImage2D(gl.TEXTURE_2D, 0, dr.Min.X, dr.Min.Y, width, dr.Dy(), gl.RGBA, gl.UNSIGNED_BYTE, pix) 67 | return 68 | } 69 | // TODO: can we use GL_UNPACK_ROW_LENGTH with glPixelStorei for stride in 70 | // ES 3.0, instead of uploading the pixels row-by-row? 71 | for y, p := dr.Min.Y, 0; y < dr.Max.Y; y++ { 72 | t.w.glctx.TexSubImage2D(gl.TEXTURE_2D, 0, dr.Min.X, y, width, 1, gl.RGBA, gl.UNSIGNED_BYTE, pix[p:]) 73 | p += buf.rgba.Stride 74 | } 75 | } 76 | 77 | func (t *textureImpl) Fill(dr image.Rectangle, src color.Color, op draw.Op) { 78 | minX := float64(dr.Min.X) 79 | minY := float64(dr.Min.Y) 80 | maxX := float64(dr.Max.X) 81 | maxY := float64(dr.Max.Y) 82 | mvp := calcMVP( 83 | t.size.X, t.size.Y, 84 | minX, minY, 85 | maxX, minY, 86 | minX, maxY, 87 | ) 88 | 89 | glctx := t.w.glctx 90 | 91 | t.w.glctxMu.Lock() 92 | defer t.w.glctxMu.Unlock() 93 | 94 | create := t.fb.Value == 0 95 | if create { 96 | //t.fb = glctx.CreateFramebuffer() 97 | } 98 | glctx.BindFramebuffer(gl.FRAMEBUFFER, t.fb) 99 | if create { 100 | glctx.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, t.id, 0) 101 | } 102 | 103 | glctx.Viewport(0, 0, t.size.X, t.size.Y) 104 | doFill(t.w.s, t.w.glctx, mvp, src, op) 105 | 106 | // We can't restore the GL state (i.e. bind the back buffer, also known as 107 | // gl.Framebuffer{Value: 0}) right away, since we don't necessarily know 108 | // the right viewport size yet. It is valid to call textureImpl.Fill before 109 | // we've gotten our first size.Event. We bind it lazily instead. 110 | //t.w.backBufferBound = false 111 | } 112 | 113 | var quadCoords = f32Bytes(binary.LittleEndian, 114 | 0, 0, // top left 115 | 1, 0, // top right 116 | 0, 1, // bottom left 117 | 1, 1, // bottom right 118 | ) 119 | 120 | const textureVertexSrc = `#version 100 121 | uniform mat3 mvp; 122 | uniform mat3 uvp; 123 | attribute vec3 pos; 124 | attribute vec2 inUV; 125 | varying vec2 uv; 126 | void main() { 127 | vec3 p = pos; 128 | p.z = 1.0; 129 | gl_Position = vec4(mvp * p, 1); 130 | uv = (uvp * vec3(inUV, 1)).xy; 131 | } 132 | ` 133 | 134 | const textureFragmentSrc = `#version 100 135 | precision mediump float; 136 | varying vec2 uv; 137 | uniform sampler2D sample; 138 | void main() { 139 | gl_FragColor = texture2D(sample, uv); 140 | } 141 | ` 142 | 143 | const fillVertexSrc = `#version 100 144 | uniform mat3 mvp; 145 | attribute vec3 pos; 146 | void main() { 147 | vec3 p = pos; 148 | p.z = 1.0; 149 | gl_Position = vec4(mvp * p, 1); 150 | } 151 | ` 152 | 153 | const fillFragmentSrc = `#version 100 154 | precision mediump float; 155 | uniform vec4 color; 156 | void main() { 157 | gl_FragColor = color; 158 | } 159 | ` 160 | -------------------------------------------------------------------------------- /driver/internal/drawer/drawer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package drawer provides functions that help implement screen.Drawer methods. 6 | package drawer // import "github.com/as/shiny/driver/internal/drawer" 7 | 8 | import ( 9 | "image" 10 | "image/draw" 11 | 12 | "github.com/as/shiny/math/f64" 13 | "github.com/as/shiny/screen" 14 | ) 15 | 16 | // Copy implements the Copy method of the screen.Drawer interface by calling 17 | // the Draw method of that same interface. 18 | func Copy(dst screen.Drawer, dp image.Point, src screen.Texture, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) { 19 | dst.Draw(f64.Aff3{ 20 | 1, 0, float64(dp.X - sr.Min.X), 21 | 0, 1, float64(dp.Y - sr.Min.Y), 22 | }, src, sr, op, opts) 23 | } 24 | 25 | // Scale implements the Scale method of the screen.Drawer interface by calling 26 | // the Draw method of that same interface. 27 | func Scale(dst screen.Drawer, dr image.Rectangle, src screen.Texture, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) { 28 | rx := float64(dr.Dx()) / float64(sr.Dx()) 29 | ry := float64(dr.Dy()) / float64(sr.Dy()) 30 | dst.Draw(f64.Aff3{ 31 | rx, 0, float64(dr.Min.X) - rx*float64(sr.Min.X), 32 | 0, ry, float64(dr.Min.Y) - ry*float64(sr.Min.Y), 33 | }, src, sr, op, opts) 34 | } 35 | -------------------------------------------------------------------------------- /driver/internal/errscreen/errscreen.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package errscreen provides a stub Screen implementation. 6 | package errscreen // import "github.com/as/shiny/driver/internal/errscreen" 7 | 8 | import ( 9 | "image" 10 | 11 | "github.com/as/shiny/screen" 12 | ) 13 | 14 | // Stub returns a Screen whose methods all return the given error. 15 | func Stub(err error) screen.Screen { 16 | return stub{err} 17 | } 18 | 19 | type stub struct { 20 | err error 21 | } 22 | 23 | func (s stub) NewBuffer(size image.Point) (screen.Buffer, error) { return nil, s.err } 24 | func (s stub) NewTexture(size image.Point) (screen.Texture, error) { return nil, s.err } 25 | func (s stub) NewWindow(opts *screen.NewWindowOptions) (screen.Window, error) { return nil, s.err } 26 | -------------------------------------------------------------------------------- /driver/internal/event/event.go: -------------------------------------------------------------------------------- 1 | package event // import "github.com/as/shiny/driver/internal/event" 2 | -------------------------------------------------------------------------------- /driver/internal/lifecycler/lifecycler.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package lifecycler tracks a window's lifecycle state. 6 | // 7 | // It eliminates sending redundant lifecycle events, ones where the From and To 8 | // stages are equal. For example, moving a window from one part of the screen 9 | // to another should not send multiple events from StageVisible to 10 | // StageVisible, even though the underlying window system's message might only 11 | // hold the new position, and not whether the window was previously visible. 12 | package lifecycler // import "github.com/as/shiny/driver/internal/lifecycler" 13 | 14 | import ( 15 | "sync" 16 | 17 | "github.com/as/shiny/event/lifecycle" 18 | ) 19 | 20 | // State is a window's lifecycle state. 21 | type State struct { 22 | mu sync.Mutex 23 | stage lifecycle.Stage 24 | dead bool 25 | focused bool 26 | visible bool 27 | } 28 | 29 | func (s *State) SetDead(b bool) { 30 | s.mu.Lock() 31 | s.dead = b 32 | s.mu.Unlock() 33 | } 34 | 35 | func (s *State) SetFocused(b bool) { 36 | s.mu.Lock() 37 | s.focused = b 38 | s.mu.Unlock() 39 | } 40 | 41 | func (s *State) SetVisible(b bool) { 42 | s.mu.Lock() 43 | s.visible = b 44 | s.mu.Unlock() 45 | } 46 | 47 | func (s *State) SendEvent(r Sender, drawContext interface{}) { 48 | s.mu.Lock() 49 | from, to := s.stage, lifecycle.StageAlive 50 | // The order of these if's is important. For example, once a window becomes 51 | // StageDead, it should never change stage again. 52 | // 53 | // Similarly, focused trumps visible. It's hard to imagine a situation 54 | // where a window is focused and not visible on screen, but in that 55 | // unlikely case, StageFocused seems the most appropriate stage. 56 | if s.dead { 57 | to = lifecycle.StageDead 58 | } else if s.focused { 59 | to = lifecycle.StageFocused 60 | } else if s.visible { 61 | to = lifecycle.StageVisible 62 | } 63 | s.stage = to 64 | s.mu.Unlock() 65 | 66 | if from != to { 67 | return 68 | r.Send(lifecycle.Event{ 69 | From: from, 70 | To: to, 71 | 72 | // TODO: does shiny use this at all? 73 | DrawContext: drawContext, 74 | }) 75 | } 76 | } 77 | 78 | // Sender is who to send the lifecycle event to. 79 | type Sender interface { 80 | Send(event interface{}) 81 | } 82 | -------------------------------------------------------------------------------- /driver/internal/swizzle/swizzle.go: -------------------------------------------------------------------------------- 1 | package swizzle 2 | 3 | var ( 4 | swizzler = pureBGRA 5 | ) 6 | 7 | func Swizzle(p, q []byte) { 8 | if len(p) < 4 { 9 | return 10 | } 11 | swizzler(p, q) 12 | } 13 | 14 | func pureBGRA(p, q []byte) { 15 | if len(p)%4 != 0 { 16 | return 17 | } 18 | for i := 0; i < len(p); i += 4 { 19 | q[i+0], q[i+1], q[i+2], q[i+3] = p[i+2], p[i+1], p[i+0], p[i+3] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /driver/internal/swizzle/swizzle_amd64.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 as 2 | // Copyright 2015 The Go Authors 3 | package swizzle 4 | 5 | func haveSSSE3() bool 6 | func haveAVX() bool 7 | func haveAVX2() bool 8 | 9 | var ( 10 | useBGRA4 = true 11 | useSSSE3 = haveSSSE3() 12 | useAVX = haveAVX() 13 | useAVX2 = haveAVX2() 14 | ) 15 | 16 | func init() { 17 | swizzler = bgra4sd 18 | if useSSSE3 { 19 | swizzler = bgra16sd 20 | } 21 | if useAVX { 22 | swizzler = bgra128sd 23 | } 24 | if useAVX2 { 25 | swizzler = bgra256sd 26 | } 27 | } 28 | 29 | func bgra256sd(p, q []byte) // swizzle_amd64.s:/bgra256sd/ 30 | func bgra128sd(p, q []byte) // swizzle_amd64.s:/bgra128sd/ 31 | func bgra16sd(p, q []byte) // swizzle_amd64.s:/bgra16sd/ 32 | func bgra4sd(p, q []byte) 33 | -------------------------------------------------------------------------------- /driver/internal/swizzle/swizzle_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !amd64 6 | 7 | package swizzle 8 | 9 | const ( 10 | useBGRA16 = false 11 | useBGRA4 = false 12 | ) 13 | 14 | func bgra16(p []byte) { panic("unreachable") } 15 | func bgra4(p []byte) { panic("unreachable") } 16 | -------------------------------------------------------------------------------- /driver/internal/swizzle/swizzle_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 as 2 | // Copyright 2015 The Go Authors 3 | 4 | package swizzle 5 | 6 | import ( 7 | "bytes" 8 | "fmt" 9 | "math/rand" 10 | "os" 11 | "strings" 12 | "testing" 13 | ) 14 | 15 | var ( 16 | rgbaslice = "abcdefghijklmnopqrstuvwxyz012345ABCDEFGHIJKLMNOPQRSTUVWXYZ6789@=" 17 | bgraslice = "cbadgfehkjilonmpsrqtwvux0zy14325CBADGFEHKJILONMPSRQTWVUX6ZY7@98=" 18 | 19 | supported = map[string]func(p, q []byte){ 20 | "pure": pureBGRA, 21 | "amd64.4": bgra4sd, 22 | "ssse.16": bgra16sd, 23 | "avx2.128": bgra128sd, 24 | "avx2.256": bgra256sd, 25 | } 26 | ) 27 | 28 | func TestMain(m *testing.M) { 29 | const safe = 1024 30 | rgbaslice = strings.Repeat(rgbaslice, safe) 31 | bgraslice = strings.Repeat(bgraslice, safe) 32 | 33 | if !haveAVX2() { 34 | delete(supported, "avx2.256") 35 | } 36 | if !haveAVX() { 37 | delete(supported, "avx2.128") 38 | } 39 | if !haveSSSE3() { 40 | delete(supported, "ssse.16") 41 | } 42 | os.Exit(m.Run()) 43 | } 44 | 45 | func TestSwizzleDistinct(t *testing.T) { 46 | for _, v := range []int{32, 64, 96, 128, 160, 192, 224, 256} { 47 | t.Run(fmt.Sprint(v), func(t *testing.T) { 48 | testSwizzle1(t, v, true) 49 | }) 50 | } 51 | } 52 | func TestSwizzleOverlap(t *testing.T) { 53 | for _, v := range []int{32, 64, 96, 128, 160, 192, 224, 256} { 54 | t.Run(fmt.Sprint(v), func(t *testing.T) { 55 | testSwizzle1(t, v, false) 56 | }) 57 | } 58 | } 59 | 60 | func testSwizzle1(t *testing.T, N int, distinct bool) { 61 | t.Helper() 62 | s := []byte(rgbaslice[:N]) 63 | d := s 64 | if distinct { 65 | d = make([]byte, N, N) 66 | } 67 | want := bgraslice[:N] 68 | bgra256sd(s, d) 69 | if string(d) != want { 70 | t.Fatalf("have: %s\nwant: %s\n", d, want) 71 | } 72 | } 73 | 74 | func makeRGBA(len int) []byte { 75 | if len%4 != 0 { 76 | panic("makeRGBA: len % 4 != 0") 77 | } 78 | return []byte(strings.Repeat("rgba", len/4)) 79 | } 80 | 81 | func TestBGRAShort(t *testing.T) { 82 | var lens = []int{ 83 | 4, 8, 12, 16, 24, 32, 48, 64, 128, 192, 256, 512, 84 | } 85 | var tab []string 86 | for _, v := range lens { 87 | tab = append(tab, rgbaslice[:v]) 88 | } 89 | 90 | for name, fn := range supported { 91 | t.Run(name, func(t *testing.T) { 92 | for i, in := range tab { 93 | want := append([]byte{}, in...) 94 | pureBGRA(append([]byte{}, in...), want) 95 | p := []byte(in) 96 | q := make([]byte, len(p)) 97 | fn(p, q) 98 | have := string(q) 99 | if want := string(want); have != want { 100 | t.Errorf("len=%d: have %q, want %q", lens[i], have, want) 101 | } 102 | } 103 | }) 104 | } 105 | } 106 | 107 | func TestBGRARandom(t *testing.T) { 108 | r := rand.New(rand.NewSource(1)) 109 | var ( 110 | p0, q0, 111 | p1, q1 [1024]byte 112 | ) 113 | for i := range q0 { 114 | p0[i] = byte(r.Intn(256)) 115 | p1[i] = p0[0] 116 | } 117 | exp := Swizzle 118 | ctl := pureBGRA 119 | 120 | for i := 0; i < 100000; i++ { 121 | o := r.Intn(len(p1)) 122 | n := r.Intn(len(p1)-o) &^ 0x03 123 | exp(p1[o:o+n], q1[o:o+n]) 124 | ctl(p0[o:o+n], q0[o:o+n]) 125 | if bytes.Equal(q0[:], q1[:]) { 126 | continue 127 | } 128 | for j := range q1 { 129 | x := q1[j] 130 | y := q1[j] 131 | if x != y { 132 | t.Fatalf("iter %d: swizzling [%d:%d+%d]: bytes differ at offset %d (aka %d+%d): %#02x vs %#02x", 133 | i, o, o, n, j, o, j-o, x, y) 134 | } 135 | } 136 | } 137 | } 138 | 139 | func BenchmarkSwizzle(b *testing.B) { 140 | sizes := []string{"64B", "64K", "64M"} 141 | for name, fn := range supported { 142 | for i, v := range sizes { 143 | b.Run(name+"/"+v, func(b *testing.B) { 144 | d := makeRGBA(64 * 1 << uint(i)) 145 | s := make([]byte, len(d)) 146 | b.SetBytes(int64(len(s))) 147 | b.ResetTimer() 148 | for i := 0; i < b.N; i++ { 149 | fn(s, d) 150 | } 151 | 152 | }) 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /driver/win32/bitmap.go: -------------------------------------------------------------------------------- 1 | package win32 2 | 3 | const ( 4 | BiRGB = 0 5 | BiBitfields = 3 6 | DibRGBColors = 0 7 | AcSrcOver = 0x00 8 | AcSrcAlpha = 0x01 9 | SrcCopy = 0x00cc0020 10 | ) 11 | const ( 12 | GmCompatible = 1 13 | GmAdvanced = 2 14 | MWTIdentity = 1 15 | ) 16 | 17 | type BitmapInfo struct { 18 | Header BitmapInfoV4 19 | Colors [1]RGBQuad 20 | } 21 | 22 | type BitmapInfoV4 struct { 23 | Size uint32 24 | Width int32 25 | Height int32 26 | Planes uint16 27 | BitCount uint16 28 | Compression uint32 29 | SizeImage uint32 30 | XPelsPerMeter int32 31 | YPelsPerMeter int32 32 | ClrUsed uint32 33 | ClrImportant uint32 34 | Red, Green, Blue, Alpha uint32 35 | Endpoints [3]uint32 36 | Gamma [3]uint32 37 | } 38 | 39 | type BitmapInfoHeader struct { 40 | Size uint32 41 | Width int32 42 | Height int32 43 | Planes uint16 44 | BitCount uint16 45 | Compression uint32 46 | SizeImage uint32 47 | XPelsPerMeter int32 48 | YPelsPerMeter int32 49 | ClrUsed uint32 50 | ClrImportant uint32 51 | } 52 | -------------------------------------------------------------------------------- /driver/win32/color.go: -------------------------------------------------------------------------------- 1 | package win32 2 | 3 | import "unsafe" 4 | 5 | type BlendFunc struct { 6 | Op byte 7 | Flags byte 8 | SrcConstAlpha byte 9 | AlphaFormat byte 10 | } 11 | 12 | // Uintptr helps to pass bf to syscall.Syscall. 13 | func (b BlendFunc) Uintptr() uintptr { 14 | return *((*uintptr)(unsafe.Pointer(&b))) 15 | } 16 | 17 | type RGBQuad struct { 18 | Blue byte 19 | Green byte 20 | Red byte 21 | Reserved byte 22 | } 23 | 24 | type ColorRef uint32 25 | 26 | func NewColorRef(c interface { 27 | RGBA() (r, g, b, a uint32) 28 | }) ColorRef { 29 | type cr = ColorRef 30 | r, g, b, a := c.RGBA() 31 | return cr(r>>8) | cr(g>>8)<<8 | cr(b>>8)<<16 | cr(a>>8)<<24 32 | } 33 | 34 | func RGB(r, g, b byte) ColorRef { 35 | return ColorRef(r) | ColorRef(g)<<8 | ColorRef(b)<<16 36 | } 37 | -------------------------------------------------------------------------------- /driver/win32/gdi.go: -------------------------------------------------------------------------------- 1 | package win32 2 | 3 | import ( 4 | "sync" 5 | "syscall" 6 | ) 7 | 8 | const windowClass = "shiny_Window" 9 | const ( 10 | CS_OWNDC = 32 11 | ) 12 | 13 | const ( 14 | msgCreateWindow = WmUser + iota 15 | msgMainCallback 16 | msgShow 17 | msgQuit 18 | msgLast 19 | ) 20 | 21 | var ( 22 | hDefaultIcon syscall.Handle 23 | hDefaultCursor syscall.Handle 24 | hThisInstance syscall.Handle 25 | ) 26 | 27 | var currentUserWM userWM 28 | 29 | func initCommon() (err error) { 30 | hDefaultIcon, err = LoadIcon(0, IdiApplication) 31 | if err != nil { 32 | return err 33 | } 34 | hDefaultCursor, err = LoadCursor(0, IdcArrow) 35 | if err != nil { 36 | return err 37 | } 38 | // TODO(andlabs) hThisInstance 39 | return nil 40 | } 41 | 42 | func initWindowClass() (err error) { 43 | wcname, err := syscall.UTF16PtrFromString(windowClass) 44 | if err != nil { 45 | return err 46 | } 47 | _, err = RegisterClass(&WindowClass{ 48 | Style: CS_OWNDC, 49 | LpszClassName: wcname, 50 | LpfnWndProc: syscall.NewCallback(windowWndProc), 51 | HIcon: hDefaultIcon, 52 | HCursor: hDefaultCursor, 53 | HInstance: hThisInstance, 54 | HbrBackground: syscall.Handle(ColorBtnface + 1), 55 | }) 56 | return err 57 | } 58 | 59 | // userWM is used to generate private (WM_USER and above) window message IDs 60 | // for use by screenWindowWndProc and windowWndProc. 61 | type userWM struct { 62 | sync.Mutex 63 | id uint32 64 | } 65 | 66 | func (m *userWM) next() uint32 { 67 | m.Lock() 68 | if m.id == 0 { 69 | m.id = msgLast 70 | } 71 | r := m.id 72 | m.id++ 73 | m.Unlock() 74 | return r 75 | } 76 | -------------------------------------------------------------------------------- /driver/win32/lifecycle.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package win32 4 | 5 | import ( 6 | "fmt" 7 | "syscall" 8 | 9 | "github.com/as/shiny/event/lifecycle" 10 | ) 11 | 12 | type Lifecycle = lifecycle.Event 13 | 14 | var LifecycleEvent func(hwnd syscall.Handle, e lifecycle.Stage) 15 | 16 | func sendFocus(h syscall.Handle, msg uint32, wp, lp uintptr) (res uintptr) { 17 | switch msg { 18 | case WmSetfocus: 19 | LifecycleEvent(h, lifecycle.StageFocused) 20 | case WmKillfocus: 21 | LifecycleEvent(h, lifecycle.StageVisible) 22 | default: 23 | panic(fmt.Sprintf("unexpected focus message: %d", msg)) 24 | } 25 | return DefWindowProc(h, msg, wp, lp) 26 | } 27 | 28 | func sendClose(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) (lResult uintptr) { 29 | LifecycleEvent(hwnd, lifecycle.StageDead) 30 | return 0 31 | } 32 | 33 | func sendShow(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) (lResult uintptr) { 34 | LifecycleEvent(hwnd, lifecycle.StageVisible) 35 | ShowWindow(hwnd, SwShowdefault) 36 | sendSize(hwnd) 37 | return 0 38 | } 39 | -------------------------------------------------------------------------------- /driver/win32/mouse.go: -------------------------------------------------------------------------------- 1 | package win32 2 | 3 | import ( 4 | "syscall" 5 | 6 | "github.com/as/shiny/event/mouse" 7 | "github.com/as/shiny/screen" 8 | ) 9 | 10 | // +build windows 11 | 12 | type Mouse = mouse.Event 13 | 14 | var MouseEvent func(hwnd syscall.Handle, e mouse.Event) 15 | 16 | type mouseevent struct { 17 | dir mouse.Direction 18 | but mouse.Button 19 | } 20 | 21 | func (m *mouseevent) send(hwnd syscall.Handle, msg uint32, wp, lp uintptr) (lResult uintptr) { 22 | screen.SendMouse(mouse.Event{ 23 | Direction: m.dir, 24 | Button: m.but, 25 | X: float32(uint16(lp)), 26 | Y: float32(uint16(lp >> 16)), 27 | Modifiers: keyModifiers(), 28 | }) 29 | return 0 30 | } 31 | func sendMouseEvent(hwnd syscall.Handle, msg uint32, wp, lp uintptr) (lResult uintptr) { 32 | return mousetab[msg].send(hwnd, msg, wp, lp) 33 | } 34 | 35 | var mousetab = [...]mouseevent{ 36 | WmLbuttondown: {mouse.DirPress, mouse.ButtonLeft}, 37 | WmMbuttondown: {mouse.DirPress, mouse.ButtonMiddle}, 38 | WmRbuttondown: {mouse.DirPress, mouse.ButtonRight}, 39 | WmLbuttonup: {mouse.DirRelease, mouse.ButtonLeft}, 40 | WmMbuttonup: {mouse.DirRelease, mouse.ButtonMiddle}, 41 | WmRbuttonup: {mouse.DirRelease, mouse.ButtonRight}, 42 | WmMousemove: {}, 43 | } 44 | -------------------------------------------------------------------------------- /driver/win32/paint.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package win32 4 | 5 | import ( 6 | "syscall" 7 | 8 | "github.com/as/shiny/event/paint" 9 | "github.com/as/shiny/screen" 10 | ) 11 | 12 | type Paint = paint.Event 13 | 14 | var PaintEvent func(hwnd syscall.Handle, e paint.Event) 15 | 16 | func sendPaint(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) (lResult uintptr) { 17 | screen.SendPaint(Paint{}) 18 | return DefWindowProc(hwnd, uMsg, wParam, lParam) 19 | } 20 | -------------------------------------------------------------------------------- /driver/win32/point.go: -------------------------------------------------------------------------------- 1 | package win32 2 | 3 | var ZP = Point{} 4 | 5 | type Point struct { 6 | X int32 7 | Y int32 8 | } 9 | 10 | func Pt(x, y int) Point { 11 | return Point{int32(x), int32(y)} 12 | } 13 | -------------------------------------------------------------------------------- /driver/win32/rect.go: -------------------------------------------------------------------------------- 1 | package win32 2 | 3 | type Rectangle struct { 4 | Min, Max Point 5 | } 6 | 7 | func (r Rectangle) Dx() int32 { 8 | return r.Max.X - r.Min.X 9 | } 10 | func (r Rectangle) Dy() int32 { 11 | return r.Max.Y - r.Min.Y 12 | } 13 | 14 | func Rect(x, y, xx, yy int) Rectangle { 15 | return Rectangle{ 16 | Min: Point{ 17 | int32(x), 18 | int32(y), 19 | }, 20 | Max: Point{ 21 | int32(xx), 22 | int32(yy), 23 | }, 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /driver/win32/screen.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package win32 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | ) 9 | 10 | // screenHWND is the handle to the "Screen window". 11 | // The Screen window encapsulates all screen.Screen operations 12 | // in an actual Windows window so they all run on the main thread. 13 | // Since any messages sent to a window will be executed on the 14 | // main thread, we can safely use the messages below. 15 | var ( 16 | screenHWND syscall.Handle 17 | screenMsgs = map[uint32]func(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) (lResult uintptr){} 18 | ) 19 | 20 | func AddScreenMsg(fn func(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr)) uint32 { 21 | uMsg := currentUserWM.next() 22 | screenMsgs[uMsg] = func(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) uintptr { 23 | fn(hwnd, uMsg, wParam, lParam) 24 | return 0 25 | } 26 | return uMsg 27 | } 28 | 29 | func SendScreenMessage(uMsg uint32, wParam uintptr, lParam uintptr) (lResult uintptr) { 30 | return SendMessage(screenHWND, uMsg, wParam, lParam) 31 | } 32 | 33 | func initScreenWindow() (err error) { 34 | const screenWindowClass = "shiny_ScreenWindow" 35 | swc, err := syscall.UTF16PtrFromString(screenWindowClass) 36 | if err != nil { 37 | return err 38 | } 39 | empty, err := syscall.UTF16PtrFromString("") 40 | if err != nil { 41 | return err 42 | } 43 | 44 | wc := WindowClass{ 45 | LpszClassName: swc, 46 | LpfnWndProc: syscall.NewCallback(screenWindowWndProc), 47 | HIcon: hDefaultIcon, 48 | HCursor: hDefaultCursor, 49 | HInstance: hThisInstance, 50 | HbrBackground: syscall.Handle(ColorBtnface + 1), 51 | } 52 | _, err = RegisterClass(&wc) 53 | if err != nil { 54 | return err 55 | } 56 | 57 | const ( 58 | //style = WsOverlappedWindow | WsVisible | WsChild 59 | style = WsOverlappedWindow 60 | def = int32(CwUseDefault) 61 | ) 62 | //screenHWND, err = CreateWindowEx(0, swc, empty, style, def, def, def, def, GetConsoleWindow(), 0, hThisInstance, 0) 63 | screenHWND, err = CreateWindowEx(0, swc, empty, style, def, def, def, def, HwndMessage, 0, hThisInstance, 0) 64 | return err 65 | } 66 | 67 | func screenWindowWndProc(hwnd syscall.Handle, uMsg uint32, wParam uintptr, lParam uintptr) (lResult uintptr) { 68 | switch uMsg { 69 | case msgCreateWindow: 70 | p := (*newWindowParams)(unsafe.Pointer(lParam)) 71 | p.w, p.err = newWindow(p.opts) 72 | case msgMainCallback: 73 | go func() { 74 | mainCallback() 75 | SendScreenMessage(msgQuit, 0, 0) 76 | }() 77 | case msgQuit: 78 | PostQuitMessage(0) 79 | } 80 | fn := screenMsgs[uMsg] 81 | if fn != nil { 82 | return fn(hwnd, uMsg, wParam, lParam) 83 | } 84 | return DefWindowProc(hwnd, uMsg, wParam, lParam) 85 | } 86 | -------------------------------------------------------------------------------- /driver/win32/scroll.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package win32 4 | 5 | import ( 6 | "syscall" 7 | 8 | "github.com/as/shiny/event/mouse" 9 | "github.com/as/shiny/screen" 10 | ) 11 | 12 | type Scroll = mouse.Event 13 | 14 | func sendScrollEvent(hwnd syscall.Handle, _ uint32, wp, lp uintptr) (lResult uintptr) { 15 | 16 | // Convert from screen to window coordinates. 17 | p := Point{int32(uint16(lp)), int32(uint16(lp >> 16))} 18 | ScreenToClient(hwnd, &p) 19 | 20 | e := mouse.Event{ 21 | X: float32(p.X), 22 | Y: float32(p.Y), 23 | Modifiers: keyModifiers(), 24 | Direction: mouse.DirStep, 25 | Button: mouse.ButtonWheelDown, 26 | } 27 | if int16(wp>>16) > 0 { 28 | e.Button = mouse.ButtonWheelUp 29 | } 30 | 31 | screen.SendScroll(e) 32 | return 33 | } 34 | -------------------------------------------------------------------------------- /driver/win32/size.go: -------------------------------------------------------------------------------- 1 | package win32 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | 7 | "github.com/as/shiny/event/size" 8 | "github.com/as/shiny/geom" 9 | "github.com/as/shiny/screen" 10 | ) 11 | 12 | type Size = size.Event 13 | 14 | var ( 15 | SizeEvent func(hwnd syscall.Handle, e size.Event) 16 | ) 17 | 18 | func sendSizeEvent(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) (lResult uintptr) { 19 | wp := (*WindowPos)(unsafe.Pointer(lParam)) 20 | if wp.Flags&SwpNosize != 0 { 21 | return 0 22 | } 23 | sendSize(hwnd) 24 | return 0 25 | } 26 | 27 | func sendSize(hwnd syscall.Handle) { 28 | r := &Rectangle{} 29 | if err := GetClientRect(hwnd, r); err != nil { 30 | panic(err) // TODO(andlabs) 31 | } 32 | 33 | dx, dy := int(r.Dx()), int(r.Dy()) 34 | screen.SendSize(size.Event{ 35 | WidthPx: dx, 36 | HeightPx: dy, 37 | WidthPt: geom.Pt(dx), 38 | HeightPt: geom.Pt(dy), 39 | PixelsPerPt: 1, 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /driver/win32/syscall_windows.go: -------------------------------------------------------------------------------- 1 | package win32 2 | 3 | //sys AlphaBlend(dcdest syscall.Handle, xoriginDest int32, yoriginDest int32, wDest int32, hDest int32, dcsrc syscall.Handle, xoriginSrc int32, yoriginSrc int32, wsrc int32, hsrc int32, ftn uintptr) (err error) = msimg32.AlphaBlend 4 | //sys BitBlt(dcdest syscall.Handle, xdest int32, ydest int32, width int32, height int32, dcsrc syscall.Handle, xsrc int32, ysrc int32, rop uint32) (err error) = gdi32.BitBlt 5 | //sys CreateCompatibleBitmap(dc syscall.Handle, width int32, height int32) (bitmap syscall.Handle, err error) = gdi32.CreateCompatibleBitmap 6 | //sys CreateCompatibleDC(dc syscall.Handle) (newdc syscall.Handle, err error) = gdi32.CreateCompatibleDC 7 | //sys CreateDIBSection(dc syscall.Handle, bmi *_BITMAPINFO, usage uint32, bits **byte, section syscall.Handle, offset uint32) (bitmap syscall.Handle, err error) = gdi32.CreateDIBSection 8 | //sys CreateSolidBrush(color _COLORREF) (brush syscall.Handle, err error) = gdi32.CreateSolidBrush 9 | //sys DeleteDC(dc syscall.Handle) (err error) = gdi32.DeleteDC 10 | //sys DeleteObject(object syscall.Handle) (err error) = gdi32.DeleteObject 11 | //sys FillRect(dc syscall.Handle, rc *_RECT, brush syscall.Handle) (err error) = user32.FillRect 12 | //sys ModifyWorldTransform(dc syscall.Handle, x *_XFORM, mode uint32) (err error) = gdi32.ModifyWorldTransform 13 | //sys SelectObject(dc syscall.Handle, gdiobj syscall.Handle) (newobj syscall.Handle, err error) = gdi32.SelectObject 14 | //sys SetGraphicsMode(dc syscall.Handle, mode int32) (oldmode int32, err error) = gdi32.SetGraphicsMode 15 | //sys SetWorldTransform(dc syscall.Handle, x *_XFORM) (err error) = gdi32.SetWorldTransform 16 | //sys StretchBlt(dcdest syscall.Handle, xdest int32, ydest int32, wdest int32, hdest int32, dcsrc syscall.Handle, xsrc int32, ysrc int32, wsrc int32, hsrc int32, rop uint32) (err error) = gdi32.StretchBlt 17 | //sys GetDeviceCaps(dc syscall.Handle, index int32) (ret int32) = gdi32.GetDeviceCaps 18 | 19 | //sys GetConsoleWindow() (h syscall.Handle) = kernel32.GetConsoleWindow 20 | //sys GetDC(hwnd syscall.Handle) (dc syscall.Handle, err error) = user32.GetDC 21 | //sys ReleaseDC(hwnd syscall.Handle, dc syscall.Handle) (err error) = user32.ReleaseDC 22 | //sys sendMessage(hwnd syscall.Handle, uMsg uint32, wParam uintptr, lParam uintptr) (lResult uintptr) = user32.SendMessageW 23 | //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 24 | //sys DefWindowProc(hwnd syscall.Handle, uMsg uint32, wParam uintptr, lParam uintptr) (lResult uintptr) = user32.DefWindowProcW 25 | //sys DestroyWindow(hwnd syscall.Handle) (err error) = user32.DestroyWindow 26 | //sys DispatchMessage(msg *_MSG) (ret int32) = user32.DispatchMessageW 27 | //sys GetClientRect(hwnd syscall.Handle, rect *_RECT) (err error) = user32.GetClientRect 28 | //sys GetWindowRect(hwnd syscall.Handle, rect *_RECT) (err error) = user32.GetWindowRect 29 | //sys GetKeyboardLayout(threadID uint32) (locale syscall.Handle) = user32.GetKeyboardLayout 30 | //sys GetKeyboardState(lpKeyState *byte) (err error) = user32.GetKeyboardState 31 | //sys GetKeyState(virtkey int32) (keystatus int16) = user32.GetKeyState 32 | //sys GetMessage(msg *_MSG, hwnd syscall.Handle, msgfiltermin uint32, msgfiltermax uint32) (ret int32, err error) [failretval==-1] = user32.GetMessageW 33 | //sys LoadCursor(hInstance syscall.Handle, cursorName uintptr) (cursor syscall.Handle, err error) = user32.LoadCursorW 34 | //sys LoadIcon(hInstance syscall.Handle, iconName uintptr) (icon syscall.Handle, err error) = user32.LoadIconW 35 | //sys MoveWindow(hwnd syscall.Handle, x int32, y int32, w int32, h int32, repaint bool) (err error) = user32.MoveWindow 36 | //sys PostMessage(hwnd syscall.Handle, uMsg uint32, wParam uintptr, lParam uintptr) (lResult bool) = user32.PostMessageW 37 | //sys PostQuitMessage(exitCode int32) = user32.PostQuitMessage 38 | //sys RegisterClass(wc *_WNDCLASS) (atom uint16, err error) = user32.RegisterClassW 39 | //sys ShowWindow(hwnd syscall.Handle, cmdshow int32) (wasvisible bool) = user32.ShowWindow 40 | //sys ScreenToClient(hwnd syscall.Handle, lpPoint *_POINT) (ok bool) = user32.ScreenToClient 41 | //sys ToUnicodeEx(wVirtKey uint32, wScanCode uint32, lpKeyState *byte, pwszBuff *uint16, cchBuff int32, wFlags uint32, dwhkl syscall.Handle) (ret int32) = user32.ToUnicodeEx 42 | //sys TranslateMessage(msg *_MSG) (done bool) = user32.TranslateMessage 43 | -------------------------------------------------------------------------------- /driver/win32/threadmain.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package win32 4 | 5 | import ( 6 | "fmt" 7 | "runtime" 8 | ) 9 | 10 | var mainCallback func() 11 | 12 | func Main(f func()) (retErr error) { 13 | runtime.LockOSThread() 14 | 15 | if err := initCommon(); err != nil { 16 | return err 17 | } 18 | 19 | if err := initScreenWindow(); err != nil { 20 | return err 21 | } 22 | defer DestroyWindow(screenHWND) 23 | 24 | if err := initWindowClass(); err != nil { 25 | return err 26 | } 27 | 28 | // Prime the pump. 29 | mainCallback = f 30 | PostMessage(screenHWND, msgMainCallback, 0, 0) 31 | 32 | // Main message pump. 33 | m := &Msg{} 34 | for { 35 | done, err := GetMessage(m, 0, 0, 0) 36 | if err != nil { 37 | return fmt.Errorf("win32 GetMessage failed: %v", err) 38 | } 39 | if done == 0 { // WM_QUIT 40 | break 41 | } 42 | TranslateMessage(m) 43 | DispatchMessage(m) 44 | } 45 | 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /driver/win32/window.go: -------------------------------------------------------------------------------- 1 | package win32 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | 7 | "github.com/as/shiny/screen" 8 | ) 9 | 10 | type newWindowParams struct { 11 | opts *screen.NewWindowOptions 12 | w syscall.Handle 13 | err error 14 | } 15 | 16 | var windowMsgs = map[uint32]func(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) (lResult uintptr){ 17 | WmSetfocus: sendFocus, 18 | WmKillfocus: sendFocus, 19 | WmPaint: sendPaint, 20 | msgShow: sendShow, 21 | WmWindowposchanged: sendSizeEvent, 22 | WmClose: sendClose, 23 | 24 | WmLbuttondown: mousetab[WmLbuttondown].send, 25 | WmLbuttonup: mousetab[WmLbuttonup].send, 26 | WmMbuttondown: mousetab[WmMbuttondown].send, 27 | WmMbuttonup: mousetab[WmMbuttonup].send, 28 | WmRbuttondown: mousetab[WmRbuttondown].send, 29 | WmRbuttonup: mousetab[WmRbuttonup].send, 30 | WmMousemove: mousetab[WmMousemove].send, 31 | WmMousewheel: sendScrollEvent, 32 | 33 | WmKeydown: keytab.sendDown, 34 | WmKeyup: keytab.sendUp, 35 | // TODO case WmSyskeydown, WmSyskeyup: 36 | 37 | // TODO(as): This will probably break something, let's not 38 | //WmInputlangchange: changeLanguage, 39 | } 40 | 41 | func NewWindow(opts *screen.NewWindowOptions) (syscall.Handle, error) { 42 | var p newWindowParams 43 | p.opts = opts 44 | SendScreenMessage(msgCreateWindow, 0, uintptr(unsafe.Pointer(&p))) 45 | return p.w, p.err 46 | } 47 | 48 | func AddWindowMsg(fn func(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr)) uint32 { 49 | uMsg := currentUserWM.next() 50 | windowMsgs[uMsg] = func(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) uintptr { 51 | fn(hwnd, uMsg, wParam, lParam) 52 | return 0 53 | } 54 | return uMsg 55 | } 56 | 57 | func SendMessage(hwnd syscall.Handle, uMsg uint32, wParam uintptr, lParam uintptr) (lResult uintptr) { 58 | return sendMessage(hwnd, uMsg, wParam, lParam) 59 | } 60 | 61 | // Resize makes hwnd client rectangle opts.Width by opts.Height in size. 62 | func Resize(h syscall.Handle, p Point) error { 63 | if p.X == 0 || p.Y == 0 { 64 | return nil 65 | } 66 | 67 | var cr Rectangle 68 | if err := GetClientRect(h, &cr); err != nil { 69 | return err 70 | } 71 | 72 | var wr Rectangle 73 | if err := GetWindowRect(h, &wr); err != nil { 74 | return err 75 | } 76 | 77 | wr.Max.X = wr.Dx() - (cr.Max.X - int32(p.X)) 78 | wr.Max.Y = wr.Dy() - (cr.Max.Y - int32(p.Y)) 79 | 80 | return Reshape(h, wr) 81 | } 82 | 83 | // Reshape makes hwnd client rectangle opts.Width by opts.Height in size. 84 | func Reshape(h syscall.Handle, r Rectangle) error { 85 | return MoveWindow(h, r.Min.X, r.Min.Y, r.Dx(), r.Dy(), false) 86 | } 87 | 88 | // Show shows a newly created window. 89 | // It sends the appropriate lifecycle events, makes the window appear 90 | // on the screen, and sends an initial size event. 91 | // 92 | // This is a separate step from NewWindow to give the driver a chance 93 | // to setup its internal state for a window before events start being 94 | // delivered. 95 | func Show(hwnd syscall.Handle) { 96 | SendMessage(hwnd, msgShow, 0, 0) 97 | } 98 | 99 | func Release(hwnd syscall.Handle) { 100 | DestroyWindow(hwnd) 101 | } 102 | 103 | func newWindow(opts *screen.NewWindowOptions) (syscall.Handle, error) { 104 | // TODO(brainman): convert windowClass to *uint16 once (in initWindowClass) 105 | wcname, err := syscall.UTF16PtrFromString(windowClass) 106 | if err != nil { 107 | return 0, err 108 | } 109 | title, err := syscall.UTF16PtrFromString(opts.GetTitle()) 110 | if err != nil { 111 | return 0, err 112 | } 113 | 114 | // h := syscall.Handle(0) 115 | // if opts.Overlay{ 116 | // h = GetConsoleWindow() 117 | // } 118 | hwnd, err := CreateWindowEx(0, 119 | wcname, title, 120 | WsOverlappedWindow, 121 | CwUseDefault, CwUseDefault, 122 | CwUseDefault, CwUseDefault, 123 | 0, // was console handle in experiment 124 | 0, hThisInstance, 0) 125 | if err != nil { 126 | return 0, err 127 | } 128 | // TODO(andlabs): use proper nCmdShow 129 | // TODO(andlabs): call UpdateWindow() 130 | 131 | return hwnd, nil 132 | } 133 | 134 | func windowWndProc(hwnd syscall.Handle, uMsg uint32, wParam uintptr, lParam uintptr) (lResult uintptr) { 135 | fn := windowMsgs[uMsg] 136 | if fn != nil { 137 | return fn(hwnd, uMsg, wParam, lParam) 138 | } 139 | return DefWindowProc(hwnd, uMsg, wParam, lParam) 140 | } 141 | -------------------------------------------------------------------------------- /driver/win32/wm.go: -------------------------------------------------------------------------------- 1 | package win32 2 | 3 | import "syscall" 4 | 5 | // Edit |tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz 6 | // Edit ,x,Wm.,|tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 7 | // Edit ,x,Wm.,x,M_,c,m, 8 | const ( 9 | WmSetfocus = 7 10 | WmKillfocus = 8 11 | WmPaint = 15 12 | WmClose = 16 13 | WmWindowposchanged = 71 14 | WmKeydown = 256 15 | WmKeyup = 257 16 | WmSyskeydown = 260 17 | WmSyskeyup = 261 18 | WmMousemove = 512 19 | WmMousewheel = 522 20 | WmLbuttondown = 513 21 | WmLbuttonup = 514 22 | WmRbuttondown = 516 23 | WmRbuttonup = 517 24 | WmMbuttondown = 519 25 | WmMbuttonup = 520 26 | WmUser = 0x0400 27 | ) 28 | 29 | const ( 30 | WsOverlapped = 0x00000000 31 | WsMaximizebox = 0x00010000 32 | WsMinimizebox = 0x00020000 33 | WsThickframe = 0x00040000 34 | WsSysmenu = 0x00080000 35 | WsDlgframe = 0x00400000 36 | WsBorder = 0x00800000 37 | WsCaption = 0x00c00000 38 | WsClipchildren = 0x02000000 39 | WsClipsiblings = 0x04000000 40 | WsDisabled = 0x08000000 41 | WsVisible = 0x10000000 42 | WsChild = 0x40000000 43 | 44 | WsOverlappedWindow = WsOverlapped | WsCaption | WsSysmenu | WsThickframe | WsMinimizebox | WsMaximizebox 45 | ) 46 | 47 | const ( 48 | VkShift = 16 49 | VkControl = 17 50 | VkMenu = 18 51 | VkLwin = 0x5B 52 | VkRwin = 0x5C 53 | ) 54 | 55 | const ( 56 | MkLbutton = 0x0001 57 | MkMbutton = 0x0010 58 | MkRbutton = 0x0002 59 | ) 60 | 61 | const ( 62 | ColorBtnface = 15 63 | ) 64 | 65 | const ( 66 | IdiApplication = 32512 67 | IdiError = 32513 68 | IdiQuestion = 32514 69 | IdiWarning = 32515 70 | IdiAsterisk = 32516 71 | IdiWinlogo = 32517 72 | IdiShield = 32518 73 | ) 74 | 75 | const ( 76 | IdcAppstarting = (32650) 77 | IdcArrow = (32512) 78 | IdcIbeam = (32513) 79 | IdcWait = (32514) 80 | IdcCross = (32515) 81 | IdcUparrow = (32516) 82 | IdcSize = (32640) 83 | IdcIcon = (32641) 84 | IdcSizenwse = (32642) 85 | IdcSizenesw = (32643) 86 | IdcSizewe = (32644) 87 | IdcSizens = (32645) 88 | IdcSizeall = (32646) 89 | IdcNo = (32648) 90 | IdcHand = (32649) 91 | IdcHelp = (32651) 92 | ) 93 | 94 | const ( 95 | CwUseDefault = 0x80000000 - 0x100000000 96 | SwShowdefault = 10 97 | HwndMessage = syscall.Handle(^uintptr(2)) // -3 98 | SwpNosize = 0x0001 99 | ) 100 | 101 | type Msg struct { 102 | HWND syscall.Handle 103 | Message uint32 104 | Wp uintptr 105 | Lp uintptr 106 | Time uint32 107 | Pt Point 108 | } 109 | 110 | type WindowClass struct { 111 | Style uint32 112 | LpfnWndProc uintptr 113 | CbClsExtra int32 114 | CbWndExtra int32 115 | HInstance syscall.Handle 116 | HIcon syscall.Handle 117 | HCursor syscall.Handle 118 | HbrBackground syscall.Handle 119 | LpszMenuName *uint16 120 | LpszClassName *uint16 121 | } 122 | 123 | type WindowPos struct { 124 | HWND syscall.Handle 125 | HWNDInsertAfter syscall.Handle 126 | X int32 127 | Y int32 128 | Cx int32 129 | Cy int32 130 | Flags uint32 131 | } 132 | -------------------------------------------------------------------------------- /driver/win32/xform.go: -------------------------------------------------------------------------------- 1 | package win32 2 | 3 | type Xform struct { 4 | M11 float32 5 | M12 float32 6 | M21 float32 7 | M22 float32 8 | Dx float32 9 | Dy float32 10 | } 11 | -------------------------------------------------------------------------------- /driver/windriver/buffer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | package windriver 8 | 9 | import ( 10 | "image" 11 | "image/draw" 12 | "syscall" 13 | 14 | "github.com/as/shiny/driver/win32" 15 | ) 16 | 17 | type bufferImpl struct { 18 | hbitmap syscall.Handle 19 | buf, buf2 []byte 20 | rgba image.RGBA 21 | size image.Point 22 | } 23 | 24 | //func (b *bufferImpl) Resize(size image.Point) *bufferImpl { 25 | // real := b.rgba.Bounds().Size() 26 | // curr := b.size 27 | // if size.X > real.X || size.Y > real.Y{ 28 | // // Client wants a larger rectangle than we can provide 29 | // return nil 30 | // } 31 | // if curr.X*curr.Y / 3 > size.X*size.Y{ 32 | // // Very small rectangle 33 | // returnnil 34 | // } 35 | //} 36 | func (b *bufferImpl) Size() image.Point { return b.size } 37 | func (b *bufferImpl) Bounds() image.Rectangle { return image.Rectangle{Max: b.size} } 38 | func (b *bufferImpl) RGBA() *image.RGBA { return &b.rgba } 39 | func (b *bufferImpl) Release() { 40 | go b.cleanUp() 41 | } 42 | 43 | func (b *bufferImpl) cleanUp() { 44 | if b.rgba.Pix != nil { 45 | b.rgba.Pix = nil 46 | win32.DeleteObject(b.hbitmap) 47 | } 48 | } 49 | 50 | func (b *bufferImpl) blitToDC(dc syscall.Handle, dp image.Point, sr image.Rectangle) error { 51 | return copyBitmapToDC(dc, sr.Add(dp.Sub(sr.Min)), b.hbitmap, sr, draw.Src) 52 | } 53 | -------------------------------------------------------------------------------- /driver/windriver/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package windriver provides the Windows driver for accessing a screen. 6 | package windriver // import "github.com/as/shiny/driver/windriver" 7 | 8 | /* 9 | Implementation Details 10 | 11 | On Windows, GUI is managed via user code and OS sending messages to 12 | a window. These messages include paint events, input events and others. 13 | Any thread that hosts GUI must handle incoming window messages through 14 | a "message loop". 15 | 16 | windriver designates the thread that calls Main as the GUI thread. 17 | It locks this thread, creates a special window to handle screen.Screen 18 | calls and runs message loop. All new windows are created by the 19 | same thread, so message loop above handles all their window messages. 20 | 21 | Some general Windows rules about thread affinity of GUI objects: 22 | 23 | part 1: Window handles 24 | https://blogs.msdn.microsoft.com/oldnewthing/20051010-09/?p=33843 25 | 26 | part 2: Device contexts 27 | https://blogs.msdn.microsoft.com/oldnewthing/20051011-10/?p=33823 28 | 29 | part 3: Menus, icons, cursors, and accelerator tables 30 | https://blogs.msdn.microsoft.com/oldnewthing/20051012-00/?p=33803 31 | 32 | part 4: GDI objects and other notes on affinity 33 | https://blogs.msdn.microsoft.com/oldnewthing/20051013-11/?p=33783 34 | 35 | part 5: Object clean-up 36 | https://blogs.msdn.microsoft.com/oldnewthing/20051014-19/?p=33763 37 | 38 | How to build Windows GUI articles: 39 | 40 | http://www.codeproject.com/Articles/1988/Guide-to-WIN-Paint-for-Beginners 41 | http://www.codeproject.com/Articles/2078/Guide-to-WIN-Paint-for-Intermediates 42 | http://www.codeproject.com/Articles/224754/Guide-to-Win-Memory-DC 43 | 44 | */ 45 | -------------------------------------------------------------------------------- /driver/windriver/other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !windows 6 | 7 | package windriver 8 | 9 | import ( 10 | "fmt" 11 | "runtime" 12 | 13 | "github.com/as/shiny/driver/internal/errscreen" 14 | "github.com/as/shiny/screen" 15 | ) 16 | 17 | // Main is called by the program's main function to run the graphical 18 | // application. 19 | // 20 | // It calls f on the Screen, possibly in a separate goroutine, as some OS- 21 | // specific libraries require being on 'the main thread'. It returns when f 22 | // returns. 23 | func Main(f func(screen.Screen)) { 24 | f(errscreen.Stub(fmt.Errorf("windriver: unsupported GOOS/GOARCH %s/%s", runtime.GOOS, runtime.GOARCH))) 25 | } 26 | -------------------------------------------------------------------------------- /driver/windriver/screen.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | package windriver 8 | 9 | import ( 10 | "fmt" 11 | "image" 12 | "unsafe" 13 | 14 | "github.com/as/shiny/driver/win32" 15 | "github.com/as/shiny/screen" 16 | ) 17 | 18 | var theScreen = &screenImpl{} 19 | 20 | type screenImpl struct { 21 | windows *windowImpl 22 | } 23 | 24 | func (*screenImpl) NewBuffer(size image.Point) (screen.Buffer, error) { 25 | const ( 26 | maxInt32 = 0x7fffffff 27 | maxBufLen = maxInt32 28 | ) 29 | if size.X < 0 || size.X > maxInt32 || size.Y < 0 || size.Y > maxInt32 || int64(size.X)*int64(size.Y)*4 > maxBufLen { 30 | return nil, fmt.Errorf("windriver: invalid buffer size %v", size) 31 | } 32 | 33 | hbitmap, bitvalues, err := mkbitmap(size) 34 | if err != nil { 35 | return nil, err 36 | } 37 | bufLen := 4 * size.X * size.Y 38 | array := (*[maxBufLen]byte)(unsafe.Pointer(bitvalues)) 39 | buf := (*array)[:bufLen:bufLen] 40 | buf2 := make([]byte, bufLen, bufLen) 41 | return &bufferImpl{ 42 | hbitmap: hbitmap, 43 | buf: buf, 44 | buf2: buf2, 45 | rgba: image.RGBA{ 46 | Pix: buf2, 47 | Stride: 4 * size.X, 48 | Rect: image.Rectangle{Max: size}, 49 | }, 50 | size: size, 51 | }, nil 52 | } 53 | 54 | func (*screenImpl) NewTexture(size image.Point) (screen.Texture, error) { 55 | return newTexture(size) 56 | } 57 | 58 | func (s *screenImpl) NewWindow(opts *screen.NewWindowOptions) (screen.Window, error) { 59 | h, err := win32.NewWindow(opts) 60 | if err != nil { 61 | return nil, err 62 | } 63 | dc, err := win32.GetDC(h) 64 | if err != nil { 65 | return nil, err 66 | } 67 | _, err = win32.SetGraphicsMode(dc, win32.GmAdvanced) 68 | if err != nil { 69 | return nil, err 70 | } 71 | 72 | s.windows = &windowImpl{ 73 | dc: dc, 74 | hwnd: h, 75 | } 76 | if err = win32.Resize(h, win32.Pt(opts.Width, opts.Height)); err != nil { 77 | return nil, err 78 | } 79 | win32.Show(h) 80 | return s.windows, nil 81 | } 82 | 83 | /* 84 | // experimental: attempt to overlay on top of existing window 85 | func (s *screenImpl) newWindow(opts *screen.NewWindowOptions) (screen.Window, error) { 86 | var ( 87 | err error 88 | ) 89 | h := win32.GetConsoleWindow() 90 | if !opts.Overlay || h == 0 { 91 | // Won't bind to the parent window; create a new one 92 | if h, err = win32.NewWindow(opts); err != nil { 93 | return nil, err 94 | } 95 | } 96 | dc, err := win32.GetDC(h) 97 | if err != nil { 98 | return nil, err 99 | } 100 | _, err = _SetGraphicsMode(dc, _GM_ADVANCED) 101 | if err != nil { 102 | return nil, err 103 | } 104 | 105 | s.windows = &windowImpl{ 106 | dc: dc, 107 | hwnd: h, 108 | } 109 | if err = win32.ResizeClientRect(h, opts); err != nil { 110 | return nil, err 111 | } 112 | win32.Show(h) 113 | return s.windows, nil 114 | } 115 | */ 116 | -------------------------------------------------------------------------------- /driver/windriver/syscall.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go 6 | 7 | package windriver 8 | -------------------------------------------------------------------------------- /driver/windriver/syscall_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package windriver 6 | -------------------------------------------------------------------------------- /driver/windriver/texture.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | package windriver 8 | 9 | import ( 10 | "github.com/as/shiny/driver/internal/swizzle" 11 | "github.com/as/shiny/driver/win32" 12 | "github.com/as/shiny/screen" 13 | "image" 14 | "image/color" 15 | "image/draw" 16 | "sync" 17 | "syscall" 18 | "unsafe" 19 | ) 20 | 21 | type textureImpl struct { 22 | size image.Point 23 | dc syscall.Handle 24 | bitmap syscall.Handle 25 | 26 | mu sync.Mutex 27 | released bool 28 | } 29 | 30 | type handleCreateTextureParams struct { 31 | size image.Point 32 | dc syscall.Handle 33 | bitmap syscall.Handle 34 | err error 35 | } 36 | 37 | var msgCreateTexture = win32.AddScreenMsg(handleCreateTexture) 38 | 39 | func newTexture(size image.Point) (screen.Texture, error) { 40 | p := handleCreateTextureParams{size: size} 41 | win32.SendScreenMessage(msgCreateTexture, 0, uintptr(unsafe.Pointer(&p))) 42 | if p.err != nil { 43 | return nil, p.err 44 | } 45 | return &textureImpl{ 46 | size: size, 47 | dc: p.dc, 48 | bitmap: p.bitmap, 49 | }, nil 50 | } 51 | 52 | func handleCreateTexture(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) { 53 | // This code needs to run on Windows message pump thread. 54 | // Firstly, it calls GetDC(nil) and, according to Windows documentation 55 | // (https://msdn.microsoft.com/en-us/library/windows/desktop/dd144871(v=vs.85).aspx), 56 | // has to be released on the same thread. 57 | // Secondly, according to Windows documentation 58 | // (https://msdn.microsoft.com/en-us/library/windows/desktop/dd183489(v=vs.85).aspx), 59 | // ... thread that calls CreateCompatibleDC owns the HDC that is created. 60 | // When this thread is destroyed, the HDC is no longer valid. ... 61 | // So making Windows message pump thread own returned HDC makes DC 62 | // live as long as we want to. 63 | p := (*handleCreateTextureParams)(unsafe.Pointer(lParam)) 64 | 65 | screenDC, err := win32.GetDC(0) 66 | if err != nil { 67 | p.err = err 68 | return 69 | } 70 | defer win32.ReleaseDC(0, screenDC) 71 | 72 | dc, err := win32.CreateCompatibleDC(screenDC) 73 | if err != nil { 74 | p.err = err 75 | return 76 | } 77 | bitmap, err := win32.CreateCompatibleBitmap(screenDC, int32(p.size.X), int32(p.size.Y)) 78 | if err != nil { 79 | win32.DeleteDC(dc) 80 | p.err = err 81 | return 82 | } 83 | p.dc = dc 84 | p.bitmap = bitmap 85 | } 86 | 87 | func (t *textureImpl) Bounds() image.Rectangle { 88 | return image.Rectangle{Max: t.size} 89 | } 90 | 91 | func (t *textureImpl) Fill(r image.Rectangle, c color.Color, op draw.Op) { 92 | err := t.update(func(dc syscall.Handle) error { 93 | return fill(dc, r, c, op) 94 | }) 95 | if err != nil { 96 | panic(err) // TODO handle error 97 | } 98 | } 99 | 100 | func (t *textureImpl) Release() { 101 | if err := t.release(); err != nil { 102 | panic(err) // TODO handle error 103 | } 104 | } 105 | 106 | func (t *textureImpl) release() error { 107 | t.mu.Lock() 108 | defer t.mu.Unlock() 109 | 110 | if t.released { 111 | return nil 112 | } 113 | t.released = true 114 | 115 | err := win32.DeleteObject(t.bitmap) 116 | if err != nil { 117 | return err 118 | } 119 | return win32.DeleteDC(t.dc) 120 | } 121 | 122 | func (t *textureImpl) Size() image.Point { 123 | return t.size 124 | } 125 | 126 | func (t *textureImpl) Upload(dp image.Point, src screen.Buffer, sr image.Rectangle) { 127 | b := src.(*bufferImpl).buf 128 | b2 := src.(*bufferImpl).buf2 129 | swizzle.Swizzle(b2, b) 130 | src.(*bufferImpl).blitToDC(t.dc, dp, sr) 131 | } 132 | 133 | // update prepares texture t for update and executes f over texture device 134 | // context dc in a safe manner. 135 | func (t *textureImpl) update(f func(dc syscall.Handle) error) (retErr error) { 136 | 137 | // Select t.bitmap into t.dc, so our drawing gets recorded 138 | // into t.bitmap and not into 1x1 default bitmap created 139 | // during CreateCompatibleDC call. 140 | _, err := win32.SelectObject(t.dc, t.bitmap) 141 | if err != nil { 142 | return err 143 | } 144 | 145 | return f(t.dc) 146 | } 147 | -------------------------------------------------------------------------------- /driver/windriver/windraw.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | package windriver 8 | 9 | import ( 10 | "fmt" 11 | "image" 12 | "image/color" 13 | "image/draw" 14 | "syscall" 15 | "unsafe" 16 | 17 | "github.com/as/shiny/driver/win32" 18 | ) 19 | 20 | func mkbitmap(size image.Point) (syscall.Handle, *byte, error) { 21 | bi := win32.BitmapInfo{ 22 | Header: win32.BitmapInfoV4{ 23 | Size: uint32(unsafe.Sizeof(win32.BitmapInfoV4{})), 24 | Width: int32(size.X), 25 | Height: -int32(size.Y), // negative height to force top-down drawing 26 | Planes: 1, 27 | BitCount: 32, 28 | Compression: win32.BiRGB, 29 | SizeImage: uint32(size.X * size.Y * 4), 30 | }, 31 | } 32 | 33 | var ppvBits *byte 34 | bitmap, err := win32.CreateDIBSection(0, &bi, win32.DibRGBColors, &ppvBits, 0, 0) 35 | if err != nil { 36 | return 0, nil, err 37 | } 38 | return bitmap, ppvBits, nil 39 | } 40 | 41 | var blendOverFunc = win32.BlendFunc{ 42 | Op: win32.AcSrcOver, 43 | Flags: 0, 44 | SrcConstAlpha: 255, // only use per-pixel alphas 45 | AlphaFormat: win32.AcSrcAlpha, // premultiplied 46 | } 47 | 48 | func copyBitmapToDC(dc syscall.Handle, dr image.Rectangle, src syscall.Handle, sr image.Rectangle, op draw.Op) (retErr error) { 49 | memdc, err := win32.CreateCompatibleDC(dc) 50 | if err != nil { 51 | return err 52 | } 53 | defer win32.DeleteDC(memdc) 54 | 55 | _, err = win32.SelectObject(memdc, src) 56 | if err != nil { 57 | return err 58 | } 59 | 60 | switch op { 61 | case draw.Src: 62 | return win32.StretchBlt(dc, int32(dr.Min.X), int32(dr.Min.Y), int32(dr.Dx()), int32(dr.Dy()), 63 | memdc, int32(sr.Min.X), int32(sr.Min.Y), int32(sr.Dx()), int32(sr.Dy()), win32.SrcCopy) 64 | case draw.Over: 65 | return win32.AlphaBlend(dc, int32(dr.Min.X), int32(dr.Min.Y), int32(dr.Dx()), int32(dr.Dy()), 66 | memdc, int32(sr.Min.X), int32(sr.Min.Y), int32(sr.Dx()), int32(sr.Dy()), blendOverFunc.Uintptr()) 67 | default: 68 | return fmt.Errorf("windriver: invalid draw operation %v", op) 69 | } 70 | } 71 | 72 | func fill(dc syscall.Handle, dr image.Rectangle, c color.Color, op draw.Op) error { 73 | const bgrmask = ((1 << 24) - 1) 74 | cr := win32.NewColorRef(c) 75 | 76 | if op == draw.Src { 77 | brush, err := win32.CreateSolidBrush(cr & bgrmask) 78 | if err != nil { 79 | return err 80 | } 81 | defer win32.DeleteObject(brush) 82 | 83 | return win32.FillRect(dc, &win32.Rectangle{ 84 | win32.Point{int32(dr.Min.X), int32(dr.Min.Y)}, 85 | win32.Point{int32(dr.Max.X), int32(dr.Max.Y)}, 86 | }, brush) 87 | } 88 | 89 | // AlphaBlend will stretch the input image (using StretchBlt's 90 | // COLORONCOLOR mode) to fill the output rectangle. Testing 91 | // this shows that the result appears to be the same as if we had 92 | // used a MxN bitmap instead. 93 | sr := image.Rectangle{image.ZP, image.Point{1, 1}} 94 | bitmap, bitvalues, err := mkbitmap(sr.Max) 95 | if err != nil { 96 | return err 97 | } 98 | defer win32.DeleteObject(bitmap) 99 | 100 | *(*win32.ColorRef)(unsafe.Pointer(bitvalues)) = cr 101 | 102 | return copyBitmapToDC(dc, dr, bitmap, sr, draw.Over) 103 | } 104 | -------------------------------------------------------------------------------- /driver/windriver/windriver.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | package windriver 8 | 9 | import ( 10 | "github.com/as/shiny/driver/internal/errscreen" 11 | "github.com/as/shiny/driver/win32" 12 | "github.com/as/shiny/screen" 13 | ) 14 | 15 | // Main is called by the program's main function to run the graphical 16 | // application. 17 | // 18 | // It calls f on the Screen, possibly in a separate goroutine, as some OS- 19 | // specific libraries require being on 'the main thread'. It returns when f 20 | // returns. 21 | func Main(f func(screen.Screen)) { 22 | if err := win32.Main(func() { f(theScreen) }); err != nil { 23 | f(errscreen.Stub(err)) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /driver/x11driver/buffer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package x11driver 6 | 7 | import ( 8 | "image" 9 | "image/color" 10 | "image/draw" 11 | "log" 12 | "unsafe" 13 | 14 | "github.com/BurntSushi/xgb" 15 | "github.com/BurntSushi/xgb/render" 16 | "github.com/BurntSushi/xgb/shm" 17 | "github.com/BurntSushi/xgb/xproto" 18 | // "github.com/as/shiny/driver/internal/swizzle" 19 | ) 20 | 21 | type bufferImpl struct { 22 | s *screenImpl 23 | 24 | addr unsafe.Pointer 25 | buf []byte 26 | rgba image.RGBA 27 | size image.Point 28 | xs shm.Seg 29 | 30 | nUpload uint32 31 | released bool 32 | } 33 | 34 | func (b *bufferImpl) degenerate() bool { return b.size.X == 0 || b.size.Y == 0 } 35 | func (b *bufferImpl) Size() image.Point { return b.size } 36 | func (b *bufferImpl) Bounds() image.Rectangle { return image.Rectangle{Max: b.size} } 37 | func (b *bufferImpl) RGBA() *image.RGBA { return &b.rgba } 38 | 39 | func (b *bufferImpl) Release() { 40 | if !b.released && b.nUpload == 0 { 41 | go b.cleanUp() 42 | } 43 | b.released = true 44 | } 45 | 46 | func (b *bufferImpl) cleanUp() { 47 | b.s.mu.Lock() 48 | delete(b.s.buffers, b.xs) 49 | b.s.mu.Unlock() 50 | 51 | if b.degenerate() { 52 | return 53 | } 54 | shm.Detach(b.s.xc, b.xs) 55 | if err := shmClose(b.addr); err != nil { 56 | log.Printf("x11driver: shmClose: %v", err) 57 | } 58 | } 59 | 60 | func (b *bufferImpl) upload(xd xproto.Drawable, xg xproto.Gcontext, depth uint8, dp image.Point, sr image.Rectangle) { 61 | originalSRMin := sr.Min 62 | sr = sr.Intersect(b.Bounds()) 63 | if sr.Empty() { 64 | return 65 | } 66 | dp = dp.Add(sr.Min.Sub(originalSRMin)) 67 | shm.PutImage( 68 | b.s.xc, xd, xg, 69 | uint16(b.size.X), uint16(b.size.Y), // TotalWidth, TotalHeight, 70 | uint16(sr.Min.X), uint16(sr.Min.Y), // SrcX, SrcY, 71 | uint16(sr.Dx()), uint16(sr.Dy()), // SrcWidth, SrcHeight, 72 | int16(dp.X), int16(dp.Y), // DstX, DstY, 73 | depth, xproto.ImageFormatZPixmap, 74 | 1, b.xs, 0, // 1 means send a completion event, 0 means a zero offset. 75 | ) 76 | } 77 | 78 | func fill(xc *xgb.Conn, xp render.Picture, dr image.Rectangle, src color.Color, op draw.Op) { 79 | r, g, b, a := src.RGBA() 80 | c := render.Color{ 81 | Red: uint16(r), 82 | Green: uint16(g), 83 | Blue: uint16(b), 84 | Alpha: uint16(a), 85 | } 86 | x, y := dr.Min.X, dr.Min.Y 87 | if x < -0x8000 || 0x7fff < x || y < -0x8000 || 0x7fff < y { 88 | return 89 | } 90 | dx, dy := dr.Dx(), dr.Dy() 91 | if dx < 0 || 0xffff < dx || dy < 0 || 0xffff < dy { 92 | return 93 | } 94 | render.FillRectangles(xc, renderOp(op), xp, c, []xproto.Rectangle{{ 95 | X: int16(x), 96 | Y: int16(y), 97 | Width: uint16(dx), 98 | Height: uint16(dy), 99 | }}) 100 | } 101 | -------------------------------------------------------------------------------- /driver/x11driver/shm_linux_ipc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build linux 6 | // +build 386 ppc64 ppc64le s390x 7 | 8 | package x11driver 9 | 10 | import ( 11 | "fmt" 12 | "syscall" 13 | "unsafe" 14 | ) 15 | 16 | // These constants are from /usr/include/linux/ipc.h 17 | const ( 18 | ipcPrivate = 0 19 | ipcRmID = 0 20 | 21 | shmAt = 21 22 | shmDt = 22 23 | shmGet = 23 24 | shmCtl = 24 25 | ) 26 | 27 | func shmOpen(size int) (shmid uintptr, addr unsafe.Pointer, err error) { 28 | shmid, _, errno0 := syscall.RawSyscall6(syscall.SYS_IPC, shmGet, ipcPrivate, uintptr(size), 0600, 0, 0) 29 | if errno0 != 0 { 30 | return 0, unsafe.Pointer(uintptr(0)), fmt.Errorf("shmget: %v", errno0) 31 | } 32 | _, _, errno1 := syscall.RawSyscall6(syscall.SYS_IPC, shmAt, shmid, 0, uintptr(unsafe.Pointer(&addr)), 0, 0) 33 | _, _, errno2 := syscall.RawSyscall6(syscall.SYS_IPC, shmCtl, shmid, ipcRmID, 0, 0, 0) 34 | if errno1 != 0 { 35 | return 0, unsafe.Pointer(uintptr(0)), fmt.Errorf("shmat: %v", errno1) 36 | } 37 | if errno2 != 0 { 38 | return 0, unsafe.Pointer(uintptr(0)), fmt.Errorf("shmctl: %v", errno2) 39 | } 40 | return shmid, addr, nil 41 | } 42 | 43 | func shmClose(p unsafe.Pointer) error { 44 | _, _, errno := syscall.RawSyscall6(syscall.SYS_IPC, shmDt, 0, 0, 0, uintptr(p), 0) 45 | if errno != 0 { 46 | return fmt.Errorf("shmdt: %v", errno) 47 | } 48 | return nil 49 | } 50 | -------------------------------------------------------------------------------- /driver/x11driver/shm_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !linux 6 | // +build !dragonfly 7 | 8 | package x11driver 9 | 10 | import ( 11 | "fmt" 12 | "runtime" 13 | "unsafe" 14 | ) 15 | 16 | func shmOpen(size int) (shmid uintptr, addr unsafe.Pointer, err error) { 17 | return 0, unsafe.Pointer(uintptr(0)), 18 | fmt.Errorf("unsupported GOOS/GOARCH %s/%s", runtime.GOOS, runtime.GOARCH) 19 | } 20 | 21 | func shmClose(p unsafe.Pointer) error { 22 | return fmt.Errorf("unsupported GOOS/GOARCH %s/%s", runtime.GOOS, runtime.GOARCH) 23 | } 24 | -------------------------------------------------------------------------------- /driver/x11driver/shm_shmopen_syscall.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build linux dragonfly 6 | // +build amd64 arm arm64 mips64 mips64le 7 | 8 | package x11driver 9 | 10 | import ( 11 | "fmt" 12 | "syscall" 13 | "unsafe" 14 | ) 15 | 16 | // These constants are from /usr/include/linux/ipc.h 17 | const ( 18 | ipcPrivate = 0 19 | ipcRmID = 0 20 | ) 21 | 22 | func shmOpen(size int) (shmid uintptr, addr unsafe.Pointer, err error) { 23 | shmid, _, errno0 := syscall.RawSyscall(syscall.SYS_SHMGET, ipcPrivate, uintptr(size), 0600) 24 | if errno0 != 0 { 25 | return 0, unsafe.Pointer(uintptr(0)), fmt.Errorf("shmget: %v", errno0) 26 | } 27 | p, _, errno1 := syscall.RawSyscall(syscall.SYS_SHMAT, shmid, 0, 0) 28 | _, _, errno2 := syscall.RawSyscall(syscall.SYS_SHMCTL, shmid, ipcRmID, 0) 29 | if errno1 != 0 { 30 | return 0, unsafe.Pointer(uintptr(0)), fmt.Errorf("shmat: %v", errno1) 31 | } 32 | if errno2 != 0 { 33 | return 0, unsafe.Pointer(uintptr(0)), fmt.Errorf("shmctl: %v", errno2) 34 | } 35 | return shmid, unsafe.Pointer(p), nil 36 | } 37 | 38 | func shmClose(p unsafe.Pointer) error { 39 | _, _, errno := syscall.RawSyscall(syscall.SYS_SHMDT, uintptr(p), 0, 0) 40 | if errno != 0 { 41 | return fmt.Errorf("shmdt: %v", errno) 42 | } 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /driver/x11driver/x11driver.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package x11driver provides the X11 driver for accessing a screen. 6 | package x11driver // import "github.com/as/shiny/driver/x11driver" 7 | 8 | // TODO: figure out what to say about the responsibility for users of this 9 | // package to check any implicit dependencies' LICENSEs. For example, the 10 | // driver might use third party software outside of golang.org/x, like an X11 11 | // or OpenGL library. 12 | 13 | import ( 14 | "fmt" 15 | 16 | "github.com/BurntSushi/xgb" 17 | "github.com/BurntSushi/xgb/render" 18 | "github.com/BurntSushi/xgb/shm" 19 | 20 | "github.com/as/shiny/driver/internal/errscreen" 21 | "github.com/as/shiny/screen" 22 | ) 23 | 24 | // Main is called by the program's main function to run the graphical 25 | // application. 26 | // 27 | // It calls f on the Screen, possibly in a separate goroutine, as some OS- 28 | // specific libraries require being on 'the main thread'. It returns when f 29 | // returns. 30 | func Main(f func(screen.Screen)) { 31 | if err := main(f); err != nil { 32 | f(errscreen.Stub(err)) 33 | } 34 | } 35 | 36 | func main(f func(screen.Screen)) (retErr error) { 37 | xc, err := xgb.NewConn() 38 | if err != nil { 39 | return fmt.Errorf("x11driver: xgb.NewConn failed: %v", err) 40 | } 41 | defer func() { 42 | if retErr != nil { 43 | xc.Close() 44 | } 45 | }() 46 | 47 | if err := render.Init(xc); err != nil { 48 | return fmt.Errorf("x11driver: render.Init failed: %v", err) 49 | } 50 | if err := shm.Init(xc); err != nil { 51 | return fmt.Errorf("x11driver: shm.Init failed: %v", err) 52 | } 53 | 54 | s, err := newScreenImpl(xc) 55 | if err != nil { 56 | return err 57 | } 58 | f(s) 59 | // TODO: tear down the s.run goroutine? It's probably not worth the 60 | // complexity of doing it cleanly, if the app is about to exit anyway. 61 | return nil 62 | } 63 | -------------------------------------------------------------------------------- /event/key/code_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=Code"; DO NOT EDIT 2 | 3 | package key 4 | 5 | import "fmt" 6 | 7 | const ( 8 | _Code_name_0 = "CodeUnknown" 9 | _Code_name_1 = "CodeACodeBCodeCCodeDCodeECodeFCodeGCodeHCodeICodeJCodeKCodeLCodeMCodeNCodeOCodePCodeQCodeRCodeSCodeTCodeUCodeVCodeWCodeXCodeYCodeZCode1Code2Code3Code4Code5Code6Code7Code8Code9Code0CodeReturnEnterCodeEscapeCodeDeleteBackspaceCodeTabCodeSpacebarCodeHyphenMinusCodeEqualSignCodeLeftSquareBracketCodeRightSquareBracketCodeBackslash" 10 | _Code_name_2 = "CodeSemicolonCodeApostropheCodeGraveAccentCodeCommaCodeFullStopCodeSlashCodeCapsLockCodeF1CodeF2CodeF3CodeF4CodeF5CodeF6CodeF7CodeF8CodeF9CodeF10CodeF11CodeF12" 11 | _Code_name_3 = "CodePauseCodeInsertCodeHomeCodePageUpCodeDeleteForwardCodeEndCodePageDownCodeRightArrowCodeLeftArrowCodeDownArrowCodeUpArrowCodeKeypadNumLockCodeKeypadSlashCodeKeypadAsteriskCodeKeypadHyphenMinusCodeKeypadPlusSignCodeKeypadEnterCodeKeypad1CodeKeypad2CodeKeypad3CodeKeypad4CodeKeypad5CodeKeypad6CodeKeypad7CodeKeypad8CodeKeypad9CodeKeypad0CodeKeypadFullStop" 12 | _Code_name_4 = "CodeKeypadEqualSignCodeF13CodeF14CodeF15CodeF16CodeF17CodeF18CodeF19CodeF20CodeF21CodeF22CodeF23CodeF24" 13 | _Code_name_5 = "CodeHelp" 14 | _Code_name_6 = "CodeMuteCodeVolumeUpCodeVolumeDown" 15 | _Code_name_7 = "CodeLeftControlCodeLeftShiftCodeLeftAltCodeLeftGUICodeRightControlCodeRightShiftCodeRightAltCodeRightGUI" 16 | _Code_name_8 = "CodeCompose" 17 | ) 18 | 19 | var ( 20 | _Code_index_0 = [...]uint8{0, 11} 21 | _Code_index_1 = [...]uint16{0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100, 105, 110, 115, 120, 125, 130, 135, 140, 145, 150, 155, 160, 165, 170, 175, 180, 195, 205, 224, 231, 243, 258, 271, 292, 314, 327} 22 | _Code_index_2 = [...]uint8{0, 13, 27, 42, 51, 63, 72, 84, 90, 96, 102, 108, 114, 120, 126, 132, 138, 145, 152, 159} 23 | _Code_index_3 = [...]uint16{0, 9, 19, 27, 37, 54, 61, 73, 87, 100, 113, 124, 141, 156, 174, 195, 213, 228, 239, 250, 261, 272, 283, 294, 305, 316, 327, 338, 356} 24 | _Code_index_4 = [...]uint8{0, 19, 26, 33, 40, 47, 54, 61, 68, 75, 82, 89, 96, 103} 25 | _Code_index_5 = [...]uint8{0, 8} 26 | _Code_index_6 = [...]uint8{0, 8, 20, 34} 27 | _Code_index_7 = [...]uint8{0, 15, 28, 39, 50, 66, 80, 92, 104} 28 | _Code_index_8 = [...]uint8{0, 11} 29 | ) 30 | 31 | func (i Code) String() string { 32 | switch { 33 | case i == 0: 34 | return _Code_name_0 35 | case 4 <= i && i <= 49: 36 | i -= 4 37 | return _Code_name_1[_Code_index_1[i]:_Code_index_1[i+1]] 38 | case 51 <= i && i <= 69: 39 | i -= 51 40 | return _Code_name_2[_Code_index_2[i]:_Code_index_2[i+1]] 41 | case 72 <= i && i <= 99: 42 | i -= 72 43 | return _Code_name_3[_Code_index_3[i]:_Code_index_3[i+1]] 44 | case 103 <= i && i <= 115: 45 | i -= 103 46 | return _Code_name_4[_Code_index_4[i]:_Code_index_4[i+1]] 47 | case i == 117: 48 | return _Code_name_5 49 | case 127 <= i && i <= 129: 50 | i -= 127 51 | return _Code_name_6[_Code_index_6[i]:_Code_index_6[i+1]] 52 | case 224 <= i && i <= 231: 53 | i -= 224 54 | return _Code_name_7[_Code_index_7[i]:_Code_index_7[i+1]] 55 | case i == 65536: 56 | return _Code_name_8 57 | default: 58 | return fmt.Sprintf("Code(%d)", i) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /event/lifecycle/lifecycle.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package lifecycle 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | // Cross is whether a lifecycle stage was crossed. 12 | type Cross uint32 13 | 14 | func (c Cross) String() string { 15 | switch c { 16 | case CrossOn: 17 | return "on" 18 | case CrossOff: 19 | return "off" 20 | } 21 | return "none" 22 | } 23 | 24 | const ( 25 | CrossNone Cross = 0 26 | CrossOn Cross = 1 27 | CrossOff Cross = 2 28 | ) 29 | 30 | // Event is a lifecycle change from an old stage to a new stage. 31 | type Event struct { 32 | From, To Stage 33 | 34 | // DrawContext is the state used for painting, if any is valid. 35 | // 36 | // For OpenGL apps, a non-nil DrawContext is a gl.Context. 37 | // 38 | // TODO: make this an App method if we move away from an event channel? 39 | DrawContext interface{} 40 | } 41 | 42 | func (e Event) String() string { 43 | return fmt.Sprintf("lifecycle.Event{From:%v, To:%v, DrawContext:%v}", e.From, e.To, e.DrawContext) 44 | } 45 | 46 | // Crosses returns whether the transition from From to To crosses the stage s: 47 | // - It returns CrossOn if it does, and the lifecycle change is positive. 48 | // - It returns CrossOff if it does, and the lifecycle change is negative. 49 | // - Otherwise, it returns CrossNone. 50 | // See the documentation for Stage for more discussion of positive and negative 51 | // crosses. 52 | func (e Event) Crosses(s Stage) Cross { 53 | switch { 54 | case e.From < s && e.To >= s: 55 | return CrossOn 56 | case e.From >= s && e.To < s: 57 | return CrossOff 58 | } 59 | return CrossNone 60 | } 61 | 62 | // Stage is a stage in the app's lifecycle. The values are ordered, so that a 63 | // lifecycle change from stage From to stage To implicitly crosses every stage 64 | // in the range (min, max], exclusive on the low end and inclusive on the high 65 | // end, where min is the minimum of From and To, and max is the maximum. 66 | // 67 | // The documentation for individual stages talk about positive and negative 68 | // crosses. A positive lifecycle change is one where its From stage is less 69 | // than its To stage. Similarly, a negative lifecycle change is one where From 70 | // is greater than To. Thus, a positive lifecycle change crosses every stage in 71 | // the range (From, To] in increasing order, and a negative lifecycle change 72 | // crosses every stage in the range (To, From] in decreasing order. 73 | type Stage uint32 74 | 75 | // TODO: how does iOS map to these stages? What do cross-platform mobile 76 | // abstractions do? 77 | 78 | const ( 79 | // StageDead is the zero stage. No lifecycle change crosses this stage, 80 | // but: 81 | // - A positive change from this stage is the very first lifecycle change. 82 | // - A negative change to this stage is the very last lifecycle change. 83 | StageDead Stage = iota 84 | 85 | // StageAlive means that the app is alive. 86 | // - A positive cross means that the app has been created. 87 | // - A negative cross means that the app is being destroyed. 88 | // Each cross, either from or to StageDead, will occur only once. 89 | // On Android, these correspond to onCreate and onDestroy. 90 | StageAlive 91 | 92 | // StageVisible means that the app window is visible. 93 | // - A positive cross means that the app window has become visible. 94 | // - A negative cross means that the app window has become invisible. 95 | // On Android, these correspond to onStart and onStop. 96 | // On Desktop, an app window can become invisible if e.g. it is minimized, 97 | // unmapped, or not on a visible workspace. 98 | StageVisible 99 | 100 | // StageFocused means that the app window has the focus. 101 | // - A positive cross means that the app window has gained the focus. 102 | // - A negative cross means that the app window has lost the focus. 103 | // On Android, these correspond to onResume and onFreeze. 104 | StageFocused 105 | ) 106 | 107 | func (s Stage) String() string { 108 | switch s { 109 | case StageDead: 110 | return "StageDead" 111 | case StageAlive: 112 | return "StageAlive" 113 | case StageVisible: 114 | return "StageVisible" 115 | case StageFocused: 116 | return "StageFocused" 117 | default: 118 | return fmt.Sprintf("lifecycle.Stage(%d)", s) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /event/mouse/mouse.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package mouse 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/as/shiny/event/key" 11 | ) 12 | 13 | // Event is a mouse event. 14 | type Event struct { 15 | // X and Y are the mouse location, in pixels. 16 | X, Y float32 17 | 18 | // Button is the mouse button being pressed or released. Its value may be 19 | // zero, for a mouse move or drag without any button change. 20 | Button Button 21 | 22 | // TODO: have a field to hold what other buttons are down, for detecting 23 | // drags or button-chords. 24 | 25 | // Modifiers is a bitmask representing a set of modifier keys: 26 | // key.ModShift, key.ModAlt, etc. 27 | Modifiers key.Modifiers 28 | 29 | // Direction is the direction of the mouse event: DirPress, DirRelease, 30 | // or DirNone (for mouse moves or drags). 31 | Direction Direction 32 | 33 | // TODO: add a Device ID, for multiple input devices? 34 | // TODO: add a time.Time? 35 | } 36 | 37 | // Button is a mouse button. 38 | type Button int32 39 | 40 | // IsWheel returns whether the button is for a scroll wheel. 41 | func (b Button) IsWheel() bool { 42 | return b < 0 43 | } 44 | 45 | // TODO: have a separate axis concept for wheel up/down? How does that relate 46 | // to joystick events? 47 | 48 | const ( 49 | ButtonNone Button = +0 50 | ButtonLeft Button = +1 51 | ButtonMiddle Button = +2 52 | ButtonRight Button = +3 53 | 54 | ButtonWheelUp Button = -1 55 | ButtonWheelDown Button = -2 56 | ButtonWheelLeft Button = -3 57 | ButtonWheelRight Button = -4 58 | ) 59 | 60 | // Direction is the direction of the mouse event. 61 | type Direction uint8 62 | 63 | const ( 64 | DirNone Direction = 0 65 | DirPress Direction = 1 66 | DirRelease Direction = 2 67 | // DirStep is a simultaneous press and release, such as a single step of a 68 | // mouse wheel. 69 | // 70 | // Its value equals DirPress | DirRelease. 71 | DirStep Direction = 3 72 | ) 73 | 74 | func (d Direction) String() string { 75 | switch d { 76 | case DirNone: 77 | return "None" 78 | case DirPress: 79 | return "Press" 80 | case DirRelease: 81 | return "Release" 82 | case DirStep: 83 | return "Step" 84 | default: 85 | return fmt.Sprintf("mouse.Direction(%d)", d) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /event/paint/paint.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package paint 6 | 7 | type Event struct { 8 | // External is true for paint events sent by the screen driver. 9 | // 10 | // An external event may be sent at any time in response to an 11 | // operating system event, for example the window opened, was 12 | // resized, or the screen memory was lost. 13 | // 14 | // Programs actively drawing to the screen as fast as vsync allows 15 | // should ignore external paint events to avoid a backlog of paint 16 | // events building up. 17 | External bool 18 | } 19 | -------------------------------------------------------------------------------- /event/size/size.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package size 6 | 7 | import ( 8 | "image" 9 | 10 | "github.com/as/shiny/geom" 11 | ) 12 | 13 | // Event holds the dimensions, physical resolution and orientation of the app's 14 | // window. 15 | type Event struct { 16 | // WidthPx and HeightPx are the window's dimensions in pixels. 17 | WidthPx, HeightPx int 18 | 19 | // WidthPt and HeightPt are the window's physical dimensions in points 20 | // (1/72 of an inch). 21 | // 22 | // The values are based on PixelsPerPt and are therefore approximate, as 23 | // per the comment on PixelsPerPt. 24 | WidthPt, HeightPt geom.Pt 25 | 26 | // PixelsPerPt is the window's physical resolution. It is the number of 27 | // pixels in a single geom.Pt, from the github.com/as/shiny/geom package. 28 | // 29 | // There are a wide variety of pixel densities in existing phones and 30 | // tablets, so apps should be written to expect various non-integer 31 | // PixelsPerPt values. In general, work in geom.Pt. 32 | // 33 | // The value is approximate, in that the OS, drivers or hardware may report 34 | // approximate or quantized values. An N x N pixel square should be roughly 35 | // 1 square inch for N = int(PixelsPerPt * 72), although different square 36 | // lengths (in pixels) might be closer to 1 inch in practice. Nonetheless, 37 | // this PixelsPerPt value should be consistent with e.g. the ratio of 38 | // WidthPx to WidthPt. 39 | PixelsPerPt float32 40 | 41 | // Orientation is the orientation of the device screen. 42 | Orientation Orientation 43 | } 44 | 45 | // Size returns the window's size in pixels, at the time this size event was 46 | // sent. 47 | func (e Event) Size() image.Point { 48 | return image.Point{e.WidthPx, e.HeightPx} 49 | } 50 | 51 | // Bounds returns the window's bounds in pixels, at the time this size event 52 | // was sent. 53 | // 54 | // The top-left pixel is always (0, 0). The bottom-right pixel is given by the 55 | // width and height. 56 | func (e Event) Bounds() image.Rectangle { 57 | return image.Rectangle{Max: image.Point{e.WidthPx, e.HeightPx}} 58 | } 59 | 60 | // Orientation is the orientation of the device screen. 61 | type Orientation int 62 | 63 | const ( 64 | // OrientationUnknown means device orientation cannot be determined. 65 | // 66 | // Equivalent on Android to Configuration.ORIENTATION_UNKNOWN 67 | // and on iOS to: 68 | // UIDeviceOrientationUnknown 69 | // UIDeviceOrientationFaceUp 70 | // UIDeviceOrientationFaceDown 71 | OrientationUnknown Orientation = iota 72 | 73 | // OrientationPortrait is a device oriented so it is tall and thin. 74 | // 75 | // Equivalent on Android to Configuration.ORIENTATION_PORTRAIT 76 | // and on iOS to: 77 | // UIDeviceOrientationPortrait 78 | // UIDeviceOrientationPortraitUpsideDown 79 | OrientationPortrait 80 | 81 | // OrientationLandscape is a device oriented so it is short and wide. 82 | // 83 | // Equivalent on Android to Configuration.ORIENTATION_LANDSCAPE 84 | // and on iOS to: 85 | // UIDeviceOrientationLandscapeLeft 86 | // UIDeviceOrientationLandscapeRight 87 | OrientationLandscape 88 | ) 89 | -------------------------------------------------------------------------------- /event/touch/touch.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package touch defines an event for touch input. 6 | package touch 7 | 8 | // The best source on android input events is the NDK: include/android/input.h 9 | // 10 | // iOS event handling guide: 11 | // https://developer.apple.com/library/ios/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS 12 | 13 | import ( 14 | "fmt" 15 | ) 16 | 17 | // Event is a touch event. 18 | type Event struct { 19 | // X and Y are the touch location, in pixels. 20 | X, Y float32 21 | 22 | // Sequence is the sequence number. The same number is shared by all events 23 | // in a sequence. A sequence begins with a single TypeBegin, is followed by 24 | // zero or more TypeMoves, and ends with a single TypeEnd. A Sequence 25 | // distinguishes concurrent sequences but its value is subsequently reused. 26 | Sequence Sequence 27 | 28 | // Type is the touch type. 29 | Type Type 30 | } 31 | 32 | // Sequence identifies a sequence of touch events. 33 | type Sequence int64 34 | 35 | // Type describes the type of a touch event. 36 | type Type byte 37 | 38 | const ( 39 | // TypeBegin is a user first touching the device. 40 | // 41 | // On Android, this is a AMOTION_EVENT_ACTION_DOWN. 42 | // On iOS, this is a call to touchesBegan. 43 | TypeBegin Type = iota 44 | 45 | // TypeMove is a user dragging across the device. 46 | // 47 | // A TypeMove is delivered between a TypeBegin and TypeEnd. 48 | // 49 | // On Android, this is a AMOTION_EVENT_ACTION_MOVE. 50 | // On iOS, this is a call to touchesMoved. 51 | TypeMove 52 | 53 | // TypeEnd is a user no longer touching the device. 54 | // 55 | // On Android, this is a AMOTION_EVENT_ACTION_UP. 56 | // On iOS, this is a call to touchesEnded. 57 | TypeEnd 58 | ) 59 | 60 | func (t Type) String() string { 61 | switch t { 62 | case TypeBegin: 63 | return "begin" 64 | case TypeMove: 65 | return "move" 66 | case TypeEnd: 67 | return "end" 68 | } 69 | return fmt.Sprintf("touch.Type(%d)", t) 70 | } 71 | -------------------------------------------------------------------------------- /geom/geom.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package geom 6 | 7 | import "fmt" 8 | 9 | // Pt is a length. 10 | // 11 | // The unit Pt is a typographical point, 1/72 of an inch (0.3527 mm). 12 | // 13 | // It can be be converted to a length in current device pixels by 14 | // multiplying with PixelsPerPt after app initialization is complete. 15 | type Pt float32 16 | 17 | // Px converts the length to current device pixels. 18 | func (p Pt) Px(pixelsPerPt float32) float32 { return float32(p) * pixelsPerPt } 19 | 20 | // String returns a string representation of p like "3.2pt". 21 | func (p Pt) String() string { return fmt.Sprintf("%.2fpt", p) } 22 | 23 | // Point is a point in a two-dimensional plane. 24 | type Point struct { 25 | X, Y Pt 26 | } 27 | 28 | // String returns a string representation of p like "(1.2,3.4)". 29 | func (p Point) String() string { return fmt.Sprintf("(%.2f,%.2f)", p.X, p.Y) } 30 | 31 | // A Rectangle is region of points. 32 | // The top-left point is Min, and the bottom-right point is Max. 33 | type Rectangle struct { 34 | Min, Max Point 35 | } 36 | 37 | // String returns a string representation of r like "(3,4)-(6,5)". 38 | func (r Rectangle) String() string { return r.Min.String() + "-" + r.Max.String() } 39 | 40 | /* 41 | The coordinate system is based on an left-handed Cartesian plane. 42 | That is, X increases to the right and Y increases down. For (x,y), 43 | 44 | (0,0) → (1,0) 45 | ↓ ↘ 46 | (0,1) (1,1) 47 | 48 | The display window places the origin (0, 0) in the upper-left corner of 49 | the screen. Positions on the plane are measured in typographic points, 50 | 1/72 of an inch, which is represented by the Pt type. 51 | 52 | Any interface that draws to the screen using types from the geom package 53 | scales the number of pixels to maintain a Pt as 1/72 of an inch. 54 | Notes on the various underlying coordinate systems. 55 | 56 | Both Android and iOS (UIKit) use upper-left-origin coordinate systems 57 | with for events, however they have different units. 58 | 59 | UIKit measures distance in points. A point is a single-pixel on a 60 | pre-Retina display. UIKit maintains a scale factor that to turn points 61 | into pixels. On current retina devices, the scale factor is 2.0. 62 | 63 | A UIKit point does not correspond to a fixed physical distance, as the 64 | iPhone has a 163 DPI/PPI (326 PPI retina) display, and the iPad has a 65 | 132 PPI (264 retina) display. Points are 32-bit floats. 66 | 67 | Even though point is the official UIKit term, they are commonly called 68 | pixels. Indeed, the units were equivalent until the retina display was 69 | introduced. 70 | 71 | N.b. as a UIKit point is unrelated to a typographic point, it is not 72 | related to this packages's Pt and Point types. 73 | 74 | More details about iOS drawing: 75 | 76 | https://developer.apple.com/library/ios/documentation/2ddrawing/conceptual/drawingprintingios/GraphicsDrawingOverview/GraphicsDrawingOverview.html 77 | 78 | Android uses pixels. Sub-pixel precision is possible, so pixels are 79 | represented as 32-bit floats. The ACONFIGURATION_DENSITY enum provides 80 | the screen DPI/PPI, which varies frequently between devices. 81 | 82 | It would be tempting to adopt the pixel, given the clear pixel/DPI split 83 | in the core android events API. However, the plot thickens: 84 | 85 | http://developer.android.com/training/multiscreen/screendensities.html 86 | 87 | Android promotes the notion of a density-independent pixel in many of 88 | their interfaces, often prefixed by "dp". 1dp is a real physical length, 89 | as "independent" means it is assumed to be 1/160th of an inch and is 90 | adjusted for the current screen. 91 | 92 | In addition, android has a scale-indepdendent pixel used for expressing 93 | a user's preferred text size. The user text size preference is a useful 94 | notion not yet expressed in the geom package. 95 | 96 | For the sake of clarity when working across platforms, the geom package 97 | tries to put distance between it and the word pixel. 98 | */ 99 | -------------------------------------------------------------------------------- /gl/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package gl implements Go bindings for OpenGL ES 2.0 and ES 3.0. 7 | 8 | The GL functions are defined on a Context object that is responsible for 9 | tracking a GL context. Typically a windowing system package (such as 10 | github.com/as/shiny/screen) will call NewContext and provide 11 | a gl.Context for a user application. 12 | 13 | If the gl package is compiled on a platform capable of supporting ES 3.0, 14 | the gl.Context object also implements gl.Context3. 15 | 16 | The bindings are deliberately minimal, staying as close the C API as 17 | possible. The semantics of each function maps onto functions 18 | described in the Khronos documentation: 19 | 20 | https://www.khronos.org/opengles/sdk/docs/man/ 21 | 22 | One notable departure from the C API is the introduction of types 23 | to represent common uses of GLint: Texture, Surface, Buffer, etc. 24 | 25 | Debug Logging 26 | 27 | A tracing version of the OpenGL bindings is behind the `gldebug` build 28 | tag. It acts as a simplified version of apitrace. Build your Go binary 29 | with 30 | 31 | -tags gldebug 32 | 33 | and each call to a GL function will log its input, output, and any 34 | error messages. For example, 35 | 36 | I/GoLog (27668): gl.GenBuffers(1) [Buffer(70001)] 37 | I/GoLog (27668): gl.BindBuffer(ARRAY_BUFFER, Buffer(70001)) 38 | I/GoLog (27668): gl.BufferData(ARRAY_BUFFER, 36, len(36), STATIC_DRAW) 39 | I/GoLog (27668): gl.BindBuffer(ARRAY_BUFFER, Buffer(70001)) 40 | I/GoLog (27668): gl.VertexAttribPointer(Attrib(0), 6, FLOAT, false, 0, 0) error: [INVALID_VALUE] 41 | 42 | The gldebug tracing has very high overhead, so make sure to remove 43 | the build tag before deploying any binaries. 44 | */ 45 | package gl // import "github.com/as/shiny/gl" 46 | 47 | /* 48 | Implementation details. 49 | 50 | All GL function calls fill out a C.struct_fnargs and drop it on the work 51 | queue. The Start function drains the work queue and hands over a batch 52 | of calls to C.process which runs them. This allows multiple GL calls to 53 | be executed in a single cgo call. 54 | 55 | A GL call is marked as blocking if it returns a value, or if it takes a 56 | Go pointer. In this case the call will not return until C.process sends a 57 | value on the retvalue channel. 58 | 59 | This implementation ensures any goroutine can make GL calls, but it does 60 | not make the GL interface safe for simultaneous use by multiple goroutines. 61 | For the purpose of analyzing this code for race conditions, picture two 62 | separate goroutines: one blocked on gl.Start, and another making calls to 63 | the gl package exported functions. 64 | */ 65 | 66 | //go:generate go run gendebug.go -o gldebug.go 67 | -------------------------------------------------------------------------------- /gl/fn.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gl 6 | 7 | import "unsafe" 8 | 9 | type call struct { 10 | args fnargs 11 | parg unsafe.Pointer 12 | blocking bool 13 | } 14 | 15 | type fnargs struct { 16 | fn glfn 17 | 18 | a0 uintptr 19 | a1 uintptr 20 | a2 uintptr 21 | a3 uintptr 22 | a4 uintptr 23 | a5 uintptr 24 | a6 uintptr 25 | a7 uintptr 26 | a8 uintptr 27 | a9 uintptr 28 | } 29 | 30 | type glfn int 31 | 32 | const ( 33 | glfnUNDEFINED glfn = iota 34 | glfnActiveTexture 35 | glfnAttachShader 36 | glfnBindAttribLocation 37 | glfnBindBuffer 38 | glfnBindFramebuffer 39 | glfnBindRenderbuffer 40 | glfnBindTexture 41 | glfnBindVertexArray 42 | glfnBlendColor 43 | glfnBlendEquation 44 | glfnBlendEquationSeparate 45 | glfnBlendFunc 46 | glfnBlendFuncSeparate 47 | glfnBufferData 48 | glfnBufferSubData 49 | glfnCheckFramebufferStatus 50 | glfnClear 51 | glfnClearColor 52 | glfnClearDepthf 53 | glfnClearStencil 54 | glfnColorMask 55 | glfnCompileShader 56 | glfnCompressedTexImage2D 57 | glfnCompressedTexSubImage2D 58 | glfnCopyTexImage2D 59 | glfnCopyTexSubImage2D 60 | glfnCreateProgram 61 | glfnCreateShader 62 | glfnCullFace 63 | glfnDeleteBuffer 64 | glfnDeleteFramebuffer 65 | glfnDeleteProgram 66 | glfnDeleteRenderbuffer 67 | glfnDeleteShader 68 | glfnDeleteTexture 69 | glfnDeleteVertexArray 70 | glfnDepthFunc 71 | glfnDepthRangef 72 | glfnDepthMask 73 | glfnDetachShader 74 | glfnDisable 75 | glfnDisableVertexAttribArray 76 | glfnDrawArrays 77 | glfnDrawElements 78 | glfnEnable 79 | glfnEnableVertexAttribArray 80 | glfnFinish 81 | glfnFlush 82 | glfnFramebufferRenderbuffer 83 | glfnFramebufferTexture2D 84 | glfnFrontFace 85 | glfnGenBuffer 86 | glfnGenFramebuffer 87 | glfnGenRenderbuffer 88 | glfnGenTexture 89 | glfnGenVertexArray 90 | glfnGenerateMipmap 91 | glfnGetActiveAttrib 92 | glfnGetActiveUniform 93 | glfnGetAttachedShaders 94 | glfnGetAttribLocation 95 | glfnGetBooleanv 96 | glfnGetBufferParameteri 97 | glfnGetError 98 | glfnGetFloatv 99 | glfnGetFramebufferAttachmentParameteriv 100 | glfnGetIntegerv 101 | glfnGetProgramInfoLog 102 | glfnGetProgramiv 103 | glfnGetRenderbufferParameteriv 104 | glfnGetShaderInfoLog 105 | glfnGetShaderPrecisionFormat 106 | glfnGetShaderSource 107 | glfnGetShaderiv 108 | glfnGetString 109 | glfnGetTexParameterfv 110 | glfnGetTexParameteriv 111 | glfnGetUniformLocation 112 | glfnGetUniformfv 113 | glfnGetUniformiv 114 | glfnGetVertexAttribfv 115 | glfnGetVertexAttribiv 116 | glfnHint 117 | glfnIsBuffer 118 | glfnIsEnabled 119 | glfnIsFramebuffer 120 | glfnIsProgram 121 | glfnIsRenderbuffer 122 | glfnIsShader 123 | glfnIsTexture 124 | glfnLineWidth 125 | glfnLinkProgram 126 | glfnPixelStorei 127 | glfnPolygonOffset 128 | glfnReadPixels 129 | glfnReleaseShaderCompiler 130 | glfnRenderbufferStorage 131 | glfnSampleCoverage 132 | glfnScissor 133 | glfnShaderSource 134 | glfnStencilFunc 135 | glfnStencilFuncSeparate 136 | glfnStencilMask 137 | glfnStencilMaskSeparate 138 | glfnStencilOp 139 | glfnStencilOpSeparate 140 | glfnTexImage2D 141 | glfnTexParameterf 142 | glfnTexParameterfv 143 | glfnTexParameteri 144 | glfnTexParameteriv 145 | glfnTexSubImage2D 146 | glfnUniform1f 147 | glfnUniform1fv 148 | glfnUniform1i 149 | glfnUniform1iv 150 | glfnUniform2f 151 | glfnUniform2fv 152 | glfnUniform2i 153 | glfnUniform2iv 154 | glfnUniform3f 155 | glfnUniform3fv 156 | glfnUniform3i 157 | glfnUniform3iv 158 | glfnUniform4f 159 | glfnUniform4fv 160 | glfnUniform4i 161 | glfnUniform4iv 162 | glfnUniformMatrix2fv 163 | glfnUniformMatrix3fv 164 | glfnUniformMatrix4fv 165 | glfnUseProgram 166 | glfnValidateProgram 167 | glfnVertexAttrib1f 168 | glfnVertexAttrib1fv 169 | glfnVertexAttrib2f 170 | glfnVertexAttrib2fv 171 | glfnVertexAttrib3f 172 | glfnVertexAttrib3fv 173 | glfnVertexAttrib4f 174 | glfnVertexAttrib4fv 175 | glfnVertexAttribPointer 176 | glfnViewport 177 | 178 | // ES 3.0 functions 179 | glfnUniformMatrix2x3fv 180 | glfnUniformMatrix3x2fv 181 | glfnUniformMatrix2x4fv 182 | glfnUniformMatrix4x2fv 183 | glfnUniformMatrix3x4fv 184 | glfnUniformMatrix4x3fv 185 | glfnBlitFramebuffer 186 | glfnUniform1ui 187 | glfnUniform2ui 188 | glfnUniform3ui 189 | glfnUniform4ui 190 | glfnUniform1uiv 191 | glfnUniform2uiv 192 | glfnUniform3uiv 193 | glfnUniform4uiv 194 | ) 195 | 196 | func goString(buf []byte) string { 197 | for i, b := range buf { 198 | if b == 0 { 199 | return string(buf[:i]) 200 | } 201 | } 202 | panic("buf is not NUL-terminated") 203 | } 204 | 205 | func glBoolean(b bool) uintptr { 206 | if b { 207 | return TRUE 208 | } 209 | return FALSE 210 | } 211 | -------------------------------------------------------------------------------- /gl/types_debug.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build linux darwin windows openbsd 6 | // +build gldebug 7 | 8 | package gl 9 | 10 | // Alternate versions of the types defined in types.go with extra 11 | // debugging information attached. For documentation, see types.go. 12 | 13 | import "fmt" 14 | 15 | type Enum uint32 16 | 17 | type Attrib struct { 18 | Value uint 19 | name string 20 | } 21 | 22 | type Program struct { 23 | Init bool 24 | Value uint32 25 | } 26 | 27 | type Shader struct { 28 | Value uint32 29 | } 30 | 31 | type Buffer struct { 32 | Value uint32 33 | } 34 | 35 | type Framebuffer struct { 36 | Value uint32 37 | } 38 | 39 | type Renderbuffer struct { 40 | Value uint32 41 | } 42 | 43 | type Texture struct { 44 | Value uint32 45 | } 46 | 47 | type Uniform struct { 48 | Value int32 49 | name string 50 | } 51 | 52 | type VertexArray struct { 53 | Value uint32 54 | } 55 | 56 | func (v Attrib) c() uintptr { return uintptr(v.Value) } 57 | func (v Enum) c() uintptr { return uintptr(v) } 58 | func (v Program) c() uintptr { 59 | if !v.Init { 60 | ret := uintptr(0) 61 | ret-- 62 | return ret 63 | } 64 | return uintptr(v.Value) 65 | } 66 | func (v Shader) c() uintptr { return uintptr(v.Value) } 67 | func (v Buffer) c() uintptr { return uintptr(v.Value) } 68 | func (v Framebuffer) c() uintptr { return uintptr(v.Value) } 69 | func (v Renderbuffer) c() uintptr { return uintptr(v.Value) } 70 | func (v Texture) c() uintptr { return uintptr(v.Value) } 71 | func (v Uniform) c() uintptr { return uintptr(v.Value) } 72 | func (v VertexArray) c() uintptr { return uintptr(v.Value) } 73 | 74 | func (v Attrib) String() string { return fmt.Sprintf("Attrib(%d:%s)", v.Value, v.name) } 75 | func (v Program) String() string { return fmt.Sprintf("Program(%d)", v.Value) } 76 | func (v Shader) String() string { return fmt.Sprintf("Shader(%d)", v.Value) } 77 | func (v Buffer) String() string { return fmt.Sprintf("Buffer(%d)", v.Value) } 78 | func (v Framebuffer) String() string { return fmt.Sprintf("Framebuffer(%d)", v.Value) } 79 | func (v Renderbuffer) String() string { return fmt.Sprintf("Renderbuffer(%d)", v.Value) } 80 | func (v Texture) String() string { return fmt.Sprintf("Texture(%d)", v.Value) } 81 | func (v Uniform) String() string { return fmt.Sprintf("Uniform(%d:%s)", v.Value, v.name) } 82 | func (v VertexArray) String() string { return fmt.Sprintf("VertexArray(%d)", v.Value) } 83 | -------------------------------------------------------------------------------- /gl/types_prod.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build linux darwin windows openbsd 6 | // +build !gldebug 7 | 8 | package gl 9 | 10 | import "fmt" 11 | 12 | // Enum is equivalent to GLenum, and is normally used with one of the 13 | // constants defined in this package. 14 | type Enum uint32 15 | 16 | // Types are defined a structs so that in debug mode they can carry 17 | // extra information, such as a string name. See typesdebug.go. 18 | 19 | // Attrib identifies the location of a specific attribute variable. 20 | type Attrib struct { 21 | Value uint 22 | } 23 | 24 | // Program identifies a compiled shader program. 25 | type Program struct { 26 | // Init is set by CreateProgram, as some GL drivers (in particular, 27 | // ANGLE) return true for glIsProgram(0). 28 | Init bool 29 | Value uint32 30 | } 31 | 32 | // Shader identifies a GLSL shader. 33 | type Shader struct { 34 | Value uint32 35 | } 36 | 37 | // Buffer identifies a GL buffer object. 38 | type Buffer struct { 39 | Value uint32 40 | } 41 | 42 | // Framebuffer identifies a GL framebuffer. 43 | type Framebuffer struct { 44 | Value uint32 45 | } 46 | 47 | // A Renderbuffer is a GL object that holds an image in an internal format. 48 | type Renderbuffer struct { 49 | Value uint32 50 | } 51 | 52 | // A Texture identifies a GL texture unit. 53 | type Texture struct { 54 | Value uint32 55 | } 56 | 57 | // Uniform identifies the location of a specific uniform variable. 58 | type Uniform struct { 59 | Value int32 60 | } 61 | 62 | // A VertexArray is a GL object that holds vertices in an internal format. 63 | type VertexArray struct { 64 | Value uint32 65 | } 66 | 67 | func (v Attrib) c() uintptr { return uintptr(v.Value) } 68 | func (v Enum) c() uintptr { return uintptr(v) } 69 | func (v Program) c() uintptr { 70 | if !v.Init { 71 | ret := uintptr(0) 72 | ret-- 73 | return ret 74 | } 75 | return uintptr(v.Value) 76 | } 77 | func (v Shader) c() uintptr { return uintptr(v.Value) } 78 | func (v Buffer) c() uintptr { return uintptr(v.Value) } 79 | func (v Framebuffer) c() uintptr { return uintptr(v.Value) } 80 | func (v Renderbuffer) c() uintptr { return uintptr(v.Value) } 81 | func (v Texture) c() uintptr { return uintptr(v.Value) } 82 | func (v Uniform) c() uintptr { return uintptr(v.Value) } 83 | func (v VertexArray) c() uintptr { return uintptr(v.Value) } 84 | 85 | func (v Attrib) String() string { return fmt.Sprintf("Attrib(%d)", v.Value) } 86 | func (v Program) String() string { return fmt.Sprintf("Program(%d)", v.Value) } 87 | func (v Shader) String() string { return fmt.Sprintf("Shader(%d)", v.Value) } 88 | func (v Buffer) String() string { return fmt.Sprintf("Buffer(%d)", v.Value) } 89 | func (v Framebuffer) String() string { return fmt.Sprintf("Framebuffer(%d)", v.Value) } 90 | func (v Renderbuffer) String() string { return fmt.Sprintf("Renderbuffer(%d)", v.Value) } 91 | func (v Texture) String() string { return fmt.Sprintf("Texture(%d)", v.Value) } 92 | func (v Uniform) String() string { return fmt.Sprintf("Uniform(%d)", v.Value) } 93 | func (v VertexArray) String() string { return fmt.Sprintf("VertexArray(%d)", v.Value) } 94 | -------------------------------------------------------------------------------- /gl/work_windows_386.s: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #include "textflag.h" 6 | 7 | // fixFloat is unnecessary for windows/386 8 | TEXT ·fixFloat(SB),NOSPLIT,$0-16 9 | RET 10 | -------------------------------------------------------------------------------- /gl/work_windows_amd64.s: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #include "textflag.h" 6 | 7 | TEXT ·fixFloat(SB),NOSPLIT,$0-32 8 | MOVQ x0+0(FP), X0 9 | MOVQ x1+8(FP), X1 10 | MOVQ x2+16(FP), X2 11 | MOVQ x3+24(FP), X3 12 | RET 13 | -------------------------------------------------------------------------------- /imageutil/imageutil.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package imageutil implements some image utility functions. 6 | package imageutil 7 | 8 | import ( 9 | "image" 10 | ) 11 | 12 | // TODO: move Border into the standard library's package image? 13 | 14 | // Border returns four rectangles that together contain those points between r 15 | // and r.Inset(inset). Visually: 16 | // 17 | // 00000000 18 | // 00000000 19 | // 11....22 20 | // 11....22 21 | // 11....22 22 | // 33333333 23 | // 33333333 24 | // 25 | // The inset may be negative, in which case the points will be outside r. 26 | // 27 | // Some of the returned rectangles may be empty. None of the returned 28 | // rectangles will overlap. 29 | func Border(r image.Rectangle, inset int) [4]image.Rectangle { 30 | if inset == 0 { 31 | return [4]image.Rectangle{} 32 | } 33 | if r.Dx() <= 2*inset || r.Dy() <= 2*inset { 34 | return [4]image.Rectangle{r} 35 | } 36 | 37 | x := [4]int{ 38 | r.Min.X, 39 | r.Min.X + inset, 40 | r.Max.X - inset, 41 | r.Max.X, 42 | } 43 | y := [4]int{ 44 | r.Min.Y, 45 | r.Min.Y + inset, 46 | r.Max.Y - inset, 47 | r.Max.Y, 48 | } 49 | if inset < 0 { 50 | x[0], x[1] = x[1], x[0] 51 | x[2], x[3] = x[3], x[2] 52 | y[0], y[1] = y[1], y[0] 53 | y[2], y[3] = y[3], y[2] 54 | } 55 | 56 | // The top and bottom sections are responsible for filling the corners. 57 | // The top and bottom sections go from x[0] to x[3], across the y's. 58 | // The left and right sections go from y[1] to y[2], across the x's. 59 | 60 | return [4]image.Rectangle{{ 61 | // Top section. 62 | Min: image.Point{ 63 | X: x[0], 64 | Y: y[0], 65 | }, 66 | Max: image.Point{ 67 | X: x[3], 68 | Y: y[1], 69 | }, 70 | }, { 71 | // Left section. 72 | Min: image.Point{ 73 | X: x[0], 74 | Y: y[1], 75 | }, 76 | Max: image.Point{ 77 | X: x[1], 78 | Y: y[2], 79 | }, 80 | }, { 81 | // Right section. 82 | Min: image.Point{ 83 | X: x[2], 84 | Y: y[1], 85 | }, 86 | Max: image.Point{ 87 | X: x[3], 88 | Y: y[2], 89 | }, 90 | }, { 91 | // Bottom section. 92 | Min: image.Point{ 93 | X: x[0], 94 | Y: y[2], 95 | }, 96 | Max: image.Point{ 97 | X: x[3], 98 | Y: y[3], 99 | }, 100 | }} 101 | } 102 | -------------------------------------------------------------------------------- /imageutil/imageutil_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package imageutil 6 | 7 | import ( 8 | "image" 9 | "testing" 10 | ) 11 | 12 | func area(r image.Rectangle) int { 13 | dx, dy := r.Dx(), r.Dy() 14 | if dx <= 0 || dy <= 0 { 15 | return 0 16 | } 17 | return dx * dy 18 | } 19 | 20 | func TestBorder(t *testing.T) { 21 | r := image.Rect(100, 200, 400, 300) 22 | 23 | insets := []int{ 24 | -100, 25 | -1, 26 | +0, 27 | +1, 28 | +20, 29 | +49, 30 | +50, 31 | +51, 32 | +149, 33 | +150, 34 | +151, 35 | } 36 | 37 | for _, inset := range insets { 38 | border := Border(r, inset) 39 | 40 | outer, inner := r, r.Inset(inset) 41 | if inset < 0 { 42 | outer, inner = inner, outer 43 | } 44 | 45 | got := 0 46 | for _, b := range border { 47 | got += area(b) 48 | } 49 | want := area(outer) - area(inner) 50 | if got != want { 51 | t.Errorf("inset=%d: total area: got %d, want %d", inset, got, want) 52 | } 53 | 54 | for i, bi := range border { 55 | for j, bj := range border { 56 | if i <= j { 57 | continue 58 | } 59 | if !bi.Intersect(bj).Empty() { 60 | t.Errorf("inset=%d: %v and %v overlap", inset, bi, bj) 61 | } 62 | } 63 | } 64 | 65 | for _, b := range border { 66 | if got := outer.Intersect(b); got != b { 67 | t.Errorf("inset=%d: outer intersection: got %v, want %v", inset, got, b) 68 | } 69 | if got := inner.Intersect(b); !got.Empty() { 70 | t.Errorf("inset=%d: inner intersection: got %v, want empty", inset, got) 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /math/f32/f32.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package f32 implements float32 vector and matrix types. 6 | package f32 // import "github.com/as/shiny/math/f32" 7 | 8 | // Vec2 is a 2-element vector. 9 | type Vec2 [2]float32 10 | 11 | // Vec3 is a 3-element vector. 12 | type Vec3 [3]float32 13 | 14 | // Vec4 is a 4-element vector. 15 | type Vec4 [4]float32 16 | 17 | // Mat3 is a 3x3 matrix in row major order. 18 | // 19 | // m[3*r + c] is the element in the r'th row and c'th column. 20 | type Mat3 [9]float32 21 | 22 | // Mat4 is a 4x4 matrix in row major order. 23 | // 24 | // m[4*r + c] is the element in the r'th row and c'th column. 25 | type Mat4 [16]float32 26 | 27 | // Aff3 is a 3x3 affine transformation matrix in row major order, where the 28 | // bottom row is implicitly [0 0 1]. 29 | // 30 | // m[3*r + c] is the element in the r'th row and c'th column. 31 | type Aff3 [6]float32 32 | 33 | // Aff4 is a 4x4 affine transformation matrix in row major order, where the 34 | // bottom row is implicitly [0 0 0 1]. 35 | // 36 | // m[4*r + c] is the element in the r'th row and c'th column. 37 | type Aff4 [12]float32 38 | -------------------------------------------------------------------------------- /math/f64/f64.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package f64 implements float64 vector and matrix types. 6 | package f64 7 | 8 | // Vec2 is a 2-element vector. 9 | type Vec2 [2]float64 10 | 11 | // Vec3 is a 3-element vector. 12 | type Vec3 [3]float64 13 | 14 | // Vec4 is a 4-element vector. 15 | type Vec4 [4]float64 16 | 17 | // Mat3 is a 3x3 matrix in row major order. 18 | // 19 | // m[3*r + c] is the element in the r'th row and c'th column. 20 | type Mat3 [9]float64 21 | 22 | // Mat4 is a 4x4 matrix in row major order. 23 | // 24 | // m[4*r + c] is the element in the r'th row and c'th column. 25 | type Mat4 [16]float64 26 | 27 | // Aff3 is a 3x3 affine transformation matrix in row major order, where the 28 | // bottom row is implicitly [0 0 1]. 29 | // 30 | // m[3*r + c] is the element in the r'th row and c'th column. 31 | type Aff3 [6]float64 32 | 33 | // Aff4 is a 4x4 affine transformation matrix in row major order, where the 34 | // bottom row is implicitly [0 0 0 1]. 35 | // 36 | // m[4*r + c] is the element in the r'th row and c'th column. 37 | type Aff4 [12]float64 38 | -------------------------------------------------------------------------------- /screen/screen.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package screen // import "github.com/as/shiny/screen" 6 | 7 | import ( 8 | "image" 9 | "image/color" 10 | "image/draw" 11 | "unicode/utf8" 12 | 13 | "github.com/as/shiny/event/key" 14 | "github.com/as/shiny/event/lifecycle" 15 | "github.com/as/shiny/event/mouse" 16 | "github.com/as/shiny/event/paint" 17 | "github.com/as/shiny/event/size" 18 | "github.com/as/shiny/math/f64" 19 | ) 20 | 21 | // Screen creates Buffers, Textures and Windows. 22 | type Screen interface { 23 | NewBuffer(size image.Point) (Buffer, error) 24 | NewTexture(size image.Point) (Texture, error) 25 | NewWindow(opts *NewWindowOptions) (Window, error) 26 | } 27 | 28 | type Buffer interface { 29 | Release() 30 | Size() image.Point 31 | Bounds() image.Rectangle 32 | RGBA() *image.RGBA 33 | } 34 | 35 | type Texture interface { 36 | Release() 37 | Size() image.Point 38 | Bounds() image.Rectangle 39 | Uploader 40 | } 41 | 42 | // Window is a top-level, double-buffered GUI window. 43 | type Window interface { 44 | Device() *Device 45 | Release() 46 | Uploader 47 | Drawer 48 | Publish() PublishResult 49 | } 50 | 51 | type ( 52 | Lifecycle = lifecycle.Event 53 | Scroll = mouse.Event 54 | Mouse = mouse.Event 55 | Key = key.Event 56 | Size = size.Event 57 | Paint = paint.Event 58 | ) 59 | 60 | var Dev = &Device{ 61 | Scroll: make(chan Scroll, 1), 62 | Mouse: make(chan Mouse, 1), 63 | Key: make(chan Key, 1), 64 | Size: make(chan Size, 1), 65 | Paint: make(chan Paint, 1), 66 | Lifecycle: make(chan Lifecycle, 1), 67 | } 68 | 69 | type Device struct { 70 | Lifecycle chan Lifecycle 71 | Scroll chan Scroll 72 | Mouse chan Mouse 73 | Key chan Key 74 | Size chan Size 75 | Paint chan Paint 76 | } 77 | 78 | func SendMouse(e Mouse) { 79 | select { 80 | case Dev.Mouse <- e: 81 | default: 82 | // TODO: Retry on failure, but only if it's a press or release 83 | // note that this may hang the user, so a better fix should be 84 | // in order 85 | if e.Button != mouse.ButtonNone && e.Direction != mouse.DirNone { 86 | Dev.Mouse <- e 87 | } 88 | } 89 | } 90 | 91 | func SendKey(e Key) { 92 | select { 93 | case Dev.Key <- e: 94 | } 95 | } 96 | 97 | func SendSize(e Size) { 98 | select { 99 | case Dev.Size <- e: 100 | default: 101 | } 102 | } 103 | 104 | func SendPaint(e Paint) { 105 | select { 106 | case Dev.Paint <- e: 107 | default: 108 | } 109 | } 110 | 111 | func SendScroll(e Scroll) { 112 | for { 113 | select { 114 | case Dev.Scroll <- e: 115 | return 116 | default: 117 | select { 118 | case <-Dev.Scroll: 119 | default: 120 | } 121 | } 122 | } 123 | } 124 | 125 | func SendLifecycle(e Lifecycle) { 126 | select { 127 | case Dev.Lifecycle <- e: 128 | } 129 | 130 | } 131 | 132 | // PublishResult is the result of an Window.Publish call. 133 | type PublishResult struct { 134 | BackBufferPreserved bool 135 | } 136 | 137 | // NewWindowOptions are optional arguments to NewWindow. 138 | // TODO(as): NewWindowOptions could be named better 139 | type NewWindowOptions struct { 140 | Width, Height int 141 | Title string 142 | 143 | // Overlay, if true, attempts to create a new window over top 144 | // of the parent process's existing window (similar to running 145 | // a graphical application in Plan9 over top an existing Rio 146 | // window). 147 | Overlay bool 148 | } 149 | 150 | func (o *NewWindowOptions) GetTitle() string { 151 | if o == nil { 152 | return "" 153 | } 154 | return sanitizeUTF8(o.Title, 4096) 155 | } 156 | 157 | func sanitizeUTF8(s string, n int) string { 158 | if n < len(s) { 159 | s = s[:n] 160 | } 161 | i := 0 162 | for i < len(s) { 163 | r, n := utf8.DecodeRuneInString(s[i:]) 164 | if r == 0 || (r == utf8.RuneError && n == 1) { 165 | break 166 | } 167 | i += n 168 | } 169 | return s[:i] 170 | } 171 | 172 | // Uploader is something you can upload a Buffer to. 173 | type Uploader interface { 174 | Upload(dp image.Point, src Buffer, sr image.Rectangle) 175 | Fill(dr image.Rectangle, src color.Color, op draw.Op) 176 | } 177 | 178 | type Drawer interface { 179 | Draw(src2dst f64.Aff3, src Texture, sr image.Rectangle, op draw.Op, opts *DrawOptions) 180 | DrawUniform(src2dst f64.Aff3, src color.Color, sr image.Rectangle, op draw.Op, opts *DrawOptions) 181 | Copy(dp image.Point, src Texture, sr image.Rectangle, op draw.Op, opts *DrawOptions) 182 | Scale(dr image.Rectangle, src Texture, sr image.Rectangle, op draw.Op, opts *DrawOptions) 183 | } 184 | 185 | const ( 186 | Over = draw.Over 187 | Src = draw.Src 188 | ) 189 | 190 | type DrawOptions struct { 191 | } 192 | -------------------------------------------------------------------------------- /screen/screen_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package screen 6 | 7 | import ( 8 | "testing" 9 | ) 10 | 11 | func TestSanitizeUTF8(t *testing.T) { 12 | const n = 8 13 | 14 | testCases := []struct { 15 | s, want string 16 | }{ 17 | {"", ""}, 18 | {"a", "a"}, 19 | {"a\x00", "a"}, 20 | {"a\x80", "a"}, 21 | {"\x00a", ""}, 22 | {"\x80a", ""}, 23 | {"abc", "abc"}, 24 | {"foo b\x00r qux", "foo b"}, 25 | {"foo b\x80r qux", "foo b"}, 26 | {"foo b\xffr qux", "foo b"}, 27 | 28 | // "\xc3\xa0" is U+00E0 LATIN SMALL LETTER A WITH GRAVE. 29 | {"\xc3\xa0pqrs", "\u00e0pqrs"}, 30 | {"a\xc3\xa0pqrs", "a\u00e0pqrs"}, 31 | {"ab\xc3\xa0pqrs", "ab\u00e0pqrs"}, 32 | {"abc\xc3\xa0pqrs", "abc\u00e0pqr"}, 33 | {"abcd\xc3\xa0pqrs", "abcd\u00e0pq"}, 34 | {"abcde\xc3\xa0pqrs", "abcde\u00e0p"}, 35 | {"abcdef\xc3\xa0pqrs", "abcdef\u00e0"}, 36 | {"abcdefg\xc3\xa0pqrs", "abcdefg"}, 37 | {"abcdefgh\xc3\xa0pqrs", "abcdefgh"}, 38 | {"abcdefghi\xc3\xa0pqrs", "abcdefgh"}, 39 | {"abcdefghij\xc3\xa0pqrs", "abcdefgh"}, 40 | 41 | // "世" is "\xe4\xb8\x96". 42 | // "界" is "\xe7\x95\x8c". 43 | {"H 世界", "H 世界"}, 44 | {"Hi 世界", "Hi 世"}, 45 | {"Hello 世界", "Hello "}, 46 | } 47 | 48 | for _, tc := range testCases { 49 | if got := sanitizeUTF8(tc.s, n); got != tc.want { 50 | t.Errorf("sanitizeUTF8(%q): got %q, want %q", tc.s, got, tc.want) 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /sys/AUTHORS: -------------------------------------------------------------------------------- 1 | # This source code refers to The Go Authors for copyright purposes. 2 | # The master list of authors is in the main Go distribution, 3 | # visible at http://tip.golang.org/AUTHORS. 4 | -------------------------------------------------------------------------------- /sys/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Go 2 | 3 | Go is an open source project. 4 | 5 | It is the work of hundreds of contributors. We appreciate your help! 6 | 7 | ## Filing issues 8 | 9 | When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions: 10 | 11 | 1. What version of Go are you using (`go version`)? 12 | 2. What operating system and processor architecture are you using? 13 | 3. What did you do? 14 | 4. What did you expect to see? 15 | 5. What did you see instead? 16 | 17 | General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker. 18 | The gophers there will answer or ask you to file an issue if you've tripped over a bug. 19 | 20 | ## Contributing code 21 | 22 | Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html) 23 | before sending patches. 24 | 25 | Unless otherwise noted, the Go source files are distributed under 26 | the BSD-style license found in the LICENSE file. 27 | -------------------------------------------------------------------------------- /sys/CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # This source code was written by the Go contributors. 2 | # The master list of contributors is in the main Go distribution, 3 | # visible at http://tip.golang.org/CONTRIBUTORS. 4 | -------------------------------------------------------------------------------- /sys/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /sys/PATENTS: -------------------------------------------------------------------------------- 1 | Additional IP Rights Grant (Patents) 2 | 3 | "This implementation" means the copyrightable works distributed by 4 | Google as part of the Go project. 5 | 6 | Google hereby grants to You a perpetual, worldwide, non-exclusive, 7 | no-charge, royalty-free, irrevocable (except as stated in this section) 8 | patent license to make, have made, use, offer to sell, sell, import, 9 | transfer and otherwise run, modify and propagate the contents of this 10 | implementation of Go, where such license applies only to those patent 11 | claims, both currently owned or controlled by Google and acquired in 12 | the future, licensable by Google that are necessarily infringed by this 13 | implementation of Go. This grant does not include claims that would be 14 | infringed only as a consequence of further modification of this 15 | implementation. If you or your agent or exclusive licensee institute or 16 | order or agree to the institution of patent litigation against any 17 | entity (including a cross-claim or counterclaim in a lawsuit) alleging 18 | that this implementation of Go or any code incorporated within this 19 | implementation of Go constitutes direct or contributory patent 20 | infringement, or inducement of patent infringement, then any patent 21 | rights granted to you under this License for this implementation of Go 22 | shall terminate as of the date such litigation is filed. 23 | -------------------------------------------------------------------------------- /sys/README.md: -------------------------------------------------------------------------------- 1 | # sys 2 | 3 | This repository holds supplemental Go packages for low-level interactions with 4 | the operating system. 5 | 6 | ## Download/Install 7 | 8 | The easiest way to install is to run `go get -u golang.org/x/sys`. You can 9 | also manually git clone the repository to `$GOPATH/src/golang.org/x/sys`. 10 | 11 | ## Report Issues / Send Patches 12 | 13 | This repository uses Gerrit for code changes. To learn how to submit changes to 14 | this repository, see https://golang.org/doc/contribute.html. 15 | 16 | The main issue tracker for the sys repository is located at 17 | https://github.com/golang/go/issues. Prefix your issue with "x/sys:" in the 18 | subject line, so it is easy to find. 19 | -------------------------------------------------------------------------------- /sys/windows/asm_windows_386.s: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // 6 | // System calls for 386, Windows are implemented in runtime/syscall_windows.goc 7 | // 8 | 9 | TEXT ·getprocaddress(SB), 7, $0-16 10 | JMP syscall·getprocaddress(SB) 11 | 12 | TEXT ·loadlibrary(SB), 7, $0-12 13 | JMP syscall·loadlibrary(SB) 14 | -------------------------------------------------------------------------------- /sys/windows/asm_windows_amd64.s: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // 6 | // System calls for amd64, Windows are implemented in runtime/syscall_windows.goc 7 | // 8 | 9 | TEXT ·getprocaddress(SB), 7, $0-32 10 | JMP syscall·getprocaddress(SB) 11 | 12 | TEXT ·loadlibrary(SB), 7, $0-8 13 | JMP syscall·loadlibrary(SB) 14 | -------------------------------------------------------------------------------- /sys/windows/env_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Windows environment variables. 6 | 7 | package windows 8 | 9 | import "syscall" 10 | 11 | func Getenv(key string) (value string, found bool) { 12 | return syscall.Getenv(key) 13 | } 14 | 15 | func Setenv(key, value string) error { 16 | return syscall.Setenv(key, value) 17 | } 18 | 19 | func Clearenv() { 20 | syscall.Clearenv() 21 | } 22 | 23 | func Environ() []string { 24 | return syscall.Environ() 25 | } 26 | 27 | func Unsetenv(key string) error { 28 | return syscall.Unsetenv(key) 29 | } 30 | -------------------------------------------------------------------------------- /sys/windows/eventlog.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | package windows 8 | 9 | const ( 10 | EVENTLOG_SUCCESS = 0 11 | EVENTLOG_ERROR_TYPE = 1 12 | EVENTLOG_WARNING_TYPE = 2 13 | EVENTLOG_INFORMATION_TYPE = 4 14 | EVENTLOG_AUDIT_SUCCESS = 8 15 | EVENTLOG_AUDIT_FAILURE = 16 16 | ) 17 | 18 | //sys RegisterEventSource(uncServerName *uint16, sourceName *uint16) (handle Handle, err error) [failretval==0] = advapi32.RegisterEventSourceW 19 | //sys DeregisterEventSource(handle Handle) (err error) = advapi32.DeregisterEventSource 20 | //sys ReportEvent(log Handle, etype uint16, category uint16, eventId uint32, usrSId uintptr, numStrings uint16, dataSize uint32, strings **uint16, rawData *byte) (err error) = advapi32.ReportEventW 21 | -------------------------------------------------------------------------------- /sys/windows/exec_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Fork, exec, wait, etc. 6 | 7 | package windows 8 | 9 | // EscapeArg rewrites command line argument s as prescribed 10 | // in http://msdn.microsoft.com/en-us/library/ms880421. 11 | // This function returns "" (2 double quotes) if s is empty. 12 | // Alternatively, these transformations are done: 13 | // - every back slash (\) is doubled, but only if immediately 14 | // followed by double quote ("); 15 | // - every double quote (") is escaped by back slash (\); 16 | // - finally, s is wrapped with double quotes (arg -> "arg"), 17 | // but only if there is space or tab inside s. 18 | func EscapeArg(s string) string { 19 | if len(s) == 0 { 20 | return "\"\"" 21 | } 22 | n := len(s) 23 | hasSpace := false 24 | for i := 0; i < len(s); i++ { 25 | switch s[i] { 26 | case '"', '\\': 27 | n++ 28 | case ' ', '\t': 29 | hasSpace = true 30 | } 31 | } 32 | if hasSpace { 33 | n += 2 34 | } 35 | if n == len(s) { 36 | return s 37 | } 38 | 39 | qs := make([]byte, n) 40 | j := 0 41 | if hasSpace { 42 | qs[j] = '"' 43 | j++ 44 | } 45 | slashes := 0 46 | for i := 0; i < len(s); i++ { 47 | switch s[i] { 48 | default: 49 | slashes = 0 50 | qs[j] = s[i] 51 | case '\\': 52 | slashes++ 53 | qs[j] = s[i] 54 | case '"': 55 | for ; slashes > 0; slashes-- { 56 | qs[j] = '\\' 57 | j++ 58 | } 59 | qs[j] = '\\' 60 | j++ 61 | qs[j] = s[i] 62 | } 63 | j++ 64 | } 65 | if hasSpace { 66 | for ; slashes > 0; slashes-- { 67 | qs[j] = '\\' 68 | j++ 69 | } 70 | qs[j] = '"' 71 | j++ 72 | } 73 | return string(qs[:j]) 74 | } 75 | 76 | func CloseOnExec(fd Handle) { 77 | SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0) 78 | } 79 | 80 | // FullPath retrieves the full path of the specified file. 81 | func FullPath(name string) (path string, err error) { 82 | p, err := UTF16PtrFromString(name) 83 | if err != nil { 84 | return "", err 85 | } 86 | n := uint32(100) 87 | for { 88 | buf := make([]uint16, n) 89 | n, err = GetFullPathName(p, uint32(len(buf)), &buf[0], nil) 90 | if err != nil { 91 | return "", err 92 | } 93 | if n <= uint32(len(buf)) { 94 | return UTF16ToString(buf[:n]), nil 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /sys/windows/memory_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package windows 6 | 7 | const ( 8 | MEM_COMMIT = 0x00001000 9 | MEM_RESERVE = 0x00002000 10 | MEM_DECOMMIT = 0x00004000 11 | MEM_RELEASE = 0x00008000 12 | MEM_RESET = 0x00080000 13 | MEM_TOP_DOWN = 0x00100000 14 | MEM_WRITE_WATCH = 0x00200000 15 | MEM_PHYSICAL = 0x00400000 16 | MEM_RESET_UNDO = 0x01000000 17 | MEM_LARGE_PAGES = 0x20000000 18 | 19 | PAGE_NOACCESS = 0x01 20 | PAGE_READONLY = 0x02 21 | PAGE_READWRITE = 0x04 22 | PAGE_WRITECOPY = 0x08 23 | PAGE_EXECUTE_READ = 0x20 24 | PAGE_EXECUTE_READWRITE = 0x40 25 | PAGE_EXECUTE_WRITECOPY = 0x80 26 | ) 27 | -------------------------------------------------------------------------------- /sys/windows/mksyscall.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package windows 6 | 7 | //go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go eventlog.go service.go syscall_windows.go security_windows.go 8 | -------------------------------------------------------------------------------- /sys/windows/race.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows,race 6 | 7 | package windows 8 | 9 | import ( 10 | "runtime" 11 | "unsafe" 12 | ) 13 | 14 | const raceenabled = true 15 | 16 | func raceAcquire(addr unsafe.Pointer) { 17 | runtime.RaceAcquire(addr) 18 | } 19 | 20 | func raceReleaseMerge(addr unsafe.Pointer) { 21 | runtime.RaceReleaseMerge(addr) 22 | } 23 | 24 | func raceReadRange(addr unsafe.Pointer, len int) { 25 | runtime.RaceReadRange(addr, len) 26 | } 27 | 28 | func raceWriteRange(addr unsafe.Pointer, len int) { 29 | runtime.RaceWriteRange(addr, len) 30 | } 31 | -------------------------------------------------------------------------------- /sys/windows/race0.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows,!race 6 | 7 | package windows 8 | 9 | import ( 10 | "unsafe" 11 | ) 12 | 13 | const raceenabled = false 14 | 15 | func raceAcquire(addr unsafe.Pointer) { 16 | } 17 | 18 | func raceReleaseMerge(addr unsafe.Pointer) { 19 | } 20 | 21 | func raceReadRange(addr unsafe.Pointer, len int) { 22 | } 23 | 24 | func raceWriteRange(addr unsafe.Pointer, len int) { 25 | } 26 | -------------------------------------------------------------------------------- /sys/windows/registry/export_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | package registry 8 | 9 | func (k Key) SetValue(name string, valtype uint32, data []byte) error { 10 | return k.setValue(name, valtype, data) 11 | } 12 | -------------------------------------------------------------------------------- /sys/windows/registry/mksyscall.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package registry 6 | 7 | //go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall.go 8 | -------------------------------------------------------------------------------- /sys/windows/registry/syscall.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | package registry 8 | 9 | import "syscall" 10 | 11 | const ( 12 | _REG_OPTION_NON_VOLATILE = 0 13 | 14 | _REG_CREATED_NEW_KEY = 1 15 | _REG_OPENED_EXISTING_KEY = 2 16 | 17 | _ERROR_NO_MORE_ITEMS syscall.Errno = 259 18 | ) 19 | 20 | func LoadRegLoadMUIString() error { 21 | return procRegLoadMUIStringW.Find() 22 | } 23 | 24 | //sys regCreateKeyEx(key syscall.Handle, subkey *uint16, reserved uint32, class *uint16, options uint32, desired uint32, sa *syscall.SecurityAttributes, result *syscall.Handle, disposition *uint32) (regerrno error) = advapi32.RegCreateKeyExW 25 | //sys regDeleteKey(key syscall.Handle, subkey *uint16) (regerrno error) = advapi32.RegDeleteKeyW 26 | //sys regSetValueEx(key syscall.Handle, valueName *uint16, reserved uint32, vtype uint32, buf *byte, bufsize uint32) (regerrno error) = advapi32.RegSetValueExW 27 | //sys regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) = advapi32.RegEnumValueW 28 | //sys regDeleteValue(key syscall.Handle, name *uint16) (regerrno error) = advapi32.RegDeleteValueW 29 | //sys regLoadMUIString(key syscall.Handle, name *uint16, buf *uint16, buflen uint32, buflenCopied *uint32, flags uint32, dir *uint16) (regerrno error) = advapi32.RegLoadMUIStringW 30 | //sys regConnectRegistry(machinename *uint16, key syscall.Handle, result *syscall.Handle) (regerrno error) = advapi32.RegConnectRegistryW 31 | 32 | //sys expandEnvironmentStrings(src *uint16, dst *uint16, size uint32) (n uint32, err error) = kernel32.ExpandEnvironmentStringsW 33 | -------------------------------------------------------------------------------- /sys/windows/registry/zsyscall_windows.go: -------------------------------------------------------------------------------- 1 | // MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT 2 | 3 | package registry 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/as/shiny/sys/windows" 10 | ) 11 | 12 | var _ unsafe.Pointer 13 | 14 | // Do the interface allocations only once for common 15 | // Errno values. 16 | const ( 17 | errnoERROR_IO_PENDING = 997 18 | ) 19 | 20 | var ( 21 | errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) 22 | ) 23 | 24 | // errnoErr returns common boxed Errno values, to prevent 25 | // allocations at runtime. 26 | func errnoErr(e syscall.Errno) error { 27 | switch e { 28 | case 0: 29 | return nil 30 | case errnoERROR_IO_PENDING: 31 | return errERROR_IO_PENDING 32 | } 33 | // TODO: add more here, after collecting data on the common 34 | // error values see on Windows. (perhaps when running 35 | // all.bat?) 36 | return e 37 | } 38 | 39 | var ( 40 | modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") 41 | modkernel32 = windows.NewLazySystemDLL("kernel32.dll") 42 | 43 | procRegCreateKeyExW = modadvapi32.NewProc("RegCreateKeyExW") 44 | procRegDeleteKeyW = modadvapi32.NewProc("RegDeleteKeyW") 45 | procRegSetValueExW = modadvapi32.NewProc("RegSetValueExW") 46 | procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW") 47 | procRegDeleteValueW = modadvapi32.NewProc("RegDeleteValueW") 48 | procRegLoadMUIStringW = modadvapi32.NewProc("RegLoadMUIStringW") 49 | procRegConnectRegistryW = modadvapi32.NewProc("RegConnectRegistryW") 50 | procExpandEnvironmentStringsW = modkernel32.NewProc("ExpandEnvironmentStringsW") 51 | ) 52 | 53 | func regCreateKeyEx(key syscall.Handle, subkey *uint16, reserved uint32, class *uint16, options uint32, desired uint32, sa *syscall.SecurityAttributes, result *syscall.Handle, disposition *uint32) (regerrno error) { 54 | r0, _, _ := syscall.Syscall9(procRegCreateKeyExW.Addr(), 9, uintptr(key), uintptr(unsafe.Pointer(subkey)), uintptr(reserved), uintptr(unsafe.Pointer(class)), uintptr(options), uintptr(desired), uintptr(unsafe.Pointer(sa)), uintptr(unsafe.Pointer(result)), uintptr(unsafe.Pointer(disposition))) 55 | if r0 != 0 { 56 | regerrno = syscall.Errno(r0) 57 | } 58 | return 59 | } 60 | 61 | func regDeleteKey(key syscall.Handle, subkey *uint16) (regerrno error) { 62 | r0, _, _ := syscall.Syscall(procRegDeleteKeyW.Addr(), 2, uintptr(key), uintptr(unsafe.Pointer(subkey)), 0) 63 | if r0 != 0 { 64 | regerrno = syscall.Errno(r0) 65 | } 66 | return 67 | } 68 | 69 | func regSetValueEx(key syscall.Handle, valueName *uint16, reserved uint32, vtype uint32, buf *byte, bufsize uint32) (regerrno error) { 70 | r0, _, _ := syscall.Syscall6(procRegSetValueExW.Addr(), 6, uintptr(key), uintptr(unsafe.Pointer(valueName)), uintptr(reserved), uintptr(vtype), uintptr(unsafe.Pointer(buf)), uintptr(bufsize)) 71 | if r0 != 0 { 72 | regerrno = syscall.Errno(r0) 73 | } 74 | return 75 | } 76 | 77 | func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) { 78 | r0, _, _ := syscall.Syscall9(procRegEnumValueW.Addr(), 8, uintptr(key), uintptr(index), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(valtype)), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(buflen)), 0) 79 | if r0 != 0 { 80 | regerrno = syscall.Errno(r0) 81 | } 82 | return 83 | } 84 | 85 | func regDeleteValue(key syscall.Handle, name *uint16) (regerrno error) { 86 | r0, _, _ := syscall.Syscall(procRegDeleteValueW.Addr(), 2, uintptr(key), uintptr(unsafe.Pointer(name)), 0) 87 | if r0 != 0 { 88 | regerrno = syscall.Errno(r0) 89 | } 90 | return 91 | } 92 | 93 | func regLoadMUIString(key syscall.Handle, name *uint16, buf *uint16, buflen uint32, buflenCopied *uint32, flags uint32, dir *uint16) (regerrno error) { 94 | r0, _, _ := syscall.Syscall9(procRegLoadMUIStringW.Addr(), 7, uintptr(key), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buf)), uintptr(buflen), uintptr(unsafe.Pointer(buflenCopied)), uintptr(flags), uintptr(unsafe.Pointer(dir)), 0, 0) 95 | if r0 != 0 { 96 | regerrno = syscall.Errno(r0) 97 | } 98 | return 99 | } 100 | 101 | func regConnectRegistry(machinename *uint16, key syscall.Handle, result *syscall.Handle) (regerrno error) { 102 | r0, _, _ := syscall.Syscall(procRegConnectRegistryW.Addr(), 3, uintptr(unsafe.Pointer(machinename)), uintptr(key), uintptr(unsafe.Pointer(result))) 103 | if r0 != 0 { 104 | regerrno = syscall.Errno(r0) 105 | } 106 | return 107 | } 108 | 109 | func expandEnvironmentStrings(src *uint16, dst *uint16, size uint32) (n uint32, err error) { 110 | r0, _, e1 := syscall.Syscall(procExpandEnvironmentStringsW.Addr(), 3, uintptr(unsafe.Pointer(src)), uintptr(unsafe.Pointer(dst)), uintptr(size)) 111 | n = uint32(r0) 112 | if n == 0 { 113 | if e1 != 0 { 114 | err = errnoErr(e1) 115 | } else { 116 | err = syscall.EINVAL 117 | } 118 | } 119 | return 120 | } 121 | -------------------------------------------------------------------------------- /sys/windows/str.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | package windows 8 | 9 | func itoa(val int) string { // do it here rather than with fmt to avoid dependency 10 | if val < 0 { 11 | return "-" + itoa(-val) 12 | } 13 | var buf [32]byte // big enough for int64 14 | i := len(buf) - 1 15 | for val >= 10 { 16 | buf[i] = byte(val%10 + '0') 17 | i-- 18 | val /= 10 19 | } 20 | buf[i] = byte(val + '0') 21 | return string(buf[i:]) 22 | } 23 | -------------------------------------------------------------------------------- /sys/windows/svc/debug/log.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | package debug 8 | 9 | import ( 10 | "os" 11 | "strconv" 12 | ) 13 | 14 | // Log interface allows different log implementations to be used. 15 | type Log interface { 16 | Close() error 17 | Info(eid uint32, msg string) error 18 | Warning(eid uint32, msg string) error 19 | Error(eid uint32, msg string) error 20 | } 21 | 22 | // ConsoleLog provides access to the console. 23 | type ConsoleLog struct { 24 | Name string 25 | } 26 | 27 | // New creates new ConsoleLog. 28 | func New(source string) *ConsoleLog { 29 | return &ConsoleLog{Name: source} 30 | } 31 | 32 | // Close closes console log l. 33 | func (l *ConsoleLog) Close() error { 34 | return nil 35 | } 36 | 37 | func (l *ConsoleLog) report(kind string, eid uint32, msg string) error { 38 | s := l.Name + "." + kind + "(" + strconv.Itoa(int(eid)) + "): " + msg + "\n" 39 | _, err := os.Stdout.Write([]byte(s)) 40 | return err 41 | } 42 | 43 | // Info writes an information event msg with event id eid to the console l. 44 | func (l *ConsoleLog) Info(eid uint32, msg string) error { 45 | return l.report("info", eid, msg) 46 | } 47 | 48 | // Warning writes an warning event msg with event id eid to the console l. 49 | func (l *ConsoleLog) Warning(eid uint32, msg string) error { 50 | return l.report("warn", eid, msg) 51 | } 52 | 53 | // Error writes an error event msg with event id eid to the console l. 54 | func (l *ConsoleLog) Error(eid uint32, msg string) error { 55 | return l.report("error", eid, msg) 56 | } 57 | -------------------------------------------------------------------------------- /sys/windows/svc/debug/service.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | // Package debug provides facilities to execute svc.Handler on console. 8 | // 9 | package debug 10 | 11 | import ( 12 | "os" 13 | "os/signal" 14 | "syscall" 15 | 16 | "github.com/as/shiny/sys/windows/svc" 17 | ) 18 | 19 | // Run executes service name by calling appropriate handler function. 20 | // The process is running on console, unlike real service. Use Ctrl+C to 21 | // send "Stop" command to your service. 22 | func Run(name string, handler svc.Handler) error { 23 | cmds := make(chan svc.ChangeRequest) 24 | changes := make(chan svc.Status) 25 | 26 | sig := make(chan os.Signal) 27 | signal.Notify(sig) 28 | 29 | go func() { 30 | status := svc.Status{State: svc.Stopped} 31 | for { 32 | select { 33 | case <-sig: 34 | cmds <- svc.ChangeRequest{Cmd: svc.Stop, CurrentStatus: status} 35 | case status = <-changes: 36 | } 37 | } 38 | }() 39 | 40 | _, errno := handler.Execute([]string{name}, cmds, changes) 41 | if errno != 0 { 42 | return syscall.Errno(errno) 43 | } 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /sys/windows/svc/event.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | package svc 8 | 9 | import ( 10 | "errors" 11 | 12 | "github.com/as/shiny/sys/windows" 13 | ) 14 | 15 | // event represents auto-reset, initially non-signaled Windows event. 16 | // It is used to communicate between go and asm parts of this package. 17 | type event struct { 18 | h windows.Handle 19 | } 20 | 21 | func newEvent() (*event, error) { 22 | h, err := windows.CreateEvent(nil, 0, 0, nil) 23 | if err != nil { 24 | return nil, err 25 | } 26 | return &event{h: h}, nil 27 | } 28 | 29 | func (e *event) Close() error { 30 | return windows.CloseHandle(e.h) 31 | } 32 | 33 | func (e *event) Set() error { 34 | return windows.SetEvent(e.h) 35 | } 36 | 37 | func (e *event) Wait() error { 38 | s, err := windows.WaitForSingleObject(e.h, windows.INFINITE) 39 | switch s { 40 | case windows.WAIT_OBJECT_0: 41 | break 42 | case windows.WAIT_FAILED: 43 | return err 44 | default: 45 | return errors.New("unexpected result from WaitForSingleObject") 46 | } 47 | return nil 48 | } 49 | -------------------------------------------------------------------------------- /sys/windows/svc/eventlog/install.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | package eventlog 8 | 9 | import ( 10 | "errors" 11 | 12 | "github.com/as/shiny/sys/windows" 13 | "github.com/as/shiny/sys/windows/registry" 14 | ) 15 | 16 | const ( 17 | // Log levels. 18 | Info = windows.EVENTLOG_INFORMATION_TYPE 19 | Warning = windows.EVENTLOG_WARNING_TYPE 20 | Error = windows.EVENTLOG_ERROR_TYPE 21 | ) 22 | 23 | const addKeyName = `SYSTEM\CurrentControlSet\Services\EventLog\Application` 24 | 25 | // Install modifies PC registry to allow logging with an event source src. 26 | // It adds all required keys and values to the event log registry key. 27 | // Install uses msgFile as the event message file. If useExpandKey is true, 28 | // the event message file is installed as REG_EXPAND_SZ value, 29 | // otherwise as REG_SZ. Use bitwise of log.Error, log.Warning and 30 | // log.Info to specify events supported by the new event source. 31 | func Install(src, msgFile string, useExpandKey bool, eventsSupported uint32) error { 32 | appkey, err := registry.OpenKey(registry.LOCAL_MACHINE, addKeyName, registry.CREATE_SUB_KEY) 33 | if err != nil { 34 | return err 35 | } 36 | defer appkey.Close() 37 | 38 | sk, alreadyExist, err := registry.CreateKey(appkey, src, registry.SET_VALUE) 39 | if err != nil { 40 | return err 41 | } 42 | defer sk.Close() 43 | if alreadyExist { 44 | return errors.New(addKeyName + `\` + src + " registry key already exists") 45 | } 46 | 47 | err = sk.SetDWordValue("CustomSource", 1) 48 | if err != nil { 49 | return err 50 | } 51 | if useExpandKey { 52 | err = sk.SetExpandStringValue("EventMessageFile", msgFile) 53 | } else { 54 | err = sk.SetStringValue("EventMessageFile", msgFile) 55 | } 56 | if err != nil { 57 | return err 58 | } 59 | err = sk.SetDWordValue("TypesSupported", eventsSupported) 60 | if err != nil { 61 | return err 62 | } 63 | return nil 64 | } 65 | 66 | // InstallAsEventCreate is the same as Install, but uses 67 | // %SystemRoot%\System32\EventCreate.exe as the event message file. 68 | func InstallAsEventCreate(src string, eventsSupported uint32) error { 69 | return Install(src, "%SystemRoot%\\System32\\EventCreate.exe", true, eventsSupported) 70 | } 71 | 72 | // Remove deletes all registry elements installed by the correspondent Install. 73 | func Remove(src string) error { 74 | appkey, err := registry.OpenKey(registry.LOCAL_MACHINE, addKeyName, registry.SET_VALUE) 75 | if err != nil { 76 | return err 77 | } 78 | defer appkey.Close() 79 | return registry.DeleteKey(appkey, src) 80 | } 81 | -------------------------------------------------------------------------------- /sys/windows/svc/eventlog/log.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | // Package eventlog implements access to Windows event log. 8 | // 9 | package eventlog 10 | 11 | import ( 12 | "errors" 13 | "syscall" 14 | 15 | "github.com/as/shiny/sys/windows" 16 | ) 17 | 18 | // Log provides access to the system log. 19 | type Log struct { 20 | Handle windows.Handle 21 | } 22 | 23 | // Open retrieves a handle to the specified event log. 24 | func Open(source string) (*Log, error) { 25 | return OpenRemote("", source) 26 | } 27 | 28 | // OpenRemote does the same as Open, but on different computer host. 29 | func OpenRemote(host, source string) (*Log, error) { 30 | if source == "" { 31 | return nil, errors.New("Specify event log source") 32 | } 33 | var s *uint16 34 | if host != "" { 35 | s = syscall.StringToUTF16Ptr(host) 36 | } 37 | h, err := windows.RegisterEventSource(s, syscall.StringToUTF16Ptr(source)) 38 | if err != nil { 39 | return nil, err 40 | } 41 | return &Log{Handle: h}, nil 42 | } 43 | 44 | // Close closes event log l. 45 | func (l *Log) Close() error { 46 | return windows.DeregisterEventSource(l.Handle) 47 | } 48 | 49 | func (l *Log) report(etype uint16, eid uint32, msg string) error { 50 | ss := []*uint16{syscall.StringToUTF16Ptr(msg)} 51 | return windows.ReportEvent(l.Handle, etype, 0, eid, 0, 1, 0, &ss[0], nil) 52 | } 53 | 54 | // Info writes an information event msg with event id eid to the end of event log l. 55 | // When EventCreate.exe is used, eid must be between 1 and 1000. 56 | func (l *Log) Info(eid uint32, msg string) error { 57 | return l.report(windows.EVENTLOG_INFORMATION_TYPE, eid, msg) 58 | } 59 | 60 | // Warning writes an warning event msg with event id eid to the end of event log l. 61 | // When EventCreate.exe is used, eid must be between 1 and 1000. 62 | func (l *Log) Warning(eid uint32, msg string) error { 63 | return l.report(windows.EVENTLOG_WARNING_TYPE, eid, msg) 64 | } 65 | 66 | // Error writes an error event msg with event id eid to the end of event log l. 67 | // When EventCreate.exe is used, eid must be between 1 and 1000. 68 | func (l *Log) Error(eid uint32, msg string) error { 69 | return l.report(windows.EVENTLOG_ERROR_TYPE, eid, msg) 70 | } 71 | -------------------------------------------------------------------------------- /sys/windows/svc/eventlog/log_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | package eventlog_test 8 | 9 | import ( 10 | "testing" 11 | 12 | "github.com/as/shiny/sys/windows/svc/eventlog" 13 | ) 14 | 15 | func TestLog(t *testing.T) { 16 | if testing.Short() { 17 | t.Skip("skipping test in short mode - it modifies system logs") 18 | } 19 | 20 | const name = "mylog" 21 | const supports = eventlog.Error | eventlog.Warning | eventlog.Info 22 | err := eventlog.InstallAsEventCreate(name, supports) 23 | if err != nil { 24 | t.Fatalf("Install failed: %s", err) 25 | } 26 | defer func() { 27 | err = eventlog.Remove(name) 28 | if err != nil { 29 | t.Fatalf("Remove failed: %s", err) 30 | } 31 | }() 32 | 33 | l, err := eventlog.Open(name) 34 | if err != nil { 35 | t.Fatalf("Open failed: %s", err) 36 | } 37 | defer l.Close() 38 | 39 | err = l.Info(1, "info") 40 | if err != nil { 41 | t.Fatalf("Info failed: %s", err) 42 | } 43 | err = l.Warning(2, "warning") 44 | if err != nil { 45 | t.Fatalf("Warning failed: %s", err) 46 | } 47 | err = l.Error(3, "error") 48 | if err != nil { 49 | t.Fatalf("Error failed: %s", err) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /sys/windows/svc/example/beep.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | package main 8 | 9 | import ( 10 | "syscall" 11 | ) 12 | 13 | // BUG(brainman): MessageBeep Windows api is broken on Windows 7, 14 | // so this example does not beep when runs as service on Windows 7. 15 | 16 | var ( 17 | beepFunc = syscall.MustLoadDLL("user32.dll").MustFindProc("MessageBeep") 18 | ) 19 | 20 | func beep() { 21 | beepFunc.Call(0xffffffff) 22 | } 23 | -------------------------------------------------------------------------------- /sys/windows/svc/example/install.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "os" 12 | "path/filepath" 13 | 14 | "github.com/as/shiny/sys/windows/svc/eventlog" 15 | "github.com/as/shiny/sys/windows/svc/mgr" 16 | ) 17 | 18 | func exePath() (string, error) { 19 | prog := os.Args[0] 20 | p, err := filepath.Abs(prog) 21 | if err != nil { 22 | return "", err 23 | } 24 | fi, err := os.Stat(p) 25 | if err == nil { 26 | if !fi.Mode().IsDir() { 27 | return p, nil 28 | } 29 | err = fmt.Errorf("%s is directory", p) 30 | } 31 | if filepath.Ext(p) == "" { 32 | p += ".exe" 33 | fi, err := os.Stat(p) 34 | if err == nil { 35 | if !fi.Mode().IsDir() { 36 | return p, nil 37 | } 38 | err = fmt.Errorf("%s is directory", p) 39 | } 40 | } 41 | return "", err 42 | } 43 | 44 | func installService(name, desc string) error { 45 | exepath, err := exePath() 46 | if err != nil { 47 | return err 48 | } 49 | m, err := mgr.Connect() 50 | if err != nil { 51 | return err 52 | } 53 | defer m.Disconnect() 54 | s, err := m.OpenService(name) 55 | if err == nil { 56 | s.Close() 57 | return fmt.Errorf("service %s already exists", name) 58 | } 59 | s, err = m.CreateService(name, exepath, mgr.Config{DisplayName: desc}, "is", "auto-started") 60 | if err != nil { 61 | return err 62 | } 63 | defer s.Close() 64 | err = eventlog.InstallAsEventCreate(name, eventlog.Error|eventlog.Warning|eventlog.Info) 65 | if err != nil { 66 | s.Delete() 67 | return fmt.Errorf("SetupEventLogSource() failed: %s", err) 68 | } 69 | return nil 70 | } 71 | 72 | func removeService(name string) error { 73 | m, err := mgr.Connect() 74 | if err != nil { 75 | return err 76 | } 77 | defer m.Disconnect() 78 | s, err := m.OpenService(name) 79 | if err != nil { 80 | return fmt.Errorf("service %s is not installed", name) 81 | } 82 | defer s.Close() 83 | err = s.Delete() 84 | if err != nil { 85 | return err 86 | } 87 | err = eventlog.Remove(name) 88 | if err != nil { 89 | return fmt.Errorf("RemoveEventLogSource() failed: %s", err) 90 | } 91 | return nil 92 | } 93 | -------------------------------------------------------------------------------- /sys/windows/svc/example/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | // Example service program that beeps. 8 | // 9 | // The program demonstrates how to create Windows service and 10 | // install / remove it on a computer. It also shows how to 11 | // stop / start / pause / continue any service, and how to 12 | // write to event log. It also shows how to use debug 13 | // facilities available in debug package. 14 | // 15 | package main 16 | 17 | import ( 18 | "fmt" 19 | "log" 20 | "os" 21 | "strings" 22 | 23 | "github.com/as/shiny/sys/windows/svc" 24 | ) 25 | 26 | func usage(errmsg string) { 27 | fmt.Fprintf(os.Stderr, 28 | "%s\n\n"+ 29 | "usage: %s \n"+ 30 | " where is one of\n"+ 31 | " install, remove, debug, start, stop, pause or continue.\n", 32 | errmsg, os.Args[0]) 33 | os.Exit(2) 34 | } 35 | 36 | func main() { 37 | const svcName = "myservice" 38 | 39 | isIntSess, err := svc.IsAnInteractiveSession() 40 | if err != nil { 41 | log.Fatalf("failed to determine if we are running in an interactive session: %v", err) 42 | } 43 | if !isIntSess { 44 | runService(svcName, false) 45 | return 46 | } 47 | 48 | if len(os.Args) < 2 { 49 | usage("no command specified") 50 | } 51 | 52 | cmd := strings.ToLower(os.Args[1]) 53 | switch cmd { 54 | case "debug": 55 | runService(svcName, true) 56 | return 57 | case "install": 58 | err = installService(svcName, "my service") 59 | case "remove": 60 | err = removeService(svcName) 61 | case "start": 62 | err = startService(svcName) 63 | case "stop": 64 | err = controlService(svcName, svc.Stop, svc.Stopped) 65 | case "pause": 66 | err = controlService(svcName, svc.Pause, svc.Paused) 67 | case "continue": 68 | err = controlService(svcName, svc.Continue, svc.Running) 69 | default: 70 | usage(fmt.Sprintf("invalid command %s", cmd)) 71 | } 72 | if err != nil { 73 | log.Fatalf("failed to %s %s: %v", cmd, svcName, err) 74 | } 75 | return 76 | } 77 | -------------------------------------------------------------------------------- /sys/windows/svc/example/manage.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "time" 12 | 13 | "github.com/as/shiny/sys/windows/svc" 14 | "github.com/as/shiny/sys/windows/svc/mgr" 15 | ) 16 | 17 | func startService(name string) error { 18 | m, err := mgr.Connect() 19 | if err != nil { 20 | return err 21 | } 22 | defer m.Disconnect() 23 | s, err := m.OpenService(name) 24 | if err != nil { 25 | return fmt.Errorf("could not access service: %v", err) 26 | } 27 | defer s.Close() 28 | err = s.Start("is", "manual-started") 29 | if err != nil { 30 | return fmt.Errorf("could not start service: %v", err) 31 | } 32 | return nil 33 | } 34 | 35 | func controlService(name string, c svc.Cmd, to svc.State) error { 36 | m, err := mgr.Connect() 37 | if err != nil { 38 | return err 39 | } 40 | defer m.Disconnect() 41 | s, err := m.OpenService(name) 42 | if err != nil { 43 | return fmt.Errorf("could not access service: %v", err) 44 | } 45 | defer s.Close() 46 | status, err := s.Control(c) 47 | if err != nil { 48 | return fmt.Errorf("could not send control=%d: %v", c, err) 49 | } 50 | timeout := time.Now().Add(10 * time.Second) 51 | for status.State != to { 52 | if timeout.Before(time.Now()) { 53 | return fmt.Errorf("timeout waiting for service to go to state=%d", to) 54 | } 55 | time.Sleep(300 * time.Millisecond) 56 | status, err = s.Query() 57 | if err != nil { 58 | return fmt.Errorf("could not retrieve service status: %v", err) 59 | } 60 | } 61 | return nil 62 | } 63 | -------------------------------------------------------------------------------- /sys/windows/svc/example/service.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "strings" 12 | "time" 13 | 14 | "github.com/as/shiny/sys/windows/svc" 15 | "github.com/as/shiny/sys/windows/svc/debug" 16 | "github.com/as/shiny/sys/windows/svc/eventlog" 17 | ) 18 | 19 | var elog debug.Log 20 | 21 | type myservice struct{} 22 | 23 | func (m *myservice) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) { 24 | const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown | svc.AcceptPauseAndContinue 25 | changes <- svc.Status{State: svc.StartPending} 26 | fasttick := time.Tick(500 * time.Millisecond) 27 | slowtick := time.Tick(2 * time.Second) 28 | tick := fasttick 29 | changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted} 30 | elog.Info(1, strings.Join(args, "-")) 31 | loop: 32 | for { 33 | select { 34 | case <-tick: 35 | beep() 36 | elog.Info(1, "beep") 37 | case c := <-r: 38 | switch c.Cmd { 39 | case svc.Interrogate: 40 | changes <- c.CurrentStatus 41 | // Testing deadlock from https://code.google.com/p/winsvc/issues/detail?id=4 42 | time.Sleep(100 * time.Millisecond) 43 | changes <- c.CurrentStatus 44 | case svc.Stop, svc.Shutdown: 45 | break loop 46 | case svc.Pause: 47 | changes <- svc.Status{State: svc.Paused, Accepts: cmdsAccepted} 48 | tick = slowtick 49 | case svc.Continue: 50 | changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted} 51 | tick = fasttick 52 | default: 53 | elog.Error(1, fmt.Sprintf("unexpected control request #%d", c)) 54 | } 55 | } 56 | } 57 | changes <- svc.Status{State: svc.StopPending} 58 | return 59 | } 60 | 61 | func runService(name string, isDebug bool) { 62 | var err error 63 | if isDebug { 64 | elog = debug.New(name) 65 | } else { 66 | elog, err = eventlog.Open(name) 67 | if err != nil { 68 | return 69 | } 70 | } 71 | defer elog.Close() 72 | 73 | elog.Info(1, fmt.Sprintf("starting %s service", name)) 74 | run := svc.Run 75 | if isDebug { 76 | run = debug.Run 77 | } 78 | err = run(name, &myservice{}) 79 | if err != nil { 80 | elog.Error(1, fmt.Sprintf("%s service failed: %v", name, err)) 81 | return 82 | } 83 | elog.Info(1, fmt.Sprintf("%s service stopped", name)) 84 | } 85 | -------------------------------------------------------------------------------- /sys/windows/svc/go12.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | // +build !go1.3 7 | 8 | // copied from pkg/runtime 9 | typedef unsigned int uint32; 10 | typedef unsigned long long int uint64; 11 | #ifdef _64BIT 12 | typedef uint64 uintptr; 13 | #else 14 | typedef uint32 uintptr; 15 | #endif 16 | 17 | // from sys_386.s or sys_amd64.s 18 | void ·servicemain(void); 19 | 20 | void 21 | ·getServiceMain(uintptr *r) 22 | { 23 | *r = (uintptr)·servicemain; 24 | } 25 | -------------------------------------------------------------------------------- /sys/windows/svc/go12.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | // +build !go1.3 7 | 8 | package svc 9 | 10 | // from go12.c 11 | func getServiceMain(r *uintptr) 12 | -------------------------------------------------------------------------------- /sys/windows/svc/go13.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | // +build go1.3 7 | 8 | package svc 9 | 10 | import "unsafe" 11 | 12 | const ptrSize = 4 << (^uintptr(0) >> 63) // unsafe.Sizeof(uintptr(0)) but an ideal const 13 | 14 | // Should be a built-in for unsafe.Pointer? 15 | func add(p unsafe.Pointer, x uintptr) unsafe.Pointer { 16 | return unsafe.Pointer(uintptr(p) + x) 17 | } 18 | 19 | // funcPC returns the entry PC of the function f. 20 | // It assumes that f is a func value. Otherwise the behavior is undefined. 21 | func funcPC(f interface{}) uintptr { 22 | return **(**uintptr)(add(unsafe.Pointer(&f), ptrSize)) 23 | } 24 | 25 | // from sys_386.s and sys_amd64.s 26 | func servicectlhandler(ctl uint32) uintptr 27 | func servicemain(argc uint32, argv **uint16) 28 | 29 | func getServiceMain(r *uintptr) { 30 | *r = funcPC(servicemain) 31 | } 32 | -------------------------------------------------------------------------------- /sys/windows/svc/mgr/config.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | package mgr 8 | 9 | import ( 10 | "syscall" 11 | "unicode/utf16" 12 | "unsafe" 13 | 14 | "github.com/as/shiny/sys/windows" 15 | ) 16 | 17 | const ( 18 | // Service start types. 19 | StartManual = windows.SERVICE_DEMAND_START // the service must be started manually 20 | StartAutomatic = windows.SERVICE_AUTO_START // the service will start by itself whenever the computer reboots 21 | StartDisabled = windows.SERVICE_DISABLED // the service cannot be started 22 | 23 | // The severity of the error, and action taken, 24 | // if this service fails to start. 25 | ErrorCritical = windows.SERVICE_ERROR_CRITICAL 26 | ErrorIgnore = windows.SERVICE_ERROR_IGNORE 27 | ErrorNormal = windows.SERVICE_ERROR_NORMAL 28 | ErrorSevere = windows.SERVICE_ERROR_SEVERE 29 | ) 30 | 31 | // TODO(brainman): Password is not returned by windows.QueryServiceConfig, not sure how to get it. 32 | 33 | type Config struct { 34 | ServiceType uint32 35 | StartType uint32 36 | ErrorControl uint32 37 | BinaryPathName string // fully qualified path to the service binary file, can also include arguments for an auto-start service 38 | LoadOrderGroup string 39 | TagId uint32 40 | Dependencies []string 41 | ServiceStartName string // name of the account under which the service should run 42 | DisplayName string 43 | Password string 44 | Description string 45 | } 46 | 47 | func toString(p *uint16) string { 48 | if p == nil { 49 | return "" 50 | } 51 | return syscall.UTF16ToString((*[4096]uint16)(unsafe.Pointer(p))[:]) 52 | } 53 | 54 | func toStringSlice(ps *uint16) []string { 55 | if ps == nil { 56 | return nil 57 | } 58 | r := make([]string, 0) 59 | for from, i, p := 0, 0, (*[1 << 24]uint16)(unsafe.Pointer(ps)); true; i++ { 60 | if p[i] == 0 { 61 | // empty string marks the end 62 | if i <= from { 63 | break 64 | } 65 | r = append(r, string(utf16.Decode(p[from:i]))) 66 | from = i + 1 67 | } 68 | } 69 | return r 70 | } 71 | 72 | // Config retrieves service s configuration paramteres. 73 | func (s *Service) Config() (Config, error) { 74 | var p *windows.QUERY_SERVICE_CONFIG 75 | n := uint32(1024) 76 | for { 77 | b := make([]byte, n) 78 | p = (*windows.QUERY_SERVICE_CONFIG)(unsafe.Pointer(&b[0])) 79 | err := windows.QueryServiceConfig(s.Handle, p, n, &n) 80 | if err == nil { 81 | break 82 | } 83 | if err.(syscall.Errno) != syscall.ERROR_INSUFFICIENT_BUFFER { 84 | return Config{}, err 85 | } 86 | if n <= uint32(len(b)) { 87 | return Config{}, err 88 | } 89 | } 90 | 91 | var p2 *windows.SERVICE_DESCRIPTION 92 | n = uint32(1024) 93 | for { 94 | b := make([]byte, n) 95 | p2 = (*windows.SERVICE_DESCRIPTION)(unsafe.Pointer(&b[0])) 96 | err := windows.QueryServiceConfig2(s.Handle, 97 | windows.SERVICE_CONFIG_DESCRIPTION, &b[0], n, &n) 98 | if err == nil { 99 | break 100 | } 101 | if err.(syscall.Errno) != syscall.ERROR_INSUFFICIENT_BUFFER { 102 | return Config{}, err 103 | } 104 | if n <= uint32(len(b)) { 105 | return Config{}, err 106 | } 107 | } 108 | 109 | return Config{ 110 | ServiceType: p.ServiceType, 111 | StartType: p.StartType, 112 | ErrorControl: p.ErrorControl, 113 | BinaryPathName: toString(p.BinaryPathName), 114 | LoadOrderGroup: toString(p.LoadOrderGroup), 115 | TagId: p.TagId, 116 | Dependencies: toStringSlice(p.Dependencies), 117 | ServiceStartName: toString(p.ServiceStartName), 118 | DisplayName: toString(p.DisplayName), 119 | Description: toString(p2.Description), 120 | }, nil 121 | } 122 | 123 | func updateDescription(handle windows.Handle, desc string) error { 124 | d := windows.SERVICE_DESCRIPTION{Description: toPtr(desc)} 125 | return windows.ChangeServiceConfig2(handle, 126 | windows.SERVICE_CONFIG_DESCRIPTION, (*byte)(unsafe.Pointer(&d))) 127 | } 128 | 129 | // UpdateConfig updates service s configuration parameters. 130 | func (s *Service) UpdateConfig(c Config) error { 131 | err := windows.ChangeServiceConfig(s.Handle, c.ServiceType, c.StartType, 132 | c.ErrorControl, toPtr(c.BinaryPathName), toPtr(c.LoadOrderGroup), 133 | nil, toStringBlock(c.Dependencies), toPtr(c.ServiceStartName), 134 | toPtr(c.Password), toPtr(c.DisplayName)) 135 | if err != nil { 136 | return err 137 | } 138 | return updateDescription(s.Handle, c.Description) 139 | } 140 | -------------------------------------------------------------------------------- /sys/windows/svc/mgr/mgr_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | package mgr_test 8 | 9 | import ( 10 | "os" 11 | "path/filepath" 12 | "sort" 13 | "strings" 14 | "syscall" 15 | "testing" 16 | "time" 17 | 18 | "github.com/as/shiny/sys/windows/svc/mgr" 19 | ) 20 | 21 | func TestOpenLanManServer(t *testing.T) { 22 | m, err := mgr.Connect() 23 | if err != nil { 24 | if errno, ok := err.(syscall.Errno); ok && errno == syscall.ERROR_ACCESS_DENIED { 25 | t.Skip("Skipping test: we don't have rights to manage services.") 26 | } 27 | t.Fatalf("SCM connection failed: %s", err) 28 | } 29 | defer m.Disconnect() 30 | 31 | s, err := m.OpenService("LanmanServer") 32 | if err != nil { 33 | t.Fatalf("OpenService(lanmanserver) failed: %s", err) 34 | } 35 | defer s.Close() 36 | 37 | _, err = s.Config() 38 | if err != nil { 39 | t.Fatalf("Config failed: %s", err) 40 | } 41 | } 42 | 43 | func install(t *testing.T, m *mgr.Mgr, name, exepath string, c mgr.Config) { 44 | // Sometimes it takes a while for the service to get 45 | // removed after previous test run. 46 | for i := 0; ; i++ { 47 | s, err := m.OpenService(name) 48 | if err != nil { 49 | break 50 | } 51 | s.Close() 52 | 53 | if i > 10 { 54 | t.Fatalf("service %s already exists", name) 55 | } 56 | time.Sleep(300 * time.Millisecond) 57 | } 58 | 59 | s, err := m.CreateService(name, exepath, c) 60 | if err != nil { 61 | t.Fatalf("CreateService(%s) failed: %v", name, err) 62 | } 63 | defer s.Close() 64 | } 65 | 66 | func depString(d []string) string { 67 | if len(d) == 0 { 68 | return "" 69 | } 70 | for i := range d { 71 | d[i] = strings.ToLower(d[i]) 72 | } 73 | ss := sort.StringSlice(d) 74 | ss.Sort() 75 | return strings.Join([]string(ss), " ") 76 | } 77 | 78 | func testConfig(t *testing.T, s *mgr.Service, should mgr.Config) mgr.Config { 79 | is, err := s.Config() 80 | if err != nil { 81 | t.Fatalf("Config failed: %s", err) 82 | } 83 | if should.DisplayName != is.DisplayName { 84 | t.Fatalf("config mismatch: DisplayName is %q, but should have %q", is.DisplayName, should.DisplayName) 85 | } 86 | if should.StartType != is.StartType { 87 | t.Fatalf("config mismatch: StartType is %v, but should have %v", is.StartType, should.StartType) 88 | } 89 | if should.Description != is.Description { 90 | t.Fatalf("config mismatch: Description is %q, but should have %q", is.Description, should.Description) 91 | } 92 | if depString(should.Dependencies) != depString(is.Dependencies) { 93 | t.Fatalf("config mismatch: Dependencies is %v, but should have %v", is.Dependencies, should.Dependencies) 94 | } 95 | return is 96 | } 97 | 98 | func remove(t *testing.T, s *mgr.Service) { 99 | err := s.Delete() 100 | if err != nil { 101 | t.Fatalf("Delete failed: %s", err) 102 | } 103 | } 104 | 105 | func TestMyService(t *testing.T) { 106 | if testing.Short() { 107 | t.Skip("skipping test in short mode - it modifies system services") 108 | } 109 | 110 | const name = "myservice" 111 | 112 | m, err := mgr.Connect() 113 | if err != nil { 114 | if errno, ok := err.(syscall.Errno); ok && errno == syscall.ERROR_ACCESS_DENIED { 115 | t.Skip("Skipping test: we don't have rights to manage services.") 116 | } 117 | t.Fatalf("SCM connection failed: %s", err) 118 | } 119 | defer m.Disconnect() 120 | 121 | c := mgr.Config{ 122 | StartType: mgr.StartDisabled, 123 | DisplayName: "my service", 124 | Description: "my service is just a test", 125 | Dependencies: []string{"LanmanServer", "W32Time"}, 126 | } 127 | 128 | exename := os.Args[0] 129 | exepath, err := filepath.Abs(exename) 130 | if err != nil { 131 | t.Fatalf("filepath.Abs(%s) failed: %s", exename, err) 132 | } 133 | 134 | install(t, m, name, exepath, c) 135 | 136 | s, err := m.OpenService(name) 137 | if err != nil { 138 | t.Fatalf("service %s is not installed", name) 139 | } 140 | defer s.Close() 141 | 142 | c.BinaryPathName = exepath 143 | c = testConfig(t, s, c) 144 | 145 | c.StartType = mgr.StartManual 146 | err = s.UpdateConfig(c) 147 | if err != nil { 148 | t.Fatalf("UpdateConfig failed: %v", err) 149 | } 150 | 151 | testConfig(t, s, c) 152 | 153 | svcnames, err := m.ListServices() 154 | if err != nil { 155 | t.Fatalf("ListServices failed: %v", err) 156 | } 157 | var myserviceIsInstalled bool 158 | for _, sn := range svcnames { 159 | if sn == name { 160 | myserviceIsInstalled = true 161 | break 162 | } 163 | } 164 | if !myserviceIsInstalled { 165 | t.Errorf("ListServices failed to find %q service", name) 166 | } 167 | 168 | remove(t, s) 169 | } 170 | -------------------------------------------------------------------------------- /sys/windows/svc/mgr/service.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | package mgr 8 | 9 | import ( 10 | "syscall" 11 | 12 | "github.com/as/shiny/sys/windows" 13 | "github.com/as/shiny/sys/windows/svc" 14 | ) 15 | 16 | // TODO(brainman): Use EnumDependentServices to enumerate dependent services. 17 | 18 | // Service is used to access Windows service. 19 | type Service struct { 20 | Name string 21 | Handle windows.Handle 22 | } 23 | 24 | // Delete marks service s for deletion from the service control manager database. 25 | func (s *Service) Delete() error { 26 | return windows.DeleteService(s.Handle) 27 | } 28 | 29 | // Close relinquish access to the service s. 30 | func (s *Service) Close() error { 31 | return windows.CloseServiceHandle(s.Handle) 32 | } 33 | 34 | // Start starts service s. 35 | // args will be passed to svc.Handler.Execute. 36 | func (s *Service) Start(args ...string) error { 37 | var p **uint16 38 | if len(args) > 0 { 39 | vs := make([]*uint16, len(args)) 40 | for i := range vs { 41 | vs[i] = syscall.StringToUTF16Ptr(args[i]) 42 | } 43 | p = &vs[0] 44 | } 45 | return windows.StartService(s.Handle, uint32(len(args)), p) 46 | } 47 | 48 | // Control sends state change request c to the servce s. 49 | func (s *Service) Control(c svc.Cmd) (svc.Status, error) { 50 | var t windows.SERVICE_STATUS 51 | err := windows.ControlService(s.Handle, uint32(c), &t) 52 | if err != nil { 53 | return svc.Status{}, err 54 | } 55 | return svc.Status{ 56 | State: svc.State(t.CurrentState), 57 | Accepts: svc.Accepted(t.ControlsAccepted), 58 | }, nil 59 | } 60 | 61 | // Query returns current status of service s. 62 | func (s *Service) Query() (svc.Status, error) { 63 | var t windows.SERVICE_STATUS 64 | err := windows.QueryServiceStatus(s.Handle, &t) 65 | if err != nil { 66 | return svc.Status{}, err 67 | } 68 | return svc.Status{ 69 | State: svc.State(t.CurrentState), 70 | Accepts: svc.Accepted(t.ControlsAccepted), 71 | }, nil 72 | } 73 | -------------------------------------------------------------------------------- /sys/windows/svc/security.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | package svc 8 | 9 | import ( 10 | "unsafe" 11 | 12 | "github.com/as/shiny/sys/windows" 13 | ) 14 | 15 | func allocSid(subAuth0 uint32) (*windows.SID, error) { 16 | var sid *windows.SID 17 | err := windows.AllocateAndInitializeSid(&windows.SECURITY_NT_AUTHORITY, 18 | 1, subAuth0, 0, 0, 0, 0, 0, 0, 0, &sid) 19 | if err != nil { 20 | return nil, err 21 | } 22 | return sid, nil 23 | } 24 | 25 | // IsAnInteractiveSession determines if calling process is running interactively. 26 | // It queries the process token for membership in the Interactive group. 27 | // http://stackoverflow.com/questions/2668851/how-do-i-detect-that-my-application-is-running-as-service-or-in-an-interactive-s 28 | func IsAnInteractiveSession() (bool, error) { 29 | interSid, err := allocSid(windows.SECURITY_INTERACTIVE_RID) 30 | if err != nil { 31 | return false, err 32 | } 33 | defer windows.FreeSid(interSid) 34 | 35 | serviceSid, err := allocSid(windows.SECURITY_SERVICE_RID) 36 | if err != nil { 37 | return false, err 38 | } 39 | defer windows.FreeSid(serviceSid) 40 | 41 | t, err := windows.OpenCurrentProcessToken() 42 | if err != nil { 43 | return false, err 44 | } 45 | defer t.Close() 46 | 47 | gs, err := t.GetTokenGroups() 48 | if err != nil { 49 | return false, err 50 | } 51 | p := unsafe.Pointer(&gs.Groups[0]) 52 | groups := (*[2 << 20]windows.SIDAndAttributes)(p)[:gs.GroupCount] 53 | for _, g := range groups { 54 | if windows.EqualSid(g.Sid, interSid) { 55 | return true, nil 56 | } 57 | if windows.EqualSid(g.Sid, serviceSid) { 58 | return false, nil 59 | } 60 | } 61 | return false, nil 62 | } 63 | -------------------------------------------------------------------------------- /sys/windows/svc/svc_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | package svc_test 8 | 9 | import ( 10 | "fmt" 11 | "io/ioutil" 12 | "math/rand" 13 | "os" 14 | "os/exec" 15 | "path/filepath" 16 | "strings" 17 | "testing" 18 | "time" 19 | 20 | "github.com/as/shiny/sys/windows/svc" 21 | "github.com/as/shiny/sys/windows/svc/mgr" 22 | ) 23 | 24 | func getState(t *testing.T, s *mgr.Service) svc.State { 25 | status, err := s.Query() 26 | if err != nil { 27 | t.Fatalf("Query(%s) failed: %s", s.Name, err) 28 | } 29 | return status.State 30 | } 31 | 32 | func testState(t *testing.T, s *mgr.Service, want svc.State) { 33 | have := getState(t, s) 34 | if have != want { 35 | t.Fatalf("%s state is=%d want=%d", s.Name, have, want) 36 | } 37 | } 38 | 39 | func waitState(t *testing.T, s *mgr.Service, want svc.State) { 40 | for i := 0; ; i++ { 41 | have := getState(t, s) 42 | if have == want { 43 | return 44 | } 45 | if i > 10 { 46 | t.Fatalf("%s state is=%d, waiting timeout", s.Name, have) 47 | } 48 | time.Sleep(300 * time.Millisecond) 49 | } 50 | } 51 | 52 | func TestExample(t *testing.T) { 53 | if testing.Short() { 54 | t.Skip("skipping test in short mode - it modifies system services") 55 | } 56 | 57 | const name = "myservice" 58 | 59 | m, err := mgr.Connect() 60 | if err != nil { 61 | t.Fatalf("SCM connection failed: %s", err) 62 | } 63 | defer m.Disconnect() 64 | 65 | dir, err := ioutil.TempDir("", "svc") 66 | if err != nil { 67 | t.Fatalf("failed to create temp directory: %v", err) 68 | } 69 | defer os.RemoveAll(dir) 70 | 71 | exepath := filepath.Join(dir, "a.exe") 72 | o, err := exec.Command("go", "build", "-o", exepath, "github.com/as/shiny/sys/windows/svc/example").CombinedOutput() 73 | if err != nil { 74 | t.Fatalf("failed to build service program: %v\n%v", err, string(o)) 75 | } 76 | 77 | s, err := m.OpenService(name) 78 | if err == nil { 79 | err = s.Delete() 80 | if err != nil { 81 | s.Close() 82 | t.Fatalf("Delete failed: %s", err) 83 | } 84 | s.Close() 85 | } 86 | s, err = m.CreateService(name, exepath, mgr.Config{DisplayName: "my service"}, "is", "auto-started") 87 | if err != nil { 88 | t.Fatalf("CreateService(%s) failed: %v", name, err) 89 | } 90 | defer s.Close() 91 | 92 | args := []string{"is", "manual-started", fmt.Sprintf("%d", rand.Int())} 93 | 94 | testState(t, s, svc.Stopped) 95 | err = s.Start(args...) 96 | if err != nil { 97 | t.Fatalf("Start(%s) failed: %s", s.Name, err) 98 | } 99 | waitState(t, s, svc.Running) 100 | time.Sleep(1 * time.Second) 101 | 102 | // testing deadlock from issues 4. 103 | _, err = s.Control(svc.Interrogate) 104 | if err != nil { 105 | t.Fatalf("Control(%s) failed: %s", s.Name, err) 106 | } 107 | _, err = s.Control(svc.Interrogate) 108 | if err != nil { 109 | t.Fatalf("Control(%s) failed: %s", s.Name, err) 110 | } 111 | time.Sleep(1 * time.Second) 112 | 113 | _, err = s.Control(svc.Stop) 114 | if err != nil { 115 | t.Fatalf("Control(%s) failed: %s", s.Name, err) 116 | } 117 | waitState(t, s, svc.Stopped) 118 | 119 | err = s.Delete() 120 | if err != nil { 121 | t.Fatalf("Delete failed: %s", err) 122 | } 123 | 124 | cmd := `Get-Eventlog -LogName Application -Newest 100` + 125 | ` | Where Source -eq "myservice"` + 126 | ` | Select -first 10` + 127 | ` | Format-table -HideTableHeaders -property ReplacementStrings` 128 | out, err := exec.Command("powershell", "-Command", cmd).CombinedOutput() 129 | if err != nil { 130 | t.Fatalf("powershell failed: %v\n%v", err, string(out)) 131 | } 132 | if want := strings.Join(append([]string{name}, args...), "-"); !strings.Contains(string(out), want) { 133 | t.Errorf("%q string does not contain %q", string(out), want) 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /sys/windows/svc/sys_386.s: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | // func servicemain(argc uint32, argv **uint16) 8 | TEXT ·servicemain(SB),7,$0 9 | MOVL argc+0(FP), AX 10 | MOVL AX, ·sArgc(SB) 11 | MOVL argv+4(FP), AX 12 | MOVL AX, ·sArgv(SB) 13 | 14 | PUSHL BP 15 | PUSHL BX 16 | PUSHL SI 17 | PUSHL DI 18 | 19 | SUBL $12, SP 20 | 21 | MOVL ·sName(SB), AX 22 | MOVL AX, (SP) 23 | MOVL $·servicectlhandler(SB), AX 24 | MOVL AX, 4(SP) 25 | MOVL $0, 8(SP) 26 | MOVL ·cRegisterServiceCtrlHandlerExW(SB), AX 27 | MOVL SP, BP 28 | CALL AX 29 | MOVL BP, SP 30 | CMPL AX, $0 31 | JE exit 32 | MOVL AX, ·ssHandle(SB) 33 | 34 | MOVL ·goWaitsH(SB), AX 35 | MOVL AX, (SP) 36 | MOVL ·cSetEvent(SB), AX 37 | MOVL SP, BP 38 | CALL AX 39 | MOVL BP, SP 40 | 41 | MOVL ·cWaitsH(SB), AX 42 | MOVL AX, (SP) 43 | MOVL $-1, AX 44 | MOVL AX, 4(SP) 45 | MOVL ·cWaitForSingleObject(SB), AX 46 | MOVL SP, BP 47 | CALL AX 48 | MOVL BP, SP 49 | 50 | exit: 51 | ADDL $12, SP 52 | 53 | POPL DI 54 | POPL SI 55 | POPL BX 56 | POPL BP 57 | 58 | MOVL 0(SP), CX 59 | ADDL $12, SP 60 | JMP CX 61 | 62 | // I do not know why, but this seems to be the only way to call 63 | // ctlHandlerProc on Windows 7. 64 | 65 | // func servicectlhandler(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr { 66 | TEXT ·servicectlhandler(SB),7,$0 67 | MOVL ·ctlHandlerExProc(SB), CX 68 | JMP CX 69 | -------------------------------------------------------------------------------- /sys/windows/svc/sys_amd64.s: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | // func servicemain(argc uint32, argv **uint16) 8 | TEXT ·servicemain(SB),7,$0 9 | MOVL CX, ·sArgc(SB) 10 | MOVQ DX, ·sArgv(SB) 11 | 12 | SUBQ $32, SP // stack for the first 4 syscall params 13 | 14 | MOVQ ·sName(SB), CX 15 | MOVQ $·servicectlhandler(SB), DX 16 | // BUG(pastarmovj): Figure out a way to pass in context in R8. 17 | MOVQ ·cRegisterServiceCtrlHandlerExW(SB), AX 18 | CALL AX 19 | CMPQ AX, $0 20 | JE exit 21 | MOVQ AX, ·ssHandle(SB) 22 | 23 | MOVQ ·goWaitsH(SB), CX 24 | MOVQ ·cSetEvent(SB), AX 25 | CALL AX 26 | 27 | MOVQ ·cWaitsH(SB), CX 28 | MOVQ $4294967295, DX 29 | MOVQ ·cWaitForSingleObject(SB), AX 30 | CALL AX 31 | 32 | exit: 33 | ADDQ $32, SP 34 | RET 35 | 36 | // I do not know why, but this seems to be the only way to call 37 | // ctlHandlerProc on Windows 7. 38 | 39 | // func ·servicectlhandler(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr { 40 | TEXT ·servicectlhandler(SB),7,$0 41 | MOVQ ·ctlHandlerExProc(SB), AX 42 | JMP AX 43 | -------------------------------------------------------------------------------- /sys/windows/syscall.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | // Package windows contains an interface to the low-level operating system 8 | // primitives. OS details vary depending on the underlying system, and 9 | // by default, godoc will display the OS-specific documentation for the current 10 | // system. If you want godoc to display syscall documentation for another 11 | // system, set $GOOS and $GOARCH to the desired system. For example, if 12 | // you want to view documentation for freebsd/arm on linux/amd64, set $GOOS 13 | // to freebsd and $GOARCH to arm. 14 | // 15 | // The primary use of this package is inside other packages that provide a more 16 | // portable interface to the system, such as "os", "time" and "net". Use 17 | // those packages rather than this one if you can. 18 | // 19 | // For details of the functions and data types in this package consult 20 | // the manuals for the appropriate operating system. 21 | // 22 | // These calls return err == nil to indicate success; otherwise 23 | // err represents an operating system error describing the failure and 24 | // holds a value of type syscall.Errno. 25 | package windows // import "github.com/as/shiny/sys/windows" 26 | 27 | import ( 28 | "syscall" 29 | ) 30 | 31 | // ByteSliceFromString returns a NUL-terminated slice of bytes 32 | // containing the text of s. If s contains a NUL byte at any 33 | // location, it returns (nil, syscall.EINVAL). 34 | func ByteSliceFromString(s string) ([]byte, error) { 35 | for i := 0; i < len(s); i++ { 36 | if s[i] == 0 { 37 | return nil, syscall.EINVAL 38 | } 39 | } 40 | a := make([]byte, len(s)+1) 41 | copy(a, s) 42 | return a, nil 43 | } 44 | 45 | // BytePtrFromString returns a pointer to a NUL-terminated array of 46 | // bytes containing the text of s. If s contains a NUL byte at any 47 | // location, it returns (nil, syscall.EINVAL). 48 | func BytePtrFromString(s string) (*byte, error) { 49 | a, err := ByteSliceFromString(s) 50 | if err != nil { 51 | return nil, err 52 | } 53 | return &a[0], nil 54 | } 55 | 56 | // Single-word zero for use when we need a valid pointer to 0 bytes. 57 | // See mksyscall.pl. 58 | var _zero uintptr 59 | 60 | func (ts *Timespec) Unix() (sec int64, nsec int64) { 61 | return int64(ts.Sec), int64(ts.Nsec) 62 | } 63 | 64 | func (tv *Timeval) Unix() (sec int64, nsec int64) { 65 | return int64(tv.Sec), int64(tv.Usec) * 1000 66 | } 67 | 68 | func (ts *Timespec) Nano() int64 { 69 | return int64(ts.Sec)*1e9 + int64(ts.Nsec) 70 | } 71 | 72 | func (tv *Timeval) Nano() int64 { 73 | return int64(tv.Sec)*1e9 + int64(tv.Usec)*1000 74 | } 75 | -------------------------------------------------------------------------------- /sys/windows/syscall_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | package windows_test 8 | 9 | import ( 10 | "syscall" 11 | "testing" 12 | 13 | "github.com/as/shiny/sys/windows" 14 | ) 15 | 16 | func testSetGetenv(t *testing.T, key, value string) { 17 | err := windows.Setenv(key, value) 18 | if err != nil { 19 | t.Fatalf("Setenv failed to set %q: %v", value, err) 20 | } 21 | newvalue, found := windows.Getenv(key) 22 | if !found { 23 | t.Fatalf("Getenv failed to find %v variable (want value %q)", key, value) 24 | } 25 | if newvalue != value { 26 | t.Fatalf("Getenv(%v) = %q; want %q", key, newvalue, value) 27 | } 28 | } 29 | 30 | func TestEnv(t *testing.T) { 31 | testSetGetenv(t, "TESTENV", "AVALUE") 32 | // make sure TESTENV gets set to "", not deleted 33 | testSetGetenv(t, "TESTENV", "") 34 | } 35 | 36 | func TestGetProcAddressByOrdinal(t *testing.T) { 37 | // Attempt calling shlwapi.dll:IsOS, resolving it by ordinal, as 38 | // suggested in 39 | // https://msdn.microsoft.com/en-us/library/windows/desktop/bb773795.aspx 40 | h, err := windows.LoadLibrary("shlwapi.dll") 41 | if err != nil { 42 | t.Fatalf("Failed to load shlwapi.dll: %s", err) 43 | } 44 | procIsOS, err := windows.GetProcAddressByOrdinal(h, 437) 45 | if err != nil { 46 | t.Fatalf("Could not find shlwapi.dll:IsOS by ordinal: %s", err) 47 | } 48 | const OS_NT = 1 49 | r, _, _ := syscall.Syscall(procIsOS, 1, OS_NT, 0, 0) 50 | if r == 0 { 51 | t.Error("shlwapi.dll:IsOS(OS_NT) returned 0, expected non-zero value") 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /sys/windows/syscall_windows_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package windows_test 6 | 7 | import ( 8 | "io/ioutil" 9 | "os" 10 | "path/filepath" 11 | "syscall" 12 | "testing" 13 | "unsafe" 14 | 15 | "github.com/as/shiny/sys/windows" 16 | ) 17 | 18 | func TestWin32finddata(t *testing.T) { 19 | dir, err := ioutil.TempDir("", "go-build") 20 | if err != nil { 21 | t.Fatalf("failed to create temp directory: %v", err) 22 | } 23 | defer os.RemoveAll(dir) 24 | 25 | path := filepath.Join(dir, "long_name.and_extension") 26 | f, err := os.Create(path) 27 | if err != nil { 28 | t.Fatalf("failed to create %v: %v", path, err) 29 | } 30 | f.Close() 31 | 32 | type X struct { 33 | fd windows.Win32finddata 34 | got byte 35 | pad [10]byte // to protect ourselves 36 | 37 | } 38 | var want byte = 2 // it is unlikely to have this character in the filename 39 | x := X{got: want} 40 | 41 | pathp, _ := windows.UTF16PtrFromString(path) 42 | h, err := windows.FindFirstFile(pathp, &(x.fd)) 43 | if err != nil { 44 | t.Fatalf("FindFirstFile failed: %v", err) 45 | } 46 | err = windows.FindClose(h) 47 | if err != nil { 48 | t.Fatalf("FindClose failed: %v", err) 49 | } 50 | 51 | if x.got != want { 52 | t.Fatalf("memory corruption: want=%d got=%d", want, x.got) 53 | } 54 | } 55 | 56 | func TestFormatMessage(t *testing.T) { 57 | dll := windows.MustLoadDLL("pdh.dll") 58 | 59 | pdhOpenQuery := func(datasrc *uint16, userdata uint32, query *windows.Handle) (errno uintptr) { 60 | r0, _, _ := syscall.Syscall(dll.MustFindProc("PdhOpenQueryW").Addr(), 3, uintptr(unsafe.Pointer(datasrc)), uintptr(userdata), uintptr(unsafe.Pointer(query))) 61 | return r0 62 | } 63 | 64 | pdhCloseQuery := func(query windows.Handle) (errno uintptr) { 65 | r0, _, _ := syscall.Syscall(dll.MustFindProc("PdhCloseQuery").Addr(), 1, uintptr(query), 0, 0) 66 | return r0 67 | } 68 | 69 | var q windows.Handle 70 | name, err := windows.UTF16PtrFromString("no_such_source") 71 | if err != nil { 72 | t.Fatal(err) 73 | } 74 | errno := pdhOpenQuery(name, 0, &q) 75 | if errno == 0 { 76 | pdhCloseQuery(q) 77 | t.Fatal("PdhOpenQuery succeeded, but expected to fail.") 78 | } 79 | 80 | const flags uint32 = syscall.FORMAT_MESSAGE_FROM_HMODULE | syscall.FORMAT_MESSAGE_ARGUMENT_ARRAY | syscall.FORMAT_MESSAGE_IGNORE_INSERTS 81 | buf := make([]uint16, 300) 82 | _, err = windows.FormatMessage(flags, uintptr(dll.Handle), uint32(errno), 0, buf, nil) 83 | if err != nil { 84 | t.Fatalf("FormatMessage for handle=%x and errno=%x failed: %v", dll.Handle, errno, err) 85 | } 86 | } 87 | 88 | func abort(funcname string, err error) { 89 | panic(funcname + " failed: " + err.Error()) 90 | } 91 | 92 | func ExampleLoadLibrary() { 93 | h, err := windows.LoadLibrary("kernel32.dll") 94 | if err != nil { 95 | abort("LoadLibrary", err) 96 | } 97 | defer windows.FreeLibrary(h) 98 | proc, err := windows.GetProcAddress(h, "GetVersion") 99 | if err != nil { 100 | abort("GetProcAddress", err) 101 | } 102 | r, _, _ := syscall.Syscall(uintptr(proc), 0, 0, 0, 0) 103 | major := byte(r) 104 | minor := uint8(r >> 8) 105 | build := uint16(r >> 16) 106 | print("windows version ", major, ".", minor, " (Build ", build, ")\n") 107 | } 108 | -------------------------------------------------------------------------------- /sys/windows/types_windows_386.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package windows 6 | 7 | type WSAData struct { 8 | Version uint16 9 | HighVersion uint16 10 | Description [WSADESCRIPTION_LEN + 1]byte 11 | SystemStatus [WSASYS_STATUS_LEN + 1]byte 12 | MaxSockets uint16 13 | MaxUdpDg uint16 14 | VendorInfo *byte 15 | } 16 | 17 | type Servent struct { 18 | Name *byte 19 | Aliases **byte 20 | Port uint16 21 | Proto *byte 22 | } 23 | -------------------------------------------------------------------------------- /sys/windows/types_windows_amd64.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package windows 6 | 7 | type WSAData struct { 8 | Version uint16 9 | HighVersion uint16 10 | MaxSockets uint16 11 | MaxUdpDg uint16 12 | VendorInfo *byte 13 | Description [WSADESCRIPTION_LEN + 1]byte 14 | SystemStatus [WSASYS_STATUS_LEN + 1]byte 15 | } 16 | 17 | type Servent struct { 18 | Name *byte 19 | Aliases **byte 20 | Proto *byte 21 | Port uint16 22 | } 23 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgb/AUTHORS: -------------------------------------------------------------------------------- 1 | Andrew Gallant is the maintainer of this fork. What follows is the original 2 | list of authors for the x-go-binding. 3 | 4 | # This is the official list of XGB authors for copyright purposes. 5 | # This file is distinct from the CONTRIBUTORS files. 6 | # See the latter for an explanation. 7 | 8 | # Names should be added to this file as 9 | # Name or Organization 10 | # The email address is not required for organizations. 11 | 12 | # Please keep the list sorted. 13 | 14 | Anthony Martin 15 | Firmansyah Adiputra 16 | Google Inc. 17 | Scott Lawrence 18 | Tor Andersson 19 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgb/CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | Andrew Gallant is the maintainer of this fork. What follows is the original 2 | list of contributors for the x-go-binding. 3 | 4 | # This is the official list of people who can contribute 5 | # (and typically have contributed) code to the XGB repository. 6 | # The AUTHORS file lists the copyright holders; this file 7 | # lists people. For example, Google employees are listed here 8 | # but not in AUTHORS, because Google holds the copyright. 9 | # 10 | # The submission process automatically checks to make sure 11 | # that people submitting code are listed in this file (by email address). 12 | # 13 | # Names should be added to this file only after verifying that 14 | # the individual or the individual's organization has agreed to 15 | # the appropriate Contributor License Agreement, found here: 16 | # 17 | # http://code.google.com/legal/individual-cla-v1.0.html 18 | # http://code.google.com/legal/corporate-cla-v1.0.html 19 | # 20 | # The agreement for individuals can be filled out on the web. 21 | # 22 | # When adding J Random Contributor's name to this file, 23 | # either J's name or J's organization's name should be 24 | # added to the AUTHORS file, depending on whether the 25 | # individual or corporate CLA was used. 26 | 27 | # Names should be added to this file like so: 28 | # Name 29 | 30 | # Please keep the list sorted. 31 | 32 | Anthony Martin 33 | Firmansyah Adiputra 34 | Ian Lance Taylor 35 | Nigel Tao 36 | Robert Griesemer 37 | Russ Cox 38 | Scott Lawrence 39 | Tor Andersson 40 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgb/LICENSE: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2009 The XGB Authors. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are 5 | // met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above 10 | // copyright notice, this list of conditions and the following disclaimer 11 | // in the documentation and/or other materials provided with the 12 | // distribution. 13 | // * Neither the name of Google Inc. nor the names of its 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | // 29 | // Subject to the terms and conditions of this License, Google hereby 30 | // grants to You a perpetual, worldwide, non-exclusive, no-charge, 31 | // royalty-free, irrevocable (except as stated in this section) patent 32 | // license to make, have made, use, offer to sell, sell, import, and 33 | // otherwise transfer this implementation of XGB, where such license 34 | // applies only to those patent claims licensable by Google that are 35 | // necessarily infringed by use of this implementation of XGB. If You 36 | // institute patent litigation against any entity (including a 37 | // cross-claim or counterclaim in a lawsuit) alleging that this 38 | // implementation of XGB or a Contribution incorporated within this 39 | // implementation of XGB constitutes direct or contributory patent 40 | // infringement, then any patent licenses granted to You under this 41 | // License for this implementation of XGB shall terminate as of the date 42 | // such litigation is filed. 43 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgb/README: -------------------------------------------------------------------------------- 1 | XGB is the X Go Binding, which is a low-level API to communicate with the 2 | core X protocol and many of the X extensions. It is closely modeled after 3 | XCB and xpyb. 4 | 5 | It is thread safe and gets immediate improvement from parallelism when 6 | GOMAXPROCS > 1. (See the benchmarks in xproto/xproto_test.go for evidence.) 7 | 8 | Please see doc.go for more info. 9 | 10 | Note that unless you know you need XGB, you can probably make your life 11 | easier by using a slightly higher level library: xgbutil. 12 | 13 | Quick Usage 14 | =========== 15 | go get github.com/BurntSushi/xgb 16 | go run go/path/src/github.com/BurntSushi/xgb/examples/create-window/main.go 17 | 18 | BurntSushi's Fork 19 | ================= 20 | I've forked the XGB repository from Google Code due to inactivty upstream. 21 | 22 | Godoc documentation can be found here: 23 | http://godoc.burntsushi.net/pkg/github.com/BurntSushi/xgb/ 24 | 25 | Much of the code has been rewritten in an effort to support thread safety 26 | and multiple extensions. Namely, go_client.py has been thrown away in favor 27 | of an xgbgen package. 28 | 29 | The biggest parts that *haven't* been rewritten by me are the connection and 30 | authentication handshakes. They're inherently messy, and there's really no 31 | reason to re-work them. The rest of XGB has been completely rewritten. 32 | 33 | I like to release my code under the WTFPL, but since I'm starting with someone 34 | else's work, I'm leaving the original license/contributor/author information 35 | in tact. 36 | 37 | I suppose I can legitimately release xgbgen under the WTFPL. To be fair, it is 38 | at least as complex as XGB itself. *sigh* 39 | 40 | What follows is the original README: 41 | 42 | XGB README 43 | ========== 44 | XGB is the X protocol Go language Binding. 45 | 46 | It is the Go equivalent of XCB, the X protocol C-language Binding 47 | (http://xcb.freedesktop.org/). 48 | 49 | Unless otherwise noted, the XGB source files are distributed 50 | under the BSD-style license found in the LICENSE file. 51 | 52 | Contributions should follow the same procedure as for the Go project: 53 | http://golang.org/doc/contribute.html 54 | 55 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgb/README.vendor: -------------------------------------------------------------------------------- 1 | This is a fork-and-subset of github.com/BurntSushi/xgb at git revision 2 | 3ac861bb (2015-04-26), plus additional manual patches (see below). 3 | 4 | The subset consists of these directories: 5 | github.com/BurntSushi/xgb 6 | github.com/BurntSushi/xgb/render 7 | github.com/BurntSushi/xgb/shm 8 | github.com/BurntSushi/xgb/xproto 9 | 10 | Other directories in the original github.com/BurntSushi/xgb repository are not 11 | used by golang.org/x/exp/shiny, and were not copied to the vendor tree here: 12 | github.com/BurntSushi/xgb/bigreq 13 | github.com/BurntSushi/xgb/composite 14 | etc 15 | github.com/BurntSushi/xgb/xvmc 16 | 17 | Additional manual patches: 18 | https://go-review.googlesource.com/#/c/12765/ 19 | edits: 20 | xproto/xproto_test.go 21 | to skip tests that fail on Darwin. 22 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgb/auth.go: -------------------------------------------------------------------------------- 1 | package xgb 2 | 3 | /* 4 | auth.go contains functions to facilitate the parsing of .Xauthority files. 5 | 6 | It is largely unmodified from the original XGB package that I forked. 7 | */ 8 | 9 | import ( 10 | "encoding/binary" 11 | "errors" 12 | "io" 13 | "os" 14 | ) 15 | 16 | // readAuthority reads the X authority file for the DISPLAY. 17 | // If hostname == "" or hostname == "localhost", 18 | // then use the system's hostname (as returned by os.Hostname) instead. 19 | func readAuthority(hostname, display string) ( 20 | name string, data []byte, err error) { 21 | 22 | // b is a scratch buffer to use and should be at least 256 bytes long 23 | // (i.e. it should be able to hold a hostname). 24 | b := make([]byte, 256) 25 | 26 | // As per /usr/include/X11/Xauth.h. 27 | const familyLocal = 256 28 | 29 | if len(hostname) == 0 || hostname == "localhost" { 30 | hostname, err = os.Hostname() 31 | if err != nil { 32 | return "", nil, err 33 | } 34 | } 35 | 36 | fname := os.Getenv("XAUTHORITY") 37 | if len(fname) == 0 { 38 | home := os.Getenv("HOME") 39 | if len(home) == 0 { 40 | err = errors.New("Xauthority not found: $XAUTHORITY, $HOME not set") 41 | return "", nil, err 42 | } 43 | fname = home + "/.Xauthority" 44 | } 45 | 46 | r, err := os.Open(fname) 47 | if err != nil { 48 | return "", nil, err 49 | } 50 | defer r.Close() 51 | 52 | for { 53 | var family uint16 54 | if err := binary.Read(r, binary.BigEndian, &family); err != nil { 55 | return "", nil, err 56 | } 57 | 58 | addr, err := getString(r, b) 59 | if err != nil { 60 | return "", nil, err 61 | } 62 | 63 | disp, err := getString(r, b) 64 | if err != nil { 65 | return "", nil, err 66 | } 67 | 68 | name0, err := getString(r, b) 69 | if err != nil { 70 | return "", nil, err 71 | } 72 | 73 | data0, err := getBytes(r, b) 74 | if err != nil { 75 | return "", nil, err 76 | } 77 | 78 | if family == familyLocal && addr == hostname && disp == display { 79 | return name0, data0, nil 80 | } 81 | } 82 | panic("unreachable") 83 | } 84 | 85 | func getBytes(r io.Reader, b []byte) ([]byte, error) { 86 | var n uint16 87 | if err := binary.Read(r, binary.BigEndian, &n); err != nil { 88 | return nil, err 89 | } else if n > uint16(len(b)) { 90 | return nil, errors.New("bytes too long for buffer") 91 | } 92 | 93 | if _, err := io.ReadFull(r, b[0:n]); err != nil { 94 | return nil, err 95 | } 96 | return b[0:n], nil 97 | } 98 | 99 | func getString(r io.Reader, b []byte) (string, error) { 100 | b, err := getBytes(r, b) 101 | if err != nil { 102 | return "", err 103 | } 104 | return string(b), nil 105 | } 106 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgb/help.go: -------------------------------------------------------------------------------- 1 | package xgb 2 | 3 | /* 4 | help.go is meant to contain a rough hodge podge of functions that are mainly 5 | used in the auto generated code. Indeed, several functions here are simple 6 | wrappers so that the sub-packages don't need to be smart about which stdlib 7 | packages to import. 8 | 9 | Also, the 'Get..' and 'Put..' functions are used through the core xgb package 10 | too. (xgbutil uses them too.) 11 | */ 12 | 13 | import ( 14 | "fmt" 15 | "strings" 16 | ) 17 | 18 | // StringsJoin is an alias to strings.Join. It allows us to avoid having to 19 | // import 'strings' in each of the generated Go files. 20 | func StringsJoin(ss []string, sep string) string { 21 | return strings.Join(ss, sep) 22 | } 23 | 24 | // Sprintf is so we don't need to import 'fmt' in the generated Go files. 25 | func Sprintf(format string, v ...interface{}) string { 26 | return fmt.Sprintf(format, v...) 27 | } 28 | 29 | // Errorf is just a wrapper for fmt.Errorf. Exists for the same reason 30 | // that 'stringsJoin' and 'sprintf' exists. 31 | func Errorf(format string, v ...interface{}) error { 32 | return fmt.Errorf(format, v...) 33 | } 34 | 35 | // Pad a length to align on 4 bytes. 36 | func Pad(n int) int { 37 | return (n + 3) & ^3 38 | } 39 | 40 | // PopCount counts the number of bits set in a value list mask. 41 | func PopCount(mask0 int) int { 42 | mask := uint32(mask0) 43 | n := 0 44 | for i := uint32(0); i < 32; i++ { 45 | if mask&(1<> 8) 56 | } 57 | 58 | // Put32 takes a 32 bit integer and copies it into a byte slice. 59 | func Put32(buf []byte, v uint32) { 60 | buf[0] = byte(v) 61 | buf[1] = byte(v >> 8) 62 | buf[2] = byte(v >> 16) 63 | buf[3] = byte(v >> 24) 64 | } 65 | 66 | // Put64 takes a 64 bit integer and copies it into a byte slice. 67 | func Put64(buf []byte, v uint64) { 68 | buf[0] = byte(v) 69 | buf[1] = byte(v >> 8) 70 | buf[2] = byte(v >> 16) 71 | buf[3] = byte(v >> 24) 72 | buf[4] = byte(v >> 32) 73 | buf[5] = byte(v >> 40) 74 | buf[6] = byte(v >> 48) 75 | buf[7] = byte(v >> 56) 76 | } 77 | 78 | // Get16 constructs a 16 bit integer from the beginning of a byte slice. 79 | func Get16(buf []byte) uint16 { 80 | v := uint16(buf[0]) 81 | v |= uint16(buf[1]) << 8 82 | return v 83 | } 84 | 85 | // Get32 constructs a 32 bit integer from the beginning of a byte slice. 86 | func Get32(buf []byte) uint32 { 87 | v := uint32(buf[0]) 88 | v |= uint32(buf[1]) << 8 89 | v |= uint32(buf[2]) << 16 90 | v |= uint32(buf[3]) << 24 91 | return v 92 | } 93 | 94 | // Get64 constructs a 64 bit integer from the beginning of a byte slice. 95 | func Get64(buf []byte) uint64 { 96 | v := uint64(buf[0]) 97 | v |= uint64(buf[1]) << 8 98 | v |= uint64(buf[2]) << 16 99 | v |= uint64(buf[3]) << 24 100 | v |= uint64(buf[4]) << 32 101 | v |= uint64(buf[5]) << 40 102 | v |= uint64(buf[6]) << 48 103 | v |= uint64(buf[7]) << 56 104 | return v 105 | } 106 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/xgb/sync.go: -------------------------------------------------------------------------------- 1 | package xgb 2 | 3 | // Sync sends a round trip request and waits for the response. 4 | // This forces all pending cookies to be dealt with. 5 | // You actually shouldn't need to use this like you might with Xlib. Namely, 6 | // buffers are automatically flushed using Go's channels and round trip requests 7 | // are forced where appropriate automatically. 8 | func (c *Conn) Sync() { 9 | cookie := c.NewCookie(true, true) 10 | c.NewRequest(c.getInputFocusRequest(), cookie) 11 | cookie.Reply() // wait for the buffer to clear 12 | } 13 | 14 | // getInputFocusRequest writes the raw bytes to a buffer. 15 | // It is duplicated from xproto/xproto.go. 16 | func (c *Conn) getInputFocusRequest() []byte { 17 | size := 4 18 | b := 0 19 | buf := make([]byte, size) 20 | 21 | buf[b] = 43 // request opcode 22 | b += 1 23 | 24 | b += 1 // padding 25 | Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units 26 | b += 2 27 | 28 | return buf 29 | } 30 | --------------------------------------------------------------------------------