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; }