988 lines
30 KiB
NSIS
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
|