AmendHub

Download:

jcs

/

wifi_da

/

amendments

/

1

*: Re-import with some System 6 fixes

System 6 doesn't seem to support popupMenuProc, so kind of implement it
ourselves.
 
Since we're running in a DA we can't access screenBits.bounds, so create
CenterDialog to work around it by creating a new Grafport and checking its
size which will be the same as the screen. I got this tip from a 1988
Usenet post by Rich Siegel.

jcs made amendment 1 about 1 year ago
--- about.c Tue Aug 22 23:19:40 2023 +++ about.c Tue Aug 22 23:19:40 2023 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 joshua stein <jcs@jcs.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "wi-fi.h" + +void +about(void) +{ + DialogPtr dp; + short hit; + Handle h; + + dp = GetNewDialog(OwnedResourceID(ABOUT_DIALOG_ID), 0L, (WindowPtr)-1L); + + ParamText("\pWi-Fi Desk Accessory", "\pCopyright 2023, joshua stein", + "\pWorld Wide Web: http://jcs.org/", + "\pI miss you, Carl"); + + CenterDialog(dp, false); + ShowWindow(dp); + + for (;;) { + ModalDialog(0, &hit); + if (hit == ok) + break; + } + + DisposDialog(dp); +} --- main.c Tue Aug 22 23:31:42 2023 +++ main.c Tue Aug 22 23:31:42 2023 @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2023 joshua stein <jcs@jcs.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include "wi-fi.h" + +/* device control entry */ +DCtlPtr dce; + +short da_state = STATE_CLOSED; +short wifi_scsi_id = WIFI_SCSI_ID_FINDING; +struct wifi_network_entry wifi_cur_info = { 0 }; +struct wifi_network_entry wifi_scan_networks[10] = { 0 }; +MenuHandle menu, apple_menu; +short menuID; +WindowPtr win = 0L; +Cursor watch; + +short main(cntrlParam *p, DCtlPtr d, short n); +void da_open(void); +void da_control(short code, short *parm); +void da_close(void); +bool handle_menu(short i); +void handle_event(EventRecord *e); +void handle_timer(void); + +short +main(cntrlParam *p, DCtlPtr d, short n) +{ + if (d->dCtlStorage == 0) { + /* open */ + if (n == 0) { + SysBeep(3); + CloseDriver(d->dCtlRefNum); + } + return 0; + } + + dce = d; + dce->dCtlFlags &= ~dCtlEnable; + + switch (n) { + case 0: + da_open(); + break; + case 2: + da_control(p->csCode, p->csParam); + break; + case 4: + da_close(); + break; + } + + dce->dCtlFlags |= dCtlEnable; + + return 0; +} + +/* + * This is called each time the DA is selected from the Apple menu, + * even if we're already open. However, the Device Manager resets + * the "dCtlFlags", "dCtlMenu", "dCtlDelay", and "dCtlEMask" fields + * of the device control entry from the corresponding fields of the + * device header each time the desk accessory is opened. + */ +void +da_open(void) +{ + CursHandle h; + bool dispose; + + dce->dCtlFlags |= dNeedLock | dNeedGoodBye; + + if (win) + SelectWindow(win); + + if (da_state != STATE_CLOSED) { + dce->dCtlMenu = menuID; + return; + } + + da_state = STATE_INIT; + + SetResLoad(false); + h = GetCursor(watchCursor); + dispose = (GetHandleSize((Handle)h) == 0); + SetResLoad(true); + BlockMove(*(h=GetCursor(watchCursor)), &watch, sizeof(Cursor)); + if (dispose) + ReleaseResource((Handle)h); + + dce->dCtlMenu = menuID = OwnedResourceID(MAIN_MENU_ID); + menu = GetMenu(menuID); + (**menu).menuID = menuID; + InsertMenu(menu = GetMenu(menuID), 0); + + DrawMenuBar(); + + dce->dCtlWindow = create_window(dce->dCtlRefNum); + + HiliteMenu(0); +} + +void +da_control(short code, short *param) +{ + switch (code) { + case accCursor: + handle_timer(); + break; + case accMenu: + handle_menu(param[1]); + break; + case accEvent: + handle_event(*((EventRecord **)param)); + break; + case goodbye: + break; + } +} + +void +da_close(void) +{ + DeleteMenu(dce->dCtlMenu); + DrawMenuBar(); + dce->dCtlMenu = 0; + DisposeMenu(menu); + destroy_window(); + dce->dCtlWindow = 0; + da_state = STATE_CLOSED; +} + +bool +handle_menu(short i) +{ + bool ret = true; + + switch (i) { + case MAIN_MENU_ABOUT_ID: + about(); + break; + case MAIN_MENU_QUIT_ID: + CloseDriver(dce->dCtlRefNum); + break; + default: + ret = false; + } + + HiliteMenu(0); + return ret; +} + +void +handle_event(EventRecord *e) +{ + short key, mk; + + switch (e->what) { + case updateEvt: + update_window(false); + break; + case mouseDown: + window_mousedown(e->where); + update_window(false); + break; + case activateEvt: + activate_window((bool)(e->modifiers & activeFlag)); + break; + case keyDown: + case autoKey: + key = e->message & charCodeMask; + if ((e->modifiers & cmdKey) != 0) { + mk = MenuKey(key); + if (mk != 0 && handle_menu(mk)) + break; + } + break; + } +} + +void +handle_timer(void) +{ + static long last_update = 0; + + if (!win) + return; + + switch (da_state) { + case STATE_SCANNING: + /* check every second */ + if ((Ticks - last_update) < (60 * 1)) + break; + last_update = Ticks; + if (!scsi_wifi_scan_finished(wifi_scsi_id)) + break; + da_state = STATE_IDLE; + update_wifi_ssid_list(); + break; + case STATE_IDLE: + /* refresh RSSI every 5 seconds */ + if ((Ticks - last_update) < (60 * 5)) + break; + last_update = Ticks; + update_wifi_cur_info(); + break; + default: + return; + } +} + +long +OwnedResourceID(long n) +{ + return (0xc000 + ((~(dce->dCtlRefNum)) << 5) + n); +} + +void +warn(char *format, ...) +{ + static char warn_str[255]; + va_list ap; + + va_start(ap, format); + vsprintf(warn_str, format, ap); + va_end(ap); + + CtoPstr(warn_str); + ParamText(warn_str, "", "", ""); + Alert(OwnedResourceID(ERROR_DIALOG_ID), 0L); +} + +void +CenterDialog(DialogPtr dp, bool titlebar) +{ + GrafPtr tport; + Rect bounds; + short width, height; + + /* screenBits is not available from a DA */ + tport = (GrafPtr)NewPtr(sizeof(GrafPort)); + OpenPort(tport); + + width = ((DialogPeek)dp)->window.port.portRect.right; + height = ((DialogPeek)dp)->window.port.portRect.bottom; + + bounds.left = ((tport->portRect.right - tport->portRect.left) / 2) - + (width / 2); + bounds.top = ((tport->portRect.bottom - tport->portRect.top) / 2) - + (height / 2) + (titlebar ? GetMBarHeight() : 0); + bounds.right = bounds.left + width; + bounds.bottom = bounds.top + height; + + ClosePort(tport); + + MoveWindow(dp, bounds.left, bounds.top, false); +} + + +/* C Extensions */ + +/* + * Appends src to string dst of size dsize (unlike strncat, dsize is the + * full size of dst, not space left). At most dsize-1 characters + * will be copied. Always NUL terminates (unless dsize <= strlen(dst)). + * Returns strlen(src) + MIN(dsize, strlen(initial dst)). + * If retval >= dsize, truncation occurred. + */ +size_t +strlcat(char *dst, const char *src, size_t dsize) +{ + const char *odst = dst; + const char *osrc = src; + size_t n = dsize; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end. */ + while (n-- != 0 && *dst != '\0') + dst++; + dlen = dst - odst; + n = dsize - dlen; + + if (n-- == 0) + return(dlen + strlen(src)); + while (*src != '\0') { + if (n != 0) { + *dst++ = *src; + n--; + } + src++; + } + *dst = '\0'; + + return(dlen + (src - osrc)); /* count does not include NUL */ +} + +/* + * Copy string src to buffer dst of size dsize. At most dsize-1 + * chars will be copied. Always NUL terminates (unless dsize == 0). + * Returns strlen(src); if retval >= dsize, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t dsize) +{ + const char *osrc = src; + size_t nleft = dsize; + + /* Copy as many bytes as will fit. */ + if (nleft != 0) { + while (--nleft != 0) { + if ((*dst++ = *src++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src. */ + if (nleft == 0) { + if (dsize != 0) + *dst = '\0'; /* NUL-terminate dst */ + while (*src++) + ; + } + + return(src - osrc - 1); /* count does not include NUL */ +} --- scsi.c Fri Jan 1 06:29:17 1904 +++ scsi.c Fri Jan 1 06:29:17 1904 @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2023 joshua stein <jcs@jcs.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <SCSI.h> +#include <string.h> +#include <stdio.h> +#include "wi-fi.h" + +struct scsi_inquiry { + unsigned char deviceType; + unsigned char deviceQualifier; + unsigned char version; + unsigned char responseFormat; + unsigned char additionalLength; + unsigned char vendor; + short reserved; + unsigned char vendorID[8]; + unsigned char productID[16]; + unsigned char revision[4]; + unsigned char vendor2[20]; + unsigned char reserved2[42]; + unsigned char vendor3[158]; +}; + +enum { + SCSI_READ, + SCSI_WRITE +}; + +unsigned char scsi_data[2000]; +struct SCSIInstr tib[2]; +short stat, msg, err; + +short scsi_io(unsigned short scsi_id, unsigned char *cdb, unsigned long cdb_len, + short io_type, unsigned long len); + +short +scsi_find_wifi(void) +{ + struct scsi_inquiry inq; + unsigned char cdb[16] = { 0 }; + short i, scsi_id; + + cdb[0] = 0x12; /* inquiry */ + cdb[4] = 5; /* length */ + + tib[0].scOpcode = scNoInc; + tib[0].scParam1 = (unsigned long)&inq; + tib[0].scParam2 = 5; + tib[1].scOpcode = scStop; + tib[1].scParam1 = 0; + tib[1].scParam2 = 0; + + SCSIReset(); + + for (scsi_id = 0; scsi_id <= 7; scsi_id++) { + for (i = 0; i <= 1; i++) { + if (SCSIGet() != noErr) + return -1; + + if (SCSISelect(scsi_id) != noErr) + break; + + if (SCSICmd((Ptr)&cdb, 6) != noErr) { + SCSIComplete(&stat, &msg, 300); + return -1; + } + + if (SCSIRead((Ptr)&tib) != noErr) { + SCSIComplete(&stat, &msg, 300); + return -1; + } + + if (i == 0) { + cdb[4] += inq.additionalLength; + tib[0].scParam2 += inq.additionalLength; + } + + if (SCSIComplete(&stat, &msg, 300) != noErr) + return -1; + + if (memcmp(inq.vendorID, "Dayna", 5) == 0 && + memcmp(inq.productID, "SCSI/Link", 9) == 0) + return scsi_id; + } + } + + return -1; +} + +short +scsi_wifi_scan(short scsi_id) +{ + unsigned char cdb[6]; + + memset(wifi_scan_networks, 0, sizeof(wifi_scan_networks)); + + memset(cdb, 0, sizeof(cdb)); + cdb[0] = BLUESCSI_NETWORK_WIFI_CMD; + cdb[2] = BLUESCSI_NETWORK_WIFI_CMD_SCAN; + cdb[4] = 0x01; + + scsi_io(scsi_id, cdb, sizeof(cdb), SCSI_READ, 1); + + return 1; +} + +bool +scsi_wifi_scan_finished(short scsi_id) +{ + unsigned char cdb[6]; + + memset(cdb, 0, sizeof(cdb)); + cdb[0] = BLUESCSI_NETWORK_WIFI_CMD; + cdb[2] = BLUESCSI_NETWORK_WIFI_CMD_COMPLETE; + cdb[4] = 0x01; + + scsi_io(scsi_id, cdb, sizeof(cdb), SCSI_READ, 1); + + if (scsi_data[0] == 1) + return true; + + return false; +} + +short +scsi_wifi_scan_results(short scsi_id, struct wifi_network_entry *resp, + short count) +{ + unsigned char cdb[6]; + size_t size, net_count; + + memset(cdb, 0, sizeof(cdb)); + cdb[0] = BLUESCSI_NETWORK_WIFI_CMD; + cdb[2] = BLUESCSI_NETWORK_WIFI_CMD_SCAN_RESULTS; + cdb[3] = (sizeof(scsi_data) >> 8) & 0xff; + cdb[4] = sizeof(scsi_data) & 0xff; + + scsi_io(scsi_id, cdb, sizeof(cdb), SCSI_READ, sizeof(scsi_data)); + + size = (scsi_data[0] << 8) | scsi_data[1]; + net_count = size / sizeof(struct wifi_network_entry); + if (net_count > count) + net_count = count; + + memset(resp, 0, sizeof(struct wifi_network_entry) * count); + memcpy(resp, scsi_data + 2, sizeof(struct wifi_network_entry) * count); + + return net_count; +} + +short +scsi_wifi_info(short scsi_id, struct wifi_network_entry *resp) +{ + static struct wifi_network_entry wifi_info; + unsigned char cdb[6]; + short size; + + memset(cdb, 0, sizeof(cdb)); + cdb[0] = BLUESCSI_NETWORK_WIFI_CMD; + cdb[2] = BLUESCSI_NETWORK_WIFI_CMD_INFO; + + size = sizeof(struct wifi_network_entry) + 2; + cdb[3] = (size >> 8) & 0xff; + cdb[4] = size & 0xff; + + scsi_io(scsi_id, cdb, sizeof(cdb), SCSI_READ, size); + + memset(resp, 0, sizeof(struct wifi_network_entry)); + /* skip returned size */ + memcpy(resp, scsi_data + 2, sizeof(struct wifi_network_entry)); + + return 1; +} + +short +scsi_wifi_join(short scsi_id, struct wifi_join_request *wjr) +{ + unsigned char cdb[6]; + unsigned short size; + unsigned char n; + + memset(cdb, 0, sizeof(cdb)); + cdb[0] = BLUESCSI_NETWORK_WIFI_CMD; + cdb[2] = BLUESCSI_NETWORK_WIFI_CMD_JOIN; + + size = sizeof(struct wifi_join_request); + cdb[3] = (size >> 8) & 0xff; + cdb[4] = size & 0xff; + + memcpy(scsi_data, wjr, sizeof(struct wifi_join_request)); + + scsi_io(scsi_id, cdb, sizeof(cdb), SCSI_WRITE, size); + + return 1; +} + +short +scsi_io(unsigned short scsi_id, unsigned char *cdb, unsigned long cdb_len, + short io_type, unsigned long len) +{ + short ret, ioret, stat; + + if (SCSIGet() != noErr) { + warn("SCSIGet failed"); + return -1; + } + + if (SCSISelect(scsi_id) != noErr) { + warn("SCSISelect failed"); + return -1; + } + + if (SCSICmd((Ptr)cdb, cdb_len) != noErr) { + SCSIComplete(&stat, &msg, 300); + warn("SCSICmd failed"); + return -1; + } + + if (len) { + switch (io_type) { + case SCSI_READ: + memset(scsi_data, 0, sizeof(scsi_data)); + + tib[0].scOpcode = scNoInc; + tib[0].scParam1 = (long)&scsi_data; + tib[0].scParam2 = len; + tib[1].scOpcode = scStop; + tib[1].scParam1 = 0; + tib[1].scParam2 = 0; + + ioret = SCSIRead((Ptr)&tib); + break; + case SCSI_WRITE: + tib[0].scOpcode = scNoInc; + tib[0].scParam1 = (long)&scsi_data; + tib[0].scParam2 = len; + tib[1].scOpcode = scStop; + tib[1].scParam1 = 0; + tib[1].scParam2 = 0; + + ioret = SCSIWrite((Ptr)&tib); + break; + } + } else { + tib[0].scOpcode = scStop; + tib[0].scParam1 = 0; + tib[0].scParam2 = 0; + + ioret = noErr; + } + + /* complete and free the bus before responding to the read/write */ + if ((ret = SCSIComplete(&stat, &msg, 300 /* 1/60 ticks */)) != noErr) { + warn("SCSIComplete failed"); + return -1; + } + + if (ioret != noErr && ioret != scPhaseErr) + warn("SCSIRead/Write failed"); + + if (stat != noErr) + warn("SCSIComplete bad status"); + + return len; +} --- wi-fi.h Tue Aug 22 23:09:56 2023 +++ wi-fi.h Tue Aug 22 23:09:56 2023 @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2023 joshua stein <jcs@jcs.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WIFI_H__ +#define __WIFI_H__ + +#include <stdlib.h> + +#define MAIN_MENU_ID 1 +#define MAIN_MENU_ABOUT_ID 1 +#define MAIN_MENU_QUIT_ID 3 + +#define SSID_MENU_ID 2 + +#define ERROR_DIALOG_ID 0 + +#define PASSWORD_DIALOG_ID 1 +#define PASSWORD_DIALOG_PASSWORD_FIELD_ID 3 + +#define ABOUT_DIALOG_ID 2 + +#define BLUESCSI_NETWORK_WIFI_CMD 0x1c +#define BLUESCSI_NETWORK_WIFI_CMD_SCAN 0x01 +#define BLUESCSI_NETWORK_WIFI_CMD_COMPLETE 0x02 +#define BLUESCSI_NETWORK_WIFI_CMD_SCAN_RESULTS 0x03 +#define BLUESCSI_NETWORK_WIFI_CMD_INFO 0x04 +#define BLUESCSI_NETWORK_WIFI_CMD_JOIN 0x05 + +#define hiword(x) (((short *)&(x))[0]) +#define loword(x) (((short *)&(x))[1]) + +#define nitems(what) (sizeof((what)) / sizeof((what)[0])) +#define member_size(type, member) sizeof(((type *)0)->member) + +typedef Boolean bool; +typedef signed long ssize_t; + +typedef short SICN[16]; +typedef SICN **SICNHandle; + +enum state { + STATE_CLOSED, + STATE_INIT, + STATE_FINDING, + STATE_SCANNING, + STATE_IDLE +}; +extern short da_state; + +enum signal_icon_id { + SIGNAL_NO_DEVICE = 0, + SIGNAL_FINDING_DEVICE = 1, + SIGNAL_NONE = 2, + SIGNAL_1 = 3, + SIGNAL_2 = 4, + SIGNAL_3 = 5, + SIGNAL_4 = 6 +}; + +struct wifi_network_entry { + char ssid[64]; + unsigned char bssid[6]; + char rssi; + unsigned char channel; + char flags; + char _padding; +}; + +struct wifi_join_request { + char ssid[64]; + char key[64]; + unsigned char channel; + unsigned char _padding; +}; + +extern WindowPtr win; + +extern short wifi_scsi_id; +enum { + WIFI_SCSI_ID_FINDING = -1, + WIFI_SCSI_ID_NONE = -2 +}; +extern struct wifi_network_entry wifi_cur_info; +extern struct wifi_network_entry wifi_scan_networks[10]; +extern Cursor watch; + +void about(void); + +size_t strlcat(char *dst, const char *src, size_t dsize); +size_t strlcpy(char *dst, const char *src, size_t dsize); +char * strndup(const char *str, size_t maxlen); + +WindowPtr create_window(short ctlrefnum); +void update_window(bool force); +void update_wifi_cur_info(void); +void update_wifi_ssid_list(void); +void destroy_window(void); +void activate_window(bool activate); +long OwnedResourceID(long n); +void warn(char *format, ...); +void CenterDialog(DialogPtr dp, bool titlebar); +void window_mousedown(Point p); + +short scsi_find_wifi(void); +short scsi_wifi_scan(short lun); +bool scsi_wifi_scan_finished(short lun); +short scsi_wifi_info(short lun, struct wifi_network_entry *resp); +short scsi_wifi_scan_results(short scsi_id, + struct wifi_network_entry *resp, short count); +short scsi_wifi_join(short scsi_id, struct wifi_join_request *wjr); + +#endif --- Wi-Fi DA.π.r Tue Aug 22 23:33:58 2023 +++ Wi-Fi DA.π.r Tue Aug 22 23:33:58 2023 @@ -0,0 +1,123 @@ +data 'ALRT' (-16000) { + $"0046 0092 00AB 0175 C180 5555" /* .F.í.´.u¡ÄUU */ +}; + +data 'DITL' (-16000) { + $"0001 0000 0000 0046 0050 005A 008C 0402" /* .......F.P.Z.å.. */ + $"4F4B 0000 0000 0009 000C 003F 00D8 8808" /* OK.....∆...?.ÿà. */ + $"5E30 5E31 5E32 5E33" /* ^0^1^2^3 */ +}; + +data 'DITL' (-15999) { + $"0004 0000 0000 0050 0136 0064 017F 0407" /* .......P.6.d.... */ + $"436F 6E6E 6563 745B 0000 0000 0050 00E6" /* Connect[.....P.. */ + $"0064 012F 0406 4361 6E63 656C 0000 0000" /* .d./..Cancel.... */ + $"0032 005A 0042 0118 1000 0000 0000 0032" /* .2.Z.B.........2 */ + $"000A 0042 0055 8809 5061 7373 776F 7264" /* ...B.Uà∆Password */ + $"3A72 0000 0000 000A 000A 0029 0182 882B" /* :r.........).Çà+ */ + $"5468 6520 5769 2D46 6920 6E65 7477 6F72" /* The Wi-Fi networ */ + $"6B20 225E 3022 2072 6571 7569 7265 7320" /* k "^0" requires */ + $"6120 7061 7373 776F 7264 2E00" /* a password.. */ +}; + +data 'DITL' (-15998) { + $"0001 0000 0000 0078 0064 008C 00A0 0402" /* .......x.d.å.†.. */ + $"4F4B 0000 0000 000A 000A 0071 00F0 880D" /* OK.........q..ଠ*/ + $"5E30 0D5E 310D 0D5E 320D 0D5E 3300" /* ^0¬^1¬¬^2¬¬^3. */ +}; + +data 'MENU' (-15998) { + $"C182 0000 0000 0000 0000 FFFF FFFF 0553" /* ¡Ç.............S */ + $"5349 4473 0B53 6361 6E6E 696E 672E 2E2E" /* SIDs.Scanning... */ + $"0000 0000 00" /* ..... */ +}; + +data 'MENU' (-15999) { + $"C181 0000 0000 0000 0000 FFFF FFFF 0557" /* ¡Å.............W */ + $"692D 4669 0E41 626F 7574 2057 692D 4669" /* i-Fi.About Wi-Fi */ + $"2E2E 2E00 0000 0000" /* ........ */ +}; + +data 'DLOG' (-15999) { + $"0066 0036 00D6 01C4 0010 0000 0100 0000" /* .f.6.÷.ƒ........ */ + $"0000 C181 0E57 692D 4669 2050 6173 7377" /* ..¡Å.Wi-Fi Passw */ + $"6F72 641C 680A" /* ord.h. */ +}; + +data 'DLOG' (-16000) { + $"002A 00A8 0064 0162 0001 0100 0000 0000" /* .*.®.d.b........ */ + $"0000 C182 0000 280A" /* ..¡Ç..(. */ +}; + +data 'DLOG' (-15998) { + $"0064 00A2 00FC 019D 0001 0000 0000 0000" /* .d.¢...ù........ */ + $"0000 C182 0000 280A" /* ..¡Ç..(. */ +}; + +data 'SICN' (-16000) { + $"0000 6006 700E 3FFC 1FF8 3E7C 67E6 4FF2" /* ..`.p.?...>|g.O. */ + $"1BD8 17E8 0FF0 1C38 399C 718E 6006 0000" /* .ÿ.....89úqé`... */ + $"0000 0FC0 1860 3010 67C8 482C 438C 444C" /* ...¿.`0.g»H,CåDL */ + $"410C 2018 1878 0FFC 07FE 001F 000F 0006" /* A. ..x.......... */ + $"0000 0000 0000 0550 0AA8 1004 22A2 4550" /* .......P.®.."¢EP */ + $"0808 1140 02A0 0400 0080 0100 0000 0000" /* ...@.†...Ä...... */ + $"0000 0000 0000 0550 0AA8 1004 22A2 4550" /* .......P.®.."¢EP */ + $"0808 1140 02A0 0400 0180 0180 0000 0000" /* ...@.†...Ä.Ä.... */ + $"0000 0000 0000 0550 0AA8 1004 22A2 4550" /* .......P.®.."¢EP */ + $"0808 13C0 07E0 0420 0180 0180 0000 0000" /* ...¿... .Ä.Ä.... */ + $"0000 0000 0000 0550 0AA8 1004 27E2 4FF0" /* .......P.®..'.O. */ + $"1818 13C8 07E0 0420 0180 0180 0000 0000" /* ...»... .Ä.Ä.... */ + $"0000 0000 0000 0FF0 1FF8 300C 67E6 4FF2" /* ..........0.g.O. */ + $"1818 13C8 07E0 0420 0180 0180 0000 0000" /* ...»... .Ä.Ä.... */ +}; + +data 'ICN#' (-16000) { + $"0000 0000 0000 0000 0000 0000 0000 0000" /* ................ */ + $"0000 0000 0000 0000 0000 0000 0000 0000" /* ................ */ + $"0000 0000 003F F800 007F FC00 01E0 0F00" /* .....?.......... */ + $"0380 0380 070F E1C0 063F F8C0 0078 3C00" /* .Ä.Ä...¿.?.¿.x<. */ + $"00E0 0E00 00C7 C600 000F E000 001C 7000" /* .....«∆.......p. */ + $"0018 3000 0001 0000 0003 8000 0001 0000" /* ..0.......Ä..... */ + $"0000 0000 0000 0000 0000 0000 0000 0000" /* ................ */ + $"0000 0000 0000 0000 0000 0000 0000 0000" /* ................ */ + $"0000 0000 0000 0000 FFFF FFFE FFFF FFFF" /* ................ */ + $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" /* ................ */ + $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" /* ................ */ + $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" /* ................ */ + $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" /* ................ */ + $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" /* ................ */ + $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" /* ................ */ + $"7FFF FFFF 0000 0000 0000 0000 0000 0000" /* ................ */ +}; + +data 'BNDL' (-16000) { + $"7769 6669 0000 0001 4652 4546 0000 0000" /* wifi....FREF.... */ + $"0080 4943 4E23 0000 0000 C180" /* .ÄICN#....¡Ä */ +}; + +data 'vers' (1) { + $"0100 8000 0000 0331 2E30 2631 2E30 20A9" /* ..Ä....1.0&1.0 © */ + $"2032 3032 332C 206A 6F73 6875 6120 7374" /* 2023, joshua st */ + $"6569 6E20 3C6A 6373 406A 6373 2E6F 7267" /* ein <jcs@jcs.org */ + $"3E" /* > */ +}; + +data 'ics#' (-16000) { + $"0000 0000 0000 0FF0 1FF8 300C 67E6 4FF2" /* ..........0.g.O. */ + $"1818 13C8 07E0 0420 0180 0180 0000 0000" /* ...»... .Ä.Ä.... */ + $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" /* ................ */ + $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" /* ................ */ +}; + +data 'wifi' (0, "Owner resource") { + $"00" /* . */ +}; + +data 'FREF' (-16000) { + $"6466 696C 0000 00" /* dfil... */ +}; + +data 'FREF' (128) { + $"6466 696C 0000 00" /* dfil... */ +}; + --- window.c Tue Aug 22 23:18:00 2023 +++ window.c Tue Aug 22 23:18:00 2023 @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2023 joshua stein <jcs@jcs.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <string.h> +#include "wi-fi.h" + +#define NO_DEVICE_TEXT "\pNo Device Found" +#define NO_NETWORK_TEXT "(No Network)" + +SICNHandle signal_icons; +ControlHandle ssid_list; +MenuHandle ssid_menu; +Rect ssid_menu_rect; + +WindowPtr +create_window(short ctlrefnum) +{ + GrafPtr savePort; + Rect bounds = { 50, 320, 75, 500 }; /* tlbr */ + + GetPort(&savePort); + + win = NewWindow(0L, &bounds, "\pWi-Fi", true, rDocProc, 0L, true, 0L); + ((WindowPeek)win)->windowKind = ctlrefnum; + + signal_icons = (SICNHandle)GetResource('SICN', OwnedResourceID(0)); + if (signal_icons == NULL) + warn("Can't find SICN"); + + ssid_menu = GetMenu(OwnedResourceID(SSID_MENU_ID)); + SetItem(ssid_menu, 1, "\pFinding Wi-Fi..."); + + SetRect(&ssid_menu_rect, 30, 3, 170, 21); /* ltrb */ + +#if 0 /* system 6 does not seem to support popupMenuProc */ + ssid_list = NewControl(win, &ssid_menu_rect, 0, true, 0, + OwnedResourceID(SSID_MENU_ID), 0, popupMenuProc /* + popupFixedWidth*/, 0L); +#endif + + update_window(true); + + if (wifi_scsi_id == WIFI_SCSI_ID_FINDING) + wifi_scsi_id = scsi_find_wifi(); + + if (wifi_scsi_id == WIFI_SCSI_ID_NONE) { + da_state = STATE_IDLE; + SetItem(ssid_menu, 1, NO_DEVICE_TEXT); + update_window(true); + } else { + scsi_wifi_info(wifi_scsi_id, &wifi_cur_info); + da_state = STATE_SCANNING; + scsi_wifi_scan(wifi_scsi_id); + } + + SetPort(savePort); + + return win; +} + +void +update_window(bool force) +{ + Str255 menuText; + GrafPtr savePort; + Point p; + short i, icon; + RgnHandle tmpRgn; + Rect r, destRect; + BitMap bm; + + GetPort(&savePort); + SetPort(win); + + if (!force) + BeginUpdate(win); + + EraseRect(&win->portRect); + DrawControls(win); + + /* system 6 does not seem to support popupMenuProc, fake it */ + FrameRect(&ssid_menu_rect); + MoveTo(ssid_menu_rect.left + 1, ssid_menu_rect.bottom); + LineTo(ssid_menu_rect.right, ssid_menu_rect.bottom); + MoveTo(ssid_menu_rect.right, ssid_menu_rect.top + 1); + LineTo(ssid_menu_rect.right, ssid_menu_rect.bottom); + MoveTo(ssid_menu_rect.left + 15, 16); + TextFont(systemFont); + GetItem(ssid_menu, 1, &menuText); + DrawString(menuText); + + HLock(signal_icons); + + SetRect(&destRect, 6, 4, 22, 20); /* ltrb */ + + if (wifi_scsi_id == WIFI_SCSI_ID_NONE) + icon = SIGNAL_NO_DEVICE; + else if (wifi_scsi_id == WIFI_SCSI_ID_FINDING) + icon = SIGNAL_FINDING_DEVICE; + else if (wifi_cur_info.ssid[0] == '\0') + icon = SIGNAL_NONE; + else if (wifi_cur_info.rssi >= -60) + icon = SIGNAL_4; + else if (wifi_cur_info.rssi >= -70) + icon = SIGNAL_3; + else if (wifi_cur_info.rssi >= -75) + icon = SIGNAL_2; + else + icon = SIGNAL_1; + + bm.baseAddr = (Ptr)(*signal_icons)[icon]; + bm.rowBytes = 2; + SetRect(&bm.bounds, 0, 0, 16, 16); + CopyBits(&bm, &win->portBits, &bm.bounds, &destRect, srcOr, NULL); + + HUnlock(signal_icons); + + if (!force) + EndUpdate(win); + + ValidRect(&win->portRect); + SetPort(savePort); +} + +void +update_wifi_cur_info(void) +{ + struct wifi_network_entry old_info; + + if (wifi_scsi_id == WIFI_SCSI_ID_NONE) + return; + + memcpy(&old_info, &wifi_cur_info, sizeof(old_info)); + + scsi_wifi_info(wifi_scsi_id, &wifi_cur_info); + + if (memcmp(&old_info, &wifi_cur_info, sizeof(old_info)) != 0) + update_window(true); +} + +void +update_wifi_ssid_list(void) +{ + struct wifi_network_entry tmpnets[10] = { 0 }; + char ssid[64] = { 0 }; + short count, n, m, mitem; + + if (wifi_scsi_id == WIFI_SCSI_ID_NONE) + return; + + count = scsi_wifi_scan_results(wifi_scsi_id, wifi_scan_networks, + nitems(wifi_scan_networks)); + if (count == 0) { + memcpy(&wifi_scan_networks, &wifi_cur_info, + sizeof(wifi_cur_info)); + count = 1; + } else { + memcpy(&tmpnets[0], &wifi_cur_info, sizeof(wifi_cur_info)); + + for (n = 0, m = 1; n < count; n++) { + if (strcmp(wifi_scan_networks[n].ssid, + wifi_cur_info.ssid) == 0) + continue; + + memcpy(&tmpnets[m++], &wifi_scan_networks[n], + sizeof(tmpnets[0])); + } + + memcpy(&wifi_scan_networks, &tmpnets, + sizeof(wifi_scan_networks)); + } + + if (wifi_scan_networks[0].ssid[0] == '\0') + strlcpy(wifi_scan_networks[0].ssid, NO_NETWORK_TEXT, + sizeof(wifi_scan_networks[0].ssid)); + + /* leave the first item and we'll set its text later */ + while (CountMItems(ssid_menu) > 1) + DelMenuItem(ssid_menu, 2); + + for (n = 0, mitem = 1; n < count; n++) { + /* + * InsMenuItem supports meta chars in item text which can + * have side effects, so insert a blank item and then set its + * name with SetItem which does not suport meta chars. + */ + strlcpy(ssid, wifi_scan_networks[n].ssid, sizeof(ssid)); + CtoPstr(ssid); + + if (n > 0) + InsMenuItem(ssid_menu, ssid, mitem); + + SetItem(ssid_menu, mitem, ssid); + + CheckItem(ssid_menu, mitem, + strcmp(wifi_scan_networks[n].ssid, wifi_cur_info.ssid) == 0); + mitem++; + } + + update_window(true); +} + +void +activate_window(bool activate) +{ + Rect r; + GrafPtr savePort; + + GetPort(&savePort); + SetPort(win); + r = win->portRect; + r.top = r.bottom - 16; + r.left = r.left - 16; + InvalRect(&r); + SetPort(savePort); +} + +void +destroy_window(void) +{ + ReleaseResource(signal_icons); + + if (!win) + return; + + DisposeWindow(win); + win = 0; +} + +void +window_mousedown(Point p) +{ + Str255 pass; + char ssid[64]; + Rect r, irect; + GrafPtr savePort; + short mitems, n, selitem, hit, itype; + long new_net; + struct wifi_network_entry *net; + struct wifi_join_request wjr; + DialogPtr dg; + EventRecord e; + ControlHandle ihandle; + + GetPort(&savePort); + SetPort(win); + + r = ssid_menu_rect; + LocalToGlobal(&r); + + /* XXX: why does PtInRect not work here? */ + if (!(p.h >= r.left && p.h <= r.left + r.right && + p.v >= r.top && p.v <= r.top + r.bottom)) { + SetPort(savePort); + return; + } + + if (wifi_scsi_id == WIFI_SCSI_ID_NONE) + return; + + InsertMenu(ssid_menu, -1); + + mitems = CountMItems(ssid_menu); + selitem = 1; + for (n = 0; n < mitems; n++) { + if (strcmp(wifi_scan_networks[n].ssid, wifi_cur_info.ssid) == 0) { + selitem = n + 1; + break; + } + } + + new_net = PopUpMenuSelect(ssid_menu, r.top + 1, r.left + 1, selitem); + + DeleteMenu((*ssid_menu)->menuID); + + if (hiword(new_net) != 0 && loword(new_net) != selitem) { + net = &wifi_scan_networks[loword(new_net) - 1]; + strlcpy(ssid, net->ssid, sizeof(ssid)); + + CtoPstr(ssid); + ParamText(ssid, "", "", ""); + PtoCstr(ssid); + + dg = GetNewDialog(OwnedResourceID(PASSWORD_DIALOG_ID), 0L, + (WindowPtr)-1L); + CenterDialog(dg, false); + ShowWindow(dg); + for (;;) { + ModalDialog(0, &hit); + if (hit == ok || hit == cancel) + break; + } + + if (hit == ok) { + GetDItem(dg, PASSWORD_DIALOG_PASSWORD_FIELD_ID, &itype, + &ihandle, &irect); + GetIText(ihandle, &pass); + PtoCstr(pass); + + memset(&wjr, 0, sizeof(wjr)); + strlcpy(wjr.ssid, ssid, sizeof(wjr.ssid)); + strlcpy(wjr.key, (char *)pass, sizeof(wjr.key)); + + DisposDialog(dg); + + scsi_wifi_join(wifi_scsi_id, &wjr); + } else + DisposDialog(dg); + } + + SetPort(savePort); +}