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