mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-09-22 17:20:05 +00:00
Compare commits
58
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2d72bd7ab | ||
|
|
268ca9ab53 | ||
|
|
dea5e88906 | ||
|
|
25c31b0d77 | ||
|
|
ea2066063a | ||
|
|
bdecb6d522 | ||
|
|
739d0977b6 | ||
|
|
717572c202 | ||
|
|
d3931fd3e2 | ||
|
|
a0a6fc0988 | ||
|
|
7d8f0d478e | ||
|
|
d849b83acb | ||
|
|
adbe42c907 | ||
|
|
99b2d9d6d0 | ||
|
|
5c179adb68 | ||
|
|
de6c94ce8f | ||
|
|
743984c318 | ||
|
|
a026ae41f1 | ||
|
|
114dd96e50 | ||
|
|
e2e1382508 | ||
|
|
1c40afc067 | ||
|
|
79677875c4 | ||
|
|
c2e90225b9 | ||
|
|
057a6f3e8e | ||
|
|
a9ec1a05b1 | ||
|
|
2454d426ed | ||
|
|
cf58137b0f | ||
|
|
c40d0b3acc | ||
|
|
dd433c89ff | ||
|
|
d2ec1d6a3a | ||
|
|
f736533933 | ||
|
|
8d82accc26 | ||
|
|
f61688fff5 | ||
|
|
c88f715ac2 | ||
|
|
cac911fbf5 | ||
|
|
1261aa55b0 | ||
|
|
5fe3f56d33 | ||
|
|
0c497abba6 | ||
|
|
625e197bc1 | ||
|
|
d40c9edb90 | ||
|
|
a6a6eed091 | ||
|
|
032df717d8 | ||
|
|
bcdc2f3026 | ||
|
|
3917218a4e | ||
|
|
c0322437d6 | ||
|
|
096d85168a | ||
|
|
735f817b0b | ||
|
|
e30c6553a4 | ||
|
|
f9ab53c0c4 | ||
|
|
d382053500 | ||
|
|
b932e6236a | ||
|
|
b9a7b64280 | ||
|
|
2078af15f9 | ||
|
|
5410fac38e | ||
|
|
ca8727c4af | ||
|
|
6304ad6955 | ||
|
|
1b4c188770 | ||
|
|
e40c980d83 |
@@ -99,6 +99,76 @@ raddbg.cpp
|
||||
If everything worked correctly, there will be a `build` folder in the root
|
||||
level of the codebase, and it will contain a freshly-built `raddbg.exe`.
|
||||
|
||||
## Short-To-Medium-Term Roadmap
|
||||
|
||||
### The Initial Alpha Battle-Testing Phase
|
||||
|
||||
The first priority for the project is to ensure that the most crucial debugger
|
||||
components are functioning extremely reliably for local, x64, Windows
|
||||
debugging. This would include parts like debug info conversion, debug info
|
||||
loading, process control, stepping, evaluation (correct usage of both location
|
||||
info and type info), and a robust frontend which ensures the lower level parts
|
||||
are usable.
|
||||
|
||||
We feel that the debugger has already come a long way in all of these respects,
|
||||
but given the massive set of possible combinations of languages, build
|
||||
settings, toolchains, used language features, and patterns of generated code,
|
||||
there are still cases where the debugger has not been tested, and so there are
|
||||
still issues. So, we feel that the top priority is eliminating these issues,
|
||||
such that the debugging experience is rock solid.
|
||||
|
||||
Additionally, the debug info conversion process is not fast (nor wide) enough
|
||||
to support extremely large projects. This is for two reasons: (a) the
|
||||
PDB-to-RADDBG converter is an unoptimized reference implementation, and (b) the
|
||||
debugger learns of new modules (and thus which PDBs to load) in a
|
||||
serially-dependent way (this is necessarily the case for correct debugging
|
||||
results). We expect that the conversion process' performance can be massively
|
||||
improved, and also that some heuristics can be used to begin converting PDBs
|
||||
to RADDBGs before the debugger knows those PDBs are needed, thus ensuring the
|
||||
associated RADDBG files are ready instantaneously when the associated modules
|
||||
are finally loaded by the debugger. Improving this situation is a major part of
|
||||
this phase, as it will make the debugger much more usable for large projects.
|
||||
|
||||
### Local x64 Linux Debugging Phase
|
||||
|
||||
The next priority for the project is to take the rock solid x64 Windows
|
||||
debugging experience, and port all of the relevant pieces to support local x64
|
||||
Linux debugging also.
|
||||
|
||||
The debugger has been written to abstract over the parts that need to differ on
|
||||
either Linux or Windows, and this is mainly going to be a task in building out
|
||||
different backends for those abstraction layers.
|
||||
|
||||
The major parts of this phase are:
|
||||
|
||||
- Porting the `src/demon` layer to implement the Demon local process control
|
||||
abstraction API.
|
||||
- Porting the `src/unwind` layer to support x64 ELF unwinding (currently, there
|
||||
is only an x64 PE unwinding implementation).
|
||||
- Creating a DWARF-to-RADDBG converter (in the same way that we've built a PDB-
|
||||
to-RADDBG converter). A partial implementation of this is in
|
||||
`src/raddbg_convert/dwarf`.
|
||||
- Porting the `src/render` layer to implement all of the rendering features the
|
||||
frontend needs on a Linux-compatible API (the backend used on Windows is D3D11).
|
||||
- Porting the `src/font_provider` layer to a Linux-compatible font
|
||||
rasterization backend, like FreeType (the backend used on Windows is
|
||||
DirectWrite).
|
||||
- Porting the `src/os` layers to Linux. This includes core operating system
|
||||
abstraction (virtual memory allocation, threading and synchronization
|
||||
primitives, and so on), and graphical operating system abstraction (windows,
|
||||
input events, and so on).
|
||||
|
||||
Once the above list is complete, and once every part is rock solid, the Windows
|
||||
debugging experience we'll have worked diligently to create will also be
|
||||
available natively on Linux machines.
|
||||
|
||||
### And Beyond!
|
||||
|
||||
There are several directions we might take after these two major phases,
|
||||
like remote debugging, porting to different architectures, further improving
|
||||
the debugger's features (like improving the visualization engine), and so on.
|
||||
But for now, we're mostly focused on those first two phases.
|
||||
|
||||
## Top-Level Directory Descriptions
|
||||
|
||||
- `data`: Small binary files which are used when building, either to embed
|
||||
@@ -187,7 +257,7 @@ A list of the layers in the codebase and their associated namespaces is below:
|
||||
- `hash_store` (`HS_`): Implements a cache for general data blobs, keyed by a
|
||||
128-bit hash of the data. Used as a general data store by other layers.
|
||||
- `metagen` (`MG_`): A metaprogram which is used to generate primarily code and
|
||||
data tables. Consumes Metadesk files, stored with the extension `.mc`, and
|
||||
data tables. Consumes Metadesk files, stored with the extension `.mdesk`, and
|
||||
generates C code which is then included by hand-written C code. Currently, it
|
||||
does not analyze the codebase's hand-written C code, but in principle this is
|
||||
possible. This allows easier & less-error-prone management of large data
|
||||
|
||||
@@ -42,12 +42,12 @@ if "%asan%"=="1" set auto_compile_flags=%auto_compile_flags% -fsanitize=add
|
||||
|
||||
:: --- Compile/Link Line Definitions ------------------------------------------
|
||||
set cl_common= /I..\src\ /I..\local\ /nologo /FC /Z7 /MP
|
||||
set clang_common= -I..\src\ -I..\local\ -maes -mssse3 -msse4 -gcodeview -fdiagnostics-absolute-paths -Wall -Wno-missing-braces -Wno-unused-function -Wno-writable-strings -Wno-unused-value -Wno-unused-variable -Wno-unused-local-typedef -Wno-deprecated-register -Wno-deprecated-declarations -Wno-unused-but-set-variable -Wno-single-bit-bitfield-constant-conversion -Xclang -flto-visibility-public-std -D_USE_MATH_DEFINES -Dstrdup=_strdup -Dgnu_printf=printf
|
||||
set cl_debug= call cl /Od /D_DEBUG %cl_common%
|
||||
set clang_common= -I..\src\ -I..\local\ -maes -mssse3 -msse4 -gcodeview -fdiagnostics-absolute-paths -Wall -Wno-unknown-warning-option -Wno-missing-braces -Wno-unused-function -Wno-writable-strings -Wno-unused-value -Wno-unused-variable -Wno-unused-local-typedef -Wno-deprecated-register -Wno-deprecated-declarations -Wno-unused-but-set-variable -Wno-single-bit-bitfield-constant-conversion -Xclang -flto-visibility-public-std -D_USE_MATH_DEFINES -Dstrdup=_strdup -Dgnu_printf=printf
|
||||
set cl_debug= call cl /Od %cl_common%
|
||||
set cl_release= call cl /O2 /DNDEBUG %cl_common%
|
||||
set clang_debug= call clang -g -O0 /D_DEBUG %clang_common%
|
||||
set clang_release= call clang -g -O3 /DNDEBUG %clang_common%
|
||||
set cl_link= /link /natvis:"%~dp0\src\natvis\base.natvis"
|
||||
set clang_debug= call clang -g -O0 %clang_common%
|
||||
set clang_release= call clang -g -O3 -DNDEBUG %clang_common%
|
||||
set cl_link= /link /INCREMENTAL:NO /natvis:"%~dp0\src\natvis\base.natvis"
|
||||
set clang_link= -Xlinker /natvis:"%~dp0\src\natvis\base.natvis"
|
||||
set cl_out= /out:
|
||||
set clang_out= -o
|
||||
@@ -85,14 +85,16 @@ if not "%no_meta%"=="1" (
|
||||
|
||||
:: --- Build Everything (@build_targets) --------------------------------------
|
||||
pushd build
|
||||
if "%raddbg%"=="1" %compile% %gfx% ..\src\raddbg\raddbg.cpp %compile_link% %out%raddbg.exe
|
||||
if "%raddbg%"=="1" %compile% %gfx% ..\src\raddbg\raddbg_main.cpp %compile_link% %out%raddbg.exe
|
||||
if "%raddbg_from_pdb%"=="1" %compile% ..\src\raddbg_convert\pdb\raddbg_from_pdb_main.c %compile_link% %out%raddbg_from_pdb.exe
|
||||
if "%raddbg_from_dwarf%"=="1" %compile% ..\src\raddbg_convert\dwarf\raddbg_from_dwarf.c %compile_link% %out%raddbg_from_dwarf.exe
|
||||
if "%raddbg_dump%"=="1" %compile% ..\src\raddbg_dump\raddbg_dump.c %compile_link% %out%raddbg_dump.exe
|
||||
if "%ryan_scratch%"=="1" %compile% ..\src\scratch\ryan_scratch.c %compile_link% %out%ryan_scratch.exe
|
||||
if "%cpp_tests%"=="1" %compile% ..\src\scratch\i_hate_c_plus_plus.cpp %compile_link% %out%cpp_tests.exe
|
||||
if "%look_at_raddbg%"=="1" %compile% ..\src\scratch\look_at_raddbg.c %compile_link% %out%look_at_raddbg.exe
|
||||
if "%mule_main%"=="1" del vc*.pdb mule*.pdb && %cl_release% /c ..\src\mule\mule_inline.cpp && %cl_release% /c ..\src\mule\mule_o2.cpp && %cl_debug% /EHsc ..\src\mule\mule_main.cpp mule\mule_c.c mule_inline.obj mule_o2.obj
|
||||
if "%mule_main%"=="1" del vc*.pdb mule*.pdb && %cl_release% /MT /c ..\src\mule\mule_inline.cpp && %cl_release% /MT /c ..\src\mule\mule_o2.cpp && %cl_debug% /MT /EHsc ..\src\mule\mule_main.cpp ..\src\mule\mule_c.c mule_inline.obj mule_o2.obj
|
||||
if "%mule_module%"=="1" %compile% ..\src\mule\mule_module.cpp %compile_link% %link_dll% %out%mule_module.dll
|
||||
if "%mule_hotload%"=="1" %compile% ..\src\mule\mule_hotload_main.c %compile_link% %out%mule_hotload.exe & %compile% ..\src\mule\mule_hotload_module_main.c %compile_link% %link_dll% %out%mule_hotload_module.dll
|
||||
popd
|
||||
|
||||
:: --- Unset ------------------------------------------------------------------
|
||||
|
||||
+20
-2
@@ -18,9 +18,8 @@ patterns =
|
||||
"*.bfs",
|
||||
"*.html",
|
||||
"*.txt",
|
||||
"*.ds",
|
||||
"*.md",
|
||||
"*.mc",
|
||||
"*.mdesk",
|
||||
"*.asm",
|
||||
};
|
||||
|
||||
@@ -55,6 +54,15 @@ commands =
|
||||
.save_dirty_files = true,
|
||||
.cursor_at_end = false,
|
||||
},
|
||||
.rjf_f2 =
|
||||
{
|
||||
.win = "build raddbg_from_pdb",
|
||||
.linux = "",
|
||||
.out = "*compilation*",
|
||||
.footer_panel = true,
|
||||
.save_dirty_files = true,
|
||||
.cursor_at_end = false,
|
||||
},
|
||||
.rjf_f3 =
|
||||
{
|
||||
.win = "pushd build && raddbg.exe && popd",
|
||||
@@ -82,6 +90,15 @@ commands =
|
||||
.save_dirty_files = true,
|
||||
.cursor_at_end = false,
|
||||
},
|
||||
.build_raddbg_dump =
|
||||
{
|
||||
.win = "build raddbg_dump",
|
||||
.linux = "",
|
||||
.out = "*compilation*",
|
||||
.footer_panel = true,
|
||||
.save_dirty_files = true,
|
||||
.cursor_at_end = false,
|
||||
},
|
||||
.build_mule_main =
|
||||
{
|
||||
.win = "build mule_main",
|
||||
@@ -113,6 +130,7 @@ fkey_command_override =
|
||||
.rjf =
|
||||
{
|
||||
.F1 = "rjf_f1",
|
||||
.F2 = "rjf_f2",
|
||||
.F3 = "rjf_f3",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -22,7 +22,7 @@ arena_alloc__sized(U64 init_res, U64 init_cmt)
|
||||
#if OS_WINDOWS
|
||||
cmt = res;
|
||||
#else
|
||||
cmt AlignPow2(init_cmt, page_size);
|
||||
cmt = AlignPow2(init_cmt, page_size);
|
||||
#endif
|
||||
|
||||
memory = os_reserve_large(res);
|
||||
|
||||
+52
-42
@@ -1366,6 +1366,7 @@ ctrl_thread__next_demon_event(Arena *arena, CTRL_Msg *msg, DEMON_RunCtrls *run_c
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
|
||||
//- rjf: loop -> try to get event, run, repeat
|
||||
U64 spoof_old_ip_value = 0;
|
||||
ProfScope("loop -> try to get event, run, repeat") for(B32 got_event = 0; got_event == 0;)
|
||||
{
|
||||
//- rjf: get next event
|
||||
@@ -1437,7 +1438,6 @@ ctrl_thread__next_demon_event(Arena *arena, CTRL_Msg *msg, DEMON_RunCtrls *run_c
|
||||
{
|
||||
// rjf: prep spoof
|
||||
B32 do_spoof = (spoof != 0 && run_ctrls->single_step_thread == 0);
|
||||
U64 spoof_old_ip_value = 0;
|
||||
U64 size_of_spoof = 0;
|
||||
if(do_spoof) ProfScope("prep spoof")
|
||||
{
|
||||
@@ -1487,6 +1487,18 @@ ctrl_thread__next_demon_event(Arena *arena, CTRL_Msg *msg, DEMON_RunCtrls *run_c
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: irrespective of what event came back, we should ALWAYS check the
|
||||
// spoof's thread and see if it hit the spoof address, because we may have
|
||||
// simply been sent other debug events first
|
||||
if(spoof != 0)
|
||||
{
|
||||
U64 spoof_thread_rip = demon_read_ip(ctrl_demon_handle_from_ctrl(spoof->thread));
|
||||
if(spoof_thread_rip == spoof->new_ip_value)
|
||||
{
|
||||
demon_write_ip(ctrl_demon_handle_from_ctrl(spoof->thread), spoof_old_ip_value);
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: push ctrl events associated with this demon event
|
||||
CTRL_EventList evts = {0};
|
||||
ProfScope("push ctrl events associated with this demon event") switch(event->kind)
|
||||
@@ -1821,6 +1833,8 @@ ctrl_thread__launch_and_init(CTRL_Msg *msg)
|
||||
str8_lit("wmain"),
|
||||
str8_lit("WinMainCRTStartup"),
|
||||
str8_lit("wWinMainCRTStartup"),
|
||||
str8_lit("mainCRTStartup"),
|
||||
str8_lit("wmainCRTStartup"),
|
||||
};
|
||||
|
||||
// rjf: find voff for one of the custom entry points attached to this msg
|
||||
@@ -2122,10 +2136,14 @@ ctrl_thread__run(CTRL_Msg *msg)
|
||||
DEMON_Handle target_process = ctrl_demon_handle_from_ctrl(msg->parent);
|
||||
U64 spoof_ip_vaddr = 911;
|
||||
|
||||
//////////////////////////////
|
||||
//- rjf: gather processes
|
||||
//
|
||||
DEMON_HandleArray processes = demon_all_processes(scratch.arena);
|
||||
|
||||
//////////////////////////////
|
||||
//- rjf: gather all initial breakpoints
|
||||
//
|
||||
DEMON_TrapChunkList user_traps = {0};
|
||||
{
|
||||
// rjf: resolve module-dependent user bps
|
||||
@@ -2147,6 +2165,7 @@ ctrl_thread__run(CTRL_Msg *msg)
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- rjf: single step "stuck threads"
|
||||
//
|
||||
// "Stuck threads" are threads that are already on a User BP and would hit
|
||||
@@ -2251,7 +2270,9 @@ ctrl_thread__run(CTRL_Msg *msg)
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- rjf: resolve trap net
|
||||
//
|
||||
DEMON_TrapChunkList trap_net_traps = {0};
|
||||
for(CTRL_TrapNode *node = msg->traps.first;
|
||||
node != 0;
|
||||
@@ -2261,14 +2282,18 @@ ctrl_thread__run(CTRL_Msg *msg)
|
||||
demon_trap_chunk_list_push(scratch.arena, &trap_net_traps, 256, &trap);
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- rjf: join user breakpoints and trap net traps
|
||||
//
|
||||
DEMON_TrapChunkList joined_traps = {0};
|
||||
{
|
||||
demon_trap_chunk_list_concat_shallow_copy(scratch.arena, &joined_traps, &user_traps);
|
||||
demon_trap_chunk_list_concat_shallow_copy(scratch.arena, &joined_traps, &trap_net_traps);
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- rjf: record start
|
||||
//
|
||||
if(stop_event == 0)
|
||||
{
|
||||
CTRL_EventList evts = {0};
|
||||
@@ -2277,31 +2302,37 @@ ctrl_thread__run(CTRL_Msg *msg)
|
||||
ctrl_c2u_push_events(&evts);
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- rjf: run loop
|
||||
//
|
||||
if(stop_event == 0)
|
||||
{
|
||||
U64 sp_check_value = demon_read_sp(target_thread);
|
||||
B32 spoof_mode = 0;
|
||||
CTRL_Spoof spoof = {0};
|
||||
U64 spoof_1_return_ip = 0;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
//////////////////////////
|
||||
//- rjf: choose low level traps
|
||||
//
|
||||
DEMON_TrapChunkList *trap_list = &joined_traps;
|
||||
if(spoof_mode)
|
||||
{
|
||||
trap_list = &user_traps;
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
//- rjf: choose spoof
|
||||
//
|
||||
CTRL_Spoof *run_spoof = 0;
|
||||
if(spoof_mode)
|
||||
{
|
||||
run_spoof = &spoof;
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
//- rjf: setup run controls
|
||||
//
|
||||
DEMON_RunCtrls run_ctrls = {0};
|
||||
run_ctrls.ignore_previous_exception = 1;
|
||||
run_ctrls.run_entity_count = msg->freeze_state_threads.count;
|
||||
@@ -2315,12 +2346,16 @@ ctrl_thread__run(CTRL_Msg *msg)
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
run_ctrls.traps = *trap_list;
|
||||
run_ctrls.traps = *trap_list;
|
||||
|
||||
//- rjf: get an event
|
||||
//////////////////////////
|
||||
//- rjf: get next event
|
||||
//
|
||||
DEMON_Event *event = ctrl_thread__next_demon_event(scratch.arena, msg, &run_ctrls, run_spoof);
|
||||
|
||||
//////////////////////////
|
||||
//- rjf: determine event handling
|
||||
//
|
||||
B32 hard_stop = 0;
|
||||
CTRL_EventCause hard_stop_cause = ctrl_event_cause_from_demon_event_kind(event->kind);
|
||||
B32 use_stepping_logic = 0;
|
||||
@@ -2354,7 +2389,9 @@ ctrl_thread__run(CTRL_Msg *msg)
|
||||
}break;
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
//- rjf: unpack info about thread attached to event
|
||||
//
|
||||
Architecture arch = demon_arch_from_object(event->thread);
|
||||
U64 reg_size = regs_block_size_from_architecture(arch);
|
||||
void *thread_regs_block = demon_read_regs(event->thread);
|
||||
@@ -2380,11 +2417,14 @@ ctrl_thread__run(CTRL_Msg *msg)
|
||||
temp_end(temp);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//- rjf: stepping logic -//
|
||||
//////////////////////////
|
||||
//- rjf: stepping logic
|
||||
//
|
||||
//{
|
||||
|
||||
//////////////////////////
|
||||
//- rjf: handle if hitting a spoof or baked in trap
|
||||
//
|
||||
B32 hit_spoof = 0;
|
||||
B32 exception_stop = 0;
|
||||
if(use_stepping_logic)
|
||||
@@ -2409,28 +2449,9 @@ ctrl_thread__run(CTRL_Msg *msg)
|
||||
}
|
||||
}
|
||||
|
||||
//- TODO(rjf): Jeff is hitting a bug where a spoof IP (911) has been
|
||||
// hit by a thread, !!!BUT!!! we seemingly don't catch that until a
|
||||
// subsequent run of this loop, probably because there are other
|
||||
// events in the queue that we report first, losing all state about
|
||||
// spoof mode.
|
||||
//
|
||||
// I'm throwing in some detection for this case, so that we can diagnose
|
||||
// it further from there.
|
||||
//
|
||||
if(event->kind == DEMON_EventKind_Exception &&
|
||||
event->instruction_pointer == 911 &&
|
||||
(hit_spoof == 0 || spoof_mode == 0))
|
||||
{
|
||||
os_graphical_message(1, str8_lit("RADDBG INTERNAL DEVELOPMENT MESSAGE"), str8_lit("a bad, rare bug that Jeff found has been detected to occur - attach with debugger now"));
|
||||
}
|
||||
|
||||
//- rjf: handle spoof hit
|
||||
if(hit_spoof)
|
||||
{
|
||||
// rjf: restore 1 ip
|
||||
demon_write_ip(target_thread, spoof_1_return_ip);
|
||||
|
||||
// rjf: clear spoof mode
|
||||
spoof_mode = 0;
|
||||
MemoryZeroStruct(&spoof);
|
||||
@@ -2673,15 +2694,12 @@ ctrl_thread__run(CTRL_Msg *msg)
|
||||
{
|
||||
// rjf: setup spoof mode
|
||||
begin_spoof_mode = 1;
|
||||
|
||||
U64 spoof_sp = demon_read_sp(target_thread);
|
||||
spoof_mode = 1;
|
||||
spoof.process = ctrl_handle_from_demon(target_process);
|
||||
spoof.thread = ctrl_handle_from_demon(target_thread);
|
||||
spoof.vaddr = spoof_sp;
|
||||
spoof.new_ip_value = spoof_ip_vaddr;
|
||||
|
||||
// rjf: remember 1 return ip
|
||||
demon_read_memory(target_process, &spoof_1_return_ip, spoof_sp, sizeof(spoof_1_return_ip));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2715,7 +2733,8 @@ ctrl_thread__run(CTRL_Msg *msg)
|
||||
}
|
||||
|
||||
//}
|
||||
//- rjf: stepping logic -//
|
||||
//
|
||||
//- rjf: stepping logic
|
||||
////////////////////////////////
|
||||
|
||||
//- rjf: handle step past trap net
|
||||
@@ -2786,20 +2805,11 @@ ctrl_thread__run(CTRL_Msg *msg)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: unstick silently-hit spoofs
|
||||
if(stop_event != 0 && stop_event->thread != target_thread && spoof_mode != 0)
|
||||
{
|
||||
U64 target_thread_rip = demon_read_ip(target_thread);
|
||||
if(target_thread_rip == spoof.new_ip_value)
|
||||
{
|
||||
// rjf: restore 1 ip
|
||||
demon_write_ip(target_thread, spoof_1_return_ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- rjf: record stop
|
||||
//
|
||||
if(stop_event != 0)
|
||||
{
|
||||
CTRL_EventList evts = {0};
|
||||
|
||||
@@ -85,6 +85,7 @@ typedef struct CTRL_Spoof CTRL_Spoof;
|
||||
struct CTRL_Spoof
|
||||
{
|
||||
CTRL_Handle process;
|
||||
CTRL_Handle thread;
|
||||
U64 vaddr;
|
||||
U64 new_ip_value;
|
||||
};
|
||||
@@ -538,7 +539,7 @@ internal Architecture ctrl_arch_from_handle(CTRL_MachineID machine, CTRL_Handle
|
||||
//- rjf: process memory reading/writing
|
||||
internal U64 ctrl_process_read(CTRL_MachineID machine_id, CTRL_Handle process, Rng1U64 range, void *dst);
|
||||
internal String8 ctrl_query_cached_data_from_process_vaddr_range(Arena *arena, CTRL_MachineID machine_id, CTRL_Handle process, Rng1U64 range);
|
||||
internal String8 ctrl_query_cached_zero_terminated_data_from_process_vaddr_limit(Arena *arena, CTRL_MachineID machine_id, CTRL_Handle process, U64 vaddr, U64 limit);
|
||||
internal String8 ctrl_query_cached_zero_terminated_data_from_process_vaddr_limit(Arena *arena, CTRL_MachineID machine_id, CTRL_Handle process, U64 vaddr, U64 limit, U64 endt_us);
|
||||
internal B32 ctrl_process_write_data(CTRL_MachineID machine_id, CTRL_Handle process, U64 vaddr, String8 data);
|
||||
internal U128 ctrl_stored_hash_from_process_vaddr_range(CTRL_MachineID machine_id, CTRL_Handle process, Rng1U64 range, B32 zero_terminated);
|
||||
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ struct DASM_InstChunkNode
|
||||
U64 count;
|
||||
};
|
||||
|
||||
typedef struct DASM_InstChunkNode DASM_InstChunkNode;
|
||||
typedef struct DASM_InstChunkList DASM_InstChunkList;
|
||||
struct DASM_InstChunkList
|
||||
{
|
||||
DASM_InstChunkNode *first;
|
||||
|
||||
+38
-66
@@ -44,7 +44,6 @@ dbgi_init(void)
|
||||
{
|
||||
dbgi_shared->parse_threads[idx] = os_launch_thread(dbgi_parse_thread_entry_point, (void *)idx, 0);
|
||||
}
|
||||
dbgi_shared->evictor_thread = os_launch_thread(dbgi_evictor_thread_entry_point, 0, 0);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
@@ -253,9 +252,34 @@ dbgi_binary_close(String8 exe_path)
|
||||
break;
|
||||
}
|
||||
}
|
||||
B32 need_deletion = 0;
|
||||
if(binary != 0 && binary->refcount>0)
|
||||
{
|
||||
binary->refcount -= 1;
|
||||
need_deletion = (binary->refcount == 0);
|
||||
}
|
||||
if(need_deletion) for(;;)
|
||||
{
|
||||
os_rw_mutex_drop_w(stripe->rw_mutex);
|
||||
for(U64 start_t = os_now_microseconds();
|
||||
os_now_microseconds() <= start_t + 250;);
|
||||
os_rw_mutex_take_w(stripe->rw_mutex);
|
||||
if(binary->refcount == 0 && ins_atomic_u64_eval(&binary->scope_touch_count) == 0)
|
||||
{
|
||||
if(binary->parse.arena != 0) { arena_release(binary->parse.arena); }
|
||||
if(binary->parse.exe_base != 0) { os_file_map_view_close(binary->exe_file_map, binary->parse.exe_base); }
|
||||
if(!os_handle_match(os_handle_zero(), binary->exe_file_map)) { os_file_map_close(binary->exe_file_map); }
|
||||
if(!os_handle_match(os_handle_zero(), binary->exe_file)) { os_file_close(binary->exe_file); }
|
||||
if(binary->parse.dbg_base != 0) { os_file_map_view_close(binary->dbg_file_map, binary->parse.dbg_base); }
|
||||
if(!os_handle_match(os_handle_zero(), binary->dbg_file_map)) { os_file_map_close(binary->dbg_file_map); }
|
||||
if(!os_handle_match(os_handle_zero(), binary->dbg_file)) { os_file_close(binary->dbg_file); }
|
||||
binary->exe_file_map = binary->exe_file = os_handle_zero();
|
||||
binary->dbg_file_map = binary->dbg_file = os_handle_zero();
|
||||
MemoryZeroStruct(&binary->parse);
|
||||
binary->last_time_enqueued_for_parse_us = 0;
|
||||
binary->gen = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
scratch_end(scratch);
|
||||
@@ -294,7 +318,7 @@ dbgi_parse_from_exe_path(DBGI_Scope *scope, String8 exe_path, U64 endt_us)
|
||||
break;
|
||||
}
|
||||
else if(!sent &&
|
||||
os_now_microseconds() >= ins_atomic_u64_eval(&binary->last_time_enqueued_for_parse_us)+1000000 &&
|
||||
ins_atomic_u64_eval(&binary->last_time_enqueued_for_parse_us) == 0 &&
|
||||
dbgi_u2p_enqueue_exe_path(exe_path, endt_us))
|
||||
{
|
||||
sent = 1;
|
||||
@@ -451,7 +475,7 @@ dbgi_parse_thread_entry_point(void *p)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(binary == 0 || binary->flags&DBGI_BinaryFlag_ParseInFlight)
|
||||
if(binary == 0 || binary->flags&DBGI_BinaryFlag_ParseInFlight || binary->refcount == 0)
|
||||
{
|
||||
task_is_taken_by_other_thread = 1;
|
||||
}
|
||||
@@ -708,8 +732,10 @@ dbgi_parse_thread_entry_point(void *p)
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: cache write, step 1: check if either EXE or raddbg file is new. if
|
||||
// so, clear all old results & store new top-level info
|
||||
//- rjf: cache write, step 1: check if refcount is still nonzero, &
|
||||
// either EXE or raddbg file is new. if so, clear all old results &
|
||||
// store new top-level info
|
||||
B32 binary_refcount_is_zero = 0;
|
||||
B32 raddbg_or_exe_file_is_updated = 0;
|
||||
if(do_task) ProfScope("cache write, step 1: check if raddbg is new & clear")
|
||||
{
|
||||
@@ -717,6 +743,11 @@ dbgi_parse_thread_entry_point(void *p)
|
||||
{
|
||||
if(str8_match(bin->exe_path, exe_path, 0))
|
||||
{
|
||||
if(bin->refcount == 0)
|
||||
{
|
||||
binary_refcount_is_zero = 1;
|
||||
break;
|
||||
}
|
||||
if(bin->parse.dbg_props.modified != raddbg_file_props.modified ||
|
||||
bin->parse.exe_props.modified != exe_file_props.modified)
|
||||
{
|
||||
@@ -751,7 +782,7 @@ dbgi_parse_thread_entry_point(void *p)
|
||||
|
||||
//- rjf: raddbg file or exe is not new? cache can stay unmodified, close
|
||||
// handles & skip to end.
|
||||
if(do_task) if(!raddbg_or_exe_file_is_updated) if(raddbg_file_is_up_to_date)
|
||||
if(do_task) if((!raddbg_or_exe_file_is_updated && raddbg_file_is_up_to_date) || binary_refcount_is_zero)
|
||||
{
|
||||
os_file_map_view_close(raddbg_file_map, raddbg_file_base);
|
||||
os_file_map_close(raddbg_file_map);
|
||||
@@ -759,6 +790,7 @@ dbgi_parse_thread_entry_point(void *p)
|
||||
os_file_map_view_close(exe_file_map, exe_file_base);
|
||||
os_file_map_close(exe_file_map);
|
||||
os_file_close(exe_file);
|
||||
arena_release(parse_arena);
|
||||
do_task = 0;
|
||||
}
|
||||
|
||||
@@ -826,63 +858,3 @@ dbgi_parse_thread_entry_point(void *p)
|
||||
scratch_end(scratch);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Evictor Thread
|
||||
|
||||
internal void
|
||||
dbgi_evictor_thread_entry_point(void *p)
|
||||
{
|
||||
TCTX tctx_;
|
||||
tctx_init_and_equip(&tctx_);
|
||||
ProfThreadName("[dbgi] evictor");
|
||||
for(;;)
|
||||
{
|
||||
ProfBegin("eviction scan");
|
||||
U64 slots_per_stripe = dbgi_shared->binary_slots_count/dbgi_shared->binary_stripes_count;
|
||||
for(U64 stripe_idx = 0; stripe_idx < dbgi_shared->binary_stripes_count; stripe_idx += 1)
|
||||
{
|
||||
DBGI_BinaryStripe *stripe = &dbgi_shared->binary_stripes[stripe_idx];
|
||||
for(U64 slot_in_stripe_idx = 0; slot_in_stripe_idx < slots_per_stripe; slot_in_stripe_idx += 1)
|
||||
{
|
||||
U64 slot_idx = slots_per_stripe*stripe_idx + slot_in_stripe_idx;
|
||||
DBGI_BinarySlot *slot = &dbgi_shared->binary_slots[slot_idx];
|
||||
B32 slot_needs_work = 0;
|
||||
OS_MutexScopeR(stripe->rw_mutex)
|
||||
{
|
||||
for(DBGI_Binary *bin = slot->first; bin != 0; bin = bin->next)
|
||||
{
|
||||
if(bin->refcount == 0 && bin->scope_touch_count == 0 && bin->flags == 0 && bin->gen > 1)
|
||||
{
|
||||
slot_needs_work = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(slot_needs_work) ProfScope("eviction task (slot %I64u)", slot_idx) OS_MutexScopeW(stripe->rw_mutex)
|
||||
{
|
||||
for(DBGI_Binary *bin = slot->first; bin != 0; bin = bin->next)
|
||||
{
|
||||
if(bin->refcount == 0 && bin->scope_touch_count == 0 && bin->flags == 0)
|
||||
{
|
||||
if(bin->parse.arena != 0) { arena_release(bin->parse.arena); }
|
||||
if(bin->parse.exe_base != 0) { os_file_map_view_close(bin->exe_file_map, bin->parse.exe_base); }
|
||||
if(!os_handle_match(os_handle_zero(), bin->exe_file_map)) { os_file_map_close(bin->exe_file_map); }
|
||||
if(!os_handle_match(os_handle_zero(), bin->exe_file)) { os_file_close(bin->exe_file); }
|
||||
if(bin->parse.dbg_base != 0) { os_file_map_view_close(bin->dbg_file_map, bin->parse.dbg_base); }
|
||||
if(!os_handle_match(os_handle_zero(), bin->dbg_file_map)) { os_file_map_close(bin->dbg_file_map); }
|
||||
if(!os_handle_match(os_handle_zero(), bin->dbg_file)) { os_file_close(bin->dbg_file); }
|
||||
bin->exe_file_map = bin->exe_file = os_handle_zero();
|
||||
bin->dbg_file_map = bin->dbg_file = os_handle_zero();
|
||||
MemoryZeroStruct(&bin->parse);
|
||||
bin->last_time_enqueued_for_parse_us = 0;
|
||||
bin->gen = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ProfEnd();
|
||||
os_sleep_milliseconds(250);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,7 +198,6 @@ struct DBGI_Shared
|
||||
// rjf: threads
|
||||
U64 parse_thread_count;
|
||||
OS_Handle *parse_threads;
|
||||
OS_Handle evictor_thread;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
@@ -254,9 +253,4 @@ internal DBGI_EventList dbgi_p2u_pop_events(Arena *arena, U64 endt_us);
|
||||
|
||||
internal void dbgi_parse_thread_entry_point(void *p);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Evictor Thread
|
||||
|
||||
internal void dbgi_evictor_thread_entry_point(void *p);
|
||||
|
||||
#endif //DBGI_H
|
||||
|
||||
@@ -957,7 +957,9 @@ demon_os_run(Arena *arena, DEMON_OS_RunCtrls *ctrls){
|
||||
}
|
||||
|
||||
// rjf: check if trap
|
||||
B32 is_trap = (!first_bp && exception->ExceptionCode == DEMON_W32_EXCEPTION_BREAKPOINT);
|
||||
B32 is_trap = (!first_bp &&
|
||||
(exception->ExceptionCode == DEMON_W32_EXCEPTION_BREAKPOINT ||
|
||||
exception->ExceptionCode == DEMON_W32_EXCEPTION_STACK_BUFFER_OVERRUN));
|
||||
|
||||
// rjf: check if this trap is currently registered
|
||||
B32 hit_user_trap = 0;
|
||||
@@ -979,7 +981,8 @@ demon_os_run(Arena *arena, DEMON_OS_RunCtrls *ctrls){
|
||||
// TODO(rjf): x86/x64 specific check
|
||||
// TODO(rjf): do we need to check to make sure the instruction
|
||||
// pointer has not changed?
|
||||
hit_explicit_trap = (instruction_byte == 0xCC);
|
||||
hit_explicit_trap = (instruction_byte == 0xCC ||
|
||||
instruction_byte == 0xCD);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1038,6 +1041,11 @@ demon_os_run(Arena *arena, DEMON_OS_RunCtrls *ctrls){
|
||||
e->kind = report_event_kind;
|
||||
}break;
|
||||
|
||||
case DEMON_W32_EXCEPTION_STACK_BUFFER_OVERRUN:
|
||||
{
|
||||
e->kind = DEMON_EventKind_Trap;
|
||||
}break;
|
||||
|
||||
case DEMON_W32_EXCEPTION_SINGLE_STEP:
|
||||
{
|
||||
e->kind = DEMON_EventKind_SingleStep;
|
||||
|
||||
+212
-15
@@ -2642,6 +2642,98 @@ df_debug_info_path_from_module(Arena *arena, DF_Entity *module)
|
||||
////////////////////////////////
|
||||
//~ rjf: Stepping "Trap Net" Builders
|
||||
|
||||
// NOTE(rjf): Stepping Algorithm Overview (2024/01/17)
|
||||
//
|
||||
// The basic idea behind all stepping algorithms in the debugger are setting up
|
||||
// a "trap net". A "trap net" is just a collection of high-level traps that are
|
||||
// meant to "catch" a thread after letting it run. This trap net is submitted
|
||||
// when the debugger frontend sends a "run" command (it is just empty if doing
|
||||
// an actual 'run' or 'continue'). The debugger control thread then uses this
|
||||
// trap net to program a state machine, to appropriately respond to a variety
|
||||
// of debug events which it is passed from the OS.
|
||||
//
|
||||
// These are "high-level traps" because they can have specific behavioral info
|
||||
// attached to them. These are encoded via the `CTRL_TrapFlags` type, which
|
||||
// allow expression of the following behaviors:
|
||||
//
|
||||
// - end-stepping: when this trap is hit, it will end the stepping operation,
|
||||
// and the target will not continue.
|
||||
// - ignore-stack-pointer-check: when a trap in the trap net is hit, it will
|
||||
// by-default be ignored if the thread's stack pointer has changed. this
|
||||
// flag disables that behavior, for when the stack pointer is expected to
|
||||
// change (e.g. step-out).
|
||||
// - single-step-after-hit: when a trap with this flag is hit, the debugger
|
||||
// will immediately single-step the thread which hit it.
|
||||
// - save-stack-pointer: when a trap with this flag is hit, it will rewrite
|
||||
// the stack pointer which is used to compare against, when deciding
|
||||
// whether or not to filter a trap (based on stack pointer changes).
|
||||
// - begin-spoof-mode: this enables "spoof mode". "spoof mode" is a special
|
||||
// mode that disables the trap net entirely, and lets the thread run
|
||||
// freely - but it catches the thread not with a trap, but a false return
|
||||
// address. the debugger will overwrite a specific return address on the
|
||||
// stack. this address will be overwritten with an address which does NOT
|
||||
// point to a valid page, such that when the thread returns out of a
|
||||
// particular call frame, the debugger will receive a debug event, at
|
||||
// which point it can move the thread back to the correct return address,
|
||||
// and resume with the trap net enabled. this is used in "step over"
|
||||
// operations, because it avoids target <-> debugger "roundtrips" (e.g.
|
||||
// target being stopped, debugger being called with debug events, then
|
||||
// target resumes when debugger's control thread is done running) for
|
||||
// recursions. (it doesn't make a difference with non-recursive calls,
|
||||
// but the debugger can't detect the difference).
|
||||
//
|
||||
// Each stepping command prepares its trap net differently.
|
||||
//
|
||||
// --- Instruction Step Into --------------------------------------------------
|
||||
// In this case, no trap net is prepared, and only a low-level single-step is
|
||||
// performed.
|
||||
//
|
||||
// --- Instruction Step Over --------------------------------------------------
|
||||
// To build a trap net for an instruction-level step-over, the next instruction
|
||||
// at the thread's current instruction pointer is decoded. If it is a call
|
||||
// instruction, or if it is a repeating instruction, then a trap with the
|
||||
// 'end-stepping' behavior is placed at the instruction immediately following
|
||||
// the 'call' instruction.
|
||||
//
|
||||
// --- Line Step Into ---------------------------------------------------------
|
||||
// For a source-line step-into, the thread's instruction pointer is first used
|
||||
// to look up into the debug info's line info, to find the machine code in the
|
||||
// thread's current source line. Every instruction in this range is decoded.
|
||||
// Traps are then built in the following way:
|
||||
//
|
||||
// - 'call' instruction -> if can decode call destination address, place
|
||||
// "end-stepping | ignore-stack-pointer-check" trap at destination. if
|
||||
// can't, "end-stepping | single-step-after | ignore-stack-pointer-check"
|
||||
// trap at call.
|
||||
// - 'jmp' (both unconditional & conditional) -> if can decode jump destination
|
||||
// address, AND if jump leaves the line, place "end-stepping | ignore-
|
||||
// stack-pointer-check" trap at destination. if can't, "end-stepping |
|
||||
// single-step-after | ignore-stack-pointer-check" trap at jmp. if jump
|
||||
// stays within the line, do nothing.
|
||||
// - 'return' -> place "end-stepping | single-step-after" trap at return inst.
|
||||
// - "end-stepping" trap is placed at the first address after the line, to
|
||||
// catch all steps which simply proceed linearly through the instruction
|
||||
// stream.
|
||||
//
|
||||
// --- Line Step Over ---------------------------------------------------------
|
||||
// For a source-line step-over, the thread's instruction pointer is first used
|
||||
// to look up into the debug info's line info, to find the machine code in the
|
||||
// thread's current source line. Every instruction in this range is decoded.
|
||||
// Traps are then built in the following way:
|
||||
//
|
||||
// - 'call' instruction -> place "single-step-after | begin-spoof-mode" trap at
|
||||
// call instruction.
|
||||
// - 'jmp' (both unconditional & conditional) -> if can decode jump destination
|
||||
// address, AND if jump leaves the line, place "end-stepping" trap at
|
||||
// destination. if can't, "end-stepping | single-step-after" trap at jmp.
|
||||
// if jump stays within the line, do nothing.
|
||||
// - 'return' -> place "end-stepping | single-step-after" trap at return inst.
|
||||
// - "end-stepping" trap is placed at the first address after the line, to
|
||||
// catch all steps which simply proceed linearly through the instruction
|
||||
// stream.
|
||||
// - for any instructions which may change the stack pointer, traps are placed
|
||||
// at them with the "save-stack-pointer | single-step-after" behaviors.
|
||||
|
||||
internal CTRL_TrapList
|
||||
df_trap_net_from_thread__step_over_inst(Arena *arena, DF_Entity *thread)
|
||||
{
|
||||
@@ -2998,7 +3090,7 @@ df_symbol_name_from_binary_voff(Arena *arena, DF_Entity *binary, U64 voff)
|
||||
String8 path = df_full_path_from_entity(scratch.arena, binary);
|
||||
DBGI_Parse *dbgi = dbgi_parse_from_exe_path(scope, path, 0);
|
||||
RADDBG_Parsed *rdbg = &dbgi->rdbg;
|
||||
if(rdbg->scope_vmap != 0)
|
||||
if(result.size == 0 && rdbg->scope_vmap != 0)
|
||||
{
|
||||
U64 scope_idx = raddbg_vmap_idx_from_voff(rdbg->scope_vmap, rdbg->scope_vmap_count, voff);
|
||||
RADDBG_Scope *scope = &rdbg->scopes[scope_idx];
|
||||
@@ -3008,6 +3100,14 @@ df_symbol_name_from_binary_voff(Arena *arena, DF_Entity *binary, U64 voff)
|
||||
U8 *name_ptr = raddbg_string_from_idx(rdbg, procedure->name_string_idx, &name_size);
|
||||
result = push_str8_copy(arena, str8(name_ptr, name_size));
|
||||
}
|
||||
if(result.size == 0 && rdbg->global_vmap != 0)
|
||||
{
|
||||
U64 global_idx = raddbg_vmap_idx_from_voff(rdbg->global_vmap, rdbg->global_vmap_count, voff);
|
||||
RADDBG_GlobalVariable *global_var = &rdbg->global_variables[global_idx];
|
||||
U64 name_size = 0;
|
||||
U8 *name_ptr = raddbg_string_from_idx(rdbg, global_var->name_string_idx, &name_size);
|
||||
result = push_str8_copy(arena, str8(name_ptr, name_size));
|
||||
}
|
||||
dbgi_scope_close(scope);
|
||||
scratch_end(scratch);
|
||||
}
|
||||
@@ -4047,6 +4147,19 @@ df_eval_from_string(Arena *arena, DBGI_Scope *scope, DF_CtrlCtx *ctrl_ctx, EVAL_
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: unpack module info & produce eval machine
|
||||
DF_Entity *module = df_module_from_process_vaddr(process, thread_unwind_ip_vaddr);
|
||||
U64 module_base = df_base_vaddr_from_module(module);
|
||||
U64 tls_base = df_tls_base_vaddr_from_thread(thread);
|
||||
EVAL_Machine machine = {0};
|
||||
machine.u = (void *)thread->parent;
|
||||
machine.arch = arch;
|
||||
machine.memory_read = df_eval_memory_read;
|
||||
machine.reg_data = thread_unwind_regs_block;
|
||||
machine.reg_size = reg_size;
|
||||
machine.module_base = &module_base;
|
||||
machine.tls_base = &tls_base;
|
||||
|
||||
//- rjf: lex & parse
|
||||
EVAL_TokenArray tokens = eval_token_array_from_text(arena, string);
|
||||
EVAL_ParseResult parse = eval_parse_expr_from_text_tokens(arena, parse_ctx, string, &tokens);
|
||||
@@ -4075,23 +4188,10 @@ df_eval_from_string(Arena *arena, DBGI_Scope *scope, DF_CtrlCtx *ctrl_ctx, EVAL_
|
||||
bytecode = eval_bytecode_from_oplist(arena, &op_list);
|
||||
}
|
||||
|
||||
//- rjf: grab thread/module
|
||||
DF_Entity *module = df_module_from_process_vaddr(process, thread_unwind_ip_vaddr);
|
||||
|
||||
//- rjf: evaluate
|
||||
EVAL_Result eval = {0};
|
||||
if(bytecode.size != 0)
|
||||
{
|
||||
U64 module_base = df_base_vaddr_from_module(module);
|
||||
U64 tls_base = df_tls_base_vaddr_from_thread(thread);
|
||||
EVAL_Machine machine = {0};
|
||||
machine.u = (void *)thread->parent;
|
||||
machine.arch = arch;
|
||||
machine.memory_read = df_eval_memory_read;
|
||||
machine.reg_data = thread_unwind_regs_block;
|
||||
machine.reg_size = reg_size;
|
||||
machine.module_base = &module_base;
|
||||
machine.tls_base = &tls_base;
|
||||
eval = eval_interpret(&machine, bytecode);
|
||||
}
|
||||
|
||||
@@ -4122,6 +4222,12 @@ df_eval_from_string(Arena *arena, DBGI_Scope *scope, DF_CtrlCtx *ctrl_ctx, EVAL_
|
||||
result.errors = errors;
|
||||
}
|
||||
|
||||
//- rjf: apply dynamic type overrides
|
||||
if(parse.expr != 0 && parse.expr->kind != EVAL_ExprKind_Cast)
|
||||
{
|
||||
result = df_dynamically_typed_eval_from_eval(parse_ctx->type_graph, parse_ctx->rdbg, ctrl_ctx, result);
|
||||
}
|
||||
|
||||
scratch_end(scratch);
|
||||
ProfEnd();
|
||||
return result;
|
||||
@@ -4196,10 +4302,83 @@ df_value_mode_eval_from_eval(TG_Graph *graph, RADDBG_Parsed *rdbg, DF_CtrlCtx *c
|
||||
return eval;
|
||||
}
|
||||
|
||||
internal DF_Eval
|
||||
df_dynamically_typed_eval_from_eval(TG_Graph *graph, RADDBG_Parsed *rdbg, DF_CtrlCtx *ctrl_ctx, DF_Eval eval)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
DF_Entity *thread = df_entity_from_handle(ctrl_ctx->thread);
|
||||
Architecture arch = df_architecture_from_entity(thread);
|
||||
DF_Entity *process = thread->parent;
|
||||
U64 unwind_count = ctrl_ctx->unwind_count;
|
||||
U64 thread_rip_vaddr = df_query_cached_rip_from_thread_unwind(thread, unwind_count);
|
||||
DF_Entity *module = df_module_from_process_vaddr(process, thread_rip_vaddr);
|
||||
TG_Key type_key = eval.type_key;
|
||||
TG_Kind type_kind = tg_kind_from_key(type_key);
|
||||
if(type_kind == TG_Kind_Ptr)
|
||||
{
|
||||
TG_Key ptee_type_key = tg_direct_from_graph_raddbg_key(graph, rdbg, type_key);
|
||||
TG_Kind ptee_type_kind = tg_kind_from_key(ptee_type_key);
|
||||
if(ptee_type_kind == TG_Kind_Struct || ptee_type_kind == TG_Kind_Class)
|
||||
{
|
||||
TG_Type *ptee_type = tg_type_from_graph_raddbg_key(scratch.arena, graph, rdbg, ptee_type_key);
|
||||
B32 has_vtable = 0;
|
||||
for(U64 idx = 0; idx < ptee_type->count; idx += 1)
|
||||
{
|
||||
if(ptee_type->members[idx].kind == TG_MemberKind_VirtualMethod)
|
||||
{
|
||||
has_vtable = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(has_vtable)
|
||||
{
|
||||
U64 ptr_vaddr = eval.offset;
|
||||
U64 addr_size = bit_size_from_arch(arch)/8;
|
||||
String8 ptr_value_memory = ctrl_query_cached_data_from_process_vaddr_range(scratch.arena, process->ctrl_machine_id, process->ctrl_handle,
|
||||
r1u64(ptr_vaddr, ptr_vaddr+addr_size));
|
||||
if(ptr_value_memory.size >= addr_size)
|
||||
{
|
||||
U64 class_base_vaddr = 0;
|
||||
MemoryCopy(&class_base_vaddr, ptr_value_memory.str, addr_size);
|
||||
String8 vtable_base_ptr_memory = ctrl_query_cached_data_from_process_vaddr_range(scratch.arena, process->ctrl_machine_id, process->ctrl_handle,
|
||||
r1u64(class_base_vaddr, class_base_vaddr+addr_size));
|
||||
if(vtable_base_ptr_memory.size >= addr_size)
|
||||
{
|
||||
U64 vtable_vaddr = 0;
|
||||
MemoryCopy(&vtable_vaddr, vtable_base_ptr_memory.str, addr_size);
|
||||
U64 vtable_voff = df_voff_from_vaddr(module, vtable_vaddr);
|
||||
U64 global_idx = raddbg_vmap_idx_from_voff(rdbg->global_vmap, rdbg->global_vmap_count, vtable_voff);
|
||||
if(0 < global_idx && global_idx < rdbg->global_variable_count)
|
||||
{
|
||||
RADDBG_GlobalVariable *global_var = &rdbg->global_variables[global_idx];
|
||||
if(global_var->link_flags & RADDBG_LinkFlag_TypeScoped &&
|
||||
0 < global_var->container_idx && global_var->container_idx < rdbg->udt_count)
|
||||
{
|
||||
RADDBG_UDT *udt = &rdbg->udts[global_var->container_idx];
|
||||
if(0 < udt->self_type_idx && udt->self_type_idx < rdbg->type_node_count)
|
||||
{
|
||||
RADDBG_TypeNode *type = &rdbg->type_nodes[udt->self_type_idx];
|
||||
TG_Key derived_type_key = tg_key_ext(tg_kind_from_raddbg_type_kind(type->kind), (U64)udt->self_type_idx);
|
||||
TG_Key ptr_to_derived_type_key = tg_cons_type_make(graph, TG_Kind_Ptr, derived_type_key, 0);
|
||||
eval.type_key = ptr_to_derived_type_key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
scratch_end(scratch);
|
||||
return eval;
|
||||
}
|
||||
|
||||
internal DF_Eval
|
||||
df_eval_from_eval_cfg_table(Arena *arena, DBGI_Scope *scope, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, DF_Eval eval, DF_CfgTable *cfg)
|
||||
{
|
||||
ProfBeginFunction();
|
||||
|
||||
//- rjf: apply view rules
|
||||
for(DF_CfgVal *val = cfg->first_val; val != 0 && val != &df_g_nil_cfg_val; val = val->linear_next)
|
||||
{
|
||||
DF_CoreViewRuleSpec *spec = df_core_view_rule_spec_from_string(val->string);
|
||||
@@ -4877,6 +5056,7 @@ df_append_viz_blocks_for_parent__rec(Arena *arena, DBGI_Scope *scope, DF_EvalVie
|
||||
//////////////////////////////
|
||||
//- rjf: apply view rules & resolve eval
|
||||
//
|
||||
eval = df_dynamically_typed_eval_from_eval(parse_ctx->type_graph, parse_ctx->rdbg, ctrl_ctx, eval);
|
||||
eval = df_eval_from_eval_cfg_table(arena, scope, ctrl_ctx, parse_ctx, eval, cfg_table);
|
||||
|
||||
//////////////////////////////
|
||||
@@ -4976,6 +5156,15 @@ df_append_viz_blocks_for_parent__rec(Arena *arena, DBGI_Scope *scope, DF_EvalVie
|
||||
for(DF_CfgVal *val = cfg_table->first_val; val != 0 && val != &df_g_nil_cfg_val; val = val->linear_next)
|
||||
{
|
||||
DF_CoreViewRuleSpec *spec = df_core_view_rule_spec_from_string(val->string);
|
||||
if(str8_match(spec->info.string, str8_lit("list"), 0) ||
|
||||
str8_match(spec->info.string, str8_lit("omit"), 0) ||
|
||||
str8_match(spec->info.string, str8_lit("only"), 0))
|
||||
{
|
||||
// TODO(rjf): "list" view rule needs to be formally moved into the visualization
|
||||
// engine hooks when the system is mature enough to support it
|
||||
// also "omit", "only"
|
||||
continue;
|
||||
}
|
||||
if(spec->info.flags & DF_CoreViewRuleSpecInfoFlag_VizBlockProd)
|
||||
{
|
||||
expand_rule = DF_EvalVizExpandRule_ViewRule;
|
||||
@@ -6227,6 +6416,14 @@ df_core_begin_frame(Arena *arena, DF_CmdList *cmds, F32 dt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: exception or unexpected trap -> push error
|
||||
if(event->cause == CTRL_EventCause_InterruptedByException ||
|
||||
event->cause == CTRL_EventCause_InterruptedByTrap)
|
||||
{
|
||||
DF_CmdParams params = df_cmd_params_zero();
|
||||
df_cmd_list_push(arena, cmds, ¶ms, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_Error));
|
||||
}
|
||||
}break;
|
||||
|
||||
//- rjf: entity creation/deletion
|
||||
@@ -7212,7 +7409,7 @@ df_core_begin_frame(Arena *arena, DF_CmdList *cmds, F32 dt)
|
||||
{
|
||||
for(DF_CfgSrc src = (DF_CfgSrc)0; src < DF_CfgSrc_COUNT; src = (DF_CfgSrc)(src+1))
|
||||
{
|
||||
cfg_load[src] = (load_cfg[src] && cfg_timestamps[src] != 0 && ((cfg_save[src] == 0 && df_state->cfg_cached_timestamp[src] != cfg_timestamps[src]) || cfg_files[src]->timestamp == 0));
|
||||
cfg_load[src] = (load_cfg[src] && ((cfg_save[src] == 0 && df_state->cfg_cached_timestamp[src] != cfg_timestamps[src]) || cfg_files[src]->timestamp == 0));
|
||||
cfg_load_any = cfg_load_any || cfg_load[src];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -818,6 +818,7 @@ struct DF_EvalVizRow
|
||||
String8 expr;
|
||||
String8 display_value;
|
||||
String8 edit_value;
|
||||
TG_KeyList inherited_type_key_chain;
|
||||
|
||||
// rjf: variable-size & hook info
|
||||
U64 size_in_rows;
|
||||
@@ -1562,6 +1563,7 @@ internal EVAL_ParseCtx df_eval_parse_ctx_from_module_voff(DBGI_Scope *scope, DF_
|
||||
internal EVAL_ParseCtx df_eval_parse_ctx_from_src_loc(DBGI_Scope *scope, DF_Entity *file, TxtPt pt);
|
||||
internal DF_Eval df_eval_from_string(Arena *arena, DBGI_Scope *scope, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, String8 string);
|
||||
internal DF_Eval df_value_mode_eval_from_eval(TG_Graph *graph, RADDBG_Parsed *rdbg, DF_CtrlCtx *ctrl_ctx, DF_Eval eval);
|
||||
internal DF_Eval df_dynamically_typed_eval_from_eval(TG_Graph *graph, RADDBG_Parsed *rdbg, DF_CtrlCtx *ctrl_ctx, DF_Eval eval);
|
||||
internal DF_Eval df_eval_from_eval_cfg_table(Arena *arena, DBGI_Scope *scope, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, DF_Eval eval, DF_CfgTable *cfg);
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
@@ -80,22 +80,23 @@ DF_EntityKindTable:
|
||||
@table(name, name_lower, c_type)
|
||||
DF_CmdParamSlotTable:
|
||||
{
|
||||
{Window, window, `DF_Handle`}
|
||||
{Panel, panel, `DF_Handle`}
|
||||
{DestPanel, dest_panel, `DF_Handle`}
|
||||
{PrevView, prev_view, `DF_Handle`}
|
||||
{View, view, `DF_Handle`}
|
||||
{Entity, entity, `DF_Handle`}
|
||||
{EntityList, entity_list, `DF_HandleList`}
|
||||
{String, string, `String8`}
|
||||
{FilePath, file_path, `String8`}
|
||||
{TextPoint, text_point, `TxtPt`}
|
||||
{CmdSpec, cmd_spec, `struct DF_CmdSpec *`}
|
||||
{VirtualAddr, vaddr, `U64`}
|
||||
{VirtualOff, voff, `U64`}
|
||||
{Index, index, `U64`}
|
||||
{ID, id, `U64`}
|
||||
{PreferDisassembly, prefer_dasm, `B32`}
|
||||
{Window, window, `DF_Handle`}
|
||||
{Panel, panel, `DF_Handle`}
|
||||
{DestPanel, dest_panel, `DF_Handle`}
|
||||
{PrevView, prev_view, `DF_Handle`}
|
||||
{View, view, `DF_Handle`}
|
||||
{Entity, entity, `DF_Handle`}
|
||||
{EntityList, entity_list, `DF_HandleList`}
|
||||
{String, string, `String8`}
|
||||
{FilePath, file_path, `String8`}
|
||||
{TextPoint, text_point, `TxtPt`}
|
||||
{CmdSpec, cmd_spec, `struct DF_CmdSpec *`}
|
||||
{VirtualAddr, vaddr, `U64`}
|
||||
{VirtualOff, voff, `U64`}
|
||||
{Index, index, `U64`}
|
||||
{ID, id, `U64`}
|
||||
{PreferDisassembly, prefer_dasm, `B32`}
|
||||
{ForceConfirm, force_confirm,`B32`}
|
||||
}
|
||||
|
||||
@table(name, display_string, args_desc)
|
||||
@@ -185,6 +186,10 @@ DF_CoreCmdTable:
|
||||
{CloseWindow 0 0 0 0 0 Window Null Null Null Null 0 0 Window "close_window" "Close Window" "Closes an opened window." "" }
|
||||
{ToggleFullscreen 0 0 0 0 0 Window Null Null Null Null 0 0 Window "toggle_fullscreen" "Toggle Fullscreen" "Toggles fullscreen view on the active window." "" }
|
||||
|
||||
//- rjf: confirmations
|
||||
{ConfirmAccept 1 0 0 0 0 Null Null Null Null Null 0 0 Null "confirm_accept" "Confirm Accept" "Accepts the active confirmation prompt." "" }
|
||||
{ConfirmCancel 1 0 0 0 0 Null Null Null Null Null 0 0 Null "confirm_cancel" "Confirm Cancel" "Cancels the active confirmation prompt." "" }
|
||||
|
||||
//- rjf: panel splitting
|
||||
{ResetToDefaultPanels 0 0 0 0 0 Window Null Null Null Null 0 0 Window "reset_to_default_panels" "Reset To Default Panel Layout" "Resets the window to the default panel layout." "panel" }
|
||||
{NewPanelRight 0 0 0 0 0 Window Panel Null Null Null 0 0 XSplit "new_panel_right" "Split Panel Vertically" "Creates a new panel to the right of the active panel." "panel" }
|
||||
@@ -54,6 +54,8 @@ DF_CmdSpecInfo df_g_core_cmd_kind_spec_info_table[] =
|
||||
{ str8_lit_comp("open_window"), str8_lit_comp("Opens a new window."), str8_lit_comp(""), str8_lit_comp("Open New Window"), (DF_CmdSpecFlag_OmitFromLists*0) | (DF_CmdSpecFlag_RunKeepsQuery*0) | (DF_CmdSpecFlag_QueryUsesOldInput*0) | (DF_CmdSpecFlag_AppliesToView*0) | (DF_CmdSpecFlag_QueryIsCode*0), {DF_CmdParamSlot_Null, DF_CmdParamSlot_Null, DF_CmdParamSlot_Null}, DF_CmdQueryRule_Null, DF_IconKind_Window, {0, 0}},
|
||||
{ str8_lit_comp("close_window"), str8_lit_comp("Closes an opened window."), str8_lit_comp(""), str8_lit_comp("Close Window"), (DF_CmdSpecFlag_OmitFromLists*0) | (DF_CmdSpecFlag_RunKeepsQuery*0) | (DF_CmdSpecFlag_QueryUsesOldInput*0) | (DF_CmdSpecFlag_AppliesToView*0) | (DF_CmdSpecFlag_QueryIsCode*0), {DF_CmdParamSlot_Window, DF_CmdParamSlot_Null, DF_CmdParamSlot_Null}, DF_CmdQueryRule_Null, DF_IconKind_Window, {0, 0}},
|
||||
{ str8_lit_comp("toggle_fullscreen"), str8_lit_comp("Toggles fullscreen view on the active window."), str8_lit_comp(""), str8_lit_comp("Toggle Fullscreen"), (DF_CmdSpecFlag_OmitFromLists*0) | (DF_CmdSpecFlag_RunKeepsQuery*0) | (DF_CmdSpecFlag_QueryUsesOldInput*0) | (DF_CmdSpecFlag_AppliesToView*0) | (DF_CmdSpecFlag_QueryIsCode*0), {DF_CmdParamSlot_Window, DF_CmdParamSlot_Null, DF_CmdParamSlot_Null}, DF_CmdQueryRule_Null, DF_IconKind_Window, {0, 0}},
|
||||
{ str8_lit_comp("confirm_accept"), str8_lit_comp("Accepts the active confirmation prompt."), str8_lit_comp(""), str8_lit_comp("Confirm Accept"), (DF_CmdSpecFlag_OmitFromLists*1) | (DF_CmdSpecFlag_RunKeepsQuery*0) | (DF_CmdSpecFlag_QueryUsesOldInput*0) | (DF_CmdSpecFlag_AppliesToView*0) | (DF_CmdSpecFlag_QueryIsCode*0), {DF_CmdParamSlot_Null, DF_CmdParamSlot_Null, DF_CmdParamSlot_Null}, DF_CmdQueryRule_Null, DF_IconKind_Null, {0, 0}},
|
||||
{ str8_lit_comp("confirm_cancel"), str8_lit_comp("Cancels the active confirmation prompt."), str8_lit_comp(""), str8_lit_comp("Confirm Cancel"), (DF_CmdSpecFlag_OmitFromLists*1) | (DF_CmdSpecFlag_RunKeepsQuery*0) | (DF_CmdSpecFlag_QueryUsesOldInput*0) | (DF_CmdSpecFlag_AppliesToView*0) | (DF_CmdSpecFlag_QueryIsCode*0), {DF_CmdParamSlot_Null, DF_CmdParamSlot_Null, DF_CmdParamSlot_Null}, DF_CmdQueryRule_Null, DF_IconKind_Null, {0, 0}},
|
||||
{ str8_lit_comp("reset_to_default_panels"), str8_lit_comp("Resets the window to the default panel layout."), str8_lit_comp("panel"), str8_lit_comp("Reset To Default Panel Layout"), (DF_CmdSpecFlag_OmitFromLists*0) | (DF_CmdSpecFlag_RunKeepsQuery*0) | (DF_CmdSpecFlag_QueryUsesOldInput*0) | (DF_CmdSpecFlag_AppliesToView*0) | (DF_CmdSpecFlag_QueryIsCode*0), {DF_CmdParamSlot_Window, DF_CmdParamSlot_Null, DF_CmdParamSlot_Null}, DF_CmdQueryRule_Null, DF_IconKind_Window, {0, 0}},
|
||||
{ str8_lit_comp("new_panel_right"), str8_lit_comp("Creates a new panel to the right of the active panel."), str8_lit_comp("panel"), str8_lit_comp("Split Panel Vertically"), (DF_CmdSpecFlag_OmitFromLists*0) | (DF_CmdSpecFlag_RunKeepsQuery*0) | (DF_CmdSpecFlag_QueryUsesOldInput*0) | (DF_CmdSpecFlag_AppliesToView*0) | (DF_CmdSpecFlag_QueryIsCode*0), {DF_CmdParamSlot_Window, DF_CmdParamSlot_Panel, DF_CmdParamSlot_Null}, DF_CmdQueryRule_Null, DF_IconKind_XSplit, {0, 0}},
|
||||
{ str8_lit_comp("new_panel_down"), str8_lit_comp("Creates a new panel at the bottom of the active panel."), str8_lit_comp("panel"), str8_lit_comp("Split Panel Horizontally"), (DF_CmdSpecFlag_OmitFromLists*0) | (DF_CmdSpecFlag_RunKeepsQuery*0) | (DF_CmdSpecFlag_QueryUsesOldInput*0) | (DF_CmdSpecFlag_AppliesToView*0) | (DF_CmdSpecFlag_QueryIsCode*0), {DF_CmdParamSlot_Window, DF_CmdParamSlot_Panel, DF_CmdParamSlot_Null}, DF_CmdQueryRule_Null, DF_IconKind_YSplit, {0, 0}},
|
||||
|
||||
@@ -104,6 +104,8 @@ DF_CoreCmdKind_DecCodeFontScale,
|
||||
DF_CoreCmdKind_OpenWindow,
|
||||
DF_CoreCmdKind_CloseWindow,
|
||||
DF_CoreCmdKind_ToggleFullscreen,
|
||||
DF_CoreCmdKind_ConfirmAccept,
|
||||
DF_CoreCmdKind_ConfirmCancel,
|
||||
DF_CoreCmdKind_ResetToDefaultPanels,
|
||||
DF_CoreCmdKind_NewPanelRight,
|
||||
DF_CoreCmdKind_NewPanelDown,
|
||||
@@ -368,6 +370,7 @@ DF_CmdParamSlot_VirtualOff,
|
||||
DF_CmdParamSlot_Index,
|
||||
DF_CmdParamSlot_ID,
|
||||
DF_CmdParamSlot_PreferDisassembly,
|
||||
DF_CmdParamSlot_ForceConfirm,
|
||||
DF_CmdParamSlot_COUNT
|
||||
} DF_CmdParamSlot;
|
||||
|
||||
@@ -407,6 +410,7 @@ U64 voff;
|
||||
U64 index;
|
||||
U64 id;
|
||||
B32 prefer_dasm;
|
||||
B32 force_confirm;
|
||||
};
|
||||
|
||||
DF_CORE_VIEW_RULE_EVAL_RESOLUTION_FUNCTION_DEF(array);
|
||||
|
||||
+228
-44
@@ -1145,6 +1145,7 @@ df_window_update_and_render(Arena *arena, OS_EventList *events, DF_Window *ws, D
|
||||
{
|
||||
ProfBeginFunction();
|
||||
B32 window_is_focused = os_window_is_focused(ws->os);
|
||||
B32 confirm_open = df_gfx_state->confirm_active;
|
||||
B32 hover_eval_is_open = (ws->hover_eval_string.size != 0 && ws->hover_eval_first_frame_idx+20 < ws->hover_eval_last_frame_idx && df_frame_index()-ws->hover_eval_last_frame_idx < 20);
|
||||
B32 any_query_is_focused = !df_panel_is_nil(ws->focused_panel) && !df_view_is_nil(df_query_view_from_panel(ws->focused_panel));
|
||||
if(!window_is_focused)
|
||||
@@ -1842,8 +1843,18 @@ df_window_update_and_render(Arena *arena, OS_EventList *events, DF_Window *ws, D
|
||||
keep_child->size_pct_of_parent.v[split_axis] *= size_pct_of_parent.v[split_axis];
|
||||
keep_child->size_pct_of_parent.v[axis2_flip(split_axis)] *= size_pct_of_parent.v[axis2_flip(split_axis)];
|
||||
|
||||
// rjf: reset focus, if needed
|
||||
if(ws->focused_panel == discard_child)
|
||||
{
|
||||
ws->focused_panel = keep_child;
|
||||
for(DF_Panel *grandchild = ws->focused_panel; !df_panel_is_nil(grandchild); grandchild = grandchild->first)
|
||||
{
|
||||
ws->focused_panel = grandchild;
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: keep-child split-axis == grandparent split-axis? bubble keep-child up into grandparent's children
|
||||
if(grandparent->split_axis == keep_child->split_axis && !df_panel_is_nil(keep_child->first))
|
||||
if(!df_panel_is_nil(grandparent) && grandparent->split_axis == keep_child->split_axis && !df_panel_is_nil(keep_child->first))
|
||||
{
|
||||
df_panel_remove(grandparent, keep_child);
|
||||
DF_Panel *prev = parent_prev;
|
||||
@@ -1858,16 +1869,6 @@ df_window_update_and_render(Arena *arena, OS_EventList *events, DF_Window *ws, D
|
||||
}
|
||||
df_panel_release(ws, keep_child);
|
||||
}
|
||||
|
||||
// rjf: reset focus, if needed
|
||||
if(ws->focused_panel == discard_child)
|
||||
{
|
||||
ws->focused_panel = keep_child;
|
||||
for(DF_Panel *grandchild = ws->focused_panel; !df_panel_is_nil(grandchild); grandchild = grandchild->first)
|
||||
{
|
||||
ws->focused_panel = grandchild;
|
||||
}
|
||||
}
|
||||
}
|
||||
// NOTE(rjf): Otherwise we can just remove this child.
|
||||
else
|
||||
@@ -3861,6 +3862,55 @@ df_window_update_and_render(Arena *arena, OS_EventList *events, DF_Window *ws, D
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
//- rjf: confirmation popup
|
||||
{
|
||||
if(df_gfx_state->confirm_t > 0.005f) UI_Focus(1) UI_TextAlignment(UI_TextAlign_Center)
|
||||
{
|
||||
Vec2F32 window_dim = dim_2f32(window_rect);
|
||||
UI_Box *bg_box = &ui_g_nil_box;
|
||||
UI_Rect(window_rect) UI_ChildLayoutAxis(Axis2_X)
|
||||
{
|
||||
Vec4F32 bg_color = ui_top_background_color();
|
||||
bg_color.w *= df_gfx_state->confirm_t;
|
||||
ui_set_next_blur_size(10*df_gfx_state->confirm_t);
|
||||
ui_set_next_background_color(bg_color);
|
||||
bg_box = ui_build_box_from_stringf(UI_BoxFlag_FixedSize|UI_BoxFlag_Floating|UI_BoxFlag_Clickable|UI_BoxFlag_Scroll|UI_BoxFlag_DefaultFocusNav|UI_BoxFlag_DrawBackgroundBlur|UI_BoxFlag_DrawBackground, "###confirm_popup_%p", ws);
|
||||
}
|
||||
if(df_gfx_state->confirm_active) UI_Parent(bg_box)
|
||||
{
|
||||
ui_ctx_menu_close();
|
||||
UI_WidthFill UI_PrefHeight(ui_children_sum(1.f)) UI_Column UI_Padding(ui_pct(1, 0))
|
||||
{
|
||||
UI_FontSize(ui_top_font_size()*2.f) UI_PrefHeight(ui_em(3.f, 1.f)) ui_label(df_gfx_state->confirm_title);
|
||||
UI_PrefHeight(ui_em(3.f, 1.f)) UI_TextColor(df_rgba_from_theme_color(DF_ThemeColor_WeakText)) ui_label(df_gfx_state->confirm_msg);
|
||||
ui_spacer(ui_em(1.5f, 1.f));
|
||||
UI_Row UI_Padding(ui_pct(1.f, 0.f)) UI_WidthFill UI_PrefHeight(ui_em(5.f, 1.f))
|
||||
{
|
||||
UI_CornerRadius00(ui_top_font_size()*0.25f)
|
||||
UI_CornerRadius01(ui_top_font_size()*0.25f)
|
||||
if(ui_buttonf("Cancel").clicked || os_key_press(ui_events(), ui_window(), 0, OS_Key_Esc))
|
||||
{
|
||||
DF_CmdParams p = df_cmd_params_zero();
|
||||
df_push_cmd__root(&p, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_ConfirmCancel));
|
||||
}
|
||||
UI_CornerRadius10(ui_top_font_size()*0.25f)
|
||||
UI_CornerRadius11(ui_top_font_size()*0.25f)
|
||||
UI_BackgroundColor(df_rgba_from_theme_color(DF_ThemeColor_ActionBackground))
|
||||
UI_TextColor(df_rgba_from_theme_color(DF_ThemeColor_ActionText))
|
||||
UI_BorderColor(df_rgba_from_theme_color(DF_ThemeColor_ActionBorder))
|
||||
if(ui_buttonf("OK").clicked || os_key_press(ui_events(), ui_window(), 0, OS_Key_Return))
|
||||
{
|
||||
DF_CmdParams p = df_cmd_params_zero();
|
||||
df_push_cmd__root(&p, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_ConfirmAccept));
|
||||
}
|
||||
}
|
||||
ui_spacer(ui_em(3.f, 1.f));
|
||||
}
|
||||
}
|
||||
ui_signal_from_box(bg_box);
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: build auto-complete lister
|
||||
ProfScope("build autocomplete lister")
|
||||
if(!ws->autocomp_force_closed && !ui_key_match(ws->autocomp_root_key, ui_key_zero()) && ws->autocomp_last_frame_idx+1 >= df_frame_index())
|
||||
@@ -4032,6 +4082,32 @@ df_window_update_and_render(Arena *arena, OS_EventList *events, DF_Window *ws, D
|
||||
//- rjf: build hover eval
|
||||
ProfScope("build hover eval")
|
||||
{
|
||||
// find panel/view pair that the mouse is on and disable hover if that view is scrolling
|
||||
if(hover_eval_is_open)
|
||||
{
|
||||
for(DF_Panel *panel = ws->root_panel; !df_panel_is_nil(panel); panel = df_panel_rec_df_pre(panel).next)
|
||||
{
|
||||
if(!df_panel_is_nil(panel->first))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
DF_View *view = df_selected_view_from_panel(panel);
|
||||
if (!df_view_is_nil(view))
|
||||
{
|
||||
Rng2F32 panel_rect = df_rect_from_panel(content_rect, ws->root_panel, panel);
|
||||
if(contains_2f32(panel_rect, ws->ui->mouse))
|
||||
{
|
||||
if(abs_f32(view->scroll_pos.x.off) > 0.01f ||
|
||||
abs_f32(view->scroll_pos.y.off) > 0.01f)
|
||||
{
|
||||
hover_eval_is_open = 0;
|
||||
ws->hover_eval_first_frame_idx = df_frame_index();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(ws->hover_eval_string.size == 0)
|
||||
{
|
||||
ws->hover_eval_num_visible_rows_t = 0;
|
||||
@@ -4122,7 +4198,7 @@ df_window_update_and_render(Arena *arena, OS_EventList *events, DF_Window *ws, D
|
||||
}
|
||||
UI_PrefWidth(ui_em(30.f, 1.f))
|
||||
{
|
||||
UI_PrefWidth(ui_text_dim(10, 1.f)) df_code_label(1.f, 1, row->expr);
|
||||
UI_PrefWidth(ui_text_dim(10, 1.f)) df_code_label(1.f, 1, df_rgba_from_theme_color(DF_ThemeColor_CodeDefault), row->expr);
|
||||
ui_spacer(ui_em(1.5f, 1.f));
|
||||
if(row->flags & DF_EvalVizRowFlag_CanEditValue)
|
||||
{
|
||||
@@ -4147,7 +4223,7 @@ df_window_update_and_render(Arena *arena, OS_EventList *events, DF_Window *ws, D
|
||||
}
|
||||
else
|
||||
{
|
||||
df_code_label(1.f, 1, row->display_value);
|
||||
df_code_label(1.f, 1, df_rgba_from_theme_color(DF_ThemeColor_CodeDefault), row->display_value);
|
||||
}
|
||||
}
|
||||
ui_spacer(ui_em(0.75f, 1.f));
|
||||
@@ -4453,7 +4529,8 @@ df_window_update_and_render(Arena *arena, OS_EventList *events, DF_Window *ws, D
|
||||
UI_Key help_menu_key = ui_key_from_string(ui_key_zero(), str8_lit("_help_menu_key_"));
|
||||
UI_CtxMenu(help_menu_key) UI_PrefWidth(ui_em(40.f, 1.f))
|
||||
{
|
||||
UI_Row UI_TextAlignment(UI_TextAlign_Center) UI_TextColor(df_rgba_from_theme_color(DF_ThemeColor_WeakText)) ui_label(str8_lit_comp(RADDBG_TITLE_STRING_LITERAL));
|
||||
UI_Row UI_TextAlignment(UI_TextAlign_Center) UI_TextColor(df_rgba_from_theme_color(DF_ThemeColor_WeakText))
|
||||
ui_label(str8_lit(RADDBG_TITLE_STRING_LITERAL));
|
||||
ui_spacer(ui_em(0.25f, 1.f));
|
||||
UI_Row
|
||||
UI_PrefWidth(ui_text_dim(10, 1))
|
||||
@@ -4939,7 +5016,8 @@ df_window_update_and_render(Arena *arena, OS_EventList *events, DF_Window *ws, D
|
||||
}break;
|
||||
case CTRL_EventCause_InterruptedByTrap:
|
||||
{
|
||||
|
||||
icon = DF_IconKind_WarningBig;
|
||||
explanation = push_str8f(scratch.arena, "%S interrupted by trap - 0x%x", thread_display_string, stop_event.exception_code);
|
||||
}break;
|
||||
case CTRL_EventCause_InterruptedByHalt:
|
||||
{
|
||||
@@ -4999,18 +5077,21 @@ df_window_update_and_render(Arena *arena, OS_EventList *events, DF_Window *ws, D
|
||||
Vec4F32 tx_color = df_rgba_from_theme_color(DF_ThemeColor_FailureText);
|
||||
F32 alpha_factor = Max(ws->error_t, 0.2f);
|
||||
tx_color.w *= alpha_factor;
|
||||
ui_set_next_text_color(tx_color);
|
||||
ui_set_next_pref_width(ui_children_sum(1));
|
||||
UI_CornerRadius(4)
|
||||
UI_Row
|
||||
UI_PrefWidth(ui_text_dim(10, 1))
|
||||
UI_TextAlignment(UI_TextAlign_Center)
|
||||
String8 error_string = str8(ws->error_buffer, ws->error_string_size);
|
||||
if(error_string.size != 0)
|
||||
{
|
||||
String8 error_string = str8(ws->error_buffer, ws->error_string_size);
|
||||
UI_Font(df_font_from_slot(DF_FontSlot_Icons))
|
||||
UI_FontSize(df_font_size_from_slot(ws, DF_FontSlot_Icons))
|
||||
ui_label(df_g_icon_kind_text_table[DF_IconKind_WarningBig]);
|
||||
ui_label(error_string);
|
||||
ui_set_next_text_color(tx_color);
|
||||
ui_set_next_pref_width(ui_children_sum(1));
|
||||
UI_CornerRadius(4)
|
||||
UI_Row
|
||||
UI_PrefWidth(ui_text_dim(10, 1))
|
||||
UI_TextAlignment(UI_TextAlign_Center)
|
||||
{
|
||||
UI_Font(df_font_from_slot(DF_FontSlot_Icons))
|
||||
UI_FontSize(df_font_size_from_slot(ws, DF_FontSlot_Icons))
|
||||
ui_label(df_g_icon_kind_text_table[DF_IconKind_WarningBig]);
|
||||
ui_label(error_string);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6760,7 +6841,7 @@ df_single_line_eval_value_strings_from_eval(Arena *arena, DF_EvalVizStringFlags
|
||||
}
|
||||
|
||||
// rjf: arrow
|
||||
if(did_ptr_value && direct_type_has_content && (flags & DF_EvalVizStringFlag_ReadOnlyDisplayRules))
|
||||
if(did_ptr_value && (direct_type_has_content || symbol_name.size != 0) && (flags & DF_EvalVizStringFlag_ReadOnlyDisplayRules))
|
||||
{
|
||||
String8 arrow = str8_lit(" -> ");
|
||||
str8_list_push(arena, &list, arrow);
|
||||
@@ -6780,7 +6861,7 @@ df_single_line_eval_value_strings_from_eval(Arena *arena, DF_EvalVizStringFlags
|
||||
}
|
||||
|
||||
// rjf: special-case: symbols
|
||||
else if(direct_type_has_content && (flags & DF_EvalVizStringFlag_ReadOnlyDisplayRules) && symbol_name.size != 0)
|
||||
else if((flags & DF_EvalVizStringFlag_ReadOnlyDisplayRules) && symbol_name.size != 0)
|
||||
{
|
||||
str8_list_push(arena, &list, symbol_name);
|
||||
space_taken += f_dim_from_tag_size_string(font, font_size, symbol_name).x;
|
||||
@@ -7140,6 +7221,7 @@ df_eval_viz_windowed_row_list_from_viz_block_list(Arena *arena, DBGI_Scope *scop
|
||||
|
||||
// rjf: apply view rules to eval
|
||||
{
|
||||
member_eval = df_dynamically_typed_eval_from_eval(parse_ctx->type_graph, parse_ctx->rdbg, ctrl_ctx, member_eval);
|
||||
member_eval = df_eval_from_eval_cfg_table(arena, scope, ctrl_ctx, parse_ctx, member_eval, &view_rule_table);
|
||||
}
|
||||
|
||||
@@ -7152,6 +7234,7 @@ df_eval_viz_windowed_row_list_from_viz_block_list(Arena *arena, DBGI_Scope *scop
|
||||
row->expr = push_str8_copy(arena, member->name);
|
||||
row->display_value = str8_list_join(arena, &display_strings, 0);
|
||||
row->edit_value = str8_list_join(arena, &edit_strings, 0);
|
||||
row->inherited_type_key_chain = tg_key_list_copy(arena, &member->inheritance_key_chain);
|
||||
row->value_ui_rule_node = value_ui_rule_node;
|
||||
row->value_ui_rule_spec = value_ui_rule_spec;
|
||||
row->expand_ui_rule_node = expand_ui_rule_node;
|
||||
@@ -7219,6 +7302,7 @@ df_eval_viz_windowed_row_list_from_viz_block_list(Arena *arena, DBGI_Scope *scop
|
||||
|
||||
// rjf: apply view rules to eval
|
||||
{
|
||||
elem_eval = df_dynamically_typed_eval_from_eval(parse_ctx->type_graph, parse_ctx->rdbg, ctrl_ctx, elem_eval);
|
||||
elem_eval = df_eval_from_eval_cfg_table(arena, scope, ctrl_ctx, parse_ctx, elem_eval, &view_rule_table);
|
||||
}
|
||||
|
||||
@@ -7298,6 +7382,7 @@ df_eval_viz_windowed_row_list_from_viz_block_list(Arena *arena, DBGI_Scope *scop
|
||||
df_cfg_table_push_unparsed_string(scratch.arena, &view_rule_table, view_rule_string, DF_CfgSrc_User);
|
||||
|
||||
// rjf: apply view rules to eval
|
||||
link_eval = df_dynamically_typed_eval_from_eval(parse_ctx->type_graph, parse_ctx->rdbg, ctrl_ctx, link_eval);
|
||||
link_eval = df_eval_from_eval_cfg_table(arena, scope, ctrl_ctx, parse_ctx, link_eval, &view_rule_table);
|
||||
TG_Kind link_type_kind = tg_kind_from_key(link_eval.type_key);
|
||||
|
||||
@@ -7553,7 +7638,7 @@ df_text_search_thread_entry_point(void *p)
|
||||
{
|
||||
TCTX tctx_;
|
||||
tctx_init_and_equip(&tctx_);
|
||||
#if !DE2CTRL
|
||||
#if 0
|
||||
// TODO(rjf): [ ] @de2ctrl text searcher -- wound up in DE_Hash
|
||||
|
||||
//- rjf: types
|
||||
@@ -8591,14 +8676,14 @@ df_entity_tooltips(DF_Entity *entity)
|
||||
stop_condition = str8_lit("true");
|
||||
}
|
||||
UI_PrefWidth(ui_em(12.f, 1.f)) UI_TextColor(df_rgba_from_theme_color(DF_ThemeColor_WeakText)) ui_labelf("Stop Condition: ");
|
||||
UI_PrefWidth(ui_text_dim(10, 1)) UI_Font(df_font_from_slot(DF_FontSlot_Code)) df_code_label(1.f, 1, stop_condition);
|
||||
UI_PrefWidth(ui_text_dim(10, 1)) UI_Font(df_font_from_slot(DF_FontSlot_Code)) df_code_label(1.f, 1, df_rgba_from_theme_color(DF_ThemeColor_CodeDefault), stop_condition);
|
||||
}
|
||||
UI_PrefWidth(ui_children_sum(1)) UI_Row
|
||||
{
|
||||
U64 hit_count = entity->u64;
|
||||
String8 hit_count_text = str8_from_u64(scratch.arena, hit_count, 10, 0, 0);
|
||||
UI_PrefWidth(ui_em(12.f, 1.f)) UI_TextColor(df_rgba_from_theme_color(DF_ThemeColor_WeakText)) ui_labelf("Hit Count: ");
|
||||
UI_PrefWidth(ui_text_dim(10, 1)) UI_Font(df_font_from_slot(DF_FontSlot_Code)) df_code_label(1.f, 1, hit_count_text);
|
||||
UI_PrefWidth(ui_text_dim(10, 1)) UI_Font(df_font_from_slot(DF_FontSlot_Code)) df_code_label(1.f, 1, df_rgba_from_theme_color(DF_ThemeColor_CodeDefault), hit_count_text);
|
||||
}
|
||||
}break;
|
||||
case DF_EntityKind_WatchPin: UI_Font(df_font_from_slot(DF_FontSlot_Code))
|
||||
@@ -8609,7 +8694,7 @@ df_entity_tooltips(DF_Entity *entity)
|
||||
ui_set_next_text_color(color);
|
||||
}
|
||||
String8 display_string = df_display_string_from_entity(scratch.arena, entity);
|
||||
UI_PrefWidth(ui_text_dim(10, 1)) df_code_label(1.f, 1, display_string);
|
||||
UI_PrefWidth(ui_text_dim(10, 1)) df_code_label(1.f, 1, df_rgba_from_theme_color(DF_ThemeColor_CodeDefault), display_string);
|
||||
}break;
|
||||
}
|
||||
scratch_end(scratch);
|
||||
@@ -9499,8 +9584,8 @@ df_code_slice(DF_Window *ws, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, DF_
|
||||
ws->entity_ctx_menu_entity = df_handle_from_entity(pin);
|
||||
}
|
||||
}
|
||||
df_code_label(0.8f, 1, pin_expr);
|
||||
df_code_label(0.6f, 1, eval_string);
|
||||
df_code_label(0.8f, 1, df_rgba_from_theme_color(DF_ThemeColor_CodeDefault), pin_expr);
|
||||
df_code_label(0.6f, 1, df_rgba_from_theme_color(DF_ThemeColor_CodeDefault), eval_string);
|
||||
}
|
||||
UI_Signal pin_sig = ui_signal_from_box(pin_box);
|
||||
if(ui_key_match(pin_box_key, ui_hot_key()))
|
||||
@@ -10411,7 +10496,7 @@ df_help_label(String8 string)
|
||||
}
|
||||
|
||||
internal D_FancyStringList
|
||||
df_fancy_string_list_from_code_string(Arena *arena, F32 alpha, B32 indirection_size_change, String8 string)
|
||||
df_fancy_string_list_from_code_string(Arena *arena, F32 alpha, B32 indirection_size_change, Vec4F32 base_color, String8 string)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
D_FancyStringList fancy_strings = {0};
|
||||
@@ -10440,6 +10525,17 @@ df_fancy_string_list_from_code_string(Arena *arena, F32 alpha, B32 indirection_s
|
||||
};
|
||||
d_fancy_string_list_push(arena, &fancy_strings, &fancy_string);
|
||||
}break;
|
||||
case TXTI_TokenKind_Identifier:
|
||||
{
|
||||
D_FancyString fancy_string =
|
||||
{
|
||||
ui_top_font(),
|
||||
token_string,
|
||||
base_color,
|
||||
ui_top_font_size() * (1.f - !!indirection_size_change*(indirection_counter/10.f)),
|
||||
};
|
||||
d_fancy_string_list_push(arena, &fancy_strings, &fancy_string);
|
||||
}break;
|
||||
case TXTI_TokenKind_Numeric:
|
||||
{
|
||||
Vec4F32 token_color_rgba_alt = token_color_rgba;
|
||||
@@ -10551,10 +10647,10 @@ df_fancy_string_list_from_code_string(Arena *arena, F32 alpha, B32 indirection_s
|
||||
}
|
||||
|
||||
internal void
|
||||
df_code_label(F32 alpha, B32 indirection_size_change, String8 string)
|
||||
df_code_label(F32 alpha, B32 indirection_size_change, Vec4F32 base_color, String8 string)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
D_FancyStringList fancy_strings = df_fancy_string_list_from_code_string(scratch.arena, alpha, indirection_size_change, string);
|
||||
D_FancyStringList fancy_strings = df_fancy_string_list_from_code_string(scratch.arena, alpha, indirection_size_change, base_color, string);
|
||||
UI_Box *box = ui_build_box_from_key(UI_BoxFlag_DrawText, ui_key_zero());
|
||||
ui_box_equip_display_fancy_strings(box, &fancy_strings);
|
||||
scratch_end(scratch);
|
||||
@@ -10760,11 +10856,11 @@ df_line_edit(DF_LineEditFlags flags, S32 depth, TxtPt *cursor, TxtPt *mark, U8 *
|
||||
if(!(flags & DF_LineEditFlag_PreferDisplayString) && pre_edit_value.size != 0)
|
||||
{
|
||||
display_string = pre_edit_value;
|
||||
df_code_label(1.f, 1, display_string);
|
||||
df_code_label(1.f, 1, ui_top_text_color(), display_string);
|
||||
}
|
||||
else if(flags & DF_LineEditFlag_DisplayStringIsCode)
|
||||
{
|
||||
df_code_label(1.f, 1, display_string);
|
||||
df_code_label(1.f, 1, ui_top_text_color(), display_string);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -10793,7 +10889,7 @@ df_line_edit(DF_LineEditFlags flags, S32 depth, TxtPt *cursor, TxtPt *mark, U8 *
|
||||
F32 total_editstr_width = total_text_width - !!(flags & (DF_LineEditFlag_Expander|DF_LineEditFlag_ExpanderSpace|DF_LineEditFlag_ExpanderPlaceholder)) * expander_size_px;
|
||||
ui_set_next_pref_width(ui_px(total_editstr_width+ui_top_font_size()*2, 0.f));
|
||||
UI_Box *editstr_box = ui_build_box_from_stringf(UI_BoxFlag_DrawText|UI_BoxFlag_DisableTextTrunc, "###editstr");
|
||||
D_FancyStringList code_fancy_strings = df_fancy_string_list_from_code_string(scratch.arena, 1.f, 0, edit_string);
|
||||
D_FancyStringList code_fancy_strings = df_fancy_string_list_from_code_string(scratch.arena, 1.f, 0, ui_top_text_color(), edit_string);
|
||||
ui_box_equip_display_fancy_strings(editstr_box, &code_fancy_strings);
|
||||
UI_LineEditDrawData *draw_data = push_array(ui_build_arena(), UI_LineEditDrawData, 1);
|
||||
draw_data->edited_string = push_str8_copy(ui_build_arena(), edit_string);
|
||||
@@ -10897,6 +10993,7 @@ df_gfx_init(OS_WindowRepaintFunctionType *window_repaint_entry_point, DF_StateDe
|
||||
df_gfx_state->num_frames_requested = 2;
|
||||
df_gfx_state->hist = hist;
|
||||
df_gfx_state->key_map_arena = arena_alloc();
|
||||
df_gfx_state->confirm_arena = arena_alloc();
|
||||
df_gfx_state->view_spec_table_size = 256;
|
||||
df_gfx_state->view_spec_table = push_array(arena, DF_ViewSpec *, df_gfx_state->view_spec_table_size);
|
||||
df_gfx_state->view_rule_spec_table_size = 1024;
|
||||
@@ -10941,6 +11038,17 @@ df_gfx_begin_frame(Arena *arena, DF_CmdList *cmds)
|
||||
arena_clear(df_gfx_state->frame_arena);
|
||||
df_gfx_state->hover_line_set_this_frame = 0;
|
||||
|
||||
//- rjf: animate confirmation
|
||||
{
|
||||
F32 rate = 1 - pow_f32(2, (-10.f * df_dt()));
|
||||
B32 confirm_open = df_gfx_state->confirm_active;
|
||||
df_gfx_state->confirm_t += rate * ((F32)!!confirm_open-df_gfx_state->confirm_t);
|
||||
if(abs_f32(df_gfx_state->confirm_t - (F32)!!confirm_open) > 0.005f)
|
||||
{
|
||||
df_gfx_request_frame();
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: capture is active? -> keep rendering
|
||||
if(ProfIsCapturing())
|
||||
{
|
||||
@@ -10990,6 +11098,22 @@ df_gfx_begin_frame(Arena *arena, DF_CmdList *cmds)
|
||||
}
|
||||
}break;
|
||||
|
||||
//- rjf: errors
|
||||
case DF_CoreCmdKind_Error:
|
||||
{
|
||||
DF_Window *window = df_window_from_handle(params.window);
|
||||
if(window == 0)
|
||||
{
|
||||
for(DF_Window *w = df_gfx_state->first_window; w != 0; w = w->next)
|
||||
{
|
||||
DF_CmdParams p = df_cmd_params_from_window(w);
|
||||
p.string = push_str8_copy(arena, params.string);
|
||||
df_cmd_params_mark_slot(&p, DF_CmdParamSlot_String);
|
||||
df_push_cmd__root(&p, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_Error));
|
||||
}
|
||||
}
|
||||
}break;
|
||||
|
||||
//- rjf: windows
|
||||
case DF_CoreCmdKind_OpenWindow:
|
||||
{
|
||||
@@ -11001,10 +11125,32 @@ df_gfx_begin_frame(Arena *arena, DF_CmdList *cmds)
|
||||
DF_Window *ws = df_window_from_handle(params.window);
|
||||
if(ws != 0)
|
||||
{
|
||||
DF_EntityList running_processes = df_query_cached_entity_list_with_kind(DF_EntityKind_Process);
|
||||
|
||||
// NOTE(rjf): if this is the last window, and targets are running, but
|
||||
// this command is not force-confirmed, then we should query the user
|
||||
// to ensure they want to close the debugger before exiting
|
||||
UI_Key key = ui_key_from_string(ui_key_zero(), str8_lit("lossy_exit_confirmation"));
|
||||
if(!ui_key_match(key, df_gfx_state->confirm_key) && running_processes.count != 0 && ws == df_gfx_state->first_window && ws == df_gfx_state->last_window && !params.force_confirm)
|
||||
{
|
||||
df_gfx_state->confirm_key = key;
|
||||
df_gfx_state->confirm_active = 1;
|
||||
arena_clear(df_gfx_state->confirm_arena);
|
||||
MemoryZeroStruct(&df_gfx_state->confirm_cmds);
|
||||
df_gfx_state->confirm_title = push_str8f(df_gfx_state->confirm_arena, "Are you sure you want to exit?");
|
||||
df_gfx_state->confirm_msg = push_str8f(df_gfx_state->confirm_arena, "The debugger is still attached to %slive process%s.",
|
||||
running_processes.count == 1 ? "a " : "",
|
||||
running_processes.count == 1 ? "" : "es");
|
||||
DF_CmdParams p = df_cmd_params_from_window(ws);
|
||||
p.force_confirm = 1;
|
||||
df_cmd_params_mark_slot(&p, DF_CmdParamSlot_ForceConfirm);
|
||||
df_cmd_list_push(df_gfx_state->confirm_arena, &df_gfx_state->confirm_cmds, &p, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_CloseWindow));
|
||||
}
|
||||
|
||||
// NOTE(rjf): if this is the last window, and it is being closed, then
|
||||
// we need to auto-save, and provide one last chance to process saving
|
||||
// commands. after doing so, we can retry.
|
||||
if(ws == df_gfx_state->first_window && ws == df_gfx_state->last_window && df_gfx_state->last_window_queued_save == 0)
|
||||
else if(ws == df_gfx_state->first_window && ws == df_gfx_state->last_window && df_gfx_state->last_window_queued_save == 0)
|
||||
{
|
||||
df_gfx_state->last_window_queued_save = 1;
|
||||
{
|
||||
@@ -11046,6 +11192,22 @@ df_gfx_begin_frame(Arena *arena, DF_CmdList *cmds)
|
||||
}
|
||||
}break;
|
||||
|
||||
//- rjf: confirmations
|
||||
case DF_CoreCmdKind_ConfirmAccept:
|
||||
{
|
||||
df_gfx_state->confirm_active = 0;
|
||||
df_gfx_state->confirm_key = ui_key_zero();
|
||||
for(DF_CmdNode *n = df_gfx_state->confirm_cmds.first; n != 0; n = n->next)
|
||||
{
|
||||
df_push_cmd__root(&n->cmd.params, n->cmd.spec);
|
||||
}
|
||||
}break;
|
||||
case DF_CoreCmdKind_ConfirmCancel:
|
||||
{
|
||||
df_gfx_state->confirm_active = 0;
|
||||
df_gfx_state->confirm_key = ui_key_zero();
|
||||
}break;
|
||||
|
||||
//- rjf: commands with implications for graphical systems, but generated
|
||||
// without context needed - pick selected window & dispatch
|
||||
case DF_CoreCmdKind_SelectThread:
|
||||
@@ -11433,7 +11595,10 @@ df_gfx_begin_frame(Arena *arena, DF_CmdList *cmds)
|
||||
}
|
||||
|
||||
//- rjf: apply keybindings
|
||||
df_clear_bindings();
|
||||
if(src == DF_CfgSrc_User)
|
||||
{
|
||||
df_clear_bindings();
|
||||
}
|
||||
DF_CfgVal *keybindings = df_cfg_val_from_string(table, str8_lit("keybindings"));
|
||||
for(DF_CfgNode *keybinding_set = keybindings->first;
|
||||
keybinding_set != &df_g_nil_cfg_node;
|
||||
@@ -11528,8 +11693,27 @@ df_gfx_begin_frame(Arena *arena, DF_CmdList *cmds)
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: if theme colors are all zeroes, then set to default - config appears busted
|
||||
{
|
||||
B32 all_colors_are_zero = 1;
|
||||
Vec4F32 zero_color = {0};
|
||||
for(DF_ThemeColor c = (DF_ThemeColor)(DF_ThemeColor_Null+1); c < DF_ThemeColor_COUNT; c = (DF_ThemeColor)(c+1))
|
||||
{
|
||||
if(!MemoryMatchStruct(&df_gfx_state->cfg_theme_target.colors[c], &zero_color))
|
||||
{
|
||||
all_colors_are_zero = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(all_colors_are_zero)
|
||||
{
|
||||
MemoryCopy(df_gfx_state->cfg_theme_target.colors, df_g_theme_preset_colors__default_dark, sizeof(df_g_theme_preset_colors__default_dark));
|
||||
MemoryCopy(df_gfx_state->cfg_theme.colors, df_g_theme_preset_colors__default_dark, sizeof(df_g_theme_preset_colors__default_dark));
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: if config opened 0 windows, we need to do some sensible default
|
||||
if(df_gfx_state->first_window == 0)
|
||||
if(src == DF_CfgSrc_User && windows->first == &df_g_nil_cfg_node)
|
||||
{
|
||||
OS_Handle preferred_monitor = os_primary_monitor();
|
||||
Vec2F32 monitor_dim = os_dim_from_monitor(preferred_monitor);
|
||||
@@ -11540,7 +11724,7 @@ df_gfx_begin_frame(Arena *arena, DF_CmdList *cmds)
|
||||
}
|
||||
|
||||
//- rjf: if config bound 0 keys, we need to do some sensible default
|
||||
if(df_gfx_state->key_map_total_count == 0)
|
||||
if(src == DF_CfgSrc_User && df_gfx_state->key_map_total_count == 0)
|
||||
{
|
||||
for(U64 idx = 0; idx < ArrayCount(df_g_default_binding_table); idx += 1)
|
||||
{
|
||||
|
||||
+12
-3
@@ -345,7 +345,7 @@ enum
|
||||
#define DF_GFX_VIEW_RULE_ROW_UI_FUNCTION_NAME(name) df_gfx_view_rule_row_ui__##name
|
||||
#define DF_GFX_VIEW_RULE_ROW_UI_FUNCTION_DEF(name) DF_GFX_VIEW_RULE_ROW_UI_FUNCTION_SIG(DF_GFX_VIEW_RULE_ROW_UI_FUNCTION_NAME(name))
|
||||
|
||||
#define DF_GFX_VIEW_RULE_BLOCK_UI_FUNCTION_SIG(name) void name(DF_Window *ws, DF_ExpandKey key, DF_Eval eval, DBGI_Scope *dbgi_scope, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, struct DF_CfgNode *cfg, Vec2F32 dim)
|
||||
#define DF_GFX_VIEW_RULE_BLOCK_UI_FUNCTION_SIG(name) void name(struct DF_Window *ws, DF_ExpandKey key, DF_Eval eval, DBGI_Scope *dbgi_scope, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, struct DF_CfgNode *cfg, Vec2F32 dim)
|
||||
#define DF_GFX_VIEW_RULE_BLOCK_UI_FUNCTION_NAME(name) df_gfx_view_rule_block_ui__##name
|
||||
#define DF_GFX_VIEW_RULE_BLOCK_UI_FUNCTION_DEF(name) DF_GFX_VIEW_RULE_BLOCK_UI_FUNCTION_SIG(DF_GFX_VIEW_RULE_BLOCK_UI_FUNCTION_NAME(name))
|
||||
|
||||
@@ -681,6 +681,15 @@ struct DF_GfxState
|
||||
DF_CmdSpec *bind_change_cmd_spec;
|
||||
DF_Binding bind_change_binding;
|
||||
|
||||
// rjf: confirmation popup state
|
||||
UI_Key confirm_key;
|
||||
B32 confirm_active;
|
||||
F32 confirm_t;
|
||||
Arena *confirm_arena;
|
||||
DF_CmdList confirm_cmds;
|
||||
String8 confirm_title;
|
||||
String8 confirm_msg;
|
||||
|
||||
// rjf: string search state
|
||||
Arena *string_search_arena;
|
||||
String8 string_search_string;
|
||||
@@ -1022,8 +1031,8 @@ internal B32 df_do_dasm_controls(DASM_Handle handle, U64 line_count_per_page, Tx
|
||||
|
||||
internal UI_Signal df_error_label(String8 string);
|
||||
internal B32 df_help_label(String8 string);
|
||||
internal D_FancyStringList df_fancy_string_list_from_code_string(Arena *arena, F32 alpha, B32 indirection_size_change, String8 string);
|
||||
internal void df_code_label(F32 alpha, B32 indirection_size_change, String8 string);
|
||||
internal D_FancyStringList df_fancy_string_list_from_code_string(Arena *arena, F32 alpha, B32 indirection_size_change, Vec4F32 base_color, String8 string);
|
||||
internal void df_code_label(F32 alpha, B32 indirection_size_change, Vec4F32 base_color, String8 string);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: UI Widgets: Line Edit
|
||||
|
||||
@@ -32,7 +32,7 @@ struct DF_TxtTopologyInfo
|
||||
////////////////////////////////
|
||||
//~ rjf: Helpers
|
||||
|
||||
internal Vec4F32 df_view_rule_hooks__rgba_from_eval(DF_Eval eval, TG_Graph *graph, RADDBG_Parsed *raddbg);
|
||||
internal Vec4F32 df_view_rule_hooks__rgba_from_eval(DF_Eval eval, TG_Graph *graph, RADDBG_Parsed *raddbg, DF_Entity *process);
|
||||
internal void df_view_rule_hooks__eval_commit_rgba(DF_Eval eval, TG_Graph *graph, RADDBG_Parsed *raddbg, DF_CtrlCtx *ctrl_ctx, Vec4F32 rgba);
|
||||
internal DF_BitmapTopologyInfo df_view_rule_hooks__bitmap_topology_info_from_cfg(DBGI_Scope *scope, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, DF_CfgNode *cfg);
|
||||
internal DF_GeoTopologyInfo df_view_rule_hooks__geo_topology_info_from_cfg(DBGI_Scope *scope, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, DF_CfgNode *cfg);
|
||||
|
||||
+58
-27
@@ -1051,22 +1051,49 @@ df_eval_watch_view_build(DF_Window *ws, DF_Panel *panel, DF_View *view, DF_EvalW
|
||||
// rjf: build
|
||||
UI_Signal sig = {0};
|
||||
B32 next_expanded = row_expanded;
|
||||
UI_TableCell UI_Font(code_font)
|
||||
UI_TextColor(row->depth > 0 ? df_rgba_from_theme_color(DF_ThemeColor_WeakText) : ui_top_text_color())
|
||||
UI_FocusHot(cell_selected) UI_FocusActive(cell_selected && ewv->input_editing)
|
||||
UI_TableCell UI_FocusHot(cell_selected) UI_FocusActive(cell_selected && ewv->input_editing)
|
||||
{
|
||||
B32 expr_editing_active = ui_is_focus_active();
|
||||
sig = df_line_editf((DF_LineEditFlag_CodeContents|
|
||||
DF_LineEditFlag_NoBackground|
|
||||
DF_LineEditFlag_DisableEdit*(!can_edit_expr)|
|
||||
DF_LineEditFlag_Expander*!!(row->flags & DF_EvalVizRowFlag_CanExpand)|
|
||||
DF_LineEditFlag_ExpanderPlaceholder*(row->depth==0)|
|
||||
DF_LineEditFlag_ExpanderSpace*(row->depth!=0)),
|
||||
row->depth,
|
||||
&ewv->input_cursor, &ewv->input_mark, ewv->input_buffer, sizeof(ewv->input_buffer), &ewv->input_size, &next_expanded,
|
||||
row->expr,
|
||||
"###row_%I64x", row_hash);
|
||||
B32 is_inherited = (row->inherited_type_key_chain.count != 0);
|
||||
UI_Font(code_font) UI_TextColor(row->depth > 0 ? df_rgba_from_theme_color(DF_ThemeColor_WeakText) : ui_top_text_color())
|
||||
{
|
||||
if(is_inherited)
|
||||
{
|
||||
Vec4F32 inherited_bg_color = df_rgba_from_theme_color(DF_ThemeColor_Highlight1);
|
||||
inherited_bg_color.w *= 0.2f;
|
||||
ui_set_next_background_color(inherited_bg_color);
|
||||
}
|
||||
sig = df_line_editf((DF_LineEditFlag_CodeContents|
|
||||
DF_LineEditFlag_NoBackground*(!is_inherited)|
|
||||
DF_LineEditFlag_DisableEdit*(!can_edit_expr)|
|
||||
DF_LineEditFlag_Expander*!!(row->flags & DF_EvalVizRowFlag_CanExpand)|
|
||||
DF_LineEditFlag_ExpanderPlaceholder*(row->depth==0)|
|
||||
DF_LineEditFlag_ExpanderSpace*(row->depth!=0)),
|
||||
row->depth,
|
||||
&ewv->input_cursor, &ewv->input_mark, ewv->input_buffer, sizeof(ewv->input_buffer), &ewv->input_size, &next_expanded,
|
||||
row->expr,
|
||||
"###row_%I64x", row_hash);
|
||||
}
|
||||
edit_commit = edit_commit || sig.commit;
|
||||
if(sig.hovering && is_inherited) UI_Tooltip
|
||||
{
|
||||
String8List inheritance_chain_type_names = {0};
|
||||
for(TG_KeyNode *n = row->inherited_type_key_chain.first; n != 0; n = n->next)
|
||||
{
|
||||
String8 inherited_type_name = tg_string_from_key(scratch.arena, parse_ctx.type_graph, parse_ctx.rdbg, n->v);
|
||||
inherited_type_name = str8_skip_chop_whitespace(inherited_type_name);
|
||||
str8_list_push(scratch.arena, &inheritance_chain_type_names, inherited_type_name);
|
||||
}
|
||||
StringJoin join = {0};
|
||||
join.sep = str8_lit("::");
|
||||
String8 inheritance_type = str8_list_join(scratch.arena, &inheritance_chain_type_names, &join);
|
||||
ui_set_next_pref_width(ui_children_sum(1));
|
||||
UI_Row
|
||||
{
|
||||
ui_labelf("Inherited from ");
|
||||
UI_Font(df_font_from_slot(DF_FontSlot_Code)) df_code_label(1.f, 1.f, df_rgba_from_theme_color(DF_ThemeColor_CodeType), inheritance_type);
|
||||
}
|
||||
}
|
||||
if(sig.hovering && DEV_eval_watch_key_tooltips) UI_Tooltip UI_Font(df_font_from_slot(DF_FontSlot_Code))
|
||||
{
|
||||
ui_labelf("Parent Key: %I64x, %I64x, %I64x", row->parent_key.uniquifier, row->parent_key.parent_hash, row->parent_key.child_num);
|
||||
@@ -1217,7 +1244,7 @@ df_eval_watch_view_build(DF_Window *ws, DF_Panel *panel, DF_View *view, DF_EvalW
|
||||
UI_Box *box = ui_build_box_from_stringf(UI_BoxFlag_Clip|UI_BoxFlag_Clickable, "###val_%I64x", row_hash);
|
||||
UI_Parent(box)
|
||||
{
|
||||
df_code_label(1.f, 1, row->display_value);
|
||||
df_code_label(1.f, 1, df_rgba_from_theme_color(DF_ThemeColor_CodeDefault), row->display_value);
|
||||
}
|
||||
sig = ui_signal_from_box(box);
|
||||
}
|
||||
@@ -1261,7 +1288,7 @@ df_eval_watch_view_build(DF_Window *ws, DF_Panel *panel, DF_View *view, DF_EvalW
|
||||
UI_Box *box = ui_build_box_from_stringf(UI_BoxFlag_Clip|UI_BoxFlag_Clickable, "###type_%I64x", row_hash);
|
||||
if(!tg_key_match(key, tg_key_zero())) UI_Parent(box)
|
||||
{
|
||||
df_code_label(1.f, 1, string);
|
||||
df_code_label(1.f, 1, df_rgba_from_theme_color(DF_ThemeColor_CodeType), string);
|
||||
}
|
||||
UI_Signal sig = ui_signal_from_box(box);
|
||||
if(sig.pressed)
|
||||
@@ -4989,7 +5016,7 @@ DF_VIEW_UI_FUNCTION_DEF(Code)
|
||||
//- rjf: search query -> matches
|
||||
//
|
||||
DF_TextSearchMatchArray search_query_matches = {0};
|
||||
#if !DE2CTRL
|
||||
#if 0
|
||||
{
|
||||
search_query_matches = df_text_search_match_array_from_entity_needle(scratch.arena, entity, search_query, entity_line_string_flags, tv->cursor);
|
||||
df_text_search_match_array_sort_in_place(&search_query_matches);
|
||||
@@ -5153,7 +5180,7 @@ DF_VIEW_UI_FUNCTION_DEF(Code)
|
||||
if(txti_buffer_is_ready) UI_Parent(container_box)
|
||||
{
|
||||
//- rjf: build fractional space
|
||||
container_box->view_off.x = view->scroll_pos.x.idx + view->scroll_pos.x.off;
|
||||
container_box->view_off.x = container_box->view_off_target.x = view->scroll_pos.x.idx + view->scroll_pos.x.off;
|
||||
container_box->view_off.y = container_box->view_off_target.y = code_line_height*mod_f32(view->scroll_pos.y.off, 1.f) + code_line_height*(view->scroll_pos.y.off < 0) - code_line_height*(view->scroll_pos.y.off == -1.f && view->scroll_pos.y.idx == 1);
|
||||
|
||||
//- rjf: build code slice
|
||||
@@ -5359,7 +5386,7 @@ DF_VIEW_UI_FUNCTION_DEF(Code)
|
||||
if(search_query.size != 0 && next_match.line != 0)
|
||||
{
|
||||
// TODO(rjf): [ ] @de2ctrl
|
||||
#if !DE2CTRL
|
||||
#if 0
|
||||
DF_TextSlice match_line_slice = df_text_slice_from_entity(scratch.arena, entity, r1s64(next_match.line, next_match.line), entity_line_string_flags);
|
||||
String8 match_line = match_line_slice.visible_range_text;
|
||||
F32 match_advance = f_dim_from_tag_size_string(code_font, code_font_size, str8_prefix(match_line, next_match.column-1)).x;
|
||||
@@ -6033,7 +6060,7 @@ DF_VIEW_UI_FUNCTION_DEF(Disassembly)
|
||||
if(has_disasm) UI_Parent(container_box)
|
||||
{
|
||||
//- rjf: build fractional space
|
||||
container_box->view_off.x = view->scroll_pos.x.idx + view->scroll_pos.x.off;
|
||||
container_box->view_off.x = container_box->view_off_target.x = view->scroll_pos.x.idx + view->scroll_pos.x.off;
|
||||
container_box->view_off.y = container_box->view_off_target.y = code_line_height*mod_f32(view->scroll_pos.y.off, 1.f) + code_line_height*(view->scroll_pos.y.off < 0) - code_line_height*(view->scroll_pos.y.off == -1.f && view->scroll_pos.y.idx == 1);
|
||||
|
||||
//- rjf: build code slice
|
||||
@@ -6803,7 +6830,7 @@ DF_VIEW_UI_FUNCTION_DEF(Output)
|
||||
//- rjf: search query -> matches
|
||||
//
|
||||
DF_TextSearchMatchArray search_query_matches = {0};
|
||||
#if !DE2CTRL
|
||||
#if 0
|
||||
{
|
||||
search_query_matches = df_text_search_match_array_from_entity_needle(scratch.arena, entity, search_query, entity_line_string_flags, tv->cursor);
|
||||
df_text_search_match_array_sort_in_place(&search_query_matches);
|
||||
@@ -6967,7 +6994,7 @@ DF_VIEW_UI_FUNCTION_DEF(Output)
|
||||
if(txti_buffer_is_ready) UI_Parent(container_box)
|
||||
{
|
||||
//- rjf: build fractional space
|
||||
container_box->view_off.x = view->scroll_pos.x.idx + view->scroll_pos.x.off;
|
||||
container_box->view_off.x = container_box->view_off_target.x = view->scroll_pos.x.idx + view->scroll_pos.x.off;
|
||||
container_box->view_off.y = container_box->view_off_target.y = code_line_height*mod_f32(view->scroll_pos.y.off, 1.f) + code_line_height*(view->scroll_pos.y.off < 0) - code_line_height*(view->scroll_pos.y.off == -1.f && view->scroll_pos.y.idx == 1);
|
||||
|
||||
//- rjf: build code slice
|
||||
@@ -7030,7 +7057,7 @@ DF_VIEW_UI_FUNCTION_DEF(Output)
|
||||
if(search_query.size != 0 && next_match.line != 0)
|
||||
{
|
||||
// TODO(rjf): [ ] @de2ctrl
|
||||
#if !DE2CTRL
|
||||
#if 0
|
||||
DF_TextSlice match_line_slice = df_text_slice_from_entity(scratch.arena, entity, r1s64(next_match.line, next_match.line), entity_line_string_flags);
|
||||
String8 match_line = match_line_slice.visible_range_text;
|
||||
F32 match_advance = f_dim_from_tag_size_string(code_font, code_font_size, str8_prefix(match_line, next_match.column-1)).x;
|
||||
@@ -7316,6 +7343,10 @@ DF_VIEW_UI_FUNCTION_DEF(Memory)
|
||||
viz_range_rows.max = clamp_1s64(scroll_idx_rng, viz_range_rows.max);
|
||||
viz_range_bytes.min = viz_range_rows.min*mv->num_columns;
|
||||
viz_range_bytes.max = (viz_range_rows.max+1)*mv->num_columns+1;
|
||||
if(viz_range_bytes.min > viz_range_bytes.max)
|
||||
{
|
||||
Swap(U64, viz_range_bytes.min, viz_range_bytes.max);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
@@ -7719,7 +7750,7 @@ DF_VIEW_UI_FUNCTION_DEF(Memory)
|
||||
UI_BoxFlag_AllowOverflowX|
|
||||
UI_BoxFlag_AllowOverflowY,
|
||||
"scrollable_box");
|
||||
scrollable_box->view_off.x = view->scroll_pos.x.idx + view->scroll_pos.x.off;
|
||||
container_box->view_off.x = container_box->view_off_target.x = view->scroll_pos.x.idx + view->scroll_pos.x.off;
|
||||
scrollable_box->view_off.y = scrollable_box->view_off_target.y = floor_f32(row_height_px*mod_f32(view->scroll_pos.y.off, 1.f) + row_height_px*(view->scroll_pos.y.off < 0));
|
||||
}
|
||||
|
||||
@@ -7924,7 +7955,7 @@ DF_VIEW_UI_FUNCTION_DEF(Memory)
|
||||
}
|
||||
if(a->type_string.size != 0)
|
||||
{
|
||||
df_code_label(1.f, 1, a->type_string);
|
||||
df_code_label(1.f, 1, df_rgba_from_theme_color(DF_ThemeColor_CodeType), a->type_string);
|
||||
}
|
||||
UI_TextColor(df_rgba_from_theme_color(DF_ThemeColor_WeakText)) ui_label(str8_from_memory_size(scratch.arena, dim_1u64(a->vaddr_range)));
|
||||
if(a->next != 0)
|
||||
@@ -8013,7 +8044,7 @@ DF_VIEW_UI_FUNCTION_DEF(Memory)
|
||||
UI_PrefHeight(ui_px(row_height_px, 0.f))
|
||||
{
|
||||
B32 cursor_in_range = (viz_range_bytes.min <= mv->cursor && mv->cursor+8 <= viz_range_bytes.max);
|
||||
ui_labelf("%016X", mv->cursor);
|
||||
ui_labelf("%016I64X", mv->cursor);
|
||||
if(cursor_in_range)
|
||||
{
|
||||
U64 as_u8 = 0;
|
||||
@@ -8028,7 +8059,7 @@ DF_VIEW_UI_FUNCTION_DEF(Memory)
|
||||
ui_labelf("%02X (%I64u)", as_u8, as_u8);
|
||||
ui_labelf("%04X (%I64u)", as_u16, as_u16);
|
||||
ui_labelf("%08X (%I64u)", as_u32, as_u32);
|
||||
ui_labelf("%016X (%I64u)", as_u64, as_u64);
|
||||
ui_labelf("%016I64X (%I64u)", as_u64, as_u64);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8193,7 +8224,7 @@ DF_VIEW_UI_FUNCTION_DEF(Breakpoints)
|
||||
UI_Parent(box)
|
||||
{
|
||||
String8 hit_count_string = str8_from_u64(scratch.arena, entity->u64, 10, 0, 0);
|
||||
UI_Font(df_font_from_slot(DF_FontSlot_Code)) df_code_label(1.f, 1, hit_count_string);
|
||||
UI_Font(df_font_from_slot(DF_FontSlot_Code)) df_code_label(1.f, 1, df_rgba_from_theme_color(DF_ThemeColor_CodeDefault), hit_count_string);
|
||||
}
|
||||
UI_Signal sig = ui_signal_from_box(box);
|
||||
if(sig.pressed)
|
||||
|
||||
@@ -942,14 +942,14 @@ eval_irtree_and_type_from_expr(Arena *arena, TG_Graph *graph, RADDBG_Parsed *rdb
|
||||
|
||||
if (l_good && r_good){
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
TG_Type *check_type = tg_type_from_graph_raddbg_key(scratch.arena, graph, rdbg, check_type_key);
|
||||
TG_MemberArray check_type_members = tg_data_members_from_graph_raddbg_key(scratch.arena, graph, rdbg, check_type_key);
|
||||
|
||||
// lookup member
|
||||
String8 member_name = exprr->name;
|
||||
TG_Member *match = 0;
|
||||
for(U64 member_idx = 0; member_idx < check_type->count; member_idx += 1)
|
||||
for(U64 member_idx = 0; member_idx < check_type_members.count; member_idx += 1)
|
||||
{
|
||||
TG_Member *member = &check_type->members[member_idx];
|
||||
TG_Member *member = &check_type_members.v[member_idx];
|
||||
if(str8_match(member->name, member_name, 0))
|
||||
{
|
||||
match = member;
|
||||
|
||||
@@ -778,6 +778,7 @@ eval_parse_expr_from_text_tokens__prec(Arena *arena, EVAL_ParseCtx *ctx, String8
|
||||
case EVAL_TokenKind_Identifier:
|
||||
{
|
||||
B32 mapped_identifier = 0;
|
||||
B32 identifier_type_is_possibly_dynamically_overridden = 0;
|
||||
B32 identifier_looks_like_type_expr = 0;
|
||||
RADDBG_LocationKind loc_kind = RADDBG_LocationKind_NULL;
|
||||
RADDBG_LocationRegister loc_reg = {0};
|
||||
@@ -808,6 +809,7 @@ eval_parse_expr_from_text_tokens__prec(Arena *arena, EVAL_ParseCtx *ctx, String8
|
||||
ctx->rdbg->type_nodes != 0)
|
||||
{
|
||||
mapped_identifier = 1;
|
||||
identifier_type_is_possibly_dynamically_overridden = 1;
|
||||
RADDBG_Local *local_var = &ctx->rdbg->locals[local_num-1];
|
||||
|
||||
// rjf: grab location info
|
||||
|
||||
@@ -86,7 +86,7 @@ int main(int argument_count, char **arguments)
|
||||
{
|
||||
String8 file_path = n->string;
|
||||
String8 file_ext = str8_skip_last_dot(file_path);
|
||||
if(str8_match(file_ext, str8_lit("mc"), 0))
|
||||
if(str8_match(file_ext, str8_lit("mdesk"), 0))
|
||||
{
|
||||
String8 data = os_data_from_file_path(mg_arena, file_path);
|
||||
MD_TokenizeResult tokenize = md_tokenize_from_text(mg_arena, data);
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
#include <stdint.h>
|
||||
#include <windows.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int lib_loaded = 0;
|
||||
HANDLE lib = {0};
|
||||
FILETIME lib_last_filetime = {0};
|
||||
int (*get_number)(void) = 0;
|
||||
for(;;)
|
||||
{
|
||||
//- rjf: hot-load dll
|
||||
{
|
||||
HANDLE file = CreateFileA("mule_hotload_module.dll", GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
FILETIME modified = {0};
|
||||
if(GetFileTime(file, 0, 0, &modified) &&
|
||||
CompareFileTime(&lib_last_filetime, &modified) == -1)
|
||||
{
|
||||
for(int reloaded = 0; !reloaded;)
|
||||
{
|
||||
if(lib_loaded)
|
||||
{
|
||||
FreeLibrary(lib);
|
||||
lib_loaded = 0;
|
||||
}
|
||||
BOOL copy_worked = CopyFile("mule_hotload_module.dll", "mule_hotload_module_temp.dll", 0);
|
||||
lib = LoadLibraryA("mule_hotload_module_temp.dll");
|
||||
if(lib != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
reloaded = 1;
|
||||
lib_last_filetime = modified;
|
||||
get_number = (int(*)(void))GetProcAddress(lib, "get_number");
|
||||
lib_loaded = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
CloseHandle(file);
|
||||
}
|
||||
int number = get_number();
|
||||
printf("got a number: %i\n", number);
|
||||
if(number == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
Sleep(1000);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
__declspec(dllexport) int
|
||||
get_number(void)
|
||||
{
|
||||
int sum = 0;
|
||||
for(int i = 0; i < 100; i += 1)
|
||||
{
|
||||
sum += i;
|
||||
sum += i;
|
||||
sum += 1;
|
||||
}
|
||||
sum = 1000;
|
||||
return sum;
|
||||
}
|
||||
+190
-1
@@ -371,6 +371,9 @@ type_coverage_eval_tests(void){
|
||||
stks *stks_first = &stks_test[0];
|
||||
stks *stks_ptr = stks_first + 8;
|
||||
|
||||
TestFunction *function = mule_get_module_function("dll_type_eval_tests");
|
||||
function();
|
||||
|
||||
int x = (int)(Anonymous_D);
|
||||
}
|
||||
|
||||
@@ -911,6 +914,18 @@ struct Template_Example3{
|
||||
}
|
||||
};
|
||||
|
||||
struct SingleInheritanceBase
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
struct SingleInheritanceDerived : SingleInheritanceBase
|
||||
{
|
||||
int z;
|
||||
int w;
|
||||
};
|
||||
|
||||
struct Has_Members{
|
||||
int a;
|
||||
int b;
|
||||
@@ -1087,6 +1102,86 @@ struct Vinheritance_Child : Vinheritance_MidLeft, Vinheritance_MidRight{
|
||||
};
|
||||
};
|
||||
|
||||
struct Minheritance_Base{
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
struct Minheritance_MidLeft : Minheritance_Base{
|
||||
float left;
|
||||
};
|
||||
|
||||
struct Minheritance_MidRight : Minheritance_Base{
|
||||
float right;
|
||||
};
|
||||
|
||||
struct Minheritance_Child : Minheritance_MidLeft, Minheritance_MidRight{
|
||||
char *name;
|
||||
};
|
||||
|
||||
struct Pure
|
||||
{
|
||||
virtual ~Pure() = default;
|
||||
virtual void Foo() = 0;
|
||||
};
|
||||
|
||||
struct PureChild : Pure
|
||||
{
|
||||
virtual ~PureChild() = default;
|
||||
virtual void Foo() {a += 1;}
|
||||
double a = 0;
|
||||
};
|
||||
|
||||
struct Base
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
int z;
|
||||
virtual ~Base() = default;
|
||||
virtual void Foo() = 0;
|
||||
};
|
||||
|
||||
struct Derived : Base
|
||||
{
|
||||
int r;
|
||||
int g;
|
||||
int b;
|
||||
int a;
|
||||
virtual ~Derived() = default;
|
||||
virtual void Foo() {a += 1;}
|
||||
};
|
||||
|
||||
struct DerivedA : Base
|
||||
{
|
||||
float a;
|
||||
float b;
|
||||
virtual void Foo() {a += 1;}
|
||||
virtual ~DerivedA() = default;
|
||||
};
|
||||
|
||||
struct DerivedB : Base
|
||||
{
|
||||
double c;
|
||||
double d;
|
||||
virtual void Foo() {c += 1;}
|
||||
virtual ~DerivedB() = default;
|
||||
};
|
||||
|
||||
struct NonVirtualBase
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
int z;
|
||||
};
|
||||
|
||||
struct NonVirtualDerived : NonVirtualBase
|
||||
{
|
||||
int r;
|
||||
int g;
|
||||
int b;
|
||||
int a;
|
||||
};
|
||||
|
||||
struct OverloadedMethods{
|
||||
int x;
|
||||
int cool_method(void){
|
||||
@@ -1242,6 +1337,12 @@ extended_type_coverage_eval_tests(void){
|
||||
Template_Example3<void *, float> temp3_vi((void *)&temp3_if, 1.f);
|
||||
Template_Example3<int, Template_Example2<int, float>> temp3_itif(123, temp_if);
|
||||
|
||||
SingleInheritanceDerived sid;
|
||||
sid.x = 123;
|
||||
sid.y = 456;
|
||||
sid.z = 789;
|
||||
sid.w = 999;
|
||||
|
||||
Pointer_To_Member pointer_to_member = {
|
||||
&Has_Members::a, &Has_Members::c, &Has_Members::bas,
|
||||
&Has_Members::x, &Has_Members::z, &Has_Members::bas_f,
|
||||
@@ -1276,6 +1377,46 @@ extended_type_coverage_eval_tests(void){
|
||||
vinheritance_child.x = -1;
|
||||
vinheritance_child.y = -1;
|
||||
|
||||
Minheritance_Child minheritance_child;
|
||||
minheritance_child.name = "foobar";
|
||||
minheritance_child.left = 10.5f;
|
||||
minheritance_child.right = 13.0f;
|
||||
minheritance_child.Minheritance_MidLeft::x = -1;
|
||||
minheritance_child.Minheritance_MidLeft::y = -1;
|
||||
minheritance_child.Minheritance_MidRight::x = +1;
|
||||
minheritance_child.Minheritance_MidRight::y = +1;
|
||||
|
||||
Pure *child = new PureChild();
|
||||
child->Foo();
|
||||
child->Foo();
|
||||
child->Foo();
|
||||
delete child;
|
||||
|
||||
Base *derived = new Derived();
|
||||
derived->Foo();
|
||||
derived->Foo();
|
||||
derived->Foo();
|
||||
delete derived;
|
||||
|
||||
NonVirtualBase *non_virtual_derived = new NonVirtualDerived();
|
||||
non_virtual_derived->x += 1;
|
||||
non_virtual_derived->x += 1;
|
||||
non_virtual_derived->x += 1;
|
||||
delete non_virtual_derived;
|
||||
|
||||
Base *base_array[1024] = {0};
|
||||
for(int i = 0; i < sizeof(base_array)/sizeof(base_array[0]); i += 1)
|
||||
{
|
||||
if(i & 1 == 1)
|
||||
{
|
||||
base_array[i] = new DerivedA();
|
||||
}
|
||||
else
|
||||
{
|
||||
base_array[i] = new DerivedB();
|
||||
}
|
||||
}
|
||||
|
||||
OverloadedMethods overloaded_methods;
|
||||
{
|
||||
overloaded_methods.x = 0;
|
||||
@@ -1314,7 +1455,53 @@ extended_type_coverage_eval_tests(void){
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// NOTE(allen): C Type Coverage
|
||||
//~ rjf: Templated Function Eval Tests
|
||||
|
||||
typedef struct TemplateArg TemplateArg;
|
||||
struct TemplateArg
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
int z;
|
||||
float a;
|
||||
float b;
|
||||
float c;
|
||||
char *name;
|
||||
};
|
||||
|
||||
template<typename T> static T
|
||||
templated_factorial(T t)
|
||||
{
|
||||
T result = t;
|
||||
if(t > 1)
|
||||
{
|
||||
result *= templated_factorial<T>(t-1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T> static T
|
||||
compute_template_arg_info(T t)
|
||||
{
|
||||
int sum = t.x + t.y + t.z;
|
||||
int size = sizeof(t);
|
||||
float sum_f = t.a + t.b + t.c;
|
||||
OutputDebugStringA(t.name);
|
||||
return t;
|
||||
}
|
||||
|
||||
static void
|
||||
templated_function_eval_tests(void)
|
||||
{
|
||||
int int_factorial = templated_factorial<int>(10);
|
||||
float float_factorial = templated_factorial<float>(10);
|
||||
TemplateArg arg = {1, 2, 3, 4.f, 5.f, 6.f, "my template arg"};
|
||||
compute_template_arg_info(arg);
|
||||
int x = 0;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ NOTE(allen): C Type Coverage
|
||||
|
||||
extern "C"{
|
||||
#include "mule_c.h"
|
||||
@@ -2340,6 +2527,8 @@ mule_main(int argc, char** argv){
|
||||
|
||||
extended_type_coverage_eval_tests();
|
||||
|
||||
templated_function_eval_tests();
|
||||
|
||||
c_type_coverage_eval_tests();
|
||||
|
||||
c_type_with_bitfield_usage();
|
||||
|
||||
+19
-15
@@ -1,38 +1,42 @@
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
/*
|
||||
** Program to run in debugger organized to provide tests for
|
||||
** stepping, breakpoints, evaluation, cross-module calls.
|
||||
*/
|
||||
|
||||
////////////////////////////////
|
||||
// NOTE(allen): Setup
|
||||
|
||||
|
||||
#if _WIN32
|
||||
#define export_function extern "C" __declspec(dllexport)
|
||||
#else
|
||||
#define export_function extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
////////////////////////////////
|
||||
// NOTE(allen): TLS Eval
|
||||
|
||||
#if _WIN32
|
||||
# define thread_var __declspec(thread)
|
||||
#else
|
||||
# define thread_var __thread
|
||||
#endif
|
||||
|
||||
typedef struct Basics Basics;
|
||||
struct Basics
|
||||
{
|
||||
int a;
|
||||
int b;
|
||||
int c;
|
||||
int d;
|
||||
};
|
||||
|
||||
thread_var float tls_a = 1.015625f;
|
||||
thread_var int tls_b = -100;
|
||||
|
||||
export_function void
|
||||
dll_tls_eval_test(void){
|
||||
dll_tls_eval_test(void)
|
||||
{
|
||||
tls_a *= 1.5f;
|
||||
tls_b *= -2;
|
||||
}
|
||||
|
||||
|
||||
export_function void
|
||||
dll_type_eval_tests(void)
|
||||
{
|
||||
Basics basics1 = {1, 2, 3, 4};
|
||||
Basics basics2 = {4, 5, 6, 7};
|
||||
int x = 0;
|
||||
(void)x;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
////////////////////////////////
|
||||
//~ rjf: @os_hooks Main Initialization API (Implemented Per-OS)
|
||||
|
||||
internal void
|
||||
os_graphical_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @os_hooks Clipboards (Implemented Per-OS)
|
||||
|
||||
internal void
|
||||
os_set_clipboard_text(String8 string)
|
||||
{
|
||||
}
|
||||
|
||||
internal String8
|
||||
os_get_clipboard_text(Arena *arena)
|
||||
{
|
||||
return str8_zero();
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @os_hooks Windows (Implemented Per-OS)
|
||||
|
||||
internal OS_Handle
|
||||
os_window_open(Vec2F32 resolution, String8 title)
|
||||
{
|
||||
OS_Handle handle = {1};
|
||||
return handle;
|
||||
}
|
||||
|
||||
internal void
|
||||
os_window_close(OS_Handle window)
|
||||
{
|
||||
}
|
||||
|
||||
internal void
|
||||
os_window_first_paint(OS_Handle window)
|
||||
{
|
||||
}
|
||||
|
||||
internal void
|
||||
os_window_equip_repaint(OS_Handle window, OS_WindowRepaintFunctionType *repaint, void *user_data)
|
||||
{
|
||||
}
|
||||
|
||||
internal void
|
||||
os_window_focus(OS_Handle window)
|
||||
{
|
||||
}
|
||||
|
||||
internal B32
|
||||
os_window_is_focused(OS_Handle window)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
internal B32
|
||||
os_window_is_fullscreen(OS_Handle window)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
internal void
|
||||
os_window_set_fullscreen(OS_Handle window, B32 fullscreen)
|
||||
{
|
||||
}
|
||||
|
||||
internal B32
|
||||
os_window_is_maximized(OS_Handle window)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
internal void
|
||||
os_window_set_maximized(OS_Handle window, B32 maximized)
|
||||
{
|
||||
}
|
||||
|
||||
internal void
|
||||
os_window_bring_to_front(OS_Handle window)
|
||||
{
|
||||
}
|
||||
|
||||
internal void
|
||||
os_window_set_monitor(OS_Handle window, OS_Handle monitor)
|
||||
{
|
||||
}
|
||||
|
||||
internal Rng2F32
|
||||
os_rect_from_window(OS_Handle window)
|
||||
{
|
||||
Rng2F32 rect = r2f32(v2f32(0, 0), v2f32(500, 500));
|
||||
return rect;
|
||||
}
|
||||
|
||||
internal Rng2F32
|
||||
os_client_rect_from_window(OS_Handle window)
|
||||
{
|
||||
Rng2F32 rect = r2f32(v2f32(0, 0), v2f32(500, 500));
|
||||
return rect;
|
||||
}
|
||||
|
||||
internal F32
|
||||
os_dpi_from_window(OS_Handle window)
|
||||
{
|
||||
return 96.f;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @os_hooks Monitors (Implemented Per-OS)
|
||||
|
||||
internal OS_HandleArray
|
||||
os_push_monitors_array(Arena *arena)
|
||||
{
|
||||
OS_HandleArray arr = {0};
|
||||
return arr;
|
||||
}
|
||||
|
||||
internal OS_Handle
|
||||
os_primary_monitor(void)
|
||||
{
|
||||
OS_Handle handle = {1};
|
||||
return handle;
|
||||
}
|
||||
|
||||
internal OS_Handle
|
||||
os_monitor_from_window(OS_Handle window)
|
||||
{
|
||||
OS_Handle handle = {1};
|
||||
return handle;
|
||||
}
|
||||
|
||||
internal String8
|
||||
os_name_from_monitor(Arena *arena, OS_Handle monitor)
|
||||
{
|
||||
return str8_zero();
|
||||
}
|
||||
|
||||
internal Vec2F32
|
||||
os_dim_from_monitor(OS_Handle monitor)
|
||||
{
|
||||
Vec2F32 v = v2f32(1000, 1000);
|
||||
return v;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @os_hooks Events (Implemented Per-OS)
|
||||
|
||||
internal void
|
||||
os_send_wakeup_event(void)
|
||||
{
|
||||
}
|
||||
|
||||
internal OS_EventList
|
||||
os_get_events(Arena *arena, B32 wait)
|
||||
{
|
||||
OS_EventList evts = {0};
|
||||
return evts;
|
||||
}
|
||||
|
||||
internal OS_EventFlags
|
||||
os_get_event_flags(void)
|
||||
{
|
||||
OS_EventFlags f = 0;
|
||||
return f;
|
||||
}
|
||||
|
||||
internal B32
|
||||
os_key_is_down(OS_Key key)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
internal Vec2F32
|
||||
os_mouse_from_window(OS_Handle window)
|
||||
{
|
||||
return v2f32(0, 0);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @os_hooks Cursors (Implemented Per-OS)
|
||||
|
||||
internal void
|
||||
os_set_cursor(OS_Cursor cursor)
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @os_hooks System Properties (Implemented Per-OS)
|
||||
|
||||
internal F32
|
||||
os_double_click_time(void)
|
||||
{
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
internal F32
|
||||
os_caret_blink_time(void)
|
||||
{
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
internal F32
|
||||
os_default_refresh_rate(void)
|
||||
{
|
||||
return 60.f;
|
||||
}
|
||||
|
||||
internal B32
|
||||
os_granular_sleep_enabled(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @os_hooks Native Messages & Panics (Implemented Per-OS)
|
||||
|
||||
internal void
|
||||
os_graphical_message(B32 error, String8 title, String8 message)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef OS_GFX_STUB_H
|
||||
#define OS_GFX_STUB_H
|
||||
|
||||
#endif // OS_GFX_STUB_H
|
||||
+5
-1
@@ -19,7 +19,7 @@
|
||||
# if OS_FEATURE_SOCKET
|
||||
# include "socket/win32/os_socket_win32.c"
|
||||
# endif
|
||||
# if OS_FEATURE_GRAPHICAL
|
||||
# if OS_FEATURE_GRAPHICAL && !OS_GFX_STUB
|
||||
# include "gfx/win32/os_gfx_win32.c"
|
||||
# endif
|
||||
#elif OS_LINUX
|
||||
@@ -27,3 +27,7 @@
|
||||
#else
|
||||
# error no OS layer setup
|
||||
#endif
|
||||
|
||||
#if OS_GFX_STUB
|
||||
#include "gfx/stub/os_gfx_stub.c"
|
||||
#endif
|
||||
|
||||
+9
-1
@@ -12,6 +12,10 @@
|
||||
# define OS_FEATURE_GRAPHICAL 0
|
||||
#endif
|
||||
|
||||
#if !defined(OS_GFX_STUB)
|
||||
# define OS_GFX_STUB 0
|
||||
#endif
|
||||
|
||||
#include "core/os_core.h"
|
||||
|
||||
#if OS_FEATURE_SOCKET
|
||||
@@ -27,7 +31,7 @@
|
||||
# if OS_FEATURE_SOCKET
|
||||
# include "socket/win32/os_socket_win32.h"
|
||||
# endif
|
||||
# if OS_FEATURE_GRAPHICAL
|
||||
# if OS_FEATURE_GRAPHICAL && !OS_GFX_STUB
|
||||
# include "gfx/win32/os_gfx_win32.h"
|
||||
# endif
|
||||
#elif OS_LINUX
|
||||
@@ -36,4 +40,8 @@
|
||||
# error no OS layer setup
|
||||
#endif
|
||||
|
||||
#if OS_GFX_STUB
|
||||
#include "gfx/stub/os_gfx_stub.h"
|
||||
#endif
|
||||
|
||||
#endif //OS_SWITCH_H
|
||||
|
||||
@@ -0,0 +1,636 @@
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Frontend Entry Points
|
||||
|
||||
internal void
|
||||
update_and_render(OS_Handle repaint_window_handle, void *user_data)
|
||||
{
|
||||
ProfTick(0);
|
||||
ProfBeginFunction();
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
|
||||
//- rjf: tick cache layers
|
||||
txt_user_clock_tick();
|
||||
geo_user_clock_tick();
|
||||
tex_user_clock_tick();
|
||||
|
||||
//- rjf: pick target hz
|
||||
// TODO(rjf): maximize target, given all windows and their monitors
|
||||
F32 target_hz = os_default_refresh_rate();
|
||||
if(frame_time_us_history_idx > 32)
|
||||
{
|
||||
// rjf: calculate average frame time out of the last N
|
||||
U64 num_frames_in_history = Min(ArrayCount(frame_time_us_history), frame_time_us_history_idx);
|
||||
U64 frame_time_history_sum_us = 0;
|
||||
for(U64 idx = 0; idx < num_frames_in_history; idx += 1)
|
||||
{
|
||||
frame_time_history_sum_us += frame_time_us_history[idx];
|
||||
}
|
||||
U64 frame_time_history_avg_us = frame_time_history_sum_us/num_frames_in_history;
|
||||
|
||||
// rjf: pick among a number of sensible targets to snap to, given how well
|
||||
// we've been performing
|
||||
F32 possible_alternate_hz_targets[] = {target_hz, 60.f, 120.f, 144.f, 240.f};
|
||||
F32 best_target_hz = target_hz;
|
||||
S64 best_target_hz_frame_time_us_diff = max_S64;
|
||||
for(U64 idx = 0; idx < ArrayCount(possible_alternate_hz_targets); idx += 1)
|
||||
{
|
||||
F32 candidate = possible_alternate_hz_targets[idx];
|
||||
if(candidate <= target_hz)
|
||||
{
|
||||
U64 candidate_frame_time_us = 1000000/(U64)candidate;
|
||||
S64 frame_time_us_diff = (S64)frame_time_history_avg_us - (S64)candidate_frame_time_us;
|
||||
if(abs_s64(frame_time_us_diff) < best_target_hz_frame_time_us_diff)
|
||||
{
|
||||
best_target_hz = candidate;
|
||||
best_target_hz_frame_time_us_diff = frame_time_us_diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
target_hz = best_target_hz;
|
||||
}
|
||||
|
||||
//- rjf: target Hz -> delta time
|
||||
F32 dt = 1.f/target_hz;
|
||||
|
||||
//- rjf: get events from the OS
|
||||
OS_EventList events = {0};
|
||||
if(os_handle_match(repaint_window_handle, os_handle_zero()))
|
||||
{
|
||||
events = os_get_events(scratch.arena, df_gfx_state->num_frames_requested == 0);
|
||||
}
|
||||
|
||||
//- rjf: begin measuring actual per-frame work
|
||||
U64 begin_time_us = os_now_microseconds();
|
||||
|
||||
//- rjf: bind change
|
||||
if(!df_gfx_state->confirm_active && df_gfx_state->bind_change_active)
|
||||
{
|
||||
if(os_key_press(&events, os_handle_zero(), 0, OS_Key_Esc))
|
||||
{
|
||||
df_gfx_state->bind_change_active = 0;
|
||||
}
|
||||
if(os_key_press(&events, os_handle_zero(), 0, OS_Key_Delete))
|
||||
{
|
||||
df_unbind_spec(df_gfx_state->bind_change_cmd_spec, df_gfx_state->bind_change_binding);
|
||||
df_gfx_state->bind_change_active = 0;
|
||||
DF_CmdParams p = df_cmd_params_from_gfx();
|
||||
df_push_cmd__root(&p, df_cmd_spec_from_core_cmd_kind(df_g_cfg_src_write_cmd_kind_table[DF_CfgSrc_User]));
|
||||
}
|
||||
for(OS_Event *event = events.first, *next = 0; event != 0; event = next)
|
||||
{
|
||||
if(event->kind == OS_EventKind_Press &&
|
||||
event->key != OS_Key_Esc &&
|
||||
event->key != OS_Key_Return &&
|
||||
event->key != OS_Key_Backspace &&
|
||||
event->key != OS_Key_Delete &&
|
||||
event->key != OS_Key_LeftMouseButton &&
|
||||
event->key != OS_Key_RightMouseButton &&
|
||||
event->key != OS_Key_Ctrl &&
|
||||
event->key != OS_Key_Alt &&
|
||||
event->key != OS_Key_Shift)
|
||||
{
|
||||
df_gfx_state->bind_change_active = 0;
|
||||
DF_Binding binding = zero_struct;
|
||||
{
|
||||
binding.key = event->key;
|
||||
binding.flags = event->flags;
|
||||
}
|
||||
df_unbind_spec(df_gfx_state->bind_change_cmd_spec, df_gfx_state->bind_change_binding);
|
||||
df_bind_spec(df_gfx_state->bind_change_cmd_spec, binding);
|
||||
U32 codepoint = os_codepoint_from_event_flags_and_key(event->flags, event->key);
|
||||
os_text(&events, os_handle_zero(), codepoint);
|
||||
os_eat_event(&events, event);
|
||||
DF_CmdParams p = df_cmd_params_from_gfx();
|
||||
df_push_cmd__root(&p, df_cmd_spec_from_core_cmd_kind(df_g_cfg_src_write_cmd_kind_table[DF_CfgSrc_User]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: take hotkeys
|
||||
{
|
||||
for(OS_Event *event = events.first, *next = 0;
|
||||
event != 0;
|
||||
event = next)
|
||||
{
|
||||
next = event->next;
|
||||
DF_Window *window = df_window_from_os_handle(event->window);
|
||||
DF_CmdParams params = window ? df_cmd_params_from_window(window) : df_cmd_params_from_gfx();
|
||||
if(event->kind == OS_EventKind_Press)
|
||||
{
|
||||
DF_Binding binding = {event->key, event->flags};
|
||||
DF_CmdSpecList spec_candidates = df_cmd_spec_list_from_binding(scratch.arena, binding);
|
||||
if(spec_candidates.first != 0 && !df_cmd_spec_is_nil(spec_candidates.first->spec))
|
||||
{
|
||||
DF_CmdSpec *spec = spec_candidates.first->spec;
|
||||
params.cmd_spec = spec;
|
||||
df_cmd_params_mark_slot(¶ms, DF_CmdParamSlot_CmdSpec);
|
||||
U32 hit_char = os_codepoint_from_event_flags_and_key(event->flags, event->key);
|
||||
os_eat_event(&events, event);
|
||||
df_push_cmd__root(¶ms, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_CommandFastPath));
|
||||
if(event->flags & OS_EventFlag_Alt)
|
||||
{
|
||||
window->menu_bar_focus_press_started = 0;
|
||||
}
|
||||
}
|
||||
df_gfx_request_frame();
|
||||
}
|
||||
else if(event->kind == OS_EventKind_Text)
|
||||
{
|
||||
String32 insertion32 = str32(&event->character, 1);
|
||||
String8 insertion8 = str8_from_32(scratch.arena, insertion32);
|
||||
DF_CmdSpec *spec = df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_InsertText);
|
||||
params.string = insertion8;
|
||||
df_cmd_params_mark_slot(¶ms, DF_CmdParamSlot_String);
|
||||
df_push_cmd__root(¶ms, spec);
|
||||
df_gfx_request_frame();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: menu bar focus
|
||||
{
|
||||
for(OS_Event *event = events.first, *next = 0; event != 0; event = next)
|
||||
{
|
||||
next = event->next;
|
||||
DF_Window *ws = df_window_from_os_handle(event->window);
|
||||
if(ws == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if(event->kind == OS_EventKind_Press && event->key == OS_Key_Alt && event->is_repeat == 0)
|
||||
{
|
||||
ws->menu_bar_focused_on_press = ws->menu_bar_focused;
|
||||
ws->menu_bar_key_held = 1;
|
||||
ws->menu_bar_focus_press_started = 1;
|
||||
}
|
||||
if(event->kind == OS_EventKind_Release && event->key == OS_Key_Alt && event->is_repeat == 0)
|
||||
{
|
||||
ws->menu_bar_key_held = 0;
|
||||
}
|
||||
if(ws->menu_bar_focused && event->kind == OS_EventKind_Press && event->key == OS_Key_Alt && event->is_repeat == 0)
|
||||
{
|
||||
os_eat_event(&events, event);
|
||||
ws->menu_bar_focused = 0;
|
||||
}
|
||||
else if(ws->menu_bar_focus_press_started && !ws->menu_bar_focused && event->kind == OS_EventKind_Release && event->key == OS_Key_Alt && event->is_repeat == 0)
|
||||
{
|
||||
os_eat_event(&events, event);
|
||||
ws->menu_bar_focused = !ws->menu_bar_focused_on_press;
|
||||
ws->menu_bar_focus_press_started = 0;
|
||||
}
|
||||
else if(event->kind == OS_EventKind_Press && event->key == OS_Key_Esc && ws->menu_bar_focused && !ui_any_ctx_menu_is_open())
|
||||
{
|
||||
os_eat_event(&events, event);
|
||||
ws->menu_bar_focused = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: gather root-level commands
|
||||
DF_CmdList cmds = df_core_gather_root_cmds(scratch.arena);
|
||||
|
||||
//- rjf: begin frame
|
||||
df_core_begin_frame(scratch.arena, &cmds, dt);
|
||||
df_gfx_begin_frame(scratch.arena, &cmds);
|
||||
|
||||
//- rjf: queue drop for drag/drop
|
||||
if(df_drag_is_active())
|
||||
{
|
||||
for(OS_Event *event = events.first; event != 0; event = event->next)
|
||||
{
|
||||
if(event->kind == OS_EventKind_Release && event->key == OS_Key_LeftMouseButton)
|
||||
{
|
||||
df_queue_drag_drop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: auto-focus moused-over windows while dragging
|
||||
if(df_drag_is_active())
|
||||
{
|
||||
B32 over_focused_window = 0;
|
||||
{
|
||||
for(DF_Window *window = df_gfx_state->first_window; window != 0; window = window->next)
|
||||
{
|
||||
Vec2F32 mouse = os_mouse_from_window(window->os);
|
||||
Rng2F32 rect = os_client_rect_from_window(window->os);
|
||||
if(os_window_is_focused(window->os) && contains_2f32(rect, mouse))
|
||||
{
|
||||
over_focused_window = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!over_focused_window)
|
||||
{
|
||||
for(DF_Window *window = df_gfx_state->first_window; window != 0; window = window->next)
|
||||
{
|
||||
Vec2F32 mouse = os_mouse_from_window(window->os);
|
||||
Rng2F32 rect = os_client_rect_from_window(window->os);
|
||||
if(!os_window_is_focused(window->os) && contains_2f32(rect, mouse))
|
||||
{
|
||||
os_window_focus(window->os);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: update & render
|
||||
{
|
||||
d_begin_frame();
|
||||
for(DF_Window *w = df_gfx_state->first_window; w != 0; w = w->next)
|
||||
{
|
||||
df_window_update_and_render(scratch.arena, &events, w, &cmds);
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: end frontend frame, send signals, etc.
|
||||
df_gfx_end_frame();
|
||||
df_core_end_frame();
|
||||
|
||||
//- rjf: submit rendering to all windows
|
||||
{
|
||||
r_begin_frame();
|
||||
for(DF_Window *w = df_gfx_state->first_window; w != 0; w = w->next)
|
||||
{
|
||||
r_window_begin_frame(w->os, w->r);
|
||||
d_submit_bucket(w->os, w->r, w->draw_bucket);
|
||||
r_window_end_frame(w->os, w->r);
|
||||
}
|
||||
r_end_frame();
|
||||
}
|
||||
|
||||
//- rjf: take window closing events
|
||||
for(OS_Event *e = events.first; e; e = e->next)
|
||||
{
|
||||
if(e->kind == OS_EventKind_WindowClose)
|
||||
{
|
||||
for(DF_Window *w = df_gfx_state->first_window; w != 0; w = w->next)
|
||||
{
|
||||
if(os_handle_match(w->os, e->window))
|
||||
{
|
||||
DF_CmdParams params = df_cmd_params_from_window(w);
|
||||
df_push_cmd__root(¶ms, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_CloseWindow));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: determine frame time, record into history
|
||||
U64 end_time_us = os_now_microseconds();
|
||||
U64 frame_time_us = end_time_us-begin_time_us;
|
||||
frame_time_us_history[frame_time_us_history_idx%ArrayCount(frame_time_us_history)] = frame_time_us;
|
||||
frame_time_us_history_idx += 1;
|
||||
|
||||
scratch_end(scratch);
|
||||
ProfEnd();
|
||||
}
|
||||
|
||||
internal CTRL_WAKEUP_FUNCTION_DEF(wakeup_hook)
|
||||
{
|
||||
os_send_wakeup_event();
|
||||
}
|
||||
|
||||
internal void
|
||||
entry_point(int argc, char **argv)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
#if PROFILE_TELEMETRY
|
||||
local_persist U8 tm_data[MB(64)];
|
||||
tmLoadLibrary(TM_RELEASE);
|
||||
tmSetMaxThreadCount(1024);
|
||||
tmInitialize(sizeof(tm_data), (char *)tm_data);
|
||||
#endif
|
||||
ThreadName("[main]");
|
||||
|
||||
//- rjf: initialize basic dependencies
|
||||
os_init(argc, argv);
|
||||
|
||||
//- rjf: parse command line arguments
|
||||
CmdLine cmdln = cmd_line_from_string_list(scratch.arena, os_get_command_line_arguments());
|
||||
ExecMode exec_mode = ExecMode_Normal;
|
||||
String8 user_cfg_path = str8_lit("");
|
||||
String8 profile_cfg_path = str8_lit("");
|
||||
B32 capture = 0;
|
||||
B32 auto_run = 0;
|
||||
B32 auto_step = 0;
|
||||
B32 jit_attach = 0;
|
||||
U64 jit_pid = 0;
|
||||
U64 jit_code = 0;
|
||||
U64 jit_addr = 0;
|
||||
{
|
||||
if(cmd_line_has_flag(&cmdln, str8_lit("ipc")))
|
||||
{
|
||||
exec_mode = ExecMode_IPCSender;
|
||||
}
|
||||
else if(cmd_line_has_flag(&cmdln, str8_lit("convert")))
|
||||
{
|
||||
exec_mode = ExecMode_Converter;
|
||||
}
|
||||
else if(cmd_line_has_flag(&cmdln, str8_lit("?")) ||
|
||||
cmd_line_has_flag(&cmdln, str8_lit("help")))
|
||||
{
|
||||
exec_mode = ExecMode_Help;
|
||||
}
|
||||
user_cfg_path = cmd_line_string(&cmdln, str8_lit("user"));
|
||||
profile_cfg_path = cmd_line_string(&cmdln, str8_lit("profile"));
|
||||
capture = cmd_line_has_flag(&cmdln, str8_lit("capture"));
|
||||
auto_run = cmd_line_has_flag(&cmdln, str8_lit("auto_run"));
|
||||
auto_step = cmd_line_has_flag(&cmdln, str8_lit("auto_step"));
|
||||
String8 jit_pid_string = {0};
|
||||
String8 jit_code_string = {0};
|
||||
String8 jit_addr_string = {0};
|
||||
jit_pid_string = cmd_line_string(&cmdln, str8_lit("jit_pid"));
|
||||
jit_code_string = cmd_line_string(&cmdln, str8_lit("jit_code"));
|
||||
jit_addr_string = cmd_line_string(&cmdln, str8_lit("jit_addr"));
|
||||
try_u64_from_str8_c_rules(jit_pid_string, &jit_pid);
|
||||
try_u64_from_str8_c_rules(jit_code_string, &jit_code);
|
||||
try_u64_from_str8_c_rules(jit_addr_string, &jit_addr);
|
||||
jit_attach = (jit_addr != 0);
|
||||
}
|
||||
|
||||
//- rjf: auto-start capture
|
||||
if(capture)
|
||||
{
|
||||
ProfBeginCapture("raddbg");
|
||||
}
|
||||
|
||||
//- rjf: set default user/profile paths
|
||||
{
|
||||
String8 user_program_data_path = os_string_from_system_path(scratch.arena, OS_SystemPath_UserProgramData);
|
||||
String8 user_data_folder = push_str8f(scratch.arena, "%S/%S", user_program_data_path, str8_lit("raddbg"));
|
||||
os_make_directory(user_data_folder);
|
||||
if(user_cfg_path.size == 0)
|
||||
{
|
||||
user_cfg_path = push_str8f(scratch.arena, "%S/default.raddbg_user", user_data_folder);
|
||||
}
|
||||
if(profile_cfg_path.size == 0)
|
||||
{
|
||||
profile_cfg_path = push_str8f(scratch.arena, "%S/default.raddbg_profile", user_data_folder);
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: dispatch to top-level codepath based on execution mode
|
||||
switch(exec_mode)
|
||||
{
|
||||
//- rjf: normal execution
|
||||
default:
|
||||
case ExecMode_Normal:
|
||||
{
|
||||
//- rjf: set up shared memory for ipc
|
||||
OS_Handle ipc_shared_memory = os_shared_memory_alloc(IPC_SHARED_MEMORY_BUFFER_SIZE, ipc_shared_memory_name);
|
||||
void *ipc_shared_memory_base = os_shared_memory_view_open(ipc_shared_memory, r1u64(0, IPC_SHARED_MEMORY_BUFFER_SIZE));
|
||||
OS_Handle ipc_semaphore = os_semaphore_alloc(1, 1, ipc_semaphore_name);
|
||||
IPCInfo *ipc_info = (IPCInfo *)ipc_shared_memory_base;
|
||||
ipc_info->msg_size = 0;
|
||||
|
||||
//- rjf: initialize stuff we depend on
|
||||
{
|
||||
hs_init();
|
||||
txt_init();
|
||||
dbgi_init();
|
||||
txti_init();
|
||||
demon_init();
|
||||
ctrl_init(wakeup_hook);
|
||||
dasm_init();
|
||||
os_graphical_init();
|
||||
fp_init();
|
||||
r_init(&cmdln);
|
||||
tex_init();
|
||||
geo_init();
|
||||
f_init();
|
||||
DF_StateDeltaHistory *hist = df_state_delta_history_alloc();
|
||||
df_core_init(user_cfg_path, profile_cfg_path, hist);
|
||||
df_gfx_init(update_and_render, hist);
|
||||
os_set_cursor(OS_Cursor_Pointer);
|
||||
}
|
||||
|
||||
//- rjf: setup initial target from command line args
|
||||
{
|
||||
String8List args = cmdln.inputs;
|
||||
if(args.node_count > 0 && args.first->string.size != 0)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
DF_Entity *target = df_entity_alloc(0, df_entity_root(), DF_EntityKind_Target);
|
||||
df_entity_equip_b32(target, 1);
|
||||
df_entity_equip_cfg_src(target, DF_CfgSrc_CommandLine);
|
||||
String8List passthrough_args_list = {0};
|
||||
for(String8Node *n = args.first->next; n != 0; n = n->next)
|
||||
{
|
||||
str8_list_push(scratch.arena, &passthrough_args_list, n->string);
|
||||
}
|
||||
|
||||
// rjf: equip exe
|
||||
if(args.first->string.size != 0)
|
||||
{
|
||||
DF_Entity *exe = df_entity_alloc(0, target, DF_EntityKind_Executable);
|
||||
df_entity_equip_name(0, exe, args.first->string);
|
||||
}
|
||||
|
||||
// rjf: equip path
|
||||
String8 path_part_of_arg = str8_chop_last_slash(args.first->string);
|
||||
if(path_part_of_arg.size != 0)
|
||||
{
|
||||
String8 path = push_str8f(scratch.arena, "%S/", path_part_of_arg);
|
||||
DF_Entity *execution_path = df_entity_alloc(0, target, DF_EntityKind_ExecutionPath);
|
||||
df_entity_equip_name(0, execution_path, path);
|
||||
}
|
||||
|
||||
// rjf: equip args
|
||||
StringJoin join = {str8_lit(""), str8_lit(" "), str8_lit("")};
|
||||
String8 args_str = str8_list_join(scratch.arena, &passthrough_args_list, &join);
|
||||
if(args_str.size != 0)
|
||||
{
|
||||
DF_Entity *args_entity = df_entity_alloc(0, target, DF_EntityKind_Arguments);
|
||||
df_entity_equip_name(0, args_entity, args_str);
|
||||
}
|
||||
scratch_end(scratch);
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: main application loop
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
//- rjf: get IPC messages & dispatch ui commands from them
|
||||
{
|
||||
if(os_semaphore_take(ipc_semaphore, max_U64))
|
||||
{
|
||||
if(ipc_info->msg_size != 0)
|
||||
{
|
||||
U8 *buffer = (U8 *)(ipc_info+1);
|
||||
U64 msg_size = ipc_info->msg_size;
|
||||
String8 cmd_string = str8(buffer, msg_size);
|
||||
ipc_info->msg_size = 0;
|
||||
DF_Window *dst_window = df_gfx_state->first_window;
|
||||
for(DF_Window *window = dst_window; window != 0; window = window->next)
|
||||
{
|
||||
if(os_window_is_focused(window->os))
|
||||
{
|
||||
dst_window = window;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(dst_window != 0)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
String8 cmd_spec_string = df_cmd_name_part_from_string(cmd_string);
|
||||
DF_CmdSpec *cmd_spec = df_cmd_spec_from_string(cmd_spec_string);
|
||||
if(!df_cmd_spec_is_nil(cmd_spec))
|
||||
{
|
||||
DF_CmdParams params = df_cmd_params_from_gfx();
|
||||
DF_CtrlCtx ctrl_ctx = df_ctrl_ctx_from_window(dst_window);
|
||||
String8 error = df_cmd_params_apply_spec_query(scratch.arena, &ctrl_ctx, ¶ms, cmd_spec, df_cmd_arg_part_from_string(cmd_string));
|
||||
if(error.size == 0)
|
||||
{
|
||||
df_push_cmd__root(¶ms, cmd_spec);
|
||||
}
|
||||
else
|
||||
{
|
||||
DF_CmdParams params = df_cmd_params_from_window(dst_window);
|
||||
params.string = error;
|
||||
df_cmd_params_mark_slot(¶ms, DF_CmdParamSlot_String);
|
||||
df_push_cmd__root(¶ms, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_Error));
|
||||
}
|
||||
}
|
||||
scratch_end(scratch);
|
||||
}
|
||||
}
|
||||
os_semaphore_drop(ipc_semaphore);
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: update & render frame
|
||||
OS_Handle repaint_window = {0};
|
||||
update_and_render(repaint_window, 0);
|
||||
|
||||
//- rjf: auto run
|
||||
if(auto_run)
|
||||
{
|
||||
auto_run = 0;
|
||||
DF_CmdParams params = df_cmd_params_from_gfx();
|
||||
df_push_cmd__root(¶ms, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_LaunchAndRun));
|
||||
}
|
||||
|
||||
//- rjf: auto step
|
||||
if(auto_step)
|
||||
{
|
||||
auto_step = 0;
|
||||
DF_CmdParams params = df_cmd_params_from_gfx();
|
||||
df_push_cmd__root(¶ms, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_StepInto));
|
||||
}
|
||||
|
||||
//- rjf: jit attach
|
||||
if(jit_attach)
|
||||
{
|
||||
jit_attach = 0;
|
||||
DF_CmdParams params = df_cmd_params_from_gfx();
|
||||
df_cmd_params_mark_slot(¶ms, DF_CmdParamSlot_ID);
|
||||
params.id = jit_pid;
|
||||
df_push_cmd__root(¶ms, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_Attach));
|
||||
}
|
||||
|
||||
//- rjf: quit if no windows are left
|
||||
if(df_gfx_state->first_window == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}break;
|
||||
|
||||
//- rjf: inter-process communication message sender
|
||||
case ExecMode_IPCSender:
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
|
||||
//- rjf: grab ipc shared memory
|
||||
OS_Handle ipc_shared_memory = os_shared_memory_open(ipc_shared_memory_name);
|
||||
void *ipc_shared_memory_base = os_shared_memory_view_open(ipc_shared_memory, r1u64(0, MB(16)));
|
||||
if(ipc_shared_memory_base != 0)
|
||||
{
|
||||
OS_Handle ipc_semaphore = os_semaphore_open(ipc_semaphore_name);
|
||||
IPCInfo *ipc_info = (IPCInfo *)ipc_shared_memory_base;
|
||||
if(os_semaphore_take(ipc_semaphore, os_now_microseconds() + Million(6)))
|
||||
{
|
||||
U8 *buffer = (U8 *)(ipc_info+1);
|
||||
U64 buffer_max = IPC_SHARED_MEMORY_BUFFER_SIZE - sizeof(IPCInfo);
|
||||
StringJoin join = {str8_lit(""), str8_lit(" "), str8_lit("")};
|
||||
String8 msg = str8_list_join(scratch.arena, &cmdln.inputs, &join);
|
||||
ipc_info->msg_size = Min(buffer_max, msg.size);
|
||||
MemoryCopy(buffer, msg.str, ipc_info->msg_size);
|
||||
os_semaphore_drop(ipc_semaphore);
|
||||
}
|
||||
}
|
||||
|
||||
scratch_end(scratch);
|
||||
}break;
|
||||
|
||||
//- rjf: built-in pdb/dwarf -> raddbg converter mode
|
||||
case ExecMode_Converter:
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
|
||||
//- rjf: parse arguments
|
||||
PDBCONV_Params *params = pdb_convert_params_from_cmd_line(scratch.arena, &cmdln);
|
||||
|
||||
//- rjf: open output file
|
||||
String8 output_name = push_str8_copy(scratch.arena, params->output_name);
|
||||
OS_Handle out_file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_Write, output_name);
|
||||
B32 out_file_is_good = !os_handle_match(out_file, os_handle_zero());
|
||||
|
||||
//- rjf: convert
|
||||
PDBCONV_Out *out = 0;
|
||||
if(out_file_is_good)
|
||||
{
|
||||
out = pdbconv_convert(scratch.arena, params);
|
||||
}
|
||||
|
||||
//- rjf: bake file
|
||||
if(out != 0 && params->output_name.size > 0)
|
||||
{
|
||||
String8List baked = {0};
|
||||
cons_bake_file(scratch.arena, out->root, &baked);
|
||||
U64 off = 0;
|
||||
for(String8Node *node = baked.first; node != 0; node = node->next)
|
||||
{
|
||||
os_file_write(out_file, r1u64(off, off+node->string.size), node->string.str);
|
||||
off += node->string.size;
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: close output file
|
||||
os_file_close(out_file);
|
||||
|
||||
scratch_end(scratch);
|
||||
}break;
|
||||
|
||||
//- rjf: help message box
|
||||
case ExecMode_Help:
|
||||
{
|
||||
os_graphical_message(0,
|
||||
str8_lit("The RAD Debugger - Help"),
|
||||
str8_lit("The following options may be used when starting the RAD Debugger from the command line:\n\n"
|
||||
"--user:<path>\n"
|
||||
"Use to specify the location of a user file which should be used. User files are used to store settings for users, including window and panel setups, path mapping, and visual settings. If this file does not exist, it will be created as necessary. This file will be autosaved as user-related changes are made.\n\n"
|
||||
"--profile:<path>\n"
|
||||
"Use to specify the location of a profile file which should be used. Profile files are used to store settings for users and projects. If this file does not exist, it will be created as necessary. This file will be autosaved as profile-related changes are made.\n\n"
|
||||
"--auto_step\n"
|
||||
"This will step into all targets after the debugger initially starts.\n\n"
|
||||
"--auto_run\n"
|
||||
"This will run all targets after the debugger initially starts.\n\n"
|
||||
"--ipc <command>\n"
|
||||
"This will launch the debugger in the non-graphical IPC mode, which is used to communicate with another running instance of the debugger. The debugger instance will launch, send the specified command, then immediately terminate. This may be used by editors or other programs to control the debugger.\n\n"));
|
||||
}break;
|
||||
}
|
||||
|
||||
scratch_end(scratch);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,463 @@
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Hot, High Priority Tasks (Complete Unusability, Crashes, Fire-Worthy)
|
||||
//
|
||||
// [ ] ** Thread/process control bullet-proofing, including solo-step mode
|
||||
// [ ] ** ASAN targets
|
||||
//
|
||||
// [ ] ** In solo-stepping mode, if I step over something like CreateFileA, it
|
||||
// pseudo-hangs the debugger. I can't seem to do anything else, including
|
||||
// "Kill All". I have to close the debugger and restart it, AFAICT?
|
||||
//
|
||||
// [ ] ** I tried to debug a console program, and "step into" didn't seem to
|
||||
// work. Instead, it just started running the program, but the program
|
||||
// seemed to hang, and then the debugger pseudo-hung with a continual
|
||||
// progress bar in the disassembly window. I had to close and restart. Is
|
||||
// console app debugging not working yet, perhaps?
|
||||
//
|
||||
// [ ] register value committing in watch
|
||||
//
|
||||
// [ ] Setting the code_font/main_font values to a font name doesn't work.
|
||||
// Should probably make note that you have to set it to a path to a TTF,
|
||||
// since that's not normally how Windows fonts work.
|
||||
//
|
||||
// [ ] ** Converter performance & heuristics for asynchronously doing it early
|
||||
//
|
||||
// [ ] disasm animation & go-to-address
|
||||
//
|
||||
// [ ] visualize mismatched source code and debug info
|
||||
// [ ] visualize remapped files (via path map)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Hot, Medium Priority Tasks (Low-Hanging-Fruit Features, UI Jank, Cleanup)
|
||||
//
|
||||
// [ ] ** while typing, "Alt" Windows menu things should not happen
|
||||
//
|
||||
// [ ] I was a little confused about what a profile file was. I understood
|
||||
// what the user file was, but the profile file sounded like it should
|
||||
// perhaps be per-project, yet it sounded like it was meant to be somewhat
|
||||
// global? I don't have any feedback here because it probably will make
|
||||
// sense once I use the debugger more, but I just thought I'd make a note
|
||||
// to say that I was confused about it after reading the manual, so
|
||||
// perhaps you could elaborate a little more on it in there.
|
||||
//
|
||||
// [ ] ** "Find Name" may not be working as advertised. In the description, it
|
||||
// says you can jump to a file, but if I type in the complete filename of
|
||||
// a file in the project and hit return, it just turns red and says it
|
||||
// couldn't find it. This happens even if the file is already open in a
|
||||
// tab.
|
||||
// [ ] "Find Name" would be a lot more useful if you could type partial
|
||||
// things, and it displayed a list, more like what happens in a
|
||||
// traditional text editor. Typing the entire name of a function to jump
|
||||
// to it is too laborious.
|
||||
//
|
||||
// [ ] Right-clicking on a thread in the Scheduler window pops up a context
|
||||
// menu, but you can't actually see it because the tooltip for the thread
|
||||
// draws on top of it, so you can't see the menu.
|
||||
//
|
||||
// [ ] In a "hover watch" (where you hover over a variable and it shows a pop-
|
||||
// up watch window), if you expand an item near the bottom of the listing,
|
||||
// it will be clipped to the bottom of the listing instead of showing the
|
||||
// actual items (ie., it doesn't resize the listing based on what's
|
||||
// actually visible)
|
||||
//
|
||||
// [ ] ** Function breakpoints should show up in the source listing. Without
|
||||
// them being visible, it is confusing when you run and you stop there,
|
||||
// because you're like "wait why did it stop" and then you later remember
|
||||
// that's because there was a function breakpoint there.
|
||||
//
|
||||
// [ ] ** One very nice feature of RemedyBG that I use all the time is the
|
||||
// ability to put "$err, hr" into the watch window, which will just show
|
||||
// the value of GetLastError() as a string. This is super useful for
|
||||
// debugging, so you don't have to litter your own code with it.
|
||||
//
|
||||
// [ ] Tooltip Coverage:
|
||||
// [ ] lock icon
|
||||
// [ ] "rotation arrow" icon next to executables
|
||||
//
|
||||
// [ ] Using the word "symbol" in "Code (Symbol)" seems like a bad idea, since
|
||||
// you're referring to non-identifier characters, but in a debugger
|
||||
// "symbol" usually means something defined in the debug information.
|
||||
//
|
||||
// [ ] I LOVE ALT-W to add watch under cursor, but I would prefer to have it
|
||||
// add what's under the MOUSE cursor instead of the keyboard cursor. Can
|
||||
// we get a command for that so I can bind ALT-W to that instead?
|
||||
//
|
||||
// [ ] For theme editing, when you hove the mouse over a theme color entry and
|
||||
// it highlights that entry, it might help to temporarily change that
|
||||
// color to white (or the inverse of the background color, or whatever) so
|
||||
// that the user can see what things on the screen use that theme color.
|
||||
//
|
||||
// [ ] I couldn't figure out how to affect the "dim" color in constants that
|
||||
// have alternating bright/dim letters to show sections of a number. Is
|
||||
// this in the theme colors somewhere?
|
||||
//
|
||||
// [ ] For breakpoint-on-function, it would be great if it showed a list of
|
||||
// (partial) matches as you type, so you can stop typing once it gets the
|
||||
// right function instead of having to type the entire function name.
|
||||
//
|
||||
// [ ] Hovering over a source tab that is clipped should probably display the
|
||||
// full thing that was in that tab (like the whole filename, etc.). Right
|
||||
// now, hovering does nothing AFAICT.
|
||||
//
|
||||
// [ ] ** I couldn't figure out how to really view threads in the debugger.
|
||||
// The only place I found a thread list was in "The Scheduler", but it
|
||||
// only lists threads by ID, which is hard to use. I can hover over them
|
||||
// to get the stack, which helps, but it would be much nicer if the top
|
||||
// function was displayed in the window by default next to the thread.
|
||||
// [ ] ** It would be nice if thread listings displayed the name of the
|
||||
// thread, instead of just the ID.
|
||||
//
|
||||
// [ ] ** Scrollbars are barely visible for me, for some reason. I could not
|
||||
// find anything in the theme that would fill them with a solid, bright
|
||||
// color. Instead they are just a thin outline and the same color as the
|
||||
// scroll bar background.
|
||||
//
|
||||
// [ ] Dragging a window tab (like Locals or Registers or whatnot) and
|
||||
// canceling with ESC should revert the window tab to where it was.
|
||||
// Currently, it leaves the window tab reordered if you dragged over its
|
||||
// window and shuffled its position.
|
||||
//
|
||||
// [ ] Many of the UI elements, like the menus, would like better if they had
|
||||
// a little bit of margin. Having the text right next to the edges, and
|
||||
// with no line spacing, makes it harder to read things quickly.
|
||||
//
|
||||
// [ ] Menus take too long to show up. I would prefer it if they were instant.
|
||||
// The animation doesn't really provide any useful cues, since I know
|
||||
// where the menu came from.
|
||||
//
|
||||
// [ ] Theme window should include font scaling. I was able to find the
|
||||
// command for increasing the font scale, but I imagine most people
|
||||
// wouldn't think to look there.
|
||||
// [ ] I had to go into the user file to change the font. That should probably
|
||||
// be in the theme window?
|
||||
//
|
||||
// [ ] The way the "commands" view worked was idiosyncratic. All the other
|
||||
// views stay up, but that one goes away whenever I select a command for
|
||||
// some reason.
|
||||
// [ ] Also, I could not move the commands window anywhere AFAICT. It seems
|
||||
// to just pop up over whatever window I currently have selected. This
|
||||
// would make sense for a hotkey (which I assume is the way it was
|
||||
// designed), but it seems like it should be permanent if you can select
|
||||
// it from the View menu.
|
||||
// [ ] If the command window is not wide enough, you cannot read the
|
||||
// description of a command because it doesn't word-wrap, nor can you
|
||||
// hover over it to get the description in a tooltip (AFAICT).
|
||||
//
|
||||
// [ ] It'd be nice to have a "goto byte" option for source views, for jumping
|
||||
// to error messages that are byte-based instead of line-based.
|
||||
//
|
||||
// [ ] Pressing the left mouse button on the menu bar and dragging does not
|
||||
// move through the menus as expected - instead, it opens the one you
|
||||
// clicked down on, then does nothing until you release, at which point it
|
||||
// opens the menu you released on.
|
||||
// [ ] Similarly, pressing the left mouse button on a menu and dragging to an
|
||||
// item, then releasing, does not trigger that item as expected. Instead,
|
||||
// it is a nop, and it waits for you to click again on the item.
|
||||
//
|
||||
// [ ] Working with panels felt cumbersome. I couldn't figure out any way to
|
||||
// quickly arrange the display without manually selecting "split panel"
|
||||
// and "close panel" and stuff from the menu, which took a long time.
|
||||
// - @polish @feature ui for dragging tab -> bundling panel split options
|
||||
//
|
||||
// [ ] I found the "context menu" convention to be confusing. For example, if
|
||||
// I left-click on a tab, it selects the tab. If I right-click on a tab,
|
||||
// it opens the context menu. However, if I left-click on a module, it
|
||||
// opens the context window. It seems like maybe menus should be right,
|
||||
// and left should do the default action, more consistently?
|
||||
//
|
||||
// [ ] Hovering over disassembly highlights blocks of instructions, which I
|
||||
// assume correspond to source lines. But perhaps it should also highlight
|
||||
// the source lines? The inverse hover works (you hover over source, and
|
||||
// it highlights ASM), but ASM->source doesn't.
|
||||
//
|
||||
// [ ] It wasn't clear to me how you save a user or profile file. I can see
|
||||
// how to load them, but not how you save them. Obviously I can just copy
|
||||
// the files myself in the shell, but it seemed weird that there was no
|
||||
// "save" option in the menus.
|
||||
//
|
||||
// [ ] @cleanup @feature double & triple click select in source views
|
||||
// [ ] @feature hovering truncated text in UI for some amount of time -> show
|
||||
// tooltip with full text
|
||||
// [ ] @feature disasm keyboard navigation & copy/paste
|
||||
// [ ] @feature debug info overrides (both path-based AND module-based)
|
||||
// [ ] configure tab size
|
||||
// [ ] run-to-line needs to work if no processes are running
|
||||
// - place temp bp, attach "die on hit" flag or something like that?
|
||||
// [ ] auto-scroll output window
|
||||
//
|
||||
// [ ] C++ single & multi inheritance member visualization in watch window
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Hot, Low Priority Tasks (UI Opinions, Less-Serious Jank, Preferences, Cleanup)
|
||||
//
|
||||
// [ ] ** Directory picking is kind of busted, as it goes through the same
|
||||
// path as file picking, and this doesn't give the user a clean path to
|
||||
// actually pick a folder, just navigate with them
|
||||
//
|
||||
// [ ] ** Hover-Eval-Popup-While-Scrolling is very annoying!
|
||||
//
|
||||
// [ ] ** In the call stack, I would like to be able to click quickly and move
|
||||
// around the stack. Right now, you can do that with the first and third
|
||||
// column, but the second column drops down a context menu. Since right
|
||||
// click is already for context menus, can it not just be that double-
|
||||
// clicking any column jumps to that stack frame?
|
||||
//
|
||||
// [ ] ** I find it really hard to read the code with the heavyweight lines
|
||||
// running through it for breakpoints and stepping and things. Is there a
|
||||
// way to turn the lines off? AFAICT they are based on thread and
|
||||
// breakpoint color, so you can't really control the line drawing? I might
|
||||
// be fine with them, but they would have to be much more light (like
|
||||
// alpha 0.1 or something)
|
||||
//
|
||||
// [ ] It's confusing that ENTER is the way you expand and collapse things in
|
||||
// the watch window, but then also how you edit them if they are not
|
||||
// expandable? It seems like this should be consistent (one way to edit,
|
||||
// one way to expand/collapse, that are distinct)
|
||||
//
|
||||
// [ ] I didn't understand the terminology "Equip With Color". Does that just
|
||||
// mean specify the color used to display it? Is "Apply Color" perhaps a
|
||||
// bit more user-friendly?
|
||||
//
|
||||
// [ ] The cursor feels a bit too huge vertically.
|
||||
//
|
||||
// [ ] The hex format for color values in the config file was a real
|
||||
// mindbender. It's prefixed with "0x", so I was assuming it was either
|
||||
// Windows Big Endian (0xAARRGGBB) or Mac Little Endian (0xAABBGGRR). To
|
||||
// my surprise, it was neither - it was actually web format (RRGGBBAA),
|
||||
// which I was not expecting because that is normally written with a
|
||||
// number sign (#AARRGGBB) not an 0x.
|
||||
//
|
||||
// [ ] Clicking on either side of a scroll bar is idiosyncratic. Normally,
|
||||
// that is "page up" / "page down", but here it is "smooth scroll upward"
|
||||
// / "smooth scroll downward" for some reason?
|
||||
//
|
||||
// [ ] Hitting ESC during a color picker drag should abort the color picking
|
||||
// and revert to the previous color. Currently, it just accepts the last
|
||||
// drag result as the new color.
|
||||
//
|
||||
// [ ] It was not clear to me why a small "tab picker" appeared when I got to
|
||||
// a certain number of tabs. It seemed to appear even if the tabs were
|
||||
// quite large, and there was no need to a drop-down menu to pick them. It
|
||||
// feels like either it should always be there, or it should only show up
|
||||
// if at least one tab gets small enough to have its name cut off?
|
||||
//
|
||||
// [ ] can it ignore stepping into _RTC_CheckStackVars generated functions?
|
||||
// [ ] mouse back button should make view to go back after I double clicked
|
||||
// on function to open it
|
||||
// [ ] middle mouse button on tab should close it
|
||||
// [ ] pressing random keyboard keys in source code advances text cursor like
|
||||
// you were inputting text, very strange.
|
||||
// [ ] Alt+8 to switch to disassembly would be nice (regardless on which
|
||||
// panel was previous, don't want to use ctrl+, multiple times)
|
||||
// Alt+8 for disasm and Alt+6 for memory view are shortcuts I often use
|
||||
// in VS
|
||||
// [ ] what's up with decimal number coloring where every group of 3 are in
|
||||
// different color? can I turn it off? And why sometimes digits in number
|
||||
// start with brighter color, but sometimes with darker - shouldn't it
|
||||
// always have the same color ordering?
|
||||
// [ ] it would be nice to have "show in explorer" for right click on source
|
||||
// file tab (opens explorer & selects the file)
|
||||
// [ ] it would be nice if Alt+o in source file would switch between .h and
|
||||
// .c/cpp file (just look for same name in same folder)
|
||||
// [ ] in watch window when I enter some new expression and then click mouse
|
||||
// away from cell, then it should behave the same as if I pressed enter.
|
||||
// Currently it does the same as if I have pressed esc and I have lost my
|
||||
// expression
|
||||
// [ ] default font size is too small for me - not only source code, but
|
||||
// menus/tab/watch names (which don't resize). Maybe you could query
|
||||
// Windows for initial font size?
|
||||
// [ ] zooming behaves very strangely - sometimes it zooms source code,
|
||||
// sometimes both source code and menu/tab/watch font size, sometimes
|
||||
// just menu/tab/watch font size not source size.
|
||||
// [ ] icon fonts glyphs sometimes disappear for specific font size, but they
|
||||
// reappear if you go +1 higher or -1 lower. Mostly red triangle in watch
|
||||
// values for "unknown identifier". But also yellow arrow in call stack
|
||||
// disappears if font size gets too large.
|
||||
// [ ] undo close tab would be nice. If not for everything, then at least
|
||||
// just for source files
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Hot, Feature Tasks (Not really "low priority" but less urgent than fixes)
|
||||
//
|
||||
// [ ] For the Scheduler window, it would be nice if you could dim or
|
||||
// folderize threads that are not your threads - eg., if a thread doesn't
|
||||
// have any resolved stack pointers in your executable code, then you can
|
||||
// ignore it when you are focusing on your own code. I don't know what the
|
||||
// best way to detect this is, other than by walking the call stack... one
|
||||
// way might be to just have a way to separate threads you've named from
|
||||
// threads you haven't? Or, there could even be a debugger-specific API
|
||||
// that you use to tag them. Just some way that would make it easier to
|
||||
// focus on your own threads.
|
||||
//
|
||||
// [ ] autocomplete lister should respect position in edited expression,
|
||||
// tabbing through should autocomplete but not exit, etc.
|
||||
//
|
||||
// [ ] Fancy View Rules
|
||||
// [ ] table column boundaries should be checked against *AFTER* table
|
||||
// contents, not before
|
||||
// [ ] `text[:lang]` - interpret memory as text, in lang `lang`
|
||||
// [ ] `disasm:arch` - interpret memory as machine code for isa `arch`
|
||||
// [ ] `memory` - view memory in usual memory hex-editor view
|
||||
// NOTE(rjf): When the visualization system is solid, layers like dasm, txti,
|
||||
// and so on can be dispensed with, as things like the source view, disasm
|
||||
// view, or memory view will simply be specializations of the general purpose
|
||||
// viz system.
|
||||
// [ ] view rule hook for standalone visualization ui, granted its own
|
||||
// tab
|
||||
//
|
||||
// [ ] search-in-all-files
|
||||
//
|
||||
// [ ] Memory View
|
||||
// [ ] memory view mutation controls
|
||||
// [ ] memory view user-made annotations
|
||||
//
|
||||
// [ ] undo/redo
|
||||
// [ ] proper "go back" + "go forward" history navigations
|
||||
//
|
||||
// [ ] globally disable/configure default view rule-like things (string
|
||||
// viz for u8s in particular)
|
||||
// [ ] globally disable/configure bp/ip lines in source view
|
||||
//
|
||||
// [ ] @feature processor/data breakpoints
|
||||
// [ ] @feature automatically snap to search matches when searching source files
|
||||
// [ ] @feature entity views: filtering & reordering
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cold, Clean-up Tasks That Probably Only Ryan Notices
|
||||
// (E.G. Because They Are Code-Related Or Because Nobody Cares)
|
||||
//
|
||||
// [ ] @bug view-snapping in scroll-lists, accounting for mapping between
|
||||
// visual positions & logical positions (variably sized rows in watch,
|
||||
// table headers, etc.)
|
||||
// [ ] @bug selected frame should be keyed by run_idx or something so that it
|
||||
// can gracefully reset to the top frame when running
|
||||
// [ ] @cleanup collapse DF_CfgNodes into just being MD trees, find another way
|
||||
// to encode config source - don't need it at every node
|
||||
// [ ] @cleanup straighten out index/number space & types & terminology for
|
||||
// scroll lists
|
||||
// [ ] @cleanup simplification pass over eval visualization pipeline & types,
|
||||
// including view rule hooks
|
||||
// [ ] @cleanup naming pass over eval visualization part of the frontend,
|
||||
// "blocks" vs. "canvas" vs. "expansion" - etc.
|
||||
// [ ] @cleanup central worker thread pool - eliminate per-layer thread pools
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cold, Unsorted Notes (Deferred Until Existing Lists Mostly Exhausted)
|
||||
//
|
||||
// [ ] @feature types -> auto view rules (don't statefully fill view rules
|
||||
// given types, just query if no other view rule is present, & autofill
|
||||
// when editing)
|
||||
// [ ] @feature eval system -> somehow evaluate breakpoint hit counts? "meta"
|
||||
// variables?
|
||||
// [ ] @feature watch window labels
|
||||
// [ ] @feature scheduler -> thread grid view?
|
||||
//
|
||||
// [ ] @feature disasm view improvement features
|
||||
// [ ] interleaved src/dasm view
|
||||
// [ ] visualize jump destinations in disasm
|
||||
//
|
||||
// [ ] @feature eval ui improvement features
|
||||
// [ ] serializing eval view maps
|
||||
// [ ] view rule editors in hover-eval
|
||||
// [ ] view rule hook coverage
|
||||
// [ ] `each:(expr addition)` - apply some additional expression to all
|
||||
// elements in an array/linked list would be useful to look at only a
|
||||
// subset of an array of complex structs
|
||||
// [ ] `slider:(min max)` view rule
|
||||
// [ ] `v2f32` view rule
|
||||
// [ ] `v3` view rule
|
||||
// [ ] `quat` view rule
|
||||
// [ ] `matrix` view rule
|
||||
// [ ] `audio` waveform view rule
|
||||
// [ ] smart scopes - expression operators for "grab me the first type X"
|
||||
// [ ] "pinning" watch expressions, to attach it to a particular ctrl_ctx
|
||||
//
|
||||
// [ ] @feature header file for target -> debugger communication; printf, log,
|
||||
// etc.
|
||||
// [ ] @feature just-in-time debugging
|
||||
// [ ] @feature step-out-of-loop
|
||||
//
|
||||
//-[ ] long-term future notes from martins
|
||||
// [ ] core dump saving/loading
|
||||
// [ ] parallel call stacks view
|
||||
// [ ] parallel watch view
|
||||
// [ ] mixed native/interpreted/jit debugging
|
||||
// - it seems python has a top-level linked list of interpreter states,
|
||||
// which should allow the debugger to map native callstacks to python
|
||||
// code
|
||||
//
|
||||
// [ ] fancy string runs can include "weakness" information for text truncation
|
||||
// ... can prioritize certain parts of strings to be truncated before
|
||||
// others. would be good for e.g. the middle of a path
|
||||
// [ ] ui code maintenance, simplification, design, & robustness pass
|
||||
// [ ] page-up & page-down correct handling in keyboard nav
|
||||
// [ ] collapse context menus & command lister into same codepaths. filter by
|
||||
// context. parameterize by context.
|
||||
// [ ] collapse text cells & command lister & etc. into same codepath (?)
|
||||
// [ ] nested context menus
|
||||
// [ ] unified top-level cursor/typing/lister helper
|
||||
// [ ] font selection lister
|
||||
// [ ] font cache eviction (both for font tags, closing fp handles, and
|
||||
// rasterizations)
|
||||
// [ ] frontend speedup opportunities
|
||||
// [ ] tables in UI -> currently building per-row, could probably cut down on
|
||||
// # of boxes and # of draws by doing per-column in some cases?
|
||||
// [ ] font cache layer -> can probably cache (string*font*size) -> (run) too
|
||||
// (not just rasterization)... would save a *lot*, there is a ton of work
|
||||
// just in looking up & stitching stuff repeatedly
|
||||
// [ ] convert UI layout pass to not be naive recursive version
|
||||
// [ ] (big change) parallelize window ui build codepaths per-panel
|
||||
|
||||
#ifndef RADDBG_H
|
||||
#define RADDBG_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Build Settings
|
||||
|
||||
#define RADDBG_VERSION_MAJOR 0
|
||||
#define RADDBG_VERSION_MINOR 9
|
||||
#define RADDBG_VERSION_PATCH 4
|
||||
#define RADDBG_VERSION_STRING_LITERAL Stringify(RADDBG_VERSION_MAJOR) "." Stringify(RADDBG_VERSION_MINOR) "." Stringify(RADDBG_VERSION_PATCH)
|
||||
#if defined(NDEBUG)
|
||||
# define RADDBG_TITLE_STRING_LITERAL "The RAD Debugger (" RADDBG_VERSION_STRING_LITERAL " ALPHA) - " __DATE__ ""
|
||||
#else
|
||||
# define RADDBG_TITLE_STRING_LITERAL "The RAD Debugger (" RADDBG_VERSION_STRING_LITERAL " ALPHA) - " __DATE__ " [Debug]"
|
||||
#endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Top-Level Execution Types
|
||||
|
||||
typedef enum ExecMode
|
||||
{
|
||||
ExecMode_Normal,
|
||||
ExecMode_IPCSender,
|
||||
ExecMode_Converter,
|
||||
ExecMode_Help,
|
||||
}
|
||||
ExecMode;
|
||||
|
||||
typedef struct IPCInfo IPCInfo;
|
||||
struct IPCInfo
|
||||
{
|
||||
U64 msg_size;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Globals
|
||||
|
||||
#define IPC_SHARED_MEMORY_BUFFER_SIZE MB(16)
|
||||
StaticAssert(IPC_SHARED_MEMORY_BUFFER_SIZE > sizeof(IPCInfo), ipc_buffer_size_requirement);
|
||||
read_only global String8 ipc_shared_memory_name = str8_lit_comp("_raddbg_ipc_shared_memory_");
|
||||
read_only global String8 ipc_semaphore_name = str8_lit_comp("_raddbg_ipc_semaphore_");
|
||||
global U64 frame_time_us_history[64] = {0};
|
||||
global U64 frame_time_us_history_idx = 0;
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Frontend Entry Points
|
||||
|
||||
internal void update_and_render(OS_Handle repaint_window_handle, void *user_data);
|
||||
|
||||
#endif // RADDBG_H
|
||||
@@ -0,0 +1,176 @@
|
||||
////////////////////////////////
|
||||
//~ rjf: Includes
|
||||
|
||||
//- rjf: [h]
|
||||
#include "base/base_inc.h"
|
||||
#include "os/os_inc.h"
|
||||
#include "mdesk/mdesk.h"
|
||||
#include "hash_store/hash_store.h"
|
||||
#include "text_cache/text_cache.h"
|
||||
#include "path/path.h"
|
||||
#include "txti/txti.h"
|
||||
#include "coff/coff.h"
|
||||
#include "pe/pe.h"
|
||||
#include "raddbg_format/raddbg_format.h"
|
||||
#include "raddbg_format/raddbg_format_parse.h"
|
||||
#include "raddbg_cons/raddbg_cons.h"
|
||||
#include "raddbg_convert/pdb/raddbg_coff.h"
|
||||
#include "raddbg_convert/pdb/raddbg_codeview.h"
|
||||
#include "raddbg_convert/pdb/raddbg_msf.h"
|
||||
#include "raddbg_convert/pdb/raddbg_pdb.h"
|
||||
#include "raddbg_convert/pdb/raddbg_coff_conversion.h"
|
||||
#include "raddbg_convert/pdb/raddbg_codeview_conversion.h"
|
||||
#include "raddbg_convert/pdb/raddbg_from_pdb.h"
|
||||
#include "raddbg_convert/pdb/raddbg_codeview_stringize.h"
|
||||
#include "raddbg_convert/pdb/raddbg_pdb_stringize.h"
|
||||
#include "regs/regs.h"
|
||||
#include "regs/raddbg/regs_raddbg.h"
|
||||
#include "type_graph/type_graph.h"
|
||||
#include "dbgi/dbgi.h"
|
||||
#include "demon/demon_inc.h"
|
||||
#include "eval/eval_compiler.h"
|
||||
#include "eval/eval_machine.h"
|
||||
#include "eval/eval_parser.h"
|
||||
#include "unwind/unwind.h"
|
||||
#include "ctrl/ctrl_inc.h"
|
||||
#include "dasm/dasm.h"
|
||||
#include "font_provider/font_provider_inc.h"
|
||||
#include "render/render_inc.h"
|
||||
#include "texture_cache/texture_cache.h"
|
||||
#include "geo_cache/geo_cache.h"
|
||||
#include "font_cache/font_cache.h"
|
||||
#include "draw/draw.h"
|
||||
#include "ui/ui_inc.h"
|
||||
#include "df/df_inc.h"
|
||||
#include "raddbg.h"
|
||||
|
||||
//- rjf: [c]
|
||||
#include "base/base_inc.c"
|
||||
#include "os/os_inc.c"
|
||||
#include "mdesk/mdesk.c"
|
||||
#include "hash_store/hash_store.c"
|
||||
#include "text_cache/text_cache.c"
|
||||
#include "path/path.c"
|
||||
#include "txti/txti.c"
|
||||
#include "coff/coff.c"
|
||||
#include "pe/pe.c"
|
||||
#include "raddbg_format/raddbg_format.c"
|
||||
#include "raddbg_format/raddbg_format_parse.c"
|
||||
#include "raddbg_cons/raddbg_cons.c"
|
||||
#include "raddbg_convert/pdb/raddbg_msf.c"
|
||||
#include "raddbg_convert/pdb/raddbg_codeview.c"
|
||||
#include "raddbg_convert/pdb/raddbg_pdb.c"
|
||||
#include "raddbg_convert/pdb/raddbg_coff_conversion.c"
|
||||
#include "raddbg_convert/pdb/raddbg_codeview_conversion.c"
|
||||
#include "raddbg_convert/pdb/raddbg_codeview_stringize.c"
|
||||
#include "raddbg_convert/pdb/raddbg_pdb_stringize.c"
|
||||
#include "raddbg_convert/pdb/raddbg_from_pdb.c"
|
||||
#include "regs/regs.c"
|
||||
#include "regs/raddbg/regs_raddbg.c"
|
||||
#include "type_graph/type_graph.c"
|
||||
#include "dbgi/dbgi.c"
|
||||
#include "demon/demon_inc.c"
|
||||
#include "eval/eval_compiler.c"
|
||||
#include "eval/eval_machine.c"
|
||||
#include "eval/eval_parser.c"
|
||||
#include "unwind/unwind.c"
|
||||
#include "ctrl/ctrl_inc.c"
|
||||
#include "dasm/dasm.c"
|
||||
#include "font_provider/font_provider_inc.c"
|
||||
#include "render/render_inc.c"
|
||||
#include "texture_cache/texture_cache.c"
|
||||
#include "geo_cache/geo_cache.c"
|
||||
#include "font_cache/font_cache.c"
|
||||
#include "draw/draw.c"
|
||||
#include "ui/ui_inc.c"
|
||||
#include "df/df_inc.c"
|
||||
#include "raddbg.c"
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Low-Level Entry Points
|
||||
|
||||
//- rjf: windows
|
||||
#if OS_WINDOWS
|
||||
|
||||
global DWORD g_saved_exception_code = 0;
|
||||
|
||||
internal DWORD
|
||||
win32_exception_filter(DWORD dwExceptionCode)
|
||||
{
|
||||
g_saved_exception_code = dwExceptionCode;
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
|
||||
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
|
||||
{
|
||||
HANDLE output_handles[3] =
|
||||
{
|
||||
GetStdHandle(STD_INPUT_HANDLE),
|
||||
GetStdHandle(STD_OUTPUT_HANDLE),
|
||||
GetStdHandle(STD_ERROR_HANDLE),
|
||||
};
|
||||
for(U64 idx = 0; idx < ArrayCount(output_handles); idx += 1)
|
||||
{
|
||||
B32 duplicate = 0;
|
||||
for(U64 idx2 = 0; idx2 < idx; idx2 += 1)
|
||||
{
|
||||
if(output_handles[idx2] == output_handles[idx])
|
||||
{
|
||||
duplicate = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(duplicate)
|
||||
{
|
||||
output_handles[idx] = 0;
|
||||
}
|
||||
}
|
||||
for(U64 idx = 0; idx < ArrayCount(output_handles); idx += 1)
|
||||
{
|
||||
if(output_handles[idx] != 0)
|
||||
{
|
||||
CloseHandle(output_handles[idx]);
|
||||
}
|
||||
}
|
||||
SetStdHandle(STD_INPUT_HANDLE, 0);
|
||||
SetStdHandle(STD_OUTPUT_HANDLE, 0);
|
||||
SetStdHandle(STD_ERROR_HANDLE, 0);
|
||||
static TCTX main_thread_tctx = {0};
|
||||
tctx_init_and_equip(&main_thread_tctx);
|
||||
Arena *perm_arena = arena_alloc();
|
||||
WCHAR *command_line = GetCommandLineW();
|
||||
int argc;
|
||||
WCHAR **argv_16 = CommandLineToArgvW(command_line, &argc);
|
||||
char **argv = push_array(perm_arena, char *, argc);
|
||||
for(int i = 0; i < argc; i += 1)
|
||||
{
|
||||
String16 arg16 = str16_cstring((U16 *)argv_16[i]);
|
||||
String8 arg8 = str8_from_16(perm_arena, arg16);
|
||||
argv[i] = (char *)arg8.str;
|
||||
}
|
||||
__try
|
||||
{
|
||||
entry_point(argc, argv);
|
||||
}
|
||||
__except(win32_exception_filter(GetExceptionCode()))
|
||||
{
|
||||
char buffer[256] = {0};
|
||||
raddbg_snprintf(buffer, sizeof(buffer), "A fatal exception (code 0x%x) occurred. The process is terminating.", (U32)g_saved_exception_code);
|
||||
os_graphical_message(1, str8_lit("Fatal Exception"), str8_cstring(buffer));
|
||||
ExitProcess(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//- rjf: linux
|
||||
#elif OS_LINUX
|
||||
|
||||
int main(int argument_count, char **arguments)
|
||||
{
|
||||
static TCTX main_thread_tctx = {0};
|
||||
tctx_init_and_equip(&main_thread_tctx);
|
||||
entry_point(argument_count, arguments);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -35,6 +35,11 @@ cons_root_new(CONS_RootParams *params){
|
||||
result->scope_count += 1;
|
||||
}
|
||||
|
||||
// rjf: setup null UDT
|
||||
{
|
||||
cons__type_udt_from_any_type(result, result->nil_type);
|
||||
}
|
||||
|
||||
// initialize maps
|
||||
{
|
||||
#define BKTCOUNT(x) ((x)?(u64_up_to_pow2(x)):(128))
|
||||
|
||||
@@ -763,7 +763,13 @@ pdbconv_type_equip_members(PDBCONV_Ctx *ctx, CONS_Type *owner_type, CV_TypeId fi
|
||||
|
||||
// discard cases - we don't currently do anything with these
|
||||
case CV_LeafKind_VFUNCTAB:
|
||||
{}break;
|
||||
{
|
||||
// TODO(rjf): error if bad range
|
||||
if(list_item_off + sizeof(CV_LeafVFuncTab) <= cap)
|
||||
{
|
||||
list_item_opl_off = list_item_off + sizeof(CV_LeafVFuncTab);
|
||||
}
|
||||
}break;
|
||||
|
||||
// unhandled or invalid cases
|
||||
default:
|
||||
@@ -1310,6 +1316,7 @@ pdbconv_type_cons_leaf_record(PDBCONV_Ctx *ctx, CV_TypeId itype){
|
||||
case CV_LeafKind_STRUCTURE:
|
||||
{
|
||||
ProfBegin("CV_LeafKind_CLASS/CV_LeafKind_STRUCTURE");
|
||||
|
||||
// TODO(allen): error if bad range
|
||||
if (sizeof(CV_LeafStruct) <= cap){
|
||||
CV_LeafStruct *lf_struct = (CV_LeafStruct*)first;
|
||||
@@ -2935,7 +2942,7 @@ str8_list_pushf(arena, &out->errors, fmt, __VA_ARGS__);\
|
||||
if (tpi != 0) ProfScope("parse tpi hash"){
|
||||
String8 hash_data = msf_data_from_stream(msf, tpi->hash_sn);
|
||||
String8 aux_data = msf_data_from_stream(msf, tpi->hash_sn_aux);
|
||||
tpi_hash = pdb_tpi_hash_from_data(arena, tpi, hash_data, aux_data);
|
||||
tpi_hash = pdb_tpi_hash_from_data(arena, strtbl, tpi, hash_data, aux_data);
|
||||
|
||||
PARSE_CHECK_ERROR(tpi_hash, "TPI hash table");
|
||||
}
|
||||
@@ -2954,7 +2961,7 @@ str8_list_pushf(arena, &out->errors, fmt, __VA_ARGS__);\
|
||||
if (ipi != 0) ProfScope("parse ipi hash"){
|
||||
String8 hash_data = msf_data_from_stream(msf, ipi->hash_sn);
|
||||
String8 aux_data = msf_data_from_stream(msf, ipi->hash_sn_aux);
|
||||
ipi_hash = pdb_tpi_hash_from_data(arena, ipi, hash_data, aux_data);
|
||||
ipi_hash = pdb_tpi_hash_from_data(arena, strtbl, ipi, hash_data, aux_data);
|
||||
|
||||
PARSE_CHECK_ERROR(tpi_hash, "IPI hash table");
|
||||
}
|
||||
|
||||
@@ -303,7 +303,7 @@ pdb_tpi_from_data(Arena *arena, String8 data){
|
||||
}
|
||||
|
||||
static PDB_TpiHashParsed*
|
||||
pdb_tpi_hash_from_data(Arena *arena, PDB_TpiParsed *tpi, String8 data, String8 aux_data){
|
||||
pdb_tpi_hash_from_data(Arena *arena, PDB_Strtbl *strtbl, PDB_TpiParsed *tpi, String8 data, String8 aux_data){
|
||||
ProfBegin("pdb_tpi_hash_from_data");
|
||||
|
||||
PDB_TpiHashParsed *result = 0;
|
||||
@@ -335,7 +335,11 @@ pdb_tpi_hash_from_data(Arena *arena, PDB_TpiParsed *tpi, String8 data, String8 a
|
||||
block = push_array(arena, PDB_TpiHashBlock, 1);
|
||||
SLLStackPush(buckets[bucket_idx], block);
|
||||
}
|
||||
block->itypes[block->local_count] = itype;
|
||||
if(block->local_count != 0)
|
||||
{
|
||||
MemoryCopy(block->itypes+1, block->itypes, sizeof(CV_TypeId)*block->local_count);
|
||||
}
|
||||
block->itypes[0] = itype;
|
||||
block->local_count += 1;
|
||||
}
|
||||
|
||||
@@ -344,15 +348,85 @@ pdb_tpi_hash_from_data(Arena *arena, PDB_TpiParsed *tpi, String8 data, String8 a
|
||||
itype += 1;
|
||||
}
|
||||
|
||||
//- rjf: compute bucket mask
|
||||
U32 bucket_mask = 0;
|
||||
if(IsPow2OrZero(bucket_count))
|
||||
{
|
||||
bucket_mask = bucket_count-1;
|
||||
}
|
||||
|
||||
//- rjf: apply hash adjustments, to pull correct type IDs to the front of
|
||||
// the chains
|
||||
if(tpi->hash_adj_size != 0)
|
||||
{
|
||||
// NOTE(rjf): this table is laid out in the following format:
|
||||
//
|
||||
// pair_count: U32 -> # of name_index/type_index pairs
|
||||
// slot_count: U32 -> # of slots in this hash table
|
||||
// present_bit_array_count: U32 -> count for next array
|
||||
// present_bit_array: U32[present_bit_array_count] -> 1 bit per slot, "is present"
|
||||
// deleted_bit_array_count: U32 -> count for next array
|
||||
// deleted_bit_array: U32[deleted_bit_array_count] -> 1 bit per slot, "is deleted"
|
||||
// (U32, U32)[pair_count] -> array of name_index/type_index pairs
|
||||
//
|
||||
U8 *adjs = data.str + tpi->hash_adj_off;
|
||||
U8 *adjs_opl = adjs + tpi->hash_adj_size;
|
||||
U8 *adjs_cursor = adjs;
|
||||
U32 pair_count = *(U32 *)adjs_cursor;
|
||||
adjs_cursor += sizeof(U32);
|
||||
U32 slot_count = *(U32 *)adjs_cursor;
|
||||
adjs_cursor += sizeof(U32);
|
||||
U32 present_bit_array_count = *(U32 *)adjs_cursor; // skip present_bit_array
|
||||
adjs_cursor += sizeof(U32);
|
||||
adjs_cursor += present_bit_array_count*sizeof(U32);
|
||||
U32 deleted_bit_array_count = *(U32 *)adjs_cursor; // skip deleted_bit_array
|
||||
adjs_cursor += sizeof(U32);
|
||||
adjs_cursor += deleted_bit_array_count*sizeof(U32);
|
||||
U32 adjs_stride = sizeof(U32)*2;
|
||||
U32 pair_idx = 0;
|
||||
for(;adjs_cursor < adjs_opl && pair_idx < pair_count;
|
||||
adjs_cursor += adjs_stride, pair_idx += 1)
|
||||
{
|
||||
U32 name_off = ((U32 *)adjs_cursor)[0];
|
||||
CV_TypeId type_id = ((CV_TypeId *)adjs_cursor)[1];
|
||||
String8 string = pdb_strtbl_string_from_off(strtbl, name_off);
|
||||
U32 hash = pdb_string_hash1(string);
|
||||
U32 bucket_idx = ((bucket_mask != 0) ? hash&bucket_mask : hash%bucket_count);
|
||||
PDB_TpiHashBlock *prev_block = 0;
|
||||
for(PDB_TpiHashBlock *block = buckets[bucket_idx];
|
||||
block != 0;
|
||||
prev_block = block, block = block->next)
|
||||
{
|
||||
for(U32 local_idx = 0;
|
||||
local_idx < block->local_count && local_idx < ArrayCount(block->itypes);
|
||||
local_idx += 1)
|
||||
{
|
||||
if(block->itypes[local_idx] == type_id)
|
||||
{
|
||||
if(prev_block != 0)
|
||||
{
|
||||
prev_block->next = block->next;
|
||||
block->next = buckets[bucket_idx];
|
||||
buckets[bucket_idx] = block;
|
||||
}
|
||||
if(local_idx != 0)
|
||||
{
|
||||
Swap(CV_TypeId, block->itypes[0], block->itypes[local_idx]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fill result
|
||||
result = push_array(arena, PDB_TpiHashParsed, 1);
|
||||
result->data = data;
|
||||
result->aux_data = aux_data;
|
||||
result->buckets = buckets;
|
||||
result->bucket_count = bucket_count;
|
||||
if (IsPow2OrZero(bucket_count)){
|
||||
result->bucket_mask = bucket_count - 1;
|
||||
}
|
||||
result->bucket_mask = bucket_mask;
|
||||
}
|
||||
|
||||
ProfEnd();
|
||||
|
||||
@@ -407,6 +407,7 @@ static PDB_Strtbl* pdb_strtbl_from_data(Arena *arena, String8 strtbl_da
|
||||
static PDB_DbiParsed* pdb_dbi_from_data(Arena *arena, String8 dbi_data);
|
||||
static PDB_TpiParsed* pdb_tpi_from_data(Arena *arena, String8 tpi_data);
|
||||
static PDB_TpiHashParsed* pdb_tpi_hash_from_data(Arena *arena,
|
||||
PDB_Strtbl *strtbl,
|
||||
PDB_TpiParsed *tpi,
|
||||
String8 tpi_hash_data,
|
||||
String8 tpi_hash_aux_data);
|
||||
|
||||
@@ -131,7 +131,7 @@ r_d3d11_instance_buffer_from_size(U64 size)
|
||||
//- rjf: top-level layer initialization
|
||||
|
||||
r_hook void
|
||||
r_init(void)
|
||||
r_init(CmdLine *cmdln)
|
||||
{
|
||||
ProfBeginFunction();
|
||||
HRESULT error = 0;
|
||||
@@ -143,24 +143,57 @@ r_init(void)
|
||||
//- rjf: create base device
|
||||
UINT creation_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
|
||||
#if !defined(NDEBUG)
|
||||
creation_flags |= D3D11_CREATE_DEVICE_DEBUG;
|
||||
if(!cmd_line_has_flag(cmdln, str8_lit("disable_d3d11_debug")))
|
||||
{
|
||||
creation_flags |= D3D11_CREATE_DEVICE_DEBUG;
|
||||
}
|
||||
#endif
|
||||
D3D_FEATURE_LEVEL feature_levels[] = { D3D_FEATURE_LEVEL_11_0 };
|
||||
D3D_DRIVER_TYPE driver_type = D3D_DRIVER_TYPE_HARDWARE;
|
||||
if(cmd_line_has_flag(cmdln, str8_lit("force_d3d11_software")))
|
||||
{
|
||||
driver_type = D3D_DRIVER_TYPE_WARP;
|
||||
}
|
||||
error = D3D11CreateDevice(0,
|
||||
D3D_DRIVER_TYPE_HARDWARE,
|
||||
driver_type,
|
||||
0,
|
||||
creation_flags,
|
||||
feature_levels, ArrayCount(feature_levels),
|
||||
D3D11_SDK_VERSION,
|
||||
&r_d3d11_state->base_device, 0, &r_d3d11_state->base_device_ctx);
|
||||
if(FAILED(error) && driver_type == D3D_DRIVER_TYPE_HARDWARE)
|
||||
{
|
||||
// try with WARP driver as backup solution in case HW device is not available
|
||||
error = D3D11CreateDevice(0,
|
||||
D3D_DRIVER_TYPE_WARP,
|
||||
0,
|
||||
creation_flags,
|
||||
feature_levels, ArrayCount(feature_levels),
|
||||
D3D11_SDK_VERSION,
|
||||
&r_d3d11_state->base_device, 0, &r_d3d11_state->base_device_ctx);
|
||||
}
|
||||
|
||||
if(FAILED(error))
|
||||
{
|
||||
char buffer[256] = {0};
|
||||
raddbg_snprintf(buffer, sizeof(buffer), "D3D11 device creation failure (%x). The process is terminating.", error);
|
||||
os_graphical_message(1, str8_lit("Fatal Error"), str8_cstring(buffer));
|
||||
os_exit_process(1);
|
||||
}
|
||||
|
||||
//- rjf: enable break-on-error
|
||||
#if !defined(NDEBUG)
|
||||
ID3D11InfoQueue *info = 0;
|
||||
error = r_d3d11_state->base_device->QueryInterface(__uuidof(ID3D11InfoQueue), (void **)(&info));
|
||||
error = info->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, TRUE);
|
||||
error = info->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, TRUE);
|
||||
info->Release();
|
||||
if(!cmd_line_has_flag(cmdln, str8_lit("disable_d3d11_debug")))
|
||||
{
|
||||
ID3D11InfoQueue *info = 0;
|
||||
error = r_d3d11_state->base_device->QueryInterface(__uuidof(ID3D11InfoQueue), (void **)(&info));
|
||||
if(SUCCEEDED(error))
|
||||
{
|
||||
error = info->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, TRUE);
|
||||
error = info->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, TRUE);
|
||||
info->Release();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//- rjf: get main device
|
||||
@@ -286,7 +319,7 @@ r_init(void)
|
||||
&vshad_source_blob,
|
||||
&vshad_source_errors);
|
||||
String8 errors = {0};
|
||||
if(vshad_source_errors)
|
||||
if(FAILED(error))
|
||||
{
|
||||
errors = str8((U8 *)vshad_source_errors->GetBufferPointer(),
|
||||
(U64)vshad_source_errors->GetBufferSize());
|
||||
@@ -307,6 +340,8 @@ r_init(void)
|
||||
vshad_source_blob->GetBufferSize(),
|
||||
&ilay);
|
||||
}
|
||||
|
||||
vshad_source_blob->Release();
|
||||
|
||||
// rjf: store
|
||||
r_d3d11_state->vshads[kind] = vshad;
|
||||
@@ -338,7 +373,7 @@ r_init(void)
|
||||
&pshad_source_blob,
|
||||
&pshad_source_errors);
|
||||
String8 errors = {0};
|
||||
if(pshad_source_errors)
|
||||
if(FAILED(error))
|
||||
{
|
||||
errors = str8((U8 *)pshad_source_errors->GetBufferPointer(),
|
||||
(U64)pshad_source_errors->GetBufferSize());
|
||||
@@ -349,6 +384,8 @@ r_init(void)
|
||||
error = r_d3d11_state->device->CreatePixelShader(pshad_source_blob->GetBufferPointer(), pshad_source_blob->GetBufferSize(), 0, &pshad);
|
||||
}
|
||||
}
|
||||
|
||||
pshad_source_blob->Release();
|
||||
|
||||
// rjf: store
|
||||
r_d3d11_state->pshads[kind] = pshad;
|
||||
@@ -442,7 +479,14 @@ r_window_equip(OS_Handle handle)
|
||||
swapchain_desc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
|
||||
swapchain_desc.Flags = 0;
|
||||
}
|
||||
r_d3d11_state->dxgi_factory->CreateSwapChainForHwnd(r_d3d11_state->device, hwnd, &swapchain_desc, 0, 0, &window->swapchain);
|
||||
HRESULT error = r_d3d11_state->dxgi_factory->CreateSwapChainForHwnd(r_d3d11_state->device, hwnd, &swapchain_desc, 0, 0, &window->swapchain);
|
||||
if(FAILED(error))
|
||||
{
|
||||
char buffer[256] = {0};
|
||||
raddbg_snprintf(buffer, sizeof(buffer), "DXGI swap chain creation failure (%x). The process is terminating.", error);
|
||||
os_graphical_message(1, str8_lit("Fatal Error"), str8_cstring(buffer));
|
||||
os_exit_process(1);
|
||||
}
|
||||
|
||||
//- rjf: create framebuffer & view
|
||||
window->swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void **)(&window->framebuffer));
|
||||
@@ -531,9 +575,15 @@ r_tex2d_alloc(R_Tex2DKind kind, Vec2S32 size, R_Tex2DFormat format, void *data)
|
||||
switch(format)
|
||||
{
|
||||
default:{}break;
|
||||
case R_Tex2DFormat_R8:{dxgi_format = DXGI_FORMAT_R8_UNORM;}break;
|
||||
case R_Tex2DFormat_RGBA8:{}break;
|
||||
case R_Tex2DFormat_BGRA8:{dxgi_format = DXGI_FORMAT_B8G8R8A8_UNORM;}break;
|
||||
case R_Tex2DFormat_R8: {dxgi_format = DXGI_FORMAT_R8_UNORM;}break;
|
||||
case R_Tex2DFormat_RG8: {dxgi_format = DXGI_FORMAT_R8G8_UNORM;}break;
|
||||
case R_Tex2DFormat_RGBA8: {dxgi_format = DXGI_FORMAT_R8G8B8A8_UNORM;}break;
|
||||
case R_Tex2DFormat_BGRA8: {dxgi_format = DXGI_FORMAT_B8G8R8A8_UNORM;}break;
|
||||
case R_Tex2DFormat_R16: {dxgi_format = DXGI_FORMAT_R16_UNORM;}break;
|
||||
case R_Tex2DFormat_RGBA16:{dxgi_format = DXGI_FORMAT_R16G16B16A16_UNORM;}break;
|
||||
case R_Tex2DFormat_R32: {dxgi_format = DXGI_FORMAT_R32_FLOAT;}break;
|
||||
case R_Tex2DFormat_RG32: {dxgi_format = DXGI_FORMAT_R32G32_FLOAT;}break;
|
||||
case R_Tex2DFormat_RGBA32:{dxgi_format = DXGI_FORMAT_R32G32B32A32_FLOAT;}break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -944,7 +994,14 @@ r_window_end_frame(OS_Handle window, R_Handle window_equip)
|
||||
////////////////////////////
|
||||
//- rjf: present
|
||||
//
|
||||
wnd->swapchain->Present(1, 0);
|
||||
HRESULT error = wnd->swapchain->Present(1, 0);
|
||||
if(FAILED(error))
|
||||
{
|
||||
char buffer[256] = {0};
|
||||
raddbg_snprintf(buffer, sizeof(buffer), "D3D11 present failure (%x). The process is terminating.", error);
|
||||
os_graphical_message(1, str8_lit("Fatal Error"), str8_cstring(buffer));
|
||||
os_exit_process(1);
|
||||
}
|
||||
d_ctx->ClearState();
|
||||
}
|
||||
ProfEnd();
|
||||
|
||||
@@ -9,8 +9,14 @@
|
||||
typedef enum R_Tex2DFormat
|
||||
{
|
||||
R_Tex2DFormat_R8,
|
||||
R_Tex2DFormat_RG8,
|
||||
R_Tex2DFormat_RGBA8,
|
||||
R_Tex2DFormat_BGRA8,
|
||||
R_Tex2DFormat_R16,
|
||||
R_Tex2DFormat_RGBA16,
|
||||
R_Tex2DFormat_R32,
|
||||
R_Tex2DFormat_RG32,
|
||||
R_Tex2DFormat_RGBA32,
|
||||
R_Tex2DFormat_COUNT
|
||||
} R_Tex2DFormat;
|
||||
|
||||
@@ -55,15 +61,27 @@ R_PassKind_COUNT
|
||||
String8 r_tex2d_format_display_string_table[] =
|
||||
{
|
||||
str8_lit_comp("R8"),
|
||||
str8_lit_comp("RG8"),
|
||||
str8_lit_comp("RGBA8"),
|
||||
str8_lit_comp("BGRA8"),
|
||||
str8_lit_comp("R16"),
|
||||
str8_lit_comp("RGBA16"),
|
||||
str8_lit_comp("R32"),
|
||||
str8_lit_comp("RG32"),
|
||||
str8_lit_comp("RGBA32"),
|
||||
};
|
||||
|
||||
U8 r_tex2d_format_bytes_per_pixel_table[] =
|
||||
{
|
||||
1,
|
||||
2,
|
||||
4,
|
||||
4,
|
||||
2,
|
||||
8,
|
||||
4,
|
||||
8,
|
||||
16,
|
||||
};
|
||||
|
||||
String8 r_tex2d_kind_display_string_table[] =
|
||||
|
||||
@@ -269,7 +269,7 @@ internal R_Pass *r_pass_from_kind(Arena *arena, R_PassList *list, R_PassKind kin
|
||||
//~ rjf: Backend Hooks
|
||||
|
||||
//- rjf: top-level layer initialization
|
||||
r_hook void r_init(void);
|
||||
r_hook void r_init(CmdLine *cmdln);
|
||||
|
||||
//- rjf: window setup/teardown
|
||||
r_hook R_Handle r_window_equip(OS_Handle window);
|
||||
|
||||
@@ -8,8 +8,14 @@
|
||||
R_Tex2DFormatTable:
|
||||
{
|
||||
{R8 "R8" 1}
|
||||
{RG8 "RG8" 2}
|
||||
{RGBA8 "RGBA8" 4}
|
||||
{BGRA8 "BGRA8" 4}
|
||||
{R16 "R16" 2}
|
||||
{RGBA16 "RGBA16" 8}
|
||||
{R32 "R32" 4}
|
||||
{RG32 "RG32" 8}
|
||||
{RGBA32 "RGBA32" 16}
|
||||
}
|
||||
|
||||
@table(name, display_string)
|
||||
@@ -4,7 +4,9 @@
|
||||
#include "render_core.c"
|
||||
|
||||
#if LANG_CPP
|
||||
# if R_BACKEND == R_BACKEND_D3D11
|
||||
# if R_BACKEND == R_BACKEND_STUB
|
||||
# include "stub/render_stub.c"
|
||||
# elif R_BACKEND == R_BACKEND_D3D11
|
||||
# include "d3d11/render_d3d11.cpp"
|
||||
# else
|
||||
# error Renderer backend not specified.
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
////////////////////////////////
|
||||
//~ rjf: Backend Constants
|
||||
|
||||
#define R_BACKEND_STUB 0
|
||||
#define R_BACKEND_D3D11 1
|
||||
|
||||
////////////////////////////////
|
||||
@@ -25,7 +26,9 @@
|
||||
//~ rjf: Backend Includes
|
||||
|
||||
#if LANG_CPP
|
||||
# if R_BACKEND == R_BACKEND_D3D11
|
||||
# if R_BACKEND == R_BACKEND_STUB
|
||||
# include "stub/render_stub.h"
|
||||
# elif R_BACKEND == R_BACKEND_D3D11
|
||||
# include "d3d11/render_d3d11.h"
|
||||
# else
|
||||
# error Renderer backend not specified.
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
//- rjf: top-level layer initialization
|
||||
|
||||
r_hook void
|
||||
r_init(CmdLine *cmdln)
|
||||
{
|
||||
}
|
||||
|
||||
//- rjf: window setup/teardown
|
||||
|
||||
r_hook R_Handle
|
||||
r_window_equip(OS_Handle window)
|
||||
{
|
||||
R_Handle handle = {0};
|
||||
handle.u64[0] = 1;
|
||||
return handle;
|
||||
}
|
||||
|
||||
r_hook void
|
||||
r_window_unequip(OS_Handle window, R_Handle window_equip)
|
||||
{
|
||||
}
|
||||
|
||||
//- rjf: textures
|
||||
|
||||
r_hook R_Handle
|
||||
r_tex2d_alloc(R_Tex2DKind kind, Vec2S32 size, R_Tex2DFormat format, void *data)
|
||||
{
|
||||
R_Handle handle = {0};
|
||||
handle.u64[0] = 1;
|
||||
return handle;
|
||||
}
|
||||
|
||||
r_hook void
|
||||
r_tex2d_release(R_Handle texture)
|
||||
{
|
||||
}
|
||||
|
||||
r_hook R_Tex2DKind
|
||||
r_kind_from_tex2d(R_Handle texture)
|
||||
{
|
||||
return R_Tex2DKind_Static;
|
||||
}
|
||||
|
||||
r_hook Vec2S32
|
||||
r_size_from_tex2d(R_Handle texture)
|
||||
{
|
||||
return v2s32(1, 1);
|
||||
}
|
||||
|
||||
r_hook R_Tex2DFormat
|
||||
r_format_from_tex2d(R_Handle texture)
|
||||
{
|
||||
return R_Tex2DFormat_RGBA8;
|
||||
}
|
||||
|
||||
r_hook void
|
||||
r_fill_tex2d_region(R_Handle texture, Rng2S32 subrect, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
//- rjf: buffers
|
||||
|
||||
r_hook R_Handle
|
||||
r_buffer_alloc(R_BufferKind kind, U64 size, void *data)
|
||||
{
|
||||
R_Handle handle = {0};
|
||||
handle.u64[0] = 1;
|
||||
return handle;
|
||||
}
|
||||
|
||||
r_hook void
|
||||
r_buffer_release(R_Handle buffer)
|
||||
{
|
||||
}
|
||||
|
||||
//- rjf: frame markers
|
||||
|
||||
r_hook void
|
||||
r_begin_frame(void)
|
||||
{
|
||||
}
|
||||
|
||||
r_hook void
|
||||
r_end_frame(void)
|
||||
{
|
||||
}
|
||||
|
||||
r_hook void
|
||||
r_window_begin_frame(OS_Handle window, R_Handle window_equip)
|
||||
{
|
||||
}
|
||||
|
||||
r_hook void
|
||||
r_window_end_frame(OS_Handle window, R_Handle window_equip)
|
||||
{
|
||||
}
|
||||
|
||||
//- rjf: render pass submission
|
||||
|
||||
r_hook void
|
||||
r_window_submit(OS_Handle window, R_Handle window_equip, R_PassList *passes)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef RENDER_STUB_H
|
||||
#define RENDER_STUB_H
|
||||
|
||||
#endif // RENDER_STUB_H
|
||||
@@ -0,0 +1,7 @@
|
||||
#include <fstream>
|
||||
|
||||
int main()
|
||||
{
|
||||
std::fstream temp;
|
||||
return 0;
|
||||
}
|
||||
+37
-295
@@ -1,307 +1,49 @@
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#include "base/base_inc.h"
|
||||
#include "os/os_inc.h"
|
||||
#include "mdesk/mdesk.h"
|
||||
// build with:
|
||||
// cl /Zi /nologo look_at_raddbg.c
|
||||
|
||||
#include "base/base_inc.c"
|
||||
#include "os/os_inc.c"
|
||||
#include "mdesk/mdesk.c"
|
||||
#include <windows.h>
|
||||
#include <stdint.h>
|
||||
#include "raddbg_format/raddbg_format.h"
|
||||
#include "raddbg_format/raddbg_format_parse.h"
|
||||
#include "raddbg_format/raddbg_format.c"
|
||||
#include "raddbg_format/raddbg_format_parse.c"
|
||||
|
||||
typedef enum MG_StrExprOpKind
|
||||
typedef struct Foo Foo;
|
||||
struct Foo
|
||||
{
|
||||
MG_StrExprOpKind_Null,
|
||||
MG_StrExprOpKind_Prefix,
|
||||
MG_StrExprOpKind_Postfix,
|
||||
MG_StrExprOpKind_Binary,
|
||||
MG_StrExprOpKind_COUNT
|
||||
}
|
||||
MG_StrExprOpKind;
|
||||
|
||||
typedef enum MG_StrExprOp
|
||||
{
|
||||
MG_StrExprOp_Null,
|
||||
|
||||
#define MG_StrExprOp_FirstString MG_StrExprOp_Dot
|
||||
MG_StrExprOp_Dot,
|
||||
MG_StrExprOp_ExpandIfTrue,
|
||||
MG_StrExprOp_Concat,
|
||||
MG_StrExprOp_BumpToColumn,
|
||||
#define MG_StrExprOp_LastString MG_StrExprOp_BumpToColumn
|
||||
|
||||
#define MG_StrExprOp_FirstNumeric MG_StrExprOp_Add
|
||||
MG_StrExprOp_Add,
|
||||
MG_StrExprOp_Subtract,
|
||||
MG_StrExprOp_Multiply,
|
||||
MG_StrExprOp_Divide,
|
||||
MG_StrExprOp_Modulo,
|
||||
MG_StrExprOp_LeftShift,
|
||||
MG_StrExprOp_RightShift,
|
||||
MG_StrExprOp_BitwiseAnd,
|
||||
MG_StrExprOp_BitwiseOr,
|
||||
MG_StrExprOp_BitwiseXor,
|
||||
MG_StrExprOp_BitwiseNegate,
|
||||
MG_StrExprOp_BooleanAnd,
|
||||
MG_StrExprOp_BooleanOr,
|
||||
MG_StrExprOp_BooleanNot,
|
||||
MG_StrExprOp_Equals,
|
||||
MG_StrExprOp_DoesNotEqual,
|
||||
#define MG_StrExprOp_LastNumeric MG_StrExprOp_DoesNotEqual
|
||||
|
||||
MG_StrExprOp_COUNT,
|
||||
}
|
||||
MG_StrExprOp;
|
||||
|
||||
read_only global String8 mg_str_expr_op_symbol_string_table[MG_StrExprOp_COUNT] =
|
||||
{
|
||||
str8_lit_comp(""),
|
||||
str8_lit_comp("."), // MG_StrExprOp_Dot
|
||||
str8_lit_comp("->"), // MG_StrExprOp_ExpandIfTrue
|
||||
str8_lit_comp(".."), // MG_StrExprOp_Concat
|
||||
str8_lit_comp("=>"), // MG_StrExprOp_BumpToColumn
|
||||
str8_lit_comp("+"), // MG_StrExprOp_Add
|
||||
str8_lit_comp("-"), // MG_StrExprOp_Subtract
|
||||
str8_lit_comp("*"), // MG_StrExprOp_Multiply
|
||||
str8_lit_comp("/"), // MG_StrExprOp_Divide
|
||||
str8_lit_comp("%"), // MG_StrExprOp_Modulo
|
||||
str8_lit_comp("<<"), // MG_StrExprOp_LeftShift
|
||||
str8_lit_comp(">>"), // MG_StrExprOp_RightShift
|
||||
str8_lit_comp("&"), // MG_StrExprOp_BitwiseAnd
|
||||
str8_lit_comp("|"), // MG_StrExprOp_BitwiseOr
|
||||
str8_lit_comp("^"), // MG_StrExprOp_BitwiseXor
|
||||
str8_lit_comp("~"), // MG_StrExprOp_BitwiseNegate
|
||||
str8_lit_comp("&&"), // MG_StrExprOp_BooleanAnd
|
||||
str8_lit_comp("||"), // MG_StrExprOp_BooleanOr
|
||||
str8_lit_comp("!"), // MG_StrExprOp_BooleanNot
|
||||
str8_lit_comp("=="), // MG_StrExprOp_Equals
|
||||
str8_lit_comp("!="), // MG_StrExprOp_DoesNotEqual
|
||||
int x;
|
||||
int y;
|
||||
int z;
|
||||
};
|
||||
|
||||
read_only global S8 mg_str_expr_op_precedence_table[MG_StrExprOp_COUNT] =
|
||||
{
|
||||
0,
|
||||
20, // MG_StrExprOp_Dot
|
||||
1, // MG_StrExprOp_ExpandIfTrue
|
||||
2, // MG_StrExprOp_Concat
|
||||
12, // MG_StrExprOp_BumpToColumn
|
||||
5, // MG_StrExprOp_Add
|
||||
5, // MG_StrExprOp_Subtract
|
||||
6, // MG_StrExprOp_Multiply
|
||||
6, // MG_StrExprOp_Divide
|
||||
6, // MG_StrExprOp_Modulo
|
||||
7, // MG_StrExprOp_LeftShift
|
||||
7, // MG_StrExprOp_RightShift
|
||||
8, // MG_StrExprOp_BitwiseAnd
|
||||
10, // MG_StrExprOp_BitwiseOr
|
||||
9, // MG_StrExprOp_BitwiseXor
|
||||
11, // MG_StrExprOp_BitwiseNegate
|
||||
3, // MG_StrExprOp_BooleanAnd
|
||||
3, // MG_StrExprOp_BooleanOr
|
||||
11, // MG_StrExprOp_BooleanNot
|
||||
4, // MG_StrExprOp_Equals
|
||||
4, // MG_StrExprOp_DoesNotEqual
|
||||
};
|
||||
|
||||
read_only global MG_StrExprOpKind mg_str_expr_op_kind_table[MG_StrExprOp_COUNT] =
|
||||
{
|
||||
MG_StrExprOpKind_Null,
|
||||
MG_StrExprOpKind_Binary, // MG_StrExprOp_Dot
|
||||
MG_StrExprOpKind_Binary, // MG_StrExprOp_ExpandIfTrue
|
||||
MG_StrExprOpKind_Binary, // MG_StrExprOp_Concat
|
||||
MG_StrExprOpKind_Prefix, // MG_StrExprOp_BumpToColumn
|
||||
MG_StrExprOpKind_Binary, // MG_StrExprOp_Add
|
||||
MG_StrExprOpKind_Binary, // MG_StrExprOp_Subtract
|
||||
MG_StrExprOpKind_Binary, // MG_StrExprOp_Multiply
|
||||
MG_StrExprOpKind_Binary, // MG_StrExprOp_Divide
|
||||
MG_StrExprOpKind_Binary, // MG_StrExprOp_Modulo
|
||||
MG_StrExprOpKind_Binary, // MG_StrExprOp_LeftShift
|
||||
MG_StrExprOpKind_Binary, // MG_StrExprOp_RightShift
|
||||
MG_StrExprOpKind_Binary, // MG_StrExprOp_BitwiseAnd
|
||||
MG_StrExprOpKind_Binary, // MG_StrExprOp_BitwiseOr
|
||||
MG_StrExprOpKind_Binary, // MG_StrExprOp_BitwiseXor
|
||||
MG_StrExprOpKind_Prefix, // MG_StrExprOp_BitwiseNegate
|
||||
MG_StrExprOpKind_Binary, // MG_StrExprOp_BooleanAnd
|
||||
MG_StrExprOpKind_Binary, // MG_StrExprOp_BooleanOr
|
||||
MG_StrExprOpKind_Prefix, // MG_StrExprOp_BooleanNot
|
||||
MG_StrExprOpKind_Binary, // MG_StrExprOp_Equals
|
||||
MG_StrExprOpKind_Binary, // MG_StrExprOp_DoesNotEqual
|
||||
};
|
||||
|
||||
typedef struct MG_StrExpr MG_StrExpr;
|
||||
struct MG_StrExpr
|
||||
{
|
||||
MG_StrExpr *parent;
|
||||
MG_StrExpr *left;
|
||||
MG_StrExpr *right;
|
||||
MG_StrExprOp op;
|
||||
MD_Node *node;
|
||||
};
|
||||
|
||||
typedef struct MG_StrExprParseResult MG_StrExprParseResult;
|
||||
struct MG_StrExprParseResult
|
||||
{
|
||||
MG_StrExpr *root;
|
||||
MD_MsgList msgs;
|
||||
MD_Node *next_node;
|
||||
};
|
||||
|
||||
global MG_StrExpr mg_str_expr_nil = {&mg_str_expr_nil, &mg_str_expr_nil, &mg_str_expr_nil};
|
||||
|
||||
internal MG_StrExpr *
|
||||
mg_push_str_expr(Arena *arena, MG_StrExprOp op, MD_Node *node)
|
||||
{
|
||||
MG_StrExpr *expr = push_array(arena, MG_StrExpr, 1);
|
||||
MemoryCopyStruct(expr, &mg_str_expr_nil);
|
||||
expr->op = op;
|
||||
expr->node = node;
|
||||
return expr;
|
||||
}
|
||||
|
||||
internal MG_StrExprParseResult
|
||||
mg_str_expr_parse_from_first_opl__min_prec(Arena *arena, MD_Node *first, MD_Node *opl, S8 min_prec)
|
||||
{
|
||||
MG_StrExprParseResult parse = {&mg_str_expr_nil};
|
||||
{
|
||||
MD_Node *it = first;
|
||||
|
||||
//- rjf: consume prefix operators
|
||||
MG_StrExpr *leafmost_op = &mg_str_expr_nil;
|
||||
for(;it < opl && !md_node_is_nil(it);)
|
||||
{
|
||||
MG_StrExprOp found_op = MG_StrExprOp_Null;
|
||||
for(MG_StrExprOp op = (MG_StrExprOp)(MG_StrExprOp_Null+1);
|
||||
op < MG_StrExprOp_COUNT;
|
||||
op = (MG_StrExprOp)(op+1))
|
||||
{
|
||||
if(mg_str_expr_op_kind_table[op] == MG_StrExprOpKind_Prefix &&
|
||||
str8_match(it->string, mg_str_expr_op_symbol_string_table[op], 0) &&
|
||||
mg_str_expr_op_precedence_table[op] >= min_prec)
|
||||
{
|
||||
found_op = op;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(found_op != MG_StrExprOp_Null)
|
||||
{
|
||||
MG_StrExpr *op_expr = mg_push_str_expr(arena, found_op, it);
|
||||
if(leafmost_op == &mg_str_expr_nil)
|
||||
{
|
||||
leafmost_op = op_expr;
|
||||
}
|
||||
op_expr->left = parse.root;
|
||||
parse.root = op_expr;
|
||||
it = it->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: parse atom
|
||||
{
|
||||
MG_StrExpr *atom = &mg_str_expr_nil;
|
||||
if(it->flags & MD_NodeFlag_Identifier|MD_NodeFlag_Numeric|MD_NodeFlag_StringLiteral &&
|
||||
md_node_is_nil(it->first))
|
||||
{
|
||||
atom = mg_push_str_expr(arena, MG_StrExprOp_Null, it);
|
||||
it = it->next;
|
||||
}
|
||||
else if(!md_node_is_nil(it->first))
|
||||
{
|
||||
MG_StrExprParseResult subparse = mg_str_expr_parse_from_first_opl__min_prec(arena, it->first, &md_nil_node, 0);
|
||||
atom = subparse.root;
|
||||
md_msg_list_concat_in_place(&parse.msgs, &subparse.msgs);
|
||||
it = it->next;
|
||||
}
|
||||
if(leafmost_op != &mg_str_expr_nil)
|
||||
{
|
||||
leafmost_op->left = atom;
|
||||
}
|
||||
else
|
||||
{
|
||||
parse.root = atom;
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: parse binary operator extensions at this precedence level
|
||||
for(;it < opl && !md_node_is_nil(it);)
|
||||
{
|
||||
// rjf: find binary op kind of `it`
|
||||
MG_StrExprOp found_op = MG_StrExprOp_Null;
|
||||
for(MG_StrExprOp op = (MG_StrExprOp)(MG_StrExprOp_Null+1);
|
||||
op < MG_StrExprOp_COUNT;
|
||||
op = (MG_StrExprOp)(op+1))
|
||||
{
|
||||
if(mg_str_expr_op_kind_table[op] == MG_StrExprOpKind_Binary &&
|
||||
str8_match(it->string, mg_str_expr_op_symbol_string_table[op], 0) &&
|
||||
mg_str_expr_op_precedence_table[op] >= min_prec)
|
||||
{
|
||||
found_op = op;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: good found_op -> build binary expr
|
||||
if(found_op != MG_StrExprOp_Null)
|
||||
{
|
||||
MG_StrExpr *op_expr = mg_push_str_expr(arena, found_op, it);
|
||||
if(leafmost_op == &mg_str_expr_nil)
|
||||
{
|
||||
leafmost_op = op_expr;
|
||||
}
|
||||
op_expr->left = parse.root;
|
||||
parse.root = op_expr;
|
||||
it = it->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// rjf: parse right hand side of binary operator
|
||||
MG_StrExprParseResult subparse = mg_str_expr_parse_from_first_opl__min_prec(arena, it, opl, mg_str_expr_op_precedence_table[found_op]+1);
|
||||
parse.root->right = subparse.root;
|
||||
md_msg_list_concat_in_place(&parse.msgs, &subparse.msgs);
|
||||
if(subparse.root == &mg_str_expr_nil)
|
||||
{
|
||||
md_msg_list_pushf(arena, &parse.msgs, it, MD_MsgKind_Error, "Missing right-hand-side of '%S'.", mg_str_expr_op_symbol_string_table[found_op]);
|
||||
}
|
||||
it = subparse.next_node;
|
||||
}
|
||||
|
||||
// rjf: store next node for more caller-side parsing
|
||||
parse.next_node = it;
|
||||
}
|
||||
return parse;
|
||||
}
|
||||
|
||||
internal MG_StrExprParseResult
|
||||
mg_str_expr_parse_from_first_opl(Arena *arena, MD_Node *first, MD_Node *opl)
|
||||
{
|
||||
MG_StrExprParseResult parse = mg_str_expr_parse_from_first_opl__min_prec(arena, first, opl, 0);
|
||||
return parse;
|
||||
}
|
||||
|
||||
internal MG_StrExprParseResult
|
||||
mg_str_expr_parse_from_root(Arena *arena, MD_Node *root)
|
||||
{
|
||||
MG_StrExprParseResult parse = mg_str_expr_parse_from_first_opl__min_prec(arena, root->first, &md_nil_node, 0);
|
||||
return parse;
|
||||
}
|
||||
|
||||
int main(int argument_count, char **arguments)
|
||||
{
|
||||
static TCTX main_thread_tctx = {0};
|
||||
tctx_init_and_equip(&main_thread_tctx);
|
||||
os_init(argument_count, arguments);
|
||||
|
||||
Arena *arena = arena_alloc();
|
||||
String8 text = str8_lit("(a.vr == \"x\" -> \"DF_GFX_VIEW_RULE_VIZ_ROW_PROD_FUNCTION_DEF(\" .. a.name_lower .. \");\")");
|
||||
MD_TokenizeResult tokenize = md_tokenize_from_text(arena, text);
|
||||
MD_ParseResult base_parse = md_parse_from_text_tokens(arena, str8_lit(""), text, tokenize.tokens);
|
||||
MG_StrExprParseResult strexpr_parse = mg_str_expr_parse_from_root(arena, base_parse.root->first);
|
||||
|
||||
Foo foo = {123, 456, 789};
|
||||
HANDLE file = CreateFileA(arguments[1], GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
DWORD size_hi32 = 0;
|
||||
DWORD size_lo32 = GetFileSize(file, &size_hi32);
|
||||
HANDLE map = CreateFileMappingA(file, 0, PAGE_READONLY, 0, 0, 0);
|
||||
uint64_t data_size = (size_lo32 | ((uint64_t)size_hi32 << 32));
|
||||
uint8_t *data = (uint8_t *)MapViewOfFile(map, FILE_MAP_READ, 0, 0, data_size);
|
||||
RADDBG_Parsed rdbg = {0};
|
||||
RADDBG_ParseStatus parse_status = raddbg_parse(data, data_size, &rdbg);
|
||||
uint64_t foo_count = 0;
|
||||
for(uint64_t idx = 0; idx < rdbg.type_node_count; idx += 1)
|
||||
{
|
||||
RADDBG_TypeNode *type_node = &rdbg.type_nodes[idx];
|
||||
if(RADDBG_TypeKind_FirstUserDefined <= type_node->kind && type_node->kind <= RADDBG_TypeKind_LastUserDefined)
|
||||
{
|
||||
uint64_t name_size = 0;
|
||||
uint8_t *name = raddbg_string_from_idx(&rdbg, type_node->user_defined.name_string_idx, &name_size);
|
||||
if(name_size == 3 && name[0] == 'f' && name[1] == 'o' && name[2] == 'o')
|
||||
{
|
||||
foo_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("%s -> %I64u foos\n", arguments[1], foo_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
+9
-2
@@ -1123,7 +1123,7 @@ txti_mut_thread_entry_point(void *p)
|
||||
U8 *append_data_buffer = push_array_no_zero(buffer->data_arena, U8, msg->string.size);
|
||||
MemoryCopy(append_data_buffer, msg->string.str, msg->string.size);
|
||||
buffer->data.size += msg->string.size;
|
||||
if(buffer->data.str == 0)
|
||||
if(buffer->data.str == 0 && msg->string.size != 0)
|
||||
{
|
||||
buffer->data.str = append_data_buffer;
|
||||
}
|
||||
@@ -1133,7 +1133,14 @@ txti_mut_thread_entry_point(void *p)
|
||||
case TXTI_MsgKind_Reload: ProfScope("reload")
|
||||
{
|
||||
arena_clear(buffer->data_arena);
|
||||
buffer->data = push_str8_copy(buffer->data_arena, file_contents);
|
||||
if(file_contents.size != 0)
|
||||
{
|
||||
buffer->data = push_str8_copy(buffer->data_arena, file_contents);
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryZeroStruct(&buffer->data);
|
||||
}
|
||||
}break;
|
||||
}
|
||||
|
||||
|
||||
+94
-22
@@ -15,6 +15,41 @@ tg_hash_from_string(U64 seed, String8 string)
|
||||
return result;
|
||||
}
|
||||
|
||||
internal int
|
||||
tg_qsort_compare_members_offset(TG_Member *a, TG_Member *b)
|
||||
{
|
||||
int result = 0;
|
||||
if(a->off < b->off)
|
||||
{
|
||||
result = -1;
|
||||
}
|
||||
else if(a->off > b->off)
|
||||
{
|
||||
result = +1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal void
|
||||
tg_key_list_push(Arena *arena, TG_KeyList *list, TG_Key key)
|
||||
{
|
||||
TG_KeyNode *n = push_array(arena, TG_KeyNode, 1);
|
||||
n->v = key;
|
||||
SLLQueuePush(list->first, list->last, n);
|
||||
list->count += 1;
|
||||
}
|
||||
|
||||
internal TG_KeyList
|
||||
tg_key_list_copy(Arena *arena, TG_KeyList *src)
|
||||
{
|
||||
TG_KeyList dst = {0};
|
||||
for(TG_KeyNode *n = src->first; n != 0; n = n->next)
|
||||
{
|
||||
tg_key_list_push(arena, &dst, n->v);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: RADDBG <-> TG Enum Conversions
|
||||
|
||||
@@ -318,7 +353,7 @@ tg_type_from_graph_raddbg_key(Arena *arena, TG_Graph *graph, RADDBG_Parsed *rdbg
|
||||
RADDBG_UDT *udt = &rdbg->udts[udt_idx];
|
||||
members_count = udt->member_count;
|
||||
members = push_array(arena, TG_Member, members_count);
|
||||
if(0 <= udt->member_first && udt->member_first+udt->member_count < rdbg->member_count)
|
||||
if(members_count != 0 && 0 <= udt->member_first && udt->member_first+udt->member_count <= rdbg->member_count)
|
||||
{
|
||||
for(U32 member_idx = udt->member_first;
|
||||
member_idx < udt->member_first+udt->member_count;
|
||||
@@ -742,7 +777,7 @@ tg_type_from_graph_raddbg_key(Arena *arena, TG_Graph *graph, RADDBG_Parsed *rdbg
|
||||
|
||||
// rjf: commit members
|
||||
type->count = members.count;
|
||||
type->members = push_array_no_zero(arena, TG_Member, 1);
|
||||
type->members = push_array_no_zero(arena, TG_Member, members.count);
|
||||
U64 idx = 0;
|
||||
for(TG_MemberNode *n = members.first; n != 0; n = n->next, idx += 1)
|
||||
{
|
||||
@@ -912,36 +947,73 @@ tg_members_from_graph_raddbg_key(Arena *arena, TG_Graph *graph, RADDBG_Parsed *r
|
||||
internal TG_MemberArray
|
||||
tg_data_members_from_graph_raddbg_key(Arena *arena, TG_Graph *graph, RADDBG_Parsed *rdbg, TG_Key key)
|
||||
{
|
||||
TG_MemberArray result = {0};
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
TG_MemberList members_list = {0};
|
||||
B32 members_need_offset_sort = 0;
|
||||
{
|
||||
TG_Type *type = tg_type_from_graph_raddbg_key(scratch.arena, graph, rdbg, key);
|
||||
if(type->members != 0)
|
||||
TG_Type *root_type = tg_type_from_graph_raddbg_key(scratch.arena, graph, rdbg, key);
|
||||
typedef struct Task Task;
|
||||
struct Task
|
||||
{
|
||||
U64 data_member_count = 0;
|
||||
for(U64 member_idx = 0; member_idx < type->count; member_idx += 1)
|
||||
Task *next;
|
||||
U64 base_off;
|
||||
TG_KeyList inheritance_chain;
|
||||
TG_Key type_key;
|
||||
TG_Type *type;
|
||||
};
|
||||
Task start_task = {0, 0, {0}, key, root_type};
|
||||
Task *first_task = &start_task;
|
||||
Task *last_task = &start_task;
|
||||
for(Task *task = first_task; task != 0; task = task->next)
|
||||
{
|
||||
TG_Type *type = task->type;
|
||||
if(type->members != 0)
|
||||
{
|
||||
if(type->members[member_idx].kind == TG_MemberKind_DataField)
|
||||
for(U64 member_idx = 0; member_idx < type->count; member_idx += 1)
|
||||
{
|
||||
data_member_count += 1;
|
||||
}
|
||||
}
|
||||
result.count = data_member_count;
|
||||
result.v = push_array_no_zero(arena, TG_Member, result.count);
|
||||
U64 idx = 0;
|
||||
for(U64 member_idx = 0; member_idx < type->count; member_idx += 1)
|
||||
{
|
||||
if(type->members[member_idx].kind == TG_MemberKind_DataField)
|
||||
{
|
||||
MemoryCopyStruct(&result.v[idx], &type->members[member_idx]);
|
||||
result.v[idx].name = push_str8_copy(arena, result.v[idx].name);
|
||||
idx += 1;
|
||||
if(type->members[member_idx].kind == TG_MemberKind_DataField)
|
||||
{
|
||||
TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1);
|
||||
MemoryCopyStruct(&n->v, &type->members[member_idx]);
|
||||
n->v.off += task->base_off;
|
||||
n->v.inheritance_key_chain = task->inheritance_chain;
|
||||
SLLQueuePush(members_list.first, members_list.last, n);
|
||||
members_list.count += 1;
|
||||
}
|
||||
else if(type->members[member_idx].kind == TG_MemberKind_Base)
|
||||
{
|
||||
Task *t = push_array(scratch.arena, Task, 1);
|
||||
t->base_off = type->members[member_idx].off + task->base_off;
|
||||
t->inheritance_chain = tg_key_list_copy(scratch.arena, &task->inheritance_chain);
|
||||
tg_key_list_push(scratch.arena, &t->inheritance_chain, type->members[member_idx].type_key);
|
||||
t->type_key = type->members[member_idx].type_key;
|
||||
t->type = tg_type_from_graph_raddbg_key(scratch.arena, graph, rdbg, type->members[member_idx].type_key);
|
||||
SLLQueuePush(first_task, last_task, t);
|
||||
members_need_offset_sort = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TG_MemberArray members = {0};
|
||||
{
|
||||
members.count = members_list.count;
|
||||
members.v = push_array(arena, TG_Member, members.count);
|
||||
U64 idx = 0;
|
||||
for(TG_MemberNode *n = members_list.first; n != 0; n = n->next)
|
||||
{
|
||||
MemoryCopyStruct(&members.v[idx], &n->v);
|
||||
members.v[idx].name = push_str8_copy(arena, members.v[idx].name);
|
||||
members.v[idx].inheritance_key_chain = tg_key_list_copy(arena, &members.v[idx].inheritance_key_chain);
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
if(members_need_offset_sort)
|
||||
{
|
||||
qsort(members.v, members.count, sizeof(TG_Member), (int (*)(const void *, const void *))tg_qsort_compare_members_offset);
|
||||
}
|
||||
scratch_end(scratch);
|
||||
return result;
|
||||
return members;
|
||||
}
|
||||
|
||||
internal void
|
||||
|
||||
@@ -31,6 +31,21 @@ struct TG_Key
|
||||
U64 u64[1]; // ext -> unique id; cons -> idx; reg -> code
|
||||
};
|
||||
|
||||
typedef struct TG_KeyNode TG_KeyNode;
|
||||
struct TG_KeyNode
|
||||
{
|
||||
TG_KeyNode *next;
|
||||
TG_Key v;
|
||||
};
|
||||
|
||||
typedef struct TG_KeyList TG_KeyList;
|
||||
struct TG_KeyList
|
||||
{
|
||||
TG_KeyNode *first;
|
||||
TG_KeyNode *last;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Graph Types
|
||||
|
||||
@@ -102,6 +117,7 @@ struct TG_Member
|
||||
TG_Key type_key;
|
||||
String8 name;
|
||||
U64 off;
|
||||
TG_KeyList inheritance_key_chain;
|
||||
};
|
||||
|
||||
typedef struct TG_MemberNode TG_MemberNode;
|
||||
@@ -179,6 +195,9 @@ thread_static Arena *tg_build_arena = 0;
|
||||
//~ rjf: Basic Helpers
|
||||
|
||||
internal U64 tg_hash_from_string(U64 seed, String8 string);
|
||||
internal int tg_qsort_compare_members_offset(TG_Member *a, TG_Member *b);
|
||||
internal void tg_key_list_push(Arena *arena, TG_KeyList *list, TG_Key key);
|
||||
internal TG_KeyList tg_key_list_copy(Arena *arena, TG_KeyList *src);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: RADDBG <-> TG Enum Conversions
|
||||
|
||||
+22
-15
@@ -115,11 +115,10 @@ ui_nav_eat_action_node(UI_NavActionList *list, UI_NavActionNode *node)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: High Level Navigation Action => Text Operations
|
||||
|
||||
internal B32
|
||||
ui_nav_char_is_scan_boundary(U8 c)
|
||||
ui_nav_char_is_code_symbol(U8 c)
|
||||
{
|
||||
return char_is_space(c) || char_is_slash(c);
|
||||
return (char_is_alpha(c) || char_is_digit(c, 10) || c == '_');
|
||||
}
|
||||
|
||||
internal S64
|
||||
@@ -127,20 +126,26 @@ ui_nav_scanned_column_from_column(String8 string, S64 start_column, Side side)
|
||||
{
|
||||
S64 new_column = start_column;
|
||||
S64 delta = (!!side)*2 - 1;
|
||||
B32 found_boundary = 0;
|
||||
for(S64 col = start_column+delta; 1 <= col && col <= string.size+1; col += delta)
|
||||
B32 found_text = 0;
|
||||
B32 found_non_space = 0;
|
||||
S64 start_off = delta < 0 ? delta : 0;
|
||||
for(S64 col = start_column+start_off; 1 <= col && col <= string.size+1; col += delta)
|
||||
{
|
||||
U8 byte = (col <= string.size) ? string.str[col-1] : 0;
|
||||
B32 is_boundary = (ui_nav_char_is_scan_boundary(byte) ^ !(side == Side_Max));
|
||||
if((found_boundary && !is_boundary) ||
|
||||
(col == 1 || col == string.size+1))
|
||||
B32 is_non_space = !char_is_space(byte);
|
||||
B32 is_name = ui_nav_char_is_code_symbol(byte);
|
||||
|
||||
if (((side == Side_Min) && (col == 1)) ||
|
||||
((side == Side_Max) && (col == string.size+1)) ||
|
||||
(found_non_space && !is_non_space) ||
|
||||
(found_text && !is_name))
|
||||
{
|
||||
new_column = col + (!side && col != 1);
|
||||
new_column = col + (!side && col != 1);
|
||||
break;
|
||||
}
|
||||
else if(!found_boundary && is_boundary)
|
||||
{
|
||||
found_boundary = 1;
|
||||
} else if (!found_text && is_name) {
|
||||
found_text = 1;
|
||||
} else if (!found_non_space && is_non_space ) {
|
||||
found_non_space = 1;
|
||||
}
|
||||
}
|
||||
return new_column;
|
||||
@@ -2568,8 +2573,10 @@ ui_signal_from_box(UI_Box *box)
|
||||
}
|
||||
|
||||
//- rjf: set hovering status
|
||||
result.hovering = mouse_is_over && (ui_key_match(ui_state->active_box_key[Side_Min], ui_key_zero()) ||
|
||||
ui_key_match(ui_state->active_box_key[Side_Min], box->key));
|
||||
result.hovering = mouse_is_over && ((ui_key_match(ui_state->hot_box_key, ui_key_zero()) ||
|
||||
ui_key_match(ui_state->hot_box_key, box->key)) &&
|
||||
(ui_key_match(ui_state->active_box_key[Side_Min], ui_key_zero()) ||
|
||||
ui_key_match(ui_state->active_box_key[Side_Min], box->key)));
|
||||
result.mouse_over = mouse_is_over;
|
||||
|
||||
//- rjf: clicking in default nav -> set navigation state to this box
|
||||
|
||||
Reference in New Issue
Block a user