This commit is contained in:
ts-korhonen
2021-12-02 19:18:13 +02:00
81 changed files with 10399 additions and 12 deletions

View File

@@ -13,7 +13,7 @@
# Copyright 2020,2021 David Hrdlička.
#
cmake_minimum_required(VERSION 3.15)
cmake_minimum_required(VERSION 3.16)
cmake_policy(SET CMP0091 NEW)
cmake_policy(SET CMP0079 NEW)
@@ -60,6 +60,7 @@ option(MUNT "MUNT" ON)
option(VRAMDUMP "Video RAM dumping" OFF)
option(DINPUT "DirectInput" OFF)
option(DISCORD "Discord integration" ON)
option(QT "QT GUI" ON)
option(NEW_DYNAREC "Use the PCem v15 (\"new\") dynamic recompiler" OFF)

View File

@@ -1,5 +1,26 @@
86Box
=====
**This Branch:** I've added initial Qt support to 86Box because I wanted to
have the configuration dialogs available on Linux, similar to what was
available on PCem.
This is work-in-progress!
Implemented
-----------
* The settings dialog
* Full screen switching
* Keyboard and mouse
* Status updates (activity lights)
TODO
----
* Emulation state and updates (titlebar in windows)
* Display output options (like forced 4:3)
* Entering full screen from within the emulated screen
Original Readme
===============
**86Box** is a low level x86 emulator that runs older operating systems and software designed for IBM PC systems and compatibles from 1981 through fairly recent system designs based on the PCI bus.
Features

View File

@@ -129,6 +129,13 @@ if(APPLE)
endif()
if(QT)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
find_package(Qt5 COMPONENTS Core Widgets REQUIRED)
endif()
find_package(Freetype REQUIRED)
include_directories(${FREETYPE_INCLUDE_DIRS})
if(APPLE)
@@ -189,14 +196,28 @@ else()
install(TARGETS 86Box)
endif()
# adjustments for macOS app bundles
if(APPLE)
set(APPS ${CMAKE_CURRENT_BINARY_DIR}/86Box.app)
install(CODE "
include(InstallRequiredSystemLibraries)
include(BundleUtilities)
fixup_bundle(\"${APPS}\" \"\" \"\")"
COMPONENT Runtime)
# needed for Qt packaging
# get the macdeployqt path
get_target_property(_qmake_executable Qt5::qmake IMPORTED_LOCATION)
get_filename_component(_qt_bin_dir "${_qmake_executable}" DIRECTORY)
find_program(MACDEPLOYQT_EXECUTABLE macdeployqt HINTS "${_qt_bin_dir}")
# excecute macdeployqt
add_custom_command(TARGET 86Box POST_BUILD
COMMAND "${MACDEPLOYQT_EXECUTABLE}"
"$<TARGET_FILE_DIR:86Box>/../.."
-always-overwrite
COMMENT "Running macdeployqt..."
)
endif()
if(VCPKG_TOOLCHAIN)
@@ -221,9 +242,12 @@ add_subdirectory(sio)
add_subdirectory(scsi)
add_subdirectory(sound)
add_subdirectory(video)
if(APPLE)
if (APPLE)
add_subdirectory(mac)
add_subdirectory(unix)
endif()
if (QT)
add_subdirectory(qt)
elseif(WIN32)
add_subdirectory(win)
else()

View File

@@ -51,7 +51,7 @@ static const mo_type_t mo_types[KNOWN_MO_TYPES] = {
typedef struct
{
const char vendor[8];
const char vendor[9];
const char model[16];
const char revision[5];
int8_t supported_media[KNOWN_MO_TYPES];

View File

@@ -20,6 +20,8 @@
#ifndef EMU_PLAT_H
# define EMU_PLAT_H
#include "86box/device.h"
#include "86box/machine.h"
#ifndef GLOBAL
# define GLOBAL extern
#endif
@@ -158,6 +160,25 @@ extern int ioctl_open(uint8_t id, char d);
extern void ioctl_reset(uint8_t id);
extern void ioctl_close(uint8_t id);
#ifdef __APPLE__
#define thread_t plat_thread_t
#define event_t plat_event_t
#define mutex_t plat_mutex_t
#define thread_create plat_thread_create
#define thread_wait plat_thread_wait
#define thread_create_event plat_thread_create_event
#define thread_set_event plat_thread_set_event
#define thread_reset_event plat_thread_reset_event
#define thread_wait_event plat_thread_wait_event
#define thread_destroy_event plat_thread_destroy_event
#define thread_create_mutex plat_thread_create_mutex
#define thread_create_mutex_with_spin_count plat_thread_create_mutex_with_spin_count
#define thread_close_mutex plat_thread_close_mutex
#define thread_wait_mutex plat_thread_wait_mutex
#define thread_release_mutex plat_thread_release_mutex
#endif
/* Thread support. */
typedef void thread_t;

View File

@@ -13,12 +13,28 @@
# Copyright 2020,2021 David Hrdlička.
#
add_library(slirp STATIC tinyglib.c arp_table.c bootp.c cksum.c dnssearch.c if.c ip_icmp.c
ip_input.c ip_output.c mbuf.c misc.c sbuf.c slirp.c socket.c tcp_input.c
tcp_output.c tcp_subr.c tcp_timer.c udp.c util.c version.c)
add_library(slirp STATIC arp_table.c bootp.c cksum.c dnssearch.c if.c ip_icmp.c
ip_input.c ip_output.c mbuf.c misc.c sbuf.c slirp.c socket.c tcp_input.c
tcp_output.c tcp_subr.c tcp_timer.c udp.c util.c version.c)
#target_link_libraries(slirp wsock32 iphlpapi)
#target_link_libraries(slirp)
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
target_link_libraries(slirp wsock32 iphlpapi)
target_link_libraries(slirp wsock32 iphlpapi)
endif()
if(QT)
if(UNIX AND NOT APPLE)
find_package(PkgConfig REQUIRED)
pkg_check_modules(GLIB_PKG glib-2.0)
if (GLIB_PKG_FOUND)
include_directories(${GLIB_PKG_INCLUDE_DIRS})
target_link_libraries(slirp glib-2.0)
target_compile_definitions(slirp PRIVATE TINYGLIB_USE_GLIB)
else()
message(ERROR "GLib development headers are required when compiling with Qt on Unix")
endif()
else()
target_sources(slirp PRIVATE tinyglib.c)
endif()
else()
target_sources(slirp PRIVATE tinyglib.c)
endif()

View File

@@ -11,7 +11,7 @@
#ifdef BSD
#define g_strlcpy strlcpy
#else
extern int g_strlcpy(gchar* dest, const gchar* src, gsize dest_size);
extern gsize g_strlcpy(gchar* dest, const gchar* src, gsize dest_size);
#endif
#endif

120
src/qt/CMakeLists.txt Normal file
View File

@@ -0,0 +1,120 @@
# Find includes in corresponding build directories
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
find_package(Threads REQUIRED)
add_library(plat STATIC qt.c qt_main.cpp qt_platform.cpp qt_midi.cpp cpp11_thread.cpp)
add_library(ui STATIC
qt_ui.cpp
qt_cdrom.c
qt_sdl.c
qt_mainwindow.cpp
qt_mainwindow.hpp
qt_mainwindow.ui
qt_machinestatus.cpp
qt_machinestatus.hpp
#qt_machinestatus.ui
qt_gleswidget.cpp
qt_gleswidget.hpp
qt_settings.cpp
qt_settings.hpp
qt_settings.ui
qt_settingsmachine.cpp
qt_settingsmachine.hpp
qt_settingsmachine.ui
qt_settingsdisplay.cpp
qt_settingsdisplay.hpp
qt_settingsdisplay.ui
qt_settingsinput.cpp
qt_settingsinput.hpp
qt_settingsinput.ui
qt_settingssound.cpp
qt_settingssound.hpp
qt_settingssound.ui
qt_settingsnetwork.cpp
qt_settingsnetwork.hpp
qt_settingsnetwork.ui
qt_settingsports.cpp
qt_settingsports.hpp
qt_settingsports.ui
qt_settingsstoragecontrollers.cpp
qt_settingsstoragecontrollers.hpp
qt_settingsstoragecontrollers.ui
qt_settingsharddisks.cpp
qt_settingsharddisks.hpp
qt_settingsharddisks.ui
qt_settingsfloppycdrom.cpp
qt_settingsfloppycdrom.hpp
qt_settingsfloppycdrom.ui
qt_settingsotherremovable.cpp
qt_settingsotherremovable.hpp
qt_settingsotherremovable.ui
qt_settingsotherperipherals.cpp
qt_settingsotherperipherals.hpp
qt_settingsotherperipherals.ui
qt_deviceconfig.cpp
qt_deviceconfig.hpp
qt_deviceconfig.ui
qt_filefield.cpp
qt_filefield.hpp
qt_filefield.ui
qt_harddiskdialog.cpp
qt_harddiskdialog.hpp
qt_harddiskdialog.ui
qt_harddrive_common.cpp
qt_harddrive_common.hpp
qt_models_common.cpp
qt_models_common.hpp
../qt_resources.qrc
)
if (APPLE)
target_sources(ui PRIVATE macos_event_filter.mm)
endif()
target_link_libraries(
plat
PRIVATE
Qt5::Widgets
Qt5::Gui
Threads::Threads
)
target_link_libraries(
ui
PRIVATE
Qt5::Widgets
Qt5::Gui
Threads::Threads
)
if (UNIX AND NOT APPLE)
find_package(X11 REQUIRED)
target_link_libraries(ui PRIVATE X11::X11)
find_package(ECM NO_MODULE)
if (ECM_FOUND)
list(APPEND CMAKE_MODULE_PATH ${ECM_MODULE_PATH})
find_package(Wayland COMPONENTS Client)
if (Wayland_FOUND)
target_link_libraries(ui PRIVATE Wayland::Client)
find_package(WaylandScanner REQUIRED)
if (WaylandScanner_FOUND)
set(WL_SOURCE_VAR)
ecm_add_wayland_client_protocol(WL_SOURCE_VAR PROTOCOL ${CMAKE_SOURCE_DIR}/wl_protocols/relative-pointer-unstable-v1.xml BASENAME relative-pointer-unstable-v1)
ecm_add_wayland_client_protocol(WL_SOURCE_VAR PROTOCOL ${CMAKE_SOURCE_DIR}/wl_protocols/pointer-constraints-unstable-v1.xml BASENAME pointer-constraints-unstable-v1)
target_include_directories(ui PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
target_sources(ui PRIVATE ${WL_SOURCE_VAR} wl_mouse.cpp)
target_compile_definitions(ui PRIVATE WAYLAND)
endif()
endif()
endif()
endif()

1
src/qt/TODO Normal file
View File

@@ -0,0 +1 @@
* Joystick support

10
src/qt/cocoa_mouse.hpp Normal file
View File

@@ -0,0 +1,10 @@
#include <QAbstractNativeEventFilter>
#include <QByteArray>
class CocoaEventFilter : public QAbstractNativeEventFilter
{
public:
CocoaEventFilter() {};
~CocoaEventFilter();
virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) override;
};

130
src/qt/cpp11_thread.cpp Normal file
View File

@@ -0,0 +1,130 @@
#include <mutex>
#include <atomic>
#include <thread>
#include <condition_variable>
#include <86box/plat.h>
struct event_cpp11_t
{
std::condition_variable cond;
std::mutex mutex;
std::atomic_bool state = false;
};
extern "C" {
thread_t *
thread_create(void (*thread_rout)(void *param), void *param)
{
auto thread = new std::thread([thread_rout, param] {
thread_rout(param);
});
return thread;
}
mutex_t *
thread_create_mutex_with_spin_count(unsigned int spin_count)
{
/* Setting spin count of a mutex is not possible with pthreads. */
return thread_create_mutex();
}
int
thread_wait(thread_t *arg, int timeout)
{
(void) timeout;
auto thread = reinterpret_cast<std::thread*>(arg);
thread->join();
return 0;
}
mutex_t *
thread_create_mutex(void)
{
auto mutex = new std::mutex;
return mutex;
}
int
thread_wait_mutex(mutex_t *_mutex)
{
if (_mutex == nullptr)
return(0);
auto mutex = reinterpret_cast<std::mutex*>(_mutex);
mutex->lock();
return 1;
}
int
thread_release_mutex(mutex_t *_mutex)
{
if (_mutex == nullptr)
return(0);
auto mutex = reinterpret_cast<std::mutex*>(_mutex);
mutex->unlock();
return 1;
}
void
thread_close_mutex(mutex_t *_mutex)
{
auto mutex = reinterpret_cast<std::mutex*>(_mutex);
delete mutex;
}
event_t *
thread_create_event()
{
auto ev = new event_cpp11_t;
return ev;
}
int
thread_wait_event(event_t *handle, int timeout)
{
auto event = reinterpret_cast<event_cpp11_t*>(handle);
auto lock = std::unique_lock<std::mutex>(event->mutex);
if (timeout < 0) {
event->cond.wait(lock, [=] { return event->state.load(); });
} else {
auto to = std::chrono::system_clock::now() + std::chrono::milliseconds(timeout);
std::cv_status status;
do {
status = event->cond.wait_until(lock, to);
} while ((status != std::cv_status::timeout) && !event->state);
if (status == std::cv_status::timeout) {
return 1;
}
}
return 0;
}
void
thread_set_event(event_t *handle)
{
auto event = reinterpret_cast<event_cpp11_t*>(handle);
event->state = true;
event->cond.notify_all();
}
void
thread_reset_event(event_t *handle)
{
auto event = reinterpret_cast<event_cpp11_t*>(handle);
event->state = false;
}
void
thread_destroy_event(event_t *handle)
{
auto event = reinterpret_cast<event_cpp11_t*>(handle);
delete event;
}
}

View File

@@ -0,0 +1,103 @@
#include <SDL.h>
//#include "86box/plat.h"
#include "cocoa_mouse.hpp"
#import <AppKit/AppKit.h>
extern "C"
{
#include <86box/86box.h>
#include <86box/keyboard.h>
#include <86box/mouse.h>
#include <86box/config.h>
//#include <86box/plat.h>
#include <86box/plat_dynld.h>
#include <86box/device.h>
#include <86box/timer.h>
#include <86box/ui.h>
#include <86box/video.h>
extern int mouse_capture;
extern void plat_mouse_capture(int);
}
typedef struct mouseinputdata
{
int deltax, deltay, deltaz;
int mousebuttons;
} mouseinputdata;
static mouseinputdata mousedata;
CocoaEventFilter::~CocoaEventFilter()
{
}
bool CocoaEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long *result)
{
if (mouse_capture)
{
if (eventType == "mac_generic_NSEvent")
{
NSEvent* event = (NSEvent*)message;
if ([event type] == NSEventTypeMouseMoved
|| [event type] == NSEventTypeLeftMouseDragged
|| [event type] == NSEventTypeRightMouseDragged
|| [event type] == NSEventTypeOtherMouseDragged)
{
mousedata.deltax += [event deltaX];
mousedata.deltay += [event deltaY];
return true;
}
if ([event type] == NSEventTypeScrollWheel)
{
mousedata.deltaz += [event deltaY];
return true;
}
switch ([event type])
{
default: return false;
case NSEventTypeLeftMouseDown:
{
mousedata.mousebuttons |= 1;
break;
}
case NSEventTypeLeftMouseUp:
{
mousedata.mousebuttons &= ~1;
break;
}
case NSEventTypeRightMouseDown:
{
mousedata.mousebuttons |= 2;
break;
}
case NSEventTypeRightMouseUp:
{
mousedata.mousebuttons &= ~2;
break;
}
case NSEventTypeOtherMouseDown:
{
mousedata.mousebuttons |= 4;
break;
}
case NSEventTypeOtherMouseUp:
{
if (mouse_get_buttons() < 3) { plat_mouse_capture(0); return true; }
mousedata.mousebuttons &= ~4;
break;
}
}
return true;
}
}
return false;
}
extern "C" void macos_poll_mouse()
{
mouse_x = mousedata.deltax;
mouse_y = mousedata.deltay;
mouse_z = mousedata.deltaz;
mousedata.deltax = mousedata.deltay = mousedata.deltaz = 0;
mouse_buttons = mousedata.mousebuttons;
}

70
src/qt/qt.c Normal file
View File

@@ -0,0 +1,70 @@
/*
* C functionality for Qt platform, where the C equivalent is not easily
* implemented in Qt
*/
#include <stdint.h>
#include <wchar.h>
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/plat.h>
#include <86box/timer.h>
#include <86box/nvr.h>
#include "qt_sdl.h"
int qt_nvr_save(void) {
return nvr_save();
}
char icon_set[256] = ""; /* name of the iconset to be used */
wchar_t* plat_get_string(int i)
{
switch (i)
{
case IDS_2077:
return L"Click to capture mouse.";
case IDS_2078:
return L"Press CTRL-END to release mouse";
case IDS_2079:
return L"Press CTRL-END or middle button to release mouse";
case IDS_2080:
return L"Failed to initialize FluidSynth";
case IDS_4099:
return L"MFM/RLL or ESDI CD-ROM drives never existed";
case IDS_2093:
return L"Failed to set up PCap";
case IDS_2094:
return L"No PCap devices found";
case IDS_2110:
return L"Unable to initialize FreeType";
case IDS_2111:
return L"Unable to initialize SDL, libsdl2 is required";
case IDS_2131:
return L"libfreetype is required for ESC/P printer emulation.";
case IDS_2132:
return L"libgs is required for automatic conversion of PostScript files to PDF.\n\nAny documents sent to the generic PostScript printer will be saved as PostScript (.ps) files.";
case IDS_2129:
return L"Make sure libpcap is installed and that you are on a libpcap-compatible network connection.";
case IDS_2114:
return L"Unable to initialize Ghostscript";
case IDS_2063:
return L"Machine \"%hs\" is not available due to missing ROMs in the roms/machines directory. Switching to an available machine.";
case IDS_2064:
return L"Video card \"%hs\" is not available due to missing ROMs in the roms/video directory. Switching to an available video card.";
case IDS_2128:
return L"Hardware not available";
}
return L"";
}
int
plat_vidapi(char* api) {
return 0;
}
char* plat_vidapi_name(int api) {
return "default";
}

264
src/qt/qt_cdrom.c Normal file
View File

@@ -0,0 +1,264 @@
/*
* 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.
*
* Handle the platform-side of CDROM/ZIP/MO drives.
*
*
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com>
*
* Copyright 2016-2018 Miran Grca.
* Copyright 2017,2018 Fred N. van Kempen.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include <86box/86box.h>
#include <86box/config.h>
#include <86box/timer.h>
#include <86box/device.h>
#include <86box/cassette.h>
#include <86box/cartridge.h>
#include <86box/fdd.h>
#include <86box/hdd.h>
#include <86box/scsi_device.h>
#include <86box/cdrom.h>
#include <86box/mo.h>
#include <86box/zip.h>
#include <86box/scsi_disk.h>
#include <86box/plat.h>
#include <86box/ui.h>
void
cassette_mount(char *fn, uint8_t wp)
{
pc_cas_set_fname(cassette, NULL);
memset(cassette_fname, 0, sizeof(cassette_fname));
cassette_ui_writeprot = wp;
pc_cas_set_fname(cassette, fn);
if (fn != NULL)
memcpy(cassette_fname, fn, MIN(511, strlen(fn)));
ui_sb_update_icon_state(SB_CASSETTE, (fn == NULL) ? 1 : 0);
//media_menu_update_cassette();
ui_sb_update_tip(SB_CASSETTE);
config_save();
}
void
cassette_eject(void)
{
pc_cas_set_fname(cassette, NULL);
memset(cassette_fname, 0x00, sizeof(cassette_fname));
ui_sb_update_icon_state(SB_CASSETTE, 1);
//media_menu_update_cassette();
ui_sb_update_tip(SB_CASSETTE);
config_save();
}
void
cartridge_mount(uint8_t id, char *fn, uint8_t wp)
{
cart_close(id);
cart_load(id, fn);
ui_sb_update_icon_state(SB_CARTRIDGE | id, strlen(cart_fns[id]) ? 0 : 1);
//media_menu_update_cartridge(id);
ui_sb_update_tip(SB_CARTRIDGE | id);
config_save();
}
void
cartridge_eject(uint8_t id)
{
cart_close(id);
ui_sb_update_icon_state(SB_CARTRIDGE | id, 1);
//media_menu_update_cartridge(id);
ui_sb_update_tip(SB_CARTRIDGE | id);
config_save();
}
void
floppy_mount(uint8_t id, char *fn, uint8_t wp)
{
fdd_close(id);
ui_writeprot[id] = wp;
fdd_load(id, fn);
ui_sb_update_icon_state(SB_FLOPPY | id, strlen(floppyfns[id]) ? 0 : 1);
//media_menu_update_floppy(id);
ui_sb_update_tip(SB_FLOPPY | id);
config_save();
}
void
floppy_eject(uint8_t id)
{
fdd_close(id);
ui_sb_update_icon_state(SB_FLOPPY | id, 1);
//media_menu_update_floppy(id);
ui_sb_update_tip(SB_FLOPPY | id);
config_save();
}
void
plat_cdrom_ui_update(uint8_t id, uint8_t reload)
{
cdrom_t *drv = &cdrom[id];
if (drv->host_drive == 0) {
ui_sb_update_icon_state(SB_CDROM|id, 1);
} else {
ui_sb_update_icon_state(SB_CDROM|id, 0);
}
//media_menu_update_cdrom(id);
ui_sb_update_tip(SB_CDROM|id);
}
void
cdrom_mount(uint8_t id, char *fn)
{
cdrom[id].prev_host_drive = cdrom[id].host_drive;
strcpy(cdrom[id].prev_image_path, cdrom[id].image_path);
if (cdrom[id].ops && cdrom[id].ops->exit)
cdrom[id].ops->exit(&(cdrom[id]));
cdrom[id].ops = NULL;
memset(cdrom[id].image_path, 0, sizeof(cdrom[id].image_path));
cdrom_image_open(&(cdrom[id]), fn);
/* Signal media change to the emulated machine. */
if (cdrom[id].insert)
cdrom[id].insert(cdrom[id].priv);
cdrom[id].host_drive = (strlen(cdrom[id].image_path) == 0) ? 0 : 200;
if (cdrom[id].host_drive == 200) {
ui_sb_update_icon_state(SB_CDROM | id, 0);
} else {
ui_sb_update_icon_state(SB_CDROM | id, 1);
}
//media_menu_update_cdrom(id);
ui_sb_update_tip(SB_CDROM | id);
config_save();
}
void
mo_eject(uint8_t id)
{
mo_t *dev = (mo_t *) mo_drives[id].priv;
mo_disk_close(dev);
if (mo_drives[id].bus_type) {
/* Signal disk change to the emulated machine. */
mo_insert(dev);
}
ui_sb_update_icon_state(SB_MO | id, 1);
//media_menu_update_mo(id);
ui_sb_update_tip(SB_MO | id);
config_save();
}
void
mo_mount(uint8_t id, char *fn, uint8_t wp)
{
mo_t *dev = (mo_t *) mo_drives[id].priv;
mo_disk_close(dev);
mo_drives[id].read_only = wp;
mo_load(dev, fn);
mo_insert(dev);
ui_sb_update_icon_state(SB_MO | id, strlen(mo_drives[id].image_path) ? 0 : 1);
//media_menu_update_mo(id);
ui_sb_update_tip(SB_MO | id);
config_save();
}
void
mo_reload(uint8_t id)
{
mo_t *dev = (mo_t *) mo_drives[id].priv;
mo_disk_reload(dev);
if (strlen(mo_drives[id].image_path) == 0) {
ui_sb_update_icon_state(SB_MO|id, 1);
} else {
ui_sb_update_icon_state(SB_MO|id, 0);
}
//media_menu_update_mo(id);
ui_sb_update_tip(SB_MO|id);
config_save();
}
void
zip_eject(uint8_t id)
{
zip_t *dev = (zip_t *) zip_drives[id].priv;
zip_disk_close(dev);
if (zip_drives[id].bus_type) {
/* Signal disk change to the emulated machine. */
zip_insert(dev);
}
ui_sb_update_icon_state(SB_ZIP | id, 1);
//media_menu_update_zip(id);
ui_sb_update_tip(SB_ZIP | id);
config_save();
}
void
zip_mount(uint8_t id, char *fn, uint8_t wp)
{
zip_t *dev = (zip_t *) zip_drives[id].priv;
zip_disk_close(dev);
zip_drives[id].read_only = wp;
zip_load(dev, fn);
zip_insert(dev);
ui_sb_update_icon_state(SB_ZIP | id, strlen(zip_drives[id].image_path) ? 0 : 1);
//media_menu_update_zip(id);
ui_sb_update_tip(SB_ZIP | id);
config_save();
}
void
zip_reload(uint8_t id)
{
zip_t *dev = (zip_t *) zip_drives[id].priv;
zip_disk_reload(dev);
if (strlen(zip_drives[id].image_path) == 0) {
ui_sb_update_icon_state(SB_ZIP|id, 1);
} else {
ui_sb_update_icon_state(SB_ZIP|id, 0);
}
//media_menu_update_zip(id);
ui_sb_update_tip(SB_ZIP|id);
config_save();
}

157
src/qt/qt_deviceconfig.cpp Normal file
View File

@@ -0,0 +1,157 @@
#include "qt_deviceconfig.hpp"
#include "ui_qt_deviceconfig.h"
#include <QDebug>
#include <QComboBox>
#include <QFormLayout>
#include <QSpinBox>
#include <QCheckBox>
extern "C" {
#include <86box/86box.h>
#include <86box/config.h>
#include <86box/device.h>
}
#include "qt_filefield.hpp"
DeviceConfig::DeviceConfig(QWidget *parent) :
QDialog(parent),
ui(new Ui::DeviceConfig)
{
ui->setupUi(this);
}
DeviceConfig::~DeviceConfig()
{
delete ui;
}
void DeviceConfig::ConfigureDevice(const _device_* device) {
DeviceConfig dc;
dc.setWindowTitle(QString("%1 Device Configuration").arg(device->name));
device_context_t device_context;
device_set_context(&device_context, device, 0);
const auto* config = device->config;
while (config->type != -1) {
switch (config->type) {
case CONFIG_BINARY:
{
auto value = config_get_int(device_context.name, const_cast<char*>(config->name), config->default_int);
auto* cbox = new QCheckBox();
cbox->setObjectName(config->name);
cbox->setChecked(value > 0);
dc.ui->formLayout->addRow(config->description, cbox);
break;
}
case CONFIG_SELECTION:
case CONFIG_MIDI:
case CONFIG_MIDI_IN:
case CONFIG_HEX16:
case CONFIG_HEX20:
{
auto* cbox = new QComboBox();
cbox->setObjectName(config->name);
auto* model = cbox->model();
int currentIndex = -1;
int selected = config_get_int(device_context.name, const_cast<char*>(config->name), config->default_int);
for (auto* sel = config->selection; (sel->description != nullptr) && (strlen(sel->description) > 0); ++sel) {
int rows = model->rowCount();
model->insertRow(rows);
auto idx = model->index(rows, 0);
model->setData(idx, sel->description, Qt::DisplayRole);
model->setData(idx, sel->value, Qt::UserRole);
if (selected == sel->value) {
currentIndex = idx.row();
}
}
dc.ui->formLayout->addRow(config->description, cbox);
cbox->setCurrentIndex(currentIndex);
break;
}
case CONFIG_SPINNER:
{
int value = config_get_int(device_context.name, const_cast<char*>(config->name), config->default_int);
auto* spinBox = new QSpinBox();
spinBox->setObjectName(config->name);
spinBox->setMaximum(config->spinner.max);
spinBox->setMinimum(config->spinner.min);
if (config->spinner.step > 0) {
spinBox->setSingleStep(config->spinner.step);
}
spinBox->setValue(value);
dc.ui->formLayout->addRow(config->description, spinBox);
break;
}
case CONFIG_FNAME:
{
auto* fileName = config_get_string(device_context.name, const_cast<char*>(config->name), const_cast<char*>(config->default_string));
auto* fileField = new FileField();
fileField->setObjectName(config->name);
fileField->setFileName(fileName);
dc.ui->formLayout->addRow(config->description, fileField);
break;
}
}
++config;
}
int res = dc.exec();
if (res == QDialog::Accepted) {
config = device->config;
while (config->type != -1) {
switch (config->type) {
case CONFIG_BINARY:
{
auto* cbox = dc.findChild<QCheckBox*>(config->name);
config_set_int(device_context.name, const_cast<char*>(config->name), cbox->isChecked() ? 1 : 0);
break;
}
case CONFIG_SELECTION:
case CONFIG_MIDI:
case CONFIG_MIDI_IN:
case CONFIG_HEX16:
case CONFIG_HEX20:
{
auto* cbox = dc.findChild<QComboBox*>(config->name);
config_set_int(device_context.name, const_cast<char*>(config->name), cbox->currentData().toInt());
break;
}
case CONFIG_FNAME:
{
auto* fbox = dc.findChild<FileField*>(config->name);
auto fileName = fbox->fileName().toUtf8();
config_set_string(device_context.name, const_cast<char*>(config->name), fileName.data());
break;
}
case CONFIG_SPINNER:
{
auto* spinBox = dc.findChild<QSpinBox*>(config->name);
config_set_int(device_context.name, const_cast<char*>(config->name), spinBox->value());
break;
}
}
config++;
}
}
}
QString DeviceConfig::DeviceName(const _device_* device, const char *internalName, int bus) {
if (QStringLiteral("none") == internalName) {
return "None";
} else if (QStringLiteral("internal") == internalName) {
return "Internal";
} else if (device == nullptr) {
return QString();
} else {
char temp[512];
device_get_name(device, bus, temp);
return temp;
}
}

View File

@@ -0,0 +1,28 @@
#ifndef QT_DEVICECONFIG_HPP
#define QT_DEVICECONFIG_HPP
#include <QDialog>
extern "C" {
struct _device_;
}
namespace Ui {
class DeviceConfig;
}
class DeviceConfig : public QDialog
{
Q_OBJECT
public:
explicit DeviceConfig(QWidget *parent = nullptr);
~DeviceConfig();
static void ConfigureDevice(const _device_* device);
static QString DeviceName(const _device_* device, const char* internalName, int bus);
private:
Ui::DeviceConfig *ui;
};
#endif // QT_DEVICECONFIG_HPP

74
src/qt/qt_deviceconfig.ui Normal file
View File

@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DeviceConfig</class>
<widget class="QDialog" name="DeviceConfig">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout"/>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>DeviceConfig</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>DeviceConfig</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

34
src/qt/qt_filefield.cpp Normal file
View File

@@ -0,0 +1,34 @@
#include "qt_filefield.hpp"
#include "ui_qt_filefield.h"
#include <QFileDialog>
FileField::FileField(QWidget *parent) :
QWidget(parent),
ui(new Ui::FileField)
{
ui->setupUi(this);
}
FileField::~FileField()
{
delete ui;
}
void FileField::setFileName(const QString &fileName) {
fileName_ = fileName;
ui->label->setText(fileName);
}
void FileField::on_pushButton_clicked() {
QString fileName;
if (createFile_) {
fileName = QFileDialog::getSaveFileName(this, "Create...");
} else {
fileName = QFileDialog::getOpenFileName(this, "Open...");
}
fileName_ = fileName;
ui->label->setText(fileName);
emit fileSelected(fileName);
}

35
src/qt/qt_filefield.hpp Normal file
View File

@@ -0,0 +1,35 @@
#ifndef QT_FILEFIELD_HPP
#define QT_FILEFIELD_HPP
#include <QWidget>
namespace Ui {
class FileField;
}
class FileField : public QWidget
{
Q_OBJECT
public:
explicit FileField(QWidget *parent = nullptr);
~FileField();
QString fileName() const { return fileName_; }
void setFileName(const QString& fileName);
void setCreateFile(bool createFile) { createFile_ = createFile; }
signals:
void fileSelected(const QString& fileName);
private slots:
void on_pushButton_clicked();
private:
Ui::FileField *ui;
QString fileName_;
bool createFile_ = false;
};
#endif // QT_FILEFIELD_HPP

47
src/qt/qt_filefield.ui Normal file
View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FileField</class>
<widget class="QWidget" name="FileField">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="3,1">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

160
src/qt/qt_gleswidget.cpp Normal file
View File

@@ -0,0 +1,160 @@
#include <QApplication>
#include <QImage>
#include <QGuiApplication>
#include <qnamespace.h>
#include "qt_gleswidget.hpp"
#ifdef __APPLE__
#include <CoreGraphics/CoreGraphics.h>
#endif
extern "C"
{
#include <86box/mouse.h>
#include <86box/plat.h>
#include <86box/video.h>
}
extern "C" void macos_poll_mouse();
void
qt_mouse_capture(int on)
{
if (!on)
{
mouse_capture = 0;
QApplication::setOverrideCursor(Qt::ArrowCursor);
#ifdef __APPLE__
CGAssociateMouseAndMouseCursorPosition(true);
#endif
return;
}
mouse_capture = 1;
QApplication::setOverrideCursor(Qt::BlankCursor);
#ifdef __APPLE__
CGAssociateMouseAndMouseCursorPosition(false);
#endif
return;
}
void GLESWidget::qt_mouse_poll()
{
#ifdef __APPLE__
return macos_poll_mouse();
#else
mouse_x = mousedata.deltax;
mouse_y = mousedata.deltay;
mouse_z = mousedata.deltaz;
mousedata.deltax = mousedata.deltay = mousedata.deltaz = 0;
mouse_buttons = mousedata.mousebuttons;
#ifdef WAYLAND
if (wayland)
wl_mouse_poll();
#endif
#endif
}
void GLESWidget::resizeGL(int w, int h)
{
glViewport(0, 0, w, h);
}
void GLESWidget::initializeGL()
{
initializeOpenGLFunctions();
connect(this, &GLESWidget::reqUpdate, this, &GLESWidget::reqUpdate_);
}
void GLESWidget::paintGL()
{
QPainter painter(this);
painter.drawImage(QRect(0, 0, width(), height()), m_image.convertToFormat(QImage::Format_RGBX8888), QRect(sx, sy, sw, sh));
painter.end();
firstupdate = true;
}
void GLESWidget::reqUpdate_()
{
update();
}
void GLESWidget::mouseReleaseEvent(QMouseEvent *event)
{
if (this->geometry().contains(event->pos()) && event->button() == Qt::LeftButton && !mouse_capture)
{
plat_mouse_capture(1);
this->setCursor(Qt::BlankCursor);
return;
}
if (mouse_capture && event->button() == Qt::MiddleButton && mouse_get_buttons() < 3)
{
plat_mouse_capture(0);
this->setCursor(Qt::ArrowCursor);
return;
}
if (mouse_capture)
{
mousedata.mousebuttons &= ~event->button();
}
}
void GLESWidget::mousePressEvent(QMouseEvent *event)
{
if (mouse_capture)
{
mousedata.mousebuttons |= event->button();
}
event->accept();
}
void GLESWidget::wheelEvent(QWheelEvent *event)
{
if (mouse_capture)
{
mousedata.deltay += event->pixelDelta().y();
}
}
int ignoreNextMouseEvent = 0;
void GLESWidget::mouseMoveEvent(QMouseEvent *event)
{
if (QApplication::platformName().contains("wayland"))
{
event->accept();
return;
}
if (!mouse_capture) { event->ignore(); return; }
#ifdef __APPLE__
event->accept();
return;
#else
static QPoint oldPos = QCursor::pos();
if (ignoreNextMouseEvent) { oldPos = event->pos(); ignoreNextMouseEvent--; event->accept(); return; }
mousedata.deltax += event->pos().x() - oldPos.x();
mousedata.deltay += event->pos().y() - oldPos.y();
QCursor::setPos(mapToGlobal(QPoint(width() / 2, height() / 2)));
oldPos = event->pos();
ignoreNextMouseEvent = 1;
#endif
}
void GLESWidget::qt_real_blit(int x, int y, int w, int h)
{
if ((w <= 0) || (h <= 0) || (w > 2048) || (h > 2048) || (buffer32 == NULL) || !firstupdate)
{
video_blit_complete();
return;
}
sx = x;
sy = y;
sw = this->w = w;
sh = this->h = h;
static auto imagebits = m_image.bits();
for (int y1 = y; y1 < (y + h - 1); y1++)
{
auto scanline = imagebits + (y1 * (2048 + 64) * 4);
video_copy(scanline + (x * 4), &(buffer32->line[y1][x]), w * 4);
}
if (screenshots)
{
video_screenshot((uint32_t *)imagebits, 0, 0, 2048 + 64);
}
video_blit_complete();
this->reqUpdate();
}

70
src/qt/qt_gleswidget.hpp Normal file
View File

@@ -0,0 +1,70 @@
#pragma once
#include <QOpenGLFunctions>
#include <QOpenGLWidget>
#include <QPainter>
#include <QEvent>
#include <QKeyEvent>
#include <atomic>
#include <qapplication.h>
#ifdef WAYLAND
#include "wl_mouse.hpp"
#endif
class GLESWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
private:
QImage m_image{QSize(2048 + 64, 2048 + 64), QImage::Format_RGB32};
int x, y, w, h, sx, sy, sw, sh;
bool wayland = false;
public:
void resizeGL(int w, int h) override;
void initializeGL() override;
void paintGL() override;
GLESWidget(QWidget* parent = nullptr)
: QOpenGLWidget(parent), QOpenGLFunctions()
{
setMinimumSize(16, 16);
#ifdef WAYLAND
if (QApplication::platformName().contains("wayland")) {
wayland = true;
wl_init();
}
#endif
}
~GLESWidget()
{
makeCurrent();
}
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void wheelEvent(QWheelEvent *event) override;
void keyPressEvent(QKeyEvent* event) override
{
event->ignore();
}
void keyReleaseEvent(QKeyEvent* event) override
{
event->ignore();
}
signals:
void reqUpdate();
public slots:
void qt_real_blit(int x, int y, int w, int h);
void qt_mouse_poll();
void reqUpdate_();
private:
struct mouseinputdata {
int deltax, deltay, deltaz;
int mousebuttons;
};
mouseinputdata mousedata;
std::atomic<bool> firstupdate{false};
};

