tuxanci2-engine
Game engine for tuxanci2
Log | Files | Refs | README | LICENSE
commit 30fa75b0df7b3a0c4efdb684ea4227b4c7ffa9f3
parent 4990e297f0e3d8a2cd709f88148d08e1c56a9e3b
Author: Connor Thomson <connor@gnufault.org>
Date:   Sat, 24 Oct 2026 20:42:46 -0700

Add cocoa port

Diffstat:
MREADME.md | 1+
Aplatforms/cocoa/.gitignore | 1+
Aplatforms/cocoa/Makefile | 43+++++++++++++++++++++++++++++++++++++++++++
Aplatforms/cocoa/cocoa.m | 461+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 506 insertions(+), 0 deletions(-)

diff --git a/README.md b/README.md @@ -5,6 +5,7 @@ Spirtiual successor to Tuxánci Tuxánci currently supports: - AAlib +- Cocoa - DOS - Dreamcast - N64 diff --git a/platforms/cocoa/.gitignore b/platforms/cocoa/.gitignore @@ -0,0 +1 @@ +tuxanci2 diff --git a/platforms/cocoa/Makefile b/platforms/cocoa/Makefile @@ -0,0 +1,42 @@ +MAKEFILE_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +ROOT_DIR := $(abspath $(MAKEFILE_DIR)/../..) + +COCOA_CC ?= cc +COCOA_BUILD ?= build/cocoa + +COCOA_TRIPLE := $(shell $(COCOA_CC) -dumpmachine 2>/dev/null) +ifneq ($(findstring darwin,$(COCOA_TRIPLE))$(findstring macos,$(COCOA_TRIPLE)),) + COCOA_CFLAGS ?= -O2 -Wno-deprecated-declarations + COCOA_LDFLAGS ?= + COCOA_LDLIBS ?= -framework Cocoa +else + COCOA_CFLAGS ?= -O2 $(filter-out -MMD -MP,$(shell gnustep-config --objc-flags)) + COCOA_LDFLAGS ?= + COCOA_LDLIBS ?= $(shell gnustep-config --gui-libs) +endif + +COCOA_SOURCE := $(MAKEFILE_DIR)cocoa.m +COCOA_BUILD_DIR := $(ROOT_DIR)/$(COCOA_BUILD) +COCOA_LIBRARY := $(COCOA_BUILD_DIR)/tuxanci2.a +TARGET := $(MAKEFILE_DIR)tuxanci2 +ROOT_INPUTS := $(shell find $(ROOT_DIR)/src -type f -name '*.c') \ + $(wildcard $(ROOT_DIR)/include/*.h) \ + $(ROOT_DIR)/include/ta_config.h.in \ + $(shell find $(ROOT_DIR)/files -type f) \ + $(ROOT_DIR)/Makefile + +.DEFAULT_GOAL := all +.PHONY: all clean + +all: $(TARGET) + +$(COCOA_LIBRARY): $(ROOT_INPUTS) + $(MAKE) -C "$(ROOT_DIR)" CC="$(COCOA_CC)" BUILD_DIR="$(COCOA_BUILD)" all + +$(TARGET): $(COCOA_SOURCE) $(COCOA_LIBRARY) + $(COCOA_CC) $(COCOA_CFLAGS) -I"$(ROOT_DIR)/include" -I"$(COCOA_BUILD_DIR)" "$(COCOA_SOURCE)" "$(COCOA_LIBRARY)" $(COCOA_LDFLAGS) $(COCOA_LDLIBS) -o "$@" + +clean: + $(RM) "$(TARGET)" + $(MAKE) -C "$(ROOT_DIR)" BUILD_DIR="$(COCOA_BUILD)" clean + +\ No newline at end of file diff --git a/platforms/cocoa/cocoa.m b/platforms/cocoa/cocoa.m @@ -0,0 +1,461 @@ +#include "ta_api.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#import <Foundation/Foundation.h> +#import <AppKit/AppKit.h> + +#define WINDOW_WIDTH 800 +#define WINDOW_HEIGHT 600 +#define WINDOW_BPP 32 +#define TARGET_FPS 60 + +#if defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 +#define TA_STYLE_MASK (NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable) +#define TA_NUMPAD_FLAG NSEventModifierFlagNumericPad +#define TA_SHIFT_FLAG NSEventModifierFlagShift +#define TA_CTRL_FLAG NSEventModifierFlagControl +#define TA_ALT_FLAG NSEventModifierFlagOption +#define TA_CMD_FLAG NSEventModifierFlagCommand +#define TA_CAPS_FLAG NSEventModifierFlagCapsLock +#else +#define TA_STYLE_MASK (NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask) +#define TA_NUMPAD_FLAG NSNumericPadKeyMask +#define TA_SHIFT_FLAG NSShiftKeyMask +#define TA_CTRL_FLAG NSControlKeyMask +#define TA_ALT_FLAG NSAlternateKeyMask +#define TA_CMD_FLAG NSCommandKeyMask +#define TA_CAPS_FLAG NSAlphaShiftKeyMask +#endif + +#define KEYCODE_TABLE_SIZE 256 + +static ta_keyboard_keys pressed_keys[KEYCODE_TABLE_SIZE]; + +static ta_uint8 modifier_down[KEYCODE_TABLE_SIZE]; + +static ta_keyboard_keys key_from_function_key(unichar c) { + if (c >= NSF1FunctionKey && c <= NSF1FunctionKey + 11) { + return (ta_keyboard_keys)(KEY_F1 + (c - NSF1FunctionKey)); + } + + switch (c) { + case NSUpArrowFunctionKey: return KEY_UP; + case NSDownArrowFunctionKey: return KEY_DOWN; + case NSLeftArrowFunctionKey: return KEY_LEFT; + case NSRightArrowFunctionKey: return KEY_RIGHT; + case NSDeleteFunctionKey: return KEY_DELETE; + case NSHomeFunctionKey: return KEY_HOME; + case NSEndFunctionKey: return KEY_END; + case NSPageUpFunctionKey: return KEY_PAGE_UP; + case NSPageDownFunctionKey: return KEY_PAGE_DOWN; + case NSInsertFunctionKey: return KEY_INSERT; + case NSPrintScreenFunctionKey: return KEY_PRINT_SCREEN; + case NSScrollLockFunctionKey: return KEY_SCROLL_LOCK; + case NSPauseFunctionKey: return KEY_PAUSE; + case NSMenuFunctionKey: return KEY_MENU; + default: return KEY_NONE; + } +} + +static ta_keyboard_keys key_from_keypad(unichar c) { + if (c >= '0' && c <= '9') { + return (ta_keyboard_keys)(KEY_KP_0 + (c - '0')); + } + + switch (c) { + case '.': return KEY_KP_DECIMAL; + case '/': return KEY_KP_DIVIDE; + case '*': return KEY_KP_MULTIPLY; + case '-': return KEY_KP_SUBTRACT; + case '+': return KEY_KP_ADD; + case '=': return KEY_KP_EQUALS; + case '\r': + case '\n': + case 3: return KEY_KP_ENTER; + default: return KEY_NONE; + } +} + +static ta_keyboard_keys key_from_event(NSEvent *event) { + NSString *characters = [event charactersIgnoringModifiers]; + if ([characters length] == 0) { + return KEY_NONE; + } + + unichar c = [characters characterAtIndex: 0]; + + if (c >= 0xF700 && c <= 0xF8FF) { + return key_from_function_key(c); + } + + if ([event modifierFlags] & TA_NUMPAD_FLAG) { + ta_keyboard_keys keypad = key_from_keypad(c); + if (keypad != KEY_NONE) { + return keypad; + } + } + + if (c >= 'A' && c <= 'Z') { + c = (unichar)(c - 'A' + 'a'); + } + + if (c >= 32 && c <= 126) { + return (ta_keyboard_keys)c; + } + + switch (c) { + case 8: + case 127: return KEY_BACKSPACE; + case 9: + case 0x19: return KEY_TAB; + case 13: + case 3: return KEY_ENTER; + case 27: return KEY_ESCAPE; + default: return KEY_NONE; + } +} + +static void clear_input(void) { + memset(pressed_keys, 0, sizeof(pressed_keys)); + memset(modifier_down, 0, sizeof(modifier_down)); + ta_keyboard_clear(); +} + +@interface TAView : NSView { + NSBitmapImageRep *image; +} +- (void)present; +@end + +@implementation TAView + +- (id)initWithFrame:(NSRect)frame { + self = [super initWithFrame: frame]; + if (!self) { + return nil; + } + + image = [[NSBitmapImageRep alloc] + initWithBitmapDataPlanes: NULL + pixelsWide: WINDOW_WIDTH + pixelsHigh: WINDOW_HEIGHT + bitsPerSample: 8 + samplesPerPixel: 4 + hasAlpha: YES + isPlanar: NO + colorSpaceName: NSDeviceRGBColorSpace + bytesPerRow: WINDOW_WIDTH * 4 + bitsPerPixel: 32]; + + return self; +} + +- (void)dealloc { + [image release]; + [super dealloc]; +} + +- (BOOL)isOpaque { + return YES; +} + +- (BOOL)acceptsFirstResponder { + return YES; +} + +- (BOOL)acceptsFirstMouse:(NSEvent *)event { + return YES; +} + +- (void)present { + if (!image || !ta_fb.buffers[0]) { + return; + } + + const ta_uint32 *source = (const ta_uint32 *)ta_fb.buffers[0]; + unsigned char *destination = [image bitmapData]; + NSInteger row_bytes = [image bytesPerRow]; + int x, y; + + for (y = 0; y < WINDOW_HEIGHT; y++) { + unsigned char *row = destination + (NSInteger)y * row_bytes; + + for (x = 0; x < WINDOW_WIDTH; x++) { + ta_uint32 pixel = source[y * WINDOW_WIDTH + x]; + + row[x * 4 + 0] = (unsigned char)((pixel >> 16) & 0xff); + row[x * 4 + 1] = (unsigned char)((pixel >> 8) & 0xff); + row[x * 4 + 2] = (unsigned char)(pixel & 0xff); + row[x * 4 + 3] = 0xff; + } + } + + [self setNeedsDisplay: YES]; +} + +- (void)drawRect:(NSRect)rect { + if (!image) { + return; + } + + [[NSGraphicsContext currentContext] setImageInterpolation: NSImageInterpolationNone]; + [image drawInRect: [self bounds]]; +} + +- (void)keyDown:(NSEvent *)event { + if ([event isARepeat]) { + return; + } + + unsigned short code = [event keyCode] % KEYCODE_TABLE_SIZE; + ta_keyboard_keys key = key_from_event(event); + + if (pressed_keys[code] != KEY_NONE) { + ta_keyboard_key_up(pressed_keys[code]); + } + + pressed_keys[code] = key; + ta_keyboard_key_down(key); +} + +- (void)keyUp:(NSEvent *)event { + unsigned short code = [event keyCode] % KEYCODE_TABLE_SIZE; + + if (pressed_keys[code] != KEY_NONE) { + ta_keyboard_key_up(pressed_keys[code]); + pressed_keys[code] = KEY_NONE; + } +} + +- (void)flagsChanged:(NSEvent *)event { + unsigned short code = [event keyCode]; + NSUInteger flags = [event modifierFlags]; + ta_keyboard_keys key = KEY_NONE; + NSUInteger mask = 0; + + switch (code) { + case 56: key = KEY_LEFT_SHIFT; mask = TA_SHIFT_FLAG; break; + case 60: key = KEY_RIGHT_SHIFT; mask = TA_SHIFT_FLAG; break; + case 59: key = KEY_LEFT_CTRL; mask = TA_CTRL_FLAG; break; + case 62: key = KEY_RIGHT_CTRL; mask = TA_CTRL_FLAG; break; + case 58: key = KEY_LEFT_ALT; mask = TA_ALT_FLAG; break; + case 61: key = KEY_RIGHT_ALT; mask = TA_ALT_FLAG; break; + case 55: key = KEY_LEFT_SUPER; mask = TA_CMD_FLAG; break; + case 54: key = KEY_RIGHT_SUPER; mask = TA_CMD_FLAG; break; + case 57: key = KEY_CAPS_LOCK; mask = TA_CAPS_FLAG; break; + default: return; + } + + if (code >= KEYCODE_TABLE_SIZE) { + return; + } + + if (key == KEY_CAPS_LOCK) { + ta_keyboard_key_down(key); + ta_keyboard_key_up(key); + return; + } + + if (flags & mask) { + modifier_down[code] = 1; + ta_keyboard_key_down(key); + } else if (modifier_down[code]) { + modifier_down[code] = 0; + ta_keyboard_key_up(key); + } +} + +@end + +@interface TAController : NSObject { + NSWindow *window; + TAView *view; + char title[256]; +} +- (void)start; +- (void)tick:(NSTimer *)timer; +@end + +@implementation TAController + +- (void)start { + NSRect content = NSMakeRect(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); + + window = [[NSWindow alloc] initWithContentRect: content + styleMask: TA_STYLE_MASK + backing: NSBackingStoreBuffered + defer: NO]; + [window setReleasedWhenClosed: NO]; + [window setDelegate: self]; + + view = [[TAView alloc] initWithFrame: content]; + [window setContentView: view]; + [window makeFirstResponder: view]; + [window center]; + + title[0] = '\0'; + [self tick: nil]; + [window makeKeyAndOrderFront: nil]; + + NSTimer *timer = [NSTimer timerWithTimeInterval: 1.0 / TARGET_FPS + target: self + selector: @selector(tick:) + userInfo: nil + repeats: YES]; +#ifdef __APPLE__ + [[NSRunLoop currentRunLoop] addTimer: timer forMode: NSRunLoopCommonModes]; +#else + [[NSRunLoop currentRunLoop] addTimer: timer forMode: NSDefaultRunLoopMode]; +#endif +} + +- (void)tick:(NSTimer *)timer { + ta_main_update(); + + if (ta_title && strncmp(title, ta_title, sizeof(title)) != 0) { + strncpy(title, ta_title, sizeof(title) - 1); + title[sizeof(title) - 1] = '\0'; + [window setTitle: [NSString stringWithUTF8String: title]]; + } + + [view present]; +} + +- (void)applicationDidFinishLaunching:(NSNotification *)notification { +#ifdef __APPLE__ + if ([NSApp respondsToSelector: @selector(setActivationPolicy:)]) { + [NSApp setActivationPolicy: NSApplicationActivationPolicyRegular]; + } +#endif + [NSApp activateIgnoringOtherApps: YES]; +} + +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)application { + return YES; +} + +- (void)windowDidResignKey:(NSNotification *)notification { + clear_input(); +} + +@end + +static void build_menu(void) { + NSMenu *menubar = [[[NSMenu alloc] init] autorelease]; + NSMenuItem *app_item = [[[NSMenuItem alloc] initWithTitle: @"" action: NULL keyEquivalent: @""] autorelease]; + NSMenu *app_menu = [[[NSMenu alloc] initWithTitle: @""] autorelease]; + NSMenuItem *quit_item = [[[NSMenuItem alloc] initWithTitle: @"Quit" + action: @selector(terminate:) + keyEquivalent: @"q"] autorelease]; + + [app_menu addItem: quit_item]; + [app_item setSubmenu: app_menu]; + [menubar addItem: app_item]; + [NSApp setMainMenu: menubar]; +} + +int main(void) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + ta_fb.width = WINDOW_WIDTH; + ta_fb.height = WINDOW_HEIGHT; + ta_fb.bpp = WINDOW_BPP; + + ta_main_init(); + + clear_input(); + + [NSApplication sharedApplication]; + + TAController *controller = [[TAController alloc] init]; + [NSApp setDelegate: controller]; + build_menu(); + [controller start]; + + [NSApp run]; + + [controller release]; + [pool release]; + + return 0; +} + +void *ta_malloc(ta_size size) { + return malloc(size); +} + +void *ta_realloc(void *ptr, ta_size new_size) { + return realloc(ptr, new_size); +} + +void ta_free(void *ptr) { + free(ptr); +} + +ta_fs_file ta_read_file(const char *path) { + ta_fs_file file = { + .data = NULL, + .size = 0 + }; + + FILE *file_pointer = fopen(path, "rb"); + if (!file_pointer) { + return file; + } + + if (fseek(file_pointer, 0, SEEK_END) != 0) { + fclose(file_pointer); + + return file; + } + + long file_size = ftell(file_pointer); + if (file_size < 0) { + fclose(file_pointer); + + return file; + } + + rewind(file_pointer); + + file.data = ta_malloc((ta_size)file_size); + if (!file.data) { + fclose(file_pointer); + + return file; + } + + ta_size bytes_read = fread(file.data, 1, (size_t)file_size, file_pointer); + + if (bytes_read != (ta_size)file_size) { + ta_free(file.data); + file.data = NULL; + fclose(file_pointer); + + return file; + } + + file.size = (ta_size)file_size; + + fclose(file_pointer); + + return file; +} + +void ta_close_file(ta_fs_file *file) { + if (!file) { + return; + } + + if (file->data) { + ta_free(file->data); + file->data = NULL; + } + + file->size = 0; +} + +void ta_exit(int status) { + exit(status); +}