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