From 231f0fc2e5e22b2d4907341cc191cfc9215556fb Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 14 Apr 2020 23:38:48 +0200 Subject: welp --- src/frontend/qt_sdl/main.cpp | 45 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/frontend/qt_sdl/main.cpp (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp new file mode 100644 index 0000000..3f61ec6 --- /dev/null +++ b/src/frontend/qt_sdl/main.cpp @@ -0,0 +1,45 @@ +/* + Copyright 2016-2020 Arisotura + + 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 +#include +#include +#include + +// Qt includes and shit here, I guess + +#include "main.h" + +#include "../version.h" + + +// + + +int main(int argc, char** argv) +{ + srand(time(NULL)); + + printf("melonDS " MELONDS_VERSION "\n"); + printf(MELONDS_URL "\n"); + + printf("Arisotura hereby admits defeat\n"); + printf("NI DIEU NI MAITRE\n"); + + return 0; +} -- cgit v1.2.3 From 439ca1b2b557040f6d5a2fe1e70dd0f5e4d74484 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 25 Apr 2020 20:43:09 +0200 Subject: get a Qt window showing up. 'tis a start, I guess. --- src/frontend/qt_sdl/CMakeLists.txt | 7 +++++-- src/frontend/qt_sdl/main.cpp | 27 ++++++++++++++++++++------- src/frontend/qt_sdl/main.h | 14 +++++++++++++- 3 files changed, 38 insertions(+), 10 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index ff2ed09..1ee7629 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -9,8 +9,11 @@ if (WIN32) endif() find_package(Qt5 COMPONENTS Core REQUIRED) +find_package(Qt5 COMPONENTS Gui REQUIRED) find_package(Qt5 COMPONENTS Widgets REQUIRED) +set(CMAKE_AUTOMOC ON) + find_package(PkgConfig REQUIRED) pkg_check_modules(SDL2 REQUIRED sdl2) @@ -41,14 +44,14 @@ if (UNIX) --generate-header "${CMAKE_SOURCE_DIR}/melon_grc.xml") if (CMAKE_SYSTEM_NAME STREQUAL "Linux") - target_link_libraries(melonDS dl Qt5::Core Qt5::Widgets) + target_link_libraries(melonDS dl Qt5::Core Qt5::Gui Qt5::Widgets) endif () target_sources(melonDS PUBLIC melon_grc.c) elseif (WIN32) target_sources(melonDS PUBLIC "${CMAKE_SOURCE_DIR}/melon.rc") target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../..") - target_link_libraries(melonDS comctl32 d2d1 dwrite uxtheme ws2_32 iphlpapi gdi32 Qt5::Core Qt5::Widgets) + target_link_libraries(melonDS comctl32 d2d1 dwrite uxtheme ws2_32 iphlpapi gdi32 Qt5::Core Qt5::Gui Qt5::Widgets) endif () install(FILES ../../net.kuribo64.melonDS.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications) diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 0b9d15e..0eb84a5 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -21,15 +21,26 @@ #include #include -// Qt includes and shit here, I guess -#include +#include +#include #include "main.h" #include "../../version.h" -// +MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) +{ + setWindowTitle("melonDS - assfucking Qt version"); + + // burp + QWidget *centralWidget = new QWidget(this); + setCentralWidget(centralWidget); +} + +MainWindow::~MainWindow() +{ +} int main(int argc, char** argv) @@ -39,10 +50,12 @@ int main(int argc, char** argv) printf("melonDS " MELONDS_VERSION "\n"); printf(MELONDS_URL "\n"); - printf("Arisotura hereby admits defeat\n"); - printf("NI DIEU NI MAITRE\n"); + QApplication melon(argc, argv); + + MainWindow win; + win.show(); - return 0; + return melon.exec(); } #ifdef __WIN32__ @@ -59,7 +72,7 @@ int CALLBACK WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int cmdsho for (int i = 0; i < argc; i++) { int len = WideCharToMultiByte(CP_UTF8, 0, argv_w[i], -1, NULL, 0, NULL, NULL); - if (len < 1) return NULL; + if (len < 1) { argv[i] = nullarg; continue; } argv[i] = new char[len]; int res = WideCharToMultiByte(CP_UTF8, 0, argv_w[i], -1, argv[i], len, NULL, NULL); if (res != len) { delete[] argv[i]; argv[i] = nullarg; } diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 65d6518..ad795ef 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -19,6 +19,18 @@ #ifndef MAIN_H #define MAIN_H -// put the class shit here +#include + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget* parent = nullptr); + ~MainWindow(); + +private: + // private shit goes here +}; #endif // MAIN_H -- cgit v1.2.3 From 690f9f38744ddda7fd65c299288227767e4b03f0 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 27 Apr 2020 12:06:44 +0200 Subject: get some of the shit going, I guess atleast the emuthread is going and we have its control system down and other fun shit, too --- src/CMakeLists.txt | 1 + src/frontend/qt_sdl/CMakeLists.txt | 2 + src/frontend/qt_sdl/Platform.cpp | 558 ++++++++++++++++++++++++++++++++ src/frontend/qt_sdl/PlatformConfig.cpp | 151 +++++++++ src/frontend/qt_sdl/PlatformConfig.h | 82 +++++ src/frontend/qt_sdl/main.cpp | 570 ++++++++++++++++++++++++++++++++- src/frontend/qt_sdl/main.h | 42 ++- 7 files changed, 1397 insertions(+), 9 deletions(-) create mode 100644 src/frontend/qt_sdl/Platform.cpp create mode 100644 src/frontend/qt_sdl/PlatformConfig.cpp create mode 100644 src/frontend/qt_sdl/PlatformConfig.h (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5537e6d..64d922c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,6 +21,7 @@ add_library(core STATIC NDS.cpp NDSCart.cpp OpenGLSupport.cpp + Platform.h RTC.cpp Savestate.cpp SPI.cpp diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 1ee7629..f24464d 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -2,6 +2,8 @@ project(qt_sdl) SET(SOURCES_QT_SDL main.cpp + Platform.cpp + PlatformConfig.cpp ) if (WIN32) diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp new file mode 100644 index 0000000..31b5277 --- /dev/null +++ b/src/frontend/qt_sdl/Platform.cpp @@ -0,0 +1,558 @@ +/* + Copyright 2016-2020 Arisotura + + 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 +#include +#include +#include +#include "Platform.h" +#include "PlatformConfig.h" +//#include "LAN_Socket.h" +//#include "LAN_PCap.h" +#include + +#ifdef __WIN32__ + #define NTDDI_VERSION 0x06000000 // GROSS FUCKING HACK + #include + //#include // FUCK THAT SHIT + extern "C" const GUID DECLSPEC_SELECTANY FOLDERID_RoamingAppData = {0x3eb685db, 0x65f9, 0x4cf6, {0xa0, 0x3a, 0xe3, 0xef, 0x65, 0x72, 0x9f, 0x3d}}; + #include + #include + #include + #define socket_t SOCKET + #define sockaddr_t SOCKADDR +#else + #include + #include + #include + #include + #include + #include + #define socket_t int + #define sockaddr_t struct sockaddr + #define closesocket close +#endif + +#ifndef INVALID_SOCKET +#define INVALID_SOCKET (socket_t)-1 +#endif + + +extern char* EmuDirectory; + +void Stop(bool internal); + + +namespace Platform +{ + + +typedef struct +{ + SDL_Thread* ID; + void (*Func)(); + +} ThreadData; + +int ThreadEntry(void* data) +{ + ThreadData* thread = (ThreadData*)data; + thread->Func(); + return 0; +} + + +socket_t MPSocket; +sockaddr_t MPSendAddr; +u8 PacketBuffer[2048]; + +#define NIFI_VER 1 + + +void StopEmu() +{ + //Stop(true); +} + + +FILE* OpenFile(const char* path, const char* mode, bool mustexist) +{ + FILE* ret; + +#ifdef __WIN32__ + + int len = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0); + if (len < 1) return NULL; + WCHAR* fatpath = new WCHAR[len]; + int res = MultiByteToWideChar(CP_UTF8, 0, path, -1, fatpath, len); + if (res != len) { delete[] fatpath; return NULL; } // checkme? + + // this will be more than enough + WCHAR fatmode[4]; + fatmode[0] = mode[0]; + fatmode[1] = mode[1]; + fatmode[2] = mode[2]; + fatmode[3] = 0; + + if (mustexist) + { + ret = _wfopen(fatpath, L"rb"); + if (ret) ret = _wfreopen(fatpath, fatmode, ret); + } + else + ret = _wfopen(fatpath, fatmode); + + delete[] fatpath; + +#else + + if (mustexist) + { + ret = fopen(path, "rb"); + if (ret) ret = freopen(path, mode, ret); + } + else + ret = fopen(path, mode); + +#endif + + return ret; +} + +#if !defined(UNIX_PORTABLE) && !defined(__WIN32__) + +FILE* OpenLocalFile(const char* path, const char* mode) +{ + std::string fullpath; + if (path[0] == '/') + { + // If it's an absolute path, just open that. + fullpath = std::string(path); + } + else + { + // Check user configuration directory + std::string confpath = std::string(g_get_user_config_dir()) + "/melonDS/"; + g_mkdir_with_parents(confpath.c_str(), 0755); + fullpath = confpath + path; + } + + return OpenFile(fullpath.c_str(), mode, mode[0] != 'w'); +} + +FILE* OpenDataFile(const char* path) +{ + const char* melondir = "melonDS"; + const char* const* sys_dirs = g_get_system_data_dirs(); + const char* user_dir = g_get_user_data_dir(); + + // First check the user's data directory + char* fullpath = g_build_path("/", user_dir, melondir, path, NULL); + if (access(fullpath, R_OK) == 0) + { + FILE* f = fopen(fullpath, "r"); + g_free(fullpath); + return f; + } + free(fullpath); + + // Then check the system data directories + for (size_t i = 0; sys_dirs[i] != NULL; i++) + { + const char* dir = sys_dirs[i]; + char* fullpath = g_build_path("/", dir, melondir, path, NULL); + + if (access(fullpath, R_OK) == 0) + { + FILE* f = fopen(fullpath, "r"); + g_free(fullpath); + return f; + } + free(fullpath); + } + + FILE* f = fopen(path, "rb"); + if (f) return f; + + return NULL; +} + +#else + +FILE* OpenLocalFile(const char* path, const char* mode) +{ + bool relpath = false; + int pathlen = strlen(path); + +#ifdef __WIN32__ + if (pathlen > 3) + { + if (path[1] == ':' && path[2] == '\\') + return OpenFile(path, mode); + } +#else + if (pathlen > 1) + { + if (path[0] == '/') + return OpenFile(path, mode); + } +#endif + + if (pathlen >= 3) + { + if (path[0] == '.' && path[1] == '.' && (path[2] == '/' || path[2] == '\\')) + relpath = true; + } + + int emudirlen = strlen(EmuDirectory); + char* emudirpath; + if (emudirlen) + { + int len = emudirlen + 1 + pathlen + 1; + emudirpath = new char[len]; + strncpy(&emudirpath[0], EmuDirectory, emudirlen); + emudirpath[emudirlen] = '/'; + strncpy(&emudirpath[emudirlen+1], path, pathlen); + emudirpath[emudirlen+1+pathlen] = '\0'; + } + else + { + emudirpath = new char[pathlen+1]; + strncpy(&emudirpath[0], path, pathlen); + emudirpath[pathlen] = '\0'; + } + + // Locations are application directory, and AppData/melonDS on Windows or XDG_CONFIG_HOME/melonDS on Linux + + FILE* f; + + // First check current working directory + f = OpenFile(path, mode, true); + if (f) { delete[] emudirpath; return f; } + + // then emu directory + f = OpenFile(emudirpath, mode, true); + if (f) { delete[] emudirpath; return f; } + +#ifdef __WIN32__ + + // a path relative to AppData wouldn't make much sense + if (!relpath) + { + // Now check AppData + PWSTR appDataPath = NULL; + SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, NULL, &appDataPath); + if (!appDataPath) + { + delete[] emudirpath; + return NULL; + } + + // this will be more than enough + WCHAR fatperm[4]; + fatperm[0] = mode[0]; + fatperm[1] = mode[1]; + fatperm[2] = mode[2]; + fatperm[3] = 0; + + int fnlen = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0); + if (fnlen < 1) { delete[] emudirpath; return NULL; } + WCHAR* wfileName = new WCHAR[fnlen]; + int res = MultiByteToWideChar(CP_UTF8, 0, path, -1, wfileName, fnlen); + if (res != fnlen) { delete[] wfileName; delete[] emudirpath; return NULL; } // checkme? + + const WCHAR* appdir = L"\\melonDS\\"; + + int pos = wcslen(appDataPath); + void* ptr = CoTaskMemRealloc(appDataPath, (pos+wcslen(appdir)+fnlen+1)*sizeof(WCHAR)); + if (!ptr) { delete[] wfileName; delete[] emudirpath; return NULL; } // oh well + appDataPath = (PWSTR)ptr; + + wcscpy(&appDataPath[pos], appdir); pos += wcslen(appdir); + wcscpy(&appDataPath[pos], wfileName); + + f = _wfopen(appDataPath, L"rb"); + if (f) f = _wfreopen(appDataPath, fatperm, f); + CoTaskMemFree(appDataPath); + delete[] wfileName; + if (f) { delete[] emudirpath; return f; } + } + +#else + + if (!relpath) + { + // Now check XDG_CONFIG_HOME + // TODO: check for memory leak there + std::string fullpath = std::string(g_get_user_config_dir()) + "/melonDS/" + path; + f = OpenFile(fullpath.c_str(), mode, true); + if (f) { delete[] emudirpath; return f; } + } + +#endif + + if (mode[0] != 'r') + { + f = OpenFile(emudirpath, mode); + if (f) { delete[] emudirpath; return f; } + } + + delete[] emudirpath; + return NULL; +} + +FILE* OpenDataFile(const char* path) +{ + return OpenLocalFile(path, "rb"); +} + +#endif + + +void* Thread_Create(void (*func)()) +{ + ThreadData* data = new ThreadData; + data->Func = func; + data->ID = SDL_CreateThread(ThreadEntry, "melonDS core thread", data); + return data; +} + +void Thread_Free(void* thread) +{ + delete (ThreadData*)thread; +} + +void Thread_Wait(void* thread) +{ + SDL_WaitThread((SDL_Thread*)((ThreadData*)thread)->ID, NULL); +} + + +void* Semaphore_Create() +{ + return SDL_CreateSemaphore(0); +} + +void Semaphore_Free(void* sema) +{ + SDL_DestroySemaphore((SDL_sem*)sema); +} + +void Semaphore_Reset(void* sema) +{ + while (SDL_SemTryWait((SDL_sem*)sema) == 0); +} + +void Semaphore_Wait(void* sema) +{ + SDL_SemWait((SDL_sem*)sema); +} + +void Semaphore_Post(void* sema) +{ + SDL_SemPost((SDL_sem*)sema); +} + + +void* GL_GetProcAddress(const char* proc) +{ + return NULL;//uiGLGetProcAddress(proc); +} + + +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; + } + + 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; +} + +void MP_DeInit() +{ + if (MPSocket >= 0) + closesocket(MPSocket); + +#ifdef __WIN32__ + WSACleanup(); +#endif // __WIN32__ +} + +int MP_SendPacket(u8* data, int len) +{ + 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; +} + +int MP_RecvPacket(u8* data, bool block) +{ + if (MPSocket < 0) + return 0; + + fd_set fd; + struct timeval tv; + + FD_ZERO(&fd); + FD_SET(MPSocket, &fd); + tv.tv_sec = 0; + tv.tv_usec = block ? 5000 : 0; + + if (!select(MPSocket+1, &fd, 0, 0, &tv)) + { + return 0; + } + + 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; + + if (ntohl(*(u32*)&PacketBuffer[0]) != 0x4946494E) + { + return 0; + } + + if (PacketBuffer[4] != NIFI_VER) + { + return 0; + } + + if (ntohs(*(u16*)&PacketBuffer[6]) != rlen) + { + return 0; + } + + memcpy(data, &PacketBuffer[8], rlen); + return rlen; +} + + + +bool LAN_Init() +{ + /*if (Config::DirectLAN) + { + if (!LAN_PCap::Init(true)) + return false; + } + else + { + if (!LAN_Socket::Init()) + return false; + }*/ + + return true; +} + +void LAN_DeInit() +{ + // checkme. blarg + //if (Config::DirectLAN) + // LAN_PCap::DeInit(); + //else + // LAN_Socket::DeInit(); + /*LAN_PCap::DeInit(); + LAN_Socket::DeInit();*/ +} + +int LAN_SendPacket(u8* data, int len) +{ + /*if (Config::DirectLAN) + return LAN_PCap::SendPacket(data, len); + else + return LAN_Socket::SendPacket(data, len);*/ + return 0; +} + +int LAN_RecvPacket(u8* data) +{ + /*if (Config::DirectLAN) + return LAN_PCap::RecvPacket(data); + else + return LAN_Socket::RecvPacket(data);*/ + return 0; +} + + +} diff --git a/src/frontend/qt_sdl/PlatformConfig.cpp b/src/frontend/qt_sdl/PlatformConfig.cpp new file mode 100644 index 0000000..f78b195 --- /dev/null +++ b/src/frontend/qt_sdl/PlatformConfig.cpp @@ -0,0 +1,151 @@ +/* + Copyright 2016-2020 Arisotura + + 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 +#include +#include +#include "PlatformConfig.h" + +namespace Config +{ + +int KeyMapping[12]; +int JoyMapping[12]; + +int HKKeyMapping[HK_MAX]; +int HKJoyMapping[HK_MAX]; + +int JoystickID; + +int WindowWidth; +int WindowHeight; +int WindowMaximized; + +int ScreenRotation; +int ScreenGap; +int ScreenLayout; +int ScreenSizing; +int ScreenFilter; + +int ScreenUseGL; +int ScreenVSync; +int ScreenRatio; + +int LimitFPS; +int AudioSync; +int ShowOSD; + +int DirectBoot; + +int SocketBindAnyAddr; +char LANDevice[128]; +int DirectLAN; + +int SavestateRelocSRAM; + +int AudioVolume; +int MicInputType; +char MicWavPath[512]; + +char LastROMFolder[512]; + + +ConfigEntry PlatformConfigFile[] = +{ + {"Key_A", 0, &KeyMapping[0], 32, NULL, 0}, + {"Key_B", 0, &KeyMapping[1], 31, NULL, 0}, + {"Key_Select", 0, &KeyMapping[2], 57, NULL, 0}, + {"Key_Start", 0, &KeyMapping[3], 28, NULL, 0}, + {"Key_Right", 0, &KeyMapping[4], 333, NULL, 0}, + {"Key_Left", 0, &KeyMapping[5], 331, NULL, 0}, + {"Key_Up", 0, &KeyMapping[6], 328, NULL, 0}, + {"Key_Down", 0, &KeyMapping[7], 336, NULL, 0}, + {"Key_R", 0, &KeyMapping[8], 54, NULL, 0}, + {"Key_L", 0, &KeyMapping[9], 86, NULL, 0}, + {"Key_X", 0, &KeyMapping[10], 17, NULL, 0}, + {"Key_Y", 0, &KeyMapping[11], 30, NULL, 0}, + + {"Joy_A", 0, &JoyMapping[0], -1, NULL, 0}, + {"Joy_B", 0, &JoyMapping[1], -1, NULL, 0}, + {"Joy_Select", 0, &JoyMapping[2], -1, NULL, 0}, + {"Joy_Start", 0, &JoyMapping[3], -1, NULL, 0}, + {"Joy_Right", 0, &JoyMapping[4], -1, NULL, 0}, + {"Joy_Left", 0, &JoyMapping[5], -1, NULL, 0}, + {"Joy_Up", 0, &JoyMapping[6], -1, NULL, 0}, + {"Joy_Down", 0, &JoyMapping[7], -1, NULL, 0}, + {"Joy_R", 0, &JoyMapping[8], -1, NULL, 0}, + {"Joy_L", 0, &JoyMapping[9], -1, NULL, 0}, + {"Joy_X", 0, &JoyMapping[10], -1, NULL, 0}, + {"Joy_Y", 0, &JoyMapping[11], -1, NULL, 0}, + + {"HKKey_Lid", 0, &HKKeyMapping[HK_Lid], 0x0D, NULL, 0}, + {"HKKey_Mic", 0, &HKKeyMapping[HK_Mic], 0x35, NULL, 0}, + {"HKKey_Pause", 0, &HKKeyMapping[HK_Pause], -1, NULL, 0}, + {"HKKey_Reset", 0, &HKKeyMapping[HK_Reset], -1, NULL, 0}, + {"HKKey_FastForward", 0, &HKKeyMapping[HK_FastForward], 0x0F, NULL, 0}, + {"HKKey_FastForwardToggle", 0, &HKKeyMapping[HK_FastForwardToggle], -1, NULL, 0}, + {"HKKey_SolarSensorDecrease", 0, &HKKeyMapping[HK_SolarSensorDecrease], 0x4B, NULL, 0}, + {"HKKey_SolarSensorIncrease", 0, &HKKeyMapping[HK_SolarSensorIncrease], 0x4D, NULL, 0}, + + {"HKJoy_Lid", 0, &HKJoyMapping[HK_Lid], -1, NULL, 0}, + {"HKJoy_Mic", 0, &HKJoyMapping[HK_Mic], -1, NULL, 0}, + {"HKJoy_Pause", 0, &HKJoyMapping[HK_Pause], -1, NULL, 0}, + {"HKJoy_Reset", 0, &HKJoyMapping[HK_Reset], -1, NULL, 0}, + {"HKJoy_FastForward", 0, &HKJoyMapping[HK_FastForward], -1, NULL, 0}, + {"HKJoy_FastForwardToggle", 0, &HKJoyMapping[HK_FastForwardToggle], -1, NULL, 0}, + {"HKJoy_SolarSensorDecrease", 0, &HKJoyMapping[HK_SolarSensorDecrease], -1, NULL, 0}, + {"HKJoy_SolarSensorIncrease", 0, &HKJoyMapping[HK_SolarSensorIncrease], -1, NULL, 0}, + + {"JoystickID", 0, &JoystickID, 0, NULL, 0}, + + {"WindowWidth", 0, &WindowWidth, 256, NULL, 0}, + {"WindowHeight", 0, &WindowHeight, 384, NULL, 0}, + {"WindowMax", 0, &WindowMaximized, 0, NULL, 0}, + + {"ScreenRotation", 0, &ScreenRotation, 0, NULL, 0}, + {"ScreenGap", 0, &ScreenGap, 0, NULL, 0}, + {"ScreenLayout", 0, &ScreenLayout, 0, NULL, 0}, + {"ScreenSizing", 0, &ScreenSizing, 0, NULL, 0}, + {"ScreenFilter", 0, &ScreenFilter, 1, NULL, 0}, + + {"ScreenUseGL", 0, &ScreenUseGL, 1, NULL, 0}, + {"ScreenVSync", 0, &ScreenVSync, 0, NULL, 0}, + {"ScreenRatio", 0, &ScreenRatio, 0, NULL, 0}, + + {"LimitFPS", 0, &LimitFPS, 0, NULL, 0}, + {"AudioSync", 0, &AudioSync, 1, NULL, 0}, + {"ShowOSD", 0, &ShowOSD, 1, NULL, 0}, + + {"DirectBoot", 0, &DirectBoot, 1, NULL, 0}, + + {"SockBindAnyAddr", 0, &SocketBindAnyAddr, 0, NULL, 0}, + {"LANDevice", 1, LANDevice, 0, "", 127}, + {"DirectLAN", 0, &DirectLAN, 0, NULL, 0}, + + {"SavStaRelocSRAM", 0, &SavestateRelocSRAM, 0, NULL, 0}, + + {"AudioVolume", 0, &AudioVolume, 256, NULL, 0}, + {"MicInputType", 0, &MicInputType, 1, NULL, 0}, + {"MicWavPath", 1, MicWavPath, 0, "", 511}, + + {"LastROMFolder", 1, LastROMFolder, 0, "", 511}, + + {"", -1, NULL, 0, NULL, 0} +}; + +} diff --git a/src/frontend/qt_sdl/PlatformConfig.h b/src/frontend/qt_sdl/PlatformConfig.h new file mode 100644 index 0000000..d0f765b --- /dev/null +++ b/src/frontend/qt_sdl/PlatformConfig.h @@ -0,0 +1,82 @@ +/* + Copyright 2016-2020 Arisotura + + 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 PLATFORMCONFIG_H +#define PLATFORMCONFIG_H + +#include "Config.h" + +enum +{ + HK_Lid = 0, + HK_Mic, + HK_Pause, + HK_Reset, + HK_FastForward, + HK_FastForwardToggle, + HK_SolarSensorDecrease, + HK_SolarSensorIncrease, + HK_MAX +}; + +namespace Config +{ + +extern int KeyMapping[12]; +extern int JoyMapping[12]; + +extern int HKKeyMapping[HK_MAX]; +extern int HKJoyMapping[HK_MAX]; + +extern int JoystickID; + +extern int WindowWidth; +extern int WindowHeight; +extern int WindowMaximized; + +extern int ScreenRotation; +extern int ScreenGap; +extern int ScreenLayout; +extern int ScreenSizing; +extern int ScreenFilter; + +extern int ScreenUseGL; +extern int ScreenVSync; +extern int ScreenRatio; + +extern int LimitFPS; +extern int AudioSync; +extern int ShowOSD; + +extern int DirectBoot; + +extern int SocketBindAnyAddr; +extern char LANDevice[128]; +extern int DirectLAN; + +extern int SavestateRelocSRAM; + +extern int AudioVolume; +extern int MicInputType; +extern char MicWavPath[512]; + +extern char LastROMFolder[512]; + +} + +#endif // PLATFORMCONFIG_H diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 0eb84a5..9192685 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -22,20 +22,364 @@ #include #include -#include +#include +#include +#include +#include +#include + +#include #include "main.h" -#include "../../version.h" +#include "types.h" +#include "version.h" + +#include "NDS.h" +#include "GBACart.h" +#include "GPU.h" +#include "SPU.h" +#include "Wifi.h" +#include "Platform.h" +#include "Config.h" + +#include "Savestate.h" + + +char* EmuDirectory; + +bool RunningSomething; +char ROMPath[2][1024]; +char SRAMPath[2][1024]; +char PrevSRAMPath[2][1024]; // for savestate 'undo load' + +bool SavestateLoaded; + +MainWindow* mainWindow; +EmuThread* emuThread; + + +EmuThread::EmuThread(QObject* parent) : QThread(parent) +{ + EmuStatus = 0; + EmuRunning = 2; +} + +void EmuThread::run() +{ + NDS::Init(); + + /*MainScreenPos[0] = 0; + MainScreenPos[1] = 0; + MainScreenPos[2] = 0; + AutoScreenSizing = 0;*/ + + /*if (Screen_UseGL) + { + uiGLMakeContextCurrent(GLContext); + GPU3D::InitRenderer(true); + uiGLMakeContextCurrent(NULL); + } + else*/ + { + GPU3D::InitRenderer(false); + } + + /*Touching = false; + KeyInputMask = 0xFFF; + JoyInputMask = 0xFFF; + KeyHotkeyMask = 0; + JoyHotkeyMask = 0; + HotkeyMask = 0; + LastHotkeyMask = 0; + LidStatus = false;*/ + + u32 nframes = 0; + u32 starttick = SDL_GetTicks(); + u32 lasttick = starttick; + u32 lastmeasuretick = lasttick; + u32 fpslimitcount = 0; + u64 perfcount = SDL_GetPerformanceCounter(); + u64 perffreq = SDL_GetPerformanceFrequency(); + float samplesleft = 0; + u32 nsamples = 0; + + char melontitle[100]; + SDL_mutex* titlemutex = SDL_CreateMutex(); + void* titledata[2] = {melontitle, titlemutex}; +printf("emu thread start: %d\n", EmuRunning); + while (EmuRunning != 0) + { + /*ProcessInput(); + + if (HotkeyPressed(HK_FastForwardToggle)) + { + Config::LimitFPS = !Config::LimitFPS; + uiQueueMain(UpdateFPSLimit, NULL); + } + // TODO: similar hotkeys for video/audio sync? + + if (HotkeyPressed(HK_Pause)) uiQueueMain(TogglePause, NULL); + if (HotkeyPressed(HK_Reset)) uiQueueMain(Reset, NULL); + + if (GBACart::CartInserted && GBACart::HasSolarSensor) + { + if (HotkeyPressed(HK_SolarSensorDecrease)) + { + if (GBACart_SolarSensor::LightLevel > 0) GBACart_SolarSensor::LightLevel--; + char msg[64]; + sprintf(msg, "Solar sensor level set to %d", GBACart_SolarSensor::LightLevel); + OSD::AddMessage(0, msg); + } + if (HotkeyPressed(HK_SolarSensorIncrease)) + { + if (GBACart_SolarSensor::LightLevel < 10) GBACart_SolarSensor::LightLevel++; + char msg[64]; + sprintf(msg, "Solar sensor level set to %d", GBACart_SolarSensor::LightLevel); + OSD::AddMessage(0, msg); + } + }*/ + + if (EmuRunning == 1) + { + EmuStatus = 1; + + // process input and hotkeys + NDS::SetKeyMask(0xFFF); + /*NDS::SetKeyMask(KeyInputMask & JoyInputMask); + + if (HotkeyPressed(HK_Lid)) + { + LidStatus = !LidStatus; + NDS::SetLidClosed(LidStatus); + OSD::AddMessage(0, LidStatus ? "Lid closed" : "Lid opened"); + }*/ + + // microphone input + /*FeedMicInput(); + + if (Screen_UseGL) + { + uiGLBegin(GLContext); + uiGLMakeContextCurrent(GLContext); + }*/ + + // auto screen layout + /*{ + MainScreenPos[2] = MainScreenPos[1]; + MainScreenPos[1] = MainScreenPos[0]; + MainScreenPos[0] = NDS::PowerControl9 >> 15; + + int guess; + if (MainScreenPos[0] == MainScreenPos[2] && + MainScreenPos[0] != MainScreenPos[1]) + { + // constant flickering, likely displaying 3D on both screens + // TODO: when both screens are used for 2D only...??? + guess = 0; + } + else + { + if (MainScreenPos[0] == 1) + guess = 1; + else + guess = 2; + } + + if (guess != AutoScreenSizing) + { + AutoScreenSizing = guess; + SetupScreenRects(WindowWidth, WindowHeight); + } + }*/ + + // emulate + u32 nlines = NDS::RunFrame(); + +#ifdef MELONCAP + MelonCap::Update(); +#endif // MELONCAP + + if (EmuRunning == 0) break; + + /*if (Screen_UseGL) + { + GLScreen_DrawScreen(); + uiGLEnd(GLContext); + } + uiAreaQueueRedrawAll(MainDrawArea);*/ + + /*bool fastforward = HotkeyDown(HK_FastForward); + + if (Config::AudioSync && !fastforward) + { + SDL_LockMutex(AudioSyncLock); + while (SPU::GetOutputSize() > 1024) + { + int ret = SDL_CondWaitTimeout(AudioSync, AudioSyncLock, 500); + if (ret == SDL_MUTEX_TIMEDOUT) break; + } + SDL_UnlockMutex(AudioSyncLock); + } + + float framerate = (1000.0f * nlines) / (60.0f * 263.0f); + + { + u32 curtick = SDL_GetTicks(); + u32 delay = curtick - lasttick; + + bool limitfps = Config::LimitFPS && !fastforward; + if (limitfps) + { + float wantedtickF = starttick + (framerate * (fpslimitcount+1)); + u32 wantedtick = (u32)ceil(wantedtickF); + if (curtick < wantedtick) SDL_Delay(wantedtick - curtick); + + lasttick = SDL_GetTicks(); + fpslimitcount++; + if ((abs(wantedtickF - (float)wantedtick) < 0.001312) || (fpslimitcount > 60)) + { + fpslimitcount = 0; + nsamples = 0; + starttick = lasttick; + } + } + else + { + if (delay < 1) SDL_Delay(1); + lasttick = SDL_GetTicks(); + } + } + + nframes++; + if (nframes >= 30) + { + u32 tick = SDL_GetTicks(); + u32 diff = tick - lastmeasuretick; + lastmeasuretick = tick; + + u32 fps; + if (diff < 1) fps = 77777; + else fps = (nframes * 1000) / diff; + nframes = 0; + + float fpstarget; + if (framerate < 1) fpstarget = 999; + else fpstarget = 1000.0f/framerate; + + SDL_LockMutex(titlemutex); + sprintf(melontitle, "[%d/%.0f] melonDS " MELONDS_VERSION, fps, fpstarget); + SDL_UnlockMutex(titlemutex); + uiQueueMain(UpdateWindowTitle, titledata); + }*/ + } + else + { + // paused + nframes = 0; + lasttick = SDL_GetTicks(); + starttick = lasttick; + lastmeasuretick = lasttick; + fpslimitcount = 0; + + if (EmuRunning == 2) + { + /*if (Screen_UseGL) + { + uiGLBegin(GLContext); + uiGLMakeContextCurrent(GLContext); + GLScreen_DrawScreen(); + uiGLEnd(GLContext); + } + uiAreaQueueRedrawAll(MainDrawArea);*/ + } + + //if (Screen_UseGL) uiGLMakeContextCurrent(NULL); + + EmuStatus = EmuRunning; + + SDL_Delay(100); + } + printf("ran iteration: status=%d run=%d\n", EmuStatus, EmuRunning); + } + + EmuStatus = 0; + + SDL_DestroyMutex(titlemutex); + + //if (Screen_UseGL) uiGLMakeContextCurrent(GLContext); + + NDS::DeInit(); + //Platform::LAN_DeInit(); + + /*if (Screen_UseGL) + { + OSD::DeInit(true); + GLScreen_DeInit(); + } + else + OSD::DeInit(false);*/ + + //if (Screen_UseGL) uiGLMakeContextCurrent(NULL); +} + +void EmuThread::emuRun() +{ + EmuRunning = 1; +} + +void EmuThread::emuPause(bool refresh) +{ + int status = refresh ? 2:3; + PrevEmuStatus = EmuRunning; + EmuRunning = status;printf("emuPause %d -> %d %d\n", PrevEmuStatus, EmuRunning, EmuStatus); + while (EmuStatus != status);printf("wait done\n"); +} + +void EmuThread::emuUnpause() +{ + EmuRunning = PrevEmuStatus; +} + +void EmuThread::emuStop() +{ + EmuRunning = 0; +} + + +MainWindowPanel::MainWindowPanel(QWidget* parent) : QWidget(parent) +{ +} + +MainWindowPanel::~MainWindowPanel() +{ +} + +void MainWindowPanel::paintEvent(QPaintEvent* event) +{ + QPainter painter(this); + + //painter. +} MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) { setWindowTitle("melonDS - assfucking Qt version"); - // burp - QWidget *centralWidget = new QWidget(this); - setCentralWidget(centralWidget); + QMenuBar* menubar = new QMenuBar(); + { + QMenu* menu = menubar->addMenu("File"); + QAction* act; + + act = menu->addAction("Open file..."); + connect(act, &QAction::triggered, this, &MainWindow::onOpenFile); + } + setMenuBar(menubar); + + panel = new MainWindowPanel(this); + setCentralWidget(panel); + panel->setMinimumSize(256, 384); } MainWindow::~MainWindow() @@ -43,6 +387,13 @@ MainWindow::~MainWindow() } +void MainWindow::onOpenFile() +{ + QString filename = QFileDialog::getOpenFileName(this, "Open ROM", "", "DS ROMs (*.nds *.srl);;Any file (*.*)"); + printf("fark: %p %d %s\n", filename, filename.isEmpty(), filename.toStdString().c_str()); +} + + int main(int argc, char** argv) { srand(time(NULL)); @@ -50,12 +401,212 @@ int main(int argc, char** argv) printf("melonDS " MELONDS_VERSION "\n"); printf(MELONDS_URL "\n"); +#if defined(__WIN32__) || defined(UNIX_PORTABLE) + if (argc > 0 && strlen(argv[0]) > 0) + { + int len = strlen(argv[0]); + while (len > 0) + { + if (argv[0][len] == '/') break; + if (argv[0][len] == '\\') break; + len--; + } + if (len > 0) + { + EmuDirectory = new char[len+1]; + strncpy(EmuDirectory, argv[0], len); + EmuDirectory[len] = '\0'; + } + else + { + EmuDirectory = new char[2]; + strcpy(EmuDirectory, "."); + } + } + else + { + EmuDirectory = new char[2]; + strcpy(EmuDirectory, "."); + } +#else + const char* confdir = g_get_user_config_dir(); + const char* confname = "/melonDS"; + EmuDirectory = new char[strlen(confdir) + strlen(confname) + 1]; + strcat(EmuDirectory, confdir); + strcat(EmuDirectory, confname); +#endif + QApplication melon(argc, argv); - MainWindow win; - win.show(); + // http://stackoverflow.com/questions/14543333/joystick-wont-work-using-sdl + SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); + + if (SDL_Init(SDL_INIT_HAPTIC) < 0) + { + printf("SDL couldn't init rumble\n"); + } + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0) + { + QMessageBox::critical(NULL, "melonDS", "SDL shat itself :("); + return 1; + } + + SDL_JoystickEventState(SDL_ENABLE); + + Config::Load(); + + //if (Config::AudioVolume < 0) Config::AudioVolume = 0; + //else if (Config::AudioVolume > 256) Config::AudioVolume = 256; - return melon.exec(); + // TODO: those should be checked before running anything + // (as to let the user specify their own BIOS/firmware path etc) +#if 0 + if (!Platform::LocalFileExists("bios7.bin") || + !Platform::LocalFileExists("bios9.bin") || + !Platform::LocalFileExists("firmware.bin")) + { +#if defined(__WIN32__) || defined(UNIX_PORTABLE) + const char* locationName = "the directory you run melonDS from"; +#else + char* locationName = EmuDirectory; +#endif + char msgboxtext[512]; + sprintf(msgboxtext, + "One or more of the following required files don't exist or couldn't be accessed:\n\n" + "bios7.bin -- ARM7 BIOS\n" + "bios9.bin -- ARM9 BIOS\n" + "firmware.bin -- firmware image\n\n" + "Dump the files from your DS and place them in %s.\n" + "Make sure that the files can be accessed.", + locationName + ); + + uiMsgBoxError(NULL, "BIOS/Firmware not found", msgboxtext); + + uiUninit(); + SDL_Quit(); + return 0; + } + if (!Platform::LocalFileExists("firmware.bin.bak")) + { + // verify the firmware + // + // there are dumps of an old hacked firmware floating around on the internet + // and those are problematic + // the hack predates WFC, and, due to this, any game that alters the WFC + // access point data will brick that firmware due to it having critical + // data in the same area. it has the same problem on hardware. + // + // but this should help stop users from reporting that issue over and over + // again, when the issue is not from melonDS but from their firmware dump. + // + // I don't know about all the firmware hacks in existence, but the one I + // looked at has 0x180 bytes from the header repeated at 0x3FC80, but + // bytes 0x0C-0x14 are different. + + FILE* f = Platform::OpenLocalFile("firmware.bin", "rb"); + u8 chk1[0x180], chk2[0x180]; + + fseek(f, 0, SEEK_SET); + fread(chk1, 1, 0x180, f); + fseek(f, -0x380, SEEK_END); + fread(chk2, 1, 0x180, f); + + memset(&chk1[0x0C], 0, 8); + memset(&chk2[0x0C], 0, 8); + + fclose(f); + + if (!memcmp(chk1, chk2, 0x180)) + { + uiMsgBoxError(NULL, + "Problematic firmware dump", + "You are using an old hacked firmware dump.\n" + "Firmware boot will stop working if you run any game that alters WFC settings.\n\n" + "Note that the issue is not from melonDS, it would also happen on an actual DS."); + } + } + { + const char* romlist_missing = "Save memory type detection will not work correctly.\n\n" + "You should use the latest version of romlist.bin (provided in melonDS release packages)."; +#if !defined(UNIX_PORTABLE) && !defined(__WIN32__) + std::string missingstr = std::string(romlist_missing) + + "\n\nThe ROM list should be placed in " + g_get_user_data_dir() + "/melonds/, otherwise " + "melonDS will search for it in the current working directory."; + const char* romlist_missing_text = missingstr.c_str(); +#else + const char* romlist_missing_text = romlist_missing; +#endif + + FILE* f = Platform::OpenDataFile("romlist.bin"); + if (f) + { + u32 data; + fread(&data, 4, 1, f); + fclose(f); + + if ((data >> 24) == 0) // old CRC-based list + { + uiMsgBoxError(NULL, "Your version of romlist.bin is outdated.", romlist_missing_text); + } + } + else + { + uiMsgBoxError(NULL, "romlist.bin not found.", romlist_missing_text); + } + } +#endif + + mainWindow = new MainWindow(); + mainWindow->show(); + + emuThread = new EmuThread(); + emuThread->start(); + emuThread->emuPause(true); + + if (argc > 1) + { + char* file = argv[1]; + char* ext = &file[strlen(file)-3]; + + if (!strcasecmp(ext, "nds") || !strcasecmp(ext, "srl")) + { + strncpy(ROMPath[0], file, 1023); + ROMPath[0][1023] = '\0'; + + //SetupSRAMPath(0); + + //if (NDS::LoadROM(ROMPath[0], SRAMPath[0], Config::DirectBoot)) + // Run(); + } + + if (argc > 2) + { + file = argv[2]; + ext = &file[strlen(file)-3]; + + if (!strcasecmp(ext, "gba")) + { + strncpy(ROMPath[1], file, 1023); + ROMPath[1][1023] = '\0'; + + //SetupSRAMPath(1); + + //NDS::LoadGBAROM(ROMPath[1], SRAMPath[1]); + } + } + } + + int ret = melon.exec(); +printf("melon over\n"); + emuThread->emuStop();printf("STOP\n"); + emuThread->wait();printf("farked\n"); + + Config::Save(); + + SDL_Quit(); + delete[] EmuDirectory; + return ret; } #ifdef __WIN32__ @@ -71,6 +622,7 @@ int CALLBACK WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int cmdsho char** argv = new char*[argc]; for (int i = 0; i < argc; i++) { + if (!argv_w) { argv[i] = nullarg; continue; } int len = WideCharToMultiByte(CP_UTF8, 0, argv_w[i], -1, NULL, 0, NULL, NULL); if (len < 1) { argv[i] = nullarg; continue; } argv[i] = new char[len]; @@ -78,6 +630,8 @@ int CALLBACK WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int cmdsho if (res != len) { delete[] argv[i]; argv[i] = nullarg; } } + if (argv_w) LocalFree(argv_w); + if (AttachConsole(ATTACH_PARENT_PROCESS)) { freopen("CONOUT$", "w", stdout); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index ad795ef..92b1846 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -19,8 +19,45 @@ #ifndef MAIN_H #define MAIN_H +#include +#include #include + +class EmuThread : public QThread +{ + Q_OBJECT + void run() override; + +public: + explicit EmuThread(QObject* parent = nullptr); + + // to be called from the UI thread + void emuRun(); + void emuPause(bool refresh); + void emuUnpause(); + void emuStop(); + +private: + volatile int EmuStatus; + int PrevEmuStatus; + int EmuRunning; +}; + + +class MainWindowPanel : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowPanel(QWidget* parent); + ~MainWindowPanel(); + +protected: + void paintEvent(QPaintEvent* event) override; +}; + + class MainWindow : public QMainWindow { Q_OBJECT @@ -29,8 +66,11 @@ public: explicit MainWindow(QWidget* parent = nullptr); ~MainWindow(); +private slots: + void onOpenFile(); + private: - // private shit goes here + MainWindowPanel* panel; }; #endif // MAIN_H -- cgit v1.2.3 From d9c55a4f1f269de2fd17fe214403f13a9cf6f341 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 27 Apr 2020 15:59:52 +0200 Subject: fix dumb include path shit. --- src/frontend/qt_sdl/CMakeLists.txt | 2 +- src/frontend/qt_sdl/main.cpp | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index f24464d..baa4be2 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -21,6 +21,7 @@ pkg_check_modules(SDL2 REQUIRED sdl2) add_executable(melonDS ${SOURCES_QT_SDL}) target_include_directories(melonDS PRIVATE ${SDL2_INCLUDE_DIRS}) +target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../..") target_link_libraries(melonDS core ${SDL2_LIBRARIES}) if (UNIX) @@ -52,7 +53,6 @@ if (UNIX) target_sources(melonDS PUBLIC melon_grc.c) elseif (WIN32) target_sources(melonDS PUBLIC "${CMAKE_SOURCE_DIR}/melon.rc") - target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../..") target_link_libraries(melonDS comctl32 d2d1 dwrite uxtheme ws2_32 iphlpapi gdi32 Qt5::Core Qt5::Gui Qt5::Widgets) endif () diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 9192685..4999d94 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -208,6 +208,7 @@ printf("emu thread start: %d\n", EmuRunning); uiGLEnd(GLContext); } uiAreaQueueRedrawAll(MainDrawArea);*/ + mainWindow->update(); /*bool fastforward = HotkeyDown(HK_FastForward); @@ -292,6 +293,7 @@ printf("emu thread start: %d\n", EmuRunning); uiGLEnd(GLContext); } uiAreaQueueRedrawAll(MainDrawArea);*/ + mainWindow->update(); } //if (Screen_UseGL) uiGLMakeContextCurrent(NULL); @@ -359,7 +361,10 @@ void MainWindowPanel::paintEvent(QPaintEvent* event) { QPainter painter(this); - //painter. + // fill background + painter.fillRect(event->rect(), QColor::fromRgb(0, 0, 0)); + + painter.fillRect(0, 0, 256, 192, QColor::fromRgb(0, 255, 255)); } -- cgit v1.2.3 From 931da1c66fb50138109f8bf4cb157db8f1e79c64 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 27 Apr 2020 22:02:45 +0200 Subject: add a bunch of code --- src/CMakeLists.txt | 2 + src/Config.h | 2 + src/frontend/FrontendUtil.h | 38 ++++++- src/frontend/Util_ROM.cpp | 236 +++++++++++++++++++++++++++++++++++++++++++ src/frontend/qt_sdl/main.cpp | 19 ++-- 5 files changed, 284 insertions(+), 13 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 64d922c..dca0ca9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -26,6 +26,8 @@ add_library(core STATIC Savestate.cpp SPI.cpp SPU.cpp + types.h + version.h Wifi.cpp WifiAP.cpp ) diff --git a/src/Config.h b/src/Config.h index 84fd57b..0aeab85 100644 --- a/src/Config.h +++ b/src/Config.h @@ -19,6 +19,8 @@ #ifndef CONFIG_H #define CONFIG_H +#include + #include "types.h" namespace Config diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index 6a199b7..7e171ce 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -21,10 +21,44 @@ #include "types.h" -namespace FrontendUtil +namespace Frontend { -// +enum +{ + ROMSlot_NDS = 0, + ROMSlot_GBA, + + ROMSlot_MAX +}; + +extern char ROMPath [ROMSlot_MAX][1024]; +extern char SRAMPath[ROMSlot_MAX][1024]; +extern bool SavestateLoaded; + + +// initialize the ROM handling utility +void Init_ROM(); + +// load a ROM file to the specified cart slot +// note: loading a ROM to the NDS slot resets emulation +bool LoadROM(char* file, int slot); + +// get the filename associated with the given savestate slot +void GetSavestateName(int slot, char* filename, int len); + +// determine whether the given savestate slot does contain a savestate +bool SavestateExists(int slot); + +// load the given savestate file +// if successful, emulation will continue from the savestate's point +bool LoadState(const char* filename); + +// save the current emulator state to the given file +bool SaveState(const char* filename); + +// undo the latest savestate load +void UndoStateLoad(); } diff --git a/src/frontend/Util_ROM.cpp b/src/frontend/Util_ROM.cpp index b3077f7..574148b 100644 --- a/src/frontend/Util_ROM.cpp +++ b/src/frontend/Util_ROM.cpp @@ -15,3 +15,239 @@ You should have received a copy of the GNU General Public License along with melonDS. If not, see http://www.gnu.org/licenses/. */ + +#include +#include + +#include "FrontendUtil.h" +#include "Config.h" +#include "qt_sdl/PlatformConfig.h" // FIXME!!! +#include "Platform.h" + +#include "NDS.h" +#include "GBACart.h" + + +namespace Frontend +{ + +char ROMPath [ROMSlot_MAX][1024]; +char SRAMPath [ROMSlot_MAX][1024]; +char PrevSRAMPath[ROMSlot_MAX][1024]; // for savestate 'undo load' + +bool SavestateLoaded; + + +void Init_ROM() +{ + SavestateLoaded = false; + + memset(ROMPath[ROMSlot_NDS], 0, 1024); + memset(ROMPath[ROMSlot_GBA], 0, 1024); + memset(SRAMPath[ROMSlot_NDS], 0, 1024); + memset(SRAMPath[ROMSlot_GBA], 0, 1024); + memset(PrevSRAMPath[ROMSlot_NDS], 0, 1024); + memset(PrevSRAMPath[ROMSlot_GBA], 0, 1024); +} + +void SetupSRAMPath(int slot) +{ + strncpy(SRAMPath[slot], ROMPath[slot], 1023); + SRAMPath[slot][1023] = '\0'; + strncpy(SRAMPath[slot] + strlen(ROMPath[slot]) - 3, "sav", 3); +} + +bool LoadROM(char* file, int slot) +{ + char oldpath[1024]; + char oldsram[1024]; + strncpy(oldpath, ROMPath[slot], 1024); + strncpy(oldsram, SRAMPath[slot], 1024); + + strncpy(ROMPath[slot], file, 1023); + ROMPath[slot][1023] = '\0'; + + SetupSRAMPath(0); + SetupSRAMPath(1); + + if (slot == ROMSlot_NDS && NDS::LoadROM(ROMPath[slot], SRAMPath[slot], Config::DirectBoot)) + { + SavestateLoaded = false; + + // Reload the inserted GBA cartridge (if any) + if (ROMPath[ROMSlot_GBA][0] != '\0') NDS::LoadGBAROM(ROMPath[ROMSlot_GBA], SRAMPath[ROMSlot_GBA]); + + strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety + return true; + } + else if (slot == ROMSlot_GBA && NDS::LoadGBAROM(ROMPath[slot], SRAMPath[slot])) + { + SavestateLoaded = false; + + strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety + return true; + } + else + { + strncpy(ROMPath[slot], oldpath, 1024); + strncpy(SRAMPath[slot], oldsram, 1024); + return false; + } +} + + +// SAVESTATE TODO +// * configurable paths. not everyone wants their ROM directory to be polluted, I guess. + +void GetSavestateName(int slot, char* filename, int len) +{ + int pos; + + if (ROMPath[ROMSlot_NDS][0] == '\0') // running firmware, no ROM + { + strcpy(filename, "firmware"); + pos = 8; + } + else + { + int l = strlen(ROMPath[ROMSlot_NDS]); + pos = l; + while (ROMPath[ROMSlot_NDS][pos] != '.' && pos > 0) pos--; + if (pos == 0) pos = l; + + // avoid buffer overflow. shoddy + if (pos > len-5) pos = len-5; + + strncpy(&filename[0], ROMPath[ROMSlot_NDS], pos); + } + strcpy(&filename[pos], ".ml"); + filename[pos+3] = '0'+slot; + filename[pos+4] = '\0'; +} + +bool SavestateExists(int slot) +{ + char ssfile[1024]; + GetSavestateName(slot, ssfile, 1024); + return Platform::FileExists(ssfile); +} + +bool LoadState(const char* filename) +{ + u32 oldGBACartCRC = GBACart::CartCRC; + + // backup + Savestate* backup = new Savestate("timewarp.mln", true); + NDS::DoSavestate(backup); + delete backup; + + bool failed = false; + + Savestate* state = new Savestate(filename, false); + if (state->Error) + { + delete state; + + //uiMsgBoxError(MainWindow, "Error", "Could not load savestate file."); + + // current state might be crapoed, so restore from sane backup + state = new Savestate("timewarp.mln", false); + failed = true; + } + + NDS::DoSavestate(state); + delete state; + + if (!failed) + { + if (Config::SavestateRelocSRAM && ROMPath[ROMSlot_NDS][0]!='\0') + { + strncpy(PrevSRAMPath[ROMSlot_NDS], SRAMPath[0], 1024); + + strncpy(SRAMPath[ROMSlot_NDS], filename, 1019); + int len = strlen(SRAMPath[ROMSlot_NDS]); + strcpy(&SRAMPath[ROMSlot_NDS][len], ".sav"); + SRAMPath[ROMSlot_NDS][len+4] = '\0'; + + NDS::RelocateSave(SRAMPath[ROMSlot_NDS], false); + } + + bool loadedPartialGBAROM = false; + + // in case we have a GBA cart inserted, and the GBA ROM changes + // due to having loaded a save state, we do not want to reload + // the previous cartridge on reset, or commit writes to any + // loaded save file. therefore, their paths are "nulled". + if (GBACart::CartInserted && GBACart::CartCRC != oldGBACartCRC) + { + ROMPath[ROMSlot_GBA][0] = '\0'; + SRAMPath[ROMSlot_GBA][0] = '\0'; + loadedPartialGBAROM = true; + } + + // TODO forward this to user in a meaningful way!! + /*char msg[64]; + if (slot > 0) sprintf(msg, "State loaded from slot %d%s", + slot, loadedPartialGBAROM ? " (GBA ROM header only)" : ""); + else sprintf(msg, "State loaded from file%s", + loadedPartialGBAROM ? " (GBA ROM header only)" : ""); + OSD::AddMessage(0, msg);*/ + + SavestateLoaded = true; + } + + return !failed; +} + +bool SaveState(const char* filename) +{ + Savestate* state = new Savestate(filename, true); + if (state->Error) + { + delete state; + return false; + } + else + { + NDS::DoSavestate(state); + delete state; + + if (Config::SavestateRelocSRAM && ROMPath[ROMSlot_NDS][0]!='\0') + { + strncpy(SRAMPath[ROMSlot_NDS], filename, 1019); + int len = strlen(SRAMPath[ROMSlot_NDS]); + strcpy(&SRAMPath[ROMSlot_NDS][len], ".sav"); + SRAMPath[ROMSlot_NDS][len+4] = '\0'; + + NDS::RelocateSave(SRAMPath[ROMSlot_NDS], true); + } + } + + /*char msg[64]; + if (slot > 0) sprintf(msg, "State saved to slot %d", slot); + else sprintf(msg, "State saved to file"); + OSD::AddMessage(0, msg);*/ + return true; +} + +void UndoStateLoad() +{ + if (!SavestateLoaded) return; + + // pray that this works + // what do we do if it doesn't??? + // but it should work. + Savestate* backup = new Savestate("timewarp.mln", false); + NDS::DoSavestate(backup); + delete backup; + + if (ROMPath[ROMSlot_NDS][0]!='\0') + { + strncpy(SRAMPath[ROMSlot_NDS], PrevSRAMPath[ROMSlot_NDS], 1024); + NDS::RelocateSave(SRAMPath[ROMSlot_NDS], false); + } + + //OSD::AddMessage(0, "State load undone"); +} + +} diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 4999d94..648cd99 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -49,11 +49,6 @@ char* EmuDirectory; bool RunningSomething; -char ROMPath[2][1024]; -char SRAMPath[2][1024]; -char PrevSRAMPath[2][1024]; // for savestate 'undo load' - -bool SavestateLoaded; MainWindow* mainWindow; EmuThread* emuThread; @@ -334,8 +329,8 @@ void EmuThread::emuPause(bool refresh) { int status = refresh ? 2:3; PrevEmuStatus = EmuRunning; - EmuRunning = status;printf("emuPause %d -> %d %d\n", PrevEmuStatus, EmuRunning, EmuStatus); - while (EmuStatus != status);printf("wait done\n"); + EmuRunning = status; + while (EmuStatus != status); } void EmuThread::emuUnpause() @@ -370,7 +365,7 @@ void MainWindowPanel::paintEvent(QPaintEvent* event) MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) { - setWindowTitle("melonDS - assfucking Qt version"); + setWindowTitle("melonDS " MELONDS_VERSION); QMenuBar* menubar = new QMenuBar(); { @@ -569,6 +564,7 @@ int main(int argc, char** argv) emuThread->start(); emuThread->emuPause(true); + #if 0 if (argc > 1) { char* file = argv[1]; @@ -601,11 +597,12 @@ int main(int argc, char** argv) } } } + #endif int ret = melon.exec(); -printf("melon over\n"); - emuThread->emuStop();printf("STOP\n"); - emuThread->wait();printf("farked\n"); + + emuThread->emuStop(); + emuThread->wait(); Config::Save(); -- cgit v1.2.3 From 3c883a2152c699b432c951b3498a1adc3d4c21b1 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 27 Apr 2020 22:32:33 +0200 Subject: hey look, it runs shit now! --- src/frontend/FrontendUtil.h | 2 +- src/frontend/Util_ROM.cpp | 2 +- src/frontend/qt_sdl/main.cpp | 60 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 58 insertions(+), 6 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index 7e171ce..91f3cdd 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -42,7 +42,7 @@ void Init_ROM(); // load a ROM file to the specified cart slot // note: loading a ROM to the NDS slot resets emulation -bool LoadROM(char* file, int slot); +bool LoadROM(const char* file, int slot); // get the filename associated with the given savestate slot void GetSavestateName(int slot, char* filename, int len); diff --git a/src/frontend/Util_ROM.cpp b/src/frontend/Util_ROM.cpp index 574148b..ffee69a 100644 --- a/src/frontend/Util_ROM.cpp +++ b/src/frontend/Util_ROM.cpp @@ -57,7 +57,7 @@ void SetupSRAMPath(int slot) strncpy(SRAMPath[slot] + strlen(ROMPath[slot]) - 3, "sav", 3); } -bool LoadROM(char* file, int slot) +bool LoadROM(const char* file, int slot) { char oldpath[1024]; char oldsram[1024]; diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 648cd99..6b4c873 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -35,13 +35,15 @@ #include "types.h" #include "version.h" +#include "FrontendUtil.h" + #include "NDS.h" -#include "GBACart.h" #include "GPU.h" #include "SPU.h" #include "Wifi.h" #include "Platform.h" #include "Config.h" +#include "PlatformConfig.h" #include "Savestate.h" @@ -102,7 +104,7 @@ void EmuThread::run() char melontitle[100]; SDL_mutex* titlemutex = SDL_CreateMutex(); void* titledata[2] = {melontitle, titlemutex}; -printf("emu thread start: %d\n", EmuRunning); + while (EmuRunning != 0) { /*ProcessInput(); @@ -389,8 +391,56 @@ MainWindow::~MainWindow() void MainWindow::onOpenFile() { - QString filename = QFileDialog::getOpenFileName(this, "Open ROM", "", "DS ROMs (*.nds *.srl);;Any file (*.*)"); - printf("fark: %p %d %s\n", filename, filename.isEmpty(), filename.toStdString().c_str()); + emuThread->emuPause(true); + + QString filename = QFileDialog::getOpenFileName(this, + "Open ROM", + Config::LastROMFolder, + "DS ROMs (*.nds *.srl);;GBA ROMs (*.gba);;Any file (*.*)"); + if (filename.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + // this shit is stupid + char file[1024]; + strncpy(file, filename.toStdString().c_str(), 1023); file[1023] = '\0'; + + int pos = strlen(file)-1; + while (file[pos] != '/' && file[pos] != '\\' && pos > 0) pos--; + strncpy(Config::LastROMFolder, file, pos); + Config::LastROMFolder[pos] = '\0'; + char* ext = &file[strlen(file)-3]; + + int slot; bool res; + if (!strcasecmp(ext, "gba")) + { + slot = 1; + res = Frontend::LoadROM(file, Frontend::ROMSlot_GBA); + } + else + { + slot = 0; + res = Frontend::LoadROM(file, Frontend::ROMSlot_NDS); + } + + if (!res) + { + QMessageBox::critical(this, + "melonDS", + "Failed to load the ROM.\n\nMake sure the file is accessible and isn't used by another application."); + emuThread->emuUnpause(); + } + else if (slot == 1) + { + // checkme + emuThread->emuUnpause(); + } + else + { + emuThread->emuRun(); + } } @@ -557,6 +607,8 @@ int main(int argc, char** argv) } #endif + RunningSomething = false; + mainWindow = new MainWindow(); mainWindow->show(); -- cgit v1.2.3 From a8aa834c16d5044c25f6d313f88a470cb79ffbfa Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 27 Apr 2020 22:42:07 +0200 Subject: now with display! --- src/frontend/qt_sdl/main.cpp | 20 ++++++++++++++++++-- src/frontend/qt_sdl/main.h | 4 ++++ 2 files changed, 22 insertions(+), 2 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 6b4c873..33dad2b 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -299,7 +299,6 @@ void EmuThread::run() SDL_Delay(100); } - printf("ran iteration: status=%d run=%d\n", EmuStatus, EmuRunning); } EmuStatus = 0; @@ -325,6 +324,7 @@ void EmuThread::run() void EmuThread::emuRun() { EmuRunning = 1; + RunningSomething = true; } void EmuThread::emuPause(bool refresh) @@ -348,10 +348,14 @@ void EmuThread::emuStop() MainWindowPanel::MainWindowPanel(QWidget* parent) : QWidget(parent) { + screen[0] = new QImage(256, 192, QImage::Format_RGB32); + screen[1] = new QImage(256, 192, QImage::Format_RGB32); } MainWindowPanel::~MainWindowPanel() { + delete screen[0]; + delete screen[1]; } void MainWindowPanel::paintEvent(QPaintEvent* event) @@ -361,7 +365,19 @@ void MainWindowPanel::paintEvent(QPaintEvent* event) // fill background painter.fillRect(event->rect(), QColor::fromRgb(0, 0, 0)); - painter.fillRect(0, 0, 256, 192, QColor::fromRgb(0, 255, 255)); + int frontbuf = GPU::FrontBuffer; + if (!GPU::Framebuffer[frontbuf][0] || !GPU::Framebuffer[frontbuf][1]) return; + + memcpy(screen[0]->scanLine(0), GPU::Framebuffer[frontbuf][0], 256*192*4); + memcpy(screen[1]->scanLine(0), GPU::Framebuffer[frontbuf][1], 256*192*4); + + QRect src = QRect(0, 0, 256, 192); + + QRect dstTop = QRect(0, 0, 256, 192); // TODO + QRect dstBot = QRect(0, 192, 256, 192); // TODO + + painter.drawImage(dstTop, *screen[0]); + painter.drawImage(dstBot, *screen[1]); } diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 92b1846..fb35fa5 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -22,6 +22,7 @@ #include #include #include +#include class EmuThread : public QThread @@ -55,6 +56,9 @@ public: protected: void paintEvent(QPaintEvent* event) override; + +private: + QImage* screen[2]; }; -- cgit v1.2.3 From 0913576ef598bee4d2ea3c4268a6a6f163cd90ef Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 27 Apr 2020 23:58:29 +0200 Subject: FPS counter is back --- src/frontend/qt_sdl/main.cpp | 32 +++++++++++++++++++++----------- src/frontend/qt_sdl/main.h | 7 +++++++ 2 files changed, 28 insertions(+), 11 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 33dad2b..e9b9122 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -60,6 +60,9 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent) { EmuStatus = 0; EmuRunning = 2; + RunningSomething = false; + + connect(this, SIGNAL(windowTitleChange(QString)), mainWindow, SLOT(onTitleUpdate(QString))); } void EmuThread::run() @@ -102,8 +105,6 @@ void EmuThread::run() u32 nsamples = 0; char melontitle[100]; - SDL_mutex* titlemutex = SDL_CreateMutex(); - void* titledata[2] = {melontitle, titlemutex}; while (EmuRunning != 0) { @@ -207,6 +208,7 @@ void EmuThread::run() uiAreaQueueRedrawAll(MainDrawArea);*/ mainWindow->update(); + bool fastforward = false; /*bool fastforward = HotkeyDown(HK_FastForward); if (Config::AudioSync && !fastforward) @@ -218,7 +220,7 @@ void EmuThread::run() if (ret == SDL_MUTEX_TIMEDOUT) break; } SDL_UnlockMutex(AudioSyncLock); - } + }*/ float framerate = (1000.0f * nlines) / (60.0f * 263.0f); @@ -265,11 +267,9 @@ void EmuThread::run() if (framerate < 1) fpstarget = 999; else fpstarget = 1000.0f/framerate; - SDL_LockMutex(titlemutex); sprintf(melontitle, "[%d/%.0f] melonDS " MELONDS_VERSION, fps, fpstarget); - SDL_UnlockMutex(titlemutex); - uiQueueMain(UpdateWindowTitle, titledata); - }*/ + changeWindowTitle(melontitle); + } } else { @@ -297,14 +297,15 @@ void EmuThread::run() EmuStatus = EmuRunning; + sprintf(melontitle, "melonDS " MELONDS_VERSION); + changeWindowTitle(melontitle); + SDL_Delay(100); } } EmuStatus = 0; - SDL_DestroyMutex(titlemutex); - //if (Screen_UseGL) uiGLMakeContextCurrent(GLContext); NDS::DeInit(); @@ -321,6 +322,11 @@ void EmuThread::run() //if (Screen_UseGL) uiGLMakeContextCurrent(NULL); } +void EmuThread::changeWindowTitle(char* title) +{ + emit windowTitleChange(QString(title)); +} + void EmuThread::emuRun() { EmuRunning = 1; @@ -460,6 +466,12 @@ void MainWindow::onOpenFile() } +void MainWindow::onTitleUpdate(QString title) +{ + setWindowTitle(title); +} + + int main(int argc, char** argv) { srand(time(NULL)); @@ -623,8 +635,6 @@ int main(int argc, char** argv) } #endif - RunningSomething = false; - mainWindow = new MainWindow(); mainWindow->show(); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index fb35fa5..a1093fe 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -33,12 +33,17 @@ class EmuThread : public QThread public: explicit EmuThread(QObject* parent = nullptr); + void changeWindowTitle(char* title); + // to be called from the UI thread void emuRun(); void emuPause(bool refresh); void emuUnpause(); void emuStop(); +signals: + void windowTitleChange(QString title); + private: volatile int EmuStatus; int PrevEmuStatus; @@ -73,6 +78,8 @@ public: private slots: void onOpenFile(); + void onTitleUpdate(QString title); + private: MainWindowPanel* panel; }; -- cgit v1.2.3 From 63efc2e02aa51c44c9cb5abc621624aa1d7bf1e5 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 28 Apr 2020 22:45:11 +0200 Subject: add menu items for running the firmware and for quitting. --- src/frontend/FrontendUtil.h | 3 +++ src/frontend/Util_ROM.cpp | 16 ++++++++++++++++ src/frontend/qt_sdl/main.cpp | 44 +++++++++++++++++++++++++++++++++++++++++--- src/frontend/qt_sdl/main.h | 9 +++++++++ 4 files changed, 69 insertions(+), 3 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index 91f3cdd..eb4cb38 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -40,6 +40,9 @@ extern bool SavestateLoaded; // initialize the ROM handling utility void Init_ROM(); +// load the BIOS/firmware and boot from it +bool LoadBIOS(); + // load a ROM file to the specified cart slot // note: loading a ROM to the NDS slot resets emulation bool LoadROM(const char* file, int slot); diff --git a/src/frontend/Util_ROM.cpp b/src/frontend/Util_ROM.cpp index ffee69a..cf40a78 100644 --- a/src/frontend/Util_ROM.cpp +++ b/src/frontend/Util_ROM.cpp @@ -57,6 +57,22 @@ void SetupSRAMPath(int slot) strncpy(SRAMPath[slot] + strlen(ROMPath[slot]) - 3, "sav", 3); } +bool LoadBIOS() +{ + // TODO: + // original code in the libui frontend called NDS::LoadGBAROM() if needed + // should this be carried over here? + // is that behavior consistent with that of LoadROM() below? + + ROMPath[ROMSlot_NDS][0] = '\0'; + SRAMPath[ROMSlot_NDS][0] = '\0'; + + NDS::LoadBIOS(); + + // TODO: error reporting? + return true; +} + bool LoadROM(const char* file, int slot) { char oldpath[1024]; diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index e9b9122..cadcec9 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -394,10 +394,17 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) QMenuBar* menubar = new QMenuBar(); { QMenu* menu = menubar->addMenu("File"); - QAction* act; - act = menu->addAction("Open file..."); - connect(act, &QAction::triggered, this, &MainWindow::onOpenFile); + actOpenROM = menu->addAction("Open file..."); + connect(actOpenROM, &QAction::triggered, this, &MainWindow::onOpenFile); + + actBootFirmware = menu->addAction("Launch DS menu"); + connect(actBootFirmware, &QAction::triggered, this, &MainWindow::onBootFirmware); + + menu->addSeparator(); + + actQuit = menu->addAction("Quit"); + connect(actQuit, &QAction::triggered, this, &MainWindow::onQuit); } setMenuBar(menubar); @@ -411,6 +418,12 @@ MainWindow::~MainWindow() } +void MainWindow::keyPressEvent(QKeyEvent* event) +{ + printf("key press. %d %d %08X %08X\n", event->key(), event->nativeScanCode(), event->modifiers(), event->nativeModifiers()); +} + + void MainWindow::onOpenFile() { emuThread->emuPause(true); @@ -465,6 +478,31 @@ void MainWindow::onOpenFile() } } +void MainWindow::onBootFirmware() +{ + // TODO: ensure the firmware is actually bootable!! + // TODO: check the whole GBA cart shito + + emuThread->emuPause(true); + + bool res = Frontend::LoadBIOS(); + if (!res) + { + // TODO! + + emuThread->emuUnpause(); + } + else + { + emuThread->emuRun(); + } +} + +void MainWindow::onQuit() +{ + QApplication::quit(); +} + void MainWindow::onTitleUpdate(QString title) { diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index a1093fe..9ccbddd 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -75,13 +75,22 @@ public: explicit MainWindow(QWidget* parent = nullptr); ~MainWindow(); +protected: + void keyPressEvent(QKeyEvent* event) override; + private slots: void onOpenFile(); + void onBootFirmware(); + void onQuit(); void onTitleUpdate(QString title); private: MainWindowPanel* panel; + + QAction* actOpenROM; + QAction* actBootFirmware; + QAction* actQuit; }; #endif // MAIN_H -- cgit v1.2.3 From 47ff012f5ed743d30d994a10de48e07b650f706b Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 28 Apr 2020 22:46:31 +0200 Subject: blarg --- src/frontend/qt_sdl/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index cadcec9..af4fd4b 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -398,7 +398,8 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) actOpenROM = menu->addAction("Open file..."); connect(actOpenROM, &QAction::triggered, this, &MainWindow::onOpenFile); - actBootFirmware = menu->addAction("Launch DS menu"); + //actBootFirmware = menu->addAction("Launch DS menu"); + actBootFirmware = menu->addAction("Boot firmware"); connect(actBootFirmware, &QAction::triggered, this, &MainWindow::onBootFirmware); menu->addSeparator(); -- cgit v1.2.3 From 5fbad464c27fd5290c837d78a72378215ee8e77f Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 29 Apr 2020 00:50:23 +0200 Subject: hook up savestate shito --- src/frontend/FrontendUtil.h | 2 +- src/frontend/Util_ROM.cpp | 2 + src/frontend/qt_sdl/main.cpp | 141 ++++++++++++++++++++++++++++++++++++++++++- src/frontend/qt_sdl/main.h | 6 ++ 4 files changed, 149 insertions(+), 2 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index eb4cb38..d9f5c58 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -47,7 +47,7 @@ bool LoadBIOS(); // note: loading a ROM to the NDS slot resets emulation bool LoadROM(const char* file, int slot); -// get the filename associated with the given savestate slot +// get the filename associated with the given savestate slot (1-8) void GetSavestateName(int slot, char* filename, int len); // determine whether the given savestate slot does contain a savestate diff --git a/src/frontend/Util_ROM.cpp b/src/frontend/Util_ROM.cpp index cf40a78..19c8eb9 100644 --- a/src/frontend/Util_ROM.cpp +++ b/src/frontend/Util_ROM.cpp @@ -69,6 +69,8 @@ bool LoadBIOS() NDS::LoadBIOS(); + SavestateLoaded = false; + // TODO: error reporting? return true; } diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index af4fd4b..a6a1423 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -395,7 +395,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) { QMenu* menu = menubar->addMenu("File"); - actOpenROM = menu->addAction("Open file..."); + actOpenROM = menu->addAction("Open ROM..."); connect(actOpenROM, &QAction::triggered, this, &MainWindow::onOpenFile); //actBootFirmware = menu->addAction("Launch DS menu"); @@ -404,6 +404,49 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) menu->addSeparator(); + { + QMenu* submenu = menu->addMenu("Save state"); + + for (int i = 1; i < 9; i++) + { + char title[16]; + sprintf(title, "%d", i); + actSaveState[i] = submenu->addAction(title); + actSaveState[i]->setShortcut(QKeySequence(Qt::ShiftModifier | (Qt::Key_F1+i-1))); + actSaveState[i]->setData(QVariant(i)); + connect(actSaveState[i], &QAction::triggered, this, &MainWindow::onSaveState); + } + + actSaveState[0] = submenu->addAction("File..."); + actSaveState[0]->setShortcut(QKeySequence(Qt::ShiftModifier | Qt::Key_F9)); + actSaveState[0]->setData(QVariant(0)); + connect(actSaveState[0], &QAction::triggered, this, &MainWindow::onSaveState); + } + { + QMenu* submenu = menu->addMenu("Load state"); + + for (int i = 1; i < 9; i++) + { + char title[16]; + sprintf(title, "%d", i); + actLoadState[i] = submenu->addAction(title); + actLoadState[i]->setShortcut(QKeySequence(Qt::Key_F1+i-1)); + actLoadState[i]->setData(QVariant(i)); + connect(actLoadState[i], &QAction::triggered, this, &MainWindow::onLoadState); + } + + actLoadState[0] = submenu->addAction("File..."); + actLoadState[0]->setShortcut(QKeySequence(Qt::Key_F9)); + actLoadState[0]->setData(QVariant(0)); + connect(actLoadState[0], &QAction::triggered, this, &MainWindow::onLoadState); + } + + actUndoStateLoad = menu->addAction("Undo state load"); + actUndoStateLoad->setShortcut(QKeySequence(Qt::Key_F12)); + connect(actUndoStateLoad, &QAction::triggered, this, &MainWindow::onUndoStateLoad); + + menu->addSeparator(); + actQuit = menu->addAction("Quit"); connect(actQuit, &QAction::triggered, this, &MainWindow::onQuit); } @@ -499,6 +542,102 @@ void MainWindow::onBootFirmware() } } +void MainWindow::onSaveState() +{ + int slot = ((QAction*)sender())->data().toInt(); + + emuThread->emuPause(true); + + char filename[1024]; + if (slot > 0) + { + Frontend::GetSavestateName(slot, filename, 1024); + } + else + { + // TODO: specific 'last directory' for savestate files? + QString qfilename = QFileDialog::getSaveFileName(this, + "Save state", + Config::LastROMFolder, + "melonDS savestates (*.mln);;Any file (*.*)"); + if (qfilename.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + strncpy(filename, qfilename.toStdString().c_str(), 1023); filename[1023] = '\0'; + } + + if (Frontend::SaveState(filename)) + { + // TODO: OSD message + } + else + { + // TODO: OSD error message? + } + + emuThread->emuUnpause(); +} + +void MainWindow::onLoadState() +{ + int slot = ((QAction*)sender())->data().toInt(); + + emuThread->emuPause(true); + + char filename[1024]; + if (slot > 0) + { + Frontend::GetSavestateName(slot, filename, 1024); + } + else + { + // TODO: specific 'last directory' for savestate files? + QString qfilename = QFileDialog::getOpenFileName(this, + "Load state", + Config::LastROMFolder, + "melonDS savestates (*.ml*);;Any file (*.*)"); + if (qfilename.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + strncpy(filename, qfilename.toStdString().c_str(), 1023); filename[1023] = '\0'; + } + + if (!Platform::FileExists(filename)) + { + /*char msg[64]; + if (slot > 0) sprintf(msg, "State slot %d is empty", slot); + else sprintf(msg, "State file does not exist"); + OSD::AddMessage(0xFFA0A0, msg);*/ + + emuThread->emuUnpause(); + return; + } + + if (Frontend::LoadState(filename)) + { + // TODO: OSD message + } + else + { + // TODO: OSD error message? + } + + emuThread->emuUnpause(); +} + +void MainWindow::onUndoStateLoad() +{ + emuThread->emuPause(true); + Frontend::UndoStateLoad(); + emuThread->emuUnpause(); +} + void MainWindow::onQuit() { QApplication::quit(); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 9ccbddd..8694838 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -81,6 +81,9 @@ protected: private slots: void onOpenFile(); void onBootFirmware(); + void onSaveState(); + void onLoadState(); + void onUndoStateLoad(); void onQuit(); void onTitleUpdate(QString title); @@ -90,6 +93,9 @@ private: QAction* actOpenROM; QAction* actBootFirmware; + QAction* actSaveState[9]; + QAction* actLoadState[9]; + QAction* actUndoStateLoad; QAction* actQuit; }; -- cgit v1.2.3 From 7f3e67c12a6b7687467c4b56d79e4770cc32ae43 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 30 Apr 2020 01:02:17 +0200 Subject: some more UI work --- src/frontend/qt_sdl/main.cpp | 93 ++++++++++++++++++++++++++++++++++++++++++++ src/frontend/qt_sdl/main.h | 19 +++++++++ 2 files changed, 112 insertions(+) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index a6a1423..5cc4826 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -63,6 +63,10 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent) RunningSomething = false; connect(this, SIGNAL(windowTitleChange(QString)), mainWindow, SLOT(onTitleUpdate(QString))); + connect(this, SIGNAL(windowEmuStart()), mainWindow, SLOT(onEmuStart())); + connect(this, SIGNAL(windowEmuStop()), mainWindow, SLOT(onEmuStop())); + + emit windowEmuStop(); } void EmuThread::run() @@ -331,6 +335,9 @@ void EmuThread::emuRun() { EmuRunning = 1; RunningSomething = true; + + // checkme + emit windowEmuStart(); } void EmuThread::emuPause(bool refresh) @@ -339,11 +346,15 @@ void EmuThread::emuPause(bool refresh) PrevEmuStatus = EmuRunning; EmuRunning = status; while (EmuStatus != status); + + //emit windowEmuPause(); } void EmuThread::emuUnpause() { EmuRunning = PrevEmuStatus; + + //emit windowEmuUnpause(); } void EmuThread::emuStop() @@ -351,6 +362,11 @@ void EmuThread::emuStop() EmuRunning = 0; } +bool EmuThread::emuIsRunning() +{ + return (EmuRunning == 1); +} + MainWindowPanel::MainWindowPanel(QWidget* parent) : QWidget(parent) { @@ -450,6 +466,19 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) actQuit = menu->addAction("Quit"); connect(actQuit, &QAction::triggered, this, &MainWindow::onQuit); } + { + QMenu* menu = menubar->addMenu("System"); + + actPause = menu->addAction("Pause"); + actPause->setCheckable(true); + connect(actPause, &QAction::triggered, this, &MainWindow::onPause); + + actReset = menu->addAction("Reset"); + connect(actReset, &QAction::triggered, this, &MainWindow::onReset); + + actStop = menu->addAction("Stop"); + connect(actStop, &QAction::triggered, this, &MainWindow::onStop); + } setMenuBar(menubar); panel = new MainWindowPanel(this); @@ -644,11 +673,75 @@ void MainWindow::onQuit() } +void MainWindow::onPause(bool checked) +{ + if (emuThread->emuIsRunning()) + { + emuThread->emuPause(true); + } + else + { + emuThread->emuUnpause(); + } +} + +void MainWindow::onReset() +{ + // +} + +void MainWindow::onStop() +{ + // +} + + void MainWindow::onTitleUpdate(QString title) { setWindowTitle(title); } +void MainWindow::onEmuStart() +{ + for (int i = 1; i < 9; i++) + { + actSaveState[i]->setEnabled(true); + actLoadState[i]->setEnabled(Frontend::SavestateExists(i)); + } + actSaveState[0]->setEnabled(true); + actLoadState[0]->setEnabled(true); + actUndoStateLoad->setEnabled(true); + + actPause->setEnabled(true); + actPause->setChecked(false); + actReset->setEnabled(true); + actStop->setEnabled(true); +} + +void MainWindow::onEmuStop() +{ + for (int i = 0; i < 9; i++) + { + actSaveState[i]->setEnabled(false); + actLoadState[i]->setEnabled(false); + } + actUndoStateLoad->setEnabled(false); + + actPause->setEnabled(false); + actReset->setEnabled(false); + actStop->setEnabled(false); +} + +void MainWindow::onEmuPause() +{ + // +} + +void MainWindow::onEmuUnpause() +{ + // +} + int main(int argc, char** argv) { diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 8694838..30cef1f 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -41,9 +41,15 @@ public: void emuUnpause(); void emuStop(); + bool emuIsRunning(); + signals: void windowTitleChange(QString title); + void windowEmuStart(); + void windowEmuStop(); + void windowPauseToggle(); + private: volatile int EmuStatus; int PrevEmuStatus; @@ -86,8 +92,17 @@ private slots: void onUndoStateLoad(); void onQuit(); + void onPause(bool checked); + void onReset(); + void onStop(); + void onTitleUpdate(QString title); + void onEmuStart(); + void onEmuStop(); + void onEmuPause(); + void onEmuUnpause(); + private: MainWindowPanel* panel; @@ -97,6 +112,10 @@ private: QAction* actLoadState[9]; QAction* actUndoStateLoad; QAction* actQuit; + + QAction* actPause; + QAction* actReset; + QAction* actStop; }; #endif // MAIN_H -- cgit v1.2.3 From 690f39ca3382f1e82a27c8a16dde1a4379f978f8 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 2 May 2020 19:41:03 +0200 Subject: enable savestate slots when saving a new savestate --- src/frontend/qt_sdl/main.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 5cc4826..bdf68bd 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -601,6 +601,8 @@ void MainWindow::onSaveState() if (Frontend::SaveState(filename)) { // TODO: OSD message + + actLoadState[slot]->setEnabled(true); } else { -- cgit v1.2.3 From aa4344e249d855bfc2ddd52af61d3213d9e99db3 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 2 May 2020 20:25:39 +0200 Subject: add audio output. HARK HARK HARK --- src/CMakeLists.txt | 3 ++ src/frontend/FrontendUtil.h | 13 ++++++ src/frontend/Util_Audio.cpp | 77 ++++++++++++++++++++++++++++++ src/frontend/qt_sdl/CMakeLists.txt | 1 + src/frontend/qt_sdl/main.cpp | 96 +++++++++++++++++++++++++++++++++++--- 5 files changed, 184 insertions(+), 6 deletions(-) create mode 100644 src/frontend/Util_Audio.cpp (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dca0ca9..245e6a2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,6 +4,7 @@ add_library(core STATIC ARCodeList.cpp AREngine.cpp ARM.cpp + ARM_InstrTable.h ARMInterpreter.cpp ARMInterpreter_ALU.cpp ARMInterpreter_Branch.cpp @@ -12,11 +13,13 @@ add_library(core STATIC CP15.cpp CRC32.cpp DMA.cpp + FIFO.h GBACart.cpp GPU.cpp GPU2D.cpp GPU3D.cpp GPU3D_OpenGL.cpp + GPU3D_OpenGL_shaders.h GPU3D_Soft.cpp NDS.cpp NDSCart.cpp diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index d9f5c58..32f28d1 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -63,6 +63,19 @@ bool SaveState(const char* filename); // undo the latest savestate load void UndoStateLoad(); + +// initialize the audio utility +void Init_Audio(int outputfreq); + +// get how many samples to read from the core audio output +// based on how many are needed by the frontend (outlen in samples) +int AudioOut_GetNumSamples(int outlen); + +// resample audio from the core audio output to match the frontend's +// output frequency, and apply user-specified volume +// note: this assumes the output buffer is interleaved stereo +void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen); + } #endif // FRONTENDUTIL_H diff --git a/src/frontend/Util_Audio.cpp b/src/frontend/Util_Audio.cpp new file mode 100644 index 0000000..fe0ecab --- /dev/null +++ b/src/frontend/Util_Audio.cpp @@ -0,0 +1,77 @@ +/* + Copyright 2016-2020 Arisotura + + 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 +#include +#include + +#include "FrontendUtil.h" +#include "Config.h" +#include "qt_sdl/PlatformConfig.h" // FIXME!!! +#include "Platform.h" + +#include "NDS.h" +#include "GBACart.h" + + +namespace Frontend +{ + +int AudioOut_Freq; +float AudioOut_SampleFrac; + + +void Init_Audio(int outputfreq) +{ + AudioOut_Freq = outputfreq; + AudioOut_SampleFrac = 0; +} + +int AudioOut_GetNumSamples(int outlen) +{ + float f_len_in = (outlen * 32823.6328125) / (float)AudioOut_Freq; + f_len_in += AudioOut_SampleFrac; + int len_in = (int)floor(f_len_in); + AudioOut_SampleFrac = f_len_in - len_in; + + return len_in; +} + +void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen) +{ + float res_incr = inlen / (float)outlen; + float res_timer = 0; + int res_pos = 0; + + int volume = Config::AudioVolume; + + for (int i = 0; i < outlen; i++) + { + outbuf[i*2 ] = (inbuf[res_pos*2 ] * volume) >> 8; + outbuf[i*2+1] = (inbuf[res_pos*2+1] * volume) >> 8; + + res_timer += res_incr; + while (res_timer >= 1.0) + { + res_timer -= 1.0; + res_pos++; + } + } +} + +} diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index fb9f547..05a4029 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -6,6 +6,7 @@ SET(SOURCES_QT_SDL PlatformConfig.cpp ../Util_ROM.cpp + ../Util_Audio.cpp ../FrontendUtil.h ) diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index bdf68bd..80c25b5 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -48,6 +48,8 @@ #include "Savestate.h" +// TODO: uniform variable spelling + char* EmuDirectory; bool RunningSomething; @@ -55,6 +57,48 @@ bool RunningSomething; MainWindow* mainWindow; EmuThread* emuThread; +SDL_AudioDeviceID audioDevice; +int audioFreq; +SDL_cond* audioSync; +SDL_mutex* audioSyncLock; + + +void audioCallback(void* data, Uint8* stream, int len) +{ + len /= (sizeof(s16) * 2); + + // resample incoming audio to match the output sample rate + + int len_in = Frontend::AudioOut_GetNumSamples(len); + s16 buf_in[1024*2]; + int num_in; + + SDL_LockMutex(audioSyncLock); + num_in = SPU::ReadOutput(buf_in, len_in); + SDL_CondSignal(audioSync); + SDL_UnlockMutex(audioSyncLock); + + if (num_in < 1) + { + memset(stream, 0, len*sizeof(s16)*2); + return; + } + + int margin = 6; + if (num_in < len_in-margin) + { + int last = num_in-1; + if (last < 0) last = 0; + + for (int i = num_in; i < len_in-margin; i++) + ((u32*)buf_in)[i] = ((u32*)buf_in)[last]; + + num_in = len_in-margin; + } + + Frontend::AudioOut_Resample(buf_in, num_in, (s16*)stream, len); +} + EmuThread::EmuThread(QObject* parent) : QThread(parent) { @@ -213,18 +257,18 @@ void EmuThread::run() mainWindow->update(); bool fastforward = false; - /*bool fastforward = HotkeyDown(HK_FastForward); + //bool fastforward = HotkeyDown(HK_FastForward); - if (Config::AudioSync && !fastforward) + if (Config::AudioSync && (!fastforward) && audioDevice) { - SDL_LockMutex(AudioSyncLock); + SDL_LockMutex(audioSyncLock); while (SPU::GetOutputSize() > 1024) { - int ret = SDL_CondWaitTimeout(AudioSync, AudioSyncLock, 500); + int ret = SDL_CondWaitTimeout(audioSync, audioSyncLock, 500); if (ret == SDL_MUTEX_TIMEDOUT) break; } - SDL_UnlockMutex(AudioSyncLock); - }*/ + SDL_UnlockMutex(audioSyncLock); + } float framerate = (1000.0f * nlines) / (60.0f * 263.0f); @@ -338,6 +382,7 @@ void EmuThread::emuRun() // checkme emit windowEmuStart(); + if (audioDevice) SDL_PauseAudioDevice(audioDevice, 0); } void EmuThread::emuPause(bool refresh) @@ -348,6 +393,7 @@ void EmuThread::emuPause(bool refresh) while (EmuStatus != status); //emit windowEmuPause(); + if (audioDevice) SDL_PauseAudioDevice(audioDevice, 1); } void EmuThread::emuUnpause() @@ -355,11 +401,14 @@ void EmuThread::emuUnpause() EmuRunning = PrevEmuStatus; //emit windowEmuUnpause(); + if (audioDevice) SDL_PauseAudioDevice(audioDevice, 0); } void EmuThread::emuStop() { EmuRunning = 0; + + if (audioDevice) SDL_PauseAudioDevice(audioDevice, 1); } bool EmuThread::emuIsRunning() @@ -908,6 +957,32 @@ int main(int argc, char** argv) } #endif + audioSync = SDL_CreateCond(); + audioSyncLock = SDL_CreateMutex(); + + audioFreq = 48000; // TODO: make configurable? + SDL_AudioSpec whatIwant, whatIget; + memset(&whatIwant, 0, sizeof(SDL_AudioSpec)); + whatIwant.freq = audioFreq; + whatIwant.format = AUDIO_S16LSB; + whatIwant.channels = 2; + whatIwant.samples = 1024; + whatIwant.callback = audioCallback; + audioDevice = SDL_OpenAudioDevice(NULL, 0, &whatIwant, &whatIget, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); + if (!audioDevice) + { + printf("Audio init failed: %s\n", SDL_GetError()); + } + else + { + audioFreq = whatIget.freq; + printf("Audio output frequency: %d Hz\n", audioFreq); + SDL_PauseAudioDevice(audioDevice, 1); + } + + Frontend::Init_ROM(); + Frontend::Init_Audio(audioFreq); + mainWindow = new MainWindow(); mainWindow->show(); @@ -955,6 +1030,15 @@ int main(int argc, char** argv) emuThread->emuStop(); emuThread->wait(); + //if (Joystick) SDL_JoystickClose(Joystick); + if (audioDevice) SDL_CloseAudioDevice(audioDevice); + //if (MicDevice) SDL_CloseAudioDevice(MicDevice); + + SDL_DestroyCond(audioSync); + SDL_DestroyMutex(audioSyncLock); + + //if (MicWavBuffer) delete[] MicWavBuffer; + Config::Save(); SDL_Quit(); -- cgit v1.2.3 From 9432a9f3822f024e97185ac4b0a9df5c07987dd1 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 3 May 2020 15:05:52 +0200 Subject: remove useless variables --- src/frontend/qt_sdl/main.cpp | 5 ----- 1 file changed, 5 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 80c25b5..643cf90 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -147,10 +147,6 @@ void EmuThread::run() u32 lasttick = starttick; u32 lastmeasuretick = lasttick; u32 fpslimitcount = 0; - u64 perfcount = SDL_GetPerformanceCounter(); - u64 perffreq = SDL_GetPerformanceFrequency(); - float samplesleft = 0; - u32 nsamples = 0; char melontitle[100]; @@ -288,7 +284,6 @@ void EmuThread::run() if ((abs(wantedtickF - (float)wantedtick) < 0.001312) || (fpslimitcount > 60)) { fpslimitcount = 0; - nsamples = 0; starttick = lasttick; } } -- cgit v1.2.3 From 978212e3e0b2d910ff3ca95464ad855f5872e500 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 16 May 2020 17:43:35 +0200 Subject: accept mouse events --- src/frontend/qt_sdl/main.cpp | 22 ++++++++++++++++++++++ src/frontend/qt_sdl/main.h | 4 ++++ 2 files changed, 26 insertions(+) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 643cf90..11602a6 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -447,6 +447,28 @@ void MainWindowPanel::paintEvent(QPaintEvent* event) } +void MainWindowPanel::mousePressEvent(QMouseEvent* event) +{ + event->accept(); + + printf("mouse press %d,%d\n", event->pos().x(), event->pos().y()); +} + +void MainWindowPanel::mouseReleaseEvent(QMouseEvent* event) +{ + event->accept(); + + printf("mouse release %d,%d\n", event->pos().x(), event->pos().y()); +} + +void MainWindowPanel::mouseMoveEvent(QMouseEvent* event) +{ + event->accept(); + + printf("mouse move %d,%d %08X\n", event->pos().x(), event->pos().y(), event->buttons()); +} + + MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) { setWindowTitle("melonDS " MELONDS_VERSION); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 30cef1f..f8ad9a3 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -68,6 +68,10 @@ public: protected: void paintEvent(QPaintEvent* event) override; + void mousePressEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + private: QImage* screen[2]; }; -- cgit v1.2.3 From 2afa70b8172339266feabbb9a93f2f165521f7d8 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 17 May 2020 02:37:23 +0200 Subject: miserable little attempt at adding a dialog --- src/frontend/qt_sdl/CMakeLists.txt | 3 + src/frontend/qt_sdl/EmuSettingsDialog.cpp | 31 +++++ src/frontend/qt_sdl/EmuSettingsDialog.h | 38 ++++++ src/frontend/qt_sdl/EmuSettingsDialog.ui | 201 ++++++++++++++++++++++++++++++ src/frontend/qt_sdl/main.cpp | 15 +++ src/frontend/qt_sdl/main.h | 4 + 6 files changed, 292 insertions(+) create mode 100644 src/frontend/qt_sdl/EmuSettingsDialog.cpp create mode 100644 src/frontend/qt_sdl/EmuSettingsDialog.h create mode 100644 src/frontend/qt_sdl/EmuSettingsDialog.ui (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 05a4029..a6aeb0e 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -2,6 +2,7 @@ project(qt_sdl) SET(SOURCES_QT_SDL main.cpp + EmuSettingsDialog.cpp Platform.cpp PlatformConfig.cpp @@ -19,6 +20,8 @@ find_package(Qt5 COMPONENTS Gui REQUIRED) find_package(Qt5 COMPONENTS Widgets REQUIRED) set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) find_package(PkgConfig REQUIRED) pkg_check_modules(SDL2 REQUIRED sdl2) diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.cpp b/src/frontend/qt_sdl/EmuSettingsDialog.cpp new file mode 100644 index 0000000..c64f192 --- /dev/null +++ b/src/frontend/qt_sdl/EmuSettingsDialog.cpp @@ -0,0 +1,31 @@ +/* + Copyright 2016-2020 Arisotura + + 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 "EmuSettingsDialog.h" +#include "ui_EmuSettingsDialog.h" + + +EmuSettingsDialog::EmuSettingsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::EmuSettingsDialog) +{ + ui->setupUi(this); +} + +EmuSettingsDialog::~EmuSettingsDialog() +{ + delete ui; +} diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.h b/src/frontend/qt_sdl/EmuSettingsDialog.h new file mode 100644 index 0000000..eb21aaa --- /dev/null +++ b/src/frontend/qt_sdl/EmuSettingsDialog.h @@ -0,0 +1,38 @@ +/* + Copyright 2016-2020 Arisotura + + 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 EMUSETTINGSDIALOG_H +#define EMUSETTINGSDIALOG_H + +#include + +namespace Ui { class EmuSettingsDialog; } + +class EmuSettingsDialog : public QDialog +{ + Q_OBJECT + +public: + explicit EmuSettingsDialog(QWidget* parent); + ~EmuSettingsDialog(); + +private: + Ui::EmuSettingsDialog* ui; +}; + +#endif // EMUSETTINGSDIALOG_H diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.ui b/src/frontend/qt_sdl/EmuSettingsDialog.ui new file mode 100644 index 0000000..e4deaba --- /dev/null +++ b/src/frontend/qt_sdl/EmuSettingsDialog.ui @@ -0,0 +1,201 @@ + + + EmuSettingsDialog + + + + 0 + 0 + 490 + 243 + + + + + 0 + 0 + + + + Emu settings - melonDS + + + + QLayout::SetFixedSize + + + + + DS mode + + + + + + + 0 + 0 + + + + + 290 + 0 + + + + + + + <html><head/><body><p>DS-mode ARM9 BIOS</p><p>Size should be 4 KB</p></body></html> + + + + + + + DS firmware: + + + + + + + DS ARM7 BIOS: + + + + + + + DS ARM9 BIOS: + + + + + + + + 0 + 0 + + + + Browse... + + + true + + + + + + + <html><head/><body><p>DS-mode ARM7 BIOS</p><p>Size should be 16 KB</p></body></html> + + + + + + + Browse... + + + + + + + <html><head/><body><p>DS-mode firmware</p><p><br/></p><p>Possible firmwares:</p><p>* 128 KB: DS-mode firmware from a DSi or 3DS. Not bootable.</p><p>* 256 KB: regular DS firmware.</p><p>* 512 KB: iQue DS firmware.</p></body></html> + + + + + + + Browse... + + + + + + + + + + Startup + + + + + + <html><head/><body><p>When loading a ROM, completely skip the regular boot process (&quot;Nintendo DS&quot; screen) to boot the ROM directly.</p><p><br/></p><p>Note: if your firmware dump isn't bootable, the ROM will be booted directly regardless of this setting.</p></body></html> + + + Boot game directly + + + + + + + + + + Qt::Vertical + + + + 20 + 20 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + EmuSettingsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + EmuSettingsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 11602a6..3986876 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -31,6 +31,7 @@ #include #include "main.h" +#include "EmuSettingsDialog.h" #include "types.h" #include "version.h" @@ -545,6 +546,12 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) actStop = menu->addAction("Stop"); connect(actStop, &QAction::triggered, this, &MainWindow::onStop); } + { + QMenu* menu = menubar->addMenu("Config"); + + actEmuSettings = menu->addAction("Emu settings"); + connect(actEmuSettings, &QAction::triggered, this, &MainWindow::onOpenEmuSettings); + } setMenuBar(menubar); panel = new MainWindowPanel(this); @@ -811,6 +818,14 @@ void MainWindow::onEmuUnpause() } +void MainWindow::onOpenEmuSettings() +{ + // TODO keep track of this pointer!! + EmuSettingsDialog* dlg = new EmuSettingsDialog(this); + dlg->show(); +} + + int main(int argc, char** argv) { srand(time(NULL)); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index f8ad9a3..bb5e903 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -107,6 +107,8 @@ private slots: void onEmuPause(); void onEmuUnpause(); + void onOpenEmuSettings(); + private: MainWindowPanel* panel; @@ -120,6 +122,8 @@ private: QAction* actPause; QAction* actReset; QAction* actStop; + + QAction* actEmuSettings; }; #endif // MAIN_H -- cgit v1.2.3 From 60ba163f08e0af4272c8de6141583238eb283fe8 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 17 May 2020 04:02:16 +0200 Subject: take this somewhere --- src/frontend/qt_sdl/EmuSettingsDialog.cpp | 70 +++++++++++++++++++++++++++++++ src/frontend/qt_sdl/EmuSettingsDialog.h | 26 ++++++++++++ src/frontend/qt_sdl/main.cpp | 4 +- 3 files changed, 97 insertions(+), 3 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.cpp b/src/frontend/qt_sdl/EmuSettingsDialog.cpp index c64f192..34cafb5 100644 --- a/src/frontend/qt_sdl/EmuSettingsDialog.cpp +++ b/src/frontend/qt_sdl/EmuSettingsDialog.cpp @@ -16,16 +16,86 @@ with melonDS. If not, see http://www.gnu.org/licenses/. */ +#include + +#include "types.h" +#include "Config.h" +#include "PlatformConfig.h" + #include "EmuSettingsDialog.h" #include "ui_EmuSettingsDialog.h" +EmuSettingsDialog* EmuSettingsDialog::currentDlg = nullptr; + +extern char* EmuDirectory; + + EmuSettingsDialog::EmuSettingsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::EmuSettingsDialog) { ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + + ui->txtBIOS9Path->setText(Config::BIOS9Path); + ui->txtBIOS7Path->setText(Config::BIOS7Path); + ui->txtFirmwarePath->setText(Config::FirmwarePath); + ui->chkDirectBoot->setChecked(Config::DirectBoot != 0); } EmuSettingsDialog::~EmuSettingsDialog() { delete ui; } + +void EmuSettingsDialog::on_EmuSettingsDialog_accepted() +{ + strncpy(Config::BIOS9Path, ui->txtBIOS9Path->text().toStdString().c_str(), 1023); Config::BIOS9Path[1023] = '\0'; + strncpy(Config::BIOS7Path, ui->txtBIOS7Path->text().toStdString().c_str(), 1023); Config::BIOS7Path[1023] = '\0'; + strncpy(Config::FirmwarePath, ui->txtFirmwarePath->text().toStdString().c_str(), 1023); Config::FirmwarePath[1023] = '\0'; + Config::DirectBoot = ui->chkDirectBoot->isChecked() ? 1:0; + + closeDlg(); +} + +void EmuSettingsDialog::on_EmuSettingsDialog_rejected() +{ + closeDlg(); +} + +void EmuSettingsDialog::on_btnBIOS9Browse_clicked() +{ + QString file = QFileDialog::getOpenFileName(this, + "Select DS-mode ARM9 BIOS...", + EmuDirectory, + "BIOS files (*.bin *.rom);;Any file (*.*)"); + + if (file.isEmpty()) return; + + ui->txtBIOS9Path->setText(file); +} + +void EmuSettingsDialog::on_btnBIOS7Browse_clicked() +{ + QString file = QFileDialog::getOpenFileName(this, + "Select DS-mode ARM7 BIOS...", + EmuDirectory, + "BIOS files (*.bin *.rom);;Any file (*.*)"); + + if (file.isEmpty()) return; + + ui->txtBIOS7Path->setText(file); +} + +void EmuSettingsDialog::on_btnFirmwareBrowse_clicked() +{ + QString file = QFileDialog::getOpenFileName(this, + "Select DS-mode firmware...", + EmuDirectory, + "Firmware files (*.bin *.rom);;Any file (*.*)"); + + if (file.isEmpty()) return; + + // TODO: check for shitty hacked firmware here? + + ui->txtFirmwarePath->setText(file); +} diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.h b/src/frontend/qt_sdl/EmuSettingsDialog.h index eb21aaa..471e0fb 100644 --- a/src/frontend/qt_sdl/EmuSettingsDialog.h +++ b/src/frontend/qt_sdl/EmuSettingsDialog.h @@ -22,6 +22,7 @@ #include namespace Ui { class EmuSettingsDialog; } +class EmuSettingsDialog; class EmuSettingsDialog : public QDialog { @@ -31,6 +32,31 @@ public: explicit EmuSettingsDialog(QWidget* parent); ~EmuSettingsDialog(); + static EmuSettingsDialog* currentDlg; + static void openDlg(QWidget* parent) + { + if (currentDlg) + { + currentDlg->activateWindow(); + return; + } + + currentDlg = new EmuSettingsDialog(parent); + currentDlg->show(); + } + static void closeDlg() + { + currentDlg = nullptr; + } + +private slots: + void on_EmuSettingsDialog_accepted(); + void on_EmuSettingsDialog_rejected(); + + void on_btnBIOS9Browse_clicked(); + void on_btnBIOS7Browse_clicked(); + void on_btnFirmwareBrowse_clicked(); + private: Ui::EmuSettingsDialog* ui; }; diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 3986876..8b97320 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -820,9 +820,7 @@ void MainWindow::onEmuUnpause() void MainWindow::onOpenEmuSettings() { - // TODO keep track of this pointer!! - EmuSettingsDialog* dlg = new EmuSettingsDialog(this); - dlg->show(); + EmuSettingsDialog::openDlg(this); } -- cgit v1.2.3 From c5c9434ac9abaa0ae9c3125ee6e8bc4653846ebd Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 17 May 2020 05:42:09 +0200 Subject: verify BIOS and firmware before booting games/firmware --- src/NDS.cpp | 2 +- src/frontend/FrontendUtil.h | 22 +++++++++- src/frontend/Util_ROM.cpp | 100 +++++++++++++++++++++++++++++++++++++++---- src/frontend/qt_sdl/main.cpp | 41 +++++++++++++++--- src/frontend/qt_sdl/main.h | 2 + 5 files changed, 149 insertions(+), 18 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/NDS.cpp b/src/NDS.cpp index 1d425a8..745ed28 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -587,7 +587,7 @@ bool DoSavestate_Scheduler(Savestate* file) } if (funcid == -1) { - printf("savestate: VERY BAD!!!!! FUNCTION POINTER FOR EVENT %d NOT IN HACKY LIST. CANNOT SAVE. SMACK STAPLEBUTTER.\n", i); + printf("savestate: VERY BAD!!!!! FUNCTION POINTER FOR EVENT %d NOT IN HACKY LIST. CANNOT SAVE. SMACK ARISOTURA.\n", i); return false; } } diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index 32f28d1..f4f6850 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -32,6 +32,24 @@ enum ROMSlot_MAX }; +enum +{ + Load_OK = 0, + + Load_BIOS9Missing, + Load_BIOS9Bad, + + Load_BIOS7Missing, + Load_BIOS7Bad, + + Load_FirmwareMissing, + Load_FirmwareBad, + Load_FirmwareNotBootable, + + // TODO: more precise errors for ROM loading + Load_ROMLoadError, +}; + extern char ROMPath [ROMSlot_MAX][1024]; extern char SRAMPath[ROMSlot_MAX][1024]; extern bool SavestateLoaded; @@ -41,11 +59,11 @@ extern bool SavestateLoaded; void Init_ROM(); // load the BIOS/firmware and boot from it -bool LoadBIOS(); +int LoadBIOS(); // load a ROM file to the specified cart slot // note: loading a ROM to the NDS slot resets emulation -bool LoadROM(const char* file, int slot); +int LoadROM(const char* file, int slot); // get the filename associated with the given savestate slot (1-8) void GetSavestateName(int slot, char* filename, int len); diff --git a/src/frontend/Util_ROM.cpp b/src/frontend/Util_ROM.cpp index 19c8eb9..3200de4 100644 --- a/src/frontend/Util_ROM.cpp +++ b/src/frontend/Util_ROM.cpp @@ -57,8 +57,77 @@ void SetupSRAMPath(int slot) strncpy(SRAMPath[slot] + strlen(ROMPath[slot]) - 3, "sav", 3); } -bool LoadBIOS() +int VerifyDSBIOS() { + FILE* f; + long len; + + f = Platform::OpenLocalFile(Config::BIOS9Path, "rb"); + if (!f) return Load_BIOS9Missing; + + fseek(f, 0, SEEK_END); + len = ftell(f); + if (len != 0x1000) + { + fclose(f); + return Load_BIOS9Bad; + } + + fclose(f); + + f = Platform::OpenLocalFile(Config::BIOS7Path, "rb"); + if (!f) return Load_BIOS7Missing; + + fseek(f, 0, SEEK_END); + len = ftell(f); + if (len != 0x4000) + { + fclose(f); + return Load_BIOS7Bad; + } + + fclose(f); + + return Load_OK; +} + +int VerifyDSFirmware() +{ + FILE* f; + long len; + + f = Platform::OpenLocalFile(Config::FirmwarePath, "rb"); + if (!f) return Load_FirmwareMissing; + + fseek(f, 0, SEEK_END); + len = ftell(f); + if (len == 0x20000) + { + // 128KB firmware, not bootable + fclose(f); + return Load_FirmwareNotBootable; + } + else if (len != 0x40000 && len != 0x80000) + { + fclose(f); + return Load_FirmwareBad; + } + + fclose(f); + + return Load_OK; +} + +int LoadBIOS() +{ + int res; + + res = VerifyDSBIOS(); + if (res != Load_OK) return res; + + res = VerifyDSFirmware(); + if (res != Load_OK) return res; + // TODO: // original code in the libui frontend called NDS::LoadGBAROM() if needed // should this be carried over here? @@ -71,12 +140,26 @@ bool LoadBIOS() SavestateLoaded = false; - // TODO: error reporting? - return true; + return Load_OK; } -bool LoadROM(const char* file, int slot) +int LoadROM(const char* file, int slot) { + int res; + bool directboot = Config::DirectBoot != 0; + + res = VerifyDSBIOS(); + if (res != Load_OK) return res; + + res = VerifyDSFirmware(); + if (res != Load_OK) + { + if (res == Load_FirmwareNotBootable) + directboot = true; + else + return res; + } + char oldpath[1024]; char oldsram[1024]; strncpy(oldpath, ROMPath[slot], 1024); @@ -88,28 +171,29 @@ bool LoadROM(const char* file, int slot) SetupSRAMPath(0); SetupSRAMPath(1); - if (slot == ROMSlot_NDS && NDS::LoadROM(ROMPath[slot], SRAMPath[slot], Config::DirectBoot)) + if (slot == ROMSlot_NDS && NDS::LoadROM(ROMPath[slot], SRAMPath[slot], directboot)) { SavestateLoaded = false; // Reload the inserted GBA cartridge (if any) + // TODO: report failure there?? if (ROMPath[ROMSlot_GBA][0] != '\0') NDS::LoadGBAROM(ROMPath[ROMSlot_GBA], SRAMPath[ROMSlot_GBA]); strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety - return true; + return Load_OK; } else if (slot == ROMSlot_GBA && NDS::LoadGBAROM(ROMPath[slot], SRAMPath[slot])) { SavestateLoaded = false; strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety - return true; + return Load_OK; } else { strncpy(ROMPath[slot], oldpath, 1024); strncpy(SRAMPath[slot], oldsram, 1024); - return false; + return Load_ROMLoadError; } } diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 8b97320..ec4b04b 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -570,6 +570,27 @@ void MainWindow::keyPressEvent(QKeyEvent* event) } +QString MainWindow::loadErrorStr(int error) +{ + switch (error) + { + case Frontend::Load_BIOS9Missing: return "DS ARM9 BIOS was not found or could not be accessed."; + case Frontend::Load_BIOS9Bad: return "DS ARM9 BIOS is not a valid BIOS dump."; + + case Frontend::Load_BIOS7Missing: return "DS ARM7 BIOS was not found or could not be accessed."; + case Frontend::Load_BIOS7Bad: return "DS ARM7 BIOS is not a valid BIOS dump."; + + case Frontend::Load_FirmwareMissing: return "DS firmware was not found or could not be accessed."; + case Frontend::Load_FirmwareBad: return "DS firmware is not a valid firmware dump."; + case Frontend::Load_FirmwareNotBootable: return "DS firmware is not bootable."; + + case Frontend::Load_ROMLoadError: return "Failed to load the ROM. Make sure the file is accessible and isn't used by another application."; + + default: return "Unknown error during launch; smack Arisotura."; + } +} + + void MainWindow::onOpenFile() { emuThread->emuPause(true); @@ -584,6 +605,11 @@ void MainWindow::onOpenFile() return; } + // TODO: validate the input file!! + // * check that it is a proper ROM + // * ensure the binary offsets are sane + // * etc + // this shit is stupid char file[1024]; strncpy(file, filename.toStdString().c_str(), 1023); file[1023] = '\0'; @@ -594,7 +620,7 @@ void MainWindow::onOpenFile() Config::LastROMFolder[pos] = '\0'; char* ext = &file[strlen(file)-3]; - int slot; bool res; + int slot; int res; if (!strcasecmp(ext, "gba")) { slot = 1; @@ -606,11 +632,11 @@ void MainWindow::onOpenFile() res = Frontend::LoadROM(file, Frontend::ROMSlot_NDS); } - if (!res) + if (res != Frontend::Load_OK) { QMessageBox::critical(this, "melonDS", - "Failed to load the ROM.\n\nMake sure the file is accessible and isn't used by another application."); + loadErrorStr(res)); emuThread->emuUnpause(); } else if (slot == 1) @@ -631,11 +657,12 @@ void MainWindow::onBootFirmware() emuThread->emuPause(true); - bool res = Frontend::LoadBIOS(); - if (!res) + int res = Frontend::LoadBIOS(); + if (res != Frontend::Load_OK) { - // TODO! - + QMessageBox::critical(this, + "melonDS", + loadErrorStr(res)); emuThread->emuUnpause(); } else diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index bb5e903..ee0094f 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -110,6 +110,8 @@ private slots: void onOpenEmuSettings(); private: + QString loadErrorStr(int error); + MainWindowPanel* panel; QAction* actOpenROM; -- cgit v1.2.3 From 0566c9e34c36f7f1841765b02147fd4c890e8550 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 17 May 2020 13:04:02 +0200 Subject: minor fix --- src/NDSCart.cpp | 5 +++ src/frontend/qt_sdl/Platform.cpp | 2 +- src/frontend/qt_sdl/main.cpp | 68 +--------------------------------------- 3 files changed, 7 insertions(+), 68 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index 5a2a0d0..585eadf 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -867,6 +867,11 @@ bool ReadROMParams(u32 gamecode, u32* params) void DecryptSecureArea(u8* out) { + // TODO: source decryption data from different possible sources + // * original DS-mode ARM7 BIOS has the key data at 0x30 + // * .srl ROMs (VC dumps) have encrypted secure areas but have precomputed + // decryption data at 0x1000 (and at the beginning of the DSi region if any) + u32 gamecode = *(u32*)&CartROM[0x0C]; u32 arm9base = *(u32*)&CartROM[0x20]; diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 31b5277..fea9166 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -202,7 +202,7 @@ FILE* OpenLocalFile(const char* path, const char* mode) #ifdef __WIN32__ if (pathlen > 3) { - if (path[1] == ':' && path[2] == '\\') + if (path[1] == ':' && (path[2] == '\\' || path[2] == '/')) return OpenFile(path, mode); } #else diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index ec4b04b..67eb3d8 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -915,74 +915,8 @@ int main(int argc, char** argv) //if (Config::AudioVolume < 0) Config::AudioVolume = 0; //else if (Config::AudioVolume > 256) Config::AudioVolume = 256; - // TODO: those should be checked before running anything - // (as to let the user specify their own BIOS/firmware path etc) + // TODO: this should be checked before running anything #if 0 - if (!Platform::LocalFileExists("bios7.bin") || - !Platform::LocalFileExists("bios9.bin") || - !Platform::LocalFileExists("firmware.bin")) - { -#if defined(__WIN32__) || defined(UNIX_PORTABLE) - const char* locationName = "the directory you run melonDS from"; -#else - char* locationName = EmuDirectory; -#endif - char msgboxtext[512]; - sprintf(msgboxtext, - "One or more of the following required files don't exist or couldn't be accessed:\n\n" - "bios7.bin -- ARM7 BIOS\n" - "bios9.bin -- ARM9 BIOS\n" - "firmware.bin -- firmware image\n\n" - "Dump the files from your DS and place them in %s.\n" - "Make sure that the files can be accessed.", - locationName - ); - - uiMsgBoxError(NULL, "BIOS/Firmware not found", msgboxtext); - - uiUninit(); - SDL_Quit(); - return 0; - } - if (!Platform::LocalFileExists("firmware.bin.bak")) - { - // verify the firmware - // - // there are dumps of an old hacked firmware floating around on the internet - // and those are problematic - // the hack predates WFC, and, due to this, any game that alters the WFC - // access point data will brick that firmware due to it having critical - // data in the same area. it has the same problem on hardware. - // - // but this should help stop users from reporting that issue over and over - // again, when the issue is not from melonDS but from their firmware dump. - // - // I don't know about all the firmware hacks in existence, but the one I - // looked at has 0x180 bytes from the header repeated at 0x3FC80, but - // bytes 0x0C-0x14 are different. - - FILE* f = Platform::OpenLocalFile("firmware.bin", "rb"); - u8 chk1[0x180], chk2[0x180]; - - fseek(f, 0, SEEK_SET); - fread(chk1, 1, 0x180, f); - fseek(f, -0x380, SEEK_END); - fread(chk2, 1, 0x180, f); - - memset(&chk1[0x0C], 0, 8); - memset(&chk2[0x0C], 0, 8); - - fclose(f); - - if (!memcmp(chk1, chk2, 0x180)) - { - uiMsgBoxError(NULL, - "Problematic firmware dump", - "You are using an old hacked firmware dump.\n" - "Firmware boot will stop working if you run any game that alters WFC settings.\n\n" - "Note that the issue is not from melonDS, it would also happen on an actual DS."); - } - } { const char* romlist_missing = "Save memory type detection will not work correctly.\n\n" "You should use the latest version of romlist.bin (provided in melonDS release packages)."; -- cgit v1.2.3 From c9a76edf210410dbf95d100690bad9c86aa5bd84 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 17 May 2020 14:23:06 +0200 Subject: probably fix some pretty bad issue good one, Generic --- src/frontend/qt_sdl/main.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 67eb3d8..a7388a0 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -888,9 +888,12 @@ int main(int argc, char** argv) #else const char* confdir = g_get_user_config_dir(); const char* confname = "/melonDS"; - EmuDirectory = new char[strlen(confdir) + strlen(confname) + 1]; - strcat(EmuDirectory, confdir); - strcat(EmuDirectory, confname); + int cdlen = strlen(confdir); + int cnlen = strlen(confname); + EmuDirectory = new char[cdlen + cnlen + 1]; + strncpy(&EmuDirectory[0], confdir, cdlen); + strncpy(&EmuDirectory[cdlen], confname, cnlen); + EmuDirectory[cdlen+cnlen] = '\0'; #endif QApplication melon(argc, argv); -- cgit v1.2.3 From 19566178ba7ee1fcd4207139286707cc3896493a Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 17 May 2020 18:33:03 +0200 Subject: begin adding input dialog --- src/frontend/qt_sdl/CMakeLists.txt | 1 + src/frontend/qt_sdl/EmuSettingsDialog.h | 5 +- src/frontend/qt_sdl/InputConfigDialog.cpp | 53 +++++++++++++ src/frontend/qt_sdl/InputConfigDialog.h | 63 +++++++++++++++ src/frontend/qt_sdl/InputConfigDialog.ui | 126 ++++++++++++++++++++++++++++++ src/frontend/qt_sdl/main.cpp | 21 ++++- src/frontend/qt_sdl/main.h | 3 + 7 files changed, 268 insertions(+), 4 deletions(-) create mode 100644 src/frontend/qt_sdl/InputConfigDialog.cpp create mode 100644 src/frontend/qt_sdl/InputConfigDialog.h create mode 100644 src/frontend/qt_sdl/InputConfigDialog.ui (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index a6aeb0e..da3bb1d 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -3,6 +3,7 @@ project(qt_sdl) SET(SOURCES_QT_SDL main.cpp EmuSettingsDialog.cpp + InputConfigDialog.cpp Platform.cpp PlatformConfig.cpp diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.h b/src/frontend/qt_sdl/EmuSettingsDialog.h index ce64145..7378641 100644 --- a/src/frontend/qt_sdl/EmuSettingsDialog.h +++ b/src/frontend/qt_sdl/EmuSettingsDialog.h @@ -33,16 +33,17 @@ public: ~EmuSettingsDialog(); static EmuSettingsDialog* currentDlg; - static void openDlg(QWidget* parent) + static EmuSettingsDialog* openDlg(QWidget* parent) { if (currentDlg) { currentDlg->activateWindow(); - return; + return currentDlg; } currentDlg = new EmuSettingsDialog(parent); currentDlg->show(); + return currentDlg; } static void closeDlg() { diff --git a/src/frontend/qt_sdl/InputConfigDialog.cpp b/src/frontend/qt_sdl/InputConfigDialog.cpp new file mode 100644 index 0000000..f7c0ab2 --- /dev/null +++ b/src/frontend/qt_sdl/InputConfigDialog.cpp @@ -0,0 +1,53 @@ +/* + Copyright 2016-2020 Arisotura + + 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 "types.h" +#include "Config.h" +#include "PlatformConfig.h" + +#include "InputConfigDialog.h" +#include "ui_InputConfigDialog.h" + + +InputConfigDialog* InputConfigDialog::currentDlg = nullptr; + + +InputConfigDialog::InputConfigDialog(QWidget* parent) : QDialog(parent), ui(new Ui::InputConfigDialog) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + + // +} + +InputConfigDialog::~InputConfigDialog() +{ + delete ui; +} + +void InputConfigDialog::on_InputConfigDialog_accepted() +{ + closeDlg(); +} + +void InputConfigDialog::on_InputConfigDialog_rejected() +{ + closeDlg(); +} diff --git a/src/frontend/qt_sdl/InputConfigDialog.h b/src/frontend/qt_sdl/InputConfigDialog.h new file mode 100644 index 0000000..2af73db --- /dev/null +++ b/src/frontend/qt_sdl/InputConfigDialog.h @@ -0,0 +1,63 @@ +/* + Copyright 2016-2020 Arisotura + + 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 INPUTCONFIGDIALOG_H +#define INPUTCONFIGDIALOG_H + +#include + +namespace Ui { class InputConfigDialog; } +class InputConfigDialog; + +class InputConfigDialog : public QDialog +{ + Q_OBJECT + +public: + explicit InputConfigDialog(QWidget* parent); + ~InputConfigDialog(); + + static InputConfigDialog* currentDlg; + static InputConfigDialog* openDlg(QWidget* parent) + { + if (currentDlg) + { + currentDlg->activateWindow(); + return currentDlg; + } + + currentDlg = new InputConfigDialog(parent); + currentDlg->open(); + return currentDlg; + } + static void closeDlg() + { + currentDlg = nullptr; + } + +private slots: + void on_InputConfigDialog_accepted(); + void on_InputConfigDialog_rejected(); + + // + +private: + Ui::InputConfigDialog* ui; +}; + +#endif // INPUTCONFIGDIALOG_H diff --git a/src/frontend/qt_sdl/InputConfigDialog.ui b/src/frontend/qt_sdl/InputConfigDialog.ui new file mode 100644 index 0000000..c1422e9 --- /dev/null +++ b/src/frontend/qt_sdl/InputConfigDialog.ui @@ -0,0 +1,126 @@ + + + InputConfigDialog + + + + 0 + 0 + 488 + 365 + + + + TDAH + + + + QLayout::SetFixedSize + + + + + 1 + + + + DS input + + + + + General hotkeys + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Joystick: + + + + + + + + 0 + 0 + + + + <html><head/><body><p>Selects which joystick will be used for joystick input, if any is present.</p></body></html> + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + InputConfigDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + InputConfigDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index a7388a0..cd7849d 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -32,6 +32,7 @@ #include "main.h" #include "EmuSettingsDialog.h" +#include "InputConfigDialog.h" #include "types.h" #include "version.h" @@ -551,6 +552,9 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) actEmuSettings = menu->addAction("Emu settings"); connect(actEmuSettings, &QAction::triggered, this, &MainWindow::onOpenEmuSettings); + + actInputConfig = menu->addAction("Input and hotkeys"); + connect(actInputConfig, &QAction::triggered, this, &MainWindow::onOpenInputConfig); } setMenuBar(menubar); @@ -850,6 +854,19 @@ void MainWindow::onOpenEmuSettings() EmuSettingsDialog::openDlg(this); } +void MainWindow::onOpenInputConfig() +{ + emuThread->emuPause(true); + + InputConfigDialog* dlg = InputConfigDialog::openDlg(this); + connect(dlg, &InputConfigDialog::finished, this, &MainWindow::onInputConfigFinished); +} + +void MainWindow::onInputConfigFinished() +{printf("FARTO\n"); + emuThread->emuUnpause(); +} + int main(int argc, char** argv) { @@ -915,8 +932,8 @@ int main(int argc, char** argv) Config::Load(); - //if (Config::AudioVolume < 0) Config::AudioVolume = 0; - //else if (Config::AudioVolume > 256) Config::AudioVolume = 256; + if (Config::AudioVolume < 0) Config::AudioVolume = 0; + else if (Config::AudioVolume > 256) Config::AudioVolume = 256; // TODO: this should be checked before running anything #if 0 diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index ee0094f..0324ecf 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -108,6 +108,8 @@ private slots: void onEmuUnpause(); void onOpenEmuSettings(); + void onOpenInputConfig(); + void onInputConfigFinished(); private: QString loadErrorStr(int error); @@ -126,6 +128,7 @@ private: QAction* actStop; QAction* actEmuSettings; + QAction* actInputConfig; }; #endif // MAIN_H -- cgit v1.2.3 From 7026bb15f6688d4148932a7624baf2f3d5d22d8f Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 19 May 2020 12:06:25 +0200 Subject: input dialog progress. --- src/frontend/qt_sdl/InputConfigDialog.cpp | 245 +++++++++++++++++++++++++++++- src/frontend/qt_sdl/InputConfigDialog.h | 32 ++++ src/frontend/qt_sdl/InputConfigDialog.ui | 11 +- src/frontend/qt_sdl/main.cpp | 4 +- src/frontend/qt_sdl/main.h | 2 +- 5 files changed, 286 insertions(+), 8 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/InputConfigDialog.cpp b/src/frontend/qt_sdl/InputConfigDialog.cpp index f7c0ab2..3b006fd 100644 --- a/src/frontend/qt_sdl/InputConfigDialog.cpp +++ b/src/frontend/qt_sdl/InputConfigDialog.cpp @@ -16,7 +16,9 @@ with melonDS. If not, see http://www.gnu.org/licenses/. */ -// +#include +#include +#include #include "types.h" #include "Config.h" @@ -28,13 +30,68 @@ InputConfigDialog* InputConfigDialog::currentDlg = nullptr; +const int dskeyorder[12] = {0, 1, 10, 11, 5, 4, 6, 7, 9, 8, 2, 3}; +const char* dskeylabels[12] = {"A", "B", "X", "Y", "Left", "Right", "Up", "Down", "L", "R", "Select", "Start"}; + +const int hk_addons[] = +{ + HK_SolarSensorIncrease, + HK_SolarSensorDecrease, +}; + +const char* hk_addons_labels[] = +{ + "[Boktai] Sunlight + ", + "[Boktai] Sunlight - ", +}; + +const int hk_general[] = +{ + HK_Pause, + HK_Reset, + HK_FastForward, + HK_FastForwardToggle, + HK_Lid, + HK_Mic, +}; + +const char* hk_general_labels[] = +{ + "Pause/resume", + "Reset", + "Fast forward", + "Toggle FPS limit", + "Close/open lid", + "Microphone", +}; + InputConfigDialog::InputConfigDialog(QWidget* parent) : QDialog(parent), ui(new Ui::InputConfigDialog) { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); - // + for (int i = 0; i < 12; i++) + { + keypadKeyMap[i] = Config::KeyMapping[dskeyorder[i]]; + keypadJoyMap[i] = Config::JoyMapping[dskeyorder[i]]; + } + + for (int i = 0; i < 2; i++) + { + addonsKeyMap[i] = Config::HKKeyMapping[hk_addons[i]]; + addonsJoyMap[i] = Config::HKJoyMapping[hk_addons[i]]; + } + + for (int i = 0; i < 6; i++) + { + hkGeneralKeyMap[i] = Config::HKKeyMapping[hk_general[i]]; + hkGeneralJoyMap[i] = Config::HKJoyMapping[hk_general[i]]; + } + + populatePage(ui->tabInput, 12, dskeylabels, keypadKeyMap, keypadJoyMap); + populatePage(ui->tabAddons, 2, hk_addons_labels, addonsKeyMap, addonsJoyMap); + populatePage(ui->tabHotkeysGeneral, 6, hk_general_labels, hkGeneralKeyMap, hkGeneralJoyMap); } InputConfigDialog::~InputConfigDialog() @@ -42,6 +99,109 @@ InputConfigDialog::~InputConfigDialog() delete ui; } +void InputConfigDialog::populatePage(QWidget* page, int num, const char** labels, int* keymap, int* joymap) +{ + // kind of a hack + bool ishotkey = (page != ui->tabInput); + + QHBoxLayout* main_layout = new QHBoxLayout(); + + QGroupBox* group; + QGridLayout* group_layout; + + group = new QGroupBox("Keyboard mappings:"); + main_layout->addWidget(group); + group_layout = new QGridLayout(); + group_layout->setSpacing(1); + for (int i = 0; i < num; i++) + { + QLabel* label = new QLabel(QString(labels[i])+":"); + KeyMapButton* btn = new KeyMapButton(nullptr, &keymap[i], ishotkey); + + group_layout->addWidget(label, i, 0); + group_layout->addWidget(btn, i, 1); + } + group_layout->setRowStretch(num, 1); + group->setLayout(group_layout); + group->setMinimumWidth(275); + + group = new QGroupBox("Joystick mappings:"); + main_layout->addWidget(group); + group_layout = new QGridLayout(); + group_layout->setSpacing(1); + for (int i = 0; i < num; i++) + { + QLabel* label = new QLabel(QString(labels[i])+":"); + QPushButton* btn = new QPushButton(); + + group_layout->addWidget(label, i, 0); + group_layout->addWidget(btn, i, 1); + + btn->setText(joyMappingName(joymap[i])); + + //btn->setProperty("mapping", QVariant(&joymap[i])); + //btn->setProperty("isHotkey", QVariant(ishotkey)); + } + group_layout->setRowStretch(num, 1); + group->setLayout(group_layout); + group->setMinimumWidth(275); + + page->setLayout(main_layout); +} + +QString InputConfigDialog::joyMappingName(int id) +{ + if (id < 0) + { + return "None"; + } + + bool hasbtn = ((id & 0xFFFF) != 0xFFFF); + QString str; + + if (hasbtn) + { + if (id & 0x100) + { + int hatnum = ((id >> 4) & 0xF) + 1; + + switch (id & 0xF) + { + case 0x1: str = "Hat %1 up"; break; + case 0x2: str = "Hat %1 right"; break; + case 0x4: str = "Hat %1 down"; break; + case 0x8: str = "Hat %1 left"; break; + } + + str = str.arg(hatnum); + } + else + { + str = QString("Button %1").arg((id & 0xFFFF) + 1); + } + } + else + { + str = ""; + } + + if (id & 0x10000) + { + int axisnum = ((id >> 24) & 0xF) + 1; + + if (hasbtn) str += " / "; + + switch ((id >> 20) & 0xF) + { + case 0: str += QString("Axis %1 +").arg(axisnum); break; + case 1: str += QString("Axis %1 -").arg(axisnum); break; + case 2: str += QString("Trigger %1").arg(axisnum); break; + } + } + + return str; +} + void InputConfigDialog::on_InputConfigDialog_accepted() { closeDlg(); @@ -51,3 +211,84 @@ void InputConfigDialog::on_InputConfigDialog_rejected() { closeDlg(); } + + +KeyMapButton::KeyMapButton(QWidget* parent, int* mapping, bool hotkey) : QPushButton(parent) +{ + this->mapping = mapping; + this->isHotkey = hotkey; + + setCheckable(true); + setText(mappingText()); + + connect(this, &KeyMapButton::clicked, this, &KeyMapButton::onClick); +} + +KeyMapButton::~KeyMapButton() +{ +} + +void KeyMapButton::keyPressEvent(QKeyEvent* event) +{ + if (!isChecked()) return QPushButton::keyPressEvent(event); +printf("KEY PRESSED = %08X %08X | %08X %08X %08X | %08X\n", event->key(), event->modifiers(), event->nativeVirtualKey(), event->nativeModifiers(), event->nativeScanCode(), Qt::SHIFT); + int key = event->key(); + bool ismod = (key == Qt::Key_Control || + key == Qt::Key_Alt || + key == Qt::Key_Shift || + key == Qt::Key_Meta); + + if (isHotkey) + { + if (ismod) + return; + } + + if (!ismod) + key |= event->modifiers(); + + *mapping = key; + click(); +} + +void KeyMapButton::focusOutEvent(QFocusEvent* event) +{ + if (isChecked()) + { + // if we lost the focus while mapping, consider it 'done' + click(); + } + + QPushButton::focusOutEvent(event); +} + +void KeyMapButton::onClick() +{ + if (isChecked()) + { + setText("[press key]"); + } + else + { + setText(mappingText()); + } +} + +QString KeyMapButton::mappingText() +{ + int key = *mapping; + + switch (key) + { + case -1: return "None"; + + case Qt::Key_Control: return "Ctrl"; + case Qt::Key_Alt: return "Alt"; + case Qt::Key_Shift: return "Shift"; + case Qt::Key_Meta: return "Meta"; + } + + QKeySequence seq(key); + QString ret = seq.toString(); + return ret.replace("&", "&&"); +} diff --git a/src/frontend/qt_sdl/InputConfigDialog.h b/src/frontend/qt_sdl/InputConfigDialog.h index 2af73db..b2ca3f2 100644 --- a/src/frontend/qt_sdl/InputConfigDialog.h +++ b/src/frontend/qt_sdl/InputConfigDialog.h @@ -20,6 +20,7 @@ #define INPUTCONFIGDIALOG_H #include +#include namespace Ui { class InputConfigDialog; } class InputConfigDialog; @@ -57,7 +58,38 @@ private slots: // private: + void populatePage(QWidget* page, int num, const char** labels, int* keymap, int* joymap); + + QString joyMappingName(int id); + Ui::InputConfigDialog* ui; + + int keypadKeyMap[12], keypadJoyMap[12]; + int addonsKeyMap[2], addonsJoyMap[2]; + int hkGeneralKeyMap[6], hkGeneralJoyMap[6]; +}; + + +class KeyMapButton : public QPushButton +{ + Q_OBJECT + +public: + explicit KeyMapButton(QWidget* parent, int* mapping, bool hotkey); + ~KeyMapButton(); + +protected: + void keyPressEvent(QKeyEvent* event) override; + void focusOutEvent(QFocusEvent* event) override; + +private slots: + void onClick(); + +private: + QString mappingText(); + + int* mapping; + bool isHotkey; }; #endif // INPUTCONFIGDIALOG_H diff --git a/src/frontend/qt_sdl/InputConfigDialog.ui b/src/frontend/qt_sdl/InputConfigDialog.ui index c1422e9..655da16 100644 --- a/src/frontend/qt_sdl/InputConfigDialog.ui +++ b/src/frontend/qt_sdl/InputConfigDialog.ui @@ -11,7 +11,7 @@ - TDAH + Input and hotkeys - melonDS @@ -20,11 +20,16 @@ - 1 + 0 - DS input + DS keypad + + + + + Add-ons diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index cd7849d..d664172 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -862,8 +862,8 @@ void MainWindow::onOpenInputConfig() connect(dlg, &InputConfigDialog::finished, this, &MainWindow::onInputConfigFinished); } -void MainWindow::onInputConfigFinished() -{printf("FARTO\n"); +void MainWindow::onInputConfigFinished(int res) +{ emuThread->emuUnpause(); } diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 0324ecf..4553875 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -109,7 +109,7 @@ private slots: void onOpenEmuSettings(); void onOpenInputConfig(); - void onInputConfigFinished(); + void onInputConfigFinished(int res); private: QString loadErrorStr(int error); -- cgit v1.2.3 From 7d69699d648b93d76f14024705c4a86f5929ba3d Mon Sep 17 00:00:00 2001 From: StapleButter Date: Tue, 19 May 2020 14:37:54 +0200 Subject: fix Linux build error --- src/frontend/qt_sdl/main.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index d664172..9d4c274 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -868,6 +868,11 @@ void MainWindow::onInputConfigFinished(int res) } +// FIXME!!!! +#if (!defined(__WIN32__) && !defined(UNIX_PORTABLE)) +#include +#endif + int main(int argc, char** argv) { srand(time(NULL)); -- cgit v1.2.3 From 9fbf9b997bb950a667d0af4583fd68098a7ed717 Mon Sep 17 00:00:00 2001 From: StapleButter Date: Tue, 19 May 2020 13:28:46 +0200 Subject: this might be a good idea --- src/frontend/qt_sdl/main.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 9d4c274..7b50af4 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -1045,6 +1045,7 @@ int main(int argc, char** argv) emuThread->emuStop(); emuThread->wait(); + delete emuThread; //if (Joystick) SDL_JoystickClose(Joystick); if (audioDevice) SDL_CloseAudioDevice(audioDevice); -- cgit v1.2.3 From 34506ff2bb1be880cf260e0c1b09efede14ac0d8 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 19 May 2020 20:48:52 +0200 Subject: actually complete the input config dialog --- src/frontend/qt_sdl/CMakeLists.txt | 1 + src/frontend/qt_sdl/Input.cpp | 119 +++++++++++++ src/frontend/qt_sdl/Input.h | 40 +++++ src/frontend/qt_sdl/InputConfigDialog.cpp | 283 ++++++++++++++++++++++++------ src/frontend/qt_sdl/InputConfigDialog.h | 32 +++- src/frontend/qt_sdl/main.cpp | 15 +- src/frontend/qt_sdl/main.h | 21 --- 7 files changed, 425 insertions(+), 86 deletions(-) create mode 100644 src/frontend/qt_sdl/Input.cpp create mode 100644 src/frontend/qt_sdl/Input.h (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index da3bb1d..42cf912 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -4,6 +4,7 @@ SET(SOURCES_QT_SDL main.cpp EmuSettingsDialog.cpp InputConfigDialog.cpp + Input.cpp Platform.cpp PlatformConfig.cpp diff --git a/src/frontend/qt_sdl/Input.cpp b/src/frontend/qt_sdl/Input.cpp new file mode 100644 index 0000000..7caf24a --- /dev/null +++ b/src/frontend/qt_sdl/Input.cpp @@ -0,0 +1,119 @@ +/* + Copyright 2016-2020 Arisotura + + 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 +#include + +#include "Input.h" +#include "PlatformConfig.h" + + +namespace Input +{ + +int JoystickID; +SDL_Joystick* Joystick = nullptr; + +u32 KeyInputMask, JoyInputMask; +u32 KeyHotkeyMask, JoyHotkeyMask; +u32 HotkeyMask, LastHotkeyMask; +u32 HotkeyPress, HotkeyRelease; + + +void OpenJoystick() +{ + if (Joystick) SDL_JoystickClose(Joystick); + + int num = SDL_NumJoysticks(); + if (num < 1) + { + Joystick = nullptr; + return; + } + + if (JoystickID >= num) + JoystickID = 0; + + Joystick = SDL_JoystickOpen(JoystickID); +} + +void CloseJoystick() +{ + if (Joystick) + { + SDL_JoystickClose(Joystick); + Joystick = nullptr; + } +} + + +void Process() +{ + SDL_JoystickUpdate(); + + if (Joystick) + { + if (!SDL_JoystickGetAttached(Joystick)) + { + SDL_JoystickClose(Joystick); + Joystick = NULL; + } + } + if (!Joystick && (SDL_NumJoysticks() > 0)) + { + JoystickID = Config::JoystickID; + OpenJoystick(); + } + + /*JoyInputMask = 0xFFF; + for (int i = 0; i < 12; i++) + if (JoystickButtonDown(Config::JoyMapping[i])) + JoyInputMask &= ~(1<nativeScanCode(); + return (scan == 0x11D || scan == 0x138 || scan == 0x36); +} +#else +bool IsRightModKey(QKeyEvent* event) +{ + quint32 scan = event->nativeScanCode(); + return (scan == 0x69 || scan == 0x6C || scan == 0x3E); +} +#endif + +} diff --git a/src/frontend/qt_sdl/Input.h b/src/frontend/qt_sdl/Input.h new file mode 100644 index 0000000..24ec3a7 --- /dev/null +++ b/src/frontend/qt_sdl/Input.h @@ -0,0 +1,40 @@ +/* + Copyright 2016-2020 Arisotura + + 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 INPUT_H +#define INPUT_H + +#include "types.h" + +namespace Input +{ + +extern int JoystickID; +extern SDL_Joystick* Joystick; + +// set joystickID before calling openJoystick() +void OpenJoystick(); +void CloseJoystick(); + +void Process(); + +bool IsRightModKey(QKeyEvent* event); + +} + +#endif // INPUT_H diff --git a/src/frontend/qt_sdl/InputConfigDialog.cpp b/src/frontend/qt_sdl/InputConfigDialog.cpp index 93204aa..2c0afc4 100644 --- a/src/frontend/qt_sdl/InputConfigDialog.cpp +++ b/src/frontend/qt_sdl/InputConfigDialog.cpp @@ -20,11 +20,13 @@ #include #include +#include + #include "types.h" #include "Config.h" #include "PlatformConfig.h" -#include "main.h" +#include "Input.h" #include "InputConfigDialog.h" #include "ui_InputConfigDialog.h" @@ -93,6 +95,22 @@ InputConfigDialog::InputConfigDialog(QWidget* parent) : QDialog(parent), ui(new populatePage(ui->tabInput, 12, dskeylabels, keypadKeyMap, keypadJoyMap); populatePage(ui->tabAddons, 2, hk_addons_labels, addonsKeyMap, addonsJoyMap); populatePage(ui->tabHotkeysGeneral, 6, hk_general_labels, hkGeneralKeyMap, hkGeneralJoyMap); + + int njoy = SDL_NumJoysticks(); + if (njoy > 0) + { + for (int i = 0; i < njoy; i++) + { + const char* name = SDL_JoystickNameForIndex(i); + ui->cbxJoystick->addItem(QString(name)); + } + ui->cbxJoystick->setCurrentIndex(Input::JoystickID); + } + else + { + ui->cbxJoystick->addItem("(no joysticks available)"); + ui->cbxJoystick->setEnabled(false); + } } InputConfigDialog::~InputConfigDialog() @@ -117,7 +135,7 @@ void InputConfigDialog::populatePage(QWidget* page, int num, const char** labels for (int i = 0; i < num; i++) { QLabel* label = new QLabel(QString(labels[i])+":"); - KeyMapButton* btn = new KeyMapButton(nullptr, &keymap[i], ishotkey); + KeyMapButton* btn = new KeyMapButton(&keymap[i], ishotkey); group_layout->addWidget(label, i, 0); group_layout->addWidget(btn, i, 1); @@ -133,15 +151,10 @@ void InputConfigDialog::populatePage(QWidget* page, int num, const char** labels for (int i = 0; i < num; i++) { QLabel* label = new QLabel(QString(labels[i])+":"); - QPushButton* btn = new QPushButton(); + JoyMapButton* btn = new JoyMapButton(&joymap[i], ishotkey); group_layout->addWidget(label, i, 0); group_layout->addWidget(btn, i, 1); - - btn->setText(joyMappingName(joymap[i])); - - //btn->setProperty("mapping", QVariant(&joymap[i])); - //btn->setProperty("isHotkey", QVariant(ishotkey)); } group_layout->setRowStretch(num, 1); group->setLayout(group_layout); @@ -150,71 +163,48 @@ void InputConfigDialog::populatePage(QWidget* page, int num, const char** labels page->setLayout(main_layout); } -QString InputConfigDialog::joyMappingName(int id) +void InputConfigDialog::on_InputConfigDialog_accepted() { - if (id < 0) + for (int i = 0; i < 12; i++) { - return "None"; + Config::KeyMapping[dskeyorder[i]] = keypadKeyMap[i]; + Config::JoyMapping[dskeyorder[i]] = keypadJoyMap[i]; } - bool hasbtn = ((id & 0xFFFF) != 0xFFFF); - QString str; - - if (hasbtn) - { - if (id & 0x100) - { - int hatnum = ((id >> 4) & 0xF) + 1; - - switch (id & 0xF) - { - case 0x1: str = "Hat %1 up"; break; - case 0x2: str = "Hat %1 right"; break; - case 0x4: str = "Hat %1 down"; break; - case 0x8: str = "Hat %1 left"; break; - } - - str = str.arg(hatnum); - } - else - { - str = QString("Button %1").arg((id & 0xFFFF) + 1); - } - } - else + for (int i = 0; i < 2; i++) { - str = ""; + Config::HKKeyMapping[hk_addons[i]] = addonsKeyMap[i]; + Config::HKJoyMapping[hk_addons[i]] = addonsJoyMap[i]; } - if (id & 0x10000) + for (int i = 0; i < 6; i++) { - int axisnum = ((id >> 24) & 0xF) + 1; - - if (hasbtn) str += " / "; - - switch ((id >> 20) & 0xF) - { - case 0: str += QString("Axis %1 +").arg(axisnum); break; - case 1: str += QString("Axis %1 -").arg(axisnum); break; - case 2: str += QString("Trigger %1").arg(axisnum); break; - } + Config::HKKeyMapping[hk_general[i]] = hkGeneralKeyMap[i]; + Config::HKJoyMapping[hk_general[i]] = hkGeneralJoyMap[i]; } - return str; -} + Config::JoystickID = Input::JoystickID; + Config::Save(); -void InputConfigDialog::on_InputConfigDialog_accepted() -{ closeDlg(); } void InputConfigDialog::on_InputConfigDialog_rejected() { + Input::JoystickID = Config::JoystickID; + Input::OpenJoystick(); + closeDlg(); } +void InputConfigDialog::on_cbxJoystick_currentIndexChanged(int id) +{ + Input::JoystickID = id; + Input::OpenJoystick(); +} + -KeyMapButton::KeyMapButton(QWidget* parent, int* mapping, bool hotkey) : QPushButton(parent) +KeyMapButton::KeyMapButton(int* mapping, bool hotkey) : QPushButton() { this->mapping = mapping; this->isHotkey = hotkey; @@ -257,7 +247,7 @@ void KeyMapButton::keyPressEvent(QKeyEvent* event) if (!ismod) key |= mod; - else if (IsRightModKey(event)) + else if (Input::IsRightModKey(event)) key |= (1<<31); *mapping = key; @@ -314,3 +304,188 @@ QString KeyMapButton::mappingText() return ret.replace("&", "&&"); } + + +JoyMapButton::JoyMapButton(int* mapping, bool hotkey) : QPushButton() +{ + this->mapping = mapping; + this->isHotkey = hotkey; + + setCheckable(true); + setText(mappingText()); + + connect(this, &JoyMapButton::clicked, this, &JoyMapButton::onClick); + + timerID = 0; +} + +JoyMapButton::~JoyMapButton() +{ +} + +void JoyMapButton::keyPressEvent(QKeyEvent* event) +{ + if (!isChecked()) return QPushButton::keyPressEvent(event); + + int key = event->key(); + int mod = event->modifiers(); + + if (!mod) + { + if (key == Qt::Key_Escape) { click(); return; } + if (key == Qt::Key_Backspace) { *mapping = -1; click(); return; } + } +} + +void JoyMapButton::focusOutEvent(QFocusEvent* event) +{ + if (isChecked()) + { + // if we lost the focus while mapping, consider it 'done' + click(); + } + + QPushButton::focusOutEvent(event); +} + +void JoyMapButton::timerEvent(QTimerEvent* event) +{ + SDL_Joystick* joy = Input::Joystick; + if (!joy) { click(); return; } + if (!SDL_JoystickGetAttached(joy)) { click(); return; } + + int oldmap; + if (*mapping == -1) oldmap = 0xFFFF; + else oldmap = *mapping; + + int nbuttons = SDL_JoystickNumButtons(joy); + for (int i = 0; i < nbuttons; i++) + { + if (SDL_JoystickGetButton(joy, i)) + { + *mapping = (oldmap & 0xFFFF0000) | i; + click(); + return; + } + } + + int nhats = SDL_JoystickNumHats(joy); + if (nhats > 16) nhats = 16; + for (int i = 0; i < nhats; i++) + { + Uint8 blackhat = SDL_JoystickGetHat(joy, i); + if (blackhat) + { + if (blackhat & 0x1) blackhat = 0x1; + else if (blackhat & 0x2) blackhat = 0x2; + else if (blackhat & 0x4) blackhat = 0x4; + else blackhat = 0x8; + + *mapping = (oldmap & 0xFFFF0000) | 0x100 | blackhat | (i << 4); + click(); + return; + } + } + + int naxes = SDL_JoystickNumAxes(joy); + if (naxes > 16) naxes = 16; + for (int i = 0; i < naxes; i++) + { + Sint16 axisval = SDL_JoystickGetAxis(joy, i); + int diff = abs(axisval - axesRest[i]); + + if (axesRest[i] < -16384 && axisval >= 0) + { + *mapping = (oldmap & 0xFFFF) | 0x10000 | (2 << 20) | (i << 24); + click(); + return; + } + else if (diff > 16384) + { + int axistype; + if (axisval > 0) axistype = 0; + else axistype = 1; + + *mapping = (oldmap & 0xFFFF) | 0x10000 | (axistype << 20) | (i << 24); + click(); + return; + } + } +} + +void JoyMapButton::onClick() +{ + if (isChecked()) + { + setText("[press button/axis]"); + timerID = startTimer(50); + + memset(axesRest, 0, sizeof(axesRest)); + if (Input::Joystick && SDL_JoystickGetAttached(Input::Joystick)) + { + int naxes = SDL_JoystickNumAxes(Input::Joystick); + if (naxes > 16) naxes = 16; + for (int a = 0; a < naxes; a++) + { + axesRest[a] = SDL_JoystickGetAxis(Input::Joystick, a); + } + } + } + else + { + setText(mappingText()); + if (timerID) { killTimer(timerID); timerID = 0; } + } +} + +QString JoyMapButton::mappingText() +{ + int id = *mapping; + + if (id == -1) return "None"; + + bool hasbtn = ((id & 0xFFFF) != 0xFFFF); + QString str; + + if (hasbtn) + { + if (id & 0x100) + { + int hatnum = ((id >> 4) & 0xF) + 1; + + switch (id & 0xF) + { + case 0x1: str = "Hat %1 up"; break; + case 0x2: str = "Hat %1 right"; break; + case 0x4: str = "Hat %1 down"; break; + case 0x8: str = "Hat %1 left"; break; + } + + str = str.arg(hatnum); + } + else + { + str = QString("Button %1").arg((id & 0xFFFF) + 1); + } + } + else + { + str = ""; + } + + if (id & 0x10000) + { + int axisnum = ((id >> 24) & 0xF) + 1; + + if (hasbtn) str += " / "; + + switch ((id >> 20) & 0xF) + { + case 0: str += QString("Axis %1 +").arg(axisnum); break; + case 1: str += QString("Axis %1 -").arg(axisnum); break; + case 2: str += QString("Trigger %1").arg(axisnum); break; + } + } + + return str; +} diff --git a/src/frontend/qt_sdl/InputConfigDialog.h b/src/frontend/qt_sdl/InputConfigDialog.h index b2ca3f2..82e37bc 100644 --- a/src/frontend/qt_sdl/InputConfigDialog.h +++ b/src/frontend/qt_sdl/InputConfigDialog.h @@ -55,13 +55,11 @@ private slots: void on_InputConfigDialog_accepted(); void on_InputConfigDialog_rejected(); - // + void on_cbxJoystick_currentIndexChanged(int id); private: void populatePage(QWidget* page, int num, const char** labels, int* keymap, int* joymap); - QString joyMappingName(int id); - Ui::InputConfigDialog* ui; int keypadKeyMap[12], keypadJoyMap[12]; @@ -75,7 +73,7 @@ class KeyMapButton : public QPushButton Q_OBJECT public: - explicit KeyMapButton(QWidget* parent, int* mapping, bool hotkey); + explicit KeyMapButton(int* mapping, bool hotkey); ~KeyMapButton(); protected: @@ -92,4 +90,30 @@ private: bool isHotkey; }; +class JoyMapButton : public QPushButton +{ + Q_OBJECT + +public: + explicit JoyMapButton(int* mapping, bool hotkey); + ~JoyMapButton(); + +protected: + void keyPressEvent(QKeyEvent* event) override; + void focusOutEvent(QFocusEvent* event) override; + void timerEvent(QTimerEvent* event) override; + +private slots: + void onClick(); + +private: + QString mappingText(); + + int* mapping; + bool isHotkey; + + int timerID; + int axesRest[16]; +}; + #endif // INPUTCONFIGDIALOG_H diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 7b50af4..8fe776e 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -27,10 +27,12 @@ #include #include #include +#include #include #include "main.h" +#include "Input.h" #include "EmuSettingsDialog.h" #include "InputConfigDialog.h" @@ -136,12 +138,6 @@ void EmuThread::run() } /*Touching = false; - KeyInputMask = 0xFFF; - JoyInputMask = 0xFFF; - KeyHotkeyMask = 0; - JoyHotkeyMask = 0; - HotkeyMask = 0; - LastHotkeyMask = 0; LidStatus = false;*/ u32 nframes = 0; @@ -154,6 +150,7 @@ void EmuThread::run() while (EmuRunning != 0) { + Input::Process(); /*ProcessInput(); if (HotkeyPressed(HK_FastForwardToggle)) @@ -999,6 +996,9 @@ int main(int argc, char** argv) Frontend::Init_ROM(); Frontend::Init_Audio(audioFreq); + Input::JoystickID = Config::JoystickID; + Input::OpenJoystick(); + mainWindow = new MainWindow(); mainWindow->show(); @@ -1047,7 +1047,8 @@ int main(int argc, char** argv) emuThread->wait(); delete emuThread; - //if (Joystick) SDL_JoystickClose(Joystick); + Input::CloseJoystick(); + if (audioDevice) SDL_CloseAudioDevice(audioDevice); //if (MicDevice) SDL_CloseAudioDevice(MicDevice); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 5d6638c..4553875 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -23,7 +23,6 @@ #include #include #include -#include class EmuThread : public QThread @@ -132,24 +131,4 @@ private: QAction* actInputConfig; }; - -// TODO: MacOS version of this! -// distinguish between left and right modifier keys (Ctrl, Alt, Shift) -// Qt provides no real cross-platform way to do this, so here we go -// for Windows and Linux we can distinguish via scancodes (but both -// provide different scancodes) -#ifdef __WIN32__ -inline bool IsRightModKey(QKeyEvent* event) -{ - quint32 scan = event->nativeScanCode(); - return (scan == 0x11D || scan == 0x138 || scan == 0x36); -} -#else -inline bool IsRightModKey(QKeyEvent* event) -{ - quint32 scan = event->nativeScanCode(); - return (scan == 0x69 || scan == 0x6C || scan == 0x3E); -} -#endif - #endif // MAIN_H -- cgit v1.2.3 From 9df8d91bdc12ab2e65569e26dc7c57246e384a8f Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 19 May 2020 21:34:24 +0200 Subject: keep the ugliness confined in Platform.cpp --- src/Platform.h | 3 +++ src/frontend/qt_sdl/Platform.cpp | 49 +++++++++++++++++++++++++++++++++++++++- src/frontend/qt_sdl/main.cpp | 47 ++------------------------------------ 3 files changed, 53 insertions(+), 46 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/Platform.h b/src/Platform.h index b6effdc..fea98dd 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -24,6 +24,9 @@ namespace Platform { +void Init(int argc, char** argv); +void DeInit(); + void StopEmu(); // fopen() wrappers diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index fea9166..6a5b466 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -53,7 +53,7 @@ #endif -extern char* EmuDirectory; +char* EmuDirectory; void Stop(bool internal); @@ -84,6 +84,53 @@ u8 PacketBuffer[2048]; #define NIFI_VER 1 +void Init(int argc, char** argv) +{ +#if defined(__WIN32__) || defined(UNIX_PORTABLE) + if (argc > 0 && strlen(argv[0]) > 0) + { + int len = strlen(argv[0]); + while (len > 0) + { + if (argv[0][len] == '/') break; + if (argv[0][len] == '\\') break; + len--; + } + if (len > 0) + { + EmuDirectory = new char[len+1]; + strncpy(EmuDirectory, argv[0], len); + EmuDirectory[len] = '\0'; + } + else + { + EmuDirectory = new char[2]; + strcpy(EmuDirectory, "."); + } + } + else + { + EmuDirectory = new char[2]; + strcpy(EmuDirectory, "."); + } +#else + const char* confdir = g_get_user_config_dir(); + const char* confname = "/melonDS"; + int cdlen = strlen(confdir); + int cnlen = strlen(confname); + EmuDirectory = new char[cdlen + cnlen + 1]; + strncpy(&EmuDirectory[0], confdir, cdlen); + strncpy(&EmuDirectory[cdlen], confname, cnlen); + EmuDirectory[cdlen+cnlen] = '\0'; +#endif +} + +void DeInit() +{ + delete[] EmuDirectory; +} + + void StopEmu() { //Stop(true); diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 8fe776e..a68f933 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -54,8 +54,6 @@ // TODO: uniform variable spelling -char* EmuDirectory; - bool RunningSomething; MainWindow* mainWindow; @@ -865,11 +863,6 @@ void MainWindow::onInputConfigFinished(int res) } -// FIXME!!!! -#if (!defined(__WIN32__) && !defined(UNIX_PORTABLE)) -#include -#endif - int main(int argc, char** argv) { srand(time(NULL)); @@ -877,43 +870,7 @@ int main(int argc, char** argv) printf("melonDS " MELONDS_VERSION "\n"); printf(MELONDS_URL "\n"); -#if defined(__WIN32__) || defined(UNIX_PORTABLE) - if (argc > 0 && strlen(argv[0]) > 0) - { - int len = strlen(argv[0]); - while (len > 0) - { - if (argv[0][len] == '/') break; - if (argv[0][len] == '\\') break; - len--; - } - if (len > 0) - { - EmuDirectory = new char[len+1]; - strncpy(EmuDirectory, argv[0], len); - EmuDirectory[len] = '\0'; - } - else - { - EmuDirectory = new char[2]; - strcpy(EmuDirectory, "."); - } - } - else - { - EmuDirectory = new char[2]; - strcpy(EmuDirectory, "."); - } -#else - const char* confdir = g_get_user_config_dir(); - const char* confname = "/melonDS"; - int cdlen = strlen(confdir); - int cnlen = strlen(confname); - EmuDirectory = new char[cdlen + cnlen + 1]; - strncpy(&EmuDirectory[0], confdir, cdlen); - strncpy(&EmuDirectory[cdlen], confname, cnlen); - EmuDirectory[cdlen+cnlen] = '\0'; -#endif + Platform::Init(argc, argv); QApplication melon(argc, argv); @@ -1060,7 +1017,7 @@ int main(int argc, char** argv) Config::Save(); SDL_Quit(); - delete[] EmuDirectory; + Platform::DeInit(); return ret; } -- cgit v1.2.3 From b262313816b9fac581353f74dd71e1dde1f23013 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 19 May 2020 22:22:21 +0200 Subject: actually hook up input to the core also unbotch CMakeLists.txt --- CMakeLists.txt | 8 +++ src/frontend/qt_sdl/Input.cpp | 124 +++++++++++++++++++++++++++++++++++++++++- src/frontend/qt_sdl/Input.h | 7 +++ src/frontend/qt_sdl/main.cpp | 14 ++++- src/frontend/qt_sdl/main.h | 1 + 5 files changed, 150 insertions(+), 4 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index ee021d9..e640a48 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,14 @@ if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() +if (CMAKE_BUILD_TYPE STREQUAL Debug) + add_compile_options(-Og) +endif() + +if (CMAKE_BUILD_TYPE STREQUAL Release) + add_compile_options(-O3) +endif() + add_compile_options(-fno-pic) add_link_options(-no-pie) diff --git a/src/frontend/qt_sdl/Input.cpp b/src/frontend/qt_sdl/Input.cpp index 7caf24a..d60271c 100644 --- a/src/frontend/qt_sdl/Input.cpp +++ b/src/frontend/qt_sdl/Input.cpp @@ -34,6 +34,21 @@ u32 KeyHotkeyMask, JoyHotkeyMask; u32 HotkeyMask, LastHotkeyMask; u32 HotkeyPress, HotkeyRelease; +u32 InputMask; + + +void Init() +{ + KeyInputMask = 0xFFF; + JoyInputMask = 0xFFF; + InputMask = 0xFFF; + + KeyHotkeyMask = 0; + JoyHotkeyMask = 0; + HotkeyMask = 0; + LastHotkeyMask = 0; +} + void OpenJoystick() { @@ -62,6 +77,109 @@ void CloseJoystick() } +int GetEventKeyVal(QKeyEvent* event) +{ + int key = event->key(); + int mod = event->modifiers(); + bool ismod = (key == Qt::Key_Control || + key == Qt::Key_Alt || + key == Qt::Key_AltGr || + key == Qt::Key_Shift || + key == Qt::Key_Meta); + + if (!ismod) + key |= mod; + else if (Input::IsRightModKey(event)) + key |= (1<<31); + + return key; +} + +void KeyPress(QKeyEvent* event) +{ + int keyHK = GetEventKeyVal(event); + int keyKP = keyHK & ~event->modifiers(); + + for (int i = 0; i < 12; i++) + if (keyKP == Config::KeyMapping[i]) + KeyInputMask &= ~(1<modifiers(); + + for (int i = 0; i < 12; i++) + if (keyKP == Config::KeyMapping[i]) + KeyInputMask |= (1<> 4) & 0xF; + int hatdir = val & 0xF; + Uint8 hatval = SDL_JoystickGetHat(Joystick, hatnum); + + bool pressed = false; + if (hatdir == 0x1) pressed = (hatval & SDL_HAT_UP); + else if (hatdir == 0x4) pressed = (hatval & SDL_HAT_DOWN); + else if (hatdir == 0x2) pressed = (hatval & SDL_HAT_RIGHT); + else if (hatdir == 0x8) pressed = (hatval & SDL_HAT_LEFT); + + if (pressed) return true; + } + else + { + int btnnum = val & 0xFFFF; + Uint8 btnval = SDL_JoystickGetButton(Joystick, btnnum); + + if (btnval) return true; + } + } + + if (val & 0x10000) + { + int axisnum = (val >> 24) & 0xF; + int axisdir = (val >> 20) & 0xF; + Sint16 axisval = SDL_JoystickGetAxis(Joystick, axisnum); + + switch (axisdir) + { + case 0: // positive + if (axisval > 16384) return true; + break; + + case 1: // negative + if (axisval < -16384) return true; + break; + + case 2: // trigger + if (axisval > 0) return true; + break; + } + } + + return false; +} + void Process() { SDL_JoystickUpdate(); @@ -80,11 +198,13 @@ void Process() OpenJoystick(); } - /*JoyInputMask = 0xFFF; + JoyInputMask = 0xFFF; for (int i = 0; i < 12; i++) if (JoystickButtonDown(Config::JoyMapping[i])) JoyInputMask &= ~(1<key(), event->nativeScanCode(), event->modifiers(), event->nativeModifiers()); + if (event->isAutoRepeat()) return; + + Input::KeyPress(event); +} + +void MainWindow::keyReleaseEvent(QKeyEvent* event) +{ + if (event->isAutoRepeat()) return; + + Input::KeyRelease(event); } diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 4553875..a3125cc 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -87,6 +87,7 @@ public: protected: void keyPressEvent(QKeyEvent* event) override; + void keyReleaseEvent(QKeyEvent* event) override; private slots: void onOpenFile(); -- cgit v1.2.3 From 95f9698077fe50d2567aacd8728ff2625f88f228 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 19 May 2020 22:37:48 +0200 Subject: add back some hotkeys. remove some legacy cruft from NDS.cpp. --- src/NDS.cpp | 18 ++++++++-------- src/NDS.h | 3 +-- src/frontend/qt_sdl/Input.cpp | 5 +++++ src/frontend/qt_sdl/Input.h | 4 ++++ src/frontend/qt_sdl/main.cpp | 48 ++++++++++++++++++++----------------------- 5 files changed, 40 insertions(+), 38 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/NDS.cpp b/src/NDS.cpp index 745ed28..e89aa66 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -951,23 +951,15 @@ void CancelEvent(u32 id) } -void PressKey(u32 key) -{ - KeyInput &= ~(1 << key); -} - -void ReleaseKey(u32 key) -{ - KeyInput |= (1 << key); -} - void TouchScreen(u16 x, u16 y) { + KeyInput &= ~(1<<22); SPI_TSC::SetTouchCoords(x, y); } void ReleaseScreen() { + KeyInput |= (1<<22); SPI_TSC::SetTouchCoords(0x000, 0xFFF); } @@ -981,6 +973,12 @@ void SetKeyMask(u32 mask) KeyInput |= key_lo | (key_hi << 16); } +bool IsLidClosed() +{ + if (KeyInput & (1<<23)) return true; + return false; +} + void SetLidClosed(bool closed) { if (closed) diff --git a/src/NDS.h b/src/NDS.h index c7b455e..daeadc4 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -142,13 +142,12 @@ void RelocateSave(const char* path, bool write); u32 RunFrame(); -void PressKey(u32 key); -void ReleaseKey(u32 key); void TouchScreen(u16 x, u16 y); void ReleaseScreen(); void SetKeyMask(u32 mask); +bool IsLidClosed(); void SetLidClosed(bool closed); void MicInputFrame(s16* data, int samples); diff --git a/src/frontend/qt_sdl/Input.cpp b/src/frontend/qt_sdl/Input.cpp index d60271c..84d20ad 100644 --- a/src/frontend/qt_sdl/Input.cpp +++ b/src/frontend/qt_sdl/Input.cpp @@ -217,6 +217,11 @@ void Process() } +bool HotkeyDown(int id) { return HotkeyMask & (1< 0) GBACart_SolarSensor::LightLevel--; - char msg[64]; - sprintf(msg, "Solar sensor level set to %d", GBACart_SolarSensor::LightLevel); - OSD::AddMessage(0, msg); + //char msg[64]; + //sprintf(msg, "Solar sensor level set to %d", GBACart_SolarSensor::LightLevel); + //OSD::AddMessage(0, msg); } - if (HotkeyPressed(HK_SolarSensorIncrease)) + if (Input::HotkeyPressed(HK_SolarSensorIncrease)) { if (GBACart_SolarSensor::LightLevel < 10) GBACart_SolarSensor::LightLevel++; - char msg[64]; - sprintf(msg, "Solar sensor level set to %d", GBACart_SolarSensor::LightLevel); - OSD::AddMessage(0, msg); + //char msg[64]; + //sprintf(msg, "Solar sensor level set to %d", GBACart_SolarSensor::LightLevel); + //OSD::AddMessage(0, msg); } - }*/ + } if (EmuRunning == 1) { @@ -186,14 +184,13 @@ void EmuThread::run() // process input and hotkeys NDS::SetKeyMask(Input::InputMask); - /*NDS::SetKeyMask(KeyInputMask & JoyInputMask); - if (HotkeyPressed(HK_Lid)) + if (Input::HotkeyPressed(HK_Lid)) { - LidStatus = !LidStatus; - NDS::SetLidClosed(LidStatus); - OSD::AddMessage(0, LidStatus ? "Lid closed" : "Lid opened"); - }*/ + bool lid = !NDS::IsLidClosed(); + NDS::SetLidClosed(lid); + //OSD::AddMessage(0, lid ? "Lid closed" : "Lid opened"); + } // microphone input /*FeedMicInput(); @@ -250,8 +247,7 @@ void EmuThread::run() uiAreaQueueRedrawAll(MainDrawArea);*/ mainWindow->update(); - bool fastforward = false; - //bool fastforward = HotkeyDown(HK_FastForward); + bool fastforward = Input::HotkeyDown(HK_FastForward); if (Config::AudioSync && (!fastforward) && audioDevice) { -- cgit v1.2.3 From 68a7865096a17b7a10a9dc521c99c7eb042582ec Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 19 May 2020 22:57:15 +0200 Subject: basic touchscreen support --- src/frontend/qt_sdl/main.cpp | 42 +++++++++++++++++++++++++++++++++++++++--- src/frontend/qt_sdl/main.h | 3 +++ 2 files changed, 42 insertions(+), 3 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 67ac92c..3b0b35d 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -410,6 +410,8 @@ MainWindowPanel::MainWindowPanel(QWidget* parent) : QWidget(parent) { screen[0] = new QImage(256, 192, QImage::Format_RGB32); screen[1] = new QImage(256, 192, QImage::Format_RGB32); + + touching = false; } MainWindowPanel::~MainWindowPanel() @@ -441,25 +443,59 @@ void MainWindowPanel::paintEvent(QPaintEvent* event) } +void MainWindowPanel::transformTSCoords(int& x, int& y) +{ + // TODO: actual screen de-transform taking screen layout/rotation/etc into account + + y -= 192; + + // clamp to screen range + if (x < 0) x = 0; + else if (x > 255) x = 255; + if (y < 0) y = 0; + else if (y > 191) y = 191; +} + void MainWindowPanel::mousePressEvent(QMouseEvent* event) { event->accept(); + if (event->button() != Qt::LeftButton) return; + + int x = event->pos().x(); + int y = event->pos().y(); - printf("mouse press %d,%d\n", event->pos().x(), event->pos().y()); + if (x >= 0 && x < 256 && y >= 192 && y < 384) + { + touching = true; + + transformTSCoords(x, y); + NDS::TouchScreen(x, y); + } } void MainWindowPanel::mouseReleaseEvent(QMouseEvent* event) { event->accept(); + if (event->button() != Qt::LeftButton) return; - printf("mouse release %d,%d\n", event->pos().x(), event->pos().y()); + if (touching) + { + touching = false; + NDS::ReleaseScreen(); + } } void MainWindowPanel::mouseMoveEvent(QMouseEvent* event) { event->accept(); + if (!(event->buttons() & Qt::LeftButton)) return; + if (!touching) return; + + int x = event->pos().x(); + int y = event->pos().y(); - printf("mouse move %d,%d %08X\n", event->pos().x(), event->pos().y(), event->buttons()); + transformTSCoords(x, y); + NDS::TouchScreen(x, y); } diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index a3125cc..5443780 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -74,6 +74,9 @@ protected: private: QImage* screen[2]; + bool touching; + + void transformTSCoords(int& x, int& y); }; -- cgit v1.2.3 From a2f9472e5dff77039d66783ffda9ee0fc2de77f8 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 20 May 2020 01:11:57 +0200 Subject: might help fix crashes on exit? --- src/frontend/qt_sdl/main.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 3b0b35d..70a6ec1 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -502,6 +502,7 @@ void MainWindowPanel::mouseMoveEvent(QMouseEvent* event) MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) { setWindowTitle("melonDS " MELONDS_VERSION); + setAttribute(Qt::WA_DeleteOnClose); QMenuBar* menubar = new QMenuBar(); { -- cgit v1.2.3 From 4dae6d8928b57792c4ba746a7078c9f7dcac44af Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 20 May 2020 01:49:40 +0200 Subject: load shit from command line --- src/frontend/qt_sdl/main.cpp | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 70a6ec1..0cd9f70 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -1006,7 +1006,6 @@ int main(int argc, char** argv) emuThread->start(); emuThread->emuPause(true); - #if 0 if (argc > 1) { char* file = argv[1]; @@ -1014,32 +1013,25 @@ int main(int argc, char** argv) if (!strcasecmp(ext, "nds") || !strcasecmp(ext, "srl")) { - strncpy(ROMPath[0], file, 1023); - ROMPath[0][1023] = '\0'; + int res = Frontend::LoadROM(file, Frontend::ROMSlot_NDS); - //SetupSRAMPath(0); - - //if (NDS::LoadROM(ROMPath[0], SRAMPath[0], Config::DirectBoot)) - // Run(); - } - - if (argc > 2) - { - file = argv[2]; - ext = &file[strlen(file)-3]; - - if (!strcasecmp(ext, "gba")) + if (res == Frontend::Load_OK) { - strncpy(ROMPath[1], file, 1023); - ROMPath[1][1023] = '\0'; + if (argc > 2) + { + file = argv[2]; + ext = &file[strlen(file)-3]; - //SetupSRAMPath(1); + if (!strcasecmp(ext, "gba")) + { + Frontend::LoadROM(file, Frontend::ROMSlot_GBA); + } + } - //NDS::LoadGBAROM(ROMPath[1], SRAMPath[1]); + emuThread->emuRun(); } } } - #endif int ret = melon.exec(); -- cgit v1.2.3 From 5ed87a634afcff941aad2769496784249a568723 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 20 May 2020 02:36:48 +0200 Subject: add drag-drop support --- src/frontend/qt_sdl/main.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++++ src/frontend/qt_sdl/main.h | 3 +++ 2 files changed, 65 insertions(+) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 0cd9f70..d38a73e 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -503,6 +504,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) { setWindowTitle("melonDS " MELONDS_VERSION); setAttribute(Qt::WA_DeleteOnClose); + setAcceptDrops(true); QMenuBar* menubar = new QMenuBar(); { @@ -612,6 +614,66 @@ void MainWindow::keyReleaseEvent(QKeyEvent* event) } +void MainWindow::dragEnterEvent(QDragEnterEvent* event) +{ + if (!event->mimeData()->hasUrls()) return; + + QList urls = event->mimeData()->urls(); + if (urls.count() > 1) return; // not handling more than one file at once + + QString filename = urls.at(0).toLocalFile(); + QString ext = filename.right(3); + + if (ext == "nds" || ext == "srl" || (ext == "gba" && RunningSomething)) + event->acceptProposedAction(); +} + +void MainWindow::dropEvent(QDropEvent* event) +{ + if (!event->mimeData()->hasUrls()) return; + + QList urls = event->mimeData()->urls(); + if (urls.count() > 1) return; // not handling more than one file at once + + emuThread->emuPause(true); + + QString filename = urls.at(0).toLocalFile(); + QString ext = filename.right(3); + + char _filename[1024]; + strncpy(_filename, filename.toStdString().c_str(), 1023); _filename[1023] = '\0'; + + int slot; int res; + if (ext == "gba") + { + slot = 1; + res = Frontend::LoadROM(_filename, Frontend::ROMSlot_GBA); + } + else + { + slot = 0; + res = Frontend::LoadROM(_filename, Frontend::ROMSlot_NDS); + } + + if (res != Frontend::Load_OK) + { + QMessageBox::critical(this, + "melonDS", + loadErrorStr(res)); + emuThread->emuUnpause(); + } + else if (slot == 1) + { + // checkme + emuThread->emuUnpause(); + } + else + { + emuThread->emuRun(); + } +} + + QString MainWindow::loadErrorStr(int error) { switch (error) diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 5443780..83777bd 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -92,6 +92,9 @@ protected: void keyPressEvent(QKeyEvent* event) override; void keyReleaseEvent(QKeyEvent* event) override; + void dragEnterEvent(QDragEnterEvent* event) override; + void dropEvent(QDropEvent* event) override; + private slots: void onOpenFile(); void onBootFirmware(); -- cgit v1.2.3 From 700b1a8b9dfb3cb7502a2f0941cea0090ddbdf44 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 20 May 2020 03:01:09 +0200 Subject: add window icon --- melon.qrc | 6 ++++++ src/frontend/qt_sdl/CMakeLists.txt | 18 ++++++++++-------- src/frontend/qt_sdl/main.cpp | 1 + 3 files changed, 17 insertions(+), 8 deletions(-) create mode 100644 melon.qrc (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/melon.qrc b/melon.qrc new file mode 100644 index 0000000..b1ea364 --- /dev/null +++ b/melon.qrc @@ -0,0 +1,6 @@ + + + + icon/melon_32x32.png + + \ No newline at end of file diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 42cf912..c3d0959 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -11,6 +11,8 @@ SET(SOURCES_QT_SDL ../Util_ROM.cpp ../Util_Audio.cpp ../FrontendUtil.h + + ../../../melon.qrc ) if (WIN32) @@ -66,12 +68,12 @@ elseif (WIN32) target_link_libraries(melonDS comctl32 d2d1 dwrite uxtheme ws2_32 iphlpapi gdi32 Qt5::Core Qt5::Gui Qt5::Widgets) endif () -install(FILES ../../net.kuribo64.melonDS.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications) -install(FILES ../../icon/melon_16x16.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/16x16/apps RENAME net.kuribo64.melonDS.png) -install(FILES ../../icon/melon_32x32.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/32x32/apps RENAME net.kuribo64.melonDS.png) -install(FILES ../../icon/melon_48x48.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/48x48/apps RENAME net.kuribo64.melonDS.png) -install(FILES ../../icon/melon_64x64.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/64x64/apps RENAME net.kuribo64.melonDS.png) -install(FILES ../../icon/melon_128x128.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/128x128/apps RENAME net.kuribo64.melonDS.png) -install(FILES ../../icon/melon_256x256.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/256x256/apps RENAME net.kuribo64.melonDS.png) -install(FILES ../../romlist.bin DESTINATION ${CMAKE_INSTALL_PREFIX}/share/melonDS) +install(FILES ../../../net.kuribo64.melonDS.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications) +install(FILES ../../../icon/melon_16x16.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/16x16/apps RENAME net.kuribo64.melonDS.png) +install(FILES ../../../icon/melon_32x32.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/32x32/apps RENAME net.kuribo64.melonDS.png) +install(FILES ../../../icon/melon_48x48.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/48x48/apps RENAME net.kuribo64.melonDS.png) +install(FILES ../../../icon/melon_64x64.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/64x64/apps RENAME net.kuribo64.melonDS.png) +install(FILES ../../../icon/melon_128x128.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/128x128/apps RENAME net.kuribo64.melonDS.png) +install(FILES ../../../icon/melon_256x256.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/256x256/apps RENAME net.kuribo64.melonDS.png) +install(FILES ../../../romlist.bin DESTINATION ${CMAKE_INSTALL_PREFIX}/share/melonDS) install(TARGETS melonDS RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index d38a73e..b81fc86 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -978,6 +978,7 @@ int main(int argc, char** argv) Platform::Init(argc, argv); QApplication melon(argc, argv); + melon.setWindowIcon(QIcon(":/melon-icon")); // http://stackoverflow.com/questions/14543333/joystick-wont-work-using-sdl SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); -- cgit v1.2.3 From 7be662b2dd1e7f99cf5a31c18c115c1106300964 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 20 May 2020 20:57:12 +0200 Subject: finish fleshing out the menus --- src/frontend/qt_sdl/main.cpp | 256 +++++++++++++++++++++++++++++++++++++------ src/frontend/qt_sdl/main.h | 41 ++++++- 2 files changed, 258 insertions(+), 39 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index b81fc86..7ab7f0e 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -524,9 +524,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) for (int i = 1; i < 9; i++) { - char title[16]; - sprintf(title, "%d", i); - actSaveState[i] = submenu->addAction(title); + actSaveState[i] = submenu->addAction(QString("%1").arg(i)); actSaveState[i]->setShortcut(QKeySequence(Qt::ShiftModifier | (Qt::Key_F1+i-1))); actSaveState[i]->setData(QVariant(i)); connect(actSaveState[i], &QAction::triggered, this, &MainWindow::onSaveState); @@ -542,9 +540,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) for (int i = 1; i < 9; i++) { - char title[16]; - sprintf(title, "%d", i); - actLoadState[i] = submenu->addAction(title); + actLoadState[i] = submenu->addAction(QString("%1").arg(i)); actLoadState[i]->setShortcut(QKeySequence(Qt::Key_F1+i-1)); actLoadState[i]->setData(QVariant(i)); connect(actLoadState[i], &QAction::triggered, this, &MainWindow::onLoadState); @@ -586,6 +582,125 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) actInputConfig = menu->addAction("Input and hotkeys"); connect(actInputConfig, &QAction::triggered, this, &MainWindow::onOpenInputConfig); + + actVideoSettings = menu->addAction("Video settings"); + connect(actVideoSettings, &QAction::triggered, this, &MainWindow::onOpenVideoSettings); + + actAudioSettings = menu->addAction("Audio settings"); + connect(actAudioSettings, &QAction::triggered, this, &MainWindow::onOpenAudioSettings); + + actWifiSettings = menu->addAction("Wifi settings"); + connect(actWifiSettings, &QAction::triggered, this, &MainWindow::onOpenWifiSettings); + + { + QMenu* submenu = menu->addMenu("Savestate settings"); + + actSavestateSRAMReloc = submenu->addAction("Separate savefiles"); + actSavestateSRAMReloc->setCheckable(true); + connect(actSavestateSRAMReloc, &QAction::triggered, this, &MainWindow::onChangeSavestateSRAMReloc); + } + + menu->addSeparator(); + + { + QMenu* submenu = menu->addMenu("Screen size"); + + for (int i = 0; i < 4; i++) + { + int data = i+1; + actScreenSize[i] = submenu->addAction(QString("%1x").arg(data)); + actScreenSize[i]->setData(QVariant(data)); + connect(actScreenSize[i], &QAction::triggered, this, &MainWindow::onChangeScreenSize); + } + } + { + QMenu* submenu = menu->addMenu("Screen rotation"); + grpScreenRotation = new QActionGroup(submenu); + + for (int i = 0; i < 4; i++) + { + int data = i*90; + actScreenRotation[i] = submenu->addAction(QString("%1°").arg(data)); + actScreenRotation[i]->setActionGroup(grpScreenRotation); + actScreenRotation[i]->setData(QVariant(data)); + actScreenRotation[i]->setCheckable(true); + } + + connect(grpScreenRotation, &QActionGroup::triggered, this, &MainWindow::onChangeScreenRotation); + } + { + QMenu* submenu = menu->addMenu("Screen gap"); + grpScreenGap = new QActionGroup(submenu); + + const int screengap[] = {0, 1, 8, 64, 90, 128}; + + for (int i = 0; i < 6; i++) + { + int data = screengap[i]; + actScreenGap[i] = submenu->addAction(QString("%1 px").arg(data)); + actScreenGap[i]->setActionGroup(grpScreenGap); + actScreenGap[i]->setData(QVariant(data)); + actScreenGap[i]->setCheckable(true); + } + + connect(grpScreenGap, &QActionGroup::triggered, this, &MainWindow::onChangeScreenGap); + } + { + QMenu* submenu = menu->addMenu("Screen layout"); + grpScreenLayout = new QActionGroup(submenu); + + const char* screenlayout[] = {"Natural", "Vertical", "Horizontal"}; + + for (int i = 0; i < 3; i++) + { + actScreenLayout[i] = submenu->addAction(QString(screenlayout[i])); + actScreenLayout[i]->setActionGroup(grpScreenLayout); + actScreenLayout[i]->setData(QVariant(i)); + actScreenLayout[i]->setCheckable(true); + } + + connect(grpScreenLayout, &QActionGroup::triggered, this, &MainWindow::onChangeScreenLayout); + } + { + QMenu* submenu = menu->addMenu("Screen sizing"); + grpScreenSizing = new QActionGroup(submenu); + + const char* screensizing[] = {"Even", "Emphasize top", "Emphasize bottom", "Auto"}; + + for (int i = 0; i < 4; i++) + { + actScreenSizing[i] = submenu->addAction(QString(screensizing[i])); + actScreenSizing[i]->setActionGroup(grpScreenSizing); + actScreenSizing[i]->setData(QVariant(i)); + actScreenSizing[i]->setCheckable(true); + } + + connect(grpScreenSizing, &QActionGroup::triggered, this, &MainWindow::onChangeScreenSizing); + + submenu->addSeparator(); + + actIntegerScaling = submenu->addAction("Force integer scaling"); + actIntegerScaling->setCheckable(true); + connect(actIntegerScaling, &QAction::triggered, this, &MainWindow::onChangeIntegerScaling); + } + + actScreenFiltering = menu->addAction("Screen filtering"); + actScreenFiltering->setCheckable(true); + connect(actScreenFiltering, &QAction::triggered, this, &MainWindow::onChangeScreenFiltering); + + actShowOSD = menu->addAction("Show OSD"); + actShowOSD->setCheckable(true); + connect(actShowOSD, &QAction::triggered, this, &MainWindow::onChangeShowOSD); + + menu->addSeparator(); + + actLimitFramerate = menu->addAction("Limit framerate"); + actLimitFramerate->setCheckable(true); + connect(actLimitFramerate, &QAction::triggered, this, &MainWindow::onChangeLimitFramerate); + + actAudioSync = menu->addAction("Audio sync"); + actAudioSync->setCheckable(true); + connect(actAudioSync, &QAction::triggered, this, &MainWindow::onChangeAudioSync); } setMenuBar(menubar); @@ -756,7 +871,6 @@ void MainWindow::onOpenFile() void MainWindow::onBootFirmware() { - // TODO: ensure the firmware is actually bootable!! // TODO: check the whole GBA cart shito emuThread->emuPause(true); @@ -902,6 +1016,106 @@ void MainWindow::onStop() } +void MainWindow::onEmuPause() +{ + // +} + +void MainWindow::onEmuUnpause() +{ + // +} + + +void MainWindow::onOpenEmuSettings() +{ + EmuSettingsDialog::openDlg(this); +} + +void MainWindow::onOpenInputConfig() +{ + emuThread->emuPause(true); + + InputConfigDialog* dlg = InputConfigDialog::openDlg(this); + connect(dlg, &InputConfigDialog::finished, this, &MainWindow::onInputConfigFinished); +} + +void MainWindow::onInputConfigFinished(int res) +{ + emuThread->emuUnpause(); +} + +void MainWindow::onOpenVideoSettings() +{ + // +} + +void MainWindow::onOpenAudioSettings() +{ + // +} + +void MainWindow::onOpenWifiSettings() +{ + // +} + +void MainWindow::onChangeSavestateSRAMReloc(bool checked) +{ + // +} + +void MainWindow::onChangeScreenSize() +{ + // +} + +void MainWindow::onChangeScreenRotation(QAction* act) +{ + printf("DATABOTTE %p\n", act); +} + +void MainWindow::onChangeScreenGap(QAction* act) +{ + // +} + +void MainWindow::onChangeScreenLayout(QAction* act) +{ + // +} + +void MainWindow::onChangeScreenSizing(QAction* act) +{ + // +} + +void MainWindow::onChangeIntegerScaling(bool checked) +{ + // +} + +void MainWindow::onChangeScreenFiltering(bool checked) +{ + // +} + +void MainWindow::onChangeShowOSD(bool checked) +{ + // +} + +void MainWindow::onChangeLimitFramerate(bool checked) +{ + // +} + +void MainWindow::onChangeAudioSync(bool checked) +{ + // +} + + void MainWindow::onTitleUpdate(QString title) { setWindowTitle(title); @@ -938,34 +1152,6 @@ void MainWindow::onEmuStop() actStop->setEnabled(false); } -void MainWindow::onEmuPause() -{ - // -} - -void MainWindow::onEmuUnpause() -{ - // -} - - -void MainWindow::onOpenEmuSettings() -{ - EmuSettingsDialog::openDlg(this); -} - -void MainWindow::onOpenInputConfig() -{ - emuThread->emuPause(true); - - InputConfigDialog* dlg = InputConfigDialog::openDlg(this); - connect(dlg, &InputConfigDialog::finished, this, &MainWindow::onInputConfigFinished); -} - -void MainWindow::onInputConfigFinished(int res) -{ - emuThread->emuUnpause(); -} int main(int argc, char** argv) diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 83777bd..b478f4e 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -23,6 +23,7 @@ #include #include #include +#include class EmuThread : public QThread @@ -107,6 +108,24 @@ private slots: void onReset(); void onStop(); + void onOpenEmuSettings(); + void onOpenInputConfig(); + void onInputConfigFinished(int res); + void onOpenVideoSettings(); + void onOpenAudioSettings(); + void onOpenWifiSettings(); + void onChangeSavestateSRAMReloc(bool checked); + void onChangeScreenSize(); + void onChangeScreenRotation(QAction* act); + void onChangeScreenGap(QAction* act); + void onChangeScreenLayout(QAction* act); + void onChangeScreenSizing(QAction* act); + void onChangeIntegerScaling(bool checked); + void onChangeScreenFiltering(bool checked); + void onChangeShowOSD(bool checked); + void onChangeLimitFramerate(bool checked); + void onChangeAudioSync(bool checked); + void onTitleUpdate(QString title); void onEmuStart(); @@ -114,10 +133,6 @@ private slots: void onEmuPause(); void onEmuUnpause(); - void onOpenEmuSettings(); - void onOpenInputConfig(); - void onInputConfigFinished(int res); - private: QString loadErrorStr(int error); @@ -136,6 +151,24 @@ private: QAction* actEmuSettings; QAction* actInputConfig; + QAction* actVideoSettings; + QAction* actAudioSettings; + QAction* actWifiSettings; + QAction* actSavestateSRAMReloc; + QAction* actScreenSize[4]; + QActionGroup* grpScreenRotation; + QAction* actScreenRotation[4]; + QActionGroup* grpScreenGap; + QAction* actScreenGap[6]; + QActionGroup* grpScreenLayout; + QAction* actScreenLayout[3]; + QActionGroup* grpScreenSizing; + QAction* actScreenSizing[4]; + QAction* actIntegerScaling; + QAction* actScreenFiltering; + QAction* actShowOSD; + QAction* actLimitFramerate; + QAction* actAudioSync; }; #endif // MAIN_H -- cgit v1.2.3 From d761db005694899342beee101b12aaa6d6cbf711 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 20 May 2020 21:19:04 +0200 Subject: populate the menus with the config data --- src/frontend/qt_sdl/PlatformConfig.cpp | 4 ++-- src/frontend/qt_sdl/PlatformConfig.h | 2 +- src/frontend/qt_sdl/main.cpp | 33 +++++++++++++++++++++++++++++++-- 3 files changed, 34 insertions(+), 5 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/PlatformConfig.cpp b/src/frontend/qt_sdl/PlatformConfig.cpp index 3b4b4aa..28c224d 100644 --- a/src/frontend/qt_sdl/PlatformConfig.cpp +++ b/src/frontend/qt_sdl/PlatformConfig.cpp @@ -40,11 +40,11 @@ int ScreenRotation; int ScreenGap; int ScreenLayout; int ScreenSizing; +int IntegerScaling; int ScreenFilter; int ScreenUseGL; int ScreenVSync; -int ScreenRatio; int LimitFPS; int AudioSync; @@ -121,11 +121,11 @@ ConfigEntry PlatformConfigFile[] = {"ScreenGap", 0, &ScreenGap, 0, NULL, 0}, {"ScreenLayout", 0, &ScreenLayout, 0, NULL, 0}, {"ScreenSizing", 0, &ScreenSizing, 0, NULL, 0}, + {"IntegerScaling", 0, &IntegerScaling, 0, NULL, 0}, {"ScreenFilter", 0, &ScreenFilter, 1, NULL, 0}, {"ScreenUseGL", 0, &ScreenUseGL, 1, NULL, 0}, {"ScreenVSync", 0, &ScreenVSync, 0, NULL, 0}, - {"ScreenRatio", 0, &ScreenRatio, 0, NULL, 0}, {"LimitFPS", 0, &LimitFPS, 0, NULL, 0}, {"AudioSync", 0, &AudioSync, 1, NULL, 0}, diff --git a/src/frontend/qt_sdl/PlatformConfig.h b/src/frontend/qt_sdl/PlatformConfig.h index 3704d14..539f9a4 100644 --- a/src/frontend/qt_sdl/PlatformConfig.h +++ b/src/frontend/qt_sdl/PlatformConfig.h @@ -53,11 +53,11 @@ extern int ScreenRotation; extern int ScreenGap; extern int ScreenLayout; extern int ScreenSizing; +extern int IntegerScaling; extern int ScreenFilter; extern int ScreenUseGL; extern int ScreenVSync; -extern int ScreenRatio; extern int LimitFPS; extern int AudioSync; diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 7ab7f0e..af95c49 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -707,6 +707,30 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) panel = new MainWindowPanel(this); setCentralWidget(panel); panel->setMinimumSize(256, 384); + + + actSavestateSRAMReloc->setChecked(Config::SavestateRelocSRAM != 0); + + actScreenRotation[Config::ScreenRotation]->setChecked(true); + + for (int i = 0; i < 6; i++) + { + if (actScreenGap[i]->data().toInt() == Config::ScreenGap) + { + actScreenGap[i]->setChecked(true); + break; + } + } + + actScreenLayout[Config::ScreenLayout]->setChecked(true); + actScreenSizing[Config::ScreenSizing]->setChecked(true); + actIntegerScaling->setChecked(Config::IntegerScaling != 0); + + actScreenFiltering->setChecked(Config::ScreenFilter != 0); + actShowOSD->setChecked(Config::ShowOSD != 0); + + actLimitFramerate->setChecked(Config::LimitFPS != 0); + actAudioSync->setChecked(Config::AudioSync != 0); } MainWindow::~MainWindow() @@ -1183,8 +1207,13 @@ int main(int argc, char** argv) Config::Load(); - if (Config::AudioVolume < 0) Config::AudioVolume = 0; - else if (Config::AudioVolume > 256) Config::AudioVolume = 256; +#define SANITIZE(var, min, max) { if (var < min) var = min; else if (var > max) var = max; } + SANITIZE(Config::AudioVolume, 0, 256); + SANITIZE(Config::ScreenRotation, 0, 3); + SANITIZE(Config::ScreenGap, 0, 500); + SANITIZE(Config::ScreenLayout, 0, 2); + SANITIZE(Config::ScreenSizing, 0, 3); +#undef SANITIZE // TODO: this should be checked before running anything #if 0 -- cgit v1.2.3 From 26dcc95c20b13eac0c2c55529507cf0ab881cb0a Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 20 May 2020 21:23:15 +0200 Subject: do the easy menus --- src/frontend/qt_sdl/main.cpp | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index af95c49..35eac70 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -1040,17 +1040,6 @@ void MainWindow::onStop() } -void MainWindow::onEmuPause() -{ - // -} - -void MainWindow::onEmuUnpause() -{ - // -} - - void MainWindow::onOpenEmuSettings() { EmuSettingsDialog::openDlg(this); @@ -1086,7 +1075,7 @@ void MainWindow::onOpenWifiSettings() void MainWindow::onChangeSavestateSRAMReloc(bool checked) { - // + Config::SavestateRelocSRAM = checked?1:0; } void MainWindow::onChangeScreenSize() @@ -1096,7 +1085,7 @@ void MainWindow::onChangeScreenSize() void MainWindow::onChangeScreenRotation(QAction* act) { - printf("DATABOTTE %p\n", act); + // } void MainWindow::onChangeScreenGap(QAction* act) @@ -1121,22 +1110,22 @@ void MainWindow::onChangeIntegerScaling(bool checked) void MainWindow::onChangeScreenFiltering(bool checked) { - // + Config::ScreenFilter = checked?1:0; } void MainWindow::onChangeShowOSD(bool checked) { - // + Config::ShowOSD = checked?1:0; } void MainWindow::onChangeLimitFramerate(bool checked) { - // + Config::LimitFPS = checked?1:0; } void MainWindow::onChangeAudioSync(bool checked) { - // + Config::AudioSync = checked?1:0; } @@ -1176,6 +1165,16 @@ void MainWindow::onEmuStop() actStop->setEnabled(false); } +void MainWindow::onEmuPause() +{ + // +} + +void MainWindow::onEmuUnpause() +{ + // +} + int main(int argc, char** argv) -- cgit v1.2.3 From 2ebb21ce3bcf1a445dce7b13f64e44d6098a3acd Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 20 May 2020 22:22:22 +0200 Subject: hook up pause and reset, w/ relevant hotkeys --- src/frontend/FrontendUtil.h | 3 ++ src/frontend/Util_ROM.cpp | 47 +++++++++++++++++++++- src/frontend/qt_sdl/main.cpp | 93 +++++++++++++++++++++++--------------------- src/frontend/qt_sdl/main.h | 10 +++-- 4 files changed, 104 insertions(+), 49 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index f4f6850..d838b63 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -65,6 +65,9 @@ int LoadBIOS(); // note: loading a ROM to the NDS slot resets emulation int LoadROM(const char* file, int slot); +// reset execution of the current ROM +int Reset(); + // get the filename associated with the given savestate slot (1-8) void GetSavestateName(int slot, char* filename, int len); diff --git a/src/frontend/Util_ROM.cpp b/src/frontend/Util_ROM.cpp index 3200de4..e13eb2c 100644 --- a/src/frontend/Util_ROM.cpp +++ b/src/frontend/Util_ROM.cpp @@ -50,6 +50,11 @@ void Init_ROM() memset(PrevSRAMPath[ROMSlot_GBA], 0, 1024); } +// TODO: currently, when failing to load a ROM for whatever reason, we attempt +// to revert to the previous state and resume execution; this may not be a very +// good thing, depending on what state the core was left in. +// should we do a better state revert (via the savestate system)? completely stop? + void SetupSRAMPath(int slot) { strncpy(SRAMPath[slot], ROMPath[slot], 1023); @@ -184,7 +189,7 @@ int LoadROM(const char* file, int slot) } else if (slot == ROMSlot_GBA && NDS::LoadGBAROM(ROMPath[slot], SRAMPath[slot])) { - SavestateLoaded = false; + SavestateLoaded = false; // checkme?? strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety return Load_OK; @@ -197,6 +202,46 @@ int LoadROM(const char* file, int slot) } } +int Reset() +{ + int res; + bool directboot = Config::DirectBoot != 0; + + res = VerifyDSBIOS(); + if (res != Load_OK) return res; + + res = VerifyDSFirmware(); + if (res != Load_OK) + { + if (res == Load_FirmwareNotBootable) + directboot = true; + else + return res; + } + + SavestateLoaded = false; + + if (ROMPath[ROMSlot_NDS][0] == '\0') + { + NDS::LoadBIOS(); + } + else + { + SetupSRAMPath(0); + if (!NDS::LoadROM(ROMPath[ROMSlot_NDS], SRAMPath[ROMSlot_NDS], directboot)) + return Load_ROMLoadError; + } + + if (ROMPath[ROMSlot_GBA][0] != '\0') + { + SetupSRAMPath(1); + if (!NDS::LoadGBAROM(ROMPath[ROMSlot_GBA], SRAMPath[ROMSlot_GBA])) + return Load_ROMLoadError; + } + + return Load_OK; +} + // SAVESTATE TODO // * configurable paths. not everyone wants their ROM directory to be polluted, I guess. diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 35eac70..a73362d 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -113,6 +113,8 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent) connect(this, SIGNAL(windowTitleChange(QString)), mainWindow, SLOT(onTitleUpdate(QString))); connect(this, SIGNAL(windowEmuStart()), mainWindow, SLOT(onEmuStart())); connect(this, SIGNAL(windowEmuStop()), mainWindow, SLOT(onEmuStop())); + connect(this, SIGNAL(windowEmuPause()), mainWindow->actPause, SLOT(trigger())); + connect(this, SIGNAL(windowEmuReset()), mainWindow->actReset, SLOT(trigger())); emit windowEmuStop(); } @@ -152,14 +154,10 @@ void EmuThread::run() { Input::Process(); - /*if (Input::HotkeyPressed(HK_FastForwardToggle)) - { - Config::LimitFPS = !Config::LimitFPS; - // TODO: reflect in UI! - }*/ + if (Input::HotkeyPressed(HK_FastForwardToggle)) emit windowLimitFPSChange(); - //if (Input::HotkeyPressed(HK_Pause)) uiQueueMain(TogglePause, NULL); - //if (Input::HotkeyPressed(HK_Reset)) uiQueueMain(Reset, NULL); + if (Input::HotkeyPressed(HK_Pause)) emit windowEmuPause(); + if (Input::HotkeyPressed(HK_Reset)) emit windowEmuReset(); if (GBACart::CartInserted && GBACart::HasSolarSensor) { @@ -318,18 +316,15 @@ void EmuThread::run() lastmeasuretick = lasttick; fpslimitcount = 0; - if (EmuRunning == 2) + /*if (Screen_UseGL) { - /*if (Screen_UseGL) - { - uiGLBegin(GLContext); - uiGLMakeContextCurrent(GLContext); - GLScreen_DrawScreen(); - uiGLEnd(GLContext); - } - uiAreaQueueRedrawAll(MainDrawArea);*/ - mainWindow->update(); + uiGLBegin(GLContext); + uiGLMakeContextCurrent(GLContext); + GLScreen_DrawScreen(); + uiGLEnd(GLContext); } + uiAreaQueueRedrawAll(MainDrawArea);*/ + mainWindow->update(); //if (Screen_UseGL) uiGLMakeContextCurrent(NULL); @@ -338,7 +333,7 @@ void EmuThread::run() sprintf(melontitle, "melonDS " MELONDS_VERSION); changeWindowTitle(melontitle); - SDL_Delay(100); + SDL_Delay(75); } } @@ -375,12 +370,11 @@ void EmuThread::emuRun() if (audioDevice) SDL_PauseAudioDevice(audioDevice, 0); } -void EmuThread::emuPause(bool refresh) +void EmuThread::emuPause() { - int status = refresh ? 2:3; PrevEmuStatus = EmuRunning; - EmuRunning = status; - while (EmuStatus != status); + EmuRunning = 2; + while (EmuStatus != 2); //emit windowEmuPause(); if (audioDevice) SDL_PauseAudioDevice(audioDevice, 1); @@ -774,7 +768,7 @@ void MainWindow::dropEvent(QDropEvent* event) QList urls = event->mimeData()->urls(); if (urls.count() > 1) return; // not handling more than one file at once - emuThread->emuPause(true); + emuThread->emuPause(); QString filename = urls.at(0).toLocalFile(); QString ext = filename.right(3); @@ -836,7 +830,7 @@ QString MainWindow::loadErrorStr(int error) void MainWindow::onOpenFile() { - emuThread->emuPause(true); + emuThread->emuPause(); QString filename = QFileDialog::getOpenFileName(this, "Open ROM", @@ -897,7 +891,7 @@ void MainWindow::onBootFirmware() { // TODO: check the whole GBA cart shito - emuThread->emuPause(true); + emuThread->emuPause(); int res = Frontend::LoadBIOS(); if (res != Frontend::Load_OK) @@ -917,7 +911,7 @@ void MainWindow::onSaveState() { int slot = ((QAction*)sender())->data().toInt(); - emuThread->emuPause(true); + emuThread->emuPause(); char filename[1024]; if (slot > 0) @@ -958,7 +952,7 @@ void MainWindow::onLoadState() { int slot = ((QAction*)sender())->data().toInt(); - emuThread->emuPause(true); + emuThread->emuPause(); char filename[1024]; if (slot > 0) @@ -1006,7 +1000,7 @@ void MainWindow::onLoadState() void MainWindow::onUndoStateLoad() { - emuThread->emuPause(true); + emuThread->emuPause(); Frontend::UndoStateLoad(); emuThread->emuUnpause(); } @@ -1019,9 +1013,11 @@ void MainWindow::onQuit() void MainWindow::onPause(bool checked) { - if (emuThread->emuIsRunning()) + if (!RunningSomething) return; + + if (checked) { - emuThread->emuPause(true); + emuThread->emuPause(); } else { @@ -1031,7 +1027,26 @@ void MainWindow::onPause(bool checked) void MainWindow::onReset() { - // + if (!RunningSomething) return; + + emuThread->emuPause(); + + actUndoStateLoad->setEnabled(false); + + int res = Frontend::Reset(); + if (res != Frontend::Load_OK) + { + QMessageBox::critical(this, + "melonDS", + loadErrorStr(res)); + emuThread->emuUnpause(); + } + else + { + // OSD::AddMessage(0, "Reset"); + + emuThread->emuRun(); + } } void MainWindow::onStop() @@ -1047,7 +1062,7 @@ void MainWindow::onOpenEmuSettings() void MainWindow::onOpenInputConfig() { - emuThread->emuPause(true); + emuThread->emuPause(); InputConfigDialog* dlg = InputConfigDialog::openDlg(this); connect(dlg, &InputConfigDialog::finished, this, &MainWindow::onInputConfigFinished); @@ -1143,7 +1158,7 @@ void MainWindow::onEmuStart() } actSaveState[0]->setEnabled(true); actLoadState[0]->setEnabled(true); - actUndoStateLoad->setEnabled(true); + actUndoStateLoad->setEnabled(false); actPause->setEnabled(true); actPause->setChecked(false); @@ -1165,16 +1180,6 @@ void MainWindow::onEmuStop() actStop->setEnabled(false); } -void MainWindow::onEmuPause() -{ - // -} - -void MainWindow::onEmuUnpause() -{ - // -} - int main(int argc, char** argv) @@ -1281,7 +1286,7 @@ int main(int argc, char** argv) emuThread = new EmuThread(); emuThread->start(); - emuThread->emuPause(true); + emuThread->emuPause(); if (argc > 1) { diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index b478f4e..22f045f 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -38,7 +38,7 @@ public: // to be called from the UI thread void emuRun(); - void emuPause(bool refresh); + void emuPause(); void emuUnpause(); void emuStop(); @@ -49,7 +49,10 @@ signals: void windowEmuStart(); void windowEmuStop(); - void windowPauseToggle(); + void windowEmuPause(); + void windowEmuReset(); + + void windowLimitFPSChange(); private: volatile int EmuStatus; @@ -130,14 +133,13 @@ private slots: void onEmuStart(); void onEmuStop(); - void onEmuPause(); - void onEmuUnpause(); private: QString loadErrorStr(int error); MainWindowPanel* panel; +public: QAction* actOpenROM; QAction* actBootFirmware; QAction* actSaveState[9]; -- cgit v1.2.3 From a9b275bc253510d77b8b6fcd80e6d24b56bc8680 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 20 May 2020 22:58:04 +0200 Subject: reimplement Stop --- src/SPU.cpp | 3 +++ src/frontend/FrontendUtil.h | 4 ++++ src/frontend/Util_ROM.cpp | 14 ++++++++++++++ src/frontend/qt_sdl/Platform.cpp | 4 ++-- src/frontend/qt_sdl/main.cpp | 37 +++++++++++++++++++++++++++++++------ 5 files changed, 54 insertions(+), 8 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/SPU.cpp b/src/SPU.cpp index 710533e..1ebfd4c 100644 --- a/src/SPU.cpp +++ b/src/SPU.cpp @@ -116,6 +116,9 @@ void Reset() void Stop() { memset(OutputBuffer, 0, 2*OutputBufferSize*2); + + OutputReadOffset = 0; + OutputWriteOffset = 0; } void DoSavestate(Savestate* file) diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index d838b63..faa29de 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -65,6 +65,10 @@ int LoadBIOS(); // note: loading a ROM to the NDS slot resets emulation int LoadROM(const char* file, int slot); +// unload the ROM loaded in the specified cart slot +// simulating ejection of the cartridge +void UnloadROM(int slot); + // reset execution of the current ROM int Reset(); diff --git a/src/frontend/Util_ROM.cpp b/src/frontend/Util_ROM.cpp index e13eb2c..ec3186a 100644 --- a/src/frontend/Util_ROM.cpp +++ b/src/frontend/Util_ROM.cpp @@ -202,6 +202,20 @@ int LoadROM(const char* file, int slot) } } +void UnloadROM(int slot) +{ + if (slot == ROMSlot_NDS) + { + // TODO! + } + else if (slot == ROMSlot_GBA) + { + GBACart::Eject(); + } + + ROMPath[slot][0] = '\0'; +} + int Reset() { int res; diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 6a5b466..dd838d7 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -55,7 +55,7 @@ char* EmuDirectory; -void Stop(bool internal); +void emuStop(); namespace Platform @@ -133,7 +133,7 @@ void DeInit() void StopEmu() { - //Stop(true); + emuStop(); } diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index a73362d..67272af 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -115,8 +115,6 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent) connect(this, SIGNAL(windowEmuStop()), mainWindow, SLOT(onEmuStop())); connect(this, SIGNAL(windowEmuPause()), mainWindow->actPause, SLOT(trigger())); connect(this, SIGNAL(windowEmuReset()), mainWindow->actReset, SLOT(trigger())); - - emit windowEmuStop(); } void EmuThread::run() @@ -140,7 +138,6 @@ void EmuThread::run() } Input::Init(); - /*Touching = false;*/ u32 nframes = 0; u32 starttick = SDL_GetTicks(); @@ -376,7 +373,6 @@ void EmuThread::emuPause() EmuRunning = 2; while (EmuStatus != 2); - //emit windowEmuPause(); if (audioDevice) SDL_PauseAudioDevice(audioDevice, 1); } @@ -384,7 +380,6 @@ void EmuThread::emuUnpause() { EmuRunning = PrevEmuStatus; - //emit windowEmuUnpause(); if (audioDevice) SDL_PauseAudioDevice(audioDevice, 0); } @@ -703,6 +698,18 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) panel->setMinimumSize(256, 384); + for (int i = 0; i < 9; i++) + { + actSaveState[i]->setEnabled(false); + actLoadState[i]->setEnabled(false); + } + actUndoStateLoad->setEnabled(false); + + actPause->setEnabled(false); + actReset->setEnabled(false); + actStop->setEnabled(false); + + actSavestateSRAMReloc->setChecked(Config::SavestateRelocSRAM != 0); actScreenRotation[Config::ScreenRotation]->setChecked(true); @@ -1051,7 +1058,10 @@ void MainWindow::onReset() void MainWindow::onStop() { - // + if (!RunningSomething) return; + + emuThread->emuPause(); + NDS::Stop(); } @@ -1168,6 +1178,8 @@ void MainWindow::onEmuStart() void MainWindow::onEmuStop() { + emuThread->emuPause(); + for (int i = 0; i < 9; i++) { actSaveState[i]->setEnabled(false); @@ -1181,6 +1193,19 @@ void MainWindow::onEmuStop() } +void emuStop() +{ + RunningSomething = false; + + Frontend::UnloadROM(Frontend::ROMSlot_NDS); + Frontend::UnloadROM(Frontend::ROMSlot_GBA); + + emit emuThread->windowEmuStop(); + + //OSD::AddMessage(0xFFC040, "Shutdown"); +} + + int main(int argc, char** argv) { -- cgit v1.2.3 From 9e43c85b4d86cdb79242045c5fcf3929e3568322 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 20 May 2020 23:55:18 +0200 Subject: hook up microphone shit. I did my best. --- src/frontend/FrontendUtil.h | 11 +++ src/frontend/Util_Audio.cpp | 64 +++++++++++++++- src/frontend/qt_sdl/main.cpp | 177 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 247 insertions(+), 5 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index faa29de..6a6f8ea 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -101,6 +101,17 @@ int AudioOut_GetNumSamples(int outlen); // note: this assumes the output buffer is interleaved stereo void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen); +// feed silence to the microphone input +void Mic_FeedSilence(); + +// feed random noise to the microphone input +void Mic_FeedNoise(); + +// feed an external buffer to the microphone input +// buffer should be mono +void Mic_FeedExternalBuffer(); +void Mic_SetExternalBuffer(s16* buffer, u32 len); + } #endif // FRONTENDUTIL_H diff --git a/src/frontend/Util_Audio.cpp b/src/frontend/Util_Audio.cpp index fe0ecab..2088efe 100644 --- a/src/frontend/Util_Audio.cpp +++ b/src/frontend/Util_Audio.cpp @@ -17,6 +17,7 @@ */ #include +#include #include #include @@ -26,7 +27,6 @@ #include "Platform.h" #include "NDS.h" -#include "GBACart.h" namespace Frontend @@ -35,13 +35,22 @@ namespace Frontend int AudioOut_Freq; float AudioOut_SampleFrac; +s16* MicBuffer; +u32 MicBufferLength; +u32 MicBufferReadPos; + void Init_Audio(int outputfreq) { AudioOut_Freq = outputfreq; AudioOut_SampleFrac = 0; + + MicBuffer = nullptr; + MicBufferLength = 0; + MicBufferReadPos = 0; } + int AudioOut_GetNumSamples(int outlen) { float f_len_in = (outlen * 32823.6328125) / (float)AudioOut_Freq; @@ -74,4 +83,57 @@ void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen) } } + +void Mic_FeedSilence() +{ + MicBufferReadPos = 0; + NDS::MicInputFrame(NULL, 0); +} + +void Mic_FeedNoise() +{ + // note: DS games seem to expect very saturated 'blowing into mic' noise + + s16 tmp[735]; + + for (int i = 0; i < 735; i++) + { + int val = rand() >> 8; + if (val < -0x8000) val = -0x8000; + else if (val > 0x7FFF) val = 0x7FFF; + + tmp[i] = val; + } + + NDS::MicInputFrame(tmp, 735); +} + +void Mic_FeedExternalBuffer() +{ + if (!MicBuffer) return Mic_FeedSilence(); + + if ((MicBufferReadPos + 735) > MicBufferLength) + { + s16 tmp[735]; + u32 len1 = MicBufferLength - MicBufferReadPos; + memcpy(&tmp[0], &MicBuffer[MicBufferReadPos], len1*sizeof(s16)); + memcpy(&tmp[len1], &MicBuffer[0], (735 - len1)*sizeof(s16)); + + NDS::MicInputFrame(tmp, 735); + MicBufferReadPos = 735 - len1; + } + else + { + NDS::MicInputFrame(&MicBuffer[MicBufferReadPos], 735); + MicBufferReadPos += 735; + } +} + +void Mic_SetExternalBuffer(s16* buffer, u32 len) +{ + MicBuffer = buffer; + MicBufferLength = len; + MicBufferReadPos = 0; +} + } diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 67272af..1615fa3 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -66,6 +66,13 @@ int audioFreq; SDL_cond* audioSync; SDL_mutex* audioSyncLock; +SDL_AudioDeviceID micDevice; +s16 micExtBuffer[2048]; +u32 micExtBufferWritePos; + +u32 micWavLength; +s16* micWavBuffer; + void audioCallback(void* data, Uint8* stream, int len) { @@ -104,6 +111,133 @@ void audioCallback(void* data, Uint8* stream, int len) } +void micLoadWav(const char* name) +{ + SDL_AudioSpec format; + memset(&format, 0, sizeof(SDL_AudioSpec)); + + if (micWavBuffer) delete[] micWavBuffer; + micWavBuffer = nullptr; + micWavLength = 0; + + u8* buf; + u32 len; + if (!SDL_LoadWAV(name, &format, &buf, &len)) + return; + + const u64 dstfreq = 44100; + + if (format.format == AUDIO_S16 || format.format == AUDIO_U16) + { + int srcinc = format.channels; + len /= (2 * srcinc); + + micWavLength = (len * dstfreq) / format.freq; + if (micWavLength < 735) micWavLength = 735; + micWavBuffer = new s16[micWavLength]; + + float res_incr = len / (float)micWavLength; + float res_timer = 0; + int res_pos = 0; + + for (int i = 0; i < micWavLength; i++) + { + u16 val = ((u16*)buf)[res_pos]; + if (SDL_AUDIO_ISUNSIGNED(format.format)) val ^= 0x8000; + + micWavBuffer[i] = val; + + res_timer += res_incr; + while (res_timer >= 1.0) + { + res_timer -= 1.0; + res_pos += srcinc; + } + } + } + else if (format.format == AUDIO_S8 || format.format == AUDIO_U8) + { + int srcinc = format.channels; + len /= srcinc; + + micWavLength = (len * dstfreq) / format.freq; + if (micWavLength < 735) micWavLength = 735; + micWavBuffer = new s16[micWavLength]; + + float res_incr = len / (float)micWavLength; + float res_timer = 0; + int res_pos = 0; + + for (int i = 0; i < micWavLength; i++) + { + u16 val = buf[res_pos] << 8; + if (SDL_AUDIO_ISUNSIGNED(format.format)) val ^= 0x8000; + + micWavBuffer[i] = val; + + res_timer += res_incr; + while (res_timer >= 1.0) + { + res_timer -= 1.0; + res_pos += srcinc; + } + } + } + else + printf("bad WAV format %08X\n", format.format); + + SDL_FreeWAV(buf); +} + +void micCallback(void* data, Uint8* stream, int len) +{ + s16* input = (s16*)stream; + len /= sizeof(s16); + + int maxlen = sizeof(micExtBuffer) / sizeof(s16); + + if ((micExtBufferWritePos + len) > maxlen) + { + u32 len1 = maxlen - micExtBufferWritePos; + memcpy(&micExtBuffer[micExtBufferWritePos], &input[0], len1*sizeof(s16)); + memcpy(&micExtBuffer[0], &input[len1], (len - len1)*sizeof(s16)); + micExtBufferWritePos = len - len1; + } + else + { + memcpy(&micExtBuffer[micExtBufferWritePos], input, len*sizeof(s16)); + micExtBufferWritePos += len; + } +} + +void micProcess() +{ + int type = Config::MicInputType; + bool cmd = Input::HotkeyDown(HK_Mic); + + if (type != 1 && !cmd) + { + type = 0; + } + + switch (type) + { + case 0: // no mic + Frontend::Mic_FeedSilence(); + break; + + case 1: // host mic + case 3: // WAV + Frontend::Mic_FeedExternalBuffer(); + break; + + case 2: // white noise + Frontend::Mic_FeedNoise(); + break; + } +} + + EmuThread::EmuThread(QObject* parent) : QThread(parent) { EmuStatus = 0; @@ -189,9 +323,9 @@ void EmuThread::run() } // microphone input - /*FeedMicInput(); + micProcess(); - if (Screen_UseGL) + /*if (Screen_UseGL) { uiGLBegin(GLContext); uiGLMakeContextCurrent(GLContext); @@ -365,6 +499,7 @@ void EmuThread::emuRun() // checkme emit windowEmuStart(); if (audioDevice) SDL_PauseAudioDevice(audioDevice, 0); + if (micDevice) SDL_PauseAudioDevice(micDevice, 0); } void EmuThread::emuPause() @@ -374,6 +509,7 @@ void EmuThread::emuPause() while (EmuStatus != 2); if (audioDevice) SDL_PauseAudioDevice(audioDevice, 1); + if (micDevice) SDL_PauseAudioDevice(micDevice, 1); } void EmuThread::emuUnpause() @@ -381,6 +517,7 @@ void EmuThread::emuUnpause() EmuRunning = PrevEmuStatus; if (audioDevice) SDL_PauseAudioDevice(audioDevice, 0); + if (micDevice) SDL_PauseAudioDevice(micDevice, 0); } void EmuThread::emuStop() @@ -388,6 +525,7 @@ void EmuThread::emuStop() EmuRunning = 0; if (audioDevice) SDL_PauseAudioDevice(audioDevice, 1); + if (micDevice) SDL_PauseAudioDevice(micDevice, 1); } bool EmuThread::emuIsRunning() @@ -1300,9 +1438,40 @@ int main(int argc, char** argv) SDL_PauseAudioDevice(audioDevice, 1); } + memset(&whatIwant, 0, sizeof(SDL_AudioSpec)); + whatIwant.freq = 44100; + whatIwant.format = AUDIO_S16LSB; + whatIwant.channels = 1; + whatIwant.samples = 1024; + whatIwant.callback = micCallback; + micDevice = SDL_OpenAudioDevice(NULL, 1, &whatIwant, &whatIget, 0); + if (!micDevice) + { + printf("Mic init failed: %s\n", SDL_GetError()); + } + else + { + SDL_PauseAudioDevice(micDevice, 1); + } + + + memset(micExtBuffer, 0, sizeof(micExtBuffer)); + micExtBufferWritePos = 0; + micWavBuffer = nullptr; + Frontend::Init_ROM(); Frontend::Init_Audio(audioFreq); + if (Config::MicInputType == 1) + { + Frontend::Mic_SetExternalBuffer(micExtBuffer, sizeof(micExtBuffer)/sizeof(s16)); + } + else if (Config::MicInputType == 3) + { + micLoadWav(Config::MicWavPath); + Frontend::Mic_SetExternalBuffer(micWavBuffer, micWavLength); + } + Input::JoystickID = Config::JoystickID; Input::OpenJoystick(); @@ -1349,12 +1518,12 @@ int main(int argc, char** argv) Input::CloseJoystick(); if (audioDevice) SDL_CloseAudioDevice(audioDevice); - //if (MicDevice) SDL_CloseAudioDevice(MicDevice); + if (micDevice) SDL_CloseAudioDevice(micDevice); SDL_DestroyCond(audioSync); SDL_DestroyMutex(audioSyncLock); - //if (MicWavBuffer) delete[] MicWavBuffer; + if (micWavBuffer) delete[] micWavBuffer; Config::Save(); -- cgit v1.2.3 From 108647e03320788729bcfa229d81c0f7678fe1fb Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 21 May 2020 01:39:41 +0200 Subject: * add audio settings dialog * attempt at betterer mic noise that doesn't work worth a damn --- src/frontend/Util_Audio.cpp | 8 +- src/frontend/qt_sdl/AudioSettingsDialog.cpp | 103 ++++++++++++++++++ src/frontend/qt_sdl/AudioSettingsDialog.h | 69 ++++++++++++ src/frontend/qt_sdl/AudioSettingsDialog.ui | 162 ++++++++++++++++++++++++++++ src/frontend/qt_sdl/CMakeLists.txt | 1 + src/frontend/qt_sdl/EmuSettingsDialog.cpp | 1 + src/frontend/qt_sdl/EmuSettingsDialog.ui | 15 +-- src/frontend/qt_sdl/main.cpp | 24 ++++- src/frontend/qt_sdl/main.h | 1 + 9 files changed, 366 insertions(+), 18 deletions(-) create mode 100644 src/frontend/qt_sdl/AudioSettingsDialog.cpp create mode 100644 src/frontend/qt_sdl/AudioSettingsDialog.h create mode 100644 src/frontend/qt_sdl/AudioSettingsDialog.ui (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/Util_Audio.cpp b/src/frontend/Util_Audio.cpp index 2088efe..3f03810 100644 --- a/src/frontend/Util_Audio.cpp +++ b/src/frontend/Util_Audio.cpp @@ -93,14 +93,16 @@ void Mic_FeedSilence() void Mic_FeedNoise() { // note: DS games seem to expect very saturated 'blowing into mic' noise + s16 noisesample[8] = {-0x8000, -0x8000, 0x7FFF, -0x8000, 0x7FFF, 0x7FFF, -0x8000, 0x7FFF}; + int j = 0; s16 tmp[735]; for (int i = 0; i < 735; i++) { - int val = rand() >> 8; - if (val < -0x8000) val = -0x8000; - else if (val > 0x7FFF) val = 0x7FFF; + int val = noisesample[j]; + j++; + if (j >= 8) j = rand() & 7; tmp[i] = val; } diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.cpp b/src/frontend/qt_sdl/AudioSettingsDialog.cpp new file mode 100644 index 0000000..2ff5307 --- /dev/null +++ b/src/frontend/qt_sdl/AudioSettingsDialog.cpp @@ -0,0 +1,103 @@ +/* + Copyright 2016-2020 Arisotura + + 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 +#include + +#include "types.h" +#include "Platform.h" +#include "Config.h" +#include "PlatformConfig.h" + +#include "AudioSettingsDialog.h" +#include "ui_AudioSettingsDialog.h" + + +AudioSettingsDialog* AudioSettingsDialog::currentDlg = nullptr; + +extern char* EmuDirectory; + + +AudioSettingsDialog::AudioSettingsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AudioSettingsDialog) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + + oldVolume = Config::AudioVolume; + + ui->slVolume->setValue(Config::AudioVolume); + + grpMicMode = new QButtonGroup(this); + grpMicMode->addButton(ui->rbMicNone, 0); + grpMicMode->addButton(ui->rbMicExternal, 1); + grpMicMode->addButton(ui->rbMicNoise, 2); + grpMicMode->addButton(ui->rbMicWav, 3); + connect(grpMicMode, SIGNAL(buttonClicked(int)), this, SLOT(onChangeMicMode(int))); + grpMicMode->button(Config::MicInputType)->setChecked(true); + + ui->txtMicWavPath->setText(Config::MicWavPath); + + bool iswav = (Config::MicInputType == 3); + ui->txtMicWavPath->setEnabled(iswav); + ui->btnMicWavBrowse->setEnabled(iswav); +} + +AudioSettingsDialog::~AudioSettingsDialog() +{ + delete ui; +} + +void AudioSettingsDialog::on_AudioSettingsDialog_accepted() +{ + Config::MicInputType = grpMicMode->checkedId(); + strncpy(Config::MicWavPath, ui->txtMicWavPath->text().toStdString().c_str(), 1023); Config::MicWavPath[1023] = '\0'; + Config::Save(); + + closeDlg(); +} + +void AudioSettingsDialog::on_AudioSettingsDialog_rejected() +{ + Config::AudioVolume = oldVolume; + + closeDlg(); +} + +void AudioSettingsDialog::on_slVolume_valueChanged(int val) +{ + Config::AudioVolume = val; +} + +void AudioSettingsDialog::onChangeMicMode(int mode) +{ + bool iswav = (mode == 3); + ui->txtMicWavPath->setEnabled(iswav); + ui->btnMicWavBrowse->setEnabled(iswav); +} + +void AudioSettingsDialog::on_btnMicWavBrowse_clicked() +{ + QString file = QFileDialog::getOpenFileName(this, + "Select WAV file...", + EmuDirectory, + "WAV files (*.wav);;Any file (*.*)"); + + if (file.isEmpty()) return; + + ui->txtMicWavPath->setText(file); +} diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.h b/src/frontend/qt_sdl/AudioSettingsDialog.h new file mode 100644 index 0000000..3bafa30 --- /dev/null +++ b/src/frontend/qt_sdl/AudioSettingsDialog.h @@ -0,0 +1,69 @@ +/* + Copyright 2016-2020 Arisotura + + 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 AUDIOSETTINGSDIALOG_H +#define AUDIOSETTINGSDIALOG_H + +#include +#include + +namespace Ui { class AudioSettingsDialog; } +class AudioSettingsDialog; + +class AudioSettingsDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AudioSettingsDialog(QWidget* parent); + ~AudioSettingsDialog(); + + static AudioSettingsDialog* currentDlg; + static AudioSettingsDialog* openDlg(QWidget* parent) + { + if (currentDlg) + { + currentDlg->activateWindow(); + return currentDlg; + } + + currentDlg = new AudioSettingsDialog(parent); + currentDlg->show(); + return currentDlg; + } + static void closeDlg() + { + currentDlg = nullptr; + } + +private slots: + void on_AudioSettingsDialog_accepted(); + void on_AudioSettingsDialog_rejected(); + + void on_slVolume_valueChanged(int val); + void onChangeMicMode(int mode); + void on_btnMicWavBrowse_clicked(); + +private: + Ui::AudioSettingsDialog* ui; + + int oldVolume; + QButtonGroup* grpMicMode; +}; + +#endif // AUDIOSETTINGSDIALOG_H diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.ui b/src/frontend/qt_sdl/AudioSettingsDialog.ui new file mode 100644 index 0000000..b6c215e --- /dev/null +++ b/src/frontend/qt_sdl/AudioSettingsDialog.ui @@ -0,0 +1,162 @@ + + + AudioSettingsDialog + + + + 0 + 0 + 482 + 230 + + + + Audio settings - melonDS + + + + + + Audio output + + + + + + Volume: + + + + + + + <html><head/><body><p>Controls the volume of the audio output.</p></body></html> + + + 256 + + + 16 + + + Qt::Horizontal + + + + + + + + + + Microphone input + + + + + + + 290 + 0 + + + + + + + + <html><head/><body><p>Forward a WAV file to the emulated microphone.</p></body></html> + + + WAV file: + + + + + + + Browse... + + + + + + + <html><head/><body><p>Input from an external microphone, if available, will be forwarded to the emulated microphone.</p></body></html> + + + External microphone + + + + + + + <html><head/><body><p>Noise will be forwarded to the emulated microphone, simulating blowing into the microphone.</p></body></html> + + + White noise + + + + + + + <html><head/><body><p>No microphone input.</p></body></html> + + + None + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + AudioSettingsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + AudioSettingsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index c3d0959..caa78e9 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -4,6 +4,7 @@ SET(SOURCES_QT_SDL main.cpp EmuSettingsDialog.cpp InputConfigDialog.cpp + AudioSettingsDialog.cpp Input.cpp Platform.cpp PlatformConfig.cpp diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.cpp b/src/frontend/qt_sdl/EmuSettingsDialog.cpp index 6264d91..5c2efc0 100644 --- a/src/frontend/qt_sdl/EmuSettingsDialog.cpp +++ b/src/frontend/qt_sdl/EmuSettingsDialog.cpp @@ -100,6 +100,7 @@ void EmuSettingsDialog::on_EmuSettingsDialog_accepted() strncpy(Config::BIOS7Path, ui->txtBIOS7Path->text().toStdString().c_str(), 1023); Config::BIOS7Path[1023] = '\0'; strncpy(Config::FirmwarePath, ui->txtFirmwarePath->text().toStdString().c_str(), 1023); Config::FirmwarePath[1023] = '\0'; Config::DirectBoot = ui->chkDirectBoot->isChecked() ? 1:0; + Config::Save(); closeDlg(); } diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.ui b/src/frontend/qt_sdl/EmuSettingsDialog.ui index e4deaba..c70c3a2 100644 --- a/src/frontend/qt_sdl/EmuSettingsDialog.ui +++ b/src/frontend/qt_sdl/EmuSettingsDialog.ui @@ -7,7 +7,7 @@ 0 0 490 - 243 + 217 @@ -138,19 +138,6 @@ - - - - Qt::Vertical - - - - 20 - 20 - - - - diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 1615fa3..245324f 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -36,6 +36,7 @@ #include "Input.h" #include "EmuSettingsDialog.h" #include "InputConfigDialog.h" +#include "AudioSettingsDialog.h" #include "types.h" #include "version.h" @@ -1228,7 +1229,27 @@ void MainWindow::onOpenVideoSettings() void MainWindow::onOpenAudioSettings() { - // + AudioSettingsDialog* dlg = AudioSettingsDialog::openDlg(this); + connect(dlg, &AudioSettingsDialog::finished, this, &MainWindow::onAudioSettingsFinished); +} + +void MainWindow::onAudioSettingsFinished(int res) +{ + if (Config::MicInputType == 3) + { + micLoadWav(Config::MicWavPath); + Frontend::Mic_SetExternalBuffer(micWavBuffer, micWavLength); + } + else + { + delete[] micWavBuffer; + micWavBuffer = nullptr; + + if (Config::MicInputType == 1) + Frontend::Mic_SetExternalBuffer(micExtBuffer, sizeof(micExtBuffer)/sizeof(s16)); + else + Frontend::Mic_SetExternalBuffer(NULL, 0); + } } void MainWindow::onOpenWifiSettings() @@ -1376,6 +1397,7 @@ int main(int argc, char** argv) #define SANITIZE(var, min, max) { if (var < min) var = min; else if (var > max) var = max; } SANITIZE(Config::AudioVolume, 0, 256); + SANITIZE(Config::MicInputType, 0, 3); SANITIZE(Config::ScreenRotation, 0, 3); SANITIZE(Config::ScreenGap, 0, 500); SANITIZE(Config::ScreenLayout, 0, 2); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 22f045f..7051a08 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -116,6 +116,7 @@ private slots: void onInputConfigFinished(int res); void onOpenVideoSettings(); void onOpenAudioSettings(); + void onAudioSettingsFinished(int res); void onOpenWifiSettings(); void onChangeSavestateSRAMReloc(bool checked); void onChangeScreenSize(); -- cgit v1.2.3 From 8f9369beebf60f3b10d75f50507b194f2ccdaceb Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 21 May 2020 18:43:07 +0200 Subject: add screen layout system --- src/frontend/FrontendUtil.h | 26 +++ src/frontend/Util_Video.cpp | 334 +++++++++++++++++++++++++++++++++++++ src/frontend/qt_sdl/CMakeLists.txt | 1 + src/frontend/qt_sdl/main.cpp | 213 ++++++++++++++++++----- src/frontend/qt_sdl/main.h | 17 +- 5 files changed, 545 insertions(+), 46 deletions(-) create mode 100644 src/frontend/Util_Video.cpp (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index 6a6f8ea..1fd7329 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -89,6 +89,32 @@ bool SaveState(const char* filename); void UndoStateLoad(); +// setup the display layout based on the provided display size and parameters +// * screenWidth/screenHeight: size of the host display +// * screenLayout: how the DS screens are laid out +// 0 = natural (top screen above bottom screen always) +// 1 = vertical +// 2 = horizontal +// * rotation: angle at which the DS screens are presented: 0/1/2/3 = 0/90/180/270 +// * sizing: how the display size is shared between the two screens +// 0 = even (both screens get same size) +// 1 = emphasize top screen (make top screen as big as possible, fit bottom screen in remaining space) +// 2 = emphasize bottom screen +// * screenGap: size of the gap between the two screens +// * integerScale: force screens to be scaled up at integer scaling factors +void SetupScreenLayout(int screenWidth, int screenHeight, int screenLayout, int rotation, int sizing, int screenGap, bool integerScale); + +// get a 2x3 transform matrix for the given screen (0=top, 1=bottom) +// note: the transform assumes an origin point at the top left of the display, +// X going left and Y going down +// for each screen the source coordinates should be (0,0) and (256,192) +float* GetScreenTransform(int screen); + +// de-transform the provided host display coordinates to get coordinates +// on the bottom screen +void GetTouchCoords(int& x, int& y); + + // initialize the audio utility void Init_Audio(int outputfreq); diff --git a/src/frontend/Util_Video.cpp b/src/frontend/Util_Video.cpp new file mode 100644 index 0000000..cd4e21c --- /dev/null +++ b/src/frontend/Util_Video.cpp @@ -0,0 +1,334 @@ +/* + Copyright 2016-2020 Arisotura + + 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 +#include +#include +#include +#include + +#include "FrontendUtil.h" + + +namespace Frontend +{ + +float TopScreenMtx[6]; +float BotScreenMtx[6]; +float TouchMtx[6]; + + +void M23_Identity(float* m) +{ + m[0] = 1; m[1] = 0; + m[2] = 0; m[3] = 1; + m[4] = 0; m[5] = 0; +} + +void M23_Scale(float* m, float s) +{ + m[0] *= s; m[1] *= s; + m[2] *= s; m[3] *= s; + m[4] *= s; m[5] *= s; +} + +void M23_RotateFast(float* m, int angle) +{ + if (angle == 0) return; + + float temp[4]; memcpy(temp, m, sizeof(float)*4); + + switch (angle) + { + case 1: // 90 + m[0] = temp[2]; + m[1] = temp[3]; + m[2] = -temp[0]; + m[3] = -temp[1]; + break; + + case 2: // 180 + m[0] = -temp[0]; + m[1] = -temp[1]; + m[2] = -temp[2]; + m[3] = -temp[3]; + break; + + case 3: // 270 + m[0] = -temp[2]; + m[1] = -temp[3]; + m[2] = temp[0]; + m[3] = temp[1]; + break; + } +} + +void M23_Translate(float* m, float tx, float ty) +{ + m[4] += tx; + m[5] += ty; +} + +void M23_Multiply(float* m, float* _a, float* _b) +{ + float a[6]; memcpy(a, _a, 6*sizeof(float)); + float b[6]; memcpy(b, _b, 6*sizeof(float)); + + m[0] = (a[0] * b[0]) + (a[2] * b[1]); + m[1] = (a[1] * b[0]) + (a[3] * b[1]); + + m[2] = (a[0] * b[2]) + (a[2] * b[3]); + m[3] = (a[1] * b[2]) + (a[3] * b[3]); + + m[4] = (a[0] * b[4]) + (a[2] * b[5]) + a[4]; + m[5] = (a[1] * b[4]) + (a[3] * b[5]) + a[5]; +} + +void M23_Transform(float* m, float& x, float& y) +{ + float vx = x; + float vy = y; + + x = (vx * m[0]) + (vy * m[2]) + m[4]; + y = (vx * m[1]) + (vy * m[3]) + m[5]; +} + + +void SetupScreenLayout(int screenWidth, int screenHeight, int screenLayout, int rotation, int sizing, int screenGap, bool integerScale) +{ + float refpoints[4][2] = + { + {0, 0}, {256, 192}, + {0, 0}, {256, 192} + }; + + int layout = screenLayout == 0 + ? ((rotation % 2 == 0) ? 0 : 1) + : screenLayout - 1; + + float botScale = 1; + float botTrans[4] = {0}; + + M23_Identity(TopScreenMtx); + M23_Identity(BotScreenMtx); + + M23_Translate(TopScreenMtx, -256/2, -192/2); + M23_Translate(BotScreenMtx, -256/2, -192/2); + + // rotation + { + float rotmtx[6]; + M23_Identity(rotmtx); + + M23_RotateFast(rotmtx, rotation); + M23_Multiply(TopScreenMtx, rotmtx, TopScreenMtx); + M23_Multiply(BotScreenMtx, rotmtx, BotScreenMtx); + + M23_Transform(TopScreenMtx, refpoints[0][0], refpoints[0][1]); + M23_Transform(TopScreenMtx, refpoints[1][0], refpoints[1][1]); + M23_Transform(BotScreenMtx, refpoints[2][0], refpoints[2][1]); + M23_Transform(BotScreenMtx, refpoints[3][0], refpoints[3][1]); + } + + // move screens apart + { + int idx = layout == 0 ? 1 : 0; + float offset = + (((layout == 0 && (rotation % 2 == 0)) || (layout == 1 && (rotation % 2 == 1)) + ? 192.f : 256.f) + + screenGap) / 2.f; + if (rotation == 1 || rotation == 2) + offset *= -1.f; + + M23_Translate(TopScreenMtx, (idx==0)?-offset:0, (idx==1)?-offset:0); + M23_Translate(BotScreenMtx, (idx==0)?offset:0, (idx==1)?offset:0); + + refpoints[0][idx] -= offset; + refpoints[1][idx] -= offset; + refpoints[2][idx] += offset; + refpoints[3][idx] += offset; + + botTrans[idx] = offset; + } + + // scale + { + if (sizing == 0) + { + float minX = refpoints[0][0], maxX = minX; + float minY = refpoints[0][1], maxY = minY; + + for (int i = 1; i < 4; i++) + { + minX = std::min(minX, refpoints[i][0]); + minY = std::min(minY, refpoints[i][1]); + maxX = std::max(maxX, refpoints[i][0]); + maxY = std::max(maxY, refpoints[i][1]); + } + + float hSize = maxX - minX; + float vSize = maxY - minY; + + // scale evenly + float scale = std::min(screenWidth / hSize, screenHeight / vSize); + + if (integerScale) + scale = floor(scale); + + M23_Scale(TopScreenMtx, scale); + M23_Scale(BotScreenMtx, scale); + + for (int i = 0; i < 4; i++) + { + refpoints[i][0] *= scale; + refpoints[i][1] *= scale; + } + + botScale = scale; + } + else + { + int primOffset = (sizing == 1) ? 0 : 2; + int secOffset = (sizing == 1) ? 2 : 0; + float* primMtx = (sizing == 1) ? TopScreenMtx : BotScreenMtx; + float* secMtx = (sizing == 1) ? BotScreenMtx : TopScreenMtx; + + float primMinX = refpoints[primOffset][0], primMaxX = primMinX; + float primMinY = refpoints[primOffset][1], primMaxY = primMinY; + float secMinX = refpoints[secOffset][0], secMaxX = secMinX; + float secMinY = refpoints[secOffset][1], secMaxY = secMinY; + + primMinX = std::min(primMinX, refpoints[primOffset+1][0]); + primMinY = std::min(primMinY, refpoints[primOffset+1][1]); + primMaxX = std::max(primMaxX, refpoints[primOffset+1][0]); + primMaxY = std::max(primMaxY, refpoints[primOffset+1][1]); + + secMinX = std::min(secMinX, refpoints[secOffset+1][0]); + secMinY = std::min(secMinY, refpoints[secOffset+1][1]); + secMaxX = std::max(secMaxX, refpoints[secOffset+1][0]); + secMaxY = std::max(secMaxY, refpoints[secOffset+1][1]); + + float primHSize = layout == 1 ? std::max(primMaxX, -primMinX) : primMaxX - primMinX; + float primVSize = layout == 0 ? std::max(primMaxY, -primMinY) : primMaxY - primMinY; + + float secHSize = layout == 1 ? std::max(secMaxX, -secMinX) : secMaxX - secMinX; + float secVSize = layout == 0 ? std::max(secMaxY, -secMinY) : secMaxY - secMinY; + + float primScale = std::min(screenWidth / primHSize, screenHeight / primVSize); + float secScale = 1.f; + + if (layout == 0) + { + if (screenHeight - primVSize * primScale < secVSize) + primScale = std::min((screenWidth - secHSize) / primHSize, (screenHeight - secVSize) / primVSize); + else + secScale = std::min((screenHeight - primVSize * primScale) / secVSize, screenWidth / secHSize); + } + else + { + if (screenWidth - primHSize * primScale < secHSize) + primScale = std::min((screenWidth - secHSize) / primHSize, (screenHeight - secVSize) / primVSize); + else + secScale = std::min((screenWidth - primHSize * primScale) / secHSize, screenHeight / secVSize); + } + + if (integerScale) + { + primScale = floor(primScale); + secScale = floor(secScale); + } + + M23_Scale(primMtx, primScale); + M23_Scale(secMtx, secScale); + + refpoints[primOffset+0][0] *= primScale; + refpoints[primOffset+0][1] *= primScale; + refpoints[primOffset+1][0] *= primScale; + refpoints[primOffset+1][1] *= primScale; + refpoints[secOffset+0][0] *= secScale; + refpoints[secOffset+0][1] *= secScale; + refpoints[secOffset+1][0] *= secScale; + refpoints[secOffset+1][1] *= secScale; + + botScale = (sizing == 1) ? secScale : primScale; + } + } + + // position + { + float minX = refpoints[0][0], maxX = minX; + float minY = refpoints[0][1], maxY = minY; + + for (int i = 1; i < 4; i++) + { + minX = std::min(minX, refpoints[i][0]); + minY = std::min(minY, refpoints[i][1]); + maxX = std::max(maxX, refpoints[i][0]); + maxY = std::max(maxY, refpoints[i][1]); + } + + float width = maxX - minX; + float height = maxY - minY; + + float tx = (screenWidth/2) - (width/2) - minX; + float ty = (screenHeight/2) - (height/2) - minY; + + M23_Translate(TopScreenMtx, tx, ty); + M23_Translate(BotScreenMtx, tx, ty); + + botTrans[2] = tx; botTrans[3] = ty; + } + + // prepare a 'reverse' matrix for the touchscreen + // this matrix undoes the transforms applied to the bottom screen + // and can be used to calculate touchscreen coords from host screen coords + { + M23_Identity(TouchMtx); + + M23_Translate(TouchMtx, -botTrans[2], -botTrans[3]); + M23_Scale(TouchMtx, 1.f / botScale); + M23_Translate(TouchMtx, -botTrans[0], -botTrans[1]); + + float rotmtx[6]; + M23_Identity(rotmtx); + M23_RotateFast(rotmtx, (4-rotation) & 3); + M23_Multiply(TouchMtx, rotmtx, TouchMtx); + + M23_Translate(TouchMtx, 256/2, 192/2); + } +} + +float* GetScreenTransform(int screen) +{ + if (screen == 0) return TopScreenMtx; + else return BotScreenMtx; +} + +void GetTouchCoords(int& x, int& y) +{ + float vx = x; + float vy = y; + + M23_Transform(TouchMtx, vx, vy); + + x = (int)vx; + y = (int)vy; +} + +} + diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index ffd7d17..7bc81f0 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -10,6 +10,7 @@ SET(SOURCES_QT_SDL PlatformConfig.cpp ../Util_ROM.cpp + ../Util_Video.cpp ../Util_Audio.cpp ../FrontendUtil.h ../mic_blow.h diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 245324f..0d10378 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -62,6 +62,8 @@ bool RunningSomething; MainWindow* mainWindow; EmuThread* emuThread; +int autoScreenSizing = 0; + SDL_AudioDeviceID audioDevice; int audioFreq; SDL_cond* audioSync; @@ -250,16 +252,19 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent) connect(this, SIGNAL(windowEmuStop()), mainWindow, SLOT(onEmuStop())); connect(this, SIGNAL(windowEmuPause()), mainWindow->actPause, SLOT(trigger())); connect(this, SIGNAL(windowEmuReset()), mainWindow->actReset, SLOT(trigger())); + connect(this, SIGNAL(screenLayoutChange()), mainWindow->panel, SLOT(onScreenLayoutChanged())); } void EmuThread::run() { + u32 mainScreenPos[3]; + NDS::Init(); - /*MainScreenPos[0] = 0; - MainScreenPos[1] = 0; - MainScreenPos[2] = 0; - AutoScreenSizing = 0;*/ + mainScreenPos[0] = 0; + mainScreenPos[1] = 0; + mainScreenPos[2] = 0; + autoScreenSizing = 0; /*if (Screen_UseGL) { @@ -333,14 +338,14 @@ void EmuThread::run() }*/ // auto screen layout - /*{ - MainScreenPos[2] = MainScreenPos[1]; - MainScreenPos[1] = MainScreenPos[0]; - MainScreenPos[0] = NDS::PowerControl9 >> 15; + { + mainScreenPos[2] = mainScreenPos[1]; + mainScreenPos[1] = mainScreenPos[0]; + mainScreenPos[0] = NDS::PowerControl9 >> 15; int guess; - if (MainScreenPos[0] == MainScreenPos[2] && - MainScreenPos[0] != MainScreenPos[1]) + if (mainScreenPos[0] == mainScreenPos[2] && + mainScreenPos[0] != mainScreenPos[1]) { // constant flickering, likely displaying 3D on both screens // TODO: when both screens are used for 2D only...??? @@ -348,18 +353,18 @@ void EmuThread::run() } else { - if (MainScreenPos[0] == 1) + if (mainScreenPos[0] == 1) guess = 1; else guess = 2; } - if (guess != AutoScreenSizing) + if (guess != autoScreenSizing) { - AutoScreenSizing = guess; - SetupScreenRects(WindowWidth, WindowHeight); + autoScreenSizing = guess; + emit screenLayoutChange(); } - }*/ + } // emulate u32 nlines = NDS::RunFrame(); @@ -540,6 +545,9 @@ MainWindowPanel::MainWindowPanel(QWidget* parent) : QWidget(parent) screen[0] = new QImage(256, 192, QImage::Format_RGB32); screen[1] = new QImage(256, 192, QImage::Format_RGB32); + screenTrans[0].reset(); + screenTrans[1].reset(); + touching = false; } @@ -549,6 +557,64 @@ MainWindowPanel::~MainWindowPanel() delete screen[1]; } +void MainWindowPanel::ensureProperMinSize() +{ + bool isHori = (Config::ScreenRotation == 1 || Config::ScreenRotation == 3); + int gap = Config::ScreenGap; + + int w = 256; + int h = 192; + + if (Config::ScreenLayout == 0) // natural + { + if (isHori) + setMinimumSize(h+gap+h, w); + else + setMinimumSize(w, h+gap+h); + } + else if (Config::ScreenLayout == 1) // vertical + { + if (isHori) + setMinimumSize(h, w+gap+w); + else + setMinimumSize(w, h+gap+h); + } + else // horizontal + { + if (isHori) + setMinimumSize(h+gap+h, w); + else + setMinimumSize(w+gap+w, h); + } +} + +void MainWindowPanel::setupScreenLayout() +{ + int w = width(); + int h = height(); + float* mtx; + + int sizing = Config::ScreenSizing; + if (sizing == 3) sizing = autoScreenSizing; + + Frontend::SetupScreenLayout(w, h, + Config::ScreenLayout, + Config::ScreenRotation, + sizing, + Config::ScreenGap, + Config::IntegerScaling != 0); + + mtx = Frontend::GetScreenTransform(0); + screenTrans[0].setMatrix(mtx[0], mtx[1], 0.f, + mtx[2], mtx[3], 0.f, + mtx[4], mtx[5], 1.f); + + mtx = Frontend::GetScreenTransform(1); + screenTrans[1].setMatrix(mtx[0], mtx[1], 0.f, + mtx[2], mtx[3], 0.f, + mtx[4], mtx[5], 1.f); +} + void MainWindowPanel::paintEvent(QPaintEvent* event) { QPainter painter(this); @@ -562,27 +628,18 @@ void MainWindowPanel::paintEvent(QPaintEvent* event) memcpy(screen[0]->scanLine(0), GPU::Framebuffer[frontbuf][0], 256*192*4); memcpy(screen[1]->scanLine(0), GPU::Framebuffer[frontbuf][1], 256*192*4); - QRect src = QRect(0, 0, 256, 192); + QRect screenrc = QRect(0, 0, 256, 192); - QRect dstTop = QRect(0, 0, 256, 192); // TODO - QRect dstBot = QRect(0, 192, 256, 192); // TODO + painter.setTransform(screenTrans[0]); + painter.drawImage(screenrc, *screen[0]); - painter.drawImage(dstTop, *screen[0]); - painter.drawImage(dstBot, *screen[1]); + painter.setTransform(screenTrans[1]); + painter.drawImage(screenrc, *screen[1]); } - -void MainWindowPanel::transformTSCoords(int& x, int& y) +void MainWindowPanel::resizeEvent(QResizeEvent* event) { - // TODO: actual screen de-transform taking screen layout/rotation/etc into account - - y -= 192; - - // clamp to screen range - if (x < 0) x = 0; - else if (x > 255) x = 255; - if (y < 0) y = 0; - else if (y > 191) y = 191; + setupScreenLayout(); } void MainWindowPanel::mousePressEvent(QMouseEvent* event) @@ -593,11 +650,11 @@ void MainWindowPanel::mousePressEvent(QMouseEvent* event) int x = event->pos().x(); int y = event->pos().y(); - if (x >= 0 && x < 256 && y >= 192 && y < 384) + Frontend::GetTouchCoords(x, y); + + if (x >= 0 && x < 256 && y >= 0 && y < 192) { touching = true; - - transformTSCoords(x, y); NDS::TouchScreen(x, y); } } @@ -623,10 +680,22 @@ void MainWindowPanel::mouseMoveEvent(QMouseEvent* event) int x = event->pos().x(); int y = event->pos().y(); - transformTSCoords(x, y); + Frontend::GetTouchCoords(x, y); + + // clamp to screen range + if (x < 0) x = 0; + else if (x > 255) x = 255; + if (y < 0) y = 0; + else if (y > 191) y = 191; + NDS::TouchScreen(x, y); } +void MainWindowPanel::onScreenLayoutChanged() +{ + setupScreenLayout(); +} + MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) { @@ -750,7 +819,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) int data = i*90; actScreenRotation[i] = submenu->addAction(QString("%1°").arg(data)); actScreenRotation[i]->setActionGroup(grpScreenRotation); - actScreenRotation[i]->setData(QVariant(data)); + actScreenRotation[i]->setData(QVariant(i)); actScreenRotation[i]->setCheckable(true); } @@ -834,8 +903,9 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) panel = new MainWindowPanel(this); setCentralWidget(panel); - panel->setMinimumSize(256, 384); + panel->ensureProperMinSize(); + resize(Config::WindowWidth, Config::WindowHeight); for (int i = 0; i < 9; i++) { @@ -877,6 +947,16 @@ MainWindow::~MainWindow() { } +void MainWindow::resizeEvent(QResizeEvent* event) +{ + int w = event->size().width(); + int h = event->size().height(); + + Config::WindowWidth = w; + Config::WindowHeight = h; + + // TODO: detect when the window gets maximized! +} void MainWindow::keyPressEvent(QKeyEvent* event) { @@ -1264,32 +1344,79 @@ void MainWindow::onChangeSavestateSRAMReloc(bool checked) void MainWindow::onChangeScreenSize() { - // + int factor = ((QAction*)sender())->data().toInt(); + + bool isHori = (Config::ScreenRotation == 1 || Config::ScreenRotation == 3); + int gap = Config::ScreenGap; + + int w = 256*factor; + int h = 192*factor; + + QSize diff = size() - panel->size(); + + if (Config::ScreenLayout == 0) // natural + { + if (isHori) + resize(QSize(h+gap+h, w) + diff); + else + resize(QSize(w, h+gap+h) + diff); + } + else if (Config::ScreenLayout == 1) // vertical + { + if (isHori) + resize(QSize(h, w+gap+w) + diff); + else + resize(QSize(w, h+gap+h) + diff); + } + else // horizontal + { + if (isHori) + resize(QSize(h+gap+h, w) + diff); + else + resize(QSize(w+gap+w, h) + diff); + } } void MainWindow::onChangeScreenRotation(QAction* act) { - // + int rot = act->data().toInt(); + Config::ScreenRotation = rot; + + panel->ensureProperMinSize(); + panel->setupScreenLayout(); } void MainWindow::onChangeScreenGap(QAction* act) { - // + int gap = act->data().toInt(); + Config::ScreenGap = gap; + + panel->ensureProperMinSize(); + panel->setupScreenLayout(); } void MainWindow::onChangeScreenLayout(QAction* act) { - // + int layout = act->data().toInt(); + Config::ScreenLayout = layout; + + panel->ensureProperMinSize(); + panel->setupScreenLayout(); } void MainWindow::onChangeScreenSizing(QAction* act) { - // + int sizing = act->data().toInt(); + Config::ScreenSizing = sizing; + + panel->setupScreenLayout(); } void MainWindow::onChangeIntegerScaling(bool checked) { - // + Config::IntegerScaling = checked?1:0; + + panel->setupScreenLayout(); } void MainWindow::onChangeScreenFiltering(bool checked) diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 7051a08..2d14eea 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -54,6 +54,8 @@ signals: void windowLimitFPSChange(); + void screenLayoutChange(); + private: volatile int EmuStatus; int PrevEmuStatus; @@ -69,18 +71,25 @@ public: explicit MainWindowPanel(QWidget* parent); ~MainWindowPanel(); + void ensureProperMinSize(); + void setupScreenLayout(); + protected: void paintEvent(QPaintEvent* event) override; + void resizeEvent(QResizeEvent* event) override; + void mousePressEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; +private slots: + void onScreenLayoutChanged(); + private: QImage* screen[2]; + QTransform screenTrans[2]; bool touching; - - void transformTSCoords(int& x, int& y); }; @@ -93,6 +102,8 @@ public: ~MainWindow(); protected: + void resizeEvent(QResizeEvent* event) override; + void keyPressEvent(QKeyEvent* event) override; void keyReleaseEvent(QKeyEvent* event) override; @@ -138,9 +149,9 @@ private slots: private: QString loadErrorStr(int error); +public: MainWindowPanel* panel; -public: QAction* actOpenROM; QAction* actBootFirmware; QAction* actSaveState[9]; -- cgit v1.2.3 From 5dcf57e86dc8e768d33406fb446f25bf186eef4d Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 21 May 2020 18:49:34 +0200 Subject: add screen filtering --- src/frontend/qt_sdl/main.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 0d10378..36a3b8e 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -628,6 +628,8 @@ void MainWindowPanel::paintEvent(QPaintEvent* event) memcpy(screen[0]->scanLine(0), GPU::Framebuffer[frontbuf][0], 256*192*4); memcpy(screen[1]->scanLine(0), GPU::Framebuffer[frontbuf][1], 256*192*4); + painter.setRenderHint(QPainter::SmoothPixmapTransform, Config::ScreenFilter!=0); + QRect screenrc = QRect(0, 0, 256, 192); painter.setTransform(screenTrans[0]); -- cgit v1.2.3 From f69f3fcb7a6f5eabe07de3db58409aee036348ea Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 22 May 2020 13:54:29 +0200 Subject: * safer window update * only do auto screen sizing if needed --- src/frontend/qt_sdl/main.cpp | 9 ++++++--- src/frontend/qt_sdl/main.h | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 36a3b8e..922206f 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -247,6 +247,8 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent) EmuRunning = 2; RunningSomething = false; + connect(this, SIGNAL(windowUpdate()), mainWindow, SLOT(update())); + //connect(this, SIGNAL(windowUpdate()), mainWindow, 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())); @@ -338,6 +340,7 @@ void EmuThread::run() }*/ // auto screen layout + if (Config::ScreenSizing == 3) { mainScreenPos[2] = mainScreenPos[1]; mainScreenPos[1] = mainScreenPos[0]; @@ -381,7 +384,7 @@ void EmuThread::run() uiGLEnd(GLContext); } uiAreaQueueRedrawAll(MainDrawArea);*/ - mainWindow->update(); + emit windowUpdate(); bool fastforward = Input::HotkeyDown(HK_FastForward); @@ -461,7 +464,7 @@ void EmuThread::run() uiGLEnd(GLContext); } uiAreaQueueRedrawAll(MainDrawArea);*/ - mainWindow->update(); + emit windowUpdate(); //if (Screen_UseGL) uiGLMakeContextCurrent(NULL); @@ -630,7 +633,7 @@ void MainWindowPanel::paintEvent(QPaintEvent* event) painter.setRenderHint(QPainter::SmoothPixmapTransform, Config::ScreenFilter!=0); - QRect screenrc = QRect(0, 0, 256, 192); + QRect screenrc(0, 0, 256, 192); painter.setTransform(screenTrans[0]); painter.drawImage(screenrc, *screen[0]); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 2d14eea..c0dc465 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -45,6 +45,7 @@ public: bool emuIsRunning(); signals: + void windowUpdate(); void windowTitleChange(QString title); void windowEmuStart(); -- cgit v1.2.3 From 16252a85e70feaa1bd10312a357ac74bd677ade3 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 24 May 2020 23:16:56 +0200 Subject: separate screen handling shit to a specialized class --- src/frontend/FrontendUtil.h | 5 +- src/frontend/Util_Video.cpp | 6 +- src/frontend/qt_sdl/main.cpp | 168 +++++++++++++++++++++++++------------------ src/frontend/qt_sdl/main.h | 22 +++++- 4 files changed, 122 insertions(+), 79 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index 1fd7329..359018f 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -104,11 +104,12 @@ void UndoStateLoad(); // * integerScale: force screens to be scaled up at integer scaling factors void SetupScreenLayout(int screenWidth, int screenHeight, int screenLayout, int rotation, int sizing, int screenGap, bool integerScale); -// get a 2x3 transform matrix for the given screen (0=top, 1=bottom) +// get a 2x3 transform matrix for each screen // note: the transform assumes an origin point at the top left of the display, // X going left and Y going down // for each screen the source coordinates should be (0,0) and (256,192) -float* GetScreenTransform(int screen); +// 'top' and 'bot' should point each to an array of 6 floats +void GetScreenTransforms(float* top, float* bot); // de-transform the provided host display coordinates to get coordinates // on the bottom screen diff --git a/src/frontend/Util_Video.cpp b/src/frontend/Util_Video.cpp index 0f5ff66..87cb9b5 100644 --- a/src/frontend/Util_Video.cpp +++ b/src/frontend/Util_Video.cpp @@ -313,10 +313,10 @@ void SetupScreenLayout(int screenWidth, int screenHeight, int screenLayout, int } } -float* GetScreenTransform(int screen) +void GetScreenTransforms(float* top, float* bot) { - if (screen == 0) return TopScreenMtx; - else return BotScreenMtx; + memcpy(top, TopScreenMtx, 6*sizeof(float)); + memcpy(bot, BotScreenMtx, 6*sizeof(float)); } void GetTouchCoords(int& x, int& y) diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 922206f..0e0a21b 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -543,24 +543,22 @@ bool EmuThread::emuIsRunning() } -MainWindowPanel::MainWindowPanel(QWidget* parent) : QWidget(parent) +void ScreenHandler::screenSetupLayout(int w, int h) { - screen[0] = new QImage(256, 192, QImage::Format_RGB32); - screen[1] = new QImage(256, 192, QImage::Format_RGB32); - - screenTrans[0].reset(); - screenTrans[1].reset(); + int sizing = Config::ScreenSizing; + if (sizing == 3) sizing = autoScreenSizing; - touching = false; -} + Frontend::SetupScreenLayout(w, h, + Config::ScreenLayout, + Config::ScreenRotation, + sizing, + Config::ScreenGap, + Config::IntegerScaling != 0); -MainWindowPanel::~MainWindowPanel() -{ - delete screen[0]; - delete screen[1]; + Frontend::GetScreenTransforms(screenMatrix[0], screenMatrix[1]); } -void MainWindowPanel::ensureProperMinSize() +QSize ScreenHandler::screenGetMinSize() { bool isHori = (Config::ScreenRotation == 1 || Config::ScreenRotation == 3); int gap = Config::ScreenGap; @@ -571,48 +569,110 @@ void MainWindowPanel::ensureProperMinSize() if (Config::ScreenLayout == 0) // natural { if (isHori) - setMinimumSize(h+gap+h, w); + return QSize(h+gap+h, w); else - setMinimumSize(w, h+gap+h); + return QSize(w, h+gap+h); } else if (Config::ScreenLayout == 1) // vertical { if (isHori) - setMinimumSize(h, w+gap+w); + return QSize(h, w+gap+w); else - setMinimumSize(w, h+gap+h); + return QSize(w, h+gap+h); } else // horizontal { if (isHori) - setMinimumSize(h+gap+h, w); + return QSize(h+gap+h, w); else - setMinimumSize(w+gap+w, h); + return QSize(w+gap+w, h); + } +} + +void ScreenHandler::screenOnMousePress(QMouseEvent* event) +{ + event->accept(); + if (event->button() != Qt::LeftButton) return; + + int x = event->pos().x(); + int y = event->pos().y(); + + Frontend::GetTouchCoords(x, y); + + if (x >= 0 && x < 256 && y >= 0 && y < 192) + { + touching = true; + NDS::TouchScreen(x, y); + } +} + +void ScreenHandler::screenOnMouseRelease(QMouseEvent* event) +{ + event->accept(); + if (event->button() != Qt::LeftButton) return; + + if (touching) + { + touching = false; + NDS::ReleaseScreen(); } } +void ScreenHandler::screenOnMouseMove(QMouseEvent* event) +{ + event->accept(); + if (!(event->buttons() & Qt::LeftButton)) return; + if (!touching) return; + + int x = event->pos().x(); + int y = event->pos().y(); + + Frontend::GetTouchCoords(x, y); + + // clamp to screen range + if (x < 0) x = 0; + else if (x > 255) x = 255; + if (y < 0) y = 0; + else if (y > 191) y = 191; + + NDS::TouchScreen(x, y); +} + + +MainWindowPanel::MainWindowPanel(QWidget* parent) : QWidget(parent) +{ + screen[0] = QImage(256, 192, QImage::Format_RGB32); + screen[1] = QImage(256, 192, QImage::Format_RGB32); + + screenTrans[0].reset(); + screenTrans[1].reset(); + + touching = false; +} + +MainWindowPanel::~MainWindowPanel() +{ +} + +void MainWindowPanel::ensureProperMinSize() +{ + setMinimumSize(screenGetMinSize()); +} + void MainWindowPanel::setupScreenLayout() { int w = width(); int h = height(); float* mtx; - int sizing = Config::ScreenSizing; - if (sizing == 3) sizing = autoScreenSizing; - - Frontend::SetupScreenLayout(w, h, - Config::ScreenLayout, - Config::ScreenRotation, - sizing, - Config::ScreenGap, - Config::IntegerScaling != 0); + screenSetupLayout(w, h); - mtx = Frontend::GetScreenTransform(0); + mtx = screenMatrix[0]; screenTrans[0].setMatrix(mtx[0], mtx[1], 0.f, mtx[2], mtx[3], 0.f, mtx[4], mtx[5], 1.f); - mtx = Frontend::GetScreenTransform(1); + mtx = screenMatrix[1]; screenTrans[1].setMatrix(mtx[0], mtx[1], 0.f, mtx[2], mtx[3], 0.f, mtx[4], mtx[5], 1.f); @@ -628,18 +688,18 @@ void MainWindowPanel::paintEvent(QPaintEvent* event) int frontbuf = GPU::FrontBuffer; if (!GPU::Framebuffer[frontbuf][0] || !GPU::Framebuffer[frontbuf][1]) return; - memcpy(screen[0]->scanLine(0), GPU::Framebuffer[frontbuf][0], 256*192*4); - memcpy(screen[1]->scanLine(0), GPU::Framebuffer[frontbuf][1], 256*192*4); + memcpy(screen[0].scanLine(0), GPU::Framebuffer[frontbuf][0], 256*192*4); + memcpy(screen[1].scanLine(0), GPU::Framebuffer[frontbuf][1], 256*192*4); painter.setRenderHint(QPainter::SmoothPixmapTransform, Config::ScreenFilter!=0); QRect screenrc(0, 0, 256, 192); painter.setTransform(screenTrans[0]); - painter.drawImage(screenrc, *screen[0]); + painter.drawImage(screenrc, screen[0]); painter.setTransform(screenTrans[1]); - painter.drawImage(screenrc, *screen[1]); + painter.drawImage(screenrc, screen[1]); } void MainWindowPanel::resizeEvent(QResizeEvent* event) @@ -649,51 +709,17 @@ void MainWindowPanel::resizeEvent(QResizeEvent* event) void MainWindowPanel::mousePressEvent(QMouseEvent* event) { - event->accept(); - if (event->button() != Qt::LeftButton) return; - - int x = event->pos().x(); - int y = event->pos().y(); - - Frontend::GetTouchCoords(x, y); - - if (x >= 0 && x < 256 && y >= 0 && y < 192) - { - touching = true; - NDS::TouchScreen(x, y); - } + screenOnMousePress(event); } void MainWindowPanel::mouseReleaseEvent(QMouseEvent* event) { - event->accept(); - if (event->button() != Qt::LeftButton) return; - - if (touching) - { - touching = false; - NDS::ReleaseScreen(); - } + screenOnMouseRelease(event); } void MainWindowPanel::mouseMoveEvent(QMouseEvent* event) { - event->accept(); - if (!(event->buttons() & Qt::LeftButton)) return; - if (!touching) return; - - int x = event->pos().x(); - int y = event->pos().y(); - - Frontend::GetTouchCoords(x, y); - - // clamp to screen range - if (x < 0) x = 0; - else if (x > 255) x = 255; - if (y < 0) y = 0; - else if (y > 191) y = 191; - - NDS::TouchScreen(x, y); + screenOnMouseMove(event); } void MainWindowPanel::onScreenLayoutChanged() diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index c0dc465..1121da1 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -64,7 +64,24 @@ private: }; -class MainWindowPanel : public QWidget +class ScreenHandler +{ +protected: + void screenSetupLayout(int w, int h); + + QSize screenGetMinSize(); + + void screenOnMousePress(QMouseEvent* event); + void screenOnMouseRelease(QMouseEvent* event); + void screenOnMouseMove(QMouseEvent* event); + + float screenMatrix[2][6]; + + bool touching; +}; + + +class MainWindowPanel : public QWidget, public ScreenHandler { Q_OBJECT @@ -88,9 +105,8 @@ private slots: void onScreenLayoutChanged(); private: - QImage* screen[2]; + QImage screen[2]; QTransform screenTrans[2]; - bool touching; }; -- cgit v1.2.3 From bc4a83abca991effe082f76f81cd6b4eef6ef0ba Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 24 May 2020 23:47:11 +0200 Subject: make frontend-util audio module config-agnostic --- src/frontend/FrontendUtil.h | 4 ++-- src/frontend/Util_Audio.cpp | 7 +------ src/frontend/qt_sdl/main.cpp | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index 359018f..6b83cbc 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -124,9 +124,9 @@ void Init_Audio(int outputfreq); int AudioOut_GetNumSamples(int outlen); // resample audio from the core audio output to match the frontend's -// output frequency, and apply user-specified volume +// output frequency, and apply specified volume // note: this assumes the output buffer is interleaved stereo -void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen); +void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen, int volume); // feed silence to the microphone input void Mic_FeedSilence(); diff --git a/src/frontend/Util_Audio.cpp b/src/frontend/Util_Audio.cpp index d4c3333..fc80c90 100644 --- a/src/frontend/Util_Audio.cpp +++ b/src/frontend/Util_Audio.cpp @@ -22,9 +22,6 @@ #include #include "FrontendUtil.h" -#include "Config.h" -#include "qt_sdl/PlatformConfig.h" // FIXME!!! -#include "Platform.h" #include "NDS.h" @@ -63,14 +60,12 @@ int AudioOut_GetNumSamples(int outlen) return len_in; } -void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen) +void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen, int volume) { float res_incr = inlen / (float)outlen; float res_timer = 0; int res_pos = 0; - int volume = Config::AudioVolume; - for (int i = 0; i < outlen; i++) { outbuf[i*2 ] = (inbuf[res_pos*2 ] * volume) >> 8; diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 0e0a21b..fac4709 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -110,7 +110,7 @@ void audioCallback(void* data, Uint8* stream, int len) num_in = len_in-margin; } - Frontend::AudioOut_Resample(buf_in, num_in, (s16*)stream, len); + Frontend::AudioOut_Resample(buf_in, num_in, (s16*)stream, len, Config::AudioVolume); } -- cgit v1.2.3 From 4e34359a80833c21ef6a8fabd35c4d26babcaaab Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 25 May 2020 03:12:09 +0200 Subject: get the GL shit going --- src/frontend/qt_sdl/CMakeLists.txt | 1 + src/frontend/qt_sdl/main.cpp | 91 ++++++++++++++++++++------- src/frontend/qt_sdl/main.h | 49 ++++++++++++--- src/frontend/qt_sdl/main_shaders.h | 124 +++++++++++++++++++++++++++++++++++++ 4 files changed, 234 insertions(+), 31 deletions(-) create mode 100644 src/frontend/qt_sdl/main_shaders.h (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 7bc81f0..5778b40 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -2,6 +2,7 @@ project(qt_sdl) SET(SOURCES_QT_SDL main.cpp + main_shaders.h EmuSettingsDialog.cpp InputConfigDialog.cpp AudioSettingsDialog.cpp diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index fac4709..b11dd10 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -639,7 +639,7 @@ void ScreenHandler::screenOnMouseMove(QMouseEvent* event) } -MainWindowPanel::MainWindowPanel(QWidget* parent) : QWidget(parent) +ScreenPanelNative::ScreenPanelNative(QWidget* parent) : QWidget(parent) { screen[0] = QImage(256, 192, QImage::Format_RGB32); screen[1] = QImage(256, 192, QImage::Format_RGB32); @@ -650,16 +650,11 @@ MainWindowPanel::MainWindowPanel(QWidget* parent) : QWidget(parent) touching = false; } -MainWindowPanel::~MainWindowPanel() +ScreenPanelNative::~ScreenPanelNative() { } -void MainWindowPanel::ensureProperMinSize() -{ - setMinimumSize(screenGetMinSize()); -} - -void MainWindowPanel::setupScreenLayout() +void ScreenPanelNative::setupScreenLayout() { int w = width(); int h = height(); @@ -678,7 +673,7 @@ void MainWindowPanel::setupScreenLayout() mtx[4], mtx[5], 1.f); } -void MainWindowPanel::paintEvent(QPaintEvent* event) +void ScreenPanelNative::paintEvent(QPaintEvent* event) { QPainter painter(this); @@ -702,28 +697,78 @@ void MainWindowPanel::paintEvent(QPaintEvent* event) painter.drawImage(screenrc, screen[1]); } -void MainWindowPanel::resizeEvent(QResizeEvent* event) +void ScreenPanelNative::resizeEvent(QResizeEvent* event) +{ + setupScreenLayout(); +} + +void ScreenPanelNative::mousePressEvent(QMouseEvent* event) +{ + screenOnMousePress(event); +} + +void ScreenPanelNative::mouseReleaseEvent(QMouseEvent* event) +{ + screenOnMouseRelease(event); +} + +void ScreenPanelNative::mouseMoveEvent(QMouseEvent* event) +{ + screenOnMouseMove(event); +} + +void ScreenPanelNative::onScreenLayoutChanged() +{ + setMinimumSize(screenGetMinSize()); + setupScreenLayout(); +} + + +ScreenPanelGL::ScreenPanelGL(QWidget* parent) : QOpenGLWidget(parent) +{ + // +} + +ScreenPanelGL::~ScreenPanelGL() +{ +} + +void ScreenPanelGL::setupScreenLayout() +{ + int w = width(); + int h = height(); + + screenSetupLayout(w, h); +} + +void ScreenPanelGL::paintEvent(QPaintEvent* event) +{ + // TODO? +} + +void ScreenPanelGL::resizeEvent(QResizeEvent* event) { setupScreenLayout(); } -void MainWindowPanel::mousePressEvent(QMouseEvent* event) +void ScreenPanelGL::mousePressEvent(QMouseEvent* event) { screenOnMousePress(event); } -void MainWindowPanel::mouseReleaseEvent(QMouseEvent* event) +void ScreenPanelGL::mouseReleaseEvent(QMouseEvent* event) { screenOnMouseRelease(event); } -void MainWindowPanel::mouseMoveEvent(QMouseEvent* event) +void ScreenPanelGL::mouseMoveEvent(QMouseEvent* event) { screenOnMouseMove(event); } -void MainWindowPanel::onScreenLayoutChanged() +void ScreenPanelGL::onScreenLayoutChanged() { + setMinimumSize(screenGetMinSize()); setupScreenLayout(); } @@ -932,9 +977,10 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) } setMenuBar(menubar); - panel = new MainWindowPanel(this); + panel = new ScreenPanelNative(this); setCentralWidget(panel); - panel->ensureProperMinSize(); + connect(this, SIGNAL(screenLayoutChange()), panel, SLOT(onScreenLayoutChanged())); + emit screenLayoutChange(); resize(Config::WindowWidth, Config::WindowHeight); @@ -1413,8 +1459,7 @@ void MainWindow::onChangeScreenRotation(QAction* act) int rot = act->data().toInt(); Config::ScreenRotation = rot; - panel->ensureProperMinSize(); - panel->setupScreenLayout(); + emit screenLayoutChange(); } void MainWindow::onChangeScreenGap(QAction* act) @@ -1422,8 +1467,7 @@ void MainWindow::onChangeScreenGap(QAction* act) int gap = act->data().toInt(); Config::ScreenGap = gap; - panel->ensureProperMinSize(); - panel->setupScreenLayout(); + emit screenLayoutChange(); } void MainWindow::onChangeScreenLayout(QAction* act) @@ -1431,8 +1475,7 @@ void MainWindow::onChangeScreenLayout(QAction* act) int layout = act->data().toInt(); Config::ScreenLayout = layout; - panel->ensureProperMinSize(); - panel->setupScreenLayout(); + emit screenLayoutChange(); } void MainWindow::onChangeScreenSizing(QAction* act) @@ -1440,14 +1483,14 @@ void MainWindow::onChangeScreenSizing(QAction* act) int sizing = act->data().toInt(); Config::ScreenSizing = sizing; - panel->setupScreenLayout(); + emit screenLayoutChange(); } void MainWindow::onChangeIntegerScaling(bool checked) { Config::IntegerScaling = checked?1:0; - panel->setupScreenLayout(); + emit screenLayoutChange(); } void MainWindow::onChangeScreenFiltering(bool checked) diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 1121da1..a244907 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -24,6 +24,7 @@ #include #include #include +#include class EmuThread : public QThread @@ -66,6 +67,11 @@ private: class ScreenHandler { + Q_GADGET + +public: + virtual ~ScreenHandler() {} + protected: void screenSetupLayout(int w, int h); @@ -81,16 +87,13 @@ protected: }; -class MainWindowPanel : public QWidget, public ScreenHandler +class ScreenPanelNative : public QWidget, public ScreenHandler { Q_OBJECT public: - explicit MainWindowPanel(QWidget* parent); - ~MainWindowPanel(); - - void ensureProperMinSize(); - void setupScreenLayout(); + explicit ScreenPanelNative(QWidget* parent); + ~ScreenPanelNative(); protected: void paintEvent(QPaintEvent* event) override; @@ -105,11 +108,40 @@ private slots: void onScreenLayoutChanged(); private: + void setupScreenLayout(); + QImage screen[2]; QTransform screenTrans[2]; }; +class ScreenPanelGL : public QOpenGLWidget, public ScreenHandler +{ + Q_OBJECT + +public: + explicit ScreenPanelGL(QWidget* parent); + ~ScreenPanelGL(); + +protected: + void paintEvent(QPaintEvent* event) override; + + void resizeEvent(QResizeEvent* event) override; + + void mousePressEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + +private slots: + void onScreenLayoutChanged(); + +private: + void setupScreenLayout(); + + // +}; + + class MainWindow : public QMainWindow { Q_OBJECT @@ -127,6 +159,9 @@ protected: void dragEnterEvent(QDragEnterEvent* event) override; void dropEvent(QDropEvent* event) override; +signals: + void screenLayoutChange(); + private slots: void onOpenFile(); void onBootFirmware(); @@ -167,7 +202,7 @@ private: QString loadErrorStr(int error); public: - MainWindowPanel* panel; + QWidget* panel; QAction* actOpenROM; QAction* actBootFirmware; diff --git a/src/frontend/qt_sdl/main_shaders.h b/src/frontend/qt_sdl/main_shaders.h new file mode 100644 index 0000000..7c4feec --- /dev/null +++ b/src/frontend/qt_sdl/main_shaders.h @@ -0,0 +1,124 @@ +/* + Copyright 2016-2020 Arisotura + + 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 MAIN_SHADERS_H +#define MAIN_SHADERS_H + +const char* kScreenVS = R"(#version 140 + +layout(std140) uniform uConfig +{ + vec2 uScreenSize; + uint u3DScale; + uint uFilterMode; +}; + +in vec2 vPosition; +in vec2 vTexcoord; + +smooth out vec2 fTexcoord; + +void main() +{ + vec4 fpos; + fpos.xy = ((vPosition * 2.0) / uScreenSize) - 1.0; + fpos.y *= -1; + fpos.z = 0.0; + fpos.w = 1.0; + + gl_Position = fpos; + fTexcoord = vTexcoord; +} +)"; + +const char* kScreenFS = R"(#version 140 + +layout(std140) uniform uConfig +{ + vec2 uScreenSize; + uint u3DScale; + uint uFilterMode; +}; + +uniform usampler2D ScreenTex; + +smooth in vec2 fTexcoord; + +out vec4 oColor; + +void main() +{ + ivec4 pixel = ivec4(texelFetch(ScreenTex, ivec2(fTexcoord), 0)); + + // TODO: filters + + oColor = vec4(vec3(pixel.bgr) / 255.0, 1.0); +} +)"; + + + +const char* kScreenVS_OSD = R"(#version 140 + +layout(std140) uniform uConfig +{ + vec2 uScreenSize; + uint u3DScale; + uint uFilterMode; +}; + +uniform ivec2 uOSDPos; +uniform ivec2 uOSDSize; + +in vec2 vPosition; + +smooth out vec2 fTexcoord; + +void main() +{ + vec4 fpos; + + vec2 osdpos = (vPosition * vec2(uOSDSize)); + fTexcoord = osdpos; + osdpos += uOSDPos; + + fpos.xy = ((osdpos * 2.0) / uScreenSize) - 1.0; + fpos.y *= -1; + fpos.z = 0.0; + fpos.w = 1.0; + + gl_Position = fpos; +} +)"; + +const char* kScreenFS_OSD = R"(#version 140 + +uniform sampler2D OSDTex; + +smooth in vec2 fTexcoord; + +out vec4 oColor; + +void main() +{ + vec4 pixel = texelFetch(OSDTex, ivec2(fTexcoord), 0); + oColor = pixel.bgra; +} +)"; + +#endif // MAIN_SHADERS_H -- cgit v1.2.3 From 10f9eda58a7bbc9becb186ea2bdf1c9b7d2c1bc1 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 25 May 2020 14:59:26 +0200 Subject: get the whole OpenGL shit going --- src/frontend/qt_sdl/main.cpp | 127 +++++++++++++++++++++++++++++++++++-- src/frontend/qt_sdl/main.h | 17 ++++- src/frontend/qt_sdl/main_shaders.h | 28 +++----- 3 files changed, 145 insertions(+), 27 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index b11dd10..bb08a87 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -54,6 +54,8 @@ #include "Savestate.h" +#include "main_shaders.h" + // TODO: uniform variable spelling @@ -247,7 +249,8 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent) EmuRunning = 2; RunningSomething = false; - connect(this, SIGNAL(windowUpdate()), mainWindow, SLOT(update())); + //connect(this, SIGNAL(windowUpdate()), mainWindow, SLOT(update())); + connect(this, SIGNAL(windowUpdate()), mainWindow->panel, SLOT(update())); //connect(this, SIGNAL(windowUpdate()), mainWindow, SLOT(repaint())); connect(this, SIGNAL(windowTitleChange(QString)), mainWindow, SLOT(onTitleUpdate(QString))); connect(this, SIGNAL(windowEmuStart()), mainWindow, SLOT(onEmuStart())); @@ -726,11 +729,18 @@ void ScreenPanelNative::onScreenLayoutChanged() ScreenPanelGL::ScreenPanelGL(QWidget* parent) : QOpenGLWidget(parent) { - // + QSurfaceFormat format; + format.setDepthBufferSize(24); + format.setStencilBufferSize(8); + format.setVersion(3, 2); + format.setProfile(QSurfaceFormat::CoreProfile); + setFormat(format); } ScreenPanelGL::~ScreenPanelGL() { + // CHECKME!!!! + delete screenShader; } void ScreenPanelGL::setupScreenLayout() @@ -741,14 +751,120 @@ void ScreenPanelGL::setupScreenLayout() screenSetupLayout(w, h); } -void ScreenPanelGL::paintEvent(QPaintEvent* event) +void ScreenPanelGL::initializeGL() { - // TODO? + initializeOpenGLFunctions(); + + glClearColor(0, 0, 0, 1); + + screenShader = new QOpenGLShaderProgram(this); + screenShader->addShaderFromSourceCode(QOpenGLShader::Vertex, kScreenVS); + screenShader->addShaderFromSourceCode(QOpenGLShader::Fragment, kScreenFS); + + GLuint pid = screenShader->programId(); + printf("program: %d\n", pid); + glBindAttribLocation(pid, 0, "vPosition"); + glBindAttribLocation(pid, 1, "vTexcoord"); + glBindFragDataLocation(pid, 0, "oColor"); + + screenShader->link(); + + screenShader->bind(); + screenShader->setUniformValue("ScreenTex", (GLint)0); + screenShader->release(); + + + float vertices[] = + { + 0, 0, 0, 0, + 0, 192, 0, 0.5, + 256, 192, 1, 0.5, + 0, 0, 0, 0, + 256, 192, 1, 0.5, + 256, 0, 1, 0, + + 0, 0, 0, 0.5, + 0, 192, 0, 1, + 256, 192, 1, 1, + 0, 0, 0, 0.5, + 256, 192, 1, 1, + 256, 0, 1, 0.5 + }; + + glGenBuffers(1, &screenVertexBuffer); + glBindBuffer(GL_ARRAY_BUFFER, screenVertexBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glGenVertexArrays(1, &screenVertexArray); + glBindVertexArray(screenVertexArray); + glEnableVertexAttribArray(0); // position + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*4, (void*)(0)); + glEnableVertexAttribArray(1); // texcoord + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*4, (void*)(2*4)); + + glGenTextures(1, &screenTexture); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, screenTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 192*2, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); +} + +void ScreenPanelGL::paintGL() +{ + int w = width(); + int h = height(); + + glClear(GL_COLOR_BUFFER_BIT); + + // TODO: check hiDPI compliance of this + glViewport(0, 0, w, h); + + screenShader->bind(); + + screenShader->setUniformValue("uScreenSize", (float)w, (float)h); + + int frontbuf = GPU::FrontBuffer; + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, screenTexture); + + if (GPU::Framebuffer[frontbuf][0] && GPU::Framebuffer[frontbuf][1]) + { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 192, GL_RGBA, + GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][0]); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192, 256, 192, GL_RGBA, + GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][1]); + } + + GLint filter = Config::ScreenFilter ? GL_LINEAR : GL_NEAREST; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); + + glBindBuffer(GL_ARRAY_BUFFER, screenVertexBuffer); + glBindVertexArray(screenVertexArray); + + GLint transloc = screenShader->uniformLocation("uTransform"); + + glUniformMatrix2x3fv(transloc, 1, GL_TRUE, screenMatrix[0]); + glDrawArrays(GL_TRIANGLES, 0, 2*3); + + glUniformMatrix2x3fv(transloc, 1, GL_TRUE, screenMatrix[1]); + glDrawArrays(GL_TRIANGLES, 2*3, 2*3); + + screenShader->release(); } void ScreenPanelGL::resizeEvent(QResizeEvent* event) { setupScreenLayout(); + + QOpenGLWidget::resizeEvent(event); +} + +void ScreenPanelGL::resizeGL(int w, int h) +{ } void ScreenPanelGL::mousePressEvent(QMouseEvent* event) @@ -977,7 +1093,8 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) } setMenuBar(menubar); - panel = new ScreenPanelNative(this); + //panel = new ScreenPanelNative(this); + panel = new ScreenPanelGL(this); setCentralWidget(panel); connect(this, SIGNAL(screenLayoutChange()), panel, SLOT(onScreenLayoutChanged())); emit screenLayoutChange(); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index a244907..8a8c041 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -24,7 +24,12 @@ #include #include #include + #include +#include +#include +#include +#include class EmuThread : public QThread @@ -115,7 +120,7 @@ private: }; -class ScreenPanelGL : public QOpenGLWidget, public ScreenHandler +class ScreenPanelGL : public QOpenGLWidget, public ScreenHandler, protected QOpenGLFunctions_3_2_Core { Q_OBJECT @@ -124,9 +129,12 @@ public: ~ScreenPanelGL(); protected: - void paintEvent(QPaintEvent* event) override; + void initializeGL() override; + + void paintGL() override; void resizeEvent(QResizeEvent* event) override; + void resizeGL(int w, int h) override; void mousePressEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; @@ -138,7 +146,10 @@ private slots: private: void setupScreenLayout(); - // + QOpenGLShaderProgram* screenShader; + GLuint screenVertexBuffer; + GLuint screenVertexArray; + GLuint screenTexture; }; diff --git a/src/frontend/qt_sdl/main_shaders.h b/src/frontend/qt_sdl/main_shaders.h index 7c4feec..2e57e59 100644 --- a/src/frontend/qt_sdl/main_shaders.h +++ b/src/frontend/qt_sdl/main_shaders.h @@ -21,12 +21,8 @@ const char* kScreenVS = R"(#version 140 -layout(std140) uniform uConfig -{ - vec2 uScreenSize; - uint u3DScale; - uint uFilterMode; -}; +uniform vec2 uScreenSize; +uniform mat2x3 uTransform; in vec2 vPosition; in vec2 vTexcoord; @@ -36,7 +32,10 @@ smooth out vec2 fTexcoord; void main() { vec4 fpos; - fpos.xy = ((vPosition * 2.0) / uScreenSize) - 1.0; + + fpos.xy = vec3(vPosition, 1.0) * uTransform; + + fpos.xy = ((fpos.xy * 2.0) / uScreenSize) - 1.0; fpos.y *= -1; fpos.z = 0.0; fpos.w = 1.0; @@ -48,14 +47,7 @@ void main() const char* kScreenFS = R"(#version 140 -layout(std140) uniform uConfig -{ - vec2 uScreenSize; - uint u3DScale; - uint uFilterMode; -}; - -uniform usampler2D ScreenTex; +uniform sampler2D ScreenTex; smooth in vec2 fTexcoord; @@ -63,11 +55,9 @@ out vec4 oColor; void main() { - ivec4 pixel = ivec4(texelFetch(ScreenTex, ivec2(fTexcoord), 0)); - - // TODO: filters + vec4 pixel = texture2D(ScreenTex, fTexcoord); - oColor = vec4(vec3(pixel.bgr) / 255.0, 1.0); + oColor = vec4(pixel.bgr, 1.0); } )"; -- cgit v1.2.3 From 36f4cdbbbf1904c1a0455bf45d9720f03872e1bd Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 25 May 2020 18:25:50 +0200 Subject: get the OpenGL renderer going. sorta. (also make the blackmagic_II branch obsolete in the process) --- src/CMakeLists.txt | 2 + src/GPU.cpp | 2 + src/GPU.h | 14 + src/GPU2D.cpp | 2 + src/GPU3D.cpp | 2 +- src/GPU3D_OpenGL.cpp | 31 +- src/GPU_OpenGL.cpp | 207 ++++++++++ src/GPU_OpenGL_shaders.h | 867 +++++++++++++++++++++++++++++++++++++++ src/OpenGLSupport.cpp | 14 +- src/OpenGLSupport.h | 30 +- src/frontend/qt_sdl/Platform.cpp | 3 +- src/frontend/qt_sdl/main.cpp | 96 ++++- src/frontend/qt_sdl/main.h | 11 + 13 files changed, 1240 insertions(+), 41 deletions(-) create mode 100644 src/GPU_OpenGL.cpp create mode 100644 src/GPU_OpenGL_shaders.h (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 245e6a2..56bb3cb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -16,6 +16,8 @@ add_library(core STATIC FIFO.h GBACart.cpp GPU.cpp + GPU_OpenGL.cpp + GPU_OpenGL_shaders.h GPU2D.cpp GPU3D.cpp GPU3D_OpenGL.cpp diff --git a/src/GPU.cpp b/src/GPU.cpp index 42bfc56..993086e 100644 --- a/src/GPU.cpp +++ b/src/GPU.cpp @@ -985,6 +985,8 @@ void StartScanline(u32 line) GPU2D_A->VBlank(); GPU2D_B->VBlank(); GPU3D::VBlank(); + + if (Accelerated) GLCompositor::RenderFrame(); } else if (VCount == 144) { diff --git a/src/GPU.h b/src/GPU.h index 661a7d9..e85a5b4 100644 --- a/src/GPU.h +++ b/src/GPU.h @@ -422,6 +422,20 @@ void SetDispStat(u32 cpu, u16 val); void SetVCount(u16 val); +namespace GLCompositor +{ + +bool Init(); +void DeInit(); +void Reset(); + +void UpdateDisplaySettings(); + +void RenderFrame(); +void BindOutputTexture(); + +} + } #endif diff --git a/src/GPU2D.cpp b/src/GPU2D.cpp index 1c6cf0c..6f950b7 100644 --- a/src/GPU2D.cpp +++ b/src/GPU2D.cpp @@ -728,6 +728,8 @@ u32 GPU2D::ColorComposite(int i, u32 val1, u32 val2) case 3: return ColorBrightnessDown(val1, EVY); case 4: return ColorBlend5(val1, val2); } + + return val1; } diff --git a/src/GPU3D.cpp b/src/GPU3D.cpp index 4f146ae..e687e37 100644 --- a/src/GPU3D.cpp +++ b/src/GPU3D.cpp @@ -616,7 +616,7 @@ int InitRenderer(bool hasGL) if (!GLRenderer::Init()) renderer = 0; } - +printf("renderer: %d\n", renderer); if (renderer == 0) SoftRenderer::Init(); Renderer = renderer; diff --git a/src/GPU3D_OpenGL.cpp b/src/GPU3D_OpenGL.cpp index b1bcaa1..dcc4b6b 100644 --- a/src/GPU3D_OpenGL.cpp +++ b/src/GPU3D_OpenGL.cpp @@ -29,6 +29,8 @@ namespace GPU3D namespace GLRenderer { +using namespace OpenGL; + // GL version requirements // * texelFetch: 3.0 (GLSL 1.30) (3.2/1.50 for MS) // * UBO: 3.1 @@ -142,7 +144,7 @@ bool BuildRenderShader(u32 flags, const char* vs, const char* fs) strcpy(&fsbuf[headerlen], kRenderFSCommon); strcpy(&fsbuf[headerlen + fsclen], fs); - bool ret = OpenGL_BuildShaderProgram(vsbuf, fsbuf, RenderShader[flags], shadername); + bool ret = OpenGL::BuildShaderProgram(vsbuf, fsbuf, RenderShader[flags], shadername); delete[] vsbuf; delete[] fsbuf; @@ -158,7 +160,7 @@ bool BuildRenderShader(u32 flags, const char* vs, const char* fs) glBindFragDataLocation(prog, 0, "oColor"); glBindFragDataLocation(prog, 1, "oAttr"); - if (!OpenGL_LinkShaderProgram(RenderShader[flags])) + if (!OpenGL::LinkShaderProgram(RenderShader[flags])) return false; GLint uni_id = glGetUniformBlockIndex(prog, "uConfig"); @@ -202,14 +204,14 @@ bool Init() glClearDepth(1.0); - if (!OpenGL_BuildShaderProgram(kClearVS, kClearFS, ClearShaderPlain, "ClearShader")) + if (!OpenGL::BuildShaderProgram(kClearVS, kClearFS, ClearShaderPlain, "ClearShader")) return false; glBindAttribLocation(ClearShaderPlain[2], 0, "vPosition"); glBindFragDataLocation(ClearShaderPlain[2], 0, "oColor"); glBindFragDataLocation(ClearShaderPlain[2], 1, "oAttr"); - if (!OpenGL_LinkShaderProgram(ClearShaderPlain)) + if (!OpenGL::LinkShaderProgram(ClearShaderPlain)) return false; ClearUniformLoc[0] = glGetUniformLocation(ClearShaderPlain[2], "uColor"); @@ -237,15 +239,15 @@ bool Init() kRenderVS_W, kRenderFS_WSM)) return false; - if (!OpenGL_BuildShaderProgram(kFinalPassVS, kFinalPassEdgeFS, FinalPassEdgeShader, "FinalPassEdgeShader")) + if (!OpenGL::BuildShaderProgram(kFinalPassVS, kFinalPassEdgeFS, FinalPassEdgeShader, "FinalPassEdgeShader")) return false; - if (!OpenGL_BuildShaderProgram(kFinalPassVS, kFinalPassFogFS, FinalPassFogShader, "FinalPassFogShader")) + if (!OpenGL::BuildShaderProgram(kFinalPassVS, kFinalPassFogFS, FinalPassFogShader, "FinalPassFogShader")) return false; glBindAttribLocation(FinalPassEdgeShader[2], 0, "vPosition"); glBindFragDataLocation(FinalPassEdgeShader[2], 0, "oColor"); - if (!OpenGL_LinkShaderProgram(FinalPassEdgeShader)) + if (!OpenGL::LinkShaderProgram(FinalPassEdgeShader)) return false; uni_id = glGetUniformBlockIndex(FinalPassEdgeShader[2], "uConfig"); @@ -261,7 +263,7 @@ bool Init() glBindAttribLocation(FinalPassFogShader[2], 0, "vPosition"); glBindFragDataLocation(FinalPassFogShader[2], 0, "oColor"); - if (!OpenGL_LinkShaderProgram(FinalPassFogShader)) + if (!OpenGL::LinkShaderProgram(FinalPassFogShader)) return false; uni_id = glGetUniformBlockIndex(FinalPassFogShader[2], "uConfig"); @@ -371,11 +373,19 @@ bool Init() glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, 1024, 48, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, NULL); + if (!GPU::GLCompositor::Init()) + { + // TODO: clean up things? fail more gracefully?? + return false; + } + return true; } void DeInit() { + GPU::GLCompositor::DeInit(); + glDeleteTextures(1, &TexMemID); glDeleteTextures(1, &TexPalMemID); @@ -392,12 +402,13 @@ void DeInit() for (int i = 0; i < 16; i++) { if (!RenderShader[i][2]) continue; - OpenGL_DeleteShaderProgram(RenderShader[i]); + OpenGL::DeleteShaderProgram(RenderShader[i]); } } void Reset() { + GPU::GLCompositor::Reset(); } void UpdateDisplaySettings() @@ -480,6 +491,8 @@ void UpdateDisplaySettings() //glLineWidth(scale); //glLineWidth(1.5); + + GPU::GLCompositor::UpdateDisplaySettings(); } diff --git a/src/GPU_OpenGL.cpp b/src/GPU_OpenGL.cpp new file mode 100644 index 0000000..c9d31f1 --- /dev/null +++ b/src/GPU_OpenGL.cpp @@ -0,0 +1,207 @@ +/* + Copyright 2016-2020 Arisotura + + 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 +#include +#include "NDS.h" +#include "GPU.h" +#include "Config.h" +#include "OpenGLSupport.h" +#include "GPU_OpenGL_shaders.h" + +namespace GPU +{ +namespace GLCompositor +{ + +using namespace OpenGL; + +int Scale; +int ScreenH, ScreenW; + +GLuint CompShader[1][3]; +GLuint CompScaleLoc[1]; + +GLuint CompVertexBufferID; +GLuint CompVertexArrayID; +float CompVertices[2 * 3*2 * 2]; // position + +GLuint CompScreenInputTex; +GLuint CompScreenOutputTex; +GLuint CompScreenOutputFB; + + +bool Init() +{ + if (!OpenGL::BuildShaderProgram(kCompositorVS, kCompositorFS_Nearest, CompShader[0], "CompositorShader")) + //if (!OpenGL::BuildShaderProgram(kCompositorVS, kCompositorFS_Linear, CompShader[0], "CompositorShader")) + //if (!OpenGL::BuildShaderProgram(kCompositorVS_xBRZ, kCompositorFS_xBRZ, CompShader[0], "CompositorShader")) + return false; + + for (int i = 0; i < 1; i++) + { + GLint uni_id; + + glBindAttribLocation(CompShader[i][2], 0, "vPosition"); + glBindFragDataLocation(CompShader[i][2], 0, "oColor"); + + if (!OpenGL::LinkShaderProgram(CompShader[i])) + return false; + + CompScaleLoc[i] = glGetUniformLocation(CompShader[i][2], "u3DScale"); + + glUseProgram(CompShader[i][2]); + uni_id = glGetUniformLocation(CompShader[i][2], "ScreenTex"); + glUniform1i(uni_id, 0); + uni_id = glGetUniformLocation(CompShader[i][2], "_3DTex"); + glUniform1i(uni_id, 1); + } + +#define SETVERTEX(i, x, y) \ + CompVertices[2*(i) + 0] = x; \ + CompVertices[2*(i) + 1] = y; + + // top screen + SETVERTEX(0, -1, 1); + SETVERTEX(1, 1, 0); + SETVERTEX(2, 1, 1); + SETVERTEX(3, -1, 1); + SETVERTEX(4, -1, 0); + SETVERTEX(5, 1, 0); + + // bottom screen + SETVERTEX(6, -1, 0); + SETVERTEX(7, 1, -1); + SETVERTEX(8, 1, 0); + SETVERTEX(9, -1, 0); + SETVERTEX(10, -1, -1); + SETVERTEX(11, 1, -1); + +#undef SETVERTEX + + glGenBuffers(1, &CompVertexBufferID); + glBindBuffer(GL_ARRAY_BUFFER, CompVertexBufferID); + glBufferData(GL_ARRAY_BUFFER, sizeof(CompVertices), CompVertices, GL_STATIC_DRAW); + + glGenVertexArrays(1, &CompVertexArrayID); + glBindVertexArray(CompVertexArrayID); + glEnableVertexAttribArray(0); // position + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2*4, (void*)(0)); + + glGenFramebuffers(1, &CompScreenOutputFB); + + glGenTextures(1, &CompScreenInputTex); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, CompScreenInputTex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8UI, 256*3 + 1, 192*2, 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, NULL); + + glGenTextures(1, &CompScreenOutputTex); + glBindTexture(GL_TEXTURE_2D, CompScreenOutputTex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + return true; +} + +void DeInit() +{ + glDeleteFramebuffers(1, &CompScreenOutputFB); + glDeleteTextures(1, &CompScreenInputTex); + glDeleteTextures(1, &CompScreenOutputTex); + + glDeleteVertexArrays(1, &CompVertexArrayID); + glDeleteBuffers(1, &CompVertexBufferID); + + for (int i = 0; i < 1; i++) + OpenGL::DeleteShaderProgram(CompShader[i]); +} + +void Reset() +{ +} + + +void UpdateDisplaySettings() +{ + int scale = Config::GL_ScaleFactor; + + Scale = scale; + ScreenW = 256 * scale; + ScreenH = 384 * scale; + + glBindTexture(GL_TEXTURE_2D, CompScreenOutputTex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ScreenW, ScreenH, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + GLenum fbassign[] = {GL_COLOR_ATTACHMENT0}; + glBindFramebuffer(GL_FRAMEBUFFER, CompScreenOutputFB); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, CompScreenOutputTex, 0); + glDrawBuffers(1, fbassign); +} + + +void RenderFrame() +{ + glBindFramebuffer(GL_FRAMEBUFFER, CompScreenOutputFB); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_STENCIL_TEST); + glDisable(GL_BLEND); + glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + glViewport(0, 0, ScreenW, ScreenH); + + // TODO: select more shaders (filtering, etc) + OpenGL::UseShaderProgram(CompShader[0]); + glUniform1ui(CompScaleLoc[0], Scale); + + //if (RunningSomething) + { + int frontbuf = GPU::FrontBuffer; + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, CompScreenInputTex); + + if (GPU::Framebuffer[frontbuf][0] && GPU::Framebuffer[frontbuf][1]) + { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256*3 + 1, 192, GL_RGBA_INTEGER, + GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][0]); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192, 256*3 + 1, 192, GL_RGBA_INTEGER, + GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][1]); + } + + glActiveTexture(GL_TEXTURE1); + GPU3D::GLRenderer::SetupAccelFrame(); + + glBindBuffer(GL_ARRAY_BUFFER, CompVertexBufferID); + glBindVertexArray(CompVertexArrayID); + glDrawArrays(GL_TRIANGLES, 0, 4*3); + } +} + +void BindOutputTexture() +{ + glBindTexture(GL_TEXTURE_2D, CompScreenOutputTex); +} + +} +} diff --git a/src/GPU_OpenGL_shaders.h b/src/GPU_OpenGL_shaders.h new file mode 100644 index 0000000..ec975ed --- /dev/null +++ b/src/GPU_OpenGL_shaders.h @@ -0,0 +1,867 @@ +/* + Copyright 2016-2020 Arisotura + + 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 GPU_OPENGL_SHADERS_H +#define GPU_OPENGL_SHADERS_H + +const char* kCompositorVS = R"(#version 140 + +in vec2 vPosition; + +smooth out vec2 fTexcoord; + +void main() +{ + vec4 fpos; + fpos.xy = vPosition; + fpos.z = 0.0; + fpos.w = 1.0; + + gl_Position = fpos; + fTexcoord = (vPosition + vec2(1.0, 1.0)) * (vec2(256.0, 384.0) / 2.0); +} +)"; + +const char* kCompositorFS_Nearest = R"(#version 140 + +uniform uint u3DScale; + +uniform usampler2D ScreenTex; +uniform sampler2D _3DTex; + +smooth in vec2 fTexcoord; + +out vec4 oColor; + +void main() +{ + ivec4 pixel = ivec4(texelFetch(ScreenTex, ivec2(fTexcoord), 0)); + + ivec4 mbright = ivec4(texelFetch(ScreenTex, ivec2(256*3, int(fTexcoord.y)), 0)); + int dispmode = mbright.b & 0x3; + + if (dispmode == 1) + { + ivec4 val1 = pixel; + ivec4 val2 = ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(256,0), 0)); + ivec4 val3 = ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(512,0), 0)); + + int compmode = val3.a & 0xF; + int eva, evb, evy; + + if (compmode == 4) + { + // 3D on top, blending + + float xpos = val3.r + fract(fTexcoord.x); + float ypos = mod(fTexcoord.y, 192); + ivec4 _3dpix = ivec4(texelFetch(_3DTex, ivec2(vec2(xpos, ypos)*u3DScale), 0).bgra + * vec4(63,63,63,31)); + + if (_3dpix.a > 0) + { + eva = (_3dpix.a & 0x1F) + 1; + evb = 32 - eva; + + val1 = ((_3dpix * eva) + (val1 * evb)) >> 5; + if (eva <= 16) val1 += ivec4(1,1,1,0); + val1 = min(val1, 0x3F); + } + else + val1 = val2; + } + else if (compmode == 1) + { + // 3D on bottom, blending + + float xpos = val3.r + fract(fTexcoord.x); + float ypos = mod(fTexcoord.y, 192); + ivec4 _3dpix = ivec4(texelFetch(_3DTex, ivec2(vec2(xpos, ypos)*u3DScale), 0).bgra + * vec4(63,63,63,31)); + + if (_3dpix.a > 0) + { + eva = val3.g; + evb = val3.b; + + val1 = ((val1 * eva) + (_3dpix * evb)) >> 4; + val1 = min(val1, 0x3F); + } + else + val1 = val2; + } + else if (compmode <= 3) + { + // 3D on top, normal/fade + + float xpos = val3.r + fract(fTexcoord.x); + float ypos = mod(fTexcoord.y, 192); + ivec4 _3dpix = ivec4(texelFetch(_3DTex, ivec2(vec2(xpos, ypos)*u3DScale), 0).bgra + * vec4(63,63,63,31)); + + if (_3dpix.a > 0) + { + evy = val3.g; + + val1 = _3dpix; + if (compmode == 2) val1 += ((ivec4(0x3F,0x3F,0x3F,0) - val1) * evy) >> 4; + else if (compmode == 3) val1 -= (val1 * evy) >> 4; + } + else + val1 = val2; + } + + pixel = val1; + } + + if (dispmode != 0) + { + int brightmode = mbright.g >> 6; + if (brightmode == 1) + { + // up + int evy = mbright.r & 0x1F; + if (evy > 16) evy = 16; + + pixel += ((ivec4(0x3F,0x3F,0x3F,0) - pixel) * evy) >> 4; + } + else if (brightmode == 2) + { + // down + int evy = mbright.r & 0x1F; + if (evy > 16) evy = 16; + + pixel -= (pixel * evy) >> 4; + } + } + + pixel.rgb <<= 2; + pixel.rgb |= (pixel.rgb >> 6); + + // TODO: filters + + oColor = vec4(vec3(pixel.rgb) / 255.0, 1.0); +} +)"; + + + +const char* kCompositorFS_Linear = R"(#version 140 + +uniform uint u3DScale; + +uniform usampler2D ScreenTex; +uniform sampler2D _3DTex; + +smooth in vec2 fTexcoord; + +out vec4 oColor; + +ivec4 Get3DPixel(vec2 pos) +{ + return ivec4(texelFetch(_3DTex, ivec2(pos*u3DScale), 0).bgra + * vec4(63,63,63,31)); +} + +ivec4 GetFullPixel(ivec4 val1, ivec4 val2, ivec4 val3, ivec4 _3dpix) +{ + int compmode = val3.a & 0xF; + int eva, evb, evy; + + if (compmode == 4) + { + // 3D on top, blending + + if (_3dpix.a > 0) + { + eva = (_3dpix.a & 0x1F) + 1; + evb = 32 - eva; + + val1 = ((_3dpix * eva) + (val1 * evb)) >> 5; + if (eva <= 16) val1 += ivec4(1,1,1,0); + val1 = min(val1, 0x3F); + } + else + val1 = val2; + } + else if (compmode == 1) + { + // 3D on bottom, blending + + if (_3dpix.a > 0) + { + eva = val3.g; + evb = val3.b; + + val1 = ((val1 * eva) + (_3dpix * evb)) >> 4; + val1 = min(val1, 0x3F); + } + else + val1 = val2; + } + else if (compmode <= 3) + { + // 3D on top, normal/fade + + if (_3dpix.a > 0) + { + evy = val3.g; + + val1 = _3dpix; + if (compmode == 2) val1 += ((ivec4(0x3F,0x3F,0x3F,0) - val1) * evy) >> 4; + else if (compmode == 3) val1 -= (val1 * evy) >> 4; + } + else + val1 = val2; + } + + return val1; +} + +ivec4 imix(ivec4 a, ivec4 b, float x) +{ + return ivec4(vec4(a)*(1-x) + vec4(b)*x); +} + +void main() +{ + ivec4 pixel = ivec4(texelFetch(ScreenTex, ivec2(fTexcoord), 0)); + + ivec4 mbright = ivec4(texelFetch(ScreenTex, ivec2(256*3, int(fTexcoord.y)), 0)); + int dispmode = mbright.b & 0x3; + + if (dispmode == 1) + { + ivec4 val1 = pixel; + ivec4 val2 = ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(256,0), 0)); + ivec4 val3 = ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(512,0), 0)); + + float xfract = fract(fTexcoord.x); + float yfract = fract(fTexcoord.y); + + float xpos = val3.r + xfract; + float ypos = mod(fTexcoord.y, 192); + ivec4 _3dpix = Get3DPixel(vec2(xpos,ypos)); + + ivec4 p00 = GetFullPixel(val1, val2, val3, _3dpix); + + int xdisp = 1 - int(step(255, fTexcoord.x)); + int ydisp = 1 - int(step(191, ypos)); + + ivec4 p01 = GetFullPixel(ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(xdisp+0 ,0), 0)), + ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(xdisp+256,0), 0)), + ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(xdisp+512,0), 0)), + _3dpix); + + ivec4 p10 = GetFullPixel(ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(0+0 ,ydisp), 0)), + ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(0+256,ydisp), 0)), + ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(0+512,ydisp), 0)), + _3dpix); + + ivec4 p11 = GetFullPixel(ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(xdisp+0 ,ydisp), 0)), + ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(xdisp+256,ydisp), 0)), + ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(xdisp+512,ydisp), 0)), + _3dpix); + + ivec4 pa = imix(p00, p01, xfract); + ivec4 pb = imix(p10, p11, xfract); + + pixel = imix(pa, pb, yfract); + } + + if (dispmode != 0) + { + int brightmode = mbright.g >> 6; + if (brightmode == 1) + { + // up + int evy = mbright.r & 0x1F; + if (evy > 16) evy = 16; + + pixel += ((ivec4(0x3F,0x3F,0x3F,0) - pixel) * evy) >> 4; + } + else if (brightmode == 2) + { + // down + int evy = mbright.r & 0x1F; + if (evy > 16) evy = 16; + + pixel -= (pixel * evy) >> 4; + } + } + + pixel.rgb <<= 2; + pixel.rgb |= (pixel.rgb >> 6); + + // TODO: filters + + oColor = vec4(vec3(pixel.rgb) / 255.0, 1.0); +} +)"; + + + + + + +// HUGE TEST ZONE ARRLGD + +const char* kCompositorVS_xBRZ = R"(#version 140 + +#define BLEND_NONE 0 +#define BLEND_NORMAL 1 +#define BLEND_DOMINANT 2 +#define LUMINANCE_WEIGHT 1.0 +#define EQUAL_COLOR_TOLERANCE 30.0/255.0 +#define STEEP_DIRECTION_THRESHOLD 2.2 +#define DOMINANT_DIRECTION_THRESHOLD 3.6 + +#if __VERSION__ >= 130 +#define COMPAT_VARYING out +#define COMPAT_ATTRIBUTE in +#define COMPAT_TEXTURE texture +#else +#define COMPAT_VARYING varying +#define COMPAT_ATTRIBUTE attribute +#define COMPAT_TEXTURE texture2D +#endif + +#ifdef GL_ES +#define COMPAT_PRECISION mediump +#else +#define COMPAT_PRECISION +#endif + +COMPAT_ATTRIBUTE vec2 vPosition; +COMPAT_VARYING vec4 TEX0; +COMPAT_VARYING vec4 t1; +COMPAT_VARYING vec4 t2; +COMPAT_VARYING vec4 t3; +COMPAT_VARYING vec4 t4; +COMPAT_VARYING vec4 t5; +COMPAT_VARYING vec4 t6; +COMPAT_VARYING vec4 t7; + +uniform COMPAT_PRECISION int FrameDirection; +uniform COMPAT_PRECISION int FrameCount; +uniform COMPAT_PRECISION vec2 OutputSize; +uniform COMPAT_PRECISION vec2 TextureSize; +uniform COMPAT_PRECISION vec2 InputSize; + +// vertex compatibility #defines +#define vTexCoord TEX0.xy +#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize +#define outsize vec4(OutputSize, 1.0 / OutputSize) + +void main() +{ + vec4 fpos; + fpos.xy = vPosition; + fpos.z = 0.0; + fpos.w = 1.0; + + gl_Position = fpos; + vec2 TexCoord = (vPosition + vec2(1.0, 1.0)) * (vec2(256.0, 384.0) / 2.0); + + + //gl_Position = MVPMatrix * VertexCoord; + //COL0 = COLOR; + TEX0.xy = TexCoord.xy; + vec2 ps = vec2(1,1);//vec2(SourceSize.z, SourceSize.w); + float dx = ps.x; + float dy = ps.y; + + // A1 B1 C1 + // A0 A B C C4 + // D0 D E F F4 + // G0 G H I I4 + // G5 H5 I5 + + t1 = vTexCoord.xxxy + vec4( -dx, 0.0, dx,-2.0*dy); // A1 B1 C1 + t2 = vTexCoord.xxxy + vec4( -dx, 0.0, dx, -dy); // A B C + t3 = vTexCoord.xxxy + vec4( -dx, 0.0, dx, 0.0); // D E F + t4 = vTexCoord.xxxy + vec4( -dx, 0.0, dx, dy); // G H I + t5 = vTexCoord.xxxy + vec4( -dx, 0.0, dx, 2.0*dy); // G5 H5 I5 + t6 = vTexCoord.xyyy + vec4(-2.0*dx,-dy, 0.0, dy); // A0 D0 G0 + t7 = vTexCoord.xyyy + vec4( 2.0*dx,-dy, 0.0, dy); // C4 F4 I4 +} +)"; + +const char* kCompositorFS_xBRZ = R"(#version 140 + +#define BLEND_NONE 0 +#define BLEND_NORMAL 1 +#define BLEND_DOMINANT 2 +#define LUMINANCE_WEIGHT 1.0 +#define EQUAL_COLOR_TOLERANCE 30.0/255.0 +#define STEEP_DIRECTION_THRESHOLD 2.2 +#define DOMINANT_DIRECTION_THRESHOLD 3.6 + +#if __VERSION__ >= 130 +#define COMPAT_VARYING in +//#define COMPAT_TEXTURE texture +#define FragColor oColor +#else +#define COMPAT_VARYING varying +#define FragColor gl_FragColor +//#define COMPAT_TEXTURE texture2D +#endif + +#ifdef GL_ES +#ifdef GL_FRAGMENT_PRECISION_HIGH +precision highp float; +#else +precision mediump float; +#endif +#define COMPAT_PRECISION mediump +#else +#define COMPAT_PRECISION +#endif + +uniform uint u3DScale; + +uniform usampler2D ScreenTex; +uniform sampler2D _3DTex; + +smooth in vec2 fTexcoord; + +out vec4 oColor; + +//uniform COMPAT_PRECISION vec2 OutputSize; +//uniform COMPAT_PRECISION vec2 TextureSize; +#define TextureSize vec2(256,384) +//uniform COMPAT_PRECISION vec2 InputSize; +//uniform sampler2D Texture; +#define Texture 1312 +COMPAT_VARYING vec4 TEX0; +COMPAT_VARYING vec4 t1; +COMPAT_VARYING vec4 t2; +COMPAT_VARYING vec4 t3; +COMPAT_VARYING vec4 t4; +COMPAT_VARYING vec4 t5; +COMPAT_VARYING vec4 t6; +COMPAT_VARYING vec4 t7; + +// fragment compatibility #defines +#define Source Texture +#define vTexCoord TEX0.xy + +#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize +#define outsize vec4(OutputSize, 1.0 / OutputSize) + + const float one_sixth = 1.0 / 6.0; + const float two_sixth = 2.0 / 6.0; + const float four_sixth = 4.0 / 6.0; + const float five_sixth = 5.0 / 6.0; + +vec4 Get2DPixel(vec2 texcoord, int level) +{ + ivec4 pixel = ivec4(texelFetch(ScreenTex, ivec2(texcoord) + ivec2(level*256,0), 0)); + + return vec4(pixel) / vec4(63.0, 63.0, 63.0, 31.0); +} + +ivec4 Get3DPixel(vec2 pos) +{ + return ivec4(texelFetch(_3DTex, ivec2(pos*u3DScale), 0).bgra + * vec4(63,63,63,31)); +} + +float reduce(const vec3 color) +{ + return dot(color, vec3(65536.0, 256.0, 1.0)); +} + +float DistYCbCr(const vec3 pixA, const vec3 pixB) +{ + const vec3 w = vec3(0.2627, 0.6780, 0.0593); + const float scaleB = 0.5 / (1.0 - w.b); + const float scaleR = 0.5 / (1.0 - w.r); + vec3 diff = pixA - pixB; + float Y = dot(diff, w); + float Cb = scaleB * (diff.b - Y); + float Cr = scaleR * (diff.r - Y); + + return sqrt( ((LUMINANCE_WEIGHT * Y) * (LUMINANCE_WEIGHT * Y)) + (Cb * Cb) + (Cr * Cr) ); +} + +bool IsPixEqual(const vec3 pixA, const vec3 pixB) +{ + return (DistYCbCr(pixA, pixB) < EQUAL_COLOR_TOLERANCE); +} + +bool IsBlendingNeeded(const ivec4 blend) +{ + return any(notEqual(blend, ivec4(BLEND_NONE))); +} + +//--------------------------------------- +// Input Pixel Mapping: --|21|22|23|-- +// 19|06|07|08|09 +// 18|05|00|01|10 +// 17|04|03|02|11 +// --|15|14|13|-- +// +// Output Pixel Mapping: 20|21|22|23|24|25 +// 19|06|07|08|09|26 +// 18|05|00|01|10|27 +// 17|04|03|02|11|28 +// 16|15|14|13|12|29 +// 35|34|33|32|31|30 + +ivec4 GetFiltered2DPixel(int level) +{ + vec2 f = fract(vTexCoord.xy);// * SourceSize.xy); + + //--------------------------------------- + // Input Pixel Mapping: 20|21|22|23|24 + // 19|06|07|08|09 + // 18|05|00|01|10 + // 17|04|03|02|11 + // 16|15|14|13|12 + + vec3 src[25]; + + src[21] = Get2DPixel(t1.xw, level).rgb; + src[22] = Get2DPixel(t1.yw, level).rgb; + src[23] = Get2DPixel(t1.zw, level).rgb; + src[ 6] = Get2DPixel(t2.xw, level).rgb; + src[ 7] = Get2DPixel(t2.yw, level).rgb; + src[ 8] = Get2DPixel(t2.zw, level).rgb; + src[ 5] = Get2DPixel(t3.xw, level).rgb; + src[ 0] = Get2DPixel(t3.yw, level).rgb; + src[ 1] = Get2DPixel(t3.zw, level).rgb; + src[ 4] = Get2DPixel(t4.xw, level).rgb; + src[ 3] = Get2DPixel(t4.yw, level).rgb; + src[ 2] = Get2DPixel(t4.zw, level).rgb; + src[15] = Get2DPixel(t5.xw, level).rgb; + src[14] = Get2DPixel(t5.yw, level).rgb; + src[13] = Get2DPixel(t5.zw, level).rgb; + src[19] = Get2DPixel(t6.xy, level).rgb; + src[18] = Get2DPixel(t6.xz, level).rgb; + src[17] = Get2DPixel(t6.xw, level).rgb; + src[ 9] = Get2DPixel(t7.xy, level).rgb; + src[10] = Get2DPixel(t7.xz, level).rgb; + src[11] = Get2DPixel(t7.xw, level).rgb; + + float v[9]; + v[0] = reduce(src[0]); + v[1] = reduce(src[1]); + v[2] = reduce(src[2]); + v[3] = reduce(src[3]); + v[4] = reduce(src[4]); + v[5] = reduce(src[5]); + v[6] = reduce(src[6]); + v[7] = reduce(src[7]); + v[8] = reduce(src[8]); + + ivec4 blendResult = ivec4(BLEND_NONE); + + // Preprocess corners + // Pixel Tap Mapping: --|--|--|--|-- + // --|--|07|08|-- + // --|05|00|01|10 + // --|04|03|02|11 + // --|--|14|13|-- + // Corner (1, 1) + if ( ((v[0] == v[1] && v[3] == v[2]) || (v[0] == v[3] && v[1] == v[2])) == false) + { + float dist_03_01 = DistYCbCr(src[ 4], src[ 0]) + DistYCbCr(src[ 0], src[ 8]) + DistYCbCr(src[14], src[ 2]) + DistYCbCr(src[ 2], src[10]) + (4.0 * DistYCbCr(src[ 3], src[ 1])); + float dist_00_02 = DistYCbCr(src[ 5], src[ 3]) + DistYCbCr(src[ 3], src[13]) + DistYCbCr(src[ 7], src[ 1]) + DistYCbCr(src[ 1], src[11]) + (4.0 * DistYCbCr(src[ 0], src[ 2])); + bool dominantGradient = (DOMINANT_DIRECTION_THRESHOLD * dist_03_01) < dist_00_02; + blendResult[2] = ((dist_03_01 < dist_00_02) && (v[0] != v[1]) && (v[0] != v[3])) ? ((dominantGradient) ? BLEND_DOMINANT : BLEND_NORMAL) : BLEND_NONE; + } + + // Pixel Tap Mapping: --|--|--|--|-- + // --|06|07|--|-- + // 18|05|00|01|-- + // 17|04|03|02|-- + // --|15|14|--|-- + // Corner (0, 1) + if ( ((v[5] == v[0] && v[4] == v[3]) || (v[5] == v[4] && v[0] == v[3])) == false) + { + float dist_04_00 = DistYCbCr(src[17], src[ 5]) + DistYCbCr(src[ 5], src[ 7]) + DistYCbCr(src[15], src[ 3]) + DistYCbCr(src[ 3], src[ 1]) + (4.0 * DistYCbCr(src[ 4], src[ 0])); + float dist_05_03 = DistYCbCr(src[18], src[ 4]) + DistYCbCr(src[ 4], src[14]) + DistYCbCr(src[ 6], src[ 0]) + DistYCbCr(src[ 0], src[ 2]) + (4.0 * DistYCbCr(src[ 5], src[ 3])); + bool dominantGradient = (DOMINANT_DIRECTION_THRESHOLD * dist_05_03) < dist_04_00; + blendResult[3] = ((dist_04_00 > dist_05_03) && (v[0] != v[5]) && (v[0] != v[3])) ? ((dominantGradient) ? BLEND_DOMINANT : BLEND_NORMAL) : BLEND_NONE; + } + + // Pixel Tap Mapping: --|--|22|23|-- + // --|06|07|08|09 + // --|05|00|01|10 + // --|--|03|02|-- + // --|--|--|--|-- + // Corner (1, 0) + if ( ((v[7] == v[8] && v[0] == v[1]) || (v[7] == v[0] && v[8] == v[1])) == false) + { + float dist_00_08 = DistYCbCr(src[ 5], src[ 7]) + DistYCbCr(src[ 7], src[23]) + DistYCbCr(src[ 3], src[ 1]) + DistYCbCr(src[ 1], src[ 9]) + (4.0 * DistYCbCr(src[ 0], src[ 8])); + float dist_07_01 = DistYCbCr(src[ 6], src[ 0]) + DistYCbCr(src[ 0], src[ 2]) + DistYCbCr(src[22], src[ 8]) + DistYCbCr(src[ 8], src[10]) + (4.0 * DistYCbCr(src[ 7], src[ 1])); + bool dominantGradient = (DOMINANT_DIRECTION_THRESHOLD * dist_07_01) < dist_00_08; + blendResult[1] = ((dist_00_08 > dist_07_01) && (v[0] != v[7]) && (v[0] != v[1])) ? ((dominantGradient) ? BLEND_DOMINANT : BLEND_NORMAL) : BLEND_NONE; + } + + // Pixel Tap Mapping: --|21|22|--|-- + // 19|06|07|08|-- + // 18|05|00|01|-- + // --|04|03|--|-- + // --|--|--|--|-- + // Corner (0, 0) + if ( ((v[6] == v[7] && v[5] == v[0]) || (v[6] == v[5] && v[7] == v[0])) == false) + { + float dist_05_07 = DistYCbCr(src[18], src[ 6]) + DistYCbCr(src[ 6], src[22]) + DistYCbCr(src[ 4], src[ 0]) + DistYCbCr(src[ 0], src[ 8]) + (4.0 * DistYCbCr(src[ 5], src[ 7])); + float dist_06_00 = DistYCbCr(src[19], src[ 5]) + DistYCbCr(src[ 5], src[ 3]) + DistYCbCr(src[21], src[ 7]) + DistYCbCr(src[ 7], src[ 1]) + (4.0 * DistYCbCr(src[ 6], src[ 0])); + bool dominantGradient = (DOMINANT_DIRECTION_THRESHOLD * dist_05_07) < dist_06_00; + blendResult[0] = ((dist_05_07 < dist_06_00) && (v[0] != v[5]) && (v[0] != v[7])) ? ((dominantGradient) ? BLEND_DOMINANT : BLEND_NORMAL) : BLEND_NONE; + } + + vec3 dst[16]; + dst[ 0] = src[0]; + dst[ 1] = src[0]; + dst[ 2] = src[0]; + dst[ 3] = src[0]; + dst[ 4] = src[0]; + dst[ 5] = src[0]; + dst[ 6] = src[0]; + dst[ 7] = src[0]; + dst[ 8] = src[0]; + dst[ 9] = src[0]; + dst[10] = src[0]; + dst[11] = src[0]; + dst[12] = src[0]; + dst[13] = src[0]; + dst[14] = src[0]; + dst[15] = src[0]; + + // Scale pixel + if (IsBlendingNeeded(blendResult) == true) + { + float dist_01_04 = DistYCbCr(src[1], src[4]); + float dist_03_08 = DistYCbCr(src[3], src[8]); + bool haveShallowLine = (STEEP_DIRECTION_THRESHOLD * dist_01_04 <= dist_03_08) && (v[0] != v[4]) && (v[5] != v[4]); + bool haveSteepLine = (STEEP_DIRECTION_THRESHOLD * dist_03_08 <= dist_01_04) && (v[0] != v[8]) && (v[7] != v[8]); + bool needBlend = (blendResult[2] != BLEND_NONE); + bool doLineBlend = ( blendResult[2] >= BLEND_DOMINANT || + ((blendResult[1] != BLEND_NONE && !IsPixEqual(src[0], src[4])) || + (blendResult[3] != BLEND_NONE && !IsPixEqual(src[0], src[8])) || + (IsPixEqual(src[4], src[3]) && IsPixEqual(src[3], src[2]) && IsPixEqual(src[2], src[1]) && IsPixEqual(src[1], src[8]) && IsPixEqual(src[0], src[2]) == false) ) == false ); + + vec3 blendPix = ( DistYCbCr(src[0], src[1]) <= DistYCbCr(src[0], src[3]) ) ? src[1] : src[3]; + dst[ 2] = mix(dst[ 2], blendPix, (needBlend && doLineBlend) ? ((haveShallowLine) ? ((haveSteepLine) ? 1.0/3.0 : 0.25) : ((haveSteepLine) ? 0.25 : 0.00)) : 0.00); + dst[ 9] = mix(dst[ 9], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.25 : 0.00); + dst[10] = mix(dst[10], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.75 : 0.00); + dst[11] = mix(dst[11], blendPix, (needBlend) ? ((doLineBlend) ? ((haveSteepLine) ? 1.00 : ((haveShallowLine) ? 0.75 : 0.50)) : 0.08677704501) : 0.00); + dst[12] = mix(dst[12], blendPix, (needBlend) ? ((doLineBlend) ? 1.00 : 0.6848532563) : 0.00); + dst[13] = mix(dst[13], blendPix, (needBlend) ? ((doLineBlend) ? ((haveShallowLine) ? 1.00 : ((haveSteepLine) ? 0.75 : 0.50)) : 0.08677704501) : 0.00); + dst[14] = mix(dst[14], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.75 : 0.00); + dst[15] = mix(dst[15], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.25 : 0.00); + + dist_01_04 = DistYCbCr(src[7], src[2]); + dist_03_08 = DistYCbCr(src[1], src[6]); + haveShallowLine = (STEEP_DIRECTION_THRESHOLD * dist_01_04 <= dist_03_08) && (v[0] != v[2]) && (v[3] != v[2]); + haveSteepLine = (STEEP_DIRECTION_THRESHOLD * dist_03_08 <= dist_01_04) && (v[0] != v[6]) && (v[5] != v[6]); + needBlend = (blendResult[1] != BLEND_NONE); + doLineBlend = ( blendResult[1] >= BLEND_DOMINANT || + !((blendResult[0] != BLEND_NONE && !IsPixEqual(src[0], src[2])) || + (blendResult[2] != BLEND_NONE && !IsPixEqual(src[0], src[6])) || + (IsPixEqual(src[2], src[1]) && IsPixEqual(src[1], src[8]) && IsPixEqual(src[8], src[7]) && IsPixEqual(src[7], src[6]) && !IsPixEqual(src[0], src[8])) ) ); + + blendPix = ( DistYCbCr(src[0], src[7]) <= DistYCbCr(src[0], src[1]) ) ? src[7] : src[1]; + dst[ 1] = mix(dst[ 1], blendPix, (needBlend && doLineBlend) ? ((haveShallowLine) ? ((haveSteepLine) ? 1.0/3.0 : 0.25) : ((haveSteepLine) ? 0.25 : 0.00)) : 0.00); + dst[ 6] = mix(dst[ 6], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.25 : 0.00); + dst[ 7] = mix(dst[ 7], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.75 : 0.00); + dst[ 8] = mix(dst[ 8], blendPix, (needBlend) ? ((doLineBlend) ? ((haveSteepLine) ? 1.00 : ((haveShallowLine) ? 0.75 : 0.50)) : 0.08677704501) : 0.00); + dst[ 9] = mix(dst[ 9], blendPix, (needBlend) ? ((doLineBlend) ? 1.00 : 0.6848532563) : 0.00); + dst[10] = mix(dst[10], blendPix, (needBlend) ? ((doLineBlend) ? ((haveShallowLine) ? 1.00 : ((haveSteepLine) ? 0.75 : 0.50)) : 0.08677704501) : 0.00); + dst[11] = mix(dst[11], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.75 : 0.00); + dst[12] = mix(dst[12], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.25 : 0.00); + + dist_01_04 = DistYCbCr(src[5], src[8]); + dist_03_08 = DistYCbCr(src[7], src[4]); + haveShallowLine = (STEEP_DIRECTION_THRESHOLD * dist_01_04 <= dist_03_08) && (v[0] != v[8]) && (v[1] != v[8]); + haveSteepLine = (STEEP_DIRECTION_THRESHOLD * dist_03_08 <= dist_01_04) && (v[0] != v[4]) && (v[3] != v[4]); + needBlend = (blendResult[0] != BLEND_NONE); + doLineBlend = ( blendResult[0] >= BLEND_DOMINANT || + !((blendResult[3] != BLEND_NONE && !IsPixEqual(src[0], src[8])) || + (blendResult[1] != BLEND_NONE && !IsPixEqual(src[0], src[4])) || + (IsPixEqual(src[8], src[7]) && IsPixEqual(src[7], src[6]) && IsPixEqual(src[6], src[5]) && IsPixEqual(src[5], src[4]) && !IsPixEqual(src[0], src[6])) ) ); + + blendPix = ( DistYCbCr(src[0], src[5]) <= DistYCbCr(src[0], src[7]) ) ? src[5] : src[7]; + dst[ 0] = mix(dst[ 0], blendPix, (needBlend && doLineBlend) ? ((haveShallowLine) ? ((haveSteepLine) ? 1.0/3.0 : 0.25) : ((haveSteepLine) ? 0.25 : 0.00)) : 0.00); + dst[15] = mix(dst[15], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.25 : 0.00); + dst[ 4] = mix(dst[ 4], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.75 : 0.00); + dst[ 5] = mix(dst[ 5], blendPix, (needBlend) ? ((doLineBlend) ? ((haveSteepLine) ? 1.00 : ((haveShallowLine) ? 0.75 : 0.50)) : 0.08677704501) : 0.00); + dst[ 6] = mix(dst[ 6], blendPix, (needBlend) ? ((doLineBlend) ? 1.00 : 0.6848532563) : 0.00); + dst[ 7] = mix(dst[ 7], blendPix, (needBlend) ? ((doLineBlend) ? ((haveShallowLine) ? 1.00 : ((haveSteepLine) ? 0.75 : 0.50)) : 0.08677704501) : 0.00); + dst[ 8] = mix(dst[ 8], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.75 : 0.00); + dst[ 9] = mix(dst[ 9], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.25 : 0.00); + + + dist_01_04 = DistYCbCr(src[3], src[6]); + dist_03_08 = DistYCbCr(src[5], src[2]); + haveShallowLine = (STEEP_DIRECTION_THRESHOLD * dist_01_04 <= dist_03_08) && (v[0] != v[6]) && (v[7] != v[6]); + haveSteepLine = (STEEP_DIRECTION_THRESHOLD * dist_03_08 <= dist_01_04) && (v[0] != v[2]) && (v[1] != v[2]); + needBlend = (blendResult[3] != BLEND_NONE); + doLineBlend = ( blendResult[3] >= BLEND_DOMINANT || + !((blendResult[2] != BLEND_NONE && !IsPixEqual(src[0], src[6])) || + (blendResult[0] != BLEND_NONE && !IsPixEqual(src[0], src[2])) || + (IsPixEqual(src[6], src[5]) && IsPixEqual(src[5], src[4]) && IsPixEqual(src[4], src[3]) && IsPixEqual(src[3], src[2]) && !IsPixEqual(src[0], src[4])) ) ); + + blendPix = ( DistYCbCr(src[0], src[3]) <= DistYCbCr(src[0], src[5]) ) ? src[3] : src[5]; + dst[ 3] = mix(dst[ 3], blendPix, (needBlend && doLineBlend) ? ((haveShallowLine) ? ((haveSteepLine) ? 1.0/3.0 : 0.25) : ((haveSteepLine) ? 0.25 : 0.00)) : 0.00); + dst[12] = mix(dst[12], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.25 : 0.00); + dst[13] = mix(dst[13], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.75 : 0.00); + dst[14] = mix(dst[14], blendPix, (needBlend) ? ((doLineBlend) ? ((haveSteepLine) ? 1.00 : ((haveShallowLine) ? 0.75 : 0.50)) : 0.08677704501) : 0.00); + dst[15] = mix(dst[15], blendPix, (needBlend) ? ((doLineBlend) ? 1.00 : 0.6848532563) : 0.00); + dst[ 4] = mix(dst[ 4], blendPix, (needBlend) ? ((doLineBlend) ? ((haveShallowLine) ? 1.00 : ((haveSteepLine) ? 0.75 : 0.50)) : 0.08677704501) : 0.00); + dst[ 5] = mix(dst[ 5], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.75 : 0.00); + dst[ 6] = mix(dst[ 6], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.25 : 0.00); + } + + vec3 res = mix( mix( mix( mix(dst[ 6], dst[ 7], step(0.25, f.x)), mix(dst[ 8], dst[ 9], step(0.75, f.x)), step(0.50, f.x)), + mix( mix(dst[ 5], dst[ 0], step(0.25, f.x)), mix(dst[ 1], dst[10], step(0.75, f.x)), step(0.50, f.x)), step(0.25, f.y)), + mix( mix( mix(dst[ 4], dst[ 3], step(0.25, f.x)), mix(dst[ 2], dst[11], step(0.75, f.x)), step(0.50, f.x)), + mix( mix(dst[15], dst[14], step(0.25, f.x)), mix(dst[13], dst[12], step(0.75, f.x)), step(0.50, f.x)), step(0.75, f.y)), + step(0.50, f.y)); + + return ivec4(res * vec3(63,63,63), 0); +} + + +void main() +{ + vec2 fTexcoord = vTexCoord.xy; + + ivec4 pixel;// = ivec4(texelFetch(ScreenTex, ivec2(fTexcoord), 0)); + + ivec4 mbright = ivec4(texelFetch(ScreenTex, ivec2(256*3, int(fTexcoord.y)), 0)); + int dispmode = mbright.b & 0x3; + + if (dispmode == 1) + { + ivec4 val1;// = pixel; + //ivec4 val2 = ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(256,0), 0)); + ivec4 val3 = ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(512,0), 0)); + + int compmode = val3.a & 0xF; + int eva, evb, evy; + + float xpos = val3.r + fract(fTexcoord.x); + float ypos = mod(fTexcoord.y, 192); + ivec4 _3dpix = Get3DPixel(vec2(xpos, ypos)); + + if (compmode == 4) + { + // 3D on top, blending + + if (_3dpix.a > 0) + { + eva = (_3dpix.a & 0x1F) + 1; + if (eva == 32) + { + val1 = _3dpix; + } + else + { + evb = 32 - eva; + + val1 = GetFiltered2DPixel(0); + + val1 = ((_3dpix * eva) + (val1 * evb)) >> 5; + if (eva <= 16) val1 += ivec4(1,1,1,0); + val1 = min(val1, 0x3F); + } + } + else + val1 = GetFiltered2DPixel(1); + } + else if (compmode == 1) + { + // 3D on bottom, blending + + if (_3dpix.a > 0) + { + eva = val3.g; + evb = val3.b; + + val1 = GetFiltered2DPixel(0); + + val1 = ((val1 * eva) + (_3dpix * evb)) >> 4; + val1 = min(val1, 0x3F); + } + else + val1 = GetFiltered2DPixel(1); + } + else if (compmode <= 3) + { + // 3D on top, normal/fade + + if (_3dpix.a > 0) + { + evy = val3.g; + + val1 = _3dpix; + if (compmode == 2) val1 += ((ivec4(0x3F,0x3F,0x3F,0) - val1) * evy) >> 4; + else if (compmode == 3) val1 -= (val1 * evy) >> 4; + } + else + val1 = GetFiltered2DPixel(1); + } + else + val1 = GetFiltered2DPixel(0); + + pixel = val1; + } + else + { + pixel = GetFiltered2DPixel(0); + } + + if (dispmode != 0) + { + int brightmode = mbright.g >> 6; + if (brightmode == 1) + { + // up + int evy = mbright.r & 0x1F; + if (evy > 16) evy = 16; + + pixel += ((ivec4(0x3F,0x3F,0x3F,0) - pixel) * evy) >> 4; + } + else if (brightmode == 2) + { + // down + int evy = mbright.r & 0x1F; + if (evy > 16) evy = 16; + + pixel -= (pixel * evy) >> 4; + } + } + + pixel.rgb <<= 2; + pixel.rgb |= (pixel.rgb >> 6); + + FragColor = vec4(vec3(pixel.rgb) / 255.0, 1.0); +} +)"; + + + + + + +#endif // GPU_OPENGL_SHADERS_H diff --git a/src/OpenGLSupport.cpp b/src/OpenGLSupport.cpp index f91af9b..27b1480 100644 --- a/src/OpenGLSupport.cpp +++ b/src/OpenGLSupport.cpp @@ -19,18 +19,20 @@ #include "OpenGLSupport.h" +namespace OpenGL +{ DO_PROCLIST(DECLPROC); -bool OpenGL_Init() +bool Init() { DO_PROCLIST(LOADPROC); return true; } -bool OpenGL_BuildShaderProgram(const char* vs, const char* fs, GLuint* ids, const char* name) +bool BuildShaderProgram(const char* vs, const char* fs, GLuint* ids, const char* name) { int len; int res; @@ -89,7 +91,7 @@ bool OpenGL_BuildShaderProgram(const char* vs, const char* fs, GLuint* ids, cons return true; } -bool OpenGL_LinkShaderProgram(GLuint* ids) +bool LinkShaderProgram(GLuint* ids) { int res; @@ -115,14 +117,16 @@ bool OpenGL_LinkShaderProgram(GLuint* ids) return true; } -void OpenGL_DeleteShaderProgram(GLuint* ids) +void DeleteShaderProgram(GLuint* ids) { glDeleteShader(ids[0]); glDeleteShader(ids[1]); glDeleteProgram(ids[2]); } -void OpenGL_UseShaderProgram(GLuint* ids) +void UseShaderProgram(GLuint* ids) { glUseProgram(ids[2]); } + +} diff --git a/src/OpenGLSupport.h b/src/OpenGLSupport.h index 5f92580..360d215 100644 --- a/src/OpenGLSupport.h +++ b/src/OpenGLSupport.h @@ -21,6 +21,8 @@ #include #include + +// TODO: different includes for each platform #include #include @@ -45,23 +47,11 @@ // if you need more OpenGL functions, add them to the macronator here -// TODO: handle conditionally loading certain functions for different GL versions - -#ifndef __WIN32__ - -#define DO_PROCLIST_1_3(func) -#else -#define DO_PROCLIST_1_3(func) \ +#define DO_PROCLIST(func) \ func(GLACTIVETEXTURE, glActiveTexture); \ func(GLBLENDCOLOR, glBlendColor); \ - -#endif - - -#define DO_PROCLIST(func) \ - DO_PROCLIST_1_3(func) \ \ func(GLGENFRAMEBUFFERS, glGenFramebuffers); \ func(GLDELETEFRAMEBUFFERS, glDeleteFramebuffers); \ @@ -122,14 +112,18 @@ func(GLGETSTRINGI, glGetStringi); \ +namespace OpenGL +{ + DO_PROCLIST(DECLPROC_EXT); +bool Init(); -bool OpenGL_Init(); +bool BuildShaderProgram(const char* vs, const char* fs, GLuint* ids, const char* name); +bool LinkShaderProgram(GLuint* ids); +void DeleteShaderProgram(GLuint* ids); +void UseShaderProgram(GLuint* ids); -bool OpenGL_BuildShaderProgram(const char* vs, const char* fs, GLuint* ids, const char* name); -bool OpenGL_LinkShaderProgram(GLuint* ids); -void OpenGL_DeleteShaderProgram(GLuint* ids); -void OpenGL_UseShaderProgram(GLuint* ids); +} #endif // OPENGLSUPPORT_H diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index dd838d7..6b256e0 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -56,6 +56,7 @@ char* EmuDirectory; void emuStop(); +void* oglGetProcAddress(const char* proc); namespace Platform @@ -418,7 +419,7 @@ void Semaphore_Post(void* sema) void* GL_GetProcAddress(const char* proc) { - return NULL;//uiGLGetProcAddress(proc); + return oglGetProcAddress(proc); } diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index bb08a87..096b91c 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -45,6 +45,7 @@ #include "NDS.h" #include "GBACart.h" +#include "OpenGLSupport.h" #include "GPU.h" #include "SPU.h" #include "Wifi.h" @@ -258,6 +259,54 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent) connect(this, SIGNAL(windowEmuPause()), mainWindow->actPause, SLOT(trigger())); connect(this, SIGNAL(windowEmuReset()), mainWindow->actReset, SLOT(trigger())); connect(this, SIGNAL(screenLayoutChange()), mainWindow->panel, SLOT(onScreenLayoutChanged())); + + initOpenGL(); +} + +void EmuThread::initOpenGL() +{ + QOpenGLContext* windowctx = mainWindow->getOGLContext(); + QSurfaceFormat format = windowctx->format(); + + oglSurface = new QOffscreenSurface(); + oglSurface->setFormat(format); + oglSurface->create(); + if (!oglSurface->isValid()) + { + // TODO handle this! + printf("oglSurface shat itself :(\n"); + delete oglSurface; + return; + } + + oglContext = new QOpenGLContext();//oglSurface); + oglContext->setFormat(oglSurface->format()); + oglContext->setShareContext(windowctx); + if (!oglContext->create()) + { + // TODO handle this! + printf("oglContext shat itself :(\n"); + delete oglContext; + delete oglSurface; + return; + } + + oglContext->moveToThread(this); +} + +void deinitOpenGL() +{ + // TODO!! +} + +void* oglGetProcAddress(const char* proc) +{ + return emuThread->oglGetProcAddress(proc); +} + +void* EmuThread::oglGetProcAddress(const char* proc) +{ + return (void*)oglContext->getProcAddress(proc); } void EmuThread::run() @@ -279,7 +328,11 @@ void EmuThread::run() } else*/ { - GPU3D::InitRenderer(false); + //GPU3D::InitRenderer(false); + bool res = oglContext->makeCurrent(oglSurface); + printf("good? %d\n", res); + OpenGL::Init(); + GPU3D::InitRenderer(res); } Input::Init(); @@ -755,6 +808,11 @@ void ScreenPanelGL::initializeGL() { initializeOpenGLFunctions(); + const GLubyte* renderer = glGetString(GL_RENDERER); // get renderer string + const GLubyte* version = glGetString(GL_VERSION); // version as a string + printf("OpenGL: renderer: %s\n", renderer); + printf("OpenGL: version: %s\n", version); + glClearColor(0, 0, 0, 1); screenShader = new QOpenGLShaderProgram(this); @@ -828,14 +886,24 @@ void ScreenPanelGL::paintGL() int frontbuf = GPU::FrontBuffer; glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, screenTexture); - if (GPU::Framebuffer[frontbuf][0] && GPU::Framebuffer[frontbuf][1]) + if (true) + { + // hardware-accelerated render + GPU::GLCompositor::BindOutputTexture(); + } + else { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 192, GL_RGBA, - GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][0]); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192, 256, 192, GL_RGBA, - GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][1]); + // regular render + glBindTexture(GL_TEXTURE_2D, screenTexture); + + if (GPU::Framebuffer[frontbuf][0] && GPU::Framebuffer[frontbuf][1]) + { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 192, GL_RGBA, + GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][0]); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192, 256, 192, GL_RGBA, + GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][1]); + } } GLint filter = Config::ScreenFilter ? GL_LINEAR : GL_NEAREST; @@ -1141,6 +1209,13 @@ MainWindow::~MainWindow() { } +QOpenGLContext* MainWindow::getOGLContext() +{ + // TODO: check whether we can actually pull this! + QOpenGLWidget* glpanel = (QOpenGLWidget*)panel; + return glpanel->context(); +} + void MainWindow::resizeEvent(QResizeEvent* event) { int w = event->size().width(); @@ -1755,6 +1830,13 @@ int main(int argc, char** argv) } #endif + QSurfaceFormat format; + format.setDepthBufferSize(24); + format.setStencilBufferSize(8); + format.setVersion(3, 2); + format.setProfile(QSurfaceFormat::CoreProfile); + QSurfaceFormat::setDefaultFormat(format); + audioSync = SDL_CreateCond(); audioSyncLock = SDL_CreateMutex(); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 8a8c041..67c93d0 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -40,6 +41,11 @@ class EmuThread : public QThread public: explicit EmuThread(QObject* parent = nullptr); + void initOpenGL(); + void deinitOpenGL(); + + void* oglGetProcAddress(const char* proc); + void changeWindowTitle(char* title); // to be called from the UI thread @@ -67,6 +73,9 @@ private: volatile int EmuStatus; int PrevEmuStatus; int EmuRunning; + + QOffscreenSurface* oglSurface; + QOpenGLContext* oglContext; }; @@ -161,6 +170,8 @@ public: explicit MainWindow(QWidget* parent = nullptr); ~MainWindow(); + QOpenGLContext* getOGLContext(); + protected: void resizeEvent(QResizeEvent* event) override; -- cgit v1.2.3 From ef2802ae31c4af965457216a190535b41efe749d Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 27 May 2020 21:29:47 +0200 Subject: * use GL shim window instead of offscreen surface * disable vsync by default (we'll take care of it later) --- src/frontend/qt_sdl/main.cpp | 19 +++++++++++++++++-- src/frontend/qt_sdl/main.h | 13 ++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 096b91c..8f2a61e 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -268,7 +268,7 @@ void EmuThread::initOpenGL() QOpenGLContext* windowctx = mainWindow->getOGLContext(); QSurfaceFormat format = windowctx->format(); - oglSurface = new QOffscreenSurface(); + /*oglSurface = new QOffscreenSurface(); oglSurface->setFormat(format); oglSurface->create(); if (!oglSurface->isValid()) @@ -277,7 +277,8 @@ void EmuThread::initOpenGL() printf("oglSurface shat itself :(\n"); delete oglSurface; return; - } + }*/ + oglSurface = new GLShim(format); oglContext = new QOpenGLContext();//oglSurface); oglContext->setFormat(oglSurface->format()); @@ -957,6 +958,19 @@ void ScreenPanelGL::onScreenLayoutChanged() } +GLShim::GLShim(QSurfaceFormat& format) : QWindow() +{ + setSurfaceType(QSurface::OpenGLSurface); + setFormat(format); + create(); + hide(); +} + +GLShim::~GLShim() +{ +} + + MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) { setWindowTitle("melonDS " MELONDS_VERSION); @@ -1835,6 +1849,7 @@ int main(int argc, char** argv) format.setStencilBufferSize(8); format.setVersion(3, 2); format.setProfile(QSurfaceFormat::CoreProfile); + format.setSwapInterval(0); QSurfaceFormat::setDefaultFormat(format); audioSync = SDL_CreateCond(); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 67c93d0..2a24960 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -74,7 +75,7 @@ private: int PrevEmuStatus; int EmuRunning; - QOffscreenSurface* oglSurface; + QSurface* oglSurface; QOpenGLContext* oglContext; }; @@ -162,6 +163,16 @@ private: }; +class GLShim : public QWindow +{ + Q_OBJECT + +public: + explicit GLShim(QSurfaceFormat& format); + ~GLShim(); +}; + + class MainWindow : public QMainWindow { Q_OBJECT -- cgit v1.2.3 From e8849db78abe4db518c7f324323b88f478acc69a Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 27 May 2020 21:40:02 +0200 Subject: we don't need a GLShim. blarg. I don't understand this anymore. --- src/GPU3D_OpenGL.cpp | 1 - src/frontend/qt_sdl/main.cpp | 18 ++---------------- src/frontend/qt_sdl/main.h | 12 +----------- 3 files changed, 3 insertions(+), 28 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/GPU3D_OpenGL.cpp b/src/GPU3D_OpenGL.cpp index dcc4b6b..74760e5 100644 --- a/src/GPU3D_OpenGL.cpp +++ b/src/GPU3D_OpenGL.cpp @@ -199,7 +199,6 @@ bool Init() glEnable(GL_DEPTH_TEST); glEnable(GL_STENCIL_TEST); - glDepthRange(0, 1); glClearDepth(1.0); diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 8f2a61e..f282310 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -268,7 +268,7 @@ void EmuThread::initOpenGL() QOpenGLContext* windowctx = mainWindow->getOGLContext(); QSurfaceFormat format = windowctx->format(); - /*oglSurface = new QOffscreenSurface(); + oglSurface = new QOffscreenSurface(); oglSurface->setFormat(format); oglSurface->create(); if (!oglSurface->isValid()) @@ -277,8 +277,7 @@ void EmuThread::initOpenGL() printf("oglSurface shat itself :(\n"); delete oglSurface; return; - }*/ - oglSurface = new GLShim(format); + } oglContext = new QOpenGLContext();//oglSurface); oglContext->setFormat(oglSurface->format()); @@ -958,19 +957,6 @@ void ScreenPanelGL::onScreenLayoutChanged() } -GLShim::GLShim(QSurfaceFormat& format) : QWindow() -{ - setSurfaceType(QSurface::OpenGLSurface); - setFormat(format); - create(); - hide(); -} - -GLShim::~GLShim() -{ -} - - MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) { setWindowTitle("melonDS " MELONDS_VERSION); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 2a24960..8759e95 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -75,7 +75,7 @@ private: int PrevEmuStatus; int EmuRunning; - QSurface* oglSurface; + QOffscreenSurface* oglSurface; QOpenGLContext* oglContext; }; @@ -163,16 +163,6 @@ private: }; -class GLShim : public QWindow -{ - Q_OBJECT - -public: - explicit GLShim(QSurfaceFormat& format); - ~GLShim(); -}; - - class MainWindow : public QMainWindow { Q_OBJECT -- cgit v1.2.3 From 0a68eb78039a7a27b16d389844beeebed139f864 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 28 May 2020 11:52:12 +0200 Subject: make it hiDPI compliant (I hope) also misc tweaks --- src/GPU3D_OpenGL.cpp | 2 +- src/OpenGLSupport.h | 5 +++++ src/frontend/qt_sdl/AudioSettingsDialog.ui | 7 +++++-- src/frontend/qt_sdl/main.cpp | 6 +++--- 4 files changed, 14 insertions(+), 6 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/GPU3D_OpenGL.cpp b/src/GPU3D_OpenGL.cpp index 74760e5..9a78504 100644 --- a/src/GPU3D_OpenGL.cpp +++ b/src/GPU3D_OpenGL.cpp @@ -1236,7 +1236,7 @@ void RenderFrame() glBlitFramebuffer(0, 0, ScreenW, ScreenH, 0, 0, ScreenW/2, ScreenH/2, GL_COLOR_BUFFER_BIT, GL_LINEAR); } - glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID[FrontBuffer]); + //glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID[FrontBuffer]); FrontBuffer = FrontBuffer ? 0 : 1; } diff --git a/src/OpenGLSupport.h b/src/OpenGLSupport.h index 360d215..edf7eda 100644 --- a/src/OpenGLSupport.h +++ b/src/OpenGLSupport.h @@ -102,6 +102,11 @@ func(GLGETUNIFORMLOCATION, glGetUniformLocation); \ func(GLGETUNIFORMBLOCKINDEX, glGetUniformBlockIndex); \ \ + func(GLFENCESYNC, glFenceSync); \ + func(GLDELETESYNC, glDeleteSync); \ + func(GLWAITSYNC, glWaitSync); \ + func(GLCLIENTWAITSYNC, glClientWaitSync); \ + \ func(GLDRAWBUFFERS, glDrawBuffers); \ \ func(GLBLENDFUNCSEPARATE, glBlendFuncSeparate); \ diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.ui b/src/frontend/qt_sdl/AudioSettingsDialog.ui index c4e993e..9ae8baa 100644 --- a/src/frontend/qt_sdl/AudioSettingsDialog.ui +++ b/src/frontend/qt_sdl/AudioSettingsDialog.ui @@ -60,12 +60,15 @@ 0 + + <html><head/><body><p>Forward a WAV file to the emulated microphone.</p><p>This input mode is activated by holding the microphone hotkey (see Input and Hotkeys).</p></body></html> + - <html><head/><body><p>Forward a WAV file to the emulated microphone.</p></body></html> + <html><head/><body><p>Forward a WAV file to the emulated microphone.</p><p>This input mode is activated by holding the microphone hotkey (see Input and Hotkeys).</p></body></html> WAV file: @@ -92,7 +95,7 @@ - <html><head/><body><p>Noise will be forwarded to the emulated microphone, simulating blowing into the microphone.</p></body></html> + <html><head/><body><p>Noise will be forwarded to the emulated microphone, simulating blowing into the microphone.</p><p>This input mode is activated by holding the microphone hotkey (see Input and Hotkeys).</p></body></html> Blow noise diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index f282310..20084ff 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -874,15 +874,15 @@ void ScreenPanelGL::paintGL() { int w = width(); int h = height(); + float factor = devicePixelRatioF(); glClear(GL_COLOR_BUFFER_BIT); - // TODO: check hiDPI compliance of this - glViewport(0, 0, w, h); + glViewport(0, 0, w*factor, h*factor); screenShader->bind(); - screenShader->setUniformValue("uScreenSize", (float)w, (float)h); + screenShader->setUniformValue("uScreenSize", (float)w*factor, (float)h*factor); int frontbuf = GPU::FrontBuffer; glActiveTexture(GL_TEXTURE0); -- cgit v1.2.3 From 695839bb0ef8173ad2a549dd7766186fa65e949a Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 28 May 2020 12:32:50 +0200 Subject: lay base for video settings dialog --- src/frontend/qt_sdl/CMakeLists.txt | 1 + src/frontend/qt_sdl/VideoSettingsDialog.cpp | 67 +++++++++ src/frontend/qt_sdl/VideoSettingsDialog.h | 67 +++++++++ src/frontend/qt_sdl/VideoSettingsDialog.ui | 220 ++++++++++++++++++++++++++++ src/frontend/qt_sdl/main.cpp | 3 +- 5 files changed, 357 insertions(+), 1 deletion(-) create mode 100644 src/frontend/qt_sdl/VideoSettingsDialog.cpp create mode 100644 src/frontend/qt_sdl/VideoSettingsDialog.h create mode 100644 src/frontend/qt_sdl/VideoSettingsDialog.ui (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 5778b40..a4bb5f5 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -5,6 +5,7 @@ SET(SOURCES_QT_SDL main_shaders.h EmuSettingsDialog.cpp InputConfigDialog.cpp + VideoSettingsDialog.cpp AudioSettingsDialog.cpp Input.cpp Platform.cpp diff --git a/src/frontend/qt_sdl/VideoSettingsDialog.cpp b/src/frontend/qt_sdl/VideoSettingsDialog.cpp new file mode 100644 index 0000000..c0ec42a --- /dev/null +++ b/src/frontend/qt_sdl/VideoSettingsDialog.cpp @@ -0,0 +1,67 @@ +/* + Copyright 2016-2020 Arisotura + + 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 +#include + +#include "types.h" +#include "Platform.h" +#include "Config.h" +#include "PlatformConfig.h" + +#include "VideoSettingsDialog.h" +#include "ui_VideoSettingsDialog.h" + + +VideoSettingsDialog* VideoSettingsDialog::currentDlg = nullptr; + + +VideoSettingsDialog::VideoSettingsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::VideoSettingsDialog) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + + // + + grp3DRenderer = new QButtonGroup(this); + grp3DRenderer->addButton(ui->rb3DSoftware, 0); + grp3DRenderer->addButton(ui->rb3DOpenGL, 1); + //connect(grp3DRenderer, SIGNAL(buttonClicked(int)), this, SLOT(onChange3DRenderer(int))); +} + +VideoSettingsDialog::~VideoSettingsDialog() +{ + delete ui; +} + +void VideoSettingsDialog::on_VideoSettingsDialog_accepted() +{ + // + Config::Save(); + + closeDlg(); +} + +void VideoSettingsDialog::on_VideoSettingsDialog_rejected() +{ + // + + closeDlg(); +} + +// diff --git a/src/frontend/qt_sdl/VideoSettingsDialog.h b/src/frontend/qt_sdl/VideoSettingsDialog.h new file mode 100644 index 0000000..d64cee2 --- /dev/null +++ b/src/frontend/qt_sdl/VideoSettingsDialog.h @@ -0,0 +1,67 @@ +/* + Copyright 2016-2020 Arisotura + + 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 VIDEOSETTINGSDIALOG_H +#define VIDEOSETTINGSDIALOG_H + +#include +#include + +namespace Ui { class VideoSettingsDialog; } +class VideoSettingsDialog; + +class VideoSettingsDialog : public QDialog +{ + Q_OBJECT + +public: + explicit VideoSettingsDialog(QWidget* parent); + ~VideoSettingsDialog(); + + static VideoSettingsDialog* currentDlg; + static VideoSettingsDialog* openDlg(QWidget* parent) + { + if (currentDlg) + { + currentDlg->activateWindow(); + return currentDlg; + } + + currentDlg = new VideoSettingsDialog(parent); + currentDlg->show(); + return currentDlg; + } + static void closeDlg() + { + currentDlg = nullptr; + } + +private slots: + void on_VideoSettingsDialog_accepted(); + void on_VideoSettingsDialog_rejected(); + + // + +private: + Ui::VideoSettingsDialog* ui; + + QButtonGroup* grp3DRenderer; +}; + +#endif // VIDEOSETTINGSDIALOG_H + diff --git a/src/frontend/qt_sdl/VideoSettingsDialog.ui b/src/frontend/qt_sdl/VideoSettingsDialog.ui new file mode 100644 index 0000000..06670e5 --- /dev/null +++ b/src/frontend/qt_sdl/VideoSettingsDialog.ui @@ -0,0 +1,220 @@ + + + VideoSettingsDialog + + + + 0 + 0 + 482 + 237 + + + + Video settings - melonDS + + + + + + OpenGL renderer + + + + + + Internal resolution: + + + + + + + <html><head/><body><p>The resolution at which the 3D graphics will be rendered. Higher resolutions improve graphics quality when the main window is enlarged, but may also cause glitches.</p></body></html> + + + + + + + + + + Software renderer + + + + + + <html><head/><body><p>Run the software renderer on a separate thread. Yields better performance on multi-core CPUs.</p></body></html> + + + Use separate thread + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Display settings + + + + + + + 0 + 0 + + + + <html><head/><body><p>The interval at which to synchronize to the monitor's refresh rate. Set to 1 for a 60Hz monitor, 2 for 120Hz, ...</p></body></html> + + + VSync interval: + + + + + + + <html><head/><body><p>The interval at which to synchronize to the monitor's refresh rate. Set to 1 for a 60Hz monitor, 2 for 120Hz, ...</p></body></html> + + + 1 + + + 20 + + + + + + + <html><head/><body><p>Use OpenGL to draw the DS screens to the main window. May result in better frame pacing. Mandatory when using the OpenGL 3D renderer.</p></body></html> + + + OpenGL display + + + + + + + <html><head/><body><p>When using OpenGL, synchronize the video output to your monitor's refresh rate.</p></body></html> + + + VSync + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + <html><head/><body><p>The OpenGL renderer may be faster than software and supports graphical enhancements, but is more prone to glitches.</p></body></html> + + + OpenGL + + + + + + + <html><head/><body><p>The software renderer is more accurate and less prone to rendering glitches, but requires more CPU power.</p></body></html> + + + Software + + + + + + + 3D renderer: + + + + + + + + + + + + buttonBox + accepted() + VideoSettingsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + VideoSettingsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 20084ff..ae4bcf3 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -36,6 +36,7 @@ #include "Input.h" #include "EmuSettingsDialog.h" #include "InputConfigDialog.h" +#include "VideoSettingsDialog.h" #include "AudioSettingsDialog.h" #include "types.h" @@ -1573,7 +1574,7 @@ void MainWindow::onInputConfigFinished(int res) void MainWindow::onOpenVideoSettings() { - // + VideoSettingsDialog* dlg = VideoSettingsDialog::openDlg(this); } void MainWindow::onOpenAudioSettings() -- cgit v1.2.3 From 0804ab3c783363e28229a2028dc70b4238cb1802 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 28 May 2020 15:53:32 +0200 Subject: * rework GPU's settings interface, make it config-agnostic * make video settings dialog functional, sorta * fix dialogs that were resizable --- src/GPU.cpp | 78 +++++++++++++- src/GPU.h | 22 +++- src/GPU2D.cpp | 14 +-- src/GPU2D.h | 2 +- src/GPU3D.cpp | 57 +---------- src/GPU3D.h | 7 +- src/GPU3D_OpenGL.cpp | 17 +--- src/GPU3D_Soft.cpp | 10 +- src/GPU_OpenGL.cpp | 39 ++++--- src/frontend/qt_sdl/AudioSettingsDialog.ui | 9 ++ src/frontend/qt_sdl/VideoSettingsDialog.cpp | 79 +++++++++++++-- src/frontend/qt_sdl/VideoSettingsDialog.h | 9 +- src/frontend/qt_sdl/VideoSettingsDialog.ui | 9 ++ src/frontend/qt_sdl/main.cpp | 152 +++++++++++++++++++--------- src/frontend/qt_sdl/main.h | 5 + 15 files changed, 342 insertions(+), 167 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/GPU.cpp b/src/GPU.cpp index 993086e..56db0e2 100644 --- a/src/GPU.cpp +++ b/src/GPU.cpp @@ -78,6 +78,7 @@ u8* VRAMPtr_BOBJ[0x8]; int FrontBuffer; u32* Framebuffer[2][2]; +int Renderer; bool Accelerated; GPU2D* GPU2D_A; @@ -93,8 +94,8 @@ bool Init() FrontBuffer = 0; Framebuffer[0][0] = NULL; Framebuffer[0][1] = NULL; Framebuffer[1][0] = NULL; Framebuffer[1][1] = NULL; + Renderer = 0; Accelerated = false; - SetDisplaySettings(false); return true; } @@ -182,6 +183,8 @@ void Reset() int backbuf = FrontBuffer ? 0 : 1; GPU2D_A->SetFramebuffer(Framebuffer[backbuf][1]); GPU2D_B->SetFramebuffer(Framebuffer[backbuf][0]); + + ResetRenderer(); } void Stop() @@ -274,8 +277,65 @@ void AssignFramebuffers() } } -void SetDisplaySettings(bool accel) +void InitRenderer(int renderer) +{ + if (renderer == 1) + { + if (!GLCompositor::Init()) + { + renderer = 0; + } + else if (!GPU3D::GLRenderer::Init()) + { + GLCompositor::DeInit(); + renderer = 0; + } + } + + if (renderer == 0) + { + GPU3D::SoftRenderer::Init(); + } + + Renderer = renderer; + Accelerated = renderer != 0; +} + +void DeInitRenderer() +{ + if (Renderer == 0) + { + GPU3D::SoftRenderer::DeInit(); + } + else + { + GPU3D::GLRenderer::DeInit(); + GLCompositor::DeInit(); + } +} + +void ResetRenderer() { + if (Renderer == 0) + { + GPU3D::SoftRenderer::Reset(); + } + else + { + GLCompositor::Reset(); + GPU3D::GLRenderer::Reset(); + } +} + +void SetRenderSettings(int renderer, RenderSettings& settings) +{ + if (renderer != Renderer) + { + DeInitRenderer(); + InitRenderer(renderer); + } + + bool accel = Accelerated; int fbsize; if (accel) fbsize = (256*3 + 1) * 192; else fbsize = 256 * 192; @@ -296,10 +356,18 @@ void SetDisplaySettings(bool accel) AssignFramebuffers(); - GPU2D_A->SetDisplaySettings(accel); - GPU2D_B->SetDisplaySettings(accel); + GPU2D_A->SetRenderSettings(accel); + GPU2D_B->SetRenderSettings(accel); - Accelerated = accel; + if (Renderer == 0) + { + GPU3D::SoftRenderer::SetRenderSettings(settings); + } + else + { + GLCompositor::SetRenderSettings(settings); + GPU3D::GLRenderer::SetRenderSettings(settings); + } } diff --git a/src/GPU.h b/src/GPU.h index e85a5b4..039e065 100644 --- a/src/GPU.h +++ b/src/GPU.h @@ -20,7 +20,6 @@ #define GPU_H #include "GPU2D.h" -#include "GPU3D.h" namespace GPU { @@ -72,6 +71,17 @@ extern u32* Framebuffer[2][2]; extern GPU2D* GPU2D_A; extern GPU2D* GPU2D_B; +extern int Renderer; + + +typedef struct +{ + bool Soft_Threaded; + + int GL_ScaleFactor; + +} RenderSettings; + bool Init(); void DeInit(); @@ -80,7 +90,11 @@ void Stop(); void DoSavestate(Savestate* file); -void SetDisplaySettings(bool accel); +void InitRenderer(int renderer); +void DeInitRenderer(); +void ResetRenderer(); + +void SetRenderSettings(int renderer, RenderSettings& settings); u8* GetUniqueBankPtr(u32 mask, u32 offset); @@ -429,7 +443,7 @@ bool Init(); void DeInit(); void Reset(); -void UpdateDisplaySettings(); +void SetRenderSettings(RenderSettings& settings); void RenderFrame(); void BindOutputTexture(); @@ -438,4 +452,6 @@ void BindOutputTexture(); } +#include "GPU3D.h" + #endif diff --git a/src/GPU2D.cpp b/src/GPU2D.cpp index 6f950b7..604a4ee 100644 --- a/src/GPU2D.cpp +++ b/src/GPU2D.cpp @@ -202,16 +202,8 @@ void GPU2D::DoSavestate(Savestate* file) file->Var32(&CaptureCnt); } - if (file->IsAtleastVersion(2, 1)) - { - file->Var32(&Win0Active); - file->Var32(&Win1Active); - } - else - { - Win0Active = 0; - Win1Active = 0; - } + file->Var32(&Win0Active); + file->Var32(&Win1Active); if (!file->Saving) { @@ -232,7 +224,7 @@ void GPU2D::SetFramebuffer(u32* buf) Framebuffer = buf; } -void GPU2D::SetDisplaySettings(bool accel) +void GPU2D::SetRenderSettings(bool accel) { Accelerated = accel; diff --git a/src/GPU2D.h b/src/GPU2D.h index c4bd2f9..521adf0 100644 --- a/src/GPU2D.h +++ b/src/GPU2D.h @@ -31,7 +31,7 @@ public: void SetEnabled(bool enable) { Enabled = enable; } void SetFramebuffer(u32* buf); - void SetDisplaySettings(bool accel); + void SetRenderSettings(bool accel); u8 Read8(u32 addr); u16 Read16(u32 addr); diff --git a/src/GPU3D.cpp b/src/GPU3D.cpp index e687e37..bd27783 100644 --- a/src/GPU3D.cpp +++ b/src/GPU3D.cpp @@ -157,8 +157,6 @@ u32 NumCommands, CurCommand, ParamCount, TotalParams; bool GeometryEnabled; bool RenderingEnabled; -int Renderer; - u32 DispCnt; u8 AlphaRefVal, AlphaRef; @@ -280,17 +278,11 @@ bool Init() CmdStallQueue = new FIFO(64); - Renderer = -1; - // SetRenderer() will be called to set it up later - return true; } void DeInit() { - if (Renderer == 0) SoftRenderer::DeInit(); - else GLRenderer::DeInit(); - delete CmdFIFO; delete CmdPIPE; @@ -391,8 +383,6 @@ void Reset() FlushAttributes = 0; ResetRenderingState(); - if (Renderer == 0) SoftRenderer::Reset(); - else GLRenderer::Reset(); } void DoSavestate(Savestate* file) @@ -607,43 +597,6 @@ void SetEnabled(bool geometry, bool rendering) } -int InitRenderer(bool hasGL) -{ - int renderer = hasGL ? Config::_3DRenderer : 0; - - if (renderer == 1) - { - if (!GLRenderer::Init()) - renderer = 0; - } -printf("renderer: %d\n", renderer); - if (renderer == 0) SoftRenderer::Init(); - - Renderer = renderer; - UpdateRendererConfig(); - GPU::SetDisplaySettings(Renderer != 0); - return renderer; -} - -void DeInitRenderer() -{ - if (Renderer == 0) SoftRenderer::DeInit(); - else GLRenderer::DeInit(); -} - -void UpdateRendererConfig() -{ - if (Renderer == 0) - { - SoftRenderer::SetupRenderThread(); - } - else - { - GLRenderer::UpdateDisplaySettings(); - } -} - - void MatrixLoadIdentity(s32* m) { @@ -2470,7 +2423,7 @@ void CheckFIFODMA() void VCount144() { - if (Renderer == 0) SoftRenderer::VCount144(); + if (GPU::Renderer == 0) SoftRenderer::VCount144(); } @@ -2552,14 +2505,14 @@ void VBlank() void VCount215() { - if (Renderer == 0) SoftRenderer::RenderFrame(); - else GLRenderer::RenderFrame(); + if (GPU::Renderer == 0) SoftRenderer::RenderFrame(); + else GLRenderer::RenderFrame(); } u32* GetLine(int line) { - if (Renderer == 0) return SoftRenderer::GetLine(line); - else return GLRenderer::GetLine(line); + if (GPU::Renderer == 0) return SoftRenderer::GetLine(line); + else return GLRenderer::GetLine(line); } diff --git a/src/GPU3D.h b/src/GPU3D.h index 1fd3383..71f069d 100644 --- a/src/GPU3D.h +++ b/src/GPU3D.h @@ -102,10 +102,6 @@ void DoSavestate(Savestate* file); void SetEnabled(bool geometry, bool rendering); -int InitRenderer(bool hasGL); -void DeInitRenderer(); -void UpdateRendererConfig(); - void ExecuteCommand(); s32 CyclesToRunFor(); @@ -134,6 +130,7 @@ bool Init(); void DeInit(); void Reset(); +void SetRenderSettings(GPU::RenderSettings& settings); void SetupRenderThread(); void VCount144(); @@ -149,7 +146,7 @@ bool Init(); void DeInit(); void Reset(); -void UpdateDisplaySettings(); +void SetRenderSettings(GPU::RenderSettings& settings); void RenderFrame(); void PrepareCaptureFrame(); diff --git a/src/GPU3D_OpenGL.cpp b/src/GPU3D_OpenGL.cpp index 9a78504..8a06874 100644 --- a/src/GPU3D_OpenGL.cpp +++ b/src/GPU3D_OpenGL.cpp @@ -372,19 +372,11 @@ bool Init() glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, 1024, 48, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, NULL); - if (!GPU::GLCompositor::Init()) - { - // TODO: clean up things? fail more gracefully?? - return false; - } - return true; } void DeInit() { - GPU::GLCompositor::DeInit(); - glDeleteTextures(1, &TexMemID); glDeleteTextures(1, &TexPalMemID); @@ -407,13 +399,12 @@ void DeInit() void Reset() { - GPU::GLCompositor::Reset(); } -void UpdateDisplaySettings() +void SetRenderSettings(GPU::RenderSettings& settings) { - int scale = Config::GL_ScaleFactor; - bool antialias = false; //Config::GL_Antialias; + int scale = settings.GL_ScaleFactor; + bool antialias = false; // REMOVE ME! if (antialias) scale *= 2; @@ -490,8 +481,6 @@ void UpdateDisplaySettings() //glLineWidth(scale); //glLineWidth(1.5); - - GPU::GLCompositor::UpdateDisplaySettings(); } diff --git a/src/GPU3D_Soft.cpp b/src/GPU3D_Soft.cpp index 8397898..e9d8e75 100644 --- a/src/GPU3D_Soft.cpp +++ b/src/GPU3D_Soft.cpp @@ -60,6 +60,7 @@ bool Enabled; // threading +bool Threaded; void* RenderThread; bool RenderThreadRunning; bool RenderThreadRendering; @@ -83,7 +84,7 @@ void StopRenderThread() void SetupRenderThread() { - if (Config::Threaded3D) + if (Threaded) { if (!RenderThreadRunning) { @@ -112,6 +113,7 @@ bool Init() Sema_RenderDone = Platform::Semaphore_Create(); Sema_ScanlineCount = Platform::Semaphore_Create(); + Threaded = false; RenderThreadRunning = false; RenderThreadRendering = false; @@ -138,6 +140,12 @@ void Reset() SetupRenderThread(); } +void SetRenderSettings(GPU::RenderSettings& settings) +{ + Threaded = settings.Soft_Threaded; + SetupRenderThread(); +} + // Notes on the interpolator: diff --git a/src/GPU_OpenGL.cpp b/src/GPU_OpenGL.cpp index 99eb845..1cb6864 100644 --- a/src/GPU_OpenGL.cpp +++ b/src/GPU_OpenGL.cpp @@ -142,9 +142,9 @@ void Reset() } -void UpdateDisplaySettings() +void SetRenderSettings(RenderSettings& settings) { - int scale = Config::GL_ScaleFactor; + int scale = settings.GL_ScaleFactor; Scale = scale; ScreenW = 256 * scale; @@ -175,28 +175,25 @@ void RenderFrame() OpenGL::UseShaderProgram(CompShader[0]); glUniform1ui(CompScaleLoc[0], Scale); - //if (RunningSomething) + int frontbuf = GPU::FrontBuffer; + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, CompScreenInputTex); + + if (GPU::Framebuffer[frontbuf][0] && GPU::Framebuffer[frontbuf][1]) { - int frontbuf = GPU::FrontBuffer; - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, CompScreenInputTex); - - if (GPU::Framebuffer[frontbuf][0] && GPU::Framebuffer[frontbuf][1]) - { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256*3 + 1, 192, GL_RGBA_INTEGER, - GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][0]); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192, 256*3 + 1, 192, GL_RGBA_INTEGER, - GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][1]); - } - - glActiveTexture(GL_TEXTURE1); - GPU3D::GLRenderer::SetupAccelFrame(); - - glBindBuffer(GL_ARRAY_BUFFER, CompVertexBufferID); - glBindVertexArray(CompVertexArrayID); - glDrawArrays(GL_TRIANGLES, 0, 4*3); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256*3 + 1, 192, GL_RGBA_INTEGER, + GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][0]); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192, 256*3 + 1, 192, GL_RGBA_INTEGER, + GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][1]); } + glActiveTexture(GL_TEXTURE1); + GPU3D::GLRenderer::SetupAccelFrame(); + + glBindBuffer(GL_ARRAY_BUFFER, CompVertexBufferID); + glBindVertexArray(CompVertexArrayID); + glDrawArrays(GL_TRIANGLES, 0, 4*3); + glFlush(); } diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.ui b/src/frontend/qt_sdl/AudioSettingsDialog.ui index 9ae8baa..bcaf937 100644 --- a/src/frontend/qt_sdl/AudioSettingsDialog.ui +++ b/src/frontend/qt_sdl/AudioSettingsDialog.ui @@ -10,10 +10,19 @@ 230 + + + 0 + 0 + + Audio settings - melonDS + + QLayout::SetFixedSize + diff --git a/src/frontend/qt_sdl/VideoSettingsDialog.cpp b/src/frontend/qt_sdl/VideoSettingsDialog.cpp index 0aeb154..09f1ab4 100644 --- a/src/frontend/qt_sdl/VideoSettingsDialog.cpp +++ b/src/frontend/qt_sdl/VideoSettingsDialog.cpp @@ -46,7 +46,7 @@ VideoSettingsDialog::VideoSettingsDialog(QWidget* parent) : QDialog(parent), ui( grp3DRenderer = new QButtonGroup(this); grp3DRenderer->addButton(ui->rb3DSoftware, 0); grp3DRenderer->addButton(ui->rb3DOpenGL, 1); - //connect(grp3DRenderer, SIGNAL(buttonClicked(int)), this, SLOT(onChange3DRenderer(int))); + connect(grp3DRenderer, SIGNAL(buttonClicked(int)), this, SLOT(onChange3DRenderer(int))); grp3DRenderer->button(Config::_3DRenderer)->setChecked(true); ui->cbGLDisplay->setChecked(Config::ScreenUseGL != 0); @@ -57,8 +57,21 @@ VideoSettingsDialog::VideoSettingsDialog(QWidget* parent) : QDialog(parent), ui( ui->cbSoftwareThreaded->setChecked(Config::Threaded3D != 0); for (int i = 1; i <= 16; i++) - ui->cbxGLResolution->addItem(QString("%1x native (%2x%3)").arg(i).arg(256*i).arg(192*i), QVariant(i)); - ui->cbxGLResolution->setCurrentIndex(Config::GL_ScaleFactor); + ui->cbxGLResolution->addItem(QString("%1x native (%2x%3)").arg(i).arg(256*i).arg(192*i)); + ui->cbxGLResolution->setCurrentIndex(Config::GL_ScaleFactor-1); + + if (Config::_3DRenderer == 0) + { + ui->cbGLDisplay->setEnabled(true); + ui->cbSoftwareThreaded->setEnabled(true); + ui->cbxGLResolution->setEnabled(false); + } + else + { + ui->cbGLDisplay->setEnabled(false); + ui->cbSoftwareThreaded->setEnabled(false); + ui->cbxGLResolution->setEnabled(true); + } } VideoSettingsDialog::~VideoSettingsDialog() @@ -68,7 +81,6 @@ VideoSettingsDialog::~VideoSettingsDialog() void VideoSettingsDialog::on_VideoSettingsDialog_accepted() { - // Config::Save(); closeDlg(); @@ -76,9 +88,64 @@ void VideoSettingsDialog::on_VideoSettingsDialog_accepted() void VideoSettingsDialog::on_VideoSettingsDialog_rejected() { - // + bool old_gl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0); + + Config::_3DRenderer = oldRenderer; + Config::ScreenUseGL = oldGLDisplay; + Config::ScreenVSync = oldVSync; + Config::ScreenVSyncInterval = oldVSyncInterval; + Config::Threaded3D = oldSoftThreaded; + Config::GL_ScaleFactor = oldGLScale; + + bool new_gl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0); + emit updateVideoSettings(old_gl != new_gl); closeDlg(); } -// +void VideoSettingsDialog::onChange3DRenderer(int renderer) +{ + bool old_gl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0); + + Config::_3DRenderer = renderer; + + if (renderer == 0) + { + ui->cbGLDisplay->setEnabled(true); + ui->cbSoftwareThreaded->setEnabled(true); + ui->cbxGLResolution->setEnabled(false); + } + else + { + ui->cbGLDisplay->setEnabled(false); + ui->cbSoftwareThreaded->setEnabled(false); + ui->cbxGLResolution->setEnabled(true); + } + + bool new_gl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0); + emit updateVideoSettings(old_gl != new_gl); +} + +void VideoSettingsDialog::on_cbGLDisplay_stateChanged(int state) +{ + bool old_gl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0); + + Config::ScreenUseGL = (state != 0); + + bool new_gl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0); + emit updateVideoSettings(old_gl != new_gl); +} + +void VideoSettingsDialog::on_cbSoftwareThreaded_stateChanged(int state) +{ + Config::Threaded3D = (state != 0); + + emit updateVideoSettings(false); +} + +void VideoSettingsDialog::on_cbxGLResolution_currentIndexChanged(int idx) +{ + Config::GL_ScaleFactor = idx+1; + + emit updateVideoSettings(false); +} diff --git a/src/frontend/qt_sdl/VideoSettingsDialog.h b/src/frontend/qt_sdl/VideoSettingsDialog.h index 05dfecb..2f6d17c 100644 --- a/src/frontend/qt_sdl/VideoSettingsDialog.h +++ b/src/frontend/qt_sdl/VideoSettingsDialog.h @@ -51,11 +51,18 @@ public: currentDlg = nullptr; } +signals: + void updateVideoSettings(bool glchange); + private slots: void on_VideoSettingsDialog_accepted(); void on_VideoSettingsDialog_rejected(); - // + void onChange3DRenderer(int renderer); + void on_cbGLDisplay_stateChanged(int state); + void on_cbxGLResolution_currentIndexChanged(int idx); + + void on_cbSoftwareThreaded_stateChanged(int state); private: Ui::VideoSettingsDialog* ui; diff --git a/src/frontend/qt_sdl/VideoSettingsDialog.ui b/src/frontend/qt_sdl/VideoSettingsDialog.ui index 06670e5..6cdd5d8 100644 --- a/src/frontend/qt_sdl/VideoSettingsDialog.ui +++ b/src/frontend/qt_sdl/VideoSettingsDialog.ui @@ -10,10 +10,19 @@ 237 + + + 0 + 0 + + Video settings - melonDS + + QLayout::SetFixedSize + diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index ae4bcf3..658ae8b 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -68,6 +68,10 @@ EmuThread* emuThread; int autoScreenSizing = 0; +int videoRenderer; +GPU::RenderSettings videoSettings; +bool videoSettingsDirty; + SDL_AudioDeviceID audioDevice; int audioFreq; SDL_cond* audioSync; @@ -251,9 +255,7 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent) EmuRunning = 2; RunningSomething = false; - //connect(this, SIGNAL(windowUpdate()), mainWindow, SLOT(update())); connect(this, SIGNAL(windowUpdate()), mainWindow->panel, SLOT(update())); - //connect(this, SIGNAL(windowUpdate()), mainWindow, 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())); @@ -312,6 +314,7 @@ void* EmuThread::oglGetProcAddress(const char* proc) void EmuThread::run() { + bool hasOGL = mainWindow->hasOGL; u32 mainScreenPos[3]; NDS::Init(); @@ -321,20 +324,20 @@ void EmuThread::run() mainScreenPos[2] = 0; autoScreenSizing = 0; - /*if (Screen_UseGL) - { - uiGLMakeContextCurrent(GLContext); - GPU3D::InitRenderer(true); - uiGLMakeContextCurrent(NULL); - } - else*/ + videoSettingsDirty = false; + videoSettings.Soft_Threaded = Config::Threaded3D != 0; + videoSettings.GL_ScaleFactor = Config::GL_ScaleFactor; + + if (hasOGL) { - //GPU3D::InitRenderer(false); - bool res = oglContext->makeCurrent(oglSurface); - printf("good? %d\n", res); - OpenGL::Init(); - GPU3D::InitRenderer(res); + oglContext->makeCurrent(oglSurface); + videoRenderer = OpenGL::Init() ? Config::_3DRenderer : 0; } + else + videoRenderer = 0; + + GPU::InitRenderer(videoRenderer); + GPU::SetRenderSettings(videoRenderer, videoSettings); Input::Init(); @@ -377,6 +380,29 @@ void EmuThread::run() { EmuStatus = 1; + // update render settings if needed + if (videoSettingsDirty) + { + if (hasOGL != mainWindow->hasOGL) + { + hasOGL = mainWindow->hasOGL; + if (hasOGL) + { + oglContext->makeCurrent(oglSurface); + videoRenderer = OpenGL::Init() ? Config::_3DRenderer : 0; + } + else + videoRenderer = 0; + } + else + videoRenderer = hasOGL ? Config::_3DRenderer : 0; + + videoSettingsDirty = false; + videoSettings.Soft_Threaded = Config::Threaded3D != 0; + videoSettings.GL_ScaleFactor = Config::GL_ScaleFactor; + GPU::SetRenderSettings(videoRenderer, videoSettings); + } + // process input and hotkeys NDS::SetKeyMask(Input::InputMask); @@ -390,12 +416,6 @@ void EmuThread::run() // microphone input micProcess(); - /*if (Screen_UseGL) - { - uiGLBegin(GLContext); - uiGLMakeContextCurrent(GLContext); - }*/ - // auto screen layout if (Config::ScreenSizing == 3) { @@ -435,12 +455,6 @@ void EmuThread::run() if (EmuRunning == 0) break; - /*if (Screen_UseGL) - { - GLScreen_DrawScreen(); - uiGLEnd(GLContext); - } - uiAreaQueueRedrawAll(MainDrawArea);*/ emit windowUpdate(); bool fastforward = Input::HotkeyDown(HK_FastForward); @@ -513,18 +527,8 @@ void EmuThread::run() lastmeasuretick = lasttick; fpslimitcount = 0; - /*if (Screen_UseGL) - { - uiGLBegin(GLContext); - uiGLMakeContextCurrent(GLContext); - GLScreen_DrawScreen(); - uiGLEnd(GLContext); - } - uiAreaQueueRedrawAll(MainDrawArea);*/ emit windowUpdate(); - //if (Screen_UseGL) uiGLMakeContextCurrent(NULL); - EmuStatus = EmuRunning; sprintf(melontitle, "melonDS " MELONDS_VERSION); @@ -536,8 +540,7 @@ void EmuThread::run() EmuStatus = 0; - //if (Screen_UseGL) uiGLMakeContextCurrent(GLContext); - + GPU::DeInitRenderer(); NDS::DeInit(); //Platform::LAN_DeInit(); @@ -549,7 +552,8 @@ void EmuThread::run() else OSD::DeInit(false);*/ - //if (Screen_UseGL) uiGLMakeContextCurrent(NULL); + if (hasOGL) + oglContext->doneCurrent(); } void EmuThread::changeWindowTitle(char* title) @@ -794,6 +798,7 @@ ScreenPanelGL::ScreenPanelGL(QWidget* parent) : QOpenGLWidget(parent) ScreenPanelGL::~ScreenPanelGL() { // CHECKME!!!! + // ALSO TODO: CLEANUP delete screenShader; } @@ -888,7 +893,7 @@ void ScreenPanelGL::paintGL() int frontbuf = GPU::FrontBuffer; glActiveTexture(GL_TEXTURE0); - if (true) + if (GPU::Renderer != 0) { // hardware-accelerated render GPU::GLCompositor::BindOutputTexture(); @@ -1162,11 +1167,8 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) } setMenuBar(menubar); - //panel = new ScreenPanelNative(this); - panel = new ScreenPanelGL(this); - setCentralWidget(panel); - connect(this, SIGNAL(screenLayoutChange()), panel, SLOT(onScreenLayoutChanged())); - emit screenLayoutChange(); + show(); + createScreenPanel(); resize(Config::WindowWidth, Config::WindowHeight); @@ -1210,9 +1212,45 @@ MainWindow::~MainWindow() { } +void MainWindow::createScreenPanel() +{ + hasOGL = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0); + + if (hasOGL) + { + ScreenPanelGL* panelGL = new ScreenPanelGL(this); + panelGL->show(); + + if (!panelGL->isValid()) + hasOGL = false; + else + { + QSurfaceFormat fmt = panelGL->format(); + if (fmt.majorVersion() < 3 || (fmt.majorVersion() == 3 && fmt.minorVersion() < 2)) + hasOGL = false; + } + + if (!hasOGL) + delete panelGL; + else + panel = panelGL; + } + + if (!hasOGL) + { + panel = new ScreenPanelNative(this); + panel->show(); + } + + setCentralWidget(panel); + connect(this, SIGNAL(screenLayoutChange()), panel, SLOT(onScreenLayoutChanged())); + emit screenLayoutChange(); +} + QOpenGLContext* MainWindow::getOGLContext() { - // TODO: check whether we can actually pull this! + if (!hasOGL) return nullptr; + QOpenGLWidget* glpanel = (QOpenGLWidget*)panel; return glpanel->context(); } @@ -1575,6 +1613,7 @@ void MainWindow::onInputConfigFinished(int res) void MainWindow::onOpenVideoSettings() { VideoSettingsDialog* dlg = VideoSettingsDialog::openDlg(this); + connect(dlg, &VideoSettingsDialog::updateVideoSettings, this, &MainWindow::onUpdateVideoSettings); } void MainWindow::onOpenAudioSettings() @@ -1745,6 +1784,23 @@ void MainWindow::onEmuStop() actStop->setEnabled(false); } +void MainWindow::onUpdateVideoSettings(bool glchange) +{ + if (glchange) + { + emuThread->emuPause(); + + delete panel; + createScreenPanel(); + connect(emuThread, SIGNAL(windowUpdate()), panel, SLOT(update())); + } + + videoSettingsDirty = true; + + if (glchange) + emuThread->emuUnpause(); +} + void emuStop() { @@ -1790,6 +1846,9 @@ int main(int argc, char** argv) Config::Load(); #define SANITIZE(var, min, max) { if (var < min) var = min; else if (var > max) var = max; } + SANITIZE(Config::_3DRenderer, 0, 1); + SANITIZE(Config::ScreenVSyncInterval, 1, 20); + SANITIZE(Config::GL_ScaleFactor, 1, 16); SANITIZE(Config::AudioVolume, 0, 256); SANITIZE(Config::MicInputType, 0, 3); SANITIZE(Config::ScreenRotation, 0, 3); @@ -1900,7 +1959,6 @@ int main(int argc, char** argv) Input::OpenJoystick(); mainWindow = new MainWindow(); - mainWindow->show(); emuThread = new EmuThread(); emuThread->start(); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 8759e95..ef51158 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -171,6 +171,7 @@ public: explicit MainWindow(QWidget* parent = nullptr); ~MainWindow(); + bool hasOGL; QOpenGLContext* getOGLContext(); protected: @@ -221,7 +222,11 @@ private slots: void onEmuStart(); void onEmuStop(); + void onUpdateVideoSettings(bool glchange); + private: + void createScreenPanel(); + QString loadErrorStr(int error); public: -- cgit v1.2.3 From f7e53c6f71c3bf6f082d1e5857f474f79f992602 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 28 May 2020 15:58:18 +0200 Subject: so you can't resize() before show()ing?? bullshit. --- src/frontend/qt_sdl/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 658ae8b..b6282fe 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -1167,11 +1167,11 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) } setMenuBar(menubar); + resize(Config::WindowWidth, Config::WindowHeight); + show(); createScreenPanel(); - resize(Config::WindowWidth, Config::WindowHeight); - for (int i = 0; i < 9; i++) { actSaveState[i]->setEnabled(false); -- cgit v1.2.3 From 2912a07b8babb602aaf315e62667044a07adf8e7 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 28 May 2020 17:00:25 +0200 Subject: fix bugs, clean up some of the shit --- src/GPU.cpp | 8 ++++---- src/frontend/qt_sdl/VideoSettingsDialog.cpp | 2 +- src/frontend/qt_sdl/main.cpp | 14 +++++++++++--- 3 files changed, 16 insertions(+), 8 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/GPU.cpp b/src/GPU.cpp index 56db0e2..60f863e 100644 --- a/src/GPU.cpp +++ b/src/GPU.cpp @@ -339,10 +339,10 @@ void SetRenderSettings(int renderer, RenderSettings& settings) int fbsize; if (accel) fbsize = (256*3 + 1) * 192; else fbsize = 256 * 192; - if (Framebuffer[0][0]) delete[] Framebuffer[0][0]; - if (Framebuffer[1][0]) delete[] Framebuffer[1][0]; - if (Framebuffer[0][1]) delete[] Framebuffer[0][1]; - if (Framebuffer[1][1]) delete[] Framebuffer[1][1]; + if (Framebuffer[0][0]) { delete[] Framebuffer[0][0]; Framebuffer[0][0] = nullptr; } + if (Framebuffer[1][0]) { delete[] Framebuffer[1][0]; Framebuffer[1][0] = nullptr; } + if (Framebuffer[0][1]) { delete[] Framebuffer[0][1]; Framebuffer[0][1] = nullptr; } + if (Framebuffer[1][1]) { delete[] Framebuffer[1][1]; Framebuffer[1][1] = nullptr; } Framebuffer[0][0] = new u32[fbsize]; Framebuffer[1][0] = new u32[fbsize]; diff --git a/src/frontend/qt_sdl/VideoSettingsDialog.cpp b/src/frontend/qt_sdl/VideoSettingsDialog.cpp index 9645a3d..efb5051 100644 --- a/src/frontend/qt_sdl/VideoSettingsDialog.cpp +++ b/src/frontend/qt_sdl/VideoSettingsDialog.cpp @@ -59,7 +59,7 @@ VideoSettingsDialog::VideoSettingsDialog(QWidget* parent) : QDialog(parent), ui( for (int i = 1; i <= 16; i++) ui->cbxGLResolution->addItem(QString("%1x native (%2x%3)").arg(i).arg(256*i).arg(192*i)); ui->cbxGLResolution->setCurrentIndex(Config::GL_ScaleFactor-1); -printf("GL scale = %d\n", Config::GL_ScaleFactor); + if (Config::_3DRenderer == 0) { ui->cbGLDisplay->setEnabled(true); diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index b6282fe..de71da9 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -282,7 +282,7 @@ void EmuThread::initOpenGL() return; } - oglContext = new QOpenGLContext();//oglSurface); + oglContext = new QOpenGLContext(); oglContext->setFormat(oglSurface->format()); oglContext->setShareContext(windowctx); if (!oglContext->create()) @@ -797,9 +797,16 @@ ScreenPanelGL::ScreenPanelGL(QWidget* parent) : QOpenGLWidget(parent) ScreenPanelGL::~ScreenPanelGL() { - // CHECKME!!!! - // ALSO TODO: CLEANUP + makeCurrent(); + + glDeleteTextures(1, &screenTexture); + + glDeleteVertexArrays(1, &screenVertexArray); + glDeleteBuffers(1, &screenVertexBuffer); + delete screenShader; + + doneCurrent(); } void ScreenPanelGL::setupScreenLayout() @@ -1793,6 +1800,7 @@ void MainWindow::onUpdateVideoSettings(bool glchange) delete panel; createScreenPanel(); connect(emuThread, SIGNAL(windowUpdate()), panel, SLOT(update())); + if (hasOGL) emuThread->initOpenGL(); } videoSettingsDirty = true; -- cgit v1.2.3 From 79d4183ccdac6d0b2a82f99ad169b7fe6349febc Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 28 May 2020 18:11:41 +0200 Subject: re-add OSD system --- src/frontend/qt_sdl/CMakeLists.txt | 3 + src/frontend/qt_sdl/OSD.cpp | 474 +++++++++++++++++++++++++++++++++++++ src/frontend/qt_sdl/OSD.h | 36 +++ src/frontend/qt_sdl/OSD_shaders.h | 65 +++++ src/frontend/qt_sdl/font.h | 135 +++++++++++ src/frontend/qt_sdl/main.cpp | 18 +- src/frontend/qt_sdl/main_shaders.h | 50 ---- 7 files changed, 730 insertions(+), 51 deletions(-) create mode 100644 src/frontend/qt_sdl/OSD.cpp create mode 100644 src/frontend/qt_sdl/OSD.h create mode 100644 src/frontend/qt_sdl/OSD_shaders.h create mode 100644 src/frontend/qt_sdl/font.h (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index a4bb5f5..0b7fa54 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -8,6 +8,9 @@ SET(SOURCES_QT_SDL VideoSettingsDialog.cpp AudioSettingsDialog.cpp Input.cpp + OSD.cpp + OSD_shaders.h + font.h Platform.cpp PlatformConfig.cpp diff --git a/src/frontend/qt_sdl/OSD.cpp b/src/frontend/qt_sdl/OSD.cpp new file mode 100644 index 0000000..4e4e40f --- /dev/null +++ b/src/frontend/qt_sdl/OSD.cpp @@ -0,0 +1,474 @@ +/* + Copyright 2016-2020 Arisotura + + 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 +#include +#include +#include +#include "../types.h" + +#include "main.h" +#include + +#include "OSD.h" +#include "OSD_shaders.h" +#include "font.h" + +#include "PlatformConfig.h" + +extern MainWindow* mainWindow; + +namespace OSD +{ + +const u32 kOSDMargin = 6; + +struct Item +{ + Uint32 Timestamp; + char Text[256]; + u32 Color; + + u32 Width, Height; + u32* Bitmap; + + bool NativeBitmapLoaded; + QImage NativeBitmap; + + bool GLTextureLoaded; + GLuint GLTexture; + +}; + +std::deque ItemQueue; + +QOpenGLShaderProgram* Shader; +GLint uScreenSize, uOSDPos, uOSDSize; +GLuint OSDVertexArray; +GLuint OSDVertexBuffer; + +volatile bool Rendering; + + +bool Init(QOpenGLFunctions_3_2_Core* f) +{ + if (f) + { + Shader = new QOpenGLShaderProgram(); + Shader->addShaderFromSourceCode(QOpenGLShader::Vertex, kScreenVS_OSD); + Shader->addShaderFromSourceCode(QOpenGLShader::Fragment, kScreenFS_OSD); + + GLuint pid = Shader->programId(); + f->glBindAttribLocation(pid, 0, "vPosition"); + f->glBindFragDataLocation(pid, 0, "oColor"); + + Shader->link(); + + Shader->bind(); + Shader->setUniformValue("OSDTex", (GLint)0); + Shader->release(); + + uScreenSize = Shader->uniformLocation("uScreenSize"); + uOSDPos = Shader->uniformLocation("uOSDPos"); + uOSDSize = Shader->uniformLocation("uOSDSize"); + + float vertices[6*2] = + { + 0, 0, + 1, 1, + 1, 0, + 0, 0, + 0, 1, + 1, 1 + }; + + f->glGenBuffers(1, &OSDVertexBuffer); + f->glBindBuffer(GL_ARRAY_BUFFER, OSDVertexBuffer); + f->glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + f->glGenVertexArrays(1, &OSDVertexArray); + f->glBindVertexArray(OSDVertexArray); + f->glEnableVertexAttribArray(0); // position + f->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)(0)); + } + + return true; +} + +void DeInit(QOpenGLFunctions_3_2_Core* f) +{ + for (auto it = ItemQueue.begin(); it != ItemQueue.end(); ) + { + Item& item = *it; + + if (item.GLTextureLoaded && f) f->glDeleteTextures(1, &item.GLTexture); + if (item.Bitmap) delete[] item.Bitmap; + + it = ItemQueue.erase(it); + } + + if (f) delete Shader; +} + + +int FindBreakPoint(const char* text, int i) +{ + // i = character that went out of bounds + + for (int j = i; j >= 0; j--) + { + if (text[j] == ' ') + return j; + } + + return i; +} + +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); + int lastbreak = -1; + int numbrk = 0; + u16* ptr; + + memset(breaks, 0, sizeof(int)*64); + + for (int i = 0; text[i] != '\0'; ) + { + int glyphsize; + if (text[i] == ' ') + { + glyphsize = 6; + } + else + { + u32 ch = text[i]; + if (ch < 0x10 || ch > 0x7E) ch = 0x7F; + + ptr = &font[(ch-0x10) << 4]; + glyphsize = ptr[0]; + if (!glyphsize) glyphsize = 6; + else glyphsize += 2; // space around the character + } + + w += glyphsize; + if (w > maxw) + { + // wrap shit as needed + if (text[i] == ' ') + { + if (numbrk >= 64) break; + breaks[numbrk++] = i; + i++; + } + else + { + int brk = FindBreakPoint(text, i); + if (brk != lastbreak) i = brk; + + if (numbrk >= 64) break; + breaks[numbrk++] = i; + + lastbreak = brk; + } + + w = 0; + h += 14; + } + else + i++; + + if (w > totalw) totalw = w; + } + + *width = totalw; + *height = h; +} + +u32 RainbowColor(u32 inc) +{ + // inspired from Acmlmboard + + if (inc < 100) return 0xFFFF9B9B + (inc << 8); + else if (inc < 200) return 0xFFFFFF9B - ((inc-100) << 16); + else if (inc < 300) return 0xFF9BFF9B + (inc-200); + else if (inc < 400) return 0xFF9BFFFF - ((inc-300) << 8); + else if (inc < 500) return 0xFF9B9BFF + ((inc-400) << 16); + else return 0xFFFF9BFF - (inc-500); +} + +void RenderText(u32 color, const char* text, Item* item) +{ + u32 w, h; + int breaks[64]; + + bool rainbow = (color == 0); + u32 rainbowinc = ((text[0] * 17) + (SDL_GetTicks() * 13)) % 600; + + color |= 0xFF000000; + const u32 shadow = 0xE0000000; + + LayoutText(text, &w, &h, breaks); + + item->Width = w; + item->Height = h; + item->Bitmap = new u32[w*h]; + memset(item->Bitmap, 0, w*h*sizeof(u32)); + + u32 x = 0, y = 1; + u32 maxw = mainWindow->panel->width() - (kOSDMargin*2); + int curline = 0; + u16* ptr; + + for (int i = 0; text[i] != '\0'; ) + { + int glyphsize; + if (text[i] == ' ') + { + x += 6; + } + else + { + u32 ch = text[i]; + if (ch < 0x10 || ch > 0x7E) ch = 0x7F; + + ptr = &font[(ch-0x10) << 4]; + int glyphsize = ptr[0]; + if (!glyphsize) x += 6; + else + { + x++; + + if (rainbow) + { + color = RainbowColor(rainbowinc); + rainbowinc = (rainbowinc + 30) % 600; + } + + // draw character + for (int cy = 0; cy < 12; cy++) + { + u16 val = ptr[4+cy]; + + for (int cx = 0; cx < glyphsize; cx++) + { + if (val & (1<Bitmap[((y+cy) * w) + x+cx] = color; + } + } + + x += glyphsize; + x++; + } + } + + i++; + if (breaks[curline] && i >= breaks[curline]) + { + i = breaks[curline++]; + if (text[i] == ' ') i++; + + x = 0; + y += 14; + } + } + + // shadow + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + { + u32 val; + + val = item->Bitmap[(y * w) + x]; + if ((val >> 24) == 0xFF) continue; + + if (x > 0) val = item->Bitmap[(y * w) + x-1]; + if (x < w-1) val |= item->Bitmap[(y * w) + x+1]; + if (y > 0) + { + if (x > 0) val |= item->Bitmap[((y-1) * w) + x-1]; + val |= item->Bitmap[((y-1) * w) + x]; + if (x < w-1) val |= item->Bitmap[((y-1) * w) + x+1]; + } + if (y < h-1) + { + if (x > 0) val |= item->Bitmap[((y+1) * w) + x-1]; + val |= item->Bitmap[((y+1) * w) + x]; + if (x < w-1) val |= item->Bitmap[((y+1) * w) + x+1]; + } + + if ((val >> 24) == 0xFF) + item->Bitmap[(y * w) + x] = shadow; + } + } +} + + +void AddMessage(u32 color, const char* text) +{ + if (!Config::ShowOSD) return; + + while (Rendering); + + Item item; + + item.Timestamp = SDL_GetTicks(); + strncpy(item.Text, text, 255); item.Text[255] = '\0'; + item.Color = color; + item.Bitmap = nullptr; + + item.NativeBitmapLoaded = false; + item.GLTextureLoaded = false; + + ItemQueue.push_back(item); +} + +void Update(QOpenGLFunctions_3_2_Core* f) +{ + if (!Config::ShowOSD) + { + Rendering = true; + for (auto it = ItemQueue.begin(); it != ItemQueue.end(); ) + { + Item& item = *it; + + if (item.GLTextureLoaded && f) f->glDeleteTextures(1, &item.GLTexture); + if (item.Bitmap) delete[] item.Bitmap; + + it = ItemQueue.erase(it); + } + Rendering = false; + return; + } + + Rendering = true; + + Uint32 tick_now = SDL_GetTicks(); + Uint32 tick_min = tick_now - 2500; + + for (auto it = ItemQueue.begin(); it != ItemQueue.end(); ) + { + Item& item = *it; + + if (item.Timestamp < tick_min) + { + if (item.GLTextureLoaded) f->glDeleteTextures(1, &item.GLTexture); + if (item.Bitmap) delete[] item.Bitmap; + + it = ItemQueue.erase(it); + continue; + } + + if (!item.Bitmap) + { + RenderText(item.Color, item.Text, &item); + } + + it++; + } + + Rendering = false; +} + +void DrawNative(QPainter& painter) +{ + if (!Config::ShowOSD) return; + + Rendering = true; + + u32 y = kOSDMargin; + + painter.resetTransform(); + + for (auto it = ItemQueue.begin(); it != ItemQueue.end(); ) + { + Item& item = *it; + + if (!item.NativeBitmapLoaded) + { + item.NativeBitmap = QImage((const uchar*)item.Bitmap, item.Width, item.Height, QImage::Format_ARGB32_Premultiplied); + item.NativeBitmapLoaded = true; + } + + painter.drawImage(kOSDMargin, y, item.NativeBitmap); + + y += item.Height; + it++; + } + + Rendering = false; +} + +void DrawGL(QOpenGLFunctions_3_2_Core* f, float w, float h) +{ + if (!Config::ShowOSD) return; + if (!mainWindow || !mainWindow->panel) return; + + Rendering = true; + + u32 y = kOSDMargin; + + Shader->bind(); + + f->glUniform2f(uScreenSize, w, h); + + f->glBindBuffer(GL_ARRAY_BUFFER, OSDVertexBuffer); + f->glBindVertexArray(OSDVertexArray); + + f->glActiveTexture(GL_TEXTURE0); + + f->glEnable(GL_BLEND); + f->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + for (auto it = ItemQueue.begin(); it != ItemQueue.end(); ) + { + Item& item = *it; + + if (!item.GLTextureLoaded) + { + f->glGenTextures(1, &item.GLTexture); + f->glBindTexture(GL_TEXTURE_2D, item.GLTexture); + f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, item.Width, item.Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, item.Bitmap); + + item.GLTextureLoaded = true; + } + + f->glBindTexture(GL_TEXTURE_2D, item.GLTexture); + f->glUniform2i(uOSDPos, kOSDMargin, y); + f->glUniform2i(uOSDSize, item.Width, item.Height); + f->glDrawArrays(GL_TRIANGLES, 0, 2*3); + + y += item.Height; + it++; + } + + f->glDisable(GL_BLEND); + Shader->release(); + + Rendering = false; +} + +} diff --git a/src/frontend/qt_sdl/OSD.h b/src/frontend/qt_sdl/OSD.h new file mode 100644 index 0000000..79d9df0 --- /dev/null +++ b/src/frontend/qt_sdl/OSD.h @@ -0,0 +1,36 @@ +/* + Copyright 2016-2020 Arisotura + + 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 OSD_H +#define OSD_H + +namespace OSD +{ + +bool Init(QOpenGLFunctions_3_2_Core* f); +void DeInit(QOpenGLFunctions_3_2_Core* f); + +void AddMessage(u32 color, const char* text); + +void Update(QOpenGLFunctions_3_2_Core* f); +void DrawNative(QPainter& painter); +void DrawGL(QOpenGLFunctions_3_2_Core* f, float w, float h); + +} + +#endif // OSD_H diff --git a/src/frontend/qt_sdl/OSD_shaders.h b/src/frontend/qt_sdl/OSD_shaders.h new file mode 100644 index 0000000..5a64f66 --- /dev/null +++ b/src/frontend/qt_sdl/OSD_shaders.h @@ -0,0 +1,65 @@ +/* + Copyright 2016-2020 Arisotura + + 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 OSD_SHADERS_H +#define OSD_SHADERS_H + +const char* kScreenVS_OSD = R"(#version 140 + +uniform vec2 uScreenSize; + +uniform ivec2 uOSDPos; +uniform ivec2 uOSDSize; + +in vec2 vPosition; + +smooth out vec2 fTexcoord; + +void main() +{ + vec4 fpos; + + vec2 osdpos = (vPosition * vec2(uOSDSize)); + fTexcoord = osdpos; + osdpos += uOSDPos; + + fpos.xy = ((osdpos * 2.0) / uScreenSize) - 1.0; + fpos.y *= -1; + fpos.z = 0.0; + fpos.w = 1.0; + + gl_Position = fpos; +} +)"; + +const char* kScreenFS_OSD = R"(#version 140 + +uniform sampler2D OSDTex; + +smooth in vec2 fTexcoord; + +out vec4 oColor; + +void main() +{ + vec4 pixel = texelFetch(OSDTex, ivec2(fTexcoord), 0); + oColor = pixel.bgra; +} +)"; + +#endif // OSD_SHADERS_H diff --git a/src/frontend/qt_sdl/font.h b/src/frontend/qt_sdl/font.h new file mode 100644 index 0000000..f2e4f87 --- /dev/null +++ b/src/frontend/qt_sdl/font.h @@ -0,0 +1,135 @@ +/* + Copyright 2016-2020 Arisotura + + 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 FONT_H +#define FONT_H +unsigned short font[] = { + 12, 0, 0, 0,0x0C03, 0x0E07, 0x070E, 0x039C, 0x01F8, 0x00F0, 0x00F0, 0x01F8, 0x039C, 0x070E, 0x0E07, 0x0C03, + 12, 0, 0, 0,0x01C0, 0x00E0, 0x0060, 0x0860, 0x0C60, 0x0FE0, 0x07F0, 0x0038, 0x001C, 0x000E, 0x0007, 0x0003, + 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0,0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0000, 0x0003, 0x0003, 0x0000, 0x0000, + 9, 0, 0, 0,0x01EF, 0x01EF, 0x018C, 0x01CE, 0x00E7, 0x0063, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 10, 0, 0, 0,0x00CC, 0x00CC, 0x03FF, 0x03FF, 0x00CC, 0x00CC, 0x03FF, 0x03FF, 0x00CC, 0x00CC, 0x0000, 0x0000, + 8, 0, 0, 0,0x0018, 0x00FE, 0x00FF, 0x001B, 0x007F, 0x00FE, 0x00D8, 0x00FF, 0x007F, 0x0018, 0x0000, 0x0000, + 10, 0, 0, 0,0x0306, 0x038F, 0x01CF, 0x00E6, 0x0070, 0x0038, 0x019C, 0x03CE, 0x03C7, 0x0183, 0x0000, 0x0000, + 10, 0, 0, 0,0x007C, 0x00FE, 0x00C6, 0x00EE, 0x007C, 0x037E, 0x03E7, 0x01F3, 0x03BF, 0x031E, 0x0000, 0x0000, + 4, 0, 0, 0,0x000F, 0x000F, 0x000C, 0x000E, 0x0007, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 4, 0, 0, 0,0x000C, 0x000E, 0x0007, 0x0003, 0x0003, 0x0003, 0x0003, 0x0007, 0x000E, 0x000C, 0x0000, 0x0000, + 4, 0, 0, 0,0x0003, 0x0007, 0x000E, 0x000C, 0x000C, 0x000C, 0x000C, 0x000E, 0x0007, 0x0003, 0x0000, 0x0000, + 10, 0, 0, 0,0x0030, 0x0333, 0x03B7, 0x01FE, 0x00FC, 0x00FC, 0x01FE, 0x03B7, 0x0333, 0x0030, 0x0000, 0x0000, + 10, 0, 0, 0,0x0030, 0x0030, 0x0030, 0x0030, 0x03FF, 0x03FF, 0x0030, 0x0030, 0x0030, 0x0030, 0x0000, 0x0000, + 4, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x000F, 0x000F, 0x000C, 0x000E, 0x0007, 0x0003, + 10, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x03FF, 0x03FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 3, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0007, 0x0007, 0x0007, 0x0000, 0x0000, + 10, 0, 0, 0,0x0300, 0x0380, 0x01C0, 0x00E0, 0x0070, 0x0038, 0x001C, 0x000E, 0x0007, 0x0003, 0x0000, 0x0000, + 8, 0, 0, 0,0x007E, 0x00FF, 0x00C3, 0x00C3, 0x00C3, 0x00C3, 0x00C3, 0x00C3, 0x00FF, 0x007E, 0x0000, 0x0000, + 4, 0, 0, 0,0x0006, 0x0007, 0x0007, 0x0006, 0x0006, 0x0006, 0x0006, 0x0006, 0x000F, 0x000F, 0x0000, 0x0000, + 8, 0, 0, 0,0x007E, 0x00FF, 0x00C3, 0x00C0, 0x00FE, 0x007F, 0x0003, 0x0003, 0x00FF, 0x00FF, 0x0000, 0x0000, + 8, 0, 0, 0,0x007F, 0x00FF, 0x00C0, 0x00C0, 0x007C, 0x00FC, 0x00C0, 0x00C0, 0x00FF, 0x007F, 0x0000, 0x0000, + 8, 0, 0, 0,0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x00FF, 0x00FE, 0x0060, 0x0060, 0x0000, 0x0000, + 8, 0, 0, 0,0x00FF, 0x00FF, 0x0003, 0x0003, 0x007F, 0x00FF, 0x00C0, 0x00C0, 0x00FF, 0x007F, 0x0000, 0x0000, + 8, 0, 0, 0,0x007E, 0x007F, 0x0003, 0x0003, 0x007F, 0x00FF, 0x00C3, 0x00C3, 0x00FF, 0x007E, 0x0000, 0x0000, + 8, 0, 0, 0,0x00FF, 0x00FF, 0x00C0, 0x00E0, 0x0070, 0x0038, 0x001C, 0x000C, 0x000C, 0x000C, 0x0000, 0x0000, + 8, 0, 0, 0,0x007E, 0x00FF, 0x00C3, 0x00C3, 0x007E, 0x00FF, 0x00C3, 0x00C3, 0x00FF, 0x007E, 0x0000, 0x0000, + 8, 0, 0, 0,0x007E, 0x00FF, 0x00C3, 0x00C3, 0x00FF, 0x00FE, 0x00C0, 0x00C0, 0x00FE, 0x007E, 0x0000, 0x0000, + 3, 0, 0, 0,0x0000, 0x0000, 0x0007, 0x0007, 0x0000, 0x0000, 0x0000, 0x0007, 0x0007, 0x0000, 0x0000, 0x0000, + 4, 0, 0, 0,0x0000, 0x0000, 0x000E, 0x000E, 0x0000, 0x0000, 0x000C, 0x000E, 0x0007, 0x0003, 0x0000, 0x0000, + 6, 0, 0, 0,0x0030, 0x0038, 0x001C, 0x000E, 0x0007, 0x0007, 0x000E, 0x001C, 0x0038, 0x0030, 0x0000, 0x0000, + 7, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x007F, 0x007F, 0x0000, 0x0000, 0x007F, 0x007F, 0x0000, 0x0000, 0x0000, + 6, 0, 0, 0,0x0003, 0x0007, 0x000E, 0x001C, 0x0038, 0x0038, 0x001C, 0x000E, 0x0007, 0x0003, 0x0000, 0x0000, + 8, 0, 0, 0,0x007E, 0x00FF, 0x00C3, 0x00C3, 0x00F0, 0x0078, 0x0018, 0x0000, 0x0018, 0x0018, 0x0000, 0x0000, + 10, 0, 0, 0,0x00FC, 0x01FE, 0x0387, 0x0333, 0x037B, 0x03FB, 0x01F3, 0x0007, 0x03FE, 0x03FC, 0x0000, 0x0000, + 9, 0, 0, 0,0x00FE, 0x01FF, 0x0183, 0x0183, 0x0183, 0x01FF, 0x01FF, 0x0183, 0x0183, 0x0183, 0x0000, 0x0000, + 9, 0, 0, 0,0x00FF, 0x01FF, 0x0183, 0x0183, 0x00FF, 0x01FF, 0x0183, 0x0183, 0x01FF, 0x00FF, 0x0000, 0x0000, + 8, 0, 0, 0,0x00FE, 0x00FF, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x00FF, 0x00FE, 0x0000, 0x0000, + 9, 0, 0, 0,0x007F, 0x00FF, 0x01C3, 0x0183, 0x0183, 0x0183, 0x0183, 0x01C3, 0x00FF, 0x007F, 0x0000, 0x0000, + 9, 0, 0, 0,0x01FF, 0x01FF, 0x0003, 0x0003, 0x00FF, 0x00FF, 0x0003, 0x0003, 0x01FF, 0x01FF, 0x0000, 0x0000, + 9, 0, 0, 0,0x01FF, 0x01FF, 0x0003, 0x0003, 0x00FF, 0x00FF, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0000, + 9, 0, 0, 0,0x01FE, 0x01FF, 0x0003, 0x0003, 0x01F3, 0x01F3, 0x0183, 0x0183, 0x01FF, 0x00FE, 0x0000, 0x0000, + 9, 0, 0, 0,0x0183, 0x0183, 0x0183, 0x0183, 0x01FF, 0x01FF, 0x0183, 0x0183, 0x0183, 0x0183, 0x0000, 0x0000, + 6, 0, 0, 0,0x003F, 0x003F, 0x000C, 0x000C, 0x000C, 0x000C, 0x000C, 0x000C, 0x003F, 0x003F, 0x0000, 0x0000, + 9, 0, 0, 0,0x01F0, 0x01F0, 0x00C0, 0x00C0, 0x00C0, 0x00C0, 0x00C3, 0x00C3, 0x00FF, 0x007E, 0x0000, 0x0000, + 9, 0, 0, 0,0x0183, 0x01C3, 0x00E3, 0x0073, 0x003F, 0x003F, 0x0073, 0x00E3, 0x01C3, 0x0183, 0x0000, 0x0000, + 7, 0, 0, 0,0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x007F, 0x007F, 0x0000, 0x0000, + 10, 0, 0, 0,0x0303, 0x0387, 0x03CF, 0x03FF, 0x037B, 0x0333, 0x0303, 0x0303, 0x0303, 0x0303, 0x0000, 0x0000, + 10, 0, 0, 0,0x0303, 0x0307, 0x030F, 0x031F, 0x033B, 0x0373, 0x03E3, 0x03C3, 0x0383, 0x0303, 0x0000, 0x0000, + 10, 0, 0, 0,0x01FE, 0x03FF, 0x0303, 0x0303, 0x0303, 0x0303, 0x0303, 0x0303, 0x03FF, 0x01FE, 0x0000, 0x0000, + 9, 0, 0, 0,0x00FF, 0x01FF, 0x0183, 0x0183, 0x01FF, 0x00FF, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0000, + 10, 0, 0, 0,0x01FE, 0x03FF, 0x0303, 0x0303, 0x0333, 0x0373, 0x03E3, 0x01C3, 0x03FF, 0x037E, 0x0000, 0x0000, + 9, 0, 0, 0,0x00FF, 0x01FF, 0x0183, 0x0183, 0x01FF, 0x00FF, 0x0073, 0x00E3, 0x01C3, 0x0183, 0x0000, 0x0000, + 10, 0, 0, 0,0x01FE, 0x01FF, 0x0003, 0x0003, 0x01FF, 0x03FE, 0x0300, 0x0300, 0x03FE, 0x01FE, 0x0000, 0x0000, + 10, 0, 0, 0,0x03FF, 0x03FF, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0000, 0x0000, + 9, 0, 0, 0,0x0183, 0x0183, 0x0183, 0x0183, 0x0183, 0x0183, 0x0183, 0x0183, 0x01FF, 0x00FE, 0x0000, 0x0000, + 10, 0, 0, 0,0x0303, 0x0303, 0x0303, 0x0303, 0x0303, 0x0387, 0x01CE, 0x00FC, 0x0078, 0x0030, 0x0000, 0x0000, + 10, 0, 0, 0,0x0303, 0x0303, 0x0303, 0x0303, 0x0333, 0x037B, 0x03FF, 0x03CF, 0x0387, 0x0303, 0x0000, 0x0000, + 10, 0, 0, 0,0x0303, 0x0387, 0x01CE, 0x00FC, 0x0078, 0x0078, 0x00FC, 0x01CE, 0x0387, 0x0303, 0x0000, 0x0000, + 10, 0, 0, 0,0x0303, 0x0387, 0x01CE, 0x00FC, 0x0078, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0000, 0x0000, + 10, 0, 0, 0,0x03FF, 0x03FF, 0x01C0, 0x00E0, 0x0070, 0x0038, 0x001C, 0x000E, 0x03FF, 0x03FF, 0x0000, 0x0000, + 4, 0, 0, 0,0x000F, 0x000F, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x000F, 0x000F, 0x0000, 0x0000, + 10, 0, 0, 0,0x0003, 0x0007, 0x000E, 0x001C, 0x0038, 0x0070, 0x00E0, 0x01C0, 0x0380, 0x0300, 0x0000, 0x0000, + 4, 0, 0, 0,0x000F, 0x000F, 0x000C, 0x000C, 0x000C, 0x000C, 0x000C, 0x000C, 0x000F, 0x000F, 0x0000, 0x0000, + 8, 0, 0, 0,0x0018, 0x003C, 0x007E, 0x00E7, 0x00C3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 10, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x03FF, 0x03FF, + 4, 0, 0, 0,0x000F, 0x000F, 0x0003, 0x0007, 0x000E, 0x000C, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 7, 0, 0, 0,0x0000, 0x0000, 0x003E, 0x007E, 0x0060, 0x007E, 0x007F, 0x0063, 0x007F, 0x007E, 0x0000, 0x0000, + 7, 0, 0, 0,0x0003, 0x0003, 0x0003, 0x003F, 0x007F, 0x0063, 0x0063, 0x0063, 0x007F, 0x003F, 0x0000, 0x0000, + 7, 0, 0, 0,0x0000, 0x0000, 0x007E, 0x007F, 0x0003, 0x0003, 0x0003, 0x0003, 0x007F, 0x007E, 0x0000, 0x0000, + 7, 0, 0, 0,0x0060, 0x0060, 0x0060, 0x007E, 0x007F, 0x0063, 0x0063, 0x0063, 0x007F, 0x007E, 0x0000, 0x0000, + 7, 0, 0, 0,0x0000, 0x0000, 0x003E, 0x007F, 0x0063, 0x007F, 0x003F, 0x0003, 0x003F, 0x003E, 0x0000, 0x0000, + 6, 0, 0, 0,0x003C, 0x003E, 0x0006, 0x0006, 0x001F, 0x001F, 0x0006, 0x0006, 0x0006, 0x0006, 0x0000, 0x0000, + 7, 0, 0, 0,0x0000, 0x0000, 0x007E, 0x007F, 0x0063, 0x0063, 0x007F, 0x007E, 0x0060, 0x0060, 0x007E, 0x003E, + 7, 0, 0, 0,0x0003, 0x0003, 0x0003, 0x003F, 0x007F, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0000, 0x0000, + 2, 0, 0, 0,0x0003, 0x0003, 0x0000, 0x0000, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0000, + 7, 0, 0, 0,0x0060, 0x0060, 0x0000, 0x0000, 0x0060, 0x0060, 0x0060, 0x0060, 0x0060, 0x0063, 0x007F, 0x003E, + 8, 0, 0, 0,0x0003, 0x0003, 0x00E3, 0x0073, 0x003B, 0x001F, 0x001F, 0x003B, 0x0073, 0x00E3, 0x0000, 0x0000, + 4, 0, 0, 0,0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x000F, 0x000E, 0x0000, 0x0000, + 10, 0, 0, 0,0x0000, 0x0000, 0x01FF, 0x03FF, 0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x0000, 0x0000, + 7, 0, 0, 0,0x0000, 0x0000, 0x003F, 0x007F, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0000, 0x0000, + 8, 0, 0, 0,0x0000, 0x0000, 0x007E, 0x00FF, 0x00C3, 0x00C3, 0x00C3, 0x00C3, 0x00FF, 0x007E, 0x0000, 0x0000, + 7, 0, 0, 0,0x0000, 0x0000, 0x003F, 0x007F, 0x0063, 0x0063, 0x007F, 0x003F, 0x0003, 0x0003, 0x0003, 0x0003, + 7, 0, 0, 0,0x0000, 0x0000, 0x007E, 0x007F, 0x0063, 0x0063, 0x007F, 0x007E, 0x0060, 0x0060, 0x0060, 0x0060, + 7, 0, 0, 0,0x0000, 0x0000, 0x003B, 0x007F, 0x0067, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0000, + 8, 0, 0, 0,0x0000, 0x0000, 0x007E, 0x007F, 0x0003, 0x007F, 0x00FE, 0x00C0, 0x00FE, 0x007E, 0x0000, 0x0000, + 6, 0, 0, 0,0x0006, 0x0006, 0x003F, 0x003F, 0x0006, 0x0006, 0x0006, 0x0006, 0x003E, 0x003C, 0x0000, 0x0000, + 7, 0, 0, 0,0x0000, 0x0000, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x007F, 0x007E, 0x0000, 0x0000, + 10, 0, 0, 0,0x0000, 0x0000, 0x0303, 0x0303, 0x0303, 0x0387, 0x01CE, 0x00FC, 0x0078, 0x0030, 0x0000, 0x0000, + 10, 0, 0, 0,0x0000, 0x0000, 0x0303, 0x0303, 0x0333, 0x037B, 0x03FF, 0x03CF, 0x0387, 0x0303, 0x0000, 0x0000, + 8, 0, 0, 0,0x0000, 0x0000, 0x00C3, 0x00E7, 0x007E, 0x003C, 0x003C, 0x007E, 0x00E7, 0x00C3, 0x0000, 0x0000, + 10, 0, 0, 0,0x0000, 0x0000, 0x0303, 0x0307, 0x038E, 0x01DC, 0x00F8, 0x0070, 0x0038, 0x001C, 0x000E, 0x0006, + 8, 0, 0, 0,0x0000, 0x0000, 0x00FF, 0x00FF, 0x0070, 0x0038, 0x001C, 0x000E, 0x00FF, 0x00FF, 0x0000, 0x0000, + 6, 0, 0, 0,0x0038, 0x003C, 0x000C, 0x000C, 0x000F, 0x000F, 0x000C, 0x000C, 0x003C, 0x0038, 0x0000, 0x0000, + 2, 0, 0, 0,0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0000, + 6, 0, 0, 0,0x0007, 0x000F, 0x000C, 0x000C, 0x003C, 0x003C, 0x000C, 0x000C, 0x000F, 0x0007, 0x0000, 0x0000, + 10, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x031C, 0x03BE, 0x01F7, 0x00E3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 11, 0, 0, 0,0x0555, 0x0000, 0x0401, 0x0000, 0x0401, 0x0000, 0x0401, 0x0000, 0x0401, 0x0000, 0x0555, 0x0000, +}; +#endif diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index de71da9..78f64f6 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -43,6 +43,7 @@ #include "version.h" #include "FrontendUtil.h" +#include "OSD.h" #include "NDS.h" #include "GBACart.h" @@ -263,7 +264,7 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent) connect(this, SIGNAL(windowEmuReset()), mainWindow->actReset, SLOT(trigger())); connect(this, SIGNAL(screenLayoutChange()), mainWindow->panel, SLOT(onScreenLayoutChanged())); - initOpenGL(); + if (mainWindow->hasOGL) initOpenGL(); } void EmuThread::initOpenGL() @@ -709,10 +710,13 @@ ScreenPanelNative::ScreenPanelNative(QWidget* parent) : QWidget(parent) screenTrans[1].reset(); touching = false; + + OSD::Init(nullptr); } ScreenPanelNative::~ScreenPanelNative() { + OSD::DeInit(nullptr); } void ScreenPanelNative::setupScreenLayout() @@ -756,6 +760,9 @@ void ScreenPanelNative::paintEvent(QPaintEvent* event) painter.setTransform(screenTrans[1]); painter.drawImage(screenrc, screen[1]); + + OSD::Update(nullptr); + OSD::DrawNative(painter); } void ScreenPanelNative::resizeEvent(QResizeEvent* event) @@ -793,12 +800,16 @@ ScreenPanelGL::ScreenPanelGL(QWidget* parent) : QOpenGLWidget(parent) format.setVersion(3, 2); format.setProfile(QSurfaceFormat::CoreProfile); setFormat(format); + + touching = false; } ScreenPanelGL::~ScreenPanelGL() { makeCurrent(); + OSD::DeInit(this); + glDeleteTextures(1, &screenTexture); glDeleteVertexArrays(1, &screenVertexArray); @@ -881,6 +892,8 @@ void ScreenPanelGL::initializeGL() glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 192*2, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + OSD::Init(this); } void ScreenPanelGL::paintGL() @@ -935,6 +948,9 @@ void ScreenPanelGL::paintGL() glDrawArrays(GL_TRIANGLES, 2*3, 2*3); screenShader->release(); + + OSD::Update(this); + OSD::DrawGL(this, w*factor, h*factor); } void ScreenPanelGL::resizeEvent(QResizeEvent* event) diff --git a/src/frontend/qt_sdl/main_shaders.h b/src/frontend/qt_sdl/main_shaders.h index 96b9ecd..c55f79e 100644 --- a/src/frontend/qt_sdl/main_shaders.h +++ b/src/frontend/qt_sdl/main_shaders.h @@ -61,54 +61,4 @@ void main() } )"; - - -const char* kScreenVS_OSD = R"(#version 140 - -layout(std140) uniform uConfig -{ - vec2 uScreenSize; - uint u3DScale; - uint uFilterMode; -}; - -uniform ivec2 uOSDPos; -uniform ivec2 uOSDSize; - -in vec2 vPosition; - -smooth out vec2 fTexcoord; - -void main() -{ - vec4 fpos; - - vec2 osdpos = (vPosition * vec2(uOSDSize)); - fTexcoord = osdpos; - osdpos += uOSDPos; - - fpos.xy = ((osdpos * 2.0) / uScreenSize) - 1.0; - fpos.y *= -1; - fpos.z = 0.0; - fpos.w = 1.0; - - gl_Position = fpos; -} -)"; - -const char* kScreenFS_OSD = R"(#version 140 - -uniform sampler2D OSDTex; - -smooth in vec2 fTexcoord; - -out vec4 oColor; - -void main() -{ - vec4 pixel = texelFetch(OSDTex, ivec2(fTexcoord), 0); - oColor = pixel.bgra; -} -)"; - #endif // MAIN_SHADERS_H -- cgit v1.2.3 From a2004785a4d49f02f8257a38489eb57a29ce4e91 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 28 May 2020 18:22:02 +0200 Subject: re-add all old OSD messages --- src/frontend/Util_ROM.cpp | 6 ------ src/frontend/qt_sdl/main.cpp | 41 +++++++++++++++++++++++++---------------- 2 files changed, 25 insertions(+), 22 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/Util_ROM.cpp b/src/frontend/Util_ROM.cpp index ec3186a..3f64b9d 100644 --- a/src/frontend/Util_ROM.cpp +++ b/src/frontend/Util_ROM.cpp @@ -384,10 +384,6 @@ bool SaveState(const char* filename) } } - /*char msg[64]; - if (slot > 0) sprintf(msg, "State saved to slot %d", slot); - else sprintf(msg, "State saved to file"); - OSD::AddMessage(0, msg);*/ return true; } @@ -407,8 +403,6 @@ void UndoStateLoad() strncpy(SRAMPath[ROMSlot_NDS], PrevSRAMPath[ROMSlot_NDS], 1024); NDS::RelocateSave(SRAMPath[ROMSlot_NDS], false); } - - //OSD::AddMessage(0, "State load undone"); } } diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 78f64f6..bea33b0 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -364,16 +364,16 @@ void EmuThread::run() if (Input::HotkeyPressed(HK_SolarSensorDecrease)) { if (GBACart_SolarSensor::LightLevel > 0) GBACart_SolarSensor::LightLevel--; - //char msg[64]; - //sprintf(msg, "Solar sensor level set to %d", GBACart_SolarSensor::LightLevel); - //OSD::AddMessage(0, msg); + char msg[64]; + sprintf(msg, "Solar sensor level set to %d", GBACart_SolarSensor::LightLevel); + OSD::AddMessage(0, msg); } if (Input::HotkeyPressed(HK_SolarSensorIncrease)) { if (GBACart_SolarSensor::LightLevel < 10) GBACart_SolarSensor::LightLevel++; - //char msg[64]; - //sprintf(msg, "Solar sensor level set to %d", GBACart_SolarSensor::LightLevel); - //OSD::AddMessage(0, msg); + char msg[64]; + sprintf(msg, "Solar sensor level set to %d", GBACart_SolarSensor::LightLevel); + OSD::AddMessage(0, msg); } } @@ -411,7 +411,7 @@ void EmuThread::run() { bool lid = !NDS::IsLidClosed(); NDS::SetLidClosed(lid); - //OSD::AddMessage(0, lid ? "Lid closed" : "Lid opened"); + OSD::AddMessage(0, lid ? "Lid closed" : "Lid opened"); } // microphone input @@ -1493,13 +1493,16 @@ void MainWindow::onSaveState() if (Frontend::SaveState(filename)) { - // TODO: OSD message + char msg[64]; + if (slot > 0) sprintf(msg, "State saved to slot %d", slot); + else sprintf(msg, "State saved to file"); + OSD::AddMessage(0, msg); actLoadState[slot]->setEnabled(true); } else { - // TODO: OSD error message? + OSD::AddMessage(0xFFA0A0, "State save failed"); } emuThread->emuUnpause(); @@ -1534,10 +1537,10 @@ void MainWindow::onLoadState() if (!Platform::FileExists(filename)) { - /*char msg[64]; + char msg[64]; if (slot > 0) sprintf(msg, "State slot %d is empty", slot); else sprintf(msg, "State file does not exist"); - OSD::AddMessage(0xFFA0A0, msg);*/ + OSD::AddMessage(0xFFA0A0, msg); emuThread->emuUnpause(); return; @@ -1545,11 +1548,14 @@ void MainWindow::onLoadState() if (Frontend::LoadState(filename)) { - // TODO: OSD message + char msg[64]; + if (slot > 0) sprintf(msg, "State loaded from slot %d", slot); + else sprintf(msg, "State loaded from file"); + OSD::AddMessage(0, msg); } else { - // TODO: OSD error message? + OSD::AddMessage(0xFFA0A0, "State load failed"); } emuThread->emuUnpause(); @@ -1560,6 +1566,8 @@ void MainWindow::onUndoStateLoad() emuThread->emuPause(); Frontend::UndoStateLoad(); emuThread->emuUnpause(); + + OSD::AddMessage(0, "State load undone"); } void MainWindow::onQuit() @@ -1575,10 +1583,12 @@ void MainWindow::onPause(bool checked) if (checked) { emuThread->emuPause(); + OSD::AddMessage(0, "Paused"); } else { emuThread->emuUnpause(); + OSD::AddMessage(0, "Resumed"); } } @@ -1600,8 +1610,7 @@ void MainWindow::onReset() } else { - // OSD::AddMessage(0, "Reset"); - + OSD::AddMessage(0, "Reset"); emuThread->emuRun(); } } @@ -1835,7 +1844,7 @@ void emuStop() emit emuThread->windowEmuStop(); - //OSD::AddMessage(0xFFC040, "Shutdown"); + OSD::AddMessage(0xFFC040, "Shutdown"); } -- cgit v1.2.3 From 590ab2ac2bdc79e536c0f332cf9f1a4d700fffe9 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 28 May 2020 22:37:37 +0200 Subject: fix things --- src/frontend/qt_sdl/CMakeLists.txt | 4 ++++ src/frontend/qt_sdl/main.cpp | 21 +++++++++------------ 2 files changed, 13 insertions(+), 12 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 0b7fa54..dbc1e3a 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -7,7 +7,10 @@ SET(SOURCES_QT_SDL InputConfigDialog.cpp VideoSettingsDialog.cpp AudioSettingsDialog.cpp + WifiSettingsDialog.cpp Input.cpp + LAN_PCap.cpp + LAN_Socket.cpp OSD.cpp OSD_shaders.h font.h @@ -40,6 +43,7 @@ pkg_check_modules(SDL2 REQUIRED sdl2) add_executable(melonDS ${SOURCES_QT_SDL}) target_include_directories(melonDS PRIVATE ${SDL2_INCLUDE_DIRS}) +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 ${SDL2_LIBRARIES}) diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index bea33b0..e60a58d 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -38,6 +38,7 @@ #include "InputConfigDialog.h" #include "VideoSettingsDialog.h" #include "AudioSettingsDialog.h" +#include "WifiSettingsDialog.h" #include "types.h" #include "version.h" @@ -298,9 +299,10 @@ void EmuThread::initOpenGL() oglContext->moveToThread(this); } -void deinitOpenGL() +void EmuThread::deinitOpenGL() { - // TODO!! + delete oglContext; + delete oglSurface; } void* oglGetProcAddress(const char* proc) @@ -545,16 +547,11 @@ void EmuThread::run() NDS::DeInit(); //Platform::LAN_DeInit(); - /*if (Screen_UseGL) - { - OSD::DeInit(true); - GLScreen_DeInit(); - } - else - OSD::DeInit(false);*/ - if (hasOGL) + { oglContext->doneCurrent(); + deinitOpenGL(); + } } void EmuThread::changeWindowTitle(char* title) @@ -844,7 +841,6 @@ void ScreenPanelGL::initializeGL() screenShader->addShaderFromSourceCode(QOpenGLShader::Fragment, kScreenFS); GLuint pid = screenShader->programId(); - printf("program: %d\n", pid); glBindAttribLocation(pid, 0, "vPosition"); glBindAttribLocation(pid, 1, "vTexcoord"); glBindFragDataLocation(pid, 0, "oColor"); @@ -1675,7 +1671,7 @@ void MainWindow::onAudioSettingsFinished(int res) void MainWindow::onOpenWifiSettings() { - // + WifiSettingsDialog::openDlg(this); } void MainWindow::onChangeSavestateSRAMReloc(bool checked) @@ -1822,6 +1818,7 @@ void MainWindow::onUpdateVideoSettings(bool glchange) { emuThread->emuPause(); + if (hasOGL) emuThread->deinitOpenGL(); delete panel; createScreenPanel(); connect(emuThread, SIGNAL(windowUpdate()), panel, SLOT(update())); -- cgit v1.2.3 From a38b20484d72d9d03ac7c807323343c8576c2c35 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 28 May 2020 23:12:21 +0200 Subject: finish the wifi dialog also guess who the idiot is who forgot to add their files --- src/frontend/qt_sdl/Platform.cpp | 22 +++-- src/frontend/qt_sdl/WifiSettingsDialog.cpp | 140 +++++++++++++++++++++++++++++ src/frontend/qt_sdl/WifiSettingsDialog.h | 68 ++++++++++++++ src/frontend/qt_sdl/main.cpp | 19 +++- src/frontend/qt_sdl/main.h | 1 + 5 files changed, 237 insertions(+), 13 deletions(-) create mode 100644 src/frontend/qt_sdl/WifiSettingsDialog.cpp create mode 100644 src/frontend/qt_sdl/WifiSettingsDialog.h (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 6b256e0..edc8d45 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -22,8 +22,8 @@ #include #include "Platform.h" #include "PlatformConfig.h" -//#include "LAN_Socket.h" -//#include "LAN_PCap.h" +#include "LAN_Socket.h" +#include "LAN_PCap.h" #include #ifdef __WIN32__ @@ -559,7 +559,7 @@ int MP_RecvPacket(u8* data, bool block) bool LAN_Init() { - /*if (Config::DirectLAN) + if (Config::DirectLAN) { if (!LAN_PCap::Init(true)) return false; @@ -568,7 +568,7 @@ bool LAN_Init() { if (!LAN_Socket::Init()) return false; - }*/ + } return true; } @@ -580,26 +580,24 @@ void LAN_DeInit() // LAN_PCap::DeInit(); //else // LAN_Socket::DeInit(); - /*LAN_PCap::DeInit(); - LAN_Socket::DeInit();*/ + LAN_PCap::DeInit(); + LAN_Socket::DeInit(); } int LAN_SendPacket(u8* data, int len) { - /*if (Config::DirectLAN) + if (Config::DirectLAN) return LAN_PCap::SendPacket(data, len); else - return LAN_Socket::SendPacket(data, len);*/ - return 0; + return LAN_Socket::SendPacket(data, len); } int LAN_RecvPacket(u8* data) { - /*if (Config::DirectLAN) + if (Config::DirectLAN) return LAN_PCap::RecvPacket(data); else - return LAN_Socket::RecvPacket(data);*/ - return 0; + return LAN_Socket::RecvPacket(data); } diff --git a/src/frontend/qt_sdl/WifiSettingsDialog.cpp b/src/frontend/qt_sdl/WifiSettingsDialog.cpp new file mode 100644 index 0000000..457a78d --- /dev/null +++ b/src/frontend/qt_sdl/WifiSettingsDialog.cpp @@ -0,0 +1,140 @@ +/* + Copyright 2016-2020 Arisotura + + 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 +#include + +#include "types.h" +#include "Platform.h" +#include "Config.h" +#include "PlatformConfig.h" + +#include "LAN_Socket.h" +#include "LAN_PCap.h" +#include "Wifi.h" + +#include "WifiSettingsDialog.h" +#include "ui_WifiSettingsDialog.h" + + +#ifdef __WIN32__ +#define PCAP_NAME "winpcap/npcap" +#else +#define PCAP_NAME "libpcap" +#endif + + +WifiSettingsDialog* WifiSettingsDialog::currentDlg = nullptr; + + +WifiSettingsDialog::WifiSettingsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::WifiSettingsDialog) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + + LAN_Socket::Init(); + haspcap = LAN_PCap::Init(false); + + ui->cbDirectMode->setText("Direct mode (requires " PCAP_NAME " and ethernet connection)"); + + ui->cbBindAnyAddr->setChecked(Config::SocketBindAnyAddr != 0); + + int sel = 0; + for (int i = 0; i < LAN_PCap::NumAdapters; i++) + { + LAN_PCap::AdapterData* adapter = &LAN_PCap::Adapters[i]; + + ui->cbxDirectAdapter->addItem(QString(adapter->FriendlyName)); + + if (!strncmp(adapter->DeviceName, Config::LANDevice, 128)) + sel = i; + } + ui->cbxDirectAdapter->setCurrentIndex(sel); + + ui->cbDirectMode->setChecked(Config::DirectLAN != 0); + if (!haspcap) ui->cbDirectMode->setEnabled(false); + + updateAdapterControls(); +} + +WifiSettingsDialog::~WifiSettingsDialog() +{ + delete ui; +} + +void WifiSettingsDialog::on_WifiSettingsDialog_accepted() +{ + Config::SocketBindAnyAddr = ui->cbBindAnyAddr->isChecked() ? 1:0; + Config::DirectLAN = ui->cbDirectMode->isChecked() ? 1:0; + + int sel = ui->cbxDirectAdapter->currentIndex(); + if (sel < 0 || sel >= LAN_PCap::NumAdapters) sel = 0; + if (LAN_PCap::NumAdapters < 1) + { + Config::LANDevice[0] = '\0'; + } + else + { + strncpy(Config::LANDevice, LAN_PCap::Adapters[sel].DeviceName, 127); + Config::LANDevice[127] = '\0'; + } + + Config::Save(); + + closeDlg(); +} + +void WifiSettingsDialog::on_WifiSettingsDialog_rejected() +{ + closeDlg(); +} + +void WifiSettingsDialog::on_cbDirectMode_stateChanged(int state) +{ + updateAdapterControls(); +} + +void WifiSettingsDialog::on_cbxDirectAdapter_currentIndexChanged(int sel) +{ + if (!haspcap) return; + + if (sel < 0 || sel >= LAN_PCap::NumAdapters) return; + if (LAN_PCap::NumAdapters < 1) return; + + LAN_PCap::AdapterData* adapter = &LAN_PCap::Adapters[sel]; + char tmp[64]; + + sprintf(tmp, "MAC: %02X:%02X:%02X:%02X:%02X:%02X", + adapter->MAC[0], adapter->MAC[1], adapter->MAC[2], + adapter->MAC[3], adapter->MAC[4], adapter->MAC[5]); + ui->lblAdapterMAC->setText(QString(tmp)); + + sprintf(tmp, "IP: %d.%d.%d.%d", + adapter->IP_v4[0], adapter->IP_v4[1], + adapter->IP_v4[2], adapter->IP_v4[3]); + ui->lblAdapterIP->setText(QString(tmp)); +} + +void WifiSettingsDialog::updateAdapterControls() +{ + bool enable = haspcap && ui->cbDirectMode->isChecked(); + + ui->cbxDirectAdapter->setEnabled(enable); + ui->lblAdapterMAC->setEnabled(enable); + ui->lblAdapterIP->setEnabled(enable); +} diff --git a/src/frontend/qt_sdl/WifiSettingsDialog.h b/src/frontend/qt_sdl/WifiSettingsDialog.h new file mode 100644 index 0000000..f8aad1b --- /dev/null +++ b/src/frontend/qt_sdl/WifiSettingsDialog.h @@ -0,0 +1,68 @@ +/* + Copyright 2016-2020 Arisotura + + 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 WIFISETTINGSDIALOG_H +#define WIFISETTINGSDIALOG_H + +#include + +namespace Ui { class WifiSettingsDialog; } +class WifiSettingsDialog; + +class WifiSettingsDialog : public QDialog +{ + Q_OBJECT + +public: + explicit WifiSettingsDialog(QWidget* parent); + ~WifiSettingsDialog(); + + static WifiSettingsDialog* currentDlg; + static WifiSettingsDialog* openDlg(QWidget* parent) + { + if (currentDlg) + { + currentDlg->activateWindow(); + return currentDlg; + } + + currentDlg = new WifiSettingsDialog(parent); + currentDlg->show(); + return currentDlg; + } + static void closeDlg() + { + currentDlg = nullptr; + } + +private slots: + void on_WifiSettingsDialog_accepted(); + void on_WifiSettingsDialog_rejected(); + + void on_cbDirectMode_stateChanged(int state); + void on_cbxDirectAdapter_currentIndexChanged(int sel); + +private: + Ui::WifiSettingsDialog* ui; + + bool haspcap; + + void updateAdapterControls(); +}; + +#endif // WIFISETTINGSDIALOG_H diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index e60a58d..5870d8a 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -1671,7 +1671,24 @@ void MainWindow::onAudioSettingsFinished(int res) void MainWindow::onOpenWifiSettings() { - WifiSettingsDialog::openDlg(this); + WifiSettingsDialog* dlg = WifiSettingsDialog::openDlg(this); + connect(dlg, &WifiSettingsDialog::finished, this, &MainWindow::onWifiSettingsFinished); +} + +void MainWindow::onWifiSettingsFinished(int res) +{ + emuThread->emuPause(); + + if (Wifi::MPInited) + { + Platform::MP_DeInit(); + Platform::MP_Init(); + } + + Platform::LAN_DeInit(); + Platform::LAN_Init(); + + emuThread->emuUnpause(); } void MainWindow::onChangeSavestateSRAMReloc(bool checked) diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index ef51158..279aed8 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -205,6 +205,7 @@ private slots: void onOpenAudioSettings(); void onAudioSettingsFinished(int res); void onOpenWifiSettings(); + void onWifiSettingsFinished(int res); void onChangeSavestateSRAMReloc(bool checked); void onChangeScreenSize(); void onChangeScreenRotation(QAction* act); -- cgit v1.2.3 From 9557e18b7cd559679552535e4362115b6d249ab3 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 29 May 2020 21:27:40 +0200 Subject: fart around --- src/frontend/qt_sdl/main.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 5870d8a..2aaac96 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -273,6 +273,8 @@ void EmuThread::initOpenGL() QOpenGLContext* windowctx = mainWindow->getOGLContext(); QSurfaceFormat format = windowctx->format(); + format.setSwapInterval(0); + oglSurface = new QOffscreenSurface(); oglSurface->setFormat(format); oglSurface->create(); @@ -791,14 +793,9 @@ void ScreenPanelNative::onScreenLayoutChanged() ScreenPanelGL::ScreenPanelGL(QWidget* parent) : QOpenGLWidget(parent) { - QSurfaceFormat format; - format.setDepthBufferSize(24); - format.setStencilBufferSize(8); - format.setVersion(3, 2); - format.setProfile(QSurfaceFormat::CoreProfile); - setFormat(format); - touching = false; + + curVSync = -1; } ScreenPanelGL::~ScreenPanelGL() -- cgit v1.2.3 From 8ddd82ca2c7c8844a1d3c2cc7418d03976c9c52e Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 29 May 2020 21:35:06 +0200 Subject: I'm a derp --- src/frontend/qt_sdl/main.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'src/frontend/qt_sdl/main.cpp') diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 2aaac96..68a32c8 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -795,7 +795,6 @@ ScreenPanelGL::ScreenPanelGL(QWidget* parent) : QOpenGLWidget(parent) { touching = false; - curVSync = -1; } ScreenPanelGL::~ScreenPanelGL() -- cgit v1.2.3