View File

@@ -0,0 +1,706 @@
#include "qt_harddiskdialog.hpp"
#include "ui_qt_harddiskdialog.h"
extern "C" {
#include <86box/86box.h>
#include <86box/hdd.h>
#include "../disk/minivhd/minivhd.h"
#include "../disk/minivhd/minivhd_util.h"
}
#include <thread>
#include <QMessageBox>
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QFileDialog>
#include <QProgressDialog>
#include <QPushButton>
#include "qt_harddrive_common.hpp"
#include "qt_models_common.hpp"
HarddiskDialog::HarddiskDialog(bool existing, QWidget *parent) :
QDialog(parent),
ui(new Ui::HarddiskDialog)
{
ui->setupUi(this);
if (existing) {
setWindowTitle("Add Existing Hard Disk");
ui->lineEditCylinders->setEnabled(false);
ui->lineEditHeads->setEnabled(false);
ui->lineEditSectors->setEnabled(false);
ui->lineEditSize->setEnabled(false);
ui->comboBoxType->setEnabled(false);
ui->comboBoxFormat->hide();
ui->labelFormat->hide();
connect(ui->fileField, &FileField::fileSelected, this, &HarddiskDialog::onExistingFileSelected);
} else {
setWindowTitle("Add New Hard Disk");
ui->fileField->setCreateFile(true);
connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &HarddiskDialog::onCreateNewFile);
}
auto* model = ui->comboBoxFormat->model();
model->insertRows(0, 6);
model->setData(model->index(0, 0), "Raw image (.img)");
model->setData(model->index(1, 0), "HDI image (.hdi)");
model->setData(model->index(2, 0), "HDX image (.hdx)");
model->setData(model->index(3, 0), "Fixed-size VHD (.vhd)");
model->setData(model->index(4, 0), "Dynamic-size VHD (.vhd)");
model->setData(model->index(5, 0), "Differencing VHD (.vhd)");
model = ui->comboBoxBlockSize->model();
model->insertRows(0, 2);
model->setData(model->index(0, 0), "Large blocks (2 MiB)");
model->setData(model->index(1, 0), "Small blocks (512 KiB)");
ui->comboBoxBlockSize->hide();
ui->labelBlockSize->hide();
Harddrives::populateBuses(ui->comboBoxBus->model());
ui->comboBoxBus->setCurrentIndex(3);
model = ui->comboBoxType->model();
for (int i = 0; i < 127; i++) {
uint64_t size = ((uint64_t) hdd_table[i][0]) * hdd_table[i][1] * hdd_table[i][2];
uint64_t size_mb = size >> 11LL;
QString text = QString("%1 MiB (CHS: %2, %3, %4)").arg(size_mb).arg(hdd_table[i][0]).arg(hdd_table[i][1]).arg(hdd_table[i][2]);
Models::AddEntry(model, text, i);
}
Models::AddEntry(model, "Custom...", 127);
Models::AddEntry(model, "Custom (large)...", 128);
ui->lineEditSize->setValidator(new QIntValidator());
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
connect(ui->fileField, &FileField::fileSelected, this, [this] {
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
});
}
HarddiskDialog::~HarddiskDialog()
{
delete ui;
}
uint8_t HarddiskDialog::bus() const {
return static_cast<uint8_t>(ui->comboBoxBus->currentData().toUInt());
}
uint8_t HarddiskDialog::channel() const {
return static_cast<uint8_t>(ui->comboBoxChannel->currentData().toUInt());
}
QString HarddiskDialog::fileName() const {
return ui->fileField->fileName();
}
void HarddiskDialog::on_comboBoxFormat_currentIndexChanged(int index) {
bool enabled;
if (index == 5) { /* They switched to a diff VHD; disable the geometry fields. */
enabled = false;
ui->lineEditCylinders->setText(QStringLiteral("(N/A)"));
ui->lineEditHeads->setText(QStringLiteral("(N/A)"));
ui->lineEditSectors->setText(QStringLiteral("(N/A)"));
ui->lineEditSize->setText(QStringLiteral("(N/A)"));
} else {
enabled = true;
ui->lineEditCylinders->setText(QString::number(cylinders_));
ui->lineEditHeads->setText(QString::number(heads_));
ui->lineEditSectors->setText(QString::number(sectors_));
recalcSize();
}
ui->lineEditCylinders->setEnabled(enabled);
ui->lineEditHeads->setEnabled(enabled);
ui->lineEditSectors->setEnabled(enabled);
ui->lineEditSize->setEnabled(enabled);
ui->comboBoxType->setEnabled(enabled);
if (index < 4) {
ui->comboBoxBlockSize->hide();
ui->labelBlockSize->hide();
} else {
ui->comboBoxBlockSize->show();
ui->labelBlockSize->show();
}
}
/* If the disk geometry requested in the 86Box GUI is not compatible with the internal VHD geometry,
* we adjust it to the next-largest size that is compatible. On average, this will be a difference
* of about 21 MB, and should only be necessary for VHDs larger than 31.5 GB, so should never be more
* than a tenth of a percent change in size.
*/
static void adjust_86box_geometry_for_vhd(MVHDGeom *_86box_geometry, MVHDGeom *vhd_geometry)
{
if (_86box_geometry->cyl <= 65535) {
vhd_geometry->cyl = _86box_geometry->cyl;
vhd_geometry->heads = _86box_geometry->heads;
vhd_geometry->spt = _86box_geometry->spt;
return;
}
int desired_sectors = _86box_geometry->cyl * _86box_geometry->heads * _86box_geometry->spt;
if (desired_sectors > 267321600)
desired_sectors = 267321600;
int remainder = desired_sectors % 85680; /* 8560 is the LCM of 1008 (63*16) and 4080 (255*16) */
if (remainder > 0)
desired_sectors += (85680 - remainder);
_86box_geometry->cyl = desired_sectors / (16 * 63);
_86box_geometry->heads = 16;
_86box_geometry->spt = 63;
vhd_geometry->cyl = desired_sectors / (16 * 255);
vhd_geometry->heads = 16;
vhd_geometry->spt = 255;
}
static HarddiskDialog* callbackPtr = nullptr;
static MVHDGeom create_drive_vhd_fixed(const QString& fileName, HarddiskDialog* p, uint16_t cyl, uint8_t heads, uint8_t spt) {
MVHDGeom _86box_geometry = { .cyl = cyl, .heads = heads, .spt = spt };
MVHDGeom vhd_geometry;
adjust_86box_geometry_for_vhd(&_86box_geometry, &vhd_geometry);
int vhd_error = 0;
QByteArray filenameBytes = fileName.toUtf8();
callbackPtr = p;
MVHDMeta *vhd = mvhd_create_fixed(filenameBytes.data(), vhd_geometry, &vhd_error, [](uint32_t current_sector, uint32_t total_sectors) {
callbackPtr->fileProgress((current_sector * 100) / total_sectors);
});
callbackPtr = nullptr;
if (vhd == NULL) {
_86box_geometry.cyl = 0;
_86box_geometry.heads = 0;
_86box_geometry.spt = 0;
} else {
mvhd_close(vhd);
}
return _86box_geometry;
}
static MVHDGeom create_drive_vhd_dynamic(const QString& fileName, uint16_t cyl, uint8_t heads, uint8_t spt, int blocksize) {
MVHDGeom _86box_geometry = { .cyl = cyl, .heads = heads, .spt = spt };
MVHDGeom vhd_geometry;
adjust_86box_geometry_for_vhd(&_86box_geometry, &vhd_geometry);
int vhd_error = 0;
QByteArray filenameBytes = fileName.toUtf8();
MVHDCreationOptions options;
options.block_size_in_sectors = blocksize;
options.path = filenameBytes.data();
options.size_in_bytes = 0;
options.geometry = vhd_geometry;
options.type = MVHD_TYPE_DYNAMIC;
MVHDMeta *vhd = mvhd_create_ex(options, &vhd_error);
if (vhd == NULL) {
_86box_geometry.cyl = 0;
_86box_geometry.heads = 0;
_86box_geometry.spt = 0;
} else {
mvhd_close(vhd);
}
return _86box_geometry;
}
static MVHDGeom create_drive_vhd_diff(const QString& fileName, const QString& parentFileName, int blocksize) {
int vhd_error = 0;
QByteArray filenameBytes = fileName.toUtf8();
QByteArray parentFilenameBytes = fileName.toUtf8();
MVHDCreationOptions options;
options.block_size_in_sectors = blocksize;
options.path = filenameBytes.data();
options.parent_path = parentFilenameBytes.data();
options.type = MVHD_TYPE_DIFF;
MVHDMeta *vhd = mvhd_create_ex(options, &vhd_error);
MVHDGeom vhd_geometry;
if (vhd == NULL) {
vhd_geometry.cyl = 0;
vhd_geometry.heads = 0;
vhd_geometry.spt = 0;
} else {
vhd_geometry = mvhd_get_geometry(vhd);
if (vhd_geometry.spt > 63) {
vhd_geometry.cyl = mvhd_calc_size_sectors(&vhd_geometry) / (16 * 63);
vhd_geometry.heads = 16;
vhd_geometry.spt = 63;
}
mvhd_close(vhd);
}
return vhd_geometry;
}
void HarddiskDialog::onCreateNewFile() {
qint64 size = ui->lineEditSize->text().toUInt() << 20U;
if (size > 0x1FFFFFFE00ll) {
QMessageBox::critical(this, "Disk image too large", "Disk images cannot be larger than 127 GiB");
return;
}
int img_format = ui->comboBoxFormat->currentIndex();
uint32_t zero = 0;
uint32_t base = 0x1000;
uint32_t sector_size = 512;
auto fileName = ui->fileField->fileName();
QString expectedSuffix;
switch (img_format) {
case 1:
expectedSuffix = "hdi";
break;
case 2:
expectedSuffix = "hdx";
break;
case 3:
case 4:
case 5:
expectedSuffix = "vhd";
break;
}
if (! expectedSuffix.isEmpty()) {
QFileInfo fileInfo(fileName);
if (fileInfo.suffix().compare(expectedSuffix, Qt::CaseInsensitive) != 0) {
fileName = QString("%1.%2").arg(fileName, expectedSuffix);
ui->fileField->setFileName(fileName);
}
}
QFile file(fileName);
if (! file.open(QIODevice::WriteOnly)) {
QMessageBox::critical(this, "Unable to write file", "Make sure the file is being saved to a writable directory");
return;
}
if (img_format == 1) { /* HDI file */
QDataStream stream(&file);
stream.setByteOrder(QDataStream::LittleEndian);
if (size >= 0x100000000ll) {
QMessageBox::critical(this, "Disk image too large", "HDI disk images cannot be larger than 4 GiB");
return;
}
uint32_t s = static_cast<uint32_t>(size);
stream << zero; /* 00000000: Zero/unknown */
stream << zero; /* 00000004: Zero/unknown */
stream << base; /* 00000008: Offset at which data starts */
stream << s; /* 0000000C: Full size of the data (32-bit) */
stream << sector_size; /* 00000010: Sector size in bytes */
stream << sectors_; /* 00000014: Sectors per cylinder */
stream << heads_; /* 00000018: Heads per cylinder */
stream << cylinders_; /* 0000001C: Cylinders */
for (int i = 0; i < 0x3f8; i++) {
stream << zero;
}
} else if (img_format == 2) { /* HDX file */
QDataStream stream(&file);
stream.setByteOrder(QDataStream::LittleEndian);
quint64 signature = 0xD778A82044445459;
stream << signature; /* 00000000: Signature */
stream << size; /* 00000008: Full size of the data (64-bit) */
stream << sector_size; /* 00000010: Sector size in bytes */
stream << sectors_; /* 00000014: Sectors per cylinder */
stream << heads_; /* 00000018: Heads per cylinder */
stream << cylinders_; /* 0000001C: Cylinders */
stream << zero; /* 00000020: [Translation] Sectors per cylinder */
stream << zero; /* 00000004: [Translation] Heads per cylinder */
} else if (img_format >= 3) { /* VHD file */
file.close();
MVHDGeom _86box_geometry;
int block_size = ui->comboBoxBlockSize->currentIndex() == 0 ? MVHD_BLOCK_LARGE : MVHD_BLOCK_SMALL;
switch (img_format) {
case 3:
{
QProgressDialog progress("Creating disk image", QString(), 0, 100, this);
connect(this, &HarddiskDialog::fileProgress, &progress, &QProgressDialog::setValue);
std::thread writer([&_86box_geometry, fileName, this] {
_86box_geometry = create_drive_vhd_fixed(fileName, this, cylinders_, heads_, sectors_);
});
progress.exec();
writer.join();
}
break;
case 4:
_86box_geometry = create_drive_vhd_dynamic(fileName, cylinders_, heads_, sectors_, block_size);
break;
case 5:
QString vhdParent = QFileDialog::getOpenFileName(this, "Select the parent VHD", QString(), "VHD files (*.vhd);;All files (*.*)");
if (vhdParent.isEmpty()) {
return;
}
_86box_geometry = create_drive_vhd_diff(fileName, vhdParent, block_size);
break;
}
if (img_format != 5) {
QMessageBox::information(this, "Disk image created", "Remember to partition and format the newly-created drive");
}
ui->lineEditCylinders->setText(QString::number(_86box_geometry.cyl));
ui->lineEditHeads->setText(QString::number(_86box_geometry.heads));
ui->lineEditSectors->setText(QString::number(_86box_geometry.spt));
cylinders_ = _86box_geometry.cyl;
heads_ = _86box_geometry.heads;
sectors_ = _86box_geometry.spt;
return;
}
// formats 0, 1 and 2
QProgressDialog progress("Creating disk image", QString(), 0, 100, this);
connect(this, &HarddiskDialog::fileProgress, &progress, &QProgressDialog::setValue);
std::thread writer([size, &file, this] {
QDataStream stream(&file);
stream.setByteOrder(QDataStream::LittleEndian);
QByteArray buf(1048576, 0);
uint64_t mibBlocks = size >> 20;
uint64_t restBlock = size & 0xfffff;
if (restBlock) {
stream << QByteArray::fromRawData(buf.data(), restBlock);
}
if (mibBlocks) {
for (uint64_t i = 0; i < mibBlocks; ++i) {
stream << buf;
emit fileProgress(static_cast<int>((i * 100) / mibBlocks));
}
}
emit fileProgress(100);
});
progress.exec();
writer.join();
QMessageBox::information(this, "Disk image created", "Remember to partition and format the newly-created drive");
}
static void adjust_vhd_geometry_for_86box(MVHDGeom *vhd_geometry) {
if (vhd_geometry->spt <= 63)
return;
int desired_sectors = vhd_geometry->cyl * vhd_geometry->heads * vhd_geometry->spt;
if (desired_sectors > 267321600)
desired_sectors = 267321600;
int remainder = desired_sectors % 85680; /* 8560 is the LCM of 1008 (63*16) and 4080 (255*16) */
if (remainder > 0)
desired_sectors -= remainder;
vhd_geometry->cyl = desired_sectors / (16 * 63);
vhd_geometry->heads = 16;
vhd_geometry->spt = 63;
}
void HarddiskDialog::recalcSelection() {
int selection = 127;
for (int i = 0; i < 127; i++) {
if ((cylinders_ == hdd_table[i][0]) &&
(heads_ == hdd_table[i][1]) &&
(sectors_ == hdd_table[i][2]))
selection = i;
}
if ((selection == 127) && (heads_ == 16) && (sectors_ == 63)) {
selection = 128;
}
ui->comboBoxType->setCurrentIndex(selection);
}
void HarddiskDialog::onExistingFileSelected(const QString &fileName) {
// TODO : Over to non-existing file selected
/*
if (!(existing & 1)) {
f = _wfopen(wopenfilestring, L"rb");
if (f != NULL) {
fclose(f);
if (settings_msgbox_ex(MBX_QUESTION_YN, (wchar_t *) IDS_4111, (wchar_t *) IDS_4118, (wchar_t *) IDS_4120, (wchar_t *) IDS_4121, NULL) != 0) / * yes * /
return FALSE;
}
}
f = _wfopen(wopenfilestring, (existing & 1) ? L"rb" : L"wb");
if (f == NULL) {
hdd_add_file_open_error:
fclose(f);
settings_msgbox_header(MBX_ERROR, (existing & 1) ? (wchar_t *) IDS_4114 : (wchar_t *) IDS_4115, (existing & 1) ? (wchar_t *) IDS_4107 : (wchar_t *) IDS_4108);
return TRUE;
}
*/
uint64_t size = 0;
uint32_t sector_size = 0;
uint32_t sectors = 0;
uint32_t heads = 0;
uint32_t cylinders = 0;
int vhd_error = 0;
QFile file(fileName);
if (! file.open(QIODevice::ReadOnly)) {
QMessageBox::critical(this, "Unable to read file", "Make sure the file exists and is readable");
return;
}
QByteArray fileNameUtf8 = fileName.toUtf8();
QFileInfo fi(file);
if (image_is_hdi(fileNameUtf8.data()) || image_is_hdx(fileNameUtf8.data(), 1)) {
file.seek(0x10);
QDataStream stream(&file);
stream.setByteOrder(QDataStream::LittleEndian);
stream >> sector_size;
if (sector_size != 512) {
QMessageBox::critical(this, "Unsupported disk image", "HDI or HDX images with a sector size other than 512 are not supported");
return;
}
sectors = heads = cylinders = 0;
stream >> sectors;
stream >> heads;
stream >> cylinders;
} else if (image_is_vhd(fileNameUtf8.data(), 1)) {
MVHDMeta* vhd = mvhd_open(fileNameUtf8.data(), 0, &vhd_error);
if (vhd == nullptr) {
QMessageBox::critical(this, "Unable to read file", "Make sure the file exists and is readable");
return;
} else if (vhd_error == MVHD_ERR_TIMESTAMP) {
QMessageBox::StandardButton btn = QMessageBox::warning(this, "Parent and child disk timestamps do not match", "This could mean that the parent image was modified after the differencing image was created.\n\nIt can also happen if the image files were moved or copied, or by a bug in the program that created this disk.\n\nDo you want to fix the timestamps?", QMessageBox::Yes | QMessageBox::No);
if (btn == QMessageBox::Yes) {
int ts_res = mvhd_diff_update_par_timestamp(vhd, &vhd_error);
if (ts_res != 0) {
QMessageBox::critical(this, "Error", "Could not fix VHD timestamp");
mvhd_close(vhd);
return;
}
} else {
mvhd_close(vhd);
return;
}
}
MVHDGeom vhd_geom = mvhd_get_geometry(vhd);
adjust_vhd_geometry_for_86box(&vhd_geom);
cylinders = vhd_geom.cyl;
heads = vhd_geom.heads;
sectors = vhd_geom.spt;
size = static_cast<uint64_t>(cylinders * heads * sectors * 512);
mvhd_close(vhd);
} else {
size = file.size();
if (((size % 17) == 0) && (size <= 142606336)) {
sectors = 17;
if (size <= 26738688)
heads = 4;
else if (((size % 3072) == 0) && (size <= 53477376))
heads = 6;
else {
int i;
for (i = 5; i < 16; i++) {
if (((size % (i << 9)) == 0) && (size <= ((i * 17) << 19)))
break;
if (i == 5)
i++;
}
heads = i;
}
} else {
sectors = 63;
heads = 16;
}
cylinders = ((size >> 9) / heads) / sectors;
}
if ((sectors > max_sectors) || (heads > max_heads) || (cylinders > max_cylinders)) {
QMessageBox::critical(this, "Unable to read file", "Make sure the file exists and is readable");
return;
}
heads_ = heads;
sectors_ = sectors;
cylinders_ = cylinders;
ui->lineEditCylinders->setText(QString::number(cylinders));
ui->lineEditHeads->setText(QString::number(heads));
ui->lineEditSectors->setText(QString::number(sectors));
recalcSize();
recalcSelection();
ui->lineEditCylinders->setEnabled(true);
ui->lineEditHeads->setEnabled(true);
ui->lineEditSectors->setEnabled(true);
ui->lineEditSize->setEnabled(true);
ui->comboBoxType->setEnabled(true);
}
void HarddiskDialog::recalcSize() {
uint64_t size = (static_cast<uint64_t>(cylinders_) * static_cast<uint64_t>(heads_) * static_cast<uint64_t>(sectors_)) << 9;
ui->lineEditSize->setText(QString::number(size >> 20));
}
bool HarddiskDialog::checkAndAdjustSectors() {
if (sectors_ > max_sectors) {
sectors_ = max_sectors;
ui->lineEditSectors->setText(QString::number(max_sectors));
recalcSize();
recalcSelection();
return false;
}
return true;
}
bool HarddiskDialog::checkAndAdjustHeads() {
if (heads_ > max_heads) {
heads_ = max_heads;
ui->lineEditHeads->setText(QString::number(max_heads));
recalcSize();
recalcSelection();
return false;
}
return true;
}
bool HarddiskDialog::checkAndAdjustCylinders() {
if (cylinders_ > max_cylinders) {
cylinders_ = max_cylinders;
ui->lineEditCylinders->setText(QString::number(max_cylinders));
recalcSize();
recalcSelection();
return false;
}
return true;
}
void HarddiskDialog::on_comboBoxBus_currentIndexChanged(int index) {
if (index < 0) {
return;
}
switch (ui->comboBoxBus->currentData().toInt()) {
case HDD_BUS_DISABLED:
default:
max_sectors = max_heads = max_cylinders = 0;
break;
case HDD_BUS_MFM:
max_sectors = 26; /* 17 for MFM, 26 for RLL. */
max_heads = 15;
max_cylinders = 2047;
break;
case HDD_BUS_XTA:
max_sectors = 63;
max_heads = 16;
max_cylinders = 1023;
break;
case HDD_BUS_ESDI:
max_sectors = 99; /* ESDI drives usually had 32 to 43 sectors per track. */
max_heads = 16;
max_cylinders = 266305;
break;
case HDD_BUS_IDE:
max_sectors = 63;
max_heads = 255;
max_cylinders = 266305;
break;
case HDD_BUS_ATAPI:
case HDD_BUS_SCSI:
max_sectors = 99;
max_heads = 255;
max_cylinders = 266305;
break;
}
checkAndAdjustCylinders();
checkAndAdjustHeads();
checkAndAdjustSectors();
if (ui->lineEditCylinders->validator() != nullptr) {
delete ui->lineEditCylinders->validator();
}
if (ui->lineEditHeads->validator() != nullptr) {
delete ui->lineEditHeads->validator();
}
if (ui->lineEditSectors->validator() != nullptr) {
delete ui->lineEditSectors->validator();
}
ui->lineEditCylinders->setValidator(new QIntValidator(1, max_cylinders, this));
ui->lineEditHeads->setValidator(new QIntValidator(1, max_heads, this));
ui->lineEditSectors->setValidator(new QIntValidator(1, max_sectors, this));
Harddrives::populateBusChannels(ui->comboBoxChannel->model(), ui->comboBoxBus->currentData().toInt());
}
void HarddiskDialog::on_lineEditSize_textEdited(const QString &text) {
uint32_t size = text.toUInt();
/* This is needed to ensure VHD standard compliance. */
hdd_image_calc_chs(&cylinders_, &heads_, &sectors_, size);
ui->lineEditCylinders->setText(QString::number(cylinders_));
ui->lineEditHeads->setText(QString::number(heads_));
ui->lineEditSectors->setText(QString::number(sectors_));
recalcSelection();
checkAndAdjustCylinders();
checkAndAdjustHeads();
checkAndAdjustSectors();
}
void HarddiskDialog::on_lineEditCylinders_textEdited(const QString &text) {
cylinders_ = text.toUInt();
if (checkAndAdjustCylinders()) {
recalcSize();
recalcSelection();
}
}
void HarddiskDialog::on_lineEditHeads_textEdited(const QString &text) {
heads_ = text.toUInt();
if (checkAndAdjustHeads()) {
recalcSize();
recalcSelection();
}
}
void HarddiskDialog::on_lineEditSectors_textEdited(const QString &text) {
sectors_ = text.toUInt();
if (checkAndAdjustSectors()) {
recalcSize();
recalcSelection();
}
}
void HarddiskDialog::on_comboBoxType_currentIndexChanged(int index) {
if (index < 0) {
return;
}
if ((index != 127) && (index != 128)) {
cylinders_ = hdd_table[index][0];
heads_ = hdd_table[index][1];
sectors_ = hdd_table[index][2];
ui->lineEditCylinders->setText(QString::number(cylinders_));
ui->lineEditHeads->setText(QString::number(heads_));
ui->lineEditSectors->setText(QString::number(sectors_));
recalcSize();
} else if (index == 128) {
heads_ = 16;
sectors_ = 63;
ui->lineEditHeads->setText(QString::number(heads_));
ui->lineEditSectors->setText(QString::number(sectors_));
recalcSize();
}
checkAndAdjustCylinders();
checkAndAdjustHeads();
checkAndAdjustSectors();
}

View File

@@ -0,0 +1,57 @@
#ifndef QT_HARDDISKDIALOG_HPP
#define QT_HARDDISKDIALOG_HPP
#include <QDialog>
namespace Ui {
class HarddiskDialog;
}
class HarddiskDialog : public QDialog
{
Q_OBJECT
public:
explicit HarddiskDialog(bool existing, QWidget *parent = nullptr);
~HarddiskDialog();
uint8_t bus() const;
uint8_t channel() const;
QString fileName() const;
uint32_t cylinders() const { return cylinders_; }
uint32_t heads() const { return heads_; }
uint32_t sectors() const { return sectors_; }
signals:
void fileProgress(int i);
private slots:
void on_comboBoxType_currentIndexChanged(int index);
void on_lineEditSectors_textEdited(const QString &arg1);
void on_lineEditHeads_textEdited(const QString &arg1);
void on_lineEditCylinders_textEdited(const QString &arg1);
void on_lineEditSize_textEdited(const QString &arg1);
void on_comboBoxBus_currentIndexChanged(int index);
void on_comboBoxFormat_currentIndexChanged(int index);
void onCreateNewFile();
void onExistingFileSelected(const QString& fileName);
private:
Ui::HarddiskDialog *ui;
uint32_t cylinders_;
uint32_t heads_;
uint32_t sectors_;
uint32_t max_sectors = 0;
uint32_t max_heads = 0;
uint32_t max_cylinders = 0;
bool checkAndAdjustCylinders();
bool checkAndAdjustHeads();
bool checkAndAdjustSectors();
void recalcSize();
void recalcSelection();
};
#endif // QT_HARDDISKDIALOG_HPP

230
src/qt/qt_harddiskdialog.ui Normal file
View File

@@ -0,0 +1,230 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>HarddiskDialog</class>
<widget class="QDialog" name="HarddiskDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>File name</string>
</property>
</widget>
</item>
<item>
<widget class="FileField" name="fileField" native="true"/>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Cylinders</string>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Sectors</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineEditCylinders"/>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEditSize"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Size (MiB)</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Heads</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLineEdit" name="lineEditHeads">
<property name="maxLength">
<number>32767</number>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Type</string>
</property>
</widget>
</item>
<item row="0" column="5">
<widget class="QLineEdit" name="lineEditSectors">
<property name="maxLength">
<number>32767</number>
</property>
</widget>
</item>
<item row="1" column="3" colspan="3">
<widget class="QComboBox" name="comboBoxType"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_8">
<property name="text">
<string>Bus</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBoxBus"/>
</item>
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>Channel</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBoxChannel"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayoutFormat">
<item>
<widget class="QLabel" name="labelFormat">
<property name="text">
<string>Format</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBoxFormat"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="topMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="labelBlockSize">
<property name="text">
<string>Block Size</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBoxBlockSize"/>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>FileField</class>
<extends>QWidget</extends>
<header>qt_filefield.hpp</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>lineEditCylinders</tabstop>
<tabstop>lineEditHeads</tabstop>
<tabstop>lineEditSectors</tabstop>
<tabstop>lineEditSize</tabstop>
<tabstop>comboBoxType</tabstop>
<tabstop>comboBoxBus</tabstop>
<tabstop>comboBoxChannel</tabstop>
<tabstop>comboBoxFormat</tabstop>
<tabstop>comboBoxBlockSize</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>HarddiskDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>HarddiskDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -0,0 +1,101 @@
#include "qt_harddrive_common.hpp"
#include <cstdint>
extern "C" {
#include <86box/hdd.h>
}
#include <QAbstractItemModel>
void Harddrives::populateBuses(QAbstractItemModel *model) {
model->removeRows(0, model->rowCount());
model->insertRows(0, 6);
model->setData(model->index(0, 0), "MFM/RLL");
model->setData(model->index(1, 0), "XT IDE");
model->setData(model->index(2, 0), "ESDI");
model->setData(model->index(3, 0), "IDE");
model->setData(model->index(4, 0), "ATAPI");
model->setData(model->index(5, 0), "SCSI");
model->setData(model->index(0, 0), HDD_BUS_MFM, Qt::UserRole);
model->setData(model->index(1, 0), HDD_BUS_XTA, Qt::UserRole);
model->setData(model->index(2, 0), HDD_BUS_ESDI, Qt::UserRole);
model->setData(model->index(3, 0), HDD_BUS_IDE, Qt::UserRole);
model->setData(model->index(4, 0), HDD_BUS_ATAPI, Qt::UserRole);
model->setData(model->index(5, 0), HDD_BUS_SCSI, Qt::UserRole);
}
void Harddrives::populateRemovableBuses(QAbstractItemModel *model) {
model->removeRows(0, model->rowCount());
model->insertRows(0, 3);
model->setData(model->index(0, 0), "Disabled");
model->setData(model->index(1, 0), "ATAPI");
model->setData(model->index(2, 0), "SCSI");
model->setData(model->index(0, 0), HDD_BUS_DISABLED, Qt::UserRole);
model->setData(model->index(1, 0), HDD_BUS_ATAPI, Qt::UserRole);
model->setData(model->index(2, 0), HDD_BUS_SCSI, Qt::UserRole);
}
void Harddrives::populateBusChannels(QAbstractItemModel *model, int bus) {
model->removeRows(0, model->rowCount());
int busRows = 0;
int shifter = 1;
int orer = 1;
int subChannelWidth = 1;
switch (bus) {
case HDD_BUS_MFM:
case HDD_BUS_XTA:
case HDD_BUS_ESDI:
busRows = 2;
break;
case HDD_BUS_IDE:
case HDD_BUS_ATAPI:
busRows = 8;
break;
case HDD_BUS_SCSI:
shifter = 4;
orer = 15;
busRows = 64;
subChannelWidth = 2;
break;
}
model->insertRows(0, busRows);
for (int i = 0; i < busRows; ++i) {
auto idx = model->index(i, 0);
model->setData(idx, QString("%1:%2").arg(i >> shifter).arg(i & orer, subChannelWidth, 10, QChar('0')));
model->setData(idx, ((i >> shifter) << shifter) | (i & orer), Qt::UserRole);
}
}
QString Harddrives::BusChannelName(uint8_t bus, uint8_t channel) {
QString busName;
switch(bus) {
case HDD_BUS_DISABLED:
busName = QString("Disabled");
break;
case HDD_BUS_MFM:
busName = QString("MFM/RLL (%1:%2)").arg(channel >> 1).arg(channel & 1);
break;
case HDD_BUS_XTA:
busName = QString("XT IDE (%1:%2)").arg(channel >> 1).arg(channel & 1);
break;
case HDD_BUS_ESDI:
busName = QString("ESDI (%1:%2)").arg(channel >> 1).arg(channel & 1);
break;
case HDD_BUS_IDE:
busName = QString("IDE (%1:%2)").arg(channel >> 1).arg(channel & 1);
break;
case HDD_BUS_ATAPI:
busName = QString("ATAPI (%1:%2)").arg(channel >> 1).arg(channel & 1);
break;
case HDD_BUS_SCSI:
busName = QString("SCSI (%1:%2)").arg(channel >> 4).arg(channel & 15, 2, 10, QChar('0'));
break;
}
return busName;
}

View File

@@ -0,0 +1,13 @@
#pragma once
#include <cstdint>
class QString;
class QAbstractItemModel;
namespace Harddrives {
void populateBuses(QAbstractItemModel* model);
void populateRemovableBuses(QAbstractItemModel* model);
void populateBusChannels(QAbstractItemModel* model, int bus);
QString BusChannelName(uint8_t bus, uint8_t channel);
};

442
src/qt/qt_machinestatus.cpp Normal file
View File

@@ -0,0 +1,442 @@
#include "qt_machinestatus.hpp"
extern "C" {
#define EMU_CPU_H // superhack - don't want timer.h to include cpu.h here, and some combo is preventing a compile
extern uint64_t tsc;
#include <86box/hdd.h>
#include <86box/timer.h>
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/cartridge.h>
#include <86box/cassette.h>
#include <86box/cdrom.h>
#include <86box/fdd.h>
#include <86box/hdc.h>
#include <86box/scsi.h>
#include <86box/scsi_device.h>
#include <86box/zip.h>
#include <86box/mo.h>
#include <86box/plat.h>
#include <86box/machine.h>
#include <86box/network.h>
#include <86box/ui.h>
};
#include <QIcon>
#include <QPicture>
#include <QLabel>
#include <QTimer>
#include <QStatusBar>
#include <array>
namespace {
struct PixmapSetActive {
QPixmap normal;
QPixmap active;
void load(const QString& basePath);
};
struct PixmapSetEmpty {
QPixmap normal;
QPixmap empty;
void load(const QString& basePath);
};
struct PixmapSetEmptyActive {
QPixmap normal;
QPixmap active;
QPixmap empty;
QPixmap empty_active;
void load(const QString& basePath);
};
struct Pixmaps {
PixmapSetEmpty cartridge;
PixmapSetEmptyActive cassette;
PixmapSetEmptyActive floppy_disabled;
PixmapSetEmptyActive floppy_525;
PixmapSetEmptyActive floppy_35;
PixmapSetEmptyActive cdrom;
PixmapSetEmptyActive zip;
PixmapSetEmptyActive mo;
PixmapSetActive hd;
PixmapSetActive net;
QPixmap sound;
};
struct StateActive {
std::unique_ptr<QLabel> label;
QTimer timer;
PixmapSetActive* pixmaps = nullptr;
bool active = false;
void setActive(bool b) {
active = b;
if (! label) {
return;
}
label->setPixmap(active ? pixmaps->active : pixmaps->normal);
timer.start(75);
}
};
struct StateEmpty {
std::unique_ptr<QLabel> label;
PixmapSetEmpty* pixmaps = nullptr;
bool empty = false;
void setEmpty(bool e) {
empty = e;
if (! label) {
return;
}
label->setPixmap(empty ? pixmaps->empty : pixmaps->normal);
}
};
struct StateEmptyActive {
std::unique_ptr<QLabel> label;
QTimer timer;
PixmapSetEmptyActive* pixmaps = nullptr;
bool empty = false;
bool active = false;
void setActive(bool b) {
active = b;
refresh();
timer.start(75);
}
void setEmpty(bool b) {
empty = b;
refresh();
}
void refresh() {
if (! label) {
return;
}
if (empty) {
label->setPixmap(active ? pixmaps->empty_active : pixmaps->empty);
} else {
label->setPixmap(active ? pixmaps->active : pixmaps->normal);
}
}
};
static const QSize pixmap_size(16, 16);
static const QString pixmap_empty = QStringLiteral("_empty");
static const QString pixmap_active = QStringLiteral("_active");
static const QString pixmap_empty_active = QStringLiteral("_empty_active");
void PixmapSetEmpty::load(const QString &basePath) {
normal = QIcon(basePath.arg(QStringLiteral(""))).pixmap(pixmap_size);
empty = QIcon(basePath.arg(pixmap_empty)).pixmap(pixmap_size);
}
void PixmapSetActive::load(const QString &basePath) {
normal = QIcon(basePath.arg(QStringLiteral(""))).pixmap(pixmap_size);
active = QIcon(basePath.arg(pixmap_active)).pixmap(pixmap_size);
}
void PixmapSetEmptyActive::load(const QString &basePath) {
normal = QIcon(basePath.arg(QStringLiteral(""))).pixmap(pixmap_size);
active = QIcon(basePath.arg(pixmap_active)).pixmap(pixmap_size);
empty = QIcon(basePath.arg(pixmap_empty)).pixmap(pixmap_size);
empty_active = QIcon(basePath.arg(pixmap_empty_active)).pixmap(pixmap_size);
}
}
struct MachineStatus::States {
Pixmaps pixmaps;
States(QObject* parent) {
pixmaps.cartridge.load(":/settings/win/icons/cartridge%1.ico");
pixmaps.cassette.load(":/settings/win/icons/cassette%1.ico");
pixmaps.floppy_disabled.normal = QIcon(QStringLiteral(":/settings/win/icons/floppy_disabled.ico")).pixmap(pixmap_size);
pixmaps.floppy_disabled.active = pixmaps.floppy_disabled.normal;
pixmaps.floppy_disabled.empty = pixmaps.floppy_disabled.normal;
pixmaps.floppy_disabled.empty_active = pixmaps.floppy_disabled.normal;
pixmaps.floppy_525.load(":/settings/win/icons/floppy_525%1.ico");
pixmaps.floppy_35.load(":/settings/win/icons/floppy_35%1.ico");
pixmaps.cdrom.load(":/settings/win/icons/cdrom%1.ico");
pixmaps.zip.load(":/settings/win/icons/zip%1.ico");
pixmaps.mo.load(":/settings/win/icons/mo%1.ico");
pixmaps.hd.load(":/settings/win/icons/hard_disk%1.ico");
pixmaps.net.load(":/settings/win/icons/network%1.ico");
pixmaps.sound = QIcon(":/settings/win/icons/sound.ico").pixmap(pixmap_size);
cartridge[0].pixmaps = &pixmaps.cartridge;
cartridge[1].pixmaps = &pixmaps.cartridge;
cassette.pixmaps = &pixmaps.cassette;
QObject::connect(&cassette.timer, &QTimer::timeout, parent, [&]{ cassette.setActive(false); });
for (auto& f : fdd) {
f.pixmaps = &pixmaps.floppy_disabled;
QObject::connect(&f.timer, &QTimer::timeout, parent, [&]{ f.setActive(false); });
}
for (auto& c : cdrom) {
c.pixmaps = &pixmaps.cdrom;
QObject::connect(&c.timer, &QTimer::timeout, parent, [&]{ c.setActive(false); });
}
for (auto& z : zip) {
z.pixmaps = &pixmaps.zip;
QObject::connect(&z.timer, &QTimer::timeout, parent, [&]{ z.setActive(false); });
}
for (auto& m : mo) {
m.pixmaps = &pixmaps.mo;
QObject::connect(&m.timer, &QTimer::timeout, parent, [&]{ m.setActive(false); });
}
for (auto& h : hdds) {
h.pixmaps = &pixmaps.hd;
QObject::connect(&h.timer, &QTimer::timeout, parent, [&]{ h.setActive(false); });
}
net.pixmaps = &pixmaps.net;
}
std::array<StateEmpty, 2> cartridge;
StateEmptyActive cassette;
std::array<StateEmptyActive, FDD_NUM> fdd;
std::array<StateEmptyActive, CDROM_NUM> cdrom;
std::array<StateEmptyActive, ZIP_NUM> zip;
std::array<StateEmptyActive, MO_NUM> mo;
std::array<StateActive, HDD_BUS_USB> hdds;
StateActive net;
std::unique_ptr<QLabel> sound;
};
MachineStatus::MachineStatus(QObject *parent) :
QObject(parent)
{
d = std::make_unique<MachineStatus::States>(this);
}
MachineStatus::~MachineStatus() = default;
static int hdd_count(int bus) {
int c = 0;
int i;
for (i = 0; i < HDD_NUM; i++) {
if (hdd[i].bus == bus) {
c++;
}
}
return(c);
}
void MachineStatus::refresh(QStatusBar* sbar) {
bool has_cart = machines[machine].flags & MACHINE_CARTRIDGE;
bool has_mfm = machines[machine].flags & MACHINE_MFM;
bool has_xta = machines[machine].flags & MACHINE_XTA;
bool has_esdi = machines[machine].flags & MACHINE_ESDI;
bool has_ide = machines[machine].flags & MACHINE_IDE_QUAD;
bool has_scsi = machines[machine].flags & MACHINE_SCSI_DUAL;
int c_mfm = hdd_count(HDD_BUS_MFM);
int c_esdi = hdd_count(HDD_BUS_ESDI);
int c_xta = hdd_count(HDD_BUS_XTA);
int c_ide = hdd_count(HDD_BUS_IDE);
int c_scsi = hdd_count(HDD_BUS_SCSI);
int do_net = (network_type == NET_TYPE_NONE) || (network_card == 0);
sbar->removeWidget(d->cassette.label.get());
for (int i = 0; i < 2; ++i) {
sbar->removeWidget(d->cartridge[i].label.get());
}
for (size_t i = 0; i < FDD_NUM; ++i) {
sbar->removeWidget(d->fdd[i].label.get());
}
for (size_t i = 0; i < CDROM_NUM; i++) {
sbar->removeWidget(d->cdrom[i].label.get());
}
for (size_t i = 0; i < ZIP_NUM; i++) {
sbar->removeWidget(d->zip[i].label.get());
}
for (size_t i = 0; i < MO_NUM; i++) {
sbar->removeWidget(d->mo[i].label.get());
}
for (size_t i = 0; i < HDD_BUS_USB; i++) {
sbar->removeWidget(d->hdds[i].label.get());
}
sbar->removeWidget(d->net.label.get());
sbar->removeWidget(d->sound.get());
if (cassette_enable) {
d->cassette.label = std::make_unique<QLabel>();
d->cassette.setEmpty(QString(cassette_fname).isEmpty());
sbar->addWidget(d->cassette.label.get());
}
if (has_cart) {
for (int i = 0; i < 2; ++i) {
d->cartridge[i].label = std::make_unique<QLabel>();
d->cartridge[i].setEmpty(QString(cart_fns[i]).isEmpty());
sbar->addWidget(d->cartridge[i].label.get());
}
}
for (size_t i = 0; i < FDD_NUM; ++i) {
if (fdd_get_type(i) != 0) {
d->fdd[i].label = std::make_unique<QLabel>();
int t = fdd_get_type(i);
if (t == 0) {
d->fdd[i].pixmaps = &d->pixmaps.floppy_disabled;
} else if (t >= 1 && t <= 6) {
d->fdd[i].pixmaps = &d->pixmaps.floppy_525;
} else {
d->fdd[i].pixmaps = &d->pixmaps.floppy_35;
}
d->fdd[i].setEmpty(QString(floppyfns[i]).isEmpty());
d->fdd[i].setActive(false);
sbar->addWidget(d->fdd[i].label.get());
}
}
auto hdc_name = QString(hdc_get_internal_name(hdc_current));
for (size_t i = 0; i < CDROM_NUM; i++) {
/* Could be Internal or External IDE.. */
if ((cdrom[i].bus_type == CDROM_BUS_ATAPI) &&
!has_ide && hdc_name != QStringLiteral("ide"))
continue;
if ((cdrom[i].bus_type == CDROM_BUS_SCSI) && !has_scsi &&
(scsi_card_current[0] == 0) && (scsi_card_current[1] == 0) &&
(scsi_card_current[2] == 0) && (scsi_card_current[3] == 0))
continue;
if (cdrom[i].bus_type != 0) {
d->cdrom[i].label = std::make_unique<QLabel>();
d->cdrom[i].setEmpty(cdrom[i].host_drive != 200 || QString(cdrom[i].image_path).isEmpty());
d->cdrom[i].setActive(false);
sbar->addWidget(d->cdrom[i].label.get());
}
}
for (size_t i = 0; i < ZIP_NUM; i++) {
/* Could be Internal or External IDE.. */
if ((zip_drives[i].bus_type == ZIP_BUS_ATAPI) &&
!has_ide && hdc_name != QStringLiteral("ide"))
continue;
if ((zip_drives[i].bus_type == ZIP_BUS_SCSI) && !has_scsi &&
(scsi_card_current[0] == 0) && (scsi_card_current[1] == 0) &&
(scsi_card_current[2] == 0) && (scsi_card_current[3] == 0))
continue;
if (zip_drives[i].bus_type != 0) {
d->zip[i].label = std::make_unique<QLabel>();
d->zip[i].setEmpty(QString(zip_drives[i].image_path).isEmpty());
d->zip[i].setActive(false);
sbar->addWidget(d->zip[i].label.get());
}
}
for (size_t i = 0; i < MO_NUM; i++) {
/* Could be Internal or External IDE.. */
if ((mo_drives[i].bus_type == MO_BUS_ATAPI) &&
!has_ide && hdc_name != QStringLiteral("ide"))
continue;
if ((mo_drives[i].bus_type == MO_BUS_SCSI) && !has_scsi &&
(scsi_card_current[0] == 0) && (scsi_card_current[1] == 0) &&
(scsi_card_current[2] == 0) && (scsi_card_current[3] == 0))
continue;
if (mo_drives[i].bus_type != 0) {
d->mo[i].label = std::make_unique<QLabel>();
d->mo[i].setEmpty(QString(mo_drives[i].image_path).isEmpty());
d->mo[i].setActive(false);
sbar->addWidget(d->mo[i].label.get());
}
}
if ((has_mfm || hdc_name == QStringLiteral("st506")) && c_mfm > 0) {
d->hdds[HDD_BUS_MFM].label = std::make_unique<QLabel>();
d->hdds[HDD_BUS_MFM].setActive(false);
sbar->addWidget(d->hdds[HDD_BUS_MFM].label.get());
}
if ((has_esdi || hdc_name == QStringLiteral("esdi")) && c_esdi > 0) {
d->hdds[HDD_BUS_ESDI].label = std::make_unique<QLabel>();
d->hdds[HDD_BUS_ESDI].setActive(false);
sbar->addWidget(d->hdds[HDD_BUS_ESDI].label.get());
}
if ((has_xta || hdc_name == QStringLiteral("xta")) && c_xta > 0) {
d->hdds[HDD_BUS_XTA].label = std::make_unique<QLabel>();
d->hdds[HDD_BUS_XTA].setActive(false);
sbar->addWidget(d->hdds[HDD_BUS_XTA].label.get());
}
if ((has_ide || hdc_name == QStringLiteral("xtide") || hdc_name == QStringLiteral("ide")) && c_ide > 0) {
d->hdds[HDD_BUS_IDE].label = std::make_unique<QLabel>();
d->hdds[HDD_BUS_IDE].setActive(false);
sbar->addWidget(d->hdds[HDD_BUS_IDE].label.get());
}
if ((has_scsi || (scsi_card_current[0] != 0) || (scsi_card_current[1] != 0) ||
(scsi_card_current[2] != 0) || (scsi_card_current[3] != 0)) && c_scsi > 0) {
d->hdds[HDD_BUS_SCSI].label = std::make_unique<QLabel>();
d->hdds[HDD_BUS_SCSI].setActive(false);
sbar->addWidget(d->hdds[HDD_BUS_SCSI].label.get());
}
if (do_net) {
d->net.label = std::make_unique<QLabel>();
d->net.setActive(false);
sbar->addWidget(d->net.label.get());
}
d->sound = std::make_unique<QLabel>();
d->sound->setPixmap(d->pixmaps.sound);
sbar->addWidget(d->sound.get());
}
void MachineStatus::setActivity(int tag, bool active) {
int category = tag & 0xfffffff0;
int item = tag & 0xf;
switch (category) {
case SB_CASSETTE:
break;
case SB_CARTRIDGE:
break;
case SB_FLOPPY:
d->fdd[item].setActive(active);
break;
case SB_CDROM:
d->cdrom[item].setActive(active);
break;
case SB_ZIP:
d->zip[item].setActive(active);
break;
case SB_MO:
d->mo[item].setActive(active);
break;
case SB_HDD:
d->hdds[item].setActive(active);
break;
case SB_NETWORK:
d->net.setActive(active);
break;
case SB_SOUND:
break;
case SB_TEXT:
break;
}
}
void MachineStatus::setEmpty(int tag, bool empty) {
int category = tag & 0xfffffff0;
int item = tag & 0xf;
switch (category) {
case SB_CASSETTE:
d->cassette.setEmpty(empty);
break;
case SB_CARTRIDGE:
d->cartridge[item].setEmpty(empty);
break;
case SB_FLOPPY:
d->fdd[item].setEmpty(empty);
break;
case SB_CDROM:
d->cdrom[item].setEmpty(empty);
break;
case SB_ZIP:
d->zip[item].setEmpty(empty);
break;
case SB_MO:
d->mo[item].setEmpty(empty);
break;
case SB_HDD:
break;
case SB_NETWORK:
break;
case SB_SOUND:
break;
case SB_TEXT:
break;
}
}

View File

@@ -0,0 +1,26 @@
#ifndef QT_MACHINESTATUS_HPP
#define QT_MACHINESTATUS_HPP
#include <QWidget>
class QStatusBar;
class MachineStatus : public QObject
{
Q_OBJECT
public:
explicit MachineStatus(QObject *parent = nullptr);
~MachineStatus();
public slots:
void refresh(QStatusBar* sbar);
void setActivity(int tag, bool active);
void setEmpty(int tag, bool active);
private:
struct States;
std::unique_ptr<States> d;
};
#endif // QT_MACHINESTATUS_HPP

120
src/qt/qt_main.cpp Normal file
View File

@@ -0,0 +1,120 @@
#include <QApplication>
#include <QDebug>
#include <QElapsedTimer>
#include <QThread>
#include <QTimer>
#include <86box/86box.h>
#include <86box/plat.h>
#include <86box/ui.h>
#include <86box/video.h>
#include <thread>
#include "SDL.h"
#include "SDL_mutex.h"
#include "SDL_timer.h"
#include "qt_mainwindow.hpp"
#include "qt_sdl.h"
#include "cocoa_mouse.hpp"
// Void Cast
#define VC(x) const_cast<wchar_t*>(x)
extern QElapsedTimer elapsed_timer;
extern int nvr_dosave;
extern MainWindow* main_window;
extern "C" {
extern int qt_nvr_save(void);
}
void
main_thread_fn()
{
uint64_t old_time, new_time;
int drawits, frames;
QThread::currentThread()->setPriority(QThread::HighestPriority);
framecountx = 0;
//title_update = 1;
old_time = elapsed_timer.elapsed();
drawits = frames = 0;
while (!is_quit && cpu_thread_run) {
/* See if it is time to run a frame of code. */
new_time = elapsed_timer.elapsed();
drawits += (new_time - old_time);
old_time = new_time;
if (drawits > 0 && !dopause) {
/* Yes, so do one frame now. */
drawits -= 10;
if (drawits > 50)
drawits = 0;
/* Run a block of code. */
pc_run();
/* Every 200 frames we save the machine status. */
if (++frames >= 200 && nvr_dosave) {
qt_nvr_save();
nvr_dosave = 0;
frames = 0;
}
} else /* Just so we dont overload the host OS. */
std::this_thread::sleep_for(std::chrono::milliseconds(1));
/* If needed, handle a screen resize. */
if (doresize && !video_fullscreen && !is_quit) {
if (vid_resize & 2)
plat_resize(fixed_size_x, fixed_size_y);
else
plat_resize(scrnsz_x, scrnsz_y);
doresize = 0;
}
}
is_quit = 1;
}
uint32_t timer_onesec(uint32_t interval, void* param)
{
pc_onesec();
return interval;
}
int main(int argc, char* argv[]) {
QApplication app(argc, argv);
#ifdef __APPLE__
CocoaEventFilter cocoafilter;
app.installNativeEventFilter(&cocoafilter);
#endif
elapsed_timer.start();
SDL_Init(SDL_INIT_TIMER);
main_window = new MainWindow();
main_window->show();
pc_init(argc, argv);
if (! pc_init_modules()) {
ui_msgbox_header(MBX_FATAL, VC(L"No ROMs found."), VC(L"86Box could not find any usable ROM images.\n\nPlease download a ROM set and extract it into the \"roms\" directory."));
return 6;
}
pc_reset_hard_init();
/* Set the PAUSE mode depending on the renderer. */
// plat_pause(0);
SDL_AddTimer(1000, timer_onesec, nullptr);
/* Initialize the rendering window, or fullscreen. */
QTimer::singleShot(50, []() { plat_resize(640, 480); } );
auto main_thread = std::thread([] {
main_thread_fn();
});
auto ret = app.exec();
cpu_thread_run = 0;
main_thread.join();
return ret;
}

729
src/qt/qt_mainwindow.cpp Normal file
View File

@@ -0,0 +1,729 @@
#include "qt_mainwindow.hpp"
#include "ui_qt_mainwindow.h"
#include <qguiapplication.h>
extern "C" {
#include <86box/86box.h>
#include <86box/config.h>
#include <86box/keyboard.h>
#include <86box/plat.h>
#include <86box/video.h>
#include "qt_sdl.h"
};
#include <QWindow>
#include <QDebug>
#include <QTimer>
#include <QThread>
#include <QKeyEvent>
#include <QMessageBox>
#include <array>
#include "qt_settings.hpp"
#include "qt_gleswidget.hpp"
#include "qt_machinestatus.hpp"
#ifdef __unix__
#include <X11/Xlib.h>
#include <X11/keysym.h>
#endif
extern void qt_mouse_poll();
extern void qt_mouse_capture(int);
extern "C" void qt_blit(int x, int y, int w, int h);
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
Q_INIT_RESOURCE(qt_resources);
status = std::make_unique<MachineStatus>(this);
ui->setupUi(this);
video_setblit(qt_blit);
//ui->glesWidget->setMouseTracking(true);
connect(this, &MainWindow::showMessageForNonQtThread, this, &MainWindow::showMessage_, Qt::BlockingQueuedConnection);
connect(this, &MainWindow::setTitleForNonQtThread, this, &MainWindow::setTitle_, Qt::BlockingQueuedConnection);
connect(this, &MainWindow::getTitleForNonQtThread, this, &MainWindow::getTitle_, Qt::BlockingQueuedConnection);
connect(this, &MainWindow::pollMouse, ui->glesWidget, &GLESWidget::qt_mouse_poll);
connect(this, &MainWindow::setMouseCapture, this, [this](bool state) {
mouse_capture = state ? 1 : 0;
qt_mouse_capture(mouse_capture);
if (mouse_capture) {
ui->glesWidget->grabMouse();
#ifdef WAYLAND
if (QGuiApplication::platformName().contains("wayland")) {
wl_mouse_capture(this->windowHandle());
}
#endif
} else {
ui->glesWidget->releaseMouse();
#ifdef WAYLAND
if (QGuiApplication::platformName().contains("wayland")) {
wl_mouse_uncapture();
}
#endif
}
});
connect(this, &MainWindow::resizeContents, this, [this](int w, int h) {
ui->glesWidget->resize(w, h);
resize(w, h + menuBar()->height() + statusBar()->height());
});
connect(ui->menubar, &QMenuBar::triggered, this, [] {
config_save();
});
connect(this, &MainWindow::updateStatusBarPanes, this, [this] {
status->refresh(ui->statusbar);
});
connect(this, &MainWindow::updateStatusBarActivity, this, [this](int i, bool b) {
status->setActivity(i, b);
});
connect(this, &MainWindow::updateStatusBarEmpty, this, [this](int i, bool b) {
status->setEmpty(i, b);
});
ui->actionKeyboard_requires_capture->setChecked(kbd_req_capture);
ui->actionRight_CTRL_is_left_ALT->setChecked(rctrl_is_lalt);
}
MainWindow::~MainWindow() {
//sdl_close();
startblit();
//delete hw_widget;
delete ui;
}
void MainWindow::on_actionKeyboard_requires_capture_triggered() {
kbd_req_capture ^= 1;
}
void MainWindow::on_actionRight_CTRL_is_left_ALT_triggered() {
rctrl_is_lalt ^= 1;
}
void MainWindow::on_actionHard_Reset_triggered() {
pc_reset_hard();
}
void MainWindow::on_actionCtrl_Alt_Del_triggered() {
pc_send_cad();
}
void MainWindow::on_actionCtrl_Alt_Esc_triggered() {
pc_send_cae();
}
void MainWindow::on_actionPause_triggered() {
plat_pause(dopause ^ 1);
}
void MainWindow::on_actionExit_triggered() {
close();
}
void MainWindow::on_actionSettings_triggered() {
int currentPause = dopause;
plat_pause(1);
Settings settings(this);
settings.setModal(true);
settings.setWindowModality(Qt::WindowModal);
settings.exec();
switch (settings.result()) {
case QDialog::Accepted:
/*
pc_reset_hard_close();
settings.save();
config_changed = 2;
pc_reset_hard_init();
*/
settings.save();
config_changed = 2;
pc_reset_hard();
break;
case QDialog::Rejected:
break;
}
plat_pause(currentPause);
}
std::array<uint32_t, 256> x11_to_xt_base
{
0,
0,
0,
0,
0,
0,
0,
0,
0,
0x01,
0x02,
0x03,
0x04,
0x05,
0x06,
0x07,
0x08,
0x09,
0x0A,
0x0B,
0x0C,
0x0D,
0x0E,
0x0F,
0x10,
0x11,
0x12,
0x13,
0x14,
0x15,
0x16,
0x17,
0x18,
0x19,
0x1A,
0x1B,
0x1C,
0x1D,
0x1E,
0x1F,
0x20,
0x21,
0x22,
0x23,
0x24,
0x25,
0x26,
0x27,
0x28,
0x29,
0x2A,
0x2B,
0x2C,
0x2D,
0x2E,
0x2F,
0x30,
0x31,
0x32,
0x33,
0x34,
0x35,
0x36,
0x37,
0x38,
0x39,
0x3A,
0x3B,
0x3C,
0x3D,
0x3E,
0x3F,
0x40,
0x41,
0x42,
0x43,
0x44,
0x45,
0x46,
0x47,
0x48,
0x49,
0x4A,
0x4B,
0x4C,
0x4D,
0x4E,
0x4F,
0x50,
0x51,
0x52,
0x53,
0x54,
0x55,
0x56,
0x57,
0x58,
0x147,
0x148,
0x149,
0,
0x14B,
0,
0x14D,
0x14F,
0x150,
0x151,
0x152,
0x153,
0x11C,
0x11D,
0, // Pause/Break key.
0x137,
0x135,
0x138,
0, // Ditto as above comment.
0x15B,
0x15C,
0x15D,
};
std::array<uint32_t, 256> x11_to_xt_2
{
0,
0,
0,
0,
0,
0,
0,
0,
0,
0x01,
0x02,
0x03,
0x04,
0x05,
0x06,
0x07,
0x08,
0x09,
0x0A,
0x0B,
0x0C,
0x0D,
0x0E,
0x0F,
0x10,
0x11,
0x12,
0x13,
0x14,
0x15,
0x16,
0x17,
0x18,
0x19,
0x1A,
0x1B,
0x1C,
0x1D,
0x1E,
0x1F,
0x20,
0x21,
0x22,
0x23,
0x24,
0x25,
0x26,
0x27,
0x28,
0x29,
0x2A,
0x2B,
0x2C,
0x2D,
0x2E,
0x2F,
0x30,
0x31,
0x32,
0x33,
0x34,
0x35,
0x36,
0x37,
0x38,
0x39,
0x3A,
0x3B,
0x3C,
0x3D,
0x3E,
0x3F,
0x40,
0x41,
0x42,
0x43,
0x44,
0x45,
0x46,
0x47,
0x48,
0x49,
0x4A,
0x4B,
0x4C,
0x4D,
0x4E,
0x4F,
0x50,
0x51,
0x52,
0x53,
0x54,
0x55,
0x56,
0x57,
0x58,
0,
0,
0,
0,
0,
0,
0,
0x11C,
0x11D,
0x135,
0x137,
0x138,
0,
0x147,
0x148,
0x149,
0x14B,
0x14D,
0x14F,
0x150,
0x151,
0x152,
0x153
};
std::array<uint32_t, 256> x11_to_xt_vnc
{
0,
0,
0,
0,
0,
0,
0,
0,
0,
0x1D,
0x11D,
0x2A,
0x36,
0,
0,
0x38,
0x138,
0x39,
0x0B,
0x02,
0x03,
0x04,
0x05,
0x06,
0x07,
0x08,
0x09,
0x0A,
0x0C,
0x0D,
0x1A,
0x1B,
0x27,
0x28,
0x29,
0x33,
0x34,
0x35,
0x2B,
0x1E,
0x30,
0x2E,
0x20,
0x12,
0x21,
0x22,
0x23,
0x17,
0x24,
0x25,
0x26,
0x32,
0x31,
0x18,
0x19,
0x10,
0x13,
0x1F,
0x14,
0x16,
0x2F,
0x11,
0x2D,
0x15,
0x2C,
0x0E,
0x1C,
0x0F,
0x01,
0x153,
0x147,
0x14F,
0x149,
0x151,
0x148,
0x150,
0x14B,
0x14D,
};
std::array<uint32_t, 256> darwin_to_xt
{
0x1E,
0x1F,
0x20,
0x21,
0x23,
0x22,
0x2C,
0x2D,
0x2E,
0x2F,
0x2B,
0x30,
0x10,
0x11,
0x12,
0x13,
0x15,
0x14,
0x02,
0x03,
0x04,
0x05,
0x07,
0x06,
0x0D,
0x0A,
0x08,
0x0C,
0x09,
0x0B,
0x1B,
0x18,
0x16,
0x1A,
0x17,
0x19,
0x1C,
0x26,
0x24,
0x28,
0x25,
0x27,
0x2B,
0x33,
0x35,
0x31,
0x32,
0x34,
0x0F,
0x39,
0x29,
0x0E,
0x11C,
0x01,
0x15C,
0x15B,
0x2A,
0x3A,
0x38,
0x1D,
0x36,
0x138,
0x11D,
0x15C,
0,
0x53,
0,
0x37,
0,
0x4E,
0,
0x45,
0x130,
0x12E,
0x120,
0x135,
0x11C,
0,
0x4A,
0,
0,
0,
0x52,
0x4F,
0x50,
0x51,
0x4B,
0x4C,
0x4D,
0x47,
0,
0x48,
0x49,
0,
0,
0,
0x3F,
0x40,
0x41,
0x3D,
0x42,
0x43,
0,
0x57,
0,
0x137,
0,
0x46,
0,
0x44,
0x15D,
0x58,
0,
0, // Pause/Break key.
0x152,
0x147,
0x149,
0x153,
0x3E,
0x14F,
0x3C,
0x151,
0x3B,
0x14B,
0x14D,
0x150,
0x148,
0,
};
static std::array<uint32_t, 256>& selected_keycode = x11_to_xt_base;
uint16_t x11_keycode_to_keysym(uint32_t keycode)
{
#ifdef __APPLE__
return darwin_to_xt[keycode];
#else
static Display* x11display = nullptr;
if (QApplication::platformName().contains("wayland"))
{
selected_keycode = x11_to_xt_2;
}
else if (!x11display)
{
x11display = XOpenDisplay(nullptr);
if (XKeysymToKeycode(x11display, XK_Home) == 110)
{
selected_keycode = x11_to_xt_2;
}
else if (XKeysymToKeycode(x11display, XK_Home) == 69)
{
selected_keycode = x11_to_xt_vnc;
}
}
return selected_keycode[keycode];
#endif
}
void MainWindow::on_actionFullscreen_triggered() {
if (video_fullscreen > 0) {
showNormal();
video_fullscreen = 0;
} else {
showFullScreen();
video_fullscreen = 1;
}
}
void MainWindow::setTitle_(const wchar_t *title)
{
this->setWindowTitle(QString::fromWCharArray(title));
}
void MainWindow::setTitle(const wchar_t *title)
{
if (QThread::currentThread() == this->thread()) {
setTitle_(title);
} else {
emit setTitleForNonQtThread(title);
}
}
void MainWindow::getTitle_(wchar_t *title)
{
this->windowTitle().toWCharArray(title);
}
void MainWindow::getTitle(wchar_t *title)
{
if (QThread::currentThread() == this->thread()) {
getTitle_(title);
} else {
emit getTitleForNonQtThread(title);
}
}
void MainWindow::showMessage(const QString& header, const QString& message) {
if (QThread::currentThread() == this->thread()) {
showMessage_(header, message);
} else {
emit showMessageForNonQtThread(header, message);
}
}
void MainWindow::showMessage_(const QString &header, const QString &message) {
QMessageBox box(QMessageBox::Warning, header, message, QMessageBox::NoButton, this);
box.exec();
}
void MainWindow::keyPressEvent(QKeyEvent* event)
{
#ifdef __APPLE__
keyboard_input(1, x11_keycode_to_keysym(event->nativeVirtualKey()));
#else
keyboard_input(1, x11_keycode_to_keysym(event->nativeScanCode()));
#endif
if (keyboard_isfsexit()) {
ui->actionFullscreen->trigger();
}
if (keyboard_ismsexit()) {
plat_mouse_capture(0);
}
}
void MainWindow::blitToWidget(int x, int y, int w, int h)
{
ui->glesWidget->qt_real_blit(x, y, w, h);
}
void MainWindow::keyReleaseEvent(QKeyEvent* event)
{
#ifdef __APPLE__
keyboard_input(0, x11_keycode_to_keysym(event->nativeVirtualKey()));
#else
keyboard_input(0, x11_keycode_to_keysym(event->nativeScanCode()));
#endif
}

