AmendHub

Download

jcs

/

wallops

/

main.c

 

(View History)

jcs   main: Hide/Show should always be enabled Latest amendment: 90 on 2024-09-12

1 /*
2 * Copyright (c) 2021-2022 joshua stein <jcs@jcs.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <string.h>
18 #include <stdarg.h>
19 #include "chatter.h"
20 #include "irc.h"
21 #include "settings.h"
22 #include "tcp.h"
23 #include "util.h"
24
25 NMRec notification = { 0 };
26 struct settings settings;
27 MenuHandle apple_menu, file_menu, edit_menu, view_menu, ignore_menu;
28
29 #ifdef MALLOC_DEBUG
30 MenuHandle debug_menu;
31 #define DEBUG_MENU_DUMP_ID 999
32 static short xalloc_frefnum = 0;
33 size_t xalloc_printf(const char *format, ...);
34 #endif
35
36 void update_menu(void);
37 void handle_exit(void);
38 bool handle_menu(long menu_id);
39 void show_connect_dialog(void);
40 void wallops_about(void);
41
42 short
43 main(void)
44 {
45 Handle mbar;
46 EventRecord event;
47 WindowPtr event_win;
48 GrafPtr old_port;
49 AppFile finder_file;
50 struct focusable *found_focusable;
51 short event_in, n;
52 char key;
53 long wait_ticks;
54 short wait_type;
55
56 SetApplLimit(GetApplLimit() - (1024 * 8));
57
58 InitGraf(&thePort);
59 InitFonts();
60 FlushEvents(everyEvent, 0);
61 InitWindows();
62 InitMenus();
63 TEInit();
64 InitDialogs(0);
65 InitCursor();
66 MaxApplZone();
67
68 util_init();
69 _atexit(handle_exit);
70
71 if (!(mbar = GetNewMBar(MBAR_ID)))
72 panic("no mbar");
73 SetMenuBar(mbar);
74 if (!(apple_menu = GetMHandle(APPLE_MENU_ID)))
75 panic("no apple menu");
76 AddResMenu(apple_menu, 'DRVR');
77 if (!(file_menu = GetMHandle(FILE_MENU_ID)))
78 panic("no file menu");
79 if (!(edit_menu = GetMHandle(EDIT_MENU_ID)))
80 panic("no edit menu");
81 if (!(view_menu = GetMHandle(VIEW_MENU_ID)))
82 panic("no view menu");
83 if (!(ignore_menu = GetMenu(IGNORE_MENU_ID)))
84 panic("no ignore menu");
85 InsertMenu(ignore_menu, -1);
86 update_menu();
87 #ifdef MALLOC_DEBUG
88 debug_menu = NewMenu(DEBUG_MENU_DUMP_ID, "\pDebug");
89 AppendMenu(debug_menu, "\pDump Allocations");
90 InsertMenu(debug_menu, 0);
91 #endif
92 DrawMenuBar();
93
94 show_connect_dialog();
95
96 for (;;) {
97 wait_type = WAIT_TYPE_BACKGROUND;
98 for (n = 0; n < nfocusables; n++) {
99 if (focusables[n]->wait_type)
100 wait_type |= focusables[n]->wait_type(focusables[n]);
101 else if (focusables[n]->visible)
102 wait_type |= WAIT_TYPE_FOREGROUND;
103 }
104
105 if (wait_type & WAIT_TYPE_URGENT)
106 wait_ticks = 0;
107 else if (wait_type & WAIT_TYPE_FOREGROUND)
108 wait_ticks = 5L;
109 else
110 wait_ticks = 60L;
111
112 WaitNextEvent(everyEvent, &event, wait_ticks, 0L);
113
114 switch (event.what) {
115 case nullEvent:
116 for (n = 0; n < nfocusables; n++) {
117 if (focusables[n]->idle)
118 focusables[n]->idle(focusables[n], &event);
119 }
120 break;
121 case keyDown:
122 case autoKey:
123 key = event.message & charCodeMask;
124 if ((event.modifiers & cmdKey) != 0 &&
125 handle_menu(MenuKey(key)) == true)
126 break;
127 if (nfocusables && focusables[0]->visible &&
128 focusables[0]->key_down)
129 focusables[0]->key_down(focusables[0], &event);
130 break;
131 case mouseDown:
132 event_in = FindWindow(event.where, &event_win);
133 found_focusable = focusable_find(event_win);
134
135 switch (event_in) {
136 case inMenuBar:
137 handle_menu(MenuSelect(event.where));
138 break;
139 case inSysWindow:
140 SystemClick(&event, event_win);
141 break;
142 case inDrag:
143 DragWindow(event_win, event.where, &screenBits.bounds);
144 break;
145 case inGrow:
146 if (event_win != FrontWindow() && found_focusable) {
147 cancel_notification();
148 focusable_show(found_focusable);
149 }
150 if (found_focusable && found_focusable->resize)
151 found_focusable->resize(found_focusable, &event);
152 break;
153 case inGoAway:
154 if (TrackGoAway(event_win, event.where) && found_focusable)
155 focusable_close(found_focusable);
156 break;
157 case inContent:
158 if (event_win != FrontWindow()) {
159 if (found_focusable) {
160 cancel_notification();
161 focusable_show(found_focusable);
162 }
163 }
164 if (found_focusable && found_focusable->mouse_down)
165 found_focusable->mouse_down(found_focusable, &event);
166 break;
167 }
168
169 update_menu();
170 break;
171 case updateEvt:
172 case activateEvt:
173 event_win = (WindowPtr)event.message;
174 found_focusable = focusable_find(event_win);
175
176 GetPort(&old_port);
177 SetPort(event_win);
178
179 if (event.what == updateEvt)
180 BeginUpdate(event_win);
181
182 if (found_focusable && found_focusable->update)
183 found_focusable->update(found_focusable, &event);
184
185 if (event.what == updateEvt)
186 EndUpdate(event_win);
187
188 SetPort(old_port);
189 break;
190 case app4Evt:
191 if (HiWord(event.message) & (1 << 8)) {
192 /* multifinder suspend/resume */
193 switch (event.message & (1 << 0)) {
194 case 0:
195 /* suspend */
196 for (n = 0; n < nfocusables; n++) {
197 if (focusables[n]->suspend)
198 focusables[n]->suspend(focusables[n], &event);
199 }
200 break;
201 case 1:
202 /* resume */
203 for (n = 0; n < nfocusables; n++) {
204 if (focusables[n]->resume)
205 focusables[n]->resume(focusables[n], &event);
206 }
207 break;
208 }
209
210 update_menu();
211 }
212 break;
213 }
214 }
215
216 return 0;
217 }
218
219 void
220 show_connect_dialog(void)
221 {
222 bool valid;
223
224 valid = settings_edit(!settings_load());
225 update_menu();
226
227 if (!valid)
228 return;
229
230 chatter_init(settings.server, settings.port, settings.password,
231 settings.nick, settings.ident, settings.realname,
232 settings.hide_motd, settings.channel);
233 }
234
235 bool
236 handle_menu(long menu_id)
237 {
238 size_t n;
239 bool ret = false;
240 bool quit = true;
241
242 switch (HiWord(menu_id)) {
243 case APPLE_MENU_ID:
244 switch (LoWord(menu_id)) {
245 case APPLE_MENU_ABOUT_ID:
246 wallops_about();
247 ret = true;
248 break;
249 default: {
250 Str255 da;
251 GrafPtr save_port;
252
253 GetItem(apple_menu, LoWord(menu_id), &da);
254 GetPort(&save_port);
255 OpenDeskAcc(da);
256 SetPort(save_port);
257
258 ret = true;
259 break;
260 }
261 }
262 break;
263 case FILE_MENU_ID:
264 switch (LoWord(menu_id)) {
265 case FILE_MENU_CONNECT_ID:
266 show_connect_dialog();
267 ret = true;
268 break;
269 case FILE_MENU_QUIT_ID:
270 ret = true;
271 if (focusables_quit())
272 ExitToShell();
273 break;
274 }
275 break;
276 case VIEW_MENU_ID:
277 switch (LoWord(menu_id)) {
278 case VIEW_MENU_HIDE_ID: {
279 Str255 text;
280 struct focusable **tfocusables = NULL;
281
282 /* hiding and showing adjusts order, so duplicate order */
283 tfocusables = xmalloc(sizeof(struct focusable *) * nfocusables);
284 memcpy(tfocusables, focusables, sizeof(struct focusable *) *
285 nfocusables);
286
287 GetItem(view_menu, VIEW_MENU_HIDE_ID, &text);
288 if (memcmp((char *)text + 1, "Hide", 4) == 0) {
289 for (n = 0; n < nfocusables; n++) {
290 if (tfocusables[n]->visible)
291 focusable_hide(tfocusables[n]);
292 }
293 } else {
294 for (n = 0; n < nfocusables; n++) {
295 if (!tfocusables[n]->visible)
296 focusable_show(tfocusables[n]);
297 }
298 }
299
300 xfree(&tfocusables);
301 update_menu();
302 ret = true;
303 break;
304 }
305 }
306 break;
307 case IGNORE_MENU_ID: {
308 short current;
309
310 GetItemMark(ignore_menu, LoWord(menu_id), &current);
311
312 switch (LoWord(menu_id)) {
313 case IGNORE_MENU_JOINS_ID:
314 if (current)
315 settings.ignores &= ~(IGNORE_JOINS);
316 else
317 settings.ignores |= (IGNORE_JOINS);
318 break;
319 case IGNORE_MENU_QUITS_ID:
320 if (current)
321 settings.ignores &= ~(IGNORE_QUITS);
322 else
323 settings.ignores |= (IGNORE_QUITS);
324 break;
325 case IGNORE_MENU_NICKS_ID:
326 if (current)
327 settings.ignores &= ~(IGNORE_NICKS);
328 else
329 settings.ignores |= (IGNORE_NICKS);
330 break;
331 }
332 settings_save(&settings);
333 update_menu();
334 ret = true;
335 break;
336 }
337 #ifdef MALLOC_DEBUG
338 case DEBUG_MENU_DUMP_ID:
339 switch (LoWord(menu_id)) {
340 case 1: {
341 Str255 vname;
342 short error, vrefnum;
343
344 error = GetVol(&vname, &vrefnum);
345 if (error)
346 panic("Failed to get volume: %d", error);
347 PtoCstr(vname);
348 strlcat((char *)vname, ":", sizeof(vname));
349 strlcat((char *)vname, PROGRAM_NAME, sizeof(vname));
350 strlcat((char *)vname, "_xalloc.txt", sizeof(vname));
351 CtoPstr(vname);
352
353 error = Create(vname, vrefnum, 'TEXT', 'TEXT');
354 if (error && error != dupFNErr)
355 panic("Failed to create file: %d", error);
356 error = FSOpen(vname, vrefnum, &xalloc_frefnum);
357 if (error)
358 panic("Failed to open file: %d", error);
359 error = SetEOF(xalloc_frefnum, 0);
360 if (error)
361 panic("Failed to truncate file: %d", error);
362
363 xalloc_print(xalloc_printf);
364 FSClose(xalloc_frefnum);
365 break;
366 }
367 }
368 ret = true;
369 break;
370 #endif
371 }
372
373 if (!ret && nfocusables && focusables[0]->visible &&
374 focusables[0]->menu)
375 ret = focusables[0]->menu(focusables[0], HiWord(menu_id),
376 LoWord(menu_id));
377
378 HiliteMenu(0);
379 return ret;
380 }
381
382 #ifdef MALLOC_DEBUG
383 size_t
384 xalloc_printf(const char *format, ...)
385 {
386 static char buf[256];
387 short error;
388 va_list va;
389 size_t len;
390
391 va_start(va, format);
392 len = vsnprintf(buf, sizeof(buf), format, va);
393 va_end(va);
394
395 error = FSWrite(xalloc_frefnum, &len, buf);
396 if (error)
397 panic("Failed to write: %d", error);
398
399 len = 1;
400 error = FSWrite(xalloc_frefnum, &len, "\r");
401 if (error)
402 panic("Failed to write: %d", error);
403
404 return len;
405 }
406 #endif
407
408 void
409 update_menu(void)
410 {
411 short n;
412 bool hidden = false;
413
414 for (n = 0; n < nfocusables; n++) {
415 if (!focusables[n]->visible) {
416 hidden = true;
417 break;
418 }
419 }
420
421 EnableItem(view_menu, VIEW_MENU_HIDE_ID);
422
423 if (hidden) {
424 SetItem(view_menu, VIEW_MENU_HIDE_ID, "\pShow Windows");
425 DisableItem(view_menu, VIEW_MENU_CLOSE_ID);
426 } else
427 SetItem(view_menu, VIEW_MENU_HIDE_ID, "\pHide Windows");
428
429 CheckItem(ignore_menu, IGNORE_MENU_JOINS_ID,
430 !!(settings.ignores & IGNORE_JOINS));
431 CheckItem(ignore_menu, IGNORE_MENU_QUITS_ID,
432 !!(settings.ignores & IGNORE_QUITS));
433 CheckItem(ignore_menu, IGNORE_MENU_NICKS_ID,
434 !!(settings.ignores & IGNORE_NICKS));
435
436 if (nfocusables && focusables[0]->visible &&
437 focusables[0]->update_menu) {
438 focusables[0]->update_menu(focusables[0]);
439 return;
440 }
441
442 DisableItem(edit_menu, EDIT_MENU_CUT_ID);
443 DisableItem(edit_menu, EDIT_MENU_COPY_ID);
444 DisableItem(edit_menu, EDIT_MENU_PASTE_ID);
445
446 DisableItem(view_menu, VIEW_MENU_HIDE_ID);
447 }
448
449 void
450 handle_exit(void)
451 {
452 focusables_quit();
453
454 while (!SLIST_EMPTY(&irc_connections_list))
455 irc_dealloc_connection(SLIST_FIRST(&irc_connections_list));
456 }
457
458 void
459 notify(void)
460 {
461 memset(&notification, 0, sizeof(notification));
462 notification.qType = nmType;
463 notification.nmMark = 1;
464 notification.nmSound = (Handle)-1;
465 notification.nmIcon = GetResource('SICN', NOTIFICATION_ICON_ID);
466 NMInstall(&notification);
467 }
468
469 void
470 cancel_notification(void)
471 {
472 if (notification.qType)
473 NMRemove(&notification);
474 memset(&notification, 0, sizeof(notification));
475 }
476
477 void
478 wallops_about(void)
479 {
480 char vers_s[255];
481 char short_vers[255] = { 0 };
482 DialogPtr dp;
483 StringHandle dast;
484 VersRecHndl vers;
485 Rect bounds, irect;
486 short hit, vlen, n, itype;
487 Handle h, ihandle, okhandle;
488
489 vers = (VersRecHndl)GetResource('vers', 1);
490 if (!vers)
491 panic("no vers");
492
493 /*
494 * vers "long version string" is a pascal string after the
495 * short version pascal string
496 */
497 HLock(vers);
498 vlen = (*vers)->shortVersion[0];
499 memcpy(short_vers, (*vers)->shortVersion + vlen + 1,
500 sizeof((*vers)->shortVersion) - vlen - 1);
501 PtoCstr(short_vers);
502 snprintf(vers_s, sizeof(vers_s), "%s %s", PROGRAM_NAME, short_vers);
503 for (n = 0; n < sizeof(vers_s); n++) {
504 if (vers_s[n] == '©') {
505 vers_s[n - 1] = '\r';
506 break;
507 }
508 }
509 ReleaseResource(vers);
510
511 CtoPstr(vers_s);
512
513 dp = GetNewDialog(ABOUT_DLOG_ID, 0L, (WindowPtr)-1L);
514
515 ParamText(vers_s, "\p" HOMEPAGE, "\pI miss you, Carl", "\p");
516
517 center_in_screen(((DialogPeek)dp)->window.port.portRect.right,
518 ((DialogPeek)dp)->window.port.portRect.bottom, false, &bounds);
519 MoveWindow(dp, bounds.left, bounds.top, false);
520 SetPort(dp);
521 ShowWindow(dp);
522
523 GetDItem(dp, ok, &itype, &okhandle, &irect);
524 for (;;) {
525 outline_button((ControlHandle)okhandle);
526 ModalDialog(0, &hit);
527 if (hit == ok)
528 break;
529 }
530
531 DisposDialog(dp);
532 }