AmendHub

Download

jcs

/

wallops

/

main.c

 

(View History)

jcs   main: If we have existing settings, auto-connect unless Cmd is held Latest amendment: 144 on 2025-12-17

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