glfw: Implement DLL version of the binding

This commit is contained in:
Hajime Hoshi 2018-12-30 15:35:49 +09:00
parent a9455d1c32
commit a21a4c75b0
5 changed files with 525 additions and 9 deletions

View File

@ -14,8 +14,13 @@
package glfw
import (
"fmt"
)
type (
Action int
ErrorCode int
Hint int
InputMode int
Joystick int
@ -88,3 +93,43 @@ const (
CursorNormal = 0x00034001
NoAPI = 0
)
const (
NotInitialized = ErrorCode(0x00010001)
NoCurrentContext = ErrorCode(0x00010002)
InvalidEnum = ErrorCode(0x00010003)
InvalidValue = ErrorCode(0x00010004)
OutOfMemory = ErrorCode(0x00010005)
APIUnavailable = ErrorCode(0x00010006)
VersionUnavailable = ErrorCode(0x00010007)
PlatformError = ErrorCode(0x00010008)
FormatUnavailable = ErrorCode(0x00010009)
NoWindowContext = ErrorCode(0x0001000A)
)
func (e ErrorCode) String() string {
switch e {
case NotInitialized:
return "not initialized"
case NoCurrentContext:
return "no current context"
case InvalidEnum:
return "invalid enum"
case InvalidValue:
return "invalid value"
case OutOfMemory:
return "out of memory"
case APIUnavailable:
return "API unavailable"
case VersionUnavailable:
return "version unavailable"
case PlatformError:
return "platform error"
case FormatUnavailable:
return "format unavailable"
case NoWindowContext:
return "no window context"
default:
return fmt.Sprintf("GLFW error code (%d)", e)
}
}

View File

@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// +build !windows
package glfw
import (

View File

@ -0,0 +1,371 @@
// Copyright 2018 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package glfw
import (
"image"
"runtime"
"sync"
"unsafe"
"golang.org/x/sys/windows"
)
type glfwWindows map[uintptr]*Window
var (
theGLFWWindows = glfwWindows{}
glfwWindowsM sync.Mutex
)
func (w glfwWindows) add(win uintptr) *Window {
if win == 0 {
return nil
}
ww := &Window{win}
glfwWindowsM.Lock()
w[win] = ww
glfwWindowsM.Unlock()
return ww
}
func (w glfwWindows) remove(win uintptr) {
glfwWindowsM.Lock()
delete(w, win)
glfwWindowsM.Unlock()
}
func (w glfwWindows) get(win uintptr) *Window {
if win == 0 {
return nil
}
glfwWindowsM.Lock()
ww := w[win]
glfwWindowsM.Unlock()
return ww
}
type glfwVidMode struct {
width int32
height int32
redBits int32
greenBits int32
blueBits int32
refreshRate int32
}
type Monitor struct {
m uintptr
}
func (m *Monitor) GetPos() (x, y int) {
glfwDLL.call("glfwGetMonitorPos", m.m, uintptr(unsafe.Pointer(&x)), uintptr(unsafe.Pointer(&y)))
panicError()
return
}
func (m *Monitor) GetVideoMode() *VidMode {
v := glfwDLL.call("glfwGetVideoMode", m.m)
panicError()
vv := (*glfwVidMode)(unsafe.Pointer(v))
return &VidMode{
Width: int(vv.width),
Height: int(vv.height),
RedBits: int(vv.redBits),
GreenBits: int(vv.greenBits),
RefreshRate: int(vv.refreshRate),
}
}
type Window struct {
w uintptr
}
func (w *Window) Destroy() {
glfwDLL.call("glfwDestroyWindow", w.w)
panicError()
theGLFWWindows.remove(w.w)
}
func (w *Window) GetAttrib(attrib Hint) int {
r := glfwDLL.call("glfwGetWindowAttrib", w.w, uintptr(attrib))
panicError()
return int(r)
}
func (w *Window) GetCursorPos() (x, y float64) {
glfwDLL.call("glfwGetCursorPos", w.w, uintptr(unsafe.Pointer(&x)), uintptr(unsafe.Pointer(&y)))
panicError()
return
}
func (w *Window) GetInputMode(mode InputMode) int {
r := glfwDLL.call("glfwGetInputMode", w.w, uintptr(mode))
panicError()
return int(r)
}
func (w *Window) GetKey(key Key) Action {
r := glfwDLL.call("glfwGetKey", w.w, uintptr(key))
panicError()
return Action(r)
}
func (w *Window) GetMonitor() *Monitor {
m := glfwDLL.call("glfwGetWindowMonitor", w.w)
panicError()
if m == 0 {
return nil
}
return &Monitor{m}
}
func (w *Window) GetMouseButton(button MouseButton) Action {
r := glfwDLL.call("glfwGetMouseButton", w.w, uintptr(button))
panicError()
return Action(r)
}
func (w *Window) GetPos() (x, y int) {
glfwDLL.call("glfwGetWindowPos", w.w, uintptr(unsafe.Pointer(&x)), uintptr(unsafe.Pointer(&y)))
panicError()
return
}
func (w *Window) GetSize() (width, height int) {
glfwDLL.call("glfwGetWindowSize", w.w, uintptr(unsafe.Pointer(&width)), uintptr(unsafe.Pointer(&height)))
panicError()
return
}
func (w *Window) MakeContextCurrent() {
glfwDLL.call("glfwMakeContextCurrent", w.w)
panicError()
}
func (w *Window) SetCharModsCallback(cbfun CharModsCallback) (previous CharModsCallback) {
var gcb uintptr
if cbfun != nil {
gcb = windows.NewCallbackCDecl(func(window uintptr, char rune, mods ModifierKey) uintptr {
cbfun(theGLFWWindows.get(window), char, mods)
return 0
})
}
glfwDLL.call("glfwSetCharModsCallback", w.w, gcb)
panicError()
return nil // TODO
}
func (w *Window) SetFramebufferSizeCallback(cbfun FramebufferSizeCallback) (previous FramebufferSizeCallback) {
var gcb uintptr
if cbfun != nil {
gcb = windows.NewCallbackCDecl(func(window uintptr, width int, height int) uintptr {
cbfun(theGLFWWindows.get(window), width, height)
return 0
})
}
glfwDLL.call("glfwSetFramebufferSizeCallback", w.w, gcb)
panicError()
return nil // TODO
}
func (w *Window) SetScrollCallback(cbfun ScrollCallback) (previous ScrollCallback) {
var gcb uintptr
if cbfun != nil {
gcb = windows.NewCallbackCDecl(func(window uintptr, xoff *float64, yoff *float64) uintptr {
// xoff and yoff were originally float64, but there is no good way to pass them on 32bit
// machines via NewCallback. We've fixed GLFW side to use pointer values.
cbfun(theGLFWWindows.get(window), *xoff, *yoff)
return 0
})
}
glfwDLL.call("glfwSetScrollCallback", w.w, gcb)
panicError()
return nil // TODO
}
func (w *Window) SetIcon(images []image.Image) {
// TODO: Implement this
// glfwDLL.call("glfwSetWindowIcon", w.w, l, p)
// panicError()
}
func (w *Window) SetInputMode(mode InputMode, value int) {
glfwDLL.call("glfwSetInputMode", w.w, uintptr(mode), uintptr(value))
panicError()
}
func (w *Window) SetMonitor(monitor *Monitor, xpos, ypos, width, height, refreshRate int) {
var m uintptr
if monitor != nil {
m = monitor.m
}
glfwDLL.call("glfwSetWindowMonitor", w.w, m, uintptr(xpos), uintptr(ypos), uintptr(width), uintptr(height), uintptr(refreshRate))
panicError()
}
func (w *Window) SetPos(xpos, ypos int) {
glfwDLL.call("glfwSetWindowPos", w.w, uintptr(xpos), uintptr(ypos))
panicError()
}
func (w *Window) SetSize(width, height int) {
glfwDLL.call("glfwSetWindowSize", w.w, uintptr(width), uintptr(height))
panicError()
}
func (w *Window) SetTitle(title string) {
s := []byte(title)
s = append(s, 0)
defer runtime.KeepAlive(s)
glfwDLL.call("glfwSetWindowTitle", w.w, uintptr(unsafe.Pointer(&s[0])))
panicError()
}
func (w *Window) ShouldClose() bool {
r := glfwDLL.call("glfwWindowShouldClose", w.w)
panicError()
return r == True
}
func (w *Window) Show() {
glfwDLL.call("glfwShowWindow", w.w)
panicError()
}
func (w *Window) SwapBuffers() {
glfwDLL.call("glfwSwapBuffers", w.w)
panicError()
}
func CreateWindow(width, height int, title string, monitor *Monitor, share *Window) (*Window, error) {
s := []byte(title)
s = append(s, 0)
defer runtime.KeepAlive(s)
var gm uintptr
if monitor != nil {
gm = monitor.m
}
var gw uintptr
if share != nil {
gw = share.w
}
w := glfwDLL.call("glfwCreateWindow", uintptr(width), uintptr(height), uintptr(unsafe.Pointer(&s[0])), gm, gw)
if w == 0 {
return nil, acceptError(APIUnavailable, VersionUnavailable)
}
return theGLFWWindows.add(w), nil
}
func GetJoystickAxes(joy Joystick) []float32 {
l := 0
ptr := glfwDLL.call("glfwGetJoystickAxes", uintptr(joy), uintptr(unsafe.Pointer(&l)))
panicError()
as := make([]float32, l)
for i := 0; i < l; i++ {
as[i] = *(*float32)(unsafe.Pointer(ptr))
ptr += unsafe.Sizeof(float32(0))
}
return as
}
func GetJoystickButtons(joy Joystick) []byte {
l := 0
ptr := glfwDLL.call("glfwGetJoystickButtons", uintptr(joy), uintptr(unsafe.Pointer(&l)))
panicError()
bs := make([]byte, l)
for i := 0; i < l; i++ {
bs[i] = *(*byte)(unsafe.Pointer(ptr))
ptr++
}
return bs
}
func GetMonitors() []*Monitor {
l := 0
ptr := glfwDLL.call("glfwGetMonitors", uintptr(unsafe.Pointer(&l)))
panicError()
ms := make([]*Monitor, l)
for i := 0; i < l; i++ {
m := *(*unsafe.Pointer)(unsafe.Pointer(ptr))
if m != nil {
ms[i] = &Monitor{uintptr(m)}
}
ptr += unsafe.Sizeof(unsafe.Pointer(uintptr(0)))
}
return ms
}
func GetPrimaryMonitor() *Monitor {
m := glfwDLL.call("glfwGetPrimaryMonitor")
panicError()
if m == 0 {
return nil
}
return &Monitor{m}
}
func Init() error {
glfwDLL.call("glfwInit")
return acceptError(APIUnavailable)
}
func JoystickPresent(joy Joystick) bool {
r := glfwDLL.call("glfwJoystickPresent", uintptr(joy))
panicError()
return r == True
}
func PollEvents() {
glfwDLL.call("glfwPollEvents")
panicError()
}
func SetMonitorCallback(cbfun func(monitor *Monitor, event MonitorEvent)) {
var gcb uintptr
if cbfun != nil {
gcb = windows.NewCallbackCDecl(func(monitor uintptr, event MonitorEvent) uintptr {
var m *Monitor
if monitor != 0 {
m = &Monitor{monitor}
}
cbfun(m, event)
return 0
})
}
glfwDLL.call("glfwSetMonitorCallback", gcb)
panicError()
}
func SwapInterval(interval int) {
glfwDLL.call("glfwSwapInterval", uintptr(interval))
panicError()
}
func Terminate() {
flushErrors()
glfwDLL.call("glfwTerminate")
if err := glfwDLL.unload(); err != nil {
panic(err)
}
}
func WindowHint(target Hint, hint int) {
glfwDLL.call("glfwWindowHint", uintptr(target), uintptr(hint))
panicError()
}

View File

@ -17,16 +17,33 @@ package glfw
import (
"bytes"
"compress/gzip"
"fmt"
"io"
"io/ioutil"
"os"
"unsafe"
"golang.org/x/sys/windows"
)
type dll struct {
d *windows.LazyDLL
path string
d *windows.LazyDLL
path string
procs map[string]*windows.LazyProc
}
func (d *dll) call(name string, args ...uintptr) uintptr {
if d.procs == nil {
d.procs = map[string]*windows.LazyProc{}
}
if _, ok := d.procs[name]; !ok {
d.procs[name] = d.d.NewProc(name)
}
r, _, err := d.procs[name].Call(args...)
if err != nil && err.(windows.Errno) != 0 {
panic(err)
}
return r
}
func createTempDLL(content io.Reader) (string, error) {
@ -73,8 +90,91 @@ func (d *dll) unload() error {
return nil
}
func init() {
if _, err := loadDLL(); err != nil {
func uintptrToString(ptr uintptr) string {
var bs []byte
for {
b := *(*byte)(unsafe.Pointer(ptr))
if b == 0 {
break
}
bs = append(bs, b)
ptr++
}
return string(bs)
}
type glfwError struct {
code ErrorCode
desc string
}
func (e *glfwError) Error() string {
return fmt.Sprintf("glfw: %s: %s", e.code.String(), e.desc)
}
var lastErr = make(chan *glfwError, 1)
func fetchError() error {
select {
case err := <-lastErr:
return err
default:
return nil
}
}
func panicError() {
if err := acceptError(); err != nil {
panic(err)
}
}
func flushErrors() {
if err := fetchError(); err != nil {
panic(fmt.Sprintf("glfw: uncaught error: %s", err))
}
}
func acceptError(codes ...ErrorCode) error {
err := fetchError()
if err == nil {
return nil
}
for _, c := range codes {
if err.(*glfwError).code == c {
return nil
}
}
if err.(*glfwError).code == PlatformError {
// PlatformError is not handled here (See github.com/go-gl/glfw's implementation).
// TODO: Should we log this error?
return nil
}
return err
}
func goGLFWErrorCallback(code uintptr, desc uintptr) uintptr {
flushErrors()
err := &glfwError{
code: ErrorCode(code),
desc: uintptrToString(desc),
}
select {
case lastErr <- err:
default:
panic(fmt.Sprintf("glfw: uncaught error: %s", err))
}
return 0
}
var glfwDLL *dll
func init() {
dll, err := loadDLL()
if err != nil {
panic(err)
}
glfwDLL = dll
glfwDLL.call("glfwSetErrorCallback", windows.NewCallbackCDecl(goGLFWErrorCallback))
}

View File

@ -14,10 +14,8 @@
package glfw
import (
"unsafe"
)
func (w *Window) GetWin32Window() uintptr {
return uintptr(unsafe.Pointer(w.w.GetWin32Window()))
r := glfwDLL.call("glfwGetWin32Window", w.w)
panicError()
return r
}