diff --git a/build_osx.sh b/build_osx.sh new file mode 100755 index 0000000..bc804d7 --- /dev/null +++ b/build_osx.sh @@ -0,0 +1,7 @@ +pushd build +clang++ ../osx/gs_osx.mm \ +-g \ +-Wno-c11-extensions -Wno-unused-variable -Wno-unused-function \ +-framework Cocoa -framework OpenGL \ +-o osx_foldhaus.out +popd \ No newline at end of file diff --git a/src/first.cpp b/src/first.cpp new file mode 100644 index 0000000..3501ced --- /dev/null +++ b/src/first.cpp @@ -0,0 +1,28 @@ +#include + +#include +#include + +typedef struct AppDel { + Class isa; + id window; +} AppDelegate; + +BOOL AppDel_didFinishLaunching(AppDelegate* self, SEL _command, id Notification) +{ + return YES; +} + +int main(int ArgCount, char** Args) +{ + Class NSApplicationClass = (Class)objc_getClass("NSApplication"); + Class AppDelegateClass = objc_allocateClassPair(NSApplicationClass, "AppDelegate", 0); + + SEL MethodSelector = sel_getUid("applicationDidFinishLaunching:"); + // NOTE(Peter): i = int, @ = object, : = method selector (SEL) + char MethodSignature[] = "i@:@"; + class_addMethod(AppDelegateClass, MethodSelector, (IMP)AppDel_didFinishLaunching, "i@:@"); + objc_registerClassPair(AppDelegateClass); + + return 0; +} \ No newline at end of file diff --git a/src/gs_osx.mm b/src/gs_osx.mm new file mode 100644 index 0000000..a95c79f --- /dev/null +++ b/src/gs_osx.mm @@ -0,0 +1,76 @@ +#include +#include "gs_osx_memory.mm" +#include "gs_osx_window.mm" +#include "gs_osx_fileio.mm" +#include "gs_osx_lib.mm" +#include "gs_osx_opengl.mm" +#include "gs_osx_time.mm" + +static void +gsosx_ProcessWindowEvents(NSApplication* App, NSWindow* Window) +{ + // Process Events + while (true) + { + NSEvent* Event = [App nextEventMatchingMask: NSEventMaskAny untilDate: [NSDate distantPast] inMode: NSDefaultRunLoopMode dequeue: YES]; + if (!Event) { break; } + + switch([Event type]) + { + case NSEventTypeKeyUp: + case NSEventTypeKeyDown: + { + // TODO: Handle Key Presses + }break; + + // TODO: Mouse Input + + default: + { + [App sendEvent: Event]; + }break; + } + } +} + +int main(int ArgCount, char** Args) +{ + NSApplication* Application = [NSApplication sharedApplication]; + [Application setActivationPolicy: NSApplicationActivationPolicyRegular]; + + gsosx_ApplicationDelegate* Delegate = [[gsosx_ApplicationDelegate alloc] init]; + [Application setDelegate: Delegate]; + + int WindowWidth = 1024; + int WindowHeight = 768; + id AppName = @"Lumenarium"; + NSWindow* Window = gsosx_CreateWindow(Application, WindowWidth, WindowHeight, AppName); + + // A really cryptic way of asking the window to open + [Window makeKeyAndOrderFront: Application]; + + NSOpenGLContext* OpenGLContext = gsoo_CreateOpenGLContext(Window, WindowWidth, WindowHeight, true); + + gsosx_time_info TimeInfo = gsosx_InitTime(); + double TargetSecondsPerFrame = 1.0 / 60; + uint64_t LastFrameEnd = gsosx_GetTime(TimeInfo); + while (true) + { + gsosx_ProcessWindowEvents(Application, Window); + + glClearColor(1, 0, 1, 1); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + gsoo_SwapBuffers(OpenGLContext); + + uint64_t ThisFrameEnd = gsosx_GetTime(TimeInfo); + double FrameSeconds = gsosx_GetSecondsElapsed(LastFrameEnd, ThisFrameEnd, TimeInfo); + double SleepSeconds = TargetSecondsPerFrame - FrameSeconds; + if (SleepSeconds > 0) + { + gsosx_Sleep(SleepSeconds, TimeInfo); + } + } + + return (0); +} diff --git a/src/gs_osx_fileio.mm b/src/gs_osx_fileio.mm new file mode 100644 index 0000000..c5ce939 --- /dev/null +++ b/src/gs_osx_fileio.mm @@ -0,0 +1,63 @@ +#include + +static uint32_t +gsosx_GetLastFileWriteTime(char* Path) +{ + int32_t Result = 0; + struct stat FileStat = {0}; + if (stat(Path, &FileStat) == 0) + { + Result = FileStat.st_mtimespec.tv_sec; + } + else + { + // TODO: Asserts + } + return Result; +} + +static uint32_t +gsosx_GetFilesize(char* Path) +{ + uint32_t Result = 0; + + int FileHandle = open(Path, O_RDONLY); + struct stat FileStat = {0}; + fstat(FileHandle, &FileStat); + close(FileHandle); + + Result = (uint32_t)FileStat.st_size; + return Result; +} + +static bool +gsosx_LoadFileIntoMemory(char* Path, uint32_t FileSize, uint8_t* FileMemory) +{ + bool Result = false; + int FileHandle = open(Path, O_RDONLY); + + struct stat FileStat = {0}; + fstat(FileHandle, &FileStat); + if (FileStat.st_size <= FileSize) + { + read(FileHandle, FileMemory, FileSize); + Result = true; + } + close(FileHandle); + + return Result; +} + +static bool +gsosx_WriteEntireFile(char* Path, uint32_t FileSize, uint8_t* FileMemory) +{ + bool Result = false; + int FileHandle = open(Path, O_WRONLY | O_CREAT, 0777); + ssize_t SizeWritten = write(FileHandle, FileMemory, FileSize); + if (SizeWritten == FileSize) + { + Result = true; + } + close(FileHandle); + return Result; +} \ No newline at end of file diff --git a/src/gs_osx_lib.mm b/src/gs_osx_lib.mm new file mode 100644 index 0000000..8758af3 --- /dev/null +++ b/src/gs_osx_lib.mm @@ -0,0 +1,30 @@ +#include + +static void* +gsosx_LoadDLL(char* Path) +{ + void* LibHandle = 0; + + LibHandle = dlopen(Path, RTLD_LAZY); + if (LibHandle) + { + dlerror(); // Clear Last Error + } + else + { + LibHandle = 0; + } + + return LibHandle; +} + +#define gsosx_GetProcAddress(libHandle, type, name) (type*)dlsym((libHandle), name) + +static void +gsosx_UnloadDLL(void* LibHandle) +{ + if (LibHandle) + { + dlclose(LibHandle); + } +} \ No newline at end of file diff --git a/src/gs_osx_memory.mm b/src/gs_osx_memory.mm new file mode 100644 index 0000000..67b904a --- /dev/null +++ b/src/gs_osx_memory.mm @@ -0,0 +1,30 @@ +#include + +static uint8_t* +gsosx_Alloc(size_t Size) +{ + uint8_t* Result = 0; + char* StartAddress = (char*)0; + int Prot = PROT_READ | PROT_WRITE; + int Flags = MAP_PRIVATE | MAP_ANON; + Result = (uint8_t*)mmap(StartAddress, Size, Prot, Flags, -1, 0); + return Result; +} + +static void +gsosx_Free(uint8_t* Base, uint32_t Size) +{ + munmap((void*)Base, (size_t)Size); +} + +static uint8_t* +gsosx_Realloc(uint8_t* Base, uint32_t OldSize, uint32_t NewSize) +{ + uint8_t* Result = gsosx_Alloc(NewSize); + for (int32_t i = 0; i < OldSize; i++) + { + Result[i] = Base[i]; + } + gsosx_Free(Base, OldSize ); + return Result; +} \ No newline at end of file diff --git a/src/gs_osx_opengl.mm b/src/gs_osx_opengl.mm new file mode 100644 index 0000000..4f6cfe4 --- /dev/null +++ b/src/gs_osx_opengl.mm @@ -0,0 +1,65 @@ +#include + +struct gsoo_opengl_state +{ + NSOpenGLContext* Context; +}; + +@interface gsoo_OpenGLView: NSOpenGLView @end +@implementation gsoo_OpenGLView : NSOpenGLView + - (void) + reshape + { + // TODO: framebufferWidth and Height were globals in the code I was pulling from + // Need some way to get the new window height in here. + CGRect FrameRect = self.frame; + glViewport(0, 0, FrameRect.size.width, FrameRect.size.height); + //glViewport(0, 0, framebufferWidth, framebufferHeight); + } +@end + +static NSOpenGLContext* +gsoo_CreateOpenGLContext(NSWindow* Window, uint32_t Width, uint32_t Height, int32_t EnableVSync) +{ + NSOpenGLContext* Result = 0; + NSOpenGLPixelFormatAttribute PixelFormatAttributes[] = { + NSOpenGLPFAClosestPolicy, + NSOpenGLPFADoubleBuffer, + NSOpenGLPFASampleBuffers, + 0, + 0 + }; + + NSOpenGLPixelFormat* PixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes: PixelFormatAttributes]; + Result = [[NSOpenGLContext alloc] initWithFormat: PixelFormat shareContext: 0]; + + if (!Result) + { + // TODO: Assert/Handle + return 0; + } + + [Result makeCurrentContext]; + GLint VSync = EnableVSync; + [Result setValues: &VSync forParameter: NSOpenGLCPSwapInterval]; + + // Set Backbuffer Resolution + GLint BackbufferDimensions[] = { Width, Height }; + CGLSetParameter(Result.CGLContextObj, kCGLCPSurfaceBackingSize, BackbufferDimensions); + CGLEnable(Result.CGLContextObj, kCGLCESurfaceBackingSize); + + // + gsoo_OpenGLView* View = [[gsoo_OpenGLView alloc] init]; + [Window setContentView: View]; + [View setOpenGLContext: Result]; + [View setPixelFormat: PixelFormat]; + [Result setView: View]; + + return Result; +} + +static void +gsoo_SwapBuffers(NSOpenGLContext* OpenGLContext) +{ + [OpenGLContext flushBuffer]; +} \ No newline at end of file diff --git a/src/gs_osx_time.mm b/src/gs_osx_time.mm new file mode 100644 index 0000000..734e268 --- /dev/null +++ b/src/gs_osx_time.mm @@ -0,0 +1,46 @@ +#include + +static const uint64_t NANOS_PER_USEC = 1000ULL; +static const uint64_t NANOS_PER_MILLISEC = 1000ULL * NANOS_PER_USEC; +static const uint64_t NANOS_PER_SEC = 1000ULL * NANOS_PER_MILLISEC; + +struct gsosx_time_info +{ + uint64_t StartTimeAbsolute; + mach_timebase_info_data_t MachTimeInfo; +}; + +static gsosx_time_info +gsosx_InitTime() +{ + gsosx_time_info Result = {0}; + Result.StartTimeAbsolute = mach_absolute_time(); + mach_timebase_info(&Result.MachTimeInfo); + return Result; +} + +static uint64_t +gsosx_GetTime(gsosx_time_info TimeInfo) +{ + uint64_t Result = mach_absolute_time() - TimeInfo.StartTimeAbsolute; + return Result; +} + +static double +gsosx_GetSecondsElapsed(uint64_t Start, uint64_t End, gsosx_time_info TimeInfo) +{ + double Result = 0; + double Elapsed = (double)(End - Start); + Result = Elapsed * (double)(TimeInfo.MachTimeInfo.numer) / (double)NANOS_PER_SEC / (double)TimeInfo.MachTimeInfo.denom; + return Result; +} + +static void +gsosx_Sleep(double Seconds, gsosx_time_info TimeInfo) +{ + // NOTE(Peter): This isn't entirely precise, and can vary by up to 500 microseconds. We could implement a sleep combined witha busy wait + // to get more precise sleeping. Do this if necessary + uint64_t WaitTimeAbs = Seconds / (double)TimeInfo.MachTimeInfo.numer * (double)NANOS_PER_SEC * (double)TimeInfo.MachTimeInfo.denom; + uint64_t NowAbs = mach_absolute_time(); + mach_wait_until(NowAbs + WaitTimeAbs); +} \ No newline at end of file diff --git a/src/gs_osx_window.mm b/src/gs_osx_window.mm new file mode 100644 index 0000000..3b0d29c --- /dev/null +++ b/src/gs_osx_window.mm @@ -0,0 +1,78 @@ +@interface gsosx_ApplicationDelegate : NSObject @end + +@implementation gsosx_ApplicationDelegate : NSObject + - (void) + applicationDidFinishLaunching: (NSNotification *)notification + { + [NSApp stop: nil]; + NSAutoreleasePool* Pool = [[NSAutoreleasePool alloc] init]; + + [Pool drain]; + } + + - (NSApplicationTerminateReply) + applicationShouldTerminate: (NSApplication*) sender + { + return NO; + } + + - (void) + dealloc + { + [super dealloc]; + } +@end + +@interface gsosx_WindowDelegate: NSObject @end +@implementation gsosx_WindowDelegate : NSObject + - (BOOL) + windowShouldClose: (id)sender + { + // TODO(Peter): Stop Application Running? + NSLog(@"Close button pressed"); + return NO; + } + + - (void) + windowDidBecomeKey: (NSNotification*)notification + { + // TODO: ??? + } + + - (void) + windowDisResignKey: (NSNotification*)notification + { + // TODO: ??? + } +@end + +static NSWindow* +gsosx_CreateWindow(NSApplication* App, int Width, int Height, id Title) +{ + int WindowStyleMask = NSWindowStyleMaskClosable; + NSRect WindowRect = NSMakeRect(0, 0, Width, Height); + + NSWindow* Window = [[NSWindow alloc] initWithContentRect: WindowRect styleMask: WindowStyleMask backing: NSBackingStoreBuffered defer: YES]; + Window.styleMask = NSWindowStyleMaskResizable | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable; + + gsosx_WindowDelegate* WindowDelegate = [[gsosx_WindowDelegate alloc] init]; + + [Window setOpaque: YES]; + [Window setDelegate: WindowDelegate]; + [Window setTitle: Title]; + + NSMenu* MenuBar = [NSMenu alloc]; + NSMenuItem* AppMenuItem = [NSMenuItem alloc]; + [MenuBar addItem: AppMenuItem]; + [App setMainMenu: MenuBar]; + + NSMenu* AppMenu = [NSMenu alloc]; + id QuitTitle = [@"Quit " stringByAppendingString: Title]; + id QuitMenuItem = [[NSMenuItem alloc] initWithTitle: QuitTitle action: @selector(terminate:) keyEquivalent: @"q"]; + [AppMenu addItem: QuitMenuItem]; + [AppMenuItem setSubmenu: AppMenu]; + + [App activateIgnoringOtherApps: YES]; + + return Window; +}