internal: add a new package glfwwin

internal/glfwwin is a pure Go implementation of GLFW for Windows.

Updates #1764
This commit is contained in:
Hajime Hoshi 2022-04-20 00:49:43 +09:00
parent 58c871df38
commit edd617f80e
14 changed files with 8597 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,622 @@
// Copyright 2002-2006 Marcus Geelnard
// Copyright 2006-2019 Camilla Löwy
// Copyright 2022 The Ebiten Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
package glfwwin
import (
"fmt"
"math"
"regexp"
"strconv"
"strings"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
func checkValidContextConfig(ctxconfig *ctxconfig) error {
if ctxconfig.share != nil {
if ctxconfig.client == NoAPI || ctxconfig.share.context.client == NoAPI {
return NoWindowContext
}
}
if ctxconfig.source != NativeContextAPI &&
ctxconfig.source != EGLContextAPI &&
ctxconfig.source != OSMesaContextAPI {
return fmt.Errorf("glfwwin: invalid context creation API 0x%08X: %w", ctxconfig.source, InvalidEnum)
}
if ctxconfig.client != NoAPI &&
ctxconfig.client != OpenGLAPI &&
ctxconfig.client != OpenGLESAPI {
return fmt.Errorf("glfwwin: invalid client API 0x%08X: %w", ctxconfig.client, InvalidEnum)
}
if ctxconfig.client == OpenGLAPI {
if (ctxconfig.major < 1 || ctxconfig.minor < 0) ||
(ctxconfig.major == 1 && ctxconfig.minor > 5) ||
(ctxconfig.major == 2 && ctxconfig.minor > 1) ||
(ctxconfig.major == 3 && ctxconfig.minor > 3) {
// OpenGL 1.0 is the smallest valid version
// OpenGL 1.x series ended with version 1.5
// OpenGL 2.x series ended with version 2.1
// OpenGL 3.x series ended with version 3.3
// For now, let everything else through
return fmt.Errorf("glfwwin: invalid OpenGL version %d.%d: %w", ctxconfig.major, ctxconfig.minor, InvalidValue)
}
if ctxconfig.profile != 0 {
if ctxconfig.profile != OpenGLCoreProfile && ctxconfig.profile != OpenGLCompatProfile {
return fmt.Errorf("glfwwin: invalid OpenGL profile 0x%08X: %w", ctxconfig.profile, InvalidEnum)
}
if ctxconfig.major <= 2 || (ctxconfig.major == 3 && ctxconfig.minor < 2) {
// Desktop OpenGL context profiles are only defined for version 3.2
// and above
return fmt.Errorf("glfwwin: context profiles are only defined for OpenGL version 3.2 and above: %w", InvalidValue)
}
}
if ctxconfig.forward && ctxconfig.major <= 2 {
// Forward-compatible contexts are only defined for OpenGL version 3.0 and above
return fmt.Errorf("glfwwin: forward-compatibility is only defined for OpenGL version 3.0 and above: %w", InvalidValue)
}
} else if ctxconfig.client == OpenGLESAPI {
if ctxconfig.major < 1 || ctxconfig.minor < 0 ||
(ctxconfig.major == 1 && ctxconfig.minor > 1) ||
(ctxconfig.major == 2 && ctxconfig.minor > 0) {
// OpenGL ES 1.0 is the smallest valid version
// OpenGL ES 1.x series ended with version 1.1
// OpenGL ES 2.x series ended with version 2.0
// For now, let everything else through
return fmt.Errorf("glfwwin: invalid OpenGL ES version %d.%d: %w", ctxconfig.major, ctxconfig.minor, InvalidValue)
}
}
if ctxconfig.robustness != 0 {
if ctxconfig.robustness != NoResetNotification && ctxconfig.robustness != LoseContextOnReset {
return fmt.Errorf("glfwwin: invalid context robustness mode 0x%08X: %w", ctxconfig.robustness, InvalidEnum)
}
}
if ctxconfig.release != 0 {
if ctxconfig.release != ReleaseBehaviorNone && ctxconfig.release != ReleaseBehaviorFlush {
return fmt.Errorf("glfwwin: invalid context release behavior 0x%08X: %w", ctxconfig.release, InvalidEnum)
}
}
return nil
}
func chooseFBConfig(desired *fbconfig, alternatives []*fbconfig) *fbconfig {
leastMissing := math.MaxInt32
leastColorDiff := math.MaxInt32
leastExtraDiff := math.MaxInt32
var closest *fbconfig
for _, current := range alternatives {
if desired.stereo && !current.stereo {
// Stereo is a hard constraint
continue
}
// Count number of missing buffers
missing := 0
if desired.alphaBits > 0 && current.alphaBits == 0 {
missing++
}
if desired.depthBits > 0 && current.depthBits == 0 {
missing++
}
if desired.stencilBits > 0 && current.stencilBits == 0 {
missing++
}
if desired.auxBuffers > 0 &&
current.auxBuffers < desired.auxBuffers {
missing += desired.auxBuffers - current.auxBuffers
}
if desired.samples > 0 && current.samples == 0 {
// Technically, several multisampling buffers could be
// involved, but that's a lower level implementation detail and
// not important to us here, so we count them as one
missing++
}
if desired.transparent != current.transparent {
missing++
}
// These polynomials make many small channel size differences matter
// less than one large channel size difference
// Calculate color channel size difference value
colorDiff := 0
if desired.redBits != DontCare {
colorDiff += (desired.redBits - current.redBits) *
(desired.redBits - current.redBits)
}
if desired.greenBits != DontCare {
colorDiff += (desired.greenBits - current.greenBits) *
(desired.greenBits - current.greenBits)
}
if desired.blueBits != DontCare {
colorDiff += (desired.blueBits - current.blueBits) *
(desired.blueBits - current.blueBits)
}
// Calculate non-color channel size difference value
extraDiff := 0
if desired.alphaBits != DontCare {
extraDiff += (desired.alphaBits - current.alphaBits) *
(desired.alphaBits - current.alphaBits)
}
if desired.depthBits != DontCare {
extraDiff += (desired.depthBits - current.depthBits) *
(desired.depthBits - current.depthBits)
}
if desired.stencilBits != DontCare {
extraDiff += (desired.stencilBits - current.stencilBits) *
(desired.stencilBits - current.stencilBits)
}
if desired.accumRedBits != DontCare {
extraDiff += (desired.accumRedBits - current.accumRedBits) *
(desired.accumRedBits - current.accumRedBits)
}
if desired.accumGreenBits != DontCare {
extraDiff += (desired.accumGreenBits - current.accumGreenBits) *
(desired.accumGreenBits - current.accumGreenBits)
}
if desired.accumBlueBits != DontCare {
extraDiff += (desired.accumBlueBits - current.accumBlueBits) *
(desired.accumBlueBits - current.accumBlueBits)
}
if desired.accumAlphaBits != DontCare {
extraDiff += (desired.accumAlphaBits - current.accumAlphaBits) *
(desired.accumAlphaBits - current.accumAlphaBits)
}
if desired.samples != DontCare {
extraDiff += (desired.samples - current.samples) *
(desired.samples - current.samples)
}
if desired.sRGB && !current.sRGB {
extraDiff++
}
// Figure out if the current one is better than the best one found so far
// Least number of missing buffers is the most important heuristic,
// then color buffer size match and lastly size match for other buffers
if missing < leastMissing {
closest = current
} else if missing == leastMissing {
if (colorDiff < leastColorDiff) || (colorDiff == leastColorDiff && extraDiff < leastExtraDiff) {
closest = current
}
}
if current == closest {
leastMissing = missing
leastColorDiff = colorDiff
leastExtraDiff = extraDiff
}
}
return closest
}
func (w *Window) refreshContextAttribs(ctxconfig *ctxconfig) (ferr error) {
const (
GL_COLOR_BUFFER_BIT = 0x00004000
GL_CONTEXT_COMPATIBILITY_PROFILE_BIT = 0x00000002
GL_CONTEXT_CORE_PROFILE_BIT = 0x00000001
GL_CONTEXT_FLAG_DEBUG_BIT = 0x00000002
GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT = 0x00000001
GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR = 0x00000008
GL_CONTEXT_FLAGS = 0x821E
GL_CONTEXT_PROFILE_MASK = 0x9126
GL_CONTEXT_RELEASE_BEHAVIOR = 0x82FB
GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH = 0x82FC
GL_LOSE_CONTEXT_ON_RESET_ARB = 0x8252
GL_NO_RESET_NOTIFICATION_ARB = 0x8261
GL_NONE = 0
GL_RESET_NOTIFICATION_STRATEGY_ARB = 0x8256
GL_VERSION = 0x1F02
)
w.context.source = ctxconfig.source
w.context.client = OpenGLAPI
p, err := _glfw.contextSlot.get()
if err != nil {
return err
}
previous := (*Window)(unsafe.Pointer(p))
defer func() {
err := previous.MakeContextCurrent()
if ferr == nil {
ferr = err
}
}()
if err := w.MakeContextCurrent(); err != nil {
return err
}
getIntegerv := w.context.getProcAddress("glGetIntegerv")
getString := w.context.getProcAddress("glGetString")
if getIntegerv == 0 || getString == 0 {
return fmt.Errorf("glfwwin: entry point retrieval is broken: %w", PlatformError)
}
r, _, _ := syscall.Syscall(getString, 1, GL_VERSION, 0, 0)
version := windows.BytePtrToString((*byte)(unsafe.Pointer(r)))
if version == "" {
if ctxconfig.client == OpenGLAPI {
return fmt.Errorf("glfwwin: OpenGL version string retrieval is broken: %w", PlatformError)
} else {
return fmt.Errorf("glfwwin: OpenGL ES version string retrieval is broken: %w", PlatformError)
}
}
for _, prefix := range []string{
"OpenGL ES-CM ",
"OpenGL ES-CL ",
"OpenGL ES "} {
if strings.HasPrefix(version, prefix) {
version = version[len(prefix):]
w.context.client = OpenGLESAPI
break
}
}
m := regexp.MustCompile(`^(\d+)(\.(\d+)(\.(\d+))?)?`).FindStringSubmatch(version)
if m == nil {
if w.context.client == OpenGLAPI {
return fmt.Errorf("glfwwin: no version found in OpenGL version string: %w", PlatformError)
} else {
return fmt.Errorf("glfwwin: no version found in OpenGL ES version string: %w", PlatformError)
}
}
w.context.major, _ = strconv.Atoi(m[1])
w.context.minor, _ = strconv.Atoi(m[3])
w.context.revision, _ = strconv.Atoi(m[5])
if w.context.major < ctxconfig.major || (w.context.major == ctxconfig.major && w.context.minor < ctxconfig.minor) {
// The desired OpenGL version is greater than the actual version
// This only happens if the machine lacks {GLX|WGL}_ARB_create_context
// /and/ the user has requested an OpenGL version greater than 1.0
// For API consistency, we emulate the behavior of the
// {GLX|WGL}_ARB_create_context extension and fail here
if w.context.client == OpenGLAPI {
return fmt.Errorf("glfwwin: requested OpenGL version %d.%d, got version %d.%d: %w", ctxconfig.major, ctxconfig.minor, w.context.major, w.context.minor, VersionUnavailable)
} else {
return fmt.Errorf("glfwwin: requested OpenGL ES version %d.%d, got version %d.%d: %w", ctxconfig.major, ctxconfig.minor, w.context.major, w.context.minor, VersionUnavailable)
}
}
if w.context.major >= 3 {
// OpenGL 3.0+ uses a different function for extension string retrieval
// We cache it here instead of in glfwExtensionSupported mostly to alert
// users as early as possible that their build may be broken
glGetStringi := w.context.getProcAddress("glGetStringi")
if glGetStringi == 0 {
return fmt.Errorf("glfwwin: entry point retrieval is broken: %w", PlatformError)
}
}
if w.context.client == OpenGLAPI {
// Read back context flags (OpenGL 3.0 and above)
if w.context.major >= 3 {
var flags int32
syscall.Syscall(getIntegerv, 2, GL_CONTEXT_FLAGS, uintptr(unsafe.Pointer(&flags)), 0)
if flags&GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT != 0 {
w.context.forward = true
}
if flags&GL_CONTEXT_FLAG_DEBUG_BIT != 0 {
w.context.debug = true
} else {
ok, err := ExtensionSupported("GL_ARB_debug_output")
if err != nil {
return err
}
if ok && ctxconfig.debug {
// HACK: This is a workaround for older drivers (pre KHR_debug)
// not setting the debug bit in the context flags for
// debug contexts
w.context.debug = true
}
}
if flags&GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR != 0 {
w.context.noerror = true
}
}
// Read back OpenGL context profile (OpenGL 3.2 and above)
if w.context.major >= 4 || (w.context.major == 3 && w.context.minor >= 2) {
var mask int32
syscall.Syscall(getIntegerv, 2, GL_CONTEXT_PROFILE_MASK, uintptr(unsafe.Pointer(&mask)), 0)
if mask&GL_CONTEXT_COMPATIBILITY_PROFILE_BIT != 0 {
w.context.profile = OpenGLCompatProfile
} else if mask&GL_CONTEXT_CORE_PROFILE_BIT != 0 {
w.context.profile = OpenGLCoreProfile
} else {
ok, err := ExtensionSupported("GL_ARB_compatibility")
if err != nil {
return err
}
if ok {
// HACK: This is a workaround for the compatibility profile bit
// not being set in the context flags if an OpenGL 3.2+
// context was created without having requested a specific
// version
w.context.profile = OpenGLCompatProfile
}
}
}
// Read back robustness strategy
ok, err := ExtensionSupported("GL_ARB_robustness")
if err != nil {
return err
}
if ok {
// NOTE: We avoid using the context flags for detection, as they are
// only present from 3.0 while the extension applies from 1.1
var strategy int32
syscall.Syscall(getIntegerv, 2, GL_RESET_NOTIFICATION_STRATEGY_ARB, uintptr(unsafe.Pointer(&strategy)), 0)
if strategy == GL_LOSE_CONTEXT_ON_RESET_ARB {
w.context.robustness = LoseContextOnReset
} else if strategy == GL_NO_RESET_NOTIFICATION_ARB {
w.context.robustness = NoResetNotification
}
}
} else {
// Read back robustness strategy
ok, err := ExtensionSupported("GL_EXT_robustness")
if err != nil {
return err
}
if ok {
// NOTE: The values of these constants match those of the OpenGL ARB
// one, so we can reuse them here
var strategy int32
syscall.Syscall(getIntegerv, 2, GL_RESET_NOTIFICATION_STRATEGY_ARB, uintptr(unsafe.Pointer(&strategy)), 0)
if strategy == GL_LOSE_CONTEXT_ON_RESET_ARB {
w.context.robustness = LoseContextOnReset
} else if strategy == GL_NO_RESET_NOTIFICATION_ARB {
w.context.robustness = NoResetNotification
}
}
}
ok, err := ExtensionSupported("GL_KHR_context_flush_control")
if err != nil {
return err
}
if ok {
var behavior int32
syscall.Syscall(getIntegerv, 2, GL_CONTEXT_RELEASE_BEHAVIOR, uintptr(unsafe.Pointer(&behavior)), 0)
if behavior == GL_NONE {
w.context.release = ReleaseBehaviorNone
} else if behavior == GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH {
w.context.release = ReleaseBehaviorFlush
}
}
// Clearing the front buffer to black to avoid garbage pixels left over from
// previous uses of our bit of VRAM
glClear := w.context.getProcAddress("glClear")
syscall.Syscall(glClear, 1, GL_COLOR_BUFFER_BIT, 0, 0)
if w.doublebuffer {
w.context.swapBuffers(w)
}
return nil
}
func (w *Window) MakeContextCurrent() error {
if !_glfw.initialized {
return NotInitialized
}
ptr, err := _glfw.contextSlot.get()
if err != nil {
return err
}
previous := (*Window)(unsafe.Pointer(ptr))
if w != nil && w.context.client == NoAPI {
return fmt.Errorf("glfwwin: cannot make current with a window that has no OpenGL or OpenGL ES context: %w", NoWindowContext)
}
if previous != nil {
if w == nil || w.context.source != previous.context.source {
if err := previous.context.makeCurrent(nil); err != nil {
return err
}
}
}
if w != nil {
if err := w.context.makeCurrent(w); err != nil {
return err
}
}
return nil
}
func GetCurrentContext() (*Window, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
ptr, err := _glfw.contextSlot.get()
if err != nil {
return nil, err
}
return (*Window)(unsafe.Pointer(ptr)), nil
}
func (w *Window) SwapBuffers() error {
if !_glfw.initialized {
return NotInitialized
}
if w.context.client == NoAPI {
return fmt.Errorf("glfwwin: cannot swap buffers of a window that has no OpenGL or OpenGL ES context: %w", NoWindowContext)
}
if err := w.context.swapBuffers(w); err != nil {
return err
}
return nil
}
func SwapInterval(interval int) error {
if !_glfw.initialized {
return NotInitialized
}
ptr, err := _glfw.contextSlot.get()
if err != nil {
return err
}
window := (*Window)(unsafe.Pointer(ptr))
if window == nil {
return fmt.Errorf("glfwwin: cannot set swap interval without a current OpenGL or OpenGL ES context %w", NoCurrentContext)
}
if err := window.context.swapInterval(interval); err != nil {
return err
}
return nil
}
func ExtensionSupported(extension string) (bool, error) {
const (
GL_EXTENSIONS = 0x1F03
GL_NUM_EXTENSIONS = 0x821D
)
if !_glfw.initialized {
return false, NotInitialized
}
ptr, err := _glfw.contextSlot.get()
if err != nil {
return false, err
}
window := (*Window)(unsafe.Pointer(ptr))
if window == nil {
return false, fmt.Errorf("glfwwin: cannot query extension without a current OpenGL or OpenGL ES context %w", NoCurrentContext)
}
if window.context.major >= 3 {
// Check if extension is in the modern OpenGL extensions string list
glGetIntegerv := window.context.getProcAddress("glGetIntegerv")
var count int32
syscall.Syscall(glGetIntegerv, 2, GL_NUM_EXTENSIONS, uintptr(unsafe.Pointer(&count)), 0)
glGetStringi := window.context.getProcAddress("glGetStringi")
for i := 0; i < int(count); i++ {
r, _, _ := syscall.Syscall(glGetStringi, 2, GL_EXTENSIONS, uintptr(i), 0)
if r == 0 {
return false, fmt.Errorf("glfwwin: extension string retrieval is broken: %w", PlatformError)
}
en := windows.BytePtrToString((*byte)(unsafe.Pointer(r)))
if en == extension {
return true, nil
}
}
} else {
// Check if extension is in the old style OpenGL extensions string
glGetString := window.context.getProcAddress("glGetString")
r, _, _ := syscall.Syscall(glGetString, 1, GL_EXTENSIONS, 0, 0)
if r == 0 {
return false, fmt.Errorf("glfwwin: extension string retrieval is broken: %w", PlatformError)
}
extensions := windows.BytePtrToString((*byte)(unsafe.Pointer(r)))
for _, str := range strings.Split(extensions, " ") {
if str == extension {
return true, nil
}
}
}
// Check if extension is in the platform-specific string
return window.context.extensionSupported(extension), nil
}
func GetProcAddress(procname string) (unsafe.Pointer, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
ptr, err := _glfw.contextSlot.get()
if err != nil {
return nil, err
}
window := (*Window)(unsafe.Pointer(ptr))
if window == nil {
return nil, fmt.Errorf("glfwwin: cannot query entry point without a current OpenGL or OpenGL ES context %w", NoCurrentContext)
}
return unsafe.Pointer(window.context.getProcAddress(procname)), nil
}

View File

@ -0,0 +1,358 @@
// Copyright 2002-2006 Marcus Geelnard
// Copyright 2006-2019 Camilla Löwy
// Copyright 2022 The Ebiten Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
package glfwwin
const (
NoAPI = 0
OpenGLAPI = 0x00030001
OpenGLESAPI = 0x00030002
NoRobustness = 0
NoResetNotification = 0x00031001
LoseContextOnReset = 0x00031002
OpenGLAnyProfile = 0
OpenGLCoreProfile = 0x00032001
OpenGLCompatProfile = 0x00032002
CursorNormal = 0x00034001
CursorHidden = 0x00034002
CursorDisabled = 0x00034003
AnyReleaseBehavior = 0
ReleaseBehaviorFlush = 0x00035001
ReleaseBehaviorNone = 0x00035002
NativeContextAPI = 0x00036001
EGLContextAPI = 0x00036002
OSMesaContextAPI = 0x00036003
DontCare = -1
)
type Action int
const (
Release Action = 0
Press Action = 1
Repeat Action = 2
)
type Hint int
const (
Focused Hint = 0x00020001
Iconified Hint = 0x00020002
Resizable Hint = 0x00020003
Visible Hint = 0x00020004
Decorated Hint = 0x00020005
AutoIconify Hint = 0x00020006
Floating Hint = 0x00020007
Maximized Hint = 0x00020008
CenterCursor Hint = 0x00020009
TransparentFramebuffer Hint = 0x0002000A
Hovered Hint = 0x0002000B
FocusOnShow Hint = 0x0002000C
RedBits Hint = 0x00021001
GreenBits Hint = 0x00021002
BlueBits Hint = 0x00021003
AlphaBits Hint = 0x00021004
DepthBits Hint = 0x00021005
StencilBits Hint = 0x00021006
AccumRedBits Hint = 0x00021007
AccumGreenBits Hint = 0x00021008
AccumBlueBits Hint = 0x00021009
AccumAlphaBits Hint = 0x0002100A
AuxBuffers Hint = 0x0002100B
Stereo Hint = 0x0002100C
Samples Hint = 0x0002100D
SRGBCapable Hint = 0x0002100E
RefreshRate Hint = 0x0002100F
DoubleBuffer Hint = 0x00021010
ClientAPI Hint = 0x00022001
ContextVersionMajor Hint = 0x00022002
ContextVersionMinor Hint = 0x00022003
ContextRevision Hint = 0x00022004
ContextRobustness Hint = 0x00022005
OpenGLForwardCompat Hint = 0x00022006
OpenGLDebugContext Hint = 0x00022007
OpenGLProfile Hint = 0x00022008
ContextReleaseBehavior Hint = 0x00022009
ContextNoError Hint = 0x0002200A
ContextCreationAPI Hint = 0x0002200B
ScaleToMonitor Hint = 0x0002200C
)
type InputMode int
const (
CursorMode InputMode = 0x00033001
StickyKeysMode InputMode = 0x00033002
StickyMouseButtonsMode InputMode = 0x00033003
LockKeyMods InputMode = 0x00033004
RawMouseMotion InputMode = 0x00033005
)
type Key int
const (
KeyUnknown Key = -1
// Printable keys
KeySpace Key = 32
KeyApostrophe Key = 39 // '
KeyComma Key = 44 // ,
KeyMinus Key = 45 // -
KeyPeriod Key = 46 // .
KeySlash Key = 47 // /
Key0 Key = 48
Key1 Key = 49
Key2 Key = 50
Key3 Key = 51
Key4 Key = 52
Key5 Key = 53
Key6 Key = 54
Key7 Key = 55
Key8 Key = 56
Key9 Key = 57
KeySemicolon Key = 59 // ;
KeyEqual Key = 61 // =
KeyA Key = 65
KeyB Key = 66
KeyC Key = 67
KeyD Key = 68
KeyE Key = 69
KeyF Key = 70
KeyG Key = 71
KeyH Key = 72
KeyI Key = 73
KeyJ Key = 74
KeyK Key = 75
KeyL Key = 76
KeyM Key = 77
KeyN Key = 78
KeyO Key = 79
KeyP Key = 80
KeyQ Key = 81
KeyR Key = 82
KeyS Key = 83
KeyT Key = 84
KeyU Key = 85
KeyV Key = 86
KeyW Key = 87
KeyX Key = 88
KeyY Key = 89
KeyZ Key = 90
KeyLeftBracket Key = 91 // [
KeyBackslash Key = 92 // \
KeyRightBracket Key = 93 // ]
KeyGraveAccent Key = 96 // `
KeyWorld1 Key = 161 // non-US #1
KeyWorld2 Key = 162 // non-US #2
// Function keys
KeyEscape Key = 256
KeyEnter Key = 257
KeyTab Key = 258
KeyBackspace Key = 259
KeyInsert Key = 260
KeyDelete Key = 261
KeyRight Key = 262
KeyLeft Key = 263
KeyDown Key = 264
KeyUp Key = 265
KeyPageUp Key = 266
KeyPageDown Key = 267
KeyHome Key = 268
KeyEnd Key = 269
KeyCapsLock Key = 280
KeyScrollLock Key = 281
KeyNumLock Key = 282
KeyPrintScreen Key = 283
KeyPause Key = 284
KeyF1 Key = 290
KeyF2 Key = 291
KeyF3 Key = 292
KeyF4 Key = 293
KeyF5 Key = 294
KeyF6 Key = 295
KeyF7 Key = 296
KeyF8 Key = 297
KeyF9 Key = 298
KeyF10 Key = 299
KeyF11 Key = 300
KeyF12 Key = 301
KeyF13 Key = 302
KeyF14 Key = 303
KeyF15 Key = 304
KeyF16 Key = 305
KeyF17 Key = 306
KeyF18 Key = 307
KeyF19 Key = 308
KeyF20 Key = 309
KeyF21 Key = 310
KeyF22 Key = 311
KeyF23 Key = 312
KeyF24 Key = 313
KeyF25 Key = 314
KeyKP0 Key = 320
KeyKP1 Key = 321
KeyKP2 Key = 322
KeyKP3 Key = 323
KeyKP4 Key = 324
KeyKP5 Key = 325
KeyKP6 Key = 326
KeyKP7 Key = 327
KeyKP8 Key = 328
KeyKP9 Key = 329
KeyKPDecimal Key = 330
KeyKPDivide Key = 331
KeyKPMultiply Key = 332
KeyKPSubtract Key = 333
KeyKPAdd Key = 334
KeyKPEnter Key = 335
KeyKPEqual Key = 336
KeyLeftShift Key = 340
KeyLeftControl Key = 341
KeyLeftAlt Key = 342
KeyLeftSuper Key = 343
KeyRightShift Key = 344
KeyRightControl Key = 345
KeyRightAlt Key = 346
KeyRightSuper Key = 347
KeyMenu Key = 348
KeyLast Key = KeyMenu
)
type ModifierKey int
const (
ModShift ModifierKey = 0x0001
ModControl ModifierKey = 0x0002
ModAlt ModifierKey = 0x0004
ModSuper ModifierKey = 0x0008
ModCapsLock ModifierKey = 0x0010
ModNumLock ModifierKey = 0x0020
)
type MouseButton int
const (
MouseButton1 MouseButton = 0
MouseButton2 MouseButton = 1
MouseButton3 MouseButton = 2
MouseButton4 MouseButton = 3
MouseButton5 MouseButton = 4
MouseButton6 MouseButton = 5
MouseButton7 MouseButton = 6
MouseButton8 MouseButton = 7
MouseButtonLast MouseButton = MouseButton8
MouseButtonLeft MouseButton = MouseButton1
MouseButtonRight MouseButton = MouseButton2
MouseButtonMiddle MouseButton = MouseButton3
)
var (
NotInitialized Error = 0x00010001
NoCurrentContext Error = 0x00010002
InvalidEnum Error = 0x00010003
InvalidValue Error = 0x00010004
OutOfMemory Error = 0x00010005
ApiUnavailable Error = 0x00010006
VersionUnavailable Error = 0x00010007
PlatformError Error = 0x00010008
FormatUnavailable Error = 0x00010009
NoWindowContext Error = 0x0001000A
)
type PeripheralEvent int
const (
Connected PeripheralEvent = 0x00040001
Disconnected PeripheralEvent = 0x00040002
)
type StandardCursor int
const (
ArrowCursor StandardCursor = 0x00036001
IBeamCursor StandardCursor = 0x00036002
CrosshairCursor StandardCursor = 0x00036003
HandCursor StandardCursor = 0x00036004
HResizeCursor StandardCursor = 0x00036005
VResizeCursor StandardCursor = 0x00036006
)
type Error int
func (e Error) Error() string {
switch e {
case NotInitialized:
return "the GLFW library is not initialized"
case NoCurrentContext:
return "there is no current context"
case InvalidEnum:
return "invalid argument for enum parameter"
case InvalidValue:
return "invalid value for parameter"
case OutOfMemory:
return "out of memory"
case ApiUnavailable:
return "the requested API is unavailable"
case VersionUnavailable:
return "the requested API version is unavailable"
case PlatformError:
return "a platform-specific error occurred"
case FormatUnavailable:
return "the requested format is unavailable"
case NoWindowContext:
return "the specified window has no context"
default:
return "ERROR: UNKNOWN GLFW ERROR"
}
}
type VidMode struct {
Width int
Height int
RedBits int
GreenBits int
BlueBits int
RefreshRate int
}
type GammaRamp struct {
Red []int
Green []int
Blue []int
}
type Image struct {
Width int
Height int
Pixels []byte
}

View File

@ -0,0 +1,97 @@
// Copyright 2002-2006 Marcus Geelnard
// Copyright 2006-2019 Camilla Löwy
// Copyright 2022 The Ebiten Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
package glfwwin
func terminate() error {
for _, w := range _glfw.windows {
if err := w.Destroy(); err != nil {
return err
}
}
for _, c := range _glfw.cursors {
if err := c.Destroy(); err != nil {
return err
}
}
for _, monitor := range _glfw.monitors {
if len(monitor.originalRamp.Red) != 0 {
monitor.platformSetGammaRamp(&monitor.originalRamp)
}
}
_glfw.monitors = nil
if err := platformTerminate(); err != nil {
return err
}
_glfw.initialized = false
if err := _glfw.contextSlot.destroy(); err != nil {
return err
}
return nil
}
func Init() (ferr error) {
defer func() {
if ferr != nil {
terminate()
}
}()
if _glfw.initialized {
return nil
}
_glfw.hints.init.hatButtons = true
if err := platformInit(); err != nil {
return err
}
if err := _glfw.contextSlot.create(); err != nil {
return err
}
_glfw.initialized = true
if err := defaultWindowHints(); err != nil {
return err
}
return nil
}
func Terminate() error {
if !_glfw.initialized {
return nil
}
if err := terminate(); err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,532 @@
// Copyright 2002-2006 Marcus Geelnard
// Copyright 2006-2019 Camilla Löwy
// Copyright 2022 The Ebiten Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
package glfwwin
import (
"fmt"
"math"
)
const stick = 3
func (w *Window) inputKey(key Key, scancode int, action Action, mods ModifierKey) {
if key >= 0 && key <= KeyLast {
var repeated bool
if action == Release && w.keys[key] == Release {
return
}
if action == Press && w.keys[key] == Press {
repeated = true
}
if action == Release && w.stickyKeys {
w.keys[key] = stick
} else {
w.keys[key] = action
}
if repeated {
action = Repeat
}
}
if !w.lockKeyMods {
mods &^= ModCapsLock | ModNumLock
}
if w.callbacks.key != nil {
w.callbacks.key(w, key, scancode, action, mods)
}
}
func (w *Window) inputChar(codepoint rune, mods ModifierKey, plain bool) {
if codepoint < 32 || (codepoint > 126 && codepoint < 160) {
return
}
if !w.lockKeyMods {
mods &^= ModCapsLock | ModNumLock
}
if w.callbacks.charmods != nil {
w.callbacks.charmods(w, codepoint, mods)
}
if plain {
if w.callbacks.character != nil {
w.callbacks.character(w, codepoint)
}
}
}
func (w *Window) inputScroll(xoffset, yoffset float64) {
if w.callbacks.scroll != nil {
w.callbacks.scroll(w, xoffset, yoffset)
}
}
func (w *Window) inputMouseClick(button MouseButton, action Action, mods ModifierKey) {
if button < 0 || button > MouseButtonLast {
return
}
if !w.lockKeyMods {
mods &^= ModCapsLock | ModNumLock
}
if action == Release && w.stickyMouseButtons {
w.mouseButtons[button] = stick
} else {
w.mouseButtons[button] = action
}
if w.callbacks.mouseButton != nil {
w.callbacks.mouseButton(w, button, action, mods)
}
}
func (w *Window) inputCursorPos(xpos float64, ypos float64) {
if w.virtualCursorPosX == xpos && w.virtualCursorPosY == ypos {
return
}
w.virtualCursorPosX = xpos
w.virtualCursorPosY = ypos
if w.callbacks.cursorPos != nil {
w.callbacks.cursorPos(w, xpos, ypos)
}
}
func (w *Window) inputCursorEnter(entered bool) {
if w.callbacks.cursorEnter != nil {
w.callbacks.cursorEnter(w, entered)
}
}
func (w *Window) inputDrop(paths []string) {
if w.callbacks.drop != nil {
w.callbacks.drop(w, paths)
}
}
func (w *Window) centerCursorInContentArea() error {
width, height, err := w.platformGetWindowSize()
if err != nil {
return err
}
if err := w.platformSetCursorPos(float64(width/2), float64(height/2)); err != nil {
return err
}
return nil
}
func (w *Window) GetInputMode(mode InputMode) (int, error) {
if !_glfw.initialized {
return 0, NotInitialized
}
switch mode {
case CursorMode:
return w.cursorMode, nil
case StickyKeysMode:
return boolToInt(w.stickyKeys), nil
case StickyMouseButtonsMode:
return boolToInt(w.stickyMouseButtons), nil
case LockKeyMods:
return boolToInt(w.lockKeyMods), nil
case RawMouseMotion:
return boolToInt(w.rawMouseMotion), nil
default:
return 0, fmt.Errorf("glfwwin: invalid input mode 0x%08X: %w", mode, InvalidEnum)
}
}
func (w *Window) SetInputMode(mode InputMode, value int) error {
if !_glfw.initialized {
return NotInitialized
}
switch mode {
case CursorMode:
if value != CursorNormal && value != CursorHidden && value != CursorDisabled {
return fmt.Errorf("glfwwin: invalid cursor mode 0x%08X: %w", value, InvalidEnum)
}
if w.cursorMode == value {
return nil
}
w.cursorMode = value
x, y, err := w.platformGetCursorPos()
if err != nil {
return err
}
w.virtualCursorPosX = x
w.virtualCursorPosY = y
if err := w.platformSetCursorMode(value); err != nil {
return err
}
return nil
case StickyKeysMode:
if w.stickyKeys == intToBool(value) {
return nil
}
if !intToBool(value) {
// Release all sticky keys
for i := Key(0); i <= KeyLast; i++ {
if w.keys[i] == stick {
w.keys[i] = Release
}
}
}
w.stickyKeys = intToBool(value)
return nil
case StickyMouseButtonsMode:
if w.stickyMouseButtons == intToBool(value) {
return nil
}
if !intToBool(value) {
// Release all sticky mouse buttons
for i := MouseButton(0); i <= MouseButtonLast; i++ {
if w.mouseButtons[i] == stick {
w.mouseButtons[i] = Release
}
}
}
w.stickyMouseButtons = intToBool(value)
return nil
case LockKeyMods:
w.lockKeyMods = intToBool(value)
return nil
case RawMouseMotion:
if !platformRawMouseMotionSupported() {
return fmt.Errorf("glfwwin: raw mouse motion is not supported on this system: %w", PlatformError)
}
if w.rawMouseMotion == intToBool(value) {
return nil
}
w.rawMouseMotion = intToBool(value)
if err := w.platformSetRawMouseMotion(intToBool(value)); err != nil {
return err
}
return nil
default:
return fmt.Errorf("glfwwin: invalid input mode 0x%08X: %w", mode, InvalidEnum)
}
}
func RawMouseMotionSupported() (bool, error) {
if !_glfw.initialized {
return false, NotInitialized
}
return platformRawMouseMotionSupported(), nil
}
func GetKeyName(key Key, scancode int) (string, error) {
if !_glfw.initialized {
return "", NotInitialized
}
if key != KeyUnknown {
if key != KeyKPEqual && (key < KeyKP0 || key > KeyKPAdd) && (key < KeyApostrophe || key > KeyWorld2) {
return "", nil
}
scancode = platformGetKeyScancode(key)
}
return platformGetScancodeName(scancode)
}
func GetKeyScancode(key Key) (int, error) {
if !_glfw.initialized {
return 0, NotInitialized
}
if key < KeySpace || key > KeyLast {
return 0, fmt.Errorf("glfwwin: invalid key %d: %w", key, InvalidEnum)
}
return platformGetKeyScancode(key), nil
}
func (w *Window) GetKey(key Key) (Action, error) {
if !_glfw.initialized {
return 0, NotInitialized
}
if key < KeySpace || key > KeyLast {
return 0, fmt.Errorf("glfwwin: invalid key %d: %w", key, InvalidEnum)
}
if w.keys[key] == stick {
// Sticky mode: release key now
w.keys[key] = Release
return Press, nil
}
return w.keys[key], nil
}
func (w *Window) GetMouseButton(button MouseButton) (Action, error) {
if !_glfw.initialized {
return 0, NotInitialized
}
if button < MouseButton1 || button > MouseButtonLast {
return 0, fmt.Errorf("glfwwin: invalid mouse button %d: %w", button, InvalidEnum)
}
if w.mouseButtons[button] == stick {
// Sticky mode: release mouse button now
w.mouseButtons[button] = Release
return Press, nil
}
return w.mouseButtons[button], nil
}
func (w *Window) GetCursorPos() (xpos, ypos float64, err error) {
if !_glfw.initialized {
return 0, 0, NotInitialized
}
if w.cursorMode == CursorDisabled {
return w.virtualCursorPosX, w.virtualCursorPosY, nil
} else {
return w.platformGetCursorPos()
}
}
func (w *Window) SetCursorPos(xpos, ypos float64) error {
if !_glfw.initialized {
return NotInitialized
}
if xpos != xpos || xpos < -math.MaxFloat64 || xpos > math.MaxFloat64 || ypos != ypos || ypos < -math.MaxFloat64 || ypos > math.MaxFloat64 {
return fmt.Errorf("glfwwin: invalid cursor position %f %f: %w", xpos, ypos, InvalidValue)
}
if !w.platformWindowFocused() {
return nil
}
if w.cursorMode == CursorDisabled {
// Only update the accumulated position if the cursor is disabled
w.virtualCursorPosX = xpos
w.virtualCursorPosY = ypos
return nil
} else {
// Update system cursor position
return w.platformSetCursorPos(xpos, ypos)
}
}
func CreateCursor(image *Image, xhot, yhot int) (*Cursor, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
cursor := &Cursor{}
_glfw.cursors = append(_glfw.cursors, cursor)
if err := cursor.platformCreateCursor(image, xhot, yhot); err != nil {
cursor.Destroy()
return nil, err
}
return cursor, nil
}
func CreateStandardCursor(shape StandardCursor) (*Cursor, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
if shape != ArrowCursor &&
shape != IBeamCursor &&
shape != CrosshairCursor &&
shape != HandCursor &&
shape != HResizeCursor &&
shape != VResizeCursor {
return nil, fmt.Errorf("glfwwin: invalid standard cursor 0x%08X: %w", shape, InvalidEnum)
}
cursor := &Cursor{}
_glfw.cursors = append(_glfw.cursors, cursor)
if err := cursor.platformCreateStandardCursor(shape); err != nil {
cursor.Destroy()
return nil, err
}
return cursor, nil
}
func (c *Cursor) Destroy() error {
if !_glfw.initialized {
return NotInitialized
}
if c == nil {
return nil
}
// Make sure the cursor is not being used by any window
for _, window := range _glfw.windows {
if window.cursor == c {
if err := window.SetCursor(nil); err != nil {
return err
}
}
}
if err := c.platformDestroyCursor(); err != nil {
return err
}
// Unlink cursor from global linked list
for i, cursor := range _glfw.cursors {
if cursor == c {
copy(_glfw.cursors[i:], _glfw.cursors[i+1:])
_glfw.cursors = _glfw.cursors[:len(_glfw.cursors)-1]
break
}
}
return nil
}
func (w *Window) SetCursor(cursor *Cursor) error {
if !_glfw.initialized {
return NotInitialized
}
w.cursor = cursor
if err := w.platformSetCursor(cursor); err != nil {
return err
}
return nil
}
func (w *Window) SetKeyCallback(cbfun KeyCallback) (KeyCallback, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
old := w.callbacks.key
w.callbacks.key = cbfun
return old, nil
}
func (w *Window) SetCharCallback(cbfun CharCallback) (CharCallback, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
old := w.callbacks.character
w.callbacks.character = cbfun
return old, nil
}
func (w *Window) SetCharModsCallback(cbfun CharModsCallback) (CharModsCallback, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
old := w.callbacks.charmods
w.callbacks.charmods = cbfun
return old, nil
}
func (w *Window) SetMouseButtonCallback(cbfun MouseButtonCallback) (MouseButtonCallback, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
old := w.callbacks.mouseButton
w.callbacks.mouseButton = cbfun
return old, nil
}
func (w *Window) SetCursorPosCallback(cbfun CursorPosCallback) (CursorPosCallback, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
old := w.callbacks.cursorPos
w.callbacks.cursorPos = cbfun
return old, nil
}
func (w *Window) SetCursorEnterCallback(cbfun CursorEnterCallback) (CursorEnterCallback, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
old := w.callbacks.cursorEnter
w.callbacks.cursorEnter = cbfun
return old, nil
}
func (w *Window) SetScrollCallback(cbfun ScrollCallback) (ScrollCallback, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
old := w.callbacks.scroll
w.callbacks.scroll = cbfun
return old, nil
}
func (w *Window) SetDropCallback(cbfun DropCallback) (DropCallback, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
old := w.callbacks.drop
w.callbacks.drop = cbfun
return old, nil
}
func (w *Window) SetClipboardString(str string) error {
if !_glfw.initialized {
return NotInitialized
}
return platformSetClipboardString(str)
}
func GetClipboardString() (string, error) {
if !_glfw.initialized {
return "", NotInitialized
}
return platformGetClipboardString()
}

View File

@ -0,0 +1,328 @@
// Copyright 2002-2006 Marcus Geelnard
// Copyright 2006-2019 Camilla Löwy
// Copyright 2022 The Ebiten Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
package glfwwin
import (
"unsafe"
"golang.org/x/sys/windows"
)
const (
_GLFW_INSERT_FIRST = 0
_GLFW_INSERT_LAST = 1
)
var _glfw library
type initconfig struct {
hatButtons bool
}
type wndconfig struct {
width int
height int
title string
resizable bool
visible bool
decorated bool
focused bool
autoIconify bool
floating bool
maximized bool
centerCursor bool
focusOnShow bool
scaleToMonitor bool
}
type ctxconfig struct {
client int
source int
major int
minor int
forward bool
debug bool
noerror bool
profile int
robustness int
release int
share *Window
}
type fbconfig struct {
redBits int
greenBits int
blueBits int
alphaBits int
depthBits int
stencilBits int
accumRedBits int
accumGreenBits int
accumBlueBits int
accumAlphaBits int
auxBuffers int
stereo bool
samples int
sRGB bool
doublebuffer bool
transparent bool
handle uintptr
}
type context struct {
client int
source int
major int
minor int
revision int
forward bool
debug bool
noerror bool
profile int
robustness int
release int
// TODO: Put these functions in an interface type.
makeCurrent func(*Window) error
swapBuffers func(*Window) error
swapInterval func(int) error
extensionSupported func(string) bool
getProcAddress func(string) uintptr
destroy func(*Window) error
wgl struct {
dc _HDC
handle _HGLRC
interval int
}
}
type (
PosCallback func(w *Window, xpos int, ypos int)
SizeCallback func(w *Window, width int, height int)
CloseCallback func(w *Window)
RefreshCallback func(w *Window)
FocusCallback func(w *Window, focused bool)
IconifyCallback func(w *Window, iconified bool)
MaximizeCallback func(w *Window, iconified bool)
FramebufferSizeCallback func(w *Window, width int, height int)
ContentScaleCallback func(w *Window, x float32, y float32)
MouseButtonCallback func(w *Window, button MouseButton, action Action, mods ModifierKey)
CursorPosCallback func(w *Window, xpos float64, ypos float64)
CursorEnterCallback func(w *Window, entered bool)
ScrollCallback func(w *Window, xoff float64, yoff float64)
KeyCallback func(w *Window, key Key, scancode int, action Action, mods ModifierKey)
CharCallback func(w *Window, char rune)
CharModsCallback func(w *Window, char rune, mods ModifierKey)
DropCallback func(w *Window, names []string)
MonitorCallback func(monitor *Monitor, event PeripheralEvent)
)
type Window struct {
resizable bool
decorated bool
autoIconify bool
floating bool
focusOnShow bool
shouldClose bool
userPointer unsafe.Pointer
doublebuffer bool
videoMode VidMode
monitor *Monitor
cursor *Cursor
minwidth int
minheight int
maxwidth int
maxheight int
numer int
denom int
stickyKeys bool
stickyMouseButtons bool
lockKeyMods bool
cursorMode int
mouseButtons [MouseButtonLast + 1]Action
keys [KeyLast + 1]Action
// Virtual cursor position when cursor is disabled
virtualCursorPosX float64
virtualCursorPosY float64
rawMouseMotion bool
context context
callbacks struct {
pos PosCallback
size SizeCallback
close CloseCallback
refresh RefreshCallback
focus FocusCallback
iconify IconifyCallback
maximize MaximizeCallback
fbsize FramebufferSizeCallback
scale ContentScaleCallback
mouseButton MouseButtonCallback
cursorPos CursorPosCallback
cursorEnter CursorEnterCallback
scroll ScrollCallback
key KeyCallback
character CharCallback
charmods CharModsCallback
drop DropCallback
}
win32 struct {
handle windows.HWND
bigIcon _HICON
smallIcon _HICON
cursorTracked bool
frameAction bool
iconified bool
maximized bool
transparent bool // Whether to enable framebuffer transparency on DWM
scaleToMonitor bool
// Cached size used to filter out duplicate events
width int
height int
// The last received cursor position, regardless of source
lastCursorPosX int
lastCursorPosY int
// The last recevied high surrogate when decoding pairs of UTF-16 messages
highSurrogate uint16
}
}
type Monitor struct {
name string
widthMM int
heightMM int
window *Window
modes []*VidMode
currentMode *VidMode
originalRamp GammaRamp
currentRamp GammaRamp
win32 struct {
handle _HMONITOR
// This size matches the static size of DISPLAY_DEVICE.DeviceName
adapterName string
displayName string
modesPruned bool
modeChanged bool
}
}
type Cursor struct {
win32 struct {
handle _HCURSOR
}
}
type tls struct {
win32 struct {
allocated bool
index uint32
}
}
type library struct {
initialized bool
hints struct {
init initconfig
framebuffer fbconfig
window wndconfig
context ctxconfig
refreshRate int
}
errors []error // TODO: Check the error at polling?
cursors []*Cursor
windows []*Window
monitors []*Monitor
contextSlot tls
callbacks struct {
monitor MonitorCallback
}
win32 struct {
helperWindowHandle windows.HWND
deviceNotificationHandle _HDEVNOTIFY
foregroundLockTimeout uint32
acquiredMonitorCount int
clipboardString string
keycodes [512]Key
scancodes [KeyLast + 1]int
keynames [KeyLast + 1]string
// Where to place the cursor when re-enabled
restoreCursorPosX float64
restoreCursorPosY float64
// The window whose disabled cursor mode is active
disabledCursorWindow *Window
rawInput []byte
mouseTrailSize uint32
}
wgl struct {
inited bool
EXT_swap_control bool
EXT_colorspace bool
ARB_multisample bool
ARB_framebuffer_sRGB bool
EXT_framebuffer_sRGB bool
ARB_pixel_format bool
ARB_create_context bool
ARB_create_context_profile bool
EXT_create_context_es2_profile bool
ARB_create_context_robustness bool
ARB_create_context_no_error bool
ARB_context_flush_control bool
}
}
func boolToInt(x bool) int {
if x {
return 1
}
return 0
}
func intToBool(x int) bool {
return x != 0
}

View File

@ -0,0 +1,313 @@
// Copyright 2002-2006 Marcus Geelnard
// Copyright 2006-2019 Camilla Löwy
// Copyright 2022 The Ebiten Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
package glfwwin
import (
"sort"
"unsafe"
)
func abs(x int) uint {
if x < 0 {
return uint(-x)
}
return uint(x)
}
func (v *VidMode) equals(other *VidMode) bool {
if v.RedBits+v.GreenBits+v.BlueBits != other.RedBits+other.GreenBits+other.BlueBits {
return false
}
if v.Width != other.Width {
return false
}
if v.Height != other.Height {
return false
}
if v.RefreshRate != other.RefreshRate {
return false
}
return true
}
func (m *Monitor) refreshVideoModes() error {
m.modes = m.modes[:0]
modes, err := m.platformAppendVideoModes(m.modes)
if err != nil {
return err
}
sort.Slice(modes, func(i, j int) bool {
a := modes[i]
b := modes[j]
abpp := a.RedBits + a.GreenBits + a.BlueBits
bbpp := b.RedBits + b.GreenBits + b.BlueBits
if abpp != bbpp {
return abpp < bbpp
}
aarea := a.Width * a.Height
barea := b.Width * b.Height
if aarea != barea {
return aarea < barea
}
if a.Width != b.Width {
return a.Width < b.Width
}
return a.RefreshRate < b.RefreshRate
})
m.modes = modes
return nil
}
func inputMonitor(monitor *Monitor, action PeripheralEvent, placement int) error {
switch action {
case Connected:
switch placement {
case _GLFW_INSERT_FIRST:
_glfw.monitors = append(_glfw.monitors, nil)
copy(_glfw.monitors[1:], _glfw.monitors)
_glfw.monitors[0] = monitor
case _GLFW_INSERT_LAST:
_glfw.monitors = append(_glfw.monitors, monitor)
}
case Disconnected:
for _, window := range _glfw.windows {
if window.monitor == monitor {
width, height, err := window.platformGetWindowSize()
if err != nil {
return err
}
window.platformSetWindowMonitor(nil, 0, 0, width, height, 0)
xoff, yoff, _, _, err := window.platformGetWindowFrameSize()
if err != nil {
return err
}
window.platformSetWindowPos(xoff, yoff)
}
}
for i, m := range _glfw.monitors {
if m == monitor {
copy(_glfw.monitors[i:], _glfw.monitors[i+1:])
_glfw.monitors = _glfw.monitors[:len(_glfw.monitors)-1]
break
}
}
}
if _glfw.callbacks.monitor != nil {
_glfw.callbacks.monitor(monitor, action)
}
return nil
}
func (m *Monitor) inputMonitorWindow(window *Window) {
m.window = window
}
func (m *Monitor) chooseVideoMode(desired *VidMode) (*VidMode, error) {
if err := m.refreshVideoModes(); err != nil {
return nil, err
}
// math.MaxUint was added at Go 1.17. See https://github.com/golang/go/issues/28538
const (
intSize = 32 << (^uint(0) >> 63)
maxUint = 1<<intSize - 1
)
var (
leastColorDiff uint = maxUint
leastSizeDiff uint = maxUint
leastRateDiff uint = maxUint
)
var closest *VidMode
for _, v := range m.modes {
var colorDiff uint
if desired.RedBits != DontCare {
colorDiff += abs(v.RedBits - desired.RedBits)
}
if desired.GreenBits != DontCare {
colorDiff += abs(v.GreenBits - desired.GreenBits)
}
if desired.BlueBits != DontCare {
colorDiff += abs(v.BlueBits - desired.BlueBits)
}
sizeDiff := abs((v.Width-desired.Width)*(v.Width-desired.Width) +
(v.Height-desired.Height)*(v.Height-desired.Height))
var rateDiff uint
if desired.RefreshRate != DontCare {
rateDiff = abs(v.RefreshRate - desired.RefreshRate)
} else {
rateDiff = maxUint - uint(v.RefreshRate)
}
if colorDiff < leastColorDiff ||
colorDiff == leastColorDiff && sizeDiff < leastSizeDiff ||
colorDiff == leastColorDiff && sizeDiff == leastSizeDiff && rateDiff < leastRateDiff {
closest = v
leastColorDiff = colorDiff
leastSizeDiff = sizeDiff
leastRateDiff = rateDiff
}
}
return closest, nil
}
func splitBPP(bpp int) (red, green, blue int) {
// We assume that by 32 the user really meant 24
if bpp == 32 {
bpp = 24
}
// Convert "bits per pixel" to red, green & blue sizes
red = bpp / 3
green = bpp / 3
blue = bpp / 3
delta := bpp - (red * 3)
if delta >= 1 {
green++
}
if delta == 2 {
red++
}
return
}
// GLFW public APIs
func GetMonitors() ([]*Monitor, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
return _glfw.monitors, nil
}
func GetPrimaryMonitor() (*Monitor, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
if len(_glfw.monitors) == 0 {
return nil, nil
}
return _glfw.monitors[0], nil
}
func (m *Monitor) GetPos() (xpos, ypos int, err error) {
if !_glfw.initialized {
return 0, 0, NotInitialized
}
xpos, ypos, ok := m.platformGetMonitorPos()
if !ok {
return 0, 0, nil
}
return xpos, ypos, nil
}
func (m *Monitor) GetWorkarea() (xpos, ypos, width, height int, err error) {
if !_glfw.initialized {
return 0, 0, 0, 0, NotInitialized
}
xpos, ypos, width, height = m.platformGetMonitorWorkarea()
return
}
func (m *Monitor) GetPhysicalSize() (widthMM, heightMM int, err error) {
if !_glfw.initialized {
return 0, 0, NotInitialized
}
return m.widthMM, m.heightMM, nil
}
func (m *Monitor) GetContentScale() (xscale, yscale float32, err error) {
if !_glfw.initialized {
return 0, 0, NotInitialized
}
xscale, yscale, err = m.platformGetMonitorContentScale()
return
}
func (m *Monitor) GetName() (string, error) {
if !_glfw.initialized {
return "", NotInitialized
}
return m.name, nil
}
func (m *Monitor) SetUserPointer(pointer unsafe.Pointer) error {
if !_glfw.initialized {
return NotInitialized
}
panic("glfwwin: Monitor.SetUserPointer is not implemented")
}
func (m *Monitor) GetUserPointer() (unsafe.Pointer, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
panic("glfwwin: Monitor.GetUserPointer is not implemented")
}
func SetMonitorCallback(cbfun MonitorCallback) (MonitorCallback, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
old := _glfw.callbacks.monitor
_glfw.callbacks.monitor = cbfun
return old, nil
}
func (m *Monitor) GetVideoModes() ([]*VidMode, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
return m.modes, nil
}
func (m *Monitor) GetVideoMode() (*VidMode, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
m.currentMode = m.platformGetVideoMode()
return m.currentMode, nil
}
func (m *Monitor) SetGamma(gamma float32) {
panic("glfwwin: Monitor.SetGamme is not implemented")
}
func (m *Monitor) GetGammaRamp() *GammaRamp {
panic("glfwwin: Monitor.GetGammeRamp is not implemented")
}
func (m *Monitor) SetGammaRamp(ramp *GammaRamp) {
panic("glfwwin: Monitor.SetGammeRamp is not implemented")
}

View File

@ -0,0 +1,573 @@
// Copyright 2002-2006 Marcus Geelnard
// Copyright 2006-2019 Camilla Löwy
// Copyright 2022 The Ebiten Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
package glfwwin
import (
"fmt"
"strings"
"unsafe"
)
func findPixelFormatAttribValue(attribs []int32, values []int32, attrib int32) int32 {
for i := range attribs {
if attribs[i] == attrib {
return values[i]
}
}
return 0
}
func (w *Window) choosePixelFormat(ctxconfig *ctxconfig, fbconfig_ *fbconfig) (int, error) {
var nativeCount int32
var attribs []int32
if _glfw.wgl.ARB_pixel_format {
var attrib int32 = _WGL_NUMBER_PIXEL_FORMATS_ARB
if err := wglGetPixelFormatAttribivARB(w.context.wgl.dc, 1, 0, 1, &attrib, &nativeCount); err != nil {
return 0, err
}
attribs = append(attribs,
_WGL_SUPPORT_OPENGL_ARB,
_WGL_DRAW_TO_WINDOW_ARB,
_WGL_PIXEL_TYPE_ARB,
_WGL_ACCELERATION_ARB,
_WGL_RED_BITS_ARB,
_WGL_RED_SHIFT_ARB,
_WGL_GREEN_BITS_ARB,
_WGL_GREEN_SHIFT_ARB,
_WGL_BLUE_BITS_ARB,
_WGL_BLUE_SHIFT_ARB,
_WGL_ALPHA_BITS_ARB,
_WGL_ALPHA_SHIFT_ARB,
_WGL_DEPTH_BITS_ARB,
_WGL_STENCIL_BITS_ARB,
_WGL_ACCUM_BITS_ARB,
_WGL_ACCUM_RED_BITS_ARB,
_WGL_ACCUM_GREEN_BITS_ARB,
_WGL_ACCUM_BLUE_BITS_ARB,
_WGL_ACCUM_ALPHA_BITS_ARB,
_WGL_AUX_BUFFERS_ARB,
_WGL_STEREO_ARB,
_WGL_DOUBLE_BUFFER_ARB)
if _glfw.wgl.ARB_multisample {
attribs = append(attribs, _WGL_SAMPLES_ARB)
}
if ctxconfig.client == OpenGLAPI {
if _glfw.wgl.ARB_framebuffer_sRGB || _glfw.wgl.EXT_framebuffer_sRGB {
attribs = append(attribs, _WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB)
}
} else {
if _glfw.wgl.EXT_colorspace {
attribs = append(attribs, _WGL_COLORSPACE_EXT)
}
}
} else {
c, err := _DescribePixelFormat(w.context.wgl.dc, 1, uint32(unsafe.Sizeof(_PIXELFORMATDESCRIPTOR{})), nil)
if err != nil {
return 0, err
}
nativeCount = c
}
usableConfigs := make([]*fbconfig, 0, nativeCount)
for i := int32(0); i < nativeCount; i++ {
var u fbconfig
pixelFormat := uintptr(i) + 1
if _glfw.wgl.ARB_pixel_format {
// Get pixel format attributes through "modern" extension
values := make([]int32, len(attribs))
if err := wglGetPixelFormatAttribivARB(w.context.wgl.dc, int32(pixelFormat), 0, uint32(len(attribs)), &attribs[0], &values[0]); err != nil {
return 0, err
}
findAttribValue := func(attrib int32) int32 {
return findPixelFormatAttribValue(attribs, values, attrib)
}
if findAttribValue(_WGL_SUPPORT_OPENGL_ARB) == 0 || findAttribValue(_WGL_DRAW_TO_WINDOW_ARB) == 0 {
continue
}
if findAttribValue(_WGL_PIXEL_TYPE_ARB) != _WGL_TYPE_RGBA_ARB {
continue
}
if findAttribValue(_WGL_ACCELERATION_ARB) == _WGL_NO_ACCELERATION_ARB {
continue
}
if (findAttribValue(_WGL_DOUBLE_BUFFER_ARB) != 0) != fbconfig_.doublebuffer {
continue
}
u.redBits = int(findAttribValue(_WGL_RED_BITS_ARB))
u.greenBits = int(findAttribValue(_WGL_GREEN_BITS_ARB))
u.blueBits = int(findAttribValue(_WGL_BLUE_BITS_ARB))
u.alphaBits = int(findAttribValue(_WGL_ALPHA_BITS_ARB))
u.depthBits = int(findAttribValue(_WGL_DEPTH_BITS_ARB))
u.stencilBits = int(findAttribValue(_WGL_STENCIL_BITS_ARB))
u.accumRedBits = int(findAttribValue(_WGL_ACCUM_RED_BITS_ARB))
u.accumGreenBits = int(findAttribValue(_WGL_ACCUM_GREEN_BITS_ARB))
u.accumBlueBits = int(findAttribValue(_WGL_ACCUM_BLUE_BITS_ARB))
u.accumAlphaBits = int(findAttribValue(_WGL_ACCUM_ALPHA_BITS_ARB))
u.auxBuffers = int(findAttribValue(_WGL_AUX_BUFFERS_ARB))
if findAttribValue(_WGL_STEREO_ARB) != 0 {
u.stereo = true
}
if _glfw.wgl.ARB_multisample {
u.samples = int(findAttribValue(_WGL_SAMPLES_ARB))
}
if ctxconfig.client == OpenGLAPI {
if _glfw.wgl.ARB_framebuffer_sRGB || _glfw.wgl.EXT_framebuffer_sRGB {
if findAttribValue(_WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB) != 0 {
u.sRGB = true
}
}
} else {
if _glfw.wgl.EXT_colorspace {
if findAttribValue(_WGL_COLORSPACE_EXT) == _WGL_COLORSPACE_SRGB_EXT {
u.sRGB = true
}
}
}
} else {
// Get pixel format attributes through legacy PFDs
var pfd _PIXELFORMATDESCRIPTOR
if _, err := _DescribePixelFormat(w.context.wgl.dc, int32(pixelFormat), uint32(unsafe.Sizeof(pfd)), &pfd); err != nil {
return 0, err
}
if pfd.dwFlags&_PFD_DRAW_TO_WINDOW == 0 || pfd.dwFlags&_PFD_SUPPORT_OPENGL == 0 {
continue
}
if pfd.dwFlags&_PFD_GENERIC_ACCELERATED == 0 && pfd.dwFlags&_PFD_GENERIC_FORMAT != 0 {
continue
}
if pfd.iPixelType != _PFD_TYPE_RGBA {
continue
}
if (pfd.dwFlags&_PFD_DOUBLEBUFFER != 0) != fbconfig_.doublebuffer {
continue
}
u.redBits = int(pfd.cRedBits)
u.greenBits = int(pfd.cGreenBits)
u.blueBits = int(pfd.cBlueBits)
u.alphaBits = int(pfd.cAlphaBits)
u.depthBits = int(pfd.cDepthBits)
u.stencilBits = int(pfd.cStencilBits)
u.accumRedBits = int(pfd.cAccumRedBits)
u.accumGreenBits = int(pfd.cAccumGreenBits)
u.accumBlueBits = int(pfd.cAccumBlueBits)
u.accumAlphaBits = int(pfd.cAccumAlphaBits)
u.auxBuffers = int(pfd.cAuxBuffers)
if pfd.dwFlags&_PFD_STEREO != 0 {
u.stereo = true
}
}
u.handle = pixelFormat
usableConfigs = append(usableConfigs, &u)
}
if len(usableConfigs) == 0 {
return 0, fmt.Errorf("glfwwin: the driver does not appear to support OpenGL")
}
closest := chooseFBConfig(fbconfig_, usableConfigs)
if closest == nil {
return 0, fmt.Errorf("glfwwin: failed to find a suitable pixel format")
}
return int(closest.handle), nil
}
func makeContextCurrentWGL(window *Window) error {
if window != nil {
if err := wglMakeCurrent(window.context.wgl.dc, window.context.wgl.handle); err != nil {
_glfw.contextSlot.set(0)
return err
}
if err := _glfw.contextSlot.set(uintptr(unsafe.Pointer(window))); err != nil {
return err
}
} else {
if err := wglMakeCurrent(0, 0); err != nil {
_glfw.contextSlot.set(0)
return err
}
if err := _glfw.contextSlot.set(0); err != nil {
return err
}
}
return nil
}
func swapBuffersWGL(window *Window) error {
if window.monitor == nil && _IsWindowsVistaOrGreater() {
// DWM Composition is always enabled on Win8+
enabled := _IsWindows8OrGreater()
if !enabled {
var err error
enabled, err = _DwmIsCompositionEnabled()
if err != nil {
return err
}
}
// HACK: Use DwmFlush when desktop composition is enabled
if enabled {
for i := 0; i < window.context.wgl.interval; i++ {
if err := _DwmFlush(); err != nil {
return err
}
}
}
}
if err := _SwapBuffers(window.context.wgl.dc); err != nil {
return err
}
return nil
}
func swapIntervalWGL(interval int) error {
ptr, err := _glfw.contextSlot.get()
if err != nil {
return err
}
window := (*Window)(unsafe.Pointer(ptr))
window.context.wgl.interval = interval
if window.monitor == nil && _IsWindowsVistaOrGreater() {
// DWM Composition is always enabled on Win8+
enabled := _IsWindows8OrGreater()
if !enabled {
var err error
enabled, err = _DwmIsCompositionEnabled()
if err != nil {
return err
}
}
// HACK: Disable WGL swap interval when desktop composition is enabled to
// avoid interfering with DWM vsync
if enabled {
interval = 0
}
}
if _glfw.wgl.EXT_swap_control {
if err := wglSwapIntervalEXT(int32(interval)); err != nil {
return err
}
}
return nil
}
func extensionSupportedWGL(extension string) bool {
var extensions string
if wglGetExtensionsStringARB_Available() {
extensions = wglGetExtensionsStringARB(wglGetCurrentDC())
} else if wglGetExtensionsStringEXT_Available() {
extensions = wglGetExtensionsStringEXT()
}
if len(extensions) == 0 {
return false
}
for _, str := range strings.Split(extensions, " ") {
if extension == str {
return true
}
}
return false
}
func getProcAddressWGL(procname string) uintptr {
proc := wglGetProcAddress(procname)
if proc != 0 {
return proc
}
return opengl32.NewProc(procname).Addr()
}
func destroyContextWGL(window *Window) error {
if window.context.wgl.handle != 0 {
if err := wglDeleteContext(window.context.wgl.handle); err != nil {
return err
}
window.context.wgl.handle = 0
}
return nil
}
func initWGL() error {
if _glfw.wgl.inited {
return nil
}
// opengl32.dll must be loaded first. The loading state might affect Windows APIs.
// This is needed at least before SetPixelFormat.
if err := opengl32.Load(); err != nil {
return err
}
// NOTE: A dummy context has to be created for opengl32.dll to load the
// OpenGL ICD, from which we can then query WGL extensions
// NOTE: This code will accept the Microsoft GDI ICD; accelerated context
// creation failure occurs during manual pixel format enumeration
dc, err := _GetDC(_glfw.win32.helperWindowHandle)
if err != nil {
return err
}
pfd := _PIXELFORMATDESCRIPTOR{
nVersion: 1,
dwFlags: _PFD_DRAW_TO_WINDOW | _PFD_SUPPORT_OPENGL | _PFD_DOUBLEBUFFER,
iPixelType: _PFD_TYPE_RGBA,
cColorBits: 24,
}
pfd.nSize = uint16(unsafe.Sizeof(pfd))
format, err := _ChoosePixelFormat(dc, &pfd)
if err != nil {
return err
}
if err := _SetPixelFormat(dc, format, &pfd); err != nil {
return err
}
rc, err := wglCreateContext(dc)
if err != nil {
return err
}
pdc := wglGetCurrentDC()
prc := wglGetCurrentContext()
if err := wglMakeCurrent(dc, rc); err != nil {
wglMakeCurrent(pdc, prc)
wglDeleteContext(rc)
return err
}
// NOTE: WGL_ARB_extensions_string and WGL_EXT_extensions_string are not
// checked below as we are already using them
_glfw.wgl.ARB_multisample = extensionSupportedWGL("WGL_ARB_multisample")
_glfw.wgl.ARB_framebuffer_sRGB = extensionSupportedWGL("WGL_ARB_framebuffer_sRGB")
_glfw.wgl.EXT_framebuffer_sRGB = extensionSupportedWGL("WGL_EXT_framebuffer_sRGB")
_glfw.wgl.ARB_create_context = extensionSupportedWGL("WGL_ARB_create_context")
_glfw.wgl.ARB_create_context_profile = extensionSupportedWGL("WGL_ARB_create_context_profile")
_glfw.wgl.EXT_create_context_es2_profile = extensionSupportedWGL("WGL_EXT_create_context_es2_profile")
_glfw.wgl.ARB_create_context_robustness = extensionSupportedWGL("WGL_ARB_create_context_robustness")
_glfw.wgl.ARB_create_context_no_error = extensionSupportedWGL("WGL_ARB_create_context_no_error")
_glfw.wgl.EXT_swap_control = extensionSupportedWGL("WGL_EXT_swap_control")
_glfw.wgl.EXT_colorspace = extensionSupportedWGL("WGL_EXT_colorspace")
_glfw.wgl.ARB_pixel_format = extensionSupportedWGL("WGL_ARB_pixel_format")
_glfw.wgl.ARB_context_flush_control = extensionSupportedWGL("WGL_ARB_context_flush_control")
if err := wglMakeCurrent(pdc, prc); err != nil {
return err
}
if err := wglDeleteContext(rc); err != nil {
return err
}
_glfw.wgl.inited = true
return nil
}
func terminateWGL() {
}
func (w *Window) createContextWGL(ctxconfig *ctxconfig, fbconfig *fbconfig) error {
var share _HGLRC
if ctxconfig.share != nil {
share = ctxconfig.share.context.wgl.handle
}
dc, err := _GetDC(w.win32.handle)
if err != nil {
return err
}
w.context.wgl.dc = dc
pixelFormat, err := w.choosePixelFormat(ctxconfig, fbconfig)
if err != nil {
return err
}
var pfd _PIXELFORMATDESCRIPTOR
if _, err := _DescribePixelFormat(w.context.wgl.dc, int32(pixelFormat), uint32(unsafe.Sizeof(pfd)), &pfd); err != nil {
return err
}
if err := _SetPixelFormat(w.context.wgl.dc, int32(pixelFormat), &pfd); err != nil {
return err
}
if ctxconfig.client == OpenGLAPI {
if ctxconfig.forward && !_glfw.wgl.ARB_create_context {
return fmt.Errorf("glfwwin: a forward compatible OpenGL context requested but WGL_ARB_create_context is unavailable: %w", VersionUnavailable)
}
if ctxconfig.profile != 0 && !_glfw.wgl.ARB_create_context_profile {
return fmt.Errorf("glfwwin: OpenGL profile requested but WGL_ARB_create_context_profile is unavailable: %w", VersionUnavailable)
}
} else {
if !_glfw.wgl.ARB_create_context || !_glfw.wgl.ARB_create_context_profile || !_glfw.wgl.EXT_create_context_es2_profile {
return fmt.Errorf("glfwwin: OpenGL ES requested but WGL_ARB_create_context_es2_profile is unavailable: %w", ApiUnavailable)
}
}
if _glfw.wgl.ARB_create_context {
var flags int32
var mask int32
if ctxconfig.client == OpenGLAPI {
if ctxconfig.forward {
flags |= _WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB
}
if ctxconfig.profile == OpenGLCoreProfile {
mask |= _WGL_CONTEXT_CORE_PROFILE_BIT_ARB
} else if ctxconfig.profile == OpenGLCompatProfile {
mask |= _WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB
}
} else {
mask |= _WGL_CONTEXT_ES2_PROFILE_BIT_EXT
}
if ctxconfig.debug {
flags |= _WGL_CONTEXT_DEBUG_BIT_ARB
}
var attribs []int32
if ctxconfig.robustness != 0 {
if _glfw.wgl.ARB_create_context_robustness {
if ctxconfig.robustness == NoResetNotification {
attribs = append(attribs, _WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, _WGL_NO_RESET_NOTIFICATION_ARB)
} else if ctxconfig.robustness == LoseContextOnReset {
attribs = append(attribs, _WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, _WGL_LOSE_CONTEXT_ON_RESET_ARB)
}
flags |= _WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB
}
}
if ctxconfig.release != 0 {
if _glfw.wgl.ARB_context_flush_control {
if ctxconfig.release == ReleaseBehaviorNone {
attribs = append(attribs, _WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, _WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB)
} else if ctxconfig.release == ReleaseBehaviorFlush {
attribs = append(attribs, _WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, _WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB)
}
}
}
if ctxconfig.noerror {
if _glfw.wgl.ARB_create_context_no_error {
attribs = append(attribs, _WGL_CONTEXT_OPENGL_NO_ERROR_ARB, 1)
}
}
// NOTE: Only request an explicitly versioned context when necessary, as
// explicitly requesting version 1.0 does not always return the
// highest version supported by the driver
if ctxconfig.major != 1 || ctxconfig.minor != 0 {
attribs = append(attribs, _WGL_CONTEXT_MAJOR_VERSION_ARB, int32(ctxconfig.major))
attribs = append(attribs, _WGL_CONTEXT_MINOR_VERSION_ARB, int32(ctxconfig.minor))
}
if flags != 0 {
attribs = append(attribs, _WGL_CONTEXT_FLAGS_ARB, flags)
}
if mask != 0 {
attribs = append(attribs, _WGL_CONTEXT_PROFILE_MASK_ARB, mask)
}
attribs = append(attribs, 0, 0)
var err error
w.context.wgl.handle, err = wglCreateContextAttribsARB(w.context.wgl.dc, share, &attribs[0])
if err != nil {
return err
}
} else {
var err error
w.context.wgl.handle, err = wglCreateContext(w.context.wgl.dc)
if err != nil {
return err
}
if share != 0 {
if err := wglShareLists(share, w.context.wgl.handle); err != nil {
return err
}
}
}
w.context.makeCurrent = makeContextCurrentWGL
w.context.swapBuffers = swapBuffersWGL
w.context.swapInterval = swapIntervalWGL
w.context.extensionSupported = extensionSupportedWGL
w.context.getProcAddress = getProcAddressWGL
w.context.destroy = destroyContextWGL
return nil
}
func getWGLContext(handle *Window) _HGLRC {
window := handle
if !_glfw.initialized {
panic(NotInitialized)
}
if window.context.source != NativeContextAPI {
// TODO: Should this return an error?
return 0
}
return window.context.wgl.handle
}

View File

@ -0,0 +1,344 @@
// Copyright 2002-2006 Marcus Geelnard
// Copyright 2006-2019 Camilla Löwy
// Copyright 2022 The Ebiten Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
package glfwwin
import (
"errors"
"unsafe"
"golang.org/x/sys/windows"
)
func createKeyTables() {
for i := range _glfw.win32.keycodes {
_glfw.win32.keycodes[i] = -1
}
for i := range _glfw.win32.scancodes {
_glfw.win32.keycodes[i] = -1
}
_glfw.win32.keycodes[0x00B] = Key0
_glfw.win32.keycodes[0x002] = Key1
_glfw.win32.keycodes[0x003] = Key2
_glfw.win32.keycodes[0x004] = Key3
_glfw.win32.keycodes[0x005] = Key4
_glfw.win32.keycodes[0x006] = Key5
_glfw.win32.keycodes[0x007] = Key6
_glfw.win32.keycodes[0x008] = Key7
_glfw.win32.keycodes[0x009] = Key8
_glfw.win32.keycodes[0x00A] = Key9
_glfw.win32.keycodes[0x01E] = KeyA
_glfw.win32.keycodes[0x030] = KeyB
_glfw.win32.keycodes[0x02E] = KeyC
_glfw.win32.keycodes[0x020] = KeyD
_glfw.win32.keycodes[0x012] = KeyE
_glfw.win32.keycodes[0x021] = KeyF
_glfw.win32.keycodes[0x022] = KeyG
_glfw.win32.keycodes[0x023] = KeyH
_glfw.win32.keycodes[0x017] = KeyI
_glfw.win32.keycodes[0x024] = KeyJ
_glfw.win32.keycodes[0x025] = KeyK
_glfw.win32.keycodes[0x026] = KeyL
_glfw.win32.keycodes[0x032] = KeyM
_glfw.win32.keycodes[0x031] = KeyN
_glfw.win32.keycodes[0x018] = KeyO
_glfw.win32.keycodes[0x019] = KeyP
_glfw.win32.keycodes[0x010] = KeyQ
_glfw.win32.keycodes[0x013] = KeyR
_glfw.win32.keycodes[0x01F] = KeyS
_glfw.win32.keycodes[0x014] = KeyT
_glfw.win32.keycodes[0x016] = KeyU
_glfw.win32.keycodes[0x02F] = KeyV
_glfw.win32.keycodes[0x011] = KeyW
_glfw.win32.keycodes[0x02D] = KeyX
_glfw.win32.keycodes[0x015] = KeyY
_glfw.win32.keycodes[0x02C] = KeyZ
_glfw.win32.keycodes[0x028] = KeyApostrophe
_glfw.win32.keycodes[0x02B] = KeyBackslash
_glfw.win32.keycodes[0x033] = KeyComma
_glfw.win32.keycodes[0x00D] = KeyEqual
_glfw.win32.keycodes[0x029] = KeyGraveAccent
_glfw.win32.keycodes[0x01A] = KeyLeftBracket
_glfw.win32.keycodes[0x00C] = KeyMinus
_glfw.win32.keycodes[0x034] = KeyPeriod
_glfw.win32.keycodes[0x01B] = KeyRightBracket
_glfw.win32.keycodes[0x027] = KeySemicolon
_glfw.win32.keycodes[0x035] = KeySlash
_glfw.win32.keycodes[0x056] = KeyWorld2
_glfw.win32.keycodes[0x00E] = KeyBackspace
_glfw.win32.keycodes[0x153] = KeyDelete
_glfw.win32.keycodes[0x14F] = KeyEnd
_glfw.win32.keycodes[0x01C] = KeyEnter
_glfw.win32.keycodes[0x001] = KeyEscape
_glfw.win32.keycodes[0x147] = KeyHome
_glfw.win32.keycodes[0x152] = KeyInsert
_glfw.win32.keycodes[0x15D] = KeyMenu
_glfw.win32.keycodes[0x151] = KeyPageDown
_glfw.win32.keycodes[0x149] = KeyPageUp
_glfw.win32.keycodes[0x045] = KeyPause
_glfw.win32.keycodes[0x146] = KeyPause
_glfw.win32.keycodes[0x039] = KeySpace
_glfw.win32.keycodes[0x00F] = KeyTab
_glfw.win32.keycodes[0x03A] = KeyCapsLock
_glfw.win32.keycodes[0x145] = KeyNumLock
_glfw.win32.keycodes[0x046] = KeyScrollLock
_glfw.win32.keycodes[0x03B] = KeyF1
_glfw.win32.keycodes[0x03C] = KeyF2
_glfw.win32.keycodes[0x03D] = KeyF3
_glfw.win32.keycodes[0x03E] = KeyF4
_glfw.win32.keycodes[0x03F] = KeyF5
_glfw.win32.keycodes[0x040] = KeyF6
_glfw.win32.keycodes[0x041] = KeyF7
_glfw.win32.keycodes[0x042] = KeyF8
_glfw.win32.keycodes[0x043] = KeyF9
_glfw.win32.keycodes[0x044] = KeyF10
_glfw.win32.keycodes[0x057] = KeyF11
_glfw.win32.keycodes[0x058] = KeyF12
_glfw.win32.keycodes[0x064] = KeyF13
_glfw.win32.keycodes[0x065] = KeyF14
_glfw.win32.keycodes[0x066] = KeyF15
_glfw.win32.keycodes[0x067] = KeyF16
_glfw.win32.keycodes[0x068] = KeyF17
_glfw.win32.keycodes[0x069] = KeyF18
_glfw.win32.keycodes[0x06A] = KeyF19
_glfw.win32.keycodes[0x06B] = KeyF20
_glfw.win32.keycodes[0x06C] = KeyF21
_glfw.win32.keycodes[0x06D] = KeyF22
_glfw.win32.keycodes[0x06E] = KeyF23
_glfw.win32.keycodes[0x076] = KeyF24
_glfw.win32.keycodes[0x038] = KeyLeftAlt
_glfw.win32.keycodes[0x01D] = KeyLeftControl
_glfw.win32.keycodes[0x02A] = KeyLeftShift
_glfw.win32.keycodes[0x15B] = KeyLeftSuper
_glfw.win32.keycodes[0x137] = KeyPrintScreen
_glfw.win32.keycodes[0x138] = KeyRightAlt
_glfw.win32.keycodes[0x11D] = KeyRightControl
_glfw.win32.keycodes[0x036] = KeyRightShift
_glfw.win32.keycodes[0x15C] = KeyRightSuper
_glfw.win32.keycodes[0x150] = KeyDown
_glfw.win32.keycodes[0x14B] = KeyLeft
_glfw.win32.keycodes[0x14D] = KeyRight
_glfw.win32.keycodes[0x148] = KeyUp
_glfw.win32.keycodes[0x052] = KeyKP0
_glfw.win32.keycodes[0x04F] = KeyKP1
_glfw.win32.keycodes[0x050] = KeyKP2
_glfw.win32.keycodes[0x051] = KeyKP3
_glfw.win32.keycodes[0x04B] = KeyKP4
_glfw.win32.keycodes[0x04C] = KeyKP5
_glfw.win32.keycodes[0x04D] = KeyKP6
_glfw.win32.keycodes[0x047] = KeyKP7
_glfw.win32.keycodes[0x048] = KeyKP8
_glfw.win32.keycodes[0x049] = KeyKP9
_glfw.win32.keycodes[0x04E] = KeyKPAdd
_glfw.win32.keycodes[0x053] = KeyKPDecimal
_glfw.win32.keycodes[0x135] = KeyKPDivide
_glfw.win32.keycodes[0x11C] = KeyKPEnter
_glfw.win32.keycodes[0x059] = KeyKPEqual
_glfw.win32.keycodes[0x037] = KeyKPMultiply
_glfw.win32.keycodes[0x04A] = KeyKPSubtract
for scancode := 0; scancode < 512; scancode++ {
if _glfw.win32.keycodes[scancode] > 0 {
_glfw.win32.scancodes[_glfw.win32.keycodes[scancode]] = scancode
}
}
}
func createHelperWindow() error {
m, err := _GetModuleHandleW("")
if err != nil {
return err
}
h, err := _CreateWindowExW(_WS_EX_OVERLAPPEDWINDOW, _GLFW_WNDCLASSNAME, "GLFW message window", _WS_CLIPSIBLINGS|_WS_CLIPCHILDREN, 0, 0, 1, 1, 0, 0, _HINSTANCE(m), nil)
if err != nil {
return err
}
_glfw.win32.helperWindowHandle = h
// HACK: The command to the first ShowWindow call is ignored if the parent
// process passed along a STARTUPINFO, so clear that with a no-op call
_ShowWindow(_glfw.win32.helperWindowHandle, _SW_HIDE)
_GUID_DEVINTERFACE_HID := windows.GUID{0x4d1e55b2, 0xf16f, 0x11cf, [...]byte{0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30}}
// Register for HID device notifications
var dbi _DEV_BROADCAST_DEVICEINTERFACE_W
dbi.dbcc_size = uint32(unsafe.Sizeof(dbi))
dbi.dbcc_devicetype = _DBT_DEVTYP_DEVICEINTERFACE
dbi.dbcc_classguid = _GUID_DEVINTERFACE_HID
notify, err := _RegisterDeviceNotificationW(windows.Handle(_glfw.win32.helperWindowHandle), unsafe.Pointer(&dbi), _DEVICE_NOTIFY_WINDOW_HANDLE)
if err != nil {
return err
}
_glfw.win32.deviceNotificationHandle = notify
var msg _MSG
for _PeekMessageW(&msg, _glfw.win32.helperWindowHandle, 0, 0, _PM_REMOVE) {
_TranslateMessage(&msg)
_DispatchMessageW(&msg)
}
return nil
}
func updateKeyNamesWin32() {
var state [256]byte
for key := KeySpace; key <= KeyLast; key++ {
scancode := _glfw.win32.scancodes[key]
if scancode == -1 {
continue
}
var vk uint32
if key >= KeyKP0 && key <= KeyKPAdd {
vks := []uint32{
_VK_NUMPAD0, _VK_NUMPAD1, _VK_NUMPAD2, _VK_NUMPAD3,
_VK_NUMPAD4, _VK_NUMPAD5, _VK_NUMPAD6, _VK_NUMPAD7,
_VK_NUMPAD8, _VK_NUMPAD9, _VK_DECIMAL, _VK_DIVIDE,
_VK_MULTIPLY, _VK_SUBTRACT, _VK_ADD,
}
vk = vks[key-KeyKP0]
} else {
vk = _MapVirtualKeyW(uint32(scancode), _MAPVK_VSC_TO_VK)
}
var chars [16]uint16
length := _ToUnicode(vk, uint32(scancode), &state[0], &chars[0], int32(len(chars)), 0)
if length == -1 {
// TODO: Why is ToUnicode called twice?
length = _ToUnicode(vk, uint32(scancode), &state[0], &chars[0], int32(len(chars)), 0)
}
if length < 1 {
continue
}
_glfw.win32.keynames[key] = windows.UTF16ToString(chars[:1])
}
}
func isWindowsVersionOrGreaterWin32(major, minor, sp uint16) bool {
osvi := _OSVERSIONINFOEXW{
dwMajorVersion: uint32(major),
dwMinorVersion: uint32(minor),
wServicePackMajor: sp,
}
osvi.dwOSVersionInfoSize = uint32(unsafe.Sizeof(osvi))
var mask uint32 = _VER_MAJORVERSION | _VER_MINORVERSION | _VER_SERVICEPACKMAJOR
cond := _VerSetConditionMask(0, _VER_MAJORVERSION, _VER_GREATER_EQUAL)
cond = _VerSetConditionMask(cond, _VER_MINORVERSION, _VER_GREATER_EQUAL)
cond = _VerSetConditionMask(cond, _VER_SERVICEPACKMAJOR, _VER_GREATER_EQUAL)
// HACK: Use RtlVerifyVersionInfo instead of VerifyVersionInfoW as the
// latter lies unless the user knew to embed a non-default manifest
// announcing support for Windows 10 via supportedOS GUID
return _RtlVerifyVersionInfo(&osvi, mask, cond) == 0
}
func isWindows10BuildOrGreaterWin32(build uint16) bool {
osvi := _OSVERSIONINFOEXW{
dwMajorVersion: 10,
dwMinorVersion: 0,
dwBuildNumber: uint32(build),
}
osvi.dwOSVersionInfoSize = uint32(unsafe.Sizeof(osvi))
var mask uint32 = _VER_MAJORVERSION | _VER_MINORVERSION | _VER_BUILDNUMBER
cond := _VerSetConditionMask(0, _VER_MAJORVERSION, _VER_GREATER_EQUAL)
cond = _VerSetConditionMask(cond, _VER_MINORVERSION, _VER_GREATER_EQUAL)
cond = _VerSetConditionMask(cond, _VER_BUILDNUMBER, _VER_GREATER_EQUAL)
// HACK: Use RtlVerifyVersionInfo instead of VerifyVersionInfoW as the
// latter lies unless the user knew to embed a non-default manifest
// announcing support for Windows 10 via supportedOS GUID
return _RtlVerifyVersionInfo(&osvi, mask, cond) == 0
}
func platformInit() error {
// To make SetForegroundWindow work as we want, we need to fiddle
// with the FOREGROUNDLOCKTIMEOUT system setting (we do this as early
// as possible in the hope of still being the foreground process)
if err := _SystemParametersInfoW(_SPI_GETFOREGROUNDLOCKTIMEOUT, 0, uintptr(unsafe.Pointer(&_glfw.win32.foregroundLockTimeout)), 0); err != nil {
return err
}
if err := _SystemParametersInfoW(_SPI_SETFOREGROUNDLOCKTIMEOUT, 0, 0, _SPIF_SENDCHANGE); err != nil {
return err
}
createKeyTables()
updateKeyNamesWin32()
if isWindows10CreatorsUpdateOrGreaterWin32() {
if err := _SetProcessDpiAwarenessContext(_DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); err != nil && !errors.Is(err, windows.ERROR_ACCESS_DENIED) {
return err
}
} else if _IsWindows8Point1OrGreater() {
if err := _SetProcessDpiAwareness(_PROCESS_PER_MONITOR_DPI_AWARE); err != nil && !errors.Is(err, windows.ERROR_ACCESS_DENIED) {
return err
}
} else if _IsWindowsVistaOrGreater() {
_SetProcessDPIAware()
}
if err := registerWindowClassWin32(); err != nil {
return err
}
if err := createHelperWindow(); err != nil {
return err
}
if err := pollMonitorsWin32(); err != nil {
return err
}
return nil
}
func platformTerminate() error {
if _glfw.win32.deviceNotificationHandle != 0 {
if err := _UnregisterDeviceNotification(_glfw.win32.deviceNotificationHandle); err != nil {
return err
}
}
if _glfw.win32.helperWindowHandle != 0 {
if err := _DestroyWindow(_glfw.win32.helperWindowHandle); err != nil {
return err
}
}
if err := unregisterWindowClassWin32(); err != nil {
return err
}
// Restore previous foreground lock timeout system setting
if err := _SystemParametersInfoW(_SPI_SETFOREGROUNDLOCKTIMEOUT, 0, uintptr(_glfw.win32.foregroundLockTimeout), _SPIF_SENDCHANGE); err != nil {
return err
}
terminateWGL()
return nil
}

View File

@ -0,0 +1,375 @@
// Copyright 2002-2006 Marcus Geelnard
// Copyright 2006-2019 Camilla Löwy
// Copyright 2022 The Ebiten Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
package glfwwin
import (
"errors"
"unsafe"
"golang.org/x/sys/windows"
)
func monitorCallback(handle _HMONITOR, dc _HDC, rect *_RECT, data _LPARAM) uintptr /* _BOOL */ {
if mi, ok := _GetMonitorInfoW_Ex(handle); ok {
monitor := (*Monitor)(unsafe.Pointer(data))
if windows.UTF16ToString(mi.szDevice[:]) == monitor.win32.adapterName {
monitor.win32.handle = handle
}
}
return 1
}
var monitorCallbackPtr = windows.NewCallbackCDecl(monitorCallback)
func createMonitor(adapter *_DISPLAY_DEVICEW, display *_DISPLAY_DEVICEW) (*Monitor, error) {
var name string
if display != nil {
name = windows.UTF16ToString(display.DeviceString[:])
} else {
name = windows.UTF16ToString(adapter.DeviceString[:])
}
if name == "" {
return nil, nil
}
adapterDeviceName := windows.UTF16ToString(adapter.DeviceName[:])
dm, ok := _EnumDisplaySettingsW(adapterDeviceName, _ENUM_CURRENT_SETTINGS)
if !ok {
return nil, nil
}
var widthMM, heightMM int
dc, err := _CreateDCW("DISPLAY", adapterDeviceName, "", nil)
if err != nil {
return nil, err
}
if _IsWindows8Point1OrGreater() {
widthMM = int(_GetDeviceCaps(dc, _HORZSIZE))
heightMM = int(_GetDeviceCaps(dc, _VERTSIZE))
} else {
widthMM = int(float64(dm.dmPelsWidth) * 25.4 / float64(_GetDeviceCaps(dc, _LOGPIXELSX)))
heightMM = int(float64(dm.dmPelsHeight) * 25.4 / float64(_GetDeviceCaps(dc, _LOGPIXELSY)))
}
if err := _DeleteDC(dc); err != nil {
return nil, err
}
monitor := &Monitor{
name: name,
widthMM: widthMM,
heightMM: heightMM,
}
if adapter.StateFlags&_DISPLAY_DEVICE_MODESPRUNED != 0 {
monitor.win32.modesPruned = true
}
monitor.win32.adapterName = adapterDeviceName
if display != nil {
monitor.win32.displayName = windows.UTF16ToString(display.DeviceName[:])
}
rect := _RECT{
left: dm.dmPosition.x,
top: dm.dmPosition.y,
right: dm.dmPosition.x + int32(dm.dmPelsWidth),
bottom: dm.dmPosition.y + int32(dm.dmPelsHeight),
}
if err := _EnumDisplayMonitors(0, &rect, monitorCallbackPtr, _LPARAM(unsafe.Pointer(monitor))); err != nil {
return nil, err
}
return monitor, nil
}
func pollMonitorsWin32() error {
disconnected := make([]*Monitor, len(_glfw.monitors))
copy(disconnected, _glfw.monitors)
adapterLoop:
for adapterIndex := uint32(0); ; adapterIndex++ {
adapter, ok := _EnumDisplayDevicesW("", adapterIndex, 0)
if !ok {
break
}
if adapter.StateFlags&_DISPLAY_DEVICE_ACTIVE == 0 {
continue
}
typ := _GLFW_INSERT_LAST
if adapter.StateFlags&_DISPLAY_DEVICE_PRIMARY_DEVICE != 0 {
typ = _GLFW_INSERT_FIRST
}
var found bool
displayLoop:
for displayIndex := uint32(0); ; displayIndex++ {
display, ok := _EnumDisplayDevicesW(windows.UTF16ToString(adapter.DeviceName[:]), displayIndex, 0)
if !ok {
break
}
found = true
if display.StateFlags&_DISPLAY_DEVICE_ACTIVE == 0 {
continue
}
for i, monitor := range disconnected {
if monitor != nil && monitor.win32.displayName == windows.UTF16ToString(display.DeviceName[:]) {
disconnected[i] = nil
_EnumDisplayMonitors(0, nil, monitorCallbackPtr, _LPARAM(unsafe.Pointer(_glfw.monitors[i])))
continue displayLoop
}
}
monitor, err := createMonitor(&adapter, &display)
if err != nil {
return err
}
if monitor == nil {
return nil
}
if err := inputMonitor(monitor, Connected, typ); err != nil {
return err
}
typ = _GLFW_INSERT_LAST
}
// HACK: If an active adapter does not have any display devices
// (as sometimes happens), add it directly as a monitor
if !found {
for i, monitor := range disconnected {
if monitor != nil && monitor.win32.displayName == windows.UTF16ToString(adapter.DeviceName[:]) {
disconnected[i] = nil
continue adapterLoop
}
}
monitor, err := createMonitor(&adapter, nil)
if err != nil {
return err
}
if monitor == nil {
return nil
}
if err := inputMonitor(monitor, Connected, typ); err != nil {
return err
}
}
}
for _, monitor := range disconnected {
if monitor != nil {
if err := inputMonitor(monitor, Disconnected, 0); err != nil {
return err
}
}
}
return nil
}
func (m *Monitor) setVideoModeWin32(desired *VidMode) error {
best, err := m.chooseVideoMode(desired)
if err != nil {
return err
}
current := m.platformGetVideoMode()
if best.equals(current) {
return nil
}
dm := _DEVMODEW{
dmFields: _DM_PELSWIDTH | _DM_PELSHEIGHT | _DM_BITSPERPEL | _DM_DISPLAYFREQUENCY,
dmPelsWidth: uint32(best.Width),
dmPelsHeight: uint32(best.Height),
dmBitsPerPel: uint32(best.RedBits + best.GreenBits + best.BlueBits),
dmDisplayFrequency: uint32(best.RefreshRate),
}
dm.dmSize = uint16(unsafe.Sizeof(dm))
if dm.dmBitsPerPel < 15 || dm.dmBitsPerPel >= 24 {
dm.dmBitsPerPel = 32
}
switch _ChangeDisplaySettingsExW(m.win32.adapterName, &dm, 0, _CDS_FULLSCREEN, nil) {
case _DISP_CHANGE_SUCCESSFUL:
m.win32.modeChanged = true
return nil
case _DISP_CHANGE_BADDUALVIEW:
return errors.New("glfwwin: the system uses DualView at Monitor.setVideoModeWin32")
case _DISP_CHANGE_BADFLAGS:
return errors.New("glfwwin: invalid flags at Monitor.setVideoModeWin32")
case _DISP_CHANGE_BADMODE:
return errors.New("glfwwin: graphics mode not supported at Monitor.setVideoModeWin32")
case _DISP_CHANGE_BADPARAM:
return errors.New("glfwwin: invalid parameter at Monitor.setVideoModeWin32")
case _DISP_CHANGE_FAILED:
return errors.New("glfwwin: graphics mode failed at Monitor.setVideoModeWin32")
case _DISP_CHANGE_NOTUPDATED:
return errors.New("glfwwin: failed to write to registry at Monitor.setVideoModeWin32")
case _DISP_CHANGE_RESTART:
return errors.New("glfwwin: computer restart required at Monitor.setVideoModeWin32")
default:
return errors.New("glfwwin: unknown error at Monitor.setVideoModeWin32")
}
}
func (m *Monitor) restoreVideoModeWin32() {
if m.win32.modeChanged {
_ChangeDisplaySettingsExW(m.win32.adapterName, nil, 0, _CDS_FULLSCREEN, nil)
m.win32.modeChanged = false
}
}
func getMonitorContentScaleWin32(handle _HMONITOR) (xscale, yscale float32, err error) {
var xdpi, ydpi uint32
if _IsWindows8Point1OrGreater() {
var err error
xdpi, ydpi, err = _GetDpiForMonitor(handle, _MDT_EFFECTIVE_DPI)
if err != nil {
return 0, 0, err
}
} else {
dc, err := _GetDC(0)
if err != nil {
return 0, 0, err
}
defer _ReleaseDC(0, dc)
xdpi = uint32(_GetDeviceCaps(dc, _LOGPIXELSX))
ydpi = uint32(_GetDeviceCaps(dc, _LOGPIXELSY))
}
xscale = float32(xdpi) / _USER_DEFAULT_SCREEN_DPI
yscale = float32(ydpi) / _USER_DEFAULT_SCREEN_DPI
return
}
func (m *Monitor) platformGetMonitorPos() (xpos, ypos int, ok bool) {
dm, ok := _EnumDisplaySettingsExW(m.win32.adapterName, _ENUM_CURRENT_SETTINGS, _EDS_ROTATEDMODE)
if !ok {
return 0, 0, false
}
return int(dm.dmPosition.x), int(dm.dmPosition.y), true
}
func (m *Monitor) platformGetMonitorContentScale() (xscale, yscale float32, err error) {
return getMonitorContentScaleWin32(m.win32.handle)
}
func (m *Monitor) platformGetMonitorWorkarea() (xpos, ypos, width, height int) {
mi, ok := _GetMonitorInfoW(m.win32.handle)
if !ok {
return 0, 0, 0, 0
}
xpos = int(mi.rcWork.left)
ypos = int(mi.rcWork.top)
width = int(mi.rcWork.right - mi.rcWork.left)
height = int(mi.rcWork.bottom - mi.rcWork.top)
return
}
func (m *Monitor) platformAppendVideoModes(monitors []*VidMode) ([]*VidMode, error) {
origLen := len(monitors)
loop:
for modeIndex := uint32(0); ; modeIndex++ {
dm, ok := _EnumDisplaySettingsW(m.win32.adapterName, modeIndex)
if !ok {
break
}
// Skip modes with less than 15 BPP
if dm.dmBitsPerPel < 15 {
continue
}
r, g, b := splitBPP(int(dm.dmBitsPerPel))
mode := &VidMode{
Width: int(dm.dmPelsWidth),
Height: int(dm.dmPelsHeight),
RefreshRate: int(dm.dmDisplayFrequency),
RedBits: r,
GreenBits: g,
BlueBits: b,
}
// Skip duplicate modes
for _, m := range monitors[origLen:] {
if m.equals(mode) {
continue loop
}
}
if m.win32.modesPruned {
// Skip modes not supported by the connected displays
if _ChangeDisplaySettingsExW(m.win32.adapterName, &dm, 0, _CDS_TEST, nil) != _DISP_CHANGE_SUCCESSFUL {
continue
}
}
monitors = append(monitors, mode)
}
if len(monitors) == origLen {
// HACK: Report the current mode if no valid modes were found
monitors = append(monitors, m.platformGetVideoMode())
}
return monitors, nil
}
func (m *Monitor) platformGetVideoMode() *VidMode {
dm, _ := _EnumDisplaySettingsW(m.win32.adapterName, _ENUM_CURRENT_SETTINGS)
r, g, b := splitBPP(int(dm.dmBitsPerPel))
return &VidMode{
Width: int(dm.dmPelsWidth),
Height: int(dm.dmPelsHeight),
RefreshRate: int(dm.dmDisplayFrequency),
RedBits: r,
GreenBits: g,
BlueBits: b,
}
}
func (m *Monitor) platformGetGammaRamp(ramp *GammaRamp) {
panic("glfwwin: platformGetGammaRamp is not implemented")
}
func (m *Monitor) platformSetGammaRamp(ramp *GammaRamp) {
panic("glfwwin: platformSetGammaRamp is not implemented")
}
func (m *Monitor) in32Adapter() (string, error) {
if !_glfw.initialized {
return "", NotInitialized
}
return m.win32.adapterName, nil
}
func (m *Monitor) win32Monitor() (string, error) {
if !_glfw.initialized {
return "", NotInitialized
}
return m.win32.displayName, nil
}

View File

@ -0,0 +1,56 @@
// Copyright 2002-2006 Marcus Geelnard
// Copyright 2006-2019 Camilla Löwy
// Copyright 2022 The Ebiten Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
package glfwwin
const (
_GLFW_WNDCLASSNAME = "GLFW30"
)
func _IsWindowsXPOrGreater() bool {
return isWindowsVersionOrGreaterWin32(uint16(_HIBYTE(_WIN32_WINNT_WINXP)), uint16(_LOBYTE(_WIN32_WINNT_WINXP)), 0)
}
func _IsWindowsVistaOrGreater() bool {
return isWindowsVersionOrGreaterWin32(uint16(_HIBYTE(_WIN32_WINNT_VISTA)), uint16(_LOBYTE(_WIN32_WINNT_VISTA)), 0)
}
func _IsWindows7OrGreater() bool {
return isWindowsVersionOrGreaterWin32(uint16(_HIBYTE(_WIN32_WINNT_WIN7)), uint16(_LOBYTE(_WIN32_WINNT_WIN7)), 0)
}
func _IsWindows8OrGreater() bool {
return isWindowsVersionOrGreaterWin32(uint16(_HIBYTE(_WIN32_WINNT_WIN8)), uint16(_LOBYTE(_WIN32_WINNT_WIN8)), 0)
}
func _IsWindows8Point1OrGreater() bool {
return isWindowsVersionOrGreaterWin32(uint16(_HIBYTE(_WIN32_WINNT_WINBLUE)), uint16(_LOBYTE(_WIN32_WINNT_WINBLUE)), 0)
}
func isWindows10AnniversaryUpdateOrGreaterWin32() bool {
return isWindows10BuildOrGreaterWin32(14393)
}
func isWindows10CreatorsUpdateOrGreaterWin32() bool {
return isWindows10BuildOrGreaterWin32(15063)
}

View File

@ -0,0 +1,65 @@
// Copyright 2002-2006 Marcus Geelnard
// Copyright 2006-2019 Camilla Löwy
// Copyright 2022 The Ebiten Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
package glfwwin
func (t *tls) create() error {
if t.win32.allocated {
panic("glfwwin: TLS must not be allocated")
}
i, err := _TlsAlloc()
if err != nil {
return err
}
t.win32.index = i
t.win32.allocated = true
return nil
}
func (t *tls) destroy() error {
if t.win32.allocated {
if err := _TlsFree(t.win32.index); err != nil {
return err
}
}
t.win32.allocated = false
t.win32.index = 0
return nil
}
func (t *tls) get() (uintptr, error) {
if !t.win32.allocated {
panic("glfwwin: TLS must be allocated")
}
return _TlsGetValue(t.win32.index)
}
func (t *tls) set(value uintptr) error {
if !t.win32.allocated {
panic("glfwwin: TLS must be allocated")
}
return _TlsSetValue(t.win32.index, value)
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,880 @@
// Copyright 2002-2006 Marcus Geelnard
// Copyright 2006-2019 Camilla Löwy
// Copyright 2022 The Ebiten Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
package glfwwin
import (
"fmt"
"math"
"unsafe"
)
func (w *Window) inputWindowFocus(focused bool) {
if w.callbacks.focus != nil {
w.callbacks.focus(w, focused)
}
if !focused {
for key := Key(0); key <= KeyLast; key++ {
if w.keys[key] == Press {
scancode := platformGetKeyScancode(key)
w.inputKey(key, scancode, Release, 0)
}
}
for button := MouseButton(0); button <= MouseButtonLast; button++ {
if w.mouseButtons[button] == Press {
w.inputMouseClick(button, Release, 0)
}
}
}
}
func (w *Window) inputWindowPos(x, y int) {
if w.callbacks.pos != nil {
w.callbacks.pos(w, x, y)
}
}
func (w *Window) inputWindowSize(width, height int) {
if w.callbacks.size != nil {
w.callbacks.size(w, width, height)
}
}
func (w *Window) inputWindowIconify(iconified bool) {
if w.callbacks.iconify != nil {
w.callbacks.iconify(w, iconified)
}
}
func (w *Window) inputWindowMaximize(maximized bool) {
if w.callbacks.maximize != nil {
w.callbacks.maximize(w, maximized)
}
}
func (w *Window) inputFramebufferSize(width, height int) {
if w.callbacks.fbsize != nil {
w.callbacks.fbsize(w, width, height)
}
}
func (w *Window) inputWindowContentScale(xscale, yscale float32) {
if w.callbacks.scale != nil {
w.callbacks.scale(w, xscale, yscale)
}
}
func (w *Window) inputWindowDamage() {
if w.callbacks.refresh != nil {
w.callbacks.refresh(w)
}
}
func (w *Window) inputWindowCloseRequest() {
w.shouldClose = true
if w.callbacks.close != nil {
w.callbacks.close(w)
}
}
func (w *Window) inputWindowMonitor(monitor *Monitor) {
w.monitor = monitor
}
func CreateWindow(width, height int, title string, monitor *Monitor, share *Window) (window *Window, ferr error) {
if !_glfw.initialized {
return nil, NotInitialized
}
if width <= 0 || height <= 0 {
return nil, fmt.Errorf("glfwwin: invalid window size %dx%d: %w", width, height, InvalidValue)
}
fbconfig := _glfw.hints.framebuffer
ctxconfig := _glfw.hints.context
wndconfig := _glfw.hints.window
wndconfig.width = width
wndconfig.height = height
wndconfig.title = title
ctxconfig.share = share
if err := checkValidContextConfig(&ctxconfig); err != nil {
return nil, err
}
window = &Window{
videoMode: VidMode{
Width: width,
Height: height,
RedBits: fbconfig.redBits,
GreenBits: fbconfig.greenBits,
BlueBits: fbconfig.blueBits,
RefreshRate: _glfw.hints.refreshRate,
},
monitor: monitor,
resizable: wndconfig.resizable,
decorated: wndconfig.decorated,
autoIconify: wndconfig.autoIconify,
floating: wndconfig.floating,
focusOnShow: wndconfig.focusOnShow,
cursorMode: CursorNormal,
doublebuffer: fbconfig.doublebuffer,
minwidth: DontCare,
minheight: DontCare,
maxwidth: DontCare,
maxheight: DontCare,
numer: DontCare,
denom: DontCare,
}
defer func() {
if ferr != nil {
window.Destroy()
}
}()
// Open the actual window and create its context
if err := window.platformCreateWindow(&wndconfig, &ctxconfig, &fbconfig); err != nil {
return nil, err
}
if ctxconfig.client != NoAPI {
if err := window.refreshContextAttribs(&ctxconfig); err != nil {
return nil, err
}
}
if window.monitor != nil {
if wndconfig.centerCursor {
if err := window.centerCursorInContentArea(); err != nil {
return nil, err
}
}
} else {
if wndconfig.visible {
window.platformShowWindow()
if wndconfig.focused {
if err := window.platformFocusWindow(); err != nil {
return nil, err
}
}
}
}
return window, nil
}
func defaultWindowHints() error {
if !_glfw.initialized {
return NotInitialized
}
// The default is OpenGL with minimum version 1.0
_glfw.hints.context = ctxconfig{
client: OpenGLAPI,
source: NativeContextAPI,
major: 1,
minor: 0,
}
// The default is a focused, visible, resizable window with decorations
_glfw.hints.window = wndconfig{
resizable: true,
visible: true,
decorated: true,
focused: true,
autoIconify: true,
centerCursor: true,
focusOnShow: true,
}
// The default is 24 bits of color, 24 bits of depth and 8 bits of stencil,
// double buffered
_glfw.hints.framebuffer = fbconfig{
redBits: 8,
greenBits: 8,
blueBits: 8,
alphaBits: 8,
depthBits: 24,
stencilBits: 8,
doublebuffer: true,
}
// The default is to select the highest available refresh rate
_glfw.hints.refreshRate = DontCare
return nil
}
func WindowHint(hint Hint, value int) error {
if !_glfw.initialized {
return NotInitialized
}
switch hint {
case RedBits:
_glfw.hints.framebuffer.redBits = value
case GreenBits:
_glfw.hints.framebuffer.greenBits = value
case BlueBits:
_glfw.hints.framebuffer.blueBits = value
case AlphaBits:
_glfw.hints.framebuffer.alphaBits = value
case DepthBits:
_glfw.hints.framebuffer.depthBits = value
case StencilBits:
_glfw.hints.framebuffer.stencilBits = value
case AccumRedBits:
_glfw.hints.framebuffer.accumRedBits = value
case AccumGreenBits:
_glfw.hints.framebuffer.accumGreenBits = value
case AccumBlueBits:
_glfw.hints.framebuffer.accumBlueBits = value
case AccumAlphaBits:
_glfw.hints.framebuffer.accumAlphaBits = value
case AuxBuffers:
_glfw.hints.framebuffer.auxBuffers = value
case Stereo:
_glfw.hints.framebuffer.stereo = intToBool(value)
case DoubleBuffer:
_glfw.hints.framebuffer.doublebuffer = intToBool(value)
case TransparentFramebuffer:
_glfw.hints.framebuffer.transparent = intToBool(value)
case Samples:
_glfw.hints.framebuffer.samples = value
case SRGBCapable:
_glfw.hints.framebuffer.sRGB = intToBool(value)
case Resizable:
_glfw.hints.window.resizable = intToBool(value)
case Decorated:
_glfw.hints.window.decorated = intToBool(value)
case Focused:
_glfw.hints.window.focused = intToBool(value)
case AutoIconify:
_glfw.hints.window.autoIconify = intToBool(value)
case Floating:
_glfw.hints.window.floating = intToBool(value)
case Maximized:
_glfw.hints.window.maximized = intToBool(value)
case Visible:
_glfw.hints.window.visible = intToBool(value)
case ScaleToMonitor:
_glfw.hints.window.scaleToMonitor = intToBool(value)
case CenterCursor:
_glfw.hints.window.centerCursor = intToBool(value)
case FocusOnShow:
_glfw.hints.window.focusOnShow = intToBool(value)
case ClientAPI:
_glfw.hints.context.client = value
case ContextCreationAPI:
_glfw.hints.context.source = value
case ContextVersionMajor:
_glfw.hints.context.major = value
case ContextVersionMinor:
_glfw.hints.context.minor = value
case ContextRobustness:
_glfw.hints.context.robustness = value
case OpenGLForwardCompat:
_glfw.hints.context.forward = intToBool(value)
case OpenGLDebugContext:
_glfw.hints.context.debug = intToBool(value)
case ContextNoError:
_glfw.hints.context.noerror = intToBool(value)
case OpenGLProfile:
_glfw.hints.context.profile = value
case ContextReleaseBehavior:
_glfw.hints.context.release = value
case RefreshRate:
_glfw.hints.refreshRate = value
default:
return fmt.Errorf("glfwwin: invalid window hint 0x%08X: %w", hint, InvalidEnum)
}
return nil
}
func WindowHintString(hint Hint, value string) {
panic("glfwwin: WindowHintString is not implemented")
}
func (w *Window) Destroy() error {
if !_glfw.initialized {
return NotInitialized
}
// Allow closing of NULL (to match the behavior of free)
if w == nil {
return nil
}
// Clear all callbacks to avoid exposing a half torn-down w object
//memset(&w.callbacks, 0, sizeof(w.callbacks))
// The w's context must not be current on another thread when the
// w is destroyed
current, err := _glfw.contextSlot.get()
if err != nil {
return err
}
if uintptr(unsafe.Pointer(w)) == current {
if err := (*Window)(nil).MakeContextCurrent(); err != nil {
return err
}
}
w.platformDestroyWindow()
return nil
}
func (w *Window) ShouldClose() (bool, error) {
if !_glfw.initialized {
return false, NotInitialized
}
return w.shouldClose, nil
}
func (w *Window) SetShouldClose(value bool) error {
if !_glfw.initialized {
return NotInitialized
}
w.shouldClose = value
return nil
}
func (w *Window) SetTitle(title string) error {
if !_glfw.initialized {
return NotInitialized
}
if err := w.platformSetWindowTitle(title); err != nil {
return err
}
return nil
}
func (w *Window) SetIcon(images []*Image) error {
if !_glfw.initialized {
return NotInitialized
}
if err := w.platformSetWindowIcon(images); err != nil {
return err
}
return nil
}
func (w *Window) GetPos() (xpos, ypos int, err error) {
if !_glfw.initialized {
return 0, 0, NotInitialized
}
return w.platformGetWindowPos()
}
func (w *Window) SetPos(xpos, ypos int) error {
if !_glfw.initialized {
return NotInitialized
}
if w.monitor != nil {
return nil
}
if err := w.platformSetWindowPos(xpos, ypos); err != nil {
return err
}
return nil
}
func (w *Window) GetSize() (width, height int, err error) {
if !_glfw.initialized {
return 0, 0, NotInitialized
}
return w.platformGetWindowSize()
}
func (w *Window) SetSize(width, height int) error {
if !_glfw.initialized {
return NotInitialized
}
w.videoMode.Width = width
w.videoMode.Height = height
if err := w.platformSetWindowSize(width, height); err != nil {
return err
}
return nil
}
func (w *Window) SetSizeLimits(minwidth, minheight, maxwidth, maxheight int) error {
if !_glfw.initialized {
return NotInitialized
}
if minwidth != DontCare && minheight != DontCare {
if minwidth < 0 || minheight < 0 {
return fmt.Errorf("glfwwin: invalid window minimum size %dx%d: %w", minwidth, minheight, InvalidValue)
}
}
if maxwidth != DontCare && maxheight != DontCare {
if maxwidth < 0 || maxheight < 0 || maxwidth < minwidth || maxheight < minheight {
return fmt.Errorf("glfwwin: invalid window maximum size %dx%d: %w", maxwidth, maxheight, InvalidValue)
}
}
w.minwidth = minwidth
w.minheight = minheight
w.maxwidth = maxwidth
w.maxheight = maxheight
if w.monitor != nil || !w.resizable {
return nil
}
if err := w.platformSetWindowSizeLimits(minwidth, minheight, maxwidth, maxheight); err != nil {
return err
}
return nil
}
func (w *Window) SetAspectRatio(numer, denom int) error {
if !_glfw.initialized {
return NotInitialized
}
if numer != DontCare && denom != DontCare {
if numer <= 0 || denom <= 0 {
return fmt.Errorf("glfwwin: invalid window aspect ratio %d:%d: %w", numer, denom, InvalidValue)
}
}
w.numer = numer
w.denom = denom
if w.monitor != nil || !w.resizable {
return nil
}
if err := w.platformSetWindowAspectRatio(numer, denom); err != nil {
return err
}
return nil
}
func (w *Window) GetFramebufferSize() (width, height int, err error) {
if !_glfw.initialized {
return 0, 0, NotInitialized
}
return w.platformGetFramebufferSize()
}
func (w *Window) GetFrameSize() (left, top, right, bottom int, err error) {
if !_glfw.initialized {
return 0, 0, 0, 0, NotInitialized
}
return w.platformGetWindowFrameSize()
}
func (w *Window) GetContentScale() (xscale, yscale float32, err error) {
if !_glfw.initialized {
return 0, 0, NotInitialized
}
return w.platformGetWindowContentScale()
}
func (w *Window) GetOpacity() (float32, error) {
if !_glfw.initialized {
return 0, NotInitialized
}
return w.platformGetWindowOpacity()
}
func (w *Window) SetOpacity(opacity float32) error {
if !_glfw.initialized {
return NotInitialized
}
if opacity != opacity || opacity < 0 || opacity > 1 {
return fmt.Errorf("glfwwin: invalid window opacity %f: %w", opacity, InvalidValue)
}
if err := w.platformSetWindowOpacity(opacity); err != nil {
return err
}
return nil
}
func (w *Window) Iconify() error {
if !_glfw.initialized {
return NotInitialized
}
w.platformIconifyWindow()
return nil
}
func (w *Window) Restore() error {
if !_glfw.initialized {
return NotInitialized
}
w.platformRestoreWindow()
return nil
}
func (w *Window) Maximize() error {
if !_glfw.initialized {
return NotInitialized
}
if w.monitor != nil {
return nil
}
w.platformMaximizeWindow()
return nil
}
func (w *Window) Show() error {
if !_glfw.initialized {
return NotInitialized
}
if w.monitor != nil {
return nil
}
w.platformShowWindow()
if w.focusOnShow {
if err := w.platformFocusWindow(); err != nil {
return err
}
}
return nil
}
func (w *Window) RequestAttention() error {
if !_glfw.initialized {
return NotInitialized
}
w.platformRequestWindowAttention()
return nil
}
func (w *Window) Hide() error {
if !_glfw.initialized {
return NotInitialized
}
if w.monitor != nil {
return nil
}
w.platformHideWindow()
return nil
}
func (w *Window) Focus() error {
if !_glfw.initialized {
return NotInitialized
}
if err := w.platformFocusWindow(); err != nil {
return err
}
return nil
}
func (w *Window) GetAttrib(attrib Hint) (int, error) {
if !_glfw.initialized {
return 0, NotInitialized
}
switch attrib {
case Focused:
return boolToInt(w.platformWindowFocused()), nil
case Iconified:
return boolToInt(w.platformWindowIconified()), nil
case Visible:
return boolToInt(w.platformWindowVisible()), nil
case Maximized:
return boolToInt(w.platformWindowMaximized()), nil
case Hovered:
b, err := w.platformWindowHovered()
if err != nil {
return 0, err
}
return boolToInt(b), nil
case FocusOnShow:
return boolToInt(w.focusOnShow), nil
case TransparentFramebuffer:
return boolToInt(w.platformFramebufferTransparent()), nil
case Resizable:
return boolToInt(w.resizable), nil
case Decorated:
return boolToInt(w.decorated), nil
case Floating:
return boolToInt(w.floating), nil
case AutoIconify:
return boolToInt(w.autoIconify), nil
case ClientAPI:
return w.context.client, nil
case ContextCreationAPI:
return w.context.source, nil
case ContextVersionMajor:
return w.context.major, nil
case ContextVersionMinor:
return w.context.minor, nil
case ContextRevision:
return w.context.revision, nil
case ContextRobustness:
return w.context.robustness, nil
case OpenGLForwardCompat:
return boolToInt(w.context.forward), nil
case OpenGLDebugContext:
return boolToInt(w.context.debug), nil
case OpenGLProfile:
return w.context.profile, nil
case ContextReleaseBehavior:
return w.context.release, nil
case ContextNoError:
return boolToInt(w.context.noerror), nil
default:
return 0, fmt.Errorf("glfwwin: invalid window attribute 0x%08X: %w", attrib, InvalidEnum)
}
}
func (w *Window) SetAttrib(attrib Hint, value int) error {
if !_glfw.initialized {
return NotInitialized
}
bValue := intToBool(value)
switch attrib {
case AutoIconify:
w.autoIconify = bValue
return nil
case Resizable:
if w.resizable == bValue {
return nil
}
w.resizable = bValue
if w.monitor == nil {
if err := w.platformSetWindowResizable(bValue); err != nil {
return nil
}
}
return nil
case Decorated:
if w.decorated == bValue {
return nil
}
w.decorated = bValue
if w.monitor == nil {
if err := w.platformSetWindowDecorated(bValue); err != nil {
return err
}
}
return nil
case Floating:
if w.floating == bValue {
return nil
}
w.floating = bValue
if w.monitor == nil {
if err := w.platformSetWindowFloating(bValue); err != nil {
return err
}
}
return nil
case FocusOnShow:
w.focusOnShow = bValue
return nil
default:
return fmt.Errorf("glfwwin: invalid window attribute 0x%08X: %w", attrib, InvalidEnum)
}
}
func (w *Window) GetMonitor() (*Monitor, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
return w.monitor, nil
}
func (w *Window) SetMonitor(monitor *Monitor, xpos, ypos, width, height, refreshRate int) error {
if !_glfw.initialized {
return NotInitialized
}
if width <= 0 || height <= 0 {
return fmt.Errorf("glfwwin: invalid window size %dx%d: %w", width, height, InvalidValue)
}
if refreshRate < 0 && refreshRate != DontCare {
return fmt.Errorf("glfwwin: invalid refresh rate %d: %w", refreshRate, InvalidValue)
}
w.videoMode.Width = width
w.videoMode.Height = height
w.videoMode.RefreshRate = refreshRate
if err := w.platformSetWindowMonitor(monitor, xpos, ypos, width, height, refreshRate); err != nil {
return err
}
return nil
}
func (w *Window) SetUserPointer(pointer unsafe.Pointer) error {
if !_glfw.initialized {
return NotInitialized
}
w.userPointer = pointer
return nil
}
func (w *Window) GetUserPointer() (unsafe.Pointer, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
return w.userPointer, nil
}
func (w *Window) SetPosCallback(cbfun PosCallback) (PosCallback, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
old := w.callbacks.pos
w.callbacks.pos = cbfun
return old, nil
}
func (w *Window) SetSizeCallback(cbfun SizeCallback) (SizeCallback, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
old := w.callbacks.size
w.callbacks.size = cbfun
return old, nil
}
func (w *Window) SetCloseCallback(cbfun CloseCallback) (CloseCallback, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
old := w.callbacks.close
w.callbacks.close = cbfun
return old, nil
}
func (w *Window) SetRefreshCallback(cbfun RefreshCallback) (RefreshCallback, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
old := w.callbacks.refresh
w.callbacks.refresh = cbfun
return old, nil
}
func (w *Window) SetFocusCallback(cbfun FocusCallback) (FocusCallback, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
old := w.callbacks.focus
w.callbacks.focus = cbfun
return old, nil
}
func (w *Window) SetIconifyCallback(cbfun IconifyCallback) (IconifyCallback, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
old := w.callbacks.iconify
w.callbacks.iconify = cbfun
return old, nil
}
func (w *Window) SetMaximizeCallback(cbfun MaximizeCallback) (MaximizeCallback, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
old := w.callbacks.maximize
w.callbacks.maximize = cbfun
return old, nil
}
func (w *Window) SetFramebufferSizeCallback(cbfun FramebufferSizeCallback) (FramebufferSizeCallback, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
old := w.callbacks.fbsize
w.callbacks.fbsize = cbfun
return old, nil
}
func (w *Window) SetContentScaleCallback(cbfun ContentScaleCallback) (ContentScaleCallback, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
old := w.callbacks.scale
w.callbacks.scale = cbfun
return old, nil
}
func PollEvents() error {
if !_glfw.initialized {
return NotInitialized
}
if err := platformPollEvents(); err != nil {
return err
}
return nil
}
func WaitEvents() error {
if !_glfw.initialized {
return NotInitialized
}
if err := platformWaitEvents(); err != nil {
return err
}
return nil
}
func WaitEventsTimeout(timeout float64) error {
if !_glfw.initialized {
return NotInitialized
}
if timeout != timeout || timeout < 0.0 || timeout > math.MaxFloat64 {
return fmt.Errorf("glfwwin: invalid time %f: %w", timeout, InvalidValue)
}
if err := platformWaitEventsTimeout(timeout); err != nil {
return err
}
return nil
}
func PostEmptyEvent() error {
if !_glfw.initialized {
return NotInitialized
}
if err := platformPostEmptyEvent(); err != nil {
return err
}
return nil
}