62
src/qt/qt_mainwindow.hpp Normal file
View File

@@ -0,0 +1,62 @@
#ifndef QT_MAINWINDOW_HPP
#define QT_MAINWINDOW_HPP
#include <QMainWindow>
#include <QLabel>
#include <QEvent>
namespace Ui {
class MainWindow;
}
class MachineStatus;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
void showMessage(const QString& header, const QString& message);
void setTitle(const wchar_t* title);
void getTitle(wchar_t* title);
void blitToWidget(int x, int y, int w, int h);
signals:
void paint(const QImage& image);
void resizeContents(int w, int h);
void pollMouse();
void updateStatusBarPanes();
void updateStatusBarActivity(int tag, bool active);
void updateStatusBarEmpty(int tag, bool empty);
void setFullscreen(bool state);
void setMouseCapture(bool state);
void showMessageForNonQtThread(const QString& header, const QString& message);
void setTitleForNonQtThread(const wchar_t* title);
void getTitleForNonQtThread(wchar_t* title);
private slots:
void on_actionFullscreen_triggered();
void on_actionSettings_triggered();
void on_actionExit_triggered();
void on_actionPause_triggered();
void on_actionCtrl_Alt_Del_triggered();
void on_actionCtrl_Alt_Esc_triggered();
void on_actionHard_Reset_triggered();
void on_actionRight_CTRL_is_left_ALT_triggered();
void on_actionKeyboard_requires_capture_triggered();
void showMessage_(const QString& header, const QString& message);
void setTitle_(const wchar_t* title);
void getTitle_(wchar_t* title);
protected:
void keyPressEvent(QKeyEvent* event) override;
void keyReleaseEvent(QKeyEvent* event) override;
private:
Ui::MainWindow *ui;
std::unique_ptr<MachineStatus> status;
};
#endif // QT_MAINWINDOW_HPP

153
src/qt/qt_mainwindow.ui Normal file
View File

@@ -0,0 +1,153 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>724</width>
<height>427</height>
</rect>
</property>
<property name="windowTitle">
<string>86Box</string>
</property>
<widget class="QWidget" name="centralwidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="GLESWidget" name="glesWidget"/>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>724</width>
<height>20</height>
</rect>
</property>
<widget class="QMenu" name="menuAction">
<property name="title">
<string>Action</string>
</property>
<addaction name="actionKeyboard_requires_capture"/>
<addaction name="actionRight_CTRL_is_left_ALT"/>
<addaction name="separator"/>
<addaction name="actionHard_Reset"/>
<addaction name="actionCtrl_Alt_Del"/>
<addaction name="separator"/>
<addaction name="actionCtrl_Alt_Esc"/>
<addaction name="separator"/>
<addaction name="actionPause"/>
<addaction name="separator"/>
<addaction name="actionExit"/>
</widget>
<widget class="QMenu" name="menuTools">
<property name="title">
<string>Tools</string>
</property>
<addaction name="actionSettings"/>
</widget>
<widget class="QMenu" name="menuView">
<property name="title">
<string>View</string>
</property>
<addaction name="actionFullscreen"/>
</widget>
<addaction name="menuAction"/>
<addaction name="menuView"/>
<addaction name="menuTools"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="actionKeyboard_requires_capture">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Keyboard requires capture</string>
</property>
</action>
<action name="actionRight_CTRL_is_left_ALT">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Right CTRL is left ALT</string>
</property>
</action>
<action name="actionHard_Reset">
<property name="text">
<string>Hard Reset</string>
</property>
</action>
<action name="actionCtrl_Alt_Del">
<property name="text">
<string>Ctrl+Alt+Del</string>
</property>
</action>
<action name="actionCtrl_Alt_Esc">
<property name="text">
<string>Ctrl+Alt+Esc</string>
</property>
</action>
<action name="actionPause">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Pause</string>
</property>
</action>
<action name="actionExit">
<property name="text">
<string>Exit</string>
</property>
</action>
<action name="actionSettings">
<property name="text">
<string>Settings</string>
</property>
</action>
<action name="actionFullscreen">
<property name="text">
<string>Fullscreen</string>
</property>
<property name="shortcut">
<string>Ctrl+Alt+PgUp</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>GLESWidget</class>
<extends>QOpenGLWidget</extends>
<header>qt_gleswidget.hpp</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

32
src/qt/qt_midi.cpp Normal file
View File

@@ -0,0 +1,32 @@
#include <cstdint>
extern "C" {
void plat_midi_play_msg(uint8_t *msg)
{}
void plat_midi_play_sysex(uint8_t *sysex, unsigned int len)
{}
void plat_midi_input_init(void)
{}
void plat_midi_input_close(void)
{}
int plat_midi_write(uint8_t val)
{ return 0; }
void plat_midi_init()
{}
void plat_midi_close()
{}
int plat_midi_get_num_devs()
{ return 0; }
int plat_midi_in_get_num_devs(void)
{ return 0; }
}

View File

@@ -0,0 +1,15 @@
#include "qt_models_common.hpp"
#include <QAbstractItemModel>
int Models::AddEntry(QAbstractItemModel *model, const QString& displayRole, int userRole)
{
int row = model->rowCount();
model->insertRow(row);
auto idx = model->index(row, 0);
model->setData(idx, displayRole, Qt::DisplayRole);
model->setData(idx, userRole, Qt::UserRole);
return row;
}

View File

@@ -0,0 +1,8 @@
#pragma once
class QString;
class QAbstractItemModel;
namespace Models
{
int AddEntry(QAbstractItemModel* model, const QString& displayRole, int userRole);
};

393
src/qt/qt_platform.cpp Normal file
View File

@@ -0,0 +1,393 @@
#include <cstdio>
#include <mutex>
#include <memory>
#include <algorithm>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QTemporaryFile>
#include <QCoreApplication>
#include <QLibrary>
#include <QElapsedTimer>
#ifdef Q_OS_UNIX
#include <sys/mman.h>
#endif
// static QByteArray buf;
extern QElapsedTimer elapsed_timer;
QElapsedTimer elapsed_timer;
static std::mutex blitmx;
class CharPointer {
public:
CharPointer(char* buf, int size) : b(buf), s(size) {}
CharPointer& operator=(const QByteArray &ba) {
if (s > 0) {
strncpy(b, ba.data(), s-1);
b[s] = 0;
} else {
// if we haven't been told the length of b, just assume enough
// because we didn't get it from emulator code
strcpy(b, ba.data());
b[ba.size()] = 0;
}
return *this;
}
private:
char* b;
int s;
};
extern "C" {
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/gameport.h>
#include <86box/plat_dynld.h>
#include <86box/config.h>
#include <86box/ui.h>
#include "../cpu/cpu.h"
#include <86box/plat.h>
volatile int cpu_thread_run = 1;
int mouse_capture = 0;
int fixed_size_x = 640;
int fixed_size_y = 480;
int rctrl_is_lalt = 0;
int update_icons = 0;
int kbd_req_capture = 0;
int hide_status_bar = 0;
uint32_t lang_id = 0x0409, lang_sys = 0x0409; // Multilangual UI variables, for now all set to LCID of en-US
plat_joystick_t plat_joystick_state[MAX_PLAT_JOYSTICKS];
joystick_t joystick_state[MAX_JOYSTICKS];
int stricmp(const char* s1, const char* s2)
{
return QByteArray(s1).compare(s2, Qt::CaseInsensitive);
}
int strnicmp(const char *s1, const char *s2, size_t n)
{
QByteArray b1(s1, std::min(strlen(s1), n));
QByteArray b2(s2, std::min(strlen(s2), n));
return b1.compare(b2, Qt::CaseInsensitive);
}
void
do_stop(void)
{
QCoreApplication::quit();
}
void plat_get_exe_name(char *s, int size)
{
CharPointer(s, size) = QCoreApplication::applicationFilePath().toUtf8();
}
uint32_t
plat_get_ticks(void)
{
return elapsed_timer.elapsed();
}
uint64_t
plat_timer_read(void)
{
return elapsed_timer.elapsed();
}
FILE *
plat_fopen(const char *path, const char *mode)
{
/*
QString filepath(path);
if (filepath.isEmpty()) {
return nullptr;
}
qWarning() << "plat_fopen" << filepath;
bool ok = false;
QFile file(filepath);
auto mode_len = strlen(mode);
for (size_t i = 0; i < mode_len; ++i) {
switch (mode[i]) {
case 'r':
ok = file.open(QIODevice::ReadOnly);
break;
case 'w':
ok = file.open(QIODevice::ReadWrite);
break;
case 'b':
case 't':
break;
default:
qWarning() << "Unhandled open mode" << mode[i];
}
}
if (ok) {
qDebug() << "filehandle" << file.handle();
QFile returned;
qDebug() << "\t" << returned.open(file.handle(), file.openMode(), QFileDevice::FileHandleFlag::DontCloseHandle);
return fdopen(returned.handle(), mode);
} else {
return nullptr;
}
*/
#ifdef Q_OS_WINDOWS
wchar_t *pathw, *modew;
int len;
FILE *fp;
if (acp_utf8)
return fopen(path, mode);
else {
len = mbstoc16s(NULL, path, 0) + 1;
pathw = malloc(sizeof(wchar_t) * len);
mbstoc16s(pathw, path, len);
len = mbstoc16s(NULL, mode, 0) + 1;
modew = malloc(sizeof(wchar_t) * len);
mbstoc16s(modew, mode, len);
fp = _wfopen(pathw, modew);
free(pathw);
free(modew);
return fp;
}
#endif
#ifdef Q_OS_UNIX
return fopen(path, mode);
#endif
}
FILE *
plat_fopen64(const char *path, const char *mode)
{
return fopen(path, mode);
}
int
plat_dir_create(char *path)
{
return QDir().mkdir(path) ? 0 : -1;
}
int
plat_dir_check(char *path)
{
QFileInfo fi(path);
return fi.isDir() ? 1 : 0;
}
int
plat_getcwd(char *bufp, int max)
{
CharPointer(bufp, max) = QDir::currentPath().toUtf8();
return 0;
}
void
plat_get_dirname(char *dest, const char *path)
{
QFileInfo fi(path);
CharPointer(dest, -1) = fi.dir().path().toUtf8();
}
char *
plat_get_extension(char *s)
{
auto len = strlen(s);
auto idx = QByteArray::fromRawData(s, len).lastIndexOf('.');
if (idx >= 0) {
return s+idx+1;
}
return s+len;
}
char *
plat_get_filename(char *s)
{
auto idx = QByteArray::fromRawData(s, strlen(s)).lastIndexOf(QDir::separator().toLatin1());
if (idx >= 0) {
return s+idx+1;
}
return s;
}
int
plat_path_abs(char *path)
{
QFileInfo fi(path);
return fi.isAbsolute() ? 1 : 0;
}
void
plat_path_slash(char *path)
{
auto len = strlen(path);
auto separator = QDir::separator().toLatin1();
if (path[len-1] != separator) {
path[len] = separator;
path[len+1] = 0;
}
}
void
plat_append_filename(char *dest, const char *s1, const char *s2)
{
strcpy(dest, s1);
plat_path_slash(dest);
strcat(dest, s2);
}
void
plat_tempfile(char *bufp, char *prefix, char *suffix)
{
QString name;
if (prefix != nullptr) {
name.append(QString("%1-").arg(prefix));
}
name.append("XXXXXX");
if (suffix != nullptr) {
name.append(suffix);
}
QTemporaryFile temp(name);
QByteArray buf(bufp);
buf = temp.fileName().toUtf8();
}
void plat_remove(char* path)
{
QFile(path).remove();
}
void *
plat_mmap(size_t size, uint8_t executable)
{
#if defined Q_OS_WINDOWS
return VirtualAlloc(NULL, size, MEM_COMMIT, executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE);
#elif defined Q_OS_UNIX
#if defined Q_OS_DARWIN && defined MAP_JIT
void *ret = mmap(0, size, PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0), MAP_ANON | MAP_PRIVATE | (executable ? MAP_JIT : 0), 0, 0);
#else
void *ret = mmap(0, size, PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0), MAP_ANON | MAP_PRIVATE, 0, 0);
#endif
auto retval = *reinterpret_cast<int*>(ret);
return (retval < 0) ? nullptr : ret;
#endif
}
void
plat_munmap(void *ptr, size_t size)
{
munmap(ptr, size);
}
void
plat_pause(int p)
{
static wchar_t oldtitle[512];
wchar_t title[512];
dopause = p;
if (p) {
wcsncpy(oldtitle, ui_window_title(NULL), sizeof_w(oldtitle) - 1);
wcscpy(title, oldtitle);
wcscat(title, L" - PAUSED -");
ui_window_title(title);
} else {
ui_window_title(oldtitle);
}
}
// because we can't include nvr.h because it's got fields named new
extern int nvr_save(void);
void
plat_power_off(void)
{
confirm_exit = 0;
nvr_save();
config_save();
/* Deduct a sufficiently large number of cycles that no instructions will
run before the main thread is terminated */
cycles -= 99999999;
cpu_thread_run = 0;
}
void set_language(uint32_t id) {
lang_id = id;
}
/* Sets up the program language before initialization. */
uint32_t plat_language_code(char* langcode) {
/* or maybe not */
return 0;
}
/* Converts back the language code to LCID */
void plat_language_code_r(uint32_t lcid, char* outbuf, int len) {
/* or maybe not */
return;
}
void* dynld_module(const char *name, dllimp_t *table)
{
QString libraryName = name;
QFileInfo fi(libraryName);
QStringList removeSuffixes = {"dll", "dylib", "so"};
if (removeSuffixes.contains(fi.suffix())) {
libraryName = fi.completeBaseName();
}
auto lib = std::unique_ptr<QLibrary>(new QLibrary(libraryName));
if (lib->load()) {
for (auto imp = table; imp->name != nullptr; imp++)
{
auto ptr = lib->resolve(imp->name);
if (ptr == nullptr) {
return nullptr;
}
auto imp_ptr = reinterpret_cast<void**>(imp->func);
*imp_ptr = reinterpret_cast<void*>(ptr);
}
} else {
return nullptr;
}
return lib.release();
}
void dynld_close(void *handle)
{
delete reinterpret_cast<QLibrary*>(handle);
}
void joystick_init(void) {}
void joystick_close(void) {}
void joystick_process(void) {}
void startblit()
{
blitmx.lock();
}
void endblit()
{
blitmx.unlock();
}
}

732
src/qt/qt_sdl.c Normal file
View File

@@ -0,0 +1,732 @@
/*
* 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.
*
* Rendering module for libSDL2
*
* NOTE: Given all the problems reported with FULLSCREEN use of SDL,
* we will not use that, but, instead, use a new window which
* coverrs the entire desktop.
*
*
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Michael Dr<44>ing, <michael@drueing.de>
*
* Copyright 2018-2020 Fred N. van Kempen.
* Copyright 2018-2020 Michael Dr<44>ing.
*
* Redistribution and use in source and binary forms, with
* or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the entire
* above notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names
* of its contributors may be used to endorse or promote
* products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <SDL2/SDL.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
/* This #undef is needed because a SDL include header redefines HAVE_STDARG_H. */
#undef HAVE_STDARG_H
#define HAVE_STDARG_H
#include <86box/86box.h>
#include <86box/mouse.h>
#include <86box/keyboard.h>
#include <86box/device.h>
#include <86box/plat.h>
#include <86box/plat_dynld.h>
#include <86box/video.h>
#include <86box/ui.h>
#include <86box/version.h>
#include "qt_sdl.h"
#define RENDERER_FULL_SCREEN 1
#define RENDERER_HARDWARE 2
#define RENDERER_OPENGL 4
static SDL_Window *sdl_win = NULL;
static SDL_Renderer *sdl_render = NULL;
static SDL_Texture *sdl_tex = NULL;
static int sdl_w, sdl_h;
static int sdl_fs, sdl_flags = -1;
static int cur_w, cur_h;
static int cur_ww = 0, cur_wh = 0;
static volatile int sdl_enabled = 0;
static SDL_mutex* sdl_mutex = NULL;
static const uint16_t sdl_to_xt[0x200] =
{
[SDL_SCANCODE_ESCAPE] = 0x01,
[SDL_SCANCODE_1] = 0x02,
[SDL_SCANCODE_2] = 0x03,
[SDL_SCANCODE_3] = 0x04,
[SDL_SCANCODE_4] = 0x05,
[SDL_SCANCODE_5] = 0x06,
[SDL_SCANCODE_6] = 0x07,
[SDL_SCANCODE_7] = 0x08,
[SDL_SCANCODE_8] = 0x09,
[SDL_SCANCODE_9] = 0x0A,
[SDL_SCANCODE_0] = 0x0B,
[SDL_SCANCODE_MINUS] = 0x0C,
[SDL_SCANCODE_EQUALS] = 0x0D,
[SDL_SCANCODE_BACKSPACE] = 0x0E,
[SDL_SCANCODE_TAB] = 0x0F,
[SDL_SCANCODE_Q] = 0x10,
[SDL_SCANCODE_W] = 0x11,
[SDL_SCANCODE_E] = 0x12,
[SDL_SCANCODE_R] = 0x13,
[SDL_SCANCODE_T] = 0x14,
[SDL_SCANCODE_Y] = 0x15,
[SDL_SCANCODE_U] = 0x16,
[SDL_SCANCODE_I] = 0x17,
[SDL_SCANCODE_O] = 0x18,
[SDL_SCANCODE_P] = 0x19,
[SDL_SCANCODE_LEFTBRACKET] = 0x1A,
[SDL_SCANCODE_RIGHTBRACKET] = 0x1B,
[SDL_SCANCODE_RETURN] = 0x1C,
[SDL_SCANCODE_LCTRL] = 0x1D,
[SDL_SCANCODE_A] = 0x1E,
[SDL_SCANCODE_S] = 0x1F,
[SDL_SCANCODE_D] = 0x20,
[SDL_SCANCODE_F] = 0x21,
[SDL_SCANCODE_G] = 0x22,
[SDL_SCANCODE_H] = 0x23,
[SDL_SCANCODE_J] = 0x24,
[SDL_SCANCODE_K] = 0x25,
[SDL_SCANCODE_L] = 0x26,
[SDL_SCANCODE_SEMICOLON] = 0x27,
[SDL_SCANCODE_APOSTROPHE] = 0x28,
[SDL_SCANCODE_GRAVE] = 0x29,
[SDL_SCANCODE_LSHIFT] = 0x2A,
[SDL_SCANCODE_BACKSLASH] = 0x2B,
[SDL_SCANCODE_Z] = 0x2C,
[SDL_SCANCODE_X] = 0x2D,
[SDL_SCANCODE_C] = 0x2E,
[SDL_SCANCODE_V] = 0x2F,
[SDL_SCANCODE_B] = 0x30,
[SDL_SCANCODE_N] = 0x31,
[SDL_SCANCODE_M] = 0x32,
[SDL_SCANCODE_COMMA] = 0x33,
[SDL_SCANCODE_PERIOD] = 0x34,
[SDL_SCANCODE_SLASH] = 0x35,
[SDL_SCANCODE_RSHIFT] = 0x36,
[SDL_SCANCODE_KP_MULTIPLY] = 0x37,
[SDL_SCANCODE_LALT] = 0x38,
[SDL_SCANCODE_SPACE] = 0x39,
[SDL_SCANCODE_CAPSLOCK] = 0x3A,
[SDL_SCANCODE_F1] = 0x3B,
[SDL_SCANCODE_F2] = 0x3C,
[SDL_SCANCODE_F3] = 0x3D,
[SDL_SCANCODE_F4] = 0x3E,
[SDL_SCANCODE_F5] = 0x3F,
[SDL_SCANCODE_F6] = 0x40,
[SDL_SCANCODE_F7] = 0x41,
[SDL_SCANCODE_F8] = 0x42,
[SDL_SCANCODE_F9] = 0x43,
[SDL_SCANCODE_F10] = 0x44,
[SDL_SCANCODE_NUMLOCKCLEAR] = 0x45,
[SDL_SCANCODE_SCROLLLOCK] = 0x46,
[SDL_SCANCODE_HOME] = 0x147,
[SDL_SCANCODE_UP] = 0x148,
[SDL_SCANCODE_PAGEUP] = 0x149,
[SDL_SCANCODE_KP_MINUS] = 0x4A,
[SDL_SCANCODE_LEFT] = 0x14B,
[SDL_SCANCODE_KP_5] = 0x4C,
[SDL_SCANCODE_RIGHT] = 0x14D,
[SDL_SCANCODE_KP_PLUS] = 0x4E,
[SDL_SCANCODE_END] = 0x14F,
[SDL_SCANCODE_DOWN] = 0x150,
[SDL_SCANCODE_PAGEDOWN] = 0x151,
[SDL_SCANCODE_INSERT] = 0x152,
[SDL_SCANCODE_DELETE] = 0x153,
[SDL_SCANCODE_F11] = 0x57,
[SDL_SCANCODE_F12] = 0x58,
[SDL_SCANCODE_KP_ENTER] = 0x11c,
[SDL_SCANCODE_RCTRL] = 0x11d,
[SDL_SCANCODE_KP_DIVIDE] = 0x135,
[SDL_SCANCODE_RALT] = 0x138,
[SDL_SCANCODE_KP_9] = 0x49,
[SDL_SCANCODE_KP_8] = 0x48,
[SDL_SCANCODE_KP_7] = 0x47,
[SDL_SCANCODE_KP_6] = 0x4D,
[SDL_SCANCODE_KP_4] = 0x4B,
[SDL_SCANCODE_KP_3] = 0x51,
[SDL_SCANCODE_KP_2] = 0x50,
[SDL_SCANCODE_KP_1] = 0x4F,
[SDL_SCANCODE_KP_0] = 0x52,
[SDL_SCANCODE_KP_PERIOD] = 0x53,
[SDL_SCANCODE_LGUI] = 0x15B,
[SDL_SCANCODE_RGUI] = 0x15C,
[SDL_SCANCODE_APPLICATION] = 0x15D,
[SDL_SCANCODE_PRINTSCREEN] = 0x137,
[SDL_SCANCODE_NONUSBACKSLASH] = 0x56,
};
typedef struct mouseinputdata
{
int deltax, deltay, deltaz;
int mousebuttons;
} mouseinputdata;
static mouseinputdata mousedata;
// #define ENABLE_SDL_LOG 3
#ifdef ENABLE_SDL_LOG
int sdl_do_log = ENABLE_SDL_LOG;
static void
sdl_log(const char *fmt, ...)
{
va_list ap;
if (sdl_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
#define sdl_log(fmt, ...)
#endif
static void
sdl_integer_scale(double *d, double *g)
{
double ratio;
if (*d > *g) {
ratio = floor(*d / *g);
*d = *g * ratio;
} else {
ratio = ceil(*d / *g);
*d = *g / ratio;
}
}
static void
sdl_stretch(int *w, int *h, int *x, int *y)
{
double hw, gw, hh, gh, dx, dy, dw, dh, gsr, hsr;
hw = (double) sdl_w;
hh = (double) sdl_h;
gw = (double) *w;
gh = (double) *h;
hsr = hw / hh;
switch (video_fullscreen_scale) {
case FULLSCR_SCALE_FULL:
default:
*w = sdl_w;
*h = sdl_h;
*x = 0;
*y = 0;
break;
case FULLSCR_SCALE_43:
case FULLSCR_SCALE_KEEPRATIO:
if (video_fullscreen_scale == FULLSCR_SCALE_43)
gsr = 4.0 / 3.0;
else
gsr = gw / gh;
if (gsr <= hsr) {
dw = hh * gsr;
dh = hh;
} else {
dw = hw;
dh = hw / gsr;
}
dx = (hw - dw) / 2.0;
dy = (hh - dh) / 2.0;
*w = (int) dw;
*h = (int) dh;
*x = (int) dx;
*y = (int) dy;
break;
case FULLSCR_SCALE_INT:
gsr = gw / gh;
if (gsr <= hsr) {
dw = hh * gsr;
dh = hh;
} else {
dw = hw;
dh = hw / gsr;
}
sdl_integer_scale(&dw, &gw);
sdl_integer_scale(&dh, &gh);
dx = (hw - dw) / 2.0;
dy = (hh - dh) / 2.0;
*w = (int) dw;
*h = (int) dh;
*x = (int) dx;
*y = (int) dy;
break;
}
}
static void
sdl_blit(int x, int y, int w, int h)
{
SDL_Rect r_src;
void *pixeldata;
int ret, pitch;
if (!sdl_enabled || (x < 0) || (y < 0) || (w <= 0) || (h <= 0) || (w > 2048) || (h > 2048) || (buffer32 == NULL) || (sdl_render == NULL) || (sdl_tex == NULL)) {
video_blit_complete();
return;
}
SDL_LockMutex(sdl_mutex);
SDL_LockTexture(sdl_tex, 0, &pixeldata, &pitch);
video_copy(pixeldata, &(buffer32->line[y][x]), h * (2048 + 64) * sizeof(uint32_t));
if (screenshots)
video_screenshot((uint32_t *) pixeldata, 0, 0, (2048 + 64));
SDL_UnlockTexture(sdl_tex);
video_blit_complete();
SDL_RenderClear(sdl_render);
r_src.x = 0;
r_src.y = 0;
r_src.w = w;
r_src.h = h;
ret = SDL_RenderCopy(sdl_render, sdl_tex, &r_src, 0);
if (ret)
sdl_log("SDL: unable to copy texture to renderer (%s)\n", sdl_GetError());
SDL_RenderPresent(sdl_render);
SDL_UnlockMutex(sdl_mutex);
}
static void
sdl_destroy_window(void)
{
if (sdl_win != NULL) {
SDL_DestroyWindow(sdl_win);
sdl_win = NULL;
}
}
static void
sdl_destroy_texture(void)
{
if (sdl_tex != NULL) {
SDL_DestroyTexture(sdl_tex);
sdl_tex = NULL;
}
/* SDL_DestroyRenderer also automatically destroys all associated textures. */
if (sdl_render != NULL) {
SDL_DestroyRenderer(sdl_render);
sdl_render = NULL;
}
}
void
sdl_close(void)
{
if (sdl_mutex != NULL)
SDL_LockMutex(sdl_mutex);
/* Unregister our renderer! */
video_setblit(NULL);
if (sdl_enabled)
sdl_enabled = 0;
if (sdl_mutex != NULL) {
SDL_DestroyMutex(sdl_mutex);
sdl_mutex = NULL;
}
sdl_destroy_texture();
sdl_destroy_window();
/* Quit. */
SDL_Quit();
sdl_flags = -1;
}
static void sdl_select_best_hw_driver(void) {
int i;
SDL_RendererInfo renderInfo;
for (i = 0; i < SDL_GetNumRenderDrivers(); ++i) {
SDL_GetRenderDriverInfo(i, &renderInfo);
if (renderInfo.flags & SDL_RENDERER_ACCELERATED) {
SDL_SetHint(SDL_HINT_RENDER_DRIVER, renderInfo.name);
return;
}
}
}
static void
sdl_init_texture(void)
{
if (sdl_flags & RENDERER_HARDWARE) {
sdl_render = SDL_CreateRenderer(sdl_win, -1, SDL_RENDERER_ACCELERATED);
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, video_filter_method ? "1" : "0");
} else {
sdl_render = SDL_CreateRenderer(sdl_win, -1, SDL_RENDERER_SOFTWARE);
}
sdl_tex = SDL_CreateTexture(sdl_render, SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING, (2048 + 64), (2048 + 64));
if (sdl_render == NULL) {
sdl_log("SDL: unable to SDL_CreateRenderer (%s)\n", SDL_GetError());
}
if (sdl_tex == NULL) {
sdl_log("SDL: unable to SDL_CreateTexture (%s)\n", SDL_GetError());
}
}
static void
sdl_reinit_texture(void)
{
if (sdl_flags == -1)
return;
sdl_destroy_texture();
sdl_init_texture();
}
void sdl_set_fs(int fs) {
SDL_SetWindowFullscreen(sdl_win, fs ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
SDL_SetRelativeMouseMode((SDL_bool)mouse_capture);
sdl_fs = fs;
if (fs) {
sdl_flags |= RENDERER_FULL_SCREEN;
} else {
sdl_flags &= ~RENDERER_FULL_SCREEN;
}
sdl_reinit_texture();
}
static int
sdl_init_common(void* win, int flags)
{
wchar_t temp[128];
SDL_version ver;
sdl_log("SDL: init (fs=%d)\n", 0);
/* Get and log the version of the DLL we are using. */
SDL_GetVersion(&ver);
sdl_log("SDL: version %d.%d.%d\n", ver.major, ver.minor, ver.patch);
/* Initialize the SDL system. */
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
sdl_log("SDL: initialization failed (%s)\n", SDL_GetError());
return(0);
}
if (flags & RENDERER_HARDWARE) {
if (flags & RENDERER_OPENGL)
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "OpenGL");
else
sdl_select_best_hw_driver();
}
/* Get the size of the (current) desktop. */
SDL_DisplayMode dm;
if (SDL_GetDesktopDisplayMode(0, &dm) != 0) {
sdl_log("SDL: SDL_GetDesktopDisplayMode failed (%s)\n", SDL_GetError());
return(0);
}
sdl_w = dm.w;
sdl_h = dm.h;
sdl_flags = flags;
sdl_win = SDL_CreateWindow("86Box renderer", 640, 480, 100, 100, sdl_flags);
if (sdl_win == NULL) {
sdl_log("SDL: unable to CreateWindowFrom (%s)\n", SDL_GetError());
}
sdl_init_texture();
sdl_set_fs(video_fullscreen & 1);
/* Make sure we get a clean exit. */
atexit(sdl_close);
/* Register our renderer! */
video_setblit(sdl_blit);
sdl_enabled = 1;
sdl_mutex = SDL_CreateMutex();
return(1);
}
int
sdl_inits(void* win)
{
return sdl_init_common(win, 0);
}
int
sdl_inith(void* win)
{
return sdl_init_common(win, RENDERER_HARDWARE);
}
int
sdl_initho(void* win)
{
return sdl_init_common(win, RENDERER_HARDWARE | RENDERER_OPENGL);
}
int
sdl_pause(void)
{
return(0);
}
void
sdl_resize(int w, int h)
{
int ww = 0, wh = 0;
if (video_fullscreen & 2)
return;
if ((w == cur_w) && (h == cur_h))
return;
SDL_LockMutex(sdl_mutex);
ww = w;
wh = h;
if (sdl_fs) {
// sdl_stretch(&ww, &wh, &wx, &wy);
// MoveWindow(hwndRender, wx, wy, ww, wh, TRUE);
}
cur_w = w;
cur_h = h;
cur_ww = ww;
cur_wh = wh;
SDL_SetWindowSize(sdl_win, cur_ww, cur_wh);
sdl_reinit_texture();
SDL_UnlockMutex(sdl_mutex);
}
void
sdl_enable(int enable)
{
if (sdl_flags == -1)
return;
SDL_LockMutex(sdl_mutex);
sdl_enabled = !!enable;
if (enable == 1) {
SDL_SetWindowSize(sdl_win, cur_ww, cur_wh);
sdl_reinit_texture();
}
SDL_UnlockMutex(sdl_mutex);
}
void
sdl_reload(void)
{
if (sdl_flags & RENDERER_HARDWARE) {
SDL_LockMutex(sdl_mutex);
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, video_filter_method ? "1" : "0");
sdl_reinit_texture();
SDL_UnlockMutex(sdl_mutex);
}
}
static int mouse_inside = 0;
enum sdl_main_status sdl_main() {
int ret = SdlMainOk;
SDL_Rect r_src;
SDL_Event event;
while (SDL_PollEvent(&event))
{
switch(event.type)
{
case SDL_QUIT:
ret = SdlMainQuit;
break;
case SDL_MOUSEWHEEL:
{
if (mouse_capture || video_fullscreen)
{
if (event.wheel.direction == SDL_MOUSEWHEEL_FLIPPED)
{
event.wheel.x *= -1;
event.wheel.y *= -1;
}
mousedata.deltaz = event.wheel.y;
}
break;
}
case SDL_MOUSEMOTION:
{
if (mouse_capture || video_fullscreen)
{
mousedata.deltax += event.motion.xrel;
mousedata.deltay += event.motion.yrel;
}
break;
}
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
{
if ((event.button.button == SDL_BUTTON_LEFT)
&& !(mouse_capture || video_fullscreen)
&& event.button.state == SDL_RELEASED
&& mouse_inside)
{
plat_mouse_capture(1);
break;
}
if (mouse_get_buttons() < 3 && event.button.button == SDL_BUTTON_MIDDLE && !video_fullscreen)
{
plat_mouse_capture(0);
break;
}
if (mouse_capture || video_fullscreen)
{
int buttonmask = 0;
switch(event.button.button)
{
case SDL_BUTTON_LEFT:
buttonmask = 1;
break;
case SDL_BUTTON_RIGHT:
buttonmask = 2;
break;
case SDL_BUTTON_MIDDLE:
buttonmask = 4;
break;
}
if (event.button.state == SDL_PRESSED)
{
mousedata.mousebuttons |= buttonmask;
}
else mousedata.mousebuttons &= ~buttonmask;
}
break;
}
case SDL_RENDER_DEVICE_RESET:
case SDL_RENDER_TARGETS_RESET:
{
sdl_reinit_texture();
break;
}
case SDL_KEYDOWN:
case SDL_KEYUP:
{
uint16_t xtkey = 0;
switch(event.key.keysym.scancode)
{
default:
xtkey = sdl_to_xt[event.key.keysym.scancode];
break;
}
keyboard_input(event.key.state == SDL_PRESSED, xtkey);
}
break;
case SDL_WINDOWEVENT:
{
switch (event.window.event)
{
case SDL_WINDOWEVENT_ENTER:
mouse_inside = 1;
break;
case SDL_WINDOWEVENT_LEAVE:
mouse_inside = 0;
break;
}
}
}
}
if (mouse_capture && keyboard_ismsexit()) {
plat_mouse_capture(0);
}
if (video_fullscreen && keyboard_isfsexit()) {
plat_setfullscreen(0);
}
return ret;
}
void sdl_mouse_capture(int on) {
SDL_SetRelativeMouseMode((SDL_bool)on);
}
void sdl_mouse_poll() {
mouse_x = mousedata.deltax;
mouse_y = mousedata.deltay;
mouse_z = mousedata.deltaz;
mousedata.deltax = mousedata.deltay = mousedata.deltaz = 0;
mouse_buttons = mousedata.mousebuttons;
}

