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