Update to spice2x-23-06-08
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
|
.vscode/*
|
||||||
|
|
||||||
# JetBrains IDEs
|
# JetBrains IDEs
|
||||||
.idea
|
.idea
|
||||||
.idea_modules
|
.idea_modules
|
||||||
@@ -13,3 +15,5 @@ build_docker.local.sh
|
|||||||
# output from build script
|
# output from build script
|
||||||
bin/*
|
bin/*
|
||||||
dist/*
|
dist/*
|
||||||
|
|
||||||
|
external/cv2pdb/*
|
||||||
@@ -10,6 +10,9 @@ set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
|||||||
# niceities for vscode
|
# niceities for vscode
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
|
||||||
|
|
||||||
|
# for RapidJSON
|
||||||
|
add_compile_definitions(RAPIDJSON_HAS_STDSTRING)
|
||||||
|
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
|
|
||||||
# disable intermediate manifest
|
# disable intermediate manifest
|
||||||
@@ -419,6 +422,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
hooks/debughook.cpp
|
hooks/debughook.cpp
|
||||||
hooks/devicehook.cpp
|
hooks/devicehook.cpp
|
||||||
hooks/graphics/graphics.cpp
|
hooks/graphics/graphics.cpp
|
||||||
|
hooks/graphics/graphics_windowed.cpp
|
||||||
hooks/graphics/backends/d3d9/d3d9_backend.cpp
|
hooks/graphics/backends/d3d9/d3d9_backend.cpp
|
||||||
hooks/graphics/backends/d3d9/d3d9_device.cpp
|
hooks/graphics/backends/d3d9/d3d9_device.cpp
|
||||||
hooks/graphics/backends/d3d9/d3d9_fake_swapchain.cpp
|
hooks/graphics/backends/d3d9/d3d9_fake_swapchain.cpp
|
||||||
@@ -471,7 +475,13 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
overlay/windows/control.cpp
|
overlay/windows/control.cpp
|
||||||
overlay/windows/eadev.cpp
|
overlay/windows/eadev.cpp
|
||||||
overlay/windows/fps.cpp
|
overlay/windows/fps.cpp
|
||||||
|
overlay/windows/generic_sub.cpp
|
||||||
|
overlay/windows/iidx_seg.cpp
|
||||||
overlay/windows/iidx_sub.cpp
|
overlay/windows/iidx_sub.cpp
|
||||||
|
overlay/windows/iopanel.cpp
|
||||||
|
overlay/windows/iopanel_ddr.cpp
|
||||||
|
overlay/windows/iopanel_gfdm.cpp
|
||||||
|
overlay/windows/iopanel_iidx.cpp
|
||||||
overlay/windows/keypad.cpp
|
overlay/windows/keypad.cpp
|
||||||
overlay/windows/kfcontrol.cpp
|
overlay/windows/kfcontrol.cpp
|
||||||
overlay/windows/log.cpp
|
overlay/windows/log.cpp
|
||||||
|
|||||||
+5
-4
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
#include "games/ddr/io.h"
|
#include "games/ddr/io.h"
|
||||||
|
#include "games/sdvx/sdvx.h"
|
||||||
#include "games/sdvx/io.h"
|
#include "games/sdvx/io.h"
|
||||||
#include "games/drs/io.h"
|
#include "games/drs/io.h"
|
||||||
#include "games/drs/drs.h"
|
#include "games/drs/drs.h"
|
||||||
@@ -92,18 +93,18 @@ static bool __cdecl ac_io_bi2a_update_control_status_buffer() {
|
|||||||
|
|
||||||
// volume left
|
// volume left
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Left))) {
|
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Left))) {
|
||||||
BI2A_VOLL = (BI2A_VOLL - 16) & 1023;
|
BI2A_VOLL = (BI2A_VOLL - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||||
}
|
}
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Right))) {
|
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Right))) {
|
||||||
BI2A_VOLL = (BI2A_VOLL + 16) & 1023;
|
BI2A_VOLL = (BI2A_VOLL + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||||
}
|
}
|
||||||
|
|
||||||
// volume right
|
// volume right
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Left))) {
|
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Left))) {
|
||||||
BI2A_VOLR = (BI2A_VOLR - 16) & 1023;
|
BI2A_VOLR = (BI2A_VOLR - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||||
}
|
}
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Right))) {
|
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Right))) {
|
||||||
BI2A_VOLR = (BI2A_VOLR + 16) & 1023;
|
BI2A_VOLR = (BI2A_VOLR + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||||
}
|
}
|
||||||
|
|
||||||
// update volumes
|
// update volumes
|
||||||
|
|||||||
+5
-4
@@ -4,6 +4,7 @@
|
|||||||
#include "games/bs/io.h"
|
#include "games/bs/io.h"
|
||||||
#include "games/nost/io.h"
|
#include "games/nost/io.h"
|
||||||
#include "games/scotto/io.h"
|
#include "games/scotto/io.h"
|
||||||
|
#include "games/sdvx/sdvx.h"
|
||||||
#include "games/sdvx/io.h"
|
#include "games/sdvx/io.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
@@ -342,18 +343,18 @@ static char __cdecl ac_io_kfca_update_control_status_buffer() {
|
|||||||
|
|
||||||
// volume left
|
// volume left
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Left))) {
|
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Left))) {
|
||||||
KFCA_VOLL = (KFCA_VOLL - 16) & 1023;
|
KFCA_VOLL = (KFCA_VOLL - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||||
}
|
}
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Right))) {
|
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Right))) {
|
||||||
KFCA_VOLL = (KFCA_VOLL + 16) & 1023;
|
KFCA_VOLL = (KFCA_VOLL + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||||
}
|
}
|
||||||
|
|
||||||
// volume right
|
// volume right
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Left))) {
|
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Left))) {
|
||||||
KFCA_VOLR = (KFCA_VOLR - 16) & 1023;
|
KFCA_VOLR = (KFCA_VOLR - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||||
}
|
}
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Right))) {
|
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Right))) {
|
||||||
KFCA_VOLR = (KFCA_VOLR + 16) & 1023;
|
KFCA_VOLR = (KFCA_VOLR + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||||
}
|
}
|
||||||
|
|
||||||
// update volumes
|
// update volumes
|
||||||
|
|||||||
+22
-14
@@ -8,6 +8,7 @@
|
|||||||
#include "launcher/launcher.h"
|
#include "launcher/launcher.h"
|
||||||
#include "touch/touch.h"
|
#include "touch/touch.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
#include "games/iidx/iidx.h"
|
||||||
|
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
using namespace rapidjson;
|
using namespace rapidjson;
|
||||||
@@ -16,6 +17,9 @@ using namespace rapidjson;
|
|||||||
namespace api::modules {
|
namespace api::modules {
|
||||||
|
|
||||||
Touch::Touch() : Module("touch") {
|
Touch::Touch() : Module("touch") {
|
||||||
|
is_sdvx = avs::game::is_model("KFC");
|
||||||
|
is_tdj_fhd = (avs::game::is_model("LDJ") && games::iidx::is_tdj_fhd());
|
||||||
|
|
||||||
functions["read"] = std::bind(&Touch::read, this, _1, _2);
|
functions["read"] = std::bind(&Touch::read, this, _1, _2);
|
||||||
functions["write"] = std::bind(&Touch::write, this, _1, _2);
|
functions["write"] = std::bind(&Touch::write, this, _1, _2);
|
||||||
functions["write_reset"] = std::bind(&Touch::write_reset, this, _1, _2);
|
functions["write_reset"] = std::bind(&Touch::write_reset, this, _1, _2);
|
||||||
@@ -78,20 +82,8 @@ namespace api::modules {
|
|||||||
auto touch_x = param[1].GetInt();
|
auto touch_x = param[1].GetInt();
|
||||||
auto touch_y = param[2].GetInt();
|
auto touch_y = param[2].GetInt();
|
||||||
|
|
||||||
// TODO: move this check somewhere better
|
apply_touch_errata(touch_x, touch_y);
|
||||||
if (avs::game::is_model("KFC")) {
|
|
||||||
|
|
||||||
// mirror
|
|
||||||
touch_points.emplace_back(TouchPoint {
|
|
||||||
.id = touch_id,
|
|
||||||
.x = 1080 - touch_y,
|
|
||||||
.y = touch_x,
|
|
||||||
.mouse = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// normal
|
|
||||||
touch_points.emplace_back(TouchPoint {
|
touch_points.emplace_back(TouchPoint {
|
||||||
.id = touch_id,
|
.id = touch_id,
|
||||||
.x = touch_x,
|
.x = touch_x,
|
||||||
@@ -99,7 +91,6 @@ namespace api::modules {
|
|||||||
.mouse = false,
|
.mouse = false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// apply touch points
|
// apply touch points
|
||||||
touch_write_points(&touch_points);
|
touch_write_points(&touch_points);
|
||||||
@@ -128,4 +119,21 @@ namespace api::modules {
|
|||||||
// remove all IDs
|
// remove all IDs
|
||||||
touch_remove_points(&touch_point_ids);
|
touch_remove_points(&touch_point_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Touch::apply_touch_errata(int &x, int &y) {
|
||||||
|
int x_raw = x;
|
||||||
|
int y_raw = y;
|
||||||
|
|
||||||
|
if (is_tdj_fhd) {
|
||||||
|
// deal with TDJ FHD resolution mismatch (upgrade 720p to 1080p)
|
||||||
|
// we don't know what screen is being shown on the companion and the API doesn't specify
|
||||||
|
// the target of the touch events so just assume it's the sub screen
|
||||||
|
x = x_raw * 1920 / 1280;
|
||||||
|
y = y_raw * 1080 / 720;
|
||||||
|
} else if (is_sdvx) {
|
||||||
|
// for exceed gear, they are both 1080p screens, but need to apply transformation
|
||||||
|
x = 1080 - y_raw;
|
||||||
|
y = x_raw;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,10 +11,14 @@ namespace api::modules {
|
|||||||
Touch();
|
Touch();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool is_sdvx;
|
||||||
|
bool is_tdj_fhd;
|
||||||
|
|
||||||
// function definitions
|
// function definitions
|
||||||
void read(Request &req, Response &res);
|
void read(Request &req, Response &res);
|
||||||
void write(Request &req, Response &res);
|
void write(Request &req, Response &res);
|
||||||
void write_reset(Request &req, Response &res);
|
void write_reset(Request &req, Response &res);
|
||||||
|
void apply_touch_errata(int &x, int &y);
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,3 +6,4 @@
|
|||||||
#define IDR_LICENSES 131
|
#define IDR_LICENSES 131
|
||||||
#define IDR_PATCHES 132
|
#define IDR_PATCHES 132
|
||||||
#define IDR_README 133
|
#define IDR_README 133
|
||||||
|
#define IDR_DSEGFONT 134
|
||||||
|
|||||||
+9
-6
@@ -29,14 +29,17 @@ BUILDDIR_32_DEBUG="./cmake-build-debug-32"
|
|||||||
BUILDDIR_64_RELEASE="./cmake-build-release-64"
|
BUILDDIR_64_RELEASE="./cmake-build-release-64"
|
||||||
BUILDDIR_64_DEBUG="./cmake-build-debug-64"
|
BUILDDIR_64_DEBUG="./cmake-build-debug-64"
|
||||||
DEBUG=0
|
DEBUG=0
|
||||||
OUTDIR="./bin/spicetools"
|
OUTDIR="./bin/spice2x"
|
||||||
UPX_ENABLE=1
|
|
||||||
|
# disabled UPX since it tends to falsely trigger malware detection
|
||||||
|
UPX_ENABLE=0
|
||||||
UPX_FLAGS="-q --best --lzma --compress-exports=0"
|
UPX_FLAGS="-q --best --lzma --compress-exports=0"
|
||||||
|
|
||||||
CLEAN_BUILD=1
|
CLEAN_BUILD=1
|
||||||
INCLUDE_SRC=1
|
INCLUDE_SRC=1
|
||||||
DIST_ENABLE=1
|
DIST_ENABLE=1
|
||||||
DIST_FOLDER="./dist"
|
DIST_FOLDER="./dist"
|
||||||
DIST_NAME="spicetools-$(date +%y)-$(date +%m)-$(date +%d).zip"
|
DIST_NAME="spice2x-$(date +%y)-$(date +%m)-$(date +%d).zip"
|
||||||
DIST_COMMENT=${DIST_NAME}$'\n'"$GIT_BRANCH - $GIT_HEAD"$'\nThank you for playing.'
|
DIST_COMMENT=${DIST_NAME}$'\n'"$GIT_BRANCH - $GIT_HEAD"$'\nThank you for playing.'
|
||||||
TARGETS_32="spicetools_stubs_kbt spicetools_stubs_kld spicetools_cfg spicetools_spice"
|
TARGETS_32="spicetools_stubs_kbt spicetools_stubs_kld spicetools_cfg spicetools_spice"
|
||||||
TARGETS_64="spicetools_stubs_kbt64 spicetools_stubs_kld64 spicetools_spice64"
|
TARGETS_64="spicetools_stubs_kbt64 spicetools_stubs_kld64 spicetools_spice64"
|
||||||
@@ -101,7 +104,7 @@ echo "Compilation process done :)"
|
|||||||
echo "==========================="
|
echo "==========================="
|
||||||
|
|
||||||
# generate PDBs
|
# generate PDBs
|
||||||
if ((DEBUG > 0))
|
if false # ((DEBUG > 0))
|
||||||
then
|
then
|
||||||
echo "Generating PDBs..."
|
echo "Generating PDBs..."
|
||||||
|
|
||||||
@@ -150,7 +153,7 @@ rm -rf ${OUTDIR}
|
|||||||
mkdir -p ${OUTDIR}
|
mkdir -p ${OUTDIR}
|
||||||
#mkdir -p ${OUTDIR}/stubs/32
|
#mkdir -p ${OUTDIR}/stubs/32
|
||||||
#mkdir -p ${OUTDIR}/stubs/64
|
#mkdir -p ${OUTDIR}/stubs/64
|
||||||
if ((DEBUG > 0))
|
if false # ((DEBUG > 0))
|
||||||
then
|
then
|
||||||
# debug files
|
# debug files
|
||||||
cp ${BUILDDIR_32}/spicetools/spicecfg-pdb.exe ${OUTDIR} 2>/dev/null
|
cp ${BUILDDIR_32}/spicetools/spicecfg-pdb.exe ${OUTDIR} 2>/dev/null
|
||||||
@@ -193,7 +196,7 @@ mkdir -p ${OUTDIR}/src
|
|||||||
if ((INCLUDE_SRC > 0))
|
if ((INCLUDE_SRC > 0))
|
||||||
then
|
then
|
||||||
echo "Generating source file archive..."
|
echo "Generating source file archive..."
|
||||||
git archive --format tar --prefix=spicetools/ HEAD | gzip -9 > ${OUTDIR}/src/spicetools-${GIT_BRANCH}.tar.gz 2>/dev/null || \
|
git archive --format tar.gz --prefix=spice2x/ HEAD > ${OUTDIR}/src/spice2x-${GIT_BRANCH}.tar.gz 2>/dev/null || \
|
||||||
echo "WARNING: Couldn't get git to create the archive. Is this a git repository?"
|
echo "WARNING: Couldn't get git to create the archive. Is this a git repository?"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
docker build --pull external/docker -t spicetools/deps
|
||||||
|
docker build . -t spicetools/spice --no-cache
|
||||||
|
docker run --rm -it -v %cd%/dist:/src/dist -v %cd%/bin:/src/bin spicetools/spice
|
||||||
|
@REM to generate PDBs, set DEBUG to 1 in build_all.sh, place cv2pdb in external\cv2pdb, and run below
|
||||||
|
@REM external\cv2pdb\cv2pdb.exe bin\spice2x\spicecfg.exe bin\spice2x\spicecfg-pdb.exe bin\spice2x\spicecfg-pdb.pdb
|
||||||
|
@REM external\cv2pdb\cv2pdb.exe bin\spice2x\spice.exe bin\spice2x\spice-pdb.exe bin\spice2x\spice-pdb.pdb
|
||||||
|
@REM external\cv2pdb\cv2pdb.exe bin\spice2x\spice64.exe bin\spice2x\spice64-pdb.exe bin\spice2x\spice64-pdb.pdb
|
||||||
@@ -13,5 +13,6 @@ IDR_CHANGELOG RCDATA "../changelog.txt"
|
|||||||
IDR_LICENSES RCDATA "../licenses.txt"
|
IDR_LICENSES RCDATA "../licenses.txt"
|
||||||
IDR_PATCHES RCDATA "../build/patches.json"
|
IDR_PATCHES RCDATA "../build/patches.json"
|
||||||
IDR_README RCDATA "../readme.txt"
|
IDR_README RCDATA "../readme.txt"
|
||||||
|
IDR_DSEGFONT RCDATA "../external/dseg/DSEG14Classic-Italic.ttf"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ cfg::ConfiguratorWindow::ConfiguratorWindow() {
|
|||||||
|
|
||||||
// determine window title
|
// determine window title
|
||||||
if (cfg::CONFIGURATOR_TYPE == cfg::ConfigType::Config) {
|
if (cfg::CONFIGURATOR_TYPE == cfg::ConfigType::Config) {
|
||||||
WINDOW_TITLE = "SpiceTools Config (" + to_string(VERSION_STRING_CFG) + ")";
|
WINDOW_TITLE = "spice2x config - a fork of SpiceTools (" + to_string(VERSION_STRING_CFG) + ")";
|
||||||
WINDOW_SIZE_X = 800;
|
WINDOW_SIZE_X = 800;
|
||||||
WINDOW_SIZE_Y = 600;
|
WINDOW_SIZE_Y = 600;
|
||||||
} else if (cfg::CONFIGURATOR_TYPE == cfg::ConfigType::KFControl) {
|
} else if (cfg::CONFIGURATOR_TYPE == cfg::ConfigType::KFControl) {
|
||||||
@@ -49,6 +49,10 @@ cfg::ConfiguratorWindow::ConfiguratorWindow() {
|
|||||||
NULL,
|
NULL,
|
||||||
GetModuleHandle(NULL),
|
GetModuleHandle(NULL),
|
||||||
(LPVOID) this);
|
(LPVOID) this);
|
||||||
|
|
||||||
|
if (this->hWnd) {
|
||||||
|
overlay::USE_WM_CHAR_FOR_IMGUI_CHAR_INPUT = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg::ConfiguratorWindow::~ConfiguratorWindow() {
|
cfg::ConfiguratorWindow::~ConfiguratorWindow() {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
name="spicecfg.exe"
|
name="spicecfg.exe"
|
||||||
type="win32"
|
type="win32"
|
||||||
/>
|
/>
|
||||||
<description>SpiceTools Config</description>
|
<description>spice2x config</description>
|
||||||
<dependency>
|
<dependency>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity
|
<assemblyIdentity
|
||||||
|
|||||||
+18
-3
@@ -68,17 +68,32 @@ std::vector<std::string> Option::values_text() const {
|
|||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Option::value_int() const {
|
uint32_t Option::value_uint32() const {
|
||||||
if (this->definition.type != OptionType::Integer && this->definition.type != OptionType::Enum) {
|
if (this->definition.type != OptionType::Integer && this->definition.type != OptionType::Enum) {
|
||||||
log_fatal("option", "value_int() called on {}/{}", this->definition.title, this->definition.type);
|
log_fatal("option", "invalid call: value_uint32() called on {}/{}", this->definition.title, this->definition.type);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
char *p;
|
char *p;
|
||||||
auto res = strtol(this->value.c_str(), &p, 10);
|
auto res = strtol(this->value.c_str(), &p, 10);
|
||||||
if (*p) {
|
if (*p) {
|
||||||
log_fatal("option", "tried to convert {} to integer ({})", this->value, this->definition.title);
|
log_fatal("option", "failed to convert {} to unsigned integer (option: {})", this->value, this->definition.title);
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t Option::value_hex64() const {
|
||||||
|
if (this->definition.type != OptionType::Hex) {
|
||||||
|
log_fatal("option", "invalid call: value_hex() called on {}/{}", this->definition.title, this->definition.type);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t affinity = 0;
|
||||||
|
try {
|
||||||
|
affinity = std::stoull(this->value.c_str(), nullptr, 16);
|
||||||
|
} catch (const std::exception &ex) {
|
||||||
|
log_fatal("option", "failed to parse {} as hexadecimal (option: {})", this->value, this->definition.title);
|
||||||
|
}
|
||||||
|
return affinity;
|
||||||
|
}
|
||||||
|
|||||||
+3
-1
@@ -9,6 +9,7 @@ enum class OptionType {
|
|||||||
Text,
|
Text,
|
||||||
Integer,
|
Integer,
|
||||||
Enum,
|
Enum,
|
||||||
|
Hex,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct OptionDefinition {
|
struct OptionDefinition {
|
||||||
@@ -54,5 +55,6 @@ public:
|
|||||||
const std::string &value_text() const;
|
const std::string &value_text() const;
|
||||||
std::vector<std::string> values() const;
|
std::vector<std::string> values() const;
|
||||||
std::vector<std::string> values_text() const;
|
std::vector<std::string> values_text() const;
|
||||||
int value_int() const;
|
uint32_t value_uint32() const;
|
||||||
|
uint64_t value_hex64() const;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,3 +4,4 @@
|
|||||||
#define IDR_LICENSES 131
|
#define IDR_LICENSES 131
|
||||||
#define IDR_PATCHES 132
|
#define IDR_PATCHES 132
|
||||||
#define IDR_README 133
|
#define IDR_README 133
|
||||||
|
#define IDR_DSEGFONT 134
|
||||||
|
|||||||
+155
-64
@@ -1,11 +1,12 @@
|
|||||||
#include "screen_resize.h"
|
#include "screen_resize.h"
|
||||||
|
|
||||||
#include "external/rapidjson/document.h"
|
#include "external/rapidjson/document.h"
|
||||||
#include "external/rapidjson/writer.h"
|
#include "external/rapidjson/pointer.h"
|
||||||
|
#include "external/rapidjson/prettywriter.h"
|
||||||
|
#include "misc/eamuse.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
#include "util/fileutils.h"
|
#include "util/fileutils.h"
|
||||||
|
#include "hooks/graphics/graphics.h"
|
||||||
using namespace rapidjson;
|
|
||||||
|
|
||||||
namespace cfg {
|
namespace cfg {
|
||||||
|
|
||||||
@@ -26,90 +27,180 @@ namespace cfg {
|
|||||||
log_info("ScreenResize", "loading config");
|
log_info("ScreenResize", "loading config");
|
||||||
|
|
||||||
std::string config = fileutils::text_read(this->config_path);
|
std::string config = fileutils::text_read(this->config_path);
|
||||||
if (!config.empty()) {
|
if (config.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// parse document
|
// parse document
|
||||||
Document doc;
|
rapidjson::Document doc;
|
||||||
doc.Parse(config.c_str());
|
doc.Parse(config.c_str());
|
||||||
|
|
||||||
// check parse error
|
// check parse error
|
||||||
auto error = doc.GetParseError();
|
auto error = doc.GetParseError();
|
||||||
if (error) {
|
if (error) {
|
||||||
log_warning("ScreenResize", "config parse error: {}", error);
|
log_warning("ScreenResize", "config parse error: {}", error);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify root is a dict
|
// verify root is a dict
|
||||||
if (doc.IsObject()) {
|
if (!doc.IsObject()) {
|
||||||
bool valid = true;
|
|
||||||
auto offset_x = doc.FindMember("offset_x");
|
|
||||||
if (offset_x == doc.MemberEnd() || !offset_x->value.IsInt()) {
|
|
||||||
log_warning("ScreenResize", "offset_x not found");
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
auto offset_y = doc.FindMember("offset_y");
|
|
||||||
if (offset_y == doc.MemberEnd() || !offset_y->value.IsInt()) {
|
|
||||||
log_warning("ScreenResize", "offset_y not found");
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
auto scale_x = doc.FindMember("scale_x");
|
|
||||||
if (scale_x == doc.MemberEnd() || !scale_x->value.IsDouble()) {
|
|
||||||
log_warning("ScreenResize", "scale_x not found");
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
auto scale_y = doc.FindMember("scale_y");
|
|
||||||
if (scale_y == doc.MemberEnd() || !scale_y->value.IsDouble()) {
|
|
||||||
log_warning("ScreenResize", "scale_y not found");
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
auto enable_screen_resize = doc.FindMember("enable_screen_resize");
|
|
||||||
if (enable_screen_resize == doc.MemberEnd() || !enable_screen_resize->value.IsBool()) {
|
|
||||||
log_warning("ScreenResize", "enable_screen_resize not found");
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
auto enable_linear_filter = doc.FindMember("enable_linear_filter");
|
|
||||||
if (enable_linear_filter == doc.MemberEnd() || !enable_linear_filter->value.IsBool()) {
|
|
||||||
log_warning("ScreenResize", "enable_linear_filter not found");
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
auto keep_aspect_ratio = doc.FindMember("keep_aspect_ratio");
|
|
||||||
if (keep_aspect_ratio == doc.MemberEnd() || !keep_aspect_ratio->value.IsBool()) {
|
|
||||||
log_warning("ScreenResize", "keep_aspect_ratio not found");
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
if (valid) {
|
|
||||||
this->offset_x = offset_x->value.GetInt();
|
|
||||||
this->offset_y = offset_y->value.GetInt();
|
|
||||||
this->scale_x = (float)scale_x->value.GetDouble();
|
|
||||||
this->scale_y = (float)scale_y->value.GetDouble();
|
|
||||||
this->enable_screen_resize = enable_screen_resize->value.GetBool();
|
|
||||||
this->enable_linear_filter = enable_linear_filter->value.GetBool();
|
|
||||||
this->keep_aspect_ratio = keep_aspect_ratio->value.GetBool();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log_warning("ScreenResize", "config not found");
|
log_warning("ScreenResize", "config not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool use_game_setting = false;
|
||||||
|
|
||||||
|
std::string root("/");
|
||||||
|
// try to find game-specific setting, if one exists
|
||||||
|
{
|
||||||
|
const auto game = rapidjson::Pointer("/sp2x_games/" + eamuse_get_game()).Get(doc);
|
||||||
|
if (game && game->IsObject()) {
|
||||||
|
use_game_setting = true;
|
||||||
|
root = "/sp2x_games/" + eamuse_get_game() + "/";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log_misc(
|
||||||
|
"ScreenResize",
|
||||||
|
"Loading fullscreen image settings. Game = {}, is_global = {}, JSON path: {}",
|
||||||
|
eamuse_get_game(),
|
||||||
|
use_game_setting,
|
||||||
|
root);
|
||||||
|
load_int_value(doc, root + "offset_x", this->offset_x);
|
||||||
|
load_int_value(doc, root + "offset_y", this->offset_y);
|
||||||
|
load_float_value(doc, root + "scale_x", this->scale_x);
|
||||||
|
load_float_value(doc, root + "scale_y", this->scale_y);
|
||||||
|
load_bool_value(doc, root + "enable_screen_resize", this->enable_screen_resize);
|
||||||
|
load_bool_value(doc, root + "enable_linear_filter", this->enable_linear_filter);
|
||||||
|
load_bool_value(doc, root + "keep_aspect_ratio", this->keep_aspect_ratio);
|
||||||
|
load_bool_value(doc, root + "centered", this->centered);
|
||||||
|
|
||||||
|
// windowed settings are always under game settings
|
||||||
|
root = "/sp2x_games/" + eamuse_get_game() + "/";
|
||||||
|
log_misc(
|
||||||
|
"ScreenResize",
|
||||||
|
"Loading window settings. Game = {}, JSON path: {}",
|
||||||
|
eamuse_get_game(),
|
||||||
|
root);
|
||||||
|
load_bool_value(doc, root + "w_always_on_top", this->window_always_on_top);
|
||||||
|
load_bool_value(doc, root + "w_enable_resize", this->enable_window_resize);
|
||||||
|
load_bool_value(doc, root + "w_keep_aspect_ratio", this->client_keep_aspect_ratio);
|
||||||
|
load_int_value(doc, root + "w_border_type", this->window_decoration);
|
||||||
|
load_uint32_value(doc, root + "w_width", this->client_width);
|
||||||
|
load_uint32_value(doc, root + "w_height", this->client_height);
|
||||||
|
load_int_value(doc, root + "w_offset_x", this->window_offset_x);
|
||||||
|
load_int_value(doc, root + "w_offset_y", this->window_offset_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScreenResize::load_bool_value(rapidjson::Document& doc, std::string path, bool& value) {
|
||||||
|
const auto v = rapidjson::Pointer(path).Get(doc);
|
||||||
|
if (!v) {
|
||||||
|
log_warning("ScreenResize", "{} not found", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!v->IsBool()) {
|
||||||
|
log_warning("ScreenResize", "{} is invalid type", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value = v->GetBool();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScreenResize::load_int_value(rapidjson::Document& doc, std::string path, int& value) {
|
||||||
|
const auto v = rapidjson::Pointer(path).Get(doc);
|
||||||
|
if (!v) {
|
||||||
|
log_warning("ScreenResize", "{} not found", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!v->IsInt()) {
|
||||||
|
log_warning("ScreenResize", "{} is invalid type", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value = v->GetInt();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScreenResize::load_uint32_value(rapidjson::Document& doc, std::string path, uint32_t& value) {
|
||||||
|
const auto v = rapidjson::Pointer(path).Get(doc);
|
||||||
|
if (!v) {
|
||||||
|
log_warning("ScreenResize", "{} not found", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!v->IsUint()) {
|
||||||
|
log_warning("ScreenResize", "{} is invalid type", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value = v->GetUint();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScreenResize::load_float_value(rapidjson::Document& doc, std::string path, float& value) {
|
||||||
|
const auto v = rapidjson::Pointer(path).Get(doc);
|
||||||
|
if (!v) {
|
||||||
|
log_warning("ScreenResize", "{} not found", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (v->IsInt()) {
|
||||||
|
value = v->GetInt();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (v->IsDouble()) {
|
||||||
|
value = v->GetDouble();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (v->IsFloat()) {
|
||||||
|
value = v->GetFloat();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScreenResize::config_save() {
|
void ScreenResize::config_save() {
|
||||||
log_info("ScreenResize", "saving config");
|
log_info("ScreenResize", "saving config");
|
||||||
|
|
||||||
// create document
|
rapidjson::Document doc;
|
||||||
Document doc;
|
std::string config = fileutils::text_read(this->config_path);
|
||||||
|
if (!config.empty()) {
|
||||||
|
doc.Parse(config.c_str());
|
||||||
|
log_misc("ScreenResize", "existing config file found");
|
||||||
|
}
|
||||||
|
if (!doc.IsObject()) {
|
||||||
|
log_misc("ScreenResize", "clearing out config file");
|
||||||
doc.SetObject();
|
doc.SetObject();
|
||||||
auto &alloc = doc.GetAllocator();
|
}
|
||||||
|
|
||||||
doc.AddMember("offset_x", this->offset_x, alloc);
|
// always save under per-game settings
|
||||||
doc.AddMember("offset_y", this->offset_y, alloc);
|
std::string root("/sp2x_games/" + eamuse_get_game() + "/");
|
||||||
doc.AddMember("scale_x", this->scale_x, alloc);
|
|
||||||
doc.AddMember("scale_y", this->scale_y, alloc);
|
log_misc(
|
||||||
doc.AddMember("enable_screen_resize", this->enable_screen_resize, alloc);
|
"ScreenResize",
|
||||||
doc.AddMember("enable_linear_filter", this->enable_linear_filter, alloc);
|
"Game = {}, JSON path = {}",
|
||||||
doc.AddMember("keep_aspect_ratio", this->keep_aspect_ratio, alloc);
|
eamuse_get_game(),
|
||||||
|
root);
|
||||||
|
|
||||||
|
// full screen image settings
|
||||||
|
rapidjson::Pointer(root + "offset_x").Set(doc, this->offset_x);
|
||||||
|
rapidjson::Pointer(root + "offset_y").Set(doc, this->offset_y);
|
||||||
|
rapidjson::Pointer(root + "scale_x").Set(doc, this->scale_x);
|
||||||
|
rapidjson::Pointer(root + "scale_y").Set(doc, this->scale_y);
|
||||||
|
rapidjson::Pointer(root + "enable_screen_resize").Set(doc, this->enable_screen_resize);
|
||||||
|
rapidjson::Pointer(root + "enable_linear_filter").Set(doc, this->enable_linear_filter);
|
||||||
|
rapidjson::Pointer(root + "keep_aspect_ratio").Set(doc, this->keep_aspect_ratio);
|
||||||
|
rapidjson::Pointer(root + "centered").Set(doc, this->centered);
|
||||||
|
|
||||||
|
// windowed mode settings
|
||||||
|
rapidjson::Pointer(root + "w_always_on_top").Set(doc, this->window_always_on_top);
|
||||||
|
rapidjson::Pointer(root + "w_enable_resize").Set(doc, this->enable_window_resize);
|
||||||
|
rapidjson::Pointer(root + "w_keep_aspect_ratio").Set(doc, this->client_keep_aspect_ratio);
|
||||||
|
rapidjson::Pointer(root + "w_border_type").Set(doc, this->window_decoration);
|
||||||
|
rapidjson::Pointer(root + "w_width").Set(doc, this->client_width);
|
||||||
|
rapidjson::Pointer(root + "w_height").Set(doc, this->client_height);
|
||||||
|
rapidjson::Pointer(root + "w_offset_x").Set(doc, this->window_offset_x);
|
||||||
|
rapidjson::Pointer(root + "w_offset_y").Set(doc, this->window_offset_y);
|
||||||
|
|
||||||
// build JSON
|
// build JSON
|
||||||
StringBuffer buffer;
|
rapidjson::StringBuffer buffer;
|
||||||
Writer<StringBuffer> writer(buffer);
|
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
|
||||||
doc.Accept(writer);
|
doc.Accept(writer);
|
||||||
|
|
||||||
// save to file
|
// save to file
|
||||||
|
|||||||
+37
-2
@@ -2,25 +2,60 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include "external/rapidjson/document.h"
|
||||||
|
|
||||||
namespace cfg {
|
namespace cfg {
|
||||||
|
|
||||||
|
enum WindowDecorationMode {
|
||||||
|
Default = 0,
|
||||||
|
Borderless = 1,
|
||||||
|
ResizableFrame = 2
|
||||||
|
};
|
||||||
|
|
||||||
class ScreenResize {
|
class ScreenResize {
|
||||||
private:
|
private:
|
||||||
std::string config_path;
|
std::string config_path;
|
||||||
// bool config_dirty = false;
|
// bool config_dirty = false;
|
||||||
|
|
||||||
|
bool load_bool_value(rapidjson::Document& doc, std::string path, bool& value);
|
||||||
|
bool load_int_value(rapidjson::Document& doc, std::string path, int& value);
|
||||||
|
bool load_uint32_value(rapidjson::Document& doc, std::string path, uint32_t& value);
|
||||||
|
bool load_float_value(rapidjson::Document& doc, std::string path, float& value);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ScreenResize();
|
ScreenResize();
|
||||||
~ScreenResize();
|
~ScreenResize();
|
||||||
|
|
||||||
|
// full screen (directx) image settings
|
||||||
int offset_x = 0;
|
int offset_x = 0;
|
||||||
int offset_y = 0;
|
int offset_y = 0;
|
||||||
float scale_x = 1.0;
|
float scale_x = 1.0;
|
||||||
float scale_y = 1.0;
|
float scale_y = 1.0;
|
||||||
bool enable_screen_resize = false;
|
bool enable_screen_resize = false;
|
||||||
bool enable_linear_filter = false;
|
bool enable_linear_filter = true;
|
||||||
bool keep_aspect_ratio = false;
|
bool keep_aspect_ratio = true;
|
||||||
|
bool centered = true;
|
||||||
|
|
||||||
|
// windowed mode sizing
|
||||||
|
// Windows terminology:
|
||||||
|
// window = rectangle including the frame
|
||||||
|
// client = just the content area without frames.
|
||||||
|
bool window_always_on_top = false;
|
||||||
|
bool client_keep_aspect_ratio = true;
|
||||||
|
bool enable_window_resize = false;
|
||||||
|
int window_decoration = 0; // enum type WindowDecorationMode
|
||||||
|
uint32_t client_width = 0;
|
||||||
|
uint32_t client_height = 0;
|
||||||
|
int32_t window_offset_x = 0;
|
||||||
|
int32_t window_offset_y = 0;
|
||||||
|
|
||||||
|
// these are not saved by config, but used by window management
|
||||||
|
uint32_t init_client_width = 0;
|
||||||
|
uint32_t init_client_height = 0;
|
||||||
|
float init_client_aspect_ratio = 1.f;
|
||||||
|
uint32_t init_window_style = 0;
|
||||||
|
uint32_t window_deco_width = 0;
|
||||||
|
uint32_t window_deco_height = 0;
|
||||||
|
|
||||||
void config_load();
|
void config_load();
|
||||||
void config_save();
|
void config_save();
|
||||||
|
|||||||
@@ -1,3 +1,82 @@
|
|||||||
|
06/08/2023 [spice2x]
|
||||||
|
Improved touch support for Jubeat and Reflec Beat.
|
||||||
|
|
||||||
|
04/23/2023 [spice2x]
|
||||||
|
Update -iidxsounddevice to automatically pick a value (wasapi in most cases)
|
||||||
|
|
||||||
|
04/21/2023 [spice2x]
|
||||||
|
Fix spicecfg not launching in some specific cases
|
||||||
|
|
||||||
|
04/20/2023 [spice2x]
|
||||||
|
Update font for IIDX segment display
|
||||||
|
New option: auto show FPS window
|
||||||
|
|
||||||
|
04/16/2023 [spice2x] (beta)
|
||||||
|
New feature: window resize. Added new options to change window size and
|
||||||
|
position on launch. Screen Resize window (F11) also updated with new
|
||||||
|
controls
|
||||||
|
Screen Resize settings are now per-game
|
||||||
|
Misc bug fixes
|
||||||
|
|
||||||
|
04/09/2023 [spice2x] (beta)
|
||||||
|
Add IIDX LED ticker (segment display) as subscreen overlay for LDJ
|
||||||
|
Add I/O panel window for all games, special support for IIDX, DDR, GFDM
|
||||||
|
Add options to automatically show certain windows on game launch
|
||||||
|
|
||||||
|
04/03/2023 [spice2x] (beta)
|
||||||
|
Move/resize for IIDX/SDVX subscreen overlay window
|
||||||
|
New options to control subscreen overlay window
|
||||||
|
Turn common config mistakes into fatal error messages
|
||||||
|
Various bug fixes for touch handling
|
||||||
|
Revert "pop'n launching at desktop refresh rate" hack from 03/30/2023
|
||||||
|
|
||||||
|
03/30/2023 [spice2x]
|
||||||
|
New option: IIDX Force Definition Type HD (720p)
|
||||||
|
New option: process priority
|
||||||
|
Fix pop'n launching at desktop refresh rate
|
||||||
|
Lower logger thread priority to reduce stutters
|
||||||
|
Misc bug fixes
|
||||||
|
|
||||||
|
03/25/2023 [spice2x]
|
||||||
|
Screen Resize UI update (F11)
|
||||||
|
FPS UI update (F12)
|
||||||
|
New option: processor affinity
|
||||||
|
Misc bug fixes
|
||||||
|
|
||||||
|
03/20/2023 [spice2x]
|
||||||
|
ASIO device override for SDVX (experimental)
|
||||||
|
Fix subscreen overlay for SDVX in Valkyrie mode using BI2X I/O
|
||||||
|
Update text for touch and audio config options
|
||||||
|
UI tweaks and minor bug fixes
|
||||||
|
|
||||||
|
03/17/2023 [spice2x]
|
||||||
|
Disable UPX (file sizes will be larger)
|
||||||
|
Fix regression from 2022 that prevented early patches
|
||||||
|
from being loaded properly (such as audio hex edits)
|
||||||
|
New option: HID lighting brightness
|
||||||
|
New option: display auto-rotate direction
|
||||||
|
New option: custom sleep duration
|
||||||
|
|
||||||
|
03/15/2023 [spice2x]
|
||||||
|
Improve IIDX TT+/- handling (allow alternate key bind)
|
||||||
|
Fix SpiceCompanion touch targets for TDJ FHD
|
||||||
|
Config UI tweaks
|
||||||
|
|
||||||
|
03/13/2023 [spice2x]
|
||||||
|
Fix TDJ FHD launching at wrong refresh rate
|
||||||
|
New IIDX/SDVX options for TT/knob sensitivity when using digital input
|
||||||
|
|
||||||
|
03/12/2023 [spice2x]
|
||||||
|
Fix mouse input for SDVX EG subscreen overlay
|
||||||
|
New unified layout for Options tab
|
||||||
|
Allow multiple DLLs in Inject Hook field
|
||||||
|
|
||||||
|
03/11/2023 [spice2x]
|
||||||
|
Beginning of spice2x fork, based on another fork
|
||||||
|
Ensure TDJ mode runs at 1080p@120Hz for newer games
|
||||||
|
Fix IIDX subscreen overlay in FHD mode
|
||||||
|
Gracefully deal with bad ea3-config.xml
|
||||||
|
|
||||||
04/02/2022 - Add lua scripting
|
04/02/2022 - Add lua scripting
|
||||||
Add button edit dialog
|
Add button edit dialog
|
||||||
Add button debounce/invert settings
|
Add button debounce/invert settings
|
||||||
|
|||||||
Vendored
+95
@@ -0,0 +1,95 @@
|
|||||||
|
Copyright (c) 2017, keshikan (http://www.keshikan.net),
|
||||||
|
with Reserved Font Name "DSEG".
|
||||||
|
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
+11023
File diff suppressed because one or more lines are too long
BIN
Binary file not shown.
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2014-2019 Omar Cornut
|
Copyright (c) 2014-2023 Omar Cornut
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
Vendored
+6
-9
@@ -30,11 +30,11 @@
|
|||||||
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||||
//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87: disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This will be folded into IMGUI_DISABLE_OBSOLETE_FUNCTIONS in a few versions.
|
//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87: disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This will be folded into IMGUI_DISABLE_OBSOLETE_FUNCTIONS in a few versions.
|
||||||
|
|
||||||
//---- Disable all of Dear ImGui or don't implement standard windows.
|
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
|
||||||
// It is very strongly recommended to NOT disable the demo windows during development. Please read comments in imgui_demo.cpp.
|
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
|
||||||
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
|
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
|
||||||
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. Not recommended.
|
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
|
||||||
//#define IMGUI_DISABLE_METRICS_WINDOW // Disable metrics/debugger and other debug tools: ShowMetricsWindow() and ShowStackToolWindow() will be empty.
|
//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowStackToolWindow() will be empty (this was called IMGUI_DISABLE_METRICS_WINDOW before 1.88).
|
||||||
|
|
||||||
//---- Don't implement some functions to reduce linkage requirements.
|
//---- Don't implement some functions to reduce linkage requirements.
|
||||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
|
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
|
||||||
@@ -93,6 +93,8 @@
|
|||||||
constexpr ImVec4(const MyVec4& f) : x(f.x), y(f.y), z(f.z), w(f.w) {} \
|
constexpr ImVec4(const MyVec4& f) : x(f.x), y(f.y), z(f.z), w(f.w) {} \
|
||||||
operator MyVec4() const { return MyVec4(x,y,z,w); }
|
operator MyVec4() const { return MyVec4(x,y,z,w); }
|
||||||
*/
|
*/
|
||||||
|
//---- ...Or use Dear ImGui's own very basic math operators.
|
||||||
|
//#define IMGUI_DEFINE_MATH_OPERATORS
|
||||||
|
|
||||||
//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.
|
//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.
|
||||||
// Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices).
|
// Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices).
|
||||||
@@ -111,11 +113,6 @@
|
|||||||
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
||||||
//#define IM_DEBUG_BREAK __debugbreak()
|
//#define IM_DEBUG_BREAK __debugbreak()
|
||||||
|
|
||||||
//---- Debug Tools: Have the Item Picker break in the ItemAdd() function instead of ItemHoverable(),
|
|
||||||
// (which comes earlier in the code, will catch a few extra items, allow picking items other than Hovered one.)
|
|
||||||
// This adds a small runtime cost which is why it is not enabled by default.
|
|
||||||
//#define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
|
|
||||||
|
|
||||||
//---- Debug Tools: Enable slower asserts
|
//---- Debug Tools: Enable slower asserts
|
||||||
//#define IMGUI_DEBUG_PARANOID
|
//#define IMGUI_DEBUG_PARANOID
|
||||||
|
|
||||||
|
|||||||
Vendored
+3236
-1676
File diff suppressed because it is too large
Load Diff
Vendored
+367
-270
File diff suppressed because it is too large
Load Diff
Vendored
+496
-382
File diff suppressed because it is too large
Load Diff
Vendored
+112
-101
@@ -1,4 +1,4 @@
|
|||||||
// dear imgui, v1.88 WIP
|
// dear imgui, v1.89.5 WIP
|
||||||
// (drawing and font code)
|
// (drawing and font code)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -26,38 +26,24 @@ Index of this file:
|
|||||||
#define _CRT_SECURE_NO_WARNINGS
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "imgui.h"
|
|
||||||
#ifndef IMGUI_DISABLE
|
|
||||||
|
|
||||||
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
||||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
#include "imgui_internal.h"
|
#include "imgui_internal.h"
|
||||||
#ifdef IMGUI_ENABLE_FREETYPE
|
#ifdef IMGUI_ENABLE_FREETYPE
|
||||||
#include "misc/freetype/imgui_freetype.h"
|
#include "misc/freetype/imgui_freetype.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdio.h> // vsnprintf, sscanf, printf
|
#include <stdio.h> // vsnprintf, sscanf, printf
|
||||||
#if !defined(alloca)
|
|
||||||
#if defined(__GLIBC__) || defined(__sun) || defined(__APPLE__) || defined(__NEWLIB__)
|
|
||||||
#include <alloca.h> // alloca (glibc uses <alloca.h>. Note that Cygwin may have _WIN32 defined, so the order matters here)
|
|
||||||
#elif defined(_WIN32)
|
|
||||||
#include <malloc.h> // alloca
|
|
||||||
#if !defined(alloca)
|
|
||||||
#define alloca _alloca // for clang with MS Codegen
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#include <stdlib.h> // alloca
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Visual Studio warnings
|
// Visual Studio warnings
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning (disable: 4127) // condition expression is constant
|
#pragma warning (disable: 4127) // condition expression is constant
|
||||||
#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
|
#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
|
||||||
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
|
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
|
||||||
#pragma warning (disable: 6255) // [Static Analyzer] _alloca indicates failure by raising a stack overflow exception. Consider using _malloca instead.
|
|
||||||
#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
|
#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
|
||||||
#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer)
|
#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer)
|
||||||
#endif
|
#endif
|
||||||
@@ -67,9 +53,6 @@ Index of this file:
|
|||||||
#if __has_warning("-Wunknown-warning-option")
|
#if __has_warning("-Wunknown-warning-option")
|
||||||
#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!
|
#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!
|
||||||
#endif
|
#endif
|
||||||
#if __has_warning("-Walloca")
|
|
||||||
#pragma clang diagnostic ignored "-Walloca" // warning: use of function '__builtin_alloca' is discouraged
|
|
||||||
#endif
|
|
||||||
#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
|
#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
|
||||||
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
|
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
|
||||||
#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok.
|
#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok.
|
||||||
@@ -399,7 +382,7 @@ void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error)
|
|||||||
for (int i = 0; i < IM_ARRAYSIZE(CircleSegmentCounts); i++)
|
for (int i = 0; i < IM_ARRAYSIZE(CircleSegmentCounts); i++)
|
||||||
{
|
{
|
||||||
const float radius = (float)i;
|
const float radius = (float)i;
|
||||||
CircleSegmentCounts[i] = (ImU8)((i > 0) ? IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, CircleSegmentMaxError) : 0);
|
CircleSegmentCounts[i] = (ImU8)((i > 0) ? IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, CircleSegmentMaxError) : IM_DRAWLIST_ARCFAST_SAMPLE_MAX);
|
||||||
}
|
}
|
||||||
ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError);
|
ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError);
|
||||||
}
|
}
|
||||||
@@ -471,12 +454,14 @@ void ImDrawList::AddDrawCmd()
|
|||||||
// Note that this leaves the ImDrawList in a state unfit for further commands, as most code assume that CmdBuffer.Size > 0 && CmdBuffer.back().UserCallback == NULL
|
// Note that this leaves the ImDrawList in a state unfit for further commands, as most code assume that CmdBuffer.Size > 0 && CmdBuffer.back().UserCallback == NULL
|
||||||
void ImDrawList::_PopUnusedDrawCmd()
|
void ImDrawList::_PopUnusedDrawCmd()
|
||||||
{
|
{
|
||||||
if (CmdBuffer.Size == 0)
|
while (CmdBuffer.Size > 0)
|
||||||
return;
|
{
|
||||||
ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
|
ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
|
||||||
if (curr_cmd->ElemCount == 0 && curr_cmd->UserCallback == NULL)
|
if (curr_cmd->ElemCount != 0 || curr_cmd->UserCallback != NULL)
|
||||||
|
return;// break;
|
||||||
CmdBuffer.pop_back();
|
CmdBuffer.pop_back();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data)
|
void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data)
|
||||||
{
|
{
|
||||||
@@ -728,7 +713,7 @@ void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, c
|
|||||||
// We avoid using the ImVec2 math operators here to reduce cost to a minimum for debug/non-inlined builds.
|
// We avoid using the ImVec2 math operators here to reduce cost to a minimum for debug/non-inlined builds.
|
||||||
void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 col, ImDrawFlags flags, float thickness)
|
void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 col, ImDrawFlags flags, float thickness)
|
||||||
{
|
{
|
||||||
if (points_count < 2)
|
if (points_count < 2 || (col & IM_COL32_A_MASK) == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const bool closed = (flags & ImDrawFlags_Closed) != 0;
|
const bool closed = (flags & ImDrawFlags_Closed) != 0;
|
||||||
@@ -761,7 +746,8 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
|
|||||||
|
|
||||||
// Temporary buffer
|
// Temporary buffer
|
||||||
// The first <points_count> items are normals at each line point, then after that there are either 2 or 4 temp points for each line point
|
// The first <points_count> items are normals at each line point, then after that there are either 2 or 4 temp points for each line point
|
||||||
ImVec2* temp_normals = (ImVec2*)alloca(points_count * ((use_texture || !thick_line) ? 3 : 5) * sizeof(ImVec2)); //-V630
|
_Data->TempBuffer.reserve_discard(points_count * ((use_texture || !thick_line) ? 3 : 5));
|
||||||
|
ImVec2* temp_normals = _Data->TempBuffer.Data;
|
||||||
ImVec2* temp_points = temp_normals + points_count;
|
ImVec2* temp_points = temp_normals + points_count;
|
||||||
|
|
||||||
// Calculate normals (tangents) for each line segment
|
// Calculate normals (tangents) for each line segment
|
||||||
@@ -985,7 +971,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
|
|||||||
// - Filled shapes must always use clockwise winding order. The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing.
|
// - Filled shapes must always use clockwise winding order. The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing.
|
||||||
void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_count, ImU32 col)
|
void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_count, ImU32 col)
|
||||||
{
|
{
|
||||||
if (points_count < 3)
|
if (points_count < 3 || (col & IM_COL32_A_MASK) == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const ImVec2 uv = _Data->TexUvWhitePixel;
|
const ImVec2 uv = _Data->TexUvWhitePixel;
|
||||||
@@ -1009,7 +995,8 @@ void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_coun
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compute normals
|
// Compute normals
|
||||||
ImVec2* temp_normals = (ImVec2*)alloca(points_count * sizeof(ImVec2)); //-V630
|
_Data->TempBuffer.reserve_discard(points_count);
|
||||||
|
ImVec2* temp_normals = _Data->TempBuffer.Data;
|
||||||
for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++)
|
for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++)
|
||||||
{
|
{
|
||||||
const ImVec2& p0 = points[i0];
|
const ImVec2& p0 = points[i0];
|
||||||
@@ -1066,7 +1053,7 @@ void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_coun
|
|||||||
|
|
||||||
void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_sample, int a_max_sample, int a_step)
|
void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_sample, int a_max_sample, int a_step)
|
||||||
{
|
{
|
||||||
if (radius <= 0.0f)
|
if (radius < 0.5f)
|
||||||
{
|
{
|
||||||
_Path.push_back(center);
|
_Path.push_back(center);
|
||||||
return;
|
return;
|
||||||
@@ -1158,7 +1145,7 @@ void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_
|
|||||||
|
|
||||||
void ImDrawList::_PathArcToN(const ImVec2& center, float radius, float a_min, float a_max, int num_segments)
|
void ImDrawList::_PathArcToN(const ImVec2& center, float radius, float a_min, float a_max, int num_segments)
|
||||||
{
|
{
|
||||||
if (radius <= 0.0f)
|
if (radius < 0.5f)
|
||||||
{
|
{
|
||||||
_Path.push_back(center);
|
_Path.push_back(center);
|
||||||
return;
|
return;
|
||||||
@@ -1177,7 +1164,7 @@ void ImDrawList::_PathArcToN(const ImVec2& center, float radius, float a_min, fl
|
|||||||
// 0: East, 3: South, 6: West, 9: North, 12: East
|
// 0: East, 3: South, 6: West, 9: North, 12: East
|
||||||
void ImDrawList::PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12)
|
void ImDrawList::PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12)
|
||||||
{
|
{
|
||||||
if (radius <= 0.0f)
|
if (radius < 0.5f)
|
||||||
{
|
{
|
||||||
_Path.push_back(center);
|
_Path.push_back(center);
|
||||||
return;
|
return;
|
||||||
@@ -1187,7 +1174,7 @@ void ImDrawList::PathArcToFast(const ImVec2& center, float radius, int a_min_of_
|
|||||||
|
|
||||||
void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments)
|
void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments)
|
||||||
{
|
{
|
||||||
if (radius <= 0.0f)
|
if (radius < 0.5f)
|
||||||
{
|
{
|
||||||
_Path.push_back(center);
|
_Path.push_back(center);
|
||||||
return;
|
return;
|
||||||
@@ -1303,6 +1290,7 @@ void ImDrawList::PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, cons
|
|||||||
ImVec2 p1 = _Path.back();
|
ImVec2 p1 = _Path.back();
|
||||||
if (num_segments == 0)
|
if (num_segments == 0)
|
||||||
{
|
{
|
||||||
|
IM_ASSERT(_Data->CurveTessellationTol > 0.0f);
|
||||||
PathBezierCubicCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, _Data->CurveTessellationTol, 0); // Auto-tessellated
|
PathBezierCubicCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, _Data->CurveTessellationTol, 0); // Auto-tessellated
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1318,6 +1306,7 @@ void ImDrawList::PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3,
|
|||||||
ImVec2 p1 = _Path.back();
|
ImVec2 p1 = _Path.back();
|
||||||
if (num_segments == 0)
|
if (num_segments == 0)
|
||||||
{
|
{
|
||||||
|
IM_ASSERT(_Data->CurveTessellationTol > 0.0f);
|
||||||
PathBezierQuadraticCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, _Data->CurveTessellationTol, 0);// Auto-tessellated
|
PathBezierQuadraticCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, _Data->CurveTessellationTol, 0);// Auto-tessellated
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1332,6 +1321,7 @@ IM_STATIC_ASSERT(ImDrawFlags_RoundCornersTopLeft == (1 << 4));
|
|||||||
static inline ImDrawFlags FixRectCornerFlags(ImDrawFlags flags)
|
static inline ImDrawFlags FixRectCornerFlags(ImDrawFlags flags)
|
||||||
{
|
{
|
||||||
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||||
|
// Obsoleted in 1.82 (from February 2021)
|
||||||
// Legacy Support for hard coded ~0 (used to be a suggested equivalent to ImDrawCornerFlags_All)
|
// Legacy Support for hard coded ~0 (used to be a suggested equivalent to ImDrawCornerFlags_All)
|
||||||
// ~0 --> ImDrawFlags_RoundCornersAll or 0
|
// ~0 --> ImDrawFlags_RoundCornersAll or 0
|
||||||
if (flags == ~0)
|
if (flags == ~0)
|
||||||
@@ -1368,7 +1358,7 @@ void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, ImDr
|
|||||||
rounding = ImMin(rounding, ImFabs(b.x - a.x) * ( ((flags & ImDrawFlags_RoundCornersTop) == ImDrawFlags_RoundCornersTop) || ((flags & ImDrawFlags_RoundCornersBottom) == ImDrawFlags_RoundCornersBottom) ? 0.5f : 1.0f ) - 1.0f);
|
rounding = ImMin(rounding, ImFabs(b.x - a.x) * ( ((flags & ImDrawFlags_RoundCornersTop) == ImDrawFlags_RoundCornersTop) || ((flags & ImDrawFlags_RoundCornersBottom) == ImDrawFlags_RoundCornersBottom) ? 0.5f : 1.0f ) - 1.0f);
|
||||||
rounding = ImMin(rounding, ImFabs(b.y - a.y) * ( ((flags & ImDrawFlags_RoundCornersLeft) == ImDrawFlags_RoundCornersLeft) || ((flags & ImDrawFlags_RoundCornersRight) == ImDrawFlags_RoundCornersRight) ? 0.5f : 1.0f ) - 1.0f);
|
rounding = ImMin(rounding, ImFabs(b.y - a.y) * ( ((flags & ImDrawFlags_RoundCornersLeft) == ImDrawFlags_RoundCornersLeft) || ((flags & ImDrawFlags_RoundCornersRight) == ImDrawFlags_RoundCornersRight) ? 0.5f : 1.0f ) - 1.0f);
|
||||||
|
|
||||||
if (rounding <= 0.0f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone)
|
if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone)
|
||||||
{
|
{
|
||||||
PathLineTo(a);
|
PathLineTo(a);
|
||||||
PathLineTo(ImVec2(b.x, a.y));
|
PathLineTo(ImVec2(b.x, a.y));
|
||||||
@@ -1414,7 +1404,7 @@ void ImDrawList::AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 c
|
|||||||
{
|
{
|
||||||
if ((col & IM_COL32_A_MASK) == 0)
|
if ((col & IM_COL32_A_MASK) == 0)
|
||||||
return;
|
return;
|
||||||
if (rounding <= 0.0f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone)
|
if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone)
|
||||||
{
|
{
|
||||||
PrimReserve(6, 4);
|
PrimReserve(6, 4);
|
||||||
PrimRect(p_min, p_max, col);
|
PrimRect(p_min, p_max, col);
|
||||||
@@ -1490,7 +1480,7 @@ void ImDrawList::AddTriangleFilled(const ImVec2& p1, const ImVec2& p2, const ImV
|
|||||||
|
|
||||||
void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness)
|
void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness)
|
||||||
{
|
{
|
||||||
if ((col & IM_COL32_A_MASK) == 0 || radius <= 0.0f)
|
if ((col & IM_COL32_A_MASK) == 0 || radius < 0.5f)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (num_segments <= 0)
|
if (num_segments <= 0)
|
||||||
@@ -1514,7 +1504,7 @@ void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int nu
|
|||||||
|
|
||||||
void ImDrawList::AddCircleFilled(const ImVec2& center, float radius, ImU32 col, int num_segments)
|
void ImDrawList::AddCircleFilled(const ImVec2& center, float radius, ImU32 col, int num_segments)
|
||||||
{
|
{
|
||||||
if ((col & IM_COL32_A_MASK) == 0 || radius <= 0.0f)
|
if ((col & IM_COL32_A_MASK) == 0 || radius < 0.5f)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (num_segments <= 0)
|
if (num_segments <= 0)
|
||||||
@@ -1654,7 +1644,7 @@ void ImDrawList::AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_mi
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
flags = FixRectCornerFlags(flags);
|
flags = FixRectCornerFlags(flags);
|
||||||
if (rounding <= 0.0f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone)
|
if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone)
|
||||||
{
|
{
|
||||||
AddImage(user_texture_id, p_min, p_max, uv_min, uv_max, col);
|
AddImage(user_texture_id, p_min, p_max, uv_min, uv_max, col);
|
||||||
return;
|
return;
|
||||||
@@ -2306,10 +2296,11 @@ void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], fl
|
|||||||
|
|
||||||
void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride)
|
void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride)
|
||||||
{
|
{
|
||||||
|
IM_ASSERT_PARANOID(w <= stride);
|
||||||
unsigned char* data = pixels + x + y * stride;
|
unsigned char* data = pixels + x + y * stride;
|
||||||
for (int j = h; j > 0; j--, data += stride)
|
for (int j = h; j > 0; j--, data += stride - w)
|
||||||
for (int i = 0; i < w; i++)
|
for (int i = w; i > 0; i--, data++)
|
||||||
data[i] = table[data[i]];
|
*data = table[*data];
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef IMGUI_ENABLE_STB_TRUETYPE
|
#ifdef IMGUI_ENABLE_STB_TRUETYPE
|
||||||
@@ -2326,7 +2317,7 @@ struct ImFontBuildSrcData
|
|||||||
int GlyphsHighest; // Highest requested codepoint
|
int GlyphsHighest; // Highest requested codepoint
|
||||||
int GlyphsCount; // Glyph count (excluding missing glyphs and glyphs already set by an earlier source font)
|
int GlyphsCount; // Glyph count (excluding missing glyphs and glyphs already set by an earlier source font)
|
||||||
ImBitVector GlyphsSet; // Glyph bit map (random access, 1-bit per codepoint. This will be a maximum of 8KB)
|
ImBitVector GlyphsSet; // Glyph bit map (random access, 1-bit per codepoint. This will be a maximum of 8KB)
|
||||||
ImVector<int> GlyphsList; // Glyph codepoints list (flattened version of GlyphsMap)
|
ImVector<int> GlyphsList; // Glyph codepoints list (flattened version of GlyphsSet)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Temporary data for one destination ImFont* (multiple source fonts can be merged into one destination ImFont)
|
// Temporary data for one destination ImFont* (multiple source fonts can be merged into one destination ImFont)
|
||||||
@@ -2398,7 +2389,12 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
|
|||||||
ImFontBuildDstData& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
|
ImFontBuildDstData& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
|
||||||
src_tmp.SrcRanges = cfg.GlyphRanges ? cfg.GlyphRanges : atlas->GetGlyphRangesDefault();
|
src_tmp.SrcRanges = cfg.GlyphRanges ? cfg.GlyphRanges : atlas->GetGlyphRangesDefault();
|
||||||
for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
|
for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
|
||||||
|
{
|
||||||
|
// Check for valid range. This may also help detect *some* dangling pointers, because a common
|
||||||
|
// user error is to setup ImFontConfig::GlyphRanges with a pointer to data that isn't persistent.
|
||||||
|
IM_ASSERT(src_range[0] <= src_range[1]);
|
||||||
src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]);
|
src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]);
|
||||||
|
}
|
||||||
dst_tmp.SrcCount++;
|
dst_tmp.SrcCount++;
|
||||||
dst_tmp.GlyphsHighest = ImMax(dst_tmp.GlyphsHighest, src_tmp.GlyphsHighest);
|
dst_tmp.GlyphsHighest = ImMax(dst_tmp.GlyphsHighest, src_tmp.GlyphsHighest);
|
||||||
}
|
}
|
||||||
@@ -2826,6 +2822,17 @@ const ImWchar* ImFontAtlas::GetGlyphRangesDefault()
|
|||||||
return &ranges[0];
|
return &ranges[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ImWchar* ImFontAtlas::GetGlyphRangesGreek()
|
||||||
|
{
|
||||||
|
static const ImWchar ranges[] =
|
||||||
|
{
|
||||||
|
0x0020, 0x00FF, // Basic Latin + Latin Supplement
|
||||||
|
0x0370, 0x03FF, // Greek and Coptic
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
return &ranges[0];
|
||||||
|
}
|
||||||
|
|
||||||
const ImWchar* ImFontAtlas::GetGlyphRangesKorean()
|
const ImWchar* ImFontAtlas::GetGlyphRangesKorean()
|
||||||
{
|
{
|
||||||
static const ImWchar ranges[] =
|
static const ImWchar ranges[] =
|
||||||
@@ -2942,19 +2949,19 @@ const ImWchar* ImFontAtlas::GetGlyphRangesJapanese()
|
|||||||
// 2999 ideograms code points for Japanese
|
// 2999 ideograms code points for Japanese
|
||||||
// - 2136 Joyo (meaning "for regular use" or "for common use") Kanji code points
|
// - 2136 Joyo (meaning "for regular use" or "for common use") Kanji code points
|
||||||
// - 863 Jinmeiyo (meaning "for personal name") Kanji code points
|
// - 863 Jinmeiyo (meaning "for personal name") Kanji code points
|
||||||
// - Sourced from the character information database of the Information-technology Promotion Agency, Japan
|
// - Sourced from official information provided by the government agencies of Japan:
|
||||||
// - https://mojikiban.ipa.go.jp/mji/
|
// - List of Joyo Kanji by the Agency for Cultural Affairs
|
||||||
// - Available under the terms of the Creative Commons Attribution-ShareAlike 2.1 Japan (CC BY-SA 2.1 JP).
|
// - https://www.bunka.go.jp/kokugo_nihongo/sisaku/joho/joho/kijun/naikaku/kanji/
|
||||||
// - https://creativecommons.org/licenses/by-sa/2.1/jp/deed.en
|
// - List of Jinmeiyo Kanji by the Ministry of Justice
|
||||||
// - https://creativecommons.org/licenses/by-sa/2.1/jp/legalcode
|
// - http://www.moj.go.jp/MINJI/minji86.html
|
||||||
|
// - Available under the terms of the Creative Commons Attribution 4.0 International (CC BY 4.0).
|
||||||
|
// - https://creativecommons.org/licenses/by/4.0/legalcode
|
||||||
// - You can generate this code by the script at:
|
// - You can generate this code by the script at:
|
||||||
// - https://github.com/vaiorabbit/everyday_use_kanji
|
// - https://github.com/vaiorabbit/everyday_use_kanji
|
||||||
// - References:
|
// - References:
|
||||||
// - List of Joyo Kanji
|
// - List of Joyo Kanji
|
||||||
// - (Official list by the Agency for Cultural Affairs) https://www.bunka.go.jp/kokugo_nihongo/sisaku/joho/joho/kakuki/14/tosin02/index.html
|
|
||||||
// - (Wikipedia) https://en.wikipedia.org/wiki/List_of_j%C5%8Dy%C5%8D_kanji
|
// - (Wikipedia) https://en.wikipedia.org/wiki/List_of_j%C5%8Dy%C5%8D_kanji
|
||||||
// - List of Jinmeiyo Kanji
|
// - List of Jinmeiyo Kanji
|
||||||
// - (Official list by the Ministry of Justice) http://www.moj.go.jp/MINJI/minji86.html
|
|
||||||
// - (Wikipedia) https://en.wikipedia.org/wiki/Jinmeiy%C5%8D_kanji
|
// - (Wikipedia) https://en.wikipedia.org/wiki/Jinmeiy%C5%8D_kanji
|
||||||
// - Missing 1 Joyo Kanji: U+20B9F (Kun'yomi: Shikaru, On'yomi: Shitsu,shichi), see https://github.com/ocornut/imgui/pull/3627 for details.
|
// - Missing 1 Joyo Kanji: U+20B9F (Kun'yomi: Shikaru, On'yomi: Shitsu,shichi), see https://github.com/ocornut/imgui/pull/3627 for details.
|
||||||
// You can use ImFontGlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters.
|
// You can use ImFontGlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters.
|
||||||
@@ -3117,7 +3124,8 @@ ImFont::ImFont()
|
|||||||
FallbackAdvanceX = 0.0f;
|
FallbackAdvanceX = 0.0f;
|
||||||
FallbackChar = (ImWchar)-1;
|
FallbackChar = (ImWchar)-1;
|
||||||
EllipsisChar = (ImWchar)-1;
|
EllipsisChar = (ImWchar)-1;
|
||||||
DotChar = (ImWchar)-1;
|
EllipsisWidth = EllipsisCharStep = 0.0f;
|
||||||
|
EllipsisCharCount = 0;
|
||||||
FallbackGlyph = NULL;
|
FallbackGlyph = NULL;
|
||||||
ContainerAtlas = NULL;
|
ContainerAtlas = NULL;
|
||||||
ConfigData = NULL;
|
ConfigData = NULL;
|
||||||
@@ -3205,8 +3213,20 @@ void ImFont::BuildLookupTable()
|
|||||||
const ImWchar dots_chars[] = { (ImWchar)'.', (ImWchar)0xFF0E };
|
const ImWchar dots_chars[] = { (ImWchar)'.', (ImWchar)0xFF0E };
|
||||||
if (EllipsisChar == (ImWchar)-1)
|
if (EllipsisChar == (ImWchar)-1)
|
||||||
EllipsisChar = FindFirstExistingGlyph(this, ellipsis_chars, IM_ARRAYSIZE(ellipsis_chars));
|
EllipsisChar = FindFirstExistingGlyph(this, ellipsis_chars, IM_ARRAYSIZE(ellipsis_chars));
|
||||||
if (DotChar == (ImWchar)-1)
|
const ImWchar dot_char = FindFirstExistingGlyph(this, dots_chars, IM_ARRAYSIZE(dots_chars));
|
||||||
DotChar = FindFirstExistingGlyph(this, dots_chars, IM_ARRAYSIZE(dots_chars));
|
if (EllipsisChar != (ImWchar)-1)
|
||||||
|
{
|
||||||
|
EllipsisCharCount = 1;
|
||||||
|
EllipsisWidth = EllipsisCharStep = FindGlyph(EllipsisChar)->X1;
|
||||||
|
}
|
||||||
|
else if (dot_char != (ImWchar)-1)
|
||||||
|
{
|
||||||
|
const ImFontGlyph* glyph = FindGlyph(dot_char);
|
||||||
|
EllipsisChar = dot_char;
|
||||||
|
EllipsisCharCount = 3;
|
||||||
|
EllipsisCharStep = (glyph->X1 - glyph->X0) + 1.0f;
|
||||||
|
EllipsisWidth = EllipsisCharStep * 3.0f - 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
// Setup fallback character
|
// Setup fallback character
|
||||||
const ImWchar fallback_chars[] = { (ImWchar)IM_UNICODE_CODEPOINT_INVALID, (ImWchar)'?', (ImWchar)' ' };
|
const ImWchar fallback_chars[] = { (ImWchar)IM_UNICODE_CODEPOINT_INVALID, (ImWchar)'?', (ImWchar)' ' };
|
||||||
@@ -3338,11 +3358,21 @@ const ImFontGlyph* ImFont::FindGlyphNoFallback(ImWchar c) const
|
|||||||
return &Glyphs.Data[i];
|
return &Glyphs.Data[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wrapping skips upcoming blanks
|
||||||
|
static inline const char* CalcWordWrapNextLineStartA(const char* text, const char* text_end)
|
||||||
|
{
|
||||||
|
while (text < text_end && ImCharIsBlankA(*text))
|
||||||
|
text++;
|
||||||
|
if (*text == '\n')
|
||||||
|
text++;
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple word-wrapping for English, not full-featured. Please submit failing cases!
|
||||||
|
// This will return the next location to wrap from. If no wrapping if necessary, this will fast-forward to e.g. text_end.
|
||||||
|
// FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.)
|
||||||
const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const
|
const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const
|
||||||
{
|
{
|
||||||
// Simple word-wrapping for English, not full-featured. Please submit failing cases!
|
|
||||||
// FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.)
|
|
||||||
|
|
||||||
// For references, possible wrap point marked with ^
|
// For references, possible wrap point marked with ^
|
||||||
// "aaa bbb, ccc,ddd. eee fff. ggg!"
|
// "aaa bbb, ccc,ddd. eee fff. ggg!"
|
||||||
// ^ ^ ^ ^ ^__ ^ ^
|
// ^ ^ ^ ^ ^__ ^ ^
|
||||||
@@ -3354,7 +3384,6 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c
|
|||||||
|
|
||||||
// Cut words that cannot possibly fit within one line.
|
// Cut words that cannot possibly fit within one line.
|
||||||
// e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish"
|
// e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish"
|
||||||
|
|
||||||
float line_width = 0.0f;
|
float line_width = 0.0f;
|
||||||
float word_width = 0.0f;
|
float word_width = 0.0f;
|
||||||
float blank_width = 0.0f;
|
float blank_width = 0.0f;
|
||||||
@@ -3365,6 +3394,7 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c
|
|||||||
bool inside_word = true;
|
bool inside_word = true;
|
||||||
|
|
||||||
const char* s = text;
|
const char* s = text;
|
||||||
|
IM_ASSERT(text_end != NULL);
|
||||||
while (s < text_end)
|
while (s < text_end)
|
||||||
{
|
{
|
||||||
unsigned int c = (unsigned int)*s;
|
unsigned int c = (unsigned int)*s;
|
||||||
@@ -3373,8 +3403,6 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c
|
|||||||
next_s = s + 1;
|
next_s = s + 1;
|
||||||
else
|
else
|
||||||
next_s = s + ImTextCharFromUtf8(&c, s, text_end);
|
next_s = s + ImTextCharFromUtf8(&c, s, text_end);
|
||||||
if (c == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (c < 32)
|
if (c < 32)
|
||||||
{
|
{
|
||||||
@@ -3434,6 +3462,10 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c
|
|||||||
s = next_s;
|
s = next_s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity.
|
||||||
|
// +1 may not be a character start point in UTF-8 but it's ok because caller loops use (text >= word_wrap_eol).
|
||||||
|
if (s == text && text < text_end)
|
||||||
|
return s + 1;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3458,11 +3490,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
|
|||||||
{
|
{
|
||||||
// Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
|
// Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
|
||||||
if (!word_wrap_eol)
|
if (!word_wrap_eol)
|
||||||
{
|
|
||||||
word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - line_width);
|
word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - line_width);
|
||||||
if (word_wrap_eol == s) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity.
|
|
||||||
word_wrap_eol++; // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s >= word_wrap_eol)
|
if (s >= word_wrap_eol)
|
||||||
{
|
{
|
||||||
@@ -3471,13 +3499,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
|
|||||||
text_size.y += line_height;
|
text_size.y += line_height;
|
||||||
line_width = 0.0f;
|
line_width = 0.0f;
|
||||||
word_wrap_eol = NULL;
|
word_wrap_eol = NULL;
|
||||||
|
s = CalcWordWrapNextLineStartA(s, text_end); // Wrapping skips upcoming blanks
|
||||||
// Wrapping skips upcoming blanks
|
|
||||||
while (s < text_end)
|
|
||||||
{
|
|
||||||
const char c = *s;
|
|
||||||
if (ImCharIsBlankA(c)) { s++; } else if (c == '\n') { s++; break; } else { break; }
|
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3486,15 +3508,9 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
|
|||||||
const char* prev_s = s;
|
const char* prev_s = s;
|
||||||
unsigned int c = (unsigned int)*s;
|
unsigned int c = (unsigned int)*s;
|
||||||
if (c < 0x80)
|
if (c < 0x80)
|
||||||
{
|
|
||||||
s += 1;
|
s += 1;
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
s += ImTextCharFromUtf8(&c, s, text_end);
|
s += ImTextCharFromUtf8(&c, s, text_end);
|
||||||
if (c == 0) // Malformed UTF-8?
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c < 32)
|
if (c < 32)
|
||||||
{
|
{
|
||||||
@@ -3562,15 +3578,25 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
|
|||||||
const float scale = size / FontSize;
|
const float scale = size / FontSize;
|
||||||
const float line_height = FontSize * scale;
|
const float line_height = FontSize * scale;
|
||||||
const bool word_wrap_enabled = (wrap_width > 0.0f);
|
const bool word_wrap_enabled = (wrap_width > 0.0f);
|
||||||
const char* word_wrap_eol = NULL;
|
|
||||||
|
|
||||||
// Fast-forward to first visible line
|
// Fast-forward to first visible line
|
||||||
const char* s = text_begin;
|
const char* s = text_begin;
|
||||||
if (y + line_height < clip_rect.y && !word_wrap_enabled)
|
if (y + line_height < clip_rect.y)
|
||||||
while (y + line_height < clip_rect.y && s < text_end)
|
while (y + line_height < clip_rect.y && s < text_end)
|
||||||
{
|
{
|
||||||
s = (const char*)memchr(s, '\n', text_end - s);
|
const char* line_end = (const char*)memchr(s, '\n', text_end - s);
|
||||||
s = s ? s + 1 : text_end;
|
if (word_wrap_enabled)
|
||||||
|
{
|
||||||
|
// FIXME-OPT: This is not optimal as do first do a search for \n before calling CalcWordWrapPositionA().
|
||||||
|
// If the specs for CalcWordWrapPositionA() were reworked to optionally return on \n we could combine both.
|
||||||
|
// However it is still better than nothing performing the fast-forward!
|
||||||
|
s = CalcWordWrapPositionA(scale, s, line_end ? line_end : text_end, wrap_width);
|
||||||
|
s = CalcWordWrapNextLineStartA(s, text_end);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s = line_end ? line_end + 1 : text_end;
|
||||||
|
}
|
||||||
y += line_height;
|
y += line_height;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3596,12 +3622,12 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
|
|||||||
const int idx_count_max = (int)(text_end - s) * 6;
|
const int idx_count_max = (int)(text_end - s) * 6;
|
||||||
const int idx_expected_size = draw_list->IdxBuffer.Size + idx_count_max;
|
const int idx_expected_size = draw_list->IdxBuffer.Size + idx_count_max;
|
||||||
draw_list->PrimReserve(idx_count_max, vtx_count_max);
|
draw_list->PrimReserve(idx_count_max, vtx_count_max);
|
||||||
|
|
||||||
ImDrawVert* vtx_write = draw_list->_VtxWritePtr;
|
ImDrawVert* vtx_write = draw_list->_VtxWritePtr;
|
||||||
ImDrawIdx* idx_write = draw_list->_IdxWritePtr;
|
ImDrawIdx* idx_write = draw_list->_IdxWritePtr;
|
||||||
unsigned int vtx_current_idx = draw_list->_VtxCurrentIdx;
|
unsigned int vtx_index = draw_list->_VtxCurrentIdx;
|
||||||
|
|
||||||
const ImU32 col_untinted = col | ~IM_COL32_A_MASK;
|
const ImU32 col_untinted = col | ~IM_COL32_A_MASK;
|
||||||
|
const char* word_wrap_eol = NULL;
|
||||||
|
|
||||||
while (s < text_end)
|
while (s < text_end)
|
||||||
{
|
{
|
||||||
@@ -3609,24 +3635,14 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
|
|||||||
{
|
{
|
||||||
// Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
|
// Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
|
||||||
if (!word_wrap_eol)
|
if (!word_wrap_eol)
|
||||||
{
|
|
||||||
word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - (x - start_x));
|
word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - (x - start_x));
|
||||||
if (word_wrap_eol == s) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity.
|
|
||||||
word_wrap_eol++; // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s >= word_wrap_eol)
|
if (s >= word_wrap_eol)
|
||||||
{
|
{
|
||||||
x = start_x;
|
x = start_x;
|
||||||
y += line_height;
|
y += line_height;
|
||||||
word_wrap_eol = NULL;
|
word_wrap_eol = NULL;
|
||||||
|
s = CalcWordWrapNextLineStartA(s, text_end); // Wrapping skips upcoming blanks
|
||||||
// Wrapping skips upcoming blanks
|
|
||||||
while (s < text_end)
|
|
||||||
{
|
|
||||||
const char c = *s;
|
|
||||||
if (ImCharIsBlankA(c)) { s++; } else if (c == '\n') { s++; break; } else { break; }
|
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3634,15 +3650,9 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
|
|||||||
// Decode and advance source
|
// Decode and advance source
|
||||||
unsigned int c = (unsigned int)*s;
|
unsigned int c = (unsigned int)*s;
|
||||||
if (c < 0x80)
|
if (c < 0x80)
|
||||||
{
|
|
||||||
s += 1;
|
s += 1;
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
s += ImTextCharFromUtf8(&c, s, text_end);
|
s += ImTextCharFromUtf8(&c, s, text_end);
|
||||||
if (c == 0) // Malformed UTF-8?
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c < 32)
|
if (c < 32)
|
||||||
{
|
{
|
||||||
@@ -3713,14 +3723,14 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
|
|||||||
|
|
||||||
// We are NOT calling PrimRectUV() here because non-inlined causes too much overhead in a debug builds. Inlined here:
|
// We are NOT calling PrimRectUV() here because non-inlined causes too much overhead in a debug builds. Inlined here:
|
||||||
{
|
{
|
||||||
idx_write[0] = (ImDrawIdx)(vtx_current_idx); idx_write[1] = (ImDrawIdx)(vtx_current_idx+1); idx_write[2] = (ImDrawIdx)(vtx_current_idx+2);
|
|
||||||
idx_write[3] = (ImDrawIdx)(vtx_current_idx); idx_write[4] = (ImDrawIdx)(vtx_current_idx+2); idx_write[5] = (ImDrawIdx)(vtx_current_idx+3);
|
|
||||||
vtx_write[0].pos.x = x1; vtx_write[0].pos.y = y1; vtx_write[0].col = glyph_col; vtx_write[0].uv.x = u1; vtx_write[0].uv.y = v1;
|
vtx_write[0].pos.x = x1; vtx_write[0].pos.y = y1; vtx_write[0].col = glyph_col; vtx_write[0].uv.x = u1; vtx_write[0].uv.y = v1;
|
||||||
vtx_write[1].pos.x = x2; vtx_write[1].pos.y = y1; vtx_write[1].col = glyph_col; vtx_write[1].uv.x = u2; vtx_write[1].uv.y = v1;
|
vtx_write[1].pos.x = x2; vtx_write[1].pos.y = y1; vtx_write[1].col = glyph_col; vtx_write[1].uv.x = u2; vtx_write[1].uv.y = v1;
|
||||||
vtx_write[2].pos.x = x2; vtx_write[2].pos.y = y2; vtx_write[2].col = glyph_col; vtx_write[2].uv.x = u2; vtx_write[2].uv.y = v2;
|
vtx_write[2].pos.x = x2; vtx_write[2].pos.y = y2; vtx_write[2].col = glyph_col; vtx_write[2].uv.x = u2; vtx_write[2].uv.y = v2;
|
||||||
vtx_write[3].pos.x = x1; vtx_write[3].pos.y = y2; vtx_write[3].col = glyph_col; vtx_write[3].uv.x = u1; vtx_write[3].uv.y = v2;
|
vtx_write[3].pos.x = x1; vtx_write[3].pos.y = y2; vtx_write[3].col = glyph_col; vtx_write[3].uv.x = u1; vtx_write[3].uv.y = v2;
|
||||||
|
idx_write[0] = (ImDrawIdx)(vtx_index); idx_write[1] = (ImDrawIdx)(vtx_index + 1); idx_write[2] = (ImDrawIdx)(vtx_index + 2);
|
||||||
|
idx_write[3] = (ImDrawIdx)(vtx_index); idx_write[4] = (ImDrawIdx)(vtx_index + 2); idx_write[5] = (ImDrawIdx)(vtx_index + 3);
|
||||||
vtx_write += 4;
|
vtx_write += 4;
|
||||||
vtx_current_idx += 4;
|
vtx_index += 4;
|
||||||
idx_write += 6;
|
idx_write += 6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3734,7 +3744,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
|
|||||||
draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ElemCount -= (idx_expected_size - draw_list->IdxBuffer.Size);
|
draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ElemCount -= (idx_expected_size - draw_list->IdxBuffer.Size);
|
||||||
draw_list->_VtxWritePtr = vtx_write;
|
draw_list->_VtxWritePtr = vtx_write;
|
||||||
draw_list->_IdxWritePtr = idx_write;
|
draw_list->_IdxWritePtr = idx_write;
|
||||||
draw_list->_VtxCurrentIdx = vtx_current_idx;
|
draw_list->_VtxCurrentIdx = vtx_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@@ -3787,6 +3797,7 @@ void ImGui::RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir d
|
|||||||
|
|
||||||
void ImGui::RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col)
|
void ImGui::RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col)
|
||||||
{
|
{
|
||||||
|
// FIXME-OPT: This should be baked in font.
|
||||||
draw_list->AddCircleFilled(pos, draw_list->_Data->FontSize * 0.20f, col, 8);
|
draw_list->AddCircleFilled(pos, draw_list->_Data->FontSize * 0.20f, col, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
+200
-36
@@ -3,11 +3,13 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cctype>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#ifndef IMGUI_VERSION
|
#ifndef IMGUI_VERSION
|
||||||
@@ -41,6 +43,10 @@ namespace ImGui
|
|||||||
|
|
||||||
FileBrowser &operator=(const FileBrowser ©From);
|
FileBrowser &operator=(const FileBrowser ©From);
|
||||||
|
|
||||||
|
// set the window position (in pixels)
|
||||||
|
// default is centered
|
||||||
|
void SetWindowPos(int posx, int posy) noexcept;
|
||||||
|
|
||||||
// set the window size (in pixels)
|
// set the window size (in pixels)
|
||||||
// default is (700, 450)
|
// default is (700, 450)
|
||||||
void SetWindowSize(int width, int height) noexcept;
|
void SetWindowSize(int width, int height) noexcept;
|
||||||
@@ -91,23 +97,36 @@ namespace ImGui
|
|||||||
// default value is 0 (the first type filter)
|
// default value is 0 (the first type filter)
|
||||||
void SetCurrentTypeFilterIndex(int index);
|
void SetCurrentTypeFilterIndex(int index);
|
||||||
|
|
||||||
|
// when ImGuiFileBrowserFlags_EnterNewFilename is set
|
||||||
|
// this function will pre-fill the input dialog with a filename.
|
||||||
|
void SetInputName(std::string_view input);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
template <class Functor>
|
template <class Functor>
|
||||||
struct ScopeGuard
|
struct ScopeGuard
|
||||||
{
|
{
|
||||||
ScopeGuard(Functor&& t) : func(std::move(t)) { }
|
ScopeGuard(Functor&& t) : func(std::move(t)) { }
|
||||||
|
|
||||||
~ScopeGuard()
|
~ScopeGuard() { func(); }
|
||||||
{
|
|
||||||
func();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
Functor func;
|
Functor func;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct FileRecord
|
||||||
|
{
|
||||||
|
bool isDir = false;
|
||||||
|
std::filesystem::path name;
|
||||||
|
std::string showName;
|
||||||
|
std::filesystem::path extension;
|
||||||
|
};
|
||||||
|
|
||||||
static std::string ToLower(const std::string &s);
|
static std::string ToLower(const std::string &s);
|
||||||
|
|
||||||
|
void UpdateFileRecords();
|
||||||
|
|
||||||
void SetPwdUncatched(const std::filesystem::path &pwd);
|
void SetPwdUncatched(const std::filesystem::path &pwd);
|
||||||
|
|
||||||
bool IsExtensionMatched(const std::filesystem::path &extension) const;
|
bool IsExtensionMatched(const std::filesystem::path &extension) const;
|
||||||
@@ -125,6 +144,8 @@ namespace ImGui
|
|||||||
|
|
||||||
int width_;
|
int width_;
|
||||||
int height_;
|
int height_;
|
||||||
|
int posX_;
|
||||||
|
int posY_;
|
||||||
ImGuiFileBrowserFlags flags_;
|
ImGuiFileBrowserFlags flags_;
|
||||||
|
|
||||||
std::string title_;
|
std::string title_;
|
||||||
@@ -134,23 +155,17 @@ namespace ImGui
|
|||||||
bool closeFlag_;
|
bool closeFlag_;
|
||||||
bool isOpened_;
|
bool isOpened_;
|
||||||
bool ok_;
|
bool ok_;
|
||||||
|
bool posIsSet_;
|
||||||
|
|
||||||
std::string statusStr_;
|
std::string statusStr_;
|
||||||
|
|
||||||
std::vector<std::string> typeFilters_;
|
std::vector<std::string> typeFilters_;
|
||||||
int typeFilterIndex_;
|
unsigned int typeFilterIndex_;
|
||||||
bool hasAllFilter_;
|
bool hasAllFilter_;
|
||||||
|
|
||||||
std::filesystem::path pwd_;
|
std::filesystem::path pwd_;
|
||||||
std::set<std::filesystem::path> selectedFilenames_;
|
std::set<std::filesystem::path> selectedFilenames_;
|
||||||
|
|
||||||
struct FileRecord
|
|
||||||
{
|
|
||||||
bool isDir = false;
|
|
||||||
std::filesystem::path name;
|
|
||||||
std::string showName;
|
|
||||||
std::filesystem::path extension;
|
|
||||||
};
|
|
||||||
std::vector<FileRecord> fileRecords_;
|
std::vector<FileRecord> fileRecords_;
|
||||||
|
|
||||||
// IMPROVE: truncate when selectedFilename_.length() > inputNameBuf_.size() - 1
|
// IMPROVE: truncate when selectedFilename_.length() > inputNameBuf_.size() - 1
|
||||||
@@ -167,20 +182,20 @@ namespace ImGui
|
|||||||
} // namespace ImGui
|
} // namespace ImGui
|
||||||
|
|
||||||
inline ImGui::FileBrowser::FileBrowser(ImGuiFileBrowserFlags flags)
|
inline ImGui::FileBrowser::FileBrowser(ImGuiFileBrowserFlags flags)
|
||||||
: width_(700), height_(450), flags_(flags),
|
: width_(700), height_(450), posX_(0), posY_(0), flags_(flags),
|
||||||
openFlag_(false), closeFlag_(false), isOpened_(false), ok_(false),
|
openFlag_(false), closeFlag_(false), isOpened_(false), ok_(false), posIsSet_(false),
|
||||||
inputNameBuf_(std::make_unique<std::array<char, INPUT_NAME_BUF_SIZE>>())
|
inputNameBuf_(std::make_unique<std::array<char, INPUT_NAME_BUF_SIZE>>())
|
||||||
{
|
{
|
||||||
if(flags_ & ImGuiFileBrowserFlags_CreateNewDir)
|
if(flags_ & ImGuiFileBrowserFlags_CreateNewDir)
|
||||||
newDirNameBuf_ = std::make_unique<
|
{
|
||||||
std::array<char, INPUT_NAME_BUF_SIZE>>();
|
newDirNameBuf_ =
|
||||||
|
std::make_unique<std::array<char, INPUT_NAME_BUF_SIZE>>();
|
||||||
|
}
|
||||||
|
|
||||||
inputNameBuf_->front() = '\0';
|
inputNameBuf_->front() = '\0';
|
||||||
inputNameBuf_->back() = '\0';
|
inputNameBuf_->back() = '\0';
|
||||||
SetTitle("File Browser");
|
SetTitle("file browser");
|
||||||
try {
|
|
||||||
SetPwd(std::filesystem::current_path());
|
SetPwd(std::filesystem::current_path());
|
||||||
} catch (std::filesystem::filesystem_error&) {}
|
|
||||||
|
|
||||||
typeFilters_.clear();
|
typeFilters_.clear();
|
||||||
typeFilterIndex_ = 0;
|
typeFilterIndex_ = 0;
|
||||||
@@ -203,6 +218,9 @@ inline ImGui::FileBrowser &ImGui::FileBrowser::operator=(
|
|||||||
width_ = copyFrom.width_;
|
width_ = copyFrom.width_;
|
||||||
height_ = copyFrom.height_;
|
height_ = copyFrom.height_;
|
||||||
|
|
||||||
|
posX_ = copyFrom.posX_;
|
||||||
|
posY_ = copyFrom.posY_;
|
||||||
|
|
||||||
flags_ = copyFrom.flags_;
|
flags_ = copyFrom.flags_;
|
||||||
SetTitle(copyFrom.title_);
|
SetTitle(copyFrom.title_);
|
||||||
|
|
||||||
@@ -210,6 +228,7 @@ inline ImGui::FileBrowser &ImGui::FileBrowser::operator=(
|
|||||||
closeFlag_ = copyFrom.closeFlag_;
|
closeFlag_ = copyFrom.closeFlag_;
|
||||||
isOpened_ = copyFrom.isOpened_;
|
isOpened_ = copyFrom.isOpened_;
|
||||||
ok_ = copyFrom.ok_;
|
ok_ = copyFrom.ok_;
|
||||||
|
posIsSet_ = copyFrom.posIsSet_;
|
||||||
|
|
||||||
statusStr_ = "";
|
statusStr_ = "";
|
||||||
|
|
||||||
@@ -224,6 +243,7 @@ inline ImGui::FileBrowser &ImGui::FileBrowser::operator=(
|
|||||||
|
|
||||||
*inputNameBuf_ = *copyFrom.inputNameBuf_;
|
*inputNameBuf_ = *copyFrom.inputNameBuf_;
|
||||||
|
|
||||||
|
openNewDirLabel_ = copyFrom.openNewDirLabel_;
|
||||||
if(flags_ & ImGuiFileBrowserFlags_CreateNewDir)
|
if(flags_ & ImGuiFileBrowserFlags_CreateNewDir)
|
||||||
{
|
{
|
||||||
newDirNameBuf_ = std::make_unique<
|
newDirNameBuf_ = std::make_unique<
|
||||||
@@ -231,9 +251,20 @@ inline ImGui::FileBrowser &ImGui::FileBrowser::operator=(
|
|||||||
*newDirNameBuf_ = *copyFrom.newDirNameBuf_;
|
*newDirNameBuf_ = *copyFrom.newDirNameBuf_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
drives_ = copyFrom.drives_;
|
||||||
|
#endif
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void ImGui::FileBrowser::SetWindowPos(int posx, int posy) noexcept
|
||||||
|
{
|
||||||
|
posX_ = posx;
|
||||||
|
posY_ = posy;
|
||||||
|
posIsSet_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
inline void ImGui::FileBrowser::SetWindowSize(int width, int height) noexcept
|
inline void ImGui::FileBrowser::SetWindowSize(int width, int height) noexcept
|
||||||
{
|
{
|
||||||
assert(width > 0 && height > 0);
|
assert(width > 0 && height > 0);
|
||||||
@@ -253,6 +284,7 @@ inline void ImGui::FileBrowser::SetTitle(std::string title)
|
|||||||
inline void ImGui::FileBrowser::Open()
|
inline void ImGui::FileBrowser::Open()
|
||||||
{
|
{
|
||||||
ClearSelected();
|
ClearSelected();
|
||||||
|
UpdateFileRecords();
|
||||||
statusStr_ = std::string();
|
statusStr_ = std::string();
|
||||||
openFlag_ = true;
|
openFlag_ = true;
|
||||||
closeFlag_ = false;
|
closeFlag_ = false;
|
||||||
@@ -282,18 +314,27 @@ inline void ImGui::FileBrowser::Display()
|
|||||||
});
|
});
|
||||||
|
|
||||||
if(openFlag_)
|
if(openFlag_)
|
||||||
|
{
|
||||||
OpenPopup(openLabel_.c_str());
|
OpenPopup(openLabel_.c_str());
|
||||||
|
}
|
||||||
isOpened_ = false;
|
isOpened_ = false;
|
||||||
|
|
||||||
// open the popup window
|
// open the popup window
|
||||||
|
|
||||||
if(openFlag_ && (flags_ & ImGuiFileBrowserFlags_NoModal))
|
if(openFlag_ && (flags_ & ImGuiFileBrowserFlags_NoModal))
|
||||||
{
|
{
|
||||||
|
if (posIsSet_)
|
||||||
|
SetNextWindowPos(
|
||||||
|
ImVec2(static_cast<float>(posX_), static_cast<float>(posY_)));
|
||||||
SetNextWindowSize(
|
SetNextWindowSize(
|
||||||
ImVec2(static_cast<float>(width_), static_cast<float>(height_)));
|
ImVec2(static_cast<float>(width_), static_cast<float>(height_)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (posIsSet_)
|
||||||
|
SetNextWindowPos(
|
||||||
|
ImVec2(static_cast<float>(posX_), static_cast<float>(posY_)),
|
||||||
|
ImGuiCond_FirstUseEver);
|
||||||
SetNextWindowSize(
|
SetNextWindowSize(
|
||||||
ImVec2(static_cast<float>(width_), static_cast<float>(height_)),
|
ImVec2(static_cast<float>(width_), static_cast<float>(height_)),
|
||||||
ImGuiCond_FirstUseEver);
|
ImGuiCond_FirstUseEver);
|
||||||
@@ -301,14 +342,17 @@ inline void ImGui::FileBrowser::Display()
|
|||||||
if(flags_ & ImGuiFileBrowserFlags_NoModal)
|
if(flags_ & ImGuiFileBrowserFlags_NoModal)
|
||||||
{
|
{
|
||||||
if(!BeginPopup(openLabel_.c_str()))
|
if(!BeginPopup(openLabel_.c_str()))
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if(!BeginPopupModal(openLabel_.c_str(), nullptr,
|
else if(!BeginPopupModal(openLabel_.c_str(), nullptr,
|
||||||
flags_ & ImGuiFileBrowserFlags_NoTitleBar ?
|
flags_ & ImGuiFileBrowserFlags_NoTitleBar ?
|
||||||
ImGuiWindowFlags_NoTitleBar : 0))
|
ImGuiWindowFlags_NoTitleBar : 0))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
isOpened_ = true;
|
isOpened_ = true;
|
||||||
ScopeGuard endPopup([] { EndPopup(); });
|
ScopeGuard endPopup([] { EndPopup(); });
|
||||||
|
|
||||||
@@ -322,13 +366,18 @@ inline void ImGui::FileBrowser::Display()
|
|||||||
if(BeginCombo("##select_drive", driveStr))
|
if(BeginCombo("##select_drive", driveStr))
|
||||||
{
|
{
|
||||||
ScopeGuard guard([&] { EndCombo(); });
|
ScopeGuard guard([&] { EndCombo(); });
|
||||||
|
|
||||||
for(int i = 0; i < 26; ++i)
|
for(int i = 0; i < 26; ++i)
|
||||||
{
|
{
|
||||||
if(!(drives_ & (1 << i)))
|
if(!(drives_ & (1 << i)))
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
char driveCh = static_cast<char>('A' + i);
|
char driveCh = static_cast<char>('A' + i);
|
||||||
char selectableStr[] = { driveCh, ':', '\0' };
|
char selectableStr[] = { driveCh, ':', '\0' };
|
||||||
bool selected = currentDrive == driveCh;
|
bool selected = currentDrive == driveCh;
|
||||||
|
|
||||||
if(Selectable(selectableStr, selected) && !selected)
|
if(Selectable(selectableStr, selected) && !selected)
|
||||||
{
|
{
|
||||||
char newPwd[] = { driveCh, ':', '\\', '\0' };
|
char newPwd[] = { driveCh, ':', '\\', '\0' };
|
||||||
@@ -342,7 +391,7 @@ inline void ImGui::FileBrowser::Display()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
int secIdx = 0, newPwdLastSecIdx = -1;
|
int secIdx = 0, newPwdLastSecIdx = -1;
|
||||||
for(auto &sec : pwd_)
|
for(const auto &sec : pwd_)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if(secIdx == 1)
|
if(secIdx == 1)
|
||||||
@@ -351,12 +400,18 @@ inline void ImGui::FileBrowser::Display()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
PushID(secIdx);
|
PushID(secIdx);
|
||||||
if(secIdx > 0)
|
if(secIdx > 0)
|
||||||
|
{
|
||||||
SameLine();
|
SameLine();
|
||||||
|
}
|
||||||
if(SmallButton(u8StrToStr(sec.u8string()).c_str()))
|
if(SmallButton(u8StrToStr(sec.u8string()).c_str()))
|
||||||
|
{
|
||||||
newPwdLastSecIdx = secIdx;
|
newPwdLastSecIdx = secIdx;
|
||||||
|
}
|
||||||
PopID();
|
PopID();
|
||||||
|
|
||||||
++secIdx;
|
++secIdx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -364,23 +419,52 @@ inline void ImGui::FileBrowser::Display()
|
|||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
std::filesystem::path newPwd;
|
std::filesystem::path newPwd;
|
||||||
for(auto &sec : pwd_)
|
for(const auto &sec : pwd_)
|
||||||
{
|
{
|
||||||
if(i++ > newPwdLastSecIdx)
|
if(i++ > newPwdLastSecIdx)
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
newPwd /= sec;
|
newPwd /= sec;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if(newPwdLastSecIdx == 0)
|
if(newPwdLastSecIdx == 0)
|
||||||
|
{
|
||||||
newPwd /= "\\";
|
newPwd /= "\\";
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SetPwd(newPwd);
|
SetPwd(newPwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
SameLine();
|
SameLine();
|
||||||
|
|
||||||
if(SmallButton("*"))
|
if(SmallButton("*"))
|
||||||
SetPwd(pwd_);
|
{
|
||||||
|
UpdateFileRecords();
|
||||||
|
|
||||||
|
std::set<std::filesystem::path> newSelectedFilenames;
|
||||||
|
for(auto &name : selectedFilenames_)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(
|
||||||
|
fileRecords_.begin(), fileRecords_.end(),
|
||||||
|
[&](const FileRecord &record)
|
||||||
|
{
|
||||||
|
return name == record.name;
|
||||||
|
});
|
||||||
|
|
||||||
|
if(it != fileRecords_.end())
|
||||||
|
{
|
||||||
|
newSelectedFilenames.insert(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(inputNameBuf_ && (*inputNameBuf_)[0])
|
||||||
|
{
|
||||||
|
newSelectedFilenames.insert(inputNameBuf_->data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(newDirNameBuf_)
|
if(newDirNameBuf_)
|
||||||
{
|
{
|
||||||
@@ -402,7 +486,9 @@ inline void ImGui::FileBrowser::Display()
|
|||||||
{
|
{
|
||||||
ScopeGuard closeNewDirPopup([] { CloseCurrentPopup(); });
|
ScopeGuard closeNewDirPopup([] { CloseCurrentPopup(); });
|
||||||
if(create_directory(pwd_ / newDirNameBuf_->data()))
|
if(create_directory(pwd_ / newDirNameBuf_->data()))
|
||||||
SetPwd(pwd_);
|
{
|
||||||
|
UpdateFileRecords();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
statusStr_ = "failed to create " +
|
statusStr_ = "failed to create " +
|
||||||
@@ -428,10 +514,14 @@ inline void ImGui::FileBrowser::Display()
|
|||||||
for(auto &rsc : fileRecords_)
|
for(auto &rsc : fileRecords_)
|
||||||
{
|
{
|
||||||
if(!rsc.isDir && !IsExtensionMatched(rsc.extension))
|
if(!rsc.isDir && !IsExtensionMatched(rsc.extension))
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if(!rsc.name.empty() && rsc.name.c_str()[0] == '$')
|
if(!rsc.name.empty() && rsc.name.c_str()[0] == '$')
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
bool selected = selectedFilenames_.find(rsc.name)
|
bool selected = selectedFilenames_.find(rsc.name)
|
||||||
!= selectedFilenames_.end();
|
!= selectedFilenames_.end();
|
||||||
@@ -447,9 +537,13 @@ inline void ImGui::FileBrowser::Display()
|
|||||||
if(selected)
|
if(selected)
|
||||||
{
|
{
|
||||||
if(!multiSelect)
|
if(!multiSelect)
|
||||||
|
{
|
||||||
selectedFilenames_.clear();
|
selectedFilenames_.clear();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
selectedFilenames_.erase(rsc.name);
|
selectedFilenames_.erase(rsc.name);
|
||||||
|
}
|
||||||
|
|
||||||
(*inputNameBuf_)[0] = '\0';
|
(*inputNameBuf_)[0] = '\0';
|
||||||
}
|
}
|
||||||
@@ -458,9 +552,15 @@ inline void ImGui::FileBrowser::Display()
|
|||||||
if((rsc.isDir && (flags_ & ImGuiFileBrowserFlags_SelectDirectory)) ||
|
if((rsc.isDir && (flags_ & ImGuiFileBrowserFlags_SelectDirectory)) ||
|
||||||
(!rsc.isDir && !(flags_ & ImGuiFileBrowserFlags_SelectDirectory)))
|
(!rsc.isDir && !(flags_ & ImGuiFileBrowserFlags_SelectDirectory)))
|
||||||
{
|
{
|
||||||
if(!multiSelect)
|
if(multiSelect)
|
||||||
selectedFilenames_.clear();
|
{
|
||||||
selectedFilenames_.insert(rsc.name);
|
selectedFilenames_.insert(rsc.name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
selectedFilenames_ = { rsc.name };
|
||||||
|
}
|
||||||
|
|
||||||
if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory))
|
if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory))
|
||||||
{
|
{
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
@@ -478,9 +578,11 @@ inline void ImGui::FileBrowser::Display()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(!multiSelect)
|
if(!multiSelect)
|
||||||
|
{
|
||||||
selectedFilenames_.clear();
|
selectedFilenames_.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(IsItemClicked(0) && IsMouseDoubleClicked(0))
|
if(IsItemClicked(0) && IsMouseDoubleClicked(0))
|
||||||
{
|
{
|
||||||
@@ -501,7 +603,9 @@ inline void ImGui::FileBrowser::Display()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(setNewPwd)
|
if(setNewPwd)
|
||||||
|
{
|
||||||
SetPwd(newPwd);
|
SetPwd(newPwd);
|
||||||
|
}
|
||||||
|
|
||||||
if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory) &&
|
if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory) &&
|
||||||
(flags_ & ImGuiFileBrowserFlags_EnterNewFilename))
|
(flags_ & ImGuiFileBrowserFlags_EnterNewFilename))
|
||||||
@@ -537,12 +641,15 @@ inline void ImGui::FileBrowser::Display()
|
|||||||
|
|
||||||
SameLine();
|
SameLine();
|
||||||
|
|
||||||
int escIdx = GetIO().KeyMap[ImGuiKey_Escape];
|
bool shouldExit =
|
||||||
if(Button("cancel") || closeFlag_ ||
|
Button("cancel") || closeFlag_ ||
|
||||||
((flags_ & ImGuiFileBrowserFlags_CloseOnEsc) &&
|
((flags_ & ImGuiFileBrowserFlags_CloseOnEsc) &&
|
||||||
IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) &&
|
IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) &&
|
||||||
escIdx >= 0 && IsKeyPressed(escIdx)))
|
IsKeyPressed(ImGuiKey_Escape));
|
||||||
|
if(shouldExit)
|
||||||
|
{
|
||||||
CloseCurrentPopup();
|
CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
|
||||||
if(!statusStr_.empty() && !(flags_ & ImGuiFileBrowserFlags_NoStatusBar))
|
if(!statusStr_.empty() && !(flags_ & ImGuiFileBrowserFlags_NoStatusBar))
|
||||||
{
|
{
|
||||||
@@ -561,9 +668,11 @@ inline void ImGui::FileBrowser::Display()
|
|||||||
|
|
||||||
for(size_t i = 0; i < typeFilters_.size(); ++i)
|
for(size_t i = 0; i < typeFilters_.size(); ++i)
|
||||||
{
|
{
|
||||||
bool selected = static_cast<int>(i) == typeFilterIndex_;
|
bool selected = i == typeFilterIndex_;
|
||||||
if(Selectable(typeFilters_[i].c_str(), selected) && !selected)
|
if(Selectable(typeFilters_[i].c_str(), selected) && !selected)
|
||||||
typeFilterIndex_ = static_cast<int>(i);
|
{
|
||||||
|
typeFilterIndex_ = static_cast<unsigned int>(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PopItemWidth();
|
PopItemWidth();
|
||||||
@@ -605,7 +714,9 @@ inline std::filesystem::path ImGui::FileBrowser::GetSelected() const
|
|||||||
// when ok_ is true, selectedFilenames_ may be empty if SelectDirectory
|
// when ok_ is true, selectedFilenames_ may be empty if SelectDirectory
|
||||||
// is enabled. return pwd in that case.
|
// is enabled. return pwd in that case.
|
||||||
if(selectedFilenames_.empty())
|
if(selectedFilenames_.empty())
|
||||||
|
{
|
||||||
return pwd_;
|
return pwd_;
|
||||||
|
}
|
||||||
return pwd_ / *selectedFilenames_.begin();
|
return pwd_ / *selectedFilenames_.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -613,11 +724,17 @@ inline std::vector<std::filesystem::path>
|
|||||||
ImGui::FileBrowser::GetMultiSelected() const
|
ImGui::FileBrowser::GetMultiSelected() const
|
||||||
{
|
{
|
||||||
if(selectedFilenames_.empty())
|
if(selectedFilenames_.empty())
|
||||||
|
{
|
||||||
return { pwd_ };
|
return { pwd_ };
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::filesystem::path> ret;
|
std::vector<std::filesystem::path> ret;
|
||||||
ret.reserve(selectedFilenames_.size());
|
ret.reserve(selectedFilenames_.size());
|
||||||
for(auto &s : selectedFilenames_)
|
for(auto &s : selectedFilenames_)
|
||||||
|
{
|
||||||
ret.push_back(pwd_ / s);
|
ret.push_back(pwd_ / s);
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -641,10 +758,12 @@ inline void ImGui::FileBrowser::SetTypeFilters(
|
|||||||
for(auto &rawFilter : _typeFilters)
|
for(auto &rawFilter : _typeFilters)
|
||||||
{
|
{
|
||||||
std::string lowerFilter = ToLower(rawFilter);
|
std::string lowerFilter = ToLower(rawFilter);
|
||||||
if(std::find(typeFilters.begin(), typeFilters.end(), lowerFilter) ==
|
auto it = std::find(typeFilters.begin(), typeFilters.end(), lowerFilter);
|
||||||
typeFilters.end())
|
if(it == typeFilters.end())
|
||||||
|
{
|
||||||
typeFilters.push_back(std::move(lowerFilter));
|
typeFilters.push_back(std::move(lowerFilter));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
@@ -667,13 +786,17 @@ inline void ImGui::FileBrowser::SetTypeFilters(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(i > 0)
|
if(i > 0)
|
||||||
|
{
|
||||||
allFiltersName += ",";
|
allFiltersName += ",";
|
||||||
|
}
|
||||||
allFiltersName += typeFilters[i];
|
allFiltersName += typeFilters[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
if(hasAllFilter_)
|
if(hasAllFilter_)
|
||||||
|
{
|
||||||
typeFilters_.push_back(std::move(allFiltersName));
|
typeFilters_.push_back(std::move(allFiltersName));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::copy(
|
std::copy(
|
||||||
typeFilters.begin(), typeFilters.end(),
|
typeFilters.begin(), typeFilters.end(),
|
||||||
@@ -684,35 +807,60 @@ inline void ImGui::FileBrowser::SetTypeFilters(
|
|||||||
|
|
||||||
inline void ImGui::FileBrowser::SetCurrentTypeFilterIndex(int index)
|
inline void ImGui::FileBrowser::SetCurrentTypeFilterIndex(int index)
|
||||||
{
|
{
|
||||||
typeFilterIndex_ = index;
|
typeFilterIndex_ = static_cast<unsigned int>(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ImGui::FileBrowser::SetInputName(std::string_view input)
|
||||||
|
{
|
||||||
|
if(flags_ & ImGuiFileBrowserFlags_EnterNewFilename)
|
||||||
|
{
|
||||||
|
if(input.size() >= static_cast<size_t>(INPUT_NAME_BUF_SIZE))
|
||||||
|
{
|
||||||
|
// If input doesn't fit trim off characters
|
||||||
|
input = input.substr(0, INPUT_NAME_BUF_SIZE - 1);
|
||||||
|
}
|
||||||
|
std::copy(input.begin(), input.end(), inputNameBuf_->begin());
|
||||||
|
inputNameBuf_->at(input.size()) = '\0';
|
||||||
|
selectedFilenames_ = { inputNameBuf_->data() };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string ImGui::FileBrowser::ToLower(const std::string &s)
|
inline std::string ImGui::FileBrowser::ToLower(const std::string &s)
|
||||||
{
|
{
|
||||||
std::string ret = s;
|
std::string ret = s;
|
||||||
for(char &c : ret)
|
for(char &c : ret)
|
||||||
|
{
|
||||||
c = static_cast<char>(std::tolower(c));
|
c = static_cast<char>(std::tolower(c));
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void ImGui::FileBrowser::SetPwdUncatched(const std::filesystem::path &pwd)
|
inline void ImGui::FileBrowser::UpdateFileRecords()
|
||||||
{
|
{
|
||||||
fileRecords_ = { FileRecord{ true, "..", "[D] ..", "" } };
|
fileRecords_ = { FileRecord{ true, "..", "[D] ..", "" } };
|
||||||
|
|
||||||
for(auto &p : std::filesystem::directory_iterator(pwd))
|
for(auto &p : std::filesystem::directory_iterator(pwd_))
|
||||||
{
|
{
|
||||||
FileRecord rcd;
|
FileRecord rcd;
|
||||||
|
|
||||||
if(p.is_regular_file())
|
if(p.is_regular_file())
|
||||||
|
{
|
||||||
rcd.isDir = false;
|
rcd.isDir = false;
|
||||||
|
}
|
||||||
else if(p.is_directory())
|
else if(p.is_directory())
|
||||||
|
{
|
||||||
rcd.isDir = true;
|
rcd.isDir = true;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
rcd.name = p.path().filename();
|
rcd.name = p.path().filename();
|
||||||
if(rcd.name.empty())
|
if(rcd.name.empty())
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
rcd.extension = p.path().filename().extension();
|
rcd.extension = p.path().filename().extension();
|
||||||
|
|
||||||
@@ -726,8 +874,12 @@ inline void ImGui::FileBrowser::SetPwdUncatched(const std::filesystem::path &pwd
|
|||||||
{
|
{
|
||||||
return (L.isDir ^ R.isDir) ? L.isDir : (L.name < R.name);
|
return (L.isDir ^ R.isDir) ? L.isDir : (L.name < R.name);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ImGui::FileBrowser::SetPwdUncatched(const std::filesystem::path &pwd)
|
||||||
|
{
|
||||||
pwd_ = absolute(pwd);
|
pwd_ = absolute(pwd);
|
||||||
|
UpdateFileRecords();
|
||||||
selectedFilenames_.clear();
|
selectedFilenames_.clear();
|
||||||
(*inputNameBuf_)[0] = '\0';
|
(*inputNameBuf_)[0] = '\0';
|
||||||
}
|
}
|
||||||
@@ -743,11 +895,15 @@ inline bool ImGui::FileBrowser::IsExtensionMatched(
|
|||||||
|
|
||||||
// no type filters
|
// no type filters
|
||||||
if(typeFilters_.empty())
|
if(typeFilters_.empty())
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// invalid type filter index
|
// invalid type filter index
|
||||||
if(static_cast<size_t>(typeFilterIndex_) >= typeFilters_.size())
|
if(static_cast<size_t>(typeFilterIndex_) >= typeFilters_.size())
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// all type filters
|
// all type filters
|
||||||
if(hasAllFilter_ && typeFilterIndex_ == 0)
|
if(hasAllFilter_ && typeFilterIndex_ == 0)
|
||||||
@@ -755,14 +911,18 @@ inline bool ImGui::FileBrowser::IsExtensionMatched(
|
|||||||
for(size_t i = 1; i < typeFilters_.size(); ++i)
|
for(size_t i = 1; i < typeFilters_.size(); ++i)
|
||||||
{
|
{
|
||||||
if(extension == typeFilters_[i])
|
if(extension == typeFilters_[i])
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// universal filter
|
// universal filter
|
||||||
if(typeFilters_[typeFilterIndex_] == std::string_view(".*"))
|
if(typeFilters_[typeFilterIndex_] == std::string_view(".*"))
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// regular filter
|
// regular filter
|
||||||
return extension == typeFilters_[typeFilterIndex_];
|
return extension == typeFilters_[typeFilterIndex_];
|
||||||
@@ -807,12 +967,16 @@ inline std::uint32_t ImGui::FileBrowser::GetDrivesBitMask()
|
|||||||
for(int i = 0; i < 26; ++i)
|
for(int i = 0; i < 26; ++i)
|
||||||
{
|
{
|
||||||
if(!(mask & (1 << i)))
|
if(!(mask & (1 << i)))
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
char rootName[4] = { static_cast<char>('A' + i), ':', '\\', '\0' };
|
char rootName[4] = { static_cast<char>('A' + i), ':', '\\', '\0' };
|
||||||
UINT type = GetDriveTypeA(rootName);
|
UINT type = GetDriveTypeA(rootName);
|
||||||
if(type == DRIVE_REMOVABLE || type == DRIVE_FIXED)
|
if(type == DRIVE_REMOVABLE || type == DRIVE_FIXED || type == DRIVE_REMOTE)
|
||||||
|
{
|
||||||
ret |= (1 << i);
|
ret |= (1 << i);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
+643
-291
File diff suppressed because it is too large
Load Diff
Vendored
+140
-138
@@ -1,16 +1,20 @@
|
|||||||
// Mini memory editor for Dear ImGui (to embed in your game/tools)
|
// Mini memory editor for Dear ImGui (to embed in your game/tools)
|
||||||
// Animated GIF: https://twitter.com/ocornut/status/894242704317530112
|
|
||||||
// Get latest version at http://www.github.com/ocornut/imgui_club
|
// Get latest version at http://www.github.com/ocornut/imgui_club
|
||||||
//
|
//
|
||||||
// Right-click anywhere to access the Options menu!
|
// Right-click anywhere to access the Options menu!
|
||||||
// You can adjust the keyboard repeat delay/rate in ImGuiIO.
|
// You can adjust the keyboard repeat delay/rate in ImGuiIO.
|
||||||
// The code assume a mono-space font for simplicity! If you don't use the default font, use ImGui::PushFont()/PopFont() to switch to a mono-space font before caling this.
|
// The code assume a mono-space font for simplicity!
|
||||||
|
// If you don't use the default font, use ImGui::PushFont()/PopFont() to switch to a mono-space font before calling this.
|
||||||
//
|
//
|
||||||
// Usage:
|
// Usage:
|
||||||
// static MemoryEditor mem_edit_1; // store your state somewhere
|
// // Create a window and draw memory editor inside it:
|
||||||
// mem_edit_1.DrawWindow("Memory Editor", mem_block, mem_block_size, 0x0000); // create a window and draw memory editor (if you already have a window, use DrawContents())
|
// static MemoryEditor mem_edit_1;
|
||||||
|
// static char data[0x10000];
|
||||||
|
// size_t data_size = 0x10000;
|
||||||
|
// mem_edit_1.DrawWindow("Memory Editor", data, data_size);
|
||||||
//
|
//
|
||||||
// Usage:
|
// Usage:
|
||||||
|
// // If you already have a window, use DrawContents() instead:
|
||||||
// static MemoryEditor mem_edit_2;
|
// static MemoryEditor mem_edit_2;
|
||||||
// ImGui::Begin("MyWindow")
|
// ImGui::Begin("MyWindow")
|
||||||
// mem_edit_2.DrawContents(this, sizeof(*this), (size_t)this);
|
// mem_edit_2.DrawContents(this, sizeof(*this), (size_t)this);
|
||||||
@@ -18,33 +22,32 @@
|
|||||||
//
|
//
|
||||||
// Changelog:
|
// Changelog:
|
||||||
// - v0.10: initial version
|
// - v0.10: initial version
|
||||||
// - v0.11: always refresh active text input with the latest byte from source memory if it's not being edited.
|
// - v0.23 (2017/08/17): added to github. fixed right-arrow triggering a byte write.
|
||||||
// - v0.12: added OptMidRowsCount to allow extra spacing every XX rows.
|
// - v0.24 (2018/06/02): changed DragInt("Rows" to use a %d data format (which is desirable since imgui 1.61).
|
||||||
// - v0.13: added optional ReadFn/WriteFn handlers to access memory via a function. various warning fixes for 64-bits.
|
// - v0.25 (2018/07/11): fixed wording: all occurrences of "Rows" renamed to "Columns".
|
||||||
// - v0.14: added GotoAddr member, added GotoAddrAndHighlight() and highlighting. fixed minor scrollbar glitch when resizing.
|
// - v0.26 (2018/08/02): fixed clicking on hex region
|
||||||
// - v0.15: added maximum window width. minor optimization.
|
// - v0.30 (2018/08/02): added data preview for common data types
|
||||||
// - v0.16: added OptGreyOutZeroes option. various sizing fixes when resizing using the "Rows" drag.
|
// - v0.31 (2018/10/10): added OptUpperCaseHex option to select lower/upper casing display [@samhocevar]
|
||||||
// - v0.17: added HighlightFn handler for optional non-contiguous highlighting.
|
// - v0.32 (2018/10/10): changed signatures to use void* instead of unsigned char*
|
||||||
// - v0.18: fixes for displaying 64-bits addresses, fixed mouse click gaps introduced in recent changes, cursor tracking scrolling fixes.
|
// - v0.33 (2018/10/10): added OptShowOptions option to hide all the interactive option setting.
|
||||||
// - v0.19: fixed auto-focus of next byte leaving WantCaptureKeyboard=false for one frame. we now capture the keyboard during that transition.
|
// - v0.34 (2019/05/07): binary preview now applies endianness setting [@nicolasnoble]
|
||||||
// - v0.20: added options menu. added OptShowAscii checkbox. added optional HexII display. split Draw() in DrawWindow()/DrawContents(). fixing glyph width. refactoring/cleaning code.
|
// - v0.35 (2020/01/29): using ImGuiDataType available since Dear ImGui 1.69.
|
||||||
// - v0.21: fixes for using DrawContents() in our own window. fixed HexII to actually be useful and not on the wrong side.
|
// - v0.36 (2020/05/05): minor tweaks, minor refactor.
|
||||||
// - v0.22: clicking Ascii view select the byte in the Hex view. Ascii view highlight selection.
|
// - v0.40 (2020/10/04): fix misuse of ImGuiListClipper API, broke with Dear ImGui 1.79. made cursor position appears on left-side of edit box. option popup appears on mouse release. fix MSVC warnings where _CRT_SECURE_NO_WARNINGS wasn't working in recent versions.
|
||||||
// - v0.23: fixed right-arrow triggering a byte write.
|
// - v0.41 (2020/10/05): fix when using with keyboard/gamepad navigation enabled.
|
||||||
// - v0.24: changed DragInt("Rows" to use a %d data format (which is desirable since imgui 1.61).
|
// - v0.42 (2020/10/14): fix for . character in ASCII view always being greyed out.
|
||||||
// - v0.25: fixed wording: all occurrences of "Rows" renamed to "Columns".
|
// - v0.43 (2021/03/12): added OptFooterExtraHeight to allow for custom drawing at the bottom of the editor [@leiradel]
|
||||||
// - v0.26: fixed clicking on hex region
|
// - v0.44 (2021/03/12): use ImGuiInputTextFlags_AlwaysOverwrite in 1.82 + fix hardcoded width.
|
||||||
// - v0.30: added data preview for common data types
|
// - v0.50 (2021/11/12): various fixes for recent dear imgui versions (fixed misuse of clipper, relying on SetKeyboardFocusHere() handling scrolling from 1.85). added default size.
|
||||||
// - v0.31: added OptUpperCaseHex option to select lower/upper casing display [@samhocevar]
|
|
||||||
// - v0.32: changed signatures to use void* instead of unsigned char*
|
|
||||||
// - v0.33: added OptShowOptions option to hide all the interactive option setting.
|
|
||||||
// - v0.34: binary preview now applies endianess setting [@nicolasnoble]
|
|
||||||
//
|
//
|
||||||
// Todo/Bugs:
|
// Todo/Bugs:
|
||||||
|
// - This is generally old/crappy code, it should work but isn't very good.. to be rewritten some day.
|
||||||
|
// - PageUp/PageDown are supported because we use _NoNav. This is a good test scenario for working out idioms of how to mix natural nav and our own...
|
||||||
// - Arrows are being sent to the InputText() about to disappear which for LeftArrow makes the text cursor appear at position 1 for one frame.
|
// - Arrows are being sent to the InputText() about to disappear which for LeftArrow makes the text cursor appear at position 1 for one frame.
|
||||||
// - Using InputText() is awkward and maybe overkill here, consider implementing something custom.
|
// - Using InputText() is awkward and maybe overkill here, consider implementing something custom.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdio.h> // sprintf, scanf
|
#include <stdio.h> // sprintf, scanf
|
||||||
#include <stdint.h> // uint8_t, etc.
|
#include <stdint.h> // uint8_t, etc.
|
||||||
|
|
||||||
@@ -56,25 +59,13 @@
|
|||||||
#define ImSnprintf snprintf
|
#define ImSnprintf snprintf
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning (push)
|
||||||
|
#pragma warning (disable: 4996) // warning C4996: 'sprintf': This function or variable may be unsafe.
|
||||||
|
#endif
|
||||||
|
|
||||||
struct MemoryEditor
|
struct MemoryEditor
|
||||||
{
|
{
|
||||||
typedef unsigned char u8;
|
|
||||||
|
|
||||||
enum DataType
|
|
||||||
{
|
|
||||||
DataType_S8,
|
|
||||||
DataType_U8,
|
|
||||||
DataType_S16,
|
|
||||||
DataType_U16,
|
|
||||||
DataType_S32,
|
|
||||||
DataType_U32,
|
|
||||||
DataType_S64,
|
|
||||||
DataType_U64,
|
|
||||||
DataType_Float,
|
|
||||||
DataType_Double,
|
|
||||||
DataType_COUNT
|
|
||||||
};
|
|
||||||
|
|
||||||
enum DataFormat
|
enum DataFormat
|
||||||
{
|
{
|
||||||
DataFormat_Bin = 0,
|
DataFormat_Bin = 0,
|
||||||
@@ -95,10 +86,11 @@ struct MemoryEditor
|
|||||||
bool OptUpperCaseHex; // = true // display hexadecimal values as "FF" instead of "ff".
|
bool OptUpperCaseHex; // = true // display hexadecimal values as "FF" instead of "ff".
|
||||||
int OptMidColsCount; // = 8 // set to 0 to disable extra spacing between every mid-cols.
|
int OptMidColsCount; // = 8 // set to 0 to disable extra spacing between every mid-cols.
|
||||||
int OptAddrDigitsCount; // = 0 // number of addr digits to display (default calculated based on maximum displayed addr).
|
int OptAddrDigitsCount; // = 0 // number of addr digits to display (default calculated based on maximum displayed addr).
|
||||||
|
float OptFooterExtraHeight; // = 0 // space to reserve at the bottom of the widget to add custom widgets
|
||||||
ImU32 HighlightColor; // // background color of highlighted bytes.
|
ImU32 HighlightColor; // // background color of highlighted bytes.
|
||||||
u8 (*ReadFn)(const u8* data, size_t off); // = NULL // optional handler to read bytes.
|
ImU8 (*ReadFn)(const ImU8* data, size_t off); // = 0 // optional handler to read bytes.
|
||||||
void (*WriteFn)(u8* data, size_t off, u8 d); // = NULL // optional handler to write bytes.
|
void (*WriteFn)(ImU8* data, size_t off, ImU8 d); // = 0 // optional handler to write bytes.
|
||||||
bool (*HighlightFn)(const u8* data, size_t off);//NULL // optional handler to return Highlight property (to support non-contiguous highlighting).
|
bool (*HighlightFn)(const ImU8* data, size_t off);//= 0 // optional handler to return Highlight property (to support non-contiguous highlighting).
|
||||||
|
|
||||||
// [Internal State]
|
// [Internal State]
|
||||||
bool ContentsWidthChanged;
|
bool ContentsWidthChanged;
|
||||||
@@ -110,7 +102,7 @@ struct MemoryEditor
|
|||||||
size_t GotoAddr;
|
size_t GotoAddr;
|
||||||
size_t HighlightMin, HighlightMax;
|
size_t HighlightMin, HighlightMax;
|
||||||
int PreviewEndianess;
|
int PreviewEndianess;
|
||||||
DataType PreviewDataType;
|
ImGuiDataType PreviewDataType;
|
||||||
|
|
||||||
MemoryEditor()
|
MemoryEditor()
|
||||||
{
|
{
|
||||||
@@ -126,6 +118,7 @@ struct MemoryEditor
|
|||||||
OptUpperCaseHex = true;
|
OptUpperCaseHex = true;
|
||||||
OptMidColsCount = 8;
|
OptMidColsCount = 8;
|
||||||
OptAddrDigitsCount = 0;
|
OptAddrDigitsCount = 0;
|
||||||
|
OptFooterExtraHeight = 0.0f;
|
||||||
HighlightColor = IM_COL32(255, 255, 255, 50);
|
HighlightColor = IM_COL32(255, 255, 255, 50);
|
||||||
ReadFn = NULL;
|
ReadFn = NULL;
|
||||||
WriteFn = NULL;
|
WriteFn = NULL;
|
||||||
@@ -140,7 +133,7 @@ struct MemoryEditor
|
|||||||
GotoAddr = (size_t)-1;
|
GotoAddr = (size_t)-1;
|
||||||
HighlightMin = HighlightMax = (size_t)-1;
|
HighlightMin = HighlightMax = (size_t)-1;
|
||||||
PreviewEndianess = 0;
|
PreviewEndianess = 0;
|
||||||
PreviewDataType = DataType_S32;
|
PreviewDataType = ImGuiDataType_S32;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GotoAddrAndHighlight(size_t addr_min, size_t addr_max)
|
void GotoAddrAndHighlight(size_t addr_min, size_t addr_max)
|
||||||
@@ -162,6 +155,8 @@ struct MemoryEditor
|
|||||||
float PosAsciiStart;
|
float PosAsciiStart;
|
||||||
float PosAsciiEnd;
|
float PosAsciiEnd;
|
||||||
float WindowWidth;
|
float WindowWidth;
|
||||||
|
|
||||||
|
Sizes() { memset(this, 0, sizeof(*this)); }
|
||||||
};
|
};
|
||||||
|
|
||||||
void CalcSizes(Sizes& s, size_t mem_size, size_t base_display_addr)
|
void CalcSizes(Sizes& s, size_t mem_size, size_t base_display_addr)
|
||||||
@@ -193,12 +188,13 @@ struct MemoryEditor
|
|||||||
{
|
{
|
||||||
Sizes s;
|
Sizes s;
|
||||||
CalcSizes(s, mem_size, base_display_addr);
|
CalcSizes(s, mem_size, base_display_addr);
|
||||||
|
ImGui::SetNextWindowSize(ImVec2(s.WindowWidth, s.WindowWidth * 0.60f), ImGuiCond_FirstUseEver);
|
||||||
ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f), ImVec2(s.WindowWidth, FLT_MAX));
|
ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f), ImVec2(s.WindowWidth, FLT_MAX));
|
||||||
|
|
||||||
Open = true;
|
Open = true;
|
||||||
if (ImGui::Begin(title, &Open, ImGuiWindowFlags_NoScrollbar))
|
if (ImGui::Begin(title, &Open, ImGuiWindowFlags_NoScrollbar))
|
||||||
{
|
{
|
||||||
if (ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows) && ImGui::IsMouseClicked(1))
|
if (ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows) && ImGui::IsMouseReleased(ImGuiMouseButton_Right))
|
||||||
ImGui::OpenPopup("context");
|
ImGui::OpenPopup("context");
|
||||||
DrawContents(mem_data, mem_size, base_display_addr);
|
DrawContents(mem_data, mem_size, base_display_addr);
|
||||||
if (ContentsWidthChanged)
|
if (ContentsWidthChanged)
|
||||||
@@ -211,9 +207,12 @@ struct MemoryEditor
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Memory Editor contents only
|
// Memory Editor contents only
|
||||||
void DrawContents(void* mem_data_void_ptr, size_t mem_size, size_t base_display_addr = 0x0000)
|
void DrawContents(void* mem_data_void, size_t mem_size, size_t base_display_addr = 0x0000)
|
||||||
{
|
{
|
||||||
u8* mem_data = (u8*)mem_data_void_ptr;
|
if (Cols < 1)
|
||||||
|
Cols = 1;
|
||||||
|
|
||||||
|
ImU8* mem_data = (ImU8*)mem_data_void;
|
||||||
Sizes s;
|
Sizes s;
|
||||||
CalcSizes(s, mem_size, base_display_addr);
|
CalcSizes(s, mem_size, base_display_addr);
|
||||||
ImGuiStyle& style = ImGui::GetStyle();
|
ImGuiStyle& style = ImGui::GetStyle();
|
||||||
@@ -221,21 +220,21 @@ struct MemoryEditor
|
|||||||
// We begin into our scrolling region with the 'ImGuiWindowFlags_NoMove' in order to prevent click from moving the window.
|
// We begin into our scrolling region with the 'ImGuiWindowFlags_NoMove' in order to prevent click from moving the window.
|
||||||
// This is used as a facility since our main click detection code doesn't assign an ActiveId so the click would normally be caught as a window-move.
|
// This is used as a facility since our main click detection code doesn't assign an ActiveId so the click would normally be caught as a window-move.
|
||||||
const float height_separator = style.ItemSpacing.y;
|
const float height_separator = style.ItemSpacing.y;
|
||||||
float footer_height = 0;
|
float footer_height = OptFooterExtraHeight;
|
||||||
if (OptShowOptions)
|
if (OptShowOptions)
|
||||||
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1;
|
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1;
|
||||||
if (OptShowDataPreview)
|
if (OptShowDataPreview)
|
||||||
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1 + ImGui::GetTextLineHeightWithSpacing() * 3;
|
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1 + ImGui::GetTextLineHeightWithSpacing() * 3;
|
||||||
ImGui::BeginChild("##scrolling", ImVec2(0, -footer_height), false, ImGuiWindowFlags_NoMove);
|
ImGui::BeginChild("##scrolling", ImVec2(0, -footer_height), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
||||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||||
|
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
||||||
|
|
||||||
|
// We are not really using the clipper API correctly here, because we rely on visible_start_addr/visible_end_addr for our scrolling function.
|
||||||
const int line_total_count = (int)((mem_size + Cols - 1) / Cols);
|
const int line_total_count = (int)((mem_size + Cols - 1) / Cols);
|
||||||
ImGuiListClipper clipper(line_total_count, s.LineHeight);
|
ImGuiListClipper clipper;
|
||||||
const size_t visible_start_addr = clipper.DisplayStart * Cols;
|
clipper.Begin(line_total_count, s.LineHeight);
|
||||||
const size_t visible_end_addr = clipper.DisplayEnd * Cols;
|
|
||||||
|
|
||||||
bool data_next = false;
|
bool data_next = false;
|
||||||
|
|
||||||
@@ -246,23 +245,14 @@ struct MemoryEditor
|
|||||||
|
|
||||||
size_t preview_data_type_size = OptShowDataPreview ? DataTypeGetSize(PreviewDataType) : 0;
|
size_t preview_data_type_size = OptShowDataPreview ? DataTypeGetSize(PreviewDataType) : 0;
|
||||||
|
|
||||||
size_t data_editing_addr_backup = DataEditingAddr;
|
|
||||||
size_t data_editing_addr_next = (size_t)-1;
|
size_t data_editing_addr_next = (size_t)-1;
|
||||||
if (DataEditingAddr != (size_t)-1)
|
if (DataEditingAddr != (size_t)-1)
|
||||||
{
|
{
|
||||||
// Move cursor but only apply on next frame so scrolling with be synchronized (because currently we can't change the scrolling while the window is being rendered)
|
// Move cursor but only apply on next frame so scrolling with be synchronized (because currently we can't change the scrolling while the window is being rendered)
|
||||||
if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)) && DataEditingAddr >= (size_t)Cols) { data_editing_addr_next = DataEditingAddr - Cols; DataEditingTakeFocus = true; }
|
if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)) && (ptrdiff_t)DataEditingAddr >= (ptrdiff_t)Cols) { data_editing_addr_next = DataEditingAddr - Cols; }
|
||||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)) && DataEditingAddr < mem_size - Cols) { data_editing_addr_next = DataEditingAddr + Cols; DataEditingTakeFocus = true; }
|
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - Cols) { data_editing_addr_next = DataEditingAddr + Cols; }
|
||||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow)) && DataEditingAddr > 0) { data_editing_addr_next = DataEditingAddr - 1; DataEditingTakeFocus = true; }
|
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow)) && (ptrdiff_t)DataEditingAddr > (ptrdiff_t)0) { data_editing_addr_next = DataEditingAddr - 1; }
|
||||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow)) && DataEditingAddr < mem_size - 1) { data_editing_addr_next = DataEditingAddr + 1; DataEditingTakeFocus = true; }
|
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow)) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - 1) { data_editing_addr_next = DataEditingAddr + 1; }
|
||||||
}
|
|
||||||
if (data_editing_addr_next != (size_t)-1 && (data_editing_addr_next / Cols) != (data_editing_addr_backup / Cols))
|
|
||||||
{
|
|
||||||
// Track cursor movements
|
|
||||||
const int scroll_offset = ((int)(data_editing_addr_next / Cols) - (int)(data_editing_addr_backup / Cols));
|
|
||||||
const bool scroll_desired = (scroll_offset < 0 && data_editing_addr_next < visible_start_addr + Cols * 2) || (scroll_offset > 0 && data_editing_addr_next > visible_end_addr - Cols * 2);
|
|
||||||
if (scroll_desired)
|
|
||||||
ImGui::SetScrollY(ImGui::GetScrollY() + scroll_offset * s.LineHeight);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw vertical separator
|
// Draw vertical separator
|
||||||
@@ -275,19 +265,14 @@ struct MemoryEditor
|
|||||||
|
|
||||||
const char* format_address = OptUpperCaseHex ? "%0*" _PRISizeT "X: " : "%0*" _PRISizeT "x: ";
|
const char* format_address = OptUpperCaseHex ? "%0*" _PRISizeT "X: " : "%0*" _PRISizeT "x: ";
|
||||||
const char* format_data = OptUpperCaseHex ? "%0*" _PRISizeT "X" : "%0*" _PRISizeT "x";
|
const char* format_data = OptUpperCaseHex ? "%0*" _PRISizeT "X" : "%0*" _PRISizeT "x";
|
||||||
const char* format_range = OptUpperCaseHex ? "Range %0*" _PRISizeT "X..%0*" _PRISizeT "X" : "Range %0*" _PRISizeT "x..%0*" _PRISizeT "x";
|
|
||||||
const char* format_byte = OptUpperCaseHex ? "%02X" : "%02x";
|
const char* format_byte = OptUpperCaseHex ? "%02X" : "%02x";
|
||||||
const char* format_byte_space = OptUpperCaseHex ? "%02X " : "%02x ";
|
const char* format_byte_space = OptUpperCaseHex ? "%02X " : "%02x ";
|
||||||
|
|
||||||
|
while (clipper.Step())
|
||||||
for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd; line_i++) // display only visible lines
|
for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd; line_i++) // display only visible lines
|
||||||
{
|
{
|
||||||
size_t addr = (size_t)(line_i * Cols);
|
size_t addr = (size_t)(line_i * Cols);
|
||||||
|
ImGui::Text(format_address, s.AddrDigitsCount, base_display_addr + addr);
|
||||||
// format fix
|
|
||||||
char addr_str_buf[512] {};
|
|
||||||
snprintf(addr_str_buf, sizeof(addr_str_buf), format_address, s.AddrDigitsCount, base_display_addr + addr);
|
|
||||||
|
|
||||||
ImGui::TextUnformatted(addr_str_buf);
|
|
||||||
|
|
||||||
// Draw Hexadecimal
|
// Draw Hexadecimal
|
||||||
for (int n = 0; n < Cols && addr < mem_size; n++, addr++)
|
for (int n = 0; n < Cols && addr < mem_size; n++, addr++)
|
||||||
@@ -322,12 +307,10 @@ struct MemoryEditor
|
|||||||
ImGui::PushID((void*)addr);
|
ImGui::PushID((void*)addr);
|
||||||
if (DataEditingTakeFocus)
|
if (DataEditingTakeFocus)
|
||||||
{
|
{
|
||||||
ImGui::SetKeyboardFocusHere();
|
ImGui::SetKeyboardFocusHere(0);
|
||||||
ImGui::CaptureKeyboardFromApp(true);
|
|
||||||
sprintf(AddrInputBuf, format_data, s.AddrDigitsCount, base_display_addr + addr);
|
sprintf(AddrInputBuf, format_data, s.AddrDigitsCount, base_display_addr + addr);
|
||||||
sprintf(DataInputBuf, format_byte, ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]);
|
sprintf(DataInputBuf, format_byte, ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]);
|
||||||
}
|
}
|
||||||
ImGui::PushItemWidth(s.GlyphWidth * 2);
|
|
||||||
struct UserData
|
struct UserData
|
||||||
{
|
{
|
||||||
// FIXME: We should have a way to retrieve the text edit cursor position more easily in the API, this is rather tedious. This is such a ugly mess we may be better off not using InputText() at all here.
|
// FIXME: We should have a way to retrieve the text edit cursor position more easily in the API, this is rather tedious. This is such a ugly mess we may be better off not using InputText() at all here.
|
||||||
@@ -338,11 +321,13 @@ struct MemoryEditor
|
|||||||
user_data->CursorPos = data->CursorPos;
|
user_data->CursorPos = data->CursorPos;
|
||||||
if (data->SelectionStart == 0 && data->SelectionEnd == data->BufTextLen)
|
if (data->SelectionStart == 0 && data->SelectionEnd == data->BufTextLen)
|
||||||
{
|
{
|
||||||
// When not editing a byte, always rewrite its content (this is a bit tricky, since InputText technically "owns" the master copy of the buffer we edit it in there)
|
// When not editing a byte, always refresh its InputText content pulled from underlying memory data
|
||||||
|
// (this is a bit tricky, since InputText technically "owns" the master copy of the buffer we edit it in there)
|
||||||
data->DeleteChars(0, data->BufTextLen);
|
data->DeleteChars(0, data->BufTextLen);
|
||||||
data->InsertChars(0, user_data->CurrentBufOverwrite);
|
data->InsertChars(0, user_data->CurrentBufOverwrite);
|
||||||
data->SelectionStart = 0;
|
data->SelectionStart = 0;
|
||||||
data->SelectionEnd = data->CursorPos = 2;
|
data->SelectionEnd = 2;
|
||||||
|
data->CursorPos = 0;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -352,13 +337,18 @@ struct MemoryEditor
|
|||||||
UserData user_data;
|
UserData user_data;
|
||||||
user_data.CursorPos = -1;
|
user_data.CursorPos = -1;
|
||||||
sprintf(user_data.CurrentBufOverwrite, format_byte, ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]);
|
sprintf(user_data.CurrentBufOverwrite, format_byte, ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]);
|
||||||
ImGuiInputTextFlags flags = ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoHorizontalScroll | ImGuiInputTextFlags_AlwaysInsertMode | ImGuiInputTextFlags_CallbackAlways;
|
ImGuiInputTextFlags flags = ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoHorizontalScroll | ImGuiInputTextFlags_CallbackAlways;
|
||||||
if (ImGui::InputText("##data", DataInputBuf, 32, flags, UserData::Callback, &user_data))
|
#if IMGUI_VERSION_NUM >= 18104
|
||||||
|
flags |= ImGuiInputTextFlags_AlwaysOverwrite;
|
||||||
|
#else
|
||||||
|
flags |= ImGuiInputTextFlags_AlwaysInsertMode;
|
||||||
|
#endif
|
||||||
|
ImGui::SetNextItemWidth(s.GlyphWidth * 2);
|
||||||
|
if (ImGui::InputText("##data", DataInputBuf, IM_ARRAYSIZE(DataInputBuf), flags, UserData::Callback, &user_data))
|
||||||
data_write = data_next = true;
|
data_write = data_next = true;
|
||||||
else if (!DataEditingTakeFocus && !ImGui::IsItemActive())
|
else if (!DataEditingTakeFocus && !ImGui::IsItemActive())
|
||||||
DataEditingAddr = data_editing_addr_next = (size_t)-1;
|
DataEditingAddr = data_editing_addr_next = (size_t)-1;
|
||||||
DataEditingTakeFocus = false;
|
DataEditingTakeFocus = false;
|
||||||
ImGui::PopItemWidth();
|
|
||||||
if (user_data.CursorPos >= 2)
|
if (user_data.CursorPos >= 2)
|
||||||
data_write = data_next = true;
|
data_write = data_next = true;
|
||||||
if (data_editing_addr_next != (size_t)-1)
|
if (data_editing_addr_next != (size_t)-1)
|
||||||
@@ -367,16 +357,16 @@ struct MemoryEditor
|
|||||||
if (data_write && sscanf(DataInputBuf, "%X", &data_input_value) == 1)
|
if (data_write && sscanf(DataInputBuf, "%X", &data_input_value) == 1)
|
||||||
{
|
{
|
||||||
if (WriteFn)
|
if (WriteFn)
|
||||||
WriteFn(mem_data, addr, (u8)data_input_value);
|
WriteFn(mem_data, addr, (ImU8)data_input_value);
|
||||||
else
|
else
|
||||||
mem_data[addr] = (u8)data_input_value;
|
mem_data[addr] = (ImU8)data_input_value;
|
||||||
}
|
}
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// NB: The trailing space is not visible but ensure there's no gap that the mouse cannot click on.
|
// NB: The trailing space is not visible but ensure there's no gap that the mouse cannot click on.
|
||||||
u8 b = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr];
|
ImU8 b = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr];
|
||||||
|
|
||||||
if (OptShowHexII)
|
if (OptShowHexII)
|
||||||
{
|
{
|
||||||
@@ -426,16 +416,18 @@ struct MemoryEditor
|
|||||||
}
|
}
|
||||||
unsigned char c = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr];
|
unsigned char c = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr];
|
||||||
char display_c = (c < 32 || c >= 128) ? '.' : c;
|
char display_c = (c < 32 || c >= 128) ? '.' : c;
|
||||||
draw_list->AddText(pos, (display_c == '.') ? color_disabled : color_text, &display_c, &display_c + 1);
|
draw_list->AddText(pos, (display_c == c) ? color_text : color_disabled, &display_c, &display_c + 1);
|
||||||
pos.x += s.GlyphWidth;
|
pos.x += s.GlyphWidth;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
clipper.End();
|
|
||||||
ImGui::PopStyleVar(2);
|
ImGui::PopStyleVar(2);
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
|
||||||
if (data_next && DataEditingAddr < mem_size)
|
// Notify the main window of our ideal child content size (FIXME: we are missing an API to get the contents size from the child)
|
||||||
|
ImGui::SetCursorPosX(s.WindowWidth);
|
||||||
|
|
||||||
|
if (data_next && DataEditingAddr + 1 < mem_size)
|
||||||
{
|
{
|
||||||
DataEditingAddr = DataPreviewAddr = DataEditingAddr + 1;
|
DataEditingAddr = DataPreviewAddr = DataEditingAddr + 1;
|
||||||
DataEditingTakeFocus = true;
|
DataEditingTakeFocus = true;
|
||||||
@@ -443,23 +435,37 @@ struct MemoryEditor
|
|||||||
else if (data_editing_addr_next != (size_t)-1)
|
else if (data_editing_addr_next != (size_t)-1)
|
||||||
{
|
{
|
||||||
DataEditingAddr = DataPreviewAddr = data_editing_addr_next;
|
DataEditingAddr = DataPreviewAddr = data_editing_addr_next;
|
||||||
|
DataEditingTakeFocus = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool next_show_data_preview = OptShowDataPreview;
|
const bool lock_show_data_preview = OptShowDataPreview;
|
||||||
if (OptShowOptions)
|
if (OptShowOptions)
|
||||||
{
|
{
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
DrawOptionsLine(s, mem_data, mem_size, base_display_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lock_show_data_preview)
|
||||||
|
{
|
||||||
|
ImGui::Separator();
|
||||||
|
DrawPreviewLine(s, mem_data, mem_size, base_display_addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawOptionsLine(const Sizes& s, void* mem_data, size_t mem_size, size_t base_display_addr)
|
||||||
|
{
|
||||||
|
IM_UNUSED(mem_data);
|
||||||
|
ImGuiStyle& style = ImGui::GetStyle();
|
||||||
|
const char* format_range = OptUpperCaseHex ? "Range %0*" _PRISizeT "X..%0*" _PRISizeT "X" : "Range %0*" _PRISizeT "x..%0*" _PRISizeT "x";
|
||||||
|
|
||||||
// Options menu
|
// Options menu
|
||||||
|
|
||||||
if (ImGui::Button("Options"))
|
if (ImGui::Button("Options"))
|
||||||
ImGui::OpenPopup("context");
|
ImGui::OpenPopup("context");
|
||||||
if (ImGui::BeginPopup("context"))
|
if (ImGui::BeginPopup("context"))
|
||||||
{
|
{
|
||||||
ImGui::PushItemWidth(56);
|
ImGui::SetNextItemWidth(s.GlyphWidth * 7 + style.FramePadding.x * 2.0f);
|
||||||
if (ImGui::DragInt("##cols", &Cols, 0.2f, 4, 32, "%d cols")) { ContentsWidthChanged = true; }
|
if (ImGui::DragInt("##cols", &Cols, 0.2f, 4, 32, "%d cols")) { ContentsWidthChanged = true; if (Cols < 1) Cols = 1; }
|
||||||
ImGui::PopItemWidth();
|
ImGui::Checkbox("Show Data Preview", &OptShowDataPreview);
|
||||||
ImGui::Checkbox("Show Data Preview", &next_show_data_preview);
|
|
||||||
ImGui::Checkbox("Show HexII", &OptShowHexII);
|
ImGui::Checkbox("Show HexII", &OptShowHexII);
|
||||||
if (ImGui::Checkbox("Show Ascii", &OptShowAscii)) { ContentsWidthChanged = true; }
|
if (ImGui::Checkbox("Show Ascii", &OptShowAscii)) { ContentsWidthChanged = true; }
|
||||||
ImGui::Checkbox("Grey out zeroes", &OptGreyOutZeroes);
|
ImGui::Checkbox("Grey out zeroes", &OptGreyOutZeroes);
|
||||||
@@ -469,13 +475,10 @@ struct MemoryEditor
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
char format_range_buf[512] {};
|
ImGui::Text(format_range, s.AddrDigitsCount, base_display_addr, s.AddrDigitsCount, base_display_addr + mem_size - 1);
|
||||||
snprintf(format_range_buf, sizeof(format_range_buf), format_range,
|
|
||||||
s.AddrDigitsCount, base_display_addr, s.AddrDigitsCount, base_display_addr + mem_size - 1);
|
|
||||||
ImGui::TextUnformatted(format_range_buf);
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::PushItemWidth((s.AddrDigitsCount + 1) * s.GlyphWidth + style.FramePadding.x * 2.0f);
|
ImGui::SetNextItemWidth((s.AddrDigitsCount + 1) * s.GlyphWidth + style.FramePadding.x * 2.0f);
|
||||||
if (ImGui::InputText("##addr", AddrInputBuf, 32, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue))
|
if (ImGui::InputText("##addr", AddrInputBuf, IM_ARRAYSIZE(AddrInputBuf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue))
|
||||||
{
|
{
|
||||||
size_t goto_addr;
|
size_t goto_addr;
|
||||||
if (sscanf(AddrInputBuf, "%" _PRISizeT "X", &goto_addr) == 1)
|
if (sscanf(AddrInputBuf, "%" _PRISizeT "X", &goto_addr) == 1)
|
||||||
@@ -484,7 +487,6 @@ struct MemoryEditor
|
|||||||
HighlightMin = HighlightMax = (size_t)-1;
|
HighlightMin = HighlightMax = (size_t)-1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::PopItemWidth();
|
|
||||||
|
|
||||||
if (GotoAddr != (size_t)-1)
|
if (GotoAddr != (size_t)-1)
|
||||||
{
|
{
|
||||||
@@ -500,58 +502,53 @@ struct MemoryEditor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (OptShowDataPreview)
|
void DrawPreviewLine(const Sizes& s, void* mem_data_void, size_t mem_size, size_t base_display_addr)
|
||||||
{
|
{
|
||||||
ImGui::Separator();
|
IM_UNUSED(base_display_addr);
|
||||||
|
ImU8* mem_data = (ImU8*)mem_data_void;
|
||||||
|
ImGuiStyle& style = ImGui::GetStyle();
|
||||||
ImGui::AlignTextToFramePadding();
|
ImGui::AlignTextToFramePadding();
|
||||||
ImGui::Text("Preview as:");
|
ImGui::Text("Preview as:");
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::PushItemWidth((s.GlyphWidth * 10.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x);
|
ImGui::SetNextItemWidth((s.GlyphWidth * 10.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x);
|
||||||
if (ImGui::BeginCombo("##combo_type", DataTypeGetDesc(PreviewDataType), ImGuiComboFlags_HeightLargest))
|
if (ImGui::BeginCombo("##combo_type", DataTypeGetDesc(PreviewDataType), ImGuiComboFlags_HeightLargest))
|
||||||
{
|
{
|
||||||
for (int n = 0; n < DataType_COUNT; n++)
|
for (int n = 0; n < ImGuiDataType_COUNT; n++)
|
||||||
if (ImGui::Selectable(DataTypeGetDesc((DataType)n), PreviewDataType == n))
|
if (ImGui::Selectable(DataTypeGetDesc((ImGuiDataType)n), PreviewDataType == n))
|
||||||
PreviewDataType = (DataType)n;
|
PreviewDataType = (ImGuiDataType)n;
|
||||||
ImGui::EndCombo();
|
ImGui::EndCombo();
|
||||||
}
|
}
|
||||||
ImGui::PopItemWidth();
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::PushItemWidth((s.GlyphWidth * 6.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x);
|
ImGui::SetNextItemWidth((s.GlyphWidth * 6.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x);
|
||||||
ImGui::Combo("##combo_endianess", &PreviewEndianess, "LE\0BE\0\0");
|
ImGui::Combo("##combo_endianess", &PreviewEndianess, "LE\0BE\0\0");
|
||||||
ImGui::PopItemWidth();
|
|
||||||
|
|
||||||
char buf[128];
|
char buf[128] = "";
|
||||||
float x = s.GlyphWidth * 6.0f;
|
float x = s.GlyphWidth * 6.0f;
|
||||||
bool has_value = DataPreviewAddr != (size_t)-1;
|
bool has_value = DataPreviewAddr != (size_t)-1;
|
||||||
if (has_value)
|
if (has_value)
|
||||||
DisplayPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Dec, buf, (size_t)IM_ARRAYSIZE(buf));
|
DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Dec, buf, (size_t)IM_ARRAYSIZE(buf));
|
||||||
ImGui::Text("Dec"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A");
|
ImGui::Text("Dec"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A");
|
||||||
if (has_value)
|
if (has_value)
|
||||||
DisplayPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Hex, buf, (size_t)IM_ARRAYSIZE(buf));
|
DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Hex, buf, (size_t)IM_ARRAYSIZE(buf));
|
||||||
ImGui::Text("Hex"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A");
|
ImGui::Text("Hex"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A");
|
||||||
if (has_value)
|
if (has_value)
|
||||||
DisplayPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Bin, buf, (size_t)IM_ARRAYSIZE(buf));
|
DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Bin, buf, (size_t)IM_ARRAYSIZE(buf));
|
||||||
|
buf[IM_ARRAYSIZE(buf) - 1] = 0;
|
||||||
ImGui::Text("Bin"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A");
|
ImGui::Text("Bin"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A");
|
||||||
}
|
}
|
||||||
|
|
||||||
OptShowDataPreview = next_show_data_preview;
|
|
||||||
|
|
||||||
// Notify the main window of our ideal child content size (FIXME: we are missing an API to get the contents size from the child)
|
|
||||||
ImGui::SetCursorPosX(s.WindowWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Utilities for Data Preview
|
// Utilities for Data Preview
|
||||||
const char* DataTypeGetDesc(DataType data_type) const
|
const char* DataTypeGetDesc(ImGuiDataType data_type) const
|
||||||
{
|
{
|
||||||
const char* descs[] = { "Int8", "Uint8", "Int16", "Uint16", "Int32", "Uint32", "Int64", "Uint64", "Float", "Double" };
|
const char* descs[] = { "Int8", "Uint8", "Int16", "Uint16", "Int32", "Uint32", "Int64", "Uint64", "Float", "Double" };
|
||||||
IM_ASSERT(data_type >= 0 && data_type < DataType_COUNT);
|
IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
|
||||||
return descs[data_type];
|
return descs[data_type];
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t DataTypeGetSize(DataType data_type) const
|
size_t DataTypeGetSize(ImGuiDataType data_type) const
|
||||||
{
|
{
|
||||||
const size_t sizes[] = { 1, 1, 2, 2, 4, 4, 8, 8, 4, 8 };
|
const size_t sizes[] = { 1, 1, 2, 2, 4, 4, 8, 8, sizeof(float), sizeof(double) };
|
||||||
IM_ASSERT(data_type >= 0 && data_type < DataType_COUNT);
|
IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
|
||||||
return sizes[data_type];
|
return sizes[data_type];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -627,7 +624,8 @@ struct MemoryEditor
|
|||||||
return out_buf;
|
return out_buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisplayPreviewData(size_t addr, const u8* mem_data, size_t mem_size, DataType data_type, DataFormat data_format, char* out_buf, size_t out_buf_size) const
|
// [Internal]
|
||||||
|
void DrawPreviewData(size_t addr, const ImU8* mem_data, size_t mem_size, ImGuiDataType data_type, DataFormat data_format, char* out_buf, size_t out_buf_size) const
|
||||||
{
|
{
|
||||||
uint8_t buf[8];
|
uint8_t buf[8];
|
||||||
size_t elem_size = DataTypeGetSize(data_type);
|
size_t elem_size = DataTypeGetSize(data_type);
|
||||||
@@ -649,7 +647,7 @@ struct MemoryEditor
|
|||||||
out_buf[0] = 0;
|
out_buf[0] = 0;
|
||||||
switch (data_type)
|
switch (data_type)
|
||||||
{
|
{
|
||||||
case DataType_S8:
|
case ImGuiDataType_S8:
|
||||||
{
|
{
|
||||||
int8_t int8 = 0;
|
int8_t int8 = 0;
|
||||||
EndianessCopy(&int8, buf, size);
|
EndianessCopy(&int8, buf, size);
|
||||||
@@ -657,7 +655,7 @@ struct MemoryEditor
|
|||||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", int8 & 0xFF); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", int8 & 0xFF); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DataType_U8:
|
case ImGuiDataType_U8:
|
||||||
{
|
{
|
||||||
uint8_t uint8 = 0;
|
uint8_t uint8 = 0;
|
||||||
EndianessCopy(&uint8, buf, size);
|
EndianessCopy(&uint8, buf, size);
|
||||||
@@ -665,7 +663,7 @@ struct MemoryEditor
|
|||||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", uint8 & 0XFF); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", uint8 & 0XFF); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DataType_S16:
|
case ImGuiDataType_S16:
|
||||||
{
|
{
|
||||||
int16_t int16 = 0;
|
int16_t int16 = 0;
|
||||||
EndianessCopy(&int16, buf, size);
|
EndianessCopy(&int16, buf, size);
|
||||||
@@ -673,7 +671,7 @@ struct MemoryEditor
|
|||||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", int16 & 0xFFFF); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", int16 & 0xFFFF); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DataType_U16:
|
case ImGuiDataType_U16:
|
||||||
{
|
{
|
||||||
uint16_t uint16 = 0;
|
uint16_t uint16 = 0;
|
||||||
EndianessCopy(&uint16, buf, size);
|
EndianessCopy(&uint16, buf, size);
|
||||||
@@ -681,7 +679,7 @@ struct MemoryEditor
|
|||||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", uint16 & 0xFFFF); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", uint16 & 0xFFFF); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DataType_S32:
|
case ImGuiDataType_S32:
|
||||||
{
|
{
|
||||||
int32_t int32 = 0;
|
int32_t int32 = 0;
|
||||||
EndianessCopy(&int32, buf, size);
|
EndianessCopy(&int32, buf, size);
|
||||||
@@ -689,7 +687,7 @@ struct MemoryEditor
|
|||||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", int32); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", int32); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DataType_U32:
|
case ImGuiDataType_U32:
|
||||||
{
|
{
|
||||||
uint32_t uint32 = 0;
|
uint32_t uint32 = 0;
|
||||||
EndianessCopy(&uint32, buf, size);
|
EndianessCopy(&uint32, buf, size);
|
||||||
@@ -697,7 +695,7 @@ struct MemoryEditor
|
|||||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", uint32); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", uint32); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DataType_S64:
|
case ImGuiDataType_S64:
|
||||||
{
|
{
|
||||||
int64_t int64 = 0;
|
int64_t int64 = 0;
|
||||||
EndianessCopy(&int64, buf, size);
|
EndianessCopy(&int64, buf, size);
|
||||||
@@ -705,7 +703,7 @@ struct MemoryEditor
|
|||||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)int64); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)int64); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DataType_U64:
|
case ImGuiDataType_U64:
|
||||||
{
|
{
|
||||||
uint64_t uint64 = 0;
|
uint64_t uint64 = 0;
|
||||||
EndianessCopy(&uint64, buf, size);
|
EndianessCopy(&uint64, buf, size);
|
||||||
@@ -713,7 +711,7 @@ struct MemoryEditor
|
|||||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)uint64); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)uint64); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DataType_Float:
|
case ImGuiDataType_Float:
|
||||||
{
|
{
|
||||||
float float32 = 0.0f;
|
float float32 = 0.0f;
|
||||||
EndianessCopy(&float32, buf, size);
|
EndianessCopy(&float32, buf, size);
|
||||||
@@ -721,7 +719,7 @@ struct MemoryEditor
|
|||||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", float32); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", float32); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DataType_Double:
|
case ImGuiDataType_Double:
|
||||||
{
|
{
|
||||||
double float64 = 0.0;
|
double float64 = 0.0;
|
||||||
EndianessCopy(&float64, buf, size);
|
EndianessCopy(&float64, buf, size);
|
||||||
@@ -729,7 +727,7 @@ struct MemoryEditor
|
|||||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", float64); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", float64); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DataType_COUNT:
|
case ImGuiDataType_COUNT:
|
||||||
break;
|
break;
|
||||||
} // Switch
|
} // Switch
|
||||||
IM_ASSERT(0); // Shouldn't reach
|
IM_ASSERT(0); // Shouldn't reach
|
||||||
@@ -738,3 +736,7 @@ struct MemoryEditor
|
|||||||
|
|
||||||
#undef _PRISizeT
|
#undef _PRISizeT
|
||||||
#undef ImSnprintf
|
#undef ImSnprintf
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning (pop)
|
||||||
|
#endif
|
||||||
Vendored
+155
-99
@@ -1,4 +1,4 @@
|
|||||||
// dear imgui, v1.88 WIP
|
// dear imgui, v1.89.5 WIP
|
||||||
// (tables and columns code)
|
// (tables and columns code)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -24,7 +24,7 @@ Index of this file:
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Navigating this file:
|
// Navigating this file:
|
||||||
// - In Visual Studio IDE: CTRL+comma ("Edit.NavigateTo") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
|
// - In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
|
||||||
// - With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments.
|
// - With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments.
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@@ -188,12 +188,12 @@ Index of this file:
|
|||||||
#define _CRT_SECURE_NO_WARNINGS
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "imgui.h"
|
|
||||||
#ifndef IMGUI_DISABLE
|
|
||||||
|
|
||||||
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
||||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
#include "imgui_internal.h"
|
#include "imgui_internal.h"
|
||||||
|
|
||||||
// System includes
|
// System includes
|
||||||
@@ -315,7 +315,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Sanity checks
|
// Sanity checks
|
||||||
IM_ASSERT(columns_count > 0 && columns_count <= IMGUI_TABLE_MAX_COLUMNS && "Only 1..64 columns allowed!");
|
IM_ASSERT(columns_count > 0 && columns_count < IMGUI_TABLE_MAX_COLUMNS);
|
||||||
if (flags & ImGuiTableFlags_ScrollX)
|
if (flags & ImGuiTableFlags_ScrollX)
|
||||||
IM_ASSERT(inner_width >= 0.0f);
|
IM_ASSERT(inner_width >= 0.0f);
|
||||||
|
|
||||||
@@ -332,11 +332,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
|||||||
|
|
||||||
// Acquire storage for the table
|
// Acquire storage for the table
|
||||||
ImGuiTable* table = g.Tables.GetOrAddByKey(id);
|
ImGuiTable* table = g.Tables.GetOrAddByKey(id);
|
||||||
const int instance_no = (table->LastFrameActive != g.FrameCount) ? 0 : table->InstanceCurrent + 1;
|
|
||||||
const ImGuiID instance_id = id + instance_no;
|
|
||||||
const ImGuiTableFlags table_last_flags = table->Flags;
|
const ImGuiTableFlags table_last_flags = table->Flags;
|
||||||
if (instance_no > 0)
|
|
||||||
IM_ASSERT(table->ColumnsCount == columns_count && "BeginTable(): Cannot change columns count mid-frame while preserving same ID");
|
|
||||||
|
|
||||||
// Acquire temporary buffers
|
// Acquire temporary buffers
|
||||||
const int table_idx = g.Tables.GetIndex(table);
|
const int table_idx = g.Tables.GetIndex(table);
|
||||||
@@ -352,17 +348,32 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
|||||||
flags = TableFixFlags(flags, outer_window);
|
flags = TableFixFlags(flags, outer_window);
|
||||||
|
|
||||||
// Initialize
|
// Initialize
|
||||||
|
const int instance_no = (table->LastFrameActive != g.FrameCount) ? 0 : table->InstanceCurrent + 1;
|
||||||
table->ID = id;
|
table->ID = id;
|
||||||
table->Flags = flags;
|
table->Flags = flags;
|
||||||
table->InstanceCurrent = (ImS16)instance_no;
|
|
||||||
table->LastFrameActive = g.FrameCount;
|
table->LastFrameActive = g.FrameCount;
|
||||||
table->OuterWindow = table->InnerWindow = outer_window;
|
table->OuterWindow = table->InnerWindow = outer_window;
|
||||||
table->ColumnsCount = columns_count;
|
table->ColumnsCount = columns_count;
|
||||||
table->IsLayoutLocked = false;
|
table->IsLayoutLocked = false;
|
||||||
table->InnerWidth = inner_width;
|
table->InnerWidth = inner_width;
|
||||||
temp_data->UserOuterSize = outer_size;
|
temp_data->UserOuterSize = outer_size;
|
||||||
if (instance_no > 0 && table->InstanceDataExtra.Size < instance_no)
|
|
||||||
|
// Instance data (for instance 0, TableID == TableInstanceID)
|
||||||
|
ImGuiID instance_id;
|
||||||
|
table->InstanceCurrent = (ImS16)instance_no;
|
||||||
|
if (instance_no > 0)
|
||||||
|
{
|
||||||
|
IM_ASSERT(table->ColumnsCount == columns_count && "BeginTable(): Cannot change columns count mid-frame while preserving same ID");
|
||||||
|
if (table->InstanceDataExtra.Size < instance_no)
|
||||||
table->InstanceDataExtra.push_back(ImGuiTableInstanceData());
|
table->InstanceDataExtra.push_back(ImGuiTableInstanceData());
|
||||||
|
instance_id = GetIDWithSeed(instance_no, GetIDWithSeed("##Instances", NULL, id)); // Push "##Instance" followed by (int)instance_no in ID stack.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
instance_id = id;
|
||||||
|
}
|
||||||
|
ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
|
||||||
|
table_instance->TableInstanceID = instance_id;
|
||||||
|
|
||||||
// When not using a child window, WorkRect.Max will grow as we append contents.
|
// When not using a child window, WorkRect.Max will grow as we append contents.
|
||||||
if (use_child_window)
|
if (use_child_window)
|
||||||
@@ -395,6 +406,14 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
|||||||
table->OuterRect = table->InnerWindow->Rect();
|
table->OuterRect = table->InnerWindow->Rect();
|
||||||
table->InnerRect = table->InnerWindow->InnerRect;
|
table->InnerRect = table->InnerWindow->InnerRect;
|
||||||
IM_ASSERT(table->InnerWindow->WindowPadding.x == 0.0f && table->InnerWindow->WindowPadding.y == 0.0f && table->InnerWindow->WindowBorderSize == 0.0f);
|
IM_ASSERT(table->InnerWindow->WindowPadding.x == 0.0f && table->InnerWindow->WindowPadding.y == 0.0f && table->InnerWindow->WindowBorderSize == 0.0f);
|
||||||
|
|
||||||
|
// When using multiple instances, ensure they have the same amount of horizontal decorations (aka vertical scrollbar) so stretched columns can be aligned)
|
||||||
|
if (instance_no == 0)
|
||||||
|
{
|
||||||
|
table->HasScrollbarYPrev = table->HasScrollbarYCurr;
|
||||||
|
table->HasScrollbarYCurr = false;
|
||||||
|
}
|
||||||
|
table->HasScrollbarYCurr |= (table->InnerWindow->ScrollMax.y > 0.0f);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -404,7 +423,9 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Push a standardized ID for both child-using and not-child-using tables
|
// Push a standardized ID for both child-using and not-child-using tables
|
||||||
PushOverrideID(instance_id);
|
PushOverrideID(id);
|
||||||
|
if (instance_no > 0)
|
||||||
|
PushOverrideID(instance_id); // FIXME: Somehow this is not resolved by stack-tool, even tho GetIDWithSeed() submitted the symbol.
|
||||||
|
|
||||||
// Backup a copy of host window members we will modify
|
// Backup a copy of host window members we will modify
|
||||||
ImGuiWindow* inner_window = table->InnerWindow;
|
ImGuiWindow* inner_window = table->InnerWindow;
|
||||||
@@ -539,7 +560,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
|||||||
if (table->RefScale != 0.0f && table->RefScale != new_ref_scale_unit)
|
if (table->RefScale != 0.0f && table->RefScale != new_ref_scale_unit)
|
||||||
{
|
{
|
||||||
const float scale_factor = new_ref_scale_unit / table->RefScale;
|
const float scale_factor = new_ref_scale_unit / table->RefScale;
|
||||||
//IMGUI_DEBUG_LOG("[table] %08X RefScaleUnit %.3f -> %.3f, scaling width by %.3f\n", table->ID, table->RefScaleUnit, new_ref_scale_unit, scale_factor);
|
//IMGUI_DEBUG_PRINT("[table] %08X RefScaleUnit %.3f -> %.3f, scaling width by %.3f\n", table->ID, table->RefScaleUnit, new_ref_scale_unit, scale_factor);
|
||||||
for (int n = 0; n < columns_count; n++)
|
for (int n = 0; n < columns_count; n++)
|
||||||
table->Columns[n].WidthRequest = table->Columns[n].WidthRequest * scale_factor;
|
table->Columns[n].WidthRequest = table->Columns[n].WidthRequest * scale_factor;
|
||||||
}
|
}
|
||||||
@@ -573,16 +594,22 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
|||||||
void ImGui::TableBeginInitMemory(ImGuiTable* table, int columns_count)
|
void ImGui::TableBeginInitMemory(ImGuiTable* table, int columns_count)
|
||||||
{
|
{
|
||||||
// Allocate single buffer for our arrays
|
// Allocate single buffer for our arrays
|
||||||
ImSpanAllocator<3> span_allocator;
|
const int columns_bit_array_size = (int)ImBitArrayGetStorageSizeInBytes(columns_count);
|
||||||
|
ImSpanAllocator<6> span_allocator;
|
||||||
span_allocator.Reserve(0, columns_count * sizeof(ImGuiTableColumn));
|
span_allocator.Reserve(0, columns_count * sizeof(ImGuiTableColumn));
|
||||||
span_allocator.Reserve(1, columns_count * sizeof(ImGuiTableColumnIdx));
|
span_allocator.Reserve(1, columns_count * sizeof(ImGuiTableColumnIdx));
|
||||||
span_allocator.Reserve(2, columns_count * sizeof(ImGuiTableCellData), 4);
|
span_allocator.Reserve(2, columns_count * sizeof(ImGuiTableCellData), 4);
|
||||||
|
for (int n = 3; n < 6; n++)
|
||||||
|
span_allocator.Reserve(n, columns_bit_array_size);
|
||||||
table->RawData = IM_ALLOC(span_allocator.GetArenaSizeInBytes());
|
table->RawData = IM_ALLOC(span_allocator.GetArenaSizeInBytes());
|
||||||
memset(table->RawData, 0, span_allocator.GetArenaSizeInBytes());
|
memset(table->RawData, 0, span_allocator.GetArenaSizeInBytes());
|
||||||
span_allocator.SetArenaBasePtr(table->RawData);
|
span_allocator.SetArenaBasePtr(table->RawData);
|
||||||
span_allocator.GetSpan(0, &table->Columns);
|
span_allocator.GetSpan(0, &table->Columns);
|
||||||
span_allocator.GetSpan(1, &table->DisplayOrderToIndex);
|
span_allocator.GetSpan(1, &table->DisplayOrderToIndex);
|
||||||
span_allocator.GetSpan(2, &table->RowCellData);
|
span_allocator.GetSpan(2, &table->RowCellData);
|
||||||
|
table->EnabledMaskByDisplayOrder = (ImU32*)span_allocator.GetSpanPtrBegin(3);
|
||||||
|
table->EnabledMaskByIndex = (ImU32*)span_allocator.GetSpanPtrBegin(4);
|
||||||
|
table->VisibleMaskByIndex = (ImU32*)span_allocator.GetSpanPtrBegin(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply queued resizing/reordering/hiding requests
|
// Apply queued resizing/reordering/hiding requests
|
||||||
@@ -721,8 +748,8 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
|||||||
const ImGuiTableFlags table_sizing_policy = (table->Flags & ImGuiTableFlags_SizingMask_);
|
const ImGuiTableFlags table_sizing_policy = (table->Flags & ImGuiTableFlags_SizingMask_);
|
||||||
table->IsDefaultDisplayOrder = true;
|
table->IsDefaultDisplayOrder = true;
|
||||||
table->ColumnsEnabledCount = 0;
|
table->ColumnsEnabledCount = 0;
|
||||||
table->EnabledMaskByIndex = 0x00;
|
ImBitArrayClearAllBits(table->EnabledMaskByIndex, table->ColumnsCount);
|
||||||
table->EnabledMaskByDisplayOrder = 0x00;
|
ImBitArrayClearAllBits(table->EnabledMaskByDisplayOrder, table->ColumnsCount);
|
||||||
table->LeftMostEnabledColumn = -1;
|
table->LeftMostEnabledColumn = -1;
|
||||||
table->MinColumnWidth = ImMax(1.0f, g.Style.FramePadding.x * 1.0f); // g.Style.ColumnsMinSpacing; // FIXME-TABLE
|
table->MinColumnWidth = ImMax(1.0f, g.Style.FramePadding.x * 1.0f); // g.Style.ColumnsMinSpacing; // FIXME-TABLE
|
||||||
|
|
||||||
@@ -787,8 +814,8 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
|||||||
else
|
else
|
||||||
table->LeftMostEnabledColumn = (ImGuiTableColumnIdx)column_n;
|
table->LeftMostEnabledColumn = (ImGuiTableColumnIdx)column_n;
|
||||||
column->IndexWithinEnabledSet = table->ColumnsEnabledCount++;
|
column->IndexWithinEnabledSet = table->ColumnsEnabledCount++;
|
||||||
table->EnabledMaskByIndex |= (ImU64)1 << column_n;
|
ImBitArraySetBit(table->EnabledMaskByIndex, column_n);
|
||||||
table->EnabledMaskByDisplayOrder |= (ImU64)1 << column->DisplayOrder;
|
ImBitArraySetBit(table->EnabledMaskByDisplayOrder, column->DisplayOrder);
|
||||||
prev_visible_column_idx = column_n;
|
prev_visible_column_idx = column_n;
|
||||||
IM_ASSERT(column->IndexWithinEnabledSet <= column->DisplayOrder);
|
IM_ASSERT(column->IndexWithinEnabledSet <= column->DisplayOrder);
|
||||||
|
|
||||||
@@ -836,7 +863,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
|||||||
table->LeftMostStretchedColumn = table->RightMostStretchedColumn = -1;
|
table->LeftMostStretchedColumn = table->RightMostStretchedColumn = -1;
|
||||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||||
{
|
{
|
||||||
if (!(table->EnabledMaskByIndex & ((ImU64)1 << column_n)))
|
if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n))
|
||||||
continue;
|
continue;
|
||||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||||
|
|
||||||
@@ -852,7 +879,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
|||||||
// Latch initial size for fixed columns and update it constantly for auto-resizing column (unless clipped!)
|
// Latch initial size for fixed columns and update it constantly for auto-resizing column (unless clipped!)
|
||||||
if (column->AutoFitQueue != 0x00)
|
if (column->AutoFitQueue != 0x00)
|
||||||
column->WidthRequest = width_auto;
|
column->WidthRequest = width_auto;
|
||||||
else if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && !column_is_resizable && (table->RequestOutputMaskByIndex & ((ImU64)1 << column_n)))
|
else if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && !column_is_resizable && column->IsRequestOutput)
|
||||||
column->WidthRequest = width_auto;
|
column->WidthRequest = width_auto;
|
||||||
|
|
||||||
// FIXME-TABLE: Increase minimum size during init frame to avoid biasing auto-fitting widgets
|
// FIXME-TABLE: Increase minimum size during init frame to avoid biasing auto-fitting widgets
|
||||||
@@ -888,17 +915,19 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
|||||||
sum_width_requests += table->CellPaddingX * 2.0f;
|
sum_width_requests += table->CellPaddingX * 2.0f;
|
||||||
}
|
}
|
||||||
table->ColumnsEnabledFixedCount = (ImGuiTableColumnIdx)count_fixed;
|
table->ColumnsEnabledFixedCount = (ImGuiTableColumnIdx)count_fixed;
|
||||||
|
table->ColumnsStretchSumWeights = stretch_sum_weights;
|
||||||
|
|
||||||
// [Part 4] Apply final widths based on requested widths
|
// [Part 4] Apply final widths based on requested widths
|
||||||
const ImRect work_rect = table->WorkRect;
|
const ImRect work_rect = table->WorkRect;
|
||||||
const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1);
|
const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1);
|
||||||
const float width_avail = ((table->Flags & ImGuiTableFlags_ScrollX) && table->InnerWidth == 0.0f) ? table->InnerClipRect.GetWidth() : work_rect.GetWidth();
|
const float width_removed = (table->HasScrollbarYPrev && !table->InnerWindow->ScrollbarY) ? g.Style.ScrollbarSize : 0.0f; // To synchronize decoration width of synched tables with mismatching scrollbar state (#5920)
|
||||||
|
const float width_avail = ImMax(1.0f, (((table->Flags & ImGuiTableFlags_ScrollX) && table->InnerWidth == 0.0f) ? table->InnerClipRect.GetWidth() : work_rect.GetWidth()) - width_removed);
|
||||||
const float width_avail_for_stretched_columns = width_avail - width_spacings - sum_width_requests;
|
const float width_avail_for_stretched_columns = width_avail - width_spacings - sum_width_requests;
|
||||||
float width_remaining_for_stretched_columns = width_avail_for_stretched_columns;
|
float width_remaining_for_stretched_columns = width_avail_for_stretched_columns;
|
||||||
table->ColumnsGivenWidth = width_spacings + (table->CellPaddingX * 2.0f) * table->ColumnsEnabledCount;
|
table->ColumnsGivenWidth = width_spacings + (table->CellPaddingX * 2.0f) * table->ColumnsEnabledCount;
|
||||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||||
{
|
{
|
||||||
if (!(table->EnabledMaskByIndex & ((ImU64)1 << column_n)))
|
if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n))
|
||||||
continue;
|
continue;
|
||||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||||
|
|
||||||
@@ -925,7 +954,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
|||||||
if (width_remaining_for_stretched_columns >= 1.0f && !(table->Flags & ImGuiTableFlags_PreciseWidths))
|
if (width_remaining_for_stretched_columns >= 1.0f && !(table->Flags & ImGuiTableFlags_PreciseWidths))
|
||||||
for (int order_n = table->ColumnsCount - 1; stretch_sum_weights > 0.0f && width_remaining_for_stretched_columns >= 1.0f && order_n >= 0; order_n--)
|
for (int order_n = table->ColumnsCount - 1; stretch_sum_weights > 0.0f && width_remaining_for_stretched_columns >= 1.0f && order_n >= 0; order_n--)
|
||||||
{
|
{
|
||||||
if (!(table->EnabledMaskByDisplayOrder & ((ImU64)1 << order_n)))
|
if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))
|
||||||
continue;
|
continue;
|
||||||
ImGuiTableColumn* column = &table->Columns[table->DisplayOrderToIndex[order_n]];
|
ImGuiTableColumn* column = &table->Columns[table->DisplayOrderToIndex[order_n]];
|
||||||
if (!(column->Flags & ImGuiTableColumnFlags_WidthStretch))
|
if (!(column->Flags & ImGuiTableColumnFlags_WidthStretch))
|
||||||
@@ -935,11 +964,19 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
|||||||
width_remaining_for_stretched_columns -= 1.0f;
|
width_remaining_for_stretched_columns -= 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine if table is hovered which will be used to flag columns as hovered.
|
||||||
|
// - In principle we'd like to use the equivalent of IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),
|
||||||
|
// but because our item is partially submitted at this point we use ItemHoverable() and a workaround (temporarily
|
||||||
|
// clear ActiveId, which is equivalent to the change provided by _AllowWhenBLockedByActiveItem).
|
||||||
|
// - This allows columns to be marked as hovered when e.g. clicking a button inside the column, or using drag and drop.
|
||||||
ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
|
ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
|
||||||
table->HoveredColumnBody = -1;
|
table->HoveredColumnBody = -1;
|
||||||
table->HoveredColumnBorder = -1;
|
table->HoveredColumnBorder = -1;
|
||||||
const ImRect mouse_hit_rect(table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.Max.x, ImMax(table->OuterRect.Max.y, table->OuterRect.Min.y + table_instance->LastOuterHeight));
|
const ImRect mouse_hit_rect(table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.Max.x, ImMax(table->OuterRect.Max.y, table->OuterRect.Min.y + table_instance->LastOuterHeight));
|
||||||
|
const ImGuiID backup_active_id = g.ActiveId;
|
||||||
|
g.ActiveId = 0;
|
||||||
const bool is_hovering_table = ItemHoverable(mouse_hit_rect, 0);
|
const bool is_hovering_table = ItemHoverable(mouse_hit_rect, 0);
|
||||||
|
g.ActiveId = backup_active_id;
|
||||||
|
|
||||||
// [Part 6] Setup final position, offset, skip/clip states and clipping rectangles, detect hovered column
|
// [Part 6] Setup final position, offset, skip/clip states and clipping rectangles, detect hovered column
|
||||||
// Process columns in their visible orders as we are comparing the visible order and adjusting host_clip_rect while looping.
|
// Process columns in their visible orders as we are comparing the visible order and adjusting host_clip_rect while looping.
|
||||||
@@ -948,14 +985,13 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
|||||||
float offset_x = ((table->FreezeColumnsCount > 0) ? table->OuterRect.Min.x : work_rect.Min.x) + table->OuterPaddingX - table->CellSpacingX1;
|
float offset_x = ((table->FreezeColumnsCount > 0) ? table->OuterRect.Min.x : work_rect.Min.x) + table->OuterPaddingX - table->CellSpacingX1;
|
||||||
ImRect host_clip_rect = table->InnerClipRect;
|
ImRect host_clip_rect = table->InnerClipRect;
|
||||||
//host_clip_rect.Max.x += table->CellPaddingX + table->CellSpacingX2;
|
//host_clip_rect.Max.x += table->CellPaddingX + table->CellSpacingX2;
|
||||||
table->VisibleMaskByIndex = 0x00;
|
ImBitArrayClearAllBits(table->VisibleMaskByIndex, table->ColumnsCount);
|
||||||
table->RequestOutputMaskByIndex = 0x00;
|
|
||||||
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
|
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
|
||||||
{
|
{
|
||||||
const int column_n = table->DisplayOrderToIndex[order_n];
|
const int column_n = table->DisplayOrderToIndex[order_n];
|
||||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||||
|
|
||||||
column->NavLayerCurrent = (ImS8)((table->FreezeRowsCount > 0 || column_n < table->FreezeColumnsCount) ? ImGuiNavLayer_Menu : ImGuiNavLayer_Main);
|
column->NavLayerCurrent = (ImS8)(table->FreezeRowsCount > 0 ? ImGuiNavLayer_Menu : ImGuiNavLayer_Main); // Use Count NOT request so Header line changes layer when frozen
|
||||||
|
|
||||||
if (offset_x_frozen && table->FreezeColumnsCount == visible_n)
|
if (offset_x_frozen && table->FreezeColumnsCount == visible_n)
|
||||||
{
|
{
|
||||||
@@ -966,7 +1002,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
|||||||
// Clear status flags
|
// Clear status flags
|
||||||
column->Flags &= ~ImGuiTableColumnFlags_StatusMask_;
|
column->Flags &= ~ImGuiTableColumnFlags_StatusMask_;
|
||||||
|
|
||||||
if ((table->EnabledMaskByDisplayOrder & ((ImU64)1 << order_n)) == 0)
|
if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))
|
||||||
{
|
{
|
||||||
// Hidden column: clear a few fields and we are done with it for the remainder of the function.
|
// Hidden column: clear a few fields and we are done with it for the remainder of the function.
|
||||||
// We set a zero-width clip rect but set Min.y/Max.y properly to not interfere with the clipper.
|
// We set a zero-width clip rect but set Min.y/Max.y properly to not interfere with the clipper.
|
||||||
@@ -1019,12 +1055,10 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
|||||||
column->IsVisibleY = true; // (column->ClipRect.Max.y > column->ClipRect.Min.y);
|
column->IsVisibleY = true; // (column->ClipRect.Max.y > column->ClipRect.Min.y);
|
||||||
const bool is_visible = column->IsVisibleX; //&& column->IsVisibleY;
|
const bool is_visible = column->IsVisibleX; //&& column->IsVisibleY;
|
||||||
if (is_visible)
|
if (is_visible)
|
||||||
table->VisibleMaskByIndex |= ((ImU64)1 << column_n);
|
ImBitArraySetBit(table->VisibleMaskByIndex, column_n);
|
||||||
|
|
||||||
// Mark column as requesting output from user. Note that fixed + non-resizable sets are auto-fitting at all times and therefore always request output.
|
// Mark column as requesting output from user. Note that fixed + non-resizable sets are auto-fitting at all times and therefore always request output.
|
||||||
column->IsRequestOutput = is_visible || column->AutoFitQueue != 0 || column->CannotSkipItemsQueue != 0;
|
column->IsRequestOutput = is_visible || column->AutoFitQueue != 0 || column->CannotSkipItemsQueue != 0;
|
||||||
if (column->IsRequestOutput)
|
|
||||||
table->RequestOutputMaskByIndex |= ((ImU64)1 << column_n);
|
|
||||||
|
|
||||||
// Mark column as SkipItems (ignoring all items/layout)
|
// Mark column as SkipItems (ignoring all items/layout)
|
||||||
column->IsSkipItems = !column->IsEnabled || table->HostSkipItems;
|
column->IsSkipItems = !column->IsEnabled || table->HostSkipItems;
|
||||||
@@ -1104,25 +1138,24 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
|||||||
table->IsUsingHeaders = false;
|
table->IsUsingHeaders = false;
|
||||||
|
|
||||||
// [Part 11] Context menu
|
// [Part 11] Context menu
|
||||||
if (table->IsContextPopupOpen && table->InstanceCurrent == table->InstanceInteracted)
|
if (TableBeginContextMenuPopup(table))
|
||||||
{
|
|
||||||
const ImGuiID context_menu_id = ImHashStr("##ContextMenu", 0, table->ID);
|
|
||||||
if (BeginPopupEx(context_menu_id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings))
|
|
||||||
{
|
{
|
||||||
TableDrawContextMenu(table);
|
TableDrawContextMenu(table);
|
||||||
EndPopup();
|
EndPopup();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
table->IsContextPopupOpen = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// [Part 13] Sanitize and build sort specs before we have a change to use them for display.
|
// [Part 12] Sanitize and build sort specs before we have a change to use them for display.
|
||||||
// This path will only be exercised when sort specs are modified before header rows (e.g. init or visibility change)
|
// This path will only be exercised when sort specs are modified before header rows (e.g. init or visibility change)
|
||||||
if (table->IsSortSpecsDirty && (table->Flags & ImGuiTableFlags_Sortable))
|
if (table->IsSortSpecsDirty && (table->Flags & ImGuiTableFlags_Sortable))
|
||||||
TableSortSpecsBuild(table);
|
TableSortSpecsBuild(table);
|
||||||
|
|
||||||
|
// [Part 13] Setup inner window decoration size (for scrolling / nav tracking to properly take account of frozen rows/columns)
|
||||||
|
if (table->FreezeColumnsRequest > 0)
|
||||||
|
table->InnerWindow->DecoInnerSizeX1 = table->Columns[table->DisplayOrderToIndex[table->FreezeColumnsRequest - 1]].MaxX - table->OuterRect.Min.x;
|
||||||
|
if (table->FreezeRowsRequest > 0)
|
||||||
|
table->InnerWindow->DecoInnerSizeY1 = table_instance->LastFrozenHeight;
|
||||||
|
table_instance->LastFrozenHeight = 0.0f;
|
||||||
|
|
||||||
// Initial state
|
// Initial state
|
||||||
ImGuiWindow* inner_window = table->InnerWindow;
|
ImGuiWindow* inner_window = table->InnerWindow;
|
||||||
if (table->Flags & ImGuiTableFlags_NoClip)
|
if (table->Flags & ImGuiTableFlags_NoClip)
|
||||||
@@ -1152,7 +1185,7 @@ void ImGui::TableUpdateBorders(ImGuiTable* table)
|
|||||||
|
|
||||||
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
|
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
|
||||||
{
|
{
|
||||||
if (!(table->EnabledMaskByDisplayOrder & ((ImU64)1 << order_n)))
|
if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const int column_n = table->DisplayOrderToIndex[order_n];
|
const int column_n = table->DisplayOrderToIndex[order_n];
|
||||||
@@ -1170,8 +1203,8 @@ void ImGui::TableUpdateBorders(ImGuiTable* table)
|
|||||||
|
|
||||||
ImGuiID column_id = TableGetColumnResizeID(table, column_n, table->InstanceCurrent);
|
ImGuiID column_id = TableGetColumnResizeID(table, column_n, table->InstanceCurrent);
|
||||||
ImRect hit_rect(column->MaxX - hit_half_width, hit_y1, column->MaxX + hit_half_width, border_y2_hit);
|
ImRect hit_rect(column->MaxX - hit_half_width, hit_y1, column->MaxX + hit_half_width, border_y2_hit);
|
||||||
|
ItemAdd(hit_rect, column_id, NULL, ImGuiItemFlags_NoNav);
|
||||||
//GetForegroundDrawList()->AddRect(hit_rect.Min, hit_rect.Max, IM_COL32(255, 0, 0, 100));
|
//GetForegroundDrawList()->AddRect(hit_rect.Min, hit_rect.Max, IM_COL32(255, 0, 0, 100));
|
||||||
KeepAliveID(column_id);
|
|
||||||
|
|
||||||
bool hovered = false, held = false;
|
bool hovered = false, held = false;
|
||||||
bool pressed = ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_NoNavFocus);
|
bool pressed = ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_NoNavFocus);
|
||||||
@@ -1284,17 +1317,23 @@ void ImGui::EndTable()
|
|||||||
splitter->Merge(inner_window->DrawList);
|
splitter->Merge(inner_window->DrawList);
|
||||||
|
|
||||||
// Update ColumnsAutoFitWidth to get us ahead for host using our size to auto-resize without waiting for next BeginTable()
|
// Update ColumnsAutoFitWidth to get us ahead for host using our size to auto-resize without waiting for next BeginTable()
|
||||||
const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1);
|
float auto_fit_width_for_fixed = 0.0f;
|
||||||
table->ColumnsAutoFitWidth = width_spacings + (table->CellPaddingX * 2.0f) * table->ColumnsEnabledCount;
|
float auto_fit_width_for_stretched = 0.0f;
|
||||||
|
float auto_fit_width_for_stretched_min = 0.0f;
|
||||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||||
if (table->EnabledMaskByIndex & ((ImU64)1 << column_n))
|
if (IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n))
|
||||||
{
|
{
|
||||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||||
if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && !(column->Flags & ImGuiTableColumnFlags_NoResize))
|
float column_width_request = ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && !(column->Flags & ImGuiTableColumnFlags_NoResize)) ? column->WidthRequest : TableGetColumnWidthAuto(table, column);
|
||||||
table->ColumnsAutoFitWidth += column->WidthRequest;
|
if (column->Flags & ImGuiTableColumnFlags_WidthFixed)
|
||||||
|
auto_fit_width_for_fixed += column_width_request;
|
||||||
else
|
else
|
||||||
table->ColumnsAutoFitWidth += TableGetColumnWidthAuto(table, column);
|
auto_fit_width_for_stretched += column_width_request;
|
||||||
|
if ((column->Flags & ImGuiTableColumnFlags_WidthStretch) && (column->Flags & ImGuiTableColumnFlags_NoResize) != 0)
|
||||||
|
auto_fit_width_for_stretched_min = ImMax(auto_fit_width_for_stretched_min, column_width_request / (column->StretchWeight / table->ColumnsStretchSumWeights));
|
||||||
}
|
}
|
||||||
|
const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1);
|
||||||
|
table->ColumnsAutoFitWidth = width_spacings + (table->CellPaddingX * 2.0f) * table->ColumnsEnabledCount + auto_fit_width_for_fixed + ImMax(auto_fit_width_for_stretched, auto_fit_width_for_stretched_min);
|
||||||
|
|
||||||
// Update scroll
|
// Update scroll
|
||||||
if ((table->Flags & ImGuiTableFlags_ScrollX) == 0 && inner_window != outer_window)
|
if ((table->Flags & ImGuiTableFlags_ScrollX) == 0 && inner_window != outer_window)
|
||||||
@@ -1322,8 +1361,10 @@ void ImGui::EndTable()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pop from id stack
|
// Pop from id stack
|
||||||
IM_ASSERT_USER_ERROR(inner_window->IDStack.back() == table->ID + table->InstanceCurrent, "Mismatching PushID/PopID!");
|
IM_ASSERT_USER_ERROR(inner_window->IDStack.back() == table_instance->TableInstanceID, "Mismatching PushID/PopID!");
|
||||||
IM_ASSERT_USER_ERROR(outer_window->DC.ItemWidthStack.Size >= temp_data->HostBackupItemWidthStackSize, "Too many PopItemWidth!");
|
IM_ASSERT_USER_ERROR(outer_window->DC.ItemWidthStack.Size >= temp_data->HostBackupItemWidthStackSize, "Too many PopItemWidth!");
|
||||||
|
if (table->InstanceCurrent > 0)
|
||||||
|
PopID();
|
||||||
PopID();
|
PopID();
|
||||||
|
|
||||||
// Restore window data that we modified
|
// Restore window data that we modified
|
||||||
@@ -1593,11 +1634,11 @@ ImRect ImGui::TableGetCellBgRect(const ImGuiTable* table, int column_n)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return the resizing ID for the right-side of the given column.
|
// Return the resizing ID for the right-side of the given column.
|
||||||
ImGuiID ImGui::TableGetColumnResizeID(const ImGuiTable* table, int column_n, int instance_no)
|
ImGuiID ImGui::TableGetColumnResizeID(ImGuiTable* table, int column_n, int instance_no)
|
||||||
{
|
{
|
||||||
IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount);
|
IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount);
|
||||||
ImGuiID id = table->ID + 1 + (instance_no * table->ColumnsCount) + column_n;
|
ImGuiID instance_id = TableGetInstanceID(table, instance_no);
|
||||||
return id;
|
return instance_id + 1 + column_n; // FIXME: #6140: still not ideal
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered.
|
// Return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered.
|
||||||
@@ -1628,7 +1669,7 @@ void ImGui::TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n
|
|||||||
return;
|
return;
|
||||||
if (column_n == -1)
|
if (column_n == -1)
|
||||||
column_n = table->CurrentColumn;
|
column_n = table->CurrentColumn;
|
||||||
if ((table->VisibleMaskByIndex & ((ImU64)1 << column_n)) == 0)
|
if (!IM_BITARRAY_TESTBIT(table->VisibleMaskByIndex, column_n))
|
||||||
return;
|
return;
|
||||||
if (table->RowCellDataCurrent < 0 || table->RowCellData[table->RowCellDataCurrent].Column != column_n)
|
if (table->RowCellDataCurrent < 0 || table->RowCellData[table->RowCellDataCurrent].Column != column_n)
|
||||||
table->RowCellDataCurrent++;
|
table->RowCellDataCurrent++;
|
||||||
@@ -1718,6 +1759,8 @@ void ImGui::TableBeginRow(ImGuiTable* table)
|
|||||||
table->RowTextBaseline = 0.0f;
|
table->RowTextBaseline = 0.0f;
|
||||||
table->RowIndentOffsetX = window->DC.Indent.x - table->HostIndentX; // Lock indent
|
table->RowIndentOffsetX = window->DC.Indent.x - table->HostIndentX; // Lock indent
|
||||||
window->DC.PrevLineTextBaseOffset = 0.0f;
|
window->DC.PrevLineTextBaseOffset = 0.0f;
|
||||||
|
window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
|
||||||
|
window->DC.IsSameLine = window->DC.IsSetPos = false;
|
||||||
window->DC.CursorMaxPos.y = next_y1;
|
window->DC.CursorMaxPos.y = next_y1;
|
||||||
|
|
||||||
// Making the header BG color non-transparent will allow us to overlay it multiple times when handling smooth dragging.
|
// Making the header BG color non-transparent will allow us to overlay it multiple times when handling smooth dragging.
|
||||||
@@ -1830,17 +1873,15 @@ void ImGui::TableEndRow(ImGuiTable* table)
|
|||||||
// get the new cursor position.
|
// get the new cursor position.
|
||||||
if (unfreeze_rows_request)
|
if (unfreeze_rows_request)
|
||||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||||
{
|
table->Columns[column_n].NavLayerCurrent = ImGuiNavLayer_Main;
|
||||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
|
||||||
column->NavLayerCurrent = (ImS8)((column_n < table->FreezeColumnsCount) ? ImGuiNavLayer_Menu : ImGuiNavLayer_Main);
|
|
||||||
}
|
|
||||||
if (unfreeze_rows_actual)
|
if (unfreeze_rows_actual)
|
||||||
{
|
{
|
||||||
IM_ASSERT(table->IsUnfrozenRows == false);
|
IM_ASSERT(table->IsUnfrozenRows == false);
|
||||||
|
const float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y);
|
||||||
table->IsUnfrozenRows = true;
|
table->IsUnfrozenRows = true;
|
||||||
|
TableGetInstanceData(table, table->InstanceCurrent)->LastFrozenHeight = y0 - table->OuterRect.Min.y;
|
||||||
|
|
||||||
// BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect
|
// BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect
|
||||||
float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y);
|
|
||||||
table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y);
|
table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y);
|
||||||
table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = window->InnerClipRect.Max.y;
|
table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = window->InnerClipRect.Max.y;
|
||||||
table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen;
|
table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen;
|
||||||
@@ -1903,7 +1944,7 @@ bool ImGui::TableSetColumnIndex(int column_n)
|
|||||||
|
|
||||||
// Return whether the column is visible. User may choose to skip submitting items based on this return value,
|
// Return whether the column is visible. User may choose to skip submitting items based on this return value,
|
||||||
// however they shouldn't skip submitting for columns that may have the tallest contribution to row height.
|
// however they shouldn't skip submitting for columns that may have the tallest contribution to row height.
|
||||||
return (table->RequestOutputMaskByIndex & ((ImU64)1 << column_n)) != 0;
|
return table->Columns[column_n].IsRequestOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
// [Public] Append into the next column, wrap and create a new row when already on last column
|
// [Public] Append into the next column, wrap and create a new row when already on last column
|
||||||
@@ -1928,8 +1969,7 @@ bool ImGui::TableNextColumn()
|
|||||||
|
|
||||||
// Return whether the column is visible. User may choose to skip submitting items based on this return value,
|
// Return whether the column is visible. User may choose to skip submitting items based on this return value,
|
||||||
// however they shouldn't skip submitting for columns that may have the tallest contribution to row height.
|
// however they shouldn't skip submitting for columns that may have the tallest contribution to row height.
|
||||||
int column_n = table->CurrentColumn;
|
return table->Columns[table->CurrentColumn].IsRequestOutput;
|
||||||
return (table->RequestOutputMaskByIndex & ((ImU64)1 << column_n)) != 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1959,10 +1999,6 @@ void ImGui::TableBeginCell(ImGuiTable* table, int column_n)
|
|||||||
window->WorkRect.Max.x = column->WorkMaxX;
|
window->WorkRect.Max.x = column->WorkMaxX;
|
||||||
window->DC.ItemWidth = column->ItemWidth;
|
window->DC.ItemWidth = column->ItemWidth;
|
||||||
|
|
||||||
// To allow ImGuiListClipper to function we propagate our row height
|
|
||||||
if (!column->IsEnabled)
|
|
||||||
window->DC.CursorPos.y = ImMax(window->DC.CursorPos.y, table->RowPosY2);
|
|
||||||
|
|
||||||
window->SkipItems = column->IsSkipItems;
|
window->SkipItems = column->IsSkipItems;
|
||||||
if (column->IsSkipItems)
|
if (column->IsSkipItems)
|
||||||
{
|
{
|
||||||
@@ -1999,6 +2035,9 @@ void ImGui::TableEndCell(ImGuiTable* table)
|
|||||||
ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];
|
ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];
|
||||||
ImGuiWindow* window = table->InnerWindow;
|
ImGuiWindow* window = table->InnerWindow;
|
||||||
|
|
||||||
|
if (window->DC.IsSetPos)
|
||||||
|
ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
|
||||||
|
|
||||||
// Report maximum position so we can infer content size per column.
|
// Report maximum position so we can infer content size per column.
|
||||||
float* p_max_pos_x;
|
float* p_max_pos_x;
|
||||||
if (table->RowFlags & ImGuiTableRowFlags_Headers)
|
if (table->RowFlags & ImGuiTableRowFlags_Headers)
|
||||||
@@ -2006,6 +2045,7 @@ void ImGui::TableEndCell(ImGuiTable* table)
|
|||||||
else
|
else
|
||||||
p_max_pos_x = table->IsUnfrozenRows ? &column->ContentMaxXUnfrozen : &column->ContentMaxXFrozen;
|
p_max_pos_x = table->IsUnfrozenRows ? &column->ContentMaxXUnfrozen : &column->ContentMaxXFrozen;
|
||||||
*p_max_pos_x = ImMax(*p_max_pos_x, window->DC.CursorMaxPos.x);
|
*p_max_pos_x = ImMax(*p_max_pos_x, window->DC.CursorMaxPos.x);
|
||||||
|
if (column->IsEnabled)
|
||||||
table->RowPosY2 = ImMax(table->RowPosY2, window->DC.CursorMaxPos.y + table->CellPaddingY);
|
table->RowPosY2 = ImMax(table->RowPosY2, window->DC.CursorMaxPos.y + table->CellPaddingY);
|
||||||
column->ItemWidth = window->DC.ItemWidth;
|
column->ItemWidth = window->DC.ItemWidth;
|
||||||
|
|
||||||
@@ -2093,7 +2133,7 @@ void ImGui::TableSetColumnWidth(int column_n, float width)
|
|||||||
if (column_0->WidthGiven == column_0_width || column_0->WidthRequest == column_0_width)
|
if (column_0->WidthGiven == column_0_width || column_0->WidthRequest == column_0_width)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//IMGUI_DEBUG_LOG("TableSetColumnWidth(%d, %.1f->%.1f)\n", column_0_idx, column_0->WidthGiven, column_0_width);
|
//IMGUI_DEBUG_PRINT("TableSetColumnWidth(%d, %.1f->%.1f)\n", column_0_idx, column_0->WidthGiven, column_0_width);
|
||||||
ImGuiTableColumn* column_1 = (column_0->NextEnabledColumn != -1) ? &table->Columns[column_0->NextEnabledColumn] : NULL;
|
ImGuiTableColumn* column_1 = (column_0->NextEnabledColumn != -1) ? &table->Columns[column_0->NextEnabledColumn] : NULL;
|
||||||
|
|
||||||
// In this surprisingly not simple because of how we support mixing Fixed and multiple Stretch columns.
|
// In this surprisingly not simple because of how we support mixing Fixed and multiple Stretch columns.
|
||||||
@@ -2266,7 +2306,7 @@ void ImGui::TableSetupDrawChannels(ImGuiTable* table)
|
|||||||
const int freeze_row_multiplier = (table->FreezeRowsCount > 0) ? 2 : 1;
|
const int freeze_row_multiplier = (table->FreezeRowsCount > 0) ? 2 : 1;
|
||||||
const int channels_for_row = (table->Flags & ImGuiTableFlags_NoClip) ? 1 : table->ColumnsEnabledCount;
|
const int channels_for_row = (table->Flags & ImGuiTableFlags_NoClip) ? 1 : table->ColumnsEnabledCount;
|
||||||
const int channels_for_bg = 1 + 1 * freeze_row_multiplier;
|
const int channels_for_bg = 1 + 1 * freeze_row_multiplier;
|
||||||
const int channels_for_dummy = (table->ColumnsEnabledCount < table->ColumnsCount || table->VisibleMaskByIndex != table->EnabledMaskByIndex) ? +1 : 0;
|
const int channels_for_dummy = (table->ColumnsEnabledCount < table->ColumnsCount || (memcmp(table->VisibleMaskByIndex, table->EnabledMaskByIndex, ImBitArrayGetStorageSizeInBytes(table->ColumnsCount)) != 0)) ? +1 : 0;
|
||||||
const int channels_total = channels_for_bg + (channels_for_row * freeze_row_multiplier) + channels_for_dummy;
|
const int channels_total = channels_for_bg + (channels_for_row * freeze_row_multiplier) + channels_for_dummy;
|
||||||
table->DrawSplitter->Split(table->InnerWindow->DrawList, channels_total);
|
table->DrawSplitter->Split(table->InnerWindow->DrawList, channels_total);
|
||||||
table->DummyDrawChannel = (ImGuiTableDrawChannelIdx)((channels_for_dummy > 0) ? channels_total - 1 : -1);
|
table->DummyDrawChannel = (ImGuiTableDrawChannelIdx)((channels_for_dummy > 0) ? channels_total - 1 : -1);
|
||||||
@@ -2342,17 +2382,24 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table)
|
|||||||
{
|
{
|
||||||
ImRect ClipRect;
|
ImRect ClipRect;
|
||||||
int ChannelsCount;
|
int ChannelsCount;
|
||||||
ImBitArray<IMGUI_TABLE_MAX_DRAW_CHANNELS> ChannelsMask;
|
ImBitArrayPtr ChannelsMask;
|
||||||
|
|
||||||
MergeGroup() { ChannelsCount = 0; }
|
|
||||||
};
|
};
|
||||||
int merge_group_mask = 0x00;
|
int merge_group_mask = 0x00;
|
||||||
MergeGroup merge_groups[4];
|
MergeGroup merge_groups[4] = {};
|
||||||
|
|
||||||
|
// Use a reusable temp buffer for the merge masks as they are dynamically sized.
|
||||||
|
const int max_draw_channels = (4 + table->ColumnsCount * 2);
|
||||||
|
const int size_for_masks_bitarrays_one = (int)ImBitArrayGetStorageSizeInBytes(max_draw_channels);
|
||||||
|
g.TempBuffer.reserve(size_for_masks_bitarrays_one * 5);
|
||||||
|
memset(g.TempBuffer.Data, 0, size_for_masks_bitarrays_one * 5);
|
||||||
|
for (int n = 0; n < IM_ARRAYSIZE(merge_groups); n++)
|
||||||
|
merge_groups[n].ChannelsMask = (ImBitArrayPtr)(void*)(g.TempBuffer.Data + (size_for_masks_bitarrays_one * n));
|
||||||
|
ImBitArrayPtr remaining_mask = (ImBitArrayPtr)(void*)(g.TempBuffer.Data + (size_for_masks_bitarrays_one * 4));
|
||||||
|
|
||||||
// 1. Scan channels and take note of those which can be merged
|
// 1. Scan channels and take note of those which can be merged
|
||||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||||
{
|
{
|
||||||
if ((table->VisibleMaskByIndex & ((ImU64)1 << column_n)) == 0)
|
if (!IM_BITARRAY_TESTBIT(table->VisibleMaskByIndex, column_n))
|
||||||
continue;
|
continue;
|
||||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||||
|
|
||||||
@@ -2363,7 +2410,7 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table)
|
|||||||
|
|
||||||
// Don't attempt to merge if there are multiple draw calls within the column
|
// Don't attempt to merge if there are multiple draw calls within the column
|
||||||
ImDrawChannel* src_channel = &splitter->_Channels[channel_no];
|
ImDrawChannel* src_channel = &splitter->_Channels[channel_no];
|
||||||
if (src_channel->_CmdBuffer.Size > 0 && src_channel->_CmdBuffer.back().ElemCount == 0 && src_channel->_CmdBuffer.back().UserCallback != NULL) // Equivalent of PopUnusedDrawCmd()
|
if (src_channel->_CmdBuffer.Size > 0 && src_channel->_CmdBuffer.back().ElemCount == 0 && src_channel->_CmdBuffer.back().UserCallback == NULL) // Equivalent of PopUnusedDrawCmd()
|
||||||
src_channel->_CmdBuffer.pop_back();
|
src_channel->_CmdBuffer.pop_back();
|
||||||
if (src_channel->_CmdBuffer.Size != 1)
|
if (src_channel->_CmdBuffer.Size != 1)
|
||||||
continue;
|
continue;
|
||||||
@@ -2384,11 +2431,11 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const int merge_group_n = (has_freeze_h && column_n < table->FreezeColumnsCount ? 0 : 1) + (has_freeze_v && merge_group_sub_n == 0 ? 0 : 2);
|
const int merge_group_n = (has_freeze_h && column_n < table->FreezeColumnsCount ? 0 : 1) + (has_freeze_v && merge_group_sub_n == 0 ? 0 : 2);
|
||||||
IM_ASSERT(channel_no < IMGUI_TABLE_MAX_DRAW_CHANNELS);
|
IM_ASSERT(channel_no < max_draw_channels);
|
||||||
MergeGroup* merge_group = &merge_groups[merge_group_n];
|
MergeGroup* merge_group = &merge_groups[merge_group_n];
|
||||||
if (merge_group->ChannelsCount == 0)
|
if (merge_group->ChannelsCount == 0)
|
||||||
merge_group->ClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);
|
merge_group->ClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);
|
||||||
merge_group->ChannelsMask.SetBit(channel_no);
|
ImBitArraySetBit(merge_group->ChannelsMask, channel_no);
|
||||||
merge_group->ChannelsCount++;
|
merge_group->ChannelsCount++;
|
||||||
merge_group->ClipRect.Add(src_channel->_CmdBuffer[0].ClipRect);
|
merge_group->ClipRect.Add(src_channel->_CmdBuffer[0].ClipRect);
|
||||||
merge_group_mask |= (1 << merge_group_n);
|
merge_group_mask |= (1 << merge_group_n);
|
||||||
@@ -2424,9 +2471,8 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table)
|
|||||||
const int LEADING_DRAW_CHANNELS = 2;
|
const int LEADING_DRAW_CHANNELS = 2;
|
||||||
g.DrawChannelsTempMergeBuffer.resize(splitter->_Count - LEADING_DRAW_CHANNELS); // Use shared temporary storage so the allocation gets amortized
|
g.DrawChannelsTempMergeBuffer.resize(splitter->_Count - LEADING_DRAW_CHANNELS); // Use shared temporary storage so the allocation gets amortized
|
||||||
ImDrawChannel* dst_tmp = g.DrawChannelsTempMergeBuffer.Data;
|
ImDrawChannel* dst_tmp = g.DrawChannelsTempMergeBuffer.Data;
|
||||||
ImBitArray<IMGUI_TABLE_MAX_DRAW_CHANNELS> remaining_mask; // We need 132-bit of storage
|
ImBitArraySetBitRange(remaining_mask, LEADING_DRAW_CHANNELS, splitter->_Count);
|
||||||
remaining_mask.SetBitRange(LEADING_DRAW_CHANNELS, splitter->_Count);
|
ImBitArrayClearBit(remaining_mask, table->Bg2DrawChannelUnfrozen);
|
||||||
remaining_mask.ClearBit(table->Bg2DrawChannelUnfrozen);
|
|
||||||
IM_ASSERT(has_freeze_v == false || table->Bg2DrawChannelUnfrozen != TABLE_DRAW_CHANNEL_BG2_FROZEN);
|
IM_ASSERT(has_freeze_v == false || table->Bg2DrawChannelUnfrozen != TABLE_DRAW_CHANNEL_BG2_FROZEN);
|
||||||
int remaining_count = splitter->_Count - (has_freeze_v ? LEADING_DRAW_CHANNELS + 1 : LEADING_DRAW_CHANNELS);
|
int remaining_count = splitter->_Count - (has_freeze_v ? LEADING_DRAW_CHANNELS + 1 : LEADING_DRAW_CHANNELS);
|
||||||
//ImRect host_rect = (table->InnerWindow == table->OuterWindow) ? table->InnerClipRect : table->HostClipRect;
|
//ImRect host_rect = (table->InnerWindow == table->OuterWindow) ? table->InnerClipRect : table->HostClipRect;
|
||||||
@@ -2459,14 +2505,14 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table)
|
|||||||
GetOverlayDrawList()->AddLine(merge_group->ClipRect.Max, merge_clip_rect.Max, IM_COL32(255, 100, 0, 200));
|
GetOverlayDrawList()->AddLine(merge_group->ClipRect.Max, merge_clip_rect.Max, IM_COL32(255, 100, 0, 200));
|
||||||
#endif
|
#endif
|
||||||
remaining_count -= merge_group->ChannelsCount;
|
remaining_count -= merge_group->ChannelsCount;
|
||||||
for (int n = 0; n < IM_ARRAYSIZE(remaining_mask.Storage); n++)
|
for (int n = 0; n < (size_for_masks_bitarrays_one >> 2); n++)
|
||||||
remaining_mask.Storage[n] &= ~merge_group->ChannelsMask.Storage[n];
|
remaining_mask[n] &= ~merge_group->ChannelsMask[n];
|
||||||
for (int n = 0; n < splitter->_Count && merge_channels_count != 0; n++)
|
for (int n = 0; n < splitter->_Count && merge_channels_count != 0; n++)
|
||||||
{
|
{
|
||||||
// Copy + overwrite new clip rect
|
// Copy + overwrite new clip rect
|
||||||
if (!merge_group->ChannelsMask.TestBit(n))
|
if (!IM_BITARRAY_TESTBIT(merge_group->ChannelsMask, n))
|
||||||
continue;
|
continue;
|
||||||
merge_group->ChannelsMask.ClearBit(n);
|
IM_BITARRAY_CLEARBIT(merge_group->ChannelsMask, n);
|
||||||
merge_channels_count--;
|
merge_channels_count--;
|
||||||
|
|
||||||
ImDrawChannel* channel = &splitter->_Channels[n];
|
ImDrawChannel* channel = &splitter->_Channels[n];
|
||||||
@@ -2484,7 +2530,7 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table)
|
|||||||
// Append unmergeable channels that we didn't reorder at the end of the list
|
// Append unmergeable channels that we didn't reorder at the end of the list
|
||||||
for (int n = 0; n < splitter->_Count && remaining_count != 0; n++)
|
for (int n = 0; n < splitter->_Count && remaining_count != 0; n++)
|
||||||
{
|
{
|
||||||
if (!remaining_mask.TestBit(n))
|
if (!IM_BITARRAY_TESTBIT(remaining_mask, n))
|
||||||
continue;
|
continue;
|
||||||
ImDrawChannel* channel = &splitter->_Channels[n];
|
ImDrawChannel* channel = &splitter->_Channels[n];
|
||||||
memcpy(dst_tmp++, channel, sizeof(ImDrawChannel));
|
memcpy(dst_tmp++, channel, sizeof(ImDrawChannel));
|
||||||
@@ -2516,7 +2562,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
|
|||||||
{
|
{
|
||||||
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
|
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
|
||||||
{
|
{
|
||||||
if (!(table->EnabledMaskByDisplayOrder & ((ImU64)1 << order_n)))
|
if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const int column_n = table->DisplayOrderToIndex[order_n];
|
const int column_n = table->DisplayOrderToIndex[order_n];
|
||||||
@@ -2844,10 +2890,9 @@ void ImGui::TableHeadersRow()
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Push an id to allow unnamed labels (generally accidental, but let's behave nicely with them)
|
// Push an id to allow unnamed labels (generally accidental, but let's behave nicely with them)
|
||||||
// - in your own code you may omit the PushID/PopID all-together, provided you know they won't collide
|
// In your own code you may omit the PushID/PopID all-together, provided you know they won't collide.
|
||||||
// - table->InstanceCurrent is only >0 when we use multiple BeginTable/EndTable calls with same identifier.
|
|
||||||
const char* name = (TableGetColumnFlags(column_n) & ImGuiTableColumnFlags_NoHeaderLabel) ? "" : TableGetColumnName(column_n);
|
const char* name = (TableGetColumnFlags(column_n) & ImGuiTableColumnFlags_NoHeaderLabel) ? "" : TableGetColumnName(column_n);
|
||||||
PushID(table->InstanceCurrent * table->ColumnsCount + column_n);
|
PushID(column_n);
|
||||||
TableHeader(name);
|
TableHeader(name);
|
||||||
PopID();
|
PopID();
|
||||||
}
|
}
|
||||||
@@ -2993,7 +3038,7 @@ void ImGui::TableHeader(const char* label)
|
|||||||
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size);
|
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size);
|
||||||
|
|
||||||
const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x);
|
const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x);
|
||||||
if (text_clipped && hovered && g.HoveredIdNotActiveTimer > g.TooltipSlowDelay)
|
if (text_clipped && hovered && g.ActiveId == 0 && IsItemHovered(ImGuiHoveredFlags_DelayNormal))
|
||||||
SetTooltip("%.*s", (int)(label_end - label), label);
|
SetTooltip("%.*s", (int)(label_end - label), label);
|
||||||
|
|
||||||
// We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden
|
// We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden
|
||||||
@@ -3028,6 +3073,17 @@ void ImGui::TableOpenContextMenu(int column_n)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ImGui::TableBeginContextMenuPopup(ImGuiTable* table)
|
||||||
|
{
|
||||||
|
if (!table->IsContextPopupOpen || table->InstanceCurrent != table->InstanceInteracted)
|
||||||
|
return false;
|
||||||
|
const ImGuiID context_menu_id = ImHashStr("##ContextMenu", 0, table->ID);
|
||||||
|
if (BeginPopupEx(context_menu_id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings))
|
||||||
|
return true;
|
||||||
|
table->IsContextPopupOpen = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Output context menu into current window (generally a popup)
|
// Output context menu into current window (generally a popup)
|
||||||
// FIXME-TABLE: Ideally this should be writable by the user. Full programmatic access to that data?
|
// FIXME-TABLE: Ideally this should be writable by the user. Full programmatic access to that data?
|
||||||
void ImGui::TableDrawContextMenu(ImGuiTable* table)
|
void ImGui::TableDrawContextMenu(ImGuiTable* table)
|
||||||
@@ -3047,15 +3103,15 @@ void ImGui::TableDrawContextMenu(ImGuiTable* table)
|
|||||||
if (column != NULL)
|
if (column != NULL)
|
||||||
{
|
{
|
||||||
const bool can_resize = !(column->Flags & ImGuiTableColumnFlags_NoResize) && column->IsEnabled;
|
const bool can_resize = !(column->Flags & ImGuiTableColumnFlags_NoResize) && column->IsEnabled;
|
||||||
if (MenuItem("Size column to fit###SizeOne", NULL, false, can_resize))
|
if (MenuItem(LocalizeGetMsg(ImGuiLocKey_TableSizeOne), NULL, false, can_resize)) // "###SizeOne"
|
||||||
TableSetColumnWidthAutoSingle(table, column_n);
|
TableSetColumnWidthAutoSingle(table, column_n);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* size_all_desc;
|
const char* size_all_desc;
|
||||||
if (table->ColumnsEnabledFixedCount == table->ColumnsEnabledCount && (table->Flags & ImGuiTableFlags_SizingMask_) != ImGuiTableFlags_SizingFixedSame)
|
if (table->ColumnsEnabledFixedCount == table->ColumnsEnabledCount && (table->Flags & ImGuiTableFlags_SizingMask_) != ImGuiTableFlags_SizingFixedSame)
|
||||||
size_all_desc = "Size all columns to fit###SizeAll"; // All fixed
|
size_all_desc = LocalizeGetMsg(ImGuiLocKey_TableSizeAllFit); // "###SizeAll" All fixed
|
||||||
else
|
else
|
||||||
size_all_desc = "Size all columns to default###SizeAll"; // All stretch or mixed
|
size_all_desc = LocalizeGetMsg(ImGuiLocKey_TableSizeAllDefault); // "###SizeAll" All stretch or mixed
|
||||||
if (MenuItem(size_all_desc, NULL))
|
if (MenuItem(size_all_desc, NULL))
|
||||||
TableSetColumnWidthAutoAll(table);
|
TableSetColumnWidthAutoAll(table);
|
||||||
want_separator = true;
|
want_separator = true;
|
||||||
@@ -3064,7 +3120,7 @@ void ImGui::TableDrawContextMenu(ImGuiTable* table)
|
|||||||
// Ordering
|
// Ordering
|
||||||
if (table->Flags & ImGuiTableFlags_Reorderable)
|
if (table->Flags & ImGuiTableFlags_Reorderable)
|
||||||
{
|
{
|
||||||
if (MenuItem("Reset order", NULL, false, !table->IsDefaultDisplayOrder))
|
if (MenuItem(LocalizeGetMsg(ImGuiLocKey_TableResetOrder), NULL, false, !table->IsDefaultDisplayOrder))
|
||||||
table->IsResetDisplayOrderRequest = true;
|
table->IsResetDisplayOrderRequest = true;
|
||||||
want_separator = true;
|
want_separator = true;
|
||||||
}
|
}
|
||||||
@@ -3457,7 +3513,7 @@ void ImGui::TableSettingsAddSettingsHandler()
|
|||||||
// Remove Table (currently only used by TestEngine)
|
// Remove Table (currently only used by TestEngine)
|
||||||
void ImGui::TableRemove(ImGuiTable* table)
|
void ImGui::TableRemove(ImGuiTable* table)
|
||||||
{
|
{
|
||||||
//IMGUI_DEBUG_LOG("TableRemove() id=0x%08X\n", table->ID);
|
//IMGUI_DEBUG_PRINT("TableRemove() id=0x%08X\n", table->ID);
|
||||||
ImGuiContext& g = *GImGui;
|
ImGuiContext& g = *GImGui;
|
||||||
int table_idx = g.Tables.GetIndex(table);
|
int table_idx = g.Tables.GetIndex(table);
|
||||||
//memset(table->RawData.Data, 0, table->RawData.size_in_bytes());
|
//memset(table->RawData.Data, 0, table->RawData.size_in_bytes());
|
||||||
@@ -3469,7 +3525,7 @@ void ImGui::TableRemove(ImGuiTable* table)
|
|||||||
// Free up/compact internal Table buffers for when it gets unused
|
// Free up/compact internal Table buffers for when it gets unused
|
||||||
void ImGui::TableGcCompactTransientBuffers(ImGuiTable* table)
|
void ImGui::TableGcCompactTransientBuffers(ImGuiTable* table)
|
||||||
{
|
{
|
||||||
//IMGUI_DEBUG_LOG("TableGcCompactTransientBuffers() id=0x%08X\n", table->ID);
|
//IMGUI_DEBUG_PRINT("TableGcCompactTransientBuffers() id=0x%08X\n", table->ID);
|
||||||
ImGuiContext& g = *GImGui;
|
ImGuiContext& g = *GImGui;
|
||||||
IM_ASSERT(table->MemoryCompacted == false);
|
IM_ASSERT(table->MemoryCompacted == false);
|
||||||
table->SortSpecs.Specs = NULL;
|
table->SortSpecs.Specs = NULL;
|
||||||
@@ -3513,7 +3569,7 @@ void ImGui::TableGcCompactSettings()
|
|||||||
// - DebugNodeTable() [Internal]
|
// - DebugNodeTable() [Internal]
|
||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
#ifndef IMGUI_DISABLE_METRICS_WINDOW
|
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
|
||||||
|
|
||||||
static const char* DebugNodeTableGetSizingPolicyDesc(ImGuiTableFlags sizing_policy)
|
static const char* DebugNodeTableGetSizingPolicyDesc(ImGuiTableFlags sizing_policy)
|
||||||
{
|
{
|
||||||
@@ -3607,7 +3663,7 @@ void ImGui::DebugNodeTableSettings(ImGuiTableSettings* settings)
|
|||||||
TreePop();
|
TreePop();
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // #ifndef IMGUI_DISABLE_METRICS_WINDOW
|
#else // #ifndef IMGUI_DISABLE_DEBUG_TOOLS
|
||||||
|
|
||||||
void ImGui::DebugNodeTable(ImGuiTable*) {}
|
void ImGui::DebugNodeTable(ImGuiTable*) {}
|
||||||
void ImGui::DebugNodeTableSettings(ImGuiTableSettings*) {}
|
void ImGui::DebugNodeTableSettings(ImGuiTableSettings*) {}
|
||||||
@@ -3947,6 +4003,7 @@ void ImGui::NextColumn()
|
|||||||
{
|
{
|
||||||
// New row/line: column 0 honor IndentX.
|
// New row/line: column 0 honor IndentX.
|
||||||
window->DC.ColumnsOffset.x = ImMax(column_padding - window->WindowPadding.x, 0.0f);
|
window->DC.ColumnsOffset.x = ImMax(column_padding - window->WindowPadding.x, 0.0f);
|
||||||
|
window->DC.IsSameLine = false;
|
||||||
columns->LineMinY = columns->LineMaxY;
|
columns->LineMinY = columns->LineMaxY;
|
||||||
}
|
}
|
||||||
window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
|
window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
|
||||||
@@ -3998,8 +4055,7 @@ void ImGui::EndColumns()
|
|||||||
const ImGuiID column_id = columns->ID + ImGuiID(n);
|
const ImGuiID column_id = columns->ID + ImGuiID(n);
|
||||||
const float column_hit_hw = COLUMNS_HIT_RECT_HALF_WIDTH;
|
const float column_hit_hw = COLUMNS_HIT_RECT_HALF_WIDTH;
|
||||||
const ImRect column_hit_rect(ImVec2(x - column_hit_hw, y1), ImVec2(x + column_hit_hw, y2));
|
const ImRect column_hit_rect(ImVec2(x - column_hit_hw, y1), ImVec2(x + column_hit_hw, y2));
|
||||||
KeepAliveID(column_id);
|
if (!ItemAdd(column_hit_rect, column_id, NULL, ImGuiItemFlags_NoNav))
|
||||||
if (IsClippedEx(column_hit_rect, column_id)) // FIXME: Can be removed or replaced with a lower-level test
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
bool hovered = false, held = false;
|
bool hovered = false, held = false;
|
||||||
|
|||||||
Vendored
+831
-497
File diff suppressed because it is too large
Load Diff
Vendored
+7
-17
@@ -2,6 +2,7 @@
|
|||||||
// This is a slightly modified version of stb_textedit.h 1.14.
|
// This is a slightly modified version of stb_textedit.h 1.14.
|
||||||
// Those changes would need to be pushed into nothings/stb:
|
// Those changes would need to be pushed into nothings/stb:
|
||||||
// - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321)
|
// - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321)
|
||||||
|
// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000)
|
||||||
// Grep for [DEAR IMGUI] to find the changes.
|
// Grep for [DEAR IMGUI] to find the changes.
|
||||||
|
|
||||||
// stb_textedit.h - v1.14 - public domain - Sean Barrett
|
// stb_textedit.h - v1.14 - public domain - Sean Barrett
|
||||||
@@ -524,29 +525,14 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s
|
|||||||
int z = STB_TEXTEDIT_STRINGLEN(str);
|
int z = STB_TEXTEDIT_STRINGLEN(str);
|
||||||
int i=0, first;
|
int i=0, first;
|
||||||
|
|
||||||
if (n == z) {
|
if (n == z && single_line) {
|
||||||
// if it's at the end, then find the last line -- simpler than trying to
|
// special case if it's at the end (may not be needed?)
|
||||||
// explicitly handle this case in the regular code
|
|
||||||
if (single_line) {
|
|
||||||
STB_TEXTEDIT_LAYOUTROW(&r, str, 0);
|
STB_TEXTEDIT_LAYOUTROW(&r, str, 0);
|
||||||
find->y = 0;
|
find->y = 0;
|
||||||
find->first_char = 0;
|
find->first_char = 0;
|
||||||
find->length = z;
|
find->length = z;
|
||||||
find->height = r.ymax - r.ymin;
|
find->height = r.ymax - r.ymin;
|
||||||
find->x = r.x1;
|
find->x = r.x1;
|
||||||
} else {
|
|
||||||
find->y = 0;
|
|
||||||
find->x = 0;
|
|
||||||
find->height = 1;
|
|
||||||
while (i < z) {
|
|
||||||
STB_TEXTEDIT_LAYOUTROW(&r, str, i);
|
|
||||||
prev_start = i;
|
|
||||||
i += r.num_chars;
|
|
||||||
}
|
|
||||||
find->first_char = i;
|
|
||||||
find->length = 0;
|
|
||||||
find->prev_first = prev_start;
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -557,9 +543,13 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s
|
|||||||
STB_TEXTEDIT_LAYOUTROW(&r, str, i);
|
STB_TEXTEDIT_LAYOUTROW(&r, str, i);
|
||||||
if (n < i + r.num_chars)
|
if (n < i + r.num_chars)
|
||||||
break;
|
break;
|
||||||
|
if (i + r.num_chars == z && z > 0 && STB_TEXTEDIT_GETCHAR(str, z - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] special handling for last line
|
||||||
|
break; // [DEAR IMGUI]
|
||||||
prev_start = i;
|
prev_start = i;
|
||||||
i += r.num_chars;
|
i += r.num_chars;
|
||||||
find->y += r.baseline_y_delta;
|
find->y += r.baseline_y_delta;
|
||||||
|
if (i == z) // [DEAR IMGUI]
|
||||||
|
break; // [DEAR IMGUI]
|
||||||
}
|
}
|
||||||
|
|
||||||
find->first_char = first = i;
|
find->first_char = first = i;
|
||||||
|
|||||||
Vendored
+1
-1
@@ -2008,7 +2008,7 @@ static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int gly
|
|||||||
start = end;
|
start = end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fdselector == -1) stbtt__new_buf(NULL, 0);
|
if (fdselector == -1) return stbtt__new_buf(NULL, 0); // [DEAR IMGUI] fixed, see #6007 and nothings/stb#1422
|
||||||
return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector));
|
return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-7
@@ -11,13 +11,13 @@ namespace games::bc {
|
|||||||
Game::attach();
|
Game::attach();
|
||||||
|
|
||||||
setupapihook_init(libutils::load_library("libaio.dll"));
|
setupapihook_init(libutils::load_library("libaio.dll"));
|
||||||
setupapihook_add({
|
|
||||||
{ 0, 0, 0, 0, },
|
SETUPAPI_SETTINGS settings {};
|
||||||
"",
|
const char hwid[] = "USB\\VID_1CCF&PID_8050\\0000";
|
||||||
"USB\\VID_1CCF&PID_8050\\0000",
|
memcpy(settings.property_hardwareid, hwid, sizeof(hwid));
|
||||||
{},
|
const char comport[] = "COM3";
|
||||||
"COM3",
|
memcpy(settings.interface_detail, comport, sizeof(comport));
|
||||||
});
|
setupapihook_add(settings);
|
||||||
|
|
||||||
auto iob = new acio2emu::IOBHandle(L"COM3");
|
auto iob = new acio2emu::IOBHandle(L"COM3");
|
||||||
iob->register_node(std::make_unique<TBSNode>());
|
iob->register_node(std::make_unique<TBSNode>());
|
||||||
|
|||||||
+88
-17
@@ -15,6 +15,7 @@
|
|||||||
#include "acioemu/handle.h"
|
#include "acioemu/handle.h"
|
||||||
#include "avs/core.h"
|
#include "avs/core.h"
|
||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
|
#include "cfg/configurator.h"
|
||||||
#include "games/io.h"
|
#include "games/io.h"
|
||||||
#include "hooks/avshook.h"
|
#include "hooks/avshook.h"
|
||||||
#include "hooks/cfgmgr32hook.h"
|
#include "hooks/cfgmgr32hook.h"
|
||||||
@@ -23,6 +24,7 @@
|
|||||||
#include "hooks/setupapihook.h"
|
#include "hooks/setupapihook.h"
|
||||||
#include "hooks/sleephook.h"
|
#include "hooks/sleephook.h"
|
||||||
#include "launcher/options.h"
|
#include "launcher/options.h"
|
||||||
|
#include "touch/touch.h"
|
||||||
#include "misc/wintouchemu.h"
|
#include "misc/wintouchemu.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
@@ -56,15 +58,19 @@ namespace games::iidx {
|
|||||||
bool FLIP_CAMS = false;
|
bool FLIP_CAMS = false;
|
||||||
bool DISABLE_CAMS = false;
|
bool DISABLE_CAMS = false;
|
||||||
bool TDJ_MODE = false;
|
bool TDJ_MODE = false;
|
||||||
|
bool FORCE_720P = false;
|
||||||
std::optional<std::string> SOUND_OUTPUT_DEVICE = std::nullopt;
|
std::optional<std::string> SOUND_OUTPUT_DEVICE = std::nullopt;
|
||||||
std::optional<std::string> ASIO_DRIVER = std::nullopt;
|
std::optional<std::string> ASIO_DRIVER = std::nullopt;
|
||||||
|
uint8_t DIGITAL_TT_SENS = 4;
|
||||||
|
std::optional<std::string> SUBSCREEN_OVERLAY_SIZE = std::nullopt;
|
||||||
|
|
||||||
// states
|
// states
|
||||||
static HKEY real_asio_reg_handle = nullptr;
|
static HKEY real_asio_reg_handle = nullptr;
|
||||||
static HKEY real_asio_device_reg_handle = nullptr;
|
static HKEY real_asio_device_reg_handle = nullptr;
|
||||||
static uint16_t IIDXIO_TT_STATE[2]{};
|
static uint16_t IIDXIO_TT_STATE[2]{};
|
||||||
static uint16_t IIDXIO_TT_DIRECTION[2]{};
|
static int8_t IIDXIO_TT_DIRECTION[2]{1, 1};
|
||||||
static bool IIDXIO_TT_PRESSED[2]{};
|
static bool IIDXIO_TT_PRESSED[2]{};
|
||||||
|
static bool IIDXIO_TT_ALT_PRESSED[2]{};
|
||||||
char IIDXIO_LED_TICKER[10] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '\x00'};
|
char IIDXIO_LED_TICKER[10] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '\x00'};
|
||||||
bool IIDXIO_LED_TICKER_READONLY = false;
|
bool IIDXIO_LED_TICKER_READONLY = false;
|
||||||
std::mutex IIDX_LED_TICKER_LOCK;
|
std::mutex IIDX_LED_TICKER_LOCK;
|
||||||
@@ -445,8 +451,8 @@ namespace games::iidx {
|
|||||||
// need to hook `avs2-core.dll` so AVS win32fs operations go through rom hook
|
// need to hook `avs2-core.dll` so AVS win32fs operations go through rom hook
|
||||||
devicehook_init(avs::core::DLL_INSTANCE);
|
devicehook_init(avs::core::DLL_INSTANCE);
|
||||||
|
|
||||||
// hook touch window
|
|
||||||
wintouchemu::FORCE = true;
|
wintouchemu::FORCE = true;
|
||||||
|
wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true;
|
||||||
wintouchemu::hook_title_ends("beatmania IIDX", "main", avs::game::DLL_INSTANCE);
|
wintouchemu::hook_title_ends("beatmania IIDX", "main", avs::game::DLL_INSTANCE);
|
||||||
|
|
||||||
// prevent crash on TDJ mode without correct DLL
|
// prevent crash on TDJ mode without correct DLL
|
||||||
@@ -538,8 +544,21 @@ namespace games::iidx {
|
|||||||
if (DISABLE_CAMS) {
|
if (DISABLE_CAMS) {
|
||||||
SetEnvironmentVariable("CONNECT_CAMERA", "0");
|
SetEnvironmentVariable("CONNECT_CAMERA", "0");
|
||||||
}
|
}
|
||||||
if (SOUND_OUTPUT_DEVICE.has_value()) {
|
|
||||||
SetEnvironmentVariable("SOUND_OUTPUT_DEVICE", SOUND_OUTPUT_DEVICE.value().c_str());
|
this->detect_sound_output_device();
|
||||||
|
|
||||||
|
// check bad model name
|
||||||
|
if (!cfg::CONFIGURATOR_STANDALONE && avs::game::is_model("TDJ")) {
|
||||||
|
log_fatal(
|
||||||
|
"iidx",
|
||||||
|
"BAD MODEL NAME ERROR\n\n\n"
|
||||||
|
"!!! model name set to TDJ, this is WRONG and will break your game !!!\n"
|
||||||
|
"If you are trying to boot IIDX with Lightning Model mode, please do the following instead:\n"
|
||||||
|
" Revert your changes to XML file so it says <model __type=\"str\">LDJ</model>\n"
|
||||||
|
" In SpiceCfg, enable 'IIDX TDJ Mode' or provide -iidxtdj flag in command line\n"
|
||||||
|
" Apply any applicable settings / patches / hex edits\n"
|
||||||
|
"!!! model name set to TDJ, this is WRONG and will break your game !!!\n\n\n"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -549,6 +568,53 @@ namespace games::iidx {
|
|||||||
devicehook_dispose();
|
devicehook_dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IIDXGame::detect_sound_output_device() {
|
||||||
|
// if the user specified a value (other than auto), use it as the environment var
|
||||||
|
// probably "wasapi" or "asio", but it's not explicitly checked here for forward compat
|
||||||
|
if (SOUND_OUTPUT_DEVICE.has_value() && SOUND_OUTPUT_DEVICE.value() != "auto") {
|
||||||
|
log_info(
|
||||||
|
"iidx",
|
||||||
|
"using user-supplied \"{}\" for SOUND_OUTPUT_DEVICE",
|
||||||
|
SOUND_OUTPUT_DEVICE.value());
|
||||||
|
SetEnvironmentVariable("SOUND_OUTPUT_DEVICE", SOUND_OUTPUT_DEVICE.value().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// automatic detection
|
||||||
|
bool use_asio = false;
|
||||||
|
log_misc("iidx", "auto-detect SOUND_OUTPUT_DEVICE...");
|
||||||
|
if (ASIO_DRIVER.has_value()) {
|
||||||
|
log_misc(
|
||||||
|
"iidx",
|
||||||
|
"-iidxasio is set to \"{}\", use asio for SOUND_OUTPUT_DEVICE",
|
||||||
|
ASIO_DRIVER.value());
|
||||||
|
use_asio = true;
|
||||||
|
} else {
|
||||||
|
HKEY subkey;
|
||||||
|
LSTATUS result;
|
||||||
|
result = RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\ASIO\\XONAR SOUND CARD(64)", &subkey);
|
||||||
|
if (result == ERROR_SUCCESS) {
|
||||||
|
RegCloseKey(subkey);
|
||||||
|
use_asio = true;
|
||||||
|
log_misc(
|
||||||
|
"iidx",
|
||||||
|
"found HKLM\\SOFTWARE\\ASIO\\XONAR SOUND CARD(64), using asio for SOUND_OUTPUT_DEVICE");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* device = "wasapi";
|
||||||
|
if (use_asio) {
|
||||||
|
device = "asio";
|
||||||
|
}
|
||||||
|
log_info("iidx", "SOUND_OUTPUT_DEVICE set to {}", device);
|
||||||
|
log_info(
|
||||||
|
"iidx",
|
||||||
|
"SOUND_OUTPUT_DEVICE only affects IIDX27+ and ignored by older games. "
|
||||||
|
"If {} is not what you wanted, you can override it with -iidxsounddevice option",
|
||||||
|
device);
|
||||||
|
SetEnvironmentVariable("SOUND_OUTPUT_DEVICE", device);
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t get_pad() {
|
uint32_t get_pad() {
|
||||||
uint32_t pad = 0;
|
uint32_t pad = 0;
|
||||||
|
|
||||||
@@ -701,7 +767,8 @@ namespace games::iidx {
|
|||||||
unsigned char get_tt(int player, bool slow) {
|
unsigned char get_tt(int player, bool slow) {
|
||||||
|
|
||||||
// check change value for high/low precision
|
// check change value for high/low precision
|
||||||
uint16_t change = slow ? (uint16_t) 1 : (uint16_t) 4;
|
uint16_t change =
|
||||||
|
slow ? (uint16_t) (DIGITAL_TT_SENS / 4) : (uint16_t) DIGITAL_TT_SENS;
|
||||||
|
|
||||||
// check player number
|
// check player number
|
||||||
if (player > 1)
|
if (player > 1)
|
||||||
@@ -713,8 +780,11 @@ namespace games::iidx {
|
|||||||
player != 0 ? Buttons::P2_TTPlus : Buttons::P1_TTPlus));
|
player != 0 ? Buttons::P2_TTPlus : Buttons::P1_TTPlus));
|
||||||
bool ttm = GameAPI::Buttons::getState(RI_MGR, buttons.at(
|
bool ttm = GameAPI::Buttons::getState(RI_MGR, buttons.at(
|
||||||
player != 0 ? Buttons::P2_TTMinus : Buttons::P1_TTMinus));
|
player != 0 ? Buttons::P2_TTMinus : Buttons::P1_TTMinus));
|
||||||
|
|
||||||
bool ttpm = GameAPI::Buttons::getState(RI_MGR, buttons.at(
|
bool ttpm = GameAPI::Buttons::getState(RI_MGR, buttons.at(
|
||||||
player != 0 ? Buttons::P2_TTPlusMinus : Buttons::P1_TTPlusMinus));
|
player != 0 ? Buttons::P2_TTPlusMinus : Buttons::P1_TTPlusMinus));
|
||||||
|
bool ttpm_alt = GameAPI::Buttons::getState(RI_MGR, buttons.at(
|
||||||
|
player != 0 ? Buttons::P2_TTPlusMinusAlt : Buttons::P1_TTPlusMinusAlt));
|
||||||
|
|
||||||
// TT+
|
// TT+
|
||||||
if (ttp)
|
if (ttp)
|
||||||
@@ -725,19 +795,16 @@ namespace games::iidx {
|
|||||||
IIDXIO_TT_STATE[player] -= change;
|
IIDXIO_TT_STATE[player] -= change;
|
||||||
|
|
||||||
// TT+/-
|
// TT+/-
|
||||||
if (ttpm) {
|
bool ttpm_rising_edge = !IIDXIO_TT_PRESSED[player] && ttpm;
|
||||||
if (IIDXIO_TT_DIRECTION[player] != 0u) {
|
bool ttpm_alt_rising_edge = !IIDXIO_TT_ALT_PRESSED[player] && ttpm_alt;
|
||||||
if (!IIDXIO_TT_PRESSED[player])
|
if (ttpm_rising_edge || ttpm_alt_rising_edge) {
|
||||||
IIDXIO_TT_DIRECTION[player] = 0;
|
IIDXIO_TT_DIRECTION[player] *= -1;
|
||||||
IIDXIO_TT_STATE[player] += change;
|
|
||||||
} else {
|
|
||||||
if (!IIDXIO_TT_PRESSED[player])
|
|
||||||
IIDXIO_TT_DIRECTION[player] = 1;
|
|
||||||
IIDXIO_TT_STATE[player] -= change;
|
|
||||||
}
|
}
|
||||||
IIDXIO_TT_PRESSED[player] = true;
|
if (ttpm || ttpm_alt) {
|
||||||
} else
|
IIDXIO_TT_STATE[player] += (change * IIDXIO_TT_DIRECTION[player]);
|
||||||
IIDXIO_TT_PRESSED[player] = false;
|
}
|
||||||
|
IIDXIO_TT_PRESSED[player] = ttpm;
|
||||||
|
IIDXIO_TT_ALT_PRESSED[player] = ttpm_alt;
|
||||||
|
|
||||||
// raw input
|
// raw input
|
||||||
auto &analogs = get_analogs();
|
auto &analogs = get_analogs();
|
||||||
@@ -793,4 +860,8 @@ namespace games::iidx {
|
|||||||
const char* get_16seg() {
|
const char* get_16seg() {
|
||||||
return IIDXIO_LED_TICKER;
|
return IIDXIO_LED_TICKER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_tdj_fhd() {
|
||||||
|
return TDJ_MODE && avs::game::is_ext(2022101900, MAXINT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,11 @@ namespace games::iidx {
|
|||||||
extern bool FLIP_CAMS;
|
extern bool FLIP_CAMS;
|
||||||
extern bool DISABLE_CAMS;
|
extern bool DISABLE_CAMS;
|
||||||
extern bool TDJ_MODE;
|
extern bool TDJ_MODE;
|
||||||
|
extern bool FORCE_720P;
|
||||||
extern std::optional<std::string> SOUND_OUTPUT_DEVICE;
|
extern std::optional<std::string> SOUND_OUTPUT_DEVICE;
|
||||||
extern std::optional<std::string> ASIO_DRIVER;
|
extern std::optional<std::string> ASIO_DRIVER;
|
||||||
|
extern uint8_t DIGITAL_TT_SENS;
|
||||||
|
extern std::optional<std::string> SUBSCREEN_OVERLAY_SIZE;
|
||||||
|
|
||||||
// state
|
// state
|
||||||
extern char IIDXIO_LED_TICKER[10];
|
extern char IIDXIO_LED_TICKER[10];
|
||||||
@@ -27,6 +30,9 @@ namespace games::iidx {
|
|||||||
virtual void attach() override;
|
virtual void attach() override;
|
||||||
virtual void pre_attach() override;
|
virtual void pre_attach() override;
|
||||||
virtual void detach() override;
|
virtual void detach() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void detect_sound_output_device();
|
||||||
};
|
};
|
||||||
|
|
||||||
// helper methods
|
// helper methods
|
||||||
@@ -38,4 +44,5 @@ namespace games::iidx {
|
|||||||
unsigned char get_tt(int player, bool slow);
|
unsigned char get_tt(int player, bool slow);
|
||||||
unsigned char get_slider(uint8_t slider);
|
unsigned char get_slider(uint8_t slider);
|
||||||
const char* get_16seg();
|
const char* get_16seg();
|
||||||
|
bool is_tdj_fhd();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ std::vector<Button> &games::iidx::get_buttons() {
|
|||||||
"P1 TT+",
|
"P1 TT+",
|
||||||
"P1 TT-",
|
"P1 TT-",
|
||||||
"P1 TT+/-",
|
"P1 TT+/-",
|
||||||
|
"P1 TT+/- Alternate",
|
||||||
"P1 Start",
|
"P1 Start",
|
||||||
"P2 1",
|
"P2 1",
|
||||||
"P2 2",
|
"P2 2",
|
||||||
@@ -32,6 +33,7 @@ std::vector<Button> &games::iidx::get_buttons() {
|
|||||||
"P2 TT+",
|
"P2 TT+",
|
||||||
"P2 TT-",
|
"P2 TT-",
|
||||||
"P2 TT+/-",
|
"P2 TT+/-",
|
||||||
|
"P2 TT+/- Alternate",
|
||||||
"P2 Start",
|
"P2 Start",
|
||||||
"EFFECT",
|
"EFFECT",
|
||||||
"VEFX",
|
"VEFX",
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ namespace games::iidx {
|
|||||||
P1_TTPlus,
|
P1_TTPlus,
|
||||||
P1_TTMinus,
|
P1_TTMinus,
|
||||||
P1_TTPlusMinus,
|
P1_TTPlusMinus,
|
||||||
|
P1_TTPlusMinusAlt,
|
||||||
P1_Start,
|
P1_Start,
|
||||||
P2_1,
|
P2_1,
|
||||||
P2_2,
|
P2_2,
|
||||||
@@ -32,6 +33,7 @@ namespace games::iidx {
|
|||||||
P2_TTPlus,
|
P2_TTPlus,
|
||||||
P2_TTMinus,
|
P2_TTMinus,
|
||||||
P2_TTPlusMinus,
|
P2_TTPlusMinus,
|
||||||
|
P2_TTPlusMinusAlt,
|
||||||
P2_Start,
|
P2_Start,
|
||||||
Effect,
|
Effect,
|
||||||
VEFX,
|
VEFX,
|
||||||
|
|||||||
+12
-10
@@ -366,8 +366,14 @@ namespace games {
|
|||||||
std::vector<unsigned short> vkey_defaults;
|
std::vector<unsigned short> vkey_defaults;
|
||||||
|
|
||||||
// overlay button definitions
|
// overlay button definitions
|
||||||
|
names.emplace_back("Screenshot");
|
||||||
|
vkey_defaults.push_back(VK_SNAPSHOT);
|
||||||
|
names.emplace_back("Toggle Sub Screen");
|
||||||
|
vkey_defaults.push_back(VK_PRIOR);
|
||||||
names.emplace_back("Insert Coin");
|
names.emplace_back("Insert Coin");
|
||||||
vkey_defaults.push_back(VK_F1);
|
vkey_defaults.push_back(VK_F1);
|
||||||
|
names.emplace_back("Toggle IO Panel");
|
||||||
|
vkey_defaults.push_back(VK_F2);
|
||||||
names.emplace_back("Toggle Config");
|
names.emplace_back("Toggle Config");
|
||||||
vkey_defaults.push_back(VK_F4);
|
vkey_defaults.push_back(VK_F4);
|
||||||
names.emplace_back("Toggle Virtual Keypad P1");
|
names.emplace_back("Toggle Virtual Keypad P1");
|
||||||
@@ -382,12 +388,14 @@ namespace games {
|
|||||||
vkey_defaults.push_back(VK_F9);
|
vkey_defaults.push_back(VK_F9);
|
||||||
names.emplace_back("Toggle Patch Manager");
|
names.emplace_back("Toggle Patch Manager");
|
||||||
vkey_defaults.push_back(VK_F10);
|
vkey_defaults.push_back(VK_F10);
|
||||||
names.emplace_back("Toggle Sub Screen");
|
names.emplace_back("Toggle Screen Resize");
|
||||||
vkey_defaults.push_back(VK_PRIOR);
|
vkey_defaults.push_back(VK_F11);
|
||||||
names.emplace_back("Toggle VR Control");
|
|
||||||
vkey_defaults.push_back(0xFF);
|
|
||||||
names.emplace_back("Toggle Overlay");
|
names.emplace_back("Toggle Overlay");
|
||||||
vkey_defaults.push_back(VK_F12);
|
vkey_defaults.push_back(VK_F12);
|
||||||
|
names.emplace_back("Toggle VR Control");
|
||||||
|
vkey_defaults.push_back(0xFF);
|
||||||
|
names.emplace_back("Screen Resize");
|
||||||
|
vkey_defaults.push_back(0xFF);
|
||||||
names.emplace_back("Navigator Activate");
|
names.emplace_back("Navigator Activate");
|
||||||
vkey_defaults.push_back(0xFF);
|
vkey_defaults.push_back(0xFF);
|
||||||
names.emplace_back("Navigator Cancel");
|
names.emplace_back("Navigator Cancel");
|
||||||
@@ -400,18 +408,12 @@ namespace games {
|
|||||||
vkey_defaults.push_back(0xFF);
|
vkey_defaults.push_back(0xFF);
|
||||||
names.emplace_back("Navigator Right");
|
names.emplace_back("Navigator Right");
|
||||||
vkey_defaults.push_back(0xFF);
|
vkey_defaults.push_back(0xFF);
|
||||||
names.emplace_back("Screenshot");
|
|
||||||
vkey_defaults.push_back(VK_SNAPSHOT);
|
|
||||||
names.emplace_back("Hotkey Enable 1");
|
names.emplace_back("Hotkey Enable 1");
|
||||||
vkey_defaults.push_back(0xFF);
|
vkey_defaults.push_back(0xFF);
|
||||||
names.emplace_back("Hotkey Enable 2");
|
names.emplace_back("Hotkey Enable 2");
|
||||||
vkey_defaults.push_back(0xFF);
|
vkey_defaults.push_back(0xFF);
|
||||||
names.emplace_back("Hotkey Toggle");
|
names.emplace_back("Hotkey Toggle");
|
||||||
vkey_defaults.push_back(0xFF);
|
vkey_defaults.push_back(0xFF);
|
||||||
names.emplace_back("Screen Resize");
|
|
||||||
vkey_defaults.push_back(0xFF);
|
|
||||||
names.emplace_back("Toggle Screen Resize");
|
|
||||||
vkey_defaults.push_back(0xFF);
|
|
||||||
|
|
||||||
// return sorted buttons
|
// return sorted buttons
|
||||||
buttons = GameAPI::Buttons::sortButtons(buttons, names, &vkey_defaults);
|
buttons = GameAPI::Buttons::sortButtons(buttons, names, &vkey_defaults);
|
||||||
|
|||||||
+6
-5
@@ -7,7 +7,10 @@ namespace games {
|
|||||||
|
|
||||||
namespace OverlayButtons {
|
namespace OverlayButtons {
|
||||||
enum {
|
enum {
|
||||||
|
Screenshot,
|
||||||
|
ToggleSubScreen,
|
||||||
InsertCoin,
|
InsertCoin,
|
||||||
|
ToggleIOPanel,
|
||||||
ToggleConfig,
|
ToggleConfig,
|
||||||
ToggleVirtualKeypadP1,
|
ToggleVirtualKeypadP1,
|
||||||
ToggleVirtualKeypadP2,
|
ToggleVirtualKeypadP2,
|
||||||
@@ -15,21 +18,19 @@ namespace games {
|
|||||||
ToggleLog,
|
ToggleLog,
|
||||||
ToggleControl,
|
ToggleControl,
|
||||||
TogglePatchManager,
|
TogglePatchManager,
|
||||||
ToggleSubScreen,
|
ToggleScreenResize,
|
||||||
ToggleVRControl,
|
|
||||||
ToggleOverlay,
|
ToggleOverlay,
|
||||||
|
ToggleVRControl,
|
||||||
|
ScreenResize,
|
||||||
NavigatorActivate,
|
NavigatorActivate,
|
||||||
NavigatorCancel,
|
NavigatorCancel,
|
||||||
NavigatorUp,
|
NavigatorUp,
|
||||||
NavigatorDown,
|
NavigatorDown,
|
||||||
NavigatorLeft,
|
NavigatorLeft,
|
||||||
NavigatorRight,
|
NavigatorRight,
|
||||||
Screenshot,
|
|
||||||
HotkeyEnable1,
|
HotkeyEnable1,
|
||||||
HotkeyEnable2,
|
HotkeyEnable2,
|
||||||
HotkeyToggle,
|
HotkeyToggle,
|
||||||
ScreenResize,
|
|
||||||
ToggleScreenResize,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,14 @@
|
|||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/libutils.h"
|
#include "util/libutils.h"
|
||||||
|
|
||||||
|
#define JB_BUTTON_SIZE 160
|
||||||
|
#define JB_BUTTON_GAP 37
|
||||||
|
#define JB_BUTTON_HITBOX (JB_BUTTON_SIZE + JB_BUTTON_GAP)
|
||||||
|
|
||||||
namespace games::jb {
|
namespace games::jb {
|
||||||
|
|
||||||
// touch stuff
|
// touch stuff
|
||||||
|
bool TOUCH_LEGACY_BOX = false;
|
||||||
static bool TOUCH_ENABLE = false;
|
static bool TOUCH_ENABLE = false;
|
||||||
static bool TOUCH_ATTACHED = false;
|
static bool TOUCH_ATTACHED = false;
|
||||||
static bool IS_PORTRAIT = true;
|
static bool IS_PORTRAIT = true;
|
||||||
@@ -68,8 +73,10 @@ namespace games::jb {
|
|||||||
memset(TOUCH_STATE, 0, sizeof(TOUCH_STATE));
|
memset(TOUCH_STATE, 0, sizeof(TOUCH_STATE));
|
||||||
|
|
||||||
// check touch points
|
// check touch points
|
||||||
|
// note that the IO code in device.cpp will correctly compensate for orientation, depending on the model.
|
||||||
TOUCH_POINTS.clear();
|
TOUCH_POINTS.clear();
|
||||||
touch_get_points(TOUCH_POINTS);
|
touch_get_points(TOUCH_POINTS);
|
||||||
|
if (TOUCH_LEGACY_BOX) {
|
||||||
auto offset = IS_PORTRAIT ? 580 : 0;
|
auto offset = IS_PORTRAIT ? 580 : 0;
|
||||||
for (auto &tp : TOUCH_POINTS) {
|
for (auto &tp : TOUCH_POINTS) {
|
||||||
|
|
||||||
@@ -83,6 +90,46 @@ namespace games::jb {
|
|||||||
TOUCH_STATE[index] = true;
|
TOUCH_STATE[index] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
for (auto &tp : TOUCH_POINTS) {
|
||||||
|
int x_relative = tp.x;
|
||||||
|
int y_relative = tp.y;
|
||||||
|
|
||||||
|
// x_relative and y_relative are relative to the top-left pixel of the first button
|
||||||
|
if (IS_PORTRAIT) {
|
||||||
|
// which is at (8, 602) in portrait:
|
||||||
|
// X: [8...759] (752 pixels wide)
|
||||||
|
// Y: [602...1353] (752 pixels high)
|
||||||
|
x_relative -= 8;
|
||||||
|
y_relative -= 602;
|
||||||
|
} else {
|
||||||
|
// and at (8, 8) in landscape
|
||||||
|
x_relative -= 8;
|
||||||
|
y_relative -= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x_relative < 0 || y_relative < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// x_hitbox and y_hitbox is relative to top-left pixel of each button
|
||||||
|
int x_index = x_relative / JB_BUTTON_HITBOX;
|
||||||
|
int x_hitbox = x_relative % JB_BUTTON_HITBOX;
|
||||||
|
int y_index = y_relative / JB_BUTTON_HITBOX;
|
||||||
|
int y_hitbox = y_relative % JB_BUTTON_HITBOX;
|
||||||
|
|
||||||
|
// check if the gap was touched
|
||||||
|
if (x_hitbox > JB_BUTTON_SIZE || y_hitbox > JB_BUTTON_SIZE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the corresponding state
|
||||||
|
int index = y_index * 4 + x_index;
|
||||||
|
if (0 <= index && index < 16) {
|
||||||
|
TOUCH_STATE[index] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
namespace games::jb {
|
namespace games::jb {
|
||||||
|
|
||||||
// touch stuff
|
// touch stuff
|
||||||
|
extern bool TOUCH_LEGACY_BOX;
|
||||||
extern bool TOUCH_STATE[16];
|
extern bool TOUCH_STATE[16];
|
||||||
void touch_update();
|
void touch_update();
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
namespace games::rb {
|
namespace games::rb {
|
||||||
|
|
||||||
|
extern uint16_t TOUCH_SCALING;
|
||||||
|
|
||||||
class RBGame : public games::Game {
|
class RBGame : public games::Game {
|
||||||
public:
|
public:
|
||||||
RBGame();
|
RBGame();
|
||||||
|
|||||||
+21
-4
@@ -11,15 +11,20 @@
|
|||||||
|
|
||||||
static std::string WINDOW_TITLE = "REFLEC BEAT";
|
static std::string WINDOW_TITLE = "REFLEC BEAT";
|
||||||
|
|
||||||
|
namespace games::rb {
|
||||||
|
uint16_t TOUCH_SCALING = 1000;
|
||||||
|
}
|
||||||
|
|
||||||
games::rb::ReflecBeatTouchDeviceHandle::ReflecBeatTouchDeviceHandle(bool log_fps) {
|
games::rb::ReflecBeatTouchDeviceHandle::ReflecBeatTouchDeviceHandle(bool log_fps) {
|
||||||
this->log_fps = log_fps;
|
this->log_fps = log_fps;
|
||||||
}
|
}
|
||||||
|
|
||||||
void games::rb::ReflecBeatTouchDeviceHandle::grid_insert(unsigned char *data, int cursor_x, int cursor_y) {
|
void games::rb::ReflecBeatTouchDeviceHandle::grid_insert(unsigned char *data, int cursor_x, int cursor_y) {
|
||||||
|
|
||||||
// scale to grid position
|
// scale to grid position - there are 48 columns and 76 rows of IR sensors.
|
||||||
|
// for whatever reason, the last y row (#75) results in weird input few rows above; just drop it
|
||||||
int grid_x = CLAMP((cursor_x * 48) / window_width, 0, 47);
|
int grid_x = CLAMP((cursor_x * 48) / window_width, 0, 47);
|
||||||
int grid_y = CLAMP((cursor_y * 76) / window_height, 0, 75);
|
int grid_y = CLAMP((cursor_y * 76) / window_height, 0, 74);
|
||||||
|
|
||||||
// get bit positions
|
// get bit positions
|
||||||
int bit_x = 88 + grid_x;
|
int bit_x = 88 + grid_x;
|
||||||
@@ -122,6 +127,8 @@ int games::rb::ReflecBeatTouchDeviceHandle::read(LPVOID lpBuffer, DWORD nNumberO
|
|||||||
data[0] = 0x55;
|
data[0] = 0x55;
|
||||||
data[2] = 0x4C;
|
data[2] = 0x4C;
|
||||||
|
|
||||||
|
const auto SCALE_FACTOR = games::rb::TOUCH_SCALING / 1000.f;
|
||||||
|
|
||||||
// iterate all touch points
|
// iterate all touch points
|
||||||
auto offset_x = (int) (window_width / 48.0 / 3.0);
|
auto offset_x = (int) (window_width / 48.0 / 3.0);
|
||||||
auto offset_y = (int) (window_height / 76.0 / 3.0);
|
auto offset_y = (int) (window_height / 76.0 / 3.0);
|
||||||
@@ -139,9 +146,19 @@ int games::rb::ReflecBeatTouchDeviceHandle::read(LPVOID lpBuffer, DWORD nNumberO
|
|||||||
y = window_height - point.x;
|
y = window_height - point.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// apply scaling
|
||||||
|
x = x - (window_width * (1.f - SCALE_FACTOR) / 2);
|
||||||
|
x = x / SCALE_FACTOR;
|
||||||
|
y = y - (window_height * (1.f - SCALE_FACTOR) / 2);
|
||||||
|
y = y / SCALE_FACTOR;
|
||||||
|
|
||||||
|
if (x < 0 || window_width <= x || y < 0 || window_height <= y) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// point scaling
|
// point scaling
|
||||||
auto point_x = (int) ((x - window_width / 2.0) * 48.0 / 54.0 + window_width / 2.0);
|
const auto point_x = (int) ((x - window_width / 2.0) * 48.0 / 54.0 + window_width / 2.0);
|
||||||
auto point_y = (int) (y - window_height / 76);
|
const auto point_y = (int) (y - window_height / 76);
|
||||||
|
|
||||||
// insert 9 times with offset to double the precision
|
// insert 9 times with offset to double the precision
|
||||||
grid_insert(data, point_x, point_y);
|
grid_insert(data, point_x, point_y);
|
||||||
|
|||||||
@@ -9,9 +9,11 @@
|
|||||||
#include "games/io.h"
|
#include "games/io.h"
|
||||||
#include "launcher/options.h"
|
#include "launcher/options.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
|
#include "games/sdvx/sdvx.h"
|
||||||
|
|
||||||
namespace games::sdvx {
|
namespace games::sdvx {
|
||||||
constexpr bool BI2X_PASSTHROUGH = false;
|
constexpr bool BI2X_PASSTHROUGH = false;
|
||||||
|
bool BI2X_INITIALIZED = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* class definitions
|
* class definitions
|
||||||
@@ -104,7 +106,6 @@ namespace games::sdvx {
|
|||||||
if (!BI2X_PASSTHROUGH) {
|
if (!BI2X_PASSTHROUGH) {
|
||||||
custom_node = new AIO_IOB2_BI2X_UFC;
|
custom_node = new AIO_IOB2_BI2X_UFC;
|
||||||
memset(&custom_node->data, 0, sizeof(custom_node->data));
|
memset(&custom_node->data, 0, sizeof(custom_node->data));
|
||||||
|
|
||||||
return custom_node;
|
return custom_node;
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@@ -163,18 +164,18 @@ namespace games::sdvx {
|
|||||||
|
|
||||||
// volume left
|
// volume left
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::VOL_L_Left])) {
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::VOL_L_Left])) {
|
||||||
VOL_L -= 64;
|
VOL_L -= ((uint16_t)DIGITAL_KNOB_SENS * 4);
|
||||||
}
|
}
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::VOL_L_Right])) {
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::VOL_L_Right])) {
|
||||||
VOL_L += 64;
|
VOL_L += ((uint16_t)DIGITAL_KNOB_SENS * 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
// volume right
|
// volume right
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::VOL_R_Left])) {
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::VOL_R_Left])) {
|
||||||
VOL_R -= 64;
|
VOL_R -= ((uint16_t)DIGITAL_KNOB_SENS * 4);
|
||||||
}
|
}
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::VOL_R_Right])) {
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::VOL_R_Right])) {
|
||||||
VOL_R += 64;
|
VOL_R += ((uint16_t)DIGITAL_KNOB_SENS * 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
// update volumes
|
// update volumes
|
||||||
@@ -364,6 +365,7 @@ namespace games::sdvx {
|
|||||||
aioNmgrIob2->pAIO_NMGR_IOB_BeginManage = AIO_NMGR_IOB_BeginManageStub;
|
aioNmgrIob2->pAIO_NMGR_IOB_BeginManage = AIO_NMGR_IOB_BeginManageStub;
|
||||||
}
|
}
|
||||||
log_info("bi2x_hook", "aioNMgrIob2_Create");
|
log_info("bi2x_hook", "aioNMgrIob2_Create");
|
||||||
|
BI2X_INITIALIZED = true;
|
||||||
return &aioNmgrIob2;
|
return &aioNmgrIob2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "hooks/libraryhook.h"
|
#include "hooks/libraryhook.h"
|
||||||
#include "hooks/powrprof.h"
|
#include "hooks/powrprof.h"
|
||||||
#include "hooks/sleephook.h"
|
#include "hooks/sleephook.h"
|
||||||
|
#include "touch/touch.h"
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "util/sigscan.h"
|
#include "util/sigscan.h"
|
||||||
@@ -21,11 +22,118 @@
|
|||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "acioemu/handle.h"
|
#include "acioemu/handle.h"
|
||||||
|
|
||||||
|
static decltype(RegCloseKey) *RegCloseKey_orig = nullptr;
|
||||||
|
static decltype(RegEnumKeyA) *RegEnumKeyA_orig = nullptr;
|
||||||
|
static decltype(RegOpenKeyA) *RegOpenKeyA_orig = nullptr;
|
||||||
|
static decltype(RegOpenKeyExA) *RegOpenKeyExA_orig = nullptr;
|
||||||
|
static decltype(RegQueryValueExA) *RegQueryValueExA_orig = nullptr;
|
||||||
|
|
||||||
namespace games::sdvx {
|
namespace games::sdvx {
|
||||||
|
|
||||||
|
// constants
|
||||||
|
const HKEY PARENT_ASIO_REG_HANDLE = reinterpret_cast<HKEY>(0x3001);
|
||||||
|
const HKEY DEVICE_ASIO_REG_HANDLE = reinterpret_cast<HKEY>(0x3002);
|
||||||
|
const char *ORIGINAL_ASIO_DEVICE_NAME = "XONAR SOUND CARD(64)";
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
bool DISABLECAMS = false;
|
bool DISABLECAMS = false;
|
||||||
bool NATIVETOUCH = false;
|
bool NATIVETOUCH = false;
|
||||||
|
uint8_t DIGITAL_KNOB_SENS = 16;
|
||||||
|
SdvxOverlayPosition OVERLAY_POS = SDVX_OVERLAY_BOTTOM;
|
||||||
|
|
||||||
|
std::optional<std::string> SOUND_OUTPUT_DEVICE = std::nullopt;
|
||||||
|
std::optional<std::string> ASIO_DRIVER = std::nullopt;
|
||||||
|
|
||||||
|
// states
|
||||||
|
static HKEY real_asio_reg_handle = nullptr;
|
||||||
|
static HKEY real_asio_device_reg_handle = nullptr;
|
||||||
|
|
||||||
|
static LONG WINAPI RegOpenKeyA_hook(HKEY hKey, LPCSTR lpSubKey, PHKEY phkResult) {
|
||||||
|
if (lpSubKey != nullptr && phkResult != nullptr) {
|
||||||
|
if (hKey == HKEY_LOCAL_MACHINE &&
|
||||||
|
ASIO_DRIVER.has_value() &&
|
||||||
|
_stricmp(lpSubKey, "software\\asio") == 0)
|
||||||
|
{
|
||||||
|
*phkResult = PARENT_ASIO_REG_HANDLE;
|
||||||
|
|
||||||
|
return RegOpenKeyA_orig(hKey, lpSubKey, &real_asio_reg_handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return RegOpenKeyA_orig(hKey, lpSubKey, phkResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
static LONG WINAPI RegOpenKeyExA_hook(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired,
|
||||||
|
PHKEY phkResult)
|
||||||
|
{
|
||||||
|
if (lpSubKey != nullptr && phkResult != nullptr) {
|
||||||
|
if (hKey == PARENT_ASIO_REG_HANDLE &&
|
||||||
|
ASIO_DRIVER.has_value() &&
|
||||||
|
_stricmp(lpSubKey, ORIGINAL_ASIO_DEVICE_NAME) == 0)
|
||||||
|
{
|
||||||
|
*phkResult = DEVICE_ASIO_REG_HANDLE;
|
||||||
|
|
||||||
|
log_info("sdvx::asio", "replacing '{}' with '{}'", lpSubKey, ASIO_DRIVER.value());
|
||||||
|
|
||||||
|
return RegOpenKeyExA_orig(real_asio_reg_handle, ASIO_DRIVER.value().c_str(), ulOptions, samDesired,
|
||||||
|
&real_asio_device_reg_handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return RegOpenKeyExA_orig(hKey, lpSubKey, ulOptions, samDesired, phkResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
static LONG WINAPI RegEnumKeyA_hook(HKEY hKey, DWORD dwIndex, LPSTR lpName, DWORD cchName) {
|
||||||
|
if (hKey == PARENT_ASIO_REG_HANDLE && ASIO_DRIVER.has_value()) {
|
||||||
|
if (dwIndex == 0) {
|
||||||
|
auto ret = RegEnumKeyA_orig(real_asio_reg_handle, dwIndex, lpName, cchName);
|
||||||
|
|
||||||
|
if (ret == ERROR_SUCCESS && lpName != nullptr) {
|
||||||
|
log_info("sdvx::asio", "stubbing '{}' with '{}'", lpName, ORIGINAL_ASIO_DEVICE_NAME);
|
||||||
|
|
||||||
|
strncpy(lpName, ORIGINAL_ASIO_DEVICE_NAME, cchName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
} else {
|
||||||
|
return ERROR_NO_MORE_ITEMS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return RegEnumKeyA_orig(hKey, dwIndex, lpName, cchName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static LONG WINAPI RegQueryValueExA_hook(HKEY hKey, LPCTSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType,
|
||||||
|
LPBYTE lpData, LPDWORD lpcbData)
|
||||||
|
{
|
||||||
|
if (lpValueName != nullptr && lpData != nullptr && lpcbData != nullptr) {
|
||||||
|
if (hKey == DEVICE_ASIO_REG_HANDLE && ASIO_DRIVER.has_value()) {
|
||||||
|
log_info("sdvx::asio", "RegQueryValueExA({}, \"{}\")", fmt::ptr((void *) hKey), lpValueName);
|
||||||
|
|
||||||
|
if (_stricmp(lpValueName, "Description") == 0) {
|
||||||
|
// sdvx does a comparison against "XONAR SOUND CARD(64)"
|
||||||
|
// otherwise you end up with this error:
|
||||||
|
// M:BMSoundLib: ASIODriver: No such driver: XONAR SOUND CARD(64)
|
||||||
|
memcpy(lpData, ORIGINAL_ASIO_DEVICE_NAME, strlen(ORIGINAL_ASIO_DEVICE_NAME) + 1);
|
||||||
|
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
} else {
|
||||||
|
hKey = real_asio_device_reg_handle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback
|
||||||
|
return RegQueryValueExA_orig(hKey, lpValueName, lpReserved, lpType, lpData, lpcbData);
|
||||||
|
}
|
||||||
|
|
||||||
|
static LONG WINAPI RegCloseKey_hook(HKEY hKey) {
|
||||||
|
if (hKey == PARENT_ASIO_REG_HANDLE || hKey == DEVICE_ASIO_REG_HANDLE) {
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RegCloseKey_orig(hKey);
|
||||||
|
}
|
||||||
|
|
||||||
SDVXGame::SDVXGame() : Game("Sound Voltex") {
|
SDVXGame::SDVXGame() : Game("Sound Voltex") {
|
||||||
}
|
}
|
||||||
@@ -161,6 +269,7 @@ namespace games::sdvx {
|
|||||||
// hook touch window
|
// hook touch window
|
||||||
if (!NATIVETOUCH) {
|
if (!NATIVETOUCH) {
|
||||||
wintouchemu::FORCE = true;
|
wintouchemu::FORCE = true;
|
||||||
|
wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true;
|
||||||
wintouchemu::hook_title_ends(
|
wintouchemu::hook_title_ends(
|
||||||
"SOUND VOLTEX",
|
"SOUND VOLTEX",
|
||||||
"Main Screen",
|
"Main Screen",
|
||||||
@@ -179,7 +288,21 @@ namespace games::sdvx {
|
|||||||
libraryhook_enable(avs::game::DLL_INSTANCE);
|
libraryhook_enable(avs::game::DLL_INSTANCE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// ASIO device hook
|
||||||
|
RegCloseKey_orig = detour::iat_try(
|
||||||
|
"RegCloseKey", RegCloseKey_hook, avs::game::DLL_INSTANCE);
|
||||||
|
RegEnumKeyA_orig = detour::iat_try(
|
||||||
|
"RegEnumKeyA", RegEnumKeyA_hook, avs::game::DLL_INSTANCE);
|
||||||
|
RegOpenKeyA_orig = detour::iat_try(
|
||||||
|
"RegOpenKeyA", RegOpenKeyA_hook, avs::game::DLL_INSTANCE);
|
||||||
|
RegOpenKeyExA_orig = detour::iat_try(
|
||||||
|
"RegOpenKeyExA", RegOpenKeyExA_hook, avs::game::DLL_INSTANCE);
|
||||||
|
RegQueryValueExA_orig = detour::iat_try(
|
||||||
|
"RegQueryValueExA", RegQueryValueExA_hook, avs::game::DLL_INSTANCE);
|
||||||
|
|
||||||
|
#ifdef SPICE64
|
||||||
powrprof_hook_init(avs::game::DLL_INSTANCE);
|
powrprof_hook_init(avs::game::DLL_INSTANCE);
|
||||||
|
|
||||||
// hook camera
|
// hook camera
|
||||||
|
|||||||
@@ -1,12 +1,25 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include "games/game.h"
|
#include "games/game.h"
|
||||||
|
|
||||||
namespace games::sdvx {
|
namespace games::sdvx {
|
||||||
|
|
||||||
|
enum SdvxOverlayPosition {
|
||||||
|
SDVX_OVERLAY_TOP,
|
||||||
|
SDVX_OVERLAY_MIDDLE,
|
||||||
|
SDVX_OVERLAY_BOTTOM
|
||||||
|
};
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
extern bool DISABLECAMS;
|
extern bool DISABLECAMS;
|
||||||
extern bool NATIVETOUCH;
|
extern bool NATIVETOUCH;
|
||||||
|
extern uint8_t DIGITAL_KNOB_SENS;
|
||||||
|
extern std::optional<std::string> ASIO_DRIVER;
|
||||||
|
extern bool BI2X_INITIALIZED;
|
||||||
|
extern SdvxOverlayPosition OVERLAY_POS;
|
||||||
|
|
||||||
class SDVXGame : public games::Game {
|
class SDVXGame : public games::Game {
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -401,23 +401,58 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::EnumAdapterModes(
|
|||||||
format2s(pMode->Format));
|
format2s(pMode->Format));
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
bool modified = false;
|
||||||
auto width = pMode->Width;
|
auto width = pMode->Width;
|
||||||
|
auto height = pMode->Height;
|
||||||
auto refresh = pMode->RefreshRate;
|
auto refresh = pMode->RefreshRate;
|
||||||
|
|
||||||
if (Mode == 0 && games::iidx::TDJ_MODE) {
|
if (Mode == 0 && games::iidx::TDJ_MODE) {
|
||||||
log_misc("graphics::d3d9", "overriding mode 0 to 1280x720 @ 120 Hz");
|
if (games::iidx::is_tdj_fhd()) {
|
||||||
|
log_misc("graphics::d3d9", "overriding mode 0 to 1920x1080 @ 120 Hz (for TDJ FHD)");
|
||||||
|
pMode->Width = 1920;
|
||||||
|
pMode->Height = 1080;
|
||||||
|
pMode->RefreshRate = 120;
|
||||||
|
modified = true;
|
||||||
|
} else {
|
||||||
|
log_misc("graphics::d3d9", "overriding mode 0 to 1280x720 @ 120 Hz (for TDJ HD)");
|
||||||
pMode->Width = 1280;
|
pMode->Width = 1280;
|
||||||
pMode->Height = 720;
|
pMode->Height = 720;
|
||||||
pMode->RefreshRate = 120;
|
pMode->RefreshRate = 120;
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For whatever reason, TDJ FHD mode prefers to pick these resolutions over 1080p@120Hz.
|
||||||
|
// Remove them entirely.
|
||||||
|
if (!modified && games::iidx::is_tdj_fhd() && refresh == 60) {
|
||||||
|
if ((width == 1920 && height == 1080) ||
|
||||||
|
(width == 1280 && height == 720)){
|
||||||
|
log_misc(
|
||||||
|
"graphics::d3d9", "removing mode {}, {}x{} @ {}Hz (for TDJ FHD)",
|
||||||
|
Mode, width, height, refresh);
|
||||||
|
memset(pMode, 0, sizeof(*pMode));
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// zero out display mode for bad entries
|
// zero out display mode for bad entries
|
||||||
// - skip non-native resolutions
|
// - skip non-native resolutions
|
||||||
// - skip 75 and 90 Hz entries because LDJ game timing is messed up with it
|
// - skip 75 and 90 Hz entries because LDJ game timing is messed up with it
|
||||||
// (TDJ should be fine as it assumes a fixed 60 Hz timing)
|
// (TDJ should be fine as it assumes a fixed 60 Hz timing)
|
||||||
if (width == 1360 || width == 1366 || refresh == 75 || refresh == 90) {
|
if (!modified && (width == 1360 || width == 1366 || refresh == 75 || refresh == 90)) {
|
||||||
|
log_misc(
|
||||||
|
"graphics::d3d9", "removing mode {}, {}x{} @ {}Hz (for LDJ/TDJ)",
|
||||||
|
Mode, width, height, refresh);
|
||||||
memset(pMode, 0, sizeof(*pMode));
|
memset(pMode, 0, sizeof(*pMode));
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!modified && !games::iidx::TDJ_MODE && games::iidx::FORCE_720P && (height > 720)) {
|
||||||
|
log_misc(
|
||||||
|
"graphics::d3d9", "removing mode {}, {}x{} @ {}Hz (-sp2x-iidxforce720p)",
|
||||||
|
Mode, width, height, refresh);
|
||||||
|
memset(pMode, 0, sizeof(*pMode));
|
||||||
|
modified = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -613,12 +648,20 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDevice(
|
|||||||
if (GRAPHICS_WINDOWED) {
|
if (GRAPHICS_WINDOWED) {
|
||||||
pPresentationParameters->Windowed = true;
|
pPresentationParameters->Windowed = true;
|
||||||
pPresentationParameters->FullScreen_RefreshRateInHz = 0;
|
pPresentationParameters->FullScreen_RefreshRateInHz = 0;
|
||||||
|
|
||||||
} else if (GRAPHICS_FORCE_REFRESH > 0) {
|
} else if (GRAPHICS_FORCE_REFRESH > 0) {
|
||||||
log_info("graphics::d3d9", "force refresh rate: {} => {} Hz",
|
log_info("graphics::d3d9", "force refresh rate: {} => {} Hz (-graphics-force-refresh option)",
|
||||||
pPresentationParameters->FullScreen_RefreshRateInHz,
|
pPresentationParameters->FullScreen_RefreshRateInHz,
|
||||||
GRAPHICS_FORCE_REFRESH);
|
GRAPHICS_FORCE_REFRESH);
|
||||||
|
|
||||||
pPresentationParameters->FullScreen_RefreshRateInHz = GRAPHICS_FORCE_REFRESH;
|
pPresentationParameters->FullScreen_RefreshRateInHz = GRAPHICS_FORCE_REFRESH;
|
||||||
|
|
||||||
|
} else if (pPresentationParameters->FullScreen_RefreshRateInHz == 0) {
|
||||||
|
log_warning(
|
||||||
|
"graphics::d3d9",
|
||||||
|
"This game sets FullScreen_RefreshRateInHz to 0, which means it will boot with whatever "
|
||||||
|
"refresh rate you have set in the desktop. If the game is launching at the wrong Hz, "
|
||||||
|
"either use -graphics-force-refresh option or change the desktop resolution beforehand.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// force single adapter
|
// force single adapter
|
||||||
@@ -910,7 +953,11 @@ static void graphics_d3d9_ldj_on_present(IDirect3DDevice9 *wrapped_device) {
|
|||||||
|
|
||||||
HRESULT hr = wrapped_device->GetSwapChain(1, &SUB_SWAP_CHAIN);
|
HRESULT hr = wrapped_device->GetSwapChain(1, &SUB_SWAP_CHAIN);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
log_warning("graphics::d3d9", "failed to acquire sub swap chain, hr={}", FMT_HRESULT(hr));
|
log_warning(
|
||||||
|
"graphics::d3d9",
|
||||||
|
"failed to acquire sub screeen swap chain; "
|
||||||
|
"this can be ignored if game does not have sub screen, hr={}",
|
||||||
|
FMT_HRESULT(hr));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -716,35 +716,55 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
|
|||||||
|
|
||||||
const int rectLeft = 1024;
|
const int rectLeft = 1024;
|
||||||
const int rectTop = 576;
|
const int rectTop = 576;
|
||||||
|
const int w = param.BackBufferWidth;
|
||||||
|
const int h = param.BackBufferHeight;
|
||||||
D3DLOCKED_RECT rect;
|
D3DLOCKED_RECT rect;
|
||||||
|
|
||||||
RECT targetRect {
|
RECT targetRect {
|
||||||
rectLeft, rectTop,
|
rectLeft,
|
||||||
(LONG) (rectLeft+param.BackBufferWidth),
|
rectTop,
|
||||||
(LONG) (rectTop+param.BackBufferHeight),
|
(LONG)(rectLeft + w),
|
||||||
|
(LONG)(rectTop + h),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// stretch to add top/left offset to avoid going negative
|
||||||
topSurface->LockRect(&rect, NULL, D3DLOCK_DONOTWAIT);
|
topSurface->LockRect(&rect, NULL, D3DLOCK_DONOTWAIT);
|
||||||
auto hr = pReal->StretchRect(backbuffer, nullptr, topSurface,
|
auto hr = pReal->StretchRect(
|
||||||
&targetRect, D3DTEXF_LINEAR);
|
backbuffer, nullptr,
|
||||||
|
topSurface, &targetRect,
|
||||||
|
D3DTEXF_LINEAR);
|
||||||
|
|
||||||
if (hr != D3D_OK) {
|
if (hr != D3D_OK) {
|
||||||
log_misc("graphics::d3d9", "StretchRect backbuffer failed");
|
log_misc("graphics::d3d9", "StretchRect backbuffer failed");
|
||||||
}
|
}
|
||||||
topSurface->UnlockRect();
|
topSurface->UnlockRect();
|
||||||
|
|
||||||
|
// do the actual zoom / offset math
|
||||||
|
if (cfg::SCREENRESIZE->centered) {
|
||||||
|
targetRect.right = (w + rectLeft) / cfg::SCREENRESIZE->scale_x;
|
||||||
|
targetRect.bottom = (h + rectTop) / cfg::SCREENRESIZE->scale_y;
|
||||||
|
const LONG deltaH = ((targetRect.bottom - targetRect.top) - h) / 2;
|
||||||
|
const LONG deltaW = ((targetRect.right - targetRect.left) - w) / 2;
|
||||||
|
targetRect.top -= deltaH;
|
||||||
|
targetRect.bottom -= deltaH;
|
||||||
|
targetRect.left -= deltaW;
|
||||||
|
targetRect.right -= deltaW;
|
||||||
|
} else {
|
||||||
targetRect.left -= cfg::SCREENRESIZE->offset_x;
|
targetRect.left -= cfg::SCREENRESIZE->offset_x;
|
||||||
targetRect.top += cfg::SCREENRESIZE->offset_y;
|
targetRect.top += cfg::SCREENRESIZE->offset_y;
|
||||||
targetRect.right = -cfg::SCREENRESIZE->offset_x;
|
targetRect.right = -cfg::SCREENRESIZE->offset_x;
|
||||||
targetRect.right += (param.BackBufferWidth+rectLeft) / cfg::SCREENRESIZE->scale_x;
|
targetRect.right += (w + rectLeft) / cfg::SCREENRESIZE->scale_x;
|
||||||
targetRect.bottom = cfg::SCREENRESIZE->offset_y;
|
targetRect.bottom = cfg::SCREENRESIZE->offset_y;
|
||||||
targetRect.bottom += (param.BackBufferHeight+rectTop) / cfg::SCREENRESIZE->scale_y;
|
targetRect.bottom += (h + rectTop) / cfg::SCREENRESIZE->scale_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw to back buffer
|
||||||
backbuffer->LockRect(&rect, NULL, D3DLOCK_DONOTWAIT);
|
backbuffer->LockRect(&rect, NULL, D3DLOCK_DONOTWAIT);
|
||||||
hr = pReal->StretchRect(topSurface, &targetRect, backbuffer,
|
hr = pReal->StretchRect(
|
||||||
nullptr, cfg::SCREENRESIZE->enable_linear_filter ? D3DTEXF_LINEAR : D3DTEXF_NONE);
|
topSurface, &targetRect,
|
||||||
|
backbuffer, nullptr,
|
||||||
|
cfg::SCREENRESIZE->enable_linear_filter ? D3DTEXF_LINEAR : D3DTEXF_NONE);
|
||||||
backbuffer->UnlockRect();
|
backbuffer->UnlockRect();
|
||||||
|
|
||||||
if (hr != D3D_OK) {
|
if (hr != D3D_OK) {
|
||||||
log_misc("graphics::d3d9", "StretchRect targetRect failed");
|
log_misc("graphics::d3d9", "StretchRect targetRect failed");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
#include "cfg/icon.h"
|
#include "cfg/icon.h"
|
||||||
#include "cfg/screen_resize.h"
|
#include "cfg/screen_resize.h"
|
||||||
|
#include "games/ddr/ddr.h"
|
||||||
#include "hooks/graphics/backends/d3d9/d3d9_backend.h"
|
#include "hooks/graphics/backends/d3d9/d3d9_backend.h"
|
||||||
#include "launcher/shutdown.h"
|
#include "launcher/shutdown.h"
|
||||||
#include "overlay/overlay.h"
|
#include "overlay/overlay.h"
|
||||||
@@ -18,6 +19,7 @@
|
|||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "util/fileutils.h"
|
#include "util/fileutils.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
#include "misc/wintouchemu.h"
|
||||||
#include "util/time.h"
|
#include "util/time.h"
|
||||||
|
|
||||||
struct CaptureData {
|
struct CaptureData {
|
||||||
@@ -47,8 +49,8 @@ bool GRAPHICS_CAPTURE_CURSOR = false;
|
|||||||
bool GRAPHICS_LOG_HRESULT = false;
|
bool GRAPHICS_LOG_HRESULT = false;
|
||||||
bool GRAPHICS_SDVX_FORCE_720 = false;
|
bool GRAPHICS_SDVX_FORCE_720 = false;
|
||||||
bool GRAPHICS_SHOW_CURSOR = false;
|
bool GRAPHICS_SHOW_CURSOR = false;
|
||||||
|
graphics_orientation GRAPHICS_ADJUST_ORIENTATION = ORIENTATION_NORMAL;
|
||||||
bool GRAPHICS_WINDOWED = false;
|
bool GRAPHICS_WINDOWED = false;
|
||||||
bool GRAPHICS_ADJUST_ORIENTATION = false;
|
|
||||||
std::vector<HWND> GRAPHICS_WINDOWS;
|
std::vector<HWND> GRAPHICS_WINDOWS;
|
||||||
UINT GRAPHICS_FORCE_REFRESH = 0;
|
UINT GRAPHICS_FORCE_REFRESH = 0;
|
||||||
bool GRAPHICS_FORCE_SINGLE_ADAPTER = false;
|
bool GRAPHICS_FORCE_SINGLE_ADAPTER = false;
|
||||||
@@ -126,6 +128,24 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (wintouchemu::INJECT_MOUSE_AS_WM_TOUCH) {
|
||||||
|
// drop mouse inputs since only wintouches should be used
|
||||||
|
switch (uMsg) {
|
||||||
|
case WM_LBUTTONDOWN:
|
||||||
|
case WM_LBUTTONUP:
|
||||||
|
case WM_MBUTTONDOWN:
|
||||||
|
case WM_MBUTTONUP:
|
||||||
|
case WM_RBUTTONDOWN:
|
||||||
|
case WM_RBUTTONUP:
|
||||||
|
case WM_XBUTTONDOWN:
|
||||||
|
case WM_XBUTTONUP:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// window resize
|
||||||
|
graphics_windowed_wndproc(hWnd, uMsg, wParam, lParam);
|
||||||
|
|
||||||
// call custom procedures
|
// call custom procedures
|
||||||
for (WNDPROC wndProc : WNDPROC_CUSTOM) {
|
for (WNDPROC wndProc : WNDPROC_CUSTOM) {
|
||||||
wndProc(hWnd, uMsg, wParam, lParam);
|
wndProc(hWnd, uMsg, wParam, lParam);
|
||||||
@@ -265,23 +285,35 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
|||||||
fmt::ptr(lpParam));
|
fmt::ptr(lpParam));
|
||||||
|
|
||||||
// set display orientation
|
// set display orientation
|
||||||
if (GRAPHICS_ADJUST_ORIENTATION) {
|
// only do this when the target window is portrait
|
||||||
if (nHeight > nWidth) {
|
// (avoid doing this for SDVX subscreen which is in landscape, for example)
|
||||||
|
if ((nHeight > nWidth) &&
|
||||||
|
(GRAPHICS_ADJUST_ORIENTATION == ORIENTATION_CW ||
|
||||||
|
GRAPHICS_ADJUST_ORIENTATION == ORIENTATION_CCW)) {
|
||||||
|
DWORD orientation = (GRAPHICS_ADJUST_ORIENTATION == ORIENTATION_CW) ? DMDO_90 : DMDO_270;
|
||||||
DEVMODE mode {};
|
DEVMODE mode {};
|
||||||
|
|
||||||
// get display settings
|
// get display settings
|
||||||
if (EnumDisplaySettings(0, ENUM_CURRENT_SETTINGS, &mode)) {
|
if (EnumDisplaySettings(0, ENUM_CURRENT_SETTINGS, &mode)) {
|
||||||
log_misc("graphics", "rotating display");
|
log_misc("graphics", "auto-rotate: rotating display to DMDO_xx mode {}", orientation);
|
||||||
mode.dmPelsWidth = nWidth;
|
mode.dmPelsWidth = nWidth;
|
||||||
mode.dmPelsHeight = nHeight;
|
mode.dmPelsHeight = nHeight;
|
||||||
mode.dmDisplayOrientation = DMDO_90;
|
mode.dmDisplayOrientation = orientation;
|
||||||
|
if (GRAPHICS_FORCE_REFRESH > 0) {
|
||||||
|
log_info(
|
||||||
|
"graphics",
|
||||||
|
"auto-rotate: force refresh rate: {} => {} Hz",
|
||||||
|
mode.dmDisplayFrequency,
|
||||||
|
GRAPHICS_FORCE_REFRESH);
|
||||||
|
|
||||||
|
mode.dmDisplayFrequency = GRAPHICS_FORCE_REFRESH;
|
||||||
|
}
|
||||||
auto disp_res = ChangeDisplaySettings(&mode, CDS_FULLSCREEN);
|
auto disp_res = ChangeDisplaySettings(&mode, CDS_FULLSCREEN);
|
||||||
if (disp_res != DISP_CHANGE_SUCCESSFUL) {
|
if (disp_res != DISP_CHANGE_SUCCESSFUL) {
|
||||||
log_warning("graphics", "failed to change display settings: {}", disp_res);
|
log_warning("graphics", "auto-rotate: failed to change display settings: {}", disp_res);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log_warning("graphics", "failed to get display settings");
|
log_warning("graphics", "auto-rotate: failed to get display settings");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -328,7 +360,11 @@ static HWND WINAPI CreateWindowExW_hook(DWORD dwExStyle, LPCWSTR lpClassName, LP
|
|||||||
|
|
||||||
// adjust window size to include window decoration
|
// adjust window size to include window decoration
|
||||||
RECT rect {};
|
RECT rect {};
|
||||||
|
if (games::ddr::SDMODE) {
|
||||||
|
SetRect(&rect, 0, 0, 800, 600);
|
||||||
|
} else {
|
||||||
SetRect(&rect, 0, 0, 1280, 720);
|
SetRect(&rect, 0, 0, 1280, 720);
|
||||||
|
}
|
||||||
AdjustWindowRect(&rect, dwStyle, (hMenu != nullptr));
|
AdjustWindowRect(&rect, dwStyle, (hMenu != nullptr));
|
||||||
|
|
||||||
nWidth = rect.right - rect.left;
|
nWidth = rect.right - rect.left;
|
||||||
@@ -683,6 +719,8 @@ void graphics_hook_window(HWND hWnd, D3DPRESENT_PARAMETERS *pPresentationParamet
|
|||||||
if (WNDPROC_ORIG == nullptr) {
|
if (WNDPROC_ORIG == nullptr) {
|
||||||
WNDPROC_ORIG = reinterpret_cast<WNDPROC>(GetWindowLongPtrA(hWnd, GWLP_WNDPROC));
|
WNDPROC_ORIG = reinterpret_cast<WNDPROC>(GetWindowLongPtrA(hWnd, GWLP_WNDPROC));
|
||||||
SetWindowLongPtrA(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(WindowProc));
|
SetWindowLongPtrA(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(WindowProc));
|
||||||
|
overlay::USE_WM_CHAR_FOR_IMGUI_CHAR_INPUT = true;
|
||||||
|
graphics_capture_initial_window(hWnd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,18 +9,30 @@
|
|||||||
|
|
||||||
#include "external/toojpeg/toojpeg.h"
|
#include "external/toojpeg/toojpeg.h"
|
||||||
|
|
||||||
|
// order must match spice2x_AutoOrientation UI enum order
|
||||||
|
enum graphics_orientation {
|
||||||
|
ORIENTATION_CW = 0,
|
||||||
|
ORIENTATION_CCW = 1,
|
||||||
|
ORIENTATION_NORMAL = 2,
|
||||||
|
};
|
||||||
|
|
||||||
// flag settings
|
// flag settings
|
||||||
extern bool GRAPHICS_CAPTURE_CURSOR;
|
extern bool GRAPHICS_CAPTURE_CURSOR;
|
||||||
extern bool GRAPHICS_LOG_HRESULT;
|
extern bool GRAPHICS_LOG_HRESULT;
|
||||||
extern bool GRAPHICS_SDVX_FORCE_720;
|
extern bool GRAPHICS_SDVX_FORCE_720;
|
||||||
extern bool GRAPHICS_SHOW_CURSOR;
|
extern bool GRAPHICS_SHOW_CURSOR;
|
||||||
extern bool GRAPHICS_WINDOWED;
|
extern bool GRAPHICS_WINDOWED;
|
||||||
extern bool GRAPHICS_ADJUST_ORIENTATION;
|
extern graphics_orientation GRAPHICS_ADJUST_ORIENTATION;
|
||||||
extern std::vector<HWND> GRAPHICS_WINDOWS;
|
extern std::vector<HWND> GRAPHICS_WINDOWS;
|
||||||
extern UINT GRAPHICS_FORCE_REFRESH;
|
extern UINT GRAPHICS_FORCE_REFRESH;
|
||||||
extern bool GRAPHICS_FORCE_SINGLE_ADAPTER;
|
extern bool GRAPHICS_FORCE_SINGLE_ADAPTER;
|
||||||
extern bool GRAPHICS_9_ON_12;
|
extern bool GRAPHICS_9_ON_12;
|
||||||
|
|
||||||
|
extern std::optional<int> GRAPHICS_WINDOW_STYLE;
|
||||||
|
extern std::optional<std::string> GRAPHICS_WINDOW_SIZE;
|
||||||
|
extern std::optional<std::string> GRAPHICS_WINDOW_POS;
|
||||||
|
extern bool GRAPHICS_WINDOW_ALWAYS_ON_TOP;
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
extern std::string GRAPHICS_DEVICEID;
|
extern std::string GRAPHICS_DEVICEID;
|
||||||
extern std::string GRAPHICS_SCREENSHOT_DIR;
|
extern std::string GRAPHICS_SCREENSHOT_DIR;
|
||||||
@@ -47,3 +59,11 @@ bool graphics_capture_receive_jpeg(int screen, TooJpeg::WRITE_ONE_BYTE receiver,
|
|||||||
uint64_t *timestamp = nullptr,
|
uint64_t *timestamp = nullptr,
|
||||||
int *width = nullptr, int *height = nullptr);
|
int *width = nullptr, int *height = nullptr);
|
||||||
std::string graphics_screenshot_genpath();
|
std::string graphics_screenshot_genpath();
|
||||||
|
|
||||||
|
// graphics_windowed.cpp
|
||||||
|
void graphics_windowed_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||||
|
void graphics_capture_initial_window(HWND hWnd);
|
||||||
|
void graphics_update_window_style(HWND hWnd);
|
||||||
|
void graphics_update_z_order(HWND hWnd);
|
||||||
|
void graphics_move_resize_window(HWND hWnd);
|
||||||
|
bool graphics_window_change_crashes_game();
|
||||||
|
|||||||
@@ -0,0 +1,431 @@
|
|||||||
|
#include "graphics.h"
|
||||||
|
|
||||||
|
#include "avs/game.h"
|
||||||
|
#include "cfg/screen_resize.h"
|
||||||
|
#include "overlay/overlay.h"
|
||||||
|
#include "util/logging.h"
|
||||||
|
#include "util/utils.h"
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
#define log_debug(module, format_str, ...) logger::push( \
|
||||||
|
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||||
|
#else
|
||||||
|
#define log_debug(module, format_str, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void graphics_load_windowed_parameters();
|
||||||
|
void graphics_wm_style_changed(HWND hWnd, bool changed);
|
||||||
|
void graphics_wm_sizing_aspect_ratio(int edge, RECT& rect);
|
||||||
|
|
||||||
|
std::optional<int> GRAPHICS_WINDOW_STYLE;
|
||||||
|
std::optional<std::string> GRAPHICS_WINDOW_SIZE;
|
||||||
|
std::optional<std::string> GRAPHICS_WINDOW_POS;
|
||||||
|
bool GRAPHICS_WINDOW_ALWAYS_ON_TOP = false;
|
||||||
|
|
||||||
|
// these flags are carefully constructed to ensure maximum compatibility
|
||||||
|
// (e.g., DDR likes to hang when SetWindowPos is called with certain params)
|
||||||
|
static const DWORD SETWINDOWPOS_NOOP =
|
||||||
|
SWP_NOMOVE |
|
||||||
|
SWP_NOSIZE |
|
||||||
|
SWP_NOREDRAW |
|
||||||
|
SWP_NOCOPYBITS |
|
||||||
|
SWP_NOACTIVATE |
|
||||||
|
SWP_NOSENDCHANGING |
|
||||||
|
SWP_DEFERERASE |
|
||||||
|
SWP_NOZORDER |
|
||||||
|
SWP_ASYNCWINDOWPOS;
|
||||||
|
|
||||||
|
void graphics_capture_initial_window(HWND hWnd) {
|
||||||
|
if (!GRAPHICS_WINDOWED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
graphics_load_windowed_parameters();
|
||||||
|
|
||||||
|
cfg::SCREENRESIZE->init_window_style = GetWindowLong(hWnd, GWL_STYLE);
|
||||||
|
|
||||||
|
log_debug("graphics-windowed", "graphics_capture_initial_window called");
|
||||||
|
|
||||||
|
RECT rect;
|
||||||
|
if (!GetClientRect(hWnd, &rect)) {
|
||||||
|
log_warning(
|
||||||
|
"graphics",
|
||||||
|
"graphics_capture_initial_window - GetClientRect failed, GLE: {}",
|
||||||
|
GetLastError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const int client_w = rect.right - rect.left;
|
||||||
|
const int client_h = rect.bottom - rect.top;
|
||||||
|
|
||||||
|
cfg::SCREENRESIZE->init_client_width = client_w;
|
||||||
|
cfg::SCREENRESIZE->init_client_height = client_h;
|
||||||
|
cfg::SCREENRESIZE->init_client_aspect_ratio = (float)client_w / (float)client_h;
|
||||||
|
log_debug(
|
||||||
|
"graphics-windowed",
|
||||||
|
"graphics_capture_initial_window initial window size {}x{}, ratio {}",
|
||||||
|
client_w, client_h, cfg::SCREENRESIZE->init_client_aspect_ratio);
|
||||||
|
|
||||||
|
// ensure frame size is captured
|
||||||
|
graphics_wm_style_changed(hWnd, false);
|
||||||
|
|
||||||
|
// if there was no user-supplied dimension, seed it with the current size
|
||||||
|
// so that the next resize operation will work
|
||||||
|
if (cfg::SCREENRESIZE->client_width == 0) {
|
||||||
|
cfg::SCREENRESIZE->client_width = client_w;
|
||||||
|
}
|
||||||
|
if (cfg::SCREENRESIZE->client_height == 0) {
|
||||||
|
cfg::SCREENRESIZE->client_height = client_h;
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply the config that was loaded from disk
|
||||||
|
// resize must be done before applying the border
|
||||||
|
if (cfg::SCREENRESIZE->enable_window_resize) {
|
||||||
|
log_info(
|
||||||
|
"graphics-windowed", "change window rect - window offset: {}x{}, client size: {}x{}",
|
||||||
|
cfg::SCREENRESIZE->window_offset_x,
|
||||||
|
cfg::SCREENRESIZE->window_offset_y,
|
||||||
|
cfg::SCREENRESIZE->client_width,
|
||||||
|
cfg::SCREENRESIZE->client_height);
|
||||||
|
graphics_move_resize_window(hWnd);
|
||||||
|
}
|
||||||
|
// ddr hangs when window frame doesn't have overlapped
|
||||||
|
if (cfg::SCREENRESIZE->window_decoration != cfg::WindowDecorationMode::Default) {
|
||||||
|
log_info(
|
||||||
|
"graphics-windowed", "change window style - decoration: {}",
|
||||||
|
cfg::SCREENRESIZE->window_decoration);
|
||||||
|
graphics_update_window_style(hWnd);
|
||||||
|
}
|
||||||
|
if (cfg::SCREENRESIZE->window_always_on_top) {
|
||||||
|
log_info("graphics-windowed", "change window z-order - always on top");
|
||||||
|
graphics_update_z_order(hWnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("graphics-windowed", "graphics_capture_initial_window returned");
|
||||||
|
}
|
||||||
|
|
||||||
|
void graphics_load_windowed_parameters() {
|
||||||
|
if (!GRAPHICS_WINDOWED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("graphics-windowed", "graphics_load_windowed_parameters called");
|
||||||
|
const auto remove_spaces = [](const char& c) {
|
||||||
|
return c == ' ';
|
||||||
|
};
|
||||||
|
|
||||||
|
if (GRAPHICS_WINDOW_STYLE.has_value()) {
|
||||||
|
log_debug(
|
||||||
|
"graphics-windowed",
|
||||||
|
"graphics_load_windowed_parameters - load GRAPHICS_WINDOW_STYLE");
|
||||||
|
cfg::SCREENRESIZE->window_decoration = GRAPHICS_WINDOW_STYLE.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GRAPHICS_WINDOW_SIZE.has_value()) {
|
||||||
|
log_debug(
|
||||||
|
"graphics-windowed",
|
||||||
|
"graphics_load_windowed_parameters - load GRAPHICS_WINDOW_SIZE");
|
||||||
|
uint32_t w, h;
|
||||||
|
auto s = GRAPHICS_WINDOW_SIZE.value();
|
||||||
|
s.erase(std::remove_if(s.begin(), s.end(), remove_spaces), s.end());
|
||||||
|
if (sscanf(s.c_str(), "%u,%u", &w, &h) == 2) {
|
||||||
|
cfg::SCREENRESIZE->enable_window_resize = true;
|
||||||
|
cfg::SCREENRESIZE->client_keep_aspect_ratio = false;
|
||||||
|
cfg::SCREENRESIZE->client_width = w;
|
||||||
|
cfg::SCREENRESIZE->client_height = h;
|
||||||
|
} else {
|
||||||
|
log_warning("graphics-windowed", "failed to parse -sp2x-windowsize");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GRAPHICS_WINDOW_POS.has_value()) {
|
||||||
|
log_debug(
|
||||||
|
"graphics-windowed",
|
||||||
|
"graphics_load_windowed_parameters - load GRAPHICS_WINDOW_POS");
|
||||||
|
int32_t x, y;
|
||||||
|
auto s = GRAPHICS_WINDOW_POS.value();
|
||||||
|
s.erase(std::remove_if(s.begin(), s.end(), remove_spaces), s.end());
|
||||||
|
if (sscanf(s.c_str(), "%d,%d", &x, &y) == 2) {
|
||||||
|
cfg::SCREENRESIZE->enable_window_resize = true;
|
||||||
|
cfg::SCREENRESIZE->window_offset_x = x;
|
||||||
|
cfg::SCREENRESIZE->window_offset_y = y;
|
||||||
|
} else {
|
||||||
|
log_warning("graphics-windowed", "failed to parse -sp2x-windowpos");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// only override if true; don't stomp on user setting
|
||||||
|
if (GRAPHICS_WINDOW_ALWAYS_ON_TOP) {
|
||||||
|
cfg::SCREENRESIZE->window_always_on_top = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("graphics-windowed", "graphics_load_windowed_parameters returned");
|
||||||
|
}
|
||||||
|
|
||||||
|
void graphics_windowed_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
||||||
|
if (!GRAPHICS_WINDOWED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (uMsg) {
|
||||||
|
case WM_MOVE: {
|
||||||
|
log_debug("graphics-windowed", "graphics_windowed_wndproc called with WM_MOVE");
|
||||||
|
RECT rect;
|
||||||
|
if (GetWindowRect(hWnd, &rect)) {
|
||||||
|
cfg::SCREENRESIZE->window_offset_x = rect.left;
|
||||||
|
cfg::SCREENRESIZE->window_offset_y = rect.top;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WM_SIZE: {
|
||||||
|
log_debug("graphics-windowed", "graphics_windowed_wndproc called with WM_SIZE");
|
||||||
|
if (wParam == SIZE_MINIMIZED) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
RECT rect;
|
||||||
|
if (GetClientRect(hWnd, &rect)) {
|
||||||
|
cfg::SCREENRESIZE->client_width = rect.right - rect.left;
|
||||||
|
cfg::SCREENRESIZE->client_height = rect.bottom - rect.top;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WM_SIZING: {
|
||||||
|
if (cfg::SCREENRESIZE->client_keep_aspect_ratio) {
|
||||||
|
graphics_wm_sizing_aspect_ratio(
|
||||||
|
static_cast<int>(wParam), *reinterpret_cast<LPRECT>(lParam));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WM_STYLECHANGED: {
|
||||||
|
graphics_wm_style_changed(hWnd, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WM_GETMINMAXINFO: {
|
||||||
|
if (cfg::SCREENRESIZE->client_keep_aspect_ratio) {
|
||||||
|
auto info = reinterpret_cast<MINMAXINFO *>(lParam);
|
||||||
|
info->ptMinTrackSize.y =
|
||||||
|
cfg::SCREENRESIZE->window_deco_height +
|
||||||
|
((info->ptMinTrackSize.x - cfg::SCREENRESIZE->window_deco_width) /
|
||||||
|
cfg::SCREENRESIZE->init_client_aspect_ratio);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void graphics_wm_style_changed(HWND hWnd, bool changed) {
|
||||||
|
log_debug("graphics-windowed", "graphics_wm_style_changed called");
|
||||||
|
RECT rect;
|
||||||
|
|
||||||
|
// ensure the style change takes in effect before doing the calculations
|
||||||
|
if (changed) {
|
||||||
|
// ensure client size doesn't change as a result of this
|
||||||
|
// since SetWindowPos will still send WM_SIZE and WM_MOVE
|
||||||
|
const auto client_w = cfg::SCREENRESIZE->client_width;
|
||||||
|
const auto client_h = cfg::SCREENRESIZE->client_height;
|
||||||
|
const auto flags = SETWINDOWPOS_NOOP | SWP_FRAMECHANGED;
|
||||||
|
SetWindowPos(
|
||||||
|
hWnd,
|
||||||
|
nullptr,
|
||||||
|
0, 0, 0, 0,
|
||||||
|
flags);
|
||||||
|
cfg::SCREENRESIZE->client_width = client_w;
|
||||||
|
cfg::SCREENRESIZE->client_height = client_h;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get window size with decoration...
|
||||||
|
if (!GetWindowRect(hWnd, &rect)) {
|
||||||
|
log_warning(
|
||||||
|
"graphics",
|
||||||
|
"graphics_wm_style_changed - GetWindowRect failed, GLE: {}",
|
||||||
|
GetLastError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const int window_w = rect.right - rect.left;
|
||||||
|
const int window_h = rect.bottom - rect.top;
|
||||||
|
|
||||||
|
// get client area (without decoration)...
|
||||||
|
if (!GetClientRect(hWnd, &rect)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const int client_w = rect.right - rect.left;
|
||||||
|
const int client_h = rect.bottom - rect.top;
|
||||||
|
|
||||||
|
// update window decoration size
|
||||||
|
cfg::SCREENRESIZE->window_deco_width = window_w - client_w;
|
||||||
|
cfg::SCREENRESIZE->window_deco_height = window_h - client_h;
|
||||||
|
log_debug(
|
||||||
|
"graphics-windowed",
|
||||||
|
"graphics_wm_style_changed updating frame dimensions {}x{}",
|
||||||
|
cfg::SCREENRESIZE->window_deco_width,
|
||||||
|
cfg::SCREENRESIZE->window_deco_height);
|
||||||
|
|
||||||
|
// adjust window to ensure client area remains the same
|
||||||
|
if (changed) {
|
||||||
|
graphics_move_resize_window(hWnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("graphics-windowed", "graphics_wm_style_changed returned");
|
||||||
|
}
|
||||||
|
|
||||||
|
void graphics_wm_sizing_aspect_ratio(int edge, RECT& rect) {
|
||||||
|
log_debug("graphics-windowed", "graphics_wm_sizing_aspect_ratio called");
|
||||||
|
|
||||||
|
const auto deco_w = cfg::SCREENRESIZE->window_deco_width;
|
||||||
|
const auto deco_h = cfg::SCREENRESIZE->window_deco_height;
|
||||||
|
const LONG desired_w = (rect.right - rect.left) - deco_w;
|
||||||
|
const LONG desired_h = (rect.bottom - rect.top) - deco_h;
|
||||||
|
const auto aspect_ratio = cfg::SCREENRESIZE->init_client_aspect_ratio;
|
||||||
|
|
||||||
|
// based on http://playtechs.blogspot.com/2007/10/forcing-window-to-maintain-particular.html
|
||||||
|
switch (edge) {
|
||||||
|
case WMSZ_BOTTOM:
|
||||||
|
case WMSZ_TOP: {
|
||||||
|
const LONG w = deco_w + (desired_h * aspect_ratio);
|
||||||
|
rect.right = rect.left + w;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WMSZ_LEFT:
|
||||||
|
case WMSZ_RIGHT: {
|
||||||
|
const LONG h = deco_h + (desired_w / aspect_ratio);
|
||||||
|
rect.bottom = rect.top + h;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WMSZ_TOPLEFT:
|
||||||
|
case WMSZ_TOPRIGHT:
|
||||||
|
case WMSZ_BOTTOMLEFT:
|
||||||
|
case WMSZ_BOTTOMRIGHT: {
|
||||||
|
int w;
|
||||||
|
int h;
|
||||||
|
if (desired_h * aspect_ratio < desired_w) {
|
||||||
|
w = rect.right - rect.left;
|
||||||
|
h = deco_h + (desired_w / aspect_ratio);
|
||||||
|
} else {
|
||||||
|
w = deco_w + (desired_h * aspect_ratio);
|
||||||
|
h = rect.bottom - rect.top;
|
||||||
|
}
|
||||||
|
if (edge == WMSZ_TOPLEFT) {
|
||||||
|
rect.left = rect.right - w;
|
||||||
|
rect.top = rect.bottom - h;
|
||||||
|
} else if (edge == WMSZ_TOPRIGHT) {
|
||||||
|
rect.right = rect.left + w;
|
||||||
|
rect.top = rect.bottom - h;
|
||||||
|
} else if (edge == WMSZ_BOTTOMLEFT) {
|
||||||
|
rect.left = rect.right - w;
|
||||||
|
rect.bottom = rect.top + h;
|
||||||
|
} else if (edge == WMSZ_BOTTOMRIGHT) {
|
||||||
|
rect.right = rect.left + w;
|
||||||
|
rect.bottom = rect.top + h;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("graphics-windowed", "graphics_wm_sizing_aspect_ratio returned");
|
||||||
|
}
|
||||||
|
|
||||||
|
void graphics_update_window_style(HWND hWnd) {
|
||||||
|
if (!GRAPHICS_WINDOWED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (graphics_window_change_crashes_game()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("graphics-windowed", "graphics_update_window_style called");
|
||||||
|
|
||||||
|
// update frame style
|
||||||
|
auto style = cfg::SCREENRESIZE->init_window_style;
|
||||||
|
switch (cfg::SCREENRESIZE->window_decoration) {
|
||||||
|
case cfg::WindowDecorationMode::Borderless:
|
||||||
|
style &= ~WS_OVERLAPPEDWINDOW;
|
||||||
|
break;
|
||||||
|
case cfg::WindowDecorationMode::ResizableFrame:
|
||||||
|
style |= WS_OVERLAPPEDWINDOW;
|
||||||
|
break;
|
||||||
|
case cfg::WindowDecorationMode::Default:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug(
|
||||||
|
"graphics-windowed",
|
||||||
|
"graphics_update_window_style - calling SetWindowLong with Mode {}, style 0x{:x}",
|
||||||
|
static_cast<int>(cfg::SCREENRESIZE->window_decoration),
|
||||||
|
style);
|
||||||
|
SetWindowLong(hWnd, GWL_STYLE, style);
|
||||||
|
|
||||||
|
// SetWindowPos must be called after SetWindowLong if the frame style changed
|
||||||
|
// this will be done in WM_STYLECHANGED handler
|
||||||
|
log_debug("graphics-windowed", "graphics_update_window_style returned");
|
||||||
|
}
|
||||||
|
|
||||||
|
void graphics_update_z_order(HWND hWnd) {
|
||||||
|
if (!GRAPHICS_WINDOWED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("graphics-windowed", "graphics_update_z_order called");
|
||||||
|
|
||||||
|
HWND insert_after = nullptr;
|
||||||
|
if (cfg::SCREENRESIZE->window_always_on_top) {
|
||||||
|
insert_after = HWND_TOPMOST;
|
||||||
|
} else {
|
||||||
|
insert_after = HWND_NOTOPMOST;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto flags = SETWINDOWPOS_NOOP;
|
||||||
|
flags &= ~SWP_NOZORDER;
|
||||||
|
SetWindowPos(
|
||||||
|
hWnd,
|
||||||
|
insert_after,
|
||||||
|
0, 0, 0, 0,
|
||||||
|
flags);
|
||||||
|
|
||||||
|
log_debug("graphics-windowed", "graphics_update_z_order returned");
|
||||||
|
}
|
||||||
|
|
||||||
|
void graphics_move_resize_window(HWND hWnd) {
|
||||||
|
if (!GRAPHICS_WINDOWED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("graphics-windowed", "graphics_move_resize_window called");
|
||||||
|
|
||||||
|
cfg::SCREENRESIZE->client_width =
|
||||||
|
CLAMP(cfg::SCREENRESIZE->client_width, 640, 1920 * 8);
|
||||||
|
cfg::SCREENRESIZE->client_height =
|
||||||
|
CLAMP(cfg::SCREENRESIZE->client_height, 480, 1080 * 8);
|
||||||
|
|
||||||
|
const auto w = cfg::SCREENRESIZE->client_width + cfg::SCREENRESIZE->window_deco_width;
|
||||||
|
const auto h = cfg::SCREENRESIZE->client_height + cfg::SCREENRESIZE->window_deco_height;
|
||||||
|
|
||||||
|
auto flags = SETWINDOWPOS_NOOP;
|
||||||
|
flags &= ~SWP_NOSIZE;
|
||||||
|
flags &= ~SWP_NOMOVE;
|
||||||
|
SetWindowPos(
|
||||||
|
hWnd,
|
||||||
|
nullptr,
|
||||||
|
cfg::SCREENRESIZE->window_offset_x,
|
||||||
|
cfg::SCREENRESIZE->window_offset_y,
|
||||||
|
w, h,
|
||||||
|
flags);
|
||||||
|
|
||||||
|
log_debug("graphics-windowed", "graphics_move_resize_window returned");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool graphics_window_change_crashes_game() {
|
||||||
|
static std::once_flag flag;
|
||||||
|
static bool result = false;
|
||||||
|
std::call_once(flag, []() {
|
||||||
|
// ddr crashes when frame style changes
|
||||||
|
result = avs::game::is_model("MDX");
|
||||||
|
if (result) {
|
||||||
|
log_warning(
|
||||||
|
"graphics-windowed",
|
||||||
|
"ignoring changes to window style due to incompatibility with this game");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
+200
-44
@@ -78,6 +78,7 @@
|
|||||||
#include "misc/wintouchemu.h"
|
#include "misc/wintouchemu.h"
|
||||||
#include "overlay/overlay.h"
|
#include "overlay/overlay.h"
|
||||||
#include "overlay/windows/patch_manager.h"
|
#include "overlay/windows/patch_manager.h"
|
||||||
|
#include "overlay/windows/iidx_seg.h"
|
||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
#include "rawinput/touch.h"
|
#include "rawinput/touch.h"
|
||||||
#include "reader/reader.h"
|
#include "reader/reader.h"
|
||||||
@@ -203,7 +204,7 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
bool load_stubs = false;
|
bool load_stubs = false;
|
||||||
bool netfix_disable = false;
|
bool netfix_disable = false;
|
||||||
bool lang_disable = false;
|
bool lang_disable = false;
|
||||||
bool realtime = false;
|
std::string process_priority_str = "high";
|
||||||
bool cardio_enabled = false;
|
bool cardio_enabled = false;
|
||||||
bool peb_print = false;
|
bool peb_print = false;
|
||||||
bool cfg_run = false;
|
bool cfg_run = false;
|
||||||
@@ -258,18 +259,20 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
if (options[launcher::Options::ConfigurationPath].is_active()) {
|
if (options[launcher::Options::ConfigurationPath].is_active()) {
|
||||||
CONFIG_PATH_OVERRIDE = options[launcher::Options::ConfigurationPath].value_text();
|
CONFIG_PATH_OVERRIDE = options[launcher::Options::ConfigurationPath].value_text();
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::EAmusementEmulation].value_bool()
|
if (options[launcher::Options::EAmusementEmulation].value_bool() &&
|
||||||
&& (options[launcher::Options::ServiceURL].is_active())) {
|
options[launcher::Options::ServiceURL].is_active() &&
|
||||||
log_warning("launcher", "-------------------------------------------------------------------");
|
!cfg::CONFIGURATOR_STANDALONE) {
|
||||||
log_warning("launcher", "WARNING - WARNING - WARNING - WARNING - WARNING - WARNING - WARNING");
|
log_fatal(
|
||||||
log_warning("launcher", "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
"launcher",
|
||||||
log_warning("launcher", "A service URL is set **AND** E-Amusement emulation is enabled.");
|
"BAD EAMUSE SETTINGS ERROR\n\n\n"
|
||||||
log_warning("launcher", "Either remove the service URL, or disable E-Amusement emulation.");
|
"-------------------------------------------------------------------\n"
|
||||||
log_warning("launcher", "Otherwise you may experience problems logging in.");
|
"WARNING - WARNING - WARNING - WARNING - WARNING - WARNING - WARNING\n"
|
||||||
log_warning("launcher", "-------------------------------------------------------------------");
|
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
|
||||||
if (!cfg::CONFIGURATOR_STANDALONE) {
|
"A service URL is set **AND** E-Amusement emulation is enabled.\n"
|
||||||
Sleep(3000);
|
"Either remove the service URL, or disable E-Amusement emulation.\n"
|
||||||
}
|
"Otherwise you may experience problems logging in.\n"
|
||||||
|
"-------------------------------------------------------------------\n\n\n"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::EAmusementEmulation].value_bool()) {
|
if (options[launcher::Options::EAmusementEmulation].value_bool()) {
|
||||||
avs::ea3::URL_SLASH = 1;
|
avs::ea3::URL_SLASH = 1;
|
||||||
@@ -279,19 +282,19 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
easrv_smart = true;
|
easrv_smart = true;
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::EAmusementMaintenance].is_active()) {
|
if (options[launcher::Options::EAmusementMaintenance].is_active()) {
|
||||||
easrv_maint = options[launcher::Options::EAmusementMaintenance].value_int() > 0;
|
easrv_maint = options[launcher::Options::EAmusementMaintenance].value_uint32() > 0;
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::WindowedMode].value_bool()) {
|
if (options[launcher::Options::WindowedMode].value_bool()) {
|
||||||
GRAPHICS_WINDOWED = true;
|
GRAPHICS_WINDOWED = true;
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::GraphicsForceRefresh].is_active()) {
|
if (options[launcher::Options::GraphicsForceRefresh].is_active()) {
|
||||||
GRAPHICS_FORCE_REFRESH = options[launcher::Options::GraphicsForceRefresh].value_int();
|
GRAPHICS_FORCE_REFRESH = options[launcher::Options::GraphicsForceRefresh].value_uint32();
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::GraphicsForceSingleAdapter].value_bool()) {
|
if (options[launcher::Options::GraphicsForceSingleAdapter].value_bool()) {
|
||||||
GRAPHICS_FORCE_SINGLE_ADAPTER = true;
|
GRAPHICS_FORCE_SINGLE_ADAPTER = true;
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::DisplayAdapter].is_active()) {
|
if (options[launcher::Options::DisplayAdapter].is_active()) {
|
||||||
D3D9_ADAPTER = options[launcher::Options::DisplayAdapter].value_int();
|
D3D9_ADAPTER = options[launcher::Options::DisplayAdapter].value_uint32();
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::CaptureCursor].value_bool()) {
|
if (options[launcher::Options::CaptureCursor].value_bool()) {
|
||||||
GRAPHICS_CAPTURE_CURSOR = true;
|
GRAPHICS_CAPTURE_CURSOR = true;
|
||||||
@@ -305,14 +308,22 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
if (options[launcher::Options::VerboseAVSLogging].value_bool()) {
|
if (options[launcher::Options::VerboseAVSLogging].value_bool()) {
|
||||||
hooks::avs::config::LOG = true;
|
hooks::avs::config::LOG = true;
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::AdjustOrientation].value_bool()) {
|
if (options[launcher::Options::spice2x_AutoOrientation].is_active()) {
|
||||||
GRAPHICS_ADJUST_ORIENTATION = true;
|
GRAPHICS_ADJUST_ORIENTATION =
|
||||||
|
(graphics_orientation)options[launcher::Options::spice2x_AutoOrientation].value_uint32();
|
||||||
|
} else if (options[launcher::Options::AdjustOrientation].value_bool()) {
|
||||||
|
GRAPHICS_ADJUST_ORIENTATION = ORIENTATION_CW;
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::LogLevel].is_active()) {
|
if (options[launcher::Options::LogLevel].is_active()) {
|
||||||
avs::core::LOG_LEVEL_CUSTOM = options[launcher::Options::LogLevel].value_text();
|
avs::core::LOG_LEVEL_CUSTOM = options[launcher::Options::LogLevel].value_text();
|
||||||
}
|
}
|
||||||
for (auto &hook : options[launcher::Options::InjectHook].values_text()) {
|
for (auto &hook : options[launcher::Options::InjectHook].values_text()) {
|
||||||
game_hooks.push_back(hook);
|
std::string buffer;
|
||||||
|
std::stringstream stream(hook);
|
||||||
|
std::vector<std::string> tokens;
|
||||||
|
while (stream >> buffer) {
|
||||||
|
game_hooks.push_back(buffer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::LoadStubs].value_bool()) {
|
if (options[launcher::Options::LoadStubs].value_bool()) {
|
||||||
load_stubs = true;
|
load_stubs = true;
|
||||||
@@ -347,6 +358,21 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
if (options[launcher::Options::SDVXNativeTouch].value_bool()) {
|
if (options[launcher::Options::SDVXNativeTouch].value_bool()) {
|
||||||
games::sdvx::NATIVETOUCH = true;
|
games::sdvx::NATIVETOUCH = true;
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::spice2x_SDVXDigitalKnobSensitivity].is_active()) {
|
||||||
|
games::sdvx::DIGITAL_KNOB_SENS = (uint8_t)
|
||||||
|
options[launcher::Options::spice2x_SDVXDigitalKnobSensitivity].value_uint32();
|
||||||
|
}
|
||||||
|
if (options[launcher::Options::spice2x_SDVXAsioDriver].is_active()) {
|
||||||
|
games::sdvx::ASIO_DRIVER = options[launcher::Options::spice2x_SDVXAsioDriver].value_text();
|
||||||
|
}
|
||||||
|
if (options[launcher::Options::spice2x_SDVXSubPos].is_active()) {
|
||||||
|
auto txt = options[launcher::Options::spice2x_SDVXSubPos].value_text();
|
||||||
|
if (txt == "top") {
|
||||||
|
games::sdvx::OVERLAY_POS = games::sdvx::SDVX_OVERLAY_TOP;
|
||||||
|
} else if (txt == "center") {
|
||||||
|
games::sdvx::OVERLAY_POS = games::sdvx::SDVX_OVERLAY_MIDDLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (options[launcher::Options::LoadIIDXModule].value_bool()) {
|
if (options[launcher::Options::LoadIIDXModule].value_bool()) {
|
||||||
attach_iidx = true;
|
attach_iidx = true;
|
||||||
}
|
}
|
||||||
@@ -365,6 +391,29 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
if (options[launcher::Options::IIDXTDJMode].value_bool()) {
|
if (options[launcher::Options::IIDXTDJMode].value_bool()) {
|
||||||
games::iidx::TDJ_MODE = true;
|
games::iidx::TDJ_MODE = true;
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::spice2x_IIDXDigitalTTSensitivity].is_active()) {
|
||||||
|
games::iidx::DIGITAL_TT_SENS = (uint8_t)
|
||||||
|
options[launcher::Options::spice2x_IIDXDigitalTTSensitivity].value_uint32();
|
||||||
|
}
|
||||||
|
if (options[launcher::Options::spice2x_IIDXLDJForce720p].value_bool()) {
|
||||||
|
games::iidx::FORCE_720P = true;
|
||||||
|
}
|
||||||
|
if (options[launcher::Options::spice2x_IIDXTDJSubSize].is_active()) {
|
||||||
|
games::iidx::SUBSCREEN_OVERLAY_SIZE =
|
||||||
|
options[launcher::Options::spice2x_IIDXTDJSubSize].value_text();
|
||||||
|
}
|
||||||
|
if (options[launcher::Options::spice2x_IIDXLEDFontSize].is_active()) {
|
||||||
|
overlay::windows::IIDX_SEGMENT_FONT_SIZE =
|
||||||
|
options[launcher::Options::spice2x_IIDXLEDFontSize].value_uint32();
|
||||||
|
}
|
||||||
|
if (options[launcher::Options::spice2x_IIDXLEDColor].is_active()) {
|
||||||
|
overlay::windows::IIDX_SEGMENT_FONT_COLOR =
|
||||||
|
options[launcher::Options::spice2x_IIDXLEDColor].value_hex64();
|
||||||
|
}
|
||||||
|
if (options[launcher::Options::spice2x_IIDXLEDPos].is_active()) {
|
||||||
|
overlay::windows::IIDX_SEGMENT_LOCATION =
|
||||||
|
options[launcher::Options::spice2x_IIDXLEDPos].value_text();
|
||||||
|
}
|
||||||
if (options[launcher::Options::LoadJubeatModule].value_bool()) {
|
if (options[launcher::Options::LoadJubeatModule].value_bool()) {
|
||||||
attach_jb = true;
|
attach_jb = true;
|
||||||
}
|
}
|
||||||
@@ -393,7 +442,7 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
attach_gitadora = true;
|
attach_gitadora = true;
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::GitaDoraCabinetType].is_active()) {
|
if (options[launcher::Options::GitaDoraCabinetType].is_active()) {
|
||||||
games::gitadora::CAB_TYPE = options[launcher::Options::GitaDoraCabinetType].value_int();
|
games::gitadora::CAB_TYPE = options[launcher::Options::GitaDoraCabinetType].value_uint32();
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::LoadNostalgiaModule].value_bool()) {
|
if (options[launcher::Options::LoadNostalgiaModule].value_bool()) {
|
||||||
attach_nostalgia = true;
|
attach_nostalgia = true;
|
||||||
@@ -492,25 +541,27 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
games::shared::PRINTER_FORMAT.push_back(path);
|
games::shared::PRINTER_FORMAT.push_back(path);
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::SDVXPrinterJPGQuality].is_active()) {
|
if (options[launcher::Options::SDVXPrinterJPGQuality].is_active()) {
|
||||||
games::shared::PRINTER_JPG_QUALITY = options[launcher::Options::SDVXPrinterJPGQuality].value_int();
|
games::shared::PRINTER_JPG_QUALITY = options[launcher::Options::SDVXPrinterJPGQuality].value_uint32();
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::SDVXPrinterOutputClear].value_bool()) {
|
if (options[launcher::Options::SDVXPrinterOutputClear].value_bool()) {
|
||||||
games::shared::PRINTER_CLEAR = true;
|
games::shared::PRINTER_CLEAR = true;
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::HTTP11].is_active()) {
|
if (options[launcher::Options::HTTP11].is_active()) {
|
||||||
avs::ea3::HTTP11 = options[launcher::Options::HTTP11].value_int();
|
avs::ea3::HTTP11 = options[launcher::Options::HTTP11].value_uint32();
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::DisableSSL].value_bool()) {
|
if (options[launcher::Options::DisableSSL].value_bool()) {
|
||||||
ssl_disable = true;
|
ssl_disable = true;
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::URLSlash].is_active()) {
|
if (options[launcher::Options::URLSlash].is_active()) {
|
||||||
avs::ea3::URL_SLASH = options[launcher::Options::URLSlash].value_int();
|
avs::ea3::URL_SLASH = options[launcher::Options::URLSlash].value_uint32();
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::RealtimeProcessPriority].value_bool()) {
|
if (options[launcher::Options::spice2x_ProcessPriority].is_active()) {
|
||||||
realtime = true;
|
process_priority_str = options[launcher::Options::spice2x_ProcessPriority].value_text();
|
||||||
|
} else if (options[launcher::Options::RealtimeProcessPriority].value_bool()) {
|
||||||
|
process_priority_str = "realtime";
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::HeapSize].is_active()) {
|
if (options[launcher::Options::HeapSize].is_active()) {
|
||||||
user_heap_size = options[launcher::Options::HeapSize].value_int();
|
user_heap_size = options[launcher::Options::HeapSize].value_uint32();
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::DisableAvsVfsDriveMountRedirection].is_active()) {
|
if (options[launcher::Options::DisableAvsVfsDriveMountRedirection].is_active()) {
|
||||||
hooks::avs::config::DISABLE_VFS_DRIVE_REDIRECTION = true;
|
hooks::avs::config::DISABLE_VFS_DRIVE_REDIRECTION = true;
|
||||||
@@ -603,7 +654,7 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
if (options[launcher::Options::APITCPPort].is_active()) {
|
if (options[launcher::Options::APITCPPort].is_active()) {
|
||||||
api_enable = true;
|
api_enable = true;
|
||||||
api_port = options[launcher::Options::APITCPPort].value_int();
|
api_port = options[launcher::Options::APITCPPort].value_uint32();
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::APIPassword].is_active()) {
|
if (options[launcher::Options::APIPassword].is_active()) {
|
||||||
api_pass = options[launcher::Options::APIPassword].value_text();
|
api_pass = options[launcher::Options::APIPassword].value_text();
|
||||||
@@ -612,7 +663,7 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
api_serial_port.push_back(options[launcher::Options::APISerialPort].value_text());
|
api_serial_port.push_back(options[launcher::Options::APISerialPort].value_text());
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::APISerialBaud].is_active()) {
|
if (options[launcher::Options::APISerialBaud].is_active()) {
|
||||||
api_serial_baud.push_back(options[launcher::Options::APISerialBaud].value_int());
|
api_serial_baud.push_back(options[launcher::Options::APISerialBaud].value_uint32());
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::APIPretty].value_bool()) {
|
if (options[launcher::Options::APIPretty].value_bool()) {
|
||||||
api_pretty = true;
|
api_pretty = true;
|
||||||
@@ -675,7 +726,7 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
hooks::audio::BACKEND = backend;
|
hooks::audio::BACKEND = backend;
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::AsioDriverId].is_active()) {
|
if (options[launcher::Options::AsioDriverId].is_active()) {
|
||||||
hooks::audio::ASIO_DRIVER_ID = options[launcher::Options::AsioDriverId].value_int();
|
hooks::audio::ASIO_DRIVER_ID = options[launcher::Options::AsioDriverId].value_uint32();
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::AudioDummy].value_bool()) {
|
if (options[launcher::Options::AudioDummy].value_bool()) {
|
||||||
hooks::audio::USE_DUMMY = true;
|
hooks::audio::USE_DUMMY = true;
|
||||||
@@ -699,6 +750,51 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
if (options[launcher::Options::GameExecutable].is_active()) {
|
if (options[launcher::Options::GameExecutable].is_active()) {
|
||||||
avs::game::DLL_NAME = options[launcher::Options::GameExecutable].value_text();
|
avs::game::DLL_NAME = options[launcher::Options::GameExecutable].value_text();
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::spice2x_LightsOverallBrightness].is_active()) {
|
||||||
|
rawinput::HID_LIGHT_BRIGHTNESS =
|
||||||
|
(uint8_t)options[launcher::Options::spice2x_LightsOverallBrightness].value_uint32();
|
||||||
|
}
|
||||||
|
if (options[launcher::Options::spice2x_FpsAutoShow].value_bool()) {
|
||||||
|
overlay::AUTO_SHOW_FPS = true;
|
||||||
|
}
|
||||||
|
if (options[launcher::Options::spice2x_SubScreenAutoShow].value_bool()) {
|
||||||
|
overlay::AUTO_SHOW_SUBSCREEN = true;
|
||||||
|
}
|
||||||
|
if (options[launcher::Options::spice2x_IOPanelAutoShow].value_bool()) {
|
||||||
|
overlay::AUTO_SHOW_IOPANEL = true;
|
||||||
|
}
|
||||||
|
if (options[launcher::Options::spice2x_KeypadAutoShow].is_active()) {
|
||||||
|
auto s = options[launcher::Options::spice2x_KeypadAutoShow].value_uint32();
|
||||||
|
switch (s) {
|
||||||
|
case 1:
|
||||||
|
overlay::AUTO_SHOW_KEYPAD_P1 = true;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
overlay::AUTO_SHOW_KEYPAD_P2 = true;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
overlay::AUTO_SHOW_KEYPAD_P1 = true;
|
||||||
|
overlay::AUTO_SHOW_KEYPAD_P2 = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (options[launcher::Options::spice2x_WindowBorder].is_active()) {
|
||||||
|
GRAPHICS_WINDOW_STYLE = options[launcher::Options::spice2x_WindowBorder].value_uint32();
|
||||||
|
}
|
||||||
|
if (options[launcher::Options::spice2x_WindowSize].is_active()) {
|
||||||
|
GRAPHICS_WINDOW_SIZE = options[launcher::Options::spice2x_WindowSize].value_text();
|
||||||
|
}
|
||||||
|
if (options[launcher::Options::spice2x_WindowPosition].is_active()) {
|
||||||
|
GRAPHICS_WINDOW_POS = options[launcher::Options::spice2x_WindowPosition].value_text();
|
||||||
|
}
|
||||||
|
GRAPHICS_WINDOW_ALWAYS_ON_TOP = options[launcher::Options::spice2x_WindowAlwaysOnTop].value_bool();
|
||||||
|
|
||||||
|
if (options[launcher::Options::spice2x_JubeatLegacyTouch].value_bool()) {
|
||||||
|
games::jb::TOUCH_LEGACY_BOX = true;
|
||||||
|
}
|
||||||
|
if (options[launcher::Options::spice2x_RBTouchScale].is_active()) {
|
||||||
|
games::rb::TOUCH_SCALING = options[launcher::Options::spice2x_RBTouchScale].value_uint32();
|
||||||
|
}
|
||||||
|
|
||||||
// API debugging
|
// API debugging
|
||||||
if (api_debug && !cfg::CONFIGURATOR_STANDALONE) {
|
if (api_debug && !cfg::CONFIGURATOR_STANDALONE) {
|
||||||
@@ -717,23 +813,32 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// delay
|
// delay
|
||||||
if (!cfg::CONFIGURATOR_STANDALONE &&
|
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||||
(options[launcher::Options::DelayBy5Seconds].value_bool()))
|
DWORD delayInSeconds = 0;
|
||||||
{
|
if (options[launcher::Options::spice2x_DelayByNSeconds].is_active()) {
|
||||||
log_info("launcher", "delay by 5000ms...");
|
delayInSeconds = (DWORD)options[launcher::Options::spice2x_DelayByNSeconds].value_uint32();
|
||||||
Sleep(5000);
|
} else if (options[launcher::Options::DelayBy5Seconds].value_bool()) {
|
||||||
|
delayInSeconds = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 < delayInSeconds) {
|
||||||
|
log_info("launcher", "delay by {}ms...", delayInSeconds * 1000);
|
||||||
|
Sleep(delayInSeconds * 1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// create log file
|
// create log file
|
||||||
|
// configurator does not write a log file because it tends to cause the
|
||||||
|
// config file to be corrupt...
|
||||||
if (!cfg::CONFIGURATOR_STANDALONE) {
|
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||||
avs::core::create_log();
|
avs::core::create_log();
|
||||||
}
|
}
|
||||||
|
|
||||||
// log
|
// log
|
||||||
#ifdef SPICE64
|
#ifdef SPICE64
|
||||||
log_info("launcher", "SpiceTools Bootstrap (x64)");
|
log_info("launcher", "SpiceTools Bootstrap (x64) (spice2x)");
|
||||||
#else
|
#else
|
||||||
log_info("launcher", "SpiceTools Bootstrap (x32)");
|
log_info("launcher", "SpiceTools Bootstrap (x32) (spice2x)");
|
||||||
#endif
|
#endif
|
||||||
log_info("launcher", "{}", VERSION_STRING);
|
log_info("launcher", "{}", VERSION_STRING);
|
||||||
|
|
||||||
@@ -772,13 +877,64 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// set process priority
|
// set process priority
|
||||||
|
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||||
|
DWORD process_priority = HIGH_PRIORITY_CLASS;
|
||||||
|
if (process_priority_str == "belownormal") {
|
||||||
|
process_priority = BELOW_NORMAL_PRIORITY_CLASS;
|
||||||
|
} else if (process_priority_str == "normal") {
|
||||||
|
process_priority = NORMAL_PRIORITY_CLASS;
|
||||||
|
} else if (process_priority_str == "abovenormal") {
|
||||||
|
process_priority = ABOVE_NORMAL_PRIORITY_CLASS;
|
||||||
|
// high is the default so it's skipped!
|
||||||
|
} else if (process_priority_str == "realtime") {
|
||||||
|
process_priority = REALTIME_PRIORITY_CLASS;
|
||||||
|
}
|
||||||
|
// while testing, realtime only worked when being set to high before
|
||||||
|
if (process_priority == REALTIME_PRIORITY_CLASS) {
|
||||||
if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) {
|
if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) {
|
||||||
log_warning("launcher", "could not set process priority to high: {}", GetLastError());
|
log_warning(
|
||||||
|
"launcher",
|
||||||
|
"could not set process priority to high, GLE:{}",
|
||||||
|
GetLastError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!SetPriorityClass(GetCurrentProcess(), process_priority)) {
|
||||||
|
log_warning(
|
||||||
|
"launcher",
|
||||||
|
"could not set process priority to {}, GLE:{}",
|
||||||
|
process_priority_str, GetLastError());
|
||||||
|
} else {
|
||||||
|
log_info(
|
||||||
|
"launcher",
|
||||||
|
"SetPriorityClass succeeded, set priority to {}",
|
||||||
|
process_priority_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set process affinity
|
||||||
|
if (!cfg::CONFIGURATOR_STANDALONE &&
|
||||||
|
options[launcher::Options::spice2x_ProcessAffinity].is_active()) {
|
||||||
|
uint64_t affinity = options[launcher::Options::spice2x_ProcessAffinity].value_hex64();
|
||||||
|
if (SetProcessAffinityMask(GetCurrentProcess(), affinity) != 0) {
|
||||||
|
log_info(
|
||||||
|
"launcher",
|
||||||
|
"SetProcessAffinityMask succeeded, affinity set to 0x{:x}",
|
||||||
|
affinity);
|
||||||
|
} else {
|
||||||
|
auto gle = GetLastError();
|
||||||
|
if (gle == ERROR_INVALID_PARAMETER) {
|
||||||
|
log_fatal(
|
||||||
|
"launcher",
|
||||||
|
"SetProcessAffinityMask failed, provided 0x{:x}, GLE: ERROR_INVALID_PARAMETER. "
|
||||||
|
"Make sure you only specify bits for processors that exist",
|
||||||
|
affinity);
|
||||||
|
} else {
|
||||||
|
log_fatal(
|
||||||
|
"launcher",
|
||||||
|
"SetProcessAffinityMask failed, provided 0x{:x}, GLE: {}",
|
||||||
|
affinity,
|
||||||
|
gle);
|
||||||
}
|
}
|
||||||
if (realtime) {
|
|
||||||
// while testing, realtime only worked when being set to high before, so this is fine
|
|
||||||
if (!SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS)) {
|
|
||||||
log_warning("launcher", "could not set process priority to realtime: {}", GetLastError());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1445,9 +1601,6 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// load AVS-EA3
|
|
||||||
avs::ea3::boot(easrv_port, easrv_maint, easrv_smart);
|
|
||||||
|
|
||||||
// apply patches
|
// apply patches
|
||||||
{
|
{
|
||||||
overlay::windows::PatchManager patch_manager(nullptr, true);
|
overlay::windows::PatchManager patch_manager(nullptr, true);
|
||||||
@@ -1457,6 +1610,9 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
script::manager_scan();
|
script::manager_scan();
|
||||||
script::manager_boot();
|
script::manager_boot();
|
||||||
|
|
||||||
|
// load AVS-EA3
|
||||||
|
avs::ea3::boot(easrv_port, easrv_maint, easrv_smart);
|
||||||
|
|
||||||
// eamuse init
|
// eamuse init
|
||||||
eamuse_autodetect_game();
|
eamuse_autodetect_game();
|
||||||
|
|
||||||
|
|||||||
@@ -148,6 +148,8 @@ namespace logger {
|
|||||||
THREAD = new std::thread([] {
|
THREAD = new std::thread([] {
|
||||||
std::unique_lock<std::mutex> lock(EVENT_MUTEX);
|
std::unique_lock<std::mutex> lock(EVENT_MUTEX);
|
||||||
|
|
||||||
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
|
||||||
|
|
||||||
// main loop
|
// main loop
|
||||||
while (RUNNING) {
|
while (RUNNING) {
|
||||||
|
|
||||||
|
|||||||
+488
-118
File diff suppressed because it is too large
Load Diff
+26
-1
@@ -39,6 +39,11 @@ namespace launcher {
|
|||||||
URLSlash,
|
URLSlash,
|
||||||
SOFTID,
|
SOFTID,
|
||||||
VREnable,
|
VREnable,
|
||||||
|
DisableOverlay,
|
||||||
|
spice2x_FpsAutoShow,
|
||||||
|
spice2x_SubScreenAutoShow,
|
||||||
|
spice2x_IOPanelAutoShow,
|
||||||
|
spice2x_KeypadAutoShow,
|
||||||
LoadIIDXModule,
|
LoadIIDXModule,
|
||||||
IIDXCameraOrderFlip,
|
IIDXCameraOrderFlip,
|
||||||
IIDXDisableCameras,
|
IIDXDisableCameras,
|
||||||
@@ -46,6 +51,12 @@ namespace launcher {
|
|||||||
IIDXAsioDriver,
|
IIDXAsioDriver,
|
||||||
IIDXBIO2FW,
|
IIDXBIO2FW,
|
||||||
IIDXTDJMode,
|
IIDXTDJMode,
|
||||||
|
spice2x_IIDXDigitalTTSensitivity,
|
||||||
|
spice2x_IIDXLDJForce720p,
|
||||||
|
spice2x_IIDXTDJSubSize,
|
||||||
|
spice2x_IIDXLEDFontSize,
|
||||||
|
spice2x_IIDXLEDColor,
|
||||||
|
spice2x_IIDXLEDPos,
|
||||||
LoadSoundVoltexModule,
|
LoadSoundVoltexModule,
|
||||||
SDVXForce720p,
|
SDVXForce720p,
|
||||||
SDVXPrinterEmulation,
|
SDVXPrinterEmulation,
|
||||||
@@ -56,6 +67,9 @@ namespace launcher {
|
|||||||
SDVXPrinterJPGQuality,
|
SDVXPrinterJPGQuality,
|
||||||
SDVXDisableCameras,
|
SDVXDisableCameras,
|
||||||
SDVXNativeTouch,
|
SDVXNativeTouch,
|
||||||
|
spice2x_SDVXDigitalKnobSensitivity,
|
||||||
|
spice2x_SDVXAsioDriver,
|
||||||
|
spice2x_SDVXSubPos,
|
||||||
LoadDDRModule,
|
LoadDDRModule,
|
||||||
DDR43Mode,
|
DDR43Mode,
|
||||||
LoadPopnMusicModule,
|
LoadPopnMusicModule,
|
||||||
@@ -123,16 +137,19 @@ namespace launcher {
|
|||||||
SextetStreamPort,
|
SextetStreamPort,
|
||||||
EnableBemaniTools5API,
|
EnableBemaniTools5API,
|
||||||
RealtimeProcessPriority,
|
RealtimeProcessPriority,
|
||||||
|
spice2x_ProcessPriority,
|
||||||
|
spice2x_ProcessAffinity,
|
||||||
HeapSize,
|
HeapSize,
|
||||||
DisableGSyncDetection,
|
DisableGSyncDetection,
|
||||||
DisableOverlay,
|
|
||||||
DisableAudioHooks,
|
DisableAudioHooks,
|
||||||
AudioBackend,
|
AudioBackend,
|
||||||
AsioDriverId,
|
AsioDriverId,
|
||||||
AudioDummy,
|
AudioDummy,
|
||||||
DelayBy5Seconds,
|
DelayBy5Seconds,
|
||||||
|
spice2x_DelayByNSeconds,
|
||||||
LoadStubs,
|
LoadStubs,
|
||||||
AdjustOrientation,
|
AdjustOrientation,
|
||||||
|
spice2x_AutoOrientation,
|
||||||
LogLevel,
|
LogLevel,
|
||||||
EAAutomap,
|
EAAutomap,
|
||||||
EANetdump,
|
EANetdump,
|
||||||
@@ -147,9 +164,17 @@ namespace launcher {
|
|||||||
DisableDebugHooks,
|
DisableDebugHooks,
|
||||||
DisableAvsVfsDriveMountRedirection,
|
DisableAvsVfsDriveMountRedirection,
|
||||||
OutputPEB,
|
OutputPEB,
|
||||||
|
spice2x_LightsOverallBrightness,
|
||||||
|
spice2x_WindowBorder,
|
||||||
|
spice2x_WindowSize,
|
||||||
|
spice2x_WindowPosition,
|
||||||
|
spice2x_WindowAlwaysOnTop,
|
||||||
|
spice2x_JubeatLegacyTouch,
|
||||||
|
spice2x_RBTouchScale,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<std::string> &get_categories(bool advanced);
|
||||||
const std::vector<OptionDefinition> &get_option_definitions();
|
const std::vector<OptionDefinition> &get_option_definitions();
|
||||||
std::unique_ptr<std::vector<Option>> parse_options(int argc, char *argv[]);
|
std::unique_ptr<std::vector<Option>> parse_options(int argc, char *argv[]);
|
||||||
std::vector<Option> merge_options(const std::vector<Option> &options, const std::vector<Option> &overrides);
|
std::vector<Option> merge_options(const std::vector<Option> &options, const std::vector<Option> &overrides);
|
||||||
|
|||||||
@@ -656,6 +656,102 @@ 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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
|
DSEG14Classic-Italic.ttf (OFL)
|
||||||
|
-------------------------------------------
|
||||||
|
Copyright (c) 2017, keshikan (http://www.keshikan.net),
|
||||||
|
with Reserved Font Name "DSEG".
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
|
|
||||||
Contributions
|
Contributions
|
||||||
-------------------------------------------
|
-------------------------------------------
|
||||||
cardio - Felix - MIT License
|
cardio - Felix - MIT License
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "cfg/config.h"
|
#include "cfg/config.h"
|
||||||
#include "games/io.h"
|
#include "games/io.h"
|
||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
|
#include "games/sdvx/sdvx.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "util/time.h"
|
#include "util/time.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
@@ -299,6 +300,13 @@ uint16_t eamuse_get_keypad_state(size_t unit) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// keypad disabled for KFC in BI2X mode, since the game won't handle it correctly
|
||||||
|
// this check for IO is necessary (as opposed to just doing a check for Valkyrie cab mode)
|
||||||
|
// because there are hex edits that allow you to use legacy (KFC/BIO2) IO while in Valk mode.
|
||||||
|
if (avs::game::is_model("KFC") && games::sdvx::BI2X_INITIALIZED) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// reset
|
// reset
|
||||||
KEYPAD_STATE[unit] = KEYPAD_STATE_OVERRIDES[unit];
|
KEYPAD_STATE[unit] = KEYPAD_STATE_OVERRIDES[unit];
|
||||||
KEYPAD_STATE[unit] |= KEYPAD_STATE_OVERRIDES_BT5[unit];
|
KEYPAD_STATE[unit] |= KEYPAD_STATE_OVERRIDES_BT5[unit];
|
||||||
|
|||||||
+85
-27
@@ -15,10 +15,19 @@
|
|||||||
#include "util/time.h"
|
#include "util/time.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
|
||||||
|
#include "avs/game.h"
|
||||||
|
|
||||||
namespace wintouchemu {
|
namespace wintouchemu {
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
POINT pos;
|
||||||
|
bool last_button_pressed;
|
||||||
|
DWORD touch_event;
|
||||||
|
} mouse_state_t;
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
bool FORCE = false;
|
bool FORCE = false;
|
||||||
|
bool INJECT_MOUSE_AS_WM_TOUCH = false;
|
||||||
bool LOG_FPS = false;
|
bool LOG_FPS = false;
|
||||||
|
|
||||||
static inline bool is_emu_enabled() {
|
static inline bool is_emu_enabled() {
|
||||||
@@ -67,6 +76,7 @@ namespace wintouchemu {
|
|||||||
std::string WINDOW_TITLE_START = "";
|
std::string WINDOW_TITLE_START = "";
|
||||||
std::optional<std::string> WINDOW_TITLE_END = std::nullopt;
|
std::optional<std::string> WINDOW_TITLE_END = std::nullopt;
|
||||||
bool INITIALIZED = false;
|
bool INITIALIZED = false;
|
||||||
|
mouse_state_t mouse_state;
|
||||||
|
|
||||||
void hook(const char *window_title, HMODULE module) {
|
void hook(const char *window_title, HMODULE module) {
|
||||||
|
|
||||||
@@ -106,7 +116,6 @@ namespace wintouchemu {
|
|||||||
// set touch inputs
|
// set touch inputs
|
||||||
bool result = false;
|
bool result = false;
|
||||||
bool mouse_used = false;
|
bool mouse_used = false;
|
||||||
static bool mouse_state_old = false;
|
|
||||||
for (UINT input = 0; input < cInputs; input++) {
|
for (UINT input = 0; input < cInputs; input++) {
|
||||||
auto *touch_input = &pInputs[input];
|
auto *touch_input = &pInputs[input];
|
||||||
|
|
||||||
@@ -130,22 +139,34 @@ namespace wintouchemu {
|
|||||||
|
|
||||||
// check touch point
|
// check touch point
|
||||||
if (touch_event) {
|
if (touch_event) {
|
||||||
|
|
||||||
// set touch point
|
// set touch point
|
||||||
result = true;
|
result = true;
|
||||||
touch_input->x = touch_event->x * 100;
|
|
||||||
touch_input->y = touch_event->y * 100;
|
auto x = touch_event->x;
|
||||||
|
auto y = touch_event->y;
|
||||||
|
auto valid = true;
|
||||||
|
if (overlay::OVERLAY) {
|
||||||
|
valid = overlay::OVERLAY->transform_touch_point(&x, &y);
|
||||||
|
}
|
||||||
|
|
||||||
|
touch_input->x = x * 100;
|
||||||
|
touch_input->y = y * 100;
|
||||||
touch_input->hSource = hTouchInput;
|
touch_input->hSource = hTouchInput;
|
||||||
touch_input->dwID = touch_event->id;
|
touch_input->dwID = touch_event->id;
|
||||||
touch_input->dwFlags = 0;
|
touch_input->dwFlags = 0;
|
||||||
switch (touch_event->type) {
|
switch (touch_event->type) {
|
||||||
case TOUCH_DOWN:
|
case TOUCH_DOWN:
|
||||||
|
if (valid) {
|
||||||
touch_input->dwFlags |= TOUCHEVENTF_DOWN;
|
touch_input->dwFlags |= TOUCHEVENTF_DOWN;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TOUCH_MOVE:
|
case TOUCH_MOVE:
|
||||||
|
if (valid) {
|
||||||
touch_input->dwFlags |= TOUCHEVENTF_MOVE;
|
touch_input->dwFlags |= TOUCHEVENTF_MOVE;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TOUCH_UP:
|
case TOUCH_UP:
|
||||||
|
// don't check valid so that this touch ID can be released
|
||||||
touch_input->dwFlags |= TOUCHEVENTF_UP;
|
touch_input->dwFlags |= TOUCHEVENTF_UP;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -160,39 +181,47 @@ namespace wintouchemu {
|
|||||||
// disable further mouse inputs this call
|
// disable further mouse inputs this call
|
||||||
mouse_used = true;
|
mouse_used = true;
|
||||||
|
|
||||||
// get mouse state
|
if (mouse_state.touch_event) {
|
||||||
bool mouse_state = (GetKeyState(VK_LBUTTON) & 0x100) != 0;
|
|
||||||
|
|
||||||
// check event needs to be generated
|
|
||||||
if (mouse_state || mouse_state_old) {
|
|
||||||
|
|
||||||
// get current cursor position
|
|
||||||
POINT cursorPos {};
|
|
||||||
GetCursorPos(&cursorPos);
|
|
||||||
|
|
||||||
// set touch input to mouse
|
|
||||||
result = true;
|
result = true;
|
||||||
touch_input->x = cursorPos.x * 100;
|
touch_input->x = mouse_state.pos.x;
|
||||||
touch_input->y = cursorPos.y * 100;
|
touch_input->y = mouse_state.pos.y;
|
||||||
|
auto valid = true;
|
||||||
|
if (overlay::OVERLAY) {
|
||||||
|
valid = overlay::OVERLAY->transform_touch_point(
|
||||||
|
&touch_input->x, &touch_input->y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// touch inputs require 100x precision per pixel
|
||||||
|
touch_input->x *= 100;
|
||||||
|
touch_input->y *= 100;
|
||||||
touch_input->hSource = hTouchInput;
|
touch_input->hSource = hTouchInput;
|
||||||
touch_input->dwID = 0;
|
touch_input->dwID = 0;
|
||||||
touch_input->dwFlags = 0;
|
touch_input->dwFlags = 0;
|
||||||
if (mouse_state && !mouse_state_old) {
|
switch (mouse_state.touch_event) {
|
||||||
|
case TOUCHEVENTF_DOWN:
|
||||||
|
if (valid) {
|
||||||
touch_input->dwFlags |= TOUCHEVENTF_DOWN;
|
touch_input->dwFlags |= TOUCHEVENTF_DOWN;
|
||||||
} else if (mouse_state && mouse_state_old) {
|
}
|
||||||
|
break;
|
||||||
|
case TOUCHEVENTF_MOVE:
|
||||||
|
if (valid) {
|
||||||
touch_input->dwFlags |= TOUCHEVENTF_MOVE;
|
touch_input->dwFlags |= TOUCHEVENTF_MOVE;
|
||||||
} else if (!mouse_state && mouse_state_old) {
|
}
|
||||||
|
break;
|
||||||
|
case TOUCHEVENTF_UP:
|
||||||
|
// don't check valid so that this touch ID can be released
|
||||||
touch_input->dwFlags |= TOUCHEVENTF_UP;
|
touch_input->dwFlags |= TOUCHEVENTF_UP;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
touch_input->dwMask = 0;
|
touch_input->dwMask = 0;
|
||||||
touch_input->dwTime = 0;
|
touch_input->dwTime = 0;
|
||||||
touch_input->dwExtraInfo = 0;
|
touch_input->dwExtraInfo = 0;
|
||||||
touch_input->cxContact = 0;
|
touch_input->cxContact = 0;
|
||||||
touch_input->cyContact = 0;
|
touch_input->cyContact = 0;
|
||||||
}
|
|
||||||
|
|
||||||
// save old state
|
// reset it since the event was consumed & propagated as touch
|
||||||
mouse_state_old = mouse_state;
|
mouse_state.touch_event = 0;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -314,12 +343,18 @@ namespace wintouchemu {
|
|||||||
// create touch window - create overlay if not yet existing at this point
|
// create touch window - create overlay if not yet existing at this point
|
||||||
touch_create_wnd(hWnd, overlay::ENABLED && !overlay::OVERLAY);
|
touch_create_wnd(hWnd, overlay::ENABLED && !overlay::OVERLAY);
|
||||||
USE_MOUSE = false;
|
USE_MOUSE = false;
|
||||||
|
} else if (INJECT_MOUSE_AS_WM_TOUCH) {
|
||||||
|
log_info(
|
||||||
|
"wintouchemu",
|
||||||
|
"using raw mouse cursor API in full screen and injecting them as WM_TOUCH events");
|
||||||
|
USE_MOUSE = true;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
log_info("wintouchemu", "activating DirectX hooks");
|
log_info("wintouchemu", "activating DirectX hooks");
|
||||||
|
|
||||||
// mouse position based input only
|
// mouse position based input only
|
||||||
touch_attach_dx_hook();
|
touch_attach_dx_hook();
|
||||||
USE_MOUSE = true;
|
USE_MOUSE = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
@@ -343,9 +378,6 @@ namespace wintouchemu {
|
|||||||
|
|
||||||
// get event count
|
// get event count
|
||||||
auto event_count = TOUCH_EVENTS.size();
|
auto event_count = TOUCH_EVENTS.size();
|
||||||
if (USE_MOUSE) {
|
|
||||||
event_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// for the fake move events
|
// for the fake move events
|
||||||
event_count += MAX(0, (int) (TOUCH_POINTS.size() - TOUCH_EVENTS.size()));
|
event_count += MAX(0, (int) (TOUCH_POINTS.size() - TOUCH_EVENTS.size()));
|
||||||
@@ -372,5 +404,31 @@ namespace wintouchemu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// send separate WM_TOUCH event for mouse
|
||||||
|
// this must be separate from actual touch events because some games will ignore the return
|
||||||
|
// value from GetTouchInputInfo or fail to read dwFlags for valid events, so it's not OK to
|
||||||
|
// send empty events when the mouse button is not clicked/released
|
||||||
|
if (hWnd != nullptr && USE_MOUSE) {
|
||||||
|
bool button_pressed = ((GetKeyState(VK_LBUTTON) & 0x100) != 0);
|
||||||
|
|
||||||
|
// figure out what kind of touch event to simulate
|
||||||
|
if (button_pressed && !mouse_state.last_button_pressed) {
|
||||||
|
mouse_state.touch_event = TOUCHEVENTF_DOWN;
|
||||||
|
} else if (button_pressed && mouse_state.last_button_pressed) {
|
||||||
|
mouse_state.touch_event = TOUCHEVENTF_MOVE;
|
||||||
|
} else if (!button_pressed && mouse_state.last_button_pressed) {
|
||||||
|
mouse_state.touch_event = TOUCHEVENTF_UP;
|
||||||
|
}
|
||||||
|
|
||||||
|
mouse_state.last_button_pressed = button_pressed;
|
||||||
|
if (mouse_state.touch_event) {
|
||||||
|
GetCursorPos(&mouse_state.pos);
|
||||||
|
// send fake event to make the game update it's touch inputs
|
||||||
|
auto wndProc = (WNDPROC) GetWindowLongPtr(hWnd, GWLP_WNDPROC);
|
||||||
|
wndProc(hWnd, WM_TOUCH, MAKEWORD(1, 0), (LPARAM) GetTouchInputInfoHook);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -6,6 +6,7 @@ namespace wintouchemu {
|
|||||||
|
|
||||||
// settings
|
// settings
|
||||||
extern bool FORCE;
|
extern bool FORCE;
|
||||||
|
extern bool INJECT_MOUSE_AS_WM_TOUCH;
|
||||||
extern bool LOG_FPS;
|
extern bool LOG_FPS;
|
||||||
|
|
||||||
void hook(const char *window_title, HMODULE module = nullptr);
|
void hook(const char *window_title, HMODULE module = nullptr);
|
||||||
|
|||||||
@@ -313,7 +313,10 @@ void ImGui_ImplSpice_NewFrame() {
|
|||||||
|
|
||||||
// trigger
|
// trigger
|
||||||
io.KeysDown[vKey] |= state;
|
io.KeysDown[vKey] |= state;
|
||||||
if (!KeysDownOld[vKey] && state) {
|
|
||||||
|
// generate character input, but only if WM_CHAR didn't take over the
|
||||||
|
// functionality
|
||||||
|
if (!overlay::USE_WM_CHAR_FOR_IMGUI_CHAR_INPUT && !KeysDownOld[vKey] && state) {
|
||||||
UCHAR buf[2];
|
UCHAR buf[2];
|
||||||
auto ret = ToAscii(
|
auto ret = ToAscii(
|
||||||
static_cast<UINT>(vKey),
|
static_cast<UINT>(vKey),
|
||||||
@@ -323,7 +326,7 @@ void ImGui_ImplSpice_NewFrame() {
|
|||||||
0);
|
0);
|
||||||
if (ret > 0) {
|
if (ret > 0) {
|
||||||
for (int i = 0; i < ret; i++) {
|
for (int i = 0; i < ret; i++) {
|
||||||
overlay::OVERLAY->input_char(buf[i], true);
|
overlay::OVERLAY->input_char(buf[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+160
-43
@@ -3,10 +3,13 @@
|
|||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
#include "cfg/configurator.h"
|
#include "cfg/configurator.h"
|
||||||
#include "games/io.h"
|
#include "games/io.h"
|
||||||
|
#include "games/iidx/iidx.h"
|
||||||
#include "hooks/graphics/graphics.h"
|
#include "hooks/graphics/graphics.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
#include "touch/touch.h"
|
#include "touch/touch.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "util/resutils.h"
|
||||||
|
#include "build/resource.h"
|
||||||
|
|
||||||
#include "imgui/impl_dx9.h"
|
#include "imgui/impl_dx9.h"
|
||||||
#include "imgui/impl_spice.h"
|
#include "imgui/impl_spice.h"
|
||||||
@@ -21,7 +24,13 @@
|
|||||||
#include "windows/config.h"
|
#include "windows/config.h"
|
||||||
#include "windows/control.h"
|
#include "windows/control.h"
|
||||||
#include "windows/fps.h"
|
#include "windows/fps.h"
|
||||||
|
#include "windows/generic_sub.h"
|
||||||
|
#include "windows/iidx_seg.h"
|
||||||
#include "windows/iidx_sub.h"
|
#include "windows/iidx_sub.h"
|
||||||
|
#include "windows/iopanel.h"
|
||||||
|
#include "windows/iopanel_ddr.h"
|
||||||
|
#include "windows/iopanel_gfdm.h"
|
||||||
|
#include "windows/iopanel_iidx.h"
|
||||||
#include "windows/sdvx_sub.h"
|
#include "windows/sdvx_sub.h"
|
||||||
#include "windows/keypad.h"
|
#include "windows/keypad.h"
|
||||||
#include "windows/kfcontrol.h"
|
#include "windows/kfcontrol.h"
|
||||||
@@ -29,14 +38,25 @@
|
|||||||
#include "windows/patch_manager.h"
|
#include "windows/patch_manager.h"
|
||||||
#include "windows/vr.h"
|
#include "windows/vr.h"
|
||||||
|
|
||||||
|
static inline ImVec4 operator*(const ImVec4& lhs, const ImVec4& rhs) \
|
||||||
|
{ return ImVec4(lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z, lhs.w * rhs.w); }
|
||||||
|
|
||||||
namespace overlay {
|
namespace overlay {
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
bool ENABLED = true;
|
bool ENABLED = true;
|
||||||
|
bool AUTO_SHOW_FPS = false;
|
||||||
|
bool AUTO_SHOW_SUBSCREEN = false;
|
||||||
|
bool AUTO_SHOW_IOPANEL = false;
|
||||||
|
bool AUTO_SHOW_KEYPAD_P1 = false;
|
||||||
|
bool AUTO_SHOW_KEYPAD_P2 = false;
|
||||||
|
|
||||||
|
bool USE_WM_CHAR_FOR_IMGUI_CHAR_INPUT = false;
|
||||||
|
|
||||||
// global
|
// global
|
||||||
std::mutex OVERLAY_MUTEX;
|
std::mutex OVERLAY_MUTEX;
|
||||||
std::unique_ptr<overlay::SpiceOverlay> OVERLAY = nullptr;
|
std::unique_ptr<overlay::SpiceOverlay> OVERLAY = nullptr;
|
||||||
|
ImFont* DSEG_FONT = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *ImGui_Alloc(size_t sz, void *user_data) {
|
static void *ImGui_Alloc(size_t sz, void *user_data) {
|
||||||
@@ -146,37 +166,71 @@ void overlay::SpiceOverlay::init() {
|
|||||||
style.WindowRounding = 0;
|
style.WindowRounding = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// red theme
|
// red theme based on:
|
||||||
/*
|
// https://github.com/ocornut/imgui/issues/707#issuecomment-760220280
|
||||||
|
// r, g, b, a
|
||||||
ImVec4* colors = ImGui::GetStyle().Colors;
|
ImVec4* colors = ImGui::GetStyle().Colors;
|
||||||
colors[ImGuiCol_Border] = ImVec4(0.50f, 0.43f, 0.43f, 0.50f);
|
// colors[ImGuiCol_Text] = ImVec4(0.75f, 0.75f, 0.75f, 1.00f);
|
||||||
colors[ImGuiCol_FrameBg] = ImVec4(0.48f, 0.16f, 0.16f, 0.54f);
|
// colors[ImGuiCol_TextDisabled] = ImVec4(0.35f, 0.35f, 0.35f, 1.00f);
|
||||||
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.98f, 0.26f, 0.26f, 0.40f);
|
|
||||||
colors[ImGuiCol_FrameBgActive] = ImVec4(0.98f, 0.26f, 0.26f, 0.67f);
|
// colors[ImGuiCol_WindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.94f);
|
||||||
|
// colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||||
|
// colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f);
|
||||||
|
|
||||||
|
colors[ImGuiCol_Border] = ImVec4(0.00f, 0.00f, 0.00f, 0.50f);
|
||||||
|
colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||||
|
colors[ImGuiCol_FrameBg] = ImVec4(0.37f, 0.14f, 0.00f, 0.54f);
|
||||||
|
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.37f, 0.14f, 0.14f, 0.67f);
|
||||||
|
colors[ImGuiCol_FrameBgActive] = ImVec4(0.39f, 0.20f, 0.20f, 0.67f);
|
||||||
|
colors[ImGuiCol_TitleBg] = ImVec4(0.04f, 0.04f, 0.04f, 1.00f);
|
||||||
colors[ImGuiCol_TitleBgActive] = ImVec4(0.48f, 0.16f, 0.16f, 1.00f);
|
colors[ImGuiCol_TitleBgActive] = ImVec4(0.48f, 0.16f, 0.16f, 1.00f);
|
||||||
colors[ImGuiCol_CheckMark] = ImVec4(0.98f, 0.26f, 0.26f, 1.00f);
|
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.48f, 0.16f, 0.16f, 1.00f);
|
||||||
colors[ImGuiCol_SliderGrab] = ImVec4(0.88f, 0.24f, 0.24f, 1.00f);
|
colors[ImGuiCol_MenuBarBg] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f);
|
||||||
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.98f, 0.26f, 0.26f, 1.00f);
|
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.53f);
|
||||||
colors[ImGuiCol_Button] = ImVec4(0.98f, 0.26f, 0.26f, 0.40f);
|
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.31f, 0.31f, 0.31f, 1.00f);
|
||||||
colors[ImGuiCol_ButtonHovered] = ImVec4(0.98f, 0.26f, 0.26f, 1.00f);
|
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f);
|
||||||
colors[ImGuiCol_ButtonActive] = ImVec4(0.98f, 0.06f, 0.06f, 1.00f);
|
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.51f, 0.51f, 0.51f, 1.00f);
|
||||||
colors[ImGuiCol_Header] = ImVec4(0.98f, 0.26f, 0.26f, 0.31f);
|
colors[ImGuiCol_CheckMark] = ImVec4(0.56f, 0.10f, 0.10f, 1.00f);
|
||||||
colors[ImGuiCol_HeaderHovered] = ImVec4(0.98f, 0.26f, 0.26f, 0.80f);
|
colors[ImGuiCol_SliderGrab] = ImVec4(1.00f, 0.19f, 0.19f, 0.40f);
|
||||||
colors[ImGuiCol_HeaderActive] = ImVec4(0.98f, 0.26f, 0.26f, 1.00f);
|
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.89f, 0.00f, 0.19f, 1.00f);
|
||||||
colors[ImGuiCol_Separator] = ImVec4(0.50f, 0.43f, 0.43f, 0.50f);
|
|
||||||
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.75f, 0.10f, 0.10f, 0.78f);
|
colors[ImGuiCol_Button] = ImVec4(1.00f, 0.19f, 0.19f, 0.40f);
|
||||||
colors[ImGuiCol_SeparatorActive] = ImVec4(0.75f, 0.10f, 0.10f, 1.00f);
|
colors[ImGuiCol_ButtonHovered] = ImVec4(0.80f, 0.17f, 0.00f, 1.00f);
|
||||||
colors[ImGuiCol_ResizeGrip] = ImVec4(0.98f, 0.26f, 0.26f, 0.25f);
|
colors[ImGuiCol_ButtonActive] = ImVec4(0.89f, 0.00f, 0.19f, 1.00f);
|
||||||
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.98f, 0.26f, 0.26f, 0.67f);
|
|
||||||
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.98f, 0.26f, 0.26f, 0.95f);
|
colors[ImGuiCol_Header] = ImVec4(0.33f, 0.35f, 0.36f, 0.53f);
|
||||||
colors[ImGuiCol_Tab] = ImVec4(0.58f, 0.18f, 0.18f, 0.86f);
|
colors[ImGuiCol_HeaderHovered] = ImVec4(0.76f, 0.28f, 0.44f, 0.67f);
|
||||||
colors[ImGuiCol_TabHovered] = ImVec4(0.98f, 0.26f, 0.26f, 0.80f);
|
colors[ImGuiCol_HeaderActive] = ImVec4(0.47f, 0.47f, 0.47f, 0.67f);
|
||||||
colors[ImGuiCol_TabActive] = ImVec4(0.68f, 0.20f, 0.20f, 1.00f);
|
colors[ImGuiCol_Separator] = ImVec4(0.32f, 0.32f, 0.32f, 1.00f);
|
||||||
colors[ImGuiCol_TabUnfocused] = ImVec4(0.15f, 0.07f, 0.07f, 0.97f);
|
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.32f, 0.32f, 0.32f, 1.00f);
|
||||||
colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.42f, 0.14f, 0.14f, 1.00f);
|
colors[ImGuiCol_SeparatorActive] = ImVec4(0.32f, 0.32f, 0.32f, 1.00f);
|
||||||
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.98f, 0.26f, 0.26f, 0.35f);
|
colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.85f);
|
||||||
colors[ImGuiCol_NavHighlight] = ImVec4(0.98f, 0.26f, 0.26f, 1.00f);
|
colors[ImGuiCol_ResizeGripHovered] = ImVec4(1.00f, 1.00f, 1.00f, 0.60f);
|
||||||
*/
|
colors[ImGuiCol_ResizeGripActive] = ImVec4(1.00f, 1.00f, 1.00f, 0.90f);
|
||||||
|
|
||||||
|
colors[ImGuiCol_Tab] = colors[ImGuiCol_Button];
|
||||||
|
colors[ImGuiCol_TabHovered] = colors[ImGuiCol_ButtonHovered];
|
||||||
|
colors[ImGuiCol_TabActive] = colors[ImGuiCol_ButtonActive];
|
||||||
|
colors[ImGuiCol_TabUnfocused] = colors[ImGuiCol_Tab] * ImVec4(1.0f, 1.0f, 1.0f, 0.6f);
|
||||||
|
colors[ImGuiCol_TabUnfocusedActive] = colors[ImGuiCol_TabActive] * ImVec4(1.0f, 1.0f, 1.0f, 0.6f);
|
||||||
|
|
||||||
|
colors[ImGuiCol_DockingPreview] = ImVec4(0.47f, 0.47f, 0.47f, 0.47f);
|
||||||
|
colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
|
||||||
|
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
|
||||||
|
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
|
||||||
|
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
||||||
|
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
|
||||||
|
colors[ImGuiCol_TableHeaderBg] = ImVec4(0.19f, 0.19f, 0.20f, 1.00f);
|
||||||
|
colors[ImGuiCol_TableBorderStrong] = ImVec4(0.31f, 0.31f, 0.35f, 1.00f);
|
||||||
|
colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f);
|
||||||
|
colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||||
|
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.07f);
|
||||||
|
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
|
||||||
|
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
|
||||||
|
colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
|
||||||
|
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
|
||||||
|
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
|
||||||
|
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
|
||||||
|
|
||||||
// configure IO
|
// configure IO
|
||||||
auto &io = ImGui::GetIO();
|
auto &io = ImGui::GetIO();
|
||||||
@@ -219,6 +273,16 @@ void overlay::SpiceOverlay::init() {
|
|||||||
io.Fonts->AddFontFromFileTTF(R"(C:\Windows\Fonts\arial.ttf)",
|
io.Fonts->AddFontFromFileTTF(R"(C:\Windows\Fonts\arial.ttf)",
|
||||||
13.0f, &config, io.Fonts->GetGlyphRangesVietnamese());
|
13.0f, &config, io.Fonts->GetGlyphRangesVietnamese());
|
||||||
|
|
||||||
|
// add special font
|
||||||
|
if (avs::game::is_model("LDJ")) {
|
||||||
|
DWORD size;
|
||||||
|
ImFontConfig config {};
|
||||||
|
config.FontDataOwnedByAtlas = false;
|
||||||
|
auto buffer = resutil::load_file(IDR_DSEGFONT, &size);
|
||||||
|
DSEG_FONT = io.Fonts->AddFontFromMemoryTTF((void *)buffer, size,
|
||||||
|
overlay::windows::IIDX_SEGMENT_FONT_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
// init implementation
|
// init implementation
|
||||||
ImGui_ImplSpice_Init(this->hWnd);
|
ImGui_ImplSpice_Init(this->hWnd);
|
||||||
switch (this->renderer) {
|
switch (this->renderer) {
|
||||||
@@ -246,27 +310,88 @@ void overlay::SpiceOverlay::init() {
|
|||||||
ImGui::Begin("Temp");
|
ImGui::Begin("Temp");
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
|
||||||
|
bool set_overlay_active = false;
|
||||||
|
|
||||||
// referenced windows
|
// referenced windows
|
||||||
this->window_add(window_fps = new overlay::windows::FPS(this));
|
this->window_add(window_fps = new overlay::windows::FPS(this));
|
||||||
|
if (!cfg::CONFIGURATOR_STANDALONE && AUTO_SHOW_FPS) {
|
||||||
|
window_fps->set_active(true);
|
||||||
|
set_overlay_active = true;
|
||||||
|
}
|
||||||
|
|
||||||
// add default windows
|
// add default windows
|
||||||
this->window_add(new overlay::windows::Config(this));
|
this->window_add(new overlay::windows::Config(this));
|
||||||
this->window_add(new overlay::windows::Control(this));
|
this->window_add(new overlay::windows::Control(this));
|
||||||
this->window_add(new overlay::windows::Log(this));
|
this->window_add(new overlay::windows::Log(this));
|
||||||
this->window_add(new overlay::windows::CardManager(this));
|
this->window_add(new overlay::windows::CardManager(this));
|
||||||
|
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||||
this->window_add(new overlay::windows::ScreenResize(this));
|
this->window_add(new overlay::windows::ScreenResize(this));
|
||||||
|
}
|
||||||
this->window_add(new overlay::windows::PatchManager(this));
|
this->window_add(new overlay::windows::PatchManager(this));
|
||||||
this->window_add(new overlay::windows::Keypad(this, 0));
|
|
||||||
this->window_add(new overlay::windows::KFControl(this));
|
this->window_add(new overlay::windows::KFControl(this));
|
||||||
this->window_add(new overlay::windows::VRWindow(this));
|
this->window_add(new overlay::windows::VRWindow(this));
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto keypad_p1 = new overlay::windows::Keypad(this, 0);
|
||||||
|
this->window_add(keypad_p1);
|
||||||
|
if (!cfg::CONFIGURATOR_STANDALONE && AUTO_SHOW_KEYPAD_P1) {
|
||||||
|
keypad_p1->set_active(true);
|
||||||
|
set_overlay_active = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (eamuse_get_game_keypads() > 1) {
|
if (eamuse_get_game_keypads() > 1) {
|
||||||
this->window_add(new overlay::windows::Keypad(this, 1));
|
const auto keypad_p2 = new overlay::windows::Keypad(this, 1);
|
||||||
|
this->window_add(keypad_p2);
|
||||||
|
if (!cfg::CONFIGURATOR_STANDALONE && AUTO_SHOW_KEYPAD_P2) {
|
||||||
|
keypad_p2->set_active(true);
|
||||||
|
set_overlay_active = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IO panel needs to know what game is running
|
||||||
|
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||||
|
overlay::Window *iopanel = nullptr;
|
||||||
if (avs::game::is_model("LDJ")) {
|
if (avs::game::is_model("LDJ")) {
|
||||||
this->window_add(new overlay::windows::IIDXSubScreen(this));
|
iopanel = new overlay::windows::IIDXIOPanel(this);
|
||||||
|
} else if (avs::game::is_model("MDX")) {
|
||||||
|
iopanel = new overlay::windows::DDRIOPanel(this);
|
||||||
|
} else if (avs::game::is_model({"J32", "J33", "K32", "K33", "L32", "L33", "M32"})) {
|
||||||
|
iopanel = new overlay::windows::GitadoraIOPanel(this);
|
||||||
|
} else {
|
||||||
|
iopanel = new overlay::windows::IOPanel(this);
|
||||||
}
|
}
|
||||||
if (avs::game::is_model("KFC")) {
|
if (iopanel) {
|
||||||
this->window_add(new overlay::windows::SDVXSubScreen(this));
|
this->window_add(iopanel);
|
||||||
|
if (AUTO_SHOW_IOPANEL) {
|
||||||
|
iopanel->set_active(true);
|
||||||
|
set_overlay_active = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// subscreens need DirectX, so don't try to initialize them in standalone
|
||||||
|
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||||
|
overlay::Window *subscreen = nullptr;
|
||||||
|
if (avs::game::is_model("LDJ")) {
|
||||||
|
if (games::iidx::TDJ_MODE) {
|
||||||
|
subscreen = new overlay::windows::IIDXSubScreen(this);
|
||||||
|
} else {
|
||||||
|
subscreen = new overlay::windows::IIDXSegmentDisplay(this);
|
||||||
|
}
|
||||||
|
} else if (avs::game::is_model("KFC")) {
|
||||||
|
subscreen = new overlay::windows::SDVXSubScreen(this);
|
||||||
|
}
|
||||||
|
if (subscreen) {
|
||||||
|
this->window_add(subscreen);
|
||||||
|
if (AUTO_SHOW_SUBSCREEN) {
|
||||||
|
subscreen->set_active(true);
|
||||||
|
set_overlay_active = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (set_overlay_active) {
|
||||||
|
this->set_active(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -533,15 +658,7 @@ void overlay::SpiceOverlay::reset_recreate() {
|
|||||||
ImGui_ImplDX9_CreateDeviceObjects();
|
ImGui_ImplDX9_CreateDeviceObjects();
|
||||||
}
|
}
|
||||||
|
|
||||||
void overlay::SpiceOverlay::input_char(unsigned int c, bool rawinput) {
|
void overlay::SpiceOverlay::input_char(unsigned int c) {
|
||||||
|
|
||||||
// disable rawinput characters once we got one from somewhere else (such as a WndProc)
|
|
||||||
if (!rawinput) {
|
|
||||||
this->rawinput_char = false;
|
|
||||||
} else if (!this->rawinput_char) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add character to ImGui
|
// add character to ImGui
|
||||||
ImGui::GetIO().AddInputCharacter(c);
|
ImGui::GetIO().AddInputCharacter(c);
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-2
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <functional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <d3d9.h>
|
#include <d3d9.h>
|
||||||
@@ -18,6 +19,12 @@ namespace overlay {
|
|||||||
|
|
||||||
// settings
|
// settings
|
||||||
extern bool ENABLED;
|
extern bool ENABLED;
|
||||||
|
extern bool AUTO_SHOW_FPS;
|
||||||
|
extern bool AUTO_SHOW_SUBSCREEN;
|
||||||
|
extern bool AUTO_SHOW_IOPANEL;
|
||||||
|
extern bool AUTO_SHOW_KEYPAD_P1;
|
||||||
|
extern bool AUTO_SHOW_KEYPAD_P2;
|
||||||
|
extern bool USE_WM_CHAR_FOR_IMGUI_CHAR_INPUT;
|
||||||
|
|
||||||
class SpiceOverlay {
|
class SpiceOverlay {
|
||||||
public:
|
public:
|
||||||
@@ -43,7 +50,7 @@ namespace overlay {
|
|||||||
static bool update_cursor();
|
static bool update_cursor();
|
||||||
static void reset_invalidate();
|
static void reset_invalidate();
|
||||||
static void reset_recreate();
|
static void reset_recreate();
|
||||||
void input_char(unsigned int c, bool rawinput = false);
|
void input_char(unsigned int c);
|
||||||
|
|
||||||
uint32_t *sw_get_pixel_data(int *width, int *height);
|
uint32_t *sw_get_pixel_data(int *width, int *height);
|
||||||
|
|
||||||
@@ -60,6 +67,22 @@ namespace overlay {
|
|||||||
return this->device;
|
return this->device;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool can_transform_touch_input() {
|
||||||
|
return (this->subscreen_mouse_handler != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool transform_touch_point(LONG *x, LONG *y) {
|
||||||
|
if (this->get_active() && this->subscreen_mouse_handler) {
|
||||||
|
return this->subscreen_mouse_handler(x, y);
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_subscreen_mouse_handler(const std::function<bool(LONG *, LONG *)> &f) {
|
||||||
|
this->subscreen_mouse_handler = f;
|
||||||
|
}
|
||||||
|
|
||||||
// renderer
|
// renderer
|
||||||
OverlayRenderer renderer;
|
OverlayRenderer renderer;
|
||||||
float total_elapsed = 0.f;
|
float total_elapsed = 0.f;
|
||||||
@@ -80,9 +103,10 @@ namespace overlay {
|
|||||||
std::vector<std::unique_ptr<Window>> windows;
|
std::vector<std::unique_ptr<Window>> windows;
|
||||||
Window *window_fps = nullptr;
|
Window *window_fps = nullptr;
|
||||||
|
|
||||||
|
std::function<bool(LONG *, LONG *)> subscreen_mouse_handler = nullptr;
|
||||||
|
|
||||||
bool active = false;
|
bool active = false;
|
||||||
bool toggle_down = false;
|
bool toggle_down = false;
|
||||||
bool rawinput_char = true;
|
|
||||||
bool hotkey_toggle = false;
|
bool hotkey_toggle = false;
|
||||||
bool hotkey_toggle_last = false;
|
bool hotkey_toggle_last = false;
|
||||||
|
|
||||||
@@ -92,6 +116,7 @@ namespace overlay {
|
|||||||
// global
|
// global
|
||||||
extern std::mutex OVERLAY_MUTEX;
|
extern std::mutex OVERLAY_MUTEX;
|
||||||
extern std::unique_ptr<overlay::SpiceOverlay> OVERLAY;
|
extern std::unique_ptr<overlay::SpiceOverlay> OVERLAY;
|
||||||
|
extern ImFont* DSEG_FONT;
|
||||||
|
|
||||||
// synchronized helpers
|
// synchronized helpers
|
||||||
void create_d3d9(HWND hWnd, IDirect3D9 *d3d, IDirect3DDevice9 *device);
|
void create_d3d9(HWND hWnd, IDirect3D9 *d3d, IDirect3DDevice9 *device);
|
||||||
|
|||||||
+17
-3
@@ -65,9 +65,9 @@ void overlay::Window::build() {
|
|||||||
if (!cfg::CONFIGURATOR_STANDALONE && (size_max.x < 0 || size_max.y < 0)) {
|
if (!cfg::CONFIGURATOR_STANDALONE && (size_max.x < 0 || size_max.y < 0)) {
|
||||||
auto &display_size = ImGui::GetIO().DisplaySize;
|
auto &display_size = ImGui::GetIO().DisplaySize;
|
||||||
ImVec2 size_max_auto(display_size.x - 100, display_size.y - 100);
|
ImVec2 size_max_auto(display_size.x - 100, display_size.y - 100);
|
||||||
ImGui::SetNextWindowSizeConstraints(size_min, size_max_auto);
|
ImGui::SetNextWindowSizeConstraints(size_min, size_max_auto, resize_callback);
|
||||||
} else {
|
} else {
|
||||||
ImGui::SetNextWindowSizeConstraints(size_min, size_max);
|
ImGui::SetNextWindowSizeConstraints(size_min, size_max, resize_callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
// background alpha
|
// background alpha
|
||||||
@@ -75,6 +75,12 @@ void overlay::Window::build() {
|
|||||||
ImGui::SetNextWindowBgAlpha(this->bg_alpha);
|
ImGui::SetNextWindowBgAlpha(this->bg_alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this->remove_window_padding) {
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
// create window
|
// create window
|
||||||
if (ImGui::Begin(
|
if (ImGui::Begin(
|
||||||
(this->title + "###" + to_string(this)).c_str(),
|
(this->title + "###" + to_string(this)).c_str(),
|
||||||
@@ -82,7 +88,8 @@ void overlay::Window::build() {
|
|||||||
this->flags)) {
|
this->flags)) {
|
||||||
|
|
||||||
// window attributes
|
// window attributes
|
||||||
ImGui::SetWindowPos(this->initial_pos(), ImGuiCond_Once);
|
this->calculate_initial_window();
|
||||||
|
ImGui::SetWindowPos(this->init_pos, ImGuiCond_Once);
|
||||||
ImGui::SetWindowSize(this->init_size, ImGuiCond_Once);
|
ImGui::SetWindowSize(this->init_size, ImGuiCond_Once);
|
||||||
|
|
||||||
// add content
|
// add content
|
||||||
@@ -96,6 +103,13 @@ void overlay::Window::build() {
|
|||||||
// end window
|
// end window
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this->remove_window_padding) {
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// add raw content
|
// add raw content
|
||||||
|
|||||||
+7
-4
@@ -10,9 +10,11 @@ namespace overlay {
|
|||||||
class Window {
|
class Window {
|
||||||
public:
|
public:
|
||||||
virtual ~Window();
|
virtual ~Window();
|
||||||
virtual const ImVec2 initial_pos() {
|
|
||||||
return this->init_pos;
|
// an opportunity to calculate initial window position and size within the ImGui
|
||||||
}
|
// window context, since it may not be possible in the Window constructor
|
||||||
|
virtual void calculate_initial_window() {}
|
||||||
|
|
||||||
virtual void build_content() = 0;
|
virtual void build_content() = 0;
|
||||||
virtual void update();
|
virtual void update();
|
||||||
virtual void after_render() {};
|
virtual void after_render() {};
|
||||||
@@ -21,7 +23,6 @@ namespace overlay {
|
|||||||
void toggle_active();
|
void toggle_active();
|
||||||
void set_active(bool active);
|
void set_active(bool active);
|
||||||
bool get_active();
|
bool get_active();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
// state
|
// state
|
||||||
@@ -30,7 +31,9 @@ namespace overlay {
|
|||||||
std::vector<Window*> children;
|
std::vector<Window*> children;
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
|
bool remove_window_padding = false;
|
||||||
bool draws_window = true;
|
bool draws_window = true;
|
||||||
|
ImGuiSizeCallback resize_callback = nullptr;
|
||||||
std::string title = "Title";
|
std::string title = "Title";
|
||||||
ImGuiWindowFlags flags = 0;
|
ImGuiWindowFlags flags = 0;
|
||||||
size_t toggle_button = ~0u;
|
size_t toggle_button = ~0u;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ namespace overlay::windows {
|
|||||||
// configure editor defaults
|
// configure editor defaults
|
||||||
this->editor = new MemoryEditor();
|
this->editor = new MemoryEditor();
|
||||||
this->editor->OptShowDataPreview = true;
|
this->editor->OptShowDataPreview = true;
|
||||||
this->editor->PreviewDataType = MemoryEditor::DataType::DataType_U16;
|
this->editor->PreviewDataType = ImGuiDataType_U16;
|
||||||
}
|
}
|
||||||
|
|
||||||
ACIOStatusBuffers::~ACIOStatusBuffers() {
|
ACIOStatusBuffers::~ACIOStatusBuffers() {
|
||||||
|
|||||||
+147
-83
@@ -4,6 +4,7 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#include <shellapi.h>
|
||||||
#include <commdlg.h>
|
#include <commdlg.h>
|
||||||
|
|
||||||
#include "build/defs.h"
|
#include "build/defs.h"
|
||||||
@@ -17,6 +18,7 @@
|
|||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
#include "launcher/launcher.h"
|
#include "launcher/launcher.h"
|
||||||
#include "launcher/shutdown.h"
|
#include "launcher/shutdown.h"
|
||||||
|
#include "launcher/options.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
#include "overlay/imgui/extensions.h"
|
#include "overlay/imgui/extensions.h"
|
||||||
#include "rawinput/piuio.h"
|
#include "rawinput/piuio.h"
|
||||||
@@ -34,6 +36,8 @@
|
|||||||
|
|
||||||
namespace overlay::windows {
|
namespace overlay::windows {
|
||||||
|
|
||||||
|
const auto PROJECT_URL = "https://spice2x.github.io";
|
||||||
|
|
||||||
Config::Config(overlay::SpiceOverlay *overlay) : Window(overlay) {
|
Config::Config(overlay::SpiceOverlay *overlay) : Window(overlay) {
|
||||||
this->title = "Configuration";
|
this->title = "Configuration";
|
||||||
this->toggle_button = games::OverlayButtons::ToggleConfig;
|
this->toggle_button = games::OverlayButtons::ToggleConfig;
|
||||||
@@ -238,6 +242,29 @@ namespace overlay::windows {
|
|||||||
|
|
||||||
// keypad buttons
|
// keypad buttons
|
||||||
ImGui::TextUnformatted("");
|
ImGui::TextUnformatted("");
|
||||||
|
if (this->games_selected_name == "Sound Voltex") {
|
||||||
|
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 8);
|
||||||
|
ImGui::TextColored(
|
||||||
|
ImVec4(1, 0.5f, 0.5f, 1.f),
|
||||||
|
"WARNING: Valkyrie Cabinet I/O will ignore the keypad!");
|
||||||
|
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 8);
|
||||||
|
ImGui::TextWrapped(
|
||||||
|
"Use Toggle Sub Screen button to show the overlay and use your mouse, "
|
||||||
|
"connect using SpiceCompanion app, or connect a touch screen to enter "
|
||||||
|
"the PIN.");
|
||||||
|
ImGui::TextUnformatted("");
|
||||||
|
} else if (this->games_selected_name == "Beatmania IIDX") {
|
||||||
|
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 8);
|
||||||
|
ImGui::TextColored(
|
||||||
|
ImVec4(1, 0.5f, 0.5f, 1.f),
|
||||||
|
"WARNING: Lightning Model (TDJ) I/O will ignore the keypad!");
|
||||||
|
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 8);
|
||||||
|
ImGui::TextWrapped(
|
||||||
|
"Use Toggle Sub Screen button to show the overlay and use your mouse, "
|
||||||
|
"connect using SpiceCompanion app, or connect a touch screen to enter "
|
||||||
|
"the PIN.");
|
||||||
|
ImGui::TextUnformatted("");
|
||||||
|
}
|
||||||
auto keypad_buttons = games::get_buttons_keypads(this->games_selected_name);
|
auto keypad_buttons = games::get_buttons_keypads(this->games_selected_name);
|
||||||
auto keypad_count = eamuse_get_game_keypads_name();
|
auto keypad_count = eamuse_get_game_keypads_name();
|
||||||
if (keypad_count == 1) {
|
if (keypad_count == 1) {
|
||||||
@@ -336,39 +363,44 @@ namespace overlay::windows {
|
|||||||
ImGui::EndTabItem();
|
ImGui::EndTabItem();
|
||||||
}
|
}
|
||||||
if (ImGui::BeginTabItem("Options")) {
|
if (ImGui::BeginTabItem("Options")) {
|
||||||
|
// options list
|
||||||
ImGui::BeginChild("Options", ImVec2(
|
ImGui::BeginChild("Options", ImVec2(
|
||||||
0, ImGui::GetWindowContentRegionMax().y - page_offset), false);
|
0, ImGui::GetWindowContentRegionMax().y - page_offset), false);
|
||||||
|
|
||||||
// get options and categories
|
|
||||||
auto options = games::get_options(this->games_selected_name);
|
auto options = games::get_options(this->games_selected_name);
|
||||||
std::vector<const char *> categories;
|
for (auto category : launcher::get_categories(false)) {
|
||||||
categories.push_back("Everything");
|
|
||||||
if (options) {
|
|
||||||
for (auto &option : *options) {
|
|
||||||
bool found = false;
|
|
||||||
for (auto &category : categories) {
|
|
||||||
if (strcmp(option.get_definition().category.c_str(), category) == 0) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
categories.push_back(option.get_definition().category.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get current category
|
|
||||||
std::string category = "";
|
|
||||||
if (this->options_category != -1 && this->options_category < (int) categories.size()) {
|
|
||||||
category = categories[this->options_category];
|
|
||||||
if (category == "Everything") {
|
|
||||||
category = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// options list
|
|
||||||
this->build_options(options, category);
|
this->build_options(options, category);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 8);
|
||||||
|
ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), "Advanced Options");
|
||||||
|
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 8);
|
||||||
|
ImGui::TextWrapped(
|
||||||
|
"Rest of the options have moved to Advanced tab.");
|
||||||
|
ImGui::TextUnformatted("");
|
||||||
|
ImGui::TextUnformatted("");
|
||||||
|
ImGui::EndChild();
|
||||||
|
|
||||||
|
// hidden options checkbox
|
||||||
|
ImGui::Checkbox("Show Hidden Options", &this->options_show_hidden);
|
||||||
|
if (!cfg::CONFIGURATOR_STANDALONE && this->options_dirty) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Restart Game")) {
|
||||||
|
launcher::restart();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::HelpMarker("You need to restart the game to apply the changed settings.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
if (ImGui::BeginTabItem("Advanced")) {
|
||||||
|
// options list
|
||||||
|
ImGui::BeginChild("AdvancedOptions", ImVec2(
|
||||||
|
0, ImGui::GetWindowContentRegionMax().y - page_offset), false);
|
||||||
|
auto options = games::get_options(this->games_selected_name);
|
||||||
|
for (auto category : launcher::get_categories(true)) {
|
||||||
|
this->build_options(options, category);
|
||||||
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
|
||||||
// hidden options checkbox
|
// hidden options checkbox
|
||||||
@@ -402,12 +434,6 @@ namespace overlay::windows {
|
|||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
// category selection
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.3f);
|
|
||||||
ImGui::Combo("Category", &this->options_category, categories.data(), categories.size());
|
|
||||||
ImGui::PopItemWidth();
|
|
||||||
|
|
||||||
ImGui::EndTabItem();
|
ImGui::EndTabItem();
|
||||||
}
|
}
|
||||||
if (ImGui::BeginTabItem("About")) {
|
if (ImGui::BeginTabItem("About")) {
|
||||||
@@ -488,10 +514,10 @@ namespace overlay::windows {
|
|||||||
style_color = true;
|
style_color = true;
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.7f, 0.7f, 0.7f, 1.f));
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.7f, 0.7f, 0.7f, 1.f));
|
||||||
}
|
}
|
||||||
vertical_align_text_column();
|
ImGui::AlignTextToFramePadding();
|
||||||
ImGui::Text("%s", button_name.c_str());
|
ImGui::Text("%s", button_name.c_str());
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
vertical_align_text_column();
|
ImGui::AlignTextToFramePadding();
|
||||||
ImGui::Text("%s", button_display.empty() ? "None" : button_display.c_str());
|
ImGui::Text("%s", button_display.empty() ? "None" : button_display.c_str());
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
if (style_color) {
|
if (style_color) {
|
||||||
@@ -622,8 +648,10 @@ namespace overlay::windows {
|
|||||||
case rawinput::HID: {
|
case rawinput::HID: {
|
||||||
auto hid = device->hidInfo;
|
auto hid = device->hidInfo;
|
||||||
|
|
||||||
// ignore touchscreen button inputs
|
// ignore touchscreen and digitizer button inputs
|
||||||
if (!rawinput::touch::is_touchscreen(device)) {
|
// digitizer has funky stuff like "Touch Valid" "Data Valid" always held high
|
||||||
|
if (!rawinput::touch::is_touchscreen(device) &&
|
||||||
|
hid->caps.UsagePage != 0x0D) {
|
||||||
|
|
||||||
// button caps
|
// button caps
|
||||||
auto button_states_list = &hid->button_states;
|
auto button_states_list = &hid->button_states;
|
||||||
@@ -1179,7 +1207,7 @@ namespace overlay::windows {
|
|||||||
}
|
}
|
||||||
ImGui::Text("%s", analog_name.c_str());
|
ImGui::Text("%s", analog_name.c_str());
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
vertical_align_text_column();
|
ImGui::AlignTextToFramePadding();
|
||||||
ImGui::Text("%s", analog_display.empty() ? "None" : analog_display.c_str());
|
ImGui::Text("%s", analog_display.empty() ? "None" : analog_display.c_str());
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
if (analog_display.empty()) {
|
if (analog_display.empty()) {
|
||||||
@@ -1502,7 +1530,7 @@ namespace overlay::windows {
|
|||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::Text("%s", light_name.c_str());
|
ImGui::Text("%s", light_name.c_str());
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
vertical_align_text_column();
|
ImGui::AlignTextToFramePadding();
|
||||||
ImGui::Text("%s", light_display.empty() ? "None" : light_display.c_str());
|
ImGui::Text("%s", light_display.empty() ? "None" : light_display.c_str());
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
if (light_display.empty()) {
|
if (light_display.empty()) {
|
||||||
@@ -1807,7 +1835,7 @@ namespace overlay::windows {
|
|||||||
if (!cfg::CONFIGURATOR_STANDALONE && !this->keypads_card_select_browser[player].IsOpened()) {
|
if (!cfg::CONFIGURATOR_STANDALONE && !this->keypads_card_select_browser[player].IsOpened()) {
|
||||||
this->keypads_card_select_browser[player].SetTitle("Card Select");
|
this->keypads_card_select_browser[player].SetTitle("Card Select");
|
||||||
this->keypads_card_select_browser[player].SetTypeFilters({".txt", "*"});
|
this->keypads_card_select_browser[player].SetTypeFilters({".txt", "*"});
|
||||||
this->keypads_card_select_browser[player].flags_ |= ImGuiFileBrowserFlags_EnterNewFilename;
|
// this->keypads_card_select_browser[player].flags_ |= ImGuiFileBrowserFlags_EnterNewFilename;
|
||||||
this->keypads_card_select_browser[player].Open();
|
this->keypads_card_select_browser[player].Open();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1903,31 +1931,25 @@ namespace overlay::windows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Config::build_options(std::vector<Option> *options, const std::string &category) {
|
void Config::build_options(std::vector<Option> *options, const std::string &category) {
|
||||||
|
int options_count;
|
||||||
|
|
||||||
ImGui::Columns(3, "OptionsColumns", true);
|
ImGui::Columns(3, "OptionsColumns", true);
|
||||||
ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), "Title"); ImGui::NextColumn();
|
ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), category.c_str());
|
||||||
|
ImGui::NextColumn();
|
||||||
ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), "Parameter");
|
ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), "Parameter");
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::HelpMarker("These are the command-line parameters you can use in your .bat file to set the options.\n"
|
ImGui::HelpMarker(
|
||||||
|
"These are the command-line parameters you can use in your .bat file to set the options.\n"
|
||||||
"Example: spice.exe -w -ea\n"
|
"Example: spice.exe -w -ea\n"
|
||||||
" spice64.exe -api 1337 -apipass changeme");
|
" spice64.exe -api 1337 -apipass changeme");
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), "Setting"); ImGui::NextColumn();
|
ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), "Setting");
|
||||||
|
ImGui::NextColumn();
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
// check if empty
|
|
||||||
if (!options || options->empty()) {
|
|
||||||
ImGui::TextUnformatted("-");
|
|
||||||
ImGui::NextColumn();
|
|
||||||
ImGui::TextUnformatted("-");
|
|
||||||
ImGui::NextColumn();
|
|
||||||
ImGui::TextUnformatted("-");
|
|
||||||
ImGui::NextColumn();
|
|
||||||
}
|
|
||||||
|
|
||||||
// iterate options
|
// iterate options
|
||||||
if (options) {
|
options_count = 0;
|
||||||
for (auto &option : *options) {
|
for (auto &option : *options) {
|
||||||
|
|
||||||
// get option definition
|
// get option definition
|
||||||
auto &definition = option.get_definition();
|
auto &definition = option.get_definition();
|
||||||
|
|
||||||
@@ -1952,9 +1974,11 @@ namespace overlay::windows {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
options_count += 1;
|
||||||
|
|
||||||
// list entry
|
// list entry
|
||||||
ImGui::PushID(&option);
|
ImGui::PushID(&option);
|
||||||
vertical_align_text_column();
|
ImGui::AlignTextToFramePadding();
|
||||||
ImGui::HelpMarker(definition.desc.c_str());
|
ImGui::HelpMarker(definition.desc.c_str());
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (option.is_active()) {
|
if (option.is_active()) {
|
||||||
@@ -1972,7 +1996,7 @@ namespace overlay::windows {
|
|||||||
ImGui::Text("%s", definition.title.c_str());
|
ImGui::Text("%s", definition.title.c_str());
|
||||||
}
|
}
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
vertical_align_text_column();
|
ImGui::AlignTextToFramePadding();
|
||||||
ImGui::Text("-%s", definition.name.c_str());
|
ImGui::Text("-%s", definition.name.c_str());
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
if (option.disabled) {
|
if (option.disabled) {
|
||||||
@@ -1997,7 +2021,7 @@ namespace overlay::windows {
|
|||||||
if ('0' <= data->EventChar && data->EventChar <= '9') {
|
if ('0' <= data->EventChar && data->EventChar <= '9') {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1; // discard
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *hint = definition.setting_name.empty() ? "Enter number..."
|
const char *hint = definition.setting_name.empty() ? "Enter number..."
|
||||||
@@ -2012,6 +2036,37 @@ namespace overlay::windows {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case OptionType::Hex: {
|
||||||
|
char buffer[512];
|
||||||
|
strncpy(buffer, option.value.c_str(), sizeof(buffer) - 1);
|
||||||
|
buffer[sizeof(buffer) - 1] = '\0';
|
||||||
|
auto digits_filter = [](ImGuiInputTextCallbackData* data) {
|
||||||
|
if ('0' <= data->EventChar && data->EventChar <= '9') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ('a' <= data->EventChar && data->EventChar <= 'f') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ('A' <= data->EventChar && data->EventChar <= 'F') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (data->EventChar == 'x' || data->EventChar == 'X') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1; // discard
|
||||||
|
};
|
||||||
|
const char *hint = definition.setting_name.empty() ? "Enter hex..."
|
||||||
|
: definition.setting_name.c_str();
|
||||||
|
|
||||||
|
if (ImGui::InputTextWithHint("", hint,
|
||||||
|
buffer, sizeof(buffer) - 1,
|
||||||
|
ImGuiInputTextFlags_CallbackCharFilter, digits_filter)) {
|
||||||
|
this->options_dirty = true;
|
||||||
|
option.value = buffer;
|
||||||
|
::Config::getInstance().updateBinding(games_list[games_selected], option);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case OptionType::Text: {
|
case OptionType::Text: {
|
||||||
char buffer[512];
|
char buffer[512];
|
||||||
strncpy(buffer, option.value.c_str(), sizeof(buffer) - 1);
|
strncpy(buffer, option.value.c_str(), sizeof(buffer) - 1);
|
||||||
@@ -2082,8 +2137,8 @@ namespace overlay::windows {
|
|||||||
// disabled help
|
// disabled help
|
||||||
if (option.disabled) {
|
if (option.disabled) {
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::HelpMarker("This option can not be edited because it was overriden by command-line "
|
ImGui::HelpMarker(
|
||||||
"options.\n"
|
"This option can not be edited because it was overriden by command-line options.\n"
|
||||||
"Run spicecfg.exe to configure the options and then run spice(64).exe directly.");
|
"Run spicecfg.exe to configure the options and then run spice(64).exe directly.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2091,30 +2146,43 @@ namespace overlay::windows {
|
|||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if empty
|
||||||
|
if (options_count == 0) {
|
||||||
|
ImGui::TextUnformatted("-");
|
||||||
|
ImGui::NextColumn();
|
||||||
|
ImGui::TextUnformatted("-");
|
||||||
|
ImGui::NextColumn();
|
||||||
|
ImGui::TextUnformatted("-");
|
||||||
|
ImGui::NextColumn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::Columns(1);
|
||||||
|
ImGui::TextUnformatted("");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::build_about() {
|
void Config::build_about() {
|
||||||
auto time = get_system_milliseconds();
|
|
||||||
auto value = ((float) cos(time * 0.0005) + 1.f) * 0.5f;
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.8f - value * 0.3f, 1.f, 1.f, 1.f));
|
|
||||||
ImGui::TextUnformatted(std::string(
|
ImGui::TextUnformatted(std::string(
|
||||||
"SpiceTools\r\n"
|
"spice2x (a fork of SpiceTools)\r\n"
|
||||||
"=========================\r\n" +
|
"=========================\r\n" +
|
||||||
to_string(VERSION_STRING) + "\r\n\r\n" + to_string(
|
to_string(VERSION_STRING)).c_str());
|
||||||
resutil::load_file_string_crlf(IDR_README)) +
|
|
||||||
|
ImGui::TextUnformatted("");
|
||||||
|
|
||||||
|
if (ImGui::Button(PROJECT_URL)) {
|
||||||
|
launch_url();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::TextUnformatted("");
|
||||||
|
ImGui::TextUnformatted(std::string(
|
||||||
|
resutil::load_file_string_crlf(IDR_README) +
|
||||||
"\r\n" +
|
"\r\n" +
|
||||||
"Changelog (Highlights):\r\n" +
|
"Changelog (Highlights):\r\n" +
|
||||||
resutil::load_file_string_crlf(IDR_CHANGELOG)).c_str());
|
resutil::load_file_string_crlf(IDR_CHANGELOG)).c_str());
|
||||||
ImGui::PopStyleColor();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::build_licenses() {
|
void Config::build_licenses() {
|
||||||
auto time = get_system_milliseconds();
|
|
||||||
auto value = ((float) cos(time * 0.0005) + 1.f) * 0.5f;
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 1.f, 0.9f - value * 0.3f, 1.f));
|
|
||||||
ImGui::TextUnformatted(resutil::load_file_string_crlf(IDR_LICENSES).c_str());
|
ImGui::TextUnformatted(resutil::load_file_string_crlf(IDR_LICENSES).c_str());
|
||||||
ImGui::PopStyleColor();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::build_launcher() {
|
void Config::build_launcher() {
|
||||||
@@ -2156,15 +2224,11 @@ namespace overlay::windows {
|
|||||||
"at the same time using pages.");
|
"at the same time using pages.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::vertical_align_text_column() {
|
void Config::launch_url() {
|
||||||
/*
|
// doing this on a separate thread to avoid polluting ImGui context
|
||||||
* see https://github.com/ocornut/imgui/issues/1284
|
std::thread t([] {
|
||||||
* height of most widgets (like Button) is GetFontSize() + GetStyle().FramePadding.y * 2
|
ShellExecuteA(NULL, "open", PROJECT_URL, NULL, NULL, SW_SHOWNORMAL);
|
||||||
* height of unformatted text is GetFontSize()
|
});
|
||||||
* by default, rows are top-aligned
|
t.join();
|
||||||
* so we add FramePadding.y to the top of unformatted text for vertical centering.
|
|
||||||
*/
|
|
||||||
auto y = ImGui::GetCursorPosY() + ImGui::GetStyle().FramePadding.y;
|
|
||||||
ImGui::SetCursorPosY(y);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,8 +69,8 @@ namespace overlay::windows {
|
|||||||
void build_about();
|
void build_about();
|
||||||
void build_licenses();
|
void build_licenses();
|
||||||
void build_launcher();
|
void build_launcher();
|
||||||
|
void launch_url();
|
||||||
|
|
||||||
static void build_page_selector(int *page);
|
static void build_page_selector(int *page);
|
||||||
static void vertical_align_text_column();
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-7
@@ -1,22 +1,26 @@
|
|||||||
|
#include <iomanip>
|
||||||
|
#include <sstream>
|
||||||
#include "fps.h"
|
#include "fps.h"
|
||||||
|
|
||||||
namespace overlay::windows {
|
namespace overlay::windows {
|
||||||
|
|
||||||
FPS::FPS(SpiceOverlay *overlay) : Window(overlay) {
|
FPS::FPS(SpiceOverlay *overlay) : Window(overlay) {
|
||||||
this->title = "FPS & Frame Time";
|
this->title = "Stats";
|
||||||
this->flags = ImGuiWindowFlags_NoTitleBar
|
this->flags = ImGuiWindowFlags_NoTitleBar
|
||||||
| ImGuiWindowFlags_NoCollapse
|
|
||||||
| ImGuiWindowFlags_NoResize
|
| ImGuiWindowFlags_NoResize
|
||||||
|
| ImGuiWindowFlags_NoCollapse
|
||||||
| ImGuiWindowFlags_AlwaysAutoResize
|
| ImGuiWindowFlags_AlwaysAutoResize
|
||||||
| ImGuiWindowFlags_NoDecoration
|
|
||||||
| ImGuiWindowFlags_NoFocusOnAppearing
|
| ImGuiWindowFlags_NoFocusOnAppearing
|
||||||
| ImGuiWindowFlags_NoNavFocus
|
| ImGuiWindowFlags_NoNavFocus
|
||||||
| ImGuiWindowFlags_NoNavInputs;
|
| ImGuiWindowFlags_NoNavInputs
|
||||||
|
| ImGuiWindowFlags_NoDocking;
|
||||||
this->bg_alpha = 0.4f;
|
this->bg_alpha = 0.4f;
|
||||||
|
this->start_time = std::chrono::system_clock::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
const ImVec2 FPS::initial_pos() {
|
void FPS::calculate_initial_window() {
|
||||||
return ImVec2(ImGui::GetIO().DisplaySize.x - 100, 10);
|
// width is 114x82 px with window decoration, 98x47 for the content
|
||||||
|
this->init_pos = ImVec2(ImGui::GetIO().DisplaySize.x - 120, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FPS::build_content() {
|
void FPS::build_content() {
|
||||||
@@ -24,6 +28,25 @@ namespace overlay::windows {
|
|||||||
// frame timers
|
// frame timers
|
||||||
ImGuiIO &io = ImGui::GetIO();
|
ImGuiIO &io = ImGui::GetIO();
|
||||||
ImGui::Text("FPS: %.1f", io.Framerate);
|
ImGui::Text("FPS: %.1f", io.Framerate);
|
||||||
ImGui::Text("FT: %.2fms", 1000 / io.Framerate);
|
// ImGui::Text("FT: %.2fms", 1000 / io.Framerate);
|
||||||
|
|
||||||
|
auto now = std::chrono::system_clock::now();
|
||||||
|
|
||||||
|
// current time
|
||||||
|
{
|
||||||
|
auto now_t = std::chrono::system_clock::to_time_t(now);
|
||||||
|
static CHAR buf[48];
|
||||||
|
std::strftime(buf, sizeof(buf), "Time: %H:%M:%S", std::localtime(&now_t));
|
||||||
|
ImGui::Text(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// elapsed time
|
||||||
|
{
|
||||||
|
auto d = now - this->start_time;
|
||||||
|
const auto h = std::chrono::duration_cast<std::chrono::hours>(d);
|
||||||
|
const auto m = std::chrono::duration_cast<std::chrono::minutes>(d - h);
|
||||||
|
const auto s = std::chrono::duration_cast<std::chrono::seconds>(d - h - m);
|
||||||
|
ImGui::Text("Up: %02d:%02d:%02d", (int)h.count(), (int)m.count(), (int)s.count());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
#include "overlay/window.h"
|
#include "overlay/window.h"
|
||||||
|
|
||||||
namespace overlay::windows {
|
namespace overlay::windows {
|
||||||
|
|
||||||
class FPS : public Window {
|
class FPS : public Window {
|
||||||
|
private:
|
||||||
|
std::chrono::system_clock::time_point start_time;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
FPS(SpiceOverlay *overlay);
|
FPS(SpiceOverlay *overlay);
|
||||||
|
|
||||||
const ImVec2 initial_pos() override;
|
void calculate_initial_window() override;
|
||||||
void build_content() override;
|
void build_content() override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,183 @@
|
|||||||
|
#undef CINTERFACE
|
||||||
|
|
||||||
|
#include "generic_sub.h"
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
#include "games/io.h"
|
||||||
|
#include "hooks/graphics/backends/d3d9/d3d9_backend.h"
|
||||||
|
#include "hooks/graphics/backends/d3d9/d3d9_device.h"
|
||||||
|
#include "util/logging.h"
|
||||||
|
#include "util/utils.h"
|
||||||
|
|
||||||
|
// #define OVERLAYDBG 1
|
||||||
|
|
||||||
|
namespace overlay::windows {
|
||||||
|
|
||||||
|
const ImVec4 YELLOW(1.f, 1.f, 0.f, 1.f);
|
||||||
|
const ImVec4 WHITE(1.f, 1.f, 1.f, 1.f);
|
||||||
|
|
||||||
|
GenericSubScreen::GenericSubScreen(SpiceOverlay *overlay) : Window(overlay), device(overlay->get_device()) {
|
||||||
|
this->remove_window_padding = true;
|
||||||
|
this->flags = ImGuiWindowFlags_NoScrollbar |
|
||||||
|
ImGuiWindowFlags_NoCollapse |
|
||||||
|
ImGuiWindowFlags_NoBackground |
|
||||||
|
ImGuiWindowFlags_NoDocking;
|
||||||
|
|
||||||
|
this->size_max = ImVec2(ImGui::GetIO().DisplaySize.x, ImGui::GetIO().DisplaySize.y);
|
||||||
|
this->size_min = ImVec2(240, 135 + ImGui::GetFrameHeight());
|
||||||
|
this->init_size = size_min;
|
||||||
|
this->resize_callback = keep_16_by_9;
|
||||||
|
|
||||||
|
this->toggle_button = games::OverlayButtons::ToggleSubScreen;
|
||||||
|
this->texture_width = 0;
|
||||||
|
this->texture_height = 0;
|
||||||
|
|
||||||
|
overlay->set_subscreen_mouse_handler([this](LONG *x, LONG *y) -> bool {
|
||||||
|
// convert to normalized form (relative window coordinates 0.f-1.f)
|
||||||
|
ImVec2 xy(*x, *y);
|
||||||
|
xy.x = (xy.x - overlay_content_top_left.x) / overlay_content_size.x;
|
||||||
|
xy.y = (xy.y - overlay_content_top_left.y) / overlay_content_size.y;
|
||||||
|
|
||||||
|
// x/y can be outside of window
|
||||||
|
if (xy.x < 0.f || 1.f < xy.x || xy.y < 0.f || 1.f < xy.y) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// call into child
|
||||||
|
this->touch_transform(xy, x, y);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenericSubScreen::touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) {}
|
||||||
|
|
||||||
|
void GenericSubScreen::build_content() {
|
||||||
|
this->draw_texture();
|
||||||
|
|
||||||
|
#if OVERLAYDBG
|
||||||
|
if (this->status_message.has_value()) {
|
||||||
|
log_warning("sub::overlay", "{}", this->status_message.value().c_str());
|
||||||
|
}
|
||||||
|
if (this->status_message.has_value()) {
|
||||||
|
ImGui::TextColored(YELLOW, "%s", this->status_message.value().c_str());
|
||||||
|
} else if (this->texture) {
|
||||||
|
ImGui::TextColored(WHITE, "Successfully acquired surface texture");
|
||||||
|
} else {
|
||||||
|
ImGui::TextColored(YELLOW, "Failed to acquire surface texture");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GenericSubScreen::build_texture(IDirect3DSurface9 *surface, UINT width, UINT height) {
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
D3DSURFACE_DESC desc {};
|
||||||
|
hr = surface->GetDesc(&desc);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
this->status_message = fmt::format("Failed to get surface descriptor, hr={}", FMT_HRESULT(hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = this->device->CreateTexture(width, height, 0, desc.Usage, desc.Format,
|
||||||
|
desc.Pool, &this->texture, nullptr);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
this->status_message = fmt::format("Failed to create render target, hr={}", FMT_HRESULT(hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->texture_width = width;
|
||||||
|
this->texture_height = height;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenericSubScreen::draw_texture() {
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
auto surface = graphics_d3d9_ldj_get_sub_screen();
|
||||||
|
if (surface == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->draws_window) {
|
||||||
|
// calculate the **content** location and size.
|
||||||
|
// GetContentRegionAvail returns the correct dimension, up to (and including) the resize handle
|
||||||
|
// don't be tempted to change it to some other ImGui routine that accomplishes similar things!
|
||||||
|
overlay_content_top_left = ImGui::GetCursorScreenPos();
|
||||||
|
overlay_content_size = ImGui::GetContentRegionAvail();
|
||||||
|
} else {
|
||||||
|
// no window, full screen
|
||||||
|
overlay_content_top_left = ImVec2(0, 0);
|
||||||
|
overlay_content_size = ImGui::GetIO().DisplaySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->draws_window &&
|
||||||
|
this->texture &&
|
||||||
|
((UINT)overlay_content_size.x != this->texture_width)) {
|
||||||
|
|
||||||
|
#if OVERLAYDBG
|
||||||
|
log_info("sub::overlay", "resize {} != {} ", overlay_content_size.x, this->texture_width);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// hack needed for SDVX; resizing the texture results in darker image, so allocate texture
|
||||||
|
// again with the new size when the window is resized
|
||||||
|
this->texture->Release();
|
||||||
|
this->texture = nullptr;
|
||||||
|
this->texture_width = 0;
|
||||||
|
this->texture_height = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->texture == nullptr) {
|
||||||
|
if (!this->build_texture(surface, overlay_content_size.x, overlay_content_size.y)) {
|
||||||
|
this->texture = nullptr;
|
||||||
|
this->texture_width = 0;
|
||||||
|
this->texture_height = 0;
|
||||||
|
surface->Release();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IDirect3DSurface9 *texture_surface = nullptr;
|
||||||
|
hr = this->texture->GetSurfaceLevel(0, &texture_surface);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
this->status_message = fmt::format("Failed to get texture surface, hr={}", FMT_HRESULT(hr));
|
||||||
|
surface->Release();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = this->device->StretchRect(surface, nullptr, texture_surface, nullptr, D3DTEXF_LINEAR);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
this->status_message = fmt::format("Failed to copy back buffer contents, hr={}", FMT_HRESULT(hr));
|
||||||
|
surface->Release();
|
||||||
|
texture_surface->Release();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
surface->Release();
|
||||||
|
texture_surface->Release();
|
||||||
|
|
||||||
|
// draw the subscreen (this draws *under* ImGui windows, over the game surface)
|
||||||
|
auto bottom_right = overlay_content_size;
|
||||||
|
bottom_right.x += overlay_content_top_left.x;
|
||||||
|
bottom_right.y += overlay_content_top_left.y;
|
||||||
|
ImGui::GetBackgroundDrawList()->AddImage(
|
||||||
|
reinterpret_cast<void *>(this->texture),
|
||||||
|
overlay_content_top_left,
|
||||||
|
bottom_right);
|
||||||
|
|
||||||
|
if (this->draws_window) {
|
||||||
|
// draw an invisible button so that it swallows mouse input
|
||||||
|
// this is needed to prevent the window from being dragged around
|
||||||
|
// (alternatively io.ConfigWindowsMoveFromTitleBarOnly can be set but that is global)
|
||||||
|
ImGui::InvisibleButton(
|
||||||
|
(this->title + "__InvisibleButton").c_str(),
|
||||||
|
overlay_content_size,
|
||||||
|
ImGuiButtonFlags_None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if OVERLAYDBG
|
||||||
|
log_info("sub::overlay", "{} / {} = {}", overlay_content_size.x, overlay_content_size.y, overlay_content_size.x / overlay_content_size.y);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
#ifndef SPICETOOLS_OVERLAY_WINDOWS_GENERIC_SUB_H
|
||||||
|
#define SPICETOOLS_OVERLAY_WINDOWS_GENERIC_SUB_H
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <d3d9.h>
|
||||||
|
|
||||||
|
#include "overlay/window.h"
|
||||||
|
|
||||||
|
namespace overlay::windows {
|
||||||
|
|
||||||
|
class GenericSubScreen : public Window {
|
||||||
|
public:
|
||||||
|
GenericSubScreen(SpiceOverlay *overlay);
|
||||||
|
|
||||||
|
void build_content() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out);
|
||||||
|
ImVec2 overlay_content_top_left;
|
||||||
|
ImVec2 overlay_content_size;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void keep_16_by_9(ImGuiSizeCallbackData* data) {
|
||||||
|
data->DesiredSize.y = (data->DesiredSize.x * 9.f / 16.f) + ImGui::GetFrameHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool build_texture(IDirect3DSurface9 *surface, UINT width, UINT height);
|
||||||
|
void draw_texture();
|
||||||
|
|
||||||
|
std::optional<std::string> status_message = std::nullopt;
|
||||||
|
|
||||||
|
IDirect3DDevice9 *device = nullptr;
|
||||||
|
IDirect3DTexture9 *texture = nullptr;
|
||||||
|
|
||||||
|
UINT texture_width;
|
||||||
|
UINT texture_height;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SPICETOOLS_OVERLAY_WINDOWS_GENERIC_SUB_H
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "iidx_seg.h"
|
||||||
|
#include "games/io.h"
|
||||||
|
#include "games/iidx/iidx.h"
|
||||||
|
#include "util/logging.h"
|
||||||
|
|
||||||
|
namespace overlay::windows {
|
||||||
|
|
||||||
|
uint32_t IIDX_SEGMENT_FONT_SIZE = 64;
|
||||||
|
std::optional<uint32_t> IIDX_SEGMENT_FONT_COLOR = std::nullopt;
|
||||||
|
std::string IIDX_SEGMENT_LOCATION = "bottom";
|
||||||
|
|
||||||
|
static const size_t TICKER_SIZE = 9;
|
||||||
|
static const ImVec4 DARK_GRAY(0.1f, 0.1f, 0.1f, 1.f);
|
||||||
|
static const ImVec4 RED(1.f, 0.f, 0.f, 1.f);
|
||||||
|
static const int PADDING_Y = 8;
|
||||||
|
static const int PADDING_X = 4;
|
||||||
|
|
||||||
|
static const std::map<char, std::pair<char, char>> CHARMAP = {\
|
||||||
|
// period - add a space afterwards (game sends 'm' for period)
|
||||||
|
{'m', {'.', ' '}},
|
||||||
|
// exclamation mark (use ./ to make it look like one)
|
||||||
|
{'!', {'.', '/'}},
|
||||||
|
// font doesn't have tilde so using a dash instead
|
||||||
|
{'~', {'-', '\0'}},
|
||||||
|
};
|
||||||
|
|
||||||
|
IIDXSegmentDisplay::IIDXSegmentDisplay(SpiceOverlay *overlay) : Window(overlay) {
|
||||||
|
if (!DSEG_FONT) {
|
||||||
|
log_fatal("iidx_seg", "DSEG_FONT is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
this->title = "IIDX LED Segment Display";
|
||||||
|
this->toggle_button = games::OverlayButtons::ToggleSubScreen;
|
||||||
|
this->remove_window_padding = true;
|
||||||
|
this->size_max = ImVec2(ImGui::GetIO().DisplaySize.x, ImGui::GetIO().DisplaySize.y);
|
||||||
|
this->flags = ImGuiWindowFlags_NoTitleBar
|
||||||
|
| ImGuiWindowFlags_NoScrollbar
|
||||||
|
| ImGuiWindowFlags_NoResize
|
||||||
|
| ImGuiWindowFlags_NoNavFocus
|
||||||
|
| ImGuiWindowFlags_NoNavInputs
|
||||||
|
| ImGuiWindowFlags_NoDocking;
|
||||||
|
|
||||||
|
if (IIDX_SEGMENT_FONT_COLOR.has_value()) {
|
||||||
|
const auto rgb = IIDX_SEGMENT_FONT_COLOR.value();
|
||||||
|
const auto rgba = IM_COL32_A_MASK | rgb;
|
||||||
|
this->color = ImGui::ColorConvertU32ToFloat4(rgba);
|
||||||
|
} else {
|
||||||
|
this->color = RED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IIDXSegmentDisplay::calculate_initial_window() {
|
||||||
|
// ImGui::CalcTextSize doesn't seem to work here, so manually calculate
|
||||||
|
ImGui::PushFont(DSEG_FONT);
|
||||||
|
this->init_size = ImGui::CalcTextSize("~.~.~.~.~.~.~.~.~.");
|
||||||
|
this->init_size.x += PADDING_X * 2;
|
||||||
|
this->init_size.y += PADDING_Y * 2;
|
||||||
|
ImGui::PopFont();
|
||||||
|
|
||||||
|
// initial horizontal position
|
||||||
|
if (IIDX_SEGMENT_LOCATION.find("left") != std::string::npos) {
|
||||||
|
this->init_pos.x = 0;
|
||||||
|
} else if (IIDX_SEGMENT_LOCATION.find("right") != std::string::npos) {
|
||||||
|
this->init_pos.x = ImGui::GetIO().DisplaySize.x - this->init_size.x;
|
||||||
|
} else {
|
||||||
|
// center
|
||||||
|
this->init_pos.x = ImGui::GetIO().DisplaySize.x / 2 - this->init_size.x / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// initial vertical position
|
||||||
|
if (IIDX_SEGMENT_LOCATION.rfind("top", 0) == 0) {
|
||||||
|
this->init_pos.y = 0;
|
||||||
|
} else {
|
||||||
|
// bottom
|
||||||
|
this->init_pos.y = ImGui::GetIO().DisplaySize.y - this->init_size.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IIDXSegmentDisplay::build_content() {
|
||||||
|
char input_ticker[TICKER_SIZE];
|
||||||
|
|
||||||
|
// get ticker content from game
|
||||||
|
games::iidx::IIDX_LED_TICKER_LOCK.lock();
|
||||||
|
memcpy(input_ticker, games::iidx::IIDXIO_LED_TICKER, TICKER_SIZE);
|
||||||
|
games::iidx::IIDX_LED_TICKER_LOCK.unlock();
|
||||||
|
|
||||||
|
// since every input character can result in up to two characters,
|
||||||
|
// need double size for output
|
||||||
|
const size_t TICKER_OUTPUT_SIZE = TICKER_SIZE * 2 + 1;
|
||||||
|
char output_ticker[TICKER_OUTPUT_SIZE];
|
||||||
|
int output_ticker_index = 0;
|
||||||
|
|
||||||
|
// look at each input char and convert into output char(s)
|
||||||
|
for (const auto c : input_ticker) {
|
||||||
|
// see if there is an applicable rule for this input character
|
||||||
|
if (0 == CHARMAP.count(c)) {
|
||||||
|
// if not, copy the first character and keep going
|
||||||
|
output_ticker[output_ticker_index] = c;
|
||||||
|
output_ticker_index += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// there is a replacement rule for this input character
|
||||||
|
auto replacements = CHARMAP.at(c);
|
||||||
|
|
||||||
|
// replace the first character...
|
||||||
|
output_ticker[output_ticker_index] = replacements.first;
|
||||||
|
output_ticker_index += 1;
|
||||||
|
|
||||||
|
// and optionally add the second character
|
||||||
|
if (replacements.second != '\0') {
|
||||||
|
output_ticker[output_ticker_index] = replacements.second;
|
||||||
|
output_ticker_index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((int)TICKER_OUTPUT_SIZE <= output_ticker_index) {
|
||||||
|
log_fatal("iidx_seg", "{} is beyond array bounds", output_ticker_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// terminating null...
|
||||||
|
output_ticker[output_ticker_index] = '\0';
|
||||||
|
|
||||||
|
// finally, draw UI elements
|
||||||
|
draw_ticker(output_ticker);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IIDXSegmentDisplay::draw_ticker(char *ticker_string) {
|
||||||
|
ImGui::PushFont(DSEG_FONT);
|
||||||
|
|
||||||
|
const auto pos = ImVec2(PADDING_X, PADDING_Y);
|
||||||
|
|
||||||
|
// to imitate LED "off"
|
||||||
|
ImGui::SetCursorPos(pos);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, DARK_GRAY);
|
||||||
|
ImGui::TextUnformatted("~.~.~.~.~.~.~.~.~.");
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
|
||||||
|
// ... then draw the LED "on" above it
|
||||||
|
ImGui::SetCursorPos(pos);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, this->color);
|
||||||
|
ImGui::TextUnformatted(ticker_string);
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
|
||||||
|
ImGui::PopFont();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include "overlay/window.h"
|
||||||
|
|
||||||
|
namespace overlay::windows {
|
||||||
|
|
||||||
|
extern uint32_t IIDX_SEGMENT_FONT_SIZE;
|
||||||
|
extern std::optional<uint32_t> IIDX_SEGMENT_FONT_COLOR;
|
||||||
|
extern std::string IIDX_SEGMENT_LOCATION;
|
||||||
|
|
||||||
|
class IIDXSegmentDisplay : public Window {
|
||||||
|
public:
|
||||||
|
IIDXSegmentDisplay(SpiceOverlay *overlay);
|
||||||
|
void calculate_initial_window() override;
|
||||||
|
void build_content() override;
|
||||||
|
private:
|
||||||
|
ImVec4 color;
|
||||||
|
void draw_ticker(char *ticker_string);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,107 +1,43 @@
|
|||||||
#undef CINTERFACE
|
#undef CINTERFACE
|
||||||
|
|
||||||
#include "iidx_sub.h"
|
#include "iidx_sub.h"
|
||||||
|
#include "games/iidx/iidx.h"
|
||||||
#include <fmt/format.h>
|
|
||||||
|
|
||||||
#include "games/io.h"
|
|
||||||
#include "hooks/graphics/backends/d3d9/d3d9_backend.h"
|
|
||||||
#include "hooks/graphics/backends/d3d9/d3d9_device.h"
|
|
||||||
#include "util/logging.h"
|
|
||||||
|
|
||||||
namespace overlay::windows {
|
namespace overlay::windows {
|
||||||
|
|
||||||
const ImVec4 YELLOW(1.f, 1.f, 0.f, 1.f);
|
IIDXSubScreen::IIDXSubScreen(SpiceOverlay *overlay) : GenericSubScreen(overlay) {
|
||||||
const ImVec4 WHITE(1.f, 1.f, 1.f, 1.f);
|
this->title = "IIDX Sub Screen";
|
||||||
|
|
||||||
IIDXSubScreen::IIDXSubScreen(SpiceOverlay *overlay) : Window(overlay), device(overlay->get_device()) {
|
float size = 0.5f;
|
||||||
|
if (games::iidx::SUBSCREEN_OVERLAY_SIZE.has_value()) {
|
||||||
|
if (games::iidx::SUBSCREEN_OVERLAY_SIZE.value() == "large") {
|
||||||
|
size = 0.8f;
|
||||||
|
} else if (games::iidx::SUBSCREEN_OVERLAY_SIZE.value() == "small") {
|
||||||
|
size = 0.3f;
|
||||||
|
} else if (games::iidx::SUBSCREEN_OVERLAY_SIZE.value() == "fullscreen") {
|
||||||
this->draws_window = false;
|
this->draws_window = false;
|
||||||
this->title = "Sub Screen";
|
}
|
||||||
this->toggle_button = games::OverlayButtons::ToggleSubScreen;
|
}
|
||||||
|
this->init_size = ImVec2(
|
||||||
|
ImGui::GetIO().DisplaySize.x * size,
|
||||||
|
(ImGui::GetIO().DisplaySize.x * size * 9 / 16) + ImGui::GetFrameHeight());
|
||||||
|
|
||||||
this->texture_size = ImVec2(0, 0);
|
this->size_max = ImVec2(
|
||||||
|
ImGui::GetIO().DisplaySize.x - ImGui::GetFrameHeight() * 2,
|
||||||
|
ImGui::GetIO().DisplaySize.y - ImGui::GetFrameHeight() * 2);
|
||||||
|
|
||||||
|
// middle / bottom
|
||||||
|
this->init_pos = ImVec2(
|
||||||
|
ImGui::GetIO().DisplaySize.x / 2 - this->init_size.x / 2,
|
||||||
|
ImGui::GetIO().DisplaySize.y - this->init_size.y - (ImGui::GetFrameHeight() / 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
void IIDXSubScreen::build_content() {
|
void IIDXSubScreen::touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) {
|
||||||
this->draw_texture();
|
if (!this->get_active()) {
|
||||||
|
|
||||||
/*
|
|
||||||
if (this->status_message.has_value()) {
|
|
||||||
ImGui::TextColored(YELLOW, "%s", this->status_message.value().c_str());
|
|
||||||
} else if (this->texture) {
|
|
||||||
ImGui::TextColored(WHITE, "Successfully acquired surface texture");
|
|
||||||
} else {
|
|
||||||
ImGui::TextColored(YELLOW, "Failed to acquire surface texture");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IIDXSubScreen::build_texture(IDirect3DSurface9 *surface) {
|
|
||||||
HRESULT hr;
|
|
||||||
|
|
||||||
D3DSURFACE_DESC desc {};
|
|
||||||
hr = surface->GetDesc(&desc);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
this->status_message = fmt::format("Failed to get surface descriptor, hr={}", FMT_HRESULT(hr));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = this->device->CreateTexture(desc.Width, desc.Height, 0, desc.Usage, desc.Format,
|
|
||||||
desc.Pool, &this->texture, nullptr);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
this->status_message = fmt::format("Failed to create render target, hr={}", FMT_HRESULT(hr));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->texture_size = ImVec2(desc.Width, desc.Height);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IIDXSubScreen::draw_texture() {
|
|
||||||
HRESULT hr;
|
|
||||||
|
|
||||||
auto surface = graphics_d3d9_ldj_get_sub_screen();
|
|
||||||
if (surface == nullptr) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->texture == nullptr) {
|
*x_out = xy_in.x * ImGui::GetIO().DisplaySize.x;
|
||||||
if (!this->build_texture(surface)) {
|
*y_out = xy_in.y * ImGui::GetIO().DisplaySize.y;
|
||||||
this->texture = nullptr;
|
|
||||||
this->texture_size = ImVec2(0, 0);
|
|
||||||
|
|
||||||
surface->Release();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IDirect3DSurface9 *texture_surface = nullptr;
|
|
||||||
hr = this->texture->GetSurfaceLevel(0, &texture_surface);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
this->status_message = fmt::format("Failed to get texture surface, hr={}", FMT_HRESULT(hr));
|
|
||||||
|
|
||||||
surface->Release();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = this->device->StretchRect(surface, nullptr, texture_surface, nullptr, D3DTEXF_NONE);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
this->status_message = fmt::format("Failed to copy back buffer contents, hr={}", FMT_HRESULT(hr));
|
|
||||||
|
|
||||||
surface->Release();
|
|
||||||
texture_surface->Release();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
surface->Release();
|
|
||||||
texture_surface->Release();
|
|
||||||
|
|
||||||
ImGui::GetBackgroundDrawList()->AddImage(
|
|
||||||
reinterpret_cast<void *>(this->texture),
|
|
||||||
ImVec2(0, 0),
|
|
||||||
this->texture_size,
|
|
||||||
ImVec2(0, 0),
|
|
||||||
ImVec2(1, 1));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,17 @@
|
|||||||
#ifndef SPICETOOLS_OVERLAY_WINDOWS_IIDX_SUB_H
|
#ifndef SPICETOOLS_OVERLAY_WINDOWS_IIDX_SUB_H
|
||||||
#define SPICETOOLS_OVERLAY_WINDOWS_IIDX_SUB_H
|
#define SPICETOOLS_OVERLAY_WINDOWS_IIDX_SUB_H
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
#include <d3d9.h>
|
|
||||||
|
|
||||||
#include "overlay/window.h"
|
#include "overlay/window.h"
|
||||||
|
#include "overlay/windows/generic_sub.h"
|
||||||
|
|
||||||
namespace overlay::windows {
|
namespace overlay::windows {
|
||||||
|
|
||||||
class IIDXSubScreen : public Window {
|
class IIDXSubScreen : public GenericSubScreen {
|
||||||
public:
|
public:
|
||||||
IIDXSubScreen(SpiceOverlay *overlay);
|
IIDXSubScreen(SpiceOverlay *overlay);
|
||||||
|
|
||||||
void build_content() override;
|
protected:
|
||||||
|
void touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) override;
|
||||||
private:
|
|
||||||
bool build_texture(IDirect3DSurface9 *surface);
|
|
||||||
void draw_texture();
|
|
||||||
|
|
||||||
std::optional<std::string> status_message = std::nullopt;
|
|
||||||
|
|
||||||
IDirect3DDevice9 *device = nullptr;
|
|
||||||
IDirect3DTexture9 *texture = nullptr;
|
|
||||||
ImVec2 texture_size;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
#include "iopanel.h"
|
||||||
|
#include "cfg/api.h"
|
||||||
|
#include "launcher/launcher.h"
|
||||||
|
#include "games/io.h"
|
||||||
|
#include "misc/eamuse.h"
|
||||||
|
|
||||||
|
namespace overlay::windows {
|
||||||
|
|
||||||
|
IOPanel::IOPanel(SpiceOverlay *overlay) : Window(overlay) {
|
||||||
|
this->title = "Operator";
|
||||||
|
this->flags = ImGuiWindowFlags_AlwaysAutoResize;
|
||||||
|
|
||||||
|
this->init_size = ImVec2(
|
||||||
|
80,
|
||||||
|
ImGui::GetFrameHeight() +
|
||||||
|
ImGui::GetStyle().WindowPadding.y * 2 +
|
||||||
|
ImGui::GetFrameHeightWithSpacing() * 3 +
|
||||||
|
ImGui::GetFrameHeight()); // title + windows padding + 4 buttons
|
||||||
|
|
||||||
|
this->init_pos =ImVec2(10, ImGui::GetIO().DisplaySize.y - this->init_size.y - 10);
|
||||||
|
this->toggle_button = games::OverlayButtons::ToggleIOPanel;
|
||||||
|
this->find_buttons();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IOPanel::find_buttons() {
|
||||||
|
const auto buttons = games::get_buttons(eamuse_get_game());
|
||||||
|
for (auto &button : *buttons) {
|
||||||
|
// since the array indices are different for each game, use string match instead
|
||||||
|
if (button.getName() == "Service") {
|
||||||
|
this->service_button = &button;
|
||||||
|
} else if (button.getName() == "Test") {
|
||||||
|
this->test_button = &button;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float IOPanel::get_suggested_height() {
|
||||||
|
// height of 4 buttons stacked vertically
|
||||||
|
// (coin, unlock, service, test, plus spacing between them)
|
||||||
|
return ImGui::GetFrameHeightWithSpacing() * 3 + ImGui::GetFrameHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IOPanel::build_content() {
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.2f, 0.1f, 0.1f, 1.f));
|
||||||
|
ImGui::PushID(this);
|
||||||
|
|
||||||
|
ImGui::BeginGroup();
|
||||||
|
this->build_operator_menu();
|
||||||
|
ImGui::EndGroup();
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
this->build_io_panel();
|
||||||
|
|
||||||
|
ImGui::PopID();
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IOPanel::build_io_panel() {}
|
||||||
|
|
||||||
|
void IOPanel::build_operator_menu() {
|
||||||
|
const ImVec2 wide(60, 0);
|
||||||
|
const float spacing_x = ImGui::GetStyle().ItemSpacing.y;
|
||||||
|
const ImVec2 tall(
|
||||||
|
ImGui::GetFrameHeight(),
|
||||||
|
ImGui::GetFrameHeightWithSpacing() + ImGui::GetFrameHeight());
|
||||||
|
|
||||||
|
if (ImGui::Button("COIN", ImVec2(wide.x + spacing_x + tall.x, wide.y))) {
|
||||||
|
eamuse_coin_add();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Checkbox("unlock", &operator_unlocked);
|
||||||
|
ImGui::BeginDisabled(!operator_unlocked);
|
||||||
|
|
||||||
|
this->build_button("SERVICE", wide, this->service_button);
|
||||||
|
this->build_button("TEST", wide, this->test_button);
|
||||||
|
|
||||||
|
// service + test button (for test menu navigation)
|
||||||
|
ImGui::SameLine(0, spacing_x);
|
||||||
|
ImGui::BeginGroup();
|
||||||
|
{
|
||||||
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() - ImGui::GetFrameHeightWithSpacing());
|
||||||
|
this->build_button("+", tall, this->test_button, this->service_button);
|
||||||
|
}
|
||||||
|
ImGui::EndGroup();
|
||||||
|
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IOPanel::build_button(
|
||||||
|
const char *label, const ImVec2 &size, Button *button, Button *button_alt, Light *light) {
|
||||||
|
|
||||||
|
ImGui::BeginDisabled(button == nullptr);
|
||||||
|
|
||||||
|
int pushed = 0;
|
||||||
|
if (light && 0.5f < GameAPI::Lights::readLight(RI_MGR, *light)) {
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.72f, 0.15f, 0.00f, 0.9f));
|
||||||
|
pushed += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Button(label, size);
|
||||||
|
|
||||||
|
if (ImGui::IsItemActivated()) {
|
||||||
|
button->override_state = GameAPI::Buttons::BUTTON_PRESSED;
|
||||||
|
button->override_velocity = 1.f;
|
||||||
|
button->override_enabled = true;
|
||||||
|
if (button_alt) {
|
||||||
|
button_alt->override_state = GameAPI::Buttons::BUTTON_PRESSED;
|
||||||
|
button_alt->override_velocity = 1.f;
|
||||||
|
button_alt->override_enabled = true;
|
||||||
|
}
|
||||||
|
} else if (ImGui::IsItemDeactivated()) {
|
||||||
|
button->override_enabled = false;
|
||||||
|
if (button_alt) {
|
||||||
|
button_alt->override_enabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PopStyleColor(pushed);
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "overlay/window.h"
|
||||||
|
#include "cfg/button.h"
|
||||||
|
|
||||||
|
namespace overlay::windows {
|
||||||
|
|
||||||
|
class IOPanel : public Window {
|
||||||
|
public:
|
||||||
|
IOPanel(SpiceOverlay *overlay);
|
||||||
|
void build_content() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void build_io_panel();
|
||||||
|
void build_button(
|
||||||
|
const char *label,
|
||||||
|
const ImVec2 &size,
|
||||||
|
Button *button,
|
||||||
|
Button *button_alt = nullptr,
|
||||||
|
Light *light = nullptr);
|
||||||
|
|
||||||
|
float get_suggested_height();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void find_buttons();
|
||||||
|
void build_operator_menu();
|
||||||
|
|
||||||
|
bool operator_unlocked = false;
|
||||||
|
Button *test_button = nullptr;
|
||||||
|
Button *service_button = nullptr;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
#include "iopanel_ddr.h"
|
||||||
|
#include "games/io.h"
|
||||||
|
#include "games/ddr/io.h"
|
||||||
|
#include "misc/eamuse.h"
|
||||||
|
#include "util/logging.h"
|
||||||
|
|
||||||
|
namespace overlay::windows {
|
||||||
|
|
||||||
|
DDRIOPanel::DDRIOPanel(SpiceOverlay *overlay) : IOPanel(overlay) {
|
||||||
|
this->title = "DDR IO Panel";
|
||||||
|
find_ddr_buttons();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DDRIOPanel::find_ddr_buttons() {
|
||||||
|
const auto buttons = games::get_buttons(eamuse_get_game());
|
||||||
|
const auto lights = games::get_lights(eamuse_get_game());
|
||||||
|
|
||||||
|
// SD cabs don't have lights for these buttons, so just use the HD ones
|
||||||
|
|
||||||
|
this->start[0] = &(*buttons)[games::ddr::Buttons::P1_START];
|
||||||
|
this->up[0] = &(*buttons)[games::ddr::Buttons::P1_MENU_UP];
|
||||||
|
this->down[0] = &(*buttons)[games::ddr::Buttons::P1_MENU_DOWN];
|
||||||
|
this->left[0] = &(*buttons)[games::ddr::Buttons::P1_MENU_LEFT];
|
||||||
|
this->right[0] = &(*buttons)[games::ddr::Buttons::P1_MENU_RIGHT];
|
||||||
|
this->start_light[0] = &(*lights)[games::ddr::Lights::HD_P1_START];
|
||||||
|
this->updown_light[0] = &(*lights)[games::ddr::Lights::HD_P1_UP_DOWN];
|
||||||
|
this->leftright_light[0] = &(*lights)[games::ddr::Lights::HD_P1_LEFT_RIGHT];
|
||||||
|
|
||||||
|
this->start[1] = &(*buttons)[games::ddr::Buttons::P2_START];
|
||||||
|
this->up[1] = &(*buttons)[games::ddr::Buttons::P2_MENU_UP];
|
||||||
|
this->down[1] = &(*buttons)[games::ddr::Buttons::P2_MENU_DOWN];
|
||||||
|
this->left[1] = &(*buttons)[games::ddr::Buttons::P2_MENU_LEFT];
|
||||||
|
this->right[1] = &(*buttons)[games::ddr::Buttons::P2_MENU_RIGHT];
|
||||||
|
this->start_light[1] = &(*lights)[games::ddr::Lights::HD_P2_START];
|
||||||
|
this->updown_light[1] = &(*lights)[games::ddr::Lights::HD_P2_UP_DOWN];
|
||||||
|
this->leftright_light[1] = &(*lights)[games::ddr::Lights::HD_P2_LEFT_RIGHT];
|
||||||
|
}
|
||||||
|
|
||||||
|
void DDRIOPanel::build_io_panel() {
|
||||||
|
ImGui::Dummy(ImVec2(12, 0));
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
this->draw_buttons(0);
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::Dummy(ImVec2(12, 0));
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
this->draw_buttons(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DDRIOPanel::draw_buttons(const int p) {
|
||||||
|
// 2x2
|
||||||
|
const ImVec2 start_button_size(
|
||||||
|
ImGui::GetFrameHeightWithSpacing() + ImGui::GetFrameHeight(),
|
||||||
|
ImGui::GetFrameHeightWithSpacing() + ImGui::GetFrameHeight()
|
||||||
|
);
|
||||||
|
// 2x1
|
||||||
|
const ImVec2 updown_size(start_button_size.x, ImGui::GetFrameHeight());
|
||||||
|
// 1x2
|
||||||
|
const ImVec2 leftright_size(ImGui::GetFrameHeight(), start_button_size.y);
|
||||||
|
|
||||||
|
ImGui::BeginGroup();
|
||||||
|
{
|
||||||
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetFrameHeightWithSpacing());
|
||||||
|
this->build_button("<", leftright_size, this->left[p], nullptr, this->leftright_light[p]);
|
||||||
|
}
|
||||||
|
ImGui::EndGroup();
|
||||||
|
|
||||||
|
ImGui::SameLine(0, ImGui::GetStyle().ItemSpacing.y);
|
||||||
|
|
||||||
|
ImGui::BeginGroup();
|
||||||
|
{
|
||||||
|
this->build_button("^", updown_size, this->up[p], nullptr, this->updown_light[p]);
|
||||||
|
const char *label;
|
||||||
|
if (p == 0) {
|
||||||
|
label = " 1 P\nStart";
|
||||||
|
} else {
|
||||||
|
label = " 2 P\nStart";
|
||||||
|
}
|
||||||
|
this->build_button(label, start_button_size, this->start[p], nullptr, this->start_light[p]);
|
||||||
|
this->build_button("v", updown_size, this->down[p], nullptr, this->updown_light[p]);
|
||||||
|
}
|
||||||
|
ImGui::EndGroup();
|
||||||
|
|
||||||
|
ImGui::SameLine(0, ImGui::GetStyle().ItemSpacing.y);
|
||||||
|
|
||||||
|
ImGui::BeginGroup();
|
||||||
|
{
|
||||||
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetFrameHeightWithSpacing());
|
||||||
|
this->build_button(">", leftright_size, this->right[p], nullptr, this->leftright_light[p]);
|
||||||
|
}
|
||||||
|
ImGui::EndGroup();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "overlay/window.h"
|
||||||
|
#include "overlay/windows/iopanel.h"
|
||||||
|
#include "cfg/button.h"
|
||||||
|
|
||||||
|
namespace overlay::windows {
|
||||||
|
|
||||||
|
class DDRIOPanel : public IOPanel {
|
||||||
|
public:
|
||||||
|
DDRIOPanel(SpiceOverlay *overlay);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void build_io_panel() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void find_ddr_buttons();
|
||||||
|
void draw_buttons(const int player);
|
||||||
|
|
||||||
|
Button *start[2];
|
||||||
|
Button *up[2];
|
||||||
|
Button *down[2];
|
||||||
|
Button *left[2];
|
||||||
|
Button *right[2];
|
||||||
|
Light *start_light[2];
|
||||||
|
Light *updown_light[2];
|
||||||
|
Light *leftright_light[2];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
#include "iopanel_gfdm.h"
|
||||||
|
#include "avs/game.h"
|
||||||
|
#include "games/io.h"
|
||||||
|
#include "games/gitadora/gitadora.h"
|
||||||
|
#include "games/gitadora/io.h"
|
||||||
|
#include "misc/eamuse.h"
|
||||||
|
#include "util/logging.h"
|
||||||
|
|
||||||
|
namespace overlay::windows {
|
||||||
|
|
||||||
|
GitadoraIOPanel::GitadoraIOPanel(SpiceOverlay *overlay) : IOPanel(overlay) {
|
||||||
|
this->title = "GITADORA IO Panel";
|
||||||
|
|
||||||
|
// by default, make a safer assumption that there are two players
|
||||||
|
this->two_players = true;
|
||||||
|
// by default, enable the extra input only available on DX cabs...
|
||||||
|
this->has_guitar_knobs = true;
|
||||||
|
|
||||||
|
// drummania can only have one player, no guitar knobs
|
||||||
|
if (avs::game::is_model({ "J32", "K32", "L32" }) ||
|
||||||
|
(avs::game::is_model("M32") && avs::game::SPEC[0] == 'B')) {
|
||||||
|
this->two_players = false;
|
||||||
|
this->has_guitar_knobs = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// and cab type 3 (white cab) only has one guitar
|
||||||
|
if (games::gitadora::CAB_TYPE.has_value() && games::gitadora::CAB_TYPE == 3) {
|
||||||
|
this->two_players = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// disable guitar knobs on known non-DX cabs
|
||||||
|
if (games::gitadora::CAB_TYPE.has_value() &&
|
||||||
|
(games::gitadora::CAB_TYPE == 2 || games::gitadora::CAB_TYPE == 3)) {
|
||||||
|
this->has_guitar_knobs = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
find_gfdm_buttons();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitadoraIOPanel::find_gfdm_buttons() {
|
||||||
|
const auto buttons = games::get_buttons(eamuse_get_game());
|
||||||
|
|
||||||
|
// device emulation treats drum controls to be the same as guitar 1p
|
||||||
|
|
||||||
|
this->start[0] = &(*buttons)[games::gitadora::Buttons::GuitarP1Start];
|
||||||
|
this->help[0] = &(*buttons)[games::gitadora::Buttons::GuitarP1Help];
|
||||||
|
this->up[0] = &(*buttons)[games::gitadora::Buttons::GuitarP1Up];
|
||||||
|
this->down[0] = &(*buttons)[games::gitadora::Buttons::GuitarP1Down];
|
||||||
|
this->left[0] = &(*buttons)[games::gitadora::Buttons::GuitarP1Left];
|
||||||
|
this->right[0] = &(*buttons)[games::gitadora::Buttons::GuitarP1Right];
|
||||||
|
|
||||||
|
this->start[1] = &(*buttons)[games::gitadora::Buttons::GuitarP2Start];
|
||||||
|
this->help[1] = &(*buttons)[games::gitadora::Buttons::GuitarP2Help];
|
||||||
|
this->up[1] = &(*buttons)[games::gitadora::Buttons::GuitarP2Up];
|
||||||
|
this->down[1] = &(*buttons)[games::gitadora::Buttons::GuitarP2Down];
|
||||||
|
this->left[1] = &(*buttons)[games::gitadora::Buttons::GuitarP2Left];
|
||||||
|
this->right[1] = &(*buttons)[games::gitadora::Buttons::GuitarP2Right];
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitadoraIOPanel::build_io_panel() {
|
||||||
|
ImGui::Dummy(ImVec2(12, 0));
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
this->draw_buttons(0);
|
||||||
|
if (this->has_guitar_knobs) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
this->draw_sliders(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw p2 only if guitar freaks
|
||||||
|
if (this->two_players) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::Dummy(ImVec2(12, 0));
|
||||||
|
ImGui::SameLine();
|
||||||
|
this->draw_buttons(1);
|
||||||
|
if (this->has_guitar_knobs) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
this->draw_sliders(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitadoraIOPanel::draw_buttons(const int p) {
|
||||||
|
// 2x2
|
||||||
|
const ImVec2 start_button_size(
|
||||||
|
ImGui::GetFrameHeightWithSpacing() + ImGui::GetFrameHeight(),
|
||||||
|
ImGui::GetFrameHeightWithSpacing() + ImGui::GetFrameHeight()
|
||||||
|
);
|
||||||
|
// 2x1
|
||||||
|
const ImVec2 updown_size(start_button_size.x, ImGui::GetFrameHeight());
|
||||||
|
// 1x2
|
||||||
|
const ImVec2 leftright_size(ImGui::GetFrameHeight(), start_button_size.y);
|
||||||
|
// 1x1
|
||||||
|
const ImVec2 tiny_size(ImGui::GetFrameHeight(), ImGui::GetFrameHeight());
|
||||||
|
|
||||||
|
ImGui::BeginGroup();
|
||||||
|
{
|
||||||
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetFrameHeightWithSpacing());
|
||||||
|
this->build_button("<", leftright_size, this->left[p]);
|
||||||
|
}
|
||||||
|
ImGui::EndGroup();
|
||||||
|
|
||||||
|
ImGui::SameLine(0, ImGui::GetStyle().ItemSpacing.y);
|
||||||
|
|
||||||
|
ImGui::BeginGroup();
|
||||||
|
{
|
||||||
|
this->build_button("^", updown_size, this->up[p]);
|
||||||
|
const char *label;
|
||||||
|
if (this->two_players) {
|
||||||
|
if (p == 0) {
|
||||||
|
label = " 1 P\nStart";
|
||||||
|
} else {
|
||||||
|
label = " 2 P\nStart";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
label = "Start";
|
||||||
|
}
|
||||||
|
this->build_button(label, start_button_size, this->start[p]);
|
||||||
|
this->build_button("v", updown_size, this->down[p]);
|
||||||
|
}
|
||||||
|
ImGui::EndGroup();
|
||||||
|
|
||||||
|
ImGui::SameLine(0, ImGui::GetStyle().ItemSpacing.y);
|
||||||
|
|
||||||
|
ImGui::BeginGroup();
|
||||||
|
{
|
||||||
|
this->build_button("?", tiny_size, this->help[p]);
|
||||||
|
this->build_button(">", leftright_size, this->right[p]);
|
||||||
|
}
|
||||||
|
ImGui::EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitadoraIOPanel::draw_sliders(const int p) {
|
||||||
|
const auto index =
|
||||||
|
(p == 0) ?
|
||||||
|
games::gitadora::Analogs::GuitarP1Knob :
|
||||||
|
games::gitadora::Analogs::GuitarP2Knob;
|
||||||
|
|
||||||
|
const ImVec2 slider_size(32, get_suggested_height());
|
||||||
|
|
||||||
|
// get analog
|
||||||
|
const auto analogs = games::get_analogs(eamuse_get_game());
|
||||||
|
auto &analog = (*analogs)[index];
|
||||||
|
|
||||||
|
// vertical slider
|
||||||
|
auto new_state = analog.override_enabled ? analog.override_state
|
||||||
|
: GameAPI::Analogs::getState(RI_MGR, analog);
|
||||||
|
|
||||||
|
ImGui::PushID(index);
|
||||||
|
ImGui::VSliderFloat(
|
||||||
|
"##v",
|
||||||
|
slider_size,
|
||||||
|
&new_state,
|
||||||
|
0.f, 1.f,
|
||||||
|
"Knob",
|
||||||
|
ImGuiSliderFlags_AlwaysClamp);
|
||||||
|
ImGui::PopID();
|
||||||
|
if (new_state != analog.override_state) {
|
||||||
|
analog.override_state = new_state;
|
||||||
|
analog.override_enabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "overlay/window.h"
|
||||||
|
#include "overlay/windows/iopanel.h"
|
||||||
|
#include "cfg/button.h"
|
||||||
|
|
||||||
|
namespace overlay::windows {
|
||||||
|
|
||||||
|
class GitadoraIOPanel : public IOPanel {
|
||||||
|
public:
|
||||||
|
GitadoraIOPanel(SpiceOverlay *overlay);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void build_io_panel() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void find_gfdm_buttons();
|
||||||
|
void draw_buttons(const int player);
|
||||||
|
void draw_sliders(const int player);
|
||||||
|
|
||||||
|
bool two_players;
|
||||||
|
bool has_guitar_knobs;
|
||||||
|
|
||||||
|
Button *start[2];
|
||||||
|
Button *help[2];
|
||||||
|
Button *up[2];
|
||||||
|
Button *down[2];
|
||||||
|
Button *left[2];
|
||||||
|
Button *right[2];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
#include <map>
|
||||||
|
#include "iopanel_iidx.h"
|
||||||
|
#include "games/io.h"
|
||||||
|
#include "games/iidx/iidx.h"
|
||||||
|
#include "games/iidx/io.h"
|
||||||
|
#include "misc/eamuse.h"
|
||||||
|
#include "util/logging.h"
|
||||||
|
|
||||||
|
namespace overlay::windows {
|
||||||
|
|
||||||
|
IIDXIOPanel::IIDXIOPanel(SpiceOverlay *overlay) : IOPanel(overlay) {
|
||||||
|
this->title = "IIDX IO Panel";
|
||||||
|
find_iidx_buttons();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IIDXIOPanel::find_iidx_buttons() {
|
||||||
|
const auto buttons = games::get_buttons(eamuse_get_game());
|
||||||
|
const auto lights = games::get_lights(eamuse_get_game());
|
||||||
|
|
||||||
|
this->start_1p = &(*buttons)[games::iidx::Buttons::P1_Start];
|
||||||
|
this->start_1p_light = &(*lights)[games::iidx::Lights::P1_Start];
|
||||||
|
|
||||||
|
this->start_2p = &(*buttons)[games::iidx::Buttons::P2_Start];
|
||||||
|
this->start_2p_light = &(*lights)[games::iidx::Lights::P2_Start];
|
||||||
|
|
||||||
|
this->vefx = &(*buttons)[games::iidx::Buttons::VEFX];
|
||||||
|
this->vefx_light = &(*lights)[games::iidx::Lights::VEFX];
|
||||||
|
|
||||||
|
this->effect_on = &(*buttons)[games::iidx::Buttons::Effect];
|
||||||
|
this->effect_on_light = &(*lights)[games::iidx::Lights::Effect];
|
||||||
|
}
|
||||||
|
|
||||||
|
void IIDXIOPanel::build_io_panel() {
|
||||||
|
ImGui::Dummy(ImVec2(12, 0));
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
this->draw_buttons();
|
||||||
|
|
||||||
|
if (!games::iidx::TDJ_MODE) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::Dummy(ImVec2(12, 0));
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
this->draw_sliders();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IIDXIOPanel::draw_buttons() {
|
||||||
|
const auto big_button_size = ImVec2(get_suggested_height(), get_suggested_height());
|
||||||
|
|
||||||
|
// width = same as above,
|
||||||
|
// height = 2 rows of buttons
|
||||||
|
const auto mid_button_size = ImVec2(
|
||||||
|
big_button_size.x,
|
||||||
|
ImGui::GetFrameHeight() * 2 + ImGui::GetStyle().ItemSpacing.y
|
||||||
|
);
|
||||||
|
|
||||||
|
// width = half of above,
|
||||||
|
// height = 4 rows of buttons
|
||||||
|
const auto tall_button_size = ImVec2(
|
||||||
|
big_button_size.x / 2,
|
||||||
|
big_button_size.y
|
||||||
|
);
|
||||||
|
|
||||||
|
this->build_button(
|
||||||
|
" 1 P\n"
|
||||||
|
"Start",
|
||||||
|
big_button_size,
|
||||||
|
this->start_1p, nullptr,
|
||||||
|
this->start_1p_light);
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::BeginGroup();
|
||||||
|
this->build_button(
|
||||||
|
"EFFECT",
|
||||||
|
mid_button_size,
|
||||||
|
this->effect_on, nullptr,
|
||||||
|
this->effect_on_light);
|
||||||
|
this->build_button(
|
||||||
|
"VEFX",
|
||||||
|
mid_button_size,
|
||||||
|
this->vefx, nullptr,
|
||||||
|
this->vefx_light);
|
||||||
|
ImGui::EndGroup();
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
this->build_button(
|
||||||
|
"EFF\n"
|
||||||
|
" +\n"
|
||||||
|
"VFX",
|
||||||
|
tall_button_size,
|
||||||
|
this->effect_on,
|
||||||
|
this->vefx);
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
this->build_button(
|
||||||
|
" 2 P\n"
|
||||||
|
"Start",
|
||||||
|
big_button_size,
|
||||||
|
this->start_2p, nullptr,
|
||||||
|
this->start_2p_light);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IIDXIOPanel::draw_sliders() {
|
||||||
|
// effector analog entries
|
||||||
|
static const std::map<size_t, const char *> ANALOG_ENTRIES {
|
||||||
|
{ games::iidx::Analogs::VEFX, "VEFX" },
|
||||||
|
{ games::iidx::Analogs::LowEQ, "LoEQ" },
|
||||||
|
{ games::iidx::Analogs::HiEQ, "HiEQ" },
|
||||||
|
{ games::iidx::Analogs::Filter, "Fltr" },
|
||||||
|
{ games::iidx::Analogs::PlayVolume, "Vol." },
|
||||||
|
};
|
||||||
|
|
||||||
|
const ImVec2 slider_size(32, get_suggested_height());
|
||||||
|
const auto reset_button_size = ImVec2(16, get_suggested_height());
|
||||||
|
|
||||||
|
// iterate analogs
|
||||||
|
bool overridden = false;
|
||||||
|
const auto analogs = games::get_analogs(eamuse_get_game());
|
||||||
|
for (auto &[index, name] : ANALOG_ENTRIES) {
|
||||||
|
|
||||||
|
// safety check
|
||||||
|
if (index >= analogs->size()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get analog
|
||||||
|
auto &analog = (*analogs)[index];
|
||||||
|
overridden |= analog.override_enabled;
|
||||||
|
|
||||||
|
// push id and style
|
||||||
|
ImGui::PushID((void *) name);
|
||||||
|
|
||||||
|
// vertical slider
|
||||||
|
auto new_state = analog.override_enabled ? analog.override_state
|
||||||
|
: GameAPI::Analogs::getState(RI_MGR, analog);
|
||||||
|
if (0 < index) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::VSliderFloat(
|
||||||
|
"##v",
|
||||||
|
slider_size,
|
||||||
|
&new_state,
|
||||||
|
0.f, 1.f,
|
||||||
|
name,
|
||||||
|
ImGuiSliderFlags_AlwaysClamp);
|
||||||
|
|
||||||
|
if (new_state != analog.override_state) {
|
||||||
|
analog.override_state = new_state;
|
||||||
|
analog.override_enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pop id and style
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset button
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("r\ne\ns\ne\nt", reset_button_size)) {
|
||||||
|
for (auto &[index, name] : ANALOG_ENTRIES) {
|
||||||
|
if (index < analogs->size()) {
|
||||||
|
(*analogs)[index].override_enabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "overlay/window.h"
|
||||||
|
#include "overlay/windows/iopanel.h"
|
||||||
|
#include "cfg/button.h"
|
||||||
|
|
||||||
|
namespace overlay::windows {
|
||||||
|
|
||||||
|
class IIDXIOPanel : public IOPanel {
|
||||||
|
public:
|
||||||
|
IIDXIOPanel(SpiceOverlay *overlay);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void build_io_panel() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void find_iidx_buttons();
|
||||||
|
void draw_buttons();
|
||||||
|
void draw_sliders();
|
||||||
|
|
||||||
|
Button *start_1p = nullptr;
|
||||||
|
Light *start_1p_light = nullptr;
|
||||||
|
|
||||||
|
Button *start_2p = nullptr;
|
||||||
|
Light *start_2p_light = nullptr;
|
||||||
|
|
||||||
|
Button *effect_on = nullptr;
|
||||||
|
Light *effect_on_light = nullptr;
|
||||||
|
|
||||||
|
Button *vefx = nullptr;
|
||||||
|
Light *vefx_light = nullptr;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -77,7 +77,7 @@ namespace overlay::windows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// mouse down handler
|
// mouse down handler
|
||||||
if (ImGui::IsItemHovered() && ImGui::IsAnyMouseDown()) {
|
if (ImGui::IsItemActive()) {
|
||||||
eamuse_set_keypad_overrides_overlay(this->unit, button.flag);
|
eamuse_set_keypad_overrides_overlay(this->unit, button.flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
#include <games/io.h>
|
#include <games/io.h>
|
||||||
|
|
||||||
#include "screen_resize.h"
|
#include "screen_resize.h"
|
||||||
|
#include "avs/game.h"
|
||||||
#include "cfg/screen_resize.h"
|
#include "cfg/screen_resize.h"
|
||||||
|
#include "hooks/graphics/graphics.h"
|
||||||
|
#include "overlay/imgui/extensions.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "util/utils.h"
|
||||||
|
|
||||||
namespace overlay::windows {
|
namespace overlay::windows {
|
||||||
|
|
||||||
ScreenResize::ScreenResize(SpiceOverlay *overlay) : Window(overlay) {
|
ScreenResize::ScreenResize(SpiceOverlay *overlay) : Window(overlay) {
|
||||||
this->title = "Screen Resize";
|
this->title = "Screen Resize";
|
||||||
this->flags = ImGuiWindowFlags_AlwaysAutoResize;
|
this->flags = ImGuiWindowFlags_AlwaysAutoResize;
|
||||||
this->init_pos = ImVec2(
|
this->init_pos = ImVec2(10, 10);
|
||||||
ImGui::GetIO().DisplaySize.x / 2 - this->init_size.x / 2,
|
|
||||||
ImGui::GetIO().DisplaySize.y / 2 - this->init_size.y / 2);
|
|
||||||
this->toggle_button = games::OverlayButtons::ToggleScreenResize;
|
this->toggle_button = games::OverlayButtons::ToggleScreenResize;
|
||||||
this->toggle_screen_resize = games::OverlayButtons::ScreenResize;
|
this->toggle_screen_resize = games::OverlayButtons::ScreenResize;
|
||||||
}
|
}
|
||||||
@@ -20,16 +22,86 @@ namespace overlay::windows {
|
|||||||
ScreenResize::~ScreenResize() {
|
ScreenResize::~ScreenResize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScreenResize::build_content() {
|
HWND ScreenResize::get_first_window() {
|
||||||
|
if (GRAPHICS_WINDOWS.size() == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return GRAPHICS_WINDOWS[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenResize::reset_window() {
|
||||||
|
this->reset_vars_to_default();
|
||||||
|
if (GRAPHICS_WINDOWED) {
|
||||||
|
const auto window = get_first_window();
|
||||||
|
if (window) {
|
||||||
|
graphics_move_resize_window(window);
|
||||||
|
graphics_update_window_style(window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenResize::reset_vars_to_default() {
|
||||||
|
cfg::SCREENRESIZE->enable_screen_resize = false;
|
||||||
|
cfg::SCREENRESIZE->enable_linear_filter = true;
|
||||||
|
cfg::SCREENRESIZE->keep_aspect_ratio = true;
|
||||||
|
cfg::SCREENRESIZE->centered = true;
|
||||||
|
cfg::SCREENRESIZE->offset_x = 0;
|
||||||
|
cfg::SCREENRESIZE->offset_y = 0;
|
||||||
|
cfg::SCREENRESIZE->scale_x = 1.f;
|
||||||
|
cfg::SCREENRESIZE->scale_y = 1.f;
|
||||||
|
|
||||||
|
cfg::SCREENRESIZE->enable_window_resize = false;
|
||||||
|
cfg::SCREENRESIZE->window_always_on_top = false;
|
||||||
|
cfg::SCREENRESIZE->window_decoration = cfg::WindowDecorationMode::Default;
|
||||||
|
cfg::SCREENRESIZE->window_offset_x = 0;
|
||||||
|
cfg::SCREENRESIZE->window_offset_y = 0;
|
||||||
|
cfg::SCREENRESIZE->client_keep_aspect_ratio = true;
|
||||||
|
cfg::SCREENRESIZE->client_width = cfg::SCREENRESIZE->init_client_width;
|
||||||
|
cfg::SCREENRESIZE->client_height = cfg::SCREENRESIZE->init_client_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenResize::build_content() {
|
||||||
|
ImGui::Text("For: %s", eamuse_get_game().c_str());
|
||||||
|
{
|
||||||
|
int flags = 0;
|
||||||
|
if (!GRAPHICS_WINDOWED || cfg::SCREENRESIZE->enable_screen_resize) {
|
||||||
|
flags |= ImGuiTreeNodeFlags_DefaultOpen;
|
||||||
|
}
|
||||||
|
if (ImGui::TreeNodeEx("Image Resize", flags)) {
|
||||||
|
this->build_fullscreen_config();
|
||||||
|
ImGui::TreePop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::BeginDisabled(!GRAPHICS_WINDOWED);
|
||||||
|
{
|
||||||
|
int flags = 0;
|
||||||
|
if (GRAPHICS_WINDOWED) {
|
||||||
|
flags |= ImGuiTreeNodeFlags_DefaultOpen;
|
||||||
|
}
|
||||||
|
if (ImGui::TreeNodeEx("Window Size", flags)) {
|
||||||
|
build_windowed_config();
|
||||||
|
ImGui::TreePop();
|
||||||
|
}
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
build_footer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenResize::build_fullscreen_config() {
|
||||||
// enable checkbox
|
// enable checkbox
|
||||||
ImGui::Checkbox("Enable Screen Resize", &cfg::SCREENRESIZE->enable_screen_resize);
|
ImGui::Checkbox("Enable", &cfg::SCREENRESIZE->enable_screen_resize);
|
||||||
if (cfg::SCREENRESIZE->enable_screen_resize) {
|
ImGui::BeginDisabled(!cfg::SCREENRESIZE->enable_screen_resize);
|
||||||
|
|
||||||
// general settings
|
// general settings
|
||||||
ImGui::Checkbox("Enable Linear Filter", &cfg::SCREENRESIZE->enable_linear_filter);
|
ImGui::Checkbox("Linear Filter", &cfg::SCREENRESIZE->enable_linear_filter);
|
||||||
|
ImGui::Checkbox("Centered", &cfg::SCREENRESIZE->centered);
|
||||||
|
if (!cfg::SCREENRESIZE->centered) {
|
||||||
ImGui::InputInt("X Offset", &cfg::SCREENRESIZE->offset_x);
|
ImGui::InputInt("X Offset", &cfg::SCREENRESIZE->offset_x);
|
||||||
ImGui::InputInt("Y Offset", &cfg::SCREENRESIZE->offset_y);
|
ImGui::InputInt("Y Offset", &cfg::SCREENRESIZE->offset_y);
|
||||||
|
}
|
||||||
|
|
||||||
// aspect ratio
|
// aspect ratio
|
||||||
ImGui::Checkbox("Keep Aspect Ratio", &cfg::SCREENRESIZE->keep_aspect_ratio);
|
ImGui::Checkbox("Keep Aspect Ratio", &cfg::SCREENRESIZE->keep_aspect_ratio);
|
||||||
@@ -42,13 +114,81 @@ namespace overlay::windows {
|
|||||||
ImGui::SliderFloat("Height Scale", &cfg::SCREENRESIZE->scale_y, 0.65f, 2.0f);
|
ImGui::SliderFloat("Height Scale", &cfg::SCREENRESIZE->scale_y, 0.65f, 2.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenResize::build_windowed_config() {
|
||||||
|
// for now, only supports the first window
|
||||||
|
const auto window = get_first_window();
|
||||||
|
if (!window) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::TextUnformatted("Warning: may cause some games to crash.");
|
||||||
|
ImGui::BeginDisabled(graphics_window_change_crashes_game());
|
||||||
|
if (ImGui::Combo(
|
||||||
|
"Window Style",
|
||||||
|
&cfg::SCREENRESIZE->window_decoration,
|
||||||
|
"Default\0Borderless\0Resizable Window\0\0")) {
|
||||||
|
graphics_update_window_style(window);
|
||||||
|
}
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::HelpMarker(
|
||||||
|
"Change window decoration. Resizable Window may not cause your mouse cursor to change, "
|
||||||
|
"but you can still drag to resize. Disabled for some games due to incompatibility.");
|
||||||
|
|
||||||
|
if (ImGui::Checkbox("Always on Top", &cfg::SCREENRESIZE->window_always_on_top) ) {
|
||||||
|
graphics_update_z_order(window);
|
||||||
|
}
|
||||||
|
ImGui::Checkbox("Keep Aspect Ratio", &cfg::SCREENRESIZE->client_keep_aspect_ratio);
|
||||||
|
ImGui::Checkbox("Manual window move/resize", &cfg::SCREENRESIZE->enable_window_resize);
|
||||||
|
ImGui::BeginDisabled(!cfg::SCREENRESIZE->enable_window_resize);
|
||||||
|
|
||||||
|
bool changed = false;
|
||||||
|
const uint32_t step = 1;
|
||||||
|
const uint32_t step_fast = 10;
|
||||||
|
ImGui::BeginDisabled(cfg::SCREENRESIZE->client_keep_aspect_ratio);
|
||||||
|
changed |= ImGui::InputScalar(
|
||||||
|
"Width",
|
||||||
|
ImGuiDataType_U32,
|
||||||
|
&cfg::SCREENRESIZE->client_width,
|
||||||
|
&step, &step_fast, nullptr,
|
||||||
|
ImGuiInputTextFlags_EnterReturnsTrue);
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
changed |= ImGui::InputScalar(
|
||||||
|
"Height",
|
||||||
|
ImGuiDataType_U32,
|
||||||
|
&cfg::SCREENRESIZE->client_height,
|
||||||
|
&step, &step_fast, nullptr,
|
||||||
|
ImGuiInputTextFlags_EnterReturnsTrue);
|
||||||
|
changed |= ImGui::InputScalar(
|
||||||
|
"X Offset",
|
||||||
|
ImGuiDataType_S32,
|
||||||
|
&cfg::SCREENRESIZE->window_offset_x,
|
||||||
|
&step, &step_fast, nullptr,
|
||||||
|
ImGuiInputTextFlags_EnterReturnsTrue);
|
||||||
|
changed |= ImGui::InputScalar(
|
||||||
|
"Y Offset",
|
||||||
|
ImGuiDataType_S32,
|
||||||
|
&cfg::SCREENRESIZE->window_offset_y,
|
||||||
|
&step, &step_fast, nullptr,
|
||||||
|
ImGuiInputTextFlags_EnterReturnsTrue);
|
||||||
|
if (changed) {
|
||||||
|
if (cfg::SCREENRESIZE->client_keep_aspect_ratio) {
|
||||||
|
cfg::SCREENRESIZE->client_width =
|
||||||
|
cfg::SCREENRESIZE->client_height * cfg::SCREENRESIZE->init_client_aspect_ratio;
|
||||||
|
}
|
||||||
|
graphics_move_resize_window(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenResize::build_footer() {
|
||||||
// reset button
|
// reset button
|
||||||
if (ImGui::Button("Reset")) {
|
if (ImGui::Button("Reset")) {
|
||||||
cfg::SCREENRESIZE->offset_x = 0;
|
this->reset_window();
|
||||||
cfg::SCREENRESIZE->offset_y = 0;
|
|
||||||
cfg::SCREENRESIZE->scale_x = 1;
|
|
||||||
cfg::SCREENRESIZE->scale_y = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// load button
|
// load button
|
||||||
|
|||||||
@@ -15,5 +15,19 @@ namespace overlay::windows {
|
|||||||
private:
|
private:
|
||||||
size_t toggle_screen_resize = ~0u;
|
size_t toggle_screen_resize = ~0u;
|
||||||
bool toggle_screen_resize_state = false;
|
bool toggle_screen_resize_state = false;
|
||||||
|
|
||||||
|
void build_fullscreen_config();
|
||||||
|
void build_windowed_config();
|
||||||
|
void build_footer();
|
||||||
|
std::string hwnd_preview(int index, HWND hwnd);
|
||||||
|
HWND get_first_window();
|
||||||
|
|
||||||
|
void reset_window();
|
||||||
|
void reset_vars_to_default();
|
||||||
|
|
||||||
|
static LRESULT CALLBACK screen_resize_wndproc(
|
||||||
|
HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||||
|
|
||||||
|
static void wndproc_wm_sizing(int edge, RECT& rect);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,107 +1,55 @@
|
|||||||
#undef CINTERFACE
|
#undef CINTERFACE
|
||||||
|
|
||||||
#include "sdvx_sub.h"
|
#include "sdvx_sub.h"
|
||||||
|
#include "games/sdvx/sdvx.h"
|
||||||
#include <fmt/format.h>
|
|
||||||
|
|
||||||
#include "games/io.h"
|
|
||||||
#include "hooks/graphics/backends/d3d9/d3d9_backend.h"
|
|
||||||
#include "hooks/graphics/backends/d3d9/d3d9_device.h"
|
|
||||||
#include "util/logging.h"
|
|
||||||
|
|
||||||
namespace overlay::windows {
|
namespace overlay::windows {
|
||||||
|
|
||||||
const ImVec4 YELLOW(1.f, 1.f, 0.f, 1.f);
|
SDVXSubScreen::SDVXSubScreen(SpiceOverlay *overlay) : GenericSubScreen(overlay) {
|
||||||
const ImVec4 WHITE(1.f, 1.f, 1.f, 1.f);
|
this->title = "SDVX Sub Screen";
|
||||||
|
|
||||||
SDVXSubScreen::SDVXSubScreen(SpiceOverlay *overlay) : Window(overlay), device(overlay->get_device()) {
|
const auto padding = ImGui::GetFrameHeight() / 2;
|
||||||
this->draws_window = false;
|
|
||||||
this->title = "Sub Screen";
|
|
||||||
this->toggle_button = games::OverlayButtons::ToggleSubScreen;
|
|
||||||
|
|
||||||
this->texture_size = ImVec2(0, 0);
|
this->init_size = ImVec2(ImGui::GetIO().DisplaySize.x - (padding * 2), 0.f);
|
||||||
|
this->init_size.y = (this->init_size.x * 9 / 16) + ImGui::GetFrameHeight();
|
||||||
|
|
||||||
|
this->init_pos = ImVec2(ImGui::GetIO().DisplaySize.x / 2 - this->init_size.x / 2, 0);
|
||||||
|
switch (games::sdvx::OVERLAY_POS) {
|
||||||
|
case games::sdvx::SDVX_OVERLAY_TOP:
|
||||||
|
this->init_pos.y = padding;
|
||||||
|
break;
|
||||||
|
case games::sdvx::SDVX_OVERLAY_BOTTOM:
|
||||||
|
this->init_pos.y = ImGui::GetIO().DisplaySize.y - this->init_size.y - padding;
|
||||||
|
break;
|
||||||
|
case games::sdvx::SDVX_OVERLAY_MIDDLE:
|
||||||
|
default:
|
||||||
|
this->init_pos.y = (ImGui::GetIO().DisplaySize.y / 2) - (this->init_size.y / 2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDVXSubScreen::build_content() {
|
void SDVXSubScreen::touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) {
|
||||||
this->draw_texture();
|
if (!this->get_active()) {
|
||||||
|
|
||||||
/*
|
|
||||||
if (this->status_message.has_value()) {
|
|
||||||
ImGui::TextColored(YELLOW, "%s", this->status_message.value().c_str());
|
|
||||||
} else if (this->texture) {
|
|
||||||
ImGui::TextColored(WHITE, "Successfully acquired surface texture");
|
|
||||||
} else {
|
|
||||||
ImGui::TextColored(YELLOW, "Failed to acquire surface texture");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SDVXSubScreen::build_texture(IDirect3DSurface9 *surface) {
|
|
||||||
HRESULT hr;
|
|
||||||
|
|
||||||
D3DSURFACE_DESC desc {};
|
|
||||||
hr = surface->GetDesc(&desc);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
this->status_message = fmt::format("Failed to get surface descriptor, hr={}", FMT_HRESULT(hr));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = this->device->CreateTexture(desc.Width, desc.Height, 0, desc.Usage, desc.Format,
|
|
||||||
desc.Pool, &this->texture, nullptr);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
this->status_message = fmt::format("Failed to create render target, hr={}", FMT_HRESULT(hr));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->texture_size = ImVec2(1080, 608);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDVXSubScreen::draw_texture() {
|
|
||||||
HRESULT hr;
|
|
||||||
|
|
||||||
auto surface = graphics_d3d9_ldj_get_sub_screen();
|
|
||||||
if (surface == nullptr) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->texture == nullptr) {
|
// SDVX Valkyrie cab needs math to make things work
|
||||||
if (!this->build_texture(surface)) {
|
// what the game expects from touch screen:
|
||||||
this->texture = nullptr;
|
// [1080, 0 1920, 1080]
|
||||||
this->texture_size = ImVec2(0, 0);
|
// [0, 0 0, 1920]
|
||||||
|
|
||||||
surface->Release();
|
// actual cursor position given by x_in (on the overlay, normalized):
|
||||||
return;
|
// [0, 0 1, 0]
|
||||||
}
|
// [0, 1 1, 1]
|
||||||
}
|
|
||||||
|
|
||||||
IDirect3DSurface9 *texture_surface = nullptr;
|
// Basically, the game operates on rotated coordinates, but on top of that,
|
||||||
hr = this->texture->GetSurfaceLevel(0, &texture_surface);
|
// it needs to be adjusted so they match up with the subscreen overlay.
|
||||||
if (FAILED(hr)) {
|
|
||||||
this->status_message = fmt::format("Failed to get texture surface, hr={}", FMT_HRESULT(hr));
|
|
||||||
|
|
||||||
surface->Release();
|
// so the math is:
|
||||||
return;
|
// Xin(0, 1) => Yout(0, 1920)
|
||||||
}
|
// Yin(0, 1) => Xout(1080, 0)
|
||||||
|
|
||||||
hr = this->device->StretchRect(surface, nullptr, texture_surface, nullptr, D3DTEXF_NONE);
|
*x_out = ImGui::GetIO().DisplaySize.x * (1.f - xy_in.y);
|
||||||
if (FAILED(hr)) {
|
*y_out = ImGui::GetIO().DisplaySize.y * xy_in.x;
|
||||||
this->status_message = fmt::format("Failed to copy back buffer contents, hr={}", FMT_HRESULT(hr));
|
|
||||||
|
|
||||||
surface->Release();
|
|
||||||
texture_surface->Release();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
surface->Release();
|
|
||||||
texture_surface->Release();
|
|
||||||
|
|
||||||
ImGui::GetBackgroundDrawList()->AddImage(
|
|
||||||
reinterpret_cast<void *>(this->texture),
|
|
||||||
ImVec2(0, 0),
|
|
||||||
this->texture_size,
|
|
||||||
ImVec2(0, 0),
|
|
||||||
ImVec2(1, 1));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,27 +4,18 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <d3d9.h>
|
|
||||||
|
|
||||||
#include "overlay/window.h"
|
#include "overlay/window.h"
|
||||||
|
#include "overlay/windows/generic_sub.h"
|
||||||
|
|
||||||
namespace overlay::windows {
|
namespace overlay::windows {
|
||||||
|
|
||||||
class SDVXSubScreen : public Window {
|
class SDVXSubScreen : public GenericSubScreen {
|
||||||
public:
|
public:
|
||||||
SDVXSubScreen(SpiceOverlay *overlay);
|
SDVXSubScreen(SpiceOverlay *overlay);
|
||||||
|
|
||||||
void build_content() override;
|
protected:
|
||||||
|
void touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) override;
|
||||||
private:
|
|
||||||
bool build_texture(IDirect3DSurface9 *surface);
|
|
||||||
void draw_texture();
|
|
||||||
|
|
||||||
std::optional<std::string> status_message = std::nullopt;
|
|
||||||
|
|
||||||
IDirect3DDevice9 *device = nullptr;
|
|
||||||
IDirect3DTexture9 *texture = nullptr;
|
|
||||||
ImVec2 texture_size;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ namespace rawinput {
|
|||||||
|
|
||||||
// settings
|
// settings
|
||||||
bool NOLEGACY = false;
|
bool NOLEGACY = false;
|
||||||
|
uint8_t HID_LIGHT_BRIGHTNESS = 100; // 100%
|
||||||
}
|
}
|
||||||
|
|
||||||
rawinput::RawInputManager::RawInputManager() {
|
rawinput::RawInputManager::RawInputManager() {
|
||||||
@@ -2211,9 +2212,12 @@ void rawinput::RawInputManager::device_write_output(Device *device, bool only_up
|
|||||||
auto &value_cap = hid->value_output_caps_list[cap_no];
|
auto &value_cap = hid->value_output_caps_list[cap_no];
|
||||||
auto &value_state = hid->value_output_states[cap_no];
|
auto &value_state = hid->value_output_states[cap_no];
|
||||||
|
|
||||||
|
// adjust output value per "brightness" setting
|
||||||
|
auto adjusted_value_state = value_state * HID_LIGHT_BRIGHTNESS / 100;
|
||||||
|
|
||||||
// build value
|
// build value
|
||||||
LONG usage_value = value_cap.LogicalMin +
|
LONG usage_value = value_cap.LogicalMin +
|
||||||
lroundf((value_cap.LogicalMax - value_cap.LogicalMin) * value_state);
|
lroundf((value_cap.LogicalMax - value_cap.LogicalMin) * adjusted_value_state);
|
||||||
if (usage_value > value_cap.LogicalMax) {
|
if (usage_value > value_cap.LogicalMax) {
|
||||||
usage_value = value_cap.LogicalMax;
|
usage_value = value_cap.LogicalMax;
|
||||||
} else if (usage_value < value_cap.LogicalMin) {
|
} else if (usage_value < value_cap.LogicalMin) {
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ namespace rawinput {
|
|||||||
// settings
|
// settings
|
||||||
extern bool NOLEGACY;
|
extern bool NOLEGACY;
|
||||||
|
|
||||||
|
extern uint8_t HID_LIGHT_BRIGHTNESS;
|
||||||
|
|
||||||
struct DeviceCallback {
|
struct DeviceCallback {
|
||||||
void *data;
|
void *data;
|
||||||
std::function<void(void*, Device*)> f;
|
std::function<void(void*, Device*)> f;
|
||||||
|
|||||||
+5
-8
@@ -1,13 +1,10 @@
|
|||||||
In respect to all those dying arcade
|
In respect to all those dying arcade games. Keep them alive, support the
|
||||||
games. Keep them alive, support the
|
developers when you can. Sources should be included, have fun!
|
||||||
developers when you can.
|
|
||||||
|
|
||||||
Sources should be included, have fun!
|
spice2x fork maintainers:
|
||||||
Want to collaborate, add features, fix
|
* sp2xdev
|
||||||
bugs or clean up messy code? Try to
|
|
||||||
contact us, any help is appreciated! :)
|
|
||||||
|
|
||||||
Contributors:
|
SpiceTools original contributors:
|
||||||
* cube
|
* cube
|
||||||
* felix
|
* felix
|
||||||
* dinsfire
|
* dinsfire
|
||||||
|
|||||||
+12
-2
@@ -873,7 +873,12 @@ void touch_get_points(std::vector<TouchPoint> &touch_points, bool overlay) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// overlay override
|
// overlay override
|
||||||
if (!overlay && overlay::OVERLAY && ImGui::GetIO().WantCaptureMouse) {
|
if (!overlay &&
|
||||||
|
overlay::OVERLAY &&
|
||||||
|
overlay::OVERLAY->get_active() &&
|
||||||
|
!overlay::OVERLAY->can_transform_touch_input() &&
|
||||||
|
ImGui::GetIO().WantCaptureMouse) {
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -895,7 +900,12 @@ void touch_get_events(std::vector<TouchEvent> &touch_events, bool overlay) {
|
|||||||
std::lock_guard<std::mutex> lock(TOUCH_EVENTS_M);
|
std::lock_guard<std::mutex> lock(TOUCH_EVENTS_M);
|
||||||
|
|
||||||
// overlay override
|
// overlay override
|
||||||
if (!overlay && overlay::OVERLAY && ImGui::GetIO().WantCaptureMouse) {
|
if (!overlay &&
|
||||||
|
overlay::OVERLAY &&
|
||||||
|
overlay::OVERLAY->get_active() &&
|
||||||
|
!overlay::OVERLAY->can_transform_touch_input() &&
|
||||||
|
ImGui::GetIO().WantCaptureMouse) {
|
||||||
|
|
||||||
TOUCH_EVENTS.reset();
|
TOUCH_EVENTS.reset();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-7
@@ -30,16 +30,24 @@ static inline void load_library_fail(const std::string &file_name, bool fatal) {
|
|||||||
std::string info_str { fmt::format(
|
std::string info_str { fmt::format(
|
||||||
"\n\nPlease check if {} exists and the permissions are fine.\n"
|
"\n\nPlease check if {} exists and the permissions are fine.\n"
|
||||||
"If the problem still persists, try installing:\n"
|
"If the problem still persists, try installing:\n"
|
||||||
"* Microsoft Visual C++ Redistributables (*all* versions, *both* x86/x64)\n"
|
|
||||||
" Hint: use one of the AIO installers (e.g. https://github.com/abbodi1406/vcredist)\n"
|
|
||||||
"* DirectX End-User Runtimes (June 2010) \n"
|
"* DirectX End-User Runtimes (June 2010) \n"
|
||||||
"* Running Windows 10 \"N\"?\n"
|
" https://www.microsoft.com/en-us/download/details.aspx?id=8109 \n"
|
||||||
|
"* Microsoft Visual C++ Redistributable Runtimes (*all* versions, x86 *AND* x64)\n"
|
||||||
|
" https://github.com/abbodi1406/vcredist (recommended All-In-One installer)\n"
|
||||||
|
" You may need to run the installer *multiple times* and reboot after each install\n"
|
||||||
|
"* Running Windows 10 \"N\" or \"KN\" Editions?\n"
|
||||||
" Grab: https://www.microsoft.com/en-us/software-download/mediafeaturepack \n"
|
" Grab: https://www.microsoft.com/en-us/software-download/mediafeaturepack \n"
|
||||||
" Check: https://support.microsoft.com/en-us/help/4562569/media-feature-pack-for-windows-10-n-may-2020 \n"
|
" Check: https://support.microsoft.com/en-us/help/4562569/media-feature-pack-for-windows-10-n-may-2020 \n"
|
||||||
"* Running Windows 7 \"N\"?\n"
|
"* Running Windows 7 \"N\" or \"KN\" Editions?\n"
|
||||||
" Grab: https://www.microsoft.com/en-us/download/details.aspx?id=16546\n"
|
" x86: https://web.archive.org/web/20190810145509/https://download.microsoft.com/download/B/9/B/B9BED058-8669-490E-BA61-D502E4E8BEB1/Windows6.1-KB968211-x86-RefreshPkg.msu \n"
|
||||||
"* Still have problems?\n"
|
" x64: https://web.archive.org/web/20190810145509/https://download.microsoft.com/download/B/9/B/B9BED058-8669-490E-BA61-D502E4E8BEB1/Windows6.1-KB968211-x64-RefreshPkg.msu \n"
|
||||||
" Find the missing dependency using: http://www.dependencywalker.com/"
|
"* Still have problems after installing above?\n"
|
||||||
|
" Ensure you do NOT have multiple copies of the game DLLs\n"
|
||||||
|
" Ensure the game DLLs are in the correct place, and double check -modules parameter\n"
|
||||||
|
" Certain games require specific NVIDIA DLLs when running with AMD GPUs\n"
|
||||||
|
" Find the missing dependency using:\n"
|
||||||
|
" https://github.com/lucasg/Dependencies (recommended for most) \n"
|
||||||
|
" http://www.dependencywalker.com/ (for old OS) \n"
|
||||||
, file_name) };
|
, file_name) };
|
||||||
if (fatal) {
|
if (fatal) {
|
||||||
log_fatal("libutils", "{}", info_str);
|
log_fatal("libutils", "{}", info_str);
|
||||||
|
|||||||
+1
-1
@@ -84,7 +84,7 @@ struct fmt::formatter<fmt_hresult> {
|
|||||||
logger::push(LOG_FORMAT("F", module, format_str, ## __VA_ARGS__), logger::Style::RED); \
|
logger::push(LOG_FORMAT("F", module, format_str, ## __VA_ARGS__), logger::Style::RED); \
|
||||||
\
|
\
|
||||||
launcher::stop_subsystems(); \
|
launcher::stop_subsystems(); \
|
||||||
Sleep(2000); \
|
Sleep(30000); \
|
||||||
launcher::kill(); \
|
launcher::kill(); \
|
||||||
std::terminate(); \
|
std::terminate(); \
|
||||||
} ((void) 0 )
|
} ((void) 0 )
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
#include "resutils.h"
|
#include "resutils.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
|
||||||
const char *resutil::load_file(int name, LPCSTR type, DWORD *size) {
|
const char *resutil::load_file(int name, DWORD *size, LPCSTR type) {
|
||||||
HMODULE handle = GetModuleHandle(NULL);
|
HMODULE handle = GetModuleHandle(NULL);
|
||||||
HRSRC rc = FindResource(handle, MAKEINTRESOURCE(name), type);
|
HRSRC rc = FindResource(handle, MAKEINTRESOURCE(name), type);
|
||||||
HGLOBAL rcData = LoadResource(handle, rc);
|
HGLOBAL rcData = LoadResource(handle, rc);
|
||||||
@@ -12,7 +12,7 @@ const char *resutil::load_file(int name, LPCSTR type, DWORD *size) {
|
|||||||
|
|
||||||
std::string resutil::load_file_string(int name, LPCSTR type) {
|
std::string resutil::load_file_string(int name, LPCSTR type) {
|
||||||
DWORD size = 0;
|
DWORD size = 0;
|
||||||
auto data = resutil::load_file(name, type, &size);
|
auto data = resutil::load_file(name, &size, type);
|
||||||
return std::string(data, size);
|
return std::string(data, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
namespace resutil {
|
namespace resutil {
|
||||||
|
|
||||||
const char* load_file(int name, LPCSTR type, DWORD* size);
|
const char* load_file(int name, DWORD* size, LPCSTR type=RT_RCDATA);
|
||||||
std::string load_file_string(int name, LPCSTR type=RT_RCDATA);
|
std::string load_file_string(int name, LPCSTR type=RT_RCDATA);
|
||||||
std::string load_file_string_crlf(int name, LPCSTR type=RT_RCDATA);
|
std::string load_file_string_crlf(int name, LPCSTR type=RT_RCDATA);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user