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