Compare commits

..

No commits in common. "2bd77e8121cdd03f7c039b487fd6803a947eea69" and "22cad985003c9bd269b291910564551e4caadcab" have entirely different histories.

7 changed files with 5 additions and 120 deletions

View File

@ -58,11 +58,6 @@ see [Screen capture](#screen-capture).
move the file to the Recycle Bin or to delete it permanently, overwriting it with random bytes move the file to the Recycle Bin or to delete it permanently, overwriting it with random bytes
first; "Don't ask again" keeps the choice until the app restarts. Only the `.mcap` goes: the first; "Don't ask again" keeps the choice until the app restarts. Only the `.mcap` goes: the
`.snagx` it was converted from is untouched. `.snagx` it was converted from is untouched.
- **Copy:** click a thumbnail to select it (it is outlined, and shown in the Capture window), then
**Ctrl-C** copies its image, as the Capture window shows it (annotations and all; a video's
frame), and **Ctrl-Shift-C** its full path. Neither acts while you type in the search box. They
are `copy_image` and `copy_path` in `[hotkeys]` in `settings.config`. Copying images is Windows
only so far.
- **Files added or deleted outside the app** (in Explorer, by another program) appear in the grid - **Files added or deleted outside the app** (in Explorer, by another program) appear in the grid
or leave it on their own, folders added, renamed or deleted too: the library is watched. or leave it on their own, folders added, renamed or deleted too: the library is watched.
**Refresh** looks at the whole folder again, should the watcher have missed something. **Refresh** looks at the whole folder again, should the watcher have missed something.
@ -98,18 +93,7 @@ grid (in its order, so through a search's matches).
### The OCR window ### The OCR window
- **Model:** PP-OCRv5 mobile (light, about 85 ms a capture on the GPU) or server (more accurate, - **Model:** PP-OCRv5 mobile (light, about 85 ms a capture on the GPU) or server (more accurate,
about 0.3 s when it runs well; see below). The choice is kept in the settings file (`model` under about 0.4 s). The choice is kept in the settings file (`model` under `[ocr]`).
`[ocr]`).
- **Known issue, the server model on the GPU:** its speed is bimodal. The same program on the same
20 captures (RTX 5080 laptop, 16 GB) runs either at about 0.27 s a capture with a peak of about
2.4 GB of process memory, or at about 1.5 s with about 6.6 GB, with no change between runs and
15 GB of the GPU's memory free beforehand. Longer runs fare worse: 50 captures peaked at 16 GB, and
100 averaged about 4.6 s a capture. The extra memory looks like GPU allocations spilling into shared
system memory, which Windows then pages: what the GPU memory defaults (onnxruntime's
kNextPowerOfTwo arena, no cuDNN max workspace, 4 workers) were meant to stop, and which they
only partly did. It is the same with Jai's allocator and with rpmalloc. Until it is fixed, scan a
large library with the mobile model. Measured on 2026-10-13 with
`ocr-bench -models ppocr-v5-server -gpu-only -limit 20`.
- **Running on:** the GPU or the CPU, and if the CPU, what to install for the GPU. - **Running on:** the GPU or the CPU, and if the CPU, what to install for the GPU.
- **Scan the library** reads every capture the model has not read yet and, with **Labels too**, - **Scan the library** reads every capture the model has not read yet and, with **Labels too**,
labels every capture not yet labelled with the current list. While a search is active the button labels every capture not yet labelled with the current list. While a search is active the button

View File

@ -18,10 +18,6 @@ Ctrl-Equal increase_font_size
Ctrl-Alt-Space reset_window Ctrl-Alt-Space reset_window
Ctrl-Shift-Alt-R reset_layout Ctrl-Shift-Alt-R reset_layout
# Library: the selected capture
Ctrl-C copy_image
Ctrl-Shift-C copy_path
# Debug # Debug
Ctrl-Shift-D toggle_debug_panel Ctrl-Shift-D toggle_debug_panel
Ctrl-Shift-L toggle_logger_panel Ctrl-Shift-L toggle_logger_panel

View File

@ -18,10 +18,6 @@ Cmd-Equal increase_font_size
Cmd-Opt-Shift-Space reset_window Cmd-Opt-Shift-Space reset_window
Cmd-Shift-Opt-R reset_layout Cmd-Shift-Opt-R reset_layout
# Library: the selected capture
Cmd-C copy_image
Cmd-Shift-C copy_path
# Debug # Debug
Cmd-Shift-D toggle_debug_panel Cmd-Shift-D toggle_debug_panel
Cmd-Shift-L toggle_logger_panel Cmd-Shift-L toggle_logger_panel

View File

@ -47,7 +47,7 @@ APP_NAME :: "mcap";
APP_FOLDER_NAME :: "mcap"; APP_FOLDER_NAME :: "mcap";
GIT_BRANCH :: "check"; GIT_BRANCH :: "check";
GIT_HASH :: "-"; GIT_HASH :: "-";
VERSION :: "0.1.16"; VERSION :: "0.1.15";
BUILD_DATE :: "-"; BUILD_DATE :: "-";
DEBUG :: true; DEBUG :: true;
COMPILE_TESTS :: true; COMPILE_TESTS :: true;

View File

@ -8,7 +8,7 @@ APP_NAME :: "mcap";
APP_FOLDER_NAME :: "mcap"; APP_FOLDER_NAME :: "mcap";
APP_BUNDLE_IDENTIFIER :: "com.example.mcap"; APP_BUNDLE_IDENTIFIER :: "com.example.mcap";
VERSION :: "0.1.16"; VERSION :: "0.1.15";
// Where everything goes: bin/, or bin-headless/ for builds made with no_crash_dialog, so automated // Where everything goes: bin/, or bin-headless/ for builds made with no_crash_dialog, so automated
// builds never write over the ones you run by hand. // builds never write over the ones you run by hand.
build_directory := "bin"; build_directory := "bin";

View File

@ -156,14 +156,6 @@ viewer: struct {
open_name: string; open_name: string;
open_result: string; // the file written, or ""; heap, the open thread's until it ends open_result: string; // the file written, or ""; heap, the open thread's until it ends
open_error: string; open_error: string;
// The selected capture's image, decoded off the main thread for the clipboard (viewer_copy_image).
copy_thread: Thread;
copy_started: bool;
copy_busy: bool; // set by the main thread, cleared by the copy thread when it ends
copy_source: string; // the .mcap; heap, main thread's until the thread starts
copy_name: string;
copy_done: Decoded; // the copy thread's until it ends
} }
// ---------------------------------------------------------------- startup and shutdown // ---------------------------------------------------------------- startup and shutdown
@ -237,7 +229,6 @@ viewer_shutdown :: () {
for 1..viewer.workers_started signal(*viewer.wake); for 1..viewer.workers_started signal(*viewer.wake);
for 0..viewer.workers_started - 1 thread_is_done(*viewer.workers[it], -1); for 0..viewer.workers_started - 1 thread_is_done(*viewer.workers[it], -1);
if viewer.open_started thread_is_done(*viewer.open_thread, -1); if viewer.open_started thread_is_done(*viewer.open_thread, -1);
if viewer.copy_started thread_is_done(*viewer.copy_thread, -1);
background_shutdown(); background_shutdown();
catalog_cache_save_at_shutdown(); // the workers have stopped: nothing changes the entries now catalog_cache_save_at_shutdown(); // the workers have stopped: nothing changes the entries now
viewer_watch_stop(); viewer_watch_stop();
@ -460,7 +451,6 @@ viewer_frame_gpu_work :: () {
viewer_apply_catalog_updates(); viewer_apply_catalog_updates();
catalog_cache_frame(); // saves the catalog cache once it is complete and has changed (app/catalog.jai) catalog_cache_frame(); // saves the catalog cache once it is complete and has changed (app/catalog.jai)
viewer_open_frame(); viewer_open_frame();
viewer_copy_frame();
segment_overlay_frame(); // the Capture window's segments: what arrived, and its texture (app/segment_ui.jai) segment_overlay_frame(); // the Capture window's segments: what arrived, and its texture (app/segment_ui.jai)
background_frame(); // takes back search results, and starts a search when visible_dirty (app/background.jai) background_frame(); // takes back search results, and starts a search when visible_dirty (app/background.jai)
} }
@ -1225,77 +1215,6 @@ viewer_open_frame :: () {
else toast_show(.Error, "Could not write % out: %", viewer.open_name, viewer.open_error); else toast_show(.Error, "Could not write % out: %", viewer.open_name, viewer.open_error);
} }
// ---------------------------------------------------------------- copying the selected capture
// The selected capture: the one outlined in the grid and shown in the Capture window. -1 when none.
viewer_selected :: () -> s32 {
index := viewer.full_index;
if index < 0 || index >= viewer.captures.count || viewer.captures[index].removed return -1;
return index;
}
// Ctrl-Shift-C (copy_path in [hotkeys]): the selected capture's path, as the system writes paths.
viewer_copy_path :: () {
index := viewer_selected();
if index < 0 { toast_show(.Error, "Select a capture to copy its path"); return; }
capture := *viewer.captures[index];
path := capture.path;
#if OS == .WINDOWS path = replace(path, "/", "\\",, temp);
clipboard_set_text(path);
toast_show(.Success, "Copied the path of %", capture.name);
}
// Ctrl-C (copy_image in [hotkeys]): the selected capture's image, as the Capture window shows it
// (annotations and all; a video's frame), decoded on a thread of its own and put on the clipboard
// by viewer_copy_frame.
viewer_copy_image :: () {
index := viewer_selected();
if index < 0 { toast_show(.Error, "Select a capture to copy its image"); return; }
capture := *viewer.captures[index];
#if OS != .WINDOWS {
toast_show(.Error, "Copying images is not available on this system yet");
return;
}
if viewer.copy_started { toast_show(.Error, "Still copying %", viewer.copy_name); return; }
push_allocator(context.default_allocator);
free(viewer.copy_source); free(viewer.copy_name);
viewer.copy_source = copy_string(capture.path);
viewer.copy_name = copy_string(capture.name);
viewer.copy_done = .{};
viewer.copy_busy = true;
// Reading an image takes a few MB of temporary storage, as on the viewer's workers.
viewer.copy_started = thread_init(*viewer.copy_thread, viewer_copy_proc, temporary_storage_size = 1024 * 1024, name = "copy image");
if !viewer.copy_started { viewer.copy_busy = false; toast_show(.Error, "Could not copy %", capture.name); return; }
thread_start(*viewer.copy_thread);
}
viewer_copy_proc :: (thread: *Thread) -> s64 {
context.allocator = context.default_allocator;
defer atomic_swap(*viewer.copy_busy, false);
viewer.copy_done = viewer_decode_full(viewer.copy_source);
return 0;
}
// Each frame: puts what viewer_copy_proc decoded on the clipboard, once it is done.
viewer_copy_frame :: () {
if !viewer.copy_started || viewer.copy_busy return;
thread_is_done(*viewer.copy_thread, -1);
thread_deinit(*viewer.copy_thread);
viewer.copy_started = false;
done := *viewer.copy_done;
defer viewer_free_pixels(done);
if done.failed { toast_show(.Error, "Could not read the image of %; see the Event Log", viewer.copy_name); return; }
pixels := []u8.{cast(s64) done.width * done.height * 4, done.pixels};
if !platform_copy_image_to_clipboard(pixels, done.width, done.height) {
log_error("[viewer] Could not put the image of % on the clipboard.", viewer.copy_source);
toast_show(.Error, "Could not put % on the clipboard", viewer.copy_name);
return;
}
scaled := ifx done.width != done.source_width || done.height != done.source_height then tprint(", scaled down from % x %", done.source_width, done.source_height) else "";
log("[viewer] Copied the image of % (% x %%).", viewer.copy_source, done.width, done.height, scaled);
toast_show(.Success, "Copied % (% x %%)", viewer.copy_name, done.width, done.height, scaled);
}
viewer_grid :: () { using ImGui; viewer_grid :: () { using ImGui;
style := GetStyle(); style := GetStyle();
cell := viewer.thumb_size; cell := viewer.thumb_size;

View File

@ -148,9 +148,8 @@ to_string_all_bindings :: (hotkey: Hotkey) -> string {
// The bindings are read from settings.config, or from DEFAULT_HOTKEYS_DATA when the file has // The bindings are read from settings.config, or from DEFAULT_HOTKEYS_DATA when the file has
// no [hotkeys] section; see hotkeys_install. once and ignore_when_text_field_active belong to the // no [hotkeys] section; see hotkeys_install. once and ignore_when_text_field_active belong to the
// action and are set here. To add an action: declare its Hotkey here, add it to // action and are set here. To add an action: declare its Hotkey here, add it to
// hotkey_actions_build and app_check_key_combinations, and bind it in both // hotkey_actions_build and app_check_key_combinations, bind it in both assets/data/default_hotkeys
// assets/data/default_hotkeys files. Settings files at APP_USER_SETTINGS_VERSION get its default // files, and raise APP_USER_SETTINGS_VERSION so existing settings files get its default.
// binding at the next start (hotkeys_missing_lines); raising the version would give them "none".
program_exit_hotkey: Hotkey; program_exit_hotkey: Hotkey;
minimize_to_tray_hotkey: Hotkey; minimize_to_tray_hotkey: Hotkey;
toggle_debug_panel_hotkey: Hotkey; toggle_debug_panel_hotkey: Hotkey;
@ -163,10 +162,6 @@ increase_font_size_hotkey: Hotkey;
reset_window_hotkey: Hotkey; reset_window_hotkey: Hotkey;
reset_layout_hotkey: Hotkey; reset_layout_hotkey: Hotkey;
// The selected capture (app/viewer.jai). Not while typing, so Ctrl-C in the search box copies its text.
copy_image_hotkey: Hotkey = .{ignore_when_text_field_active = true};
copy_path_hotkey: Hotkey = .{ignore_when_text_field_active = true};
Hotkey_Action :: struct { Hotkey_Action :: struct {
name: string; // as written in [hotkeys] name: string; // as written in [hotkeys]
hotkey: *Hotkey; hotkey: *Hotkey;
@ -187,8 +182,6 @@ hotkey_actions_build :: (out: *[..] Hotkey_Action) {
add(out, "increase_font_size", *increase_font_size_hotkey); add(out, "increase_font_size", *increase_font_size_hotkey);
add(out, "reset_window", *reset_window_hotkey); add(out, "reset_window", *reset_window_hotkey);
add(out, "reset_layout", *reset_layout_hotkey); add(out, "reset_layout", *reset_layout_hotkey);
add(out, "copy_image", *copy_image_hotkey);
add(out, "copy_path", *copy_path_hotkey);
} }
hotkey_actions: [..] Hotkey_Action; hotkey_actions: [..] Hotkey_Action;
@ -634,9 +627,6 @@ app_check_key_combinations :: () {
else toast_show(.Error, "Could not reset the window"); else toast_show(.Error, "Could not reset the window");
} }
if check_key_combination(*reset_layout_hotkey) imgui_reset_layout_requested = true; // applied between frames if check_key_combination(*reset_layout_hotkey) imgui_reset_layout_requested = true; // applied between frames
// The viewer is the shutdown thread's once shutdown has started.
if check_key_combination(*copy_image_hotkey) && !app_exit_shutdown_started() viewer_copy_image();
if check_key_combination(*copy_path_hotkey) && !app_exit_shutdown_started() viewer_copy_path();
if should_exit_this_frame { if should_exit_this_frame {
log("Calling exit hotkey... exiting program!"); log("Calling exit hotkey... exiting program!");