Merge pull request #635 from Arisotura/qt

Qt
This commit is contained in:
Arisotura 2020-05-29 21:36:26 +02:00 committed by GitHub
commit 0cadd4bd12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
332 changed files with 13290 additions and 43412 deletions

View File

@ -10,50 +10,32 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
project(melonDS)
if(NOT CMAKE_BUILD_TYPE)
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
if (CMAKE_BUILD_TYPE STREQUAL Release)
option(ENABLE_LTO "Enable link-time optimization" ON)
else()
option(ENABLE_LTO "Enable link-time optimization" OFF)
endif()
if (CMAKE_BUILD_TYPE STREQUAL Debug)
add_compile_options(-Og)
endif()
if(ENABLE_LTO)
add_compile_options(-O3 -flto)
add_link_options(-flto)
if (${CMAKE_CXX_COMPILER_ID} STREQUAL Clang)
find_program(LLD ld.lld)
if (LLD)
add_link_options(-fuse-ld=lld)
else()
add_link_options(-fuse-linker-plugin)
endif()
set(CMAKE_AR "llvm-ar")
else()
set(CMAKE_AR "gcc-ar")
endif()
set(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> qcs <TARGET> <LINK_FLAGS> <OBJECTS>")
set(CMAKE_C_ARCHIVE_FINISH true)
set(CMAKE_CXX_ARCHIVE_CREATE "<CMAKE_AR> qcs <TARGET> <LINK_FLAGS> <OBJECTS>")
set(CMAKE_CXX_ARCHIVE_FINISH true)
if (CMAKE_BUILD_TYPE STREQUAL Release)
add_compile_options(-O3)
add_link_options(-s)
endif()
add_compile_options(-fno-pic)
add_link_options(-no-pie)
option(BUILD_LIBUI "Build libui frontend" ON)
option(BUILD_QT_SDL "Build Qt/SDL frontend" ON)
if (WIN32)
option(BUILD_STATIC "Statically link dependencies" OFF)
endif()
add_subdirectory(src)
if (BUILD_LIBUI)
add_subdirectory(src/libui_sdl)
if (BUILD_QT_SDL)
add_subdirectory(src/frontend/qt_sdl)
endif()
configure_file(

6
melon.qrc Normal file
View File

@ -0,0 +1,6 @@
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource>
<file alias="melon-icon">icon/melon_32x32.png</file>
</qresource>
</RCC>

View File

@ -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,19 +13,26 @@ add_library(core STATIC
CP15.cpp
CRC32.cpp
DMA.cpp
FIFO.h
GBACart.cpp
GPU.cpp
GPU_OpenGL.cpp
GPU_OpenGL_shaders.h
GPU2D.cpp
GPU3D.cpp
GPU3D_OpenGL.cpp
GPU3D_OpenGL_shaders.h
GPU3D_Soft.cpp
NDS.cpp
NDSCart.cpp
OpenGLSupport.cpp
Platform.h
RTC.cpp
Savestate.cpp
SPI.cpp
SPU.cpp
types.h
version.h
Wifi.cpp
WifiAP.cpp
)

View File

@ -28,6 +28,10 @@ namespace Config
const char* kConfigFile = "melonDS.ini";
char BIOS9Path[1024];
char BIOS7Path[1024];
char FirmwarePath[1024];
int _3DRenderer;
int Threaded3D;
@ -36,6 +40,10 @@ int GL_Antialias;
ConfigEntry ConfigFile[] =
{
{"BIOS9Path", 1, BIOS9Path, 0, "", 1023},
{"BIOS7Path", 1, BIOS7Path, 0, "", 1023},
{"FirmwarePath", 1, FirmwarePath, 0, "", 1023},
{"3DRenderer", 0, &_3DRenderer, 1, NULL, 0},
{"Threaded3D", 0, &Threaded3D, 1, NULL, 0},
@ -82,7 +90,8 @@ void Load()
while (!feof(f))
{
fgets(linebuf, 1024, f);
int ret = sscanf(linebuf, "%32[A-Za-z_0-9]=%[^\t\n]", entryname, entryval);
int ret = sscanf(linebuf, "%31[A-Za-z_0-9]=%[^\t\n]", entryname, entryval);
entryname[31] = '\0';
if (ret < 2) continue;
ConfigEntry* entry = &ConfigFile[0];

View File

@ -19,6 +19,8 @@
#ifndef CONFIG_H
#define CONFIG_H
#include <stdio.h>
#include "types.h"
namespace Config
@ -40,6 +42,10 @@ bool HasConfigFile(const char* fileName);
void Load();
void Save();
extern char BIOS9Path[1024];
extern char BIOS7Path[1024];
extern char FirmwarePath[1024];
extern int _3DRenderer;
extern int Threaded3D;

View File

@ -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,15 +277,72 @@ 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;
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];
@ -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);
}
}
@ -985,6 +1053,8 @@ void StartScanline(u32 line)
GPU2D_A->VBlank();
GPU2D_B->VBlank();
GPU3D::VBlank();
if (Accelerated) GLCompositor::RenderFrame();
}
else if (VCount == 144)
{

View File

@ -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);
@ -422,6 +436,22 @@ void SetDispStat(u32 cpu, u16 val);
void SetVCount(u16 val);
namespace GLCompositor
{
bool Init();
void DeInit();
void Reset();
void SetRenderSettings(RenderSettings& settings);
void RenderFrame();
void BindOutputTexture();
}
}
#include "GPU3D.h"
#endif

View File

@ -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;
@ -728,6 +720,8 @@ u32 GPU2D::ColorComposite(int i, u32 val1, u32 val2)
case 3: return ColorBrightnessDown(val1, EVY);
case 4: return ColorBlend5(val1, val2);
}
return val1;
}

View File

@ -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);

View File

@ -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<CmdFIFOEntry>(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;
}
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);
}

View File

@ -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();

View File

@ -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");
@ -197,19 +199,18 @@ bool Init()
glEnable(GL_DEPTH_TEST);
glEnable(GL_STENCIL_TEST);
glDepthRange(0, 1);
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 +238,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 +262,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");
@ -392,7 +393,7 @@ void DeInit()
for (int i = 0; i < 16; i++)
{
if (!RenderShader[i][2]) continue;
OpenGL_DeleteShaderProgram(RenderShader[i]);
OpenGL::DeleteShaderProgram(RenderShader[i]);
}
}
@ -400,10 +401,10 @@ void 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;
@ -1224,7 +1225,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;
}

View File

@ -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:

206
src/GPU_OpenGL.cpp Normal file
View File

