AmendHub

Download

jcs

/

wallops

/

main.c

 

(View History)

jcs   focusable: Let each dictate the minimum sleep time for WaitNextEvent Latest amendment: 28 on 2022-02-10

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 "chatter.h"
19 #include "irc.h"
20 #include "tcp.h"
21 #include "util.h"
22
23 MenuHandle apple_menu, file_menu;
24 struct focusable **focusables = NULL;
25 short nfocusables = 0;
26 NMRec notification = { 0 };
27
28 enum {
29 CONFIG_TYPE_STRING,
30 CONFIG_TYPE_SHORT,
31 CONFIG_TYPE_PASSWORD
32 };
33
34 struct config_field {
35 char name[32];
36 short type;
37 unsigned short min;
38 unsigned short max;
39 short ditl_id;
40 short res_id;
41 char sdefault[32];
42 char password_storage[256];
43 } config_fields[] = {
44 { "Server Name", CONFIG_TYPE_STRING, 1, 0,
45 CONNECT_SERVER_ID, STR_SERVER_ID, DEFAULT_SERVER_NAME },
46 { "Server Port", CONFIG_TYPE_SHORT, 1, 65535,
47 CONNECT_PORT_ID, STR_PORT_ID, DEFAULT_PORT },
48 { "Server Password", CONFIG_TYPE_PASSWORD, 0, 0,
49 CONNECT_PASSWORD_ID, STR_PASSWORD_ID, "" },
50 { "Nickname", CONFIG_TYPE_STRING, 1, 0,
51 CONNECT_NICK_ID, STR_NICK_ID, "" },
52 { "Ident", CONFIG_TYPE_STRING, 1, 0,
53 CONNECT_IDENT_ID, STR_IDENT_ID, DEFAULT_IDENT },
54 { "Realname", CONFIG_TYPE_STRING, 1, 0,
55 CONNECT_REALNAME_ID, STR_REALNAME_ID, DEFAULT_REALNAME },
56 { "Channel", CONFIG_TYPE_STRING, 0, 0,
57 CONNECT_CHANNEL_ID, STR_CHANNEL_ID, DEFAULT_CHANNEL },
58 };
59
60 void update_menu(void);
61 void handle_exit(void);
62 bool handle_menu(long menu_id);
63 short show_connect_dialog(void);
64
65 int
66 main(void)
67 {
68 Handle mbar;
69 EventRecord event;
70 WindowPtr event_win;
71 GrafPtr old_port;
72 AppFile finder_file;
73 struct focusable *found_focusable;
74 short event_in, n;
75 char key;
76 long wait_ticks;
77 short wait_type;
78
79 SetApplLimit(GetApplLimit() - (1024 * 8));
80
81 InitGraf(&thePort);
82 InitFonts();
83 FlushEvents(everyEvent, 0);
84 InitWindows();
85 InitMenus();
86 TEInit();
87 InitDialogs(0);
88 InitCursor();
89 MaxApplZone();
90
91 _atexit(handle_exit);
92
93 if (!(mbar = GetNewMBar(MBAR_ID)))
94 panic("no mbar");
95 SetMenuBar(mbar);
96 if (!(apple_menu = GetMHandle(APPLE_MENU_ID)))
97 panic("no apple menu");
98 AddResMenu(apple_menu, 'DRVR');
99 if (!(file_menu = GetMHandle(FILE_MENU_ID)))
100 panic("no file menu");
101 update_menu();
102 DrawMenuBar();
103
104 show_connect_dialog();
105
106 for (;;) {
107 wait_type = WAIT_TYPE_BACKGROUND;
108 for (n = 0; n < nfocusables; n++) {
109 if (focusables[n]->wait_type)
110 wait_type |= focusables[n]->wait_type(focusables[n]);
111 else if (focusables[n]->visible)
112 wait_type |= WAIT_TYPE_FOREGROUND;
113 }
114
115 if (wait_type & WAIT_TYPE_URGENT)
116 wait_ticks = 0;
117 else if (wait_type & WAIT_TYPE_FOREGROUND)
118 wait_ticks = 5L;
119 else
120 wait_ticks = 60L;
121
122 WaitNextEvent(everyEvent, &event, wait_ticks, 0L);
123
124 if (event.what != nullEvent) {
125 event_in = FindWindow(event.where, &event_win);
126 found_focusable = NULL;
127 for (n = 0; n < nfocusables; n++) {
128 if (focusables[n]->win == event_win) {
129 found_focusable = focusables[n];
130 break;
131 }
132 }
133 }
134
135 switch (event.what) {
136 case nullEvent:
137 for (n = 0; n < nfocusables; n++) {
138 if (focusables[n]->idle)
139 focusables[n]->idle(focusables[n], &event);
140 }
141 break;
142 case keyDown:
143 case autoKey:
144 key = event.message & charCodeMask;
145 if ((event.modifiers & cmdKey) != 0 &&
146 handle_menu(MenuKey(key)) == true)
147 break;
148 if (nfocusables && focusables[0]->visible &&
149 focusables[0]->key_down)
150 focusables[0]->key_down(focusables[0], &event);
151 break;
152 case mouseDown:
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 inGoAway:
164 if (TrackGoAway(event_win, event.where) &&
165 found_focusable && found_focusable->close)
166 found_focusable->close(found_focusable, &event);
167 break;
168 case inContent:
169 if (event_win != FrontWindow()) {
170 if (found_focusable)
171 show_focusable(found_focusable);
172 }
173 if (found_focusable && found_focusable->mouse_down)
174 found_focusable->mouse_down(found_focusable, &event);
175 break;
176 }
177 break;
178 case updateEvt:
179 case activateEvt:
180 event_win = (WindowPtr)event.message;
181
182 if (event.what == updateEvt) {
183 GetPort(&old_port);
184 SetPort(event_win);
185 BeginUpdate(event_win);
186 }
187
188 if (found_focusable && found_focusable->update)
189 found_focusable->update(found_focusable, &event);
190
191 if (event.what == updateEvt) {
192 EndUpdate(event_win);
193 SetPort(old_port);
194 }
195 break;
196 case app4Evt:
197 if (HiWord(event.message) & (1 << 8)) {
198 /* multifinder suspend/resume */
199 switch (event.message & (1 << 0)) {
200 case 0:
201 /* suspend */
202 for (n = 0; n < nfocusables; n++) {
203 if (focusables[n]->suspend)
204 focusables[n]->suspend(focusables[n], &event);
205 }
206 break;
207 case 1:
208 /* resume */
209 for (n = 0; n < nfocusables; n++) {
210 if (focusables[n]->resume)
211 focusables[n]->resume(focusables[n], &event);
212 }
213 break;
214 }
215 }
216 break;
217 }
218 }
219
220 return 0;
221 }
222
223 short
224 show_connect_dialog(void)
225 {
226 Str255 txt, server, ports, nick, ident, realname, channel, password;
227 StringHandle h;
228 DialogPtr dlg;
229 Handle ihandle;
230 Rect irect;
231 size_t size, n, m;
232 long port;
233 short hit, itype, ret;
234
235 if ((dlg = GetNewDialog(CONNECT_DLOG_ID, nil, (WindowPtr)-1)) == NULL)
236 panic("Can't find connection DLOG %d", CONNECT_DLOG_ID);
237
238 for (n = 0; n < nitems(config_fields); n++) {
239 struct config_field *cf = &config_fields[n];
240 short sval;
241
242 GetDItem(dlg, cf->ditl_id, &itype, &ihandle, &irect);
243
244 if (cf->type == CONFIG_TYPE_PASSWORD) {
245 PasswordDialogFieldFilterSetup(cf->ditl_id,
246 cf->password_storage, sizeof(cf->password_storage));
247 }
248
249 if (cf->res_id && (h = GetString(cf->res_id))) {
250 HLock(h);
251 if (cf->type == CONFIG_TYPE_PASSWORD) {
252 size = (*h)[0];
253 for (m = 0; m < size; m++)
254 cf->password_storage[m] = '•';
255 cf->password_storage[m] = '\0';
256 CtoPstr(cf->password_storage);
257 SetIText(ihandle, cf->password_storage);
258 strlcpy(cf->password_storage, PtoCstr(*h),
259 sizeof(cf->password_storage));
260 } else {
261 SetIText(ihandle, *h);
262 }
263 HUnlock(h);
264 ReleaseResource(h);
265 } else if (cf->sdefault[0] != '\0') {
266 strlcpy((char *)&txt, cf->sdefault, sizeof(txt));
267 CtoPstr(txt);
268 SetIText(ihandle, txt);
269 }
270 }
271
272 ShowWindow(dlg);
273
274 get_input:
275 ModalDialog(PasswordDialogFieldFilter, &hit);
276 switch (hit) {
277 case -1:
278 DisposeDialog(dlg);
279 return;
280 case OK:
281 goto verify;
282 default:
283 goto get_input;
284 }
285
286 verify:
287 for (n = 0; n < nitems(config_fields); n++) {
288 struct config_field *cf = &config_fields[n];
289 long lval;
290 short sval;
291
292 if (cf->type == CONFIG_TYPE_PASSWORD) {
293 memcpy((char *)&txt, cf->password_storage, sizeof(txt));
294 } else {
295 GetDItem(dlg, cf->ditl_id, &itype, &ihandle, &irect);
296 GetIText(ihandle, txt);
297 PtoCstr(txt);
298 }
299
300 switch (cf->type) {
301 case CONFIG_TYPE_STRING:
302 case CONFIG_TYPE_PASSWORD:
303 if (cf->min && strlen((char *)txt) < cf->min) {
304 warn("%s is too short (minimum %d)", cf->name, cf->min);
305 goto get_input;
306 }
307 if (cf->max && strlen((char *)txt) > cf->max) {
308 warn("%s is too long (maximum %d)", cf->name, cf->max);
309 goto get_input;
310 }
311 break;
312 case CONFIG_TYPE_SHORT:
313 lval = atol((char *)txt);
314 if (lval < cf->min) {
315 warn("%s must be at least %d", cf->name, cf->min);
316 goto get_input;
317 }
318 if (lval > cf->max) {
319 warn("%s must be less than %d", cf->name, cf->max);
320 goto get_input;
321 }
322 break;
323 }
324
325 switch (cf->ditl_id) {
326 case CONNECT_SERVER_ID:
327 strlcpy((char *)&server, (char *)txt, sizeof(server));
328 break;
329 case CONNECT_PORT_ID:
330 port = atol((char *)txt);
331 break;
332 case CONNECT_PASSWORD_ID:
333 strlcpy((char *)&password, (char *)txt, sizeof(password));
334 break;
335 case CONNECT_NICK_ID:
336 strlcpy((char *)&nick, (char *)txt, sizeof(nick));
337 break;
338 case CONNECT_IDENT_ID:
339 strlcpy((char *)&ident, (char *)txt, sizeof(ident));
340 break;
341 case CONNECT_REALNAME_ID:
342 strlcpy((char *)&realname, (char *)txt, sizeof(realname));
343 break;
344 case CONNECT_CHANNEL_ID:
345 strlcpy((char *)&channel, (char *)txt, sizeof(channel));
346 break;
347 }
348
349 if ((h = GetString(cf->res_id)) != NULL) {
350 RmveResource(h);
351 ReleaseResource(h);
352 }
353 size = strlen((char *)txt) + 1;
354 h = (StringHandle)xNewHandle(size);
355 strlcpy((char *)*h, (char *)txt, size);
356 CtoPstr(*h);
357 strlcpy((char *)txt, cf->name, sizeof(txt));
358 CtoPstr(txt);
359 AddResource(h, 'STR ', cf->res_id, txt);
360 ReleaseResource(h);
361 }
362
363 PasswordDialogFieldFinish();
364 DisposeDialog(dlg);
365
366 chatter_init((char *)&server, port, (char *)&password, (char *)&nick,
367 (char *)&ident, (char *)&realname, (char *)&channel);
368 }
369
370 bool
371 handle_menu(long menu_id)
372 {
373 size_t n;
374 bool ret = false;
375 bool quit = true;
376
377 switch (HiWord(menu_id)) {
378 case APPLE_MENU_ID:
379 switch (LoWord(menu_id)) {
380 case APPLE_MENU_ABOUT_ID: {
381 char vers_s[255];
382
383 sprintf(vers_s, "%s %s", PROGRAM_NAME, get_version(true));
384 note("%s", vers_s);
385
386 ret = true;
387 break;
388 }
389 }
390 break;
391 case FILE_MENU_ID:
392 switch (LoWord(menu_id)) {
393 case FILE_MENU_CONNECT_ID:
394 show_connect_dialog();
395 ret = true;
396 break;
397 case FILE_MENU_QUIT_ID:
398 ret = true;
399 quit = true;
400 for (n = 0; n < nfocusables; n++) {
401 if (focusables[n] && focusables[n]->quit &&
402 !focusables[n]->quit(focusables[n])) {
403 quit = false;
404 break;
405 }
406 }
407 if (quit)
408 ExitToShell();
409 break;
410 }
411 break;
412 default:
413 if (nfocusables && focusables[0]->visible && focusables[0]->menu)
414 ret = focusables[0]->menu(focusables[0], HiWord(menu_id),
415 LoWord(menu_id));
416 }
417
418 HiliteMenu(0);
419 return ret;
420 }
421
422 void
423 update_menu(void)
424 {
425 }
426
427 void
428 handle_exit(void)
429 {
430 short n;
431
432 for (n = 0; n < nfocusables; n++) {
433 if (focusables[n]->atexit)
434 focusables[n]->atexit(focusables[n]);
435 }
436 }
437
438 void
439 add_focusable(struct focusable *focusable)
440 {
441 nfocusables++;
442 focusables = xreallocarray(focusables, sizeof(Ptr), nfocusables);
443
444 if (nfocusables > 1)
445 memmove(focusables + sizeof(Ptr), focusables,
446 sizeof(Ptr) * (nfocusables - 1));
447
448 focusables[0] = focusable;
449
450 show_focusable(focusable);
451 }
452
453 void
454 show_focusable(struct focusable *focusable)
455 {
456 struct focusable *last, *tmp;
457 short n;
458
459 if (nfocusables > 1 && focusables[0] != focusable) {
460 last = focusables[0];
461 focusables[0] = focusable;
462 for (n = 1; n < nfocusables; n++) {
463 tmp = focusables[n];
464 focusables[n] = last;
465 last = tmp;
466 if (last == focusable)
467 break;
468 }
469 }
470
471 focusable->visible = true;
472 ShowWindow(focusable->win);
473 SelectWindow(focusable->win);
474
475 cancel_notification();
476 }
477
478 void
479 close_focusable(struct focusable *focusable)
480 {
481 short n;
482
483 if (focusable->visible)
484 CloseWindow(focusable->win);
485
486 for (n = 0; n < nfocusables; n++) {
487 if (focusables[n] == focusable) {
488 for (; n < nfocusables - 1; n++)
489 focusables[n] = focusables[n + 1];
490 break;
491 }
492 }
493
494 nfocusables--;
495 if (nfocusables)
496 focusables = xreallocarray(focusables, sizeof(Ptr), nfocusables);
497 else {
498 free(focusables);
499 focusables = NULL;
500 }
501
502 if (nfocusables && focusables[0]->visible)
503 SelectWindow(focusables[0]->win);
504 }
505
506 void
507 hide_focusable(struct focusable *focusable)
508 {
509 short n;
510
511 HideWindow(focusable->win);
512 focusable->visible = false;
513
514 for (n = 0; n < nfocusables; n++) {
515 if (focusables[n] == focusable) {
516 for (; n < nfocusables - 1; n++)
517 focusables[n] = focusables[n + 1];
518 break;
519 }
520 }
521 focusables[nfocusables - 1] = focusable;
522 }
523
524 void
525 notify(void)
526 {
527 memset(&notification, 0, sizeof(notification));
528 notification.qType = nmType;
529 notification.nmMark = 1;
530 notification.nmSound = (Handle)-1;
531 notification.nmIcon = GetResource('SICN', NOTIFICATION_ICON_ID);
532 NMInstall(&notification);
533 }
534
535 void
536 cancel_notification(void)
537 {
538 if (notification.qType)
539 NMRemove(&notification);
540 memset(&notification, 0, sizeof(notification));
541 }