// C/C++ API for Mac OS X Windowing - windowing.cpp
// Created by John Ryland (jryland@xiaofrog.com), 29/10/2017
// Copyright (c) 2017 InvertedLogic
// All rights reserved.
#include <cstdlib>
#include <cstdio>
#include <objc/message.h>
#include <objc/runtime.h>
#include <ApplicationServices/ApplicationServices.h>
#include "windowing.h"
#include <OpenGL/gl.h>
Window::~Window()
{
}
void Window::flush()
{
#if USE_OPENGL
id ctx = objc_msgSend((id)view, sel_getUid("openGLContext"));
objc_msgSend(ctx, sel_getUid("makeCurrentContext"));
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
glDrawPixels(buffer.w, buffer.h, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer.pixels.data());
glFlush();
objc_msgSend(ctx, sel_getUid("flushBuffer"));
#else
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef bitmapContext = CGBitmapContextCreate(buffer.pixels.data(), buffer.w, buffer.h, 8, 4*buffer.w, colorSpace, kCGImageAlphaNoneSkipLast);
CFRelease(colorSpace);
CGImageRef imageRef = CGBitmapContextCreateImage(bitmapContext);
id gfxContext = objc_msgSend((id)objc_getClass("NSGraphicsContext"), sel_getUid("currentContext"));
// graphicsPort is deprecated and since 10.10, docs say the new API is CGContext
CGContextRef ctx = (CGContextRef)objc_msgSend(gfxContext, sel_getUid("graphicsPort"));
CGContextDrawImage(ctx, CGRectMake(0, 0, buffer.w, buffer.h), imageRef);
CGImageRelease(imageRef);
#endif
}
struct NSRect { double x, y, width, height; };
void WindowOnDraw(id self, SEL _cmd, NSRect rect)
{
Window* winPtr = 0;
object_getInstanceVariable(self, "WindowPtr", (void**)&winPtr);
winPtr->onDraw(int(rect.x), int(rect.y), int(rect.x + rect.width), int(rect.y + rect.height));
winPtr->flush();
}
struct NSSize { double width, height; };
void WindowOnResize(id self, SEL _cmd, NSSize newSize)
{
Window* winPtr = 0;
object_getInstanceVariable(self, "WindowPtr", (void**)&winPtr);
winPtr->onResize(int(newSize.width), int(newSize.height));
winPtr->onDraw(0, 0, winPtr->getWidth(), winPtr->getHeight());
}
struct NSEvent;
struct NSPoint { double x, y; };
void WindowOnMouseEvent(id self, SEL _cmd, NSEvent* event)
{
Window* winPtr = 0;
object_getInstanceVariable(self, "WindowPtr", (void**)&winPtr);
id nsEvent = objc_getClass("NSEvent");
NSPoint pnt = (*(NSPoint(*)(id, SEL, ...))&objc_msgSend)((id)event, sel_getUid("locationInWindow"));
unsigned buttons = (*(unsigned(*)(id, SEL, ...))&objc_msgSend)(nsEvent, sel_getUid("pressedMouseButtons"));
winPtr->onMouseEvent(int(pnt.x), winPtr->getHeight()-int(pnt.y), buttons);
}
void Window::onResize(int w, int h)
{
buffer.pixels.resize(w*h);
buffer.w = w;
buffer.h = h;
}
void Window::refresh()
{
objc_msgSend((id)view, sel_getUid("setNeedsDisplay:"), YES);
}
void Window::create(int w, int h)
{
// NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask | NSWindowStyleMaskFullSizeContentView
unsigned windowFlags = (1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<15);
Window::onResize(w, h);
window = objc_msgSend((id)objc_getClass("NSWindow"), sel_getUid("alloc"));
window = objc_msgSend((id)window, sel_getUid("initWithContentRect:styleMask:backing:defer:"), (NSRect){0,0,w,h}, windowFlags, 2, false);
view = objc_msgSend(objc_msgSend((id)objc_getClass("View"), sel_getUid("alloc")), sel_getUid("initWithFrame:"), (NSRect){0,0,w,h});
object_setInstanceVariable((id)view, "WindowPtr", (void*)this);
objc_msgSend((id)window, sel_getUid("setContentView:"), view);
objc_msgSend((id)window, sel_getUid("becomeFirstResponder"));
objc_msgSend((id)window, sel_getUid("makeKeyAndOrderFront:"), window);
}
BOOL AppDidFinishLaunching(id self, SEL _cmd, id notification)
{
Application* appPtr = 0;
object_getInstanceVariable(self, "ApplicationPtr", (void**)&appPtr);
printf("AppDidFinishLaunching %i\n", __LINE__);
appPtr->onStart();
printf("AppDidFinishLaunching %i\n", __LINE__);
return YES;
}
Application::Application()
{
Class AppDelClass;
AppDelClass = objc_allocateClassPair((Class)objc_getClass("NSObject"), "AppDelegate", 0);
class_addMethod(AppDelClass, sel_getUid("applicationDidFinishLaunching:"), (IMP)AppDidFinishLaunching, "i@:@");
class_addIvar(AppDelClass, "ApplicationPtr", sizeof(Application*), sizeof(Application*), "Application*");
objc_registerClassPair(AppDelClass);
Class ViewClass;
#if USE_OPENGL
ViewClass = objc_allocateClassPair((Class)objc_getClass("NSOpenGLView"), "View", 0);
#else
ViewClass = objc_allocateClassPair((Class)objc_getClass("NSView"), "View", 0);
#endif
class_addMethod(ViewClass, sel_getUid("drawRect:"), (IMP)WindowOnDraw, "v@:");
class_addMethod(ViewClass, sel_getUid("setFrameSize:"), (IMP)WindowOnResize, "v@:");
class_addMethod(ViewClass, sel_getUid("mouseDown:"), (IMP)WindowOnMouseEvent, "v@:");
class_addMethod(ViewClass, sel_getUid("mouseDragged:"), (IMP)WindowOnMouseEvent, "v@:");
class_addMethod(ViewClass, sel_getUid("mouseUp:"), (IMP)WindowOnMouseEvent, "v@:");
class_addMethod(ViewClass, sel_getUid("mouseMoved:"), (IMP)WindowOnMouseEvent, "v@:");
class_addIvar(ViewClass, "WindowPtr", sizeof(Window*), sizeof(Window*), "Window*");
objc_registerClassPair(ViewClass);
objc_msgSend((id)objc_getClass("NSApplication"), sel_getUid("sharedApplication"));
}
int Application::run()
{
extern id NSApp;
id pool = objc_msgSend((id)objc_getClass("NSAutoreleasePool"), sel_getUid("alloc"));
pool = objc_msgSend(pool, sel_getUid("init"));
id appDelObj = objc_msgSend((id)objc_getClass("AppDelegate"), sel_getUid("alloc"));
appDelObj = objc_msgSend(appDelObj, sel_getUid("init"));
object_setInstanceVariable(appDelObj, "ApplicationPtr", (void*)this);
objc_msgSend(NSApp, sel_getUid("setDelegate:"), appDelObj);
objc_msgSend(NSApp, sel_getUid("run"));
objc_msgSend(pool, sel_getUid("release"));
return EXIT_SUCCESS;
}