73
src/qt/qt_sdl.h Normal file
View File

@@ -0,0 +1,73 @@
/*
* 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.
*
* Definitions for the libSDL2 rendering module.
*
*
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Michael Drüing, <michael@drueing.de>
*
* Copyright 2018,2019 Fred N. van Kempen.
* Copyright 2018,2019 Michael Drüing.
*
* Redistribution and use in source and binary forms, with
* or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the entire
* above notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names
* of its contributors may be used to endorse or promote
* products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef WIN_SDL_H
# define WIN_SDL_H
extern void* sdl_win_handle;
extern void sdl_close(void);
extern int sdl_inits();
extern int sdl_inith();
extern int sdl_initho();
extern int sdl_pause(void);
extern void sdl_resize(int w, int h);
extern void sdl_enable(int enable);
extern void sdl_set_fs(int fs);
extern void sdl_reload(void);
enum sdl_main_status {
SdlMainOk,
SdlMainQuit,
};
extern enum sdl_main_status sdl_main();
extern void sdl_mouse_capture(int on);
extern void sdl_mouse_poll();
#endif /*WIN_SDL_H*/

131
src/qt/qt_settings.cpp Normal file
View File

@@ -0,0 +1,131 @@
#include "qt_settings.hpp"
#include "ui_qt_settings.h"
#include "qt_settingsmachine.hpp"
#include "qt_settingsdisplay.hpp"
#include "qt_settingsinput.hpp"
#include "qt_settingssound.hpp"
#include "qt_settingsnetwork.hpp"
#include "qt_settingsports.hpp"
#include "qt_settingsstoragecontrollers.hpp"
#include "qt_settingsharddisks.hpp"
#include "qt_settingsfloppycdrom.hpp"
#include "qt_settingsotherremovable.hpp"
#include "qt_settingsotherperipherals.hpp"
#include <QDebug>
class SettingsModel : public QAbstractListModel {
public:
SettingsModel(QObject* parent) : QAbstractListModel(parent) {}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
private:
QStringList pages = {
"Machine",
"Display",
"Input Devices",
"Sound",
"Network",
"Ports (COM & LPT)",
"Storage Controllers",
"Hard Disks",
"Floppy & CD-ROM Drives",
"Other Removable Devices",
"Other Peripherals",
};
QStringList page_icons = {
"machine",
"display",
"input_devices",
"sound",
"network",
"ports",
"storage_controllers",
"hard_disk",
"floppy_and_cdrom_drives",
"other_removable_devices",
"other_peripherals",
};
};
QVariant SettingsModel::data(const QModelIndex &index, int role) const {
Q_ASSERT(checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::ParentIsInvalid));
switch (role) {
case Qt::DisplayRole:
return pages.at(index.row());
case Qt::DecorationRole:
return QIcon(QString(":/settings/win/icons/%1.ico").arg(page_icons[index.row()]));
default:
return {};
}
}
int SettingsModel::rowCount(const QModelIndex &parent) const {
(void) parent;
return pages.size();
}
Settings::Settings(QWidget *parent) :
QDialog(parent),
ui(new Ui::Settings)
{
ui->setupUi(this);
ui->listView->setModel(new SettingsModel(this));
machine = new SettingsMachine(this);
display = new SettingsDisplay(this);
input = new SettingsInput(this);
sound = new SettingsSound(this);
network = new SettingsNetwork(this);
ports = new SettingsPorts(this);
storageControllers = new SettingsStorageControllers(this);
harddisks = new SettingsHarddisks(this);
floppyCdrom = new SettingsFloppyCDROM(this);
otherRemovable = new SettingsOtherRemovable(this);
otherPeripherals = new SettingsOtherPeripherals(this);
ui->stackedWidget->addWidget(machine);
ui->stackedWidget->addWidget(display);
ui->stackedWidget->addWidget(input);
ui->stackedWidget->addWidget(sound);
ui->stackedWidget->addWidget(network);
ui->stackedWidget->addWidget(ports);
ui->stackedWidget->addWidget(storageControllers);
ui->stackedWidget->addWidget(harddisks);
ui->stackedWidget->addWidget(floppyCdrom);
ui->stackedWidget->addWidget(otherRemovable);
ui->stackedWidget->addWidget(otherPeripherals);
connect(machine, &SettingsMachine::currentMachineChanged, display, &SettingsDisplay::onCurrentMachineChanged);
connect(machine, &SettingsMachine::currentMachineChanged, input, &SettingsInput::onCurrentMachineChanged);
connect(machine, &SettingsMachine::currentMachineChanged, sound, &SettingsSound::onCurrentMachineChanged);
connect(machine, &SettingsMachine::currentMachineChanged, network, &SettingsNetwork::onCurrentMachineChanged);
connect(machine, &SettingsMachine::currentMachineChanged, storageControllers, &SettingsStorageControllers::onCurrentMachineChanged);
connect(ui->listView->selectionModel(), &QItemSelectionModel::currentChanged, this, [this](const QModelIndex &current, const QModelIndex &previous) {
ui->stackedWidget->setCurrentIndex(current.row());
});
}
Settings::~Settings()
{
delete ui;
}
void Settings::save() {
machine->save();
display->save();
input->save();
sound->save();
network->save();
ports->save();
storageControllers->save();
harddisks->save();
floppyCdrom->save();
otherRemovable->save();
otherPeripherals->save();
}

46
src/qt/qt_settings.hpp Normal file
View File

@@ -0,0 +1,46 @@
#ifndef QT_SETTINGS_HPP
#define QT_SETTINGS_HPP
#include <QDialog>
namespace Ui {
class Settings;
}
class SettingsMachine;
class SettingsDisplay;
class SettingsInput;
class SettingsSound;
class SettingsNetwork;
class SettingsPorts;
class SettingsStorageControllers;
class SettingsHarddisks;
class SettingsFloppyCDROM;
class SettingsOtherRemovable;
class SettingsOtherPeripherals;
class Settings : public QDialog
{
Q_OBJECT
public:
explicit Settings(QWidget *parent = nullptr);
~Settings();
void save();
private:
Ui::Settings *ui;
SettingsMachine* machine;
SettingsDisplay* display;
SettingsInput* input;
SettingsSound* sound;
SettingsNetwork* network;
SettingsPorts* ports;
SettingsStorageControllers* storageControllers;
SettingsHarddisks* harddisks;
SettingsFloppyCDROM* floppyCdrom;
SettingsOtherRemovable* otherRemovable;
SettingsOtherPeripherals* otherPeripherals;
};
#endif // QT_SETTINGS_HPP

87
src/qt/qt_settings.ui Normal file
View File

@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Settings</class>
<widget class="QDialog" name="Settings">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>900</width>
<height>595</height>
</rect>
</property>
<property name="windowTitle">
<string>86Box Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,3">
<item>
<widget class="QListView" name="listView"/>
</item>
<item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>-1</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Settings</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Settings</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -0,0 +1,105 @@
#include "qt_settingsdisplay.hpp"
#include "ui_qt_settingsdisplay.h"
#include <QDebug>
extern "C" {
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/machine.h>
#include <86box/video.h>
}
#include "qt_deviceconfig.hpp"
#include "qt_models_common.hpp"
SettingsDisplay::SettingsDisplay(QWidget *parent) :
QWidget(parent),
ui(new Ui::SettingsDisplay)
{
ui->setupUi(this);
onCurrentMachineChanged(machine);
}
SettingsDisplay::~SettingsDisplay()
{
delete ui;
}
void SettingsDisplay::save() {
gfxcard = ui->comboBoxVideo->currentData().toInt();
voodoo_enabled = ui->checkBoxVoodoo->isChecked() ? 1 : 0;
}
void SettingsDisplay::onCurrentMachineChanged(int machineId) {
// win_settings_video_proc, WM_INITDIALOG
this->machineId = machineId;
auto* machine = &machines[machineId];
auto* model = ui->comboBoxVideo->model();
auto removeRows = model->rowCount();
int c = 0;
int selectedRow = 0;
while (true) {
/* Skip "internal" if machine doesn't have it. */
if ((c == 1) && !(machine->flags & MACHINE_VIDEO)) {
c++;
continue;
}
const device_t* video_dev = video_card_getdevice(c);
QString name = DeviceConfig::DeviceName(video_dev, video_get_internal_name(c), 1);
if (name.isEmpty()) {
break;
}
if (video_card_available(c) &&
device_is_valid(video_dev, machine->flags)) {
int row = Models::AddEntry(model, name, c);
if (c == gfxcard) {
selectedRow = row - removeRows;
}
}
c++;
}
model->removeRows(0, removeRows);
if (machine->flags & MACHINE_VIDEO_ONLY) {
ui->comboBoxVideo->setEnabled(false);
selectedRow = 1;
} else {
ui->comboBoxVideo->setEnabled(true);
}
ui->comboBoxVideo->setCurrentIndex(selectedRow);
}
void SettingsDisplay::on_pushButtonConfigure_clicked() {
auto* device = video_card_getdevice(ui->comboBoxVideo->currentData().toInt());
DeviceConfig::ConfigureDevice(device);
}
void SettingsDisplay::on_pushButtonConfigureVoodoo_clicked() {
DeviceConfig::ConfigureDevice(&voodoo_device);
}
void SettingsDisplay::on_comboBoxVideo_currentIndexChanged(int index) {
if (index < 0) {
return;
}
int videoCard = ui->comboBoxVideo->currentData().toInt();
ui->pushButtonConfigure->setEnabled(video_card_has_config(videoCard) > 0);
bool machineHasPci = machines[machineId].flags & MACHINE_BUS_PCI;
ui->checkBoxVoodoo->setEnabled(machineHasPci);
if (machineHasPci) {
ui->checkBoxVoodoo->setChecked(voodoo_enabled);
}
ui->pushButtonConfigureVoodoo->setEnabled(machineHasPci && ui->checkBoxVoodoo->isChecked());
}
void SettingsDisplay::on_checkBoxVoodoo_stateChanged(int state) {
ui->pushButtonConfigureVoodoo->setEnabled(state == Qt::Checked);
}

View File

@@ -0,0 +1,34 @@
#ifndef QT_SETTINGSDISPLAY_HPP
#define QT_SETTINGSDISPLAY_HPP
#include <QWidget>
namespace Ui {
class SettingsDisplay;
}
class SettingsDisplay : public QWidget
{
Q_OBJECT
public:
explicit SettingsDisplay(QWidget *parent = nullptr);
~SettingsDisplay();
void save();
public slots:
void onCurrentMachineChanged(int machineId);
private slots:
void on_checkBoxVoodoo_stateChanged(int state);
void on_comboBoxVideo_currentIndexChanged(int index);
void on_pushButtonConfigureVoodoo_clicked();
void on_pushButtonConfigure_clicked();
private:
Ui::SettingsDisplay *ui;
int machineId = 0;
};
#endif // QT_SETTINGSDISPLAY_HPP

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SettingsDisplay</class>
<widget class="QWidget" name="SettingsDisplay">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="1">
<widget class="QComboBox" name="comboBoxVideo"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Video</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="pushButtonConfigure">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="pushButtonConfigureVoodoo">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QCheckBox" name="checkBoxVoodoo">
<property name="text">
<string>Voodoo Graphics</string>
</property>
</widget>
</item>
<item row="2" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,217 @@
#include "qt_settingsfloppycdrom.hpp"
#include "ui_qt_settingsfloppycdrom.h"
extern "C" {
#include <86box/timer.h>
#include <86box/fdd.h>
#include <86box/cdrom.h>
}
#include <QStandardItemModel>
#include "qt_models_common.hpp"
#include "qt_harddrive_common.hpp"
static void setFloppyType(QAbstractItemModel* model, const QModelIndex& idx, int type) {
QIcon icon;
if (type == 0) {
icon = QIcon(":/settings/win/icons/floppy_disabled.ico");
} else if (type >= 1 && type <= 6) {
icon = QIcon(":/settings/win/icons/floppy_525.ico");
} else {
icon = QIcon(":/settings/win/icons/floppy_35.ico");
}
model->setData(idx, fdd_getname(type));
model->setData(idx, type, Qt::UserRole);
model->setData(idx, icon, Qt::DecorationRole);
}
static void setCDROMBus(QAbstractItemModel* model, const QModelIndex& idx, uint8_t bus, uint8_t channel) {
QIcon icon;
switch (bus) {
case CDROM_BUS_DISABLED:
icon = QIcon(":/settings/win/icons/cdrom_disabled.ico");
break;
case CDROM_BUS_ATAPI:
case CDROM_BUS_SCSI:
icon = QIcon(":/settings/win/icons/cdrom.ico");
break;
}
auto i = idx.siblingAtColumn(0);
model->setData(i, Harddrives::BusChannelName(bus, channel));
model->setData(i, bus, Qt::UserRole);
model->setData(i, channel, Qt::UserRole + 1);
model->setData(i, icon, Qt::DecorationRole);
}
static void setCDROMSpeed(QAbstractItemModel* model, const QModelIndex& idx, uint8_t speed) {
auto i = idx.siblingAtColumn(1);
model->setData(i, QString("%1x").arg(speed));
model->setData(i, speed, Qt::UserRole);
}
SettingsFloppyCDROM::SettingsFloppyCDROM(QWidget *parent) :
QWidget(parent),
ui(new Ui::SettingsFloppyCDROM)
{
ui->setupUi(this);
auto* model = ui->comboBoxFloppyType->model();
int i = 0;
while (true) {
QString name = fdd_getname(i);
if (name.isEmpty()) {
break;
}
Models::AddEntry(model, name, i);
++i;
}
model = new QStandardItemModel(0, 3, this);
ui->tableViewFloppy->setModel(model);
model->setHeaderData(0, Qt::Horizontal, "Type");
model->setHeaderData(1, Qt::Horizontal, "Turbo");
model->setHeaderData(2, Qt::Horizontal, "Check BPB");
model->insertRows(0, FDD_NUM);
/* Floppy drives category */
for (int i = 0; i < FDD_NUM; i++) {
auto idx = model->index(i, 0);
int type = fdd_get_type(i);
setFloppyType(model, idx, type);
model->setData(idx.siblingAtColumn(1), fdd_get_turbo(i) > 0 ? "On" : "Off");
model->setData(idx.siblingAtColumn(2), fdd_get_check_bpb(i) > 0 ? "On" : "Off");
}
ui->tableViewFloppy->resizeColumnsToContents();
ui->tableViewFloppy->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
connect(ui->tableViewFloppy->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &SettingsFloppyCDROM::onFloppyRowChanged);
ui->tableViewFloppy->setCurrentIndex(model->index(0, 0));
Harddrives::populateRemovableBuses(ui->comboBoxBus->model());
model = ui->comboBoxSpeed->model();
for (int i = 0; i <= 72; i++) {
Models::AddEntry(model, QString("%1x").arg(i), i);
}
model = new QStandardItemModel(0, 2, this);
ui->tableViewCDROM->setModel(model);
model->setHeaderData(0, Qt::Horizontal, "Bus");
model->setHeaderData(1, Qt::Horizontal, "Speed");
model->insertRows(0, CDROM_NUM);
for (int i = 0; i < CDROM_NUM; i++) {
auto idx = model->index(i, 0);
setCDROMBus(model, idx, cdrom[i].bus_type, cdrom[i].res);
setCDROMSpeed(model, idx.siblingAtColumn(1), cdrom[i].speed);
}
ui->tableViewCDROM->resizeColumnsToContents();
ui->tableViewCDROM->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
connect(ui->tableViewCDROM->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &SettingsFloppyCDROM::onCDROMRowChanged);
ui->tableViewCDROM->setCurrentIndex(model->index(0, 0));
}
SettingsFloppyCDROM::~SettingsFloppyCDROM()
{
delete ui;
}
void SettingsFloppyCDROM::save() {
auto* model = ui->tableViewFloppy->model();
for (int i = 0; i < FDD_NUM; i++) {
fdd_set_type(i, model->index(i, 0).data(Qt::UserRole).toInt());
fdd_set_turbo(i, model->index(i, 1).data() == "On" ? 1 : 0);
fdd_set_check_bpb(i, model->index(i, 2).data() == "On" ? 1 : 0);
}
/* Removable devices category */
model = ui->tableViewCDROM->model();
memset(cdrom, 0, sizeof(cdrom));
for (int i = 0; i < CDROM_NUM; i++) {
cdrom[i].bus_type = model->index(i, 0).data(Qt::UserRole).toUInt();
cdrom[i].res = model->index(i, 0).data(Qt::UserRole + 1).toUInt();
cdrom[i].speed = model->index(i, 1).data(Qt::UserRole).toUInt();
}
}
void SettingsFloppyCDROM::onFloppyRowChanged(const QModelIndex &current) {
int type = current.siblingAtColumn(0).data(Qt::UserRole).toInt();
ui->comboBoxFloppyType->setCurrentIndex(type);
ui->checkBoxTurboTimings->setChecked(current.siblingAtColumn(1).data() == "On");
ui->checkBoxCheckBPB->setChecked(current.siblingAtColumn(2).data() == "On");
}
void SettingsFloppyCDROM::onCDROMRowChanged(const QModelIndex &current) {
uint8_t bus = current.siblingAtColumn(0).data(Qt::UserRole).toUInt();
uint8_t channel = current.siblingAtColumn(0).data(Qt::UserRole + 1).toUInt();
uint8_t speed = current.siblingAtColumn(1).data(Qt::UserRole).toUInt();
ui->comboBoxBus->setCurrentIndex(-1);
auto* model = ui->comboBoxBus->model();
auto match = model->match(model->index(0, 0), Qt::UserRole, bus);
if (! match.isEmpty()) {
ui->comboBoxBus->setCurrentIndex(match.first().row());
}
model = ui->comboBoxChannel->model();
match = model->match(model->index(0, 0), Qt::UserRole, channel);
if (! match.isEmpty()) {
ui->comboBoxChannel->setCurrentIndex(match.first().row());
}
ui->comboBoxSpeed->setCurrentIndex(speed);
}
void SettingsFloppyCDROM::on_checkBoxTurboTimings_stateChanged(int arg1) {
auto idx = ui->tableViewFloppy->selectionModel()->currentIndex();
ui->tableViewFloppy->model()->setData(idx.siblingAtColumn(1), arg1 == Qt::Checked ? "On" : "Off");
}
void SettingsFloppyCDROM::on_checkBoxCheckBPB_stateChanged(int arg1) {
auto idx = ui->tableViewFloppy->selectionModel()->currentIndex();
ui->tableViewFloppy->model()->setData(idx.siblingAtColumn(2), arg1 == Qt::Checked ? "On" : "Off");
}
void SettingsFloppyCDROM::on_comboBoxFloppyType_activated(int index) {
setFloppyType(ui->tableViewFloppy->model(), ui->tableViewFloppy->selectionModel()->currentIndex(), index);
}
void SettingsFloppyCDROM::on_comboBoxBus_currentIndexChanged(int index) {
if (index < 0) {
return;
}
int bus = ui->comboBoxBus->currentData().toInt();
bool enabled = (bus != CDROM_BUS_DISABLED);
ui->comboBoxChannel->setEnabled(enabled);
ui->comboBoxSpeed->setEnabled(enabled);
Harddrives::populateBusChannels(ui->comboBoxChannel->model(), bus);
}
void SettingsFloppyCDROM::on_comboBoxSpeed_activated(int index) {
auto idx = ui->tableViewCDROM->selectionModel()->currentIndex();
setCDROMSpeed(ui->tableViewCDROM->model(), idx.siblingAtColumn(1), index);
}
void SettingsFloppyCDROM::on_comboBoxBus_activated(int) {
setCDROMBus(
ui->tableViewCDROM->model(),
ui->tableViewCDROM->selectionModel()->currentIndex(),
ui->comboBoxBus->currentData().toUInt(),
ui->comboBoxChannel->currentData().toUInt());
}
void SettingsFloppyCDROM::on_comboBoxChannel_activated(int) {
setCDROMBus(
ui->tableViewCDROM->model(),
ui->tableViewCDROM->selectionModel()->currentIndex(),
ui->comboBoxBus->currentData().toUInt(),
ui->comboBoxChannel->currentData().toUInt());
}

View File

@@ -0,0 +1,35 @@
#ifndef QT_SETTINGSFLOPPYCDROM_HPP
#define QT_SETTINGSFLOPPYCDROM_HPP
#include <QWidget>
namespace Ui {
class SettingsFloppyCDROM;
}
class SettingsFloppyCDROM : public QWidget
{
Q_OBJECT
public:
explicit SettingsFloppyCDROM(QWidget *parent = nullptr);
~SettingsFloppyCDROM();
void save();
private slots:
void on_comboBoxChannel_activated(int index);
void on_comboBoxBus_activated(int index);
void on_comboBoxSpeed_activated(int index);
void on_comboBoxBus_currentIndexChanged(int index);
void on_comboBoxFloppyType_activated(int index);
void on_checkBoxCheckBPB_stateChanged(int arg1);
void on_checkBoxTurboTimings_stateChanged(int arg1);
void onFloppyRowChanged(const QModelIndex &current);
void onCDROMRowChanged(const QModelIndex &current);
private:
Ui::SettingsFloppyCDROM *ui;
};
#endif // QT_SETTINGSFLOPPYCDROM_HPP

View File

@@ -0,0 +1,148 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SettingsFloppyCDROM</class>
<widget class="QWidget" name="SettingsFloppyCDROM">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>544</width>
<height>617</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Floppy Drives</string>
</property>
</widget>
</item>
<item>
<widget class="QTableView" name="tableViewFloppy">
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Type</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBoxFloppyType"/>
</item>
<item>
<widget class="QCheckBox" name="checkBoxTurboTimings">
<property name="text">
<string>Turbo Timings</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxCheckBPB">
<property name="text">
<string>Check BPB</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>CD-ROM Drives</string>
</property>
</widget>
</item>
<item>
<widget class="QTableView" name="tableViewCDROM">
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="2">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Channel</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboBoxBus"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Speed</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Bus</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QComboBox" name="comboBoxChannel"/>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="comboBoxSpeed"/>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,225 @@
#include "qt_settingsharddisks.hpp"
#include "ui_qt_settingsharddisks.h"
extern "C" {
#include <86box/86box.h>
#include <86box/hdd.h>
}
#include <QStandardItemModel>
#include "qt_harddiskdialog.hpp"
#include "qt_harddrive_common.hpp"
const int ColumnBus = 0;
const int ColumnFilename = 1;
const int ColumnCylinders = 2;
const int ColumnHeads = 3;
const int ColumnSectors = 4;
const int ColumnSize = 5;
const int DataBus = Qt::UserRole;
const int DataBusChannel = Qt::UserRole + 1;
/*
static void
normalize_hd_list()
{
hard_disk_t ihdd[HDD_NUM];
int i, j;
j = 0;
memset(ihdd, 0x00, HDD_NUM * sizeof(hard_disk_t));
for (i = 0; i < HDD_NUM; i++) {
if (temp_hdd[i].bus != HDD_BUS_DISABLED) {
memcpy(&(ihdd[j]), &(temp_hdd[i]), sizeof(hard_disk_t));
j++;
}
}
memcpy(temp_hdd, ihdd, HDD_NUM * sizeof(hard_disk_t));
}
*/
static QString busChannelName(const QModelIndex& idx) {
return Harddrives::BusChannelName(idx.data(DataBus).toUInt(), idx.data(DataBusChannel).toUInt());
}
static void addRow(QAbstractItemModel* model, hard_disk_t* hd) {
const QString userPath = usr_path;
int row = model->rowCount();
model->insertRow(row);
QString busName = Harddrives::BusChannelName(hd->bus, hd->channel);
model->setData(model->index(row, ColumnBus), busName);
model->setData(model->index(row, ColumnBus), QIcon(":/settings/win/icons/hard_disk.ico"), Qt::DecorationRole);
model->setData(model->index(row, ColumnBus), hd->bus, DataBus);
model->setData(model->index(row, ColumnBus), hd->channel, DataBusChannel);
QString fileName = hd->fn;
if (fileName.startsWith(userPath, Qt::CaseInsensitive)) {
model->setData(model->index(row, ColumnFilename), fileName.mid(userPath.size()));
} else {
model->setData(model->index(row, ColumnFilename), fileName);
}
model->setData(model->index(row, ColumnFilename), fileName, Qt::UserRole);
model->setData(model->index(row, ColumnCylinders), hd->tracks);
model->setData(model->index(row, ColumnHeads), hd->hpc);
model->setData(model->index(row, ColumnSectors), hd->spt);
model->setData(model->index(row, ColumnSize), (hd->tracks * hd->hpc * hd->spt) >> 11);
}
SettingsHarddisks::SettingsHarddisks(QWidget *parent) :
QWidget(parent),
ui(new Ui::SettingsHarddisks)
{
ui->setupUi(this);
QAbstractItemModel* model = new QStandardItemModel(0, 6, this);
model->setHeaderData(ColumnBus, Qt::Horizontal, "Bus");
model->setHeaderData(ColumnFilename, Qt::Horizontal, "File");
model->setHeaderData(ColumnCylinders, Qt::Horizontal, "C");
model->setHeaderData(ColumnHeads, Qt::Horizontal, "H");
model->setHeaderData(ColumnSectors, Qt::Horizontal, "S");
model->setHeaderData(ColumnSize, Qt::Horizontal, "MiB");
ui->tableView->setModel(model);
for (int i = 0; i < HDD_NUM; i++) {
if (hdd[i].bus > 0) {
addRow(model, &hdd[i]);
}
}
ui->tableView->resizeColumnsToContents();
ui->tableView->horizontalHeader()->setSectionResizeMode(ColumnFilename, QHeaderView::Stretch);
auto* tableSelectionModel = ui->tableView->selectionModel();
connect(tableSelectionModel, &QItemSelectionModel::currentRowChanged, this, &SettingsHarddisks::onTableRowChanged);
onTableRowChanged(QModelIndex());
Harddrives::populateBuses(ui->comboBoxBus->model());
on_comboBoxBus_currentIndexChanged(0);
}
SettingsHarddisks::~SettingsHarddisks()
{
delete ui;
}
void SettingsHarddisks::save() {
memset(hdd, 0, sizeof(hdd));
auto* model = ui->tableView->model();
int rows = model->rowCount();
for (int i = 0; i < rows; ++i) {
auto idx = model->index(i, ColumnBus);
hdd[i].bus = idx.data(DataBus).toUInt();
hdd[i].channel = idx.data(DataBusChannel).toUInt();
hdd[i].tracks = idx.siblingAtColumn(ColumnCylinders).data().toUInt();
hdd[i].hpc = idx.siblingAtColumn(ColumnHeads).data().toUInt();
hdd[i].spt = idx.siblingAtColumn(ColumnSectors).data().toUInt();
QByteArray fileName = idx.siblingAtColumn(ColumnFilename).data(Qt::UserRole).toString().toUtf8();
strncpy(hdd[i].fn, fileName.data(), sizeof(hdd[i].fn));
hdd[i].priv = nullptr;
}
}
void SettingsHarddisks::on_comboBoxBus_currentIndexChanged(int index) {
if (index < 0) {
return;
}
auto idx = ui->tableView->selectionModel()->currentIndex();
if (idx.isValid()) {
auto* model = ui->tableView->model();
auto col = idx.siblingAtColumn(ColumnBus);
model->setData(col, ui->comboBoxBus->currentData(Qt::UserRole), DataBus);
model->setData(col, busChannelName(col), Qt::DisplayRole);
}
Harddrives::populateBusChannels(ui->comboBoxChannel->model(), ui->comboBoxBus->currentData().toInt());
}
void SettingsHarddisks::on_comboBoxChannel_currentIndexChanged(int index) {
if (index < 0) {
return;
}
auto idx = ui->tableView->selectionModel()->currentIndex();
if (idx.isValid()) {
auto* model = ui->tableView->model();
auto col = idx.siblingAtColumn(ColumnBus);
model->setData(col, ui->comboBoxChannel->currentData(Qt::UserRole), DataBusChannel);
model->setData(col, busChannelName(col), Qt::DisplayRole);
}
}
void SettingsHarddisks::onTableRowChanged(const QModelIndex &current) {
bool hidden = !current.isValid();
ui->labelBus->setHidden(hidden);
ui->labelChannel->setHidden(hidden);
ui->comboBoxBus->setHidden(hidden);
ui->comboBoxChannel->setHidden(hidden);
uint32_t bus = current.siblingAtColumn(ColumnBus).data(DataBus).toUInt();
uint32_t busChannel = current.siblingAtColumn(ColumnBus).data(DataBusChannel).toUInt();
auto* model = ui->comboBoxBus->model();
auto match = model->match(model->index(0, 0), Qt::UserRole, bus);
if (! match.isEmpty()) {
ui->comboBoxBus->setCurrentIndex(match.first().row());
}
model = ui->comboBoxChannel->model();
match = model->match(model->index(0, 0), Qt::UserRole, busChannel);
if (! match.isEmpty()) {
ui->comboBoxChannel->setCurrentIndex(match.first().row());
}
}
static void addDriveFromDialog(Ui::SettingsHarddisks* ui, const HarddiskDialog& dlg) {
QByteArray fn = dlg.fileName().toUtf8();
hard_disk_t hd;
hd.bus = dlg.bus();
hd.channel = dlg.channel();
hd.tracks = dlg.cylinders();
hd.hpc = dlg.heads();
hd.spt = dlg.sectors();
strncpy(hd.fn, fn.data(), sizeof(hd.fn));
addRow(ui->tableView->model(), &hd);
ui->tableView->resizeColumnsToContents();
ui->tableView->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
}
void SettingsHarddisks::on_pushButtonNew_clicked() {
HarddiskDialog dialog(false, this);
switch (dialog.exec()) {
case QDialog::Accepted:
addDriveFromDialog(ui, dialog);
break;
}
}
void SettingsHarddisks::on_pushButtonExisting_clicked() {
HarddiskDialog dialog(true, this);
switch (dialog.exec()) {
case QDialog::Accepted:
addDriveFromDialog(ui, dialog);
break;
}
}
void SettingsHarddisks::on_pushButtonRemove_clicked() {
auto idx = ui->tableView->selectionModel()->currentIndex();
if (! idx.isValid()) {
return;
}
auto* model = ui->tableView->model();
model->removeRow(idx.row());
}

