AmendHub

Download

jcs

/

wifi_da

/

window.c

 

(View History)

jcs   window+Rez: Use an off-screen static text field for password storage Latest amendment: 25 on 2024-01-27

1 /*
2 * Copyright (c) 2023 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 <stdio.h>
18 #include <string.h>
19 #include "wi-fi.h"
20
21 #define NO_DEVICE_TEXT "\pNo Device Found"
22 #define NO_NETWORK_TEXT "(No Network)"
23
24 #define DA_HEIGHT 25
25 #define DA_WIDTH 180
26
27 SICNHandle signal_icons;
28 ControlHandle ssid_list;
29 MenuHandle ssid_menu;
30 Rect ssid_menu_rect;
31 BitMap win_shadow;
32 short nwifi_scan_networks = 0;
33 struct wifi_network_entry wifi_menu_networks[nitems(wifi_scan_networks)] = { 0 };
34 short nwifi_menu_networks = 0;
35
36 pascal Boolean WiFiPasswordDialogFieldFilter(DialogPtr dlg,
37 EventRecord *event, short *hit);
38
39 WindowPtr
40 create_window(short ctlrefnum)
41 {
42 GrafPtr savePort, tport;
43 Rect bounds;
44
45 GetPort(&savePort);
46
47 /* screenBits is not available from a DA */
48 tport = (GrafPtr)NewPtr(sizeof(GrafPort));
49 OpenPort(tport);
50 /* upper right corner, but leave space for desktop icon */
51 bounds.top = 45;
52 bounds.bottom = bounds.top + DA_HEIGHT;
53 bounds.left = tport->portRect.right - DA_WIDTH - 70;
54 bounds.right = bounds.left + DA_WIDTH;
55 ClosePort(tport);
56
57 win = NewWindow(0L, &bounds, "\pWi-Fi", true, rDocProc, 0L, true, 0L);
58 ((WindowPeek)win)->windowKind = ctlrefnum;
59
60 win_shadow.rowBytes = (((DA_WIDTH - 1) / 16) + 1) * 2;
61 win_shadow.baseAddr = xmalloczero(win_shadow.rowBytes * DA_HEIGHT);
62 SetRect(&win_shadow.bounds, 0, 0, DA_WIDTH, DA_HEIGHT);
63
64 signal_icons = (SICNHandle)GetResource('SICN', OwnedResourceID(0));
65 if (signal_icons == NULL)
66 warn("Can't find SICN");
67
68 ssid_menu = NewMenu(OwnedResourceID(10), "\p");
69 InsMenuItem(ssid_menu, "\pFinding Wi-Fi...", 1);
70
71 SetRect(&ssid_menu_rect, 30, 3, 170, 21); /* ltrb */
72
73 #if 0 /* system 6 does not seem to support popupMenuProc */
74 ssid_list = NewControl(win, &ssid_menu_rect, 0, true, 0,
75 OwnedResourceID(SSID_MENU_ID), 0, popupMenuProc + popupFixedWidth, 0L);
76 #endif
77
78 update_window(true);
79
80 if (wifi_scsi_id == WIFI_SCSI_ID_FINDING)
81 wifi_scsi_id = scsi_find_wifi();
82
83 if (wifi_scsi_id == WIFI_SCSI_ID_NONE) {
84 da_state = STATE_IDLE;
85 SetItem(ssid_menu, 1, NO_DEVICE_TEXT);
86 update_window(true);
87 } else {
88 scsi_wifi_info(wifi_scsi_id, &wifi_cur_info);
89 da_state = STATE_SCANNING;
90 scsi_wifi_scan(wifi_scsi_id);
91 }
92
93 SetPort(savePort);
94
95 return win;
96 }
97
98 void
99 update_window(bool force)
100 {
101 Str255 menuText;
102 GrafPtr savePort;
103 Point p;
104 short i, icon, x, w;
105 RgnHandle tmpRgn;
106 Rect r, destRect;
107 BitMap bm, cur_bits;
108
109 GetPort(&savePort);
110 SetPort(win);
111
112 DEBUG_LOG(("redrawing window"));
113 cur_bits = win->portBits;
114 SetPortBits(&win_shadow);
115
116 if (!force)
117 BeginUpdate(win);
118
119 EraseRect(&win->portRect);
120 DrawControls(win);
121
122 /* system 6 does not seem to support popupMenuProc, fake it */
123 FrameRect(&ssid_menu_rect);
124 MoveTo(ssid_menu_rect.left + 1, ssid_menu_rect.bottom);
125 LineTo(ssid_menu_rect.right, ssid_menu_rect.bottom);
126 MoveTo(ssid_menu_rect.right, ssid_menu_rect.top + 1);
127 LineTo(ssid_menu_rect.right, ssid_menu_rect.bottom);
128
129 TextFont(systemFont);
130 TextSize(0);
131 GetItem(ssid_menu, 1, &menuText);
132 x = 15;
133 MoveTo(ssid_menu_rect.left + x, 16);
134
135 for (i = 1; i <= menuText[0]; i++) {
136 w = CharWidth(menuText[i]);
137 if (i < menuText[0] &&
138 x + w >= (ssid_menu_rect.right - ssid_menu_rect.left - 14)) {
139 /* truncate */
140 GetPen(&p);
141 MoveTo(p.h - 1, p.v);
142 DrawChar('…');
143 break;
144 }
145 DrawChar(menuText[i]);
146 x += w;
147 }
148
149 HLock(signal_icons);
150
151 SetRect(&destRect, 6, 4, 22, 20); /* ltrb */
152
153 if (wifi_scsi_id == WIFI_SCSI_ID_NONE)
154 icon = SIGNAL_NO_DEVICE;
155 else if (wifi_scsi_id == WIFI_SCSI_ID_FINDING)
156 icon = SIGNAL_FINDING_DEVICE;
157 else if (wifi_cur_info.ssid[0] == '\0')
158 icon = SIGNAL_NONE;
159 else if (wifi_cur_info.rssi >= -60)
160 icon = SIGNAL_4;
161 else if (wifi_cur_info.rssi >= -70)
162 icon = SIGNAL_3;
163 else if (wifi_cur_info.rssi >= -75)
164 icon = SIGNAL_2;
165 else
166 icon = SIGNAL_1;
167
168 bm.baseAddr = (Ptr)(*signal_icons)[icon];
169 bm.rowBytes = 2;
170 SetRect(&bm.bounds, 0, 0, 16, 16);
171 CopyBits(&bm, &win->portBits, &bm.bounds, &destRect, srcOr, NULL);
172
173 HUnlock(signal_icons);
174
175 if (!force)
176 EndUpdate(win);
177
178 SetPortBits(&cur_bits);
179
180 CopyBits(&win_shadow, &win->portBits, &win_shadow.bounds,
181 &win_shadow.bounds, srcCopy, nil);
182
183 ValidRect(&win->portRect);
184 SetPort(savePort);
185 }
186
187 void
188 update_wifi_cur_info(void)
189 {
190 struct wifi_network_entry old_info;
191
192 if (wifi_scsi_id == WIFI_SCSI_ID_NONE)
193 return;
194
195 memcpy(&old_info, &wifi_cur_info, sizeof(old_info));
196
197 scsi_wifi_info(wifi_scsi_id, &wifi_cur_info);
198
199 if (memcmp(&old_info, &wifi_cur_info, sizeof(old_info)) != 0) {
200 if (memcmp(&old_info.ssid, &wifi_cur_info.ssid,
201 sizeof(old_info.ssid)) == 0) {
202 /* just updating RSSI */
203 DEBUG_LOG(("update_wifi_cur_info: just updating rssi to %d",
204 wifi_cur_info.rssi));
205 update_window(true);
206 } else {
207 DEBUG_LOG(("update_wifi_cur_info: updating ssid list"));
208 update_wifi_ssid_list(false);
209 }
210 }
211 }
212
213 void
214 update_wifi_ssid_list(bool update_networks)
215 {
216 char ssid[64] = { 0 };
217 short n, m, mitem;
218
219 if (wifi_scsi_id == WIFI_SCSI_ID_NONE)
220 return;
221
222 if (update_networks) {
223 nwifi_scan_networks = scsi_wifi_scan_results(wifi_scsi_id,
224 wifi_scan_networks, nitems(wifi_scan_networks));
225 DEBUG_LOG(("scsi_wifi_scan_results: %d", nwifi_scan_networks));
226
227 if (nwifi_scan_networks == 0) {
228 memcpy(&wifi_scan_networks, &wifi_cur_info,
229 sizeof(wifi_cur_info));
230 nwifi_scan_networks = 1;
231 }
232 }
233
234 /* put the current network first, if any */
235 memcpy(&wifi_menu_networks[0], &wifi_cur_info,
236 sizeof(wifi_menu_networks[0]));
237
238 nwifi_menu_networks = nwifi_scan_networks;
239 if (nwifi_menu_networks > nitems(wifi_menu_networks)) {
240 nwifi_menu_networks = nitems(wifi_menu_networks);
241 DEBUG_LOG(("capping nets to %d", nwifi_menu_networks));
242 }
243
244 /* add each additional network from the scan, excluding current */
245 for (n = 0, m = 1; n < nwifi_menu_networks; n++) {
246 if (strcmp(wifi_scan_networks[n].ssid,
247 wifi_cur_info.ssid) == 0) {
248 DEBUG_LOG(("skipping %s, is current",
249 wifi_scan_networks[n].ssid));
250 continue;
251 }
252
253 DEBUG_LOG(("net %d: %s", m, wifi_scan_networks[n].ssid));
254
255 memcpy(&wifi_menu_networks[m++], &wifi_scan_networks[n],
256 sizeof(wifi_menu_networks[0]));
257 }
258
259 nwifi_menu_networks = m;
260
261 if (wifi_menu_networks[0].ssid[0] == '\0')
262 strlcpy(wifi_menu_networks[0].ssid, NO_NETWORK_TEXT,
263 sizeof(wifi_menu_networks[0].ssid));
264
265 /* leave the first item and we'll set its text later */
266 while (CountMItems(ssid_menu) > 1)
267 DelMenuItem(ssid_menu, 2);
268
269 for (n = 0, mitem = 1; n < nwifi_menu_networks; n++) {
270 /*
271 * InsMenuItem supports meta chars in item text which can
272 * have side effects, so insert a blank item and then set its
273 * name with SetItem which does not suport meta chars.
274 */
275 strlcpy(ssid, wifi_menu_networks[n].ssid, sizeof(ssid));
276 CtoPstr(ssid);
277
278 if (n > 0)
279 InsMenuItem(ssid_menu, "\p...", mitem);
280
281 SetItem(ssid_menu, mitem, ssid);
282
283 if (wifi_cur_info.ssid[0] == '\0' && n == 0)
284 CheckItem(ssid_menu, mitem, true);
285 else
286 CheckItem(ssid_menu, mitem,
287 (strcmp(wifi_menu_networks[n].ssid,
288 wifi_cur_info.ssid) == 0));
289
290 mitem++;
291 }
292
293 update_window(true);
294 }
295
296 void
297 activate_window(bool activate)
298 {
299 Rect r;
300 GrafPtr savePort;
301
302 #if 0
303 GetPort(&savePort);
304 SetPort(win);
305 r = win->portRect;
306 r.top = r.bottom - 16;
307 r.left = r.left - 16;
308 InvalRect(&r);
309 SetPort(savePort);
310 #endif
311 }
312
313 void
314 destroy_window(void)
315 {
316 ReleaseResource(signal_icons);
317
318 if (!win)
319 return;
320
321 DisposeWindow(win);
322 win = 0;
323 }
324
325 void
326 window_mousedown(Point p)
327 {
328 Str255 password;
329 char ssid[64];
330 Rect r, irect;
331 GrafPtr savePort;
332 short mitems, n, selitem, hit, itype;
333 long new_net;
334 struct wifi_network_entry *net;
335 struct wifi_join_request wjr;
336 DialogPtr dg;
337 EventRecord e;
338 ControlHandle ihandle;
339
340 GetPort(&savePort);
341 SetPort(win);
342
343 r = ssid_menu_rect;
344 LocalToGlobal(&r);
345
346 /* XXX: why does PtInRect not work here? */
347 if (!(p.h >= r.left && p.h <= r.left + r.right &&
348 p.v >= r.top && p.v <= r.top + r.bottom)) {
349 SetPort(savePort);
350 return;
351 }
352
353 if (wifi_scsi_id == WIFI_SCSI_ID_NONE)
354 return;
355
356 InsertMenu(ssid_menu, -1);
357
358 mitems = CountMItems(ssid_menu);
359 selitem = 1;
360 for (n = 0; n < mitems; n++) {
361 if (strcmp(wifi_menu_networks[n].ssid, wifi_cur_info.ssid) == 0) {
362 selitem = n + 1;
363 break;
364 }
365 }
366
367 new_net = PopUpMenuSelect(ssid_menu, r.top + 1, r.left + 1, selitem);
368
369 DeleteMenu((*ssid_menu)->menuID);
370 if (hiword(new_net) == 0 || loword(new_net) == selitem)
371 goto menu_done;
372
373 net = &wifi_menu_networks[loword(new_net) - 1];
374
375 memset(&wjr, 0, sizeof(wjr));
376 strlcpy(wjr.ssid, net->ssid, sizeof(wjr.ssid));
377
378 if (net->flags & WIFI_NETWORK_FLAG_AUTH) {
379 strlcpy(ssid, net->ssid, sizeof(ssid));
380 CtoPstr(ssid);
381 ParamText(ssid, "\p", "\p", "\p");
382 PtoCstr(ssid);
383
384 dg = GetNewDialog(OwnedResourceID(PASSWORD_DIALOG_ID), 0L,
385 (WindowPtr)-1L);
386 center_in_screen(((DialogPeek)dg)->window.port.portRect.right,
387 ((DialogPeek)dg)->window.port.portRect.bottom, false, &r);
388 MoveWindow(dg, r.left, r.top, false);
389
390 ShowWindow(dg);
391 SetPort(dg);
392
393 GetDItem(dg, PASSWORD_DIALOG_PASSWORD_STORAGE_ID, &itype,
394 &ihandle, &irect);
395 SetIText(ihandle, "\p");
396
397 /* outline ok button */
398 GetDItem(dg, ok, &itype, &ihandle, &irect);
399 PenSize(3, 3);
400 InsetRect(&irect, -4, -4);
401 FrameRoundRect(&irect, 16, 16);
402 PenNormal();
403
404 for (;;) {
405 ModalDialog(WiFiPasswordDialogFieldFilter, &hit);
406 if (hit == ok || hit == cancel)
407 break;
408 }
409
410 if (hit != ok) {
411 DisposDialog(dg);
412 goto menu_done;
413 }
414
415 GetDItem(dg, PASSWORD_DIALOG_PASSWORD_STORAGE_ID, &itype,
416 &ihandle, &irect);
417 GetIText(ihandle, &password);
418 PtoCstr(password);
419 //DEBUG_LOG(("pass is \"%s\"", password));
420 strlcpy(wjr.key, (char *)password, sizeof(wjr.key));
421
422 DisposDialog(dg);
423 }
424
425 scsi_wifi_join(wifi_scsi_id, &wjr);
426
427 /* force an update of the list */
428 scsi_wifi_info(wifi_scsi_id, &wifi_cur_info);
429 update_wifi_ssid_list(false);
430
431 menu_done:
432 SetPort(savePort);
433 }
434
435 pascal Boolean
436 WiFiPasswordDialogFieldFilter(DialogPtr dlg, EventRecord *event,
437 short *hit)
438 {
439 Str255 password;
440 DialogPeek dlgp;
441 ControlHandle ihandle;
442 Rect irect;
443 short sel_start, sel_end, itype;
444 char key;
445
446 dlgp = (DialogPeek)dlg;
447
448 switch (event->what) {
449 case keyDown:
450 case autoKey:
451 sel_start = (*(dlgp->textH))->selStart;
452 sel_end = (*(dlgp->textH))->selEnd;
453
454 key = event->message & charCodeMask;
455 if (event->modifiers & cmdKey) {
456 /* TODO: implement DlgPaste for cmd+v? */
457 event->what = nullEvent;
458 return FALSE;
459 }
460
461 GetDItem(dlg, PASSWORD_DIALOG_PASSWORD_STORAGE_ID, &itype,
462 &ihandle, &irect);
463 GetIText(ihandle, &password);
464 PtoCstr(password);
465
466 if (key == 8) {
467 /* backspace */
468 if (sel_start == sel_end && sel_start > 0)
469 memmove(password + sel_start - 1, password + sel_start,
470 sizeof(password) - sel_start - 1);
471 else if (sel_start != sel_end)
472 memmove(password + sel_start, password + sel_end,
473 sizeof(password) - sel_end - 1);
474 } else if (sel_start >= sizeof(password)) {
475 event->what = nullEvent;
476 return FALSE;
477 } else if (key >= ' ' && key <= '~') {
478 if (sel_start != sel_end)
479 /* delete selection before making space for new char */
480 memmove(password + sel_start, password + sel_end,
481 sizeof(password) - sel_end - 1);
482 memmove(password + sel_start + 1, password + sel_start,
483 sizeof(password) - sel_start - 1);
484 password[sel_start] = key;
485 event->message = '•';
486 }
487 password[(*(dlgp->textH))->teLength + 1] = '\0';
488 CtoPstr(password);
489
490 SetIText(ihandle, password);
491 sel_start = 0;
492 break;
493 }
494
495 return ModalDialogFilter(dlg, event, hit);
496 }