Search
lxdream.org :: lxdream/src/cocoaui/cocoaui.m :: diff
lxdream 0.9.1
released Jun 29
Download Now
filename src/cocoaui/cocoaui.m
changeset 964:f2f3c7612d06
next1015: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 +0000
1.2 +++ b/src/cocoaui/cocoaui.m Thu Jan 15 04:15:11 2009 +0000
1.3 @@ -0,0 +1,475 @@
1.4 +/**
1.5 + * $Id$
1.6 + *
1.7 + * Core Cocoa-based user interface
1.8 + *
1.9 + * Copyright (c) 2008 Nathan Keynes.
1.10 + *
1.11 + * This program is free software; you can redistribute it and/or modify
1.12 + * it under the terms of the GNU General Public License as published by
1.13 + * the Free Software Foundation; either version 2 of the License, or
1.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 of
1.18 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.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 runtime
1.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 +@end
1.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 any
1.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 Menu
1.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 AppName
1.119 + title = [@"Hide " stringByAppendingString:appName];
1.120 + [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
1.121 +
1.122 + // Hide Others
1.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 All
1.129 + [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
1.130 + [appleMenu addItem:[NSMenuItem separatorItem]];
1.131 +
1.132 + // Quit AppName
1.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 : NSObject
1.173 +@end
1.174 +
1.175 +@implementation LxdreamDelegate
1.176 +- (void)windowWillClose: (NSNotification *)notice
1.177 +{
1.178 + dreamcast_shutdown();
1.179 + exit(0);
1.180 +}
1.181 +- (void)windowDidBecomeMain: (NSNotification *)notice
1.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 *)notice
1.189 +{
1.190 + display_set_focused( TRUE );
1.191 +}
1.192 +- (void)windowDidResignKey: (NSNotification *)notice
1.193 +{
1.194 + display_set_focused( FALSE );
1.195 + [mainWindow setIsGrabbed: NO];
1.196 +}
1.197 +- (BOOL)application: (NSApplication *)app openFile: (NSString *)filename
1.198 +{
1.199 + const gchar *cname = [filename UTF8String];
1.200 + if( file_load_magic(cname) ) {
1.201 + // Queue up a run event
1.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)sender
1.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)sender
1.219 +{
1.220 + cocoa_gui_show_preferences();
1.221 +}
1.222 +- (void) load_action: (id)sender
1.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)sender
1.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)sender
1.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)sender
1.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)sender
1.269 +{
1.270 + dreamcast_stop();
1.271 +}
1.272 +
1.273 +- (void) reset_action: (id)sender
1.274 +{
1.275 + dreamcast_reset();
1.276 +}
1.277 +- (void) run_action: (id)sender
1.278 +{
1.279 + if( !dreamcast_is_running() ) {
1.280 + cocoa_gui_run_later();
1.281 + }
1.282 +}
1.283 +- (void) run_immediate
1.284 +{
1.285 + dreamcast_run();
1.286 + [mainWindow setRunning: NO];
1.287 +}
1.288 +- (void) gdrom_list_action: (id)sender
1.289 +{
1.290 + gdrom_list_set_selection( [sender tag] );
1.291 +}
1.292 +@end
1.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 like
1.298 + * -psn_0_... - we want to remove this so that lxdream doesn't try to process it
1.299 + * normally
1.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 loop
1.385 + pool = [NSAutoreleasePool new];
1.386 + while( (event = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: nil
1.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 bit
1.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) and
1.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: 1
1.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->data
1.454 + pixelsWide: buffer->width pixelsHigh: buffer->height
1.455 + bitsPerSample: 8 samplesPerPixel: 3
1.456 + hasAlpha: NO isPlanar: NO
1.457 + colorSpaceName: NSDeviceRGBColorSpace bitmapFormat: 0
1.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 +}
.