/* $TOG: panner.c /main/6 1997/03/31 13:38:32 dbl $ */ /* * @OPENGROUP_COPYRIGHT@ * COPYRIGHT NOTICE * Copyright (c) 1990, 1991, 1992, 1993 Open Software Foundation, Inc. * Copyright (c) 1996, 1997, 1998, 1999, 2000 The Open Group * ALL RIGHTS RESERVED (MOTIF). See the file named COPYRIGHT.MOTIF for * the full copyright text. * * This software is subject to an open license. It may only be * used on, with or for operating systems which are themselves open * source systems. You must contact The Open Group for a license * allowing distribution and sublicensing of this software on, with, * or for operating systems which are not Open Source programs. * * See http://www.opengroup.org/openmotif/license for full * details of the license agreement. Any use, reproduction, or * distribution of the program constitutes recipient's acceptance of * this agreement. * * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS * PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY * WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY * OR FITNESS FOR A PARTICULAR PURPOSE * * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT * NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE * EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * */ /* * HISTORY */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define INIT_SCREEN_WIDTH 50 #define MAX_DISPLAY_COUNT 10 #define LOCAL 0 #define PAN_FORMAT 8 #define PAN_SIZE 9 #define WM_SELECTION_FORMAT "WM_S%1d" #define COLOR_COUNT 20 String fallback[] = { "Panner.mappedWhenManaged: FALSE", "Panner.width: 325", "Panner.height: 290", "Panner*frame.marginWidth: 7", "Panner*frame.marginHeight: 7", "Panner*notebook.firstPageNumber: 0", "Panner*tab.shadowThickness: 1", "Panner*canvas.background: grey40", "Panner*canvas.foreground: yellow", /* Menu entry definitions */ "Panner*cascade1.labelString: File", "Panner*cascade2.labelString: Display", "Panner*cascade3.labelString: Help", "Panner*b1.labelString: Quit", "Panner*b2.labelString: Update", "Panner*b3.labelString: New Display...", "Panner*promptDlog*selectionLabelString: Display name:", "Panner*messageDlog*messageString:\ Panner Demo\\n-----------\\nGrab and move dashed box to pan display.\\n\ Use 'New Display...' command to add another display.\\n\ Select displays from tabs in the notebook.", "Panner*warningDlog*messageString:\ The panner window is not pinned!\\n\ Add the line:\\n\\n \"Mwm*Panner*ClientPinned: True\"\\n\\n\ to your .Xdefaults file and restart Mwm.", NULL }; typedef struct _PannerInfoRec { Display *display; Screen *screen; Widget shell; Widget utmShell; /* drawing area used for UTM */ Widget canvas; int thumbX, thumbY; unsigned int thumbW, thumbH; int lastEventX, lastEventY; Atom WM; /* need selections on the correct display */ Atom WM_PAN; Atom WM_GOTO; Atom WM_PAN_POS; } PannerInfoRec; typedef struct _PanPostion { long x; long y; } PanPosition; typedef enum { MENU_QUIT, MENU_UPDATE, MENU_NEW, MENU_HELP } MenuFunction; typedef enum { UNKNOWN, VERIFYING, VERIFIED } PinState; /* * globals. */ XtAppContext app; unsigned short DSP; /* index of active display */ Widget notebook; GC thumbGC, canvasGC; XContext context; PannerInfoRec *pInfoList; unsigned long cells[COLOR_COUNT]; PinState pinnedState = UNKNOWN; int origX, origY; #ifdef X_ONLY Boolean LOCK = False; #endif static void OpenNewDisplay(String, Widget, PannerInfoRec *); static void UpdatePannerCB (Widget, XtPointer, XtPointer); static void ChangePageCB (Widget, XtPointer, XtPointer); static void DoAddDisplayCB (Widget, XtPointer, XtPointer); static void DestinationCB (Widget, XtPointer, XtPointer); static void DoneMoveScreenCB (Widget, XtPointer, XtPointer); static void WatchForWindowPanning (Display *dsp); static void HandlePropertyChange (XEvent *event); static void UpdatePannerView (PannerInfoRec *pInfoList, int remoteDsp); static void DrawWindows (PannerInfoRec *); static void DrawThumb (PannerInfoRec *); static void SetupColorsAndGCs(); static GC GetXorGC (Widget); static GC GetCanvasGC (Widget); static void SetWindowColor (PannerInfoRec *, int); static void TranslateCoordinates (PannerInfoRec *, int, int, unsigned int, unsigned int, int*,int*,unsigned int*,unsigned int*); static int IgnoreError (Display *dsp, XErrorEvent *event); static void StartTracking (Widget, XtPointer, XEvent *, Boolean *); static void DoTracking (Widget, XtPointer, XEvent *, Boolean *); static void StopTracking (Widget, XtPointer, XEvent *, Boolean *); static void MoveScreen (PannerInfoRec *, int, int, Time); static XtPointer PackCARD32 (XtPointer, CARD32); static XtPointer PackCARD16 (XtPointer, CARD16); static XtPointer PackCARD8 (XtPointer, CARD8); static void CreateMenuBar (Widget parent); static void MenuCB (Widget w, XtPointer clientData, XtPointer callData); static void DoUpdatePanner (); static void DoAddDisplay (); static void DoHelp (); static void DoQuit (); static void CheckPinnedState (); static void ShowPinStateWarning (); static void HandleInitialExpose (Widget, XtPointer, XEvent *, Boolean *); /*----------------------------------------------------------------* | main | *----------------------------------------------------------------*/ void main (int argc, char** argv) { Widget mainWin, frame; XEvent event; pInfoList = (PannerInfoRec *) XtMalloc(sizeof(PannerInfoRec) * MAX_DISPLAY_COUNT); for (DSP = 0; DSP < MAX_DISPLAY_COUNT; DSP++) pInfoList[DSP].display = NULL; DSP = LOCAL; pInfoList[LOCAL].shell = XtVaAppInitialize(&app, "Panner", NULL, 0, &argc, argv, fallback, NULL); pInfoList[LOCAL].display = XtDisplay(pInfoList[LOCAL].shell); mainWin = XmCreateMainWindow(pInfoList[LOCAL].shell, "mainWin", NULL, 0); XtManageChild(mainWin); CreateMenuBar(mainWin); frame = XtVaCreateManagedWidget("frame", xmFrameWidgetClass, mainWin, NULL); notebook = XtVaCreateManagedWidget("notebook", xmNotebookWidgetClass, frame, NULL); XtRealizeWidget(pInfoList[LOCAL].shell); context = XUniqueContext (); OpenNewDisplay(/*$DISPLAY*/NULL, notebook, pInfoList); SetupColorsAndGCs(); XtAddCallback(notebook, XmNpageChangedCallback, ChangePageCB, pInfoList); XtAddEventHandler(notebook, ExposureMask, False, HandleInitialExpose, NULL); XtMapWidget(pInfoList[LOCAL].shell); for (;;) { XtAppNextEvent(app, &event); if (event.type == PropertyNotify) HandlePropertyChange(&event); XtDispatchEvent(&event); } } /*----------------------------------------------------------------* | OpenNewDisplay | | - Get new display connection to named display. | | - If display name is not NULL, create shell on the display. | | - Fill in correct record in pInfoList. | *----------------------------------------------------------------*/ static void OpenNewDisplay( String displayName, Widget notebook, PannerInfoRec *pInfoList) { int newDsp = 0; int argc = 0; char **argv = NULL; Dimension canvasW, canvasH; char selectionName[40]; PannerInfoRec *pInfo; Widget tab; XmString tabName; XtCallbackList cbList; /* * If NULL, then the display's already been created. */ if (displayName != NULL) { XtVaGetValues(notebook, XmNlastPageNumber, &newDsp, XmNpageChangedCallback, &cbList, NULL); newDsp++; if ((pInfoList[newDsp].display = XOpenDisplay(displayName)) == NULL) { fprintf(stderr, "ERROR - Can't open display \"%s\".\n", displayName); return; } XtDisplayInitialize(app, pInfoList[newDsp].display, "panner", "Panner", NULL, 0, &argc, argv); /* create an unmapped shell on the remote display */ pInfoList[newDsp].shell = XtVaAppCreateShell( "panner", "Panner", applicationShellWidgetClass, pInfoList[newDsp].display, XmNmappedWhenManaged, False, NULL); XtRealizeWidget(pInfoList[newDsp].shell); } /* * For UTM to work, there must be a drawing area or UTM saavy * widget. * * We must set-up a destination callback function that * does the actual transfer of the parameter info to Mwm. */ pInfoList[newDsp].utmShell = XtVaCreateManagedWidget("utmShell", xmDrawingAreaWidgetClass, pInfoList[newDsp].shell, XmNmappedWhenManaged, False, NULL); XtAddCallback(pInfoList[newDsp].utmShell, XmNdestinationCallback, DestinationCB, &(pInfoList[newDsp])); /* * Initialize the correct record in the pInfoList. */ pInfoList[newDsp].screen = XtScreen(pInfoList[newDsp].shell); /* * setup handler to watch when Mwm changes the root property. * first store some data on the root window. */ XSaveContext(pInfoList[newDsp].display, DefaultRootWindow(pInfoList[newDsp].display), context, (XPointer)(long)newDsp); /* store index into panner info. */ WatchForWindowPanning(pInfoList[newDsp].display); /* * Add another page to the notebook. * First must set size correctly. */ XtVaGetValues(pInfoList[LOCAL].shell, XmNwidth, &canvasW, XmNheight, &canvasH, NULL); pInfoList[newDsp].canvas = XtVaCreateManagedWidget("canvas", xmDrawingAreaWidgetClass, notebook, XmNchildType, XmPAGE, XmNpageNumber, newDsp, XmNwidth, canvasW, XmNheight, canvasH, NULL); XtAddCallback(pInfoList[newDsp].canvas, XmNexposeCallback, UpdatePannerCB, pInfoList); if (displayName == NULL) tabName = XmStringCreate("LOCAL",XmFONTLIST_DEFAULT_TAG); else tabName = XmStringCreate(displayName,XmFONTLIST_DEFAULT_TAG); tab = XtVaCreateManagedWidget("tab", xmPushButtonWidgetClass, notebook, XmNlabelString, tabName, XmNchildType, XmMAJOR_TAB, NULL); XmStringFree(tabName); pInfoList[newDsp].thumbW = INIT_SCREEN_WIDTH; pInfoList[newDsp].thumbH = pInfoList[newDsp].thumbW * HeightOfScreen(pInfoList[newDsp].screen) / WidthOfScreen(pInfoList[newDsp].screen); XtVaGetValues(pInfoList[newDsp].canvas, XmNwidth, &canvasW, XmNheight, &canvasH, NULL); pInfoList[newDsp].thumbX = (int)canvasW/2 - (int)pInfoList[newDsp].thumbW/2; pInfoList[newDsp].thumbY = (int)canvasH/2 - (int)pInfoList[newDsp].thumbH/2; /* Setup the atoms needed to communicate with Mwm. Check screen number! */ sprintf(selectionName, WM_SELECTION_FORMAT, XScreenNumberOfScreen(pInfoList[newDsp].screen)); pInfoList[newDsp].WM = XmInternAtom(pInfoList[newDsp].display, selectionName, False); pInfoList[newDsp].WM_PAN = XmInternAtom(pInfoList[newDsp].display, "_MOTIF_WM_PAN", False); pInfoList[newDsp].WM_GOTO = XmInternAtom(pInfoList[newDsp].display, "_MOTIF_WM_GOTO", False); pInfoList[newDsp].WM_PAN_POS = XmInternAtom(pInfoList[newDsp].display, "_MOTIF_WM_PAN_POSITION", False); XtAddEventHandler(pInfoList[newDsp].canvas, ButtonPressMask, False, StartTracking, (XtPointer)&pInfoList[newDsp]); } /*========================== CALLBACKS ==========================*/ /*----------------------------------------------------------------* | UpdatePannerCB | *----------------------------------------------------------------*/ static void UpdatePannerCB ( Widget w, XtPointer clientData, XtPointer callData) { XmDrawingAreaCallbackStruct *cb = (XmDrawingAreaCallbackStruct *)callData; PannerInfoRec *pInfoList = (PannerInfoRec *)clientData; if (cb->reason == XmCR_EXPOSE) { XExposeEvent *event = (XExposeEvent *)cb->event; /* Last expose event received - do the drawing. */ if (event->count == 0) UpdatePannerView(pInfoList, DSP); } else /* * Update button pressed. */ UpdatePannerView(pInfoList, DSP); } /*----------------------------------------------------------------* | ChangePageCB | | Invoked just prior to notebook page change. Any drawing here | | would be lost. | *----------------------------------------------------------------*/ static void ChangePageCB ( Widget w, XtPointer clientData, XtPointer callData) { PannerInfoRec *pInfoList = (PannerInfoRec *)clientData; XmNotebookCallbackStruct *nbData = (XmNotebookCallbackStruct *)callData; int pageNumber; pageNumber = nbData->page_number; if ((pageNumber >= MAX_DISPLAY_COUNT) || (pInfoList[pageNumber].display == NULL)) { fprintf(stderr, "ERROR - bad display index. (%d).\n", pageNumber); } else { DSP = pageNumber; } } /*----------------------------------------------------------------* | DoAddDisplayCB | *----------------------------------------------------------------*/ static void DoAddDisplayCB ( Widget w, XtPointer clientData, XtPointer callData) { XmSelectionBoxCallbackStruct *cb = (XmSelectionBoxCallbackStruct *)callData; PannerInfoRec *pInfoList = (PannerInfoRec *)clientData; char *dspName; /* Free when done. */ String appName, appClass; /* Don't free - owned by Xt. */ XtGetApplicationNameAndClass(pInfoList[LOCAL].display, &appName, &appClass); XmStringGetLtoR(cb->value, XmSTRING_DEFAULT_CHARSET, &dspName); OpenNewDisplay(dspName, notebook, pInfoList); if (dspName) XtFree(dspName); } /*----------------------------------------------------------------* | DestinationCB | | This function gets invoked by UTM when a sink has been estab- | | lished and a request initiated against another selection. The | | purpose here is to set-up the parameters and pass them to the | | owner of the selection. The parameter data has already been | | allocated in the MoveScreen() function. | | clientData holds the pannerInfoRec corresponding to the right | | display. | *----------------------------------------------------------------*/ static void DestinationCB ( Widget w, XtPointer clientData, XtPointer callData) { XmDestinationCallbackStruct *dcs = (XmDestinationCallbackStruct *)callData; PannerInfoRec *pInfo = (PannerInfoRec *)clientData; Atom target; /* * Pass the data to free in the clientData field. * location_data points to the param data. This was set in * MoveScreen(). */ /* FIRST - setup the parameters to pass. */ XmTransferSetParameters(dcs->transfer_id, dcs->location_data, /* pointer to param data. */ PAN_FORMAT, PAN_SIZE, /* should be calculated. */ dcs->selection); /* type - don't care. */ /* LAST - Make the transfer. */ XmTransferValue(dcs->transfer_id, pInfo->WM_PAN, /* target for conversion. */ DoneMoveScreenCB, /* CB proc invoked when done. */ dcs->location_data, /* clientData - to be freed. */ dcs->time); } /*----------------------------------------------------------------* | DoneMoveScreenCB | *----------------------------------------------------------------*/ static void DoneMoveScreenCB ( Widget w, XtPointer clientData, XtPointer callData) { /* * Conversion completed. Safe to free param data. */ XtFree((char *)clientData); } /*=========================== PAN-HANDLING ==========================*/ /*----------------------------------------------------------------* | WatchForWindowPanning | *----------------------------------------------------------------*/ static void WatchForWindowPanning (Display *dsp) { XWindowAttributes attr; Window rwin = DefaultRootWindow(dsp); /* Watch whenever the window manager's panning position changes. */ /* Mwm stores the position in properties on the root window. */ /* This is stored in the _MOTIF_WM_PAN_POSITION property. */ XGetWindowAttributes(dsp, rwin, &attr); if (! (attr.your_event_mask & PropertyChangeMask)) XSelectInput(dsp, rwin, attr.your_event_mask | PropertyChangeMask); } /*----------------------------------------------------------------* | HandlePropertyChange | | This routine checks the property changed and if its the right | | property, grab the new panning position. | *----------------------------------------------------------------*/ static void HandlePropertyChange (XEvent *event) { XPropertyEvent *propEvent = (XPropertyEvent *)event; int iDsp; /* Get the correct info record stored with the context manager. */ if (XFindContext(propEvent->display, propEvent->window, context, (XPointer*)&iDsp)) return; /* check if this is the right one. Othersize, we'll update when another * display changes. */ if (propEvent && propEvent->atom != pInfoList[iDsp].WM_PAN_POS) return; /* if the display doesn't match the current one, then punt. */ if (iDsp == DSP) { if (pinnedState == VERIFIED) UpdatePannerView(pInfoList, iDsp); else { Window rWin, child; int x, y, newX, newY; unsigned int width, height, bWidth, depth; /* Get position of the top-level shell */ XGetGeometry(pInfoList[LOCAL].display, XtWindow(pInfoList[LOCAL].shell), &rWin, &x, &y, &width, &height, &bWidth, &depth); XTranslateCoordinates(pInfoList[LOCAL].display, XtWindow(pInfoList[LOCAL].shell), rWin, x, y, &newX, &newY, &child); if ((newX == origX) && (newY == origY)) pinnedState = VERIFIED; else ShowPinStateWarning(); } } #ifdef X_ONLY LOCK = False; #endif } /*============================ DRAWING ===========================*/ /*----------------------------------------------------------------* | UpdatePannerView | *----------------------------------------------------------------*/ static void UpdatePannerView ( PannerInfoRec *pInfoList, int remoteDsp) { XClearArea(pInfoList[LOCAL].display, XtWindow(pInfoList[remoteDsp].canvas), 0, 0, 0, 0, False); DrawWindows(pInfoList); DrawThumb(&pInfoList[remoteDsp]); } /*----------------------------------------------------------------* | DrawWindows | *----------------------------------------------------------------*/ static void DrawWindows (PannerInfoRec *pInfoList) { Window realRoot, root, parent, *child = NULL; int i, x, y; unsigned int childCount, width, height; int (*oldHandler)(); realRoot = RootWindow(pInfoList[DSP].display, XScreenNumberOfScreen(pInfoList[DSP].screen)); if (XQueryTree(pInfoList[DSP].display, realRoot, &root, &parent, &child, &childCount)) { /* * We need to install an error handler since the window-tree may * become invalid while where still processing the list. */ oldHandler = XSetErrorHandler(IgnoreError); for (i=0; icanvas), XtWindow(pInfo->canvas), thumbGC, pInfo->thumbX, pInfo->thumbY, pInfo->thumbW, pInfo->thumbH); } /*----------------------------------------------------------------* | SetupColorsAndGCs | | Called once at the beginning to setup some drawing stuff. | *----------------------------------------------------------------*/ static void SetupColorsAndGCs() { int i; XColor color; Colormap cmap = DefaultColormapOfScreen(pInfoList[LOCAL].screen); /* * set-up the global GCs. */ thumbGC = GetXorGC(pInfoList[LOCAL].canvas); canvasGC = GetCanvasGC(pInfoList[LOCAL].canvas); /* * Allocate the global color cells for the drawing of each windows. * The more random the colors, the better. */ if (XAllocColorCells(pInfoList[LOCAL].display, cmap, False, NULL, 0, cells, COLOR_COUNT)) for (i=0; idisplay, canvasGC, cells[(i+1)%COLOR_COUNT]); } /*----------------------------------------------------------------* | TranslateCoordinates | *----------------------------------------------------------------*/ static void TranslateCoordinates ( PannerInfoRec *pInfo, int x1, int y1, unsigned int width1, unsigned int height1, int *x2, int *y2, unsigned int *width2, unsigned int *height2) { int rootW, rootH; rootW = WidthOfScreen(pInfo->screen); rootH = HeightOfScreen(pInfo->screen); *x2 = x1 * (int)pInfo->thumbW / (int)rootW + pInfo->thumbX; *y2 = y1 * (int)pInfo->thumbH / (int)rootH + pInfo->thumbY; *width2 = width1 * pInfo->thumbW / rootW; *height2 = height1 * pInfo->thumbH / rootH; } /*----------------------------------------------------------------* | IgnoreError | *----------------------------------------------------------------*/ static int IgnoreError (Display *dsp, XErrorEvent *event) { /* * Do Nothing... * This is needed since we will may be updating the list of window * while one of them goes away. Ie: the window list received from * XQueryTree may not be valid for the entire loop where we get each * window's geometry. */ return 0; /* make compiler happy */ } /*======================= TRACKING HANDLERS ======================*/ /*----------------------------------------------------------------* | StartTracking | *----------------------------------------------------------------*/ static void StartTracking ( Widget w, XtPointer clientData, XEvent *event, Boolean *dispatch) { PannerInfoRec *pInfo = (PannerInfoRec *)clientData; XPointerMovedEvent *motionEvent = (XPointerMovedEvent *)event; if ((pinnedState == VERIFIED) && (event->xbutton.button == Button1)) { pInfo->lastEventX = event->xbutton.x; pInfo->lastEventY = event->xbutton.y; if ((event->xbutton.x < pInfo->thumbX) || (event->xbutton.y < pInfo->thumbY) || (event->xbutton.x > pInfo->thumbX + (int)pInfo->thumbW) || (event->xbutton.y > pInfo->thumbY + (int)pInfo->thumbH)) { /* * if on the canvas, then center the thumb over the pointer. */ MoveScreen(pInfo, event->xbutton.x - (int)pInfo->thumbW/2, event->xbutton.y - (int)pInfo->thumbH/2, motionEvent->time); } XtAddEventHandler(w, ButtonReleaseMask, False, StopTracking, clientData); XtAddEventHandler(w, Button1MotionMask, False, DoTracking, clientData); } else if (pinnedState != VERIFIED) CheckPinnedState (); } /*----------------------------------------------------------------* | DoTracking | *----------------------------------------------------------------*/ static void DoTracking ( Widget w, XtPointer clientData, XEvent *event, Boolean *dispatch) { PannerInfoRec *pInfo = (PannerInfoRec *)clientData; XPointerMovedEvent *motionEvent = (XPointerMovedEvent *)event; MoveScreen(pInfo, pInfo->thumbX + event->xbutton.x - pInfo->lastEventX, pInfo->thumbY + event->xbutton.y - pInfo->lastEventY, motionEvent->time); pInfo->lastEventX = event->xbutton.x; pInfo->lastEventY = event->xbutton.y; } /*----------------------------------------------------------------* | StopTracking | *----------------------------------------------------------------*/ static void StopTracking ( Widget w, XtPointer clientData, XEvent *event, Boolean *dispatch) { if (event->xbutton.button == Button1) { XtRemoveEventHandler(w, Button1MotionMask, False, DoTracking, clientData); XtRemoveEventHandler(w, ButtonReleaseMask, False, StopTracking, clientData); } } /*----------------------------------------------------------------* | MoveScreen | *----------------------------------------------------------------*/ static void MoveScreen ( PannerInfoRec *pInfo, int newX, int newY, Time time) { int dx, dy, panDx, panDy, rootW, rootH; XtPointer msg, fulldata; unsigned long size; DrawThumb(pInfo); dx = newX - pInfo->thumbX; dy = newY - pInfo->thumbY; pInfo->thumbX = newX; pInfo->thumbY = newY; DrawThumb(pInfo); /* * Send Pan message to mwm. */ rootW = WidthOfScreen(pInfo->screen); rootH = HeightOfScreen(pInfo->screen); panDx = -(dx * rootW / (int)pInfo->thumbW); panDy = -(dy * rootH / (int)pInfo->thumbH); size = sizeof(CARD32); /* panDx */ size += sizeof(CARD32); /* panDy */ size += sizeof(CARD8); /* config */ msg = fulldata = (XtPointer) XtMalloc(sizeof(CARD8) * size); msg = PackCARD32(msg, panDx); msg = PackCARD32(msg, panDy); msg = PackCARD8(msg, True); #ifdef X_ONLY { Display *display; Window window; Atom MY_PANNER_PROP; display = XtDisplay(notebook); /* notebook just happens to be a global. */ window = XtWindow(notebook); MY_PANNER_PROP = XInternAtom(display, "MY_PANNER_PROP", False); /* * Note - to really make this work across multiple displays, * the window argument must reside on the same display as WM_Si. */ /* * Use a lock to make sure the property was read by Mwm. * When the pan-property is updated, it's safe to make another * conversion. */ if (!LOCK) { LOCK = True; /* Freed in HandlePropertyChange. */ XChangeProperty(display, window, MY_PANNER_PROP, MY_PANNER_PROP, PAN_FORMAT, PropModeReplace, (unsigned char *)fulldata, size); XConvertSelection(pInfo->display, pInfo->WM, pInfo->WM_PAN, MY_PANNER_PROP, window, time); } } #else if (! XmeNamedSink(pInfo->utmShell, /* widget with destination callback */ pInfo->WM, /* named selection - ie. Window Manager */ XmCOPY, /* operation to perform on the data */ (XtPointer)fulldata, /* pointer to param data, */ time) /* free n TransferDone proc. */ ) printf("Error - UTM Transfer failed.\n"); #endif /* X_ONLY */ } /*----------------------------------------------------------------* | PACKING FUNCTIONS | *----------------------------------------------------------------*/ static XtPointer PackCARD32 (XtPointer data, CARD32 val) { CARD16 bottom = val & (0xFFFF); CARD16 top = val >> 16; data = PackCARD16(data, top); data = PackCARD16(data, bottom); return(data); } static XtPointer PackCARD16 (XtPointer data, CARD16 val) { CARD8 bottom = val & (0xFF); CARD8 top = val >> 8; data = PackCARD8(data, top); data = PackCARD8(data, bottom); return(data); } static XtPointer PackCARD8 (XtPointer data, CARD8 val) { CARD8 *ptr = (CARD8 *) data; *ptr = (CARD8) val; data = ((char*)data) + 1; return(data); } /*======================= USER INTERFACE ======================*/ /*----------------------------------------------------------------* | CreateMenuBar | *----------------------------------------------------------------*/ static void CreateMenuBar (Widget parent) { Cardinal n; Arg args[10]; Widget menuBar; Widget cascade1, cascade2, cascade3; Widget menuPane1, menuPane2; Widget b1, b2, b3; menuBar = XmCreateMenuBar(parent, "menuBar", NULL, 0); menuPane1 = XmCreatePulldownMenu(menuBar, "menuPane1", NULL, 0); menuPane2 = XmCreatePulldownMenu(menuBar, "menuPane2", NULL, 0); b1 = XtCreateManagedWidget("b1", xmPushButtonWidgetClass, menuPane1, NULL,0); XtAddCallback(b1, XmNactivateCallback, MenuCB, (XtPointer)MENU_QUIT); b2 = XtCreateManagedWidget("b2", xmPushButtonWidgetClass, menuPane2, NULL,0); XtAddCallback(b2, XmNactivateCallback, MenuCB, (XtPointer)MENU_UPDATE); b3 = XtCreateManagedWidget("b3", xmPushButtonWidgetClass, menuPane2, NULL,0); XtAddCallback(b3, XmNactivateCallback, MenuCB, (XtPointer)MENU_NEW); n = 0; XtSetArg(args[n], XmNsubMenuId, menuPane1); n++; cascade1 = XmCreateCascadeButton(menuBar, "cascade1", args, n); XtManageChild(cascade1); n = 0; XtSetArg(args[n], XmNsubMenuId, menuPane2); n++; cascade2 = XmCreateCascadeButton(menuBar, "cascade2", args, n); XtManageChild(cascade2); n = 0; cascade3 = XmCreateCascadeButton(menuBar, "cascade3", args, n); XtAddCallback(cascade3, XmNactivateCallback, MenuCB, (XtPointer)MENU_HELP); XtManageChild(cascade3); n = 0; XtSetArg(args[n], XmNmenuHelpWidget, cascade3); n++; XtSetValues(menuBar, args, n); XtManageChild(menuBar); } /*----------------------------------------------------------------* | MenuCB | *----------------------------------------------------------------*/ static void MenuCB (Widget w, XtPointer clientData, XtPointer callData) { switch ((long)clientData) { case MENU_UPDATE: DoUpdatePanner(); break; case MENU_NEW: DoAddDisplay(); break; case MENU_QUIT: DoQuit(); break; case MENU_HELP: DoHelp(); break; } } /*----------------------------------------------------------------* | DoUpdatePanner | *----------------------------------------------------------------*/ static void DoUpdatePanner () { XClearArea(pInfoList[LOCAL].display, XtWindow(pInfoList[DSP].canvas), 0, 0, 0, 0, False); DrawWindows(pInfoList); DrawThumb(&pInfoList[DSP]); } /*----------------------------------------------------------------* | DoAddDisplay | *----------------------------------------------------------------*/ static void DoAddDisplay () { static Widget dlog = NULL; Arg args[3]; int n; if (dlog == NULL) { n = 0; XtSetArg(args[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++; dlog = XmCreatePromptDialog(pInfoList[LOCAL].shell, "promptDlog", args, n); XtAddCallback(dlog, XmNokCallback, DoAddDisplayCB, pInfoList); XtUnmanageChild( XmSelectionBoxGetChild (dlog, XmDIALOG_HELP_BUTTON) ); } XtManageChild(dlog); } /*----------------------------------------------------------------* | DoHelp | *----------------------------------------------------------------*/ static void DoHelp () { static Widget dlog = NULL; Arg args[3]; int n; if (dlog == NULL) { dlog = XmCreateInformationDialog(pInfoList[LOCAL].shell, "messageDlog", NULL, 0); XtUnmanageChild( XmMessageBoxGetChild (dlog, XmDIALOG_HELP_BUTTON) ); XtUnmanageChild( XmMessageBoxGetChild (dlog, XmDIALOG_CANCEL_BUTTON) ); } XtManageChild(dlog); } /*----------------------------------------------------------------* | DoQuit | *----------------------------------------------------------------*/ static void DoQuit () { XSync (pInfoList[LOCAL].display, False); XCloseDisplay (pInfoList[LOCAL].display); exit(0); } /*----------------------------------------------------------------* | GetTimeStamp | *----------------------------------------------------------------*/ Time GetTimestamp (Display *dsp) { XEvent event; XWindowAttributes attr; Atom timeProp = XInternAtom(dsp, "_MOTIF_CURRENT_TIME", False); Window rwin = DefaultRootWindow(dsp); XGetWindowAttributes(dsp, rwin, &attr); if (! (attr.your_event_mask & PropertyChangeMask)) XSelectInput(dsp, rwin, attr.your_event_mask | PropertyChangeMask); XChangeProperty(dsp, rwin, timeProp, timeProp, 8, PropModeAppend, NULL, 0); XWindowEvent(dsp, rwin, PropertyChangeMask, &event); if (! (attr.your_event_mask & PropertyChangeMask)) XSelectInput(dsp, rwin, attr.your_event_mask); return(event.xproperty.time); } /*----------------------------------------------------------------* | CheckPinnedState | *----------------------------------------------------------------*/ static void CheckPinnedState () { static int panDx=0, panDy=-1; XtPointer msg, fulldata; unsigned long size; Window rWin, child; int x, y; unsigned int width, height, bWidth, depth; Time time = GetTimestamp(pInfoList[LOCAL].display); if (pinnedState == VERIFIED) return; panDy = -panDy; pinnedState = VERIFYING; /* Get position of the top-level shell */ XGetGeometry(pInfoList[LOCAL].display, XtWindow(pInfoList[LOCAL].shell), &rWin, &x, &y, &width, &height, &bWidth, &depth); XTranslateCoordinates(pInfoList[LOCAL].display, XtWindow(pInfoList[LOCAL].shell), rWin, x, y, &origX, &origY, &child); size = sizeof(CARD32); /* panDx */ size += sizeof(CARD32); /* panDy */ size += sizeof(CARD8); /* config */ msg = fulldata = (XtPointer) XtMalloc(sizeof(CARD8) * size); msg = PackCARD32(msg, panDx); msg = PackCARD32(msg, panDy); msg = PackCARD8(msg, True); if (! XmeNamedSink(pInfoList[LOCAL].utmShell, pInfoList[LOCAL].WM, XmCOPY, (XtPointer)fulldata, time) ) printf("Error - UTM Transfer failed.\n"); } /*----------------------------------------------------------------* | ShowPinStateWarning | *----------------------------------------------------------------*/ static void ShowPinStateWarning () { static Widget dlog = NULL; Arg args[3]; int n; if (dlog == NULL) { dlog = XmCreateWarningDialog(pInfoList[LOCAL].shell, "warningDlog", NULL, 0); XtUnmanageChild( XmMessageBoxGetChild (dlog, XmDIALOG_HELP_BUTTON) ); XtUnmanageChild( XmMessageBoxGetChild (dlog, XmDIALOG_CANCEL_BUTTON) ); } XtManageChild(dlog); } /*----------------------------------------------------------------* | HandleInitialExpose | *----------------------------------------------------------------*/ static void HandleInitialExpose ( Widget w, XtPointer clientData, XEvent *event, Boolean *cont) { XtRemoveEventHandler(w, ExposureMask, False, HandleInitialExpose, NULL); CheckPinnedState(); }