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