@@ -17,7 +17,7 @@
|
||||
|
||||
# WIN32 marks us as a GUI app on Windows
|
||||
add_executable(86Box WIN32 MACOSX_BUNDLE 86box.c config.c log.c random.c timer.c io.c acpi.c apm.c
|
||||
dma.c ddma.c nmi.c pic.c pit.c port_6x.c port_92.c ppi.c pci.c mca.c usb.c fifo8.c
|
||||
dma.c ddma.c discord.c nmi.c pic.c pit.c port_6x.c port_92.c ppi.c pci.c mca.c usb.c fifo8.c
|
||||
device.c nvr.c nvr_at.c nvr_ps2.c)
|
||||
|
||||
if(CPPTHREADS)
|
||||
@@ -75,8 +75,31 @@ if(APPLE)
|
||||
# Force using the newest library if it's installed by homebrew
|
||||
set(CMAKE_FIND_FRAMEWORK LAST)
|
||||
|
||||
# setting our compilation target to macOS 10.13 High Sierra
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13")
|
||||
# setting our compilation target to macOS 10.15 Catalina if targetting Qt6, macOS 10.13 High Sierra otherwise
|
||||
if (USE_QT6)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15")
|
||||
else()
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(QT)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
if (USE_QT6)
|
||||
set(QT_MAJOR 6)
|
||||
else()
|
||||
set(QT_MAJOR 5)
|
||||
endif()
|
||||
|
||||
# if we want to use qt5-static to eliminate need for bundling qt dlls (windows)
|
||||
#set(QT_STATIC ON)
|
||||
# needed to build with qt5-static if both qt5 and qt5-static are installed
|
||||
#set(CMAKE_PREFIX_PATH "path/to/qt5-static")
|
||||
|
||||
find_package(Qt${QT_MAJOR} COMPONENTS Core Widgets OpenGL REQUIRED)
|
||||
find_package(Qt${QT_MAJOR}LinguistTools REQUIRED)
|
||||
endif()
|
||||
|
||||
find_package(Freetype REQUIRED)
|
||||
@@ -155,15 +178,85 @@ else()
|
||||
install(TARGETS 86Box)
|
||||
endif()
|
||||
|
||||
# prepare everything for Qt5 bundle creation
|
||||
if (APPLE AND QT)
|
||||
set(prefix "86Box.app/Contents")
|
||||
set(INSTALL_RUNTIME_DIR "${prefix}/MacOS")
|
||||
set(INSTALL_CMAKE_DIR "${prefix}/Resources")
|
||||
endif()
|
||||
|
||||
# loads a macro to install Qt5 plugins on macOS
|
||||
# based on https://stackoverflow.com/questions/35612687/cmake-macos-x-bundle-with-bundleutiliies-for-qt-application
|
||||
macro(install_qt5_plugin _qt_plugin_name _qt_plugins_var _prefix)
|
||||
get_target_property(_qt_plugin_path "${_qt_plugin_name}" LOCATION)
|
||||
if(EXISTS "${_qt_plugin_path}")
|
||||
get_filename_component(_qt_plugin_file "${_qt_plugin_path}" NAME)
|
||||
get_filename_component(_qt_plugin_type "${_qt_plugin_path}" PATH)
|
||||
get_filename_component(_qt_plugin_type "${_qt_plugin_type}" NAME)
|
||||
set(_qt_plugin_dest "${_prefix}/PlugIns/${_qt_plugin_type}")
|
||||
install(FILES "${_qt_plugin_path}"
|
||||
DESTINATION "${_qt_plugin_dest}")
|
||||
set(${_qt_plugins_var}
|
||||
"${${_qt_plugins_var}};\$ENV{DEST_DIR}\${CMAKE_INSTALL_PREFIX}/${_qt_plugin_dest}/${_qt_plugin_file}")
|
||||
else()
|
||||
message(FATAL_ERROR "QT plugin ${_qt_plugin_name} not found")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
# Install our dependencies to the macOS bundle
|
||||
if(APPLE)
|
||||
install(CODE "
|
||||
include(BundleUtilities)
|
||||
get_filename_component(CMAKE_INSTALL_PREFIX_ABSOLUTE \${CMAKE_INSTALL_PREFIX} ABSOLUTE)
|
||||
fixup_bundle(\"\${CMAKE_INSTALL_PREFIX_ABSOLUTE}/86Box.app\" \"\" \"\")"
|
||||
COMPONENT Runtime)
|
||||
if (NOT QT)
|
||||
install(CODE "
|
||||
include(BundleUtilities)
|
||||
get_filename_component(CMAKE_INSTALL_PREFIX_ABSOLUTE \${CMAKE_INSTALL_PREFIX} ABSOLUTE)
|
||||
fixup_bundle(\"\${CMAKE_INSTALL_PREFIX_ABSOLUTE}/86Box.app\" \"\" \"\")"
|
||||
COMPONENT Runtime)
|
||||
endif()
|
||||
|
||||
if(QT)
|
||||
# using the install_qt5_plugin to add Qt plugins into the macOS app bundle
|
||||
if (USE_QT6)
|
||||
install_qt5_plugin("Qt6::QCocoaIntegrationPlugin" QT_PLUGINS ${prefix})
|
||||
else()
|
||||
install_qt5_plugin("Qt5::QCocoaIntegrationPlugin" QT_PLUGINS ${prefix})
|
||||
install_qt5_plugin("Qt5::QMacStylePlugin" QT_PLUGINS ${prefix})
|
||||
install_qt5_plugin("Qt5::QICOPlugin" QT_PLUGINS ${prefix})
|
||||
install_qt5_plugin("Qt5::QICNSPlugin" QT_PLUGINS ${prefix})
|
||||
endif()
|
||||
|
||||
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/qt.conf"
|
||||
"[Paths]\nPlugins = ${_qt_plugin_dir}\n")
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/qt.conf"
|
||||
DESTINATION "${INSTALL_CMAKE_DIR}")
|
||||
|
||||
# Note Mac specific extension .app
|
||||
set(APPS "\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/86Box.app")
|
||||
|
||||
# Directories to look for dependencies
|
||||
set(DIRS "${CMAKE_BINARY_DIR}")
|
||||
|
||||
# Path used for searching by FIND_XXX(), with appropriate suffixes added
|
||||
if(CMAKE_PREFIX_PATH)
|
||||
foreach(dir ${CMAKE_PREFIX_PATH})
|
||||
list(APPEND DIRS "${dir}/bin" "${dir}/lib")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# Append Qt's lib folder which is two levels above Qt5Widgets_DIR
|
||||
list(APPEND DIRS "${Qt5Widgets_DIR}/../..")
|
||||
|
||||
include(InstallRequiredSystemLibraries)
|
||||
|
||||
message(STATUS "APPS: ${APPS}")
|
||||
message(STATUS "QT_PLUGINS: ${QT_PLUGINS}")
|
||||
message(STATUS "DIRS: ${DIRS}")
|
||||
|
||||
install(CODE "include(BundleUtilities)
|
||||
fixup_bundle(\"${APPS}\" \"${QT_PLUGINS}\" \"${DIRS}\")")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# Install our dependencies if using vcpkg
|
||||
if(VCPKG_TOOLCHAIN)
|
||||
x_vcpkg_install_local_dependencies(TARGETS 86Box DESTINATION ".")
|
||||
@@ -198,9 +291,12 @@ add_subdirectory(sio)
|
||||
add_subdirectory(scsi)
|
||||
add_subdirectory(sound)
|
||||
add_subdirectory(video)
|
||||
if(APPLE)
|
||||
add_subdirectory(mac)
|
||||
add_subdirectory(unix)
|
||||
if (APPLE)
|
||||
add_subdirectory(mac)
|
||||
endif()
|
||||
|
||||
if (QT)
|
||||
add_subdirectory(qt)
|
||||
elseif(WIN32)
|
||||
add_subdirectory(win)
|
||||
else()
|
||||
|
||||
@@ -19,18 +19,23 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <windows.h>
|
||||
#include <time.h>
|
||||
#define HAVE_STDARG_H
|
||||
#include <86box/86box.h>
|
||||
#include "cpu.h"
|
||||
#include "cpu/cpu.h"
|
||||
#include <86box/machine.h>
|
||||
#include <86box/plat.h>
|
||||
#include <86box/plat_dynld.h>
|
||||
#include <86box/win_discord.h>
|
||||
#include <86box/discord.h>
|
||||
#include <discord_game_sdk.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define PATH_DISCORD_DLL "discord_game_sdk.dll"
|
||||
#elif defined __APPLE__
|
||||
#define PATH_DISCORD_DLL "discord_game_sdk.dylib"
|
||||
#else
|
||||
#define PATH_DISCORD_DLL "discord_game_sdk.so"
|
||||
#endif
|
||||
|
||||
int discord_loaded = 0;
|
||||
|
||||
@@ -74,7 +79,7 @@ discord_update_activity(int paused)
|
||||
if(discord_activities == NULL)
|
||||
return;
|
||||
|
||||
discord_log("win_discord: discord_update_activity(paused=%d)\n", paused);
|
||||
discord_log("discord: discord_update_activity(paused=%d)\n", paused);
|
||||
|
||||
memset(&activity, 0x00, sizeof(activity));
|
||||
|
||||
@@ -85,13 +90,13 @@ discord_update_activity(int paused)
|
||||
|
||||
if (strlen(vm_name) < 100)
|
||||
{
|
||||
sprintf_s(activity.details, sizeof(activity.details), "Running \"%s\"", vm_name);
|
||||
sprintf_s(activity.state, sizeof(activity.state), "%s (%s/%s)", strchr(machine_getname(), ']') + 2, cpufamily, cpu_s->name);
|
||||
snprintf(activity.details, sizeof(activity.details), "Running \"%s\"", vm_name);
|
||||
snprintf(activity.state, sizeof(activity.state), "%s (%s/%s)", strchr(machine_getname(), ']') + 2, cpufamily, cpu_s->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(activity.details, strchr(machine_getname(), ']') + 2, sizeof(activity.details) - 1);
|
||||
sprintf_s(activity.state, sizeof(activity.state), "%s/%s", cpufamily, cpu_s->name);
|
||||
snprintf(activity.state, sizeof(activity.state), "%s/%s", cpufamily, cpu_s->name);
|
||||
}
|
||||
|
||||
activity.timestamps.start = time(NULL);
|
||||
@@ -139,7 +144,7 @@ discord_load()
|
||||
|
||||
if (discord_handle == NULL)
|
||||
{
|
||||
discord_log("win_discord: couldn't load " PATH_DISCORD_DLL "\n");
|
||||
discord_log("discord: couldn't load " PATH_DISCORD_DLL "\n");
|
||||
discord_close();
|
||||
|
||||
return(0);
|
||||
@@ -165,7 +170,7 @@ discord_init()
|
||||
result = discord_create(DISCORD_VERSION, ¶ms, &discord_core);
|
||||
if (result != DiscordResult_Ok)
|
||||
{
|
||||
discord_log("win_discord: DiscordCreate returned %d\n", result);
|
||||
discord_log("discord: DiscordCreate returned %d\n", result);
|
||||
discord_close();
|
||||
return;
|
||||
}
|
||||
@@ -17,6 +17,11 @@
|
||||
#ifndef WIN_DISCORD_H
|
||||
# define WIN_DISCORD_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
extern int discord_loaded;
|
||||
|
||||
extern int discord_load();
|
||||
@@ -25,4 +30,8 @@ extern void discord_close();
|
||||
extern void discord_update_activity(int paused);
|
||||
extern void discord_run_callbacks();
|
||||
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -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
|
||||
@@ -164,6 +166,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;
|
||||
|
||||
@@ -48,6 +48,7 @@ configure_file(Info.plist.in Info.plist @ONLY)
|
||||
set_target_properties(86Box
|
||||
PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_BINARY_DIR}/Info.plist)
|
||||
|
||||
#set(XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "YES")
|
||||
#set(XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "-")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS "-o linker-signed")
|
||||
set(XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "YES")
|
||||
set(XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "-")
|
||||
#set(XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS ${CMAKE_CURRENT_SOURCE_DIR}/mac/codesign/dev/app.entitlements)
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <pthread.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <stdatomic.h>
|
||||
#endif
|
||||
|
||||
#include <minitrace/minitrace.h>
|
||||
@@ -66,8 +67,8 @@ typedef struct raw_event {
|
||||
static raw_event_t *event_buffer;
|
||||
static raw_event_t *flush_buffer;
|
||||
static volatile int event_count;
|
||||
static __attribute__ ((aligned (32))) volatile long is_tracing = FALSE;
|
||||
static __attribute__ ((aligned (32))) volatile long stop_flushing_requested = FALSE;
|
||||
static __attribute__ ((aligned (32))) atomic_long is_tracing = FALSE;
|
||||
static __attribute__ ((aligned (32))) atomic_long stop_flushing_requested = FALSE;
|
||||
static int is_flushing = FALSE;
|
||||
static int events_in_progress = 0;
|
||||
static int64_t time_offset;
|
||||
@@ -93,8 +94,6 @@ void mtr_flush_with_state(int);
|
||||
// mtr_time_s()
|
||||
// pthread basics
|
||||
#ifdef _WIN32
|
||||
#define atomic_load(a) InterlockedOr((a), 0)
|
||||
#define atomic_store(a, b) InterlockedExchange((a), b)
|
||||
|
||||
static int get_cur_thread_id() {
|
||||
return (int)GetCurrentThreadId();
|
||||
@@ -162,6 +161,31 @@ static inline int get_cur_process_id() {
|
||||
return (int)getpid();
|
||||
}
|
||||
|
||||
static pthread_t thread_handle = 0;
|
||||
static void* thread_flush_proc(void* param) {
|
||||
while(1) {
|
||||
mtr_flush_with_state(0);
|
||||
if(atomic_load(&stop_flushing_requested)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static void init_flushing_thread(void) {
|
||||
pthread_mutex_lock(&mutex);
|
||||
is_flushing = FALSE;
|
||||
pthread_mutex_unlock(&mutex);
|
||||
if (pthread_create(&thread_handle, NULL, thread_flush_proc, NULL) != 0)
|
||||
{
|
||||
thread_handle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void join_flushing_thread(void) {
|
||||
if (thread_handle) pthread_join(thread_handle, NULL);
|
||||
thread_handle = 0;
|
||||
}
|
||||
|
||||
#if defined(BLACKBERRY)
|
||||
double mtr_time_s() {
|
||||
struct timespec time;
|
||||
@@ -269,9 +293,9 @@ void mtr_start() {
|
||||
#ifndef MTR_ENABLED
|
||||
return;
|
||||
#endif
|
||||
pthread_cond_init(&buffer_not_full_cond);
|
||||
pthread_cond_init(&buffer_full_cond);
|
||||
atomic_store(&is_tracing, TRUE);
|
||||
pthread_cond_init(&buffer_not_full_cond, NULL);
|
||||
pthread_cond_init(&buffer_full_cond, NULL);
|
||||
atomic_store(&is_tracing, TRUE);
|
||||
init_flushing_thread();
|
||||
}
|
||||
|
||||
|
||||
@@ -13,12 +13,27 @@
|
||||
# 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
|
||||
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 IMPORTED_TARGET glib-2.0)
|
||||
if (GLIB_PKG_FOUND)
|
||||
target_compile_definitions(slirp PRIVATE TINYGLIB_USE_GLIB)
|
||||
target_link_libraries(slirp PkgConfig::GLIB_PKG)
|
||||
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()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
220
src/qt/CMakeLists.txt
Normal file
220
src/qt/CMakeLists.txt
Normal file
@@ -0,0 +1,220 @@
|
||||
# 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
|
||||
)
|
||||
|
||||
add_library(ui STATIC
|
||||
qt_ui.cpp
|
||||
qt_cdrom.c
|
||||
|
||||
qt_mainwindow.cpp
|
||||
qt_mainwindow.hpp
|
||||
qt_mainwindow.ui
|
||||
qt_machinestatus.cpp
|
||||
qt_machinestatus.hpp
|
||||
qt_mediamenu.cpp
|
||||
qt_mediamenu.hpp
|
||||
qt_rendererstack.cpp
|
||||
qt_rendererstack.hpp
|
||||
qt_rendererstack.ui
|
||||
qt_renderercommon.cpp
|
||||
qt_renderercommon.hpp
|
||||
qt_softwarerenderer.cpp
|
||||
qt_softwarerenderer.hpp
|
||||
qt_hardwarerenderer.cpp
|
||||
qt_hardwarerenderer.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_settings_bus_tracking.cpp
|
||||
qt_settings_bus_tracking.hpp
|
||||
|
||||
qt_deviceconfig.cpp
|
||||
qt_deviceconfig.hpp
|
||||
qt_deviceconfig.ui
|
||||
qt_joystickconfiguration.cpp
|
||||
qt_joystickconfiguration.hpp
|
||||
qt_joystickconfiguration.ui
|
||||
|
||||
qt_filefield.cpp
|
||||
qt_filefield.hpp
|
||||
qt_filefield.ui
|
||||
qt_newfloppydialog.cpp
|
||||
qt_newfloppydialog.hpp
|
||||
qt_newfloppydialog.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_specifydimensions.h
|
||||
qt_specifydimensions.cpp
|
||||
qt_specifydimensions.ui
|
||||
qt_soundgain.hpp
|
||||
qt_soundgain.cpp
|
||||
qt_soundgain.ui
|
||||
|
||||
qt_styleoverride.cpp
|
||||
qt_styleoverride.hpp
|
||||
qt_progsettings.hpp
|
||||
qt_progsettings.cpp
|
||||
qt_progsettings.ui
|
||||
qt_util.hpp
|
||||
qt_util.cpp
|
||||
|
||||
../qt_resources.qrc
|
||||
)
|
||||
|
||||
|
||||
if(WIN32)
|
||||
enable_language(RC)
|
||||
target_sources(86Box PUBLIC ../win/86Box-qt.rc)
|
||||
target_sources(plat PRIVATE win_joystick_rawinput.c)
|
||||
target_link_libraries(86Box hid)
|
||||
|
||||
# CMake 3.22 messed this up for clang/clang++
|
||||
# See https://gitlab.kitware.com/cmake/cmake/-/issues/22611
|
||||
if(MSVC OR (NOT MINGW AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.22))
|
||||
# MSVC linker adds its own manifest to the executable, which fails if
|
||||
# we include ours in 86Box.rc. We therefore need to pass the manifest
|
||||
# directly as as a source file, so the linker can use that instead.
|
||||
set_property(SOURCE ../win/86Box-qt.rc PROPERTY COMPILE_DEFINITIONS NO_INCLUDE_MANIFEST)
|
||||
target_sources(86Box PRIVATE ../win/86Box.manifest)
|
||||
endif()
|
||||
else()
|
||||
target_sources(plat PRIVATE sdl_joystick.cpp)
|
||||
endif()
|
||||
|
||||
if(WIN32 AND NOT MINGW)
|
||||
target_sources(plat PRIVATE ../win/win_opendir.c)
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
target_sources(ui PRIVATE macos_event_filter.mm)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
target_sources(ui PRIVATE
|
||||
qt_winrawinputfilter.hpp
|
||||
qt_winrawinputfilter.cpp
|
||||
qt_winmanagerfilter.hpp
|
||||
qt_winmanagerfilter.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(
|
||||
plat
|
||||
PRIVATE
|
||||
Qt${QT_MAJOR}::Widgets
|
||||
Qt${QT_MAJOR}::Gui
|
||||
Threads::Threads
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
ui
|
||||
PRIVATE
|
||||
Qt${QT_MAJOR}::Widgets
|
||||
Qt${QT_MAJOR}::Gui
|
||||
Qt${QT_MAJOR}::OpenGL
|
||||
Threads::Threads
|
||||
)
|
||||
|
||||
# needed for static builds
|
||||
if (WIN32)
|
||||
qt_import_plugins(plat INCLUDE Qt${QT_MAJOR}::QWindowsIntegrationPlugin Qt${QT_MAJOR}::QICOPlugin QWindowsVistaStylePlugin)
|
||||
endif()
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
find_package(X11 REQUIRED)
|
||||
target_link_libraries(ui PRIVATE X11::X11)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(LIBEVDEV IMPORTED_TARGET libevdev)
|
||||
if (LIBEVDEV_FOUND)
|
||||
target_compile_definitions(ui PRIVATE EVDEV_INPUT)
|
||||
target_link_libraries(ui PUBLIC PkgConfig::LIBEVDEV)
|
||||
target_sources(ui PRIVATE evdev_mouse.cpp)
|
||||
endif()
|
||||
|
||||
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} ${Qt${QT_MAJOR}Gui_PRIVATE_INCLUDE_DIRS})
|
||||
target_sources(ui PRIVATE ${WL_SOURCE_VAR} wl_mouse.cpp)
|
||||
target_compile_definitions(ui PRIVATE WAYLAND)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
set(QM_FILES)
|
||||
file(GLOB po_files "${CMAKE_CURRENT_SOURCE_DIR}/languages/*.po")
|
||||
foreach(po_file ${po_files})
|
||||
get_target_property(LCONVERT_EXECUTABLE Qt${QT_MAJOR}::lconvert IMPORTED_LOCATION)
|
||||
get_filename_component(_lconvert_bin_dir "${LCONVERT_EXECUTABLE}" DIRECTORY)
|
||||
find_program(LCONVERT_EXECUTABLE lconvert HINTS "${_lconvert_bin_dir}")
|
||||
|
||||
get_filename_component(PO_FILE_NAME ${po_file} NAME_WE)
|
||||
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/86box_${PO_FILE_NAME}.qm"
|
||||
COMMAND ${LCONVERT_EXECUTABLE} -i ${po_file} -o ${CMAKE_CURRENT_BINARY_DIR}/86box_${PO_FILE_NAME}.qm
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
DEPENDS "${po_file}")
|
||||
list(APPEND QM_FILES "${CMAKE_CURRENT_BINARY_DIR}/86box_${PO_FILE_NAME}.qm")
|
||||
list(APPEND QM_FILES "${po_file}")
|
||||
endforeach()
|
||||
configure_file(qt_translations.qrc ${CMAKE_CURRENT_BINARY_DIR} COPYONLY)
|
||||
target_sources(ui PRIVATE ${QM_FILES} ${CMAKE_CURRENT_BINARY_DIR}/qt_translations.qrc)
|
||||
1
src/qt/TODO
Normal file
1
src/qt/TODO
Normal file
@@ -0,0 +1 @@
|
||||
* Joystick support
|
||||
16
src/qt/cocoa_mouse.hpp
Normal file
16
src/qt/cocoa_mouse.hpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <QAbstractNativeEventFilter>
|
||||
#include <QByteArray>
|
||||
|
||||
#if QT_VERSION_MAJOR >= 6
|
||||
#define result_t qintptr
|
||||
#else
|
||||
#define result_t long
|
||||
#endif
|
||||
|
||||
class CocoaEventFilter : public QAbstractNativeEventFilter
|
||||
{
|
||||
public:
|
||||
CocoaEventFilter() {};
|
||||
~CocoaEventFilter();
|
||||
virtual bool nativeEventFilter(const QByteArray &eventType, void *message, result_t *result) override;
|
||||
};
|
||||
102
src/qt/evdev_mouse.cpp
Normal file
102
src/qt/evdev_mouse.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
#include "evdev_mouse.hpp"
|
||||
#include <libevdev/libevdev.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
#include <QThread>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <86box/86box.h>
|
||||
#include <86box/plat.h>
|
||||
#include <86box/mouse.h>
|
||||
}
|
||||
|
||||
static std::vector<std::pair<int, libevdev*>> evdev_mice;
|
||||
static std::atomic<bool> stopped = false;
|
||||
static QThread* evdev_thread;
|
||||
|
||||
static std::atomic<int> evdev_mouse_rel_x = 0, evdev_mouse_rel_y = 0;
|
||||
|
||||
void evdev_mouse_poll()
|
||||
{
|
||||
if (!evdev_mice.size() || !mouse_capture)
|
||||
{
|
||||
evdev_mouse_rel_x = 0;
|
||||
evdev_mouse_rel_y = 0;
|
||||
return;
|
||||
}
|
||||
mouse_x = evdev_mouse_rel_x;
|
||||
mouse_y = evdev_mouse_rel_y;
|
||||
evdev_mouse_rel_x = evdev_mouse_rel_y = 0;
|
||||
}
|
||||
|
||||
void evdev_thread_func()
|
||||
{
|
||||
while (!stopped)
|
||||
{
|
||||
for (int i = 0; i < evdev_mice.size(); i++)
|
||||
{
|
||||
struct input_event ev;
|
||||
int rc = libevdev_next_event(evdev_mice[i].second, LIBEVDEV_READ_FLAG_NORMAL, &ev);
|
||||
if (rc == 0 && ev.type == EV_REL && mouse_capture)
|
||||
{
|
||||
if (ev.code == REL_X) evdev_mouse_rel_x += ev.value;
|
||||
if (ev.code == REL_Y) evdev_mouse_rel_y += ev.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < evdev_mice.size(); i++)
|
||||
{
|
||||
libevdev_free(evdev_mice[i].second);
|
||||
close(evdev_mice[i].first);
|
||||
}
|
||||
evdev_mice.clear();
|
||||
}
|
||||
|
||||
void evdev_stop()
|
||||
{
|
||||
stopped = true;
|
||||
evdev_thread->wait();
|
||||
}
|
||||
|
||||
void evdev_init()
|
||||
{
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
std::string evdev_device_path = "/dev/input/event" + std::to_string(i);
|
||||
int fd = open(evdev_device_path.c_str(), O_NONBLOCK | O_RDONLY);
|
||||
if (fd != -1)
|
||||
{
|
||||
libevdev* input_struct = nullptr;
|
||||
int rc = libevdev_new_from_fd(fd, &input_struct);
|
||||
if (rc <= -1)
|
||||
{
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!libevdev_has_event_type(input_struct, EV_REL) || !libevdev_has_event_code(input_struct, EV_KEY, BTN_LEFT))
|
||||
{
|
||||
libevdev_free(input_struct);
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
evdev_mice.push_back(std::make_pair(fd, input_struct));
|
||||
}
|
||||
}
|
||||
else if (errno == ENOENT) break;
|
||||
}
|
||||
if (evdev_mice.size() != 0)
|
||||
{
|
||||
evdev_thread = QThread::create(evdev_thread_func);
|
||||
evdev_thread->start();
|
||||
atexit(evdev_stop);
|
||||
}
|
||||
}
|
||||
4
src/qt/evdev_mouse.hpp
Normal file
4
src/qt/evdev_mouse.hpp
Normal file
@@ -0,0 +1,4 @@
|
||||
#ifdef EVDEV_INPUT
|
||||
void evdev_init();
|
||||
void evdev_mouse_poll();
|
||||
#endif
|
||||
1215
src/qt/languages/cs-CZ.po
Normal file
1215
src/qt/languages/cs-CZ.po
Normal file
File diff suppressed because it is too large
Load Diff
1215
src/qt/languages/de-DE.po
Normal file
1215
src/qt/languages/de-DE.po
Normal file
File diff suppressed because it is too large
Load Diff
1215
src/qt/languages/en-GB.po
Normal file
1215
src/qt/languages/en-GB.po
Normal file
File diff suppressed because it is too large
Load Diff
1215
src/qt/languages/en-US.po
Normal file
1215
src/qt/languages/en-US.po
Normal file
File diff suppressed because it is too large
Load Diff
1215
src/qt/languages/es-ES.po
Normal file
1215
src/qt/languages/es-ES.po
Normal file
File diff suppressed because it is too large
Load Diff
1215
src/qt/languages/fi-FI.po
Normal file
1215
src/qt/languages/fi-FI.po
Normal file
File diff suppressed because it is too large
Load Diff
1215
src/qt/languages/fr-FR.po
Normal file
1215
src/qt/languages/fr-FR.po
Normal file
File diff suppressed because it is too large
Load Diff
1215
src/qt/languages/hr-HR.po
Normal file
1215
src/qt/languages/hr-HR.po
Normal file
File diff suppressed because it is too large
Load Diff
1215
src/qt/languages/hu-HU.po
Normal file
1215
src/qt/languages/hu-HU.po
Normal file
File diff suppressed because it is too large
Load Diff
1215
src/qt/languages/it-IT.po
Normal file
1215
src/qt/languages/it-IT.po
Normal file
File diff suppressed because it is too large
Load Diff
1215
src/qt/languages/ja-JP.po
Normal file
1215
src/qt/languages/ja-JP.po
Normal file
File diff suppressed because it is too large
Load Diff
1215
src/qt/languages/ko-KR.po
Normal file
1215
src/qt/languages/ko-KR.po
Normal file
File diff suppressed because it is too large
Load Diff
1215
src/qt/languages/pl-PL.po
Normal file
1215
src/qt/languages/pl-PL.po
Normal file
File diff suppressed because it is too large
Load Diff
1215
src/qt/languages/pt-BR.po
Normal file
1215
src/qt/languages/pt-BR.po
Normal file
File diff suppressed because it is too large
Load Diff
1215
src/qt/languages/pt-PT.po
Normal file
1215
src/qt/languages/pt-PT.po
Normal file
File diff suppressed because it is too large
Load Diff
1215
src/qt/languages/ru-RU.po
Normal file
1215
src/qt/languages/ru-RU.po
Normal file
File diff suppressed because it is too large
Load Diff
1215
src/qt/languages/sl-SI.po
Normal file
1215
src/qt/languages/sl-SI.po
Normal file
File diff suppressed because it is too large
Load Diff
1215
src/qt/languages/tr-TR.po
Normal file
1215
src/qt/languages/tr-TR.po
Normal file
File diff suppressed because it is too large
Load Diff
1215
src/qt/languages/uk-UA.po
Normal file
1215
src/qt/languages/uk-UA.po
Normal file
File diff suppressed because it is too large
Load Diff
1215
src/qt/languages/zh-CN.po
Normal file
1215
src/qt/languages/zh-CN.po
Normal file
File diff suppressed because it is too large
Load Diff
103
src/qt/macos_event_filter.mm
Normal file
103
src/qt/macos_event_filter.mm
Normal 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, result_t *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;
|
||||
}
|
||||
63
src/qt/qt.c
Normal file
63
src/qt/qt.c
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* C functionality for Qt platform, where the C equivalent is not easily
|
||||
* implemented in Qt
|
||||
*/
|
||||
#if !defined(_WIN32) || !defined(__clang__)
|
||||
#include <strings.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#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>
|
||||
|
||||
int qt_nvr_save(void) {
|
||||
return nvr_save();
|
||||
}
|
||||
|
||||
char icon_set[256] = ""; /* name of the iconset to be used */
|
||||
|
||||
int
|
||||
plat_vidapi(char* api) {
|
||||
if (!strcasecmp(api, "default") || !strcasecmp(api, "system")) {
|
||||
return 0;
|
||||
} else if (!strcasecmp(api, "qt_software")) {
|
||||
return 0;
|
||||
} else if (!strcasecmp(api, "qt_opengl")) {
|
||||
return 1;
|
||||
} else if (!strcasecmp(api, "qt_opengles")) {
|
||||
return 2;
|
||||
} else if (!strcasecmp(api, "qt_opengl3")) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* plat_vidapi_name(int api) {
|
||||
char* name = "default";
|
||||
|
||||
switch (api) {
|
||||
case 0:
|
||||
name = "qt_software";
|
||||
break;
|
||||
case 1:
|
||||
name = "qt_opengl";
|
||||
break;
|
||||
case 2:
|
||||
name = "qt_opengles";
|
||||
break;
|
||||
case 3:
|
||||
name = "qt_opengl3";
|
||||
break;
|
||||
default:
|
||||
fatal("Unknown renderer: %i\n", api);
|
||||
break;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
55
src/qt/qt_cdrom.c
Normal file
55
src/qt/qt_cdrom.c
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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
|
||||
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);
|
||||
}
|
||||
213
src/qt/qt_deviceconfig.cpp
Normal file
213
src/qt/qt_deviceconfig.cpp
Normal file
@@ -0,0 +1,213 @@
|
||||
#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 <86box/midi_rtmidi.h>
|
||||
}
|
||||
|
||||
#include "qt_filefield.hpp"
|
||||
#include "qt_models_common.hpp"
|
||||
|
||||
DeviceConfig::DeviceConfig(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::DeviceConfig)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
DeviceConfig::~DeviceConfig()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void DeviceConfig::ConfigureDevice(const _device_* device, int instance) {
|
||||
DeviceConfig dc;
|
||||
dc.setWindowTitle(QString("%1 Device Configuration").arg(device->name));
|
||||
|
||||
device_context_t device_context;
|
||||
device_set_context(&device_context, device, instance);
|
||||
|
||||
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_MIDI:
|
||||
{
|
||||
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 (int i = 0; i < rtmidi_get_num_devs(); i++) {
|
||||
char midiName[512] = { 0 };
|
||||
rtmidi_get_dev_name(i, midiName);
|
||||
|
||||
Models::AddEntry(model, midiName, i);
|
||||
if (selected == i) {
|
||||
currentIndex = i;
|
||||
}
|
||||
}
|
||||
dc.ui->formLayout->addRow(config->description, cbox);
|
||||
cbox->setCurrentIndex(currentIndex);
|
||||
break;
|
||||
}
|
||||
case CONFIG_MIDI_IN:
|
||||
{
|
||||
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 (int i = 0; i < rtmidi_in_get_num_devs(); i++) {
|
||||
char midiName[512] = { 0 };
|
||||
rtmidi_in_get_dev_name(i, midiName);
|
||||
|
||||
Models::AddEntry(model, midiName, i);
|
||||
if (selected == i) {
|
||||
currentIndex = i;
|
||||
}
|
||||
}
|
||||
dc.ui->formLayout->addRow(config->description, cbox);
|
||||
cbox->setCurrentIndex(currentIndex);
|
||||
break;
|
||||
}
|
||||
case CONFIG_SELECTION:
|
||||
case CONFIG_HEX16:
|
||||
case CONFIG_HEX20:
|
||||
{
|
||||
auto* cbox = new QComboBox();
|
||||
cbox->setObjectName(config->name);
|
||||
auto* model = cbox->model();
|
||||
int currentIndex = -1;
|
||||
int selected;
|
||||
switch (config->type) {
|
||||
case CONFIG_SELECTION:
|
||||
selected = config_get_int(device_context.name, const_cast<char*>(config->name), config->default_int);
|
||||
break;
|
||||
case CONFIG_HEX16:
|
||||
selected = config_get_hex16(device_context.name, const_cast<char*>(config->name), config->default_int);
|
||||
break;
|
||||
case CONFIG_HEX20:
|
||||
selected = config_get_hex20(device_context.name, const_cast<char*>(config->name), config->default_int);
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto* sel = config->selection; (sel->description != nullptr) && (strlen(sel->description) > 0); ++sel) {
|
||||
int row = Models::AddEntry(model, sel->description, sel->value);
|
||||
if (selected == sel->value) {
|
||||
currentIndex = 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);
|
||||
fileField->setFilter(QString(config->file_filter).left(strcspn(config->file_filter, "|")));
|
||||
dc.ui->formLayout->addRow(config->description, fileField);
|
||||
break;
|
||||
}
|
||||
}
|
||||
++config;
|
||||
}
|
||||
|
||||
dc.setFixedSize(dc.minimumSizeHint());
|
||||
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_MIDI:
|
||||
case CONFIG_MIDI_IN:
|
||||
case CONFIG_SELECTION:
|
||||
{
|
||||
auto* cbox = dc.findChild<QComboBox*>(config->name);
|
||||
config_set_int(device_context.name, const_cast<char*>(config->name), cbox->currentData().toInt());
|
||||
break;
|
||||
}
|
||||
case CONFIG_HEX16:
|
||||
{
|
||||
auto* cbox = dc.findChild<QComboBox*>(config->name);
|
||||
config_set_hex16(device_context.name, const_cast<char*>(config->name), cbox->currentData().toInt());
|
||||
break;
|
||||
}
|
||||
case CONFIG_HEX20:
|
||||
{
|
||||
auto* cbox = dc.findChild<QComboBox*>(config->name);
|
||||
config_set_hex20(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 tr("None");
|
||||
} else if (QStringLiteral("internal") == internalName) {
|
||||
return tr("Internal controller");
|
||||
} else if (device == nullptr) {
|
||||
return QString();
|
||||
} else {
|
||||
char temp[512];
|
||||
device_get_name(device, bus, temp);
|
||||
return tr(temp, nullptr, 512);
|
||||
}
|
||||
}
|
||||
28
src/qt/qt_deviceconfig.hpp
Normal file
28
src/qt/qt_deviceconfig.hpp
Normal 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, int instance = 0);
|
||||
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
74
src/qt/qt_deviceconfig.ui
Normal 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>
|
||||
41
src/qt/qt_filefield.cpp
Normal file
41
src/qt/qt_filefield.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include "qt_filefield.hpp"
|
||||
#include "ui_qt_filefield.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
|
||||
FileField::FileField(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::FileField)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
connect(ui->label, &QLineEdit::editingFinished, this, [this] () {
|
||||
fileName_ = ui->label->text();
|
||||
emit fileSelected(ui->label->text());
|
||||
});
|
||||
}
|
||||
|
||||
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, QString(), QString(), filter_, &selectedFilter_);
|
||||
} else {
|
||||
fileName = QFileDialog::getOpenFileName(this, QString(), QString(), filter_, &selectedFilter_);
|
||||
}
|
||||
|
||||
if (!fileName.isNull()) {
|
||||
fileName_ = fileName;
|
||||
ui->label->setText(fileName);
|
||||
emit fileSelected(fileName);
|
||||
}
|
||||
}
|
||||
41
src/qt/qt_filefield.hpp
Normal file
41
src/qt/qt_filefield.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#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 setFilter(const QString& filter) { filter_ = filter; }
|
||||
QString selectedFilter() const { return selectedFilter_; }
|
||||
|
||||
void setCreateFile(bool createFile) { createFile_ = createFile; }
|
||||
bool createFile() { return createFile_; }
|
||||
|
||||
signals:
|
||||
void fileSelected(const QString& fileName);
|
||||
|
||||
private slots:
|
||||
void on_pushButton_clicked();
|
||||
|
||||
private:
|
||||
Ui::FileField *ui;
|
||||
QString fileName_;
|
||||
QString selectedFilter_;
|
||||
QString filter_;
|
||||
bool createFile_ = false;
|
||||
};
|
||||
|
||||
#endif // QT_FILEFIELD_HPP
|
||||
62
src/qt/qt_filefield.ui
Normal file
62
src/qt/qt_filefield.ui
Normal file
@@ -0,0 +1,62 @@
|
||||
<?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>354</width>
|
||||
<height>25</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</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="QLineEdit" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Specify...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
739
src/qt/qt_harddiskdialog.cpp
Normal file
739
src/qt/qt_harddiskdialog.cpp
Normal file
@@ -0,0 +1,739 @@
|
||||
#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 <QStringBuilder>
|
||||
|
||||
#include "qt_harddrive_common.hpp"
|
||||
#include "qt_models_common.hpp"
|
||||
#include "qt_util.hpp"
|
||||
|
||||
HarddiskDialog::HarddiskDialog(bool existing, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::HarddiskDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->fileField->setFilter(tr("Hard disk images") % util::DlgFilter({ "hd?","im?","vhd" }) % tr("All files") % util::DlgFilter({ "*" }, true));
|
||||
if (existing) {
|
||||
setWindowTitle(tr("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(tr("Add New Hard Disk"));
|
||||
ui->fileField->setCreateFile(true);
|
||||
}
|
||||
|
||||
auto* model = ui->comboBoxFormat->model();
|
||||
model->insertRows(0, 6);
|
||||
model->setData(model->index(0, 0), tr("Raw image (.img)"));
|
||||
model->setData(model->index(1, 0), tr("HDI image (.hdi)"));
|
||||
model->setData(model->index(2, 0), tr("HDX image (.hdx)"));
|
||||
model->setData(model->index(3, 0), tr("Fixed-size VHD (.vhd)"));
|
||||
model->setData(model->index(4, 0), tr("Dynamic-size VHD (.vhd)"));
|
||||
model->setData(model->index(5, 0), tr("Differencing VHD (.vhd)"));
|
||||
|
||||
model = ui->comboBoxBlockSize->model();
|
||||
model->insertRows(0, 2);
|
||||
model->setData(model->index(0, 0), tr("Large blocks (2 MB)"));
|
||||
model->setData(model->index(1, 0), tr("Small blocks (512 KB)"));
|
||||
|
||||
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];
|
||||
uint32_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]);
|
||||
QString text = QString::asprintf(tr("%u MB (CHS: %i, %i, %i)").toUtf8().constData(), (size_mb), (hdd_table[i][0]), (hdd_table[i][1]), (hdd_table[i][2]));
|
||||
Models::AddEntry(model, text, i);
|
||||
}
|
||||
Models::AddEntry(model, tr("Custom..."), 127);
|
||||
Models::AddEntry(model, tr("Custom (large)..."), 128);
|
||||
|
||||
ui->lineEditSize->setValidator(new QIntValidator());
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
if (!existing) 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(tr("(N/A)"));
|
||||
ui->lineEditHeads->setText(tr("(N/A)"));
|
||||
ui->lineEditSectors->setText(tr("(N/A)"));
|
||||
ui->lineEditSize->setText(tr("(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() {
|
||||
|
||||
for (auto& curObject : children())
|
||||
{
|
||||
if (qobject_cast<QWidget*>(curObject)) qobject_cast<QWidget*>(curObject)->setDisabled(true);
|
||||
}
|
||||
|
||||
ui->progressBar->setEnabled(true);
|
||||
setResult(QDialog::Rejected);
|
||||
qint64 size = ui->lineEditSize->text().toUInt() << 20U;
|
||||
if (size > 0x1FFFFFFE00ll) {
|
||||
QMessageBox::critical(this, tr("Disk image too large"), tr("Disk images cannot be larger than 127 GB."));
|
||||
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, tr("Unable to write file"), tr("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, tr("Disk image too large"), tr("HDI disk images cannot be larger than 4 GB."));
|
||||
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:
|
||||
{
|
||||
connect(this, &HarddiskDialog::fileProgress, this, [this] (int value) { ui->progressBar->setValue(value); QApplication::processEvents(); } );
|
||||
ui->progressBar->setVisible(true);
|
||||
[&_86box_geometry, fileName, this] {
|
||||
_86box_geometry = create_drive_vhd_fixed(fileName, this, cylinders_, heads_, sectors_);
|
||||
}();
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
_86box_geometry = create_drive_vhd_dynamic(fileName, cylinders_, heads_, sectors_, block_size);
|
||||
break;
|
||||
case 5:
|
||||
QString vhdParent = QFileDialog::getOpenFileName(
|
||||
this,
|
||||
tr("Select the parent VHD"),
|
||||
QString(),
|
||||
tr("VHD files") %
|
||||
util::DlgFilter({ "vhd" }) %
|
||||
tr("All files") %
|
||||
util::DlgFilter({ "*" }, true));
|
||||
|
||||
if (vhdParent.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
_86box_geometry = create_drive_vhd_diff(fileName, vhdParent, block_size);
|
||||
break;
|
||||
}
|
||||
|
||||
if (_86box_geometry.cyl == 0 &&
|
||||
_86box_geometry.heads == 0 &&
|
||||
_86box_geometry.spt == 0)
|
||||
{
|
||||
QMessageBox::critical(this, tr("Unable to write file"), tr("Make sure the file is being saved to a writable directory."));
|
||||
return;
|
||||
}
|
||||
else if (img_format != 5) {
|
||||
QMessageBox::information(this, tr("Disk image created"), tr("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;
|
||||
setResult(QDialog::Accepted);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// formats 0, 1 and 2
|
||||
connect(this, &HarddiskDialog::fileProgress, this, [this] (int value) { ui->progressBar->setValue(value); QApplication::processEvents(); } );
|
||||
ui->progressBar->setVisible(true);
|
||||
[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.writeRawData(buf.data(), restBlock);
|
||||
}
|
||||
|
||||
if (mibBlocks) {
|
||||
for (uint64_t i = 0; i < mibBlocks; ++i) {
|
||||
stream.writeRawData(buf.data(), buf.size());
|
||||
emit fileProgress(static_cast<int>((i * 100) / mibBlocks));
|
||||
}
|
||||
}
|
||||
emit fileProgress(100);
|
||||
}();
|
||||
|
||||
QMessageBox::information(this, tr("Disk image created"), tr("Remember to partition and format the newly-created drive."));
|
||||
setResult(QDialog::Accepted);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
QFile file(fileName);
|
||||
if (! file.open(QIODevice::ReadOnly)) {
|
||||
QMessageBox::critical(this, tr("Unable to read file"), tr("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, tr("Unsupported disk image"), tr("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, tr("Unable to read file"), tr("Make sure the file exists and is readable"));
|
||||
return;
|
||||
} else if (vhd_error == MVHD_ERR_TIMESTAMP) {
|
||||
QMessageBox::StandardButton btn = QMessageBox::warning(this, tr("Parent and child disk timestamps do not match"), tr("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, tr("Error"), tr("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, tr("Unable to read file"), tr("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);
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->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_, §ors_, 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();
|
||||
}
|
||||
|
||||
void HarddiskDialog::accept()
|
||||
{
|
||||
if (ui->fileField->createFile()) onCreateNewFile();
|
||||
else setResult(QDialog::Accepted);
|
||||
QDialog::done(result());
|
||||
}
|
||||
59
src/qt/qt_harddiskdialog.hpp
Normal file
59
src/qt/qt_harddiskdialog.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#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);
|
||||
|
||||
public slots:
|
||||
void accept() override;
|
||||
|
||||
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
|
||||
285
src/qt/qt_harddiskdialog.ui
Normal file
285
src/qt/qt_harddiskdialog.ui
Normal file
@@ -0,0 +1,285 @@
|
||||
<?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>421</width>
|
||||
<height>269</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>421</width>
|
||||
<height>269</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>421</width>
|
||||
<height>269</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="1" colspan="5">
|
||||
<widget class="FileField" name="fileField" native="true"/>
|
||||
</item>
|
||||
<item row="5" column="4">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Channel:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="4">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Sectors:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="2">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Type:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="lineEditCylinders">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>64</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLineEdit" name="lineEditSize">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>64</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Size (MB):</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="5">
|
||||
<widget class="QComboBox" name="comboBoxChannel"/>
|
||||
</item>
|
||||
<item row="11" column="0" colspan="6">
|
||||
<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>
|
||||
<item row="9" column="0" colspan="6">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Cylinders:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="5">
|
||||
<widget class="QLineEdit" name="lineEditSectors">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>64</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maxLength">
|
||||
<number>32767</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="labelFormat">
|
||||
<property name="text">
|
||||
<string>Image Format:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="2">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Heads:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="3" colspan="3">
|
||||
<widget class="QComboBox" name="comboBoxType"/>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Bus:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1" colspan="3">
|
||||
<widget class="QComboBox" name="comboBoxFormat"/>
|
||||
</item>
|
||||
<item row="7" column="1" colspan="3">
|
||||
<widget class="QComboBox" name="comboBoxBlockSize"/>
|
||||
</item>
|
||||
<item row="3" column="3">
|
||||
<widget class="QLineEdit" name="lineEditHeads">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>64</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maxLength">
|
||||
<number>32767</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1" colspan="3">
|
||||
<widget class="QComboBox" name="comboBoxBus"/>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="labelBlockSize">
|
||||
<property name="text">
|
||||
<string>Block Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>File name:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0" colspan="6">
|
||||
<widget class="QProgressBar" name="progressBar">
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="textVisible">
|
||||
<bool>true</bool>
|
||||
</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>
|
||||
101
src/qt/qt_harddrive_common.cpp
Normal file
101
src/qt/qt_harddrive_common.cpp
Normal 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), QObject::tr("Disabled"));
|
||||
model->setData(model->index(1, 0), QObject::tr("ATAPI"));
|
||||
model->setData(model->index(2, 0), QObject::tr("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(QObject::tr("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;
|
||||
}
|
||||
15
src/qt/qt_harddrive_common.hpp
Normal file
15
src/qt/qt_harddrive_common.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class QString;
|
||||
class QAbstractItemModel;
|
||||
class SettingsBusTracking;
|
||||
|
||||
namespace Harddrives {
|
||||
void populateBuses(QAbstractItemModel* model);
|
||||
void populateRemovableBuses(QAbstractItemModel* model);
|
||||
void populateBusChannels(QAbstractItemModel* model, int bus);
|
||||
QString BusChannelName(uint8_t bus, uint8_t channel);
|
||||
inline SettingsBusTracking* busTrackClass = nullptr;
|
||||
};
|
||||
202
src/qt/qt_hardwarerenderer.cpp
Normal file
202
src/qt/qt_hardwarerenderer.cpp
Normal file
@@ -0,0 +1,202 @@
|
||||
#include "qt_hardwarerenderer.hpp"
|
||||
#include <QApplication>
|
||||
#include <QVector2D>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
|
||||
extern "C" {
|
||||
#include <86box/86box.h>
|
||||
#include <86box/plat.h>
|
||||
#include <86box/video.h>
|
||||
}
|
||||
|
||||
void HardwareRenderer::resizeGL(int w, int h)
|
||||
{
|
||||
glViewport(0, 0, w * devicePixelRatio(), h * devicePixelRatio());
|
||||
}
|
||||
|
||||
#define PROGRAM_VERTEX_ATTRIBUTE 0
|
||||
#define PROGRAM_TEXCOORD_ATTRIBUTE 1
|
||||
|
||||
void HardwareRenderer::initializeGL()
|
||||
{
|
||||
m_context->makeCurrent(this);
|
||||
initializeOpenGLFunctions();
|
||||
m_texture = new QOpenGLTexture(QImage(2048,2048, QImage::Format::Format_RGB32));
|
||||
m_blt = new QOpenGLTextureBlitter;
|
||||
m_blt->setRedBlueSwizzle(true);
|
||||
m_blt->create();
|
||||
QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this);
|
||||
const char *vsrc =
|
||||
"attribute highp vec4 VertexCoord;\n"
|
||||
"attribute mediump vec4 TexCoord;\n"
|
||||
"varying mediump vec4 texc;\n"
|
||||
"uniform mediump mat4 MVPMatrix;\n"
|
||||
"void main(void)\n"
|
||||
"{\n"
|
||||
" gl_Position = MVPMatrix * VertexCoord;\n"
|
||||
" texc = TexCoord;\n"
|
||||
"}\n";
|
||||
QString vsrccore =
|
||||
"in highp vec4 VertexCoord;\n"
|
||||
"in mediump vec4 TexCoord;\n"
|
||||
"out mediump vec4 texc;\n"
|
||||
"uniform mediump mat4 MVPMatrix;\n"
|
||||
"void main(void)\n"
|
||||
"{\n"
|
||||
" gl_Position = MVPMatrix * VertexCoord;\n"
|
||||
" texc = TexCoord;\n"
|
||||
"}\n";
|
||||
if (m_context->isOpenGLES() && m_context->format().version() >= qMakePair(3, 0))
|
||||
{
|
||||
vsrccore.prepend("#version 300 es\n");
|
||||
vshader->compileSourceCode(vsrccore);
|
||||
}
|
||||
else if (m_context->format().version() >= qMakePair(3, 0) && m_context->format().profile() == QSurfaceFormat::CoreProfile)
|
||||
{
|
||||
vsrccore.prepend("#version 130\n");
|
||||
vshader->compileSourceCode(vsrccore);
|
||||
}
|
||||
else vshader->compileSourceCode(vsrc);
|
||||
|
||||
QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this);
|
||||
const char *fsrc =
|
||||
"uniform sampler2D texture;\n"
|
||||
"varying mediump vec4 texc;\n"
|
||||
"void main(void)\n"
|
||||
"{\n"
|
||||
" gl_FragColor = texture2D(texture, texc.st).bgra;\n"
|
||||
"}\n";
|
||||
QString fsrccore =
|
||||
"uniform sampler2D texture;\n"
|
||||
"in mediump vec4 texc;\n"
|
||||
"out highp vec4 FragColor;\n"
|
||||
"void main(void)\n"
|
||||
"{\n"
|
||||
" FragColor = texture2D(texture, texc.st).bgra;\n"
|
||||
"}\n";
|
||||
if (m_context->isOpenGLES() && m_context->format().version() >= qMakePair(3, 0))
|
||||
{
|
||||
fsrccore.prepend("#version 300 es\n");
|
||||
fshader->compileSourceCode(fsrccore);
|
||||
}
|
||||
else if (m_context->format().version() >= qMakePair(3, 0) && m_context->format().profile() == QSurfaceFormat::CoreProfile)
|
||||
{
|
||||
fsrccore.prepend("#version 130\n");
|
||||
fshader->compileSourceCode(fsrccore);
|
||||
}
|
||||
else fshader->compileSourceCode(fsrc);
|
||||
|
||||
m_prog = new QOpenGLShaderProgram;
|
||||
m_prog->addShader(vshader);
|
||||
m_prog->addShader(fshader);
|
||||
m_prog->bindAttributeLocation("VertexCoord", PROGRAM_VERTEX_ATTRIBUTE);
|
||||
m_prog->bindAttributeLocation("TexCoord", PROGRAM_TEXCOORD_ATTRIBUTE);
|
||||
m_prog->link();
|
||||
|
||||
m_prog->bind();
|
||||
m_prog->setUniformValue("texture", 0);
|
||||
|
||||
if (m_context->format().version() >= qMakePair(3, 0) && m_vao.create()) {
|
||||
m_vao.bind();
|
||||
}
|
||||
|
||||
m_vbo[PROGRAM_VERTEX_ATTRIBUTE].create();
|
||||
m_vbo[PROGRAM_VERTEX_ATTRIBUTE].bind();
|
||||
m_vbo[PROGRAM_VERTEX_ATTRIBUTE].allocate(sizeof(QVector2D) * 4);
|
||||
m_vbo[PROGRAM_TEXCOORD_ATTRIBUTE].create();
|
||||
m_vbo[PROGRAM_TEXCOORD_ATTRIBUTE].bind();
|
||||
m_vbo[PROGRAM_TEXCOORD_ATTRIBUTE].allocate(sizeof(QVector2D) * 4);
|
||||
|
||||
pclog("OpenGL vendor: %s\n", glGetString(GL_VENDOR));
|
||||
pclog("OpenGL renderer: %s\n", glGetString(GL_RENDERER));
|
||||
pclog("OpenGL version: %s\n", glGetString(GL_VERSION));
|
||||
pclog("OpenGL shader language version: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
|
||||
glClearColor(0, 0, 0, 1);
|
||||
}
|
||||
|
||||
void HardwareRenderer::paintGL() {
|
||||
m_context->makeCurrent(this);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
QVector<QVector2D> verts, texcoords;
|
||||
QMatrix4x4 mat;
|
||||
mat.setToIdentity();
|
||||
mat.ortho(QRect(0, 0, width(), height()));
|
||||
verts.push_back(QVector2D((float)destination.x(), (float)destination.y()));
|
||||
verts.push_back(QVector2D((float)destination.x(), (float)destination.y() + destination.height()));
|
||||
verts.push_back(QVector2D((float)destination.x() + destination.width(), (float)destination.y() + destination.height()));
|
||||
verts.push_back(QVector2D((float)destination.x() + destination.width(), (float)destination.y()));
|
||||
texcoords.push_back(QVector2D((float)source.x() / 2048.f, (float)(source.y()) / 2048.f));
|
||||
texcoords.push_back(QVector2D((float)source.x() / 2048.f, (float)(source.y() + source.height()) / 2048.f));
|
||||
texcoords.push_back(QVector2D((float)(source.x() + source.width()) / 2048.f, (float)(source.y() + source.height()) / 2048.f));
|
||||
texcoords.push_back(QVector2D((float)(source.x() + source.width()) / 2048.f, (float)(source.y()) / 2048.f));
|
||||
m_vbo[PROGRAM_VERTEX_ATTRIBUTE].bind(); m_vbo[PROGRAM_VERTEX_ATTRIBUTE].write(0, verts.data(), sizeof(QVector2D) * 4); m_vbo[PROGRAM_VERTEX_ATTRIBUTE].release();
|
||||
m_vbo[PROGRAM_TEXCOORD_ATTRIBUTE].bind(); m_vbo[PROGRAM_TEXCOORD_ATTRIBUTE].write(0, texcoords.data(), sizeof(QVector2D) * 4); m_vbo[PROGRAM_TEXCOORD_ATTRIBUTE].release();
|
||||
|
||||
m_prog->setUniformValue("MVPMatrix", mat);
|
||||
m_prog->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE);
|
||||
m_prog->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE);
|
||||
|
||||
m_vbo[PROGRAM_VERTEX_ATTRIBUTE].bind(); m_prog->setAttributeBuffer(PROGRAM_VERTEX_ATTRIBUTE, GL_FLOAT, 0, 2, 0); m_vbo[PROGRAM_VERTEX_ATTRIBUTE].release();
|
||||
m_vbo[PROGRAM_TEXCOORD_ATTRIBUTE].bind(); m_prog->setAttributeBuffer(PROGRAM_TEXCOORD_ATTRIBUTE, GL_FLOAT, 0, 2, 0); m_vbo[PROGRAM_TEXCOORD_ATTRIBUTE].release();
|
||||
m_texture->bind();
|
||||
m_texture->setMinMagFilters(video_filter_method ? QOpenGLTexture::Linear : QOpenGLTexture::Nearest, video_filter_method ? QOpenGLTexture::Linear : QOpenGLTexture::Nearest);
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
}
|
||||
|
||||
void HardwareRenderer::setRenderType(RenderType type) {
|
||||
QSurfaceFormat format;
|
||||
switch (type) {
|
||||
case RenderType::OpenGL3:
|
||||
format.setVersion(3, 0);
|
||||
format.setProfile(QSurfaceFormat::CoreProfile);
|
||||
case RenderType::OpenGL:
|
||||
format.setRenderableType(QSurfaceFormat::OpenGL);
|
||||
break;
|
||||
case RenderType::OpenGLES:
|
||||
format.setRenderableType(QSurfaceFormat::OpenGLES);
|
||||
break;
|
||||
}
|
||||
format.setSwapInterval(0);
|
||||
setFormat(format);
|
||||
}
|
||||
|
||||
void HardwareRenderer::onBlit(int buf_idx, int x, int y, int w, int h) {
|
||||
auto tval = this;
|
||||
void* nuldata = 0;
|
||||
if (memcmp(&tval, &nuldata, sizeof(void*)) == 0) return;
|
||||
if (!m_texture || !m_texture->isCreated())
|
||||
{
|
||||
buf_usage[buf_idx].clear();
|
||||
source.setRect(x, y, w, h);
|
||||
return;
|
||||
}
|
||||
m_context->makeCurrent(this);
|
||||
m_texture->setData(QOpenGLTexture::PixelFormat::RGBA, QOpenGLTexture::PixelType::UInt8, (const void*)imagebufs[buf_idx].get());
|
||||
buf_usage[buf_idx].clear();
|
||||
source.setRect(x, y, w, h);
|
||||
update();
|
||||
}
|
||||
|
||||
void HardwareRenderer::resizeEvent(QResizeEvent *event) {
|
||||
onResize(width(), height());
|
||||
|
||||
QOpenGLWindow::resizeEvent(event);
|
||||
}
|
||||
|
||||
bool HardwareRenderer::event(QEvent *event)
|
||||
{
|
||||
bool res = false;
|
||||
if (!eventDelegate(event, res)) return QOpenGLWindow::event(event);
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<std::tuple<uint8_t*, std::atomic_flag*>> HardwareRenderer::getBuffers()
|
||||
{
|
||||
std::vector<std::tuple<uint8_t*, std::atomic_flag*>> buffers;
|
||||
|
||||
buffers.push_back(std::make_tuple(imagebufs[0].get(), &buf_usage[0]));
|
||||
buffers.push_back(std::make_tuple(imagebufs[1].get(), &buf_usage[1]));
|
||||
|
||||
return buffers;
|
||||
}
|
||||
92
src/qt/qt_hardwarerenderer.hpp
Normal file
92
src/qt/qt_hardwarerenderer.hpp
Normal file
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QOpenGLBuffer>
|
||||
#include <QOpenGLWindow>
|
||||
#include <QOpenGLVertexArrayObject>
|
||||
#include <QOpenGLTexture>
|
||||
#include <QOpenGLShader>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QOpenGLTextureBlitter>
|
||||
#include <QPainter>
|
||||
#include <QEvent>
|
||||
#include <QKeyEvent>
|
||||
#include <QWidget>
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <QApplication>
|
||||
|
||||
#include "qt_renderercommon.hpp"
|
||||
|
||||
#ifdef WAYLAND
|
||||
#include "wl_mouse.hpp"
|
||||
#endif
|
||||
|
||||
class HardwareRenderer : public QOpenGLWindow, protected QOpenGLFunctions, public RendererCommon
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
bool wayland = false;
|
||||
QOpenGLContext* m_context;
|
||||
QOpenGLTexture* m_texture{nullptr};
|
||||
QOpenGLShaderProgram* m_prog{nullptr};
|
||||
QOpenGLTextureBlitter* m_blt{nullptr};
|
||||
QOpenGLBuffer m_vbo[2];
|
||||
QOpenGLVertexArrayObject m_vao;
|
||||
public:
|
||||
enum class RenderType {
|
||||
OpenGL,
|
||||
OpenGLES,
|
||||
OpenGL3,
|
||||
};
|
||||
void resizeGL(int w, int h) override;
|
||||
void initializeGL() override;
|
||||
void paintGL() override;
|
||||
std::vector<std::tuple<uint8_t*, std::atomic_flag*>> getBuffers() override;
|
||||
HardwareRenderer(QWidget* parent = nullptr, RenderType rtype = RenderType::OpenGL)
|
||||
: QOpenGLWindow(QOpenGLWindow::NoPartialUpdate, parent->windowHandle()), QOpenGLFunctions()
|
||||
{
|
||||
imagebufs[0] = std::unique_ptr<uint8_t>(new uint8_t[2048 * 2048 * 4]);
|
||||
imagebufs[1] = std::unique_ptr<uint8_t>(new uint8_t[2048 * 2048 * 4]);
|
||||
|
||||
buf_usage = std::vector<std::atomic_flag>(2);
|
||||
buf_usage[0].clear();
|
||||
buf_usage[1].clear();
|
||||
|
||||
setMinimumSize(QSize(16, 16));
|
||||
setFlags(Qt::FramelessWindowHint);
|
||||
parentWidget = parent;
|
||||
setRenderType(rtype);
|
||||
|
||||
m_context = new QOpenGLContext();
|
||||
m_context->setFormat(format());
|
||||
m_context->create();
|
||||
}
|
||||
~HardwareRenderer()
|
||||
{
|
||||
m_context->makeCurrent(this);
|
||||
if (m_blt) m_blt->destroy();
|
||||
m_prog->release();
|
||||
delete m_prog;
|
||||
m_prog = nullptr;
|
||||
m_context->doneCurrent();
|
||||
delete m_context;
|
||||
}
|
||||
|
||||
|
||||
void setRenderType(RenderType type);
|
||||
|
||||
public slots:
|
||||
void onBlit(int buf_idx, int x, int y, int w, int h);
|
||||
|
||||
protected:
|
||||
std::array<std::unique_ptr<uint8_t>, 2> imagebufs;
|
||||
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
bool event(QEvent* event) override;
|
||||
};
|
||||
186
src/qt/qt_joystickconfiguration.cpp
Normal file
186
src/qt/qt_joystickconfiguration.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
#include "qt_joystickconfiguration.hpp"
|
||||
#include "ui_qt_joystickconfiguration.h"
|
||||
|
||||
extern "C" {
|
||||
#include <86box/device.h>
|
||||
#include <86box/gameport.h>
|
||||
}
|
||||
|
||||
|
||||
#include <QLabel>
|
||||
#include <QComboBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include "qt_models_common.hpp"
|
||||
|
||||
JoystickConfiguration::JoystickConfiguration(int type, int joystick_nr, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::JoystickConfiguration),
|
||||
type(type),
|
||||
joystick_nr(joystick_nr)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
auto model = ui->comboBoxDevice->model();
|
||||
Models::AddEntry(model, "None", 0);
|
||||
for (int c = 0; c < joysticks_present; c++) {
|
||||
Models::AddEntry(model, plat_joystick_state[c].name, c+1);
|
||||
}
|
||||
|
||||
ui->comboBoxDevice->setCurrentIndex(joystick_state[joystick_nr].plat_joystick_nr);
|
||||
setFixedSize(minimumSizeHint());
|
||||
}
|
||||
|
||||
JoystickConfiguration::~JoystickConfiguration()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
int JoystickConfiguration::selectedDevice() {
|
||||
return ui->comboBoxDevice->currentIndex();
|
||||
}
|
||||
|
||||
int JoystickConfiguration::selectedAxis(int axis) {
|
||||
auto* cbox = findChild<QComboBox*>(QString("cboxAxis%1").arg(QString::number(axis)));
|
||||
if (cbox == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return cbox->currentIndex();
|
||||
}
|
||||
|
||||
int JoystickConfiguration::selectedButton(int button) {
|
||||
auto* cbox = findChild<QComboBox*>(QString("cboxButton%1").arg(QString::number(button)));
|
||||
if (cbox == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return cbox->currentIndex();
|
||||
}
|
||||
|
||||
int JoystickConfiguration::selectedPov(int pov) {
|
||||
auto* cbox = findChild<QComboBox*>(QString("cboxPov%1").arg(QString::number(pov)));
|
||||
if (cbox == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return cbox->currentIndex();
|
||||
}
|
||||
|
||||
void JoystickConfiguration::on_comboBoxDevice_currentIndexChanged(int index) {
|
||||
for (auto w : widgets) {
|
||||
ui->ct->removeWidget(w);
|
||||
}
|
||||
|
||||
if (index == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int joystick = index - 1;
|
||||
int row = 0;
|
||||
for (int c = 0; c < joystick_get_axis_count(type); c++) {
|
||||
/*Combo box*/
|
||||
auto label = new QLabel(joystick_get_axis_name(type, c), this);
|
||||
auto cbox = new QComboBox(this);
|
||||
cbox->setObjectName(QString("cboxAxis%1").arg(QString::number(c)));
|
||||
auto model = cbox->model();
|
||||
|
||||
for (int d = 0; d < plat_joystick_state[joystick].nr_axes; d++) {
|
||||
Models::AddEntry(model, plat_joystick_state[joystick].axis[d].name, 0);
|
||||
}
|
||||
|
||||
for (int d = 0; d < plat_joystick_state[joystick].nr_povs; d++) {
|
||||
Models::AddEntry(model, QString("%1 (X axis)").arg(plat_joystick_state[joystick].pov[d].name), 0);
|
||||
Models::AddEntry(model, QString("%1 (Y axis)").arg(plat_joystick_state[joystick].pov[d].name), 0);
|
||||
}
|
||||
|
||||
for (int d = 0; d < plat_joystick_state[joystick].nr_sliders; d++) {
|
||||
Models::AddEntry(model, plat_joystick_state[joystick].slider[d].name, 0);
|
||||
}
|
||||
|
||||
int nr_axes = plat_joystick_state[joystick].nr_axes;
|
||||
int nr_povs = plat_joystick_state[joystick].nr_povs;
|
||||
int mapping = joystick_state[joystick_nr].axis_mapping[c];
|
||||
if (mapping & POV_X)
|
||||
cbox->setCurrentIndex(nr_axes + (mapping & 3) * 2);
|
||||
else if (mapping & POV_Y)
|
||||
cbox->setCurrentIndex(nr_axes + (mapping & 3) * 2 + 1);
|
||||
else if (mapping & SLIDER)
|
||||
cbox->setCurrentIndex(nr_axes + nr_povs * 2 + (mapping & 3));
|
||||
else
|
||||
cbox->setCurrentIndex(mapping);
|
||||
|
||||
ui->ct->addWidget(label, row, 0);
|
||||
ui->ct->addWidget(cbox, row, 1);
|
||||
|
||||
widgets.append(label);
|
||||
widgets.append(cbox);
|
||||
|
||||
++row;
|
||||
}
|
||||
|
||||
for (int c = 0; c < joystick_get_button_count(type); c++) {
|
||||
auto label = new QLabel(joystick_get_button_name(type, c), this);
|
||||
auto cbox = new QComboBox(this);
|
||||
cbox->setObjectName(QString("cboxButton%1").arg(QString::number(c)));
|
||||
auto model = cbox->model();
|
||||
|
||||
for (int d = 0; d < plat_joystick_state[joystick].nr_buttons; d++) {
|
||||
Models::AddEntry(model, plat_joystick_state[joystick].button[d].name, 0);
|
||||
}
|
||||
|
||||
cbox->setCurrentIndex(joystick_state[joystick_nr].button_mapping[c]);
|
||||
|
||||
ui->ct->addWidget(label, row, 0);
|
||||
ui->ct->addWidget(cbox, row, 1);
|
||||
|
||||
widgets.append(label);
|
||||
widgets.append(cbox);
|
||||
|
||||
++row;
|
||||
}
|
||||
|
||||
for (int c = 0; c < joystick_get_pov_count(type) * 2; c++) {
|
||||
QLabel* label;
|
||||
if (c & 1) {
|
||||
label = new QLabel(QString("%1 (Y axis)").arg(joystick_get_pov_name(type, c/2)), this);
|
||||
} else {
|
||||
label = new QLabel(QString("%1 (X axis)").arg(joystick_get_pov_name(type, c/2)), this);
|
||||
}
|
||||
auto cbox = new QComboBox(this);
|
||||
cbox->setObjectName(QString("cboxPov%1").arg(QString::number(c)));
|
||||
auto model = cbox->model();
|
||||
|
||||
for (int d = 0; d < plat_joystick_state[joystick].nr_povs; d++) {
|
||||
Models::AddEntry(model, QString("%1 (X axis)").arg(plat_joystick_state[joystick].pov[d].name), 0);
|
||||
Models::AddEntry(model, QString("%1 (Y axis)").arg(plat_joystick_state[joystick].pov[d].name), 0);
|
||||
}
|
||||
|
||||
for (int d = 0; d < plat_joystick_state[joystick].nr_axes; d++) {
|
||||
Models::AddEntry(model, plat_joystick_state[joystick].axis[d].name, 0);
|
||||
}
|
||||
|
||||
int mapping = joystick_state[joystick_nr].pov_mapping[c][0];
|
||||
int nr_povs = plat_joystick_state[joystick].nr_povs;
|
||||
if (mapping & POV_X)
|
||||
cbox->setCurrentIndex((mapping & 3) * 2);
|
||||
else if (mapping & POV_Y)
|
||||
cbox->setCurrentIndex((mapping & 3)*2 + 1);
|
||||
else
|
||||
cbox->setCurrentIndex(mapping + nr_povs * 2);
|
||||
|
||||
mapping = joystick_state[joystick_nr].pov_mapping[c][1];
|
||||
if (mapping & POV_X)
|
||||
cbox->setCurrentIndex((mapping & 3)*2);
|
||||
else if (mapping & POV_Y)
|
||||
cbox->setCurrentIndex((mapping & 3)*2 + 1);
|
||||
else
|
||||
cbox->setCurrentIndex(mapping + nr_povs*2);
|
||||
|
||||
ui->ct->addWidget(label, row, 0);
|
||||
ui->ct->addWidget(cbox, row, 1);
|
||||
|
||||
widgets.append(label);
|
||||
widgets.append(cbox);
|
||||
|
||||
++row;
|
||||
}
|
||||
|
||||
setFixedSize(minimumSizeHint());
|
||||
}
|
||||
32
src/qt/qt_joystickconfiguration.hpp
Normal file
32
src/qt/qt_joystickconfiguration.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef QT_JOYSTICKCONFIGURATION_HPP
|
||||
#define QT_JOYSTICKCONFIGURATION_HPP
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class JoystickConfiguration;
|
||||
}
|
||||
|
||||
class JoystickConfiguration : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit JoystickConfiguration(int type, int joystick_nr, QWidget *parent = nullptr);
|
||||
~JoystickConfiguration();
|
||||
|
||||
int selectedDevice();
|
||||
int selectedAxis(int axis);
|
||||
int selectedButton(int button);
|
||||
int selectedPov(int pov);
|
||||
private slots:
|
||||
void on_comboBoxDevice_currentIndexChanged(int index);
|
||||
|
||||
private:
|
||||
Ui::JoystickConfiguration *ui;
|
||||
QList<QWidget*> widgets;
|
||||
int type;
|
||||
int joystick_nr;
|
||||
};
|
||||
|
||||
#endif // QT_JOYSTICKCONFIGURATION_HPP
|
||||
87
src/qt/qt_joystickconfiguration.ui
Normal file
87
src/qt/qt_joystickconfiguration.ui
Normal file
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>JoystickConfiguration</class>
|
||||
<widget class="QDialog" name="JoystickConfiguration">
|
||||
<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="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0" colspan="2">
|
||||
<layout class="QGridLayout" name="ct"/>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="comboBoxDevice"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Device</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<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>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>JoystickConfiguration</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>223</x>
|
||||
<y>278</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>199</x>
|
||||
<y>149</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>JoystickConfiguration</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>223</x>
|
||||
<y>278</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>199</x>
|
||||
<y>149</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
571
src/qt/qt_machinestatus.cpp
Normal file
571
src/qt/qt_machinestatus.cpp
Normal file
@@ -0,0 +1,571 @@
|
||||
#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 <QMenu>
|
||||
|
||||
#include "qt_mediamenu.hpp"
|
||||
#include "qt_mainwindow.hpp"
|
||||
#include "qt_soundgain.hpp"
|
||||
#include "qt_progsettings.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
extern MainWindow* main_window;
|
||||
|
||||
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(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 = ProgSettings::loadIcon(basePath.arg(QStringLiteral(""))).pixmap(pixmap_size);
|
||||
empty = ProgSettings::loadIcon(basePath.arg(pixmap_empty)).pixmap(pixmap_size);
|
||||
}
|
||||
|
||||
void PixmapSetActive::load(const QString &basePath) {
|
||||
normal = ProgSettings::loadIcon(basePath.arg(QStringLiteral(""))).pixmap(pixmap_size);
|
||||
active = ProgSettings::loadIcon(basePath.arg(pixmap_active)).pixmap(pixmap_size);
|
||||
}
|
||||
|
||||
void PixmapSetEmptyActive::load(QString basePath) {
|
||||
normal = ProgSettings::loadIcon(basePath.arg(QStringLiteral(""))).pixmap(pixmap_size);
|
||||
active = ProgSettings::loadIcon(basePath.arg(pixmap_active)).pixmap(pixmap_size);
|
||||
empty = ProgSettings::loadIcon(basePath.arg(pixmap_empty)).pixmap(pixmap_size);
|
||||
empty_active = ProgSettings::loadIcon(basePath.arg(pixmap_empty_active)).pixmap(pixmap_size);
|
||||
}
|
||||
}
|
||||
|
||||
struct MachineStatus::States {
|
||||
Pixmaps pixmaps;
|
||||
|
||||
States(QObject* parent) {
|
||||
pixmaps.cartridge.load("/cartridge%1.ico");
|
||||
pixmaps.cassette.load("/cassette%1.ico");
|
||||
pixmaps.floppy_disabled.normal = ProgSettings::loadIcon(QStringLiteral("/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("/floppy_525%1.ico");
|
||||
pixmaps.floppy_35.load("/floppy_35%1.ico");
|
||||
pixmaps.cdrom.load("/cdrom%1.ico");
|
||||
pixmaps.zip.load("/zip%1.ico");
|
||||
pixmaps.mo.load("/mo%1.ico");
|
||||
pixmaps.hd.load("/hard_disk%1.ico");
|
||||
pixmaps.net.load("/network%1.ico");
|
||||
pixmaps.sound = ProgSettings::loadIcon("/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<ClickableLabel> sound;
|
||||
std::unique_ptr<QLabel> text;
|
||||
};
|
||||
|
||||
MachineStatus::MachineStatus(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
d = std::make_unique<MachineStatus::States>(this);
|
||||
}
|
||||
|
||||
MachineStatus::~MachineStatus() = default;
|
||||
|
||||
bool MachineStatus::hasCassette() {
|
||||
return cassette_enable > 0 ? true : false;
|
||||
}
|
||||
|
||||
bool MachineStatus::hasIDE() {
|
||||
return machine_has_flags(machine, MACHINE_IDE_QUAD) > 0;
|
||||
}
|
||||
|
||||
bool MachineStatus::hasSCSI() {
|
||||
return machine_has_flags(machine, MACHINE_SCSI_DUAL) > 0;
|
||||
}
|
||||
|
||||
void MachineStatus::iterateFDD(const std::function<void (int)> &cb) {
|
||||
for (int i = 0; i < FDD_NUM; ++i) {
|
||||
if (fdd_get_type(i) != 0) {
|
||||
cb(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MachineStatus::iterateCDROM(const std::function<void (int)> &cb) {
|
||||
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) &&
|
||||
!hasIDE() && hdc_name != QStringLiteral("ide") &&
|
||||
hdc_name != QStringLiteral("xtide"))
|
||||
continue;
|
||||
if ((cdrom[i].bus_type == CDROM_BUS_SCSI) && !hasSCSI() &&
|
||||
(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) {
|
||||
cb(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MachineStatus::iterateZIP(const std::function<void (int)> &cb) {
|
||||
auto hdc_name = QString(hdc_get_internal_name(hdc_current));
|
||||
for (size_t i = 0; i < ZIP_NUM; i++) {
|
||||
/* Could be Internal or External IDE.. */
|
||||
if ((zip_drives[i].bus_type == ZIP_BUS_ATAPI) &&
|
||||
!hasIDE() && hdc_name != QStringLiteral("ide") &&
|
||||
hdc_name != QStringLiteral("xtide"))
|
||||
continue;
|
||||
if ((zip_drives[i].bus_type == ZIP_BUS_SCSI) && !hasSCSI() &&
|
||||
(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) {
|
||||
cb(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MachineStatus::iterateMO(const std::function<void (int)> &cb) {
|
||||
auto hdc_name = QString(hdc_get_internal_name(hdc_current));
|
||||
for (size_t i = 0; i < MO_NUM; i++) {
|
||||
/* Could be Internal or External IDE.. */
|
||||
if ((mo_drives[i].bus_type == MO_BUS_ATAPI) &&
|
||||
!hasIDE() && hdc_name != QStringLiteral("ide") &&
|
||||
hdc_name != QStringLiteral("xtide"))
|
||||
continue;
|
||||
if ((mo_drives[i].bus_type == MO_BUS_SCSI) && !hasSCSI() &&
|
||||
(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) {
|
||||
cb(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_mfm = machine_has_flags(machine, MACHINE_MFM) > 0;
|
||||
bool has_xta = machine_has_flags(machine, MACHINE_XTA) > 0;
|
||||
bool has_esdi = machine_has_flags(machine, MACHINE_ESDI) > 0;
|
||||
|
||||
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<ClickableLabel>();
|
||||
d->cassette.setEmpty(QString(cassette_fname).isEmpty());
|
||||
connect((ClickableLabel*)d->cassette.label.get(), &ClickableLabel::clicked, [this](QPoint pos) {
|
||||
MediaMenu::ptr->cassetteMenu->popup(pos - QPoint(0, MediaMenu::ptr->cassetteMenu->sizeHint().height()));
|
||||
});
|
||||
d->cassette.label->setToolTip(MediaMenu::ptr->cassetteMenu->title());
|
||||
sbar->addWidget(d->cassette.label.get());
|
||||
}
|
||||
|
||||
if (machine_has_cartridge(machine)) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
d->cartridge[i].label = std::make_unique<ClickableLabel>();
|
||||
d->cartridge[i].setEmpty(QString(cart_fns[i]).isEmpty());
|
||||
connect((ClickableLabel*)d->cartridge[i].label.get(), &ClickableLabel::clicked, [this, i](QPoint pos) {
|
||||
MediaMenu::ptr->cartridgeMenus[i]->popup(pos - QPoint(0, MediaMenu::ptr->cartridgeMenus[i]->sizeHint().height()));
|
||||
});
|
||||
d->cartridge[i].label->setToolTip(MediaMenu::ptr->cartridgeMenus[i]->title());
|
||||
sbar->addWidget(d->cartridge[i].label.get());
|
||||
}
|
||||
}
|
||||
|
||||
iterateFDD([this, sbar](int i) {
|
||||
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].label = std::make_unique<ClickableLabel>();
|
||||
d->fdd[i].setEmpty(QString(floppyfns[i]).isEmpty());
|
||||
d->fdd[i].setActive(false);
|
||||
connect((ClickableLabel*)d->fdd[i].label.get(), &ClickableLabel::clicked, [this, i](QPoint pos) {
|
||||
MediaMenu::ptr->floppyMenus[i]->popup(pos - QPoint(0, MediaMenu::ptr->floppyMenus[i]->sizeHint().height()));
|
||||
});
|
||||
d->fdd[i].label->setToolTip(MediaMenu::ptr->floppyMenus[i]->title());
|
||||
sbar->addWidget(d->fdd[i].label.get());
|
||||
});
|
||||
|
||||
iterateCDROM([this, sbar](int i) {
|
||||
d->cdrom[i].label = std::make_unique<ClickableLabel>();
|
||||
d->cdrom[i].setEmpty(cdrom[i].host_drive != 200 || QString(cdrom[i].image_path).isEmpty());
|
||||
d->cdrom[i].setActive(false);
|
||||
connect((ClickableLabel*)d->cdrom[i].label.get(), &ClickableLabel::clicked, [this, i](QPoint pos) {
|
||||
MediaMenu::ptr->cdromMenus[i]->popup(pos - QPoint(0, MediaMenu::ptr->cdromMenus[i]->sizeHint().height()));
|
||||
});
|
||||
d->cdrom[i].label->setToolTip(MediaMenu::ptr->cdromMenus[i]->title());
|
||||
sbar->addWidget(d->cdrom[i].label.get());
|
||||
});
|
||||
|
||||
iterateZIP([this, sbar](int i) {
|
||||
d->zip[i].label = std::make_unique<ClickableLabel>();
|
||||
d->zip[i].setEmpty(QString(zip_drives[i].image_path).isEmpty());
|
||||
d->zip[i].setActive(false);
|
||||
connect((ClickableLabel*)d->zip[i].label.get(), &ClickableLabel::clicked, [this, i](QPoint pos) {
|
||||
MediaMenu::ptr->zipMenus[i]->popup(pos - QPoint(0, MediaMenu::ptr->zipMenus[i]->sizeHint().height()));
|
||||
});
|
||||
d->zip[i].label->setToolTip(MediaMenu::ptr->zipMenus[i]->title());
|
||||
sbar->addWidget(d->zip[i].label.get());
|
||||
});
|
||||
|
||||
iterateMO([this, sbar](int i) {
|
||||
d->mo[i].label = std::make_unique<ClickableLabel>();
|
||||
d->mo[i].setEmpty(QString(mo_drives[i].image_path).isEmpty());
|
||||
d->mo[i].setActive(false);
|
||||
connect((ClickableLabel*)d->mo[i].label.get(), &ClickableLabel::clicked, [this, i](QPoint pos) {
|
||||
MediaMenu::ptr->moMenus[i]->popup(pos - QPoint(0, MediaMenu::ptr->moMenus[i]->sizeHint().height()));
|
||||
});
|
||||
d->mo[i].label->setToolTip(MediaMenu::ptr->moMenus[i]->title());
|
||||
sbar->addWidget(d->mo[i].label.get());
|
||||
});
|
||||
|
||||
auto hdc_name = QString(hdc_get_internal_name(hdc_current));
|
||||
if ((has_mfm || hdc_name.left(5) == QStringLiteral("st506")) && c_mfm > 0) {
|
||||
d->hdds[HDD_BUS_MFM].label = std::make_unique<QLabel>();
|
||||
d->hdds[HDD_BUS_MFM].setActive(false);
|
||||
d->hdds[HDD_BUS_MFM].label->setToolTip(tr("Hard disk (%s)").replace("%s", "MFM/RLL"));
|
||||
sbar->addWidget(d->hdds[HDD_BUS_MFM].label.get());
|
||||
}
|
||||
if ((has_esdi || hdc_name.left(4) == QStringLiteral("esdi")) && c_esdi > 0) {
|
||||
d->hdds[HDD_BUS_ESDI].label = std::make_unique<QLabel>();
|
||||
d->hdds[HDD_BUS_ESDI].setActive(false);
|
||||
d->hdds[HDD_BUS_ESDI].label->setToolTip(tr("Hard disk (%s)").replace("%s", "ESDI"));
|
||||
sbar->addWidget(d->hdds[HDD_BUS_ESDI].label.get());
|
||||
}
|
||||
if ((has_xta || hdc_name.left(3) == QStringLiteral("xta")) && c_xta > 0) {
|
||||
d->hdds[HDD_BUS_XTA].label = std::make_unique<QLabel>();
|
||||
d->hdds[HDD_BUS_XTA].setActive(false);
|
||||
d->hdds[HDD_BUS_XTA].label->setToolTip(tr("Hard disk (%s)").replace("%s", "XTA"));
|
||||
sbar->addWidget(d->hdds[HDD_BUS_XTA].label.get());
|
||||
}
|
||||
if ((hasIDE() || hdc_name.left(5) == QStringLiteral("xtide") || hdc_name.left(3) == QStringLiteral("ide")) && c_ide > 0) {
|
||||
d->hdds[HDD_BUS_IDE].label = std::make_unique<QLabel>();
|
||||
d->hdds[HDD_BUS_IDE].setActive(false);
|
||||
d->hdds[HDD_BUS_IDE].label->setToolTip(tr("Hard disk (%s)").replace("%s", "IDE"));
|
||||
sbar->addWidget(d->hdds[HDD_BUS_IDE].label.get());
|
||||
}
|
||||
if ((hasSCSI() || (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);
|
||||
d->hdds[HDD_BUS_SCSI].label->setToolTip(tr("Hard disk (%s)").replace("%s", "SCSI"));
|
||||
sbar->addWidget(d->hdds[HDD_BUS_SCSI].label.get());
|
||||
}
|
||||
|
||||
if (do_net) {
|
||||
d->net.label = std::make_unique<QLabel>();
|
||||
d->net.setActive(false);
|
||||
d->net.label->setToolTip(tr("Network"));
|
||||
sbar->addWidget(d->net.label.get());
|
||||
}
|
||||
d->sound = std::make_unique<ClickableLabel>();
|
||||
d->sound->setPixmap(d->pixmaps.sound);
|
||||
|
||||
connect(d->sound.get(), &ClickableLabel::doubleClicked, d->sound.get(), [this](QPoint pos) {
|
||||
SoundGain gain(main_window);
|
||||
gain.exec();
|
||||
});
|
||||
d->sound->setToolTip(tr("Sound"));
|
||||
sbar->addWidget(d->sound.get());
|
||||
d->text = std::make_unique<QLabel>();
|
||||
sbar->addWidget(d->text.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;
|
||||
}
|
||||
}
|
||||
|
||||
void MachineStatus::message(const QString &msg) {
|
||||
d->text->setText(msg);
|
||||
}
|
||||
|
||||
QString MachineStatus::getMessage() {
|
||||
return d->text->text();
|
||||
}
|
||||
|
||||
void MachineStatus::updateTip(int tag)
|
||||
{
|
||||
int category = tag & 0xfffffff0;
|
||||
int item = tag & 0xf;
|
||||
switch (category) {
|
||||
case SB_CASSETTE:
|
||||
d->cassette.label->setToolTip(MediaMenu::ptr->cassetteMenu->title());
|
||||
break;
|
||||
case SB_CARTRIDGE:
|
||||
d->cartridge[item].label->setToolTip(MediaMenu::ptr->cartridgeMenus[item]->title());
|
||||
break;
|
||||
case SB_FLOPPY:
|
||||
d->fdd[item].label->setToolTip(MediaMenu::ptr->floppyMenus[item]->title());
|
||||
break;
|
||||
case SB_CDROM:
|
||||
d->cdrom[item].label->setToolTip(MediaMenu::ptr->cdromMenus[item]->title());
|
||||
break;
|
||||
case SB_ZIP:
|
||||
d->zip[item].label->setToolTip(MediaMenu::ptr->zipMenus[item]->title());
|
||||
break;
|
||||
case SB_MO:
|
||||
d->mo[item].label->setToolTip(MediaMenu::ptr->moMenus[item]->title());
|
||||
break;
|
||||
case SB_HDD:
|
||||
break;
|
||||
case SB_NETWORK:
|
||||
break;
|
||||
case SB_SOUND:
|
||||
break;
|
||||
case SB_TEXT:
|
||||
break;
|
||||
}
|
||||
}
|
||||
57
src/qt/qt_machinestatus.hpp
Normal file
57
src/qt/qt_machinestatus.hpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#ifndef QT_MACHINESTATUS_HPP
|
||||
#define QT_MACHINESTATUS_HPP
|
||||
|
||||
#include <QWidget>
|
||||
#include <QLabel>
|
||||
#include <QMouseEvent>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class QStatusBar;
|
||||
|
||||
class ClickableLabel : public QLabel {
|
||||
Q_OBJECT;
|
||||
public:
|
||||
explicit ClickableLabel(QWidget* parent = nullptr)
|
||||
: QLabel(parent) {}
|
||||
~ClickableLabel() {};
|
||||
|
||||
signals:
|
||||
void clicked(QPoint);
|
||||
void doubleClicked(QPoint);
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent* event) override { emit clicked(event->globalPos()); }
|
||||
void mouseDoubleClickEvent(QMouseEvent* event) override { emit doubleClicked(event->globalPos()); }
|
||||
};
|
||||
|
||||
class MachineStatus : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MachineStatus(QObject *parent = nullptr);
|
||||
~MachineStatus();
|
||||
|
||||
static bool hasCassette();
|
||||
static bool hasIDE();
|
||||
static bool hasSCSI();
|
||||
static void iterateFDD(const std::function<void(int i)>& cb);
|
||||
static void iterateCDROM(const std::function<void(int i)>& cb);
|
||||
static void iterateZIP(const std::function<void(int i)>& cb);
|
||||
static void iterateMO(const std::function<void(int i)>& cb);
|
||||
|
||||
QString getMessage();
|
||||
public slots:
|
||||
void refresh(QStatusBar* sbar);
|
||||
void setActivity(int tag, bool active);
|
||||
void setEmpty(int tag, bool active);
|
||||
void message(const QString& msg);
|
||||
void updateTip(int tag);
|
||||
|
||||
private:
|
||||
struct States;
|
||||
std::unique_ptr<States> d;
|
||||
};
|
||||
|
||||
#endif // QT_MACHINESTATUS_HPP
|
||||
233
src/qt/qt_main.cpp
Normal file
233
src/qt/qt_main.cpp
Normal file
@@ -0,0 +1,233 @@
|
||||
#include <QApplication>
|
||||
#include <QSurfaceFormat>
|
||||
#include <QDebug>
|
||||
#include <QElapsedTimer>
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
#include <QTranslator>
|
||||
#include <QDirIterator>
|
||||
#include <QLibraryInfo>
|
||||
|
||||
#ifdef QT_STATIC
|
||||
/* Static builds need plugin imports */
|
||||
#include <QtPlugin>
|
||||
Q_IMPORT_PLUGIN(QICOPlugin)
|
||||
#ifdef Q_OS_WINDOWS
|
||||
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)
|
||||
Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
#include "qt_winrawinputfilter.hpp"
|
||||
#include "qt_winmanagerfilter.hpp"
|
||||
#include <86box/win.h>
|
||||
#endif
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <86box/86box.h>
|
||||
#include <86box/config.h>
|
||||
#include <86box/plat.h>
|
||||
#include <86box/ui.h>
|
||||
#include <86box/video.h>
|
||||
#include <86box/discord.h>
|
||||
}
|
||||
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include "qt_mainwindow.hpp"
|
||||
#include "qt_progsettings.hpp"
|
||||
#include "qt_settings.hpp"
|
||||
#include "cocoa_mouse.hpp"
|
||||
#include "qt_styleoverride.hpp"
|
||||
|
||||
// Void Cast
|
||||
#define VC(x) const_cast<wchar_t*>(x)
|
||||
|
||||
extern QElapsedTimer elapsed_timer;
|
||||
extern MainWindow* main_window;
|
||||
|
||||
extern "C" {
|
||||
#include <86box/timer.h>
|
||||
#include <86box/nvr.h>
|
||||
extern int qt_nvr_save(void);
|
||||
}
|
||||
|
||||
void qt_set_sequence_auto_mnemonic(bool b);
|
||||
|
||||
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. */
|
||||
if (drawits < -1)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
else
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
/* If needed, handle a screen resize. */
|
||||
if (!atomic_flag_test_and_set(&doresize) && !video_fullscreen && !is_quit) {
|
||||
if (vid_resize & 2)
|
||||
plat_resize(fixed_size_x, fixed_size_y);
|
||||
else
|
||||
plat_resize(scrnsz_x, scrnsz_y);
|
||||
}
|
||||
}
|
||||
|
||||
is_quit = 1;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
QApplication app(argc, argv);
|
||||
qt_set_sequence_auto_mnemonic(false);
|
||||
Q_INIT_RESOURCE(qt_resources);
|
||||
Q_INIT_RESOURCE(qt_translations);
|
||||
QSurfaceFormat fmt = QSurfaceFormat::defaultFormat();
|
||||
fmt.setSwapInterval(0);
|
||||
QSurfaceFormat::setDefaultFormat(fmt);
|
||||
app.setStyle(new StyleOverride());
|
||||
|
||||
#ifdef __APPLE__
|
||||
CocoaEventFilter cocoafilter;
|
||||
app.installNativeEventFilter(&cocoafilter);
|
||||
#endif
|
||||
elapsed_timer.start();
|
||||
|
||||
if (!pc_init(argc, argv))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
ProgSettings::loadTranslators(&app);
|
||||
if (! pc_init_modules()) {
|
||||
ui_msgbox_header(MBX_FATAL, (void*)IDS_2120, (void*)IDS_2056);
|
||||
return 6;
|
||||
}
|
||||
|
||||
if (settings_only)
|
||||
{
|
||||
Settings settings;
|
||||
if (settings.exec() == QDialog::Accepted)
|
||||
{
|
||||
settings.save();
|
||||
config_save();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
discord_load();
|
||||
|
||||
main_window = new MainWindow();
|
||||
main_window->show();
|
||||
app.installEventFilter(main_window);
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
/* Setup VM-manager messages */
|
||||
std::unique_ptr<WindowsManagerFilter> wmfilter;
|
||||
if (source_hwnd)
|
||||
{
|
||||
HWND main_hwnd = (HWND)main_window->winId();
|
||||
|
||||
wmfilter.reset(new WindowsManagerFilter());
|
||||
QObject::connect(wmfilter.get(), &WindowsManagerFilter::showsettings, main_window, &MainWindow::showSettings);
|
||||
QObject::connect(wmfilter.get(), &WindowsManagerFilter::pause, main_window, &MainWindow::togglePause);
|
||||
QObject::connect(wmfilter.get(), &WindowsManagerFilter::reset, main_window, &MainWindow::hardReset);
|
||||
QObject::connect(wmfilter.get(), &WindowsManagerFilter::shutdown, [](){ plat_power_off(); });
|
||||
QObject::connect(wmfilter.get(), &WindowsManagerFilter::ctrlaltdel, [](){ pc_send_cad(); });
|
||||
QObject::connect(wmfilter.get(), &WindowsManagerFilter::dialogstatus, [main_hwnd](bool open){
|
||||
PostMessage((HWND)(uintptr_t)source_hwnd, WM_SENDDLGSTATUS, (WPARAM)(open ? 1 : 0), (LPARAM)main_hwnd);
|
||||
});
|
||||
|
||||
/* Native filter to catch VM-managers commands */
|
||||
app.installNativeEventFilter(wmfilter.get());
|
||||
|
||||
/* Filter to catch main window being blocked (by modal dialog) */
|
||||
main_window->installEventFilter(wmfilter.get());
|
||||
|
||||
/* Send main window HWND to manager */
|
||||
PostMessage((HWND)(uintptr_t)source_hwnd, WM_SENDHWND, (WPARAM)unique_id, (LPARAM)main_hwnd);
|
||||
|
||||
/* Send shutdown message to manager */
|
||||
QObject::connect(&app, &QApplication::destroyed, [main_hwnd](QObject*) {
|
||||
PostMessage((HWND)(uintptr_t)source_hwnd, WM_HAS_SHUTDOWN, (WPARAM)0, (LPARAM)main_hwnd);
|
||||
});
|
||||
}
|
||||
|
||||
/* Setup raw input */
|
||||
auto rawInputFilter = WindowsRawInputFilter::Register(main_window);
|
||||
if (rawInputFilter)
|
||||
{
|
||||
app.installNativeEventFilter(rawInputFilter.get());
|
||||
QObject::disconnect(main_window, &MainWindow::pollMouse, 0, 0);
|
||||
QObject::connect(main_window, &MainWindow::pollMouse, (WindowsRawInputFilter*)rawInputFilter.get(), &WindowsRawInputFilter::mousePoll, Qt::DirectConnection);
|
||||
main_window->setSendKeyboardInput(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
pc_reset_hard_init();
|
||||
|
||||
/* Set the PAUSE mode depending on the renderer. */
|
||||
// plat_pause(0);
|
||||
QTimer onesec;
|
||||
QTimer discordupdate;
|
||||
QObject::connect(&onesec, &QTimer::timeout, &app, [] {
|
||||
pc_onesec();
|
||||
});
|
||||
onesec.setTimerType(Qt::PreciseTimer);
|
||||
onesec.start(1000);
|
||||
if (discord_loaded) {
|
||||
QTimer::singleShot(1000, &app, [] {
|
||||
if (enable_discord) {
|
||||
discord_init();
|
||||
discord_update_activity(dopause);
|
||||
} else
|
||||
discord_close();
|
||||
});
|
||||
QObject::connect(&discordupdate, &QTimer::timeout, &app, [] {
|
||||
discord_run_callbacks();
|
||||
});
|
||||
discordupdate.start(0);
|
||||
}
|
||||
|
||||
/* Initialize the rendering window, or fullscreen. */
|
||||
auto main_thread = std::thread([] {
|
||||
main_thread_fn();
|
||||
});
|
||||
|
||||
auto ret = app.exec();
|
||||
cpu_thread_run = 0;
|
||||
main_thread.join();
|
||||
|
||||
return ret;
|
||||
}
|
||||
1577
src/qt/qt_mainwindow.cpp
Normal file
1577
src/qt/qt_mainwindow.cpp
Normal file
File diff suppressed because it is too large
Load Diff
136
src/qt/qt_mainwindow.hpp
Normal file
136
src/qt/qt_mainwindow.hpp
Normal file
@@ -0,0 +1,136 @@
|
||||
#ifndef QT_MAINWINDOW_HPP
|
||||
#define QT_MAINWINDOW_HPP
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QLabel>
|
||||
#include <QEvent>
|
||||
#include <QFocusEvent>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class MediaMenu;
|
||||
|
||||
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 getTitle(wchar_t* title);
|
||||
void blitToWidget(int x, int y, int w, int h);
|
||||
QSize getRenderWidgetSize();
|
||||
void setSendKeyboardInput(bool enabled);
|
||||
signals:
|
||||
void paint(const QImage& image);
|
||||
void resizeContents(int w, int h);
|
||||
void pollMouse();
|
||||
void statusBarMessage(const QString& msg);
|
||||
void updateStatusBarPanes();
|
||||
void updateStatusBarActivity(int tag, bool active);
|
||||
void updateStatusBarEmpty(int tag, bool empty);
|
||||
void updateStatusBarTip(int tag);
|
||||
void updateMenuResizeOptions();
|
||||
void updateWindowRememberOption();
|
||||
|
||||
void setTitle(const QString& title);
|
||||
void setFullscreen(bool state);
|
||||
void setMouseCapture(bool state);
|
||||
|
||||
void showMessageForNonQtThread(const QString& header, const QString& message);
|
||||
void getTitleForNonQtThread(wchar_t* title);
|
||||
public slots:
|
||||
void showSettings();
|
||||
void hardReset();
|
||||
void togglePause();
|
||||
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 on_actionHardware_Renderer_OpenGL_ES_triggered();
|
||||
void on_actionHardware_Renderer_OpenGL_triggered();
|
||||
void on_actionSoftware_Renderer_triggered();
|
||||
void on_actionResizable_window_triggered(bool checked);
|
||||
void on_actionInverted_VGA_monitor_triggered();
|
||||
void on_action0_5x_triggered();
|
||||
void on_action1x_triggered();
|
||||
void on_action1_5x_triggered();
|
||||
void on_action2x_triggered();
|
||||
void on_actionLinear_triggered();
|
||||
void on_actionNearest_triggered();
|
||||
void on_actionFullScreen_int_triggered();
|
||||
void on_actionFullScreen_keepRatio_triggered();
|
||||
void on_actionFullScreen_43_triggered();
|
||||
void on_actionFullScreen_stretch_triggered();
|
||||
void on_actionWhite_monitor_triggered();
|
||||
void on_actionGreen_monitor_triggered();
|
||||
void on_actionAmber_monitor_triggered();
|
||||
void on_actionRGB_Grayscale_triggered();
|
||||
void on_actionRGB_Color_triggered();
|
||||
void on_actionAverage_triggered();
|
||||
void on_actionBT709_HDTV_triggered();
|
||||
void on_actionBT601_NTSC_PAL_triggered();
|
||||
void on_actionDocumentation_triggered();
|
||||
void on_actionAbout_86Box_triggered();
|
||||
void on_actionAbout_Qt_triggered();
|
||||
void on_actionForce_4_3_display_ratio_triggered();
|
||||
void on_actionChange_contrast_for_monochrome_display_triggered();
|
||||
void on_actionCGA_PCjr_Tandy_EGA_S_VGA_overscan_triggered();
|
||||
void on_actionRemember_size_and_position_triggered();
|
||||
void on_actionSpecify_dimensions_triggered();
|
||||
void on_actionHiDPI_scaling_triggered();
|
||||
void on_actionHide_status_bar_triggered();
|
||||
void on_actionHide_tool_bar_triggered();
|
||||
void on_actionUpdate_status_bar_icons_triggered();
|
||||
|
||||
void refreshMediaMenu();
|
||||
void showMessage_(const QString& header, const QString& message);
|
||||
void getTitle_(wchar_t* title);
|
||||
void on_actionTake_screenshot_triggered();
|
||||
|
||||
void on_actionSound_gain_triggered();
|
||||
|
||||
void on_actionOpenGL_3_0_Core_triggered();
|
||||
|
||||
void on_actionPreferences_triggered();
|
||||
|
||||
void on_actionEnable_Discord_integration_triggered(bool checked);
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
void keyReleaseEvent(QKeyEvent* event) override;
|
||||
void focusInEvent(QFocusEvent* event) override;
|
||||
void focusOutEvent(QFocusEvent* event) override;
|
||||
bool eventFilter(QObject* receiver, QEvent* event) override;
|
||||
void showEvent(QShowEvent* event) override;
|
||||
void closeEvent(QCloseEvent* event) override;
|
||||
void changeEvent(QEvent* event) override;
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
std::unique_ptr<MachineStatus> status;
|
||||
std::shared_ptr<MediaMenu> mm;
|
||||
|
||||
/* If main window should send keyboard input */
|
||||
bool send_keyboard_input = true;
|
||||
bool shownonce = false;
|
||||
|
||||
friend class SpecifyDimensions;
|
||||
friend class ProgSettings;
|
||||
};
|
||||
|
||||
#endif // QT_MAINWINDOW_HPP
|
||||
713
src/qt/qt_mainwindow.ui
Normal file
713
src/qt/qt_mainwindow.ui
Normal file
@@ -0,0 +1,713 @@
|
||||
<?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="RendererStack" name="stackedWidget"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>724</width>
|
||||
<height>22</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"/>
|
||||
<addaction name="actionUpdate_status_bar_icons"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionEnable_Discord_integration"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionTake_screenshot"/>
|
||||
<addaction name="actionSound_gain"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionPreferences"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionBegin_trace"/>
|
||||
<addaction name="actionEnd_trace"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuView">
|
||||
<property name="title">
|
||||
<string>&View</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuRenderer">
|
||||
<property name="title">
|
||||
<string>Re&nderer</string>
|
||||
</property>
|
||||
<addaction name="actionSoftware_Renderer"/>
|
||||
<addaction name="actionHardware_Renderer_OpenGL"/>
|
||||
<addaction name="actionHardware_Renderer_OpenGL_ES"/>
|
||||
<addaction name="actionOpenGL_3_0_Core"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuWindow_scale_factor">
|
||||
<property name="title">
|
||||
<string>&Window scale factor</string>
|
||||
</property>
|
||||
<addaction name="action0_5x"/>
|
||||
<addaction name="action1x"/>
|
||||
<addaction name="action1_5x"/>
|
||||
<addaction name="action2x"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuFilter_method">
|
||||
<property name="title">
|
||||
<string>Filter method</string>
|
||||
</property>
|
||||
<addaction name="actionNearest"/>
|
||||
<addaction name="actionLinear"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuFullscreen_stretch_mode">
|
||||
<property name="title">
|
||||
<string>Fullscreen &stretch mode</string>
|
||||
</property>
|
||||
<addaction name="actionFullScreen_stretch"/>
|
||||
<addaction name="actionFullScreen_43"/>
|
||||
<addaction name="actionFullScreen_keepRatio"/>
|
||||
<addaction name="actionFullScreen_int"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuEGA_S_VGA_settings">
|
||||
<property name="title">
|
||||
<string>E&GA/(S)VGA settings</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuVGA_screen_type">
|
||||
<property name="title">
|
||||
<string>VGA screen &type</string>
|
||||
</property>
|
||||
<addaction name="actionRGB_Color"/>
|
||||
<addaction name="actionRGB_Grayscale"/>
|
||||
<addaction name="actionAmber_monitor"/>
|
||||
<addaction name="actionGreen_monitor"/>
|
||||
<addaction name="actionWhite_monitor"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuGrayscale_conversion_type">
|
||||
<property name="title">
|
||||
<string>Grayscale &conversion type</string>
|
||||
</property>
|
||||
<addaction name="actionBT601_NTSC_PAL"/>
|
||||
<addaction name="actionBT709_HDTV"/>
|
||||
<addaction name="actionAverage"/>
|
||||
</widget>
|
||||
<addaction name="actionInverted_VGA_monitor"/>
|
||||
<addaction name="menuVGA_screen_type"/>
|
||||
<addaction name="menuGrayscale_conversion_type"/>
|
||||
</widget>
|
||||
<addaction name="actionHide_tool_bar"/>
|
||||
<addaction name="actionHide_status_bar"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionResizable_window"/>
|
||||
<addaction name="actionRemember_size_and_position"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="menuRenderer"/>
|
||||
<addaction name="actionSpecify_dimensions"/>
|
||||
<addaction name="actionForce_4_3_display_ratio"/>
|
||||
<addaction name="menuWindow_scale_factor"/>
|
||||
<addaction name="menuFilter_method"/>
|
||||
<addaction name="actionHiDPI_scaling"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionFullscreen"/>
|
||||
<addaction name="menuFullscreen_stretch_mode"/>
|
||||
<addaction name="menuEGA_S_VGA_settings"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionCGA_PCjr_Tandy_EGA_S_VGA_overscan"/>
|
||||
<addaction name="actionChange_contrast_for_monochrome_display"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuMedia">
|
||||
<property name="title">
|
||||
<string>&Media</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuAbout">
|
||||
<property name="title">
|
||||
<string>&Help</string>
|
||||
</property>
|
||||
<addaction name="actionDocumentation"/>
|
||||
<addaction name="actionAbout_86Box"/>
|
||||
<addaction name="actionAbout_Qt"/>
|
||||
</widget>
|
||||
<addaction name="menuAction"/>
|
||||
<addaction name="menuView"/>
|
||||
<addaction name="menuMedia"/>
|
||||
<addaction name="menuTools"/>
|
||||
<addaction name="menuAbout"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
<widget class="QToolBar" name="toolBar">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::PreventContextMenu</enum>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">toolBar</string>
|
||||
</property>
|
||||
<property name="movable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="allowedAreas">
|
||||
<set>Qt::TopToolBarArea</set>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonIconOnly</enum>
|
||||
</property>
|
||||
<property name="floatable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="toolBarArea">
|
||||
<enum>TopToolBarArea</enum>
|
||||
</attribute>
|
||||
<attribute name="toolBarBreak">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<addaction name="actionPause"/>
|
||||
<addaction name="actionHard_Reset"/>
|
||||
<addaction name="actionACPI_Shutdown"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionCtrl_Alt_Del"/>
|
||||
<addaction name="actionCtrl_Alt_Esc"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionSettings"/>
|
||||
</widget>
|
||||
<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="icon">
|
||||
<iconset resource="../qt_resources.qrc">
|
||||
<normaloff>:/menuicons/win/icons/hard_reset.ico</normaloff>:/menuicons/win/icons/hard_reset.ico</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Hard Reset...</string>
|
||||
</property>
|
||||
<property name="iconVisibleInMenu">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCtrl_Alt_Del">
|
||||
<property name="icon">
|
||||
<iconset resource="../qt_resources.qrc">
|
||||
<normaloff>:/menuicons/win/icons/send_cad.ico</normaloff>:/menuicons/win/icons/send_cad.ico</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Ctrl+Alt+Del</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Ctrl+Alt+Del</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+F12</string>
|
||||
</property>
|
||||
<property name="iconVisibleInMenu">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="shortcutVisibleInContextMenu">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCtrl_Alt_Esc">
|
||||
<property name="icon">
|
||||
<iconset resource="../qt_resources.qrc">
|
||||
<normaloff>:/menuicons/win/icons/send_cae.ico</normaloff>:/menuicons/win/icons/send_cae.ico</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Ctrl+Alt+&Esc</string>
|
||||
</property>
|
||||
<property name="iconVisibleInMenu">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPause">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../qt_resources.qrc">
|
||||
<normaloff>:/menuicons/win/icons/pause.ico</normaloff>:/menuicons/win/icons/pause.ico</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Pause</string>
|
||||
</property>
|
||||
<property name="iconVisibleInMenu">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExit">
|
||||
<property name="text">
|
||||
<string>Exit</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSettings">
|
||||
<property name="icon">
|
||||
<iconset resource="../qt_resources.qrc">
|
||||
<normaloff>:/menuicons/win/icons/settings.ico</normaloff>:/menuicons/win/icons/settings.ico</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Settings...</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::PreferencesRole</enum>
|
||||
</property>
|
||||
<property name="iconVisibleInMenu">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFullscreen">
|
||||
<property name="text">
|
||||
<string>&Fullscreen</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Alt+PgUp</string>
|
||||
</property>
|
||||
<property name="shortcutVisibleInContextMenu">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSoftware_Renderer">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Qt (Software)</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionHardware_Renderer_OpenGL">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Qt (&OpenGL)</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionHardware_Renderer_OpenGL_ES">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Qt (OpenGL &ES)</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionHide_status_bar">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Hide status bar</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionResizable_window">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Resizeable window</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRemember_size_and_position">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>R&emember size && position</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSpecify_dimensions">
|
||||
<property name="text">
|
||||
<string>Specify dimensions...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionForce_4_3_display_ratio">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>F&orce 4:3 display ratio</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionHiDPI_scaling">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Hi&DPI scaling</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCGA_PCjr_Tandy_EGA_S_VGA_overscan">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>CGA/PCjr/Tandy/E&GA/(S)VGA overscan</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionChange_contrast_for_monochrome_display">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Change contrast for &monochrome display</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action0_5x">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&0.5x</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action1x">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&1x</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action1_5x">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>1.&5x</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action2x">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&2x</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionNearest">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Nearest</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionLinear">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Linear</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFullScreen_stretch">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Full screen stretch</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFullScreen_43">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&4:3</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFullScreen_keepRatio">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Square pixels (Keep ratio)</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFullScreen_int">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Integer scale</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionInverted_VGA_monitor">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Inverted VGA monitor</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRGB_Color">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>RGB &Color</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRGB_Grayscale">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&RGB Grayscale</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAmber_monitor">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Amber monitor</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGreen_monitor">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Green monitor</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionWhite_monitor">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&White monitor</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionBT601_NTSC_PAL">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>BT&601 (NTSC/PAL)</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionBT709_HDTV">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>BT&709 (HDTV)</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAverage">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Average</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAbout_Qt">
|
||||
<property name="text">
|
||||
<string>About Qt</string>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::AboutQtRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAbout_86Box">
|
||||
<property name="text">
|
||||
<string>&About 86Box...</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::AboutRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDocumentation">
|
||||
<property name="text">
|
||||
<string>&Documentation...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionUpdate_status_bar_icons">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Update status bar icons</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionTake_screenshot">
|
||||
<property name="text">
|
||||
<string>Take s&creenshot</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+F11</string>
|
||||
</property>
|
||||
<property name="shortcutVisibleInContextMenu">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSound_gain">
|
||||
<property name="text">
|
||||
<string>Sound &gain...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionOpenGL_3_0_Core">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Open&GL (3.0 Core)</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPreferences">
|
||||
<property name="text">
|
||||
<string>&Preferences...</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEnable_Discord_integration">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable &Discord integration</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionHide_tool_bar">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Hide tool bar</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Hide tool bar</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionACPI_Shutdown">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../qt_resources.qrc">
|
||||
<normaloff>:/menuicons/win/icons/acpi_shutdown.ico</normaloff>:/menuicons/win/icons/acpi_shutdown.ico</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>ACPI Shutdown</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>ACPI Shutdown</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionBegin_trace">
|
||||
<property name="text">
|
||||
<string>Begin trace</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+T</string>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="shortcutVisibleInContextMenu">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEnd_trace">
|
||||
<property name="text">
|
||||
<string>End trace</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+T</string>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="shortcutVisibleInContextMenu">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>RendererStack</class>
|
||||
<extends>QStackedWidget</extends>
|
||||
<header>qt_rendererstack.hpp</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../qt_resources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
636
src/qt/qt_mediamenu.cpp
Normal file
636
src/qt/qt_mediamenu.cpp
Normal file
@@ -0,0 +1,636 @@
|
||||
#include "qt_mediamenu.hpp"
|
||||
|
||||
#include "qt_machinestatus.hpp"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QStringBuilder>
|
||||
|
||||
extern "C" {
|
||||
#include <86box/config.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/timer.h>
|
||||
#include <86box/plat.h>
|
||||
#include <86box/cassette.h>
|
||||
#include <86box/cartridge.h>
|
||||
#include <86box/fdd.h>
|
||||
#include <86box/fdd_86f.h>
|
||||
#include <86box/cdrom.h>
|
||||
#include <86box/scsi_device.h>
|
||||
#include <86box/zip.h>
|
||||
#include <86box/mo.h>
|
||||
#include <86box/sound.h>
|
||||
#include <86box/ui.h>
|
||||
};
|
||||
|
||||
#include "qt_newfloppydialog.hpp"
|
||||
#include "qt_util.hpp"
|
||||
|
||||
std::shared_ptr<MediaMenu> MediaMenu::ptr;
|
||||
|
||||
MediaMenu::MediaMenu(QWidget* parent) : QObject(parent) {
|
||||
parentWidget = parent;
|
||||
}
|
||||
|
||||
void MediaMenu::refresh(QMenu *parentMenu) {
|
||||
parentMenu->clear();
|
||||
|
||||
if(MachineStatus::hasCassette()) {
|
||||
cassetteMenu = parentMenu->addMenu("");
|
||||
cassetteMenu->addAction(tr("&New image..."), [this]() { cassetteNewImage(); });
|
||||
cassetteMenu->addSeparator();
|
||||
cassetteMenu->addAction(tr("&Existing image..."), [this]() { cassetteSelectImage(false); });
|
||||
cassetteMenu->addAction(tr("Existing image (&Write-protected)..."), [this]() { cassetteSelectImage(true); });
|
||||
cassetteMenu->addSeparator();
|
||||
cassetteRecordPos = cassetteMenu->children().count();
|
||||
cassetteMenu->addAction(tr("&Record"), [this] { pc_cas_set_mode(cassette, 1); cassetteUpdateMenu(); })->setCheckable(true);
|
||||
cassettePlayPos = cassetteMenu->children().count();
|
||||
cassetteMenu->addAction(tr("&Play"), [this] { pc_cas_set_mode(cassette, 0); cassetteUpdateMenu(); })->setCheckable(true);
|
||||
cassetteRewindPos = cassetteMenu->children().count();
|
||||
cassetteMenu->addAction(tr("&Rewind to the beginning"), [] { pc_cas_rewind(cassette); });
|
||||
cassetteFastFwdPos = cassetteMenu->children().count();
|
||||
cassetteMenu->addAction(tr("&Fast forward to the end"), [] { pc_cas_append(cassette); });
|
||||
cassetteMenu->addSeparator();
|
||||
cassetteEjectPos = cassetteMenu->children().count();
|
||||
cassetteMenu->addAction(tr("E&ject"), [this]() { cassetteEject(); });
|
||||
cassetteUpdateMenu();
|
||||
}
|
||||
|
||||
cartridgeMenus.clear();
|
||||
if (machine_has_cartridge(machine)) {
|
||||
for(int i = 0; i < 2; i++) {
|
||||
auto* menu = parentMenu->addMenu("");
|
||||
menu->addAction(tr("&Image..."), [this, i]() { cartridgeSelectImage(i); });
|
||||
menu->addSeparator();
|
||||
cartridgeEjectPos = menu->children().count();
|
||||
menu->addAction(tr("E&ject"), [this, i]() { cartridgeEject(i); });
|
||||
cartridgeMenus[i] = menu;
|
||||
cartridgeUpdateMenu(i);
|
||||
}
|
||||
}
|
||||
|
||||
floppyMenus.clear();
|
||||
MachineStatus::iterateFDD([this, parentMenu](int i) {
|
||||
auto* menu = parentMenu->addMenu("");
|
||||
menu->addAction(tr("&New image..."), [this, i]() { floppyNewImage(i); });
|
||||
menu->addSeparator();
|
||||
menu->addAction(tr("&Existing image..."), [this, i]() { floppySelectImage(i, false); });
|
||||
menu->addAction(tr("Existing image (&Write-protected)..."), [this, i]() { floppySelectImage(i, true); });
|
||||
menu->addSeparator();
|
||||
floppyExportPos = menu->children().count();
|
||||
menu->addAction(tr("E&xport to 86F..."), [this, i]() { floppyExportTo86f(i); });
|
||||
menu->addSeparator();
|
||||
floppyEjectPos = menu->children().count();
|
||||
menu->addAction(tr("E&ject"), [this, i]() { floppyEject(i); });
|
||||
floppyMenus[i] = menu;
|
||||
floppyUpdateMenu(i);
|
||||
});
|
||||
|
||||
cdromMenus.clear();
|
||||
MachineStatus::iterateCDROM([this, parentMenu](int i) {
|
||||
auto* menu = parentMenu->addMenu("");
|
||||
cdromMutePos = menu->children().count();
|
||||
menu->addAction(tr("&Mute"), [this, i]() { cdromMute(i); })->setCheckable(true);
|
||||
menu->addSeparator();
|
||||
cdromEmptyPos = menu->children().count();
|
||||
menu->addAction(tr("E&mpty"), [this, i]() { cdromEject(i); })->setCheckable(true);
|
||||
cdromReloadPos = menu->children().count();
|
||||
menu->addAction(tr("&Reload previous image"), [this, i]() { cdromReload(i); });
|
||||
menu->addSeparator();
|
||||
cdromImagePos = menu->children().count();
|
||||
menu->addAction(tr("&Image"), [this, i]() { cdromMount(i); })->setCheckable(true);
|
||||
cdromMenus[i] = menu;
|
||||
cdromUpdateMenu(i);
|
||||
});
|
||||
|
||||
zipMenus.clear();
|
||||
MachineStatus::iterateZIP([this, parentMenu](int i) {
|
||||
auto* menu = parentMenu->addMenu("");
|
||||
menu->addAction(tr("&New image..."), [this, i]() { zipNewImage(i); });
|
||||
menu->addSeparator();
|
||||
menu->addAction(tr("&Existing image..."), [this, i]() { zipSelectImage(i, false); });
|
||||
menu->addAction(tr("Existing image (&Write-protected)..."), [this, i]() { zipSelectImage(i, true); });
|
||||
menu->addSeparator();
|
||||
zipEjectPos = menu->children().count();
|
||||
menu->addAction(tr("E&ject"), [this, i]() { zipEject(i); });
|
||||
zipReloadPos = menu->children().count();
|
||||
menu->addAction(tr("&Reload previous image"), [this, i]() { zipReload(i); });
|
||||
zipMenus[i] = menu;
|
||||
zipUpdateMenu(i);
|
||||
});
|
||||
|
||||
moMenus.clear();
|
||||
MachineStatus::iterateMO([this, parentMenu](int i) {
|
||||
auto* menu = parentMenu->addMenu("");
|
||||
menu->addAction(tr("&New image..."), [this, i]() { moNewImage(i); });
|
||||
menu->addSeparator();
|
||||
menu->addAction(tr("&Existing image..."), [this, i]() { moSelectImage(i, false); });
|
||||
menu->addAction(tr("Existing image (&Write-protected)..."), [this, i]() { moSelectImage(i, true); });
|
||||
menu->addSeparator();
|
||||
moEjectPos = menu->children().count();
|
||||
menu->addAction(tr("E&ject"), [this, i]() { moEject(i); });
|
||||
moReloadPos = menu->children().count();
|
||||
menu->addAction(tr("&Reload previous image"), [this, i]() { moReload(i); });
|
||||
moMenus[i] = menu;
|
||||
moUpdateMenu(i);
|
||||
});
|
||||
}
|
||||
|
||||
void MediaMenu::cassetteNewImage() {
|
||||
auto filename = QFileDialog::getSaveFileName(parentWidget, tr("Create..."));
|
||||
QFileInfo fileinfo(filename);
|
||||
if (fileinfo.suffix().isEmpty()) {
|
||||
filename.append(".cas");
|
||||
}
|
||||
if (!filename.isNull()) {
|
||||
if (filename.isEmpty()) cassetteEject();
|
||||
else cassetteMount(filename, false);
|
||||
}
|
||||
}
|
||||
|
||||
void MediaMenu::cassetteSelectImage(bool wp) {
|
||||
auto filename = QFileDialog::getOpenFileName(parentWidget,
|
||||
QString(),
|
||||
QString(),
|
||||
tr("Cassette images") %
|
||||
util::DlgFilter({ "pcm","raw","wav","cas" }) %
|
||||
tr("All files") %
|
||||
util::DlgFilter({ "*" }, true));
|
||||
|
||||
if (!filename.isEmpty()) cassetteMount(filename, wp);
|
||||
}
|
||||
|
||||
void MediaMenu::cassetteMount(const QString& filename, bool wp) {
|
||||
pc_cas_set_fname(cassette, nullptr);
|
||||
memset(cassette_fname, 0, sizeof(cassette_fname));
|
||||
cassette_ui_writeprot = wp ? 1 : 0;
|
||||
|
||||
if (! filename.isEmpty()) {
|
||||
QByteArray filenameBytes = filename.toUtf8();
|
||||
strncpy(cassette_fname, filenameBytes.data(), sizeof(cassette_fname));
|
||||
pc_cas_set_fname(cassette, cassette_fname);
|
||||
}
|
||||
|
||||
ui_sb_update_icon_state(SB_CASSETTE, filename.isEmpty() ? 1 : 0);
|
||||
cassetteUpdateMenu();
|
||||
ui_sb_update_tip(SB_CASSETTE);
|
||||
config_save();
|
||||
}
|
||||
|
||||
void MediaMenu::cassetteEject() {
|
||||
pc_cas_set_fname(cassette, nullptr);
|
||||
memset(cassette_fname, 0, sizeof(cassette_fname));
|
||||
ui_sb_update_icon_state(SB_CASSETTE, 1);
|
||||
cassetteUpdateMenu();
|
||||
ui_sb_update_tip(SB_CASSETTE);
|
||||
config_save();
|
||||
}
|
||||
|
||||
void MediaMenu::cassetteUpdateMenu() {
|
||||
QString name = cassette_fname;
|
||||
QString mode = cassette_mode;
|
||||
auto childs = cassetteMenu->children();
|
||||
auto* recordMenu = dynamic_cast<QAction*>(childs[cassetteRecordPos]);
|
||||
auto* playMenu = dynamic_cast<QAction*>(childs[cassettePlayPos]);
|
||||
auto* rewindMenu = dynamic_cast<QAction*>(childs[cassetteRewindPos]);
|
||||
auto* fastFwdMenu = dynamic_cast<QAction*>(childs[cassetteFastFwdPos]);
|
||||
auto* ejectMenu = dynamic_cast<QAction*>(childs[cassetteEjectPos]);
|
||||
|
||||
recordMenu->setEnabled(!name.isEmpty());
|
||||
playMenu->setEnabled(!name.isEmpty());
|
||||
rewindMenu->setEnabled(!name.isEmpty());
|
||||
fastFwdMenu->setEnabled(!name.isEmpty());
|
||||
ejectMenu->setEnabled(!name.isEmpty());
|
||||
|
||||
bool isSaving = mode == QStringLiteral("save");
|
||||
recordMenu->setChecked(isSaving);
|
||||
playMenu->setChecked(! isSaving);
|
||||
|
||||
cassetteMenu->setTitle(QString::asprintf(tr("Cassette: %s").toUtf8().constData(), (name.isEmpty() ? tr("(empty)") : name).toUtf8().constData()));
|
||||
}
|
||||
|
||||
void MediaMenu::cartridgeSelectImage(int i) {
|
||||
auto filename = QFileDialog::getOpenFileName(
|
||||
parentWidget,
|
||||
QString(),
|
||||
QString(),
|
||||
tr("Cartridge images") %
|
||||
util::DlgFilter({ "a","b","jrc" }) %
|
||||
tr("All files") %
|
||||
util::DlgFilter({ "*" }, true));
|
||||
|
||||
if (filename.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
cart_close(i);
|
||||
QByteArray filenameBytes = filename.toUtf8();
|
||||
cart_load(i, filenameBytes.data());
|
||||
|
||||
ui_sb_update_icon_state(SB_CARTRIDGE | i, filename.isEmpty() ? 1 : 0);
|
||||
cartridgeUpdateMenu(i);
|
||||
ui_sb_update_tip(SB_CARTRIDGE | i);
|
||||
config_save();
|
||||
}
|
||||
|
||||
void MediaMenu::cartridgeEject(int i) {
|
||||
cart_close(i);
|
||||
ui_sb_update_icon_state(SB_CARTRIDGE | i, 1);
|
||||
cartridgeUpdateMenu(i);
|
||||
ui_sb_update_tip(SB_CARTRIDGE | i);
|
||||
config_save();
|
||||
}
|
||||
|
||||
void MediaMenu::cartridgeUpdateMenu(int i) {
|
||||
QString name = cart_fns[i];
|
||||
auto* menu = cartridgeMenus[i];
|
||||
auto childs = menu->children();
|
||||
auto* ejectMenu = dynamic_cast<QAction*>(childs[cartridgeEjectPos]);
|
||||
ejectMenu->setEnabled(!name.isEmpty());
|
||||
//menu->setTitle(tr("Cartridge %1: %2").arg(QString::number(i+1), name.isEmpty() ? tr("(empty)") : name));
|
||||
menu->setTitle(QString::asprintf(tr("Cartridge %i: %ls").toUtf8().constData(), i + 1, name.isEmpty() ? tr("(empty)").toStdU16String().data() : name.toStdU16String().data()));
|
||||
}
|
||||
|
||||
void MediaMenu::floppyNewImage(int i) {
|
||||
NewFloppyDialog dialog(NewFloppyDialog::MediaType::Floppy, parentWidget);
|
||||
switch (dialog.exec()) {
|
||||
case QDialog::Accepted:
|
||||
QByteArray filename = dialog.fileName().toUtf8();
|
||||
floppyMount(i, filename, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MediaMenu::floppySelectImage(int i, bool wp) {
|
||||
auto filename = QFileDialog::getOpenFileName(
|
||||
parentWidget,
|
||||
QString(),
|
||||
QString(),
|
||||
tr("All images") %
|
||||
util::DlgFilter({ "0??","1??","??0","86f","bin","cq?","d??","flp","hdm","im?","json","td0","*fd?","mfm","xdf" }) %
|
||||
tr("Advanced sector images") %
|
||||
util::DlgFilter({ "imd","json","td0" }) %
|
||||
tr("Basic sector images") %
|
||||
util::DlgFilter({ "0??","1??","??0","bin","cq?","d??","flp","hdm","im?","xdf","*fd?" }) %
|
||||
tr("Flux images") %
|
||||
util::DlgFilter({ "fdi" }) %
|
||||
tr("Surface images") %
|
||||
util::DlgFilter({ "86f","mfm" }) %
|
||||
tr("All files") %
|
||||
util::DlgFilter({ "*" }, true));
|
||||
|
||||
if (!filename.isEmpty()) floppyMount(i, filename, wp);
|
||||
}
|
||||
|
||||
void MediaMenu::floppyMount(int i, const QString &filename, bool wp) {
|
||||
fdd_close(i);
|
||||
ui_writeprot[i] = wp ? 1 : 0;
|
||||
if (! filename.isEmpty()) {
|
||||
QByteArray filenameBytes = filename.toUtf8();
|
||||
fdd_load(i, filenameBytes.data());
|
||||
}
|
||||
ui_sb_update_icon_state(SB_FLOPPY | i, filename.isEmpty() ? 1 : 0);
|
||||
floppyUpdateMenu(i);
|
||||
ui_sb_update_tip(SB_FLOPPY | i);
|
||||
config_save();
|
||||
}
|
||||
|
||||
void MediaMenu::floppyEject(int i) {
|
||||
fdd_close(i);
|
||||
ui_sb_update_icon_state(SB_FLOPPY | i, 1);
|
||||
floppyUpdateMenu(i);
|
||||
ui_sb_update_tip(SB_FLOPPY | i);
|
||||
config_save();
|
||||
}
|
||||
|
||||
void MediaMenu::floppyExportTo86f(int i) {
|
||||
auto filename = QFileDialog::getSaveFileName(parentWidget, QString(), QString(), tr("Surface images") % util::DlgFilter({ "86f" }, true));
|
||||
if (! filename.isEmpty()) {
|
||||
QByteArray filenameBytes = filename.toUtf8();
|
||||
plat_pause(1);
|
||||
if (d86f_export(i, filenameBytes.data()) == 0) {
|
||||
QMessageBox::critical(parentWidget, tr("Unable to write file"), tr("Make sure the file is being saved to a writable directory"));
|
||||
}
|
||||
plat_pause(0);
|
||||
}
|
||||
}
|
||||
|
||||
void MediaMenu::floppyUpdateMenu(int i) {
|
||||
QString name = floppyfns[i];
|
||||
|
||||
auto* menu = floppyMenus[i];
|
||||
auto childs = menu->children();
|
||||
|
||||
auto* ejectMenu = dynamic_cast<QAction*>(childs[floppyEjectPos]);
|
||||
auto* exportMenu = dynamic_cast<QAction*>(childs[floppyExportPos]);
|
||||
ejectMenu->setEnabled(!name.isEmpty());
|
||||
exportMenu->setEnabled(!name.isEmpty());
|
||||
|
||||
int type = fdd_get_type(i);
|
||||
//floppyMenus[i]->setTitle(tr("Floppy %1 (%2): %3").arg(QString::number(i+1), fdd_getname(type), name.isEmpty() ? tr("(empty)") : name));
|
||||
floppyMenus[i]->setTitle(QString::asprintf(tr("Floppy %i (%s): %ls").toUtf8().constData(), i + 1, fdd_getname(type), name.isEmpty() ? tr("(empty)").toStdU16String().data() : name.toStdU16String().data()));
|
||||
}
|
||||
|
||||
void MediaMenu::cdromMute(int i) {
|
||||
cdrom[i].sound_on ^= 1;
|
||||
config_save();
|
||||
cdromUpdateMenu(i);
|
||||
sound_cd_thread_reset();
|
||||
}
|
||||
|
||||
void MediaMenu::cdromMount(int i) {
|
||||
QString dir;
|
||||
QFileInfo fi(cdrom[i].image_path);
|
||||
|
||||
auto filename = QFileDialog::getOpenFileName(
|
||||
parentWidget,
|
||||
QString(),
|
||||
QString(),
|
||||
tr("CD-ROM images") %
|
||||
util::DlgFilter({ "iso","cue" }) %
|
||||
tr("All files") %
|
||||
util::DlgFilter({ "*" }, true));
|
||||
|
||||
if (filename.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
QByteArray fn = filename.toUtf8().data();
|
||||
|
||||
cdrom[i].prev_host_drive = cdrom[i].host_drive;
|
||||
strcpy(cdrom[i].prev_image_path, cdrom[i].image_path);
|
||||
if (cdrom[i].ops && cdrom[i].ops->exit)
|
||||
cdrom[i].ops->exit(&(cdrom[i]));
|
||||
|
||||
cdrom[i].ops = nullptr;
|
||||
memset(cdrom[i].image_path, 0, sizeof(cdrom[i].image_path));
|
||||
cdrom_image_open(&(cdrom[i]), fn.data());
|
||||
/* Signal media change to the emulated machine. */
|
||||
if (cdrom[i].insert)
|
||||
cdrom[i].insert(cdrom[i].priv);
|
||||
cdrom[i].host_drive = (strlen(cdrom[i].image_path) == 0) ? 0 : 200;
|
||||
if (cdrom[i].host_drive == 200) {
|
||||
ui_sb_update_icon_state(SB_CDROM | i, 0);
|
||||
} else {
|
||||
ui_sb_update_icon_state(SB_CDROM | i, 1);
|
||||
}
|
||||
cdromUpdateMenu(i);
|
||||
ui_sb_update_tip(SB_CDROM | i);
|
||||
config_save();
|
||||
}
|
||||
|
||||
void MediaMenu::cdromEject(int i) {
|
||||
cdrom_eject(i);
|
||||
cdromUpdateMenu(i);
|
||||
ui_sb_update_tip(SB_CDROM | i);
|
||||
}
|
||||
|
||||
void MediaMenu::cdromReload(int i) {
|
||||
cdrom_reload(i);
|
||||
cdromUpdateMenu(i);
|
||||
ui_sb_update_tip(SB_CDROM | i);
|
||||
}
|
||||
|
||||
void MediaMenu::cdromUpdateMenu(int i) {
|
||||
QString name = cdrom[i].image_path;
|
||||
auto* menu = cdromMenus[i];
|
||||
auto childs = menu->children();
|
||||
|
||||
auto* muteMenu = dynamic_cast<QAction*>(childs[cdromMutePos]);
|
||||
muteMenu->setChecked(cdrom[i].sound_on == 0);
|
||||
|
||||
auto* imageMenu = dynamic_cast<QAction*>(childs[cdromImagePos]);
|
||||
auto* emptyMenu = dynamic_cast<QAction*>(childs[cdromEmptyPos]);
|
||||
imageMenu->setChecked(cdrom[i].host_drive == 200);
|
||||
emptyMenu->setChecked(cdrom[i].host_drive != 200);
|
||||
|
||||
auto* prevMenu = dynamic_cast<QAction*>(childs[cdromReloadPos]);
|
||||
prevMenu->setEnabled(cdrom[i].prev_host_drive != 0);
|
||||
|
||||
QString busName = tr("Unknown Bus");
|
||||
switch (cdrom[i].bus_type) {
|
||||
case CDROM_BUS_ATAPI:
|
||||
busName = "ATAPI";
|
||||
break;
|
||||
case CDROM_BUS_SCSI:
|
||||
busName = "SCSI";
|
||||
break;
|
||||
}
|
||||
|
||||
//menu->setTitle(tr("CD-ROM %1 (%2): %3").arg(QString::number(i+1), busName, name.isEmpty() ? tr("(empty)") : name));
|
||||
menu->setTitle(QString::asprintf(tr("CD-ROM %i (%s): %s").toUtf8().constData(), i + 1, busName.toUtf8().data(), name.isEmpty() ? tr("(empty)").toUtf8().data() : name.toUtf8().data()));
|
||||
}
|
||||
|
||||
void MediaMenu::zipNewImage(int i) {
|
||||
NewFloppyDialog dialog(NewFloppyDialog::MediaType::Zip, parentWidget);
|
||||
switch (dialog.exec()) {
|
||||
case QDialog::Accepted:
|
||||
QByteArray filename = dialog.fileName().toUtf8();
|
||||
zipMount(i, filename, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MediaMenu::zipSelectImage(int i, bool wp) {
|
||||
auto filename = QFileDialog::getOpenFileName(
|
||||
parentWidget,
|
||||
QString(),
|
||||
QString(),
|
||||
tr("ZIP images") %
|
||||
util::DlgFilter({ "im?","zdi" }) %
|
||||
tr("All files") %
|
||||
util::DlgFilter({ "*" }, true));
|
||||
|
||||
if (!filename.isEmpty()) zipMount(i, filename, wp);
|
||||
}
|
||||
|
||||
void MediaMenu::zipMount(int i, const QString &filename, bool wp) {
|
||||
zip_t *dev = (zip_t *) zip_drives[i].priv;
|
||||
|
||||
zip_disk_close(dev);
|
||||
zip_drives[i].read_only = wp;
|
||||
if (! filename.isEmpty()) {
|
||||
QByteArray filenameBytes = filename.toUtf8();
|
||||
zip_load(dev, filenameBytes.data());
|
||||
zip_insert(dev);
|
||||
}
|
||||
|
||||
ui_sb_update_icon_state(SB_ZIP | i, filename.isEmpty() ? 1 : 0);
|
||||
zipUpdateMenu(i);
|
||||
ui_sb_update_tip(SB_ZIP | i);
|
||||
|
||||
config_save();
|
||||
}
|
||||
|
||||
void MediaMenu::zipEject(int i) {
|
||||
zip_t *dev = (zip_t *) zip_drives[i].priv;
|
||||
|
||||
zip_disk_close(dev);
|
||||
if (zip_drives[i].bus_type) {
|
||||
/* Signal disk change to the emulated machine. */
|
||||
zip_insert(dev);
|
||||
}
|
||||
|
||||
ui_sb_update_icon_state(SB_ZIP | i, 1);
|
||||
zipUpdateMenu(i);
|
||||
ui_sb_update_tip(SB_ZIP | i);
|
||||
config_save();
|
||||
}
|
||||
|
||||
void MediaMenu::zipReload(int i) {
|
||||
zip_t *dev = (zip_t *) zip_drives[i].priv;
|
||||
|
||||
zip_disk_reload(dev);
|
||||
if (strlen(zip_drives[i].image_path) == 0) {
|
||||
ui_sb_update_icon_state(SB_ZIP|i, 1);
|
||||
} else {
|
||||
ui_sb_update_icon_state(SB_ZIP|i, 0);
|
||||
}
|
||||
|
||||
zipUpdateMenu(i);
|
||||
ui_sb_update_tip(SB_ZIP|i);
|
||||
|
||||
config_save();
|
||||
}
|
||||
|
||||
void MediaMenu::zipUpdateMenu(int i) {
|
||||
QString name = zip_drives[i].image_path;
|
||||
QString prev_name = zip_drives[i].prev_image_path;
|
||||
auto* menu = zipMenus[i];
|
||||
auto childs = menu->children();
|
||||
|
||||
auto* ejectMenu = dynamic_cast<QAction*>(childs[zipEjectPos]);
|
||||
auto* reloadMenu = dynamic_cast<QAction*>(childs[zipReloadPos]);
|
||||
ejectMenu->setEnabled(!name.isEmpty());
|
||||
reloadMenu->setEnabled(!prev_name.isEmpty());
|
||||
|
||||
QString busName = tr("Unknown Bus");
|
||||
switch (zip_drives[i].bus_type) {
|
||||
case ZIP_BUS_ATAPI:
|
||||
busName = "ATAPI";
|
||||
break;
|
||||
case ZIP_BUS_SCSI:
|
||||
busName = "SCSI";
|
||||
break;
|
||||
}
|
||||
|
||||
//menu->setTitle(tr("ZIP %1 %2 (%3): %4").arg((zip_drives[i].is_250 > 0) ? "250" : "100", QString::number(i+1), busName, name.isEmpty() ? tr("(empty)") : name));
|
||||
menu->setTitle(QString::asprintf(tr("ZIP %03i %i (%s): %ls").toUtf8().constData(), (zip_drives[i].is_250 > 0) ? 250 : 100, i + 1, busName.toUtf8().data(), name.isEmpty() ? tr("(empty)").toStdU16String().data() : name.toStdU16String().data()));
|
||||
}
|
||||
|
||||
void MediaMenu::moNewImage(int i) {
|
||||
NewFloppyDialog dialog(NewFloppyDialog::MediaType::Mo, parentWidget);
|
||||
switch (dialog.exec()) {
|
||||
case QDialog::Accepted:
|
||||
QByteArray filename = dialog.fileName().toUtf8();
|
||||
moMount(i, filename, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MediaMenu::moSelectImage(int i, bool wp) {
|
||||
auto filename = QFileDialog::getOpenFileName(
|
||||
parentWidget,
|
||||
QString(),
|
||||
QString(),
|
||||
tr("MO images") %
|
||||
util::DlgFilter({ "im?", "mdi" }) %
|
||||
tr("All files") %
|
||||
util::DlgFilter({ "*", }, true));
|
||||
|
||||
if (!filename.isEmpty()) moMount(i, filename, wp);
|
||||
}
|
||||
|
||||
void MediaMenu::moMount(int i, const QString &filename, bool wp) {
|
||||
mo_t *dev = (mo_t *) mo_drives[i].priv;
|
||||
|
||||
mo_disk_close(dev);
|
||||
mo_drives[i].read_only = wp;
|
||||
if (! filename.isEmpty()) {
|
||||
QByteArray filenameBytes = filename.toUtf8();
|
||||
mo_load(dev, filenameBytes.data());
|
||||
mo_insert(dev);
|
||||
}
|
||||
|
||||
ui_sb_update_icon_state(SB_MO | i, filename.isEmpty() ? 1 : 0);
|
||||
moUpdateMenu(i);
|
||||
ui_sb_update_tip(SB_MO | i);
|
||||
|
||||
config_save();
|
||||
}
|
||||
|
||||
void MediaMenu::moEject(int i) {
|
||||
mo_t *dev = (mo_t *) mo_drives[i].priv;
|
||||
|
||||
mo_disk_close(dev);
|
||||
if (mo_drives[i].bus_type) {
|
||||
/* Signal disk change to the emulated machine. */
|
||||
mo_insert(dev);
|
||||
}
|
||||
|
||||
ui_sb_update_icon_state(SB_MO | i, 1);
|
||||
moUpdateMenu(i);
|
||||
ui_sb_update_tip(SB_MO | i);
|
||||
config_save();
|
||||
}
|
||||
|
||||
void MediaMenu::moReload(int i) {
|
||||
mo_t *dev = (mo_t *) mo_drives[i].priv;
|
||||
|
||||
mo_disk_reload(dev);
|
||||
if (strlen(mo_drives[i].image_path) == 0) {
|
||||
ui_sb_update_icon_state(SB_MO|i, 1);
|
||||
} else {
|
||||
ui_sb_update_icon_state(SB_MO|i, 0);
|
||||
}
|
||||
|
||||
moUpdateMenu(i);
|
||||
ui_sb_update_tip(SB_MO|i);
|
||||
|
||||
config_save();
|
||||
}
|
||||
|
||||
void MediaMenu::moUpdateMenu(int i) {
|
||||
QString name = mo_drives[i].image_path;
|
||||
QString prev_name = mo_drives[i].prev_image_path;
|
||||
auto* menu = moMenus[i];
|
||||
auto childs = menu->children();
|
||||
|
||||
auto* ejectMenu = dynamic_cast<QAction*>(childs[moEjectPos]);
|
||||
auto* reloadMenu = dynamic_cast<QAction*>(childs[moReloadPos]);
|
||||
ejectMenu->setEnabled(!name.isEmpty());
|
||||
reloadMenu->setEnabled(!prev_name.isEmpty());
|
||||
|
||||
QString busName = tr("Unknown Bus");
|
||||
switch (mo_drives[i].bus_type) {
|
||||
case MO_BUS_ATAPI:
|
||||
busName = "ATAPI";
|
||||
break;
|
||||
case MO_BUS_SCSI:
|
||||
busName = "SCSI";
|
||||
break;
|
||||
}
|
||||
|
||||
menu->setTitle(QString::asprintf(tr("MO %i (%ls): %ls").toUtf8().constData(), i + 1, busName.toStdU16String().data(), name.isEmpty() ? tr("(empty)").toStdU16String().data() : name.toStdU16String().data()));
|
||||
}
|
||||
|
||||
|
||||
// callbacks from 86box C code
|
||||
extern "C" {
|
||||
|
||||
void zip_eject(uint8_t id) {
|
||||
MediaMenu::ptr->zipEject(id);
|
||||
}
|
||||
|
||||
void zip_reload(uint8_t id) {
|
||||
MediaMenu::ptr->zipReload(id);
|
||||
}
|
||||
|
||||
void mo_eject(uint8_t id) {
|
||||
MediaMenu::ptr->moEject(id);
|
||||
}
|
||||
|
||||
void mo_reload(uint8_t id) {
|
||||
MediaMenu::ptr->moReload(id);
|
||||
}
|
||||
|
||||
}
|
||||
90
src/qt/qt_mediamenu.hpp
Normal file
90
src/qt/qt_mediamenu.hpp
Normal file
@@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <QObject>
|
||||
#include <QMap>
|
||||
|
||||
class QMenu;
|
||||
|
||||
class MediaMenu : QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
MediaMenu(QWidget* parent);
|
||||
|
||||
void refresh(QMenu* parentMenu);
|
||||
|
||||
// because some 86box C-only code needs to call zip and
|
||||
// mo eject directly
|
||||
static std::shared_ptr<MediaMenu> ptr;
|
||||
|
||||
void cassetteNewImage();
|
||||
void cassetteSelectImage(bool wp);
|
||||
void cassetteMount(const QString& filename, bool wp);
|
||||
void cassetteEject();
|
||||
void cassetteUpdateMenu();
|
||||
|
||||
void cartridgeSelectImage(int i);
|
||||
void cartridgeEject(int i);
|
||||
void cartridgeUpdateMenu(int i);
|
||||
|
||||
void floppyNewImage(int i);
|
||||
void floppySelectImage(int i, bool wp);
|
||||
void floppyMount(int i, const QString& filename, bool wp);
|
||||
void floppyEject(int i);
|
||||
void floppyExportTo86f(int i);
|
||||
void floppyUpdateMenu(int i);
|
||||
|
||||
void cdromMute(int i);
|
||||
void cdromMount(int i);
|
||||
void cdromEject(int i);
|
||||
void cdromReload(int i);
|
||||
void cdromUpdateMenu(int i);
|
||||
|
||||
void zipNewImage(int i);
|
||||
void zipSelectImage(int i, bool wp);
|
||||
void zipMount(int i, const QString& filename, bool wp);
|
||||
void zipEject(int i);
|
||||
void zipReload(int i);
|
||||
void zipUpdateMenu(int i);
|
||||
|
||||
void moNewImage(int i);
|
||||
void moSelectImage(int i, bool wp);
|
||||
void moMount(int i, const QString& filename, bool wp);
|
||||
void moEject(int i);
|
||||
void moReload(int i);
|
||||
void moUpdateMenu(int i);
|
||||
private:
|
||||
QWidget* parentWidget = nullptr;
|
||||
|
||||
QMenu* cassetteMenu = nullptr;
|
||||
QMap<int, QMenu*> cartridgeMenus;
|
||||
QMap<int, QMenu*> floppyMenus;
|
||||
QMap<int, QMenu*> cdromMenus;
|
||||
QMap<int, QMenu*> zipMenus;
|
||||
QMap<int, QMenu*> moMenus;
|
||||
|
||||
int cassetteRecordPos;
|
||||
int cassettePlayPos;
|
||||
int cassetteRewindPos;
|
||||
int cassetteFastFwdPos;
|
||||
int cassetteEjectPos;
|
||||
|
||||
int cartridgeEjectPos;
|
||||
|
||||
int floppyExportPos;
|
||||
int floppyEjectPos;
|
||||
|
||||
int cdromMutePos;
|
||||
int cdromEmptyPos;
|
||||
int cdromReloadPos;
|
||||
int cdromImagePos;
|
||||
|
||||
int zipEjectPos;
|
||||
int zipReloadPos;
|
||||
|
||||
int moEjectPos;
|
||||
int moReloadPos;
|
||||
|
||||
friend class MachineStatus;
|
||||
};
|
||||
44
src/qt/qt_midi.cpp
Normal file
44
src/qt/qt_midi.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#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; }
|
||||
|
||||
void plat_midi_get_dev_name(int num, char *s)
|
||||
{
|
||||
s[0] = ' ';
|
||||
s[1] = 0;
|
||||
}
|
||||
|
||||
void plat_midi_in_get_dev_name(int num, char *s)
|
||||
{
|
||||
s[0] = ' ';
|
||||
s[1] = 0;
|
||||
}
|
||||
|
||||
}
|
||||
15
src/qt/qt_models_common.cpp
Normal file
15
src/qt/qt_models_common.cpp
Normal 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;
|
||||
}
|
||||
8
src/qt/qt_models_common.hpp
Normal file
8
src/qt/qt_models_common.hpp
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
class QString;
|
||||
class QAbstractItemModel;
|
||||
namespace Models
|
||||
{
|
||||
int AddEntry(QAbstractItemModel* model, const QString& displayRole, int userRole);
|
||||
};
|
||||
664
src/qt/qt_newfloppydialog.cpp
Normal file
664
src/qt/qt_newfloppydialog.cpp
Normal file
@@ -0,0 +1,664 @@
|
||||
#include "qt_newfloppydialog.hpp"
|
||||
#include "ui_qt_newfloppydialog.h"
|
||||
|
||||
#include "qt_models_common.hpp"
|
||||
#include "qt_util.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <86box/random.h>
|
||||
#include <86box/scsi_device.h>
|
||||
#include <86box/zip.h>
|
||||
#include <86box/mo.h>
|
||||
}
|
||||
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QMessageBox>
|
||||
#include <QProgressDialog>
|
||||
#include <thread>
|
||||
#include <QStringBuilder>
|
||||
|
||||
struct disk_size_t {
|
||||
int hole;
|
||||
int sides;
|
||||
int data_rate;
|
||||
int encoding;
|
||||
int rpm;
|
||||
int tracks;
|
||||
int sectors; /* For IMG and Japanese FDI only. */
|
||||
int sector_len; /* For IMG and Japanese FDI only. */
|
||||
int media_desc;
|
||||
int spc;
|
||||
int num_fats;
|
||||
int spfat;
|
||||
int root_dir_entries;
|
||||
};
|
||||
|
||||
static const disk_size_t disk_sizes[14] = { { 0, 1, 2, 1, 0, 40, 8, 2, 0xfe, 2, 2, 1, 64 }, /* 160k */
|
||||
{ 0, 1, 2, 1, 0, 40, 9, 2, 0xfc, 2, 2, 1, 64 }, /* 180k */
|
||||
{ 0, 2, 2, 1, 0, 40, 8, 2, 0xff, 2, 2, 1, 112 }, /* 320k */
|
||||
{ 0, 2, 2, 1, 0, 40, 9, 2, 0xfd, 2, 2, 2, 112 }, /* 360k */
|
||||
{ 0, 2, 2, 1, 0, 80, 8, 2, 0xfb, 2, 2, 2, 112 }, /* 640k */
|
||||
{ 0, 2, 2, 1, 0, 80, 9, 2, 0xf9, 2, 2, 3, 112 }, /* 720k */
|
||||
{ 1, 2, 0, 1, 1, 80, 15, 2, 0xf9, 1, 2, 7, 224 }, /* 1.2M */
|
||||
{ 1, 2, 0, 1, 1, 77, 8, 3, 0xfe, 1, 2, 2, 192 }, /* 1.25M */
|
||||
{ 1, 2, 0, 1, 0, 80, 18, 2, 0xf0, 1, 2, 9, 224 }, /* 1.44M */
|
||||
{ 1, 2, 0, 1, 0, 80, 21, 2, 0xf0, 2, 2, 5, 16 }, /* DMF cluster 1024 */
|
||||
{ 1, 2, 0, 1, 0, 80, 21, 2, 0xf0, 4, 2, 3, 16 }, /* DMF cluster 2048 */
|
||||
{ 2, 2, 3, 1, 0, 80, 36, 2, 0xf0, 2, 2, 9, 240 }, /* 2.88M */
|
||||
{ 0, 64, 0, 0, 0, 96, 32, 2, 0, 0, 0, 0, 0 }, /* ZIP 100 */
|
||||
{ 0, 64, 0, 0, 0, 239, 32, 2, 0, 0, 0, 0, 0 } }; /* ZIP 250 */
|
||||
|
||||
static const QStringList rpmModes = {
|
||||
"Perfect RPM",
|
||||
"1%% below perfect RPM",
|
||||
"1.5%% below perfect RPM",
|
||||
"2%% below perfect RPM",
|
||||
};
|
||||
|
||||
static const QStringList floppyTypes = {
|
||||
"160 kB",
|
||||
"180 kB",
|
||||
"320 kB",
|
||||
"360 kB",
|
||||
"640 kB",
|
||||
"720 kB",
|
||||
"1.2 MB",
|
||||
"1.25 MB",
|
||||
"1.44 MB",
|
||||
"DMF (cluster 1024)",
|
||||
"DMF (cluster 2048)",
|
||||
"2.88 MB",
|
||||
};
|
||||
|
||||
static const QStringList zipTypes = {
|
||||
"ZIP 100",
|
||||
"ZIP 250",
|
||||
};
|
||||
|
||||
static const QStringList moTypes = {
|
||||
"3.5\" 128Mb M.O. (ISO 10090)",
|
||||
"3.5\" 230Mb M.O. (ISO 13963)",
|
||||
"3.5\" 540Mb M.O. (ISO 15498)",
|
||||
"3.5\" 640Mb M.O. (ISO 15498)",
|
||||
"3.5\" 1.3Gb M.O. (GigaMO)",
|
||||
"3.5\" 2.3Gb M.O. (GigaMO 2)",
|
||||
"5.25\" 600Mb M.O.",
|
||||
"5.25\" 650Mb M.O.",
|
||||
"5.25\" 1Gb M.O.",
|
||||
"5.25\" 1.3Gb M.O.",
|
||||
};
|
||||
|
||||
NewFloppyDialog::NewFloppyDialog(MediaType type, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::NewFloppyDialog),
|
||||
mediaType_(type)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->fileField->setCreateFile(true);
|
||||
|
||||
auto* model = ui->comboBoxSize->model();
|
||||
switch (type) {
|
||||
case MediaType::Floppy:
|
||||
for (int i = 0; i < floppyTypes.size(); ++i) {
|
||||
Models::AddEntry(model, tr(floppyTypes[i].toUtf8().data()), i);
|
||||
}
|
||||
ui->fileField->setFilter(
|
||||
tr("All images") %
|
||||
util::DlgFilter({ "86f","dsk","flp","im?","*fd?" }) %
|
||||
tr("Basic sector images") %
|
||||
util::DlgFilter({ "dsk","flp","im?","img","*fd?" }) %
|
||||
tr("Surface images") %
|
||||
util::DlgFilter({ "86f" }, true));
|
||||
|
||||
break;
|
||||
case MediaType::Zip:
|
||||
for (int i = 0; i < zipTypes.size(); ++i) {
|
||||
Models::AddEntry(model, tr(zipTypes[i].toUtf8().data()), i);
|
||||
}
|
||||
ui->fileField->setFilter(tr("ZIP images") % util::DlgFilter({ "im?","zdi" }, true));
|
||||
break;
|
||||
case MediaType::Mo:
|
||||
for (int i = 0; i < moTypes.size(); ++i) {
|
||||
Models::AddEntry(model, tr(moTypes[i].toUtf8().data()), i);
|
||||
}
|
||||
ui->fileField->setFilter(tr("MO images") % util::DlgFilter({ "im?","mdi" }) % tr("All files") % util::DlgFilter({ "*" }, true));
|
||||
break;
|
||||
}
|
||||
|
||||
model = ui->comboBoxRpm->model();
|
||||
for (int i = 0; i < rpmModes.size(); ++i) {
|
||||
Models::AddEntry(model, tr(rpmModes[i].toUtf8().data()).replace("%%", "%"), i);
|
||||
}
|
||||
|
||||
connect(ui->fileField, &FileField::fileSelected, this, [this](const QString& filename) {
|
||||
bool hide = true;
|
||||
if (mediaType_ == MediaType::Floppy) {
|
||||
if (QFileInfo(filename).suffix().toLower() == QStringLiteral("86f")) {
|
||||
hide = false;
|
||||
}
|
||||
}
|
||||
|
||||
ui->labelRpm->setHidden(hide);
|
||||
ui->comboBoxRpm->setHidden(hide);
|
||||
});
|
||||
connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &NewFloppyDialog::onCreate);
|
||||
|
||||
ui->labelRpm->setHidden(true);
|
||||
ui->comboBoxRpm->setHidden(true);
|
||||
}
|
||||
|
||||
NewFloppyDialog::~NewFloppyDialog() {
|
||||
delete ui;
|
||||
}
|
||||
|
||||
QString NewFloppyDialog::fileName() const{
|
||||
return ui->fileField->fileName();
|
||||
}
|
||||
|
||||
void NewFloppyDialog::onCreate() {
|
||||
auto filename = ui->fileField->fileName();
|
||||
QFileInfo fi(filename);
|
||||
FileType fileType;
|
||||
|
||||
QProgressDialog progress("Creating floppy image", QString(), 0, 100, this);
|
||||
connect(this, &NewFloppyDialog::fileProgress, &progress, &QProgressDialog::setValue);
|
||||
switch (mediaType_) {
|
||||
case MediaType::Floppy:
|
||||
if (fi.suffix().toLower() == QStringLiteral("86f")) {
|
||||
if (create86f(filename, disk_sizes[ui->comboBoxSize->currentIndex()], ui->comboBoxRpm->currentIndex())) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
fileType = fi.suffix().toLower() == QStringLiteral("zdi") ? FileType::Fdi : FileType::Img;
|
||||
if (createSectorImage(filename, disk_sizes[ui->comboBoxSize->currentIndex()], fileType)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MediaType::Zip:
|
||||
{
|
||||
fileType = fi.suffix().toLower() == QStringLiteral("zdi") ? FileType::Zdi: FileType::Img;
|
||||
|
||||
std::atomic_bool res;
|
||||
std::thread t([this, &res, filename, fileType, &progress] {
|
||||
res = createZipSectorImage(filename, disk_sizes[ui->comboBoxSize->currentIndex() + 12], fileType, progress);
|
||||
});
|
||||
progress.exec();
|
||||
t.join();
|
||||
|
||||
if (res) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MediaType::Mo:
|
||||
{
|
||||
fileType = fi.suffix().toLower() == QStringLiteral("mdi") ? FileType::Mdi: FileType::Img;
|
||||
|
||||
std::atomic_bool res;
|
||||
std::thread t([this, &res, filename, fileType, &progress] {
|
||||
res = createMoSectorImage(filename, ui->comboBoxSize->currentIndex(), fileType, progress);
|
||||
});
|
||||
progress.exec();
|
||||
t.join();
|
||||
|
||||
if (res) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
QMessageBox::critical(this, tr("Unable to write file"), tr("Make sure the file is being saved to a writable directory"));
|
||||
reject();
|
||||
}
|
||||
|
||||
bool NewFloppyDialog::create86f(const QString& filename, const disk_size_t& disk_size, uint8_t rpm_mode)
|
||||
{
|
||||
uint32_t magic = 0x46423638;
|
||||
uint16_t version = 0x020C;
|
||||
uint16_t dflags = 0;
|
||||
uint16_t tflags = 0;
|
||||
uint32_t index_hole_pos = 0;
|
||||
uint32_t tarray[512];
|
||||
uint32_t array_size;
|
||||
uint32_t track_base, track_size;
|
||||
int i;
|
||||
uint32_t shift = 0;
|
||||
|
||||
dflags = 0; /* Has surface data? - Assume no for now. */
|
||||
dflags |= (disk_size.hole << 1); /* Hole */
|
||||
dflags |= ((disk_size.sides - 1) << 3); /* Sides. */
|
||||
dflags |= (0 << 4); /* Write protect? - Assume no for now. */
|
||||
dflags |= (rpm_mode << 5); /* RPM mode. */
|
||||
dflags |= (0 << 7); /* Has extra bit cells? - Assume no for now. */
|
||||
|
||||
tflags = disk_size.data_rate; /* Data rate. */
|
||||
tflags |= (disk_size.encoding << 3); /* Encoding. */
|
||||
tflags |= (disk_size.rpm << 5); /* RPM. */
|
||||
|
||||
switch (disk_size.hole) {
|
||||
case 0:
|
||||
case 1:
|
||||
default:
|
||||
switch(rpm_mode) {
|
||||
case 1:
|
||||
array_size = 25250;
|
||||
break;
|
||||
case 2:
|
||||
array_size = 25374;
|
||||
break;
|
||||
case 3:
|
||||
array_size = 25750;
|
||||
break;
|
||||
default:
|
||||
array_size = 25000;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
switch(rpm_mode) {
|
||||
case 1:
|
||||
array_size = 50500;
|
||||
break;
|
||||
case 2:
|
||||
array_size = 50750;
|
||||
break;
|
||||
case 3:
|
||||
array_size = 51000;
|
||||
break;
|
||||
default:
|
||||
array_size = 50000;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
QByteArray bytes(array_size, 0);
|
||||
memset(tarray, 0, 2048);
|
||||
|
||||
QFile file(filename);
|
||||
if (! file.open(QIODevice::WriteOnly)) {
|
||||
return false;
|
||||
}
|
||||
QDataStream stream(&file);
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
|
||||
stream << magic;
|
||||
stream << version;
|
||||
stream << dflags;
|
||||
|
||||
track_size = array_size + 6;
|
||||
track_base = 8 + ((disk_size.sides == 2) ? 2048 : 1024);
|
||||
|
||||
if (disk_size.tracks <= 43)
|
||||
shift = 1;
|
||||
|
||||
for (i = 0; i < (disk_size.tracks * disk_size.sides) << shift; i++)
|
||||
tarray[i] = track_base + (i * track_size);
|
||||
|
||||
stream.writeRawData(reinterpret_cast<const char *>(tarray), (disk_size.sides == 2) ? 2048 : 1024);
|
||||
|
||||
int max = i < (disk_size.tracks * disk_size.sides) << shift;
|
||||
for (i = 0; i < max; i++) {
|
||||
stream << tflags;
|
||||
stream << index_hole_pos;
|
||||
stream.writeRawData(bytes, bytes.size());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NewFloppyDialog::createSectorImage(const QString &filename, const disk_size_t& disk_size, FileType type)
|
||||
{
|
||||
uint32_t total_size = 0;
|
||||
uint32_t total_sectors = 0;
|
||||
uint32_t sector_bytes = 0;
|
||||
uint32_t root_dir_bytes = 0;
|
||||
uint32_t fat_size = 0;
|
||||
uint32_t fat1_offs = 0;
|
||||
uint32_t fat2_offs = 0;
|
||||
uint32_t zero_bytes = 0;
|
||||
uint16_t base = 0x1000;
|
||||
|
||||
QFile file(filename);
|
||||
if (! file.open(QIODevice::WriteOnly)) {
|
||||
return false;
|
||||
}
|
||||
QDataStream stream(&file);
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
|
||||
sector_bytes = (128 << disk_size.sector_len);
|
||||
total_sectors = disk_size.sides * disk_size.tracks * disk_size.sectors;
|
||||
if (total_sectors > ZIP_SECTORS)
|
||||
total_sectors = ZIP_250_SECTORS;
|
||||
total_size = total_sectors * sector_bytes;
|
||||
root_dir_bytes = (disk_size.root_dir_entries << 5);
|
||||
fat_size = (disk_size.spfat * sector_bytes);
|
||||
fat1_offs = sector_bytes;
|
||||
fat2_offs = fat1_offs + fat_size;
|
||||
zero_bytes = fat2_offs + fat_size + root_dir_bytes;
|
||||
|
||||
if (type == FileType::Fdi) {
|
||||
QByteArray bytes(base, 0);
|
||||
auto empty = bytes.data();
|
||||
*(uint32_t *) &(empty[0x08]) = (uint32_t) base;
|
||||
*(uint32_t *) &(empty[0x0C]) = total_size;
|
||||
*(uint16_t *) &(empty[0x10]) = (uint16_t) sector_bytes;
|
||||
*(uint8_t *) &(empty[0x14]) = (uint8_t) disk_size.sectors;
|
||||
*(uint8_t *) &(empty[0x18]) = (uint8_t) disk_size.sides;
|
||||
*(uint8_t *) &(empty[0x1C]) = (uint8_t) disk_size.tracks;
|
||||
stream.writeRawData(empty, base);
|
||||
}
|
||||
|
||||
QByteArray bytes(total_size, 0);
|
||||
auto empty = bytes.data();
|
||||
|
||||
memset(empty + zero_bytes, 0xF6, total_size - zero_bytes);
|
||||
|
||||
empty[0x00] = 0xEB; /* Jump to make MS-DOS happy. */
|
||||
empty[0x01] = 0x58;
|
||||
empty[0x02] = 0x90;
|
||||
|
||||
empty[0x03] = 0x38; /* '86BOX5.0' OEM ID. */
|
||||
empty[0x04] = 0x36;
|
||||
empty[0x05] = 0x42;
|
||||
empty[0x06] = 0x4F;
|
||||
empty[0x07] = 0x58;
|
||||
empty[0x08] = 0x35;
|
||||
empty[0x09] = 0x2E;
|
||||
empty[0x0A] = 0x30;
|
||||
|
||||
*(uint16_t *) &(empty[0x0B]) = (uint16_t) sector_bytes;
|
||||
*(uint8_t *) &(empty[0x0D]) = (uint8_t) disk_size.spc;
|
||||
*(uint16_t *) &(empty[0x0E]) = (uint16_t) 1;
|
||||
*(uint8_t *) &(empty[0x10]) = (uint8_t) disk_size.num_fats;
|
||||
*(uint16_t *) &(empty[0x11]) = (uint16_t) disk_size.root_dir_entries;
|
||||
*(uint16_t *) &(empty[0x13]) = (uint16_t) total_sectors;
|
||||
*(uint8_t *) &(empty[0x15]) = (uint8_t) disk_size.media_desc;
|
||||
*(uint16_t *) &(empty[0x16]) = (uint16_t) disk_size.spfat;
|
||||
*(uint8_t *) &(empty[0x18]) = (uint8_t) disk_size.sectors;
|
||||
*(uint8_t *) &(empty[0x1A]) = (uint8_t) disk_size.sides;
|
||||
|
||||
empty[0x26] = 0x29; /* ')' followed by randomly-generated volume serial number. */
|
||||
empty[0x27] = random_generate();
|
||||
empty[0x28] = random_generate();
|
||||
empty[0x29] = random_generate();
|
||||
empty[0x2A] = random_generate();
|
||||
|
||||
memset(&(empty[0x2B]), 0x20, 11);
|
||||
|
||||
empty[0x36] = 'F';
|
||||
empty[0x37] = 'A';
|
||||
empty[0x38] = 'T';
|
||||
empty[0x39] = '1';
|
||||
empty[0x3A] = '2';
|
||||
memset(&(empty[0x3B]), 0x20, 0x0003);
|
||||
|
||||
empty[0x1FE] = 0x55;
|
||||
empty[0x1FF] = 0xAA;
|
||||
|
||||
empty[fat1_offs + 0x00] = empty[fat2_offs + 0x00] = empty[0x15];
|
||||
empty[fat1_offs + 0x01] = empty[fat2_offs + 0x01] = 0xFF;
|
||||
empty[fat1_offs + 0x02] = empty[fat2_offs + 0x02] = 0xFF;
|
||||
|
||||
stream.writeRawData(empty, total_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NewFloppyDialog::createZipSectorImage(const QString &filename, const disk_size_t& disk_size, FileType type, QProgressDialog& pbar)
|
||||
{
|
||||
uint32_t total_size = 0;
|
||||
uint32_t total_sectors = 0;
|
||||
uint32_t sector_bytes = 0;
|
||||
uint32_t root_dir_bytes = 0;
|
||||
uint32_t fat_size = 0;
|
||||
uint32_t fat1_offs = 0;
|
||||
uint32_t fat2_offs = 0;
|
||||
uint32_t zero_bytes = 0;
|
||||
uint16_t base = 0x1000;
|
||||
uint32_t pbar_max = 0;
|
||||
|
||||
QFile file(filename);
|
||||
if (! file.open(QIODevice::WriteOnly)) {
|
||||
return false;
|
||||
}
|
||||
QDataStream stream(&file);
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
|
||||
sector_bytes = (128 << disk_size.sector_len);
|
||||
total_sectors = disk_size.sides * disk_size.tracks * disk_size.sectors;
|
||||
if (total_sectors > ZIP_SECTORS)
|
||||
total_sectors = ZIP_250_SECTORS;
|
||||
total_size = total_sectors * sector_bytes;
|
||||
root_dir_bytes = (disk_size.root_dir_entries << 5);
|
||||
fat_size = (disk_size.spfat * sector_bytes);
|
||||
fat1_offs = sector_bytes;
|
||||
fat2_offs = fat1_offs + fat_size;
|
||||
zero_bytes = fat2_offs + fat_size + root_dir_bytes;
|
||||
|
||||
pbar_max = total_size;
|
||||
if (type == FileType::Zdi) {
|
||||
pbar_max += base;
|
||||
}
|
||||
pbar_max >>= 11;
|
||||
|
||||
if (type == FileType::Zdi) {
|
||||
QByteArray data(base, 0);
|
||||
auto empty = data.data();
|
||||
|
||||
*(uint32_t *) &(empty[0x08]) = (uint32_t) base;
|
||||
*(uint32_t *) &(empty[0x0C]) = total_size;
|
||||
*(uint16_t *) &(empty[0x10]) = (uint16_t) sector_bytes;
|
||||
*(uint8_t *) &(empty[0x14]) = (uint8_t) disk_size.sectors;
|
||||
*(uint8_t *) &(empty[0x18]) = (uint8_t) disk_size.sides;
|
||||
*(uint8_t *) &(empty[0x1C]) = (uint8_t) disk_size.tracks;
|
||||
|
||||
stream.writeRawData(empty, base);
|
||||
pbar_max -= 2;
|
||||
}
|
||||
|
||||
QByteArray bytes(total_size, 0);
|
||||
auto empty = bytes.data();
|
||||
|
||||
if (total_sectors == ZIP_SECTORS) {
|
||||
/* ZIP 100 */
|
||||
/* MBR */
|
||||
*(uint64_t *) &(empty[0x0000]) = 0x2054524150492EEBLL;
|
||||
*(uint64_t *) &(empty[0x0008]) = 0x3930302065646F63LL;
|
||||
*(uint64_t *) &(empty[0x0010]) = 0x67656D6F49202D20LL;
|
||||
*(uint64_t *) &(empty[0x0018]) = 0x726F70726F432061LL;
|
||||
*(uint64_t *) &(empty[0x0020]) = 0x202D206E6F697461LL;
|
||||
*(uint64_t *) &(empty[0x0028]) = 0x30392F33322F3131LL;
|
||||
|
||||
*(uint64_t *) &(empty[0x01AE]) = 0x0116010100E90644LL;
|
||||
*(uint64_t *) &(empty[0x01B6]) = 0xED08BBE5014E0135LL;
|
||||
*(uint64_t *) &(empty[0x01BE]) = 0xFFFFFE06FFFFFE80LL;
|
||||
*(uint64_t *) &(empty[0x01C6]) = 0x0002FFE000000020LL;
|
||||
|
||||
*(uint16_t *) &(empty[0x01FE]) = 0xAA55;
|
||||
|
||||
/* 31 sectors filled with 0x48 */
|
||||
memset(&(empty[0x0200]), 0x48, 0x3E00);
|
||||
|
||||
/* Boot sector */
|
||||
*(uint64_t *) &(empty[0x4000]) = 0x584F4236389058EBLL;
|
||||
*(uint64_t *) &(empty[0x4008]) = 0x0008040200302E35LL;
|
||||
*(uint64_t *) &(empty[0x4010]) = 0x00C0F80000020002LL;
|
||||
*(uint64_t *) &(empty[0x4018]) = 0x0000002000FF003FLL;
|
||||
*(uint32_t *) &(empty[0x4020]) = 0x0002FFE0;
|
||||
*(uint16_t *) &(empty[0x4024]) = 0x0080;
|
||||
|
||||
empty[0x4026] = 0x29; /* ')' followed by randomly-generated volume serial number. */
|
||||
empty[0x4027] = random_generate();
|
||||
empty[0x4028] = random_generate();
|
||||
empty[0x4029] = random_generate();
|
||||
empty[0x402A] = random_generate();
|
||||
|
||||
memset(&(empty[0x402B]), 0x00, 0x000B);
|
||||
memset(&(empty[0x4036]), 0x20, 0x0008);
|
||||
|
||||
empty[0x4036] = 'F';
|
||||
empty[0x4037] = 'A';
|
||||
empty[0x4038] = 'T';
|
||||
empty[0x4039] = '1';
|
||||
empty[0x403A] = '6';
|
||||
memset(&(empty[0x403B]), 0x20, 0x0003);
|
||||
|
||||
empty[0x41FE] = 0x55;
|
||||
empty[0x41FF] = 0xAA;
|
||||
|
||||
empty[0x5000] = empty[0x1D000] = empty[0x4015];
|
||||
empty[0x5001] = empty[0x1D001] = 0xFF;
|
||||
empty[0x5002] = empty[0x1D002] = 0xFF;
|
||||
empty[0x5003] = empty[0x1D003] = 0xFF;
|
||||
|
||||
/* Root directory = 0x35000
|
||||
Data = 0x39000 */
|
||||
} else {
|
||||
/* ZIP 250 */
|
||||
/* MBR */
|
||||
*(uint64_t *) &(empty[0x0000]) = 0x2054524150492EEBLL;
|
||||
*(uint64_t *) &(empty[0x0008]) = 0x3930302065646F63LL;
|
||||
*(uint64_t *) &(empty[0x0010]) = 0x67656D6F49202D20LL;
|
||||
*(uint64_t *) &(empty[0x0018]) = 0x726F70726F432061LL;
|
||||
*(uint64_t *) &(empty[0x0020]) = 0x202D206E6F697461LL;
|
||||
*(uint64_t *) &(empty[0x0028]) = 0x30392F33322F3131LL;
|
||||
|
||||
*(uint64_t *) &(empty[0x01AE]) = 0x0116010100E900E9LL;
|
||||
*(uint64_t *) &(empty[0x01B6]) = 0x2E32A7AC014E0135LL;
|
||||
|
||||
*(uint64_t *) &(empty[0x01EE]) = 0xEE203F0600010180LL;
|
||||
*(uint64_t *) &(empty[0x01F6]) = 0x000777E000000020LL;
|
||||
*(uint16_t *) &(empty[0x01FE]) = 0xAA55;
|
||||
|
||||
/* 31 sectors filled with 0x48 */
|
||||
memset(&(empty[0x0200]), 0x48, 0x3E00);
|
||||
|
||||
/* The second sector begins with some strange data
|
||||
in my reference image. */
|
||||
*(uint64_t *) &(empty[0x0200]) = 0x3831393230334409LL;
|
||||
*(uint64_t *) &(empty[0x0208]) = 0x6A57766964483130LL;
|
||||
*(uint64_t *) &(empty[0x0210]) = 0x3C3A34676063653FLL;
|
||||
*(uint64_t *) &(empty[0x0218]) = 0x586A56A8502C4161LL;
|
||||
*(uint64_t *) &(empty[0x0220]) = 0x6F2D702535673D6CLL;
|
||||
*(uint64_t *) &(empty[0x0228]) = 0x255421B8602D3456LL;
|
||||
*(uint64_t *) &(empty[0x0230]) = 0x577B22447B52603ELL;
|
||||
*(uint64_t *) &(empty[0x0238]) = 0x46412CC871396170LL;
|
||||
*(uint64_t *) &(empty[0x0240]) = 0x704F55237C5E2626LL;
|
||||
*(uint64_t *) &(empty[0x0248]) = 0x6C7932C87D5C3C20LL;
|
||||
*(uint64_t *) &(empty[0x0250]) = 0x2C50503E47543D6ELL;
|
||||
*(uint64_t *) &(empty[0x0258]) = 0x46394E807721536ALL;
|
||||
*(uint64_t *) &(empty[0x0260]) = 0x505823223F245325LL;
|
||||
*(uint64_t *) &(empty[0x0268]) = 0x365C79B0393B5B6ELL;
|
||||
|
||||
/* Boot sector */
|
||||
*(uint64_t *) &(empty[0x4000]) = 0x584F4236389058EBLL;
|
||||
*(uint64_t *) &(empty[0x4008]) = 0x0001080200302E35LL;
|
||||
*(uint64_t *) &(empty[0x4010]) = 0x00EFF80000020002LL;
|
||||
*(uint64_t *) &(empty[0x4018]) = 0x0000002000400020LL;
|
||||
*(uint32_t *) &(empty[0x4020]) = 0x000777E0;
|
||||
*(uint16_t *) &(empty[0x4024]) = 0x0080;
|
||||
|
||||
empty[0x4026] = 0x29; /* ')' followed by randomly-generated volume serial number. */
|
||||
empty[0x4027] = random_generate();
|
||||
empty[0x4028] = random_generate();
|
||||
empty[0x4029] = random_generate();
|
||||
empty[0x402A] = random_generate();
|
||||
|
||||
memset(&(empty[0x402B]), 0x00, 0x000B);
|
||||
memset(&(empty[0x4036]), 0x20, 0x0008);
|
||||
|
||||
empty[0x4036] = 'F';
|
||||
empty[0x4037] = 'A';
|
||||
empty[0x4038] = 'T';
|
||||
empty[0x4039] = '1';
|
||||
empty[0x403A] = '6';
|
||||
memset(&(empty[0x403B]), 0x20, 0x0003);
|
||||
|
||||
empty[0x41FE] = 0x55;
|
||||
empty[0x41FF] = 0xAA;
|
||||
|
||||
empty[0x4200] = empty[0x22000] = empty[0x4015];
|
||||
empty[0x4201] = empty[0x22001] = 0xFF;
|
||||
empty[0x4202] = empty[0x22002] = 0xFF;
|
||||
empty[0x4203] = empty[0x22003] = 0xFF;
|
||||
|
||||
/* Root directory = 0x3FE00
|
||||
Data = 0x38200 */
|
||||
}
|
||||
|
||||
pbar.setMaximum(pbar_max);
|
||||
for (uint32_t i = 0; i < pbar_max; i++) {
|
||||
stream.writeRawData(&empty[i << 11], 2048);
|
||||
fileProgress(i);
|
||||
}
|
||||
fileProgress(pbar_max);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool NewFloppyDialog::createMoSectorImage(const QString& filename, int8_t disk_size, FileType type, QProgressDialog& pbar)
|
||||
{
|
||||
const mo_type_t *dp = &mo_types[disk_size];
|
||||
uint32_t total_size = 0, total_size2;
|
||||
uint32_t total_sectors = 0;
|
||||
uint32_t sector_bytes = 0;
|
||||
uint16_t base = 0x1000;
|
||||
uint32_t pbar_max = 0, blocks_num;
|
||||
|
||||
QFile file(filename);
|
||||
if (! file.open(QIODevice::WriteOnly)) {
|
||||
return false;
|
||||
}
|
||||
QDataStream stream(&file);
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
|
||||
sector_bytes = dp->bytes_per_sector;
|
||||
total_sectors = dp->sectors;
|
||||
total_size = total_sectors * sector_bytes;
|
||||
|
||||
total_size2 = (total_size >> 20) << 20;
|
||||
total_size2 = total_size - total_size2;
|
||||
|
||||
pbar_max = total_size;
|
||||
pbar_max >>= 20;
|
||||
blocks_num = pbar_max;
|
||||
if (type == FileType::Mdi)
|
||||
pbar_max++;
|
||||
if (total_size2 == 0)
|
||||
pbar_max++;
|
||||
|
||||
if (type == FileType::Mdi) {
|
||||
QByteArray bytes(base, 0);
|
||||
auto empty = bytes.data();
|
||||
|
||||
*(uint32_t *) &(empty[0x08]) = (uint32_t) base;
|
||||
*(uint32_t *) &(empty[0x0C]) = total_size;
|
||||
*(uint16_t *) &(empty[0x10]) = (uint16_t) sector_bytes;
|
||||
*(uint8_t *) &(empty[0x14]) = (uint8_t) 25;
|
||||
*(uint8_t *) &(empty[0x18]) = (uint8_t) 64;
|
||||
*(uint8_t *) &(empty[0x1C]) = (uint8_t) (dp->sectors / 64) / 25;
|
||||
|
||||
stream.writeRawData(empty, base);
|
||||
}
|
||||
|
||||
QByteArray bytes(1048576, 0);
|
||||
auto empty = bytes.data();
|
||||
|
||||
pbar.setMaximum(blocks_num);
|
||||
for (uint32_t i = 0; i < blocks_num; i++) {
|
||||
stream.writeRawData(empty, bytes.size());
|
||||
fileProgress(i);
|
||||
}
|
||||
|
||||
if (total_size2 > 0) {
|
||||
QByteArray extra_bytes(total_size2, 0);
|
||||
stream.writeRawData(extra_bytes.data(), total_size2);
|
||||
}
|
||||
fileProgress(blocks_num);
|
||||
|
||||
return true;
|
||||
}
|
||||
50
src/qt/qt_newfloppydialog.hpp
Normal file
50
src/qt/qt_newfloppydialog.hpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#ifndef QT_NEWFLOPPYDIALOG_HPP
|
||||
#define QT_NEWFLOPPYDIALOG_HPP
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class NewFloppyDialog;
|
||||
}
|
||||
|
||||
struct disk_size_t;
|
||||
class QProgressDialog;
|
||||
|
||||
class NewFloppyDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum class MediaType {
|
||||
Floppy,
|
||||
Zip,
|
||||
Mo,
|
||||
};
|
||||
enum class FileType {
|
||||
Img,
|
||||
Fdi,
|
||||
Zdi,
|
||||
Mdi,
|
||||
};
|
||||
explicit NewFloppyDialog(MediaType type, QWidget *parent = nullptr);
|
||||
~NewFloppyDialog();
|
||||
|
||||
QString fileName() const;
|
||||
|
||||
signals:
|
||||
void fileProgress(int i);
|
||||
|
||||
private slots:
|
||||
void onCreate();
|
||||
|
||||
private:
|
||||
Ui::NewFloppyDialog *ui;
|
||||
MediaType mediaType_;
|
||||
|
||||
bool create86f(const QString& filename, const disk_size_t& disk_size, uint8_t rpm_mode);
|
||||
bool createSectorImage(const QString& filename, const disk_size_t& disk_size, FileType type);
|
||||
bool createZipSectorImage(const QString& filename, const disk_size_t& disk_size, FileType type, QProgressDialog& pbar);
|
||||
bool createMoSectorImage(const QString& filename, int8_t disk_size, FileType type, QProgressDialog& pbar);
|
||||
};
|
||||
|
||||
#endif // QT_NEWFLOPPYDIALOG_HPP
|
||||
148
src/qt/qt_newfloppydialog.ui
Normal file
148
src/qt/qt_newfloppydialog.ui
Normal file
@@ -0,0 +1,148 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>NewFloppyDialog</class>
|
||||
<widget class="QDialog" name="NewFloppyDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>327</width>
|
||||
<height>200</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>327</width>
|
||||
<height>200</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>327</width>
|
||||
<height>200</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>New Image</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>File name:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="FileField" name="fileField" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Disk size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="comboBoxSize">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="labelRpm">
|
||||
<property name="text">
|
||||
<string>RPM mode:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="comboBoxRpm">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</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="4" column="0" colspan="2">
|
||||
<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>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>NewFloppyDialog</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>NewFloppyDialog</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>
|
||||
533
src/qt/qt_platform.cpp
Normal file
533
src/qt/qt_platform.cpp
Normal file
@@ -0,0 +1,533 @@
|
||||
#include <cstdio>
|
||||
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QTemporaryFile>
|
||||
#include <QCoreApplication>
|
||||
#include <QDateTime>
|
||||
|
||||
#include <QLibrary>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
#include "qt_mainwindow.hpp"
|
||||
#include "qt_progsettings.hpp"
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
// static QByteArray buf;
|
||||
extern QElapsedTimer elapsed_timer;
|
||||
extern MainWindow* main_window;
|
||||
QElapsedTimer elapsed_timer;
|
||||
|
||||
static std::atomic_int blitmx_contention = 0;
|
||||
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" {
|
||||
#ifdef Q_OS_WINDOWS
|
||||
#define NOMINMAX
|
||||
#include <windows.h>
|
||||
#include <86box/win.h>
|
||||
#else
|
||||
#include <strings.h>
|
||||
#endif
|
||||
#include <86box/86box.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/gameport.h>
|
||||
#include <86box/timer.h>
|
||||
#include <86box/nvr.h>
|
||||
#include <86box/plat_dynld.h>
|
||||
#include <86box/config.h>
|
||||
#include <86box/ui.h>
|
||||
#include <86box/discord.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;
|
||||
int hide_tool_bar = 0;
|
||||
uint32_t lang_id = 0x0409, lang_sys = 0x0409; // Multilangual UI variables, for now all set to LCID of en-US
|
||||
|
||||
int stricmp(const char* s1, const char* s2)
|
||||
{
|
||||
#ifdef Q_OS_WINDOWS
|
||||
return _stricmp(s1, s2);
|
||||
#else
|
||||
return strcasecmp(s1, s2);
|
||||
#endif
|
||||
}
|
||||
|
||||
int strnicmp(const char *s1, const char *s2, size_t n)
|
||||
{
|
||||
#ifdef Q_OS_WINDOWS
|
||||
return _strnicmp(s1, s2, n);
|
||||
#else
|
||||
return strncasecmp(s1, s2, n);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
do_stop(void)
|
||||
{
|
||||
cpu_thread_run = 0;
|
||||
//main_window->close();
|
||||
}
|
||||
|
||||
void plat_get_exe_name(char *s, int size)
|
||||
{
|
||||
QByteArray exepath_temp = QCoreApplication::applicationDirPath().toLocal8Bit();
|
||||
|
||||
memcpy(s, exepath_temp.data(), std::min((qsizetype)exepath_temp.size(),(qsizetype)size));
|
||||
|
||||
plat_path_slash(s);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return fopen(QString::fromUtf8(path).toLocal8Bit(), mode);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
#ifdef Q_OS_WINDOWS
|
||||
int c = strlen(s) - 1;
|
||||
|
||||
while (c > 0) {
|
||||
if (s[c] == '/' || s[c] == '\\')
|
||||
return(&s[c+1]);
|
||||
c--;
|
||||
}
|
||||
|
||||
return(s);
|
||||
#else
|
||||
auto idx = QByteArray::fromRawData(s, strlen(s)).lastIndexOf(QDir::separator().toLatin1());
|
||||
if (idx >= 0) {
|
||||
return s+idx+1;
|
||||
}
|
||||
return s;
|
||||
#endif
|
||||
}
|
||||
|
||||
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(QDateTime::currentDateTime().toString("yyyyMMdd-hhmmss-zzzz"));
|
||||
if (suffix) name.append(suffix);
|
||||
sprintf(&bufp[strlen(bufp)], "%s", name.toUtf8().data());
|
||||
}
|
||||
|
||||
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), -1, 0);
|
||||
#else
|
||||
void *ret = mmap(0, size, PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0), MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
#endif
|
||||
return (ret == MAP_FAILED) ? nullptr : ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
plat_munmap(void *ptr, size_t size)
|
||||
{
|
||||
#if defined Q_OS_WINDOWS
|
||||
VirtualFree(ptr, 0, MEM_RELEASE);
|
||||
#else
|
||||
munmap(ptr, size);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
plat_pause(int p)
|
||||
{
|
||||
static wchar_t oldtitle[512];
|
||||
wchar_t title[512];
|
||||
|
||||
if ((p == 0) && (time_sync & TIME_SYNC_ENABLED))
|
||||
nvr_time_sync();
|
||||
|
||||
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);
|
||||
}
|
||||
discord_update_activity(dopause);
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
if (source_hwnd)
|
||||
PostMessage((HWND)(uintptr_t)source_hwnd, WM_SENDSTATUS, (WPARAM)!!p, (LPARAM)(HWND)main_window->winId());
|
||||
#endif
|
||||
}
|
||||
|
||||
// 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;
|
||||
main_window->close();
|
||||
}
|
||||
|
||||
void set_language(uint32_t id) {
|
||||
lang_id = id;
|
||||
}
|
||||
|
||||
extern "C++"
|
||||
{
|
||||
QMap<uint32_t, QPair<QString, QString>> ProgSettings::lcid_langcode =
|
||||
{
|
||||
{0x0405, {"cs-CZ", "Czech (Czech Republic)"} },
|
||||
{0x0407, {"de-DE", "German (Germany)"} },
|
||||
{0x0408, {"en-US", "English (United States)"} },
|
||||
{0x0809, {"en-GB", "English (United Kingdom)"} },
|
||||
{0x0C0A, {"es-ES", "Spanish (Spain)"} },
|
||||
{0x040B, {"fi-FI", "Finnish (Finland)"} },
|
||||
{0x040C, {"fr-FR", "French (France)"} },
|
||||
{0x041A, {"hr-HR", "Croatian (Croatia)"} },
|
||||
{0x040E, {"hu-HU", "Hungarian (Hungary)"} },
|
||||
{0x0410, {"it-IT", "Italian (Italy)"} },
|
||||
{0x0411, {"ja-JP", "Japanese (Japan)"} },
|
||||
{0x0412, {"ko-KR", "Korean (Korea)"} },
|
||||
{0x0415, {"pl-PL", "Polish (Poland)"} },
|
||||
{0x0416, {"pt-BR", "Portuguese (Brazil)"} },
|
||||
{0x0816, {"pt-PT", "Portuguese (Portugal)"} },
|
||||
{0x0419, {"ru-RU", "Russian (Russia)"} },
|
||||
{0x0424, {"sl-SI", "Slovenian (Slovenia)"} },
|
||||
{0x041F, {"tr-TR", "Turkish (Turkey)"} },
|
||||
{0x0422, {"uk-UA", "Ukrainian (Ukraine)"} },
|
||||
{0x0804, {"zh-CN", "Chinese (China)"} },
|
||||
{0xFFFF, {"system", "(System Default)"} },
|
||||
};
|
||||
}
|
||||
|
||||
/* Sets up the program language before initialization. */
|
||||
uint32_t plat_language_code(char* langcode) {
|
||||
for (auto& curKey : ProgSettings::lcid_langcode.keys())
|
||||
{
|
||||
if (ProgSettings::lcid_langcode[curKey].first == langcode)
|
||||
{
|
||||
return curKey;
|
||||
}
|
||||
}
|
||||
return 0xFFFF;
|
||||
}
|
||||
|
||||
/* Converts back the language code to LCID */
|
||||
void plat_language_code_r(uint32_t lcid, char* outbuf, int len) {
|
||||
if (!ProgSettings::lcid_langcode.contains(lcid))
|
||||
{
|
||||
qstrncpy(outbuf, "system", len);
|
||||
return;
|
||||
}
|
||||
qstrncpy(outbuf, ProgSettings::lcid_langcode[lcid].first.toUtf8().constData(), len);
|
||||
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 startblit()
|
||||
{
|
||||
blitmx_contention++;
|
||||
if (blitmx.try_lock()) {
|
||||
return;
|
||||
}
|
||||
|
||||
blitmx.lock();
|
||||
}
|
||||
|
||||
void endblit()
|
||||
{
|
||||
blitmx_contention--;
|
||||
blitmx.unlock();
|
||||
if (blitmx_contention > 0) {
|
||||
// a deadlock has been observed on linux when toggling via video_toggle_option
|
||||
// because the mutex is typically unfair on linux
|
||||
// => sleep if there's contention
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
size_t mbstoc16s(uint16_t dst[], const char src[], int len)
|
||||
{
|
||||
if (src == NULL) return 0;
|
||||
if (len < 0) return 0;
|
||||
|
||||
size_t ret = MultiByteToWideChar(CP_UTF8, 0, src, -1, reinterpret_cast<LPWSTR>(dst), dst == NULL ? 0 : len);
|
||||
|
||||
if (!ret) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t c16stombs(char dst[], const uint16_t src[], int len)
|
||||
{
|
||||
if (src == NULL) return 0;
|
||||
if (len < 0) return 0;
|
||||
|
||||
size_t ret = WideCharToMultiByte(CP_UTF8, 0, reinterpret_cast<LPCWCH>(src), -1, dst, dst == NULL ? 0 : len, NULL, NULL);
|
||||
|
||||
if (!ret) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define LIB_NAME_FLUIDSYNTH "libfluidsynth.dll"
|
||||
#define LIB_NAME_GS "gsdll32.dll"
|
||||
#define LIB_NAME_FREETYPE "freetype.dll"
|
||||
#define MOUSE_CAPTURE_KEYSEQ "F8+F12"
|
||||
#else
|
||||
#define LIB_NAME_FLUIDSYNTH "libfluidsynth"
|
||||
#define LIB_NAME_GS "libgs"
|
||||
#define LIB_NAME_FREETYPE "libfreetype"
|
||||
#define MOUSE_CAPTURE_KEYSEQ "CTRL-END"
|
||||
#endif
|
||||
#ifdef Q_OS_MACOS
|
||||
#define ROMDIR "~/Library/Application Support/net.86box.86box/roms"
|
||||
#else
|
||||
#define ROMDIR "roms"
|
||||
#endif
|
||||
|
||||
|
||||
QMap<int, std::wstring> ProgSettings::translatedstrings;
|
||||
|
||||
void ProgSettings::reloadStrings()
|
||||
{
|
||||
translatedstrings.clear();
|
||||
translatedstrings[IDS_2077] = QCoreApplication::translate("", "Click to capture mouse").toStdWString();
|
||||
translatedstrings[IDS_2078] = QCoreApplication::translate("", "Press F8+F12 to release mouse").replace("F8+F12", MOUSE_CAPTURE_KEYSEQ).replace("CTRL-END", QLocale::system().name() == "de_DE" ? "Strg+Ende" : "CTRL-END").toStdWString();
|
||||
translatedstrings[IDS_2079] = QCoreApplication::translate("", "Press F8+F12 or middle button to release mouse").replace("F8+F12", MOUSE_CAPTURE_KEYSEQ).replace("CTRL-END", QLocale::system().name() == "de_DE" ? "Strg+Ende" : "CTRL-END").toStdWString();
|
||||
translatedstrings[IDS_2080] = QCoreApplication::translate("", "Failed to initialize FluidSynth").toStdWString();
|
||||
translatedstrings[IDS_4099] = QCoreApplication::translate("", "MFM/RLL or ESDI CD-ROM drives never existed").toStdWString();
|
||||
translatedstrings[IDS_2093] = QCoreApplication::translate("", "Failed to set up PCap").toStdWString();
|
||||
translatedstrings[IDS_2094] = QCoreApplication::translate("", "No PCap devices found").toStdWString();
|
||||
translatedstrings[IDS_2110] = QCoreApplication::translate("", "Unable to initialize FreeType").toStdWString();
|
||||
translatedstrings[IDS_2111] = QCoreApplication::translate("", "Unable to initialize SDL, libsdl2 is required").toStdWString();
|
||||
translatedstrings[IDS_2129] = QCoreApplication::translate("", "Make sure libpcap is installed and that you are on a libpcap-compatible network connection.").toStdWString();
|
||||
translatedstrings[IDS_2114] = QCoreApplication::translate("", "Unable to initialize Ghostscript").toStdWString();
|
||||
translatedstrings[IDS_2063] = QCoreApplication::translate("", "Machine \"%hs\" is not available due to missing ROMs in the roms/machines directory. Switching to an available machine.").toStdWString();
|
||||
translatedstrings[IDS_2064] = QCoreApplication::translate("", "Video card \"%hs\" is not available due to missing ROMs in the roms/video directory. Switching to an available video card.").toStdWString();
|
||||
translatedstrings[IDS_2128] = QCoreApplication::translate("", "Hardware not available").toStdWString();
|
||||
translatedstrings[IDS_2120] = QCoreApplication::translate("", "No ROMs found").toStdWString();
|
||||
translatedstrings[IDS_2056] = QCoreApplication::translate("", "86Box could not find any usable ROM images.\n\nPlease <a href=\"https://github.com/86Box/roms/releases/latest\">download</a> a ROM set and extract it into the \"roms\" directory.").replace("roms", ROMDIR).toStdWString();
|
||||
|
||||
auto flsynthstr = QCoreApplication::translate("", " is required for FluidSynth MIDI output.");
|
||||
if (flsynthstr.contains("libfluidsynth"))
|
||||
{
|
||||
flsynthstr.replace("libfluidsynth", LIB_NAME_FLUIDSYNTH);
|
||||
}
|
||||
else flsynthstr.prepend(LIB_NAME_FLUIDSYNTH);
|
||||
translatedstrings[IDS_2133] = flsynthstr.toStdWString();
|
||||
auto gssynthstr = QCoreApplication::translate("", " 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.");
|
||||
if (gssynthstr.contains("libgs"))
|
||||
{
|
||||
gssynthstr.replace("libgs", LIB_NAME_GS);
|
||||
}
|
||||
else gssynthstr.prepend(LIB_NAME_GS);
|
||||
translatedstrings[IDS_2132] = flsynthstr.toStdWString();
|
||||
auto ftsynthstr = QCoreApplication::translate("", " is required for ESC/P printer emulation.");
|
||||
if (ftsynthstr.contains("libfreetype"))
|
||||
{
|
||||
ftsynthstr.replace("libfreetype", LIB_NAME_FREETYPE);
|
||||
}
|
||||
else ftsynthstr.prepend(LIB_NAME_FREETYPE);
|
||||
translatedstrings[IDS_2131] = ftsynthstr.toStdWString();
|
||||
}
|
||||
|
||||
wchar_t* plat_get_string(int i)
|
||||
{
|
||||
if (ProgSettings::translatedstrings.empty()) ProgSettings::reloadStrings();
|
||||
return ProgSettings::translatedstrings[i].data();
|
||||
}
|
||||
|
||||
int
|
||||
plat_chdir(char *path)
|
||||
{
|
||||
return QDir::setCurrent(QString(path)) ? 0 : -1;
|
||||
}
|
||||
176
src/qt/qt_progsettings.cpp
Normal file
176
src/qt/qt_progsettings.cpp
Normal file
@@ -0,0 +1,176 @@
|
||||
#include <QDebug>
|
||||
|
||||
#include "qt_progsettings.hpp"
|
||||
#include "ui_qt_progsettings.h"
|
||||
#include "qt_mainwindow.hpp"
|
||||
#include "ui_qt_mainwindow.h"
|
||||
#include "qt_machinestatus.hpp"
|
||||
|
||||
#include <QMap>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QLibraryInfo>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <86box/86box.h>
|
||||
#include <86box/config.h>
|
||||
#include <86box/plat.h>
|
||||
}
|
||||
|
||||
|
||||
static QMap<QString, QString> iconset_to_qt;
|
||||
extern MainWindow* main_window;
|
||||
|
||||
ProgSettings::CustomTranslator* ProgSettings::translator = nullptr;
|
||||
QTranslator* ProgSettings::qtTranslator = nullptr;
|
||||
QString ProgSettings::getIconSetPath()
|
||||
{
|
||||
QString roms_root;
|
||||
if (rom_path[0])
|
||||
roms_root = rom_path;
|
||||
else {
|
||||
roms_root = QString("%1/roms").arg(exe_path);
|
||||
}
|
||||
|
||||
if (iconset_to_qt.isEmpty())
|
||||
{
|
||||
iconset_to_qt.insert("", ":/settings/win/icons");
|
||||
QDir dir(roms_root + "/icons/");
|
||||
if (dir.isReadable())
|
||||
{
|
||||
auto dirList = dir.entryList(QDir::AllDirs | QDir::Executable | QDir::Readable);
|
||||
for (auto &curIconSet : dirList)
|
||||
{
|
||||
if (curIconSet == "." || curIconSet == "..") continue;
|
||||
iconset_to_qt.insert(curIconSet, (dir.canonicalPath() + '/') + curIconSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
return iconset_to_qt[icon_set];
|
||||
}
|
||||
|
||||
QIcon ProgSettings::loadIcon(QString file)
|
||||
{
|
||||
(void)getIconSetPath();
|
||||
if (!QFile::exists(iconset_to_qt[icon_set] + file)) return QIcon(iconset_to_qt[""] + file);
|
||||
return QIcon(iconset_to_qt[icon_set] + file);
|
||||
}
|
||||
|
||||
ProgSettings::ProgSettings(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::ProgSettings)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
(void)getIconSetPath();
|
||||
ui->comboBox->setItemData(0, "");
|
||||
ui->comboBox->setCurrentIndex(0);
|
||||
for (auto i = iconset_to_qt.begin(); i != iconset_to_qt.end(); i++)
|
||||
{
|
||||
if (i.key() == "") continue;
|
||||
QFile iconfile(i.value() + "/iconinfo.txt");
|
||||
iconfile.open(QFile::ReadOnly);
|
||||
QString friendlyName;
|
||||
QString iconsetinfo(iconfile.readAll());
|
||||
iconfile.close();
|
||||
if (iconsetinfo.isEmpty()) friendlyName = i.key();
|
||||
else friendlyName = iconsetinfo.split('\n')[0];
|
||||
ui->comboBox->addItem(friendlyName, i.key());
|
||||
if (strcmp(icon_set, i.key().toUtf8().data()) == 0)
|
||||
{
|
||||
ui->comboBox->setCurrentIndex(ui->comboBox->findData(i.key()));
|
||||
}
|
||||
}
|
||||
ui->comboBox->setItemData(0, '(' + tr("Default") + ')', Qt::DisplayRole);
|
||||
|
||||
ui->comboBoxLanguage->setItemData(0, 0xFFFF);
|
||||
for (auto i = lcid_langcode.begin(); i != lcid_langcode.end(); i++)
|
||||
{
|
||||
if (i.key() == 0xFFFF) continue;
|
||||
ui->comboBoxLanguage->addItem(lcid_langcode[i.key()].second, i.key());
|
||||
if (i.key() == lang_id)
|
||||
{
|
||||
ui->comboBoxLanguage->setCurrentIndex(ui->comboBoxLanguage->findData(i.key()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProgSettings::accept()
|
||||
{
|
||||
strcpy(icon_set, ui->comboBox->currentData().toString().toUtf8().data());
|
||||
lang_id = ui->comboBoxLanguage->currentData().toUInt();
|
||||
|
||||
loadTranslators(QCoreApplication::instance());
|
||||
reloadStrings();
|
||||
update_mouse_msg();
|
||||
main_window->ui->retranslateUi(main_window);
|
||||
QString msg = main_window->status->getMessage();
|
||||
main_window->status.reset(new MachineStatus(main_window));
|
||||
main_window->refreshMediaMenu();
|
||||
main_window->status->message(msg);
|
||||
connect(main_window, &MainWindow::updateStatusBarTip, main_window->status.get(), &MachineStatus::updateTip);
|
||||
connect(main_window, &MainWindow::updateStatusBarActivity, main_window->status.get(), &MachineStatus::setActivity);
|
||||
connect(main_window, &MainWindow::updateStatusBarEmpty, main_window->status.get(), &MachineStatus::setEmpty);
|
||||
connect(main_window, &MainWindow::statusBarMessage, main_window->status.get(), &MachineStatus::message);
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
ProgSettings::~ProgSettings()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void ProgSettings::on_pushButton_released()
|
||||
{
|
||||
ui->comboBox->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
void ProgSettings::loadTranslators(QObject *parent)
|
||||
{
|
||||
if (qtTranslator)
|
||||
{
|
||||
QApplication::removeTranslator(qtTranslator);
|
||||
qtTranslator = nullptr;
|
||||
}
|
||||
if (translator)
|
||||
{
|
||||
QApplication::removeTranslator(translator);
|
||||
translator = nullptr;
|
||||
}
|
||||
qtTranslator = new QTranslator(parent);
|
||||
translator = new CustomTranslator(parent);
|
||||
QString localetofilename = "";
|
||||
if (lang_id == 0xFFFF || lcid_langcode.contains(lang_id) == false)
|
||||
{
|
||||
for (int i = 0; i < QLocale::system().uiLanguages().size(); i++)
|
||||
{
|
||||
localetofilename = QLocale::system().uiLanguages()[i];
|
||||
if (translator->load(QLatin1String("86box_") + localetofilename, QLatin1String(":/")))
|
||||
{
|
||||
qDebug() << "Translations loaded.\n";
|
||||
QCoreApplication::installTranslator(translator);
|
||||
if (!qtTranslator->load(QLatin1String("qtbase_") + localetofilename.replace('-', '_'), QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
|
||||
qtTranslator->load(QLatin1String("qt_") + localetofilename.replace('-', '_'), QApplication::applicationDirPath() + "/./translations/");
|
||||
if (QApplication::installTranslator(qtTranslator))
|
||||
{
|
||||
qDebug() << "Qt translations loaded." << "\n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
translator->load(QLatin1String("86box_") + lcid_langcode[lang_id].first, QLatin1String(":/"));
|
||||
QCoreApplication::installTranslator(translator);
|
||||
if (!qtTranslator->load(QLatin1String("qtbase_") + QString(lcid_langcode[lang_id].first).replace('-', '_'), QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
|
||||
qtTranslator->load(QLatin1String("qt_") + QString(lcid_langcode[lang_id].first).replace('-', '_'), QApplication::applicationDirPath() + "/./translations/");
|
||||
QCoreApplication::installTranslator(qtTranslator);
|
||||
}
|
||||
}
|
||||
|
||||
void ProgSettings::on_pushButtonLanguage_released()
|
||||
{
|
||||
ui->comboBoxLanguage->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
67
src/qt/qt_progsettings.hpp
Normal file
67
src/qt/qt_progsettings.hpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#ifndef QT_PROGSETTINGS_HPP
|
||||
#define QT_PROGSETTINGS_HPP
|
||||
|
||||
#include <QDialog>
|
||||
#include <QTranslator>
|
||||
|
||||
namespace Ui {
|
||||
class ProgSettings;
|
||||
}
|
||||
|
||||
class ProgSettings : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ProgSettings(QWidget *parent = nullptr);
|
||||
~ProgSettings();
|
||||
static QString getIconSetPath();
|
||||
static QIcon loadIcon(QString file);
|
||||
static void loadTranslators(QObject* parent = nullptr);
|
||||
static void reloadStrings();
|
||||
class CustomTranslator : public QTranslator
|
||||
{
|
||||
public:
|
||||
CustomTranslator(QObject* parent = nullptr) : QTranslator(parent) {};
|
||||
protected:
|
||||
QString translate(const char *context, const char *sourceText,
|
||||
const char *disambiguation = nullptr, int n = -1) const override
|
||||
{
|
||||
if (strcmp(sourceText, "&Fullscreen") == 0) sourceText = "&Fullscreen\tCtrl+Alt+PageUP";
|
||||
if (strcmp(sourceText, "&Ctrl+Alt+Del") == 0) sourceText = "&Ctrl+Alt+Del\tCtrl+F12";
|
||||
if (strcmp(sourceText, "Take s&creenshot") == 0) sourceText = "Take s&creenshot\tCtrl+F11";
|
||||
if (strcmp(sourceText, "Begin trace") == 0) sourceText = "Begin trace\tCtrl+T";
|
||||
if (strcmp(sourceText, "End trace") == 0) sourceText = "End trace\tCtrl+T";
|
||||
if (strcmp(sourceText, "Dump &video RAM") == 0) sourceText = "Dump &video RAM\tCtrl+F1";
|
||||
if (strcmp(sourceText, "&Qt (Software)") == 0)
|
||||
{
|
||||
QString finalstr = QTranslator::translate("", "&SDL (Software)", disambiguation, n);
|
||||
finalstr.replace("SDL", "Qt");
|
||||
finalstr.replace("(&S)", "(&Q)");
|
||||
return finalstr;
|
||||
}
|
||||
QString finalstr = QTranslator::translate("", sourceText, disambiguation, n);
|
||||
#ifdef Q_OS_MACOS
|
||||
if (finalstr.contains('\t')) finalstr.truncate(finalstr.indexOf('\t'));
|
||||
#endif
|
||||
return finalstr;
|
||||
}
|
||||
};
|
||||
static CustomTranslator* translator;
|
||||
static QTranslator* qtTranslator;
|
||||
static QMap<uint32_t, QPair<QString, QString>> lcid_langcode;
|
||||
static QMap<int, std::wstring> translatedstrings;
|
||||
|
||||
protected slots:
|
||||
void accept() override;
|
||||
private slots:
|
||||
void on_pushButton_released();
|
||||
void on_pushButtonLanguage_released();
|
||||
|
||||
private:
|
||||
Ui::ProgSettings *ui;
|
||||
|
||||
friend class MainWindow;
|
||||
};
|
||||
|
||||
#endif // QT_PROGSETTINGS_HPP
|
||||
164
src/qt/qt_progsettings.ui
Normal file
164
src/qt/qt_progsettings.ui
Normal file
@@ -0,0 +1,164 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ProgSettings</class>
|
||||
<widget class="QDialog" name="ProgSettings">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>370</width>
|
||||
<height>228</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>370</width>
|
||||
<height>228</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>370</width>
|
||||
<height>228</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Preferences</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="6" column="1">
|
||||
<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="4" column="0" colspan="2">
|
||||
<widget class="QComboBox" name="comboBoxLanguage">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>(System Default)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QPushButton" name="pushButton">
|
||||
<property name="text">
|
||||
<string>Default</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Language:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QComboBox" name="comboBox">
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>(Default)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="2">
|
||||
<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>
|
||||
<item row="5" column="0">
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Icon set:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QPushButton" name="pushButtonLanguage">
|
||||
<property name="text">
|
||||
<string>Default</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>ProgSettings</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>ProgSettings</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>
|
||||
107
src/qt/qt_renderercommon.cpp
Normal file
107
src/qt/qt_renderercommon.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
#include "qt_renderercommon.hpp"
|
||||
#include "qt_mainwindow.hpp"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QWidget>
|
||||
#include <QEvent>
|
||||
#include <QApplication>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
extern "C" {
|
||||
#include <86box/86box.h>
|
||||
#include <86box/video.h>
|
||||
}
|
||||
|
||||
RendererCommon::RendererCommon() = default;
|
||||
|
||||
extern MainWindow* main_window;
|
||||
|
||||
static void integer_scale(double *d, double *g) {
|
||||
double ratio;
|
||||
|
||||
if (*d > *g) {
|
||||
ratio = std::floor(*d / *g);
|
||||
*d = *g * ratio;
|
||||
} else {
|
||||
ratio = std::ceil(*d / *g);
|
||||
*d = *g / ratio;
|
||||
}
|
||||
}
|
||||
|
||||
void RendererCommon::onResize(int width, int height) {
|
||||
if (video_fullscreen == 0) {
|
||||
destination.setRect(0, 0, width, height);
|
||||
return;
|
||||
}
|
||||
double dx, dy, dw, dh, gsr;
|
||||
|
||||
double hw = width;
|
||||
double hh = height;
|
||||
double gw = source.width();
|
||||
double gh = source.height();
|
||||
double hsr = hw / hh;
|
||||
|
||||
switch (video_fullscreen_scale) {
|
||||
case FULLSCR_SCALE_INT:
|
||||
gsr = gw / gh;
|
||||
if (gsr <= hsr) {
|
||||
dw = hh * gsr;
|
||||
dh = hh;
|
||||
} else {
|
||||
dw = hw;
|
||||
dh = hw / gsr;
|
||||
}
|
||||
integer_scale(&dw, &gw);
|
||||
integer_scale(&dh, &gh);
|
||||
dx = (hw - dw) / 2.0;
|
||||
dy = (hh - dh) / 2.0;
|
||||
destination.setRect(dx, dy, dw, dh);
|
||||
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;
|
||||
destination.setRect(dx, dy, dw, dh);
|
||||
break;
|
||||
case FULLSCR_SCALE_FULL:
|
||||
default:
|
||||
destination.setRect(0, 0, hw, hh);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool RendererCommon::eventDelegate(QEvent *event, bool& result)
|
||||
{
|
||||
switch (event->type())
|
||||
{
|
||||
default:
|
||||
return false;
|
||||
case QEvent::KeyPress:
|
||||
case QEvent::KeyRelease:
|
||||
result = QApplication::sendEvent(main_window, event);
|
||||
return true;
|
||||
case QEvent::MouseButtonPress:
|
||||
case QEvent::MouseMove:
|
||||
case QEvent::MouseButtonRelease:
|
||||
case QEvent::Wheel:
|
||||
case QEvent::Enter:
|
||||
case QEvent::Leave:
|
||||
result = QApplication::sendEvent(parentWidget, event);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
27
src/qt/qt_renderercommon.hpp
Normal file
27
src/qt/qt_renderercommon.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <QRect>
|
||||
#include <QImage>
|
||||
#include <QEvent>
|
||||
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
#include <atomic>
|
||||
|
||||
class QWidget;
|
||||
|
||||
class RendererCommon
|
||||
{
|
||||
public:
|
||||
RendererCommon();
|
||||
|
||||
void onResize(int width, int height);
|
||||
virtual std::vector<std::tuple<uint8_t*, std::atomic_flag*>> getBuffers() = 0;
|
||||
protected:
|
||||
bool eventDelegate(QEvent* event, bool& result);
|
||||
|
||||
QRect source, destination;
|
||||
QWidget* parentWidget{nullptr};
|
||||
|
||||
std::vector<std::atomic_flag> buf_usage;
|
||||
};
|
||||
255
src/qt/qt_rendererstack.cpp
Normal file
255
src/qt/qt_rendererstack.cpp
Normal file
@@ -0,0 +1,255 @@
|
||||
#include "qt_rendererstack.hpp"
|
||||
#include "ui_qt_rendererstack.h"
|
||||
|
||||
#include "qt_softwarerenderer.hpp"
|
||||
#include "qt_hardwarerenderer.hpp"
|
||||
|
||||
#include "qt_mainwindow.hpp"
|
||||
|
||||
#include "evdev_mouse.hpp"
|
||||
|
||||
#include <QScreen>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <CoreGraphics/CoreGraphics.h>
|
||||
#endif
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <86box/mouse.h>
|
||||
#include <86box/plat.h>
|
||||
#include <86box/video.h>
|
||||
}
|
||||
|
||||
extern MainWindow* main_window;
|
||||
RendererStack::RendererStack(QWidget *parent) :
|
||||
QStackedWidget(parent),
|
||||
ui(new Ui::RendererStack)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
#ifdef WAYLAND
|
||||
if (QApplication::platformName().contains("wayland")) {
|
||||
wl_init();
|
||||
}
|
||||
#endif
|
||||
#ifdef EVDEV_INPUT
|
||||
if (QApplication::platformName() == "xcb" || QApplication::platformName() == "eglfs") {
|
||||
evdev_init();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
RendererStack::~RendererStack()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
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 RendererStack::mousePoll()
|
||||
{
|
||||
#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 (QApplication::platformName().contains("wayland"))
|
||||
wl_mouse_poll();
|
||||
#endif
|
||||
#ifdef EVDEV_INPUT
|
||||
evdev_mouse_poll();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
int ignoreNextMouseEvent = 1;
|
||||
void RendererStack::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (this->geometry().contains(event->pos()) && event->button() == Qt::LeftButton && !mouse_capture && (isMouseDown & 1))
|
||||
{
|
||||
plat_mouse_capture(1);
|
||||
this->setCursor(Qt::BlankCursor);
|
||||
if (!ignoreNextMouseEvent) ignoreNextMouseEvent++; // Avoid jumping cursor when moved.
|
||||
isMouseDown &= ~1;
|
||||
return;
|
||||
}
|
||||
if (mouse_capture && event->button() == Qt::MiddleButton && mouse_get_buttons() < 3)
|
||||
{
|
||||
plat_mouse_capture(0);
|
||||
this->setCursor(Qt::ArrowCursor);
|
||||
isMouseDown &= ~1;
|
||||
return;
|
||||
}
|
||||
if (mouse_capture)
|
||||
{
|
||||
mousedata.mousebuttons &= ~event->button();
|
||||
}
|
||||
isMouseDown &= ~1;
|
||||
}
|
||||
void RendererStack::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
isMouseDown |= 1;
|
||||
if (mouse_capture)
|
||||
{
|
||||
mousedata.mousebuttons |= event->button();
|
||||
}
|
||||
if (main_window->frameGeometry().contains(event->pos()) && !geometry().contains(event->pos()))
|
||||
{
|
||||
main_window->windowHandle()->startSystemMove();
|
||||
}
|
||||
event->accept();
|
||||
}
|
||||
void RendererStack::wheelEvent(QWheelEvent *event)
|
||||
{
|
||||
if (mouse_capture)
|
||||
{
|
||||
mousedata.deltaz += event->pixelDelta().y();
|
||||
}
|
||||
}
|
||||
|
||||
void RendererStack::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();
|
||||
if (QApplication::platformName() == "eglfs")
|
||||
{
|
||||
leaveEvent((QEvent*)event);
|
||||
ignoreNextMouseEvent--;
|
||||
}
|
||||
else if (event->globalPos().x() == 0 || event->globalPos().y() == 0) leaveEvent((QEvent*)event);
|
||||
else if (event->globalPos().x() == (screen()->geometry().width() - 1) || event->globalPos().y() == (screen()->geometry().height() - 1)) leaveEvent((QEvent*)event);
|
||||
oldPos = event->pos();
|
||||
#endif
|
||||
}
|
||||
|
||||
void RendererStack::leaveEvent(QEvent* event)
|
||||
{
|
||||
if (QApplication::platformName().contains("wayland"))
|
||||
{
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
if (!mouse_capture) return;
|
||||
QCursor::setPos(mapToGlobal(QPoint(width() / 2, height() / 2)));
|
||||
ignoreNextMouseEvent = 2;
|
||||
event->accept();
|
||||
}
|
||||
|
||||
void RendererStack::switchRenderer(Renderer renderer) {
|
||||
startblit();
|
||||
if (current) {
|
||||
removeWidget(current.get());
|
||||
}
|
||||
|
||||
switch (renderer) {
|
||||
case Renderer::Software:
|
||||
{
|
||||
auto sw = new SoftwareRenderer(this);
|
||||
rendererWindow = sw;
|
||||
connect(this, &RendererStack::blitToRenderer, sw, &SoftwareRenderer::onBlit, Qt::QueuedConnection);
|
||||
current.reset(this->createWindowContainer(sw, this));
|
||||
}
|
||||
break;
|
||||
case Renderer::OpenGL:
|
||||
{
|
||||
this->createWinId();
|
||||
auto hw = new HardwareRenderer(this);
|
||||
rendererWindow = hw;
|
||||
connect(this, &RendererStack::blitToRenderer, hw, &HardwareRenderer::onBlit, Qt::QueuedConnection);
|
||||
current.reset(this->createWindowContainer(hw, this));
|
||||
break;
|
||||
}
|
||||
case Renderer::OpenGLES:
|
||||
{
|
||||
this->createWinId();
|
||||
auto hw = new HardwareRenderer(this, HardwareRenderer::RenderType::OpenGLES);
|
||||
rendererWindow = hw;
|
||||
connect(this, &RendererStack::blitToRenderer, hw, &HardwareRenderer::onBlit, Qt::QueuedConnection);
|
||||
current.reset(this->createWindowContainer(hw, this));
|
||||
break;
|
||||
}
|
||||
case Renderer::OpenGL3:
|
||||
{
|
||||
this->createWinId();
|
||||
auto hw = new HardwareRenderer(this, HardwareRenderer::RenderType::OpenGL3);
|
||||
rendererWindow = hw;
|
||||
connect(this, &RendererStack::blitToRenderer, hw, &HardwareRenderer::onBlit, Qt::QueuedConnection);
|
||||
current.reset(this->createWindowContainer(hw, this));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
imagebufs = std::move(rendererWindow->getBuffers());
|
||||
|
||||
current->setFocusPolicy(Qt::NoFocus);
|
||||
current->setFocusProxy(this);
|
||||
addWidget(current.get());
|
||||
|
||||
this->setStyleSheet("background-color: black");
|
||||
|
||||
endblit();
|
||||
}
|
||||
|
||||
// called from blitter thread
|
||||
void RendererStack::blit(int x, int y, int w, int h)
|
||||
{
|
||||
if ((w <= 0) || (h <= 0) || (w > 2048) || (h > 2048) || (buffer32 == NULL) || std::get<std::atomic_flag*>(imagebufs[currentBuf])->test_and_set())
|
||||
{
|
||||
video_blit_complete();
|
||||
return;
|
||||
}
|
||||
sx = x;
|
||||
sy = y;
|
||||
sw = this->w = w;
|
||||
sh = this->h = h;
|
||||
uint8_t* imagebits = std::get<uint8_t*>(imagebufs[currentBuf]);
|
||||
for (int y1 = y; y1 < (y + h); y1++)
|
||||
{
|
||||
auto scanline = imagebits + (y1 * (2048) * 4) + (x * 4);
|
||||
video_copy(scanline, &(buffer32->line[y1][x]), w * 4);
|
||||
}
|
||||
|
||||
if (screenshots)
|
||||
{
|
||||
video_screenshot((uint32_t *)imagebits, x, y, 2048);
|
||||
}
|
||||
video_blit_complete();
|
||||
emit blitToRenderer(currentBuf, sx, sy, sw, sh);
|
||||
currentBuf = (currentBuf + 1) % imagebufs.size();
|
||||
}
|
||||
75
src/qt/qt_rendererstack.hpp
Normal file
75
src/qt/qt_rendererstack.hpp
Normal file
@@ -0,0 +1,75 @@
|
||||
#ifndef QT_RENDERERCONTAINER_HPP
|
||||
#define QT_RENDERERCONTAINER_HPP
|
||||
|
||||
#include <QStackedWidget>
|
||||
#include <QKeyEvent>
|
||||
#include <QEvent>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
#include <tuple>
|
||||
|
||||
namespace Ui {
|
||||
class RendererStack;
|
||||
}
|
||||
|
||||
class RendererCommon;
|
||||
class RendererStack : public QStackedWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RendererStack(QWidget *parent = nullptr);
|
||||
~RendererStack();
|
||||
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||
void mouseMoveEvent(QMouseEvent* event) override;
|
||||
void wheelEvent(QWheelEvent *event) override;
|
||||
void leaveEvent(QEvent *event) override;
|
||||
void keyPressEvent(QKeyEvent* event) override
|
||||
{
|
||||
event->ignore();
|
||||
}
|
||||
void keyReleaseEvent(QKeyEvent* event) override
|
||||
{
|
||||
event->ignore();
|
||||
}
|
||||
|
||||
enum class Renderer {
|
||||
Software,
|
||||
OpenGL,
|
||||
OpenGLES,
|
||||
OpenGL3
|
||||
};
|
||||
void switchRenderer(Renderer renderer);
|
||||
|
||||
RendererCommon* rendererWindow{nullptr};
|
||||
signals:
|
||||
void blitToRenderer(int buf_idx, int x, int y, int w, int h);
|
||||
|
||||
public slots:
|
||||
void blit(int x, int y, int w, int h);
|
||||
void mousePoll();
|
||||
|
||||
private:
|
||||
Ui::RendererStack *ui;
|
||||
|
||||
struct mouseinputdata {
|
||||
int deltax, deltay, deltaz;
|
||||
int mousebuttons;
|
||||
};
|
||||
mouseinputdata mousedata;
|
||||
|
||||
int x, y, w, h, sx, sy, sw, sh;
|
||||
|
||||
int currentBuf = 0;
|
||||
int isMouseDown = 0;
|
||||
std::vector<std::tuple<uint8_t*, std::atomic_flag*>> imagebufs;
|
||||
|
||||
std::unique_ptr<QWidget> current;
|
||||
|
||||
friend class MainWindow;
|
||||
};
|
||||
|
||||
#endif // QT_RENDERERCONTAINER_HPP
|
||||
19
src/qt/qt_rendererstack.ui
Normal file
19
src/qt/qt_rendererstack.ui
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>RendererStack</class>
|
||||
<widget class="QStackedWidget" name="RendererStack">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>StackedWidget</string>
|
||||
</property>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
732
src/qt/qt_sdl.c
Normal file
732
src/qt/qt_sdl.c
Normal 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) * sizeof(uint32_t));
|
||||
|
||||
if (screenshots)
|
||||
video_screenshot((uint32_t *) pixeldata, 0, 0, (2048));
|
||||
|
||||
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), (2048));
|
||||
|
||||
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
73
src/qt/qt_sdl.h
Normal 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*/
|
||||
168
src/qt/qt_settings.cpp
Normal file
168
src/qt/qt_settings.cpp
Normal file
@@ -0,0 +1,168 @@
|
||||
#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 "qt_progsettings.hpp"
|
||||
#include "qt_harddrive_common.hpp"
|
||||
#include "qt_settings_bus_tracking.hpp"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <86box/86box.h>
|
||||
}
|
||||
|
||||
#include <QDebug>
|
||||
#include <QMessageBox>
|
||||
#include <QCheckBox>
|
||||
|
||||
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 tr(pages.at(index.row()).toUtf8().data());
|
||||
case Qt::DecorationRole:
|
||||
return QIcon(QString("%1/%2.ico").arg(ProgSettings::getIconSetPath(), 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));
|
||||
|
||||
Harddrives::busTrackClass = new SettingsBusTracking;
|
||||
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);
|
||||
|
||||
ui->listView->setFixedWidth(ui->listView->sizeHintForColumn(0) + 5);
|
||||
|
||||
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(machine, &SettingsMachine::currentMachineChanged, otherPeripherals, &SettingsOtherPeripherals::onCurrentMachineChanged);
|
||||
|
||||
connect(ui->listView->selectionModel(), &QItemSelectionModel::currentChanged, this, [this](const QModelIndex ¤t, const QModelIndex &previous) {
|
||||
ui->stackedWidget->setCurrentIndex(current.row());
|
||||
});
|
||||
}
|
||||
|
||||
Settings::~Settings()
|
||||
{
|
||||
delete ui;
|
||||
delete Harddrives::busTrackClass;
|
||||
Harddrives::busTrackClass = nullptr;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
void Settings::accept()
|
||||
{
|
||||
if (confirm_save && !settings_only)
|
||||
{
|
||||
QMessageBox questionbox(QMessageBox::Icon::Question, "86Box", QStringLiteral("%1\n\n%2").arg(tr("Do you want to save the settings?"), tr("This will hard reset the emulated machine.")), QMessageBox::Save | QMessageBox::Cancel, this);
|
||||
QCheckBox *chkbox = new QCheckBox(tr("Don't show this message again"));
|
||||
questionbox.setCheckBox(chkbox);
|
||||
chkbox->setChecked(!confirm_save);
|
||||
QObject::connect(chkbox, &QCheckBox::stateChanged, [](int state) {
|
||||
confirm_save = (state == Qt::CheckState::Unchecked);
|
||||
});
|
||||
questionbox.exec();
|
||||
if (questionbox.result() == QMessageBox::Cancel) {
|
||||
confirm_save = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
QDialog::accept();
|
||||
}
|
||||
49
src/qt/qt_settings.hpp
Normal file
49
src/qt/qt_settings.hpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#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();
|
||||
|
||||
protected slots:
|
||||
void accept() override;
|
||||
|
||||
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
|
||||
99
src/qt/qt_settings.ui
Normal file
99
src/qt/qt_settings.ui
Normal file
@@ -0,0 +1,99 @@
|
||||
<?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>831</width>
|
||||
<height>595</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>831</width>
|
||||
<height>595</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>831</width>
|
||||
<height>595</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>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>
|
||||
256
src/qt/qt_settings_bus_tracking.cpp
Normal file
256
src/qt/qt_settings_bus_tracking.cpp
Normal file
@@ -0,0 +1,256 @@
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "86box/hdd.h"
|
||||
#include "qt_settings_bus_tracking.hpp"
|
||||
|
||||
|
||||
SettingsBusTracking::SettingsBusTracking()
|
||||
{
|
||||
int i;
|
||||
|
||||
mfm_tracking = 0x0000000000000000ULL;
|
||||
esdi_tracking = 0x0000000000000000ULL;
|
||||
xta_tracking = 0x0000000000000000ULL;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (i < 4)
|
||||
ide_tracking[i] = 0x0000000000000000ULL;
|
||||
|
||||
scsi_tracking[i] = 0x0000000000000000ULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
SettingsBusTracking::next_free_mfm_channel()
|
||||
{
|
||||
if ((mfm_tracking & 0xff00ULL) && !(mfm_tracking & 0x00ffULL))
|
||||
return 1;
|
||||
|
||||
if (!(mfm_tracking & 0xff00ULL) && (mfm_tracking & 0x00ffULL))
|
||||
return 0;
|
||||
|
||||
return CHANNEL_NONE;
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
SettingsBusTracking::next_free_esdi_channel()
|
||||
{
|
||||
if ((esdi_tracking & 0xff00ULL) && !(esdi_tracking & 0x00ffULL))
|
||||
return 1;
|
||||
|
||||
if (!(esdi_tracking & 0xff00ULL) && (esdi_tracking & 0x00ffULL))
|
||||
return 0;
|
||||
|
||||
return CHANNEL_NONE;
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
SettingsBusTracking::next_free_xta_channel()
|
||||
{
|
||||
if ((xta_tracking & 0xff00ULL) && !(xta_tracking & 0x00ffULL))
|
||||
return 1;
|
||||
|
||||
if (!(xta_tracking & 0xff00ULL) && (xta_tracking & 0x00ffULL))
|
||||
return 0;
|
||||
|
||||
return CHANNEL_NONE;
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
SettingsBusTracking::next_free_ide_channel()
|
||||
{
|
||||
int i, element;
|
||||
uint64_t mask;
|
||||
uint8_t ret = CHANNEL_NONE;
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
element = ((i << 3) >> 6);
|
||||
mask = 0xffULL << ((uint64_t) ((i << 3) & 0x3f));
|
||||
|
||||
if (!(ide_tracking[element] & mask)) {
|
||||
ret = (uint8_t) i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
SettingsBusTracking::next_free_scsi_id()
|
||||
{
|
||||
int i, element;
|
||||
uint64_t mask;
|
||||
uint8_t ret = CHANNEL_NONE;
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
element = ((i << 3) >> 6);
|
||||
mask = 0xffULL << ((uint64_t) ((i << 3) & 0x3f));
|
||||
|
||||
if (!(scsi_tracking[element] & mask)) {
|
||||
ret = (uint8_t) i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
SettingsBusTracking::mfm_bus_full()
|
||||
{
|
||||
int i;
|
||||
uint64_t mask;
|
||||
uint8_t count = 0;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
mask = 0xffULL << ((uint64_t) ((i << 3) & 0x3f));
|
||||
|
||||
if (mfm_tracking & mask)
|
||||
count++;
|
||||
}
|
||||
|
||||
return (count == 2);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
SettingsBusTracking::esdi_bus_full()
|
||||
{
|
||||
int i;
|
||||
uint64_t mask;
|
||||
uint8_t count = 0;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
mask = 0xffULL << ((uint64_t) ((i << 3) & 0x3f));
|
||||
|
||||
if (esdi_tracking & mask)
|
||||
count++;
|
||||
}
|
||||
|
||||
return (count == 2);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
SettingsBusTracking::xta_bus_full()
|
||||
{
|
||||
int i;
|
||||
uint64_t mask;
|
||||
uint8_t count = 0;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
mask = 0xffULL << ((uint64_t) ((i << 3) & 0x3f));
|
||||
|
||||
if (xta_tracking & mask)
|
||||
count++;
|
||||
}
|
||||
|
||||
return (count == 2);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
SettingsBusTracking::ide_bus_full()
|
||||
{
|
||||
int i, element;
|
||||
uint64_t mask;
|
||||
uint8_t count = 0;
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
element = ((i << 3) >> 6);
|
||||
mask = 0xffULL << ((uint64_t) ((i << 3) & 0x3f));
|
||||
|
||||
if (ide_tracking[element] & mask)
|
||||
count++;
|
||||
}
|
||||
|
||||
return (count == 32);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
SettingsBusTracking::scsi_bus_full()
|
||||
{
|
||||
int i, element;
|
||||
uint64_t mask;
|
||||
uint8_t count = 0;
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
element = ((i << 3) >> 6);
|
||||
mask = 0xffULL << ((uint64_t) ((i << 3) & 0x3f));
|
||||
|
||||
if (scsi_tracking[element] & mask)
|
||||
count++;
|
||||
}
|
||||
|
||||
return (count == 64);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SettingsBusTracking::device_track(int set, uint8_t dev_type, int bus, int channel)
|
||||
{
|
||||
int i, element;
|
||||
uint64_t mask;
|
||||
uint8_t count = 0;
|
||||
|
||||
switch (bus) {
|
||||
case HDD_BUS_MFM:
|
||||
mask = ((uint64_t) dev_type) << ((uint64_t) ((channel << 3) & 0x3f));
|
||||
|
||||
if (set)
|
||||
mfm_tracking |= mask;
|
||||
else
|
||||
mfm_tracking &= ~mask;
|
||||
break;
|
||||
|
||||
case HDD_BUS_ESDI:
|
||||
mask = ((uint64_t) dev_type) << ((uint64_t) ((channel << 3) & 0x3f));
|
||||
|
||||
if (set)
|
||||
esdi_tracking |= mask;
|
||||
else
|
||||
esdi_tracking &= ~mask;
|
||||
break;
|
||||
|
||||
case HDD_BUS_XTA:
|
||||
mask = ((uint64_t) dev_type) << ((uint64_t) ((channel << 3) & 0x3f));
|
||||
|
||||
if (set)
|
||||
xta_tracking |= mask;
|
||||
else
|
||||
xta_tracking &= ~mask;
|
||||
break;
|
||||
|
||||
case HDD_BUS_IDE:
|
||||
case HDD_BUS_ATAPI:
|
||||
element = ((channel << 3) >> 6);
|
||||
mask = ((uint64_t) dev_type) << ((uint64_t) ((channel << 3) & 0x3f));
|
||||
|
||||
if (set)
|
||||
ide_tracking[element] |= mask;
|
||||
else
|
||||
ide_tracking[element] &= ~mask;
|
||||
break;
|
||||
|
||||
case HDD_BUS_SCSI:
|
||||
element = ((channel << 3) >> 6);
|
||||
mask = ((uint64_t) dev_type) << ((uint64_t) ((channel << 3) & 0x3f));
|
||||
|
||||
if (set)
|
||||
scsi_tracking[element] |= mask;
|
||||
else
|
||||
scsi_tracking[element] &= ~mask;
|
||||
break;
|
||||
}
|
||||
}
|
||||
63
src/qt/qt_settings_bus_tracking.hpp
Normal file
63
src/qt/qt_settings_bus_tracking.hpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#ifndef QT_SETTINGS_BUS_TRACKING_HPP
|
||||
#define QT_SETTINGS_BUS_TRACKING_HPP
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#define TRACK_CLEAR 0
|
||||
#define TRACK_SET 1
|
||||
|
||||
#define DEV_HDD 0x01
|
||||
#define DEV_CDROM 0x02
|
||||
#define DEV_ZIP 0x04
|
||||
#define DEV_MO 0x08
|
||||
|
||||
#define BUS_MFM 0
|
||||
#define BUS_ESDI 1
|
||||
#define BUS_XTA 2
|
||||
#define BUS_IDE 3
|
||||
#define BUS_SCSI 4
|
||||
|
||||
#define CHANNEL_NONE 0xff
|
||||
|
||||
namespace Ui {
|
||||
class SettingsBusTracking;
|
||||
}
|
||||
|
||||
class SettingsBusTracking
|
||||
{
|
||||
public:
|
||||
explicit SettingsBusTracking();
|
||||
~SettingsBusTracking() = default;
|
||||
|
||||
/* These return 0xff is none is free. */
|
||||
uint8_t next_free_mfm_channel();
|
||||
uint8_t next_free_esdi_channel();
|
||||
uint8_t next_free_xta_channel();
|
||||
uint8_t next_free_ide_channel();
|
||||
uint8_t next_free_scsi_id();
|
||||
|
||||
int mfm_bus_full();
|
||||
int esdi_bus_full();
|
||||
int xta_bus_full();
|
||||
int ide_bus_full();
|
||||
int scsi_bus_full();
|
||||
|
||||
/* Set: 0 = Clear the device from the tracking, 1 = Set the device on the tracking.
|
||||
Device type: 1 = Hard Disk, 2 = CD-ROM, 4 = ZIP, 8 = Magneto-Optical.
|
||||
Bus: 0 = MFM, 1 = ESDI, 2 = XTA, 3 = IDE, 4 = SCSI. */
|
||||
void device_track(int set, uint8_t dev_type, int bus, int channel);
|
||||
|
||||
private:
|
||||
/* 1 channel, 2 devices per channel, 8 bits per device = 16 bits. */
|
||||
uint64_t mfm_tracking{0};
|
||||
/* 1 channel, 2 devices per channel, 8 bits per device = 16 bits. */
|
||||
uint64_t esdi_tracking{0};
|
||||
/* 1 channel, 2 devices per channel, 8 bits per device = 16 bits. */
|
||||
uint64_t xta_tracking{0};
|
||||
/* 16 channels (prepatation for that weird IDE card), 2 devices per channel, 8 bits per device = 256 bits. */
|
||||
uint64_t ide_tracking[4]{0, 0, 0, 0};
|
||||
/* 4 buses, 16 devices per bus, 8 bits per device (future-proofing) = 512 bits. */
|
||||
uint64_t scsi_tracking[8]{0, 0, 0, 0, 0, 0, 0, 0};
|
||||
};
|
||||
|
||||
#endif // QT_SETTINGS_BUS_TRACKING_HPP
|
||||
104
src/qt/qt_settingsdisplay.cpp
Normal file
104
src/qt/qt_settingsdisplay.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
#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* 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_has_flags(machineId, MACHINE_VIDEO) == 0)) {
|
||||
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, machineId)) {
|
||||
int row = Models::AddEntry(model, name, c);
|
||||
if (c == gfxcard) {
|
||||
selectedRow = row - removeRows;
|
||||
}
|
||||
}
|
||||
|
||||
c++;
|
||||
}
|
||||
model->removeRows(0, removeRows);
|
||||
|
||||
if (machine_has_flags(machineId, MACHINE_VIDEO_ONLY) > 0) {
|
||||
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 = machine_has_bus(machineId, MACHINE_BUS_PCI) > 0;
|
||||
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);
|
||||
}
|
||||
34
src/qt/qt_settingsdisplay.hpp
Normal file
34
src/qt/qt_settingsdisplay.hpp
Normal 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
|
||||
77
src/qt/qt_settingsdisplay.ui
Normal file
77
src/qt/qt_settingsdisplay.ui
Normal 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>
|
||||
235
src/qt/qt_settingsfloppycdrom.cpp
Normal file
235
src/qt/qt_settingsfloppycdrom.cpp
Normal file
@@ -0,0 +1,235 @@
|
||||
#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"
|
||||
#include "qt_settings_bus_tracking.hpp"
|
||||
#include "qt_progsettings.hpp"
|
||||
|
||||
static void setFloppyType(QAbstractItemModel* model, const QModelIndex& idx, int type) {
|
||||
QIcon icon;
|
||||
if (type == 0) {
|
||||
icon = ProgSettings::loadIcon("/floppy_disabled.ico");
|
||||
} else if (type >= 1 && type <= 6) {
|
||||
icon = ProgSettings::loadIcon("/floppy_525.ico");
|
||||
} else {
|
||||
icon = ProgSettings::loadIcon("/floppy_35.ico");
|
||||
}
|
||||
|
||||
model->setData(idx, QObject::tr(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 = ProgSettings::loadIcon("/cdrom_disabled.ico");
|
||||
break;
|
||||
case CDROM_BUS_ATAPI:
|
||||
case CDROM_BUS_SCSI:
|
||||
icon = ProgSettings::loadIcon("/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 = tr(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, tr("Type"));
|
||||
model->setHeaderData(1, Qt::Horizontal, tr("Turbo"));
|
||||
model->setHeaderData(2, Qt::Horizontal, tr("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 ? tr("On") : tr("Off"));
|
||||
model->setData(idx.siblingAtColumn(2), fdd_get_check_bpb(i) > 0 ? tr("On") : tr("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 + 1), i + 1);
|
||||
}
|
||||
|
||||
model = new QStandardItemModel(0, 2, this);
|
||||
ui->tableViewCDROM->setModel(model);
|
||||
model->setHeaderData(0, Qt::Horizontal, tr("Bus"));
|
||||
model->setHeaderData(1, Qt::Horizontal, tr("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);
|
||||
Harddrives::busTrackClass->device_track(1, DEV_CDROM, cdrom[i].bus_type, cdrom[i].bus_type == CDROM_BUS_ATAPI ? cdrom[i].ide_channel : cdrom[i].scsi_device_id);
|
||||
}
|
||||
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() == tr("On") ? 1 : 0);
|
||||
fdd_set_check_bpb(i, model->index(i, 2).data() == tr("On") ? 1 : 0);
|
||||
}
|
||||
|
||||
/* Removable devices category */
|
||||
model = ui->tableViewCDROM->model();
|
||||
for (int i = 0; i < CDROM_NUM; i++) {
|
||||
cdrom[i].img_fp = NULL;
|
||||
cdrom[i].priv = NULL;
|
||||
cdrom[i].ops = NULL;
|
||||
cdrom[i].image = NULL;
|
||||
cdrom[i].insert = NULL;
|
||||
cdrom[i].close = NULL;
|
||||
cdrom[i].get_volume = NULL;
|
||||
cdrom[i].get_channel = NULL;
|
||||
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 ¤t) {
|
||||
int type = current.siblingAtColumn(0).data(Qt::UserRole).toInt();
|
||||
ui->comboBoxFloppyType->setCurrentIndex(type);
|
||||
ui->checkBoxTurboTimings->setChecked(current.siblingAtColumn(1).data() == tr("On"));
|
||||
ui->checkBoxCheckBPB->setChecked(current.siblingAtColumn(2).data() == tr("On"));
|
||||
}
|
||||
|
||||
void SettingsFloppyCDROM::onCDROMRowChanged(const QModelIndex ¤t) {
|
||||
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());
|
||||
}
|
||||
else ui->comboBoxChannel->setCurrentIndex(8);
|
||||
ui->comboBoxSpeed->setCurrentIndex(speed - 1);
|
||||
}
|
||||
|
||||
void SettingsFloppyCDROM::on_checkBoxTurboTimings_stateChanged(int arg1) {
|
||||
auto idx = ui->tableViewFloppy->selectionModel()->currentIndex();
|
||||
ui->tableViewFloppy->model()->setData(idx.siblingAtColumn(1), arg1 == Qt::Checked ? tr("On") : tr("Off"));
|
||||
}
|
||||
|
||||
void SettingsFloppyCDROM::on_checkBoxCheckBPB_stateChanged(int arg1) {
|
||||
auto idx = ui->tableViewFloppy->selectionModel()->currentIndex();
|
||||
ui->tableViewFloppy->model()->setData(idx.siblingAtColumn(2), arg1 == Qt::Checked ? tr("On") : tr("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 + 1);
|
||||
}
|
||||
|
||||
|
||||
void SettingsFloppyCDROM::on_comboBoxBus_activated(int) {
|
||||
auto i = ui->tableViewCDROM->selectionModel()->currentIndex().siblingAtColumn(0);
|
||||
Harddrives::busTrackClass->device_track(0, DEV_CDROM, ui->tableViewCDROM->model()->data(i, Qt::UserRole).toInt(), ui->tableViewCDROM->model()->data(i, Qt::UserRole + 1).toInt());
|
||||
ui->comboBoxChannel->setCurrentIndex(ui->comboBoxBus->currentData().toUInt() == CDROM_BUS_ATAPI ? Harddrives::busTrackClass->next_free_ide_channel() : Harddrives::busTrackClass->next_free_scsi_id());
|
||||
setCDROMBus(
|
||||
ui->tableViewCDROM->model(),
|
||||
ui->tableViewCDROM->selectionModel()->currentIndex(),
|
||||
ui->comboBoxBus->currentData().toUInt(),
|
||||
ui->comboBoxChannel->currentData().toUInt());
|
||||
Harddrives::busTrackClass->device_track(1, DEV_CDROM, ui->tableViewCDROM->model()->data(i, Qt::UserRole).toInt(), ui->tableViewCDROM->model()->data(i, Qt::UserRole + 1).toInt());
|
||||
}
|
||||
|
||||
|
||||
void SettingsFloppyCDROM::on_comboBoxChannel_activated(int) {
|
||||
auto i = ui->tableViewCDROM->selectionModel()->currentIndex().siblingAtColumn(0);
|
||||
Harddrives::busTrackClass->device_track(0, DEV_CDROM, ui->tableViewCDROM->model()->data(i, Qt::UserRole).toInt(), ui->tableViewCDROM->model()->data(i, Qt::UserRole + 1).toInt());
|
||||
setCDROMBus(
|
||||
ui->tableViewCDROM->model(),
|
||||
ui->tableViewCDROM->selectionModel()->currentIndex(),
|
||||
ui->comboBoxBus->currentData().toUInt(),
|
||||
ui->comboBoxChannel->currentData().toUInt());
|
||||
Harddrives::busTrackClass->device_track(1, DEV_CDROM, ui->tableViewCDROM->model()->data(i, Qt::UserRole).toInt(), ui->tableViewCDROM->model()->data(i, Qt::UserRole + 1).toInt());
|
||||
}
|
||||
|
||||
35
src/qt/qt_settingsfloppycdrom.hpp
Normal file
35
src/qt/qt_settingsfloppycdrom.hpp
Normal 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 ¤t);
|
||||
void onCDROMRowChanged(const QModelIndex ¤t);
|
||||
|
||||
private:
|
||||
Ui::SettingsFloppyCDROM *ui;
|
||||
};
|
||||
|
||||
#endif // QT_SETTINGSFLOPPYCDROM_HPP
|
||||
148
src/qt/qt_settingsfloppycdrom.ui
Normal file
148
src/qt/qt_settingsfloppycdrom.ui
Normal 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>
|
||||
279
src/qt/qt_settingsharddisks.cpp
Normal file
279
src/qt/qt_settingsharddisks.cpp
Normal file
@@ -0,0 +1,279 @@
|
||||
#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"
|
||||
#include "qt_settings_bus_tracking.hpp"
|
||||
#include "qt_progsettings.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;
|
||||
const int DataBusPrevious = Qt::UserRole + 2;
|
||||
const int DataBusChannelPrevious = Qt::UserRole + 3;
|
||||
|
||||
/*
|
||||
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), ProgSettings::loadIcon( "/hard_disk.ico"), Qt::DecorationRole);
|
||||
model->setData(model->index(row, ColumnBus), hd->bus, DataBus);
|
||||
model->setData(model->index(row, ColumnBus), hd->bus, DataBusPrevious);
|
||||
model->setData(model->index(row, ColumnBus), hd->channel, DataBusChannel);
|
||||
model->setData(model->index(row, ColumnBus), hd->channel, DataBusChannelPrevious);
|
||||
Harddrives::busTrackClass->device_track(1, DEV_HDD, hd->bus, hd->channel);
|
||||
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, tr("Bus"));
|
||||
model->setHeaderData(ColumnFilename, Qt::Horizontal, tr("File"));
|
||||
model->setHeaderData(ColumnCylinders, Qt::Horizontal, tr("C"));
|
||||
model->setHeaderData(ColumnHeads, Qt::Horizontal, tr("H"));
|
||||
model->setHeaderData(ColumnSectors, Qt::Horizontal, tr("S"));
|
||||
model->setHeaderData(ColumnSize, Qt::Horizontal, tr("MiB"));
|
||||
ui->tableView->setModel(model);
|
||||
|
||||
for (int i = 0; i < HDD_NUM; i++) {
|
||||
if (hdd[i].bus > 0) {
|
||||
addRow(model, &hdd[i]);
|
||||
}
|
||||
}
|
||||
if (model->rowCount() == HDD_NUM)
|
||||
{
|
||||
ui->pushButtonNew->setEnabled(false);
|
||||
ui->pushButtonExisting->setEnabled(false);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
buschangeinprogress = true;
|
||||
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::busTrackClass->device_track(0, DEV_HDD, model->data(col, DataBusPrevious).toInt(), model->data(col, DataBusChannelPrevious).toInt());
|
||||
model->setData(col, ui->comboBoxBus->currentData(Qt::UserRole), DataBusPrevious);
|
||||
}
|
||||
|
||||
Harddrives::populateBusChannels(ui->comboBoxChannel->model(), ui->comboBoxBus->currentData().toInt());
|
||||
int chanIdx = 0;
|
||||
|
||||
switch (ui->comboBoxBus->currentData().toInt())
|
||||
{
|
||||
case HDD_BUS_MFM:
|
||||
chanIdx = (Harddrives::busTrackClass->next_free_mfm_channel());
|
||||
break;
|
||||
case HDD_BUS_XTA:
|
||||
chanIdx = (Harddrives::busTrackClass->next_free_xta_channel());
|
||||
break;
|
||||
case HDD_BUS_ESDI:
|
||||
chanIdx = (Harddrives::busTrackClass->next_free_esdi_channel());
|
||||
break;
|
||||
case HDD_BUS_ATAPI:
|
||||
case HDD_BUS_IDE:
|
||||
chanIdx = (Harddrives::busTrackClass->next_free_ide_channel());
|
||||
break;
|
||||
case HDD_BUS_SCSI:
|
||||
chanIdx = (Harddrives::busTrackClass->next_free_scsi_id());
|
||||
break;
|
||||
}
|
||||
|
||||
if (idx.isValid()) {
|
||||
auto* model = ui->tableView->model();
|
||||
auto col = idx.siblingAtColumn(ColumnBus);
|
||||
model->setData(col, chanIdx, DataBusChannelPrevious);
|
||||
}
|
||||
ui->comboBoxChannel->setCurrentIndex(chanIdx);
|
||||
buschangeinprogress = false;
|
||||
}
|
||||
|
||||
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);
|
||||
if (!buschangeinprogress) Harddrives::busTrackClass->device_track(0, DEV_HDD, model->data(col, DataBus).toInt(), model->data(col, DataBusChannelPrevious).toUInt());
|
||||
Harddrives::busTrackClass->device_track(1, DEV_HDD, model->data(col, DataBus).toInt(), model->data(col, DataBusChannel).toUInt());
|
||||
model->setData(col, ui->comboBoxChannel->currentData(Qt::UserRole), DataBusChannelPrevious);
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsHarddisks::onTableRowChanged(const QModelIndex ¤t) {
|
||||
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);
|
||||
if (ui->tableView->model()->rowCount() == HDD_NUM)
|
||||
{
|
||||
ui->pushButtonNew->setEnabled(false);
|
||||
ui->pushButtonExisting->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
ui->pushButtonNew->setEnabled(true);
|
||||
ui->pushButtonExisting->setEnabled(true);
|
||||
}
|
||||
|
||||
36
src/qt/qt_settingsharddisks.hpp
Normal file
36
src/qt/qt_settingsharddisks.hpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#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;
|
||||
bool buschangeinprogress = false;
|
||||
};
|
||||
|
||||
#endif // QT_SETTINGSHARDDISKS_HPP
|
||||
95
src/qt/qt_settingsharddisks.ui
Normal file
95
src/qt/qt_settingsharddisks.ui
Normal 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>
|
||||
187
src/qt/qt_settingsinput.cpp
Normal file
187
src/qt/qt_settingsinput.cpp
Normal file
@@ -0,0 +1,187 @@
|
||||
#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_models_common.hpp"
|
||||
#include "qt_deviceconfig.hpp"
|
||||
#include "qt_joystickconfiguration.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) && (machine_has_flags(machineId, MACHINE_MOUSE) == 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (device_is_valid(dev, machineId) == 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 = Models::AddEntry(joystickModel, tr(joyName).toUtf8().data(), i);
|
||||
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));
|
||||
}
|
||||
|
||||
static int get_axis(JoystickConfiguration& jc, int axis, int joystick_nr) {
|
||||
int axis_sel = jc.selectedAxis(axis);
|
||||
int nr_axes = plat_joystick_state[joystick_state[joystick_nr].plat_joystick_nr - 1].nr_axes;
|
||||
int nr_povs = plat_joystick_state[joystick_state[joystick_nr].plat_joystick_nr - 1].nr_povs;
|
||||
|
||||
if (axis_sel < nr_axes) {
|
||||
return axis_sel;
|
||||
}
|
||||
|
||||
axis_sel -= nr_axes;
|
||||
if (axis_sel < nr_povs * 2) {
|
||||
if (axis_sel & 1)
|
||||
return POV_Y | (axis_sel >> 1);
|
||||
else
|
||||
return POV_X | (axis_sel >> 1);
|
||||
}
|
||||
axis_sel -= nr_povs;
|
||||
|
||||
return SLIDER | (axis_sel >> 1);
|
||||
}
|
||||
|
||||
static int get_pov(JoystickConfiguration& jc, int pov, int joystick_nr) {
|
||||
int pov_sel = jc.selectedPov(pov);
|
||||
int nr_povs = plat_joystick_state[joystick_state[joystick_nr].plat_joystick_nr-1].nr_povs*2;
|
||||
|
||||
if (pov_sel < nr_povs)
|
||||
{
|
||||
if (pov_sel & 1)
|
||||
return POV_Y | (pov_sel >> 1);
|
||||
else
|
||||
return POV_X | (pov_sel >> 1);
|
||||
}
|
||||
|
||||
return pov_sel - nr_povs;
|
||||
}
|
||||
|
||||
static void updateJoystickConfig(int type, int joystick_nr, QWidget* parent) {
|
||||
JoystickConfiguration jc(type, joystick_nr, parent);
|
||||
switch (jc.exec()) {
|
||||
case QDialog::Rejected:
|
||||
return;
|
||||
case QDialog::Accepted:
|
||||
break;
|
||||
}
|
||||
|
||||
joystick_state[joystick_nr].plat_joystick_nr = jc.selectedDevice();
|
||||
if (joystick_state[joystick_nr].plat_joystick_nr) {
|
||||
for (int c = 0; c < joystick_get_axis_count(type); c++) {
|
||||
joystick_state[joystick_nr].axis_mapping[c] = get_axis(jc, c, joystick_nr);
|
||||
}
|
||||
for (int c = 0; c < joystick_get_button_count(type); c++) {
|
||||
joystick_state[joystick_nr].button_mapping[c] = jc.selectedButton(c);
|
||||
}
|
||||
for (int c = 0; c < joystick_get_button_count(type); c++) {
|
||||
joystick_state[joystick_nr].pov_mapping[c][0] = get_pov(jc, c, joystick_nr);
|
||||
joystick_state[joystick_nr].pov_mapping[c][1] = get_pov(jc, c, joystick_nr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsInput::on_pushButtonJoystick1_clicked() {
|
||||
updateJoystickConfig(ui->comboBoxJoystick->currentData().toInt(), 0, this);
|
||||
}
|
||||
|
||||
void SettingsInput::on_pushButtonJoystick2_clicked() {
|
||||
updateJoystickConfig(ui->comboBoxJoystick->currentData().toInt(), 1, this);
|
||||
}
|
||||
|
||||
void SettingsInput::on_pushButtonJoystick3_clicked() {
|
||||
updateJoystickConfig(ui->comboBoxJoystick->currentData().toInt(), 2, this);
|
||||
}
|
||||
|
||||
void SettingsInput::on_pushButtonJoystick4_clicked() {
|
||||
updateJoystickConfig(ui->comboBoxJoystick->currentData().toInt(), 3, this);
|
||||
}
|
||||
|
||||
37
src/qt/qt_settingsinput.hpp
Normal file
37
src/qt/qt_settingsinput.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#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);
|
||||
void on_pushButtonJoystick1_clicked();
|
||||
void on_pushButtonJoystick2_clicked();
|
||||
void on_pushButtonJoystick3_clicked();
|
||||
void on_pushButtonJoystick4_clicked();
|
||||
|
||||
private:
|
||||
Ui::SettingsInput *ui;
|
||||
int machineId = 0;
|
||||
};
|
||||
|
||||
#endif // QT_SETTINGSINPUT_HPP
|
||||
134
src/qt/qt_settingsinput.ui
Normal file
134
src/qt/qt_settingsinput.ui
Normal 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>
|
||||
270
src/qt/qt_settingsmachine.cpp
Normal file
270
src/qt/qt_settingsmachine.cpp
Normal file
@@ -0,0 +1,270 @@
|
||||
#include "qt_settingsmachine.hpp"
|
||||
#include "ui_qt_settingsmachine.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDialog>
|
||||
#include <QFrame>
|
||||
#include <QVBoxLayout>
|
||||
#include <QDialogButtonBox>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
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"
|
||||
|
||||
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, tr("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::asprintf(tr("%i Wait state(s)").toUtf8().constData(), 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 == machine_get_type(machine)) {
|
||||
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;
|
||||
int64_t temp_mem_size;
|
||||
if (machine_get_ram_granularity(machine) < 1024) {
|
||||
temp_mem_size = ui->spinBoxRAM->value();
|
||||
} else {
|
||||
temp_mem_size = ui->spinBoxRAM->value() * 1024;
|
||||
}
|
||||
|
||||
temp_mem_size &= ~(machine_get_ram_granularity(machine) - 1);
|
||||
if (temp_mem_size < machine_get_min_ram(machine)) {
|
||||
temp_mem_size = machine_get_min_ram(machine);
|
||||
} else if (temp_mem_size > machine_get_max_ram(machine)) {
|
||||
temp_mem_size = machine_get_max_ram(machine);
|
||||
}
|
||||
mem_size = static_cast<uint32_t>(temp_mem_size);
|
||||
|
||||
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 ((machine_get_type(i) == 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);
|
||||
|
||||
int divisor;
|
||||
if ((machine_get_ram_granularity(machineId) < 1024)) {
|
||||
divisor = 1;
|
||||
ui->spinBoxRAM->setSuffix(QCoreApplication::translate("", "KB").prepend(' '));
|
||||
} else {
|
||||
divisor = 1024;
|
||||
ui->spinBoxRAM->setSuffix(QCoreApplication::translate("", "MB").prepend(' '));
|
||||
}
|
||||
ui->spinBoxRAM->setMinimum(machine_get_min_ram(machineId) / divisor);
|
||||
ui->spinBoxRAM->setMaximum(machine_get_max_ram(machineId) / divisor);
|
||||
ui->spinBoxRAM->setSingleStep(machine_get_ram_granularity(machineId) / divisor);
|
||||
ui->spinBoxRAM->setValue(mem_size / divisor);
|
||||
ui->spinBoxRAM->setEnabled(machine_get_min_ram(machineId) != machine_get_max_ram(machineId));
|
||||
|
||||
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);
|
||||
}
|
||||
41
src/qt/qt_settingsmachine.hpp
Normal file
41
src/qt/qt_settingsmachine.hpp
Normal 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
|
||||
229
src/qt/qt_settingsmachine.ui
Normal file
229
src/qt/qt_settingsmachine.ui
Normal 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 type:</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>
|
||||
113
src/qt/qt_settingsnetwork.cpp
Normal file
113
src/qt/qt_settingsnetwork.cpp
Normal 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 && network_card_has_config(ui->comboBoxAdapter->currentData().toInt()));
|
||||
}
|
||||
|
||||
SettingsNetwork::SettingsNetwork(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::SettingsNetwork)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
auto* model = ui->comboBoxNetwork->model();
|
||||
Models::AddEntry(model, tr("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, tr(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), machineId)) {
|
||||
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()));
|
||||
}
|
||||
|
||||
33
src/qt/qt_settingsnetwork.hpp
Normal file
33
src/qt/qt_settingsnetwork.hpp
Normal 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
|
||||
101
src/qt/qt_settingsnetwork.ui
Normal file
101
src/qt/qt_settingsnetwork.ui
Normal 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>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user