diff --git a/cache.db b/cache.db index 8d6e153..904b1c8 100644 Binary files a/cache.db and b/cache.db differ diff --git a/dialog.jai b/dialog.jai index 3243b16..4183958 100644 --- a/dialog.jai +++ b/dialog.jai @@ -110,6 +110,8 @@ dialog_proc :: (hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT trimmed_url := trim(url_utf8); if trimmed_url { 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_data, read_ok := read_entire_file(config_filename); if read_ok { @@ -119,13 +121,23 @@ dialog_proc :: (hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT } else { mode = copy_string("system_proxy"); } + port_str := get_json_val_field(config_data, "\"_port\""); + if port_str { + val, ok := to_integer(port_str); + if ok port = xx val; + } + interval_str := get_json_val_field(config_data, "\"_update_interval\""); + if interval_str { + val, ok := to_integer(interval_str); + if ok update_interval = xx val; + } free(config_data); } else { mode = copy_string("system_proxy"); } defer free(mode); - minimal_json := tprint("{\n \"_url\": \"%\",\n \"_mode\": \"%\",\n \"inbounds\": [],\n \"outbounds\": []\n}", trimmed_url, mode); + minimal_json := tprint("{\n \"_url\": \"%\",\n \"_mode\": \"%\",\n \"_port\": %,\n \"_update_interval\": %,\n \"inbounds\": [],\n \"outbounds\": []\n}", trimmed_url, mode, port, update_interval); write_entire_file(config_filename, minimal_json); state.url_saved = true; } @@ -342,6 +354,7 @@ show_config_port_dialog :: (parent_hwnd: HWND, is_test_mode := false) -> bool { config_data, read_ok := read_entire_file(config_filename); url := ""; mode := "system_proxy"; + update_interval := 3600; if read_ok { extracted_url := get_json_string_field(config_data, "\"_url\""); if extracted_url url = copy_string(extracted_url); @@ -353,12 +366,18 @@ show_config_port_dialog :: (parent_hwnd: HWND, is_test_mode := false) -> bool { mode = copy_string("system_proxy"); } - updated := set_json_metadata(config_data, url, mode, port); + interval_str := get_json_val_field(config_data, "\"_update_interval\""); + if interval_str { + val, ok := to_integer(interval_str); + if ok update_interval = xx val; + } + + updated := set_json_metadata(config_data, url, mode, port, update_interval); write_entire_file(config_filename, updated); free(config_data); free(updated); } else { - minimal_json := tprint("{\n \"_url\": \"\",\n \"_mode\": \"system_proxy\",\n \"_port\": %,\n \"inbounds\": [],\n \"outbounds\": []\n}", port); + minimal_json := tprint("{\n \"_url\": \"\",\n \"_mode\": \"system_proxy\",\n \"_port\": %,\n \"_update_interval\": %,\n \"inbounds\": [],\n \"outbounds\": []\n}", port, update_interval); write_entire_file(config_filename, minimal_json); } if url free(url); diff --git a/main.jai b/main.jai index fd039c9..4f209e4 100644 --- a/main.jai +++ b/main.jai @@ -602,6 +602,18 @@ show_context_menu :: (app: *App_State) { AppendMenuW(hMenu, MF_STRING, CMD_SET_PORT, utf8_to_wide("Configure Port...")); AppendMenuW(hMenu, MF_STRING, CMD_UPDATE_NOW, utf8_to_wide("Update Config Now")); + hUpdateMenu := CreatePopupMenu(); + current_interval := app.updater_data.update_interval_seconds; + AppendMenuW(hUpdateMenu, MF_STRING | (ifx current_interval == 0 then MF_CHECKED else 0), CMD_UPDATE_NEVER, utf8_to_wide("Never")); + AppendMenuW(hUpdateMenu, MF_STRING | (ifx current_interval == 1800 then MF_CHECKED else 0), CMD_UPDATE_30M, utf8_to_wide("30 minutes")); + AppendMenuW(hUpdateMenu, MF_STRING | (ifx current_interval == 3600 then MF_CHECKED else 0), CMD_UPDATE_1H, utf8_to_wide("1 hour")); + AppendMenuW(hUpdateMenu, MF_STRING | (ifx current_interval == 21600 then MF_CHECKED else 0), CMD_UPDATE_6H, utf8_to_wide("6 hours")); + AppendMenuW(hUpdateMenu, MF_STRING | (ifx current_interval == 43200 then MF_CHECKED else 0), CMD_UPDATE_12H, utf8_to_wide("12 hours")); + AppendMenuW(hUpdateMenu, MF_STRING | (ifx current_interval == 86400 then MF_CHECKED else 0), CMD_UPDATE_DAILY, utf8_to_wide("Daily")); + 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(hMenu, MF_POPUP, cast(s64) hUpdateMenu, utf8_to_wide("Config Auto-Update")); + AppendMenuW(hMenu, MF_SEPARATOR, 0, null); proxy_flags := MF_STRING; @@ -685,7 +697,7 @@ show_context_menu :: (app: *App_State) { config_data, read_ok := read_entire_file(config_filename); if read_ok { url := get_json_string_field(config_data, "\"_url\""); - updated := set_json_metadata(config_data, url, "proxy", app.port); + updated := set_json_metadata(config_data, url, "proxy", app.port, app.updater_data.update_interval_seconds); write_entire_file(config_filename, updated); free(config_data); free(updated); @@ -710,7 +722,7 @@ show_context_menu :: (app: *App_State) { config_data, read_ok := read_entire_file(config_filename); if read_ok { url := get_json_string_field(config_data, "\"_url\""); - updated := set_json_metadata(config_data, url, "system_proxy", app.port); + updated := set_json_metadata(config_data, url, "system_proxy", app.port, app.updater_data.update_interval_seconds); write_entire_file(config_filename, updated); free(config_data); free(updated); @@ -738,6 +750,38 @@ 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_UPDATE_NEVER; { + save_update_interval(app, 0); + log_print("Auto-update interval set to: Never\n"); + } + case CMD_UPDATE_30M; { + save_update_interval(app, 1800); + log_print("Auto-update interval set to: 30 minutes\n"); + } + case CMD_UPDATE_1H; { + save_update_interval(app, 3600); + log_print("Auto-update interval set to: 1 hour\n"); + } + case CMD_UPDATE_6H; { + save_update_interval(app, 21600); + log_print("Auto-update interval set to: 6 hours\n"); + } + case CMD_UPDATE_12H; { + save_update_interval(app, 43200); + log_print("Auto-update interval set to: 12 hours\n"); + } + case CMD_UPDATE_DAILY; { + save_update_interval(app, 86400); + log_print("Auto-update interval set to: Daily\n"); + } + case CMD_UPDATE_3D; { + save_update_interval(app, 259200); + log_print("Auto-update interval set to: 3 days\n"); + } + case CMD_UPDATE_WEEKLY; { + save_update_interval(app, 604800); + log_print("Auto-update interval set to: weekly\n"); + } case CMD_EXIT; { DestroyWindow(app.hwnd); } @@ -816,6 +860,25 @@ load_stock_icon :: (siid: s32) -> HICON { 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_data, read_ok := read_entire_file(config_filename); + if read_ok { + url := get_json_string_field(config_data, "\"_url\""); + mode := get_json_string_field(config_data, "\"_mode\""); + + mode_val := ifx mode then mode else "system_proxy"; + + updated := set_json_metadata(config_data, url, mode_val, app.port, interval); + write_entire_file(config_filename, updated); + + free(config_data); + free(updated); + } +} + main :: () { args := get_command_line_arguments(); is_test := false; @@ -891,7 +954,9 @@ main :: () { config_filename := ifx app_state.is_test_mode then "config_test.json" else "config.json"; - // Load persisted mode and port from config file + app_state.updater_data.update_interval_seconds = 3600; // default + + // Load persisted mode, port, and update interval from config file config_data, read_ok := read_entire_file(config_filename); if read_ok { extracted_mode := get_json_string_field(config_data, "\"_mode\""); @@ -899,6 +964,15 @@ main :: () { app_state.use_system_proxy = false; } + interval_str := get_json_val_field(config_data, "\"_update_interval\""); + if interval_str { + val, ok := to_integer(interval_str); + if ok { + app_state.updater_data.update_interval_seconds = xx val; + log_print("Loaded update interval: % seconds\n", app_state.updater_data.update_interval_seconds); + } + } + if app_state.is_test_mode { app_state.port = 10899; // force test port in test mode log_info("Loaded config from %. Test mode: forcing port to %.\n", config_filename, app_state.port); @@ -946,7 +1020,6 @@ main :: () { defer KillTimer(hwnd, TIMER_PROCESS_CHECK); app_state.updater_data.hwnd = hwnd; - app_state.updater_data.update_interval_seconds = 3600; app_state.updater_data.stop_event = CreateEventW(null, TRUE, FALSE, null); app_state.updater_data.is_test_mode = app_state.is_test_mode; diff --git a/updater.jai b/updater.jai index f9f6a40..8a6f6bf 100644 --- a/updater.jai +++ b/updater.jai @@ -110,6 +110,15 @@ perform_update :: (is_test_mode := false) -> changed: bool, success: bool, error } } + update_interval := 3600; + interval_str := get_json_val_field(config_data, "\"_update_interval\""); + if interval_str { + val, parse_ok, remainder := to_integer(interval_str); + if parse_ok { + update_interval = xx val; + } + } + downloaded, success, err_msg := download_url(url); if !success return false, false, err_msg; defer free(downloaded); @@ -117,7 +126,7 @@ perform_update :: (is_test_mode := false) -> changed: bool, success: bool, error modified := modify_config_inbounds(downloaded, port); defer free(modified); - final_config := set_json_metadata(modified, url, mode, port); + final_config := set_json_metadata(modified, url, mode, port, update_interval); defer free(final_config); if config_data == final_config { @@ -137,27 +146,37 @@ updater_thread_proc :: (thread: *Thread) -> s64 { data := cast(*Updater_Thread_Data) thread.data; if !data return 1; + elapsed_seconds := 0; while true { - // Wait on the stop event for the specified interval. - // If the stop event is signaled, wake up immediately and terminate. - status := WaitForSingleObject(data.stop_event, cast(DWORD) (data.update_interval_seconds * 1000)); + status := WaitForSingleObject(data.stop_event, 1000); if status == WAIT_OBJECT_0 { break; } - log_debug("Background update: Starting automatic config check...\n"); - changed, success, err_msg := perform_update(data.is_test_mode); - if success { - if changed { - log_info("Background update: Configuration updated successfully. Triggering sing-box restart...\n"); - if data.hwnd { - PostMessageW(data.hwnd, WM_RESTART_SINGBOX, 0, 0); + interval := data.update_interval_seconds; + if interval <= 0 { + elapsed_seconds = 0; + continue; + } + + elapsed_seconds += 1; + if elapsed_seconds >= interval { + elapsed_seconds = 0; + + log_debug("Background update: Starting automatic config check...\n"); + changed, success, err_msg := perform_update(data.is_test_mode); + if success { + if changed { + log_info("Background update: Configuration updated successfully. Triggering sing-box restart...\n"); + if data.hwnd { + PostMessageW(data.hwnd, WM_RESTART_SINGBOX, 0, 0); + } + } else { + log_debug("Background update: Configuration is already up-to-date.\n"); } } else { - log_debug("Background update: Configuration is already up-to-date.\n"); + log_error("Background update failed: %\n", err_msg); } - } else { - log_error("Background update failed: %\n", err_msg); } } @@ -277,7 +296,7 @@ get_json_val_field :: (json: string, field: string) -> string { return trim(slice(json, val_start, val_end - val_start + 1)); } -set_json_metadata :: (json: string, url: string, mode: string, port: int) -> string { +set_json_metadata :: (json: string, url: string, mode: string, port: int, update_interval: int) -> string { lines := split(json, "\n"); defer array_free(lines); @@ -289,12 +308,13 @@ set_json_metadata :: (json: string, url: string, mode: string, port: int) -> str if find_index_from_left(trimmed, "\"_url\"") != -1 continue; if find_index_from_left(trimmed, "\"_mode\"") != -1 continue; if find_index_from_left(trimmed, "\"_port\"") != -1 continue; + if find_index_from_left(trimmed, "\"_update_interval\"") != -1 continue; append(*builder, it); append(*builder, "\n"); if !inserted && find_index_from_left(trimmed, "{") != -1 { - append(*builder, tprint(" \"_url\": \"%\",\n \"_mode\": \"%\",\n \"_port\": %,\n", url, mode, port)); + append(*builder, tprint(" \"_url\": \"%\",\n \"_mode\": \"%\",\n \"_port\": %,\n \"_update_interval\": %,\n", url, mode, port, update_interval)); inserted = true; } } @@ -312,6 +332,7 @@ strip_json_metadata :: (json: string) -> string { if find_index_from_left(trimmed, "\"_url\"") != -1 continue; if find_index_from_left(trimmed, "\"_mode\"") != -1 continue; if find_index_from_left(trimmed, "\"_port\"") != -1 continue; + if find_index_from_left(trimmed, "\"_update_interval\"") != -1 continue; append(*builder, it); append(*builder, "\n"); diff --git a/win32.jai b/win32.jai index d63a100..6e7c7f1 100644 --- a/win32.jai +++ b/win32.jai @@ -46,7 +46,9 @@ MF_GRAYED : u32 : 0x00000001; MF_DISABLED : u32 : 0x00000002; MF_SEPARATOR : u32 : 0x00000800; MF_CHECKED : u32 : 0x00000008; +MF_POPUP : u32 : 0x00000010; +// NIM constants NIM_ADD : u32 : 0x00000000; NIM_MODIFY : u32 : 0x00000001; NIM_DELETE : u32 : 0x00000002; @@ -83,6 +85,14 @@ CMD_MODE_PROXY :: 1006; CMD_MODE_SYS_PROXY :: 1007; CMD_SET_PORT :: 1008; CMD_TOGGLE_AUTOSTART:: 1009; +CMD_UPDATE_NEVER :: 1010; +CMD_UPDATE_30M :: 1011; +CMD_UPDATE_1H :: 1012; +CMD_UPDATE_6H :: 1013; +CMD_UPDATE_12H :: 1014; +CMD_UPDATE_DAILY :: 1015; +CMD_UPDATE_3D :: 1016; +CMD_UPDATE_WEEKLY :: 1017; // Boolean constants FALSE :: 0;