Created
August 28, 2024 04:04
-
-
Save colrdavidson/aecf689196fcae49122749f866780854 to your computer and use it in GitHub Desktop.
OSX Platform Layer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //+build darwin | |
| package main | |
| import "core:fmt" | |
| import "core:strings" | |
| import "core:os" | |
| import "core:time" | |
| import gl "vendor:OpenGL" | |
| import NS "vendor:darwin/Foundation" | |
| import CA "vendor:darwin/QuartzCore" | |
| normalize_key :: proc(v: NS.kVK) -> KeyType { | |
| switch v { | |
| case .ANSI_A: return .A | |
| case .ANSI_S: return .S | |
| case .ANSI_D: return .D | |
| case .ANSI_F: return .F | |
| case .ANSI_H: return .H | |
| case .ANSI_G: return .G | |
| case .ANSI_Z: return .Z | |
| case .ANSI_X: return .X | |
| case .ANSI_C: return .C | |
| case .ANSI_V: return .V | |
| case .ANSI_B: return .B | |
| case .ANSI_Q: return .Q | |
| case .ANSI_W: return .W | |
| case .ANSI_E: return .E | |
| case .ANSI_R: return .R | |
| case .ANSI_Y: return .Y | |
| case .ANSI_T: return .T | |
| case .ANSI_1: return ._1 | |
| case .ANSI_2: return ._2 | |
| case .ANSI_3: return ._3 | |
| case .ANSI_4: return ._4 | |
| case .ANSI_6: return ._6 | |
| case .ANSI_5: return ._5 | |
| case .ANSI_Equal: return .Equal | |
| case .ANSI_9: return ._9 | |
| case .ANSI_7: return ._7 | |
| case .ANSI_Minus: return .Minus | |
| case .ANSI_8: return ._8 | |
| case .ANSI_0: return ._0 | |
| case .ANSI_RightBracket: return .RightBracket | |
| case .ANSI_O: return .O | |
| case .ANSI_U: return .U | |
| case .ANSI_LeftBracket: return .LeftBracket | |
| case .ANSI_I: return .I | |
| case .ANSI_P: return .P | |
| case .ANSI_L: return .L | |
| case .ANSI_J: return .J | |
| case .ANSI_Quote: return .Quote | |
| case .ANSI_K: return .K | |
| case .ANSI_Semicolon: return .Semicolon | |
| case .ANSI_Backslash: return .Backslash | |
| case .ANSI_Comma: return .Comma | |
| case .ANSI_Slash: return .Slash | |
| case .ANSI_N: return .N | |
| case .ANSI_M: return .M | |
| case .ANSI_Period: return .Period | |
| case .ANSI_Grave: return .Grave | |
| case .ANSI_KeypadDecimal: return .Keypad_Period | |
| case .ANSI_KeypadMultiply: return .Keypad_Multiply | |
| case .ANSI_KeypadPlus: return .Keypad_Plus | |
| case .ANSI_KeypadClear: return .Keypad_Clear | |
| case .ANSI_KeypadDivide: return .Keypad_Divide | |
| case .ANSI_KeypadEnter: return .Keypad_Enter | |
| case .ANSI_KeypadMinus: return .Keypad_Minus | |
| case .ANSI_KeypadEquals: return .Keypad_Equal | |
| case .ANSI_Keypad0: return ._0 | |
| case .ANSI_Keypad1: return ._1 | |
| case .ANSI_Keypad2: return ._2 | |
| case .ANSI_Keypad3: return ._3 | |
| case .ANSI_Keypad4: return ._4 | |
| case .ANSI_Keypad5: return ._5 | |
| case .ANSI_Keypad6: return ._6 | |
| case .ANSI_Keypad7: return ._7 | |
| case .ANSI_Keypad8: return ._8 | |
| case .ANSI_Keypad9: return ._9 | |
| case .Return: return .Return | |
| case .Tab: return .Tab | |
| case .Space: return .Space | |
| case .Delete: return .Delete | |
| case .Escape: return .Escape | |
| case .Command: return .LeftSuper | |
| case .Shift: return .LeftShift | |
| case .CapsLock: return .CapsLock | |
| case .Option: return .LeftAlt | |
| case .Control: return .LeftControl | |
| case .RightCommand: return .RightSuper | |
| case .RightShift: return .RightShift | |
| case .RightOption: return .RightAlt | |
| case .RightControl: return .RightControl | |
| case .Function: return .Function | |
| case .F17: return .F17 | |
| case .VolumeUp: return .VolumeUp | |
| case .VolumeDown: return .VolumeDown | |
| case .Mute: return .Mute | |
| case .F18: return .F18 | |
| case .F19: return .F19 | |
| case .F20: return .F20 | |
| case .F5: return .F5 | |
| case .F6: return .F6 | |
| case .F7: return .F7 | |
| case .F3: return .F3 | |
| case .F8: return .F8 | |
| case .F9: return .F9 | |
| case .F11: return .F11 | |
| case .F13: return .F13 | |
| case .F16: return .F16 | |
| case .F14: return .F14 | |
| case .F10: return .F10 | |
| case .F12: return .F12 | |
| case .F15: return .F16 | |
| case .Help: return .Help | |
| case .Home: return .Home | |
| case .PageUp: return .PageUp | |
| case .ForwardDelete: return .FwdDelete | |
| case .F4: return .F4 | |
| case .End: return .End | |
| case .F2: return .F2 | |
| case .PageDown: return .PageDown | |
| case .F1: return .F1 | |
| case .LeftArrow: return .ArrowLeft | |
| case .RightArrow: return .ArrowRight | |
| case .DownArrow: return .ArrowDown | |
| case .UpArrow: return .ArrowUp | |
| case .JIS_Yen: return .None | |
| case .JIS_Underscore: return .None | |
| case .JIS_KeypadComma: return .None | |
| case .JIS_Eisu: return .None | |
| case .JIS_Kana: return .None | |
| case .ISO_Section: return .None | |
| } | |
| return .None | |
| } | |
| open_file_dialog :: proc() -> (string, bool) { | |
| panel := NS.OpenPanel.openPanel() | |
| panel->setCanChooseFiles(true) | |
| panel->setResolvesAliases(true) | |
| panel->setCanChooseDirectories(false) | |
| panel->setAllowsMultipleSelection(false) | |
| if panel->runModal() == .OK { | |
| urls := panel->URLs() | |
| ret_count := urls->count() | |
| if ret_count != 1 { | |
| return "", false | |
| } | |
| url := urls->objectAs(0, ^NS.URL) | |
| return strings.clone_from_cstring(url->fileSystemRepresentation()), true | |
| } | |
| return "", false | |
| } | |
| GFX_Context :: struct { | |
| app: ^NS.Application, | |
| window: ^NS.Window, | |
| ctx: ^NS.OpenGLContext, | |
| } | |
| create_context :: proc(title: cstring, width, height: int) -> GFX_Context { | |
| gfx := GFX_Context{} | |
| frame_rect := NS.Rect{{0, 0}, {NS.Float(width), NS.Float(height)}} | |
| app: ^NS.Application | |
| window: ^NS.Window | |
| { | |
| app = NS.Application.sharedApplication() | |
| app->setActivationPolicy(.Regular) | |
| // auto-center window on main-screen | |
| screen := NS.Screen_mainScreen() | |
| screen_rect := screen->frame() | |
| screen_width := screen_rect.size.width | |
| screen_height := screen_rect.size.height | |
| frame_rect.origin.x = NS.Float((f32(screen_rect.size.width) / 2) - (f32(width) / 2)) | |
| frame_rect.origin.y = NS.Float((f32(screen_rect.size.height) / 2) - (f32(height) / 2)) | |
| window = NS.Window_alloc() | |
| window->initWithContentRect(frame_rect, {.Resizable, .Miniaturizable, .Closable, .Titled}, .Buffered, false, screen) | |
| window->setTitle(NS.AT("Zxx")) | |
| { | |
| delegate := NS.window_delegate_register_and_alloc({ | |
| windowShouldClose = proc(^NS.Window) -> NS.BOOL { | |
| running = false | |
| return true | |
| }, | |
| windowDidResize = proc(^NS.Notification) { | |
| fmt.printf("resized!\n") | |
| }, | |
| windowDidBecomeKey = proc(^NS.Notification) { | |
| fmt.printf("Got focus\n") | |
| }, | |
| windowDidResignKey = proc(^NS.Notification) { | |
| fmt.printf("Lost focus\n") | |
| }, | |
| }, "window_delegate", context) | |
| window->setDelegate(delegate) | |
| } | |
| } | |
| attrs := [?]NS.OpenGLPixelFormatAttribute{ | |
| NS.OpenGLPFAAccelerated, NS.OpenGLPFADoubleBuffer, | |
| NS.OpenGLPFAOpenGLProfile, NS.OpenGLProfileVersion3_2Core, | |
| NS.OpenGLPFAColorSize, 24, | |
| NS.OpenGLPFAAlphaSize, 8, | |
| NS.OpenGLPFADepthSize, 24, | |
| NS.OpenGLPFAStencilSize, 8, | |
| NS.OpenGLPFASampleBuffers, 0, | |
| 0, | |
| } | |
| gl_pixelfmt := NS.OpenGLPixelFormat_alloc() | |
| gl_pixelfmt->initWithAttributes(raw_data(attrs[:])) | |
| gl_view := NS.OpenGLView_alloc() | |
| gl_view->initWithFrame(frame_rect, gl_pixelfmt) | |
| gl_view->updateTrackingAreas() | |
| gl_view->setWantsBestResolutionOpenGLSurface(true) | |
| gl_ctx := gl_view->getOpenGLContext() | |
| gl_ctx->makeCurrentContext() | |
| swap_int : i32 = 1 | |
| gl_ctx->setValues(&swap_int, NS.OpenGLContextParameterSwapInterval) | |
| window->setContentView(gl_view) | |
| window->makeFirstResponder(gl_view) | |
| window->makeKeyAndOrderFront(nil) | |
| app->activateIgnoringOtherApps(true) | |
| major_version := 3 | |
| minor_version := 3 | |
| gl.load_up_to(major_version, minor_version, NS.gl_set_proc_address) | |
| gfx.app = app | |
| gfx.window = window | |
| gfx.ctx = gl_ctx | |
| return gfx | |
| } | |
| get_next_event :: proc(gfx: ^GFX_Context, wait: bool) -> Event { | |
| if !running { | |
| return Event{type = .Exit} | |
| } | |
| timeout : ^NS.Date | |
| if wait { | |
| timeout = NS.Date_distantFuture() | |
| } | |
| event := gfx.app->nextEventMatchingMask(NS.EventMaskAny, timeout, NS.DefaultRunLoopMode, true) | |
| if event == nil { | |
| return Event{type = .None} | |
| } | |
| ev_type := event->type() | |
| handled := false | |
| defer { if !handled { gfx.app->sendEvent(event) } } | |
| #partial switch ev_type { | |
| case .LeftMouseDown: { | |
| pos := event->locationInWindow() | |
| return Event{type = .LeftMouseDown, x = f32(pos.x), y = f32(pos.y)} | |
| } | |
| case .LeftMouseUp: { | |
| pos := event->locationInWindow() | |
| return Event{type = .LeftMouseUp, x = f32(pos.x), y = f32(pos.y)} | |
| } | |
| case .MouseMoved: { | |
| pos := event->locationInWindow() | |
| return Event{type = .MouseMoved, x = f32(pos.x), y = f32(pos.y)} | |
| } | |
| case .ScrollWheel: { | |
| dx, dy := event->scrollingDelta() | |
| return Event{type = .Scroll, x = f32(dx), y = f32(dy)} | |
| } | |
| case .Magnify: { | |
| pos := event->locationInWindow() | |
| scale := event->deltaZ() | |
| return Event{type = .Zoom, x = f32(pos.x), y = f32(pos.x), z = f32(scale)} | |
| } | |
| case .Rotate: { | |
| pos := event->locationInWindow() | |
| rot := event->deltaZ() | |
| return Event{type = .Rotate, x = f32(pos.x), y = f32(pos.x), z = f32(rot)} | |
| } | |
| case .KeyDown: { | |
| key_code := event->keyCode() | |
| key := normalize_key(NS.kVK(key_code)) | |
| handled = true | |
| return Event{type = .KeyDown, key = key} | |
| } | |
| case .KeyUp: { | |
| key_code := event->keyCode() | |
| key := normalize_key(NS.kVK(key_code)) | |
| return Event{type = .KeyUp, key = key} | |
| } | |
| case .FlagsChanged: { | |
| key_code := event->keyCode() | |
| key := normalize_key(NS.kVK(key_code)) | |
| flags := event->modifierFlags() | |
| return Event{type = .KeyDown, key = key} | |
| } | |
| case .AppleEvent: { | |
| ev_subtype := event->subtype() | |
| if ev_subtype == -32768 { | |
| return Event{type = .Exit} | |
| } | |
| return Event{type = .None} | |
| } | |
| case: { | |
| return Event{type = .None} | |
| } | |
| } | |
| } | |
| swap_buffers :: proc(gfx: ^GFX_Context) { | |
| gfx.ctx->flushBuffer() | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment