modernize camera_betterer

This commit is contained in:
Arisotura
2022-09-25 21:33:17 +02:00
parent 4ec22dda9e
commit e63c2985fc
86 changed files with 9697 additions and 3375 deletions

View File

@ -67,6 +67,20 @@ AudioSettingsDialog::AudioSettingsDialog(QWidget* parent) : QDialog(parent), ui(
bool iswav = (Config::MicInputType == 3);
ui->txtMicWavPath->setEnabled(iswav);
ui->btnMicWavBrowse->setEnabled(iswav);
int inst = Platform::InstanceID();
if (inst > 0)
{
ui->lblInstanceNum->setText(QString("Configuring settings for instance %1").arg(inst+1));
ui->cbInterpolation->setEnabled(false);
ui->cbBitrate->setEnabled(false);
for (QAbstractButton* btn : grpMicMode->buttons())
btn->setEnabled(false);
ui->txtMicWavPath->setEnabled(false);
ui->btnMicWavBrowse->setEnabled(false);
}
else
ui->lblInstanceNum->hide();
}
AudioSettingsDialog::~AudioSettingsDialog()

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>482</width>
<height>256</height>
<height>301</height>
</rect>
</property>
<property name="sizePolicy">
@ -23,6 +23,13 @@
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item>
<widget class="QLabel" name="lblInstanceNum">
<property name="text">
<string>Configuring settings for instance X</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
@ -76,7 +83,7 @@
<item row="1" column="1">
<widget class="QComboBox" name="cbBitrate">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The bitrate of audio playback. If set to &quot;Automatic&quot; this will be 10-bit for DS mode and 16-bit for DSi mode.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The bitrate of audio playback. If set to &quot;Automatic&quot; this will be 10-bit for DS mode and 16-bit for DSi mode.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>

View File

@ -1,10 +1,12 @@
project(qt_sdl)
include(CMakeDependentOption)
SET(SOURCES_QT_SDL
include(FixInterfaceIncludes)
set(SOURCES_QT_SDL
main.cpp
main_shaders.h
CheatsDialog.cpp
Config.cpp
Config.cpp
EmuSettingsDialog.cpp
PowerManagement/PowerManagementDialog.cpp
PowerManagement/resources/battery.qrc
@ -16,6 +18,7 @@ SET(SOURCES_QT_SDL
AudioSettingsDialog.cpp
FirmwareSettingsDialog.cpp
PathSettingsDialog.cpp
MPSettingsDialog.cpp
WifiSettingsDialog.cpp
InterfaceSettingsDialog.cpp
ROMInfoDialog.cpp
@ -24,13 +27,13 @@ SET(SOURCES_QT_SDL
Input.cpp
LAN_PCap.cpp
LAN_Socket.cpp
LocalMP.cpp
OSD.cpp
OSD_shaders.h
font.h
Platform.cpp
QPathInput.h
ROMManager.cpp
SaveManager.cpp
CameraManager.cpp
ArchiveUtil.h
@ -42,7 +45,7 @@ SET(SOURCES_QT_SDL
../mic_blow.h
${CMAKE_SOURCE_DIR}/res/melon.qrc
)
)
if (APPLE)
option(USE_QT6 "Build using Qt 6 instead of 5" ON)
@ -56,29 +59,11 @@ if (WIN32)
endif()
if (USE_QT6)
if (BUILD_STATIC AND QT6_STATIC_DIR)
set(QT6_STATIC_BASE ${QT6_STATIC_DIR}/lib/cmake/Qt6)
set(Qt6_DIR ${QT6_STATIC_BASE})
set(Qt6Core_DIR ${QT6_STATIC_BASE}Core)
set(Qt6Gui_DIR ${QT6_STATIC_BASE}Gui)
set(Qt6Widgets_DIR ${QT6_STATIC_BASE}Widgets)
set(Qt6Network_DIR ${QT6_STATIC_BASE}Network)
set(Qt6Multimedia_DIR ${QT6_STATIC_BASE}Multimedia)
set(Qt6OpenGL_DIR ${QT6_STATIC_BASE}OpenGL)
set(Qt6OpenGLWidgets_DIR ${QT6_STATIC_BASE}OpenGLWidgets)
endif()
find_package(Qt6 COMPONENTS Core Gui Widgets Network Multimedia OpenGL OpenGLWidgets REQUIRED)
set(QT_LINK_LIBS Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Network Qt6::Multimedia Qt6::OpenGL Qt6::OpenGLWidgets)
else()
if (BUILD_STATIC AND QT5_STATIC_DIR)
set(QT5_STATIC_BASE ${QT5_STATIC_DIR}/lib/cmake/Qt5)
set(Qt5_DIR ${QT5_STATIC_BASE})
set(Qt5Core_DIR ${QT5_STATIC_BASE}Core)
set(Qt5Gui_DIR ${QT5_STATIC_BASE}Gui)
set(Qt5Widgets_DIR ${QT5_STATIC_BASE}Widgets)
set(Qt5Network_DIR ${QT5_STATIC_BASE}Network)
set(Qt5Multimedia_DIR ${QT5_STATIC_BASE}Multimedia)
endif()
find_package(Qt5 COMPONENTS Core Gui Widgets Network Multimedia REQUIRED)
set(QT_LINK_LIBS Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Network Qt5::Multimedia)
endif()
@ -87,66 +72,53 @@ set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
find_package(Threads REQUIRED)
find_package(PkgConfig REQUIRED)
find_package(Iconv REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2)
pkg_check_modules(SLIRP REQUIRED slirp)
pkg_check_modules(LIBARCHIVE REQUIRED libarchive)
add_compile_definitions(ARCHIVE_SUPPORT_ENABLED)
if (WIN32 AND (CMAKE_BUILD_TYPE STREQUAL Release))
add_executable(melonDS WIN32 ${SOURCES_QT_SDL})
else()
add_executable(melonDS ${SOURCES_QT_SDL})
if (BUILD_STATIC)
list(APPEND PKG_CONFIG_EXECUTABLE "--static")
endif()
target_link_libraries(melonDS ${CMAKE_THREAD_LIBS_INIT})
find_package(Threads REQUIRED)
find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED IMPORTED_TARGET sdl2)
pkg_check_modules(Slirp REQUIRED IMPORTED_TARGET slirp)
pkg_check_modules(LibArchive REQUIRED IMPORTED_TARGET libarchive)
target_include_directories(melonDS PRIVATE ${SDL2_INCLUDE_DIRS} ${SDL2_PREFIX}/include ${SLIRP_INCLUDE_DIRS} ${LIBARCHIVE_INCLUDE_DIRS})
target_link_directories(melonDS PRIVATE ${SDL2_LIBRARY_DIRS} ${SLIRP_LIBRARY_DIRS})
target_link_directories(melonDS PRIVATE ${LIBARCHIVE_LIBRARY_DIRS})
fix_interface_includes(PkgConfig::SDL2 PkgConfig::Slirp PkgConfig::LibArchive)
add_compile_definitions(ARCHIVE_SUPPORT_ENABLED)
add_executable(melonDS ${SOURCES_QT_SDL})
if (BUILD_STATIC)
qt_import_plugins(melonDS INCLUDE Qt::QSvgPlugin)
target_link_options(melonDS PRIVATE -static)
endif()
target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../..")
target_link_libraries(melonDS core)
if (BUILD_STATIC)
target_link_libraries(melonDS -static ${SDL2_STATIC_LIBRARIES} ${SLIRP_STATIC_LIBRARIES} ${LIBARCHIVE_STATIC_LIBRARIES})
qt_import_plugins(melonDS INCLUDE Qt::QSvgPlugin)
else()
target_link_libraries(melonDS ${SDL2_LIBRARIES} ${SLIRP_LIBRARIES} ${LIBARCHIVE_LIBRARIES})
endif()
if (NOT Iconv_IS_BUILT_IN)
target_link_libraries(melonDS ${Iconv_LIBRARIES})
endif()
target_link_libraries(melonDS PRIVATE core)
target_link_libraries(melonDS PRIVATE PkgConfig::SDL2 PkgConfig::Slirp PkgConfig::LibArchive)
target_link_libraries(melonDS PRIVATE ${QT_LINK_LIBS} ${CMAKE_DL_LIBS})
if (UNIX)
option(PORTABLE "Make a portable build that looks for its configuration in the current directory" OFF)
target_link_libraries(melonDS ${QT_LINK_LIBS})
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_link_libraries(melonDS dl)
endif()
elseif (WIN32)
option(PORTABLE "Make a portable build that looks for its configuration in the current directory" ON)
configure_file("${CMAKE_SOURCE_DIR}/res/melon.rc.in" "${CMAKE_SOURCE_DIR}/melon.rc")
target_sources(melonDS PUBLIC "${CMAKE_SOURCE_DIR}/melon.rc")
target_link_libraries(melonDS comctl32 d2d1 dwrite uxtheme ws2_32 iphlpapi gdi32)
if (BUILD_STATIC)
target_link_libraries(melonDS imm32 winmm version setupapi -static z zstd ${QT_LINK_LIBS})
else()
target_link_libraries(melonDS ${QT_LINK_LIBS})
endif()
target_link_libraries(melonDS PRIVATE ws2_32 iphlpapi)
set_target_properties(melonDS PROPERTIES LINK_FLAGS_DEBUG "-mconsole")
endif()
if (PORTABLE)
add_definitions(-DPORTABLE)
target_compile_definitions(melonDS PRIVATE PORTABLE)
endif()
if (APPLE)
target_sources(melonDS PRIVATE sem_timedwait.cpp)
# Copy icon into the bundle
set(RESOURCE_FILES "${CMAKE_SOURCE_DIR}/res/melon.icns")
target_sources(melonDS PUBLIC "${RESOURCE_FILES}")
@ -156,7 +128,7 @@ if (APPLE)
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/res/melon.plist.in
OUTPUT_NAME melonDS
RESOURCE "${RESOURCE_FILES}")
option(MACOS_BUNDLE_LIBS "Bundle libraries with the app on macOS" OFF)
option(MACOS_BUILD_DMG "Build DMG image of the macOS application bundle" OFF)
@ -174,8 +146,8 @@ endif()
if (UNIX AND NOT APPLE)
foreach(SIZE 16 32 48 64 128 256)
install(FILES ${CMAKE_SOURCE_DIR}/res/icon/melon_${SIZE}x${SIZE}.png
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/${SIZE}x${SIZE}/apps
RENAME net.kuribo64.melonDS.png)
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/${SIZE}x${SIZE}/apps
RENAME net.kuribo64.melonDS.png)
endforeach()
install(FILES ${CMAKE_SOURCE_DIR}/res/net.kuribo64.melonDS.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)

View File

@ -106,9 +106,10 @@ int FirmwareBirthdayDay;
int FirmwareFavouriteColour;
std::string FirmwareMessage;
std::string FirmwareMAC;
bool RandomizeMAC;
bool SocketBindAnyAddr;
int MPAudioMode;
int MPRecvTimeout;
std::string LANDevice;
bool DirectLAN;
@ -143,211 +144,206 @@ CameraConfig Camera[2];
const char* kConfigFile = "melonDS.ini";
const char* kUniqueConfigFile = "melonDS.%d.ini";
ConfigEntry ConfigFile[] =
{
{"Key_A", 0, &KeyMapping[0], -1},
{"Key_B", 0, &KeyMapping[1], -1},
{"Key_Select", 0, &KeyMapping[2], -1},
{"Key_Start", 0, &KeyMapping[3], -1},
{"Key_Right", 0, &KeyMapping[4], -1},
{"Key_Left", 0, &KeyMapping[5], -1},
{"Key_Up", 0, &KeyMapping[6], -1},
{"Key_Down", 0, &KeyMapping[7], -1},
{"Key_R", 0, &KeyMapping[8], -1},
{"Key_L", 0, &KeyMapping[9], -1},
{"Key_X", 0, &KeyMapping[10], -1},
{"Key_Y", 0, &KeyMapping[11], -1},
{"Key_A", 0, &KeyMapping[0], -1, true},
{"Key_B", 0, &KeyMapping[1], -1, true},
{"Key_Select", 0, &KeyMapping[2], -1, true},
{"Key_Start", 0, &KeyMapping[3], -1, true},
{"Key_Right", 0, &KeyMapping[4], -1, true},
{"Key_Left", 0, &KeyMapping[5], -1, true},
{"Key_Up", 0, &KeyMapping[6], -1, true},
{"Key_Down", 0, &KeyMapping[7], -1, true},
{"Key_R", 0, &KeyMapping[8], -1, true},
{"Key_L", 0, &KeyMapping[9], -1, true},
{"Key_X", 0, &KeyMapping[10], -1, true},
{"Key_Y", 0, &KeyMapping[11], -1, true},
{"Joy_A", 0, &JoyMapping[0], -1},
{"Joy_B", 0, &JoyMapping[1], -1},
{"Joy_Select", 0, &JoyMapping[2], -1},
{"Joy_Start", 0, &JoyMapping[3], -1},
{"Joy_Right", 0, &JoyMapping[4], -1},
{"Joy_Left", 0, &JoyMapping[5], -1},
{"Joy_Up", 0, &JoyMapping[6], -1},
{"Joy_Down", 0, &JoyMapping[7], -1},
{"Joy_R", 0, &JoyMapping[8], -1},
{"Joy_L", 0, &JoyMapping[9], -1},
{"Joy_X", 0, &JoyMapping[10], -1},
{"Joy_Y", 0, &JoyMapping[11], -1},
{"Joy_A", 0, &JoyMapping[0], -1, true},
{"Joy_B", 0, &JoyMapping[1], -1, true},
{"Joy_Select", 0, &JoyMapping[2], -1, true},
{"Joy_Start", 0, &JoyMapping[3], -1, true},
{"Joy_Right", 0, &JoyMapping[4], -1, true},
{"Joy_Left", 0, &JoyMapping[5], -1, true},
{"Joy_Up", 0, &JoyMapping[6], -1, true},
{"Joy_Down", 0, &JoyMapping[7], -1, true},
{"Joy_R", 0, &JoyMapping[8], -1, true},
{"Joy_L", 0, &JoyMapping[9], -1, true},
{"Joy_X", 0, &JoyMapping[10], -1, true},
{"Joy_Y", 0, &JoyMapping[11], -1, true},
{"HKKey_Lid", 0, &HKKeyMapping[HK_Lid], -1},
{"HKKey_Mic", 0, &HKKeyMapping[HK_Mic], -1},
{"HKKey_Pause", 0, &HKKeyMapping[HK_Pause], -1},
{"HKKey_Reset", 0, &HKKeyMapping[HK_Reset], -1},
{"HKKey_FastForward", 0, &HKKeyMapping[HK_FastForward], -1},
{"HKKey_FastForwardToggle", 0, &HKKeyMapping[HK_FastForwardToggle], -1},
{"HKKey_FullscreenToggle", 0, &HKKeyMapping[HK_FullscreenToggle], -1},
{"HKKey_SwapScreens", 0, &HKKeyMapping[HK_SwapScreens], -1},
{"HKKey_SolarSensorDecrease", 0, &HKKeyMapping[HK_SolarSensorDecrease], -1},
{"HKKey_SolarSensorIncrease", 0, &HKKeyMapping[HK_SolarSensorIncrease], -1},
{"HKKey_FrameStep", 0, &HKKeyMapping[HK_FrameStep], -1},
{"HKKey_Lid", 0, &HKKeyMapping[HK_Lid], -1, true},
{"HKKey_Mic", 0, &HKKeyMapping[HK_Mic], -1, true},
{"HKKey_Pause", 0, &HKKeyMapping[HK_Pause], -1, true},
{"HKKey_Reset", 0, &HKKeyMapping[HK_Reset], -1, true},
{"HKKey_FastForward", 0, &HKKeyMapping[HK_FastForward], -1, true},
{"HKKey_FastForwardToggle", 0, &HKKeyMapping[HK_FastForwardToggle], -1, true},
{"HKKey_FullscreenToggle", 0, &HKKeyMapping[HK_FullscreenToggle], -1, true},
{"HKKey_SwapScreens", 0, &HKKeyMapping[HK_SwapScreens], -1, true},
{"HKKey_SolarSensorDecrease", 0, &HKKeyMapping[HK_SolarSensorDecrease], -1, true},
{"HKKey_SolarSensorIncrease", 0, &HKKeyMapping[HK_SolarSensorIncrease], -1, true},
{"HKKey_FrameStep", 0, &HKKeyMapping[HK_FrameStep], -1, true},
{"HKJoy_Lid", 0, &HKJoyMapping[HK_Lid], -1},
{"HKJoy_Mic", 0, &HKJoyMapping[HK_Mic], -1},
{"HKJoy_Pause", 0, &HKJoyMapping[HK_Pause], -1},
{"HKJoy_Reset", 0, &HKJoyMapping[HK_Reset], -1},
{"HKJoy_FastForward", 0, &HKJoyMapping[HK_FastForward], -1},
{"HKJoy_FastForwardToggle", 0, &HKJoyMapping[HK_FastForwardToggle], -1},
{"HKJoy_FullscreenToggle", 0, &HKJoyMapping[HK_FullscreenToggle], -1},
{"HKJoy_SwapScreens", 0, &HKJoyMapping[HK_SwapScreens], -1},
{"HKJoy_SolarSensorDecrease", 0, &HKJoyMapping[HK_SolarSensorDecrease], -1},
{"HKJoy_SolarSensorIncrease", 0, &HKJoyMapping[HK_SolarSensorIncrease], -1},
{"HKJoy_FrameStep", 0, &HKJoyMapping[HK_FrameStep], -1},
{"HKJoy_Lid", 0, &HKJoyMapping[HK_Lid], -1, true},
{"HKJoy_Mic", 0, &HKJoyMapping[HK_Mic], -1, true},
{"HKJoy_Pause", 0, &HKJoyMapping[HK_Pause], -1, true},
{"HKJoy_Reset", 0, &HKJoyMapping[HK_Reset], -1, true},
{"HKJoy_FastForward", 0, &HKJoyMapping[HK_FastForward], -1, true},
{"HKJoy_FastForwardToggle", 0, &HKJoyMapping[HK_FastForwardToggle], -1, true},
{"HKJoy_FullscreenToggle", 0, &HKJoyMapping[HK_FullscreenToggle], -1, true},
{"HKJoy_SwapScreens", 0, &HKJoyMapping[HK_SwapScreens], -1, true},
{"HKJoy_SolarSensorDecrease", 0, &HKJoyMapping[HK_SolarSensorDecrease], -1, true},
{"HKJoy_SolarSensorIncrease", 0, &HKJoyMapping[HK_SolarSensorIncrease], -1, true},
{"HKJoy_FrameStep", 0, &HKJoyMapping[HK_FrameStep], -1, true},
{"JoystickID", 0, &JoystickID, 0},
{"JoystickID", 0, &JoystickID, 0, true},
{"WindowWidth", 0, &WindowWidth, 256},
{"WindowHeight", 0, &WindowHeight, 384},
{"WindowMax", 1, &WindowMaximized, false},
{"WindowWidth", 0, &WindowWidth, 256, true},
{"WindowHeight", 0, &WindowHeight, 384, true},
{"WindowMax", 1, &WindowMaximized, false, true},
{"ScreenRotation", 0, &ScreenRotation, 0},
{"ScreenGap", 0, &ScreenGap, 0},
{"ScreenLayout", 0, &ScreenLayout, 0},
{"ScreenSwap", 1, &ScreenSwap, false},
{"ScreenSizing", 0, &ScreenSizing, 0},
{"IntegerScaling", 1, &IntegerScaling, false},
{"ScreenAspectTop",0, &ScreenAspectTop,0},
{"ScreenAspectBot",0, &ScreenAspectBot,0},
{"ScreenFilter", 1, &ScreenFilter, true},
{"ScreenRotation", 0, &ScreenRotation, 0, true},
{"ScreenGap", 0, &ScreenGap, 0, true},
{"ScreenLayout", 0, &ScreenLayout, 0, true},
{"ScreenSwap", 1, &ScreenSwap, false, true},
{"ScreenSizing", 0, &ScreenSizing, 0, true},
{"IntegerScaling", 1, &IntegerScaling, false, true},
{"ScreenAspectTop",0, &ScreenAspectTop,0, true},
{"ScreenAspectBot",0, &ScreenAspectBot,0, true},
{"ScreenFilter", 1, &ScreenFilter, true, true},
{"ScreenUseGL", 1, &ScreenUseGL, false},
{"ScreenVSync", 1, &ScreenVSync, false},
{"ScreenVSyncInterval", 0, &ScreenVSyncInterval, 1},
{"ScreenUseGL", 1, &ScreenUseGL, false, false},
{"ScreenVSync", 1, &ScreenVSync, false, false},
{"ScreenVSyncInterval", 0, &ScreenVSyncInterval, 1, false},
{"3DRenderer", 0, &_3DRenderer, 0},
{"Threaded3D", 1, &Threaded3D, true},
{"3DRenderer", 0, &_3DRenderer, 0, false},
{"Threaded3D", 1, &Threaded3D, true, false},
{"GL_ScaleFactor", 0, &GL_ScaleFactor, 1},
{"GL_BetterPolygons", 1, &GL_BetterPolygons, false},
{"GL_ScaleFactor", 0, &GL_ScaleFactor, 1, false},
{"GL_BetterPolygons", 1, &GL_BetterPolygons, false, false},
{"LimitFPS", 1, &LimitFPS, true},
{"LimitFPS", 1, &LimitFPS, true, false},
{"AudioSync", 1, &AudioSync, false},
{"ShowOSD", 1, &ShowOSD, true},
{"ShowOSD", 1, &ShowOSD, true, false},
{"ConsoleType", 0, &ConsoleType, 0},
{"DirectBoot", 1, &DirectBoot, true},
{"ConsoleType", 0, &ConsoleType, 0, false},
{"DirectBoot", 1, &DirectBoot, true, false},
#ifdef JIT_ENABLED
{"JIT_Enable", 1, &JIT_Enable, false},
{"JIT_MaxBlockSize", 0, &JIT_MaxBlockSize, 32},
{"JIT_BranchOptimisations", 1, &JIT_BranchOptimisations, true},
{"JIT_LiteralOptimisations", 1, &JIT_LiteralOptimisations, true},
{"JIT_Enable", 1, &JIT_Enable, false, false},
{"JIT_MaxBlockSize", 0, &JIT_MaxBlockSize, 32, false},
{"JIT_BranchOptimisations", 1, &JIT_BranchOptimisations, true, false},
{"JIT_LiteralOptimisations", 1, &JIT_LiteralOptimisations, true, false},
#ifdef __APPLE__
{"JIT_FastMemory", 1, &JIT_FastMemory, false},
{"JIT_FastMemory", 1, &JIT_FastMemory, false, false},
#else
{"JIT_FastMemory", 1, &JIT_FastMemory, true},
{"JIT_FastMemory", 1, &JIT_FastMemory, true, false},
#endif
#endif
{"ExternalBIOSEnable", 1, &ExternalBIOSEnable, false},
{"ExternalBIOSEnable", 1, &ExternalBIOSEnable, false, false},
{"BIOS9Path", 2, &BIOS9Path, (std::string)""},
{"BIOS7Path", 2, &BIOS7Path, (std::string)""},
{"FirmwarePath", 2, &FirmwarePath, (std::string)""},
{"BIOS9Path", 2, &BIOS9Path, (std::string)"", false},
{"BIOS7Path", 2, &BIOS7Path, (std::string)"", false},
{"FirmwarePath", 2, &FirmwarePath, (std::string)"", false},
{"DSiBIOS9Path", 2, &DSiBIOS9Path, (std::string)""},
{"DSiBIOS7Path", 2, &DSiBIOS7Path, (std::string)""},
{"DSiFirmwarePath", 2, &DSiFirmwarePath, (std::string)""},
{"DSiNANDPath", 2, &DSiNANDPath, (std::string)""},
{"DSiBIOS9Path", 2, &DSiBIOS9Path, (std::string)"", false},
{"DSiBIOS7Path", 2, &DSiBIOS7Path, (std::string)"", false},
{"DSiFirmwarePath", 2, &DSiFirmwarePath, (std::string)"", false},
{"DSiNANDPath", 2, &DSiNANDPath, (std::string)"", false},
{"DLDIEnable", 1, &DLDIEnable, false},
{"DLDISDPath", 2, &DLDISDPath, (std::string)"dldi.bin"},
{"DLDISize", 0, &DLDISize, 0},
{"DLDIReadOnly", 1, &DLDIReadOnly, false},
{"DLDIFolderSync", 1, &DLDIFolderSync, false},
{"DLDIFolderPath", 2, &DLDIFolderPath, (std::string)""},
{"DLDIEnable", 1, &DLDIEnable, false, false},
{"DLDISDPath", 2, &DLDISDPath, (std::string)"dldi.bin", false},
{"DLDISize", 0, &DLDISize, 0, false},
{"DLDIReadOnly", 1, &DLDIReadOnly, false, false},
{"DLDIFolderSync", 1, &DLDIFolderSync, false, false},
{"DLDIFolderPath", 2, &DLDIFolderPath, (std::string)"", false},
{"DSiSDEnable", 1, &DSiSDEnable, false},
{"DSiSDPath", 2, &DSiSDPath, (std::string)"dsisd.bin"},
{"DSiSDSize", 0, &DSiSDSize, 0},
{"DSiSDReadOnly", 1, &DSiSDReadOnly, false},
{"DSiSDFolderSync", 1, &DSiSDFolderSync, false},
{"DSiSDFolderPath", 2, &DSiSDFolderPath, (std::string)""},
{"DSiSDEnable", 1, &DSiSDEnable, false, false},
{"DSiSDPath", 2, &DSiSDPath, (std::string)"dsisd.bin", false},
{"DSiSDSize", 0, &DSiSDSize, 0, false},
{"DSiSDReadOnly", 1, &DSiSDReadOnly, false, false},
{"DSiSDFolderSync", 1, &DSiSDFolderSync, false, false},
{"DSiSDFolderPath", 2, &DSiSDFolderPath, (std::string)"", false},
{"FirmwareOverrideSettings", 1, &FirmwareOverrideSettings, false},
{"FirmwareUsername", 2, &FirmwareUsername, (std::string)"melonDS"},
{"FirmwareLanguage", 0, &FirmwareLanguage, 1},
{"FirmwareBirthdayMonth", 0, &FirmwareBirthdayMonth, 1},
{"FirmwareBirthdayDay", 0, &FirmwareBirthdayDay, 1},
{"FirmwareFavouriteColour", 0, &FirmwareFavouriteColour, 0},
{"FirmwareMessage", 2, &FirmwareMessage, (std::string)""},
{"FirmwareMAC", 2, &FirmwareMAC, (std::string)""},
{"RandomizeMAC", 1, &RandomizeMAC, false},
{"FirmwareOverrideSettings", 1, &FirmwareOverrideSettings, false, true},
{"FirmwareUsername", 2, &FirmwareUsername, (std::string)"melonDS", true},
{"FirmwareLanguage", 0, &FirmwareLanguage, 1, true},
{"FirmwareBirthdayMonth", 0, &FirmwareBirthdayMonth, 1, true},
{"FirmwareBirthdayDay", 0, &FirmwareBirthdayDay, 1, true},
{"FirmwareFavouriteColour", 0, &FirmwareFavouriteColour, 0, true},
{"FirmwareMessage", 2, &FirmwareMessage, (std::string)"", true},
{"FirmwareMAC", 2, &FirmwareMAC, (std::string)"", true},
{"SockBindAnyAddr", 1, &SocketBindAnyAddr, false},
{"LANDevice", 2, &LANDevice, (std::string)""},
{"DirectLAN", 1, &DirectLAN, false},
{"MPAudioMode", 0, &MPAudioMode, 1, false},
{"MPRecvTimeout", 0, &MPRecvTimeout, 25, false},
{"SavStaRelocSRAM", 1, &SavestateRelocSRAM, false},
{"LANDevice", 2, &LANDevice, (std::string)"", false},
{"DirectLAN", 1, &DirectLAN, false, false},
{"AudioInterp", 0, &AudioInterp, 0},
{"AudioBitrate", 0, &AudioBitrate, 0},
{"AudioVolume", 0, &AudioVolume, 256},
{"MicInputType", 0, &MicInputType, 1},
{"MicWavPath", 2, &MicWavPath, (std::string)""},
{"SavStaRelocSRAM", 1, &SavestateRelocSRAM, false, false},
{"LastROMFolder", 2, &LastROMFolder, (std::string)""},
{"AudioInterp", 0, &AudioInterp, 0, false},
{"AudioBitrate", 0, &AudioBitrate, 0, false},
{"AudioVolume", 0, &AudioVolume, 256, true},
{"MicInputType", 0, &MicInputType, 1, false},
{"MicWavPath", 2, &MicWavPath, (std::string)"", false},
{"RecentROM_0", 2, &RecentROMList[0], (std::string)""},
{"RecentROM_1", 2, &RecentROMList[1], (std::string)""},
{"RecentROM_2", 2, &RecentROMList[2], (std::string)""},
{"RecentROM_3", 2, &RecentROMList[3], (std::string)""},
{"RecentROM_4", 2, &RecentROMList[4], (std::string)""},
{"RecentROM_5", 2, &RecentROMList[5], (std::string)""},
{"RecentROM_6", 2, &RecentROMList[6], (std::string)""},
{"RecentROM_7", 2, &RecentROMList[7], (std::string)""},
{"RecentROM_8", 2, &RecentROMList[8], (std::string)""},
{"RecentROM_9", 2, &RecentROMList[9], (std::string)""},
{"LastROMFolder", 2, &LastROMFolder, (std::string)"", true},
{"SaveFilePath", 2, &SaveFilePath, (std::string)""},
{"SavestatePath", 2, &SavestatePath, (std::string)""},
{"CheatFilePath", 2, &CheatFilePath, (std::string)""},
{"RecentROM_0", 2, &RecentROMList[0], (std::string)"", true},
{"RecentROM_1", 2, &RecentROMList[1], (std::string)"", true},
{"RecentROM_2", 2, &RecentROMList[2], (std::string)"", true},
{"RecentROM_3", 2, &RecentROMList[3], (std::string)"", true},
{"RecentROM_4", 2, &RecentROMList[4], (std::string)"", true},
{"RecentROM_5", 2, &RecentROMList[5], (std::string)"", true},
{"RecentROM_6", 2, &RecentROMList[6], (std::string)"", true},
{"RecentROM_7", 2, &RecentROMList[7], (std::string)"", true},
{"RecentROM_8", 2, &RecentROMList[8], (std::string)"", true},
{"RecentROM_9", 2, &RecentROMList[9], (std::string)"", true},
{"EnableCheats", 1, &EnableCheats, false},
{"SaveFilePath", 2, &SaveFilePath, (std::string)"", true},
{"SavestatePath", 2, &SavestatePath, (std::string)"", true},
{"CheatFilePath", 2, &CheatFilePath, (std::string)"", true},
{"MouseHide", 1, &MouseHide, false},
{"MouseHideSeconds", 0, &MouseHideSeconds, 5},
{"PauseLostFocus", 1, &PauseLostFocus, false},
{"EnableCheats", 1, &EnableCheats, false, true},
{"DSBatteryLevelOkay", 1, &DSBatteryLevelOkay, true},
{"DSiBatteryLevel", 0, &DSiBatteryLevel, 0xF},
{"DSiBatteryCharging", 1, &DSiBatteryCharging, true},
{"MouseHide", 1, &MouseHide, false, false},
{"MouseHideSeconds", 0, &MouseHideSeconds, 5, false},
{"PauseLostFocus", 1, &PauseLostFocus, false, false},
{"DSBatteryLevelOkay", 1, &DSBatteryLevelOkay, true, true},
{"DSiBatteryLevel", 0, &DSiBatteryLevel, 0xF, true},
{"DSiBatteryCharging", 1, &DSiBatteryCharging, true, true},
// TODO!!
// we need a more elegant way to deal with this
{"Camera0_InputType", 0, &Camera[0].InputType, 0},
{"Camera0_ImagePath", 2, &Camera[0].ImagePath, (std::string)""},
{"Camera0_CamDeviceName", 2, &Camera[0].CamDeviceName, (std::string)""},
{"Camera0_XFlip", 1, &Camera[0].XFlip, false},
{"Camera1_InputType", 0, &Camera[1].InputType, 0},
{"Camera1_ImagePath", 2, &Camera[1].ImagePath, (std::string)""},
{"Camera1_CamDeviceName", 2, &Camera[1].CamDeviceName, (std::string)""},
{"Camera1_XFlip", 1, &Camera[1].XFlip, false},
{"Camera0_InputType", 0, &Camera[0].InputType, 0, false},
{"Camera0_ImagePath", 2, &Camera[0].ImagePath, (std::string)"", false},
{"Camera0_CamDeviceName", 2, &Camera[0].CamDeviceName, (std::string)"", false},
{"Camera0_XFlip", 1, &Camera[0].XFlip, false, false},
{"Camera1_InputType", 0, &Camera[1].InputType, 0, false},
{"Camera1_ImagePath", 2, &Camera[1].ImagePath, (std::string)"", false},
{"Camera1_CamDeviceName", 2, &Camera[1].CamDeviceName, (std::string)"", false},
{"Camera1_XFlip", 1, &Camera[1].XFlip, false, false},
{"", -1, nullptr, 0}
{"", -1, nullptr, 0, false}
};
void Load()
void LoadFile(int inst)
{
ConfigEntry* entry = &ConfigFile[0];
for (;;)
FILE* f;
if (inst > 0)
{
if (!entry->Value) break;
switch (entry->Type)
{
case 0: *(int*)entry->Value = std::get<int>(entry->Default); break;
case 1: *(bool*)entry->Value = std::get<bool>(entry->Default); break;
case 2: *(std::string*)entry->Value = std::get<std::string>(entry->Default); break;
}
entry++;
char name[100] = {0};
snprintf(name, 99, kUniqueConfigFile, inst+1);
f = Platform::OpenLocalFile(name, "r");
}
else
f = Platform::OpenLocalFile(kConfigFile, "r");
FILE* f = Platform::OpenLocalFile(kConfigFile, "r");
if (!f) return;
char linebuf[1024];
@ -362,13 +358,13 @@ void Load()
entryname[31] = '\0';
if (ret < 2) continue;
ConfigEntry* entry = &ConfigFile[0];
for (;;)
for (ConfigEntry* entry = &ConfigFile[0]; entry->Value; entry++)
{
if (!entry->Value) break;
if (!strncmp(entry->Name, entryname, 32))
{
if ((inst > 0) && (!entry->InstanceUnique))
break;
switch (entry->Type)
{
case 0: *(int*)entry->Value = strtol(entryval, NULL, 10); break;
@ -378,23 +374,52 @@ void Load()
break;
}
entry++;
}
}
fclose(f);
}
void Load()
{
for (ConfigEntry* entry = &ConfigFile[0]; entry->Value; entry++)
{
switch (entry->Type)
{
case 0: *(int*)entry->Value = std::get<int>(entry->Default); break;
case 1: *(bool*)entry->Value = std::get<bool>(entry->Default); break;
case 2: *(std::string*)entry->Value = std::get<std::string>(entry->Default); break;
}
}
LoadFile(0);
int inst = Platform::InstanceID();
if (inst > 0)
LoadFile(inst);
}
void Save()
{
FILE* f = Platform::OpenLocalFile(kConfigFile, "w");
int inst = Platform::InstanceID();
FILE* f;
if (inst > 0)
{
char name[100] = {0};
snprintf(name, 99, kUniqueConfigFile, inst+1);
f = Platform::OpenLocalFile(name, "w");
}
else
f = Platform::OpenLocalFile(kConfigFile, "w");
if (!f) return;
ConfigEntry* entry = &ConfigFile[0];
for (;;)
for (ConfigEntry* entry = &ConfigFile[0]; entry->Value; entry++)
{
if (!entry->Value) break;
if ((inst > 0) && (!entry->InstanceUnique))
continue;
switch (entry->Type)
{
@ -402,8 +427,6 @@ void Save()
case 1: fprintf(f, "%s=%d\r\n", entry->Name, *(bool*)entry->Value ? 1:0); break;
case 2: fprintf(f, "%s=%s\r\n", entry->Name, (*(std::string*)entry->Value).c_str()); break;
}
entry++;
}
fclose(f);

View File

@ -58,6 +58,7 @@ struct ConfigEntry
int Type; // 0=int 1=bool 2=string
void* Value; // pointer to the value variable
std::variant<int, bool, std::string> Default;
bool InstanceUnique; // whether the setting can exist individually for each instance in multiplayer
};
struct CameraConfig
@ -149,9 +150,10 @@ extern int FirmwareBirthdayDay;
extern int FirmwareFavouriteColour;
extern std::string FirmwareMessage;
extern std::string FirmwareMAC;
extern bool RandomizeMAC;
extern bool SocketBindAnyAddr;
extern int MPAudioMode;
extern int MPRecvTimeout;
extern std::string LANDevice;
extern bool DirectLAN;

View File

@ -18,6 +18,7 @@
#include <QMessageBox>
#include "Platform.h"
#include "Config.h"
#include "FirmwareSettingsDialog.h"
@ -64,10 +65,14 @@ FirmwareSettingsDialog::FirmwareSettingsDialog(QWidget* parent) : QDialog(parent
ui->overrideFirmwareBox->setChecked(Config::FirmwareOverrideSettings);
ui->txtMAC->setText(QString::fromStdString(Config::FirmwareMAC));
ui->cbRandomizeMAC->setChecked(Config::RandomizeMAC);
on_overrideFirmwareBox_toggled();
on_cbRandomizeMAC_toggled();
int inst = Platform::InstanceID();
if (inst > 0)
ui->lblInstanceNum->setText(QString("Configuring settings for instance %1").arg(inst+1));
else
ui->lblInstanceNum->hide();
}
FirmwareSettingsDialog::~FirmwareSettingsDialog()
@ -135,7 +140,6 @@ void FirmwareSettingsDialog::done(int r)
std::string newMessage = ui->messageEdit->text().toStdString();
std::string newMAC = ui->txtMAC->text().toStdString();
bool newRandomizeMAC = ui->cbRandomizeMAC->isChecked();
if ( newOverride != Config::FirmwareOverrideSettings
|| newName != Config::FirmwareUsername
@ -144,8 +148,7 @@ void FirmwareSettingsDialog::done(int r)
|| newBirthdayDay != Config::FirmwareBirthdayDay
|| newBirthdayMonth != Config::FirmwareBirthdayMonth
|| newMessage != Config::FirmwareMessage
|| newMAC != Config::FirmwareMAC
|| newRandomizeMAC != Config::RandomizeMAC)
|| newMAC != Config::FirmwareMAC)
{
if (RunningSomething
&& QMessageBox::warning(this, "Reset necessary to apply changes",
@ -163,7 +166,6 @@ void FirmwareSettingsDialog::done(int r)
Config::FirmwareMessage = newMessage;
Config::FirmwareMAC = newMAC;
Config::RandomizeMAC = newRandomizeMAC;
Config::Save();
@ -210,9 +212,3 @@ void FirmwareSettingsDialog::on_overrideFirmwareBox_toggled()
ui->grpUserSettings->setDisabled(disable);
ui->grpWifiSettings->setDisabled(disable);
}
void FirmwareSettingsDialog::on_cbRandomizeMAC_toggled()
{
bool disable = ui->cbRandomizeMAC->isChecked();
ui->txtMAC->setDisabled(disable);
}

View File

@ -124,7 +124,6 @@ private slots:
void on_cbxBirthdayMonth_currentIndexChanged(int idx);
void on_overrideFirmwareBox_toggled();
void on_cbRandomizeMAC_toggled();
private:
bool verifyMAC();

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>511</width>
<height>342</height>
<height>357</height>
</rect>
</property>
<property name="sizePolicy">
@ -23,6 +23,13 @@
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item>
<widget class="QLabel" name="lblInstanceNum">
<property name="text">
<string>Configuring settings for instance X</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="grpGeneral">
<property name="title">
@ -144,9 +151,9 @@
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="cbRandomizeMAC">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Randomize</string>
<string>(leave empty to use default MAC)</string>
</property>
</widget>
</item>

View File

@ -24,6 +24,7 @@
#include <SDL2/SDL.h>
#include "types.h"
#include "Platform.h"
#include "Config.h"
#include "MapButton.h"
@ -123,6 +124,12 @@ InputConfigDialog::InputConfigDialog(QWidget* parent) : QDialog(parent), ui(new
}
setupKeypadPage();
int inst = Platform::InstanceID();
if (inst > 0)
ui->lblInstanceNum->setText(QString("Configuring mappings for instance %1").arg(inst+1));
else
ui->lblInstanceNum->hide();
}
InputConfigDialog::~InputConfigDialog()

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>770</width>
<height>719</height>
<height>678</height>
</rect>
</property>
<property name="windowTitle">
@ -20,7 +20,7 @@
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item row="7" column="1">
<item row="8" column="1">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@ -30,49 +30,7 @@
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<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="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Joystick:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cbxJoystick">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Selects which joystick will be used for joystick input, if any is present.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0" colspan="2">
<item row="1" column="0" colspan="2">
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
@ -167,7 +125,7 @@
<widget class="QPushButton" name="btnKeyR">
<property name="minimumSize">
<size>
<width>76</width>
<width>70</width>
<height>0</height>
</size>
</property>
@ -258,7 +216,7 @@
<widget class="QPushButton" name="btnKeyL">
<property name="minimumSize">
<size>
<width>76</width>
<width>70</width>
<height>0</height>
</size>
</property>
@ -384,7 +342,7 @@
<widget class="QPushButton" name="btnKeyX">
<property name="minimumSize">
<size>
<width>76</width>
<width>70</width>
<height>0</height>
</size>
</property>
@ -464,7 +422,7 @@
<widget class="QPushButton" name="btnKeyY">
<property name="minimumSize">
<size>
<width>76</width>
<width>70</width>
<height>0</height>
</size>
</property>
@ -523,7 +481,7 @@
<widget class="QPushButton" name="btnKeyA">
<property name="minimumSize">
<size>
<width>76</width>
<width>70</width>
<height>0</height>
</size>
</property>
@ -615,7 +573,7 @@
<widget class="QPushButton" name="btnKeyB">
<property name="minimumSize">
<size>
<width>76</width>
<width>70</width>
<height>0</height>
</size>
</property>
@ -698,7 +656,7 @@
<widget class="QPushButton" name="btnKeySelect">
<property name="minimumSize">
<size>
<width>76</width>
<width>70</width>
<height>0</height>
</size>
</property>
@ -757,7 +715,7 @@
<widget class="QPushButton" name="btnKeyStart">
<property name="minimumSize">
<size>
<width>76</width>
<width>70</width>
<height>0</height>
</size>
</property>
@ -882,7 +840,7 @@
<widget class="QPushButton" name="btnKeyUp">
<property name="minimumSize">
<size>
<width>76</width>
<width>70</width>
<height>0</height>
</size>
</property>
@ -962,7 +920,7 @@
<widget class="QPushButton" name="btnKeyLeft">
<property name="minimumSize">
<size>
<width>76</width>
<width>70</width>
<height>0</height>
</size>
</property>
@ -1021,7 +979,7 @@
<widget class="QPushButton" name="btnKeyRight">
<property name="minimumSize">
<size>
<width>76</width>
<width>70</width>
<height>0</height>
</size>
</property>
@ -1113,7 +1071,7 @@
<widget class="QPushButton" name="btnKeyDown">
<property name="minimumSize">
<size>
<width>76</width>
<width>70</width>
<height>0</height>
</size>
</property>
@ -1289,7 +1247,7 @@
<widget class="QPushButton" name="btnJoyL">
<property name="minimumSize">
<size>
<width>76</width>
<width>70</width>
<height>0</height>
</size>
</property>
@ -1441,7 +1399,7 @@
<widget class="QPushButton" name="btnJoyUp">
<property name="minimumSize">
<size>
<width>76</width>
<width>70</width>
<height>0</height>
</size>
</property>
@ -1521,7 +1479,7 @@
<widget class="QPushButton" name="btnJoyLeft">
<property name="minimumSize">
<size>
<width>76</width>
<width>70</width>
<height>0</height>
</size>
</property>
@ -1580,7 +1538,7 @@
<widget class="QPushButton" name="btnJoyRight">
<property name="minimumSize">
<size>
<width>76</width>
<width>70</width>
<height>0</height>
</size>
</property>
@ -1672,7 +1630,7 @@
<widget class="QPushButton" name="btnJoyDown">
<property name="minimumSize">
<size>
<width>76</width>
<width>70</width>
<height>0</height>
</size>
</property>
@ -1814,7 +1772,7 @@
<widget class="QPushButton" name="btnJoyX">
<property name="minimumSize">
<size>
<width>76</width>
<width>70</width>
<height>0</height>
</size>
</property>
@ -1894,7 +1852,7 @@
<widget class="QPushButton" name="btnJoyY">
<property name="minimumSize">
<size>
<width>76</width>
<width>70</width>
<height>0</height>
</size>
</property>
@ -1953,7 +1911,7 @@
<widget class="QPushButton" name="btnJoyA">
<property name="minimumSize">
<size>
<width>76</width>
<width>70</width>
<height>0</height>
</size>
</property>
@ -2045,7 +2003,7 @@
<widget class="QPushButton" name="btnJoyB">
<property name="minimumSize">
<size>
<width>76</width>
<width>70</width>
<height>0</height>
</size>
</property>
@ -2128,7 +2086,7 @@
<widget class="QPushButton" name="btnJoySelect">
<property name="minimumSize">
<size>
<width>76</width>
<width>70</width>
<height>0</height>
</size>
</property>
@ -2187,7 +2145,7 @@
<widget class="QPushButton" name="btnJoyStart">
<property name="minimumSize">
<size>
<width>76</width>
<width>70</width>
<height>0</height>
</size>
</property>
@ -2251,7 +2209,7 @@
<widget class="QPushButton" name="btnJoyR">
<property name="minimumSize">
<size>
<width>76</width>
<width>70</width>
<height>0</height>
</size>
</property>
@ -2321,6 +2279,55 @@
</widget>
</widget>
</item>
<item row="7" column="0" colspan="2">
<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="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Joystick:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cbxJoystick">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Selects which joystick will be used for joystick input, if any is present.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QLabel" name="lblInstanceNum">
<property name="text">
<string>Configuring mappings for instance X</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources>

View File

@ -163,6 +163,7 @@ public:
setCheckable(true);
setText(mappingText());
setFocusPolicy(Qt::StrongFocus); //Fixes binding keys in macOS
connect(this, &JoyMapButton::clicked, this, &JoyMapButton::onClick);
@ -245,19 +246,21 @@ protected:
Sint16 axisval = SDL_JoystickGetAxis(joy, i);
int diff = abs(axisval - axesRest[i]);
if (axesRest[i] < -16384 && axisval >= 0)
if (diff >= 16384)
{
*mapping = (oldmap & 0xFFFF) | 0x10000 | (2 << 20) | (i << 24);
click();
return;
}
else if (diff > 16384)
{
int axistype;
if (axisval > 0) axistype = 0;
else axistype = 1;
if (axesRest[i] < -16384) // Trigger
{
*mapping = (oldmap & 0xFFFF) | 0x10000 | (2 << 20) | (i << 24);
}
else // Analog stick
{
int axistype;
if (axisval > 0) axistype = 0;
else axistype = 1;
*mapping = (oldmap & 0xFFFF) | 0x10000 | (axistype << 20) | (i << 24);
}
*mapping = (oldmap & 0xFFFF) | 0x10000 | (axistype << 20) | (i << 24);
click();
return;
}
@ -352,4 +355,4 @@ private:
int axesRest[16];
};
#endif // MAPBUTTON_H
#endif // MAPBUTTON_H

View File

@ -114,6 +114,12 @@ bool TryLoadPCap(void* lib)
bool Init(bool open_adapter)
{
PCapAdapter = NULL;
PacketLen = 0;
RXNum = 0;
NumAdapters = 0;
// TODO: how to deal with cases where an adapter is unplugged or changes config??
if (!PCapLib)
{
@ -142,12 +148,6 @@ bool Init(bool open_adapter)
}
}
PCapAdapter = NULL;
PacketLen = 0;
RXNum = 0;
NumAdapters = 0;
char errbuf[PCAP_ERRBUF_SIZE];
int ret;

View File