View File

@@ -0,0 +1,35 @@
#ifndef QT_SETTINGSHARDDISKS_HPP
#define QT_SETTINGSHARDDISKS_HPP
#include <QWidget>
namespace Ui {
class SettingsHarddisks;
}
class SettingsHarddisks : public QWidget
{
Q_OBJECT
public:
explicit SettingsHarddisks(QWidget *parent = nullptr);
~SettingsHarddisks();
void save();
private slots:
void on_comboBoxChannel_currentIndexChanged(int index);
private slots:
void on_pushButtonRemove_clicked();
void on_pushButtonExisting_clicked();
void on_pushButtonNew_clicked();
void on_comboBoxBus_currentIndexChanged(int index);
void onTableRowChanged(const QModelIndex& current);
private:
Ui::SettingsHarddisks *ui;
};
#endif // QT_SETTINGSHARDDISKS_HPP

View File

@@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SettingsHarddisks</class>
<widget class="QWidget" name="SettingsHarddisks">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTableView" name="tableView">
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="labelBus">
<property name="text">
<string>Bus</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBoxBus"/>
</item>
<item>
<widget class="QLabel" name="labelChannel">
<property name="text">
<string>ID</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBoxChannel"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="pushButtonNew">
<property name="text">
<string>New</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonExisting">
<property name="text">
<string>Existing</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonRemove">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

114
src/qt/qt_settingsinput.cpp Normal file
View File

@@ -0,0 +1,114 @@
#include "qt_settingsinput.hpp"
#include "ui_qt_settingsinput.h"
#include <QDebug>
extern "C" {
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/machine.h>
#include <86box/mouse.h>
#include <86box/gameport.h>
}
#include "qt_deviceconfig.hpp"
SettingsInput::SettingsInput(QWidget *parent) :
QWidget(parent),
ui(new Ui::SettingsInput)
{
ui->setupUi(this);
onCurrentMachineChanged(machine);
}
SettingsInput::~SettingsInput()
{
delete ui;
}
void SettingsInput::save() {
mouse_type = ui->comboBoxMouse->currentData().toInt();
joystick_type = ui->comboBoxJoystick->currentData().toInt();
}
void SettingsInput::onCurrentMachineChanged(int machineId) {
// win_settings_video_proc, WM_INITDIALOG
this->machineId = machineId;
const auto* machine = &machines[machineId];
auto* mouseModel = ui->comboBoxMouse->model();
auto removeRows = mouseModel->rowCount();
int selectedRow = 0;
for (int i = 0; i < mouse_get_ndev(); ++i) {
const auto* dev = mouse_get_device(i);
if ((i == MOUSE_TYPE_INTERNAL) && !(machines[machineId].flags & MACHINE_MOUSE)) {
continue;
}
if (device_is_valid(dev, machine->flags) == 0) {
continue;
}
QString name = DeviceConfig::DeviceName(dev, mouse_get_internal_name(i), 0);
int row = mouseModel->rowCount();
mouseModel->insertRow(row);
auto idx = mouseModel->index(row, 0);
mouseModel->setData(idx, name, Qt::DisplayRole);
mouseModel->setData(idx, i, Qt::UserRole);
if (i == mouse_type) {
selectedRow = row - removeRows;
}
}
mouseModel->removeRows(0, removeRows);
ui->comboBoxMouse->setCurrentIndex(selectedRow);
int i = 0;
char* joyName = joystick_get_name(i);
auto* joystickModel = ui->comboBoxJoystick->model();
removeRows = joystickModel->rowCount();
selectedRow = 0;
while (joyName) {
int row = joystickModel->rowCount();
joystickModel->insertRow(row);
auto idx = joystickModel->index(row, 0);
joystickModel->setData(idx, joyName, Qt::DisplayRole);
joystickModel->setData(idx, i, Qt::UserRole);
if (i == joystick_type) {
selectedRow = row - removeRows;
}
++i;
joyName = joystick_get_name(i);
}
joystickModel->removeRows(0, removeRows);
ui->comboBoxJoystick->setCurrentIndex(selectedRow);
}
void SettingsInput::on_comboBoxMouse_currentIndexChanged(int index) {
int mouseId = ui->comboBoxMouse->currentData().toInt();
ui->pushButtonConfigureMouse->setEnabled(mouse_has_config(mouseId) > 0);
}
void SettingsInput::on_comboBoxJoystick_currentIndexChanged(int index) {
int joystickId = ui->comboBoxJoystick->currentData().toInt();
for (int i = 0; i < 4; ++i) {
auto* btn = findChild<QPushButton*>(QString("pushButtonJoystick%1").arg(i+1));
if (btn == nullptr) {
continue;
}
btn->setEnabled(joystick_get_max_joysticks(joystickId) > i);
}
}
void SettingsInput::on_pushButtonConfigureMouse_clicked() {
int mouseId = ui->comboBoxMouse->currentData().toInt();
DeviceConfig::ConfigureDevice(mouse_get_device(mouseId));
}

View File

@@ -0,0 +1,33 @@
#ifndef QT_SETTINGSINPUT_HPP
#define QT_SETTINGSINPUT_HPP
#include <QWidget>
namespace Ui {
class SettingsInput;
}
class SettingsInput : public QWidget
{
Q_OBJECT
public:
explicit SettingsInput(QWidget *parent = nullptr);
~SettingsInput();
void save();
public slots:
void onCurrentMachineChanged(int machineId);
private slots:
void on_pushButtonConfigureMouse_clicked();
void on_comboBoxJoystick_currentIndexChanged(int index);
void on_comboBoxMouse_currentIndexChanged(int index);
private:
Ui::SettingsInput *ui;
int machineId = 0;
};
#endif // QT_SETTINGSINPUT_HPP

134
src/qt/qt_settingsinput.ui Normal file
View File

@@ -0,0 +1,134 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SettingsInput</class>
<widget class="QWidget" name="SettingsInput">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Mouse</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboBoxMouse"/>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="pushButtonConfigureMouse">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Joystick</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="comboBoxJoystick"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="widget_2" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="pushButtonJoystick1">
<property name="text">
<string>Joystick 1...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonJoystick2">
<property name="text">
<string>Joystick 2...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonJoystick3">
<property name="text">
<string>Joystick 3...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonJoystick4">
<property name="text">
<string>Joystick 4...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,348 @@
#include "qt_settingsmachine.hpp"
#include "ui_qt_settingsmachine.h"
#include <QDebug>
#include <QDialog>
#include <QFrame>
#include <QVBoxLayout>
#include <QDialogButtonBox>
extern "C" {
#include "../cpu/cpu.h"
#include <86box/86box.h>
#include <86box/config.h>
#include <86box/device.h>
#include <86box/machine.h>
}
// from nvr.h, which we can't import into CPP code
#define TIME_SYNC_DISABLED 0
#define TIME_SYNC_ENABLED 1
#define TIME_SYNC_UTC 2
#include "qt_deviceconfig.hpp"
#include "qt_models_common.hpp"
/*
class MachineModel : public QAbstractListModel {
public:
MachineModel(QObject* parent) : QAbstractListModel(parent) {}
bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
private:
struct Entry {
QString name;
int id;
};
QList<Entry> entries;
};
bool MachineModel::insertRows(int row, int count, const QModelIndex &parent) {
beginInsertRows(parent, row, row + count - 1);
for (int i = 0; i < count; ++i) {
entries.insert(row, Entry{});
}
endInsertRows();
return true;
}
bool MachineModel::removeRows(int row, int count, const QModelIndex &parent) {
beginRemoveRows(parent, row, row + count - 1);
for (int i = 0; i < count; ++i) {
entries.removeAt(row);
}
endRemoveRows();
return true;
}
QVariant MachineModel::data(const QModelIndex &index, int role) const {
Q_ASSERT(checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::ParentIsInvalid));
switch (role) {
case Qt::DisplayRole:
return entries.at(index.row()).name;
case Qt::UserRole:
return entries.at(index.row()).id;
default:
return {};
}
}
int MachineModel::rowCount(const QModelIndex &parent) const {
(void) parent;
return entries.size();
}
bool MachineModel::setData(const QModelIndex &index, const QVariant &value, int role) {
Entry* entry = nullptr;
if (index.row() < entries.size()) {
entry = &entries[index.row()];
} else if (index.row() == entries.size()) {
entries.append(Entry{});
entry = &entries.back();
}
bool ret = true;
if (entry != nullptr) {
switch (role) {
case Qt::DisplayRole:
entry->name = value.toString();
case Qt::UserRole:
entry->id = value.toInt();
default:
ret = false;
break;
}
}
return ret;
}
*/
SettingsMachine::SettingsMachine(QWidget *parent) :
QWidget(parent),
ui(new Ui::SettingsMachine)
{
ui->setupUi(this);
switch (time_sync) {
case TIME_SYNC_ENABLED:
ui->radioButtonLocalTime->setChecked(true);
break;
case TIME_SYNC_ENABLED | TIME_SYNC_UTC:
ui->radioButtonUTC->setChecked(true);
break;
case TIME_SYNC_DISABLED:
default:
ui->radioButtonDisabled->setChecked(true);
break;
}
auto* waitStatesModel = ui->comboBoxWaitStates->model();
waitStatesModel->insertRows(0, 9);
auto idx = waitStatesModel->index(0, 0);
waitStatesModel->setData(idx, "Default", Qt::DisplayRole);
waitStatesModel->setData(idx, 0, Qt::UserRole);
for (int i = 0; i < 8; ++i) {
idx = waitStatesModel->index(i+1, 0);
waitStatesModel->setData(idx, QString("%1 Wait State(s)").arg(i), Qt::DisplayRole);
waitStatesModel->setData(idx, i+1, Qt::UserRole);
}
int selectedMachineType = 0;
auto* machineTypesModel = ui->comboBoxMachineType->model();
for (int i = 0; i < MACHINE_TYPE_MAX; ++i) {
Models::AddEntry(machineTypesModel, machine_types[i].name, machine_types[i].id);
if (machine_types[i].id == machines[machine].type) {
selectedMachineType = i;
}
}
ui->comboBoxMachineType->setCurrentIndex(selectedMachineType);
}
SettingsMachine::~SettingsMachine() {
delete ui;
}
void SettingsMachine::save() {
machine = ui->comboBoxMachine->currentData().toInt();
cpu_f = const_cast<cpu_family_t*>(&cpu_families[ui->comboBoxCPU->currentData().toInt()]);
cpu = ui->comboBoxSpeed->currentData().toInt();
fpu_type = ui->comboBoxFPU->currentData().toInt();
cpu_use_dynarec = ui->checkBoxDynamicRecompiler->isChecked() ? 1 : 0;
if (machines[machine].ram_granularity < 1024) {
mem_size = ui->spinBoxRAM->value();
} else {
mem_size = ui->spinBoxRAM->value() * 1024;
}
if (ui->comboBoxWaitStates->isEnabled()) {
cpu_waitstates = ui->comboBoxWaitStates->currentData().toInt();
} else {
cpu_waitstates = 0;
}
time_sync = 0;
if (ui->radioButtonLocalTime->isChecked()) {
time_sync = TIME_SYNC_ENABLED;
}
if (ui->radioButtonUTC->isChecked()) {
time_sync = TIME_SYNC_ENABLED | TIME_SYNC_UTC;
}
}
void SettingsMachine::on_comboBoxMachineType_currentIndexChanged(int index) {
auto* model = ui->comboBoxMachine->model();
int removeRows = model->rowCount();
int selectedMachineRow = 0;
for (int i = 0; i < machine_count(); ++i) {
if ((machines[i].type == index) && machine_available(i)) {
int row = Models::AddEntry(model, machines[i].name, i);
if (i == machine) {
selectedMachineRow = row - removeRows;
}
}
}
model->removeRows(0, removeRows);
ui->comboBoxMachine->setCurrentIndex(-1);
ui->comboBoxMachine->setCurrentIndex(selectedMachineRow);
}
void SettingsMachine::on_comboBoxMachine_currentIndexChanged(int index) {
// win_settings_machine_recalc_machine
if (index < 0) {
return;
}
int machineId = ui->comboBoxMachine->currentData().toInt();
const auto* device = machine_getdevice(machineId);
ui->pushButtonConfigure->setEnabled((device != nullptr) && (device->config != nullptr));
auto* modelCpu = ui->comboBoxCPU->model();
int removeRows = modelCpu->rowCount();
int i = 0;
int eligibleRows = 0;
int selectedCpuFamilyRow = 0;
while (cpu_families[i].package != 0) {
if (cpu_family_is_eligible(&cpu_families[i], machineId)) {
Models::AddEntry(modelCpu, QString("%1 %2").arg(cpu_families[i].manufacturer, cpu_families[i].name), i);
if (&cpu_families[i] == cpu_f) {
selectedCpuFamilyRow = eligibleRows;
}
++eligibleRows;
}
++i;
}
modelCpu->removeRows(0, removeRows);
ui->comboBoxCPU->setEnabled(eligibleRows > 1);
ui->comboBoxCPU->setCurrentIndex(-1);
ui->comboBoxCPU->setCurrentIndex(selectedCpuFamilyRow);
auto* machine = &machines[machineId];
if ((machine->ram_granularity < 1024)) {
ui->spinBoxRAM->setMinimum(machine->min_ram);
ui->spinBoxRAM->setMaximum(machine->max_ram);
ui->spinBoxRAM->setSingleStep(machine->ram_granularity);
ui->spinBoxRAM->setSuffix(" KiB");
ui->spinBoxRAM->setValue(mem_size);
} else {
uint maxram;
#if (!(defined __amd64__ || defined _M_X64 || defined __aarch64__ || defined _M_ARM64))
maxram = std::min(machine->max_ram, 2097152U);
#else
maxram = std::min(machine->max_ram, 3145728U);
#endif
ui->spinBoxRAM->setMinimum(machine->min_ram / 1024);
ui->spinBoxRAM->setMaximum(maxram / 1024);
ui->spinBoxRAM->setSingleStep(machine->ram_granularity / 1024);
ui->spinBoxRAM->setSuffix(" MiB");
ui->spinBoxRAM->setValue(mem_size / 1024);
}
ui->spinBoxRAM->setEnabled(machine->min_ram != machine->max_ram);
ui->spinBoxRAM->setEnabled(machine->min_ram != machine->max_ram);
emit currentMachineChanged(machineId);
}
void SettingsMachine::on_comboBoxCPU_currentIndexChanged(int index) {
if (index < 0) {
return;
}
int machineId = ui->comboBoxMachine->currentData().toInt();
int cpuFamilyId = ui->comboBoxCPU->currentData().toInt();
const auto* cpuFamily = &cpu_families[cpuFamilyId];
auto* modelSpeed = ui->comboBoxSpeed->model();
int removeRows = modelSpeed->rowCount();
// win_settings_machine_recalc_cpu_m
int i = 0;
int eligibleRows = 0;
int selectedSpeedRow = 0;
while (cpuFamily->cpus[i].cpu_type != 0) {
if (cpu_is_eligible(cpuFamily, i, machineId)) {
Models::AddEntry(modelSpeed, QString("%1").arg(cpuFamily->cpus[i].name), i);
if (cpu == i) {
selectedSpeedRow = eligibleRows;
}
++eligibleRows;
}
++i;
}
modelSpeed->removeRows(0, removeRows);
ui->comboBoxSpeed->setEnabled(eligibleRows > 1);
ui->comboBoxSpeed->setCurrentIndex(-1);
ui->comboBoxSpeed->setCurrentIndex(selectedSpeedRow);
}
void SettingsMachine::on_comboBoxSpeed_currentIndexChanged(int index) {
if (index < 0) {
return;
}
// win_settings_machine_recalc_cpu
int cpuFamilyId = ui->comboBoxCPU->currentData().toInt();
const auto* cpuFamily = &cpu_families[cpuFamilyId];
int cpuId = ui->comboBoxSpeed->currentData().toInt();
uint cpuType = cpuFamily->cpus[cpuId].cpu_type;
if ((cpuType >= CPU_286) && (cpuType <= CPU_386DX)) {
ui->comboBoxWaitStates->setEnabled(true);
ui->comboBoxWaitStates->setCurrentIndex(cpu_waitstates);
} else {
ui->comboBoxWaitStates->setCurrentIndex(0);
ui->comboBoxWaitStates->setEnabled(false);
}
#ifdef USE_DYNAREC
uint8_t flags = cpuFamily->cpus[cpuId].cpu_flags;
if (! (flags & CPU_SUPPORTS_DYNAREC)) {
ui->checkBoxDynamicRecompiler->setChecked(false);
ui->checkBoxDynamicRecompiler->setEnabled(false);
} else if (flags & CPU_REQUIRES_DYNAREC) {
ui->checkBoxDynamicRecompiler->setChecked(true);
ui->checkBoxDynamicRecompiler->setEnabled(false);
} else {
ui->checkBoxDynamicRecompiler->setChecked(cpu_use_dynarec);
ui->checkBoxDynamicRecompiler->setEnabled(true);
}
#endif
// win_settings_machine_recalc_fpu
auto* modelFpu = ui->comboBoxFPU->model();
int removeRows = modelFpu->rowCount();
int i = 0;
int selectedFpuRow = 0;
for (const char* fpuName = fpu_get_name_from_index(cpuFamily, cpuId, i); fpuName != nullptr; fpuName = fpu_get_name_from_index(cpuFamily, cpuId, ++i)) {
auto fpuType = fpu_get_type_from_index(cpuFamily, cpuId, i);
Models::AddEntry(modelFpu, QString("%1").arg(fpuName), fpuType);
if (fpu_type == fpuType) {
selectedFpuRow = i;
}
}
modelFpu->removeRows(0, removeRows);
ui->comboBoxFPU->setEnabled(modelFpu->rowCount() > 1);
ui->comboBoxFPU->setCurrentIndex(-1);
ui->comboBoxFPU->setCurrentIndex(selectedFpuRow);
}
void SettingsMachine::on_pushButtonConfigure_clicked() {
// deviceconfig_inst_open
int machineId = ui->comboBoxMachine->currentData().toInt();
const auto* device = machine_getdevice(machineId);
DeviceConfig::ConfigureDevice(device);
}

View File

@@ -0,0 +1,41 @@
#ifndef QT_SETTINGSMACHINE_HPP
#define QT_SETTINGSMACHINE_HPP
#include <QWidget>
namespace Ui {
class SettingsMachine;
}
class SettingsMachine : public QWidget
{
Q_OBJECT
public:
explicit SettingsMachine(QWidget *parent = nullptr);
~SettingsMachine();
void save();
signals:
void currentMachineChanged(int machineId);
private slots:
void on_pushButtonConfigure_clicked();
private slots:
void on_comboBoxSpeed_currentIndexChanged(int index);
private slots:
void on_comboBoxCPU_currentIndexChanged(int index);
private slots:
void on_comboBoxMachine_currentIndexChanged(int index);
private slots:
void on_comboBoxMachineType_currentIndexChanged(int index);
private:
Ui::SettingsMachine *ui;
};
#endif // QT_SETTINGSMACHINE_HPP

View File

@@ -0,0 +1,229 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SettingsMachine</class>
<widget class="QWidget" name="SettingsMachine">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>458</width>
<height>390</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QFormLayout" name="formLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Machine Type</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboBoxMachineType"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Machine</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>CPU</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>FPU</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Wait States</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Memory</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="comboBoxFPU"/>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="comboBoxWaitStates"/>
</item>
<item row="6" column="1">
<widget class="QSpinBox" name="spinBoxRAM"/>
</item>
<item row="3" column="1">
<widget class="QWidget" name="widget_2" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QComboBox" name="comboBoxCPU"/>
</item>
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>Speed</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBoxSpeed"/>
</item>
</layout>
</widget>
</item>
<item row="2" column="1">
<widget class="QWidget" name="widget_3" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QComboBox" name="comboBoxMachine"/>
</item>
<item>
<widget class="QPushButton" name="pushButtonConfigure">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxDynamicRecompiler">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>2</horstretch>
<verstretch>2</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Dynamic Recompiler</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Time Synchronization</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QRadioButton" name="radioButtonDisabled">
<property name="text">
<string>Disabled</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButtonLocalTime">
<property name="text">
<string>Enabled (Local Time)</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButtonUTC">
<property name="text">
<string>Enabled (UTC)</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,113 @@
#include "qt_settingsnetwork.hpp"
#include "ui_qt_settingsnetwork.h"
extern "C" {
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/machine.h>
#include <86box/network.h>
}
#include "qt_models_common.hpp"
#include "qt_deviceconfig.hpp"
static void enableElements(Ui::SettingsNetwork *ui) {
int netType = ui->comboBoxNetwork->currentData().toInt();
ui->comboBoxPcap->setEnabled(netType == NET_TYPE_PCAP);
bool adaptersEnabled = netType == NET_TYPE_SLIRP ||
(netType == NET_TYPE_PCAP && ui->comboBoxPcap->currentData().toInt() > 0);
ui->comboBoxAdapter->setEnabled(adaptersEnabled);
ui->pushButtonConfigure->setEnabled(adaptersEnabled && ui->comboBoxAdapter->currentIndex() > 0);
}
SettingsNetwork::SettingsNetwork(QWidget *parent) :
QWidget(parent),
ui(new Ui::SettingsNetwork)
{
ui->setupUi(this);
auto* model = ui->comboBoxNetwork->model();
Models::AddEntry(model, "None", NET_TYPE_NONE);
Models::AddEntry(model, "PCap", NET_TYPE_PCAP);
Models::AddEntry(model, "SLiRP", NET_TYPE_SLIRP);
ui->comboBoxNetwork->setCurrentIndex(network_type);
int selectedRow = 0;
model = ui->comboBoxPcap->model();
QString currentPcapDevice = network_host;
for (int c = 0; c < network_ndev; c++) {
Models::AddEntry(model, network_devs[c].description, c);
if (QString(network_devs[c].device) == currentPcapDevice) {
selectedRow = c;
}
}
ui->comboBoxPcap->setCurrentIndex(-1);
ui->comboBoxPcap->setCurrentIndex(selectedRow);
onCurrentMachineChanged(machine);
enableElements(ui);
}
SettingsNetwork::~SettingsNetwork()
{
delete ui;
}
void SettingsNetwork::save() {
network_type = ui->comboBoxNetwork->currentData().toInt();
memset(network_host, '\0', sizeof(network_host));
strcpy(network_host, network_devs[ui->comboBoxPcap->currentData().toInt()].device);
network_card = ui->comboBoxAdapter->currentData().toInt();
}
void SettingsNetwork::onCurrentMachineChanged(int machineId) {
this->machineId = machineId;
auto* machine = &machines[machineId];
auto* model = ui->comboBoxAdapter->model();
auto removeRows = model->rowCount();
int c = 0;
int selectedRow = 0;
while (true) {
auto name = DeviceConfig::DeviceName(network_card_getdevice(c), network_card_get_internal_name(c), 1);
if (name.isEmpty()) {
break;
}
if (network_card_available(c) && device_is_valid(network_card_getdevice(c), machine->flags)) {
int row = Models::AddEntry(model, name, c);
if (c == network_card) {
selectedRow = row - removeRows;
}
}
c++;
}
model->removeRows(0, removeRows);
ui->comboBoxAdapter->setEnabled(model->rowCount() > 0);
ui->comboBoxAdapter->setCurrentIndex(-1);
ui->comboBoxAdapter->setCurrentIndex(selectedRow);
}
void SettingsNetwork::on_comboBoxNetwork_currentIndexChanged(int index) {
if (index < 0) {
return;
}
enableElements(ui);
}
void SettingsNetwork::on_comboBoxAdapter_currentIndexChanged(int index) {
if (index < 0) {
return;
}
enableElements(ui);
}
void SettingsNetwork::on_pushButtonConfigure_clicked() {
DeviceConfig::ConfigureDevice(network_card_getdevice(ui->comboBoxAdapter->currentData().toInt()));
}

View File

@@ -0,0 +1,33 @@
#ifndef QT_SETTINGSNETWORK_HPP
#define QT_SETTINGSNETWORK_HPP
#include <QWidget>
namespace Ui {
class SettingsNetwork;
}
class SettingsNetwork : public QWidget
{
Q_OBJECT
public:
explicit SettingsNetwork(QWidget *parent = nullptr);
~SettingsNetwork();
void save();
public slots:
void onCurrentMachineChanged(int machineId);
private slots:
void on_pushButtonConfigure_clicked();
void on_comboBoxAdapter_currentIndexChanged(int index);
void on_comboBoxNetwork_currentIndexChanged(int index);
private:
Ui::SettingsNetwork *ui;
int machineId = 0;
};
#endif // QT_SETTINGSNETWORK_HPP

View File

@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SettingsNetwork</class>
<widget class="QWidget" name="SettingsNetwork">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>PCap Device</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Network Adapter</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="comboBoxPcap"/>
</item>
<item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboBoxNetwork"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Network Type</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QComboBox" name="comboBoxAdapter"/>
</item>
<item>
<widget class="QPushButton" name="pushButtonConfigure">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,133 @@
#include "qt_settingsotherperipherals.hpp"
#include "ui_qt_settingsotherperipherals.h"
extern "C" {
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/isamem.h>
#include <86box/isartc.h>
}
#include "qt_deviceconfig.hpp"
#include "qt_models_common.hpp"
SettingsOtherPeripherals::SettingsOtherPeripherals(QWidget *parent) :
QWidget(parent),
ui(new Ui::SettingsOtherPeripherals)
{
ui->setupUi(this);
ui->checkBoxISABugger->setChecked(bugger_enabled > 0 ? true : false);
ui->checkBoxPOSTCard->setChecked(postcard_enabled > 0 ? true : false);
auto* model = ui->comboBoxRTC->model();
int d = 0;
int selectedRow = 0;
while (true) {
QString name = DeviceConfig::DeviceName(isartc_get_device(d), isartc_get_internal_name(d), 0);
if (name.isEmpty()) {
break;
}
int row = Models::AddEntry(model, name, d);
if (d == isartc_type) {
selectedRow = row;
}
++d;
}
ui->comboBoxRTC->setCurrentIndex(selectedRow);
for (int c = 0; c < ISAMEM_MAX; c++) {
auto* cbox = findChild<QComboBox*>(QString("comboBoxCard%1").arg(c + 1));
model = cbox->model();
d = 0;
selectedRow = 0;
while (true) {
QString name = DeviceConfig::DeviceName(isamem_get_device(d), isamem_get_internal_name(d), 0);
if (name.isEmpty()) {
break;
}
int row = Models::AddEntry(model, name, d);
if (d == isartc_type) {
selectedRow = row;
}
++d;
}
cbox->setCurrentIndex(-1);
cbox->setCurrentIndex(selectedRow);
}
}
SettingsOtherPeripherals::~SettingsOtherPeripherals()
{
delete ui;
}
void SettingsOtherPeripherals::save() {
/* Other peripherals category */
bugger_enabled = ui->checkBoxISABugger->isChecked() ? 1 : 0;
postcard_enabled = ui->checkBoxPOSTCard->isChecked() ? 1 : 0;
isartc_type = ui->comboBoxRTC->currentData().toInt();
/* ISA memory boards. */
for (int i = 0; i < ISAMEM_MAX; i++) {
auto* cbox = findChild<QComboBox*>(QString("comboBoxCard%1").arg(i + 1));
isamem_type[i] = cbox->currentData().toInt();
}
}
void SettingsOtherPeripherals::on_comboBoxRTC_currentIndexChanged(int index) {
if (index < 0) {
return;
}
ui->pushButtonConfigureRTC->setEnabled(index != 0);
}
void SettingsOtherPeripherals::on_pushButtonConfigureRTC_clicked() {
DeviceConfig::ConfigureDevice(isartc_get_device(ui->comboBoxRTC->currentData().toInt()));
}
void SettingsOtherPeripherals::on_comboBoxCard1_currentIndexChanged(int index) {
if (index < 0) {
return;
}
ui->pushButtonConfigureCard1->setEnabled(index != 0);
}
void SettingsOtherPeripherals::on_pushButtonConfigureCard1_clicked() {
DeviceConfig::ConfigureDevice(isamem_get_device(ui->comboBoxCard1->currentData().toInt()));
}
void SettingsOtherPeripherals::on_comboBoxCard2_currentIndexChanged(int index) {
if (index < 0) {
return;
}
ui->pushButtonConfigureCard2->setEnabled(index != 0);
}
void SettingsOtherPeripherals::on_pushButtonConfigureCard2_clicked() {
DeviceConfig::ConfigureDevice(isamem_get_device(ui->comboBoxCard2->currentData().toInt()));
}
void SettingsOtherPeripherals::on_comboBoxCard3_currentIndexChanged(int index) {
if (index < 0) {
return;
}
ui->pushButtonConfigureCard3->setEnabled(index != 0);
}
void SettingsOtherPeripherals::on_pushButtonConfigureCard3_clicked() {
DeviceConfig::ConfigureDevice(isamem_get_device(ui->comboBoxCard3->currentData().toInt()));
}
void SettingsOtherPeripherals::on_comboBoxCard4_currentIndexChanged(int index) {
if (index < 0) {
return;
}
ui->pushButtonConfigureCard4->setEnabled(index != 0);
}
void SettingsOtherPeripherals::on_pushButtonConfigureCard4_clicked() {
DeviceConfig::ConfigureDevice(isamem_get_device(ui->comboBoxCard4->currentData().toInt()));
}

