mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 18:58:54 +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