SDL-1.2.14
[sdl_omap.git] / src / main / macos / SDL_main.c
1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2009 Sam Lantinga
4
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Lesser General Public
7     License as published by the Free Software Foundation; either
8     version 2.1 of the License, or (at your option) any later version.
9
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Lesser General Public License for more details.
14
15     You should have received a copy of the GNU Lesser General Public
16     License along with this library; if not, write to the Free Software
17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19     Sam Lantinga
20     slouken@libsdl.org
21 */
22
23 /* This file takes care of command line argument parsing, and stdio redirection
24    in the MacOS environment. (stdio/stderr is *not* directed for Mach-O builds)
25  */
26
27 #if defined(__APPLE__) && defined(__MACH__)
28 #include <Carbon/Carbon.h>
29 #elif TARGET_API_MAC_CARBON && (UNIVERSAL_INTERFACES_VERSION > 0x0335)
30 #include <Carbon.h>
31 #else
32 #include <Dialogs.h>
33 #include <Fonts.h>
34 #include <Events.h>
35 #include <Resources.h>
36 #include <Folders.h>
37 #endif
38
39 /* Include the SDL main definition header */
40 #include "SDL.h"
41 #include "SDL_main.h"
42 #ifdef main
43 #undef main
44 #endif
45
46 #if !(defined(__APPLE__) && defined(__MACH__))
47 /* The standard output files */
48 #define STDOUT_FILE     "stdout.txt"
49 #define STDERR_FILE     "stderr.txt"
50 #endif
51
52 #if !defined(__MWERKS__) && !TARGET_API_MAC_CARBON
53         /* In MPW, the qd global has been removed from the libraries */
54         QDGlobals qd;
55 #endif
56
57 /* Structure for keeping prefs in 1 variable */
58 typedef struct {
59     Str255  command_line;
60     Str255  video_driver_name;
61     Boolean output_to_file;
62 }  PrefsRecord;
63
64 /* See if the command key is held down at startup */
65 static Boolean CommandKeyIsDown(void)
66 {
67         KeyMap  theKeyMap;
68
69         GetKeys(theKeyMap);
70
71         if (((unsigned char *) theKeyMap)[6] & 0x80) {
72                 return(true);
73         }
74         return(false);
75 }
76
77 #if !(defined(__APPLE__) && defined(__MACH__))
78
79 /* Parse a command line buffer into arguments */
80 static int ParseCommandLine(char *cmdline, char **argv)
81 {
82         char *bufp;
83         int argc;
84
85         argc = 0;
86         for ( bufp = cmdline; *bufp; ) {
87                 /* Skip leading whitespace */
88                 while ( SDL_isspace(*bufp) ) {
89                         ++bufp;
90                 }
91                 /* Skip over argument */
92                 if ( *bufp == '"' ) {
93                         ++bufp;
94                         if ( *bufp ) {
95                                 if ( argv ) {
96                                         argv[argc] = bufp;
97                                 }
98                                 ++argc;
99                         }
100                         /* Skip over word */
101                         while ( *bufp && (*bufp != '"') ) {
102                                 ++bufp;
103                         }
104                 } else {
105                         if ( *bufp ) {
106                                 if ( argv ) {
107                                         argv[argc] = bufp;
108                                 }
109                                 ++argc;
110                         }
111                         /* Skip over word */
112                         while ( *bufp && ! SDL_isspace(*bufp) ) {
113                                 ++bufp;
114                         }
115                 }
116                 if ( *bufp ) {
117                         if ( argv ) {
118                                 *bufp = '\0';
119                         }
120                         ++bufp;
121                 }
122         }
123         if ( argv ) {
124                 argv[argc] = NULL;
125         }
126         return(argc);
127 }
128
129 /* Remove the output files if there was no output written */
130 static void cleanup_output(void)
131 {
132         FILE *file;
133         int empty;
134
135         /* Flush the output in case anything is queued */
136         fclose(stdout);
137         fclose(stderr);
138
139         /* See if the files have any output in them */
140         file = fopen(STDOUT_FILE, "rb");
141         if ( file ) {
142                 empty = (fgetc(file) == EOF) ? 1 : 0;
143                 fclose(file);
144                 if ( empty ) {
145                         remove(STDOUT_FILE);
146                 }
147         }
148         file = fopen(STDERR_FILE, "rb");
149         if ( file ) {
150                 empty = (fgetc(file) == EOF) ? 1 : 0;
151                 fclose(file);
152                 if ( empty ) {
153                         remove(STDERR_FILE);
154                 }
155         }
156 }
157
158 #endif //!(defined(__APPLE__) && defined(__MACH__))
159
160 static int getCurrentAppName (StrFileName name) {
161         
162     ProcessSerialNumber process;
163     ProcessInfoRec      process_info;
164     FSSpec              process_fsp;
165     
166     process.highLongOfPSN = 0;
167     process.lowLongOfPSN  = kCurrentProcess;
168     process_info.processInfoLength = sizeof (process_info);
169     process_info.processName    = NULL;
170     process_info.processAppSpec = &process_fsp;
171     
172     if ( noErr != GetProcessInformation (&process, &process_info) )
173        return 0;
174     
175     SDL_memcpy(name, process_fsp.name, process_fsp.name[0] + 1);
176     return 1;
177 }
178
179 static int getPrefsFile (FSSpec *prefs_fsp, int create) {
180
181     /* The prefs file name is the application name, possibly truncated, */
182     /* plus " Preferences */
183     
184     #define  SUFFIX   " Preferences"
185     #define  MAX_NAME 19             /* 31 - strlen (SUFFIX) */
186     
187     short  volume_ref_number;
188     long   directory_id;
189     StrFileName  prefs_name;
190     StrFileName  app_name;
191     
192     /* Get Preferences folder - works with Multiple Users */
193     if ( noErr != FindFolder ( kOnSystemDisk, kPreferencesFolderType, kDontCreateFolder,
194                                &volume_ref_number, &directory_id) )
195         exit (-1);
196     
197     if ( ! getCurrentAppName (app_name) )
198         exit (-1);
199     
200     /* Truncate if name is too long */
201     if (app_name[0] > MAX_NAME )
202         app_name[0] = MAX_NAME;
203         
204     SDL_memcpy(prefs_name + 1, app_name + 1, app_name[0]);    
205     SDL_memcpy(prefs_name + app_name[0] + 1, SUFFIX, strlen (SUFFIX));
206     prefs_name[0] = app_name[0] + strlen (SUFFIX);
207    
208     /* Make the file spec for prefs file */
209     if ( noErr != FSMakeFSSpec (volume_ref_number, directory_id, prefs_name, prefs_fsp) ) {
210         if ( !create )
211             return 0;
212         else {
213             /* Create the prefs file */
214             SDL_memcpy(prefs_fsp->name, prefs_name, prefs_name[0] + 1);
215             prefs_fsp->parID   = directory_id;
216             prefs_fsp->vRefNum = volume_ref_number;
217                 
218             FSpCreateResFile (prefs_fsp, 0x3f3f3f3f, 'pref', 0); // '????' parsed as trigraph
219             
220             if ( noErr != ResError () )
221                 return 0;
222         }
223      }
224     return 1;
225 }
226
227 static int readPrefsResource (PrefsRecord *prefs) {
228     
229     Handle prefs_handle;
230     
231     prefs_handle = Get1Resource( 'CLne', 128 );
232
233         if (prefs_handle != NULL) {
234                 int offset = 0;
235 //              int j      = 0;
236                 
237                 HLock(prefs_handle);
238                 
239                 /* Get command line string */   
240                 SDL_memcpy(prefs->command_line, *prefs_handle, (*prefs_handle)[0]+1);
241
242                 /* Get video driver name */
243                 offset += (*prefs_handle)[0] + 1;       
244                 SDL_memcpy(prefs->video_driver_name, *prefs_handle + offset, (*prefs_handle)[offset] + 1);              
245                 
246                 /* Get save-to-file option (1 or 0) */
247                 offset += (*prefs_handle)[offset] + 1;
248                 prefs->output_to_file = (*prefs_handle)[offset];
249                 
250                 ReleaseResource( prefs_handle );
251     
252         return ResError() == noErr;
253     }
254
255     return 0;
256 }
257
258 static int writePrefsResource (PrefsRecord *prefs, short resource_file) {
259
260     Handle prefs_handle;
261     
262     UseResFile (resource_file);
263     
264     prefs_handle = Get1Resource ( 'CLne', 128 );
265     if (prefs_handle != NULL)
266         RemoveResource (prefs_handle);
267     
268     prefs_handle = NewHandle ( prefs->command_line[0] + prefs->video_driver_name[0] + 4 );
269     if (prefs_handle != NULL) {
270     
271         int offset;
272         
273         HLock (prefs_handle);
274         
275         /* Command line text */
276         offset = 0;
277         SDL_memcpy(*prefs_handle, prefs->command_line, prefs->command_line[0] + 1);
278         
279         /* Video driver name */
280         offset += prefs->command_line[0] + 1;
281         SDL_memcpy(*prefs_handle + offset, prefs->video_driver_name, prefs->video_driver_name[0] + 1);
282         
283         /* Output-to-file option */
284         offset += prefs->video_driver_name[0] + 1;
285         *( *((char**)prefs_handle) + offset)     = (char)prefs->output_to_file;
286         *( *((char**)prefs_handle) + offset + 1) = 0;
287               
288         AddResource   (prefs_handle, 'CLne', 128, "\pCommand Line");
289         WriteResource (prefs_handle);
290         UpdateResFile (resource_file);
291         DisposeHandle (prefs_handle);
292         
293         return ResError() == noErr;
294     }
295     
296     return 0;
297 }
298
299 static int readPreferences (PrefsRecord *prefs) {
300
301     int    no_error = 1;
302     FSSpec prefs_fsp;
303
304     /* Check for prefs file first */
305     if ( getPrefsFile (&prefs_fsp, 0) ) {
306     
307         short  prefs_resource;
308         
309         prefs_resource = FSpOpenResFile (&prefs_fsp, fsRdPerm);
310         if ( prefs_resource == -1 ) /* this shouldn't happen, but... */
311             return 0;
312     
313         UseResFile   (prefs_resource);
314         no_error = readPrefsResource (prefs);     
315         CloseResFile (prefs_resource);
316     }
317     
318     /* Fall back to application's resource fork (reading only, so this is safe) */
319     else {
320     
321           no_error = readPrefsResource (prefs);
322      }
323
324     return no_error;
325 }
326
327 static int writePreferences (PrefsRecord *prefs) {
328     
329     int    no_error = 1;
330     FSSpec prefs_fsp;
331     
332     /* Get prefs file, create if it doesn't exist */
333     if ( getPrefsFile (&prefs_fsp, 1) ) {
334     
335         short  prefs_resource;
336         
337         prefs_resource = FSpOpenResFile (&prefs_fsp, fsRdWrPerm);
338         if (prefs_resource == -1)
339             return 0;
340         no_error = writePrefsResource (prefs, prefs_resource);
341         CloseResFile (prefs_resource);
342     }
343     
344     return no_error;
345 }
346
347 /* This is where execution begins */
348 int main(int argc, char *argv[])
349 {
350
351 #if !(defined(__APPLE__) && defined(__MACH__))
352 #pragma unused(argc, argv)
353 #endif
354         
355 #define DEFAULT_ARGS "\p"                /* pascal string for default args */
356 #define DEFAULT_VIDEO_DRIVER "\ptoolbox" /* pascal string for default video driver name */      
357 #define DEFAULT_OUTPUT_TO_FILE 1         /* 1 == output to file, 0 == no output */
358
359 #define VIDEO_ID_DRAWSPROCKET 1          /* these correspond to popup menu choices */
360 #define VIDEO_ID_TOOLBOX      2
361
362     PrefsRecord prefs = { DEFAULT_ARGS, DEFAULT_VIDEO_DRIVER, DEFAULT_OUTPUT_TO_FILE }; 
363         
364 #if !(defined(__APPLE__) && defined(__MACH__))
365         int     nargs;
366         char   **args;
367         char   *commandLine;
368         
369         StrFileName  appNameText;
370 #endif
371         int     videodriver     = VIDEO_ID_TOOLBOX;
372     int     settingsChanged = 0;
373     
374     long        i;
375
376         /* Kyle's SDL command-line dialog code ... */
377 #if !TARGET_API_MAC_CARBON
378         InitGraf    (&qd.thePort);
379         InitFonts   ();
380         InitWindows ();
381         InitMenus   ();
382         InitDialogs (nil);
383 #endif
384         InitCursor ();
385         FlushEvents(everyEvent,0);
386 #if !TARGET_API_MAC_CARBON
387         MaxApplZone ();
388 #endif
389         MoreMasters ();
390         MoreMasters ();
391 #if 0
392         /* Intialize SDL, and put up a dialog if we fail */
393         if ( SDL_Init (0) < 0 ) {
394
395 #define kErr_OK         1
396 #define kErr_Text       2
397
398         DialogPtr errorDialog;
399         short     dummyType;
400         Rect      dummyRect;
401             Handle    dummyHandle;
402             short     itemHit;
403         
404                 errorDialog = GetNewDialog (1001, nil, (WindowPtr)-1);
405                 if (errorDialog == NULL)
406                     return -1;
407                 DrawDialog (errorDialog);
408                 
409                 GetDialogItem (errorDialog, kErr_Text, &dummyType, &dummyHandle, &dummyRect);
410                 SetDialogItemText (dummyHandle, "\pError Initializing SDL");
411                 
412 #if TARGET_API_MAC_CARBON
413                 SetPort (GetDialogPort(errorDialog));
414 #else
415                 SetPort (errorDialog);
416 #endif
417                 do {
418                         ModalDialog (nil, &itemHit);
419                 } while (itemHit != kErr_OK);
420                 
421                 DisposeDialog (errorDialog);
422                 exit (-1);
423         }
424         atexit(cleanup_output);
425         atexit(SDL_Quit);
426 #endif
427
428 /* Set up SDL's QuickDraw environment  */
429 #if !TARGET_API_MAC_CARBON
430         SDL_InitQuickDraw(&qd);
431 #endif
432
433          if ( readPreferences (&prefs) ) {
434                 
435         if (SDL_memcmp(prefs.video_driver_name+1, "DSp", 3) == 0)
436             videodriver = 1;
437         else if (SDL_memcmp(prefs.video_driver_name+1, "toolbox", 7) == 0)
438             videodriver = 2;
439          }
440                 
441         if ( CommandKeyIsDown() ) {
442
443 #define kCL_OK          1
444 #define kCL_Cancel      2
445 #define kCL_Text        3
446 #define kCL_File        4
447 #define kCL_Video   6
448        
449         DialogPtr commandDialog;
450         short     dummyType;
451         Rect      dummyRect;
452         Handle    dummyHandle;
453         short     itemHit;
454    #if TARGET_API_MAC_CARBON
455         ControlRef control;
456    #endif
457         
458         /* Assume that they will change settings, rather than do exhaustive check */
459         settingsChanged = 1;
460         
461         /* Create dialog and display it */
462         commandDialog = GetNewDialog (1000, nil, (WindowPtr)-1);
463     #if TARGET_API_MAC_CARBON
464         SetPort ( GetDialogPort(commandDialog) );
465     #else
466         SetPort (commandDialog);
467      #endif
468            
469         /* Setup controls */
470     #if TARGET_API_MAC_CARBON
471         GetDialogItemAsControl(commandDialog, kCL_File, &control);
472         SetControlValue (control, prefs.output_to_file);
473     #else
474         GetDialogItem   (commandDialog, kCL_File, &dummyType, &dummyHandle, &dummyRect); /* MJS */
475         SetControlValue ((ControlHandle)dummyHandle, prefs.output_to_file );
476     #endif
477
478         GetDialogItem     (commandDialog, kCL_Text, &dummyType, &dummyHandle, &dummyRect);
479         SetDialogItemText (dummyHandle, prefs.command_line);
480
481     #if TARGET_API_MAC_CARBON
482         GetDialogItemAsControl(commandDialog, kCL_Video, &control);
483         SetControlValue (control, videodriver);
484    #else
485         GetDialogItem   (commandDialog, kCL_Video, &dummyType, &dummyHandle, &dummyRect);
486         SetControlValue ((ControlRef)dummyHandle, videodriver);
487      #endif
488
489         SetDialogDefaultItem (commandDialog, kCL_OK);
490         SetDialogCancelItem  (commandDialog, kCL_Cancel);
491
492         do {
493                         
494                 ModalDialog(nil, &itemHit); /* wait for user response */
495             
496             /* Toggle command-line output checkbox */   
497                 if ( itemHit == kCL_File ) {
498         #if TARGET_API_MAC_CARBON
499                         GetDialogItemAsControl(commandDialog, kCL_File, &control);
500                         SetControlValue (control, !GetControlValue(control));
501         #else
502                         GetDialogItem(commandDialog, kCL_File, &dummyType, &dummyHandle, &dummyRect); /* MJS */
503                         SetControlValue((ControlHandle)dummyHandle, !GetControlValue((ControlHandle)dummyHandle) );
504         #endif
505                 }
506
507         } while (itemHit != kCL_OK && itemHit != kCL_Cancel);
508
509         /* Get control values, even if they did not change */
510         GetDialogItem     (commandDialog, kCL_Text, &dummyType, &dummyHandle, &dummyRect); /* MJS */
511         GetDialogItemText (dummyHandle, prefs.command_line);
512
513     #if TARGET_API_MAC_CARBON
514         GetDialogItemAsControl(commandDialog, kCL_File, &control);
515         prefs.output_to_file = GetControlValue(control);
516         #else
517         GetDialogItem (commandDialog, kCL_File, &dummyType, &dummyHandle, &dummyRect); /* MJS */
518         prefs.output_to_file = GetControlValue ((ControlHandle)dummyHandle);
519         #endif
520
521     #if TARGET_API_MAC_CARBON
522         GetDialogItemAsControl(commandDialog, kCL_Video, &control);
523         videodriver = GetControlValue(control);
524     #else
525         GetDialogItem (commandDialog, kCL_Video, &dummyType, &dummyHandle, &dummyRect);
526         videodriver = GetControlValue ((ControlRef)dummyHandle);
527      #endif
528
529         DisposeDialog (commandDialog);
530
531         if (itemHit == kCL_Cancel ) {
532                 exit (0);
533         }
534         }
535     
536     /* Set pseudo-environment variables for video driver, update prefs */
537         switch ( videodriver ) {
538            case VIDEO_ID_DRAWSPROCKET: 
539               SDL_putenv("SDL_VIDEODRIVER=DSp");
540               SDL_memcpy(prefs.video_driver_name, "\pDSp", 4);
541               break;
542            case VIDEO_ID_TOOLBOX:
543               SDL_putenv("SDL_VIDEODRIVER=toolbox");
544               SDL_memcpy(prefs.video_driver_name, "\ptoolbox", 8);
545               break;
546         }
547
548 #if !(defined(__APPLE__) && defined(__MACH__))
549     /* Redirect standard I/O to files */
550         if ( prefs.output_to_file ) {
551                 freopen (STDOUT_FILE, "w", stdout);
552                 freopen (STDERR_FILE, "w", stderr);
553         } else {
554                 fclose (stdout);
555                 fclose (stderr);
556         }
557 #endif
558    
559     if (settingsChanged) {
560         /* Save the prefs, even if they might not have changed (but probably did) */
561         if ( ! writePreferences (&prefs) )
562             fprintf (stderr, "WARNING: Could not save preferences!\n");
563     }
564    
565 #if !(defined(__APPLE__) && defined(__MACH__))
566     appNameText[0] = 0;
567     getCurrentAppName (appNameText); /* check for error here ? */
568
569     commandLine = (char*) malloc (appNameText[0] + prefs.command_line[0] + 2);
570     if ( commandLine == NULL ) {
571        exit(-1);
572     }
573
574     /* Rather than rewrite ParseCommandLine method, let's replace  */
575     /* any spaces in application name with underscores,            */
576     /* so that the app name is only 1 argument                     */   
577     for (i = 1; i < 1+appNameText[0]; i++)
578         if ( appNameText[i] == ' ' ) appNameText[i] = '_';
579
580     /* Copy app name & full command text to command-line C-string */      
581     SDL_memcpy(commandLine, appNameText + 1, appNameText[0]);
582     commandLine[appNameText[0]] = ' ';
583     SDL_memcpy(commandLine + appNameText[0] + 1, prefs.command_line + 1, prefs.command_line[0]);
584     commandLine[ appNameText[0] + 1 + prefs.command_line[0] ] = '\0';
585
586     /* Parse C-string into argv and argc */
587     nargs = ParseCommandLine (commandLine, NULL);
588     args = (char **)malloc((nargs+1)*(sizeof *args));
589     if ( args == NULL ) {
590                 exit(-1);
591         }
592         ParseCommandLine (commandLine, args);
593         
594         /* Run the main application code */
595         SDL_main(nargs, args);
596         free (args);
597         free (commandLine);
598    
599         /* Remove useless stdout.txt and stderr.txt */
600         cleanup_output ();
601 #else // defined(__APPLE__) && defined(__MACH__)
602         SDL_main(argc, argv);
603 #endif
604         
605         /* Exit cleanly, calling atexit() functions */
606         exit (0);    
607
608         /* Never reached, but keeps the compiler quiet */
609         return (0);
610 }