From 2a8f104228d8b102799e10e95e4e5cdc5989e47b Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Sat, 23 Jan 2021 11:39:45 +1100 Subject: [PATCH] Fully port cocoa backend to ObjC --- misc/Window.m | 263 -------------------------------------------- misc/readme.md | 4 +- src/interop_cocoa.m | 100 ++++++++--------- 3 files changed, 45 insertions(+), 322 deletions(-) delete mode 100644 misc/Window.m diff --git a/misc/Window.m b/misc/Window.m deleted file mode 100644 index 7b64c5b1e..000000000 --- a/misc/Window.m +++ /dev/null @@ -1,263 +0,0 @@ -#include "Window.h" -#include "Platform.h" -#include "Input.h" -#include "Event.h" -#include "Logger.h" -#import - -// this is the source used to generate the raw code used in window.c -// clang -rewrite-objc winmm.m -// generates a winmm.cpp with the objective C code as C++ -// I also compile the game including this file and manually verify assembly output - -static NSApplication* appHandle; -static NSWindow* winHandle; -static int windowX, windowY; - -struct GraphicsMode { int R, G, B, A, IsIndexed; }; -extern void Window_CommonInit(void); -extern int Window_MapKey(UInt32 key); - -@interface ClassiCubeWindowDelegate : NSObject { } -@end - -@implementation ClassiCubeWindowDelegate -static void Window_RefreshBounds(void); - -- (void)windowDidResize:(NSNotification *)notification -{ - Window_RefreshBounds(); - Event_RaiseVoid(&WindowEvents.Resized); -} - -- (void)windowDidMove:(NSNotification *)notification -{ - Window_RefreshBounds(); - GLContext_Update(); -} - -- (void)windowDidBecomeKey:(NSNotification *)notification -{ - WindowInfo.Focused = true; - Event_RaiseVoid(&WindowEvents.FocusChanged); -} - -- (void)windowDidResignKey:(NSNotification *)notification -{ - WindowInfo.Focused = false; - Event_RaiseVoid(&WindowEvents.FocusChanged); -} - -- (void)windowDidMiniaturize:(NSNotification *)notification -{ - Event_RaiseVoid(&WindowEvents.StateChanged); -} - -- (void)windowDidDeminiaturize:(NSNotification *)notification -{ - Event_RaiseVoid(&WindowEvents.StateChanged); -} - -- (void)windowWillClose:(NSNotification *)notification -{ - Event_RaiseVoid(&WindowEvents.Closing); -} -@end - - -static void Window_RefreshBounds(void) { - NSView* view; - NSRect rect; - - view = [winHandle contentView]; - rect = [view bounds]; - rect = [winHandle convertRectToScreen: rect]; - - windowX = (int)rect.origin.x; - windowY = (int)rect.origin.y; - WindowInfo.Width = (int)rect.size.width; - WindowInfo.Height = (int)rect.size.height; - Platform_Log2("WINPOS: %i, %i", &windowX, &windowY); -} - -void Window_SetSize1(int width, int height) { - NSSize size; - size.width = width; size.height = height; - [winHandle setContentSize: size]; -} - -void Window_Close1(void) { - [winHandle close]; -} - -void Window_Init1(void) { - appHandle = [NSApplication sharedApplication]; - [appHandle activateIgnoringOtherApps: YES]; - Window_CommonInit(); -} - -#define Display_CentreX(width) (DisplayInfo.X + (DisplayInfo.Width - width) / 2) -#define Display_CentreY(height) (DisplayInfo.Y + (DisplayInfo.Height - height) / 2) - -void Window_Create1(int width, int height) { - NSRect rect; - // TODO: don't set, RefreshBounds - WindowInfo.Width = width; - WindowInfo.Height = height; - WindowInfo.Exists = true; - - rect.origin.x = Display_CentreX(width); - rect.origin.y = Display_CentreY(height); - rect.size.width = width; - rect.size.height = height; - // TODO: opentk seems to flip y? - - winHandle = [NSWindow alloc]; - [winHandle initWithContentRect: rect styleMask: NSTitledWindowMask|NSClosableWindowMask|NSResizableWindowMask|NSMiniaturizableWindowMask backing:0 defer:NO]; - - [winHandle makeKeyAndOrderFront: appHandle]; - Window_RefreshBounds(); -} - -void Window_SetTitle1(const String* title) { - UInt8 str[600]; - CFStringRef titleCF; - int len; - - /* TODO: This leaks memory, old title isn't released */ - len = Platform_ConvertString(str, title); - titleCF = CFStringCreateWithBytes(kCFAllocatorDefault, str, len, kCFStringEncodingUTF8, false); - [winHandle setTitle: (NSString*)titleCF]; -} - -static int Window_MapMouse(int button) { - if (button == 0) return KEY_LMOUSE; - if (button == 1) return KEY_RMOUSE; - if (button == 2) return KEY_MMOUSE; - return 0; -} - -void Window_ProcessEvents1(void) { - NSEvent* ev; - NSPoint loc; - CGFloat dx, dy; - int type, key, mouseX, mouseY; - - for (;;) { - ev = [appHandle nextEventMatchingMask: 0xFFFFFFFFU untilDate:Nil inMode:NSDefaultRunLoopMode dequeue:YES]; - if (!ev) break; - type = [ev type]; - - switch (type) { - case NSLeftMouseDown: - case NSRightMouseDown: - case NSOtherMouseDown: - key = Window_MapMouse([ev buttonNumber]); - if (key) Input_SetPressed(key, true); - break; - - case NSLeftMouseUp: - case NSRightMouseUp: - case NSOtherMouseUp: - key = Window_MapMouse([ev buttonNumber]); - if (key) Input_SetPressed(key, false); - break; - - case NSKeyDown: - key = Window_MapKey([ev keyCode]); - if (key) Input_SetPressed(key, true); - break; - - case NSKeyUp: - key = Window_MapKey([ev keyCode]); - if (key) Input_SetPressed(key, false); - break; - - case NSScrollWheel: - Mouse_ScrollWheel([ev deltaY]); - break; - - case NSMouseMoved: - case NSLeftMouseDragged: - case NSRightMouseDragged: - case NSOtherMouseDragged: - loc = [NSEvent mouseLocation]; - dx = [ev deltaX]; - dy = [ev deltaY]; - - mouseX = (int)loc.x - windowX; - mouseY = (int)loc.y - windowY; - /* need to flip Y coordinates because cocoa has window origin at bottom left */ - mouseY = WindowInfo.Height - mouseY; - Pointer_SetPosition(0, mouseX, mouseY); - - if (Input_RawMode) Event_RaiseRawMove(&PointerEvents.RawMoved, dx, dy); - break; - } - - Platform_Log1("EVENT: %i", &type); - [appHandle sendEvent:ev]; - } -} - -static NSOpenGLContext* ctxHandle; -static NSOpenGLPixelFormat* SelectPixelFormat(struct GraphicsMode* mode, bool fullscreen) { - NSOpenGLPixelFormat* fmt; - uint32_t attribs[7] = { - NSOpenGLPFAColorSize, 0, - NSOpenGLPFADepthSize, GLCONTEXT_DEFAULT_DEPTH, - NSOpenGLPFADoubleBuffer, 0, 0 - }; - - attribs[1] = mode->R + mode->G + mode->B + mode->A; - attribs[5] = fullscreen ? NSOpenGLPFAFullScreen : 0; - - fmt = [NSOpenGLPixelFormat alloc]; - return [fmt initWithAttributes: attribs]; -} - -void GLContext_Init1(struct GraphicsMode* mode) { - NSView* view; - NSOpenGLPixelFormat* fmt; - - fmt = SelectPixelFormat(mode, true); - if (!fmt) { - Platform_LogConst("Failed to create full screen pixel format."); - Platform_LogConst("Trying again to create a non-fullscreen pixel format."); - fmt = SelectPixelFormat(mode, false); - } - if (!fmt) Logger_Abort("Choosing pixel format"); - - ctxHandle = [NSOpenGLContext alloc]; - ctxHandle = [ctxHandle initWithFormat:fmt shareContext:Nil]; - if (!ctxHandle) Logger_Abort("Failed to create OpenGL context"); - - view = [winHandle contentView]; - [ctxHandle setView:view]; - /* TODO: Support high DPI OSX */ - /* [ctxHandle setWantsBestResolutionOpenGLSurface:YES]; */ - - [fmt release]; - [ctxHandle makeCurrentContext]; - [ctxHandle update]; -} - -void GLContext_Update1(void) { - [ctxHandle update]; -} - -void GLContext_Free1(void) { - [NSOpenGLContext clearCurrentContext]; - [ctxHandle clearDrawable]; - [ctxHandle release]; -} - -bool GLContext_SwapBuffers1(void) { - [ctxHandle flushBuffer]; - return true; -} - -void GLContext_SetFpsLimit1(bool vsync, float minFrameMs) { - GLint value = vsync ? 1 : 0; - [ctxHandle setValues: &value forParameter: NSOpenGLCPSwapInterval]; -} diff --git a/misc/readme.md b/misc/readme.md index e3d02c522..e21089b45 100644 --- a/misc/readme.md +++ b/misc/readme.md @@ -20,6 +20,4 @@ TODO: Explain how to compile your own icon for all the platforms ## Other files -Info.plist is the Info.plist you would use when creating an Application Bundle for macOS. - -Window.m was the original basis code for the Cocoa window backend. \ No newline at end of file +Info.plist is the Info.plist you would use when creating an Application Bundle for macOS. \ No newline at end of file diff --git a/src/interop_cocoa.m b/src/interop_cocoa.m index 783751415..70b146d50 100644 --- a/src/interop_cocoa.m +++ b/src/interop_cocoa.m @@ -1,4 +1,5 @@ #include "Logger.h" +#include "ExtMath.h" #include "Platform.h" #include "Window.h" #include "Input.h" @@ -6,9 +7,6 @@ #include "Bitmap.h" #include "String.h" #include -#include -#include -#include /*########################################################################################################################* *-------------------------------------------------------Cocoa window------------------------------------------------------* @@ -41,93 +39,83 @@ static void RefreshWindowBounds(void) { WindowInfo.Height = (int)view.size.height; } -static void OnDidResize(id self, SEL cmd, id notification) { +@interface CCWindow : NSWindow { } +@end +@implementation CCWindow +/* If this isn't overriden, an annoying beep sound plays anytime a key is pressed */ +- (void)keyDown:(NSEvent *)event { } +@end + +@interface CCWindowDelegate : NSObject { } +@end +@implementation CCWindowDelegate +- (void)windowDidResize:(NSNotification *)notification { RefreshWindowBounds(); Event_RaiseVoid(&WindowEvents.Resized); } -static void OnDidMove(id self, SEL cmd, id notification) { +- (void)windowDidMove:(NSNotification *)notification { RefreshWindowBounds(); GLContext_Update(); } -static void OnDidBecomeKey(id self, SEL cmd, id notification) { +- (void)windowDidBecomeKey:(NSNotification *)notification { WindowInfo.Focused = true; Event_RaiseVoid(&WindowEvents.FocusChanged); } -static void OnDidResignKey(id self, SEL cmd, id notification) { +- (void)windowDidResignKey:(NSNotification *)notification { WindowInfo.Focused = false; Event_RaiseVoid(&WindowEvents.FocusChanged); } -static void OnDidMiniaturize(id self, SEL cmd, id notification) { +- (void)windowDidMiniaturize:(NSNotification *)notification { Event_RaiseVoid(&WindowEvents.StateChanged); } -static void OnDidDeminiaturize(id self, SEL cmd, id notification) { +- (void)windowDidDeminiaturize:(NSNotification *)notification { Event_RaiseVoid(&WindowEvents.StateChanged); } -static void OnWillClose(id self, SEL cmd, id notification) { +- (void)windowWillClose:(NSNotification *)notification { WindowInfo.Exists = false; Event_RaiseVoid(&WindowEvents.Closing); } +@end -/* If this isn't overriden, an annoying beep sound plays anytime a key is pressed */ -static void OnKeyDown(id self, SEL cmd, id ev) { } -static Class Window_MakeClass(void) { - Class c = objc_allocateClassPair(objc_getClass("NSWindow"), "ClassiCube_Window", 0); +static void DoDrawFramebuffer(CGRect dirty); +@interface CCView : NSView { } +@end +@implementation CCView - class_addMethod(c, sel_registerName("windowDidResize:"), OnDidResize, "v@:@"); - class_addMethod(c, sel_registerName("windowDidMove:"), OnDidMove, "v@:@"); - class_addMethod(c, sel_registerName("windowDidBecomeKey:"), OnDidBecomeKey, "v@:@"); - class_addMethod(c, sel_registerName("windowDidResignKey:"), OnDidResignKey, "v@:@"); - class_addMethod(c, sel_registerName("windowDidMiniaturize:"), OnDidMiniaturize, "v@:@"); - class_addMethod(c, sel_registerName("windowDidDeminiaturize:"), OnDidDeminiaturize, "v@:@"); - class_addMethod(c, sel_registerName("windowWillClose:"), OnWillClose, "v@:@"); - class_addMethod(c, sel_registerName("keyDown:"), OnKeyDown, "v@:@"); +- (void)drawRect:(CGRect)dirty { DoDrawFramebuffer(dirty); } - objc_registerClassPair(c); - return c; -} - -/* When the user users left mouse to drag reisze window, this enters 'live resize' mode */ -/* Although the game receives a left mouse down event, it does NOT receive a left mouse up */ -/* This causes the game to get stuck with left mouse down after user finishes resizing */ -/* So work arond that by always releasing left mouse when a live resize is finished */ -static void DidEndLiveResize(id self, SEL cmd) { +- (void)viewDidEndLiveResize { + /* When the user users left mouse to drag reisze window, this enters 'live resize' mode */ + /* Although the game receives a left mouse down event, it does NOT receive a left mouse up */ + /* This causes the game to get stuck with left mouse down after user finishes resizing */ + /* So work arond that by always releasing left mouse when a live resize is finished */ Input_SetReleased(KEY_LMOUSE); } +@end + -static void View_DrawRect(id self, SEL cmd, CGRect r); static void MakeContentView(void) { CGRect rect; - id view; - Class c; + NSView* view; view = [winHandle contentView]; rect = [view frame]; - - c = objc_allocateClassPair(objc_getClass("NSView"), "ClassiCube_View", 0); - // TODO: test rect is actually correct in View_DrawRect on both 32 and 64 bit -#ifdef __i386__ - class_addMethod(c, sel_registerName("drawRect:"), View_DrawRect, "v@:{NSRect={NSPoint=ff}{NSSize=ff}}"); -#else - class_addMethod(c, sel_registerName("drawRect:"), View_DrawRect, "v@:{NSRect={NSPoint=dd}{NSSize=dd}}"); -#endif - class_addMethod(c, sel_registerName("viewDidEndLiveResize"), DidEndLiveResize, "v@:"); - objc_registerClassPair(c); - viewHandle = [c alloc]; + viewHandle = [CCView alloc]; [viewHandle initWithFrame:rect]; [winHandle setContentView:viewHandle]; } void Window_Init(void) { appHandle = [NSApplication sharedApplication]; - [appHandle activateIgnoringOtherApps:true]; + [appHandle activateIgnoringOtherApps:YES]; Window_CommonInit(); } @@ -164,7 +152,7 @@ static void ApplyIcon(void) { } #define WIN_MASK (NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask) void Window_Create(int width, int height) { - Class winClass; + CCWindowDelegate* del; CGRect rect; /* Technically the coordinates for the origin are at bottom left corner */ @@ -174,12 +162,12 @@ void Window_Create(int width, int height) { rect.size.width = width; rect.size.height = height; - winClass = Window_MakeClass(); - winHandle = [winClass alloc]; + winHandle = [CCWindow alloc]; [winHandle initWithContentRect:rect styleMask:WIN_MASK backing:0 defer:false]; Window_CommonCreate(); - [winHandle setDelegate:winHandle]; + del = [CCWindowDelegate alloc]; + [winHandle setDelegate:del]; RefreshWindowBounds(); MakeContentView(); ApplyIcon(); @@ -228,7 +216,7 @@ void Window_SetSize(int width, int height) { rect.origin.y += WindowInfo.Height - height; rect.size.width += width - WindowInfo.Width; rect.size.height += height - WindowInfo.Height; - [winHandle setFrame:rect display:true]; + [winHandle setFrame:rect display:YES]; } void Window_Close(void) { @@ -246,7 +234,7 @@ static void ProcessKeyChars(id ev) { char buffer[128]; const char* src; cc_string str; - id chars; + NSString* chars; int i, len, flags; /* Ignore text input while cmd is held down */ @@ -289,7 +277,7 @@ void Window_ProcessEvents(void) { CGFloat dx, dy; for (;;) { - ev = [appHandle nextEventMatchingMask:0xFFFFFFFFU untilDate:NULL inMode:NSDefaultRunLoopMode dequeue:true]; + ev = [appHandle nextEventMatchingMask:0xFFFFFFFFU untilDate:Nil inMode:NSDefaultRunLoopMode dequeue:YES]; if (!ev) break; type = [ev type]; @@ -388,13 +376,13 @@ void Window_AllocFramebuffer(struct Bitmap* bmp) { fb_bmp = *bmp; } -static void View_DrawRect(id self, SEL cmd, CGRect r_) { +static void DoDrawFramebuffer(CGRect dirty) { CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGContextRef context = NULL; CGDataProviderRef provider; + NSGraphicsContext* nsContext; CGImageRef image; CGRect rect; - id nsContext; /* Unfortunately CGImageRef is immutable, so changing the */ /* underlying data doesn't change what shows when drawing. */ @@ -466,7 +454,7 @@ void GLContext_Create(void) { if (!fmt) Logger_Abort("Choosing pixel format"); ctxHandle = [NSOpenGLContext alloc]; - ctxHandle = [ctxHandle initWithFormat:fmt shareContext:NULL]; + ctxHandle = [ctxHandle initWithFormat:fmt shareContext:Nil]; if (!ctxHandle) Logger_Abort("Failed to create OpenGL context"); [ctxHandle setView:viewHandle];