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 | } |