From 2d9929452c6f7c9a9fc96641ea3342a64fd45abf Mon Sep 17 00:00:00 2001 From: Ryan Fleury Date: Fri, 6 Jun 2025 12:09:07 -0700 Subject: [PATCH] memory view go-to-address / cursor/mark tracking fixes --- src/raddbg/generated/raddbg.meta.c | 2 +- src/raddbg/raddbg.mdesk | 2 + src/raddbg/raddbg_views.c | 386 +++++++++++++++-------------- 3 files changed, 201 insertions(+), 189 deletions(-) diff --git a/src/raddbg/generated/raddbg.meta.c b/src/raddbg/generated/raddbg.meta.c index 3c41da85..66bb9d1c 100644 --- a/src/raddbg/generated/raddbg.meta.c +++ b/src/raddbg/generated/raddbg.meta.c @@ -419,7 +419,7 @@ RD_NameSchemaInfo rd_name_schema_info_table[24] = {str8_lit_comp("watch"), str8_lit_comp("@inherit(tab) x:\n{\n @override @display_name('Tab Row Height') @description(\"Controls the tab's row height, in multiples of the font size.\")\n 'row_height': @range[1.75f, 5.f] f32,\n 'label': code_string,\n @description(\"The root expression which is evaluated to produce the watch window.\")\n 'expression': expr_string,\n @no_expand 'watches': query,\n}\n")}, {str8_lit_comp("text"), str8_lit_comp("@inherit(tab) x:\n{\n @description(\"An expression to describe data which should be viewed as text or code.\")\n 'expression': expr_string,\n @description(\"The language that the text should be interpreted as being within. Used for syntax highlighting and other parsing features.\")\n 'lang': code_string,\n @default(1) @description(\"Controls whether or not line numbers are shown.\")\n 'show_line_numbers':bool,\n @no_callee_helper @default(0) @display_name('Scroll To Bottom On Change') @description(\"Scrolls to the bottom if the text is changed.\")\n 'scroll_to_bottom_on_change':bool,\n @no_callee_helper @no_revert @default(0) @display_name('Transient') @description(\"Controls whether or not this tab will be automatically replaced by the debugger when it snaps to new source code locations.\")\n 'auto': bool,\n}\n")}, {str8_lit_comp("disasm"), str8_lit_comp("@inherit(tab) x:\n{\n @description(\"An expression to describe the base address or offset of the disassembly.\")\n 'expression': expr_string,\n 'arch': code_string,\n 'syntax': code_string,\n 'size': expr_string,\n @no_callee_helper @default(1) @description(\"Controls whether or not addresses are shown in the disassembly text.\")\n 'show_addresses': bool,\n @no_callee_helper @default(0) @description(\"Controls whether or not code bytes are shown in the disassembly text.\")\n 'show_code_bytes': bool,\n @no_callee_helper @default(1) @description(\"Controls whether or not source lines, corresponding to disassembly instruction ranges, are shown in the disassembly text.\")\n 'show_source_lines': bool,\n @no_callee_helper @default(1) @description(\"Controls whether or not disassembly text is decorated with symbol names.\")\n 'show_symbol_names': bool,\n @no_callee_helper @default(1) @description(\"Controls whether or not line numbers are shown.\")\n 'show_line_numbers': bool,\n}\n")}, -{str8_lit_comp("memory"), str8_lit_comp("@inherit(tab) x:\n{\n @description(\"An expression which refers to the base address of data which should be viewed as memory.\")\n 'expression': expr_string,\n @display_name(\"Address Range Size\") @description(\"The number of bytes of the viewed memory range.\")\n 'size': expr_string,\n @display_name(\"Cursor Address\") @description(\"The address of the cursor.\")\n 'cursor': expr_string,\n @display_name(\"Cursor Size\") @description(\"The size, in bytes, of the cursor.\")\n 'cursor_size': @range[1, 16] u64,\n @default(16) @description(\"The number of columns to build before building new rows.\")\n 'num_columns': @range[1, 64] u64,\n}\n")}, +{str8_lit_comp("memory"), str8_lit_comp("@inherit(tab) x:\n{\n @description(\"An expression which refers to the base address of data which should be viewed as memory.\")\n 'expression': expr_string,\n @display_name(\"Address Range Size\") @description(\"The number of bytes of the viewed memory range.\")\n 'size': expr_string,\n @display_name(\"Cursor Address\") @description(\"The address of the cursor.\")\n 'cursor': expr_string,\n @display_name(\"Cursor Size\") @description(\"The size, in bytes, of the cursor.\")\n 'cursor_size': @range[1, 16] u64,\n @default(16) @description(\"The number of columns to build before building new rows.\")\n 'num_columns': @range[1, 64] u64,\n @default(1) @display_name(\"Track Mark To Cursor\") @description(\"Ensures that the mark always follows the cursor, if the cursor value is updated.\")\n 'track_mark_to_cursor': bool,\n}\n")}, {str8_lit_comp("bitmap"), str8_lit_comp("@inherit(tab) x:\n{\n @description(\"An expression which refers to the base address of data which should be viewed as a bitmap.\")\n 'expression': expr_string,\n @description(\"An expression describing the width of the bitmap, in pixels.\") @order(0) 'w': u64,\n @description(\"An expression describing the height of the bitmap, in pixels.\") @order(1) 'h': u64,\n @display_name(\"Bitmap Format\") @description(\"The pixel format that the bitmap data should be interpreted as being within.\")\n 'fmt': code_string,\n}\n")}, {str8_lit_comp("color"), str8_lit_comp("@inherit(tab) x:\n{\n @display_name(\"Value\") @description(\"An expression to describe the value or location of the color.\")\n 'expression': expr_string,\n}\n")}, {str8_lit_comp("geo3d"), str8_lit_comp("@inherit(tab) x:\n{\n @display_name(\"Expression\") @description(\"An expression to describe the base address of the index buffer.\")\n 'expression': expr_string,\n 'count': expr_string,\n 'vtx': expr_string,\n 'vtx_size': expr_string,\n 'yaw': @range[0, 1] f32,\n 'pitch': @range[-0.5, 0] f32,\n 'zoom': @range[0, 100] f32,\n}\n")}, diff --git a/src/raddbg/raddbg.mdesk b/src/raddbg/raddbg.mdesk index bc1f7a80..a9592f64 100644 --- a/src/raddbg/raddbg.mdesk +++ b/src/raddbg/raddbg.mdesk @@ -517,6 +517,8 @@ RD_VocabTable: 'cursor_size': @range[1, 16] u64, @default(16) @description("The number of columns to build before building new rows.") 'num_columns': @range[1, 64] u64, + @default(1) @display_name("Track Mark To Cursor") @description("Ensures that the mark always follows the cursor, if the cursor value is updated.") + 'track_mark_to_cursor': bool, } ``` } diff --git a/src/raddbg/raddbg_views.c b/src/raddbg/raddbg_views.c index bf6472e6..5d763edc 100644 --- a/src/raddbg/raddbg_views.c +++ b/src/raddbg/raddbg_views.c @@ -2536,6 +2536,7 @@ RD_VIEW_UI_FUNCTION_DEF(memory) U64 initial_cursor_base_vaddr = cursor_base_vaddr; U64 initial_mark_base_vaddr = mark_base_vaddr; U64 num_columns = rd_view_setting_u64_from_name(str8_lit("num_columns")); + B32 track_mark_to_cursor = rd_view_setting_b32_from_name(str8_lit("track_mark_to_cursor")); if(num_columns == 0) { num_columns = 16; @@ -2580,29 +2581,6 @@ RD_VIEW_UI_FUNCTION_DEF(memory) F32 row_height_px = floor_f32(font_size*2.f); F32 cell_width_px = floor_f32(font_size*2.f); - ////////////////////////////// - //- rjf: determine legal scroll range - // - U64 view_range_last = view_range.max; - if(view_range_last != 0) - { - view_range_last -= 1; - } - Rng1S64 scroll_idx_rng = r1s64(0, (view_range_last - view_range.min) / num_columns); - - ////////////////////////////// - //- rjf: determine visible range of rows (including occluded) - // - Rng1S64 viz_range_rows = {0}; - S64 num_possible_visible_rows = 0; - { - num_possible_visible_rows = dim_2f32(rect).y/row_height_px; - viz_range_rows.min = scroll_pos.y.idx + (S64)scroll_pos.y.off - !!(scroll_pos.y.off<0); - viz_range_rows.max = scroll_pos.y.idx + (S64)scroll_pos.y.off + num_possible_visible_rows, - viz_range_rows.min = clamp_1s64(scroll_idx_rng, viz_range_rows.min); - viz_range_rows.max = clamp_1s64(scroll_idx_rng, viz_range_rows.max); - } - ////////////////////////////// //- rjf: calculate rectangles // @@ -2614,189 +2592,220 @@ RD_VIEW_UI_FUNCTION_DEF(memory) Rng2F32 content_rect = r2f32p(0, row_height_px, panel_dim.x-scroll_bar_dim, panel_dim.y); ////////////////////////////// - //- rjf: determine visible range of rows (only non-occluded) + //- rjf: determine visible range of rows (occluded & non-occluded) // - Rng1S64 viz_range_nonoccluded_rows = {0}; - S64 num_possible_nonoccluded_visible_rows = 0; + S64 num_possible_visible_rows = num_possible_visible_rows = dim_2f32(rect).y/row_height_px;; + S64 num_possible_nonoccluded_visible_rows = (dim_2f32(content_rect).y - dim_2f32(footer_rect).y) / row_height_px; + + ////////////////////////////// + //- rjf: determine legal scroll range + // + U64 view_range_last = view_range.max; + if(view_range_last != 0) { - num_possible_nonoccluded_visible_rows = (dim_2f32(content_rect).y - dim_2f32(footer_rect).y) / row_height_px; - viz_range_nonoccluded_rows.min = viz_range_rows.min + (S64)(content_rect.y0 / row_height_px); - viz_range_nonoccluded_rows.max = viz_range_nonoccluded_rows.min + num_possible_nonoccluded_visible_rows; - viz_range_nonoccluded_rows.min = clamp_1s64(scroll_idx_rng, viz_range_nonoccluded_rows.min); - viz_range_nonoccluded_rows.max = clamp_1s64(scroll_idx_rng, viz_range_nonoccluded_rows.max); + view_range_last -= 1; + } + Rng1S64 scroll_idx_rng = r1s64(0, (view_range_last - view_range.min) / num_columns); + + ////////////////////////////// + //- rjf: on cursor rugpull -> update mark if needed + // + { + Rng1U64 cursor_range = r1u64(cursor_base_vaddr, cursor_base_vaddr+cursor_size); + if(mv->last_cursor_range.min != cursor_range.min || + mv->last_cursor_range.max != cursor_range.max) + { + mv->contain_cursor = 1; + if(track_mark_to_cursor) + { + mark_base_vaddr = cursor_base_vaddr; + } + } } ////////////////////////////// + //- rjf: loop: compute boundaries, take events, repeat + // + B32 need_update = 1; + Rng1U64 cursor_valid_rng = {0}; + for(;;) + { + //- rjf: break if no further updates needed + if(!need_update) + { + break; + } + need_update = 0; + + //- rjf: take keyboard controls + UI_Focus(UI_FocusKind_On) if(ui_is_focus_active()) + { + U64 next_cursor_base_vaddr = cursor_base_vaddr; + U64 next_mark_base_vaddr = mark_base_vaddr; + for(UI_Event *evt = 0; ui_next_event(&evt);) + { + Vec2S64 cell_delta = {0}; + switch(evt->delta_unit) + { + default:{}break; + case UI_EventDeltaUnit_Char: + { + cell_delta.x = (S64)evt->delta_2s32.x; + cell_delta.y = (S64)evt->delta_2s32.y; + }break; + case UI_EventDeltaUnit_Word: + case UI_EventDeltaUnit_Page: + { + if(evt->delta_2s32.x < 0) + { + cell_delta.x = -(S64)(cursor_base_vaddr%num_columns); + } + else if(evt->delta_2s32.x > 0) + { + cell_delta.x = (num_columns-1) - (S64)(cursor_base_vaddr%num_columns); + } + if(evt->delta_2s32.y < 0) + { + cell_delta.y = -4; + } + else if(evt->delta_2s32.y > 0) + { + cell_delta.y = +4; + } + }break; + } + B32 good_action = 0; + if(evt->delta_2s32.x != 0 || evt->delta_2s32.y != 0) + { + good_action = 1; + } + if(good_action && evt->flags & UI_EventFlag_ZeroDeltaOnSelect && cursor_base_vaddr != mark_base_vaddr) + { + MemoryZeroStruct(&cell_delta); + } + if(good_action) + { + cell_delta.x = ClampBot(cell_delta.x, (S64)-next_cursor_base_vaddr); + cell_delta.y = ClampBot(cell_delta.y, (S64)-(next_cursor_base_vaddr/num_columns)); + next_cursor_base_vaddr += cell_delta.x; + next_cursor_base_vaddr += cell_delta.y*num_columns; + } + if(good_action && evt->flags & UI_EventFlag_PickSelectSide && cursor_base_vaddr != mark_base_vaddr) + { + if(evt->delta_2s32.x < 0 || evt->delta_2s32.y < 0) + { + next_cursor_base_vaddr = Min(cursor_base_vaddr, mark_base_vaddr); + } + else + { + next_cursor_base_vaddr = Max(cursor_base_vaddr, mark_base_vaddr); + } + } + if(good_action && !(evt->flags & UI_EventFlag_KeepMark)) + { + next_mark_base_vaddr = next_cursor_base_vaddr; + } + if(good_action) + { + need_update = 1; + mv->contain_cursor = 1; + ui_eat_event(evt); + } + } + cursor_base_vaddr = next_cursor_base_vaddr; + mark_base_vaddr = next_mark_base_vaddr; + } + + //- rjf: clamp cursor + cursor_valid_rng = view_range; + if(cursor_valid_rng.max != 0) + { + cursor_valid_rng.max -= 1; + } + if(cursor_base_vaddr != initial_cursor_base_vaddr) + { + cursor_base_vaddr = clamp_1u64(cursor_valid_rng, cursor_base_vaddr); + } + if(mark_base_vaddr != initial_mark_base_vaddr) + { + mark_base_vaddr = clamp_1u64(cursor_valid_rng, mark_base_vaddr); + } + + //- rjf: center cursor if range has changed + if(mv->last_view_range.max != view_range.max || + mv->last_view_range.min != view_range.min) + { + mv->center_cursor = 1; + mv->last_view_range = view_range; + } + + //- rjf: center cursor + if(mv->center_cursor) + { + mv->center_cursor = 0; + S64 cursor_row_idx = (cursor_base_vaddr - view_range.min) / num_columns; + S64 new_idx = (cursor_row_idx-num_possible_nonoccluded_visible_rows/2+1); + new_idx = clamp_1s64(scroll_idx_rng, new_idx); + ui_scroll_pt_target_idx(&scroll_pos.y, new_idx); + } + + //- rjf: contain cursor + if(mv->contain_cursor) + { + mv->contain_cursor = 0; + Rng1S64 viz_range_nonoccluded_rows = {0}; + viz_range_nonoccluded_rows.min = scroll_pos.y.idx + (S64)(content_rect.y0 / row_height_px); + viz_range_nonoccluded_rows.max = viz_range_nonoccluded_rows.min + num_possible_nonoccluded_visible_rows; + viz_range_nonoccluded_rows.min = clamp_1s64(scroll_idx_rng, viz_range_nonoccluded_rows.min); + viz_range_nonoccluded_rows.max = clamp_1s64(scroll_idx_rng, viz_range_nonoccluded_rows.max); + S64 cursor_row_idx = (cursor_base_vaddr - view_range.min) / num_columns; + Rng1S64 cursor_viz_range = r1s64(clamp_1s64(scroll_idx_rng, cursor_row_idx-2), clamp_1s64(scroll_idx_rng, cursor_row_idx+3)); + S64 min_delta = Min(0, cursor_viz_range.min-viz_range_nonoccluded_rows.min); + S64 max_delta = Max(0, cursor_viz_range.max-viz_range_nonoccluded_rows.max); + S64 new_idx = scroll_pos.y.idx+min_delta+max_delta; + new_idx = clamp_1s64(scroll_idx_rng, new_idx); + ui_scroll_pt_target_idx(&scroll_pos.y, new_idx); + } + } + + //////////////////////////// + //- rjf: determine selection + // + Rng1U64 selection = union_1u64(r1u64(cursor_base_vaddr, cursor_base_vaddr+cursor_size-1), + r1u64(mark_base_vaddr, mark_base_vaddr+cursor_size-1)); + + //////////////////////////// + //- rjf: determine visible range of rows (including occluded) + // + Rng1S64 viz_range_rows = {0}; + { + viz_range_rows.min = scroll_pos.y.idx + (S64)scroll_pos.y.off - !!(scroll_pos.y.off<0); + viz_range_rows.max = scroll_pos.y.idx + (S64)scroll_pos.y.off + num_possible_visible_rows, + viz_range_rows.min = clamp_1s64(scroll_idx_rng, viz_range_rows.min); + viz_range_rows.max = clamp_1s64(scroll_idx_rng, viz_range_rows.max); + } + + //////////////////////////// //- rjf: bump backwards if we are past the first + // if(viz_range_rows.min > 0) { viz_range_rows.min -= 1; content_rect.y0 -= row_height_px; } - ////////////////////////////// + //////////////////////////// //- rjf: determine visible range of bytes // Rng1U64 viz_range_bytes = {0}; - viz_range_bytes.min = view_range.min + (viz_range_rows.min)*num_columns; - viz_range_bytes.max = view_range.min + (viz_range_rows.max+1)*num_columns+1; - if(viz_range_bytes.min > viz_range_bytes.max) { - Swap(U64, viz_range_bytes.min, viz_range_bytes.max); - } - viz_range_bytes = intersect_1u64(view_range, viz_range_bytes); - - ////////////////////////////// - //- rjf: take keyboard controls - // - UI_Focus(UI_FocusKind_On) if(ui_is_focus_active()) - { - U64 next_cursor_base_vaddr = cursor_base_vaddr; - U64 next_mark_base_vaddr = mark_base_vaddr; - for(UI_Event *evt = 0; ui_next_event(&evt);) + viz_range_bytes.min = view_range.min + (viz_range_rows.min)*num_columns; + viz_range_bytes.max = view_range.min + (viz_range_rows.max+1)*num_columns+1; + if(viz_range_bytes.min > viz_range_bytes.max) { - Vec2S64 cell_delta = {0}; - switch(evt->delta_unit) - { - default:{}break; - case UI_EventDeltaUnit_Char: - { - cell_delta.x = (S64)evt->delta_2s32.x; - cell_delta.y = (S64)evt->delta_2s32.y; - }break; - case UI_EventDeltaUnit_Word: - case UI_EventDeltaUnit_Page: - { - if(evt->delta_2s32.x < 0) - { - cell_delta.x = -(S64)(cursor_base_vaddr%num_columns); - } - else if(evt->delta_2s32.x > 0) - { - cell_delta.x = (num_columns-1) - (S64)(cursor_base_vaddr%num_columns); - } - if(evt->delta_2s32.y < 0) - { - cell_delta.y = -4; - } - else if(evt->delta_2s32.y > 0) - { - cell_delta.y = +4; - } - }break; - } - B32 good_action = 0; - if(evt->delta_2s32.x != 0 || evt->delta_2s32.y != 0) - { - good_action = 1; - } - if(good_action && evt->flags & UI_EventFlag_ZeroDeltaOnSelect && cursor_base_vaddr != mark_base_vaddr) - { - MemoryZeroStruct(&cell_delta); - } - if(good_action) - { - cell_delta.x = ClampBot(cell_delta.x, (S64)-next_cursor_base_vaddr); - cell_delta.y = ClampBot(cell_delta.y, (S64)-(next_cursor_base_vaddr/num_columns)); - next_cursor_base_vaddr += cell_delta.x; - next_cursor_base_vaddr += cell_delta.y*num_columns; - } - if(good_action && evt->flags & UI_EventFlag_PickSelectSide && cursor_base_vaddr != mark_base_vaddr) - { - if(evt->delta_2s32.x < 0 || evt->delta_2s32.y < 0) - { - next_cursor_base_vaddr = Min(cursor_base_vaddr, mark_base_vaddr); - } - else - { - next_cursor_base_vaddr = Max(cursor_base_vaddr, mark_base_vaddr); - } - } - if(good_action && !(evt->flags & UI_EventFlag_KeepMark)) - { - next_mark_base_vaddr = next_cursor_base_vaddr; - } - if(good_action) - { - mv->contain_cursor = 1; - ui_eat_event(evt); - } + Swap(U64, viz_range_bytes.min, viz_range_bytes.max); } - cursor_base_vaddr = next_cursor_base_vaddr; - mark_base_vaddr = next_mark_base_vaddr; - } - - ////////////////////////////// - //- rjf: clamp cursor - // - Rng1U64 cursor_valid_rng = view_range; - if(cursor_valid_rng.max != 0) - { - cursor_valid_rng.max -= 1; - } - if(cursor_base_vaddr != initial_cursor_base_vaddr) - { - cursor_base_vaddr = clamp_1u64(cursor_valid_rng, cursor_base_vaddr); - } - if(mark_base_vaddr != initial_mark_base_vaddr) - { - mark_base_vaddr = clamp_1u64(cursor_valid_rng, mark_base_vaddr); - } - - ////////////////////////////// - //- rjf: unpack post-move cursor/mark info - // - Rng1U64 cursor_range = r1u64(cursor_base_vaddr, cursor_base_vaddr + cursor_size); - Rng1U64 mark_range = r1u64(mark_base_vaddr, mark_base_vaddr + cursor_size); - Rng1U64 selection = union_1u64(r1u64(cursor_base_vaddr, cursor_base_vaddr+cursor_size-1), - r1u64(mark_base_vaddr, mark_base_vaddr+cursor_size-1)); - - ////////////////////////////// - //- rjf: center cursor if range has changed - // - if(mv->last_view_range.max != view_range.max || - mv->last_view_range.min != view_range.min) - { - mv->center_cursor = 1; - mv->last_view_range = view_range; - } - - ////////////////////////////// - //- rjf: match mark to cursor if cursor has changed - // - if(mv->last_cursor_range.min != cursor_range.min || - mv->last_cursor_range.max != cursor_range.max) - { - mv->contain_cursor = 1; - mv->last_cursor_range = cursor_range; - mark_range = cursor_range; - } - - ////////////////////////////// - //- rjf: center cursor - // - if(mv->center_cursor) - { - mv->center_cursor = 0; - S64 cursor_row_idx = (cursor_base_vaddr - view_range.min) / num_columns; - S64 new_idx = (cursor_row_idx-num_possible_nonoccluded_visible_rows/2+1); - new_idx = clamp_1s64(scroll_idx_rng, new_idx); - ui_scroll_pt_target_idx(&scroll_pos.y, new_idx); - } - - ////////////////////////////// - //- rjf: contain cursor - // - if(mv->contain_cursor) - { - mv->contain_cursor = 0; - S64 cursor_row_idx = (cursor_base_vaddr - view_range.min) / num_columns; - Rng1S64 cursor_viz_range = r1s64(clamp_1s64(scroll_idx_rng, cursor_row_idx-2), clamp_1s64(scroll_idx_rng, cursor_row_idx+3)); - S64 min_delta = Min(0, cursor_viz_range.min-viz_range_nonoccluded_rows.min); - S64 max_delta = Max(0, cursor_viz_range.max-viz_range_nonoccluded_rows.max); - S64 new_idx = scroll_pos.y.idx+min_delta+max_delta; - new_idx = clamp_1s64(scroll_idx_rng, new_idx); - ui_scroll_pt_target_idx(&scroll_pos.y, new_idx); + viz_range_bytes = intersect_1u64(view_range, viz_range_bytes); } ////////////////////////////// @@ -3414,6 +3423,7 @@ RD_VIEW_UI_FUNCTION_DEF(memory) ////////////////////////////// //- rjf: save parameters // + mv->last_cursor_range = r1u64(cursor_base_vaddr, cursor_base_vaddr + cursor_size); if(cursor_base_vaddr != initial_cursor_base_vaddr) { rd_store_view_param_u64(str8_lit("cursor"), cursor_base_vaddr);