@ -0,0 +1,206 @@
/*
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 <stdio.h>
#include <string.h>
#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 SetRenderSettings(RenderSettings& settings)
{
int scale = settings.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);
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);
glFlush();
}
void BindOutputTexture()
{
glBindTexture(GL_TEXTURE_2D, CompScreenOutputTex);
}
}
}

867
src/GPU_OpenGL_shaders.h Normal file
View File

@ -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.bgr) / 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.bgr) / 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.bgr) / 255.0, 1.0);
}
)";
#endif // GPU_OPENGL_SHADERS_H

View File

@ -416,7 +416,7 @@ void Reset()
RunningGame = false;
LastSysClockCycles = 0;
f = Platform::OpenLocalFile("bios9.bin", "rb");
f = Platform::OpenLocalFile(Config::BIOS9Path, "rb");
if (!f)
{
printf("ARM9 BIOS not found\n");
@ -433,7 +433,7 @@ void Reset()
fclose(f);
}
f = Platform::OpenLocalFile("bios7.bin", "rb");
f = Platform::OpenLocalFile(Config::BIOS7Path, "rb");
if (!f)
{
printf("ARM7 BIOS not found\n");
@ -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;
}
}
@ -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)

View File

@ -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);

View File

@ -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];
@ -898,6 +903,7 @@ bool LoadROM(const char* path, const char* sram, bool direct)
{
// TODO: streaming mode? for really big ROMs or systems with limited RAM
// for now we're lazy
// also TODO: validate what we're loading!!
FILE* f = Platform::OpenFile(path, "rb");
if (!f)

View File

@ -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]);
}
}

View File

@ -21,6 +21,8 @@
#include <stdio.h>
#include <string.h>
// TODO: different includes for each platform
#include <GL/gl.h>
#include <GL/glext.h>
@ -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) \
func(GLACTIVETEXTURE, glActiveTexture); \
func(GLBLENDCOLOR, glBlendColor); \
#endif
#define DO_PROCLIST(func) \
DO_PROCLIST_1_3(func) \
func(GLACTIVETEXTURE, glActiveTexture); \
func(GLBLENDCOLOR, glBlendColor); \
\
func(GLGENFRAMEBUFFERS, glGenFramebuffers); \
func(GLDELETEFRAMEBUFFERS, glDeleteFramebuffers); \
@ -112,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); \
@ -122,14 +117,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

View File

@ -24,6 +24,9 @@
namespace Platform
{
void Init(int argc, char** argv);
void DeInit();
void StopEmu();
// fopen() wrappers

View File

@ -28,6 +28,7 @@
namespace SPI_Firmware
{
char FirmwarePath[1024];
u8* Firmware;
u32 FirmwareLength;
u32 FirmwareMask;
@ -76,6 +77,7 @@ bool VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset)
bool Init()
{
memset(FirmwarePath, 0, sizeof(FirmwarePath));
Firmware = NULL;
return true;
}
@ -90,10 +92,12 @@ void Reset()
if (Firmware) delete[] Firmware;
Firmware = NULL;
FILE* f = Platform::OpenLocalFile("firmware.bin", "rb");
strncpy(FirmwarePath, Config::FirmwarePath, 1023);
FILE* f = Platform::OpenLocalFile(FirmwarePath, "rb");
if (!f)
{
printf("firmware.bin not found\n");
printf("Firmware not found\n");
// TODO: generate default firmware
return;
@ -129,7 +133,11 @@ void Reset()
fclose(f);
// take a backup
const char* firmbkp = "firmware.bin.bak";
char firmbkp[1028];
int fplen = strlen(FirmwarePath);
strncpy(&firmbkp[0], FirmwarePath, fplen);
strncpy(&firmbkp[fplen], ".bak", 1028-fplen);
firmbkp[fplen+4] = '\0';
f = Platform::OpenLocalFile(firmbkp, "rb");
if (f) fclose(f);
else
@ -325,7 +333,7 @@ void Write(u8 val, u32 hold)
if (!hold && (CurCmd == 0x02 || CurCmd == 0x0A))
{
FILE* f = Platform::OpenLocalFile("firmware.bin", "r+b");
FILE* f = Platform::OpenLocalFile(FirmwarePath, "r+b");
if (f)
{
u32 cutoff = 0x7FA00 & FirmwareMask;

View File

@ -116,6 +116,9 @@ void Reset()
void Stop()
{
memset(OutputBuffer, 0, 2*OutputBufferSize*2);
OutputReadOffset = 0;
OutputWriteOffset = 0;
}
void DoSavestate(Savestate* file)

144
src/frontend/FrontendUtil.h Normal file
View File

@ -0,0 +1,144 @@
/*
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 FRONTENDUTIL_H
#define FRONTENDUTIL_H
#include "types.h"
namespace Frontend
{
enum
{
ROMSlot_NDS = 0,
ROMSlot_GBA,
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;
// initialize the ROM handling utility
void Init_ROM();
// load the BIOS/firmware and boot from it
int LoadBIOS();
// load a ROM file to the specified cart slot
// 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();
// 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
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();
// 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 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)
// '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
void GetTouchCoords(int& x, int& y);
// 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 specified volume
// note: this assumes the output buffer is interleaved stereo
void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen, int volume);
// 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

135
src/frontend/Util_Audio.cpp Normal file
View File

@ -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/.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "FrontendUtil.h"
#include "NDS.h"
#include "mic_blow.h"
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;
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, int volume)
{
float res_incr = inlen / (float)outlen;
float res_timer = 0;
int res_pos = 0;
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++;
}
}
}
void Mic_FeedSilence()
{
MicBufferReadPos = 0;
NDS::MicInputFrame(NULL, 0);
}
void Mic_FeedNoise()
{
int sample_len = sizeof(mic_blow) / sizeof(u16);
static int sample_pos = 0;
s16 tmp[735];
for (int i = 0; i < 735; i++)
{
tmp[i] = mic_blow[sample_pos];
sample_pos++;
if (sample_pos >= sample_len) sample_pos = 0;
}
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;
}
}

408
src/frontend/Util_ROM.cpp Normal file
View File

@ -0,0 +1,408 @@
/*
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 <stdio.h>
#include <string.h>
#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);
}
// 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);
SRAMPath[slot][1023] = '\0';
strncpy(SRAMPath[slot] + strlen(ROMPath[slot]) - 3, "sav", 3);
}
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?
// is that behavior consistent with that of LoadROM() below?
ROMPath[ROMSlot_NDS][0] = '\0';
SRAMPath[ROMSlot_NDS][0] = '\0';
NDS::LoadBIOS();
SavestateLoaded = false;
return Load_OK;
}
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);
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], 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 Load_OK;
}
else if (slot == ROMSlot_GBA && NDS::LoadGBAROM(ROMPath[slot], SRAMPath[slot]))
{
SavestateLoaded = false; // checkme??
strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety
return Load_OK;
}
else
{
strncpy(ROMPath[slot], oldpath, 1024);
strncpy(SRAMPath[slot], oldsram, 1024);
return Load_ROMLoadError;
}
}
void UnloadROM(int slot)
{
if (slot == ROMSlot_NDS)
{
// TODO!
}
else if (slot == ROMSlot_GBA)
{
GBACart::Eject();
}
ROMPath[slot][0] = '\0';
}
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.
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);
}
}
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);
}
}
}

334
src/frontend/Util_Video.cpp Normal file
View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cmath>
#include <algorithm>
#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 / 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 / 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);
}
}
void GetScreenTransforms(float* top, float* bot)
{
memcpy(top, TopScreenMtx, 6*sizeof(float));
memcpy(bot, BotScreenMtx, 6*sizeof(float));
}
void GetTouchCoords(int& x, int& y)
{
float vx = x;
float vy = y;
M23_Transform(TouchMtx, vx, vy);
x = (int)vx;
y = (int)vy;
}
}

5539
src/frontend/mic_blow.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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 <stdio.h>
#include <QFileDialog>
#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);
}

View File

@ -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 <QDialog>
#include <QButtonGroup>
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

View File

@ -0,0 +1,174 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AudioSettingsDialog</class>
<widget class="QDialog" name="AudioSettingsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>482</width>
<height>230</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Audio settings - melonDS</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Audio output</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Volume:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSlider" name="slVolume">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Controls the volume of the audio output.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="maximum">
<number>256</number>
</property>
<property name="pageStep">
<number>16</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Microphone input</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="1">
<widget class="QLineEdit" name="txtMicWavPath">
<property name="minimumSize">
<size>
<width>290</width>
<height>0</height>
</size>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Forward a WAV file to the emulated microphone.&lt;/p&gt;&lt;p&gt;This input mode is activated by holding the microphone hotkey (see Input and Hotkeys).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QRadioButton" name="rbMicWav">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Forward a WAV file to the emulated microphone.&lt;/p&gt;&lt;p&gt;This input mode is activated by holding the microphone hotkey (see Input and Hotkeys).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>WAV file:</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QPushButton" name="btnMicWavBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="3">
<widget class="QRadioButton" name="rbMicExternal">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Input from an external microphone, if available, will be forwarded to the emulated microphone.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>External microphone</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="3">
<widget class="QRadioButton" name="rbMicNoise">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Noise will be forwarded to the emulated microphone, simulating blowing into the microphone.&lt;/p&gt;&lt;p&gt;This input mode is activated by holding the microphone hotkey (see Input and Hotkeys).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Blow noise</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="3">
<widget class="QRadioButton" name="rbMicNone">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;No microphone input.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>None</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>AudioSettingsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>AudioSettingsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,114 @@
project(qt_sdl)
SET(SOURCES_QT_SDL
main.cpp
main_shaders.h
EmuSettingsDialog.cpp
InputConfigDialog.cpp
VideoSettingsDialog.cpp
AudioSettingsDialog.cpp
WifiSettingsDialog.cpp
Input.cpp
LAN_PCap.cpp
LAN_Socket.cpp
OSD.cpp
OSD_shaders.h
font.h
Platform.cpp
PlatformConfig.cpp
../Util_ROM.cpp
../Util_Video.cpp
../Util_Audio.cpp
../FrontendUtil.h
../mic_blow.h
../../../melon.qrc
)
if (WIN32)
set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> -i <SOURCE> -o <OBJECT>")
endif()
if (BUILD_STATIC AND QT5_STATIC_DIR)
set(QT5_STATIC_BASE ${QT5_STATIC_DIR}/lib/cmake/Qt5)
set(Qt5_DIR ${QT5_STATIC_BASE})
set(Qt5Core_DIR ${QT5_STATIC_BASE}Core)
set(Qt5Gui_DIR ${QT5_STATIC_BASE}Gui)
set(Qt5Widgets_DIR ${QT5_STATIC_BASE}Widgets)
endif()
find_package(Qt5 COMPONENTS Core Gui Widgets REQUIRED)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2)
if (WIN32 AND (CMAKE_BUILD_TYPE STREQUAL Release))
add_executable(melonDS WIN32 ${SOURCES_QT_SDL})
else()
add_executable(melonDS ${SOURCES_QT_SDL})
endif()
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)
if (BUILD_STATIC)
target_link_libraries(melonDS -static ${SDL2_LIBRARIES})
else()
target_link_libraries(melonDS ${SDL2_LIBRARIES})
endif()
if (UNIX)
option(UNIX_PORTABLE "Make a portable build that looks for its configuration in the current directory" OFF)
if (UNIX_PORTABLE)
add_definitions(-DUNIX_PORTABLE)
endif()
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
target_include_directories(melonDS PRIVATE ${GTK3_INCLUDE_DIRS})
target_link_libraries(melonDS ${GTK3_LIBRARIES})
ADD_DEFINITIONS(${GTK3_CFLAGS_OTHER})
add_custom_command(OUTPUT melon_grc.c
COMMAND glib-compile-resources --sourcedir=${CMAKE_SOURCE_DIR}
--target=${CMAKE_CURRENT_BINARY_DIR}/melon_grc.c
--generate-source "${CMAKE_SOURCE_DIR}/melon_grc.xml"
COMMAND glib-compile-resources --sourcedir=${CMAKE_SOURCE_DIR}
--target=${CMAKE_CURRENT_BINARY_DIR}/melon_grc.h
--generate-header "${CMAKE_SOURCE_DIR}/melon_grc.xml")
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
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_link_libraries(melonDS comctl32 d2d1 dwrite uxtheme ws2_32 iphlpapi gdi32)
if (BUILD_STATIC)
target_link_libraries(melonDS imm32 winmm version setupapi -static Qt5::Core Qt5::Gui Qt5::Widgets z zstd)
else()
target_link_libraries(melonDS Qt5::Core Qt5::Gui Qt5::Widgets)
endif()
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(TARGETS melonDS RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)

View File

@ -0,0 +1,147 @@
/*
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 <stdio.h>
#include <QFileDialog>
#include <QMessageBox>
#include "types.h"
#include "Platform.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::verifyFirmware()
{
// 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.
char filename[1024];
strncpy(filename, ui->txtFirmwarePath->text().toStdString().c_str(), 1023); filename[1023] = '\0';
FILE* f = Platform::OpenLocalFile(filename, "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))
{
QMessageBox::warning((QWidget*)this->parent(),
"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.");
}
}
void EmuSettingsDialog::on_EmuSettingsDialog_accepted()
{
verifyFirmware();
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;
Config::Save();
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;
ui->txtFirmwarePath->setText(file);
}

View File

@ -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 EMUSETTINGSDIALOG_H
#define EMUSETTINGSDIALOG_H
#include <QDialog>
namespace Ui { class EmuSettingsDialog; }
class EmuSettingsDialog;
class EmuSettingsDialog : public QDialog
{
Q_OBJECT
public:
explicit EmuSettingsDialog(QWidget* parent);
~EmuSettingsDialog();
static EmuSettingsDialog* currentDlg;
static EmuSettingsDialog* openDlg(QWidget* parent)
{
if (currentDlg)
{
currentDlg->activateWindow();
return currentDlg;
}
currentDlg = new EmuSettingsDialog(parent);
currentDlg->show();
return currentDlg;
}
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:
void verifyFirmware();
Ui::EmuSettingsDialog* ui;
};
#endif // EMUSETTINGSDIALOG_H

View File

@ -0,0 +1,188 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EmuSettingsDialog</class>
<widget class="QDialog" name="EmuSettingsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>490</width>
<height>217</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Emu settings - melonDS</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>DS mode</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="1">
<widget class="QLineEdit" name="txtBIOS9Path">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>290</width>
<height>0</height>
</size>
</property>
<property name="statusTip">
<string/>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;DS-mode ARM9 BIOS&lt;/p&gt;&lt;p&gt;Size should be 4 KB&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>DS firmware:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>DS ARM7 BIOS:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>DS ARM9 BIOS:</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="btnBIOS9Browse">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Browse...</string>
</property>
<property name="autoDefault">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="txtBIOS7Path">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;DS-mode ARM7 BIOS&lt;/p&gt;&lt;p&gt;Size should be 16 KB&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="btnBIOS7Browse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="txtFirmwarePath">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;DS-mode firmware&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Possible firmwares:&lt;/p&gt;&lt;p&gt;* 128 KB: DS-mode firmware from a DSi or 3DS. Not bootable.&lt;/p&gt;&lt;p&gt;* 256 KB: regular DS firmware.&lt;/p&gt;&lt;p&gt;* 512 KB: iQue DS firmware.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="btnFirmwareBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Startup</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QCheckBox" name="chkDirectBoot">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When loading a ROM, completely skip the regular boot process (&amp;quot;Nintendo DS&amp;quot; screen) to boot the ROM directly.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Note: if your firmware dump isn't bootable, the ROM will be booted directly regardless of this setting.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Boot game directly</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>EmuSettingsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>EmuSettingsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,244 @@
/*
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 <QKeyEvent>
#include <SDL2/SDL.h>
#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;
u32 InputMask;
void Init()
{
KeyInputMask = 0xFFF;
JoyInputMask = 0xFFF;
InputMask = 0xFFF;
KeyHotkeyMask = 0;
JoyHotkeyMask = 0;
HotkeyMask = 0;
LastHotkeyMask = 0;
}
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;
}
}
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<<i);
for (int i = 0; i < HK_MAX; i++)
if (keyHK == Config::HKKeyMapping[i])
KeyHotkeyMask |= (1<<i);
}
void KeyRelease(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<<i);
for (int i = 0; i < HK_MAX; i++)
if (keyHK == Config::HKKeyMapping[i])
KeyHotkeyMask &= ~(1<<i);
}
bool JoystickButtonDown(int val)
{
if (val == -1) return false;
bool hasbtn = ((val & 0xFFFF) != 0xFFFF);
if (hasbtn)
{
if (val & 0x100)
{
int hatnum = (val >> 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();
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<<i);
InputMask = KeyInputMask & JoyInputMask;
JoyHotkeyMask = 0;
for (int i = 0; i < HK_MAX; i++)
if (JoystickButtonDown(Config::HKJoyMapping[i]))
JoyHotkeyMask |= (1<<i);
HotkeyMask = KeyHotkeyMask | JoyHotkeyMask;
HotkeyPress = HotkeyMask & ~LastHotkeyMask;
HotkeyRelease = LastHotkeyMask & ~HotkeyMask;
LastHotkeyMask = HotkeyMask;
}
bool HotkeyDown(int id) { return HotkeyMask & (1<<id); }
bool HotkeyPressed(int id) { return HotkeyPress & (1<<id); }
bool HotkeyReleased(int id) { return HotkeyRelease & (1<<id); }
// 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__
bool IsRightModKey(QKeyEvent* event)
{
quint32 scan = event->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
}

View File

@ -16,19 +16,36 @@
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef MELONCAP_H
#define MELONCAP_H
#ifndef INPUT_H
#define INPUT_H
#include "types.h"
namespace MelonCap
namespace Input
{
void Init();
void DeInit();
extern int JoystickID;
extern SDL_Joystick* Joystick;
void Update();
extern u32 InputMask;
void Init();
// set joystickID before calling openJoystick()
void OpenJoystick();
void CloseJoystick();
void KeyPress(QKeyEvent* event);
void KeyRelease(QKeyEvent* event);
void Process();
bool HotkeyDown(int id);
bool HotkeyPressed(int id);
bool HotkeyReleased(int id);
bool IsRightModKey(QKeyEvent* event);
}
#endif // MELONCAP_H
#endif // INPUT_H

View File

@ -0,0 +1,494 @@
/*
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 <QGroupBox>
#include <QLabel>
#include <QKeyEvent>
#include <SDL2/SDL.h>
#include "types.h"
#include "Config.h"
#include "PlatformConfig.h"
#include "Input.h"
#include "InputConfigDialog.h"
#include "ui_InputConfigDialog.h"
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);
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()
{
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(&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])+":");
JoyMapButton* btn = new JoyMapButton(&joymap[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);
page->setLayout(main_layout);
}
void InputConfigDialog::on_InputConfigDialog_accepted()
{
for (int i = 0; i < 12; i++)
{
Config::KeyMapping[dskeyorder[i]] = keypadKeyMap[i];
Config::JoyMapping[dskeyorder[i]] = keypadJoyMap[i];
}
for (int i = 0; i < 2; i++)
{
Config::HKKeyMapping[hk_addons[i]] = addonsKeyMap[i];
Config::HKJoyMapping[hk_addons[i]] = addonsJoyMap[i];
}
for (int i = 0; i < 6; i++)
{
Config::HKKeyMapping[hk_general[i]] = hkGeneralKeyMap[i];
Config::HKJoyMapping[hk_general[i]] = hkGeneralJoyMap[i];
}
Config::JoystickID = Input::JoystickID;
Config::Save();
closeDlg();
}
void InputConfigDialog::on_InputConfigDialog_rejected()
{
Input::JoystickID = Config::JoystickID;
Input::OpenJoystick();
closeDlg();
}
void InputConfigDialog::on_cbxJoystick_currentIndexChanged(int id)
{
// prevent a spurious change
if (ui->cbxJoystick->count() < 2) return;
Input::JoystickID = id;
Input::OpenJoystick();
}
KeyMapButton::KeyMapButton(int* mapping, bool hotkey) : QPushButton()
{
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\n", event->key(), event->modifiers(), event->nativeVirtualKey(), event->nativeModifiers(), event->nativeScanCode());
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 (!mod)
{
if (key == Qt::Key_Escape) { click(); return; }
if (key == Qt::Key_Backspace) { *mapping = -1; click(); return; }
}
if (isHotkey)
{
if (ismod)
return;
}
if (!ismod)
key |= mod;
else if (Input::IsRightModKey(event))
key |= (1<<31);
*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;
if (key == -1) return "None";
QString isright = (key & (1<<31)) ? "Right " : "Left ";
key &= ~(1<<31);
switch (key)
{
case Qt::Key_Control: return isright + "Ctrl";
case Qt::Key_Alt: return "Alt";
case Qt::Key_AltGr: return "AltGr";
case Qt::Key_Shift: return isright + "Shift";
case Qt::Key_Meta: return "Meta";
}
QKeySequence seq(key);
QString ret = seq.toString();
// weak attempt at detecting garbage key names
if (ret.length() == 2 && ret[0].unicode() > 0xFF)
return QString("[%1]").arg(key, 8, 16);
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;
}

View File

@ -0,0 +1,123 @@
/*
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 <QDialog>
#include <QPushButton>
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();
void on_cbxJoystick_currentIndexChanged(int id);
private:
void populatePage(QWidget* page, int num, const char** labels, int* keymap, int* joymap);
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(int* mapping, bool hotkey);
~KeyMapButton();
protected:
void keyPressEvent(QKeyEvent* event) override;
void focusOutEvent(QFocusEvent* event) override;
bool focusNextPrevChild(bool next) override { return false; }
private slots:
void onClick();
private:
QString mappingText();
int* mapping;
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;
bool focusNextPrevChild(bool next) override { return false; }
private slots:
void onClick();
private:
QString mappingText();
int* mapping;
bool isHotkey;
int timerID;
int axesRest[16];
};
#endif // INPUTCONFIGDIALOG_H

View File

@ -0,0 +1,131 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>InputConfigDialog</class>
<widget class="QDialog" name="InputConfigDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>488</width>
<height>365</height>
</rect>
</property>
<property name="windowTitle">
<string>Input and hotkeys - melonDS</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tabInput">
<attribute name="title">
<string>DS keypad</string>
</attribute>
</widget>
<widget class="QWidget" name="tabAddons">
<attribute name="title">
<string>Add-ons</string>
</attribute>
</widget>
<widget class="QWidget" name="tabHotkeysGeneral">
<attribute name="title">
<string>General hotkeys</string>
</attribute>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Joystick:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cbxJoystick">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Selects which joystick will be used for joystick input, if any is present.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>InputConfigDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>InputConfigDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -22,15 +22,16 @@
#include <SDL2/SDL.h>
#include "../types.h"
#include "libui/ui.h"
#include "../OpenGLSupport.h"
#include "main.h"
#include <QPainter>
#include "OSD.h"
#include "OSD_shaders.h"
#include "font.h"
#include "PlatformConfig.h"
extern int WindowWidth, WindowHeight;
extern MainWindow* mainWindow;
namespace OSD
{
@ -46,8 +47,8 @@ struct Item
u32 Width, Height;
u32* Bitmap;
bool DrawBitmapLoaded;
uiDrawBitmap* DrawBitmap;
bool NativeBitmapLoaded;
QImage NativeBitmap;
bool GLTextureLoaded;
GLuint GLTexture;
@ -56,20 +57,35 @@ struct Item
std::deque<Item> ItemQueue;
GLint uOSDPos, uOSDSize;
QOpenGLShaderProgram* Shader;
GLint uScreenSize, uOSDPos, uOSDSize;
GLuint OSDVertexArray;
GLuint OSDVertexBuffer;
volatile bool Rendering;
bool Init(bool opengl)
bool Init(QOpenGLFunctions_3_2_Core* f)
{
if (opengl)
if (f)
{
GLuint prog; glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&prog);
uOSDPos = glGetUniformLocation(prog, "uOSDPos");
uOSDSize = glGetUniformLocation(prog, "uOSDSize");
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] =
{
@ -81,31 +97,32 @@ bool Init(bool opengl)
1, 1
};
glGenBuffers(1, &OSDVertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, OSDVertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
f->glGenBuffers(1, &OSDVertexBuffer);
f->glBindBuffer(GL_ARRAY_BUFFER, OSDVertexBuffer);
f->glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glGenVertexArrays(1, &OSDVertexArray);
glBindVertexArray(OSDVertexArray);
glEnableVertexAttribArray(0); // position
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)(0));
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(bool opengl)
void DeInit(QOpenGLFunctions_3_2_Core* f)
{
for (auto it = ItemQueue.begin(); it != ItemQueue.end(); )
{
Item& item = *it;
if (item.DrawBitmapLoaded && item.DrawBitmap) uiDrawFreeBitmap(item.DrawBitmap);
if (item.GLTextureLoaded && opengl) glDeleteTextures(1, &item.GLTexture);
if (item.GLTextureLoaded && f) f->glDeleteTextures(1, &item.GLTexture);
if (item.Bitmap) delete[] item.Bitmap;
it = ItemQueue.erase(it);
}
if (f) delete Shader;
}
@ -127,7 +144,7 @@ void LayoutText(const char* text, u32* width, u32* height, int* breaks)
u32 w = 0;
u32 h = 14;
u32 totalw = 0;
u32 maxw = WindowWidth - (kOSDMargin*2);
u32 maxw = mainWindow->panel->width() - (kOSDMargin*2);
int lastbreak = -1;
int numbrk = 0;
u16* ptr;
@ -217,7 +234,7 @@ void RenderText(u32 color, const char* text, Item* item)
memset(item->Bitmap, 0, w*h*sizeof(u32));
u32 x = 0, y = 1;
u32 maxw = WindowWidth - (kOSDMargin*2);
u32 maxw = mainWindow->panel->width() - (kOSDMargin*2);
int curline = 0;
u16* ptr;
@ -317,38 +334,28 @@ void AddMessage(u32 color, const char* text)
item.Timestamp = SDL_GetTicks();
strncpy(item.Text, text, 255); item.Text[255] = '\0';
item.Color = color;
item.Bitmap = NULL;
item.Bitmap = nullptr;
item.DrawBitmapLoaded = false;
item.NativeBitmapLoaded = false;
item.GLTextureLoaded = false;
ItemQueue.push_back(item);
}
void WindowResized(bool opengl)
{
/*for (auto it = ItemQueue.begin(); it != ItemQueue.end(); )
{
Item& item = *it;
if (item->DrawBitmapLoaded && item->DrawBitmap) uiDrawFreeBitmap(item->DrawBitmap);
//if (item->GLTextureLoaded && opengl) glDeleteTextures(1, &item->GLTexture);
item->DrawBitmapLoaded = false;
item->GLTextureLoaded = false;
if (item->Bitmap) delete[] item->Bitmap;
it++;
}*/
}
void Update(bool opengl, uiAreaDrawParams* params)
void Update(QOpenGLFunctions_3_2_Core* f)
{
if (!Config::ShowOSD)
{
Rendering = true;
if (ItemQueue.size() > 0) DeInit(opengl);
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;
}
@ -357,18 +364,6 @@ void Update(bool opengl, uiAreaDrawParams* params)
Uint32 tick_now = SDL_GetTicks();
Uint32 tick_min = tick_now - 2500;
u32 y = kOSDMargin;
if (opengl)
{
glBindBuffer(GL_ARRAY_BUFFER, OSDVertexBuffer);
glBindVertexArray(OSDVertexArray);
glActiveTexture(GL_TEXTURE0);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}
for (auto it = ItemQueue.begin(); it != ItemQueue.end(); )
{
@ -376,8 +371,7 @@ void Update(bool opengl, uiAreaDrawParams* params)
if (item.Timestamp < tick_min)
{
if (item.DrawBitmapLoaded && item.DrawBitmap) uiDrawFreeBitmap(item.DrawBitmap);
if (item.GLTextureLoaded && opengl) glDeleteTextures(1, &item.GLTexture);
if (item.GLTextureLoaded) f->glDeleteTextures(1, &item.GLTexture);
if (item.Bitmap) delete[] item.Bitmap;
it = ItemQueue.erase(it);
@ -389,41 +383,33 @@ void Update(bool opengl, uiAreaDrawParams* params)
RenderText(item.Color, item.Text, &item);
}
if (opengl)
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)
{
if (!item.GLTextureLoaded)
{
glGenTextures(1, &item.GLTexture);
glBindTexture(GL_TEXTURE_2D, item.GLTexture);
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, item.Width, item.Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, item.Bitmap);
item.GLTextureLoaded = true;
}
glBindTexture(GL_TEXTURE_2D, item.GLTexture);
glUniform2i(uOSDPos, kOSDMargin, y);
glUniform2i(uOSDSize, item.Width, item.Height);
glDrawArrays(GL_TRIANGLES, 0, 2*3);
item.NativeBitmap = QImage((const uchar*)item.Bitmap, item.Width, item.Height, QImage::Format_ARGB32_Premultiplied);
item.NativeBitmapLoaded = true;
}
else
{
if (!item.DrawBitmapLoaded)
{
item.DrawBitmap = uiDrawNewBitmap(params->Context, item.Width, item.Height, 1);
uiDrawBitmapUpdate(item.DrawBitmap, item.Bitmap);
item.DrawBitmapLoaded = true;
}
uiRect rc_src = {0, 0, (int) item.Width, (int) item.Height};
uiRect rc_dst = {kOSDMargin, (int) y, (int) item.Width, (int) item.Height};
uiDrawBitmapDraw(params->Context, item.DrawBitmap, &rc_src, &rc_dst, 0);
}
painter.drawImage(kOSDMargin, y, item.NativeBitmap);
y += item.Height;
it++;
@ -432,4 +418,57 @@ void Update(bool opengl, uiAreaDrawParams* params)
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;
}
}

View File

@ -22,13 +22,14 @@
namespace OSD
{
bool Init(bool opengl);
void DeInit(bool opengl);
bool Init(QOpenGLFunctions_3_2_Core* f);
void DeInit(QOpenGLFunctions_3_2_Core* f);
void AddMessage(u32 color, const char* text);
void WindowResized(bool opengl);
void Update(bool opengl, uiAreaDrawParams* params);
void Update(QOpenGLFunctions_3_2_Core* f);
void DrawNative(QPainter& painter);
void DrawGL(QOpenGLFunctions_3_2_Core* f, float w, float h);
}

View File

@ -16,16 +16,50 @@
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef DLGINPUTCONFIG_H
#define DLGINPUTCONFIG_H
#ifndef OSD_SHADERS_H
#define OSD_SHADERS_H
namespace DlgInputConfig
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;
void Open(int type);
void Close(int type);
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;
}
)";
#endif // DLGINPUTCONFIG_H
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

View File

@ -20,11 +20,10 @@
#include <stdlib.h>
#include <string.h>
#include <SDL2/SDL.h>
#include "../Platform.h"
#include "Platform.h"
#include "PlatformConfig.h"
#include "LAN_Socket.h"
#include "LAN_PCap.h"
#include "libui/ui.h"
#include <string>
#ifdef __WIN32__
@ -54,9 +53,10 @@
#endif
extern char* EmuDirectory;
char* EmuDirectory;
void Stop(bool internal);
void emuStop();
void* oglGetProcAddress(const char* proc);
namespace Platform
@ -85,9 +85,56 @@ 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);
emuStop();
}
@ -203,7 +250,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
@ -372,7 +419,7 @@ void Semaphore_Post(void* sema)
void* GL_GetProcAddress(const char* proc)
{
return uiGLGetProcAddress(proc);
return oglGetProcAddress(proc);
}

View File

