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