AmendHub

Download

jcs

/

wallops

/

main.c

 

(View History)

jcs   main: Center connection dialog on the screen Latest amendment: 36 on 2022-09-06

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 DialogTHndl dlgh;
229 DialogPtr dlg;
230 Handle ihandle;
231 Rect irect;
232 size_t size, n, m;
233 long port;
234 short hit, itype, ret;
235
236 /* center dialog in screen */
237 dlgh = (DialogTHndl)xGetResource('DLOG', CONNECT_DLOG_ID);
238 HLock(dlgh);
239 center_in_screen((**dlgh).boundsRect.right - (**dlgh).boundsRect.left,
240 (**dlgh).boundsRect.bottom - (**dlgh).boundsRect.top,
241 true, &(**dlgh).boundsRect);
242 HUnlock(dlgh);
243
244 if ((dlg = GetNewDialog(CONNECT_DLOG_ID, nil, (WindowPtr)-1)) == NULL)
245 panic("Can't find connection DLOG %d", CONNECT_DLOG_ID);
246
247 for (n = 0; n < nitems(config_fields); n++) {
248 struct config_field *cf = &config_fields[n];
249 short sval;
250
251 GetDItem(dlg, cf->ditl_id, &itype, &ihandle, &irect);
252
253 if (cf->type == CONFIG_TYPE_PASSWORD) {
254 PasswordDialogFieldFilterSetup(cf->ditl_id,
255 cf->password_storage, sizeof(cf->password_storage));
256 }
257
258 if (cf->res_id && (h = GetString(cf->res_id))) {
259 HLock(h);
260 if (cf->type == CONFIG_TYPE_PASSWORD) {
261 size = (*h)[0];
262 for (m = 0; m < size; m++)
263 cf->password_storage[m] = '•';
264 cf->password_storage[m] = '\0';
265 CtoPstr(cf->password_storage);
266 SetIText(ihandle, cf->password_storage);
267 strlcpy(cf->password_storage, PtoCstr(*h),
268 sizeof(cf->password_storage));
269 } else {
270 SetIText(ihandle, *h);
271 }
272 HUnlock(h);
273 ReleaseResource(h);
274 } else if (cf->sdefault[0] != '\0') {
275 strlcpy((char *)&txt, cf->sdefault, sizeof(txt));
276 CtoPstr(txt);
277 SetIText(ihandle, txt);
278 }
279 }
280
281 ShowWindow(dlg);
282
283 get_input:
284 ModalDialog(PasswordDialogFieldFilter, &hit);
285 switch (hit) {
286 case -1:
287 DisposeDialog(dlg);
288 ReleaseResource(dlgh);
289 return;
290 case OK:
291 goto verify;
292 default:
293 goto get_input;
294 }
295
296 verify:
297 for (n = 0; n < nitems(config_fields); n++) {
298 struct config_field *cf = &config_fields[n];
299 long lval;
300 short sval;
301
302 if (cf->type == CONFIG_TYPE_PASSWORD) {
303 memcpy((char *)&txt, cf->password_storage, sizeof(txt));
304 } else {
305 GetDItem(dlg, cf->ditl_id, &itype, &ihandle, &irect);
306 GetIText(ihandle, txt);
307 PtoCstr(txt);
308 }
309
310 switch (cf->type) {
311 case CONFIG_TYPE_STRING:
312 case CONFIG_TYPE_PASSWORD:
313 if (cf->min && strlen((char *)txt) < cf->min) {
314 warn("%s is too short (minimum %d)", cf->name, cf->min);
315 goto get_input;
316 }
317 if (cf->max && strlen((char *)txt) > cf->max) {
318 warn("%s is too long (maximum %d)", cf->name, cf->max);
319 goto get_input;
320 }
321 break;
322 case CONFIG_TYPE_SHORT:
323 lval = atol((char *)txt);
324 if (lval < cf->min) {
325 warn("%s must be at least %d", cf->name, cf->min);
326 goto get_input;
327 }
328 if (lval > cf->max) {
329 warn("%s must be less than %d", cf->name, cf->max);
330 goto get_input;
331 }
332 break;
333 }
334
335 switch (cf->ditl_id) {
336 case CONNECT_SERVER_ID:
337 strlcpy((char *)&server, (char *)txt, sizeof(server));
338 break;
339 case CONNECT_PORT_ID:
340 port = atol((char *)txt);
341 break;
342 case CONNECT_PASSWORD_ID:
343 strlcpy((char *)&password, (char *)txt, sizeof(password));
344 break;
345 case CONNECT_NICK_ID:
346 strlcpy((char *)&nick, (char *)txt, sizeof(nick));
347 break;
348 case CONNECT_IDENT_ID:
349 strlcpy((char *)&ident, (char *)txt, sizeof(ident));
350 break;
351 case CONNECT_REALNAME_ID:
352 strlcpy((char *)&realname, (char *)txt, sizeof(realname));
353 break;
354 case CONNECT_CHANNEL_ID:
355 strlcpy((char *)&channel, (char *)txt, sizeof(channel));
356 break;
357 }
358
359 if ((h = GetString(cf->res_id)) != NULL) {
360 RmveResource(h);
361 ReleaseResource(h);
362 }
363 size = strlen((char *)txt) + 1;
364 h = (StringHandle)xNewHandle(size);
365 strlcpy((char *)*h, (char *)txt, size);
366 CtoPstr(*h);
367 strlcpy((char *)txt, cf->name, sizeof(txt));
368 CtoPstr(txt);
369 AddResource(h, 'STR ', cf->res_id, txt);
370 ReleaseResource(h);
371 }
372
373 PasswordDialogFieldFinish();
374 DisposeDialog(dlg);
375 ReleaseResource(dlgh);
376
377 chatter_init((char *)&server, port, (char *)&password, (char *)&nick,
378 (char *)&ident, (char *)&realname, (char *)&channel);
379 }
380
381 bool
382 handle_menu(long menu_id)
383 {
384 size_t n;
385 bool ret = false;
386 bool quit = true;
387
388 switch (HiWord(menu_id)) {
389 case APPLE_MENU_ID:
390 switch (LoWord(menu_id)) {
391 case APPLE_MENU_ABOUT_ID: {
392 char vers_s[255];
393
394 sprintf(vers_s, "%s %s", PROGRAM_NAME, get_version(true));
395 note("%s", vers_s);
396
397 ret = true;
398 break;
399 }
400 default: {
401 Str255 da;
402 GrafPtr save_port;
403
404 GetItem(apple_menu, LoWord(menu_id), &da);
405 GetPort(&save_port);
406 OpenDeskAcc(da);
407 SetPort(save_port);
408
409 ret = true;
410 break;
411 }
412 }
413 break;
414 case FILE_MENU_ID:
415 switch (LoWord(menu_id)) {
416 case FILE_MENU_CONNECT_ID:
417 show_connect_dialog();
418 ret = true;
419 break;
420 case FILE_MENU_QUIT_ID:
421 ret = true;
422 quit = true;
423 for (n = 0; n < nfocusables; n++) {
424 if (focusables[n] && focusables[n]->quit &&
425 !focusables[n]->quit(focusables[n])) {
426 quit = false;
427 break;
428 }
429 }
430 if (quit)
431 ExitToShell();
432 break;
433 }
434 break;
435 default:
436 if (nfocusables && focusables[0]->visible && focusables[0]->menu)
437 ret = focusables[0]->menu(focusables[0], HiWord(menu_id),
438 LoWord(menu_id));
439 }
440
441 HiliteMenu(0);
442 return ret;
443 }
444
445 void
446 update_menu(void)
447 {
448 }
449
450 void
451 handle_exit(void)
452 {
453 short n;
454
455 for (n = 0; n < nfocusables; n++) {
456 if (focusables[n]->atexit)
457 focusables[n]->atexit(focusables[n]);
458 }
459 }
460
461 void
462 add_focusable(struct focusable *focusable)
463 {
464 nfocusables++;
465 focusables = xreallocarray(focusables, sizeof(Ptr), nfocusables);
466
467 if (nfocusables > 1)
468 memmove(focusables + sizeof(Ptr), focusables,
469 sizeof(Ptr) * (nfocusables - 1));
470
471 focusables[0] = focusable;
472
473 show_focusable(focusable);
474 }
475
476 void
477 show_focusable(struct focusable *focusable)
478 {
479 struct focusable *last, *tmp;
480 short n;
481
482 if (nfocusables > 1 && focusables[0] != focusable) {
483 last = focusables[0];
484 focusables[0] = focusable;
485 for (n = 1; n < nfocusables; n++) {
486 tmp = focusables[n];
487 focusables[n] = last;
488 last = tmp;
489 if (last == focusable)
490 break;
491 }
492 }
493
494 focusable->visible = true;
495 ShowWindow(focusable->win);
496 SelectWindow(focusable->win);
497
498 cancel_notification();
499 }
500
501 void
502 close_focusable(struct focusable *focusable)
503 {
504 short n;
505
506 if (focusable->visible)
507 CloseWindow(focusable->win);
508
509 for (n = 0; n < nfocusables; n++) {
510 if (focusables[n] == focusable) {
511 for (; n < nfocusables - 1; n++)
512 focusables[n] = focusables[n + 1];
513 break;
514 }
515 }
516
517 nfocusables--;
518 if (nfocusables)
519 focusables = xreallocarray(focusables, sizeof(Ptr), nfocusables);
520 else
521 xfree(&focusables);
522
523 if (nfocusables && focusables[0]->visible)
524 SelectWindow(focusables[0]->win);
525 }
526
527 void
528 hide_focusable(struct focusable *focusable)
529 {
530 short n;
531
532 HideWindow(focusable->win);
533 focusable->visible = false;
534
535 for (n = 0; n < nfocusables; n++) {
536 if (focusables[n] == focusable) {
537 for (; n < nfocusables - 1; n++)
538 focusables[n] = focusables[n + 1];
539 break;
540 }
541 }
542 focusables[nfocusables - 1] = focusable;
543 }
544
545 void
546 notify(void)
547 {
548 memset(&notification, 0, sizeof(notification));
549 notification.qType = nmType;
550 notification.nmMark = 1;
551 notification.nmSound = (Handle)-1;
552 notification.nmIcon = GetResource('SICN', NOTIFICATION_ICON_ID);
553 NMInstall(&notification);
554 }
555
556 void
557 cancel_notification(void)
558 {
559 if (notification.qType)
560 NMRemove(&notification);
561 memset(&notification, 0, sizeof(notification));
562 }