filename | src/cocoaui/cocoaui.m |
changeset | 964:f2f3c7612d06 |
next | 1015:ad448bedc48a |
author | nkeynes |
date | Thu Jan 15 04:15:11 2009 +0000 (15 years ago) |
permissions | -rw-r--r-- |
last change | Add support for the Intel ICC compiler (C only, icc doesn't support Obj-C) - Rename Obj-C source to .m - Separate paths.c into paths_unix.c and paths_osx.m - Add configuration detection of ICC, along with specific opt flags |
file | annotate | diff | log | raw |
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +00001.2 +++ b/src/cocoaui/cocoaui.m Thu Jan 15 04:15:11 2009 +00001.3 @@ -0,0 +1,475 @@1.4 +/**1.5 + * $Id$1.6 + *1.7 + * Core Cocoa-based user interface1.8 + *1.9 + * Copyright (c) 2008 Nathan Keynes.1.10 + *1.11 + * This program is free software; you can redistribute it and/or modify1.12 + * it under the terms of the GNU General Public License as published by1.13 + * the Free Software Foundation; either version 2 of the License, or1.14 + * (at your option) any later version.1.15 + *1.16 + * This program is distributed in the hope that it will be useful,1.17 + * but WITHOUT ANY WARRANTY; without even the implied warranty of1.18 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the1.19 + * GNU General Public License for more details.1.20 + */1.21 +1.22 +#include <AppKit/AppKit.h>1.23 +#include <stdio.h>1.24 +#include <stdlib.h>1.25 +#include <string.h>1.26 +#include <sys/time.h>1.27 +#include "lxdream.h"1.28 +#include "dream.h"1.29 +#include "dreamcast.h"1.30 +#include "config.h"1.31 +#include "display.h"1.32 +#include "gui.h"1.33 +#include "gdrom/gdrom.h"1.34 +#include "gdlist.h"1.35 +#include "loader.h"1.36 +#include "cocoaui/cocoaui.h"1.37 +1.38 +void cocoa_gui_update( void );1.39 +void cocoa_gui_start( void );1.40 +void cocoa_gui_stop( void );1.41 +void cocoa_gui_run_later( void );1.42 +uint32_t cocoa_gui_run_slice( uint32_t nanosecs );1.43 +1.44 +struct dreamcast_module cocoa_gui_module = { "gui", NULL,1.45 + cocoa_gui_update,1.46 + cocoa_gui_start,1.47 + cocoa_gui_run_slice,1.48 + cocoa_gui_stop,1.49 + NULL, NULL };1.50 +1.51 +/**1.52 + * Count of running nanoseconds - used to cut back on the GUI runtime1.53 + */1.54 +static uint32_t cocoa_gui_nanos = 0;1.55 +static uint32_t cocoa_gui_ticks = 0;1.56 +static struct timeval cocoa_gui_lasttv;1.57 +static BOOL cocoa_gui_autorun = NO;1.58 +static BOOL cocoa_gui_is_running = NO;1.59 +static LxdreamMainWindow *mainWindow = NULL;1.60 +1.61 +@interface NSApplication (PrivateAdditions)1.62 +- (void) setAppleMenu:(NSMenu *)aMenu;1.63 +@end1.64 +1.65 +gboolean cocoa_gui_disc_changed( gdrom_disc_t disc, const gchar *disc_name, void *user_data )1.66 +{1.67 + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];1.68 + LxdreamMainWindow *window = (LxdreamMainWindow *)user_data;1.69 + [window updateTitle];1.70 + [pool release];1.71 + return TRUE;1.72 +}1.73 +1.74 +/**1.75 + * Produces the menu title by looking the text up in gettext, removing any1.76 + * underscores, and returning the result as an NSString.1.77 + */1.78 +static NSString *NSMENU_( const char *text )1.79 +{1.80 + const char *s = gettext(text);1.81 + char buf[strlen(s)+1];1.82 + char *d = buf;1.83 +1.84 + while( *s != '\0' ) {1.85 + if( *s != '_' ) {1.86 + *d++ = *s;1.87 + }1.88 + s++;1.89 + }1.90 + *d = '\0';1.91 +1.92 + return [NSString stringWithUTF8String: buf];1.93 +}1.94 +1.95 +static void cocoa_gui_create_menu(void)1.96 +{1.97 + NSMenu *appleMenu, *services;1.98 + NSMenuItem *menuItem;1.99 + NSString *title;1.100 + NSString *appName;1.101 +1.102 + appName = @"Lxdream";1.103 + appleMenu = [[NSMenu alloc] initWithTitle:@""];1.104 +1.105 + /* Add menu items */1.106 + title = [@"About " stringByAppendingString:appName];1.107 + [appleMenu addItemWithTitle:title action:@selector(about_action:) keyEquivalent:@""];1.108 +1.109 + [appleMenu addItem:[NSMenuItem separatorItem]];1.110 + [appleMenu addItemWithTitle: NSMENU_("_Preferences...") action:@selector(preferences_action:) keyEquivalent:@","];1.111 +1.112 + // Services Menu1.113 + [appleMenu addItem:[NSMenuItem separatorItem]];1.114 + services = [[[NSMenu alloc] init] autorelease];1.115 + [appleMenu addItemWithTitle: NS_("Services") action:nil keyEquivalent:@""];1.116 + [appleMenu setSubmenu: services forItem: [appleMenu itemWithTitle: @"Services"]];1.117 +1.118 + // Hide AppName1.119 + title = [@"Hide " stringByAppendingString:appName];1.120 + [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];1.121 +1.122 + // Hide Others1.123 + menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others"1.124 + action:@selector(hideOtherApplications:)1.125 + keyEquivalent:@"h"];1.126 + [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];1.127 +1.128 + // Show All1.129 + [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];1.130 + [appleMenu addItem:[NSMenuItem separatorItem]];1.131 +1.132 + // Quit AppName1.133 + title = [@"Quit " stringByAppendingString:appName];1.134 + [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];1.135 +1.136 + /* Put menu into the menubar */1.137 + menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];1.138 + [menuItem setSubmenu: appleMenu];1.139 + NSMenu *menu = [NSMenu new];1.140 + [menu addItem: menuItem];1.141 +1.142 + NSMenu *gdromMenu = cocoa_gdrom_menu_new();1.143 +1.144 + NSMenu *fileMenu = [[NSMenu alloc] initWithTitle: NSMENU_("_File")];1.145 + [fileMenu addItemWithTitle: NSMENU_("Load _Binary...") action: @selector(load_binary_action:) keyEquivalent: @"b"];1.146 + [[fileMenu addItemWithTitle: NSMENU_("_GD-Rom") action: nil keyEquivalent: @""]1.147 + setSubmenu: gdromMenu];1.148 + [fileMenu addItem: [NSMenuItem separatorItem]];1.149 + [[fileMenu addItemWithTitle: NSMENU_("_Reset") action: @selector(reset_action:) keyEquivalent: @"r"]1.150 + setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];1.151 + [fileMenu addItemWithTitle: NSMENU_("_Pause") action: @selector(pause_action:) keyEquivalent: @"p"];1.152 + [fileMenu addItemWithTitle: NS_("Resume") action: @selector(run_action:) keyEquivalent: @"r"];1.153 + [fileMenu addItem: [NSMenuItem separatorItem]];1.154 + [fileMenu addItemWithTitle: NSMENU_("_Load State...") action: @selector(load_action:) keyEquivalent: @"o"];1.155 + [fileMenu addItemWithTitle: NSMENU_("_Save State...") action: @selector(save_action:) keyEquivalent: @"s"];1.156 +1.157 + menuItem = [[NSMenuItem alloc] initWithTitle:NSMENU_("_File") action: nil keyEquivalent: @""];1.158 + [menuItem setSubmenu: fileMenu];1.159 + [menu addItem: menuItem];1.160 +1.161 + /* Tell the application object that this is now the application menu */1.162 + [NSApp setMainMenu: menu];1.163 + [NSApp setAppleMenu: appleMenu];1.164 + [NSApp setServicesMenu: services];1.165 +1.166 + /* Finally give up our references to the objects */1.167 + [appleMenu release];1.168 + [menuItem release];1.169 + [menu release];1.170 +}1.171 +1.172 +@interface LxdreamDelegate : NSObject1.173 +@end1.174 +1.175 +@implementation LxdreamDelegate1.176 +- (void)windowWillClose: (NSNotification *)notice1.177 +{1.178 + dreamcast_shutdown();1.179 + exit(0);1.180 +}1.181 +- (void)windowDidBecomeMain: (NSNotification *)notice1.182 +{1.183 + if( cocoa_gui_autorun ) {1.184 + cocoa_gui_autorun = NO;1.185 + cocoa_gui_run_later();1.186 + }1.187 +}1.188 +- (void)windowDidBecomeKey: (NSNotification *)notice1.189 +{1.190 + display_set_focused( TRUE );1.191 +}1.192 +- (void)windowDidResignKey: (NSNotification *)notice1.193 +{1.194 + display_set_focused( FALSE );1.195 + [mainWindow setIsGrabbed: NO];1.196 +}1.197 +- (BOOL)application: (NSApplication *)app openFile: (NSString *)filename1.198 +{1.199 + const gchar *cname = [filename UTF8String];1.200 + if( file_load_magic(cname) ) {1.201 + // Queue up a run event1.202 + cocoa_gui_run_later();1.203 + return YES;1.204 + } else {1.205 + return NO;1.206 + }1.207 +1.208 +}1.209 +- (void) about_action: (id)sender1.210 +{1.211 + NSArray *keys = [NSArray arrayWithObjects: @"Version", @"Copyright", nil];1.212 + NSArray *values = [NSArray arrayWithObjects: NS_(lxdream_full_version), NS_(lxdream_copyright), nil];1.213 +1.214 + NSDictionary *options= [NSDictionary dictionaryWithObjects: values forKeys: keys];1.215 +1.216 + [NSApp orderFrontStandardAboutPanelWithOptions: options];1.217 +}1.218 +- (void) preferences_action: (id)sender1.219 +{1.220 + cocoa_gui_show_preferences();1.221 +}1.222 +- (void) load_action: (id)sender1.223 +{1.224 + NSOpenPanel *panel = [NSOpenPanel openPanel];1.225 + const gchar *dir = lxdream_get_config_value(CONFIG_SAVE_PATH);1.226 + NSString *path = (dir == NULL ? NSHomeDirectory() : [NSString stringWithCString: dir]);1.227 + NSArray *fileTypes = [NSArray arrayWithObject: @"dst"];1.228 + int result = [panel runModalForDirectory: path file: nil types: fileTypes];1.229 + if( result == NSOKButton && [[panel filenames] count] > 0 ) {1.230 + NSString *filename = [[panel filenames] objectAtIndex: 0];1.231 + dreamcast_load_state( [filename UTF8String] );1.232 + }1.233 +}1.234 +- (void) save_action: (id)sender1.235 +{1.236 + NSSavePanel *panel = [NSSavePanel savePanel];1.237 + const gchar *dir = lxdream_get_config_value(CONFIG_SAVE_PATH);1.238 + NSString *path = (dir == NULL ? NSHomeDirectory() : [NSString stringWithCString: dir]);1.239 + [panel setRequiredFileType: @"dst"];1.240 + int result = [panel runModalForDirectory: path file:@""];1.241 + if( result == NSOKButton ) {1.242 + NSString *filename = [panel filename];1.243 + dreamcast_save_state( [filename UTF8String] );1.244 + }1.245 +}1.246 +- (void) load_binary_action: (id)sender1.247 +{1.248 + NSOpenPanel *panel = [NSOpenPanel openPanel];1.249 + const gchar *dir = lxdream_get_config_value(CONFIG_DEFAULT_PATH);1.250 + NSString *path = (dir == NULL ? NSHomeDirectory() : [NSString stringWithCString: dir]);1.251 + int result = [panel runModalForDirectory: path file: nil types: nil];1.252 + if( result == NSOKButton && [[panel filenames] count] > 0 ) {1.253 + NSString *filename = [[panel filenames] objectAtIndex: 0];1.254 + file_load_magic( [filename UTF8String] );1.255 + }1.256 +}1.257 +- (void) mount_action: (id)sender1.258 +{1.259 + NSOpenPanel *panel = [NSOpenPanel openPanel];1.260 + const gchar *dir = lxdream_get_config_value(CONFIG_DEFAULT_PATH);1.261 + NSString *path = (dir == NULL ? NSHomeDirectory() : [NSString stringWithCString: dir]);1.262 + int result = [panel runModalForDirectory: path file: nil types: nil];1.263 + if( result == NSOKButton && [[panel filenames] count] > 0 ) {1.264 + NSString *filename = [[panel filenames] objectAtIndex: 0];1.265 + gdrom_mount_image( [filename UTF8String] );1.266 + }1.267 +}1.268 +- (void) pause_action: (id)sender1.269 +{1.270 + dreamcast_stop();1.271 +}1.272 +1.273 +- (void) reset_action: (id)sender1.274 +{1.275 + dreamcast_reset();1.276 +}1.277 +- (void) run_action: (id)sender1.278 +{1.279 + if( !dreamcast_is_running() ) {1.280 + cocoa_gui_run_later();1.281 + }1.282 +}1.283 +- (void) run_immediate1.284 +{1.285 + dreamcast_run();1.286 + [mainWindow setRunning: NO];1.287 +}1.288 +- (void) gdrom_list_action: (id)sender1.289 +{1.290 + gdrom_list_set_selection( [sender tag] );1.291 +}1.292 +@end1.293 +1.294 +1.295 +gboolean gui_parse_cmdline( int *argc, char **argv[] )1.296 +{1.297 + /* If started from the finder, the first (and only) arg will look something like1.298 + * -psn_0_... - we want to remove this so that lxdream doesn't try to process it1.299 + * normally1.300 + */1.301 + if( *argc == 2 && strncmp((*argv)[1], "-psn_", 5) == 0 ) {1.302 + *argc = 1;1.303 + }1.304 + return TRUE;1.305 +}1.306 +1.307 +gboolean gui_init( gboolean withDebug )1.308 +{1.309 + dreamcast_register_module( &cocoa_gui_module );1.310 +1.311 + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];1.312 + [NSApplication sharedApplication];1.313 +1.314 + LxdreamDelegate *delegate = [[LxdreamDelegate alloc] init];1.315 + [NSApp setDelegate: delegate];1.316 + NSString *iconFile = [[NSBundle mainBundle] pathForResource:@"dcemu" ofType:@"gif"];1.317 + NSImage *iconImage = [[NSImage alloc] initWithContentsOfFile: iconFile];1.318 + [iconImage setName: @"NSApplicationIcon"];1.319 + [NSApp setApplicationIconImage: iconImage];1.320 + cocoa_gui_create_menu();1.321 + mainWindow = cocoa_gui_create_main_window();1.322 + [mainWindow makeKeyAndOrderFront: nil];1.323 + [NSApp activateIgnoringOtherApps: YES];1.324 +1.325 + register_gdrom_disc_change_hook( cocoa_gui_disc_changed, mainWindow );1.326 +1.327 + [pool release];1.328 + return TRUE;1.329 +}1.330 +1.331 +void gui_main_loop( gboolean run )1.332 +{1.333 + if( run ) {1.334 + cocoa_gui_autorun = YES;1.335 + }1.336 + cocoa_gui_is_running = YES;1.337 + [NSApp run];1.338 + cocoa_gui_is_running = NO;1.339 +}1.340 +1.341 +void gui_update_state(void)1.342 +{1.343 + cocoa_gui_update();1.344 +}1.345 +1.346 +void gui_set_use_grab( gboolean grab )1.347 +{1.348 + [mainWindow setUseGrab: (grab ? YES : NO)];1.349 +}1.350 +1.351 +gboolean gui_error_dialog( const char *msg, ... )1.352 +{1.353 + if( cocoa_gui_is_running ) {1.354 + NSString *error_string;1.355 +1.356 + va_list args;1.357 + va_start(args, msg);1.358 + error_string = [[NSString alloc] initWithFormat: [NSString stringWithCString: msg] arguments: args];1.359 + NSRunAlertPanel(NS_("Error in Lxdream"), error_string, nil, nil, nil);1.360 + va_end(args);1.361 + return TRUE;1.362 + } else {1.363 + return FALSE;1.364 + }1.365 +}1.366 +1.367 +void gui_update_io_activity( io_activity_type io, gboolean active )1.368 +{1.369 +1.370 +}1.371 +1.372 +1.373 +uint32_t cocoa_gui_run_slice( uint32_t nanosecs )1.374 +{1.375 + NSEvent *event;1.376 + NSAutoreleasePool *pool;1.377 +1.378 + cocoa_gui_nanos += nanosecs;1.379 + if( cocoa_gui_nanos > GUI_TICK_PERIOD ) { /* 10 ms */1.380 + cocoa_gui_nanos -= GUI_TICK_PERIOD;1.381 + cocoa_gui_ticks ++;1.382 + uint32_t current_period = cocoa_gui_ticks * GUI_TICK_PERIOD;1.383 +1.384 + // Run the event loop1.385 + pool = [NSAutoreleasePool new];1.386 + while( (event = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: nil1.387 + inMode: NSDefaultRunLoopMode dequeue: YES]) != nil ) {1.388 + [NSApp sendEvent: event];1.389 + }1.390 + [pool release];1.391 +1.392 + struct timeval tv;1.393 + gettimeofday(&tv,NULL);1.394 + uint32_t ns = ((tv.tv_sec - cocoa_gui_lasttv.tv_sec) * 1000000000) +1.395 + (tv.tv_usec - cocoa_gui_lasttv.tv_usec)*1000;1.396 + if( (ns * 1.05) < current_period ) {1.397 + // We've gotten ahead - sleep for a little bit1.398 + struct timespec tv;1.399 + tv.tv_sec = 0;1.400 + tv.tv_nsec = current_period - ns;1.401 + nanosleep(&tv, &tv);1.402 + }1.403 +1.404 + /* Update the display every 10 ticks (ie 10 times a second) and1.405 + * save the current tv value */1.406 + if( cocoa_gui_ticks > 10 ) {1.407 + gchar buf[32];1.408 + cocoa_gui_ticks -= 10;1.409 +1.410 + double speed = (float)( (double)current_period * 100.0 / ns );1.411 + cocoa_gui_lasttv.tv_sec = tv.tv_sec;1.412 + cocoa_gui_lasttv.tv_usec = tv.tv_usec;1.413 + snprintf( buf, 32, _("Running (%2.4f%%)"), speed );1.414 + [mainWindow setStatusText: buf];1.415 +1.416 + }1.417 + }1.418 + return nanosecs;1.419 +}1.420 +1.421 +void cocoa_gui_update( void )1.422 +{1.423 +1.424 +}1.425 +1.426 +void cocoa_gui_start( void )1.427 +{1.428 + [mainWindow setRunning: YES];1.429 + cocoa_gui_nanos = 0;1.430 + gettimeofday(&cocoa_gui_lasttv,NULL);1.431 +}1.432 +1.433 +void cocoa_gui_stop( void )1.434 +{1.435 + [mainWindow setRunning: NO];1.436 +}1.437 +1.438 +/**1.439 + * Queue a dreamcast_run() to execute after the currently event(s)1.440 + */1.441 +void cocoa_gui_run_later( void )1.442 +{1.443 + [[NSRunLoop currentRunLoop] performSelector: @selector(run_immediate)1.444 + target: [NSApp delegate] argument: nil order: 11.445 + modes: [NSArray arrayWithObject: NSDefaultRunLoopMode] ];1.446 +}1.447 +1.448 +/*************************** Convenience methods ***************************/1.449 +1.450 +NSImage *NSImage_new_from_framebuffer( frame_buffer_t buffer )1.451 +{1.452 + NSBitmapImageRep *rep =1.453 + [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: &buffer->data1.454 + pixelsWide: buffer->width pixelsHigh: buffer->height1.455 + bitsPerSample: 8 samplesPerPixel: 31.456 + hasAlpha: NO isPlanar: NO1.457 + colorSpaceName: NSDeviceRGBColorSpace bitmapFormat: 01.458 + bytesPerRow: buffer->rowstride bitsPerPixel: 24];1.459 +1.460 + NSImage *image = [[NSImage alloc] initWithSize: NSMakeSize(0.0,0.0)];1.461 + [image addRepresentation: rep];1.462 + return image;1.463 +}1.464 +1.465 +1.466 +NSTextField *cocoa_gui_add_label( NSView *parent, NSString *text, NSRect frame )1.467 +{1.468 + NSTextField *label = [[NSTextField alloc] initWithFrame: frame];1.469 + [label setStringValue: text];1.470 + [label setBordered: NO];1.471 + [label setDrawsBackground: NO];1.472 + [label setEditable: NO];1.473 + [label setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];1.474 + if( parent != NULL ) {1.475 + [parent addSubview: label];1.476 + }1.477 + return label;1.478 +}
.