Files
86Box/src/win/win_devconf.c
2023-08-23 04:15:57 -04:00

833 lines
30 KiB
C

/*
* 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, <https://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
*
* Copyright 2008-2018 Sarah Walker.
* Copyright 2016-2018 Miran Grca.
* Copyright 2021-2023 Jasmine Iwanek.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#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 <windowsx.h>
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, UNUSED(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];
const char *str;
const 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(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);
}