diff --git a/main b/main index b1040bd..a11839a 100755 Binary files a/main and b/main differ diff --git a/main.c b/main.c index 901fa91..3013afc 100644 --- a/main.c +++ b/main.c @@ -63,9 +63,24 @@ struct Screen *screen; struct Window *window; struct Gadget *windowGadgets; -//struct NewWindow aboutWindowLayout = { }; -struct Window *aboutWindow; -struct Gadget *aboutWindowGadgets; +#define ABOUT_WINDOW_WIDTH (240) +#define ABOUT_WINDOW_HEIGHT (50) + +struct NewWindow aboutWindowLayout = { + 40, 40, + ABOUT_WINDOW_WIDTH, ABOUT_WINDOW_HEIGHT, + 0, 1, + IDCMP_REFRESHWINDOW | IDCMP_CLOSEWINDOW | BUTTONIDCMP, + WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_CLOSEGADGET | WFLG_ACTIVATE, + NULL, + NULL, + "About Pizza Timer", + NULL, + NULL, + ABOUT_WINDOW_WIDTH, ABOUT_WINDOW_HEIGHT, + ABOUT_WINDOW_WIDTH, ABOUT_WINDOW_HEIGHT, + WBENCHSCREEN +}; // Fonts! struct TextAttr Topaz80 = { "topaz.font", 8, 0, 0 }; @@ -88,6 +103,11 @@ struct Gadget *timerGadget, *hourSlider, *minuteSlider, *secondSlider; // gadtools menus // https://wiki.amigaos.net/wiki/GadTools_Menus + +// Why the special struct, constants and struct instantiation? +// Because this way, when we get a menu message from Intuition, +// we can look up the menu item selected by ID, rather than +// looking it up by position in the menu tree structure. struct MenuData { int id; }; @@ -118,6 +138,8 @@ struct timeval currentSystemTime; // get that bell #define BELL_FILENAME "bell.8svx" +APTR bellSound = NULL; + // our business logic // for how long should I cook this pizza? unsigned int uiHours = 0; @@ -229,10 +251,8 @@ struct Gadget *buildUI(void) { currentGadget = CreateContext(&glist); ng.ng_LeftEdge = WINDOW_CHROME_WIDTH; - // TODO: constantize these ng.ng_TopEdge = 11; ng.ng_Height = 12; - ng.ng_Width = 0; ng.ng_GadgetText = NULL; ng.ng_TextAttr = &Topaz80; @@ -258,6 +278,7 @@ struct Gadget *buildUI(void) { ng.ng_TextAttr = &Topaz80; ng.ng_Height = 12; + // start/stop button ng.ng_Width = (WINDOW_WIDTH - WINDOW_CHROME_WIDTH * 2) / 2; ng.ng_TopEdge += 18; if (timerIsRunning) { @@ -276,6 +297,7 @@ struct Gadget *buildUI(void) { TAG_END ); + // reset button ng.ng_LeftEdge += (WINDOW_WIDTH - WINDOW_CHROME_WIDTH * 2) / 2; ng.ng_GadgetText = "_Reset"; ng.ng_GadgetID = RESET_BUTTON_ID; @@ -289,6 +311,7 @@ struct Gadget *buildUI(void) { TAG_END ); + // hours slider ng.ng_LeftEdge = 85; ng.ng_Width = (WINDOW_WIDTH - WINDOW_CHROME_WIDTH * 2) - 85 + 4; ng.ng_TopEdge += 12; @@ -314,6 +337,7 @@ struct Gadget *buildUI(void) { TAG_END ); + // minutes slider ng.ng_TopEdge += 12; ng.ng_GadgetText = "Mins: "; ng.ng_GadgetID = MINUTES_SLIDER_ID; @@ -331,6 +355,7 @@ struct Gadget *buildUI(void) { TAG_END ); + // seconds slider ng.ng_TopEdge += 12; ng.ng_GadgetText = "Secs: "; ng.ng_GadgetID = SECONDS_SLIDER_ID; @@ -351,6 +376,9 @@ struct Gadget *buildUI(void) { return glist; } +/** + * Set the text of the timer and redraw the necessary gadgets. + */ void setTimerText(void) { // Only change the timer widget text if the time is different // from the last render. This prevents unnecessary renders and potential @@ -422,8 +450,6 @@ void clearUI(void) { void handleToggleTimer(void) { timerIsRunning = !timerIsRunning; - // TODO: don't reset the timer when it's stopped/started - if (timerIsRunning) { // http://amigadev.elowar.com/read/ADCD_2.1/Includes_and_Autodocs_2._guide/node04FA.html GetSysTime(¤tSystemTime); @@ -441,9 +467,12 @@ void handleToggleTimer(void) { timerStarted = TRUE; - // start the async timer + // start the timer startTimer(); } else { + // put the sliders back. + // it causes a lot of UI flashing to update these as the timer runs, + // so only do it once we stop the timer. GT_SetGadgetAttrs( hourSlider, window, @@ -487,6 +516,93 @@ void handleResetTimer(void) { clearUI(); renderUI(); } +// +// To keep this simple, the about window will block +int openAboutWindow(void) { + struct Window *aboutWindow; + struct NewGadget ng; + struct Gadget *currentGadget; + struct Gadget *glist; + struct IntuiMessage *iMessage; + + ULONG windowSignal; + BOOL closeAbout = FALSE; + aboutWindow = OpenWindow(&aboutWindowLayout); + + if (!aboutWindow) { + return 1; + } + + currentGadget = CreateContext(&glist); + + ng.ng_LeftEdge = WINDOW_CHROME_WIDTH + 4; + ng.ng_TopEdge = 15; + ng.ng_Height = 10; + ng.ng_Width = 0; + ng.ng_GadgetText = NULL; + ng.ng_TextAttr = &Topaz80; + ng.ng_VisualInfo = visualInfo; + + ng.ng_GadgetID = NULL; + ng.ng_Flags = PLACETEXT_IN; + + currentGadget = CreateGadget( + TEXT_KIND, + currentGadget, + &ng, + GTTX_Text, "Topaz's Pizza Timer", + GTTX_CopyText, TRUE, + TAG_DONE + ); + + ng.ng_TopEdge += 10; + currentGadget = CreateGadget( + TEXT_KIND, + currentGadget, + &ng, + GTTX_Text, "By John Bintz", + GTTX_CopyText, TRUE, + TAG_DONE + ); + + ng.ng_TopEdge += 10; + currentGadget = CreateGadget( + TEXT_KIND, + currentGadget, + &ng, + GTTX_Text, "theindustriousrabbit.com", + GTTX_CopyText, TRUE, + TAG_DONE + ); + + AddGList(aboutWindow, glist, -1, -1, NULL); + RefreshGList(glist, aboutWindow, NULL, -1); + GT_RefreshWindow(aboutWindow, NULL); + + windowSignal = 1L << aboutWindow->UserPort->mp_SigBit; + + while (!closeAbout) { + Wait(windowSignal); + + while (!closeAbout && (iMessage = GT_GetIMsg(aboutWindow->UserPort))) { + switch (iMessage->Class) { + case IDCMP_CLOSEWINDOW: + closeAbout = TRUE; + break; + case IDCMP_REFRESHWINDOW: + GT_BeginRefresh(window); + GT_EndRefresh(window, TRUE); + break; + } + } + } + + RemoveGList(aboutWindow, glist, -1); + FreeGadgets(glist); + CloseWindow(aboutWindow); + + return 0; +} /** * Process a single Intuition message. @@ -514,8 +630,8 @@ void handleIntuitionMessage(struct IntuiMessage *iMessage) { break; } - break; + // We picked a menu item. case IDCMP_MENUPICK: // https://en.wikibooks.org/wiki/Aros/Developer/Docs/Libraries/GadTools @@ -526,11 +642,20 @@ void handleIntuitionMessage(struct IntuiMessage *iMessage) { switch (menuData->id) { // I originally tried to use MENU_QUIT.id here // https://stackoverflow.com/questions/14069737/switch-case-error-case-label-does-not-reduce-to-an-integer-constant + // This is why there's a bunch more overhead for building + // the menu userdata. case MENU_QUIT_ID: terminated = TRUE; break; + case MENU_ABOUT_ID: + // yes, this blocks, deal with it + // it also returns non-zero if something goes wrong. + // in that case, kill the program + if (openAboutWindow()) terminated = TRUE; + break; } break; + // We've moved the mouse. In slider gadget talk, we've changed // the value of the slider. case IDCMP_MOUSEMOVE: @@ -554,8 +679,8 @@ void handleIntuitionMessage(struct IntuiMessage *iMessage) { if (rerenderTimer) setTimerText(); break; - // bye bye case IDCMP_CLOSEWINDOW: + // bye bye terminated = TRUE; break; @@ -568,12 +693,15 @@ void handleIntuitionMessage(struct IntuiMessage *iMessage) { } } -void endTimer(void) { - // TODO: play an included IFF 8SVX sound - APTR bellSound = NULL; + +/** + * Start playing a bell sound, and start a timer to tear down + * the bell data once it's done. + */ +void startBellSound(void) { struct dtTrigger myTrigger; struct MsgPort *TimerPort; - ULONG soundPlayResult, timerSignal; + ULONG soundPlayResult; if (bellSound = NewDTObject(BELL_FILENAME, DTA_GroupID, GID_SOUND, TAG_END)) { myTrigger.MethodID = DTM_TRIGGER; @@ -592,7 +720,30 @@ void endTimer(void) { TimerIO->tr_time.tv_micro = 0; SendIO((struct IORequest *)TimerIO); } +} +/** + * Tear down the bell once the timer runs out. + */ +void waitForBellToFinish(void) { + struct MsgPort *TimerPort; + ULONG timerSignal; + + if (bellSound) { + TimerPort = TimerIO->tr_node.io_Message.mn_ReplyPort; + + timerSignal = 1L << TimerPort->mp_SigBit; + Wait(timerSignal); + + DisposeDTObject(bellSound); + } +} + +/** + * Handle the timer running out of time. + */ +void endTimer(void) { + startBellSound(); DisplayBeep(screen); uiHours = uiMinutes = uiSeconds = 0; @@ -604,12 +755,7 @@ void endTimer(void) { clearUI(); renderUI(); - if (bellSound) { - timerSignal = 1L << TimerPort->mp_SigBit; - Wait(timerSignal); - - DisposeDTObject(bellSound); - } + waitForBellToFinish(); } void handleTimerMessage(void) { @@ -648,7 +794,6 @@ void clearMenu(void) { } int main() { - // TODO: Menu bar with about menu and change the title to match the time struct IntuiMessage *iMessage; struct MsgPort *TimerPort; ULONG windowSignal, timerSignal, foundSignals; @@ -671,14 +816,16 @@ int main() { return 1; } + // http://amigadev.elowar.com/read/ADCD_2.1/Includes_and_Autodocs_3._guide/node03F8.html + SetWindowTitles(window, (UBYTE *) ~0, "Topaz's Pizza Timer - theindustriousrabbit.com"); + buildMenu(); + // these create the bit mask for Wait() to listen to events on windowSignal = 1L << window->UserPort->mp_SigBit; timerSignal = 1L << TimerPort->mp_SigBit; renderUI(); - buildMenu(); - while (!terminated) { // http://amigadev.elowar.com/read/ADCD_2.1/Libraries_Manual_guide/node02EB.html // http://www.amigadev.elowar.com/read/ADCD_2.1/Includes_and_Autodocs_2._guide/node038A.html