@ -40,11 +40,12 @@ int ScreenRotation;
int ScreenGap;
int ScreenLayout;
int ScreenSizing;
int IntegerScaling;
int ScreenFilter;
int ScreenUseGL;
int ScreenVSync;
int ScreenRatio;
int ScreenVSyncInterval;
int LimitFPS;
int AudioSync;
@ -60,25 +61,25 @@ int SavestateRelocSRAM;
int AudioVolume;
int MicInputType;
char MicWavPath[512];
char MicWavPath[1024];
char LastROMFolder[512];
char LastROMFolder[1024];
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},
{"Key_A", 0, &KeyMapping[0], -1, NULL, 0},
{"Key_B", 0, &KeyMapping[1], -1, NULL, 0},
{"Key_Select", 0, &KeyMapping[2], -1, NULL, 0},
{"Key_Start", 0, &KeyMapping[3], -1, NULL, 0},
{"Key_Right", 0, &KeyMapping[4], -1, NULL, 0},
{"Key_Left", 0, &KeyMapping[5], -1, NULL, 0},
{"Key_Up", 0, &KeyMapping[6], -1, NULL, 0},
{"Key_Down", 0, &KeyMapping[7], -1, NULL, 0},
{"Key_R", 0, &KeyMapping[8], -1, NULL, 0},
{"Key_L", 0, &KeyMapping[9], -1, NULL, 0},
{"Key_X", 0, &KeyMapping[10], -1, NULL, 0},
{"Key_Y", 0, &KeyMapping[11], -1, NULL, 0},
{"Joy_A", 0, &JoyMapping[0], -1, NULL, 0},
{"Joy_B", 0, &JoyMapping[1], -1, NULL, 0},
@ -93,14 +94,14 @@ ConfigEntry PlatformConfigFile[] =
{"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},
{"HKKey_Lid", 0, &HKKeyMapping[HK_Lid], -1, NULL, 0},
{"HKKey_Mic", 0, &HKKeyMapping[HK_Mic], -1, 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], -1, NULL, 0},
{"HKKey_FastForwardToggle", 0, &HKKeyMapping[HK_FastForwardToggle], -1, NULL, 0},
{"HKKey_SolarSensorDecrease", 0, &HKKeyMapping[HK_SolarSensorDecrease], -1, NULL, 0},
{"HKKey_SolarSensorIncrease", 0, &HKKeyMapping[HK_SolarSensorIncrease], -1, NULL, 0},
{"HKJoy_Lid", 0, &HKJoyMapping[HK_Lid], -1, NULL, 0},
{"HKJoy_Mic", 0, &HKJoyMapping[HK_Mic], -1, NULL, 0},
@ -121,11 +122,12 @@ 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},
{"ScreenUseGL", 0, &ScreenUseGL, 1, NULL, 0},
{"ScreenVSync", 0, &ScreenVSync, 0, NULL, 0},
{"ScreenVSyncInterval", 0, &ScreenVSyncInterval, 1, NULL, 0},
{"LimitFPS", 0, &LimitFPS, 0, NULL, 0},
{"AudioSync", 0, &AudioSync, 1, NULL, 0},
@ -141,9 +143,9 @@ ConfigEntry PlatformConfigFile[] =
{"AudioVolume", 0, &AudioVolume, 256, NULL, 0},
{"MicInputType", 0, &MicInputType, 1, NULL, 0},
{"MicWavPath", 1, MicWavPath, 0, "", 511},
{"MicWavPath", 1, MicWavPath, 0, "", 1023},
{"LastROMFolder", 1, LastROMFolder, 0, "", 511},
{"LastROMFolder", 1, LastROMFolder, 0, "", 1023},
{"", -1, NULL, 0, NULL, 0}
};

View File

@ -19,7 +19,7 @@
#ifndef PLATFORMCONFIG_H
#define PLATFORMCONFIG_H
#include "../Config.h"
#include "Config.h"
enum
{
@ -53,11 +53,12 @@ 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 ScreenVSyncInterval;
extern int LimitFPS;
extern int AudioSync;
@ -73,9 +74,9 @@ extern int SavestateRelocSRAM;
extern int AudioVolume;
extern int MicInputType;
extern char MicWavPath[512];
extern char MicWavPath[1024];
extern char LastROMFolder[512];
extern char LastROMFolder[1024];
}

View File

