Newer
Older
Import / research / match3 / windowing.cpp
//  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;
}