ClassiCube/misc/Window.m
UnknownShadow200 3ea64a61d0 Generate a 64 bit mac icon file and fixup Window.m
64 mac build doesn't show icon quite yet, still WIP
2020-06-11 23:38:33 +10:00

263 lines
6.9 KiB
Objective-C

#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];
}