Files
2026-11-04 14:29:22 -06:00

988 lines
30 KiB
NSIS

; =============================================================================================
; LiveWall installer
;
; A dark, branded setup that matches the app's own look (Themes/Dark.xaml). No Modern UI wizard:
; every page is drawn by hand with nsDialogs so the whole window is the app's palette, and the
; stock Next/Back/Cancel row is replaced by flat buttons.
;
; Layout notes (they matter if you edit this):
; * the page measures the window at run time ($PU x $PV) and every control is placed as a
; percentage of that (see LayoutVars), so the layout keeps its proportions at any DPI;
; * SetCtlColors takes colours as 0xRRGGBB;
; * the wizard dialog itself is painted with SetCtlColors $HWNDPARENT, and the page dialog is
; stretched over it (MeasurePage) and painted too. Do NOT add a full-page background label:
; it ends up above the controls and swallows every click;
; * the install runs in the page's leave function, so there is no InstFiles page at all and the
; window never flashes a second, differently themed screen;
; * discrete sizes (the app mark, the tick boxes, the button insets) are plain pixels.
;
; Per-user install (no UAC): %LOCALAPPDATA%\Programs\LiveWall, shortcuts and the uninstall entry in
; HKCU. Build with installer/build-installer.sh, which passes the version in from LiveWall.csproj.
;
; Upgrade handling: an existing installation is detected through HKCU\Software\LiveWall
; ("InstallDir" + "Version"). An older version is updated in place and keeps %APPDATA%\LiveWall;
; the same version asks before reinstalling, a newer one asks before downgrading.
; =============================================================================================
Unicode true
!include "LogicLib.nsh"
!include "x64.nsh"
!include "FileFunc.nsh"
!include "nsDialogs.nsh"
!include "WinMessages.nsh"
!define APP_NAME "LiveWall"
!define APP_EXE "LiveWall.exe"
!define APP_PUBLISHER "LiveWall"
!define APP_REGKEY "Software\LiveWall"
!define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\LiveWall"
!define DOTNET_URL "https://dotnet.microsoft.com/download/dotnet/8.0/runtime/desktop"
!define APP_TAGLINE "Animated wallpapers for Windows 11"
; ------------------------------------------------------------------ palette (Themes/Dark.xaml)
!define C_BG "0x0E1116" ; BgWindow
!define C_CARD "0x151A22" ; BgCard
!define C_FIELD "0x1B222C" ; BgField
!define C_HOVER "0x232C38" ; BgHover
!define C_LINE "0x242C38" ; BorderBrushSoft
!define C_TEXT "0xE7EBF3" ; TextBrush
!define C_DIM "0x8C95A5" ; TextDim
!define C_ACCENT "0x6C8CFF" ; AccentBrush (matches the icon's indigo)
!define C_ACCENT_HI "0x865EF2" ; icon gradient end violet
!define C_GREEN "0x35D0A5" ; GreenBrush (the icon's dot)
!define C_ON_ACCENT "0x0E1116"
!ifndef VERSION
!define VERSION "1.7.0"
!endif
!ifndef VERSION4
!define VERSION4 "1.7.0.0"
!endif
!ifndef SOURCE_DIR
!define SOURCE_DIR "..\prebuilt"
!endif
; Output name suffix, e.g. "-zlib" when building the alternate installer used for antivirus triage.
!ifndef OUT_SUFFIX
!define OUT_SUFFIX ""
!endif
Name "${APP_NAME} ${VERSION}"
OutFile "..\${APP_NAME}-Setup-${VERSION}${OUT_SUFFIX}.exe"
InstallDir "$LOCALAPPDATA\Programs\${APP_NAME}"
InstallDirRegKey HKCU "${APP_REGKEY}" "InstallDir"
Icon "..\LiveWall\Assets\app.ico"
UninstallIcon "..\LiveWall\Assets\app.ico"
RequestExecutionLevel user
BrandingText " "
; ------------------------------------------------------------------ compression
;
; Default: solid LZMA, the smallest installer. Some heuristic engines (VBA32 "TScope", Trapmine's
; ML score) react to solid NSIS archives, so the compressor can be switched at build time:
;
; makensis /DCOMPRESS=zlib (no solid block, lowest false-positive surface, bigger file)
; makensis /DCOMPRESS=lzma (LZMA, one stream per file)
; makensis /DCOMPRESS=lzma-solid (default)
;
!ifndef COMPRESS
!define COMPRESS "lzma-solid"
!endif
!if "${COMPRESS}" == "zlib"
SetCompressor zlib
!else if "${COMPRESS}" == "lzma"
SetCompressor lzma
SetCompressorDictSize 32
!else
SetCompressor /SOLID lzma
SetCompressorDictSize 32
!endif
; Sign with Authenticode when a signing command is supplied (see installer\sign.ps1):
; makensis /DSIGNTOOL='signtool sign /fd sha256 /tr http://timestamp.digicert.com /td sha256 $%1'
; NSIS then signs the generated uninstaller and the finished installer.
!ifdef SIGNTOOL
SignTool signtool "${SIGNTOOL}"
!endif
VIProductVersion "${VERSION4}"
VIAddVersionKey "ProductName" "${APP_NAME}"
VIAddVersionKey "FileDescription" "${APP_NAME} installer"
VIAddVersionKey "FileVersion" "${VERSION4}"
VIAddVersionKey "ProductVersion" "${VERSION}"
VIAddVersionKey "CompanyName" "${APP_PUBLISHER}"
VIAddVersionKey "LegalCopyright" "${APP_PUBLISHER}"
VIAddVersionKey "LegalTrademarks" "${APP_NAME}"
VIAddVersionKey "OriginalFilename" "${APP_NAME}-Setup-${VERSION}.exe"
VIAddVersionKey "Comments" "Live wallpaper for Windows 11 - no bundled offers, no telemetry, installs for the current user only."
; ------------------------------------------------------------------ pages
Page custom SetupPageShow SetupPageLeave
Page custom DonePageShow DonePageLeave
UninstPage custom un.ConfirmPageShow un.ConfirmPageLeave
UninstPage instfiles
; ------------------------------------------------------------------ state
Var InstalledDir
Var InstalledVersion
Var InstalledScore
Var ThisScore
Var UpgradeFrom ; non-empty => this run updates an existing copy
Var DeleteSettings
Var OptDesktop
Var OptStartup
Var OptStartApp
Var OptOpenFolder
Var StartAfter
Var PageDlg ; the page dialog returned by nsDialogs::Create
Var X0
Var X1
Var X_RIGHT
Var X_BTN1
Var X_BTN2
Var W_TEXT
Var W_RULE
Var W_LEFT
Var W_BTN
Var Y_ICON
Var Y_TITLE
Var Y_TAG
Var Y_RULE
Var Y_NOTE
Var Y_VERSION
Var Y_PATH
Var Y_OPT1
Var Y_OPT2
Var Y_OPT3
Var Y_FOOT
Var Y_BTN
Var H_TITLE
Var H_LINE
Var H_BTN
Var STRIP_H
Var UpgradeNote
Var PathLabel
Var InstallBtn
Var FinishBtn
Var Bx1
Var Tx1
Var S1
Var Bx2
Var Tx2
Var S2
Var Bx3
Var Tx3
Var S3
Var Bx4
Var Tx4
Var S4
Var Bx5
Var Tx5
Var S5
; ------------------------------------------------------------------ layout helpers
;
; The page is measured at run time and every control is placed as a percentage of that size, so the
; layout keeps its proportions on high-DPI screens (the font sizes follow the system DPI on their own).
; $PU / $PV hold the client size of the wizard window in pixels.
Var PU
Var PV
Var TmpX
Var TmpY
Var TmpW
!macro Pct OUT BASE PERCENT
IntOp ${OUT} ${BASE} * ${PERCENT}
IntOp ${OUT} ${OUT} / 100
!macroend
; Sizes the page over the whole wizard client area and records $PU / $PV.
Function MeasurePage
System::Call '*(i,i,i,i) i .r5'
System::Call 'user32::GetClientRect(p $HWNDPARENT, p r5) i .r4'
System::Call '*$5(i .r6, i .r7, i .r8, i .r9)'
StrCpy $PU $8
StrCpy $PV $9
; SWP_NOZORDER|SWP_NOACTIVATE: pin the page to the client origin and stretch it
System::Call 'user32::SetWindowPos(p $PageDlg, p 0, i 0, i 0, i $8, i $9, i 0x14)'
FunctionEnd
; ------------------------------------------------------------------ helpers
; "1.6.3" / "1.6.3.0" / "1.6" -> a comparable integer (major*1e6 + minor*1e3 + patch).
; Input: $R0 Output: $R9 (all other registers restored)
Function VersionScore
Push $0
Push $1
Push $2
Push $3
Push $4
StrCpy $1 0 ; accumulated score
StrCpy $2 0 ; current component value
StrCpy $3 0 ; character index
StrCpy $4 0 ; components seen
vs_loop:
StrCpy $0 $R0 1 $3
${If} $0 == ""
Goto vs_end
${EndIf}
${If} $0 == "."
${If} $4 >= 2
Goto vs_end ; ignore anything after the third component
${EndIf}
IntOp $4 $4 + 1
IntOp $1 $1 * 1000
IntOp $1 $1 + $2
StrCpy $2 0
Goto vs_next
${EndIf}
IntOp $0 $0 + 0 ; digit value (0 for anything that is not a number)
IntOp $2 $2 * 10
IntOp $2 $2 + $0
vs_next:
IntOp $3 $3 + 1
Goto vs_loop
vs_end:
IntOp $1 $1 * 1000
IntOp $1 $1 + $2
; pad missing components ("1.6" must sort below "1.6.1")
${If} $4 < 2
IntOp $1 $1 * 1000
${EndIf}
StrCpy $R9 $1
Pop $4
Pop $3
Pop $2
Pop $1
Pop $0
FunctionEnd
; Finds an existing installation and decides what to do about it.
Function DetectExisting
StrCpy $InstalledDir ""
StrCpy $InstalledVersion ""
StrCpy $UpgradeFrom ""
ReadRegStr $InstalledDir HKCU "${APP_REGKEY}" "InstallDir"
ReadRegStr $InstalledVersion HKCU "${APP_REGKEY}" "Version"
; No registry entry (older installer or a copied folder): look in the default location.
${If} $InstalledDir == ""
${If} ${FileExists} "$LOCALAPPDATA\Programs\${APP_NAME}\${APP_EXE}"
StrCpy $InstalledDir "$LOCALAPPDATA\Programs\${APP_NAME}"
${EndIf}
${EndIf}
; Installed, but the version was never recorded: treat it as an unknown older build.
${If} $InstalledDir != ""
${AndIf} $InstalledVersion == ""
StrCpy $InstalledVersion "0.0.0"
${EndIf}
${If} $InstalledDir == ""
Return ; nothing installed - a fresh install
${EndIf}
StrCpy $R0 "$InstalledVersion"
Call VersionScore
StrCpy $InstalledScore $R9
StrCpy $R0 "${VERSION}"
Call VersionScore
StrCpy $ThisScore $R9
${If} $InstalledScore >= $ThisScore
Return ; same or newer - handled by the caller
${EndIf}
StrCpy $UpgradeFrom "$InstalledVersion"
StrCpy $INSTDIR "$InstalledDir" ; never install an update beside the old copy
FunctionEnd
; ------------------------------------------------------------------ chrome
; Paints the wizard window itself, so nothing around the page is left white, and hides the stock
; Next / Back / Cancel row (the pages bring their own flat buttons; Enter and Esc still work).
Function PaintChrome
SetCtlColors $HWNDPARENT "${C_TEXT}" "${C_BG}"
GetDlgItem $0 $HWNDPARENT 1
ShowWindow $0 ${SW_HIDE}
GetDlgItem $0 $HWNDPARENT 2
ShowWindow $0 ${SW_HIDE}
GetDlgItem $0 $HWNDPARENT 3
ShowWindow $0 ${SW_HIDE}
FunctionEnd
; One flat surface: background, the icon's gradient strip, the app icon, the wordmark and a rule.
!macro PageChrome
; The page dialog itself is the background. A full-page label would sit above the controls and
; swallow every click, which is why there is none.
SetCtlColors $PageDlg "${C_TEXT}" "${C_BG}"
; the indigo-to-violet gradient of the app icon, as two flat halves
${NSD_CreateLabel} 0 0 50% $STRIP_H ""
Pop $0
SetCtlColors $0 "${C_ACCENT}" "${C_ACCENT}"
${NSD_CreateLabel} 50% 0 50% $STRIP_H ""
Pop $0
SetCtlColors $0 "${C_ACCENT_HI}" "${C_ACCENT_HI}"
; The app mark, drawn from flat blocks so it stays pixel-crisp at any DPI: the icon's
; indigo-to-violet square in two halves, with its green dot at the lower right.
StrCpy $TmpX $X0
IntOp $TmpX $TmpX + 16
StrCpy $TmpY $Y_ICON
IntOp $TmpY $TmpY + 24
${NSD_CreateLabel} $X0 $Y_ICON 16 32 ""
Pop $0
SetCtlColors $0 "${C_ACCENT}" "${C_ACCENT}"
${NSD_CreateLabel} $TmpX $Y_ICON 16 32 ""
Pop $0
SetCtlColors $0 "${C_ACCENT_HI}" "${C_ACCENT_HI}"
${NSD_CreateLabel} $TmpX $TmpY 8 8 ""
Pop $0
SetCtlColors $0 "${C_GREEN}" "${C_GREEN}"
${NSD_CreateLabel} $X1 $Y_TITLE $W_TEXT $H_TITLE "${APP_NAME}"
Pop $0
SetCtlColors $0 "${C_TEXT}" "${C_BG}"
CreateFont $1 "Segoe UI" 15 700
SendMessage $0 ${WM_SETFONT} $1 1
${NSD_CreateLabel} $X1 $Y_TAG $W_TEXT $H_LINE "${APP_TAGLINE}"
Pop $0
SetCtlColors $0 "${C_DIM}" "${C_BG}"
CreateFont $1 "Segoe UI" 9 400
SendMessage $0 ${WM_SETFONT} $1 1
${NSD_CreateLabel} $X0 $Y_RULE $W_RULE 1 "${C_LINE}"
Pop $0
SetCtlColors $0 "${C_LINE}" "${C_LINE}"
!macroend
; All of the geometry in one place, as percentages of the page size.
!macro LayoutVars
!insertmacro Pct $X0 $PU 4
IntOp $X1 $X0 + 46
IntOp $W_TEXT $PU - $X1
IntOp $W_TEXT $W_TEXT - $X0
!insertmacro Pct $W_RULE $PU 92
!insertmacro Pct $W_LEFT $PU 60
!insertmacro Pct $X_RIGHT $PU 78
!insertmacro Pct $Y_ICON $PV 5
!insertmacro Pct $Y_TITLE $PV 4
!insertmacro Pct $H_TITLE $PV 10
!insertmacro Pct $Y_TAG $PV 15
!insertmacro Pct $H_LINE $PV 6
!insertmacro Pct $Y_RULE $PV 25
!insertmacro Pct $Y_NOTE $PV 28
!insertmacro Pct $Y_VERSION $PV 36
!insertmacro Pct $Y_PATH $PV 42
!insertmacro Pct $Y_OPT1 $PV 51
!insertmacro Pct $Y_OPT2 $PV 59
!insertmacro Pct $Y_OPT3 $PV 67
!insertmacro Pct $Y_FOOT $PV 88
!insertmacro Pct $Y_BTN $PV 85
!insertmacro Pct $H_BTN $PV 9
!insertmacro Pct $W_BTN $PU 16
!insertmacro Pct $X_BTN2 $PU 62
!insertmacro Pct $X_BTN1 $PU 80
!macroend
; A tick box: a flat square plus a label, both clickable. $BX / $TX / $ST are the box handle,
; the label handle and the state (0/1). PaintOption repaints it, the toggles flip it.
!macro DefPaintOption PFX
Function ${PFX}PaintOption
${If} $R5 == 1
SetCtlColors $R3 "${C_ON_ACCENT}" "${C_ACCENT}"
${NSD_SetText} $R3 "✓"
${Else}
SetCtlColors $R3 "${C_TEXT}" "${C_HOVER}"
${NSD_SetText} $R3 ""
${EndIf}
FunctionEnd
!macroend
!insertmacro DefPaintOption ""
!insertmacro DefPaintOption "un."
!macro ToggleClick FUNC PAINT BX TX ST
Function ${FUNC}
StrCpy $R3 ${BX}
StrCpy $R4 ${TX}
StrCpy $R5 ${ST}
${If} $R5 == 1
StrCpy $R5 0
${Else}
StrCpy $R5 1
${EndIf}
Call ${PAINT}
StrCpy ${ST} $R5
FunctionEnd
!macroend
; Creates the square and the label of one option; the label sits $TXOFF to the right.
!macro MakeOption BX TX FUNC X Y TEXT TXOFF
${NSD_CreateLabel} ${X} ${Y} 12 12 ""
Pop ${BX}
CreateFont $1 "Segoe UI Symbol" 8 700
SendMessage ${BX} ${WM_SETFONT} $1 1
SetCtlColors ${BX} "${C_TEXT}" "${C_HOVER}"
${NSD_OnClick} ${BX} ${FUNC}
StrCpy $TmpX ${X}
IntOp $TmpX $TmpX + ${TXOFF}
StrCpy $TmpY ${Y}
IntOp $TmpY $TmpY + 1
${NSD_CreateLabel} $TmpX $TmpY 330 16 "${TEXT}"
Pop ${TX}
SetCtlColors ${TX} "${C_TEXT}" "${C_BG}"
CreateFont $1 "Segoe UI" 9 400
SendMessage ${TX} ${WM_SETFONT} $1 1
${NSD_OnClick} ${TX} ${FUNC}
!macroend
!insertmacro ToggleClick Toggle1 PaintOption $Bx1 $Tx1 $S1
!insertmacro ToggleClick Toggle2 PaintOption $Bx2 $Tx2 $S2
!insertmacro ToggleClick Toggle3 PaintOption $Bx3 $Tx3 $S3
!insertmacro ToggleClick Toggle4 PaintOption $Bx4 $Tx4 $S4
!insertmacro ToggleClick Toggle5 PaintOption $Bx5 $Tx5 $S5
!insertmacro ToggleClick un.Toggle1 un.PaintOption $Bx1 $Tx1 $S1
; A flat button: a coloured block with a padded caption on top, both clickable.
!macro MakeButton BLK TXT FUNC X Y W H TEXT BGFG FG WEIGHT
${NSD_CreateLabel} ${X} ${Y} ${W} ${H} ""
Pop ${BLK}
SetCtlColors ${BLK} ${BGFG} ${BGFG}
${NSD_OnClick} ${BLK} ${FUNC}
StrCpy $TmpX ${X}
IntOp $TmpX $TmpX + 9
StrCpy $TmpY ${H}
IntOp $TmpY $TmpY - 15
IntOp $TmpY $TmpY / 2
IntOp $TmpY $TmpY + ${Y}
StrCpy $TmpW ${W}
IntOp $TmpW $TmpW - 18
${NSD_CreateLabel} $TmpX $TmpY $TmpW 15 "${TEXT}"
Pop ${TXT}
SetCtlColors ${TXT} ${FG} ${BGFG}
CreateFont $1 "Segoe UI" 9 ${WEIGHT}
SendMessage ${TXT} ${WM_SETFONT} $1 1
${NSD_OnClick} ${TXT} ${FUNC}
!macroend
; ------------------------------------------------------------------ setup page
Function SetupPageShow
nsDialogs::Create 1018
Pop $PageDlg
${If} $PageDlg == error
Abort
${EndIf}
Call PaintChrome
Call MeasurePage
!insertmacro LayoutVars
StrCpy $STRIP_H 2
!insertmacro PageChrome
; ---- what this run is going to do -----------------------------------------------------------
${If} $UpgradeFrom != ""
${NSD_CreateLabel} $X0 $Y_NOTE $W_RULE $H_LINE \
"Updating in place - your wallpaper and settings are kept."
Pop $UpgradeNote
SetCtlColors $UpgradeNote "${C_GREEN}" "${C_BG}"
CreateFont $1 "Segoe UI" 9 400
SendMessage $UpgradeNote ${WM_SETFONT} $1 1
${If} $InstalledVersion == "0.0.0" ; an older install that never recorded its version
${NSD_CreateLabel} $X0 $Y_VERSION $W_LEFT $H_LINE \
"The LiveWall copy you already have → ${VERSION}"
${Else}
${NSD_CreateLabel} $X0 $Y_VERSION $W_LEFT $H_LINE \
"${APP_NAME} $UpgradeFrom → ${VERSION}"
${EndIf}
${Else}
${NSD_CreateLabel} $X0 $Y_NOTE $W_RULE $H_LINE \
"LiveWall animates your wallpaper behind the desktop icons."
Pop $UpgradeNote
SetCtlColors $UpgradeNote "${C_DIM}" "${C_BG}"
CreateFont $1 "Segoe UI" 9 400
SendMessage $UpgradeNote ${WM_SETFONT} $1 1
${NSD_CreateLabel} $X0 $Y_VERSION $W_LEFT $H_LINE \
"Version ${VERSION} · installs for your account only"
${EndIf}
Pop $0
SetCtlColors $0 "${C_DIM}" "${C_BG}"
CreateFont $1 "Segoe UI" 9 400
SendMessage $0 ${WM_SETFONT} $1 1
StrCpy $TmpW $X_RIGHT
IntOp $TmpW $TmpW - $X0
IntOp $TmpW $TmpW - 10
${NSD_CreateLabel} $X0 $Y_PATH $TmpW $H_LINE "$INSTDIR"
Pop $PathLabel
SetCtlColors $PathLabel "${C_TEXT}" "${C_BG}"
CreateFont $1 "Segoe UI" 8 400
SendMessage $PathLabel ${WM_SETFONT} $1 1
${If} $UpgradeFrom == ""
${NSD_CreateLabel} $X_RIGHT $Y_PATH 50 $H_LINE "Change"
Pop $0
SetCtlColors $0 "${C_ACCENT}" "${C_BG}"
CreateFont $1 "Segoe UI" 9 400
SendMessage $0 ${WM_SETFONT} $1 1
${NSD_OnClick} $0 OnChangeFolder
${EndIf}
; ---- options -------------------------------------------------------------------------------
!insertmacro MakeOption $Bx1 $Tx1 Toggle1 $X0 $Y_OPT1 "Create a desktop shortcut" 16
${If} $OptDesktop == 1
StrCpy $S1 1
${Else}
StrCpy $S1 0
${EndIf}
!insertmacro MakeOption $Bx2 $Tx2 Toggle2 $X0 $Y_OPT2 "Start ${APP_NAME} with Windows" 16
${If} $OptStartup == 1
StrCpy $S2 1
${Else}
StrCpy $S2 0
${EndIf}
${If} $UpgradeFrom == ""
!insertmacro MakeOption $Bx3 $Tx3 Toggle3 $X0 $Y_OPT3 "Start ${APP_NAME} when Setup finishes" 16
StrCpy $S3 1
${Else}
!insertmacro MakeOption $Bx3 $Tx3 Toggle3 $X0 $Y_OPT3 "Also delete my ${APP_NAME} settings and start fresh" 16
StrCpy $S3 0
${EndIf}
StrCpy $R3 $Bx1
StrCpy $R5 $S1
Call PaintOption
StrCpy $R3 $Bx2
StrCpy $R5 $S2
Call PaintOption
StrCpy $R3 $Bx3
StrCpy $R5 $S3
Call PaintOption
; ---- footer --------------------------------------------------------------------------------
${NSD_CreateLabel} $X0 $Y_FOOT $W_LEFT $H_LINE "No bundled offers · no telemetry"
Pop $0
SetCtlColors $0 "${C_DIM}" "${C_BG}"
CreateFont $1 "Segoe UI" 8 400
SendMessage $0 ${WM_SETFONT} $1 1
!insertmacro MakeButton $0 $2 OnCancelClick $X_BTN2 $Y_BTN $W_BTN $H_BTN "Cancel" "${C_FIELD}" "${C_TEXT}" 400
${If} $UpgradeFrom == ""
!insertmacro MakeButton $0 $InstallBtn OnInstallClick $X_BTN1 $Y_BTN $W_BTN $H_BTN "Install" "${C_ACCENT}" "${C_ON_ACCENT}" 700
${Else}
!insertmacro MakeButton $0 $InstallBtn OnInstallClick $X_BTN1 $Y_BTN $W_BTN $H_BTN "Update" "${C_ACCENT}" "${C_ON_ACCENT}" 700
${EndIf}
nsDialogs::Show
FunctionEnd
Function SetupPageLeave
; the third option means different things for a fresh install and for an update
StrCpy $OptDesktop $S1
StrCpy $OptStartup $S2
${If} $UpgradeFrom == ""
StrCpy $OptStartApp $S3
StrCpy $DeleteSettings 0
${Else}
StrCpy $DeleteSettings $S3
${EndIf}
; This wizard has no InstFiles page: the files are written here, while the first page is leaving.
; The second page follows straight away.
${NSD_SetText} $InstallBtn "Installing..."
System::Call 'user32::UpdateWindow(p $HWNDPARENT)'
Call DoInstall
FunctionEnd
Function OnChangeFolder
${If} $UpgradeFrom != ""
Return ; an update always goes into the folder that is already there
${EndIf}
nsDialogs::SelectFolderDialog "Where should ${APP_NAME} be installed?" "$INSTDIR"
Pop $0
${If} $0 == error
Return
${EndIf}
${If} $0 == ""
Return
${EndIf}
StrCpy $INSTDIR "$0"
${If} $PathLabel != 0
${NSD_SetText} $PathLabel "$INSTDIR"
${EndIf}
FunctionEnd
Function OnInstallClick
; the page's leave function does the work; this just moves the wizard on
SendMessage $HWNDPARENT ${WM_COMMAND} 1 0
FunctionEnd
Function OnCancelClick
SendMessage $HWNDPARENT ${WM_COMMAND} 2 0
FunctionEnd
; ------------------------------------------------------------------ done page
Function DonePageShow
nsDialogs::Create 1018
Pop $PageDlg
${If} $PageDlg == error
Abort
${EndIf}
Call PaintChrome
Call MeasurePage
!insertmacro LayoutVars
StrCpy $STRIP_H 2
!insertmacro Pct $Y_OPT1 $PV 60
!insertmacro Pct $Y_OPT2 $PV 69
!insertmacro PageChrome
${NSD_CreateLabel} $X0 $Y_NOTE $W_RULE $H_LINE \
"${APP_NAME} ${VERSION} is installed and ready."
Pop $0
SetCtlColors $0 "${C_GREEN}" "${C_BG}"
CreateFont $1 "Segoe UI" 10 700
SendMessage $0 ${WM_SETFONT} $1 1
StrCpy $TmpX $H_LINE
IntOp $TmpX $TmpX * 2
${NSD_CreateLabel} $X0 $Y_VERSION $W_RULE $TmpX \
"It sits in your notification area - right-click the tray icon for settings, wallpapers, pause, \
interactive mode and plugins."
Pop $0
SetCtlColors $0 "${C_DIM}" "${C_BG}"
CreateFont $1 "Segoe UI" 9 400
SendMessage $0 ${WM_SETFONT} $1 1
!insertmacro MakeOption $Bx4 $Tx4 Toggle4 $X0 $Y_OPT1 "Start ${APP_NAME} now" 16
StrCpy $S4 1
!insertmacro MakeOption $Bx5 $Tx5 Toggle5 $X0 $Y_OPT2 "Open the ${APP_NAME} folder" 16
StrCpy $S5 0
StrCpy $R3 $Bx4
StrCpy $R5 $S4
Call PaintOption
StrCpy $R3 $Bx5
StrCpy $R5 $S5
Call PaintOption
${NSD_CreateLabel} $X0 $Y_FOOT $W_LEFT $H_LINE "Uninstall any time from Windows Settings."
Pop $0
SetCtlColors $0 "${C_DIM}" "${C_BG}"
CreateFont $1 "Segoe UI" 8 400
SendMessage $0 ${WM_SETFONT} $1 1
!insertmacro MakeButton $0 $FinishBtn OnFinishClick $X_BTN1 $Y_BTN $W_BTN $H_BTN "Finish" "${C_ACCENT}" "${C_ON_ACCENT}" 700
nsDialogs::Show
FunctionEnd
Function DonePageLeave
StrCpy $StartAfter $S4
StrCpy $OptOpenFolder $S5
FunctionEnd
Function OnFinishClick
SendMessage $HWNDPARENT ${WM_COMMAND} 1 0
FunctionEnd
; ------------------------------------------------------------------ install
; All of the file work, called from the setup page's leave function (and from .onInit when silent).
Function DoInstall
SetShellVarContext current
; Replacing an older version: start from a clean folder so no stale dependency survives.
${If} ${FileExists} "$INSTDIR\${APP_EXE}"
RMDir /r "$INSTDIR"
${EndIf}
${If} $DeleteSettings == 1
RMDir /r "$APPDATA\LiveWall"
${EndIf}
SetOutPath "$INSTDIR"
File /r "${SOURCE_DIR}\*.*"
WriteUninstaller "$INSTDIR\Uninstall.exe"
CreateShortcut "$SMPROGRAMS\${APP_NAME}.lnk" "$INSTDIR\${APP_EXE}" "" "$INSTDIR\${APP_EXE}" 0
${If} $OptDesktop == 1
CreateShortcut "$DESKTOP\${APP_NAME}.lnk" "$INSTDIR\${APP_EXE}" "" "$INSTDIR\${APP_EXE}" 0
${Else}
Delete "$DESKTOP\${APP_NAME}.lnk"
${EndIf}
${If} $OptStartup == 1
CreateShortcut "$SMSTARTUP\${APP_NAME}.lnk" "$INSTDIR\${APP_EXE}" "" "$INSTDIR\${APP_EXE}" 0
${Else}
Delete "$SMSTARTUP\${APP_NAME}.lnk"
${EndIf}
WriteRegStr HKCU "${APP_REGKEY}" "InstallDir" "$INSTDIR"
WriteRegStr HKCU "${APP_REGKEY}" "Version" "${VERSION}"
${If} $UpgradeFrom != ""
WriteRegStr HKCU "${APP_REGKEY}" "UpdatedFrom" "$UpgradeFrom"
${EndIf}
WriteRegStr HKCU "${UNINST_KEY}" "DisplayName" "${APP_NAME}"
WriteRegStr HKCU "${UNINST_KEY}" "DisplayVersion" "${VERSION}"
WriteRegStr HKCU "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\${APP_EXE},0"
WriteRegStr HKCU "${UNINST_KEY}" "Publisher" "${APP_PUBLISHER}"
WriteRegStr HKCU "${UNINST_KEY}" "InstallLocation" "$INSTDIR"
WriteRegStr HKCU "${UNINST_KEY}" "UninstallString" "$\"$INSTDIR\Uninstall.exe$\""
WriteRegStr HKCU "${UNINST_KEY}" "QuietUninstallString" "$\"$INSTDIR\Uninstall.exe$\" /S"
WriteRegDWORD HKCU "${UNINST_KEY}" "NoModify" 1
WriteRegDWORD HKCU "${UNINST_KEY}" "NoRepair" 1
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
IntFmt $0 "0x%08X" $0
WriteRegDWORD HKCU "${UNINST_KEY}" "EstimatedSize" "$0"
; the last thing before the page changes, so "Start now" points at the finished install
${If} $OptStartApp == 1
StrCpy $StartAfter 1
${EndIf}
FunctionEnd
; ------------------------------------------------------------------ sections
;
; The installer has no InstFiles page, so this section is never executed - DoInstall() does the work
; while the setup page is leaving. It exists because NSIS wants at least one regular section.
Section "-Core"
SectionEnd
Section "Uninstall"
SetShellVarContext current
nsExec::Exec 'taskkill /IM ${APP_EXE} /F'
Pop $0
Sleep 500
Delete "$SMPROGRAMS\${APP_NAME}.lnk"
Delete "$DESKTOP\${APP_NAME}.lnk"
Delete "$SMSTARTUP\${APP_NAME}.lnk"
RMDir /r "$INSTDIR"
DeleteRegKey HKCU "${UNINST_KEY}"
DeleteRegKey HKCU "${APP_REGKEY}"
; Settings, logs and the WebView2 cache go only if the user asked for it on the first page.
${If} $DeleteSettings == 1
RMDir /r "$APPDATA\LiveWall"
${EndIf}
SectionEnd
Function un.ConfirmPageShow
nsDialogs::Create 1018
Pop $PageDlg
${If} $PageDlg == error
Abort
${EndIf}
Call un.PaintChrome
Call un.MeasurePage
!insertmacro LayoutVars
StrCpy $STRIP_H 2
!insertmacro Pct $Y_OPT1 $PV 64 ; the description needs three lines here
!insertmacro PageChrome
${NSD_CreateLabel} $X0 $Y_NOTE $W_RULE $H_LINE "Remove ${APP_NAME}?"
Pop $0
SetCtlColors $0 "${C_TEXT}" "${C_BG}"
CreateFont $1 "Segoe UI" 10 700
SendMessage $0 ${WM_SETFONT} $1 1
StrCpy $TmpX $H_LINE
IntOp $TmpX $TmpX * 3
${NSD_CreateLabel} $X0 $Y_VERSION $W_RULE $TmpX \
"The program files, shortcuts and uninstall entry are removed. Your wallpaper, settings and the \
web-player cache in %APPDATA%\LiveWall stay unless you tick the box below."
Pop $0
SetCtlColors $0 "${C_DIM}" "${C_BG}"
CreateFont $1 "Segoe UI" 9 400
SendMessage $0 ${WM_SETFONT} $1 1
!insertmacro MakeOption $Bx1 $Tx1 un.Toggle1 $X0 $Y_OPT1 "Also delete my ${APP_NAME} settings, logs and cache" 16
StrCpy $S1 0
StrCpy $R3 $Bx1
StrCpy $R5 $S1
Call un.PaintOption
!insertmacro MakeButton $0 $2 un.OnUninstallCancel $X_BTN2 $Y_BTN $W_BTN $H_BTN "Cancel" "${C_FIELD}" "${C_TEXT}" 400
!insertmacro MakeButton $0 $InstallBtn un.OnUninstallClick $X_BTN1 $Y_BTN $W_BTN $H_BTN "Remove" "${C_ACCENT}" "${C_ON_ACCENT}" 700
nsDialogs::Show
FunctionEnd
Function un.OnUninstallClick
SendMessage $HWNDPARENT ${WM_COMMAND} 1 0
FunctionEnd
Function un.OnUninstallCancel
SendMessage $HWNDPARENT ${WM_COMMAND} 2 0
FunctionEnd
Function un.ConfirmPageLeave
StrCpy $DeleteSettings $S1 ; the uninstaller has one option, and it starts cleared
FunctionEnd
; Duplicated for the uninstaller: NSIS compiles the two halves separately.
Function un.PaintChrome
SetCtlColors $HWNDPARENT "${C_TEXT}" "${C_BG}"
GetDlgItem $0 $HWNDPARENT 1
ShowWindow $0 ${SW_HIDE}
GetDlgItem $0 $HWNDPARENT 2
ShowWindow $0 ${SW_HIDE}
GetDlgItem $0 $HWNDPARENT 3
ShowWindow $0 ${SW_HIDE}
FunctionEnd
Function un.MeasurePage
System::Call '*(i,i,i,i) i .r5'
System::Call 'user32::GetClientRect(p $HWNDPARENT, p r5) i .r4'
System::Call '*$5(i .r6, i .r7, i .r8, i .r9)'
StrCpy $PU $8
StrCpy $PV $9
System::Call 'user32::SetWindowPos(p $PageDlg, p 0, i 0, i 0, i $8, i $9, i 0x14)'
FunctionEnd
; ------------------------------------------------------------------ callbacks
Function .onInit
; The published build is win-x64.
${IfNot} ${RunningX64}
MessageBox MB_ICONSTOP "${APP_NAME} needs 64-bit Windows."
Abort
${EndIf}
SetShellVarContext current
; Defaults: desktop shortcut on, autostart off (the app has its own switch), start after setup on.
StrCpy $OptDesktop 1
StrCpy $OptStartup 0
StrCpy $OptStartApp 1
StrCpy $OptOpenFolder 0
StrCpy $StartAfter 0
StrCpy $DeleteSettings 0
Call DetectExisting
${If} $InstalledDir != ""
; A running copy would keep the exe locked.
nsExec::Exec 'taskkill /IM ${APP_EXE} /F'
Pop $0
Sleep 700
${EndIf}
${If} $InstalledDir == ""
Goto init_dotnet
${EndIf}
${If} $InstalledScore == $ThisScore
IfSilent init_dotnet
MessageBox MB_YESNO|MB_ICONQUESTION \
"${APP_NAME} ${VERSION} is already installed in:$\r$\n$INSTDIR$\r$\n$\r$\nReinstall it?" \
IDYES init_dotnet
Abort
${EndIf}
${If} $InstalledScore > $ThisScore
IfSilent init_dotnet
MessageBox MB_YESNO|MB_ICONEXCLAMATION \
"A newer ${APP_NAME} is already installed:$\r$\n$\r$\n installed: $InstalledVersion$\r$\n this package: ${VERSION}$\r$\n$\r$\nDowngrade to ${VERSION} anyway?" \
IDYES init_dotnet
Abort
${EndIf}
init_dotnet:
SetRegView 64
Call CheckDotNet
SetRegView lastused
; Silent installs have no pages, so everything runs here and the process ends.
${If} ${Silent}
Call DoInstall
Quit
${EndIf}
FunctionEnd
; Runs when the wizard closes: start the app, or open the folder, whichever was ticked.
Function .onGUIEnd
${If} $StartAfter == 1
Exec '"$INSTDIR\${APP_EXE}"'
${ElseIf} $OptOpenFolder == 1
ExecShell "open" "$INSTDIR"
${EndIf}
FunctionEnd
Function un.onInit
SetRegView 64
StrCpy $DeleteSettings 0
FunctionEnd
; Looks for the .NET Desktop Runtime 8 (or newer) and offers the download page if it is missing.
Function CheckDotNet
StrCpy $1 0
StrCpy $4 0
dotnet_loop:
EnumRegKey $2 HKLM "SOFTWARE\dotnet\Setup\InstalledVersions\x64\sharedfx\Microsoft.WindowsDesktop.App" $1
StrCmp $2 "" dotnet_done
StrCpy $3 $2 1
${If} $3 == "8"
${OrIf} $3 == "9"
StrCpy $4 1
Goto dotnet_done
${EndIf}
${If} $3 == "1" ; 10.x and later
StrCpy $4 1
Goto dotnet_done
${EndIf}
IntOp $1 $1 + 1
Goto dotnet_loop
dotnet_done:
${If} $4 == 0
IfSilent dotnet_skip
MessageBox MB_YESNO|MB_ICONINFORMATION \
"${APP_NAME} needs the .NET Desktop Runtime (8 or newer) and it does not look installed.$\r$\n$\r$\nOpen the Microsoft download page now? You can also continue and install it later - ${APP_NAME} will tell you what is missing." \
IDNO dotnet_skip
ExecShell "open" "${DOTNET_URL}"
dotnet_skip:
${EndIf}
FunctionEnd