1 /* main() ... start everything up. See mainw.c for main window stuff.
2 */
3
4 /*
5
6 Copyright (C) 1991-2003 The National Gallery
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License along
19 with this program; if not, write to the Free Software Foundation, Inc.,
20 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
22 */
23
24 /*
25
26 These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
27
28 */
29
30 #include "ip.h"
31
32 /*
33 #define DEBUG
34 */
35
36 /* Show all paint actions with flashing stuff.
37 #define DEBUG_UPDATES
38 */
39
40 /* Stop startup creation of externs for all VIPS functions etc.
41 #define DEBUG_NOAUTO
42 */
43
44 /* Stop on any gtk error/warning/whatever. Usually set by configure for dev
45 * builds.
46 #define DEBUG_FATAL
47 */
48
49 /* But some themes can trigger warnings, argh, so sometimes we need to
50 * undef it. VipsObject sets can trigger warnings. libgoffice will warn about
51 * precision issues if run under valgrind.
52 */
53 #undef DEBUG_FATAL
54
55 /* Time startup.
56 #define DEBUG_TIME
57 */
58
59 /* On quit, make sure we free stuff we can free.
60 #define DEBUG_LEAK
61 */
62
63 /* Sometimes we need to be able to disable these at build time.
64 #undef DEBUG_LEAK
65 #undef DEBUG_FATAL
66 */
67
68 /* General stuff.
69 */
70 Workspaceroot *main_workspaceroot = NULL; /* All the workspaces */
71 Toolkitgroup *main_toolkitgroup = NULL; /* All the toolkits */
72 Symbol *main_symbol_root = NULL; /* Root of symtable */
73 Watchgroup *main_watchgroup = NULL; /* All of the watches */
74 Imageinfogroup *main_imageinfogroup = NULL; /* All of the images */
75
76 void *main_c_stack_base = NULL; /* Base of C stack */
77
78 gboolean main_starting = TRUE; /* In startup */
79
80 static const char *main_argv0 = NULL; /* argv[0] */
81 static iOpenFile *main_stdin = NULL; /* stdin as an iOpenFile */
82 static GtkIconFactory *main_icon_factory = NULL;/* Add stocks to this */
83
84 static char *main_option_script = NULL;
85 static char *main_option_expression = NULL;
86 gboolean main_option_batch = FALSE;
87 static gboolean main_option_no_load_menus = FALSE;
88 static gboolean main_option_no_load_args = FALSE;
89 static gboolean main_option_stdin_ws = FALSE;
90 static gboolean main_option_stdin_def = FALSE;
91 static char *main_option_output = NULL;
92 static char **main_option_set = NULL;
93 static gboolean main_option_benchmark = FALSE;
94 gboolean main_option_time_save = FALSE;
95 gboolean main_option_profile = FALSE;
96 gboolean main_option_i18n = FALSE;
97 gboolean main_option_verbose = FALSE;
98 static gboolean main_option_print_main = FALSE;
99 static gboolean main_option_version = FALSE;
100 static gboolean main_option_test = FALSE;
101 static char *main_option_prefix = NULL;
102
103 static GOptionEntry main_option[] = {
104 { "expression", 'e', 0, G_OPTION_ARG_STRING, &main_option_expression,
105 N_( "evaluate and print EXPRESSION" ),
106 "EXPRESSION" },
107 { "script", 's', 0, G_OPTION_ARG_FILENAME, &main_option_script,
108 N_( "load FILE as a set of definitions" ),
109 "FILE" },
110 { "output", 'o', 0, G_OPTION_ARG_FILENAME, &main_option_output,
111 N_( "write value of 'main' to FILE" ), "FILE" },
112 { "batch", 'b', 0, G_OPTION_ARG_NONE, &main_option_batch,
113 N_( "run in batch mode" ), NULL },
114 { "set", '=', 0, G_OPTION_ARG_STRING_ARRAY, &main_option_set,
115 N_( "set values" ), NULL },
116 { "verbose", 'V', 0, G_OPTION_ARG_NONE, &main_option_verbose,
117 N_( "verbose error output" ), NULL },
118 { "no-load-menus", 'm', 0, G_OPTION_ARG_NONE,
119 &main_option_no_load_menus,
120 N_( "don't load menu definitions" ), NULL },
121 { "no-load-args", 'a', 0, G_OPTION_ARG_NONE, &main_option_no_load_args,
122 N_( "don't try to load command-line arguments" ), NULL },
123 { "stdin-ws", 'w', 0, G_OPTION_ARG_NONE, &main_option_stdin_ws,
124 N_( "load stdin as a workspace" ), NULL },
125 { "stdin-def", 'd', 0, G_OPTION_ARG_NONE, &main_option_stdin_def,
126 N_( "load stdin as a set of definitions" ), NULL },
127 { "print-main", 'p', 0, G_OPTION_ARG_NONE, &main_option_print_main,
128 N_( "print value of 'main' to stdout" ),
129 NULL },
130 { "benchmark", 'c', 0, G_OPTION_ARG_NONE, &main_option_benchmark,
131 N_( "start up and shut down" ),
132 NULL },
133 { "time-save", 't', 0, G_OPTION_ARG_NONE, &main_option_time_save,
134 N_( "time image save operations" ),
135 NULL },
136 { "profile", 'r', 0, G_OPTION_ARG_NONE, &main_option_profile,
137 N_( "profile workspace calculation" ),
138 NULL },
139 { "prefix", 'x', 0, G_OPTION_ARG_FILENAME, &main_option_prefix,
140 N_( "start as if installed to PREFIX" ), "PREFIX" },
141 { "i18n", 'i', 0, G_OPTION_ARG_NONE, &main_option_i18n,
142 N_( "output strings for internationalisation" ),
143 NULL },
144 { "version", 'v', 0, G_OPTION_ARG_NONE, &main_option_version,
145 N_( "print version number" ),
146 NULL },
147 { "test", 'T', 0, G_OPTION_ARG_NONE, &main_option_test,
148 N_( "test for errors and quit" ), NULL },
149 { NULL }
150 };
151
152 /* Accumulate startup errors here.
153 */
154 static char main_start_error_txt[MAX_STRSIZE];
155 static VipsBuf main_start_error = VIPS_BUF_STATIC( main_start_error_txt );
156
157 static void
main_log_add(const char * fmt,...)158 main_log_add( const char *fmt, ... )
159 {
160 va_list ap;
161
162 va_start( ap, fmt );
163 vips_buf_vappendf( &main_start_error, fmt, ap );
164 va_end( ap );
165 }
166
167 static const char *
main_log_get(void)168 main_log_get( void )
169 {
170 return( vips_buf_all( &main_start_error ) );
171 }
172
173 static gboolean
main_log_is_empty(void)174 main_log_is_empty( void )
175 {
176 return( vips_buf_is_empty( &main_start_error ) );
177 }
178
179 /* NULL log handler. Used to suppress output on win32 without DEBUG_FATAL.
180 */
181 #ifndef DEBUG_FATAL
182 #ifdef OS_WIN32
183 static void
main_log_null(const char * log_domain,GLogLevelFlags log_level,const char * message,void * user_data)184 main_log_null( const char *log_domain, GLogLevelFlags log_level,
185 const char *message, void *user_data )
186 {
187 }
188 #endif /*OS_WIN32*/
189 #endif /*!DEBUG_FATAL*/
190
191 /* Print all errors and quit. Batch mode only.
192 */
193 static void
main_error_exit(const char * fmt,...)194 main_error_exit( const char *fmt, ... )
195 {
196 va_list args;
197
198 va_start( args, fmt );
199 (void) vfprintf( stderr, fmt, args );
200 va_end( args );
201 fprintf( stderr, "\n" );
202
203 if( strcmp( error_get_top(), "" ) != 0 ) {
204 fprintf( stderr, "%s\n", error_get_top() );
205 if( strcmp( error_get_sub(), "" ) != 0 )
206 fprintf( stderr, "%s\n", error_get_sub() );
207 }
208
209 if( main_option_verbose ) {
210 char txt[MAX_STRSIZE];
211 VipsBuf buf = VIPS_BUF_STATIC( txt );
212
213 slist_map( expr_error_all,
214 (SListMapFn) expr_error_print, &buf );
215 fprintf( stderr, "%s", vips_buf_all( &buf ) );
216 }
217
218 exit( 1 );
219 }
220
221 /* Output a single main.
222 */
223 static void
main_print_main(Symbol * sym)224 main_print_main( Symbol *sym )
225 {
226 PElement *root;
227
228 root = &sym->expr->root;
229 if( !symbol_recalculate_check( sym ) ||
230 !reduce_pelement( reduce_context, reduce_spine_strict, root ) )
231 main_error_exit( _( "error calculating \"%s\"" ),
232 symbol_name_scope( sym ) );
233
234 if( main_option_output ) {
235 char filename[FILENAME_MAX];
236
237 im_strncpy( filename, main_option_output, FILENAME_MAX );
238 if( !group_save_item( root, filename ) )
239 main_error_exit( _( "error saving \"%s\"" ),
240 symbol_name_scope( sym ) );
241 }
242
243 if( main_option_print_main )
244 graph_value( root );
245 }
246
247 static void *
main_print_ws(Workspace * ws,gboolean * found)248 main_print_ws( Workspace *ws, gboolean *found )
249 {
250 Symbol *sym;
251
252 if( (sym = compile_lookup( ws->sym->expr->compile, "main" )) ) {
253 main_print_main( sym );
254 *found = TRUE;
255 }
256
257 return( NULL );
258 }
259
260 /* Clean up our application and quit. Not interactive! Do any "has been
261 * modified, OK to quit?" stuff before this, see main_quit_test().
262 */
263 static void
main_quit(void)264 main_quit( void )
265 {
266 #if HAVE_FFTW || HAVE_FFTW3
267 iOpenFile *of;
268 #endif /*HAVE_FFTW || HAVE_FFTW3*/
269
270 #ifdef DEBUG
271 printf( "main_quit: cleaning up ...\n" );
272 #endif/*DEBUG*/
273
274 if( main_option_print_main ||
275 main_option_output ) {
276 Symbol *sym;
277 gboolean found;
278
279 symbol_recalculate_all();
280
281 /* Process all the mains we can find: one at the top level,
282 * one in each workspace.
283 */
284 found = FALSE;
285 if( (sym = compile_lookup(
286 symbol_root->expr->compile, "main" )) ) {
287 main_print_main( sym );
288 found = TRUE;
289 }
290 workspace_map( (workspace_map_fn) main_print_ws, &found, NULL );
291
292 if( !found )
293 main_error_exit( "%s", _( "no \"main\" found" ) );
294 }
295
296 /* Force all our windows down.
297 */
298 iwindow_map_all( (iWindowMapFn) iwindow_kill, NULL );
299
300 /* Saves recent and stuff like that.
301 */
302 mainw_shutdown();
303
304 /* Dump wisdom back again.
305 */
306 #if HAVE_FFTW || HAVE_FFTW3
307 if( (of = ifile_open_write( "%s" G_DIR_SEPARATOR_S "wisdom",
308 get_savedir() )) ) {
309 fftw_export_wisdom_to_file( of->fp );
310 ifile_close( of );
311 }
312 #endif /*HAVE_FFTW*/
313
314 /* Remove any ws retain files.
315 */
316 workspacegroup_autosave_clean();
317
318 /* Junk all symbols. This may remove a bunch of intermediate images
319 * too.
320 */
321 UNREF( main_watchgroup );
322 UNREF( main_symbol_root );
323 UNREF( main_toolkitgroup );
324 UNREF( main_workspaceroot );
325
326 /* Junk reduction machine ... this should remove all image temps.
327 */
328 reduce_destroy( reduce_context );
329
330 #ifdef DEBUG_LEAK
331 /* Free other GTK stuff.
332 */
333 if( main_icon_factory )
334 gtk_icon_factory_remove_default( main_icon_factory );
335 junk_tooltips();
336
337 #ifdef HAVE_LIBGOFFICE
338 /* Not quite sure what this does, but don't do it in batch mode.
339 */
340 if( !main_option_batch )
341 libgoffice_shutdown ();
342 #endif /*HAVE_LIBGOFFICE*/
343
344 path_rewrite_free_all();
345
346 /* Should have freed everything now.
347 */
348
349 /* Make sure!
350
351 FIXME ... #ifdef this lot out at some point
352
353 */
354 UNREF( main_imageinfogroup );
355 heap_check_all_destroyed();
356 vips_shutdown();
357 managed_check_all_destroyed();
358 util_check_all_destroyed();
359 call_check_all_destroyed();
360 #endif /*DEBUG_LEAK*/
361
362 #ifdef DEBUG
363 printf( "main_quit: exit( 0 )\n" );
364 #endif/*DEBUG*/
365
366 /* And exit.
367 */
368 exit( 0 );
369 }
370
371 /* We mustn't quit recursively!
372 */
373 static gboolean main_quit_running = FALSE;
374
375 static void
main_quit_test_cb(void * sys,iWindowResult result)376 main_quit_test_cb( void *sys, iWindowResult result )
377 {
378 #ifdef DEBUG
379 printf( "main_quit_test_cb:\n" );
380 #endif/*DEBUG*/
381
382 if( result == IWINDOW_YES )
383 /* No return from this.
384 */
385 main_quit();
386 else
387 /* Quit has been cancelled.
388 */
389 main_quit_running = FALSE;
390 }
391
392 /* Check before quitting.
393 */
394 void
main_quit_test(void)395 main_quit_test( void )
396 {
397 if( main_quit_running ) {
398 #ifdef DEBUG
399 printf( "main_quit_test: recursive quit blocked\n" );
400 #endif/*DEBUG*/
401 return;
402 }
403 main_quit_running = TRUE;
404
405 #ifdef DEBUG
406 printf( "main_quit_test:\n" );
407 #endif/*DEBUG*/
408
409 /* Flush any pending preference saves before we look for dirty
410 * objects.
411 */
412 watchgroup_flush( main_watchgroup );
413
414 /* Close registered models.
415 */
416 filemodel_inter_close_registered_cb( iwindow_pick_one(), NULL,
417 main_quit_test_cb, NULL );
418 }
419
420 static void
main_watchgroup_changed_cb(void)421 main_watchgroup_changed_cb( void )
422 {
423 /* Only set this in GUI mode. Otherwise, let the user control CPUs
424 * with the env variable and --vips-concurrency args.
425 */
426 if( !main_option_batch )
427 im_concurrency_set( VIPS_CPUS );
428 }
429
430 /* Try to load a thing, anything at all. Actually, we don't load plugins
431 * experimentally, win32 pops up an annoying error dialog if you try that.
432 */
433 static gboolean
main_load(Workspace * ws,const char * filename)434 main_load( Workspace *ws, const char *filename )
435 {
436 Workspacegroup *new_wsg;
437
438 if( (new_wsg = workspacegroup_new_from_file( main_workspaceroot,
439 filename, filename )) ) {
440 Mainw *mainw;
441
442 if( !main_option_batch ) {
443 mainw = mainw_new( new_wsg );
444 gtk_widget_show( GTK_WIDGET( mainw ) );
445 }
446
447 mainw_recent_add( &mainw_recent_workspace, filename );
448
449 return( TRUE );
450 }
451
452 error_clear();
453
454 /* workspace_load_file() needs to recalc to work, try to avoid that by
455 * doing .defs first.
456 */
457 if( is_file_type( &filesel_dfile_type, filename ) ) {
458 if( toolkit_new_from_file( main_toolkitgroup, filename ) )
459 return( TRUE );
460 }
461
462 /* Try as matrix or image. Have to do these via definitions.
463 */
464 if( workspace_load_file( ws, filename ) )
465 return( TRUE );
466
467 error_clear();
468
469 error_top( _( "Unknown file type." ) );
470 error_sub( _( "Unable to load \"%s\"." ), filename );
471
472 return( FALSE );
473 }
474
475 #ifndef DEBUG_NOAUTO
476 static void *
main_load_plug(char * name)477 main_load_plug( char *name )
478 {
479 if( !calli_string_filename( (calli_string_fn) im_load_plugin,
480 name, NULL, NULL, NULL ) ) {
481 error_top( _( "Unable to load." ) );
482 error_sub( _( "Error loading plug-in \"%s\"." ), name );
483 error_vips();
484 iwindow_alert( NULL, GTK_MESSAGE_ERROR );
485 }
486
487 return( NULL );
488 }
489 #endif /*!DEBUG_NOAUTO*/
490
491 static void *
main_load_def(const char * filename)492 main_load_def( const char *filename )
493 {
494 Toolkit *kit;
495
496 if( !main_option_no_load_menus || im_skip_dir( filename )[0] == '_' ) {
497 progress_update_loading( 0, im_skip_dir( filename ) );
498
499 if( !(kit = toolkit_new_from_file( main_toolkitgroup,
500 filename )) )
501 iwindow_alert( NULL, GTK_MESSAGE_ERROR );
502 else
503 filemodel_set_auto_load( FILEMODEL( kit ) );
504 }
505
506 return( NULL );
507 }
508
509 static void *
main_load_wsg(const char * filename)510 main_load_wsg( const char *filename )
511 {
512 Workspacegroup *wsg;
513
514 #ifdef DEBUG
515 printf( "main_load_wsg: %s\n", filename );
516 #endif/*DEBUG*/
517
518 progress_update_loading( 0, im_skip_dir( filename ) );
519
520 if( !(wsg = workspacegroup_new_from_file( main_workspaceroot,
521 filename, filename )) )
522 iwindow_alert( NULL, GTK_MESSAGE_ERROR );
523 else {
524 filemodel_set_auto_load( FILEMODEL( wsg ) );
525 }
526
527 return( NULL );
528 }
529
530 #ifndef DEBUG_NOAUTO
531 /* Link all the packages in a function.
532 */
533 static void *
main_link_package(im_package * pack)534 main_link_package( im_package *pack)
535 {
536 char name[MAX_STRSIZE];
537 Toolkit *kit;
538 int i;
539
540 im_snprintf( name, MAX_STRSIZE, "_%s", pack->name );
541 kit = toolkit_new( main_toolkitgroup, name );
542
543 for( i = 0; i < pack->nfuncs; i++ )
544 if( call_is_callable( pack->table[i] ) ) {
545 Symbol *sym;
546
547 sym = symbol_new( symbol_root->expr->compile,
548 pack->table[i]->name );
549 g_assert( sym->type == SYM_ZOMBIE );
550 sym->type = SYM_EXTERNAL;
551 sym->function = pack->table[i];
552 sym->fn_nargs = call_n_args( pack->table[i] );
553 (void) tool_new_sym( kit, -1, sym );
554 symbol_made( sym );
555 }
556
557 filemodel_set_auto_load( FILEMODEL( kit ) );
558 filemodel_set_modified( FILEMODEL( kit ), FALSE );
559 kit->pseudo = TRUE;
560
561 return( NULL );
562 }
563 #endif /*!DEBUG_NOAUTO*/
564
565 /* Load all plugins and defs.
566 */
567 static void
main_load_startup(void)568 main_load_startup( void )
569 {
570 mainw_recent_freeze();
571
572 /* Stop load of builtins, plugs and vips ... handy for debugging if you're
573 * tracing symbol.c
574 */
575 #ifdef DEBUG_NOAUTO
576 printf( "*** DEBUG_NOAUTO set, not loading builtin, plugs and vips\n" );
577 #else /*!DEBUG_NOAUTO*/
578
579 #ifdef DEBUG
580 printf( "built-ins init\n" );
581 #endif/*DEBUG*/
582
583 /* Add builtin toolkit.
584 */
585 builtin_init();
586
587 #ifdef DEBUG
588 printf( "plug-ins init\n" );
589 #endif/*DEBUG*/
590
591 /* Load any plug-ins on PATH_START.
592 */
593 (void) path_map( PATH_START, "*.plg",
594 (path_map_fn) main_load_plug, NULL );
595
596 /* Link all VIPS functions as SYM_EXTERNAL.
597 */
598 (void) im_map_packages( (VSListMap2Fn) main_link_package, NULL );
599 #endif /*!DEBUG_NOAUTO*/
600
601 /* Load up all defs and wses.
602 */
603 #ifdef DEBUG
604 printf( "definitions init\n" );
605 #endif/*DEBUG*/
606 (void) path_map( PATH_START, "*.def",
607 (path_map_fn) main_load_def, NULL );
608
609 #ifdef DEBUG
610 printf( "ws init\n" );
611 #endif/*DEBUG*/
612 (void) path_map( PATH_START, "*.ws",
613 (path_map_fn) main_load_wsg, NULL );
614
615 mainw_recent_thaw();
616 }
617
618 static void *
main_junk_auto_load(Filemodel * filemodel)619 main_junk_auto_load( Filemodel *filemodel )
620 {
621 g_assert( IS_FILEMODEL( filemodel ) );
622
623 if( filemodel->auto_load )
624 IDESTROY( filemodel );
625
626 return( NULL );
627 }
628
629 /* Remove and reload all menus/plugins/workspaces.
630 */
631 void
main_reload(void)632 main_reload( void )
633 {
634 progress_begin();
635
636 /* Remove.
637 */
638 toolkitgroup_map( main_toolkitgroup,
639 (toolkit_map_fn) main_junk_auto_load, NULL, NULL );
640 workspace_map( (workspace_map_fn) main_junk_auto_load, NULL, NULL );
641 im_close_plugins();
642
643 /* Reload.
644 */
645 main_load_startup();
646
647 /* We may have changed our prefs ... link the watches to the
648 * new prefs workspace.
649 */
650 watch_relink_all();
651
652 progress_end();
653 }
654
655 /* Use a file to paint a named stock item.
656 */
657 static void
main_file_for_stock(GtkIconFactory * icon_factory,const char * stock,const char * file)658 main_file_for_stock( GtkIconFactory *icon_factory,
659 const char *stock, const char *file )
660 {
661 GtkIconSource *icon_source;
662 GtkIconSet *icon_set;
663 char buf[FILENAME_MAX];
664
665 im_snprintf( buf, FILENAME_MAX,
666 "$VIPSHOME/share/$PACKAGE/data/%s", file );
667 path_expand( buf );
668 icon_source = gtk_icon_source_new();
669 gtk_icon_source_set_filename( icon_source, buf );
670 icon_set = gtk_icon_set_new();
671 gtk_icon_set_add_source( icon_set, icon_source );
672 gtk_icon_source_free( icon_source );
673 gtk_icon_factory_add( icon_factory, stock, icon_set );
674 gtk_icon_set_unref( icon_set );
675 }
676
677 /* Make our custom icon sets.
678 */
679 static void
main_register_icons(void)680 main_register_icons( void )
681 {
682 static const GtkStockItem stock_item[] = {
683 /* Can be (eg.)
684 *
685 * { GTK_STOCK_COPY, N_("_Copy"), GDK_CONTROL_MASK, 'c', GETTEXT_PACKAGE },
686 *
687 */
688 { STOCK_NEXT_ERROR,
689 N_( "Next _Error" ), 0, 0, GETTEXT_PACKAGE },
690 { STOCK_DROPPER, N_( "Ink dropper" ), 0, 0, GETTEXT_PACKAGE },
691 { STOCK_DUPLICATE, N_( "D_uplicate" ), 0, 0, GETTEXT_PACKAGE },
692 { STOCK_PAINTBRUSH, N_( "Pen" ), 0, 0, GETTEXT_PACKAGE },
693 { STOCK_LINE, N_( "Line" ), 0, 0, GETTEXT_PACKAGE },
694 { STOCK_TEXT, N_( "Text" ), 0, 0, GETTEXT_PACKAGE },
695 { STOCK_SMUDGE, N_( "Smudge" ), 0, 0, GETTEXT_PACKAGE },
696 { STOCK_FLOOD, N_( "Flood" ), 0, 0, GETTEXT_PACKAGE },
697 { STOCK_FLOOD_BLOB, N_( "Flood Blob" ), 0, 0, GETTEXT_PACKAGE },
698 { STOCK_RECT, N_( "Fill Rectangle" ), 0, 0, GETTEXT_PACKAGE },
699 { STOCK_MOVE, N_( "Pan" ), 0, 0, GETTEXT_PACKAGE },
700 { STOCK_SELECT, N_( "Select" ), 0, 0, GETTEXT_PACKAGE },
701 { STOCK_LOCK, N_( "Locked" ), 0, 0, GETTEXT_PACKAGE },
702
703 /* And the LEDs we use.
704 */
705 { STOCK_LED_RED, N_( "Red LED" ), 0, 0, GETTEXT_PACKAGE },
706 { STOCK_LED_GREEN, N_( "Green LED" ), 0, 0, GETTEXT_PACKAGE },
707 { STOCK_LED_BLUE, N_( "Blue LED" ), 0, 0, GETTEXT_PACKAGE },
708 { STOCK_LED_YELLOW, N_( "Yellow LED" ), 0, 0, GETTEXT_PACKAGE },
709 { STOCK_LED_CYAN, N_( "Cyan LED" ), 0, 0, GETTEXT_PACKAGE },
710 { STOCK_LED_OFF, N_( "Off LED" ), 0, 0, GETTEXT_PACKAGE }
711 };
712
713 GtkIconSet *icon_set;
714
715 gtk_stock_add_static( stock_item, IM_NUMBER( stock_item ) );
716 main_icon_factory = gtk_icon_factory_new();
717
718 /* Make a colour picker stock ... take the stock icon and add our own
719 * text (gtk defines no text for the standard version of this stock
720 * icon).
721 */
722 icon_set = gtk_icon_factory_lookup_default( GTK_STOCK_COLOR_PICKER );
723 gtk_icon_factory_add( main_icon_factory, STOCK_DROPPER, icon_set );
724
725 /* For Next Error, use JUMP_TO.
726 */
727 icon_set = gtk_icon_factory_lookup_default( GTK_STOCK_JUMP_TO );
728 gtk_icon_factory_add( main_icon_factory, STOCK_NEXT_ERROR, icon_set );
729
730 /* For clone, use the DND_MULTIPLE icon (close enough).
731 */
732 icon_set = gtk_icon_factory_lookup_default( GTK_STOCK_DND_MULTIPLE );
733 gtk_icon_factory_add( main_icon_factory, STOCK_DUPLICATE, icon_set );
734
735 /* Link to our stock .pngs.
736 */
737 main_file_for_stock( main_icon_factory,
738 STOCK_PAINTBRUSH, "stock-tool-ink-22.png" );
739 main_file_for_stock( main_icon_factory,
740 STOCK_LINE, "stock-tool-path-22.png" );
741 main_file_for_stock( main_icon_factory,
742 STOCK_TEXT, "stock-tool-text-22.png" );
743 main_file_for_stock( main_icon_factory,
744 STOCK_SMUDGE, "stock-tool-smudge-22.png" );
745 main_file_for_stock( main_icon_factory,
746 STOCK_FLOOD, "stock-tool-bucket-fill-22.png" );
747 main_file_for_stock( main_icon_factory,
748 STOCK_FLOOD_BLOB, "stock-tool-bucket-fill-22.png" );
749 main_file_for_stock( main_icon_factory,
750 STOCK_RECT, "stock-tool-rect-select-22.png" );
751 main_file_for_stock( main_icon_factory,
752 STOCK_MOVE, "stock-tool-move-22.png" );
753 main_file_for_stock( main_icon_factory,
754 STOCK_SELECT, "stock-tool-select-22.png" );
755 main_file_for_stock( main_icon_factory,
756 STOCK_LOCK, "stock-padlock-closed-22.png" );
757 main_file_for_stock( main_icon_factory,
758 STOCK_ALERT, "stock-alert-22.png" );
759 main_file_for_stock( main_icon_factory,
760 STOCK_LED_RED, "stock-led-red-18.png" );
761 main_file_for_stock( main_icon_factory,
762 STOCK_LED_GREEN, "stock-led-green-18.png" );
763 main_file_for_stock( main_icon_factory,
764 STOCK_LED_BLUE, "stock-led-blue-18.png" );
765 main_file_for_stock( main_icon_factory,
766 STOCK_LED_YELLOW, "stock-led-yellow-18.png" );
767 main_file_for_stock( main_icon_factory,
768 STOCK_LED_CYAN, "stock-led-cyan-18.png" );
769 main_file_for_stock( main_icon_factory,
770 STOCK_LED_OFF, "stock-led-off-18.png" );
771
772 gtk_icon_factory_add_default( main_icon_factory );
773 g_object_unref( main_icon_factory );
774 }
775
776 /* Init the display connection stuff.
777 */
778 static void
main_x_init(int * argc,char *** argv)779 main_x_init( int *argc, char ***argv )
780 {
781 char buf[FILENAME_MAX];
782
783 #ifdef DEBUG
784 printf( "X11 init\n" );
785 #endif/*DEBUG*/
786
787 (void) calli_string_filename(
788 (calli_string_fn) gtk_rc_add_default_file,
789 "$VIPSHOME" G_DIR_SEPARATOR_S "share" G_DIR_SEPARATOR_S
790 PACKAGE G_DIR_SEPARATOR_S "rc" G_DIR_SEPARATOR_S
791 "ipgtkrc", NULL, NULL, NULL );
792 gtk_init( argc, argv );
793
794 /* Set the default icon.
795 */
796 im_strncpy( buf,
797 "$VIPSHOME/share/$PACKAGE/data/vips-128.png", FILENAME_MAX );
798 path_expand( buf );
799 gtk_window_set_default_icon_from_file( buf, NULL );
800
801 /* Turn off startup notification. Startup is done when we pop our
802 * first window, not when we make this secret window.
803 */
804 gtk_window_set_auto_startup_notification( FALSE );
805
806 #ifdef DEBUG_UPDATES
807 printf( "*** debug updates is on\n" );
808 gdk_window_set_debug_updates( TRUE );
809 #endif /*DEBUG_UPDATES*/
810
811 main_register_icons();
812
813 /* Next window we make is end of startup.
814 */
815 gtk_window_set_auto_startup_notification( TRUE );
816
817 /* Load up any saved accelerators.
818 */
819 calli_string_filenamef( (calli_string_fn) gtk_accel_map_load,
820 "%s" G_DIR_SEPARATOR_S "accel_map", get_savedir() );
821 }
822
823 static void *
main_toobig_done_sub(const char * filename)824 main_toobig_done_sub( const char *filename )
825 {
826 unlinkf( "%s", filename );
827
828 return( NULL );
829 }
830
831 /* OK in "flush temps" yesno.
832 */
833 static void
main_toobig_done(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)834 main_toobig_done( iWindow *iwnd,
835 void *client, iWindowNotifyFn nfn, void *sys )
836 {
837 /* Don't "rm *", too dangerous.
838 */
839 path_map_dir( PATH_TMP, "*.v",
840 (path_map_fn) main_toobig_done_sub, NULL );
841 path_map_dir( PATH_TMP, "*.ws",
842 (path_map_fn) main_toobig_done_sub, NULL );
843
844 /* _stdenv.def:magick can generate .tif files.
845 */
846 path_map_dir( PATH_TMP, "*.tif",
847 (path_map_fn) main_toobig_done_sub, NULL );
848
849 /* autotrace can make some others.
850 */
851 path_map_dir( PATH_TMP, "*.ppm",
852 (path_map_fn) main_toobig_done_sub, NULL );
853 path_map_dir( PATH_TMP, "*.svg",
854 (path_map_fn) main_toobig_done_sub, NULL );
855
856 /* Tell space-free indicators to update.
857 */
858 if( main_imageinfogroup )
859 iobject_changed( IOBJECT( main_imageinfogroup ) );
860
861 nfn( sys, IWINDOW_YES );
862 }
863
864 /* Test for a bunch of stuff in the TMP area. Need to do this before
865 * we load args in case there are large JPEGs there. Only bother in
866 * interactive mode: we won't be able to question the user without an
867 * X connection.
868 */
869 static void
main_check_temp(double total)870 main_check_temp( double total )
871 {
872 if( total > 10 * 1024 * 1024 ) {
873 char txt[256];
874 VipsBuf buf = VIPS_BUF_STATIC( txt );
875 char tmp[FILENAME_MAX];
876
877 im_strncpy( tmp, PATH_TMP, FILENAME_MAX );
878 path_expand( tmp );
879 vips_buf_append_size( &buf, total );
880
881 box_yesno( NULL,
882 main_toobig_done, iwindow_true_cb, NULL,
883 NULL, NULL,
884 _( "Empty temp area" ),
885 _( "Many files in temp area." ),
886 _( "The temp area \"%s\" contains %s of files. "
887 "Would you like to empty the temp area? "
888 "This will delete any workspace backups and "
889 "cannot be undone." ),
890 tmp, vips_buf_all( &buf ) );
891 }
892 }
893
894 /* Make sure a savedir exists. Used to build the "~/.nip2-xx/tmp" etc.
895 * directory tree.
896 */
897 static void
main_mkdir(const char * dir)898 main_mkdir( const char *dir )
899 {
900 if( !existsf( "%s" G_DIR_SEPARATOR_S "%s", get_savedir(), dir ) )
901 if( !mkdirf( "%s" G_DIR_SEPARATOR_S "%s", get_savedir(), dir ) )
902 error_exit( _( "unable to make %s %s: %s" ),
903 get_savedir(), dir, g_strerror( errno ) );
904 }
905
906 static gboolean
main_set(const char * str)907 main_set( const char *str )
908 {
909 Symbol *sym;
910
911 attach_input_string( str );
912 if( !(sym = parse_set_symbol()) )
913 return( FALSE );
914
915 /* Put the input just after the '=', ready to parse a RHS into the
916 * symbol.
917 */
918 attach_input_string( str +
919 IM_CLIP( 0, input_state.charpos - 1, strlen( str ) ) );
920
921 if( !symbol_user_init( sym ) ||
922 !parse_rhs( sym->expr, PARSE_RHS ) ) {
923 /* Another parse error.
924 */
925 expr_error_get( sym->expr );
926
927 /* Block changes to error_string ... symbol_destroy()
928 * can set this for compound objects.
929 */
930 error_block();
931 IDESTROY( sym );
932 error_unblock();
933
934 return( FALSE );
935 }
936
937 symbol_made( sym );
938
939 /* Is there a row? Make sure any modified text there can't zap our new
940 * text.
941 */
942 if( sym->expr->row ) {
943 Row *row = sym->expr->row;
944
945 heapmodel_set_modified(
946 HEAPMODEL( row->child_rhs->itext ), FALSE );
947 }
948
949 return( TRUE );
950 }
951
952 static char prefix_buffer[FILENAME_MAX];
953 static gboolean prefix_valid = FALSE;
954
955 /* Override the install guess from vips. Handy for testing.
956 */
957 static void
set_prefix(const char * prefix)958 set_prefix( const char *prefix )
959 {
960 im_strncpy( prefix_buffer, prefix, FILENAME_MAX );
961 nativeize_path( prefix_buffer );
962 absoluteize_path( prefix_buffer );
963 setenvf( "VIPSHOME", "%s", prefix_buffer );
964 prefix_valid = TRUE;
965 }
966
967 /* Guess VIPSHOME, if we can.
968 */
969 const char *
get_prefix(void)970 get_prefix( void )
971 {
972 if( !prefix_valid ) {
973 const char *prefix;
974
975 if( !(prefix = im_guess_prefix( main_argv0, "VIPSHOME" )) ) {
976 error_top( _( "Unable to find install area." ) );
977 error_vips();
978
979 return( NULL );
980 }
981
982 set_prefix( prefix );
983 }
984
985 return( prefix_buffer );
986 }
987
988 /* Start here!
989 */
990 int
main(int argc,char * argv[])991 main( int argc, char *argv[] )
992 {
993 gboolean welcome_message = FALSE;
994 Workspacegroup *wsg;
995 Workspace *ws;
996 GError *error = NULL;
997 GOptionContext *context;
998 const char *prefix;
999 int i;
1000 double total = 0.0;
1001 #ifdef HAVE_GETRLIMIT
1002 struct rlimit rlp;
1003 #endif /*HAVE_GETRLIMIT*/
1004 char name[256];
1005 #if HAVE_FFTW || HAVE_FFTW3
1006 iOpenFile *of;
1007 #endif /*HAVE_FFTW*/
1008 Toolkit *kit;
1009 char txt[MAX_STRSIZE];
1010 VipsBuf buf = VIPS_BUF_STATIC( txt );
1011
1012 #ifdef DEBUG_TIME
1013 GTimer *startup_timer = g_timer_new();
1014 printf( "DEBUG_TIME: startup timer zeroed ...\n" );
1015 #endif /*DEBUG_TIME*/
1016
1017 /* In startup phase.
1018 */
1019 main_starting = TRUE;
1020
1021 /* Want numeric locale to be "C", so we have C rules for doing
1022 * double <-> string (ie. no "," for decimal point).
1023 */
1024 setlocale( LC_ALL, "" );
1025 setlocale( LC_NUMERIC, "C" );
1026
1027 /* Make sure our LC_NUMERIC setting is not trashed.
1028 */
1029 gtk_disable_setlocale();
1030
1031 #ifdef DEBUG
1032 printf( "main: sizeof( HeapNode ) == %zd\n", sizeof( HeapNode ) );
1033
1034 /* Should be 3 pointers, hopefully.
1035 */
1036 if( sizeof( HeapNode ) != 3 * sizeof( void * ) )
1037 printf( "*** struct packing problem!\n" );
1038 #endif/*DEBUG*/
1039
1040 /* Yuk .. shouldn't really write to argv0. This can't change the
1041 * string length.
1042 *
1043 * On win32 we will sometimes get paths with mixed '/' and '\' which
1044 * confuses vips's prefix guessing. Make sure we have one or the other.
1045 */
1046 nativeize_path( argv[0] );
1047
1048 main_argv0 = argv[0];
1049 main_c_stack_base = &argc;
1050
1051 /* Pass config.h stuff down to .ws files.
1052 */
1053 setenvf( "PACKAGE", "%s", PACKAGE );
1054 setenvf( "VERSION", "%s", VERSION );
1055
1056 #ifdef OS_WIN32
1057 {
1058 /* No HOME on windows ... make one from HOMEDRIVE and HOMEDIR (via
1059 * glib).
1060 */
1061 const char *home;
1062 char buf[FILENAME_MAX];
1063
1064 if( !(home = g_getenv( "HOME" )) )
1065 home = g_get_home_dir();
1066
1067 /* We need native paths.
1068 */
1069 strncpy( buf, home, FILENAME_MAX );
1070 nativeize_path( buf );
1071 setenvf( "HOME", "%s", buf );
1072 }
1073 #endif /*OS_WIN32*/
1074
1075 /* Name of the dir we store our config stuff in. This can get used by
1076 * Preferences.ws.
1077 */
1078 setenvf( "SAVEDIR", "%s", get_savedir() );
1079
1080 /* Path separator on this platform.
1081 */
1082 setenvf( "SEP", "%s", G_DIR_SEPARATOR_S );
1083
1084 /* Executable file extension (eg. ".exe" on Windows).
1085 */
1086 setenvf( "EXEEXT", "%s", VIPS_EXEEXT );
1087
1088 /* Start up vips.
1089 */
1090 if( im_init_world( main_argv0 ) )
1091 error_exit( "unable to start VIPS" );
1092
1093 /* The vips8 cache is no use to us. We have our own cache which is
1094 * integrated with our invalidate system.
1095 */
1096 vips_cache_set_max( 0 );
1097
1098 /* Init i18n ... get catalogues from $VIPSHOME/share/locale so we're
1099 * relocatable.
1100 */
1101 prefix = get_prefix();
1102 im_snprintf( name, 256,
1103 "%s" G_DIR_SEPARATOR_S "share" G_DIR_SEPARATOR_S "locale",
1104 prefix );
1105 #ifdef DEBUG
1106 printf( "bindtextdomain: %s\n", name );
1107 #endif /*DEBUG*/
1108 textdomain( GETTEXT_PACKAGE );
1109 bindtextdomain( GETTEXT_PACKAGE, name );
1110 bind_textdomain_codeset( GETTEXT_PACKAGE, "UTF-8" );
1111
1112 /* Set localised application name.
1113 */
1114 g_set_application_name( _( PACKAGE ) );
1115
1116 context = g_option_context_new( _( "- image processing spreadsheet" ) );
1117 g_option_context_add_main_entries( context,
1118 main_option, GETTEXT_PACKAGE );
1119
1120 /* Don't start X here! We may be in batch mode.
1121 */
1122 g_option_context_add_group( context, gtk_get_option_group( FALSE ) );
1123 g_option_context_add_group( context, im_get_option_group() );
1124
1125 if( !g_option_context_parse( context, &argc, &argv, &error ) )
1126 vfatal( &error );
1127
1128 g_option_context_free( context );
1129
1130 /* Override the install guess from vips. This won't pick up msg
1131 * cats sadly :( since we have to init i18n before arg parsing. Handy
1132 * for testing without installing.
1133 */
1134 if( main_option_prefix )
1135 set_prefix( main_option_prefix );
1136
1137 if( main_option_version ) {
1138 printf( "%s-%s", PACKAGE, VERSION );
1139 printf( "\n" );
1140
1141 printf( _( "linked to vips-%s" ), im_version_string() );
1142 printf( "\n" );
1143
1144 exit( 0 );
1145 }
1146
1147 #ifdef DEBUG_FATAL
1148 /* Set masks for debugging ... stop on any problem.
1149 */
1150 g_log_set_always_fatal(
1151 G_LOG_FLAG_RECURSION |
1152 G_LOG_FLAG_FATAL |
1153 G_LOG_LEVEL_ERROR |
1154 G_LOG_LEVEL_CRITICAL |
1155 G_LOG_LEVEL_WARNING );
1156 #else /*!DEBUG_FATAL*/
1157 #ifdef OS_WIN32
1158 /* No logging output ... on win32, log output pops up a very annoying
1159 * console text box.
1160 */
1161 g_log_set_handler( "GLib",
1162 G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,
1163 main_log_null, NULL );
1164 g_log_set_handler( "Gtk",
1165 G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,
1166 main_log_null, NULL );
1167 g_log_set_handler( NULL,
1168 G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,
1169 main_log_null, NULL );
1170 #endif /*OS_WIN32*/
1171 #endif /*DEBUG_FATAL*/
1172
1173 main_stdin = ifile_open_read_stdin();
1174
1175 #ifdef HAVE_GETRLIMIT
1176 /* Make sure we have lots of file descriptors. Some platforms have cur
1177 * as 256 and max at 1024 to keep stdio happy.
1178 */
1179 if( getrlimit( RLIMIT_NOFILE, &rlp ) == 0 ) {
1180 rlim_t old_limit = rlp.rlim_cur;
1181
1182 rlp.rlim_cur = rlp.rlim_max;
1183 if( setrlimit( RLIMIT_NOFILE, &rlp ) == 0 ) {
1184 #ifdef DEBUG
1185 printf( "set max file descriptors to %d\n",
1186 (int) rlp.rlim_max );
1187 #endif /*DEBUG*/
1188 }
1189 else if( (int) rlp.rlim_max != -1 ) {
1190 /* -1 means can't-be-set, at least on os x, so don't
1191 * warn.
1192 */
1193 g_warning( _( "unable to change max file descriptors\n"
1194 "max file descriptors still set to %d" ),
1195 (int) old_limit );
1196 }
1197 }
1198 else {
1199 g_warning( _( "unable to read max file descriptors" ) );
1200 }
1201 #endif /*HAVE_GETRLIMIT*/
1202
1203 /* Make our file types.
1204 */
1205 filesel_startup();
1206
1207 /* Set default values for paths.
1208 */
1209 path_init();
1210
1211 /* First time we've been run? Welcome message.
1212 */
1213 if( !existsf( "%s", get_savedir() ) )
1214 welcome_message = TRUE;
1215
1216 /* Always make these in case some got deleted.
1217 */
1218 main_mkdir( "" );
1219 main_mkdir( "tmp" );
1220 main_mkdir( "start" );
1221 main_mkdir( "data" );
1222
1223 /* Init other stuff.
1224 */
1225 #ifdef HAVE_FFTW3
1226 fftw_import_system_wisdom();
1227 #endif /*HAVE_FFTW3*/
1228 #if HAVE_FFTW || HAVE_FFTW3
1229 if( (of = ifile_open_read( "%s" G_DIR_SEPARATOR_S "wisdom",
1230 get_savedir() )) ) {
1231 fftw_import_wisdom_from_file( of->fp );
1232 ifile_close( of );
1233 }
1234 #endif /*HAVE_FFTW*/
1235
1236 mainw_startup();
1237 reduce_context = reduce_new();
1238 main_symbol_root = symbol_root_init();
1239 g_object_ref( G_OBJECT( main_symbol_root ) );
1240 iobject_sink( IOBJECT( main_symbol_root ) );
1241 model_base_init();
1242 main_workspaceroot = workspaceroot_new( "Workspaces" );
1243 g_object_ref( G_OBJECT( main_workspaceroot ) );
1244 iobject_sink( IOBJECT( main_workspaceroot ) );
1245 main_watchgroup = watchgroup_new( main_workspaceroot, "Preferences" );
1246 g_object_ref( G_OBJECT( main_watchgroup ) );
1247 iobject_sink( IOBJECT( main_watchgroup ) );
1248 main_toolkitgroup = toolkitgroup_new( symbol_root );
1249 g_object_ref( G_OBJECT( main_toolkitgroup ) );
1250 iobject_sink( IOBJECT( main_toolkitgroup ) );
1251 main_imageinfogroup = imageinfogroup_new();
1252 g_object_ref( G_OBJECT( main_imageinfogroup ) );
1253 iobject_sink( IOBJECT( main_imageinfogroup ) );
1254
1255 /* First pass at command-line options. Just look at the flags that
1256 * imply other flags, don't do any processing yet.
1257 */
1258 if( main_option_script ) {
1259 main_option_batch = TRUE;
1260 main_option_no_load_menus = TRUE;
1261 main_option_no_load_args = TRUE;
1262 main_option_print_main = TRUE;
1263 }
1264
1265 if( main_option_test ) {
1266 main_option_batch = TRUE;
1267 main_option_verbose = TRUE;
1268 }
1269
1270 if( main_option_expression ) {
1271 main_option_batch = TRUE;
1272 main_option_no_load_menus = TRUE;
1273 main_option_no_load_args = TRUE;
1274 main_option_print_main = TRUE;
1275 }
1276
1277 if( main_option_benchmark ) {
1278 main_option_batch = TRUE;
1279 main_option_no_load_menus = FALSE;
1280 }
1281
1282 if( main_option_i18n ) {
1283 /* Just start up and shutdown, no X. Output constant
1284 * i18n strings.
1285 */
1286 main_option_batch = TRUE;
1287 main_option_no_load_menus = FALSE;
1288 }
1289
1290 #ifdef DEBUG
1291 if( main_option_batch )
1292 printf( "non-interactive mode\n" );
1293 #endif /*DEBUG*/
1294
1295 /* Start the X connection. We need this before _load_all(), so that
1296 * we can pop up error dialogs.
1297 */
1298 if( !main_option_batch )
1299 main_x_init( &argc, &argv );
1300
1301 #ifdef HAVE_LIBGOFFICE
1302 libgoffice_init();
1303 go_plugins_init( NULL, NULL, NULL, NULL, TRUE,
1304 GO_TYPE_PLUGIN_LOADER_MODULE );
1305 #endif /*HAVE_LIBGOFFICE*/
1306
1307 /* Load start-up stuff. Builtins, plugins, externals etc. We need to
1308 * do this before we load any user code so we can prevent redefinition
1309 * of builtins.
1310 */
1311 main_load_startup();
1312
1313 /* Recalc to build all classes and gets prefs working.
1314 *
1315 * We have to do this in batch
1316 * mode since we can find dirties through dynamic lookups. Even though
1317 * you might think we could just follow recomps.
1318 */
1319 symbol_recalculate_all_force( TRUE );
1320
1321 #ifdef DEBUG
1322 printf( "arg processing\n" );
1323 #endif/*DEBUG*/
1324
1325 /* Might make this from stdin/whatever if we have a special
1326 * command-line flag.
1327 */
1328 wsg = NULL;
1329 ws = NULL;
1330
1331 /* Second command-line pass. This time we do any actions.
1332 */
1333 if( main_option_script ) {
1334 if( !toolkit_new_from_file( main_toolkitgroup,
1335 main_option_script ) )
1336 main_log_add( "%s\n", error_get_sub() );
1337 }
1338
1339 if( main_option_expression ) {
1340 kit = toolkit_new( main_toolkitgroup, "_expression" );
1341
1342 vips_buf_appendf( &buf, "main = %s;", main_option_expression );
1343 attach_input_string( vips_buf_all( &buf ) );
1344 (void) parse_onedef( kit, -1 );
1345
1346 filemodel_set_modified( FILEMODEL( kit ), FALSE );
1347 }
1348
1349 if( main_option_stdin_def ) {
1350 if( !(kit = toolkit_new_from_openfile(
1351 main_toolkitgroup, main_stdin )) )
1352 main_log_add( "%s\n", error_get_sub() );
1353 }
1354
1355 if( main_option_stdin_ws ) {
1356 if( !(wsg = workspacegroup_new_from_openfile(
1357 main_workspaceroot, main_stdin )) )
1358 main_log_add( "%s\n", error_get_sub() );
1359 else
1360 /* Don't want to have "stdin" as the filename.
1361 */
1362 filemodel_set_filename( FILEMODEL( wsg ), NULL );
1363 }
1364
1365 /* Make a start workspace and workspacegroup to load
1366 * stuff into.
1367 */
1368 if( !wsg ) {
1369 wsg = workspacegroup_new_blank( main_workspaceroot, NULL );
1370 ws = WORKSPACE( icontainer_get_nth_child(
1371 ICONTAINER( wsg ), 0 ) );
1372 }
1373
1374 /* Reset IM_CONCURRENCY if a watch changes. Need to do this after
1375 * parsing options so we skip in batch mode.
1376 */
1377 g_signal_connect( main_watchgroup, "watch_changed",
1378 G_CALLBACK( main_watchgroup_changed_cb ), NULL );
1379
1380 /* Pass PATH_TMP down to vips via TMPDIR. See im_system(), for
1381 * example. We need to do this after the first recomp so that prefs
1382 * are loaded.
1383 */
1384 {
1385 char buf[FILENAME_MAX];
1386
1387 im_strncpy( buf, PATH_TMP, FILENAME_MAX );
1388 path_expand( buf );
1389 setenvf( "TMPDIR", "%s", buf );
1390
1391 path_rewrite_add( PATH_TMP, "$TMPDIR", TRUE );
1392 }
1393
1394 /* Measure amount of stuff in temp area ... need this for checking
1395 * temps later. We pop a dialog if there are too many, so only useful
1396 * in interactive mode.
1397 */
1398 if( !main_option_batch )
1399 total = directory_size( PATH_TMP );
1400
1401 /* Make nip's argc/argv[].
1402 */
1403 kit = toolkit_new( main_toolkitgroup, "_args" );
1404 vips_buf_rewind( &buf );
1405 vips_buf_appendf( &buf, "argc = %d;", argc );
1406 attach_input_string( vips_buf_all( &buf ) );
1407 (void) parse_onedef( kit, -1 );
1408
1409 vips_buf_rewind( &buf );
1410 vips_buf_appendf( &buf, "argv = [" );
1411 for( i = 0; i < argc; i++ ) {
1412 /* Ignore "--" args. Consider eg.
1413 *
1414 * ./try201.nip2 -o x.v -- -12 ~/pics/shark.jpg
1415 *
1416 * if we didn't remove --, all scripts would need to.
1417 */
1418 if( strcmp( argv[i], "--" ) == 0 )
1419 continue;
1420
1421 if( i > 0 )
1422 vips_buf_appendf( &buf, ", " );
1423 vips_buf_appendf( &buf, "\"%s\"", argv[i] );
1424 }
1425 vips_buf_appendf( &buf, "];" );
1426
1427 attach_input_string( vips_buf_all( &buf ) );
1428 if( !parse_onedef( kit, -1 ) )
1429 main_log_add( "%s\n", error_get_sub() );
1430
1431 filemodel_set_modified( FILEMODEL( kit ), FALSE );
1432
1433 /* Double-check: we often forget to move the prefs ws to the latest
1434 * version.
1435 */
1436 #ifdef DEBUG_LEAK
1437 {
1438 Symbol *wsr_sym = main_workspaceroot->sym;
1439 Symbol *ws_sym = SYMBOL( icontainer_child_lookup(
1440 ICONTAINER( wsr_sym->expr->compile ), "Preferences" ) );
1441
1442 if( !ws_sym )
1443 printf( "No prefs workspace!\n" );
1444 else {
1445 Workspace *ws = ws_sym->ws;
1446
1447 if( ws->compat_major ||
1448 ws->compat_minor )
1449 printf( "Preferences loaded in compat mode!\n" );
1450 }
1451 }
1452 #endif /*DEBUG_LEAK*/
1453
1454 if( !main_option_no_load_args ) {
1455 /* Load args as files, if we can.
1456 */
1457 for( i = 1; i < argc; i++ ) {
1458 char buf[FILENAME_MAX];
1459
1460 /* We want to use the absolute, compact form of the
1461 * filename object so we don't get a dependency on CWD.
1462 */
1463 im_strncpy( buf, argv[i], FILENAME_MAX );
1464 path_compact( buf );
1465
1466 if( !main_load( ws, buf ) )
1467 main_log_add( "%s\n", error_get_sub() );
1468 }
1469 }
1470
1471 /* In batch mode give up if there are startup errors.
1472 */
1473 if( main_option_batch ) {
1474 if( !main_log_is_empty() ) {
1475 fprintf( stderr, _( "Startup error log:\n%s" ),
1476 main_log_get() );
1477 exit( 1 );
1478 }
1479 }
1480
1481 if( main_option_set ) {
1482 int i;
1483
1484 for( i = 0; main_option_set[i]; i++ ) {
1485 if( main_option_verbose )
1486 printf( "main_set: %s\n", main_option_set[i] );
1487
1488 if( !main_set( main_option_set[i] ) )
1489 main_log_add( "%s\n%s",
1490 error_get_top(), error_get_sub() );
1491 }
1492 }
1493
1494 /* Make sure our start ws doesn't have modified set. We may have
1495 * loaded some images or whatever into it.
1496 */
1497 workspace_set_modified( ws, FALSE );
1498
1499 /* If the start ws is empty (we didn't load anything into it) and we
1500 * loaded some other workspaces, we can junk the empty ws.
1501 */
1502 if( icontainer_get_n_children( ICONTAINER( main_workspaceroot ) ) > 2 &&
1503 workspace_is_empty( ws ) ) {
1504 IDESTROY( wsg );
1505 wsg = NULL;
1506 ws = NULL;
1507 }
1508
1509 #ifdef DEBUG_TIME
1510 printf( "DEBUG_TIME: main init in %gs\n",
1511 g_timer_elapsed( startup_timer, NULL ) );
1512 #endif /*DEBUG_TIME*/
1513
1514 /* Are we running interactively? Start the main window and loop.
1515 */
1516 if( !main_option_batch ) {
1517 if( wsg ) {
1518 Mainw *mainw;
1519
1520 mainw = mainw_new( wsg );
1521 gtk_widget_show( GTK_WIDGET( mainw ) );
1522 }
1523
1524 /* Process a few events ... we want the window to be mapped so
1525 * that log/welcome/clean? messages we pop appear in the right
1526 * place on the screen.
1527 */
1528 while( g_main_context_iteration( NULL, FALSE ) )
1529 ;
1530
1531 if( !main_log_is_empty() ) {
1532 error_top( _( "Startup error." ) );
1533 error_sub( _( "Startup error log:\n%s" ),
1534 main_log_get() );
1535 iwindow_alert( NULL, GTK_MESSAGE_ERROR );
1536 }
1537
1538 if( welcome_message ) {
1539 char save_dir[FILENAME_MAX];
1540 char buf[256];
1541
1542 im_snprintf( buf, 256,
1543 _( "Welcome to %s-%s!" ), PACKAGE, VERSION );
1544 im_strncpy( save_dir, get_savedir(), FILENAME_MAX );
1545 path_expand( save_dir );
1546 error_top( "%s", buf );
1547 error_sub(
1548 _( "A new directory has been created to hold startup, "
1549 "data and temporary files:\n\n"
1550 " %s\n\n"
1551 "If you've used previous versions of %s, you might want "
1552 "to copy files over from your old work area." ),
1553 save_dir, PACKAGE );
1554 iwindow_alert( NULL, GTK_MESSAGE_INFO );
1555 }
1556
1557 main_check_temp( total );
1558
1559 #ifdef DEBUG
1560 printf( "starting event dispatch loop\n" );
1561 #endif/*DEBUG*/
1562
1563 main_starting = FALSE;
1564
1565 symbol_recalculate_all_force( FALSE );
1566
1567 gtk_main();
1568 }
1569
1570 if( main_option_test ) {
1571 /* Make sure we've had at least one recomp.
1572 */
1573 symbol_recalculate_all_force( TRUE );
1574 if( expr_error_all )
1575 main_error_exit( "--test: errors found" );
1576 }
1577
1578 /* No return from this.
1579 */
1580 main_quit();
1581
1582 return( 0 );
1583 }
1584
1585 #ifdef OS_WIN32
1586 /* Get non-cmd line args on win32.
1587 */
1588 static int
breakargs(char * program,char * line,char ** argv)1589 breakargs( char *program, char *line, char **argv )
1590 {
1591 int argc = 1;
1592
1593 argv[0] = program;
1594
1595 while( *line && argc < MAX_SYSTEM - 1 ) {
1596 while( *line && isspace( *line ) )
1597 line++;
1598
1599 if( *line == '"' ) {
1600 /* Windows-95 quoted arguments
1601 */
1602 char *start = line + 1;
1603 char *end = start;
1604
1605 while( *end && *end != '"' )
1606 end++;
1607
1608 if( *end == '"' ) {
1609 *end = '\0';
1610 argv[argc++] = start;
1611 line = end + 1;
1612 continue;
1613 }
1614 }
1615
1616 if( *line ) {
1617 argv[argc++] = line;
1618 while( *line && !isspace( *line ) )
1619 line++;
1620
1621 if( *line )
1622 *line++ = '\0';
1623 }
1624 }
1625
1626 /* add trailing NULL pointer to argv
1627 */
1628 argv[argc] = NULL;
1629
1630 return( argc );
1631 }
1632
1633 int WINAPI
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpszCmdLine,int nShowCmd)1634 WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
1635 LPSTR lpszCmdLine, int nShowCmd )
1636 {
1637 char *argv[MAX_SYSTEM];
1638 int argc;
1639 TCHAR program[MAXPATHLEN];
1640
1641 GetModuleFileName( hInstance, program, sizeof(program) );
1642 argc = breakargs( (char *) program, lpszCmdLine, argv );
1643
1644 return( main( argc, argv ) );
1645 }
1646 #endif /*OS_WIN32*/
1647