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