feat: remove curllib dependency
This commit is contained in:
parent
1c030cfa09
commit
6ea2cfd4b8
17
build.jai
17
build.jai
@ -15,22 +15,7 @@
|
||||
options := get_build_options(w);
|
||||
options.output_executable_name = "singbox_tray";
|
||||
|
||||
// Copy libcurl.dll from compiler modules to output directory
|
||||
for options.import_path {
|
||||
dll_path := tprint("%Curl/windows/lib/libcurl.dll", it);
|
||||
if file_exists(dll_path) {
|
||||
dest_path := "libcurl.dll";
|
||||
if options.output_path {
|
||||
dest_path = tprint("%/libcurl.dll", options.output_path);
|
||||
}
|
||||
if copy_file(dll_path, dest_path) {
|
||||
print("Build: Successfully copied libcurl.dll to %\n", dest_path);
|
||||
} else {
|
||||
print("Build Warning: Could not copy libcurl.dll to %\n", dest_path);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// No longer need to copy libcurl.dll since we use native Windows WinINet API.
|
||||
|
||||
// options.additional_linker_arguments is a slice, so copy it to a dynamic array, append, and assign back
|
||||
new_args: [..] string;
|
||||
|
||||
@ -12,7 +12,7 @@ graph TD
|
||||
B -->|Right Click| C[Context Menu]
|
||||
C -->|Set URL| D[Custom Edit Dialog]
|
||||
C -->|Start/Stop| E[Process Manager: sing-box.exe]
|
||||
C -->|Update Now| F[Auto-Update Loop: libcurl]
|
||||
C -->|Update Now| F[Auto-Update Loop: WinINet]
|
||||
C -->|Exit| G[Shutdown & Cleanup]
|
||||
F -->|Timer/Thread| F
|
||||
```
|
||||
@ -35,7 +35,7 @@ To avoid needing an external resource compiler (`.rc`) or complex UI libraries,
|
||||
- **Behavior**: Block interactions with other menus, read the edit text on OK, write it to a `url.txt` file, and trigger an immediate config update.
|
||||
|
||||
### C. Config Downloader (Auto-Update Engine)
|
||||
- **Curl Library**: Use the standard `Curl` module to perform HTTP requests.
|
||||
- **Downloader**: Use the Windows WinINet API to perform HTTP/HTTPS requests.
|
||||
- **Storage**: Save the downloaded JSON content to `config.json` in the same directory as the executable.
|
||||
- **Auto-Update Thread**: Spawn a background thread (using `Thread` module) that sleeps for a set interval (e.g., 1 hour), then downloads the URL. If the file content changes, it updates `config.json` and restarts the `sing-box` process if it was running.
|
||||
|
||||
@ -49,7 +49,7 @@ To avoid needing an external resource compiler (`.rc`) or complex UI libraries,
|
||||
|
||||
1. **`main.jai`**: Core application entry point, hidden window loop, tray icon management.
|
||||
2. **`dialog.jai`**: Edit-box dialog for config URL input.
|
||||
3. **`updater.jai`**: Libcurl wrapper and background auto-update thread.
|
||||
3. **`updater.jai`**: WinINet HTTP/HTTPS client and background auto-update thread.
|
||||
4. **`build.jai`**: Jai build script (metaprogram) to compile the app without standard console window popup (using `-subsystem windows`).
|
||||
|
||||
## 4. Key Questions & Decisions
|
||||
|
||||
84
updater.jai
84
updater.jai
@ -1,5 +1,4 @@
|
||||
#import "Basic";
|
||||
#import "Curl";
|
||||
#import "File";
|
||||
#import "Thread";
|
||||
#import "Windows";
|
||||
@ -12,44 +11,65 @@ Updater_Thread_Data :: struct {
|
||||
stop_event: HANDLE;
|
||||
}
|
||||
|
||||
write_callback :: (ptr: *u8, size: u64, nmemb: u64, userdata: *void) -> u64 #c_call {
|
||||
builder := cast(*String_Builder) userdata;
|
||||
push_context {
|
||||
append(builder, ptr, xx nmemb);
|
||||
}
|
||||
return nmemb;
|
||||
}
|
||||
|
||||
download_url :: (url: string) -> string, bool, string {
|
||||
curl := curl_easy_init();
|
||||
if !curl return "", false, "Failed to initialize Curl";
|
||||
defer curl_easy_cleanup(curl);
|
||||
hInternet := InternetOpenW(
|
||||
utf8_to_wide("SingboxTrayUpdater"),
|
||||
INTERNET_OPEN_TYPE_PRECONFIG,
|
||||
null,
|
||||
null,
|
||||
0
|
||||
);
|
||||
if !hInternet {
|
||||
return "", false, "Failed to initialize WinINet session";
|
||||
}
|
||||
defer InternetCloseHandle(hInternet);
|
||||
|
||||
curl_easy_setopt(curl, .URL, temp_c_string(url));
|
||||
curl_easy_setopt(curl, .FOLLOWLOCATION, 1);
|
||||
curl_easy_setopt(curl, .TIMEOUT, 30);
|
||||
flags: DWORD = INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE;
|
||||
if starts_with(url, "https") {
|
||||
flags |= INTERNET_FLAG_SECURE;
|
||||
}
|
||||
|
||||
// Disable SSL verification to prevent issues with missing CA cert bundles on Windows
|
||||
curl_easy_setopt(curl, .SSL_VERIFYPEER, 0);
|
||||
curl_easy_setopt(curl, .SSL_VERIFYHOST, 0);
|
||||
hUrl := InternetOpenUrlW(
|
||||
hInternet,
|
||||
utf8_to_wide(url),
|
||||
null,
|
||||
0,
|
||||
flags,
|
||||
0
|
||||
);
|
||||
if !hUrl {
|
||||
err := GetLastError();
|
||||
return "", false, tprint("Failed to open URL (Win32 Error: %)", err);
|
||||
}
|
||||
defer InternetCloseHandle(hUrl);
|
||||
|
||||
// Query response code
|
||||
response_code: DWORD;
|
||||
response_code_size: DWORD = size_of(type_of(response_code));
|
||||
if HttpQueryInfoW(hUrl, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, *response_code, *response_code_size, null) {
|
||||
if response_code != 200 {
|
||||
return "", false, tprint("HTTP request failed with status code %", response_code);
|
||||
}
|
||||
} else {
|
||||
err := GetLastError();
|
||||
return "", false, tprint("Failed to query HTTP status code (Win32 Error: %)", err);
|
||||
}
|
||||
|
||||
builder: String_Builder;
|
||||
defer free_buffers(*builder);
|
||||
|
||||
curl_easy_setopt(curl, .WRITEFUNCTION, write_callback);
|
||||
curl_easy_setopt(curl, .WRITEDATA, *builder);
|
||||
|
||||
curl_code := curl_easy_perform(curl);
|
||||
if curl_code != .OK {
|
||||
err_msg := tprint("Curl perform failed: %", to_string(curl_easy_strerror(curl_code)));
|
||||
return "", false, err_msg;
|
||||
}
|
||||
|
||||
response_code: int;
|
||||
curl_easy_getinfo(curl, .RESPONSE_CODE, *response_code);
|
||||
if response_code != 200 {
|
||||
err_msg := tprint("HTTP request failed with status code %", response_code);
|
||||
return "", false, err_msg;
|
||||
buffer: [4096] u8;
|
||||
bytes_read: DWORD;
|
||||
while true {
|
||||
ok := InternetReadFile(hUrl, buffer.data, xx buffer.count, *bytes_read);
|
||||
if !ok {
|
||||
err := GetLastError();
|
||||
return "", false, tprint("Error reading from URL (Win32 Error: %)", err);
|
||||
}
|
||||
if bytes_read == 0 {
|
||||
break;
|
||||
}
|
||||
append(*builder, buffer.data, xx bytes_read);
|
||||
}
|
||||
|
||||
return builder_to_string(*builder), true, "";
|
||||
|
||||
46
win32.jai
46
win32.jai
@ -153,6 +153,52 @@ RegCloseKey :: (hKey: HKEY) -> LSTATUS #foreign advapi32;
|
||||
|
||||
InternetSetOptionW :: (hInternet: HANDLE, dwOption: DWORD, lpBuffer: *void, dwBufferLength: DWORD) -> BOOL #foreign wininet;
|
||||
|
||||
HINTERNET :: *void;
|
||||
|
||||
INTERNET_OPEN_TYPE_PRECONFIG :: 0;
|
||||
INTERNET_FLAG_RELOAD :: 0x80000000;
|
||||
INTERNET_FLAG_SECURE :: 0x00800000;
|
||||
INTERNET_FLAG_NO_CACHE_WRITE :: 0x04000000;
|
||||
|
||||
HTTP_QUERY_STATUS_CODE :: 19;
|
||||
HTTP_QUERY_FLAG_NUMBER :: 0x20000000;
|
||||
|
||||
InternetOpenW :: (
|
||||
lpszAgent: LPCWSTR,
|
||||
dwAccessType: DWORD,
|
||||
lpszProxy: LPCWSTR,
|
||||
lpszProxyBypass: LPCWSTR,
|
||||
dwFlags: DWORD
|
||||
) -> HINTERNET #foreign wininet;
|
||||
|
||||
InternetOpenUrlW :: (
|
||||
hInternet: HINTERNET,
|
||||
lpszUrl: LPCWSTR,
|
||||
lpszHeaders: LPCWSTR,
|
||||
dwHeadersLength: DWORD,
|
||||
dwFlags: DWORD,
|
||||
dwContext: u64
|
||||
) -> HINTERNET #foreign wininet;
|
||||
|
||||
InternetReadFile :: (
|
||||
hFile: HINTERNET,
|
||||
lpBuffer: *void,
|
||||
dwNumberOfBytesToRead: DWORD,
|
||||
lpdwNumberOfBytesRead: *DWORD
|
||||
) -> BOOL #foreign wininet;
|
||||
|
||||
InternetCloseHandle :: (
|
||||
hInternet: HINTERNET
|
||||
) -> BOOL #foreign wininet;
|
||||
|
||||
HttpQueryInfoW :: (
|
||||
hRequest: HINTERNET,
|
||||
dwInfoLevel: DWORD,
|
||||
lpBuffer: *void,
|
||||
lpdwBufferLength: *DWORD,
|
||||
lpdwIndex: *DWORD
|
||||
) -> BOOL #foreign wininet;
|
||||
|
||||
wcslen :: (str: *u16) -> int {
|
||||
len := 0;
|
||||
while str[len] != 0 {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user