/* ui_main.c: user interface main window */ #include #include #include #include #include #include "config.h" #include "ui.h" #include "uit.h" #include "defaults.h" #include "render.h" #include "canvas.h" #include "batch.h" #include "error.h" #include "options.h" #include "camera.h" #include "ipc.h" /* #define EXTERNAL_CANVAS_TESTING */ /******************** local interface related variables **********************/ static CANVAS_TYPE canvastype = INTEGRATED_CANVAS; int batch_processing = FALSE; int batch_quit_at_end = FALSE; static Window extern_canvas_window = 0; static Dimension hres = 0, vres = 0; static char *canvas_dpyname = NULL; static Display *canvas_display; /****************** global declarations concerning the GUI ********************/ /* the program detects the best X visual to use and creates a suitables * standard colormap to use with it automatically. The way to do this depends * on the graphics library being used, and is coded in render.c */ XVisualInfo best_visual; Colormap cmap; /* the X visual info is not correctly passed from parent to sub menus * with Motif 1.2, so we set it explicitely ourselves when creating * the submenus */ Arg visargs[3]; int nrvisargs; /* X application context and some important widgets */ XtAppContext app_context; Widget topLevel=0, canvas; Display *display; /* current directory needed when loading MGF files. */ char *current_directory; /* fallback resources ... in ui_defaults.c (automatically generated) */ extern char *fallback_resources[]; static void ProcessWaitingExternalCanvasEvents(void) { if (canvastype == EXTERNAL_CANVAS) { while (XPending(canvas_display)) { XEvent event; XNextEvent(canvas_display, &event); ExternalCanvasEvent(&event); } } } /* Processes pending X events */ void ProcessWaitingEvents(void) { if (Ipc.have_ipc) IpcCheckForMessages(); if (!batch_processing || canvastype == INTEGRATED_CANVAS) { XtInputMask mask; while ((mask = XtAppPending(app_context))) XtAppProcessEvent(app_context, mask); } ProcessWaitingExternalCanvasEvents(); } /* Called during radiance computations - checks for events (like the user clicking * on some button, or moving the mouse while holding one of the mouse buttons ... ) * makes the program behave much more flexible */ void CheckForEvents(void) { clock_t lastt = 0; if (batch_processing && canvastype == OFFSCREEN_CANVAS) return; /* no GUI, so don't even bother calling clock() ... */ /* checking for events too often slows down the computations too much ... * we check only once each second. */ if (clock() - lastt < CLOCKS_PER_SEC) return; ProcessWaitingEvents(); lastt = clock(); } /* returns TRUE if the program thinks it is working and FALSE if it * thinks it is not working. The program thinks it is working when the * current canvas mode is CANVASMODE_WORKING, see canvas.h. This * routine is used for e.g. warning the user about options that can't be * changed until the computations are interrupted. */ int Working(void) { return CanvasGetMode() == CANVASMODE_WORKING; } void SetWindowTitle(char *title) { if (topLevel) SetDialogTitle(topLevel, title); } /************************** interface options ********************************/ static void BatchOption(void *value) { batch_processing = TRUE; } static void BatchQuitOption(void *value) { batch_quit_at_end = TRUE; } static void OffscreenOption(void *value) { canvastype = OFFSCREEN_CANVAS; } static void CanvasIdOption(void *value) { extern_canvas_window = *(int *)value; canvastype = EXTERNAL_CANVAS; } static void CloseCanvasDisplay(void) { XCloseDisplay(canvas_display); } static void CanvasDisplayOption(void *value) { canvas_dpyname = *(char **)value; } static void HResOption(void *value) { hres = *(int *)value; } static void VResOption(void *value) { vres = *(int *)value; } #ifdef EXTERNAL_CANVAS_TESTING /* serves for external canvas testing */ static void CreateTestCanvas(void *value) { Display *dpy; Window win; dpy = XOpenDisplay(NULL); if (RenderGetVisualInfoAndColormap(DefaultScreenOfDisplay(dpy), &best_visual, &cmap)) { XSetWindowAttributes swattr; swattr.background_pixmap = None; swattr.background_pixel = 0; swattr.border_pixmap = CopyFromParent; swattr.border_pixel = 0; swattr.backing_store = NotUseful; swattr.colormap = cmap; win = XCreateWindow(dpy, DefaultRootWindow(dpy), 0, 0, 640, 480, 0, best_visual.depth, InputOutput, best_visual.visual, CWBackPixel | CWBorderPixel | CWBackingStore | CWColormap, &swattr); } else { win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0, 640 ,480, 0, 0, 0); } XSelectInput(dpy, win, KeyReleaseMask); XMapWindow(dpy, win); fprintf(stderr, "win = %ld\n", win); while (1) { XEvent event; XNextEvent(dpy, &event); switch (event.type) { case KeyRelease: exit(0); default: break; } } } #endif static void ThreeButtonMotionOption(void *value) { twobutton_motion = FALSE; } static void TwoButtonMotionOption(void *value) { twobutton_motion = TRUE; } static CMDLINEOPTDESC interfaceOptions[] = { {"-batch", 3, TYPELESS, NULL, BatchOption, "-batch \t\t: non-interactive processing"}, {"-batch-quit-at-end", 8, TYPELESS, NULL, BatchQuitOption, "-batch-quit-at-end\t: (batch mode) quit program at end"}, {"-offscreen", 3, TYPELESS, NULL, OffscreenOption, "-offscreen \t\t: render into offscreen window"}, {"-canvas-window", 3, Tint, NULL, CanvasIdOption, "-canvas-window \t: render into external window with given id"}, {"-canvas-display", 9, Tstring, NULL, CanvasDisplayOption, "-canvas-display \t: display name of external canvas window"}, {"-hres", 3, Tint, NULL, HResOption, "-hres \t\t: horizontal resolution (private canvas only)"}, {"-vres", 3, Tint, NULL, VResOption, "-vres \t\t: vertical resolution (private canvas only)"}, #ifdef EXTERNAL_CANVAS_TESTING {"-create-test-canvas", 3, TYPELESS, NULL, CreateTestCanvas, "-create-test-canvas\t: for external canvas testing"}, #endif {"-2button-motion", 3, TYPELESS, NULL, TwoButtonMotionOption, "-2button-motion\t: camera manipulation using 2 mouse buttons"}, {"-3button-motion", 3, TYPELESS, NULL, ThreeButtonMotionOption, "-3button-motion\t: camera manipulation using 3 mouse buttons"}, {NULL , 0, TYPELESS, NULL, DEFAULT_ACTION, NULL} }; void InterfaceDefaults(void) { twobutton_motion = FALSE; } void ParseInterfaceOptions(int *argc, char **argv) { ParseOptions(interfaceOptions, argc, argv); ParseBatchOptions(argc, argv); } void PrintInterfaceOptions(FILE *fp) { fprintf(fp, "\nInterface options:\n"); PrintOptions(fp, interfaceOptions); PrintBatchOptions(fp); } /*************************** menubar ******************************************/ /* creates the submenus in the main menu bar */ static void CreateMainMenu(Widget menuBar) { CreateFileMenu(menuBar); CreateRadianceMenu(menuBar); CreateRenderMenu(menuBar); CreateRayTracingMenu(menuBar); CreateToneMappingMenu(menuBar); CreateCameraMenu(menuBar); #ifdef GIDEBUG CreateDebugMenu(menuBar); #endif CreateHelpMenu(menuBar); } /* work procedure for batch processing */ static Boolean StartBatchProcessing(XtPointer client_data) { while (!RenderInitialized()) ProcessWaitingEvents(); RenderScene(); Batch(); if (batch_quit_at_end) exit(0); switch (canvastype) { case OFFSCREEN_CANVAS: exit(0); case EXTERNAL_CANVAS: while (1) { XEvent event; if (XCheckMaskEvent(canvas_display, ~NoEventMask, &event)) ExternalCanvasEvent(&event); if (Ipc.have_ipc) IpcCheckForMessages(); usleep(100000); } default: /* do nothing */ break; } return True; } void StartUserInterface(int *argc, char **argv) { Widget mainWindow, menuBar; XSetWindowAttributes wattrs; if (!batch_processing || canvastype == INTEGRATED_CANVAS) { /* Create a toplevel widget, handle options ... */ topLevel = XtVaAppInitialize(&app_context, /* application context */ APP_CLASS_NAME, /* application class name */ NULL, 0, /* command line option list */ argc, argv, /* command line args */ fallback_resources, /* used when the application * defaults file is missing. */ NULL); /* terminate varargs list */ XmRepTypeInstallTearOffModelConverter(); display = XtDisplay(topLevel); } else display = (Display *)NULL; /* All options should have disappeared from argv now. */ if (*argc > 1) { if (*argv[1] == '-') Error(NULL, "Unrecognized option '%s'", argv[1]); else ReadFile(argv[1]); } /* obtain the XVisualInfo and Colormap to use for rendering with the * graphics library. If a non-default visual and colormap is needed, * fill in the visualid, depth and colomrap in the visarg argument list, * so the visual can be set on each widget whenever needed. Failing to do so * will lead to BadMatch errors from the X server. */ if (canvastype == INTEGRATED_CANVAS && RenderGetVisualInfoAndColormap(XtScreen(topLevel), &best_visual, &cmap)) { /* Set the visual, depth, and colormap for the shell - as a matter of fact, these things * have to be passed to all routines creating popup shells and dialogs */ XtSetArg(visargs[0], XtNvisual, best_visual.visual); XtSetArg(visargs[1], XtNdepth, best_visual.depth); XtSetArg(visargs[2], XtNcolormap, cmap); nrvisargs = 3; XtSetValues(topLevel, visargs, nrvisargs); } else nrvisargs = 0; if (!batch_processing) { /* get user config resources */ LoadUserOptions(); /* create main window */ mainWindow = CreateForm(topLevel, "mainWindow"); /* menubar on top of the main window */ menuBar = CreateMenuBar(mainWindow, "menuBar"); /* create the menus in the menubar */ CreateMainMenu(menuBar); XtManageChild(menuBar); } else mainWindow = topLevel; /* create the window in which to render (canvas window) */ switch (canvastype) { case INTEGRATED_CANVAS: canvas = CreateIntegratedCanvasWindow(mainWindow, hres, vres); if (twobutton_motion && !batch_processing) { /* create canvas popup menu, activated with third mouse button. */ CreateCanvasPopupMenu(); } break; case EXTERNAL_CANVAS: canvas_display = XOpenDisplay(canvas_dpyname); if (!canvas_display) Fatal(1, NULL, "Couldn't open connection to display '%s'", canvas_dpyname ? canvas_dpyname : "(null)"); else atexit(CloseCanvasDisplay); /* make sure the canvas displayc onnection is closed at exit */ ConnectExternalCanvasWindow(canvas_display, extern_canvas_window); break; case OFFSCREEN_CANVAS: if (hres <= 0) hres = 640; /* these default values should preferably be */ if (vres <= 0) vres = 480; /* extracted from the Xt resource database. */ CreateOffscreenCanvasWindow(hres, vres); break; default: Fatal(-1, "StartUserInterface", "Invalid canvas type %d", canvastype); } if (!batch_processing) { XtManageChild(mainWindow); } if (!batch_processing || canvastype == INTEGRATED_CANVAS) { /* realize all widgets - bring them on the screen */ XtRealizeWidget(topLevel); } if (canvastype == INTEGRATED_CANVAS) { /* set the backing store attribuut of the canvas window to NotUseful */ wattrs.backing_store = NotUseful; XChangeWindowAttributes(XtDisplay(canvas), XtWindow(canvas), CWBackingStore, &wattrs); #ifdef EXTERNAL_CANVAS_TESTING fprintf(stderr, "Canvas Window ID: %ld\n", XtWindow(canvas)); #endif } /* initialize the canvas widget */ CanvasInit(); if (batch_processing) { if (canvastype == INTEGRATED_CANVAS) XtAppAddWorkProc(app_context, StartBatchProcessing, NULL); else { StartBatchProcessing((XtPointer)NULL); exit(0); } } /* infinte loop catching and handling events */ if (canvastype == EXTERNAL_CANVAS) { /* we use polling since we need to handle events from two displays * simultaneously. */ while (1) { ProcessWaitingEvents(); usleep(50000); } } else XtAppMainLoop(app_context); }