@ -0,0 +1,169 @@
/*
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 <stdio.h>
#include <QFileDialog>
#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);
oldRenderer = Config::_3DRenderer;
oldGLDisplay = Config::ScreenUseGL;
oldVSync = Config::ScreenVSync;
oldVSyncInterval = Config::ScreenVSyncInterval;
oldSoftThreaded = Config::Threaded3D;
oldGLScale = Config::GL_ScaleFactor;
grp3DRenderer = new QButtonGroup(this);
grp3DRenderer->addButton(ui->rb3DSoftware, 0);
grp3DRenderer->addButton(ui->rb3DOpenGL, 1);
connect(grp3DRenderer, SIGNAL(buttonClicked(int)), this, SLOT(onChange3DRenderer(int)));
grp3DRenderer->button(Config::_3DRenderer)->setChecked(true);
ui->cbGLDisplay->setChecked(Config::ScreenUseGL != 0);
ui->cbVSync->setChecked(Config::ScreenVSync != 0);
ui->sbVSyncInterval->setValue(Config::ScreenVSyncInterval);
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));
ui->cbxGLResolution->setCurrentIndex(Config::GL_ScaleFactor-1);
if (!Config::ScreenVSync)
ui->sbVSyncInterval->setEnabled(false);
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()
{
delete ui;
}
void VideoSettingsDialog::on_VideoSettingsDialog_accepted()
{
Config::Save();
closeDlg();
}
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_cbVSync_stateChanged(int state)
{
bool vsync = (state != 0);
ui->sbVSyncInterval->setEnabled(vsync);
Config::ScreenVSync = vsync;
}
void VideoSettingsDialog::on_sbVSyncInterval_valueChanged(int val)
{
Config::ScreenVSyncInterval = val;
}
void VideoSettingsDialog::on_cbSoftwareThreaded_stateChanged(int state)
{
Config::Threaded3D = (state != 0);
emit updateVideoSettings(false);
}
void VideoSettingsDialog::on_cbxGLResolution_currentIndexChanged(int idx)
{
// prevent a spurious change
if (ui->cbxGLResolution->count() < 16) return;
Config::GL_ScaleFactor = idx+1;
emit updateVideoSettings(false);
}

View File

@ -0,0 +1,84 @@
/*
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 <QDialog>
#include <QButtonGroup>
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;
}
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_cbVSync_stateChanged(int state);
void on_sbVSyncInterval_valueChanged(int val);
void on_cbxGLResolution_currentIndexChanged(int idx);
void on_cbSoftwareThreaded_stateChanged(int state);
private:
Ui::VideoSettingsDialog* ui;
QButtonGroup* grp3DRenderer;
int oldRenderer;
int oldGLDisplay;
int oldVSync;
int oldVSyncInterval;
int oldSoftThreaded;
int oldGLScale;
};
#endif // VIDEOSETTINGSDIALOG_H

View File

@ -0,0 +1,229 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>VideoSettingsDialog</class>
<widget class="QDialog" name="VideoSettingsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>482</width>
<height>237</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Video settings - melonDS</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item row="1" column="1">
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>OpenGL renderer</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Internal resolution:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QComboBox" name="cbxGLResolution">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="1">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Software renderer</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QCheckBox" name="cbSoftwareThreaded">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Run the software renderer on a separate thread. Yields better performance on multi-core CPUs.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Use separate thread</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="2" column="1">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0" rowspan="3">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Display settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="6" column="0">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The interval at which to synchronize to the monitor's refresh rate. Set to 1 for a 60Hz monitor, 2 for 120Hz, ...&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>VSync interval:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QSpinBox" name="sbVSyncInterval">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The interval at which to synchronize to the monitor's refresh rate. Set to 1 for a 60Hz monitor, 2 for 120Hz, ...&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>20</number>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QCheckBox" name="cbGLDisplay">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use OpenGL to draw the DS screens to the main window. May result in better frame pacing. Mandatory when using the OpenGL 3D renderer.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>OpenGL display</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QCheckBox" name="cbVSync">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When using OpenGL, synchronize the video output to your monitor's refresh rate.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>VSync</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0" colspan="2">
<widget class="QRadioButton" name="rb3DOpenGL">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The OpenGL renderer may be faster than software and supports graphical enhancements, but is more prone to glitches.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>OpenGL</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QRadioButton" name="rb3DSoftware">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The software renderer is more accurate and less prone to rendering glitches, but requires more CPU power.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Software</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label">
<property name="text">
<string>3D renderer:</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>VideoSettingsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>VideoSettingsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,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 <stdio.h>
#include <QFileDialog>
#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);
}

View File

@ -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 <QDialog>
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

View File

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WifiSettingsDialog</class>
<widget class="QDialog" name="WifiSettingsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>479</width>
<height>217</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Wifi settings - melonDS</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Local</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QCheckBox" name="cbBindAnyAddr">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enabling this allows (theoretically) playing local multiplayer games over a local network. It may or may not help make for a better connection in general.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Bind socket to any address</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Online</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>MAC address:</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QCheckBox" name="cbDirectMode">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Direct mode directly routes network traffic to the host network. It is the most reliable, but requires an ethernet connection.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Non-direct mode uses a layer of emulation to get around this, but is more prone to problems.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Direct mode [TEXT PLACEHOLDER]</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="cbxDirectAdapter">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>350</width>
<height>0</height>
</size>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Selects the network adapter through which to route network traffic under direct mode.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Network adapter:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>IP address:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="lblAdapterMAC">
<property name="text">
<string>[PLACEHOLDER]</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="lblAdapterIP">
<property name="text">
<string>[PLACEHOLDER]</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>WifiSettingsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>WifiSettingsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

2100
src/frontend/qt_sdl/main.cpp Normal file

File diff suppressed because it is too large Load Diff

269
src/frontend/qt_sdl/main.h Normal file
View File

@ -0,0 +1,269 @@
/*
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_H
#define MAIN_H
#include <QThread>
#include <QWidget>
#include <QWindow>
#include <QMainWindow>
#include <QImage>
#include <QActionGroup>
#include <QOffscreenSurface>
#include <QOpenGLWidget>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QOpenGLFunctions_3_2_Core>
#include <QOpenGLShaderProgram>
class EmuThread : public QThread
{
Q_OBJECT
void run() override;
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
void emuRun();
void emuPause();
void emuUnpause();
void emuStop();
bool emuIsRunning();
signals:
void windowUpdate();
void windowTitleChange(QString title);
void windowEmuStart();
void windowEmuStop();
void windowEmuPause();
void windowEmuReset();
void windowLimitFPSChange();
void screenLayoutChange();
private:
volatile int EmuStatus;
int PrevEmuStatus;
int EmuRunning;
QOffscreenSurface* oglSurface;
QOpenGLContext* oglContext;
};
class ScreenHandler
{
Q_GADGET
public:
virtual ~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 ScreenPanelNative : public QWidget, public ScreenHandler
{
Q_OBJECT
public:
explicit ScreenPanelNative(QWidget* parent);
~ScreenPanelNative();
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();
QImage screen[2];
QTransform screenTrans[2];
};
class ScreenPanelGL : public QOpenGLWidget, public ScreenHandler, protected QOpenGLFunctions_3_2_Core
{
Q_OBJECT
public:
explicit ScreenPanelGL(QWidget* parent);
~ScreenPanelGL();
protected:
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;
void mouseMoveEvent(QMouseEvent* event) override;
private slots:
void onScreenLayoutChanged();
private:
void setupScreenLayout();
QOpenGLShaderProgram* screenShader;
GLuint screenVertexBuffer;
GLuint screenVertexArray;
GLuint screenTexture;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget* parent = nullptr);
~MainWindow();
bool hasOGL;
QOpenGLContext* getOGLContext();
protected:
void resizeEvent(QResizeEvent* event) override;
void keyPressEvent(QKeyEvent* event) override;
void keyReleaseEvent(QKeyEvent* event) override;
void dragEnterEvent(QDragEnterEvent* event) override;
void dropEvent(QDropEvent* event) override;
signals:
void screenLayoutChange();
private slots:
void onOpenFile();
void onBootFirmware();
void onSaveState();
void onLoadState();
void onUndoStateLoad();
void onQuit();
void onPause(bool checked);
void onReset();
void onStop();
void onOpenEmuSettings();
void onOpenInputConfig();
void onInputConfigFinished(int res);
void onOpenVideoSettings();
void onOpenAudioSettings();
void onAudioSettingsFinished(int res);
void onOpenWifiSettings();
void onWifiSettingsFinished(int res);
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();
void onEmuStop();
void onUpdateVideoSettings(bool glchange);
private:
void createScreenPanel();
QString loadErrorStr(int error);
public:
QWidget* panel;
QAction* actOpenROM;
QAction* actBootFirmware;
QAction* actSaveState[9];
QAction* actLoadState[9];
QAction* actUndoStateLoad;
QAction* actQuit;
QAction* actPause;
QAction* actReset;
QAction* actStop;
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

View File

@ -16,16 +16,49 @@
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef DLGWIFISETTINGS_H
#define DLGWIFISETTINGS_H
#ifndef MAIN_SHADERS_H
#define MAIN_SHADERS_H
namespace DlgWifiSettings
const char* kScreenVS = R"(#version 140
uniform vec2 uScreenSize;
uniform mat2x3 uTransform;
in vec2 vPosition;
in vec2 vTexcoord;
smooth out vec2 fTexcoord;
void main()
{
vec4 fpos;
void Open();
void Close();
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;
gl_Position = fpos;
fTexcoord = vTexcoord;
}
)";
#endif // DLGWIFISETTINGS_H
const char* kScreenFS = R"(#version 140
uniform sampler2D ScreenTex;
smooth in vec2 fTexcoord;
out vec4 oColor;
void main()
{
vec4 pixel = texture(ScreenTex, fTexcoord);
oColor = vec4(pixel.bgr, 1.0);
}
)";
#endif // MAIN_SHADERS_H

View File

@ -1,73 +0,0 @@
project(libui_sdl)
SET(SOURCES_LIBUI
main.cpp
Platform.cpp
PlatformConfig.cpp
LAN_Socket.cpp
LAN_PCap.cpp
DlgAudioSettings.cpp
DlgEmuSettings.cpp
DlgInputConfig.cpp
DlgVideoSettings.cpp
DlgWifiSettings.cpp
OSD.cpp
)
if (WIN32)
set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> -i <SOURCE> -o <OBJECT>")
endif()
option(BUILD_SHARED_LIBS "Whether to build libui as a shared library or a static library" ON)
set(BUILD_SHARED_LIBS OFF)
add_subdirectory(libui)
find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2)
add_executable(melonDS ${SOURCES_LIBUI})
target_include_directories(melonDS PRIVATE ${SDL2_INCLUDE_DIRS})
target_link_libraries(melonDS core libui ${SDL2_LIBRARIES})
if (UNIX)
option(UNIX_PORTABLE "Make a portable build that looks for its configuration in the current directory" OFF)
if (UNIX_PORTABLE)
add_definitions(-DUNIX_PORTABLE)
endif()
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
target_include_directories(melonDS PRIVATE ${GTK3_INCLUDE_DIRS})
target_link_libraries(melonDS ${GTK3_LIBRARIES})
ADD_DEFINITIONS(${GTK3_CFLAGS_OTHER})
add_custom_command(OUTPUT melon_grc.c
COMMAND glib-compile-resources --sourcedir=${CMAKE_SOURCE_DIR}
--target=${CMAKE_CURRENT_BINARY_DIR}/melon_grc.c
--generate-source "${CMAKE_SOURCE_DIR}/melon_grc.xml"
COMMAND glib-compile-resources --sourcedir=${CMAKE_SOURCE_DIR}
--target=${CMAKE_CURRENT_BINARY_DIR}/melon_grc.h
--generate-header "${CMAKE_SOURCE_DIR}/melon_grc.xml")
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_link_libraries(melonDS dl)
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)
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(TARGETS melonDS RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)

View File

@ -1,197 +0,0 @@
/*
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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "libui/ui.h"
#include "../types.h"
#include "PlatformConfig.h"
#include "DlgAudioSettings.h"
void MicLoadWav(char* path);
namespace DlgAudioSettings
{
bool opened;
uiWindow* win;
uiSlider* slVolume;
uiRadioButtons* rbMicInputType;
uiEntry* txMicWavPath;
int oldvolume;
void RevertSettings()
{
Config::AudioVolume = oldvolume;
}
int OnCloseWindow(uiWindow* window, void* blarg)
{
RevertSettings();
opened = false;
return 1;
}
void OnVolumeChanged(uiSlider* slider, void* blarg)
{
Config::AudioVolume = uiSliderValue(slVolume);
}
void OnMicWavBrowse(uiButton* btn, void* blarg)
{
char* file = uiOpenFile(win, "WAV file (*.wav)|*.wav|Any file|*.*", NULL);
if (!file)
{
return;
}
uiEntrySetText(txMicWavPath, file);
uiFreeText(file);
}
void OnCancel(uiButton* btn, void* blarg)
{
RevertSettings();
uiControlDestroy(uiControl(win));
opened = false;
}
void OnOk(uiButton* btn, void* blarg)
{
Config::AudioVolume = uiSliderValue(slVolume);
Config::MicInputType = uiRadioButtonsSelected(rbMicInputType);
char* wavpath = uiEntryText(txMicWavPath);
strncpy(Config::MicWavPath, wavpath, 511);
uiFreeText(wavpath);
Config::Save();
if (Config::MicInputType == 3) MicLoadWav(Config::MicWavPath);
uiControlDestroy(uiControl(win));
opened = false;
}
void Open()
{
if (opened)
{
uiControlSetFocus(uiControl(win));
return;
}
opened = true;
win = uiNewWindow("Audio settings - melonDS", 400, 100, 0, 0, 0);
uiWindowSetMargined(win, 1);
uiWindowOnClosing(win, OnCloseWindow, NULL);
uiBox* top = uiNewVerticalBox();
uiWindowSetChild(win, uiControl(top));
uiBoxSetPadded(top, 1);
{
uiGroup* grp = uiNewGroup("Audio output");
uiBoxAppend(top, uiControl(grp), 0);
uiGroupSetMargined(grp, 1);
uiBox* in_ctrl = uiNewVerticalBox();
uiGroupSetChild(grp, uiControl(in_ctrl));
uiLabel* label_vol = uiNewLabel("Volume:");
uiBoxAppend(in_ctrl, uiControl(label_vol), 0);
slVolume = uiNewSlider(0, 256);
uiSliderOnChanged(slVolume, OnVolumeChanged, NULL);
uiBoxAppend(in_ctrl, uiControl(slVolume), 0);
}
{
uiGroup* grp = uiNewGroup("Microphone input");
uiBoxAppend(top, uiControl(grp), 0);
uiGroupSetMargined(grp, 1);
uiBox* in_ctrl = uiNewVerticalBox();
uiGroupSetChild(grp, uiControl(in_ctrl));
rbMicInputType = uiNewRadioButtons();
uiRadioButtonsAppend(rbMicInputType, "None");
uiRadioButtonsAppend(rbMicInputType, "Microphone");
uiRadioButtonsAppend(rbMicInputType, "White noise");
uiRadioButtonsAppend(rbMicInputType, "WAV file:");
uiBoxAppend(in_ctrl, uiControl(rbMicInputType), 0);
uiBox* path_box = uiNewHorizontalBox();
uiBoxAppend(in_ctrl, uiControl(path_box), 0);
txMicWavPath = uiNewEntry();
uiBoxAppend(path_box, uiControl(txMicWavPath), 1);
uiButton* path_browse = uiNewButton("...");
uiButtonOnClicked(path_browse, OnMicWavBrowse, NULL);
uiBoxAppend(path_box, uiControl(path_browse), 0);
}
{
uiBox* in_ctrl = uiNewHorizontalBox();
uiBoxSetPadded(in_ctrl, 1);
uiBoxAppend(top, uiControl(in_ctrl), 0);
uiLabel* dummy = uiNewLabel("");
uiBoxAppend(in_ctrl, uiControl(dummy), 1);
uiButton* btncancel = uiNewButton("Cancel");
uiButtonOnClicked(btncancel, OnCancel, NULL);
uiBoxAppend(in_ctrl, uiControl(btncancel), 0);
uiButton* btnok = uiNewButton("Ok");
uiButtonOnClicked(btnok, OnOk, NULL);
uiBoxAppend(in_ctrl, uiControl(btnok), 0);
}
if (Config::AudioVolume < 0) Config::AudioVolume = 0;
else if (Config::AudioVolume > 256) Config::AudioVolume = 256;
oldvolume = Config::AudioVolume;
uiSliderSetValue(slVolume, Config::AudioVolume);
uiRadioButtonsSetSelected(rbMicInputType, Config::MicInputType);
uiEntrySetText(txMicWavPath, Config::MicWavPath);
uiControlShow(uiControl(win));
}
void Close()
{
if (!opened) return;
uiControlDestroy(uiControl(win));
opened = false;
}
}

View File

@ -1,30 +0,0 @@
/*
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 DLGAUDIOSETTINGS_H
#define DLGAUDIOSETTINGS_H
namespace DlgAudioSettings
{
void Open();
void Close();
}
#endif // DLGAUDIOSETTINGS_H

View File

@ -1,117 +0,0 @@
/*
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 <stdlib.h>
#include <stdio.h>
#include "libui/ui.h"
#include "../types.h"
#include "PlatformConfig.h"
#include "DlgEmuSettings.h"
void ApplyNewSettings(int type);
namespace DlgEmuSettings
{
bool opened;
uiWindow* win;
uiCheckbox* cbDirectBoot;
int OnCloseWindow(uiWindow* window, void* blarg)
{
opened = false;
return 1;
}
void OnCancel(uiButton* btn, void* blarg)
{
uiControlDestroy(uiControl(win));
opened = false;
}
void OnOk(uiButton* btn, void* blarg)
{
Config::DirectBoot = uiCheckboxChecked(cbDirectBoot);
Config::Save();
uiControlDestroy(uiControl(win));
opened = false;
}
void Open()
{
if (opened)
{
uiControlSetFocus(uiControl(win));
return;
}
opened = true;
win = uiNewWindow("Emu settings - melonDS", 300, 200, 0, 0, 0);
uiWindowSetMargined(win, 1);
uiWindowOnClosing(win, OnCloseWindow, NULL);
uiBox* top = uiNewVerticalBox();
uiWindowSetChild(win, uiControl(top));
{
uiBox* in_ctrl = uiNewVerticalBox();
uiBoxAppend(top, uiControl(in_ctrl), 1);
cbDirectBoot = uiNewCheckbox("Boot game directly");
uiBoxAppend(in_ctrl, uiControl(cbDirectBoot), 0);
}
{
uiBox* in_ctrl = uiNewHorizontalBox();
uiBoxSetPadded(in_ctrl, 1);
uiBoxAppend(top, uiControl(in_ctrl), 0);
uiLabel* dummy = uiNewLabel("");
uiBoxAppend(in_ctrl, uiControl(dummy), 1);
uiButton* btncancel = uiNewButton("Cancel");
uiButtonOnClicked(btncancel, OnCancel, NULL);
uiBoxAppend(in_ctrl, uiControl(btncancel), 0);
uiButton* btnok = uiNewButton("Ok");
uiButtonOnClicked(btnok, OnOk, NULL);
uiBoxAppend(in_ctrl, uiControl(btnok), 0);
}
uiCheckboxSetChecked(cbDirectBoot, Config::DirectBoot);
uiControlShow(uiControl(win));
}
void Close()
{
if (!opened) return;
uiControlDestroy(uiControl(win));
opened = false;
}
}

View File

@ -1,30 +0,0 @@
/*
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 DLGEMUSETTINGS_H
#define DLGEMUSETTINGS_H
namespace DlgEmuSettings
{
void Open();
void Close();
}
#endif // DLGEMUSETTINGS_H

View File

@ -1,663 +0,0 @@
/*
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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <SDL2/SDL.h>
#include "libui/ui.h"
#include "../types.h"
#include "PlatformConfig.h"
#include "DlgInputConfig.h"
extern int JoystickID;
extern SDL_Joystick* Joystick;
extern void OpenJoystick();
namespace DlgInputConfig
{
typedef struct
{
int type;
uiWindow* win;
uiAreaHandler areahandler;
uiArea* keypresscatcher;
int numkeys;
int keymap[32];
int joymap[32];
int pollid;
uiButton* pollbtn;
SDL_TimerID timer;
int axes_rest[16];
} InputDlgData;
int dskeyorder[12] = {0, 1, 10, 11, 5, 4, 6, 7, 9, 8, 3, 2};
char dskeylabels[12][8] = {"A:", "B:", "Select:", "Start:", "Right:", "Left:", "Up:", "Down:", "R:", "L:", "X:", "Y:"};
int identity[32] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
char hotkeylabels[HK_MAX][32] =
{
"Close/open lid:",
"Microphone:",
"Pause/resume:",
"Reset:",
"Fast forward:",
"Fast forward (toggle):",
"Decrease sunlight (Boktai):",
"Increase sunlight (Boktai):"
};
int openedmask;
InputDlgData inputdlg[2];
void KeyMappingName(int id, char* str)
{
if (id < 0)
{
strcpy(str, "None");
return;
}
int key = id & 0xFFFF;
char* keyname = uiKeyName(key);
strncpy(str, keyname, 63); str[63] = '\0';
uiFreeText(keyname);
int mod = id >> 16;
if (key == 0x11D) mod = 0;
else if (key == 0x138) mod = 0;
else if (key == 0x036) mod = 0;
if (mod != 0)
{
// CTRL / ALT / SHIFT
const int modscan[] = {0x1D, 0x38, 0x2A};
char tmp[64];
for (int m = 2; m >= 0; m--)
{
if (!(mod & (1<<m))) continue;
char* modname = uiKeyName(modscan[m]);
memcpy(tmp, str, 64);
snprintf(str, 64, "%s+%s", modname, tmp);
uiFreeText(modname);
}
}
str[63] = '\0';
}
void JoyMappingName(int id, char* str)
{
if (id < 0)
{
strcpy(str, "None");
return;
}
bool hasbtn = ((id & 0xFFFF) != 0xFFFF);
if (hasbtn)
{
if (id & 0x100)
{
int hatnum = ((id >> 4) & 0xF) + 1;
switch (id & 0xF)
{
case 0x1: sprintf(str, "Hat %d up", hatnum); break;
case 0x2: sprintf(str, "Hat %d right", hatnum); break;
case 0x4: sprintf(str, "Hat %d down", hatnum); break;
case 0x8: sprintf(str, "Hat %d left", hatnum); break;
}
}
else
{
sprintf(str, "Button %d", (id & 0xFFFF) + 1);
}
}
else
{
strcpy(str, "");
}
if (id & 0x10000)
{
int axisnum = ((id >> 24) & 0xF) + 1;
char tmp[64];
memcpy(tmp, str, 64);
switch ((id >> 20) & 0xF)
{
case 0: sprintf(str, "%s%sAxis %d +", tmp, hasbtn?" / ":"", axisnum); break;
case 1: sprintf(str, "%s%sAxis %d -", tmp, hasbtn?" / ":"", axisnum); break;
case 2: sprintf(str, "%s%sTrigger %d", tmp, hasbtn?" / ":"", axisnum); break;
}
}
}
void OnAreaDraw(uiAreaHandler* handler, uiArea* area, uiAreaDrawParams* params)
{
}
void OnAreaMouseEvent(uiAreaHandler* handler, uiArea* area, uiAreaMouseEvent* evt)
{
}
void OnAreaMouseCrossed(uiAreaHandler* handler, uiArea* area, int left)
{
}
void OnAreaDragBroken(uiAreaHandler* handler, uiArea* area)
{
}
void OnAreaResize(uiAreaHandler* handler, uiArea* area, int width, int height)
{
}
int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt)
{
InputDlgData* dlg = (InputDlgData*)uiControl(area)->UserData;
if (dlg->pollid < 0)
return 0;
if (evt->Scancode == 0x1D) // CTRL
return 1;
if (evt->Scancode == 0x38) // ALT
return 1;
if (evt->Scancode == 0x2A) // SHIFT
return 1;
if (dlg->pollid > 12)
{
if (dlg->pollid < 0x100) return 0;
int id = dlg->pollid & 0xFF;
if (id > 12) return 0;
if (evt->Scancode != 0x1 || evt->Modifiers != 0) // ESC
{
if (evt->Scancode == 0xE && evt->Modifiers == 0) // backspace
dlg->joymap[id] = -1;
else
return 1;
}
char keyname[64];
JoyMappingName(dlg->joymap[id], keyname);
uiButtonSetText(dlg->pollbtn, keyname);
uiControlEnable(uiControl(dlg->pollbtn));
dlg->pollid = -1;
uiControlSetFocus(uiControl(dlg->pollbtn));
return 1;
}
if (!evt->Up)
{
// set key.
if (evt->Scancode != 0x1 || evt->Modifiers != 0) // ESC
{
int mod = (dlg->type == 0) ? 0 : evt->Modifiers;
if (evt->Scancode == 0xE && evt->Modifiers == 0) // backspace
dlg->keymap[dlg->pollid] = -1;
else
dlg->keymap[dlg->pollid] = evt->Scancode | (mod << 16);
}
char keyname[64];
KeyMappingName(dlg->keymap[dlg->pollid], keyname);
uiButtonSetText(dlg->pollbtn, keyname);
uiControlEnable(uiControl(dlg->pollbtn));
dlg->pollid = -1;
uiControlSetFocus(uiControl(dlg->pollbtn));
}
return 1;
}
void FinishJoyMapping(void* param)
{
InputDlgData* dlg = (InputDlgData*)param;
int id = dlg->pollid & 0xFF;
char keyname[64];
JoyMappingName(dlg->joymap[id], keyname);
uiButtonSetText(dlg->pollbtn, keyname);
uiControlEnable(uiControl(dlg->pollbtn));
dlg->pollid = -1;
uiControlSetFocus(uiControl(dlg->pollbtn));
}
Uint32 JoyPoll(Uint32 interval, void* param)
{
InputDlgData* dlg = (InputDlgData*)param;
if (dlg->pollid < 0x100)
{
dlg->timer = 0;
return 0;
}
int id = dlg->pollid & 0xFF;
if (id > 12)
{
dlg->timer = 0;
return 0;
}
SDL_Joystick* joy = Joystick;
if (!joy)
{
dlg->timer = 0;
return 0;
}
if (!SDL_JoystickGetAttached(joy))
{
dlg->timer = 0;
return 0;
}
int oldmap;
if (dlg->joymap[id] == -1) oldmap = 0xFFFF;
else oldmap = dlg->joymap[id];
int nbuttons = SDL_JoystickNumButtons(joy);
for (int i = 0; i < nbuttons; i++)
{
if (SDL_JoystickGetButton(joy, i))
{
dlg->joymap[id] = (oldmap & 0xFFFF0000) | i;
uiQueueMain(FinishJoyMapping, dlg);
dlg->timer = 0;
return 0;
}
}
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;
dlg->joymap[id] = (oldmap & 0xFFFF0000) | 0x100 | blackhat | (i << 4);
uiQueueMain(FinishJoyMapping, dlg);
dlg->timer = 0;
return 0;
}
}
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 - dlg->axes_rest[i]);
if (dlg->axes_rest[i] < -16384 && axisval >= 0)
{
dlg->joymap[id] = (oldmap & 0xFFFF) | 0x10000 | (2 << 20) | (i << 24);
uiQueueMain(FinishJoyMapping, dlg);
dlg->timer = 0;
return 0;
}
else if (diff > 16384)
{
int axistype;
if (axisval > 0) axistype = 0;
else axistype = 1;
dlg->joymap[id] = (oldmap & 0xFFFF) | 0x10000 | (axistype << 20) | (i << 24);
uiQueueMain(FinishJoyMapping, dlg);
dlg->timer = 0;
return 0;
}
}
return interval;
}
void OnKeyStartConfig(uiButton* btn, void* data)
{
InputDlgData* dlg = (InputDlgData*)uiControl(btn)->UserData;
if (dlg->pollid != -1)
{
// TODO: handle this better?
if (dlg->pollid <= 12)
uiControlSetFocus(uiControl(dlg->keypresscatcher));
return;
}
int id = *(int*)data;
dlg->pollid = id;
dlg->pollbtn = btn;
uiButtonSetText(btn, "[press key]");
uiControlDisable(uiControl(btn));
uiControlSetFocus(uiControl(dlg->keypresscatcher));
}
void OnJoyStartConfig(uiButton* btn, void* data)
{
InputDlgData* dlg = (InputDlgData*)uiControl(btn)->UserData;
if (dlg->pollid != -1)
{
// TODO: handle this better?
if (dlg->pollid <= 12)
uiControlSetFocus(uiControl(dlg->keypresscatcher));
return;
}
int naxes = SDL_JoystickNumAxes(Joystick);
if (naxes > 16) naxes = 16;
for (int a = 0; a < naxes; a++)
{
dlg->axes_rest[a] = SDL_JoystickGetAxis(Joystick, a);
}
int id = *(int*)data;
dlg->pollid = id | 0x100;
dlg->pollbtn = btn;
uiButtonSetText(btn, "[press button / axis]");
uiControlDisable(uiControl(btn));
dlg->timer = SDL_AddTimer(100, JoyPoll, dlg);
uiControlSetFocus(uiControl(dlg->keypresscatcher));
}
void OnJoystickChanged(uiCombobox* cb, void* data)
{
JoystickID = uiComboboxSelected(cb);
OpenJoystick();
}
int OnCloseWindow(uiWindow* window, void* blarg)
{
InputDlgData* dlg = (InputDlgData*)(uiControl(window)->UserData);
openedmask &= ~(1 << dlg->type);
if (dlg->timer) SDL_RemoveTimer(dlg->timer);
JoystickID = Config::JoystickID;
OpenJoystick();
return 1;
}
void OnGetFocus(uiWindow* window, void* blarg)
{
InputDlgData* dlg = (InputDlgData*)(uiControl(window)->UserData);
if (dlg->pollid >= 0)
uiControlSetFocus(uiControl(dlg->keypresscatcher));
}
void OnLoseFocus(uiWindow* window, void* blarg)
{
}
void OnCancel(uiButton* btn, void* data)
{
InputDlgData* dlg = (InputDlgData*)data;
uiControlDestroy(uiControl(dlg->win));
openedmask &= ~(1 << dlg->type);
if (dlg->timer) SDL_RemoveTimer(dlg->timer);
JoystickID = Config::JoystickID;
OpenJoystick();
}
void OnOk(uiButton* btn, void* data)
{
InputDlgData* dlg = (InputDlgData*)data;
if (dlg->type == 0)
{
memcpy(Config::KeyMapping, dlg->keymap, sizeof(int)*12);
memcpy(Config::JoyMapping, dlg->joymap, sizeof(int)*12);
}
else if (dlg->type == 1)
{
memcpy(Config::HKKeyMapping, dlg->keymap, sizeof(int)*HK_MAX);
memcpy(Config::HKJoyMapping, dlg->joymap, sizeof(int)*HK_MAX);
}
Config::JoystickID = JoystickID;
Config::Save();
uiControlDestroy(uiControl(dlg->win));
openedmask &= ~(1 << dlg->type);
if (dlg->timer) SDL_RemoveTimer(dlg->timer);
}
void Open(int type)
{
InputDlgData* dlg = &inputdlg[type];
int mask = 1 << type;
if (openedmask & mask)
{
uiControlSetFocus(uiControl(dlg->win));
return;
}
openedmask |= mask;
dlg->type = type;
dlg->pollid = -1;
dlg->timer = 0;
if (type == 0)
{
dlg->numkeys = 12;
memcpy(dlg->keymap, Config::KeyMapping, sizeof(int)*12);
memcpy(dlg->joymap, Config::JoyMapping, sizeof(int)*12);
dlg->win = uiNewWindow("Input config - melonDS", 600, 100, 0, 0, 0);
}
else if (type == 1)
{
dlg->numkeys = HK_MAX;
memcpy(dlg->keymap, Config::HKKeyMapping, sizeof(int)*HK_MAX);
memcpy(dlg->joymap, Config::HKJoyMapping, sizeof(int)*HK_MAX);
dlg->win = uiNewWindow("Hotkey config - melonDS", 700, 100, 0, 0, 0);
}
uiControl(dlg->win)->UserData = dlg;
uiWindowSetMargined(dlg->win, 1);
uiWindowOnClosing(dlg->win, OnCloseWindow, NULL);
uiWindowOnGetFocus(dlg->win, OnGetFocus, NULL);
uiWindowOnLoseFocus(dlg->win, OnLoseFocus, NULL);
dlg->areahandler.Draw = OnAreaDraw;
dlg->areahandler.MouseEvent = OnAreaMouseEvent;
dlg->areahandler.MouseCrossed = OnAreaMouseCrossed;
dlg->areahandler.DragBroken = OnAreaDragBroken;
dlg->areahandler.KeyEvent = OnAreaKeyEvent;
dlg->areahandler.Resize = OnAreaResize;
uiBox* top = uiNewVerticalBox();
uiWindowSetChild(dlg->win, uiControl(top));
uiControlHide(uiControl(top));
{
uiBox* in_ctrl = uiNewHorizontalBox();
uiBoxAppend(top, uiControl(in_ctrl), 0);
uiBoxSetPadded(in_ctrl, 1);
uiGroup* g_key = uiNewGroup("Keyboard");
uiBoxAppend(in_ctrl, uiControl(g_key), 1);
uiGrid* b_key = uiNewGrid();
uiGroupSetChild(g_key, uiControl(b_key));
const int width = 240;
for (int i = 0; i < dlg->numkeys; i++)
{
int j = (type==0) ? dskeyorder[i] : i;
uiLabel* label = uiNewLabel((type==0) ? dskeylabels[j] : hotkeylabels[j]);
uiGridAppend(b_key, uiControl(label), 0, i, 1, 1, 1, uiAlignStart, 1, uiAlignCenter);
uiControlSetMinSize(uiControl(label), width, 1);
char keyname[64];
KeyMappingName(dlg->keymap[j], keyname);
uiButton* btn = uiNewButton(keyname);
uiControl(btn)->UserData = dlg;
uiGridAppend(b_key, uiControl(btn), 1, i, 1, 1, 1, uiAlignFill, 1, uiAlignCenter);
uiButtonOnClicked(btn, OnKeyStartConfig, (type==0) ? &dskeyorder[i] : &identity[i]);
uiControlSetMinSize(uiControl(btn), width, 1);
}
uiGroup* g_joy = uiNewGroup("Joystick");
uiBoxAppend(in_ctrl, uiControl(g_joy), 1);
uiGrid* b_joy = uiNewGrid();
uiGroupSetChild(g_joy, uiControl(b_joy));
for (int i = 0; i < dlg->numkeys; i++)
{
int j = (type==0) ? dskeyorder[i] : i;
uiLabel* label = uiNewLabel((type==0) ? dskeylabels[j] : hotkeylabels[j]);
uiGridAppend(b_joy, uiControl(label), 0, i, 1, 1, 1, uiAlignStart, 1, uiAlignCenter);
uiControlSetMinSize(uiControl(label), width, 1);
char keyname[64];
JoyMappingName(dlg->joymap[j], keyname);
uiButton* btn = uiNewButton(keyname);
uiControl(btn)->UserData = dlg;
uiGridAppend(b_joy, uiControl(btn), 1, i, 1, 1, 1, uiAlignFill, 1, uiAlignCenter);
uiButtonOnClicked(btn, OnJoyStartConfig, (type==0) ? &dskeyorder[i] : &identity[i]);
uiControlSetMinSize(uiControl(btn), width, 1);
}
if (type == 0)
{
uiLabel* dummy = uiNewLabel(" ");
uiGridAppend(b_key, uiControl(dummy), 0, dlg->numkeys, 2, 1, 1, uiAlignFill, 1, uiAlignCenter);
uiCombobox* joycombo = uiNewCombobox();
uiGridAppend(b_joy, uiControl(joycombo), 0, dlg->numkeys, 2, 1, 1, uiAlignFill, 1, uiAlignCenter);
int numjoys = SDL_NumJoysticks();
if (numjoys < 1)
{
uiComboboxAppend(joycombo, "(no joysticks available)");
uiControlDisable(uiControl(joycombo));
}
else
{
for (int i = 0; i < numjoys; i++)
{
const char* joyname = SDL_JoystickNameForIndex(i);
char fullname[256];
snprintf(fullname, 256, "%d. %s", i+1, joyname);
uiComboboxAppend(joycombo, fullname);
}
uiComboboxSetSelected(joycombo, JoystickID);
uiComboboxOnSelected(joycombo, OnJoystickChanged, NULL);
}
}
}
uiLabel* filler = uiNewLabel("");
uiBoxAppend(top, uiControl(filler), 1);
{
uiBox* in_ctrl = uiNewHorizontalBox();
uiBoxSetPadded(in_ctrl, 1);
uiBoxAppend(top, uiControl(in_ctrl), 0);
uiLabel* dummy = uiNewLabel("");
uiBoxAppend(in_ctrl, uiControl(dummy), 1);
dlg->keypresscatcher = uiNewArea(&dlg->areahandler);
uiControl(dlg->keypresscatcher)->UserData = dlg;
uiBoxAppend(in_ctrl, uiControl(dlg->keypresscatcher), 0);
uiButton* btncancel = uiNewButton("Cancel");
uiButtonOnClicked(btncancel, OnCancel, dlg);
uiBoxAppend(in_ctrl, uiControl(btncancel), 0);
uiButton* btnok = uiNewButton("Ok");
uiButtonOnClicked(btnok, OnOk, dlg);
uiBoxAppend(in_ctrl, uiControl(btnok), 0);
}
uiControlShow(uiControl(top));
uiControlShow(uiControl(dlg->win));
}
void Close(int type)
{
if (openedmask & (1<<type))
uiControlDestroy(uiControl(inputdlg[type].win));
openedmask &= ~(1<<type);
if (inputdlg[type].timer) SDL_RemoveTimer(inputdlg[type].timer);
}
}

View File

@ -1,354 +0,0 @@
/*
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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "libui/ui.h"
#include "../types.h"
#include "PlatformConfig.h"
#include "DlgVideoSettings.h"
void ApplyNewSettings(int type);
namespace DlgVideoSettings
{
bool opened;
uiWindow* win;
uiRadioButtons* rbRenderer;
uiCheckbox* cbGLDisplay;
uiCheckbox* cbVSync;
uiCheckbox* cbThreaded3D;
uiCombobox* cbResolution;
uiCheckbox* cbAntialias;
int old_renderer;
int old_gldisplay;
int old_vsync;
int old_threaded3D;
int old_resolution;
int old_antialias;
void UpdateControls()
{
int renderer = uiRadioButtonsSelected(rbRenderer);
if (renderer == 0)
{
uiControlEnable(uiControl(cbGLDisplay));
uiControlEnable(uiControl(cbThreaded3D));
uiControlDisable(uiControl(cbResolution));
//uiControlDisable(uiControl(cbAntialias));
}
else
{
uiControlDisable(uiControl(cbGLDisplay));
uiControlDisable(uiControl(cbThreaded3D));
uiControlEnable(uiControl(cbResolution));
//uiControlEnable(uiControl(cbAntialias));
}
}
void RevertSettings()
{
bool apply0 = false;
bool apply2 = false;
bool apply3 = false;
bool old_usegl = (old_gldisplay != 0) || (old_renderer != 0);
bool new_usegl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0);
if (old_renderer != Config::_3DRenderer)
{
Config::_3DRenderer = old_renderer;
apply3 = true;
}
if (old_gldisplay != Config::ScreenUseGL)
{
Config::ScreenUseGL = old_gldisplay;
}
if (old_vsync != Config::ScreenVSync)
{
Config::ScreenVSync = old_vsync;
//ApplyNewSettings(4);
}
if (old_usegl != new_usegl)
{
apply2 = true;
}
if (old_threaded3D != Config::Threaded3D)
{
Config::Threaded3D = old_threaded3D;
apply0 = true;
}
if (old_resolution != Config::GL_ScaleFactor ||
old_antialias != Config::GL_Antialias)
{
Config::GL_ScaleFactor = old_resolution;
Config::GL_Antialias = old_antialias;
apply0 = true;
}
if (apply2) ApplyNewSettings(2);
else if (apply3) ApplyNewSettings(3);
if (apply0) ApplyNewSettings(0);
}
int OnCloseWindow(uiWindow* window, void* blarg)
{
RevertSettings();
opened = false;
return 1;
}
void OnRendererChanged(uiRadioButtons* rb, void* blarg)
{
int id = uiRadioButtonsSelected(rb);
bool old_usegl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0);
Config::_3DRenderer = id;
UpdateControls();
bool new_usegl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0);
if (new_usegl) uiControlEnable(uiControl(cbVSync));
else uiControlDisable(uiControl(cbVSync));
if (new_usegl != old_usegl)
ApplyNewSettings(2);
else
ApplyNewSettings(3);
uiControlSetFocus(uiControl(win));
}
void OnGLDisplayChanged(uiCheckbox* cb, void* blarg)
{
Config::ScreenUseGL = uiCheckboxChecked(cb);
if (Config::ScreenUseGL) uiControlEnable(uiControl(cbVSync));
else uiControlDisable(uiControl(cbVSync));
ApplyNewSettings(2);
uiControlSetFocus(uiControl(win));
}
void OnVSyncChanged(uiCheckbox* cb, void* blarg)
{
Config::ScreenVSync = uiCheckboxChecked(cb);
//ApplyNewSettings(4);
}
void OnThreaded3DChanged(uiCheckbox* cb, void* blarg)
{
Config::Threaded3D = uiCheckboxChecked(cb);
ApplyNewSettings(0);
}
void OnResolutionChanged(uiCombobox* cb, void* blarg)
{
int id = uiComboboxSelected(cb);
Config::GL_ScaleFactor = id+1;
ApplyNewSettings(0);
}
void OnAntialiasChanged(uiCheckbox* cb, void* blarg)
{
Config::GL_Antialias = uiCheckboxChecked(cb);
ApplyNewSettings(0);
}
void OnCancel(uiButton* btn, void* blarg)
{
RevertSettings();
uiControlDestroy(uiControl(win));
opened = false;
}
void OnOk(uiButton* btn, void* blarg)
{
Config::Save();
uiControlDestroy(uiControl(win));
opened = false;
}
void Open()
{
if (opened)
{
uiControlSetFocus(uiControl(win));
return;
}
opened = true;
win = uiNewWindow("Video settings - melonDS", 400, 100, 0, 0, 0);
uiWindowSetMargined(win, 1);
uiWindowOnClosing(win, OnCloseWindow, NULL);
uiBox* top = uiNewVerticalBox();
uiWindowSetChild(win, uiControl(top));
uiBoxSetPadded(top, 1);
uiBox* splitter = uiNewHorizontalBox();
uiBoxAppend(top, uiControl(splitter), 0);
uiBoxSetPadded(splitter, 1);
uiBox* left = uiNewVerticalBox();
uiBoxAppend(splitter, uiControl(left), 1);
uiBoxSetPadded(left, 1);
uiBox* right = uiNewVerticalBox();
uiBoxAppend(splitter, uiControl(right), 1);
uiBoxSetPadded(right, 1);
{
uiGroup* grp = uiNewGroup("Display settings");
uiBoxAppend(left, uiControl(grp), 0);
uiGroupSetMargined(grp, 1);
uiBox* in_ctrl = uiNewVerticalBox();
uiGroupSetChild(grp, uiControl(in_ctrl));
uiLabel* lbl = uiNewLabel("3D renderer:");
uiBoxAppend(in_ctrl, uiControl(lbl), 0);
rbRenderer = uiNewRadioButtons();
uiRadioButtonsAppend(rbRenderer, "Software");
uiRadioButtonsAppend(rbRenderer, "OpenGL");
uiRadioButtonsOnSelected(rbRenderer, OnRendererChanged, NULL);
uiBoxAppend(in_ctrl, uiControl(rbRenderer), 0);
lbl = uiNewLabel("");
uiBoxAppend(in_ctrl, uiControl(lbl), 0);
cbGLDisplay = uiNewCheckbox("OpenGL display");
uiCheckboxOnToggled(cbGLDisplay, OnGLDisplayChanged, NULL);
uiBoxAppend(in_ctrl, uiControl(cbGLDisplay), 0);
cbVSync = uiNewCheckbox("VSync");
uiCheckboxOnToggled(cbVSync, OnVSyncChanged, NULL);
uiBoxAppend(in_ctrl, uiControl(cbVSync), 0);
}
{
uiGroup* grp = uiNewGroup("Software renderer");
uiBoxAppend(right, uiControl(grp), 0);
uiGroupSetMargined(grp, 1);
uiBox* in_ctrl = uiNewVerticalBox();
uiGroupSetChild(grp, uiControl(in_ctrl));
cbThreaded3D = uiNewCheckbox("Threaded");
uiCheckboxOnToggled(cbThreaded3D, OnThreaded3DChanged, NULL);
uiBoxAppend(in_ctrl, uiControl(cbThreaded3D), 0);
}
{
uiGroup* grp = uiNewGroup("OpenGL renderer");
uiBoxAppend(right, uiControl(grp), 0);
uiGroupSetMargined(grp, 1);
uiBox* in_ctrl = uiNewVerticalBox();
uiGroupSetChild(grp, uiControl(in_ctrl));
uiLabel* lbl = uiNewLabel("Internal resolution:");
uiBoxAppend(in_ctrl, uiControl(lbl), 0);
cbResolution = uiNewCombobox();
uiComboboxOnSelected(cbResolution, OnResolutionChanged, NULL);
for (int i = 1; i <= 8; i++)
{
char txt[64];
sprintf(txt, "%dx native (%dx%d)", i, 256*i, 192*i);
uiComboboxAppend(cbResolution, txt);
}
uiBoxAppend(in_ctrl, uiControl(cbResolution), 0);
//lbl = uiNewLabel("");
//uiBoxAppend(in_ctrl, uiControl(lbl), 0);
//cbAntialias = uiNewCheckbox("Antialiasing");
//uiCheckboxOnToggled(cbAntialias, OnAntialiasChanged, NULL);
//uiBoxAppend(in_ctrl, uiControl(cbAntialias), 0);
}
{
uiBox* in_ctrl = uiNewHorizontalBox();
uiBoxSetPadded(in_ctrl, 1);
uiBoxAppend(top, uiControl(in_ctrl), 0);
uiLabel* dummy = uiNewLabel("");
uiBoxAppend(in_ctrl, uiControl(dummy), 1);
uiButton* btncancel = uiNewButton("Cancel");
uiButtonOnClicked(btncancel, OnCancel, NULL);
uiBoxAppend(in_ctrl, uiControl(btncancel), 0);
uiButton* btnok = uiNewButton("Ok");
uiButtonOnClicked(btnok, OnOk, NULL);
uiBoxAppend(in_ctrl, uiControl(btnok), 0);
}
Config::_3DRenderer = Config::_3DRenderer ? 1 : 0;
if (Config::GL_ScaleFactor < 1) Config::GL_ScaleFactor = 1;
else if (Config::GL_ScaleFactor > 8) Config::GL_ScaleFactor = 8;
old_renderer = Config::_3DRenderer;
old_gldisplay = Config::ScreenUseGL;
old_vsync = Config::ScreenVSync;
old_threaded3D = Config::Threaded3D;
old_resolution = Config::GL_ScaleFactor;
old_antialias = Config::GL_Antialias;
uiCheckboxSetChecked(cbGLDisplay, Config::ScreenUseGL);
uiCheckboxSetChecked(cbVSync, Config::ScreenVSync);
uiCheckboxSetChecked(cbThreaded3D, Config::Threaded3D);
uiComboboxSetSelected(cbResolution, Config::GL_ScaleFactor-1);
//uiCheckboxSetChecked(cbAntialias, Config::GL_Antialias);
uiRadioButtonsSetSelected(rbRenderer, Config::_3DRenderer);
UpdateControls();
if (Config::ScreenUseGL || Config::_3DRenderer != 0)
uiControlEnable(uiControl(cbVSync));
else
uiControlDisable(uiControl(cbVSync));
uiControlShow(uiControl(win));
}
void Close()
{
if (!opened) return;
uiControlDestroy(uiControl(win));
opened = false;
}
}

View File

@ -1,30 +0,0 @@
/*
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 DLGVIDEOSETTINGS_H
#define DLGVIDEOSETTINGS_H
namespace DlgVideoSettings
{
void Open();
void Close();
}
#endif // DLGVIDEOSETTINGS_H

View File

@ -1,271 +0,0 @@
/*
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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "libui/ui.h"
#include "../types.h"
#include "PlatformConfig.h"
#include "LAN_Socket.h"
#include "LAN_PCap.h"
#include "DlgWifiSettings.h"
#ifdef __WIN32__
#define PCAP_NAME "winpcap/npcap"
#else
#define PCAP_NAME "libpcap"
#endif // __WIN32__
void ApplyNewSettings(int type);
namespace DlgWifiSettings
{
bool opened;
uiWindow* win;
bool haspcap;
uiCheckbox* cbBindAnyAddr;
uiLabel* lbAdapterList;
uiCombobox* cmAdapterList;
uiCheckbox* cbDirectLAN;
uiLabel* lbAdapterMAC;
uiLabel* lbAdapterIP;
uiLabel* lbAdapterDNS0;
uiLabel* lbAdapterDNS1;
void UpdateAdapterControls()
{
bool enable = haspcap && uiCheckboxChecked(cbDirectLAN);
if (enable)
{
uiControlEnable(uiControl(lbAdapterList));
uiControlEnable(uiControl(cmAdapterList));
uiControlEnable(uiControl(lbAdapterMAC));
uiControlEnable(uiControl(lbAdapterIP));
}
else
{
uiControlDisable(uiControl(lbAdapterList));
uiControlDisable(uiControl(cmAdapterList));
uiControlDisable(uiControl(lbAdapterMAC));
uiControlDisable(uiControl(lbAdapterIP));
}
}
void UpdateAdapterInfo()
{
if (!haspcap) return;
int sel = uiComboboxSelected(cmAdapterList);
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]);
uiLabelSetText(lbAdapterMAC, tmp);
sprintf(tmp, "IP: %d.%d.%d.%d",
adapter->IP_v4[0], adapter->IP_v4[1],
adapter->IP_v4[2], adapter->IP_v4[3]);
uiLabelSetText(lbAdapterIP, tmp);
/*sprintf(tmp, "Primary DNS: %d.%d.%d.%d",
adapter->DNS[0][0], adapter->DNS[0][1],
adapter->DNS[0][2], adapter->DNS[0][3]);
uiLabelSetText(lbAdapterDNS0, tmp);
sprintf(tmp, "Secondary DNS: %d.%d.%d.%d",
adapter->DNS[1][0], adapter->DNS[1][1],
adapter->DNS[1][2], adapter->DNS[1][3]);
uiLabelSetText(lbAdapterDNS1, tmp);*/
}
int OnCloseWindow(uiWindow* window, void* blarg)
{
opened = false;
return 1;
}
void OnDirectModeToggle(uiCheckbox* c, void* blarg)
{
UpdateAdapterControls();
}
void OnAdapterSelect(uiCombobox* c, void* blarg)
{
UpdateAdapterInfo();
}
void OnCancel(uiButton* btn, void* blarg)
{
uiControlDestroy(uiControl(win));
opened = false;
}
void OnOk(uiButton* btn, void* blarg)
{
Config::SocketBindAnyAddr = uiCheckboxChecked(cbBindAnyAddr);
Config::DirectLAN = uiCheckboxChecked(cbDirectLAN);
int sel = uiComboboxSelected(cmAdapterList);
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();
uiControlDestroy(uiControl(win));
opened = false;
ApplyNewSettings(1);
}
void Open()
{
if (opened)
{
uiControlSetFocus(uiControl(win));
return;
}
LAN_Socket::Init();
haspcap = LAN_PCap::Init(false);
opened = true;
win = uiNewWindow("Wifi settings - melonDS", 400, 100, 0, 0, 0);
uiWindowSetMargined(win, 1);
uiWindowOnClosing(win, OnCloseWindow, NULL);
uiBox* top = uiNewVerticalBox();
uiWindowSetChild(win, uiControl(top));
uiBoxSetPadded(top, 1);
{
uiGroup* grp = uiNewGroup("Local");
uiBoxAppend(top, uiControl(grp), 0);
uiGroupSetMargined(grp, 1);
uiBox* in_ctrl = uiNewVerticalBox();
uiGroupSetChild(grp, uiControl(in_ctrl));
cbBindAnyAddr = uiNewCheckbox("Bind socket to any address");
uiBoxAppend(in_ctrl, uiControl(cbBindAnyAddr), 0);
}
{
uiLabel* lbl;
uiGroup* grp = uiNewGroup("Online");
uiBoxAppend(top, uiControl(grp), 0);
uiGroupSetMargined(grp, 1);
uiBox* in_ctrl = uiNewVerticalBox();
uiGroupSetChild(grp, uiControl(in_ctrl));
cbDirectLAN = uiNewCheckbox("Direct mode (requires " PCAP_NAME " and ethernet connection)");
uiCheckboxOnToggled(cbDirectLAN, OnDirectModeToggle, NULL);
uiBoxAppend(in_ctrl, uiControl(cbDirectLAN), 0);
lbAdapterList = uiNewLabel("Network adapter:");
uiBoxAppend(in_ctrl, uiControl(lbAdapterList), 0);
cmAdapterList = uiNewCombobox();
uiComboboxOnSelected(cmAdapterList, OnAdapterSelect, NULL);
uiBoxAppend(in_ctrl, uiControl(cmAdapterList), 0);
lbAdapterMAC = uiNewLabel("MAC: ??");
uiBoxAppend(in_ctrl, uiControl(lbAdapterMAC), 0);
lbAdapterIP = uiNewLabel("IP: ??");
uiBoxAppend(in_ctrl, uiControl(lbAdapterIP), 0);
/*lbAdapterDNS0 = uiNewLabel("DNS0");
uiBoxAppend(in_ctrl, uiControl(lbAdapterDNS0), 0);
lbAdapterDNS1 = uiNewLabel("DNS1");
uiBoxAppend(in_ctrl, uiControl(lbAdapterDNS1), 0);*/
}
{
uiBox* in_ctrl = uiNewHorizontalBox();
uiBoxSetPadded(in_ctrl, 1);
uiBoxAppend(top, uiControl(in_ctrl), 0);
uiLabel* dummy = uiNewLabel("");
uiBoxAppend(in_ctrl, uiControl(dummy), 1);
uiButton* btncancel = uiNewButton("Cancel");
uiButtonOnClicked(btncancel, OnCancel, NULL);
uiBoxAppend(in_ctrl, uiControl(btncancel), 0);
uiButton* btnok = uiNewButton("Ok");
uiButtonOnClicked(btnok, OnOk, NULL);
uiBoxAppend(in_ctrl, uiControl(btnok), 0);
}
uiCheckboxSetChecked(cbBindAnyAddr, Config::SocketBindAnyAddr);
int sel = 0;
for (int i = 0; i < LAN_PCap::NumAdapters; i++)
{
LAN_PCap::AdapterData* adapter = &LAN_PCap::Adapters[i];
uiComboboxAppend(cmAdapterList, adapter->FriendlyName);
if (!strncmp(adapter->DeviceName, Config::LANDevice, 128))
sel = i;
}
uiComboboxSetSelected(cmAdapterList, sel);
UpdateAdapterInfo();
uiCheckboxSetChecked(cbDirectLAN, Config::DirectLAN);
if (!haspcap) uiControlDisable(uiControl(cbDirectLAN));
UpdateAdapterControls();
uiControlShow(uiControl(win));
}
void Close()
{
if (!opened) return;
uiControlDestroy(uiControl(win));
opened = false;
}
}

