mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-25 19:28:57 +01:00
internal: add a new package glfwwin
internal/glfwwin is a pure Go implementation of GLFW for Windows. Updates #1764
This commit is contained in:
parent
58c871df38
commit
edd617f80e
1939
internal/glfwwin/api_windows.go
Normal file
1939
internal/glfwwin/api_windows.go
Normal file
File diff suppressed because it is too large
Load Diff
622
internal/glfwwin/context_windows.go
Normal file
622
internal/glfwwin/context_windows.go
Normal 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
|
||||||
|
}
|
358
internal/glfwwin/glfw3h_windows.go
Normal file
358
internal/glfwwin/glfw3h_windows.go
Normal 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
|
||||||
|
}
|
97
internal/glfwwin/init_windows.go
Normal file
97
internal/glfwwin/init_windows.go
Normal 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
|
||||||
|
}
|
532
internal/glfwwin/input_windows.go
Normal file
532
internal/glfwwin/input_windows.go
Normal 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()
|
||||||
|
}
|
328
internal/glfwwin/internal_windows.go
Normal file
328
internal/glfwwin/internal_windows.go
Normal 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
|
||||||
|
}
|
313
internal/glfwwin/monitor_windows.go
Normal file
313
internal/glfwwin/monitor_windows.go
Normal 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")
|
||||||
|
}
|
573
internal/glfwwin/wglcontext_windows.go
Normal file
573
internal/glfwwin/wglcontext_windows.go
Normal 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
|
||||||
|
}
|
344
internal/glfwwin/win32init_windows.go
Normal file
344
internal/glfwwin/win32init_windows.go
Normal 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
|
||||||
|
}
|
375
internal/glfwwin/win32monitor_windows.go
Normal file
375
internal/glfwwin/win32monitor_windows.go
Normal 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
|
||||||
|
}
|
56
internal/glfwwin/win32platform_windows.go
Normal file
56
internal/glfwwin/win32platform_windows.go
Normal 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)
|
||||||
|
}
|
65
internal/glfwwin/win32thread_windows.go
Normal file
65
internal/glfwwin/win32thread_windows.go
Normal 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)
|
||||||
|
}
|
2115
internal/glfwwin/win32window_windows.go
Normal file
2115
internal/glfwwin/win32window_windows.go
Normal file
File diff suppressed because it is too large
Load Diff
880
internal/glfwwin/window_windows.go
Normal file
880
internal/glfwwin/window_windows.go
Normal 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
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user