diff --git a/.github/workflows/release-windows.yml b/.github/workflows/release-windows.yml index dff7cba..1231cd2 100644 --- a/.github/workflows/release-windows.yml +++ b/.github/workflows/release-windows.yml @@ -60,7 +60,6 @@ jobs: mingw-w64-clang-x86_64-gtk4 mingw-w64-clang-x86_64-gtksourceview5 mingw-w64-clang-x86_64-libadwaita - mingw-w64-clang-x86_64-imagemagick mingw-w64-clang-x86_64-openssl mingw-w64-clang-x86_64-pkg-config mingw-w64-clang-x86_64-toolchain @@ -117,18 +116,6 @@ jobs: cp /clang64/ssl/certs/ca-bundle.crt "$bundle/ssl/certs/" 2>/dev/null || true cp /clang64/etc/ssl/certs/ca-bundle.crt "$bundle/ssl/certs/" 2>/dev/null || true - cat > "$bundle/aetheris.bat" <<'EOF' - @echo off - set "AETHERIS_DIR=%~dp0" - set "PATH=%AETHERIS_DIR%bin;%PATH%" - set "XDG_DATA_DIRS=%AETHERIS_DIR%share" - set "GSETTINGS_SCHEMA_DIR=%AETHERIS_DIR%share\glib-2.0\schemas" - set "GDK_PIXBUF_MODULEDIR=%AETHERIS_DIR%lib\gdk-pixbuf-2.0\2.10.0\loaders" - set "GIO_MODULE_DIR=%AETHERIS_DIR%lib\gio\modules" - set "SSL_CERT_FILE=%AETHERIS_DIR%ssl\certs\ca-bundle.crt" - "%AETHERIS_DIR%bin\aetheris.exe" %* - EOF - ( cd target/windows zip -r "../../dist/${{ steps.names.outputs.windows_name }}" aetheris @@ -144,18 +131,9 @@ jobs: shell: msys2 {0} run: | set -euo pipefail - mkdir -p target/windows - magick \ - data/icons/hicolor/64x64/apps/org.luminusos.Aetheris.png \ - data/icons/hicolor/128x128/apps/org.luminusos.Aetheris.png \ - data/icons/hicolor/256x256/apps/org.luminusos.Aetheris.png \ - data/icons/hicolor/512x512/apps/org.luminusos.Aetheris.png \ - target/windows/aetheris.ico - cp target/windows/aetheris.ico target/windows/aetheris/aetheris.ico - source_dir="$(cygpath -w "$(pwd)/target/windows/aetheris")" output_dir="$(cygpath -w "$(pwd)/dist")" - setup_icon_file="$(cygpath -w "$(pwd)/target/windows/aetheris.ico")" + setup_icon_file="$(cygpath -w "$(pwd)/data/icons/windows/aetheris.ico")" inno_script="$(cygpath -w "$(pwd)/build-aux/inno/aetheris.iss")" MSYS2_ARG_CONV_EXCL='*' iscc \ diff --git a/Cargo.lock b/Cargo.lock index 621127d..3515c4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,6 +10,7 @@ dependencies = [ "aetheris-openconnect", "anyhow", "dirs", + "embed-resource", "futures", "gettext-rs", "relm4", @@ -457,6 +458,20 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +[[package]] +name = "embed-resource" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbfdaacccebec3b28e4866b8973543c7647797db5ada1bdab552e48fe665fbbd" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml", + "vswhom", + "winreg", +] + [[package]] name = "encoding_rs" version = "0.8.35" @@ -2695,6 +2710,26 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "vte4" version = "0.10.0" @@ -2835,6 +2870,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.61.2" @@ -2917,6 +2961,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.55.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" +dependencies = [ + "cfg-if", + "windows-sys 0.59.0", +] + [[package]] name = "wit-bindgen" version = "0.57.1" diff --git a/build-aux/inno/aetheris.iss b/build-aux/inno/aetheris.iss index 4f6d6d7..f7e5ac0 100644 --- a/build-aux/inno/aetheris.iss +++ b/build-aux/inno/aetheris.iss @@ -32,7 +32,7 @@ AppSupportURL=https://github.com/luminusOS/aetheris/issues AppUpdatesURL=https://github.com/luminusOS/aetheris/releases DefaultDirName={localappdata}\Programs\Aetheris DefaultGroupName=Aetheris -UninstallDisplayIcon={app}\aetheris.ico +UninstallDisplayIcon={app}\bin\aetheris.exe OutputDir={#OutputDir} OutputBaseFilename=aetheris-setup Compression=lzma2/ultra64 @@ -66,9 +66,9 @@ Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{ Source: "{#SourceDir}\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs [Icons] -Name: "{group}\Aetheris"; Filename: "{app}\aetheris.bat"; IconFilename: "{app}\aetheris.ico" +Name: "{group}\Aetheris"; Filename: "{app}\bin\aetheris.exe" Name: "{group}\Uninstall Aetheris"; Filename: "{uninstallexe}" -Name: "{autodesktop}\Aetheris"; Filename: "{app}\aetheris.bat"; IconFilename: "{app}\aetheris.ico"; Tasks: desktopicon +Name: "{autodesktop}\Aetheris"; Filename: "{app}\bin\aetheris.exe"; Tasks: desktopicon [Run] -Filename: "{app}\aetheris.bat"; Description: "Launch Aetheris"; Flags: nowait postinstall skipifnotsilent +Filename: "{app}\bin\aetheris.exe"; Description: "Launch Aetheris"; Flags: nowait postinstall skipifnotsilent diff --git a/crates/aetheris-app/Cargo.toml b/crates/aetheris-app/Cargo.toml index dd79240..b9b704b 100644 --- a/crates/aetheris-app/Cargo.toml +++ b/crates/aetheris-app/Cargo.toml @@ -80,3 +80,6 @@ vte4 = "0.10" [target.'cfg(all(unix, not(target_os = "macos")))'.dependencies] gettext-rs = { version = "0.7", features = ["gettext-system"] } + +[target.'cfg(target_os = "windows")'.build-dependencies] +embed-resource = "3.0.11" diff --git a/crates/aetheris-app/aetheris.rc b/crates/aetheris-app/aetheris.rc new file mode 100644 index 0000000..aefbef9 --- /dev/null +++ b/crates/aetheris-app/aetheris.rc @@ -0,0 +1 @@ +1 ICON "../../data/icons/windows/aetheris.ico" diff --git a/crates/aetheris-app/build.rs b/crates/aetheris-app/build.rs new file mode 100644 index 0000000..2dc451b --- /dev/null +++ b/crates/aetheris-app/build.rs @@ -0,0 +1,10 @@ +fn main() { + #[cfg(target_os = "windows")] + { + println!("cargo:rerun-if-changed=aetheris.rc"); + println!("cargo:rerun-if-changed=../../data/icons/windows/aetheris.ico"); + embed_resource::compile("aetheris.rc", embed_resource::NONE) + .manifest_required() + .unwrap(); + } +} diff --git a/crates/aetheris-app/src/app/ansi.rs b/crates/aetheris-app/src/app/ansi.rs index 4642eb8..e917d59 100644 --- a/crates/aetheris-app/src/app/ansi.rs +++ b/crates/aetheris-app/src/app/ansi.rs @@ -15,10 +15,6 @@ pub(super) fn setup_log_highlighting(buffer: >k::TextBuffer) { ensure_text_tag(buffer, "ansi-fg-white", &[("foreground", &"#f6f5f4")]); } -/// Appends `line` to `buffer`, translating ANSI SGR color/bold escape -/// codes (the ones frameworks like Logback/Spring Boot emit for console -/// output) into GTK text tags instead of leaving the raw escape bytes in -/// the log text. pub(super) fn insert_ansi_line(buffer: >k::TextBuffer, line: &str) { let mut iter = buffer.end_iter(); for (text, tags) in parse_ansi_line(line) { @@ -33,8 +29,6 @@ pub(super) fn insert_ansi_line(buffer: >k::TextBuffer, line: &str) { } } -/// Splits `line` into (text, active-tag-names) segments, dropping the -/// escape sequences themselves from the text. fn parse_ansi_line(line: &str) -> Vec<(String, Vec<&'static str>)> { let mut segments = Vec::new(); let mut current_text = String::new(); @@ -46,7 +40,7 @@ fn parse_ansi_line(line: &str) -> Vec<(String, Vec<&'static str>)> { current_text.push(ch); continue; } - chars.next(); // consume '[' + chars.next(); let mut code = String::new(); let mut terminator = None; diff --git a/crates/aetheris-app/src/main.rs b/crates/aetheris-app/src/main.rs index 0f855a9..dd93331 100644 --- a/crates/aetheris-app/src/main.rs +++ b/crates/aetheris-app/src/main.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + mod app; use relm4::RelmApp; @@ -5,7 +7,50 @@ use relm4::RelmApp; pub const APP_ID: &str = "org.luminusos.Aetheris"; pub const GETTEXT_PACKAGE: &str = APP_ID; +#[cfg(windows)] +fn configure_bundled_runtime() { + let Ok(exe) = std::env::current_exe() else { + return; + }; + let Some(root) = exe.parent().and_then(|bin| bin.parent()) else { + return; + }; + + let share = root.join("share"); + let vars = [ + ("XDG_DATA_DIRS", share.clone()), + ( + "GSETTINGS_SCHEMA_DIR", + share.join("glib-2.0").join("schemas"), + ), + ( + "GDK_PIXBUF_MODULEDIR", + root.join("lib") + .join("gdk-pixbuf-2.0") + .join("2.10.0") + .join("loaders"), + ), + ( + "GIO_MODULE_DIR", + root.join("lib").join("gio").join("modules"), + ), + ( + "SSL_CERT_FILE", + root.join("ssl").join("certs").join("ca-bundle.crt"), + ), + ]; + + for (key, value) in vars { + // SAFETY: called once, single-threaded, before GTK/GLib or any other + // thread reads the environment. + unsafe { std::env::set_var(key, value) }; + } +} + fn main() { + #[cfg(windows)] + configure_bundled_runtime(); + app::i18n::init(); tracing_subscriber::fmt() diff --git a/data/icons/windows/aetheris.ico b/data/icons/windows/aetheris.ico new file mode 100644 index 0000000..c829e95 Binary files /dev/null and b/data/icons/windows/aetheris.ico differ