/* * 86Box A hypervisor and IBM PC system emulator that specializes in * running old operating systems and software designed for IBM * PC systems and compatibles from 1981 through fairly recent * system designs based on the PCI bus. * * This file is part of the 86Box distribution. * * Windows device configuration dialog implementation. * * * * Authors: Sarah Walker, * Miran Grca, * * Copyright 2008-2018 Sarah Walker. * Copyright 2016-2018 Miran Grca. */ #include #include #include #include #include #include <86box/86box.h> #include <86box/ini.h> #include <86box/config.h> #include <86box/device.h> #include <86box/plat.h> #include <86box/mem.h> #include <86box/rom.h> #include <86box/midi_rtmidi.h> #include <86box/ui.h> #include <86box/win.h> #include static device_context_t config_device; static uint8_t deviceconfig_changed = 0; static int combo_to_struct[256]; #if defined(__amd64__) || defined(__aarch64__) static LRESULT CALLBACK #else static BOOL CALLBACK #endif deviceconfig_dlgproc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) { HWND h; int val_int; int id; int c; int d; int p; int q; #ifdef USE_RTMIDI int num; #endif int changed; int cid; const device_config_t *config; const device_config_selection_t *selection; const device_config_bios_t *bios; char s[512]; char file_filter[512]; char *str; char *val_str; wchar_t ws[512]; wchar_t *wstr; LPTSTR lptsTemp; config = config_device.dev->config; switch (message) { case WM_INITDIALOG: id = IDC_CONFIG_BASE; config = config_device.dev->config; lptsTemp = (LPTSTR) malloc(512); memset(combo_to_struct, 0, 256 * sizeof(int)); while (config->type != -1) { selection = config->selection; bios = config->bios; h = GetDlgItem(hdlg, id); switch (config->type) { case CONFIG_BINARY: val_int = config_get_int((char *) config_device.name, (char *) config->name, config->default_int); SendMessage(h, BM_SETCHECK, val_int, 0); id++; break; case CONFIG_SELECTION: val_int = config_get_int((char *) config_device.name, (char *) config->name, config->default_int); c = 0; while (selection && selection->description && selection->description[0]) { mbstowcs(lptsTemp, selection->description, strlen(selection->description) + 1); SendMessage(h, CB_ADDSTRING, 0, (LPARAM) (LPCSTR) lptsTemp); if (val_int == selection->value) SendMessage(h, CB_SETCURSEL, c, 0); selection++; c++; } id += 2; break; case CONFIG_BIOS: val_str = config_get_string((char *) config_device.name, (char *) config->name, (char *) config->default_string); c = 0; q = 0; while (bios && (bios->files_no > 0)) { mbstowcs(lptsTemp, bios->name, strlen(bios->name) + 1); p = 0; for (d = 0; d < bios->files_no; d++) p += !!rom_present((char *) bios->files[d]); if (p == bios->files_no) { SendMessage(h, CB_ADDSTRING, 0, (LPARAM) (LPCSTR) lptsTemp); if (!strcmp(val_str, bios->internal_name)) SendMessage(h, CB_SETCURSEL, c, 0); combo_to_struct[c] = q; c++; } q++; bios++; } id += 2; break; #ifdef USE_RTMIDI case CONFIG_MIDI_OUT: val_int = config_get_int((char *) config_device.name, (char *) config->name, config->default_int); num = rtmidi_out_get_num_devs(); for (c = 0; c < num; c++) { rtmidi_out_get_dev_name(c, s); mbstowcs(lptsTemp, s, strlen(s) + 1); SendMessage(h, CB_ADDSTRING, 0, (LPARAM) (LPCSTR) lptsTemp); if (val_int == c) SendMessage(h, CB_SETCURSEL, c, 0); } id += 2; break; case CONFIG_MIDI_IN: val_int = config_get_int((char *) config_device.name, (char *) config->name, config->default_int); num = rtmidi_in_get_num_devs(); for (c = 0; c < num; c++) { rtmidi_in_get_dev_name(c, s); mbstowcs(lptsTemp, s, strlen(s) + 1); SendMessage(h, CB_ADDSTRING, 0, (LPARAM) (LPCSTR) lptsTemp); if (val_int == c) SendMessage(h, CB_SETCURSEL, c, 0); } id += 2; break; #endif case CONFIG_SPINNER: val_int = config_get_int((char *) config_device.name, (char *) config->name, config->default_int); _swprintf(ws, L"%i", val_int); SendMessage(h, WM_SETTEXT, 0, (LPARAM) ws); id += 2; break; case CONFIG_FNAME: case CONFIG_STRING: wstr = config_get_wstring((char *) config_device.name, (char *) config->name, 0); if (wstr) SendMessage(h, WM_SETTEXT, 0, (LPARAM) wstr); id += 3; break; case CONFIG_HEX16: val_int = config_get_hex16((char *) config_device.name, (char *) config->name, config->default_int); c = 0; while (selection && selection->description && selection->description[0]) { mbstowcs(lptsTemp, selection->description, strlen(selection->description) + 1); SendMessage(h, CB_ADDSTRING, 0, (LPARAM) (LPCSTR) lptsTemp); if (val_int == selection->value) SendMessage(h, CB_SETCURSEL, c, 0); selection++; c++; } id += 2; break; case CONFIG_HEX20: val_int = config_get_hex20((char *) config_device.name, (char *) config->name, config->default_int); c = 0; while (selection && selection->description && selection->description[0]) { mbstowcs(lptsTemp, selection->description, strlen(selection->description) + 1); SendMessage(h, CB_ADDSTRING, 0, (LPARAM) (LPCSTR) lptsTemp); if (val_int == selection->value) SendMessage(h, CB_SETCURSEL, c, 0); selection++; c++; } id += 2; break; } config++; } free(lptsTemp); return TRUE; case WM_COMMAND: cid = LOWORD(wParam); if (cid == IDOK) { id = IDC_CONFIG_BASE; config = config_device.dev->config; changed = 0; char s[512]; while (config->type != -1) { const device_config_selection_t *selection = config->selection; h = GetDlgItem(hdlg, id); switch (config->type) { case CONFIG_BINARY: val_int = config_get_int((char *) config_device.name, (char *) config->name, config->default_int); if (val_int != SendMessage(h, BM_GETCHECK, 0, 0)) changed = 1; id++; break; case CONFIG_SELECTION: val_int = config_get_int((char *) config_device.name, (char *) config->name, config->default_int); c = SendMessage(h, CB_GETCURSEL, 0, 0); for (; c > 0; c--) selection++; if (val_int != selection->value) changed = 1; id += 2; break; case CONFIG_BIOS: bios = config->bios; val_str = config_get_string((char *) config_device.name, (char *) config->name, (char *) config->default_string); c = combo_to_struct[SendMessage(h, CB_GETCURSEL, 0, 0)]; for (; c > 0; c--) bios++; if (strcmp(val_str, bios->internal_name)) changed = 1; id += 2; break; case CONFIG_MIDI_OUT: val_int = config_get_int((char *) config_device.name, (char *) config->name, config->default_int); c = SendMessage(h, CB_GETCURSEL, 0, 0); if (val_int != c) changed = 1; id += 2; break; case CONFIG_MIDI_IN: val_int = config_get_int((char *) config_device.name, (char *) config->name, config->default_int); c = SendMessage(h, CB_GETCURSEL, 0, 0); if (val_int != c) changed = 1; id += 2; break; case CONFIG_FNAME: case CONFIG_STRING: str = config_get_string((char *) config_device.name, (char *) config->name, (char *) ""); SendMessage(h, WM_GETTEXT, 511, (LPARAM) s); if (strcmp(str, s)) changed = 1; id += 3; break; case CONFIG_SPINNER: val_int = config_get_int((char *) config_device.name, (char *) config->name, config->default_int); if (val_int > config->spinner.max) val_int = config->spinner.max; else if (val_int < config->spinner.min) val_int = config->spinner.min; SendMessage(h, WM_GETTEXT, 79, (LPARAM) ws); wcstombs(s, ws, 512); sscanf(s, "%i", &c); if (val_int != c) changed = 1; id += 2; break; case CONFIG_HEX16: val_int = config_get_hex16((char *) config_device.name, (char *) config->name, config->default_int); c = SendMessage(h, CB_GETCURSEL, 0, 0); for (; c > 0; c--) selection++; if (val_int != selection->value) changed = 1; id += 2; break; case CONFIG_HEX20: val_int = config_get_hex20((char *) config_device.name, (char *) config->name, config->default_int); c = SendMessage(h, CB_GETCURSEL, 0, 0); for (; c > 0; c--) selection++; if (val_int != selection->value) changed = 1; id += 2; break; } config++; } if (!changed) { deviceconfig_changed = 0; EndDialog(hdlg, 0); return TRUE; } deviceconfig_changed = 1; id = IDC_CONFIG_BASE; config = config_device.dev->config; while (config->type != -1) { selection = config->selection; h = GetDlgItem(hdlg, id); switch (config->type) { case CONFIG_BINARY: config_set_int((char *) config_device.name, (char *) config->name, SendMessage(h, BM_GETCHECK, 0, 0)); id++; break; case CONFIG_SELECTION: c = SendMessage(h, CB_GETCURSEL, 0, 0); for (; c > 0; c--) selection++; config_set_int((char *) config_device.name, (char *) config->name, selection->value); id += 2; break; case CONFIG_BIOS: bios = config->bios; c = combo_to_struct[SendMessage(h, CB_GETCURSEL, 0, 0)]; for (; c > 0; c--) bios++; config_set_string((char *) config_device.name, (char *) config->name, (char *) bios->internal_name); id += 2; break; case CONFIG_MIDI_OUT: c = SendMessage(h, CB_GETCURSEL, 0, 0); config_set_int((char *) config_device.name, (char *) config->name, c); id += 2; break; case CONFIG_MIDI_IN: c = SendMessage(h, CB_GETCURSEL, 0, 0); config_set_int((char *) config_device.name, (char *) config->name, c); id += 2; break; case CONFIG_FNAME: case CONFIG_STRING: SendMessage(h, WM_GETTEXT, 511, (LPARAM) ws); config_set_wstring((char *) config_device.name, (char *) config->name, ws); id += 3; break; case CONFIG_SPINNER: SendMessage(h, WM_GETTEXT, 79, (LPARAM) ws); wcstombs(s, ws, 512); sscanf(s, "%i", &c); if (c > config->spinner.max) c = config->spinner.max; else if (c < config->spinner.min) c = config->spinner.min; config_set_int((char *) config_device.name, (char *) config->name, c); id += 2; break; case CONFIG_HEX16: c = SendMessage(h, CB_GETCURSEL, 0, 0); for (; c > 0; c--) selection++; config_set_hex16((char *) config_device.name, (char *) config->name, selection->value); id += 2; break; case CONFIG_HEX20: c = SendMessage(h, CB_GETCURSEL, 0, 0); for (; c > 0; c--) selection++; config_set_hex20((char *) config_device.name, (char *) config->name, selection->value); id += 2; break; } config++; } EndDialog(hdlg, 0); return TRUE; } else if (cid == IDCANCEL) { deviceconfig_changed = 0; EndDialog(hdlg, 0); return TRUE; } else { id = IDC_CONFIG_BASE; while (config->type != -1) { switch (config->type) { case CONFIG_BINARY: id++; break; case CONFIG_SELECTION: case CONFIG_HEX16: case CONFIG_HEX20: case CONFIG_BIOS: case CONFIG_MIDI_OUT: case CONFIG_MIDI_IN: case CONFIG_SPINNER: case CONFIG_STRING: id += 2; break; case CONFIG_FNAME: if (cid == id + 1) { s[0] = 0; h = GetDlgItem(hdlg, id); SendMessage(h, WM_GETTEXT, 511, (LPARAM) s); file_filter[0] = 0; strcat(file_filter, config->file_filter); strcat(file_filter, "|All files (*.*)|*.*|"); mbstowcs(ws, file_filter, strlen(file_filter) + 1); d = strlen(file_filter); /* replace | with \0 */ for (c = 0; c < d; ++c) { if (ws[c] == L'|') ws[c] = 0; } if (!file_dlg(hdlg, ws, s, NULL, 0)) SendMessage(h, WM_SETTEXT, 0, (LPARAM) wopenfilestring); } break; } config++; } } break; } return FALSE; } uint8_t deviceconfig_inst_open(HWND hwnd, const device_t *device, int inst) { const device_config_t *config = device->config; uint16_t *data_block; uint16_t *data; DLGTEMPLATE *dlg; DLGITEMTEMPLATE *item; data_block = malloc(16384); dlg = (DLGTEMPLATE *) data_block; int y = 10; int id = IDC_CONFIG_BASE; deviceconfig_changed = 0; memset(data_block, 0, 16384); dlg->style = DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU; dlg->x = 10; dlg->y = 10; dlg->cx = 220; dlg->cy = 70; data = (uint16_t *) (dlg + 1); *data++ = 0; /*no menu*/ *data++ = 0; /*predefined dialog box class*/ data += wsprintf(data, plat_get_string(IDS_2142), device->name) + 1; *data++ = 9; /*Point*/ data += MultiByteToWideChar(CP_ACP, 0, "Segoe UI", -1, data, 120); if (((uintptr_t) data) & 2) data++; while (config->type != -1) { switch (config->type) { case CONFIG_BINARY: item = (DLGITEMTEMPLATE *) data; item->x = 10; item->y = y; item->id = id++; item->cx = 100; item->cy = 15; item->style = WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX; data = (uint16_t *) (item + 1); *data++ = 0xFFFF; *data++ = 0x0080; /* button class */ data += MultiByteToWideChar(CP_ACP, 0, config->description, -1, data, 256); *data++ = 0; /* no creation data */ y += 20; break; case CONFIG_SELECTION: case CONFIG_MIDI_OUT: case CONFIG_MIDI_IN: case CONFIG_HEX16: case CONFIG_HEX20: case CONFIG_BIOS: /*Combo box*/ item = (DLGITEMTEMPLATE *) data; item->x = 70; item->y = y; item->id = id++; item->cx = 140; item->cy = 150; item->style = WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | WS_VSCROLL; data = (uint16_t *) (item + 1); *data++ = 0xFFFF; *data++ = 0x0085; /* combo box class */ data += MultiByteToWideChar(CP_ACP, 0, config->description, -1, data, 256); *data++ = 0; /* no creation data */ if (((uintptr_t) data) & 2) data++; /*Static text*/ item = (DLGITEMTEMPLATE *) data; item->x = 10; item->y = y + 2; item->id = id++; item->cx = 60; item->cy = 20; item->style = WS_CHILD | WS_VISIBLE; data = (uint16_t *) (item + 1); *data++ = 0xFFFF; *data++ = 0x0082; /* static class */ data += MultiByteToWideChar(CP_ACP, 0, config->description, -1, data, 256); *data++ = 0; /* no creation data */ if (((uintptr_t) data) & 2) data++; y += 20; break; case CONFIG_SPINNER: /*Spinner*/ item = (DLGITEMTEMPLATE *) data; item->x = 70; item->y = y; item->id = id++; item->cx = 140; item->cy = 14; item->style = WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | ES_NUMBER; item->dwExtendedStyle = WS_EX_CLIENTEDGE; data = (uint16_t *) (item + 1); *data++ = 0xFFFF; *data++ = 0x0081; /* edit text class */ data += MultiByteToWideChar(CP_ACP, 0, "", -1, data, 256); *data++ = 0; /* no creation data */ if (((uintptr_t) data) & 2) data++; /* TODO: add up down class */ /*Static text*/ item = (DLGITEMTEMPLATE *) data; item->x = 10; item->y = y + 2; item->id = id++; item->cx = 60; item->cy = 20; item->style = WS_CHILD | WS_VISIBLE; data = (uint16_t *) (item + 1); *data++ = 0xFFFF; *data++ = 0x0082; /* static class */ data += MultiByteToWideChar(CP_ACP, 0, config->description, -1, data, 256); *data++ = 0; /* no creation data */ if (((uintptr_t) data) & 2) data++; y += 20; break; case CONFIG_STRING: /*Editable Text*/ item = (DLGITEMTEMPLATE *) data; item->x = 70; item->y = y; item->id = id++; item->cx = 140; item->cy = 14; item->style = WS_CHILD | WS_VISIBLE | ES_READONLY; item->dwExtendedStyle = WS_EX_CLIENTEDGE; data = (uint16_t *) (item + 1); *data++ = 0xFFFF; *data++ = 0x0081; /* edit text class */ data += MultiByteToWideChar(CP_ACP, 0, "", -1, data, 256); *data++ = 0; /* no creation data */ if (((uintptr_t) data) & 2) data++; /*Static text*/ item = (DLGITEMTEMPLATE *) data; item->x = 10; item->y = y + 2; item->id = id++; item->cx = 60; item->cy = 20; item->style = WS_CHILD | WS_VISIBLE; data = (uint16_t *) (item + 1); *data++ = 0xFFFF; *data++ = 0x0082; /* static class */ data += MultiByteToWideChar(CP_ACP, 0, config->description, -1, data, 256); *data++ = 0; /* no creation data */ if (((uintptr_t) data) & 2) data++; y += 20; break; case CONFIG_FNAME: /*File*/ item = (DLGITEMTEMPLATE *) data; item->x = 70; item->y = y; item->id = id++; item->cx = 100; item->cy = 14; item->style = WS_CHILD | WS_VISIBLE | ES_READONLY; item->dwExtendedStyle = WS_EX_CLIENTEDGE; data = (uint16_t *) (item + 1); *data++ = 0xFFFF; *data++ = 0x0081; /* edit text class */ data += MultiByteToWideChar(CP_ACP, 0, "", -1, data, 256); *data++ = 0; /* no creation data */ if (((uintptr_t) data) & 2) data++; /* Button */ item = (DLGITEMTEMPLATE *) data; item->x = 175; item->y = y; item->id = id++; item->cx = 35; item->cy = 14; item->style = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON; data = (uint16_t *) (item + 1); *data++ = 0xFFFF; *data++ = 0x0080; /* button class */ data += MultiByteToWideChar(CP_ACP, 0, "Browse", -1, data, 256); *data++ = 0; /* no creation data */ if (((uintptr_t) data) & 2) data++; /*Static text*/ item = (DLGITEMTEMPLATE *) data; item->x = 10; item->y = y + 2; item->id = id++; item->cx = 60; item->cy = 20; item->style = WS_CHILD | WS_VISIBLE; data = (uint16_t *) (item + 1); *data++ = 0xFFFF; *data++ = 0x0082; /* static class */ data += MultiByteToWideChar(CP_ACP, 0, config->description, -1, data, 256); *data++ = 0; /* no creation data */ if (((uintptr_t) data) & 2) data++; y += 20; break; } if (((uintptr_t) data) & 2) data++; config++; } dlg->cdit = (id - IDC_CONFIG_BASE) + 2; item = (DLGITEMTEMPLATE *) data; item->x = 100; item->y = y + 5; item->cx = 50; item->cy = 14; item->id = IDOK; /* OK button identifier */ item->style = WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON; data = (uint16_t *) (item + 1); *data++ = 0xFFFF; *data++ = 0x0080; /* button class */ data += MultiByteToWideChar(CP_ACP, 0, "OK", -1, data, 50); *data++ = 0; /* no creation data */ if (((uintptr_t) data) & 2) data++; item = (DLGITEMTEMPLATE *) data; item->x = 160; item->y = y + 5; item->cx = 50; item->cy = 14; item->id = IDCANCEL; /* OK button identifier */ item->style = WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON; data = (uint16_t *) (item + 1); *data++ = 0xFFFF; *data++ = 0x0080; /* button class */ data += MultiByteToWideChar(CP_ACP, 0, "Cancel", -1, data, 50); *data++ = 0; /* no creation data */ dlg->cy = y + 25; device_set_context(&config_device, device, inst); DialogBoxIndirect(hinstance, dlg, hwnd, deviceconfig_dlgproc); free(data_block); return deviceconfig_changed; } uint8_t deviceconfig_open(HWND hwnd, const device_t *device) { return deviceconfig_inst_open(hwnd, device, 0); }