internal/ui: remove Cgo on darwin in ui_glfw_darwin.go (#2329)

Updates #1162
This commit is contained in:
TotallyGamerJet 2022-09-15 22:53:46 -04:00 committed by GitHub
parent 45b2bd7b2b
commit 043397c20e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 396 additions and 234 deletions

View File

@ -26,6 +26,11 @@ var (
class_NSMethodSignature = objc.GetClass("NSMethodSignature")
class_NSAutoreleasePool = objc.GetClass("NSAutoreleasePool")
class_NSString = objc.GetClass("NSString")
class_NSProcessInfo = objc.GetClass("NSProcessInfo")
class_NSColor = objc.GetClass("NSColor")
class_NSWindow = objc.GetClass("NSWindow")
class_NSView = objc.GetClass("NSView")
class_NSScreen = objc.GetClass("NSScreen")
)
var (
@ -38,23 +43,165 @@ var (
sel_setArgumentAtIndex = objc.RegisterName("setArgument:atIndex:")
sel_getReturnValue = objc.RegisterName("getReturnValue:")
sel_invoke = objc.RegisterName("invoke")
sel_invokeWithTarget = objc.RegisterName("invokeWithTarget:")
sel_instanceMethodSignatureForSelector = objc.RegisterName("instanceMethodSignatureForSelector:")
sel_signatureWithObjCTypes = objc.RegisterName("signatureWithObjCTypes:")
sel_initWithUTF8String = objc.RegisterName("initWithUTF8String:")
sel_UTF8String = objc.RegisterName("UTF8String")
sel_length = objc.RegisterName("length")
sel_processInfo = objc.RegisterName("processInfo")
sel_isOperatingSystemAtLeastVersion = objc.RegisterName("isOperatingSystemAtLeastVersion:")
sel_frame = objc.RegisterName("frame")
sel_contentView = objc.RegisterName("contentView")
sel_setBackgroundColor = objc.RegisterName("setBackgroundColor:")
sel_colorWithSRGBRedGreenBlueAlpha = objc.RegisterName("colorWithSRGBRed:green:blue:alpha:")
sel_setFrameSize = objc.RegisterName("setFrameSize:")
sel_object = objc.RegisterName("object")
sel_styleMask = objc.RegisterName("styleMask")
sel_setStyleMask = objc.RegisterName("setStyleMask:")
sel_mainScreen = objc.RegisterName("mainScreen")
sel_screen = objc.RegisterName("screen")
sel_isVisible = objc.RegisterName("isVisible")
sel_deviceDescription = objc.RegisterName("deviceDescription")
sel_objectForKey = objc.RegisterName("objectForKey:")
sel_unsignedIntValue = objc.RegisterName("unsignedIntValue")
)
type CGFloat float64
const NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7
const (
NSWindowStyleMaskResizable = 1 << 3
NSWindowStyleMaskFullScreen = 1 << 14
)
type CGFloat = float64
type CGSize struct {
Width, Height CGFloat
}
type CGPoint struct {
X, Y float64
}
type CGRect struct {
Origin CGPoint
Size CGSize
}
type NSUInteger = uint
type NSInteger = int
type NSPoint = CGPoint
type NSRect = CGRect
type NSSize = CGSize
type NSError struct {
objc.ID
}
type NSColor struct {
objc.ID
}
func NSColor_colorWithSRGBRedGreenBlueAlpha(red, green, blue, alpha CGFloat) (color NSColor) {
sig := NSMethodSignature_signatureWithObjCTypes("@@:ffff")
inv := NSInvocation_invocationWithMethodSignature(sig)
inv.SetSelector(sel_colorWithSRGBRedGreenBlueAlpha)
inv.SetArgumentAtIndex(unsafe.Pointer(&red), 2)
inv.SetArgumentAtIndex(unsafe.Pointer(&green), 3)
inv.SetArgumentAtIndex(unsafe.Pointer(&blue), 4)
inv.SetArgumentAtIndex(unsafe.Pointer(&alpha), 5)
inv.InvokeWithTarget(objc.ID(class_NSColor))
inv.GetReturnValue(unsafe.Pointer(&color))
return color
}
type NSOperatingSystemVersion struct {
Major, Minor, Patch NSInteger
}
type NSProcessInfo struct {
objc.ID
}
func NSProcessInfo_processInfo() NSProcessInfo {
return NSProcessInfo{objc.ID(class_NSProcessInfo).Send(sel_processInfo)}
}
func (p NSProcessInfo) IsOperatingSystemAtLeastVersion(version NSOperatingSystemVersion) bool {
sig := NSMethodSignature_instanceMethodSignatureForSelector(objc.ID(class_NSProcessInfo), sel_isOperatingSystemAtLeastVersion)
inv := NSInvocation_invocationWithMethodSignature(sig)
inv.SetTarget(p.ID)
inv.SetSelector(sel_isOperatingSystemAtLeastVersion)
inv.SetArgumentAtIndex(unsafe.Pointer(&version), 2)
inv.Invoke()
var ret int
inv.GetReturnValue(unsafe.Pointer(&ret))
return ret != 0
}
type NSWindow struct {
objc.ID
}
func (w NSWindow) StyleMask() NSUInteger {
return NSUInteger(w.Send(sel_styleMask))
}
func (w NSWindow) SetStyleMask(styleMask NSUInteger) {
w.Send(sel_setStyleMask, styleMask)
}
func (w NSWindow) SetBackgroundColor(color NSColor) {
w.Send(sel_setBackgroundColor, color.ID)
}
func (w NSWindow) IsVisibile() bool {
return w.Send(sel_isVisible) != 0
}
func (w NSWindow) Screen() NSScreen {
return NSScreen{w.Send(sel_screen)}
}
func (w NSWindow) Frame() NSRect {
sig := NSMethodSignature_instanceMethodSignatureForSelector(objc.ID(class_NSWindow), sel_frame)
inv := NSInvocation_invocationWithMethodSignature(sig)
inv.SetTarget(w.ID)
inv.SetSelector(sel_frame)
inv.Invoke()
var rect NSRect
inv.GetReturnValue(unsafe.Pointer(&rect))
return rect
}
func (w NSWindow) ContentView() NSView {
return NSView{w.Send(sel_contentView)}
}
type NSView struct {
objc.ID
}
func (v NSView) SetFrameSize(size CGSize) {
sig := NSMethodSignature_instanceMethodSignatureForSelector(objc.ID(class_NSView), sel_setFrameSize)
inv := NSInvocation_invocationWithMethodSignature(sig)
inv.SetSelector(sel_setFrameSize)
inv.SetArgumentAtIndex(unsafe.Pointer(&size), 2)
inv.InvokeWithTarget(v.ID)
}
func (v NSView) Frame() NSRect {
sig := NSMethodSignature_instanceMethodSignatureForSelector(objc.ID(class_NSView), sel_frame)
inv := NSInvocation_invocationWithMethodSignature(sig)
inv.SetSelector(sel_frame)
inv.InvokeWithTarget(v.ID)
var rect NSRect
inv.GetReturnValue(unsafe.Pointer(&rect))
return rect
}
// NSInvocation is being used to call functions that can't be called directly with purego.SyscallN.
// See the downsides of that function for what it cannot do.
type NSInvocation struct {
@ -65,24 +212,28 @@ func NSInvocation_invocationWithMethodSignature(sig NSMethodSignature) NSInvocat
return NSInvocation{objc.ID(class_NSInvocation).Send(sel_invocationWithMethodSignature, sig.ID)}
}
func (inv NSInvocation) SetSelector(cmd objc.SEL) {
inv.Send(sel_setSelector, cmd)
func (i NSInvocation) SetSelector(cmd objc.SEL) {
i.Send(sel_setSelector, cmd)
}
func (inv NSInvocation) SetTarget(target objc.ID) {
inv.Send(sel_setTarget, target)
func (i NSInvocation) SetTarget(target objc.ID) {
i.Send(sel_setTarget, target)
}
func (inv NSInvocation) SetArgumentAtIndex(arg unsafe.Pointer, idx int) {
inv.Send(sel_setArgumentAtIndex, arg, idx)
func (i NSInvocation) SetArgumentAtIndex(arg unsafe.Pointer, idx int) {
i.Send(sel_setArgumentAtIndex, arg, idx)
}
func (inv NSInvocation) GetReturnValue(ret unsafe.Pointer) {
inv.Send(sel_getReturnValue, ret)
func (i NSInvocation) GetReturnValue(ret unsafe.Pointer) {
i.Send(sel_getReturnValue, ret)
}
func (inv NSInvocation) Invoke() {
inv.Send(sel_invoke)
func (i NSInvocation) Invoke() {
i.Send(sel_invoke)
}
func (i NSInvocation) InvokeWithTarget(target objc.ID) {
i.Send(sel_invokeWithTarget, target)
}
type NSMethodSignature struct {
@ -109,8 +260,8 @@ func NSAutoreleasePool_new() NSAutoreleasePool {
return NSAutoreleasePool{objc.ID(class_NSAutoreleasePool).Send(sel_new)}
}
func (pool NSAutoreleasePool) Release() {
pool.Send(sel_release)
func (p NSAutoreleasePool) Release() {
p.Send(sel_release)
}
type NSString struct {
@ -136,3 +287,39 @@ func (s NSString) String() string {
header.Cap = header.Len
return string(b)
}
type NSNotification struct {
objc.ID
}
func (n NSNotification) Object() objc.ID {
return n.Send(sel_object)
}
type NSScreen struct {
objc.ID
}
func NSScreen_mainScreen() NSScreen {
return NSScreen{objc.ID(class_NSScreen).Send(sel_mainScreen)}
}
func (s NSScreen) DeviceDescription() NSDictionary {
return NSDictionary{s.Send(sel_deviceDescription)}
}
type NSDictionary struct {
objc.ID
}
func (d NSDictionary) ObjectForKey(object objc.ID) objc.ID {
return d.Send(sel_objectForKey, object)
}
type NSNumber struct {
objc.ID
}
func (n NSNumber) UnsignedIntValue() uint {
return uint(n.Send(sel_unsignedIntValue))
}

View File

@ -17,3 +17,7 @@ package glfw
func (w *Window) GetCocoaWindow() uintptr {
return uintptr(w.w.GetCocoaWindow())
}
func (m *Monitor) GetCocoaMonitor() uintptr {
return m.m.GetCocoaMonitor()
}

View File

@ -17,224 +17,107 @@
package ui
// #cgo CFLAGS: -x objective-c
// #cgo LDFLAGS: -framework AppKit
//
// #import <AppKit/AppKit.h>
//
// @interface EbitengineWindowDelegate : NSObject <NSWindowDelegate>
// @end
//
// @implementation EbitengineWindowDelegate {
// id<NSWindowDelegate> origDelegate_;
// bool origResizable_;
// }
//
// - (instancetype)initWithOrigDelegate:(id<NSWindowDelegate>)origDelegate {
// self = [super init];
// if (self != nil) {
// origDelegate_ = origDelegate;
// }
// return self;
// }
//
// // The method set of origDelegate_ must sync with GLFWWindowDelegate's implementation.
// // See cocoa_window.m in GLFW.
// - (BOOL)windowShouldClose:(id)sender {
// return [origDelegate_ windowShouldClose:sender];
// }
// - (void)windowDidResize:(NSNotification *)notification {
// [origDelegate_ windowDidResize:notification];
// }
// - (void)windowDidMove:(NSNotification *)notification {
// [origDelegate_ windowDidMove:notification];
// }
// - (void)windowDidMiniaturize:(NSNotification *)notification {
// [origDelegate_ windowDidMiniaturize:notification];
// }
// - (void)windowDidDeminiaturize:(NSNotification *)notification {
// [origDelegate_ windowDidDeminiaturize:notification];
// }
// - (void)windowDidBecomeKey:(NSNotification *)notification {
// [origDelegate_ windowDidBecomeKey:notification];
// }
// - (void)windowDidResignKey:(NSNotification *)notification {
// [origDelegate_ windowDidResignKey:notification];
// }
// - (void)windowDidChangeOcclusionState:(NSNotification* )notification {
// [origDelegate_ windowDidChangeOcclusionState:notification];
// }
//
// - (void)pushResizableState:(NSWindow*)window {
// origResizable_ = window.styleMask & NSWindowStyleMaskResizable;
// if (!origResizable_) {
// window.styleMask |= NSWindowStyleMaskResizable;
// }
// }
//
// - (void)popResizableState:(NSWindow*)window {
// if (!origResizable_) {
// window.styleMask &= ~NSWindowStyleMaskResizable;
// }
// origResizable_ = false;
// }
//
// - (void)windowWillEnterFullScreen:(NSNotification *)notification {
// NSWindow* window = (NSWindow*)[notification object];
// [self pushResizableState:window];
// }
//
// - (void)windowDidEnterFullScreen:(NSNotification *)notification {
// NSWindow* window = (NSWindow*)[notification object];
// [self popResizableState:window];
// }
//
// - (void)windowWillExitFullScreen:(NSNotification *)notification {
// NSWindow* window = (NSWindow*)[notification object];
// [self pushResizableState:window];
// }
//
// - (void)windowDidExitFullScreen:(NSNotification *)notification {
// NSWindow* window = (NSWindow*)[notification object];
// [self popResizableState:window];
// // Do not call setFrame here (#2295). setFrame here causes unexpected results.
// }
//
// @end
//
// static void initializeWindow(uintptr_t windowPtr) {
// NSWindow* window = (NSWindow*)windowPtr;
// // This delegate is never released. This assumes that the window lives until the process lives.
// window.delegate = [[EbitengineWindowDelegate alloc] initWithOrigDelegate:window.delegate];
// }
//
// static void currentMonitorPos(uintptr_t windowPtr, int* x, int* y) {
// @autoreleasepool {
// NSScreen* screen = [NSScreen mainScreen];
// if (windowPtr) {
// NSWindow* window = (NSWindow*)windowPtr;
// if ([window isVisible]) {
// // When the window is visible, the window is already initialized.
// // [NSScreen mainScreen] sometimes tells a lie when the window is put across monitors (#703).
// screen = [window screen];
// }
// }
// NSDictionary* screenDictionary = [screen deviceDescription];
// NSNumber* screenID = [screenDictionary objectForKey:@"NSScreenNumber"];
// CGDirectDisplayID aID = [screenID unsignedIntValue];
// const CGRect bounds = CGDisplayBounds(aID);
// *x = bounds.origin.x;
// *y = bounds.origin.y;
// }
// }
//
// static bool isNativeFullscreen(uintptr_t windowPtr) {
// if (!windowPtr) {
// return false;
// }
// NSWindow* window = (NSWindow*)windowPtr;
// return (window.styleMask & NSWindowStyleMaskFullScreen) != 0;
// }
//
// static void setNativeFullscreen(uintptr_t windowPtr, bool fullscreen) {
// NSWindow* window = (NSWindow*)windowPtr;
// if (((window.styleMask & NSWindowStyleMaskFullScreen) != 0) == fullscreen) {
// return;
// }
//
// // Even though EbitengineWindowDelegate is used, this hack is still required.
// // toggleFullscreen doesn't work when the window is not resizable.
// bool origFullscreen = window.collectionBehavior & NSWindowCollectionBehaviorFullScreenPrimary;
// if (!origFullscreen) {
// window.collectionBehavior |= NSWindowCollectionBehaviorFullScreenPrimary;
// }
// [window toggleFullScreen:nil];
// if (!origFullscreen) {
// window.collectionBehavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
// }
// }
//
// static void adjustViewSize(uintptr_t windowPtr) {
// NSWindow* window = (NSWindow*)windowPtr;
// if ((window.styleMask & NSWindowStyleMaskFullScreen) == 0) {
// return;
// }
//
// // Apparently, adjusting the view size is not needed as of macOS 12 (#1745).
// static int majorVersion = 0;
// if (majorVersion == 0) {
// majorVersion = [[NSProcessInfo processInfo] operatingSystemVersion].majorVersion;
// }
// if (majorVersion >= 12) {
// return;
// }
//
// // Reduce the view height (#1745).
// // https://stackoverflow.com/questions/27758027/sprite-kit-serious-fps-issue-in-full-screen-mode-on-os-x
// CGSize windowSize = [window frame].size;
// NSView* view = [window contentView];
// CGSize viewSize = [view frame].size;
// if (windowSize.width != viewSize.width || windowSize.height != viewSize.height) {
// return;
// }
// viewSize.width--;
// [view setFrameSize:viewSize];
//
// // NSColor.blackColor (0, 0, 0, 1) didn't work.
// // Use the transparent color instead.
// [window setBackgroundColor: [NSColor colorWithSRGBRed:0 green:0 blue:0 alpha:0]];
// }
//
// static void setNativeCursor(int cursorID) {
// id cursor = [[NSCursor class] performSelector:@selector(arrowCursor)];
// switch (cursorID) {
// case 0:
// cursor = [[NSCursor class] performSelector:@selector(arrowCursor)];
// break;
// case 1:
// cursor = [[NSCursor class] performSelector:@selector(IBeamCursor)];
// break;
// case 2:
// cursor = [[NSCursor class] performSelector:@selector(crosshairCursor)];
// break;
// case 3:
// cursor = [[NSCursor class] performSelector:@selector(pointingHandCursor)];
// break;
// case 4:
// cursor = [[NSCursor class] performSelector:@selector(_windowResizeEastWestCursor)];
// break;
// case 5:
// cursor = [[NSCursor class] performSelector:@selector(_windowResizeNorthSouthCursor)];
// break;
// }
// [cursor push];
// }
//
// static void currentMouseLocation(int* x, int* y) {
// NSPoint location = [NSEvent mouseLocation];
// *x = (int)(location.x);
// *y = (int)(location.y);
// }
//
// static void setAllowFullscreen(uintptr_t windowPtr, bool allowFullscreen) {
// NSWindow* window = (NSWindow*)windowPtr;
// if (allowFullscreen) {
// window.collectionBehavior |= NSWindowCollectionBehaviorFullScreenPrimary;
// } else {
// window.collectionBehavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
// }
// }
import "C"
import (
"fmt"
"unsafe"
"github.com/ebitengine/purego/objc"
"github.com/hajimehoshi/ebiten/v2/internal/cocoa"
"github.com/hajimehoshi/ebiten/v2/internal/glfw"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl"
)
var class_EbitengineWindowDelegate objc.Class
func init() {
class_EbitengineWindowDelegate = objc.AllocateClassPair(objc.GetClass("NSObject"), "EbitengineWindowDelegate", 0)
protocol := objc.GetProtocol("NSWindowDelegate")
class_EbitengineWindowDelegate.AddProtocol(protocol)
// TODO: remove these Ivars and place them in Go since there is only ever one instance of this class
class_EbitengineWindowDelegate.AddIvar("origDelegate", objc.ID(0), "@")
class_EbitengineWindowDelegate.AddIvar("origResizable", false, "B")
origDelegateOffset := class_EbitengineWindowDelegate.InstanceVariable("origDelegate").Offset()
origResizableOffset := class_EbitengineWindowDelegate.InstanceVariable("origResizable").Offset()
getOrigDelegate := func(self objc.ID) objc.ID {
selfPtr := *(**uintptr)(unsafe.Pointer(&self))
return *(*objc.ID)(unsafe.Pointer(uintptr(unsafe.Pointer(selfPtr)) + origDelegateOffset))
}
getResizable := func(self objc.ID) bool {
selfPtr := *(**uintptr)(unsafe.Pointer(&self))
return *(*bool)(unsafe.Pointer(uintptr(unsafe.Pointer(selfPtr)) + origResizableOffset))
}
setResizable := func(self objc.ID, resizable bool) {
selfPtr := *(**uintptr)(unsafe.Pointer(&self))
*(*bool)(unsafe.Pointer(uintptr(unsafe.Pointer(selfPtr)) + origResizableOffset)) = resizable
}
pushResizableState := func(self, w objc.ID) {
window := cocoa.NSWindow{ID: w}
setResizable(self, window.StyleMask()&cocoa.NSWindowStyleMaskResizable != 0)
if !getResizable(self) {
window.SetStyleMask(window.StyleMask() | cocoa.NSWindowStyleMaskResizable)
}
}
popResizableState := func(self, w objc.ID) {
window := cocoa.NSWindow{ID: w}
if !getResizable(self) {
window.SetStyleMask(window.StyleMask() & ^uint(cocoa.NSWindowStyleMaskResizable))
}
setResizable(self, false)
}
class_EbitengineWindowDelegate.AddMethod(objc.RegisterName("initWithOrigDelegate:"), objc.NewIMP(func(self objc.ID, cmd objc.SEL, origDelegate objc.ID) objc.ID {
self = self.SendSuper(objc.RegisterName("init"))
if self != 0 {
selfPtr := *(**uintptr)(unsafe.Pointer(&self))
*(*objc.ID)(unsafe.Pointer(uintptr(unsafe.Pointer(selfPtr)) + origDelegateOffset)) = origDelegate
}
return self
}), "@@:B")
// The method set of origDelegate_ must sync with GLFWWindowDelegate's implementation.
// See cocoa_window.m in GLFW.
class_EbitengineWindowDelegate.AddMethod(objc.RegisterName("windowShouldClose:"), objc.NewIMP(func(self objc.ID, cmd objc.SEL, notification objc.ID) int {
return int(getOrigDelegate(self).Send(objc.RegisterName("windowShouldClose:"), notification))
}), "B@:@")
class_EbitengineWindowDelegate.AddMethod(objc.RegisterName("windowDidResize:"), objc.NewIMP(func(self objc.ID, cmd objc.SEL, notification objc.ID) {
getOrigDelegate(self).Send(cmd, notification)
}), "v@:@")
class_EbitengineWindowDelegate.AddMethod(objc.RegisterName("windowDidMove:"), objc.NewIMP(func(self objc.ID, cmd objc.SEL, notification objc.ID) {
getOrigDelegate(self).Send(cmd, notification)
}), "v@:@")
class_EbitengineWindowDelegate.AddMethod(objc.RegisterName("windowDidMiniaturize:"), objc.NewIMP(func(self objc.ID, cmd objc.SEL, notification objc.ID) {
getOrigDelegate(self).Send(cmd, notification)
}), "v@:@")
class_EbitengineWindowDelegate.AddMethod(objc.RegisterName("windowDidDeminiaturize:"), objc.NewIMP(func(self objc.ID, cmd objc.SEL, notification objc.ID) {
getOrigDelegate(self).Send(cmd, notification)
}), "v@:@")
class_EbitengineWindowDelegate.AddMethod(objc.RegisterName("windowDidBecomeKey:"), objc.NewIMP(func(self objc.ID, cmd objc.SEL, notification objc.ID) {
getOrigDelegate(self).Send(cmd, notification)
}), "v@:@")
class_EbitengineWindowDelegate.AddMethod(objc.RegisterName("windowDidResignKey:"), objc.NewIMP(func(self objc.ID, cmd objc.SEL, notification objc.ID) {
getOrigDelegate(self).Send(cmd, notification)
}), "v@:@")
class_EbitengineWindowDelegate.AddMethod(objc.RegisterName("windowDidChangeOcclusionState:"), objc.NewIMP(func(self objc.ID, cmd objc.SEL, notification objc.ID) {
getOrigDelegate(self).Send(cmd, notification)
}), "v@:@")
class_EbitengineWindowDelegate.AddMethod(objc.RegisterName("windowWillEnterFullScreen:"), objc.NewIMP(func(self objc.ID, cmd objc.SEL, notification objc.ID) {
pushResizableState(self, cocoa.NSNotification{ID: notification}.Object())
}), "v@:@")
class_EbitengineWindowDelegate.AddMethod(objc.RegisterName("windowDidEnterFullScreen:"), objc.NewIMP(func(self objc.ID, cmd objc.SEL, notification objc.ID) {
popResizableState(self, cocoa.NSNotification{ID: notification}.Object())
}), "v@:@")
class_EbitengineWindowDelegate.AddMethod(objc.RegisterName("windowWillExitFullScreen:"), objc.NewIMP(func(self objc.ID, cmd objc.SEL, notification objc.ID) {
pushResizableState(self, cocoa.NSNotification{ID: notification}.Object())
}), "v@:@")
class_EbitengineWindowDelegate.AddMethod(objc.RegisterName("windowDidExitFullScreen:"), objc.NewIMP(func(self objc.ID, cmd objc.SEL, notification objc.ID) {
popResizableState(self, cocoa.NSNotification{ID: notification}.Object())
// Do not call setFrame here (#2295). setFrame here causes unexpected results.
}), "v@:@")
class_EbitengineWindowDelegate.Register()
}
type graphicsDriverCreatorImpl struct {
transparent bool
}
@ -300,10 +183,22 @@ func flipY(y int) int {
return y
}
var class_NSEvent = objc.GetClass("NSEvent")
var sel_mouseLocation = objc.RegisterName("mouseLocation")
func currentMouseLocation() (x, y int) {
sig := cocoa.NSMethodSignature_signatureWithObjCTypes("{NSPoint=dd}@:")
inv := cocoa.NSInvocation_invocationWithMethodSignature(sig)
inv.SetTarget(objc.ID(class_NSEvent))
inv.SetSelector(sel_mouseLocation)
inv.Invoke()
var point cocoa.NSPoint
inv.GetReturnValue(unsafe.Pointer(&point))
return int(point.X), int(point.Y)
}
func initialMonitorByOS() (*glfw.Monitor, error) {
var cx, cy C.int
C.currentMouseLocation(&cx, &cy)
x, y := int(cx), flipY(int(cy))
x, y := currentMouseLocation()
// Find the monitor including the cursor.
for _, m := range ensureMonitors() {
@ -317,10 +212,20 @@ func initialMonitorByOS() (*glfw.Monitor, error) {
}
func monitorFromWindowByOS(w *glfw.Window) *glfw.Monitor {
var x, y C.int
C.currentMonitorPos(C.uintptr_t(w.GetCocoaWindow()), &x, &y)
window := cocoa.NSWindow{ID: objc.ID(w.GetCocoaWindow())}
pool := cocoa.NSAutoreleasePool_new()
screen := cocoa.NSScreen_mainScreen()
if window.ID != 0 && window.IsVisibile() {
// When the window is visible, the window is already initialized.
// [NSScreen mainScreen] sometimes tells a lie when the window is put across monitors (#703).
screen = window.Screen()
}
screenDictionary := screen.DeviceDescription()
screenID := cocoa.NSNumber{ID: screenDictionary.ObjectForKey(cocoa.NSString_alloc().InitWithUTF8String("NSScreenNumber").ID)}
aID := uintptr(screenID.UnsignedIntValue()) // CGDirectDisplayID
pool.Release()
for _, m := range ensureMonitors() {
if int(x) == m.x && int(y) == m.y {
if m.m.GetCocoaMonitor() == aID {
return m.m
}
}
@ -332,11 +237,29 @@ func (u *userInterfaceImpl) nativeWindow() uintptr {
}
func (u *userInterfaceImpl) isNativeFullscreen() bool {
return bool(C.isNativeFullscreen(C.uintptr_t(u.window.GetCocoaWindow())))
return cocoa.NSWindow{ID: objc.ID(u.window.GetCocoaWindow())}.StyleMask()&cocoa.NSWindowStyleMaskFullScreen != 0
}
func (u *userInterfaceImpl) setNativeCursor(shape CursorShape) {
C.setNativeCursor(C.int(shape))
class_NSCursor := objc.GetClass("NSCursor")
NSCursor := objc.ID(class_NSCursor).Send(objc.RegisterName("class"))
sel_performSelector := objc.RegisterName("performSelector:")
cursor := NSCursor.Send(sel_performSelector, objc.RegisterName("arrowCursor"))
switch shape {
case 0:
cursor = NSCursor.Send(sel_performSelector, objc.RegisterName("arrowCursor"))
case 1:
cursor = NSCursor.Send(sel_performSelector, objc.RegisterName("IBeamCursor"))
case 2:
cursor = NSCursor.Send(sel_performSelector, objc.RegisterName("crosshairCursor"))
case 3:
cursor = NSCursor.Send(sel_performSelector, objc.RegisterName("pointHandCursor"))
case 4:
cursor = NSCursor.Send(sel_performSelector, objc.RegisterName("_windowResizeEastWestCursor"))
case 5:
cursor = NSCursor.Send(sel_performSelector, objc.RegisterName("_windowResizeNorthSouthCursor"))
}
cursor.Send(objc.RegisterName("push"))
}
func (u *userInterfaceImpl) isNativeFullscreenAvailable() bool {
@ -345,27 +268,75 @@ func (u *userInterfaceImpl) isNativeFullscreenAvailable() bool {
return true
}
var sel_collectionBehavior = objc.RegisterName("collectionBehavior")
var sel_setCollectionBehavior = objc.RegisterName("setCollectionBehavior:")
func (u *userInterfaceImpl) setNativeFullscreen(fullscreen bool) {
// Toggling fullscreen might ignore events like keyUp. Ensure that events are fired.
glfw.WaitEventsTimeout(0.1)
C.setNativeFullscreen(C.uintptr_t(u.window.GetCocoaWindow()), C.bool(fullscreen))
window := cocoa.NSWindow{ID: objc.ID(u.window.GetCocoaWindow())}
if window.StyleMask()&cocoa.NSWindowStyleMaskFullScreen != 0 == fullscreen {
return
}
// Even though EbitengineWindowDelegate is used, this hack is still required.
// toggleFullscreen doesn't work when the window is not resizable.
origFullScreen := window.Send(sel_collectionBehavior)&cocoa.NSWindowCollectionBehaviorFullScreenPrimary != 0
if !origFullScreen {
window.Send(sel_setCollectionBehavior, window.Send(sel_collectionBehavior)|cocoa.NSWindowCollectionBehaviorFullScreenPrimary)
}
window.Send(objc.RegisterName("toggleFullScreen:"), 0)
if !origFullScreen {
window.Send(sel_setCollectionBehavior, window.Send(sel_collectionBehavior)&cocoa.NSWindowCollectionBehaviorFullScreenPrimary)
}
}
func (u *userInterfaceImpl) adjustViewSize() {
if u.graphicsDriver.IsGL() {
return
}
C.adjustViewSize(C.uintptr_t(u.window.GetCocoaWindow()))
window := cocoa.NSWindow{ID: objc.ID(u.window.GetCocoaWindow())}
if window.StyleMask()&cocoa.NSWindowStyleMaskFullScreen == 0 {
return
}
// Apparently, adjusting the view size is not needed as of macOS 12 (#1745).
if cocoa.NSProcessInfo_processInfo().IsOperatingSystemAtLeastVersion(cocoa.NSOperatingSystemVersion{
Major: 12,
}) {
return
}
// Reduce the view height (#1745).
// https://stackoverflow.com/questions/27758027/sprite-kit-serious-fps-issue-in-full-screen-mode-on-os-x
windowSize := window.Frame().Size
view := window.ContentView()
viewSize := view.Frame().Size
if windowSize.Width != viewSize.Width || windowSize.Height != viewSize.Height {
return
}
viewSize.Width--
view.SetFrameSize(viewSize)
// NSColor.blackColor (0, 0, 0, 1) didn't work.
// Use the transparent color instead.
window.SetBackgroundColor(cocoa.NSColor_colorWithSRGBRedGreenBlueAlpha(0, 0, 0, 0))
}
func (u *userInterfaceImpl) setWindowResizingModeForOS(mode WindowResizingMode) {
allowFullscreen := mode == WindowResizingModeOnlyFullscreenEnabled ||
mode == WindowResizingModeEnabled
C.setAllowFullscreen(C.uintptr_t(u.window.GetCocoaWindow()), C.bool(allowFullscreen))
collectionBehavior := int(objc.ID(u.window.GetCocoaWindow()).Send(sel_collectionBehavior))
if allowFullscreen {
collectionBehavior |= cocoa.NSWindowCollectionBehaviorFullScreenPrimary
} else {
collectionBehavior &^= cocoa.NSWindowCollectionBehaviorFullScreenPrimary
}
objc.ID(u.window.GetCocoaWindow()).Send(objc.RegisterName("setCollectionBehavior:"), collectionBehavior)
}
func initializeWindowAfterCreation(w *glfw.Window) {
// TODO: Register NSWindowWillEnterFullScreenNotification and so on.
// Enable resizing temporary before making the window fullscreen.
C.initializeWindow(C.uintptr_t(w.GetCocoaWindow()))
nswindow := objc.ID(w.GetCocoaWindow())
sel_delegate := objc.RegisterName("delegate")
delegate := objc.ID(class_EbitengineWindowDelegate).Send(objc.RegisterName("alloc")).Send(objc.RegisterName("initWithOrigDelegate:"), nswindow.Send(sel_delegate))
nswindow.Send(objc.RegisterName("setDelegate:"), delegate)
}