386 lines
14 KiB
Plaintext
386 lines
14 KiB
Plaintext
USING_SDL :: OS == .LINUX || OS == .MACOS;
|
|
USING_WINDOWS :: OS == .WINDOWS;
|
|
|
|
#if !USING_SDL && !USING_WINDOWS
|
|
#assert false "Invalid configuration";
|
|
|
|
// The arena and fixed_arena suites live in modules/Basic/tests and arrive with LOAD_TESTS
|
|
// on the Basic import below.
|
|
#if COMPILE_TESTS {
|
|
#load "test/testing.jai";
|
|
#load "test/logger_tests.jai";
|
|
#load "test/catalog_tests.jai";
|
|
#load "test/ocr_tests.jai";
|
|
#load "test/labels_tests.jai";
|
|
#load "test/import_tests.jai";
|
|
}
|
|
|
|
#load "app/main.jai"; // entry point
|
|
#load "app/app_ui.jai";
|
|
#load "app/viewer.jai";
|
|
#load "app/ocr_ui.jai";
|
|
#load "app/catalog.jai";
|
|
#load "app/stats_ui.jai";
|
|
#load "app/background.jai";
|
|
#load "app/labels_ui.jai";
|
|
#load "app/import.jai";
|
|
#load "app/open_file.jai";
|
|
#load "app/segment_ui.jai";
|
|
#load "app/gpu_bench_ui.jai"; // the gpu-bench branch: the GPU Benchmark window
|
|
#load "../tools/gpu_bench/results.jai"; // its results files
|
|
#load "app/library_folders.jai";
|
|
#load "app/exit_shutdown.jai";
|
|
#load "app/screen_capture.jai";
|
|
#load "app/screen_record.jai";
|
|
#if OS == .WINDOWS #load "app/screen_capture_win32.jai";
|
|
#if OS == .MACOS #load "app/screen_capture_macos.jai";
|
|
#if OS == .WINDOWS #load "app/screen_record_win32.jai";
|
|
#if OS == .MACOS #load "app/screen_record_macos.jai";
|
|
|
|
#load "base/circle_buffer.jai";
|
|
#load "base/debug.jai";
|
|
#load "base/fonts.jai";
|
|
#load "base/log_queue.jai";
|
|
#load "base/logger.jai";
|
|
#load "base/prune_files.jai";
|
|
|
|
#load "ui/imgui.jai";
|
|
#load "ui/imgui_keyboard.jai";
|
|
#load "ui/imgui_theme.jai";
|
|
#load "ui/imgui_theme_file.jai";
|
|
#load "ui/imgui_theme_registry.jai";
|
|
#load "ui/imgui_theme_editor.jai";
|
|
#load "ui/imgui_toast.jai";
|
|
#load "ui/imgui_exit_overlay.jai";
|
|
|
|
#import "Atomics";
|
|
#import "Basic"()(MEMORY_DEBUGGER=false,ARENA_DEBUG=DEBUG,LOAD_TESTS=COMPILE_TESTS);
|
|
#import "Clipboard";
|
|
#import "String";
|
|
#import "Sort";
|
|
#import "Unicode";
|
|
#import "File";
|
|
#import "Random"; // the noise viewer_overwrite_and_delete writes
|
|
#import "Float16"; // label vectors are stored as 16-bit floats
|
|
#import "File_Utilities";
|
|
#import "Math";
|
|
#import "System";
|
|
#import "Thread";
|
|
#import "Hash_Table";
|
|
#import "Hash"; // fnv1a_hash, for the label list's hash
|
|
Default_Allocator :: #import "Default_Allocator"; // rpmalloc (modules/Default_Allocator), for thread_idle at the workers' waits
|
|
Shared_Channel :: #import "Shared_Memory_Channel";
|
|
#import "stb_image";
|
|
#import "stb_image_resize";
|
|
#import "Capture_File"; // the .mcap format, copied from D:\McapIndex\SnagitConverter
|
|
#import "Image_Decode"; // any image file to pixels: stb_image, and WIC on Windows
|
|
#import "Snagx"; // .snagx captures to .mcap, as SnagitConverter makes them
|
|
#import,dir "../modules/jaison/unicode_utils"; // is_valid_utf8
|
|
#import "OCR";
|
|
#import "Labels";
|
|
#import "Segment";
|
|
#import "Capture_Text";
|
|
#import "Base64";
|
|
#import "onnxruntime";
|
|
#if OS == .WINDOWS || OS == .MACOS #import "Screen_Record"; // screen recording: Desktop Duplication or Windows.Graphics.Capture, to NVENC
|
|
|
|
// Basic's arenas import Secure_Memory at its defaults; this import adds its test suite.
|
|
using Secure_Memory :: #import "Secure_Memory"()(LOAD_TESTS=COMPILE_TESTS);
|
|
|
|
Graphics_Backend :: enum s32 {
|
|
Unknown :: 0;
|
|
DX11;
|
|
Vulkan;
|
|
OpenGL;
|
|
Metal;
|
|
DX12;
|
|
}
|
|
|
|
graphics_backend_from_string :: (name: string) -> Graphics_Backend {
|
|
if name == {
|
|
case "vulkan"; return .Vulkan;
|
|
case "opengl"; return .OpenGL;
|
|
case "metal"; return .Metal;
|
|
case "direct3d11"; return .DX11;
|
|
case "direct3d12"; return .DX12;
|
|
}
|
|
assert(false, "Unknown Graphics_Backend!");
|
|
return .Unknown;
|
|
}
|
|
|
|
Memory_Usage :: struct {
|
|
working_set: s64;
|
|
virtual_memory: s64;
|
|
peak_working_set: s64;
|
|
peak_virtual_memory: s64;
|
|
}
|
|
|
|
perf: Perf;
|
|
Perf :: struct {
|
|
backend: Graphics_Backend;
|
|
renderer_name: string;
|
|
adapter_name: string; // e.g. "NVIDIA GeForce RTX 4070"; empty when the adapter could not be queried
|
|
renderer_is_software: bool;
|
|
time_to_first_frame_ms: float64;
|
|
|
|
// See #WarpFirstStartup in graphics/DX11.jai. hw_device_ready_ms is measured from app.startup_time.
|
|
started_on_warp: bool;
|
|
startup_device_ms: float64; // D3D11CreateDevice for the startup device
|
|
startup_swap_chain_ms: float64; // its swap chain and render target
|
|
hw_device_create_ms: float64;
|
|
hw_device_ready_ms: float64;
|
|
hw_swap_ms: float64;
|
|
|
|
memory_usage: Memory_Usage;
|
|
|
|
frame_times: Circle_Buffer(Frame_Timing);
|
|
}
|
|
|
|
Frame_Timing :: struct {
|
|
work: float32; // seconds from the start of app_frame to just before Present (doesn't include vsync or swapchain waits)
|
|
delta: float32; // io.DeltaTime: wall time since the previous frame, vsync wait included
|
|
}
|
|
|
|
PERF_FRAME_TIME_HISTORY :: 1024;
|
|
|
|
load_hardware_information :: () {
|
|
{ // Get CPU information:
|
|
using cpu_info;
|
|
physical_cpu_count = get_number_of_processors(.ALL_PHYSICAL);
|
|
logical_cpu_count = get_number_of_processors(.ALL_LOGICAL);
|
|
performance_cpu_count = get_number_of_processors(.PERFORMANCE_PHYSICAL);
|
|
}
|
|
#if OS == .WINDOWS windows_get_cpu_information();
|
|
}
|
|
|
|
load_platform_information :: () {
|
|
platform_load_platform_information();
|
|
}
|
|
|
|
cpu_info: CPU_Info;
|
|
CPU_Info :: struct {
|
|
logical_cpu_count: s32;
|
|
physical_cpu_count: s32;
|
|
performance_cpu_count: s32;
|
|
}
|
|
|
|
CPUID_Type :: enum s32 {
|
|
All :: 0; // All Logical CPUs
|
|
Performance :: 1;
|
|
Efficiency :: 2;
|
|
|
|
// Top_Performance :: 64; // Typically two CPUs are marked with this value
|
|
}
|
|
|
|
platform: Platform; // Windows, MacOS, Linux
|
|
|
|
app: App;
|
|
App :: struct {
|
|
frame_count: s64;
|
|
startup_time: Apollo_Time;
|
|
frame_time: Apollo_Time;
|
|
|
|
win_scale: float32;
|
|
minimized_to_tray: bool;
|
|
was_minimized_to_tray: bool; // as of the last frame; main.jai drops stale input when it changes
|
|
vsync_on: bool = true;
|
|
sleep_on_fast_frame: bool = false;
|
|
should_exit: bool = false;
|
|
|
|
show_debug_panel: bool = DEBUG;
|
|
show_logger_panel: bool = DEBUG;
|
|
show_frame_time_graph: bool = DEBUG;
|
|
show_style_editor: bool;
|
|
show_help_panel: bool; // the Command Line window (base/debug.jai): -h, --help, or the Debug Panel
|
|
help_panel_focus: bool; // bring it forward next frame
|
|
minimize_to_tray_this_frame: bool;
|
|
restore_window_this_frame: bool;
|
|
capture_region_this_frame: bool; // the capture_region global hotkey fired: app/screen_capture.jai
|
|
record_region_this_frame: bool; // the record_region global hotkey fired: app/screen_record.jai
|
|
is_master: bool; // Are we the first instance of this app launched?
|
|
allow_multiple_instances: bool; // Skips the single-instance check, so a dev build can run beside an installed one.
|
|
should_exit_early: bool;
|
|
run_tests: bool;
|
|
headless: bool; // -headless (or a no_crash_dialog build): no message boxes on failures, and run_tests exits when done
|
|
exit_code: s32; // the process's exit code when the main loop ends
|
|
library_folder: string; // the .mcap folder the viewer shows: -library FOLDER, else [library] folder, else LIBRARY_FOLDER_DEFAULT
|
|
scan_and_exit: bool; // -scan: scan the library with the chosen OCR model once it has loaded, then exit (0, or 1 if any capture failed)
|
|
stats_and_exit: bool; // -stats: log the Statistics window's numbers once the catalog has loaded, then exit
|
|
catalog_threads: s64; // -catalog-threads N: workers loading the catalog at once, in place of all of the background group's (for measuring)
|
|
cold_start: bool;
|
|
no_catalog_cache: bool; // -no-catalog-cache: read every capture from its file, as without the catalog cache (for measuring)
|
|
launch_time: Apollo_Time; // monotonic, when main() started: the [startup] log lines count from it // -cold: load the library as on a cold start, its files dropped from the system's file cache first
|
|
start_search: string; // -search QUERY: the search box's text at startup
|
|
erase_and_exit: string; // -erase ocr|labels|segments|all: erase that from the whole library once it has loaded, then exit
|
|
record_and_exit: float64; // -record SECONDS: record the screen under the cursor that long, put it in the library, then exit (0, or 1 if it failed)
|
|
#if OS == .WINDOWS record_region: RECT; // -record-region X Y W H: -record records that, on the virtual desktop, not the whole screen
|
|
segment_and_exit: bool; // -segment: segment the library folders set to segment once the catalog has loaded, then exit (0, or 1 if any image failed)
|
|
import_source, import_dest: string; // -import SOURCE DEST: import SOURCE into the library folder DEST (app/import.jai), then exit
|
|
open_file: string; // mcap.exe FILE: the file to show (app/open_file.jai); absolute, with / between its folders
|
|
view_mode: bool; // FILE was given while another instance runs: only it is shown, with Import (app/open_file.jai)
|
|
open_import_dest: string; // -open-import DEST: FILE imported into the library folder DEST as its Import button does, then exit (0, or 1 if it failed)
|
|
last_window_restore_time: Apollo_Time;
|
|
|
|
imgui_context: *ImGui.ImGuiContext;
|
|
|
|
write_channel: Shared_Channel.Write_Channel;
|
|
read_channel: Shared_Channel.Read_Channel;
|
|
read_channel_fatal_errors: s32; // the first resets the queue, the second stops polling
|
|
|
|
large_pages_allowed: bool;
|
|
#if USING_WINDOWS {
|
|
is_admin: bool; // running with elevated privileges?
|
|
ui_global_clear_color: [4]float : .[0.45, 0.55, 0.60, 1.00];
|
|
} else #if USING_SDL {
|
|
ui_global_clear_color: SDL_FColor = .{ r=0.45, g=0.55, b=0.60, a=1.0 };
|
|
}
|
|
|
|
program_data_dir: string; // "<os program data>/<APP_FOLDER_NAME>"; see app_program_data_dir()
|
|
machine_name: string;
|
|
username: string;
|
|
args: []string;
|
|
}
|
|
|
|
#if USING_SDL {
|
|
// This uses SDL3 and SDL_GPU
|
|
ImGui :: #import "imgui-sdl";
|
|
|
|
#import "sdl3";
|
|
#import "freetype";
|
|
|
|
// Load the backend(s) you want. This *must* be #load because they might rely on other libraries.
|
|
// In this case, sdl3_gpu3 relies on types from "sdl3", so by importing sdl3 and loading imgui, imgui
|
|
// gets access to the types it needs.
|
|
#load "backends/sdl3_gpu3.jai";
|
|
#load "freetype/freetype.jai";
|
|
|
|
#load "base/platform/sdl.jai";
|
|
|
|
#load "base/platform/platform_posix.jai";
|
|
#if OS == .LINUX {
|
|
#load "base/platform/platform_linux.jai";
|
|
POSIX :: #import "POSIX";
|
|
}
|
|
else #if OS == .MACOS {
|
|
#load "base/platform/platform_macos.jai";
|
|
#load "base/platform/macos_bindings.jai";
|
|
#load "base/platform/macos_global_hotkeys.jai";
|
|
POSIX :: #import "POSIX";
|
|
}
|
|
|
|
APP_TRAY_ICON_PATH :: "images/tray_icon.png";
|
|
}
|
|
|
|
#if USING_WINDOWS {
|
|
// This uses Win32 API directly and DX11 for graphics
|
|
ImGui :: #import "imgui-dx11-win32"()(DEBUG = DEBUG);
|
|
|
|
#import "d3d11";
|
|
#import "d3d_compiler";
|
|
#import "dxgi";
|
|
|
|
#import "Windows";
|
|
#import "Windows_Utf8";
|
|
|
|
#load "base/platform/platform_windows.jai";
|
|
#load "base/platform/win32_bindings.jai";
|
|
#load "base/platform/win32_gdi_bindings.jai";
|
|
#load "graphics/DX11.jai";
|
|
|
|
APP_TRAY_ICON_PATH :: "images/tray_icon.ico";
|
|
}
|
|
|
|
// The folder the app's data, logs and images live in: the executable's, or on macOS the one
|
|
// holding the .app when run from the bundle, so replacing the bundle leaves the data alone. Ends in
|
|
// a separator, as path_strip_filename does. Set by the first call, which the logger makes at startup.
|
|
app_install_directory :: () -> string {
|
|
if !app_install_directory_path {
|
|
directory := path_strip_filename(get_path_of_running_executable(,, temp));
|
|
#if OS == .MACOS {
|
|
BUNDLE_EXECUTABLE_FOLDER :: "/Contents/MacOS/";
|
|
if ends_with(directory, tprint(".app%", BUNDLE_EXECUTABLE_FOLDER)) {
|
|
directory = path_strip_filename(slice(directory, 0, directory.count - BUNDLE_EXECUTABLE_FOLDER.count));
|
|
app_from_app_bundle = true;
|
|
}
|
|
}
|
|
app_install_directory_path = copy_string(directory,, context.default_allocator);
|
|
}
|
|
return app_install_directory_path;
|
|
}
|
|
|
|
app_running_from_app_bundle :: () -> bool {
|
|
app_install_directory();
|
|
return app_from_app_bundle;
|
|
}
|
|
|
|
// The folder the models' folders (ocr-models, label-models, segment-models) are in: the bundle's
|
|
// Contents/Resources on macOS, else beside the executable. Ends in a separator.
|
|
app_resource_directory :: () -> string {
|
|
directory := path_strip_filename(get_path_of_running_executable());
|
|
#if OS == .MACOS if app_running_from_app_bundle() return tprint("%../Resources/", directory);
|
|
return directory;
|
|
}
|
|
|
|
#scope_file
|
|
app_install_directory_path: string;
|
|
app_from_app_bundle: bool;
|
|
#scope_export
|
|
|
|
get_app_tray_icon_path :: () -> string {
|
|
push_allocator(temp);
|
|
icon_path := tprint("%1%2", app_install_directory(), APP_TRAY_ICON_PATH);
|
|
log("icon_path: %", icon_path);
|
|
return icon_path;
|
|
}
|
|
|
|
APP_LOG_DIRECTORY :: #run -> string {
|
|
return tprint("logs/%", VERSION);
|
|
}
|
|
APP_USER_SETTINGS_FILE_NAME :: "settings.config";
|
|
APP_USER_SETTINGS_VERSION :: 1;
|
|
// A file at any version from this one through APP_USER_SETTINGS_VERSION is readable. Raise
|
|
// APP_USER_SETTINGS_VERSION when an action is added to [hotkeys] or [global_hotkeys]; see
|
|
// hotkeys_missing_lines.
|
|
APP_USER_SETTINGS_OLDEST_READABLE_VERSION :: 1;
|
|
|
|
APP_SHARED_MEMORY_CHANNEL_HEADER_SIZE :: 2; // 1 byte for message-or-done, 1 byte for the checksum.
|
|
App_Shared_Memory_Tag :: enum u8 {
|
|
Done :: 0;
|
|
Bring_To_Front :: 1;
|
|
Open_File :: 2; // the path follows the header: show it (open_file_request)
|
|
Show_Help :: 3; // mcap --help while this one runs: come forward with the Command Line window
|
|
}
|
|
|
|
APP_SHARED_MEMORY_CHANNEL_BYTES :: 4096; // only one page?
|
|
|
|
APP_WINDOW_TITLE :: #run -> string {
|
|
append_debug := ifx DEBUG then " (DEBUG)\0" else " (RELEASE)\0";
|
|
git_info := ifx GIT_BRANCH != "master" && GIT_BRANCH != "main" then
|
|
tprint(" %-%", GIT_BRANCH, GIT_HASH) else tprint(" %", GIT_HASH);
|
|
return tprint("%1 %2%3%4", APP_NAME, VERSION,
|
|
git_info, append_debug);
|
|
}
|
|
|
|
// The window's title, ending in a 0 for the OS: in view mode, the file's name first.
|
|
app_window_title :: () -> string {
|
|
if !app.view_mode return APP_WINDOW_TITLE;
|
|
return tprint("% - % (view)\0", path_filename(app.open_file), APP_NAME);
|
|
}
|
|
|
|
APP_TRAY_TOOLTIP_TEXT :: #run -> string {
|
|
return ifx DEBUG
|
|
tprint("% % (DEBUG)\0", APP_NAME, VERSION)
|
|
else
|
|
tprint("% %\0", APP_NAME, VERSION);
|
|
}
|
|
|
|
#if OS == .LINUX {
|
|
clipboard_set_text :: (text: string) {
|
|
ImGui.SetClipboardText(temp_c_string(text));
|
|
}
|
|
} else {
|
|
clipboard_set_text :: (text: string) {
|
|
os_clipboard_set_text(text);
|
|
}
|
|
}
|