/* * Copyright (c) 2023 joshua stein * * 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 #include #include #include "wi-fi.h" /* device control entry */ DCtlPtr dce = NULL; SysEnvRec world; #define ON_SYSTEM_7 (world.systemVersion >= 0x0700) short da_state = STATE_CLOSED; short wifi_scsi_id = WIFI_SCSI_ID_FINDING; unsigned long wifi_scan_started = 0; struct wifi_network_entry wifi_cur_info = { 0 }; struct wifi_network_entry wifi_scan_networks[10] = { 0 }; MenuHandle menu, apple_menu; short menuID = 0; WindowPtr win = 0L; Cursor watch; short logger_y = 12; WindowPtr logger = 0; 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); #ifdef DEBUG_LOGGING void logger_vprintf(const char *format, va_list ap); #endif short main(cntrlParam *p, DCtlPtr d, short n) { if (d->dCtlStorage == 0) { /* open */ if (n == 0) { SysBeep(3); CloseDriver(d->dCtlRefNum); } return 0; } if (dce == NULL) { util_init(d); SysEnvirons(1, &world); } 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; Rect bounds; bool dispose; dce->dCtlFlags |= dNeedLock | dNeedTime | dNeedGoodBye; dce->dCtlDelay = 61; #ifdef DEBUG_LOGGING if (!logger) { bounds.top = 150; bounds.bottom = 320; bounds.left = 20; bounds.right = 400; logger = NewWindow(0L, &bounds, "\pLogger", true, rDocProc, 0L, true, 0L); ((WindowPeek)logger)->windowKind = dce->dCtlRefNum; } #endif 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); if (!ON_SYSTEM_7) { 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 accMenu: handle_menu(param[1]); break; case accEvent: handle_event(*((EventRecord **)param)); break; case accRun: handle_timer(); break; case goodbye: break; } } void da_close(void) { if (!ON_SYSTEM_7) { DeleteMenu(dce->dCtlMenu); DrawMenuBar(); DisposeMenu(menu); } dce->dCtlMenu = 0; destroy_window(); if (logger) { DisposeWindow(logger); logger = 0; } dce->dCtlWindow = 0; da_state = STATE_CLOSED; } bool handle_menu(short i) { bool ret = true; switch (i) { case MAIN_MENU_ABOUT_ID: wifi_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; static long last_scan = 0; if (!win) return; switch (da_state) { case STATE_SCANNING: /* check every second */ if ((Ticks - last_update) < (60 * 1)) break; DEBUG_LOG(("%ld - handle_timer - checking for scan completion", Ticks)); last_update = Ticks; if (!scsi_wifi_scan_finished(wifi_scsi_id)) { DEBUG_LOG(("%ld - handle_timer - scan not done", Ticks)); if (Time - wifi_scan_started <= 5) break; DEBUG_LOG(("Wi-Fi scan failed to finish, canceling")); } last_scan = Ticks; da_state = STATE_IDLE; DEBUG_LOG(("%ld - handle_timer - scan done, updating list", Ticks)); update_wifi_ssid_list(true); break; case STATE_IDLE: /* refresh SSID/RSSI every 5 seconds, scan every 30 */ if ((Ticks - last_update) < (60 * 5)) break; last_update = Ticks; if ((Ticks - last_scan) > (60 * 30)) { da_state = STATE_SCANNING; DEBUG_LOG(("%ld - handle_timer - idle, scanning", Ticks)); scsi_wifi_scan(wifi_scsi_id); } else { DEBUG_LOG(("%ld - handle_timer - idle, updating info", Ticks)); update_wifi_cur_info(); } break; } } #ifdef DEBUG_LOGGING void logger_printf(const char *format, ...) { va_list va; if (!logger) return; va_start(va, format); logger_vprintf(format, va); va_end(va); } void logger_vprintf(const char *format, va_list ap) { static char buf[512]; GrafPtr savePort; ssize_t len; len = vsnprintf(buf, sizeof(buf), format, ap); GetPort(&savePort); SetPort(logger); if (logger_y >= (logger->portRect.bottom - logger->portRect.top)) { EraseRect(&logger->portRect); logger_y = 12; } MoveTo(4, logger_y); TextFont(geneva); TextSize(9); DrawText(buf, 0, len); logger_y += 10; ValidRect(&logger->portRect); SetPort(savePort); } #endif