@ -0,0 +1,634 @@
/*
Copyright 2016-2022 melonDS team
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef __WIN32__
#include <windows.h>
#else
#include <fcntl.h>
#include <semaphore.h>
#include <time.h>
#ifdef __APPLE__
#include "sem_timedwait.h"
#endif
#endif
#include <string>
#include <QSharedMemory>
#include "Config.h"
#include "LocalMP.h"
namespace LocalMP
{
u32 MPUniqueID;
u8 PacketBuffer[2048];
struct MPQueueHeader
{
u16 NumInstances;
u16 InstanceBitmask; // bitmask of all instances present
u16 ConnectedBitmask; // bitmask of which instances are ready to send/receive packets
u32 PacketWriteOffset;
u32 ReplyWriteOffset;
u16 MPHostInstanceID; // instance ID from which the last CMD frame was sent
u16 MPReplyBitmask; // bitmask of which clients replied in time
};
struct MPPacketHeader
{
u32 Magic;
u32 SenderID;
u32 Type; // 0=regular 1=CMD 2=reply 3=ack
u32 Length;
u64 Timestamp;
};
struct MPSync
{
u32 Magic;
u32 SenderID;
u16 ClientMask;
u16 Type;
u64 Timestamp;
};
QSharedMemory* MPQueue;
int InstanceID;
u32 PacketReadOffset;
u32 ReplyReadOffset;
const u32 kQueueSize = 0x20000;
const u32 kMaxFrameSize = 0x800;
const u32 kPacketStart = sizeof(MPQueueHeader);
const u32 kReplyStart = kQueueSize / 2;
const u32 kPacketEnd = kReplyStart;
const u32 kReplyEnd = kQueueSize;
int RecvTimeout;
int LastHostID;
// we need to come up with our own abstraction layer for named semaphores
// because QSystemSemaphore doesn't support waiting with a timeout
// and, as such, is unsuitable to our needs
#ifdef __WIN32__
bool SemInited[32];
HANDLE SemPool[32];
void SemPoolInit()
{
for (int i = 0; i < 32; i++)
{
SemPool[i] = INVALID_HANDLE_VALUE;
SemInited[i] = false;
}
}
void SemDeinit(int num);
void SemPoolDeinit()
{
for (int i = 0; i < 32; i++)
SemDeinit(i);
}
bool SemInit(int num)
{
if (SemInited[num])
return true;
char semname[64];
sprintf(semname, "Local\\melonNIFI_Sem%02d", num);
HANDLE sem = CreateSemaphore(nullptr, 0, 64, semname);
SemPool[num] = sem;
SemInited[num] = true;
return sem != INVALID_HANDLE_VALUE;
}
void SemDeinit(int num)
{
if (SemPool[num] != INVALID_HANDLE_VALUE)
{
CloseHandle(SemPool[num]);
SemPool[num] = INVALID_HANDLE_VALUE;
}
SemInited[num] = false;
}
bool SemPost(int num)
{
SemInit(num);
return ReleaseSemaphore(SemPool[num], 1, nullptr) != 0;
}
bool SemWait(int num, int timeout)
{
return WaitForSingleObject(SemPool[num], timeout) == WAIT_OBJECT_0;
}
void SemReset(int num)
{
while (WaitForSingleObject(SemPool[num], 0) == WAIT_OBJECT_0);
}
#else
bool SemInited[32];
sem_t* SemPool[32];
void SemPoolInit()
{
for (int i = 0; i < 32; i++)
{
SemPool[i] = SEM_FAILED;
SemInited[i] = false;
}
}
void SemDeinit(int num);
void SemPoolDeinit()
{
for (int i = 0; i < 32; i++)
SemDeinit(i);
}
bool SemInit(int num)
{
if (SemInited[num])
return true;
char semname[64];
sprintf(semname, "/melonNIFI_Sem%02d", num);
sem_t* sem = sem_open(semname, O_CREAT, 0644, 0);
SemPool[num] = sem;
SemInited[num] = true;
return sem != SEM_FAILED;
}
void SemDeinit(int num)
{
if (SemPool[num] != SEM_FAILED)
{
sem_close(SemPool[num]);
SemPool[num] = SEM_FAILED;
}
SemInited[num] = false;
}
bool SemPost(int num)
{
SemInit(num);
return sem_post(SemPool[num]) == 0;
}
bool SemWait(int num, int timeout)
{
if (!timeout)
return sem_trywait(SemPool[num]) == 0;
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_nsec += timeout * 1000000;
long sec = ts.tv_nsec / 1000000000;
ts.tv_nsec -= sec * 1000000000;
ts.tv_sec += sec;
return sem_timedwait(SemPool[num], &ts) == 0;
}
void SemReset(int num)
{
while (sem_trywait(SemPool[num]) == 0);
}
#endif
bool Init()
{
MPQueue = new QSharedMemory("melonNIFI");
if (!MPQueue->attach())
{
printf("MP sharedmem doesn't exist. creating\n");
if (!MPQueue->create(kQueueSize))
{
printf("MP sharedmem create failed :(\n");
return false;
}
MPQueue->lock();
memset(MPQueue->data(), 0, MPQueue->size());
MPQueueHeader* header = (MPQueueHeader*)MPQueue->data();
header->PacketWriteOffset = kPacketStart;
header->ReplyWriteOffset = kReplyStart;
MPQueue->unlock();
}
MPQueue->lock();
MPQueueHeader* header = (MPQueueHeader*)MPQueue->data();
u16 mask = header->InstanceBitmask;
for (int i = 0; i < 16; i++)
{
if (!(mask & (1<<i)))
{
InstanceID = i;
header->InstanceBitmask |= (1<<i);
//header->ConnectedBitmask |= (1 << i);
break;
}
}
header->NumInstances++;
PacketReadOffset = header->PacketWriteOffset;
ReplyReadOffset = header->ReplyWriteOffset;
MPQueue->unlock();
// prepare semaphores
// semaphores 0-15: regular frames; semaphore I is posted when instance I needs to process a new frame
// semaphores 16-31: MP replies; semaphore I is posted when instance I needs to process a new MP reply
SemPoolInit();
SemInit(InstanceID);
SemInit(16+InstanceID);
LastHostID = -1;
printf("MP comm init OK, instance ID %d\n", InstanceID);
RecvTimeout = 25;
return true;
}
void DeInit()
{
MPQueue->lock();
MPQueueHeader* header = (MPQueueHeader*)MPQueue->data();
header->ConnectedBitmask &= ~(1 << InstanceID);
header->InstanceBitmask &= ~(1 << InstanceID);
header->NumInstances--;
MPQueue->unlock();
SemPoolDeinit();
MPQueue->detach();
delete MPQueue;
}
void SetRecvTimeout(int timeout)
{
RecvTimeout = timeout;
}
void Begin()
{
MPQueue->lock();
MPQueueHeader* header = (MPQueueHeader*)MPQueue->data();
PacketReadOffset = header->PacketWriteOffset;
ReplyReadOffset = header->ReplyWriteOffset;
SemReset(InstanceID);
SemReset(16+InstanceID);
header->ConnectedBitmask |= (1 << InstanceID);
MPQueue->unlock();
}
void End()
{
MPQueue->lock();
MPQueueHeader* header = (MPQueueHeader*)MPQueue->data();
//SemReset(InstanceID);
//SemReset(16+InstanceID);
header->ConnectedBitmask &= ~(1 << InstanceID);
MPQueue->unlock();
}
void FIFORead(int fifo, void* buf, int len)
{
u8* data = (u8*)MPQueue->data();
u32 offset, start, end;
if (fifo == 0)
{
offset = PacketReadOffset;
start = kPacketStart;
end = kPacketEnd;
}
else
{
offset = ReplyReadOffset;
start = kReplyStart;
end = kReplyEnd;
}
if ((offset + len) >= end)
{
u32 part1 = end - offset;
memcpy(buf, &data[offset], part1);
memcpy(&((u8*)buf)[part1], &data[start], len - part1);
offset = start + len - part1;
}
else
{
memcpy(buf, &data[offset], len);
offset += len;
}
if (fifo == 0) PacketReadOffset = offset;
else ReplyReadOffset = offset;
}
void FIFOWrite(int fifo, void* buf, int len)
{
u8* data = (u8*)MPQueue->data();
MPQueueHeader* header = (MPQueueHeader*)&data[0];
u32 offset, start, end;
if (fifo == 0)
{
offset = header->PacketWriteOffset;
start = kPacketStart;
end = kPacketEnd;
}
else
{
offset = header->ReplyWriteOffset;
start = kReplyStart;
end = kReplyEnd;
}
if ((offset + len) >= end)
{
u32 part1 = end - offset;
memcpy(&data[offset], buf, part1);
memcpy(&data[start], &((u8*)buf)[part1], len - part1);
offset = start + len - part1;
}
else
{
memcpy(&data[offset], buf, len);
offset += len;
}
if (fifo == 0) header->PacketWriteOffset = offset;
else header->ReplyWriteOffset = offset;
}
int SendPacketGeneric(u32 type, u8* packet, int len, u64 timestamp)
{
MPQueue->lock();
u8* data = (u8*)MPQueue->data();
MPQueueHeader* header = (MPQueueHeader*)&data[0];
u16 mask = header->ConnectedBitmask;
// TODO: check if the FIFO is full!
MPPacketHeader pktheader;
pktheader.Magic = 0x4946494E;
pktheader.SenderID = InstanceID;
pktheader.Type = type;
pktheader.Length = len;
pktheader.Timestamp = timestamp;
type &= 0xFFFF;
int nfifo = (type == 2) ? 1 : 0;
FIFOWrite(nfifo, &pktheader, sizeof(pktheader));
if (len)
FIFOWrite(nfifo, packet, len);
if (type == 1)
{
// NOTE: this is not guarded against, say, multiple multiplay games happening on the same machine
// we would need to pass the packet's SenderID through the wifi module for that
header->MPHostInstanceID = InstanceID;
header->MPReplyBitmask = 0;
ReplyReadOffset = header->ReplyWriteOffset;
SemReset(16 + InstanceID);
}
else if (type == 2)
{
header->MPReplyBitmask |= (1 << InstanceID);
}
MPQueue->unlock();
if (type == 2)
{
SemPost(16 + header->MPHostInstanceID);
}
else
{
for (int i = 0; i < 16; i++)
{
if (mask & (1<<i))
SemPost(i);
}
}
return len;
}
int RecvPacketGeneric(u8* packet, bool block, u64* timestamp)
{
for (;;)
{
if (!SemWait(InstanceID, block ? RecvTimeout : 0))
{
return 0;
}
MPQueue->lock();
u8* data = (u8*)MPQueue->data();
MPQueueHeader* header = (MPQueueHeader*)&data[0];
MPPacketHeader pktheader;
FIFORead(0, &pktheader, sizeof(pktheader));
if (pktheader.Magic != 0x4946494E)
{
printf("PACKET FIFO OVERFLOW\n");
PacketReadOffset = header->PacketWriteOffset;
SemReset(InstanceID);
MPQueue->unlock();
return 0;
}
if (pktheader.SenderID == InstanceID)
{
// skip this packet
PacketReadOffset += pktheader.Length;
if (PacketReadOffset >= kPacketEnd)
PacketReadOffset += kPacketStart - kPacketEnd;
MPQueue->unlock();
continue;
}
if (pktheader.Length)
{
FIFORead(0, packet, pktheader.Length);
if (pktheader.Type == 1)
LastHostID = pktheader.SenderID;
}
if (timestamp) *timestamp = pktheader.Timestamp;
MPQueue->unlock();
return pktheader.Length;
}
}
int SendPacket(u8* packet, int len, u64 timestamp)
{
return SendPacketGeneric(0, packet, len, timestamp);
}
int RecvPacket(u8* packet, u64* timestamp)
{
return RecvPacketGeneric(packet, false, timestamp);
}
int SendCmd(u8* packet, int len, u64 timestamp)
{
return SendPacketGeneric(1, packet, len, timestamp);
}
int SendReply(u8* packet, int len, u64 timestamp, u16 aid)
{
return SendPacketGeneric(2 | (aid<<16), packet, len, timestamp);
}
int SendAck(u8* packet, int len, u64 timestamp)
{
return SendPacketGeneric(3, packet, len, timestamp);
}
int RecvHostPacket(u8* packet, u64* timestamp)
{
if (LastHostID != -1)
{
// check if the host is still connected
MPQueue->lock();
u8* data = (u8*)MPQueue->data();
MPQueueHeader* header = (MPQueueHeader*)&data[0];
u16 curinstmask = header->ConnectedBitmask;
MPQueue->unlock();
if (!(curinstmask & (1 << LastHostID)))
return -1;
}
return RecvPacketGeneric(packet, true, timestamp);
}
u16 RecvReplies(u8* packets, u64 timestamp, u16 aidmask)
{
u16 ret = 0;
u16 myinstmask = (1 << InstanceID);
u16 curinstmask;
{
MPQueue->lock();
u8* data = (u8*)MPQueue->data();
MPQueueHeader* header = (MPQueueHeader*)&data[0];
curinstmask = header->ConnectedBitmask;
MPQueue->unlock();
}
// if all clients have left: return early
if ((myinstmask & curinstmask) == curinstmask)
return 0;
for (;;)
{
if (!SemWait(16+InstanceID, RecvTimeout))
{
// no more replies available
return ret;
}
MPQueue->lock();
u8* data = (u8*)MPQueue->data();
MPQueueHeader* header = (MPQueueHeader*)&data[0];
MPPacketHeader pktheader;
FIFORead(1, &pktheader, sizeof(pktheader));
if (pktheader.Magic != 0x4946494E)
{
printf("REPLY FIFO OVERFLOW\n");
ReplyReadOffset = header->ReplyWriteOffset;
SemReset(16+InstanceID);
MPQueue->unlock();
return 0;
}
if ((pktheader.SenderID == InstanceID) || // packet we sent out (shouldn't happen, but hey)
(pktheader.Timestamp < (timestamp - 32))) // stale packet
{
// skip this packet
ReplyReadOffset += pktheader.Length;
if (ReplyReadOffset >= kReplyEnd)
ReplyReadOffset += kReplyStart - kReplyEnd;
MPQueue->unlock();
continue;
}
if (pktheader.Length)
{
u32 aid = (pktheader.Type >> 16);
FIFORead(1, &packets[(aid-1)*1024], pktheader.Length);
ret |= (1 << aid);
}
myinstmask |= (1 << pktheader.SenderID);
if (((myinstmask & curinstmask) == curinstmask) ||
((ret & aidmask) == aidmask))
{
// all the clients have sent their reply
MPQueue->unlock();
return ret;
}
MPQueue->unlock();
}
}
}

View File

@ -0,0 +1,45 @@
/*
Copyright 2016-2022 melonDS team
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef LOCALMP_H
#define LOCALMP_H
#include "types.h"
namespace LocalMP
{
bool Init();
void DeInit();
void SetRecvTimeout(int timeout);
void Begin();
void End();
int SendPacket(u8* data, int len, u64 timestamp);
int RecvPacket(u8* data, u64* timestamp);
int SendCmd(u8* data, int len, u64 timestamp);
int SendReply(u8* data, int len, u64 timestamp, u16 aid);
int SendAck(u8* data, int len, u64 timestamp);
int RecvHostPacket(u8* data, u64* timestamp);
u16 RecvReplies(u8* data, u64 timestamp, u16 aidmask);
}
#endif // LOCALMP_H

View File

@ -0,0 +1,73 @@
/*
Copyright 2016-2022 melonDS team
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <QMessageBox>
#include "types.h"
#include "Platform.h"
#include "Config.h"
#include "LAN_Socket.h"
#include "LAN_PCap.h"
#include "Wifi.h"
#include "MPSettingsDialog.h"
#include "ui_MPSettingsDialog.h"
MPSettingsDialog* MPSettingsDialog::currentDlg = nullptr;
extern bool RunningSomething;
MPSettingsDialog::MPSettingsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::MPSettingsDialog)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
grpAudioMode = new QButtonGroup(this);
grpAudioMode->addButton(ui->rbAudioAll, 0);
grpAudioMode->addButton(ui->rbAudioOneOnly, 1);
grpAudioMode->addButton(ui->rbAudioActiveOnly, 2);
grpAudioMode->button(Config::MPAudioMode)->setChecked(true);
ui->sbReceiveTimeout->setValue(Config::MPRecvTimeout);
}
MPSettingsDialog::~MPSettingsDialog()
{
delete ui;
}
void MPSettingsDialog::done(int r)
{
if (r == QDialog::Accepted)
{
Config::MPAudioMode = grpAudioMode->checkedId();
Config::MPRecvTimeout = ui->sbReceiveTimeout->value();
Config::Save();
}
QDialog::done(r);
closeDlg();
}
//

View File

@ -0,0 +1,65 @@
/*
Copyright 2016-2022 melonDS team
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef MPSETTINGSDIALOG_H
#define MPSETTINGSDIALOG_H
#include <QDialog>
#include <QButtonGroup>
namespace Ui { class MPSettingsDialog; }
class MPSettingsDialog;
class MPSettingsDialog : public QDialog
{
Q_OBJECT
public:
explicit MPSettingsDialog(QWidget* parent);
~MPSettingsDialog();
static MPSettingsDialog* currentDlg;
static MPSettingsDialog* openDlg(QWidget* parent)
{
if (currentDlg)
{
currentDlg->activateWindow();
return currentDlg;
}
currentDlg = new MPSettingsDialog(parent);
currentDlg->open();
return currentDlg;
}
static void closeDlg()
{
currentDlg = nullptr;
}
private slots:
void done(int r);
//
private:
Ui::MPSettingsDialog* ui;
QButtonGroup* grpAudioMode;
};
#endif // MPSETTINGSDIALOG_H

View File

@ -0,0 +1,142 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MPSettingsDialog</class>
<widget class="QDialog" name="MPSettingsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>466</width>
<height>202</height>
</rect>
</property>
<property name="windowTitle">
<string>Multiplayer settings - melonDS</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Audio output</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="0">
<widget class="QRadioButton" name="rbAudioOneOnly">
<property name="text">
<string>Instance 1 only</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QRadioButton" name="rbAudioAll">
<property name="text">
<string>All instances</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QRadioButton" name="rbAudioActiveOnly">
<property name="text">
<string>Active instance only</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Network</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QSpinBox" name="sbReceiveTimeout">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="maximum">
<number>1000</number>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Data reception timeout: </string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>milliseconds</string>
</property>
</widget>
</item>
</layout>
</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>MPSettingsDialog</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>MPSettingsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -146,7 +146,7 @@ void LayoutText(const char* text, u32* width, u32* height, int* breaks)
u32 w = 0;
u32 h = 14;
u32 totalw = 0;
u32 maxw = mainWindow->panel->width() - (kOSDMargin*2);
u32 maxw = mainWindow->panelWidget->width() - (kOSDMargin*2);
int lastbreak = -1;
int numbrk = 0;
u16* ptr;
@ -236,7 +236,7 @@ void RenderText(u32 color, const char* text, Item* item)
memset(item->Bitmap, 0, w*h*sizeof(u32));
u32 x = 0, y = 1;
u32 maxw = mainWindow->panel->width() - (kOSDMargin*2);
u32 maxw = mainWindow->panelWidget->width() - (kOSDMargin*2);
int curline = 0;
u16* ptr;

View File

@ -22,6 +22,7 @@
#include "types.h"
#include "Config.h"
#include "Platform.h"
#include "PathSettingsDialog.h"
#include "ui_PathSettingsDialog.h"
@ -43,6 +44,12 @@ PathSettingsDialog::PathSettingsDialog(QWidget* parent) : QDialog(parent), ui(ne
ui->txtSaveFilePath->setText(QString::fromStdString(Config::SaveFilePath));
ui->txtSavestatePath->setText(QString::fromStdString(Config::SavestatePath));
ui->txtCheatFilePath->setText(QString::fromStdString(Config::CheatFilePath));
int inst = Platform::InstanceID();
if (inst > 0)
ui->lblInstanceNum->setText(QString("Configuring paths for instance %1").arg(inst+1));
else
ui->lblInstanceNum->hide();
}
PathSettingsDialog::~PathSettingsDialog()

View File

@ -7,49 +7,63 @@
<x>0</x>
<y>0</y>
<width>439</width>
<height>166</height>
<height>185</height>
</rect>
</property>
<property name="windowTitle">
<string>Path settings - melonDS</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QLineEdit" name="txtSaveFilePath">
<property name="clearButtonEnabled">
<bool>true</bool>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Cheat files path:</string>
</property>
</widget>
</item>
<item row="2" column="2">
<item row="3" column="2">
<widget class="QPushButton" name="btnCheatFileBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="1" column="0">
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Savestates path:</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="3">
<widget class="QLabel" name="label_5">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="txtSavestatePath">
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="btnSaveFileBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="txtCheatFilePath">
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0" colspan="3">
<widget class="QLabel" name="label">
<property name="text">
<string>Leave a path blank to use the current ROM's path.</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="3">
<item row="6" column="0" colspan="3">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@ -59,35 +73,14 @@
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Cheat files path:</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="btnSaveFileBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="1" column="2">
<item row="2" column="2">
<widget class="QPushButton" name="btnSavestateBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="txtSavestatePath">
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Save files path:</string>
@ -95,9 +88,23 @@
</widget>
</item>
<item row="4" column="0" colspan="3">
<widget class="QLabel" name="label_5">
<widget class="QLabel" name="label">
<property name="text">
<string/>
<string>Leave a path blank to use the current ROM's path.</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="txtSaveFilePath">
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0" colspan="3">
<widget class="QLabel" name="lblInstanceNum">
<property name="text">
<string>Configuring paths for instance X</string>
</property>
</widget>
</item>

View File

@ -20,28 +20,7 @@
#include <stdlib.h>
#include <string.h>
#ifdef __WIN32__
#define NTDDI_VERSION 0x06000000 // GROSS FUCKING HACK
#include <winsock2.h>
#include <windows.h>
//#include <knownfolders.h> // FUCK THAT SHIT
#include <shlobj.h>
#include <ws2tcpip.h>
#include <io.h>
#define dup _dup
#define socket_t SOCKET
#define sockaddr_t SOCKADDR
#else
#include <unistd.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <sys/socket.h>
#define socket_t int
#define sockaddr_t struct sockaddr
#define closesocket close
#endif
#include <string>
#include <QStandardPaths>
#include <QString>
#include <QDir>
@ -49,6 +28,7 @@
#include <QSemaphore>
#include <QMutex>
#include <QOpenGLContext>
#include <QSharedMemory>
#include "Platform.h"
#include "Config.h"
@ -56,11 +36,7 @@
#include "CameraManager.h"
#include "LAN_Socket.h"
#include "LAN_PCap.h"
#include <string>
#ifndef INVALID_SOCKET
#define INVALID_SOCKET (socket_t)-1
#endif
#include "LocalMP.h"
std::string EmuDirectory;
@ -73,11 +49,63 @@ extern CameraManager* camManager[2];
namespace Platform
{
socket_t MPSocket;
sockaddr_t MPSendAddr;
u8 PacketBuffer[2048];
QSharedMemory* IPCBuffer = nullptr;
int IPCInstanceID;
#define NIFI_VER 1
void IPCInit()
{
IPCInstanceID = 0;
IPCBuffer = new QSharedMemory("melonIPC");
if (!IPCBuffer->attach())
{
printf("IPC sharedmem doesn't exist. creating\n");
if (!IPCBuffer->create(1024))
{
printf("IPC sharedmem create failed :(\n");
delete IPCBuffer;
IPCBuffer = nullptr;
return;
}
IPCBuffer->lock();
memset(IPCBuffer->data(), 0, IPCBuffer->size());
IPCBuffer->unlock();
}
IPCBuffer->lock();
u8* data = (u8*)IPCBuffer->data();
u16 mask = *(u16*)&data[0];
for (int i = 0; i < 16; i++)
{
if (!(mask & (1<<i)))
{
IPCInstanceID = i;
*(u16*)&data[0] |= (1<<i);
break;
}
}
IPCBuffer->unlock();
printf("IPC: instance ID %d\n", IPCInstanceID);
}
void IPCDeInit()
{
if (IPCBuffer)
{
IPCBuffer->lock();
u8* data = (u8*)IPCBuffer->data();
*(u16*)&data[0] &= ~(1<<IPCInstanceID);
IPCBuffer->unlock();
IPCBuffer->detach();
delete IPCBuffer;
}
IPCBuffer = nullptr;
}
void Init(int argc, char** argv)
@ -113,10 +141,13 @@ void Init(int argc, char** argv)
confdir = config.absolutePath() + "/melonDS/";
EmuDirectory = confdir.toStdString();
#endif
IPCInit();
}
void DeInit()
{
IPCDeInit();
}
@ -126,6 +157,22 @@ void StopEmu()
}
int InstanceID()
{
return IPCInstanceID;
}
std::string InstanceFileSuffix()
{
int inst = IPCInstanceID;
if (inst == 0) return "";
char suffix[16] = {0};
snprintf(suffix, 15, ".%d", inst+1);
return suffix;
}
int GetConfigInt(ConfigEntry entry)
{
const int imgsizes[] = {0, 256, 512, 1024, 2048, 4096};
@ -172,7 +219,6 @@ bool GetConfigBool(ConfigEntry entry)
case DSiSD_ReadOnly: return Config::DSiSDReadOnly != 0;
case DSiSD_FolderSync: return Config::DSiSDFolderSync != 0;
case Firm_RandomizeMAC: return Config::RandomizeMAC != 0;
case Firm_OverrideSettings: return Config::FirmwareOverrideSettings != 0;
}
@ -394,146 +440,60 @@ void WriteGBASave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen
}
bool MP_Init()
{
int opt_true = 1;
int res;
#ifdef __WIN32__
WSADATA wsadata;
if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
{
return false;
}
#endif // __WIN32__
MPSocket = socket(AF_INET, SOCK_DGRAM, 0);
if (MPSocket < 0)
{
return false;
}
res = setsockopt(MPSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt_true, sizeof(int));
if (res < 0)
{
closesocket(MPSocket);
MPSocket = INVALID_SOCKET;
return false;
}
#if defined(BSD) || defined(__APPLE__)
res = setsockopt(MPSocket, SOL_SOCKET, SO_REUSEPORT, (const char*)&opt_true, sizeof(int));
if (res < 0)
{
closesocket(MPSocket);
MPSocket = INVALID_SOCKET;
return false;
}
#endif
sockaddr_t saddr;
saddr.sa_family = AF_INET;
*(u32*)&saddr.sa_data[2] = htonl(Config::SocketBindAnyAddr ? INADDR_ANY : INADDR_LOOPBACK);
*(u16*)&saddr.sa_data[0] = htons(7064);
res = bind(MPSocket, &saddr, sizeof(sockaddr_t));
if (res < 0)
{
closesocket(MPSocket);
MPSocket = INVALID_SOCKET;
return false;
}
res = setsockopt(MPSocket, SOL_SOCKET, SO_BROADCAST, (const char*)&opt_true, sizeof(int));
if (res < 0)
{
closesocket(MPSocket);
MPSocket = INVALID_SOCKET;
return false;
}
MPSendAddr.sa_family = AF_INET;
*(u32*)&MPSendAddr.sa_data[2] = htonl(INADDR_BROADCAST);
*(u16*)&MPSendAddr.sa_data[0] = htons(7064);
return true;
return LocalMP::Init();
}
void MP_DeInit()
{
if (MPSocket >= 0)
closesocket(MPSocket);
#ifdef __WIN32__
WSACleanup();
#endif // __WIN32__
return LocalMP::DeInit();
}
int MP_SendPacket(u8* data, int len)
void MP_Begin()
{
if (MPSocket < 0)
return 0;
if (len > 2048-8)
{
printf("MP_SendPacket: error: packet too long (%d)\n", len);
return 0;
}
*(u32*)&PacketBuffer[0] = htonl(0x4946494E); // NIFI
PacketBuffer[4] = NIFI_VER;
PacketBuffer[5] = 0;
*(u16*)&PacketBuffer[6] = htons(len);
memcpy(&PacketBuffer[8], data, len);
int slen = sendto(MPSocket, (const char*)PacketBuffer, len+8, 0, &MPSendAddr, sizeof(sockaddr_t));
if (slen < 8) return 0;
return slen - 8;
return LocalMP::Begin();
}
int MP_RecvPacket(u8* data, bool block)
void MP_End()
{
if (MPSocket < 0)
return 0;
return LocalMP::End();
}
fd_set fd;
struct timeval tv;
int MP_SendPacket(u8* data, int len, u64 timestamp)
{
return LocalMP::SendPacket(data, len, timestamp);
}
FD_ZERO(&fd);
FD_SET(MPSocket, &fd);
tv.tv_sec = 0;
tv.tv_usec = block ? 5000 : 0;
int MP_RecvPacket(u8* data, u64* timestamp)
{
return LocalMP::RecvPacket(data, timestamp);
}
if (!select(MPSocket+1, &fd, 0, 0, &tv))
{
return 0;
}
int MP_SendCmd(u8* data, int len, u64 timestamp)
{
return LocalMP::SendCmd(data, len, timestamp);
}
sockaddr_t fromAddr;
socklen_t fromLen = sizeof(sockaddr_t);
int rlen = recvfrom(MPSocket, (char*)PacketBuffer, 2048, 0, &fromAddr, &fromLen);
if (rlen < 8+24)
{
return 0;
}
rlen -= 8;
int MP_SendReply(u8* data, int len, u64 timestamp, u16 aid)
{
return LocalMP::SendReply(data, len, timestamp, aid);
}
if (ntohl(*(u32*)&PacketBuffer[0]) != 0x4946494E)
{
return 0;
}
int MP_SendAck(u8* data, int len, u64 timestamp)
{
return LocalMP::SendAck(data, len, timestamp);
}
if (PacketBuffer[4] != NIFI_VER)
{
return 0;
}
int MP_RecvHostPacket(u8* data, u64* timestamp)
{
return LocalMP::RecvHostPacket(data, timestamp);
}
if (ntohs(*(u16*)&PacketBuffer[6]) != rlen)
{
return 0;
}
memcpy(data, &PacketBuffer[8], rlen);
return rlen;
u16 MP_RecvReplies(u8* data, u64 timestamp, u16 aidmask)
{
return LocalMP::RecvReplies(data, timestamp, aidmask);
}

View File

@ -23,6 +23,7 @@
#include "DSi_I2C.h"
#include "NDS.h"
#include "Config.h"
#include "Platform.h"
#include "types.h"
@ -65,6 +66,12 @@ PowerManagementDialog::PowerManagementDialog(QWidget* parent) : QDialog(parent),
}
ui->sliderDSiBatteryLevel->setValue(dsiBatterySliderPos);
int inst = Platform::InstanceID();
if (inst > 0)
ui->lblInstanceNum->setText(QString("Setting battery levels for instance %1").arg(inst+1));
else
ui->lblInstanceNum->hide();
inited = true;
}

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>562</width>
<height>279</height>
<height>288</height>
</rect>
</property>
<property name="sizePolicy">
@ -23,37 +23,7 @@
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item row="0" column="0">
<widget class="QGroupBox" name="grpDSBattery">
<property name="title">
<string>DS Battery</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="1">
<widget class="QRadioButton" name="rbDSBatteryLow">
<property name="text">
<string>Low</string>
</property>
</widget>
</item>
<item row="0" column="0" rowspan="2">
<widget class="QLabel" name="label">
<property name="text">
<string>Battery Level</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QRadioButton" name="rbDSBatteryOkay">
<property name="text">
<string>Okay</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<item row="4" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@ -63,7 +33,7 @@
</property>
</widget>
</item>
<item row="2" column="0">
<item row="3" column="0">
<widget class="QGroupBox" name="grpDSiBattery">
<property name="title">
<string>DSi Battery</string>
@ -219,6 +189,49 @@
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="grpDSBattery">
<property name="title">
<string>DS Battery</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="1">
<widget class="QRadioButton" name="rbDSBatteryLow">
<property name="text">
<string>Low</string>
</property>
</widget>
</item>
<item row="0" column="0" rowspan="2">
<widget class="QLabel" name="label">
<property name="text">
<string>Battery Level</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QRadioButton" name="rbDSBatteryOkay">
<property name="text">
<string>Okay</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="lblInstanceNum">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Configuring settings for instance X</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources>

View File

@ -75,12 +75,12 @@ ROMInfoDialog::ROMInfoDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ROMI
ui->iconTitle->setText(QString::fromUtf16(NDSCart::Banner.EnglishTitle));
ui->japaneseTitle->setText(QString::fromUtf16(NDSCart::Banner.JapaneseTitle, 128));
ui->englishTitle->setText(QString::fromUtf16(NDSCart::Banner.EnglishTitle, 128));
ui->frenchTitle->setText(QString::fromUtf16(NDSCart::Banner.FrenchTitle, 128));
ui->germanTitle->setText(QString::fromUtf16(NDSCart::Banner.GermanTitle, 128));
ui->italianTitle->setText(QString::fromUtf16(NDSCart::Banner.ItalianTitle, 128));
ui->spanishTitle->setText(QString::fromUtf16(NDSCart::Banner.SpanishTitle, 128));
ui->japaneseTitle->setText(QString::fromUtf16(NDSCart::Banner.JapaneseTitle));
ui->englishTitle->setText(QString::fromUtf16(NDSCart::Banner.EnglishTitle));
ui->frenchTitle->setText(QString::fromUtf16(NDSCart::Banner.FrenchTitle));
ui->germanTitle->setText(QString::fromUtf16(NDSCart::Banner.GermanTitle));
ui->italianTitle->setText(QString::fromUtf16(NDSCart::Banner.ItalianTitle));
ui->spanishTitle->setText(QString::fromUtf16(NDSCart::Banner.SpanishTitle));
if (NDSCart::Banner.Version > 1)
ui->chineseTitle->setText(QString::fromUtf16(NDSCart::Banner.ChineseTitle));

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>427</width>
<height>434</height>
<width>559</width>
<height>532</height>
</rect>
</property>
<property name="sizePolicy">
@ -22,12 +22,6 @@
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<widget class="QGroupBox" name="titles">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Titles</string>
</property>
@ -350,12 +344,6 @@
</item>
<item row="3" column="1">
<widget class="QGroupBox" name="filesystem">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Filesystem</string>
</property>
@ -441,12 +429,6 @@
</item>
<item row="3" column="0">
<widget class="QGroupBox" name="generalInfo">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>General info</string>
</property>
@ -668,7 +650,7 @@
</layout>
</widget>
</item>
<item row="0" column="3">
<item row="0" column="2">
<widget class="QGroupBox" name="dsiIconBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
@ -742,43 +724,11 @@
</layout>
</widget>
</item>
<item row="0" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>55</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="4">
<item row="0" column="3">
<spacer name="horizontalSpacer_3">
<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="2">
<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="1" column="1">
@ -788,6 +738,13 @@
</property>
</widget>
</item>
<item row="0" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</spacer>
</item>
</layout>
</widget>
</item>

View File

@ -326,6 +326,7 @@ bool LoadState(std::string filename)
std::string savefile = filename.substr(LastSep(filename)+1);
savefile = GetAssetPath(false, Config::SaveFilePath, ".sav", savefile);
savefile += Platform::InstanceFileSuffix();
NDSSave->SetPath(savefile, true);
}
@ -350,6 +351,7 @@ bool SaveState(std::string filename)
{
std::string savefile = filename.substr(LastSep(filename)+1);
savefile = GetAssetPath(false, Config::SaveFilePath, ".sav", savefile);
savefile += Platform::InstanceFileSuffix();
NDSSave->SetPath(savefile, false);
}
@ -432,6 +434,7 @@ void Reset()
{
std::string oldsave = NDSSave->GetPath();
std::string newsave = GetAssetPath(false, Config::SaveFilePath, ".sav");
newsave += Platform::InstanceFileSuffix();
if (oldsave != newsave)
NDSSave->SetPath(newsave, false);
}
@ -440,6 +443,7 @@ void Reset()
{
std::string oldsave = GBASave->GetPath();
std::string newsave = GetAssetPath(true, Config::SaveFilePath, ".sav");
newsave += Platform::InstanceFileSuffix();
if (oldsave != newsave)
GBASave->SetPath(newsave, false);
}
@ -562,7 +566,11 @@ bool LoadROM(QStringList filepath, bool reset)
u8* savedata = nullptr;
std::string savname = GetAssetPath(false, Config::SaveFilePath, ".sav");
std::string origsav = savname;
savname += Platform::InstanceFileSuffix();
FILE* sav = Platform::OpenFile(savname, "rb", true);
if (!sav) sav = Platform::OpenFile(origsav, "rb", true);
if (sav)
{
fseek(sav, 0, SEEK_END);
@ -711,7 +719,11 @@ bool LoadGBAROM(QStringList filepath)
u8* savedata = nullptr;
std::string savname = GetAssetPath(true, Config::SaveFilePath, ".sav");
std::string origsav = savname;
savname += Platform::InstanceFileSuffix();
FILE* sav = Platform::OpenFile(savname, "rb", true);
if (!sav) sav = Platform::OpenFile(origsav, "rb", true);
if (sav)
{
fseek(sav, 0, SEEK_END);

View File

@ -31,7 +31,7 @@
#include "ui_TitleImportDialog.h"
FILE* TitleManagerDialog::curNAND = nullptr;
bool TitleManagerDialog::NANDInited = false;
TitleManagerDialog* TitleManagerDialog::currentDlg = nullptr;
extern std::string EmuDirectory;
@ -136,6 +136,8 @@ void TitleManagerDialog::createTitleItem(u32 category, u32 titleid)
bool TitleManagerDialog::openNAND()
{
NANDInited = false;
FILE* bios7i = Platform::OpenLocalFile(Config::DSiBIOS7Path, "rb");
if (!bios7i)
return false;
@ -145,28 +147,21 @@ bool TitleManagerDialog::openNAND()
fread(es_keyY, 16, 1, bios7i);
fclose(bios7i);
curNAND = Platform::OpenLocalFile(Config::DSiNANDPath, "r+b");
if (!curNAND)
return false;
if (!DSi_NAND::Init(curNAND, es_keyY))
if (!DSi_NAND::Init(es_keyY))
{
fclose(curNAND);
curNAND = nullptr;
return false;
}
NANDInited = true;
return true;
}
void TitleManagerDialog::closeNAND()
{
if (curNAND)
if (NANDInited)
{
DSi_NAND::DeInit();
fclose(curNAND);
curNAND = nullptr;
NANDInited = false;
}
}

View File

@ -45,7 +45,7 @@ public:
explicit TitleManagerDialog(QWidget* parent);
~TitleManagerDialog();
static FILE* curNAND;
static bool NANDInited;
static bool openNAND();
static void closeNAND();

View File

@ -50,12 +50,12 @@ WifiSettingsDialog::WifiSettingsDialog(QWidget* parent) : QDialog(parent), ui(ne
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
LAN_Socket::Init();
haspcap = LAN_PCap::Init(false);
ui->rbDirectMode->setText("Direct mode (requires " PCAP_NAME " and ethernet connection)");
ui->cbBindAnyAddr->setChecked(Config::SocketBindAnyAddr);
ui->lblAdapterMAC->setText("(none)");
ui->lblAdapterIP->setText("(none)");
int sel = 0;
for (int i = 0; i < LAN_PCap::NumAdapters; i++)
@ -88,7 +88,6 @@ void WifiSettingsDialog::done(int r)
if (r == QDialog::Accepted)
{
Config::SocketBindAnyAddr = ui->cbBindAnyAddr->isChecked();
Config::DirectLAN = ui->rbDirectMode->isChecked();
int sel = ui->cbxDirectAdapter->currentIndex();

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>572</width>
<height>273</height>
<height>217</height>
</rect>
</property>
<property name="sizePolicy">
@ -26,92 +26,10 @@
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Local</string>
<string>Network mode</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QCheckBox" name="cbBindAnyAddr">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enabling this allows (theoretically) playing local multiplayer games over a local network. It may or may not help make for a better connection in general.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Bind socket to any address</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Online</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="3" column="0" rowspan="3" colspan="2">
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Direct mode settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Network adapter:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="cbxDirectAdapter">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Selects the network adapter through which to route network traffic under direct mode.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>MAC address:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="lblAdapterMAC">
<property name="text">
<string>[PLACEHOLDER]</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>IP address:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="lblAdapterIP">
<property name="text">
<string>[PLACEHOLDER]</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QRadioButton" name="rbIndirectMode">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Indirect mode uses libslirp. It requires no extra setup and is easy to use.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -121,7 +39,7 @@
</property>
</widget>
</item>
<item row="2" column="0">
<item row="1" column="0">
<widget class="QRadioButton" name="rbDirectMode">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Direct mode directly routes network traffic to the host network. It is the most reliable, but requires an ethernet connection.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Non-direct mode uses a layer of emulation to get around this, but is more prone to problems.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -134,6 +52,69 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Direct mode settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Network adapter:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="cbxDirectAdapter">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Selects the network adapter through which to route network traffic under direct mode.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>MAC address:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="lblAdapterMAC">
<property name="text">
<string>[PLACEHOLDER]</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>IP address:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="lblAdapterIP">
<property name="text">
<string>[PLACEHOLDER]</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">

View File

@ -25,6 +25,7 @@
#include <string>
#include <algorithm>
#include <QProcess>
#include <QApplication>
#include <QMessageBox>
#include <QMenuBar>
@ -58,6 +59,7 @@
#include "AudioSettingsDialog.h"
#include "FirmwareSettingsDialog.h"
#include "PathSettingsDialog.h"
#include "MPSettingsDialog.h"
#include "WifiSettingsDialog.h"
#include "InterfaceSettingsDialog.h"
#include "ROMInfoDialog.h"
@ -78,6 +80,7 @@
#include "SPU.h"
#include "Wifi.h"
#include "Platform.h"
#include "LocalMP.h"
#include "Config.h"
#include "Savestate.h"
@ -103,6 +106,7 @@ bool videoSettingsDirty;
SDL_AudioDeviceID audioDevice;
int audioFreq;
bool audioMuted;
SDL_cond* audioSync;
SDL_mutex* audioSyncLock;
@ -115,6 +119,15 @@ s16* micWavBuffer;
CameraManager* camManager[2];
const struct { int id; float ratio; const char* label; } aspectRatios[] =
{
{ 0, 1, "4:3 (native)" },
{ 4, (5.f / 3) / (4.f / 3), "5:3 (3DS)"},
{ 1, (16.f / 9) / (4.f / 3), "16:9" },
{ 2, (21.f / 9) / (4.f / 3), "21:9" },
{ 3, 0, "window" }
};
void micCallback(void* data, Uint8* stream, int len);
@ -134,7 +147,7 @@ void audioCallback(void* data, Uint8* stream, int len)
SDL_CondSignal(audioSync);
SDL_UnlockMutex(audioSyncLock);
if (num_in < 1)
if ((num_in < 1) || audioMuted)
{
memset(stream, 0, len*sizeof(s16)*2);
return;
@ -154,6 +167,23 @@ void audioCallback(void* data, Uint8* stream, int len)
Frontend::AudioOut_Resample(buf_in, num_in, (s16*)stream, len, Config::AudioVolume);
}
void audioMute()
{
int inst = Platform::InstanceID();
audioMuted = false;
switch (Config::MPAudioMode)
{
case 1: // only instance 1
if (inst > 0) audioMuted = true;
break;
case 2: // only currently focused instance
if (!mainWindow->isActiveWindow()) audioMuted = true;
break;
}
}
void micOpen()
{
@ -326,7 +356,7 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent)
EmuPause = 0;
RunningSomething = false;
connect(this, SIGNAL(windowUpdate()), mainWindow->panel, SLOT(repaint()));
connect(this, SIGNAL(windowUpdate()), mainWindow->panelWidget, SLOT(repaint()));
connect(this, SIGNAL(windowTitleChange(QString)), mainWindow, SLOT(onTitleUpdate(QString)));
connect(this, SIGNAL(windowEmuStart()), mainWindow, SLOT(onEmuStart()));
connect(this, SIGNAL(windowEmuStop()), mainWindow, SLOT(onEmuStop()));
@ -334,7 +364,7 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent)
connect(this, SIGNAL(windowEmuReset()), mainWindow->actReset, SLOT(trigger()));
connect(this, SIGNAL(windowEmuFrameStep()), mainWindow->actFrameStep, SLOT(trigger()));
connect(this, SIGNAL(windowLimitFPSChange()), mainWindow->actLimitFramerate, SLOT(trigger()));
connect(this, SIGNAL(screenLayoutChange()), mainWindow->panel, SLOT(onScreenLayoutChanged()));
connect(this, SIGNAL(screenLayoutChange()), mainWindow->panelWidget, SLOT(onScreenLayoutChanged()));
connect(this, SIGNAL(windowFullscreenToggle()), mainWindow, SLOT(onFullscreenToggled()));
connect(this, SIGNAL(swapScreensToggle()), mainWindow->actScreenSwap, SLOT(trigger()));
@ -683,7 +713,11 @@ printf("PROULON\n");
if (winUpdateFreq < 1)
winUpdateFreq = 1;
sprintf(melontitle, "[%d/%.0f] melonDS " MELONDS_VERSION, fps, fpstarget);
int inst = Platform::InstanceID();
if (inst == 0)
sprintf(melontitle, "[%d/%.0f] melonDS " MELONDS_VERSION, fps, fpstarget);
else
sprintf(melontitle, "[%d/%.0f] melonDS (%d)", fps, fpstarget, inst+1);
changeWindowTitle(melontitle);
}
}
@ -698,7 +732,11 @@ printf("PROULON\n");
EmuStatus = EmuRunning;
sprintf(melontitle, "melonDS " MELONDS_VERSION);
int inst = Platform::InstanceID();
if (inst == 0)
sprintf(melontitle, "melonDS " MELONDS_VERSION);
else
sprintf(melontitle, "melonDS (%d)", inst+1);
changeWindowTitle(melontitle);
SDL_Delay(75);
@ -789,19 +827,39 @@ bool EmuThread::emuIsActive()
return (RunningSomething == 1);
}
ScreenHandler::ScreenHandler(QWidget* widget)
{
widget->setMouseTracking(true);
widget->setAttribute(Qt::WA_AcceptTouchEvents);
QTimer* mouseTimer = setupMouseTimer();
widget->connect(mouseTimer, &QTimer::timeout, [=] { if (Config::MouseHide) widget->setCursor(Qt::BlankCursor);});
}
ScreenHandler::~ScreenHandler()
{
mouseTimer->stop();
}
void ScreenHandler::screenSetupLayout(int w, int h)
{
int sizing = Config::ScreenSizing;
if (sizing == 3) sizing = autoScreenSizing;
float aspectRatios[] =
float aspectTop, aspectBot;
for (auto ratio : aspectRatios)
{
1.f,
(16.f/9)/(4.f/3),
(21.f/9)/(4.f/3),
((float)w/h)/(4.f/3)
};
if (ratio.id == Config::ScreenAspectTop)
aspectTop = ratio.ratio;
if (ratio.id == Config::ScreenAspectBot)
aspectBot = ratio.ratio;
}
if (aspectTop == 0)
aspectTop = (float) w / h;
if (aspectBot == 0)
aspectBot = (float) w / h;
Frontend::SetupScreenLayout(w, h,
Config::ScreenLayout,
@ -810,8 +868,8 @@ void ScreenHandler::screenSetupLayout(int w, int h)
Config::ScreenGap,
Config::IntegerScaling != 0,
Config::ScreenSwap != 0,
aspectRatios[Config::ScreenAspectTop],
aspectRatios[Config::ScreenAspectBot]);
aspectTop,
aspectBot);
numScreens = Frontend::GetScreenTransforms(screenMatrix[0], screenKind);
}
@ -819,11 +877,16 @@ void ScreenHandler::screenSetupLayout(int w, int h)
QSize ScreenHandler::screenGetMinSize(int factor = 1)
{
bool isHori = (Config::ScreenRotation == 1 || Config::ScreenRotation == 3);
int gap = Config::ScreenGap;
int gap = Config::ScreenGap * factor;
int w = 256 * factor;
int h = 192 * factor;
if (Config::ScreenSizing == 4 || Config::ScreenSizing == 5)
{
return QSize(w, h);
}
if (Config::ScreenLayout == 0) // natural
{
if (isHori)
@ -959,7 +1022,7 @@ void ScreenHandler::screenHandleTouch(QTouchEvent* event)
void ScreenHandler::showCursor()
{
mainWindow->panel->setCursor(Qt::ArrowCursor);
mainWindow->panelWidget->setCursor(Qt::ArrowCursor);
mouseTimer->start();
}
@ -973,7 +1036,7 @@ QTimer* ScreenHandler::setupMouseTimer()
return mouseTimer;
}
ScreenPanelNative::ScreenPanelNative(QWidget* parent) : QWidget(parent)
ScreenPanelNative::ScreenPanelNative(QWidget* parent) : QWidget(parent), ScreenHandler(this)
{
screen[0] = QImage(256, 192, QImage::Format_RGB32);
screen[1] = QImage(256, 192, QImage::Format_RGB32);
@ -981,17 +1044,12 @@ ScreenPanelNative::ScreenPanelNative(QWidget* parent) : QWidget(parent)
screenTrans[0].reset();
screenTrans[1].reset();
touching = false;
setAttribute(Qt::WA_AcceptTouchEvents);
OSD::Init(nullptr);
}
ScreenPanelNative::~ScreenPanelNative()
{
OSD::DeInit(nullptr);
mouseTimer->stop();
}
void ScreenPanelNative::setupScreenLayout()
@ -1090,17 +1148,11 @@ void ScreenPanelNative::onScreenLayoutChanged()
}
ScreenPanelGL::ScreenPanelGL(QWidget* parent) : QOpenGLWidget(parent)
{
touching = false;
setAttribute(Qt::WA_AcceptTouchEvents);
}
ScreenPanelGL::ScreenPanelGL(QWidget* parent) : QOpenGLWidget(parent), ScreenHandler(this)
{}
ScreenPanelGL::~ScreenPanelGL()
{
mouseTimer->stop();
makeCurrent();
OSD::DeInit(this);
@ -1356,6 +1408,9 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
setWindowTitle("melonDS " MELONDS_VERSION);
setAttribute(Qt::WA_DeleteOnClose);
setAcceptDrops(true);
setFocusPolicy(Qt::ClickFocus);
int inst = Platform::InstanceID();
QMenuBar* menubar = new QMenuBar();
{
@ -1488,19 +1543,30 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
actEnableCheats->setCheckable(true);
connect(actEnableCheats, &QAction::triggered, this, &MainWindow::onEnableCheats);
actSetupCheats = menu->addAction("Setup cheat codes");
actSetupCheats->setMenuRole(QAction::NoRole);
connect(actSetupCheats, &QAction::triggered, this, &MainWindow::onSetupCheats);
//if (inst == 0)
{
actSetupCheats = menu->addAction("Setup cheat codes");
actSetupCheats->setMenuRole(QAction::NoRole);
connect(actSetupCheats, &QAction::triggered, this, &MainWindow::onSetupCheats);
menu->addSeparator();
actROMInfo = menu->addAction("ROM info");
connect(actROMInfo, &QAction::triggered, this, &MainWindow::onROMInfo);
menu->addSeparator();
actROMInfo = menu->addAction("ROM info");
connect(actROMInfo, &QAction::triggered, this, &MainWindow::onROMInfo);
actRAMInfo = menu->addAction("RAM search");
connect(actRAMInfo, &QAction::triggered, this, &MainWindow::onRAMInfo);
actRAMInfo = menu->addAction("RAM search");
connect(actRAMInfo, &QAction::triggered, this, &MainWindow::onRAMInfo);
actTitleManager = menu->addAction("Manage DSi titles");
connect(actTitleManager, &QAction::triggered, this, &MainWindow::onOpenTitleManager);
actTitleManager = menu->addAction("Manage DSi titles");
connect(actTitleManager, &QAction::triggered, this, &MainWindow::onOpenTitleManager);
}
{
menu->addSeparator();
QMenu* submenu = menu->addMenu("Multiplayer");
actMPNewInstance = submenu->addAction("Launch new instance");
connect(actMPNewInstance, &QAction::triggered, this, &MainWindow::onMPNewInstance);
}
}
{
QMenu* menu = menubar->addMenu("Config");
@ -1509,7 +1575,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
connect(actEmuSettings, &QAction::triggered, this, &MainWindow::onOpenEmuSettings);
#ifdef __APPLE__
QAction* actPreferences = menu->addAction("Preferences...");
actPreferences = menu->addAction("Preferences...");
connect(actPreferences, &QAction::triggered, this, &MainWindow::onOpenEmuSettings);
actPreferences->setMenuRole(QAction::PreferencesRole);
#endif
@ -1526,15 +1592,18 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
actAudioSettings = menu->addAction("Audio settings");
connect(actAudioSettings, &QAction::triggered, this, &MainWindow::onOpenAudioSettings);
actMPSettings = menu->addAction("Multiplayer settings");
connect(actMPSettings, &QAction::triggered, this, &MainWindow::onOpenMPSettings);
actWifiSettings = menu->addAction("Wifi settings");
connect(actWifiSettings, &QAction::triggered, this, &MainWindow::onOpenWifiSettings);
actInterfaceSettings = menu->addAction("Interface settings");
connect(actInterfaceSettings, &QAction::triggered, this, &MainWindow::onOpenInterfaceSettings);
actFirmwareSettings = menu->addAction("Firmware settings");
connect(actFirmwareSettings, &QAction::triggered, this, &MainWindow::onOpenFirmwareSettings);
actInterfaceSettings = menu->addAction("Interface settings");
connect(actInterfaceSettings, &QAction::triggered, this, &MainWindow::onOpenInterfaceSettings);
actPathSettings = menu->addAction("Path settings");
connect(actPathSettings, &QAction::triggered, this, &MainWindow::onOpenPathSettings);
@ -1638,34 +1707,34 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
{
QMenu* submenu = menu->addMenu("Aspect ratio");
grpScreenAspectTop = new QActionGroup(submenu);
const char* aspectRatiosTop[] = {"Top 4:3 (native)", "Top 16:9", "Top 21:9", "Top window"};
for (int i = 0; i < 4; i++)
{
actScreenAspectTop[i] = submenu->addAction(QString(aspectRatiosTop[i]));
actScreenAspectTop[i]->setActionGroup(grpScreenAspectTop);
actScreenAspectTop[i]->setData(QVariant(i));
actScreenAspectTop[i]->setCheckable(true);
}
connect(grpScreenAspectTop, &QActionGroup::triggered, this, &MainWindow::onChangeScreenAspectTop);
submenu->addSeparator();
grpScreenAspectBot = new QActionGroup(submenu);
actScreenAspectTop = new QAction*[sizeof(aspectRatios) / sizeof(aspectRatios[0])];
actScreenAspectBot = new QAction*[sizeof(aspectRatios) / sizeof(aspectRatios[0])];
const char* aspectRatiosBot[] = {"Bottom 4:3 (native)", "Bottom 16:9", "Bottom 21:9", "Bottom window"};
for (int i = 0; i < 4; i++)
for (int i = 0; i < 2; i++)
{
actScreenAspectBot[i] = submenu->addAction(QString(aspectRatiosBot[i]));
actScreenAspectBot[i]->setActionGroup(grpScreenAspectBot);
actScreenAspectBot[i]->setData(QVariant(i));
actScreenAspectBot[i]->setCheckable(true);
}
QActionGroup* group = grpScreenAspectTop;
QAction** actions = actScreenAspectTop;
connect(grpScreenAspectBot, &QActionGroup::triggered, this, &MainWindow::onChangeScreenAspectBot);
if (i == 1)
{
group = grpScreenAspectBot;
submenu->addSeparator();
actions = actScreenAspectBot;
}
for (int j = 0; j < sizeof(aspectRatios) / sizeof(aspectRatios[0]); j++)
{
auto ratio = aspectRatios[j];
QString label = QString("%1 %2").arg(i ? "Bottom" : "Top", ratio.label);
actions[j] = submenu->addAction(label);
actions[j]->setActionGroup(group);
actions[j]->setData(QVariant(ratio.id));
actions[j]->setCheckable(true);
}
connect(group, &QActionGroup::triggered, this, &MainWindow::onChangeScreenAspect);
}
}
actScreenFiltering = menu->addAction("Screen filtering");
@ -1690,6 +1759,9 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
resize(Config::WindowWidth, Config::WindowHeight);
if (Config::FirmwareUsername == "Arisotura")
actMPNewInstance->setText("Fart");
#ifdef Q_OS_MAC
QPoint screenCenter = screen()->availableGeometry().center();
QRect frameGeo = frameGeometry();
@ -1756,8 +1828,13 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
actScreenSwap->setChecked(Config::ScreenSwap);
actScreenAspectTop[Config::ScreenAspectTop]->setChecked(true);
actScreenAspectBot[Config::ScreenAspectBot]->setChecked(true);
for (int i = 0; i < sizeof(aspectRatios) / sizeof(aspectRatios[0]); i++)
{
if (Config::ScreenAspectTop == aspectRatios[i].id)
actScreenAspectTop[i]->setChecked(true);
if (Config::ScreenAspectBot == aspectRatios[i].id)
actScreenAspectBot[i]->setChecked(true);
}
actScreenFiltering->setChecked(Config::ScreenFilter);
actShowOSD->setChecked(Config::ShowOSD);
@ -1765,11 +1842,18 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
actLimitFramerate->setChecked(Config::LimitFPS);
actAudioSync->setChecked(Config::AudioSync);
if (inst > 0)
{
actEmuSettings->setEnabled(false);
actVideoSettings->setEnabled(false);
actMPSettings->setEnabled(false);
actWifiSettings->setEnabled(false);
actInterfaceSettings->setEnabled(false);
const QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
for (const QCameraInfo &cameraInfo : cameras)
printf("CAMERAFAZIL: %s\n", cameraInfo.deviceName().toStdString().c_str());
#ifdef __APPLE__
actPreferences->setEnabled(false);
#endif // __APPLE__
}
}
MainWindow::~MainWindow()
@ -1780,17 +1864,13 @@ void MainWindow::createScreenPanel()
{
hasOGL = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0);
QTimer* mouseTimer;
if (hasOGL)
{
panelGL = new ScreenPanelGL(this);
ScreenPanelGL* panelGL = new ScreenPanelGL(this);
panelGL->show();
panel = panelGL;
panelGL->setMouseTracking(true);
mouseTimer = panelGL->setupMouseTimer();
connect(mouseTimer, &QTimer::timeout, [=] { if (Config::MouseHide) panelGL->setCursor(Qt::BlankCursor);});
panelWidget = panelGL;
if (!panelGL->isValid())
hasOGL = false;
@ -1807,17 +1887,14 @@ void MainWindow::createScreenPanel()
if (!hasOGL)
{
panelNative = new ScreenPanelNative(this);
ScreenPanelNative* panelNative = new ScreenPanelNative(this);
panel = panelNative;
panel->show();
panelNative->setMouseTracking(true);
mouseTimer = panelNative->setupMouseTimer();
connect(mouseTimer, &QTimer::timeout, [=] { if (Config::MouseHide) panelNative->setCursor(Qt::BlankCursor);});
panelWidget = panelNative;
panelWidget->show();
}
setCentralWidget(panel);
setCentralWidget(panelWidget);
connect(this, SIGNAL(screenLayoutChange()), panel, SLOT(onScreenLayoutChanged()));
connect(this, SIGNAL(screenLayoutChange()), panelWidget, SLOT(onScreenLayoutChanged()));
emit screenLayoutChange();
}
@ -1825,7 +1902,7 @@ QOpenGLContext* MainWindow::getOGLContext()
{
if (!hasOGL) return nullptr;
QOpenGLWidget* glpanel = (QOpenGLWidget*)panel;
QOpenGLWidget* glpanel = dynamic_cast<QOpenGLWidget*>(panel);
return glpanel->context();
}
@ -1967,6 +2044,16 @@ void MainWindow::dropEvent(QDropEvent* event)
}
}
void MainWindow::focusInEvent(QFocusEvent* event)
{
audioMute();
}
void MainWindow::focusOutEvent(QFocusEvent* event)
{
audioMute();
}
void MainWindow::onAppStateChanged(Qt::ApplicationState state)
{
if (state == Qt::ApplicationInactive)
@ -2639,6 +2726,23 @@ void MainWindow::onOpenTitleManager()
TitleManagerDialog* dlg = TitleManagerDialog::openDlg(this);
}
void MainWindow::onMPNewInstance()
{
//QProcess::startDetached(QApplication::applicationFilePath());
QProcess newinst;
newinst.setProgram(QApplication::applicationFilePath());
newinst.setArguments(QApplication::arguments().mid(1, QApplication::arguments().length()-1));
#ifdef __WIN32__
newinst.setCreateProcessArgumentsModifier([] (QProcess::CreateProcessArguments *args)
{
args->flags |= CREATE_NEW_CONSOLE;
});
#endif
newinst.startDetached();
}
void MainWindow::onOpenEmuSettings()
{
emuThread->emuPause();
@ -2779,6 +2883,22 @@ void MainWindow::onAudioSettingsFinished(int res)
micOpen();
}
void MainWindow::onOpenMPSettings()
{
emuThread->emuPause();
MPSettingsDialog* dlg = MPSettingsDialog::openDlg(this);
connect(dlg, &MPSettingsDialog::finished, this, &MainWindow::onMPSettingsFinished);
}
void MainWindow::onMPSettingsFinished(int res)
{
audioMute();
LocalMP::SetRecvTimeout(Config::MPRecvTimeout);
emuThread->emuUnpause();
}
void MainWindow::onOpenWifiSettings()
{
emuThread->emuPause();
@ -2789,12 +2909,6 @@ void MainWindow::onOpenWifiSettings()
void MainWindow::onWifiSettingsFinished(int res)
{
if (Wifi::MPInited)
{
Platform::MP_DeInit();
Platform::MP_Init();
}
Platform::LAN_DeInit();
Platform::LAN_Init();
@ -2814,10 +2928,7 @@ void MainWindow::onOpenInterfaceSettings()
void MainWindow::onUpdateMouseTimer()
{
if (hasOGL)
panelGL->mouseTimer->setInterval(Config::MouseHideSeconds*1000);
else
panelNative->mouseTimer->setInterval(Config::MouseHideSeconds*1000);
panel->mouseTimer->setInterval(Config::MouseHideSeconds*1000);
}
void MainWindow::onInterfaceSettingsFinished(int res)
@ -2833,8 +2944,8 @@ void MainWindow::onChangeSavestateSRAMReloc(bool checked)
void MainWindow::onChangeScreenSize()
{
int factor = ((QAction*)sender())->data().toInt();
QSize diff = size() - panel->size();
resize(dynamic_cast<ScreenHandler*>(panel)->screenGetMinSize(factor) + diff);
QSize diff = size() - panelWidget->size();
resize(panel->screenGetMinSize(factor) + diff);
}
void MainWindow::onChangeScreenRotation(QAction* act)
@ -2892,18 +3003,19 @@ void MainWindow::onChangeScreenSizing(QAction* act)
emit screenLayoutChange();
}
void MainWindow::onChangeScreenAspectTop(QAction* act)
void MainWindow::onChangeScreenAspect(QAction* act)
{
int aspect = act->data().toInt();
Config::ScreenAspectTop = aspect;
QActionGroup* group = act->actionGroup();
emit screenLayoutChange();
}
void MainWindow::onChangeScreenAspectBot(QAction* act)
{
int aspect = act->data().toInt();
Config::ScreenAspectBot = aspect;
if (group == grpScreenAspectTop)
{
Config::ScreenAspectTop = aspect;
}
else
{
Config::ScreenAspectBot = aspect;
}
emit screenLayoutChange();
}
@ -3006,16 +3118,10 @@ void MainWindow::onUpdateVideoSettings(bool glchange)
emuThread->emuPause();
if (hasOGL)
{
emuThread->deinitOpenGL();
delete panelGL;
}
else
{
delete panelNative;
}
delete panel;
createScreenPanel();
connect(emuThread, SIGNAL(windowUpdate()), panel, SLOT(repaint()));
connect(emuThread, SIGNAL(windowUpdate()), panelWidget, SLOT(repaint()));
if (hasOGL) emuThread->initOpenGL();
}
@ -3121,6 +3227,7 @@ int main(int argc, char** argv)
format.setSwapInterval(0);
QSurfaceFormat::setDefaultFormat(format);
audioMuted = false;
audioSync = SDL_CreateCond();
audioSyncLock = SDL_CreateMutex();
@ -3176,6 +3283,8 @@ int main(int argc, char** argv)
emuThread->start();
emuThread->emuPause();
audioMute();
QObject::connect(&melon, &QApplication::applicationStateChanged, mainWindow, &MainWindow::onAppStateChanged);
if (argc > 1)
@ -3221,7 +3330,7 @@ int CALLBACK WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int cmdsho
{
int argc = 0;
wchar_t** argv_w = CommandLineToArgvW(GetCommandLineW(), &argc);
char* nullarg = "";
char nullarg[] = {'\0'};
char** argv = new char*[argc];
for (int i = 0; i < argc; i++)
@ -3236,7 +3345,8 @@ int CALLBACK WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int cmdsho
if (argv_w) LocalFree(argv_w);
/*if (AttachConsole(ATTACH_PARENT_PROCESS))
//if (AttachConsole(ATTACH_PARENT_PROCESS))
/*if (AllocConsole())
{
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);

View File

@ -101,7 +101,8 @@ class ScreenHandler
Q_GADGET
public:
virtual ~ScreenHandler() {}
ScreenHandler(QWidget* widget);
virtual ~ScreenHandler();
QTimer* setupMouseTimer();
void updateMouseTimer();
QTimer* mouseTimer;
@ -121,7 +122,7 @@ protected:
int screenKind[Frontend::MaxScreenTransforms];
int numScreens;
bool touching;
bool touching = false;
void showCursor();
};
@ -133,7 +134,7 @@ class ScreenPanelNative : public QWidget, public ScreenHandler
public:
explicit ScreenPanelNative(QWidget* parent);
~ScreenPanelNative();
virtual ~ScreenPanelNative();
protected:
void paintEvent(QPaintEvent* event) override;
@ -163,7 +164,7 @@ class ScreenPanelGL : public QOpenGLWidget, public ScreenHandler, protected QOpe
public:
explicit ScreenPanelGL(QWidget* parent);
~ScreenPanelGL();
virtual ~ScreenPanelGL();
protected:
void initializeGL() override;
@ -225,6 +226,9 @@ protected:
void dragEnterEvent(QDragEnterEvent* event) override;
void dropEvent(QDropEvent* event) override;
void focusInEvent(QFocusEvent* event) override;
void focusOutEvent(QFocusEvent* event) override;
signals:
void screenLayoutChange();
@ -254,6 +258,7 @@ private slots:
void onROMInfo();
void onRAMInfo();
void onOpenTitleManager();
void onMPNewInstance();
void onOpenEmuSettings();
void onEmuSettingsDialogFinished(int res);
@ -267,6 +272,8 @@ private slots:
void onOpenPathSettings();
void onUpdateAudioSettings();
void onAudioSettingsFinished(int res);
void onOpenMPSettings();
void onMPSettingsFinished(int res);
void onOpenWifiSettings();
void onWifiSettingsFinished(int res);
void onFirmwareSettingsFinished(int res);
@ -281,8 +288,7 @@ private slots:
void onChangeScreenLayout(QAction* act);
void onChangeScreenSwap(bool checked);
void onChangeScreenSizing(QAction* act);
void onChangeScreenAspectTop(QAction* act);
void onChangeScreenAspectBot(QAction* act);
void onChangeScreenAspect(QAction* act);
void onChangeIntegerScaling(bool checked);
void onChangeScreenFiltering(bool checked);
void onChangeShowOSD(bool checked);
@ -318,9 +324,8 @@ private:
bool oldMax;
public:
QWidget* panel;
ScreenPanelGL* panelGL;
ScreenPanelNative* panelNative;
ScreenHandler* panel;
QWidget* panelWidget;
QAction* actOpenROM;
QAction* actBootFirmware;
@ -346,13 +351,18 @@ public:
QAction* actROMInfo;
QAction* actRAMInfo;
QAction* actTitleManager;
QAction* actMPNewInstance;
QAction* actEmuSettings;
#ifdef __APPLE__
QAction* actPreferences;
#endif
QAction* actPowerManagement;
QAction* actInputConfig;
QAction* actVideoSettings;
QAction* actCameraSettings;
QAction* actAudioSettings;
QAction* actMPSettings;
QAction* actWifiSettings;
QAction* actFirmwareSettings;
QAction* actPathSettings;
@ -370,9 +380,9 @@ public:
QAction* actScreenSizing[6];
QAction* actIntegerScaling;
QActionGroup* grpScreenAspectTop;
QAction* actScreenAspectTop[4];
QAction** actScreenAspectTop;
QActionGroup* grpScreenAspectBot;
QAction* actScreenAspectBot[4];
QAction** actScreenAspectBot;
QAction* actScreenFiltering;
QAction* actShowOSD;
QAction* actLimitFramerate;

View File

@ -0,0 +1,488 @@
/*
* s e m _ t i m e d w a i t
*
* Function:
* Implements a version of sem_timedwait().
*
* Description:
* Not all systems implement sem_timedwait(), which is a version of
* sem_wait() with a timeout. Mac OS X is one example, at least up to
* and including version 10.6 (Leopard). If such a function is needed,
* this code provides a reasonable implementation, which I think is
* compatible with the standard version, although possibly less
* efficient. It works by creating a thread that interrupts a normal
* sem_wait() call after the specified timeout.
*
* Call:
*
* The Linux man pages say:
*
* #include <semaphore.h>
*
* int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
*
* sem_timedwait() is the same as sem_wait(), except that abs_timeout
* specifies a limit on the amount of time that the call should block if
* the decrement cannot be immediately performed. The abs_timeout argument
* points to a structure that specifies an absolute timeout in seconds and
* nanoseconds since the Epoch (00:00:00, 1 January 1970). This structure
* is defined as follows:
*
* struct timespec {
* time_t tv_sec; Seconds
* long tv_nsec; Nanoseconds [0 .. 999999999]
* };
*
* If the timeout has already expired by the time of the call, and the
* semaphore could not be locked immediately, then sem_timedwait() fails
* with a timeout error (errno set to ETIMEDOUT).
* If the operation can be performed immediately, then sem_timedwait()
* never fails with a timeout error, regardless of the value of abs_timeout.
* Furthermore, the validity of abs_timeout is not checked in this case.
*
* Limitations:
*
* The mechanism used involves sending a SIGUSR2 signal to the thread
* calling sem_timedwait(). The handler for this signal is set to a null
* routine which does nothing, and with any flags for the signal
* (eg SA_RESTART) cleared. Note that this effective disabling of the
* SIGUSR2 signal is a side-effect of using this routine, and means it
* may not be a completely transparent plug-in replacement for a
* 'normal' sig_timedwait() call. Since OS X does not declare the
* sem_timedwait() call in its standard include files, the relevant
* declaration (shown above in the man pages extract) will probably have
* to be added to any code that uses this.
*
* Compiling:
* This compiles and runs cleanly on OS X (10.6) with gcc with the
* -Wall -ansi -pedantic flags. On Linux, using -ansi causes a sweep of
* compiler complaints about the timespec structure, but it compiles
* and works fine with just -Wall -pedantic. (Since Linux provides
* sem_timedwait() anyway, this really isn't needed on Linux.) However,
* since Linux provides sem_timedwait anyway, the sem_timedwait()
* code in this file is only compiled on OS X, and is a null on other
* systems.
*
* Testing:
* This file contains a test program that exercises the sem_timedwait
* code. It is compiled if the pre-processor variable TEST is defined.
* For more details, see the comments for the test routine at the end
* of the file.
*
* Author: Keith Shortridge, AAO.
*
* History:
* 8th Sep 2009. Original version. KS.
* 24th Sep 2009. Added test that the calling thread still exists before
* trying to set the timed-out flag. KS.
* 2nd Oct 2009. No longer restores the original SIGUSR2 signal handler.
* See comments in the body of the code for more details.
* Prototypes for now discontinued internal routines removed.
* 12th Aug 2010. Added the cleanup handler, so that this code no longer
* leaks resources if the calling thread is cancelled. KS.
* 21st Sep 2011. Added copyright notice below. Modified header comments
* to describe the use of SIGUSR2 more accurately in the
* light of the 2/10/09 change above. Now undefs DEBUG
* before defining it, to avoid any possible clash. KS.
* 14th Feb 2012. Tidied out a number of TABs that had got into the
* code. KS.
* 6th May 2013. Copyright notice modified to one based on the MIT licence,
* which is more permissive than the previous notice. KS.
*
* Copyright (c) Australian Astronomical Observatory (AAO), (2013).
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifdef __APPLE__
#include <semaphore.h>
#include <time.h>
#include <sys/time.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <setjmp.h>
#include "sem_timedwait.h"
/* Some useful definitions - TRUE, FALSE, and DEBUG */
#undef TRUE
#define TRUE 1
#undef FALSE
#define FALSE 0
#undef DEBUG
#define DEBUG printf
/* A structure of type timeoutDetails is passed to the thread used to
* implement the timeout.
*/
typedef struct {
struct timespec delay; /* Specifies the delay, relative to now */
pthread_t callingThread; /* The thread doing the sem_wait call */
volatile short *timedOutShort; /* Address of a flag set to indicate that
* the timeout was triggered. */
} timeoutDetails;
/* A structure of type cleanupDetails is passed to the thread cleanup
* routine which is called at the end of the routine or if the thread calling
* it is cancelled.
*/
typedef struct {
pthread_t *threadIdAddr; /* Address of the variable that holds
* the Id of the timeout thread. */
struct sigaction *sigHandlerAddr; /* Address of the old signal action
* handler. */
volatile short *timedOutShort; /* Address of a flag set to indicate that
* the timeout was triggered. */
} cleanupDetails;
/* Forward declarations of internal routines */
static void* timeoutThreadMain (void* passedPtr);
static int triggerSignal (int Signal, pthread_t Thread);
static void ignoreSignal (int Signal);
static void timeoutThreadCleanup (void* passedPtr);
/* -------------------------------------------------------------------------- */
/*
* s e m _ t i m e d w a i t
*
* This is the main code for the sem_timedwait() implementation.
*/
int sem_timedwait (
sem_t *sem,
const struct timespec *abs_timeout)
{
int result = 0; /* Code returned by this routine 0 or -1 */
/* "Under no circumstances shall the function fail if the semaphore
* can be locked immediately". So we try to get it quickly to see if we
* can avoid all the timeout overheads.
*/
if (sem_trywait(sem) == 0) {
/* Yes, got it immediately. */
result = 0;
} else {
/* No, we've got to do it with a sem_wait() call and a thread to run
* the timeout. First, work out the time from now to the specified
* timeout, which we will pass to the timeout thread in a way that can
* be used to pass to nanosleep(). So we need this in seconds and
* nanoseconds. Along the way, we check for an invalid passed time,
* and for one that's already expired.
*/
if ((abs_timeout->tv_nsec < 0) || (abs_timeout->tv_nsec > 1000000000)) {
/* Passed time is invalid */
result = -1;
errno = EINVAL;
} else {
struct timeval currentTime; /* Time now */
long secsToWait,nsecsToWait; /* Seconds and nsec to delay */
gettimeofday (&currentTime,NULL);
secsToWait = abs_timeout->tv_sec - currentTime.tv_sec;
nsecsToWait = (abs_timeout->tv_nsec - (currentTime.tv_usec * 1000));
while (nsecsToWait < 0) {
nsecsToWait += 1000000000;
secsToWait--;
}
if ((secsToWait < 0) || ((secsToWait == 0) && (nsecsToWait < 0))) {
/* Time has passed. Report an immediate timeout. */
result = -1;
errno = ETIMEDOUT;
} else {
/* We're going to have to do a sem_wait() with a timeout thread.
* The thread will wait the specified time, then will issue a
* SIGUSR2 signal that will interrupt the sem_wait() call.
* We pass the thread the id of the current thread, the delay,
* and the address of a flag to set on a timeout, so we can
* distinguish an interrupt caused by the timeout thread from
* one caused by some other signal.
*/
volatile short timedOut; /* Flag to set on timeout */
timeoutDetails details; /* All the stuff the thread must know */
struct sigaction oldSignalAction; /* Current signal setting */
pthread_t timeoutThread; /* Id of timeout thread */
cleanupDetails cleaningDetails; /* What the cleanup routine needs */
int oldCancelState; /* Previous cancellation state */
int ignoreCancelState; /* Used in call, but ignored */
int createStatus; /* Status of pthread_create() call */
/* If the current thread is cancelled (and CML does do this)
* we don't want to leave our timer thread running - if we've
* started the thread we want to make sure we join it in order
* to release its resources. So we set a cleanup handler to
* do this. We pass it the address of the structure that will
* hold all it needs to know. While we set all this up,
* we prevent ourselves being cancelled, so all this data is
* coherent.
*/
pthread_setcancelstate (PTHREAD_CANCEL_DISABLE,&oldCancelState);
timeoutThread = (pthread_t) 0;
cleaningDetails.timedOutShort = &timedOut;
cleaningDetails.threadIdAddr = &timeoutThread;
cleaningDetails.sigHandlerAddr = &oldSignalAction;
pthread_cleanup_push (timeoutThreadCleanup,&cleaningDetails);
/* Set up the details for the thread. Clear the timeout flag,
* record the current SIGUSR2 action settings so we can restore
* them later.
*/
details.delay.tv_sec = secsToWait;
details.delay.tv_nsec = nsecsToWait;
details.callingThread = pthread_self();
details.timedOutShort = &timedOut;
timedOut = FALSE;
sigaction (SIGUSR2,NULL,&oldSignalAction);
/* Start up the timeout thread. Once we've done that, we can
* restore the previous cancellation state.
*/
createStatus = pthread_create(&timeoutThread,NULL,
timeoutThreadMain, (void*)&details);
pthread_setcancelstate (oldCancelState,&ignoreCancelState);
if (createStatus < 0) {
/* Failed to create thread. errno will already be set properly */
result = -1;
} else {
/* Thread created OK. This is where we wait for the semaphore.
*/
if (sem_wait(sem) == 0) {
/* Got the semaphore OK. We return zero, and all's well. */
result = 0;
} else {
/* If we got a -1 error from sem_wait(), it may be because
* it was interrupted by a timeout, or failed for some
* other reason. We check for the expected timeout
* condition, which is an 'interrupted' status and the
* timeout flag set by the timeout thread. We report that as
* a timeout error. Anything else is some other error and
* errno is already set properly.
*/
result = -1;
if (errno == EINTR) {
if (timedOut) errno = ETIMEDOUT;
}
}
}
/* The cleanup routine - timeoutThreadCleanup() - packages up
* any tidying up that is needed, including joining with the
* timer thread. This will be called if the current thread is
* cancelled, but we need it to happen anyway, so we set the
* execute flag true here as we remove it from the list of
* cleanup routines to be called. So normally, this line amounts
* to calling timeoutThreadCleanup().
*/
pthread_cleanup_pop (TRUE);
}
}
}
return (result);
}
/* -------------------------------------------------------------------------- */
/*
* t i m e o u t T h r e a d C l e a n u p
*
* This internal routine tidies up at the end of a sem_timedwait() call.
* It is set as a cleanup routine for the current thread (not the timer
* thread) so it is executed even if the thread is cancelled. This is
* important, as we need to tidy up the timeout thread. If we took the
* semaphore (in other words, if we didn't timeout) then the timer thread
* will still be running, sitting in its nanosleep() call, and we need
* to cancel it. If the timer thread did signal a timeout then it will
* now be closing down. In either case, we need to join it (using a call
* to pthread_join()) or its resources will never be released.
* The single argument is a pointer to a cleanupDetails structure that has
* all the routine needs to know.
*/
static void timeoutThreadCleanup (void* passedPtr)
{
/* Get what we need from the structure we've been passed. */
cleanupDetails *detailsPtr = (cleanupDetails*) passedPtr;
short timedOut = *(detailsPtr->timedOutShort);
pthread_t timeoutThread = *(detailsPtr->threadIdAddr);
/* If we created the thread, stop it - doesn't matter if it's no longer
* running, pthread_cancel can handle that. We make sure we wait for it
* to complete, because it is this pthread_join() call that releases any
* memory the thread may have allocated. Note that cancelling a thread is
* generally not a good idea, because of the difficulty of cleaning up
* after it, but this is a very simple thread that does nothing but call
* nanosleep(), and that we can cancel quite happily.
*/
if (!timedOut) pthread_cancel(timeoutThread);
pthread_join(timeoutThread,NULL);
/* The code originally restored the old action handler, which generally
* was the default handler that caused the task to exit. Just occasionally,
* there seem to be cases where the signal is still queued and ready to
* trigger even though the thread that presumably sent it off just before
* it was cancelled has finished. I had thought that once we'd joined
* that thread, we could be sure of not seeing the signal, but that seems
* not to be the case, and so restoring a handler that will allow the task
* to crash is not a good idea, and so the line below has been commented
* out.
*
* sigaction (SIGUSR2,detailsPtr->sigHandlerAddr,NULL);
*/
}
/* -------------------------------------------------------------------------- */
/*
* t i m e o u t T h r e a d M a i n
*
* This internal routine is the main code for the timeout thread.
* The single argument is a pointer to a timeoutDetails structure that has
* all the thread needs to know - thread to signal, delay time, and the
* address of a flag to set if it triggers a timeout.
*/
static void* timeoutThreadMain (void* passedPtr)
{
void* Return = (void*) 0;
/* We grab all the data held in the calling thread right now. In some
* cases, we find that the calling thread has vanished and released
* its memory, including the details structure, by the time the timeout
* expires, and then we get an access violation when we try to set the
* 'timed out' flag.
*/
timeoutDetails details = *((timeoutDetails*) passedPtr);
struct timespec requestedDelay = details.delay;
/* We do a nanosleep() for the specified delay, and then trigger a
* timeout. Note that we allow for the case where the nanosleep() is
* interrupted, and restart it for the remaining time. If the
* thread that is doing the sem_wait() call gets the semaphore, it
* will cancel this thread, which is fine as we aren't doing anything
* other than a sleep and a signal.
*/
for (;;) {
struct timespec remainingDelay;
if (nanosleep (&requestedDelay,&remainingDelay) == 0) {
break;
} else if (errno == EINTR) {
requestedDelay = remainingDelay;
} else {
Return = (void*) errno;
break;
}
}
/* We've completed the delay without being cancelled, so we now trigger
* the timeout by sending a signal to the calling thread. And that's it,
* although we set the timeout flag first to indicate that it was us
* that interrupted the sem_wait() call. One precaution: before we
* try to set the timed-out flag, make sure the calling thread still
* exists - this may not be the case if things are closing down a bit
* messily. We check this quickly using a zero test signal.
*/
if (pthread_kill(details.callingThread,0) == 0) {
*(details.timedOutShort) = TRUE;
if (triggerSignal (SIGUSR2,details.callingThread) < 0) {
Return = (void*) errno;
}
}
return Return;
}
/* -------------------------------------------------------------------------- */
/*
* t r i g g e r S i g n a l
*
* This is a general purpose routine that sends a specified signal to
* a specified thread, setting up a signal handler that does nothing,
* and then giving the signal. The only effect will be to interrupt any
* operation that is currently blocking - in this case, we expect this to
* be a sem_wait() call.
*/
static int triggerSignal (int Signal, pthread_t Thread)
{
int Result = 0;
struct sigaction SignalDetails;
SignalDetails.sa_handler = ignoreSignal;
SignalDetails.sa_flags = 0;
(void) sigemptyset(&SignalDetails.sa_mask);
if ((Result = sigaction(Signal,&SignalDetails,NULL)) == 0) {
Result = pthread_kill(Thread,Signal);
}
return Result;
}
/* -------------------------------------------------------------------------- */
/*
* i g n o r e S i g n a l
*
* And this is the signal handler that does nothing. (It clears its argument,
* but this has no effect and prevents a compiler warning about an unused
* argument.)
*/
static void ignoreSignal (int Signal) {
Signal = 0;
}
#endif

View File

@ -0,0 +1,8 @@
#ifndef __SEM_TIMEDWAIT_H
#define __SEM_TIMEDWAIT_H
#ifdef __APPLE__
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
#endif
#endif