vendor dependencies, make some changes to how input is done

This commit is contained in:
2026-06-15 18:54:00 +02:00
parent 535130933c
commit 4800eb28d9
759 changed files with 360941 additions and 30 deletions
+52
View File
@@ -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")
}
File diff suppressed because it is too large Load Diff
+14
View File
@@ -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"
@@ -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
View File
@@ -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"
@@ -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
;
}
@@ -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, &current);
const GLFWvidmode* best = _glfwChooseVideoMode(monitor, desired);
if (_glfwCompareVideoModes(&current, 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;
}
@@ -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);
@@ -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)
}
File diff suppressed because it is too large Load Diff
+204
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
@@ -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))
}
@@ -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;
}
@@ -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
View File
@@ -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
View File
@@ -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
}
File diff suppressed because it is too large Load Diff
@@ -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
View File
@@ -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()
}
@@ -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;
}
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
@@ -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
View File
@@ -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
View File
@@ -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)
}
@@ -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);
@@ -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;
}
@@ -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;
}
@@ -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);
@@ -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)
}
@@ -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);
}
@@ -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;
@@ -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;
}
@@ -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
View File
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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)
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+884
View File
@@ -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
}
File diff suppressed because it is too large Load Diff
@@ -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, &current);
if (_glfwCompareVideoModes(&current, 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;
}
@@ -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);
File diff suppressed because it is too large Load Diff
@@ -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;
}
@@ -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);