feat: animated connecting icon

This commit is contained in:
Ixniy Evonniy 2026-07-01 00:32:43 +03:00
parent 8c0b65220a
commit 9e37d72071
3 changed files with 61 additions and 1 deletions

BIN
cache.db

Binary file not shown.

View File

@ -10,6 +10,7 @@
// Custom constants
TIMER_PROCESS_CHECK :: 1;
TIMER_ANIMATION :: 2;
IDI_APPLICATION :: cast(*u16) 32512;
IDI_SHIELD :: cast(*u16) 32518;
@ -28,6 +29,12 @@ App_State :: struct {
icon_running: HICON;
icon_stopped: HICON;
is_connecting: bool;
connecting_ticks: int;
is_updating: bool;
animation_frame: int;
icon_frames: [4] HICON;
}
// Custom log print sending logs to OutputDebugStringW
@ -133,7 +140,13 @@ update_tray :: (app: *App_State) {
nid.uID = 1;
nid.uFlags = NIF_ICON | NIF_TIP;
if app.singbox_running {
if app.is_connecting {
nid.hIcon = app.icon_frames[app.animation_frame];
set_tray_tip(*nid, "Sing-box: Connecting...");
} else if app.is_updating {
nid.hIcon = app.icon_frames[app.animation_frame];
set_tray_tip(*nid, "Sing-box: Updating Config...");
} else if app.singbox_running {
nid.hIcon = app.icon_running;
set_tray_tip(*nid, "Sing-box: Running");
} else {
@ -218,6 +231,11 @@ start_singbox :: (app: *App_State) -> bool {
app.singbox_job_handle = job;
app.singbox_running = true;
app.is_connecting = true;
app.connecting_ticks = 0;
app.animation_frame = 0;
SetTimer(app.hwnd, TIMER_ANIMATION, 250, null);
if app.use_system_proxy {
set_windows_system_proxy(true, tprint("127.0.0.1:%", app.port));
log_print("System proxy enabled on port %.\n", app.port);
@ -229,6 +247,9 @@ start_singbox :: (app: *App_State) -> bool {
}
stop_singbox :: (app: *App_State) {
app.is_connecting = false;
KillTimer(app.hwnd, TIMER_ANIMATION);
if !app.singbox_running return;
TerminateProcess(app.singbox_process_handle, 0);
@ -279,7 +300,15 @@ check_process_status :: (app: *App_State) {
}
trigger_immediate_update :: (app: *App_State) {
app.is_updating = true;
app.animation_frame = 0;
SetTimer(app.hwnd, TIMER_ANIMATION, 150, null);
update_tray(app);
changed, success, err_msg := perform_update();
app.is_updating = false;
if success {
if changed {
log_print("Config updated, restarting Sing-box...\n");
@ -293,10 +322,15 @@ trigger_immediate_update :: (app: *App_State) {
} else {
if !app.singbox_running {
start_singbox(app);
} else {
KillTimer(app.hwnd, TIMER_ANIMATION);
update_tray(app);
}
MessageBoxW(app.hwnd, utf8_to_wide("Configuration is already up-to-date."), utf8_to_wide("Sing-box Tray"), MB_ICONINFORMATION);
}
} else {
KillTimer(app.hwnd, TIMER_ANIMATION);
update_tray(app);
msg := tprint("Failed to download configuration.\nError: %", err_msg);
MessageBoxW(app.hwnd, utf8_to_wide(msg), utf8_to_wide("Sing-box Tray"), MB_ICONERROR);
}
@ -464,6 +498,22 @@ app_wnd_proc :: (hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESUL
case WM_TIMER; {
if wparam == TIMER_PROCESS_CHECK {
check_process_status(app);
} else if wparam == TIMER_ANIMATION {
if app.is_connecting {
app.connecting_ticks += 1;
app.animation_frame = (app.animation_frame + 1) % 4;
update_tray(app);
// Stop connecting animation after 12 ticks (3 seconds at 250ms)
if app.connecting_ticks >= 12 {
app.is_connecting = false;
KillTimer(hwnd, TIMER_ANIMATION);
update_tray(app);
}
} else if app.is_updating {
app.animation_frame = (app.animation_frame + 1) % 4;
update_tray(app);
}
}
return 0;
}
@ -504,6 +554,10 @@ main :: () {
app_state: App_State;
app_state.icon_running = load_stock_icon(SIID_WORLD);
app_state.icon_stopped = load_stock_icon(SIID_ERROR);
app_state.icon_frames[0] = load_stock_icon(SIID_SERVER);
app_state.icon_frames[1] = load_stock_icon(SIID_DRIVENET);
app_state.icon_frames[2] = load_stock_icon(SIID_WORLD);
app_state.icon_frames[3] = load_stock_icon(SIID_INTERNET);
hwnd := CreateWindowExW(
0,
@ -593,4 +647,7 @@ main :: () {
if app_state.icon_running DestroyIcon(app_state.icon_running);
if app_state.icon_stopped DestroyIcon(app_state.icon_stopped);
for app_state.icon_frames {
if it DestroyIcon(it);
}
}

View File

@ -207,8 +207,11 @@ SHSTOCKICONINFO :: struct {
szPath: [260] u16;
}
SIID_DRIVENET :: 9;
SIID_WORLD :: 13;
SIID_SERVER :: 15;
SIID_ERROR :: 80;
SIID_INTERNET :: 104;
SHGSI_ICON :: 0x00000100;
SHGSI_SMALLICON :: 0x00000001;