home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1995 August / NEBULA.mdf / SourceCode / MiniExamples / AppKit / Winfo / WinInfo.m < prev    next >
Encoding:
Text File  |  1992-12-09  |  15.7 KB  |  649 lines

  1. /*
  2.  * Winfo
  3.  * by Paul S. Kleppner
  4.  * modified by Ali Ozer 12/92 for 3.0 and to use dumpwindow operator for info.
  5.  *
  6.  * This program may be freely distributed, but not sold.
  7.  * It is provided without warranty of any kind, expressed or
  8.  * implied, as to its fitness for any particular use.
  9.  */
  10.  
  11. /*
  12.  * WinInfo class.  This is the controlling object of the
  13.  * application -- it manages the report panel, and does
  14.  * all the work.
  15.  */
  16.  
  17. #import <appkit/appkit.h>
  18. #import "WinInfo.h"
  19. #include "wwrap.h"
  20. #include <stdlib.h>
  21.  
  22. #import "WView.h"
  23. #import "WinApp.h"
  24.  
  25. #define DISPLAYWINDOWSLEVEL NX_NORMALLEVEL
  26. #define REPORTPANELLEVEL 1000
  27.  
  28. // Values for selected context...
  29. #define ALLCONTEXTS -2
  30. #define NOCONTEXT -1
  31.  
  32. // Display modes
  33. #define CONTENTSMODE 1
  34. #define TRANSPARENTMODE 2
  35. #define OUTLINEMODE 3
  36.  
  37. extern BOOL getWindowBytes (int windowNum, int *bytes, int *type);
  38.  
  39. @implementation WinInfo
  40.  
  41. + new
  42. {
  43.   self = [super new];
  44.   windowList = NULL;
  45.   wpList = NULL;
  46.   onScreenList = NULL;
  47.   displayWin = nil;
  48.   activeFlag = NO;
  49.  
  50.   myFont = [Font newFont:"Helvetica" size:10.0 style:0
  51.           matrix:NX_IDENTITYMATRIX];
  52.   if( [myFont screenFont] )
  53.     myFont = [myFont screenFont];
  54.  
  55.   [NXApp setDelegate:self];
  56.  
  57.   displayMode = OUTLINEMODE;
  58.   offScreenMode = NO;
  59.   highlightAlpha = highlightDepth = YES;
  60.   {
  61.     const char *tmp = NXGetDefaultValue([NXApp appName],"ShowNonRetained");
  62.     showNonretained = (tmp && tmp[0] == 'Y');
  63.   }
  64.   allContexts = NO;
  65.  
  66.   return self;
  67. }
  68.  
  69. // target of the start/stop button.
  70. - startStop:sender
  71. {
  72.   activeFlag = !activeFlag;
  73.  
  74.   if( activeFlag ) 
  75.     [self start:sender];
  76.   else
  77.     [self stop:sender];
  78.  
  79.   [timeDelay setEnabled:!activeFlag];
  80.  
  81.   return self;
  82. }
  83.  
  84. // Start things going.  Messaged when the user
  85. // has presed the "start" button.
  86. // sender is the start/stop button.
  87. - start:sender
  88. {
  89.   id vw;
  90.   NXRect r;
  91.   struct winfo *wp;
  92.   int i;
  93.  
  94.   int secs, osecs;
  95.   NXTrackingTimer myTimer;
  96.  
  97.   selectedContext = allContexts ? ALLCONTEXTS : NOCONTEXT;
  98.  
  99.   // Delay, if user requested
  100.   osecs = secs = [timeDelay intValue];
  101.  
  102.   if( secs > 0 ){
  103.     NXBeginTimer(&myTimer, 0.0, 1.0);
  104.     for( ; secs > 0; --secs ){
  105.       [NXApp getNextEvent:NX_TIMERMASK];
  106.       [timeDelay setIntValue:secs-1];
  107.       NXPing();
  108.     }
  109.     NXEndTimer(&myTimer);
  110.   }
  111.  
  112.   [self clearReport];
  113.  
  114.   // Get information on all windows on the system.
  115.   [self buildWindowList];
  116.  
  117.   // And get numbers of on-screen windows.
  118.   [self buildOnScreenList];
  119.  
  120.   // Look through the on-screen list, and set the winfo struct
  121.   // to record that each of these windows is on screen.
  122.   for( i = 0; i < numOnScreen; ++i ){
  123.     wp = [self findWinfoByNumber:onScreenList[i]];
  124.     if( wp != NULL )
  125.       wp->origOnScreen = wp->onScreenNow = YES;
  126.   }
  127.  
  128.   // Set up displayWin.  This is a window the size of the full screen,
  129.   // into which we draw the outlines or contents of all other windows.
  130.   // displayWin gets set in front of all other windows on the system
  131.   // (except the Winfo report panel), and hides them all.
  132.   // displayWin is a panel, so that we can avoid making it key
  133.   [NXApp getScreenSize:&r.size];
  134.   
  135.   //Initialize the origin of the rectangle
  136.   r.origin.x = r.origin.y = 0.0;
  137.   
  138.   displayWin = [Panel newContent:&r style:NX_PLAINSTYLE backing:NX_BUFFERED
  139.         buttonMask:0 defer:NO];
  140.   [displayWin setBecomeKeyOnlyIfNeeded:YES];        // it will never be key
  141.   vw = [WView newForClient:self];
  142.   
  143.   [displayWin setContentView:vw];
  144.  
  145.   // Put the report panel at the top-most level
  146.   [sender lockFocus];
  147.   mySetCurrentWindowLevel(REPORTPANELLEVEL);
  148.   [[sender window] orderFront:self];
  149.   [sender unlockFocus];
  150.  
  151.   if (selectedContext != NOCONTEXT) {
  152.     [self moveOnScreen:offScreenMode forContext:selectedContext];
  153.   }
  154.  
  155.   // Fill in display window with images of all other windows
  156.   [self showAllWindows];
  157.  
  158.   // Summarize info for all windows...
  159.   [self showReportSummaryForAll];
  160.  
  161.   // Put the display window one level beneath the report panel
  162.   // (but in front of everybody else)
  163.   [vw lockFocus];
  164.   mySetCurrentWindowLevel(REPORTPANELLEVEL-1);
  165.   [vw unlockFocus];
  166.  
  167.   [displayWin orderFront:self];
  168.  
  169.   [timeDelay setIntValue:osecs];
  170.  
  171.   // If there was a timer, we may not be active at this point;
  172.   // so activate us.
  173.   [NXApp activateSelf:YES];
  174.  
  175.   NXPing();
  176.  
  177.   return self;
  178. }
  179.  
  180. // Called when the stop button is pressed.  sender is that button.
  181. // Free up data structures, and the displayWin window.
  182.  
  183. - stop:sender
  184. {
  185.   [self clearOffScreen];
  186.  
  187.   if( windowList != NULL )
  188.     free(windowList);
  189.   if( wpList != NULL )
  190.     free(wpList);
  191.   [displayWin free];
  192.   windowList = NULL;
  193.   wpList = NULL;
  194.   displayWin = nil;
  195.  
  196.   [[sender window] orderFront:self];
  197.   mySetCurrentWindowLevel(NX_NORMALLEVEL);
  198.  
  199.   return self;
  200. }
  201.  
  202. // Mouse down proxy.  Called by the WView when a mouse down occurs
  203. // on the full-screen display window.  WView masks all other
  204. // windows (except the Winfo report panel), so it receives all mouse hits.
  205. // We figure out what window was at that point, and bring that window
  206. // to front (or send it to back if a command-click).  Then we
  207. // update the report panel appropriately.
  208. - mouseDownProxy:(NXEvent *)event
  209. {
  210.   struct winfo *wp;
  211.  
  212.   // Get the winfo for the window at the mouse-hit.
  213.   wp = [self mapPointToWindow:&event->location];
  214.  
  215.   if( wp != NULL ){
  216.     if( event->flags & NX_COMMANDMASK ){
  217.       // command-click: send to back
  218.       myOrderWindow(NX_BELOW, 0, wp->num);
  219.     } else if( event->flags & NX_ALTERNATEMASK ){
  220.       // alt-click: send to front
  221.       myOrderWindow(NX_ABOVE, 0, wp->num);
  222.     } else {
  223.       // regular click: send to front
  224.       if( offScreenMode && !allContexts && (wp->context != selectedContext )) {
  225.         [self moveOnScreen:NO forContext:selectedContext];
  226.         [self moveOnScreen:YES forContext:wp->context];
  227.         selectedContext = wp->context;
  228.       }
  229.       myOrderWindow(NX_ABOVE, 0, wp->num);
  230.       // Update report info for windows
  231.       [self showReportFor:wp];
  232.     }
  233.  
  234.     // Redisplay image of all windows, to reflect new order
  235.     [self showAllWindows];
  236.   }
  237.   return self;
  238. }
  239.  
  240.  
  241. // showAllWindows.  This contructs an image of all windows
  242. // on the displayWin which hides them all.  If outlineMode is set,
  243. // then we just draw outlines; otherwise, we fill in the images.
  244. - showAllWindows
  245. {
  246.   struct winfo *wp;
  247.   NXRect r;
  248.   int i;
  249.  
  250.   [[displayWin contentView] lockFocus];
  251.   [myFont set];
  252.  
  253.   [[displayWin contentView] getBounds:&r];
  254.  
  255.   if( displayMode != CONTENTSMODE ){
  256.     // clear background to dark gray
  257.     PSsetgray(NX_DKGRAY);
  258.     NXRectFill(&r);
  259.  
  260.     [self buildOnScreenList];
  261.     for( i = numOnScreen-1; i >= 0; --i ){
  262.       if( wp = [self findWinfoByNumber:onScreenList[i]] ){
  263.         if( !wp->isMe )
  264.           [self showOutlineWindow:wp];
  265.       }
  266.     }
  267.   } else {
  268.     fillBelowWin([self trueWinNumberFor:reportPanel],
  269.                  [self trueWinNumberFor:displayWin]);
  270.   }
  271.  
  272.   [[displayWin contentView] unlockFocus];
  273.  
  274.   [displayWin flushWindow];
  275.   NXPing();
  276.  
  277.   return self;
  278. }
  279.  
  280. - setReportPanel:anObject
  281. {
  282.     reportPanel = anObject;
  283.     [reportPanel setFrameAutosaveName:"Report Panel"];
  284.     return self;
  285. }
  286.  
  287. - (void)getDepthName:(NXWindowDepth)depth into:(char *)buffer
  288. {
  289.     switch (depth) {
  290.     case NX_TwoBitGrayDepth: sprintf (buffer, "2 bit gray"); break;
  291.     case NX_EightBitGrayDepth: sprintf (buffer, "8 bit gray"); break;
  292.     case NX_TwelveBitRGBDepth: sprintf (buffer, "12 bit RGB"); break;
  293.     case NX_TwentyFourBitRGBDepth: sprintf (buffer, "24 bit RGB"); break;
  294.     default: sprintf (buffer, "%d spp, %d bps", NXNumberOfColorComponents(NXColorSpaceFromDepth(depth)), NXBPSFromDepth(depth)); break;
  295.     }
  296. }
  297.  
  298. - changeHighlightOptions:sender
  299. {
  300.   highlightAlpha = [[sender findCellWithTag:0] state];
  301.   highlightDepth = [[sender findCellWithTag:1] state];
  302.   if( activeFlag ){
  303.     [self showAllWindows];
  304.   }
  305.   return self;
  306. }
  307.  
  308. - changeDisplayOptions:sender
  309. {
  310.   displayMode = [sender selectedTag];
  311.   if( activeFlag ){
  312.     [self showAllWindows];
  313.   }
  314.   return self;
  315. }
  316.  
  317. - changeOffScreenOptions:sender
  318. {
  319.   switch ([sender selectedTag]) {
  320.     case 1: offScreenMode = allContexts = NO; break;
  321.     case 2:    offScreenMode = YES; allContexts = NO; break;
  322.     case 3: offScreenMode = allContexts = YES; break;
  323.   }
  324.   selectedContext = allContexts ? ALLCONTEXTS : (reportedWindow ? reportedWindow->context : NOCONTEXT);
  325.   if( activeFlag ){
  326.     [self moveOnScreen:offScreenMode forContext:selectedContext];
  327.     [self showAllWindows];
  328.   }
  329.   return self;
  330. }
  331.  
  332. // Draw the outline for the given window.
  333. //
  334. - showOutlineWindow:(struct winfo *)wp
  335. {
  336.   char cbuf[128];
  337.   float borderWidth = (wp == reportedWindow) ? 5.0 : ((reportedWindow && (wp->context == reportedWindow->context)) ? 3.0 : 1.0);
  338.   
  339.   PSsetgray(NX_BLACK);
  340.  
  341.   if (displayMode == OUTLINEMODE) {
  342.     BOOL highlight = 
  343.         ((highlightAlpha && wp->hasAlpha ) ||
  344.          (highlightDepth && (wp->depth != NX_TwoBitGrayDepth)));
  345.     PSsetgray(highlight ? NX_LTGRAY : NX_WHITE );
  346.     NXRectFill (&wp->place);
  347.   } else {
  348.     PSsetgray(NX_WHITE );
  349.     PSsetalpha (0.16666667);
  350.     PScompositerect(NX_X(&wp->place),NX_Y(&wp->place),NX_WIDTH(&wp->place),NX_HEIGHT(&wp->place),NX_SOVER);
  351.     PSsetalpha (1.0);
  352.   }    
  353.   PSsetgray(NX_BLACK);
  354.   NXFrameRectWithWidth(&wp->place, borderWidth);
  355.  
  356.   PSmoveto(wp->place.origin.x + 5, wp->place.origin.y + wp->place.size.height - 13);
  357.   sprintf(cbuf, "%d", wp->num);
  358.   PSshow(cbuf);
  359.  
  360.   PSmoveto(wp->place.origin.x + 5, wp->place.origin.y + wp->place.size.height - 23);
  361.   if (wp->bytes >= 0) sprintf(cbuf, "%.1fkb", ((float) wp->bytes) / 1024);
  362.   else sprintf(cbuf, "?kb");
  363.   PSshow(cbuf);
  364.   
  365.   return self;
  366. }
  367.  
  368. // Given a point, find the winfo struct for the top-most window at
  369. // that point.  Returns NULL if not found.
  370. - (struct winfo *) mapPointToWindow:(NXPoint *)p
  371. {
  372.   int wnum;
  373.   float wx, wy;
  374.   int found;
  375.  
  376.   myFindWindow(p->x, p->y, NX_BELOW, [self trueWinNumberFor:displayWin],
  377.                &wx, &wy, &wnum, &found);
  378.   if( found )
  379.     return [self findWinfoByNumber:wnum];
  380.   else
  381.     return NULL;
  382. }
  383.  
  384. // Clear the report panel
  385. - clearReport
  386. {
  387.   [repBox setTitle:"No Window Selected"];
  388.   [repBox display];
  389.  
  390.   [repContext setStringValue:""];
  391.   [repSize setStringValue:""];
  392.   [repAlpha setStringValue:""];
  393.   [repDepth setStringValue:""];
  394.   [repDepthLimit setStringValue:""];
  395.   [repBackingStore setStringValue:""];
  396.   [repType setStringValue:""];
  397.   [repTotal setStringValue:""];
  398.   [repTotalBackingStore setStringValue:""];
  399.   [repAllTotal setStringValue:""];
  400.   [repAllTotalBackingStore setStringValue:""];
  401.  
  402.   reportedWindow = NULL;
  403.  
  404.   return self;
  405. }
  406.  
  407. // Display in the report panel a report for the 
  408. // window with the given winfo struct.
  409. - showReportFor:(struct winfo *)wp
  410. {
  411.     NXRect *rp;
  412.     char cbuf[128];
  413.     int i, tback, non, noff;
  414.     struct winfo *wp2;
  415.     
  416.     reportedWindow = wp;
  417.  
  418.     rp = &wp->place;
  419.  
  420.     // Set the selected report box title to specify this window's number
  421.     sprintf(cbuf, "Window #%d", wp->num);
  422.     [repBox setTitle:cbuf];
  423.     [repBox display];
  424.  
  425.     // Show context
  426.     sprintf(cbuf, "%u", (unsigned int)(wp->context));
  427.     [repContext setStringValue:cbuf];
  428.  
  429.     // Show size
  430.     sprintf(cbuf, "%.0f x %.0f", rp->size.width, rp->size.height);
  431.     [repSize setStringValue:cbuf];
  432.  
  433.     // Show alpha
  434.     if( wp->hasAlpha )
  435.     [repAlpha setStringValue:"Yes"];
  436.     else
  437.     [repAlpha setStringValue:"No"];
  438.  
  439.     // Show depth and colors
  440.     {
  441.     char buffer[100];
  442.     [self getDepthName:wp->depth into:buffer];
  443.     [repDepth setStringValue:buffer];
  444.     [self getDepthName:wp->depthLimit into:buffer];
  445.     [repDepthLimit setStringValue:buffer];
  446.     }
  447.  
  448.     // Show backing store
  449.     if (wp->bytes >= 0) [repBackingStore setIntValue:wp->bytes];
  450.     else [repBackingStore setStringValue:"Unknown"];
  451.  
  452.     switch (wp->type) {
  453.     case NX_NONRETAINED: [repType setStringValue:"Nonretained"]; break;
  454.     case NX_RETAINED: [repType setStringValue:"Retained"]; break;
  455.     case NX_BUFFERED: [repType setStringValue:"Buffered"]; break;
  456.     default: [repBackingStore setStringValue:"Unknown"]; break;
  457.     }
  458.  
  459.     // For all windows in same context, count number
  460.     // on and off-screen, and total their backing store
  461.  
  462.     non = noff = 0;
  463.     tback = 0;
  464.  
  465.     for( i = 0; i < numWins; ++i ){
  466.     wp2 = wpList + i;
  467.     if( wp2->context == wp->context ){
  468.         if (wp2->bytes >= 0) tback += wp2->bytes;
  469.         if( wp2->origOnScreen )
  470.         ++non;
  471.         else
  472.         ++noff;
  473.     }
  474.     }
  475.  
  476.     sprintf (cbuf, "%d windows (%d on, %d off)", non+noff, non, noff);    
  477.     [repTotal setStringValue:cbuf];
  478.     [repTotalBackingStore setIntValue:tback];
  479.   
  480.   return self;
  481. }
  482.  
  483. // The appkit assigns logical window number to each window, apparantly
  484. // for its convenience.  This function returns the "true" window
  485. // number assigned by the window server, given an Appkig window number.
  486. - (int) trueWinNumberFor:win
  487. {
  488.   int n;
  489.   id vw;
  490.  
  491.   vw = [win contentView];
  492.   [vw lockFocus];
  493.   PScurrentwindow(&n);
  494.   [vw unlockFocus];
  495.   return n;
  496. }
  497.  
  498. - clearOffScreen
  499. {
  500.   [self moveOnScreen:NO forContext:selectedContext];
  501.   [self showAllWindows];
  502.   return self;
  503. }
  504.  
  505. // how == YES to move off-screen windows on, else moves them back off
  506. // context is what context's windows to change
  507. - moveOnScreen:(BOOL)how forContext:(int)context
  508. {
  509.   int i;
  510.   struct winfo *wp;
  511.  
  512.   for( i = 0; i < numWins; ++i ){
  513.     wp = wpList + i;
  514.  
  515.     if( !wp->origOnScreen ){
  516.       BOOL show = ((wp->type != NX_NONRETAINED) || showNonretained) && ((context == wp->context) || (context == ALLCONTEXTS));
  517.       if( how && show ){
  518.         if( !wp->onScreenNow ){
  519.           if (wp->level > DISPLAYWINDOWSLEVEL) {
  520.             mySetCurrentWindowLevelOfWindow(wp->num, DISPLAYWINDOWSLEVEL);
  521.           }
  522.           myOrderWindow(NX_ABOVE, 0, wp->num);
  523.           wp->onScreenNow = YES;
  524.         }
  525.       } else {
  526.         if( wp->onScreenNow ){
  527.           myOrderWindow(NX_OUT, 0, wp->num);
  528.           if (wp->level > DISPLAYWINDOWSLEVEL) {
  529.             mySetCurrentWindowLevelOfWindow(wp->num, wp->level);
  530.           }
  531.           wp->onScreenNow = NO;
  532.         }
  533.       }
  534.     }
  535.   }
  536.   return self;
  537. }
  538.  
  539. - appWillTerminate:sender
  540. {
  541.   if( wpList != NULL )
  542.     [self clearOffScreen];
  543.  
  544.   return self;
  545. }
  546.  
  547. // Given a DPS window number, return the winfo struct that
  548. // describes that window.  We just do a linear scan; this is fine
  549. // given the relatively small number of windows we expect.
  550. - (struct winfo *) findWinfoByNumber:(int)wnum
  551. {
  552.   struct winfo *wp;
  553.   int i;
  554.  
  555.   wp = wpList;
  556.   for( wp = wpList, i = numWins; i > 0; --i, ++wp )
  557.     if( wp->num == wnum )
  558.       return wp;
  559.   return NULL;
  560. }
  561.  
  562. - (void)showReportSummaryForAll
  563. {
  564.     struct winfo *wp2;
  565.     char buf[100];
  566.     int tback = 0, non = 0, noff= 0, i;
  567.     
  568.     for( i = 0; i < numWins; ++i ){
  569.     wp2 = wpList + i;
  570.     if (wp2->bytes >= 0) tback += wp2->bytes;
  571.     if( wp2->origOnScreen )
  572.         ++non;
  573.     else
  574.         ++noff;
  575.     }
  576.  
  577.     sprintf (buf, "%d windows (%d on, %d off)", non+noff, non, noff);    
  578.     [repAllTotal setStringValue:buf];
  579.     [repAllTotalBackingStore setIntValue:tback];
  580. }
  581.  
  582. // Build the complete window list.  This will calculate numWins,
  583. // create wpList, and fill in a wpList with info on all existing windows.
  584. - buildWindowList
  585. {
  586.   int i;
  587.   int alpha;
  588.   struct winfo *wp;
  589.   int myContext;
  590.  
  591.  
  592.   myContext = [NXApp contextNumber];
  593.  
  594.   // Get an array of DPS window numbers
  595.   PScountwindowlist(0, &numWins);
  596.   windowList = malloc(sizeof(int) * numWins);
  597.   PSwindowlist(0, numWins, windowList);
  598.  
  599.   wpList = malloc(sizeof(struct winfo) * numWins);
  600.  
  601.   --numWins;                    // skip back-most window
  602.  
  603.   // For each window, get info for winfo struct describing window
  604.   for( i = 0; i < numWins; ++i ){
  605.     wp = wpList + i;
  606.     wp->num = windowList[i];
  607.     myCurrentWindowBounds(windowList[i],
  608.                           &wp->place.origin.x, &wp->place.origin.y,
  609.                           &wp->place.size.width, &wp->place.size.height);
  610.     myCurrentWindowAlpha(windowList[i], &alpha);
  611.     wp->hasAlpha = (alpha != 2);
  612.  
  613.     myCurrentWindowDepth(windowList[i], &wp->depth);
  614.     myCurrentWindowDepthLimit(windowList[i], &wp->depthLimit);
  615.  
  616.     if (!getWindowBytes (windowList[i], &wp->bytes, &wp->type)) {
  617.         wp->bytes = wp->type = -1;
  618.     }
  619.  
  620.     myCurrentOwner(windowList[i], &wp->context);
  621.     myCurrentWindowLevel(windowList[i], &wp->level);
  622.     
  623.     if( wp->context == myContext )
  624.       wp->isMe = YES;
  625.     else
  626.       wp->isMe = NO;
  627.  
  628.     wp->origOnScreen = wp->onScreenNow = NO;
  629.   }
  630.  
  631.   return self;
  632. }
  633.  
  634. // Build the on-screen list of windows.
  635. - buildOnScreenList
  636. {
  637.   // Find the on-screen windows
  638.   // with their order.
  639.   PScountscreenlist(0, &numOnScreen);
  640.   if( onScreenList )
  641.     free(onScreenList);
  642.   onScreenList = malloc(sizeof(int) * numOnScreen);
  643.   PSscreenlist(0, numOnScreen, onScreenList);
  644.  
  645.   return self;
  646. }
  647.  
  648. @end
  649.