feat: move temp files to userspace folder

This commit is contained in:
Ixniy Evonniy 2026-07-01 10:00:09 +03:00
parent 1d349d6692
commit 40602121ea
5 changed files with 108 additions and 26 deletions

BIN
cache.db

Binary file not shown.

View File

@ -55,7 +55,7 @@ dialog_proc :: (hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT
SendMessageW(state.edit_hwnd, WM_SETFONT, cast(WPARAM) hFont, TRUE);
// Populate with existing URL if present
config_filename := ifx state.is_test_mode then "config_test.json" else "config.json";
config_filename := global_config_path;
config_data, read_ok := read_entire_file(config_filename);
if read_ok {
existing_url := get_json_string_field(config_data, "\"_url\"");
@ -112,7 +112,7 @@ dialog_proc :: (hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT
mode: string;
port := ifx state.is_test_mode then 10899 else 10801;
update_interval := 3600;
config_filename := ifx state.is_test_mode then "config_test.json" else "config.json";
config_filename := global_config_path;
config_data, read_ok := read_entire_file(config_filename);
if read_ok {
extracted_mode := get_json_string_field(config_data, "\"_mode\"");
@ -142,7 +142,7 @@ dialog_proc :: (hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT
state.url_saved = true;
}
} else {
config_filename := ifx state.is_test_mode then "config_test.json" else "config.json";
config_filename := global_config_path;
DeleteFileW(utf8_to_wide(config_filename));
state.url_saved = true;
}
@ -279,7 +279,7 @@ show_config_port_dialog :: (parent_hwnd: HWND, is_test_mode := false) -> bool {
SendMessageW(state.edit_hwnd, WM_SETFONT, cast(WPARAM) hFont, TRUE);
// Populate with existing port if present
config_filename := ifx state.is_test_mode then "config_test.json" else "config.json";
config_filename := global_config_path;
config_data, read_ok := read_entire_file(config_filename);
port := ifx state.is_test_mode then 10899 else 10801;
if !state.is_test_mode && read_ok {
@ -350,7 +350,7 @@ show_config_port_dialog :: (parent_hwnd: HWND, is_test_mode := false) -> bool {
if ok && val > 0 && val <= 65535 {
port := xx val;
config_filename := ifx state.is_test_mode then "config_test.json" else "config.json";
config_filename := global_config_path;
config_data, read_ok := read_entire_file(config_filename);
url := "";
mode := "system_proxy";

110
main.jai
View File

@ -8,6 +8,67 @@
#load "dialog.jai";
#load "updater.jai";
global_userspace_dir: string;
global_config_path: string;
global_config_run_path: string;
global_log_path: string;
init_userspace_paths :: (is_test: bool) {
name_wide := utf8_to_wide("LOCALAPPDATA");
len := GetEnvironmentVariableW(name_wide, null, 0);
if len > 0 {
buf := alloc(cast(s64) (len * 2));
defer free(buf);
GetEnvironmentVariableW(name_wide, cast(*u16) buf, len);
local_app_data := wide_to_utf8(cast(*u16) buf);
defer free(local_app_data);
global_userspace_dir = sprint("%\\singbox-tray", local_app_data);
} else {
global_userspace_dir = copy_string(".");
}
// Ensure directory exists
dir_wide := utf8_to_wide(global_userspace_dir);
CreateDirectoryW(dir_wide, null);
config_name := ifx is_test then "config_test.json" else "config.json";
config_run_name := ifx is_test then "config_run_test.json" else "config_run.json";
log_name := ifx is_test then "singbox_tray_test.log" else "singbox_tray.log";
global_config_path = sprint("%\\%", global_userspace_dir, config_name);
global_config_run_path = sprint("%\\%", global_userspace_dir, config_run_name);
global_log_path = sprint("%\\%", global_userspace_dir, log_name);
// Migrate existing configuration if it exists in the executable's directory
// but not in the userspace directory yet.
if file_exists(config_name) {
if !file_exists(global_config_path) {
data, ok := read_entire_file(config_name);
if ok {
write_entire_file(global_config_path, data);
free(data);
}
}
}
if !is_test {
test_config := sprint("%\\config_test.json", global_userspace_dir);
defer free(test_config);
test_config_run := sprint("%\\config_run_test.json", global_userspace_dir);
defer free(test_config_run);
test_log := sprint("%\\singbox_tray_test.log", global_userspace_dir);
defer free(test_log);
test_singbox_log := sprint("%\\sing-box_test.log", global_userspace_dir);
defer free(test_singbox_log);
DeleteFileW(utf8_to_wide(test_config));
DeleteFileW(utf8_to_wide(test_config_run));
DeleteFileW(utf8_to_wide(test_log));
DeleteFileW(utf8_to_wide(test_singbox_log));
}
}
// Custom constants
TIMER_PROCESS_CHECK :: 1;
TIMER_ANIMATION :: 2;
@ -58,7 +119,7 @@ append_to_log_file :: (text: string) {
OPEN_ALWAYS :: 4;
FILE_END :: 2;
filename := ifx global_is_test_mode then "singbox_tray_test.log" else "singbox_tray.log";
filename := ifx global_log_path.count > 0 then global_log_path else (ifx global_is_test_mode then "singbox_tray_test.log" else "singbox_tray.log");
wide_path := utf8_to_wide(filename);
hFile := CreateFileW(
@ -147,7 +208,7 @@ log_singbox :: (message: string, is_test_mode: bool) {
OutputDebugStringW(utf8_to_wide(colored_log));
// Append to the correct log file
filename := ifx is_test_mode then "singbox_tray_test.log" else "singbox_tray.log";
filename := ifx global_log_path.count > 0 then global_log_path else (ifx is_test_mode then "singbox_tray_test.log" else "singbox_tray.log");
wide_path := utf8_to_wide(filename);
hFile := CreateFileW(
@ -267,7 +328,7 @@ set_autostart :: (enable: bool) -> bool {
}
// Hidden Process Spawning with Job Object configuration and stdout/stderr redirection
create_process_hidden :: (cmd: string, stdout_handle: HANDLE = INVALID_HANDLE_VALUE) -> HANDLE, PROCESS_INFORMATION, bool {
create_process_hidden :: (cmd: string, stdout_handle: HANDLE = INVALID_HANDLE_VALUE, working_dir: string = "") -> HANDLE, PROCESS_INFORMATION, bool {
startup_info: STARTUPINFOW;
startup_info.cb = size_of(STARTUPINFOW);
startup_info.dwFlags = STARTF_USESHOWWINDOW;
@ -294,6 +355,11 @@ create_process_hidden :: (cmd: string, stdout_handle: HANDLE = INVALID_HANDLE_VA
inherit_handles := ifx stdout_handle != INVALID_HANDLE_VALUE then cast(BOOL) 1 else cast(BOOL) 0;
working_dir_wide: *u16 = null;
if working_dir.count > 0 {
working_dir_wide = utf8_to_wide(working_dir);
}
success := CreateProcessW(
null,
cmd_wide,
@ -302,7 +368,7 @@ create_process_hidden :: (cmd: string, stdout_handle: HANDLE = INVALID_HANDLE_VA
inherit_handles,
CREATE_NO_WINDOW,
null,
null,
working_dir_wide,
*startup_info,
*process_info
);
@ -351,9 +417,8 @@ update_tray :: (app: *App_State) {
start_singbox :: (app: *App_State) -> bool {
if app.singbox_running return true;
config_filename := ifx app.is_test_mode then "config_test.json" else "config.json";
config_run_filename := ifx app.is_test_mode then "config_run_test.json" else "config_run.json";
singbox_log_filename := ifx app.is_test_mode then "sing-box_test.log" else "sing-box.log";
config_filename := global_config_path;
config_run_filename := global_config_run_path;
has_config := file_exists(config_filename);
url_present := false;
@ -431,13 +496,14 @@ start_singbox :: (app: *App_State) -> bool {
}
}
exe_path := "sing-box.exe";
relative_exe_path := "sing-box.exe";
// Check if sing-box.exe is not in current folder, check in the sing-box subfolder
if !file_exists("sing-box.exe") {
if file_exists("sing-box/sing-box.exe") {
exe_path = "sing-box\\sing-box.exe";
relative_exe_path = "sing-box\\sing-box.exe";
}
}
exe_path := get_absolute_path(relative_exe_path);
hReadPipe, hWritePipe: HANDLE;
sa: SECURITY_ATTRIBUTES;
@ -454,7 +520,7 @@ start_singbox :: (app: *App_State) -> bool {
}
cmd := tprint("% run -c %", exe_path, config_run_filename);
job, pi, ok := create_process_hidden(cmd, hWritePipe);
job, pi, ok := create_process_hidden(cmd, hWritePipe, global_userspace_dir);
if hWritePipe != INVALID_HANDLE_VALUE {
CloseHandle(hWritePipe);
@ -528,9 +594,7 @@ stop_singbox :: (app: *App_State) {
log_info("Test Mode: System proxy bypass kept on stop.\n");
}
// Clean up temporary run config
config_run_filename := ifx app.is_test_mode then "config_run_test.json" else "config_run.json";
DeleteFileW(utf8_to_wide(config_run_filename));
DeleteFileW(utf8_to_wide(global_config_run_path));
update_tray(app);
log_print("Sing-box stopped.\n");
@ -567,8 +631,7 @@ check_process_status :: (app: *App_State) {
log_info("Test Mode: System proxy bypass kept on unexpected exit.\n");
}
config_run_filename := ifx app.is_test_mode then "config_run_test.json" else "config_run.json";
DeleteFileW(utf8_to_wide(config_run_filename));
DeleteFileW(utf8_to_wide(global_config_run_path));
update_tray(app);
}
@ -644,6 +707,8 @@ show_context_menu :: (app: *App_State) {
AppendMenuW(hUpdateMenu, MF_STRING | (ifx current_interval == 259200 then MF_CHECKED else 0), CMD_UPDATE_3D, utf8_to_wide("3 days"));
AppendMenuW(hUpdateMenu, MF_STRING | (ifx current_interval == 604800 then MF_CHECKED else 0), CMD_UPDATE_WEEKLY, utf8_to_wide("weekly"));
AppendMenuW(hConfigureMenu, MF_POPUP, cast(s64) hUpdateMenu, utf8_to_wide("Auto-update"));
AppendMenuW(hConfigureMenu, MF_SEPARATOR, 0, null);
AppendMenuW(hConfigureMenu, MF_STRING, CMD_CONFIG_DIR, utf8_to_wide("Config directory..."));
AppendMenuW(hMenu, MF_POPUP, cast(s64) hConfigureMenu, utf8_to_wide("Configure"));
@ -691,7 +756,7 @@ show_context_menu :: (app: *App_State) {
if changed {
log_print("Port configured, updating state...\n");
config_filename := ifx app.is_test_mode then "config_test.json" else "config.json";
config_filename := global_config_path;
config_data, read_ok := read_entire_file(config_filename);
if read_ok {
port_str := get_json_val_field(config_data, "\"_port\"");
@ -718,7 +783,7 @@ show_context_menu :: (app: *App_State) {
if app.use_system_proxy {
app.use_system_proxy = false;
config_filename := ifx app.is_test_mode then "config_test.json" else "config.json";
config_filename := global_config_path;
config_data, read_ok := read_entire_file(config_filename);
if read_ok {
url := get_json_string_field(config_data, "\"_url\"");
@ -743,7 +808,7 @@ show_context_menu :: (app: *App_State) {
if !app.use_system_proxy {
app.use_system_proxy = true;
config_filename := ifx app.is_test_mode then "config_test.json" else "config.json";
config_filename := global_config_path;
config_data, read_ok := read_entire_file(config_filename);
if read_ok {
url := get_json_string_field(config_data, "\"_url\"");
@ -775,6 +840,9 @@ show_context_menu :: (app: *App_State) {
MessageBoxW(app.hwnd, utf8_to_wide("Failed to update Windows registry autostart configuration."), utf8_to_wide("Sing-box Tray"), MB_ICONERROR);
}
}
case CMD_CONFIG_DIR; {
ShellExecuteW(null, utf8_to_wide("open"), utf8_to_wide(global_userspace_dir), null, null, 1);
}
case CMD_UPDATE_NEVER; {
save_update_interval(app, 0);
log_print("Auto-update interval set to: Never\n");
@ -971,7 +1039,7 @@ global_is_test_mode: bool = false;
save_update_interval :: (app: *App_State, interval: s32) {
app.updater_data.update_interval_seconds = interval;
config_filename := ifx app.is_test_mode then "config_test.json" else "config.json";
config_filename := global_config_path;
config_data, read_ok := read_entire_file(config_filename);
if read_ok {
url := get_json_string_field(config_data, "\"_url\"");
@ -1017,6 +1085,8 @@ main :: () {
}
}
init_userspace_paths(is_test);
hInstance := GetModuleHandleW(null);
class_name_str := ifx is_test then "SingboxTrayControllerClassTest" else "SingboxTrayControllerClass";
class_name := utf8_to_wide(class_name_str);
@ -1060,7 +1130,7 @@ main :: () {
app_state.hwnd = hwnd;
config_filename := ifx app_state.is_test_mode then "config_test.json" else "config.json";
config_filename := global_config_path;
app_state.updater_data.update_interval_seconds = 3600; // default

View File

@ -81,7 +81,7 @@ download_url :: (url: string) -> string, bool, string {
}
perform_update :: (is_test_mode := false) -> changed: bool, success: bool, error_msg: string {
config_filename := ifx is_test_mode then "config_test.json" else "config.json";
config_filename := global_config_path;
config_data, read_ok := read_entire_file(config_filename);
if !read_ok {

View File

@ -164,6 +164,7 @@ CMD_UPDATE_12H :: 1014;
CMD_UPDATE_DAILY :: 1015;
CMD_UPDATE_3D :: 1016;
CMD_UPDATE_WEEKLY :: 1017;
CMD_CONFIG_DIR :: 1018;
// Boolean constants
FALSE :: 0;
@ -204,6 +205,8 @@ GetWindowTextLengthW :: (hWnd: HWND) -> s32 #foreign user32;
SendMessageW :: (hWnd: HWND, Msg: u32, wParam: WPARAM, lParam: LPARAM) -> LRESULT #foreign user32;
SetEvent :: (hEvent: HANDLE) -> BOOL #foreign kernel32;
GetEnvironmentVariableW :: (lpName: *u16, lpBuffer: *u16, nSize: u32) -> u32 #foreign kernel32;
CreateDirectoryW :: (lpPathName: *u16, lpSecurityAttributes: *void) -> BOOL #foreign kernel32;
SetFocus :: (hWnd: HWND) -> HWND #foreign user32;
LOWORD :: (val: WPARAM) -> u16 {
@ -314,6 +317,15 @@ SHGetStockIconInfo :: (
psii: *SHSTOCKICONINFO
) -> s32 #foreign shell32;
ShellExecuteW :: (
hwnd: HWND,
lpOperation: LPCWSTR,
lpFile: LPCWSTR,
lpParameters: LPCWSTR,
lpDirectory: LPCWSTR,
nShowCmd: s32
) -> HANDLE #foreign shell32;
DestroyIcon :: (hIcon: HICON) -> BOOL #foreign user32;
wcslen :: (str: *u16) -> int {