home *** CD-ROM | disk | FTP | other *** search
- /*
- * Task Priority Manager
- * Copyright 1993, 1994 Barry McConnell
- * bmccnnll@tcd.ie
- *
- * The main program loop
-
- * Set tab stops to 4 when editing this file.
- */
-
- #define MAIN /* tells header file to define (as opposed to declare) variables */
- #include "PriMan.h"
-
- long __oslibversion = 36; /* tell SAS/C what library versions we need */
- static UBYTE version[] = "$VER: PriMan "VERSION" ("DATE")"; /* version string */
-
- /*
- * First we have some global variables. These would normally go in the
- * header file, but it's easier to define structures with initial data
- * here. First is the broker structure for the Commodity interface.
- */
- struct NewBroker newBroker =
- {
- NB_VERSION,
- "PriMan",
- "PriMan "VERSION" by Barry McConnell",
- "Task priority manager",
- NBU_UNIQUE | NBU_NOTIFY,
- COF_SHOW_HIDE,
- 0, 0, 0
- };
-
- /*
- * The starting point for PriMan. First we need to parse the ToolTypes and
- * initialiase some variables. Then we set up the Commodity interface if
- * necessary. And finally, we handle all the possible messages that can
- * arrive at our ports. SAS/C kindly opens all the libraries for us, so we
- * don't need to worry about that...
- */
- void main(int argc, char *argv[])
- {
- BOOL more, /* might be more messages waiting for us */
- help; /* user has asked for help at startup time */
-
- ULONG signal, /* signal type received */
- class, /* message type received */
- winSignal, /* window port signal bit */
- appSignal, /* AppIcon port signal bit */
- cxSignal; /* Commodities port signal bit */
-
- UWORD shift, /* Shift key was pressed */
- ctrl; /* Ctrl key was pressed */
-
- WORD code, /* code within message structure */
- key, /* VANILLAKEY code, converted to lower-case */
- raw; /* RAWKEY code */
-
- struct Message *message; /* message received from port */
- struct IntuiMessage *imsg; /* GadTools-parsed message */
- struct AmigaGuideMsg *amsg; /* AmigaGuide-parsed message */
- struct Window *window; /* window the message refers to */
- struct Gadget *selectedGad; /* gadget the message refers to */
-
- /*
- * Template for the error messages PriMan can give before exiting.
- */
- struct EasyStruct errorMessage =
- {
- sizeof(struct EasyStruct),
- 0,
- "PriMan Fatal Error",
- "%s",
- "Okay"
- };
-
- /*
- * Check what OS version we're running under, so we can take advantage
- * of some new 3.x features where possible.
- */
- osver = SysBase -> LibNode.lib_Version;
-
- /*
- * Create the three message ports that PriMan needs to talk to the
- * outside world with, and note their signal bits.
- */
- winPort = CreateMsgPort();
- appPort = CreateMsgPort();
- cxPort = CreateMsgPort();
- winSignal = 1L << winPort -> mp_SigBit;
- appSignal = 1L << appPort -> mp_SigBit;
- cxSignal = 1L << cxPort -> mp_SigBit;
-
- /*
- * We need to get the TextAttr structures to point to their font names.
- */
- propTA.ta_Name = propName;
- monoTA.ta_Name = monoName;
-
- /*
- * Here we set up the defaults for all the settings variables, in case
- * the user doesn't change them. Note that the font information get set
- * to 0 on startup by the compiler.
- */
- winLeft = winTop = -1;
- winWidth = winHeight = 0;
- iconLeft = iconTop = priority = 0;
- confirm = commodity = popup = iconify = TRUE;
- refresh = SMARTWINDOW;
- open = DEFAULTSCREEN;
- strcpy(hotkey, "control alt p");
- help = FALSE; /* default is no immediate online help */
-
- /*
- * When being iconfied, we use another icon file, which gets filled in
- * with PriMan's icon imagery below if possible. This call should never
- * fail, so I don't do any checking on it.
- */
- wbIcon = GetDefDiskObject(WBTOOL);
-
- /*
- * Before parsing the ToolTypes, we must open PriMan's .info file. If
- * we're running from the Workbench (argc is 0), we take the filename
- * from the WBStartup structure. From the Shell, we use argv[0]. We
- * then prefix this by PROGDIR: to form a full pathname.
- */
- sprintf(myName, "PROGDIR:%s",
- argc ? argv[0] : ((struct WBStartup *)argv) -> sm_ArgList -> wa_Name);
- if (myIcon = GetDiskObject(myName))
- {
- if (myIcon -> do_Type != WBTOOL)
- {
- /*
- * Sanity failure! We've found a .info file, but it doesn't
- * really belong to PriMan.
- */
- FreeDiskObject(myIcon);
- myIcon = NULL;
- }
- else
- {
- /*
- * The correct .info file is open, so we first parse the
- * ToolTypes from it, and then copy the imagery across to the
- * AppIcon.
- */
- HandleToolTypes(myIcon -> do_ToolTypes, &help);
-
- wbIcon -> do_Gadget.Flags = myIcon -> do_Gadget.Flags;
- wbIcon -> do_Gadget.Width = myIcon -> do_Gadget.Width;
- wbIcon -> do_Gadget.Height = myIcon -> do_Gadget.Height;
- wbIcon -> do_CurrentX = iconLeft;
- wbIcon -> do_CurrentY = iconTop;
- wbIcon -> do_Gadget.GadgetRender = myIcon -> do_Gadget.GadgetRender;
- wbIcon -> do_Gadget.SelectRender = myIcon -> do_Gadget.SelectRender;
- }
- }
-
- /*
- * If we're running from the Shell, there may be additional arguments.
- */
- if (argc)
- {
- /*
- * It's possible the user is just enquiring as to what PriMan is
- * all about (first argument is a question mark), so we print out
- * some info and exit.
- */
- if (argc > 1 && !strcmp(argv[1], "?"))
- {
- Print( "PriMan " VERSION " by Barry McConnell - The Task Priority Manager\n" \
- "Usage: PriMan [COMMODITY=YES|NO] [CX_POPUP=YES|NO] [CX_POPKEY=hotkey]\n" \
- " [CX_PRIORITY=priority] [LEFT=left edge] [TOP=top edge]\n" \
- " [WIDTH=width] [HEIGHT=height] [GADFONT=font name]\n" \
- " [GADSIZE=font size] [LISTFONT=font name] [LISTSIZE=font size]\n" \
- " [REFRESH=SMART|SIMPLE] [SCREEN=DEFAULT|FRONT] [ICONLEFT=left edge]\n"\
- " [ICONTOP=top edge] [CONFIRM=YES|NO] [ICONIFY=YES|NO]\n" \
- " [TOOLPRI=task priority]\n" \
- "Type \"PriMan HELP\" to get AmigaGuide help.\n");
- error = ALL_OKAY;
- }
- else
- HandleToolTypes(argv, &help);
- }
-
- /*
- * Now we install ourselves as a Commodity, assuming the error variable
- * wasn't set above. If there is an existing PriMan running as a
- * Commodity, error will be set here, and the main loop below will fall
- * out immediately.
- */
- if (!error)
- {
- newBroker.nb_Port = cxPort;
- newBroker.nb_Pri = priority;
- if (commodity)
- SetupCommodity();
-
- if (!error)
- {
- /*
- * Since pos gets initialised to 0 by the compiler, and there
- * won't actually be a task selected to start with, we change
- * it to -1 here. This isn't strictly necessary (since some
- * code later on does this for us), but it tells the function
- * which creates the main window's gadgetry to disable some
- * buttons when they are being created, so we avoid an initial
- * "flashing" the first time the window is opened.
- */
- pos = -1;
-
- /*
- * Here we define what happens on startup. If the user does not
- * want us to "popup when launched", and we are running as a
- * Commodity or can be iconified, then we don't open a window,
- * and instead create an AppIcon if necessary. Otherwise, we
- * must open the main window.
- */
- if (!popup && (iconify || commodity))
- Iconify();
- else
- Show();
-
- if (!error)
- /*
- * If the user wants online help, we wait until here to
- * fire up AmigaGuide (in case something went wrong setting
- * stuff up above).
- */
- if (help)
- Help();
- }
- }
-
- /*
- * This is the heart of PriMan. As long as nothing has gone wrong, we
- * go round in a loop waiting for a message from any of our three
- * ports, the AmigaGuide port (if applicable), or a Ctrl-C/F signal,
- * handle that message, then loop back.
- */
- while (!error)
- {
- signal = Wait(winSignal | appSignal | cxSignal | guideSignal |
- SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_F);
- do
- {
- /*
- * We use the "more" variable to let us know when to stop
- * checking each port for more messages. If it's still set to
- * FALSE at the end of the loop, it means all ports are now
- * empty, and we can go back to sleep.
- */
- more = FALSE;
-
- /*
- * If we get a Ctrl-C signal, we immediately abort the
- * while loop, without checking the other ports.
- */
- if (signal & SIGBREAKF_CTRL_C)
- {
- error = ALL_OKAY;
- break;
- }
-
- /*
- * A Ctrl-F signal means we should make ourselves visible.
- */
- if (signal & SIGBREAKF_CTRL_F)
- Show();
-
- /*
- * The only possible message from the AppIcon's port will
- * tell us to wake up, so if we receive it, we open the
- * main window, and reply to the message.
- */
- if (signal & appSignal && (message = GetMsg(appPort)))
- {
- ReplyMsg(message);
- Show();
- more = TRUE;
- }
-
- /*
- * We need to handle lots of difference cases if we get a
- * message from the Commodities port. We might need to
- * open (or bring to the front) the main window if the
- * Show button was pressed in Exchange, or the hotkey was
- * pressed, or another PriMan tried installing itself as a
- * Commodity. We might need to hide our windows if the Hide
- * button was pressed in Exchange. And finally, if Exchange
- * sends us a Kill message, we must close down. Any other
- * message types are ignored.
- */
- if (signal & cxSignal && (message = GetMsg(cxPort)))
- {
- switch (class = CxMsgType((CxMsg *)message))
- {
- case CXM_COMMAND:
- switch (CxMsgID((CxMsg *)message))
- {
- case CXCMD_APPEAR:
- case CXCMD_UNIQUE:
- Show();
- break;
-
- case CXCMD_DISAPPEAR:
- Hide();
- break;
-
- case CXCMD_KILL:
- error = ALL_OKAY;
- break;
- }
- break;
-
- case CXM_IEVENT: /* user pressed hotkey */
- Show();
- break;
- }
- ReplyMsg(message);
- more = TRUE;
- }
-
- /*
- * Here is a very basic AmigaGuide handler. We only check for
- * one type of message and ignore all the rest - no error
- * handling is performed. The message we need to check for is
- * ActiveToolID: this means the process spawned in the Help()
- * function has successfully opened the PriMan.guide file, and
- * is waiting for further instructions. We actually call Help()
- * again in this case, as now that we have a valid handle on
- * the guide file, that function will jump to the first node
- * in it for us.
- */
- if (signal & guideSignal && (amsg = GetAmigaGuideMsg(guideHandle)))
- {
- if (amsg -> agm_Type == ActiveToolID)
- Help();
- ReplyAmigaGuideMsg(amsg);
- more = TRUE;
- }
-
- /* This is the fun bit. A message to our window port can
- * come from either window. The easiest thing to do is to
- * simply pass it onto a separate procedure. But first,
- * there is a little bit of complicated stuff involving
- * window resizing...
- */
- if (signal & winSignal && (imsg = GT_GetIMsg(winPort)))
- {
- /*
- * Make a note of the important information in the
- * message.
- */
- key = raw = 0;
- class = imsg -> Class;
- selectedGad = imsg -> IAddress;
- code = imsg -> Code;
- shift = imsg -> Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT);
- ctrl = imsg -> Qualifier & IEQUALIFIER_CONTROL;
- window = imsg -> IDCMPWindow;
-
- /*
- * There are several events which must be handled before
- * replying to the message.
- */
- switch (class)
- {
- /*
- * If we get a SIZEVERIFY message, we need to remove
- * all the GadTools gadgets before we allow the user
- * to move the size gadget. This will prevent Intuition
- * doing any unnecessary refreshing, and will also
- * ensure the window's borders don't get clobbered.
- */
- case IDCMP_SIZEVERIFY:
- RemoveGList(mainWindow, mainGads, -1);
- window = NULL;
- break;
-
- /*
- * A NEWSIZE message generally means all the gadgets
- * must be deallocated, recreated, and added back into
- * the window. However, if the window size did not
- * actually change, there is no need to do this, and
- * instead we just quietly add back in the existing
- * gadget list (which didn't actually get deallocated
- * on SIZEVERIFY).
- */
- case IDCMP_NEWSIZE:
- if (winWidth == mainWindow -> Width && winHeight == mainWindow -> Height)
- AddGList(mainWindow, mainGads, ~0, -1, NULL);
- else
- {
- GetListTop(); /* so we can restore it later */
- FreeGadgets(mainGads);
- mainGads = NULL;
- OpenMainWindow();
- }
- window = NULL;
- break;
-
- /*
- * In addition, while we could pass on the INTUITICKS
- * messages to the appropriate procedures, these
- * actually do nothing with this event, so in order
- * not to waste CPU time, we ignore this event by
- * setting the window variable to NULL (this is also
- * done for the two events above, as they have been
- * appropriately handled right here).
- */
- case IDCMP_INTUITICKS:
- window = NULL;
- break;
-
- /*
- * We want to do a little mapping of VANILLAKEY codes.
- * If Shift is being pressed, we must set bit 5 of the
- * keycode to convert it to lower-case. For Ctrl, we
- * ensure both bits 5 and 6 are set. The end result is
- * a lower-case version of any keypress handled by this
- * Intuition event. (Check an ASCII table to see how
- * keypresses come out with these modifiers!)
- */
- case IDCMP_VANILLAKEY:
- key = code | (shift ? 32 : 0) | (ctrl ? 96 : 0);
- break;
-
- /*
- * RAWKEY codes don't need mapping, and in fact we only
- * use these for the Help key!
- */
- case IDCMP_RAWKEY:
- raw = code;
- break;
- }
-
- GT_ReplyIMsg(imsg);
- more = TRUE;
-
- /*
- * Now we despatch the message to another function if
- * it is still necessary (i.e. the window variable
- * wasn't cleared above).
- */
- if (window == mainWindow)
- HandleMainWindow(class, code, key, raw, shift, ctrl, selectedGad);
- else if (window == setWindow)
- HandleSettingsWindow(class, code, key, raw, shift, selectedGad);
- }
- }
- while (more); /* could be more messages waiting for us */
- }
-
- /*
- * If we've got this far, either something bad happened, or the "all
- * okay" error condition was raised (meaning the user wanted us to
- * quit). We need to shut down windows, free up gadgets, deallocate
- * memory, and free up other system stuff. Then - if necessary - an
- * error requester is displayed.
- */
- CloseSettingsWindow();
- CloseMainWindow();
- FreeRemember(&memoryKey, TRUE);
-
- DeleteCxObjAll(broker);
- FreeAslRequest(propFontReq);
- FreeAslRequest(monoFontReq);
-
- if (guideHandle)
- CloseAmigaGuide(guideHandle);
- CloseLibrary(AmigaGuideBase);
-
- if (appIcon)
- RemoveAppIcon(appIcon);
- if (wbIcon)
- FreeDiskObject(wbIcon);
- if (myIcon)
- FreeDiskObject(myIcon);
-
- WipePort(winPort);
- WipePort(appPort);
- WipePort(cxPort);
-
- switch (error) /* there does not necessarily have to be one */
- {
- case LOCK_ERROR:
- SimpleRequest(&errorMessage, "Can't lock default public screen.");
- break;
- case VISINFO_ERROR:
- SimpleRequest(&errorMessage, "Can't get VisualInfo for this screen.");
- break;
- case FONT_ERROR:
- SimpleRequest(&errorMessage, "Can't open your selected fonts.");
- break;
- case GADGET_ERROR:
- SimpleRequest(&errorMessage, "Can't create list of gadgets.\n(Out of memory?)");
- break;
- case MAIN_ERROR:
- SimpleRequest(&errorMessage, "Can't open main window.\n(Out of memory?)");
- break;
- case TASK_ERROR:
- SimpleRequest(&errorMessage, "Can't create list of tasks.\n(Out of memory?)");
- break;
- case SETTINGS_ERROR:
- SimpleRequest(&errorMessage, "Can't open settings window.\n(Out of memory?)");
- break;
- case CX_ERROR:
- SimpleRequest(&errorMessage, "Can't set up Commodity interface.");
- break;
- case MENU_ERROR:
- SimpleRequest(&errorMessage, "Can't create menus.\n(Out of memory?)");
- break;
- }
- }
-
-
- /*
- * Since the user can pass settings information in two ways (ToolTypes and
- * Shell arguments), we put the code to parse them in a separate function.
- * It takes in an array of ToolTypes (from the .info file) or Shell
- * arguments (from argv[]) and examines their contents using ArgInt() or
- * ArgString(). If HELP is one of those keywords, it sets the help flag
- * passed in.
- */
- void HandleToolTypes(char **tools, BOOL *help)
- {
- /*
- * For the numeric settings, if a ToolType can't be found, we use the
- * existing value of the settings variable. (Which will either be the
- * internal default, or - if we're examining Shell arguments - the
- * ToolType value from the last time this function was called.)
- */
- winLeft = (WORD)ArgInt(tools, "LEFT", winLeft);
- winTop = (WORD)ArgInt(tools, "TOP", winTop);
- winWidth = (WORD)ArgInt(tools, "WIDTH", winWidth);
- winHeight = (WORD)ArgInt(tools, "HEIGHT", winHeight);
- propTA.ta_YSize = (UWORD)ArgInt(tools, "GADSIZE", propTA.ta_YSize);
- monoTA.ta_YSize = (UWORD)ArgInt(tools, "LISTSIZE", monoTA.ta_YSize);
- iconLeft = (WORD)ArgInt(tools, "ICONLEFT", iconLeft);
- iconTop = (WORD)ArgInt(tools, "ICONTOP", iconTop);
- priority = (BYTE)ArgInt(tools, "CX_PRIORITY", priority);
-
- /*
- * The string settings are handled similarly.
- */
- strcpy(propName, ArgString(tools, "GADFONT", propName));
- strcpy(monoName, ArgString(tools, "LISTFONT", monoName));
- strncpy(hotkey, ArgString(tools, "CX_POPKEY", hotkey), MaxHotkey);
-
- /*
- * The YES/NO settings follow. Since the defaults are all YES, we bias
- * towards this, and only allow a NO if the first letter is 'n'. (So
- * "xyz" would be YES.)
- */
- confirm = (ArgString(tools, "CONFIRM", confirm ? "Y" : "N")[0] & ~32) != 'N';
- commodity = (ArgString(tools, "COMMODITY", commodity ? "Y" : "N")[0] & ~32) != 'N';
- popup = (ArgString(tools, "CX_POPUP", popup ? "Y" : "N")[0] & ~32) != 'N';
- iconify = (ArgString(tools, "ICONIFY", iconify ? "Y" : "N")[0] & ~32) != 'N';
-
- /*
- * Our defaults for Window Type and Open On are SMART and DEFAULT,
- * respectively. As above, we are biased, and only change these if the
- * user enters exactly the text SIMPLE or FRONT.
- */
- refresh = !Compare(ArgString(tools, "REFRESH", refresh == SMARTWINDOW ? "SMART" : "SIMPLE"), "SIMPLE");
- open = !Compare(ArgString(tools, "SCREEN", open == DEFAULTSCREEN ? "DEFAULT" : "FRONT"), "FRONT");
-
- /*
- * Here we handle the TOOLPRI setting. (This may already have been
- * done if we were launched from Workbench.) We examine our own task
- * structure to see what priority we are at currently, and "change" to
- * that if no ToolType was given.
- */
- SetTaskPri(FindTask(NULL), (BYTE)ArgInt(tools, "TOOLPRI", FindTask(NULL) -> tc_Node.ln_Pri));
-
- /*
- * Finally, if the user wants help, we set a flag to ensure he gets it
- * later. We don't call Help() ourselves now since if there was an
- * error during startup, we'd run into real trouble trying to kill the
- * AmigaGuide process while it was doing its thing!
- *
- * If the help flag was already set, obviously we don't need to do the
- * test for the HELP keyword again.
- */
- *help = *help || FindToolType(tools, "HELP");
- }
-