View File

@@ -0,0 +1,53 @@
#ifndef QT_SETTINGSOTHERPERIPHERALS_HPP
#define QT_SETTINGSOTHERPERIPHERALS_HPP
#include <QWidget>
namespace Ui {
class SettingsOtherPeripherals;
}
class SettingsOtherPeripherals : public QWidget
{
Q_OBJECT
public:
explicit SettingsOtherPeripherals(QWidget *parent = nullptr);
~SettingsOtherPeripherals();
void save();
private slots:
void on_pushButtonConfigureCard4_clicked();
private slots:
void on_comboBoxCard4_currentIndexChanged(int index);
private slots:
void on_pushButtonConfigureCard3_clicked();
private slots:
void on_comboBoxCard3_currentIndexChanged(int index);
private slots:
void on_pushButtonConfigureCard2_clicked();
private slots:
void on_comboBoxCard2_currentIndexChanged(int index);
private slots:
void on_pushButtonConfigureCard1_clicked();
private slots:
void on_comboBoxCard1_currentIndexChanged(int index);
private slots:
void on_pushButtonConfigureRTC_clicked();
private slots:
void on_comboBoxRTC_currentIndexChanged(int index);
private:
Ui::SettingsOtherPeripherals *ui;
};
#endif // QT_SETTINGSOTHERPERIPHERALS_HPP

View File

@@ -0,0 +1,162 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SettingsOtherPeripherals</class>
<widget class="QWidget" name="SettingsOtherPeripherals">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>421</width>
<height>458</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>ISA RTC</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBoxRTC"/>
</item>
<item>
<widget class="QPushButton" name="pushButtonConfigureRTC">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>ISA Memory Expansion</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="2">
<widget class="QPushButton" name="pushButtonConfigureCard2">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="comboBoxCard2"/>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="pushButtonConfigureCard3">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Card 2</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Card 3</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="pushButtonConfigureCard1">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboBoxCard1"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Card 1</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="comboBoxCard3"/>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="comboBoxCard4"/>
</item>
<item row="3" column="2">
<widget class="QPushButton" name="pushButtonConfigureCard4">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Card 4</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QCheckBox" name="checkBoxISABugger">
<property name="text">
<string>ISABugger Device</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxPOSTCard">
<property name="text">
<string>POST card</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,267 @@
#include "qt_settingsotherremovable.hpp"
#include "ui_qt_settingsotherremovable.h"
extern "C" {
#include <86box/timer.h>
#include <86box/scsi_device.h>
#include <86box/mo.h>
#include <86box/zip.h>
}
#include <QStandardItemModel>
#include "qt_models_common.hpp"
#include "qt_harddrive_common.hpp"
static QString moDriveTypeName(int i) {
return QString("%1 %2 %3").arg(mo_drive_types[i].vendor, mo_drive_types[i].model, mo_drive_types[i].revision);
}
static void setMOBus(QAbstractItemModel* model, const QModelIndex& idx, uint8_t bus, uint8_t channel) {
QIcon icon;
switch (bus) {
case MO_BUS_DISABLED:
icon = QIcon(":/settings/win/icons/mo_disabled.ico");
break;
case MO_BUS_ATAPI:
case MO_BUS_SCSI:
icon = QIcon(":/settings/win/icons/mo.ico");
break;
}
auto i = idx.siblingAtColumn(0);
model->setData(i, Harddrives::BusChannelName(bus, channel));
model->setData(i, bus, Qt::UserRole);
model->setData(i, channel, Qt::UserRole + 1);
model->setData(i, icon, Qt::DecorationRole);
}
static void setMOType(QAbstractItemModel* model, const QModelIndex& idx, uint32_t type) {
auto i = idx.siblingAtColumn(1);
if (idx.siblingAtColumn(0).data(Qt::UserRole).toUInt() == MO_BUS_DISABLED) {
model->setData(i, "None");
} else {
model->setData(i, moDriveTypeName(type));
}
model->setData(i, type, Qt::UserRole);
}
static void setZIPBus(QAbstractItemModel* model, const QModelIndex& idx, uint8_t bus, uint8_t channel) {
QIcon icon;
switch (bus) {
case ZIP_BUS_DISABLED:
icon = QIcon(":/settings/win/icons/zip_disabled.ico");
break;
case ZIP_BUS_ATAPI:
case ZIP_BUS_SCSI:
icon = QIcon(":/settings/win/icons/zip.ico");
break;
}
auto i = idx.siblingAtColumn(0);
model->setData(i, Harddrives::BusChannelName(bus, channel));
model->setData(i, bus, Qt::UserRole);
model->setData(i, channel, Qt::UserRole + 1);
model->setData(i, icon, Qt::DecorationRole);
}
static void setZIPType(QAbstractItemModel* model, const QModelIndex& idx, bool is250) {
auto i = idx.siblingAtColumn(1);
model->setData(i, is250 ? "ZIP 250" : "ZIP 100");
model->setData(i, is250, Qt::UserRole);
}
SettingsOtherRemovable::SettingsOtherRemovable(QWidget *parent) :
QWidget(parent),
ui(new Ui::SettingsOtherRemovable)
{
ui->setupUi(this);
Harddrives::populateRemovableBuses(ui->comboBoxMOBus->model());
auto* model = ui->comboBoxMOType->model();
for (uint32_t i = 0; i < KNOWN_MO_DRIVE_TYPES; i++) {
Models::AddEntry(model, moDriveTypeName(i), i);
}
model = new QStandardItemModel(0, 2, this);
ui->tableViewMO->setModel(model);
model->setHeaderData(0, Qt::Horizontal, "Bus");
model->setHeaderData(1, Qt::Horizontal, "Type");
model->insertRows(0, MO_NUM);
for (int i = 0; i < MO_NUM; i++) {
auto idx = model->index(i, 0);
setMOBus(model, idx, mo_drives[i].bus_type, mo_drives[i].res);
setMOType(model, idx.siblingAtColumn(1), mo_drives[i].type);
}
ui->tableViewMO->resizeColumnsToContents();
ui->tableViewMO->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
connect(ui->tableViewMO->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &SettingsOtherRemovable::onMORowChanged);
ui->tableViewMO->setCurrentIndex(model->index(0, 0));
Harddrives::populateRemovableBuses(ui->comboBoxZIPBus->model());
model = new QStandardItemModel(0, 2, this);
ui->tableViewZIP->setModel(model);
model->setHeaderData(0, Qt::Horizontal, "Bus");
model->setHeaderData(1, Qt::Horizontal, "Type");
model->insertRows(0, ZIP_NUM);
for (int i = 0; i < ZIP_NUM; i++) {
auto idx = model->index(i, 0);
setZIPBus(model, idx, zip_drives[i].bus_type, zip_drives[i].res);
setZIPType(model, idx, zip_drives[i].is_250 > 0);
}
ui->tableViewZIP->resizeColumnsToContents();
ui->tableViewZIP->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
connect(ui->tableViewZIP->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &SettingsOtherRemovable::onZIPRowChanged);
ui->tableViewZIP->setCurrentIndex(model->index(0, 0));
}
SettingsOtherRemovable::~SettingsOtherRemovable()
{
delete ui;
}
void SettingsOtherRemovable::save() {
auto* model = ui->tableViewMO->model();
memset(mo_drives, 0, sizeof(mo_drives));
for (int i = 0; i < MO_NUM; i++) {
mo_drives[i].f = NULL;
mo_drives[i].priv = NULL;
mo_drives[i].bus_type = model->index(i, 0).data(Qt::UserRole).toUInt();
mo_drives[i].res = model->index(i, 0).data(Qt::UserRole + 1).toUInt();
mo_drives[i].type = model->index(i, 1).data(Qt::UserRole).toUInt();
}
model = ui->tableViewZIP->model();
memset(zip_drives, 0, sizeof(zip_drives));
for (int i = 0; i < ZIP_NUM; i++) {
zip_drives[i].f = NULL;
zip_drives[i].priv = NULL;
zip_drives[i].bus_type = model->index(i, 0).data(Qt::UserRole).toUInt();
zip_drives[i].res = model->index(i, 0).data(Qt::UserRole + 1).toUInt();
zip_drives[i].is_250 = model->index(i, 1).data(Qt::UserRole).toBool() ? 1 : 0;
}
}
void SettingsOtherRemovable::onMORowChanged(const QModelIndex &current) {
uint8_t bus = current.siblingAtColumn(0).data(Qt::UserRole).toUInt();
uint8_t channel = current.siblingAtColumn(0).data(Qt::UserRole + 1).toUInt();
uint8_t type = current.siblingAtColumn(1).data(Qt::UserRole).toUInt();
ui->comboBoxMOBus->setCurrentIndex(-1);
auto* model = ui->comboBoxMOBus->model();
auto match = model->match(model->index(0, 0), Qt::UserRole, bus);
if (! match.isEmpty()) {
ui->comboBoxMOBus->setCurrentIndex(match.first().row());
}
model = ui->comboBoxMOChannel->model();
match = model->match(model->index(0, 0), Qt::UserRole, channel);
if (! match.isEmpty()) {
ui->comboBoxMOChannel->setCurrentIndex(match.first().row());
}
ui->comboBoxMOType->setCurrentIndex(type);
}
void SettingsOtherRemovable::onZIPRowChanged(const QModelIndex &current) {
uint8_t bus = current.siblingAtColumn(0).data(Qt::UserRole).toUInt();
uint8_t channel = current.siblingAtColumn(0).data(Qt::UserRole + 1).toUInt();
bool is250 = current.siblingAtColumn(1).data(Qt::UserRole).toBool();
ui->comboBoxZIPBus->setCurrentIndex(-1);
auto* model = ui->comboBoxZIPBus->model();
auto match = model->match(model->index(0, 0), Qt::UserRole, bus);
if (! match.isEmpty()) {
ui->comboBoxZIPBus->setCurrentIndex(match.first().row());
}
model = ui->comboBoxZIPChannel->model();
match = model->match(model->index(0, 0), Qt::UserRole, channel);
if (! match.isEmpty()) {
ui->comboBoxZIPChannel->setCurrentIndex(match.first().row());
}
ui->checkBoxZIP250->setChecked(is250);
}
void SettingsOtherRemovable::on_comboBoxMOBus_currentIndexChanged(int index) {
if (index < 0) {
return;
}
int bus = ui->comboBoxMOBus->currentData().toInt();
bool enabled = (bus != MO_BUS_DISABLED);
ui->comboBoxMOChannel->setEnabled(enabled);
ui->comboBoxMOType->setEnabled(enabled);
Harddrives::populateBusChannels(ui->comboBoxMOChannel->model(), bus);
}
void SettingsOtherRemovable::on_comboBoxMOBus_activated(int) {
setMOBus(
ui->tableViewMO->model(),
ui->tableViewMO->selectionModel()->currentIndex(),
ui->comboBoxMOBus->currentData().toUInt(),
ui->comboBoxMOChannel->currentData().toUInt());
setMOType(
ui->tableViewMO->model(),
ui->tableViewMO->selectionModel()->currentIndex(),
ui->comboBoxMOType->currentData().toUInt());
ui->tableViewMO->resizeColumnsToContents();
ui->tableViewMO->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
}
void SettingsOtherRemovable::on_comboBoxMOChannel_activated(int) {
setMOBus(
ui->tableViewMO->model(),
ui->tableViewMO->selectionModel()->currentIndex(),
ui->comboBoxMOBus->currentData().toUInt(),
ui->comboBoxMOChannel->currentData().toUInt());
}
void SettingsOtherRemovable::on_comboBoxMOType_activated(int) {
setMOType(
ui->tableViewMO->model(),
ui->tableViewMO->selectionModel()->currentIndex(),
ui->comboBoxMOType->currentData().toUInt());
ui->tableViewMO->resizeColumnsToContents();
ui->tableViewMO->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
}
void SettingsOtherRemovable::on_comboBoxZIPBus_currentIndexChanged(int index) {
if (index < 0) {
return;
}
int bus = ui->comboBoxZIPBus->currentData().toInt();
bool enabled = (bus != ZIP_BUS_DISABLED);
ui->comboBoxZIPChannel->setEnabled(enabled);
ui->checkBoxZIP250->setEnabled(enabled);
Harddrives::populateBusChannels(ui->comboBoxZIPChannel->model(), bus);
}
void SettingsOtherRemovable::on_comboBoxZIPBus_activated(int) {
setZIPBus(
ui->tableViewZIP->model(),
ui->tableViewZIP->selectionModel()->currentIndex(),
ui->comboBoxZIPBus->currentData().toUInt(),
ui->comboBoxZIPChannel->currentData().toUInt());
}
void SettingsOtherRemovable::on_comboBoxZIPChannel_activated(int) {
setZIPBus(
ui->tableViewZIP->model(),
ui->tableViewZIP->selectionModel()->currentIndex(),
ui->comboBoxZIPBus->currentData().toUInt(),
ui->comboBoxZIPChannel->currentData().toUInt());
}
void SettingsOtherRemovable::on_checkBoxZIP250_stateChanged(int state) {
setZIPType(
ui->tableViewZIP->model(),
ui->tableViewZIP->selectionModel()->currentIndex(),
state == Qt::Checked);
}

View File

@@ -0,0 +1,52 @@
#ifndef QT_SETTINGSOTHERREMOVABLE_HPP
#define QT_SETTINGSOTHERREMOVABLE_HPP
#include <QWidget>
namespace Ui {
class SettingsOtherRemovable;
}
class SettingsOtherRemovable : public QWidget
{
Q_OBJECT
public:
explicit SettingsOtherRemovable(QWidget *parent = nullptr);
~SettingsOtherRemovable();
void save();
private slots:
void on_checkBoxZIP250_stateChanged(int arg1);
private slots:
void on_comboBoxZIPChannel_activated(int index);
private slots:
void on_comboBoxZIPBus_activated(int index);
private slots:
void on_comboBoxZIPBus_currentIndexChanged(int index);
private slots:
void on_comboBoxMOType_activated(int index);
private slots:
void on_comboBoxMOChannel_activated(int index);
private slots:
void on_comboBoxMOBus_activated(int index);
private slots:
void on_comboBoxMOBus_currentIndexChanged(int index);
private slots:
void onMORowChanged(const QModelIndex &current);
void onZIPRowChanged(const QModelIndex &current);
private:
Ui::SettingsOtherRemovable *ui;
};
#endif // QT_SETTINGSOTHERREMOVABLE_HPP

View File

@@ -0,0 +1,151 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SettingsOtherRemovable</class>
<widget class="QWidget" name="SettingsOtherRemovable">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>418</width>
<height>433</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>MO Drives</string>
</property>
</widget>
</item>
<item>
<widget class="QTableView" name="tableViewMO">
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Bus</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Channel</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboBoxMOBus"/>
</item>
<item row="0" column="3">
<widget class="QComboBox" name="comboBoxMOChannel"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Type</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="3">
<widget class="QComboBox" name="comboBoxMOType"/>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>ZIP Drives</string>
</property>
</widget>
</item>
<item>
<widget class="QTableView" name="tableViewZIP">
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Bus</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBoxZIPBus"/>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Channel</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBoxZIPChannel"/>
</item>
<item>
<widget class="QCheckBox" name="checkBoxZIP250">
<property name="text">
<string>ZIP 250</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,81 @@
#include "qt_settingsports.hpp"
#include "ui_qt_settingsports.h"
extern "C" {
#include <86box/86box.h>
#include <86box/timer.h>
#include <86box/device.h>
#include <86box/machine.h>
#include <86box/lpt.h>
}
#include "qt_deviceconfig.hpp"
#include "qt_models_common.hpp"
SettingsPorts::SettingsPorts(QWidget *parent) :
QWidget(parent),
ui(new Ui::SettingsPorts)
{
ui->setupUi(this);
for (int i = 0; i < 3; i++) {
auto* cbox = findChild<QComboBox*>(QString("comboBoxLpt%1").arg(i+1));
auto* model = cbox->model();
int c = 0;
int selectedRow = 0;
while (true) {
const char* lptName = lpt_device_get_name(c);
if (lptName == nullptr) {
break;
}
Models::AddEntry(model, lptName, c);
if (c == lpt_ports[i].device) {
selectedRow = c;
}
c++;
}
cbox->setCurrentIndex(selectedRow);
auto* checkBox = findChild<QCheckBox*>(QString("checkBoxParallel%1").arg(i+1));
checkBox->setChecked(lpt_ports[i].enabled > 0);
cbox->setEnabled(lpt_ports[i].enabled > 0);
}
for (int i = 0; i < 4; i++) {
auto* checkBox = findChild<QCheckBox*>(QString("checkBoxSerial%1").arg(i+1));
checkBox->setChecked(serial_enabled[i] > 0);
}
}
SettingsPorts::~SettingsPorts()
{
delete ui;
}
void SettingsPorts::save() {
for (int i = 0; i < 3; i++) {
auto* cbox = findChild<QComboBox*>(QString("comboBoxLpt%1").arg(i+1));
auto* checkBox = findChild<QCheckBox*>(QString("checkBoxParallel%1").arg(i+1));
lpt_ports[i].device = cbox->currentData().toInt();
lpt_ports[i].enabled = checkBox->isChecked() ? 1 : 0;
}
for (int i = 0; i < 4; i++) {
auto* checkBox = findChild<QCheckBox*>(QString("checkBoxSerial%1").arg(i+1));
serial_enabled[i] = checkBox->isChecked() ? 1 : 0;
}
}
void SettingsPorts::on_checkBoxParallel1_stateChanged(int state) {
ui->comboBoxLpt1->setEnabled(state == Qt::Checked);
}
void SettingsPorts::on_checkBoxParallel2_stateChanged(int state) {
ui->comboBoxLpt2->setEnabled(state == Qt::Checked);
}
void SettingsPorts::on_checkBoxParallel3_stateChanged(int state) {
ui->comboBoxLpt3->setEnabled(state == Qt::Checked);
}

View File

@@ -0,0 +1,28 @@
#ifndef QT_SETTINGSPORTS_HPP
#define QT_SETTINGSPORTS_HPP
#include <QWidget>
namespace Ui {
class SettingsPorts;
}
class SettingsPorts : public QWidget
{
Q_OBJECT
public:
explicit SettingsPorts(QWidget *parent = nullptr);
~SettingsPorts();
void save();
private slots:
void on_checkBoxParallel3_stateChanged(int arg1);
void on_checkBoxParallel2_stateChanged(int arg1);
void on_checkBoxParallel1_stateChanged(int arg1);
private:
Ui::SettingsPorts *ui;
};
#endif // QT_SETTINGSPORTS_HPP

133
src/qt/qt_settingsports.ui Normal file
View File

@@ -0,0 +1,133 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SettingsPorts</class>
<widget class="QWidget" name="SettingsPorts">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>398</width>
<height>341</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>LPT1 Device</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboBoxLpt1"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>LPT2 Device</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="comboBoxLpt2"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>LPT3 Device</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="comboBoxLpt3"/>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QCheckBox" name="checkBoxSerial1">
<property name="text">
<string>Serial Port 1</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="checkBoxParallel1">
<property name="text">
<string>Parallel Port 1</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="checkBoxSerial2">
<property name="text">
<string>Serial Port 2</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="checkBoxParallel2">
<property name="text">
<string>Parallel Port 2</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="checkBoxSerial3">
<property name="text">
<string>Serial Port 3</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="checkBoxParallel3">
<property name="text">
<string>Parallel Port 3</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="checkBoxSerial4">
<property name="text">
<string>Serial Port 4</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

223
src/qt/qt_settingssound.cpp Normal file
View File

@@ -0,0 +1,223 @@
#include "qt_settingssound.hpp"
#include "ui_qt_settingssound.h"
extern "C" {
#include <86box/86box.h>
#include <86box/timer.h>
#include <86box/device.h>
#include <86box/machine.h>
#include <86box/sound.h>
#include <86box/midi.h>
#include <86box/snd_mpu401.h>
}
#include "qt_deviceconfig.hpp"
#include "qt_models_common.hpp"
SettingsSound::SettingsSound(QWidget *parent) :
QWidget(parent),
ui(new Ui::SettingsSound)
{
ui->setupUi(this);
onCurrentMachineChanged(machine);
}
SettingsSound::~SettingsSound()
{
delete ui;
}
void SettingsSound::save() {
sound_card_current = ui->comboBoxSoundCard->currentData().toInt();
midi_device_current = ui->comboBoxMidiOut->currentData().toInt();
midi_input_device_current = ui->comboBoxMidiIn->currentData().toInt();
mpu401_standalone_enable = ui->checkBoxMPU401->isChecked() ? 1 : 0;
SSI2001 = ui->checkBoxSSI2001->isChecked() ? 1 : 0;;
GAMEBLASTER = ui->checkBoxCMS->isChecked() ? 1 : 0;
GUS = ui->checkBoxGUS->isChecked() ? 1 : 0;;
sound_is_float = ui->checkBoxFloat32->isChecked() ? 1 : 0;;
}
void SettingsSound::onCurrentMachineChanged(int machineId) {
this->machineId = machineId;
auto* machine = &machines[machineId];
auto* model = ui->comboBoxSoundCard->model();
auto removeRows = model->rowCount();
int c = 0;
int selectedRow = 0;
while (true) {
/* Skip "internal" if machine doesn't have it. */
if ((c == 1) && !(machine->flags & MACHINE_SOUND)) {
c++;
continue;
}
auto* sound_dev = sound_card_getdevice(c);
QString name = DeviceConfig::DeviceName(sound_dev, sound_card_get_internal_name(c), 1);
if (name.isEmpty()) {
break;
}
if (sound_card_available(c)) {
if (device_is_valid(sound_dev, machine->flags)) {
int row = Models::AddEntry(model, name, c);
if (c == sound_card_current) {
selectedRow = row - removeRows;
}
}
}
c++;
}
model->removeRows(0, removeRows);
ui->comboBoxSoundCard->setEnabled(model->rowCount() > 0);
ui->comboBoxSoundCard->setCurrentIndex(-1);
ui->comboBoxSoundCard->setCurrentIndex(selectedRow);
model = ui->comboBoxMidiOut->model();
removeRows = model->rowCount();
c = 0;
selectedRow = 0;
while (true) {
QString name = DeviceConfig::DeviceName(midi_device_getdevice(c), midi_device_get_internal_name(c), 0);
if (name.isEmpty()) {
break;
}
if (midi_device_available(c)) {
int row = Models::AddEntry(model, name, c);
if (c == midi_input_device_current) {
selectedRow = row - removeRows;
}
}
c++;
}
model->removeRows(0, removeRows);
ui->comboBoxMidiOut->setEnabled(model->rowCount() > 0);
ui->comboBoxMidiOut->setCurrentIndex(-1);
ui->comboBoxMidiOut->setCurrentIndex(selectedRow);
model = ui->comboBoxMidiIn->model();
removeRows = model->rowCount();
c = 0;
selectedRow = 0;
while (true) {
QString name = DeviceConfig::DeviceName(midi_in_device_getdevice(c), midi_device_get_internal_name(c), 0);
if (name.isEmpty()) {
break;
}
if (midi_in_device_available(c)) {
int row = Models::AddEntry(model, name, c);
if (c == midi_device_current) {
selectedRow = row - removeRows;
}
}
c++;
}
model->removeRows(0, removeRows);
ui->comboBoxMidiIn->setEnabled(model->rowCount() > 0);
ui->comboBoxMidiIn->setCurrentIndex(-1);
ui->comboBoxMidiIn->setCurrentIndex(selectedRow);
ui->checkBoxMPU401->setChecked(mpu401_standalone_enable > 0);
ui->checkBoxSSI2001->setChecked(SSI2001 > 0);
ui->checkBoxCMS->setChecked(GAMEBLASTER > 0);
ui->checkBoxGUS->setChecked(GUS > 0);
ui->checkBoxFloat32->setChecked(sound_is_float > 0);
ui->pushButtonConfigureSSI2001->setEnabled((SSI2001 > 0) && (machine->flags & MACHINE_BUS_ISA));
ui->pushButtonConfigureCMS->setEnabled((GAMEBLASTER > 0) && (machine->flags & MACHINE_BUS_ISA));
ui->pushButtonConfigureGUS->setEnabled((GUS > 0) && (machine->flags & MACHINE_BUS_ISA16));
}
static bool allowMpu401(Ui::SettingsSound *ui) {
QString midiOut = midi_device_get_internal_name(ui->comboBoxMidiOut->currentData().toInt());
QString midiIn = midi_in_device_get_internal_name(ui->comboBoxMidiIn->currentData().toInt());
if (midiOut.isEmpty()) {
return false;
}
if (midiOut == QStringLiteral("none") && midiIn == QStringLiteral("none")) {
return false;
}
return true;
}
void SettingsSound::on_comboBoxSoundCard_currentIndexChanged(int index) {
if (index < 0) {
return;
}
ui->pushButtonConfigureSoundCard->setEnabled(sound_card_has_config(ui->comboBoxSoundCard->currentData().toInt()));
}
void SettingsSound::on_pushButtonConfigureSoundCard_clicked() {
DeviceConfig::ConfigureDevice(sound_card_getdevice(ui->comboBoxSoundCard->currentData().toInt()));
}
void SettingsSound::on_comboBoxMidiOut_currentIndexChanged(int index) {
if (index < 0) {
return;
}
ui->pushButtonConfigureMidiOut->setEnabled(midi_device_has_config(ui->comboBoxMidiOut->currentData().toInt()));
ui->checkBoxMPU401->setEnabled(allowMpu401(ui));
ui->pushButtonConfigureMPU401->setEnabled(allowMpu401(ui) && ui->checkBoxMPU401->isChecked());
}
void SettingsSound::on_pushButtonConfigureMidiOut_clicked() {
DeviceConfig::ConfigureDevice(midi_device_getdevice(ui->comboBoxMidiOut->currentData().toInt()));
}
void SettingsSound::on_comboBoxMidiIn_currentIndexChanged(int index) {
if (index < 0) {
return;
}
ui->pushButtonConfigureMidiIn->setEnabled(midi_in_device_has_config(ui->comboBoxMidiIn->currentData().toInt()));
ui->checkBoxMPU401->setEnabled(allowMpu401(ui));
ui->pushButtonConfigureMPU401->setEnabled(allowMpu401(ui) && ui->checkBoxMPU401->isChecked());
}
void SettingsSound::on_pushButtonConfigureMidiIn_clicked() {
DeviceConfig::ConfigureDevice(midi_in_device_getdevice(ui->comboBoxMidiIn->currentData().toInt()));
}
void SettingsSound::on_checkBoxMPU401_stateChanged(int state) {
ui->pushButtonConfigureMPU401->setEnabled(state == Qt::Checked);
}
void SettingsSound::on_checkBoxSSI2001_stateChanged(int state) {
ui->pushButtonConfigureSSI2001->setEnabled(state == Qt::Checked);
}
void SettingsSound::on_checkBoxCMS_stateChanged(int state) {
ui->pushButtonConfigureCMS->setEnabled(state == Qt::Checked);
}
void SettingsSound::on_checkBoxGUS_stateChanged(int state) {
ui->pushButtonConfigureGUS->setEnabled(state == Qt::Checked);
}
void SettingsSound::on_pushButtonConfigureMPU401_clicked() {
if (machines[machineId].flags & MACHINE_MCA) {
DeviceConfig::ConfigureDevice(&mpu401_mca_device);
} else {
DeviceConfig::ConfigureDevice(&mpu401_device);
}
}
void SettingsSound::on_pushButtonConfigureSSI2001_clicked() {
DeviceConfig::ConfigureDevice(&ssi2001_device);
}
void SettingsSound::on_pushButtonConfigureCMS_clicked() {
DeviceConfig::ConfigureDevice(&cms_device);
}
void SettingsSound::on_pushButtonConfigureGUS_clicked() {
DeviceConfig::ConfigureDevice(&gus_device);
}

View File

@@ -0,0 +1,44 @@
#ifndef QT_SETTINGSSOUND_HPP
#define QT_SETTINGSSOUND_HPP
#include <QWidget>
namespace Ui {
class SettingsSound;
}
class SettingsSound : public QWidget
{
Q_OBJECT
public:
explicit SettingsSound(QWidget *parent = nullptr);
~SettingsSound();
void save();
public slots:
void onCurrentMachineChanged(int machineId);
private slots:
void on_pushButtonConfigureGUS_clicked();
void on_pushButtonConfigureCMS_clicked();
void on_pushButtonConfigureSSI2001_clicked();
void on_pushButtonConfigureMPU401_clicked();
void on_checkBoxGUS_stateChanged(int arg1);
void on_checkBoxCMS_stateChanged(int arg1);
void on_checkBoxSSI2001_stateChanged(int arg1);
void on_checkBoxMPU401_stateChanged(int arg1);
void on_pushButtonConfigureMidiIn_clicked();
void on_pushButtonConfigureMidiOut_clicked();
void on_comboBoxMidiIn_currentIndexChanged(int index);
void on_comboBoxMidiOut_currentIndexChanged(int index);
void on_pushButtonConfigureSoundCard_clicked();
void on_comboBoxSoundCard_currentIndexChanged(int index);
private:
Ui::SettingsSound *ui;
int machineId = 0;
};
#endif // QT_SETTINGSSOUND_HPP

160
src/qt/qt_settingssound.ui Normal file
View File