View File

@ -1,347 +0,0 @@
/*
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 <stdio.h>
#include <string.h>
#include "MelonCap.h"
#include "libui/ui.h"
#include "../NDS.h"
#include "../GPU.h"
#include <windows.h>
#include <setupapi.h>
#include <guiddef.h>
#include <winusb.h>
namespace MelonCap
{
uiWindow* Window;
uiArea* Area;
uiAreaHandler AreaHandler;
uiDrawBitmap* WinBitmap;
bool WinBitmapInited;
u32* WinBitmapData;
// this crap was built from the reverse-engineering of ds_capture.exe
// mixed in with their Linux capture sample code
GUID InterfaceClass = {0xA0B880F6, 0xD6A5, 0x4700, {0xA8, 0xEA, 0x22, 0x28, 0x2A, 0xCA, 0x55, 0x87}};
HANDLE CapHandle;
WINUSB_INTERFACE_HANDLE CapUSBHandle;
void OnAreaDraw(uiAreaHandler* handler, uiArea* area, uiAreaDrawParams* params)
{
if (!WinBitmapInited)
{
if (WinBitmap) uiDrawFreeBitmap(WinBitmap);
WinBitmapInited = true;
WinBitmap = uiDrawNewBitmap(params->Context, 768, 384, 0);
}
if (!WinBitmap) return;
if (!WinBitmapData) return;
uiRect rc = {0, 0, 768, 384};
uiDrawBitmapUpdate(WinBitmap, WinBitmapData);
uiDrawBitmapDraw(params->Context, WinBitmap, &rc, &rc, 0);
}
void OnAreaMouseEvent(uiAreaHandler* handler, uiArea* area, uiAreaMouseEvent* evt)
{
}
void OnAreaMouseCrossed(uiAreaHandler* handler, uiArea* area, int left)
{
}
void OnAreaDragBroken(uiAreaHandler* handler, uiArea* area)
{
}
int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt)
{
return 1;
}
void OnAreaResize(uiAreaHandler* handler, uiArea* area, int width, int height)
{
}
void Init()
{
printf("MelonCap init\n");
HDEVINFO devinfo = SetupDiGetClassDevsW(&InterfaceClass, NULL, NULL, DIGCF_DEVICEINTERFACE|DIGCF_PRESENT);
if (devinfo == INVALID_HANDLE_VALUE) return;
int member = 0;
bool good = false;
for (;;)
{
SP_DEVICE_INTERFACE_DATA interfacedata;
memset(&interfacedata, 0, sizeof(interfacedata));
interfacedata.cbSize = sizeof(interfacedata);
BOOL ret = SetupDiEnumDeviceInterfaces(devinfo, NULL, &InterfaceClass, member, &interfacedata);
if (!ret)
{
printf("found %d interfaces\n", member);
break;
}
DWORD requiredsize = 0;
SetupDiGetDeviceInterfaceDetailW(devinfo, &interfacedata, NULL, NULL, &requiredsize, NULL);
printf("%d: required size %d\n", member, requiredsize);
PSP_DEVICE_INTERFACE_DETAIL_DATA_W interfacedetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA_W)new u8[requiredsize];
interfacedetail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA_W);
ret = SetupDiGetDeviceInterfaceDetailW(devinfo, &interfacedata, interfacedetail, requiredsize, NULL, NULL);
if (ret)
{
printf("got interface detail: path=%S\n", interfacedetail->DevicePath);
HANDLE file = CreateFileW(interfacedetail->DevicePath, GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
if (file != INVALID_HANDLE_VALUE)
{
WINUSB_INTERFACE_HANDLE usbhandle;
ret = WinUsb_Initialize(file, &usbhandle);
if (ret)
{
int val;
val = 0x1E;
WinUsb_SetPipePolicy(usbhandle, 0x00, PIPE_TRANSFER_TIMEOUT, 4, &val);
val = 0x32;
WinUsb_SetPipePolicy(usbhandle, 0x82, PIPE_TRANSFER_TIMEOUT, 4, &val);
val = 0x01;
WinUsb_SetPipePolicy(usbhandle, 0x82, RAW_IO, 1, &val);
printf("looking good\n");
good = true;
CapHandle = file;
CapUSBHandle = usbhandle;
}
else
CloseHandle(file);
}
}
delete[] (u8*)interfacedetail;
if (good) break;
member++;
}
SetupDiDestroyDeviceInfoList(devinfo);
AreaHandler.Draw = OnAreaDraw;
AreaHandler.MouseEvent = OnAreaMouseEvent;
AreaHandler.MouseCrossed = OnAreaMouseCrossed;
AreaHandler.DragBroken = OnAreaDragBroken;
AreaHandler.KeyEvent = OnAreaKeyEvent;
AreaHandler.Resize = OnAreaResize;
WinBitmapInited = false;
WinBitmapData = new u32[768*384];
Window = uiNewWindow("melonDS - topnotch pixel checker", 768, 384, 0, 0, 0);
Area = uiNewArea(&AreaHandler);
uiWindowSetChild(Window, uiControl(Area));
uiControlShow(uiControl(Window));
}
void DeInit()
{
uiControlDestroy(uiControl(Window));
uiDrawFreeBitmap(WinBitmap);
WinBitmapInited = false;
delete[] WinBitmapData;
WinUsb_Free(CapUSBHandle);
CloseHandle(CapHandle);
}
int VendorIn(u8 req, u16 len, u8* buf)
{
WINUSB_SETUP_PACKET pkt;
pkt.RequestType = 0xC0; // device to host
pkt.Request = req;
pkt.Value = 0; // ?????
pkt.Index = 0;
pkt.Length = len;
ULONG ret = 0;
BOOL res = WinUsb_ControlTransfer(CapUSBHandle, pkt, buf, len, &ret, NULL);
if (!res) return -1;
return ret;
}
int VendorOut(u8 req, u16 val, u16 len, u8* buf)
{
WINUSB_SETUP_PACKET pkt;
pkt.RequestType = 0x40; // host to device
pkt.Request = req;
pkt.Value = val;
pkt.Index = 0;
pkt.Length = len;
ULONG ret = 0;
BOOL res = WinUsb_ControlTransfer(CapUSBHandle, pkt, buf, len, &ret, NULL);
if (!res) return -1;
return ret;
}
int BulkIn(u8* buf, u32 len)
{
ULONG ret = 0;
BOOL res = WinUsb_ReadPipe(CapUSBHandle, 0x82, buf, len, &ret, NULL);
if (!res) return -1;
return ret;
}
u32 ConvertColor(u16 col)
{
u32 b = col & 0x001F;
u32 g = (col & 0x07E0) >> 5;
u32 r = (col & 0xF800) >> 11;
u32 ret = 0xFF000000;
ret |= ((r << 3) | (r >> 2)) << 16;
ret |= ((g << 2) | (g >> 4)) << 8;
ret |= (b << 3) | (b >> 2);
return ret;
}
void CaptureFrame()
{
u32 ret;
u8 derp;
u32 framelen = 256*384*2;
u16 frame[framelen/2];
u32 framepos = 0;
u8 frameinfo[64];
ret = VendorOut(0x30, 0, 0, &derp);
if (ret < 0) return;
int tries = 0;
while (framepos < framelen)
{
ret = BulkIn((u8*)&frame[framepos/2], framelen-framepos);
if (ret < 0) break;
if (ret == 0)
{
tries++;
if (tries >= 100) break;
continue;
}
framepos += ret;
}
ret = VendorIn(0x30, 64, frameinfo);
if (ret < 0) return;
if ((frameinfo[0] & 0x03) != 0x03) return;
if (!frameinfo[52]) return;
u16* in = &frame[0];
u32* out = &WinBitmapData[256];
for (int y = 0; y < 384; y++)
{
u32* out = &WinBitmapData[((y/2)*768) + ((y&1)*128) + 256];
if (!(frameinfo[y>>3] & (1<<(y&7))))
{
continue;
}
for (int x = 0; x < 256/2; x++)
{
out[0] = ConvertColor(in[1]);
out[768*192] = ConvertColor(in[0]);
out++;
in += 2;
}
}
}
void Update()
{
// melonDS output
int frontbuf = GPU::FrontBuffer;
u32* topbuf = GPU::Framebuffer[frontbuf][0];
if (topbuf)
{
for (int y = 0; y < 192; y++)
{
memcpy(&WinBitmapData[y*768], &topbuf[y*256], 256*4);
}
}
u32* botbuf = GPU::Framebuffer[frontbuf][1];
if (botbuf)
{
for (int y = 0; y < 192; y++)
{
memcpy(&WinBitmapData[(y+192)*768], &botbuf[y*256], 256*4);
}
}
// DS capture
CaptureFrame();
// compare
for (int y = 0; y < 384; y++)
{
for (int x = 0; x < 256; x++)
{
u32 colA = WinBitmapData[(y*768) + x + 0];
u32 colB = WinBitmapData[(y*768) + x + 256];
// best we get from the capture card is RGB565
// so we'll ignore the lower bits
const u32 mask = 0x00F8FCF8;
colA &= mask;
colB &= mask;
if (colA == colB) WinBitmapData[(y*768) + x + 512] = 0xFF000000;//0xFF00FF00;
else WinBitmapData[(y*768) + x + 512] = 0xFFFFFFFF;//0xFFFF0000;
}
}
uiAreaQueueRedrawAll(Area);
}
}

View File

@ -1,23 +0,0 @@
os:
- linux
- osx
# This makes us use Ubuntu 14 instead of 12
dist: trusty
# Notes:
# - Travis uses cmake 3.0.2 on OS X; we need 3.1 or newer (thanks tbodt)
language: c
script:
- if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get update; fi
- if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get install libgtk-3-dev -y || sudo apt-cache search libgtk3; fi
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update; fi
- mkdir build
- cd build
- cmake --version
- cmake .. -G "Unix Makefiles"
- make tester examples
- rm -rf *
- cmake .. -G "Unix Makefiles" -DBUILD_SHARED_LIBS=OFF
- make tester examples

View File

@ -1,22 +0,0 @@
# Old Announcements
* **29 May 2016**
* **Alpha 3 is here!** Get it [here](https://github.com/andlabs/libui/releases/tag/alpha3).
* The next packaged release will introduce:
* uiGrid, another way to lay out controls, a la GtkGrid
* uiOpenGLArea, a way to render OpenGL content in a libui uiArea
* uiTable, a data grid control that may or may not have tree facilities (if it does, it will be called uiTree instead)
* a complete, possibly rewritten, drawing and text rendering infrastructure
* **24 May 2016**
* You can now help choose [a potential new build system for libui](https://github.com/andlabs/libui/issues/62).
* Tomorrow I will decide if OS X 10.7 will also be dropped alongside GTK+ 3.4-3.8 this Saturday. Stay tuned.
* **22 May 2016**
* Two more open questions I'd like your feedback on are available [here](https://github.com/andlabs/libui/issues/48) and [here](https://github.com/andlabs/libui/issues/25).
* Sometime in the next 48 hours (before 23:59 EDT on 24 May 2016) I will split `uiCombobox` into two separate controls, `uiCombobox` and `uiEditableCombobox`, each with slightly different events and "selected item" mechanics. Prepare your existing code.
* **21 May 2016**
* I will now post announcements and updates here.
* Now that Ubuntu 16.04 LTS is here, no earlier than next Saturday, 28 May 2016 at noon EDT, **I will bump the minimum GTK+ version from 3.4 to 3.10**. This will add a lot of new features that I can now add to libui, such as search-oriented uiEntries, lists of arbitrary control layouts, and more. If you are still running a Linux distribution that doesn't come with 3.10, you will either need to upgrade or use jhbuild to set up a newer version of GTK+ in a private environment.
* You can decide if I should also drop OS X 10.7 [here](https://github.com/andlabs/libui/issues/46).

View File

@ -1,219 +0,0 @@
# 3 june 2016
# see https://cmake.org/gitweb?p=cmake.git;a=commit;h=95cdf132489c79e88a10fdf7a7566fa002c7680b (thanks ngladitz in irc.freenode.net/#cmake)
cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
# TODOs
# - silence entering/leaving messages?
# - uname -s for more refined OS control
# - Haiku for haiku
# - debian DESTDIR? https://github.com/andlabs/libui/pull/10
# - libui-combined* needs to be deleted so that custom command can run every time
# - add notelemetry.obj to *ALL TARGETS* on VS2015 and up - https://www.infoq.com/news/2016/06/visual-cpp-telemetry
# - switch to 3.1.0 features
# the docs say we need to set this up prior to project()
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8")
# we want to disable incremental linking
# see also:
# - https://github.com/bulletphysics/bullet3/blob/master/CMakeLists.txt#L43
# - https://cmake.org/pipermail/cmake/2010-February/035174.html
# this must also go before project()
set(MSVC_INCREMENTAL_DEFAULT ON)
# default to debug builds
# do this before project() just to be safe
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE DEBUG CACHE STRING "" FORCE)
endif()
project(libui)
option(BUILD_SHARED_LIBS "Whether to build libui as a shared library or a static library" ON)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out")
set(CMAKE_PDB_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out")
if(APPLE)
set(_OSNAME darwin)
set(_HASVERSION TRUE)
set(_VERSION "A")
# always use our rpath
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
# the / is required by some older versions of OS X
set(CMAKE_INSTALL_RPATH "@executable_path/")
set(CMAKE_MACOSX_RPATH TRUE)
elseif(WIN32)
set(_OSNAME windows)
# and don't include the default libraries with ANY of the builds
# note the CACHE FORCE stuff is required here
set(CMAKE_C_STANDARD_LIBRARIES CACHE STRING "" FORCE)
set(CMAKE_CXX_STANDARD_LIBRARIES CACHE STRING "" FORCE)
else()
set(_OSNAME unix)
set(_HASVERSION TRUE)
set(_VERSION "0")
# always use our rpath
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
set(CMAKE_INSTALL_RPATH "\$ORIGIN")
endif()
# common flags
if(MSVC)
# TODO subsystem version
# TODO /Wall does too much
# TODO -Wno-switch equivalent
# TODO /sdl turns C4996 into an ERROR
# don't use /analyze; that requires us to write annotations everywhere
# TODO undecided flags from qo?
# /RTCc is not supplied because it's discouraged as of VS2015; see https://www.reddit.com/r/cpp/comments/46mhne/rtcc_rejects_conformant_code_with_visual_c_2015/d06auq5
# /EHsc is to shut the compiler up in some cases
# TODO make /EHsc C++-only
set(_COMMON_CFLAGS
/W4 /wd4100
/bigobj /nologo
/RTC1 /RTCs /RTCu
/EHsc
)
# note the /MANIFEST:NO (which must be / and uppercase); thanks FraGag (https://github.com/andlabs/libui/issues/93#issuecomment-223183436)
# TODO warnings on undefined symbols
set(_COMMON_LDFLAGS
/LARGEADDRESSAWARE
/NOLOGO
/INCREMENTAL:NO
/MANIFEST:NO
)
# TODO autogenerate a .def file?
# more incremental linking fixes
# TODO actually get rid of incremental linking here
else()
set(_COMMON_CFLAGS
-Wall -Wextra -pedantic
-Wno-unused-parameter
-Wno-switch
-fvisibility=hidden
)
# don't use C_VERSION or CXX_VERSION because they use GNU standards
# TODO we can actually do this; set both C_EXTENSIONS and CXX_EXTENSIONS to OFF
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --std=c99")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11")
set(_COMMON_LDFLAGS
-fvisibility=hidden
)
# don't require shipping the MinGW-w64 DLLs
if(WIN32)
list(APPEND _COMMON_LDFLAGS
-static
-static-libgcc
-static-libstdc++
)
endif()
endif()
# problem:
# - target_link_libraries() only supports - for flags
# - but cmake only doesn't generate the manifest if the flag has a /
macro(_target_link_options_private _target)
foreach(_opt IN LISTS ${ARGN})
set_property(TARGET ${_target} APPEND_STRING PROPERTY
LINK_FLAGS " ${_opt}")
endforeach()
endmacro()
add_subdirectory("common")
add_subdirectory("${_OSNAME}")
add_library(${_LIBUINAME} ${_LIBUI_SOURCES})
target_include_directories(${_LIBUINAME}
PUBLIC .
PRIVATE ${_LIBUI_INCLUEDIRS})
target_compile_definitions(${_LIBUINAME}
PRIVATE ${_LIBUI_DEFS})
# cmake produces this for us by default but only for shared libraries
target_compile_definitions(${_LIBUINAME}
PRIVATE libui_EXPORTS)
target_compile_options(${_LIBUINAME}
PUBLIC ${_COMMON_CFLAGS}
PRIVATE ${_LIBUI_CFLAGS})
# TODO link directories?
if(BUILD_SHARED_LIBS)
target_link_libraries(${_LIBUINAME}
PRIVATE ${_LIBUI_LIBS})
endif()
# TODO INTERFACE libs don't inherit to grandhcildren?
# on Windows the linker for static libraries is different; don't give it the flags
if(BUILD_SHARED_LIBS)
_target_link_options_private(${_LIBUINAME}
_COMMON_LDFLAGS
_LIBUI_LDFLAGS)
endif()
if(NOT BUILD_SHARED_LIBS)
_handle_static()
# TODO figure out a way to tell libui that it's static
target_compile_definitions(${_LIBUINAME}
PUBLIC _UI_STATIC)
endif()
if(NOT MSVC)
# on non-MSVC compilers cmake adds an extra lib-
# note that we apply this to libui, not to any intermediates
set_target_properties(libui PROPERTIES
OUTPUT_NAME ui)
# flags for warning on undefined symbols
# TODO figure out why FreeBSD follows linked libraries here
# TODO figure out MSVC equivalents
if(BUILD_SHARED_LIBS)
if(NOT (${CMAKE_SYSTEM_NAME} STREQUAL FreeBSD))
# on OS X we don't need to do this; Apple's linker warns about undefined symbols in -shared builds!
if(NOT APPLE)
target_link_libraries(libui
PRIVATE -Wl,--no-undefined -Wl,--no-allow-shlib-undefined
)
endif()
endif()
endif()
endif()
if(BUILD_SHARED_LIBS)
if(_HASVERSION)
set_target_properties(${_LIBUINAME} PROPERTIES
SOVERSION "${_VERSION}")
endif()
endif()
macro(_add_exec _name)
add_executable(${_name}
WIN32 EXCLUDE_FROM_ALL
${ARGN})
target_link_libraries(${_name} libui ${_LIBUI_STATIC_RES})
_target_link_options_private(${_name}
_COMMON_LDFLAGS)
# make shared-linked executables PIC too
if(BUILD_SHARED_LIBS)
set_property(TARGET ${_name} PROPERTY
POSITION_INDEPENDENT_CODE True)
endif()
# TODO see above about INTERFACE
if(NOT BUILD_SHARED_LIBS)
target_link_libraries(${_name}
${_LIBUI_LIBS})
endif()
# TODOfor some reason these don't propagate
if(NOT WIN32)
target_include_directories(${_name}
PUBLIC .)
target_compile_options(${_name}
PUBLIC ${_COMMON_CFLAGS})
endif()
endmacro()
add_subdirectory("test")
add_subdirectory("examples")

View File

@ -1,33 +0,0 @@
# Old Updates
* **29 May 2016**
* Thanks to @pcwalton, we can now statically link libui! Simply do `make STATIC=1` instead of just `make`.
* On Windows you must link both `libui.lib` and `libui.res` AND provide a Common Controls 6 manifest for output static binaries to work properly.
* **28 May 2016**
* As promised, **the minimum system requirements are now OS X 10.8 and GTK+ 3.10 for OS X and Unix, respectively**.
* **26 May 2016**
* Two OS X-specific functions have been added: `uiDarwinMarginAmount()` and `uiDarwinPaddingAmount()`. These return the amount of margins and padding, respectively, to give to a control, and are intended for container implementations. These are suitable for the constant of a NSLayoutConstraint. They both take a pointer parameter that is reserved for future use and should be `NULL`.
* **25 May 2016**
* uiDrawTextLayout attributes are now specified in units of *graphemes* on all platforms. This means characters as seen from a user's perspective, not Unicode codepoints or UTF-8 bytes. So a long string of combining marker codepoints after one codepoint would still count as one grapheme.
* **24 May 2016**
* As promised, `uiCombobox` is now split into `uiCombobox` for non-editable comboboxes and `uiEditableCombobox` for editable comboboxes. Mind the function changes as well :)
* There is a new function `uiMainStep()`, which runs one iteration of the main loop. It takes a single boolean argument, indicating whether to wait for an event to occur or not. It returns true if an event was processed (or if no event is available if you don't want to wait) and false if the event loop was told to stop (for instance, `uiQuit()` was called).
* **23 May 2016**
* Fixed surrogate pair drawing on OS X.
* **22 May 2016**
* Removed `uiControlVerifyDestroy()`; that is now part of `uiFreeControl()` itself.
* Added `uiPi`, a constant for π. This is provided for C and C++ programmers, where there is no standard named constant for π; bindings authors shouldn't need to worry about this.
* Fixed uiMultilineEntry not properly having line breaks on Windows.
* Added `uiNewNonWrappingMultilineEntry()`, which creates a uiMultilineEntry that scrolls horizontally instead of wrapping lines. (This is not documented as being changeable after the fact on Windows, hence it's a creation-time choice.)
* uiAreas on Windows and some internal Direct2D areas now respond to `WM_PRINTCLIENT` properly, which should hopefully increase the quality of screenshots.
* uiDateTimePicker on GTK+ works properly on RTL layouts and no longer disappears off the bottom of the screen if not enough room is available. It will also no longer be marked for localization of the time format (what the separator should be and whether to use 24-hour time), as that information is not provided by the locale system. :(
* Added `uiUserBugCannotSetParentOnToplevel()`, which should be used by implementations of toplevel controls in their `SetParent()` implementations. This will also be the beginning of consolidating common user bug messages into a single place, though this will be one of the only few exported user bug functions.
* uiSpinbox and uiSlider now merely swap their min and max if min ≥ max. They will no longer panic and do nothing, respectively.
* Matrix scaling will no longer leave the matrix in an invalid state on OS X and GTK+.
* `uiMultilineEntrySetText()` and `uiMutlilineEntryAppend()` on GTK+ no longer fire `OnChanged()` events.

View File

@ -1,141 +0,0 @@
# Useful things in newer versions
## Windows
### Windows 7
http://channel9.msdn.com/blogs/pdc2008/pc43
TODO look up PDC 2008 talk "new shell user interface"
- new animation and text engine
- ribbon control (didn't this have some additional license?)
- LVITEM.piColFmt
### Windows 8
### Windows 8.1
### Windows 10
## GTK+
TODO what ships with Ubuntu Quantal (12.10)?
### GTK+ 3.6
ships with: Ubuntu Raring (13.04)
- GtkEntry and GtkTextView have input purposes and input hints for external input methods but do not change input themselves
- according to Company, we connect to insert-text for that
- GtkLevelBar
- GtkMenuButton
- **GtkSearchEntry**
### GTK+ 3.8
ships with: Ubuntu Saucy (13.10)
Not many interesting new things to us here, unless you count widget-internal tickers and single-click instead of double-click to select list items (a la KDE)... and oh yeah, also widget opacity.
### GTK+ 3.10
ships with: **Ubuntu Trusty (14.04 LTS)**
<br>GLib version: 2.40
- tab character stops in GtkEntry
- GtkHeaderBar
- intended for titlebar overrides; GtkInfoBar is what I keep thinking GtkHeaderBar is
- **GtkListBox**
- GtkRevealer for smooth animations of disclosure triangles
- GtkSearchBar for custom search popups
- **GtkStack and GtkStackSwitcher**
- titlebar overrides (seems to be the hot new thing)
### GTK+ 3.12
ships with: Ubuntu Utopic (14.10)
<br>GLib version: 2.42
- GtkActionBar (basically like the bottom-of-the-window toolbars in Mac programs)
- gtk_get_locale_direction(), for internationalization
- more control over GtkHeaderBar
- **GtkPopover**
- GtkPopovers on GtkMenuButtons
- GtkStack signaling
- **gtk_tree_path_new_from_indicesv()** (for when we add Table if we have trees too)
### GTK+ 3.14
ships with: **Debian Jessie**, Ubuntu Vivid (15.04)
<br>GLib version: Debian: 2.42, Ubuntu: 2.44
- gestures
- better GtkListbox selection handling
- more style classes (TODO also prior?)
- delayed switch changes on GtkSwitch
### GTK+ 3.16
ships with: Ubuntu Wily (15.10)
<br>GLib version: 2.46
- gtk_clipboard_get_default() (???)
- **GtkGLArea**
- proper xalign and yalign for GtkLabel; should get rid of runtime deprecation warnings
- better control of GtkListBox model-based creation (probably not relevant but)
- GtkModelButton (for GActions; probably not relevant?)
- wide handles on GtkPaned
- GtkPopoverMenu
- IPP paper names in GtkPaperSize (TODO will this be important for printing?)
- multiple matches in GtkSearchEntry (TODO evaluate priority)
- **GtkStackSidebar**
- GTK_STYLE_CLASS_LABEL, GTK_STYLE_CLASS_MONOSPACE, GTK_STYLE_CLASS_STATUSBAR, GTK_STYLE_CLASS_TOUCH_SELECTION, GTK_STYLE_CLASS_WIDE (TODO figure out which of these are useful)
- GtkTextView: extend-selection
- GtkTextView: font fallbacks
### GTK+ 3.18
### GTK+ 3.20
## Cocoa
### Mac OS X 10.8
- Foundation ([full details](https://developer.apple.com/library/mac/releasenotes/Foundation/RN-FoundationOlderNotes/#//apple_ref/doc/uid/TP40008080-TRANSLATED_CHAPTER_965-TRANSLATED_DEST_999B))
- NSDateComponents supports leap months
- NSNumberFormatter and NSDateFormatter default to 10.4 behavior by default (need to explicitly do this on 10.7)
- **NSUserNotification and NSUserNotificationCenter for Growl-style notifications**
- better linguistic triggers for Spanish and Italian
- NSByteCountFormatter
- AppKit ([full details](https://developer.apple.com/library/mac/releasenotes/AppKit/RN-AppKitOlderNotes/#X10_8Notes))
- view-based NSTableView/NSOutlineView have expansion tooltips
- NSScrollView magnification
- Quick Look events; TODO see if they conflict with keyboard handling in Area
- NSPageController (maybe useful?)
- not useful for package UI, but may be useful for a new library (probably not by me): NSSharingService
- NSOpenPanel and NSSavePanel are now longer NSPanels or NSWindows in sandboxed applications; this may be an issue should anyone dare to enable sandboxing on a program that uses package ui
- NSTextAlternatives
- -[NSOpenGLContext setFullScreen] now ineffective
- +[NSColor underPageBackgroundColor]
### Mac OS X 10.9
- Foundation ([full details](https://developer.apple.com/library/mac/releasenotes/Foundation/RN-Foundation/))
- system-provided progress reporting/cancellation support
- NSURLComponents
- **NSCalendar, NSDateFormatter, and NSNumberFormatter are now thread-safe**
- various NSCalendar and NSDateComponents improvements
- AppKit ([full details](https://developer.apple.com/library/mac/releasenotes/AppKit/RN-AppKit/))
- sheet handling is now block-based, queued, and in NSWindow; the delegate-based NSApplication API will still exist, except without the queue
- similar changes to NSAlert
- **return value changes to NSAlert**
- window visibility APIs (occlusion)
- NSApplicationActivationPolicyAccessory
- fullscreen toolbar behavior changes
- status items for multiple menu bars
- better NSSharingService support
- a special accelerated scrolling mode, Responsive Scrolling; won't matter for us since I plan to support the scroll wheel and it won't
- NSScrollView live scrolling notifications
- NSScrollView floating (anchored/non-scrolling) subviews
- better multimonitor support
- better key-value observing for NSOpenPanel/NSSavePanel (might want to look this up to see if we can override some other juicy details... TODO)
- better accessory view key-view handling in NSOpenPanel/NSSavePanel
- NSAppearance
- **-[NSTableView moveRowAtIndex:toIndex:] bug regarding first responders fixed**
- view-specific RTL overrides
### Mac OS X 10.10
### Mac OS X 10.11
* **NSLayoutGuide**

View File

@ -1,9 +0,0 @@
Copyright (c) 2014 Pietro Gagliardi
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
(this is called the MIT License or Expat License; see http://www.opensource.org/licenses/MIT)

View File

@ -1,185 +0,0 @@
# libui: a portable GUI library for C
This README is being written.<br>
[![Build Status](https://travis-ci.org/andlabs/libui.svg)](https://travis-ci.org/andlabs/libui)
## Announcements
* **27 November 2016**
* Decided to split the table stuff into its own branch. It will be developed independently of everything else, along with a few other features.
* **2 November 2016**
* Added two new functions to replace the deleted `uiWindowPosition()` and friends: `uiAreaBeginUserWindowMove()` and `uiAreaBeginUserWindowResize()`. When used in a `uiAreaHandler.Mouse()` event handler, these let you initiate a user-driven mouse move or mouse resize of the window at any point in a uiArea.
* **31 October 2016**
* @krakjoe noticed that I accidentally used thread-unsafe code in uiQueueMain() on Unix. Fixed.
* **24 October 2016**
* `uiWindowSetContentSize()` on Unix no longer needs to call up the GTK+ main loop. As a result, bugs related to strange behavior using that function (and the now-deleted `uiWindowSetPosition()` and `uiWindowCenter()`) should go away. I'll need to go through the bugs to verify as much, though.
* **22 October 2016**
* Due to being unable to guarantee they will work (especially as we move toward capability-driven window systems like Wayland), or being unable to work without hacking that breaks other things, the following functions have been removed: `uiWindowPosition()`, `uiWindowSetPosition()`, `uiWindowCenter()`, and `uiWindowOnPositionChanged()`. Centering may come back at some point in the future, albeit in a possibly restricted form. A function to initiate a user move when a part of a uiArea is clicked will be provided soon.
* **21 October 2016**
* `uiDrawTextWeightUltraBold` is now spelled correctly. Thanks to @krakjoe.
* **18 June 2016**
* Help decide [the design of tables and trees in libui](https://github.com/andlabs/libui/issues/159); the implementation starts within the next few days, if not tomorrow!
* **17 June 2016**
* **CMake 3.1.0 is now required.** This is due to CMake's rapid development pace in the past few years adding things libui needs to build on as many systems as possible. If your OS is supported by libui but its repositories ship with an older version of CMake, you will need to find an updated one somewhere.
* Please help [plan out a better menu API](https://github.com/andlabs/libui/issues/152).
* **5 June 2016**
* **Alpha 3.1 is here.** This was a much-needed update to Alpha 3 that changes a few things:
* **The build system is now cmake.** cmake 2.8.11 or higher is needed.
* Static linking is now fully possible.
* MinGW linking is back, but static only.
*Old announcements can be found in the ANNOUNCE.md file.*
## Updates
*Note that today's entry (Eastern Time) may be updated later today.*
* **<codedate**
* Added `uiTable` TODO
* **17 June 2016**
* `uiMainSteps()` no longer takes any arguments and no longer needs to invoke a function to do the work. You still need to call it, but once you do, it will return immediately and you can then get right to your main loop.
* **CMake 3.1.0 is now required.** This is due to CMake's rapid development pace in the past few years adding things libui needs to build on as many systems as possible. If your OS is supported by libui but its repositories ship with an older version of CMake, you will need to find an updated one somewhere.
* Added `uiNewVerticalSeparator()` to complement `uiNewHorizontalSeparator()`.
* **16 June 2016**
* Added `uiWindowContentSize()`, `uiWindowSetContentSize()`, and `uiWindowOnContentSizeChanged()` methods for manipulating uiWindow content sizes. Note the use of "content size"; the size you work with does NOT include window decorations (titlebars, menus, etc.).
* Added `uiWindowFullscreen()` and `uiWindowSetFullscreen()` to allow making fullscreen uiWindows, taking advantage of OS facilities for fullscreen and without changing the screen resolution (!).
* Added `uiWindowBorderless()` and `uiWindowSetBorderless()` for allowing borderless uiWindows.
* Added `uiMainSteps()`. You call this instead of `uiMain()` if you want to run the main loop yourself. You pass in a function that will be called; within that function, you call `uiMainStep()` repeatedly until it returns 0, doing whatever you need to do in the meantime. (This was needed because just having `uiMainStep()` by itself only worked on some systems.)
* Added `uiProgressBarValue()` and allowed passing -1 to `uiProgressBarSetValue()` to make an indeterminate progress bar. Thanks to @emersion.
* **15 June 2016**
* Added `uiFormDelete()`; thanks to @emersion.
* Added `uiWindowPosition()`, `uiWindowSetPosition()`, `uiWindowCenter()`, and `uiWindowOnPositionChanged()`, methods for manipulating uiWindow position.
* **14 June 2016**
* uiDarwinControl now has a `ChildVisibilityChanged()` method and a corresponding `NotifyVisibilityChanged()` function that is called by the default show/hide handlers. This is used to make visibility changes work on OS X; uiBox, uiForm, and uiGrid all respect these now.
* The same has been done on the Windows side as well.
* Hiding and showing controls and padding calculations are now correct on Windows at long last.
* Hiding a control in a uiForm now hides its label on all platforms.
* **13 June 2016**
* `intmax_t` and `uintmax_t` are no longer used for libui API functions; now we use `int`. This should make things much easier for bindings. `int` should be at least 32 bits wide; this should be sufficient for all but the most extreme cases.
* **12 June 2016**
* Added `uiGrid`, a new container control that arranges controls in rows and columns, with stretchy ("expanding") rows, stretchy ("expanding") columns, cells that span rows and columns, and cells whose content is aligned in either direction rather than just filling. It's quite powerful, is it? =P
* **8 June 2016**
* Added `uiForm`, a new container control that arranges controls vertically, with properly aligned labels on each. Have fun!
* **6 June 2016**
* Added `uiRadioButtonsSelected()`, `uiRadioButtonsSetSelected()`, and `uiRadioButtonsOnSelected()` to control selection of a radio button and catch an event when such a thing happens.
* **5 June 2016**
* Added `uiNewPasswordEntry()`, which creates a new `uiEntry` suitable for entering passwords.
* Added `uiNewSearchEntry()`, which creates a new `uiEntry` suitable for searching. On some systems, the `OnChanged()` event will be slightly delayed and/or combined, to produce a more natural feel when searching.
*Old updates can be found in the Changelog.md file.*
## Runtime Requirements
* Windows: Windows Vista SP2 with Platform Update or newer
* Unix: GTK+ 3.10 or newer
* Mac OS X: OS X 10.8 or newer
## Build Requirements
* All platforms:
* CMake 3.1.0 or newer
* Windows: either
* Microsoft Visual Studio 2013 or newer (2013 is needed for `va_copy()`) — you can build either a static or a shared library
* MinGW-w64 (other flavors of MinGW may not work) — **you can only build a static library**; shared library support will be re-added once the following features come in:
* [Isolation awareness](https://msdn.microsoft.com/en-us/library/aa375197%28v=vs.85%29.aspx), which is how you get themed controls from a DLL without needing a manifest
* Unix: nothing else specific
* Mac OS X: nothing else specific, so long as you can build Cocoa programs
## Building
Out-of-tree builds typical of cmake are preferred:
```
$ # you must be in the top-level libui directory, otherwise this won't work
$ mkdir build
$ cd build
$ cmake ..
```
Pass `-DBUILD_SHARED_LIBS=OFF` to `cmake` to build a static library. The standard cmake build configurations are provided; if none is specified, `Debug` is used.
If you use a makefile generator with cmake, then
```
$ make
$ make tester # for the test program
$ make examples # for examples
```
and pass `VERBOSE=1` to see build commands. Build targets will be in the `build/out` folder.
Project file generators should work, but are untested by me.
On Windows, I use the `Unix Makefiles` generator and GNU make (built using the `build_w32.bat` script included in the source and run in the Visual Studio command line). In this state, if MinGW-w64 (either 32-bit or 64-bit) is not in your `%PATH%`, cmake will use MSVC by default; otherwise, cmake will use with whatever MinGW-w64 is in your path. `set PATH=%PATH%;c:\msys2\mingw(32/64)\bin` should be enough to temporarily change to a MinGW-w64 build for the current command line session only if you installed MinGW-w64 through [MSYS2](https://msys2.github.io/); no need to change global environment variables constantly.
## Installation
#### Arch Linux
Can be built from AUR: https://aur.archlinux.org/packages/libui-git/
## Documentation
Needs to be written. Consult `ui.h` and the examples for details for now.
## Language Bindings
libui was originally written as part of my [package ui for Go](https://github.com/andlabs/ui). Now that libui is separate, package ui has become a binding to libui. As such, package ui is the only official binding.
Other people have made bindings to other languages:
Language | Bindings
--- | ---
C#/.net | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding), [SharpUI](https://github.com/benpye/sharpui/)
CHICKEN Scheme | [wasamasa/libui](https://github.com/wasamasa/libui)
Crystal | [libui.cr](https://github.com/Fusion/libui.cr)
D | [DerelictLibui (flat API)](https://github.com/Extrawurst/DerelictLibui), [libuid (object-oriented)](https://github.com/mogud/libuid)
Euphoria | [libui-euphoria](https://github.com/ghaberek/libui-euphoria)
Harbour | [HBUI](https://github.com/RJopek/HBUI)
Haskell | [libui-haskell](https://github.com/ajnsit/libui-haskell), [beijaflor-io/haskell-libui (complete FFI bindings, extensions and higher-level API)](https://github.com/beijaflor-io/haskell-libui)
JavaScript | [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js)
Julia | [Libui.jl](https://github.com/joa-quim/Libui.jl)
Lua | [libuilua](https://github.com/zevv/libuilua), [libui-lua](https://github.com/mdombroski/libui-lua)
Nim | [ui](https://github.com/nim-lang/ui)
Node.js | [libui-node](https://github.com/parro-it/libui-node)
PHP | [ui](https://github.com/krakjoe/ui)
Python | [pylibui](https://github.com/joaoventura/pylibui)
Ruby | [libui-ruby](https://github.com/jamescook/libui-ruby)
Rust | [libui-rs](https://github.com/pcwalton/libui-rs)
Swift | [libui-swift](https://github.com/sclukey/libui-swift)
## Frequently Asked Questions
### Why does my program start in the background on OS X if I run from the command line?
OS X normally does not start program executables directly; instead, it uses [Launch Services](https://developer.apple.com/reference/coreservices/1658613-launch_services?language=objc) to coordinate the launching of the program between the various parts of the system and the loading of info from an .app bundle. One of these coordination tasks is responsible for bringing a newly launched app into the foreground. This is called "activation".
When you run a binary directly from the Terminal, however, you are running it directly, not through Launch Services. Therefore, the program starts in the background, because no one told it to activate! Now, it turns out [there is an API](https://developer.apple.com/reference/appkit/nsapplication/1428468-activateignoringotherapps) that we can use to force our app to be activated. But if we use it, then we'd be trampling over Launch Services, which already knows whether it should activate or not. Therefore, libui does not step over Launch Services, at the cost of requiring an extra user step if running directly from the command line.
See also [this](https://github.com/andlabs/libui/pull/20#issuecomment-211381971) and [this](http://stackoverflow.com/questions/25318524/what-exactly-should-i-pass-to-nsapp-activateignoringotherapps-to-get-my-appl).
## Screenshots
From examples/controlgallery:
![Windows](examples/controlgallery/windows.png)
![Unix](examples/controlgallery/unix.png)
![OS X](examples/controlgallery/darwin.png)

View File

@ -1,129 +0,0 @@
- make sure the last line of text layouts include leading
- documentation notes:
- static binaries do not link system libraries, meaning apps still depend on shared GTK+, etc.
- ui*Buttons are NOT compatible with uiButton functions
- more robust layout handling
- uiFormTie() for ensuring multiple uiForms have the same label area widths
- uiSizeGroup for size groups (GtkSizeGroup on GTK+, auto layout constraints on OS X; consider adding after 10.8 is gone)
- windows: should the initial hwndInsertAfter be HWND_BOTTOM for what we want?
- windows: document the rules for controls and containers
- windows: document the minimum size change propagation system
- provisions for controls that cannot be grown? especiailly for windows
- LC_VERSION_MIN_MACOSX has the 10.11 SDK; see if we can knock it down to 10.8 too; OS version is fine
- apply the OS version stuff to the test program and examples too
- what about micro versions (10.8.x)? force 10.8.0?
- go through ALL the objective-c objects we create and make sure we are using the proper ownership (alloc/init and new are owned by us, all class method constructors are autoreleased - thanks mikeash)
- on OS X, edit shortcuts like command-C working require that the menu entries be defined, or so it seems, even for NSAlert
- other platforms?
- make sure all OS X event handlers that use target-action set the action to NULL when the target is unset
- provide a way to get the currently selected uiTab page? set?
- make it so that the windows cntrols only register a resize if their new minimum size is larger than their current size to easen the effect of flicker
- it won't remove that outright, but it'll help
- add an option to the test program to run page7b as an independent test in its own window
- same for page7c
- http://blogs.msdn.com/b/oldnewthing/archive/2004/01/12/57833.aspx provide a DEF file on Windows
- all ports: update state when adding a control to a parent
- should uiWindowsSizing be computed against the window handle, not the parent?
- DPI awareness on windows
- http://stackoverflow.com/questions/4543087/applicationwillterminate-and-the-dock-but-wanting-to-cancel-this-action
ultimately:
- MAYBE readd lifetime handling/destruction blocking
- related? [12:25] <ZeroOne> And the blue outline on those buttons [ALL clicked buttons on Windows 7] won't go away
- I get this too
- not anymore
- SWP_NOCOPYBITS to avoid button redraw issues on Windows when not in tab, but only when making resize faster
- secondary side alignment control in uiBox
- Windows: don't abort if a cleanup function fails?
- 32-bit Mac OS X support (requires lots of code changes)
- change the build system to be more receptive to arch changes
notes to self
- explicitly document label position at top-left corner
- explicitly document that if number of radio buttons >= 1 there will always be a selection
- mark that uiControlShow() on a uiWindow() will bring to front and give keyboard focus because of OS X
- make sure ShowWindow() is sufficient for zorder on Windows
- document that you CAN use InsertAt functions to insert at the first invalid index, even if the array is empty
- note that uiTabInsertAt() does NOT change the current tab page (it may change its index if inserting before the current page)
- note that the default action for uiWindowOnClosing() is to return 0 (keep the window open)
- note that uiInitOptions should be initialized to zero
- explicitly document that uiCheckboxSetChecked() and uiEntrySetText() do not fire uiCheckboxOnToggled() and uiEntryOnChanged(), respectively
- note that if a menu is requested on systems with menubars on windows but no menus are defined, the result is a blank menubar, with whatever that means left up to the OS to decide
- note that handling of multiple consecutive separators in menus, leading separators in menus, and trailing separators in menus are all OS-defined
- note that uiDrawMatrixInvert() does not change the matrix if it fails
- note that the use of strings that are not strictly valid UTF-8 results in undefined behavior
- test RTL
- automate RTL
- now that stock items are deprecated, I have to maintain translations of the Cancel, Open, and Save buttons on GTK+ myself (thanks baedert in irc.gimp.net/#gtk+)
- either that or keep using stock items
- http://blogs.msdn.com/b/oldnewthing/archive/2014/02/26/10503148.aspx
- build optimizations
- use http://www.appveyor.com/ to do Windows build CI since people want CI
- consider just having the windows backend in C++
- consider having it all in C++
don't forget LONGTERMs as well
notes
- http://blogs.msdn.com/b/oldnewthing/archive/2004/03/29/101121.aspx on accelerators
- group and tab should act as if they have no child if the child is hidden
on windows
- a way to do recursive main loops
- how do we handle 0 returns from non-recursive uiMainStep() calls that aren't the main loop? (event handlers, for instance)
- should repeated calls to uiMainStep() after uiQuit() return 0 reliably? this will be needed for non-recursive loops
http://stackoverflow.com/questions/38338426/meaning-of-ampersand-in-rc-files/38338841?noredirect=1#comment64093084_38338841
label shortcut keys
- remove whining from source code
[01:41:47] <vrishab> Hi. does pango support "fgalpha". I see that foreground="112233xx" works ( alpha=xx ), but fgalpha is a no-op
[01:52:29] <vrishab> pango_attr_foreground_alpha_new (32767) seems to be called in either case, but only the "foreground" attr works
[01:56:09] lolek (lolek@ip-91-244-230-76.simant.pl) joined the channel
[01:57:48] <vrishab> ok. seems like "foreground" is mandatory attr, 1. "foreground-without-alpha" + "alpha" works 2. "foreground-with-alpha" works. 3. "alpha" alone doesn
[01:57:52] <vrishab> 't work
[01:58:29] <vrishab> Is there a way to just specify alpha on the current foreground color ?
[02:00:23] lolek (lolek@ip-91-244-230-76.simant.pl) left the channel
[02:07:41] mjog (mjog@uniwide-pat-pool-129-94-8-98.gw.unsw.edu.au) left IRC (Quit: mjog)
[02:08:10] seb128 (seb128@53542B83.cm-6-5a.dynamic.ziggo.nl) joined the channel
[02:12:37] <andlabs> huh
[02:12:41] <andlabs> what version of pango?
[02:13:05] <vrishab> the latest .
[02:15:00] <vrishab> 1.40.3
[02:20:46] <andlabs> I'll ahve to keep this in mind then, thanks
[02:20:59] <andlabs> if only there was a cairo-specific attribute for alpha...

View File

@ -1,90 +0,0 @@
struct uiWindow {
// constraints
void (*onPositionChanged)(uiWindow *, void *);
void *onPositionChangedData;
BOOL suppressPositionChanged;
// onContentSizeChanged
};
@interface windowDelegateClass : NSObject<NSWindowDelegate> {
// windowShouldClose:
- (void)windowDidMove:(NSNotification *)note;
// windowDidResize:
@end
@implementation windowDelegateClass
// - (BOOL)windowShouldClose:(id)sender
// TODO doesn't happen live
- (void)windowDidMove:(NSNotification *)note
{
uiWindow *w;
w = [self lookupWindow:((NSWindow *) [note object])];
if (!w->suppressPositionChanged)
(*(w->onPositionChanged))(w, w->onPositionChangedData);
}
// - (void)windowDidResize:(NSNotification *)note
// void uiWindowSetTitle(uiWindow *w, const char *title)
void uiWindowPosition(uiWindow *w, int *x, int *y)
{
NSScreen *screen;
NSRect r;
r = [w->window frame];
*x = r.origin.x;
// this is the right screen to use; thanks mikeash in irc.freenode.net/#macdev
// -mainScreen is useless for positioning (it's just the key window's screen)
// and we use -frame, not -visibleFrame, for dealing with absolute positions
screen = (NSScreen *) [[NSScreen screens] objectAtIndex:0];
*y = ([screen frame].size.height - r.origin.y) - r.size.height;
}
void uiWindowSetPosition(uiWindow *w, int x, int y)
{
// -[NSWindow setFrameTopLeftPoint:] is acting weird so...
NSRect r;
NSScreen *screen;
// this fires windowDidMove:
w->suppressPositionChanged = YES;
r = [w->window frame];
r.origin.x = x;
screen = (NSScreen *) [[NSScreen screens] objectAtIndex:0];
r.origin.y = [screen frame].size.height - (y + r.size.height);
[w->window setFrameOrigin:r.origin];
w->suppressPositionChanged = NO;
}
void uiWindowCenter(uiWindow *w)
{
w->suppressPositionChanged = YES;
[w->window center];
w->suppressPositionChanged = NO;
}
void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data)
{
w->onPositionChanged = f;
w->onPositionChangedData = data;
}
// void uiWindowContentSize(uiWindow *w, int *width, int *height)
// static int defaultOnClosing(uiWindow *w, void *data)
static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data)
{
// do nothing
}
uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar)
{
// uiWindowOnClosing(w, defaultOnClosing, NULL);
uiWindowOnPositionChanged(w, defaultOnPositionContentSizeChanged, NULL);
// uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL);
}

View File

@ -1,65 +0,0 @@
static uiSpinbox *x, *y;
static void moveX(uiSpinbox *s, void *data)
{
uiWindow *w = uiWindow(data);
int xp, yp;
uiWindowPosition(w, &xp, &yp);
xp = uiSpinboxValue(x);
uiWindowSetPosition(w, xp, yp);
}
static void moveY(uiSpinbox *s, void *data)
{
uiWindow *w = uiWindow(data);
int xp, yp;
uiWindowPosition(w, &xp, &yp);
yp = uiSpinboxValue(y);
uiWindowSetPosition(w, xp, yp);
}
static void updatepos(uiWindow *w)
{
int xp, yp;
uiWindowPosition(w, &xp, &yp);
uiSpinboxSetValue(x, xp);
uiSpinboxSetValue(y, yp);
}
static void center(uiButton *b, void *data)
{
uiWindow *w = uiWindow(data);
uiWindowCenter(w);
updatepos(w);
}
void onMove(uiWindow *w, void *data)
{
printf("move\n");
updatepos(w);
}
uiBox *makePage15(uiWindow *w)
{
hbox = newHorizontalBox();
// TODO if I make this 1 and not add anything else AND not call uiWindowOnPositionChanged(), on OS X the box won't be able to grow vertically
uiBoxAppend(page15, uiControl(hbox), 0);
uiBoxAppend(hbox, uiControl(uiNewLabel("Position")), 0);
x = uiNewSpinbox(INT_MIN, INT_MAX);
uiBoxAppend(hbox, uiControl(x), 1);
y = uiNewSpinbox(INT_MIN, INT_MAX);
uiBoxAppend(hbox, uiControl(y), 1);
button = uiNewButton("Center");
uiBoxAppend(hbox, uiControl(button), 0);
uiSpinboxOnChanged(x, moveX, w);
uiSpinboxOnChanged(y, moveY, w);
uiButtonOnClicked(button, center, w);
uiWindowOnPositionChanged(w, onMove, NULL);
updatepos(w);
}

View File

@ -1,6 +0,0 @@
// uiWindowSetTitle
_UI_EXTERN void uiWindowPosition(uiWindow *w, int *x, int *y);
_UI_EXTERN void uiWindowSetPosition(uiWindow *w, int x, int y);
_UI_EXTERN void uiWindowCenter(uiWindow *w);
_UI_EXTERN void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data);
// uiWindowContentSize

View File

@ -1,97 +0,0 @@
struct uiWindow {
// void *onClosingData;
void (*onPositionChanged)(uiWindow *, void *);
void *onPositionChangedData;
gboolean changingPosition;
// void (*onContentSizeChanged)(uiWindow *, void *);
};
// static gboolean onClosing(GtkWidget *win, GdkEvent *e, gpointer data)
static gboolean onConfigure(GtkWidget *win, GdkEvent *e, gpointer data)
{
uiWindow *w = uiWindow(data);
// there doesn't seem to be a way to determine if only moving or only resizing is happening :/
if (w->changingPosition)
w->changingPosition = FALSE;
else
(*(w->onPositionChanged))(w, w->onPositionChangedData);
// always continue handling
return FALSE;
}
// static void onSizeAllocate(GtkWidget *widget, GdkRectangle *allocation, gpointer data)
// static int defaultOnClosing(uiWindow *w, void *data)
static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data)
{
// do nothing
}
// static void uiWindowDestroy(uiControl *c)
// void uiWindowSetTitle(uiWindow *w, const char *title)
// TODO allow specifying either as NULL on all platforms
void uiWindowPosition(uiWindow *w, int *x, int *y)
{
gint rx, ry;
gtk_window_get_position(w->window, &rx, &ry);
*x = rx;
*y = ry;
}
void uiWindowSetPosition(uiWindow *w, int x, int y)
{
w->changingPosition = TRUE;
gtk_window_move(w->window, x, y);
// gtk_window_move() is asynchronous
// we need to wait for a configure-event
// thanks to hergertme in irc.gimp.net/#gtk+
while (w->changingPosition)
if (!uiMainStep(1))
break; // stop early if uiQuit() called
}
void uiWindowCenter(uiWindow *w)
{
gint x, y;
GtkAllocation winalloc;
GdkWindow *gdkwin;
GdkScreen *screen;
GdkRectangle workarea;
gtk_widget_get_allocation(w->widget, &winalloc);
gdkwin = gtk_widget_get_window(w->widget);
screen = gdk_window_get_screen(gdkwin);
gdk_screen_get_monitor_workarea(screen,
gdk_screen_get_monitor_at_window(screen, gdkwin),
&workarea);
x = (workarea.width - winalloc.width) / 2;
y = (workarea.height - winalloc.height) / 2;
// TODO move up slightly? see what Mutter or GNOME Shell or GNOME Terminal do(es)?
uiWindowSetPosition(w, x, y);
}
// TODO this and size changed get set during uiWindowDestroy
void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data)
{
w->onPositionChanged = f;
w->onPositionChangedData = data;
}
// void uiWindowContentSize(uiWindow *w, int *width, int *height)
uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar)
{
// g_signal_connect(w->widget, "delete-event", G_CALLBACK(onClosing), w);
g_signal_connect(w->widget, "configure-event", G_CALLBACK(onConfigure), w);
// g_signal_connect(w->childHolderWidget, "size-allocate", G_CALLBACK(onSizeAllocate), w);
// uiWindowOnClosing(w, defaultOnClosing, NULL);
uiWindowOnPositionChanged(w, defaultOnPositionContentSizeChanged, NULL);
// uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL);
}

View File

@ -1,86 +0,0 @@
struct uiWindow {
// BOOL hasMenubar;
void (*onPositionChanged)(uiWindow *, void *);
void *onPositionChangedData;
BOOL changingPosition; // to avoid triggering the above when programmatically doing this
// void (*onContentSizeChanged)(uiWindow *, void *);
};
static LRESULT CALLBACK windowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
case WM_WINDOWPOSCHANGED:
if ((wp->flags & SWP_NOMOVE) == 0)
if (!w->changingPosition)
(*(w->onPositionChanged))(w, w->onPositionChangedData);
// and continue anyway
// if ((wp->flags & SWP_NOSIZE) != 0)
}
// static int defaultOnClosing(uiWindow *w, void *data)
static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data)
{
// do nothing
}
// static std::map<uiWindow *, bool> windows;
// void uiWindowSetTitle(uiWindow *w, const char *title)
void uiWindowPosition(uiWindow *w, int *x, int *y)
{
RECT r;
uiWindowsEnsureGetWindowRect(w->hwnd, &r);
*x = r.left;
*y = r.top;
}
void uiWindowSetPosition(uiWindow *w, int x, int y)
{
w->changingPosition = TRUE;
if (SetWindowPos(w->hwnd, NULL, x, y, 0, 0, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER) == 0)
logLastError(L"error moving window");
w->changingPosition = FALSE;
}
// static void windowMonitorRect(HWND hwnd, RECT *r)
// TODO use the work rect instead?
void uiWindowCenter(uiWindow *w)
{
RECT wr, mr;
int x, y;
LONG wwid, mwid;
LONG wht, mht;
uiWindowsEnsureGetWindowRect(w->hwnd, &wr);
windowMonitorRect(w->hwnd, &mr);
wwid = wr.right - wr.left;
mwid = mr.right - mr.left;
x = (mwid - wwid) / 2;
wht = wr.bottom - wr.top;
mht = mr.bottom - mr.top;
y = (mht - wht) / 2;
// y is now evenly divided, however https://msdn.microsoft.com/en-us/library/windows/desktop/dn742502(v=vs.85).aspx says that 45% should go above and 55% should go below
// so just move 5% of the way up
// TODO should this be on the work area?
// TODO is this calculation correct?
y -= y / 20;
uiWindowSetPosition(w, x, y);
}
void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data)
{
w->onPositionChanged = f;
w->onPositionChangedData = data;
}
// void uiWindowContentSize(uiWindow *w, int *width, int *height)
uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar)
{
// uiWindowOnClosing(w, defaultOnClosing, NULL);
uiWindowOnPositionChanged(w, defaultOnPositionContentSizeChanged, NULL);
// uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL);
}

View File

@ -1,6 +0,0 @@
every rule in ui_darwin.h
SetParent must be followed by SetSuperview and SyncEnableState
TODO can child cache it?
adding a child must be followed by a call to SyncEnableState
SyncEnableState() must call ShouldStopSyncEnableState() first thing
Enable() and Disable() must call SyncEnableState() AFTER CHANGING WHAT Enabled() WILL RETURN

View File

@ -1,3 +0,0 @@
every rule in ui_unix.h
SetParent must be followed by SetContainer
TODO can child cache it?

View File

@ -1,25 +0,0 @@
2016-05-26 22:38:12.877 svtest[81790:544681] backgroundColor NSNamedColorSpace System controlColor
2016-05-26 22:38:12.877 svtest[81790:544681] drawsBackground 1
2016-05-26 22:38:12.877 svtest[81790:544681] borderType 2
2016-05-26 22:38:12.877 svtest[81790:544681] documentCursor (null)
2016-05-26 22:38:12.877 svtest[81790:544681] hasHorizontalScroller 1
2016-05-26 22:38:12.877 svtest[81790:544681] hasVerticalScroller 1
2016-05-26 22:38:12.877 svtest[81790:544681] autohidesScrollers 0
2016-05-26 22:38:12.877 svtest[81790:544681] hasHorizontalRuler 0
2016-05-26 22:38:12.878 svtest[81790:544681] hasVerticalRuler 0
2016-05-26 22:38:12.878 svtest[81790:544681] rulersVisible 0
2016-05-26 22:38:12.878 svtest[81790:544681] 10.10 autoAdjContentInsets 1
2016-05-26 22:38:12.878 svtest[81790:544681] scrollerKnobStyle 0
2016-05-26 22:38:12.878 svtest[81790:544681] scrollerStyle 1
2016-05-26 22:38:12.878 svtest[81790:544681] horizontalLineScroll 10
2016-05-26 22:38:12.878 svtest[81790:544681] verticalLineScroll 10
2016-05-26 22:38:12.886 svtest[81790:544681] horizontalPageScroll 10
2016-05-26 22:38:12.886 svtest[81790:544681] verticalPageScroll 10
2016-05-26 22:38:12.886 svtest[81790:544681] scrollsDynamically 1
2016-05-26 22:38:12.886 svtest[81790:544681] findBarPosition 1
2016-05-26 22:38:12.886 svtest[81790:544681] usesPredomAxisScroll 0
2016-05-26 22:38:12.886 svtest[81790:544681] horizontalElasticity 0
2016-05-26 22:38:12.886 svtest[81790:544681] verticalElasticity 0
2016-05-26 22:38:12.887 svtest[81790:544681] 10.8 allowsMagnification 0
2016-05-26 22:38:12.887 svtest[81790:544681] 10.8 maxMagnification 4
2016-05-26 22:38:12.887 svtest[81790:544681] 10.8 minMagnification 0.25

Some files were not shown because too many files have changed in this diff Show More