cyberslak
/lightsout
/amendments
/12
main: smorgasbord of changes
- Add preferences support
- Add support for adding entities (Cmd-A)
- Load saved entities
- Handle update/activate/suspend/resume events better
- Port handling tweaks
cyberslak made amendment 12 25 days ago
--- main.c Fri Mar 7 14:35:13 2025
+++ main.c Sun Mar 9 23:43:56 2025
@@ -1,16 +1,21 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
+#include <unistd.h>
#include "util.h"
#include "net.h"
#include "ha.h"
#include "list.h"
#include "slider.h"
+#include "preferences.h"
+#include "entity_ldef.h"
#define kScrollBarWidth 15
#define kSliderCDEFId 128
+#define kEntityLDEFId 128
#define kCustomSliderProc (16 * kSliderCDEFId)
-#define kBarUpdateRate (1 * 60) // 1 second
+#define kCustomListProc (kEntityLDEFId)
+#define kBarUpdateRate (1 * 60) // 1 second(s)
#define kBarWidth 100
#define kCtrlWidth 40
#define kCtrlHeight 80
@@ -27,52 +32,110 @@
/* GLOBALS */
Boolean gRunning = true;
-Handle gSliderCDEF;
-WindowPtr gMainWindow;
-list_t gEntities = LIST_INIT(gEntities);
+short gNumBars = 0;
-Pattern gSliderPattern;
+Handle gSliderCDEF, gEntityLDEF;
+PrefHandle gPreferences;
+Pattern gSliderPattern, gBackPattern;
+
+WindowPtr gMainWindow = NULL;
+list_t gEntities = LIST_INIT(gEntities);
+
/* PROTOTYPES */
+void ensure_valid_prefs(void);
+void dialog_info_free(struct dialog_info *dinfo);
+void entity_list_add(struct dialog_info *dinfo);
+void entity_list_mousedown(EventRecord* evt, WindowPtr win);
+void entity_list_activate(bool activate, WindowPtr win);
+void entity_list_show(void);
void event_loop(void);
+void event_update(WindowPtr win);
+void event_activate(bool activate, EventRecord* evt, WindowPtr win);
void event_mousedown(EventRecord*);
void menu_init(void);
void menu_click(long);
-void lightsout_init();
+void lightsout_init(Handle previousEnts);
void create_bar(WindowPtr win, const char* id);
void resolve_slider_cdef();
+void resolve_entity_ldef();
pascal void dialog_list_update(WindowPtr theWin, short itemNo);
+void memory_init(void);
+
struct dialog_info
{
ListHandle lHnd;
+ struct list ents;
void (*handle_mousedown)(EventRecord*, WindowPtr);
+ void (*handle_activate)(bool activate, WindowPtr);
};
-int main()
+void memory_init(void)
{
short i;
+ Ptr applLimit;
+
+ applLimit = GetApplLimit();
+ SetApplLimit(applLimit - 0x8000);
- toolbox_init();
MaxApplZone();
for (i = 0; i < 5; ++i)
MoreMasters();
-
+}
+
+void ensure_valid_prefs(void)
+{
+ bool updatedPrefs = false;
+
+ gPreferences = preferences_load();
+
+ while (ha_test_prefs() != 0)
+ {
+ info("Server configuration is invalid; "
+ "please review your settings.");
+
+ // preferences invalid; show preferences dialog
+ if (preferences_dialog() != 0)
+ {
+ ExitToShell();
+ } else {
+ updatedPrefs = true;
+ }
+ }
+
+ if (updatedPrefs)
+ preferences_save(gPreferences);
+}
+
+int main()
+{
+ Handle entHnd;
+
+ toolbox_init();
+ memory_init();
+ net_init();
+
resolve_slider_cdef();
+ resolve_entity_ldef();
GetIndPattern(&gSliderPattern, 0, 22);
+ GetIndPattern(&gBackPattern, 0, 3);
- net_init();
- lightsout_init();
-
menu_init();
+
+ ensure_valid_prefs();
+
+ lightsout_init(preferences_get_entities());
event_loop();
net_fini();
+ preferences_save_entities(&gEntities);
+
return 0;
}
@@ -86,6 +149,16 @@ void resolve_slider_cdef()
slider_cdef->addr = slider_proc;
}
+void resolve_entity_ldef()
+{
+ def_jmp_t* entity_ldef;
+ gEntityLDEF = GetResource('LDEF', 128);
+ HLock(gEntityLDEF);
+
+ entity_ldef = (def_jmp_t*)*gEntityLDEF;
+ entity_ldef->addr = entity_ldef_proc;
+}
+
void create_bar(WindowPtr win, const char* id)
{
struct entity* ent = xmalloc(sizeof(struct entity));
@@ -115,6 +188,12 @@ void create_bar(WindowPtr win, const char* id)
(long)ent
);
+ ent->ctrl = ctrl;
+
+ list_add_tail(&gEntities, &ent->node);
+
+ gNumBars++;
+
barOffset += kBarWidth;
}
@@ -137,12 +216,41 @@ pascal void dialog_list_update(WindowPtr theWin, short
FrameRect(&frameRect);
}
+void dialog_info_free(struct dialog_info *dinfo)
+{
+ list_t *node;
+ LDispose(dinfo->lHnd);
+
+ while ((node = list_pop(&dinfo->ents)) != NULL)
+ {
+ free(container_of(node, struct entity, node));
+ }
+
+ free(dinfo);
+}
+
+void entity_list_add(struct dialog_info* dinfo)
+{
+ Point pt = {0, 0};
+ struct entity* ent;
+ if (LGetSelect(true, &pt, dinfo->lHnd))
+ {
+ short len = 4;
+ LGetCell(&ent, &len, pt, dinfo->lHnd);
+ create_bar(gMainWindow, ent->id);
+ }
+}
+
void entity_list_mousedown(EventRecord* evt, WindowPtr win)
{
DialogPtr theDialog;
short itemHit;
struct dialog_info* dinfo = (void*)GetWRefCon(win);
+ GrafPtr oldPort;
+ GetPort(&oldPort);
+ SetPort(win);
+
DialogSelect(evt, &theDialog, &itemHit);
GlobalToLocal(&evt->where);
@@ -150,61 +258,80 @@ void entity_list_mousedown(EventRecord* evt, WindowPtr
switch (itemHit)
{
case 1:
- LDispose(dinfo->lHnd);
- free(dinfo);
+ entity_list_add(dinfo);
+
+ // must dispose of list before dialog
+ dialog_info_free(dinfo);
DisposeDialog(theDialog);
break;
case 2:
LClick(evt->where, evt->modifiers, dinfo->lHnd);
break;
}
+
+ SetPort(oldPort);
}
-void show_entity_list()
+void entity_list_activate(bool activate, WindowPtr win)
{
- short itemType, itemHit;
+ struct dialog_info *dinfo = (void*)GetWRefCon(win);
+ LActivate(activate, dinfo->lHnd);
+}
+
+/* Open the entity selection dialog. */
+void entity_list_show()
+{
+ short type, itemHit;
Handle itemHnd;
Rect itemRect, contentRect;
Rect dataBounds;
Point cSize = {0, 0};
Point pt = {0, 0};
- bool dialogDone = false;
- GrafPtr oldPort;
ListHandle lHnd;
struct dialog_info *dinfo = xmalloc(sizeof(struct dialog_info));
-
- DialogPtr theDialog = GetNewDialog(
+ struct entity *ent;
+ list_t *node;
+
+ short numEnts = ha_get_entities(&dinfo->ents);
+
+ DialogPtr dlog = GetNewDialog(
kEntityListDialog, nil, (WindowPtr)-1);
- GetDialogItem(theDialog, 2, &itemType, &itemHnd, &itemRect);
- SetDialogItem(theDialog, 2, itemType, (Handle)dialog_list_update, &itemRect);
+ GetDialogItem(dlog, 2, &type, &itemHnd, &itemRect);
+ SetDialogItem(dlog, 2, type, (Handle)dialog_list_update, &itemRect);
- GetPort(&oldPort);
- SetPort(theDialog);
-
contentRect = itemRect;
contentRect.right -= kScrollBarWidth;
rect_expand(&itemRect, 1);
SetRect(&dataBounds, 0, 0, 1, 0);
- lHnd = LNew(&contentRect, &dataBounds, cSize, 0,
- theDialog, true, false, false, true);
+ lHnd = LNew(&contentRect, &dataBounds, cSize, kCustomListProc,
+ dlog, true, false, false, true);
+ // todo allow multiple selections
+ (**lHnd).selFlags |= lOnlyOne;
+
dinfo->lHnd = lHnd;
dinfo->handle_mousedown = entity_list_mousedown;
+ dinfo->handle_activate = entity_list_activate;
- SetWRefCon(theDialog, (long)dinfo);
+ SetWRefCon(dlog, (long)dinfo);
- LAddRow(1, 0, lHnd);
- LSetCell("Hello", 5, pt, lHnd);
+ LAddRow(numEnts, 0, lHnd);
- ShowWindow(theDialog);
-
- SetPort(oldPort);
+ list_foreach(&dinfo->ents, node)
+ {
+ size_t len;
+ ent = container_of(node, struct entity, node);
+ LSetCell(&ent, sizeof(Ptr), pt, lHnd);
+ pt.v++;
+ }
+
+ ShowWindow(dlog);
}
-void lightsout_init()
+void lightsout_init(Handle previousEnts)
{
WindowPtr win;
Rect controlPos;
@@ -217,14 +344,28 @@ void lightsout_init()
SetPort(win);
+ win_center(win);
+
gMainWindow = win;
- create_bar(win, "light.eettafel_spaghetti_light");
- create_bar(win, "light.woonkamer");
- create_bar(win, "light.nachtkastlamp");
-
-
- DrawControls(win);
+ if (previousEnts)
+ {
+ // previous entities are packed into the handle
+ // as consecutive NUL-terminated strings.
+ size_t sz = GetHandleSize(previousEnts);
+ size_t idx = 0;
+ size_t len = 0;
+ HLock(previousEnts);
+
+ while (idx < sz)
+ {
+ len = strlen(*previousEnts + idx);
+ create_bar(win, *previousEnts + idx);
+ idx += len + 1;
+ }
+
+ HUnlock(previousEnts);
+ }
}
void menu_init(void)
@@ -237,7 +378,6 @@ void menu_init(void)
appleMenu = GetMenuHandle(mApple);
AppendResMenu(appleMenu, 'DRVR');
- SetMenuBar(menuBar);
DrawMenuBar();
}
@@ -268,7 +408,7 @@ void menu_click(long menuChoice)
case mFile:
if (itemId == miSend)
{
- show_entity_list();
+ entity_list_show();
}
else if (itemId == miQuit)
{
@@ -324,6 +464,7 @@ static void draw_bars(WindowPtr w)
{
WindowPeek win = (WindowPeek)w;
ControlHandle ctrl;
+
for (ctrl = win->controlList; ctrl; ctrl = (**ctrl).nextControl)
{
@@ -338,13 +479,17 @@ static void update_bars()
struct entity_state *st;
static uint32_t lastTickCount = 0;
WindowPeek win = (WindowPeek)gMainWindow;
- short val;
+ short val;
+ GrafPtr oldPort;
if (lastTickCount + kBarUpdateRate > TickCount())
return;
if (FrontWindow() != gMainWindow)
return;
+
+ if (!FrontWindow())
+ return;
//SysBeep(20);
@@ -364,73 +509,47 @@ static void update_bars()
if (st->brightness != val)
{
SetControlValue(ctrl, st->brightness);
+ GetPort(&oldPort);
+ SetPort(gMainWindow);
InvalRect(&gMainWindow->portRect);
+ SetPort(oldPort);
}
}
lastTickCount = TickCount();
}
-bool win_is_dialog(WindowPtr win)
+static void draw_background(WindowPtr win)
{
- WindowPeek wPeek = (WindowPeek)win;
- return (wPeek->windowKind == dialogKind);
+ Rect drawRect;
+ short height = rect_height(&win->portRect);
+ short width = rect_width(&win->portRect);
+ short offset = kBarWidth * gNumBars;
+ if (offset > width)
+ return;
+
+ SetRect(&drawRect, offset, 0, width, height);
+
+ FillRect(&drawRect, &gBackPattern);
+ FrameRect(&drawRect);
}
-void do_update(WindowPtr win)
+void event_update(WindowPtr win)
{
WindowPeek wPeek = (WindowPeek)win;
+ GrafPtr oldPort;
+
+ GetPort(&oldPort);
+ SetPort(win);
+
if (win == gMainWindow)
{
draw_bars(win);
+ draw_background(win);
UpdateControls(win, win->visRgn);
}
-}
-
-void event_loop()
-{
- EventRecord event;
- WindowPtr win;
- Boolean gotEvent;
- short theChar;
- short junk;
-
- while (gRunning)
- {
- update_bars();
-
- if (WaitNextEvent(everyEvent, &event, 10L, nil))
- {
- switch(event.what)
- {
- case mouseDown:
- event_mousedown(&event);
- break;
- case updateEvt:
- win = (WindowPtr)event.message;
-
- if (win_is_dialog(win))
- {
- DialogSelect(&event, &(DialogPtr)win, &junk);
- }
- BeginUpdate(win);
- do_update(win);
- EndUpdate(win);
- break;
- case keyDown:
- case autoKey:
- theChar = event.message & charCodeMask;
- if (event.modifiers & cmdKey)
- menu_click(MenuKey(theChar));
- break;
- case activateEvt:
- break;
- default:
- break;
- }
- }
- }
+ SetPort(oldPort);
}
void event_mousedown(EventRecord* evt)
@@ -441,18 +560,13 @@ void event_mousedown(EventRecord* evt)
short inPart = FindWindow(evt->where, &win);
short inCtrlPart;
long menuChoice;
- GrafPtr oldPort;
- GetPort(&oldPort);
-
if (win_is_dialog(frontWin) && win != frontWin)
{
SysBeep(20);
return;
}
- SetPort(win);
-
switch (inPart)
{
case inGoAway:
@@ -490,6 +604,75 @@ void event_mousedown(EventRecord* evt)
default:
break;
}
+}
+
+void event_activate(bool activate, EventRecord* evt, WindowPtr win)
+{
+ short junk;
+ if (win_is_dialog(win))
+ {
+ struct dialog_info* dinfo = (void*)GetWRefCon(win);
+ DialogSelect(evt, &(DialogPtr)win, &junk);
+ dinfo->handle_activate(activate, win);
+ }
+}
+
+void event_loop()
+{
+ EventRecord event;
+ WindowPtr win;
+ Boolean gotEvent;
+ GrafPtr oldPort;
- SetPort(oldPort);
-}
+ short theChar;
+ short junk;
+
+ while (gRunning)
+ {
+ update_bars();
+
+ if (WaitNextEvent(everyEvent, &event, 10L, nil))
+ {
+ switch(event.what)
+ {
+ case mouseDown:
+ event_mousedown(&event);
+ break;
+ case updateEvt:
+ win = (WindowPtr)event.message;
+ if (win_is_dialog(win))
+ {
+ DialogSelect(&event, &(DialogPtr)win, &junk);
+ } else {
+ BeginUpdate(win);
+ event_update(win);
+ EndUpdate(win);
+ }
+ break;
+ case keyDown:
+ case autoKey:
+ theChar = event.message & charCodeMask;
+ if (event.modifiers & cmdKey)
+ menu_click(MenuKey(theChar));
+ break;
+ case activateEvt:
+ {
+ bool activate = event.modifiers & 1;
+ win = (WindowPtr)event.message;
+ event_activate(activate, &event, win);
+ break;
+ }
+ case osEvt:
+ if ((event.message >> 24) == suspendResumeMessage)
+ {
+ bool activate = event.message & 1;
+ win = FrontWindow();
+ event_activate(activate, &event, win);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}