xmag.c revision 8f65982a
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 magnifing 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	sprintf(string, "Pixel %ld at (%d,%d) colored (%x,%x,%x).",
370		pixel, x + data->x, y + data->y,
371		color.red, color.green, color.blue);
372	n = 0;
373	XtSetArg(wargs[n], XtNlabel, string); n++;
374	XtSetValues(data->pixLabel, wargs, n);
375	XtPopup(data->pixShell, XtGrabNone);
376    }
377}
378
379
380
381/*
382 * PopdownPixelAP() -- Remove pixel info.
383 */
384static void			/* ARGSUSED */
385PopdownPixelAP(Widget w, XEvent *event, String *params, Cardinal *num_params)
386{
387  int n;
388  Arg wargs[3];
389  hlPtr data = NULL;
390
391  n = 0;
392  XtSetArg(wargs[0], XtNuserData, &data); n++;
393  XtGetValues(w, wargs, n);
394
395  if (data)
396    XtPopdown(data->pixShell);
397}
398
399
400
401static void			/* ARGSUSED */
402SelectRegionAP(Widget w, XEvent *event, String *params, Cardinal *num_params)
403{
404/***** NOT SURE WHAT TO DO WITH THIS
405    if (app_resources.unmap)
406	XtUnmapWidget(toplevel);
407    Redisplay(XtDisplay(w), RootWindow(XtDisplay(w),
408				       DefaultScreen(XtDisplay(w))),
409	      source.width, source.height,
410	      app_resources.freq, app_resources.puls,
411	      ul_angle, lr_angle,
412	      app_resources.grab);
413
414    if (app_resources.unmap)
415	XtMapWidget(toplevel);
416******/
417}
418
419
420
421/*
422 * CheckPoints() -- Change the cursor for the correct quadrant.
423 *                  Make sure the first point is less than the second
424 *                  for drawing the selection rectangle.
425 *
426 */
427static void
428CheckPoints(Position *x1, Position *x2, Position *y1, Position *y2)
429{
430  Position tmp;
431  Boolean above, left;
432  Cursor newC;
433  above = (*y2 < *y1); left = (*x2 < *x1);
434  if (above&&left) newC = ulAngle;
435  else if (above&&!left) newC = urAngle;
436  else if (!above&&!left) newC = lrAngle;
437  else newC = llAngle;
438  XChangeActivePointerGrab
439    (dpy, PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
440     newC, CurrentTime);
441  if (*x2 < *x1) { tmp = *x1; *x1 = *x2; *x2 = tmp; }
442  if (*y2 < *y1) { tmp = *y1; *y1 = *y2; *y2 = tmp; }
443}
444
445
446
447/*
448 * HighlightTO() -- Timer to highlight the selection box
449 */
450static void
451HighlightTO(XtPointer closure, XtIntervalId *id)	/* ARGSUSED */
452{
453  hlPtr data = (hlPtr)closure;
454  XGrabServer(dpy);
455  if (data->selectMode == drag) {
456    XDrawRectangle(dpy, DefaultRootWindow(dpy), data->gc,
457		   data->x, data->y, data->width, data->height);
458    XFlush(dpy);
459    HLSLEEP;
460    XDrawRectangle(dpy, DefaultRootWindow(dpy), data->gc,
461		   data->x, data->y, data->width, data->height);
462  }
463  else if (data->selectMode == resize) {
464    Position x1 = data->homeX,
465             x2 = data->x,
466             y1 = data->homeY,
467             y2 = data->y;
468    CheckPoints(&x1, &x2, &y1, &y2);
469    XDrawRectangle(dpy, DefaultRootWindow(dpy), data->gc,
470		   x1, y1, x2 - x1, y2 - y1);
471    XFlush(dpy);
472    HLSLEEP;
473    XDrawRectangle(dpy, DefaultRootWindow(dpy), data->gc,
474		   x1, y1, x2 - x1, y2 - y1);
475  }
476  XUngrabServer(dpy);
477  if (data->selectMode != done)
478    XtAppAddTimeOut(app, HLINTERVAL, HighlightTO, (XtPointer)data);
479}
480
481
482
483/*
484 * CloseCB() -- Delete this xmag dialog.  If its the only one on the screen
485 *             then exit.
486 */
487static void			/* ARGSUSED */
488CloseCB(Widget w, XtPointer clientData, XtPointer callData)
489{
490  Widget shell = (Widget)clientData;
491  if (!--numXmags) exit(0);
492  XtPopdown(shell);
493  XtDestroyWidget(shell);
494}
495
496
497
498/*
499 * ReplaceCB() -- Replace this particular xmag dialog.
500 */
501static void                     /* ARGSUSED */
502ReplaceCB(Widget w, XtPointer clientData, XtPointer callData)
503{
504  hlPtr data = (hlPtr)clientData;
505  StartRootPtrGrab(False, data);
506}
507
508
509
510/*
511 * NewCB() -- Create an additional xmag dialog.
512 */
513static void			/* ARGSUSED */
514NewCB(Widget w, XtPointer clientData, XtPointer callData)
515{
516  StartRootPtrGrab(True, NULL);
517}
518
519
520
521/*
522 * SelectCB() -- Own the primary selection.
523 */
524static void			/* ARGSUSED */
525SelectCB(Widget w, XtPointer clientData, XtPointer callData)
526{
527  hlPtr data = (hlPtr)clientData;
528  SWGrabSelection(data->scaleInstance, XtLastTimestampProcessed(dpy));
529}
530
531
532
533/*
534 * PasteCB() -- Paste from the primary selectin into xmag.
535 */
536static void			/* ARGSUSED */
537PasteCB(Widget w, XtPointer clientData, XtPointer callData)
538{
539  hlPtr data = (hlPtr)clientData;
540  SWRequestSelection(data->scaleInstance, XtLastTimestampProcessed(dpy));
541}
542
543
544
545/*
546 * SetupGC() -- Graphics context for magnification selection.
547 */
548static void
549SetupGC(void)
550{
551    selectGCV.function = GXxor;
552    selectGCV.foreground = 0xffffffff;
553    selectGCV.subwindow_mode = IncludeInferiors;
554    selectGC = XtGetGC(toplevel, GCFunction|GCForeground|GCSubwindowMode,
555		       &selectGCV);
556}
557
558
559
560/*
561 * FindWindow() -- Determin window the pointer is over.
562 *
563 */
564static Window
565FindWindow(int x, int y)	/* Locatation of cursor */
566{
567  XWindowAttributes wa;
568  Window findW = DefaultRootWindow(dpy), stopW, childW;
569
570  /* Setup for first window find */
571  stopW = findW;
572
573  while (stopW) {
574    XTranslateCoordinates(dpy, findW, stopW,
575			  x, y, &x, &y, &childW);
576    findW = stopW;
577    /* If child is not InputOutput (for example, InputOnly) */
578    /* then don't continue, return the present findW which */
579    /* can be the root, or a root child of class InputOutput */
580    if (childW &&
581	XGetWindowAttributes(dpy, childW, &wa) &&
582	wa.class != InputOutput)
583	break;
584    stopW = childW;
585  }
586  return findW;
587}
588
589
590
591/*
592 * ResizeEH() -- Event Handler for resize of selection box.
593 */
594static void
595ResizeEH(Widget w, XtPointer closure, XEvent *event,
596	 Boolean *continue_to_dispatch)	/* ARGSUSED */
597{
598  hlPtr data = (hlPtr)closure;
599  switch (event->type) {
600  case MotionNotify:
601    data->x = event->xmotion.x_root;
602    data->y = event->xmotion.y_root;
603    break;
604  case ButtonRelease:
605    GetImageAndAttributes(FindWindow(event->xmotion.x_root,
606			event->xmotion.y_root),
607	     min(data->homeX,event->xbutton.x_root),
608	     min(data->homeY,event->xbutton.y_root),
609	     abs(data->homeX - event->xbutton.x_root),
610	     abs(data->homeY - event->xbutton.y_root),
611	     data);
612    if (data->newScale)
613      PopupNewScale(data);
614    else
615      SWSetImage(data->scaleInstance, data->image);
616    XtUngrabPointer(w, CurrentTime);
617/*****
618    XtRemoveRawEventHandler(w, PointerMotionMask|ButtonReleaseMask,
619			 True, ResizeEH, (XtPointer)data);
620*****/
621    XtRemoveEventHandler(w, PointerMotionMask|ButtonReleaseMask,
622			 True, ResizeEH, (XtPointer)data);
623    data->selectMode = done;
624    break;
625  }
626}
627
628
629
630/*
631 * DragEH() -- Event Handler for draging selection box.
632 */
633static void
634DragEH(Widget w, XtPointer closure, XEvent *event,
635       Boolean *continue_to_dispatch) /* ARGSUSED */
636{
637  hlPtr data = (hlPtr)closure;
638  switch (event->type) {
639  case MotionNotify:		/* drag mode */
640    data->x = event->xmotion.x_root;
641    data->y = event->xmotion.y_root;
642    break;
643  case ButtonRelease:		/* end drag mode */
644    if (event->xbutton.button == Button1) { /* get image */
645      /* Problem: You can't get bits with XGetImage outside of its window.
646       *          xmag will only do a GetImage on the actual window in the case
647       *          where the depth of the window does not match the depth of
648       *          the root window.
649       */
650      GetImageAndAttributes(FindWindow(event->xmotion.x_root,
651			  event->xmotion.y_root),
652	       event->xbutton.x_root,
653	       event->xbutton.y_root,
654	       srcWidth, srcHeight, data);
655      if (data->newScale)
656	PopupNewScale(data);
657      else
658	RedoOldScale(data);
659      XtUngrabPointer(w, CurrentTime);
660      XtRemoveRawEventHandler(w, PointerMotionMask|ButtonPressMask|
661			      ButtonReleaseMask, True, DragEH,
662			      (XtPointer)data);
663      data->selectMode = done;
664    }
665
666    break;
667  case ButtonPress:
668    if (event->xbutton.button == Button2) {	/* turn on resize mode */
669      data->homeX = event->xbutton.x_root;
670      data->homeY = event->xbutton.y_root;
671      data->x = event->xbutton.x_root + srcWidth;
672      data->y = event->xbutton.y_root + srcHeight;
673      data->selectMode = resize;
674      XtRemoveRawEventHandler(w, PointerMotionMask|ButtonPressMask|
675			   ButtonReleaseMask, True, DragEH, (XtPointer)data);
676      XChangeActivePointerGrab
677	(dpy, PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
678	 lrAngle, CurrentTime);
679      XWarpPointer(dpy, None, None, 0, 0, 0, 0,
680		   srcWidth, srcHeight);
681      XtAddEventHandler(w, PointerMotionMask|ButtonReleaseMask,
682			True, ResizeEH, (XtPointer)data);
683    }
684    break;
685  }
686}
687
688
689
690
691/*
692 * StartRootPtrGrab() -- Bring up the selection box.
693 *
694 */
695static void
696StartRootPtrGrab(int new, 	/* do we cretate a new scale instance? */
697		 hlPtr data)	/* highligh data */
698{
699  Window    rootR, childR;
700  int       rootX, rootY, winX, winY;
701  unsigned  int mask;
702  hlPtr hlData;
703  XtGrabPointer
704    (root, False,
705     PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
706     GrabModeAsync, GrabModeAsync, None, ulAngle, CurrentTime);
707  XQueryPointer(dpy, DefaultRootWindow(dpy), &rootR, &childR,
708		&rootX, &rootY, &winX, &winY, &mask);
709  if (new) {
710    numXmags++;
711    hlData = (hlPtr)XtMalloc(sizeof(hlStruct));
712  }
713  else hlData = data;
714  hlData->newScale   = new;
715  hlData->selectMode = drag;
716  hlData->x          = rootX;
717  hlData->y          = rootY;
718  hlData->gc         = selectGC;
719  hlData->width      = srcWidth;
720  hlData->height     = srcHeight;
721  XtAddRawEventHandler
722    (root, PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
723     True, DragEH, (XtPointer)hlData);
724  (void) XtAppAddTimeOut(app, HLINTERVAL, HighlightTO, (XtPointer)hlData);
725}
726
727
728
729/*
730 * CreateRoot() -- Create a root window widget. If the user specified x and y
731 *                 in his source geometry then use this to directly get the
732 *                 image.
733 */
734static void
735CreateRoot(void)
736{
737  hlPtr data;
738  root = XtCreateWidget("root", rootWindowWidgetClass, toplevel, NULL, 0);
739  XtRealizeWidget(root);
740  if (XValue & srcStat && YValue &srcStat) {
741    numXmags = 1;
742    data = (hlPtr)XtMalloc(sizeof(hlStruct));
743    data = data;
744    data->newScale   = True;
745    data->selectMode = drag;
746    data->x          = srcX;
747    data->y          = srcY;
748    data->gc         = selectGC;
749    data->width      = srcWidth;
750    data->height     = srcHeight;
751    GetImageAndAttributes(RootWindow(dpy, scr), srcX, srcY, srcWidth,
752			  srcHeight, data);
753    PopupNewScale(data);
754    return;
755  }
756}
757
758
759/*
760 * GetImageAndAttributes() -- Get the image bits from the screen.
761 *               We will also determin here the colormap, depth, and
762 *               visual to be used for the magnification image.
763 */
764static void
765GetImageAndAttributes(Window w, int x, int y, int width, int height,
766		      hlPtr data)
767{
768    /* get parameters of window being magnified */
769    XGetWindowAttributes(dpy, w, &data->win_info);
770
771    if (data->win_info.depth == DefaultDepth(dpy, scr)) {
772	/* avoid off screen pixels */
773	if (x < 0)
774	    x = 0;
775	if (y < 0)
776	    y = 0;
777	if (x + width > DisplayWidth(dpy,scr))
778	    x = DisplayWidth(dpy,scr) - width;
779	if (y + height > DisplayHeight(dpy,scr))
780	    y = DisplayHeight(dpy,scr) - height;
781	data->x = x; data->y = y;
782	/* get image pixels */
783	data->image = XGetImage (dpy,
784				 RootWindow(dpy, scr),
785				 x, y,
786				 width, height,
787				 AllPlanes, ZPixmap);
788    }
789    else {
790	int	t0, t1;
791	int	x0, x1, y0, y1;
792	int	xInWin, yInWin;
793	Window	childWin;
794
795	XTranslateCoordinates(dpy, DefaultRootWindow(dpy), w, x, y,
796			      &xInWin, &yInWin, &childWin);
797
798	/* Avoid off screen pixels. Assume this routine is not
799	 * called for totally offscreen windows. */
800	x0 = max(x, 0);
801	y0 = max(y, 0);
802	x1 = min(DisplayWidth(dpy, scr),
803		 min(x0 + width, x0 + (data->win_info.width - xInWin)));
804	y1 = min(DisplayHeight(dpy, scr),
805		 min(y0 + height, y0 + (data->win_info.height - yInWin)));
806
807	/* Try to use up to width x height pixels */
808	if (x1 - x0 < width) {
809	    t0 = x0;
810	    t1 = max(0, x - xInWin + data->win_info.width -
811		     DisplayWidth(dpy, scr));
812	    x0 = max(0, x1 - min(width, data->win_info.width - t1));
813	    xInWin -= t0 - x0;
814	}
815	if (y1 - y0 < height) {
816	    t0 = y0;
817	    t1 = max(0, y - yInWin + data->win_info.height -
818		     DisplayHeight(dpy, scr));
819	    y0 = max(0, y1 - min(height, data->win_info.height - t1));
820	    yInWin -= t0 - y0;
821	}
822
823	data->x = x0;
824	data->y = y0;
825	data->width = x1 - x0;
826	data->height = y1 - y0;
827
828	data->image = XGetImage (dpy,
829				 w,
830				 xInWin, yInWin,
831				 data->width, data->height,
832				 AllPlanes, ZPixmap);
833
834    }
835}
836
837
838
839/*
840 * Get_XColors() Get the XColors of all pixels in image - returns # of colors
841 *               This function was taken from xwd (thanks Bob...)
842 */
843#define lowbit(x) ((x) & (~(x) + 1))
844static int
845Get_XColors(XWindowAttributes *win_info, XColor **colors)
846{
847    int i, ncolors;
848
849    if (!win_info->colormap)
850        return(0);
851
852    ncolors = win_info->visual->map_entries;
853    if (!(*colors = (XColor *) XtMalloc (sizeof(XColor) * ncolors)))
854      XtError("Out of memory!");
855
856    if (win_info->visual->class == DirectColor ||
857        win_info->visual->class == TrueColor) {
858        Pixel red, green, blue, red1, green1, blue1;
859
860        red = green = blue = 0;
861        red1 = lowbit(win_info->visual->red_mask);
862        green1 = lowbit(win_info->visual->green_mask);
863        blue1 = lowbit(win_info->visual->blue_mask);
864        for (i=0; i<ncolors; i++) {
865          (*colors)[i].pixel = red|green|blue;
866          (*colors)[i].pad = 0;
867          red += red1;
868          if (red > win_info->visual->red_mask)
869            red = 0;
870          green += green1;
871          if (green > win_info->visual->green_mask)
872            green = 0;
873          blue += blue1;
874          if (blue > win_info->visual->blue_mask)
875            blue = 0;
876        }
877    } else {
878        for (i=0; i<ncolors; i++) {
879          (*colors)[i].pixel = i;
880          (*colors)[i].pad = 0;
881        }
882    }
883
884    XQueryColors(dpy, win_info->colormap, *colors, ncolors);
885
886    return(ncolors);
887}
888
889
890
891#define Intensity(cptr) (3.0*cptr->red+0.59*cptr->green+0.11*cptr->blue)
892
893/*
894 * GetMaxIntensity() -- Find the maximum intensity pixel value for a colormap.
895 */
896static Pixel
897GetMaxIntensity(hlPtr data)
898{
899  XColor *colors = NULL, *mptr, *tptr;
900  int i, ncolors;
901
902  if (data->win_info.colormap == DefaultColormap(dpy, scr))
903    return WhitePixel(dpy, scr);
904  ncolors = Get_XColors(&data->win_info, &colors);
905  mptr = tptr = colors; tptr++;
906  for (i=1; i<ncolors; i++) {
907    if ((int)Intensity(mptr) < (int)Intensity(tptr))
908      mptr = tptr;
909    tptr++;
910  }
911  /* Null pointer protection */
912  if(mptr)
913    return mptr->pixel;
914  else
915    return WhitePixel(dpy, scr);
916}
917
918/*
919 * GetMinIntensity() -- Find the minimum intensity pixel value for a colormap.
920 */
921static Pixel
922GetMinIntensity(hlPtr data)
923{
924  XColor *colors = NULL, *mptr, *tptr;
925  int i, ncolors;
926
927  if (data->win_info.colormap == DefaultColormap(dpy, scr))
928    return BlackPixel(dpy, scr);
929  ncolors = Get_XColors(&data->win_info, &colors);
930  mptr = tptr = colors; tptr++;
931  for (i=1; i<ncolors; i++)  {
932    if ((int)Intensity(mptr) > (int)Intensity(tptr))
933      mptr = tptr;
934    tptr++;
935  }
936  /* Null pointer protection */
937  if(mptr)
938    return mptr->pixel;
939  else
940    return BlackPixel(dpy, scr);
941}
942
943
944
945
946static Widget pane1, pane2, pane3, cclose, replace, new, select_w, paste;
947
948/*
949 * PopupNewScale() -- Create and popup a new scale composite.
950 */
951static void
952PopupNewScale(hlPtr data)
953{
954  Arg warg;
955
956  data->scaleShell =
957    XtVaCreatePopupShell("xmag", topLevelShellWidgetClass, toplevel,
958			 XtNgeometry, (XtArgVal)options.geometry,
959			 XtNtitle, (XtArgVal)options.title,
960			 NULL);
961  pane1 = XtCreateManagedWidget("pane1", panedWidgetClass, data->scaleShell,
962				(Arg *) NULL, 0);
963  pane2 = XtCreateManagedWidget("pane2", panedWidgetClass, pane1,
964				(Arg *) NULL, 0);
965  cclose = XtCreateManagedWidget("close", commandWidgetClass, pane2,
966				 (Arg *) NULL, 0);
967  XtAddCallback(cclose, XtNcallback, CloseCB, (XtPointer)data->scaleShell);
968  replace = XtCreateManagedWidget("replace", commandWidgetClass, pane2,
969				  (Arg *) NULL, 0);
970  XtAddCallback(replace, XtNcallback, ReplaceCB, (XtPointer)data);
971  new = XtCreateManagedWidget("new", commandWidgetClass, pane2,
972			      (Arg *) NULL, 0);
973  XtAddCallback(new, XtNcallback, NewCB, (XtPointer)NULL);
974  select_w = XtCreateManagedWidget("select", commandWidgetClass, pane2,
975			      (Arg *) NULL, 0);
976  XtAddCallback(select_w, XtNcallback, SelectCB, (XtPointer)data);
977  paste = XtCreateManagedWidget("paste", commandWidgetClass, pane2,
978			      (Arg *) NULL, 0);
979  XtAddCallback(paste, XtNcallback, PasteCB, (XtPointer)data);
980  (void) XtCreateManagedWidget("helpLabel", labelWidgetClass, pane2,
981			       (Arg *) NULL, 0);
982  pane3 = XtCreateManagedWidget("pane2", panedWidgetClass, pane1,
983				(Arg *) NULL, 0);
984  data->scaleInstance =
985    XtVaCreateManagedWidget("scale", scaleWidgetClass,
986			    pane3,
987			    XtNvisual, (XtArgVal)data->win_info.visual,
988			    XtNcolormap, (XtArgVal)data->win_info.colormap,
989			    XtNdepth, (XtArgVal)data->win_info.depth,
990			    XtNscaleX, (XtArgVal)options.mag,
991			    XtNscaleY, (XtArgVal)options.mag,
992			    NULL);
993  SWSetImage(data->scaleInstance, data->image);
994  XtOverrideTranslations
995    (data->scaleShell,
996     XtParseTranslationTable ("<Message>WM_PROTOCOLS: close()"));
997  XtSetArg(warg, XtNuserData, data);
998  XtSetValues(data->scaleInstance, &warg, 1);
999  data->pixShell =
1000    XtVaCreatePopupShell("pixShell", overrideShellWidgetClass,
1001			 toplevel,
1002			 XtNvisual, (XtArgVal)data->win_info.visual,
1003			 XtNcolormap, (XtArgVal)data->win_info.colormap,
1004			 XtNdepth, (XtArgVal)data->win_info.depth,
1005			 XtNborderWidth, (XtPointer)0,
1006			 NULL);
1007  data->pixLabel =
1008    XtVaCreateManagedWidget("pixLabel", labelWidgetClass,
1009			    data->pixShell,
1010			    XtNforeground, (XtPointer)GetMaxIntensity(data),
1011			    XtNbackground, (XtPointer)GetMinIntensity(data),
1012			    XtNborderWidth, (XtPointer)0,
1013			    NULL);
1014  XtInstallAllAccelerators(pane1, pane1);	/* install accelerators */
1015  if (data->newScale) {
1016    XtPopup(data->scaleShell, XtGrabNone);
1017    (void) XSetWMProtocols	/* ICCCM delete window */
1018      (dpy, XtWindow(data->scaleShell), &wm_delete_window, 1);
1019  }
1020  if (data->win_info.colormap != DefaultColormap(dpy, scr)) {
1021    data->cmapWinList[0] = data->scaleShell;
1022    data->cmapWinList[1] = data->scaleInstance;
1023    XtSetWMColormapWindows(data->scaleShell, data->cmapWinList, 2);
1024  }
1025}
1026
1027
1028
1029/*
1030 * RedoOldScale() -- If the visual, depth, or colormap has changed, unrealize
1031 *                   the scale widget and change its colormap/depth/visual.
1032 *                   Then re-realize it.  Also do this for the pixel display
1033 *                   widget.
1034 */
1035static void
1036RedoOldScale(hlPtr data)
1037{
1038  Arg wargs[3];
1039  int n;
1040  Visual *oldVis;
1041  int oldDepth;
1042  Colormap oldCmap;
1043
1044  n=0;
1045  XtSetArg(wargs[n], XtNvisual, &oldVis); n++;
1046  XtSetArg(wargs[n], XtNdepth, &oldDepth); n++;
1047  XtSetArg(wargs[n], XtNcolormap, &oldCmap); n++;
1048  XtGetValues(data->scaleInstance, wargs, n);
1049  if (oldVis == data->win_info.visual && oldDepth == data->win_info.depth
1050      && oldCmap == data->win_info.colormap) {
1051    SWSetImage(data->scaleInstance, data->image);
1052    return;
1053  }
1054  /* get width and height, save and reuse them */
1055  XtUnmanageChild(data->scaleInstance);
1056  XtUnrealizeWidget(data->scaleInstance);
1057  n=0;
1058  XtSetArg(wargs[n], XtNcolormap, data->win_info.colormap); n++;
1059  XtSetArg(wargs[n], XtNdepth, data->win_info.depth); n++;
1060  XtSetArg(wargs[n], XtNvisual, data->win_info.visual); n++;
1061  XtSetValues(data->scaleInstance, wargs, n);
1062  n=0;
1063  XtSetArg(wargs[n], XtNforeground, GetMaxIntensity(data)); n++;
1064  XtSetArg(wargs[n], XtNbackground, GetMinIntensity(data)); n++;
1065  XtSetValues(data->pixLabel, wargs, n);
1066  SWSetImage(data->scaleInstance, data->image);
1067  XtRealizeWidget(data->scaleInstance);
1068  XtManageChild(data->scaleInstance);
1069}
1070
1071
1072
1073/*
1074 * InitCursors() -- Create our cursors for area selection.
1075 */
1076static void
1077InitCursors(void)
1078{
1079  ulAngle = XCreateFontCursor(dpy, XC_ul_angle);
1080  urAngle = XCreateFontCursor(dpy, XC_ur_angle);
1081  lrAngle = XCreateFontCursor(dpy, XC_lr_angle);
1082  llAngle = XCreateFontCursor(dpy, XC_ll_angle);
1083}
1084
1085
1086
1087/*
1088 * ParseSourceGeom() -- Determin dimensions of area to magnify from resources.
1089 */
1090static void
1091ParseSourceGeom(void)
1092{
1093				/* source */
1094  srcStat =
1095    XParseGeometry(options.source, &srcX, &srcY, &srcWidth, &srcHeight);
1096  if (!srcWidth) srcWidth = SRCWIDTH;
1097  if (!srcHeight) srcHeight = SRCHEIGHT;
1098  if (XNegative & srcStat) srcX = DisplayWidth(dpy, scr) + srcX - srcWidth;
1099  if (YNegative & srcStat) srcY = DisplayHeight(dpy, scr) + srcY - srcHeight;
1100				/* mag */
1101}
1102
1103
1104
1105/*
1106 * Main program.
1107 */
1108int
1109main(int argc, char *argv[])
1110{
1111  XSetErrorHandler(Error);
1112
1113				/* SUPPRESS 594 */
1114  toplevel = XtAppInitialize(&app, "Xmag", optionDesc, XtNumber(optionDesc),
1115			     &argc, argv, NULL,
1116			     NULL, 0);
1117
1118  dpy = XtDisplay(toplevel);
1119  scr = DefaultScreen(dpy);
1120  XtGetApplicationResources(toplevel, (XtPointer) &options, resources,
1121			    XtNumber(resources), NULL, 0);
1122  if (argc != 1) {
1123    fprintf (stderr,
1124	    "usage:  xmag [-source geom] [-mag magfactor] [-toolkitoption]\n");
1125    exit(1);
1126  }
1127
1128
1129  ParseSourceGeom();
1130  XtAppAddActions(app, actions_table, XtNumber(actions_table));
1131  InitCursors();
1132  SetupGC();
1133  CreateRoot();
1134  if (!(XValue & srcStat && YValue & srcStat))
1135    StartRootPtrGrab(True, (hlPtr)NULL);
1136  wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
1137  XtAppMainLoop(app);
1138  exit(0);
1139}
1140