642 lines
24 KiB
Plaintext
642 lines
24 KiB
Plaintext
#import "Basic";
|
|
#import "Windows";
|
|
#import "Windows_Utf8";
|
|
#import "File";
|
|
#import "String";
|
|
#import "Thread";
|
|
|
|
// Win32 declarations are provided by win32.jai loaded in the main workspace.
|
|
|
|
Dialog_State :: struct {
|
|
edit_hwnd: HWND;
|
|
ok_hwnd: HWND;
|
|
cancel_hwnd: HWND;
|
|
dialog_done: bool;
|
|
url_saved: bool;
|
|
is_test_mode: bool;
|
|
}
|
|
|
|
dialog_proc :: (hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #c_call {
|
|
ctx: #Context;
|
|
push_context ctx {
|
|
state := cast(*Dialog_State) GetWindowLongPtrW(hwnd, GWLP_USERDATA);
|
|
|
|
if msg == {
|
|
case WM_CREATE; {
|
|
create_struct := cast(*CREATESTRUCTW) lparam;
|
|
state = cast(*Dialog_State) create_struct.lpCreateParams;
|
|
SetWindowLongPtrW(hwnd, GWLP_USERDATA, cast(LONG_PTR) state);
|
|
|
|
hFont := GetStockObject(DEFAULT_GUI_FONT);
|
|
|
|
static_label := CreateWindowExW(
|
|
0,
|
|
utf8_to_wide("STATIC"),
|
|
utf8_to_wide("Enter Sing-box Config URL:"),
|
|
WS_CHILD | WS_VISIBLE | SS_LEFT,
|
|
20, 20, 410, 20,
|
|
hwnd,
|
|
null,
|
|
null,
|
|
null
|
|
);
|
|
SendMessageW(static_label, WM_SETFONT, cast(WPARAM) hFont, TRUE);
|
|
|
|
state.edit_hwnd = CreateWindowExW(
|
|
WS_EX_CLIENTEDGE,
|
|
utf8_to_wide("EDIT"),
|
|
utf8_to_wide(""),
|
|
WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER | ES_AUTOHSCROLL,
|
|
20, 45, 410, 25,
|
|
hwnd,
|
|
null,
|
|
null,
|
|
null
|
|
);
|
|
SendMessageW(state.edit_hwnd, WM_SETFONT, cast(WPARAM) hFont, TRUE);
|
|
|
|
// Populate with existing URL if present
|
|
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\"");
|
|
if existing_url {
|
|
wide_url := utf8_to_wide(existing_url);
|
|
SetWindowTextW(state.edit_hwnd, wide_url);
|
|
}
|
|
free(config_data);
|
|
}
|
|
|
|
state.ok_hwnd = CreateWindowExW(
|
|
0,
|
|
utf8_to_wide("BUTTON"),
|
|
utf8_to_wide("OK"),
|
|
WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_DEFPUSHBUTTON,
|
|
220, 85, 100, 30,
|
|
hwnd,
|
|
cast(HMENU) IDOK,
|
|
null,
|
|
null
|
|
);
|
|
SendMessageW(state.ok_hwnd, WM_SETFONT, cast(WPARAM) hFont, TRUE);
|
|
|
|
state.cancel_hwnd = CreateWindowExW(
|
|
0,
|
|
utf8_to_wide("BUTTON"),
|
|
utf8_to_wide("Cancel"),
|
|
WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
|
|
330, 85, 100, 30,
|
|
hwnd,
|
|
cast(HMENU) IDCANCEL,
|
|
null,
|
|
null
|
|
);
|
|
SendMessageW(state.cancel_hwnd, WM_SETFONT, cast(WPARAM) hFont, TRUE);
|
|
|
|
SetFocus(state.edit_hwnd);
|
|
return 0;
|
|
}
|
|
case WM_COMMAND; {
|
|
control_id := LOWORD(wparam);
|
|
if control_id == IDOK {
|
|
length := GetWindowTextLengthW(state.edit_hwnd);
|
|
if length > 0 {
|
|
buffer := cast(*u16) alloc((length + 1) * size_of(u16));
|
|
defer free(buffer);
|
|
|
|
GetWindowTextW(state.edit_hwnd, buffer, length + 1);
|
|
url_utf8 := wide_to_utf8(buffer);
|
|
defer free(url_utf8);
|
|
|
|
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 := global_config_path;
|
|
config_data, read_ok := read_entire_file(config_filename);
|
|
if read_ok {
|
|
extracted_mode := get_json_string_field(config_data, "\"_mode\"");
|
|
if extracted_mode {
|
|
mode = copy_string(extracted_mode);
|
|
} 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 \"_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;
|
|
}
|
|
} else {
|
|
config_filename := global_config_path;
|
|
DeleteFileW(utf8_to_wide(config_filename));
|
|
state.url_saved = true;
|
|
}
|
|
DestroyWindow(hwnd);
|
|
return 0;
|
|
} else if control_id == IDCANCEL {
|
|
DestroyWindow(hwnd);
|
|
return 0;
|
|
}
|
|
}
|
|
case WM_CLOSE; {
|
|
DestroyWindow(hwnd);
|
|
return 0;
|
|
}
|
|
case WM_DESTROY; {
|
|
if state {
|
|
state.dialog_done = true;
|
|
}
|
|
PostThreadMessageW(GetCurrentThreadId(), WM_NULL, 0, 0);
|
|
return 0;
|
|
}
|
|
}
|
|
return DefWindowProcW(hwnd, msg, wparam, lparam);
|
|
}
|
|
}
|
|
|
|
show_config_url_dialog :: (parent_hwnd: HWND, is_test_mode := false) -> bool {
|
|
hInstance := GetModuleHandleW(null);
|
|
class_name := utf8_to_wide("SingboxConfigUrlDialogClass");
|
|
|
|
wclass: WNDCLASSEXW;
|
|
wclass.cbSize = size_of(WNDCLASSEXW);
|
|
wclass.style = CS_HREDRAW | CS_VREDRAW;
|
|
wclass.lpfnWndProc = dialog_proc;
|
|
wclass.hInstance = hInstance;
|
|
wclass.hCursor = LoadCursorW(null, IDC_ARROW);
|
|
wclass.hbrBackground = cast(HBRUSH) (COLOR_WINDOW + 1);
|
|
wclass.lpszClassName = class_name;
|
|
|
|
RegisterClassExW(*wclass);
|
|
defer UnregisterClassW(class_name, hInstance);
|
|
|
|
screen_w := GetSystemMetrics(SM_CXSCREEN);
|
|
screen_h := GetSystemMetrics(SM_CYSCREEN);
|
|
dialog_w: s32 = 460;
|
|
dialog_h: s32 = 170;
|
|
dialog_x := (screen_w - dialog_w) / 2;
|
|
dialog_y := (screen_h - dialog_h) / 2;
|
|
|
|
state: Dialog_State;
|
|
state.is_test_mode = is_test_mode;
|
|
|
|
dialog_hwnd := CreateWindowExW(
|
|
WS_EX_DLGMODALFRAME,
|
|
class_name,
|
|
utf8_to_wide("Configure Sing-box URL"),
|
|
WS_POPUP | WS_CAPTION | WS_SYSMENU,
|
|
dialog_x, dialog_y, dialog_w, dialog_h,
|
|
parent_hwnd,
|
|
null,
|
|
hInstance,
|
|
*state
|
|
);
|
|
|
|
if !dialog_hwnd return false;
|
|
|
|
if parent_hwnd {
|
|
EnableWindow(parent_hwnd, FALSE);
|
|
}
|
|
|
|
ShowWindow(dialog_hwnd, SW_SHOW);
|
|
UpdateWindow(dialog_hwnd);
|
|
|
|
while !state.dialog_done {
|
|
msg: MSG;
|
|
if GetMessageW(*msg, null, 0, 0) {
|
|
if !IsDialogMessageW(dialog_hwnd, *msg) {
|
|
TranslateMessage(*msg);
|
|
DispatchMessageW(*msg);
|
|
}
|
|
} else {
|
|
PostQuitMessage(cast(s32) msg.wParam);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if parent_hwnd {
|
|
EnableWindow(parent_hwnd, TRUE);
|
|
SetFocus(parent_hwnd);
|
|
}
|
|
|
|
return state.url_saved;
|
|
}
|
|
|
|
show_config_port_dialog :: (parent_hwnd: HWND, is_test_mode := false) -> bool {
|
|
// Port Dialog Proc
|
|
port_dialog_proc :: (hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #c_call {
|
|
ctx: #Context;
|
|
push_context ctx {
|
|
state := cast(*Dialog_State) GetWindowLongPtrW(hwnd, GWLP_USERDATA);
|
|
|
|
if msg == {
|
|
case WM_CREATE; {
|
|
create_struct := cast(*CREATESTRUCTW) lparam;
|
|
state = cast(*Dialog_State) create_struct.lpCreateParams;
|
|
SetWindowLongPtrW(hwnd, GWLP_USERDATA, cast(LONG_PTR) state);
|
|
|
|
hFont := GetStockObject(DEFAULT_GUI_FONT);
|
|
|
|
label_hwnd := CreateWindowExW(
|
|
0,
|
|
utf8_to_wide("STATIC"),
|
|
utf8_to_wide("Enter SOCKS/HTTP proxy port (default 10801):"),
|
|
WS_CHILD | WS_VISIBLE,
|
|
20, 20, 410, 20,
|
|
hwnd,
|
|
null,
|
|
null,
|
|
null
|
|
);
|
|
SendMessageW(label_hwnd, WM_SETFONT, cast(WPARAM) hFont, TRUE);
|
|
|
|
state.edit_hwnd = CreateWindowExW(
|
|
0,
|
|
utf8_to_wide("EDIT"),
|
|
utf8_to_wide(""),
|
|
WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER | ES_AUTOHSCROLL | ES_NUMBER,
|
|
20, 45, 410, 25,
|
|
hwnd,
|
|
null,
|
|
null,
|
|
null
|
|
);
|
|
SendMessageW(state.edit_hwnd, WM_SETFONT, cast(WPARAM) hFont, TRUE);
|
|
|
|
// Populate with existing port if present
|
|
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 {
|
|
port_str := get_json_val_field(config_data, "\"_port\"");
|
|
if port_str {
|
|
val, ok := to_integer(port_str);
|
|
if ok {
|
|
port = xx val;
|
|
}
|
|
}
|
|
free(config_data);
|
|
} else if state.is_test_mode && read_ok {
|
|
port_str := get_json_val_field(config_data, "\"_port\"");
|
|
if port_str {
|
|
val, ok := to_integer(port_str);
|
|
if ok {
|
|
port = xx val;
|
|
}
|
|
}
|
|
free(config_data);
|
|
}
|
|
|
|
wide_port := utf8_to_wide(tprint("%", port));
|
|
SetWindowTextW(state.edit_hwnd, wide_port);
|
|
|
|
state.ok_hwnd = CreateWindowExW(
|
|
0,
|
|
utf8_to_wide("BUTTON"),
|
|
utf8_to_wide("OK"),
|
|
WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_DEFPUSHBUTTON,
|
|
220, 85, 100, 30,
|
|
hwnd,
|
|
cast(HMENU) IDOK,
|
|
null,
|
|
null
|
|
);
|
|
SendMessageW(state.ok_hwnd, WM_SETFONT, cast(WPARAM) hFont, TRUE);
|
|
|
|
state.cancel_hwnd = CreateWindowExW(
|
|
0,
|
|
utf8_to_wide("BUTTON"),
|
|
utf8_to_wide("Cancel"),
|
|
WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
|
|
330, 85, 100, 30,
|
|
hwnd,
|
|
cast(HMENU) IDCANCEL,
|
|
null,
|
|
null
|
|
);
|
|
SendMessageW(state.cancel_hwnd, WM_SETFONT, cast(WPARAM) hFont, TRUE);
|
|
|
|
SetFocus(state.edit_hwnd);
|
|
return 0;
|
|
}
|
|
case WM_COMMAND; {
|
|
control_id := LOWORD(wparam);
|
|
if control_id == IDOK {
|
|
length := GetWindowTextLengthW(state.edit_hwnd);
|
|
if length > 0 {
|
|
buffer := cast(*u16) alloc((length + 1) * size_of(u16));
|
|
defer free(buffer);
|
|
|
|
GetWindowTextW(state.edit_hwnd, buffer, length + 1);
|
|
port_utf8 := wide_to_utf8(buffer);
|
|
defer free(port_utf8);
|
|
|
|
val, ok := to_integer(trim(port_utf8));
|
|
if ok && val > 0 && val <= 65535 {
|
|
port := xx val;
|
|
|
|
config_filename := global_config_path;
|
|
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);
|
|
|
|
extracted_mode := get_json_string_field(config_data, "\"_mode\"");
|
|
if extracted_mode {
|
|
mode = copy_string(extracted_mode);
|
|
} else {
|
|
mode = copy_string("system_proxy");
|
|
}
|
|
|
|
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 \"_update_interval\": %,\n \"inbounds\": [],\n \"outbounds\": []\n}", port, update_interval);
|
|
write_entire_file(config_filename, minimal_json);
|
|
}
|
|
if url free(url);
|
|
free(mode);
|
|
state.url_saved = true;
|
|
}
|
|
}
|
|
DestroyWindow(hwnd);
|
|
return 0;
|
|
} else if control_id == IDCANCEL {
|
|
DestroyWindow(hwnd);
|
|
return 0;
|
|
}
|
|
}
|
|
case WM_CLOSE; {
|
|
DestroyWindow(hwnd);
|
|
return 0;
|
|
}
|
|
case WM_DESTROY; {
|
|
state.dialog_done = true;
|
|
// Wake up the dialog loop
|
|
PostThreadMessageW(GetCurrentThreadId(), WM_NULL, 0, 0);
|
|
return 0;
|
|
}
|
|
}
|
|
return DefWindowProcW(hwnd, msg, wparam, lparam);
|
|
}
|
|
}
|
|
|
|
hInstance := GetModuleHandleW(null);
|
|
class_name := utf8_to_wide("SingboxPortDialogClass");
|
|
|
|
wclass: WNDCLASSEXW;
|
|
wclass.cbSize = size_of(WNDCLASSEXW);
|
|
wclass.lpfnWndProc = port_dialog_proc;
|
|
wclass.hInstance = hInstance;
|
|
wclass.hCursor = LoadCursorW(null, IDC_ARROW);
|
|
wclass.lpszClassName = class_name;
|
|
|
|
RegisterClassExW(*wclass);
|
|
defer UnregisterClassW(class_name, hInstance);
|
|
|
|
screen_w := GetSystemMetrics(SM_CXSCREEN);
|
|
screen_h := GetSystemMetrics(SM_CYSCREEN);
|
|
dialog_w: s32 = 460;
|
|
dialog_h: s32 = 170;
|
|
dialog_x := (screen_w - dialog_w) / 2;
|
|
dialog_y := (screen_h - dialog_h) / 2;
|
|
|
|
state: Dialog_State;
|
|
state.is_test_mode = is_test_mode;
|
|
|
|
dialog_hwnd := CreateWindowExW(
|
|
WS_EX_DLGMODALFRAME,
|
|
class_name,
|
|
utf8_to_wide("Configure Sing-box Port"),
|
|
WS_POPUP | WS_CAPTION | WS_SYSMENU,
|
|
dialog_x, dialog_y, dialog_w, dialog_h,
|
|
parent_hwnd,
|
|
null,
|
|
hInstance,
|
|
*state
|
|
);
|
|
|
|
if !dialog_hwnd return false;
|
|
|
|
if parent_hwnd {
|
|
EnableWindow(parent_hwnd, FALSE);
|
|
}
|
|
|
|
ShowWindow(dialog_hwnd, SW_SHOW);
|
|
UpdateWindow(dialog_hwnd);
|
|
|
|
while !state.dialog_done {
|
|
msg: MSG;
|
|
if GetMessageW(*msg, null, 0, 0) {
|
|
if !IsDialogMessageW(dialog_hwnd, *msg) {
|
|
TranslateMessage(*msg);
|
|
DispatchMessageW(*msg);
|
|
}
|
|
} else {
|
|
PostQuitMessage(cast(s32) msg.wParam);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if parent_hwnd {
|
|
EnableWindow(parent_hwnd, TRUE);
|
|
SetFocus(parent_hwnd);
|
|
}
|
|
|
|
return state.url_saved;
|
|
}
|
|
|
|
Download_Thread_Data :: struct {
|
|
url: string;
|
|
dest_path: string;
|
|
success: bool;
|
|
error_msg: string;
|
|
done: bool;
|
|
}
|
|
|
|
download_thread_proc :: (thread: *Thread) -> s64 {
|
|
data := cast(*Download_Thread_Data) thread.data;
|
|
data.success, data.error_msg = download_file(data.url, data.dest_path);
|
|
data.done = true;
|
|
return 0;
|
|
}
|
|
|
|
Download_Dialog_State :: struct {
|
|
dialog_done: bool;
|
|
thread: Thread;
|
|
thread_data: Download_Thread_Data;
|
|
static_hwnd: HWND;
|
|
}
|
|
|
|
download_dialog_proc :: (hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #c_call {
|
|
ctx: #Context;
|
|
push_context ctx {
|
|
state := cast(*Download_Dialog_State) GetWindowLongPtrW(hwnd, GWLP_USERDATA);
|
|
|
|
if msg == {
|
|
case WM_CREATE; {
|
|
create_struct := cast(*CREATESTRUCTW) lparam;
|
|
state = cast(*Download_Dialog_State) create_struct.lpCreateParams;
|
|
SetWindowLongPtrW(hwnd, GWLP_USERDATA, cast(LONG_PTR) state);
|
|
|
|
hFont := GetStockObject(DEFAULT_GUI_FONT);
|
|
|
|
state.static_hwnd = CreateWindowExW(
|
|
0,
|
|
utf8_to_wide("STATIC"),
|
|
utf8_to_wide("Downloading Sing-box core executable...\nThis may take a moment, please wait."),
|
|
WS_CHILD | WS_VISIBLE | SS_LEFT,
|
|
30, 30, 400, 50,
|
|
hwnd,
|
|
null,
|
|
null,
|
|
null
|
|
);
|
|
SendMessageW(state.static_hwnd, WM_SETFONT, cast(WPARAM) hFont, TRUE);
|
|
|
|
thread_init(*state.thread, download_thread_proc);
|
|
state.thread.data = *state.thread_data;
|
|
thread_start(*state.thread);
|
|
|
|
SetTimer(hwnd, 1, 100, null);
|
|
return 0;
|
|
}
|
|
case WM_TIMER; {
|
|
if wparam == 1 {
|
|
if state.thread_data.done {
|
|
KillTimer(hwnd, 1);
|
|
thread_is_done(*state.thread, -1);
|
|
// Capture error_msg (which may be temp-allocated inside download thread) BEFORE deinit frees the thread temp storage.
|
|
captured_success := state.thread_data.success;
|
|
captured_err := copy_string(state.thread_data.error_msg);
|
|
thread_deinit(*state.thread);
|
|
// Overwrite with safe copies (heap allocated) so that the post-loop return sees valid data.
|
|
state.thread_data.success = captured_success;
|
|
state.thread_data.error_msg = captured_err;
|
|
// Free the (copied) input paths now that thread is done with them.
|
|
if state.thread_data.url.count > 0 { free(state.thread_data.url); state.thread_data.url = ""; }
|
|
if state.thread_data.dest_path.count > 0 { free(state.thread_data.dest_path); state.thread_data.dest_path = ""; }
|
|
DestroyWindow(hwnd);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
case WM_CLOSE; {
|
|
if state.thread_data.done {
|
|
DestroyWindow(hwnd);
|
|
}
|
|
return 0;
|
|
}
|
|
case WM_DESTROY; {
|
|
if state {
|
|
state.dialog_done = true;
|
|
}
|
|
PostThreadMessageW(GetCurrentThreadId(), WM_NULL, 0, 0);
|
|
return 0;
|
|
}
|
|
}
|
|
return DefWindowProcW(hwnd, msg, wparam, lparam);
|
|
}
|
|
}
|
|
|
|
show_download_progress_dialog :: (parent_hwnd: HWND, url: string, dest_path: string) -> bool, string {
|
|
hInstance := GetModuleHandleW(null);
|
|
class_name := utf8_to_wide("SingboxDownloadDialogClass");
|
|
|
|
wclass: WNDCLASSEXW;
|
|
wclass.cbSize = size_of(WNDCLASSEXW);
|
|
wclass.style = CS_HREDRAW | CS_VREDRAW;
|
|
wclass.lpfnWndProc = download_dialog_proc;
|
|
wclass.hInstance = hInstance;
|
|
wclass.hCursor = LoadCursorW(null, IDC_ARROW);
|
|
wclass.hbrBackground = cast(HBRUSH) (COLOR_WINDOW + 1);
|
|
wclass.lpszClassName = class_name;
|
|
|
|
RegisterClassExW(*wclass);
|
|
defer UnregisterClassW(class_name, hInstance);
|
|
|
|
screen_w := GetSystemMetrics(SM_CXSCREEN);
|
|
screen_h := GetSystemMetrics(SM_CYSCREEN);
|
|
dialog_w: s32 = 460;
|
|
dialog_h: s32 = 150;
|
|
dialog_x := (screen_w - dialog_w) / 2;
|
|
dialog_y := (screen_h - dialog_h) / 2;
|
|
|
|
state: Download_Dialog_State;
|
|
state.thread_data.url = copy_string(url);
|
|
state.thread_data.dest_path = copy_string(dest_path);
|
|
|
|
dialog_hwnd := CreateWindowExW(
|
|
WS_EX_DLGMODALFRAME,
|
|
class_name,
|
|
utf8_to_wide("Downloading Sing-box Core"),
|
|
WS_POPUP | WS_CAPTION,
|
|
dialog_x, dialog_y, dialog_w, dialog_h,
|
|
parent_hwnd,
|
|
null,
|
|
hInstance,
|
|
*state
|
|
);
|
|
|
|
if !dialog_hwnd {
|
|
free(state.thread_data.url);
|
|
free(state.thread_data.dest_path);
|
|
return false, "Failed to create dialog window";
|
|
}
|
|
|
|
if parent_hwnd {
|
|
EnableWindow(parent_hwnd, FALSE);
|
|
}
|
|
|
|
ShowWindow(dialog_hwnd, SW_SHOW);
|
|
UpdateWindow(dialog_hwnd);
|
|
|
|
while !state.dialog_done {
|
|
msg: MSG;
|
|
if GetMessageW(*msg, null, 0, 0) {
|
|
if !IsDialogMessageW(dialog_hwnd, *msg) {
|
|
TranslateMessage(*msg);
|
|
DispatchMessageW(*msg);
|
|
}
|
|
} else {
|
|
PostQuitMessage(cast(s32) msg.wParam);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if parent_hwnd {
|
|
EnableWindow(parent_hwnd, TRUE);
|
|
SetFocus(parent_hwnd);
|
|
}
|
|
|
|
return state.thread_data.success, state.thread_data.error_msg;
|
|
}
|
|
|