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

  1. /* twm.c
  2.  
  3.    Tiny-Window Manager v1.0         (c) 1987 Transactor Publishing Inc.
  4.    by Nick Sullivan
  5.  
  6.    This program is freely redistributable provided that no charge is made
  7.    for the redistribution beyond reasonable reproduction costs, and that no
  8.    changes are made except with the prior written approval of Transactor
  9.    Publishing Inc., 85 West Wilmot St. #10, Richmond Hill, Ontario L4B 1K7.
  10.  
  11.    ------------------------------------------------------------------------
  12.  
  13.    TWM provides a storage area in which applications that are inactive, but
  14.    running, can wait to be re-activated without using any chip RAM. It thus
  15.    provides an alternative to the "tiny window" approach to minimizing chip
  16.    RAM use, as exemplified by such programs as Uedit and PopColours.
  17.  
  18.    When TWM is run, it puts up its own tiny window, and creates a public
  19.    message port. Client applications should check for the existence of this
  20.    port and, if it is present, send a "twMessage" (as defined in twm.h)
  21.    with the tmAction field set to TWM_ACTION_ADD when they wish to go to
  22.    sleep. When the TWM window is clicked in thereafter, a larger window will
  23.    be put up containing gadgets bearing the names of each client application
  24.    that has been added. Clicking on one of these gadgets will cause the
  25.    twMessage to be replied to, which is the signal for them to reawaken.
  26.    At the same time as the reply is sent, the large TWM window will be taken
  27.    down, and the gadget for that client removed.
  28.  
  29.    If a client wishes to reactivate itself before its TWM gadget is clicked,
  30.    or if it wishes to exit altogether, it should first send a twMessage with
  31.    the twmAction field set to TWM_ACTION_DELETE.
  32.  
  33.    The twmAction field is used by TWM to return a code to the client that
  34.    indicates whether the requested operation was successful. The code for
  35.    success is E_OK. Other possibilities are:
  36.  
  37.    E_NO_MEM
  38.       A client has asked TWM to add a gadget for him, but TWM was unable to
  39.       allocate memory for the gadget structure.
  40.    E_ABANDON_SHIP
  41.       1) A client has asked TWM to add a gadget for him while TWM had its
  42.       large window up. In this case, TWM closes the large window, and
  43.       re-opens it after rethinking the gadget positions and the window size.
  44.       2) The user has clicked on TWM's tiny window, or closed its large
  45.       window, causing TWM to close the current window and attempt to open the
  46.       other one.
  47.          If the window open fails in either of these cases, TWM sends all
  48.       current clients this error message, then exits, since it has no means
  49.       of recovery.
  50.    E_TASK_UNKNOWN
  51.       A client has sent a TWM_ACTION_DELETE, but TWM does not currently have
  52.       a gadget for that client.
  53.    E_ACTION_UNKNOWN
  54.       A message has been received with the twmAction field set to an unknown
  55.       code... currently the only possibilities are TWM_ACTION_DELETE and
  56.       TWM_ACTION_ADD.
  57.  
  58.  
  59.    Sample C code for applications wishing to interface correctly with TWM
  60.    is contained in the file twmClient.c. The header file twm.h is also
  61.    required. Before using other functions in this file, the client should
  62.    call twmInit().
  63.       The function PostMe() in twmClient.c should be called when the
  64.    client wishes to deactivate. If this function returns TRUE, the client
  65.    should resume its life as an active application. If it returns FALSE,
  66.    either TWM is not present in the system or else the attempt to post failed
  67.    for some reason. In this case, the client should take an alternative
  68.    approach to deactivated living (like making its own tiny window), or else
  69.    not allow itself to be deactivated.
  70.       Programs that wish to be able to receive messages even when deactivated
  71.    (time-outs, for example), will need to use a modified version of PostMe().
  72.    If such a program wishes to reactivate before its gadget has been clicked
  73.    in the TWM window, it should first call UnPostMe() (no arguments, no
  74.    return) to inform TWM that the gadget should be taken down. Programs that
  75.    will NOT need to call UnPostMe() (i.e. most programs) can use a version
  76.    of twmClient.c from which the UnPostMe() function and all references to
  77.    the global variable Delmsg have been removed.
  78.       Before the first call to PostMe(), the function twmInit() must be
  79.    invoked to set up the required messages and ports that PostMe() will need.
  80.       Before the client exits, it should call the function UnPostMe() to
  81.    deallocate resources twmInit() has allocated.
  82.    
  83.    Update history:
  84.    
  85.       Nov 12/87: Now making absolutely sure message port is cleared before
  86.       exiting.
  87. */
  88.  
  89. #include "header/twm.h"
  90.  
  91.  
  92. struct Gadget WakeUpGadget =
  93. {
  94.    NULL,                         /* address of next gadget        */
  95.    2, 10, 116, 10,               /* left, top, width, height      */
  96.    GADGHNONE,                    /* flags - no highlighting       */
  97.    RELVERIFY,                    /* activation flags              */
  98.    BOOLGADGET,                   /* gadget type                   */
  99.    NULL,                         /* no imagery                    */
  100.    NULL,                         /* no alternate imagery          */
  101.    NULL,                         /* no text                       */
  102.    0,                            /* mutual exclude                */
  103.    NULL,                         /* SpecialInfo                   */
  104.    0,                            /* gadget ID                     */
  105.    NULL,                         /* user data                     */
  106. };
  107.  
  108. struct NewWindow wtiny =
  109. {
  110.    480, 60, 120, 20,    /* left, top, width, height   */
  111.      0,  1,             /* detail pen, block pen      */
  112.    GADGETUP             /* IDCMP flags                */
  113.    | CLOSEWINDOW,
  114.    WINDOWDRAG           /* Window flags               */
  115.    | WINDOWCLOSE
  116.    | WINDOWDEPTH,
  117.    &WakeUpGadget,       /* application gadget list    */
  118.    NULL,                /* special checkmark imagery  */
  119.    (UBYTE *)"TWM",      /* window title               */
  120.    NULL,                /* custom screen pointer      */
  121.    NULL,                /* super bitmap pointer       */
  122.    0, 0, 0, 0,          /* min/max width and height   */
  123.    WBENCHSCREEN         /* screen type                */
  124. };
  125.  
  126. struct NewWindow whuge =
  127. {
  128.    480, 60, 0, 0,       /* left, top, width, height   */
  129.      0,  1,             /* detail pen, block pen      */
  130.    GADGETUP
  131.    | CLOSEWINDOW,       /* IDCMP flags                */
  132.    WINDOWDRAG           /* Window flags               */
  133.    | WINDOWCLOSE
  134.    | WINDOWDEPTH
  135.    | SMART_REFRESH,
  136.    NULL,                /* application gadget list    */
  137.    NULL,                /* special checkmark imagery  */
  138.    (UBYTE *)"TWM",      /* window title               */
  139.    NULL,                /* custom screen pointer      */
  140.    NULL,                /* super bitmap pointer       */
  141.    0, 0, 0, 0,          /* min/max width and height   */
  142.    WBENCHSCREEN         /* screen type                */
  143. };
  144.  
  145. struct TextAttr twmFont =    /* 80 column topaz font          */
  146. {
  147.    (UBYTE *)"topaz.font",
  148.    TOPAZ_EIGHTY,
  149.    FS_NORMAL,
  150.    FPF_ROMFONT
  151. };
  152.  
  153. SHORT borderlines[5][2] =    /* simple box around gadgets     */
  154. {
  155.    {-3,              -3            },
  156.    {GADGWIDTH + 3,   -3            },
  157.    {GADGWIDTH + 3,   GADGHEIGHT + 2},
  158.    {-3,              GADGHEIGHT + 2},
  159.    {-3,              -3            }
  160. };
  161.  
  162. struct twmGadget gadgTemplate =
  163. {
  164.    /* intuition gadget structure */
  165.    NULL,                         /* address of next gadget        */
  166.    0, 0, GADGWIDTH, GADGHEIGHT,  /* left, top, width, height      */
  167.    GADGHCOMP,                    /* flags - invert to highlightg  */
  168.    RELVERIFY,                    /* activation flags              */
  169.    BOOLGADGET,                   /* gadget type                   */
  170.    NULL,                         /* address of border struct      */
  171.    NULL,                         /* SelectRender                  */
  172.    NULL,                         /* address of intuitext struct   */
  173.    0,                            /* mutual exclude                */
  174.    NULL,                         /* SpecialInfo                   */
  175.    0,                            /* gadget ID                     */
  176.    NULL,                         /* user data - client replyport  */
  177.  
  178.    /* intuition border structure */
  179.    0, 0,                         /* left edge, top edge           */
  180.    2, 0, JAM1,                   /* front, back pens, draw mode   */
  181.    5,                            /* number of points in border    */
  182.    (SHORT *)borderlines,         /* address of coordinate array   */
  183.    NULL,                         /* address of next border        */
  184.  
  185.    /* intuitext structure */
  186.    1, 0, JAM1,                   /* front, back pens, draw mode   */
  187.    0, 1,                         /* left edge, top edge           */
  188.    &twmFont,                     /* address of TextAttr struct    */
  189.    NULL,                         /* pointer to text               */
  190.    NULL,                         /* address of next IntuiText     */
  191.  
  192.    /* name of gadget as supplied by client */
  193.    "",
  194.  
  195.    /* pointer to message that requested this gadget */
  196.    NULL
  197. };
  198.  
  199.  
  200. extern VOID *OpenWindow(), *OpenLibrary();
  201. extern VOID *CreatePort(), *FindPort();
  202. extern VOID *GetMsg(),     *AllocMem();
  203.  
  204. struct IntuitionBase *IntuitionBase;
  205.  
  206. struct twmGadget *NewGadget();
  207.  
  208. struct MsgPort      *mp;     /* public port (called PORTNAME) */
  209. struct NewWindow    *nw;     /* describes current window      */
  210. struct Window       *w;      /* pointer to current window     */
  211. struct twmMessage   *Tmsg;   /* message arrived at mp         */
  212. struct IntuiMessage *Imsg;   /* message arrived at IDCMP      */
  213. struct twmGadget    *twmg;   /* first gadget in my list       */
  214.  
  215.  
  216. /* main
  217. *
  218. *  Update history:
  219. *     Nov 12/87: Public port no longer checked here if a CLOSEWINDOW event
  220. *     has been detected in the IDCMP loop above.
  221. */
  222.  
  223. main ()
  224. {
  225. register int exitflag;              /* quit input loop if set        */
  226. register int swapflag;              /* use other window (tiny/huge)  */
  227. register int tinyflag;              /* currently using tiny window   */
  228. register UWORD class;               /* IDCMP message class           */
  229. UWORD code;                         /* IDCMP message code            */
  230. int gadgCount;                      /* # of gadgets in my list       */
  231. register struct twmGadget *gadget;  /* gadget clicked in huge window */
  232.  
  233.    exitflag = FALSE;
  234.    swapflag = FALSE;
  235.    tinyflag = TRUE;                 /* start out with tiny window    */
  236.  
  237.    gadgCount = 0;
  238.  
  239.    if ((IntuitionBase = OpenLibrary("intuition.library", 33L)) == NULL)
  240.       CloseStuff(E_OPEN_INTUI);
  241.  
  242.    /* if we already exist, quit  */
  243.    if (FindPort(PORTNAME) != NULL)
  244.       CloseStuff(E_ALREADY_UP);
  245.  
  246.    if ((mp = CreatePort(PORTNAME, 0L)) == NULL)
  247.       CloseStuff(E_OPEN_PORT);
  248.  
  249.    if ((w = OpenWindow(nw = &wtiny)) == NULL)
  250.       CloseStuff(E_OPEN_WINDOW);
  251.  
  252.    /* exitflag set by close gadget on tiny window if no current clients */
  253.    while (!exitflag)
  254.    {
  255.       /* waiting for message at IDCMP or our own port */
  256.       Wait(1L << w->UserPort->mp_SigBit | 1L << mp->mp_SigBit);
  257.  
  258.       /* check IDCMP messages first */
  259.       while (Imsg = GetMsg(w->UserPort))
  260.       {
  261.          class  = Imsg->Class;
  262.          code   = Imsg->Code;
  263.          gadget = (struct twmGadget *)Imsg->IAddress;
  264.          ReplyMsg(Imsg);
  265.  
  266.          if (class == CLOSEWINDOW)
  267.             /* exit from tiny window only if we have no clients, else beep */
  268.             if (tinyflag)
  269.                if (gadgCount == 0)
  270.                   exitflag = TRUE;
  271.                else
  272.                   DisplayBeep(w->WScreen);
  273.             /* close gadget on huge window means switch back to tiny */
  274.             else
  275.                swapflag = TRUE;
  276.  
  277.          /* this message means gadget pressed in huge window   */
  278.          else if (class == GADGETUP)
  279.             if (tinyflag)
  280.                swapflag = TRUE;
  281.             else
  282.             {
  283.                gadget->tgMessage->tmAction = E_OK;  /* return code E_OK  */
  284.                KillGadget(gadget->tgMessage, TRUE); /* get rid of gadget */
  285.                gadgCount--;
  286.                swapflag = TRUE;                     /* switch to tiny    */
  287.                ReplyMsg(gadget->tgMessage);         /* inform client     */
  288.             }
  289.       }
  290.  
  291.       /* now check messages at our public port  */
  292.       while (!exitflag && (Tmsg = GetMsg(mp)))
  293.       {
  294.          /* client going on vacation, create a gadget for him  */
  295.          if (Tmsg->tmAction == TWM_ACTION_ADD)
  296.          {
  297.             if ((gadget = NewGadget(Tmsg)) == NULL)
  298.             {
  299.                Tmsg->tmAction = E_NO_MEM;       /* send regrets */
  300.                ReplyMsg(Tmsg);
  301.             }
  302.             else
  303.                gadgCount++;
  304.  
  305.             /* if the huge window is up right now, close and re-open so that
  306.                we can be sure the new gadget will fit
  307.             */
  308.             if (!tinyflag)
  309.             {
  310.                SavePosCloseW(nw, w);
  311.                CalcGadgPos(nw);
  312.                if ((w = OpenWindow(nw)) == NULL)
  313.                   CloseStuff(E_ABANDON_SHIP);
  314.             }
  315.          }
  316.  
  317.          /* client going right out of business, cancel his gadget */
  318.          else if (Tmsg->tmAction == TWM_ACTION_DELETE)
  319.          {
  320.             /* kill the gadget, and ghost it if huge window is up */
  321.             if (KillGadget(Tmsg, !tinyflag))
  322.             {
  323.                Tmsg->tmAction = E_OK;
  324.                gadgCount--;
  325.             }
  326.             else
  327.                Tmsg->tmAction = E_TASK_UNKNOWN;    /* unrecognized client */
  328.  
  329.             ReplyMsg(Tmsg);
  330.          }
  331.  
  332.          /* some message type we don't know */
  333.          else
  334.          {
  335.             Tmsg->tmAction = E_ACTION_UNKNOWN;
  336.             ReplyMsg(Tmsg);
  337.          }
  338.       }
  339.  
  340.       /* switch between huge and tiny windows */
  341.       if (swapflag)
  342.       {
  343.          swapflag = FALSE;
  344.          SavePosCloseW(nw, w);
  345.  
  346.          nw = tinyflag ? &whuge : &wtiny;
  347.  
  348.          tinyflag = !tinyflag;
  349.  
  350.          /* if we're going to open huge window, reformat gadgets and
  351.             recalculate the window size
  352.          */
  353.          if (!tinyflag)
  354.             CalcGadgPos(nw);
  355.  
  356.          if ((w = OpenWindow(nw)) == NULL)
  357.             CloseStuff(E_ABANDON_SHIP);
  358.       }
  359.    }
  360.  
  361.    CloseStuff(E_OK);
  362. }
  363.  
  364.  
  365. /* CloseStuff
  366. *
  367. *  Close and deallocate everything. If there are any active clients, that
  368. *  means something has gone wrong, so we send them an E_ABANDON_SHIP. The
  369. *  return error codes start at 500 as defined in twm/header.h.
  370. *
  371. *  Update history:
  372. *     Nov 12/87: Clients who have messages pending at our port when we're
  373. *   about to shut down are also sent an E_ABANDON_SHIP.
  374. */
  375.  
  376. CloseStuff (error)
  377. int error;
  378. {
  379. register struct twmGadget *g;
  380.  
  381.    g = twmg;
  382.  
  383.    if (w)               CloseWindow(w);
  384.    if (IntuitionBase)   CloseLibrary(IntuitionBase);
  385.  
  386.    while (g != NULL)
  387.    {
  388.       g->tgMessage->tmAction = E_ABANDON_SHIP;
  389.       ReplyMsg(g->tgMessage);
  390.       KillGadget(g->tgMessage);
  391.    }
  392.  
  393.    if (mp)
  394.    {
  395.       Forbid();
  396.  
  397.       while ((Tmsg = GetMsg(mp)) != NULL)
  398.       {
  399.          Tmsg->tmAction = E_ABANDON_SHIP;
  400.          ReplyMsg(Tmsg);
  401.       }
  402.  
  403.       DeletePort(mp);
  404.  
  405.       Permit();
  406.    }
  407.  
  408.    exit(error);
  409. }
  410.  
  411.  
  412. /* NewGadget
  413. *
  414. *  We have a new client to create a gadget for. We link him to the
  415. *  NextGadget field of the last gadget on the list, set up the new gadget
  416. *  and return its address.
  417. */
  418.  
  419. struct twmGadget *
  420. NewGadget (msg)
  421. struct twmMessage *msg;
  422. {
  423. register struct twmGadget *g, *gprev;
  424. register char *clientname;
  425. register char c;
  426.  
  427.    gprev = NULL;
  428.    g     = twmg;
  429.  
  430.    while (g != NULL)
  431.    {
  432.       gprev = g;
  433.       g = g->tgMynext;
  434.    }
  435.  
  436.    if ((g = AllocMem((long)sizeof(struct twmGadget), 0L)) == NULL)
  437.       return FALSE;
  438.  
  439.    if (gprev != NULL)
  440.    {
  441.       gprev->tgMynext = g;
  442.       gprev->tgGadget.NextGadget = &g->tgGadget;
  443.    }
  444.  
  445.    *g = gadgTemplate;
  446.  
  447.    clientname = msg->tmName + strlen(msg->tmName);
  448.  
  449.    while (clientname > msg->tmName
  450.          && (c = *(clientname - 1)) != ':'
  451.          && c != '/')
  452.       clientname--;
  453.  
  454.    strncpy(g->tgName, clientname, GADGNAMESIZE - 1);
  455.  
  456.    g->tgGadget.GadgetRender   = (APTR)&g->tgBorder;
  457.    g->tgGadget.GadgetText     = &g->tgIText;
  458.  
  459.    g->tgIText.LeftEdge        = (GADGNAMESIZE - strlen(g->tgName)) << 2;
  460.    g->tgIText.IText           = (UBYTE *)g->tgName;
  461.  
  462.    g->tgMessage               = msg;
  463.  
  464.    if (twmg == NULL)
  465.       twmg = g;
  466.  
  467.    return g;
  468. }
  469.  
  470.  
  471. /* KillGadget
  472. *
  473. *  Get rid of a gadget currently on our list. If off_flag is true, the
  474. *  gadget is currently being displayed, so we'll ghost it. Return FALSE
  475. *  if the gadget is not on the list.
  476. */
  477.  
  478. KillGadget (msg, off_flag)
  479. struct twmMessage *msg;
  480. int off_flag;
  481. {
  482. register struct twmGadget *g, *gprev;
  483. int flag;
  484.  
  485.    flag  = FALSE;
  486.    gprev = NULL;
  487.    g     = twmg;
  488.  
  489.    while (g != NULL && !flag)
  490.       if (g->tgMessage->tmMessage.mn_ReplyPort == msg->tmMessage.mn_ReplyPort)
  491.          flag = TRUE;
  492.       else
  493.       {
  494.          gprev = g;
  495.          g = g->tgMynext;
  496.       }
  497.  
  498.    if (flag)
  499.    {
  500.       if (off_flag)
  501.          OffGadget(&g->tgGadget, w, 0L);
  502.  
  503.       RemoveGadget(w, &g->tgGadget);
  504.  
  505.       if (gprev != NULL)
  506.          gprev->tgMynext = g->tgMynext;
  507.       else
  508.          twmg = g->tgMynext;
  509.  
  510.       FreeMem(g, (long)sizeof(struct twmGadget));
  511.    }
  512.  
  513.    return flag;
  514. }
  515.  
  516.  
  517. /* CalcGadgPos
  518. *
  519. *  Position the gadgets in the huge window, and set the window size to
  520. *  accommodate them. The gadgets are displayed four across, to the maximum
  521. *  depth of the screen.
  522. */
  523.  
  524. CalcGadgPos (nw)
  525. register struct NewWindow *nw;
  526. {
  527. register int i, x, y;
  528. register struct twmGadget *g;
  529.  
  530.    i = 0;                  /* gadget counter          */
  531.    x = GADGHGUTTER +  2;   /* starting x position     */
  532.    y = GADGVGUTTER + 10;   /* starting y position     */
  533.  
  534.    g = twmg;               /* address of 1st gadget   */
  535.  
  536.    /* chain through gadget list, writing in new left and top   */
  537.    while (g)
  538.    {
  539.       g->tgGadget.LeftEdge  = x;
  540.       g->tgGadget.TopEdge   = y;
  541.  
  542.       /* if this gadget is in R.H. column, reposition to left of next line */
  543.       if ((i++ & 3) == 3)
  544.       {
  545.          x = GADGHGUTTER + 2;
  546.          y += GADGHEIGHT + GADGVGUTTER;
  547.       }
  548.       else
  549.          x += GADGWIDTH + GADGHGUTTER;
  550.  
  551.       /* chain to next gadget */
  552.       g = g->tgMynext;
  553.    }
  554.  
  555.    /* if there are no gadgets, make the window big enough to hold 1 */
  556.    if (i == 0)
  557.       x = GADGHGUTTER * 2 + GADGWIDTH + 2;
  558.  
  559.    /* if less than 4 gadgets, make window just big enough to hold them */
  560.    if (i < 4)
  561.       nw->Width = x;
  562.    /* otherwise make it full width */
  563.    else
  564.       nw->Width = GADGHGUTTER + 2 + (GADGHGUTTER + GADGWIDTH) * 4;
  565.  
  566.    /* if the last gadget on the list falls at the R.H. edge of the window,
  567.       y is already big enough; otherwise, make it so
  568.    */
  569.    if ((i & 3) == 0)
  570.       nw->Height = y;
  571.    else
  572.       nw->Height = y + GADGVGUTTER + GADGHEIGHT;
  573.  
  574.    /* make sure that new dimensions of window will still fit on the screen...
  575.       if necessary reposition the window
  576.    */
  577.    if (nw->LeftEdge + nw->Width > w->WScreen->Width)
  578.       nw->LeftEdge = w->WScreen->Width - nw->Width;
  579.  
  580.    if (nw->TopEdge + nw->Height > w->WScreen->Height)
  581.       nw->TopEdge = w->WScreen->Height - nw->Height;
  582.  
  583.    /* install our first gadget as the new window's first gadget */
  584.    nw->FirstGadget = &twmg->tgGadget;
  585. }
  586.  
  587.  
  588. /* SavePosCloseW
  589. *
  590. *  Save the current window position and size in the NewWindow structure for
  591. *  that window, then close the window.
  592. */
  593.  
  594. SavePosCloseW (nw, w)
  595. register struct NewWindow *nw;
  596. register struct Window *w;
  597. {
  598.    nw->LeftEdge = w->LeftEdge;
  599.    nw->TopEdge  = w->TopEdge;
  600.    nw->Width    = w->Width;
  601.    nw->Height   = w->Height;
  602.  
  603.    CloseWindow(w);
  604. }
  605.  
  606.  
  607. /* When compiling with Aztec, the following two stubs replace the Aztec
  608.    code for parsing the command line, thus reducing code size a bit
  609. */
  610.  
  611. #ifdef AZTEC_C
  612.  
  613. _wb_parse ()
  614. {
  615. }
  616.  
  617.  
  618. _cli_parse ()
  619. {
  620. }
  621.  
  622. #endif !AZTEC_C
  623.