xmag.c revision 0c7e83b2
1/*
2
3Copyright 1991, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included
12in all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
18OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall
23not be used in advertising or otherwise to promote the sale, use or
24other dealings in this Software without prior written authorization
25from The Open Group.
26
27*/
28
29#include "config.h"
30
31#include <stdlib.h>		/* for exit() and abs() */
32#include <stdio.h>
33
34#include <X11/Intrinsic.h>
35#include <X11/StringDefs.h>
36#include <X11/Xaw/Paned.h>
37#include <X11/Xaw/Command.h>
38#include <X11/Xaw/Label.h>
39#include <X11/Shell.h>
40#include <X11/cursorfont.h>
41#include <X11/Xmu/Error.h>
42#include "RootWin.h"
43#include "Scale.h"
44#include "CutPaste.h"
45
46#define SRCWIDTH  64
47#define SRCHEIGHT 64
48
49#ifndef min
50#define min(a, b) ((a) < (b) ? (a) : (b))
51#endif
52
53#ifndef max
54#define max(a, b) ((a) > (b) ? (a) : (b))
55#endif
56
57
58
59/* highlight interval (in milliseconds) */
60#define HLINTERVAL  100
61
62/* sleep between draw & erase of highlight
63 * 20 milliseconds - enough for screen refresh - not too long to annoy users
64 *  since we hold a server grab during this time
65 */
66#define HLSLEEPINTERVAL 20 /* milliseconds */
67
68#ifdef HAVE_NANOSLEEP
69#include <time.h>
70#define HLSLEEP	    do { \
71	struct timespec sleeptime = { 0 , HLSLEEPINTERVAL * 1000000 } ;	\
72	nanosleep(&sleeptime, NULL); \
73    } while(0)
74#elif defined(HAVE_POLL)
75#include <poll.h>
76#define HLSLEEP	    poll(NULL, 0, HLSLEEPINTERVAL)
77#elif defined(HAVE_SELECT)
78#include <X11/Xpoll.h>
79#define HLSLEEP	    do { \
80	struct timeval sleeptime = { 0 , HLSLEEPINTERVAL * 1000 } ;	\
81	select(0, NULL, NULL, NULL, &sleeptime); \
82    } while(0)
83#else
84#define HLSLEEP	XSync(dpy, False)
85#endif
86
87/* highlight mode */
88typedef enum { drag, resize, done } hlMode;
89
90/* highlight data structure */
91typedef struct {
92  Boolean   newScale;
93  hlMode    selectMode;
94  GC        gc;
95  XWindowAttributes win_info;
96  XImage     *image;
97  Position  homeX, homeY, x, y;
98  Dimension width, height;
99  Widget    scaleShell, scaleInstance, pixShell, pixLabel, cmapWinList [2];
100  } hlStruct, *hlPtr;
101
102
103
104/* global variables */
105static XtAppContext app;
106static Cursor ulAngle, urAngle, lrAngle, llAngle;
107static Display *dpy;
108static int scr;
109static GC selectGC;
110static XGCValues selectGCV;
111static Widget toplevel, root;
112static Atom wm_delete_window;
113static int numXmags = 0;
114static int srcStat, srcX, srcY;
115static unsigned int srcWidth, srcHeight;
116
117/* forward declarations */
118
119static int Error(Display *, XErrorEvent *);
120static void CloseAP(Widget, XEvent *, String *, Cardinal *);
121static void SetCmapPropsAP(Widget, XEvent *, String *, Cardinal *);
122static void UnsetCmapPropsAP(Widget, XEvent *, String *, Cardinal *);
123static void NewAP(Widget, XEvent *, String *, Cardinal *);
124static void ReplaceAP(Widget, XEvent *, String *, Cardinal *);
125static void PopupPixelAP(Widget, XEvent *, String *, Cardinal *);
126static void UpdatePixelAP(Widget, XEvent *, String *, Cardinal *);
127static void PopdownPixelAP(Widget, XEvent *, String *, Cardinal *);
128static void SelectRegionAP(Widget, XEvent *, String *, Cardinal *);
129static void CheckPoints(Position *, Position *, Position *, Position *);
130static void HighlightTO(XtPointer, XtIntervalId *);
131static void CloseCB(Widget, XtPointer, XtPointer);
132static void ReplaceCB(Widget, XtPointer, XtPointer);
133static void NewCB(Widget, XtPointer, XtPointer);
134static void SelectCB(Widget, XtPointer, XtPointer);
135static void PasteCB(Widget, XtPointer, XtPointer);
136static void SetupGC(void);
137static Window FindWindow(int, int);
138static void ResizeEH(Widget, XtPointer, XEvent *, Boolean *);
139static void DragEH(Widget, XtPointer, XEvent *, Boolean *);
140static void StartRootPtrGrab(int, hlPtr);
141static void CreateRoot(void);
142static void GetImageAndAttributes(Window, int, int, int, int, hlPtr);
143static int Get_XColors(XWindowAttributes *, XColor **);
144static Pixel GetMaxIntensity(hlPtr);
145static Pixel GetMinIntensity(hlPtr);
146static void PopupNewScale(hlPtr);
147static void RedoOldScale(hlPtr);
148static void InitCursors(void);
149static void ParseSourceGeom(void);
150
151/* application resources */
152
153typedef struct { String geometry, source, mag, title; } OptionsRec;
154static OptionsRec options;
155
156#define Offset(field) XtOffsetOf(OptionsRec, field)
157static XtResource resources[] = {
158  {"geometry", "Geometry", XtRString, sizeof(String),
159     Offset(geometry), XtRString, (XtPointer)NULL},
160  {"mag", "Mag", XtRString, sizeof(String),
161     Offset(mag), XtRString, (XtPointer)"5.0"},
162  {"source", "Source", XtRString, sizeof(String),
163     Offset(source), XtRString, (XtPointer)"SRCWIDTHxSRCHEIGHT"},
164  {"title", XtCString, XtRString, sizeof(char *),
165     Offset(title), XtRString, "xmag"},
166};
167#undef Offset
168
169static XrmOptionDescRec optionDesc[] = {
170  {"-bd",         "*borderColor", XrmoptionSepArg, (XtPointer)NULL},
171  {"-bg",         "*background",   XrmoptionSepArg, (XtPointer)NULL},
172  {"-bw",         "*borderWidth", XrmoptionSepArg, (XtPointer)NULL},
173
174  {"-geometry", "*geometry", XrmoptionSepArg, (XtPointer)NULL},
175  {"-mag",      "*mag",                XrmoptionSepArg, (XtPointer)NULL},
176  {"-source",   "*source",             XrmoptionSepArg, (XtPointer)NULL},
177  {"-title",    "*title",              XrmoptionSepArg, (XtPointer)NULL},
178};
179
180
181
182/* action table */
183
184static XtActionsRec actions_table[] = {
185  {"close", CloseAP},
186  {"set-colors", SetCmapPropsAP},
187  {"unset-colors", UnsetCmapPropsAP},
188  {"new", NewAP},
189  {"replace", ReplaceAP},
190  {"popup-pixel", PopupPixelAP},
191  {"update-pixel", UpdatePixelAP},
192  {"popdown-pixel", PopdownPixelAP},
193  {"select-region", SelectRegionAP}
194};
195
196
197
198/*
199 * Error() -- Error handler:  Catch a bad match in magnifying an
200 *            area that contains bits of different depths.
201 */
202static int
203Error(Display *dpy, XErrorEvent *err)
204{
205  (void) XmuPrintDefaultErrorMessage (dpy, err, stderr);
206  return 0;
207}
208
209
210/*
211 * CloseAP() -- Close this dialog.  If its the last one exit the program.
212 *
213 */
214static void			/* ARGSUSED */
215CloseAP(Widget w, XEvent *event, String *params, Cardinal *num_params)
216{
217  Arg wargs[2]; int n; hlPtr data;
218  if (!--numXmags) exit(0);
219  if (event->type != ClientMessage) {
220    n = 0;			/* get user data */
221    XtSetArg(wargs[0], XtNuserData, &data); n++;
222    XtGetValues(w, wargs, n);
223    w = data->scaleShell;
224  }
225  XtPopdown(w);
226  XtDestroyWidget(w);
227}
228
229
230
231/*
232 * SetCmapPropsAP() -- Put the scale widget first in WM_COLORMAP_WINDOWS
233 *
234 */
235static void			/* ARGSUSED */
236SetCmapPropsAP(Widget w, XEvent *event, String *params, Cardinal *num_params)
237{
238  Arg wargs[2]; int n; hlPtr data;
239  n = 0;			/* get user data */
240  XtSetArg(wargs[0], XtNuserData, &data); n++;
241  XtGetValues(w, wargs, n);
242  if (data->win_info.colormap != DefaultColormap(dpy, scr)) {
243    data->cmapWinList[0] = data->scaleInstance;
244    data->cmapWinList[1] = data->scaleShell;
245    XtSetWMColormapWindows(data->scaleShell, data->cmapWinList, 2);
246  }
247}
248
249
250
251/*
252 * UnsetCmapPropsAP() -- Put the shell first in WM_COLORMAP_WINDOWS
253 *
254 */
255static void			/* ARGSUSED */
256UnsetCmapPropsAP(Widget w, XEvent *event, String *params, Cardinal *num_params)
257{
258  Arg wargs[2]; int n; hlPtr data;
259  n = 0;			/* get user data */
260  XtSetArg(wargs[0], XtNuserData, &data); n++;
261  XtGetValues(w, wargs, n);
262  if (data->win_info.colormap != DefaultColormap(dpy, scr)) {
263    data->cmapWinList[0] = data->scaleShell;
264    data->cmapWinList[1] = data->scaleInstance;
265    XtSetWMColormapWindows(data->scaleShell, data->cmapWinList, 2);
266  }
267}
268
269
270
271/*
272 * NewAP() -- Create an additional xmag dialog. THIS IS A COPY OF NewEH
273 *                                              FIND A BETTER WAY....
274 */
275static void			/* ARGSUSED */
276NewAP(Widget w, XEvent *event, String *params, Cardinal *num_params)
277{
278  StartRootPtrGrab(True, NULL);
279}
280
281
282
283/*
284 * ReplaceAP() -- Replace this particular xmag dialog.
285 */
286static void                     /* ARGSUSED */
287ReplaceAP(Widget w, XEvent *event, String *params, Cardinal *num_params)
288{
289  Arg wargs[2]; int n; hlPtr data;
290  n = 0;			/* get user data */
291  XtSetArg(wargs[0], XtNuserData, &data); n++;
292  XtGetValues(w, wargs, n);
293  StartRootPtrGrab(False, data);
294}
295
296
297
298/*
299 * PopupPixelAP() -- Show pixel information.
300 */
301static void			/* ARGSUSED */
302PopupPixelAP(Widget w, XEvent *event, String *params, Cardinal *num_params)
303{
304    Position scale_x, scale_y;
305    Dimension scale_height;
306    Position label_x, label_y;
307    Dimension label_height;
308    int n;
309    Arg wargs[3];
310    hlPtr data;
311
312    n = 0;			/* get user data */
313    XtSetArg(wargs[0], XtNuserData, &data); n++;
314    XtGetValues(w, wargs, n);
315
316    n = 0;
317    XtSetArg(wargs[n], XtNheight, &scale_height); n++;
318    XtGetValues(w, wargs, n);
319    XtTranslateCoords(w, -1, -1, &scale_x, &scale_y);
320
321    XtRealizeWidget(data->pixShell); /* to get the right height  */
322
323    n = 0;
324    XtSetArg(wargs[n], XtNheight, &label_height); n++;
325    XtGetValues(data->pixShell, wargs, n);
326
327    if ((double) event->xbutton.y / (double) scale_height > 0.5) {
328	label_x = scale_x;
329	label_y = scale_y;
330    }
331    else {
332	label_x = scale_x;
333	label_y = scale_y + scale_height - label_height;
334    }
335
336    n = 0;
337    XtSetArg(wargs[n], XtNx, label_x); n++;
338    XtSetArg(wargs[n], XtNy, label_y); n++;
339    XtSetValues(data->pixShell, wargs, n);
340
341    UpdatePixelAP(w, event, NULL, NULL);
342}
343
344
345
346/*
347 * UpdatePixelAP() -- Update pixel information.
348 */
349static void			/* ARGSUSED */
350UpdatePixelAP(Widget w, XEvent *event, String *params, Cardinal *num_params)
351{
352    Position x, y;
353    Pixel pixel;
354    XColor color;
355    int n;
356    Arg wargs[3];
357    char string[80];
358    hlPtr data;
359
360    n = 0;
361    XtSetArg(wargs[0], XtNuserData, &data); n++;
362    XtGetValues(w, wargs, n);
363
364    if (SWGetImagePixel(w, event->xbutton.x, event->xbutton.y, &x, &y, &pixel))
365	XtPopdown(data->pixShell);
366    else {
367	color.pixel = pixel;
368	XQueryColor(dpy, data->win_info.colormap, &color);
369	snprintf(string, sizeof(string),
370		 "Pixel %ld at (%d,%d) colored (%x,%x,%x).",
371		 pixel, x + data->x, y + data->y,
372		 color.red, color.green, color.blue);
373	n = 0;
374	XtSetArg(wargs[n], XtNlabel, string); n++;
375	XtSetValues(data->pixLabel, wargs, n);
376	XtPopup(data->pixShell, XtGrabNone);
377    }
378}
379
380
381
382/*
383 * PopdownPixelAP() -- Remove pixel info.
384 */
385static void			/* ARGSUSED */
386PopdownPixelAP(Widget w, XEvent *event, String *params, Cardinal *num_params)
387{
388  int n;
389  Arg wargs[3];
390  hlPtr data = NULL;
391
392  n = 0;
393  XtSetArg(wargs[0], XtNuserData, &data); n++;
394  XtGetValues(w, wargs, n);
395
396  if (data)
397    XtPopdown(data->pixShell);
398}
399
400
401
402static void			/* ARGSUSED */
403SelectRegionAP(Widget w, XEvent *event, String *params, Cardinal *num_params)
404{
405/***** NOT SURE WHAT TO DO WITH THIS
406    if (app_resources.unmap)
407	XtUnmapWidget(toplevel);
408    Redisplay(XtDisplay(w), RootWindow(XtDisplay(w),
409				       DefaultScreen(XtDisplay(w))),
410	      source.width, source.height,
411	      app_resources.freq, app_resources.puls,
412	      ul_angle, lr_angle,
413	      app_resources.grab);
414
415    if (app_resources.unmap)
416	XtMapWidget(toplevel);
417******/
418}
419
420
421
422/*
423 * CheckPoints() -- Change the cursor for the correct quadrant.
424 *                  Make sure the first point is less than the second
425 *                  for drawing the selection rectangle.
426 *
427 */
428static void
429CheckPoints(Position *x1, Position *x2, Position *y1, Position *y2)
430{
431  Position tmp;
432  Boolean above, left;
433  Cursor newC;
434  above = (*y2 < *y1); left = (*x2 < *x1);
435  if (above&&left) newC = ulAngle;
436  else if (above&&!left) newC = urAngle;
437  else if (!above&&!left) newC = lrAngle;
438  else newC = llAngle;
439  XChangeActivePointerGrab
440    (dpy, PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
441     newC, CurrentTime);
442  if (*x2 < *x1) { tmp = *x1; *x1 = *x2; *x2 = tmp; }
443  if (*y2 < *y1) { tmp = *y1; *y1 = *y2; *y2 = tmp; }
444}
445
446
447
448/*
449 * HighlightTO() -- Timer to highlight the selection box
450 */
451static void
452HighlightTO(XtPointer closure, XtIntervalId *id)	/* ARGSUSED */
453{
454  hlPtr data = (hlPtr)closure;
455  XGrabServer(dpy);
456  if (data->selectMode == drag) {
457    XDrawRectangle(dpy, DefaultRootWindow(dpy), data->gc,
458		   data->x, data->y, data->width, data->height);
459    XFlush(dpy);
460    HLSLEEP;
461    XDrawRectangle(dpy, DefaultRootWindow(dpy), data->gc,
462		   data->x, data->y, data->width, data->height);
463  }
464  else if (data->selectMode == resize) {
465    Position x1 = data->homeX,
466             x2 = data->x,
467             y1 = data->homeY,
468             y2 = data->y;
469    CheckPoints(&x1, &x2, &y1, &y2);
470    XDrawRectangle(dpy, DefaultRootWindow(dpy), data->gc,
471		   x1, y1, x2 - x1, y2 - y1);
472    XFlush(dpy);
473    HLSLEEP;
474    XDrawRectangle(dpy, DefaultRootWindow(dpy), data->gc,
475		   x1, y1, x2 - x1, y2 - y1);
476  }
477  XUngrabServer(dpy);
478  if (data->selectMode != done)
479    XtAppAddTimeOut(app, HLINTERVAL, HighlightTO, (XtPointer)data);
480}
481
482
483
484/*
485 * CloseCB() -- Delete this xmag dialog.  If its the only one on the screen
486 *             then exit.
487 */
488static void			/* ARGSUSED */
489CloseCB(Widget w, XtPointer clientData, XtPointer callData)
490{
491  Widget shell = (Widget)clientData;
492  if (!--numXmags) exit(0);
493  XtPopdown(shell);
494  XtDestroyWidget(shell);
495}
496
497
498
499/*
500 * ReplaceCB() -- Replace this particular xmag dialog.
501 */
502static void                     /* ARGSUSED */
503ReplaceCB(Widget w, XtPointer clientData, XtPointer callData)
504{
505  hlPtr data = (hlPtr)clientData;
506  StartRootPtrGrab(False, data);
507}
508
509
510
511/*
512 * NewCB() -- Create an additional xmag dialog.
513 */
514static void			/* ARGSUSED */
515NewCB(Widget w, XtPointer clientData, XtPointer callData)
516{
517  StartRootPtrGrab(True, NULL);
518}
519
520
521
522/*
523 * SelectCB() -- Own the primary selection.
524 */
525static void			/* ARGSUSED */
526SelectCB(Widget w, XtPointer clientData, XtPointer callData)
527{
528  hlPtr data = (hlPtr)clientData;
529  SWGrabSelection(data->scaleInstance, XtLastTimestampProcessed(dpy));
530}
531
532
533
534/*
535 * PasteCB() -- Paste from the primary selectin into xmag.
536 */
537static void			/* ARGSUSED */
538PasteCB(Widget w, XtPointer clientData, XtPointer callData)
539{
540  hlPtr data = (hlPtr)clientData;
541  SWRequestSelection(data->scaleInstance, XtLastTimestampProcessed(dpy));
542}
543
544
545
546/*
547 * SetupGC() -- Graphics context for magnification selection.
548 */
549static void
550SetupGC(void)
551{
552    selectGCV.function = GXxor;
553    selectGCV.foreground = 0xffffffff;
554    selectGCV.subwindow_mode = IncludeInferiors;
555    selectGC = XtGetGC(toplevel, GCFunction|GCForeground|GCSubwindowMode,
556		       &selectGCV);
557}
558
559
560
561/*
562 * FindWindow() -- Determine window the pointer is over.
563 *
564 */
565static Window
566FindWindow(int x, int y)	/* Location of cursor */
567{
568  XWindowAttributes wa;
569  Window findW = DefaultRootWindow(dpy), stopW, childW;
570
571  /* Setup for first window find */
572  stopW = findW;
573
574  while (stopW) {
575    XTranslateCoordinates(dpy, findW, stopW,
576			  x, y, &x, &y, &childW);
577    findW = stopW;
578    /* If child is not InputOutput (for example, InputOnly) */
579    /* then don't continue, return the present findW which */
580    /* can be the root, or a root child of class InputOutput */
581    if (childW &&
582	XGetWindowAttributes(dpy, childW, &wa) &&
583	wa.class != InputOutput)
584	break;
585    stopW = childW;
586  }
587  return findW;
588}
589
590
591
592/*
593 * ResizeEH() -- Event Handler for resize of selection box.
594 */
595static void
596ResizeEH(Widget w, XtPointer closure, XEvent *event,
597	 Boolean *continue_to_dispatch)	/* ARGSUSED */
598{
599  hlPtr data = (hlPtr)closure;
600  switch (event->type) {
601  case MotionNotify:
602    data->x = event->xmotion.x_root;
603    data->y = event->xmotion.y_root;
604    break;
605  case ButtonRelease:
606    GetImageAndAttributes(FindWindow(event->xmotion.x_root,
607			event->xmotion.y_root),
608	     min(data->homeX,event->xbutton.x_root),
609	     min(data->homeY,event->xbutton.y_root),
610	     abs(data->homeX - event->xbutton.x_root),
611	     abs(data->homeY - event->xbutton.y_root),
612	     data);
613    if (data->newScale)
614      PopupNewScale(data);
615    else
616      SWSetImage(data->scaleInstance, data->image);
617    XtUngrabPointer(w, CurrentTime);
618/*****
619    XtRemoveRawEventHandler(w, PointerMotionMask|ButtonReleaseMask,
620			 True, ResizeEH, (XtPointer)data);
621*****/
622    XtRemoveEventHandler(w, PointerMotionMask|ButtonReleaseMask,
623			 True, ResizeEH, (XtPointer)data);
624    data->selectMode = done;
625    break;
626  }
627}
628
629
630
631/*
632 * DragEH() -- Event Handler for dragging selection box.
633 */
634static void
635DragEH(Widget w, XtPointer closure, XEvent *event,
636       Boolean *continue_to_dispatch) /* ARGSUSED */
637{
638  hlPtr data = (hlPtr)closure;
639  switch (event->type) {
640  case MotionNotify:		/* drag mode */
641    data->x = event->xmotion.x_root;
642    data->y = event->xmotion.y_root;
643    break;
644  case ButtonRelease:		/* end drag mode */
645    if (event->xbutton.button == Button1) { /* get image */
646      /* Problem: You can't get bits with XGetImage outside of its window.
647       *          xmag will only do a GetImage on the actual window in the case
648       *          where the depth of the window does not match the depth of
649       *          the root window.
650       */
651      GetImageAndAttributes(FindWindow(event->xmotion.x_root,
652			  event->xmotion.y_root),
653	       event->xbutton.x_root,
654	       event->xbutton.y_root,
655	       srcWidth, srcHeight, data);
656      if (data->newScale)
657	PopupNewScale(data);
658      else
659	RedoOldScale(data);
660      XtUngrabPointer(w, CurrentTime);
661      XtRemoveRawEventHandler(w, PointerMotionMask|ButtonPressMask|
662			      ButtonReleaseMask, True, DragEH,
663			      (XtPointer)data);
664      data->selectMode = done;
665    }
666
667    break;
668  case ButtonPress:
669    if (event->xbutton.button == Button2) {	/* turn on resize mode */
670      data->homeX = event->xbutton.x_root;
671      data->homeY = event->xbutton.y_root;
672      data->x = event->xbutton.x_root + srcWidth;
673      data->y = event->xbutton.y_root + srcHeight;
674      data->selectMode = resize;
675      XtRemoveRawEventHandler(w, PointerMotionMask|ButtonPressMask|
676			   ButtonReleaseMask, True, DragEH, (XtPointer)data);
677      XChangeActivePointerGrab
678	(dpy, PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
679	 lrAngle, CurrentTime);
680      XWarpPointer(dpy, None, None, 0, 0, 0, 0,
681		   srcWidth, srcHeight);
682      XtAddEventHandler(w, PointerMotionMask|ButtonReleaseMask,
683			True, ResizeEH, (XtPointer)data);
684    }
685    break;
686  }
687}
688
689
690
691
692/*
693 * StartRootPtrGrab() -- Bring up the selection box.
694 *
695 */
696static void
697StartRootPtrGrab(int new, 	/* do we create a new scale instance? */
698		 hlPtr data)	/* highlight data */
699{
700  Window    rootR, childR;
701  int       rootX, rootY, winX, winY;
702  unsigned  int mask;
703  hlPtr hlData;
704  XtGrabPointer
705    (root, False,
706     PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
707     GrabModeAsync, GrabModeAsync, None, ulAngle, CurrentTime);
708  XQueryPointer(dpy, DefaultRootWindow(dpy), &rootR, &childR,
709		&rootX, &rootY, &winX, &winY, &mask);
710  if (new) {
711    numXmags++;
712    hlData = (hlPtr)XtMalloc(sizeof(hlStruct));
713  }
714  else hlData = data;
715  hlData->newScale   = new;
716  hlData->selectMode = drag;
717  hlData->x          = rootX;
718  hlData->y          = rootY;
719  hlData->gc         = selectGC;
720  hlData->width      = srcWidth;
721  hlData->height     = srcHeight;
722  XtAddRawEventHandler
723    (root, PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
724     True, DragEH, (XtPointer)hlData);
725  (void) XtAppAddTimeOut(app, HLINTERVAL, HighlightTO, (XtPointer)hlData);
726}
727
728
729
730/*
731 * CreateRoot() -- Create a root window widget. If the user specified x and y
732 *                 in the source geometry then use this to directly get the
733 *                 image.
734 */
735static void
736CreateRoot(void)
737{
738  hlPtr data;
739  root = XtCreateWidget("root", rootWindowWidgetClass, toplevel, NULL, 0);
740  XtRealizeWidget(root);
741  if (XValue & srcStat && YValue &srcStat) {
742    numXmags = 1;
743    data = (hlPtr)XtMalloc(sizeof(hlStruct));
744    data = data;
745    data->newScale   = True;
746    data->selectMode = drag;
747    data->x          = srcX;
748    data->y          = srcY;
749    data->gc         = selectGC;
750    data->width      = srcWidth;
751    data->height     = srcHeight;
752    GetImageAndAttributes(RootWindow(dpy, scr), srcX, srcY, srcWidth,
753			  srcHeight, data);
754    PopupNewScale(data);
755    return;
756  }
757}
758
759
760/*
761 * GetImageAndAttributes() -- Get the image bits from the screen.
762 *               We will also determine here the colormap, depth, and
763 *               visual to be used for the magnification image.
764 */
765static void
766GetImageAndAttributes(Window w, int x, int y, int width, int height,
767		      hlPtr data)
768{
769    /* get parameters of window being magnified */
770    XGetWindowAttributes(dpy, w, &data->win_info);
771
772    if (data->win_info.depth == DefaultDepth(dpy, scr)) {
773	/* avoid off screen pixels */
774	if (x < 0)
775	    x = 0;
776	if (y < 0)
777	    y = 0;
778	if (x + width > DisplayWidth(dpy,scr))
779	    x = DisplayWidth(dpy,scr) - width;
780	if (y + height > DisplayHeight(dpy,scr))
781	    y = DisplayHeight(dpy,scr) - height;
782	data->x = x; data->y = y;
783	/* get image pixels */
784	data->image = XGetImage (dpy,
785				 RootWindow(dpy, scr),
786				 x, y,
787				 width, height,
788				 AllPlanes, ZPixmap);
789    }
790    else {
791	int	t0, t1;
792	int	x0, x1, y0, y1;
793	int	xInWin, yInWin;
794	Window	childWin;
795
796	XTranslateCoordinates(dpy, DefaultRootWindow(dpy), w, x, y,
797			      &xInWin, &yInWin, &childWin);
798
799	/* Avoid off screen pixels. Assume this routine is not
800	 * called for totally offscreen windows. */
801	x0 = max(x, 0);
802	y0 = max(y, 0);
803	x1 = min(DisplayWidth(dpy, scr),
804		 min(x0 + width, x0 + (data->win_info.width - xInWin)));
805	y1 = min(DisplayHeight(dpy, scr),
806		 min(y0 + height, y0 + (data->win_info.height - yInWin)));
807
808	/* Try to use up to width x height pixels */
809	if (x1 - x0 < width) {
810	    t0 = x0;
811	    t1 = max(0, x - xInWin + data->win_info.width -
812		     DisplayWidth(dpy, scr));
813	    x0 = max(0, x1 - min(width, data->win_info.width - t1));
814	    xInWin -= t0 - x0;
815	}
816	if (y1 - y0 < height) {
817	    t0 = y0;
818	    t1 = max(0, y - yInWin + data->win_info.height -
819		     DisplayHeight(dpy, scr));
820	    y0 = max(0, y1 - min(height, data->win_info.height - t1));
821	    yInWin -= t0 - y0;
822	}
823
824	data->x = x0;
825	data->y = y0;
826	data->width = x1 - x0;
827	data->height = y1 - y0;
828
829	data->image = XGetImage (dpy,
830				 w,
831				 xInWin, yInWin,
832				 data->width, data->height,
833				 AllPlanes, ZPixmap);
834
835    }
836}
837
838
839
840/*
841 * Get_XColors() Get the XColors of all pixels in image - returns # of colors
842 *               This function was taken from xwd (thanks Bob...)
843 */
844#define lowbit(x) ((x) & (~(x) + 1))
845static int
846Get_XColors(XWindowAttributes *win_info, XColor **colors)
847{
848    int i, ncolors;
849
850    if (!win_info->colormap)
851        return(0);
852
853    ncolors = win_info->visual->map_entries;
854    if (!(*colors = (XColor *) XtMalloc (sizeof(XColor) * ncolors)))
855      XtError("Out of memory!");
856
857    if (win_info->visual->class == DirectColor ||
858        win_info->visual->class == TrueColor) {
859        Pixel red, green, blue, red1, green1, blue1;
860
861        red = green = blue = 0;
862        red1 = lowbit(win_info->visual->red_mask);
863        green1 = lowbit(win_info->visual->green_mask);
864        blue1 = lowbit(win_info->visual->blue_mask);
865        for (i=0; i<ncolors; i++) {
866          (*colors)[i].pixel = red|green|blue;
867          (*colors)[i].pad = 0;
868          red += red1;
869          if (red > win_info->visual->red_mask)
870            red = 0;
871          green += green1;
872          if (green > win_info->visual->green_mask)
873            green = 0;
874          blue += blue1;
875          if (blue > win_info->visual->blue_mask)
876            blue = 0;
877        }
878    } else {
879        for (i=0; i<ncolors; i++) {
880          (*colors)[i].pixel = i;
881          (*colors)[i].pad = 0;
882        }
883    }
884
885    XQueryColors(dpy, win_info->colormap, *colors, ncolors);
886
887    return(ncolors);
888}
889
890
891
892#define Intensity(cptr) (3.0*cptr->red+0.59*cptr->green+0.11*cptr->blue)
893
894/*
895 * GetMaxIntensity() -- Find the maximum intensity pixel value for a colormap.
896 */
897static Pixel
898GetMaxIntensity(hlPtr data)
899{
900  XColor *colors = NULL, *mptr, *tptr;
901  int i, ncolors;
902
903  if (data->win_info.colormap == DefaultColormap(dpy, scr))
904    return WhitePixel(dpy, scr);
905  ncolors = Get_XColors(&data->win_info, &colors);
906  mptr = tptr = colors; tptr++;
907  for (i=1; i<ncolors; i++) {
908    if ((int)Intensity(mptr) < (int)Intensity(tptr))
909      mptr = tptr;
910    tptr++;
911  }
912  /* Null pointer protection */
913  if(mptr)
914    return mptr->pixel;
915  else
916    return WhitePixel(dpy, scr);
917}
918
919/*
920 * GetMinIntensity() -- Find the minimum intensity pixel value for a colormap.
921 */
922static Pixel
923GetMinIntensity(hlPtr data)
924{
925  XColor *colors = NULL, *mptr, *tptr;
926  int i, ncolors;
927
928  if (data->win_info.colormap == DefaultColormap(dpy, scr))
929    return BlackPixel(dpy, scr);
930  ncolors = Get_XColors(&data->win_info, &colors);
931  mptr = tptr = colors; tptr++;
932  for (i=1; i<ncolors; i++)  {
933    if ((int)Intensity(mptr) > (int)Intensity(tptr))
934      mptr = tptr;
935    tptr++;
936  }
937  /* Null pointer protection */
938  if(mptr)
939    return mptr->pixel;
940  else
941    return BlackPixel(dpy, scr);
942}
943
944
945
946
947static Widget pane1, pane2, pane3, cclose, replace, new, select_w, paste;
948
949/*
950 * PopupNewScale() -- Create and popup a new scale composite.
951 */
952static void
953PopupNewScale(hlPtr data)
954{
955  Arg warg;
956
957  data->scaleShell =
958    XtVaCreatePopupShell("xmag", topLevelShellWidgetClass, toplevel,
959			 XtNgeometry, (XtArgVal)options.geometry,
960			 XtNtitle, (XtArgVal)options.title,
961			 NULL);
962  pane1 = XtCreateManagedWidget("pane1", panedWidgetClass, data->scaleShell,
963				(Arg *) NULL, 0);
964  pane2 = XtCreateManagedWidget("pane2", panedWidgetClass, pane1,
965				(Arg *) NULL, 0);
966  cclose = XtCreateManagedWidget("close", commandWidgetClass, pane2,
967				 (Arg *) NULL, 0);
968  XtAddCallback(cclose, XtNcallback, CloseCB, (XtPointer)data->scaleShell);
969  replace = XtCreateManagedWidget("replace", commandWidgetClass, pane2,
970				  (Arg *) NULL, 0);
971  XtAddCallback(replace, XtNcallback, ReplaceCB, (XtPointer)data);
972  new = XtCreateManagedWidget("new", commandWidgetClass, pane2,
973			      (Arg *) NULL, 0);
974  XtAddCallback(new, XtNcallback, NewCB, (XtPointer)NULL);
975  select_w = XtCreateManagedWidget("select", commandWidgetClass, pane2,
976			      (Arg *) NULL, 0);
977  XtAddCallback(select_w, XtNcallback, SelectCB, (XtPointer)data);
978  paste = XtCreateManagedWidget("paste", commandWidgetClass, pane2,
979			      (Arg *) NULL, 0);
980  XtAddCallback(paste, XtNcallback, PasteCB, (XtPointer)data);
981  (void) XtCreateManagedWidget("helpLabel", labelWidgetClass, pane2,
982			       (Arg *) NULL, 0);
983  pane3 = XtCreateManagedWidget("pane2", panedWidgetClass, pane1,
984				(Arg *) NULL, 0);
985  data->scaleInstance =
986    XtVaCreateManagedWidget("scale", scaleWidgetClass,
987			    pane3,
988			    XtNvisual, (XtArgVal)data->win_info.visual,
989			    XtNcolormap, (XtArgVal)data->win_info.colormap,
990			    XtNdepth, (XtArgVal)data->win_info.depth,
991			    XtNscaleX, (XtArgVal)options.mag,
992			    XtNscaleY, (XtArgVal)options.mag,
993			    NULL);
994  SWSetImage(data->scaleInstance, data->image);
995  XtOverrideTranslations
996    (data->scaleShell,
997     XtParseTranslationTable ("<Message>WM_PROTOCOLS: close()"));
998  XtSetArg(warg, XtNuserData, data);
999  XtSetValues(data->scaleInstance, &warg, 1);
1000  data->pixShell =
1001    XtVaCreatePopupShell("pixShell", overrideShellWidgetClass,
1002			 toplevel,
1003			 XtNvisual, (XtArgVal)data->win_info.visual,
1004			 XtNcolormap, (XtArgVal)data->win_info.colormap,
1005			 XtNdepth, (XtArgVal)data->win_info.depth,
1006			 XtNborderWidth, (XtPointer)0,
1007			 NULL);
1008  data->pixLabel =
1009    XtVaCreateManagedWidget("pixLabel", labelWidgetClass,
1010			    data->pixShell,
1011			    XtNforeground, (XtPointer)GetMaxIntensity(data),
1012			    XtNbackground, (XtPointer)GetMinIntensity(data),
1013			    XtNborderWidth, (XtPointer)0,
1014			    NULL);
1015  XtInstallAllAccelerators(pane1, pane1);	/* install accelerators */
1016  if (data->newScale) {
1017    XtPopup(data->scaleShell, XtGrabNone);
1018    (void) XSetWMProtocols	/* ICCCM delete window */
1019      (dpy, XtWindow(data->scaleShell), &wm_delete_window, 1);
1020  }
1021  if (data->win_info.colormap != DefaultColormap(dpy, scr)) {
1022    data->cmapWinList[0] = data->scaleShell;
1023    data->cmapWinList[1] = data->scaleInstance;
1024    XtSetWMColormapWindows(data->scaleShell, data->cmapWinList, 2);
1025  }
1026}
1027
1028
1029
1030/*
1031 * RedoOldScale() -- If the visual, depth, or colormap has changed, unrealize
1032 *                   the scale widget and change its colormap/depth/visual.
1033 *                   Then re-realize it.  Also do this for the pixel display
1034 *                   widget.
1035 */
1036static void
1037RedoOldScale(hlPtr data)
1038{
1039  Arg wargs[3];
1040  int n;
1041  Visual *oldVis;
1042  int oldDepth;
1043  Colormap oldCmap;
1044
1045  n=0;
1046  XtSetArg(wargs[n], XtNvisual, &oldVis); n++;
1047  XtSetArg(wargs[n], XtNdepth, &oldDepth); n++;
1048  XtSetArg(wargs[n], XtNcolormap, &oldCmap); n++;
1049  XtGetValues(data->scaleInstance, wargs, n);
1050  if (oldVis == data->win_info.visual && oldDepth == data->win_info.depth
1051      && oldCmap == data->win_info.colormap) {
1052    SWSetImage(data->scaleInstance, data->image);
1053    return;
1054  }
1055  /* get width and height, save and reuse them */
1056  XtUnmanageChild(data->scaleInstance);
1057  XtUnrealizeWidget(data->scaleInstance);
1058  n=0;
1059  XtSetArg(wargs[n], XtNcolormap, data->win_info.colormap); n++;
1060  XtSetArg(wargs[n], XtNdepth, data->win_info.depth); n++;
1061  XtSetArg(wargs[n], XtNvisual, data->win_info.visual); n++;
1062  XtSetValues(data->scaleInstance, wargs, n);
1063  n=0;
1064  XtSetArg(wargs[n], XtNforeground, GetMaxIntensity(data)); n++;
1065  XtSetArg(wargs[n], XtNbackground, GetMinIntensity(data)); n++;
1066  XtSetValues(data->pixLabel, wargs, n);
1067  SWSetImage(data->scaleInstance, data->image);
1068  XtRealizeWidget(data->scaleInstance);
1069  XtManageChild(data->scaleInstance);
1070}
1071
1072
1073
1074/*
1075 * InitCursors() -- Create our cursors for area selection.
1076 */
1077static void
1078InitCursors(void)
1079{
1080  ulAngle = XCreateFontCursor(dpy, XC_ul_angle);
1081  urAngle = XCreateFontCursor(dpy, XC_ur_angle);
1082  lrAngle = XCreateFontCursor(dpy, XC_lr_angle);
1083  llAngle = XCreateFontCursor(dpy, XC_ll_angle);
1084}
1085
1086
1087
1088/*
1089 * ParseSourceGeom() -- Determine dimensions of area to magnify from resources.
1090 */
1091static void
1092ParseSourceGeom(void)
1093{
1094				/* source */
1095  srcStat =
1096    XParseGeometry(options.source, &srcX, &srcY, &srcWidth, &srcHeight);
1097  if (!srcWidth) srcWidth = SRCWIDTH;
1098  if (!srcHeight) srcHeight = SRCHEIGHT;
1099  if (XNegative & srcStat) srcX = DisplayWidth(dpy, scr) + srcX - srcWidth;
1100  if (YNegative & srcStat) srcY = DisplayHeight(dpy, scr) + srcY - srcHeight;
1101				/* mag */
1102}
1103
1104
1105
1106/*
1107 * Main program.
1108 */
1109int
1110main(int argc, char *argv[])
1111{
1112  XSetErrorHandler(Error);
1113
1114				/* SUPPRESS 594 */
1115  toplevel = XtAppInitialize(&app, "Xmag", optionDesc, XtNumber(optionDesc),
1116			     &argc, argv, NULL,
1117			     NULL, 0);
1118
1119  dpy = XtDisplay(toplevel);
1120  scr = DefaultScreen(dpy);
1121  XtGetApplicationResources(toplevel, (XtPointer) &options, resources,
1122			    XtNumber(resources), NULL, 0);
1123  if (argc != 1) {
1124    fprintf (stderr,
1125	    "usage:  xmag [-source geom] [-mag magfactor] [-toolkitoption]\n");
1126    exit(1);
1127  }
1128
1129
1130  ParseSourceGeom();
1131  XtAppAddActions(app, actions_table, XtNumber(actions_table));
1132  InitCursors();
1133  SetupGC();
1134  CreateRoot();
1135  if (!(XValue & srcStat && YValue & srcStat))
1136    StartRootPtrGrab(True, (hlPtr)NULL);
1137  wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
1138  XtAppMainLoop(app);
1139  exit(0);
1140}
1141