home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 074.lha / MonIDCMP.c < prev    next >
C/C++ Source or Header  |  1986-11-20  |  32KB  |  1,020 lines

  1. /*
  2.  *  MonIDCMP.C  -  Monitor the IDCMP port for any window.    26-May-1987
  3.  *
  4.  *  Copyright (c) 1987 by Davide P. Cervone
  5.  *  You may use this code provided this copyright notice is kept intact.
  6.  */
  7.  
  8. #include <intuition/intuitionbase.h>
  9. #include <libraries/dos.h>
  10. #include <devices/inputevent.h>
  11. #include <stdio.h>
  12.  
  13. #define USAGE    "MonIDCMP [MASK <mask>] <WindowTitle> [<ScreenTitle>]\n"
  14.  
  15. #define INTUITION_REV   0
  16. #define ONE             1L
  17. #define SHOWN_FLAG      0x80        /* flag used to tell if a message has */
  18.                                     /* already been seen by the monitor */
  19.  
  20. #define SHOW_USAGE      0
  21. #define LIST_WINDOWS    1
  22. #define MONITOR_WINDOW  2
  23.  
  24. #define ARGMATCH(s,n)      (stricmp(s,argv[n]) == 0)
  25. #define NO_MATCH(s1,s2)    (stricmp(s1,s2) != 0)
  26.  
  27. extern struct Task *FindTask();
  28. extern LONG AllocSignal();
  29.  
  30. struct IntuitionBase *IntuitionBase = NULL;
  31. struct Task          *theTask;              /* the monitor task */
  32. struct MsgPort       *thePort;              /* the IDCMP port being viewed */
  33. struct List          *theMsgList;           /* the Message List for thePort */
  34. struct Window        *theWindow = NULL;     /* the IDCMP window */
  35. LONG                 theSignal;             /* IDCMP message signal bit */
  36. LONG                 theMask;               /*  and mask */
  37. BYTE                 oldPri;                /* our old priority */
  38. struct Task          *oldTask = NULL;       /* the monitored task */
  39. int                  NotDone = TRUE;        /* not done monitoring? */
  40. int                  GotSignal = FALSE;     /* did AllocSignal succeed? */
  41. char                 *WindowTitle = "";     /* the name of the IDCMP window */
  42. char                 *ScreenTitle = "Workbench Screen";   /* the Screen Title */
  43. ULONG                EventMask = 0xFFFFFFFF;   /* the IntuiMessage classes */
  44.                                                /* that we want to see */
  45.  
  46. char *version = "MonIDCMP v1.0 (May 1987)";
  47. char *author  = "Copyright (c) 1987 by Davide P. Cervone";
  48.  
  49.  
  50. /*
  51.  *  Ctrl_C()
  52.  *
  53.  *  Do nothing routine for Lattice control-C trapping (we do our own).
  54.  */
  55.  
  56. #ifdef LATTICE
  57. Ctrl_C()
  58. {
  59.    return(0);
  60. }
  61. #endif
  62.  
  63.  
  64. /*
  65.  *  DoExit()
  66.  *
  67.  *  General clean-up-and-exit routine.  If the string 's' is not a null
  68.  *  pointer, then print the message that it points to (it can take up to
  69.  *  three optional arguments).  Close the Intuition library if it is open.
  70.  */
  71.  
  72. void DoExit(s,x1,x2,x3)
  73. char *s, *x1, *x2, *x3;
  74. {
  75.    LONG status = 0;
  76.  
  77.    if (s != NULL)
  78.    {
  79.       printf(s,x1,x2,x3);
  80.       printf("\n");
  81.       status = RETURN_ERROR;
  82.    }
  83.    if (IntuitionBase != NULL) CloseLibrary(IntuitionBase);
  84.    exit(status);
  85. }
  86.  
  87.  
  88. /*
  89.  *  CheckLibOpen()
  90.  *
  91.  *  General library open routine.  It opens a library and sets a pointer
  92.  *  to it.  It checks that the library was openned successfully.
  93.  */
  94.  
  95. void CheckLibOpen(lib,name,rev)
  96. APTR *lib;
  97. char *name;
  98. int rev;
  99. {
  100.    extern APTR OpenLibrary();
  101.  
  102.    if ((*lib = OpenLibrary(name,(LONG)rev)) == NULL)
  103.       DoExit("Can't open %s\n",name);
  104. }
  105.  
  106.  
  107. /*
  108.  *  ParseArguments()
  109.  *
  110.  *  Parse the command-line arguments and return a function-code that
  111.  *  tells the main program what to do.  The valid options are:  the
  112.  *  words "LIST WINDOWS", which causes MonIDCMP to print a list of all
  113.  *  the screens and their associated windows; or, a window title followed
  114.  *  by an optional screen title (if none is supplied, "Workbench Screen" 
  115.  *  is assumed).  The window title can be preceeded optionally by the word
  116.  *  "MASK" followed by a HEX mask value that represents the IDCMP classes
  117.  *  that should be reported.  Only those messages that match a bit in the
  118.  *  mask value (and in the windows IDCMPFlags field) will be reported.  
  119.  *  For instance, a mask of 2C will allow all GADGETDOWN, MOUSEBUTTONS and
  120.  *  REFRESHWINDOW messages to be reported.
  121.  *
  122.  *  If the arguments do not match one of these templates, then the USAGE
  123.  *  function is performed.
  124.  */
  125.  
  126. int ParseArguments(argc,argv)
  127. int argc;
  128. char *argv[];
  129. {
  130.    int function = SHOW_USAGE;
  131.    ULONG eMask;
  132.    
  133.    if (argc >=2 && argc <= 5)
  134.    {
  135.       if (argc > 3 && ARGMATCH("MASK",1))
  136.       {
  137.          if (sscanf(argv[2],"%lx",&eMask) != 1)
  138.             DoExit("Bad mask value - '%s'",argv[2]);
  139.          argc -= 2;
  140.          argv += 2;
  141.          EventMask = eMask;
  142.       }
  143.       if (argc == 3 && ARGMATCH("LIST",1) && ARGMATCH("WINDOWS",2))
  144.       {
  145.          function = LIST_WINDOWS;
  146.       } else {
  147.          if (argc <= 3)
  148.          {
  149.             WindowTitle = argv[1];
  150.             if (argc == 3)
  151.                ScreenTitle = argv[2];
  152.             function = MONITOR_WINDOW;
  153.          }
  154.       }
  155.    }
  156.    return(function);
  157. }
  158.  
  159.  
  160. /*
  161.  *  ListWindows()
  162.  *
  163.  *  List all the screens and their associated windows.  The first screen
  164.  *  is found in the IntuitionBase structure; subsequent screens are found
  165.  *  from the NextScreen field of the preceeding screen.  The windows for
  166.  *  each screen are linked in a similar fashion.  Forbid() and Permit()
  167.  *  are used to insure that the IntuitionBase lists won't change while 
  168.  *  we're looking at them.  These should be LockIBase() and UnlockIBase(),
  169.  *  but I don't have the documentation for these, so I can't use them.
  170.  */
  171.  
  172. void ListWindows()
  173. {
  174.    struct Window       *theWindow;
  175.    struct Screen       *theScreen;
  176.  
  177.    Forbid();
  178.    for (theScreen = IntuitionBase->FirstScreen; theScreen;
  179.       theScreen = theScreen->NextScreen)
  180.    {
  181.       if (theScreen->DefaultTitle)
  182.          printf("\n'%s' [Screen]\n",theScreen->DefaultTitle);
  183.         else
  184.          printf("\n[No Screen Title]\n");
  185.       for (theWindow = theScreen->FirstWindow; theWindow;
  186.          theWindow = theWindow->NextWindow)
  187.       {
  188.          if (theWindow->Title)
  189.             printf("   '%s'\n",theWindow->Title);
  190.            else
  191.             printf("   [No Window Title]\n");
  192.       }
  193.    }
  194.    Permit();
  195.    printf("\n");
  196. }
  197.  
  198.  
  199. /*
  200.  *  FindWindow()
  201.  *
  202.  *  Look through the IntuitionBase pointers to find the screen and
  203.  *  window that the user supplied and report an error if they don't
  204.  *  exist.  The titles are matched with a case-insensative compare 
  205.  *  funtion (stricmp).  Forbid() and Permit() are used to be sure that
  206.  *  the Intuition lists don't change while we're looking.  These should
  207.  *  really be LockIBase() and UnlockIBase(), but I don't have the 
  208.  *  documentation for these, so I can't use them.
  209.  */
  210.  
  211. void FindWindow()
  212. {
  213.    struct Screen *theScreen;
  214.    
  215.    Forbid();
  216.  
  217.    for (theScreen = IntuitionBase->FirstScreen;
  218.         theScreen && NO_MATCH(ScreenTitle,theScreen->DefaultTitle);
  219.         theScreen = theScreen->NextScreen);
  220.  
  221.    if (theScreen)
  222.       for (theWindow = theScreen->FirstWindow;
  223.            theWindow && NO_MATCH(WindowTitle,theWindow->Title);
  224.            theWindow = theWindow->NextWindow);
  225.  
  226.    Permit();
  227.  
  228.    if (theScreen == NULL)
  229.       DoExit("Screen '%s' not found",ScreenTitle);
  230.    if (theWindow == NULL)
  231.       DoExit("Window '%s' not found on Screen '%s'",WindowTitle,ScreenTitle);
  232. }
  233.  
  234.  
  235. /*
  236.  *  SetupMsgPort()
  237.  *
  238.  *  Here's the trick that makes it all work:  the UserPort of the window is
  239.  *  an Exec message port (MsgPort), which contains (among other things) a
  240.  *  pointer to a task to signal when new messages arrive at the port.
  241.  *  We save the old task pointer and insert a pointer to our own task 
  242.  *  in the message port so that we will be signalled instead of the owner
  243.  *  of the window (later, when we have finished printing the messages, we will 
  244.  *  signal the original process "by hand").
  245.  *
  246.  *  We can't use GetMsg() to look at the IntuiMessages since this would
  247.  *  remove them from the port.  Instead, we look through the mp_MsgList
  248.  *  (an Exec List of Exec Node structures) and extract the pointers to 
  249.  *  the IntuiMessages directly, leaving the message port intact.  When we are
  250.  *  done looking, we Signal() the original task that the messages have
  251.  *  arrived.
  252.  *
  253.  *  Once we signal the other process, it uses GetMsg() to remove the messages
  254.  *  from the list.  If new messages arrive while the original process is 
  255.  *  getting message from the port, it could  remove those messages before we 
  256.  *  get the chance to see them.  To overcome this problem, we must run at a 
  257.  *  higher priority than the monitored task.  That way, when a new message 
  258.  *  arrives and we are signalled, we pre-empt the monitored task (i.e., we 
  259.  *  interupt whatever it is doing), and can look through the message list for 
  260.  *  new messages before we let it go any further with the list.  Since most 
  261.  *  of what we do is Wait() for the signal from the message port, we should 
  262.  *  not interfere with the normal functions of the monitored task.
  263.  *
  264.  *  We don't need to ReplyMsg() any messages, since the monitored process
  265.  *  should be doing this itself.
  266.  */
  267.  
  268. void SetupMsgPort()
  269. {
  270.    Forbid();
  271.    thePort    = theWindow->UserPort;
  272.    theMsgList = &(thePort->mp_MsgList);
  273.    theSignal  = thePort->mp_SigBit;
  274.    theMask    = ONE << theSignal;
  275.    oldTask    = thePort->mp_SigTask;
  276.    thePort->mp_SigTask = theTask;
  277.    Permit();
  278.    
  279.    oldPri = SetTaskPri(theTask,(ULONG)(oldTask->tc_Node.ln_Pri + 1));
  280. }
  281.  
  282.  
  283. /*
  284.  *  ResetMsgPort()
  285.  *
  286.  *  Put the window's UserPort back the way it was and reset our own 
  287.  *  priority.  Since it is possible that the window was closed (and its
  288.  *  associated memory freed) without our knowing about it, we only reset
  289.  *  the port variables if they still are what we set them to originally.
  290.  *  This avoids unwanted system crashes.
  291.  */
  292.  
  293. void ResetMsgPort()
  294. {
  295.    Forbid();
  296.    if (thePort->mp_SigBit == theSignal && thePort->mp_SigTask == theTask)
  297.       thePort->mp_SigTask = oldTask;
  298.    Permit();
  299.    
  300.    SetTaskPri(theTask,(ULONG)oldPri);
  301. }
  302.  
  303.  
  304. /*
  305.  *  GetSignal()
  306.  *
  307.  *  Try to allocate the same signal that the UserPort is using (this saves
  308.  *  a lot of trouble when we want to signal the original process).  If the
  309.  *  signal is already in use, we give the user the option of aborting or
  310.  *  re-using the signal (we may be signalled at the wrong times if we do).
  311.  *  GotSignal is used when we want to free the signal later.
  312.  */
  313.  
  314. void GetSignal()
  315. {
  316.    char c;
  317.  
  318.    if (AllocSignal(theSignal) != theSignal)
  319.    {
  320.       printf("Signal %ld already in use - continue anyway?  ",theSignal);
  321.       c = getchar();
  322.       if (c != 'y' && c != 'Y')
  323.       {
  324.          ResetMsgPort();
  325.          DoExit("Monitor aborted");
  326.       }
  327.    } else {
  328.       GotSignal = TRUE;
  329.    }
  330. }
  331.  
  332.  
  333. /*
  334.  *  These macros are helpful in printing the Qualifier fields of the
  335.  *  IntuiMessage that we received
  336.  */
  337.  
  338. #define CODE            (Code & ~IECODE_UP_PREFIX)
  339. #define QUAL(x)         (Qualifier & (x))
  340. #define ADDQUAL(n)      s = AddQual(s,n)
  341. #define ADDCHAR(c)      *s++ = c;
  342. #define ADDLETTER(q,c)  if (QUAL(q)) ADDCHAR(c);
  343.  
  344. /*
  345.  *  These are shorhands for the InputEvent qualifier flags
  346.  */
  347.  
  348. #define LSHIFT          IEQUALIFIER_LSHIFT
  349. #define RSHIFT          IEQUALIFIER_RSHIFT
  350. #define LOCK            IEQUALIFIER_CAPSLOCK
  351. #define SHIFT           (LSHIFT | RSHIFT | LOCK)
  352. #define CONTROL         IEQUALIFIER_CONTROL
  353. #define LALT            IEQUALIFIER_LALT
  354. #define RALT            IEQUALIFIER_RALT
  355. #define ALT             (LALT | RALT)
  356. #define LAMIGA          IEQUALIFIER_LCOMMAND
  357. #define RAMIGA          IEQUALIFIER_RCOMMAND
  358. #define AMIGAS          (LAMIGA | RAMIGA)
  359. #define NUMPAD          IEQUALIFIER_NUMERICPAD
  360. #define REPEAT          IEQUALIFIER_REPEAT
  361. #define MBUTTON         IEQUALIFIER_MIDBUTTON
  362. #define RBUTTON         IEQUALIFIER_RBUTTON
  363. #define LBUTTON         IEQUALIFIER_LEFTBUTTON
  364. #define ANYBUTTON       (LBUTTON | RBUTTON | MBUTTON)
  365. #define RELMOUSE        IEQUALIFIER_RELATIVEMOUSE
  366.  
  367. /*
  368.  *  AddQual()
  369.  *
  370.  *  Add a comma and a qualifier name to a string and return a pointer
  371.  *  to the character after the end of the added name.
  372.  */
  373.  
  374. char *AddQual(s,name)
  375. char *s, *name;
  376. {
  377.    *s++ = ',';
  378.    strcpy(s,name);
  379.    return(s + strlen(s));
  380. }
  381.  
  382.  
  383. /*
  384.  *  ShowQual()
  385.  *
  386.  *  Display the type of IntuiMessage that we received together with
  387.  *  the mouse position and qualifier flags (if any).  Buttons, shift,
  388.  *  ALT, and Amiga keys are listed with L for left, R for right, M for
  389.  *  middle (button, for future compatibility), and C for CapsLock.
  390.  *
  391.  *  If the event occured in a window other than the monitored one (but
  392.  *  that uses the same UserPort as the monitored window), the window
  393.  *  name is displayed as well (in square brackets).
  394.  */
  395.  
  396. void ShowQual(name,theMessage)
  397. char *name;
  398. struct IntuiMessage *theMessage;
  399. {
  400.    USHORT Qualifier = theMessage->Qualifier;
  401.    char qual[85];
  402.    char *s = &qual[0];
  403.    int len = 26;
  404.  
  405.    printf("%-17s (%03d,%03d)",name,theMessage->MouseX,theMessage->MouseY);
  406.    if (Qualifier)
  407.    {
  408.       printf(" %04X",Qualifier); len += 5;
  409.       if (QUAL(ANYBUTTON))
  410.       {
  411.          ADDQUAL("Button(");
  412.          ADDLETTER(LBUTTON,'L');
  413.          ADDLETTER(RBUTTON,'R');
  414.          ADDLETTER(MBUTTON,'M');
  415.          ADDCHAR(')');
  416.       }
  417.       if (QUAL(REPEAT)) ADDQUAL("Repeat");
  418.       if (QUAL(NUMPAD)) ADDQUAL("NumPad");
  419.       if (QUAL(AMIGAS))
  420.       {
  421.          ADDQUAL("Amiga(");
  422.          ADDLETTER(LAMIGA,'L');
  423.          ADDLETTER(RAMIGA,'R');
  424.          ADDCHAR(')');
  425.       }
  426.       if (QUAL(ALT))
  427.       {
  428.          ADDQUAL("ALT(");
  429.          ADDLETTER(LALT,'L');
  430.          ADDLETTER(RALT,'R');
  431.          ADDCHAR(')');
  432.       }
  433.       if (QUAL(CONTROL)) ADDQUAL("CTRL");
  434.       if (QUAL(SHIFT))
  435.       {
  436.          ADDQUAL("Shift(");
  437.          ADDLETTER(LSHIFT,'L');
  438.          ADDLETTER(RSHIFT,'R');
  439.          ADDLETTER(LOCK,  'C');
  440.          ADDCHAR(')');
  441.       }
  442.       ADDCHAR('\0');
  443.       if (qual[0] != '\0')
  444.       {
  445.          qual[0] = ' ';
  446.          len += strlen(qual);
  447.          if (len > 76)
  448.          {
  449.             printf("\n"); len -= 31;
  450.          }
  451.          printf(qual);
  452.       }
  453.    }
  454.    if (theMessage->IDCMPWindow != theWindow)
  455.    {
  456.       if (len + strlen(theMessage->IDCMPWindow->Title) > 72) printf("\n");
  457.       printf(" [%s]",theMessage->IDCMPWindow->Title);
  458.    }
  459. }
  460.  
  461.  
  462. /*
  463.  *  ShowMouse()
  464.  *
  465.  *  Display a mouse button code.  We use the constants as defined in
  466.  *  Intuition.h
  467.  */
  468.  
  469. void ShowMouse(Code)
  470. int Code;
  471. {
  472.    if (Code == SELECTUP)   printf("\n SELECTUP");
  473.    if (Code == SELECTDOWN) printf("\n SELECTDOWN");
  474.    if (Code == MENUUP)     printf("\n MENUUP");
  475.    if (Code == MENUDOWN)   printf("\n MENUDOWN");
  476. }
  477.  
  478. /*
  479.  *  Macros for Gadget fields
  480. */
  481.  
  482. #define GFLAG(f)    (theGadget->Flags & (f))
  483. #define GTYPEF(t)   ((theGadget->GadgetType & GADGETTYPE) & (t))
  484. #define GTYPE(t)    ((theGadget->GadgetType & ~GADGETTYPE) == (t))
  485.  
  486.  
  487. /*
  488.  *  ShowGadget()
  489.  *
  490.  *  Display information about gadget messages:  the GadgetID field,
  491.  *  the GadgetText (if non-NULL), the GadgetType, and the GadgetFlags 
  492.  *  (as a HEX value).  If the gadget is a STRGADGET, the contents of the
  493.  *  StringInfo buffer is displayed.  If the gadget is a PROPGADGET, the
  494.  *  values of the Pot and Body fields of the PropInfo structure are shown
  495.  *  (for the directions that the gadget moves), and the status of the KNOBHIT
  496.  *  flag is printed.
  497.  */
  498.  
  499. void ShowGadget(theGadget)
  500. struct Gadget *theGadget;
  501. {
  502.    int special = (GTYPE(STRGADGET) || GTYPE(PROPGADGET));
  503.    struct StringInfo *SI = (struct StringInfo *) theGadget->SpecialInfo;
  504.    struct PropInfo   *PI = (struct PropInfo *)   theGadget->SpecialInfo;
  505.  
  506.    printf("\n ID = %d,",theGadget->GadgetID);
  507.    if (theGadget->GadgetText && theGadget->GadgetText->IText)
  508.       printf(" '%s'",theGadget->GadgetText->IText);
  509.    if (GTYPE(STRGADGET))  printf(" STRGADGET");
  510.    if (GTYPE(BOOLGADGET)) printf(" BOOLGADGET");
  511.    if (GTYPE(PROPGADGET)) printf(" PROPGADGET");
  512.    if (GTYPEF(REQGADGET)) printf("+REQGADGET");
  513.    if (GFLAG(SELECTED))   printf(" (SELECTED)");
  514.    printf(", Flags = %04X",theGadget->Flags);
  515.    if (special)
  516.    {
  517.       printf("\n");
  518.       if (GTYPE(STRGADGET))
  519.       {
  520.          if (SI->Buffer) printf(" Buffer = '%s'",SI->Buffer);
  521.       } else {
  522.          if (PI->Flags & FREEHORIZ)
  523.             printf(" HPot = %5d, HBody = %5d,",PI->HorizPot,PI->HorizBody);
  524.          if (PI->Flags & FREEVERT)
  525.             printf(" VPot = %5d, VBody = %5d,",PI->VertPot,PI->VertBody);
  526.          if (PI->Flags & KNOBHIT)
  527.             printf(" KNOBHIT");
  528.            else
  529.             printf(" Knob not hit");
  530.       }
  531.    }
  532. }
  533.  
  534.  
  535. /*
  536.  *  Macros for menu fields
  537.  */
  538. #define ITEXT(p)    (((struct IntuiText *)((p)->ItemFill))->IText)
  539. #define MFLAG(f)    (theMItem->Flags & (f))
  540.  
  541.  
  542. /*
  543.  *  ShowMenu()
  544.  *
  545.  *  Display the message's Code field and translate it into the menu number,
  546.  *  the item number and the sub-item number.  Then look through the
  547.  *  window's MenuStrip for each of these items (ItemAddress only gives us the
  548.  *  final address, so we look though the MenuStrip by hand), and print their
  549.  *  menu text (if they are text items).  Finally, if the menu item is checked,
  550.  *  we report that.
  551.  *
  552.  *  Since the Amiga allows multiple menu items to be picked within the same
  553.  *  MENUPICK message, we follow the NextSelect field and print out the
  554.  *  data for each additional menu item that was picked.
  555.  */
  556.  
  557. void ShowMenu(Code,theWindow)
  558. int Code;
  559. struct Window *theWindow;
  560. {
  561.    int menu,item,sub;
  562.    struct Menu *theMenu;
  563.    struct MenuItem *theMItem;
  564.    extern struct MenuItem *ItemAddress();
  565.  
  566.    do
  567.    {
  568.       if (Code == MENUNULL)
  569.       {
  570.          printf("\n Code = MENUNULL (NOMENU,NOITEM,NOSUB)");
  571.       } else {
  572.          menu = MENUNUM(Code);
  573.          item = ITEMNUM(Code);
  574.          sub  = SUBNUM(Code);
  575.          printf("\n Code = %4X (%d",Code,menu);
  576.          if (item == NOITEM) printf(",NOITEM"); else printf(",%d",item);
  577.          if (sub == NOSUB) printf(",NOSUB)"); else printf(",%d)",sub);
  578.  
  579.          for(theMenu = theWindow->MenuStrip; menu; menu--)
  580.             theMenu = theMenu->NextMenu;
  581.          printf("  %s",(theMenu->MenuName)? theMenu->MenuName: "[NONAME]");
  582.  
  583.          for (theMItem = theMenu->FirstItem; item; item--)
  584.             theMItem = theMItem->NextItem;
  585.          if (MFLAG(ITEMTEXT) && theMItem->ItemFill && ITEXT(theMItem))
  586.             printf(", %s",ITEXT(theMItem)); else printf(", [NONAME]");
  587.  
  588.          if (sub != NOSUB)
  589.          {
  590.             for (theMItem = theMItem->SubItem; sub; sub--)
  591.                theMItem = theMItem->NextItem;
  592.             if (MFLAG(ITEMTEXT) && theMItem->ItemFill && ITEXT(theMItem))
  593.                printf(", %s",ITEXT(theMItem)); else printf(", [NONAME]");
  594.          }
  595.  
  596.          if (MFLAG(CHECKED)) printf(" (CHECKED)");
  597.          Code = (ItemAddress(theWindow->MenuStrip,(ULONG)Code))->NextSelect;
  598.       }
  599.    } while (Code != MENUNULL);
  600. }
  601.  
  602.  
  603. /*
  604.  *  Cheap translation of RAWKEY key codes to their keyboard equivalents.
  605.  *  I could have used RawKeyConvert, but that requires a console device
  606.  *  to be opened, but I didn't want to bother with that.
  607.  */
  608.  
  609. char Keys[] =
  610. {
  611.  '`','1','2','3','4','5','6','7','8','9','0','-','=','\\',NULL,'\012',
  612.  'Q','W','E','R','T','Y','U','I','O','P','[',']',NULL,'\001','\002','\003',  
  613.  'A','S','D','F','G','H','J','K','L',';','"',NULL,NULL,'\004','\005','\006',
  614.  NULL,'Z','X','C','V','B','N','M',',','.','/',NULL,'\013','\007','\010','\011'
  615. };
  616.  
  617. /*
  618.  *  Names for non-printing keys
  619.  */
  620.  
  621. char *KNames[] =
  622. {
  623.    "UNDEFINED", "SPACE", "BACKSPACE", "TAB", "ENTER", "RETURN", "ESC",
  624.    "DEL", NULL, NULL, NULL, "KeyPad -", NULL, "UP ARROW", "DOWN ARROW", 
  625.    "RIGHT ARROW", "LEFT ARROW", "F1", "F2", "F3", "F4", "F5", "F6", "F7",
  626.    "F8", "F9", "F10", NULL, NULL, NULL, NULL, NULL, "HELP", "LEFT SHIFT",
  627.    "RIGHT SHIFT", "CAPS LOCK", "CTRL", "LEFT ALT", "RIGHT ALT", "LEFT AMIGA",
  628.    "RIGHT AMIGA", "LEFT BUTTON", "RIGHT BUTTON", "MIDDLE BUTTON"
  629. };
  630.  
  631. /*
  632.  *  Error conditions (just to be complete; I don't even know how to
  633.  *  generate these, so I couldn't test that they worked)
  634.  */
  635.  
  636. char *KErrs[] =
  637. {
  638.    "LAST WAS BAD", "BUFFER OVERFLOW", "CATASTROPHE", "TEST FAILED",
  639.    "POWERUP START", "POWERUP END", "MOUSE MOVE"
  640. };
  641.  
  642. #define FIRST_NAME      0x40
  643. #define FIRST_ERROR     0xF9
  644. #define KEYCODEMASK     (~IECODE_UP_PREFIX)
  645.  
  646.  
  647. /*
  648.  *  ShowKey()
  649.  *
  650.  *  Display the RAWKEY code and which keyboard key produced it.  The
  651.  *  IntuiMessage IAddress field points to the previous key pressed so that
  652.  *  Dead Keys (ones that produce diacritical markings) can be implemented.
  653.  *  See the Enhancer documentation (pp 65-66) for more information.  I used
  654.  *  trial-and-error to figure out what they were pointing at.  It looks like
  655.  *  additional previous keystrokes are stored farther along, but I don't
  656.  *  know how long they are valid.
  657.  */
  658.  
  659. void ShowKey(Code,PrevKey)
  660. int Code;
  661. unsigned char *PrevKey;
  662. {
  663.    unsigned char c = Code & KEYCODEMASK;
  664.    unsigned char c1, *s;
  665.  
  666.    printf("\n Key %02X %s",c,(Code & IECODE_UP_PREFIX)? "UP  ": "DOWN");
  667.    if (c < FIRST_NAME)
  668.    {
  669.       c1 = Keys[c];
  670.       if (c1 > ' ')
  671.       {
  672.          printf(" (%c)",c1);
  673.       } else {
  674.          if (c1 == '\013')
  675.             printf(" (KeyPad .)");
  676.            else
  677.             printf(" (KeyPad %c)",(c1 % 10) + '0');
  678.       }
  679.    } else {
  680.       if (Code >= FIRST_ERROR)
  681.       {
  682.          printf(" (%s)",KErrs[Code-FIRST_ERROR]);
  683.       } else {
  684.          s = KNames[c - FIRST_NAME + 1];
  685.          if (s == NULL) s = KNames[0];
  686.          printf(" (%s)",s);
  687.       }
  688.    }
  689.    if (PrevKey && *PrevKey)
  690.    {
  691.       printf(" Previous = %02X",*PrevKey++);
  692.       if (*PrevKey) printf(" Qualifiers = %02X",*PrevKey);
  693.    }
  694. }
  695.  
  696.  
  697. /*
  698.  *  ShowVanilla()
  699.  *
  700.  *  Display the ASCII key that was reported.  Non-printing characters
  701.  *  are shown in a semi-reasonable fashion (I don't really like the
  702.  *  "META" stuff).
  703.  */
  704.  
  705. void ShowVanilla(Code)
  706. int Code;
  707. {
  708.    unsigned char c = Code & 0x7F;
  709.  
  710.    printf("\n ASCII = %02X",Code);
  711.    if (c >= ' ' && c < 0x7F)
  712.    {
  713.       printf("  (%c",Code);
  714.       if (Code > 0x7F) printf(" = META-%c",c);
  715.       printf(")");
  716.    } else {
  717.       if (c < ' ')
  718.          printf("  (CTRL-%s%c)",(Code > 0x7F)? "META-" :"",c+'@');
  719.         else
  720.          printf("  (%sDEL)",(Code > 0x7F)? "META-" :"");
  721.    }
  722. }
  723.  
  724.  
  725. /*
  726.  *  PrintMessage()
  727.  *
  728.  *  Call the right routines for each message class.  Only those that were
  729.  *  specified in the MASK on the command line (default is all messages) are
  730.  *  shown.  Note that only those messages that are actually POSTED to the
  731.  *  UserPort are received by the monitor, so only those classes that appear
  732.  *  in both the MASK and in the window's IDCMPFlags field are shown.  That is
  733.  *  to say, we do NOT perform a ModifyIDCMP() call to include any classes that
  734.  *  are not in the IDCMPFlags field of the window.
  735.  *
  736.  *  If a CLOSEWINDOW event is received for the monitored window, the monitor
  737.  *  assumes that the window is about to be closed and the UserPort freed so
  738.  *  it stops monitoring the window.  If another window using the same port is
  739.  *  closed, however, the monitor does not shut down.
  740.  */
  741.  
  742. void PrintMessage(theMessage)
  743. struct IntuiMessage *theMessage;
  744. {
  745.    ULONG  Class = theMessage->Class;
  746.    USHORT Code  = theMessage->Code;
  747.  
  748.    if (EventMask & Class)
  749.    {
  750.       switch(Class)
  751.       {
  752.          case SIZEVERIFY:
  753.             ShowQual("Size Verify",theMessage);
  754.             break;
  755.  
  756.          case NEWSIZE:
  757.             ShowQual("New Size",theMessage);
  758.             break;
  759.  
  760.          case REFRESHWINDOW:
  761.             ShowQual("Refresh Window",theMessage);
  762.             break;
  763.  
  764.          case MOUSEBUTTONS:
  765.             ShowQual("Mouse Button",theMessage);
  766.             ShowMouse(Code);
  767.             break;
  768.    
  769.          case MOUSEMOVE:
  770.             ShowQual("Mouse move",theMessage);
  771.             break;
  772.  
  773.          case GADGETDOWN:
  774.             ShowQual("Gadget Down",theMessage);
  775.             ShowGadget(theMessage->IAddress);
  776.             break;
  777.  
  778.          case GADGETUP:
  779.             ShowQual("Gadget Up",theMessage);
  780.             ShowGadget(theMessage->IAddress);
  781.             break;
  782.  
  783.          case REQSET:
  784.             ShowQual("Requester Set",theMessage);
  785.             break;
  786.  
  787.          case MENUPICK:
  788.             ShowQual("Menu Pick",theMessage);
  789.             ShowMenu(Code,theMessage->IDCMPWindow);
  790.             break;
  791.  
  792.          case CLOSEWINDOW:
  793.             ShowQual("Close Window",theMessage);
  794.             break;
  795.  
  796.          case RAWKEY:
  797.             ShowQual("Raw Key",theMessage);
  798.             ShowKey(Code,theMessage->IAddress);
  799.             break;
  800.  
  801.          case REQVERIFY:
  802.             ShowQual("Requester Verify",theMessage);
  803.             break;
  804.  
  805.          case REQCLEAR:
  806.             ShowQual("Requester Clear",theMessage);
  807.             break;
  808.  
  809.          case MENUVERIFY:
  810.             ShowQual("Menu Verify",theMessage);
  811.             break;
  812.  
  813.          case NEWPREFS:
  814.             ShowQual("New Preferences",theMessage);
  815.             break;
  816.  
  817.          case DISKINSERTED:
  818.             ShowQual("Disk Inserted",theMessage);
  819.             break;
  820.  
  821.          case DISKREMOVED:
  822.             ShowQual("Disk Removed",theMessage);
  823.             break;
  824.  
  825.          case WBENCHMESSAGE:
  826.             ShowQual("WorkBench Message",theMessage);
  827.             if (Code == WBENCHOPEN)  printf("\n Code = WBENCHOPEN");
  828.             if (Code == WBENCHCLOSE) printf("\n Code = WBENCHCLOSE");
  829.             break;
  830.  
  831.          case ACTIVEWINDOW:
  832.             ShowQual("Activate Window",theMessage);
  833.             break;
  834.  
  835.          case INACTIVEWINDOW:
  836.             ShowQual("Inactivate Window",theMessage);
  837.             break;
  838.  
  839.          case VANILLAKEY:
  840.             ShowQual("Vanilla Key",theMessage);
  841.             ShowVanilla(Code);
  842.             break;
  843.  
  844.          case INTUITICKS:
  845.             ShowQual("IntuiTicks",theMessage);
  846.             break;
  847.  
  848.          default:
  849.             printf("Unknown Class:  %04X",theMessage->Class);
  850.             if (theMessage->IDCMPWindow != theWindow)
  851.                printf("   (%s)",theMessage->IDCMPWindow->Title);
  852.             break;
  853.       }
  854.       printf("\n");
  855.    }
  856.    if (Class == CLOSEWINDOW && theMessage->IDCMPWindow == theWindow)
  857.       NotDone = FALSE;
  858. }
  859.  
  860.  
  861. /*
  862.  *  MonitorIDCMP()
  863.  *
  864.  *  This is the main loop for the monitor process.  It calls Wait() to
  865.  *  wait for either a message to arrive in the UserPort or for a CTRL-C
  866.  *  to be pressed.  CTRL-C tells MonIDCMP to stop monitoring and clean things
  867.  *  up.
  868.  *
  869.  *  When a message appears in the UserPort, we look through the message list
  870.  *  structure (an Exec List of Exec Node structures), which contains the
  871.  *  pointers to the IntuiMessages posted to the UserPort by Intuition.
  872.  *  Forbid() and Permit() are used so that the list doesn't change while we're 
  873.  *  looking at it.  These should be LockIBase() and UnlockIBase(), but I don't
  874.  *  have the documentation for these, so I can't use them.
  875.  *
  876.  *  For each message in the list we check to see if we've seen it already
  877.  *  and if not, we print it and mark it as seen.  Since Intuition may post
  878.  *  messages to the UserPort at any time (e.g., after we have signalled the
  879.  *  monitored process that new messages have arrived, but before it has had 
  880.  *  a chance to GetMsg() all of them), we have to know which messages we've
  881.  *  already printed and which are new, so when Intuition signals us we don't
  882.  *  reprint messages that are still in the message list.  To do this, we use
  883.  *  a bit of a kludge:  we alter the message's ln_Type field (which should be
  884.  *  NT_MESSAGE) by setting the high bit.  The monitored process doesn't usually
  885.  *  use this field, and it doesn't seem to annoy GetMsg() or ReplyMsg(), so 
  886.  *  I think it works.  The key is that when Intuition re-uses the message, it 
  887.  *  resets this field (actually, I suspect PutMsg() does this), so we can tell
  888.  *  new messages from old messages.  I tried using the ln_Pri and other un-used
  889.  *  Node fields, but these were not reset, so were not useful for this function.
  890.  *  Take note, however, that this DOES alter the contents of the message, and
  891.  *  may cause some programs to malfucntion.
  892.  *
  893.  *  Another approach would have been to take over the ReplyPort for the
  894.  *  messages and keep track of which ones have come back, but this seemed
  895.  *  needlessly complicated and caused other problems instead,
  896.  *
  897.  *  Once we are through printing the new messages, we Permit() and then
  898.  *  Signal() the monitored process that new messages have arrived.  It then
  899.  *  processes them normally, and calls ReplyMsg() itself.
  900.  *
  901.  *  We rely on the fact that we are running at a higher priority than the
  902.  *  monitored process in order to get ALL the messages, even ones that come
  903.  *  while that process is running (i.e., not in a Wait() call).  If the
  904.  *  monitored process calls Forbid() or changes it's priority, however, we
  905.  *  are likely to miss messages, particularly ones that come in groups
  906.  *  (INACTIVEWINDOW/ACTIVEWINDOW, MENUUP/MENUPICK, etc.).
  907.  *
  908.  *  WARNING:  since we print the contents of the messages BEFORE the 
  909.  *  monitored process gets them, this can cause deadlock situations.  For 
  910.  *  instnace, suppose the monitored process has locked the screen layers and
  911.  *  is waiting for mouse moves or button releases.  When these arrive, MonIDCMP
  912.  *  tries to print them, but the layers are locked, so it waits for them to
  913.  *  become unlocked; but this never happens, since the monitored process is
  914.  *  waiting for MonIDCMP to signal the messages.  This appears to be what 
  915.  *  happens when you monitor the WorkBench window and try to move a disk icon, 
  916.  *  for example.  To solve this problem, you can re-direct the output from 
  917.  *  MonIDCMP to a file rather than to the screen.
  918.  *
  919.  *  Note that this method of monitoring a port is not resticted to the
  920.  *  IDCMP ports.  It can be used on ANY port, provided you know where to 
  921.  *  look for it, and what it's contents are supposed to look like.
  922.  */
  923.  
  924. void MonitorIDCMP()
  925. {
  926.    struct Node *theNode;
  927.    UBYTE theType;
  928.    
  929.    while (NotDone)
  930.    {
  931.       if (Wait(theMask | SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
  932.       {
  933.          printf("Cancelling...");
  934.          NotDone = FALSE;
  935.       } else {
  936.          if (theMsgList->lh_TailPred != (struct Node *) theMsgList)
  937.          {
  938.             Forbid();
  939.             for (theNode = theMsgList->lh_Head; theNode->ln_Succ;
  940.                theNode = theNode->ln_Succ)
  941.             {
  942.                if (((theType = theNode->ln_Type) & SHOWN_FLAG) == 0)
  943.                {
  944.                   if (theType != NT_MESSAGE) printf("Type: 0x%X - ",theType);
  945.                   PrintMessage(theNode);
  946.                   theNode->ln_Type |= SHOWN_FLAG;
  947.                }
  948.             }
  949.             Permit();
  950.          }
  951.          Signal(oldTask,theMask);
  952.       }
  953.    }
  954. }
  955.  
  956.  
  957. /*
  958.  *  MonitorWindow()
  959.  *
  960.  *  Get the pointer to the window that the user has specified and set up
  961.  *  the MsgPort so that we are signalled when messages arrive in it.  Once
  962.  *  this is done, identify the program, and start the monitoring.  When
  963.  *  the user presses CTRL-C (or closes the monitored window), we are done, so
  964.  *  we clean up the MsgPort and free the signal, then quit.
  965.  */
  966.  
  967. void MonitorWindow()
  968. {
  969.    theTask = FindTask(NULL);
  970.  
  971.    FindWindow();
  972.    #ifdef LATTICE
  973.       onbreak(Ctrl_C);
  974.    #endif
  975.    SetupMsgPort();
  976.    GetSignal();
  977.  
  978.    printf("%s - IDCMP Monitor Program\n",version);
  979.    printf("Monitor Installed - press CTRL-C to cancel\n");
  980.  
  981.    MonitorIDCMP();
  982.  
  983.    ResetMsgPort();
  984.    if (GotSignal) FreeSignal(theSignal);
  985.    printf("Monitor Removed\n");
  986. }
  987.  
  988.  
  989. /*
  990.  *  main()
  991.  *
  992.  *  Open Intuition and then parse the command-line options to find out what
  993.  *  action the user wants to do.  Either print the usage information or
  994.  *  call the proper routine.  When we're done, exit with no message.
  995.  */
  996.  
  997. void main(argc,argv)
  998. int argc;
  999. char *argv[];
  1000. {
  1001.    CheckLibOpen(&IntuitionBase,"intuition.library",INTUITION_REV);
  1002.  
  1003.    switch(ParseArguments(argc,argv))
  1004.    {
  1005.       case SHOW_USAGE:
  1006.          printf("Usage:  %s",USAGE);
  1007.          printf("   or   MonIDCMP LIST WINDOWS\n");
  1008.          break;
  1009.  
  1010.       case LIST_WINDOWS:
  1011.          ListWindows();
  1012.          break;
  1013.  
  1014.       case MONITOR_WINDOW:
  1015.          MonitorWindow();
  1016.          break;
  1017.    }
  1018.    DoExit(NULL);
  1019. }
  1020.