Fully port cocoa backend to ObjC

This commit is contained in:
UnknownShadow200 2021-01-23 11:39:45 +11:00
parent a58b0660a7
commit 2a8f104228
3 changed files with 45 additions and 322 deletions

View File

@ -1,263 +0,0 @@
#include "Window.h"
#include "Platform.h"
#include "Input.h"
#include "Event.h"
#include "Logger.h"
#import <Cocoa/Cocoa.h>
// 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];
}

View File

@ -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.
Info.plist is the Info.plist you would use when creating an Application Bundle for macOS.

View File

@ -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 <Cocoa/Cocoa.h>
#include <OpenGL/OpenGL.h>
#include <objc/message.h>
#include <objc/runtime.h>
/*########################################################################################################################*
*-------------------------------------------------------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];