vendor dependencies, make some changes to how input is done
This commit is contained in:
+52
@@ -0,0 +1,52 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2009-2016 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
|
||||
|
||||
package glfw
|
||||
|
||||
// #include "internal_unix.h"
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ebitengine/purego"
|
||||
)
|
||||
|
||||
type mach_timebase_info_data_t struct {
|
||||
numer uint32
|
||||
denom uint32
|
||||
}
|
||||
|
||||
var mach_absolute_time func() uint64
|
||||
var mach_timebase_info func(*mach_timebase_info_data_t)
|
||||
var pthread_key_create func(key *C.pthread_key_t, destructor uintptr) int32
|
||||
var pthread_key_delete func(key C.pthread_key_t) int32
|
||||
var pthread_getspecific func(key C.pthread_key_t) uintptr
|
||||
var pthread_setspecific func(key C.pthread_key_t, value uintptr) int32
|
||||
var pthread_mutex_init func(mutex *C.pthread_mutex_t, attr *C.pthread_mutexattr_t) int32
|
||||
var pthread_mutex_destroy func(mutex *C.pthread_mutex_t) int32
|
||||
var pthread_mutex_lock func(mutex *C.pthread_mutex_t) int32
|
||||
var pthread_mutex_unlock func(mutex *C.pthread_mutex_t) int32
|
||||
|
||||
// TODO: replace with Go error handling
|
||||
var _glfwInputError func(code int32, format *C.char)
|
||||
|
||||
func init() {
|
||||
purego.RegisterLibFunc(&_glfwInputError, purego.RTLD_DEFAULT, "_glfwInputError")
|
||||
|
||||
libSystem, err := purego.Dlopen("/usr/lib/libSystem.B.dylib", purego.RTLD_LAZY|purego.RTLD_GLOBAL)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("glfw: failed to dlopen: %w", err))
|
||||
}
|
||||
purego.RegisterLibFunc(&mach_absolute_time, libSystem, "mach_absolute_time")
|
||||
purego.RegisterLibFunc(&mach_timebase_info, libSystem, "mach_timebase_info")
|
||||
purego.RegisterLibFunc(&pthread_key_create, libSystem, "pthread_key_create")
|
||||
purego.RegisterLibFunc(&pthread_key_delete, libSystem, "pthread_key_delete")
|
||||
purego.RegisterLibFunc(&pthread_getspecific, libSystem, "pthread_getspecific")
|
||||
purego.RegisterLibFunc(&pthread_setspecific, libSystem, "pthread_setspecific")
|
||||
purego.RegisterLibFunc(&pthread_mutex_init, libSystem, "pthread_mutex_init")
|
||||
purego.RegisterLibFunc(&pthread_mutex_destroy, libSystem, "pthread_mutex_destroy")
|
||||
purego.RegisterLibFunc(&pthread_mutex_lock, libSystem, "pthread_mutex_lock")
|
||||
purego.RegisterLibFunc(&pthread_mutex_unlock, libSystem, "pthread_mutex_unlock")
|
||||
}
|
||||
+1842
File diff suppressed because it is too large
Load Diff
+14
@@ -0,0 +1,14 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2012 The glfw3-go Authors
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build freebsd || netbsd || openbsd
|
||||
|
||||
package glfw
|
||||
|
||||
import "C"
|
||||
|
||||
// #cgo pkg-config: x11 xau xcb xdmcp
|
||||
// #cgo CFLAGS: -D_GLFW_HAS_DLOPEN -D_GLFW_X11 -D_GLFW_HAS_GLXGETPROCADDRESSARB
|
||||
// #cgo LDFLAGS: -lm
|
||||
import "C"
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2012 The glfw3-go Authors
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
package glfw
|
||||
|
||||
// #cgo CFLAGS: -D_GLFW_COCOA -Wno-deprecated-declarations
|
||||
// #cgo LDFLAGS: -framework Cocoa -framework IOKit -framework CoreVideo
|
||||
import "C"
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2012 The glfw3-go Authors
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
package glfw
|
||||
|
||||
// #cgo CFLAGS: -D_GLFW_X11
|
||||
// #cgo LDFLAGS: -lX11 -lXrandr -lXxf86vm -lXi -lXcursor -lm -lXinerama -ldl -lrt
|
||||
import "C"
|
||||
+573
@@ -0,0 +1,573 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2009-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
#include "internal_unix.h"
|
||||
#include <sys/param.h> // For MAXPATHLEN
|
||||
|
||||
// Needed for _NSGetProgname
|
||||
#include <crt_externs.h>
|
||||
|
||||
// Change to our application bundle's resources directory, if present
|
||||
//
|
||||
static void changeToResourcesDirectory(void)
|
||||
{
|
||||
char resourcesPath[MAXPATHLEN];
|
||||
|
||||
CFBundleRef bundle = CFBundleGetMainBundle();
|
||||
if (!bundle)
|
||||
return;
|
||||
|
||||
CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle);
|
||||
|
||||
CFStringRef last = CFURLCopyLastPathComponent(resourcesURL);
|
||||
if (CFStringCompare(CFSTR("Resources"), last, 0) != kCFCompareEqualTo)
|
||||
{
|
||||
CFRelease(last);
|
||||
CFRelease(resourcesURL);
|
||||
return;
|
||||
}
|
||||
|
||||
CFRelease(last);
|
||||
|
||||
if (!CFURLGetFileSystemRepresentation(resourcesURL,
|
||||
true,
|
||||
(UInt8*) resourcesPath,
|
||||
MAXPATHLEN))
|
||||
{
|
||||
CFRelease(resourcesURL);
|
||||
return;
|
||||
}
|
||||
|
||||
CFRelease(resourcesURL);
|
||||
|
||||
chdir(resourcesPath);
|
||||
}
|
||||
|
||||
// Set up the menu bar (manually)
|
||||
// This is nasty, nasty stuff -- calls to undocumented semi-private APIs that
|
||||
// could go away at any moment, lots of stuff that really should be
|
||||
// localize(d|able), etc. Add a nib to save us this horror.
|
||||
//
|
||||
static void createMenuBar(void)
|
||||
{
|
||||
size_t i;
|
||||
NSString* appName = nil;
|
||||
NSDictionary* bundleInfo = [[NSBundle mainBundle] infoDictionary];
|
||||
NSString* nameKeys[] =
|
||||
{
|
||||
@"CFBundleDisplayName",
|
||||
@"CFBundleName",
|
||||
@"CFBundleExecutable",
|
||||
};
|
||||
|
||||
// Try to figure out what the calling application is called
|
||||
|
||||
for (i = 0; i < sizeof(nameKeys) / sizeof(nameKeys[0]); i++)
|
||||
{
|
||||
id name = bundleInfo[nameKeys[i]];
|
||||
if (name &&
|
||||
[name isKindOfClass:[NSString class]] &&
|
||||
![name isEqualToString:@""])
|
||||
{
|
||||
appName = name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!appName)
|
||||
{
|
||||
char** progname = _NSGetProgname();
|
||||
if (progname && *progname)
|
||||
appName = @(*progname);
|
||||
else
|
||||
appName = @"GLFW Application";
|
||||
}
|
||||
|
||||
NSMenu* bar = [[NSMenu alloc] init];
|
||||
[NSApp setMainMenu:bar];
|
||||
|
||||
NSMenuItem* appMenuItem =
|
||||
[bar addItemWithTitle:@"" action:NULL keyEquivalent:@""];
|
||||
NSMenu* appMenu = [[NSMenu alloc] init];
|
||||
[appMenuItem setSubmenu:appMenu];
|
||||
|
||||
[appMenu addItemWithTitle:[NSString stringWithFormat:@"About %@", appName]
|
||||
action:@selector(orderFrontStandardAboutPanel:)
|
||||
keyEquivalent:@""];
|
||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||
NSMenu* servicesMenu = [[NSMenu alloc] init];
|
||||
[NSApp setServicesMenu:servicesMenu];
|
||||
[[appMenu addItemWithTitle:@"Services"
|
||||
action:NULL
|
||||
keyEquivalent:@""] setSubmenu:servicesMenu];
|
||||
[servicesMenu release];
|
||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||
[appMenu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", appName]
|
||||
action:@selector(hide:)
|
||||
keyEquivalent:@"h"];
|
||||
[[appMenu addItemWithTitle:@"Hide Others"
|
||||
action:@selector(hideOtherApplications:)
|
||||
keyEquivalent:@"h"]
|
||||
setKeyEquivalentModifierMask:NSEventModifierFlagOption | NSEventModifierFlagCommand];
|
||||
[appMenu addItemWithTitle:@"Show All"
|
||||
action:@selector(unhideAllApplications:)
|
||||
keyEquivalent:@""];
|
||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||
[appMenu addItemWithTitle:[NSString stringWithFormat:@"Quit %@", appName]
|
||||
action:@selector(terminate:)
|
||||
keyEquivalent:@"q"];
|
||||
|
||||
NSMenuItem* windowMenuItem =
|
||||
[bar addItemWithTitle:@"" action:NULL keyEquivalent:@""];
|
||||
[bar release];
|
||||
NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
|
||||
[NSApp setWindowsMenu:windowMenu];
|
||||
[windowMenuItem setSubmenu:windowMenu];
|
||||
|
||||
[windowMenu addItemWithTitle:@"Minimize"
|
||||
action:@selector(performMiniaturize:)
|
||||
keyEquivalent:@"m"];
|
||||
[windowMenu addItemWithTitle:@"Zoom"
|
||||
action:@selector(performZoom:)
|
||||
keyEquivalent:@""];
|
||||
[windowMenu addItem:[NSMenuItem separatorItem]];
|
||||
[windowMenu addItemWithTitle:@"Bring All to Front"
|
||||
action:@selector(arrangeInFront:)
|
||||
keyEquivalent:@""];
|
||||
|
||||
// TODO: Make this appear at the bottom of the menu (for consistency)
|
||||
[windowMenu addItem:[NSMenuItem separatorItem]];
|
||||
[[windowMenu addItemWithTitle:@"Enter Full Screen"
|
||||
action:@selector(toggleFullScreen:)
|
||||
keyEquivalent:@"f"]
|
||||
setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand];
|
||||
|
||||
// Prior to Snow Leopard, we need to use this oddly-named semi-private API
|
||||
// to get the application menu working properly.
|
||||
SEL setAppleMenuSelector = NSSelectorFromString(@"setAppleMenu:");
|
||||
[NSApp performSelector:setAppleMenuSelector withObject:appMenu];
|
||||
}
|
||||
|
||||
// Create key code translation tables
|
||||
//
|
||||
static void createKeyTables(void)
|
||||
{
|
||||
int scancode;
|
||||
|
||||
memset(_glfw.ns.keycodes, -1, sizeof(_glfw.ns.keycodes));
|
||||
memset(_glfw.ns.scancodes, -1, sizeof(_glfw.ns.scancodes));
|
||||
|
||||
_glfw.ns.keycodes[0x1D] = GLFW_KEY_0;
|
||||
_glfw.ns.keycodes[0x12] = GLFW_KEY_1;
|
||||
_glfw.ns.keycodes[0x13] = GLFW_KEY_2;
|
||||
_glfw.ns.keycodes[0x14] = GLFW_KEY_3;
|
||||
_glfw.ns.keycodes[0x15] = GLFW_KEY_4;
|
||||
_glfw.ns.keycodes[0x17] = GLFW_KEY_5;
|
||||
_glfw.ns.keycodes[0x16] = GLFW_KEY_6;
|
||||
_glfw.ns.keycodes[0x1A] = GLFW_KEY_7;
|
||||
_glfw.ns.keycodes[0x1C] = GLFW_KEY_8;
|
||||
_glfw.ns.keycodes[0x19] = GLFW_KEY_9;
|
||||
_glfw.ns.keycodes[0x00] = GLFW_KEY_A;
|
||||
_glfw.ns.keycodes[0x0B] = GLFW_KEY_B;
|
||||
_glfw.ns.keycodes[0x08] = GLFW_KEY_C;
|
||||
_glfw.ns.keycodes[0x02] = GLFW_KEY_D;
|
||||
_glfw.ns.keycodes[0x0E] = GLFW_KEY_E;
|
||||
_glfw.ns.keycodes[0x03] = GLFW_KEY_F;
|
||||
_glfw.ns.keycodes[0x05] = GLFW_KEY_G;
|
||||
_glfw.ns.keycodes[0x04] = GLFW_KEY_H;
|
||||
_glfw.ns.keycodes[0x22] = GLFW_KEY_I;
|
||||
_glfw.ns.keycodes[0x26] = GLFW_KEY_J;
|
||||
_glfw.ns.keycodes[0x28] = GLFW_KEY_K;
|
||||
_glfw.ns.keycodes[0x25] = GLFW_KEY_L;
|
||||
_glfw.ns.keycodes[0x2E] = GLFW_KEY_M;
|
||||
_glfw.ns.keycodes[0x2D] = GLFW_KEY_N;
|
||||
_glfw.ns.keycodes[0x1F] = GLFW_KEY_O;
|
||||
_glfw.ns.keycodes[0x23] = GLFW_KEY_P;
|
||||
_glfw.ns.keycodes[0x0C] = GLFW_KEY_Q;
|
||||
_glfw.ns.keycodes[0x0F] = GLFW_KEY_R;
|
||||
_glfw.ns.keycodes[0x01] = GLFW_KEY_S;
|
||||
_glfw.ns.keycodes[0x11] = GLFW_KEY_T;
|
||||
_glfw.ns.keycodes[0x20] = GLFW_KEY_U;
|
||||
_glfw.ns.keycodes[0x09] = GLFW_KEY_V;
|
||||
_glfw.ns.keycodes[0x0D] = GLFW_KEY_W;
|
||||
_glfw.ns.keycodes[0x07] = GLFW_KEY_X;
|
||||
_glfw.ns.keycodes[0x10] = GLFW_KEY_Y;
|
||||
_glfw.ns.keycodes[0x06] = GLFW_KEY_Z;
|
||||
|
||||
_glfw.ns.keycodes[0x27] = GLFW_KEY_APOSTROPHE;
|
||||
_glfw.ns.keycodes[0x2A] = GLFW_KEY_BACKSLASH;
|
||||
_glfw.ns.keycodes[0x2B] = GLFW_KEY_COMMA;
|
||||
_glfw.ns.keycodes[0x18] = GLFW_KEY_EQUAL;
|
||||
_glfw.ns.keycodes[0x32] = GLFW_KEY_GRAVE_ACCENT;
|
||||
_glfw.ns.keycodes[0x21] = GLFW_KEY_LEFT_BRACKET;
|
||||
_glfw.ns.keycodes[0x1B] = GLFW_KEY_MINUS;
|
||||
_glfw.ns.keycodes[0x2F] = GLFW_KEY_PERIOD;
|
||||
_glfw.ns.keycodes[0x1E] = GLFW_KEY_RIGHT_BRACKET;
|
||||
_glfw.ns.keycodes[0x29] = GLFW_KEY_SEMICOLON;
|
||||
_glfw.ns.keycodes[0x2C] = GLFW_KEY_SLASH;
|
||||
_glfw.ns.keycodes[0x0A] = GLFW_KEY_WORLD_1;
|
||||
|
||||
_glfw.ns.keycodes[0x33] = GLFW_KEY_BACKSPACE;
|
||||
_glfw.ns.keycodes[0x39] = GLFW_KEY_CAPS_LOCK;
|
||||
_glfw.ns.keycodes[0x75] = GLFW_KEY_DELETE;
|
||||
_glfw.ns.keycodes[0x7D] = GLFW_KEY_DOWN;
|
||||
_glfw.ns.keycodes[0x77] = GLFW_KEY_END;
|
||||
_glfw.ns.keycodes[0x24] = GLFW_KEY_ENTER;
|
||||
_glfw.ns.keycodes[0x35] = GLFW_KEY_ESCAPE;
|
||||
_glfw.ns.keycodes[0x7A] = GLFW_KEY_F1;
|
||||
_glfw.ns.keycodes[0x78] = GLFW_KEY_F2;
|
||||
_glfw.ns.keycodes[0x63] = GLFW_KEY_F3;
|
||||
_glfw.ns.keycodes[0x76] = GLFW_KEY_F4;
|
||||
_glfw.ns.keycodes[0x60] = GLFW_KEY_F5;
|
||||
_glfw.ns.keycodes[0x61] = GLFW_KEY_F6;
|
||||
_glfw.ns.keycodes[0x62] = GLFW_KEY_F7;
|
||||
_glfw.ns.keycodes[0x64] = GLFW_KEY_F8;
|
||||
_glfw.ns.keycodes[0x65] = GLFW_KEY_F9;
|
||||
_glfw.ns.keycodes[0x6D] = GLFW_KEY_F10;
|
||||
_glfw.ns.keycodes[0x67] = GLFW_KEY_F11;
|
||||
_glfw.ns.keycodes[0x6F] = GLFW_KEY_F12;
|
||||
_glfw.ns.keycodes[0x69] = GLFW_KEY_F13;
|
||||
_glfw.ns.keycodes[0x6B] = GLFW_KEY_F14;
|
||||
_glfw.ns.keycodes[0x71] = GLFW_KEY_F15;
|
||||
_glfw.ns.keycodes[0x6A] = GLFW_KEY_F16;
|
||||
_glfw.ns.keycodes[0x40] = GLFW_KEY_F17;
|
||||
_glfw.ns.keycodes[0x4F] = GLFW_KEY_F18;
|
||||
_glfw.ns.keycodes[0x50] = GLFW_KEY_F19;
|
||||
_glfw.ns.keycodes[0x5A] = GLFW_KEY_F20;
|
||||
_glfw.ns.keycodes[0x73] = GLFW_KEY_HOME;
|
||||
_glfw.ns.keycodes[0x72] = GLFW_KEY_INSERT;
|
||||
_glfw.ns.keycodes[0x7B] = GLFW_KEY_LEFT;
|
||||
_glfw.ns.keycodes[0x3A] = GLFW_KEY_LEFT_ALT;
|
||||
_glfw.ns.keycodes[0x3B] = GLFW_KEY_LEFT_CONTROL;
|
||||
_glfw.ns.keycodes[0x38] = GLFW_KEY_LEFT_SHIFT;
|
||||
_glfw.ns.keycodes[0x37] = GLFW_KEY_LEFT_SUPER;
|
||||
_glfw.ns.keycodes[0x6E] = GLFW_KEY_MENU;
|
||||
_glfw.ns.keycodes[0x47] = GLFW_KEY_NUM_LOCK;
|
||||
_glfw.ns.keycodes[0x79] = GLFW_KEY_PAGE_DOWN;
|
||||
_glfw.ns.keycodes[0x74] = GLFW_KEY_PAGE_UP;
|
||||
_glfw.ns.keycodes[0x7C] = GLFW_KEY_RIGHT;
|
||||
_glfw.ns.keycodes[0x3D] = GLFW_KEY_RIGHT_ALT;
|
||||
_glfw.ns.keycodes[0x3E] = GLFW_KEY_RIGHT_CONTROL;
|
||||
_glfw.ns.keycodes[0x3C] = GLFW_KEY_RIGHT_SHIFT;
|
||||
_glfw.ns.keycodes[0x36] = GLFW_KEY_RIGHT_SUPER;
|
||||
_glfw.ns.keycodes[0x31] = GLFW_KEY_SPACE;
|
||||
_glfw.ns.keycodes[0x30] = GLFW_KEY_TAB;
|
||||
_glfw.ns.keycodes[0x7E] = GLFW_KEY_UP;
|
||||
|
||||
_glfw.ns.keycodes[0x52] = GLFW_KEY_KP_0;
|
||||
_glfw.ns.keycodes[0x53] = GLFW_KEY_KP_1;
|
||||
_glfw.ns.keycodes[0x54] = GLFW_KEY_KP_2;
|
||||
_glfw.ns.keycodes[0x55] = GLFW_KEY_KP_3;
|
||||
_glfw.ns.keycodes[0x56] = GLFW_KEY_KP_4;
|
||||
_glfw.ns.keycodes[0x57] = GLFW_KEY_KP_5;
|
||||
_glfw.ns.keycodes[0x58] = GLFW_KEY_KP_6;
|
||||
_glfw.ns.keycodes[0x59] = GLFW_KEY_KP_7;
|
||||
_glfw.ns.keycodes[0x5B] = GLFW_KEY_KP_8;
|
||||
_glfw.ns.keycodes[0x5C] = GLFW_KEY_KP_9;
|
||||
_glfw.ns.keycodes[0x45] = GLFW_KEY_KP_ADD;
|
||||
_glfw.ns.keycodes[0x41] = GLFW_KEY_KP_DECIMAL;
|
||||
_glfw.ns.keycodes[0x4B] = GLFW_KEY_KP_DIVIDE;
|
||||
_glfw.ns.keycodes[0x4C] = GLFW_KEY_KP_ENTER;
|
||||
_glfw.ns.keycodes[0x51] = GLFW_KEY_KP_EQUAL;
|
||||
_glfw.ns.keycodes[0x43] = GLFW_KEY_KP_MULTIPLY;
|
||||
_glfw.ns.keycodes[0x4E] = GLFW_KEY_KP_SUBTRACT;
|
||||
|
||||
for (scancode = 0; scancode < 256; scancode++)
|
||||
{
|
||||
// Store the reverse translation for faster key name lookup
|
||||
if (_glfw.ns.keycodes[scancode] >= 0)
|
||||
_glfw.ns.scancodes[_glfw.ns.keycodes[scancode]] = scancode;
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve Unicode data for the current keyboard layout
|
||||
//
|
||||
static GLFWbool updateUnicodeDataNS(void)
|
||||
{
|
||||
if (_glfw.ns.inputSource)
|
||||
{
|
||||
CFRelease(_glfw.ns.inputSource);
|
||||
_glfw.ns.inputSource = NULL;
|
||||
_glfw.ns.unicodeData = nil;
|
||||
}
|
||||
|
||||
_glfw.ns.inputSource = TISCopyCurrentKeyboardLayoutInputSource();
|
||||
if (!_glfw.ns.inputSource)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Cocoa: Failed to retrieve keyboard layout input source");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
_glfw.ns.unicodeData =
|
||||
TISGetInputSourceProperty(_glfw.ns.inputSource,
|
||||
kTISPropertyUnicodeKeyLayoutData);
|
||||
if (!_glfw.ns.unicodeData)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Cocoa: Failed to retrieve keyboard layout Unicode data");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
// Load HIToolbox.framework and the TIS symbols we need from it
|
||||
//
|
||||
static GLFWbool initializeTIS(void)
|
||||
{
|
||||
// This works only because Cocoa has already loaded it properly
|
||||
_glfw.ns.tis.bundle =
|
||||
CFBundleGetBundleWithIdentifier(CFSTR("com.apple.HIToolbox"));
|
||||
if (!_glfw.ns.tis.bundle)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Cocoa: Failed to load HIToolbox.framework");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
CFStringRef* kPropertyUnicodeKeyLayoutData =
|
||||
CFBundleGetDataPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("kTISPropertyUnicodeKeyLayoutData"));
|
||||
_glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource =
|
||||
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("TISCopyCurrentKeyboardLayoutInputSource"));
|
||||
_glfw.ns.tis.GetInputSourceProperty =
|
||||
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("TISGetInputSourceProperty"));
|
||||
_glfw.ns.tis.GetKbdType =
|
||||
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("LMGetKbdType"));
|
||||
|
||||
if (!kPropertyUnicodeKeyLayoutData ||
|
||||
!TISCopyCurrentKeyboardLayoutInputSource ||
|
||||
!TISGetInputSourceProperty ||
|
||||
!LMGetKbdType)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Cocoa: Failed to load TIS API symbols");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
_glfw.ns.tis.kPropertyUnicodeKeyLayoutData =
|
||||
*kPropertyUnicodeKeyLayoutData;
|
||||
|
||||
return updateUnicodeDataNS();
|
||||
}
|
||||
|
||||
@interface GLFWHelper : NSObject
|
||||
@end
|
||||
|
||||
@implementation GLFWHelper
|
||||
|
||||
- (void)selectedKeyboardInputSourceChanged:(NSObject* )object
|
||||
{
|
||||
updateUnicodeDataNS();
|
||||
}
|
||||
|
||||
- (void)doNothing:(id)object
|
||||
{
|
||||
}
|
||||
|
||||
@end // GLFWHelper
|
||||
|
||||
@interface GLFWApplicationDelegate : NSObject <NSApplicationDelegate>
|
||||
@end
|
||||
|
||||
@implementation GLFWApplicationDelegate
|
||||
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
|
||||
{
|
||||
_GLFWwindow* window;
|
||||
|
||||
for (window = _glfw.windowListHead; window; window = window->next)
|
||||
_glfwInputWindowCloseRequest(window);
|
||||
|
||||
return NSTerminateCancel;
|
||||
}
|
||||
|
||||
- (void)applicationDidChangeScreenParameters:(NSNotification *) notification
|
||||
{
|
||||
_GLFWwindow* window;
|
||||
|
||||
for (window = _glfw.windowListHead; window; window = window->next)
|
||||
{
|
||||
if (window->context.client != GLFW_NO_API)
|
||||
[window->context.nsgl.object update];
|
||||
}
|
||||
|
||||
_glfwPollMonitorsNS();
|
||||
}
|
||||
|
||||
- (void)applicationWillFinishLaunching:(NSNotification *)notification
|
||||
{
|
||||
if (_glfw.hints.init.ns.menubar)
|
||||
{
|
||||
// Menu bar setup must go between sharedApplication and finishLaunching
|
||||
// in order to properly emulate the behavior of NSApplicationMain
|
||||
|
||||
if ([[NSBundle mainBundle] pathForResource:@"MainMenu" ofType:@"nib"])
|
||||
{
|
||||
[[NSBundle mainBundle] loadNibNamed:@"MainMenu"
|
||||
owner:NSApp
|
||||
topLevelObjects:&_glfw.ns.nibObjects];
|
||||
}
|
||||
else
|
||||
createMenuBar();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)notification
|
||||
{
|
||||
_glfw.ns.finishedLaunching = GLFW_TRUE;
|
||||
_glfwPlatformPostEmptyEvent();
|
||||
|
||||
// In case we are unbundled, make us a proper UI application
|
||||
if (_glfw.hints.init.ns.menubar)
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
|
||||
[NSApp stop:nil];
|
||||
}
|
||||
|
||||
- (void)applicationDidHide:(NSNotification *)notification
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < _glfw.monitorCount; i++)
|
||||
_glfwRestoreVideoModeNS(_glfw.monitors[i]);
|
||||
}
|
||||
|
||||
@end // GLFWApplicationDelegate
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW platform API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int _glfwPlatformInit(void)
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
||||
_glfw.ns.helper = [[GLFWHelper alloc] init];
|
||||
|
||||
[NSThread detachNewThreadSelector:@selector(doNothing:)
|
||||
toTarget:_glfw.ns.helper
|
||||
withObject:nil];
|
||||
|
||||
if (NSApp)
|
||||
_glfw.ns.finishedLaunching = GLFW_TRUE;
|
||||
|
||||
[NSApplication sharedApplication];
|
||||
|
||||
_glfw.ns.delegate = [[GLFWApplicationDelegate alloc] init];
|
||||
if (_glfw.ns.delegate == nil)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Cocoa: Failed to create application delegate");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
[NSApp setDelegate:_glfw.ns.delegate];
|
||||
|
||||
NSEvent* (^block)(NSEvent*) = ^ NSEvent* (NSEvent* event)
|
||||
{
|
||||
if ([event modifierFlags] & NSEventModifierFlagCommand)
|
||||
[[NSApp keyWindow] sendEvent:event];
|
||||
|
||||
return event;
|
||||
};
|
||||
|
||||
_glfw.ns.keyUpMonitor =
|
||||
[NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyUp
|
||||
handler:block];
|
||||
|
||||
if (_glfw.hints.init.ns.chdir)
|
||||
changeToResourcesDirectory();
|
||||
|
||||
// Press and Hold prevents some keys from emitting repeated characters
|
||||
NSDictionary* defaults = @{@"ApplePressAndHoldEnabled":@NO};
|
||||
[[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
|
||||
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserver:_glfw.ns.helper
|
||||
selector:@selector(selectedKeyboardInputSourceChanged:)
|
||||
name:NSTextInputContextKeyboardSelectionDidChangeNotification
|
||||
object:nil];
|
||||
|
||||
createKeyTables();
|
||||
|
||||
_glfw.ns.eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
|
||||
if (!_glfw.ns.eventSource)
|
||||
return GLFW_FALSE;
|
||||
|
||||
CGEventSourceSetLocalEventsSuppressionInterval(_glfw.ns.eventSource, 0.0);
|
||||
|
||||
if (!initializeTIS())
|
||||
return GLFW_FALSE;
|
||||
|
||||
_glfwInitTimerNS();
|
||||
|
||||
_glfwPollMonitorsNS();
|
||||
return GLFW_TRUE;
|
||||
|
||||
} // autoreleasepool
|
||||
}
|
||||
|
||||
void _glfwPlatformTerminate(void)
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
||||
if (_glfw.ns.inputSource)
|
||||
{
|
||||
CFRelease(_glfw.ns.inputSource);
|
||||
_glfw.ns.inputSource = NULL;
|
||||
_glfw.ns.unicodeData = nil;
|
||||
}
|
||||
|
||||
if (_glfw.ns.eventSource)
|
||||
{
|
||||
CFRelease(_glfw.ns.eventSource);
|
||||
_glfw.ns.eventSource = NULL;
|
||||
}
|
||||
|
||||
if (_glfw.ns.delegate)
|
||||
{
|
||||
[NSApp setDelegate:nil];
|
||||
[_glfw.ns.delegate release];
|
||||
_glfw.ns.delegate = nil;
|
||||
}
|
||||
|
||||
if (_glfw.ns.helper)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
removeObserver:_glfw.ns.helper
|
||||
name:NSTextInputContextKeyboardSelectionDidChangeNotification
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
removeObserver:_glfw.ns.helper];
|
||||
[_glfw.ns.helper release];
|
||||
_glfw.ns.helper = nil;
|
||||
}
|
||||
|
||||
if (_glfw.ns.keyUpMonitor)
|
||||
[NSEvent removeMonitor:_glfw.ns.keyUpMonitor];
|
||||
|
||||
free(_glfw.ns.clipboardString);
|
||||
|
||||
_glfwTerminateNSGL();
|
||||
_glfwTerminateEGL();
|
||||
_glfwTerminateOSMesa();
|
||||
|
||||
} // autoreleasepool
|
||||
}
|
||||
|
||||
const char* _glfwPlatformGetVersionString(void)
|
||||
{
|
||||
return _GLFW_VERSION_NUMBER " Cocoa NSGL EGL OSMesa"
|
||||
#if defined(_GLFW_BUILD_DLL)
|
||||
" dynamic"
|
||||
#endif
|
||||
;
|
||||
}
|
||||
|
||||
+603
@@ -0,0 +1,603 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
#include "internal_unix.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <IOKit/graphics/IOGraphicsLib.h>
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
|
||||
|
||||
// Get the name of the specified display, or NULL
|
||||
//
|
||||
static char* getMonitorName(CGDirectDisplayID displayID, NSScreen* screen)
|
||||
{
|
||||
// IOKit doesn't work on Apple Silicon anymore
|
||||
// Luckily, 10.15 introduced -[NSScreen localizedName].
|
||||
// Use it if available, and fall back to IOKit otherwise.
|
||||
if (screen)
|
||||
{
|
||||
if ([screen respondsToSelector:@selector(localizedName)])
|
||||
{
|
||||
NSString* name = [screen valueForKey:@"localizedName"];
|
||||
if (name)
|
||||
return _glfw_strdup([name UTF8String]);
|
||||
}
|
||||
}
|
||||
|
||||
io_iterator_t it;
|
||||
io_service_t service;
|
||||
CFDictionaryRef info;
|
||||
|
||||
if (IOServiceGetMatchingServices(MACH_PORT_NULL,
|
||||
IOServiceMatching("IODisplayConnect"),
|
||||
&it) != 0)
|
||||
{
|
||||
// This may happen if a desktop Mac is running headless
|
||||
return _glfw_strdup("Display");
|
||||
}
|
||||
|
||||
while ((service = IOIteratorNext(it)) != 0)
|
||||
{
|
||||
info = IODisplayCreateInfoDictionary(service,
|
||||
kIODisplayOnlyPreferredName);
|
||||
|
||||
CFNumberRef vendorIDRef =
|
||||
CFDictionaryGetValue(info, CFSTR(kDisplayVendorID));
|
||||
CFNumberRef productIDRef =
|
||||
CFDictionaryGetValue(info, CFSTR(kDisplayProductID));
|
||||
if (!vendorIDRef || !productIDRef)
|
||||
{
|
||||
CFRelease(info);
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned int vendorID, productID;
|
||||
CFNumberGetValue(vendorIDRef, kCFNumberIntType, &vendorID);
|
||||
CFNumberGetValue(productIDRef, kCFNumberIntType, &productID);
|
||||
|
||||
if (CGDisplayVendorNumber(displayID) == vendorID &&
|
||||
CGDisplayModelNumber(displayID) == productID)
|
||||
{
|
||||
// Info dictionary is used and freed below
|
||||
break;
|
||||
}
|
||||
|
||||
CFRelease(info);
|
||||
}
|
||||
|
||||
IOObjectRelease(it);
|
||||
|
||||
if (!service)
|
||||
return _glfw_strdup("Display");
|
||||
|
||||
CFDictionaryRef names =
|
||||
CFDictionaryGetValue(info, CFSTR(kDisplayProductName));
|
||||
|
||||
CFStringRef nameRef;
|
||||
|
||||
if (!names || !CFDictionaryGetValueIfPresent(names, CFSTR("en_US"),
|
||||
(const void**) &nameRef))
|
||||
{
|
||||
// This may happen if a desktop Mac is running headless
|
||||
CFRelease(info);
|
||||
return _glfw_strdup("Display");
|
||||
}
|
||||
|
||||
const CFIndex size =
|
||||
CFStringGetMaximumSizeForEncoding(CFStringGetLength(nameRef),
|
||||
kCFStringEncodingUTF8);
|
||||
char* name = calloc(size + 1, 1);
|
||||
CFStringGetCString(nameRef, name, size, kCFStringEncodingUTF8);
|
||||
|
||||
CFRelease(info);
|
||||
return name;
|
||||
}
|
||||
|
||||
// Check whether the display mode should be included in enumeration
|
||||
//
|
||||
static GLFWbool modeIsGood(CGDisplayModeRef mode)
|
||||
{
|
||||
uint32_t flags = CGDisplayModeGetIOFlags(mode);
|
||||
|
||||
if (!(flags & kDisplayModeValidFlag) || !(flags & kDisplayModeSafeFlag))
|
||||
return GLFW_FALSE;
|
||||
if (flags & kDisplayModeInterlacedFlag)
|
||||
return GLFW_FALSE;
|
||||
if (flags & kDisplayModeStretchedFlag)
|
||||
return GLFW_FALSE;
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100
|
||||
CFStringRef format = CGDisplayModeCopyPixelEncoding(mode);
|
||||
if (CFStringCompare(format, CFSTR(IO16BitDirectPixels), 0) &&
|
||||
CFStringCompare(format, CFSTR(IO32BitDirectPixels), 0))
|
||||
{
|
||||
CFRelease(format);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
CFRelease(format);
|
||||
#endif /* MAC_OS_X_VERSION_MAX_ALLOWED */
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
// Convert Core Graphics display mode to GLFW video mode
|
||||
//
|
||||
static GLFWvidmode vidmodeFromCGDisplayMode(CGDisplayModeRef mode,
|
||||
double fallbackRefreshRate)
|
||||
{
|
||||
GLFWvidmode result;
|
||||
result.width = (int) CGDisplayModeGetWidth(mode);
|
||||
result.height = (int) CGDisplayModeGetHeight(mode);
|
||||
result.refreshRate = (int) round(CGDisplayModeGetRefreshRate(mode));
|
||||
|
||||
if (result.refreshRate == 0)
|
||||
result.refreshRate = (int) round(fallbackRefreshRate);
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100
|
||||
CFStringRef format = CGDisplayModeCopyPixelEncoding(mode);
|
||||
if (CFStringCompare(format, CFSTR(IO16BitDirectPixels), 0) == 0)
|
||||
{
|
||||
result.redBits = 5;
|
||||
result.greenBits = 5;
|
||||
result.blueBits = 5;
|
||||
}
|
||||
else
|
||||
#endif /* MAC_OS_X_VERSION_MAX_ALLOWED */
|
||||
{
|
||||
result.redBits = 8;
|
||||
result.greenBits = 8;
|
||||
result.blueBits = 8;
|
||||
}
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100
|
||||
CFRelease(format);
|
||||
#endif /* MAC_OS_X_VERSION_MAX_ALLOWED */
|
||||
return result;
|
||||
}
|
||||
|
||||
// Starts reservation for display fading
|
||||
//
|
||||
static CGDisplayFadeReservationToken beginFadeReservation(void)
|
||||
{
|
||||
CGDisplayFadeReservationToken token = kCGDisplayFadeReservationInvalidToken;
|
||||
|
||||
if (CGAcquireDisplayFadeReservation(5, &token) == kCGErrorSuccess)
|
||||
{
|
||||
CGDisplayFade(token, 0.3,
|
||||
kCGDisplayBlendNormal,
|
||||
kCGDisplayBlendSolidColor,
|
||||
0.0, 0.0, 0.0,
|
||||
TRUE);
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
// Ends reservation for display fading
|
||||
//
|
||||
static void endFadeReservation(CGDisplayFadeReservationToken token)
|
||||
{
|
||||
if (token != kCGDisplayFadeReservationInvalidToken)
|
||||
{
|
||||
CGDisplayFade(token, 0.5,
|
||||
kCGDisplayBlendSolidColor,
|
||||
kCGDisplayBlendNormal,
|
||||
0.0, 0.0, 0.0,
|
||||
FALSE);
|
||||
CGReleaseDisplayFadeReservation(token);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the display refresh rate queried from the I/O registry
|
||||
//
|
||||
static double getFallbackRefreshRate(CGDirectDisplayID displayID)
|
||||
{
|
||||
double refreshRate = 60.0;
|
||||
|
||||
io_iterator_t it;
|
||||
io_service_t service;
|
||||
|
||||
if (IOServiceGetMatchingServices(MACH_PORT_NULL,
|
||||
IOServiceMatching("IOFramebuffer"),
|
||||
&it) != 0)
|
||||
{
|
||||
return refreshRate;
|
||||
}
|
||||
|
||||
while ((service = IOIteratorNext(it)) != 0)
|
||||
{
|
||||
const CFNumberRef indexRef =
|
||||
IORegistryEntryCreateCFProperty(service,
|
||||
CFSTR("IOFramebufferOpenGLIndex"),
|
||||
kCFAllocatorDefault,
|
||||
kNilOptions);
|
||||
if (!indexRef)
|
||||
continue;
|
||||
|
||||
uint32_t index = 0;
|
||||
CFNumberGetValue(indexRef, kCFNumberIntType, &index);
|
||||
CFRelease(indexRef);
|
||||
|
||||
if (CGOpenGLDisplayMaskToDisplayID(1 << index) != displayID)
|
||||
continue;
|
||||
|
||||
const CFNumberRef clockRef =
|
||||
IORegistryEntryCreateCFProperty(service,
|
||||
CFSTR("IOFBCurrentPixelClock"),
|
||||
kCFAllocatorDefault,
|
||||
kNilOptions);
|
||||
const CFNumberRef countRef =
|
||||
IORegistryEntryCreateCFProperty(service,
|
||||
CFSTR("IOFBCurrentPixelCount"),
|
||||
kCFAllocatorDefault,
|
||||
kNilOptions);
|
||||
|
||||
uint32_t clock = 0, count = 0;
|
||||
|
||||
if (clockRef)
|
||||
{
|
||||
CFNumberGetValue(clockRef, kCFNumberIntType, &clock);
|
||||
CFRelease(clockRef);
|
||||
}
|
||||
|
||||
if (countRef)
|
||||
{
|
||||
CFNumberGetValue(countRef, kCFNumberIntType, &count);
|
||||
CFRelease(countRef);
|
||||
}
|
||||
|
||||
if (clock > 0 && count > 0)
|
||||
refreshRate = clock / (double) count;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
IOObjectRelease(it);
|
||||
return refreshRate;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW internal API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Poll for changes in the set of connected monitors
|
||||
//
|
||||
void _glfwPollMonitorsNS(void)
|
||||
{
|
||||
uint32_t displayCount;
|
||||
CGGetOnlineDisplayList(0, NULL, &displayCount);
|
||||
CGDirectDisplayID* displays = calloc(displayCount, sizeof(CGDirectDisplayID));
|
||||
CGGetOnlineDisplayList(displayCount, displays, &displayCount);
|
||||
|
||||
for (int i = 0; i < _glfw.monitorCount; i++)
|
||||
_glfw.monitors[i]->ns.screen = nil;
|
||||
|
||||
_GLFWmonitor** disconnected = NULL;
|
||||
uint32_t disconnectedCount = _glfw.monitorCount;
|
||||
if (disconnectedCount)
|
||||
{
|
||||
disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*));
|
||||
memcpy(disconnected,
|
||||
_glfw.monitors,
|
||||
_glfw.monitorCount * sizeof(_GLFWmonitor*));
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < displayCount; i++)
|
||||
{
|
||||
if (CGDisplayIsAsleep(displays[i]))
|
||||
continue;
|
||||
|
||||
const uint32_t unitNumber = CGDisplayUnitNumber(displays[i]);
|
||||
NSScreen* screen = nil;
|
||||
|
||||
for (screen in [NSScreen screens])
|
||||
{
|
||||
NSNumber* screenNumber = [screen deviceDescription][@"NSScreenNumber"];
|
||||
|
||||
// HACK: Compare unit numbers instead of display IDs to work around
|
||||
// display replacement on machines with automatic graphics
|
||||
// switching
|
||||
if (CGDisplayUnitNumber([screenNumber unsignedIntValue]) == unitNumber)
|
||||
break;
|
||||
}
|
||||
|
||||
// HACK: Compare unit numbers instead of display IDs to work around
|
||||
// display replacement on machines with automatic graphics
|
||||
// switching
|
||||
uint32_t j;
|
||||
for (j = 0; j < disconnectedCount; j++)
|
||||
{
|
||||
if (disconnected[j] && disconnected[j]->ns.unitNumber == unitNumber)
|
||||
{
|
||||
disconnected[j]->ns.screen = screen;
|
||||
disconnected[j] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (j < disconnectedCount)
|
||||
continue;
|
||||
|
||||
const CGSize size = CGDisplayScreenSize(displays[i]);
|
||||
char* name = getMonitorName(displays[i], screen);
|
||||
if (!name)
|
||||
continue;
|
||||
|
||||
_GLFWmonitor* monitor = _glfwAllocMonitor(name, size.width, size.height);
|
||||
monitor->ns.displayID = displays[i];
|
||||
monitor->ns.unitNumber = unitNumber;
|
||||
monitor->ns.screen = screen;
|
||||
|
||||
free(name);
|
||||
|
||||
CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displays[i]);
|
||||
if (CGDisplayModeGetRefreshRate(mode) == 0.0)
|
||||
monitor->ns.fallbackRefreshRate = getFallbackRefreshRate(displays[i]);
|
||||
CGDisplayModeRelease(mode);
|
||||
|
||||
_glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < disconnectedCount; i++)
|
||||
{
|
||||
if (disconnected[i])
|
||||
_glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0);
|
||||
}
|
||||
|
||||
free(disconnected);
|
||||
free(displays);
|
||||
}
|
||||
|
||||
// Change the current video mode
|
||||
//
|
||||
void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired)
|
||||
{
|
||||
GLFWvidmode current;
|
||||
_glfwPlatformGetVideoMode(monitor, ¤t);
|
||||
|
||||
const GLFWvidmode* best = _glfwChooseVideoMode(monitor, desired);
|
||||
if (_glfwCompareVideoModes(¤t, best) == 0)
|
||||
return;
|
||||
|
||||
CFArrayRef modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL);
|
||||
const CFIndex count = CFArrayGetCount(modes);
|
||||
CGDisplayModeRef native = NULL;
|
||||
|
||||
for (CFIndex i = 0; i < count; i++)
|
||||
{
|
||||
CGDisplayModeRef dm = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i);
|
||||
if (!modeIsGood(dm))
|
||||
continue;
|
||||
|
||||
const GLFWvidmode mode =
|
||||
vidmodeFromCGDisplayMode(dm, monitor->ns.fallbackRefreshRate);
|
||||
if (_glfwCompareVideoModes(best, &mode) == 0)
|
||||
{
|
||||
native = dm;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (native)
|
||||
{
|
||||
if (monitor->ns.previousMode == NULL)
|
||||
monitor->ns.previousMode = CGDisplayCopyDisplayMode(monitor->ns.displayID);
|
||||
|
||||
CGDisplayFadeReservationToken token = beginFadeReservation();
|
||||
CGDisplaySetDisplayMode(monitor->ns.displayID, native, NULL);
|
||||
endFadeReservation(token);
|
||||
}
|
||||
|
||||
CFRelease(modes);
|
||||
}
|
||||
|
||||
// Restore the previously saved (original) video mode
|
||||
//
|
||||
void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor)
|
||||
{
|
||||
if (monitor->ns.previousMode)
|
||||
{
|
||||
CGDisplayFadeReservationToken token = beginFadeReservation();
|
||||
CGDisplaySetDisplayMode(monitor->ns.displayID,
|
||||
monitor->ns.previousMode, NULL);
|
||||
endFadeReservation(token);
|
||||
|
||||
CGDisplayModeRelease(monitor->ns.previousMode);
|
||||
monitor->ns.previousMode = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW platform API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
|
||||
{
|
||||
}
|
||||
|
||||
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
||||
const CGRect bounds = CGDisplayBounds(monitor->ns.displayID);
|
||||
|
||||
if (xpos)
|
||||
*xpos = (int) bounds.origin.x;
|
||||
if (ypos)
|
||||
*ypos = (int) bounds.origin.y;
|
||||
|
||||
} // autoreleasepool
|
||||
}
|
||||
|
||||
void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
|
||||
float* xscale, float* yscale)
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
||||
if (!monitor->ns.screen)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Cocoa: Cannot query content scale without screen");
|
||||
}
|
||||
|
||||
const NSRect points = [monitor->ns.screen frame];
|
||||
const NSRect pixels = [monitor->ns.screen convertRectToBacking:points];
|
||||
|
||||
if (xscale)
|
||||
*xscale = (float) (pixels.size.width / points.size.width);
|
||||
if (yscale)
|
||||
*yscale = (float) (pixels.size.height / points.size.height);
|
||||
|
||||
} // autoreleasepool
|
||||
}
|
||||
|
||||
void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor,
|
||||
int* xpos, int* ypos,
|
||||
int* width, int* height)
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
||||
if (!monitor->ns.screen)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Cocoa: Cannot query workarea without screen");
|
||||
}
|
||||
|
||||
const NSRect frameRect = [monitor->ns.screen visibleFrame];
|
||||
|
||||
if (xpos)
|
||||
*xpos = frameRect.origin.x;
|
||||
if (ypos)
|
||||
*ypos = _glfwTransformYNS(frameRect.origin.y + frameRect.size.height - 1);
|
||||
if (width)
|
||||
*width = frameRect.size.width;
|
||||
if (height)
|
||||
*height = frameRect.size.height;
|
||||
|
||||
} // autoreleasepool
|
||||
}
|
||||
|
||||
GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
||||
*count = 0;
|
||||
|
||||
CFArrayRef modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL);
|
||||
const CFIndex found = CFArrayGetCount(modes);
|
||||
GLFWvidmode* result = calloc(found, sizeof(GLFWvidmode));
|
||||
|
||||
for (CFIndex i = 0; i < found; i++)
|
||||
{
|
||||
CGDisplayModeRef dm = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i);
|
||||
if (!modeIsGood(dm))
|
||||
continue;
|
||||
|
||||
const GLFWvidmode mode =
|
||||
vidmodeFromCGDisplayMode(dm, monitor->ns.fallbackRefreshRate);
|
||||
CFIndex j;
|
||||
|
||||
for (j = 0; j < *count; j++)
|
||||
{
|
||||
if (_glfwCompareVideoModes(result + j, &mode) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip duplicate modes
|
||||
if (j < *count)
|
||||
continue;
|
||||
|
||||
(*count)++;
|
||||
result[*count - 1] = mode;
|
||||
}
|
||||
|
||||
CFRelease(modes);
|
||||
return result;
|
||||
|
||||
} // autoreleasepool
|
||||
}
|
||||
|
||||
void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode *mode)
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
||||
CGDisplayModeRef native = CGDisplayCopyDisplayMode(monitor->ns.displayID);
|
||||
*mode = vidmodeFromCGDisplayMode(native, monitor->ns.fallbackRefreshRate);
|
||||
CGDisplayModeRelease(native);
|
||||
|
||||
} // autoreleasepool
|
||||
}
|
||||
|
||||
GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
||||
uint32_t size = CGDisplayGammaTableCapacity(monitor->ns.displayID);
|
||||
CGGammaValue* values = calloc(size * 3, sizeof(CGGammaValue));
|
||||
|
||||
CGGetDisplayTransferByTable(monitor->ns.displayID,
|
||||
size,
|
||||
values,
|
||||
values + size,
|
||||
values + size * 2,
|
||||
&size);
|
||||
|
||||
_glfwAllocGammaArrays(ramp, size);
|
||||
|
||||
for (uint32_t i = 0; i < size; i++)
|
||||
{
|
||||
ramp->red[i] = (unsigned short) (values[i] * 65535);
|
||||
ramp->green[i] = (unsigned short) (values[i + size] * 65535);
|
||||
ramp->blue[i] = (unsigned short) (values[i + size * 2] * 65535);
|
||||
}
|
||||
|
||||
free(values);
|
||||
return GLFW_TRUE;
|
||||
|
||||
} // autoreleasepool
|
||||
}
|
||||
|
||||
void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
||||
CGGammaValue* values = calloc(ramp->size * 3, sizeof(CGGammaValue));
|
||||
|
||||
for (unsigned int i = 0; i < ramp->size; i++)
|
||||
{
|
||||
values[i] = ramp->red[i] / 65535.f;
|
||||
values[i + ramp->size] = ramp->green[i] / 65535.f;
|
||||
values[i + ramp->size * 2] = ramp->blue[i] / 65535.f;
|
||||
}
|
||||
|
||||
CGSetDisplayTransferByTable(monitor->ns.displayID,
|
||||
ramp->size,
|
||||
values,
|
||||
values + ramp->size,
|
||||
values + ramp->size * 2);
|
||||
|
||||
free(values);
|
||||
|
||||
} // autoreleasepool
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW native API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* handle)
|
||||
{
|
||||
_GLFWmonitor* monitor = (_GLFWmonitor*) handle;
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(kCGNullDirectDisplay);
|
||||
return monitor->ns.displayID;
|
||||
}
|
||||
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2009-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
#include <stdint.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <Carbon/Carbon.h>
|
||||
|
||||
// NOTE: All of NSGL was deprecated in the 10.14 SDK
|
||||
// This disables the pointless warnings for every symbol we use
|
||||
#ifndef GL_SILENCE_DEPRECATION
|
||||
#define GL_SILENCE_DEPRECATION
|
||||
#endif
|
||||
|
||||
#if defined(__OBJC__)
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#else
|
||||
typedef void* id;
|
||||
#endif
|
||||
|
||||
// NOTE: Many Cocoa enum values have been renamed and we need to build across
|
||||
// SDK versions where one is unavailable or deprecated.
|
||||
// We use the newer names in code and replace them with the older names if
|
||||
// the base SDK does not provide the newer names.
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED < 101200
|
||||
#define NSBitmapFormatAlphaNonpremultiplied NSAlphaNonpremultipliedBitmapFormat
|
||||
#define NSEventMaskAny NSAnyEventMask
|
||||
#define NSEventMaskKeyUp NSKeyUpMask
|
||||
#define NSEventModifierFlagCapsLock NSAlphaShiftKeyMask
|
||||
#define NSEventModifierFlagCommand NSCommandKeyMask
|
||||
#define NSEventModifierFlagControl NSControlKeyMask
|
||||
#define NSEventModifierFlagDeviceIndependentFlagsMask NSDeviceIndependentModifierFlagsMask
|
||||
#define NSEventModifierFlagOption NSAlternateKeyMask
|
||||
#define NSEventModifierFlagShift NSShiftKeyMask
|
||||
#define NSEventTypeApplicationDefined NSApplicationDefined
|
||||
#define NSWindowStyleMaskBorderless NSBorderlessWindowMask
|
||||
#define NSWindowStyleMaskClosable NSClosableWindowMask
|
||||
#define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask
|
||||
#define NSWindowStyleMaskResizable NSResizableWindowMask
|
||||
#define NSWindowStyleMaskTitled NSTitledWindowMask
|
||||
#endif
|
||||
|
||||
// NOTE: Many Cocoa dynamically linked constants have been renamed and we need
|
||||
// to build across SDK versions where one is unavailable or deprecated.
|
||||
// We use the newer names in code and replace them with the older names if
|
||||
// the deployment target is older than the newer names.
|
||||
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 101300
|
||||
#define NSPasteboardTypeURL NSURLPboardType
|
||||
#endif
|
||||
|
||||
#include "posix_thread_unix.h"
|
||||
#include "nsgl_context_darwin.h"
|
||||
#include "egl_context_unix.h"
|
||||
#include "osmesa_context_unix.h"
|
||||
|
||||
#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL)
|
||||
#define _glfw_dlclose(handle) dlclose(handle)
|
||||
#define _glfw_dlsym(handle, name) dlsym(handle, name)
|
||||
|
||||
#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->ns.layer)
|
||||
#define _GLFW_EGL_NATIVE_DISPLAY EGL_DEFAULT_DISPLAY
|
||||
|
||||
#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNS ns
|
||||
#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryNS ns
|
||||
#define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerNS ns
|
||||
#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorNS ns
|
||||
#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorNS ns
|
||||
|
||||
// HIToolbox.framework pointer typedefs
|
||||
#define kTISPropertyUnicodeKeyLayoutData _glfw.ns.tis.kPropertyUnicodeKeyLayoutData
|
||||
typedef TISInputSourceRef (*PFN_TISCopyCurrentKeyboardLayoutInputSource)(void);
|
||||
#define TISCopyCurrentKeyboardLayoutInputSource _glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource
|
||||
typedef void* (*PFN_TISGetInputSourceProperty)(TISInputSourceRef,CFStringRef);
|
||||
#define TISGetInputSourceProperty _glfw.ns.tis.GetInputSourceProperty
|
||||
typedef UInt8 (*PFN_LMGetKbdType)(void);
|
||||
#define LMGetKbdType _glfw.ns.tis.GetKbdType
|
||||
|
||||
|
||||
// Cocoa-specific per-window data
|
||||
//
|
||||
typedef struct _GLFWwindowNS
|
||||
{
|
||||
id object;
|
||||
id delegate;
|
||||
id view;
|
||||
id layer;
|
||||
|
||||
GLFWbool maximized;
|
||||
GLFWbool occluded;
|
||||
GLFWbool retina;
|
||||
|
||||
// Cached window properties to filter out duplicate events
|
||||
int width, height;
|
||||
int fbWidth, fbHeight;
|
||||
float xscale, yscale;
|
||||
|
||||
// The total sum of the distances the cursor has been warped
|
||||
// since the last cursor motion event was processed
|
||||
// This is kept to counteract Cocoa doing the same internally
|
||||
double cursorWarpDeltaX, cursorWarpDeltaY;
|
||||
} _GLFWwindowNS;
|
||||
|
||||
// Cocoa-specific global data
|
||||
//
|
||||
typedef struct _GLFWlibraryNS
|
||||
{
|
||||
CGEventSourceRef eventSource;
|
||||
id delegate;
|
||||
GLFWbool finishedLaunching;
|
||||
GLFWbool cursorHidden;
|
||||
TISInputSourceRef inputSource;
|
||||
id unicodeData;
|
||||
id helper;
|
||||
id keyUpMonitor;
|
||||
id nibObjects;
|
||||
|
||||
char keynames[GLFW_KEY_LAST + 1][17];
|
||||
short int keycodes[256];
|
||||
short int scancodes[GLFW_KEY_LAST + 1];
|
||||
char* clipboardString;
|
||||
CGPoint cascadePoint;
|
||||
// Where to place the cursor when re-enabled
|
||||
double restoreCursorPosX, restoreCursorPosY;
|
||||
// The window whose disabled cursor mode is active
|
||||
_GLFWwindow* disabledCursorWindow;
|
||||
|
||||
struct {
|
||||
CFBundleRef bundle;
|
||||
PFN_TISCopyCurrentKeyboardLayoutInputSource CopyCurrentKeyboardLayoutInputSource;
|
||||
PFN_TISGetInputSourceProperty GetInputSourceProperty;
|
||||
PFN_LMGetKbdType GetKbdType;
|
||||
CFStringRef kPropertyUnicodeKeyLayoutData;
|
||||
} tis;
|
||||
} _GLFWlibraryNS;
|
||||
|
||||
// Cocoa-specific per-monitor data
|
||||
//
|
||||
typedef struct _GLFWmonitorNS
|
||||
{
|
||||
CGDirectDisplayID displayID;
|
||||
CGDisplayModeRef previousMode;
|
||||
uint32_t unitNumber;
|
||||
id screen;
|
||||
double fallbackRefreshRate;
|
||||
} _GLFWmonitorNS;
|
||||
|
||||
// Cocoa-specific per-cursor data
|
||||
//
|
||||
typedef struct _GLFWcursorNS
|
||||
{
|
||||
id object;
|
||||
} _GLFWcursorNS;
|
||||
|
||||
// Cocoa-specific global timer data
|
||||
//
|
||||
typedef struct _GLFWtimerNS
|
||||
{
|
||||
uint64_t frequency;
|
||||
} _GLFWtimerNS;
|
||||
|
||||
|
||||
void _glfwInitTimerNS(void);
|
||||
|
||||
void _glfwPollMonitorsNS(void);
|
||||
void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired);
|
||||
void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor);
|
||||
|
||||
float _glfwTransformYNS(float y);
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2009-2016 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
|
||||
|
||||
package glfw
|
||||
|
||||
// #include "internal_unix.h"
|
||||
import "C"
|
||||
|
||||
//export _glfwInitTimerNS
|
||||
func _glfwInitTimerNS() {
|
||||
var info mach_timebase_info_data_t
|
||||
mach_timebase_info(&info)
|
||||
C._glfw.timer.ns.frequency = C.ulonglong(info.denom*1e9) / C.ulonglong(info.numer)
|
||||
}
|
||||
|
||||
//export _glfwPlatformGetTimerValue
|
||||
func _glfwPlatformGetTimerValue() uint64 {
|
||||
return mach_absolute_time()
|
||||
}
|
||||
|
||||
//export _glfwPlatformGetTimerFrequency
|
||||
func _glfwPlatformGetTimerFrequency() uint64 {
|
||||
return uint64(C._glfw.timer.ns.frequency)
|
||||
}
|
||||
+1854
File diff suppressed because it is too large
Load Diff
+204
@@ -0,0 +1,204 @@
|
||||
// Copyright 2018 The Ebiten Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build darwin || freebsd || linux || netbsd || openbsd || windows
|
||||
|
||||
package glfw
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type (
|
||||
Action int
|
||||
ErrorCode int
|
||||
Hint int
|
||||
InputMode int
|
||||
Key int
|
||||
ModifierKey int
|
||||
MouseButton int
|
||||
PeripheralEvent int
|
||||
StandardCursor int
|
||||
)
|
||||
|
||||
const (
|
||||
DontCare = -1
|
||||
False = 0
|
||||
True = 1
|
||||
)
|
||||
|
||||
const (
|
||||
Release = Action(0)
|
||||
Press = Action(1)
|
||||
Repeat = Action(2)
|
||||
)
|
||||
|
||||
const (
|
||||
ModAlt = ModifierKey(0x0004)
|
||||
ModCapsLock = ModifierKey(0x0010)
|
||||
ModControl = ModifierKey(0x0002)
|
||||
ModNumLock = ModifierKey(0x0020)
|
||||
ModShift = ModifierKey(0x0001)
|
||||
ModSuper = ModifierKey(0x0008)
|
||||
)
|
||||
|
||||
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 = MouseButton8
|
||||
MouseButtonLeft = MouseButton1
|
||||
MouseButtonRight = MouseButton2
|
||||
MouseButtonMiddle = MouseButton3
|
||||
)
|
||||
|
||||
const (
|
||||
AccumAlphaBits = Hint(0x0002100A)
|
||||
AccumBlueBits = Hint(0x00021009)
|
||||
AccumGreenBits = Hint(0x00021008)
|
||||
AccumRedBits = Hint(0x00021007)
|
||||
AlphaBits = Hint(0x00021004)
|
||||
AutoIconify = Hint(0x00020006)
|
||||
AuxBuffers = Hint(0x0002100B)
|
||||
BlueBits = Hint(0x00021003)
|
||||
CenterCursor = Hint(0x00020009)
|
||||
ClientAPI = Hint(0x00022001)
|
||||
ContextCreationAPI = Hint(0x0002200B)
|
||||
ContextNoError = Hint(0x0002200A)
|
||||
ContextReleaseBehavior = Hint(0x00022009)
|
||||
ContextRevision = Hint(0x00022004)
|
||||
ContextRobustness = Hint(0x00022005)
|
||||
ContextVersionMajor = Hint(0x00022002)
|
||||
ContextVersionMinor = Hint(0x00022003)
|
||||
Decorated = Hint(0x00020005)
|
||||
DepthBits = Hint(0x00021005)
|
||||
DoubleBuffer = Hint(0x00021010)
|
||||
Floating = Hint(0x00020007)
|
||||
Focused = Hint(0x00020001)
|
||||
FocusOnShow = Hint(0x0002000C)
|
||||
GreenBits = Hint(0x00021002)
|
||||
Hovered = Hint(0x0002000B)
|
||||
Iconified = Hint(0x00020002)
|
||||
Maximized = Hint(0x00020008)
|
||||
MousePassthrough = Hint(0x0002000D)
|
||||
OpenGLDebugContext = Hint(0x00022007)
|
||||
OpenGLForwardCompat = Hint(0x00022006)
|
||||
OpenGLProfile = Hint(0x00022008)
|
||||
RedBits = Hint(0x00021001)
|
||||
RefreshRate = Hint(0x0002100F)
|
||||
Resizable = Hint(0x00020003)
|
||||
Samples = Hint(0x0002100D)
|
||||
ScaleToMonitor = Hint(0x0002200C)
|
||||
SRGBCapable = Hint(0x0002100E)
|
||||
StencilBits = Hint(0x00021006)
|
||||
Stereo = Hint(0x0002100C)
|
||||
TransparentFramebuffer = Hint(0x0002000A)
|
||||
Visible = Hint(0x00020004)
|
||||
X11ClassName = Hint(0x00024001)
|
||||
X11InstanceName = Hint(0x00024002)
|
||||
)
|
||||
|
||||
const (
|
||||
CursorMode = InputMode(0x00033001)
|
||||
LockKeyMods = InputMode(0x00033004)
|
||||
RawMouseMotion = InputMode(0x00033005)
|
||||
StickyKeysMode = InputMode(0x00033002)
|
||||
StickyMouseButtonsMode = InputMode(0x00033003)
|
||||
)
|
||||
|
||||
const (
|
||||
AnyReleaseBehavior = 0
|
||||
CursorDisabled = 0x00034003
|
||||
CursorHidden = 0x00034002
|
||||
CursorNormal = 0x00034001
|
||||
EGLContextAPI = 0x00036002
|
||||
LoseContextOnReset = 0x00031002
|
||||
NativeContextAPI = 0x00036001
|
||||
NoAPI = 0
|
||||
NoResetNotification = 0x00031001
|
||||
NoRobustness = 0
|
||||
OpenGLAPI = 0x00030001
|
||||
OpenGLAnyProfile = 0
|
||||
OpenGLCompatProfile = 0x00032002
|
||||
OpenGLCoreProfile = 0x00032001
|
||||
OpenGLESAPI = 0x00030002
|
||||
OSMesaContextAPI = 0x00036003
|
||||
ReleaseBehaviorFlush = 0x00035001
|
||||
ReleaseBehaviorNone = 0x00035002
|
||||
)
|
||||
|
||||
const (
|
||||
NotInitialized = ErrorCode(0x00010001)
|
||||
NoCurrentContext = ErrorCode(0x00010002)
|
||||
InvalidEnum = ErrorCode(0x00010003)
|
||||
InvalidValue = ErrorCode(0x00010004)
|
||||
OutOfMemory = ErrorCode(0x00010005)
|
||||
APIUnavailable = ErrorCode(0x00010006)
|
||||
VersionUnavailable = ErrorCode(0x00010007)
|
||||
PlatformError = ErrorCode(0x00010008)
|
||||
FormatUnavailable = ErrorCode(0x00010009)
|
||||
NoWindowContext = ErrorCode(0x0001000A)
|
||||
)
|
||||
|
||||
func (e ErrorCode) 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 fmt.Sprintf("GLFW error (%d)", e)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
Connected = PeripheralEvent(0x00040001)
|
||||
Disconnected = PeripheralEvent(0x00040002)
|
||||
)
|
||||
|
||||
const (
|
||||
ArrowCursor = StandardCursor(0x00036001)
|
||||
IBeamCursor = StandardCursor(0x00036002)
|
||||
CrosshairCursor = StandardCursor(0x00036003)
|
||||
HandCursor = StandardCursor(0x00036004)
|
||||
HResizeCursor = StandardCursor(0x00036005)
|
||||
VResizeCursor = StandardCursor(0x00036006)
|
||||
|
||||
// v3.4
|
||||
ResizeNWSECursor = StandardCursor(0x00036007)
|
||||
ResizeNESWCursor = StandardCursor(0x00036008)
|
||||
ResizeAllCursor = StandardCursor(0x00036009)
|
||||
NotAllowedCursor = StandardCursor(0x0003600A)
|
||||
)
|
||||
+736
@@ -0,0 +1,736 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2016 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || netbsd || openbsd
|
||||
|
||||
#include "internal_unix.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW internal API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Checks whether the desired context attributes are valid
|
||||
//
|
||||
// This function checks things like whether the specified client API version
|
||||
// exists and whether all relevant options have supported and non-conflicting
|
||||
// values
|
||||
//
|
||||
GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig)
|
||||
{
|
||||
if (ctxconfig->share)
|
||||
{
|
||||
if (ctxconfig->client == GLFW_NO_API ||
|
||||
ctxconfig->share->context.client == GLFW_NO_API)
|
||||
{
|
||||
_glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctxconfig->source != GLFW_NATIVE_CONTEXT_API &&
|
||||
ctxconfig->source != GLFW_EGL_CONTEXT_API &&
|
||||
ctxconfig->source != GLFW_OSMESA_CONTEXT_API)
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_ENUM,
|
||||
"Invalid context creation API 0x%08X",
|
||||
ctxconfig->source);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (ctxconfig->client != GLFW_NO_API &&
|
||||
ctxconfig->client != GLFW_OPENGL_API &&
|
||||
ctxconfig->client != GLFW_OPENGL_ES_API)
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_ENUM,
|
||||
"Invalid client API 0x%08X",
|
||||
ctxconfig->client);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (ctxconfig->client == GLFW_OPENGL_API)
|
||||
{
|
||||
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
|
||||
|
||||
_glfwInputError(GLFW_INVALID_VALUE,
|
||||
"Invalid OpenGL version %i.%i",
|
||||
ctxconfig->major, ctxconfig->minor);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (ctxconfig->profile)
|
||||
{
|
||||
if (ctxconfig->profile != GLFW_OPENGL_CORE_PROFILE &&
|
||||
ctxconfig->profile != GLFW_OPENGL_COMPAT_PROFILE)
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_ENUM,
|
||||
"Invalid OpenGL profile 0x%08X",
|
||||
ctxconfig->profile);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (ctxconfig->major <= 2 ||
|
||||
(ctxconfig->major == 3 && ctxconfig->minor < 2))
|
||||
{
|
||||
// Desktop OpenGL context profiles are only defined for version 3.2
|
||||
// and above
|
||||
|
||||
_glfwInputError(GLFW_INVALID_VALUE,
|
||||
"Context profiles are only defined for OpenGL version 3.2 and above");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctxconfig->forward && ctxconfig->major <= 2)
|
||||
{
|
||||
// Forward-compatible contexts are only defined for OpenGL version 3.0 and above
|
||||
_glfwInputError(GLFW_INVALID_VALUE,
|
||||
"Forward-compatibility is only defined for OpenGL version 3.0 and above");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
}
|
||||
else if (ctxconfig->client == GLFW_OPENGL_ES_API)
|
||||
{
|
||||
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
|
||||
|
||||
_glfwInputError(GLFW_INVALID_VALUE,
|
||||
"Invalid OpenGL ES version %i.%i",
|
||||
ctxconfig->major, ctxconfig->minor);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctxconfig->robustness)
|
||||
{
|
||||
if (ctxconfig->robustness != GLFW_NO_RESET_NOTIFICATION &&
|
||||
ctxconfig->robustness != GLFW_LOSE_CONTEXT_ON_RESET)
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_ENUM,
|
||||
"Invalid context robustness mode 0x%08X",
|
||||
ctxconfig->robustness);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctxconfig->release)
|
||||
{
|
||||
if (ctxconfig->release != GLFW_RELEASE_BEHAVIOR_NONE &&
|
||||
ctxconfig->release != GLFW_RELEASE_BEHAVIOR_FLUSH)
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_ENUM,
|
||||
"Invalid context release behavior 0x%08X",
|
||||
ctxconfig->release);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
// Chooses the framebuffer config that best matches the desired one
|
||||
//
|
||||
const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired,
|
||||
const _GLFWfbconfig* alternatives,
|
||||
unsigned int count)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int missing, leastMissing = UINT_MAX;
|
||||
unsigned int colorDiff, leastColorDiff = UINT_MAX;
|
||||
unsigned int extraDiff, leastExtraDiff = UINT_MAX;
|
||||
const _GLFWfbconfig* current;
|
||||
const _GLFWfbconfig* closest = NULL;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
current = alternatives + i;
|
||||
|
||||
if (desired->stereo > 0 && current->stereo == 0)
|
||||
{
|
||||
// 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 != GLFW_DONT_CARE)
|
||||
{
|
||||
colorDiff += (desired->redBits - current->redBits) *
|
||||
(desired->redBits - current->redBits);
|
||||
}
|
||||
|
||||
if (desired->greenBits != GLFW_DONT_CARE)
|
||||
{
|
||||
colorDiff += (desired->greenBits - current->greenBits) *
|
||||
(desired->greenBits - current->greenBits);
|
||||
}
|
||||
|
||||
if (desired->blueBits != GLFW_DONT_CARE)
|
||||
{
|
||||
colorDiff += (desired->blueBits - current->blueBits) *
|
||||
(desired->blueBits - current->blueBits);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate non-color channel size difference value
|
||||
{
|
||||
extraDiff = 0;
|
||||
|
||||
if (desired->alphaBits != GLFW_DONT_CARE)
|
||||
{
|
||||
extraDiff += (desired->alphaBits - current->alphaBits) *
|
||||
(desired->alphaBits - current->alphaBits);
|
||||
}
|
||||
|
||||
if (desired->depthBits != GLFW_DONT_CARE)
|
||||
{
|
||||
extraDiff += (desired->depthBits - current->depthBits) *
|
||||
(desired->depthBits - current->depthBits);
|
||||
}
|
||||
|
||||
if (desired->stencilBits != GLFW_DONT_CARE)
|
||||
{
|
||||
extraDiff += (desired->stencilBits - current->stencilBits) *
|
||||
(desired->stencilBits - current->stencilBits);
|
||||
}
|
||||
|
||||
if (desired->accumRedBits != GLFW_DONT_CARE)
|
||||
{
|
||||
extraDiff += (desired->accumRedBits - current->accumRedBits) *
|
||||
(desired->accumRedBits - current->accumRedBits);
|
||||
}
|
||||
|
||||
if (desired->accumGreenBits != GLFW_DONT_CARE)
|
||||
{
|
||||
extraDiff += (desired->accumGreenBits - current->accumGreenBits) *
|
||||
(desired->accumGreenBits - current->accumGreenBits);
|
||||
}
|
||||
|
||||
if (desired->accumBlueBits != GLFW_DONT_CARE)
|
||||
{
|
||||
extraDiff += (desired->accumBlueBits - current->accumBlueBits) *
|
||||
(desired->accumBlueBits - current->accumBlueBits);
|
||||
}
|
||||
|
||||
if (desired->accumAlphaBits != GLFW_DONT_CARE)
|
||||
{
|
||||
extraDiff += (desired->accumAlphaBits - current->accumAlphaBits) *
|
||||
(desired->accumAlphaBits - current->accumAlphaBits);
|
||||
}
|
||||
|
||||
if (desired->samples != GLFW_DONT_CARE)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// Retrieves the attributes of the current context
|
||||
//
|
||||
GLFWbool _glfwRefreshContextAttribs(_GLFWwindow* window,
|
||||
const _GLFWctxconfig* ctxconfig)
|
||||
{
|
||||
int i;
|
||||
_GLFWwindow* previous;
|
||||
const char* version;
|
||||
const char* prefixes[] =
|
||||
{
|
||||
"OpenGL ES-CM ",
|
||||
"OpenGL ES-CL ",
|
||||
"OpenGL ES ",
|
||||
NULL
|
||||
};
|
||||
|
||||
window->context.source = ctxconfig->source;
|
||||
window->context.client = GLFW_OPENGL_API;
|
||||
|
||||
previous = _glfwPlatformGetTls(&_glfw.contextSlot);
|
||||
glfwMakeContextCurrent((GLFWwindow*) window);
|
||||
|
||||
window->context.GetIntegerv = (PFNGLGETINTEGERVPROC)
|
||||
window->context.getProcAddress("glGetIntegerv");
|
||||
window->context.GetString = (PFNGLGETSTRINGPROC)
|
||||
window->context.getProcAddress("glGetString");
|
||||
if (!window->context.GetIntegerv || !window->context.GetString)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Entry point retrieval is broken");
|
||||
glfwMakeContextCurrent((GLFWwindow*) previous);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
version = (const char*) window->context.GetString(GL_VERSION);
|
||||
if (!version)
|
||||
{
|
||||
if (ctxconfig->client == GLFW_OPENGL_API)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"OpenGL version string retrieval is broken");
|
||||
}
|
||||
else
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"OpenGL ES version string retrieval is broken");
|
||||
}
|
||||
|
||||
glfwMakeContextCurrent((GLFWwindow*) previous);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
for (i = 0; prefixes[i]; i++)
|
||||
{
|
||||
const size_t length = strlen(prefixes[i]);
|
||||
|
||||
if (strncmp(version, prefixes[i], length) == 0)
|
||||
{
|
||||
version += length;
|
||||
window->context.client = GLFW_OPENGL_ES_API;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sscanf(version, "%d.%d.%d",
|
||||
&window->context.major,
|
||||
&window->context.minor,
|
||||
&window->context.revision))
|
||||
{
|
||||
if (window->context.client == GLFW_OPENGL_API)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"No version found in OpenGL version string");
|
||||
}
|
||||
else
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"No version found in OpenGL ES version string");
|
||||
}
|
||||
|
||||
glfwMakeContextCurrent((GLFWwindow*) previous);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (window->context.major < ctxconfig->major ||
|
||||
(window->context.major == ctxconfig->major &&
|
||||
window->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 (window->context.client == GLFW_OPENGL_API)
|
||||
{
|
||||
_glfwInputError(GLFW_VERSION_UNAVAILABLE,
|
||||
"Requested OpenGL version %i.%i, got version %i.%i",
|
||||
ctxconfig->major, ctxconfig->minor,
|
||||
window->context.major, window->context.minor);
|
||||
}
|
||||
else
|
||||
{
|
||||
_glfwInputError(GLFW_VERSION_UNAVAILABLE,
|
||||
"Requested OpenGL ES version %i.%i, got version %i.%i",
|
||||
ctxconfig->major, ctxconfig->minor,
|
||||
window->context.major, window->context.minor);
|
||||
}
|
||||
|
||||
glfwMakeContextCurrent((GLFWwindow*) previous);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (window->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
|
||||
|
||||
window->context.GetStringi = (PFNGLGETSTRINGIPROC)
|
||||
window->context.getProcAddress("glGetStringi");
|
||||
if (!window->context.GetStringi)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Entry point retrieval is broken");
|
||||
glfwMakeContextCurrent((GLFWwindow*) previous);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (window->context.client == GLFW_OPENGL_API)
|
||||
{
|
||||
// Read back context flags (OpenGL 3.0 and above)
|
||||
if (window->context.major >= 3)
|
||||
{
|
||||
GLint flags;
|
||||
window->context.GetIntegerv(GL_CONTEXT_FLAGS, &flags);
|
||||
|
||||
if (flags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT)
|
||||
window->context.forward = GLFW_TRUE;
|
||||
|
||||
if (flags & GL_CONTEXT_FLAG_DEBUG_BIT)
|
||||
window->context.debug = GLFW_TRUE;
|
||||
else if (glfwExtensionSupported("GL_ARB_debug_output") &&
|
||||
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
|
||||
window->context.debug = GLFW_TRUE;
|
||||
}
|
||||
|
||||
if (flags & GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR)
|
||||
window->context.noerror = GLFW_TRUE;
|
||||
}
|
||||
|
||||
// Read back OpenGL context profile (OpenGL 3.2 and above)
|
||||
if (window->context.major >= 4 ||
|
||||
(window->context.major == 3 && window->context.minor >= 2))
|
||||
{
|
||||
GLint mask;
|
||||
window->context.GetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask);
|
||||
|
||||
if (mask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT)
|
||||
window->context.profile = GLFW_OPENGL_COMPAT_PROFILE;
|
||||
else if (mask & GL_CONTEXT_CORE_PROFILE_BIT)
|
||||
window->context.profile = GLFW_OPENGL_CORE_PROFILE;
|
||||
else if (glfwExtensionSupported("GL_ARB_compatibility"))
|
||||
{
|
||||
// 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
|
||||
window->context.profile = GLFW_OPENGL_COMPAT_PROFILE;
|
||||
}
|
||||
}
|
||||
|
||||
// Read back robustness strategy
|
||||
if (glfwExtensionSupported("GL_ARB_robustness"))
|
||||
{
|
||||
// NOTE: We avoid using the context flags for detection, as they are
|
||||
// only present from 3.0 while the extension applies from 1.1
|
||||
|
||||
GLint strategy;
|
||||
window->context.GetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB,
|
||||
&strategy);
|
||||
|
||||
if (strategy == GL_LOSE_CONTEXT_ON_RESET_ARB)
|
||||
window->context.robustness = GLFW_LOSE_CONTEXT_ON_RESET;
|
||||
else if (strategy == GL_NO_RESET_NOTIFICATION_ARB)
|
||||
window->context.robustness = GLFW_NO_RESET_NOTIFICATION;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read back robustness strategy
|
||||
if (glfwExtensionSupported("GL_EXT_robustness"))
|
||||
{
|
||||
// NOTE: The values of these constants match those of the OpenGL ARB
|
||||
// one, so we can reuse them here
|
||||
|
||||
GLint strategy;
|
||||
window->context.GetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB,
|
||||
&strategy);
|
||||
|
||||
if (strategy == GL_LOSE_CONTEXT_ON_RESET_ARB)
|
||||
window->context.robustness = GLFW_LOSE_CONTEXT_ON_RESET;
|
||||
else if (strategy == GL_NO_RESET_NOTIFICATION_ARB)
|
||||
window->context.robustness = GLFW_NO_RESET_NOTIFICATION;
|
||||
}
|
||||
}
|
||||
|
||||
if (glfwExtensionSupported("GL_KHR_context_flush_control"))
|
||||
{
|
||||
GLint behavior;
|
||||
window->context.GetIntegerv(GL_CONTEXT_RELEASE_BEHAVIOR, &behavior);
|
||||
|
||||
if (behavior == GL_NONE)
|
||||
window->context.release = GLFW_RELEASE_BEHAVIOR_NONE;
|
||||
else if (behavior == GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH)
|
||||
window->context.release = GLFW_RELEASE_BEHAVIOR_FLUSH;
|
||||
}
|
||||
|
||||
// Clearing the front buffer to black to avoid garbage pixels left over from
|
||||
// previous uses of our bit of VRAM
|
||||
{
|
||||
PFNGLCLEARPROC glClear = (PFNGLCLEARPROC)
|
||||
window->context.getProcAddress("glClear");
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
if (window->doublebuffer)
|
||||
window->context.swapBuffers(window);
|
||||
}
|
||||
|
||||
glfwMakeContextCurrent((GLFWwindow*) previous);
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
// Searches an extension string for the specified extension
|
||||
//
|
||||
GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions)
|
||||
{
|
||||
const char* start = extensions;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const char* where;
|
||||
const char* terminator;
|
||||
|
||||
where = strstr(start, string);
|
||||
if (!where)
|
||||
return GLFW_FALSE;
|
||||
|
||||
terminator = where + strlen(string);
|
||||
if (where == start || *(where - 1) == ' ')
|
||||
{
|
||||
if (*terminator == ' ' || *terminator == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
start = terminator;
|
||||
}
|
||||
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW public API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
_GLFWwindow* previous;
|
||||
|
||||
_GLFW_REQUIRE_INIT();
|
||||
|
||||
previous = _glfwPlatformGetTls(&_glfw.contextSlot);
|
||||
|
||||
if (window && window->context.client == GLFW_NO_API)
|
||||
{
|
||||
_glfwInputError(GLFW_NO_WINDOW_CONTEXT,
|
||||
"Cannot make current with a window that has no OpenGL or OpenGL ES context");
|
||||
return;
|
||||
}
|
||||
|
||||
if (previous)
|
||||
{
|
||||
if (!window || window->context.source != previous->context.source)
|
||||
previous->context.makeCurrent(NULL);
|
||||
}
|
||||
|
||||
if (window)
|
||||
window->context.makeCurrent(window);
|
||||
}
|
||||
|
||||
GLFWAPI GLFWwindow* glfwGetCurrentContext(void)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
return _glfwPlatformGetTls(&_glfw.contextSlot);
|
||||
}
|
||||
|
||||
GLFWAPI void glfwSwapBuffers(GLFWwindow* handle)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT();
|
||||
|
||||
if (window->context.client == GLFW_NO_API)
|
||||
{
|
||||
_glfwInputError(GLFW_NO_WINDOW_CONTEXT,
|
||||
"Cannot swap buffers of a window that has no OpenGL or OpenGL ES context");
|
||||
return;
|
||||
}
|
||||
|
||||
window->context.swapBuffers(window);
|
||||
}
|
||||
|
||||
GLFWAPI void glfwSwapInterval(int interval)
|
||||
{
|
||||
_GLFWwindow* window;
|
||||
|
||||
_GLFW_REQUIRE_INIT();
|
||||
|
||||
window = _glfwPlatformGetTls(&_glfw.contextSlot);
|
||||
if (!window)
|
||||
{
|
||||
_glfwInputError(GLFW_NO_CURRENT_CONTEXT,
|
||||
"Cannot set swap interval without a current OpenGL or OpenGL ES context");
|
||||
return;
|
||||
}
|
||||
|
||||
window->context.swapInterval(interval);
|
||||
}
|
||||
|
||||
GLFWAPI int glfwExtensionSupported(const char* extension)
|
||||
{
|
||||
_GLFWwindow* window;
|
||||
assert(extension != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
|
||||
|
||||
window = _glfwPlatformGetTls(&_glfw.contextSlot);
|
||||
if (!window)
|
||||
{
|
||||
_glfwInputError(GLFW_NO_CURRENT_CONTEXT,
|
||||
"Cannot query extension without a current OpenGL or OpenGL ES context");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (*extension == '\0')
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_VALUE, "Extension name cannot be an empty string");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (window->context.major >= 3)
|
||||
{
|
||||
int i;
|
||||
GLint count;
|
||||
|
||||
// Check if extension is in the modern OpenGL extensions string list
|
||||
|
||||
window->context.GetIntegerv(GL_NUM_EXTENSIONS, &count);
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
const char* en = (const char*)
|
||||
window->context.GetStringi(GL_EXTENSIONS, i);
|
||||
if (!en)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Extension string retrieval is broken");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (strcmp(en, extension) == 0)
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check if extension is in the old style OpenGL extensions string
|
||||
|
||||
const char* extensions = (const char*)
|
||||
window->context.GetString(GL_EXTENSIONS);
|
||||
if (!extensions)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Extension string retrieval is broken");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (_glfwStringInExtensionString(extension, extensions))
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
// Check if extension is in the platform-specific string
|
||||
return window->context.extensionSupported(extension);
|
||||
}
|
||||
|
||||
GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname)
|
||||
{
|
||||
_GLFWwindow* window;
|
||||
assert(procname != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
|
||||
window = _glfwPlatformGetTls(&_glfw.contextSlot);
|
||||
if (!window)
|
||||
{
|
||||
_glfwInputError(GLFW_NO_CURRENT_CONTEXT,
|
||||
"Cannot query entry point without a current OpenGL or OpenGL ES context");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return window->context.getProcAddress(procname);
|
||||
}
|
||||
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2012 The glfw3-go Authors
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || netbsd || openbsd
|
||||
|
||||
package glfw
|
||||
|
||||
//#include <stdlib.h>
|
||||
//#define GLFW_INCLUDE_NONE
|
||||
//#include "glfw3_unix.h"
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// MakeContextCurrent makes the context of the window current.
|
||||
// Originally GLFW 3 passes a null pointer to detach the context.
|
||||
// But since we're using receivers, DetachCurrentContext should
|
||||
// be used instead.
|
||||
func (w *Window) MakeContextCurrent() error {
|
||||
C.glfwMakeContextCurrent(w.data)
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DetachCurrentContext detaches the current context.
|
||||
func DetachCurrentContext() error {
|
||||
C.glfwMakeContextCurrent(nil)
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetCurrentContext returns the window whose context is current.
|
||||
func GetCurrentContext() (*Window, error) {
|
||||
w := C.glfwGetCurrentContext()
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if w == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return windows.get(w), nil
|
||||
}
|
||||
|
||||
// SwapBuffers swaps the front and back buffers of the window. If the
|
||||
// swap interval is greater than zero, the GPU driver waits the specified number
|
||||
// of screen updates before swapping the buffers.
|
||||
func (w *Window) SwapBuffers() error {
|
||||
C.glfwSwapBuffers(w.data)
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SwapInterval sets the swap interval for the current context, i.e. the number
|
||||
// of screen updates to wait before swapping the buffers of a window and
|
||||
// returning from SwapBuffers. This is sometimes called
|
||||
// 'vertical synchronization', 'vertical retrace synchronization' or 'vsync'.
|
||||
//
|
||||
// Contexts that support either of the WGL_EXT_swap_control_tear and
|
||||
// GLX_EXT_swap_control_tear extensions also accept negative swap intervals,
|
||||
// which allow the driver to swap even if a frame arrives a little bit late.
|
||||
// You can check for the presence of these extensions using
|
||||
// ExtensionSupported. For more information about swap tearing,
|
||||
// see the extension specifications.
|
||||
//
|
||||
// Some GPU drivers do not honor the requested swap interval, either because of
|
||||
// user settings that override the request or due to bugs in the driver.
|
||||
func SwapInterval(interval int) error {
|
||||
C.glfwSwapInterval(C.int(interval))
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExtensionSupported reports whether the specified OpenGL or context creation
|
||||
// API extension is supported by the current context. For example, on Windows
|
||||
// both the OpenGL and WGL extension strings are checked.
|
||||
//
|
||||
// As this functions searches one or more extension strings on each call, it is
|
||||
// recommended that you cache its results if it's going to be used frequently.
|
||||
// The extension strings will not change during the lifetime of a context, so
|
||||
// there is no danger in doing this.
|
||||
func ExtensionSupported(extension string) (bool, error) {
|
||||
e := C.CString(extension)
|
||||
defer C.free(unsafe.Pointer(e))
|
||||
ret := C.glfwExtensionSupported(e) != 0
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// GetProcAddress returns the address of the specified OpenGL or OpenGL ES core
|
||||
// or extension function, if it is supported by the current context.
|
||||
//
|
||||
// A context must be current on the calling thread. Calling this function
|
||||
// without a current context will cause a GLFW_NO_CURRENT_CONTEXT error.
|
||||
//
|
||||
// This function is used to provide GL proc resolving capabilities to an
|
||||
// external C library.
|
||||
func GetProcAddress(procname string) (unsafe.Pointer, error) {
|
||||
p := C.CString(procname)
|
||||
defer C.free(unsafe.Pointer(p))
|
||||
ret := unsafe.Pointer(C.glfwGetProcAddress(p))
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
+610
@@ -0,0 +1,610 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
package glfw
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ebitengine/purego"
|
||||
)
|
||||
|
||||
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("glfw: invalid context creation API 0x%08X: %w", ctxconfig.source, InvalidEnum)
|
||||
}
|
||||
|
||||
if ctxconfig.client != NoAPI &&
|
||||
ctxconfig.client != OpenGLAPI &&
|
||||
ctxconfig.client != OpenGLESAPI {
|
||||
return fmt.Errorf("glfw: 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("glfw: 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("glfw: 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("glfw: 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("glfw: 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("glfw: 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("glfw: invalid context robustness mode 0x%08X: %w", ctxconfig.robustness, InvalidEnum)
|
||||
}
|
||||
}
|
||||
|
||||
if ctxconfig.release != 0 {
|
||||
if ctxconfig.release != ReleaseBehaviorNone && ctxconfig.release != ReleaseBehaviorFlush {
|
||||
return fmt.Errorf("glfw: 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("glfw: entry point retrieval is broken: %w", PlatformError)
|
||||
}
|
||||
|
||||
r, _, _ := purego.SyscallN(getString, GL_VERSION)
|
||||
version := bytePtrToString((*byte)(unsafe.Pointer(r)))
|
||||
if version == "" {
|
||||
if ctxconfig.client == OpenGLAPI {
|
||||
return fmt.Errorf("glfw: OpenGL version string retrieval is broken: %w", PlatformError)
|
||||
} else {
|
||||
return fmt.Errorf("glfw: 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("glfw: no version found in OpenGL version string: %w", PlatformError)
|
||||
} else {
|
||||
return fmt.Errorf("glfw: 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("glfw: 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("glfw: 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("glfw: 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
|
||||
_, _, _ = purego.SyscallN(getIntegerv, GL_CONTEXT_FLAGS, uintptr(unsafe.Pointer(&flags)))
|
||||
|
||||
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
|
||||
_, _, _ = purego.SyscallN(getIntegerv, GL_CONTEXT_PROFILE_MASK, uintptr(unsafe.Pointer(&mask)))
|
||||
|
||||
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
|
||||
_, _, _ = purego.SyscallN(getIntegerv, GL_RESET_NOTIFICATION_STRATEGY_ARB, uintptr(unsafe.Pointer(&strategy)))
|
||||
|
||||
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
|
||||
_, _, _ = purego.SyscallN(getIntegerv, GL_RESET_NOTIFICATION_STRATEGY_ARB, uintptr(unsafe.Pointer(&strategy)))
|
||||
|
||||
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
|
||||
_, _, _ = purego.SyscallN(getIntegerv, GL_CONTEXT_RELEASE_BEHAVIOR, uintptr(unsafe.Pointer(&behavior)))
|
||||
|
||||
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")
|
||||
_, _, _ = purego.SyscallN(glClear, GL_COLOR_BUFFER_BIT)
|
||||
|
||||
if w.doublebuffer {
|
||||
if err := w.context.swapBuffers(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
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("glfw: 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("glfw: 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("glfw: 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("glfw: 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
|
||||
_, _, _ = purego.SyscallN(glGetIntegerv, GL_NUM_EXTENSIONS, uintptr(unsafe.Pointer(&count)))
|
||||
|
||||
glGetStringi := window.context.getProcAddress("glGetStringi")
|
||||
for i := 0; i < int(count); i++ {
|
||||
r, _, _ := purego.SyscallN(glGetStringi, GL_EXTENSIONS, uintptr(i))
|
||||
if r == 0 {
|
||||
return false, fmt.Errorf("glfw: extension string retrieval is broken: %w", PlatformError)
|
||||
}
|
||||
|
||||
en := 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, _, _ := purego.SyscallN(glGetString, GL_EXTENSIONS)
|
||||
if r == 0 {
|
||||
return false, fmt.Errorf("glfw: extension string retrieval is broken: %w", PlatformError)
|
||||
}
|
||||
|
||||
extensions := 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
|
||||
}
|
||||
|
||||
// bytePtrToString takes a pointer to a sequence of text and returns the corresponding string.
|
||||
// If the pointer is nil, it returns the empty string. It assumes that the text sequence is
|
||||
// terminated at a zero byte; if the zero byte is not present, the program may crash.
|
||||
// It is copied from golang.org/x/sys/windows/syscall.go for use on macOS, Linux and Windows
|
||||
func bytePtrToString(p *byte) string {
|
||||
if p == nil {
|
||||
return ""
|
||||
}
|
||||
if *p == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Find NUL terminator.
|
||||
n := 0
|
||||
for ptr := unsafe.Pointer(p); *(*byte)(ptr) != 0; n++ {
|
||||
ptr = unsafe.Add(ptr, 1)
|
||||
}
|
||||
|
||||
// unsafe.String(p, n) is available as of Go 1.20.
|
||||
return string(unsafe.Slice(p, n))
|
||||
}
|
||||
+771
@@ -0,0 +1,771 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || netbsd || openbsd
|
||||
|
||||
#include "internal_unix.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
// Return a description of the specified EGL error
|
||||
//
|
||||
static const char* getEGLErrorString(EGLint error)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
case EGL_SUCCESS:
|
||||
return "Success";
|
||||
case EGL_NOT_INITIALIZED:
|
||||
return "EGL is not or could not be initialized";
|
||||
case EGL_BAD_ACCESS:
|
||||
return "EGL cannot access a requested resource";
|
||||
case EGL_BAD_ALLOC:
|
||||
return "EGL failed to allocate resources for the requested operation";
|
||||
case EGL_BAD_ATTRIBUTE:
|
||||
return "An unrecognized attribute or attribute value was passed in the attribute list";
|
||||
case EGL_BAD_CONTEXT:
|
||||
return "An EGLContext argument does not name a valid EGL rendering context";
|
||||
case EGL_BAD_CONFIG:
|
||||
return "An EGLConfig argument does not name a valid EGL frame buffer configuration";
|
||||
case EGL_BAD_CURRENT_SURFACE:
|
||||
return "The current surface of the calling thread is a window, pixel buffer or pixmap that is no longer valid";
|
||||
case EGL_BAD_DISPLAY:
|
||||
return "An EGLDisplay argument does not name a valid EGL display connection";
|
||||
case EGL_BAD_SURFACE:
|
||||
return "An EGLSurface argument does not name a valid surface configured for GL rendering";
|
||||
case EGL_BAD_MATCH:
|
||||
return "Arguments are inconsistent";
|
||||
case EGL_BAD_PARAMETER:
|
||||
return "One or more argument values are invalid";
|
||||
case EGL_BAD_NATIVE_PIXMAP:
|
||||
return "A NativePixmapType argument does not refer to a valid native pixmap";
|
||||
case EGL_BAD_NATIVE_WINDOW:
|
||||
return "A NativeWindowType argument does not refer to a valid native window";
|
||||
case EGL_CONTEXT_LOST:
|
||||
return "The application must destroy all contexts and reinitialise";
|
||||
default:
|
||||
return "ERROR: UNKNOWN EGL ERROR";
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the specified attribute of the specified EGLConfig
|
||||
//
|
||||
static int getEGLConfigAttrib(EGLConfig config, int attrib)
|
||||
{
|
||||
int value;
|
||||
eglGetConfigAttrib(_glfw.egl.display, config, attrib, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
// Return the EGLConfig most closely matching the specified hints
|
||||
//
|
||||
static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig,
|
||||
const _GLFWfbconfig* desired,
|
||||
EGLConfig* result)
|
||||
{
|
||||
EGLConfig* nativeConfigs;
|
||||
_GLFWfbconfig* usableConfigs;
|
||||
const _GLFWfbconfig* closest;
|
||||
int i, nativeCount, usableCount;
|
||||
|
||||
eglGetConfigs(_glfw.egl.display, NULL, 0, &nativeCount);
|
||||
if (!nativeCount)
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE, "EGL: No EGLConfigs returned");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
nativeConfigs = calloc(nativeCount, sizeof(EGLConfig));
|
||||
eglGetConfigs(_glfw.egl.display, nativeConfigs, nativeCount, &nativeCount);
|
||||
|
||||
usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig));
|
||||
usableCount = 0;
|
||||
|
||||
for (i = 0; i < nativeCount; i++)
|
||||
{
|
||||
const EGLConfig n = nativeConfigs[i];
|
||||
_GLFWfbconfig* u = usableConfigs + usableCount;
|
||||
|
||||
// Only consider RGB(A) EGLConfigs
|
||||
if (getEGLConfigAttrib(n, EGL_COLOR_BUFFER_TYPE) != EGL_RGB_BUFFER)
|
||||
continue;
|
||||
|
||||
// Only consider window EGLConfigs
|
||||
if (!(getEGLConfigAttrib(n, EGL_SURFACE_TYPE) & EGL_WINDOW_BIT))
|
||||
continue;
|
||||
|
||||
#if defined(_GLFW_X11)
|
||||
{
|
||||
XVisualInfo vi = {0};
|
||||
|
||||
// Only consider EGLConfigs with associated Visuals
|
||||
vi.visualid = getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID);
|
||||
if (!vi.visualid)
|
||||
continue;
|
||||
|
||||
if (desired->transparent)
|
||||
{
|
||||
int count;
|
||||
XVisualInfo* vis =
|
||||
XGetVisualInfo(_glfw.x11.display, VisualIDMask, &vi, &count);
|
||||
if (vis)
|
||||
{
|
||||
u->transparent = _glfwIsVisualTransparentX11(vis[0].visual);
|
||||
XFree(vis);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // _GLFW_X11
|
||||
|
||||
if (ctxconfig->client == GLFW_OPENGL_ES_API)
|
||||
{
|
||||
if (ctxconfig->major == 1)
|
||||
{
|
||||
if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES_BIT))
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES2_BIT))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (ctxconfig->client == GLFW_OPENGL_API)
|
||||
{
|
||||
if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_BIT))
|
||||
continue;
|
||||
}
|
||||
|
||||
u->redBits = getEGLConfigAttrib(n, EGL_RED_SIZE);
|
||||
u->greenBits = getEGLConfigAttrib(n, EGL_GREEN_SIZE);
|
||||
u->blueBits = getEGLConfigAttrib(n, EGL_BLUE_SIZE);
|
||||
|
||||
u->alphaBits = getEGLConfigAttrib(n, EGL_ALPHA_SIZE);
|
||||
u->depthBits = getEGLConfigAttrib(n, EGL_DEPTH_SIZE);
|
||||
u->stencilBits = getEGLConfigAttrib(n, EGL_STENCIL_SIZE);
|
||||
|
||||
u->samples = getEGLConfigAttrib(n, EGL_SAMPLES);
|
||||
u->doublebuffer = desired->doublebuffer;
|
||||
|
||||
u->handle = (uintptr_t) n;
|
||||
usableCount++;
|
||||
}
|
||||
|
||||
closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount);
|
||||
if (closest)
|
||||
*result = (EGLConfig) closest->handle;
|
||||
|
||||
free(nativeConfigs);
|
||||
free(usableConfigs);
|
||||
|
||||
return closest != NULL;
|
||||
}
|
||||
|
||||
static void makeContextCurrentEGL(_GLFWwindow* window)
|
||||
{
|
||||
if (window)
|
||||
{
|
||||
if (!eglMakeCurrent(_glfw.egl.display,
|
||||
window->context.egl.surface,
|
||||
window->context.egl.surface,
|
||||
window->context.egl.handle))
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"EGL: Failed to make context current: %s",
|
||||
getEGLErrorString(eglGetError()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!eglMakeCurrent(_glfw.egl.display,
|
||||
EGL_NO_SURFACE,
|
||||
EGL_NO_SURFACE,
|
||||
EGL_NO_CONTEXT))
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"EGL: Failed to clear current context: %s",
|
||||
getEGLErrorString(eglGetError()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_glfwPlatformSetTls(&_glfw.contextSlot, window);
|
||||
}
|
||||
|
||||
static void swapBuffersEGL(_GLFWwindow* window)
|
||||
{
|
||||
if (window != _glfwPlatformGetTls(&_glfw.contextSlot))
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"EGL: The context must be current on the calling thread when swapping buffers");
|
||||
return;
|
||||
}
|
||||
|
||||
eglSwapBuffers(_glfw.egl.display, window->context.egl.surface);
|
||||
}
|
||||
|
||||
static void swapIntervalEGL(int interval)
|
||||
{
|
||||
eglSwapInterval(_glfw.egl.display, interval);
|
||||
}
|
||||
|
||||
static int extensionSupportedEGL(const char* extension)
|
||||
{
|
||||
const char* extensions = eglQueryString(_glfw.egl.display, EGL_EXTENSIONS);
|
||||
if (extensions)
|
||||
{
|
||||
if (_glfwStringInExtensionString(extension, extensions))
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
static GLFWglproc getProcAddressEGL(const char* procname)
|
||||
{
|
||||
_GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot);
|
||||
|
||||
if (window->context.egl.client)
|
||||
{
|
||||
GLFWglproc proc = (GLFWglproc) _glfw_dlsym(window->context.egl.client,
|
||||
procname);
|
||||
if (proc)
|
||||
return proc;
|
||||
}
|
||||
|
||||
return eglGetProcAddress(procname);
|
||||
}
|
||||
|
||||
static void destroyContextEGL(_GLFWwindow* window)
|
||||
{
|
||||
#if defined(_GLFW_X11)
|
||||
// NOTE: Do not unload libGL.so.1 while the X11 display is still open,
|
||||
// as it will make XCloseDisplay segfault
|
||||
if (window->context.client != GLFW_OPENGL_API)
|
||||
#endif // _GLFW_X11
|
||||
{
|
||||
if (window->context.egl.client)
|
||||
{
|
||||
_glfw_dlclose(window->context.egl.client);
|
||||
window->context.egl.client = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (window->context.egl.surface)
|
||||
{
|
||||
eglDestroySurface(_glfw.egl.display, window->context.egl.surface);
|
||||
window->context.egl.surface = EGL_NO_SURFACE;
|
||||
}
|
||||
|
||||
if (window->context.egl.handle)
|
||||
{
|
||||
eglDestroyContext(_glfw.egl.display, window->context.egl.handle);
|
||||
window->context.egl.handle = EGL_NO_CONTEXT;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW internal API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Initialize EGL
|
||||
//
|
||||
GLFWbool _glfwInitEGL(void)
|
||||
{
|
||||
int i;
|
||||
const char* sonames[] =
|
||||
{
|
||||
#if defined(_GLFW_EGL_LIBRARY)
|
||||
_GLFW_EGL_LIBRARY,
|
||||
#elif defined(_GLFW_COCOA)
|
||||
"libEGL.dylib",
|
||||
#elif defined(__CYGWIN__)
|
||||
"libEGL-1.so",
|
||||
#elif defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
"libEGL.so",
|
||||
#else
|
||||
"libEGL.so.1",
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
if (_glfw.egl.handle)
|
||||
return GLFW_TRUE;
|
||||
|
||||
for (i = 0; sonames[i]; i++)
|
||||
{
|
||||
_glfw.egl.handle = _glfw_dlopen(sonames[i]);
|
||||
if (_glfw.egl.handle)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!_glfw.egl.handle)
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE, "EGL: Library not found");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
_glfw.egl.prefix = (strncmp(sonames[i], "lib", 3) == 0);
|
||||
|
||||
_glfw.egl.GetConfigAttrib = (PFN_eglGetConfigAttrib)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglGetConfigAttrib");
|
||||
_glfw.egl.GetConfigs = (PFN_eglGetConfigs)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglGetConfigs");
|
||||
_glfw.egl.GetDisplay = (PFN_eglGetDisplay)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglGetDisplay");
|
||||
_glfw.egl.GetError = (PFN_eglGetError)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglGetError");
|
||||
_glfw.egl.Initialize = (PFN_eglInitialize)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglInitialize");
|
||||
_glfw.egl.Terminate = (PFN_eglTerminate)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglTerminate");
|
||||
_glfw.egl.BindAPI = (PFN_eglBindAPI)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglBindAPI");
|
||||
_glfw.egl.CreateContext = (PFN_eglCreateContext)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglCreateContext");
|
||||
_glfw.egl.DestroySurface = (PFN_eglDestroySurface)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglDestroySurface");
|
||||
_glfw.egl.DestroyContext = (PFN_eglDestroyContext)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglDestroyContext");
|
||||
_glfw.egl.CreateWindowSurface = (PFN_eglCreateWindowSurface)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglCreateWindowSurface");
|
||||
_glfw.egl.MakeCurrent = (PFN_eglMakeCurrent)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglMakeCurrent");
|
||||
_glfw.egl.SwapBuffers = (PFN_eglSwapBuffers)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglSwapBuffers");
|
||||
_glfw.egl.SwapInterval = (PFN_eglSwapInterval)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglSwapInterval");
|
||||
_glfw.egl.QueryString = (PFN_eglQueryString)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglQueryString");
|
||||
_glfw.egl.GetProcAddress = (PFN_eglGetProcAddress)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglGetProcAddress");
|
||||
|
||||
if (!_glfw.egl.GetConfigAttrib ||
|
||||
!_glfw.egl.GetConfigs ||
|
||||
!_glfw.egl.GetDisplay ||
|
||||
!_glfw.egl.GetError ||
|
||||
!_glfw.egl.Initialize ||
|
||||
!_glfw.egl.Terminate ||
|
||||
!_glfw.egl.BindAPI ||
|
||||
!_glfw.egl.CreateContext ||
|
||||
!_glfw.egl.DestroySurface ||
|
||||
!_glfw.egl.DestroyContext ||
|
||||
!_glfw.egl.CreateWindowSurface ||
|
||||
!_glfw.egl.MakeCurrent ||
|
||||
!_glfw.egl.SwapBuffers ||
|
||||
!_glfw.egl.SwapInterval ||
|
||||
!_glfw.egl.QueryString ||
|
||||
!_glfw.egl.GetProcAddress)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"EGL: Failed to load required entry points");
|
||||
|
||||
_glfwTerminateEGL();
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
_glfw.egl.display = eglGetDisplay(_GLFW_EGL_NATIVE_DISPLAY);
|
||||
if (_glfw.egl.display == EGL_NO_DISPLAY)
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE,
|
||||
"EGL: Failed to get EGL display: %s",
|
||||
getEGLErrorString(eglGetError()));
|
||||
|
||||
_glfwTerminateEGL();
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (!eglInitialize(_glfw.egl.display, &_glfw.egl.major, &_glfw.egl.minor))
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE,
|
||||
"EGL: Failed to initialize EGL: %s",
|
||||
getEGLErrorString(eglGetError()));
|
||||
|
||||
_glfwTerminateEGL();
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
_glfw.egl.KHR_create_context =
|
||||
extensionSupportedEGL("EGL_KHR_create_context");
|
||||
_glfw.egl.KHR_create_context_no_error =
|
||||
extensionSupportedEGL("EGL_KHR_create_context_no_error");
|
||||
_glfw.egl.KHR_gl_colorspace =
|
||||
extensionSupportedEGL("EGL_KHR_gl_colorspace");
|
||||
_glfw.egl.KHR_get_all_proc_addresses =
|
||||
extensionSupportedEGL("EGL_KHR_get_all_proc_addresses");
|
||||
_glfw.egl.KHR_context_flush_control =
|
||||
extensionSupportedEGL("EGL_KHR_context_flush_control");
|
||||
_glfw.egl.EXT_present_opaque =
|
||||
extensionSupportedEGL("EGL_EXT_present_opaque");
|
||||
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
// Terminate EGL
|
||||
//
|
||||
void _glfwTerminateEGL(void)
|
||||
{
|
||||
if (_glfw.egl.display)
|
||||
{
|
||||
eglTerminate(_glfw.egl.display);
|
||||
_glfw.egl.display = EGL_NO_DISPLAY;
|
||||
}
|
||||
|
||||
if (_glfw.egl.handle)
|
||||
{
|
||||
_glfw_dlclose(_glfw.egl.handle);
|
||||
_glfw.egl.handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#define setAttrib(a, v) \
|
||||
{ \
|
||||
assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \
|
||||
attribs[index++] = a; \
|
||||
attribs[index++] = v; \
|
||||
}
|
||||
|
||||
// Create the OpenGL or OpenGL ES context
|
||||
//
|
||||
GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
|
||||
const _GLFWctxconfig* ctxconfig,
|
||||
const _GLFWfbconfig* fbconfig)
|
||||
{
|
||||
EGLint attribs[40];
|
||||
EGLConfig config;
|
||||
EGLContext share = NULL;
|
||||
int index = 0;
|
||||
|
||||
if (!_glfw.egl.display)
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE, "EGL: API not available");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (ctxconfig->share)
|
||||
share = ctxconfig->share->context.egl.handle;
|
||||
|
||||
if (!chooseEGLConfig(ctxconfig, fbconfig, &config))
|
||||
{
|
||||
_glfwInputError(GLFW_FORMAT_UNAVAILABLE,
|
||||
"EGL: Failed to find a suitable EGLConfig");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (ctxconfig->client == GLFW_OPENGL_ES_API)
|
||||
{
|
||||
if (!eglBindAPI(EGL_OPENGL_ES_API))
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE,
|
||||
"EGL: Failed to bind OpenGL ES: %s",
|
||||
getEGLErrorString(eglGetError()));
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!eglBindAPI(EGL_OPENGL_API))
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE,
|
||||
"EGL: Failed to bind OpenGL: %s",
|
||||
getEGLErrorString(eglGetError()));
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (_glfw.egl.KHR_create_context)
|
||||
{
|
||||
int mask = 0, flags = 0;
|
||||
|
||||
if (ctxconfig->client == GLFW_OPENGL_API)
|
||||
{
|
||||
if (ctxconfig->forward)
|
||||
flags |= EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR;
|
||||
|
||||
if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE)
|
||||
mask |= EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR;
|
||||
else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE)
|
||||
mask |= EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR;
|
||||
}
|
||||
|
||||
if (ctxconfig->debug)
|
||||
flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR;
|
||||
|
||||
if (ctxconfig->robustness)
|
||||
{
|
||||
if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION)
|
||||
{
|
||||
setAttrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR,
|
||||
EGL_NO_RESET_NOTIFICATION_KHR);
|
||||
}
|
||||
else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET)
|
||||
{
|
||||
setAttrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR,
|
||||
EGL_LOSE_CONTEXT_ON_RESET_KHR);
|
||||
}
|
||||
|
||||
flags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR;
|
||||
}
|
||||
|
||||
if (ctxconfig->noerror)
|
||||
{
|
||||
if (_glfw.egl.KHR_create_context_no_error)
|
||||
setAttrib(EGL_CONTEXT_OPENGL_NO_ERROR_KHR, GLFW_TRUE);
|
||||
}
|
||||
|
||||
if (ctxconfig->major != 1 || ctxconfig->minor != 0)
|
||||
{
|
||||
setAttrib(EGL_CONTEXT_MAJOR_VERSION_KHR, ctxconfig->major);
|
||||
setAttrib(EGL_CONTEXT_MINOR_VERSION_KHR, ctxconfig->minor);
|
||||
}
|
||||
|
||||
if (mask)
|
||||
setAttrib(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, mask);
|
||||
|
||||
if (flags)
|
||||
setAttrib(EGL_CONTEXT_FLAGS_KHR, flags);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ctxconfig->client == GLFW_OPENGL_ES_API)
|
||||
setAttrib(EGL_CONTEXT_CLIENT_VERSION, ctxconfig->major);
|
||||
}
|
||||
|
||||
if (_glfw.egl.KHR_context_flush_control)
|
||||
{
|
||||
if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE)
|
||||
{
|
||||
setAttrib(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR,
|
||||
EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR);
|
||||
}
|
||||
else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH)
|
||||
{
|
||||
setAttrib(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR,
|
||||
EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR);
|
||||
}
|
||||
}
|
||||
|
||||
setAttrib(EGL_NONE, EGL_NONE);
|
||||
|
||||
window->context.egl.handle = eglCreateContext(_glfw.egl.display,
|
||||
config, share, attribs);
|
||||
|
||||
if (window->context.egl.handle == EGL_NO_CONTEXT)
|
||||
{
|
||||
_glfwInputError(GLFW_VERSION_UNAVAILABLE,
|
||||
"EGL: Failed to create context: %s",
|
||||
getEGLErrorString(eglGetError()));
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
// Set up attributes for surface creation
|
||||
index = 0;
|
||||
|
||||
if (fbconfig->sRGB)
|
||||
{
|
||||
if (_glfw.egl.KHR_gl_colorspace)
|
||||
setAttrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR);
|
||||
}
|
||||
|
||||
if (!fbconfig->doublebuffer)
|
||||
setAttrib(EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER);
|
||||
|
||||
if (_glfw.egl.EXT_present_opaque)
|
||||
setAttrib(EGL_PRESENT_OPAQUE_EXT, !fbconfig->transparent);
|
||||
|
||||
setAttrib(EGL_NONE, EGL_NONE);
|
||||
|
||||
window->context.egl.surface =
|
||||
eglCreateWindowSurface(_glfw.egl.display,
|
||||
config,
|
||||
_GLFW_EGL_NATIVE_WINDOW,
|
||||
attribs);
|
||||
if (window->context.egl.surface == EGL_NO_SURFACE)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"EGL: Failed to create window surface: %s",
|
||||
getEGLErrorString(eglGetError()));
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
window->context.egl.config = config;
|
||||
|
||||
// Load the appropriate client library
|
||||
if (!_glfw.egl.KHR_get_all_proc_addresses)
|
||||
{
|
||||
int i;
|
||||
const char** sonames;
|
||||
const char* es1sonames[] =
|
||||
{
|
||||
#if defined(_GLFW_GLESV1_LIBRARY)
|
||||
_GLFW_GLESV1_LIBRARY,
|
||||
#elif defined(_GLFW_COCOA)
|
||||
"libGLESv1_CM.dylib",
|
||||
#elif defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
"libGLESv1_CM.so",
|
||||
#else
|
||||
"libGLESv1_CM.so.1",
|
||||
"libGLES_CM.so.1",
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
const char* es2sonames[] =
|
||||
{
|
||||
#if defined(_GLFW_GLESV2_LIBRARY)
|
||||
_GLFW_GLESV2_LIBRARY,
|
||||
#elif defined(_GLFW_COCOA)
|
||||
"libGLESv2.dylib",
|
||||
#elif defined(__CYGWIN__)
|
||||
"libGLESv2-2.so",
|
||||
#elif defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
"libGLESv2.so",
|
||||
#else
|
||||
"libGLESv2.so.2",
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
const char* glsonames[] =
|
||||
{
|
||||
#if defined(_GLFW_OPENGL_LIBRARY)
|
||||
_GLFW_OPENGL_LIBRARY,
|
||||
#elif defined(_GLFW_COCOA)
|
||||
#elif defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
"libGL.so",
|
||||
#else
|
||||
"libGL.so.1",
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
if (ctxconfig->client == GLFW_OPENGL_ES_API)
|
||||
{
|
||||
if (ctxconfig->major == 1)
|
||||
sonames = es1sonames;
|
||||
else
|
||||
sonames = es2sonames;
|
||||
}
|
||||
else
|
||||
sonames = glsonames;
|
||||
|
||||
for (i = 0; sonames[i]; i++)
|
||||
{
|
||||
// HACK: Match presence of lib prefix to increase chance of finding
|
||||
// a matching pair in the jungle that is Win32 EGL/GLES
|
||||
if (_glfw.egl.prefix != (strncmp(sonames[i], "lib", 3) == 0))
|
||||
continue;
|
||||
|
||||
window->context.egl.client = _glfw_dlopen(sonames[i]);
|
||||
if (window->context.egl.client)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!window->context.egl.client)
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE,
|
||||
"EGL: Failed to load client library");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
window->context.makeCurrent = makeContextCurrentEGL;
|
||||
window->context.swapBuffers = swapBuffersEGL;
|
||||
window->context.swapInterval = swapIntervalEGL;
|
||||
window->context.extensionSupported = extensionSupportedEGL;
|
||||
window->context.getProcAddress = getProcAddressEGL;
|
||||
window->context.destroy = destroyContextEGL;
|
||||
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
#undef setAttrib
|
||||
|
||||
// Returns the Visual and depth of the chosen EGLConfig
|
||||
//
|
||||
#if defined(_GLFW_X11)
|
||||
GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig,
|
||||
const _GLFWctxconfig* ctxconfig,
|
||||
const _GLFWfbconfig* fbconfig,
|
||||
Visual** visual, int* depth)
|
||||
{
|
||||
XVisualInfo* result;
|
||||
XVisualInfo desired;
|
||||
EGLConfig native;
|
||||
EGLint visualID = 0, count = 0;
|
||||
const long vimask = VisualScreenMask | VisualIDMask;
|
||||
|
||||
if (!chooseEGLConfig(ctxconfig, fbconfig, &native))
|
||||
{
|
||||
_glfwInputError(GLFW_FORMAT_UNAVAILABLE,
|
||||
"EGL: Failed to find a suitable EGLConfig");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
eglGetConfigAttrib(_glfw.egl.display, native,
|
||||
EGL_NATIVE_VISUAL_ID, &visualID);
|
||||
|
||||
desired.screen = _glfw.x11.screen;
|
||||
desired.visualid = visualID;
|
||||
|
||||
result = XGetVisualInfo(_glfw.x11.display, vimask, &desired, &count);
|
||||
if (!result)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"EGL: Failed to retrieve Visual for EGLConfig");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
*visual = result->visual;
|
||||
*depth = result->depth;
|
||||
|
||||
XFree(result);
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
#endif // _GLFW_X11
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW native API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GLFWAPI EGLDisplay glfwGetEGLDisplay(void)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_DISPLAY);
|
||||
return _glfw.egl.display;
|
||||
}
|
||||
|
||||
GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* handle)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_CONTEXT);
|
||||
|
||||
if (window->context.source != GLFW_EGL_CONTEXT_API)
|
||||
{
|
||||
_glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
|
||||
return EGL_NO_CONTEXT;
|
||||
}
|
||||
|
||||
return window->context.egl.handle;
|
||||
}
|
||||
|
||||
GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* handle)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_SURFACE);
|
||||
|
||||
if (window->context.source != GLFW_EGL_CONTEXT_API)
|
||||
{
|
||||
_glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
|
||||
return EGL_NO_SURFACE;
|
||||
}
|
||||
|
||||
return window->context.egl.surface;
|
||||
}
|
||||
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2017 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || netbsd || openbsd
|
||||
|
||||
#if defined(_GLFW_USE_EGLPLATFORM_H)
|
||||
#include <EGL/eglplatform.h>
|
||||
#elif defined(_GLFW_COCOA)
|
||||
#define EGLAPIENTRY
|
||||
typedef void* EGLNativeDisplayType;
|
||||
typedef id EGLNativeWindowType;
|
||||
#elif defined(_GLFW_X11)
|
||||
#define EGLAPIENTRY
|
||||
typedef Display* EGLNativeDisplayType;
|
||||
typedef Window EGLNativeWindowType;
|
||||
#else
|
||||
#error "No supported EGL platform selected"
|
||||
#endif
|
||||
|
||||
#define EGL_SUCCESS 0x3000
|
||||
#define EGL_NOT_INITIALIZED 0x3001
|
||||
#define EGL_BAD_ACCESS 0x3002
|
||||
#define EGL_BAD_ALLOC 0x3003
|
||||
#define EGL_BAD_ATTRIBUTE 0x3004
|
||||
#define EGL_BAD_CONFIG 0x3005
|
||||
#define EGL_BAD_CONTEXT 0x3006
|
||||
#define EGL_BAD_CURRENT_SURFACE 0x3007
|
||||
#define EGL_BAD_DISPLAY 0x3008
|
||||
#define EGL_BAD_MATCH 0x3009
|
||||
#define EGL_BAD_NATIVE_PIXMAP 0x300a
|
||||
#define EGL_BAD_NATIVE_WINDOW 0x300b
|
||||
#define EGL_BAD_PARAMETER 0x300c
|
||||
#define EGL_BAD_SURFACE 0x300d
|
||||
#define EGL_CONTEXT_LOST 0x300e
|
||||
#define EGL_COLOR_BUFFER_TYPE 0x303f
|
||||
#define EGL_RGB_BUFFER 0x308e
|
||||
#define EGL_SURFACE_TYPE 0x3033
|
||||
#define EGL_WINDOW_BIT 0x0004
|
||||
#define EGL_RENDERABLE_TYPE 0x3040
|
||||
#define EGL_OPENGL_ES_BIT 0x0001
|
||||
#define EGL_OPENGL_ES2_BIT 0x0004
|
||||
#define EGL_OPENGL_BIT 0x0008
|
||||
#define EGL_ALPHA_SIZE 0x3021
|
||||
#define EGL_BLUE_SIZE 0x3022
|
||||
#define EGL_GREEN_SIZE 0x3023
|
||||
#define EGL_RED_SIZE 0x3024
|
||||
#define EGL_DEPTH_SIZE 0x3025
|
||||
#define EGL_STENCIL_SIZE 0x3026
|
||||
#define EGL_SAMPLES 0x3031
|
||||
#define EGL_OPENGL_ES_API 0x30a0
|
||||
#define EGL_OPENGL_API 0x30a2
|
||||
#define EGL_NONE 0x3038
|
||||
#define EGL_RENDER_BUFFER 0x3086
|
||||
#define EGL_SINGLE_BUFFER 0x3085
|
||||
#define EGL_EXTENSIONS 0x3055
|
||||
#define EGL_CONTEXT_CLIENT_VERSION 0x3098
|
||||
#define EGL_NATIVE_VISUAL_ID 0x302e
|
||||
#define EGL_NO_SURFACE ((EGLSurface) 0)
|
||||
#define EGL_NO_DISPLAY ((EGLDisplay) 0)
|
||||
#define EGL_NO_CONTEXT ((EGLContext) 0)
|
||||
#define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType) 0)
|
||||
|
||||
#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002
|
||||
#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001
|
||||
#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002
|
||||
#define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001
|
||||
#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR 0x31bd
|
||||
#define EGL_NO_RESET_NOTIFICATION_KHR 0x31be
|
||||
#define EGL_LOSE_CONTEXT_ON_RESET_KHR 0x31bf
|
||||
#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004
|
||||
#define EGL_CONTEXT_MAJOR_VERSION_KHR 0x3098
|
||||
#define EGL_CONTEXT_MINOR_VERSION_KHR 0x30fb
|
||||
#define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30fd
|
||||
#define EGL_CONTEXT_FLAGS_KHR 0x30fc
|
||||
#define EGL_CONTEXT_OPENGL_NO_ERROR_KHR 0x31b3
|
||||
#define EGL_GL_COLORSPACE_KHR 0x309d
|
||||
#define EGL_GL_COLORSPACE_SRGB_KHR 0x3089
|
||||
#define EGL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x2097
|
||||
#define EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR 0
|
||||
#define EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR 0x2098
|
||||
#define EGL_PRESENT_OPAQUE_EXT 0x31df
|
||||
|
||||
typedef int EGLint;
|
||||
typedef unsigned int EGLBoolean;
|
||||
typedef unsigned int EGLenum;
|
||||
typedef void* EGLConfig;
|
||||
typedef void* EGLContext;
|
||||
typedef void* EGLDisplay;
|
||||
typedef void* EGLSurface;
|
||||
|
||||
// EGL function pointer typedefs
|
||||
typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigAttrib)(EGLDisplay,EGLConfig,EGLint,EGLint*);
|
||||
typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigs)(EGLDisplay,EGLConfig*,EGLint,EGLint*);
|
||||
typedef EGLDisplay (EGLAPIENTRY * PFN_eglGetDisplay)(EGLNativeDisplayType);
|
||||
typedef EGLint (EGLAPIENTRY * PFN_eglGetError)(void);
|
||||
typedef EGLBoolean (EGLAPIENTRY * PFN_eglInitialize)(EGLDisplay,EGLint*,EGLint*);
|
||||
typedef EGLBoolean (EGLAPIENTRY * PFN_eglTerminate)(EGLDisplay);
|
||||
typedef EGLBoolean (EGLAPIENTRY * PFN_eglBindAPI)(EGLenum);
|
||||
typedef EGLContext (EGLAPIENTRY * PFN_eglCreateContext)(EGLDisplay,EGLConfig,EGLContext,const EGLint*);
|
||||
typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroySurface)(EGLDisplay,EGLSurface);
|
||||
typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroyContext)(EGLDisplay,EGLContext);
|
||||
typedef EGLSurface (EGLAPIENTRY * PFN_eglCreateWindowSurface)(EGLDisplay,EGLConfig,EGLNativeWindowType,const EGLint*);
|
||||
typedef EGLBoolean (EGLAPIENTRY * PFN_eglMakeCurrent)(EGLDisplay,EGLSurface,EGLSurface,EGLContext);
|
||||
typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapBuffers)(EGLDisplay,EGLSurface);
|
||||
typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapInterval)(EGLDisplay,EGLint);
|
||||
typedef const char* (EGLAPIENTRY * PFN_eglQueryString)(EGLDisplay,EGLint);
|
||||
typedef GLFWglproc (EGLAPIENTRY * PFN_eglGetProcAddress)(const char*);
|
||||
#define eglGetConfigAttrib _glfw.egl.GetConfigAttrib
|
||||
#define eglGetConfigs _glfw.egl.GetConfigs
|
||||
#define eglGetDisplay _glfw.egl.GetDisplay
|
||||
#define eglGetError _glfw.egl.GetError
|
||||
#define eglInitialize _glfw.egl.Initialize
|
||||
#define eglTerminate _glfw.egl.Terminate
|
||||
#define eglBindAPI _glfw.egl.BindAPI
|
||||
#define eglCreateContext _glfw.egl.CreateContext
|
||||
#define eglDestroySurface _glfw.egl.DestroySurface
|
||||
#define eglDestroyContext _glfw.egl.DestroyContext
|
||||
#define eglCreateWindowSurface _glfw.egl.CreateWindowSurface
|
||||
#define eglMakeCurrent _glfw.egl.MakeCurrent
|
||||
#define eglSwapBuffers _glfw.egl.SwapBuffers
|
||||
#define eglSwapInterval _glfw.egl.SwapInterval
|
||||
#define eglQueryString _glfw.egl.QueryString
|
||||
#define eglGetProcAddress _glfw.egl.GetProcAddress
|
||||
|
||||
#define _GLFW_EGL_CONTEXT_STATE _GLFWcontextEGL egl
|
||||
#define _GLFW_EGL_LIBRARY_CONTEXT_STATE _GLFWlibraryEGL egl
|
||||
|
||||
|
||||
// EGL-specific per-context data
|
||||
//
|
||||
typedef struct _GLFWcontextEGL
|
||||
{
|
||||
EGLConfig config;
|
||||
EGLContext handle;
|
||||
EGLSurface surface;
|
||||
|
||||
void* client;
|
||||
} _GLFWcontextEGL;
|
||||
|
||||
// EGL-specific global data
|
||||
//
|
||||
typedef struct _GLFWlibraryEGL
|
||||
{
|
||||
EGLDisplay display;
|
||||
EGLint major, minor;
|
||||
GLFWbool prefix;
|
||||
|
||||
GLFWbool KHR_create_context;
|
||||
GLFWbool KHR_create_context_no_error;
|
||||
GLFWbool KHR_gl_colorspace;
|
||||
GLFWbool KHR_get_all_proc_addresses;
|
||||
GLFWbool KHR_context_flush_control;
|
||||
GLFWbool EXT_present_opaque;
|
||||
|
||||
void* handle;
|
||||
|
||||
PFN_eglGetConfigAttrib GetConfigAttrib;
|
||||
PFN_eglGetConfigs GetConfigs;
|
||||
PFN_eglGetDisplay GetDisplay;
|
||||
PFN_eglGetError GetError;
|
||||
PFN_eglInitialize Initialize;
|
||||
PFN_eglTerminate Terminate;
|
||||
PFN_eglBindAPI BindAPI;
|
||||
PFN_eglCreateContext CreateContext;
|
||||
PFN_eglDestroySurface DestroySurface;
|
||||
PFN_eglDestroyContext DestroyContext;
|
||||
PFN_eglCreateWindowSurface CreateWindowSurface;
|
||||
PFN_eglMakeCurrent MakeCurrent;
|
||||
PFN_eglSwapBuffers SwapBuffers;
|
||||
PFN_eglSwapInterval SwapInterval;
|
||||
PFN_eglQueryString QueryString;
|
||||
PFN_eglGetProcAddress GetProcAddress;
|
||||
} _GLFWlibraryEGL;
|
||||
|
||||
|
||||
GLFWbool _glfwInitEGL(void);
|
||||
void _glfwTerminateEGL(void);
|
||||
GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
|
||||
const _GLFWctxconfig* ctxconfig,
|
||||
const _GLFWfbconfig* fbconfig);
|
||||
#if defined(_GLFW_X11)
|
||||
GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig,
|
||||
const _GLFWctxconfig* ctxconfig,
|
||||
const _GLFWfbconfig* fbconfig,
|
||||
Visual** visual, int* depth);
|
||||
#endif /*_GLFW_X11*/
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2012 The glfw3-go Authors
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || netbsd || openbsd
|
||||
|
||||
package glfw
|
||||
|
||||
// #define GLFW_INCLUDE_NONE
|
||||
// #include "glfw3_unix.h"
|
||||
//
|
||||
// void goErrorCB(int code, char* desc);
|
||||
//
|
||||
// static void glfwSetErrorCallbackCB() {
|
||||
// glfwSetErrorCallback((GLFWerrorfun)goErrorCB);
|
||||
// }
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Note: There are many cryptic caveats to proper error handling here.
|
||||
// See: https://github.com/go-gl/glfw3/pull/86
|
||||
|
||||
// lastError holds the value of the last error.
|
||||
var lastError = make(chan error, 1)
|
||||
|
||||
//export goErrorCB
|
||||
func goErrorCB(code C.int, desc *C.char) {
|
||||
err := fmt.Errorf("glfw: %s: %w", C.GoString(desc), ErrorCode(code))
|
||||
select {
|
||||
case lastError <- err:
|
||||
default:
|
||||
fmt.Fprintln(os.Stderr, "GLFW: An uncaught error has occurred:", err)
|
||||
fmt.Fprintln(os.Stderr, "GLFW: Please report this bug in the Go package immediately.")
|
||||
}
|
||||
}
|
||||
|
||||
// Set the glfw callback internally
|
||||
func init() {
|
||||
C.glfwSetErrorCallbackCB()
|
||||
}
|
||||
|
||||
// fetchErrorIgnoringPlatformError is fetchError ignoring platformError.
|
||||
func fetchErrorIgnoringPlatformError() error {
|
||||
select {
|
||||
case err := <-lastError:
|
||||
if errors.Is(err, PlatformError) {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || netbsd || openbsd || windows
|
||||
|
||||
package glfw
|
||||
|
||||
type VidMode struct {
|
||||
Width int
|
||||
Height int
|
||||
RedBits int
|
||||
GreenBits int
|
||||
BlueBits int
|
||||
RefreshRate int
|
||||
}
|
||||
|
||||
type Image struct {
|
||||
Width int
|
||||
Height int
|
||||
Pixels []byte
|
||||
}
|
||||
+4945
File diff suppressed because it is too large
Load Diff
+475
@@ -0,0 +1,475 @@
|
||||
/*************************************************************************
|
||||
* GLFW 3.3 - www.glfw.org
|
||||
* A library for OpenGL, window and input
|
||||
*------------------------------------------------------------------------
|
||||
* Copyright (c) 2002-2006 Marcus Geelnard
|
||||
* Copyright (c) 2006-2018 Camilla Löwy <elmindreda@glfw.org>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef _glfw3_native_h_
|
||||
#define _glfw3_native_h_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
* Doxygen documentation
|
||||
*************************************************************************/
|
||||
|
||||
/*! @file glfw3native_unix.h
|
||||
* @brief The header of the native access functions.
|
||||
*
|
||||
* This is the header file of the native access functions. See @ref native for
|
||||
* more information.
|
||||
*/
|
||||
/*! @defgroup native Native access
|
||||
* @brief Functions related to accessing native handles.
|
||||
*
|
||||
* **By using the native access functions you assert that you know what you're
|
||||
* doing and how to fix problems caused by using them. If you don't, you
|
||||
* shouldn't be using them.**
|
||||
*
|
||||
* Before the inclusion of @ref glfw3native_unix.h, you may define zero or more
|
||||
* window system API macro and zero or more context creation API macros.
|
||||
*
|
||||
* The chosen backends must match those the library was compiled for. Failure
|
||||
* to do this will cause a link-time error.
|
||||
*
|
||||
* The available window API macros are:
|
||||
* * `GLFW_EXPOSE_NATIVE_WIN32`
|
||||
* * `GLFW_EXPOSE_NATIVE_COCOA`
|
||||
* * `GLFW_EXPOSE_NATIVE_X11`
|
||||
*
|
||||
* The available context API macros are:
|
||||
* * `GLFW_EXPOSE_NATIVE_WGL`
|
||||
* * `GLFW_EXPOSE_NATIVE_NSGL`
|
||||
* * `GLFW_EXPOSE_NATIVE_GLX`
|
||||
* * `GLFW_EXPOSE_NATIVE_EGL`
|
||||
* * `GLFW_EXPOSE_NATIVE_OSMESA`
|
||||
*
|
||||
* These macros select which of the native access functions that are declared
|
||||
* and which platform-specific headers to include. It is then up your (by
|
||||
* definition platform-specific) code to handle which of these should be
|
||||
* defined.
|
||||
*
|
||||
* If you do not want the platform-specific headers to be included, define
|
||||
* `GLFW_NATIVE_INCLUDE_NONE` before including the @ref glfw3native_unix.h header.
|
||||
*
|
||||
* @code
|
||||
* #define GLFW_EXPOSE_NATIVE_WIN32
|
||||
* #define GLFW_EXPOSE_NATIVE_WGL
|
||||
* #define GLFW_NATIVE_INCLUDE_NONE
|
||||
* #include <GLFW/glfw3native_unix.h>
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
* System headers and types
|
||||
*************************************************************************/
|
||||
|
||||
#if !defined(GLFW_NATIVE_INCLUDE_NONE)
|
||||
|
||||
#if defined(GLFW_EXPOSE_NATIVE_COCOA) || defined(GLFW_EXPOSE_NATIVE_NSGL)
|
||||
#if defined(__OBJC__)
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#else
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#include <objc/objc.h>
|
||||
#endif
|
||||
#elif defined(GLFW_EXPOSE_NATIVE_X11) || defined(GLFW_EXPOSE_NATIVE_GLX)
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/extensions/Xrandr.h>
|
||||
#endif
|
||||
|
||||
#if defined(GLFW_EXPOSE_NATIVE_NSGL)
|
||||
/* NSGL is declared by Cocoa.h */
|
||||
#endif
|
||||
#if defined(GLFW_EXPOSE_NATIVE_GLX)
|
||||
/* This is a workaround for the fact that glfw3_unix.h defines GLAPIENTRY because by
|
||||
* default it also acts as an OpenGL header
|
||||
* However, glx.h will include gl.h, which will define it unconditionally
|
||||
*/
|
||||
#if defined(GLFW_GLAPIENTRY_DEFINED)
|
||||
#undef GLAPIENTRY
|
||||
#undef GLFW_GLAPIENTRY_DEFINED
|
||||
#endif
|
||||
#include <GL/glx.h>
|
||||
#endif
|
||||
#if defined(GLFW_EXPOSE_NATIVE_EGL)
|
||||
#include <EGL/egl.h>
|
||||
#endif
|
||||
#if defined(GLFW_EXPOSE_NATIVE_OSMESA)
|
||||
/* This is a workaround for the fact that glfw3_unix.h defines GLAPIENTRY because by
|
||||
* default it also acts as an OpenGL header
|
||||
* However, osmesa.h will include gl.h, which will define it unconditionally
|
||||
*/
|
||||
#if defined(GLFW_GLAPIENTRY_DEFINED)
|
||||
#undef GLAPIENTRY
|
||||
#undef GLFW_GLAPIENTRY_DEFINED
|
||||
#endif
|
||||
#include <GL/osmesa.h>
|
||||
#endif
|
||||
|
||||
#endif /*GLFW_NATIVE_INCLUDE_NONE*/
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
* Functions
|
||||
*************************************************************************/
|
||||
|
||||
#if defined(GLFW_EXPOSE_NATIVE_COCOA)
|
||||
/*! @brief Returns the `CGDirectDisplayID` of the specified monitor.
|
||||
*
|
||||
* @return The `CGDirectDisplayID` of the specified monitor, or
|
||||
* `kCGNullDirectDisplay` if an [error](@ref error_handling) occurred.
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
|
||||
*
|
||||
* @thread_safety This function may be called from any thread. Access is not
|
||||
* synchronized.
|
||||
*
|
||||
* @since Added in version 3.1.
|
||||
*
|
||||
* @ingroup native
|
||||
*/
|
||||
GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* monitor);
|
||||
|
||||
/*! @brief Returns the `NSWindow` of the specified window.
|
||||
*
|
||||
* @return The `NSWindow` of the specified window, or `nil` if an
|
||||
* [error](@ref error_handling) occurred.
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
|
||||
*
|
||||
* @thread_safety This function may be called from any thread. Access is not
|
||||
* synchronized.
|
||||
*
|
||||
* @since Added in version 3.0.
|
||||
*
|
||||
* @ingroup native
|
||||
*/
|
||||
GLFWAPI id glfwGetCocoaWindow(GLFWwindow* window);
|
||||
#endif
|
||||
|
||||
#if defined(GLFW_EXPOSE_NATIVE_NSGL)
|
||||
/*! @brief Returns the `NSOpenGLContext` of the specified window.
|
||||
*
|
||||
* @return The `NSOpenGLContext` of the specified window, or `nil` if an
|
||||
* [error](@ref error_handling) occurred.
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
|
||||
* GLFW_NOT_INITIALIZED.
|
||||
*
|
||||
* @thread_safety This function may be called from any thread. Access is not
|
||||
* synchronized.
|
||||
*
|
||||
* @since Added in version 3.0.
|
||||
*
|
||||
* @ingroup native
|
||||
*/
|
||||
GLFWAPI id glfwGetNSGLContext(GLFWwindow* window);
|
||||
#endif
|
||||
|
||||
#if defined(GLFW_EXPOSE_NATIVE_X11)
|
||||
/*! @brief Returns the `Display` used by GLFW.
|
||||
*
|
||||
* @return The `Display` used by GLFW, or `NULL` if an
|
||||
* [error](@ref error_handling) occurred.
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
|
||||
*
|
||||
* @thread_safety This function may be called from any thread. Access is not
|
||||
* synchronized.
|
||||
*
|
||||
* @since Added in version 3.0.
|
||||
*
|
||||
* @ingroup native
|
||||
*/
|
||||
GLFWAPI Display* glfwGetX11Display(void);
|
||||
|
||||
/*! @brief Returns the `RRCrtc` of the specified monitor.
|
||||
*
|
||||
* @return The `RRCrtc` of the specified monitor, or `None` if an
|
||||
* [error](@ref error_handling) occurred.
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
|
||||
*
|
||||
* @thread_safety This function may be called from any thread. Access is not
|
||||
* synchronized.
|
||||
*
|
||||
* @since Added in version 3.1.
|
||||
*
|
||||
* @ingroup native
|
||||
*/
|
||||
GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* monitor);
|
||||
|
||||
/*! @brief Returns the `RROutput` of the specified monitor.
|
||||
*
|
||||
* @return The `RROutput` of the specified monitor, or `None` if an
|
||||
* [error](@ref error_handling) occurred.
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
|
||||
*
|
||||
* @thread_safety This function may be called from any thread. Access is not
|
||||
* synchronized.
|
||||
*
|
||||
* @since Added in version 3.1.
|
||||
*
|
||||
* @ingroup native
|
||||
*/
|
||||
GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor);
|
||||
|
||||
/*! @brief Returns the `Window` of the specified window.
|
||||
*
|
||||
* @return The `Window` of the specified window, or `None` if an
|
||||
* [error](@ref error_handling) occurred.
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
|
||||
*
|
||||
* @thread_safety This function may be called from any thread. Access is not
|
||||
* synchronized.
|
||||
*
|
||||
* @since Added in version 3.0.
|
||||
*
|
||||
* @ingroup native
|
||||
*/
|
||||
GLFWAPI Window glfwGetX11Window(GLFWwindow* window);
|
||||
|
||||
/*! @brief Sets the current primary selection to the specified string.
|
||||
*
|
||||
* @param[in] string A UTF-8 encoded string.
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
|
||||
* GLFW_PLATFORM_ERROR.
|
||||
*
|
||||
* @pointer_lifetime The specified string is copied before this function
|
||||
* returns.
|
||||
*
|
||||
* @thread_safety This function must only be called from the main thread.
|
||||
*
|
||||
* @sa @ref clipboard
|
||||
* @sa glfwGetX11SelectionString
|
||||
* @sa glfwSetClipboardString
|
||||
*
|
||||
* @since Added in version 3.3.
|
||||
*
|
||||
* @ingroup native
|
||||
*/
|
||||
GLFWAPI void glfwSetX11SelectionString(const char* string);
|
||||
|
||||
/*! @brief Returns the contents of the current primary selection as a string.
|
||||
*
|
||||
* If the selection is empty or if its contents cannot be converted, `NULL`
|
||||
* is returned and a @ref GLFW_FORMAT_UNAVAILABLE error is generated.
|
||||
*
|
||||
* @return The contents of the selection as a UTF-8 encoded string, or `NULL`
|
||||
* if an [error](@ref error_handling) occurred.
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
|
||||
* GLFW_PLATFORM_ERROR.
|
||||
*
|
||||
* @pointer_lifetime The returned string is allocated and freed by GLFW. You
|
||||
* should not free it yourself. It is valid until the next call to @ref
|
||||
* glfwGetX11SelectionString or @ref glfwSetX11SelectionString, or until the
|
||||
* library is terminated.
|
||||
*
|
||||
* @thread_safety This function must only be called from the main thread.
|
||||
*
|
||||
* @sa @ref clipboard
|
||||
* @sa glfwSetX11SelectionString
|
||||
* @sa glfwGetClipboardString
|
||||
*
|
||||
* @since Added in version 3.3.
|
||||
*
|
||||
* @ingroup native
|
||||
*/
|
||||
GLFWAPI const char* glfwGetX11SelectionString(void);
|
||||
#endif
|
||||
|
||||
#if defined(GLFW_EXPOSE_NATIVE_GLX)
|
||||
/*! @brief Returns the `GLXContext` of the specified window.
|
||||
*
|
||||
* @return The `GLXContext` of the specified window, or `NULL` if an
|
||||
* [error](@ref error_handling) occurred.
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
|
||||
* GLFW_NOT_INITIALIZED.
|
||||
*
|
||||
* @thread_safety This function may be called from any thread. Access is not
|
||||
* synchronized.
|
||||
*
|
||||
* @since Added in version 3.0.
|
||||
*
|
||||
* @ingroup native
|
||||
*/
|
||||
GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* window);
|
||||
|
||||
/*! @brief Returns the `GLXWindow` of the specified window.
|
||||
*
|
||||
* @return The `GLXWindow` of the specified window, or `None` if an
|
||||
* [error](@ref error_handling) occurred.
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
|
||||
* GLFW_NOT_INITIALIZED.
|
||||
*
|
||||
* @thread_safety This function may be called from any thread. Access is not
|
||||
* synchronized.
|
||||
*
|
||||
* @since Added in version 3.2.
|
||||
*
|
||||
* @ingroup native
|
||||
*/
|
||||
GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* window);
|
||||
#endif
|
||||
|
||||
#if defined(GLFW_EXPOSE_NATIVE_EGL)
|
||||
/*! @brief Returns the `EGLDisplay` used by GLFW.
|
||||
*
|
||||
* @return The `EGLDisplay` used by GLFW, or `EGL_NO_DISPLAY` if an
|
||||
* [error](@ref error_handling) occurred.
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
|
||||
*
|
||||
* @remark Because EGL is initialized on demand, this function will return
|
||||
* `EGL_NO_DISPLAY` until the first context has been created via EGL.
|
||||
*
|
||||
* @thread_safety This function may be called from any thread. Access is not
|
||||
* synchronized.
|
||||
*
|
||||
* @since Added in version 3.0.
|
||||
*
|
||||
* @ingroup native
|
||||
*/
|
||||
GLFWAPI EGLDisplay glfwGetEGLDisplay(void);
|
||||
|
||||
/*! @brief Returns the `EGLContext` of the specified window.
|
||||
*
|
||||
* @return The `EGLContext` of the specified window, or `EGL_NO_CONTEXT` if an
|
||||
* [error](@ref error_handling) occurred.
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
|
||||
* GLFW_NOT_INITIALIZED.
|
||||
*
|
||||
* @thread_safety This function may be called from any thread. Access is not
|
||||
* synchronized.
|
||||
*
|
||||
* @since Added in version 3.0.
|
||||
*
|
||||
* @ingroup native
|
||||
*/
|
||||
GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window);
|
||||
|
||||
/*! @brief Returns the `EGLSurface` of the specified window.
|
||||
*
|
||||
* @return The `EGLSurface` of the specified window, or `EGL_NO_SURFACE` if an
|
||||
* [error](@ref error_handling) occurred.
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
|
||||
* GLFW_NOT_INITIALIZED.
|
||||
*
|
||||
* @thread_safety This function may be called from any thread. Access is not
|
||||
* synchronized.
|
||||
*
|
||||
* @since Added in version 3.0.
|
||||
*
|
||||
* @ingroup native
|
||||
*/
|
||||
GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window);
|
||||
#endif
|
||||
|
||||
#if defined(GLFW_EXPOSE_NATIVE_OSMESA)
|
||||
/*! @brief Retrieves the color buffer associated with the specified window.
|
||||
*
|
||||
* @param[in] window The window whose color buffer to retrieve.
|
||||
* @param[out] width Where to store the width of the color buffer, or `NULL`.
|
||||
* @param[out] height Where to store the height of the color buffer, or `NULL`.
|
||||
* @param[out] format Where to store the OSMesa pixel format of the color
|
||||
* buffer, or `NULL`.
|
||||
* @param[out] buffer Where to store the address of the color buffer, or
|
||||
* `NULL`.
|
||||
* @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an
|
||||
* [error](@ref error_handling) occurred.
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
|
||||
* GLFW_NOT_INITIALIZED.
|
||||
*
|
||||
* @thread_safety This function may be called from any thread. Access is not
|
||||
* synchronized.
|
||||
*
|
||||
* @since Added in version 3.3.
|
||||
*
|
||||
* @ingroup native
|
||||
*/
|
||||
GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width, int* height, int* format, void** buffer);
|
||||
|
||||
/*! @brief Retrieves the depth buffer associated with the specified window.
|
||||
*
|
||||
* @param[in] window The window whose depth buffer to retrieve.
|
||||
* @param[out] width Where to store the width of the depth buffer, or `NULL`.
|
||||
* @param[out] height Where to store the height of the depth buffer, or `NULL`.
|
||||
* @param[out] bytesPerValue Where to store the number of bytes per depth
|
||||
* buffer element, or `NULL`.
|
||||
* @param[out] buffer Where to store the address of the depth buffer, or
|
||||
* `NULL`.
|
||||
* @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an
|
||||
* [error](@ref error_handling) occurred.
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
|
||||
* GLFW_NOT_INITIALIZED.
|
||||
*
|
||||
* @thread_safety This function may be called from any thread. Access is not
|
||||
* synchronized.
|
||||
*
|
||||
* @since Added in version 3.3.
|
||||
*
|
||||
* @ingroup native
|
||||
*/
|
||||
GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* window, int* width, int* height, int* bytesPerValue, void** buffer);
|
||||
|
||||
/*! @brief Returns the `OSMesaContext` of the specified window.
|
||||
*
|
||||
* @return The `OSMesaContext` of the specified window, or `NULL` if an
|
||||
* [error](@ref error_handling) occurred.
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
|
||||
* GLFW_NOT_INITIALIZED.
|
||||
*
|
||||
* @thread_safety This function may be called from any thread. Access is not
|
||||
* synchronized.
|
||||
*
|
||||
* @since Added in version 3.3.
|
||||
*
|
||||
* @ingroup native
|
||||
*/
|
||||
GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* window);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _glfw3_native_h_ */
|
||||
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2012 The glfw3-go Authors
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || netbsd || openbsd
|
||||
|
||||
package glfw
|
||||
|
||||
//#include <stdlib.h>
|
||||
//#define GLFW_INCLUDE_NONE
|
||||
//#include "glfw3_unix.h"
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Version constants.
|
||||
const (
|
||||
VersionMajor = C.GLFW_VERSION_MAJOR // This is incremented when the API is changed in non-compatible ways.
|
||||
VersionMinor = C.GLFW_VERSION_MINOR // This is incremented when features are added to the API but it remains backward-compatible.
|
||||
VersionRevision = C.GLFW_VERSION_REVISION // This is incremented when a bug fix release is made that does not contain any API changes.
|
||||
)
|
||||
|
||||
// Init initializes the GLFW library. Before most GLFW functions can be used,
|
||||
// GLFW must be initialized, and before a program terminates GLFW should be
|
||||
// terminated in order to free any resources allocated during or after
|
||||
// initialization.
|
||||
//
|
||||
// If this function fails, it calls Terminate before returning. If it succeeds,
|
||||
// you should call Terminate before the program exits.
|
||||
//
|
||||
// Additional calls to this function after successful initialization but before
|
||||
// termination will succeed but will do nothing.
|
||||
//
|
||||
// This function may take several seconds to complete on some systems, while on
|
||||
// other systems it may take only a fraction of a second to complete.
|
||||
//
|
||||
// On Mac OS X, this function will change the current directory of the
|
||||
// application to the Contents/Resources subdirectory of the application's
|
||||
// bundle, if present.
|
||||
//
|
||||
// This function may only be called from the main thread.
|
||||
func Init() error {
|
||||
C.glfwInit()
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Terminate destroys all remaining windows, frees any allocated resources and
|
||||
// sets the library to an uninitialized state. Once this is called, you must
|
||||
// again call Init successfully before you will be able to use most GLFW
|
||||
// functions.
|
||||
//
|
||||
// If GLFW has been successfully initialized, this function should be called
|
||||
// before the program exits. If initialization fails, there is no need to call
|
||||
// this function, as it is called by Init before it returns failure.
|
||||
//
|
||||
// This function may only be called from the main thread.
|
||||
func Terminate() error {
|
||||
C.glfwTerminate()
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InitHint function sets hints for the next initialization of GLFW.
|
||||
//
|
||||
// The values you set hints to are never reset by GLFW, but they only take
|
||||
// effect during initialization. Once GLFW has been initialized, any values you
|
||||
// set will be ignored until the library is terminated and initialized again.
|
||||
//
|
||||
// Some hints are platform specific. These may be set on any platform but they
|
||||
// will only affect their specific platform. Other platforms will ignore them.
|
||||
// Setting these hints requires no platform specific headers or functions.
|
||||
//
|
||||
// This function must only be called from the main thread.
|
||||
func InitHint(hint Hint, value int) {
|
||||
C.glfwInitHint(C.int(hint), C.int(value))
|
||||
}
|
||||
|
||||
// GetVersion retrieves the major, minor and revision numbers of the GLFW
|
||||
// library. It is intended for when you are using GLFW as a shared library and
|
||||
// want to ensure that you are using the minimum required version.
|
||||
//
|
||||
// This function may be called before Init.
|
||||
func GetVersion() (major, minor, revision int) {
|
||||
var (
|
||||
maj C.int
|
||||
min C.int
|
||||
rev C.int
|
||||
)
|
||||
|
||||
C.glfwGetVersion(&maj, &min, &rev)
|
||||
return int(maj), int(min), int(rev)
|
||||
}
|
||||
|
||||
// GetVersionString returns a static string generated at compile-time according
|
||||
// to which configuration macros were defined. This is intended for use when
|
||||
// submitting bug reports, to allow developers to see which code paths are
|
||||
// enabled in a binary.
|
||||
//
|
||||
// This function may be called before Init.
|
||||
func GetVersionString() string {
|
||||
return C.GoString(C.glfwGetVersionString())
|
||||
}
|
||||
|
||||
// GetClipboardString returns the contents of the system clipboard, if it
|
||||
// contains or is convertible to a UTF-8 encoded string.
|
||||
//
|
||||
// This function may only be called from the main thread.
|
||||
func GetClipboardString() (string, error) {
|
||||
cs := C.glfwGetClipboardString(nil)
|
||||
if cs == nil {
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
if errors.Is(err, FormatUnavailable) {
|
||||
return "", nil
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
return C.GoString(cs), nil
|
||||
}
|
||||
|
||||
// SetClipboardString sets the system clipboard to the specified UTF-8 encoded
|
||||
// string.
|
||||
//
|
||||
// This function may only be called from the main thread.
|
||||
func SetClipboardString(str string) error {
|
||||
cp := C.CString(str)
|
||||
defer C.free(unsafe.Pointer(cp))
|
||||
C.glfwSetClipboardString(nil, cp)
|
||||
return fetchErrorIgnoringPlatformError()
|
||||
}
|
||||
+679
@@ -0,0 +1,679 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build freebsd || linux || netbsd || openbsd
|
||||
|
||||
#include "internal_unix.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifndef GLXBadProfileARB
|
||||
#define GLXBadProfileARB 13
|
||||
#endif
|
||||
|
||||
|
||||
// Returns the specified attribute of the specified GLXFBConfig
|
||||
//
|
||||
static int getGLXFBConfigAttrib(GLXFBConfig fbconfig, int attrib)
|
||||
{
|
||||
int value;
|
||||
glXGetFBConfigAttrib(_glfw.x11.display, fbconfig, attrib, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
// Return the GLXFBConfig most closely matching the specified hints
|
||||
//
|
||||
static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired,
|
||||
GLXFBConfig* result)
|
||||
{
|
||||
GLXFBConfig* nativeConfigs;
|
||||
_GLFWfbconfig* usableConfigs;
|
||||
const _GLFWfbconfig* closest;
|
||||
int i, nativeCount, usableCount;
|
||||
const char* vendor;
|
||||
GLFWbool trustWindowBit = GLFW_TRUE;
|
||||
|
||||
// HACK: This is a (hopefully temporary) workaround for Chromium
|
||||
// (VirtualBox GL) not setting the window bit on any GLXFBConfigs
|
||||
vendor = glXGetClientString(_glfw.x11.display, GLX_VENDOR);
|
||||
if (vendor && strcmp(vendor, "Chromium") == 0)
|
||||
trustWindowBit = GLFW_FALSE;
|
||||
|
||||
nativeConfigs =
|
||||
glXGetFBConfigs(_glfw.x11.display, _glfw.x11.screen, &nativeCount);
|
||||
if (!nativeConfigs || !nativeCount)
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE, "GLX: No GLXFBConfigs returned");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig));
|
||||
usableCount = 0;
|
||||
|
||||
for (i = 0; i < nativeCount; i++)
|
||||
{
|
||||
const GLXFBConfig n = nativeConfigs[i];
|
||||
_GLFWfbconfig* u = usableConfigs + usableCount;
|
||||
|
||||
// Only consider RGBA GLXFBConfigs
|
||||
if (!(getGLXFBConfigAttrib(n, GLX_RENDER_TYPE) & GLX_RGBA_BIT))
|
||||
continue;
|
||||
|
||||
// Only consider window GLXFBConfigs
|
||||
if (!(getGLXFBConfigAttrib(n, GLX_DRAWABLE_TYPE) & GLX_WINDOW_BIT))
|
||||
{
|
||||
if (trustWindowBit)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (getGLXFBConfigAttrib(n, GLX_DOUBLEBUFFER) != desired->doublebuffer)
|
||||
continue;
|
||||
|
||||
if (desired->transparent)
|
||||
{
|
||||
XVisualInfo* vi = glXGetVisualFromFBConfig(_glfw.x11.display, n);
|
||||
if (vi)
|
||||
{
|
||||
u->transparent = _glfwIsVisualTransparentX11(vi->visual);
|
||||
XFree(vi);
|
||||
}
|
||||
}
|
||||
|
||||
u->redBits = getGLXFBConfigAttrib(n, GLX_RED_SIZE);
|
||||
u->greenBits = getGLXFBConfigAttrib(n, GLX_GREEN_SIZE);
|
||||
u->blueBits = getGLXFBConfigAttrib(n, GLX_BLUE_SIZE);
|
||||
|
||||
u->alphaBits = getGLXFBConfigAttrib(n, GLX_ALPHA_SIZE);
|
||||
u->depthBits = getGLXFBConfigAttrib(n, GLX_DEPTH_SIZE);
|
||||
u->stencilBits = getGLXFBConfigAttrib(n, GLX_STENCIL_SIZE);
|
||||
|
||||
u->accumRedBits = getGLXFBConfigAttrib(n, GLX_ACCUM_RED_SIZE);
|
||||
u->accumGreenBits = getGLXFBConfigAttrib(n, GLX_ACCUM_GREEN_SIZE);
|
||||
u->accumBlueBits = getGLXFBConfigAttrib(n, GLX_ACCUM_BLUE_SIZE);
|
||||
u->accumAlphaBits = getGLXFBConfigAttrib(n, GLX_ACCUM_ALPHA_SIZE);
|
||||
|
||||
u->auxBuffers = getGLXFBConfigAttrib(n, GLX_AUX_BUFFERS);
|
||||
|
||||
if (getGLXFBConfigAttrib(n, GLX_STEREO))
|
||||
u->stereo = GLFW_TRUE;
|
||||
|
||||
if (_glfw.glx.ARB_multisample)
|
||||
u->samples = getGLXFBConfigAttrib(n, GLX_SAMPLES);
|
||||
|
||||
if (_glfw.glx.ARB_framebuffer_sRGB || _glfw.glx.EXT_framebuffer_sRGB)
|
||||
u->sRGB = getGLXFBConfigAttrib(n, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB);
|
||||
|
||||
u->handle = (uintptr_t) n;
|
||||
usableCount++;
|
||||
}
|
||||
|
||||
closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount);
|
||||
if (closest)
|
||||
*result = (GLXFBConfig) closest->handle;
|
||||
|
||||
XFree(nativeConfigs);
|
||||
free(usableConfigs);
|
||||
|
||||
return closest != NULL;
|
||||
}
|
||||
|
||||
// Create the OpenGL context using legacy API
|
||||
//
|
||||
static GLXContext createLegacyContextGLX(_GLFWwindow* window,
|
||||
GLXFBConfig fbconfig,
|
||||
GLXContext share)
|
||||
{
|
||||
return glXCreateNewContext(_glfw.x11.display,
|
||||
fbconfig,
|
||||
GLX_RGBA_TYPE,
|
||||
share,
|
||||
True);
|
||||
}
|
||||
|
||||
static void makeContextCurrentGLX(_GLFWwindow* window)
|
||||
{
|
||||
if (window)
|
||||
{
|
||||
if (!glXMakeCurrent(_glfw.x11.display,
|
||||
window->context.glx.window,
|
||||
window->context.glx.handle))
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"GLX: Failed to make context current");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!glXMakeCurrent(_glfw.x11.display, None, NULL))
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"GLX: Failed to clear current context");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_glfwPlatformSetTls(&_glfw.contextSlot, window);
|
||||
}
|
||||
|
||||
static void swapBuffersGLX(_GLFWwindow* window)
|
||||
{
|
||||
glXSwapBuffers(_glfw.x11.display, window->context.glx.window);
|
||||
}
|
||||
|
||||
static void swapIntervalGLX(int interval)
|
||||
{
|
||||
_GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot);
|
||||
|
||||
if (_glfw.glx.EXT_swap_control)
|
||||
{
|
||||
_glfw.glx.SwapIntervalEXT(_glfw.x11.display,
|
||||
window->context.glx.window,
|
||||
interval);
|
||||
}
|
||||
else if (_glfw.glx.MESA_swap_control)
|
||||
_glfw.glx.SwapIntervalMESA(interval);
|
||||
else if (_glfw.glx.SGI_swap_control)
|
||||
{
|
||||
if (interval > 0)
|
||||
_glfw.glx.SwapIntervalSGI(interval);
|
||||
}
|
||||
}
|
||||
|
||||
static int extensionSupportedGLX(const char* extension)
|
||||
{
|
||||
const char* extensions =
|
||||
glXQueryExtensionsString(_glfw.x11.display, _glfw.x11.screen);
|
||||
if (extensions)
|
||||
{
|
||||
if (_glfwStringInExtensionString(extension, extensions))
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
static GLFWglproc getProcAddressGLX(const char* procname)
|
||||
{
|
||||
if (_glfw.glx.GetProcAddress)
|
||||
return _glfw.glx.GetProcAddress((const GLubyte*) procname);
|
||||
else if (_glfw.glx.GetProcAddressARB)
|
||||
return _glfw.glx.GetProcAddressARB((const GLubyte*) procname);
|
||||
else
|
||||
return _glfw_dlsym(_glfw.glx.handle, procname);
|
||||
}
|
||||
|
||||
static void destroyContextGLX(_GLFWwindow* window)
|
||||
{
|
||||
if (window->context.glx.window)
|
||||
{
|
||||
glXDestroyWindow(_glfw.x11.display, window->context.glx.window);
|
||||
window->context.glx.window = None;
|
||||
}
|
||||
|
||||
if (window->context.glx.handle)
|
||||
{
|
||||
glXDestroyContext(_glfw.x11.display, window->context.glx.handle);
|
||||
window->context.glx.handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW internal API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Initialize GLX
|
||||
//
|
||||
GLFWbool _glfwInitGLX(void)
|
||||
{
|
||||
int i;
|
||||
const char* sonames[] =
|
||||
{
|
||||
#if defined(_GLFW_GLX_LIBRARY)
|
||||
_GLFW_GLX_LIBRARY,
|
||||
#elif defined(__CYGWIN__)
|
||||
"libGL-1.so",
|
||||
#elif defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
"libGL.so",
|
||||
#else
|
||||
"libGL.so.1",
|
||||
"libGL.so",
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
if (_glfw.glx.handle)
|
||||
return GLFW_TRUE;
|
||||
|
||||
for (i = 0; sonames[i]; i++)
|
||||
{
|
||||
_glfw.glx.handle = _glfw_dlopen(sonames[i]);
|
||||
if (_glfw.glx.handle)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!_glfw.glx.handle)
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE, "GLX: Failed to load GLX");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
_glfw.glx.GetFBConfigs =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXGetFBConfigs");
|
||||
_glfw.glx.GetFBConfigAttrib =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXGetFBConfigAttrib");
|
||||
_glfw.glx.GetClientString =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXGetClientString");
|
||||
_glfw.glx.QueryExtension =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXQueryExtension");
|
||||
_glfw.glx.QueryVersion =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXQueryVersion");
|
||||
_glfw.glx.DestroyContext =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXDestroyContext");
|
||||
_glfw.glx.MakeCurrent =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXMakeCurrent");
|
||||
_glfw.glx.SwapBuffers =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXSwapBuffers");
|
||||
_glfw.glx.QueryExtensionsString =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXQueryExtensionsString");
|
||||
_glfw.glx.CreateNewContext =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXCreateNewContext");
|
||||
_glfw.glx.CreateWindow =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXCreateWindow");
|
||||
_glfw.glx.DestroyWindow =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXDestroyWindow");
|
||||
_glfw.glx.GetVisualFromFBConfig =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXGetVisualFromFBConfig");
|
||||
|
||||
if (!_glfw.glx.GetFBConfigs ||
|
||||
!_glfw.glx.GetFBConfigAttrib ||
|
||||
!_glfw.glx.GetClientString ||
|
||||
!_glfw.glx.QueryExtension ||
|
||||
!_glfw.glx.QueryVersion ||
|
||||
!_glfw.glx.DestroyContext ||
|
||||
!_glfw.glx.MakeCurrent ||
|
||||
!_glfw.glx.SwapBuffers ||
|
||||
!_glfw.glx.QueryExtensionsString ||
|
||||
!_glfw.glx.CreateNewContext ||
|
||||
!_glfw.glx.CreateWindow ||
|
||||
!_glfw.glx.DestroyWindow ||
|
||||
!_glfw.glx.GetVisualFromFBConfig)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"GLX: Failed to load required entry points");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
// NOTE: Unlike GLX 1.3 entry points these are not required to be present
|
||||
_glfw.glx.GetProcAddress = (PFNGLXGETPROCADDRESSPROC)
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXGetProcAddress");
|
||||
_glfw.glx.GetProcAddressARB = (PFNGLXGETPROCADDRESSPROC)
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXGetProcAddressARB");
|
||||
|
||||
if (!glXQueryExtension(_glfw.x11.display,
|
||||
&_glfw.glx.errorBase,
|
||||
&_glfw.glx.eventBase))
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE, "GLX: GLX extension not found");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (!glXQueryVersion(_glfw.x11.display, &_glfw.glx.major, &_glfw.glx.minor))
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE,
|
||||
"GLX: Failed to query GLX version");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (_glfw.glx.major == 1 && _glfw.glx.minor < 3)
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE,
|
||||
"GLX: GLX version 1.3 is required");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (extensionSupportedGLX("GLX_EXT_swap_control"))
|
||||
{
|
||||
_glfw.glx.SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)
|
||||
getProcAddressGLX("glXSwapIntervalEXT");
|
||||
|
||||
if (_glfw.glx.SwapIntervalEXT)
|
||||
_glfw.glx.EXT_swap_control = GLFW_TRUE;
|
||||
}
|
||||
|
||||
if (extensionSupportedGLX("GLX_SGI_swap_control"))
|
||||
{
|
||||
_glfw.glx.SwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC)
|
||||
getProcAddressGLX("glXSwapIntervalSGI");
|
||||
|
||||
if (_glfw.glx.SwapIntervalSGI)
|
||||
_glfw.glx.SGI_swap_control = GLFW_TRUE;
|
||||
}
|
||||
|
||||
if (extensionSupportedGLX("GLX_MESA_swap_control"))
|
||||
{
|
||||
_glfw.glx.SwapIntervalMESA = (PFNGLXSWAPINTERVALMESAPROC)
|
||||
getProcAddressGLX("glXSwapIntervalMESA");
|
||||
|
||||
if (_glfw.glx.SwapIntervalMESA)
|
||||
_glfw.glx.MESA_swap_control = GLFW_TRUE;
|
||||
}
|
||||
|
||||
if (extensionSupportedGLX("GLX_ARB_multisample"))
|
||||
_glfw.glx.ARB_multisample = GLFW_TRUE;
|
||||
|
||||
if (extensionSupportedGLX("GLX_ARB_framebuffer_sRGB"))
|
||||
_glfw.glx.ARB_framebuffer_sRGB = GLFW_TRUE;
|
||||
|
||||
if (extensionSupportedGLX("GLX_EXT_framebuffer_sRGB"))
|
||||
_glfw.glx.EXT_framebuffer_sRGB = GLFW_TRUE;
|
||||
|
||||
if (extensionSupportedGLX("GLX_ARB_create_context"))
|
||||
{
|
||||
_glfw.glx.CreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)
|
||||
getProcAddressGLX("glXCreateContextAttribsARB");
|
||||
|
||||
if (_glfw.glx.CreateContextAttribsARB)
|
||||
_glfw.glx.ARB_create_context = GLFW_TRUE;
|
||||
}
|
||||
|
||||
if (extensionSupportedGLX("GLX_ARB_create_context_robustness"))
|
||||
_glfw.glx.ARB_create_context_robustness = GLFW_TRUE;
|
||||
|
||||
if (extensionSupportedGLX("GLX_ARB_create_context_profile"))
|
||||
_glfw.glx.ARB_create_context_profile = GLFW_TRUE;
|
||||
|
||||
if (extensionSupportedGLX("GLX_EXT_create_context_es2_profile"))
|
||||
_glfw.glx.EXT_create_context_es2_profile = GLFW_TRUE;
|
||||
|
||||
if (extensionSupportedGLX("GLX_ARB_create_context_no_error"))
|
||||
_glfw.glx.ARB_create_context_no_error = GLFW_TRUE;
|
||||
|
||||
if (extensionSupportedGLX("GLX_ARB_context_flush_control"))
|
||||
_glfw.glx.ARB_context_flush_control = GLFW_TRUE;
|
||||
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
// Terminate GLX
|
||||
//
|
||||
void _glfwTerminateGLX(void)
|
||||
{
|
||||
// NOTE: This function must not call any X11 functions, as it is called
|
||||
// after XCloseDisplay (see _glfwPlatformTerminate for details)
|
||||
|
||||
if (_glfw.glx.handle)
|
||||
{
|
||||
_glfw_dlclose(_glfw.glx.handle);
|
||||
_glfw.glx.handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#define setAttrib(a, v) \
|
||||
{ \
|
||||
assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \
|
||||
attribs[index++] = a; \
|
||||
attribs[index++] = v; \
|
||||
}
|
||||
|
||||
// Create the OpenGL or OpenGL ES context
|
||||
//
|
||||
GLFWbool _glfwCreateContextGLX(_GLFWwindow* window,
|
||||
const _GLFWctxconfig* ctxconfig,
|
||||
const _GLFWfbconfig* fbconfig)
|
||||
{
|
||||
int attribs[40];
|
||||
GLXFBConfig native = NULL;
|
||||
GLXContext share = NULL;
|
||||
|
||||
if (ctxconfig->share)
|
||||
share = ctxconfig->share->context.glx.handle;
|
||||
|
||||
if (!chooseGLXFBConfig(fbconfig, &native))
|
||||
{
|
||||
_glfwInputError(GLFW_FORMAT_UNAVAILABLE,
|
||||
"GLX: Failed to find a suitable GLXFBConfig");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (ctxconfig->client == GLFW_OPENGL_ES_API)
|
||||
{
|
||||
if (!_glfw.glx.ARB_create_context ||
|
||||
!_glfw.glx.ARB_create_context_profile ||
|
||||
!_glfw.glx.EXT_create_context_es2_profile)
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE,
|
||||
"GLX: OpenGL ES requested but GLX_EXT_create_context_es2_profile is unavailable");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctxconfig->forward)
|
||||
{
|
||||
if (!_glfw.glx.ARB_create_context)
|
||||
{
|
||||
_glfwInputError(GLFW_VERSION_UNAVAILABLE,
|
||||
"GLX: Forward compatibility requested but GLX_ARB_create_context_profile is unavailable");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctxconfig->profile)
|
||||
{
|
||||
if (!_glfw.glx.ARB_create_context ||
|
||||
!_glfw.glx.ARB_create_context_profile)
|
||||
{
|
||||
_glfwInputError(GLFW_VERSION_UNAVAILABLE,
|
||||
"GLX: An OpenGL profile requested but GLX_ARB_create_context_profile is unavailable");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
_glfwGrabErrorHandlerX11();
|
||||
|
||||
if (_glfw.glx.ARB_create_context)
|
||||
{
|
||||
int index = 0, mask = 0, flags = 0;
|
||||
|
||||
if (ctxconfig->client == GLFW_OPENGL_API)
|
||||
{
|
||||
if (ctxconfig->forward)
|
||||
flags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
|
||||
|
||||
if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE)
|
||||
mask |= GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
|
||||
else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE)
|
||||
mask |= GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
|
||||
}
|
||||
else
|
||||
mask |= GLX_CONTEXT_ES2_PROFILE_BIT_EXT;
|
||||
|
||||
if (ctxconfig->debug)
|
||||
flags |= GLX_CONTEXT_DEBUG_BIT_ARB;
|
||||
|
||||
if (ctxconfig->robustness)
|
||||
{
|
||||
if (_glfw.glx.ARB_create_context_robustness)
|
||||
{
|
||||
if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION)
|
||||
{
|
||||
setAttrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB,
|
||||
GLX_NO_RESET_NOTIFICATION_ARB);
|
||||
}
|
||||
else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET)
|
||||
{
|
||||
setAttrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB,
|
||||
GLX_LOSE_CONTEXT_ON_RESET_ARB);
|
||||
}
|
||||
|
||||
flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctxconfig->release)
|
||||
{
|
||||
if (_glfw.glx.ARB_context_flush_control)
|
||||
{
|
||||
if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE)
|
||||
{
|
||||
setAttrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB,
|
||||
GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB);
|
||||
}
|
||||
else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH)
|
||||
{
|
||||
setAttrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB,
|
||||
GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ctxconfig->noerror)
|
||||
{
|
||||
if (_glfw.glx.ARB_create_context_no_error)
|
||||
setAttrib(GLX_CONTEXT_OPENGL_NO_ERROR_ARB, GLFW_TRUE);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
setAttrib(GLX_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major);
|
||||
setAttrib(GLX_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor);
|
||||
}
|
||||
|
||||
if (mask)
|
||||
setAttrib(GLX_CONTEXT_PROFILE_MASK_ARB, mask);
|
||||
|
||||
if (flags)
|
||||
setAttrib(GLX_CONTEXT_FLAGS_ARB, flags);
|
||||
|
||||
setAttrib(None, None);
|
||||
|
||||
window->context.glx.handle =
|
||||
_glfw.glx.CreateContextAttribsARB(_glfw.x11.display,
|
||||
native,
|
||||
share,
|
||||
True,
|
||||
attribs);
|
||||
|
||||
// HACK: This is a fallback for broken versions of the Mesa
|
||||
// implementation of GLX_ARB_create_context_profile that fail
|
||||
// default 1.0 context creation with a GLXBadProfileARB error in
|
||||
// violation of the extension spec
|
||||
if (!window->context.glx.handle)
|
||||
{
|
||||
if (_glfw.x11.errorCode == _glfw.glx.errorBase + GLXBadProfileARB &&
|
||||
ctxconfig->client == GLFW_OPENGL_API &&
|
||||
ctxconfig->profile == GLFW_OPENGL_ANY_PROFILE &&
|
||||
ctxconfig->forward == GLFW_FALSE)
|
||||
{
|
||||
window->context.glx.handle =
|
||||
createLegacyContextGLX(window, native, share);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
window->context.glx.handle =
|
||||
createLegacyContextGLX(window, native, share);
|
||||
}
|
||||
|
||||
_glfwReleaseErrorHandlerX11();
|
||||
|
||||
if (!window->context.glx.handle)
|
||||
{
|
||||
_glfwInputErrorX11(GLFW_VERSION_UNAVAILABLE, "GLX: Failed to create context");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
window->context.glx.window =
|
||||
glXCreateWindow(_glfw.x11.display, native, window->x11.handle, NULL);
|
||||
if (!window->context.glx.window)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "GLX: Failed to create window");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
window->context.makeCurrent = makeContextCurrentGLX;
|
||||
window->context.swapBuffers = swapBuffersGLX;
|
||||
window->context.swapInterval = swapIntervalGLX;
|
||||
window->context.extensionSupported = extensionSupportedGLX;
|
||||
window->context.getProcAddress = getProcAddressGLX;
|
||||
window->context.destroy = destroyContextGLX;
|
||||
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
#undef setAttrib
|
||||
|
||||
// Returns the Visual and depth of the chosen GLXFBConfig
|
||||
//
|
||||
GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig,
|
||||
const _GLFWctxconfig* ctxconfig,
|
||||
const _GLFWfbconfig* fbconfig,
|
||||
Visual** visual, int* depth)
|
||||
{
|
||||
GLXFBConfig native;
|
||||
XVisualInfo* result;
|
||||
|
||||
if (!chooseGLXFBConfig(fbconfig, &native))
|
||||
{
|
||||
_glfwInputError(GLFW_FORMAT_UNAVAILABLE,
|
||||
"GLX: Failed to find a suitable GLXFBConfig");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
result = glXGetVisualFromFBConfig(_glfw.x11.display, native);
|
||||
if (!result)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"GLX: Failed to retrieve Visual for GLXFBConfig");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
*visual = result->visual;
|
||||
*depth = result->depth;
|
||||
|
||||
XFree(result);
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW native API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* handle)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
|
||||
if (window->context.source != GLFW_NATIVE_CONTEXT_API)
|
||||
{
|
||||
_glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return window->context.glx.handle;
|
||||
}
|
||||
|
||||
GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* handle)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(None);
|
||||
|
||||
if (window->context.source != GLFW_NATIVE_CONTEXT_API)
|
||||
{
|
||||
_glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
|
||||
return None;
|
||||
}
|
||||
|
||||
return window->context.glx.window;
|
||||
}
|
||||
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2017 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build freebsd || linux || netbsd || openbsd
|
||||
|
||||
#define GLX_VENDOR 1
|
||||
#define GLX_RGBA_BIT 0x00000001
|
||||
#define GLX_WINDOW_BIT 0x00000001
|
||||
#define GLX_DRAWABLE_TYPE 0x8010
|
||||
#define GLX_RENDER_TYPE 0x8011
|
||||
#define GLX_RGBA_TYPE 0x8014
|
||||
#define GLX_DOUBLEBUFFER 5
|
||||
#define GLX_STEREO 6
|
||||
#define GLX_AUX_BUFFERS 7
|
||||
#define GLX_RED_SIZE 8
|
||||
#define GLX_GREEN_SIZE 9
|
||||
#define GLX_BLUE_SIZE 10
|
||||
#define GLX_ALPHA_SIZE 11
|
||||
#define GLX_DEPTH_SIZE 12
|
||||
#define GLX_STENCIL_SIZE 13
|
||||
#define GLX_ACCUM_RED_SIZE 14
|
||||
#define GLX_ACCUM_GREEN_SIZE 15
|
||||
#define GLX_ACCUM_BLUE_SIZE 16
|
||||
#define GLX_ACCUM_ALPHA_SIZE 17
|
||||
#define GLX_SAMPLES 0x186a1
|
||||
#define GLX_VISUAL_ID 0x800b
|
||||
|
||||
#define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20b2
|
||||
#define GLX_CONTEXT_DEBUG_BIT_ARB 0x00000001
|
||||
#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
|
||||
#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
|
||||
#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126
|
||||
#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
|
||||
#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
|
||||
#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
|
||||
#define GLX_CONTEXT_FLAGS_ARB 0x2094
|
||||
#define GLX_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004
|
||||
#define GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004
|
||||
#define GLX_LOSE_CONTEXT_ON_RESET_ARB 0x8252
|
||||
#define GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256
|
||||
#define GLX_NO_RESET_NOTIFICATION_ARB 0x8261
|
||||
#define GLX_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097
|
||||
#define GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0
|
||||
#define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098
|
||||
#define GLX_CONTEXT_OPENGL_NO_ERROR_ARB 0x31b3
|
||||
|
||||
typedef XID GLXWindow;
|
||||
typedef XID GLXDrawable;
|
||||
typedef struct __GLXFBConfig* GLXFBConfig;
|
||||
typedef struct __GLXcontext* GLXContext;
|
||||
typedef void (*__GLXextproc)(void);
|
||||
|
||||
typedef int (*PFNGLXGETFBCONFIGATTRIBPROC)(Display*,GLXFBConfig,int,int*);
|
||||
typedef const char* (*PFNGLXGETCLIENTSTRINGPROC)(Display*,int);
|
||||
typedef Bool (*PFNGLXQUERYEXTENSIONPROC)(Display*,int*,int*);
|
||||
typedef Bool (*PFNGLXQUERYVERSIONPROC)(Display*,int*,int*);
|
||||
typedef void (*PFNGLXDESTROYCONTEXTPROC)(Display*,GLXContext);
|
||||
typedef Bool (*PFNGLXMAKECURRENTPROC)(Display*,GLXDrawable,GLXContext);
|
||||
typedef void (*PFNGLXSWAPBUFFERSPROC)(Display*,GLXDrawable);
|
||||
typedef const char* (*PFNGLXQUERYEXTENSIONSSTRINGPROC)(Display*,int);
|
||||
typedef GLXFBConfig* (*PFNGLXGETFBCONFIGSPROC)(Display*,int,int*);
|
||||
typedef GLXContext (*PFNGLXCREATENEWCONTEXTPROC)(Display*,GLXFBConfig,int,GLXContext,Bool);
|
||||
typedef __GLXextproc (* PFNGLXGETPROCADDRESSPROC)(const GLubyte *procName);
|
||||
typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display*,GLXDrawable,int);
|
||||
typedef XVisualInfo* (*PFNGLXGETVISUALFROMFBCONFIGPROC)(Display*,GLXFBConfig);
|
||||
typedef GLXWindow (*PFNGLXCREATEWINDOWPROC)(Display*,GLXFBConfig,Window,const int*);
|
||||
typedef void (*PFNGLXDESTROYWINDOWPROC)(Display*,GLXWindow);
|
||||
|
||||
typedef int (*PFNGLXSWAPINTERVALMESAPROC)(int);
|
||||
typedef int (*PFNGLXSWAPINTERVALSGIPROC)(int);
|
||||
typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLXContext,Bool,const int*);
|
||||
|
||||
// libGL.so function pointer typedefs
|
||||
#define glXGetFBConfigs _glfw.glx.GetFBConfigs
|
||||
#define glXGetFBConfigAttrib _glfw.glx.GetFBConfigAttrib
|
||||
#define glXGetClientString _glfw.glx.GetClientString
|
||||
#define glXQueryExtension _glfw.glx.QueryExtension
|
||||
#define glXQueryVersion _glfw.glx.QueryVersion
|
||||
#define glXDestroyContext _glfw.glx.DestroyContext
|
||||
#define glXMakeCurrent _glfw.glx.MakeCurrent
|
||||
#define glXSwapBuffers _glfw.glx.SwapBuffers
|
||||
#define glXQueryExtensionsString _glfw.glx.QueryExtensionsString
|
||||
#define glXCreateNewContext _glfw.glx.CreateNewContext
|
||||
#define glXGetVisualFromFBConfig _glfw.glx.GetVisualFromFBConfig
|
||||
#define glXCreateWindow _glfw.glx.CreateWindow
|
||||
#define glXDestroyWindow _glfw.glx.DestroyWindow
|
||||
|
||||
#define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextGLX glx
|
||||
#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryGLX glx
|
||||
|
||||
|
||||
// GLX-specific per-context data
|
||||
//
|
||||
typedef struct _GLFWcontextGLX
|
||||
{
|
||||
GLXContext handle;
|
||||
GLXWindow window;
|
||||
} _GLFWcontextGLX;
|
||||
|
||||
// GLX-specific global data
|
||||
//
|
||||
typedef struct _GLFWlibraryGLX
|
||||
{
|
||||
int major, minor;
|
||||
int eventBase;
|
||||
int errorBase;
|
||||
|
||||
// dlopen handle for libGL.so.1
|
||||
void* handle;
|
||||
|
||||
// GLX 1.3 functions
|
||||
PFNGLXGETFBCONFIGSPROC GetFBConfigs;
|
||||
PFNGLXGETFBCONFIGATTRIBPROC GetFBConfigAttrib;
|
||||
PFNGLXGETCLIENTSTRINGPROC GetClientString;
|
||||
PFNGLXQUERYEXTENSIONPROC QueryExtension;
|
||||
PFNGLXQUERYVERSIONPROC QueryVersion;
|
||||
PFNGLXDESTROYCONTEXTPROC DestroyContext;
|
||||
PFNGLXMAKECURRENTPROC MakeCurrent;
|
||||
PFNGLXSWAPBUFFERSPROC SwapBuffers;
|
||||
PFNGLXQUERYEXTENSIONSSTRINGPROC QueryExtensionsString;
|
||||
PFNGLXCREATENEWCONTEXTPROC CreateNewContext;
|
||||
PFNGLXGETVISUALFROMFBCONFIGPROC GetVisualFromFBConfig;
|
||||
PFNGLXCREATEWINDOWPROC CreateWindow;
|
||||
PFNGLXDESTROYWINDOWPROC DestroyWindow;
|
||||
|
||||
// GLX 1.4 and extension functions
|
||||
PFNGLXGETPROCADDRESSPROC GetProcAddress;
|
||||
PFNGLXGETPROCADDRESSPROC GetProcAddressARB;
|
||||
PFNGLXSWAPINTERVALSGIPROC SwapIntervalSGI;
|
||||
PFNGLXSWAPINTERVALEXTPROC SwapIntervalEXT;
|
||||
PFNGLXSWAPINTERVALMESAPROC SwapIntervalMESA;
|
||||
PFNGLXCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB;
|
||||
GLFWbool SGI_swap_control;
|
||||
GLFWbool EXT_swap_control;
|
||||
GLFWbool MESA_swap_control;
|
||||
GLFWbool ARB_multisample;
|
||||
GLFWbool ARB_framebuffer_sRGB;
|
||||
GLFWbool EXT_framebuffer_sRGB;
|
||||
GLFWbool ARB_create_context;
|
||||
GLFWbool ARB_create_context_profile;
|
||||
GLFWbool ARB_create_context_robustness;
|
||||
GLFWbool EXT_create_context_es2_profile;
|
||||
GLFWbool ARB_create_context_no_error;
|
||||
GLFWbool ARB_context_flush_control;
|
||||
} _GLFWlibraryGLX;
|
||||
|
||||
GLFWbool _glfwInitGLX(void);
|
||||
void _glfwTerminateGLX(void);
|
||||
GLFWbool _glfwCreateContextGLX(_GLFWwindow* window,
|
||||
const _GLFWctxconfig* ctxconfig,
|
||||
const _GLFWfbconfig* fbconfig);
|
||||
void _glfwDestroyContextGLX(_GLFWwindow* window);
|
||||
GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig,
|
||||
const _GLFWctxconfig* ctxconfig,
|
||||
const _GLFWfbconfig* fbconfig,
|
||||
Visual** visual, int* depth);
|
||||
|
||||
+387
@@ -0,0 +1,387 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2018 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || netbsd || openbsd
|
||||
|
||||
#include "internal_unix.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
// NOTE: The global variables below comprise all mutable global data in GLFW
|
||||
// Any other mutable global variable is a bug
|
||||
|
||||
// This contains all mutable state shared between compilation units of GLFW
|
||||
//
|
||||
_GLFWlibrary _glfw = { GLFW_FALSE };
|
||||
|
||||
// These are outside of _glfw so they can be used before initialization and
|
||||
// after termination without special handling when _glfw is cleared to zero
|
||||
//
|
||||
static _GLFWerror _glfwMainThreadError;
|
||||
static GLFWerrorfun _glfwErrorCallback;
|
||||
static _GLFWinitconfig _glfwInitHints =
|
||||
{
|
||||
{
|
||||
GLFW_TRUE, // macOS menu bar
|
||||
GLFW_TRUE // macOS bundle chdir
|
||||
}
|
||||
};
|
||||
|
||||
// Terminate the library
|
||||
//
|
||||
static void terminate(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
memset(&_glfw.callbacks, 0, sizeof(_glfw.callbacks));
|
||||
|
||||
while (_glfw.windowListHead)
|
||||
glfwDestroyWindow((GLFWwindow*) _glfw.windowListHead);
|
||||
|
||||
while (_glfw.cursorListHead)
|
||||
glfwDestroyCursor((GLFWcursor*) _glfw.cursorListHead);
|
||||
|
||||
for (i = 0; i < _glfw.monitorCount; i++)
|
||||
{
|
||||
_GLFWmonitor* monitor = _glfw.monitors[i];
|
||||
if (monitor->originalRamp.size)
|
||||
_glfwPlatformSetGammaRamp(monitor, &monitor->originalRamp);
|
||||
_glfwFreeMonitor(monitor);
|
||||
}
|
||||
|
||||
free(_glfw.monitors);
|
||||
_glfw.monitors = NULL;
|
||||
_glfw.monitorCount = 0;
|
||||
|
||||
_glfwPlatformTerminate();
|
||||
|
||||
_glfw.initialized = GLFW_FALSE;
|
||||
|
||||
while (_glfw.errorListHead)
|
||||
{
|
||||
_GLFWerror* error = _glfw.errorListHead;
|
||||
_glfw.errorListHead = error->next;
|
||||
free(error);
|
||||
}
|
||||
|
||||
_glfwPlatformDestroyTls(&_glfw.contextSlot);
|
||||
_glfwPlatformDestroyTls(&_glfw.errorSlot);
|
||||
_glfwPlatformDestroyMutex(&_glfw.errorLock);
|
||||
|
||||
memset(&_glfw, 0, sizeof(_glfw));
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW internal API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Encode a Unicode code point to a UTF-8 stream
|
||||
// Based on cutef8 by Jeff Bezanson (Public Domain)
|
||||
//
|
||||
size_t _glfwEncodeUTF8(char* s, uint32_t codepoint)
|
||||
{
|
||||
size_t count = 0;
|
||||
|
||||
if (codepoint < 0x80)
|
||||
s[count++] = (char) codepoint;
|
||||
else if (codepoint < 0x800)
|
||||
{
|
||||
s[count++] = (codepoint >> 6) | 0xc0;
|
||||
s[count++] = (codepoint & 0x3f) | 0x80;
|
||||
}
|
||||
else if (codepoint < 0x10000)
|
||||
{
|
||||
s[count++] = (codepoint >> 12) | 0xe0;
|
||||
s[count++] = ((codepoint >> 6) & 0x3f) | 0x80;
|
||||
s[count++] = (codepoint & 0x3f) | 0x80;
|
||||
}
|
||||
else if (codepoint < 0x110000)
|
||||
{
|
||||
s[count++] = (codepoint >> 18) | 0xf0;
|
||||
s[count++] = ((codepoint >> 12) & 0x3f) | 0x80;
|
||||
s[count++] = ((codepoint >> 6) & 0x3f) | 0x80;
|
||||
s[count++] = (codepoint & 0x3f) | 0x80;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// Splits and translates a text/uri-list into separate file paths
|
||||
// NOTE: This function destroys the provided string
|
||||
//
|
||||
char** _glfwParseUriList(char* text, int* count)
|
||||
{
|
||||
const char* prefix = "file://";
|
||||
char** paths = NULL;
|
||||
char* line;
|
||||
|
||||
*count = 0;
|
||||
|
||||
while ((line = strtok(text, "\r\n")))
|
||||
{
|
||||
char* path;
|
||||
|
||||
text = NULL;
|
||||
|
||||
if (line[0] == '#')
|
||||
continue;
|
||||
|
||||
if (strncmp(line, prefix, strlen(prefix)) == 0)
|
||||
{
|
||||
line += strlen(prefix);
|
||||
// TODO: Validate hostname
|
||||
while (*line != '/')
|
||||
line++;
|
||||
}
|
||||
|
||||
(*count)++;
|
||||
|
||||
path = calloc(strlen(line) + 1, 1);
|
||||
paths = realloc(paths, *count * sizeof(char*));
|
||||
paths[*count - 1] = path;
|
||||
|
||||
while (*line)
|
||||
{
|
||||
if (line[0] == '%' && line[1] && line[2])
|
||||
{
|
||||
const char digits[3] = { line[1], line[2], '\0' };
|
||||
*path = (char) strtol(digits, NULL, 16);
|
||||
line += 2;
|
||||
}
|
||||
else
|
||||
*path = *line;
|
||||
|
||||
path++;
|
||||
line++;
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
char* _glfw_strdup(const char* source)
|
||||
{
|
||||
const size_t length = strlen(source);
|
||||
char* result = calloc(length + 1, 1);
|
||||
strcpy(result, source);
|
||||
return result;
|
||||
}
|
||||
|
||||
int _glfw_min(int a, int b)
|
||||
{
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
int _glfw_max(int a, int b)
|
||||
{
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
float _glfw_fminf(float a, float b)
|
||||
{
|
||||
if (a != a)
|
||||
return b;
|
||||
else if (b != b)
|
||||
return a;
|
||||
else if (a < b)
|
||||
return a;
|
||||
else
|
||||
return b;
|
||||
}
|
||||
|
||||
float _glfw_fmaxf(float a, float b)
|
||||
{
|
||||
if (a != a)
|
||||
return b;
|
||||
else if (b != b)
|
||||
return a;
|
||||
else if (a > b)
|
||||
return a;
|
||||
else
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW event API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Notifies shared code of an error
|
||||
//
|
||||
void _glfwInputError(int code, const char* format, ...)
|
||||
{
|
||||
_GLFWerror* error;
|
||||
char description[_GLFW_MESSAGE_SIZE];
|
||||
|
||||
if (format)
|
||||
{
|
||||
va_list vl;
|
||||
|
||||
va_start(vl, format);
|
||||
vsnprintf(description, sizeof(description), format, vl);
|
||||
va_end(vl);
|
||||
|
||||
description[sizeof(description) - 1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
if (code == GLFW_NOT_INITIALIZED)
|
||||
strcpy(description, "The GLFW library is not initialized");
|
||||
else if (code == GLFW_NO_CURRENT_CONTEXT)
|
||||
strcpy(description, "There is no current context");
|
||||
else if (code == GLFW_INVALID_ENUM)
|
||||
strcpy(description, "Invalid argument for enum parameter");
|
||||
else if (code == GLFW_INVALID_VALUE)
|
||||
strcpy(description, "Invalid value for parameter");
|
||||
else if (code == GLFW_OUT_OF_MEMORY)
|
||||
strcpy(description, "Out of memory");
|
||||
else if (code == GLFW_API_UNAVAILABLE)
|
||||
strcpy(description, "The requested API is unavailable");
|
||||
else if (code == GLFW_VERSION_UNAVAILABLE)
|
||||
strcpy(description, "The requested API version is unavailable");
|
||||
else if (code == GLFW_PLATFORM_ERROR)
|
||||
strcpy(description, "A platform-specific error occurred");
|
||||
else if (code == GLFW_FORMAT_UNAVAILABLE)
|
||||
strcpy(description, "The requested format is unavailable");
|
||||
else if (code == GLFW_NO_WINDOW_CONTEXT)
|
||||
strcpy(description, "The specified window has no context");
|
||||
else
|
||||
strcpy(description, "ERROR: UNKNOWN GLFW ERROR");
|
||||
}
|
||||
|
||||
if (_glfw.initialized)
|
||||
{
|
||||
error = _glfwPlatformGetTls(&_glfw.errorSlot);
|
||||
if (!error)
|
||||
{
|
||||
error = calloc(1, sizeof(_GLFWerror));
|
||||
_glfwPlatformSetTls(&_glfw.errorSlot, error);
|
||||
_glfwPlatformLockMutex(&_glfw.errorLock);
|
||||
error->next = _glfw.errorListHead;
|
||||
_glfw.errorListHead = error;
|
||||
_glfwPlatformUnlockMutex(&_glfw.errorLock);
|
||||
}
|
||||
}
|
||||
else
|
||||
error = &_glfwMainThreadError;
|
||||
|
||||
error->code = code;
|
||||
strcpy(error->description, description);
|
||||
|
||||
if (_glfwErrorCallback)
|
||||
_glfwErrorCallback(code, description);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW public API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GLFWAPI int glfwInit(void)
|
||||
{
|
||||
if (_glfw.initialized)
|
||||
return GLFW_TRUE;
|
||||
|
||||
memset(&_glfw, 0, sizeof(_glfw));
|
||||
_glfw.hints.init = _glfwInitHints;
|
||||
|
||||
if (!_glfwPlatformInit())
|
||||
{
|
||||
terminate();
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (!_glfwPlatformCreateMutex(&_glfw.errorLock) ||
|
||||
!_glfwPlatformCreateTls(&_glfw.errorSlot) ||
|
||||
!_glfwPlatformCreateTls(&_glfw.contextSlot))
|
||||
{
|
||||
terminate();
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
_glfwPlatformSetTls(&_glfw.errorSlot, &_glfwMainThreadError);
|
||||
|
||||
_glfw.initialized = GLFW_TRUE;
|
||||
_glfw.timer.offset = _glfwPlatformGetTimerValue();
|
||||
|
||||
glfwDefaultWindowHints();
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
GLFWAPI void glfwTerminate(void)
|
||||
{
|
||||
if (!_glfw.initialized)
|
||||
return;
|
||||
|
||||
terminate();
|
||||
}
|
||||
|
||||
GLFWAPI void glfwInitHint(int hint, int value)
|
||||
{
|
||||
switch (hint)
|
||||
{
|
||||
case GLFW_COCOA_CHDIR_RESOURCES:
|
||||
_glfwInitHints.ns.chdir = value;
|
||||
return;
|
||||
case GLFW_COCOA_MENUBAR:
|
||||
_glfwInitHints.ns.menubar = value;
|
||||
return;
|
||||
}
|
||||
|
||||
_glfwInputError(GLFW_INVALID_ENUM,
|
||||
"Invalid init hint 0x%08X", hint);
|
||||
}
|
||||
|
||||
GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev)
|
||||
{
|
||||
if (major != NULL)
|
||||
*major = GLFW_VERSION_MAJOR;
|
||||
if (minor != NULL)
|
||||
*minor = GLFW_VERSION_MINOR;
|
||||
if (rev != NULL)
|
||||
*rev = GLFW_VERSION_REVISION;
|
||||
}
|
||||
|
||||
GLFWAPI const char* glfwGetVersionString(void)
|
||||
{
|
||||
return _glfwPlatformGetVersionString();
|
||||
}
|
||||
|
||||
GLFWAPI int glfwGetError(const char** description)
|
||||
{
|
||||
_GLFWerror* error;
|
||||
int code = GLFW_NO_ERROR;
|
||||
|
||||
if (description)
|
||||
*description = NULL;
|
||||
|
||||
if (_glfw.initialized)
|
||||
error = _glfwPlatformGetTls(&_glfw.errorSlot);
|
||||
else
|
||||
error = &_glfwMainThreadError;
|
||||
|
||||
if (error)
|
||||
{
|
||||
code = error->code;
|
||||
error->code = GLFW_NO_ERROR;
|
||||
if (description && code)
|
||||
*description = error->description;
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun)
|
||||
{
|
||||
_GLFW_SWAP_POINTERS(_glfwErrorCallback, cbfun);
|
||||
return cbfun;
|
||||
}
|
||||
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2018 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
package glfw
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
_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 {
|
||||
// InvalidValue can happen when specific joysticks are used. This issue
|
||||
// will be fixed in GLFW 3.3.5. As a temporary fix, ignore this error.
|
||||
// See go-gl/glfw#292, go-gl/glfw#324, and glfw/glfw#1763
|
||||
// (#1229).
|
||||
if errors.Is(ferr, InvalidValue) {
|
||||
ferr = nil
|
||||
return
|
||||
}
|
||||
_ = 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
|
||||
}
|
||||
+616
@@ -0,0 +1,616 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || netbsd || openbsd
|
||||
|
||||
#include "internal_unix.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// Internal key state used for sticky keys
|
||||
#define _GLFW_STICK 3
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW event API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Notifies shared code of a physical key event
|
||||
//
|
||||
void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int mods)
|
||||
{
|
||||
if (key >= 0 && key <= GLFW_KEY_LAST)
|
||||
{
|
||||
GLFWbool repeated = GLFW_FALSE;
|
||||
|
||||
if (action == GLFW_RELEASE && window->keys[key] == GLFW_RELEASE)
|
||||
return;
|
||||
|
||||
if (action == GLFW_PRESS && window->keys[key] == GLFW_PRESS)
|
||||
repeated = GLFW_TRUE;
|
||||
|
||||
if (action == GLFW_RELEASE && window->stickyKeys)
|
||||
window->keys[key] = _GLFW_STICK;
|
||||
else
|
||||
window->keys[key] = (char) action;
|
||||
|
||||
if (repeated)
|
||||
action = GLFW_REPEAT;
|
||||
}
|
||||
|
||||
if (!window->lockKeyMods)
|
||||
mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
|
||||
|
||||
if (window->callbacks.key)
|
||||
window->callbacks.key((GLFWwindow*) window, key, scancode, action, mods);
|
||||
}
|
||||
|
||||
// Notifies shared code of a Unicode codepoint input event
|
||||
// The 'plain' parameter determines whether to emit a regular character event
|
||||
//
|
||||
void _glfwInputChar(_GLFWwindow* window, uint32_t codepoint, int mods, GLFWbool plain)
|
||||
{
|
||||
if (codepoint < 32 || (codepoint > 126 && codepoint < 160))
|
||||
return;
|
||||
|
||||
if (!window->lockKeyMods)
|
||||
mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
|
||||
|
||||
if (window->callbacks.charmods)
|
||||
window->callbacks.charmods((GLFWwindow*) window, codepoint, mods);
|
||||
|
||||
if (plain)
|
||||
{
|
||||
if (window->callbacks.character)
|
||||
window->callbacks.character((GLFWwindow*) window, codepoint);
|
||||
}
|
||||
}
|
||||
|
||||
// Notifies shared code of a scroll event
|
||||
//
|
||||
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset)
|
||||
{
|
||||
if (window->callbacks.scroll)
|
||||
window->callbacks.scroll((GLFWwindow*) window, xoffset, yoffset);
|
||||
}
|
||||
|
||||
// Notifies shared code of a mouse button click event
|
||||
//
|
||||
void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods)
|
||||
{
|
||||
if (button < 0 || button > GLFW_MOUSE_BUTTON_LAST)
|
||||
return;
|
||||
|
||||
if (!window->lockKeyMods)
|
||||
mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
|
||||
|
||||
if (action == GLFW_RELEASE && window->stickyMouseButtons)
|
||||
window->mouseButtons[button] = _GLFW_STICK;
|
||||
else
|
||||
window->mouseButtons[button] = (char) action;
|
||||
|
||||
if (window->callbacks.mouseButton)
|
||||
window->callbacks.mouseButton((GLFWwindow*) window, button, action, mods);
|
||||
}
|
||||
|
||||
// Notifies shared code of a cursor motion event
|
||||
// The position is specified in content area relative screen coordinates
|
||||
//
|
||||
void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos)
|
||||
{
|
||||
if (window->virtualCursorPosX == xpos && window->virtualCursorPosY == ypos)
|
||||
return;
|
||||
|
||||
window->virtualCursorPosX = xpos;
|
||||
window->virtualCursorPosY = ypos;
|
||||
|
||||
if (window->callbacks.cursorPos)
|
||||
window->callbacks.cursorPos((GLFWwindow*) window, xpos, ypos);
|
||||
}
|
||||
|
||||
// Notifies shared code of a cursor enter/leave event
|
||||
//
|
||||
void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool entered)
|
||||
{
|
||||
if (window->callbacks.cursorEnter)
|
||||
window->callbacks.cursorEnter((GLFWwindow*) window, entered);
|
||||
}
|
||||
|
||||
// Notifies shared code of files or directories dropped on a window
|
||||
//
|
||||
void _glfwInputDrop(_GLFWwindow* window, int count, const char** paths)
|
||||
{
|
||||
if (window->callbacks.drop)
|
||||
window->callbacks.drop((GLFWwindow*) window, count, paths);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW internal API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Center the cursor in the content area of the specified window
|
||||
//
|
||||
void _glfwCenterCursorInContentArea(_GLFWwindow* window)
|
||||
{
|
||||
int width, height;
|
||||
|
||||
_glfwPlatformGetWindowSize(window, &width, &height);
|
||||
_glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW public API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(0);
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case GLFW_CURSOR:
|
||||
return window->cursorMode;
|
||||
case GLFW_STICKY_KEYS:
|
||||
return window->stickyKeys;
|
||||
case GLFW_STICKY_MOUSE_BUTTONS:
|
||||
return window->stickyMouseButtons;
|
||||
case GLFW_LOCK_KEY_MODS:
|
||||
return window->lockKeyMods;
|
||||
case GLFW_RAW_MOUSE_MOTION:
|
||||
return window->rawMouseMotion;
|
||||
}
|
||||
|
||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT();
|
||||
|
||||
if (mode == GLFW_CURSOR)
|
||||
{
|
||||
if (value != GLFW_CURSOR_NORMAL &&
|
||||
value != GLFW_CURSOR_HIDDEN &&
|
||||
value != GLFW_CURSOR_DISABLED)
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_ENUM,
|
||||
"Invalid cursor mode 0x%08X",
|
||||
value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (window->cursorMode == value)
|
||||
return;
|
||||
|
||||
window->cursorMode = value;
|
||||
|
||||
_glfwPlatformGetCursorPos(window,
|
||||
&window->virtualCursorPosX,
|
||||
&window->virtualCursorPosY);
|
||||
_glfwPlatformSetCursorMode(window, value);
|
||||
}
|
||||
else if (mode == GLFW_STICKY_KEYS)
|
||||
{
|
||||
value = value ? GLFW_TRUE : GLFW_FALSE;
|
||||
if (window->stickyKeys == value)
|
||||
return;
|
||||
|
||||
if (!value)
|
||||
{
|
||||
int i;
|
||||
|
||||
// Release all sticky keys
|
||||
for (i = 0; i <= GLFW_KEY_LAST; i++)
|
||||
{
|
||||
if (window->keys[i] == _GLFW_STICK)
|
||||
window->keys[i] = GLFW_RELEASE;
|
||||
}
|
||||
}
|
||||
|
||||
window->stickyKeys = value;
|
||||
}
|
||||
else if (mode == GLFW_STICKY_MOUSE_BUTTONS)
|
||||
{
|
||||
value = value ? GLFW_TRUE : GLFW_FALSE;
|
||||
if (window->stickyMouseButtons == value)
|
||||
return;
|
||||
|
||||
if (!value)
|
||||
{
|
||||
int i;
|
||||
|
||||
// Release all sticky mouse buttons
|
||||
for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++)
|
||||
{
|
||||
if (window->mouseButtons[i] == _GLFW_STICK)
|
||||
window->mouseButtons[i] = GLFW_RELEASE;
|
||||
}
|
||||
}
|
||||
|
||||
window->stickyMouseButtons = value;
|
||||
}
|
||||
else if (mode == GLFW_LOCK_KEY_MODS)
|
||||
{
|
||||
window->lockKeyMods = value ? GLFW_TRUE : GLFW_FALSE;
|
||||
}
|
||||
else if (mode == GLFW_RAW_MOUSE_MOTION)
|
||||
{
|
||||
if (!_glfwPlatformRawMouseMotionSupported())
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Raw mouse motion is not supported on this system");
|
||||
return;
|
||||
}
|
||||
|
||||
value = value ? GLFW_TRUE : GLFW_FALSE;
|
||||
if (window->rawMouseMotion == value)
|
||||
return;
|
||||
|
||||
window->rawMouseMotion = value;
|
||||
_glfwPlatformSetRawMouseMotion(window, value);
|
||||
}
|
||||
else
|
||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode);
|
||||
}
|
||||
|
||||
GLFWAPI int glfwRawMouseMotionSupported(void)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
|
||||
return _glfwPlatformRawMouseMotionSupported();
|
||||
}
|
||||
|
||||
GLFWAPI const char* glfwGetKeyName(int key, int scancode)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
|
||||
if (key != GLFW_KEY_UNKNOWN)
|
||||
{
|
||||
if (key != GLFW_KEY_KP_EQUAL &&
|
||||
(key < GLFW_KEY_KP_0 || key > GLFW_KEY_KP_ADD) &&
|
||||
(key < GLFW_KEY_APOSTROPHE || key > GLFW_KEY_WORLD_2))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
scancode = _glfwPlatformGetKeyScancode(key);
|
||||
}
|
||||
|
||||
return _glfwPlatformGetScancodeName(scancode);
|
||||
}
|
||||
|
||||
GLFWAPI int glfwGetKeyScancode(int key)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(-1);
|
||||
|
||||
if (key < GLFW_KEY_SPACE || key > GLFW_KEY_LAST)
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid key %i", key);
|
||||
return GLFW_RELEASE;
|
||||
}
|
||||
|
||||
return _glfwPlatformGetKeyScancode(key);
|
||||
}
|
||||
|
||||
GLFWAPI int glfwGetKey(GLFWwindow* handle, int key)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_RELEASE);
|
||||
|
||||
if (key < GLFW_KEY_SPACE || key > GLFW_KEY_LAST)
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid key %i", key);
|
||||
return GLFW_RELEASE;
|
||||
}
|
||||
|
||||
if (window->keys[key] == _GLFW_STICK)
|
||||
{
|
||||
// Sticky mode: release key now
|
||||
window->keys[key] = GLFW_RELEASE;
|
||||
return GLFW_PRESS;
|
||||
}
|
||||
|
||||
return (int) window->keys[key];
|
||||
}
|
||||
|
||||
GLFWAPI int glfwGetMouseButton(GLFWwindow* handle, int button)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_RELEASE);
|
||||
|
||||
if (button < GLFW_MOUSE_BUTTON_1 || button > GLFW_MOUSE_BUTTON_LAST)
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid mouse button %i", button);
|
||||
return GLFW_RELEASE;
|
||||
}
|
||||
|
||||
if (window->mouseButtons[button] == _GLFW_STICK)
|
||||
{
|
||||
// Sticky mode: release mouse button now
|
||||
window->mouseButtons[button] = GLFW_RELEASE;
|
||||
return GLFW_PRESS;
|
||||
}
|
||||
|
||||
return (int) window->mouseButtons[button];
|
||||
}
|
||||
|
||||
GLFWAPI void glfwGetCursorPos(GLFWwindow* handle, double* xpos, double* ypos)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
|
||||
if (xpos)
|
||||
*xpos = 0;
|
||||
if (ypos)
|
||||
*ypos = 0;
|
||||
|
||||
_GLFW_REQUIRE_INIT();
|
||||
|
||||
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
||||
{
|
||||
if (xpos)
|
||||
*xpos = window->virtualCursorPosX;
|
||||
if (ypos)
|
||||
*ypos = window->virtualCursorPosY;
|
||||
}
|
||||
else
|
||||
_glfwPlatformGetCursorPos(window, xpos, ypos);
|
||||
}
|
||||
|
||||
GLFWAPI void glfwSetCursorPos(GLFWwindow* handle, double xpos, double ypos)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT();
|
||||
|
||||
if (xpos != xpos || xpos < -DBL_MAX || xpos > DBL_MAX ||
|
||||
ypos != ypos || ypos < -DBL_MAX || ypos > DBL_MAX)
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_VALUE,
|
||||
"Invalid cursor position %f %f",
|
||||
xpos, ypos);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_glfwPlatformWindowFocused(window))
|
||||
return;
|
||||
|
||||
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
||||
{
|
||||
// Only update the accumulated position if the cursor is disabled
|
||||
window->virtualCursorPosX = xpos;
|
||||
window->virtualCursorPosY = ypos;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update system cursor position
|
||||
_glfwPlatformSetCursorPos(window, xpos, ypos);
|
||||
}
|
||||
}
|
||||
|
||||
GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot)
|
||||
{
|
||||
_GLFWcursor* cursor;
|
||||
|
||||
assert(image != NULL);
|
||||
assert(image->pixels != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
|
||||
if (image->width <= 0 || image->height <= 0)
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_VALUE, "Invalid image dimensions for cursor");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cursor = calloc(1, sizeof(_GLFWcursor));
|
||||
cursor->next = _glfw.cursorListHead;
|
||||
_glfw.cursorListHead = cursor;
|
||||
|
||||
if (!_glfwPlatformCreateCursor(cursor, image, xhot, yhot))
|
||||
{
|
||||
glfwDestroyCursor((GLFWcursor*) cursor);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (GLFWcursor*) cursor;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape)
|
||||
{
|
||||
_GLFWcursor* cursor;
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
|
||||
if (shape != GLFW_ARROW_CURSOR &&
|
||||
shape != GLFW_IBEAM_CURSOR &&
|
||||
shape != GLFW_CROSSHAIR_CURSOR &&
|
||||
shape != GLFW_HAND_CURSOR &&
|
||||
shape != GLFW_HRESIZE_CURSOR &&
|
||||
shape != GLFW_VRESIZE_CURSOR &&
|
||||
shape != GLFW_RESIZE_NWSE_CURSOR &&
|
||||
shape != GLFW_RESIZE_NESW_CURSOR &&
|
||||
shape != GLFW_RESIZE_ALL_CURSOR &&
|
||||
shape != GLFW_NOT_ALLOWED_CURSOR)
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid standard cursor 0x%08X", shape);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cursor = calloc(1, sizeof(_GLFWcursor));
|
||||
cursor->next = _glfw.cursorListHead;
|
||||
_glfw.cursorListHead = cursor;
|
||||
|
||||
if (!_glfwPlatformCreateStandardCursor(cursor, shape))
|
||||
{
|
||||
glfwDestroyCursor((GLFWcursor*) cursor);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (GLFWcursor*) cursor;
|
||||
}
|
||||
|
||||
GLFWAPI void glfwDestroyCursor(GLFWcursor* handle)
|
||||
{
|
||||
_GLFWcursor* cursor = (_GLFWcursor*) handle;
|
||||
|
||||
_GLFW_REQUIRE_INIT();
|
||||
|
||||
if (cursor == NULL)
|
||||
return;
|
||||
|
||||
// Make sure the cursor is not being used by any window
|
||||
{
|
||||
_GLFWwindow* window;
|
||||
|
||||
for (window = _glfw.windowListHead; window; window = window->next)
|
||||
{
|
||||
if (window->cursor == cursor)
|
||||
glfwSetCursor((GLFWwindow*) window, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
_glfwPlatformDestroyCursor(cursor);
|
||||
|
||||
// Unlink cursor from global linked list
|
||||
{
|
||||
_GLFWcursor** prev = &_glfw.cursorListHead;
|
||||
|
||||
while (*prev != cursor)
|
||||
prev = &((*prev)->next);
|
||||
|
||||
*prev = cursor->next;
|
||||
}
|
||||
|
||||
free(cursor);
|
||||
}
|
||||
|
||||
GLFWAPI void glfwSetCursor(GLFWwindow* windowHandle, GLFWcursor* cursorHandle)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) windowHandle;
|
||||
_GLFWcursor* cursor = (_GLFWcursor*) cursorHandle;
|
||||
assert(window != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT();
|
||||
|
||||
window->cursor = cursor;
|
||||
|
||||
_glfwPlatformSetCursor(window, cursor);
|
||||
}
|
||||
|
||||
GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* handle, GLFWkeyfun cbfun)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
_GLFW_SWAP_POINTERS(window->callbacks.key, cbfun);
|
||||
return cbfun;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* handle, GLFWcharfun cbfun)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
_GLFW_SWAP_POINTERS(window->callbacks.character, cbfun);
|
||||
return cbfun;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* handle, GLFWcharmodsfun cbfun)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
_GLFW_SWAP_POINTERS(window->callbacks.charmods, cbfun);
|
||||
return cbfun;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* handle,
|
||||
GLFWmousebuttonfun cbfun)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
_GLFW_SWAP_POINTERS(window->callbacks.mouseButton, cbfun);
|
||||
return cbfun;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* handle,
|
||||
GLFWcursorposfun cbfun)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
_GLFW_SWAP_POINTERS(window->callbacks.cursorPos, cbfun);
|
||||
return cbfun;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* handle,
|
||||
GLFWcursorenterfun cbfun)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
_GLFW_SWAP_POINTERS(window->callbacks.cursorEnter, cbfun);
|
||||
return cbfun;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* handle,
|
||||
GLFWscrollfun cbfun)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
_GLFW_SWAP_POINTERS(window->callbacks.scroll, cbfun);
|
||||
return cbfun;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* handle, GLFWdropfun cbfun)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
_GLFW_SWAP_POINTERS(window->callbacks.drop, cbfun);
|
||||
return cbfun;
|
||||
}
|
||||
|
||||
GLFWAPI void glfwSetClipboardString(GLFWwindow* handle, const char* string)
|
||||
{
|
||||
assert(string != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT();
|
||||
_glfwPlatformSetClipboardString(string);
|
||||
}
|
||||
|
||||
GLFWAPI const char* glfwGetClipboardString(GLFWwindow* handle)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
return _glfwPlatformGetClipboardString();
|
||||
}
|
||||
+486
@@ -0,0 +1,486 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2012 The glfw3-go Authors
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || netbsd || openbsd
|
||||
|
||||
package glfw
|
||||
|
||||
// #include <stdlib.h>
|
||||
// #define GLFW_INCLUDE_NONE
|
||||
// #include "glfw3_unix.h"
|
||||
//
|
||||
// void goKeyCB(void* window, int key, int scancode, int action, int mods);
|
||||
// void goCharCB(void* window, unsigned int character);
|
||||
// void goCharModsCB(void* window, unsigned int character, int mods);
|
||||
// void goMouseButtonCB(void* window, int button, int action, int mods);
|
||||
// void goCursorPosCB(void* window, double xpos, double ypos);
|
||||
// void goCursorEnterCB(void* window, int entered);
|
||||
// void goScrollCB(void* window, double xoff, double yoff);
|
||||
// void goDropCB(void* window, int count, char** names);
|
||||
//
|
||||
// static void glfwSetKeyCallbackCB(GLFWwindow *window) {
|
||||
// glfwSetKeyCallback(window, (GLFWkeyfun)goKeyCB);
|
||||
// }
|
||||
//
|
||||
// static void glfwSetCharCallbackCB(GLFWwindow *window) {
|
||||
// glfwSetCharCallback(window, (GLFWcharfun)goCharCB);
|
||||
// }
|
||||
//
|
||||
// static void glfwSetCharModsCallbackCB(GLFWwindow *window) {
|
||||
// glfwSetCharModsCallback(window, (GLFWcharmodsfun)goCharModsCB);
|
||||
// }
|
||||
//
|
||||
// static void glfwSetMouseButtonCallbackCB(GLFWwindow *window) {
|
||||
// glfwSetMouseButtonCallback(window, (GLFWmousebuttonfun)goMouseButtonCB);
|
||||
// }
|
||||
//
|
||||
// static void glfwSetCursorPosCallbackCB(GLFWwindow *window) {
|
||||
// glfwSetCursorPosCallback(window, (GLFWcursorposfun)goCursorPosCB);
|
||||
// }
|
||||
//
|
||||
// static void glfwSetCursorEnterCallbackCB(GLFWwindow *window) {
|
||||
// glfwSetCursorEnterCallback(window, (GLFWcursorenterfun)goCursorEnterCB);
|
||||
// }
|
||||
//
|
||||
// static void glfwSetScrollCallbackCB(GLFWwindow *window) {
|
||||
// glfwSetScrollCallback(window, (GLFWscrollfun)goScrollCB);
|
||||
// }
|
||||
//
|
||||
// static void glfwSetDropCallbackCB(GLFWwindow *window) {
|
||||
// glfwSetDropCallback(window, (GLFWdropfun)goDropCB);
|
||||
// }
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"image"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Cursor represents a cursor.
|
||||
type Cursor struct {
|
||||
data *C.GLFWcursor
|
||||
}
|
||||
|
||||
//export goMouseButtonCB
|
||||
func goMouseButtonCB(window unsafe.Pointer, button, action, mods C.int) {
|
||||
w := windows.get((*C.GLFWwindow)(window))
|
||||
w.fMouseButtonHolder(w, MouseButton(button), Action(action), ModifierKey(mods))
|
||||
}
|
||||
|
||||
//export goCursorPosCB
|
||||
func goCursorPosCB(window unsafe.Pointer, xpos, ypos C.double) {
|
||||
w := windows.get((*C.GLFWwindow)(window))
|
||||
w.fCursorPosHolder(w, float64(xpos), float64(ypos))
|
||||
}
|
||||
|
||||
//export goCursorEnterCB
|
||||
func goCursorEnterCB(window unsafe.Pointer, entered C.int) {
|
||||
w := windows.get((*C.GLFWwindow)(window))
|
||||
hasEntered := entered != 0
|
||||
w.fCursorEnterHolder(w, hasEntered)
|
||||
}
|
||||
|
||||
//export goScrollCB
|
||||
func goScrollCB(window unsafe.Pointer, xoff, yoff C.double) {
|
||||
w := windows.get((*C.GLFWwindow)(window))
|
||||
w.fScrollHolder(w, float64(xoff), float64(yoff))
|
||||
}
|
||||
|
||||
//export goKeyCB
|
||||
func goKeyCB(window unsafe.Pointer, key, scancode, action, mods C.int) {
|
||||
w := windows.get((*C.GLFWwindow)(window))
|
||||
w.fKeyHolder(w, Key(key), int(scancode), Action(action), ModifierKey(mods))
|
||||
}
|
||||
|
||||
//export goCharCB
|
||||
func goCharCB(window unsafe.Pointer, character C.uint) {
|
||||
w := windows.get((*C.GLFWwindow)(window))
|
||||
w.fCharHolder(w, rune(character))
|
||||
}
|
||||
|
||||
//export goCharModsCB
|
||||
func goCharModsCB(window unsafe.Pointer, character C.uint, mods C.int) {
|
||||
w := windows.get((*C.GLFWwindow)(window))
|
||||
w.fCharModsHolder(w, rune(character), ModifierKey(mods))
|
||||
}
|
||||
|
||||
//export goDropCB
|
||||
func goDropCB(window unsafe.Pointer, count C.int, names **C.char) { // TODO: The types of name can be `**C.char` or `unsafe.Pointer`, use whichever is better.
|
||||
w := windows.get((*C.GLFWwindow)(window))
|
||||
namesSlice := make([]string, int(count)) // TODO: Make this better. This part is unfinished, hacky, probably not correct, and not idiomatic.
|
||||
for i := 0; i < int(count); i++ { // TODO: Make this better. It should be cleaned up and vetted.
|
||||
var x *C.char // TODO: Make this better.
|
||||
p := (**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(names)) + uintptr(i)*unsafe.Sizeof(x))) // TODO: Make this better.
|
||||
namesSlice[i] = C.GoString(*p) // TODO: Make this better.
|
||||
}
|
||||
w.fDropHolder(w, namesSlice)
|
||||
}
|
||||
|
||||
// GetInputMode returns the value of an input option of the window.
|
||||
func (w *Window) GetInputMode(mode InputMode) (int, error) {
|
||||
ret := int(C.glfwGetInputMode(w.data, C.int(mode)))
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// SetInputMode sets an input option for the window.
|
||||
func (w *Window) SetInputMode(mode InputMode, value int) error {
|
||||
C.glfwSetInputMode(w.data, C.int(mode), C.int(value))
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RawMouseMotionSupported returns whether raw mouse motion is supported on the
|
||||
// current system. This status does not change after GLFW has been initialized
|
||||
// so you only need to check this once. If you attempt to enable raw motion on
|
||||
// a system that does not support it, PlatformError will be emitted.
|
||||
//
|
||||
// Raw mouse motion is closer to the actual motion of the mouse across a
|
||||
// surface. It is not affected by the scaling and acceleration applied to the
|
||||
// motion of the desktop cursor. That processing is suitable for a cursor while
|
||||
// raw motion is better for controlling for example a 3D camera. Because of
|
||||
// this, raw mouse motion is only provided when the cursor is disabled.
|
||||
//
|
||||
// This function must only be called from the main thread.
|
||||
func RawMouseMotionSupported() bool {
|
||||
return int(C.glfwRawMouseMotionSupported()) == True
|
||||
}
|
||||
|
||||
// GetKeyScancode function returns the platform-specific scancode of the
|
||||
// specified key.
|
||||
//
|
||||
// If the key is KeyUnknown or does not exist on the keyboard this method will
|
||||
// return -1.
|
||||
func GetKeyScancode(key Key) int {
|
||||
return int(C.glfwGetKeyScancode(C.int(key)))
|
||||
}
|
||||
|
||||
// GetKey returns the last reported state of a keyboard key. The returned state
|
||||
// is one of Press or Release. The higher-level state Repeat is only reported to
|
||||
// the key callback.
|
||||
//
|
||||
// If the StickyKeys input mode is enabled, this function returns Press the first
|
||||
// time you call this function after a key has been pressed, even if the key has
|
||||
// already been released.
|
||||
//
|
||||
// The key functions deal with physical keys, with key tokens named after their
|
||||
// use on the standard US keyboard layout. If you want to input text, use the
|
||||
// Unicode character callback instead.
|
||||
func (w *Window) GetKey(key Key) (Action, error) {
|
||||
ret := Action(C.glfwGetKey(w.data, C.int(key)))
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// GetKeyName returns the localized name of the specified printable key.
|
||||
//
|
||||
// If the key is glfw.KeyUnknown, the scancode is used, otherwise the scancode is ignored.
|
||||
func GetKeyName(key Key, scancode int) (string, error) {
|
||||
ret := C.glfwGetKeyName(C.int(key), C.int(scancode))
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return C.GoString(ret), nil
|
||||
}
|
||||
|
||||
// GetMouseButton returns the last state reported for the specified mouse button.
|
||||
//
|
||||
// If the StickyMouseButtons input mode is enabled, this function returns Press
|
||||
// the first time you call this function after a mouse button has been pressed,
|
||||
// even if the mouse button has already been released.
|
||||
func (w *Window) GetMouseButton(button MouseButton) (Action, error) {
|
||||
ret := Action(C.glfwGetMouseButton(w.data, C.int(button)))
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// GetCursorPos returns the last reported position of the cursor.
|
||||
//
|
||||
// If the cursor is disabled (with CursorDisabled) then the cursor position is
|
||||
// unbounded and limited only by the minimum and maximum values of a double.
|
||||
//
|
||||
// The coordinate can be converted to their integer equivalents with the floor
|
||||
// function. Casting directly to an integer type works for positive coordinates,
|
||||
// but fails for negative ones.
|
||||
func (w *Window) GetCursorPos() (x, y float64, err error) {
|
||||
var xpos, ypos C.double
|
||||
C.glfwGetCursorPos(w.data, &xpos, &ypos)
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return float64(xpos), float64(ypos), nil
|
||||
}
|
||||
|
||||
// SetCursorPos sets the position of the cursor. The specified window must
|
||||
// be focused. If the window does not have focus when this function is called,
|
||||
// it fails silently.
|
||||
//
|
||||
// If the cursor is disabled (with CursorDisabled) then the cursor position is
|
||||
// unbounded and limited only by the minimum and maximum values of a double.
|
||||
func (w *Window) SetCursorPos(xpos, ypos float64) error {
|
||||
C.glfwSetCursorPos(w.data, C.double(xpos), C.double(ypos))
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateCursor creates a new custom cursor image that can be set for a window with SetCursor.
|
||||
// The cursor can be destroyed with Destroy. Any remaining cursors are destroyed by Terminate.
|
||||
//
|
||||
// The image is ideally provided in the form of *image.NRGBA.
|
||||
// The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight
|
||||
// bits per channel with the red channel first. They are arranged canonically
|
||||
// as packed sequential rows, starting from the top-left corner. If the image
|
||||
// type is not *image.NRGBA, it will be converted to it.
|
||||
//
|
||||
// The cursor hotspot is specified in pixels, relative to the upper-left corner of the cursor image.
|
||||
// Like all other coordinate systems in GLFW, the X-axis points to the right and the Y-axis points down.
|
||||
func CreateCursor(img image.Image, xhot, yhot int) (*Cursor, error) {
|
||||
glfwImg, free := imageToGLFWImage(img)
|
||||
defer free()
|
||||
|
||||
c := C.glfwCreateCursor(&glfwImg, C.int(xhot), C.int(yhot))
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Cursor{c}, nil
|
||||
}
|
||||
|
||||
// CreateStandardCursor returns a cursor with a standard shape,
|
||||
// that can be set for a window with SetCursor.
|
||||
func CreateStandardCursor(shape StandardCursor) (*Cursor, error) {
|
||||
c := C.glfwCreateStandardCursor(C.int(shape))
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Cursor{c}, nil
|
||||
}
|
||||
|
||||
// Destroy destroys a cursor previously created with CreateCursor.
|
||||
// Any remaining cursors will be destroyed by Terminate.
|
||||
func (c *Cursor) Destroy() error {
|
||||
C.glfwDestroyCursor(c.data)
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetCursor sets the cursor image to be used when the cursor is over the client area
|
||||
// of the specified window. The set cursor will only be visible when the cursor mode of the
|
||||
// window is CursorNormal.
|
||||
//
|
||||
// On some platforms, the set cursor may not be visible unless the window also has input focus.
|
||||
func (w *Window) SetCursor(c *Cursor) error {
|
||||
if c == nil {
|
||||
C.glfwSetCursor(w.data, nil)
|
||||
} else {
|
||||
C.glfwSetCursor(w.data, c.data)
|
||||
}
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// KeyCallback is the key callback.
|
||||
type KeyCallback func(w *Window, key Key, scancode int, action Action, mods ModifierKey)
|
||||
|
||||
// SetKeyCallback sets the key callback which is called when a key is pressed,
|
||||
// repeated or released.
|
||||
//
|
||||
// The key functions deal with physical keys, with layout independent key tokens
|
||||
// named after their values in the standard US keyboard layout. If you want to
|
||||
// input text, use the SetCharCallback instead.
|
||||
//
|
||||
// When a window loses focus, it will generate synthetic key release events for
|
||||
// all pressed keys. You can tell these events from user-generated events by the
|
||||
// fact that the synthetic ones are generated after the window has lost focus,
|
||||
// i.e. Focused will be false and the focus callback will have already been
|
||||
// called.
|
||||
func (w *Window) SetKeyCallback(cbfun KeyCallback) (previous KeyCallback, err error) {
|
||||
previous = w.fKeyHolder
|
||||
w.fKeyHolder = cbfun
|
||||
if cbfun == nil {
|
||||
C.glfwSetKeyCallback(w.data, nil)
|
||||
} else {
|
||||
C.glfwSetKeyCallbackCB(w.data)
|
||||
}
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return previous, nil
|
||||
}
|
||||
|
||||
// CharCallback is the character callback.
|
||||
type CharCallback func(w *Window, char rune)
|
||||
|
||||
// SetCharCallback sets the character callback which is called when a
|
||||
// Unicode character is input.
|
||||
//
|
||||
// The character callback is intended for Unicode text input. As it deals with
|
||||
// characters, it is keyboard layout dependent, whereas the
|
||||
// key callback is not. Characters do not map 1:1
|
||||
// to physical keys, as a key may produce zero, one or more characters. If you
|
||||
// want to know whether a specific physical key was pressed or released, see
|
||||
// the key callback instead.
|
||||
//
|
||||
// The character callback behaves as system text input normally does and will
|
||||
// not be called if modifier keys are held down that would prevent normal text
|
||||
// input on that platform, for example a Super (Command) key on OS X or Alt key
|
||||
// on Windows. There is a character with modifiers callback that receives these events.
|
||||
func (w *Window) SetCharCallback(cbfun CharCallback) (previous CharCallback, err error) {
|
||||
previous = w.fCharHolder
|
||||
w.fCharHolder = cbfun
|
||||
if cbfun == nil {
|
||||
C.glfwSetCharCallback(w.data, nil)
|
||||
} else {
|
||||
C.glfwSetCharCallbackCB(w.data)
|
||||
}
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return previous, nil
|
||||
}
|
||||
|
||||
// CharModsCallback is the character with modifiers callback.
|
||||
type CharModsCallback func(w *Window, char rune, mods ModifierKey)
|
||||
|
||||
// SetCharModsCallback sets the character with modifiers callback which is called when a
|
||||
// Unicode character is input regardless of what modifier keys are used.
|
||||
//
|
||||
// Deprecated: Scheduled for removal in version 4.0.
|
||||
//
|
||||
// The character with modifiers callback is intended for implementing custom
|
||||
// Unicode character input. For regular Unicode text input, see the
|
||||
// character callback. Like the character callback, the character with modifiers callback
|
||||
// deals with characters and is keyboard layout dependent. Characters do not
|
||||
// map 1:1 to physical keys, as a key may produce zero, one or more characters.
|
||||
// If you want to know whether a specific physical key was pressed or released,
|
||||
// see the key callback instead.
|
||||
func (w *Window) SetCharModsCallback(cbfun CharModsCallback) (previous CharModsCallback, err error) {
|
||||
previous = w.fCharModsHolder
|
||||
w.fCharModsHolder = cbfun
|
||||
if cbfun == nil {
|
||||
C.glfwSetCharModsCallback(w.data, nil)
|
||||
} else {
|
||||
C.glfwSetCharModsCallbackCB(w.data)
|
||||
}
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return previous, nil
|
||||
}
|
||||
|
||||
// MouseButtonCallback is the mouse button callback.
|
||||
type MouseButtonCallback func(w *Window, button MouseButton, action Action, mods ModifierKey)
|
||||
|
||||
// SetMouseButtonCallback sets the mouse button callback which is called when a
|
||||
// mouse button is pressed or released.
|
||||
//
|
||||
// When a window loses focus, it will generate synthetic mouse button release
|
||||
// events for all pressed mouse buttons. You can tell these events from
|
||||
// user-generated events by the fact that the synthetic ones are generated after
|
||||
// the window has lost focus, i.e. Focused will be false and the focus
|
||||
// callback will have already been called.
|
||||
func (w *Window) SetMouseButtonCallback(cbfun MouseButtonCallback) (previous MouseButtonCallback, err error) {
|
||||
previous = w.fMouseButtonHolder
|
||||
w.fMouseButtonHolder = cbfun
|
||||
if cbfun == nil {
|
||||
C.glfwSetMouseButtonCallback(w.data, nil)
|
||||
} else {
|
||||
C.glfwSetMouseButtonCallbackCB(w.data)
|
||||
}
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return previous, nil
|
||||
}
|
||||
|
||||
// CursorPosCallback the cursor position callback.
|
||||
type CursorPosCallback func(w *Window, xpos float64, ypos float64)
|
||||
|
||||
// SetCursorPosCallback sets the cursor position callback which is called
|
||||
// when the cursor is moved. The callback is provided with the position relative
|
||||
// to the upper-left corner of the client area of the window.
|
||||
func (w *Window) SetCursorPosCallback(cbfun CursorPosCallback) (previous CursorPosCallback, err error) {
|
||||
previous = w.fCursorPosHolder
|
||||
w.fCursorPosHolder = cbfun
|
||||
if cbfun == nil {
|
||||
C.glfwSetCursorPosCallback(w.data, nil)
|
||||
} else {
|
||||
C.glfwSetCursorPosCallbackCB(w.data)
|
||||
}
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return previous, nil
|
||||
}
|
||||
|
||||
// CursorEnterCallback is the cursor boundary crossing callback.
|
||||
type CursorEnterCallback func(w *Window, entered bool)
|
||||
|
||||
// SetCursorEnterCallback the cursor boundary crossing callback which is called
|
||||
// when the cursor enters or leaves the client area of the window.
|
||||
func (w *Window) SetCursorEnterCallback(cbfun CursorEnterCallback) (previous CursorEnterCallback, err error) {
|
||||
previous = w.fCursorEnterHolder
|
||||
w.fCursorEnterHolder = cbfun
|
||||
if cbfun == nil {
|
||||
C.glfwSetCursorEnterCallback(w.data, nil)
|
||||
} else {
|
||||
C.glfwSetCursorEnterCallbackCB(w.data)
|
||||
}
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return previous, nil
|
||||
}
|
||||
|
||||
// ScrollCallback is the scroll callback.
|
||||
type ScrollCallback func(w *Window, xoff float64, yoff float64)
|
||||
|
||||
// SetScrollCallback sets the scroll callback which is called when a scrolling
|
||||
// device is used, such as a mouse wheel or scrolling area of a touchpad.
|
||||
func (w *Window) SetScrollCallback(cbfun ScrollCallback) (previous ScrollCallback, err error) {
|
||||
previous = w.fScrollHolder
|
||||
w.fScrollHolder = cbfun
|
||||
if cbfun == nil {
|
||||
C.glfwSetScrollCallback(w.data, nil)
|
||||
} else {
|
||||
C.glfwSetScrollCallbackCB(w.data)
|
||||
}
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return previous, nil
|
||||
}
|
||||
|
||||
// DropCallback is the drop callback.
|
||||
type DropCallback func(w *Window, names []string)
|
||||
|
||||
// SetDropCallback sets the drop callback which is called when an object
|
||||
// is dropped over the window.
|
||||
func (w *Window) SetDropCallback(cbfun DropCallback) (previous DropCallback, err error) {
|
||||
previous = w.fDropHolder
|
||||
w.fDropHolder = cbfun
|
||||
if cbfun == nil {
|
||||
C.glfwSetDropCallback(w.data, nil)
|
||||
} else {
|
||||
C.glfwSetDropCallbackCB(w.data)
|
||||
}
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return previous, nil
|
||||
}
|
||||
+502
@@ -0,0 +1,502 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
package glfw
|
||||
|
||||
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("glfw: 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("glfw: 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("glfw: 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("glfw: 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("glfw: 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("glfw: 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("glfw: 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("glfw: 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 CreateStandardCursor(shape StandardCursor) (*Cursor, error) {
|
||||
if !_glfw.initialized {
|
||||
return nil, NotInitialized
|
||||
}
|
||||
|
||||
if shape != ArrowCursor &&
|
||||
shape != IBeamCursor &&
|
||||
shape != CrosshairCursor &&
|
||||
shape != HandCursor &&
|
||||
shape != HResizeCursor &&
|
||||
shape != VResizeCursor &&
|
||||
shape != ResizeNWSECursor &&
|
||||
shape != ResizeNESWCursor &&
|
||||
shape != ResizeAllCursor &&
|
||||
shape != NotAllowedCursor {
|
||||
return nil, fmt.Errorf("glfw: 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()
|
||||
}
|
||||
+595
@@ -0,0 +1,595 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || netbsd || openbsd
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(_GLFW_USE_CONFIG_H)
|
||||
#include "glfw_config.h"
|
||||
#endif
|
||||
|
||||
#if defined(GLFW_INCLUDE_GLCOREARB) || \
|
||||
defined(GLFW_INCLUDE_ES1) || \
|
||||
defined(GLFW_INCLUDE_ES2) || \
|
||||
defined(GLFW_INCLUDE_ES3) || \
|
||||
defined(GLFW_INCLUDE_ES31) || \
|
||||
defined(GLFW_INCLUDE_ES32) || \
|
||||
defined(GLFW_INCLUDE_NONE) || \
|
||||
defined(GLFW_INCLUDE_GLEXT) || \
|
||||
defined(GLFW_INCLUDE_GLU) || \
|
||||
defined(GLFW_DLL)
|
||||
#error "You must not define any header option macros when compiling GLFW"
|
||||
#endif
|
||||
|
||||
#define GLFW_INCLUDE_NONE
|
||||
#include "glfw3_unix.h"
|
||||
|
||||
#define _GLFW_INSERT_FIRST 0
|
||||
#define _GLFW_INSERT_LAST 1
|
||||
|
||||
#define _GLFW_POLL_PRESENCE 0
|
||||
#define _GLFW_POLL_AXES 1
|
||||
#define _GLFW_POLL_BUTTONS 2
|
||||
#define _GLFW_POLL_ALL (_GLFW_POLL_AXES | _GLFW_POLL_BUTTONS)
|
||||
|
||||
#define _GLFW_MESSAGE_SIZE 1024
|
||||
|
||||
typedef int GLFWbool;
|
||||
|
||||
typedef struct _GLFWerror _GLFWerror;
|
||||
typedef struct _GLFWinitconfig _GLFWinitconfig;
|
||||
typedef struct _GLFWwndconfig _GLFWwndconfig;
|
||||
typedef struct _GLFWctxconfig _GLFWctxconfig;
|
||||
typedef struct _GLFWfbconfig _GLFWfbconfig;
|
||||
typedef struct _GLFWcontext _GLFWcontext;
|
||||
typedef struct _GLFWwindow _GLFWwindow;
|
||||
typedef struct _GLFWlibrary _GLFWlibrary;
|
||||
typedef struct _GLFWmonitor _GLFWmonitor;
|
||||
typedef struct _GLFWcursor _GLFWcursor;
|
||||
typedef struct _GLFWmapelement _GLFWmapelement;
|
||||
typedef struct _GLFWtls _GLFWtls;
|
||||
typedef struct _GLFWmutex _GLFWmutex;
|
||||
|
||||
typedef void (* _GLFWmakecontextcurrentfun)(_GLFWwindow*);
|
||||
typedef void (* _GLFWswapbuffersfun)(_GLFWwindow*);
|
||||
typedef void (* _GLFWswapintervalfun)(int);
|
||||
typedef int (* _GLFWextensionsupportedfun)(const char*);
|
||||
typedef GLFWglproc (* _GLFWgetprocaddressfun)(const char*);
|
||||
typedef void (* _GLFWdestroycontextfun)(_GLFWwindow*);
|
||||
|
||||
#define GL_VERSION 0x1f02
|
||||
#define GL_NONE 0
|
||||
#define GL_COLOR_BUFFER_BIT 0x00004000
|
||||
#define GL_UNSIGNED_BYTE 0x1401
|
||||
#define GL_EXTENSIONS 0x1f03
|
||||
#define GL_NUM_EXTENSIONS 0x821d
|
||||
#define GL_CONTEXT_FLAGS 0x821e
|
||||
#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001
|
||||
#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002
|
||||
#define GL_CONTEXT_PROFILE_MASK 0x9126
|
||||
#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002
|
||||
#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001
|
||||
#define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256
|
||||
#define GL_LOSE_CONTEXT_ON_RESET_ARB 0x8252
|
||||
#define GL_NO_RESET_NOTIFICATION_ARB 0x8261
|
||||
#define GL_CONTEXT_RELEASE_BEHAVIOR 0x82fb
|
||||
#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82fc
|
||||
#define GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR 0x00000008
|
||||
|
||||
typedef int GLint;
|
||||
typedef unsigned int GLuint;
|
||||
typedef unsigned int GLenum;
|
||||
typedef unsigned int GLbitfield;
|
||||
typedef unsigned char GLubyte;
|
||||
|
||||
typedef void (APIENTRY * PFNGLCLEARPROC)(GLbitfield);
|
||||
typedef const GLubyte* (APIENTRY * PFNGLGETSTRINGPROC)(GLenum);
|
||||
typedef void (APIENTRY * PFNGLGETINTEGERVPROC)(GLenum,GLint*);
|
||||
typedef const GLubyte* (APIENTRY * PFNGLGETSTRINGIPROC)(GLenum,GLuint);
|
||||
|
||||
#if defined(_GLFW_COCOA)
|
||||
#include "cocoa_platform_darwin.h"
|
||||
#elif defined(_GLFW_X11)
|
||||
#include "x11_platform_linbsd.h"
|
||||
#else
|
||||
#error "No supported window creation API selected"
|
||||
#endif
|
||||
|
||||
// Constructs a version number string from the public header macros
|
||||
#define _GLFW_CONCAT_VERSION(m, n, r) #m "." #n "." #r
|
||||
#define _GLFW_MAKE_VERSION(m, n, r) _GLFW_CONCAT_VERSION(m, n, r)
|
||||
#define _GLFW_VERSION_NUMBER _GLFW_MAKE_VERSION(GLFW_VERSION_MAJOR, \
|
||||
GLFW_VERSION_MINOR, \
|
||||
GLFW_VERSION_REVISION)
|
||||
|
||||
// Checks for whether the library has been initialized
|
||||
#define _GLFW_REQUIRE_INIT() \
|
||||
if (!_glfw.initialized) \
|
||||
{ \
|
||||
_glfwInputError(GLFW_NOT_INITIALIZED, NULL); \
|
||||
return; \
|
||||
}
|
||||
#define _GLFW_REQUIRE_INIT_OR_RETURN(x) \
|
||||
if (!_glfw.initialized) \
|
||||
{ \
|
||||
_glfwInputError(GLFW_NOT_INITIALIZED, NULL); \
|
||||
return x; \
|
||||
}
|
||||
|
||||
// Swaps the provided pointers
|
||||
#define _GLFW_SWAP_POINTERS(x, y) \
|
||||
{ \
|
||||
void* t; \
|
||||
t = x; \
|
||||
x = y; \
|
||||
y = t; \
|
||||
}
|
||||
|
||||
// Per-thread error structure
|
||||
//
|
||||
struct _GLFWerror
|
||||
{
|
||||
_GLFWerror* next;
|
||||
int code;
|
||||
char description[_GLFW_MESSAGE_SIZE];
|
||||
};
|
||||
|
||||
// Initialization configuration
|
||||
//
|
||||
// Parameters relating to the initialization of the library
|
||||
//
|
||||
struct _GLFWinitconfig
|
||||
{
|
||||
struct {
|
||||
GLFWbool menubar;
|
||||
GLFWbool chdir;
|
||||
} ns;
|
||||
};
|
||||
|
||||
// Window configuration
|
||||
//
|
||||
// Parameters relating to the creation of the window but not directly related
|
||||
// to the framebuffer. This is used to pass window creation parameters from
|
||||
// shared code to the platform API.
|
||||
//
|
||||
struct _GLFWwndconfig
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
const char* title;
|
||||
GLFWbool resizable;
|
||||
GLFWbool visible;
|
||||
GLFWbool decorated;
|
||||
GLFWbool focused;
|
||||
GLFWbool autoIconify;
|
||||
GLFWbool floating;
|
||||
GLFWbool maximized;
|
||||
GLFWbool centerCursor;
|
||||
GLFWbool focusOnShow;
|
||||
GLFWbool mousePassthrough;
|
||||
GLFWbool scaleToMonitor;
|
||||
struct {
|
||||
GLFWbool retina;
|
||||
char frameName[256];
|
||||
} ns;
|
||||
struct {
|
||||
char className[256];
|
||||
char instanceName[256];
|
||||
} x11;
|
||||
};
|
||||
|
||||
// Context configuration
|
||||
//
|
||||
// Parameters relating to the creation of the context but not directly related
|
||||
// to the framebuffer. This is used to pass context creation parameters from
|
||||
// shared code to the platform API.
|
||||
//
|
||||
struct _GLFWctxconfig
|
||||
{
|
||||
int client;
|
||||
int source;
|
||||
int major;
|
||||
int minor;
|
||||
GLFWbool forward;
|
||||
GLFWbool debug;
|
||||
GLFWbool noerror;
|
||||
int profile;
|
||||
int robustness;
|
||||
int release;
|
||||
_GLFWwindow* share;
|
||||
struct {
|
||||
GLFWbool offline;
|
||||
} nsgl;
|
||||
};
|
||||
|
||||
// Framebuffer configuration
|
||||
//
|
||||
// This describes buffers and their sizes. It also contains
|
||||
// a platform-specific ID used to map back to the backend API object.
|
||||
//
|
||||
// It is used to pass framebuffer parameters from shared code to the platform
|
||||
// API and also to enumerate and select available framebuffer configs.
|
||||
//
|
||||
struct _GLFWfbconfig
|
||||
{
|
||||
int redBits;
|
||||
int greenBits;
|
||||
int blueBits;
|
||||
int alphaBits;
|
||||
int depthBits;
|
||||
int stencilBits;
|
||||
int accumRedBits;
|
||||
int accumGreenBits;
|
||||
int accumBlueBits;
|
||||
int accumAlphaBits;
|
||||
int auxBuffers;
|
||||
GLFWbool stereo;
|
||||
int samples;
|
||||
GLFWbool sRGB;
|
||||
GLFWbool doublebuffer;
|
||||
GLFWbool transparent;
|
||||
uintptr_t handle;
|
||||
};
|
||||
|
||||
// Context structure
|
||||
//
|
||||
struct _GLFWcontext
|
||||
{
|
||||
int client;
|
||||
int source;
|
||||
int major, minor, revision;
|
||||
GLFWbool forward, debug, noerror;
|
||||
int profile;
|
||||
int robustness;
|
||||
int release;
|
||||
|
||||
PFNGLGETSTRINGIPROC GetStringi;
|
||||
PFNGLGETINTEGERVPROC GetIntegerv;
|
||||
PFNGLGETSTRINGPROC GetString;
|
||||
|
||||
_GLFWmakecontextcurrentfun makeCurrent;
|
||||
_GLFWswapbuffersfun swapBuffers;
|
||||
_GLFWswapintervalfun swapInterval;
|
||||
_GLFWextensionsupportedfun extensionSupported;
|
||||
_GLFWgetprocaddressfun getProcAddress;
|
||||
_GLFWdestroycontextfun destroy;
|
||||
|
||||
// This is defined in the context API's context.h
|
||||
_GLFW_PLATFORM_CONTEXT_STATE;
|
||||
// This is defined in egl_context_unix.h
|
||||
_GLFW_EGL_CONTEXT_STATE;
|
||||
// This is defined in osmesa_context_unix.h
|
||||
_GLFW_OSMESA_CONTEXT_STATE;
|
||||
};
|
||||
|
||||
// Window and context structure
|
||||
//
|
||||
struct _GLFWwindow
|
||||
{
|
||||
struct _GLFWwindow* next;
|
||||
|
||||
// Window settings and state
|
||||
GLFWbool resizable;
|
||||
GLFWbool decorated;
|
||||
GLFWbool autoIconify;
|
||||
GLFWbool floating;
|
||||
GLFWbool focusOnShow;
|
||||
GLFWbool mousePassthrough;
|
||||
GLFWbool shouldClose;
|
||||
void* userPointer;
|
||||
GLFWbool doublebuffer;
|
||||
GLFWvidmode videoMode;
|
||||
_GLFWmonitor* monitor;
|
||||
_GLFWcursor* cursor;
|
||||
|
||||
int minwidth, minheight;
|
||||
int maxwidth, maxheight;
|
||||
int numer, denom;
|
||||
|
||||
GLFWbool stickyKeys;
|
||||
GLFWbool stickyMouseButtons;
|
||||
GLFWbool lockKeyMods;
|
||||
int cursorMode;
|
||||
char mouseButtons[GLFW_MOUSE_BUTTON_LAST + 1];
|
||||
char keys[GLFW_KEY_LAST + 1];
|
||||
// Virtual cursor position when cursor is disabled
|
||||
double virtualCursorPosX, virtualCursorPosY;
|
||||
GLFWbool rawMouseMotion;
|
||||
|
||||
_GLFWcontext context;
|
||||
|
||||
struct {
|
||||
GLFWwindowposfun pos;
|
||||
GLFWwindowsizefun size;
|
||||
GLFWwindowclosefun close;
|
||||
GLFWwindowrefreshfun refresh;
|
||||
GLFWwindowfocusfun focus;
|
||||
GLFWwindowiconifyfun iconify;
|
||||
GLFWwindowmaximizefun maximize;
|
||||
GLFWframebuffersizefun fbsize;
|
||||
GLFWwindowcontentscalefun scale;
|
||||
GLFWmousebuttonfun mouseButton;
|
||||
GLFWcursorposfun cursorPos;
|
||||
GLFWcursorenterfun cursorEnter;
|
||||
GLFWscrollfun scroll;
|
||||
GLFWkeyfun key;
|
||||
GLFWcharfun character;
|
||||
GLFWcharmodsfun charmods;
|
||||
GLFWdropfun drop;
|
||||
} callbacks;
|
||||
|
||||
// This is defined in the window API's platform.h
|
||||
_GLFW_PLATFORM_WINDOW_STATE;
|
||||
};
|
||||
|
||||
// Monitor structure
|
||||
//
|
||||
struct _GLFWmonitor
|
||||
{
|
||||
char name[128];
|
||||
void* userPointer;
|
||||
|
||||
// Physical dimensions in millimeters.
|
||||
int widthMM, heightMM;
|
||||
|
||||
// The window whose video mode is current on this monitor
|
||||
_GLFWwindow* window;
|
||||
|
||||
GLFWvidmode* modes;
|
||||
int modeCount;
|
||||
GLFWvidmode currentMode;
|
||||
|
||||
GLFWgammaramp originalRamp;
|
||||
GLFWgammaramp currentRamp;
|
||||
|
||||
// This is defined in the window API's platform.h
|
||||
_GLFW_PLATFORM_MONITOR_STATE;
|
||||
};
|
||||
|
||||
// Cursor structure
|
||||
//
|
||||
struct _GLFWcursor
|
||||
{
|
||||
_GLFWcursor* next;
|
||||
|
||||
// This is defined in the window API's platform.h
|
||||
_GLFW_PLATFORM_CURSOR_STATE;
|
||||
};
|
||||
|
||||
// Thread local storage structure
|
||||
//
|
||||
struct _GLFWtls
|
||||
{
|
||||
// This is defined in the platform's thread.h
|
||||
_GLFW_PLATFORM_TLS_STATE;
|
||||
};
|
||||
|
||||
// Mutex structure
|
||||
//
|
||||
struct _GLFWmutex
|
||||
{
|
||||
// This is defined in the platform's thread.h
|
||||
_GLFW_PLATFORM_MUTEX_STATE;
|
||||
};
|
||||
|
||||
// Library global data
|
||||
//
|
||||
struct _GLFWlibrary
|
||||
{
|
||||
GLFWbool initialized;
|
||||
|
||||
struct {
|
||||
_GLFWinitconfig init;
|
||||
_GLFWfbconfig framebuffer;
|
||||
_GLFWwndconfig window;
|
||||
_GLFWctxconfig context;
|
||||
int refreshRate;
|
||||
} hints;
|
||||
|
||||
_GLFWerror* errorListHead;
|
||||
_GLFWcursor* cursorListHead;
|
||||
_GLFWwindow* windowListHead;
|
||||
|
||||
_GLFWmonitor** monitors;
|
||||
int monitorCount;
|
||||
|
||||
_GLFWtls errorSlot;
|
||||
_GLFWtls contextSlot;
|
||||
_GLFWmutex errorLock;
|
||||
|
||||
struct {
|
||||
uint64_t offset;
|
||||
// This is defined in the platform's time.h
|
||||
_GLFW_PLATFORM_LIBRARY_TIMER_STATE;
|
||||
} timer;
|
||||
|
||||
struct {
|
||||
GLFWmonitorfun monitor;
|
||||
} callbacks;
|
||||
|
||||
// This is defined in the window API's platform.h
|
||||
_GLFW_PLATFORM_LIBRARY_WINDOW_STATE;
|
||||
// This is defined in the context API's context.h
|
||||
_GLFW_PLATFORM_LIBRARY_CONTEXT_STATE;
|
||||
// This is defined in egl_context_unix.h
|
||||
_GLFW_EGL_LIBRARY_CONTEXT_STATE;
|
||||
// This is defined in osmesa_context_unix.h
|
||||
_GLFW_OSMESA_LIBRARY_CONTEXT_STATE;
|
||||
};
|
||||
|
||||
// Global state shared between compilation units of GLFW
|
||||
//
|
||||
extern _GLFWlibrary _glfw;
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW platform API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int _glfwPlatformInit(void);
|
||||
void _glfwPlatformTerminate(void);
|
||||
const char* _glfwPlatformGetVersionString(void);
|
||||
|
||||
void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos);
|
||||
void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos);
|
||||
void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode);
|
||||
void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled);
|
||||
GLFWbool _glfwPlatformRawMouseMotionSupported(void);
|
||||
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
|
||||
const GLFWimage* image, int xhot, int yhot);
|
||||
int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape);
|
||||
void _glfwPlatformDestroyCursor(_GLFWcursor* cursor);
|
||||
void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor);
|
||||
|
||||
const char* _glfwPlatformGetScancodeName(int scancode);
|
||||
int _glfwPlatformGetKeyScancode(int key);
|
||||
|
||||
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor);
|
||||
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos);
|
||||
void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
|
||||
float* xscale, float* yscale);
|
||||
void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int *width, int *height);
|
||||
GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count);
|
||||
void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode);
|
||||
GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp);
|
||||
void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp);
|
||||
|
||||
void _glfwPlatformSetClipboardString(const char* string);
|
||||
const char* _glfwPlatformGetClipboardString(void);
|
||||
|
||||
uint64_t _glfwPlatformGetTimerValue(void);
|
||||
uint64_t _glfwPlatformGetTimerFrequency(void);
|
||||
|
||||
int _glfwPlatformCreateWindow(_GLFWwindow* window,
|
||||
const _GLFWwndconfig* wndconfig,
|
||||
const _GLFWctxconfig* ctxconfig,
|
||||
const _GLFWfbconfig* fbconfig);
|
||||
void _glfwPlatformDestroyWindow(_GLFWwindow* window);
|
||||
void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title);
|
||||
void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
|
||||
int count, const GLFWimage* images);
|
||||
void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos);
|
||||
void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos);
|
||||
void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height);
|
||||
void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height);
|
||||
void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
|
||||
int minwidth, int minheight,
|
||||
int maxwidth, int maxheight);
|
||||
void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom);
|
||||
void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height);
|
||||
void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
|
||||
int* left, int* top,
|
||||
int* right, int* bottom);
|
||||
void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
|
||||
float* xscale, float* yscale);
|
||||
void _glfwPlatformIconifyWindow(_GLFWwindow* window);
|
||||
void _glfwPlatformRestoreWindow(_GLFWwindow* window);
|
||||
void _glfwPlatformMaximizeWindow(_GLFWwindow* window);
|
||||
void _glfwPlatformShowWindow(_GLFWwindow* window);
|
||||
void _glfwPlatformHideWindow(_GLFWwindow* window);
|
||||
void _glfwPlatformRequestWindowAttention(_GLFWwindow* window);
|
||||
void _glfwPlatformFocusWindow(_GLFWwindow* window);
|
||||
void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor,
|
||||
int xpos, int ypos, int width, int height,
|
||||
int refreshRate);
|
||||
int _glfwPlatformWindowFocused(_GLFWwindow* window);
|
||||
int _glfwPlatformWindowIconified(_GLFWwindow* window);
|
||||
int _glfwPlatformWindowVisible(_GLFWwindow* window);
|
||||
int _glfwPlatformWindowMaximized(_GLFWwindow* window);
|
||||
int _glfwPlatformWindowHovered(_GLFWwindow* window);
|
||||
int _glfwPlatformFramebufferTransparent(_GLFWwindow* window);
|
||||
float _glfwPlatformGetWindowOpacity(_GLFWwindow* window);
|
||||
void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled);
|
||||
void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled);
|
||||
void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled);
|
||||
void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity);
|
||||
void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, GLFWbool enabled);
|
||||
|
||||
void _glfwPlatformPollEvents(void);
|
||||
void _glfwPlatformWaitEvents(void);
|
||||
void _glfwPlatformWaitEventsTimeout(double timeout);
|
||||
void _glfwPlatformPostEmptyEvent(void);
|
||||
|
||||
GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls);
|
||||
void _glfwPlatformDestroyTls(_GLFWtls* tls);
|
||||
void* _glfwPlatformGetTls(_GLFWtls* tls);
|
||||
void _glfwPlatformSetTls(_GLFWtls* tls, void* value);
|
||||
|
||||
GLFWbool _glfwPlatformCreateMutex(_GLFWmutex* mutex);
|
||||
void _glfwPlatformDestroyMutex(_GLFWmutex* mutex);
|
||||
void _glfwPlatformLockMutex(_GLFWmutex* mutex);
|
||||
void _glfwPlatformUnlockMutex(_GLFWmutex* mutex);
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW event API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused);
|
||||
void _glfwInputWindowPos(_GLFWwindow* window, int xpos, int ypos);
|
||||
void _glfwInputWindowSize(_GLFWwindow* window, int width, int height);
|
||||
void _glfwInputFramebufferSize(_GLFWwindow* window, int width, int height);
|
||||
void _glfwInputWindowContentScale(_GLFWwindow* window,
|
||||
float xscale, float yscale);
|
||||
void _glfwInputWindowIconify(_GLFWwindow* window, GLFWbool iconified);
|
||||
void _glfwInputWindowMaximize(_GLFWwindow* window, GLFWbool maximized);
|
||||
void _glfwInputWindowDamage(_GLFWwindow* window);
|
||||
void _glfwInputWindowCloseRequest(_GLFWwindow* window);
|
||||
void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor);
|
||||
|
||||
void _glfwInputKey(_GLFWwindow* window,
|
||||
int key, int scancode, int action, int mods);
|
||||
void _glfwInputChar(_GLFWwindow* window,
|
||||
uint32_t codepoint, int mods, GLFWbool plain);
|
||||
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset);
|
||||
void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods);
|
||||
void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos);
|
||||
void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool entered);
|
||||
void _glfwInputDrop(_GLFWwindow* window, int count, const char** names);
|
||||
|
||||
void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement);
|
||||
void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window);
|
||||
|
||||
#if defined(__GNUC__)
|
||||
void _glfwInputError(int code, const char* format, ...)
|
||||
__attribute__((format(printf, 2, 3)));
|
||||
#else
|
||||
void _glfwInputError(int code, const char* format, ...);
|
||||
#endif
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW internal API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions);
|
||||
const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired,
|
||||
const _GLFWfbconfig* alternatives,
|
||||
unsigned int count);
|
||||
GLFWbool _glfwRefreshContextAttribs(_GLFWwindow* window,
|
||||
const _GLFWctxconfig* ctxconfig);
|
||||
GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig);
|
||||
|
||||
const GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor,
|
||||
const GLFWvidmode* desired);
|
||||
int _glfwCompareVideoModes(const GLFWvidmode* first, const GLFWvidmode* second);
|
||||
_GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM);
|
||||
void _glfwFreeMonitor(_GLFWmonitor* monitor);
|
||||
void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size);
|
||||
void _glfwFreeGammaArrays(GLFWgammaramp* ramp);
|
||||
void _glfwSplitBPP(int bpp, int* red, int* green, int* blue);
|
||||
|
||||
void _glfwCenterCursorInContentArea(_GLFWwindow* window);
|
||||
|
||||
size_t _glfwEncodeUTF8(char* s, uint32_t codepoint);
|
||||
char** _glfwParseUriList(char* text, int* count);
|
||||
|
||||
char* _glfw_strdup(const char* source);
|
||||
int _glfw_min(int a, int b);
|
||||
int _glfw_max(int a, int b);
|
||||
float _glfw_fminf(float a, float b);
|
||||
float _glfw_fmaxf(float a, float b);
|
||||
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
package glfw
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
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
|
||||
mousePassthrough 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
|
||||
|
||||
platform platformContextState
|
||||
}
|
||||
|
||||
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
|
||||
mousePassthrough 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
|
||||
}
|
||||
|
||||
platform platformWindowState
|
||||
}
|
||||
|
||||
type Monitor struct {
|
||||
name string
|
||||
|
||||
window *Window
|
||||
|
||||
modes []*VidMode
|
||||
|
||||
platform platformMonitorState
|
||||
}
|
||||
|
||||
type Cursor struct {
|
||||
platform platformCursorState
|
||||
}
|
||||
|
||||
type tls struct {
|
||||
platform platformTLSState
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
platformWindow platformLibraryWindowState
|
||||
platformContext platformLibraryContextState
|
||||
}
|
||||
|
||||
func boolToInt(x bool) int {
|
||||
if x {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func intToBool(x int) bool {
|
||||
return x != 0
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
// Copyright 2013 The Ebitengine Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Code generated by genkeys.go using 'go generate'. DO NOT EDIT.
|
||||
|
||||
//go:build darwin || freebsd || linux || netbsd || openbsd || windows
|
||||
|
||||
package glfw
|
||||
|
||||
const (
|
||||
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)
|
||||
KeyA = Key(65)
|
||||
KeyApostrophe = Key(39)
|
||||
KeyB = Key(66)
|
||||
KeyBackslash = Key(92)
|
||||
KeyBackspace = Key(259)
|
||||
KeyC = Key(67)
|
||||
KeyCapsLock = Key(280)
|
||||
KeyComma = Key(44)
|
||||
KeyD = Key(68)
|
||||
KeyDelete = Key(261)
|
||||
KeyDown = Key(264)
|
||||
KeyE = Key(69)
|
||||
KeyEnd = Key(269)
|
||||
KeyEnter = Key(257)
|
||||
KeyEqual = Key(61)
|
||||
KeyEscape = Key(256)
|
||||
KeyF = Key(70)
|
||||
KeyF1 = Key(290)
|
||||
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)
|
||||
KeyF2 = Key(291)
|
||||
KeyF20 = Key(309)
|
||||
KeyF21 = Key(310)
|
||||
KeyF22 = Key(311)
|
||||
KeyF23 = Key(312)
|
||||
KeyF24 = Key(313)
|
||||
KeyF3 = Key(292)
|
||||
KeyF4 = Key(293)
|
||||
KeyF5 = Key(294)
|
||||
KeyF6 = Key(295)
|
||||
KeyF7 = Key(296)
|
||||
KeyF8 = Key(297)
|
||||
KeyF9 = Key(298)
|
||||
KeyG = Key(71)
|
||||
KeyGraveAccent = Key(96)
|
||||
KeyH = Key(72)
|
||||
KeyHome = Key(268)
|
||||
KeyI = Key(73)
|
||||
KeyInsert = Key(260)
|
||||
KeyJ = Key(74)
|
||||
KeyK = Key(75)
|
||||
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)
|
||||
KeyKPAdd = Key(334)
|
||||
KeyKPDecimal = Key(330)
|
||||
KeyKPDivide = Key(331)
|
||||
KeyKPEnter = Key(335)
|
||||
KeyKPEqual = Key(336)
|
||||
KeyKPMultiply = Key(332)
|
||||
KeyKPSubtract = Key(333)
|
||||
KeyL = Key(76)
|
||||
KeyLast = Key(348)
|
||||
KeyLeft = Key(263)
|
||||
KeyLeftAlt = Key(342)
|
||||
KeyLeftBracket = Key(91)
|
||||
KeyLeftControl = Key(341)
|
||||
KeyLeftShift = Key(340)
|
||||
KeyLeftSuper = Key(343)
|
||||
KeyM = Key(77)
|
||||
KeyMenu = Key(348)
|
||||
KeyMinus = Key(45)
|
||||
KeyN = Key(78)
|
||||
KeyNumLock = Key(282)
|
||||
KeyO = Key(79)
|
||||
KeyP = Key(80)
|
||||
KeyPageDown = Key(267)
|
||||
KeyPageUp = Key(266)
|
||||
KeyPause = Key(284)
|
||||
KeyPeriod = Key(46)
|
||||
KeyPrintScreen = Key(283)
|
||||
KeyQ = Key(81)
|
||||
KeyR = Key(82)
|
||||
KeyRight = Key(262)
|
||||
KeyRightAlt = Key(346)
|
||||
KeyRightBracket = Key(93)
|
||||
KeyRightControl = Key(345)
|
||||
KeyRightShift = Key(344)
|
||||
KeyRightSuper = Key(347)
|
||||
KeyS = Key(83)
|
||||
KeyScrollLock = Key(281)
|
||||
KeySemicolon = Key(59)
|
||||
KeySlash = Key(47)
|
||||
KeySpace = Key(32)
|
||||
KeyT = Key(84)
|
||||
KeyTab = Key(258)
|
||||
KeyU = Key(85)
|
||||
KeyUnknown = Key(-1)
|
||||
KeyUp = Key(265)
|
||||
KeyV = Key(86)
|
||||
KeyW = Key(87)
|
||||
KeyWorld1 = Key(161)
|
||||
KeyWorld2 = Key(162)
|
||||
KeyX = Key(88)
|
||||
KeyY = Key(89)
|
||||
KeyZ = Key(90)
|
||||
)
|
||||
+520
@@ -0,0 +1,520 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || netbsd || openbsd
|
||||
|
||||
#include "internal_unix.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
|
||||
// Lexically compare video modes, used by qsort
|
||||
//
|
||||
static int compareVideoModes(const void* fp, const void* sp)
|
||||
{
|
||||
const GLFWvidmode* fm = fp;
|
||||
const GLFWvidmode* sm = sp;
|
||||
const int fbpp = fm->redBits + fm->greenBits + fm->blueBits;
|
||||
const int sbpp = sm->redBits + sm->greenBits + sm->blueBits;
|
||||
const int farea = fm->width * fm->height;
|
||||
const int sarea = sm->width * sm->height;
|
||||
|
||||
// First sort on color bits per pixel
|
||||
if (fbpp != sbpp)
|
||||
return fbpp - sbpp;
|
||||
|
||||
// Then sort on screen area
|
||||
if (farea != sarea)
|
||||
return farea - sarea;
|
||||
|
||||
// Then sort on width
|
||||
if (fm->width != sm->width)
|
||||
return fm->width - sm->width;
|
||||
|
||||
// Lastly sort on refresh rate
|
||||
return fm->refreshRate - sm->refreshRate;
|
||||
}
|
||||
|
||||
// Retrieves the available modes for the specified monitor
|
||||
//
|
||||
static GLFWbool refreshVideoModes(_GLFWmonitor* monitor)
|
||||
{
|
||||
int modeCount;
|
||||
GLFWvidmode* modes;
|
||||
|
||||
if (monitor->modes)
|
||||
return GLFW_TRUE;
|
||||
|
||||
modes = _glfwPlatformGetVideoModes(monitor, &modeCount);
|
||||
if (!modes)
|
||||
return GLFW_FALSE;
|
||||
|
||||
qsort(modes, modeCount, sizeof(GLFWvidmode), compareVideoModes);
|
||||
|
||||
free(monitor->modes);
|
||||
monitor->modes = modes;
|
||||
monitor->modeCount = modeCount;
|
||||
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW event API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Notifies shared code of a monitor connection or disconnection
|
||||
//
|
||||
void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement)
|
||||
{
|
||||
if (action == GLFW_CONNECTED)
|
||||
{
|
||||
_glfw.monitorCount++;
|
||||
_glfw.monitors =
|
||||
realloc(_glfw.monitors, sizeof(_GLFWmonitor*) * _glfw.monitorCount);
|
||||
|
||||
if (placement == _GLFW_INSERT_FIRST)
|
||||
{
|
||||
memmove(_glfw.monitors + 1,
|
||||
_glfw.monitors,
|
||||
((size_t) _glfw.monitorCount - 1) * sizeof(_GLFWmonitor*));
|
||||
_glfw.monitors[0] = monitor;
|
||||
}
|
||||
else
|
||||
_glfw.monitors[_glfw.monitorCount - 1] = monitor;
|
||||
}
|
||||
else if (action == GLFW_DISCONNECTED)
|
||||
{
|
||||
int i;
|
||||
_GLFWwindow* window;
|
||||
|
||||
for (window = _glfw.windowListHead; window; window = window->next)
|
||||
{
|
||||
if (window->monitor == monitor)
|
||||
{
|
||||
int width, height, xoff, yoff;
|
||||
_glfwPlatformGetWindowSize(window, &width, &height);
|
||||
_glfwPlatformSetWindowMonitor(window, NULL, 0, 0, width, height, 0);
|
||||
_glfwPlatformGetWindowFrameSize(window, &xoff, &yoff, NULL, NULL);
|
||||
_glfwPlatformSetWindowPos(window, xoff, yoff);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < _glfw.monitorCount; i++)
|
||||
{
|
||||
if (_glfw.monitors[i] == monitor)
|
||||
{
|
||||
_glfw.monitorCount--;
|
||||
memmove(_glfw.monitors + i,
|
||||
_glfw.monitors + i + 1,
|
||||
((size_t) _glfw.monitorCount - i) * sizeof(_GLFWmonitor*));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_glfw.callbacks.monitor)
|
||||
_glfw.callbacks.monitor((GLFWmonitor*) monitor, action);
|
||||
|
||||
if (action == GLFW_DISCONNECTED)
|
||||
_glfwFreeMonitor(monitor);
|
||||
}
|
||||
|
||||
// Notifies shared code that a full screen window has acquired or released
|
||||
// a monitor
|
||||
//
|
||||
void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window)
|
||||
{
|
||||
monitor->window = window;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW internal API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Allocates and returns a monitor object with the specified name and dimensions
|
||||
//
|
||||
_GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM)
|
||||
{
|
||||
_GLFWmonitor* monitor = calloc(1, sizeof(_GLFWmonitor));
|
||||
monitor->widthMM = widthMM;
|
||||
monitor->heightMM = heightMM;
|
||||
|
||||
strncpy(monitor->name, name, sizeof(monitor->name) - 1);
|
||||
|
||||
return monitor;
|
||||
}
|
||||
|
||||
// Frees a monitor object and any data associated with it
|
||||
//
|
||||
void _glfwFreeMonitor(_GLFWmonitor* monitor)
|
||||
{
|
||||
if (monitor == NULL)
|
||||
return;
|
||||
|
||||
_glfwPlatformFreeMonitor(monitor);
|
||||
|
||||
_glfwFreeGammaArrays(&monitor->originalRamp);
|
||||
_glfwFreeGammaArrays(&monitor->currentRamp);
|
||||
|
||||
free(monitor->modes);
|
||||
free(monitor);
|
||||
}
|
||||
|
||||
// Allocates red, green and blue value arrays of the specified size
|
||||
//
|
||||
void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size)
|
||||
{
|
||||
ramp->red = calloc(size, sizeof(unsigned short));
|
||||
ramp->green = calloc(size, sizeof(unsigned short));
|
||||
ramp->blue = calloc(size, sizeof(unsigned short));
|
||||
ramp->size = size;
|
||||
}
|
||||
|
||||
// Frees the red, green and blue value arrays and clears the struct
|
||||
//
|
||||
void _glfwFreeGammaArrays(GLFWgammaramp* ramp)
|
||||
{
|
||||
free(ramp->red);
|
||||
free(ramp->green);
|
||||
free(ramp->blue);
|
||||
|
||||
memset(ramp, 0, sizeof(GLFWgammaramp));
|
||||
}
|
||||
|
||||
// Chooses the video mode most closely matching the desired one
|
||||
//
|
||||
const GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor,
|
||||
const GLFWvidmode* desired)
|
||||
{
|
||||
int i;
|
||||
unsigned int sizeDiff, leastSizeDiff = UINT_MAX;
|
||||
unsigned int rateDiff, leastRateDiff = UINT_MAX;
|
||||
unsigned int colorDiff, leastColorDiff = UINT_MAX;
|
||||
const GLFWvidmode* current;
|
||||
const GLFWvidmode* closest = NULL;
|
||||
|
||||
if (!refreshVideoModes(monitor))
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < monitor->modeCount; i++)
|
||||
{
|
||||
current = monitor->modes + i;
|
||||
|
||||
colorDiff = 0;
|
||||
|
||||
if (desired->redBits != GLFW_DONT_CARE)
|
||||
colorDiff += abs(current->redBits - desired->redBits);
|
||||
if (desired->greenBits != GLFW_DONT_CARE)
|
||||
colorDiff += abs(current->greenBits - desired->greenBits);
|
||||
if (desired->blueBits != GLFW_DONT_CARE)
|
||||
colorDiff += abs(current->blueBits - desired->blueBits);
|
||||
|
||||
sizeDiff = abs((current->width - desired->width) *
|
||||
(current->width - desired->width) +
|
||||
(current->height - desired->height) *
|
||||
(current->height - desired->height));
|
||||
|
||||
if (desired->refreshRate != GLFW_DONT_CARE)
|
||||
rateDiff = abs(current->refreshRate - desired->refreshRate);
|
||||
else
|
||||
rateDiff = UINT_MAX - current->refreshRate;
|
||||
|
||||
if ((colorDiff < leastColorDiff) ||
|
||||
(colorDiff == leastColorDiff && sizeDiff < leastSizeDiff) ||
|
||||
(colorDiff == leastColorDiff && sizeDiff == leastSizeDiff && rateDiff < leastRateDiff))
|
||||
{
|
||||
closest = current;
|
||||
leastSizeDiff = sizeDiff;
|
||||
leastRateDiff = rateDiff;
|
||||
leastColorDiff = colorDiff;
|
||||
}
|
||||
}
|
||||
|
||||
return closest;
|
||||
}
|
||||
|
||||
// Performs lexical comparison between two @ref GLFWvidmode structures
|
||||
//
|
||||
int _glfwCompareVideoModes(const GLFWvidmode* fm, const GLFWvidmode* sm)
|
||||
{
|
||||
return compareVideoModes(fm, sm);
|
||||
}
|
||||
|
||||
// Splits a color depth into red, green and blue bit depths
|
||||
//
|
||||
void _glfwSplitBPP(int bpp, int* red, int* green, int* blue)
|
||||
{
|
||||
int delta;
|
||||
|
||||
// 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 = *green = *blue = bpp / 3;
|
||||
delta = bpp - (*red * 3);
|
||||
if (delta >= 1)
|
||||
*green = *green + 1;
|
||||
|
||||
if (delta == 2)
|
||||
*red = *red + 1;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW public API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GLFWAPI GLFWmonitor** glfwGetMonitors(int* count)
|
||||
{
|
||||
assert(count != NULL);
|
||||
|
||||
*count = 0;
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
|
||||
*count = _glfw.monitorCount;
|
||||
return (GLFWmonitor**) _glfw.monitors;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
|
||||
if (!_glfw.monitorCount)
|
||||
return NULL;
|
||||
|
||||
return (GLFWmonitor*) _glfw.monitors[0];
|
||||
}
|
||||
|
||||
GLFWAPI void glfwGetMonitorPos(GLFWmonitor* handle, int* xpos, int* ypos)
|
||||
{
|
||||
_GLFWmonitor* monitor = (_GLFWmonitor*) handle;
|
||||
assert(monitor != NULL);
|
||||
|
||||
if (xpos)
|
||||
*xpos = 0;
|
||||
if (ypos)
|
||||
*ypos = 0;
|
||||
|
||||
_GLFW_REQUIRE_INIT();
|
||||
|
||||
_glfwPlatformGetMonitorPos(monitor, xpos, ypos);
|
||||
}
|
||||
|
||||
GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* handle,
|
||||
int* xpos, int* ypos,
|
||||
int* width, int* height)
|
||||
{
|
||||
_GLFWmonitor* monitor = (_GLFWmonitor*) handle;
|
||||
assert(monitor != NULL);
|
||||
|
||||
if (xpos)
|
||||
*xpos = 0;
|
||||
if (ypos)
|
||||
*ypos = 0;
|
||||
if (width)
|
||||
*width = 0;
|
||||
if (height)
|
||||
*height = 0;
|
||||
|
||||
_GLFW_REQUIRE_INIT();
|
||||
|
||||
_glfwPlatformGetMonitorWorkarea(monitor, xpos, ypos, width, height);
|
||||
}
|
||||
|
||||
GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* handle, int* widthMM, int* heightMM)
|
||||
{
|
||||
_GLFWmonitor* monitor = (_GLFWmonitor*) handle;
|
||||
assert(monitor != NULL);
|
||||
|
||||
if (widthMM)
|
||||
*widthMM = 0;
|
||||
if (heightMM)
|
||||
*heightMM = 0;
|
||||
|
||||
_GLFW_REQUIRE_INIT();
|
||||
|
||||
if (widthMM)
|
||||
*widthMM = monitor->widthMM;
|
||||
if (heightMM)
|
||||
*heightMM = monitor->heightMM;
|
||||
}
|
||||
|
||||
GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* handle,
|
||||
float* xscale, float* yscale)
|
||||
{
|
||||
_GLFWmonitor* monitor = (_GLFWmonitor*) handle;
|
||||
assert(monitor != NULL);
|
||||
|
||||
if (xscale)
|
||||
*xscale = 0.f;
|
||||
if (yscale)
|
||||
*yscale = 0.f;
|
||||
|
||||
_GLFW_REQUIRE_INIT();
|
||||
_glfwPlatformGetMonitorContentScale(monitor, xscale, yscale);
|
||||
}
|
||||
|
||||
GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* handle)
|
||||
{
|
||||
_GLFWmonitor* monitor = (_GLFWmonitor*) handle;
|
||||
assert(monitor != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
return monitor->name;
|
||||
}
|
||||
|
||||
GLFWAPI void glfwSetMonitorUserPointer(GLFWmonitor* handle, void* pointer)
|
||||
{
|
||||
_GLFWmonitor* monitor = (_GLFWmonitor*) handle;
|
||||
assert(monitor != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT();
|
||||
monitor->userPointer = pointer;
|
||||
}
|
||||
|
||||
GLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* handle)
|
||||
{
|
||||
_GLFWmonitor* monitor = (_GLFWmonitor*) handle;
|
||||
assert(monitor != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
return monitor->userPointer;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun cbfun)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
_GLFW_SWAP_POINTERS(_glfw.callbacks.monitor, cbfun);
|
||||
return cbfun;
|
||||
}
|
||||
|
||||
GLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* handle, int* count)
|
||||
{
|
||||
_GLFWmonitor* monitor = (_GLFWmonitor*) handle;
|
||||
assert(monitor != NULL);
|
||||
assert(count != NULL);
|
||||
|
||||
*count = 0;
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
|
||||
if (!refreshVideoModes(monitor))
|
||||
return NULL;
|
||||
|
||||
*count = monitor->modeCount;
|
||||
return monitor->modes;
|
||||
}
|
||||
|
||||
GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* handle)
|
||||
{
|
||||
_GLFWmonitor* monitor = (_GLFWmonitor*) handle;
|
||||
assert(monitor != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
|
||||
_glfwPlatformGetVideoMode(monitor, &monitor->currentMode);
|
||||
return &monitor->currentMode;
|
||||
}
|
||||
|
||||
GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned short* values;
|
||||
GLFWgammaramp ramp;
|
||||
const GLFWgammaramp* original;
|
||||
assert(handle != NULL);
|
||||
assert(gamma > 0.f);
|
||||
assert(gamma <= FLT_MAX);
|
||||
|
||||
_GLFW_REQUIRE_INIT();
|
||||
|
||||
if (gamma != gamma || gamma <= 0.f || gamma > FLT_MAX)
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_VALUE, "Invalid gamma value %f", gamma);
|
||||
return;
|
||||
}
|
||||
|
||||
original = glfwGetGammaRamp(handle);
|
||||
if (!original)
|
||||
return;
|
||||
|
||||
values = calloc(original->size, sizeof(unsigned short));
|
||||
|
||||
for (i = 0; i < original->size; i++)
|
||||
{
|
||||
float value;
|
||||
|
||||
// Calculate intensity
|
||||
value = i / (float) (original->size - 1);
|
||||
// Apply gamma curve
|
||||
value = powf(value, 1.f / gamma) * 65535.f + 0.5f;
|
||||
// Clamp to value range
|
||||
value = _glfw_fminf(value, 65535.f);
|
||||
|
||||
values[i] = (unsigned short) value;
|
||||
}
|
||||
|
||||
ramp.red = values;
|
||||
ramp.green = values;
|
||||
ramp.blue = values;
|
||||
ramp.size = original->size;
|
||||
|
||||
glfwSetGammaRamp(handle, &ramp);
|
||||
free(values);
|
||||
}
|
||||
|
||||
GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* handle)
|
||||
{
|
||||
_GLFWmonitor* monitor = (_GLFWmonitor*) handle;
|
||||
assert(monitor != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
|
||||
_glfwFreeGammaArrays(&monitor->currentRamp);
|
||||
if (!_glfwPlatformGetGammaRamp(monitor, &monitor->currentRamp))
|
||||
return NULL;
|
||||
|
||||
return &monitor->currentRamp;
|
||||
}
|
||||
|
||||
GLFWAPI void glfwSetGammaRamp(GLFWmonitor* handle, const GLFWgammaramp* ramp)
|
||||
{
|
||||
_GLFWmonitor* monitor = (_GLFWmonitor*) handle;
|
||||
assert(monitor != NULL);
|
||||
assert(ramp != NULL);
|
||||
assert(ramp->size > 0);
|
||||
assert(ramp->red != NULL);
|
||||
assert(ramp->green != NULL);
|
||||
assert(ramp->blue != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT();
|
||||
|
||||
if (ramp->size <= 0)
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_VALUE,
|
||||
"Invalid gamma ramp size %i",
|
||||
ramp->size);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!monitor->originalRamp.size)
|
||||
{
|
||||
if (!_glfwPlatformGetGammaRamp(monitor, &monitor->originalRamp))
|
||||
return;
|
||||
}
|
||||
|
||||
_glfwPlatformSetGammaRamp(monitor, ramp);
|
||||
}
|
||||
|
||||
+292
@@ -0,0 +1,292 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2012 The glfw3-go Authors
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || netbsd || openbsd
|
||||
|
||||
package glfw
|
||||
|
||||
// #define GLFW_INCLUDE_NONE
|
||||
// #include "glfw3_unix.h"
|
||||
//
|
||||
// void goMonitorCB(void* monitor, int event);
|
||||
//
|
||||
// static GLFWmonitor *GetMonitorAtIndex(GLFWmonitor **monitors, int index) {
|
||||
// return monitors[index];
|
||||
// }
|
||||
//
|
||||
// static GLFWvidmode GetVidmodeAtIndex(GLFWvidmode *vidmodes, int index) {
|
||||
// return vidmodes[index];
|
||||
// }
|
||||
//
|
||||
// static void glfwSetMonitorCallbackCB() {
|
||||
// glfwSetMonitorCallback((GLFWmonitorfun)goMonitorCB);
|
||||
// }
|
||||
//
|
||||
// static unsigned int GetGammaAtIndex(unsigned short *color, int i) {
|
||||
// return color[i];
|
||||
// }
|
||||
//
|
||||
// static void SetGammaAtIndex(unsigned short *color, int i, unsigned short value) {
|
||||
// color[i] = value;
|
||||
// }
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Monitor represents a monitor.
|
||||
type Monitor struct {
|
||||
data *C.GLFWmonitor
|
||||
}
|
||||
|
||||
// GammaRamp describes the gamma ramp for a monitor.
|
||||
type GammaRamp struct {
|
||||
Red []uint16 // A slice of value describing the response of the red channel.
|
||||
Green []uint16 // A slice of value describing the response of the green channel.
|
||||
Blue []uint16 // A slice of value describing the response of the blue channel.
|
||||
}
|
||||
|
||||
var fMonitorHolder func(monitor *Monitor, event PeripheralEvent)
|
||||
|
||||
//export goMonitorCB
|
||||
func goMonitorCB(monitor unsafe.Pointer, event C.int) {
|
||||
fMonitorHolder(&Monitor{(*C.GLFWmonitor)(monitor)}, PeripheralEvent(event))
|
||||
}
|
||||
|
||||
// GetMonitors returns a slice of handles for all currently connected monitors.
|
||||
func GetMonitors() ([]*Monitor, error) {
|
||||
var length int
|
||||
|
||||
mC := C.glfwGetMonitors((*C.int)(unsafe.Pointer(&length)))
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if mC == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
m := make([]*Monitor, length)
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
m[i] = &Monitor{C.GetMonitorAtIndex(mC, C.int(i))}
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// GetPrimaryMonitor returns the primary monitor. This is usually the monitor
|
||||
// where elements like the Windows task bar or the OS X menu bar is located.
|
||||
func GetPrimaryMonitor() (*Monitor, error) {
|
||||
m := C.glfwGetPrimaryMonitor()
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return &Monitor{m}, nil
|
||||
}
|
||||
|
||||
// GetPos returns the position, in screen coordinates, of the upper-left
|
||||
// corner of the monitor.
|
||||
func (m *Monitor) GetPos() (x, y int, err error) {
|
||||
var xpos, ypos C.int
|
||||
C.glfwGetMonitorPos(m.data, &xpos, &ypos)
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return int(xpos), int(ypos), nil
|
||||
}
|
||||
|
||||
// GetWorkarea returns the position, in screen coordinates, of the upper-left
|
||||
// corner of the work area of the specified monitor along with the work area
|
||||
// size in screen coordinates. The work area is defined as the area of the
|
||||
// monitor not occluded by the operating system task bar where present. If no
|
||||
// task bar exists then the work area is the monitor resolution in screen
|
||||
// coordinates.
|
||||
//
|
||||
// This function must only be called from the main thread.
|
||||
func (m *Monitor) GetWorkarea() (x, y, width, height int) {
|
||||
var cX, cY, cWidth, cHeight C.int
|
||||
C.glfwGetMonitorWorkarea(m.data, &cX, &cY, &cWidth, &cHeight)
|
||||
x, y, width, height = int(cX), int(cY), int(cWidth), int(cHeight)
|
||||
return
|
||||
}
|
||||
|
||||
// GetContentScale function retrieves the content scale for the specified monitor.
|
||||
// The content scale is the ratio between the current DPI and the platform's
|
||||
// default DPI. If you scale all pixel dimensions by this scale then your content
|
||||
// should appear at an appropriate size. This is especially important for text
|
||||
// and any UI elements.
|
||||
//
|
||||
// This function must only be called from the main thread.
|
||||
func (m *Monitor) GetContentScale() (float32, float32, error) {
|
||||
var x, y C.float
|
||||
C.glfwGetMonitorContentScale(m.data, &x, &y)
|
||||
return float32(x), float32(y), nil
|
||||
}
|
||||
|
||||
// SetUserPointer sets the user-defined pointer of the monitor. The current value
|
||||
// is retained until the monitor is disconnected. The initial value is nil.
|
||||
//
|
||||
// This function may be called from the monitor callback, even for a monitor
|
||||
// that is being disconnected.
|
||||
//
|
||||
// This function may be called from any thread. Access is not synchronized.
|
||||
func (m *Monitor) SetUserPointer(pointer unsafe.Pointer) {
|
||||
C.glfwSetMonitorUserPointer(m.data, pointer)
|
||||
}
|
||||
|
||||
// GetUserPointer returns the current value of the user-defined pointer of the
|
||||
// monitor. The initial value is nil.
|
||||
//
|
||||
// This function may be called from the monitor callback, even for a monitor
|
||||
// that is being disconnected.
|
||||
//
|
||||
// This function may be called from any thread. Access is not synchronized.
|
||||
func (m *Monitor) GetUserPointer() unsafe.Pointer {
|
||||
return C.glfwGetMonitorUserPointer(m.data)
|
||||
}
|
||||
|
||||
// GetPhysicalSize returns the size, in millimetres, of the display area of the
|
||||
// monitor.
|
||||
//
|
||||
// Note: Some operating systems do not provide accurate information, either
|
||||
// because the monitor's EDID data is incorrect, or because the driver does not
|
||||
// report it accurately.
|
||||
func (m *Monitor) GetPhysicalSize() (width, height int, err error) {
|
||||
var wi, h C.int
|
||||
C.glfwGetMonitorPhysicalSize(m.data, &wi, &h)
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return int(wi), int(h), nil
|
||||
}
|
||||
|
||||
// GetName returns a human-readable name of the monitor, encoded as UTF-8.
|
||||
func (m *Monitor) GetName() (string, error) {
|
||||
mn := C.glfwGetMonitorName(m.data)
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if mn == nil {
|
||||
return "", nil
|
||||
}
|
||||
return C.GoString(mn), nil
|
||||
}
|
||||
|
||||
// MonitorCallback is the signature for monitor configuration callback
|
||||
// functions.
|
||||
type MonitorCallback func(monitor *Monitor, event PeripheralEvent)
|
||||
|
||||
// SetMonitorCallback sets the monitor configuration callback, or removes the
|
||||
// currently set callback. This is called when a monitor is connected to or
|
||||
// disconnected from the system.
|
||||
//
|
||||
// This function must only be called from the main thread.
|
||||
func SetMonitorCallback(cbfun MonitorCallback) (MonitorCallback, error) {
|
||||
previous := fMonitorHolder
|
||||
fMonitorHolder = cbfun
|
||||
if cbfun == nil {
|
||||
C.glfwSetMonitorCallback(nil)
|
||||
} else {
|
||||
C.glfwSetMonitorCallbackCB()
|
||||
}
|
||||
return previous, nil
|
||||
}
|
||||
|
||||
// GetVideoModes returns an array of all video modes supported by the monitor.
|
||||
// The returned array is sorted in ascending order, first by color bit depth
|
||||
// (the sum of all channel depths) and then by resolution area (the product of
|
||||
// width and height).
|
||||
func (m *Monitor) GetVideoModes() ([]*VidMode, error) {
|
||||
var length int
|
||||
|
||||
vC := C.glfwGetVideoModes(m.data, (*C.int)(unsafe.Pointer(&length)))
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if vC == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
v := make([]*VidMode, length)
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
t := C.GetVidmodeAtIndex(vC, C.int(i))
|
||||
v[i] = &VidMode{int(t.width), int(t.height), int(t.redBits), int(t.greenBits), int(t.blueBits), int(t.refreshRate)}
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// GetVideoMode returns the current video mode of the monitor. If you
|
||||
// are using a full screen window, the return value will therefore depend on
|
||||
// whether it is focused.
|
||||
func (m *Monitor) GetVideoMode() (*VidMode, error) {
|
||||
t := C.glfwGetVideoMode(m.data)
|
||||
if t == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &VidMode{int(t.width), int(t.height), int(t.redBits), int(t.greenBits), int(t.blueBits), int(t.refreshRate)}, nil
|
||||
}
|
||||
|
||||
// SetGamma generates a 256-element gamma ramp from the specified exponent and then calls
|
||||
// SetGamma with it.
|
||||
func (m *Monitor) SetGamma(gamma float32) error {
|
||||
C.glfwSetGamma(m.data, C.float(gamma))
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetGammaRamp retrieves the current gamma ramp of the monitor.
|
||||
func (m *Monitor) GetGammaRamp() (*GammaRamp, error) {
|
||||
var ramp GammaRamp
|
||||
|
||||
rampC := C.glfwGetGammaRamp(m.data)
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if rampC == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
length := int(rampC.size)
|
||||
ramp.Red = make([]uint16, length)
|
||||
ramp.Green = make([]uint16, length)
|
||||
ramp.Blue = make([]uint16, length)
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
ramp.Red[i] = uint16(C.GetGammaAtIndex(rampC.red, C.int(i)))
|
||||
ramp.Green[i] = uint16(C.GetGammaAtIndex(rampC.green, C.int(i)))
|
||||
ramp.Blue[i] = uint16(C.GetGammaAtIndex(rampC.blue, C.int(i)))
|
||||
}
|
||||
|
||||
return &ramp, nil
|
||||
}
|
||||
|
||||
// SetGammaRamp sets the current gamma ramp for the monitor.
|
||||
func (m *Monitor) SetGammaRamp(ramp *GammaRamp) error {
|
||||
var rampC C.GLFWgammaramp
|
||||
|
||||
length := len(ramp.Red)
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
C.SetGammaAtIndex(rampC.red, C.int(i), C.ushort(ramp.Red[i]))
|
||||
C.SetGammaAtIndex(rampC.green, C.int(i), C.ushort(ramp.Green[i]))
|
||||
C.SetGammaAtIndex(rampC.blue, C.int(i), C.ushort(ramp.Blue[i]))
|
||||
}
|
||||
|
||||
C.glfwSetGammaRamp(m.data, &rampC)
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+274
@@ -0,0 +1,274 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
package glfw
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
if err := window.platformSetWindowMonitor(nil, 0, 0, width, height, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
xoff, yoff, _, _, err := window.platformGetWindowFrameSize()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := window.platformSetWindowPos(xoff, yoff); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
for i, m := range _glfw.monitors {
|
||||
if m == monitor {
|
||||
copy(_glfw.monitors[i:], _glfw.monitors[i+1:])
|
||||
_glfw.monitors[len(_glfw.monitors)-1] = nil
|
||||
_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
|
||||
}
|
||||
|
||||
// GetPhysicalSize is not implemented.
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// SetUserPointer is not implemented.
|
||||
// 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
|
||||
}
|
||||
return m.platformGetVideoMode(), nil
|
||||
}
|
||||
|
||||
// SetGamma is not implemented.
|
||||
// GetGammaRamp is not implemented.
|
||||
// SetGammaRamp is not implemented.
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2012 The glfw3-go Authors
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
package glfw
|
||||
|
||||
/*
|
||||
#define GLFW_EXPOSE_NATIVE_COCOA
|
||||
#define GLFW_EXPOSE_NATIVE_NSGL
|
||||
#include "glfw3_unix.h"
|
||||
#include "glfw3native_unix.h"
|
||||
|
||||
// workaround wrappers needed due to a cgo and/or LLVM bug.
|
||||
// See: https://github.com/go-gl/glfw/issues/136
|
||||
static void *workaround_glfwGetCocoaWindow(GLFWwindow *w) {
|
||||
return (void *)glfwGetCocoaWindow(w);
|
||||
}
|
||||
static void *workaround_glfwGetNSGLContext(GLFWwindow *w) {
|
||||
return (void *)glfwGetNSGLContext(w);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
import "unsafe"
|
||||
|
||||
// GetCocoaMonitor returns the CGDirectDisplayID of the monitor.
|
||||
func (m *Monitor) GetCocoaMonitor() (uintptr, error) {
|
||||
ret := uintptr(C.glfwGetCocoaMonitor(m.data))
|
||||
return ret, fetchErrorIgnoringPlatformError()
|
||||
}
|
||||
|
||||
// GetCocoaWindow returns the NSWindow of the window.
|
||||
func (w *Window) GetCocoaWindow() (uintptr, error) {
|
||||
ret := uintptr(C.workaround_glfwGetCocoaWindow(w.data))
|
||||
return ret, fetchErrorIgnoringPlatformError()
|
||||
}
|
||||
|
||||
// GetNSGLContext returns the NSOpenGLContext of the window.
|
||||
func (w *Window) GetNSGLContext() (unsafe.Pointer, error) {
|
||||
ret := C.workaround_glfwGetNSGLContext(w.data)
|
||||
return ret, fetchErrorIgnoringPlatformError()
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2012 The glfw3-go Authors
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build freebsd || linux || netbsd || openbsd
|
||||
|
||||
package glfw
|
||||
|
||||
//#include <stdlib.h>
|
||||
//#define GLFW_EXPOSE_NATIVE_X11
|
||||
//#define GLFW_EXPOSE_NATIVE_GLX
|
||||
//#define GLFW_INCLUDE_NONE
|
||||
//#include "glfw3_unix.h"
|
||||
//#include "glfw3native_unix.h"
|
||||
import "C"
|
||||
import "unsafe"
|
||||
|
||||
func GetX11Display() (*C.Display, error) {
|
||||
ret := C.glfwGetX11Display()
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// GetX11Adapter returns the RRCrtc of the monitor.
|
||||
func (m *Monitor) GetX11Adapter() (C.RRCrtc, error) {
|
||||
ret := C.glfwGetX11Adapter(m.data)
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// GetX11Monitor returns the RROutput of the monitor.
|
||||
func (m *Monitor) GetX11Monitor() (C.RROutput, error) {
|
||||
ret := C.glfwGetX11Monitor(m.data)
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// GetX11Window returns the Window of the window.
|
||||
func (w *Window) GetX11Window() (C.Window, error) {
|
||||
ret := C.glfwGetX11Window(w.data)
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// GetGLXContext returns the GLXContext of the window.
|
||||
func (w *Window) GetGLXContext() (C.GLXContext, error) {
|
||||
ret := C.glfwGetGLXContext(w.data)
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// GetGLXWindow returns the GLXWindow of the window.
|
||||
func (w *Window) GetGLXWindow() (C.GLXWindow, error) {
|
||||
ret := C.glfwGetGLXWindow(w.data)
|
||||
if err := fetchErrorIgnoringPlatformError(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// SetX11SelectionString sets the X11 selection string.
|
||||
func SetX11SelectionString(str string) {
|
||||
s := C.CString(str)
|
||||
defer C.free(unsafe.Pointer(s))
|
||||
C.glfwSetX11SelectionString(s)
|
||||
}
|
||||
|
||||
// GetX11SelectionString gets the X11 selection string.
|
||||
func GetX11SelectionString() string {
|
||||
s := C.glfwGetX11SelectionString()
|
||||
return C.GoString(s)
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2009-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
// NOTE: Many Cocoa enum values have been renamed and we need to build across
|
||||
// SDK versions where one is unavailable or deprecated.
|
||||
// We use the newer names in code and replace them with the older names if
|
||||
// the base SDK does not provide the newer names.
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED < 101400
|
||||
#define NSOpenGLContextParameterSwapInterval NSOpenGLCPSwapInterval
|
||||
#define NSOpenGLContextParameterSurfaceOpacity NSOpenGLCPSurfaceOpacity
|
||||
#endif
|
||||
|
||||
#define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextNSGL nsgl
|
||||
#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryNSGL nsgl
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
|
||||
// NSGL-specific per-context data
|
||||
//
|
||||
typedef struct _GLFWcontextNSGL
|
||||
{
|
||||
id pixelFormat;
|
||||
id object;
|
||||
} _GLFWcontextNSGL;
|
||||
|
||||
// NSGL-specific global data
|
||||
//
|
||||
typedef struct _GLFWlibraryNSGL
|
||||
{
|
||||
// dlopen handle for OpenGL.framework (for glfwGetProcAddress)
|
||||
CFBundleRef framework;
|
||||
} _GLFWlibraryNSGL;
|
||||
|
||||
|
||||
GLFWbool _glfwInitNSGL(void);
|
||||
void _glfwTerminateNSGL(void);
|
||||
GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
|
||||
const _GLFWctxconfig* ctxconfig,
|
||||
const _GLFWfbconfig* fbconfig);
|
||||
void _glfwDestroyContextNSGL(_GLFWwindow* window);
|
||||
|
||||
+352
@@ -0,0 +1,352 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2009-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
#include "internal_unix.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
|
||||
static void makeContextCurrentNSGL(_GLFWwindow* window)
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
||||
if (window)
|
||||
[window->context.nsgl.object makeCurrentContext];
|
||||
else
|
||||
[NSOpenGLContext clearCurrentContext];
|
||||
|
||||
_glfwPlatformSetTls(&_glfw.contextSlot, window);
|
||||
|
||||
} // autoreleasepool
|
||||
}
|
||||
|
||||
static void swapBuffersNSGL(_GLFWwindow* window)
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
||||
// HACK: Simulate vsync with usleep as NSGL swap interval does not apply to
|
||||
// windows with a non-visible occlusion state
|
||||
if (window->ns.occluded)
|
||||
{
|
||||
int interval = 0;
|
||||
[window->context.nsgl.object getValues:&interval
|
||||
forParameter:NSOpenGLContextParameterSwapInterval];
|
||||
|
||||
if (interval > 0)
|
||||
{
|
||||
const double framerate = 60.0;
|
||||
const uint64_t frequency = _glfwPlatformGetTimerFrequency();
|
||||
const uint64_t value = _glfwPlatformGetTimerValue();
|
||||
|
||||
const double elapsed = value / (double) frequency;
|
||||
const double period = 1.0 / framerate;
|
||||
const double delay = period - fmod(elapsed, period);
|
||||
|
||||
usleep(floorl(delay * 1e6));
|
||||
}
|
||||
}
|
||||
|
||||
[window->context.nsgl.object flushBuffer];
|
||||
|
||||
} // autoreleasepool
|
||||
}
|
||||
|
||||
static void swapIntervalNSGL(int interval)
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
||||
_GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot);
|
||||
if (window)
|
||||
{
|
||||
[window->context.nsgl.object setValues:&interval
|
||||
forParameter:NSOpenGLContextParameterSwapInterval];
|
||||
}
|
||||
|
||||
} // autoreleasepool
|
||||
}
|
||||
|
||||
static int extensionSupportedNSGL(const char* extension)
|
||||
{
|
||||
// There are no NSGL extensions
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
static GLFWglproc getProcAddressNSGL(const char* procname)
|
||||
{
|
||||
CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault,
|
||||
procname,
|
||||
kCFStringEncodingASCII);
|
||||
|
||||
GLFWglproc symbol = CFBundleGetFunctionPointerForName(_glfw.nsgl.framework,
|
||||
symbolName);
|
||||
|
||||
CFRelease(symbolName);
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
static void destroyContextNSGL(_GLFWwindow* window)
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
||||
[window->context.nsgl.pixelFormat release];
|
||||
window->context.nsgl.pixelFormat = nil;
|
||||
|
||||
[window->context.nsgl.object release];
|
||||
window->context.nsgl.object = nil;
|
||||
|
||||
} // autoreleasepool
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW internal API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Initialize OpenGL support
|
||||
//
|
||||
GLFWbool _glfwInitNSGL(void)
|
||||
{
|
||||
if (_glfw.nsgl.framework)
|
||||
return GLFW_TRUE;
|
||||
|
||||
_glfw.nsgl.framework =
|
||||
CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
|
||||
if (_glfw.nsgl.framework == NULL)
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE,
|
||||
"NSGL: Failed to locate OpenGL framework");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
// Terminate OpenGL support
|
||||
//
|
||||
void _glfwTerminateNSGL(void)
|
||||
{
|
||||
}
|
||||
|
||||
// Create the OpenGL context
|
||||
//
|
||||
GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
|
||||
const _GLFWctxconfig* ctxconfig,
|
||||
const _GLFWfbconfig* fbconfig)
|
||||
{
|
||||
if (ctxconfig->client == GLFW_OPENGL_ES_API)
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE,
|
||||
"NSGL: OpenGL ES is not available on macOS");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (ctxconfig->major > 2)
|
||||
{
|
||||
if (ctxconfig->major == 3 && ctxconfig->minor < 2)
|
||||
{
|
||||
_glfwInputError(GLFW_VERSION_UNAVAILABLE,
|
||||
"NSGL: The targeted version of macOS does not support OpenGL 3.0 or 3.1 but may support 3.2 and above");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (!ctxconfig->forward || ctxconfig->profile != GLFW_OPENGL_CORE_PROFILE)
|
||||
{
|
||||
_glfwInputError(GLFW_VERSION_UNAVAILABLE,
|
||||
"NSGL: The targeted version of macOS only supports forward-compatible core profile contexts for OpenGL 3.2 and above");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// Context robustness modes (GL_KHR_robustness) are not yet supported by
|
||||
// macOS but are not a hard constraint, so ignore and continue
|
||||
|
||||
// Context release behaviors (GL_KHR_context_flush_control) are not yet
|
||||
// supported by macOS but are not a hard constraint, so ignore and continue
|
||||
|
||||
// Debug contexts (GL_KHR_debug) are not yet supported by macOS but are not
|
||||
// a hard constraint, so ignore and continue
|
||||
|
||||
// No-error contexts (GL_KHR_no_error) are not yet supported by macOS but
|
||||
// are not a hard constraint, so ignore and continue
|
||||
|
||||
#define addAttrib(a) \
|
||||
{ \
|
||||
assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \
|
||||
attribs[index++] = a; \
|
||||
}
|
||||
#define setAttrib(a, v) { addAttrib(a); addAttrib(v); }
|
||||
|
||||
NSOpenGLPixelFormatAttribute attribs[40];
|
||||
int index = 0;
|
||||
|
||||
addAttrib(NSOpenGLPFAAccelerated);
|
||||
addAttrib(NSOpenGLPFAClosestPolicy);
|
||||
|
||||
if (ctxconfig->nsgl.offline)
|
||||
{
|
||||
addAttrib(NSOpenGLPFAAllowOfflineRenderers);
|
||||
// NOTE: This replaces the NSSupportsAutomaticGraphicsSwitching key in
|
||||
// Info.plist for unbundled applications
|
||||
// HACK: This assumes that NSOpenGLPixelFormat will remain
|
||||
// a straightforward wrapper of its CGL counterpart
|
||||
addAttrib(kCGLPFASupportsAutomaticGraphicsSwitching);
|
||||
}
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
|
||||
if (ctxconfig->major >= 4)
|
||||
{
|
||||
setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core);
|
||||
}
|
||||
else
|
||||
#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/
|
||||
if (ctxconfig->major >= 3)
|
||||
{
|
||||
setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core);
|
||||
}
|
||||
|
||||
if (ctxconfig->major <= 2)
|
||||
{
|
||||
if (fbconfig->auxBuffers != GLFW_DONT_CARE)
|
||||
setAttrib(NSOpenGLPFAAuxBuffers, fbconfig->auxBuffers);
|
||||
|
||||
if (fbconfig->accumRedBits != GLFW_DONT_CARE &&
|
||||
fbconfig->accumGreenBits != GLFW_DONT_CARE &&
|
||||
fbconfig->accumBlueBits != GLFW_DONT_CARE &&
|
||||
fbconfig->accumAlphaBits != GLFW_DONT_CARE)
|
||||
{
|
||||
const int accumBits = fbconfig->accumRedBits +
|
||||
fbconfig->accumGreenBits +
|
||||
fbconfig->accumBlueBits +
|
||||
fbconfig->accumAlphaBits;
|
||||
|
||||
setAttrib(NSOpenGLPFAAccumSize, accumBits);
|
||||
}
|
||||
}
|
||||
|
||||
if (fbconfig->redBits != GLFW_DONT_CARE &&
|
||||
fbconfig->greenBits != GLFW_DONT_CARE &&
|
||||
fbconfig->blueBits != GLFW_DONT_CARE)
|
||||
{
|
||||
int colorBits = fbconfig->redBits +
|
||||
fbconfig->greenBits +
|
||||
fbconfig->blueBits;
|
||||
|
||||
// macOS needs non-zero color size, so set reasonable values
|
||||
if (colorBits == 0)
|
||||
colorBits = 24;
|
||||
else if (colorBits < 15)
|
||||
colorBits = 15;
|
||||
|
||||
setAttrib(NSOpenGLPFAColorSize, colorBits);
|
||||
}
|
||||
|
||||
if (fbconfig->alphaBits != GLFW_DONT_CARE)
|
||||
setAttrib(NSOpenGLPFAAlphaSize, fbconfig->alphaBits);
|
||||
|
||||
if (fbconfig->depthBits != GLFW_DONT_CARE)
|
||||
setAttrib(NSOpenGLPFADepthSize, fbconfig->depthBits);
|
||||
|
||||
if (fbconfig->stencilBits != GLFW_DONT_CARE)
|
||||
setAttrib(NSOpenGLPFAStencilSize, fbconfig->stencilBits);
|
||||
|
||||
if (fbconfig->stereo)
|
||||
{
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
|
||||
_glfwInputError(GLFW_FORMAT_UNAVAILABLE,
|
||||
"NSGL: Stereo rendering is deprecated");
|
||||
return GLFW_FALSE;
|
||||
#else
|
||||
addAttrib(NSOpenGLPFAStereo);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (fbconfig->doublebuffer)
|
||||
addAttrib(NSOpenGLPFADoubleBuffer);
|
||||
|
||||
if (fbconfig->samples != GLFW_DONT_CARE)
|
||||
{
|
||||
if (fbconfig->samples == 0)
|
||||
{
|
||||
setAttrib(NSOpenGLPFASampleBuffers, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
setAttrib(NSOpenGLPFASampleBuffers, 1);
|
||||
setAttrib(NSOpenGLPFASamples, fbconfig->samples);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: All NSOpenGLPixelFormats on the relevant cards support sRGB
|
||||
// framebuffer, so there's no need (and no way) to request it
|
||||
|
||||
addAttrib(0);
|
||||
|
||||
#undef addAttrib
|
||||
#undef setAttrib
|
||||
|
||||
window->context.nsgl.pixelFormat =
|
||||
[[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
|
||||
if (window->context.nsgl.pixelFormat == nil)
|
||||
{
|
||||
_glfwInputError(GLFW_FORMAT_UNAVAILABLE,
|
||||
"NSGL: Failed to find a suitable pixel format");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
NSOpenGLContext* share = nil;
|
||||
|
||||
if (ctxconfig->share)
|
||||
share = ctxconfig->share->context.nsgl.object;
|
||||
|
||||
window->context.nsgl.object =
|
||||
[[NSOpenGLContext alloc] initWithFormat:window->context.nsgl.pixelFormat
|
||||
shareContext:share];
|
||||
if (window->context.nsgl.object == nil)
|
||||
{
|
||||
_glfwInputError(GLFW_VERSION_UNAVAILABLE,
|
||||
"NSGL: Failed to create OpenGL context");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (fbconfig->transparent)
|
||||
{
|
||||
GLint opaque = 0;
|
||||
[window->context.nsgl.object setValues:&opaque
|
||||
forParameter:NSOpenGLContextParameterSurfaceOpacity];
|
||||
}
|
||||
|
||||
[window->ns.view setWantsBestResolutionOpenGLSurface:window->ns.retina];
|
||||
|
||||
[window->context.nsgl.object setView:window->ns.view];
|
||||
|
||||
window->context.makeCurrent = makeContextCurrentNSGL;
|
||||
window->context.swapBuffers = swapBuffersNSGL;
|
||||
window->context.swapInterval = swapIntervalNSGL;
|
||||
window->context.extensionSupported = extensionSupportedNSGL;
|
||||
window->context.getProcAddress = getProcAddressNSGL;
|
||||
window->context.destroy = destroyContextNSGL;
|
||||
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW native API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GLFWAPI id glfwGetNSGLContext(GLFWwindow* handle)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(nil);
|
||||
|
||||
if (window->context.source != GLFW_NATIVE_CONTEXT_API)
|
||||
{
|
||||
_glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
|
||||
return nil;
|
||||
}
|
||||
|
||||
return window->context.nsgl.object;
|
||||
}
|
||||
|
||||
+361
@@ -0,0 +1,361 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2016 Google Inc.
|
||||
// SPDX-FileCopyrightText: 2016-2017 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || netbsd || openbsd
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "internal_unix.h"
|
||||
|
||||
|
||||
static void makeContextCurrentOSMesa(_GLFWwindow* window)
|
||||
{
|
||||
if (window)
|
||||
{
|
||||
int width, height;
|
||||
_glfwPlatformGetFramebufferSize(window, &width, &height);
|
||||
|
||||
// Check to see if we need to allocate a new buffer
|
||||
if ((window->context.osmesa.buffer == NULL) ||
|
||||
(width != window->context.osmesa.width) ||
|
||||
(height != window->context.osmesa.height))
|
||||
{
|
||||
free(window->context.osmesa.buffer);
|
||||
|
||||
// Allocate the new buffer (width * height * 8-bit RGBA)
|
||||
window->context.osmesa.buffer = calloc(4, (size_t) width * height);
|
||||
window->context.osmesa.width = width;
|
||||
window->context.osmesa.height = height;
|
||||
}
|
||||
|
||||
if (!OSMesaMakeCurrent(window->context.osmesa.handle,
|
||||
window->context.osmesa.buffer,
|
||||
GL_UNSIGNED_BYTE,
|
||||
width, height))
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"OSMesa: Failed to make context current");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_glfwPlatformSetTls(&_glfw.contextSlot, window);
|
||||
}
|
||||
|
||||
static GLFWglproc getProcAddressOSMesa(const char* procname)
|
||||
{
|
||||
return (GLFWglproc) OSMesaGetProcAddress(procname);
|
||||
}
|
||||
|
||||
static void destroyContextOSMesa(_GLFWwindow* window)
|
||||
{
|
||||
if (window->context.osmesa.handle)
|
||||
{
|
||||
OSMesaDestroyContext(window->context.osmesa.handle);
|
||||
window->context.osmesa.handle = NULL;
|
||||
}
|
||||
|
||||
if (window->context.osmesa.buffer)
|
||||
{
|
||||
free(window->context.osmesa.buffer);
|
||||
window->context.osmesa.width = 0;
|
||||
window->context.osmesa.height = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void swapBuffersOSMesa(_GLFWwindow* window)
|
||||
{
|
||||
// No double buffering on OSMesa
|
||||
}
|
||||
|
||||
static void swapIntervalOSMesa(int interval)
|
||||
{
|
||||
// No swap interval on OSMesa
|
||||
}
|
||||
|
||||
static int extensionSupportedOSMesa(const char* extension)
|
||||
{
|
||||
// OSMesa does not have extensions
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW internal API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GLFWbool _glfwInitOSMesa(void)
|
||||
{
|
||||
int i;
|
||||
const char* sonames[] =
|
||||
{
|
||||
#if defined(_GLFW_OSMESA_LIBRARY)
|
||||
_GLFW_OSMESA_LIBRARY,
|
||||
#elif defined(__APPLE__)
|
||||
"libOSMesa.8.dylib",
|
||||
#elif defined(__CYGWIN__)
|
||||
"libOSMesa-8.so",
|
||||
#elif defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
"libOSMesa.so",
|
||||
#else
|
||||
"libOSMesa.so.8",
|
||||
"libOSMesa.so.6",
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
if (_glfw.osmesa.handle)
|
||||
return GLFW_TRUE;
|
||||
|
||||
for (i = 0; sonames[i]; i++)
|
||||
{
|
||||
_glfw.osmesa.handle = _glfw_dlopen(sonames[i]);
|
||||
if (_glfw.osmesa.handle)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!_glfw.osmesa.handle)
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE, "OSMesa: Library not found");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
_glfw.osmesa.CreateContextExt = (PFN_OSMesaCreateContextExt)
|
||||
_glfw_dlsym(_glfw.osmesa.handle, "OSMesaCreateContextExt");
|
||||
_glfw.osmesa.CreateContextAttribs = (PFN_OSMesaCreateContextAttribs)
|
||||
_glfw_dlsym(_glfw.osmesa.handle, "OSMesaCreateContextAttribs");
|
||||
_glfw.osmesa.DestroyContext = (PFN_OSMesaDestroyContext)
|
||||
_glfw_dlsym(_glfw.osmesa.handle, "OSMesaDestroyContext");
|
||||
_glfw.osmesa.MakeCurrent = (PFN_OSMesaMakeCurrent)
|
||||
_glfw_dlsym(_glfw.osmesa.handle, "OSMesaMakeCurrent");
|
||||
_glfw.osmesa.GetColorBuffer = (PFN_OSMesaGetColorBuffer)
|
||||
_glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetColorBuffer");
|
||||
_glfw.osmesa.GetDepthBuffer = (PFN_OSMesaGetDepthBuffer)
|
||||
_glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetDepthBuffer");
|
||||
_glfw.osmesa.GetProcAddress = (PFN_OSMesaGetProcAddress)
|
||||
_glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetProcAddress");
|
||||
|
||||
if (!_glfw.osmesa.CreateContextExt ||
|
||||
!_glfw.osmesa.DestroyContext ||
|
||||
!_glfw.osmesa.MakeCurrent ||
|
||||
!_glfw.osmesa.GetColorBuffer ||
|
||||
!_glfw.osmesa.GetDepthBuffer ||
|
||||
!_glfw.osmesa.GetProcAddress)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"OSMesa: Failed to load required entry points");
|
||||
|
||||
_glfwTerminateOSMesa();
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
void _glfwTerminateOSMesa(void)
|
||||
{
|
||||
if (_glfw.osmesa.handle)
|
||||
{
|
||||
_glfw_dlclose(_glfw.osmesa.handle);
|
||||
_glfw.osmesa.handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#define setAttrib(a, v) \
|
||||
{ \
|
||||
assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \
|
||||
attribs[index++] = a; \
|
||||
attribs[index++] = v; \
|
||||
}
|
||||
|
||||
GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window,
|
||||
const _GLFWctxconfig* ctxconfig,
|
||||
const _GLFWfbconfig* fbconfig)
|
||||
{
|
||||
OSMesaContext share = NULL;
|
||||
const int accumBits = fbconfig->accumRedBits +
|
||||
fbconfig->accumGreenBits +
|
||||
fbconfig->accumBlueBits +
|
||||
fbconfig->accumAlphaBits;
|
||||
|
||||
if (ctxconfig->client == GLFW_OPENGL_ES_API)
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE,
|
||||
"OSMesa: OpenGL ES is not available on OSMesa");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (ctxconfig->share)
|
||||
share = ctxconfig->share->context.osmesa.handle;
|
||||
|
||||
if (OSMesaCreateContextAttribs)
|
||||
{
|
||||
int index = 0, attribs[40];
|
||||
|
||||
setAttrib(OSMESA_FORMAT, OSMESA_RGBA);
|
||||
setAttrib(OSMESA_DEPTH_BITS, fbconfig->depthBits);
|
||||
setAttrib(OSMESA_STENCIL_BITS, fbconfig->stencilBits);
|
||||
setAttrib(OSMESA_ACCUM_BITS, accumBits);
|
||||
|
||||
if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE)
|
||||
{
|
||||
setAttrib(OSMESA_PROFILE, OSMESA_CORE_PROFILE);
|
||||
}
|
||||
else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE)
|
||||
{
|
||||
setAttrib(OSMESA_PROFILE, OSMESA_COMPAT_PROFILE);
|
||||
}
|
||||
|
||||
if (ctxconfig->major != 1 || ctxconfig->minor != 0)
|
||||
{
|
||||
setAttrib(OSMESA_CONTEXT_MAJOR_VERSION, ctxconfig->major);
|
||||
setAttrib(OSMESA_CONTEXT_MINOR_VERSION, ctxconfig->minor);
|
||||
}
|
||||
|
||||
if (ctxconfig->forward)
|
||||
{
|
||||
_glfwInputError(GLFW_VERSION_UNAVAILABLE,
|
||||
"OSMesa: Forward-compatible contexts not supported");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
setAttrib(0, 0);
|
||||
|
||||
window->context.osmesa.handle =
|
||||
OSMesaCreateContextAttribs(attribs, share);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ctxconfig->profile)
|
||||
{
|
||||
_glfwInputError(GLFW_VERSION_UNAVAILABLE,
|
||||
"OSMesa: OpenGL profiles unavailable");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
window->context.osmesa.handle =
|
||||
OSMesaCreateContextExt(OSMESA_RGBA,
|
||||
fbconfig->depthBits,
|
||||
fbconfig->stencilBits,
|
||||
accumBits,
|
||||
share);
|
||||
}
|
||||
|
||||
if (window->context.osmesa.handle == NULL)
|
||||
{
|
||||
_glfwInputError(GLFW_VERSION_UNAVAILABLE,
|
||||
"OSMesa: Failed to create context");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
window->context.makeCurrent = makeContextCurrentOSMesa;
|
||||
window->context.swapBuffers = swapBuffersOSMesa;
|
||||
window->context.swapInterval = swapIntervalOSMesa;
|
||||
window->context.extensionSupported = extensionSupportedOSMesa;
|
||||
window->context.getProcAddress = getProcAddressOSMesa;
|
||||
window->context.destroy = destroyContextOSMesa;
|
||||
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
#undef setAttrib
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW native API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* handle, int* width,
|
||||
int* height, int* format, void** buffer)
|
||||
{
|
||||
void* mesaBuffer;
|
||||
GLint mesaWidth, mesaHeight, mesaFormat;
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
|
||||
|
||||
if (window->context.source != GLFW_OSMESA_CONTEXT_API)
|
||||
{
|
||||
_glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (!OSMesaGetColorBuffer(window->context.osmesa.handle,
|
||||
&mesaWidth, &mesaHeight,
|
||||
&mesaFormat, &mesaBuffer))
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"OSMesa: Failed to retrieve color buffer");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (width)
|
||||
*width = mesaWidth;
|
||||
if (height)
|
||||
*height = mesaHeight;
|
||||
if (format)
|
||||
*format = mesaFormat;
|
||||
if (buffer)
|
||||
*buffer = mesaBuffer;
|
||||
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* handle,
|
||||
int* width, int* height,
|
||||
int* bytesPerValue,
|
||||
void** buffer)
|
||||
{
|
||||
void* mesaBuffer;
|
||||
GLint mesaWidth, mesaHeight, mesaBytes;
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
|
||||
|
||||
if (window->context.source != GLFW_OSMESA_CONTEXT_API)
|
||||
{
|
||||
_glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (!OSMesaGetDepthBuffer(window->context.osmesa.handle,
|
||||
&mesaWidth, &mesaHeight,
|
||||
&mesaBytes, &mesaBuffer))
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"OSMesa: Failed to retrieve depth buffer");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (width)
|
||||
*width = mesaWidth;
|
||||
if (height)
|
||||
*height = mesaHeight;
|
||||
if (bytesPerValue)
|
||||
*bytesPerValue = mesaBytes;
|
||||
if (buffer)
|
||||
*buffer = mesaBuffer;
|
||||
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* handle)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
|
||||
if (window->context.source != GLFW_OSMESA_CONTEXT_API)
|
||||
{
|
||||
_glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return window->context.osmesa.handle;
|
||||
}
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2016 Google Inc.
|
||||
// SPDX-FileCopyrightText: 2016-2017 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || netbsd || openbsd
|
||||
|
||||
#define OSMESA_RGBA 0x1908
|
||||
#define OSMESA_FORMAT 0x22
|
||||
#define OSMESA_DEPTH_BITS 0x30
|
||||
#define OSMESA_STENCIL_BITS 0x31
|
||||
#define OSMESA_ACCUM_BITS 0x32
|
||||
#define OSMESA_PROFILE 0x33
|
||||
#define OSMESA_CORE_PROFILE 0x34
|
||||
#define OSMESA_COMPAT_PROFILE 0x35
|
||||
#define OSMESA_CONTEXT_MAJOR_VERSION 0x36
|
||||
#define OSMESA_CONTEXT_MINOR_VERSION 0x37
|
||||
|
||||
typedef void* OSMesaContext;
|
||||
typedef void (*OSMESAproc)(void);
|
||||
|
||||
typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextExt)(GLenum,GLint,GLint,GLint,OSMesaContext);
|
||||
typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextAttribs)(const int*,OSMesaContext);
|
||||
typedef void (GLAPIENTRY * PFN_OSMesaDestroyContext)(OSMesaContext);
|
||||
typedef int (GLAPIENTRY * PFN_OSMesaMakeCurrent)(OSMesaContext,void*,int,int,int);
|
||||
typedef int (GLAPIENTRY * PFN_OSMesaGetColorBuffer)(OSMesaContext,int*,int*,int*,void**);
|
||||
typedef int (GLAPIENTRY * PFN_OSMesaGetDepthBuffer)(OSMesaContext,int*,int*,int*,void**);
|
||||
typedef GLFWglproc (GLAPIENTRY * PFN_OSMesaGetProcAddress)(const char*);
|
||||
#define OSMesaCreateContextExt _glfw.osmesa.CreateContextExt
|
||||
#define OSMesaCreateContextAttribs _glfw.osmesa.CreateContextAttribs
|
||||
#define OSMesaDestroyContext _glfw.osmesa.DestroyContext
|
||||
#define OSMesaMakeCurrent _glfw.osmesa.MakeCurrent
|
||||
#define OSMesaGetColorBuffer _glfw.osmesa.GetColorBuffer
|
||||
#define OSMesaGetDepthBuffer _glfw.osmesa.GetDepthBuffer
|
||||
#define OSMesaGetProcAddress _glfw.osmesa.GetProcAddress
|
||||
|
||||
#define _GLFW_OSMESA_CONTEXT_STATE _GLFWcontextOSMesa osmesa
|
||||
#define _GLFW_OSMESA_LIBRARY_CONTEXT_STATE _GLFWlibraryOSMesa osmesa
|
||||
|
||||
|
||||
// OSMesa-specific per-context data
|
||||
//
|
||||
typedef struct _GLFWcontextOSMesa
|
||||
{
|
||||
OSMesaContext handle;
|
||||
int width;
|
||||
int height;
|
||||
void* buffer;
|
||||
} _GLFWcontextOSMesa;
|
||||
|
||||
// OSMesa-specific global data
|
||||
//
|
||||
typedef struct _GLFWlibraryOSMesa
|
||||
{
|
||||
void* handle;
|
||||
|
||||
PFN_OSMesaCreateContextExt CreateContextExt;
|
||||
PFN_OSMesaCreateContextAttribs CreateContextAttribs;
|
||||
PFN_OSMesaDestroyContext DestroyContext;
|
||||
PFN_OSMesaMakeCurrent MakeCurrent;
|
||||
PFN_OSMesaGetColorBuffer GetColorBuffer;
|
||||
PFN_OSMesaGetDepthBuffer GetDepthBuffer;
|
||||
PFN_OSMesaGetProcAddress GetProcAddress;
|
||||
} _GLFWlibraryOSMesa;
|
||||
|
||||
|
||||
GLFWbool _glfwInitOSMesa(void);
|
||||
void _glfwTerminateOSMesa(void);
|
||||
GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window,
|
||||
const _GLFWctxconfig* ctxconfig,
|
||||
const _GLFWfbconfig* fbconfig);
|
||||
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2017 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
|
||||
|
||||
package glfw
|
||||
|
||||
// #include "internal_unix.h"
|
||||
import "C"
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// TODO: make these methods on GLFWtls like on windows
|
||||
// TODO: use uintptr instead of unsafe.Pointer once matching C API is no longer needed
|
||||
|
||||
//export _glfwPlatformCreateTls
|
||||
func _glfwPlatformCreateTls(tls *C._GLFWtls) C.GLFWbool {
|
||||
if tls.posix.allocated != False {
|
||||
panic("glfw: TLS must not be allocated")
|
||||
}
|
||||
if pthread_key_create(&tls.posix.key, 0) != 0 {
|
||||
_glfwInputError(int32(PlatformError),
|
||||
C.CString("POSIX: Failed to create context TLS"))
|
||||
return False
|
||||
}
|
||||
tls.posix.allocated = True
|
||||
return True
|
||||
}
|
||||
|
||||
//export _glfwPlatformDestroyTls
|
||||
func _glfwPlatformDestroyTls(tls *C._GLFWtls) {
|
||||
if tls.posix.allocated != 0 {
|
||||
pthread_key_delete(tls.posix.key)
|
||||
}
|
||||
*tls = C._GLFWtls{}
|
||||
}
|
||||
|
||||
//export _glfwPlatformGetTls
|
||||
func _glfwPlatformGetTls(tls *C._GLFWtls) unsafe.Pointer {
|
||||
if tls.posix.allocated != True {
|
||||
panic("glfw: TLS must be allocated")
|
||||
}
|
||||
var p = pthread_getspecific(tls.posix.key)
|
||||
return *(*unsafe.Pointer)(unsafe.Pointer(&p)) // TODO: replace with uintptr
|
||||
}
|
||||
|
||||
//export _glfwPlatformSetTls
|
||||
func _glfwPlatformSetTls(tls *C._GLFWtls, value unsafe.Pointer) {
|
||||
if tls.posix.allocated != True {
|
||||
panic("glfw: TLS must be allocated")
|
||||
}
|
||||
pthread_setspecific(tls.posix.key, uintptr(value))
|
||||
}
|
||||
|
||||
//export _glfwPlatformCreateMutex
|
||||
func _glfwPlatformCreateMutex(mutex *C._GLFWmutex) C.GLFWbool {
|
||||
if mutex.posix.allocated != False {
|
||||
panic("glfw: mutex must not be allocated")
|
||||
}
|
||||
if pthread_mutex_init(&mutex.posix.handle, nil) != 0 {
|
||||
_glfwInputError(int32(PlatformError), C.CString("POSIX: Failed to create mutex"))
|
||||
return False
|
||||
}
|
||||
mutex.posix.allocated = True
|
||||
return True
|
||||
}
|
||||
|
||||
//export _glfwPlatformDestroyMutex
|
||||
func _glfwPlatformDestroyMutex(mutex *C._GLFWmutex) {
|
||||
if mutex.posix.allocated != 0 {
|
||||
pthread_mutex_destroy(&mutex.posix.handle)
|
||||
}
|
||||
*mutex = C._GLFWmutex{}
|
||||
}
|
||||
|
||||
//export _glfwPlatformLockMutex
|
||||
func _glfwPlatformLockMutex(mutex *C._GLFWmutex) {
|
||||
if mutex.posix.allocated != True {
|
||||
panic("glfw: mutex must be allocated")
|
||||
}
|
||||
pthread_mutex_lock(&mutex.posix.handle)
|
||||
}
|
||||
|
||||
//export _glfwPlatformUnlockMutex
|
||||
func _glfwPlatformUnlockMutex(mutex *C._GLFWmutex) {
|
||||
if mutex.posix.allocated != True {
|
||||
panic("glfw: mutex must be allocated")
|
||||
}
|
||||
pthread_mutex_unlock(&mutex.posix.handle)
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2017 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build freebsd || linux || netbsd || openbsd
|
||||
|
||||
#include "internal_unix.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW platform API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls)
|
||||
{
|
||||
assert(tls->posix.allocated == GLFW_FALSE);
|
||||
|
||||
if (pthread_key_create(&tls->posix.key, NULL) != 0)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"POSIX: Failed to create context TLS");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
tls->posix.allocated = GLFW_TRUE;
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
void _glfwPlatformDestroyTls(_GLFWtls* tls)
|
||||
{
|
||||
if (tls->posix.allocated)
|
||||
pthread_key_delete(tls->posix.key);
|
||||
memset(tls, 0, sizeof(_GLFWtls));
|
||||
}
|
||||
|
||||
void* _glfwPlatformGetTls(_GLFWtls* tls)
|
||||
{
|
||||
assert(tls->posix.allocated == GLFW_TRUE);
|
||||
return pthread_getspecific(tls->posix.key);
|
||||
}
|
||||
|
||||
void _glfwPlatformSetTls(_GLFWtls* tls, void* value)
|
||||
{
|
||||
assert(tls->posix.allocated == GLFW_TRUE);
|
||||
pthread_setspecific(tls->posix.key, value);
|
||||
}
|
||||
|
||||
GLFWbool _glfwPlatformCreateMutex(_GLFWmutex* mutex)
|
||||
{
|
||||
assert(mutex->posix.allocated == GLFW_FALSE);
|
||||
|
||||
if (pthread_mutex_init(&mutex->posix.handle, NULL) != 0)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "POSIX: Failed to create mutex");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
return mutex->posix.allocated = GLFW_TRUE;
|
||||
}
|
||||
|
||||
void _glfwPlatformDestroyMutex(_GLFWmutex* mutex)
|
||||
{
|
||||
if (mutex->posix.allocated)
|
||||
pthread_mutex_destroy(&mutex->posix.handle);
|
||||
memset(mutex, 0, sizeof(_GLFWmutex));
|
||||
}
|
||||
|
||||
void _glfwPlatformLockMutex(_GLFWmutex* mutex)
|
||||
{
|
||||
assert(mutex->posix.allocated == GLFW_TRUE);
|
||||
pthread_mutex_lock(&mutex->posix.handle);
|
||||
}
|
||||
|
||||
void _glfwPlatformUnlockMutex(_GLFWmutex* mutex)
|
||||
{
|
||||
assert(mutex->posix.allocated == GLFW_TRUE);
|
||||
pthread_mutex_unlock(&mutex->posix.handle);
|
||||
}
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2017 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || netbsd || openbsd
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#define _GLFW_PLATFORM_TLS_STATE _GLFWtlsPOSIX posix
|
||||
#define _GLFW_PLATFORM_MUTEX_STATE _GLFWmutexPOSIX posix
|
||||
|
||||
|
||||
// POSIX-specific thread local storage data
|
||||
//
|
||||
typedef struct _GLFWtlsPOSIX
|
||||
{
|
||||
GLFWbool allocated;
|
||||
pthread_key_t key;
|
||||
} _GLFWtlsPOSIX;
|
||||
|
||||
// POSIX-specific mutex data
|
||||
//
|
||||
typedef struct _GLFWmutexPOSIX
|
||||
{
|
||||
GLFWbool allocated;
|
||||
pthread_mutex_t handle;
|
||||
} _GLFWmutexPOSIX;
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2017 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build freebsd || linux || netbsd || openbsd
|
||||
|
||||
#include "internal_unix.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW internal API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Initialise timer
|
||||
//
|
||||
void _glfwInitTimerPOSIX(void)
|
||||
{
|
||||
#if defined(CLOCK_MONOTONIC)
|
||||
struct timespec ts;
|
||||
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
|
||||
{
|
||||
_glfw.timer.posix.monotonic = GLFW_TRUE;
|
||||
_glfw.timer.posix.frequency = 1000000000;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
_glfw.timer.posix.monotonic = GLFW_FALSE;
|
||||
_glfw.timer.posix.frequency = 1000000;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW platform API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
uint64_t _glfwPlatformGetTimerValue(void)
|
||||
{
|
||||
#if defined(CLOCK_MONOTONIC)
|
||||
if (_glfw.timer.posix.monotonic)
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (uint64_t) ts.tv_sec * (uint64_t) 1000000000 + (uint64_t) ts.tv_nsec;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return (uint64_t) tv.tv_sec * (uint64_t) 1000000 + (uint64_t) tv.tv_usec;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t _glfwPlatformGetTimerFrequency(void)
|
||||
{
|
||||
return _glfw.timer.posix.frequency;
|
||||
}
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2017 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build freebsd || linux || netbsd || openbsd
|
||||
|
||||
#define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerPOSIX posix
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
// POSIX-specific global timer data
|
||||
//
|
||||
typedef struct _GLFWtimerPOSIX
|
||||
{
|
||||
GLFWbool monotonic;
|
||||
uint64_t frequency;
|
||||
} _GLFWtimerPOSIX;
|
||||
|
||||
|
||||
void _glfwInitTimerPOSIX(void);
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2012 The glfw3-go Authors
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || netbsd || openbsd
|
||||
|
||||
package glfw
|
||||
|
||||
// #include <stdlib.h>
|
||||
// #define GLFW_INCLUDE_NONE
|
||||
// #include "glfw3_unix.h"
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/draw"
|
||||
)
|
||||
|
||||
func imageToGLFWImage(img image.Image) (glfwImg C.GLFWimage, free func()) {
|
||||
b := img.Bounds()
|
||||
if b.Dx() == 0 || b.Dy() == 0 {
|
||||
return C.GLFWimage{
|
||||
width: C.int(b.Dx()),
|
||||
height: C.int(b.Dy()),
|
||||
}, func() {}
|
||||
}
|
||||
|
||||
m := image.NewNRGBA(image.Rect(0, 0, b.Dx(), b.Dy()))
|
||||
draw.Draw(m, m.Bounds(), img, b.Min, draw.Src)
|
||||
pixels := m.Pix
|
||||
|
||||
cpixels := C.CBytes(pixels)
|
||||
free = func() {
|
||||
C.free(cpixels)
|
||||
}
|
||||
|
||||
return C.GLFWimage{
|
||||
width: C.int(b.Dx()),
|
||||
height: C.int(b.Dy()),
|
||||
pixels: (*C.uchar)(cpixels),
|
||||
}, free
|
||||
}
|
||||
+596
@@ -0,0 +1,596 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
package glfw
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/microsoftgdk"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/winver"
|
||||
)
|
||||
|
||||
type platformContextState struct {
|
||||
dc _HDC
|
||||
handle _HGLRC
|
||||
interval int
|
||||
}
|
||||
|
||||
type platformLibraryContextState 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 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.platformContext.ARB_pixel_format {
|
||||
var attrib int32 = _WGL_NUMBER_PIXEL_FORMATS_ARB
|
||||
if err := wglGetPixelFormatAttribivARB(w.context.platform.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.platformContext.ARB_multisample {
|
||||
attribs = append(attribs, _WGL_SAMPLES_ARB)
|
||||
}
|
||||
|
||||
if ctxconfig.client == OpenGLAPI {
|
||||
if _glfw.platformContext.ARB_framebuffer_sRGB || _glfw.platformContext.EXT_framebuffer_sRGB {
|
||||
attribs = append(attribs, _WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB)
|
||||
}
|
||||
} else {
|
||||
if _glfw.platformContext.EXT_colorspace {
|
||||
attribs = append(attribs, _WGL_COLORSPACE_EXT)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
c, err := _DescribePixelFormat(w.context.platform.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.platformContext.ARB_pixel_format {
|
||||
// Get pixel format attributes through "modern" extension
|
||||
values := make([]int32, len(attribs))
|
||||
if err := wglGetPixelFormatAttribivARB(w.context.platform.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.platformContext.ARB_multisample {
|
||||
u.samples = int(findAttribValue(_WGL_SAMPLES_ARB))
|
||||
}
|
||||
|
||||
if ctxconfig.client == OpenGLAPI {
|
||||
if _glfw.platformContext.ARB_framebuffer_sRGB || _glfw.platformContext.EXT_framebuffer_sRGB {
|
||||
if findAttribValue(_WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB) != 0 {
|
||||
u.sRGB = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if _glfw.platformContext.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.platform.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("glfw: the driver does not appear to support OpenGL")
|
||||
}
|
||||
|
||||
closest := chooseFBConfig(fbconfig_, usableConfigs)
|
||||
if closest == nil {
|
||||
return 0, fmt.Errorf("glfw: 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.platform.dc, window.context.platform.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 && winver.IsWindowsVistaOrGreater() {
|
||||
// DWM Composition is always enabled on Win8+
|
||||
enabled := winver.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.platform.interval; i++ {
|
||||
// Ignore an error from DWM functions as they might not be implemented e.g. on Proton (#2113).
|
||||
_ = _DwmFlush()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := _SwapBuffers(window.context.platform.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.platform.interval = interval
|
||||
|
||||
if window.monitor == nil && winver.IsWindowsVistaOrGreater() {
|
||||
// DWM Composition is always enabled on Win8+
|
||||
enabled := winver.IsWindows8OrGreater()
|
||||
|
||||
if !enabled {
|
||||
e, err := _DwmIsCompositionEnabled()
|
||||
// Ignore an error from DWM functions as they might not be implemented e.g. on Proton (#2113).
|
||||
if err == nil {
|
||||
enabled = e
|
||||
}
|
||||
}
|
||||
|
||||
// HACK: Disable WGL swap interval when desktop composition is enabled to
|
||||
// avoid interfering with DWM vsync
|
||||
if enabled {
|
||||
interval = 0
|
||||
}
|
||||
}
|
||||
|
||||
if _glfw.platformContext.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.platform.handle != 0 {
|
||||
// Ignore ERROR_BUSY. This happens when the thread is different from the context thread (#2518).
|
||||
// This is a known issue of GLFW (glfw/glfw#2239).
|
||||
// TODO: Delete the context on an appropriate thread.
|
||||
if err := wglDeleteContext(window.context.platform.handle); err != nil && !errors.Is(err, windows.ERROR_BUSY) {
|
||||
return err
|
||||
}
|
||||
window.context.platform.handle = 0
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func initWGL() error {
|
||||
if microsoftgdk.IsXbox() {
|
||||
return fmt.Errorf("glfw: WGL is not available in Xbox")
|
||||
}
|
||||
|
||||
if _glfw.platformContext.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.platformWindow.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: Functions must be loaded first as they're needed to retrieve the
|
||||
// extension string that tells us whether the functions are supported
|
||||
//
|
||||
// Interestingly, wglGetProcAddress might return 0 after extensionSupportedWGL is called.
|
||||
initWGLExtensionFunctions()
|
||||
|
||||
// NOTE: WGL_ARB_extensions_string and WGL_EXT_extensions_string are not
|
||||
// checked below as we are already using them
|
||||
_glfw.platformContext.ARB_multisample = extensionSupportedWGL("WGL_ARB_multisample")
|
||||
_glfw.platformContext.ARB_framebuffer_sRGB = extensionSupportedWGL("WGL_ARB_framebuffer_sRGB")
|
||||
_glfw.platformContext.EXT_framebuffer_sRGB = extensionSupportedWGL("WGL_EXT_framebuffer_sRGB")
|
||||
_glfw.platformContext.ARB_create_context = extensionSupportedWGL("WGL_ARB_create_context")
|
||||
_glfw.platformContext.ARB_create_context_profile = extensionSupportedWGL("WGL_ARB_create_context_profile")
|
||||
_glfw.platformContext.EXT_create_context_es2_profile = extensionSupportedWGL("WGL_EXT_create_context_es2_profile")
|
||||
_glfw.platformContext.ARB_create_context_robustness = extensionSupportedWGL("WGL_ARB_create_context_robustness")
|
||||
_glfw.platformContext.ARB_create_context_no_error = extensionSupportedWGL("WGL_ARB_create_context_no_error")
|
||||
_glfw.platformContext.EXT_swap_control = extensionSupportedWGL("WGL_EXT_swap_control")
|
||||
_glfw.platformContext.EXT_colorspace = extensionSupportedWGL("WGL_EXT_colorspace")
|
||||
_glfw.platformContext.ARB_pixel_format = extensionSupportedWGL("WGL_ARB_pixel_format")
|
||||
_glfw.platformContext.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.platformContext.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.platform.handle
|
||||
}
|
||||
|
||||
dc, err := _GetDC(w.platform.handle)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.context.platform.dc = dc
|
||||
|
||||
pixelFormat, err := w.choosePixelFormat(ctxconfig, fbconfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var pfd _PIXELFORMATDESCRIPTOR
|
||||
if _, err := _DescribePixelFormat(w.context.platform.dc, int32(pixelFormat), uint32(unsafe.Sizeof(pfd)), &pfd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := _SetPixelFormat(w.context.platform.dc, int32(pixelFormat), &pfd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ctxconfig.client == OpenGLAPI {
|
||||
if ctxconfig.forward && !_glfw.platformContext.ARB_create_context {
|
||||
return fmt.Errorf("glfw: a forward compatible OpenGL context requested but WGL_ARB_create_context is unavailable: %w", VersionUnavailable)
|
||||
}
|
||||
|
||||
if ctxconfig.profile != 0 && !_glfw.platformContext.ARB_create_context_profile {
|
||||
return fmt.Errorf("glfw: OpenGL profile requested but WGL_ARB_create_context_profile is unavailable: %w", VersionUnavailable)
|
||||
}
|
||||
} else {
|
||||
if !_glfw.platformContext.ARB_create_context || !_glfw.platformContext.ARB_create_context_profile || !_glfw.platformContext.EXT_create_context_es2_profile {
|
||||
return fmt.Errorf("glfw: OpenGL ES requested but WGL_ARB_create_context_es2_profile is unavailable: %w", APIUnavailable)
|
||||
}
|
||||
}
|
||||
|
||||
if _glfw.platformContext.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.platformContext.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.platformContext.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.platformContext.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.platform.handle, err = wglCreateContextAttribsARB(w.context.platform.dc, share, &attribs[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
w.context.platform.handle, err = wglCreateContext(w.context.platform.dc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if share != 0 {
|
||||
if err := wglShareLists(share, w.context.platform.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.platform.handle
|
||||
}
|
||||
+327
@@ -0,0 +1,327 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
package glfw
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/microsoftgdk"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/winver"
|
||||
)
|
||||
|
||||
func createKeyTables() {
|
||||
for i := range _glfw.platformWindow.keycodes {
|
||||
_glfw.platformWindow.keycodes[i] = -1
|
||||
}
|
||||
for i := range _glfw.platformWindow.scancodes {
|
||||
_glfw.platformWindow.keycodes[i] = -1
|
||||
}
|
||||
|
||||
_glfw.platformWindow.keycodes[0x00B] = Key0
|
||||
_glfw.platformWindow.keycodes[0x002] = Key1
|
||||
_glfw.platformWindow.keycodes[0x003] = Key2
|
||||
_glfw.platformWindow.keycodes[0x004] = Key3
|
||||
_glfw.platformWindow.keycodes[0x005] = Key4
|
||||
_glfw.platformWindow.keycodes[0x006] = Key5
|
||||
_glfw.platformWindow.keycodes[0x007] = Key6
|
||||
_glfw.platformWindow.keycodes[0x008] = Key7
|
||||
_glfw.platformWindow.keycodes[0x009] = Key8
|
||||
_glfw.platformWindow.keycodes[0x00A] = Key9
|
||||
_glfw.platformWindow.keycodes[0x01E] = KeyA
|
||||
_glfw.platformWindow.keycodes[0x030] = KeyB
|
||||
_glfw.platformWindow.keycodes[0x02E] = KeyC
|
||||
_glfw.platformWindow.keycodes[0x020] = KeyD
|
||||
_glfw.platformWindow.keycodes[0x012] = KeyE
|
||||
_glfw.platformWindow.keycodes[0x021] = KeyF
|
||||
_glfw.platformWindow.keycodes[0x022] = KeyG
|
||||
_glfw.platformWindow.keycodes[0x023] = KeyH
|
||||
_glfw.platformWindow.keycodes[0x017] = KeyI
|
||||
_glfw.platformWindow.keycodes[0x024] = KeyJ
|
||||
_glfw.platformWindow.keycodes[0x025] = KeyK
|
||||
_glfw.platformWindow.keycodes[0x026] = KeyL
|
||||
_glfw.platformWindow.keycodes[0x032] = KeyM
|
||||
_glfw.platformWindow.keycodes[0x031] = KeyN
|
||||
_glfw.platformWindow.keycodes[0x018] = KeyO
|
||||
_glfw.platformWindow.keycodes[0x019] = KeyP
|
||||
_glfw.platformWindow.keycodes[0x010] = KeyQ
|
||||
_glfw.platformWindow.keycodes[0x013] = KeyR
|
||||
_glfw.platformWindow.keycodes[0x01F] = KeyS
|
||||
_glfw.platformWindow.keycodes[0x014] = KeyT
|
||||
_glfw.platformWindow.keycodes[0x016] = KeyU
|
||||
_glfw.platformWindow.keycodes[0x02F] = KeyV
|
||||
_glfw.platformWindow.keycodes[0x011] = KeyW
|
||||
_glfw.platformWindow.keycodes[0x02D] = KeyX
|
||||
_glfw.platformWindow.keycodes[0x015] = KeyY
|
||||
_glfw.platformWindow.keycodes[0x02C] = KeyZ
|
||||
|
||||
_glfw.platformWindow.keycodes[0x028] = KeyApostrophe
|
||||
_glfw.platformWindow.keycodes[0x02B] = KeyBackslash
|
||||
_glfw.platformWindow.keycodes[0x033] = KeyComma
|
||||
_glfw.platformWindow.keycodes[0x00D] = KeyEqual
|
||||
_glfw.platformWindow.keycodes[0x029] = KeyGraveAccent
|
||||
_glfw.platformWindow.keycodes[0x01A] = KeyLeftBracket
|
||||
_glfw.platformWindow.keycodes[0x00C] = KeyMinus
|
||||
_glfw.platformWindow.keycodes[0x034] = KeyPeriod
|
||||
_glfw.platformWindow.keycodes[0x01B] = KeyRightBracket
|
||||
_glfw.platformWindow.keycodes[0x027] = KeySemicolon
|
||||
_glfw.platformWindow.keycodes[0x035] = KeySlash
|
||||
_glfw.platformWindow.keycodes[0x056] = KeyWorld2
|
||||
|
||||
_glfw.platformWindow.keycodes[0x00E] = KeyBackspace
|
||||
_glfw.platformWindow.keycodes[0x153] = KeyDelete
|
||||
_glfw.platformWindow.keycodes[0x14F] = KeyEnd
|
||||
_glfw.platformWindow.keycodes[0x01C] = KeyEnter
|
||||
_glfw.platformWindow.keycodes[0x001] = KeyEscape
|
||||
_glfw.platformWindow.keycodes[0x147] = KeyHome
|
||||
_glfw.platformWindow.keycodes[0x152] = KeyInsert
|
||||
_glfw.platformWindow.keycodes[0x15D] = KeyMenu
|
||||
_glfw.platformWindow.keycodes[0x151] = KeyPageDown
|
||||
_glfw.platformWindow.keycodes[0x149] = KeyPageUp
|
||||
_glfw.platformWindow.keycodes[0x045] = KeyPause
|
||||
_glfw.platformWindow.keycodes[0x039] = KeySpace
|
||||
_glfw.platformWindow.keycodes[0x00F] = KeyTab
|
||||
_glfw.platformWindow.keycodes[0x03A] = KeyCapsLock
|
||||
_glfw.platformWindow.keycodes[0x145] = KeyNumLock
|
||||
_glfw.platformWindow.keycodes[0x046] = KeyScrollLock
|
||||
_glfw.platformWindow.keycodes[0x03B] = KeyF1
|
||||
_glfw.platformWindow.keycodes[0x03C] = KeyF2
|
||||
_glfw.platformWindow.keycodes[0x03D] = KeyF3
|
||||
_glfw.platformWindow.keycodes[0x03E] = KeyF4
|
||||
_glfw.platformWindow.keycodes[0x03F] = KeyF5
|
||||
_glfw.platformWindow.keycodes[0x040] = KeyF6
|
||||
_glfw.platformWindow.keycodes[0x041] = KeyF7
|
||||
_glfw.platformWindow.keycodes[0x042] = KeyF8
|
||||
_glfw.platformWindow.keycodes[0x043] = KeyF9
|
||||
_glfw.platformWindow.keycodes[0x044] = KeyF10
|
||||
_glfw.platformWindow.keycodes[0x057] = KeyF11
|
||||
_glfw.platformWindow.keycodes[0x058] = KeyF12
|
||||
_glfw.platformWindow.keycodes[0x064] = KeyF13
|
||||
_glfw.platformWindow.keycodes[0x065] = KeyF14
|
||||
_glfw.platformWindow.keycodes[0x066] = KeyF15
|
||||
_glfw.platformWindow.keycodes[0x067] = KeyF16
|
||||
_glfw.platformWindow.keycodes[0x068] = KeyF17
|
||||
_glfw.platformWindow.keycodes[0x069] = KeyF18
|
||||
_glfw.platformWindow.keycodes[0x06A] = KeyF19
|
||||
_glfw.platformWindow.keycodes[0x06B] = KeyF20
|
||||
_glfw.platformWindow.keycodes[0x06C] = KeyF21
|
||||
_glfw.platformWindow.keycodes[0x06D] = KeyF22
|
||||
_glfw.platformWindow.keycodes[0x06E] = KeyF23
|
||||
_glfw.platformWindow.keycodes[0x076] = KeyF24
|
||||
_glfw.platformWindow.keycodes[0x038] = KeyLeftAlt
|
||||
_glfw.platformWindow.keycodes[0x01D] = KeyLeftControl
|
||||
_glfw.platformWindow.keycodes[0x02A] = KeyLeftShift
|
||||
_glfw.platformWindow.keycodes[0x15B] = KeyLeftSuper
|
||||
_glfw.platformWindow.keycodes[0x137] = KeyPrintScreen
|
||||
_glfw.platformWindow.keycodes[0x138] = KeyRightAlt
|
||||
_glfw.platformWindow.keycodes[0x11D] = KeyRightControl
|
||||
_glfw.platformWindow.keycodes[0x036] = KeyRightShift
|
||||
_glfw.platformWindow.keycodes[0x15C] = KeyRightSuper
|
||||
_glfw.platformWindow.keycodes[0x150] = KeyDown
|
||||
_glfw.platformWindow.keycodes[0x14B] = KeyLeft
|
||||
_glfw.platformWindow.keycodes[0x14D] = KeyRight
|
||||
_glfw.platformWindow.keycodes[0x148] = KeyUp
|
||||
|
||||
_glfw.platformWindow.keycodes[0x052] = KeyKP0
|
||||
_glfw.platformWindow.keycodes[0x04F] = KeyKP1
|
||||
_glfw.platformWindow.keycodes[0x050] = KeyKP2
|
||||
_glfw.platformWindow.keycodes[0x051] = KeyKP3
|
||||
_glfw.platformWindow.keycodes[0x04B] = KeyKP4
|
||||
_glfw.platformWindow.keycodes[0x04C] = KeyKP5
|
||||
_glfw.platformWindow.keycodes[0x04D] = KeyKP6
|
||||
_glfw.platformWindow.keycodes[0x047] = KeyKP7
|
||||
_glfw.platformWindow.keycodes[0x048] = KeyKP8
|
||||
_glfw.platformWindow.keycodes[0x049] = KeyKP9
|
||||
_glfw.platformWindow.keycodes[0x04E] = KeyKPAdd
|
||||
_glfw.platformWindow.keycodes[0x053] = KeyKPDecimal
|
||||
_glfw.platformWindow.keycodes[0x135] = KeyKPDivide
|
||||
_glfw.platformWindow.keycodes[0x11C] = KeyKPEnter
|
||||
_glfw.platformWindow.keycodes[0x059] = KeyKPEqual
|
||||
_glfw.platformWindow.keycodes[0x037] = KeyKPMultiply
|
||||
_glfw.platformWindow.keycodes[0x04A] = KeyKPSubtract
|
||||
|
||||
for scancode := 0; scancode < 512; scancode++ {
|
||||
if _glfw.platformWindow.keycodes[scancode] > 0 {
|
||||
_glfw.platformWindow.scancodes[_glfw.platformWindow.keycodes[scancode]] = scancode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateKeyNamesWin32() {
|
||||
// MapVirtualKeyW is not implemented in Xbox.
|
||||
if microsoftgdk.IsXbox() {
|
||||
return
|
||||
}
|
||||
|
||||
for i := range _glfw.platformWindow.keynames {
|
||||
_glfw.platformWindow.keynames[i] = ""
|
||||
}
|
||||
|
||||
var state [256]byte
|
||||
|
||||
for key := KeySpace; key <= KeyLast; key++ {
|
||||
scancode := _glfw.platformWindow.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[:], chars[:], int32(len(chars)), 0)
|
||||
if length == -1 {
|
||||
// This is a dead key, so we need a second simulated key press
|
||||
// to make it output its own character (usually a diacritic)
|
||||
length = _ToUnicode(vk, uint32(scancode), state[:], chars[:], int32(len(chars)), 0)
|
||||
}
|
||||
if length < 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
_glfw.platformWindow.keynames[key] = windows.UTF16ToString(chars[:length])
|
||||
}
|
||||
}
|
||||
|
||||
func createHelperWindow() error {
|
||||
h, err := _CreateWindowExW(_WS_EX_OVERLAPPEDWINDOW, _GLFW_WNDCLASSNAME, "GLFW message window", _WS_CLIPSIBLINGS|_WS_CLIPCHILDREN, 0, 0, 1, 1, 0, 0, _glfw.platformWindow.instance, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_glfw.platformWindow.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.platformWindow.helperWindowHandle, _SW_HIDE)
|
||||
|
||||
// Register for HID device notifications
|
||||
if !microsoftgdk.IsXbox() {
|
||||
_GUID_DEVINTERFACE_HID := windows.GUID{
|
||||
Data1: 0x4d1e55b2,
|
||||
Data2: 0xf16f,
|
||||
Data3: 0x11cf,
|
||||
Data4: [...]byte{0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30},
|
||||
}
|
||||
|
||||
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.platformWindow.helperWindowHandle), unsafe.Pointer(&dbi), _DEVICE_NOTIFY_WINDOW_HANDLE)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_glfw.platformWindow.deviceNotificationHandle = notify
|
||||
}
|
||||
|
||||
var msg _MSG
|
||||
for _PeekMessageW(&msg, _glfw.platformWindow.helperWindowHandle, 0, 0, _PM_REMOVE) {
|
||||
_TranslateMessage(&msg)
|
||||
_DispatchMessageW(&msg)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func platformInit() error {
|
||||
// Changing the foreground lock timeout was removed from the original code.
|
||||
// See https://github.com/glfw/glfw/commit/58b48a3a00d9c2a5ca10cc23069a71d8773cc7a4
|
||||
|
||||
m, err := _GetModuleHandleExW(_GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS|_GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, unsafe.Pointer(&_glfw))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_glfw.platformWindow.instance = _HINSTANCE(m)
|
||||
|
||||
createKeyTables()
|
||||
updateKeyNamesWin32()
|
||||
|
||||
if winver.IsWindows10CreatorsUpdateOrGreater() {
|
||||
if !microsoftgdk.IsXbox() {
|
||||
// Ignore the error as SetProcessDpiAwarenessContext returns an error on Steam Deck (#2113).
|
||||
// This seems an issue in Wine and/or Proton, but there is nothing we can do.
|
||||
_ = _SetProcessDpiAwarenessContext(_DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)
|
||||
}
|
||||
} else if winver.IsWindows8Point1OrGreater() {
|
||||
if err := _SetProcessDpiAwareness(_PROCESS_PER_MONITOR_DPI_AWARE); err != nil && !errors.Is(err, handleError(windows.E_ACCESSDENIED)) {
|
||||
return err
|
||||
}
|
||||
} else if winver.IsWindowsVistaOrGreater() {
|
||||
_SetProcessDPIAware()
|
||||
}
|
||||
|
||||
if err := registerWindowClassWin32(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := createHelperWindow(); err != nil {
|
||||
return err
|
||||
}
|
||||
if microsoftgdk.IsXbox() {
|
||||
// On Xbox, APIs to get monitors are not available.
|
||||
// Create a pseudo monitor instance instead.
|
||||
w, h := microsoftgdk.MonitorResolution()
|
||||
mode := &VidMode{
|
||||
Width: w,
|
||||
Height: h,
|
||||
RedBits: 8,
|
||||
GreenBits: 8,
|
||||
BlueBits: 8,
|
||||
RefreshRate: 0, // TODO: Is it possible to get an appropriate refresh rate?
|
||||
}
|
||||
m := &Monitor{
|
||||
name: "Xbox Monitor",
|
||||
modes: []*VidMode{mode},
|
||||
}
|
||||
if err := inputMonitor(m, Connected, _GLFW_INSERT_LAST); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := pollMonitorsWin32(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func platformTerminate() error {
|
||||
if _glfw.platformWindow.deviceNotificationHandle != 0 {
|
||||
if err := _UnregisterDeviceNotification(_glfw.platformWindow.deviceNotificationHandle); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if _glfw.platformWindow.helperWindowHandle != 0 {
|
||||
if !microsoftgdk.IsXbox() {
|
||||
// An error 'invalid window handle' can occur without any specific reasons (#2551).
|
||||
// As there is nothing to do, just ignore this error.
|
||||
if err := _DestroyWindow(_glfw.platformWindow.helperWindowHandle); err != nil && !errors.Is(err, windows.ERROR_INVALID_WINDOW_HANDLE) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := unregisterWindowClassWin32(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
terminateWGL()
|
||||
|
||||
return nil
|
||||
}
|
||||
+357
@@ -0,0 +1,357 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2021 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
package glfw
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/microsoftgdk"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/winver"
|
||||
)
|
||||
|
||||
func monitorCallback(handle _HMONITOR, dc _HDC, rect *_RECT, monitor *Monitor /* _LPARAM */) uintptr /* _BOOL */ {
|
||||
if mi, ok := _GetMonitorInfoW_Ex(handle); ok {
|
||||
if windows.UTF16ToString(mi.szDevice[:]) == monitor.platform.adapterName {
|
||||
monitor.platform.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
|
||||
}
|
||||
|
||||
monitor := &Monitor{
|
||||
name: name,
|
||||
}
|
||||
|
||||
if adapter.StateFlags&_DISPLAY_DEVICE_MODESPRUNED != 0 {
|
||||
monitor.platform.modesPruned = true
|
||||
}
|
||||
|
||||
monitor.platform.adapterName = adapterDeviceName
|
||||
if display != nil {
|
||||
monitor.platform.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 {
|
||||
if microsoftgdk.IsXbox() {
|
||||
return nil
|
||||
}
|
||||
|
||||
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.platform.displayName == windows.UTF16ToString(display.DeviceName[:]) {
|
||||
disconnected[i] = nil
|
||||
err := _EnumDisplayMonitors(0, nil, monitorCallbackPtr, _LPARAM(unsafe.Pointer(_glfw.monitors[i])))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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.platform.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.platform.adapterName, &dm, 0, _CDS_FULLSCREEN, nil) {
|
||||
case _DISP_CHANGE_SUCCESSFUL:
|
||||
m.platform.modeChanged = true
|
||||
return nil
|
||||
case _DISP_CHANGE_BADDUALVIEW:
|
||||
return errors.New("glfw: the system uses DualView at Monitor.setVideoModeWin32")
|
||||
case _DISP_CHANGE_BADFLAGS:
|
||||
return errors.New("glfw: invalid flags at Monitor.setVideoModeWin32")
|
||||
case _DISP_CHANGE_BADMODE:
|
||||
return errors.New("glfw: graphics mode not supported at Monitor.setVideoModeWin32")
|
||||
case _DISP_CHANGE_BADPARAM:
|
||||
return errors.New("glfw: invalid parameter at Monitor.setVideoModeWin32")
|
||||
case _DISP_CHANGE_FAILED:
|
||||
return errors.New("glfw: graphics mode failed at Monitor.setVideoModeWin32")
|
||||
case _DISP_CHANGE_NOTUPDATED:
|
||||
return errors.New("glfw: failed to write to registry at Monitor.setVideoModeWin32")
|
||||
case _DISP_CHANGE_RESTART:
|
||||
return errors.New("glfw: computer restart required at Monitor.setVideoModeWin32")
|
||||
default:
|
||||
return errors.New("glfw: unknown error at Monitor.setVideoModeWin32")
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Monitor) restoreVideoModeWin32() {
|
||||
if m.platform.modeChanged {
|
||||
_ChangeDisplaySettingsExW(m.platform.adapterName, nil, 0, _CDS_FULLSCREEN, nil)
|
||||
m.platform.modeChanged = false
|
||||
}
|
||||
}
|
||||
|
||||
func getMonitorContentScaleWin32(handle _HMONITOR) (xscale, yscale float32, err error) {
|
||||
var xdpi, ydpi uint32
|
||||
|
||||
if winver.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) {
|
||||
if microsoftgdk.IsXbox() {
|
||||
return 0, 0, true
|
||||
}
|
||||
|
||||
dm, ok := _EnumDisplaySettingsExW(m.platform.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) {
|
||||
if microsoftgdk.IsXbox() {
|
||||
return 1, 1, nil
|
||||
}
|
||||
|
||||
return getMonitorContentScaleWin32(m.platform.handle)
|
||||
}
|
||||
|
||||
func (m *Monitor) platformGetMonitorWorkarea() (xpos, ypos, width, height int) {
|
||||
if microsoftgdk.IsXbox() {
|
||||
w, h := microsoftgdk.MonitorResolution()
|
||||
return 0, 0, w, h
|
||||
}
|
||||
|
||||
mi, ok := _GetMonitorInfoW(m.platform.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.platform.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.platform.modesPruned {
|
||||
// Skip modes not supported by the connected displays
|
||||
if _ChangeDisplaySettingsExW(m.platform.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 {
|
||||
if microsoftgdk.IsXbox() {
|
||||
return m.modes[0]
|
||||
}
|
||||
|
||||
dm, _ := _EnumDisplaySettingsW(m.platform.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) in32Adapter() (string, error) {
|
||||
if !_glfw.initialized {
|
||||
return "", NotInitialized
|
||||
}
|
||||
return m.platform.adapterName, nil
|
||||
}
|
||||
|
||||
func (m *Monitor) win32Monitor() (string, error) {
|
||||
if !_glfw.initialized {
|
||||
return "", NotInitialized
|
||||
}
|
||||
return m.platform.displayName, nil
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
package glfw
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
const (
|
||||
_GLFW_WNDCLASSNAME = "GLFW30"
|
||||
)
|
||||
|
||||
type platformWindowState 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 received high surrogate when decoding pairs of UTF-16 messages
|
||||
highSurrogate uint16
|
||||
}
|
||||
|
||||
type platformMonitorState struct {
|
||||
handle _HMONITOR
|
||||
|
||||
// This size matches the static size of DISPLAY_DEVICE.DeviceName
|
||||
adapterName string
|
||||
displayName string
|
||||
modesPruned bool
|
||||
modeChanged bool
|
||||
}
|
||||
|
||||
type platformCursorState struct {
|
||||
handle _HCURSOR
|
||||
}
|
||||
|
||||
type platformTLSState struct {
|
||||
allocated bool
|
||||
index uint32
|
||||
}
|
||||
|
||||
type platformLibraryWindowState struct {
|
||||
instance _HINSTANCE
|
||||
helperWindowHandle windows.HWND
|
||||
deviceNotificationHandle _HDEVNOTIFY
|
||||
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
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2017 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
package glfw
|
||||
|
||||
func (t *tls) create() error {
|
||||
if t.platform.allocated {
|
||||
panic("glfw: TLS must not be allocated")
|
||||
}
|
||||
|
||||
i, err := _TlsAlloc()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.platform.index = i
|
||||
t.platform.allocated = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tls) destroy() error {
|
||||
if t.platform.allocated {
|
||||
if err := _TlsFree(t.platform.index); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
t.platform.allocated = false
|
||||
t.platform.index = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tls) get() (uintptr, error) {
|
||||
if !t.platform.allocated {
|
||||
panic("glfw: TLS must be allocated")
|
||||
}
|
||||
|
||||
return _TlsGetValue(t.platform.index)
|
||||
}
|
||||
|
||||
func (t *tls) set(value uintptr) error {
|
||||
if !t.platform.allocated {
|
||||
panic("glfw: TLS must be allocated")
|
||||
}
|
||||
|
||||
return _TlsSetValue(t.platform.index, value)
|
||||
}
|
||||
+2353
File diff suppressed because it is too large
Load Diff
+1090
File diff suppressed because it is too large
Load Diff
+1023
File diff suppressed because it is too large
Load Diff
+884
@@ -0,0 +1,884 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2012 Torsten Walluhn <tw@mad-cad.net>
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
package glfw
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"image/draw"
|
||||
"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("glfw: 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,
|
||||
mousePassthrough: wndconfig.mousePassthrough,
|
||||
cursorMode: CursorNormal,
|
||||
|
||||
doublebuffer: fbconfig.doublebuffer,
|
||||
|
||||
minwidth: DontCare,
|
||||
minheight: DontCare,
|
||||
maxwidth: DontCare,
|
||||
maxheight: DontCare,
|
||||
numer: DontCare,
|
||||
denom: DontCare,
|
||||
}
|
||||
defer func() {
|
||||
if ferr != nil {
|
||||
_ = window.Destroy()
|
||||
}
|
||||
}()
|
||||
_glfw.windows = append(_glfw.windows, window)
|
||||
|
||||
// Open the actual window and create its context
|
||||
if err := window.platformCreateWindow(&wndconfig, &ctxconfig, &fbconfig); 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: NoAPI, // This is different from the original GLFW, which uses OpenGLAPI by default.
|
||||
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 MousePassthrough:
|
||||
_glfw.hints.window.mousePassthrough = 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("glfw: invalid window hint 0x%08X: %w", hint, InvalidEnum)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WindowHintString is not implemented.
|
||||
func WindowHintString(hint Hint, value string) error {
|
||||
// Do nothing.
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
// TODO: Clear 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
|
||||
}
|
||||
}
|
||||
|
||||
for i, window := range _glfw.windows {
|
||||
if window == w {
|
||||
copy(_glfw.windows[i:], _glfw.windows[i+1:])
|
||||
_glfw.windows[len(_glfw.windows)-1] = nil
|
||||
_glfw.windows = _glfw.windows[:len(_glfw.windows)-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err := w.platformDestroyWindow(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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.Image) error {
|
||||
if !_glfw.initialized {
|
||||
return NotInitialized
|
||||
}
|
||||
|
||||
gimgs := make([]*Image, len(images))
|
||||
for i, img := range images {
|
||||
b := img.Bounds()
|
||||
m := image.NewNRGBA(image.Rect(0, 0, b.Dx(), b.Dy()))
|
||||
draw.Draw(m, m.Bounds(), img, b.Min, draw.Src)
|
||||
gimgs[i] = &Image{
|
||||
Width: b.Dx(),
|
||||
Height: b.Dy(),
|
||||
Pixels: m.Pix,
|
||||
}
|
||||
}
|
||||
|
||||
if err := w.platformSetWindowIcon(gimgs); 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("glfw: 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("glfw: 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("glfw: 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("glfw: 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
|
||||
}
|
||||
if err := w.platformMaximizeWindow(); err != nil {
|
||||
return err
|
||||
}
|
||||
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 MousePassthrough:
|
||||
return boolToInt(w.mousePassthrough), 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("glfw: 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
|
||||
case MousePassthrough:
|
||||
w.mousePassthrough = bValue
|
||||
if err := w.platformSetWindowMousePassthrough(bValue); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("glfw: 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("glfw: invalid window size %dx%d: %w", width, height, InvalidValue)
|
||||
}
|
||||
|
||||
if refreshRate < 0 && refreshRate != DontCare {
|
||||
return fmt.Errorf("glfw: 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("glfw: 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
|
||||
}
|
||||
+1284
File diff suppressed because it is too large
Load Diff
+592
@@ -0,0 +1,592 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build freebsd || linux || netbsd || openbsd
|
||||
|
||||
#include "internal_unix.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
|
||||
// Check whether the display mode should be included in enumeration
|
||||
//
|
||||
static GLFWbool modeIsGood(const XRRModeInfo* mi)
|
||||
{
|
||||
return (mi->modeFlags & RR_Interlace) == 0;
|
||||
}
|
||||
|
||||
// Calculates the refresh rate, in Hz, from the specified RandR mode info
|
||||
//
|
||||
static int calculateRefreshRate(const XRRModeInfo* mi)
|
||||
{
|
||||
if (mi->hTotal && mi->vTotal)
|
||||
return (int) round((double) mi->dotClock / ((double) mi->hTotal * (double) mi->vTotal));
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Returns the mode info for a RandR mode XID
|
||||
//
|
||||
static const XRRModeInfo* getModeInfo(const XRRScreenResources* sr, RRMode id)
|
||||
{
|
||||
for (int i = 0; i < sr->nmode; i++)
|
||||
{
|
||||
if (sr->modes[i].id == id)
|
||||
return sr->modes + i;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Convert RandR mode info to GLFW video mode
|
||||
//
|
||||
static GLFWvidmode vidmodeFromModeInfo(const XRRModeInfo* mi,
|
||||
const XRRCrtcInfo* ci)
|
||||
{
|
||||
GLFWvidmode mode;
|
||||
|
||||
if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270)
|
||||
{
|
||||
mode.width = mi->height;
|
||||
mode.height = mi->width;
|
||||
}
|
||||
else
|
||||
{
|
||||
mode.width = mi->width;
|
||||
mode.height = mi->height;
|
||||
}
|
||||
|
||||
mode.refreshRate = calculateRefreshRate(mi);
|
||||
|
||||
_glfwSplitBPP(DefaultDepth(_glfw.x11.display, _glfw.x11.screen),
|
||||
&mode.redBits, &mode.greenBits, &mode.blueBits);
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW internal API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Poll for changes in the set of connected monitors
|
||||
//
|
||||
void _glfwPollMonitorsX11(void)
|
||||
{
|
||||
if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
|
||||
{
|
||||
int disconnectedCount, screenCount = 0;
|
||||
_GLFWmonitor** disconnected = NULL;
|
||||
XineramaScreenInfo* screens = NULL;
|
||||
XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display,
|
||||
_glfw.x11.root);
|
||||
RROutput primary = XRRGetOutputPrimary(_glfw.x11.display,
|
||||
_glfw.x11.root);
|
||||
|
||||
if (_glfw.x11.xinerama.available)
|
||||
screens = XineramaQueryScreens(_glfw.x11.display, &screenCount);
|
||||
|
||||
disconnectedCount = _glfw.monitorCount;
|
||||
if (disconnectedCount)
|
||||
{
|
||||
disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*));
|
||||
memcpy(disconnected,
|
||||
_glfw.monitors,
|
||||
_glfw.monitorCount * sizeof(_GLFWmonitor*));
|
||||
}
|
||||
|
||||
for (int i = 0; i < sr->noutput; i++)
|
||||
{
|
||||
int j, type, widthMM, heightMM;
|
||||
|
||||
XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, sr->outputs[i]);
|
||||
if (oi->connection != RR_Connected || oi->crtc == None)
|
||||
{
|
||||
XRRFreeOutputInfo(oi);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (j = 0; j < disconnectedCount; j++)
|
||||
{
|
||||
if (disconnected[j] &&
|
||||
disconnected[j]->x11.output == sr->outputs[i])
|
||||
{
|
||||
disconnected[j] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (j < disconnectedCount)
|
||||
{
|
||||
XRRFreeOutputInfo(oi);
|
||||
continue;
|
||||
}
|
||||
|
||||
XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, oi->crtc);
|
||||
if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270)
|
||||
{
|
||||
widthMM = oi->mm_height;
|
||||
heightMM = oi->mm_width;
|
||||
}
|
||||
else
|
||||
{
|
||||
widthMM = oi->mm_width;
|
||||
heightMM = oi->mm_height;
|
||||
}
|
||||
|
||||
if (widthMM <= 0 || heightMM <= 0)
|
||||
{
|
||||
// HACK: If RandR does not provide a physical size, assume the
|
||||
// X11 default 96 DPI and calculate from the CRTC viewport
|
||||
// NOTE: These members are affected by rotation, unlike the mode
|
||||
// info and output info members
|
||||
widthMM = (int) (ci->width * 25.4f / 96.f);
|
||||
heightMM = (int) (ci->height * 25.4f / 96.f);
|
||||
}
|
||||
|
||||
_GLFWmonitor* monitor = _glfwAllocMonitor(oi->name, widthMM, heightMM);
|
||||
monitor->x11.output = sr->outputs[i];
|
||||
monitor->x11.crtc = oi->crtc;
|
||||
|
||||
for (j = 0; j < screenCount; j++)
|
||||
{
|
||||
if (screens[j].x_org == ci->x &&
|
||||
screens[j].y_org == ci->y &&
|
||||
screens[j].width == ci->width &&
|
||||
screens[j].height == ci->height)
|
||||
{
|
||||
monitor->x11.index = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (monitor->x11.output == primary)
|
||||
type = _GLFW_INSERT_FIRST;
|
||||
else
|
||||
type = _GLFW_INSERT_LAST;
|
||||
|
||||
_glfwInputMonitor(monitor, GLFW_CONNECTED, type);
|
||||
|
||||
XRRFreeOutputInfo(oi);
|
||||
XRRFreeCrtcInfo(ci);
|
||||
}
|
||||
|
||||
XRRFreeScreenResources(sr);
|
||||
|
||||
if (screens)
|
||||
XFree(screens);
|
||||
|
||||
for (int i = 0; i < disconnectedCount; i++)
|
||||
{
|
||||
if (disconnected[i])
|
||||
_glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0);
|
||||
}
|
||||
|
||||
free(disconnected);
|
||||
}
|
||||
else
|
||||
{
|
||||
const int widthMM = DisplayWidthMM(_glfw.x11.display, _glfw.x11.screen);
|
||||
const int heightMM = DisplayHeightMM(_glfw.x11.display, _glfw.x11.screen);
|
||||
|
||||
_glfwInputMonitor(_glfwAllocMonitor("Display", widthMM, heightMM),
|
||||
GLFW_CONNECTED,
|
||||
_GLFW_INSERT_FIRST);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the current video mode for the specified monitor
|
||||
//
|
||||
void _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired)
|
||||
{
|
||||
if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
|
||||
{
|
||||
GLFWvidmode current;
|
||||
RRMode native = None;
|
||||
|
||||
const GLFWvidmode* best = _glfwChooseVideoMode(monitor, desired);
|
||||
_glfwPlatformGetVideoMode(monitor, ¤t);
|
||||
if (_glfwCompareVideoModes(¤t, best) == 0)
|
||||
return;
|
||||
|
||||
XRRScreenResources* sr =
|
||||
XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
|
||||
XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
|
||||
XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output);
|
||||
|
||||
for (int i = 0; i < oi->nmode; i++)
|
||||
{
|
||||
const XRRModeInfo* mi = getModeInfo(sr, oi->modes[i]);
|
||||
if (!modeIsGood(mi))
|
||||
continue;
|
||||
|
||||
const GLFWvidmode mode = vidmodeFromModeInfo(mi, ci);
|
||||
if (_glfwCompareVideoModes(best, &mode) == 0)
|
||||
{
|
||||
native = mi->id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (native)
|
||||
{
|
||||
if (monitor->x11.oldMode == None)
|
||||
monitor->x11.oldMode = ci->mode;
|
||||
|
||||
XRRSetCrtcConfig(_glfw.x11.display,
|
||||
sr, monitor->x11.crtc,
|
||||
CurrentTime,
|
||||
ci->x, ci->y,
|
||||
native,
|
||||
ci->rotation,
|
||||
ci->outputs,
|
||||
ci->noutput);
|
||||
}
|
||||
|
||||
XRRFreeOutputInfo(oi);
|
||||
XRRFreeCrtcInfo(ci);
|
||||
XRRFreeScreenResources(sr);
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the saved (original) video mode for the specified monitor
|
||||
//
|
||||
void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor)
|
||||
{
|
||||
if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
|
||||
{
|
||||
if (monitor->x11.oldMode == None)
|
||||
return;
|
||||
|
||||
XRRScreenResources* sr =
|
||||
XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
|
||||
XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
|
||||
|
||||
XRRSetCrtcConfig(_glfw.x11.display,
|
||||
sr, monitor->x11.crtc,
|
||||
CurrentTime,
|
||||
ci->x, ci->y,
|
||||
monitor->x11.oldMode,
|
||||
ci->rotation,
|
||||
ci->outputs,
|
||||
ci->noutput);
|
||||
|
||||
XRRFreeCrtcInfo(ci);
|
||||
XRRFreeScreenResources(sr);
|
||||
|
||||
monitor->x11.oldMode = None;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW platform API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
|
||||
{
|
||||
}
|
||||
|
||||
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
|
||||
{
|
||||
if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
|
||||
{
|
||||
XRRScreenResources* sr =
|
||||
XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
|
||||
XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
|
||||
|
||||
if (ci)
|
||||
{
|
||||
if (xpos)
|
||||
*xpos = ci->x;
|
||||
if (ypos)
|
||||
*ypos = ci->y;
|
||||
|
||||
XRRFreeCrtcInfo(ci);
|
||||
}
|
||||
|
||||
XRRFreeScreenResources(sr);
|
||||
}
|
||||
}
|
||||
|
||||
void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
|
||||
float* xscale, float* yscale)
|
||||
{
|
||||
if (xscale)
|
||||
*xscale = _glfw.x11.contentScaleX;
|
||||
if (yscale)
|
||||
*yscale = _glfw.x11.contentScaleY;
|
||||
}
|
||||
|
||||
void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height)
|
||||
{
|
||||
int areaX = 0, areaY = 0, areaWidth = 0, areaHeight = 0;
|
||||
|
||||
if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
|
||||
{
|
||||
XRRScreenResources* sr =
|
||||
XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
|
||||
XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
|
||||
|
||||
areaX = ci->x;
|
||||
areaY = ci->y;
|
||||
|
||||
const XRRModeInfo* mi = getModeInfo(sr, ci->mode);
|
||||
|
||||
if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270)
|
||||
{
|
||||
areaWidth = mi->height;
|
||||
areaHeight = mi->width;
|
||||
}
|
||||
else
|
||||
{
|
||||
areaWidth = mi->width;
|
||||
areaHeight = mi->height;
|
||||
}
|
||||
|
||||
XRRFreeCrtcInfo(ci);
|
||||
XRRFreeScreenResources(sr);
|
||||
}
|
||||
else
|
||||
{
|
||||
areaWidth = DisplayWidth(_glfw.x11.display, _glfw.x11.screen);
|
||||
areaHeight = DisplayHeight(_glfw.x11.display, _glfw.x11.screen);
|
||||
}
|
||||
|
||||
if (_glfw.x11.NET_WORKAREA && _glfw.x11.NET_CURRENT_DESKTOP)
|
||||
{
|
||||
Atom* extents = NULL;
|
||||
Atom* desktop = NULL;
|
||||
const unsigned long extentCount =
|
||||
_glfwGetWindowPropertyX11(_glfw.x11.root,
|
||||
_glfw.x11.NET_WORKAREA,
|
||||
XA_CARDINAL,
|
||||
(unsigned char**) &extents);
|
||||
|
||||
if (_glfwGetWindowPropertyX11(_glfw.x11.root,
|
||||
_glfw.x11.NET_CURRENT_DESKTOP,
|
||||
XA_CARDINAL,
|
||||
(unsigned char**) &desktop) > 0)
|
||||
{
|
||||
if (extentCount >= 4 && *desktop < extentCount / 4)
|
||||
{
|
||||
const int globalX = extents[*desktop * 4 + 0];
|
||||
const int globalY = extents[*desktop * 4 + 1];
|
||||
const int globalWidth = extents[*desktop * 4 + 2];
|
||||
const int globalHeight = extents[*desktop * 4 + 3];
|
||||
|
||||
if (areaX < globalX)
|
||||
{
|
||||
areaWidth -= globalX - areaX;
|
||||
areaX = globalX;
|
||||
}
|
||||
|
||||
if (areaY < globalY)
|
||||
{
|
||||
areaHeight -= globalY - areaY;
|
||||
areaY = globalY;
|
||||
}
|
||||
|
||||
if (areaX + areaWidth > globalX + globalWidth)
|
||||
areaWidth = globalX - areaX + globalWidth;
|
||||
if (areaY + areaHeight > globalY + globalHeight)
|
||||
areaHeight = globalY - areaY + globalHeight;
|
||||
}
|
||||
}
|
||||
|
||||
if (extents)
|
||||
XFree(extents);
|
||||
if (desktop)
|
||||
XFree(desktop);
|
||||
}
|
||||
|
||||
if (xpos)
|
||||
*xpos = areaX;
|
||||
if (ypos)
|
||||
*ypos = areaY;
|
||||
if (width)
|
||||
*width = areaWidth;
|
||||
if (height)
|
||||
*height = areaHeight;
|
||||
}
|
||||
|
||||
GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
|
||||
{
|
||||
GLFWvidmode* result;
|
||||
|
||||
*count = 0;
|
||||
|
||||
if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
|
||||
{
|
||||
XRRScreenResources* sr =
|
||||
XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
|
||||
XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
|
||||
XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output);
|
||||
|
||||
result = calloc(oi->nmode, sizeof(GLFWvidmode));
|
||||
|
||||
for (int i = 0; i < oi->nmode; i++)
|
||||
{
|
||||
const XRRModeInfo* mi = getModeInfo(sr, oi->modes[i]);
|
||||
if (!modeIsGood(mi))
|
||||
continue;
|
||||
|
||||
const GLFWvidmode mode = vidmodeFromModeInfo(mi, ci);
|
||||
int j;
|
||||
|
||||
for (j = 0; j < *count; j++)
|
||||
{
|
||||
if (_glfwCompareVideoModes(result + j, &mode) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip duplicate modes
|
||||
if (j < *count)
|
||||
continue;
|
||||
|
||||
(*count)++;
|
||||
result[*count - 1] = mode;
|
||||
}
|
||||
|
||||
XRRFreeOutputInfo(oi);
|
||||
XRRFreeCrtcInfo(ci);
|
||||
XRRFreeScreenResources(sr);
|
||||
}
|
||||
else
|
||||
{
|
||||
*count = 1;
|
||||
result = calloc(1, sizeof(GLFWvidmode));
|
||||
_glfwPlatformGetVideoMode(monitor, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode)
|
||||
{
|
||||
if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
|
||||
{
|
||||
XRRScreenResources* sr =
|
||||
XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
|
||||
XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
|
||||
|
||||
if (ci)
|
||||
{
|
||||
const XRRModeInfo* mi = getModeInfo(sr, ci->mode);
|
||||
if (mi) // mi can be NULL if the monitor has been disconnected
|
||||
*mode = vidmodeFromModeInfo(mi, ci);
|
||||
|
||||
XRRFreeCrtcInfo(ci);
|
||||
}
|
||||
|
||||
XRRFreeScreenResources(sr);
|
||||
}
|
||||
else
|
||||
{
|
||||
mode->width = DisplayWidth(_glfw.x11.display, _glfw.x11.screen);
|
||||
mode->height = DisplayHeight(_glfw.x11.display, _glfw.x11.screen);
|
||||
mode->refreshRate = 0;
|
||||
|
||||
_glfwSplitBPP(DefaultDepth(_glfw.x11.display, _glfw.x11.screen),
|
||||
&mode->redBits, &mode->greenBits, &mode->blueBits);
|
||||
}
|
||||
}
|
||||
|
||||
GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
|
||||
{
|
||||
if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken)
|
||||
{
|
||||
const size_t size = XRRGetCrtcGammaSize(_glfw.x11.display,
|
||||
monitor->x11.crtc);
|
||||
XRRCrtcGamma* gamma = XRRGetCrtcGamma(_glfw.x11.display,
|
||||
monitor->x11.crtc);
|
||||
|
||||
_glfwAllocGammaArrays(ramp, size);
|
||||
|
||||
memcpy(ramp->red, gamma->red, size * sizeof(unsigned short));
|
||||
memcpy(ramp->green, gamma->green, size * sizeof(unsigned short));
|
||||
memcpy(ramp->blue, gamma->blue, size * sizeof(unsigned short));
|
||||
|
||||
XRRFreeGamma(gamma);
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
else if (_glfw.x11.vidmode.available)
|
||||
{
|
||||
int size;
|
||||
XF86VidModeGetGammaRampSize(_glfw.x11.display, _glfw.x11.screen, &size);
|
||||
|
||||
_glfwAllocGammaArrays(ramp, size);
|
||||
|
||||
XF86VidModeGetGammaRamp(_glfw.x11.display,
|
||||
_glfw.x11.screen,
|
||||
ramp->size, ramp->red, ramp->green, ramp->blue);
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"X11: Gamma ramp access not supported by server");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
|
||||
{
|
||||
if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken)
|
||||
{
|
||||
if (XRRGetCrtcGammaSize(_glfw.x11.display, monitor->x11.crtc) != ramp->size)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"X11: Gamma ramp size must match current ramp size");
|
||||
return;
|
||||
}
|
||||
|
||||
XRRCrtcGamma* gamma = XRRAllocGamma(ramp->size);
|
||||
|
||||
memcpy(gamma->red, ramp->red, ramp->size * sizeof(unsigned short));
|
||||
memcpy(gamma->green, ramp->green, ramp->size * sizeof(unsigned short));
|
||||
memcpy(gamma->blue, ramp->blue, ramp->size * sizeof(unsigned short));
|
||||
|
||||
XRRSetCrtcGamma(_glfw.x11.display, monitor->x11.crtc, gamma);
|
||||
XRRFreeGamma(gamma);
|
||||
}
|
||||
else if (_glfw.x11.vidmode.available)
|
||||
{
|
||||
XF86VidModeSetGammaRamp(_glfw.x11.display,
|
||||
_glfw.x11.screen,
|
||||
ramp->size,
|
||||
(unsigned short*) ramp->red,
|
||||
(unsigned short*) ramp->green,
|
||||
(unsigned short*) ramp->blue);
|
||||
}
|
||||
else
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"X11: Gamma ramp access not supported by server");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW native API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* handle)
|
||||
{
|
||||
_GLFWmonitor* monitor = (_GLFWmonitor*) handle;
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(None);
|
||||
return monitor->x11.crtc;
|
||||
}
|
||||
|
||||
GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* handle)
|
||||
{
|
||||
_GLFWmonitor* monitor = (_GLFWmonitor*) handle;
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(None);
|
||||
return monitor->x11.output;
|
||||
}
|
||||
|
||||
+434
@@ -0,0 +1,434 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build freebsd || linux || netbsd || openbsd
|
||||
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/keysym.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xcursor/Xcursor.h>
|
||||
|
||||
// The XRandR extension provides mode setting and gamma control
|
||||
#include <X11/extensions/Xrandr.h>
|
||||
|
||||
// The Xkb extension provides improved keyboard support
|
||||
#include <X11/XKBlib.h>
|
||||
|
||||
// The Xinerama extension provides legacy monitor indices
|
||||
#include <X11/extensions/Xinerama.h>
|
||||
|
||||
// The XInput extension provides raw mouse motion input
|
||||
#include <X11/extensions/XInput2.h>
|
||||
|
||||
// The Shape extension provides custom window shapes
|
||||
#include <X11/extensions/shape.h>
|
||||
|
||||
typedef XRRCrtcGamma* (* PFN_XRRAllocGamma)(int);
|
||||
typedef void (* PFN_XRRFreeCrtcInfo)(XRRCrtcInfo*);
|
||||
typedef void (* PFN_XRRFreeGamma)(XRRCrtcGamma*);
|
||||
typedef void (* PFN_XRRFreeOutputInfo)(XRROutputInfo*);
|
||||
typedef void (* PFN_XRRFreeScreenResources)(XRRScreenResources*);
|
||||
typedef XRRCrtcGamma* (* PFN_XRRGetCrtcGamma)(Display*,RRCrtc);
|
||||
typedef int (* PFN_XRRGetCrtcGammaSize)(Display*,RRCrtc);
|
||||
typedef XRRCrtcInfo* (* PFN_XRRGetCrtcInfo) (Display*,XRRScreenResources*,RRCrtc);
|
||||
typedef XRROutputInfo* (* PFN_XRRGetOutputInfo)(Display*,XRRScreenResources*,RROutput);
|
||||
typedef RROutput (* PFN_XRRGetOutputPrimary)(Display*,Window);
|
||||
typedef XRRScreenResources* (* PFN_XRRGetScreenResourcesCurrent)(Display*,Window);
|
||||
typedef Bool (* PFN_XRRQueryExtension)(Display*,int*,int*);
|
||||
typedef Status (* PFN_XRRQueryVersion)(Display*,int*,int*);
|
||||
typedef void (* PFN_XRRSelectInput)(Display*,Window,int);
|
||||
typedef Status (* PFN_XRRSetCrtcConfig)(Display*,XRRScreenResources*,RRCrtc,Time,int,int,RRMode,Rotation,RROutput*,int);
|
||||
typedef void (* PFN_XRRSetCrtcGamma)(Display*,RRCrtc,XRRCrtcGamma*);
|
||||
typedef int (* PFN_XRRUpdateConfiguration)(XEvent*);
|
||||
#define XRRAllocGamma _glfw.x11.randr.AllocGamma
|
||||
#define XRRFreeCrtcInfo _glfw.x11.randr.FreeCrtcInfo
|
||||
#define XRRFreeGamma _glfw.x11.randr.FreeGamma
|
||||
#define XRRFreeOutputInfo _glfw.x11.randr.FreeOutputInfo
|
||||
#define XRRFreeScreenResources _glfw.x11.randr.FreeScreenResources
|
||||
#define XRRGetCrtcGamma _glfw.x11.randr.GetCrtcGamma
|
||||
#define XRRGetCrtcGammaSize _glfw.x11.randr.GetCrtcGammaSize
|
||||
#define XRRGetCrtcInfo _glfw.x11.randr.GetCrtcInfo
|
||||
#define XRRGetOutputInfo _glfw.x11.randr.GetOutputInfo
|
||||
#define XRRGetOutputPrimary _glfw.x11.randr.GetOutputPrimary
|
||||
#define XRRGetScreenResourcesCurrent _glfw.x11.randr.GetScreenResourcesCurrent
|
||||
#define XRRQueryExtension _glfw.x11.randr.QueryExtension
|
||||
#define XRRQueryVersion _glfw.x11.randr.QueryVersion
|
||||
#define XRRSelectInput _glfw.x11.randr.SelectInput
|
||||
#define XRRSetCrtcConfig _glfw.x11.randr.SetCrtcConfig
|
||||
#define XRRSetCrtcGamma _glfw.x11.randr.SetCrtcGamma
|
||||
#define XRRUpdateConfiguration _glfw.x11.randr.UpdateConfiguration
|
||||
|
||||
typedef XcursorImage* (* PFN_XcursorImageCreate)(int,int);
|
||||
typedef void (* PFN_XcursorImageDestroy)(XcursorImage*);
|
||||
typedef Cursor (* PFN_XcursorImageLoadCursor)(Display*,const XcursorImage*);
|
||||
typedef char* (* PFN_XcursorGetTheme)(Display*);
|
||||
typedef int (* PFN_XcursorGetDefaultSize)(Display*);
|
||||
typedef XcursorImage* (* PFN_XcursorLibraryLoadImage)(const char*,const char*,int);
|
||||
#define XcursorImageCreate _glfw.x11.xcursor.ImageCreate
|
||||
#define XcursorImageDestroy _glfw.x11.xcursor.ImageDestroy
|
||||
#define XcursorImageLoadCursor _glfw.x11.xcursor.ImageLoadCursor
|
||||
#define XcursorGetTheme _glfw.x11.xcursor.GetTheme
|
||||
#define XcursorGetDefaultSize _glfw.x11.xcursor.GetDefaultSize
|
||||
#define XcursorLibraryLoadImage _glfw.x11.xcursor.LibraryLoadImage
|
||||
|
||||
typedef Bool (* PFN_XineramaIsActive)(Display*);
|
||||
typedef Bool (* PFN_XineramaQueryExtension)(Display*,int*,int*);
|
||||
typedef XineramaScreenInfo* (* PFN_XineramaQueryScreens)(Display*,int*);
|
||||
#define XineramaIsActive _glfw.x11.xinerama.IsActive
|
||||
#define XineramaQueryExtension _glfw.x11.xinerama.QueryExtension
|
||||
#define XineramaQueryScreens _glfw.x11.xinerama.QueryScreens
|
||||
|
||||
typedef XID xcb_window_t;
|
||||
typedef XID xcb_visualid_t;
|
||||
typedef struct xcb_connection_t xcb_connection_t;
|
||||
typedef xcb_connection_t* (* PFN_XGetXCBConnection)(Display*);
|
||||
#define XGetXCBConnection _glfw.x11.x11xcb.GetXCBConnection
|
||||
|
||||
typedef Bool (* PFN_XF86VidModeQueryExtension)(Display*,int*,int*);
|
||||
typedef Bool (* PFN_XF86VidModeGetGammaRamp)(Display*,int,int,unsigned short*,unsigned short*,unsigned short*);
|
||||
typedef Bool (* PFN_XF86VidModeSetGammaRamp)(Display*,int,int,unsigned short*,unsigned short*,unsigned short*);
|
||||
typedef Bool (* PFN_XF86VidModeGetGammaRampSize)(Display*,int,int*);
|
||||
#define XF86VidModeQueryExtension _glfw.x11.vidmode.QueryExtension
|
||||
#define XF86VidModeGetGammaRamp _glfw.x11.vidmode.GetGammaRamp
|
||||
#define XF86VidModeSetGammaRamp _glfw.x11.vidmode.SetGammaRamp
|
||||
#define XF86VidModeGetGammaRampSize _glfw.x11.vidmode.GetGammaRampSize
|
||||
|
||||
typedef Status (* PFN_XIQueryVersion)(Display*,int*,int*);
|
||||
typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int);
|
||||
#define XIQueryVersion _glfw.x11.xi.QueryVersion
|
||||
#define XISelectEvents _glfw.x11.xi.SelectEvents
|
||||
|
||||
typedef Bool (* PFN_XRenderQueryExtension)(Display*,int*,int*);
|
||||
typedef Status (* PFN_XRenderQueryVersion)(Display*dpy,int*,int*);
|
||||
typedef XRenderPictFormat* (* PFN_XRenderFindVisualFormat)(Display*,Visual const*);
|
||||
#define XRenderQueryExtension _glfw.x11.xrender.QueryExtension
|
||||
#define XRenderQueryVersion _glfw.x11.xrender.QueryVersion
|
||||
#define XRenderFindVisualFormat _glfw.x11.xrender.FindVisualFormat
|
||||
|
||||
typedef Bool (* PFN_XShapeQueryExtension)(Display*,int*,int*);
|
||||
typedef Status (* PFN_XShapeQueryVersion)(Display*dpy,int*,int*);
|
||||
typedef void (* PFN_XShapeCombineRegion)(Display*,Window,int,int,int,Region,int);
|
||||
typedef void (* PFN_XShapeCombineMask)(Display*,Window,int,int,int,Pixmap,int);
|
||||
|
||||
#define XShapeQueryExtension _glfw.x11.xshape.QueryExtension
|
||||
#define XShapeQueryVersion _glfw.x11.xshape.QueryVersion
|
||||
#define XShapeCombineRegion _glfw.x11.xshape.ShapeCombineRegion
|
||||
#define XShapeCombineMask _glfw.x11.xshape.ShapeCombineMask
|
||||
|
||||
#include "posix_thread_unix.h"
|
||||
#include "posix_time_linbsd.h"
|
||||
#include "xkb_unicode_linbsd.h"
|
||||
#include "glx_context_linbsd.h"
|
||||
#include "egl_context_unix.h"
|
||||
#include "osmesa_context_unix.h"
|
||||
|
||||
#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL)
|
||||
#define _glfw_dlclose(handle) dlclose(handle)
|
||||
#define _glfw_dlsym(handle, name) dlsym(handle, name)
|
||||
|
||||
#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->x11.handle)
|
||||
#define _GLFW_EGL_NATIVE_DISPLAY ((EGLNativeDisplayType) _glfw.x11.display)
|
||||
|
||||
#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowX11 x11
|
||||
#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryX11 x11
|
||||
#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorX11 x11
|
||||
#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorX11 x11
|
||||
|
||||
|
||||
// X11-specific per-window data
|
||||
//
|
||||
typedef struct _GLFWwindowX11
|
||||
{
|
||||
Colormap colormap;
|
||||
Window handle;
|
||||
Window parent;
|
||||
XIC ic;
|
||||
|
||||
GLFWbool overrideRedirect;
|
||||
GLFWbool iconified;
|
||||
GLFWbool maximized;
|
||||
|
||||
// Whether the visual supports framebuffer transparency
|
||||
GLFWbool transparent;
|
||||
|
||||
// Cached position and size used to filter out duplicate events
|
||||
int width, height;
|
||||
int xpos, ypos;
|
||||
|
||||
// The last received cursor position, regardless of source
|
||||
int lastCursorPosX, lastCursorPosY;
|
||||
// The last position the cursor was warped to by GLFW
|
||||
int warpCursorPosX, warpCursorPosY;
|
||||
|
||||
// The time of the last KeyPress event per keycode, for discarding
|
||||
// duplicate key events generated for some keys by ibus
|
||||
Time keyPressTimes[256];
|
||||
} _GLFWwindowX11;
|
||||
|
||||
// X11-specific global data
|
||||
//
|
||||
typedef struct _GLFWlibraryX11
|
||||
{
|
||||
Display* display;
|
||||
int screen;
|
||||
Window root;
|
||||
|
||||
// System content scale
|
||||
float contentScaleX, contentScaleY;
|
||||
// Helper window for IPC
|
||||
Window helperWindowHandle;
|
||||
// Invisible cursor for hidden cursor mode
|
||||
Cursor hiddenCursorHandle;
|
||||
// Context for mapping window XIDs to _GLFWwindow pointers
|
||||
XContext context;
|
||||
// XIM input method
|
||||
XIM im;
|
||||
// The previous X error handler, to be restored later
|
||||
XErrorHandler errorHandler;
|
||||
// Most recent error code received by X error handler
|
||||
int errorCode;
|
||||
// Primary selection string (while the primary selection is owned)
|
||||
char* primarySelectionString;
|
||||
// Clipboard string (while the selection is owned)
|
||||
char* clipboardString;
|
||||
// Key name string
|
||||
char keynames[GLFW_KEY_LAST + 1][5];
|
||||
// X11 keycode to GLFW key LUT
|
||||
short int keycodes[256];
|
||||
// GLFW key to X11 keycode LUT
|
||||
short int scancodes[GLFW_KEY_LAST + 1];
|
||||
// Where to place the cursor when re-enabled
|
||||
double restoreCursorPosX, restoreCursorPosY;
|
||||
// The window whose disabled cursor mode is active
|
||||
_GLFWwindow* disabledCursorWindow;
|
||||
int emptyEventPipe[2];
|
||||
|
||||
// Window manager atoms
|
||||
Atom NET_SUPPORTED;
|
||||
Atom NET_SUPPORTING_WM_CHECK;
|
||||
Atom WM_PROTOCOLS;
|
||||
Atom WM_STATE;
|
||||
Atom WM_DELETE_WINDOW;
|
||||
Atom NET_WM_NAME;
|
||||
Atom NET_WM_ICON_NAME;
|
||||
Atom NET_WM_ICON;
|
||||
Atom NET_WM_PID;
|
||||
Atom NET_WM_PING;
|
||||
Atom NET_WM_WINDOW_TYPE;
|
||||
Atom NET_WM_WINDOW_TYPE_NORMAL;
|
||||
Atom NET_WM_STATE;
|
||||
Atom NET_WM_STATE_ABOVE;
|
||||
Atom NET_WM_STATE_FULLSCREEN;
|
||||
Atom NET_WM_STATE_MAXIMIZED_VERT;
|
||||
Atom NET_WM_STATE_MAXIMIZED_HORZ;
|
||||
Atom NET_WM_STATE_DEMANDS_ATTENTION;
|
||||
Atom NET_WM_BYPASS_COMPOSITOR;
|
||||
Atom NET_WM_FULLSCREEN_MONITORS;
|
||||
Atom NET_WM_WINDOW_OPACITY;
|
||||
Atom NET_WM_CM_Sx;
|
||||
Atom NET_WORKAREA;
|
||||
Atom NET_CURRENT_DESKTOP;
|
||||
Atom NET_ACTIVE_WINDOW;
|
||||
Atom NET_FRAME_EXTENTS;
|
||||
Atom NET_REQUEST_FRAME_EXTENTS;
|
||||
Atom MOTIF_WM_HINTS;
|
||||
|
||||
// Xdnd (drag and drop) atoms
|
||||
Atom XdndAware;
|
||||
Atom XdndEnter;
|
||||
Atom XdndPosition;
|
||||
Atom XdndStatus;
|
||||
Atom XdndActionCopy;
|
||||
Atom XdndDrop;
|
||||
Atom XdndFinished;
|
||||
Atom XdndSelection;
|
||||
Atom XdndTypeList;
|
||||
Atom text_uri_list;
|
||||
|
||||
// Selection (clipboard) atoms
|
||||
Atom TARGETS;
|
||||
Atom MULTIPLE;
|
||||
Atom INCR;
|
||||
Atom CLIPBOARD;
|
||||
Atom PRIMARY;
|
||||
Atom CLIPBOARD_MANAGER;
|
||||
Atom SAVE_TARGETS;
|
||||
Atom NULL_;
|
||||
Atom UTF8_STRING;
|
||||
Atom COMPOUND_STRING;
|
||||
Atom ATOM_PAIR;
|
||||
Atom GLFW_SELECTION;
|
||||
|
||||
struct {
|
||||
GLFWbool available;
|
||||
void* handle;
|
||||
int eventBase;
|
||||
int errorBase;
|
||||
int major;
|
||||
int minor;
|
||||
GLFWbool gammaBroken;
|
||||
GLFWbool monitorBroken;
|
||||
PFN_XRRAllocGamma AllocGamma;
|
||||
PFN_XRRFreeCrtcInfo FreeCrtcInfo;
|
||||
PFN_XRRFreeGamma FreeGamma;
|
||||
PFN_XRRFreeOutputInfo FreeOutputInfo;
|
||||
PFN_XRRFreeScreenResources FreeScreenResources;
|
||||
PFN_XRRGetCrtcGamma GetCrtcGamma;
|
||||
PFN_XRRGetCrtcGammaSize GetCrtcGammaSize;
|
||||
PFN_XRRGetCrtcInfo GetCrtcInfo;
|
||||
PFN_XRRGetOutputInfo GetOutputInfo;
|
||||
PFN_XRRGetOutputPrimary GetOutputPrimary;
|
||||
PFN_XRRGetScreenResourcesCurrent GetScreenResourcesCurrent;
|
||||
PFN_XRRQueryExtension QueryExtension;
|
||||
PFN_XRRQueryVersion QueryVersion;
|
||||
PFN_XRRSelectInput SelectInput;
|
||||
PFN_XRRSetCrtcConfig SetCrtcConfig;
|
||||
PFN_XRRSetCrtcGamma SetCrtcGamma;
|
||||
PFN_XRRUpdateConfiguration UpdateConfiguration;
|
||||
} randr;
|
||||
|
||||
struct {
|
||||
GLFWbool available;
|
||||
GLFWbool detectable;
|
||||
int majorOpcode;
|
||||
int eventBase;
|
||||
int errorBase;
|
||||
int major;
|
||||
int minor;
|
||||
unsigned int group;
|
||||
} xkb;
|
||||
|
||||
struct {
|
||||
int count;
|
||||
int timeout;
|
||||
int interval;
|
||||
int blanking;
|
||||
int exposure;
|
||||
} saver;
|
||||
|
||||
struct {
|
||||
int version;
|
||||
Window source;
|
||||
Atom format;
|
||||
} xdnd;
|
||||
|
||||
struct {
|
||||
void* handle;
|
||||
PFN_XcursorImageCreate ImageCreate;
|
||||
PFN_XcursorImageDestroy ImageDestroy;
|
||||
PFN_XcursorImageLoadCursor ImageLoadCursor;
|
||||
PFN_XcursorGetTheme GetTheme;
|
||||
PFN_XcursorGetDefaultSize GetDefaultSize;
|
||||
PFN_XcursorLibraryLoadImage LibraryLoadImage;
|
||||
} xcursor;
|
||||
|
||||
struct {
|
||||
GLFWbool available;
|
||||
void* handle;
|
||||
int major;
|
||||
int minor;
|
||||
PFN_XineramaIsActive IsActive;
|
||||
PFN_XineramaQueryExtension QueryExtension;
|
||||
PFN_XineramaQueryScreens QueryScreens;
|
||||
} xinerama;
|
||||
|
||||
struct {
|
||||
void* handle;
|
||||
PFN_XGetXCBConnection GetXCBConnection;
|
||||
} x11xcb;
|
||||
|
||||
struct {
|
||||
GLFWbool available;
|
||||
void* handle;
|
||||
int eventBase;
|
||||
int errorBase;
|
||||
PFN_XF86VidModeQueryExtension QueryExtension;
|
||||
PFN_XF86VidModeGetGammaRamp GetGammaRamp;
|
||||
PFN_XF86VidModeSetGammaRamp SetGammaRamp;
|
||||
PFN_XF86VidModeGetGammaRampSize GetGammaRampSize;
|
||||
} vidmode;
|
||||
|
||||
struct {
|
||||
GLFWbool available;
|
||||
void* handle;
|
||||
int majorOpcode;
|
||||
int eventBase;
|
||||
int errorBase;
|
||||
int major;
|
||||
int minor;
|
||||
PFN_XIQueryVersion QueryVersion;
|
||||
PFN_XISelectEvents SelectEvents;
|
||||
} xi;
|
||||
|
||||
struct {
|
||||
GLFWbool available;
|
||||
void* handle;
|
||||
int major;
|
||||
int minor;
|
||||
int eventBase;
|
||||
int errorBase;
|
||||
PFN_XRenderQueryExtension QueryExtension;
|
||||
PFN_XRenderQueryVersion QueryVersion;
|
||||
PFN_XRenderFindVisualFormat FindVisualFormat;
|
||||
} xrender;
|
||||
|
||||
struct {
|
||||
GLFWbool available;
|
||||
void* handle;
|
||||
int major;
|
||||
int minor;
|
||||
int eventBase;
|
||||
int errorBase;
|
||||
PFN_XShapeQueryExtension QueryExtension;
|
||||
PFN_XShapeCombineRegion ShapeCombineRegion;
|
||||
PFN_XShapeQueryVersion QueryVersion;
|
||||
PFN_XShapeCombineMask ShapeCombineMask;
|
||||
} xshape;
|
||||
} _GLFWlibraryX11;
|
||||
|
||||
// X11-specific per-monitor data
|
||||
//
|
||||
typedef struct _GLFWmonitorX11
|
||||
{
|
||||
RROutput output;
|
||||
RRCrtc crtc;
|
||||
RRMode oldMode;
|
||||
|
||||
// Index of corresponding Xinerama screen,
|
||||
// for EWMH full screen window placement
|
||||
int index;
|
||||
} _GLFWmonitorX11;
|
||||
|
||||
// X11-specific per-cursor data
|
||||
//
|
||||
typedef struct _GLFWcursorX11
|
||||
{
|
||||
Cursor handle;
|
||||
} _GLFWcursorX11;
|
||||
|
||||
|
||||
void _glfwPollMonitorsX11(void);
|
||||
void _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired);
|
||||
void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor);
|
||||
|
||||
Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot);
|
||||
|
||||
unsigned long _glfwGetWindowPropertyX11(Window window,
|
||||
Atom property,
|
||||
Atom type,
|
||||
unsigned char** value);
|
||||
GLFWbool _glfwIsVisualTransparentX11(Visual* visual);
|
||||
|
||||
void _glfwGrabErrorHandlerX11(void);
|
||||
void _glfwReleaseErrorHandlerX11(void);
|
||||
void _glfwInputErrorX11(int error, const char* message);
|
||||
|
||||
void _glfwPushSelectionToManagerX11(void);
|
||||
|
||||
+3134
File diff suppressed because it is too large
Load Diff
+920
@@ -0,0 +1,920 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
||||
// SPDX-FileCopyrightText: 2006-2017 Camilla Löwy <elmindreda@glfw.org>
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build freebsd || linux || netbsd || openbsd
|
||||
|
||||
#include "internal_unix.h"
|
||||
|
||||
|
||||
/*
|
||||
* Marcus: This code was originally written by Markus G. Kuhn.
|
||||
* I have made some slight changes (trimmed it down a bit from >60 KB to
|
||||
* 20 KB), but the functionality is the same.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This module converts keysym values into the corresponding ISO 10646
|
||||
* (UCS, Unicode) values.
|
||||
*
|
||||
* The array keysymtab[] contains pairs of X11 keysym values for graphical
|
||||
* characters and the corresponding Unicode value. The function
|
||||
* _glfwKeySym2Unicode() maps a keysym onto a Unicode value using a binary
|
||||
* search, therefore keysymtab[] must remain SORTED by keysym value.
|
||||
*
|
||||
* We allow to represent any UCS character in the range U-00000000 to
|
||||
* U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff.
|
||||
* This admittedly does not cover the entire 31-bit space of UCS, but
|
||||
* it does cover all of the characters up to U-10FFFF, which can be
|
||||
* represented by UTF-16, and more, and it is very unlikely that higher
|
||||
* UCS codes will ever be assigned by ISO. So to get Unicode character
|
||||
* U+ABCD you can directly use keysym 0x0100abcd.
|
||||
*
|
||||
* Original author: Markus G. Kuhn <mkuhn@acm.org>, University of
|
||||
* Cambridge, April 2001
|
||||
*
|
||||
* Special thanks to Richard Verhoeven <river@win.tue.nl> for preparing
|
||||
* an initial draft of the mapping table.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
//************************************************************************
|
||||
//**** KeySym to Unicode mapping table ****
|
||||
//************************************************************************
|
||||
|
||||
static const struct codepair {
|
||||
unsigned short keysym;
|
||||
unsigned short ucs;
|
||||
} keysymtab[] = {
|
||||
{ 0x01a1, 0x0104 },
|
||||
{ 0x01a2, 0x02d8 },
|
||||
{ 0x01a3, 0x0141 },
|
||||
{ 0x01a5, 0x013d },
|
||||
{ 0x01a6, 0x015a },
|
||||
{ 0x01a9, 0x0160 },
|
||||
{ 0x01aa, 0x015e },
|
||||
{ 0x01ab, 0x0164 },
|
||||
{ 0x01ac, 0x0179 },
|
||||
{ 0x01ae, 0x017d },
|
||||
{ 0x01af, 0x017b },
|
||||
{ 0x01b1, 0x0105 },
|
||||
{ 0x01b2, 0x02db },
|
||||
{ 0x01b3, 0x0142 },
|
||||
{ 0x01b5, 0x013e },
|
||||
{ 0x01b6, 0x015b },
|
||||
{ 0x01b7, 0x02c7 },
|
||||
{ 0x01b9, 0x0161 },
|
||||
{ 0x01ba, 0x015f },
|
||||
{ 0x01bb, 0x0165 },
|
||||
{ 0x01bc, 0x017a },
|
||||
{ 0x01bd, 0x02dd },
|
||||
{ 0x01be, 0x017e },
|
||||
{ 0x01bf, 0x017c },
|
||||
{ 0x01c0, 0x0154 },
|
||||
{ 0x01c3, 0x0102 },
|
||||
{ 0x01c5, 0x0139 },
|
||||
{ 0x01c6, 0x0106 },
|
||||
{ 0x01c8, 0x010c },
|
||||
{ 0x01ca, 0x0118 },
|
||||
{ 0x01cc, 0x011a },
|
||||
{ 0x01cf, 0x010e },
|
||||
{ 0x01d0, 0x0110 },
|
||||
{ 0x01d1, 0x0143 },
|
||||
{ 0x01d2, 0x0147 },
|
||||
{ 0x01d5, 0x0150 },
|
||||
{ 0x01d8, 0x0158 },
|
||||
{ 0x01d9, 0x016e },
|
||||
{ 0x01db, 0x0170 },
|
||||
{ 0x01de, 0x0162 },
|
||||
{ 0x01e0, 0x0155 },
|
||||
{ 0x01e3, 0x0103 },
|
||||
{ 0x01e5, 0x013a },
|
||||
{ 0x01e6, 0x0107 },
|
||||
{ 0x01e8, 0x010d },
|
||||
{ 0x01ea, 0x0119 },
|
||||
{ 0x01ec, 0x011b },
|
||||
{ 0x01ef, 0x010f },
|
||||
{ 0x01f0, 0x0111 },
|
||||
{ 0x01f1, 0x0144 },
|
||||
{ 0x01f2, 0x0148 },
|
||||
{ 0x01f5, 0x0151 },
|
||||
{ 0x01f8, 0x0159 },
|
||||
{ 0x01f9, 0x016f },
|
||||
{ 0x01fb, 0x0171 },
|
||||
{ 0x01fe, 0x0163 },
|
||||
{ 0x01ff, 0x02d9 },
|
||||
{ 0x02a1, 0x0126 },
|
||||
{ 0x02a6, 0x0124 },
|
||||
{ 0x02a9, 0x0130 },
|
||||
{ 0x02ab, 0x011e },
|
||||
{ 0x02ac, 0x0134 },
|
||||
{ 0x02b1, 0x0127 },
|
||||
{ 0x02b6, 0x0125 },
|
||||
{ 0x02b9, 0x0131 },
|
||||
{ 0x02bb, 0x011f },
|
||||
{ 0x02bc, 0x0135 },
|
||||
{ 0x02c5, 0x010a },
|
||||
{ 0x02c6, 0x0108 },
|
||||
{ 0x02d5, 0x0120 },
|
||||
{ 0x02d8, 0x011c },
|
||||
{ 0x02dd, 0x016c },
|
||||
{ 0x02de, 0x015c },
|
||||
{ 0x02e5, 0x010b },
|
||||
{ 0x02e6, 0x0109 },
|
||||
{ 0x02f5, 0x0121 },
|
||||
{ 0x02f8, 0x011d },
|
||||
{ 0x02fd, 0x016d },
|
||||
{ 0x02fe, 0x015d },
|
||||
{ 0x03a2, 0x0138 },
|
||||
{ 0x03a3, 0x0156 },
|
||||
{ 0x03a5, 0x0128 },
|
||||
{ 0x03a6, 0x013b },
|
||||
{ 0x03aa, 0x0112 },
|
||||
{ 0x03ab, 0x0122 },
|
||||
{ 0x03ac, 0x0166 },
|
||||
{ 0x03b3, 0x0157 },
|
||||
{ 0x03b5, 0x0129 },
|
||||
{ 0x03b6, 0x013c },
|
||||
{ 0x03ba, 0x0113 },
|
||||
{ 0x03bb, 0x0123 },
|
||||
{ 0x03bc, 0x0167 },
|
||||
{ 0x03bd, 0x014a },
|
||||
{ 0x03bf, 0x014b },
|
||||
{ 0x03c0, 0x0100 },
|
||||
{ 0x03c7, 0x012e },
|
||||
{ 0x03cc, 0x0116 },
|
||||
{ 0x03cf, 0x012a },
|
||||
{ 0x03d1, 0x0145 },
|
||||
{ 0x03d2, 0x014c },
|
||||
{ 0x03d3, 0x0136 },
|
||||
{ 0x03d9, 0x0172 },
|
||||
{ 0x03dd, 0x0168 },
|
||||
{ 0x03de, 0x016a },
|
||||
{ 0x03e0, 0x0101 },
|
||||
{ 0x03e7, 0x012f },
|
||||
{ 0x03ec, 0x0117 },
|
||||
{ 0x03ef, 0x012b },
|
||||
{ 0x03f1, 0x0146 },
|
||||
{ 0x03f2, 0x014d },
|
||||
{ 0x03f3, 0x0137 },
|
||||
{ 0x03f9, 0x0173 },
|
||||
{ 0x03fd, 0x0169 },
|
||||
{ 0x03fe, 0x016b },
|
||||
{ 0x047e, 0x203e },
|
||||
{ 0x04a1, 0x3002 },
|
||||
{ 0x04a2, 0x300c },
|
||||
{ 0x04a3, 0x300d },
|
||||
{ 0x04a4, 0x3001 },
|
||||
{ 0x04a5, 0x30fb },
|
||||
{ 0x04a6, 0x30f2 },
|
||||
{ 0x04a7, 0x30a1 },
|
||||
{ 0x04a8, 0x30a3 },
|
||||
{ 0x04a9, 0x30a5 },
|
||||
{ 0x04aa, 0x30a7 },
|
||||
{ 0x04ab, 0x30a9 },
|
||||
{ 0x04ac, 0x30e3 },
|
||||
{ 0x04ad, 0x30e5 },
|
||||
{ 0x04ae, 0x30e7 },
|
||||
{ 0x04af, 0x30c3 },
|
||||
{ 0x04b0, 0x30fc },
|
||||
{ 0x04b1, 0x30a2 },
|
||||
{ 0x04b2, 0x30a4 },
|
||||
{ 0x04b3, 0x30a6 },
|
||||
{ 0x04b4, 0x30a8 },
|
||||
{ 0x04b5, 0x30aa },
|
||||
{ 0x04b6, 0x30ab },
|
||||
{ 0x04b7, 0x30ad },
|
||||
{ 0x04b8, 0x30af },
|
||||
{ 0x04b9, 0x30b1 },
|
||||
{ 0x04ba, 0x30b3 },
|
||||
{ 0x04bb, 0x30b5 },
|
||||
{ 0x04bc, 0x30b7 },
|
||||
{ 0x04bd, 0x30b9 },
|
||||
{ 0x04be, 0x30bb },
|
||||
{ 0x04bf, 0x30bd },
|
||||
{ 0x04c0, 0x30bf },
|
||||
{ 0x04c1, 0x30c1 },
|
||||
{ 0x04c2, 0x30c4 },
|
||||
{ 0x04c3, 0x30c6 },
|
||||
{ 0x04c4, 0x30c8 },
|
||||
{ 0x04c5, 0x30ca },
|
||||
{ 0x04c6, 0x30cb },
|
||||
{ 0x04c7, 0x30cc },
|
||||
{ 0x04c8, 0x30cd },
|
||||
{ 0x04c9, 0x30ce },
|
||||
{ 0x04ca, 0x30cf },
|
||||
{ 0x04cb, 0x30d2 },
|
||||
{ 0x04cc, 0x30d5 },
|
||||
{ 0x04cd, 0x30d8 },
|
||||
{ 0x04ce, 0x30db },
|
||||
{ 0x04cf, 0x30de },
|
||||
{ 0x04d0, 0x30df },
|
||||
{ 0x04d1, 0x30e0 },
|
||||
{ 0x04d2, 0x30e1 },
|
||||
{ 0x04d3, 0x30e2 },
|
||||
{ 0x04d4, 0x30e4 },
|
||||
{ 0x04d5, 0x30e6 },
|
||||
{ 0x04d6, 0x30e8 },
|
||||
{ 0x04d7, 0x30e9 },
|
||||
{ 0x04d8, 0x30ea },
|
||||
{ 0x04d9, 0x30eb },
|
||||
{ 0x04da, 0x30ec },
|
||||
{ 0x04db, 0x30ed },
|
||||
{ 0x04dc, 0x30ef },
|
||||
{ 0x04dd, 0x30f3 },
|
||||
{ 0x04de, 0x309b },
|
||||
{ 0x04df, 0x309c },
|
||||
{ 0x05ac, 0x060c },
|
||||
{ 0x05bb, 0x061b },
|
||||
{ 0x05bf, 0x061f },
|
||||
{ 0x05c1, 0x0621 },
|
||||
{ 0x05c2, 0x0622 },
|
||||
{ 0x05c3, 0x0623 },
|
||||
{ 0x05c4, 0x0624 },
|
||||
{ 0x05c5, 0x0625 },
|
||||
{ 0x05c6, 0x0626 },
|
||||
{ 0x05c7, 0x0627 },
|
||||
{ 0x05c8, 0x0628 },
|
||||
{ 0x05c9, 0x0629 },
|
||||
{ 0x05ca, 0x062a },
|
||||
{ 0x05cb, 0x062b },
|
||||
{ 0x05cc, 0x062c },
|
||||
{ 0x05cd, 0x062d },
|
||||
{ 0x05ce, 0x062e },
|
||||
{ 0x05cf, 0x062f },
|
||||
{ 0x05d0, 0x0630 },
|
||||
{ 0x05d1, 0x0631 },
|
||||
{ 0x05d2, 0x0632 },
|
||||
{ 0x05d3, 0x0633 },
|
||||
{ 0x05d4, 0x0634 },
|
||||
{ 0x05d5, 0x0635 },
|
||||
{ 0x05d6, 0x0636 },
|
||||
{ 0x05d7, 0x0637 },
|
||||
{ 0x05d8, 0x0638 },
|
||||
{ 0x05d9, 0x0639 },
|
||||
{ 0x05da, 0x063a },
|
||||
{ 0x05e0, 0x0640 },
|
||||
{ 0x05e1, 0x0641 },
|
||||
{ 0x05e2, 0x0642 },
|
||||
{ 0x05e3, 0x0643 },
|
||||
{ 0x05e4, 0x0644 },
|
||||
{ 0x05e5, 0x0645 },
|
||||
{ 0x05e6, 0x0646 },
|
||||
{ 0x05e7, 0x0647 },
|
||||
{ 0x05e8, 0x0648 },
|
||||
{ 0x05e9, 0x0649 },
|
||||
{ 0x05ea, 0x064a },
|
||||
{ 0x05eb, 0x064b },
|
||||
{ 0x05ec, 0x064c },
|
||||
{ 0x05ed, 0x064d },
|
||||
{ 0x05ee, 0x064e },
|
||||
{ 0x05ef, 0x064f },
|
||||
{ 0x05f0, 0x0650 },
|
||||
{ 0x05f1, 0x0651 },
|
||||
{ 0x05f2, 0x0652 },
|
||||
{ 0x06a1, 0x0452 },
|
||||
{ 0x06a2, 0x0453 },
|
||||
{ 0x06a3, 0x0451 },
|
||||
{ 0x06a4, 0x0454 },
|
||||
{ 0x06a5, 0x0455 },
|
||||
{ 0x06a6, 0x0456 },
|
||||
{ 0x06a7, 0x0457 },
|
||||
{ 0x06a8, 0x0458 },
|
||||
{ 0x06a9, 0x0459 },
|
||||
{ 0x06aa, 0x045a },
|
||||
{ 0x06ab, 0x045b },
|
||||
{ 0x06ac, 0x045c },
|
||||
{ 0x06ae, 0x045e },
|
||||
{ 0x06af, 0x045f },
|
||||
{ 0x06b0, 0x2116 },
|
||||
{ 0x06b1, 0x0402 },
|
||||
{ 0x06b2, 0x0403 },
|
||||
{ 0x06b3, 0x0401 },
|
||||
{ 0x06b4, 0x0404 },
|
||||
{ 0x06b5, 0x0405 },
|
||||
{ 0x06b6, 0x0406 },
|
||||
{ 0x06b7, 0x0407 },
|
||||
{ 0x06b8, 0x0408 },
|
||||
{ 0x06b9, 0x0409 },
|
||||
{ 0x06ba, 0x040a },
|
||||
{ 0x06bb, 0x040b },
|
||||
{ 0x06bc, 0x040c },
|
||||
{ 0x06be, 0x040e },
|
||||
{ 0x06bf, 0x040f },
|
||||
{ 0x06c0, 0x044e },
|
||||
{ 0x06c1, 0x0430 },
|
||||
{ 0x06c2, 0x0431 },
|
||||
{ 0x06c3, 0x0446 },
|
||||
{ 0x06c4, 0x0434 },
|
||||
{ 0x06c5, 0x0435 },
|
||||
{ 0x06c6, 0x0444 },
|
||||
{ 0x06c7, 0x0433 },
|
||||
{ 0x06c8, 0x0445 },
|
||||
{ 0x06c9, 0x0438 },
|
||||
{ 0x06ca, 0x0439 },
|
||||
{ 0x06cb, 0x043a },
|
||||
{ 0x06cc, 0x043b },
|
||||
{ 0x06cd, 0x043c },
|
||||
{ 0x06ce, 0x043d },
|
||||
{ 0x06cf, 0x043e },
|
||||
{ 0x06d0, 0x043f },
|
||||
{ 0x06d1, 0x044f },
|
||||
{ 0x06d2, 0x0440 },
|
||||
{ 0x06d3, 0x0441 },
|
||||
{ 0x06d4, 0x0442 },
|
||||
{ 0x06d5, 0x0443 },
|
||||
{ 0x06d6, 0x0436 },
|
||||
{ 0x06d7, 0x0432 },
|
||||
{ 0x06d8, 0x044c },
|
||||
{ 0x06d9, 0x044b },
|
||||
{ 0x06da, 0x0437 },
|
||||
{ 0x06db, 0x0448 },
|
||||
{ 0x06dc, 0x044d },
|
||||
{ 0x06dd, 0x0449 },
|
||||
{ 0x06de, 0x0447 },
|
||||
{ 0x06df, 0x044a },
|
||||
{ 0x06e0, 0x042e },
|
||||
{ 0x06e1, 0x0410 },
|
||||
{ 0x06e2, 0x0411 },
|
||||
{ 0x06e3, 0x0426 },
|
||||
{ 0x06e4, 0x0414 },
|
||||
{ 0x06e5, 0x0415 },
|
||||
{ 0x06e6, 0x0424 },
|
||||
{ 0x06e7, 0x0413 },
|
||||
{ 0x06e8, 0x0425 },
|
||||
{ 0x06e9, 0x0418 },
|
||||
{ 0x06ea, 0x0419 },
|
||||
{ 0x06eb, 0x041a },
|
||||
{ 0x06ec, 0x041b },
|
||||
{ 0x06ed, 0x041c },
|
||||
{ 0x06ee, 0x041d },
|
||||
{ 0x06ef, 0x041e },
|
||||
{ 0x06f0, 0x041f },
|
||||
{ 0x06f1, 0x042f },
|
||||
{ 0x06f2, 0x0420 },
|
||||
{ 0x06f3, 0x0421 },
|
||||
{ 0x06f4, 0x0422 },
|
||||
{ 0x06f5, 0x0423 },
|
||||
{ 0x06f6, 0x0416 },
|
||||
{ 0x06f7, 0x0412 },
|
||||
{ 0x06f8, 0x042c },
|
||||
{ 0x06f9, 0x042b },
|
||||
{ 0x06fa, 0x0417 },
|
||||
{ 0x06fb, 0x0428 },
|
||||
{ 0x06fc, 0x042d },
|
||||
{ 0x06fd, 0x0429 },
|
||||
{ 0x06fe, 0x0427 },
|
||||
{ 0x06ff, 0x042a },
|
||||
{ 0x07a1, 0x0386 },
|
||||
{ 0x07a2, 0x0388 },
|
||||
{ 0x07a3, 0x0389 },
|
||||
{ 0x07a4, 0x038a },
|
||||
{ 0x07a5, 0x03aa },
|
||||
{ 0x07a7, 0x038c },
|
||||
{ 0x07a8, 0x038e },
|
||||
{ 0x07a9, 0x03ab },
|
||||
{ 0x07ab, 0x038f },
|
||||
{ 0x07ae, 0x0385 },
|
||||
{ 0x07af, 0x2015 },
|
||||
{ 0x07b1, 0x03ac },
|
||||
{ 0x07b2, 0x03ad },
|
||||
{ 0x07b3, 0x03ae },
|
||||
{ 0x07b4, 0x03af },
|
||||
{ 0x07b5, 0x03ca },
|
||||
{ 0x07b6, 0x0390 },
|
||||
{ 0x07b7, 0x03cc },
|
||||
{ 0x07b8, 0x03cd },
|
||||
{ 0x07b9, 0x03cb },
|
||||
{ 0x07ba, 0x03b0 },
|
||||
{ 0x07bb, 0x03ce },
|
||||
{ 0x07c1, 0x0391 },
|
||||
{ 0x07c2, 0x0392 },
|
||||
{ 0x07c3, 0x0393 },
|
||||
{ 0x07c4, 0x0394 },
|
||||
{ 0x07c5, 0x0395 },
|
||||
{ 0x07c6, 0x0396 },
|
||||
{ 0x07c7, 0x0397 },
|
||||
{ 0x07c8, 0x0398 },
|
||||
{ 0x07c9, 0x0399 },
|
||||
{ 0x07ca, 0x039a },
|
||||
{ 0x07cb, 0x039b },
|
||||
{ 0x07cc, 0x039c },
|
||||
{ 0x07cd, 0x039d },
|
||||
{ 0x07ce, 0x039e },
|
||||
{ 0x07cf, 0x039f },
|
||||
{ 0x07d0, 0x03a0 },
|
||||
{ 0x07d1, 0x03a1 },
|
||||
{ 0x07d2, 0x03a3 },
|
||||
{ 0x07d4, 0x03a4 },
|
||||
{ 0x07d5, 0x03a5 },
|
||||
{ 0x07d6, 0x03a6 },
|
||||
{ 0x07d7, 0x03a7 },
|
||||
{ 0x07d8, 0x03a8 },
|
||||
{ 0x07d9, 0x03a9 },
|
||||
{ 0x07e1, 0x03b1 },
|
||||
{ 0x07e2, 0x03b2 },
|
||||
{ 0x07e3, 0x03b3 },
|
||||
{ 0x07e4, 0x03b4 },
|
||||
{ 0x07e5, 0x03b5 },
|
||||
{ 0x07e6, 0x03b6 },
|
||||
{ 0x07e7, 0x03b7 },
|
||||
{ 0x07e8, 0x03b8 },
|
||||
{ 0x07e9, 0x03b9 },
|
||||
{ 0x07ea, 0x03ba },
|
||||
{ 0x07eb, 0x03bb },
|
||||
{ 0x07ec, 0x03bc },
|
||||
{ 0x07ed, 0x03bd },
|
||||
{ 0x07ee, 0x03be },
|
||||
{ 0x07ef, 0x03bf },
|
||||
{ 0x07f0, 0x03c0 },
|
||||
{ 0x07f1, 0x03c1 },
|
||||
{ 0x07f2, 0x03c3 },
|
||||
{ 0x07f3, 0x03c2 },
|
||||
{ 0x07f4, 0x03c4 },
|
||||
{ 0x07f5, 0x03c5 },
|
||||
{ 0x07f6, 0x03c6 },
|
||||
{ 0x07f7, 0x03c7 },
|
||||
{ 0x07f8, 0x03c8 },
|
||||
{ 0x07f9, 0x03c9 },
|
||||
{ 0x08a1, 0x23b7 },
|
||||
{ 0x08a2, 0x250c },
|
||||
{ 0x08a3, 0x2500 },
|
||||
{ 0x08a4, 0x2320 },
|
||||
{ 0x08a5, 0x2321 },
|
||||
{ 0x08a6, 0x2502 },
|
||||
{ 0x08a7, 0x23a1 },
|
||||
{ 0x08a8, 0x23a3 },
|
||||
{ 0x08a9, 0x23a4 },
|
||||
{ 0x08aa, 0x23a6 },
|
||||
{ 0x08ab, 0x239b },
|
||||
{ 0x08ac, 0x239d },
|
||||
{ 0x08ad, 0x239e },
|
||||
{ 0x08ae, 0x23a0 },
|
||||
{ 0x08af, 0x23a8 },
|
||||
{ 0x08b0, 0x23ac },
|
||||
{ 0x08bc, 0x2264 },
|
||||
{ 0x08bd, 0x2260 },
|
||||
{ 0x08be, 0x2265 },
|
||||
{ 0x08bf, 0x222b },
|
||||
{ 0x08c0, 0x2234 },
|
||||
{ 0x08c1, 0x221d },
|
||||
{ 0x08c2, 0x221e },
|
||||
{ 0x08c5, 0x2207 },
|
||||
{ 0x08c8, 0x223c },
|
||||
{ 0x08c9, 0x2243 },
|
||||
{ 0x08cd, 0x21d4 },
|
||||
{ 0x08ce, 0x21d2 },
|
||||
{ 0x08cf, 0x2261 },
|
||||
{ 0x08d6, 0x221a },
|
||||
{ 0x08da, 0x2282 },
|
||||
{ 0x08db, 0x2283 },
|
||||
{ 0x08dc, 0x2229 },
|
||||
{ 0x08dd, 0x222a },
|
||||
{ 0x08de, 0x2227 },
|
||||
{ 0x08df, 0x2228 },
|
||||
{ 0x08ef, 0x2202 },
|
||||
{ 0x08f6, 0x0192 },
|
||||
{ 0x08fb, 0x2190 },
|
||||
{ 0x08fc, 0x2191 },
|
||||
{ 0x08fd, 0x2192 },
|
||||
{ 0x08fe, 0x2193 },
|
||||
{ 0x09e0, 0x25c6 },
|
||||
{ 0x09e1, 0x2592 },
|
||||
{ 0x09e2, 0x2409 },
|
||||
{ 0x09e3, 0x240c },
|
||||
{ 0x09e4, 0x240d },
|
||||
{ 0x09e5, 0x240a },
|
||||
{ 0x09e8, 0x2424 },
|
||||
{ 0x09e9, 0x240b },
|
||||
{ 0x09ea, 0x2518 },
|
||||
{ 0x09eb, 0x2510 },
|
||||
{ 0x09ec, 0x250c },
|
||||
{ 0x09ed, 0x2514 },
|
||||
{ 0x09ee, 0x253c },
|
||||
{ 0x09ef, 0x23ba },
|
||||
{ 0x09f0, 0x23bb },
|
||||
{ 0x09f1, 0x2500 },
|
||||
{ 0x09f2, 0x23bc },
|
||||
{ 0x09f3, 0x23bd },
|
||||
{ 0x09f4, 0x251c },
|
||||
{ 0x09f5, 0x2524 },
|
||||
{ 0x09f6, 0x2534 },
|
||||
{ 0x09f7, 0x252c },
|
||||
{ 0x09f8, 0x2502 },
|
||||
{ 0x0aa1, 0x2003 },
|
||||
{ 0x0aa2, 0x2002 },
|
||||
{ 0x0aa3, 0x2004 },
|
||||
{ 0x0aa4, 0x2005 },
|
||||
{ 0x0aa5, 0x2007 },
|
||||
{ 0x0aa6, 0x2008 },
|
||||
{ 0x0aa7, 0x2009 },
|
||||
{ 0x0aa8, 0x200a },
|
||||
{ 0x0aa9, 0x2014 },
|
||||
{ 0x0aaa, 0x2013 },
|
||||
{ 0x0aae, 0x2026 },
|
||||
{ 0x0aaf, 0x2025 },
|
||||
{ 0x0ab0, 0x2153 },
|
||||
{ 0x0ab1, 0x2154 },
|
||||
{ 0x0ab2, 0x2155 },
|
||||
{ 0x0ab3, 0x2156 },
|
||||
{ 0x0ab4, 0x2157 },
|
||||
{ 0x0ab5, 0x2158 },
|
||||
{ 0x0ab6, 0x2159 },
|
||||
{ 0x0ab7, 0x215a },
|
||||
{ 0x0ab8, 0x2105 },
|
||||
{ 0x0abb, 0x2012 },
|
||||
{ 0x0abc, 0x2329 },
|
||||
{ 0x0abe, 0x232a },
|
||||
{ 0x0ac3, 0x215b },
|
||||
{ 0x0ac4, 0x215c },
|
||||
{ 0x0ac5, 0x215d },
|
||||
{ 0x0ac6, 0x215e },
|
||||
{ 0x0ac9, 0x2122 },
|
||||
{ 0x0aca, 0x2613 },
|
||||
{ 0x0acc, 0x25c1 },
|
||||
{ 0x0acd, 0x25b7 },
|
||||
{ 0x0ace, 0x25cb },
|
||||
{ 0x0acf, 0x25af },
|
||||
{ 0x0ad0, 0x2018 },
|
||||
{ 0x0ad1, 0x2019 },
|
||||
{ 0x0ad2, 0x201c },
|
||||
{ 0x0ad3, 0x201d },
|
||||
{ 0x0ad4, 0x211e },
|
||||
{ 0x0ad6, 0x2032 },
|
||||
{ 0x0ad7, 0x2033 },
|
||||
{ 0x0ad9, 0x271d },
|
||||
{ 0x0adb, 0x25ac },
|
||||
{ 0x0adc, 0x25c0 },
|
||||
{ 0x0add, 0x25b6 },
|
||||
{ 0x0ade, 0x25cf },
|
||||
{ 0x0adf, 0x25ae },
|
||||
{ 0x0ae0, 0x25e6 },
|
||||
{ 0x0ae1, 0x25ab },
|
||||
{ 0x0ae2, 0x25ad },
|
||||
{ 0x0ae3, 0x25b3 },
|
||||
{ 0x0ae4, 0x25bd },
|
||||
{ 0x0ae5, 0x2606 },
|
||||
{ 0x0ae6, 0x2022 },
|
||||
{ 0x0ae7, 0x25aa },
|
||||
{ 0x0ae8, 0x25b2 },
|
||||
{ 0x0ae9, 0x25bc },
|
||||
{ 0x0aea, 0x261c },
|
||||
{ 0x0aeb, 0x261e },
|
||||
{ 0x0aec, 0x2663 },
|
||||
{ 0x0aed, 0x2666 },
|
||||
{ 0x0aee, 0x2665 },
|
||||
{ 0x0af0, 0x2720 },
|
||||
{ 0x0af1, 0x2020 },
|
||||
{ 0x0af2, 0x2021 },
|
||||
{ 0x0af3, 0x2713 },
|
||||
{ 0x0af4, 0x2717 },
|
||||
{ 0x0af5, 0x266f },
|
||||
{ 0x0af6, 0x266d },
|
||||
{ 0x0af7, 0x2642 },
|
||||
{ 0x0af8, 0x2640 },
|
||||
{ 0x0af9, 0x260e },
|
||||
{ 0x0afa, 0x2315 },
|
||||
{ 0x0afb, 0x2117 },
|
||||
{ 0x0afc, 0x2038 },
|
||||
{ 0x0afd, 0x201a },
|
||||
{ 0x0afe, 0x201e },
|
||||
{ 0x0ba3, 0x003c },
|
||||
{ 0x0ba6, 0x003e },
|
||||
{ 0x0ba8, 0x2228 },
|
||||
{ 0x0ba9, 0x2227 },
|
||||
{ 0x0bc0, 0x00af },
|
||||
{ 0x0bc2, 0x22a5 },
|
||||
{ 0x0bc3, 0x2229 },
|
||||
{ 0x0bc4, 0x230a },
|
||||
{ 0x0bc6, 0x005f },
|
||||
{ 0x0bca, 0x2218 },
|
||||
{ 0x0bcc, 0x2395 },
|
||||
{ 0x0bce, 0x22a4 },
|
||||
{ 0x0bcf, 0x25cb },
|
||||
{ 0x0bd3, 0x2308 },
|
||||
{ 0x0bd6, 0x222a },
|
||||
{ 0x0bd8, 0x2283 },
|
||||
{ 0x0bda, 0x2282 },
|
||||
{ 0x0bdc, 0x22a2 },
|
||||
{ 0x0bfc, 0x22a3 },
|
||||
{ 0x0cdf, 0x2017 },
|
||||
{ 0x0ce0, 0x05d0 },
|
||||
{ 0x0ce1, 0x05d1 },
|
||||
{ 0x0ce2, 0x05d2 },
|
||||
{ 0x0ce3, 0x05d3 },
|
||||
{ 0x0ce4, 0x05d4 },
|
||||
{ 0x0ce5, 0x05d5 },
|
||||
{ 0x0ce6, 0x05d6 },
|
||||
{ 0x0ce7, 0x05d7 },
|
||||
{ 0x0ce8, 0x05d8 },
|
||||
{ 0x0ce9, 0x05d9 },
|
||||
{ 0x0cea, 0x05da },
|
||||
{ 0x0ceb, 0x05db },
|
||||
{ 0x0cec, 0x05dc },
|
||||
{ 0x0ced, 0x05dd },
|
||||
{ 0x0cee, 0x05de },
|
||||
{ 0x0cef, 0x05df },
|
||||
{ 0x0cf0, 0x05e0 },
|
||||
{ 0x0cf1, 0x05e1 },
|
||||
{ 0x0cf2, 0x05e2 },
|
||||
{ 0x0cf3, 0x05e3 },
|
||||
{ 0x0cf4, 0x05e4 },
|
||||
{ 0x0cf5, 0x05e5 },
|
||||
{ 0x0cf6, 0x05e6 },
|
||||
{ 0x0cf7, 0x05e7 },
|
||||
{ 0x0cf8, 0x05e8 },
|
||||
{ 0x0cf9, 0x05e9 },
|
||||
{ 0x0cfa, 0x05ea },
|
||||
{ 0x0da1, 0x0e01 },
|
||||
{ 0x0da2, 0x0e02 },
|
||||
{ 0x0da3, 0x0e03 },
|
||||
{ 0x0da4, 0x0e04 },
|
||||
{ 0x0da5, 0x0e05 },
|
||||
{ 0x0da6, 0x0e06 },
|
||||
{ 0x0da7, 0x0e07 },
|
||||
{ 0x0da8, 0x0e08 },
|
||||
{ 0x0da9, 0x0e09 },
|
||||
{ 0x0daa, 0x0e0a },
|
||||
{ 0x0dab, 0x0e0b },
|
||||
{ 0x0dac, 0x0e0c },
|
||||
{ 0x0dad, 0x0e0d },
|
||||
{ 0x0dae, 0x0e0e },
|
||||
{ 0x0daf, 0x0e0f },
|
||||
{ 0x0db0, 0x0e10 },
|
||||
{ 0x0db1, 0x0e11 },
|
||||
{ 0x0db2, 0x0e12 },
|
||||
{ 0x0db3, 0x0e13 },
|
||||
{ 0x0db4, 0x0e14 },
|
||||
{ 0x0db5, 0x0e15 },
|
||||
{ 0x0db6, 0x0e16 },
|
||||
{ 0x0db7, 0x0e17 },
|
||||
{ 0x0db8, 0x0e18 },
|
||||
{ 0x0db9, 0x0e19 },
|
||||
{ 0x0dba, 0x0e1a },
|
||||
{ 0x0dbb, 0x0e1b },
|
||||
{ 0x0dbc, 0x0e1c },
|
||||
{ 0x0dbd, 0x0e1d },
|
||||
{ 0x0dbe, 0x0e1e },
|
||||
{ 0x0dbf, 0x0e1f },
|
||||
{ 0x0dc0, 0x0e20 },
|
||||
{ 0x0dc1, 0x0e21 },
|
||||
{ 0x0dc2, 0x0e22 },
|
||||
{ 0x0dc3, 0x0e23 },
|
||||
{ 0x0dc4, 0x0e24 },
|
||||
{ 0x0dc5, 0x0e25 },
|
||||
{ 0x0dc6, 0x0e26 },
|
||||
{ 0x0dc7, 0x0e27 },
|
||||
{ 0x0dc8, 0x0e28 },
|
||||
{ 0x0dc9, 0x0e29 },
|
||||
{ 0x0dca, 0x0e2a },
|
||||
{ 0x0dcb, 0x0e2b },
|
||||
{ 0x0dcc, 0x0e2c },
|
||||
{ 0x0dcd, 0x0e2d },
|
||||
{ 0x0dce, 0x0e2e },
|
||||
{ 0x0dcf, 0x0e2f },
|
||||
{ 0x0dd0, 0x0e30 },
|
||||
{ 0x0dd1, 0x0e31 },
|
||||
{ 0x0dd2, 0x0e32 },
|
||||
{ 0x0dd3, 0x0e33 },
|
||||
{ 0x0dd4, 0x0e34 },
|
||||
{ 0x0dd5, 0x0e35 },
|
||||
{ 0x0dd6, 0x0e36 },
|
||||
{ 0x0dd7, 0x0e37 },
|
||||
{ 0x0dd8, 0x0e38 },
|
||||
{ 0x0dd9, 0x0e39 },
|
||||
{ 0x0dda, 0x0e3a },
|
||||
{ 0x0ddf, 0x0e3f },
|
||||
{ 0x0de0, 0x0e40 },
|
||||
{ 0x0de1, 0x0e41 },
|
||||
{ 0x0de2, 0x0e42 },
|
||||
{ 0x0de3, 0x0e43 },
|
||||
{ 0x0de4, 0x0e44 },
|
||||
{ 0x0de5, 0x0e45 },
|
||||
{ 0x0de6, 0x0e46 },
|
||||
{ 0x0de7, 0x0e47 },
|
||||
{ 0x0de8, 0x0e48 },
|
||||
{ 0x0de9, 0x0e49 },
|
||||
{ 0x0dea, 0x0e4a },
|
||||
{ 0x0deb, 0x0e4b },
|
||||
{ 0x0dec, 0x0e4c },
|
||||
{ 0x0ded, 0x0e4d },
|
||||
{ 0x0df0, 0x0e50 },
|
||||
{ 0x0df1, 0x0e51 },
|
||||
{ 0x0df2, 0x0e52 },
|
||||
{ 0x0df3, 0x0e53 },
|
||||
{ 0x0df4, 0x0e54 },
|
||||
{ 0x0df5, 0x0e55 },
|
||||
{ 0x0df6, 0x0e56 },
|
||||
{ 0x0df7, 0x0e57 },
|
||||
{ 0x0df8, 0x0e58 },
|
||||
{ 0x0df9, 0x0e59 },
|
||||
{ 0x0ea1, 0x3131 },
|
||||
{ 0x0ea2, 0x3132 },
|
||||
{ 0x0ea3, 0x3133 },
|
||||
{ 0x0ea4, 0x3134 },
|
||||
{ 0x0ea5, 0x3135 },
|
||||
{ 0x0ea6, 0x3136 },
|
||||
{ 0x0ea7, 0x3137 },
|
||||
{ 0x0ea8, 0x3138 },
|
||||
{ 0x0ea9, 0x3139 },
|
||||
{ 0x0eaa, 0x313a },
|
||||
{ 0x0eab, 0x313b },
|
||||
{ 0x0eac, 0x313c },
|
||||
{ 0x0ead, 0x313d },
|
||||
{ 0x0eae, 0x313e },
|
||||
{ 0x0eaf, 0x313f },
|
||||
{ 0x0eb0, 0x3140 },
|
||||
{ 0x0eb1, 0x3141 },
|
||||
{ 0x0eb2, 0x3142 },
|
||||
{ 0x0eb3, 0x3143 },
|
||||
{ 0x0eb4, 0x3144 },
|
||||
{ 0x0eb5, 0x3145 },
|
||||
{ 0x0eb6, 0x3146 },
|
||||
{ 0x0eb7, 0x3147 },
|
||||
{ 0x0eb8, 0x3148 },
|
||||
{ 0x0eb9, 0x3149 },
|
||||
{ 0x0eba, 0x314a },
|
||||
{ 0x0ebb, 0x314b },
|
||||
{ 0x0ebc, 0x314c },
|
||||
{ 0x0ebd, 0x314d },
|
||||
{ 0x0ebe, 0x314e },
|
||||
{ 0x0ebf, 0x314f },
|
||||
{ 0x0ec0, 0x3150 },
|
||||
{ 0x0ec1, 0x3151 },
|
||||
{ 0x0ec2, 0x3152 },
|
||||
{ 0x0ec3, 0x3153 },
|
||||
{ 0x0ec4, 0x3154 },
|
||||
{ 0x0ec5, 0x3155 },
|
||||
{ 0x0ec6, 0x3156 },
|
||||
{ 0x0ec7, 0x3157 },
|
||||
{ 0x0ec8, 0x3158 },
|
||||
{ 0x0ec9, 0x3159 },
|
||||
{ 0x0eca, 0x315a },
|
||||
{ 0x0ecb, 0x315b },
|
||||
{ 0x0ecc, 0x315c },
|
||||
{ 0x0ecd, 0x315d },
|
||||
{ 0x0ece, 0x315e },
|
||||
{ 0x0ecf, 0x315f },
|
||||
{ 0x0ed0, 0x3160 },
|
||||
{ 0x0ed1, 0x3161 },
|
||||
{ 0x0ed2, 0x3162 },
|
||||
{ 0x0ed3, 0x3163 },
|
||||
{ 0x0ed4, 0x11a8 },
|
||||
{ 0x0ed5, 0x11a9 },
|
||||
{ 0x0ed6, 0x11aa },
|
||||
{ 0x0ed7, 0x11ab },
|
||||
{ 0x0ed8, 0x11ac },
|
||||
{ 0x0ed9, 0x11ad },
|
||||
{ 0x0eda, 0x11ae },
|
||||
{ 0x0edb, 0x11af },
|
||||
{ 0x0edc, 0x11b0 },
|
||||
{ 0x0edd, 0x11b1 },
|
||||
{ 0x0ede, 0x11b2 },
|
||||
{ 0x0edf, 0x11b3 },
|
||||
{ 0x0ee0, 0x11b4 },
|
||||
{ 0x0ee1, 0x11b5 },
|
||||
{ 0x0ee2, 0x11b6 },
|
||||
{ 0x0ee3, 0x11b7 },
|
||||
{ 0x0ee4, 0x11b8 },
|
||||
{ 0x0ee5, 0x11b9 },
|
||||
{ 0x0ee6, 0x11ba },
|
||||
{ 0x0ee7, 0x11bb },
|
||||
{ 0x0ee8, 0x11bc },
|
||||
{ 0x0ee9, 0x11bd },
|
||||
{ 0x0eea, 0x11be },
|
||||
{ 0x0eeb, 0x11bf },
|
||||
{ 0x0eec, 0x11c0 },
|
||||
{ 0x0eed, 0x11c1 },
|
||||
{ 0x0eee, 0x11c2 },
|
||||
{ 0x0eef, 0x316d },
|
||||
{ 0x0ef0, 0x3171 },
|
||||
{ 0x0ef1, 0x3178 },
|
||||
{ 0x0ef2, 0x317f },
|
||||
{ 0x0ef3, 0x3181 },
|
||||
{ 0x0ef4, 0x3184 },
|
||||
{ 0x0ef5, 0x3186 },
|
||||
{ 0x0ef6, 0x318d },
|
||||
{ 0x0ef7, 0x318e },
|
||||
{ 0x0ef8, 0x11eb },
|
||||
{ 0x0ef9, 0x11f0 },
|
||||
{ 0x0efa, 0x11f9 },
|
||||
{ 0x0eff, 0x20a9 },
|
||||
{ 0x13a4, 0x20ac },
|
||||
{ 0x13bc, 0x0152 },
|
||||
{ 0x13bd, 0x0153 },
|
||||
{ 0x13be, 0x0178 },
|
||||
{ 0x20ac, 0x20ac },
|
||||
{ 0xfe50, '`' },
|
||||
{ 0xfe51, 0x00b4 },
|
||||
{ 0xfe52, '^' },
|
||||
{ 0xfe53, '~' },
|
||||
{ 0xfe54, 0x00af },
|
||||
{ 0xfe55, 0x02d8 },
|
||||
{ 0xfe56, 0x02d9 },
|
||||
{ 0xfe57, 0x00a8 },
|
||||
{ 0xfe58, 0x02da },
|
||||
{ 0xfe59, 0x02dd },
|
||||
{ 0xfe5a, 0x02c7 },
|
||||
{ 0xfe5b, 0x00b8 },
|
||||
{ 0xfe5c, 0x02db },
|
||||
{ 0xfe5d, 0x037a },
|
||||
{ 0xfe5e, 0x309b },
|
||||
{ 0xfe5f, 0x309c },
|
||||
{ 0xfe63, '/' },
|
||||
{ 0xfe64, 0x02bc },
|
||||
{ 0xfe65, 0x02bd },
|
||||
{ 0xfe66, 0x02f5 },
|
||||
{ 0xfe67, 0x02f3 },
|
||||
{ 0xfe68, 0x02cd },
|
||||
{ 0xfe69, 0xa788 },
|
||||
{ 0xfe6a, 0x02f7 },
|
||||
{ 0xfe6e, ',' },
|
||||
{ 0xfe6f, 0x00a4 },
|
||||
{ 0xfe80, 'a' }, // XK_dead_a
|
||||
{ 0xfe81, 'A' }, // XK_dead_A
|
||||
{ 0xfe82, 'e' }, // XK_dead_e
|
||||
{ 0xfe83, 'E' }, // XK_dead_E
|
||||
{ 0xfe84, 'i' }, // XK_dead_i
|
||||
{ 0xfe85, 'I' }, // XK_dead_I
|
||||
{ 0xfe86, 'o' }, // XK_dead_o
|
||||
{ 0xfe87, 'O' }, // XK_dead_O
|
||||
{ 0xfe88, 'u' }, // XK_dead_u
|
||||
{ 0xfe89, 'U' }, // XK_dead_U
|
||||
{ 0xfe8a, 0x0259 },
|
||||
{ 0xfe8b, 0x018f },
|
||||
{ 0xfe8c, 0x00b5 },
|
||||
{ 0xfe90, '_' },
|
||||
{ 0xfe91, 0x02c8 },
|
||||
{ 0xfe92, 0x02cc },
|
||||
{ 0xff80 /*XKB_KEY_KP_Space*/, ' ' },
|
||||
{ 0xff95 /*XKB_KEY_KP_7*/, 0x0037 },
|
||||
{ 0xff96 /*XKB_KEY_KP_4*/, 0x0034 },
|
||||
{ 0xff97 /*XKB_KEY_KP_8*/, 0x0038 },
|
||||
{ 0xff98 /*XKB_KEY_KP_6*/, 0x0036 },
|
||||
{ 0xff99 /*XKB_KEY_KP_2*/, 0x0032 },
|
||||
{ 0xff9a /*XKB_KEY_KP_9*/, 0x0039 },
|
||||
{ 0xff9b /*XKB_KEY_KP_3*/, 0x0033 },
|
||||
{ 0xff9c /*XKB_KEY_KP_1*/, 0x0031 },
|
||||
{ 0xff9d /*XKB_KEY_KP_5*/, 0x0035 },
|
||||
{ 0xff9e /*XKB_KEY_KP_0*/, 0x0030 },
|
||||
{ 0xffaa /*XKB_KEY_KP_Multiply*/, '*' },
|
||||
{ 0xffab /*XKB_KEY_KP_Add*/, '+' },
|
||||
{ 0xffac /*XKB_KEY_KP_Separator*/, ',' },
|
||||
{ 0xffad /*XKB_KEY_KP_Subtract*/, '-' },
|
||||
{ 0xffae /*XKB_KEY_KP_Decimal*/, '.' },
|
||||
{ 0xffaf /*XKB_KEY_KP_Divide*/, '/' },
|
||||
{ 0xffb0 /*XKB_KEY_KP_0*/, 0x0030 },
|
||||
{ 0xffb1 /*XKB_KEY_KP_1*/, 0x0031 },
|
||||
{ 0xffb2 /*XKB_KEY_KP_2*/, 0x0032 },
|
||||
{ 0xffb3 /*XKB_KEY_KP_3*/, 0x0033 },
|
||||
{ 0xffb4 /*XKB_KEY_KP_4*/, 0x0034 },
|
||||
{ 0xffb5 /*XKB_KEY_KP_5*/, 0x0035 },
|
||||
{ 0xffb6 /*XKB_KEY_KP_6*/, 0x0036 },
|
||||
{ 0xffb7 /*XKB_KEY_KP_7*/, 0x0037 },
|
||||
{ 0xffb8 /*XKB_KEY_KP_8*/, 0x0038 },
|
||||
{ 0xffb9 /*XKB_KEY_KP_9*/, 0x0039 },
|
||||
{ 0xffbd /*XKB_KEY_KP_Equal*/, '=' }
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW internal API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Convert XKB KeySym to Unicode
|
||||
//
|
||||
uint32_t _glfwKeySym2Unicode(unsigned int keysym)
|
||||
{
|
||||
int min = 0;
|
||||
int max = sizeof(keysymtab) / sizeof(struct codepair) - 1;
|
||||
int mid;
|
||||
|
||||
// First check for Latin-1 characters (1:1 mapping)
|
||||
if ((keysym >= 0x0020 && keysym <= 0x007e) ||
|
||||
(keysym >= 0x00a0 && keysym <= 0x00ff))
|
||||
{
|
||||
return keysym;
|
||||
}
|
||||
|
||||
// Also check for directly encoded 24-bit UCS characters
|
||||
if ((keysym & 0xff000000) == 0x01000000)
|
||||
return keysym & 0x00ffffff;
|
||||
|
||||
// Binary search in table
|
||||
while (max >= min)
|
||||
{
|
||||
mid = (min + max) / 2;
|
||||
if (keysymtab[mid].keysym < keysym)
|
||||
min = mid + 1;
|
||||
else if (keysymtab[mid].keysym > keysym)
|
||||
max = mid - 1;
|
||||
else
|
||||
return keysymtab[mid].ucs;
|
||||
}
|
||||
|
||||
// No matching Unicode value found
|
||||
return GLFW_INVALID_CODEPOINT;
|
||||
}
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2014 Jonas Ådahl <jadahl@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build freebsd || linux || netbsd || openbsd
|
||||
|
||||
#define GLFW_INVALID_CODEPOINT 0xffffffffu
|
||||
|
||||
uint32_t _glfwKeySym2Unicode(unsigned int keysym);
|
||||
|
||||
Reference in New Issue
Block a user