@@ -0,0 +1,160 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SettingsSound</class>
<widget class="QWidget" name="SettingsSound">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>387</width>
<height>332</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>MIDI In</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QCheckBox" name="checkBoxSSI2001">
<property name="text">
<string>Innovation SSI-2001</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QCheckBox" name="checkBoxGUS">
<property name="text">
<string>Gravis Ultrasound</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Sound Card</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="pushButtonConfigureSoundCard">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="comboBoxMidiIn"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>MIDI Out</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="checkBoxMPU401">
<property name="text">
<string>Standalone MPU-401</string>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QPushButton" name="pushButtonConfigureMPU401">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QPushButton" name="pushButtonConfigureMidiIn">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QPushButton" name="pushButtonConfigureSSI2001">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="checkBoxCMS">
<property name="text">
<string>CMS / Game Blaster</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboBoxSoundCard"/>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="comboBoxMidiOut"/>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="pushButtonConfigureMidiOut">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QCheckBox" name="checkBoxFloat32">
<property name="text">
<string>Use FLOAT32 sound</string>
</property>
</widget>
</item>
<item row="5" column="3">
<widget class="QPushButton" name="pushButtonConfigureCMS">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="6" column="3">
<widget class="QPushButton" name="pushButtonConfigureGUS">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="8" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,233 @@
#include "qt_settingsstoragecontrollers.hpp"
#include "ui_qt_settingsstoragecontrollers.h"
extern "C" {
#include <86box/86box.h>
#include <86box/timer.h>
#include <86box/device.h>
#include <86box/machine.h>
#include <86box/hdc.h>
#include <86box/hdc_ide.h>
#include <86box/fdc_ext.h>
#include <86box/scsi.h>
#include <86box/scsi_device.h>
#include <86box/cassette.h>
}
#include "qt_deviceconfig.hpp"
#include "qt_models_common.hpp"
SettingsStorageControllers::SettingsStorageControllers(QWidget *parent) :
QWidget(parent),
ui(new Ui::SettingsStorageControllers)
{
ui->setupUi(this);
ui->checkBoxCassette->setChecked(cassette_enable > 0);
onCurrentMachineChanged(machine);
}
SettingsStorageControllers::~SettingsStorageControllers()
{
delete ui;
}
void SettingsStorageControllers::save() {
/* Storage devices category */
for (int i = 0; i < SCSI_BUS_MAX; ++i) {
auto* cbox = findChild<QComboBox*>(QString("comboBoxSCSI%1").arg(i+1));
scsi_card_current[i] = cbox->currentData().toInt();
}
hdc_current = ui->comboBoxHD->currentData().toInt();
fdc_type = ui->comboBoxFD->currentData().toInt();
ide_ter_enabled = ui->checkBoxTertiaryIDE->isChecked() ? 1 : 0;
ide_qua_enabled = ui->checkBoxQuaternaryIDE->isChecked() ? 1 : 0;
cassette_enable = ui->checkBoxCassette->isChecked() ? 1 : 0;
}
void SettingsStorageControllers::onCurrentMachineChanged(int machineId) {
this->machineId = machineId;
auto* machine = &machines[machineId];
/*HD controller config*/
auto* model = ui->comboBoxHD->model();
auto removeRows = model->rowCount();
int c = 0;
int selectedRow = 0;
while (true) {
/* Skip "internal" if machine doesn't have it. */
if ((c == 1) && !(machine->flags & MACHINE_HDC)) {
c++;
continue;
}
QString name = DeviceConfig::DeviceName(hdc_get_device(c), hdc_get_internal_name(c), 1);
if (name.isEmpty()) {
break;
}
if (hdc_available(c)) {
auto* hdc_dev = hdc_get_device(c);
if (device_is_valid(hdc_dev, machine->flags)) {
int row = Models::AddEntry(model, name, c);
if (c == hdc_current) {
selectedRow = row - removeRows;
}
}
}
c++;
}
model->removeRows(0, removeRows);
ui->comboBoxHD->setEnabled(model->rowCount() > 0);
ui->comboBoxHD->setCurrentIndex(-1);
ui->comboBoxHD->setCurrentIndex(selectedRow);
/*FD controller config*/
model = ui->comboBoxFD->model();
removeRows = model->rowCount();
c = 0;
selectedRow = 0;
while (true) {
QString name = DeviceConfig::DeviceName(fdc_card_getdevice(c), fdc_card_get_internal_name(c), 1);
if (name.isEmpty()) {
break;
}
if (fdc_card_available(c)) {
auto* fdc_dev = fdc_card_getdevice(c);
if (device_is_valid(fdc_dev, machine->flags)) {
int row = Models::AddEntry(model, name, c);
if (c == fdc_type) {
selectedRow = row - removeRows;
}
}
}
c++;
}
model->removeRows(0, removeRows);
ui->comboBoxFD->setEnabled(model->rowCount() > 0);
ui->comboBoxFD->setCurrentIndex(-1);
ui->comboBoxFD->setCurrentIndex(selectedRow);
for (int i = 0; i < SCSI_BUS_MAX; ++i) {
auto* cbox = findChild<QComboBox*>(QString("comboBoxSCSI%1").arg(i+1));
model = cbox->model();
removeRows = model->rowCount();
c = 0;
selectedRow = 0;
while (true) {
auto name = DeviceConfig::DeviceName(scsi_card_getdevice(c), scsi_card_get_internal_name(c), 1);
if (name.isEmpty()) {
break;
}
if (scsi_card_available(c)) {
auto* scsi_dev = scsi_card_getdevice(c);
if (device_is_valid(scsi_dev, machine->flags)) {
int row = Models::AddEntry(model, name, c);
if (c == scsi_card_current[i]) {
selectedRow = row - removeRows;
}
}
}
c++;
}
model->removeRows(0, removeRows);
cbox->setEnabled(model->rowCount() > 0);
cbox->setCurrentIndex(-1);
cbox->setCurrentIndex(selectedRow);
}
int is_at = IS_AT(machineId);
ui->checkBoxTertiaryIDE->setEnabled(is_at > 0);
ui->checkBoxQuaternaryIDE->setEnabled(is_at > 0);
}
void SettingsStorageControllers::on_comboBoxHD_currentIndexChanged(int index) {
if (index < 0) {
return;
}
ui->pushButtonHD->setEnabled(hdc_has_config(ui->comboBoxHD->currentData().toInt()) > 0);
}
void SettingsStorageControllers::on_comboBoxFD_currentIndexChanged(int index) {
if (index < 0) {
return;
}
ui->pushButtonFD->setEnabled(hdc_has_config(ui->comboBoxFD->currentData().toInt()) > 0);
}
void SettingsStorageControllers::on_checkBoxTertiaryIDE_stateChanged(int arg1) {
ui->pushButtonTertiaryIDE->setEnabled(arg1 == Qt::Checked);
}
void SettingsStorageControllers::on_checkBoxQuaternaryIDE_stateChanged(int arg1) {
ui->pushButtonQuaternaryIDE->setEnabled(arg1 == Qt::Checked);
}
void SettingsStorageControllers::on_pushButtonHD_clicked() {
DeviceConfig::ConfigureDevice(hdc_get_device(ui->comboBoxHD->currentData().toInt()));
}
void SettingsStorageControllers::on_pushButtonFD_clicked() {
DeviceConfig::ConfigureDevice(fdc_card_getdevice(ui->comboBoxFD->currentData().toInt()));
}
void SettingsStorageControllers::on_pushButtonTertiaryIDE_clicked() {
DeviceConfig::ConfigureDevice(&ide_ter_device);
}
void SettingsStorageControllers::on_pushButtonQuaternaryIDE_clicked() {
DeviceConfig::ConfigureDevice(&ide_qua_device);
}
void SettingsStorageControllers::on_comboBoxSCSI1_currentIndexChanged(int index) {
if (index < 0) {
return;
}
ui->pushButtonSCSI1->setEnabled(scsi_card_has_config(ui->comboBoxSCSI1->currentData().toInt()) > 0);
}
void SettingsStorageControllers::on_comboBoxSCSI2_currentIndexChanged(int index) {
if (index < 0) {
return;
}
ui->pushButtonSCSI2->setEnabled(scsi_card_has_config(ui->comboBoxSCSI2->currentData().toInt()) > 0);
}
void SettingsStorageControllers::on_comboBoxSCSI3_currentIndexChanged(int index) {
if (index < 0) {
return;
}
ui->pushButtonSCSI3->setEnabled(scsi_card_has_config(ui->comboBoxSCSI3->currentData().toInt()) > 0);
}
void SettingsStorageControllers::on_comboBoxSCSI4_currentIndexChanged(int index) {
if (index < 0) {
return;
}
ui->pushButtonSCSI4->setEnabled(scsi_card_has_config(ui->comboBoxSCSI4->currentData().toInt()) > 0);
}
void SettingsStorageControllers::on_pushButtonSCSI1_clicked() {
DeviceConfig::ConfigureDevice(scsi_card_getdevice(ui->comboBoxSCSI1->currentData().toInt()));
}
void SettingsStorageControllers::on_pushButtonSCSI2_clicked() {
DeviceConfig::ConfigureDevice(scsi_card_getdevice(ui->comboBoxSCSI2->currentData().toInt()));
}
void SettingsStorageControllers::on_pushButtonSCSI3_clicked() {
DeviceConfig::ConfigureDevice(scsi_card_getdevice(ui->comboBoxSCSI3->currentData().toInt()));
}
void SettingsStorageControllers::on_pushButtonSCSI4_clicked() {
DeviceConfig::ConfigureDevice(scsi_card_getdevice(ui->comboBoxSCSI4->currentData().toInt()));
}

View File

@@ -0,0 +1,76 @@
#ifndef QT_SETTINGSSTORAGECONTROLLERS_HPP
#define QT_SETTINGSSTORAGECONTROLLERS_HPP
#include <QWidget>
namespace Ui {
class SettingsStorageControllers;
}
class SettingsStorageControllers : public QWidget
{
Q_OBJECT
public:
explicit SettingsStorageControllers(QWidget *parent = nullptr);
~SettingsStorageControllers();
void save();
public slots:
void onCurrentMachineChanged(int machineId);
private slots:
void on_pushButtonSCSI4_clicked();
private slots:
void on_pushButtonSCSI3_clicked();
private slots:
void on_pushButtonSCSI2_clicked();
private slots:
void on_pushButtonSCSI1_clicked();
private slots:
void on_comboBoxSCSI4_currentIndexChanged(int index);
private slots:
void on_comboBoxSCSI3_currentIndexChanged(int index);
private slots:
void on_comboBoxSCSI2_currentIndexChanged(int index);
private slots:
void on_comboBoxSCSI1_currentIndexChanged(int index);
private slots:
void on_pushButtonQuaternaryIDE_clicked();
private slots:
void on_pushButtonTertiaryIDE_clicked();
private slots:
void on_pushButtonFD_clicked();
private slots:
void on_pushButtonHD_clicked();
private slots:
void on_checkBoxQuaternaryIDE_stateChanged(int arg1);
private slots:
void on_checkBoxTertiaryIDE_stateChanged(int arg1);
private slots:
void on_comboBoxFD_currentIndexChanged(int index);
private slots:
void on_comboBoxHD_currentIndexChanged(int index);
private:
Ui::SettingsStorageControllers *ui;
int machineId = 0;
};
#endif // QT_SETTINGSSTORAGECONTROLLERS_HPP

View File

@@ -0,0 +1,202 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SettingsStorageControllers</class>
<widget class="QWidget" name="SettingsStorageControllers">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>496</width>
<height>449</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>HD Controller</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="pushButtonFD">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>FD Controller</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboBoxHD"/>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="pushButtonHD">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="comboBoxFD"/>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="checkBoxTertiaryIDE">
<property name="text">
<string>Tertiary IDE Controller</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="checkBoxQuaternaryIDE">
<property name="text">
<string>Quaternary IDE Controller</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="pushButtonTertiaryIDE">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QPushButton" name="pushButtonQuaternaryIDE">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>SCSI</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="3" column="2">
<widget class="QComboBox" name="comboBoxSCSI4"/>
</item>
<item row="3" column="3">
<widget class="QPushButton" name="pushButtonSCSI4">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Controller 1</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QComboBox" name="comboBoxSCSI1"/>
</item>
<item row="1" column="2">
<widget class="QComboBox" name="comboBoxSCSI2"/>
</item>
<item row="3" column="0" rowspan="2">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Controller 4</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Controller 2</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="pushButtonSCSI1">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="pushButtonSCSI2">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Controller 3</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QComboBox" name="comboBoxSCSI3"/>
</item>
<item row="2" column="3">
<widget class="QPushButton" name="pushButtonSCSI3">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxCassette">
<property name="text">
<string>Cassette</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

114
src/qt/qt_ui.cpp Normal file
View File

@@ -0,0 +1,114 @@
#include <cstdint>
#include <QDebug>
#include <QThread>
#include <QStatusBar>
#include "qt_mainwindow.hpp"
std::atomic_int resize_pending = 0;
std::atomic_int resize_w = 0;
std::atomic_int resize_h = 0;
MainWindow* main_window = nullptr;
extern "C" {
#include <86box/plat.h>
void
plat_delay_ms(uint32_t count)
{
QThread::msleep(count);
}
wchar_t* ui_window_title(wchar_t* str)
{
if (str == nullptr) {
static wchar_t title[512];
memset(title, 0, sizeof(title));
main_window->getTitle(title);
str = title;
} else {
main_window->setTitle(str);
}
return str;
}
extern "C" void qt_blit(int x, int y, int w, int h)
{
main_window->blitToWidget(x, y, w, h);
}
void mouse_poll() {
main_window->pollMouse();
}
void plat_resize(int w, int h) {
resize_w = w;
resize_h = h;
resize_pending = 1;
main_window->resizeContents(w, h);
}
void plat_setfullscreen(int on) {
main_window->setFullscreen(on > 0 ? true : false);
}
void plat_mouse_capture(int on) {
main_window->setMouseCapture(on > 0 ? true : false);
}
int ui_msgbox_header(int flags, void *header, void* message) {
if (header <= (void*)7168) header = plat_get_string(reinterpret_cast<long>(header));
if (message <= (void*)7168) message = plat_get_string(reinterpret_cast<long>(message));
auto hdr = QString::fromWCharArray(reinterpret_cast<const wchar_t*>(header));
auto msg = QString::fromWCharArray(reinterpret_cast<const wchar_t*>(message));
main_window->showMessage(hdr, msg);
return 0;
}
int ui_msgbox(int flags, void *message) {
return ui_msgbox_header(flags, nullptr, message);
}
void ui_sb_set_text_w(wchar_t *wstr) {
main_window->statusBar()->showMessage(QString::fromWCharArray(wstr));
}
void
ui_sb_update_tip(int arg) {
qDebug() << Q_FUNC_INFO << arg;
}
void
ui_sb_update_panes() {
main_window->updateStatusBarPanes();
}
void ui_sb_bugui(char *str) {
main_window->statusBar()->showMessage(str);
}
void ui_sb_set_ready(int ready) {
qDebug() << Q_FUNC_INFO << ready;
}
void
ui_sb_update_icon_state(int tag, int state) {
if (main_window == nullptr) {
return;
}
main_window->updateStatusBarEmpty(tag, state > 0 ? true : false);
}
void
ui_sb_update_icon(int tag, int active) {
main_window->updateStatusBarActivity(tag, active > 0 ? true : false);
}
}

89
src/qt/wl_mouse.cpp Normal file
View File

@@ -0,0 +1,89 @@
#include "wl_mouse.hpp"
#include <QGuiApplication>
#include <wayland-client-core.h>
#include <wayland-client-protocol.h>
#include <wayland-relative-pointer-unstable-v1-client-protocol.h>
#include <wayland-pointer-constraints-unstable-v1-client-protocol.h>
#include <qpa/qplatformnativeinterface.h>
#include <QWindow>
#include <QGuiApplication>
static zwp_relative_pointer_manager_v1* rel_manager = nullptr;
static zwp_relative_pointer_v1* rel_pointer = nullptr;
static zwp_pointer_constraints_v1* conf_pointer_interface = nullptr;
static zwp_locked_pointer_v1* conf_pointer = nullptr;
static int rel_mouse_x = 0, rel_mouse_y = 0;
void rel_mouse_event(void* data, zwp_relative_pointer_v1* zwp_relative_pointer_v1, uint32_t tstmp, uint32_t tstmpl, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t dx_real, wl_fixed_t dy_real)
{
rel_mouse_x += wl_fixed_to_int(dx_real);
rel_mouse_y += wl_fixed_to_int(dy_real);
}
extern "C"
{
extern int mouse_x, mouse_y;
}
void wl_mouse_poll()
{
mouse_x = rel_mouse_x;
mouse_y = rel_mouse_y;
rel_mouse_x = 0;
rel_mouse_y = 0;
}
static struct zwp_relative_pointer_v1_listener rel_listener =
{
rel_mouse_event
};
static void
display_handle_global(void *data, struct wl_registry *registry, uint32_t id,
const char *interface, uint32_t version)
{
if (!strcmp(interface, "zwp_relative_pointer_manager_v1"))
{
rel_manager = (zwp_relative_pointer_manager_v1*)wl_registry_bind(registry, id, &zwp_relative_pointer_manager_v1_interface, version);
}
if (!strcmp(interface, "zwp_pointer_constraints_v1"))
{
conf_pointer_interface = (zwp_pointer_constraints_v1*)wl_registry_bind(registry, id, &zwp_pointer_constraints_v1_interface, version);
}
}
static const struct wl_registry_listener registry_listener = {
display_handle_global,
nullptr
};
void wl_init()
{
wl_display* display = (wl_display*)QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("wl_display");
if (display)
{
auto registry = wl_display_get_registry(display);
if (registry)
{
wl_registry_add_listener(registry, &registry_listener, nullptr);
wl_display_roundtrip(display);
}
}
}
void wl_mouse_capture(QWindow *window)
{
rel_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(rel_manager, (wl_pointer*)QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("wl_pointer"));
zwp_relative_pointer_v1_add_listener(rel_pointer, &rel_listener, nullptr);
conf_pointer = zwp_pointer_constraints_v1_lock_pointer(conf_pointer_interface, (wl_surface*)QGuiApplication::platformNativeInterface()->nativeResourceForWindow("surface", window), (wl_pointer*)QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("wl_pointer"), nullptr, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
}
void wl_mouse_uncapture()
{
zwp_locked_pointer_v1_destroy(conf_pointer);
zwp_relative_pointer_v1_destroy(rel_pointer);
rel_pointer = nullptr;
conf_pointer = nullptr;
}

5
src/qt/wl_mouse.hpp Normal file
View File

@@ -0,0 +1,5 @@
class QWindow;
void wl_mouse_capture(QWindow* window);
void wl_mouse_uncapture();
void wl_mouse_poll();
void wl_init();

47
src/qt_resources.qrc Normal file
View File

@@ -0,0 +1,47 @@
<RCC>
<qresource prefix="/settings">
<file>win/icons/cartridge.ico</file>
<file>win/icons/cartridge_empty.ico</file>
<file>win/icons/cassette.ico</file>
<file>win/icons/cassette_active.ico</file>
<file>win/icons/cassette_empty.ico</file>
<file>win/icons/cassette_empty_active.ico</file>
<file>win/icons/cdrom.ico</file>
<file>win/icons/cdrom_active.ico</file>
<file>win/icons/cdrom_disabled.ico</file>
<file>win/icons/cdrom_empty.ico</file>
<file>win/icons/cdrom_empty_active.ico</file>
<file>win/icons/display.ico</file>
<file>win/icons/floppy_35.ico</file>
<file>win/icons/floppy_35_active.ico</file>
<file>win/icons/floppy_35_empty.ico</file>
<file>win/icons/floppy_35_empty_active.ico</file>
<file>win/icons/floppy_525.ico</file>
<file>win/icons/floppy_525_active.ico</file>
<file>win/icons/floppy_525_empty.ico</file>
<file>win/icons/floppy_525_empty_active.ico</file>
<file>win/icons/floppy_and_cdrom_drives.ico</file>
<file>win/icons/floppy_disabled.ico</file>
<file>win/icons/hard_disk.ico</file>
<file>win/icons/hard_disk_active.ico</file>
<file>win/icons/input_devices.ico</file>
<file>win/icons/machine.ico</file>
<file>win/icons/mo.ico</file>
<file>win/icons/mo_active.ico</file>
<file>win/icons/mo_disabled.ico</file>
<file>win/icons/mo_empty.ico</file>
<file>win/icons/mo_empty_active.ico</file>
<file>win/icons/network.ico</file>
<file>win/icons/network_active.ico</file>
<file>win/icons/other_peripherals.ico</file>
<file>win/icons/other_removable_devices.ico</file>
<file>win/icons/ports.ico</file>
<file>win/icons/sound.ico</file>
<file>win/icons/storage_controllers.ico</file>
<file>win/icons/zip.ico</file>
<file>win/icons/zip_active.ico</file>
<file>win/icons/zip_disabled.ico</file>
<file>win/icons/zip_empty.ico</file>
<file>win/icons/zip_empty_active.ico</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,339 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="pointer_constraints_unstable_v1">
<copyright>
Copyright © 2014 Jonas Ådahl
Copyright © 2015 Red Hat Inc.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</copyright>
<description summary="protocol for constraining pointer motions">
This protocol specifies a set of interfaces used for adding constraints to
the motion of a pointer. Possible constraints include confining pointer
motions to a given region, or locking it to its current position.
In order to constrain the pointer, a client must first bind the global
interface "wp_pointer_constraints" which, if a compositor supports pointer
constraints, is exposed by the registry. Using the bound global object, the
client uses the request that corresponds to the type of constraint it wants
to make. See wp_pointer_constraints for more details.
Warning! The protocol described in this file is experimental and backward
incompatible changes may be made. Backward compatible changes may be added
together with the corresponding interface version bump. Backward
incompatible changes are done by bumping the version number in the protocol
and interface names and resetting the interface version. Once the protocol
is to be declared stable, the 'z' prefix and the version number in the
protocol and interface names are removed and the interface version number is
reset.
</description>
<interface name="zwp_pointer_constraints_v1" version="1">
<description summary="constrain the movement of a pointer">
The global interface exposing pointer constraining functionality. It
exposes two requests: lock_pointer for locking the pointer to its
position, and confine_pointer for locking the pointer to a region.
The lock_pointer and confine_pointer requests create the objects
wp_locked_pointer and wp_confined_pointer respectively, and the client can
use these objects to interact with the lock.
For any surface, only one lock or confinement may be active across all
wl_pointer objects of the same seat. If a lock or confinement is requested
when another lock or confinement is active or requested on the same surface
and with any of the wl_pointer objects of the same seat, an
'already_constrained' error will be raised.
</description>
<enum name="error">
<description summary="wp_pointer_constraints error values">
These errors can be emitted in response to wp_pointer_constraints
requests.
</description>
<entry name="already_constrained" value="1"
summary="pointer constraint already requested on that surface"/>
</enum>
<enum name="lifetime">
<description summary="constraint lifetime">
These values represent different lifetime semantics. They are passed
as arguments to the factory requests to specify how the constraint
lifetimes should be managed.
</description>
<entry name="oneshot" value="1">
<description summary="the pointer constraint is defunct once deactivated">
A oneshot pointer constraint will never reactivate once it has been
deactivated. See the corresponding deactivation event
(wp_locked_pointer.unlocked and wp_confined_pointer.unconfined) for
details.
</description>
</entry>
<entry name="persistent" value="2">
<description summary="the pointer constraint may reactivate">
A persistent pointer constraint may again reactivate once it has
been deactivated. See the corresponding deactivation event
(wp_locked_pointer.unlocked and wp_confined_pointer.unconfined) for
details.
</description>
</entry>
</enum>
<request name="destroy" type="destructor">
<description summary="destroy the pointer constraints manager object">
Used by the client to notify the server that it will no longer use this
pointer constraints object.
</description>
</request>
<request name="lock_pointer">
<description summary="lock pointer to a position">
The lock_pointer request lets the client request to disable movements of
the virtual pointer (i.e. the cursor), effectively locking the pointer
to a position. This request may not take effect immediately; in the
future, when the compositor deems implementation-specific constraints
are satisfied, the pointer lock will be activated and the compositor
sends a locked event.
The protocol provides no guarantee that the constraints are ever
satisfied, and does not require the compositor to send an error if the
constraints cannot ever be satisfied. It is thus possible to request a
lock that will never activate.
There may not be another pointer constraint of any kind requested or
active on the surface for any of the wl_pointer objects of the seat of
the passed pointer when requesting a lock. If there is, an error will be
raised. See general pointer lock documentation for more details.
The intersection of the region passed with this request and the input
region of the surface is used to determine where the pointer must be
in order for the lock to activate. It is up to the compositor whether to
warp the pointer or require some kind of user interaction for the lock
to activate. If the region is null the surface input region is used.
A surface may receive pointer focus without the lock being activated.
The request creates a new object wp_locked_pointer which is used to
interact with the lock as well as receive updates about its state. See
the the description of wp_locked_pointer for further information.
Note that while a pointer is locked, the wl_pointer objects of the
corresponding seat will not emit any wl_pointer.motion events, but
relative motion events will still be emitted via wp_relative_pointer
objects of the same seat. wl_pointer.axis and wl_pointer.button events
are unaffected.
</description>
<arg name="id" type="new_id" interface="zwp_locked_pointer_v1"/>
<arg name="surface" type="object" interface="wl_surface"
summary="surface to lock pointer to"/>
<arg name="pointer" type="object" interface="wl_pointer"
summary="the pointer that should be locked"/>
<arg name="region" type="object" interface="wl_region" allow-null="true"
summary="region of surface"/>
<arg name="lifetime" type="uint" enum="lifetime" summary="lock lifetime"/>
</request>
<request name="confine_pointer">
<description summary="confine pointer to a region">
The confine_pointer request lets the client request to confine the
pointer cursor to a given region. This request may not take effect
immediately; in the future, when the compositor deems implementation-
specific constraints are satisfied, the pointer confinement will be
activated and the compositor sends a confined event.
The intersection of the region passed with this request and the input
region of the surface is used to determine where the pointer must be
in order for the confinement to activate. It is up to the compositor
whether to warp the pointer or require some kind of user interaction for
the confinement to activate. If the region is null the surface input
region is used.
The request will create a new object wp_confined_pointer which is used
to interact with the confinement as well as receive updates about its
state. See the the description of wp_confined_pointer for further
information.
</description>
<arg name="id" type="new_id" interface="zwp_confined_pointer_v1"/>
<arg name="surface" type="object" interface="wl_surface"
summary="surface to lock pointer to"/>
<arg name="pointer" type="object" interface="wl_pointer"
summary="the pointer that should be confined"/>
<arg name="region" type="object" interface="wl_region" allow-null="true"
summary="region of surface"/>
<arg name="lifetime" type="uint" enum="lifetime" summary="confinement lifetime"/>
</request>
</interface>
<interface name="zwp_locked_pointer_v1" version="1">
<description summary="receive relative pointer motion events">
The wp_locked_pointer interface represents a locked pointer state.
While the lock of this object is active, the wl_pointer objects of the
associated seat will not emit any wl_pointer.motion events.
This object will send the event 'locked' when the lock is activated.
Whenever the lock is activated, it is guaranteed that the locked surface
will already have received pointer focus and that the pointer will be
within the region passed to the request creating this object.
To unlock the pointer, send the destroy request. This will also destroy
the wp_locked_pointer object.
If the compositor decides to unlock the pointer the unlocked event is
sent. See wp_locked_pointer.unlock for details.
When unlocking, the compositor may warp the cursor position to the set
cursor position hint. If it does, it will not result in any relative
motion events emitted via wp_relative_pointer.
If the surface the lock was requested on is destroyed and the lock is not
yet activated, the wp_locked_pointer object is now defunct and must be
destroyed.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the locked pointer object">
Destroy the locked pointer object. If applicable, the compositor will
unlock the pointer.
</description>
</request>
<request name="set_cursor_position_hint">
<description summary="set the pointer cursor position hint">
Set the cursor position hint relative to the top left corner of the
surface.
If the client is drawing its own cursor, it should update the position
hint to the position of its own cursor. A compositor may use this
information to warp the pointer upon unlock in order to avoid pointer
jumps.
The cursor position hint is double buffered. The new hint will only take
effect when the associated surface gets it pending state applied. See
wl_surface.commit for details.
</description>
<arg name="surface_x" type="fixed"
summary="surface-local x coordinate"/>
<arg name="surface_y" type="fixed"
summary="surface-local y coordinate"/>
</request>
<request name="set_region">
<description summary="set a new lock region">
Set a new region used to lock the pointer.
The new lock region is double-buffered. The new lock region will
only take effect when the associated surface gets its pending state
applied. See wl_surface.commit for details.
For details about the lock region, see wp_locked_pointer.
</description>
<arg name="region" type="object" interface="wl_region" allow-null="true"
summary="region of surface"/>
</request>
<event name="locked">
<description summary="lock activation event">
Notification that the pointer lock of the seat's pointer is activated.
</description>
</event>
<event name="unlocked">
<description summary="lock deactivation event">
Notification that the pointer lock of the seat's pointer is no longer
active. If this is a oneshot pointer lock (see
wp_pointer_constraints.lifetime) this object is now defunct and should
be destroyed. If this is a persistent pointer lock (see
wp_pointer_constraints.lifetime) this pointer lock may again
reactivate in the future.
</description>
</event>
</interface>
<interface name="zwp_confined_pointer_v1" version="1">
<description summary="confined pointer object">
The wp_confined_pointer interface represents a confined pointer state.
This object will send the event 'confined' when the confinement is
activated. Whenever the confinement is activated, it is guaranteed that
the surface the pointer is confined to will already have received pointer
focus and that the pointer will be within the region passed to the request
creating this object. It is up to the compositor to decide whether this
requires some user interaction and if the pointer will warp to within the
passed region if outside.
To unconfine the pointer, send the destroy request. This will also destroy
the wp_confined_pointer object.
If the compositor decides to unconfine the pointer the unconfined event is
sent. The wp_confined_pointer object is at this point defunct and should
be destroyed.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the confined pointer object">
Destroy the confined pointer object. If applicable, the compositor will
unconfine the pointer.
</description>
</request>
<request name="set_region">
<description summary="set a new confine region">
Set a new region used to confine the pointer.
The new confine region is double-buffered. The new confine region will
only take effect when the associated surface gets its pending state
applied. See wl_surface.commit for details.
If the confinement is active when the new confinement region is applied
and the pointer ends up outside of newly applied region, the pointer may
warped to a position within the new confinement region. If warped, a
wl_pointer.motion event will be emitted, but no
wp_relative_pointer.relative_motion event.
The compositor may also, instead of using the new region, unconfine the
pointer.
For details about the confine region, see wp_confined_pointer.
</description>
<arg name="region" type="object" interface="wl_region" allow-null="true"
summary="region of surface"/>
</request>
<event name="confined">
<description summary="pointer confined">
Notification that the pointer confinement of the seat's pointer is
activated.
</description>
</event>
<event name="unconfined">
<description summary="pointer unconfined">
Notification that the pointer confinement of the seat's pointer is no
longer active. If this is a oneshot pointer confinement (see
wp_pointer_constraints.lifetime) this object is now defunct and should
be destroyed. If this is a persistent pointer confinement (see
wp_pointer_constraints.lifetime) this pointer confinement may again
reactivate in the future.
</description>
</event>
</interface>
</protocol>

View File

@@ -0,0 +1,136 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="relative_pointer_unstable_v1">
<copyright>
Copyright © 2014 Jonas Ådahl
Copyright © 2015 Red Hat Inc.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</copyright>
<description summary="protocol for relative pointer motion events">
This protocol specifies a set of interfaces used for making clients able to
receive relative pointer events not obstructed by barriers (such as the
monitor edge or other pointer barriers).
To start receiving relative pointer events, a client must first bind the
global interface "wp_relative_pointer_manager" which, if a compositor
supports relative pointer motion events, is exposed by the registry. After
having created the relative pointer manager proxy object, the client uses
it to create the actual relative pointer object using the
"get_relative_pointer" request given a wl_pointer. The relative pointer
motion events will then, when applicable, be transmitted via the proxy of
the newly created relative pointer object. See the documentation of the
relative pointer interface for more details.
Warning! The protocol described in this file is experimental and backward
incompatible changes may be made. Backward compatible changes may be added
together with the corresponding interface version bump. Backward
incompatible changes are done by bumping the version number in the protocol
and interface names and resetting the interface version. Once the protocol
is to be declared stable, the 'z' prefix and the version number in the
protocol and interface names are removed and the interface version number is
reset.
</description>
<interface name="zwp_relative_pointer_manager_v1" version="1">
<description summary="get relative pointer objects">
A global interface used for getting the relative pointer object for a
given pointer.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the relative pointer manager object">
Used by the client to notify the server that it will no longer use this
relative pointer manager object.
</description>
</request>
<request name="get_relative_pointer">
<description summary="get a relative pointer object">
Create a relative pointer interface given a wl_pointer object. See the
wp_relative_pointer interface for more details.
</description>
<arg name="id" type="new_id" interface="zwp_relative_pointer_v1"/>
<arg name="pointer" type="object" interface="wl_pointer"/>
</request>
</interface>
<interface name="zwp_relative_pointer_v1" version="1">
<description summary="relative pointer object">
A wp_relative_pointer object is an extension to the wl_pointer interface
used for emitting relative pointer events. It shares the same focus as
wl_pointer objects of the same seat and will only emit events when it has
focus.
</description>
<request name="destroy" type="destructor">
<description summary="release the relative pointer object"/>
</request>
<event name="relative_motion">
<description summary="relative pointer motion">
Relative x/y pointer motion from the pointer of the seat associated with
this object.
A relative motion is in the same dimension as regular wl_pointer motion
events, except they do not represent an absolute position. For example,
moving a pointer from (x, y) to (x', y') would have the equivalent
relative motion (x' - x, y' - y). If a pointer motion caused the
absolute pointer position to be clipped by for example the edge of the
monitor, the relative motion is unaffected by the clipping and will
represent the unclipped motion.
This event also contains non-accelerated motion deltas. The
non-accelerated delta is, when applicable, the regular pointer motion
delta as it was before having applied motion acceleration and other
transformations such as normalization.
Note that the non-accelerated delta does not represent 'raw' events as
they were read from some device. Pointer motion acceleration is device-
and configuration-specific and non-accelerated deltas and accelerated
deltas may have the same value on some devices.
Relative motions are not coupled to wl_pointer.motion events, and can be
sent in combination with such events, but also independently. There may
also be scenarios where wl_pointer.motion is sent, but there is no
relative motion. The order of an absolute and relative motion event
originating from the same physical motion is not guaranteed.
If the client needs button events or focus state, it can receive them
from a wl_pointer object of the same seat that the wp_relative_pointer
object is associated with.
</description>
<arg name="utime_hi" type="uint"
summary="high 32 bits of a 64 bit timestamp with microsecond granularity"/>
<arg name="utime_lo" type="uint"
summary="low 32 bits of a 64 bit timestamp with microsecond granularity"/>
<arg name="dx" type="fixed"
summary="the x component of the motion vector"/>
<arg name="dy" type="fixed"
summary="the y component of the motion vector"/>
<arg name="dx_unaccel" type="fixed"
summary="the x component of the unaccelerated motion vector"/>
<arg name="dy_unaccel" type="fixed"
summary="the y component of the unaccelerated motion vector"/>
</event>
</interface>
</protocol>