Compare commits

...

23 Commits

Author SHA1 Message Date
dreamsyntax fe70e90973
Merge 714271c22f into 897978e955 2025-01-29 19:21:15 +01:00
JMC47 897978e955
Merge pull request #13310 from jordan-woyak/small-vec-placement-new
Common: Make SmallVector work with non-standard-layout types.
2025-01-28 20:59:14 -05:00
JMC47 e16e3f9a61
Merge pull request #13291 from iwubcode/imgui_1_91_7
Externals / VideoCommon: update imgui to 1.91.7 and implot to v0.16
2025-01-28 20:57:28 -05:00
Admiral H. Curtiss 3f79aa23b4
Merge pull request #13267 from Sintendo/arm64-fix-gt-micro
JitArm64_SystemRegisters: Small FixGTBeforeSettingCRFieldBit optimization
2025-01-28 19:43:53 +01:00
Admiral H. Curtiss 0b7f9541d0
Merge pull request #13304 from JoshuaVandaele/argsegfault
Fix segfault when passing invalid arguments
2025-01-28 19:27:23 +01:00
JMC47 f92f174450
Merge pull request #13297 from jordan-woyak/config-ext-btn
DolphinQt: Add a "Configure Extension" button under the extension selection combo box.
2025-01-27 21:17:41 -05:00
JMC47 e18a4d04b4
Merge pull request #13178 from jordan-woyak/input-expressions-conditional-op
InputCommon: Add ternary conditional operator to input expressions.
2025-01-27 21:16:29 -05:00
JMC47 2b5cd96cb1
Merge pull request #11261 from TryTwo/PR_MemoryView_Auto_Update
MemoryView auto-update while running and color recently changed cells.
2025-01-27 21:15:57 -05:00
JosJuice d117614c00
Merge pull request #13213 from JosJuice/remove-filter-patches-lock
Core: Remove redundant lock for FilterApprovedPatches call
2025-01-27 20:15:00 +01:00
Jordan Woyak 9777e8e76b Common: Make SmallVector work with non-standard-layout types. 2025-01-26 13:03:39 -06:00
Joshua Vandaële f1f147965b
Fix segfault when passing invalid arguments 2025-01-24 20:52:33 +01:00
Jordan Woyak a33368b102 DolphinQt: Add a "Configure Extension" button under the extension
selection combo box.
2025-01-22 02:58:32 -06:00
iwubcode 25c805be99 Externals / VideoCommon: update imgui to 1.91.7 and implot to v0.16; imgui changed types for ImTextureId, which was addressed by using an implicit cast 2025-01-20 14:47:14 -06:00
TryTwo 0b8301ff97 MemoryViewWidget: Add auto update toggle. 2025-01-19 23:24:59 -07:00
TryTwo 7b19192134 MemoryViewWidget: Color recently changed memory when auto updating. 2025-01-19 23:24:08 -07:00
TryTwo 6d8f40c245 MemoryViewWidget: Reduce amount of unnecessary update calls. 2025-01-19 23:19:40 -07:00
TryTwo 32e135e6a9 MemoryViewWidget: Add OnFrameEnd callback to auto-update memory while a game is running. 2025-01-19 23:18:38 -07:00
TryTwo 3edb5accca MemoryViewWidget: Refactor updates using a dispatch function. Isolate memory reads from table updates.
Preparations for auto update while a game is running.
2025-01-08 13:40:46 -07:00
TryTwo 2e006d9787 MemoryViewWidget: Refactor. Remove OnItemChanged signal and QSignalBlocker - replace with a signal that is only sent at the correct time. 2025-01-07 15:57:53 -07:00
Sintendo 24f2981e54 JitArm64_SystemRegisters: Small FixGTBeforeSettingCRFieldBit optimization
The computed value is only used when the register is equal to zero, so
we can fully precompute it and materialize the constant instead. In
other words, we change from

```
return reg == 0 ? (reg | 1ULL << 63) : reg;
```

to

```
return reg == 0 ? 1ULL << 63 : reg;
```

The number of instructions remains the same, but we eliminate an
unnecessary dependency on the register value.

Before:
0xb241037a   orr    x26, x27, #0x8000000000000000
0xeb1f037f   cmp    x27, xzr
0x9a9a137b   csel   x27, x27, x26, ne

After:
0xd2f0001a   mov    x26, #-0x8000000000000000 ; =-9223372036854775808
0xeb1f037f   cmp    x27, xzr
0x9a9a137b   csel   x27, x27, x26, ne
2025-01-06 12:09:12 +01:00
dreamsyntax 714271c22f Gecko: Shadow the Hedgehog: Reloaded codes 2024-12-28 23:16:06 -07:00
JosJuice 0c46205ba7 Core: Remove redundant lock for FilterApprovedPatches call
This was necessary before we added locking inside FilterApprovedPatches,
but not anymore.
2024-12-07 19:06:23 +01:00
Jordan Woyak 5078a63084 InputCommon: Add ternary conditional operator to input expressions. 2024-11-07 08:31:25 -06:00
34 changed files with 14257 additions and 5954 deletions

View File

@ -0,0 +1,35 @@
# GUPR8P - Shadow the Hedgehog: Reloaded
[Gecko]
$No Idle ChaosPowers Music v2 [dreamsyntax]
040A4FF0 60000000
040A4D84 60000000
*Disables the jingle when Shadow is in HeroShadow or DarkShadow state. Chaos Powers (Blast/Control) will still play their special music/sounds. After the Chaos Power completes, the original stage music will resume instead of restarting from the beginning.
$Mute music and cutscenes
0458F99C 00000000
0458FA90 00000000
*Mutes all adx tracks (including cutscenes).
$Skip Loading Any Events In Story Mode / Last Story [dreamsyntax]
0425D460 60000000
*Makes StageSequenceManager skip all events/sfd. For example pressing Last Story will immediately start The Last Way instead of playing the multiple events.
$No Subtitles [LimblessVector]
045E1B9C 00000000
*Voice lines will play, but no subtitles will render to the screen.
*This includes pause text and rank info on select mode.
$Restore Partner Intro Cutscene Behavior [dreamsyntax]
04073E10 4082004C
*Restore the camera event on first partner meet.
$Force Restart on Death (Hold L) [dreamsyntax]
C2204854 00000005
3C808057 60845D5E
88840000 2C040010
38800008 41820008
40820008 38800007
60000000 00000000
*QoL code for speedruns.
*Hold L to force a restart on death.

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2014-2021 Omar Cornut
Copyright (c) 2014-2025 Omar Cornut
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,5 +1,5 @@
//-----------------------------------------------------------------------------
// COMPILE-TIME OPTIONS FOR DEAR IMGUI
// DEAR IMGUI COMPILE-TIME OPTIONS
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
//-----------------------------------------------------------------------------
@ -9,64 +9,73 @@
// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp
// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using.
// Call IMGUI_CHECKVERSION() from your .cpp file to verify that the data structures your files are using are matching the ones imgui.cpp is using.
//-----------------------------------------------------------------------------
#pragma once
#include "Common/Assert.h"
//---- Define assertion handler. Defaults to calling assert().
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
#define IM_ASSERT(_EXPR) ASSERT(_EXPR)
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
// DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
//#define IMGUI_API __declspec( dllexport )
//#define IMGUI_API __declspec( dllimport )
// - Windows DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
//#define IMGUI_API __declspec(dllexport) // MSVC Windows: DLL export
//#define IMGUI_API __declspec(dllimport) // MSVC Windows: DLL import
//#define IMGUI_API __attribute__((visibility("default"))) // GCC/Clang: override visibility when set is hidden
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names.
#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.
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
// 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_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() 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).
//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowIDStackToolWindow() will be empty.
//---- 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_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime).
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
//#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default platform_io.Platform_OpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")).
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
//#define IMGUI_DISABLE_DEFAULT_FONT // Disable default embedded font (ProggyClean.ttf), remove ~9.5 KB from output binary. AddFontDefault() will assert.
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
//---- Include imgui_user.h at the end of imgui.h as a convenience
//#define IMGUI_INCLUDE_IMGUI_USER_H
//---- Enable Test Engine / Automation features.
//#define IMGUI_ENABLE_TEST_ENGINE // Enable imgui_test_engine hooks. Generally set automatically by include "imgui_te_config.h", see Test Engine for details.
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
//---- Include imgui_user.h at the end of imgui.h as a convenience
// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
//#define IMGUI_INCLUDE_IMGUI_USER_H
//#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h"
//---- Pack vertex colors as BGRA8 instead of RGBA8 (to avoid converting from one to another). Need dedicated backend support.
//#define IMGUI_USE_BGRA_PACKED_COLOR
//---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
//---- Use legacy CRC32-adler tables (used before 1.91.6), in order to preserve old .ini data that you cannot afford to invalidate.
//#define IMGUI_USE_LEGACY_CRC32_ADLER
//---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
//#define IMGUI_USE_WCHAR32
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if enabled
//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if IMGUI_USE_STB_SPRINTF is defined.
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
//#define IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION // only disabled if IMGUI_USE_STB_SPRINTF is defined.
//---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
// Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h.
@ -77,6 +86,15 @@
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
//#define IMGUI_ENABLE_FREETYPE
//---- Use FreeType + plutosvg or lunasvg to render OpenType SVG fonts (SVGinOT)
// Only works in combination with IMGUI_ENABLE_FREETYPE.
// - lunasvg is currently easier to acquire/install, as e.g. it is part of vcpkg.
// - plutosvg will support more fonts and may load them faster. It currently requires to be built manually but it is fairly easy. See misc/freetype/README for instructions.
// - Both require headers to be available in the include path + program to be linked with the library code (not provided).
// - (note: lunasvg implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
//#define IMGUI_ENABLE_FREETYPE_PLUTOSVG
//#define IMGUI_ENABLE_FREETYPE_LUNASVG
//---- Use stb_truetype to build and rasterize the font atlas (default)
// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
//#define IMGUI_ENABLE_STB_TRUETYPE
@ -107,7 +125,7 @@
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
//#define ImDrawCallback MyImDrawCallback
//---- Debug Tools: Macro to break in Debugger
//---- Debug Tools: Macro to break in Debugger (we provide a default implementation of this in the codebase)
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
//#define IM_DEBUG_BREAK IM_ASSERT(0)
//#define IM_DEBUG_BREAK __debugbreak()
@ -115,10 +133,10 @@
//---- Debug Tools: Enable slower asserts
//#define IMGUI_DEBUG_PARANOID
//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
//---- Tip: You can add extra functions within the ImGui:: namespace from anywhere (e.g. your own sources/header files)
/*
namespace ImGui
{
void MyFunction(const char* name, const MyMatrix44& v);
void MyFunction(const char* name, MyMatrix44* mtx);
}
*/

