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