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