nkeynes@964: /** nkeynes@964: * $Id$ nkeynes@964: * nkeynes@964: * Core Cocoa-based user interface nkeynes@964: * nkeynes@964: * Copyright (c) 2008 Nathan Keynes. nkeynes@964: * nkeynes@964: * This program is free software; you can redistribute it and/or modify nkeynes@964: * it under the terms of the GNU General Public License as published by nkeynes@964: * the Free Software Foundation; either version 2 of the License, or nkeynes@964: * (at your option) any later version. nkeynes@964: * nkeynes@964: * This program is distributed in the hope that it will be useful, nkeynes@964: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@964: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@964: * GNU General Public License for more details. nkeynes@964: */ nkeynes@964: nkeynes@964: #include nkeynes@964: #include nkeynes@964: #include nkeynes@964: #include nkeynes@964: #include nkeynes@964: #include "lxdream.h" nkeynes@964: #include "dream.h" nkeynes@964: #include "dreamcast.h" nkeynes@964: #include "config.h" nkeynes@1041: #include "lxpaths.h" nkeynes@964: #include "display.h" nkeynes@964: #include "gui.h" nkeynes@964: #include "gdrom/gdrom.h" nkeynes@964: #include "gdlist.h" nkeynes@964: #include "loader.h" nkeynes@964: #include "cocoaui/cocoaui.h" nkeynes@964: nkeynes@964: void cocoa_gui_update( void ); nkeynes@964: void cocoa_gui_start( void ); nkeynes@964: void cocoa_gui_stop( void ); nkeynes@964: uint32_t cocoa_gui_run_slice( uint32_t nanosecs ); nkeynes@964: nkeynes@964: struct dreamcast_module cocoa_gui_module = { "gui", NULL, nkeynes@964: cocoa_gui_update, nkeynes@964: cocoa_gui_start, nkeynes@964: cocoa_gui_run_slice, nkeynes@964: cocoa_gui_stop, nkeynes@964: NULL, NULL }; nkeynes@964: nkeynes@964: /** nkeynes@964: * Count of running nanoseconds - used to cut back on the GUI runtime nkeynes@964: */ nkeynes@964: static uint32_t cocoa_gui_nanos = 0; nkeynes@964: static uint32_t cocoa_gui_ticks = 0; nkeynes@964: static struct timeval cocoa_gui_lasttv; nkeynes@964: static BOOL cocoa_gui_autorun = NO; nkeynes@964: static BOOL cocoa_gui_is_running = NO; nkeynes@964: static LxdreamMainWindow *mainWindow = NULL; nkeynes@964: nkeynes@964: @interface NSApplication (PrivateAdditions) nkeynes@964: - (void) setAppleMenu:(NSMenu *)aMenu; nkeynes@964: @end nkeynes@964: nkeynes@1098: gboolean cocoa_gui_disc_changed( cdrom_disc_t disc, const gchar *disc_name, void *user_data ) nkeynes@964: { nkeynes@964: NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; nkeynes@964: LxdreamMainWindow *window = (LxdreamMainWindow *)user_data; nkeynes@964: [window updateTitle]; nkeynes@964: [pool release]; nkeynes@964: return TRUE; nkeynes@964: } nkeynes@964: nkeynes@964: /** nkeynes@964: * Produces the menu title by looking the text up in gettext, removing any nkeynes@964: * underscores, and returning the result as an NSString. nkeynes@964: */ nkeynes@964: static NSString *NSMENU_( const char *text ) nkeynes@964: { nkeynes@964: const char *s = gettext(text); nkeynes@964: char buf[strlen(s)+1]; nkeynes@964: char *d = buf; nkeynes@964: nkeynes@964: while( *s != '\0' ) { nkeynes@964: if( *s != '_' ) { nkeynes@964: *d++ = *s; nkeynes@964: } nkeynes@964: s++; nkeynes@964: } nkeynes@964: *d = '\0'; nkeynes@964: nkeynes@964: return [NSString stringWithUTF8String: buf]; nkeynes@964: } nkeynes@964: nkeynes@964: static void cocoa_gui_create_menu(void) nkeynes@964: { nkeynes@1040: int i; nkeynes@964: NSMenu *appleMenu, *services; nkeynes@964: NSMenuItem *menuItem; nkeynes@964: NSString *title; nkeynes@964: NSString *appName; nkeynes@964: nkeynes@964: appName = @"Lxdream"; nkeynes@964: appleMenu = [[NSMenu alloc] initWithTitle:@""]; nkeynes@964: nkeynes@964: /* Add menu items */ nkeynes@964: title = [@"About " stringByAppendingString:appName]; nkeynes@964: [appleMenu addItemWithTitle:title action:@selector(about_action:) keyEquivalent:@""]; nkeynes@964: nkeynes@964: [appleMenu addItem:[NSMenuItem separatorItem]]; nkeynes@964: [appleMenu addItemWithTitle: NSMENU_("_Preferences...") action:@selector(preferences_action:) keyEquivalent:@","]; nkeynes@964: nkeynes@964: // Services Menu nkeynes@964: [appleMenu addItem:[NSMenuItem separatorItem]]; nkeynes@964: services = [[[NSMenu alloc] init] autorelease]; nkeynes@964: [appleMenu addItemWithTitle: NS_("Services") action:nil keyEquivalent:@""]; nkeynes@964: [appleMenu setSubmenu: services forItem: [appleMenu itemWithTitle: @"Services"]]; nkeynes@964: nkeynes@964: // Hide AppName nkeynes@964: title = [@"Hide " stringByAppendingString:appName]; nkeynes@964: [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; nkeynes@964: nkeynes@964: // Hide Others nkeynes@964: menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" nkeynes@964: action:@selector(hideOtherApplications:) nkeynes@964: keyEquivalent:@"h"]; nkeynes@964: [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; nkeynes@964: nkeynes@964: // Show All nkeynes@964: [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; nkeynes@964: [appleMenu addItem:[NSMenuItem separatorItem]]; nkeynes@964: nkeynes@964: // Quit AppName nkeynes@964: title = [@"Quit " stringByAppendingString:appName]; nkeynes@964: [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; nkeynes@964: nkeynes@964: /* Put menu into the menubar */ nkeynes@964: menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; nkeynes@964: [menuItem setSubmenu: appleMenu]; nkeynes@964: NSMenu *menu = [NSMenu new]; nkeynes@964: [menu addItem: menuItem]; nkeynes@964: nkeynes@964: NSMenu *gdromMenu = cocoa_gdrom_menu_new(); nkeynes@964: nkeynes@1040: NSMenu *quickStateMenu = [[NSMenu alloc] initWithTitle:NSMENU_("_Quick State")]; nkeynes@1040: int quickState = dreamcast_get_quick_state(); nkeynes@1040: for( i=0; i<=MAX_QUICK_STATE; i++ ) { nkeynes@1040: NSString *label = [NSString stringWithFormat: NSMENU_("State _%d"), i]; nkeynes@1040: NSString *keyEquiv = [NSString stringWithFormat: @"%d", i]; nkeynes@1040: menuItem = [[NSMenuItem alloc] initWithTitle: label action: @selector(quick_state_action:) keyEquivalent: keyEquiv]; nkeynes@1040: [menuItem setTag: i]; nkeynes@1040: if( i == quickState ) { nkeynes@1040: [menuItem setState:NSOnState]; nkeynes@1040: } nkeynes@1040: [quickStateMenu addItem: menuItem]; nkeynes@1040: } nkeynes@1040: nkeynes@964: NSMenu *fileMenu = [[NSMenu alloc] initWithTitle: NSMENU_("_File")]; nkeynes@964: [fileMenu addItemWithTitle: NSMENU_("Load _Binary...") action: @selector(load_binary_action:) keyEquivalent: @"b"]; nkeynes@964: [[fileMenu addItemWithTitle: NSMENU_("_GD-Rom") action: nil keyEquivalent: @""] nkeynes@964: setSubmenu: gdromMenu]; nkeynes@964: [fileMenu addItem: [NSMenuItem separatorItem]]; nkeynes@964: [[fileMenu addItemWithTitle: NSMENU_("_Reset") action: @selector(reset_action:) keyEquivalent: @"r"] nkeynes@964: setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; nkeynes@964: [fileMenu addItemWithTitle: NSMENU_("_Pause") action: @selector(pause_action:) keyEquivalent: @"p"]; nkeynes@964: [fileMenu addItemWithTitle: NS_("Resume") action: @selector(run_action:) keyEquivalent: @"r"]; nkeynes@964: [fileMenu addItem: [NSMenuItem separatorItem]]; nkeynes@1040: [fileMenu addItemWithTitle: NSMENU_("L_oad State...") action: @selector(load_action:) keyEquivalent: @"o"]; nkeynes@1040: [fileMenu addItemWithTitle: NSMENU_("S_ave State...") action: @selector(save_action:) keyEquivalent: @"a"]; nkeynes@1040: menuItem = [[NSMenuItem alloc] initWithTitle:NSMENU_("Select _Quick State") action: nil keyEquivalent: @""]; nkeynes@1040: [fileMenu addItem: [NSMenuItem separatorItem]]; nkeynes@1040: [fileMenu addItemWithTitle: NSMENU_("_Load Quick State") action: @selector(quick_load_action:) keyEquivalent: @"l"]; nkeynes@1040: [fileMenu addItemWithTitle: NSMENU_("_Save Quick State") action: @selector(quick_save_action:) keyEquivalent: @"s"]; nkeynes@1040: [menuItem setSubmenu: quickStateMenu]; nkeynes@1040: [fileMenu addItem: menuItem]; nkeynes@1028: [fileMenu addItem: [NSMenuItem separatorItem]]; nkeynes@1028: [fileMenu addItemWithTitle: NSMENU_("_Full Screen...") action: @selector(fullscreen_action:) keyEquivalent: @"\r"]; nkeynes@964: nkeynes@964: menuItem = [[NSMenuItem alloc] initWithTitle:NSMENU_("_File") action: nil keyEquivalent: @""]; nkeynes@964: [menuItem setSubmenu: fileMenu]; nkeynes@964: [menu addItem: menuItem]; nkeynes@964: nkeynes@964: /* Tell the application object that this is now the application menu */ nkeynes@964: [NSApp setMainMenu: menu]; nkeynes@964: [NSApp setAppleMenu: appleMenu]; nkeynes@964: [NSApp setServicesMenu: services]; nkeynes@964: nkeynes@964: /* Finally give up our references to the objects */ nkeynes@964: [appleMenu release]; nkeynes@964: [menuItem release]; nkeynes@964: [menu release]; nkeynes@964: } nkeynes@964: nkeynes@964: @interface LxdreamDelegate : NSObject nkeynes@964: @end nkeynes@964: nkeynes@964: @implementation LxdreamDelegate nkeynes@964: - (void)windowWillClose: (NSNotification *)notice nkeynes@964: { nkeynes@964: dreamcast_shutdown(); nkeynes@964: exit(0); nkeynes@964: } nkeynes@964: - (void)windowDidBecomeMain: (NSNotification *)notice nkeynes@964: { nkeynes@964: if( cocoa_gui_autorun ) { nkeynes@964: cocoa_gui_autorun = NO; nkeynes@1019: gui_do_later(dreamcast_run); nkeynes@964: } nkeynes@964: } nkeynes@964: - (void)windowDidBecomeKey: (NSNotification *)notice nkeynes@964: { nkeynes@964: display_set_focused( TRUE ); nkeynes@964: } nkeynes@964: - (void)windowDidResignKey: (NSNotification *)notice nkeynes@964: { nkeynes@964: display_set_focused( FALSE ); nkeynes@964: [mainWindow setIsGrabbed: NO]; nkeynes@964: } nkeynes@964: - (BOOL)application: (NSApplication *)app openFile: (NSString *)filename nkeynes@964: { nkeynes@964: const gchar *cname = [filename UTF8String]; nkeynes@1109: ERROR err; nkeynes@1109: if( file_load_magic(cname, FALSE, &err) != FILE_ERROR ) { nkeynes@964: // Queue up a run event nkeynes@1019: gui_do_later(dreamcast_run); nkeynes@964: return YES; nkeynes@964: } else { nkeynes@964: return NO; nkeynes@964: } nkeynes@964: nkeynes@964: } nkeynes@964: - (void) about_action: (id)sender nkeynes@964: { nkeynes@964: NSArray *keys = [NSArray arrayWithObjects: @"Version", @"Copyright", nil]; nkeynes@964: NSArray *values = [NSArray arrayWithObjects: NS_(lxdream_full_version), NS_(lxdream_copyright), nil]; nkeynes@964: nkeynes@964: NSDictionary *options= [NSDictionary dictionaryWithObjects: values forKeys: keys]; nkeynes@964: nkeynes@964: [NSApp orderFrontStandardAboutPanelWithOptions: options]; nkeynes@964: } nkeynes@964: - (void) preferences_action: (id)sender nkeynes@964: { nkeynes@964: cocoa_gui_show_preferences(); nkeynes@964: } nkeynes@964: - (void) load_action: (id)sender nkeynes@964: { nkeynes@964: NSOpenPanel *panel = [NSOpenPanel openPanel]; nkeynes@1041: NSString *path = [NSString stringWithCString: get_gui_path(CONFIG_SAVE_PATH)]; nkeynes@964: NSArray *fileTypes = [NSArray arrayWithObject: @"dst"]; nkeynes@964: int result = [panel runModalForDirectory: path file: nil types: fileTypes]; nkeynes@964: if( result == NSOKButton && [[panel filenames] count] > 0 ) { nkeynes@964: NSString *filename = [[panel filenames] objectAtIndex: 0]; nkeynes@964: dreamcast_load_state( [filename UTF8String] ); nkeynes@1041: set_gui_path(CONFIG_SAVE_PATH, [[panel directory] UTF8String]); nkeynes@964: } nkeynes@964: } nkeynes@964: - (void) save_action: (id)sender nkeynes@964: { nkeynes@964: NSSavePanel *panel = [NSSavePanel savePanel]; nkeynes@1041: NSString *path = [NSString stringWithCString: get_gui_path(CONFIG_SAVE_PATH)]; nkeynes@964: [panel setRequiredFileType: @"dst"]; nkeynes@964: int result = [panel runModalForDirectory: path file:@""]; nkeynes@964: if( result == NSOKButton ) { nkeynes@964: NSString *filename = [panel filename]; nkeynes@964: dreamcast_save_state( [filename UTF8String] ); nkeynes@1041: set_gui_path(CONFIG_SAVE_PATH, [[panel directory] UTF8String]); nkeynes@964: } nkeynes@964: } nkeynes@964: - (void) load_binary_action: (id)sender nkeynes@964: { nkeynes@964: NSOpenPanel *panel = [NSOpenPanel openPanel]; nkeynes@1041: NSString *path = [NSString stringWithCString: get_gui_path(CONFIG_DEFAULT_PATH)]; nkeynes@964: int result = [panel runModalForDirectory: path file: nil types: nil]; nkeynes@964: if( result == NSOKButton && [[panel filenames] count] > 0 ) { nkeynes@964: NSString *filename = [[panel filenames] objectAtIndex: 0]; nkeynes@1109: ERROR err; nkeynes@1109: gboolean ok = file_load_exec( [filename UTF8String], &err ); nkeynes@1041: set_gui_path(CONFIG_DEFAULT_PATH, [[panel directory] UTF8String]); nkeynes@1109: if( !ok ) { nkeynes@1109: ERROR( err.msg ); nkeynes@1109: } nkeynes@964: } nkeynes@964: } nkeynes@964: - (void) mount_action: (id)sender nkeynes@964: { nkeynes@964: NSOpenPanel *panel = [NSOpenPanel openPanel]; nkeynes@1041: NSString *path = [NSString stringWithCString: get_gui_path(CONFIG_DEFAULT_PATH)]; nkeynes@964: int result = [panel runModalForDirectory: path file: nil types: nil]; nkeynes@964: if( result == NSOKButton && [[panel filenames] count] > 0 ) { nkeynes@1109: ERROR err; nkeynes@964: NSString *filename = [[panel filenames] objectAtIndex: 0]; nkeynes@1109: gboolean ok = gdrom_mount_image( [filename UTF8String], &err ); nkeynes@1109: if( !ok ) { nkeynes@1109: ERROR(err.msg); nkeynes@1109: } nkeynes@1041: set_gui_path(CONFIG_DEFAULT_PATH, [[panel directory] UTF8String]); nkeynes@964: } nkeynes@964: } nkeynes@964: - (void) pause_action: (id)sender nkeynes@964: { nkeynes@964: dreamcast_stop(); nkeynes@964: } nkeynes@964: nkeynes@964: - (void) reset_action: (id)sender nkeynes@964: { nkeynes@964: dreamcast_reset(); nkeynes@964: } nkeynes@964: - (void) run_action: (id)sender nkeynes@964: { nkeynes@964: if( !dreamcast_is_running() ) { nkeynes@1019: gui_do_later(dreamcast_run); nkeynes@964: } nkeynes@964: } nkeynes@964: - (void) gdrom_list_action: (id)sender nkeynes@964: { nkeynes@1109: ERROR err; nkeynes@1109: gboolean ok = gdrom_list_set_selection( [sender tag], &err ); nkeynes@1109: if( !ok ) { nkeynes@1109: ERROR( err.msg ); nkeynes@1109: } nkeynes@964: } nkeynes@1028: - (void) fullscreen_action: (id)sender nkeynes@1028: { nkeynes@1028: [mainWindow setFullscreen: ![mainWindow isFullscreen]]; nkeynes@1028: } nkeynes@1040: - (void) quick_state_action: (id)sender nkeynes@1040: { nkeynes@1040: [[[sender menu] itemWithTag: dreamcast_get_quick_state()] setState: NSOffState ]; nkeynes@1040: [sender setState: NSOnState ]; nkeynes@1040: dreamcast_set_quick_state( [sender tag] ); nkeynes@1040: } nkeynes@1040: - (void) quick_save_action: (id)sender nkeynes@1040: { nkeynes@1040: dreamcast_quick_save(); nkeynes@1040: } nkeynes@1040: - (void) quick_load_action: (id)sender nkeynes@1040: { nkeynes@1040: dreamcast_quick_load(); nkeynes@1040: } nkeynes@964: @end nkeynes@964: nkeynes@964: nkeynes@964: gboolean gui_parse_cmdline( int *argc, char **argv[] ) nkeynes@964: { nkeynes@964: /* If started from the finder, the first (and only) arg will look something like nkeynes@964: * -psn_0_... - we want to remove this so that lxdream doesn't try to process it nkeynes@964: * normally nkeynes@964: */ nkeynes@964: if( *argc == 2 && strncmp((*argv)[1], "-psn_", 5) == 0 ) { nkeynes@964: *argc = 1; nkeynes@964: } nkeynes@964: return TRUE; nkeynes@964: } nkeynes@964: nkeynes@1015: gboolean gui_init( gboolean withDebug, gboolean withFullscreen ) nkeynes@964: { nkeynes@964: dreamcast_register_module( &cocoa_gui_module ); nkeynes@964: nkeynes@964: NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; nkeynes@964: [NSApplication sharedApplication]; nkeynes@964: nkeynes@964: LxdreamDelegate *delegate = [[LxdreamDelegate alloc] init]; nkeynes@964: [NSApp setDelegate: delegate]; nkeynes@1017: NSString *iconFile = [[NSBundle mainBundle] pathForResource:@"lxdream" ofType:@"png"]; nkeynes@964: NSImage *iconImage = [[NSImage alloc] initWithContentsOfFile: iconFile]; nkeynes@964: [iconImage setName: @"NSApplicationIcon"]; nkeynes@964: [NSApp setApplicationIconImage: iconImage]; nkeynes@964: cocoa_gui_create_menu(); nkeynes@964: mainWindow = cocoa_gui_create_main_window(); nkeynes@964: [mainWindow makeKeyAndOrderFront: nil]; nkeynes@964: [NSApp activateIgnoringOtherApps: YES]; nkeynes@964: nkeynes@964: register_gdrom_disc_change_hook( cocoa_gui_disc_changed, mainWindow ); nkeynes@1028: if( withFullscreen ) { nkeynes@1028: [mainWindow setFullscreen: YES]; nkeynes@1028: } nkeynes@964: [pool release]; nkeynes@964: return TRUE; nkeynes@964: } nkeynes@964: nkeynes@964: void gui_main_loop( gboolean run ) nkeynes@964: { nkeynes@964: if( run ) { nkeynes@964: cocoa_gui_autorun = YES; nkeynes@964: } nkeynes@964: cocoa_gui_is_running = YES; nkeynes@964: [NSApp run]; nkeynes@964: cocoa_gui_is_running = NO; nkeynes@964: } nkeynes@964: nkeynes@964: void gui_update_state(void) nkeynes@964: { nkeynes@964: cocoa_gui_update(); nkeynes@964: } nkeynes@964: nkeynes@964: void gui_set_use_grab( gboolean grab ) nkeynes@964: { nkeynes@964: [mainWindow setUseGrab: (grab ? YES : NO)]; nkeynes@964: } nkeynes@964: nkeynes@964: gboolean gui_error_dialog( const char *msg, ... ) nkeynes@964: { nkeynes@964: if( cocoa_gui_is_running ) { nkeynes@964: NSString *error_string; nkeynes@964: nkeynes@964: va_list args; nkeynes@964: va_start(args, msg); nkeynes@964: error_string = [[NSString alloc] initWithFormat: [NSString stringWithCString: msg] arguments: args]; nkeynes@964: NSRunAlertPanel(NS_("Error in Lxdream"), error_string, nil, nil, nil); nkeynes@964: va_end(args); nkeynes@964: return TRUE; nkeynes@964: } else { nkeynes@964: return FALSE; nkeynes@964: } nkeynes@964: } nkeynes@964: nkeynes@964: void gui_update_io_activity( io_activity_type io, gboolean active ) nkeynes@964: { nkeynes@964: nkeynes@964: } nkeynes@964: nkeynes@964: nkeynes@964: uint32_t cocoa_gui_run_slice( uint32_t nanosecs ) nkeynes@964: { nkeynes@964: NSEvent *event; nkeynes@964: NSAutoreleasePool *pool; nkeynes@964: nkeynes@964: cocoa_gui_nanos += nanosecs; nkeynes@964: if( cocoa_gui_nanos > GUI_TICK_PERIOD ) { /* 10 ms */ nkeynes@964: cocoa_gui_nanos -= GUI_TICK_PERIOD; nkeynes@964: cocoa_gui_ticks ++; nkeynes@964: uint32_t current_period = cocoa_gui_ticks * GUI_TICK_PERIOD; nkeynes@964: nkeynes@964: // Run the event loop nkeynes@964: pool = [NSAutoreleasePool new]; nkeynes@964: while( (event = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: nil nkeynes@964: inMode: NSDefaultRunLoopMode dequeue: YES]) != nil ) { nkeynes@964: [NSApp sendEvent: event]; nkeynes@964: } nkeynes@964: [pool release]; nkeynes@964: nkeynes@964: struct timeval tv; nkeynes@964: gettimeofday(&tv,NULL); nkeynes@964: uint32_t ns = ((tv.tv_sec - cocoa_gui_lasttv.tv_sec) * 1000000000) + nkeynes@964: (tv.tv_usec - cocoa_gui_lasttv.tv_usec)*1000; nkeynes@964: if( (ns * 1.05) < current_period ) { nkeynes@964: // We've gotten ahead - sleep for a little bit nkeynes@964: struct timespec tv; nkeynes@964: tv.tv_sec = 0; nkeynes@964: tv.tv_nsec = current_period - ns; nkeynes@964: nanosleep(&tv, &tv); nkeynes@964: } nkeynes@964: nkeynes@964: /* Update the display every 10 ticks (ie 10 times a second) and nkeynes@964: * save the current tv value */ nkeynes@964: if( cocoa_gui_ticks > 10 ) { nkeynes@964: gchar buf[32]; nkeynes@964: cocoa_gui_ticks -= 10; nkeynes@964: nkeynes@964: double speed = (float)( (double)current_period * 100.0 / ns ); nkeynes@964: cocoa_gui_lasttv.tv_sec = tv.tv_sec; nkeynes@964: cocoa_gui_lasttv.tv_usec = tv.tv_usec; nkeynes@964: snprintf( buf, 32, _("Running (%2.4f%%)"), speed ); nkeynes@964: [mainWindow setStatusText: buf]; nkeynes@964: nkeynes@964: } nkeynes@964: } nkeynes@964: return nanosecs; nkeynes@964: } nkeynes@964: nkeynes@964: void cocoa_gui_update( void ) nkeynes@964: { nkeynes@964: nkeynes@964: } nkeynes@964: nkeynes@964: void cocoa_gui_start( void ) nkeynes@964: { nkeynes@964: [mainWindow setRunning: YES]; nkeynes@964: cocoa_gui_nanos = 0; nkeynes@964: gettimeofday(&cocoa_gui_lasttv,NULL); nkeynes@964: } nkeynes@964: nkeynes@964: void cocoa_gui_stop( void ) nkeynes@964: { nkeynes@964: [mainWindow setRunning: NO]; nkeynes@964: } nkeynes@964: nkeynes@1019: @interface DoLaterStub : NSObject nkeynes@1019: { nkeynes@1019: do_later_callback_t func; nkeynes@1019: } nkeynes@1019: @end nkeynes@1019: nkeynes@1019: @implementation DoLaterStub nkeynes@1019: - (id) init: (do_later_callback_t)f nkeynes@1019: { nkeynes@1019: [super init]; nkeynes@1019: func = f; nkeynes@1019: return self; nkeynes@1019: } nkeynes@1019: - (void) do nkeynes@1019: { nkeynes@1019: func(); nkeynes@1019: } nkeynes@1019: @end nkeynes@1019: nkeynes@964: /** nkeynes@964: * Queue a dreamcast_run() to execute after the currently event(s) nkeynes@964: */ nkeynes@1019: void gui_do_later( do_later_callback_t func ) nkeynes@964: { nkeynes@1019: DoLaterStub *stub = [[[DoLaterStub alloc] init: func] autorelease]; nkeynes@1019: [[NSRunLoop currentRunLoop] performSelector: @selector(do) nkeynes@1019: target: stub argument: nil order: 1 nkeynes@964: modes: [NSArray arrayWithObject: NSDefaultRunLoopMode] ]; nkeynes@964: } nkeynes@964: nkeynes@964: /*************************** Convenience methods ***************************/ nkeynes@964: nkeynes@964: NSImage *NSImage_new_from_framebuffer( frame_buffer_t buffer ) nkeynes@964: { nkeynes@964: NSBitmapImageRep *rep = nkeynes@964: [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: &buffer->data nkeynes@964: pixelsWide: buffer->width pixelsHigh: buffer->height nkeynes@964: bitsPerSample: 8 samplesPerPixel: 3 nkeynes@964: hasAlpha: NO isPlanar: NO nkeynes@964: colorSpaceName: NSDeviceRGBColorSpace bitmapFormat: 0 nkeynes@964: bytesPerRow: buffer->rowstride bitsPerPixel: 24]; nkeynes@964: nkeynes@964: NSImage *image = [[NSImage alloc] initWithSize: NSMakeSize(0.0,0.0)]; nkeynes@964: [image addRepresentation: rep]; nkeynes@964: return image; nkeynes@964: } nkeynes@964: nkeynes@964: nkeynes@964: NSTextField *cocoa_gui_add_label( NSView *parent, NSString *text, NSRect frame ) nkeynes@964: { nkeynes@964: NSTextField *label = [[NSTextField alloc] initWithFrame: frame]; nkeynes@964: [label setStringValue: text]; nkeynes@964: [label setBordered: NO]; nkeynes@964: [label setDrawsBackground: NO]; nkeynes@964: [label setEditable: NO]; nkeynes@964: [label setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)]; nkeynes@964: if( parent != NULL ) { nkeynes@964: [parent addSubview: label]; nkeynes@964: } nkeynes@964: return label; nkeynes@964: }