/* +-------------------------------------------------------------------+ */ /* | Copyright 1992, 1993, David Koblas (koblas@netcom.com) | */ /* | Copyright 1995, 1996 Torsten Martinsen (bullestock@dk-online.dk) | */ /* | | */ /* | Permission to use, copy, modify, and to distribute this software | */ /* | and its documentation for any purpose is hereby granted without | */ /* | fee, provided that the above copyright notice appear in all | */ /* | copies and that both that copyright notice and this permission | */ /* | notice appear in supporting documentation. There is no | */ /* | representations about the suitability of this software for | */ /* | any purpose. this software is provided "as is" without express | */ /* | or implied warranty. | */ /* | | */ /* +-------------------------------------------------------------------+ */ /* $Id: main.c,v 1.17 2005/03/20 20:15:32 demailly Exp $ */ #define DEFAULT_GEOMETRY "640x480" #ifdef __VMS #define XtDisplay XTDISPLAY #define XtScreen XTSCREEN #define XtWindow XTWINDOW #endif #include #include #include #include #include #include #include #ifndef NOSTDHDRS #include #include #endif #include #define DEFINE_GLOBAL #include "xpaint.h" #include "Paint.h" #include "misc.h" #include "operation.h" #include "graphic.h" #include "messages.h" #include "protocol.h" #include "image.h" #include "rw/rwTable.h" #include "XPaintIcon.xpm" String *helpText; int helpNum; String *msgText; int msgNum; char lang[10]; char *routine; extern void BrushInit(Widget toplevel); static char *appDefaults[] = { #include "XPaint.ad.h" NULL }; typedef struct { String visualType; String lang; String size; String winsize; String help; String shareDir; String rcFile; String helpFile; String msgFile; int zoom; Boolean fullpopup; Boolean nomenubar; Boolean popped; Boolean snapshot; Boolean clipboard; Boolean horizontal; Boolean nowarn; } AppInfo; static AppInfo appInfo; static XtResource resources[] = { {"visualType", "VisualType", XtRString, sizeof(String), XtOffset(AppInfo *, visualType), XtRImmediate, (XtPointer) "default"}, {"lang", "Lang", XtRString, sizeof(String), XtOffset(AppInfo *, lang), XtRImmediate, (XtPointer) NULL}, {"size", "Size", XtRString, sizeof(String), XtOffset(AppInfo *, size), XtRImmediate, (XtPointer) DEFAULT_GEOMETRY}, {"winsize", "WinSize", XtRString, sizeof(String), XtOffset(AppInfo *, winsize), XtRImmediate, (XtPointer) "0x0"}, {"shareDir", "ShareDir", XtRString, sizeof(String), XtOffset(AppInfo *, shareDir), XtRImmediate, (XtPointer) NULL}, {"rcFile", "RcFile", XtRString, sizeof(String), XtOffset(AppInfo *, rcFile), XtRImmediate, (XtPointer) NULL}, {"help", "Help", XtRString, sizeof(String), XtOffset(AppInfo *, help), XtRImmediate, (XtPointer) NULL}, {"helpFile", "HelpFile", XtRString, sizeof(String), XtOffset(AppInfo *, helpFile), XtRImmediate, (XtPointer) NULL}, {"msgFile", "MsgFile", XtRString, sizeof(String), XtOffset(AppInfo *, msgFile), XtRImmediate, (XtPointer) NULL}, {"zoom", "Zoom", XtRInt, sizeof(int), XtOffset(AppInfo *, zoom), XtRImmediate, (XtPointer) 1}, {"fullpopup", "FullPopup", XtRBoolean, sizeof(Boolean), XtOffset(AppInfo *, fullpopup), XtRImmediate, (XtPointer) True}, {"nomenubar", "NoMenubar", XtRBoolean, sizeof(Boolean), XtOffset(AppInfo *, nomenubar), XtRImmediate, (XtPointer) False}, {"popped", "Popped", XtRBoolean, sizeof(Boolean), XtOffset(AppInfo *, popped), XtRImmediate, (XtPointer) False}, {"snapshot", "Snapshot", XtRBoolean, sizeof(Boolean), XtOffset(AppInfo *, snapshot), XtRImmediate, (XtPointer) False}, {"clipboard", "Clipboard", XtRBoolean, sizeof(Boolean), XtOffset(AppInfo *, clipboard), XtRImmediate, (XtPointer) False}, {"horizontal", "Horizontal", XtRBoolean, sizeof(Boolean), XtOffset(AppInfo *, horizontal), XtRImmediate, (XtPointer) False}, {"nowarn", "NoWarn", XtRBoolean, sizeof(Boolean), XtOffset(AppInfo *, nowarn), XtRImmediate, (XtPointer) False}, }; static XrmOptionDescRec options[] = { {"-24", ".visualType", XrmoptionNoArg, (XtPointer) "true24"}, {"-12", ".visualType", XrmoptionNoArg, (XtPointer) "cmap12"}, {"-language", ".lang", XrmoptionSepArg, (XtPointer) NULL}, {"-size", ".size", XrmoptionSepArg, (XtPointer) NULL}, {"-winsize", ".winsize", XrmoptionSepArg, (XtPointer) "0x0"}, {"-sharedir", ".shareDir", XrmoptionSepArg, (XtPointer) SHAREDIR}, {"-rcfile", ".rcFile", XrmoptionSepArg, (XtPointer) NULL}, {"-helpfile", ".helpFile", XrmoptionSepArg, (XtPointer) NULL}, {"-msgfile", ".msgFile", XrmoptionSepArg, (XtPointer) NULL}, {"-zoom", ".zoom", XrmoptionSepArg, (XtPointer) "-1"}, {"-simplepopup", ".fullpopup", XrmoptionNoArg, (XtPointer) "False"}, {"-fullpopup", ".fullpopup", XrmoptionNoArg, (XtPointer) "True"}, {"-nomenubar", ".nomenubar", XrmoptionNoArg, (XtPointer) "True"}, {"-popped", ".popped", XrmoptionNoArg, (XtPointer) "True"}, {"-snapshot", ".snapshot", XrmoptionNoArg, (XtPointer) "True"}, {"-clipboard", ".clipboard", XrmoptionNoArg, (XtPointer) "True"}, {"-horizontal", ".horizontal", XrmoptionNoArg, (XtPointer) "True"}, {"-nowarn", ".nowarn", XrmoptionNoArg, (XtPointer) "True"}, {"-help", ".help", XrmoptionNoArg, (XtPointer) "command"}, {"+help", ".help", XrmoptionSepArg, (XtPointer) NULL}, {"-8", ".visualType", XrmoptionNoArg, (XtPointer) "cmap8"}, {"-visual", ".visualType", XrmoptionSepArg, (XtPointer) NULL}, }; /* ** Public query functions for application defaults */ void SetDefaultWHZ(int w, int h, int zoom) { char str[40]; if (w<=0) w = 1; if (h<=0) h = 1; if (zoom<=0) zoom = -1; if (zoom>32) zoom = 32; sprintf(str, "%dx%d", w, h); appInfo.size = strdup(str); appInfo.zoom = zoom; } void GetDefaultWH(int *w, int *h) { int x, y; unsigned int width, height; XParseGeometry(appInfo.size, &x, &y, &width, &height); *w = width; *h = height; } void GetPaintWH(int *w, int *h) { int x, y; unsigned int width, height; XParseGeometry(appInfo.winsize, &x, &y, &width, &height); *w = width; *h = height; } void GetInitZoom(int *z) { *z = appInfo.zoom; if (*z<=0) *z = -1; if (*z>32) *z = 32; } void SetMenuBarHidden(Boolean flag) { appInfo.nomenubar = flag; } Boolean ThereIsNoMenuBar() { return appInfo.nomenubar; } void SetFullMenuPopup(Boolean flag) { appInfo.fullpopup = flag; } Boolean IsFullPopup() { return appInfo.fullpopup; } Boolean ToolsAreHorizontal() { return appInfo.horizontal; } char * GetDefaultRC(void) { return appInfo.rcFile; } char * GetShareDir(void) { return appInfo.shareDir; } /* ** Create a nice icon image for XPaint... */ void SetIconImage(Widget w) { static Pixmap icon = None; static int iconW = 1, iconH = 1; Window iconWindow; Screen *screen = XtScreen(w); Display *dpy = XtDisplay(w); XpmAttributes myattributes; /* ** Build the XPaint icon */ iconWindow = XCreateSimpleWindow(dpy, RootWindowOfScreen(screen), 0, 0, /* x, y */ iconW, iconH, 0, BlackPixelOfScreen(screen), BlackPixelOfScreen(screen)); if (icon == None) { myattributes.valuemask = XpmCloseness; myattributes.closeness = 65535; if (XpmCreatePixmapFromData(dpy, iconWindow, XPaintIcon_xpm, &icon, NULL, &myattributes)) { fprintf(stderr, msgText[CANNOT_INSTALL_XPM_ICON]); exit(1); } GetPixmapWHD(dpy, icon, &iconW, &iconH, NULL); XResizeWindow(dpy, iconWindow, iconW, iconH); } XSetWindowBackgroundPixmap(dpy, iconWindow, icon); XtVaSetValues(w, XtNiconWindow, iconWindow, NULL); } /* ** The rest of main */ static void usage(char *prog, char *msg) { if (msg) fprintf(stderr, "%s\n", msg); fprintf(stderr, "%s %s (XPaint %s):\n", msgText[USAGE_FOR], prog, XPAINT_VERSION); HelpTextOutput(stderr, appInfo.help == NULL ? "command" : appInfo.help); exit(1); } typedef struct { XtWorkProcId id; Widget toplevel; int nfiles; int pos; char **files; } LocalInfo; static Boolean workProc(LocalInfo * l) { char *file; void *v; if ((l == NULL) || (l->id == None)) return TRUE; file = l->files[l->pos]; StateSetBusy(True); if ((v = ReadMagic(file)) != NULL) GraphicOpenFile(l->toplevel, file, v); else Notice(l->toplevel, msgText[UNABLE_TO_OPEN_INPUT_FILE], file, RWGetMsg()); StateSetBusy(False); if (++l->pos == l->nfiles) { XtFree((XtPointer) l->files); XtFree((XtPointer) l); return TRUE; } else return FALSE; } /* * You can specify: * the visual * the visual and the depth * the visualID * Any other combinations may not generate the results that you expect * Also note that the visualID is strongly dependent on the platform * being used. We will let the user specify any visualID that they want. */ XVisualInfo GetFutureVisual(Widget *toplevel, int desiredDepth, int desiredVisual, int desiredVisualID, Boolean *valid) { Display *display = XtDisplay(*toplevel); int i, visnum = 0; XVisualInfo rtnval; int visualsMatched; XVisualInfo vTemplate; XVisualInfo visual[50]; /* If a machine has more than 50 visuals then this will fail. Higher end SGI's have 20 so this is probably OK */ XVisualInfo *visualList = visual; *valid=FALSE; vTemplate.screen = DefaultScreen(display); if (desiredVisualID >= 0) visualList = XGetVisualInfo (display, VisualScreenMask, &vTemplate, &visualsMatched); else { vTemplate.class = desiredVisual; if (desiredDepth > 0) { vTemplate.depth = desiredDepth; visualList = XGetVisualInfo(display, VisualClassMask | VisualScreenMask | VisualDepthMask, &vTemplate, &visualsMatched); } else { visualList = XGetVisualInfo(display, VisualClassMask | VisualScreenMask, &vTemplate, &visualsMatched); } } if (visualsMatched && desiredVisualID >= 0) { visnum = -1; /* look through the entire list for our visual ID */ for (i = 0; i < visualsMatched; i++) { if (XVisualIDFromVisual(visualList[i].visual) == desiredVisualID) visnum = i; } if (visnum < 0) visualsMatched=0; } if (visualsMatched == 0) { fprintf(stderr, msgText[NO_MATCH_FOUND_FOR_VISUAL]); exit(1); } rtnval = visualList[visnum]; *valid = TRUE; XFree(visualList); return rtnval; } void LoadMessages() { FILE *fd; int i, j; char namebuf[256]; char buf[256]; static char alert[] = "!! No messages found !!"; static const char failure[] = "Memory allocation failure\n"; static const char msgs_ver_str[] = "# XPaint Messages version "; static const int msgs_ver_str_len = sizeof(msgs_ver_str)/sizeof(char) - 1; helpNum = 0; if (appInfo.helpFile) { if (*appInfo.helpFile!='/' && *appInfo.helpFile!='.') sprintf(namebuf, "%s/%s", appInfo.shareDir, appInfo.helpFile); else strcpy(namebuf, appInfo.helpFile); fd = fopen(namebuf, "r"); } else fd = NULL; if (!fd) { sprintf(namebuf, "%s/help/Help", appInfo.shareDir); fd = fopen(namebuf, "r"); } if (fd) { while (!feof(fd)) { ++helpNum; helpText = (String *)realloc(helpText, helpNum*sizeof(String)); fgets(buf, 250, fd); i = strlen(buf)-1; if (i>0 && buf[i] == '\n') buf[i] = '\0'; helpText[helpNum-1] = strdup(buf); } fclose(fd); } else { fprintf(stderr, "No help file found !!\n"); } msgNum = 0; if (appInfo.msgFile) { if (*appInfo.msgFile!='/' && *appInfo.msgFile!='.') sprintf(namebuf, "%s/%s", appInfo.shareDir, appInfo.msgFile); else strcpy(namebuf, appInfo.msgFile); fd = fopen(namebuf, "r"); } else fd = NULL; if (!fd) { sprintf(namebuf, "%s/messages/Messages", appInfo.shareDir); fd = fopen(namebuf, "r"); } if (fd) { int version_checked = 0; while (!feof(fd)) { fgets(buf, 250, fd); if (*buf == '#') { /* GRR 20050806: Avoid version incompatibilities by requiring * version number of runtime Messages file to match that with * which we were compiled. Note that it need not necessarily * match XPAINT_VERSION, however. */ if (!strncmp(buf, msgs_ver_str, msgs_ver_str_len)) { char *ptr = buf + msgs_ver_str_len; int last = strlen(ptr) - 1; if (last >= 0 && ptr[last] == '\n') ptr[last--] = '\0'; while (last >= 0 && isspace(ptr[last])) ptr[last--] = '\0'; if (strcmp(ptr, MESSAGES_VERSION)) { fprintf(stderr, "Error: %s is version %s; expected " "version %s\n", namebuf, ptr, MESSAGES_VERSION); exit(1); } version_checked = 1; } if (!strncmp(buf+1, "END_MESSAGES", 12)) break; continue; } if (!version_checked) { fprintf(stderr, "Error: %s is version 2.7.7 or earlier; " "expected version %s\n", namebuf, MESSAGES_VERSION); exit(1); } ++msgNum; msgText = (String *)realloc(msgText, msgNum*sizeof(String)); if (!msgText) { fprintf(stderr, failure); exit(1); } i = 0; j = 0; while (buf[i]) { if (buf[i] == '\n') break; if (buf[i] == '\\' && buf[i+1] == 'n') { ++i; buf[j] = '\n'; } else if (j= 4) break; } fclose(fd); } } } int main(int argc, char *argv[]) { Display *dpy; Widget toplevel; XtAppContext app; char xpTitle[20]; int i; XEvent event; Arg args[5]; int nargs = 0; XrmDatabase rdb; Boolean isIcon; Widget temp; int desiredVisual = -1; XVisualInfo vis; Boolean valid; int depth; int desiredDepth; int tempargc; char **tempargv; char phrase[128]; String app_def_file; int desiredVisualID = -1; InitTypeConverters(); msgText = NULL; helpText = NULL; tempargc = argc; tempargv = xmalloc(argc * sizeof(char *)); *lang = '\0'; for (i = 0; i < argc; i++) { tempargv[i] = xmalloc(strlen(argv[i])+1); if (i < argc-1 && !strncasecmp(argv[i], "-language", strlen(argv[i]))) strncpy(lang, argv[i+1], 8); strcpy(tempargv[i], argv[i]); } app_def_file = GetAppDefaultFile(); if (!app_def_file) { fprintf(stderr, "%s : %s\n", "Can't find app_defaults file", app_def_file); exit(1); } /* * Create a temp widget that we can interrogate. * This widget will not be realized. * There should be a better way to do this... */ temp = XtAppInitialize(&Global.appContext, app_def_file, options, XtNumber(options), &tempargc, tempargv, appDefaults, args, nargs); XtGetApplicationResources(temp, (XtPointer) & appInfo, resources, XtNumber(resources), NULL, 0); /* ** Load help and messages files */ LoadMessages(); rdb = XtDatabase(XtDisplay(temp)); for (i = 0; i < tempargc; i++) free(tempargv[i]); if (appInfo.help != NULL) usage(argv[0], NULL); desiredDepth = 0; if (strcmp(appInfo.visualType, "default") != 0) { char *cp = appInfo.visualType; char *vp = phrase; /* this includes the terminating null character */ for (i = 0; i <= strlen(cp); i++) phrase[i] = tolower(cp[i]); if (strncmp(vp, "cmap", 4) == 0) { XrmPutStringResource(&rdb, "Canvas*visual", "PseudoColor"); desiredVisual = PseudoColor; vp += 4; } else if (strncmp(vp, "directcolor", 11) == 0) { XrmPutStringResource(&rdb, "Canvas*visual", "DirectColor"); desiredVisual = DirectColor; desiredDepth = 24; vp += 11; } else if (strncmp(vp, "truecolor", 9) == 0) { XrmPutStringResource(&rdb, "Canvas*visual", "TrueColor"); desiredVisual = TrueColor; desiredDepth = 24; vp += 9; } else if (strncmp(vp, "grayscale", 9) == 0) { XrmPutStringResource(&rdb, "Canvas*visual", "GrayScale"); desiredVisual = GrayScale; desiredDepth = 8; vp += 9; } else if (strncmp(vp, "pseudocolor", 11) == 0) { XrmPutStringResource(&rdb, "Canvas*visual", "PseudoColor"); desiredVisual = PseudoColor; vp += 11; } else if (strncmp(vp, "staticgray", 10) == 0) { XrmPutStringResource(&rdb, "Canvas*visual", "StaticGray"); desiredVisual = StaticGray; desiredDepth = 8; vp += 10; } else if (strncmp(vp, "staticcolor", 11) == 0) { XrmPutStringResource(&rdb, "Canvas*visual", "StaticColor"); desiredVisual = StaticColor; desiredDepth = 8; vp += 11; } else if (strncmp(vp, "true", 4) == 0) { XrmPutStringResource(&rdb, "Canvas*visual", "TrueColor"); desiredVisual = TrueColor; vp += 4; } else if (strncmp(vp, "gray", 4) == 0) { XrmPutStringResource(&rdb, "Canvas*visual", "StaticGray"); desiredVisual = StaticGray; vp += 4; } else if (atoi(vp)>0 || (vp[0]=='0' && vp[1]=='\0')) { desiredVisualID = atoi(vp); *vp = '\0'; } else { fprintf(stderr, msgText[BAD_VISUAL_TYPE_SPECIFICATION], argv[0], vp); exit(1); } if (*vp != '\0') { desiredDepth=atoi(vp); if (desiredDepth > 0) XrmPutStringResource(&rdb, "Canvas*depth", vp); else desiredDepth = 0; } } else { /* go find out what the default visual is and get its parameters */ Global.vis.depth = DefaultDepth(XtDisplay(temp), DefaultScreen(XtDisplay(temp))); Global.vis.visual = DefaultVisual(XtDisplay(temp), DefaultScreen(XtDisplay(temp))); } if (desiredVisual >= 0 || desiredVisualID >= 0) { vis = GetFutureVisual(&temp, desiredDepth, desiredVisual, desiredVisualID, &valid); if (valid) { depth = vis.depth; Global.vis = vis; } else { fprintf(stderr, msgText[NO_MATCH_FOUND_FOR_VISUAL]); exit(1); } } else { Global.vis.depth = DefaultDepth(XtDisplay(temp), DefaultScreen(XtDisplay(temp))); Global.vis.visual = DefaultVisual(XtDisplay(temp), DefaultScreen(XtDisplay(temp))); } sprintf(phrase, "%d", Global.vis.depth); XrmPutStringResource(&rdb, "Canvas*depth", phrase); /* destroy our temporary widget */ XtDestroyWidget(temp); /* ** Create the application context */ toplevel = XtAppInitialize(&Global.appContext, app_def_file, options, XtNumber(options), &argc, argv, appDefaults, args, nargs); XtGetApplicationResources(toplevel, (XtPointer) & appInfo, resources, XtNumber(resources), NULL, 0); if (argc != 1 && argv[1][0] == '-') usage(argv[0], "Invalid option"); checkRCsize(); /* ** A little initialization */ Global.toplevel = toplevel; Global.canvas = None; Global.back = None; Global.patternshell = None; Global.patterninfo = NULL; Global.region.image = NULL; Global.region.cmap = None; Global.region.width = 0; Global.region.height = 0; Global.region.pix = None; Global.region.mask = None; Global.nbrushes = 0; Global.brushes = NULL; Global.join = JoinMiter; Global.escape = False; Global.dashlist[0] = '\0'; Global.dashnumber = 0; Global.dashoffset = 0; Global.numregions = 0; Global.transparent = 0; Global.regiondata = NULL; /* ** */ XtVaGetValues(toplevel, XtNiconic, &isIcon, NULL); if (isIcon) XrmPutStringResource(&rdb, "Canvas.iconic", "on"); /* ** GRR 960525: check depth and warn user (use AlertBox() instead?) */ if (!appInfo.nowarn && !appInfo.snapshot) { int depth = Global.vis.depth; /* XtVaGetValues(toplevel, XtNdepth, &depth, NULL); */ HelpTextOutput(stderr, "data_loss"); fprintf(stderr, msgText[YOUR_CANVAS_DEPTH_IS_THAT_MANY_BITS], depth); switch (depth) { case 8: case 12: fprintf(stderr, msgText[SEPARATOR_STRIP_ONE]); fprintf(stderr, msgText[WARNING_ONE]); fprintf(stderr, msgText[SEPARATOR_STRIP_ONE]); fprintf(stderr, "\n"); break; case 1: case 2: case 4: case 6: case 15: case 16: fprintf(stderr, msgText[SEPARATOR_STRIP_TWO]); fprintf(stderr, msgText[WARNING_TWO_A]); fprintf(stderr, msgText[WARNING_TWO_B]); fprintf(stderr, msgText[SEPARATOR_STRIP_TWO]); fprintf(stderr, "\n"); break; case 24: case 32: fprintf(stderr, msgText[WARNING_THREE_A]); fprintf(stderr, msgText[WARNING_THREE_B]); fprintf(stderr, msgText[WARNING_THREE_C]); break; default: break; } } /* ** Now build and construct the widgets */ Global.timeToDie = False; Global.explore = False; app = XtWidgetToApplicationContext(toplevel); dpy = Global.display = XtDisplay(toplevel); /* ** Call the initializers */ OperationInit(toplevel); /* ** A few rogue initializers */ BrushInit(toplevel); HelpInit(toplevel); SRANDOM(time(0)); SetIconImage(toplevel); /* ** GRR 960526: put version number in title string (and icon name?) */ sprintf(xpTitle, "XPaint %s", XPAINT_VERSION); XtVaSetValues(toplevel, XtNtitle, xpTitle, NULL); XtVaSetValues(toplevel, XtNiconName, xpTitle, XtNtitle, xpTitle, NULL); if (appInfo.snapshot) { SnapshotImage(toplevel, NULL, 0); } /* ** Realize it (doesn't hurt) */ XtRealizeWidget(toplevel); /* ** Now open any file on the command line */ if (argc != 1) { if (appInfo.clipboard) { Image * image; for (i = 1; i < argc; i++) { image = ImageFromFile(argv[i]); if (image) ImageToMemory(image); } } else { LocalInfo *l = XtNew(LocalInfo); l->pos = 0; l->toplevel = toplevel; l->nfiles = argc - 1; l->files = (char **) XtCalloc(argc, sizeof(char *)); for (i = 1; i < argc; i++) l->files[i - 1] = argv[i]; l->id = XtAppAddWorkProc(app, (XtWorkProc) workProc, (XtPointer) l); } } else if (appInfo.popped) { /* ** If nothing is coming up, bring up a blank canvas */ GraphicCreate(toplevel, 0); } XtUnmanageChild(Global.bar); XMapWindow(dpy, XtWindow(Global.bar)); XtUnmanageChild(Global.back); XMapWindow(dpy, XtWindow(Global.back)); /* ** MainLoop */ do { XtAppNextEvent(app, &event); if (event.type == ButtonPress || event.type == ButtonRelease) Global.currentTime = event.xbutton.time; XtDispatchEvent(&event); } while (!Global.timeToDie); return 0; }