e14743d1 |
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 | } |