File diff suppressed because it is too large Load Diff

2295
Externals/imgui/imgui.h vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -2,8 +2,11 @@
// This is a slightly modified version of stb_textedit.h 1.14.
// 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_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000)
// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783)
// - Added name to struct or it may be forward declared in our code.
// - Added UTF-8 support (see https://github.com/nothings/stb/issues/188 + https://github.com/ocornut/imgui/pull/7925)
// Grep for [DEAR IMGUI] to find the changes.
// - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_*
// stb_textedit.h - v1.14 - public domain - Sean Barrett
// Development of this library was sponsored by RAD Game Tools
@ -30,7 +33,7 @@
// DEPENDENCIES
//
// Uses the C runtime function 'memmove', which you can override
// by defining STB_TEXTEDIT_memmove before the implementation.
// by defining IMSTB_TEXTEDIT_memmove before the implementation.
// Uses no other functions. Performs no runtime allocations.
//
//
@ -40,7 +43,7 @@
// 1.13 (2019-02-07) fix bug in undo size management
// 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash
// 1.11 (2017-03-03) fix HOME on last line, dragging off single-line textfield
// 1.10 (2016-10-25) supress warnings about casting away const with -Wcast-qual
// 1.10 (2016-10-25) suppress warnings about casting away const with -Wcast-qual
// 1.9 (2016-08-27) customizable move-by-word
// 1.8 (2016-04-02) better keyboard handling when mouse button is down
// 1.7 (2015-09-13) change y range handling in case baseline is non-0
@ -208,6 +211,7 @@
// int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
// int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len)
// void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXEDIT_KEYTYPE key)
// void stb_textedit_text(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int text_len)
//
// Each of these functions potentially updates the string and updates the
// state.
@ -242,7 +246,12 @@
// various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit
// set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is
// clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to
// anything other type you wante before including.
// anything other type you want before including.
// if the STB_TEXTEDIT_KEYTOTEXT function is defined, selected keys are
// transformed into text and stb_textedit_text() is automatically called.
//
// text: [DEAR IMGUI] added 2024-09
// call this to text inputs sent to the textfield.
//
//
// When rendering, you can read the cursor position and selection state from
@ -274,8 +283,8 @@
////
////
#ifndef INCLUDE_STB_TEXTEDIT_H
#define INCLUDE_STB_TEXTEDIT_H
#ifndef INCLUDE_IMSTB_TEXTEDIT_H
#define INCLUDE_IMSTB_TEXTEDIT_H
////////////////////////////////////////////////////////////////////////
//
@ -286,38 +295,38 @@
// and undo state.
//
#ifndef STB_TEXTEDIT_UNDOSTATECOUNT
#define STB_TEXTEDIT_UNDOSTATECOUNT 99
#ifndef IMSTB_TEXTEDIT_UNDOSTATECOUNT
#define IMSTB_TEXTEDIT_UNDOSTATECOUNT 99
#endif
#ifndef STB_TEXTEDIT_UNDOCHARCOUNT
#define STB_TEXTEDIT_UNDOCHARCOUNT 999
#ifndef IMSTB_TEXTEDIT_UNDOCHARCOUNT
#define IMSTB_TEXTEDIT_UNDOCHARCOUNT 999
#endif
#ifndef STB_TEXTEDIT_CHARTYPE
#define STB_TEXTEDIT_CHARTYPE int
#ifndef IMSTB_TEXTEDIT_CHARTYPE
#define IMSTB_TEXTEDIT_CHARTYPE int
#endif
#ifndef STB_TEXTEDIT_POSITIONTYPE
#define STB_TEXTEDIT_POSITIONTYPE int
#ifndef IMSTB_TEXTEDIT_POSITIONTYPE
#define IMSTB_TEXTEDIT_POSITIONTYPE int
#endif
typedef struct
{
// private data
STB_TEXTEDIT_POSITIONTYPE where;
STB_TEXTEDIT_POSITIONTYPE insert_length;
STB_TEXTEDIT_POSITIONTYPE delete_length;
IMSTB_TEXTEDIT_POSITIONTYPE where;
IMSTB_TEXTEDIT_POSITIONTYPE insert_length;
IMSTB_TEXTEDIT_POSITIONTYPE delete_length;
int char_storage;
} StbUndoRecord;
typedef struct
{
// private data
StbUndoRecord undo_rec [STB_TEXTEDIT_UNDOSTATECOUNT];
STB_TEXTEDIT_CHARTYPE undo_char[STB_TEXTEDIT_UNDOCHARCOUNT];
StbUndoRecord undo_rec [IMSTB_TEXTEDIT_UNDOSTATECOUNT];
IMSTB_TEXTEDIT_CHARTYPE undo_char[IMSTB_TEXTEDIT_UNDOCHARCOUNT];
short undo_point, redo_point;
int undo_char_point, redo_char_point;
} StbUndoState;
typedef struct
typedef struct STB_TexteditState
{
/////////////////////
//
@ -371,7 +380,7 @@ typedef struct
float ymin,ymax; // height of row above and below baseline
int num_chars;
} StbTexteditRow;
#endif //INCLUDE_STB_TEXTEDIT_H
#endif //INCLUDE_IMSTB_TEXTEDIT_H
////////////////////////////////////////////////////////////////////////////
@ -384,11 +393,11 @@ typedef struct
// implementation isn't include-guarded, since it might have indirectly
// included just the "header" portion
#ifdef STB_TEXTEDIT_IMPLEMENTATION
#ifdef IMSTB_TEXTEDIT_IMPLEMENTATION
#ifndef STB_TEXTEDIT_memmove
#ifndef IMSTB_TEXTEDIT_memmove
#include <string.h>
#define STB_TEXTEDIT_memmove memmove
#define IMSTB_TEXTEDIT_memmove memmove
#endif
@ -398,7 +407,7 @@ typedef struct
//
// traverse the layout to locate the nearest character to a display position
static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)
static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
{
StbTexteditRow r;
int n = STB_TEXTEDIT_STRINGLEN(str);
@ -437,13 +446,13 @@ static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)
if (x < r.x1) {
// search characters in row for one that straddles 'x'
prev_x = r.x0;
for (k=0; k < r.num_chars; ++k) {
for (k=0; k < r.num_chars; k = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k) - i) {
float w = STB_TEXTEDIT_GETWIDTH(str, i, k);
if (x < prev_x+w) {
if (x < prev_x+w/2)
return k+i;
else
return k+i+1;
return IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k);
}
prev_x += w;
}
@ -458,7 +467,7 @@ static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)
}
// API click: on mouse down, move the cursor to the clicked location, and reset the selection
static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
{
// In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
// goes off the top or bottom of the text
@ -476,7 +485,7 @@ static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *stat
}
// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location
static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
{
int p = 0;
@ -502,11 +511,11 @@ static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state
//
// forward declarations
static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state);
static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state);
static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length);
static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state);
static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state);
static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length);
static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length);
static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length);
static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length);
typedef struct
{
@ -518,7 +527,7 @@ typedef struct
// find the x/y location of a character, and remember info about the previous row in
// case we get a move-up event (for page up, we'll have to rescan)
static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *str, int n, int single_line)
static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING *str, int n, int single_line)
{
StbTexteditRow r;
int prev_start = 0;
@ -549,7 +558,10 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s
i += r.num_chars;
find->y += r.baseline_y_delta;
if (i == z) // [DEAR IMGUI]
{
r.num_chars = 0; // [DEAR IMGUI]
break; // [DEAR IMGUI]
}
}
find->first_char = first = i;
@ -559,14 +571,14 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s
// now scan to find xpos
find->x = r.x0;
for (i=0; first+i < n; ++i)
for (i=0; first+i < n; i = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, first + i) - first)
find->x += STB_TEXTEDIT_GETWIDTH(str, first, i);
}
#define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end)
// make the selection/cursor state valid if client altered the string
static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
static void stb_textedit_clamp(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{
int n = STB_TEXTEDIT_STRINGLEN(str);
if (STB_TEXT_HAS_SELECTION(state)) {
@ -580,7 +592,7 @@ static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *stat
}
// delete characters while updating undo
static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len)
static void stb_textedit_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len)
{
stb_text_makeundo_delete(str, state, where, len);
STB_TEXTEDIT_DELETECHARS(str, where, len);
@ -588,7 +600,7 @@ static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *sta
}
// delete the section
static void stb_textedit_delete_selection(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
static void stb_textedit_delete_selection(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{
stb_textedit_clamp(str, state);
if (STB_TEXT_HAS_SELECTION(state)) {
@ -625,7 +637,7 @@ static void stb_textedit_move_to_first(STB_TexteditState *state)
}
// move cursor to last character of selection
static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{
if (STB_TEXT_HAS_SELECTION(state)) {
stb_textedit_sortselection(state);
@ -636,14 +648,25 @@ static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditStat
}
}
// [DEAR IMGUI]
// Functions must be implemented for UTF8 support
// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
// There is not necessarily a '[DEAR IMGUI]' at the usage sites.
#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX
#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx) (idx - 1)
#endif
#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX
#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx) (idx + 1)
#endif
#ifdef STB_TEXTEDIT_IS_SPACE
static int is_word_boundary( STB_TEXTEDIT_STRING *str, int idx )
static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
{
return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1;
}
#ifndef STB_TEXTEDIT_MOVEWORDLEFT
static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c )
static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c )
{
--c; // always move at least one character
while( c >= 0 && !is_word_boundary( str, c ) )
@ -658,7 +681,7 @@ static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c )
#endif
#ifndef STB_TEXTEDIT_MOVEWORDRIGHT
static int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *str, int c )
static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c )
{
const int len = STB_TEXTEDIT_STRINGLEN(str);
++c; // always move at least one character
@ -685,7 +708,7 @@ static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state)
}
// API cut: delete selection
static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
static int stb_textedit_cut(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{
if (STB_TEXT_HAS_SELECTION(state)) {
stb_textedit_delete_selection(str,state); // implicitly clamps
@ -696,7 +719,7 @@ static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
}
// API paste: replace existing selection with passed-in text
static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len)
static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE *text, int len)
{
// if there's a selection, the paste should delete it
stb_textedit_clamp(str, state);
@ -716,36 +739,44 @@ static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditSta
#define STB_TEXTEDIT_KEYTYPE int
#endif
// [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility.
static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len)
{
// can't add newline in single-line mode
if (text[0] == '\n' && state->single_line)
return;
if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
state->cursor += text_len;
state->has_preferred_x = 0;
}
}
else {
stb_textedit_delete_selection(str, state); // implicitly clamps
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
stb_text_makeundo_insert(state, state->cursor, text_len);
state->cursor += text_len;
state->has_preferred_x = 0;
}
}
}
// API key: process a keyboard input
static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
{
retry:
switch (key) {
default: {
#ifdef STB_TEXTEDIT_KEYTOTEXT
int c = STB_TEXTEDIT_KEYTOTEXT(key);
if (c > 0) {
STB_TEXTEDIT_CHARTYPE ch = (STB_TEXTEDIT_CHARTYPE) c;
// can't add newline in single-line mode
if (c == '\n' && state->single_line)
break;
if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {
++state->cursor;
state->has_preferred_x = 0;
}
} else {
stb_textedit_delete_selection(str,state); // implicitly clamps
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {
stb_text_makeundo_insert(state, state->cursor, 1);
++state->cursor;
state->has_preferred_x = 0;
}
}
IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c;
stb_textedit_text(str, state, &ch, 1);
}
#endif
break;
}
@ -771,7 +802,7 @@ retry:
stb_textedit_move_to_first(state);
else
if (state->cursor > 0)
--state->cursor;
state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
state->has_preferred_x = 0;
break;
@ -780,7 +811,7 @@ retry:
if (STB_TEXT_HAS_SELECTION(state))
stb_textedit_move_to_last(str, state);
else
++state->cursor;
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
stb_textedit_clamp(str, state);
state->has_preferred_x = 0;
break;
@ -790,7 +821,7 @@ retry:
stb_textedit_prep_selection_at_cursor(state);
// move selection left
if (state->select_end > 0)
--state->select_end;
state->select_end = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->select_end);
state->cursor = state->select_end;
state->has_preferred_x = 0;
break;
@ -840,7 +871,7 @@ retry:
case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT:
stb_textedit_prep_selection_at_cursor(state);
// move selection right
++state->select_end;
state->select_end = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->select_end);
stb_textedit_clamp(str, state);
state->cursor = state->select_end;
state->has_preferred_x = 0;
@ -889,14 +920,14 @@ retry:
x = row.x0;
for (i=0; i < row.num_chars; ++i) {
float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
#ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE
if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE)
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
break;
#endif
x += dx;
if (x > goal_x)
break;
++state->cursor;
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
}
stb_textedit_clamp(str, state);
@ -951,14 +982,14 @@ retry:
x = row.x0;
for (i=0; i < row.num_chars; ++i) {
float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
#ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE
if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE)
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
break;
#endif
x += dx;
if (x > goal_x)
break;
++state->cursor;
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
}
stb_textedit_clamp(str, state);
@ -986,7 +1017,7 @@ retry:
else {
int n = STB_TEXTEDIT_STRINGLEN(str);
if (state->cursor < n)
stb_textedit_delete(str, state, state->cursor, 1);
stb_textedit_delete(str, state, state->cursor, IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor) - state->cursor);
}
state->has_preferred_x = 0;
break;
@ -998,8 +1029,9 @@ retry:
else {
stb_textedit_clamp(str, state);
if (state->cursor > 0) {
stb_textedit_delete(str, state, state->cursor-1, 1);
--state->cursor;
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
stb_textedit_delete(str, state, prev, state->cursor - prev);
state->cursor = prev;
}
}
state->has_preferred_x = 0;
@ -1109,8 +1141,8 @@ retry:
static void stb_textedit_flush_redo(StbUndoState *state)
{
state->redo_point = STB_TEXTEDIT_UNDOSTATECOUNT;
state->redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT;
state->redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT;
state->redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT;
}
// discard the oldest entry in the undo list
@ -1122,13 +1154,13 @@ static void stb_textedit_discard_undo(StbUndoState *state)
int n = state->undo_rec[0].insert_length, i;
// delete n characters from all other records
state->undo_char_point -= n;
STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE)));
IMSTB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(IMSTB_TEXTEDIT_CHARTYPE)));
for (i=0; i < state->undo_point; ++i)
if (state->undo_rec[i].char_storage >= 0)
state->undo_rec[i].char_storage -= n; // @OPTIMIZE: get rid of char_storage and infer it
}
--state->undo_point;
STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0])));
IMSTB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0])));
}
}
@ -1138,7 +1170,7 @@ static void stb_textedit_discard_undo(StbUndoState *state)
// fill up even though the undo buffer didn't
static void stb_textedit_discard_redo(StbUndoState *state)
{
int k = STB_TEXTEDIT_UNDOSTATECOUNT-1;
int k = IMSTB_TEXTEDIT_UNDOSTATECOUNT-1;
if (state->redo_point <= k) {
// if the k'th undo state has characters, clean those up
@ -1146,7 +1178,7 @@ static void stb_textedit_discard_redo(StbUndoState *state)
int n = state->undo_rec[k].insert_length, i;
// move the remaining redo character data to the end of the buffer
state->redo_char_point += n;
STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((STB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE)));
IMSTB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((IMSTB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(IMSTB_TEXTEDIT_CHARTYPE)));
// adjust the position of all the other records to account for above memmove
for (i=state->redo_point; i < k; ++i)
if (state->undo_rec[i].char_storage >= 0)
@ -1154,12 +1186,12 @@ static void stb_textedit_discard_redo(StbUndoState *state)
}
// now move all the redo records towards the end of the buffer; the first one is at 'redo_point'
// [DEAR IMGUI]
size_t move_size = (size_t)((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0]));
size_t move_size = (size_t)((IMSTB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0]));
const char* buf_begin = (char*)state->undo_rec; (void)buf_begin;
const char* buf_end = (char*)state->undo_rec + sizeof(state->undo_rec); (void)buf_end;
IM_ASSERT(((char*)(state->undo_rec + state->redo_point)) >= buf_begin);
IM_ASSERT(((char*)(state->undo_rec + state->redo_point + 1) + move_size) <= buf_end);
STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size);
IMSTB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size);
// now move redo_point to point to the new one
++state->redo_point;
@ -1173,32 +1205,32 @@ static StbUndoRecord *stb_text_create_undo_record(StbUndoState *state, int numch
// if we have no free records, we have to make room, by sliding the
// existing records down
if (state->undo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
if (state->undo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
stb_textedit_discard_undo(state);
// if the characters to store won't possibly fit in the buffer, we can't undo
if (numchars > STB_TEXTEDIT_UNDOCHARCOUNT) {
if (numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT) {
state->undo_point = 0;
state->undo_char_point = 0;
return NULL;
}
// if we don't have enough free characters in the buffer, we have to make room
while (state->undo_char_point + numchars > STB_TEXTEDIT_UNDOCHARCOUNT)
while (state->undo_char_point + numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT)
stb_textedit_discard_undo(state);
return &state->undo_rec[state->undo_point++];
}
static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len)
static IMSTB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len)
{
StbUndoRecord *r = stb_text_create_undo_record(state, insert_len);
if (r == NULL)
return NULL;
r->where = pos;
r->insert_length = (STB_TEXTEDIT_POSITIONTYPE) insert_len;
r->delete_length = (STB_TEXTEDIT_POSITIONTYPE) delete_len;
r->insert_length = (IMSTB_TEXTEDIT_POSITIONTYPE) insert_len;
r->delete_length = (IMSTB_TEXTEDIT_POSITIONTYPE) delete_len;
if (insert_len == 0) {
r->char_storage = -1;
@ -1210,7 +1242,7 @@ static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos,
}
}
static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{
StbUndoState *s = &state->undostate;
StbUndoRecord u, *r;
@ -1237,7 +1269,7 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
// characters stored for *undoing* don't leave room for redo
// if the last is true, we have to bail
if (s->undo_char_point + u.delete_length >= STB_TEXTEDIT_UNDOCHARCOUNT) {
if (s->undo_char_point + u.delete_length >= IMSTB_TEXTEDIT_UNDOCHARCOUNT) {
// the undo records take up too much character space; there's no space to store the redo characters
r->insert_length = 0;
} else {
@ -1246,7 +1278,7 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
// there's definitely room to store the characters eventually
while (s->undo_char_point + u.delete_length > s->redo_char_point) {
// should never happen:
if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
return;
// there's currently not enough room, so discard a redo record
stb_textedit_discard_redo(s);
@ -1278,11 +1310,11 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
s->redo_point--;
}
static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{
StbUndoState *s = &state->undostate;
StbUndoRecord *u, r;
if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
return;
// we need to do two things: apply the redo record, and create an undo record
@ -1334,20 +1366,20 @@ static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int le
stb_text_createundo(&state->undostate, where, 0, length);
}
static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length)
static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length)
{
int i;
STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0);
IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0);
if (p) {
for (i=0; i < length; ++i)
p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
}
}
static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length)
static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length)
{
int i;
STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length);
IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length);
if (p) {
for (i=0; i < old_length; ++i)
p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
@ -1359,8 +1391,8 @@ static void stb_textedit_clear_state(STB_TexteditState *state, int is_single_lin
{
state->undostate.undo_point = 0;
state->undostate.undo_char_point = 0;
state->undostate.redo_point = STB_TEXTEDIT_UNDOSTATECOUNT;
state->undostate.redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT;
state->undostate.redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT;
state->undostate.redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT;
state->select_end = state->select_start = 0;
state->cursor = 0;
state->has_preferred_x = 0;
@ -1383,16 +1415,16 @@ static void stb_textedit_initialize_state(STB_TexteditState *state, int is_singl
#pragma GCC diagnostic ignored "-Wcast-qual"
#endif
static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len)
static int stb_textedit_paste(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE const *ctext, int len)
{
return stb_textedit_paste_internal(str, state, (STB_TEXTEDIT_CHARTYPE *) ctext, len);
return stb_textedit_paste_internal(str, state, (IMSTB_TEXTEDIT_CHARTYPE *) ctext, len);
}
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif
#endif//STB_TEXTEDIT_IMPLEMENTATION
#endif//IMSTB_TEXTEDIT_IMPLEMENTATION
/*
------------------------------------------------------------------------------

View File

@ -656,7 +656,7 @@ STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h
STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip);
// If skip != 0, this tells stb_truetype to skip any codepoints for which
// there is no corresponding glyph. If skip=0, which is the default, then
// codepoints without a glyph recived the font's "missing character" glyph,
// codepoints without a glyph received the font's "missing character" glyph,
// typically an empty box by convention.
STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above

View File

@ -8,8 +8,15 @@
// https://github.com/ocornut/imgui/wiki/Useful-Extensions#cness
#include "imgui.h"
#ifndef IMGUI_DISABLE
#include "imgui_stdlib.h"
// Clang warnings with -Weverything
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
#endif
struct InputTextCallback_UserData
{
std::string* Str;
@ -73,3 +80,9 @@ bool ImGui::InputTextWithHint(const char* label, const char* hint, std::string*
cb_user_data.ChainCallbackUserData = user_data;
return InputTextWithHint(label, hint, (char*)str->c_str(), str->capacity() + 1, flags, InputTextCallback, &cb_user_data);
}
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
#endif // #ifndef IMGUI_DISABLE

View File

@ -9,6 +9,8 @@
#pragma once
#ifndef IMGUI_DISABLE
#include <string>
namespace ImGui
@ -19,3 +21,5 @@ namespace ImGui
IMGUI_API bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr);
IMGUI_API bool InputTextWithHint(const char* label, const char* hint, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr);
}
#endif // #ifndef IMGUI_DISABLE

@ -1 +1 @@
Subproject commit cc5e1daa5c7f2335a9460ae79c829011dc5cef2d
Subproject commit 18c72431f8265e2b0b5378a3a73d8a883b2175ff

View File

@ -3,8 +3,8 @@
#pragma once
#include <array>
#include <cassert>
#include <cstddef>
#include <type_traits>
#include <utility>
namespace Common
@ -14,39 +14,99 @@ namespace Common
template <typename T, size_t MaxSize>
class SmallVector final
{
static_assert(std::is_standard_layout_v<T> == true, "Type must be a standard layout type");
public:
using value_type = T;
SmallVector() = default;
explicit SmallVector(size_t size) : m_size(size) {}
explicit SmallVector(size_t new_size) { resize(new_size); }
void push_back(const T& x) { m_array[m_size++] = x; }
void push_back(T&& x) { m_array[m_size++] = std::move(x); }
~SmallVector() { clear(); }
template <typename... Args>
T& emplace_back(Args&&... args)
SmallVector(const SmallVector& other)
{
return m_array[m_size++] = T{std::forward<Args>(args)...};
for (auto& value : other)
emplace_back(value);
}
T& operator[](size_t i) { return m_array[i]; }
const T& operator[](size_t i) const { return m_array[i]; }
SmallVector& operator=(const SmallVector& rhs)
{
clear();
for (auto& value : rhs)
emplace_back(value);
return *this;
}
auto data() { return m_array.data(); }
auto begin() { return m_array.begin(); }
auto end() { return m_array.begin() + m_size; }
SmallVector(SmallVector&& other)
{
for (auto& value : other)
emplace_back(std::move(value));
other.clear();
}
auto data() const { return m_array.data(); }
auto begin() const { return m_array.begin(); }
auto end() const { return m_array.begin() + m_size; }
SmallVector& operator=(SmallVector&& rhs)
{
clear();
for (auto& value : rhs)
emplace_back(std::move(value));
rhs.clear();
return *this;
}
void push_back(const value_type& x) { emplace_back(x); }
void push_back(value_type&& x) { emplace_back(std::move(x)); }
template <typename... Args>
value_type& emplace_back(Args&&... args)
{
assert(m_size < MaxSize);
return *::new (&m_array[m_size++ * sizeof(value_type)]) value_type{std::forward<Args>(args)...};
}
void pop_back()
{
assert(m_size > 0);
std::destroy_at(data() + --m_size);
}
value_type& operator[](size_t i)
{
assert(i < m_size);
return data()[i];
}
const value_type& operator[](size_t i) const
{
assert(i < m_size);
return data()[i];
}
auto data() { return std::launder(reinterpret_cast<value_type*>(m_array.data())); }
auto begin() { return data(); }
auto end() { return data() + m_size; }
auto data() const { return std::launder(reinterpret_cast<const value_type*>(m_array.data())); }
auto begin() const { return data(); }
auto end() const { return data() + m_size; }
size_t capacity() const { return MaxSize; }
size_t size() const { return m_size; }
bool empty() const { return m_size == 0; }
void clear() { m_size = 0; }
void resize(size_t new_size)
{
assert(new_size <= MaxSize);
while (size() < new_size)
emplace_back();
while (size() > new_size)
pop_back();
}
void clear() { resize(0); }
private:
std::array<T, MaxSize> m_array{};
alignas(value_type) std::array<std::byte, MaxSize * sizeof(value_type)> m_array;
size_t m_size = 0;
};

View File

@ -183,10 +183,7 @@ void LoadPatches()
LoadPatchSection("OnFrame", &s_on_frame, globalIni, localIni);
#ifdef USE_RETRO_ACHIEVEMENTS
{
std::lock_guard lg{AchievementManager::GetInstance().GetLock()};
AchievementManager::GetInstance().FilterApprovedPatches(s_on_frame, sconfig.GetGameID());
}
AchievementManager::GetInstance().FilterApprovedPatches(s_on_frame, sconfig.GetGameID());
#endif // USE_RETRO_ACHIEVEMENTS
// Check if I'm syncing Codes

View File

@ -156,7 +156,7 @@ void JitArm64::FixGTBeforeSettingCRFieldBit(ARM64Reg reg)
// doesn't accidentally become considered set. Gross but necessary; this can break actual games.
auto WA = gpr.GetScopedReg();
ARM64Reg XA = EncodeRegTo64(WA);
ORR(XA, reg, LogicalImm(1ULL << 63, GPRSize::B64));
MOVI2R(XA, 1ULL << 63);
CMP(reg, ARM64Reg::ZR);
CSEL(reg, reg, XA, CC_NEQ);
}

View File

@ -161,6 +161,8 @@ void ControlExpressionSyntaxHighlighter::highlightBlock(const QString&)
case TokenType::TOK_LPAREN:
case TokenType::TOK_RPAREN:
case TokenType::TOK_COMMA:
case TokenType::TOK_QUESTION:
case TokenType::TOK_COLON:
char_format = GetSpecialCharFormat();
break;
case TokenType::TOK_LITERAL:
@ -304,6 +306,7 @@ void IOWindow::CreateMainLayout()
m_operators_combo->addItem(tr("< Less-than"));
m_operators_combo->addItem(tr("& And"));
m_operators_combo->addItem(tr("^ Xor"));
m_operators_combo->addItem(tr("? Conditional"));
}
m_operators_combo->addItem(tr("| Or"));
m_operators_combo->addItem(tr("$ User Variable"));

View File

@ -579,3 +579,8 @@ void MappingWindow::ShowExtensionMotionTabs(bool show)
m_tab_widget->removeTab(4);
}
}
void MappingWindow::ActivateExtensionTab()
{
m_tab_widget->setCurrentIndex(3);
}

View File

@ -53,6 +53,7 @@ public:
ControllerEmu::EmulatedController* GetController() const;
bool IsMappingAllDevices() const;
void ShowExtensionMotionTabs(bool show);
void ActivateExtensionTab();
signals:
// Emitted when config has changed so widgets can update to reflect the change.

View File

@ -59,7 +59,13 @@ void WiimoteEmuGeneral::CreateMainLayout()
extension->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
static_cast<QFormLayout*>(extension->layout())->insertRow(0, combo_hbox);
auto* const ext_layout = static_cast<QFormLayout*>(extension->layout());
ext_layout->insertRow(0, combo_hbox);
m_configure_ext_button = new QPushButton(tr("Configure Extension"));
m_configure_ext_button->setDisabled(true);
m_configure_ext_button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
ext_layout->insertRow(1, m_configure_ext_button);
layout->addWidget(extension, 0, 3);
layout->addWidget(CreateGroupBox(tr("Rumble"), Wiimote::GetWiimoteGroup(
@ -81,6 +87,8 @@ void WiimoteEmuGeneral::Connect()
connect(m_extension_combo, &QComboBox::activated, this, &WiimoteEmuGeneral::OnAttachmentSelected);
connect(this, &MappingWidget::ConfigChanged, this, &WiimoteEmuGeneral::ConfigChanged);
connect(this, &MappingWidget::Update, this, &WiimoteEmuGeneral::Update);
connect(m_configure_ext_button, &QPushButton::clicked, GetParent(),
&MappingWindow::ActivateExtensionTab);
}
void WiimoteEmuGeneral::OnAttachmentChanged(int extension)
@ -88,6 +96,8 @@ void WiimoteEmuGeneral::OnAttachmentChanged(int extension)
GetParent()->ShowExtensionMotionTabs(extension == WiimoteEmu::ExtensionNumber::NUNCHUK);
m_extension_widget->ChangeExtensionType(extension);
m_configure_ext_button->setEnabled(extension != WiimoteEmu::ExtensionNumber::NONE);
}
void WiimoteEmuGeneral::OnAttachmentSelected(int extension)

View File

@ -34,6 +34,7 @@ private:
// Extensions
QComboBox* m_extension_combo;
QLabel* m_extension_combo_dynamic_indicator;
QPushButton* m_configure_ext_button;
WiimoteEmuExtension* m_extension_widget;
};

View File

@ -8,12 +8,14 @@
#include <QApplication>
#include <QClipboard>
#include <QColorDialog>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QLineEdit>
#include <QMenu>
#include <QMouseEvent>
#include <QScrollBar>
#include <QSignalBlocker>
#include <QStyledItemDelegate>
#include <QTableWidget>
#include <QtGlobal>
@ -42,6 +44,7 @@ constexpr int MISC_COLUMNS = 2;
constexpr auto USER_ROLE_IS_ROW_BREAKPOINT_CELL = Qt::UserRole;
constexpr auto USER_ROLE_CELL_ADDRESS = Qt::UserRole + 1;
constexpr auto USER_ROLE_VALUE_TYPE = Qt::UserRole + 2;
constexpr auto USER_ROLE_VALID_ADDRESS = Qt::UserRole + 3;
// Numbers for the scrollbar. These affect how much big the draggable part of the scrollbar is, how
// smooth it scrolls, and how much memory it traverses while dragging.
@ -52,6 +55,14 @@ constexpr int SCROLLBAR_CENTER = SCROLLBAR_MAXIMUM / 2;
const QString INVALID_MEMORY = QStringLiteral("-");
void TableEditDelegate::setModelData(QWidget* editor, QAbstractItemModel* model,
const QModelIndex& index) const
{
// Triggers on placing data into a cell. Editor has the text to be input, index has the location.
const QString input = qobject_cast<QLineEdit*>(editor)->text();
emit editFinished(index.row(), index.column(), input);
}
class MemoryViewTable final : public QTableWidget
{
public:
@ -66,6 +77,11 @@ public:
setSelectionMode(NoSelection);
setTextElideMode(Qt::TextElideMode::ElideNone);
// Route user's direct cell inputs to an editFinished signal. Much better than an itemChanged
// signal and QSignalBlock juggling.
TableEditDelegate* table_edit_delegate = new TableEditDelegate(this);
setItemDelegate(table_edit_delegate);
// Prevent colors from changing based on focus.
QPalette palette(m_view->palette());
palette.setBrush(QPalette::Inactive, QPalette::Highlight, palette.brush(QPalette::Highlight));
@ -78,13 +94,17 @@ public:
connect(this, &MemoryViewTable::customContextMenuRequested, m_view,
&MemoryViewWidget::OnContextMenu);
connect(this, &MemoryViewTable::itemChanged, this, &MemoryViewTable::OnItemChanged);
connect(table_edit_delegate, &TableEditDelegate::editFinished, this,
&MemoryViewTable::OnDirectTableEdit);
}
void resizeEvent(QResizeEvent* event) override
{
QTableWidget::resizeEvent(event);
m_view->CreateTable();
// Remakes table if vertically resized
const int rows = std::round((height() / static_cast<float>(rowHeight(0))) - 0.25);
if (rows != rowCount())
m_view->UpdateDispatcher(MemoryViewWidget::UpdateType::Full);
}
void keyPressEvent(QKeyEvent* event) override
@ -93,24 +113,21 @@ public:
{
case Qt::Key_Up:
m_view->m_address -= m_view->m_bytes_per_row;
m_view->Update();
return;
break;
case Qt::Key_Down:
m_view->m_address += m_view->m_bytes_per_row;
m_view->Update();
return;
break;
case Qt::Key_PageUp:
m_view->m_address -= this->rowCount() * m_view->m_bytes_per_row;
m_view->Update();
return;
break;
case Qt::Key_PageDown:
m_view->m_address += this->rowCount() * m_view->m_bytes_per_row;
m_view->Update();
return;
break;
default:
QWidget::keyPressEvent(event);
break;
return;
}
m_view->UpdateDispatcher(MemoryViewWidget::UpdateType::Addresses);
}
void wheelEvent(QWheelEvent* event) override
@ -122,7 +139,7 @@ public:
return;
m_view->m_address += delta * m_view->m_bytes_per_row;
m_view->Update();
m_view->UpdateDispatcher(MemoryViewWidget::UpdateType::Addresses);
}
void mousePressEvent(QMouseEvent* event) override
@ -138,7 +155,6 @@ public:
{
const u32 address = item->data(USER_ROLE_CELL_ADDRESS).toUInt();
m_view->ToggleBreakpoint(address, true);
m_view->Update();
}
else
{
@ -146,9 +162,9 @@ public:
}
}
void OnItemChanged(QTableWidgetItem* item)
void OnDirectTableEdit(const int row, const int column, const QString& text)
{
QString text = item->text();
QTableWidgetItem* item = this->item(row, column);
MemoryViewWidget::Type type =
static_cast<MemoryViewWidget::Type>(item->data(USER_ROLE_VALUE_TYPE).toInt());
std::vector<u8> bytes = m_view->ConvertTextToBytes(type, text);
@ -156,17 +172,18 @@ public:
u32 address = item->data(USER_ROLE_CELL_ADDRESS).toUInt();
u32 end_address = address + static_cast<u32>(bytes.size()) - 1;
AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_view->GetAddressSpace());
const Core::CPUThreadGuard guard(m_view->m_system);
if (!bytes.empty() && accessors->IsValidAddress(guard, address) &&
accessors->IsValidAddress(guard, end_address))
{
for (const u8 c : bytes)
accessors->WriteU8(guard, address++, c);
const Core::CPUThreadGuard guard(m_view->m_system);
if (!bytes.empty() && accessors->IsValidAddress(guard, address) &&
accessors->IsValidAddress(guard, end_address))
{
for (const u8 c : bytes)
accessors->WriteU8(guard, address++, c);
}
}
m_view->Update();
m_view->UpdateDispatcher(MemoryViewWidget::UpdateType::Values);
}
private:
@ -198,13 +215,25 @@ MemoryViewWidget::MemoryViewWidget(Core::System& system, QWidget* parent)
this->setLayout(layout);
connect(&Settings::Instance(), &Settings::DebugFontChanged, this, &MemoryViewWidget::UpdateFont);
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
qOverload<>(&MemoryViewWidget::UpdateColumns));
connect(Host::GetInstance(), &Host::PPCBreakpointsChanged, this,
qOverload<>(&MemoryViewWidget::Update));
connect(Host::GetInstance(), &Host::UpdateDisasmDialog, this,
qOverload<>(&MemoryViewWidget::UpdateColumns));
connect(&Settings::Instance(), &Settings::ThemeChanged, this, &MemoryViewWidget::Update);
&MemoryViewWidget::UpdateBreakpointTags);
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this] {
// UpdateDisasmDialog currently catches pauses, no need to signal it twice.
if (Core::GetState(m_system) != Core::State::Paused)
UpdateDispatcher(UpdateType::Values);
});
connect(Host::GetInstance(), &Host::UpdateDisasmDialog, this, [this] {
// Disasm spam will break updates while running. Only need it for things like steps when paused
// and breaks which trigger a pause.
if (Core::GetState(m_system) != Core::State::Running)
UpdateDispatcher(UpdateType::Values);
});
// CPU Thread to Main Thread.
connect(this, &MemoryViewWidget::AutoUpdate, this,
[this] { UpdateDispatcher(UpdateType::Auto); });
connect(&Settings::Instance(), &Settings::ThemeChanged, this,
[this] { UpdateDispatcher(UpdateType::Full); });
// Also calls create table.
UpdateFont(Settings::Instance().GetDebugFont());
@ -220,7 +249,7 @@ void MemoryViewWidget::UpdateFont(const QFont& font)
m_font_width = fm.horizontalAdvance(QLatin1Char('0'));
m_table->setFont(font);
CreateTable();
UpdateDispatcher(UpdateType::Full);
}
constexpr int GetTypeSize(MemoryViewWidget::Type type)
@ -282,6 +311,42 @@ constexpr int GetCharacterCount(MemoryViewWidget::Type type)
}
}
void MemoryViewWidget::UpdateDispatcher(UpdateType type)
{
std::unique_lock lock(m_updating, std::defer_lock);
// A full update may change parameters like row count, so make sure it goes through.
if (type == UpdateType::Full)
lock.lock();
else if (!isVisible() || !lock.try_lock())
return;
// Check if table needs to be created.
if (m_table->item(2, 1) == nullptr)
type = UpdateType::Full;
switch (type)
{
case UpdateType::Full:
CreateTable();
[[fallthrough]];
case UpdateType::Addresses:
Update();
[[fallthrough]];
case UpdateType::Values:
if (Core::GetState(m_system) == Core::State::Paused)
GetValues();
UpdateColumns();
break;
case UpdateType::Auto:
// Values were captured on CPU thread while doing a callback.
if (m_values.size() != 0)
UpdateColumns();
default:
break;
}
}
void MemoryViewWidget::CreateTable()
{
m_table->clearContents();
@ -292,8 +357,6 @@ void MemoryViewWidget::CreateTable()
m_table->verticalHeader()->setMinimumSectionSize(m_font_vspace);
m_table->horizontalHeader()->setMinimumSectionSize(m_font_width * 2);
const QSignalBlocker blocker(m_table);
// Set column and row parameters.
// Span is the number of unique memory values covered in one row.
const int data_span = m_bytes_per_row / GetTypeSize(m_type);
@ -388,17 +451,10 @@ void MemoryViewWidget::CreateTable()
const int width = m_font_width * GetCharacterCount(m_type);
for (int i = start_fill; i < total_columns; i++)
m_table->setColumnWidth(i, width);
Update();
}
void MemoryViewWidget::Update()
{
// Check if table is created
if (m_table->item(1, 1) == nullptr)
return;
const QSignalBlocker blocker(m_table);
m_table->clearSelection();
// Update addresses
@ -406,6 +462,9 @@ void MemoryViewWidget::Update()
u32 row_address = address - (m_table->rowCount() / 2) * m_bytes_per_row;
const int data_span = m_bytes_per_row / GetTypeSize(m_type);
m_address_range.first = row_address;
m_address_range.second = row_address + m_table->rowCount() * m_bytes_per_row - 1;
for (int i = 0; i < m_table->rowCount(); i++, row_address += m_bytes_per_row)
{
auto* bp_item = m_table->item(i, 0);
@ -426,69 +485,139 @@ void MemoryViewWidget::Update()
item_address = row_address + c * GetTypeSize(m_type);
item->setData(USER_ROLE_CELL_ADDRESS, item_address);
// Reset highlighting.
item->setBackground(Qt::transparent);
item->setData(USER_ROLE_VALID_ADDRESS, false);
}
}
UpdateColumns();
UpdateBreakpointTags();
m_table->viewport()->update();
m_table->update();
update();
}
void MemoryViewWidget::UpdateColumns()
{
if (!isVisible())
return;
// Check if table is created
if (m_table->item(1, 1) == nullptr)
return;
if (Core::GetState(m_system) == Core::State::Paused)
{
const Core::CPUThreadGuard guard(m_system);
UpdateColumns(&guard);
}
else
{
// If the core is running, blank out the view of memory instead of reading anything.
UpdateColumns(nullptr);
}
}
void MemoryViewWidget::UpdateColumns(const Core::CPUThreadGuard* guard)
{
// Check if table is created
if (m_table->item(1, 1) == nullptr)
return;
const QSignalBlocker blocker(m_table);
for (int i = 0; i < m_table->rowCount(); i++)
{
for (int c = 0; c < m_data_columns; c++)
{
auto* cell_item = m_table->item(i, c + MISC_COLUMNS);
if (!cell_item)
return;
const u32 cell_address = cell_item->data(USER_ROLE_CELL_ADDRESS).toUInt();
const Type type = static_cast<Type>(cell_item->data(USER_ROLE_VALUE_TYPE).toInt());
std::optional<QString> new_text;
cell_item->setText(guard ? ValueToString(*guard, cell_address, type) : INVALID_MEMORY);
// Dual view auto sets the type of the left-side based on m_type. Only time type and
// m_type differ.
if (type != m_type)
{
new_text = m_values_dual_view.empty() || !m_values_dual_view.contains(cell_address) ?
std::nullopt :
m_values_dual_view.at(cell_address);
}
else
{
new_text = m_values.empty() || !m_values.contains(cell_address) ? std::nullopt :
m_values.at(cell_address);
}
// Set search address to selected / colored
if (cell_address == m_address_highlight)
cell_item->setSelected(true);
// Color recently changed items.
QColor bcolor = cell_item->background().color();
const bool valid = cell_item->data(USER_ROLE_VALID_ADDRESS).toBool();
// It gets a bit complicated, because invalid addresses becoming valid should not be
// colored.
if (!new_text.has_value())
{
cell_item->setBackground(Qt::transparent);
cell_item->setData(USER_ROLE_VALID_ADDRESS, false);
cell_item->setText(INVALID_MEMORY);
}
else if (!valid)
{
// Wasn't valid on last update, is valid now.
cell_item->setData(USER_ROLE_VALID_ADDRESS, true);
cell_item->setText(new_text.value());
}
else if (bcolor.rgb() != m_highlight_color.rgb() && bcolor != Qt::transparent)
{
// Filter out colors that shouldn't change, such as breakpoints.
cell_item->setText(new_text.value());
}
else if (cell_item->text() != new_text.value())
{
// Cell changed, apply highlighting.
cell_item->setBackground(m_highlight_color);
cell_item->setText(new_text.value());
}
else if (bcolor.alpha() > 0)
{
// Fade out highlighting each frame.
bcolor.setAlpha(bcolor.alpha() - 1);
cell_item->setBackground(bcolor);
}
}
}
}
// Always runs on CPU thread from a callback.
void MemoryViewWidget::UpdateOnFrameEnd()
{
std::unique_lock lock(m_updating, std::try_to_lock);
if (lock)
{
GetValues();
// Should not directly trigger widget updates on a cpu thread. Signal main thread to do it.
emit AutoUpdate();
}
}
void MemoryViewWidget::GetValues()
{
m_values.clear();
m_values_dual_view.clear();
// Check for dual view type
Type type = Type::Null;
if (m_dual_view)
{
if (GetTypeSize(m_type) == 1)
type = Type::Hex8;
else if (GetTypeSize(m_type) == 2)
type = Type::Hex16;
else if (GetTypeSize(m_type) == 8)
type = Type::Hex64;
else
type = Type::Hex32;
}
// Grab memory values as QStrings
Core::CPUThreadGuard guard(m_system);
for (u32 address = m_address_range.first; address <= m_address_range.second;
address += GetTypeSize(m_type))
{
m_values.insert(std::pair(address, ValueToString(guard, address, m_type)));
if (m_dual_view)
m_values_dual_view.insert(std::pair(address, ValueToString(guard, address, type)));
}
}
// May only be called if we have taken on the role of the CPU thread
QString MemoryViewWidget::ValueToString(const Core::CPUThreadGuard& guard, u32 address, Type type)
std::optional<QString> MemoryViewWidget::ValueToString(const Core::CPUThreadGuard& guard,
u32 address, Type type)
{
const AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_address_space);
if (!accessors->IsValidAddress(guard, address))
return INVALID_MEMORY;
return std::nullopt;
switch (type)
{
@ -550,7 +679,7 @@ QString MemoryViewWidget::ValueToString(const Core::CPUThreadGuard& guard, u32 a
return string;
}
default:
return INVALID_MEMORY;
return std::nullopt;
}
}
@ -606,7 +735,7 @@ void MemoryViewWidget::SetAddressSpace(AddressSpace::Type address_space)
}
m_address_space = address_space;
Update();
UpdateDispatcher(UpdateType::Addresses);
}
AddressSpace::Type MemoryViewWidget::GetAddressSpace() const
@ -777,7 +906,55 @@ void MemoryViewWidget::SetDisplay(Type type, int bytes_per_row, int alignment, b
else
m_alignment = alignment;
CreateTable();
UpdateDispatcher(UpdateType::Full);
}
void MemoryViewWidget::ToggleHighlights(bool enabled)
{
// m_highlight_color should hold the current highlight color even when disabled, so it can
// be used if re-enabled. If modifying the enabled alpha, change in .h file as well.
if (enabled)
{
m_highlight_color.setAlpha(100);
}
else
{
// Treated as being interchangable with Qt::transparent.
m_highlight_color.setAlpha(0);
// Immediately remove highlights when paused.
for (int i = 0; i < m_table->rowCount(); i++)
{
for (int c = 0; c < m_data_columns; c++)
m_table->item(i, c + MISC_COLUMNS)->setBackground(m_highlight_color);
}
}
}
void MemoryViewWidget::SetHighlightColor()
{
// Could allow custom alphas to be set, which would change fade-out rate.
QColor color = QColorDialog::getColor(m_highlight_color);
if (!color.isValid())
return;
const bool enabled = m_highlight_color.alpha() != 0;
m_highlight_color = color;
m_highlight_color.setAlpha(enabled ? 100 : 0);
if (!enabled)
return;
// Immediately update colors. Only useful for playing with colors while paused.
for (int i = 0; i < m_table->rowCount(); i++)
{
for (int c = 0; c < m_data_columns; c++)
{
auto* item = m_table->item(i, c + MISC_COLUMNS);
// Get current cell alpha state.
color.setAlpha(item->background().color().alpha());
m_table->item(i, c + MISC_COLUMNS)->setBackground(color);
}
}
}
void MemoryViewWidget::SetBPType(BPType type)
@ -793,7 +970,7 @@ void MemoryViewWidget::SetAddress(u32 address)
m_address = address;
Update();
UpdateDispatcher(UpdateType::Addresses);
}
void MemoryViewWidget::SetBPLoggingEnabled(bool enabled)
@ -916,7 +1093,7 @@ void MemoryViewWidget::ScrollbarActionTriggered(int action)
// User is currently dragging the scrollbar.
// Adjust the memory view by the exact drag difference.
m_address += difference * m_bytes_per_row;
Update();
UpdateDispatcher(UpdateType::Addresses);
}
else
{
@ -931,7 +1108,7 @@ void MemoryViewWidget::ScrollbarActionTriggered(int action)
m_address += (difference < 0 ? -1 : 1) * m_bytes_per_row * m_table->rowCount();
}
Update();
UpdateDispatcher(UpdateType::Addresses);
// Manually reset the draggable part of the bar back to the center.
m_scrollbar->setSliderPosition(SCROLLBAR_CENTER);
}

View File

@ -2,7 +2,9 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <mutex>
#include <QStyledItemDelegate>
#include <QWidget>
#include "Common/CommonTypes.h"
@ -22,6 +24,21 @@ class CPUThreadGuard;
class System;
} // namespace Core
// Captures direct editing of the table.
class TableEditDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit TableEditDelegate(QObject* parent) : QStyledItemDelegate(parent) {}
void setModelData(QWidget* editor, QAbstractItemModel* model,
const QModelIndex& index) const override;
signals:
void editFinished(const int row, const int column, const QString& text) const;
};
class MemoryViewTable;
class MemoryViewWidget final : public QWidget
@ -54,10 +71,21 @@ public:
WriteOnly
};
enum class UpdateType
{
Full,
Addresses,
Values,
Auto,
};
explicit MemoryViewWidget(Core::System& system, QWidget* parent = nullptr);
void CreateTable();
void UpdateDispatcher(UpdateType type = UpdateType::Addresses);
void Update();
void UpdateOnFrameEnd();
void GetValues();
void UpdateFont(const QFont& font);
void ToggleBreakpoint(u32 addr, bool row);
@ -65,6 +93,8 @@ public:
void SetAddressSpace(AddressSpace::Type address_space);
AddressSpace::Type GetAddressSpace() const;
void SetDisplay(Type type, int bytes_per_row, int alignment, bool dual_view);
void ToggleHighlights(bool enabled);
void SetHighlightColor();
void SetBPType(BPType type);
void SetAddress(u32 address);
void SetFocus() const;
@ -72,6 +102,7 @@ public:
void SetBPLoggingEnabled(bool enabled);
signals:
void AutoUpdate();
void ShowCode(u32 address);
void RequestWatch(QString name, u32 address);
@ -81,10 +112,9 @@ private:
void OnCopyHex(u32 addr);
void UpdateBreakpointTags();
void UpdateColumns();
void UpdateColumns(const Core::CPUThreadGuard* guard);
void ScrollbarActionTriggered(int action);
void ScrollbarSliderReleased();
QString ValueToString(const Core::CPUThreadGuard& guard, u32 address, Type type);
std::optional<QString> ValueToString(const Core::CPUThreadGuard& guard, u32 address, Type type);
Core::System& m_system;
@ -95,6 +125,9 @@ private:
BPType m_bp_type = BPType::ReadWrite;
bool m_do_log = true;
u32 m_address = 0x80000000;
std::pair<u32, u32> m_address_range;
std::map<u32, std::optional<QString>> m_values;
std::map<u32, std::optional<QString>> m_values_dual_view;
u32 m_address_highlight = 0;
int m_font_width = 0;
int m_font_vspace = 0;
@ -102,6 +135,8 @@ private:
int m_alignment = 16;
int m_data_columns;
bool m_dual_view = false;
std::mutex m_updating;
QColor m_highlight_color = QColor(120, 255, 255, 100);
friend class MemoryViewTable;
};

View File

@ -66,9 +66,13 @@ MemoryWidget::MemoryWidget(Core::System& system, QWidget* parent)
connect(&Settings::Instance(), &Settings::DebugModeToggled, this,
[this](bool enabled) { setHidden(!enabled || !Settings::Instance().IsMemoryVisible()); });
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, &MemoryWidget::Update);
connect(Host::GetInstance(), &Host::UpdateDisasmDialog, this, &MemoryWidget::Update);
connect(this, &QDockWidget::visibilityChanged, this, [this](bool visible) {
// Stop auto-update if MemoryView is tabbed out.
if (visible && m_auto_update_enabled)
RegisterAfterFrameEventCallback();
else
RemoveAfterFrameEventCallback();
});
LoadSettings();
ConnectWidgets();
@ -250,12 +254,34 @@ void MemoryWidget::CreateWidgets()
// Sidebar top menu
QMenuBar* menubar = new QMenuBar(sidebar);
menubar->setNativeMenuBar(false);
QMenu* menu_views = new QMenu(tr("&View"), menubar);
menubar->addMenu(menu_views);
QMenu* menu_import = new QMenu(tr("&Import"), menubar);
menu_import->addAction(tr("&Load file to current address"), this,
&MemoryWidget::OnSetValueFromFile);
menubar->addMenu(menu_import);
auto* auto_update_action =
menu_views->addAction(tr("Auto update memory values"), this, [this](bool checked) {
m_auto_update_enabled = checked;
if (checked)
RegisterAfterFrameEventCallback();
else
RemoveAfterFrameEventCallback();
});
auto_update_action->setCheckable(true);
auto_update_action->setChecked(true);
auto* highlight_update_action =
menu_views->addAction(tr("Highlight recently changed values"), this,
[this](bool checked) { m_memory_view->ToggleHighlights(checked); });
highlight_update_action->setCheckable(true);
highlight_update_action->setChecked(true);
menu_views->addAction(tr("Highlight color"), this,
[this] { m_memory_view->SetHighlightColor(); });
QMenu* menu_export = new QMenu(tr("&Export"), menubar);
menu_export->addAction(tr("Dump &MRAM"), this, &MemoryWidget::OnDumpMRAM);
menu_export->addAction(tr("Dump &ExRAM"), this, &MemoryWidget::OnDumpExRAM);
@ -340,19 +366,43 @@ void MemoryWidget::ConnectWidgets()
void MemoryWidget::closeEvent(QCloseEvent*)
{
Settings::Instance().SetMemoryVisible(false);
RemoveAfterFrameEventCallback();
}
void MemoryWidget::showEvent(QShowEvent* event)
{
if (m_auto_update_enabled)
RegisterAfterFrameEventCallback();
Update();
}
void MemoryWidget::hideEvent(QHideEvent* event)
{
RemoveAfterFrameEventCallback();
}
void MemoryWidget::RegisterAfterFrameEventCallback()
{
m_vi_end_field_event = VIEndFieldEvent::Register([this] { AutoUpdateTable(); }, "MemoryWidget");
}
void MemoryWidget::RemoveAfterFrameEventCallback()
{
m_vi_end_field_event.reset();
}
void MemoryWidget::AutoUpdateTable()
{
m_memory_view->UpdateOnFrameEnd();
}
void MemoryWidget::Update()
{
if (!isVisible())
return;
m_memory_view->Update();
m_memory_view->UpdateDispatcher(MemoryViewWidget::UpdateType::Addresses);
update();
}

View File

@ -9,6 +9,7 @@
#include <QDockWidget>
#include "Common/CommonTypes.h"
#include "VideoCommon/VideoEvents.h"
class MemoryViewWidget;
class QCheckBox;
@ -76,7 +77,11 @@ private:
void FindValue(bool next);
void closeEvent(QCloseEvent*) override;
void hideEvent(QHideEvent* event) override;
void showEvent(QShowEvent* event) override;
void RegisterAfterFrameEventCallback();
void RemoveAfterFrameEventCallback();
void AutoUpdateTable();
Core::System& m_system;
@ -109,4 +114,7 @@ private:
QRadioButton* m_bp_read_only;
QRadioButton* m_bp_write_only;
QCheckBox* m_bp_log_check;
Common::EventHook m_vi_end_field_event;
bool m_auto_update_enabled = true;
};

View File

@ -171,6 +171,10 @@ Token Lexer::NextToken()
return Token(TOK_RPAREN);
case '@':
return Token(TOK_HOTKEY);
case '?':
return Token(TOK_QUESTION);
case ':':
return Token(TOK_COLON);
case '&':
return Token(TOK_AND);
case '|':
@ -747,7 +751,7 @@ private:
{
// Read one argument.
// Grab an expression, but stop at comma.
auto arg = ParseBinary(BinaryOperatorPrecedence(TOK_COMMA));
auto arg = ParseInfixOperations(OperatorPrecedence(TOK_COMMA));
if (ParseStatus::Successful != arg.status)
return arg;
@ -846,7 +850,7 @@ private:
}
}
static int BinaryOperatorPrecedence(TokenType type)
static constexpr int OperatorPrecedence(TokenType type = TOK_EOF)
{
switch (type)
{
@ -867,16 +871,16 @@ private:
case TOK_OR:
return 6;
case TOK_ASSIGN:
case TOK_QUESTION:
return 7;
case TOK_COMMA:
return 8;
default:
ASSERT(false);
return 0;
return 999;
}
}
ParseResult ParseBinary(int precedence = 999)
ParseResult ParseInfixOperations(int precedence = OperatorPrecedence())
{
ParseResult lhs = ParseAtom(Chew());
@ -886,18 +890,48 @@ private:
std::unique_ptr<Expression> expr = std::move(lhs.expr);
// TODO: handle LTR/RTL associativity?
while (Peek().IsBinaryOperator() && BinaryOperatorPrecedence(Peek().type) < precedence)
while (true)
{
const Token tok = Chew();
ParseResult rhs = ParseBinary(BinaryOperatorPrecedence(tok.type));
if (rhs.status == ParseStatus::SyntaxError)
const Token op = Peek();
if (op.IsBinaryOperator() && OperatorPrecedence(op.type) < precedence)
{
return rhs;
Chew();
ParseResult rhs = ParseInfixOperations(OperatorPrecedence(op.type));
if (rhs.status == ParseStatus::SyntaxError)
return rhs;
expr = std::make_unique<BinaryExpression>(op.type, std::move(expr), std::move(rhs.expr));
}
else if (op.type == TOK_QUESTION && OperatorPrecedence(TOK_QUESTION) <= precedence)
{
// Handle conditional operator: (a ? b : c)
Chew();
auto true_result = ParseInfixOperations(OperatorPrecedence(op.type));
if (true_result.status != ParseStatus::Successful)
return true_result;
expr = std::make_unique<BinaryExpression>(tok.type, std::move(expr), std::move(rhs.expr));
const Token should_be_colon = Chew();
if (should_be_colon.type != TOK_COLON)
return ParseResult::MakeErrorResult(should_be_colon,
Common::GetStringT("Expected colon."));
auto false_result = ParseInfixOperations(OperatorPrecedence(op.type));
if (false_result.status != ParseStatus::Successful)
return false_result;
auto conditional = MakeFunctionExpression("if");
std::vector<std::unique_ptr<Expression>> args;
args.emplace_back(std::move(expr));
args.emplace_back(std::move(true_result.expr));
args.emplace_back(std::move(false_result.expr));
conditional->SetArguments(std::move(args));
expr = std::move(conditional);
}
else
{
break;
}
}
return ParseResult::MakeSuccessfulResult(std::move(expr));
}
@ -950,7 +984,7 @@ private:
return ParseResult::MakeSuccessfulResult(std::make_unique<HotkeyExpression>(std::move(inputs)));
}
ParseResult ParseToplevel() { return ParseBinary(); }
ParseResult ParseToplevel() { return ParseInfixOperations(); }
}; // namespace ExpressionParser
ParseResult ParseTokens(const std::vector<Token>& tokens)

View File

@ -26,6 +26,8 @@ enum TokenType
TOK_BAREWORD,
TOK_COMMENT,
TOK_HOTKEY,
TOK_QUESTION,
TOK_COLON,
// Binary Ops:
TOK_BINARY_OPS_BEGIN,
TOK_AND = TOK_BINARY_OPS_BEGIN,

View File

@ -688,7 +688,7 @@ void Shutdown()
StopScanThread();
#if GCADAPTER_USE_LIBUSB_IMPLEMENTATION
#if LIBUSB_API_HAS_HOTPLUG
if (s_libusb_context->IsValid() && s_libusb_hotplug_enabled)
if (s_libusb_context && s_libusb_context->IsValid() && s_libusb_hotplug_enabled)
libusb_hotplug_deregister_callback(*s_libusb_context, s_hotplug_handle);
#endif
#endif

View File

@ -5,12 +5,17 @@
#include <algorithm>
#include <imgui.h>
#include "Common/Assert.h"
#include "Common/Image.h"
#include "Common/MsgHandler.h"
#include "VideoCommon/AbstractGfx.h"
#include "VideoCommon/AbstractStagingTexture.h"
static_assert(std::is_same_v<AbstractTexture::imgui_texture_id, ImTextureID>,
"The typedef should match ImTextureID in imgui");
AbstractTexture::AbstractTexture(const TextureConfig& c) : m_config(c)
{
}

View File

@ -16,6 +16,10 @@ public:
explicit AbstractTexture(const TextureConfig& c);
virtual ~AbstractTexture() = default;
// Support implicit conversion between AbstractTexture and ImTextureId
using imgui_texture_id = unsigned long long;
operator imgui_texture_id() const { return reinterpret_cast<imgui_texture_id>(this); }
virtual void CopyRectangleFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect,

View File

@ -109,8 +109,8 @@ static float DrawMessage(int index, Message& msg, const ImVec2& position, int ti
if (msg.texture)
{
ImGui::Image(msg.texture.get(), ImVec2(static_cast<float>(msg.icon->width),
static_cast<float>(msg.icon->height)));
ImGui::Image(*msg.texture.get(), ImVec2(static_cast<float>(msg.icon->width),
static_cast<float>(msg.icon->height)));
ImGui::SameLine();
}
}

View File

@ -90,7 +90,7 @@ bool OnScreenUI::Initialize(u32 width, u32 height, float scale)
font_tex->Load(0, font_tex_width, font_tex_height, font_tex_width, font_tex_pixels,
sizeof(u32) * font_tex_width * font_tex_height);
io.Fonts->TexID = font_tex.get();
io.Fonts->TexID = *font_tex.get();
m_imgui_textures.push_back(std::move(font_tex));
}
@ -371,8 +371,8 @@ void OnScreenUI::DrawChallengesAndLeaderboards()
{
for (auto& [name, texture] : m_challenge_texture_map)
{
ImGui::Image(texture.get(), ImVec2(static_cast<float>(texture->GetWidth()) * scale,
static_cast<float>(texture->GetHeight()) * scale));
ImGui::Image(*texture.get(), ImVec2(static_cast<float>(texture->GetWidth()) * scale,
static_cast<float>(texture->GetHeight()) * scale));
ImGui::SameLine();
}
}