mcap/src/base/platform/platform_linux.jai
Musa Mahmood 22cad98500 [0.1.15] Startup: a catalog cache, the catalog on the background threads, rpmalloc, and [startup] timings
The catalog is kept in .catalog-cache in the library, each entry with the size and modified time of
the file it was read from; a start takes the entries whose file is unchanged from it and reads only
the rest. It is saved once the catalog is complete and has changed (at most every 30 s) and at
shutdown. After a reboot the catalog was complete 145 ms after launch, against 3.5 s reading every
file.

The catalog loads on the background Thread_Group, every worker of it, instead of the four preview
workers. rpmalloc (modules/Default_Allocator, 2.0.1-jai.16) takes the place of Jai's single locked
heap: entries from the cache 380 -> 50 ms, and reading the files now scales with threads.
thread_idle is called where workers idle. check_targets.jai checks other OSes with Jai's own
allocator, as rpmalloc runs at compile time with the target's OS layer.

-cold drops the library from the system's file cache first; -no-catalog-cache and -catalog-threads N
are for measuring. [startup] log lines time the listing, the first frame, the cache, the first
preview and the complete catalog.
2026-10-13 07:26:42 +01:00

145 lines
4.7 KiB
Plaintext

platform_load_platform_information :: () {
}
platform_get_path_program_data :: inline () -> string {
return XDG.get_path(.DATA_HOME);
}
platform_get_path_config_data :: inline () -> string {
return XDG.get_path(.CONFIG_HOME);
}
// A tag such as ja-JP from the locale variables, or "". Temp.
platform_preferred_language :: () -> string {
for string.["LC_ALL", "LC_MESSAGES", "LANG"] {
value := posix_getenv(it);
if !value || value == "C" || value == "POSIX" continue;
tag := copy_string(value,, temp);
dot := find_index_from_left(tag, #char "."); // ja_JP.UTF-8
if dot >= 0 tag.count = dot;
return replace(tag, "_", "-",, temp);
}
return "";
}
// Temp. Noto Sans CJK's collection holds every script: JP, KR, SC, TC in that order.
platform_cjk_font_candidates :: () -> [] Cjk_Font {
list: [..] Cjk_Font;
list.allocator = temp;
for folder: string.["/usr/share/fonts/opentype/noto", "/usr/share/fonts/noto-cjk"] {
path := tprint("%/NotoSansCJK-Regular.ttc", folder);
cjk_candidate(*list, .JAPANESE, path, "Noto Sans CJK JP", face = 0);
cjk_candidate(*list, .KOREAN, path, "Noto Sans CJK KR", face = 1);
cjk_candidate(*list, .SIMPLIFIED, path, "Noto Sans CJK SC", face = 2);
cjk_candidate(*list, .TRADITIONAL, path, "Noto Sans CJK TC", face = 3);
}
return list;
}
// Wayland has no global hotkey API, and X11 key grabs are not implemented, so none are registered.
// A desktop's own shortcut settings can run the executable instead: a second launch brings the
// running instance forward over the IPC channel.
platform_apply_global_hotkeys :: (actions: [] Hotkey_Action) {
bound := false;
for actions if it.hotkey.binding_count bound = true;
if bound log("[hotkeys] Global hotkeys are not supported on Linux; [global_hotkeys] is ignored.");
}
platform_unregister_global_hotkeys :: () {}
// Resident and virtual sizes, and their peaks, from /proc/self/status.
platform_update_memory_usage_metrics :: () { using perf.memory_usage;
data, ok := read_entire_file("/proc/self/status", log_errors = false,, temp);
if !ok return;
kilobytes :: (data: string, key: string) -> s64 {
index := find_index_from_left(data, key);
if index < 0 return -1;
rest := slice(data, index + key.count, data.count - index - key.count);
value, ok := string_to_int(trim_left(rest));
return ifx ok then value else -1;
}
rss := kilobytes(data, "VmRSS:");
hwm := kilobytes(data, "VmHWM:");
size := kilobytes(data, "VmSize:");
peak := kilobytes(data, "VmPeak:");
if rss >= 0 working_set = rss * 1024;
if hwm >= 0 peak_working_set = hwm * 1024;
if size >= 0 virtual_memory = size * 1024;
if peak >= 0 peak_virtual_memory = peak * 1024;
}
linux_get_desktop_environment :: () -> Linux_Desktop_Environment {
push_allocator(temp);
result := to_string(SDL_getenv("XDG_CURRENT_DESKTOP"));
log("XDG_CURRENT_DESKTOP: %", result);
// Some desktops list several names, as in ubuntu:GNOME.
for name: split(to_lower_copy(result), ":") {
if name == {
case "xfce"; return .XFCE;
case "gnome"; return .GNOME;
case "kde"; return .KDE;
case "hyprland"; return .Hyprland;
case "sway"; return .sway;
case "unity"; return .Unity;
case "lxqt"; return .LXQt;
case "mate"; return .MATE;
}
}
return .UNKNOWN;
}
Linux_Desktop_Environment :: enum s32 {
UNKNOWN :: 0;
XFCE :: 1;
GNOME :: 2;
KDE :: 3;
Hyprland :: 4;
sway :: 5;
Unity :: 6;
LXQt :: 7;
MATE :: 8;
}
Linux_Display_Backend :: enum s32 {
X11 :: 0;
WAYLAND :: 1;
OTHER :: -1;
}
linux_get_SDL3_backend :: () -> Linux_Display_Backend {
driver := to_string(SDL_GetCurrentVideoDriver());
if driver == "x11"
return .X11;
if driver == "wayland"
return .WAYLAND;
return .OTHER;
}
linux_platform_setup :: () { using platform;
desktop_environment = linux_get_desktop_environment();
display_backend = linux_get_SDL3_backend();
log("[linux] Desktop environment: %, display backend: %.", desktop_environment, display_backend);
}
os_clear_unused_pages :: () {
log("[os_clear_unused_pages] Not implemented on Linux.");
}
platform_set_thread_affinity_all :: #bake_arguments platform_set_thread_affinity(affinity=.All);
platform_set_thread_affinity :: (thread: *Thread, affinity: CPUID_Type) {
}
XDG :: #import "XDG";
// Drops what the page cache holds of the file, so the next read comes from the disk (-cold). Clean
// pages go at once; pages not yet written back stay. False when it cannot be opened.
platform_evict_file_cache :: (path: string) -> bool {
fd := POSIX.open(temp_c_string(path), POSIX.O_RDONLY | POSIX.O_CLOEXEC);
if fd < 0 return false;
defer POSIX.close(fd);
return POSIX.posix_fadvise(fd, 0, 0, POSIX.POSIX_FADV_DONTNEED) == 0;
}