home *** CD-ROM | disk | FTP | other *** search
- /* twm.c
-
- Tiny-Window Manager v1.0 (c) 1987 Transactor Publishing Inc.
- by Nick Sullivan
-
- This program is freely redistributable provided that no charge is made
- for the redistribution beyond reasonable reproduction costs, and that no
- changes are made except with the prior written approval of Transactor
- Publishing Inc., 85 West Wilmot St. #10, Richmond Hill, Ontario L4B 1K7.
-
- ------------------------------------------------------------------------
-
- TWM provides a storage area in which applications that are inactive, but
- running, can wait to be re-activated without using any chip RAM. It thus
- provides an alternative to the "tiny window" approach to minimizing chip
- RAM use, as exemplified by such programs as Uedit and PopColours.
-
- When TWM is run, it puts up its own tiny window, and creates a public
- message port. Client applications should check for the existence of this
- port and, if it is present, send a "twMessage" (as defined in twm.h)
- with the tmAction field set to TWM_ACTION_ADD when they wish to go to
- sleep. When the TWM window is clicked in thereafter, a larger window will
- be put up containing gadgets bearing the names of each client application
- that has been added. Clicking on one of these gadgets will cause the
- twMessage to be replied to, which is the signal for them to reawaken.
- At the same time as the reply is sent, the large TWM window will be taken
- down, and the gadget for that client removed.
-
- If a client wishes to reactivate itself before its TWM gadget is clicked,
- or if it wishes to exit altogether, it should first send a twMessage with
- the twmAction field set to TWM_ACTION_DELETE.
-
- The twmAction field is used by TWM to return a code to the client that
- indicates whether the requested operation was successful. The code for
- success is E_OK. Other possibilities are:
-
- E_NO_MEM
- A client has asked TWM to add a gadget for him, but TWM was unable to
- allocate memory for the gadget structure.
- E_ABANDON_SHIP
- 1) A client has asked TWM to add a gadget for him while TWM had its
- large window up. In this case, TWM closes the large window, and
- re-opens it after rethinking the gadget positions and the window size.
- 2) The user has clicked on TWM's tiny window, or closed its large
- window, causing TWM to close the current window and attempt to open the
- other one.
- If the window open fails in either of these cases, TWM sends all
- current clients this error message, then exits, since it has no means
- of recovery.
- E_TASK_UNKNOWN
- A client has sent a TWM_ACTION_DELETE, but TWM does not currently have
- a gadget for that client.
- E_ACTION_UNKNOWN
- A message has been received with the twmAction field set to an unknown
- code... currently the only possibilities are TWM_ACTION_DELETE and
- TWM_ACTION_ADD.
-
-
- Sample C code for applications wishing to interface correctly with TWM
- is contained in the file twmClient.c. The header file twm.h is also
- required. Before using other functions in this file, the client should
- call twmInit().
- The function PostMe() in twmClient.c should be called when the
- client wishes to deactivate. If this function returns TRUE, the client
- should resume its life as an active application. If it returns FALSE,
- either TWM is not present in the system or else the attempt to post failed
- for some reason. In this case, the client should take an alternative
- approach to deactivated living (like making its own tiny window), or else
- not allow itself to be deactivated.
- Programs that wish to be able to receive messages even when deactivated
- (time-outs, for example), will need to use a modified version of PostMe().
- If such a program wishes to reactivate before its gadget has been clicked
- in the TWM window, it should first call UnPostMe() (no arguments, no
- return) to inform TWM that the gadget should be taken down. Programs that
- will NOT need to call UnPostMe() (i.e. most programs) can use a version
- of twmClient.c from which the UnPostMe() function and all references to
- the global variable Delmsg have been removed.
- Before the first call to PostMe(), the function twmInit() must be
- invoked to set up the required messages and ports that PostMe() will need.
- Before the client exits, it should call the function UnPostMe() to
- deallocate resources twmInit() has allocated.
-
- Update history:
-
- Nov 12/87: Now making absolutely sure message port is cleared before
- exiting.
- */
-
- #include "header/twm.h"
-
-
- struct Gadget WakeUpGadget =
- {
- NULL, /* address of next gadget */
- 2, 10, 116, 10, /* left, top, width, height */
- GADGHNONE, /* flags - no highlighting */
- RELVERIFY, /* activation flags */
- BOOLGADGET, /* gadget type */
- NULL, /* no imagery */
- NULL, /* no alternate imagery */
- NULL, /* no text */
- 0, /* mutual exclude */
- NULL, /* SpecialInfo */
- 0, /* gadget ID */
- NULL, /* user data */
- };
-
- struct NewWindow wtiny =
- {
- 480, 60, 120, 20, /* left, top, width, height */
- 0, 1, /* detail pen, block pen */
- GADGETUP /* IDCMP flags */
- | CLOSEWINDOW,
- WINDOWDRAG /* Window flags */
- | WINDOWCLOSE
- | WINDOWDEPTH,
- &WakeUpGadget, /* application gadget list */
- NULL, /* special checkmark imagery */
- (UBYTE *)"TWM", /* window title */
- NULL, /* custom screen pointer */
- NULL, /* super bitmap pointer */
- 0, 0, 0, 0, /* min/max width and height */
- WBENCHSCREEN /* screen type */
- };
-
- struct NewWindow whuge =
- {
- 480, 60, 0, 0, /* left, top, width, height */
- 0, 1, /* detail pen, block pen */
- GADGETUP
- | CLOSEWINDOW, /* IDCMP flags */
- WINDOWDRAG /* Window flags */
- | WINDOWCLOSE
- | WINDOWDEPTH
- | SMART_REFRESH,
- NULL, /* application gadget list */
- NULL, /* special checkmark imagery */
- (UBYTE *)"TWM", /* window title */
- NULL, /* custom screen pointer */
- NULL, /* super bitmap pointer */
- 0, 0, 0, 0, /* min/max width and height */
- WBENCHSCREEN /* screen type */
- };
-
- struct TextAttr twmFont = /* 80 column topaz font */
- {
- (UBYTE *)"topaz.font",
- TOPAZ_EIGHTY,
- FS_NORMAL,
- FPF_ROMFONT
- };
-
- SHORT borderlines[5][2] = /* simple box around gadgets */
- {
- {-3, -3 },
- {GADGWIDTH + 3, -3 },
- {GADGWIDTH + 3, GADGHEIGHT + 2},
- {-3, GADGHEIGHT + 2},
- {-3, -3 }
- };
-
- struct twmGadget gadgTemplate =
- {
- /* intuition gadget structure */
- NULL, /* address of next gadget */
- 0, 0, GADGWIDTH, GADGHEIGHT, /* left, top, width, height */
- GADGHCOMP, /* flags - invert to highlightg */
- RELVERIFY, /* activation flags */
- BOOLGADGET, /* gadget type */
- NULL, /* address of border struct */
- NULL, /* SelectRender */
- NULL, /* address of intuitext struct */
- 0, /* mutual exclude */
- NULL, /* SpecialInfo */
- 0, /* gadget ID */
- NULL, /* user data - client replyport */
-
- /* intuition border structure */
- 0, 0, /* left edge, top edge */
- 2, 0, JAM1, /* front, back pens, draw mode */
- 5, /* number of points in border */
- (SHORT *)borderlines, /* address of coordinate array */
- NULL, /* address of next border */
-
- /* intuitext structure */
- 1, 0, JAM1, /* front, back pens, draw mode */
- 0, 1, /* left edge, top edge */
- &twmFont, /* address of TextAttr struct */
- NULL, /* pointer to text */
- NULL, /* address of next IntuiText */
-
- /* name of gadget as supplied by client */
- "",
-
- /* pointer to message that requested this gadget */
- NULL
- };
-
-
- extern VOID *OpenWindow(), *OpenLibrary();
- extern VOID *CreatePort(), *FindPort();
- extern VOID *GetMsg(), *AllocMem();
-
- struct IntuitionBase *IntuitionBase;
-
- struct twmGadget *NewGadget();
-
- struct MsgPort *mp; /* public port (called PORTNAME) */
- struct NewWindow *nw; /* describes current window */
- struct Window *w; /* pointer to current window */
- struct twmMessage *Tmsg; /* message arrived at mp */
- struct IntuiMessage *Imsg; /* message arrived at IDCMP */
- struct twmGadget *twmg; /* first gadget in my list */
-
-
- /* main
- *
- * Update history:
- * Nov 12/87: Public port no longer checked here if a CLOSEWINDOW event
- * has been detected in the IDCMP loop above.
- */
-
- main ()
- {
- register int exitflag; /* quit input loop if set */
- register int swapflag; /* use other window (tiny/huge) */
- register int tinyflag; /* currently using tiny window */
- register UWORD class; /* IDCMP message class */
- UWORD code; /* IDCMP message code */
- int gadgCount; /* # of gadgets in my list */
- register struct twmGadget *gadget; /* gadget clicked in huge window */
-
- exitflag = FALSE;
- swapflag = FALSE;
- tinyflag = TRUE; /* start out with tiny window */
-
- gadgCount = 0;
-
- if ((IntuitionBase = OpenLibrary("intuition.library", 33L)) == NULL)
- CloseStuff(E_OPEN_INTUI);
-
- /* if we already exist, quit */
- if (FindPort(PORTNAME) != NULL)
- CloseStuff(E_ALREADY_UP);
-
- if ((mp = CreatePort(PORTNAME, 0L)) == NULL)
- CloseStuff(E_OPEN_PORT);
-
- if ((w = OpenWindow(nw = &wtiny)) == NULL)
- CloseStuff(E_OPEN_WINDOW);
-
- /* exitflag set by close gadget on tiny window if no current clients */
- while (!exitflag)
- {
- /* waiting for message at IDCMP or our own port */
- Wait(1L << w->UserPort->mp_SigBit | 1L << mp->mp_SigBit);
-
- /* check IDCMP messages first */
- while (Imsg = GetMsg(w->UserPort))
- {
- class = Imsg->Class;
- code = Imsg->Code;
- gadget = (struct twmGadget *)Imsg->IAddress;
- ReplyMsg(Imsg);
-
- if (class == CLOSEWINDOW)
- /* exit from tiny window only if we have no clients, else beep */
- if (tinyflag)
- if (gadgCount == 0)
- exitflag = TRUE;
- else
- DisplayBeep(w->WScreen);
- /* close gadget on huge window means switch back to tiny */
- else
- swapflag = TRUE;
-
- /* this message means gadget pressed in huge window */
- else if (class == GADGETUP)
- if (tinyflag)
- swapflag = TRUE;
- else
- {
- gadget->tgMessage->tmAction = E_OK; /* return code E_OK */
- KillGadget(gadget->tgMessage, TRUE); /* get rid of gadget */
- gadgCount--;
- swapflag = TRUE; /* switch to tiny */
- ReplyMsg(gadget->tgMessage); /* inform client */
- }
- }
-
- /* now check messages at our public port */
- while (!exitflag && (Tmsg = GetMsg(mp)))
- {
- /* client going on vacation, create a gadget for him */
- if (Tmsg->tmAction == TWM_ACTION_ADD)
- {
- if ((gadget = NewGadget(Tmsg)) == NULL)
- {
- Tmsg->tmAction = E_NO_MEM; /* send regrets */
- ReplyMsg(Tmsg);
- }
- else
- gadgCount++;
-
- /* if the huge window is up right now, close and re-open so that
- we can be sure the new gadget will fit
- */
- if (!tinyflag)
- {
- SavePosCloseW(nw, w);
- CalcGadgPos(nw);
- if ((w = OpenWindow(nw)) == NULL)
- CloseStuff(E_ABANDON_SHIP);
- }
- }
-
- /* client going right out of business, cancel his gadget */
- else if (Tmsg->tmAction == TWM_ACTION_DELETE)
- {
- /* kill the gadget, and ghost it if huge window is up */
- if (KillGadget(Tmsg, !tinyflag))
- {
- Tmsg->tmAction = E_OK;
- gadgCount--;
- }
- else
- Tmsg->tmAction = E_TASK_UNKNOWN; /* unrecognized client */
-
- ReplyMsg(Tmsg);
- }
-
- /* some message type we don't know */
- else
- {
- Tmsg->tmAction = E_ACTION_UNKNOWN;
- ReplyMsg(Tmsg);
- }
- }
-
- /* switch between huge and tiny windows */
- if (swapflag)
- {
- swapflag = FALSE;
- SavePosCloseW(nw, w);
-
- nw = tinyflag ? &whuge : &wtiny;
-
- tinyflag = !tinyflag;
-
- /* if we're going to open huge window, reformat gadgets and
- recalculate the window size
- */
- if (!tinyflag)
- CalcGadgPos(nw);
-
- if ((w = OpenWindow(nw)) == NULL)
- CloseStuff(E_ABANDON_SHIP);
- }
- }
-
- CloseStuff(E_OK);
- }
-
-
- /* CloseStuff
- *
- * Close and deallocate everything. If there are any active clients, that
- * means something has gone wrong, so we send them an E_ABANDON_SHIP. The
- * return error codes start at 500 as defined in twm/header.h.
- *
- * Update history:
- * Nov 12/87: Clients who have messages pending at our port when we're
- * about to shut down are also sent an E_ABANDON_SHIP.
- */
-
- CloseStuff (error)
- int error;
- {
- register struct twmGadget *g;
-
- g = twmg;
-
- if (w) CloseWindow(w);
- if (IntuitionBase) CloseLibrary(IntuitionBase);
-
- while (g != NULL)
- {
- g->tgMessage->tmAction = E_ABANDON_SHIP;
- ReplyMsg(g->tgMessage);
- KillGadget(g->tgMessage);
- }
-
- if (mp)
- {
- Forbid();
-
- while ((Tmsg = GetMsg(mp)) != NULL)
- {
- Tmsg->tmAction = E_ABANDON_SHIP;
- ReplyMsg(Tmsg);
- }
-
- DeletePort(mp);
-
- Permit();
- }
-
- exit(error);
- }
-
-
- /* NewGadget
- *
- * We have a new client to create a gadget for. We link him to the
- * NextGadget field of the last gadget on the list, set up the new gadget
- * and return its address.
- */
-
- struct twmGadget *
- NewGadget (msg)
- struct twmMessage *msg;
- {
- register struct twmGadget *g, *gprev;
- register char *clientname;
- register char c;
-
- gprev = NULL;
- g = twmg;
-
- while (g != NULL)
- {
- gprev = g;
- g = g->tgMynext;
- }
-
- if ((g = AllocMem((long)sizeof(struct twmGadget), 0L)) == NULL)
- return FALSE;
-
- if (gprev != NULL)
- {
- gprev->tgMynext = g;
- gprev->tgGadget.NextGadget = &g->tgGadget;
- }
-
- *g = gadgTemplate;
-
- clientname = msg->tmName + strlen(msg->tmName);
-
- while (clientname > msg->tmName
- && (c = *(clientname - 1)) != ':'
- && c != '/')
- clientname--;
-
- strncpy(g->tgName, clientname, GADGNAMESIZE - 1);
-
- g->tgGadget.GadgetRender = (APTR)&g->tgBorder;
- g->tgGadget.GadgetText = &g->tgIText;
-
- g->tgIText.LeftEdge = (GADGNAMESIZE - strlen(g->tgName)) << 2;
- g->tgIText.IText = (UBYTE *)g->tgName;
-
- g->tgMessage = msg;
-
- if (twmg == NULL)
- twmg = g;
-
- return g;
- }
-
-
- /* KillGadget
- *
- * Get rid of a gadget currently on our list. If off_flag is true, the
- * gadget is currently being displayed, so we'll ghost it. Return FALSE
- * if the gadget is not on the list.
- */
-
- KillGadget (msg, off_flag)
- struct twmMessage *msg;
- int off_flag;
- {
- register struct twmGadget *g, *gprev;
- int flag;
-
- flag = FALSE;
- gprev = NULL;
- g = twmg;
-
- while (g != NULL && !flag)
- if (g->tgMessage->tmMessage.mn_ReplyPort == msg->tmMessage.mn_ReplyPort)
- flag = TRUE;
- else
- {
- gprev = g;
- g = g->tgMynext;
- }
-
- if (flag)
- {
- if (off_flag)
- OffGadget(&g->tgGadget, w, 0L);
-
- RemoveGadget(w, &g->tgGadget);
-
- if (gprev != NULL)
- gprev->tgMynext = g->tgMynext;
- else
- twmg = g->tgMynext;
-
- FreeMem(g, (long)sizeof(struct twmGadget));
- }
-
- return flag;
- }
-
-
- /* CalcGadgPos
- *
- * Position the gadgets in the huge window, and set the window size to
- * accommodate them. The gadgets are displayed four across, to the maximum
- * depth of the screen.
- */
-
- CalcGadgPos (nw)
- register struct NewWindow *nw;
- {
- register int i, x, y;
- register struct twmGadget *g;
-
- i = 0; /* gadget counter */
- x = GADGHGUTTER + 2; /* starting x position */
- y = GADGVGUTTER + 10; /* starting y position */
-
- g = twmg; /* address of 1st gadget */
-
- /* chain through gadget list, writing in new left and top */
- while (g)
- {
- g->tgGadget.LeftEdge = x;
- g->tgGadget.TopEdge = y;
-
- /* if this gadget is in R.H. column, reposition to left of next line */
- if ((i++ & 3) == 3)
- {
- x = GADGHGUTTER + 2;
- y += GADGHEIGHT + GADGVGUTTER;
- }
- else
- x += GADGWIDTH + GADGHGUTTER;
-
- /* chain to next gadget */
- g = g->tgMynext;
- }
-
- /* if there are no gadgets, make the window big enough to hold 1 */
- if (i == 0)
- x = GADGHGUTTER * 2 + GADGWIDTH + 2;
-
- /* if less than 4 gadgets, make window just big enough to hold them */
- if (i < 4)
- nw->Width = x;
- /* otherwise make it full width */
- else
- nw->Width = GADGHGUTTER + 2 + (GADGHGUTTER + GADGWIDTH) * 4;
-
- /* if the last gadget on the list falls at the R.H. edge of the window,
- y is already big enough; otherwise, make it so
- */
- if ((i & 3) == 0)
- nw->Height = y;
- else
- nw->Height = y + GADGVGUTTER + GADGHEIGHT;
-
- /* make sure that new dimensions of window will still fit on the screen...
- if necessary reposition the window
- */
- if (nw->LeftEdge + nw->Width > w->WScreen->Width)
- nw->LeftEdge = w->WScreen->Width - nw->Width;
-
- if (nw->TopEdge + nw->Height > w->WScreen->Height)
- nw->TopEdge = w->WScreen->Height - nw->Height;
-
- /* install our first gadget as the new window's first gadget */
- nw->FirstGadget = &twmg->tgGadget;
- }
-
-
- /* SavePosCloseW
- *
- * Save the current window position and size in the NewWindow structure for
- * that window, then close the window.
- */
-
- SavePosCloseW (nw, w)
- register struct NewWindow *nw;
- register struct Window *w;
- {
- nw->LeftEdge = w->LeftEdge;
- nw->TopEdge = w->TopEdge;
- nw->Width = w->Width;
- nw->Height = w->Height;
-
- CloseWindow(w);
- }
-
-
- /* When compiling with Aztec, the following two stubs replace the Aztec
- code for parsing the command line, thus reducing code size a bit
- */
-
- #ifdef AZTEC_C
-
- _wb_parse ()
- {
- }
-
-
- _cli_parse ()
- {
- }
-
- #endif !AZTEC_C
-