• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2012 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 */
CommandKeyIsDown(void)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 */
ParseCommandLine(char * cmdline,char ** argv)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 */
cleanup_output(void)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 
getCurrentAppName(StrFileName name)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 
getPrefsFile(FSSpec * prefs_fsp,int create)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 
readPrefsResource(PrefsRecord * prefs)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 
writePrefsResource(PrefsRecord * prefs,short resource_file)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 
readPreferences(PrefsRecord * prefs)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 
writePreferences(PrefsRecord * prefs)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 */
main(int argc,char * argv[])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 }
611