Line data Source code
1 : /* Copyright (C) 2000-2012 by George Williams */
2 : /*
3 : * Redistribution and use in source and binary forms, with or without
4 : * modification, are permitted provided that the following conditions are met:
5 :
6 : * Redistributions of source code must retain the above copyright notice, this
7 : * list of conditions and the following disclaimer.
8 :
9 : * Redistributions in binary form must reproduce the above copyright notice,
10 : * this list of conditions and the following disclaimer in the documentation
11 : * and/or other materials provided with the distribution.
12 :
13 : * The name of the author may not be used to endorse or promote products
14 : * derived from this software without specific prior written permission.
15 :
16 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 : * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 : * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 : * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 : * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 : * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 : * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 : * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 : * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 : */
27 :
28 : #include "ffglib.h"
29 :
30 : #include <fontforge-config.h>
31 : #include "autosave.h"
32 : #include "bitmapchar.h"
33 : #include "clipnoui.h"
34 : #include "encoding.h"
35 : #include "fontforgeui.h"
36 : #include "lookups.h"
37 : #include "start.h"
38 :
39 : #ifndef _NO_LIBUNICODENAMES
40 : #include <libunicodenames.h> /* need to open a database when we start */
41 : extern uninm_names_db names_db; /* Unicode character names and annotations database */
42 : extern uninm_blocks_db blocks_db;
43 : #endif
44 : #include <gfile.h>
45 : #include <gresource.h>
46 : #include <ustring.h>
47 : #include <ltdl.h>
48 : #include <time.h>
49 : #include <sys/time.h>
50 : #include <locale.h>
51 : #include <unistd.h>
52 : #include <dynamic.h>
53 : #include <stdlib.h> /* getenv,setenv */
54 : #include <sys/stat.h>
55 : #include <sys/types.h>
56 : #include "../gdraw/hotkeys.h"
57 : #include "gutils/prefs.h"
58 :
59 :
60 : #define GTimer GTimer_GTK
61 : #include <glib.h>
62 : #include <glib-object.h>
63 : #undef GTimer
64 :
65 : #ifdef __Mac
66 : extern void setup_cocoa_app();
67 : #endif
68 :
69 : #ifdef _NO_LIBPNG
70 : # define PNGLIBNAME "libpng"
71 : #else
72 : # include <png.h> /* for version number to find up shared image name */
73 : # if !defined(PNG_LIBPNG_VER_MAJOR) || (PNG_LIBPNG_VER_MAJOR==1 && PNG_LIBPNG_VER_MINOR<2)
74 : # define PNGLIBNAME "libpng"
75 : # else
76 : # define xstr(s) str(s)
77 : # define str(s) #s
78 : # define PNGLIBNAME "libpng" xstr(PNG_LIBPNG_VER_MAJOR) xstr(PNG_LIBPNG_VER_MINOR)
79 : # endif
80 : #endif
81 : #ifdef __Mac
82 : # include <carbon.h>
83 : /* For reasons obscure to me RunApplicationEventLoop is not defined in */
84 : /* the mac header files if we are in 64 bit mode. Strangely it seems to */
85 : /* be in the libraries and functional */
86 : /*
87 : * It was found in Dec 2014 that using RunApplicationEventLoop() could induce strange
88 : * and extremely frustrating pausing issues on osx. The main generic event handling
89 : * seems to work just fine, so there doesn't seem to be a need for this specialized
90 : * Application Event Loop.
91 : *
92 : * See this issue bringing back Breakpad usage and the issues linked in comments 2,3
93 : * by adrientetar:
94 : * https://github.com/fontforge/fontforge/issues/2120
95 : */
96 : //# if __LP64__
97 : //extern void RunApplicationEventLoop(void);
98 : //# endif
99 : #endif
100 :
101 : #if defined(__MINGW32__)
102 : #include <windows.h>
103 : #define sleep(n) Sleep(1000 * (n))
104 : #endif
105 :
106 : #include "collabclientui.h"
107 : #include "scripting.h"
108 :
109 : extern int AutoSaveFrequency;
110 : int splash = 1;
111 : static int localsplash;
112 : static int unique = 0;
113 :
114 : /**
115 : * In osx versions prior to 10.9.x a special -psn_ flag was supplied
116 : * when fontforge was run by osx in some cases. For opening an sfd
117 : * file from finder we need to register the openWith event in order to
118 : * get the name of the file to open. So it makes sense to always
119 : * register for Apple events on OSX so that we can get those file
120 : * names as they come through.
121 : */
122 : #if defined(__Mac)
123 : static int listen_to_apple_events = true; // This was once true, but Apple broke it.
124 : #else
125 : static int listen_to_apple_events = false;
126 : #endif
127 : static bool ProcessPythonInitFiles = 1;
128 :
129 0 : static void _dousage(void) {
130 0 : printf( "fontforge [options] [fontfiles]\n" );
131 0 : printf( "\t-new\t\t\t (creates a new font)\n" );
132 0 : printf( "\t-last\t\t\t (loads the last sfd file closed)\n" );
133 : #if HANYANG
134 : printf( "\t-newkorean\t\t (creates a new korean font)\n" );
135 : #endif
136 0 : printf( "\t-recover none|auto|inquire|clean (control error recovery)\n" );
137 0 : printf( "\t-allglyphs\t\t (load all glyphs in the 'glyf' table\n\t\t\t of a truetype collection)\n" );
138 0 : printf( "\t-nosplash\t\t (no splash screen)\n" );
139 0 : printf( "\t-quiet\t\t\t (don't print non-essential information to stderr)\n" );
140 0 : printf( "\t-unique\t\t\t (if a fontforge is already running open\n\t\t\t all arguments in it and have this process exit)\n" );
141 0 : printf( "\t-display display-name\t (sets the X display)\n" );
142 0 : printf( "\t-depth val\t\t (sets the display depth if possible)\n" );
143 0 : printf( "\t-vc val\t\t\t (sets the visual class if possible)\n" );
144 0 : printf( "\t-cmap current|copy|private\t (sets the type of colormap)\n" );
145 0 : printf( "\t-dontopenxdevices\t (in case that fails)\n" );
146 0 : printf( "\t-sync\t\t\t (syncs the display, debugging)\n" );
147 0 : printf( "\t-keyboard ibm|mac|sun|ppc (generates appropriate hotkeys in menus)\n" );
148 : #if MyMemory
149 : printf( "\t-memory\t\t\t (turns on memory checks, debugging)\n" );
150 : #endif
151 : #ifndef _NO_LIBCAIRO
152 0 : printf( "\t-usecairo=yes|no Use (or not) the cairo library for drawing\n" );
153 : #endif
154 0 : printf( "\t-help\t\t\t (displays this message, and exits)\n" );
155 0 : printf( "\t-docs\t\t\t (displays this message, invokes a browser)\n\t\t\t\t (Using the BROWSER environment variable)\n" );
156 0 : printf( "\t-version\t\t (prints the version of fontforge and exits)\n" );
157 0 : printf( "\t-library-status\t (prints information about optional libraries\n\t\t\t\t and exits)\n" );
158 : #ifndef _NO_PYTHON
159 0 : printf( "\t-lang=py\t\t use python for scripts (may precede -script)\n" );
160 : #endif
161 : #ifndef _NO_FFSCRIPT
162 0 : printf( "\t-lang=ff\t\t use fontforge's legacy scripting language\n" );
163 : #endif
164 0 : printf( "\t-script scriptfile\t (executes scriptfile)\n" );
165 0 : printf( "\t\tmust be the first option (or follow -lang).\n" );
166 0 : printf( "\t\tAll others passed to scriptfile.\n" );
167 0 : printf( "\t-dry scriptfile\t\t (syntax checks scriptfile)\n" );
168 0 : printf( "\t\tmust be the first option. All others passed to scriptfile.\n" );
169 0 : printf( "\t\tOnly for fontforge's own scripting language, not python.\n" );
170 0 : printf( "\t-c script-string\t (executes argument as scripting cmds)\n" );
171 0 : printf( "\t\tmust be the first option. All others passed to the script.\n" );
172 0 : printf( "\n" );
173 0 : printf( "FontForge will read postscript (pfa, pfb, ps, cid), opentype (otf),\n" );
174 0 : printf( "\ttruetype (ttf,ttc), macintosh resource fonts (dfont,bin,hqx),\n" );
175 0 : printf( "\tand bdf and pcf fonts. It will also read its own format --\n" );
176 0 : printf( "\tsfd files.\n" );
177 0 : printf( "If no fontfiles are specified (and -new is not either and there's nothing\n" );
178 0 : printf( "\tto recover) then fontforge will produce an open font dlg.\n" );
179 0 : printf( "If a scriptfile is specified then FontForge will not open the X display\n" );
180 0 : printf( "\tnor will it process any additional arguments. It will execute the\n" );
181 0 : printf( "\tscriptfile and give it any remaining arguments\n" );
182 0 : printf( "If the first argument is an executable filename, and that file's first\n" );
183 0 : printf( "\tline contains \"fontforge\" then it will be treated as a scriptfile.\n\n" );
184 0 : printf( "For more information see:\n\thttp://fontforge.sourceforge.net/\n" );
185 0 : printf( "Send bug reports to:\tfontforge-devel@lists.sourceforge.net\n" );
186 0 : }
187 :
188 0 : static void dousage(void) {
189 0 : _dousage();
190 0 : exit(0);
191 : }
192 :
193 0 : static void dohelp(void) {
194 0 : _dousage();
195 0 : help("overview.html");
196 0 : exit(0);
197 : }
198 :
199 : struct delayed_event {
200 : void *data;
201 : void (*func)(void *);
202 : };
203 :
204 0 : static void BuildCharHook(GDisplay *gd) {
205 0 : GWidgetCreateInsChar();
206 0 : }
207 :
208 0 : static void InsCharHook(GDisplay *gd,unichar_t ch) {
209 0 : GInsCharSetChar(ch);
210 0 : }
211 :
212 : extern GImage splashimage;
213 : static GWindow splashw;
214 : static GTimer *autosave_timer, *splasht;
215 : static GFont *splash_font, *splash_italic;
216 : static int as,fh, linecnt;
217 : static unichar_t msg[470];
218 : static unichar_t *lines[30], *is, *ie;
219 :
220 0 : void ShowAboutScreen(void) {
221 : static int first=1;
222 :
223 0 : if ( first ) {
224 0 : GDrawResize(splashw,splashimage.u.image->width,splashimage.u.image->height+linecnt*fh);
225 0 : first = false;
226 : }
227 0 : if ( splasht!=NULL )
228 0 : GDrawCancelTimer(splasht);
229 0 : splasht=NULL;
230 0 : GDrawSetVisible(splashw,true);
231 0 : }
232 :
233 0 : static void SplashLayout() {
234 : unichar_t *start, *pt, *lastspace;
235 : extern const char *source_modtime_str;
236 : extern const char *source_version_str;
237 :
238 0 : uc_strcpy(msg, "When my father finished his book on Renaissance printing (The Craft of Printing and the Publication of Shakespeare's Works) he told me that I would have to write the chapter on computer typography. This is my attempt to do so.");
239 :
240 0 : GDrawSetFont(splashw,splash_font);
241 0 : linecnt = 0;
242 0 : lines[linecnt++] = msg-1;
243 0 : for ( start = msg; *start!='\0'; start = pt ) {
244 0 : lastspace = NULL;
245 0 : for ( pt=start; ; ++pt ) {
246 0 : if ( *pt==' ' || *pt=='\0' ) {
247 0 : if ( GDrawGetTextWidth(splashw,start,pt-start)<splashimage.u.image->width-10 )
248 0 : lastspace = pt;
249 : else
250 0 : break;
251 0 : if ( *pt=='\0' )
252 0 : break;
253 : }
254 0 : }
255 0 : if ( lastspace!=NULL )
256 0 : pt = lastspace;
257 0 : lines[linecnt++] = pt;
258 0 : if ( *pt ) ++pt;
259 : }
260 0 : uc_strcpy(pt, " FontForge used to be named PfaEdit.");
261 :
262 0 : pt += u_strlen(pt);
263 0 : lines[linecnt++] = pt;
264 0 : uc_strcpy(pt," git hash: ");
265 0 : pt += u_strlen(pt);
266 0 : lines[linecnt++] = pt;
267 0 : uc_strcat(pt, " ");
268 0 : uc_strcat(pt, FONTFORGE_GIT_VERSION);
269 :
270 0 : pt += u_strlen(pt);
271 0 : lines[linecnt++] = pt;
272 0 : uc_strcpy(pt," Version: ");
273 0 : uc_strcat(pt,FONTFORGE_MODTIME_STR);
274 :
275 0 : pt += u_strlen(pt);
276 0 : lines[linecnt++] = pt;
277 0 : uc_strcat(pt," (");
278 0 : uc_strcat(pt,FONTFORGE_MODTIME_STR);
279 0 : uc_strcat(pt,"-ML");
280 : #ifdef FREETYPE_HAS_DEBUGGER
281 : uc_strcat(pt,"-TtfDb");
282 : #endif
283 : #ifdef _NO_PYTHON
284 : uc_strcat(pt,"-NoPython");
285 : #endif
286 : #ifdef FONTFORGE_CONFIG_USE_DOUBLE
287 0 : uc_strcat(pt,"-D");
288 : #endif
289 0 : uc_strcat(pt,")");
290 0 : pt += u_strlen(pt);
291 0 : lines[linecnt++] = pt;
292 0 : uc_strcpy(pt," Lib Version: ");
293 0 : uc_strcat(pt,FONTFORGE_MODTIME_STR);
294 0 : lines[linecnt++] = pt+u_strlen(pt);
295 0 : lines[linecnt] = NULL;
296 0 : is = u_strchr(msg,'(');
297 0 : ie = u_strchr(msg,')');
298 0 : }
299 :
300 0 : void DelayEvent(void (*func)(void *), void *data) {
301 0 : struct delayed_event *info = calloc(1,sizeof(struct delayed_event));
302 :
303 0 : info->data = data;
304 0 : info->func = func;
305 0 : GDrawRequestTimer(splashw,100,0,info);
306 0 : }
307 :
308 0 : static void DoDelayedEvents(GEvent *event) {
309 0 : GTimer *t = event->u.timer.timer;
310 0 : struct delayed_event *info = (struct delayed_event *) (event->u.timer.userdata);
311 :
312 0 : if ( info!=NULL ) {
313 0 : (info->func)(info->data);
314 0 : free(info);
315 : }
316 0 : GDrawCancelTimer(t);
317 0 : }
318 :
319 : struct argsstruct {
320 : int next;
321 : int argc;
322 : char **argv;
323 : int any;
324 : };
325 :
326 0 : static void SendNextArg(struct argsstruct *args) {
327 : int i;
328 : char *msg;
329 : static GTimer *timeout;
330 :
331 0 : if ( timeout!=NULL ) {
332 0 : GDrawCancelTimer(timeout);
333 0 : timeout = NULL;
334 : }
335 :
336 0 : for ( i=args->next; i<args->argc; ++i ) {
337 0 : if ( *args->argv[i]!='-' ||
338 0 : strcmp(args->argv[i],"-quit")==0 || strcmp(args->argv[i],"--quit")==0 ||
339 0 : strcmp(args->argv[i],"-new")==0 || strcmp(args->argv[i],"--new")==0 )
340 : break;
341 : }
342 0 : if ( i>=args->argc ) {
343 0 : if ( args->any )
344 0 : exit(0); /* Sent everything */
345 0 : msg = "-open";
346 : } else
347 0 : msg = args->argv[i];
348 0 : args->next = i+1;
349 0 : args->any = true;
350 :
351 0 : GDrawGrabSelection(splashw,sn_user1);
352 0 : GDrawAddSelectionType(splashw,sn_user1,"STRING",
353 0 : copy(msg),strlen(msg),1,
354 : NULL,NULL);
355 :
356 : /* If we just sent the other fontforge a request to die, it will never*/
357 : /* take the selection back. So we should just die quietly */
358 : /* But we can't die instantly, or it will never get our death threat */
359 : /* (it won't have a chance to ask us for the selection if we're dead)*/
360 0 : timeout = GDrawRequestTimer(splashw,1000,0,NULL);
361 0 : }
362 :
363 : /* When we want to send filenames to another running fontforge we want a */
364 : /* different event handler. We won't have a splash window in that case, */
365 : /* just an invisible utility window on which we perform a little selection */
366 : /* dance */
367 0 : static int request_e_h(GWindow gw, GEvent *event) {
368 :
369 0 : if ( event->type == et_selclear ) {
370 0 : SendNextArg( GDrawGetUserData(gw));
371 0 : } else if ( event->type == et_timer )
372 0 : exit( 0 );
373 :
374 0 : return( true );
375 : }
376 :
377 0 : static void PingOtherFontForge(int argc, char **argv) {
378 : struct argsstruct args;
379 :
380 0 : args.next = 1;
381 0 : args.argc = argc;
382 0 : args.argv = argv;
383 0 : args.any = false;
384 0 : GDrawSetUserData(splashw,&args);
385 0 : SendNextArg(&args);
386 0 : GDrawEventLoop(NULL);
387 0 : exit( 0 ); /* But the event loop should never return */
388 : }
389 :
390 0 : static void start_splash_screen(void){
391 0 : GDrawSetVisible(splashw,true);
392 0 : GDrawSync(NULL);
393 0 : GDrawProcessPendingEvents(NULL);
394 0 : GDrawProcessPendingEvents(NULL);
395 0 : splasht = GDrawRequestTimer(splashw,1000,1000,NULL);
396 :
397 0 : localsplash = false;
398 0 : }
399 :
400 : #if defined(__Mac)
401 : static FILE *logfile;
402 :
403 : /* These are the four apple events to which we currently respond */
404 : static pascal OSErr OpenApplicationAE( const AppleEvent * theAppleEvent,
405 : AppleEvent * reply, SInt32 handlerRefcon) {
406 : fprintf( logfile, "OPENAPP event received.\n" ); fflush( logfile );
407 : if ( localsplash )
408 : start_splash_screen();
409 : system( "DYLD_LIBRARY_PATH=\"\"; osascript -e 'tell application \"X11\" to activate'" );
410 : if ( fv_list==NULL )
411 : _FVMenuOpen(NULL);
412 : fprintf( logfile, " event processed %d.\n", noErr ); fflush( logfile );
413 : return( noErr );
414 : }
415 :
416 : static pascal OSErr ReopenApplicationAE( const AppleEvent * theAppleEvent,
417 : AppleEvent * reply, SInt32 handlerRefcon) {
418 : fprintf( logfile, "ReOPEN event received.\n" ); fflush( logfile );
419 : if ( localsplash )
420 : start_splash_screen();
421 : system( "DYLD_LIBRARY_PATH=\"\"; osascript -e 'tell application \"X11\" to activate'" );
422 : if ( fv_list==NULL )
423 : _FVMenuOpen(NULL);
424 : fprintf( logfile, " event processed %d.\n", noErr ); fflush( logfile );
425 : return( noErr );
426 : }
427 :
428 : static pascal OSErr ShowPreferencesAE( const AppleEvent * theAppleEvent,
429 : AppleEvent * reply, SInt32 handlerRefcon) {
430 : fprintf( logfile, "PREFS event received.\n" ); fflush( logfile );
431 : if ( localsplash )
432 : start_splash_screen();
433 : system( "DYLD_LIBRARY_PATH=\"\"; osascript -e 'tell application \"X11\" to activate'" );
434 : DoPrefs();
435 : fprintf( logfile, " event processed %d.\n", noErr ); fflush( logfile );
436 : return( noErr );
437 : }
438 :
439 : static pascal OSErr OpenDocumentsAE( const AppleEvent * theAppleEvent,
440 : AppleEvent * reply, SInt32 handlerRefcon) {
441 : AEDescList docList;
442 : FSRef theFSRef;
443 : long index;
444 : long count = 0;
445 : OSErr err;
446 : char buffer[2048];
447 :
448 : fprintf( logfile, "OPEN event received.\n" ); fflush( logfile );
449 : if ( localsplash )
450 : start_splash_screen();
451 :
452 : err = AEGetParamDesc(theAppleEvent, keyDirectObject,
453 : typeAEList, &docList);
454 : err = AECountItems(&docList, &count);
455 : for(index = 1; index <= count; index++) {
456 : err = AEGetNthPtr(&docList, index, typeFSRef,
457 : NULL, NULL, &theFSRef,
458 : sizeof(theFSRef), NULL);// 4
459 : err = FSRefMakePath(&theFSRef,(unsigned char *) buffer,sizeof(buffer));
460 : ViewPostScriptFont(buffer,0);
461 : fprintf( logfile, " file: %s\n", buffer );
462 : }
463 : system( "DYLD_LIBRARY_PATH=\"\"; osascript -e 'tell application \"X11\" to activate'" );
464 : AEDisposeDesc(&docList);
465 : fprintf( logfile, " event processed %d.\n", err ); fflush( logfile );
466 :
467 : return( err );
468 : }
469 :
470 : static void AttachErrorCode(AppleEvent *event,OSStatus err) {
471 : OSStatus returnVal;
472 :
473 : if ( event==NULL )
474 : return;
475 :
476 : if (event->descriptorType != typeNull) {
477 : /* Check there isn't already an error attached */
478 : returnVal = AESizeOfParam(event, keyErrorNumber, NULL, NULL);
479 : if (returnVal != noErr ) { /* Add success if no previous error */
480 : AEPutParamPtr(event, keyErrorNumber,
481 : typeSInt32, &err, sizeof(err));
482 : }
483 : }
484 : }
485 :
486 : static AppleEvent *quit_event = NULL;
487 : static void we_are_dead(void) {
488 : AttachErrorCode(quit_event,noErr);
489 : /* Send the reply (I hope) */
490 : AESendMessage(quit_event,NULL, kAENoReply, kAEDefaultTimeout);
491 : AEDisposeDesc(quit_event);
492 : /* fall off the end of the world and die */
493 : fprintf( logfile, " event succeded.\n"); fflush( logfile );
494 : }
495 :
496 : static pascal OSErr QuitApplicationAE( const AppleEvent * theAppleEvent,
497 : AppleEvent * reply, SInt32 handlerRefcon) {
498 : static int first_time = true;
499 :
500 : fprintf( logfile, "QUIT event received.\n" ); fflush( logfile );
501 : quit_event = reply;
502 : if ( first_time ) {
503 : atexit( we_are_dead );
504 : first_time = false;
505 : }
506 : MenuExit(NULL,NULL,NULL);
507 : /* if we get here, they canceled the quit, so we return a failure */
508 : quit_event = NULL;
509 : fprintf( logfile, " event failed %d.\n", errAEEventFailed ); fflush( logfile );
510 : return(errAEEventFailed);
511 : }
512 :
513 : /* Install event handlers for the Apple Events we care about */
514 : static OSErr install_apple_event_handlers(void) {
515 : OSErr err;
516 :
517 : err = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication,
518 : NewAEEventHandlerUPP(OpenApplicationAE), 0, false);
519 : require_noerr(err, CantInstallAppleEventHandler);
520 :
521 : err = AEInstallEventHandler(kCoreEventClass, kAEReopenApplication,
522 : NewAEEventHandlerUPP(ReopenApplicationAE), 0, false);
523 : require_noerr(err, CantInstallAppleEventHandler);
524 :
525 : err = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments,
526 : NewAEEventHandlerUPP(OpenDocumentsAE), 0, false);
527 : require_noerr(err, CantInstallAppleEventHandler);
528 :
529 : err = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,
530 : NewAEEventHandlerUPP(QuitApplicationAE), 0, false);
531 : require_noerr(err, CantInstallAppleEventHandler);
532 :
533 : err = AEInstallEventHandler(kCoreEventClass, kAEShowPreferences,
534 : NewAEEventHandlerUPP(ShowPreferencesAE), 0, false);
535 : require_noerr(err, CantInstallAppleEventHandler);
536 :
537 : /* some debugging code, for now */
538 : if ( getenv("HOME")!=NULL ) {
539 : char buffer[1024];
540 : sprintf( buffer, "%s/.FontForge-LogFile.txt", getenv("HOME"));
541 : logfile = fopen("/tmp/LogFile.txt","w");
542 : }
543 : if ( logfile==NULL )
544 : logfile = stderr;
545 :
546 : CantInstallAppleEventHandler:
547 : return err;
548 :
549 : }
550 :
551 : static pascal void DoRealStuff(EventLoopTimerRef timer,void *ignored_data) {
552 : GDrawProcessPendingEvents(NULL);
553 : MacServiceReadFDs();
554 : }
555 :
556 : static void install_mac_timer(void) {
557 : EventLoopTimerRef timer;
558 :
559 : InstallEventLoopTimer(GetMainEventLoop(),
560 : .001*kEventDurationSecond,.001*kEventDurationSecond,
561 : NewEventLoopTimerUPP(DoRealStuff), NULL,
562 : &timer);
563 : }
564 : #endif
565 :
566 0 : static int splash_e_h(GWindow gw, GEvent *event) {
567 : static int splash_cnt;
568 : GRect old;
569 : int i, y, x;
570 : static char *foolishness[] = {
571 : /* GT: These strings are for fun. If they are offensive or incomprehensible */
572 : /* GT: simply translate them as something dull like: "FontForge" */
573 : /* GT: This is a spoof of political slogans, designed to point out how foolish they are */
574 : N_("A free press discriminates\nagainst the illiterate."),
575 : N_("A free press discriminates\nagainst the illiterate."),
576 : /* GT: This is a pun on the old latin drinking song "Gaudeamus igature!" */
577 : N_("Gaudeamus Ligature!"),
578 : N_("Gaudeamus Ligature!"),
579 : /* GT: Spoof on the bible */
580 : N_("In the beginning was the letter..."),
581 : /* GT: Some wit at MIT came up with this ("ontology recapitulates phylogony" is the original) */
582 : N_("fontology recapitulates file-ogeny")
583 : };
584 :
585 0 : switch ( event->type ) {
586 : case et_create:
587 0 : GDrawGrabSelection(gw,sn_user1);
588 0 : break;
589 : case et_expose:
590 0 : GDrawPushClip(gw,&event->u.expose.rect,&old);
591 0 : GDrawDrawImage(gw,&splashimage,NULL,0,0);
592 0 : GDrawSetFont(gw,splash_font);
593 0 : y = splashimage.u.image->height + as + fh/2;
594 0 : for ( i=1; i<linecnt; ++i ) {
595 0 : if ( is>=lines[i-1]+1 && is<lines[i] ) {
596 0 : x = 8+GDrawDrawText(gw,8,y,lines[i-1]+1,is-lines[i-1]-1,0x000000);
597 0 : GDrawSetFont(gw,splash_italic);
598 0 : GDrawDrawText(gw,x,y,is,lines[i]-is,0x000000);
599 0 : } else if ( ie>=lines[i-1]+1 && ie<lines[i] ) {
600 0 : x = 8+GDrawDrawText(gw,8,y,lines[i-1]+1,ie-lines[i-1]-1,0x000000);
601 0 : GDrawSetFont(gw,splash_font);
602 0 : GDrawDrawText(gw,x,y,ie,lines[i]-ie,0x000000);
603 : } else
604 0 : GDrawDrawText(gw,8,y,lines[i-1]+1,lines[i]-lines[i-1]-1,0x000000);
605 0 : y += fh;
606 : }
607 0 : GDrawPopClip(gw,&old);
608 0 : break;
609 : case et_map:
610 0 : splash_cnt = 0;
611 0 : break;
612 : case et_timer:
613 0 : if ( event->u.timer.timer==autosave_timer ) {
614 0 : DoAutoSaves();
615 0 : } else if ( event->u.timer.timer==splasht ) {
616 0 : if ( ++splash_cnt==1 )
617 0 : GDrawResize(gw,splashimage.u.image->width,splashimage.u.image->height-30);
618 0 : else if ( splash_cnt==2 )
619 0 : GDrawResize(gw,splashimage.u.image->width,splashimage.u.image->height);
620 0 : else if ( splash_cnt>=7 ) {
621 0 : GGadgetEndPopup();
622 0 : GDrawSetVisible(gw,false);
623 0 : GDrawCancelTimer(splasht);
624 0 : splasht = NULL;
625 : }
626 : } else {
627 0 : DoDelayedEvents(event);
628 : }
629 0 : break;
630 : case et_char:
631 : case et_mousedown:
632 : case et_close:
633 0 : GGadgetEndPopup();
634 0 : GDrawSetVisible(gw,false);
635 0 : break;
636 : case et_mousemove:
637 0 : GGadgetPreparePopup8(gw,_(foolishness[rand()%(sizeof(foolishness)/sizeof(foolishness[0]))]) );
638 0 : break;
639 : case et_selclear:
640 : /* If this happens, it means someone wants to send us a message with a*/
641 : /* filename to open. So we need to ask for it, process it, and then */
642 : /* take the selection back again */
643 0 : if ( event->u.selclear.sel == sn_user1 ) {
644 : int len;
645 : char *arg;
646 0 : arg = GDrawRequestSelection(splashw,sn_user1,"STRING",&len);
647 0 : if ( arg==NULL )
648 0 : return( true );
649 0 : if ( strcmp(arg,"-new")==0 || strcmp(arg,"--new")==0 )
650 0 : FontNew();
651 0 : else if ( strcmp(arg,"-open")==0 || strcmp(arg,"--open")==0 )
652 0 : _FVMenuOpen(NULL);
653 0 : else if ( strcmp(arg,"-quit")==0 || strcmp(arg,"--quit")==0 )
654 0 : MenuExit(NULL,NULL,NULL);
655 : else
656 0 : ViewPostScriptFont(arg,0);
657 0 : free(arg);
658 0 : GDrawGrabSelection(splashw,sn_user1);
659 : }
660 0 : break;
661 : case et_destroy:
662 0 : IError("Who killed the splash screen?");
663 0 : break;
664 : }
665 0 : return( true );
666 : }
667 :
668 0 : static void AddR(char *program_name, char *window_name, char *cmndline_val) {
669 : /* Add this command line value to this GUI resource. */
670 : /* These are the command line options expected when using this routine: */
671 : /* -depth, -vc,-cmap or -colormap,-dontopenxdevices, -keyboard */
672 : char *full;
673 0 : if ((full = malloc(strlen(window_name)+strlen(cmndline_val)+4))!=NULL) {
674 0 : strcpy(full,window_name);
675 0 : strcat(full,": ");
676 0 : strcat(full,cmndline_val);
677 0 : GResourceAddResourceString(full,program_name);
678 0 : free(full);
679 : }
680 0 : }
681 :
682 0 : static int ReopenLastFonts(void) {
683 : char buffer[1024];
684 0 : char *ffdir = getFontForgeUserDir(Config);
685 : FILE *old;
686 0 : int any = 0;
687 :
688 0 : if ( ffdir==NULL ) return false;
689 :
690 0 : sprintf( buffer, "%s/FontsOpenAtLastQuit", ffdir );
691 0 : old = fopen(buffer,"r");
692 0 : if ( old==NULL ) {
693 0 : free(ffdir);
694 0 : return false;
695 : }
696 0 : while ( fgets(buffer,sizeof(buffer),old)!=NULL ) {
697 0 : if ( ViewPostScriptFont(g_strchomp(buffer),0)!=0 )
698 0 : any = 1;
699 : }
700 0 : fclose(old);
701 0 : free(ffdir);
702 0 : return any;
703 : }
704 :
705 : #if defined(__Mac)
706 : /* Read a property from the x11 properties files */
707 : /* At the moment we want to know if we get the command key, or if the menubar */
708 : /* eats it */
709 : static int get_mac_x11_prop(char *keystr) {
710 : CFPropertyListRef ret;
711 : CFStringRef key, appID;
712 : int val;
713 :
714 : appID = CFStringCreateWithBytes(NULL,(uint8 *) "com.apple.x11",strlen("com.apple.x11"), kCFStringEncodingISOLatin1, 0);
715 : key = CFStringCreateWithBytes(NULL,(uint8 *) keystr,strlen(keystr), kCFStringEncodingISOLatin1, 0);
716 : ret = CFPreferencesCopyAppValue(key,appID);
717 : if ( ret==NULL ) {
718 : /* Sigh. Apple uses a different preference file under 10.5.6 I really */
719 : /* wish they'd stop making stupid, unnecessary changes */
720 : appID = CFStringCreateWithBytes(NULL,(uint8 *) "org.x.X11",strlen("org.x.X11"), kCFStringEncodingISOLatin1, 0);
721 : ret = CFPreferencesCopyAppValue(key,appID);
722 : }
723 : if ( ret==NULL )
724 : return( -1 );
725 : if ( CFGetTypeID(ret)!=CFBooleanGetTypeID())
726 : return( -2 );
727 : val = CFBooleanGetValue(ret);
728 : CFRelease(ret);
729 : return( val );
730 : }
731 :
732 : static int uses_local_x(int argc,char **argv) {
733 : int i;
734 : char *arg;
735 :
736 : for ( i=1; i<argc; ++i ) {
737 : arg = argv[i];
738 : if ( *arg=='-' ) {
739 : if ( arg[0]=='-' && arg[1]=='-' && arg[2]!='\0')
740 : ++arg;
741 : if ( strcmp(arg,"-display")==0 )
742 : return( i+1<argc && strcmp(argv[i+1],":0")!=0 && strcmp(argv[i+1],":0.0")!=0? 2 : 0 );
743 : if ( strcmp(argv[i],"-c")==0 )
744 : return( false ); /* we use a script string, no x display at all */
745 : if ( strcmp(arg,"-script")==0 )
746 : return( false ); /* we use a script, no x display at all */
747 : if ( strcmp(argv[i],"-")==0 )
748 : return( false ); /* script on stdin */
749 : } else {
750 : /* Is this argument a script file ? */
751 : FILE *temp = fopen(argv[i],"r");
752 : char buffer[200];
753 : if ( temp==NULL )
754 : return( true ); /* not a script file, so need local local X */
755 : buffer[0] = '\0';
756 : fgets(buffer,sizeof(buffer),temp);
757 : fclose(temp);
758 : if ( buffer[0]=='#' && buffer[1]=='!' &&
759 : (strstr(buffer,"pfaedit")!=NULL || strstr(buffer,"fontforge")!=NULL )) {
760 : return( false ); /* is a script file, so no need for X */
761 :
762 : return( true ); /* not a script, so needs X */
763 : }
764 : }
765 : }
766 : return( true );
767 : }
768 : #endif
769 :
770 :
771 : #if defined(__Mac)
772 : static int hasquit( int argc, char **argv ) {
773 : int i;
774 :
775 : for ( i=1; i<argc; ++i )
776 : if ( strcmp(argv[i],"-quit")==0 || strcmp(argv[i],"--quit")==0 )
777 : return( true );
778 :
779 : return( false );
780 : }
781 : #endif
782 :
783 0 : static void GrokNavigationMask(void) {
784 : extern int navigation_mask;
785 :
786 0 : navigation_mask = GMenuItemParseMask(H_("NavigationMask|None"));
787 0 : }
788 :
789 : /**
790 : * Create the directory basedir/dirname with the given mode.
791 : * Silently ignore any errors that might happen.
792 : */
793 0 : static void ffensuredir( const char* basedir, const char* dirname, mode_t mode ) {
794 0 : const int buffersz = PATH_MAX;
795 0 : char buffer[buffersz+1];
796 :
797 0 : snprintf(buffer,buffersz,"%s/%s", basedir, dirname );
798 : // ignore errors, this is just to help the user aftre all.
799 0 : mkdir( buffer, mode );
800 0 : }
801 :
802 0 : static void ensureDotFontForgeIsSetup() {
803 0 : char *basedir = getFontForgeUserDir(Config);
804 0 : if ( !basedir ) {
805 0 : return;
806 : }
807 0 : ffensuredir( basedir, "", S_IRWXU );
808 0 : ffensuredir( basedir, "python", S_IRWXU );
809 0 : free(basedir);
810 : }
811 :
812 0 : static void DoAutoRecoveryPostRecover_PromptUserGraphically(SplineFont *sf)
813 : {
814 : /* Ask user to save-as file */
815 : char *buts[4];
816 0 : buts[0] = _("_OK");
817 0 : buts[1] = 0;
818 0 : gwwv_ask( _("Recovery Complete"),(const char **) buts,0,1,_("Your file %s has been recovered.\nYou must now Save your file to continue working on it."), sf->filename );
819 0 : _FVMenuSaveAs( (FontView*)sf->fv );
820 0 : }
821 :
822 : #if defined(__MINGW32__) && !defined(_NO_LIBCAIRO)
823 : /**
824 : * \brief Load fonts from the specified folder for the UI to use.
825 : * This should only be used if Cairo is used on Windows, which defaults to the
826 : * Win32 font backend.
827 : * This is an ANSI version, so files which contain characters outside of the
828 : * user's locale will fail to be loaded.
829 : * \param prefix The folder to read fonts from. Currently the pixmaps folder
830 : * and the folder 'ui-fonts' in the FontForge preferences folder.
831 : */
832 : static void WinLoadUserFonts(const char *prefix) {
833 : HANDLE fileHandle;
834 : WIN32_FIND_DATA fileData;
835 : char path[MAX_PATH], *ext;
836 : HRESULT ret;
837 : int i;
838 :
839 : if (prefix == NULL) {
840 : return;
841 : }
842 : ret = snprintf(path, MAX_PATH, "%s/*.???", prefix);
843 : if (ret <= 0 || ret >= MAX_PATH) {
844 : return;
845 : }
846 :
847 : fileHandle = FindFirstFileA(path, &fileData);
848 : if (fileHandle != INVALID_HANDLE_VALUE) do {
849 : ext = strrchr(fileData.cFileName, '.');
850 : if (!ext || (strcasecmp(ext, ".ttf") && strcasecmp(ext, ".ttc") &&
851 : strcasecmp(ext,".otf")))
852 : {
853 : continue;
854 : }
855 : ret = snprintf(path, MAX_PATH, "%s/%s", prefix, fileData.cFileName);
856 : if (ret > 0 && ret < MAX_PATH) {
857 : //printf("WIN32-FONT-TEST: %s\n", path);
858 : ret = AddFontResourceExA(path, FR_PRIVATE, NULL);
859 : //if (ret > 0) {
860 : // printf("\tLOADED FONT OK!\n");
861 : //}
862 : }
863 : } while (FindNextFileA(fileHandle, &fileData) != 0);
864 : }
865 : #endif
866 :
867 :
868 40 : int fontforge_main( int argc, char **argv ) {
869 : extern const char *source_modtime_str;
870 : extern const char *source_version_str;
871 40 : const char *load_prefs = getenv("FONTFORGE_LOADPREFS");
872 : int i;
873 40 : int recover=2;
874 : int any;
875 40 : int next_recent=0;
876 : GRect pos;
877 : GWindowAttrs wattrs;
878 40 : char *display = NULL;
879 : FontRequest rq;
880 : int ds, ld;
881 40 : int openflags=0;
882 40 : int doopen=0, quit_request=0;
883 40 : bool use_cairo = true;
884 :
885 : #if !(GLIB_CHECK_VERSION(2, 35, 0))
886 : g_type_init();
887 : #endif
888 :
889 : /* Must be done before we cache the current directory */
890 : /* Change to HOME dir if specified on the commandline */
891 408 : for ( i=1; i<argc; ++i ) {
892 368 : char *pt = argv[i];
893 368 : if ( pt[0]=='-' && pt[1]=='-' ) ++pt;
894 368 : if (strcmp(pt,"-home")==0 || strncmp(pt,"-psn_",5)==0) {
895 : /* OK, I don't know what _-psn_ means, but to GW it means */
896 : /* we've been started on the mac from the FontForge.app */
897 : /* structure, and the current directory is (shudder) "/" */
898 0 : if (getenv("HOME")!=NULL) chdir(getenv("HOME"));
899 0 : break; /* Done - Unnecessary to check more arguments */
900 : }
901 368 : if (strcmp(pt,"-quiet")==0)
902 0 : quiet = 1;
903 : }
904 :
905 40 : if (!quiet) {
906 40 : fprintf( stderr, "Copyright (c) 2000-2014 by George Williams. See AUTHORS for Contributors.\n" );
907 40 : fprintf( stderr, " License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n" );
908 40 : fprintf( stderr, " with many parts BSD <http://fontforge.org/license.html>. Please read LICENSE.\n" );
909 40 : fprintf( stderr, " Based on sources from %s"
910 : "-ML"
911 : #ifdef FREETYPE_HAS_DEBUGGER
912 : "-TtfDb"
913 : #endif
914 : #ifdef _NO_PYTHON
915 : "-NoPython"
916 : #endif
917 : #ifdef FONTFORGE_CONFIG_USE_DOUBLE
918 : "-D"
919 : #endif
920 : ".\n",
921 : FONTFORGE_MODTIME_STR );
922 40 : fprintf( stderr, " Based on source from git with hash: %s\n", FONTFORGE_GIT_VERSION );
923 : }
924 :
925 : #if defined(__Mac)
926 : /* Start X if they haven't already done so. Well... try anyway */
927 : /* Must be before we change DYLD_LIBRARY_PATH or X won't start */
928 : /* (osascript depends on a libjpeg which isn't found if we look in /sw/lib first */
929 : int local_x = uses_local_x(argc,argv);
930 : if ( local_x==1 && getenv("DISPLAY")==NULL ) {
931 : /* Don't start X if we're just going to quit. */
932 : /* if X exists, it isn't needed. If X doesn't exist it's wrong */
933 : if ( !hasquit(argc,argv)) {
934 : /* This sequence is supposed to bring up an app without a window */
935 : /* but X still opens an xterm */
936 : system( "osascript -e 'tell application \"X11\" to launch'" );
937 : system( "osascript -e 'tell application \"X11\" to activate'" );
938 : }
939 : setenv("DISPLAY",":0.0",0);
940 : } else if ( local_x==1 && *getenv("DISPLAY")!='/' && strcmp(getenv("DISPLAY"),":0.0")!=0 && strcmp(getenv("DISPLAY"),":0")!=0 )
941 : /* 10.5.7 uses a named socket or something "/tmp/launch-01ftWX:0" */
942 : local_x = 0;
943 : #endif
944 :
945 : #if defined(__MINGW32__)
946 : if( getenv("DISPLAY")==NULL ) {
947 : putenv("DISPLAY=127.0.0.1:0.0");
948 : }
949 : if( getenv("LC_ALL")==NULL ){
950 : char lang[8];
951 : char env[32];
952 : if( GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, lang, 8) > 0 ){
953 : strcpy(env, "LC_ALL=");
954 : strcat(env, lang);
955 : putenv(env);
956 : }
957 : }
958 : #endif
959 :
960 40 : FF_SetUiInterface(&gdraw_ui_interface);
961 40 : FF_SetPrefsInterface(&gdraw_prefs_interface);
962 40 : FF_SetSCInterface(&gdraw_sc_interface);
963 40 : FF_SetCVInterface(&gdraw_cv_interface);
964 40 : FF_SetBCInterface(&gdraw_bc_interface);
965 40 : FF_SetFVInterface(&gdraw_fv_interface);
966 40 : FF_SetFIInterface(&gdraw_fi_interface);
967 40 : FF_SetMVInterface(&gdraw_mv_interface);
968 40 : FF_SetClipInterface(&gdraw_clip_interface);
969 : #ifndef _NO_PYTHON
970 40 : PythonUI_Init();
971 : #endif
972 :
973 40 : FindProgDir(argv[0]);
974 40 : InitSimpleStuff();
975 :
976 : #if defined(__MINGW32__)
977 : {
978 : char path[MAX_PATH];
979 : unsigned int len = GetModuleFileNameA(NULL, path, MAX_PATH);
980 : path[len] = '\0';
981 :
982 : //The '.exe' must be removed as resources presumes it's not there.
983 : GResourceSetProg(GFileRemoveExtension(GFileNormalizePath(path)));
984 : }
985 : #else
986 40 : GResourceSetProg(argv[0]);
987 : #endif
988 :
989 : #if defined(__Mac)
990 : /* The mac seems to default to the "C" locale, LANG and LC_MESSAGES are not*/
991 : /* defined. This means that gettext will not bother to look up any message*/
992 : /* files -- even if we have a "C" or "POSIX" entry in the locale diretory */
993 : /* Now if X11 gives us the command key, I want to force a rebinding to use */
994 : /* Cmd rather than Control key -- more mac-like. But I can't do that if */
995 : /* there is no locale. So I force a locale if there is none specified */
996 : /* I force the US English locale, because that's the what the messages are */
997 : /* by default so I'm changing as little as I can. I think. */
998 : /* Now the locale command will treat a LANG which is "" as undefined, but */
999 : /* gettext will not. So I don't bother to check for null strings or "C" */
1000 : /* or "POSIX". If they've mucked with the locale perhaps they know what */
1001 : /* they are doing */
1002 : {
1003 : int did_keybindings = 0;
1004 : int useCommandKey = get_mac_x11_prop("enable_key_equivalents") <= 0;
1005 :
1006 : if ( local_x && useCommandKey ) {
1007 : hotkeySystemSetCanUseMacCommand( 1 );
1008 :
1009 : /* Ok, we get the command key */
1010 : if ( getenv("LANG")==NULL && getenv("LC_MESSAGES")==NULL ) {
1011 : setenv("LC_MESSAGES","en_US.UTF-8",0);
1012 : }
1013 : /* Can we find a set of keybindings designed for the mac with cmd key? */
1014 : bind_textdomain_codeset("Mac-FontForge-MenuShortCuts","UTF-8");
1015 : bindtextdomain("Mac-FontForge-MenuShortCuts", getLocaleDir());
1016 : if ( *dgettext("Mac-FontForge-MenuShortCuts","Flag0x10+")!='F' ) {
1017 : GMenuSetShortcutDomain("Mac-FontForge-MenuShortCuts");
1018 : did_keybindings = 1;
1019 : }
1020 : }
1021 : if ( !did_keybindings ) {
1022 : /* Nope. we can't. Fall back to the normal stuff */
1023 : #endif
1024 40 : GMenuSetShortcutDomain("FontForge-MenuShortCuts");
1025 40 : bind_textdomain_codeset("FontForge-MenuShortCuts","UTF-8");
1026 40 : bindtextdomain("FontForge-MenuShortCuts", getLocaleDir());
1027 : #if defined(__Mac)
1028 : }
1029 : }
1030 : #endif
1031 40 : bind_textdomain_codeset("FontForge","UTF-8");
1032 40 : bindtextdomain("FontForge", getLocaleDir());
1033 40 : textdomain("FontForge");
1034 40 : GResourceUseGetText();
1035 : {
1036 : char shareDir[PATH_MAX];
1037 40 : char* sd = getShareDir();
1038 40 : strncpy( shareDir, sd, PATH_MAX );
1039 40 : shareDir[PATH_MAX-1] = '\0';
1040 40 : if(!sd) {
1041 0 : strcpy( shareDir, SHAREDIR );
1042 : }
1043 :
1044 : char path[PATH_MAX];
1045 40 : snprintf(path, PATH_MAX, "%s%s", shareDir, "/pixmaps" );
1046 40 : GGadgetSetImageDir( path );
1047 :
1048 40 : snprintf(path, PATH_MAX, "%s%s", shareDir, "/resources/fontforge.resource" );
1049 40 : GResourceAddResourceFile(path, GResourceProgramName,false);
1050 : }
1051 40 : hotkeysLoad();
1052 : // loadPrefsFiles();
1053 40 : Prefs_LoadDefaultPreferences();
1054 :
1055 40 : if ( load_prefs!=NULL && strcasecmp(load_prefs,"Always")==0 )
1056 0 : LoadPrefs();
1057 40 : if ( default_encoding==NULL )
1058 40 : default_encoding=FindOrMakeEncoding("ISO8859-1");
1059 40 : if ( default_encoding==NULL )
1060 0 : default_encoding=&custom; /* In case iconv is broken */
1061 :
1062 : // This no longer starts embedded Python unless control passes to the Python executors,
1063 : // which exit independently rather than returning here.
1064 40 : CheckIsScript(argc,argv); /* Will run the script and exit if it is a script */
1065 : /* If there is no UI, there is always a script */
1066 : /* and we will never return from the above */
1067 0 : if ( load_prefs==NULL ||
1068 0 : (strcasecmp(load_prefs,"Always")!=0 && /* Already loaded */
1069 0 : strcasecmp(load_prefs,"Never")!=0 ))
1070 0 : LoadPrefs();
1071 0 : GrokNavigationMask();
1072 0 : for ( i=1; i<argc; ++i ) {
1073 0 : char *pt = argv[i];
1074 0 : if ( pt[0]=='-' && pt[1]=='-' )
1075 0 : ++pt;
1076 0 : if ( strcmp(pt,"-sync")==0 )
1077 0 : GResourceAddResourceString("Gdraw.Synchronize: true",argv[0]);
1078 0 : else if ( strcmp(pt,"-depth")==0 && i<argc-1 )
1079 0 : AddR(argv[0],"Gdraw.Depth", argv[++i]);
1080 0 : else if ( strcmp(pt,"-vc")==0 && i<argc-1 )
1081 0 : AddR(argv[0],"Gdraw.VisualClass", argv[++i]);
1082 0 : else if ( (strcmp(pt,"-cmap")==0 || strcmp(pt,"-colormap")==0) && i<argc-1 )
1083 0 : AddR(argv[0],"Gdraw.Colormap", argv[++i]);
1084 0 : else if ( (strcmp(pt,"-dontopenxdevices")==0) )
1085 0 : AddR(argv[0],"Gdraw.DontOpenXDevices", "true");
1086 0 : else if ( strcmp(pt,"-keyboard")==0 && i<argc-1 )
1087 0 : AddR(argv[0],"Gdraw.Keyboard", argv[++i]);
1088 0 : else if ( strcmp(pt,"-display")==0 && i<argc-1 )
1089 0 : display = argv[++i];
1090 : # if MyMemory
1091 : else if ( strcmp(pt,"-memory")==0 )
1092 : __malloc_debug(5);
1093 : # endif
1094 0 : else if ( strncmp(pt,"-usecairo",strlen("-usecairo"))==0 ) {
1095 0 : if ( strcmp(pt,"-usecairo=no")==0 )
1096 0 : use_cairo = false;
1097 : else
1098 0 : use_cairo = true;
1099 0 : GDrawEnableCairo(use_cairo);
1100 0 : } else if ( strcmp(pt,"-nosplash")==0 )
1101 0 : splash = 0;
1102 0 : else if ( strcmp(pt,"-quiet")==0 )
1103 : /* already checked for this earlier, no need to do it again */;
1104 0 : else if ( strcmp(pt,"-unique")==0 )
1105 0 : unique = 1;
1106 0 : else if ( strcmp(pt,"-forceuihidden")==0 )
1107 0 : cmdlinearg_forceUIHidden = 0;
1108 0 : else if ( strcmp(pt,"-recover")==0 && i<argc-1 ) {
1109 0 : ++i;
1110 0 : if ( strcmp(argv[i],"none")==0 )
1111 0 : recover=0;
1112 0 : else if ( strcmp(argv[i],"clean")==0 )
1113 0 : recover= -1;
1114 0 : else if ( strcmp(argv[i],"auto")==0 )
1115 0 : recover= 1;
1116 0 : else if ( strcmp(argv[i],"inquire")==0 )
1117 0 : recover= 2;
1118 : else {
1119 0 : fprintf( stderr, "Invalid argument to -recover, must be none, auto, inquire or clean\n" );
1120 0 : dousage();
1121 : }
1122 0 : } else if ( strcmp(pt,"-recover=none")==0 ) {
1123 0 : recover = 0;
1124 0 : } else if ( strcmp(pt,"-recover=clean")==0 ) {
1125 0 : recover = -1;
1126 0 : } else if ( strcmp(pt,"-recover=auto")==0 ) {
1127 0 : recover = 1;
1128 0 : } else if ( strcmp(pt,"-recover=inquire")==0 ) {
1129 0 : recover = 2;
1130 0 : } else if ( strcmp(pt,"-docs")==0 )
1131 0 : dohelp();
1132 0 : else if ( strcmp(pt,"-help")==0 )
1133 0 : dousage();
1134 0 : else if ( strcmp(pt,"-version")==0 || strcmp(pt,"-v")==0 || strcmp(pt,"-V")==0 )
1135 0 : doversion(FONTFORGE_MODTIME_STR);
1136 0 : else if ( strcmp(pt,"-quit")==0 )
1137 0 : quit_request = true;
1138 0 : else if ( strcmp(pt,"-home")==0 )
1139 : /* already did a chdir earlier, don't need to do it again */;
1140 : #if defined(__Mac)
1141 : else if ( strncmp(pt,"-psn_",5)==0 ) {
1142 : /* OK, I don't know what _-psn_ means, but to GW it means */
1143 : /* we've been started on the mac from the FontForge.app */
1144 : /* structure, and the current directory was (shudder) "/" */
1145 : /* (however, we changed to HOME earlier in main routine). */
1146 : unique = 1;
1147 : listen_to_apple_events = true; // This has been problematic on Mavericks and later.
1148 : }
1149 : #endif
1150 : }
1151 :
1152 0 : ensureDotFontForgeIsSetup();
1153 : #if defined(__MINGW32__) && !defined(_NO_LIBCAIRO)
1154 : //Load any custom fonts for the user interface
1155 : if (use_cairo) {
1156 : char *system_load = getGResourceProgramDir();
1157 : char *user_load = getFontForgeUserDir(Data);
1158 : char lbuf[MAX_PATH];
1159 : int lret;
1160 :
1161 : if (system_load != NULL) {
1162 : //Follow the FontConfig APPSHAREFONTDIR location
1163 : lret = snprintf(lbuf, MAX_PATH, "%s/../share/fonts", system_load);
1164 : if (lret > 0 && lret < MAX_PATH) {
1165 : WinLoadUserFonts(lbuf);
1166 : }
1167 : }
1168 : if (user_load != NULL) {
1169 : lret = snprintf(lbuf, MAX_PATH, "%s/%s", user_load, "ui-fonts");
1170 : if (lret > 0 && lret < MAX_PATH) {
1171 : WinLoadUserFonts(lbuf);
1172 : }
1173 : free(user_load);
1174 : }
1175 : }
1176 : #endif
1177 0 : InitImageCache(); // This is in gtextinfo.c. It zeroes imagecache for us.
1178 0 : atexit(&ClearImageCache); // We register the destructor, which is also in gtextinfo.c.
1179 0 : GDrawCreateDisplays(display,argv[0]);
1180 0 : atexit(&GDrawDestroyDisplays); // We register the destructor so that it runs even if we call exit without finishing this function.
1181 0 : default_background = GDrawGetDefaultBackground(screen_display);
1182 0 : InitToolIconClut(default_background);
1183 0 : InitToolIcons();
1184 0 : InitCursors();
1185 :
1186 : /**
1187 : * we have to do a quick sniff of argv[] here to see if the user
1188 : * wanted to skip loading these python init files.
1189 : */
1190 0 : for ( i=1; i<argc; ++i ) {
1191 0 : char *pt = argv[i];
1192 :
1193 0 : if ( !strcmp(pt,"-SkipPythonInitFiles")) {
1194 0 : ProcessPythonInitFiles = 0;
1195 : }
1196 : }
1197 :
1198 : #ifndef _NO_PYTHON
1199 : /*# ifndef GWW_TEST*/
1200 0 : FontForge_InitializeEmbeddedPython(); /* !!!!!! debug (valgrind doesn't like python) */
1201 : /*# endif*/
1202 : #endif
1203 :
1204 : #ifndef _NO_PYTHON
1205 0 : if( ProcessPythonInitFiles )
1206 0 : PyFF_ProcessInitFiles();
1207 : #endif
1208 :
1209 : /* the splash screen used not to have a title bar (wam_nodecor) */
1210 : /* but I found I needed to know how much the window manager moved */
1211 : /* the window around, which I can determine if I have a positioned */
1212 : /* decorated window created at the begining */
1213 : /* Actually I don't care any more */
1214 0 : wattrs.mask = wam_events|wam_cursor|wam_bordwidth|wam_backcol|wam_positioned|wam_utf8_wtitle|wam_isdlg;
1215 0 : wattrs.event_masks = ~(1<<et_charup);
1216 0 : wattrs.positioned = 1;
1217 0 : wattrs.cursor = ct_pointer;
1218 0 : wattrs.utf8_window_title = "FontForge";
1219 0 : wattrs.border_width = 2;
1220 0 : wattrs.background_color = 0xffffff;
1221 0 : wattrs.is_dlg = !listen_to_apple_events;
1222 0 : pos.x = pos.y = 200;
1223 0 : pos.width = splashimage.u.image->width;
1224 0 : pos.height = splashimage.u.image->height-56; /* 54 */
1225 0 : GDrawBindSelection(NULL,sn_user1,"FontForge");
1226 0 : if ( unique && GDrawSelectionOwned(NULL,sn_user1)) {
1227 : /* Different event handler, not a dialog */
1228 0 : wattrs.is_dlg = false;
1229 0 : splashw = GDrawCreateTopWindow(NULL,&pos,request_e_h,NULL,&wattrs);
1230 0 : PingOtherFontForge(argc,argv);
1231 : } else {
1232 0 : if ( quit_request )
1233 0 : exit( 0 );
1234 0 : splashw = GDrawCreateTopWindow(NULL,&pos,splash_e_h,NULL,&wattrs);
1235 : }
1236 :
1237 0 : memset(&rq,0,sizeof(rq));
1238 0 : rq.utf8_family_name = SERIF_UI_FAMILIES;
1239 0 : rq.point_size = 12;
1240 0 : rq.weight = 400;
1241 0 : splash_font = GDrawInstanciateFont(NULL,&rq);
1242 0 : splash_font = GResourceFindFont("Splash.Font",splash_font);
1243 0 : GDrawDecomposeFont(splash_font, &rq);
1244 0 : rq.style = fs_italic;
1245 0 : splash_italic = GDrawInstanciateFont(NULL,&rq);
1246 0 : splash_italic = GResourceFindFont("Splash.ItalicFont",splash_italic);
1247 0 : GDrawSetFont(splashw,splash_font);
1248 :
1249 0 : SplashLayout();
1250 0 : localsplash = splash;
1251 :
1252 0 : if ( localsplash && !listen_to_apple_events )
1253 0 : start_splash_screen();
1254 :
1255 : //
1256 : // The below call will initialize the fontconfig cache if required.
1257 : // That can take a while the first time it happens.
1258 : //
1259 0 : GDrawWindowFontMetrics(splashw,splash_font,&as,&ds,&ld);
1260 0 : fh = as+ds+ld;
1261 :
1262 0 : if ( AutoSaveFrequency>0 )
1263 0 : autosave_timer=GDrawRequestTimer(splashw,2*AutoSaveFrequency*1000,AutoSaveFrequency*1000,NULL);
1264 :
1265 0 : GDrawProcessPendingEvents(NULL);
1266 0 : GDrawSetBuildCharHooks(BuildCharHook,InsCharHook);
1267 :
1268 0 : any = 0;
1269 0 : if ( recover==-1 )
1270 0 : CleanAutoRecovery();
1271 0 : else if ( recover )
1272 0 : any = DoAutoRecoveryExtended( recover-1 );
1273 :
1274 0 : openflags = 0;
1275 0 : for ( i=1; i<argc; ++i ) {
1276 : char buffer[1025];
1277 0 : char *pt = argv[i];
1278 :
1279 0 : GDrawProcessPendingEvents(NULL);
1280 0 : if ( pt[0]=='-' && pt[1]=='-' && pt[2]!='\0')
1281 0 : ++pt;
1282 0 : if ( strcmp(pt,"-new")==0 ) {
1283 0 : FontNew();
1284 0 : any = 1;
1285 : # if HANYANG
1286 : } else if ( strcmp(pt,"-newkorean")==0 ) {
1287 : MenuNewComposition(NULL,NULL,NULL);
1288 : any = 1;
1289 : # endif
1290 0 : } else if ( !strcmp(pt,"-SkipPythonInitFiles")) {
1291 : // already handled above.
1292 0 : } else if ( strcmp(pt,"-last")==0 ) {
1293 0 : if ( next_recent<RECENT_MAX && RecentFiles[next_recent]!=NULL )
1294 0 : if ( ViewPostScriptFont(RecentFiles[next_recent++],openflags))
1295 0 : any = 1;
1296 0 : } else if ( strcmp(pt,"-sync")==0 || strcmp(pt,"-memory")==0 ||
1297 0 : strcmp(pt,"-nosplash")==0 || strcmp(pt,"-recover=none")==0 ||
1298 0 : strcmp(pt,"-recover=clean")==0 || strcmp(pt,"-recover=auto")==0 ||
1299 0 : strcmp(pt,"-dontopenxdevices")==0 || strcmp(pt,"-unique")==0 ||
1300 0 : strncmp(pt,"-usecairo",strlen("-usecairo"))==0 ||
1301 0 : strcmp(pt,"-home")==0 || strcmp(pt,"-quiet")==0
1302 0 : || strcmp(pt,"-forceuihidden")==0 )
1303 : /* Already done, needed to be before display opened */;
1304 0 : else if ( strncmp(pt,"-psn_",5)==0 )
1305 : /* Already done */;
1306 0 : else if ( (strcmp(pt,"-depth")==0 || strcmp(pt,"-vc")==0 ||
1307 0 : strcmp(pt,"-cmap")==0 || strcmp(pt,"-colormap")==0 ||
1308 0 : strcmp(pt,"-keyboard")==0 ||
1309 0 : strcmp(pt,"-display")==0 || strcmp(pt,"-recover")==0 ) &&
1310 0 : i<argc-1 )
1311 0 : ++i; /* Already done, needed to be before display opened */
1312 0 : else if ( strcmp(pt,"-allglyphs")==0 )
1313 0 : openflags |= of_all_glyphs_in_ttc;
1314 0 : else if ( strcmp(pt,"-open")==0 )
1315 0 : doopen = true;
1316 : else {
1317 0 : printf("else argv[i]:%s\n", argv[i] );
1318 0 : if ( strstr(argv[i],"://")!=NULL ) { /* Assume an absolute URL */
1319 0 : strncpy(buffer,argv[i],sizeof(buffer));
1320 0 : buffer[sizeof(buffer)-1]= '\0';
1321 : } else
1322 0 : GFileGetAbsoluteName(argv[i],buffer,sizeof(buffer));
1323 0 : if ( GFileIsDir(buffer) || (strstr(buffer,"://")!=NULL && buffer[strlen(buffer)-1]=='/')) {
1324 : char *fname;
1325 0 : fname = malloc(strlen(buffer)+strlen("/glyphs/contents.plist")+1);
1326 0 : strcpy(fname,buffer); strcat(fname,"/glyphs/contents.plist");
1327 0 : if ( GFileExists(fname)) {
1328 : /* It's probably a Unified Font Object directory */
1329 0 : free(fname);
1330 0 : if ( ViewPostScriptFont(buffer,openflags) )
1331 0 : any = 1;
1332 : } else {
1333 0 : strcpy(fname,buffer); strcat(fname,"/font.props");
1334 0 : if ( GFileExists(fname)) {
1335 : /* It's probably a sf dir collection */
1336 0 : free(fname);
1337 0 : if ( ViewPostScriptFont(buffer,openflags) )
1338 0 : any = 1;
1339 : } else {
1340 0 : free(fname);
1341 0 : if ( buffer[strlen(buffer)-1]!='/' ) {
1342 : /* If dirname doesn't end in "/" we'll be looking in parent dir */
1343 0 : buffer[strlen(buffer)+1]='\0';
1344 0 : buffer[strlen(buffer)] = '/';
1345 : }
1346 0 : fname = GetPostScriptFontName(buffer,false);
1347 0 : if ( fname!=NULL )
1348 0 : ViewPostScriptFont(fname,openflags);
1349 0 : any = 1; /* Even if we didn't get a font, don't bring up dlg again */
1350 0 : free(fname);
1351 : }
1352 : }
1353 0 : } else if ( ViewPostScriptFont(buffer,openflags)!=0 )
1354 0 : any = 1;
1355 : }
1356 : }
1357 0 : if ( !any && !doopen )
1358 0 : any = ReopenLastFonts();
1359 :
1360 0 : collabclient_ensureClientBeacon();
1361 0 : collabclient_sniffForLocalServer();
1362 : #ifndef _NO_PYTHON
1363 0 : PythonUI_namedpipe_Init();
1364 : #endif
1365 :
1366 : #if defined(__Mac)
1367 : if ( listen_to_apple_events ) {
1368 : install_apple_event_handlers();
1369 : install_mac_timer();
1370 : setup_cocoa_app();
1371 :
1372 :
1373 : // WARNING: See declaration of RunApplicationEventLoop() above as to
1374 : // why you might not want to call that function anymore.
1375 : // RunApplicationEventLoop();
1376 :
1377 : } else
1378 : #endif
1379 0 : if ( doopen || !any )
1380 0 : _FVMenuOpen(NULL);
1381 0 : GDrawEventLoop(NULL);
1382 0 : GDrawDestroyDisplays();
1383 :
1384 : #ifndef _NO_PYTHON
1385 : /*# ifndef GWW_TEST*/
1386 0 : FontForge_FinalizeEmbeddedPython(); /* !!!!!! debug (valgrind doesn't like python) */
1387 : /*# endif*/
1388 : #endif
1389 :
1390 : // These free menu translations, mostly.
1391 0 : BitmapViewFinishNonStatic();
1392 0 : MetricsViewFinishNonStatic();
1393 0 : CharViewFinishNonStatic();
1394 0 : FontViewFinishNonStatic();
1395 :
1396 0 : ClearImageCache(); // This frees the contents of imagecache.
1397 0 : hotkeysSave();
1398 0 : LastFonts_Save();
1399 :
1400 : #ifndef _NO_LIBUNICODENAMES
1401 : uninm_names_db_close(names_db); /* close this database before exiting */
1402 : uninm_blocks_db_close(blocks_db);
1403 : #endif
1404 :
1405 0 : lt_dlexit();
1406 :
1407 0 : return( 0 );
1408 : }
|