Add Windows MSI installer with WiX v6 (#4847)

* Add Windows MSI installer with WiX v6

Build a dual-scope MSI installer that lets users choose per-user
(%LocalAppData%\Zellij) or per-machine (Program Files\Zellij) install,
with automatic PATH configuration and clean uninstall support.

- Custom UI based on stock WixToolset.UI.wixext dialogs to work around
  the long-standing WixUI_Advanced dual-scope bug (wixtoolset/issues#7137)
- Custom folder resolution CAs (SetFolderPerUser/SetFolderPerMachine)
- Embed application icon into zellij.exe via embed-resource
- Icon shown in Add/Remove Programs via ARPPRODUCTICON
- MSI build integrated into release.yml CI workflow

* Add MSI installer to no-web build and fix artifact naming

- Add MSI build steps to build-release-noweb job
- Add SHA256 checksums for MSI artifacts
- Pin WixToolset.UI.wixext to 6.0.2 to avoid v7 RC incompatibility
- Use -installer suffix after target in artifact names

---------

Co-authored-by: divens <[email protected]>
This commit is contained in:
divens
2026-03-16 12:38:38 +01:00
committed by GitHub
co-authored by divens
parent f59d6b9288
commit c4a572a884
11 changed files with 607 additions and 6 deletions
+102
View File
@@ -162,6 +162,57 @@ jobs:
asset_name: zellij-${{matrix.target}}.sha256sum
asset_content_type: text/plain
# ===== Windows MSI installer =====
- name: Install WiX v6
if: runner.os == 'Windows'
run: dotnet tool install --global wix
- name: Add WiX UI extension
if: runner.os == 'Windows'
run: wix extension add WixToolset.UI.wixext/6.0.2
- name: Build MSI installer
if: runner.os == 'Windows'
run: |
$version = (Select-String -Path Cargo.toml -Pattern '^version\s*=\s*"(.+)"' | Select-Object -First 1).Matches.Groups[1].Value
New-Item -ItemType Directory -Force -Path target\wix
wix build wix\main.wxs wix\Zellij.wxl `
-d Version=$version `
-arch x64 `
-ext WixToolset.UI.wixext `
-o target\wix\zellij-x86_64-pc-windows-msvc-installer.msi
- name: Create MSI checksum
if: runner.os == 'Windows'
id: make-checksum-msi
run: |
$checksum_name = "zellij-${{ matrix.target }}-installer.sha256sum"
$hash = (Get-FileHash "target\wix\zellij-${{ matrix.target }}-installer.msi" -Algorithm SHA256).Hash.ToLower()
"$hash zellij-${{ matrix.target }}-installer.msi" | Out-File -Encoding ascii $checksum_name
echo "checksum_name=$checksum_name" >> $env:GITHUB_OUTPUT
- name: Upload MSI installer
if: runner.os == 'Windows'
uses: actions/[email protected]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: target\wix\zellij-${{ matrix.target }}-installer.msi
asset_name: zellij-${{ matrix.target }}-installer.msi
asset_content_type: application/octet-stream
- name: Upload MSI checksum
if: runner.os == 'Windows'
uses: actions/[email protected]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: ${{ steps.make-checksum-msi.outputs.checksum_name }}
asset_name: zellij-${{ matrix.target }}-installer.sha256sum
asset_content_type: text/plain
build-release-noweb:
needs: create-release
name: build-release-noweb
@@ -301,3 +352,54 @@ jobs:
asset_path: ${{ steps.make-checksum-unix.outputs.checksum_name || steps.make-checksum-windows.outputs.checksum_name }}
asset_name: zellij-no-web-${{matrix.target}}.sha256sum
asset_content_type: text/plain
# ===== Windows MSI installer (no-web) =====
- name: Install WiX v6
if: runner.os == 'Windows'
run: dotnet tool install --global wix
- name: Add WiX UI extension
if: runner.os == 'Windows'
run: wix extension add WixToolset.UI.wixext/6.0.2
- name: Build MSI installer (no-web)
if: runner.os == 'Windows'
run: |
$version = (Select-String -Path Cargo.toml -Pattern '^version\s*=\s*"(.+)"' | Select-Object -First 1).Matches.Groups[1].Value
New-Item -ItemType Directory -Force -Path target\wix
wix build wix\main.wxs wix\Zellij.wxl `
-d Version=$version `
-arch x64 `
-ext WixToolset.UI.wixext `
-o target\wix\zellij-no-web-${{ matrix.target }}-installer.msi
- name: Create MSI checksum (no-web)
if: runner.os == 'Windows'
id: make-checksum-msi
run: |
$checksum_name = "zellij-no-web-${{ matrix.target }}-installer.sha256sum"
$hash = (Get-FileHash "target\wix\zellij-no-web-${{ matrix.target }}-installer.msi" -Algorithm SHA256).Hash.ToLower()
"$hash zellij-no-web-${{ matrix.target }}-installer.msi" | Out-File -Encoding ascii $checksum_name
echo "checksum_name=$checksum_name" >> $env:GITHUB_OUTPUT
- name: Upload MSI installer (no-web)
if: runner.os == 'Windows'
uses: actions/[email protected]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: target\wix\zellij-no-web-${{ matrix.target }}-installer.msi
asset_name: zellij-no-web-${{ matrix.target }}-installer.msi
asset_content_type: application/octet-stream
- name: Upload MSI checksum (no-web)
if: runner.os == 'Windows'
uses: actions/[email protected]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: ${{ steps.make-checksum-msi.outputs.checksum_name }}
asset_name: zellij-no-web-${{ matrix.target }}-installer.sha256sum
asset_content_type: text/plain
Generated
+130 -6
View File
@@ -965,6 +965,20 @@ version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "embed-resource"
version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55a075fc573c64510038d7ee9abc7990635863992f83ebc52c8b433b8411a02e"
dependencies = [
"cc",
"memchr",
"rustc_version",
"toml 0.9.6",
"vswhom",
"winreg",
]
[[package]]
name = "encode_unicode"
version = "0.3.6"
@@ -2908,6 +2922,15 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
"semver 1.0.27",
]
[[package]]
name = "rustix"
version = "0.37.28"
@@ -3085,6 +3108,12 @@ dependencies = [
"semver-parser",
]
[[package]]
name = "semver"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
[[package]]
name = "semver-parser"
version = "0.10.2"
@@ -3096,18 +3125,28 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.217"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.217"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
@@ -3136,6 +3175,15 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_spanned"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776"
dependencies = [
"serde_core",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
@@ -3838,6 +3886,45 @@ dependencies = [
"serde",
]
[[package]]
name = "toml"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae2a4cf385da23d1d53bc15cdfa5c2109e93d8d362393c801e87da2f72f0e201"
dependencies = [
"indexmap 2.7.1",
"serde_core",
"serde_spanned",
"toml_datetime",
"toml_parser",
"toml_writer",
"winnow",
]
[[package]]
name = "toml_datetime"
version = "0.7.5+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_parser"
version = "1.0.9+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4"
dependencies = [
"winnow",
]
[[package]]
name = "toml_writer"
version = "1.0.6+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607"
[[package]]
name = "tower"
version = "0.5.2"
@@ -4086,6 +4173,26 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[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 = "vte"
version = "0.10.1"
@@ -4655,6 +4762,22 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
[[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 = "winx"
version = "0.36.3"
@@ -4729,7 +4852,7 @@ dependencies = [
"anyhow",
"prost-build",
"serde_json",
"toml",
"toml 0.5.10",
"which",
"xflags",
"xshell",
@@ -4774,6 +4897,7 @@ dependencies = [
"anyhow",
"clap",
"dialoguer",
"embed-resource",
"humantime",
"insta",
"interprocess",
@@ -4865,7 +4989,7 @@ dependencies = [
"once_cell",
"prost",
"regex",
"semver",
"semver 0.11.0",
"serde",
"serde_json",
"signal-hook",
+3
View File
@@ -33,6 +33,9 @@ isahc = { workspace = true }
[target.'cfg(unix)'.dependencies]
nix = { workspace = true }
[target.'cfg(windows)'.build-dependencies]
embed-resource = "3"
[dev-dependencies]
insta = { version = "1.6.0", features = ["backtrace"] }
ssh2 = "0.9.1"
Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

+1
View File
@@ -0,0 +1 @@
1 ICON "assets\\logo128.ico"
+4
View File
@@ -6,4 +6,8 @@ fn main() {
if cfg!(target_os = "windows") && std::env::var("PROFILE").unwrap_or_default() != "release" {
println!("cargo:rustc-link-arg=/STACK:8388608");
}
// Embed the application icon into the Windows executable.
#[cfg(target_os = "windows")]
embed_resource::compile("assets/zellij.rc", embed_resource::NONE);
}
+15
View File
@@ -0,0 +1,15 @@
{\rtf1\ansi\deff0
{\fonttbl{\f0\fswiss Segoe UI;}}
\viewkind4\uc1
\pard\f0\fs20
\b MIT License\b0\par
\par
Copyright (c) 2020 Zellij contributors\par
\par
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \ldblquote Software\rdblquote ), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\par
\par
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\par
\par
THE SOFTWARE IS PROVIDED \ldblquote AS IS\rdblquote , WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\par
}
+5
View File
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="en-US">
<String Id="AdvancedWelcomeEulaDlgDescriptionPerMachine" Value="Click Install to install Zellij for all users. Click Advanced to choose install scope and directory." />
<String Id="AdvancedWelcomeEulaDlgDescriptionPerUser" Value="Click Install to install Zellij just for you. Click Advanced to choose install scope and directory." />
</WixLocalization>
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 451 KiB

+347
View File
@@ -0,0 +1,347 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Zellij MSI installer (WiX v6).
Custom UI built from individual WixToolset.UI.wixext stock dialogs plus a
custom welcome/EULA dialog (ZellijWelcomeDlg). We do NOT use the stock
WixUI_Advanced dialog set because:
- It does not support dual-scope packages (wixtoolset/issues#7137)
- Its AdvancedWelcomeEulaDlg cannot be overridden (not declared virtual)
- It includes a Print button and FeaturesDlg we don't need
Instead we reference stock dialogs individually (InstallScopeDlg,
InstallDirDlg, VerifyReadyDlg, etc.) and wire up navigation ourselves.
Dual-scope support:
Scope="perUserOrMachine" sets ALLUSERS=2, MSIINSTALLPERUSER=1.
Custom actions (SetFolderPerUser / SetFolderPerMachine) resolve
APPLICATIONFOLDER to the correct path based on scope selection.
The stock WixUI_Advanced folder CAs are broken for this scenario.
Dialog flow:
New install: ZellijWelcomeDlg → [Advanced] → InstallScopeDlg
→ InstallDirDlg → VerifyReadyDlg
Quick install: ZellijWelcomeDlg → [Install] (per-user, no UAC)
Maintenance: MaintenanceWelcomeDlg → MaintenanceTypeDlg → VerifyReadyDlg
Build:
wix build wix\main.wxs wix\Zellij.wxl -d Version=X.Y.Z -arch x64
-ext WixToolset.UI.wixext -o target\wix\zellij-x86_64-pc-windows-msvc.msi
-->
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Package Name="Zellij"
Manufacturer="Zellij Contributors"
Version="$(var.Version)"
UpgradeCode="BEE7A897-6DE0-4243-9977-5924C8CDB391"
Scope="perUserOrMachine"
InstallerVersion="500">
<!-- Properties used by stock InstallScopeDlg and InstallDirDlg -->
<Property Id="ApplicationFolderName" Value="Zellij" />
<Property Id="WixAppFolder" Value="WixPerUserFolder" />
<Property Id="WIXUI_INSTALLDIR" Value="APPLICATIONFOLDER" />
<!-- Icon shown in Add/Remove Programs -->
<Icon Id="ZellijIcon" SourceFile="assets\logo128.ico" />
<Property Id="ARPPRODUCTICON" Value="ZellijIcon" />
<!-- Auto-remove previous versions -->
<MajorUpgrade DowngradeErrorMessage="A newer version of Zellij is already installed." />
<!-- Embed the .cab inside the MSI -->
<MediaTemplate EmbedCab="yes" />
<!-- ===== Directory tree ===== -->
<StandardDirectory Id="ProgramFilesFolder">
<Directory Id="APPLICATIONFOLDER" Name="Zellij" />
</StandardDirectory>
<!-- ===== Custom actions for folder resolution =====
The stock WixUI_Advanced CAs (WixSetPerUserFolder / WixSetPerMachineFolder)
resolve incorrectly under Scope="perUserOrMachine" because WixPerMachineFolder
is evaluated before the user selects a scope. We bypass them entirely and set
APPLICATIONFOLDER to explicit paths based on MSIINSTALLPERUSER. -->
<CustomAction Id="SetFolderPerUser"
Property="APPLICATIONFOLDER"
Value="[LocalAppDataFolder]Zellij" />
<CustomAction Id="SetFolderPerMachine"
Property="APPLICATIONFOLDER"
Value="[ProgramFilesFolder]Zellij" />
<!-- Schedule in both UI and Execute sequences so the correct path is set
even after UAC elevation re-launches the server process. -->
<InstallExecuteSequence>
<Custom Action="SetFolderPerUser" Before="CostFinalize"
Condition="MSIINSTALLPERUSER = 1" />
<Custom Action="SetFolderPerMachine" Before="CostFinalize"
Condition="NOT MSIINSTALLPERUSER" />
</InstallExecuteSequence>
<InstallUISequence>
<Custom Action="SetFolderPerUser" Before="CostFinalize"
Condition="MSIINSTALLPERUSER = 1" />
<Custom Action="SetFolderPerMachine" Before="CostFinalize"
Condition="NOT MSIINSTALLPERUSER" />
</InstallUISequence>
<!-- ===== Components =====
Three components: binary (always), plus two conditional PATH components.
ALLUSERS is set by InstallScopeDlg: 1 for per-machine, empty for per-user.
Default (quick install without scope dialog): ALLUSERS=2, which is <> 1,
so path_user activates — correct for the per-user default. -->
<Component Id="binary"
Directory="APPLICATIONFOLDER"
Guid="BC9D18AE-17CA-4790-ADB0-C64530DEDE57">
<File Id="zellij_exe"
Source="target\release\zellij.exe"
KeyPath="yes" />
</Component>
<Component Id="path_machine"
Directory="APPLICATIONFOLDER"
Guid="85C2CC85-8379-4C27-8337-A90EAD9AC265"
Condition="ALLUSERS = 1">
<RegistryValue Root="HKLM"
Key="Software\Zellij"
Name="InstallPath"
Type="string"
Value="[APPLICATIONFOLDER]"
KeyPath="yes" />
<Environment Id="PATH_machine"
Name="PATH"
Value="[APPLICATIONFOLDER]"
Permanent="no"
Part="last"
Action="set"
System="yes" />
</Component>
<Component Id="path_user"
Directory="APPLICATIONFOLDER"
Guid="EED3D01E-8FBC-4F3D-A3E5-DF55939970CD"
Condition="ALLUSERS &lt;&gt; 1">
<RegistryValue Root="HKCU"
Key="Software\Zellij"
Name="InstallPath"
Type="string"
Value="[APPLICATIONFOLDER]"
KeyPath="yes" />
<Environment Id="PATH_user"
Name="PATH"
Value="[APPLICATIONFOLDER]"
Permanent="no"
Part="last"
Action="set"
System="no" />
</Component>
<!-- ===== Feature ===== -->
<Feature Id="MainFeature" Title="Zellij" Level="1">
<ComponentRef Id="binary" />
<ComponentRef Id="path_machine" />
<ComponentRef Id="path_user" />
</Feature>
<!-- ===== Custom UI =====
We define our own UI element instead of using <ui:WixUI Id="WixUI_Advanced" />
because WiX v6 does not allow overriding extension dialogs (they are not
declared virtual). We reference individual stock dialogs from
WixToolset.UI.wixext and provide our own ZellijWelcomeDlg and navigation. -->
<UI>
<TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="8" />
<TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="12" />
<TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="9" Bold="yes" />
<TextStyle Id="WixUI_Font_Emphasized" FaceName="Tahoma" Size="9" Bold="yes" />
<Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />
<!-- Individual stock dialogs from WixToolset.UI.wixext -->
<DialogRef Id="BrowseDlg" />
<DialogRef Id="DiskCostDlg" />
<DialogRef Id="ErrorDlg" />
<DialogRef Id="ExitDialog" />
<DialogRef Id="FatalError" />
<DialogRef Id="FilesInUse" />
<DialogRef Id="InstallDirDlg" />
<DialogRef Id="InstallScopeDlg" />
<DialogRef Id="MaintenanceTypeDlg" />
<DialogRef Id="MaintenanceWelcomeDlg" />
<DialogRef Id="MsiRMFilesInUse" />
<DialogRef Id="PrepareDlg" />
<DialogRef Id="ProgressDlg" />
<DialogRef Id="ResumeDlg" />
<DialogRef Id="UserExit" />
<DialogRef Id="VerifyReadyDlg" />
<DialogRef Id="WaitForCostingDlg" />
<!-- ==========================================
ZellijWelcomeDlg — custom welcome + EULA dialog.
Based on the stock AdvancedWelcomeEulaDlg from WixToolset.UI.wixext
(src/ext/UI/wixlib/AdvancedWelcomeEulaDlg.wxs) with Print button removed.
Localization strings (!(loc.XXX)) come from the extension's WXL files;
overrides are in wix/Zellij.wxl.
========================================== -->
<Dialog Id="ZellijWelcomeDlg" Width="370" Height="270"
Title="!(loc.AdvancedWelcomeEulaDlg_Title)">
<Control Id="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="44"
TabSkip="no" Text="!(loc.AdvancedWelcomeEulaDlgBannerBitmap)" />
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="373" Height="0" />
<Control Id="Title" Type="Text" X="20" Y="10" Width="300" Height="24"
Transparent="yes" NoPrefix="yes"
Text="!(loc.AdvancedWelcomeEulaDlgTitle)" />
<Control Id="DescriptionPerMachine" Type="Text" X="20" Y="202" Width="330" Height="31"
Transparent="yes" NoPrefix="yes" Hidden="yes"
Text="!(loc.AdvancedWelcomeEulaDlgDescriptionPerMachine)"
ShowCondition="ALLUSERS" />
<Control Id="DescriptionPerUser" Type="Text" X="20" Y="202" Width="330" Height="31"
Transparent="yes" NoPrefix="yes" Hidden="yes"
Text="!(loc.AdvancedWelcomeEulaDlgDescriptionPerUser)"
ShowCondition="NOT ALLUSERS" />
<Control Id="LicenseAcceptedCheckBox" Type="CheckBox" X="20" Y="180" Width="226" Height="18"
CheckBoxValue="1" Property="LicenseAccepted"
Text="!(loc.WelcomeEulaDlgLicenseAcceptedCheckBox)" />
<Control Id="LicenseText" Type="ScrollableText" X="20" Y="55" Width="330" Height="121"
Sunken="yes" TabSkip="no">
<Text SourceFile="!(wix.WixUILicenseRtf)" />
</Control>
<Control Id="Advanced" Type="PushButton" X="156" Y="243" Width="56" Height="17"
Text="!(loc.AdvancedWelcomeEulaDlgAdvanced)"
DisableCondition="LicenseAccepted &lt;&gt; &quot;1&quot;"
EnableCondition="LicenseAccepted = &quot;1&quot;" />
<Control Id="Install" Type="PushButton" ElevationShield="yes"
X="212" Y="243" Width="80" Height="17" Default="yes"
Text="!(loc.AdvancedWelcomeEulaDlgInstall)" Hidden="yes"
DisableCondition="LicenseAccepted &lt;&gt; &quot;1&quot;"
EnableCondition="LicenseAccepted = &quot;1&quot;"
ShowCondition="ALLUSERS">
<Publish Event="SpawnWaitDialog" Value="WaitForCostingDlg"
Condition="!(wix.WixUICostingPopupOptOut) OR CostingComplete = 1" />
<Publish Event="EndDialog" Value="Return"
Condition="OutOfDiskSpace &lt;&gt; 1" />
<Publish Event="SpawnDialog" Value="OutOfRbDiskDlg"
Condition="OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND (PROMPTROLLBACKCOST=&quot;P&quot; OR NOT PROMPTROLLBACKCOST)" />
<Publish Event="EndDialog" Value="Return"
Condition="OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND PROMPTROLLBACKCOST=&quot;D&quot;" />
<Publish Event="EnableRollback" Value="False"
Condition="OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND PROMPTROLLBACKCOST=&quot;D&quot;" />
<Publish Event="SpawnDialog" Value="OutOfDiskDlg"
Condition="(OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 1) OR (OutOfDiskSpace = 1 AND PROMPTROLLBACKCOST=&quot;F&quot;)" />
</Control>
<Control Id="InstallNoShield" Type="PushButton" ElevationShield="no"
X="212" Y="243" Width="80" Height="17" Default="yes"
Text="!(loc.AdvancedWelcomeEulaDlgInstall)" Hidden="yes"
DisableCondition="LicenseAccepted &lt;&gt; &quot;1&quot;"
EnableCondition="LicenseAccepted = &quot;1&quot;"
ShowCondition="NOT ALLUSERS">
<Publish Event="SpawnWaitDialog" Value="WaitForCostingDlg"
Condition="!(wix.WixUICostingPopupOptOut) OR CostingComplete = 1" />
<Publish Event="EndDialog" Value="Return"
Condition="OutOfDiskSpace &lt;&gt; 1" />
<Publish Event="SpawnDialog" Value="OutOfRbDiskDlg"
Condition="OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND (PROMPTROLLBACKCOST=&quot;P&quot; OR NOT PROMPTROLLBACKCOST)" />
<Publish Event="EndDialog" Value="Return"
Condition="OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND PROMPTROLLBACKCOST=&quot;D&quot;" />
<Publish Event="EnableRollback" Value="False"
Condition="OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND PROMPTROLLBACKCOST=&quot;D&quot;" />
<Publish Event="SpawnDialog" Value="OutOfDiskDlg"
Condition="(OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 1) OR (OutOfDiskSpace = 1 AND PROMPTROLLBACKCOST=&quot;F&quot;)" />
</Control>
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17"
Cancel="yes" Text="!(loc.WixUICancel)">
<Publish Event="SpawnDialog" Value="CancelDlg" />
</Control>
</Dialog>
<!-- ==========================================
Navigation: new install flow.
Scope dialog sets ALLUSERS, MSIINSTALLPERUSER, and fires our
folder CAs so InstallDirDlg shows the correct default path.
========================================== -->
<Publish Dialog="ZellijWelcomeDlg" Control="Advanced"
Event="NewDialog" Value="InstallScopeDlg" />
<Publish Dialog="InstallScopeDlg" Control="Back"
Event="NewDialog" Value="ZellijWelcomeDlg" />
<Publish Dialog="InstallScopeDlg" Control="Next"
Property="WixAppFolder" Value="WixPerUserFolder" Order="1"
Condition="NOT Privileged" />
<Publish Dialog="InstallScopeDlg" Control="Next"
Property="ALLUSERS" Value="{}" Order="2"
Condition="WixAppFolder = &quot;WixPerUserFolder&quot;" />
<Publish Dialog="InstallScopeDlg" Control="Next"
Property="ALLUSERS" Value="1" Order="3"
Condition="WixAppFolder = &quot;WixPerMachineFolder&quot;" />
<Publish Dialog="InstallScopeDlg" Control="Next"
Property="MSIINSTALLPERUSER" Value="1" Order="4"
Condition="WixAppFolder = &quot;WixPerUserFolder&quot;" />
<Publish Dialog="InstallScopeDlg" Control="Next"
Property="MSIINSTALLPERUSER" Value="{}" Order="5"
Condition="WixAppFolder = &quot;WixPerMachineFolder&quot;" />
<Publish Dialog="InstallScopeDlg" Control="Next"
Event="DoAction" Value="SetFolderPerUser" Order="6"
Condition="WixAppFolder = &quot;WixPerUserFolder&quot;" />
<Publish Dialog="InstallScopeDlg" Control="Next"
Event="DoAction" Value="SetFolderPerMachine" Order="7"
Condition="WixAppFolder = &quot;WixPerMachineFolder&quot;" />
<Publish Dialog="InstallScopeDlg" Control="Next"
Event="NewDialog" Value="InstallDirDlg" Order="8" />
<Publish Dialog="InstallDirDlg" Control="Back"
Event="NewDialog" Value="InstallScopeDlg" />
<Publish Dialog="InstallDirDlg" Control="Next"
Event="SetTargetPath" Value="[WIXUI_INSTALLDIR]" Order="1" />
<Publish Dialog="InstallDirDlg" Control="Next"
Event="NewDialog" Value="VerifyReadyDlg" Order="2" />
<Publish Dialog="InstallDirDlg" Control="ChangeFolder"
Property="_BrowseProperty" Value="[WIXUI_INSTALLDIR]" Order="1" />
<Publish Dialog="InstallDirDlg" Control="ChangeFolder"
Event="SpawnDialog" Value="BrowseDlg" Order="2" />
<Publish Dialog="BrowseDlg" Control="OK"
Event="SetTargetPath" Value="[_BrowseProperty]" Order="1" />
<Publish Dialog="BrowseDlg" Control="OK"
Event="EndDialog" Value="Return" Order="2" />
<Publish Dialog="VerifyReadyDlg" Control="Back"
Event="NewDialog" Value="InstallDirDlg" Order="1"
Condition="NOT Installed" />
<Publish Dialog="VerifyReadyDlg" Control="Back"
Event="NewDialog" Value="MaintenanceTypeDlg" Order="2"
Condition="Installed AND NOT PATCH" />
<Publish Dialog="ExitDialog" Control="Finish"
Event="EndDialog" Value="Return" Order="999" />
<!-- Navigation: maintenance (repair/uninstall) -->
<Publish Dialog="MaintenanceWelcomeDlg" Control="Next"
Event="NewDialog" Value="MaintenanceTypeDlg" />
<Publish Dialog="MaintenanceTypeDlg" Control="RepairButton"
Event="NewDialog" Value="VerifyReadyDlg" />
<Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton"
Event="NewDialog" Value="VerifyReadyDlg" />
<Publish Dialog="MaintenanceTypeDlg" Control="Back"
Event="NewDialog" Value="MaintenanceWelcomeDlg" />
</UI>
<!-- Show our welcome dialog for new installs -->
<InstallUISequence>
<Show Dialog="ZellijWelcomeDlg" Before="ProgressDlg"
Condition="NOT Installed" />
</InstallUISequence>
<!-- WixUI_Common provides shared error/progress/cancel dialogs -->
<UIRef Id="WixUI_Common" />
<!-- These variables were originally defined in the WixUI_Advanced fragment
which we no longer use. InstallScopeDlg references them to decide
which radio buttons to show. -->
<WixVariable Id="WixUISupportPerUser" Value="1" />
<WixVariable Id="WixUISupportPerMachine" Value="1" />
<WixVariable Id="WixUILicenseRtf" Value="wix\License.rtf" />
<WixVariable Id="WixUIBannerBmp" Value="wix\banner.bmp" />
<WixVariable Id="WixUIDialogBmp" Value="wix\dialog.bmp" />
</Package>
</Wix>