From c51365f97f932edbb23a2089caf3e0a161931f09 Mon Sep 17 00:00:00 2001 From: Simone Date: Wed, 29 Apr 2026 13:25:14 +0200 Subject: [PATCH] initial: nsResize plugin 1.0.0 --- .editorconfig | 24 + .gitattributes | 52 ++ .github/ISSUE_TEMPLATE/bug_report.yml | 76 +++ .github/ISSUE_TEMPLATE/config.yml | 8 + .github/ISSUE_TEMPLATE/feature_request.yml | 32 + .github/PULL_REQUEST_TEMPLATE.md | 31 + .github/workflows/build.yml | 63 ++ .github/workflows/release.yml | 99 +++ .gitignore | 63 ++ CHANGELOG.md | 19 + CONTRIBUTING.md | 137 +++++ LICENSE | 21 + README.md | 170 ++++++ README_IT.md | 170 ++++++ SECURITY.md | 40 ++ VERSION | 1 + build_plugin.py | 679 +++++++++++++++++++++ dist/amd64-unicode/nsResize.dll | Bin 0 -> 8192 bytes dist/x86-ansi/nsResize.dll | Bin 0 -> 6144 bytes dist/x86-unicode/nsResize.dll | Bin 0 -> 6144 bytes docs/Readme.txt | 108 ++++ examples/Circle.nsi | 44 ++ examples/LargeWindow.nsi | 118 ++++ include/nsResize.nsh | 187 ++++++ src/api.h | 85 +++ src/build_pluginapi_x64.cmd | 7 + src/build_pluginapi_x86.cmd | 15 + src/nsResize.c | 257 ++++++++ src/nsResize.h | 7 + src/nsResize.rc | Bin 0 -> 4644 bytes src/nsResize.sln | 23 + src/nsResize.vcxproj | 165 +++++ src/nsResize.vcxproj.filters | 35 ++ src/nsis_tchar.h | 229 +++++++ src/pluginapi.c | 305 +++++++++ src/pluginapi.h | 109 ++++ src/resource.h | 14 + 37 files changed, 3393 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 README_IT.md create mode 100644 SECURITY.md create mode 100644 VERSION create mode 100644 build_plugin.py create mode 100644 dist/amd64-unicode/nsResize.dll create mode 100644 dist/x86-ansi/nsResize.dll create mode 100644 dist/x86-unicode/nsResize.dll create mode 100644 docs/Readme.txt create mode 100644 examples/Circle.nsi create mode 100644 examples/LargeWindow.nsi create mode 100644 include/nsResize.nsh create mode 100644 src/api.h create mode 100644 src/build_pluginapi_x64.cmd create mode 100644 src/build_pluginapi_x86.cmd create mode 100644 src/nsResize.c create mode 100644 src/nsResize.h create mode 100644 src/nsResize.rc create mode 100644 src/nsResize.sln create mode 100644 src/nsResize.vcxproj create mode 100644 src/nsResize.vcxproj.filters create mode 100644 src/nsis_tchar.h create mode 100644 src/pluginapi.c create mode 100644 src/pluginapi.h create mode 100644 src/resource.h diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..55629c5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,24 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 4 + +[*.{yml,yaml,json}] +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[*.{cmd,bat,ps1}] +end_of_line = crlf + +[*.{nsi,nsh,sln,vcxproj,filters}] +end_of_line = crlf + +[Makefile] +indent_style = tab diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..e2cfe88 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,52 @@ +# Normalizzazione line endings +* text=auto eol=lf + +# File di codice +*.c text eol=lf +*.cpp text eol=lf +*.h text eol=lf +*.hpp text eol=lf +*.def text eol=lf +*.py text eol=lf +*.ps1 text eol=lf +*.cmd text eol=crlf +*.bat text eol=crlf + +# NSIS +*.nsi text eol=crlf +*.nsh text eol=crlf + +# Markdown / docs / config +*.md text eol=lf +*.yml text eol=lf +*.yaml text eol=lf +*.json text eol=lf +*.txt text eol=lf +*.xml text eol=lf + +# Visual Studio (CRLF richiesto) +*.sln text eol=crlf +*.vcxproj text eol=crlf merge=union +*.filters text eol=crlf merge=union +# Binari +*.dll binary +*.exe binary +*.lib binary +*.obj binary +*.pdb binary +*.zip binary +*.7z binary +*.png binary +*.ico binary +*.gif binary +*.jpg binary +*.jpeg binary +*.bmp binary +*.pdf binary + +# Linguistic detection (GitHub Linguist / Gitea analogo) +*.nsi linguist-language=NSIS +*.nsh linguist-language=NSIS + +*.rc binary +*.rc2 binary diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..7599f2a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,76 @@ +name: Bug Report +description: Segnala un bug riproducibile +title: "[Bug]: " +labels: ["bug", "triage"] +body: + - type: markdown + attributes: + value: | + Grazie per la segnalazione. Compila i campi sotto per aiutarci a riprodurre il problema. + + - type: input + id: plugin-version + attributes: + label: Versione plugin + description: Contenuto del file VERSION o tag della release scaricata + placeholder: "1.6.0" + validations: + required: true + + - type: dropdown + id: architecture + attributes: + label: Architettura DLL usata + options: + - x86-ansi + - x86-unicode + - amd64-unicode + - non so / multiple + validations: + required: true + + - type: input + id: nsis-version + attributes: + label: Versione NSIS + placeholder: "3.10" + validations: + required: true + + - type: input + id: os + attributes: + label: Sistema operativo + placeholder: "Windows 11 23H2 x64" + validations: + required: true + + - type: textarea + id: repro + attributes: + label: Passi per riprodurre + description: Script .nsi minimale che riproduce il problema + render: nsis + validations: + required: true + + - type: textarea + id: expected + attributes: + label: Comportamento atteso + validations: + required: true + + - type: textarea + id: actual + attributes: + label: Comportamento osservato + description: Includi messaggi di errore esatti, output del log NSIS, screenshot + validations: + required: true + + - type: textarea + id: additional + attributes: + label: Note aggiuntive + description: Qualunque altra informazione utile (workaround tentati, ecc.) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..925af5e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Domande generali + url: https://gitea.emulab.it/Simone/nsis-plugin-nsresize/issues?type=question + about: Per domande di utilizzo che non sono bug, apri una issue con label "question" + - name: Security + url: https://gitea.emulab.it/Simone/nsis-plugin-nsresize/security/advisories/new + about: Segnala vulnerabilità di security in privato (non aprire issue pubbliche) diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..fae6d8d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,32 @@ +name: Feature Request +description: Suggerisci una nuova funzionalità o miglioramento +title: "[Feature]: " +labels: ["enhancement"] +body: + - type: textarea + id: problem + attributes: + label: Problema / Caso d'uso + description: Quale problema vorresti risolvere? Quale workflow vuoi abilitare? + validations: + required: true + + - type: textarea + id: proposal + attributes: + label: Proposta + description: Come dovrebbe funzionare? Includi esempio di API/uso negli script NSIS se possibile. + render: nsis + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternative considerate + description: Altri approcci che hai valutato e perché sono stati scartati + + - type: textarea + id: additional + attributes: + label: Note aggiuntive diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..3758b50 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,31 @@ +## Descrizione + + + +## Tipo di Modifica + +- [ ] Bug fix (modifica non breaking che risolve un problema) +- [ ] Feature (modifica non breaking che aggiunge funzionalità) +- [ ] Breaking change (fix o feature che rompe la compatibilità) +- [ ] Documentazione +- [ ] Build / CI + +## Architetture Testate + +- [ ] `x86-ansi` +- [ ] `x86-unicode` +- [ ] `amd64-unicode` + +## Checklist + +- [ ] Build completa senza warning: `python build_plugin.py` +- [ ] CHANGELOG.md aggiornato (sezione `[Unreleased]`) +- [ ] README / docs aggiornati se l'API è cambiata +- [ ] Esempi aggiornati se l'API è cambiata +- [ ] Conventional Commits rispettato +- [ ] Nessun file in `dist/` o `__pycache__/` committato +- [ ] Self-review completata + +## Note Aggiuntive + + diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..4fe517a --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,63 @@ +# Build (GitHub) — pipeline CI primaria +# +# Strategia hosting: +# - Repo primario su Gitea (push iniziale) +# - Push mirror automatico Gitea -> GitHub +# - GitHub esegue TUTTA la CI (Gitea non esegue Actions) +# +# Trigger: +# - push su main (incluso quello propagato dal mirror Gitea) +# - PR aperte su GitHub +# - run manuale via UI + +name: Build + +on: + push: + branches: [main] + paths-ignore: + - '**.md' + - 'docs/**' + - 'examples/**' + pull_request: + branches: [main] + workflow_dispatch: + +jobs: + build: + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + config: [x86-ansi, x86-unicode, x64-unicode] + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Setup MSBuild + uses: microsoft/setup-msbuild@v2 + + - name: Read VERSION + id: version + shell: pwsh + run: | + $v = (Get-Content VERSION -Raw).Trim() + "version=$v" | Out-File -FilePath $env:GITHUB_OUTPUT -Append + + - name: Build ${{ matrix.config }} + run: python build_plugin.py --config ${{ matrix.config }} --verbosity minimal + + - name: Upload DLL + uses: actions/upload-artifact@v4 + with: + name: ${{ github.event.repository.name }}-${{ steps.version.outputs.version }}-${{ matrix.config }} + path: dist/** + if-no-files-found: error + retention-days: 30 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..e4cf67d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,99 @@ +# Release (GitHub) — pipeline release primaria +# +# Triggerata quando un tag v* viene propagato dal mirror Gitea. +# Costruisce gli artifact per tutte le architetture e crea la GitHub Release. + +name: Release + +on: + push: + tags: ['v*'] + workflow_dispatch: + inputs: + tag: + description: 'Tag versione (es. v1.7.0)' + required: true + +permissions: + contents: write + +jobs: + build: + runs-on: windows-latest + strategy: + fail-fast: true + matrix: + config: [x86-ansi, x86-unicode, x64-unicode] + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - uses: microsoft/setup-msbuild@v2 + + - name: Build ${{ matrix.config }} + run: python build_plugin.py --config ${{ matrix.config }} + + - name: Determine version & arch + id: meta + shell: pwsh + run: | + $tag = "${{ github.ref_name }}" + if ([string]::IsNullOrEmpty($tag) -or $tag -eq "main") { $tag = "${{ inputs.tag }}" } + $v = $tag -replace '^v','' + $map = @{ 'x86-ansi' = 'x86-ansi'; 'x86-unicode' = 'x86-unicode'; 'x64-unicode' = 'amd64-unicode' } + $dir = $map['${{ matrix.config }}'] + echo "version=$v" >> $env:GITHUB_OUTPUT + echo "tag=$tag" >> $env:GITHUB_OUTPUT + echo "dir=$dir" >> $env:GITHUB_OUTPUT + + - name: Package + shell: pwsh + run: | + $name = "${{ github.event.repository.name }}-${{ steps.meta.outputs.version }}-${{ steps.meta.outputs.dir }}" + $stage = "stage/$name" + New-Item -ItemType Directory -Force -Path $stage | Out-Null + Copy-Item "dist/${{ steps.meta.outputs.dir }}/*" $stage -Recurse + Copy-Item README.md, LICENSE, VERSION, CHANGELOG.md $stage + if (Test-Path "include") { Copy-Item "include" $stage -Recurse } + if (Test-Path "examples") { Copy-Item "examples" $stage -Recurse } + Compress-Archive -Path "$stage/*" -DestinationPath "$name.zip" -Force + + - uses: actions/upload-artifact@v4 + with: + name: pkg-${{ matrix.config }} + path: '*.zip' + + publish: + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4 + with: + path: artifacts + merge-multiple: true + + - name: SHA256SUMS + run: | + cd artifacts + sha256sum *.zip > SHA256SUMS.txt + cat SHA256SUMS.txt + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + files: | + artifacts/*.zip + artifacts/SHA256SUMS.txt + generate_release_notes: true + body: | + **Repository primario**: vedi link Gitea nel README. + + Il mirror GitHub è generato automaticamente dal repo Gitea (sorgente di verità). + Per issue e PR aprire ticket sul repository primario Gitea. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2c3a730 --- /dev/null +++ b/.gitignore @@ -0,0 +1,63 @@ +# Build output (src/ subdirs only — dist/ is tracked, contains pre-built DLLs) +bin/ +obj/ +Build/ +build/ + +# Visual Studio +*.user +*.suo +*.ncb +*.sdf +.vs/ +ipch/ +*.VC.db +*.VC.VC.opendb +*.opendb +*.aps +*.tlog +*.idb +*.pdb +*.ilk +*.exp +*.lib +*.obj + +# Python +__pycache__/ +*.pyc +*.pyo +.pytest_cache/ +*.egg-info/ +.venv/ +venv/ + +# Editor / IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +Thumbs.db +desktop.ini +.DS_Store + +# NSIS +*.nsh.bak + +# Logs +*.log +.logs/ + +# Sensitive +.env +.env.local +*.pem +*.key + +# Migration/automation scripts (temporary, contain tokens — never commit) +tools/t*.py +tools/check_*.py +tools/legacy/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..28872db --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,19 @@ +# Changelog + +Tutte le modifiche rilevanti a questo progetto sono documentate in questo file. + +Il formato segue [Keep a Changelog](https://keepachangelog.com/it/1.0.0/), +e il progetto aderisce al [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [1.0.0] — 2026-04-29 + +### Added +- Prima release come repository indipendente +- Build script unificato (`build_plugin.py`) con CLI canonica +- CI/CD via GitHub Actions (mirror automatico Gitea → GitHub) +- Documentazione completa (README, CONTRIBUTING, SECURITY) + +[Unreleased]: https://gitea.emulab.it/Simone/nsis-plugin-nsresize/compare/v1.0.0...HEAD +[1.0.0]: https://gitea.emulab.it/Simone/nsis-plugin-nsresize/releases/tag/v1.0.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ca1e34d --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,137 @@ +# Contribuire a nsResize + +Grazie per il tuo interesse a contribuire! + +> Repository primario: [https://gitea.emulab.it/Simone/nsis-plugin-nsresize](https://gitea.emulab.it/Simone/nsis-plugin-nsresize) +> Mirror GitHub: [https://github.com/systempal/nsis-plugin-nsresize](https://github.com/systempal/nsis-plugin-nsresize) (read-only — apri PR su Gitea) + +## Prerequisiti + +| Strumento | Versione | Note | +|-----------|----------|------| +| Visual Studio | 2022 / 2026 | Workload: Desktop development with C++ (toolset v143/v145) | +| Python | 3.7+ | Per `build_plugin.py` | +| Git | 2.30+ | Con supporto submoduli se applicabile | + +## Quick Start + +```powershell +git clone https://gitea.emulab.it/Simone/nsis-plugin-nsresize.git +cd nsis-plugin-nsresize +python build_plugin.py +``` + +Output in `dist/{x86-ansi,x86-unicode,amd64-unicode}/`. + +## Workflow Sviluppo + +### 1. Branch + +```powershell +git checkout -b feat/nome-feature # nuova feature +git checkout -b fix/descrizione-bug # bugfix +git checkout -b docs/aggiornamento # solo doc +git checkout -b chore/manutenzione # build, dipendenze +``` + +### 2. Commit + +Segui [Conventional Commits](https://www.conventionalcommits.org/it/v1.0.0/): + +``` +feat: aggiunge supporto per formato XYZ +fix: corregge crash su Windows 11 ARM +docs: aggiorna esempio di utilizzo +chore: aggiorna toolset MSVC +refactor: estrae logica parsing in funzione separata +test: aggiunge test per edge case +build: aggiorna script di build +ci: aggiorna workflow GitHub Actions +``` + +Breaking changes: + +``` +feat!: rinomina API Foo() in Bar() + +BREAKING CHANGE: Foo() rinominata in Bar(); aggiornare tutti i chiamanti. +``` + +### 3. Pull Request + +PR aperte verso il branch `main` su Gitea. Verifica: + +- [ ] Build completa senza warning per tutte le architetture: `python build_plugin.py` +- [ ] Esempi aggiornati se cambia l'API +- [ ] `CHANGELOG.md` aggiornato (sezione `[Unreleased]`) +- [ ] Documentazione aggiornata (README, docs/API.md se esiste) +- [ ] Conventional Commits rispettato +- [ ] Nessun file in `dist/` o `__pycache__/` committato + +## Build Script + +``` +python build_plugin.py [opzioni] +``` + +| Opzione | Default | Descrizione | +|---------|---------|-------------| +| `--config` | `all` | Architettura: `x86-ansi`, `x86-unicode`, `x64-unicode`, `all` | +| `--toolset` | `auto` | Visual Studio: `2022`, `2026`, `auto` (rileva il più recente) | +| `--jobs` | numero CPU | Thread paralleli per la build | +| `--clean` | false | Pulisce `dist/` prima di compilare | +| `--install-dir` | none | Copia DLL anche in `/{arch}/` (es. NSIS plugin folder) | +| `--verbose` | false | Output dettagliato (mostra comandi MSBuild) | + +Exit codes: +- `0` — Successo +- `1` — Errore di build +- `2` — Prerequisiti mancanti +- `3` — Argomenti invalidi + +## Release Workflow + +Le release sono automatiche via tag Git: + +```powershell +# 1. Aggiorna VERSION e CHANGELOG.md +echo "1.7.0" > VERSION +# (modifica CHANGELOG.md spostando entry da [Unreleased] a [1.7.0]) + +# 2. Commit +git add VERSION CHANGELOG.md +git commit -m "chore(release): v1.7.0" + +# 3. Tag annotato +git tag -a v1.7.0 -m "Release v1.7.0" + +# 4. Push +git push origin main +git push origin v1.7.0 +``` + +Il workflow `.github/workflows/release.yml` compilerà tutte le architetture +e creerà la release su Gitea con gli artifact ZIP allegati. Il mirror GitHub +riceverà automaticamente il tag. + +## Code Style + +- C/C++: stile coerente con il file modificato +- Python: PEP 8 (4 spazi indent, line length flessibile) +- Header file: `#include` ordinati: stdlib → Win32 → progetto +- Encoding: UTF-8 senza BOM (eccetto `.rc` che richiede UTF-16 LE) +- Line endings: LF in repo (`.gitattributes` normalizza) + +## Reporting Bug + +Apri una [issue](https://gitea.emulab.it/Simone/nsis-plugin-nsresize/issues/new) usando il template "Bug Report". +Includi: +- Versione plugin e architettura DLL usata +- Versione NSIS +- Sistema operativo +- Script `.nsi` minimale che riproduce il problema +- Output / errore esatto + +## Domande + +Per domande generali, apri una [issue](https://gitea.emulab.it/Simone/nsis-plugin-nsresize/issues) con label `question`. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bc3fdc5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +zlib License + +Copyright (c) 2026 Simone + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. diff --git a/README.md b/README.md new file mode 100644 index 0000000..f2f9e14 --- /dev/null +++ b/README.md @@ -0,0 +1,170 @@ +# nsResize NSIS Plugin + +**Personal modified version** + +--- + +## Original Description + +**Author:** Stuart 'Afrow UK' Welch +**Company:** Afrow Soft Ltd +**Date:** 13th April 2013 +**Version:** 1.0.0.0 + +A small NSIS plug-in for moving or resizing windows/controls. + +See Examples\nsResize\*. + +## Macros + +nsResize.nsh includes macros used by the LargeWindow example script. + +## Functions + +### nsResize::SetRTL + +```nsis +nsResize::SetRTL $(^RTL) +``` + +Sets right-to-left mode on or off. This is only needed when using the nsResize::Set function. + +### nsResize::Set + +```nsis +nsResize::Set control_HWND x y width height +``` + +Sets a control's new X and Y coordinates and a new width and height. You may use the same unit values as nsDialogs. An empty string for x or y will ensure the control is not moved. An empty string for width or height will ensure the control is not resized. + +### nsResize::Add + +```nsis +nsResize::Add control_HWND x y width height +``` + +Adds the given X, Y, width and height values. You may use the same unit values as nsDialogs. Note that special treatment must be done for right-to-left languages. + +### nsResize::Top + +```nsis +nsResize::Top control_HWND +``` + +Moves a control to the top of the Z-order. + +### nsResize::GetPos + +```nsis +nsResize::GetPos control_HWND +Pop $X +Pop $Y +``` + +Gets the control position in dialog units relative to its parent window. + +### nsResize::GetPosPx + +```nsis +nsResize::GetPosPx control_HWND +Pop $X +Pop $Y +``` + +Gets the control position in pixels relative to its parent window. + +### nsResize::GetSize + +```nsis +nsResize::GetSize control_HWND +Pop $Width +Pop $Height +``` + +Gets the control size in dialog units. + +### nsResize::GetSizePx + +```nsis +nsResize::GetSizePx control_HWND +Pop $Width +Pop $Height +``` + +Gets the control size in pixels. + +## License + +This plug-in is provided 'as-is', without any express or implied warranty. In no event will the author be held liable for any damages arising from the use of this plug-in. + +Permission is granted to anyone to use this plug-in for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: + +1. The origin of this plug-in must not be misrepresented; you must not claim that you wrote the original plug-in. If you use this plug-in in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered versions must be plainly marked as such, and must not be misrepresented as being the original plug-in. +3. This notice may not be removed or altered from any distribution. + +--- + +## ⚠️ Differences in the Personal Version + +This version includes the following changes from the original distribution: + +### New supported architecture: x64 (amd64-unicode) + +The original supported only: +- x86-ansi +- x86-unicode + +The personal version adds support for: +- **amd64-unicode** (x64) + +### Visual Studio Project + +The original included: +- `Contrib/nsResize/nsResize.sln` - VS2012 Solution +- `Contrib/nsResize/nsResize.vcxproj` - VS2012 Project (x86 only) + +The personal version updates to VS2022 with x64 support: +- `Src/nsResize.sln` - VS2022 Solution +- `Src/nsResize.vcxproj` - VS2022 Project (x86-ansi, x86-unicode, amd64-unicode) + +### Changed Folder Structure + +Source files have been moved from `Contrib/nsResize/` to `Src/` for a cleaner structure. + +### Added Files + +- `build_plugin.py` - Python script to compile the plugin for all architectures +- `Src/api.h` - NSIS API header +- `Src/pluginapi.c` / `Src/pluginapi.h` - NSIS plugin API +- `Src/nsis_tchar.h` - Header for TCHAR support +- `Src/nsis_unicode/pluginapi_x64.lib` - Plugin API library for x64 +- `Src/build_pluginapi_x64.cmd` / `Src/build_pluginapi_x86.cmd` - Scripts to compile plugin API +- `Examples/nsResize/*.exe` - Compiled examples + +### Build + +```powershell +python build_plugin.py # Build all architectures +python build_plugin.py --config x86-unicode # Single architecture (x86-ansi|x86-unicode|amd64-unicode|all) +python build_plugin.py --toolset 2026 # Specific toolset (2022|2026|auto) +python build_plugin.py --jobs 4 # Number of parallel MSBuild jobs (default: CPU count) +python build_plugin.py --clean # Clean dist/ before build +python build_plugin.py --install-dir "C:\NSIS\Plugins" # Copy to additional NSIS directory +python build_plugin.py --verbose # Extended MSBuild output +python build_plugin.py --version # Print version and exit +``` + +DLLs are copied to `dist/{platform}/nsResize.dll`. + +### Removed Files + +Pre-compiled DLL files have been removed from the distribution: +- `Plugins/nsResize.dll` +- `Unicode/Plugins/nsResize.dll` + +DLLs are generated by the build and copied to `plugins/{platform}/`. + +--- + +*See [README_IT.md](README_IT.md) for the Italian version.* diff --git a/README_IT.md b/README_IT.md new file mode 100644 index 0000000..f1d60ed --- /dev/null +++ b/README_IT.md @@ -0,0 +1,170 @@ +# nsResize NSIS Plugin + +**Versione personale modificata** + +--- + +## Descrizione Originale + +**Autore:** Stuart 'Afrow UK' Welch +**Azienda:** Afrow Soft Ltd +**Data:** 13 Aprile 2013 +**Versione:** 1.0.0.0 + +Un piccolo plug-in NSIS per spostare o ridimensionare finestre/controlli. + +Vedi Examples\nsResize\*. + +## Macro + +nsResize.nsh include macro usate dallo script di esempio LargeWindow. + +## Funzioni + +### nsResize::SetRTL + +```nsis +nsResize::SetRTL $(^RTL) +``` + +Abilita o disabilita la modalità da destra a sinistra. Necessario solo quando si usa nsResize::Set. + +### nsResize::Set + +```nsis +nsResize::Set control_HWND x y width height +``` + +Imposta le nuove coordinate X e Y e le nuove dimensioni di un controllo. Stringa vuota per x o y = non spostare. Stringa vuota per width o height = non ridimensionare. + +### nsResize::Add + +```nsis +nsResize::Add control_HWND x y width height +``` + +Aggiunge i valori X, Y, width e height. Per lingue da destra a sinistra è necessaria una gestione speciale. + +### nsResize::Top + +```nsis +nsResize::Top control_HWND +``` + +Sposta un controllo in cima all'ordine Z. + +### nsResize::GetPos + +```nsis +nsResize::GetPos control_HWND +Pop $X +Pop $Y +``` + +Ottiene la posizione del controllo in unità dialogo relative alla finestra padre. + +### nsResize::GetPosPx + +```nsis +nsResize::GetPosPx control_HWND +Pop $X +Pop $Y +``` + +Ottiene la posizione del controllo in pixel relative alla finestra padre. + +### nsResize::GetSize + +```nsis +nsResize::GetSize control_HWND +Pop $Width +Pop $Height +``` + +Ottiene le dimensioni del controllo in unità dialogo. + +### nsResize::GetSizePx + +```nsis +nsResize::GetSizePx control_HWND +Pop $Width +Pop $Height +``` + +Ottiene le dimensioni del controllo in pixel. + +## Licenza + +Questo plug-in viene fornito 'così com'è', senza alcuna garanzia esplicita o implicita. In nessun caso l'autore sarà responsabile per danni derivanti dall'uso di questo plug-in. + +È concessa l'autorizzazione a chiunque di usare questo plug-in per qualsiasi scopo, incluse applicazioni commerciali, e di modificarlo e ridistribuirlo liberamente, soggetto alle seguenti restrizioni: + +1. L'origine di questo plug-in non deve essere travisata; non si può affermare di avere scritto il plug-in originale. +2. Le versioni alterate devono essere chiaramente contrassegnate come tali. +3. Questo avviso non può essere rimosso o alterato da alcuna distribuzione. + +--- + +## ⚠️ Differenze nella versione personale + +Questa versione include le seguenti modifiche rispetto alla distribuzione originale: + +### Nuova architettura supportata: x64 (amd64-unicode) + +L'originale supportava solo: +- x86-ansi +- x86-unicode + +La versione personale aggiunge il supporto per: +- **amd64-unicode** (x64) + +### Progetto Visual Studio + +L'originale includeva: +- `Contrib/nsResize/nsResize.sln` - Solution VS2012 +- `Contrib/nsResize/nsResize.vcxproj` - Progetto VS2012 (solo x86) + +La versione personale aggiorna a VS2022 con supporto x64: +- `Src/nsResize.sln` - Solution VS2022 +- `Src/nsResize.vcxproj` - Progetto VS2022 (x86-ansi, x86-unicode, amd64-unicode) + +### Struttura cartelle modificata + +I file sorgenti sono stati spostati da `Contrib/nsResize/` a `Src/` per una struttura più pulita. + +### File aggiunti + +- `build_plugin.py` - Script Python per compilare il plugin per tutte le architetture +- `Src/api.h` - Header API NSIS +- `Src/pluginapi.c` / `Src/pluginapi.h` - API plugin NSIS +- `Src/nsis_tchar.h` - Header per supporto TCHAR +- `Src/nsis_unicode/pluginapi_x64.lib` - Libreria plugin API per x64 +- `Src/build_pluginapi_x64.cmd` / `Src/build_pluginapi_x86.cmd` - Script per compilare plugin API +- `Examples/nsResize/*.exe` - Esempi compilati + +### Compilazione + +```powershell +python build_plugin.py # Compila tutte le architetture +python build_plugin.py --config x86-unicode # Solo un'architettura (x86-ansi|x86-unicode|amd64-unicode|all) +python build_plugin.py --toolset 2026 # Toolset specifico (2022|2026|auto) +python build_plugin.py --jobs 4 # Numero di job MSBuild paralleli (default: CPU count) +python build_plugin.py --clean # Pulizia dist/ prima della build +python build_plugin.py --install-dir "C:\NSIS\Plugins" # Copia in directory NSIS aggiuntiva +python build_plugin.py --verbose # Output MSBuild esteso +python build_plugin.py --version # Stampa versione ed esce +``` + +I DLL vengono copiati in `dist/{platform}/nsResize.dll`. + +### File rimossi + +I file DLL precompilati sono stati rimossi dalla distribuzione: +- `Plugins/nsResize.dll` +- `Unicode/Plugins/nsResize.dll` + +I DLL vengono generati dalla compilazione e copiati in `plugins/{platform}/`. + +--- + +*See [README.md](README.md) for the English version.* diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..93034e3 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,40 @@ +# Security Policy + +## Versioni Supportate + +Solo l'ultima versione minor riceve fix di security. + +| Versione | Supporto | +|----------|----------| +| Latest minor (es. 1.0.0) | ✅ | +| Versioni precedenti | ❌ | + +## Reporting di una Vulnerabilità + +**Non aprire issue pubbliche per vulnerabilità di security.** + +Per segnalare una vulnerabilità: + +1. **Preferito**: usa la funzione [Security Advisory privato](https://gitea.emulab.it/Simone/nsis-plugin-nsresize/security/advisories/new) di Gitea +2. **Alternativa**: invia email a `security@example.com` (sostituire con email reale prima del primo release) + +Includi: +- Descrizione della vulnerabilità +- Passi per riprodurla +- Versione/i affette +- Possibile mitigazione (se nota) + +## Tempo di Risposta + +- Acknowledgment iniziale: entro 7 giorni +- Valutazione: entro 14 giorni +- Patch (se confermata): tempistiche dipendono dalla severità + +## Hardening + +Questo plugin gira con i permessi del processo NSIS chiamante (tipicamente +l'utente che esegue l'installer). Non richiede privilegi elevati per la +compilazione né per l'esecuzione, salvo dove esplicitamente documentato. + +DLL distribuite tramite Releases sono compilate da CI GitHub Actions con +toolchain pinnata. Verificare hash SHA256 pubblicati nelle release. diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..3eefcb9 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.0.0 diff --git a/build_plugin.py b/build_plugin.py new file mode 100644 index 0000000..c0bf830 --- /dev/null +++ b/build_plugin.py @@ -0,0 +1,679 @@ +#!/usr/bin/env python3 +""" +Build script for nsResize plugin - All configurations +Supports multiple build configurations with flexible parameters +""" + +import argparse +import errno +import multiprocessing +import os +import shutil +import stat +import subprocess +import sys +import threading +import time +import xml.etree.ElementTree as ET +from concurrent.futures import ThreadPoolExecutor, as_completed +from pathlib import Path +from typing import List, Optional, Tuple + +# Ensure stdout/stderr handle Unicode on Windows CI (cp1252 default breaks non-ASCII) +if sys.stdout.encoding and sys.stdout.encoding.lower() not in ('utf-8', 'utf_8'): + import io + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') + sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace') + + +# --------------------------------------------------------------------------- +# Filesystem helpers +# --------------------------------------------------------------------------- + +def _on_rmtree_error(func, path, exc_info): + if func in (os.unlink, os.rmdir) and exc_info[1].errno == errno.EACCES: + try: + os.chmod(path, stat.S_IWRITE) + func(path) + return + except Exception: + pass + for i in range(5): + try: + time.sleep(0.1 * (i + 1)) + if os.path.isdir(path): + shutil.rmtree(path) + else: + os.unlink(path) + return + except Exception: + pass + raise exc_info[1] + + +def robust_rmtree(path): + if not os.path.exists(path): + return + try: + shutil.rmtree(path, onexc=_on_rmtree_error) + except TypeError: + try: + shutil.rmtree(path, onerror=_on_rmtree_error) + except Exception: + pass + except Exception: + try: + shutil.rmtree(path, ignore_errors=True) + except Exception: + pass + + +# --------------------------------------------------------------------------- +# Colors & Spinner +# --------------------------------------------------------------------------- + +class Colors: + CYAN = "\033[36m" + GREEN = "\033[32m" + YELLOW = "\033[33m" + RED = "\033[31m" + GRAY = "\033[90m" + BLUE = "\033[34m" + RESET = "\033[0m" + BOLD = "\033[1m" + BRIGHT_GREEN = "\033[92m" + BRIGHT_CYAN = "\033[96m" + BRIGHT_WHITE = "\033[97m" + BRIGHT_YELLOW = "\033[93m" + BRIGHT_RED = "\033[91m" + + +class Spinner: + def __init__(self, message: str = "Building...", delay: float = 0.1, total: int = 0): + self.spinner = ['\u280b', '\u2819', '\u2839', '\u2838', '\u283c', '\u2834', '\u2826', '\u2827', '\u2807', '\u280f'] + self.delay = delay + self.message = message + self.running = False + self.thread = None + self.start_time = time.time() + + def update(self, current=None): + pass + + def _spin(self): + idx = 0 + _block = '\u28ff' + while self.running: + elapsed = time.time() - self.start_time + n_blocks = int(elapsed // 2) + time_blocks = f"{Colors.YELLOW}{_block * n_blocks}{Colors.RESET}" + spin_char = f"{Colors.YELLOW}{self.spinner[idx % len(self.spinner)]}{Colors.RESET}" + msg = f"{Colors.BOLD}{Colors.CYAN}{self.message}{Colors.RESET}" + time_str = f"{Colors.GREEN}{int(elapsed)}s{Colors.RESET}" + sys.stdout.write(f"\r{msg} {time_str} {time_blocks}{spin_char} ") + sys.stdout.flush() + idx += 1 + time.sleep(self.delay) + sys.stdout.write("\r" + " " * (len(self.message) + 80) + "\r") + sys.stdout.flush() + + def __enter__(self): + if sys.stdout.isatty(): + self.running = True + self.thread = threading.Thread(target=self._spin, daemon=True) + self.thread.start() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + if self.running: + self.running = False + if self.thread: + self.thread.join() + + +# --------------------------------------------------------------------------- +# Build configuration +# --------------------------------------------------------------------------- + +class BuildConfig: + def __init__(self, name: str, config: str, platform: str, dest_dir: str): + self.name = name + self.config = config + self.platform = platform + self.dest_dir = dest_dir + + +CONFIGS = { + 'x86-ansi': BuildConfig('x86-ansi', 'Release', 'Win32', 'x86-ansi'), + 'x86-unicode': BuildConfig('x86-unicode', 'Release Unicode', 'Win32', 'x86-unicode'), + 'x64-unicode': BuildConfig('x64-unicode', 'Release Unicode', 'x64', 'amd64-unicode'), +} + +DLL_NAME = 'nsResize.dll' +_VSWHERE = Path(r'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe') +_VS_RANGES = {'2026': '[18.0,19.0)', '2022': '[17.0,18.0)'} +_VS_TOOLSETS = {'2026': 'v145', '2022': 'v143'} + + +# --------------------------------------------------------------------------- +# MSBuild discovery +# --------------------------------------------------------------------------- + +def _find_msbuild_vswhere(vs_version: str) -> Optional[Tuple[Path, str, str]]: + if not _VSWHERE.exists(): + return None + to_try = ['2026', '2022'] if vs_version == 'auto' else [vs_version] + for ver in to_try: + if ver not in _VS_RANGES: + continue + try: + result = subprocess.run( + [str(_VSWHERE), '-version', _VS_RANGES[ver], '-latest', + '-requires', 'Microsoft.Component.MSBuild', + '-find', r'MSBuild\**\Bin\MSBuild.exe'], + capture_output=True, text=True, timeout=15, + ) + if result.returncode == 0: + lines = [ln.strip() for ln in result.stdout.splitlines() if ln.strip()] + if lines: + p = Path(lines[0]) + if p.exists(): + return p, _VS_TOOLSETS[ver], ver + except Exception: + pass + return None + + +def find_msbuild(vs_version: str = 'auto') -> Optional[Tuple[Path, str, str]]: + result = _find_msbuild_vswhere(vs_version) + if result: + return result + # Fallback: well-known paths + to_try = ['2026', '2022'] if vs_version == 'auto' else [vs_version] + for ver in to_try: + for edition in ('Community', 'Professional', 'Enterprise', 'BuildTools'): + p = Path(rf'C:\Program Files\Microsoft Visual Studio\{ver}\{edition}\MSBuild\Current\Bin\MSBuild.exe') + if p.exists(): + return p, _VS_TOOLSETS[ver], ver + return None + + +# --------------------------------------------------------------------------- +# Project paths & version +# --------------------------------------------------------------------------- + +def get_project_paths() -> Tuple[Path, Path, Path]: + script_dir = Path(__file__).parent.absolute() + project_dir = script_dir / 'src' + project_file = project_dir / 'nsResize.vcxproj' + dist_dir = script_dir / 'dist' + return project_dir, project_file, dist_dir + + +def get_plugins_dir() -> Optional[Path]: + """Return workspace-level plugins/ dir (4 levels above src/modules//).""" + p = Path(__file__).parent.parent.parent.parent / 'plugins' + return p if p.is_dir() else None + + +def read_version() -> str: + vf = Path(__file__).parent / 'VERSION' + try: + return vf.read_text(encoding='utf-8-sig').strip() + except Exception: + return '0.0.0' + + +# --------------------------------------------------------------------------- +# CPU info & build optimizations +# --------------------------------------------------------------------------- + +def get_cpu_info() -> dict: + try: + import psutil + logical = psutil.cpu_count(logical=True) + physical = psutil.cpu_count(logical=False) + return {'logical_cores': logical, 'physical_cores': physical, + 'has_hyperthreading': logical > physical, 'has_psutil': True} + except ImportError: + n = multiprocessing.cpu_count() + return {'logical_cores': n, 'physical_cores': n, + 'has_hyperthreading': False, 'has_psutil': False} + + +def get_optimal_threads() -> int: + info = get_cpu_info() + return info['physical_cores'] if info['has_hyperthreading'] and info['physical_cores'] > 1 else info['logical_cores'] + + +def get_build_optimizations() -> List[str]: + return [ + '/p:BuildInParallel=true', + '/p:MultiProcessorCompilation=true', + '/p:PreferredToolArchitecture=x64', + '/p:UseSharedCompilation=true', + '/nodeReuse:true', + '/p:GenerateResourceUsePreserializedResources=true', + ] + + +def get_memory_optimizations() -> List[str]: + try: + import psutil + gb = psutil.virtual_memory().total / (1024 ** 3) + if gb >= 16: + return ['/p:DisableFastUpToDateCheck=false', '/p:BuildProjectReferences=true', + '/p:UseCommonOutputDirectory=false'] + elif gb >= 8: + return ['/p:DisableFastUpToDateCheck=false'] + return [] + except ImportError: + return ['/p:DisableFastUpToDateCheck=false'] + + +def print_cpu_info(use_parallel: bool, use_optimizations: bool = True) -> None: + if not use_parallel: + print(f"{Colors.CYAN}Build mode: {Colors.RESET} {Colors.BOLD}Single-threaded{Colors.RESET}") + if use_optimizations: + print(f"{Colors.CYAN}Optimizations: {Colors.RESET} {Colors.GREEN}ENABLED{Colors.RESET} {Colors.GRAY}(memory, caching){Colors.RESET}") + return + + info = get_cpu_info() + optimal = get_optimal_threads() + print(f"{Colors.CYAN}Build mode: {Colors.RESET} {Colors.BRIGHT_WHITE}{'Parallel' if use_parallel else 'Sequential'}{Colors.RESET}") + print(f"{Colors.CYAN}Logical cores: {Colors.RESET} {Colors.BRIGHT_WHITE}{info['logical_cores']}{Colors.RESET}") + print(f"{Colors.CYAN}Physical cores: {Colors.RESET} {Colors.BRIGHT_WHITE}{info['physical_cores']}{Colors.RESET}") + if info['has_hyperthreading']: + print(f"{Colors.CYAN}Hyperthreading: {Colors.RESET} {Colors.BRIGHT_GREEN}ENABLED{Colors.RESET}") + print(f"{Colors.CYAN}Optimal threads: {Colors.RESET} {Colors.BRIGHT_WHITE}{optimal}{Colors.RESET} {Colors.GRAY}(using physical cores){Colors.RESET}") + else: + print(f"{Colors.CYAN}Hyperthreading: {Colors.RESET} {Colors.GRAY}NOT AVAILABLE{Colors.RESET}") + print(f"{Colors.CYAN}Optimal threads: {Colors.RESET} {Colors.BRIGHT_WHITE}{optimal}{Colors.RESET}") + print(f"{Colors.CYAN}MSBuild threads: {Colors.RESET} {Colors.BRIGHT_WHITE}{optimal}{Colors.RESET}") + if use_optimizations: + print(f"{Colors.CYAN}Optimizations: {Colors.RESET} {Colors.BRIGHT_GREEN}ENABLED{Colors.RESET} {Colors.GRAY}(parallel, memory, caching){Colors.RESET}") + try: + import psutil + gb = psutil.virtual_memory().total / (1024 ** 3) + print(f"{Colors.CYAN}Available memory: {Colors.RESET} {Colors.BRIGHT_WHITE}{gb:.1f} GB{Colors.RESET}") + except ImportError: + print(f"{Colors.CYAN}Available memory: {Colors.RESET} {Colors.GRAY}Unknown (install psutil){Colors.RESET}") + else: + print(f"{Colors.CYAN}Optimizations: {Colors.RESET} {Colors.BRIGHT_RED}DISABLED{Colors.RESET}") + if not info['has_psutil']: + print(f"{Colors.GRAY}Note: Install 'psutil' for detailed CPU/memory info (pip install psutil){Colors.RESET}") + print() + + +# --------------------------------------------------------------------------- +# Build helpers +# --------------------------------------------------------------------------- + +def format_time(seconds: float) -> str: + if seconds < 60: + return f"{seconds:.2f}s" + elif seconds < 3600: + return f"{int(seconds // 60)}m {seconds % 60:.1f}s" + return f"{int(seconds // 3600)}h {int((seconds % 3600) // 60)}m {seconds % 60:.0f}s" + + +def build_configuration( + msbuild_path: Path, + project_file: Path, + config: BuildConfig, + platform_toolset: str, + *, + rebuild: bool = True, + verbosity: str = 'quiet', + parallel: bool = True, + optimizations: bool = True, + counter: str = "", + capture_output: bool = False, +) -> Tuple[bool, float, str]: + optimal = get_optimal_threads() + cmd = [ + str(msbuild_path), + str(project_file), + f'/t:{"Rebuild" if rebuild else "Build"}', + f'/p:Configuration={config.config}', + f'/p:Platform={config.platform}', + f'/p:OutDir=Build\\{config.name}\\', + f'/p:IntDir=Build\\{config.name}\\obj\\', + '/p:WindowsTargetPlatformVersion=10.0', + f'/p:PlatformToolset={platform_toolset}', + f'/v:{verbosity}', + ] + if parallel: + cmd += [f'/maxcpucount:{optimal}', '/p:UseMultiToolTask=true', f'/p:CL_MPCount={optimal}'] + cmd += ['/p:Optimization=MaxSpeed', '/p:ExceptionHandling=Sync'] + if optimizations: + cmd += get_build_optimizations() + get_memory_optimizations() + + if not capture_output: + color = Colors.BRIGHT_YELLOW + print(f"\n{color}{'='*60}{Colors.RESET}") + label = f"[{counter}] " if counter else "" + print(f"{color}Building {Colors.BRIGHT_WHITE}{config.name:15s}{Colors.RESET} {color}{label}({'rebuild' if rebuild else 'incremental'}){Colors.RESET}") + print(f"{color}{'='*60}{Colors.RESET}") + + start = time.time() + try: + if capture_output: + result = subprocess.run(cmd, check=False, capture_output=True, text=True) + return result.returncode == 0, time.time() - start, result.stdout + result.stderr + else: + result = subprocess.run(cmd, check=False) + return result.returncode == 0, time.time() - start, "" + except Exception as e: + elapsed = time.time() - start + msg = f"ERROR: Build failed with exception: {e}" + if capture_output: + return False, elapsed, msg + print(msg) + return False, elapsed, "" + + +def copy_output(project_dir: Path, dist_dir: Path, config: BuildConfig) -> Tuple[bool, int, Optional[Path]]: + output_file = project_dir / 'Build' / config.name / DLL_NAME + if not output_file.exists(): + print(f"ERROR: DLL not found: {output_file}") + return False, 0, None + dest = dist_dir / config.dest_dir + dest.mkdir(parents=True, exist_ok=True) + try: + dst = dest / DLL_NAME + shutil.copy2(output_file, dst) + return True, output_file.stat().st_size, dst + except Exception as e: + print(f"ERROR: Failed to copy {config.name}: {e}") + return False, 0, None + + +def copy_to_plugins(dist_dir: Path, config: BuildConfig, plugins_dir: Path) -> Optional[Path]: + """Copy DLL from dist// to plugins//.""" + src = dist_dir / config.dest_dir / DLL_NAME + if not src.exists(): + return None + dst_dir = plugins_dir / config.dest_dir + dst_dir.mkdir(parents=True, exist_ok=True) + dst = dst_dir / DLL_NAME + shutil.copy2(src, dst) + return dst + + +def clean_build_artifacts(project_dir: Path, configs: List[BuildConfig]) -> None: + print(f"\n{Colors.CYAN}Cleaning build artifacts...{Colors.RESET}") + build_base = project_dir / 'Build' + if not build_base.exists(): + return + for cfg in configs: + d = build_base / cfg.name + if d.exists(): + try: + robust_rmtree(d) + print(f" {Colors.GRAY}- Cleaned:{Colors.RESET} {cfg.name}") + except Exception as e: + print(f" {Colors.RED}- Failed to clean {cfg.name}: {e}{Colors.RESET}") + try: + if build_base.exists() and not any(build_base.iterdir()): + build_base.rmdir() + except Exception: + pass + print(f"{Colors.BRIGHT_GREEN}Build artifacts cleaned successfully.{Colors.RESET}") + + +def print_available_configurations(project_file: Path) -> None: + try: + tree = ET.parse(project_file) + root = tree.getroot() + ns = {'ms': 'http://schemas.microsoft.com/developer/msbuild/2003'} + configs = [] + for ig in root.findall('.//ms:ItemGroup', ns): + for pc in ig.findall('ms:ProjectConfiguration', ns): + inc = pc.get('Include', '') + parts = inc.split('|') + if len(parts) == 2: + configs.append(tuple(parts)) + print("Available configurations in project:") + print("-" * 60) + from collections import defaultdict + grouped: dict = defaultdict(list) + for cfg, plat in configs: + grouped[cfg].append(plat) + for cfg_name in sorted(grouped): + print(f" {cfg_name:25s} - {', '.join(sorted(grouped[cfg_name]))}") + print(f"\nTotal: {len(configs)} configuration(s)\n") + except Exception as e: + print(f"Could not parse project file: {e}") + + +_print_lock = threading.Lock() + + +def _build_configs_parallel( + msbuild_path: Path, + project_file: Path, + configs: List[BuildConfig], + platform_toolset: str, + *, + rebuild: bool, + verbosity: str, + parallel: bool, + optimizations: bool, + project_dir: Path, + dist_dir: Path, +) -> list: + n = len(configs) + print(f"\nParallel: launching {n} builds simultaneously...") + print("=" * 50) + total_start = time.time() + results_by_idx: dict = {} + idx_lock = threading.Lock() + + def _build_one(idx: int, config: BuildConfig): + import contextlib + import io as _io + success, build_time, captured = build_configuration( + msbuild_path, project_file, config, platform_toolset, + rebuild=rebuild, verbosity=verbosity, + parallel=parallel, optimizations=optimizations, + capture_output=True, + ) + copy_buf = _io.StringIO() + if success: + with contextlib.redirect_stdout(copy_buf): + copy_ok, file_size, dest_path = copy_output(project_dir, dist_dir, config) + else: + copy_ok, file_size, dest_path = False, 0, None + + with _print_lock: + sys.stdout.write("\r" + " " * 80 + "\r") + sys.stdout.flush() + tag = "OK" if (success and copy_ok) else "FAILED" + size_str = f"{file_size:,} bytes" if file_size > 0 else "N/A" + copy_out = copy_buf.getvalue() + if copy_out.strip(): + print(copy_out.rstrip()) + if captured.strip(): + lines = [ln for ln in captured.splitlines() if "MSBuild" not in ln and "Copyright" not in ln] + filtered = "\n".join(lines).strip() + if filtered: + print(filtered) + color = Colors.BRIGHT_GREEN if (success and copy_ok) else Colors.RED + print(f"{color}[{tag}]{Colors.RESET} {config.name} ({format_time(build_time)}) {size_str}") + if dest_path: + print(f" -> {dest_path}") + + with idx_lock: + results_by_idx[idx] = (config, success and copy_ok, build_time, file_size, dest_path) + + with Spinner("Building configurations...", total=n) as s: + with ThreadPoolExecutor(max_workers=n) as executor: + futures = {executor.submit(_build_one, i, cfg): i for i, cfg in enumerate(configs)} + for fut in as_completed(futures): + s.update() + exc = fut.exception() + if exc: + idx = futures[fut] + with _print_lock: + print(f"ERROR: worker for config index {idx} raised: {exc}") + with idx_lock: + results_by_idx[idx] = (configs[idx], False, 0.0, 0, None) + + wall = time.time() - total_start + print(f"\nAll {n} configs finished in {format_time(wall)} (wall clock)") + return [results_by_idx[i] for i in range(n)] + + +# --------------------------------------------------------------------------- +# main +# --------------------------------------------------------------------------- + +def main() -> int: + parser = argparse.ArgumentParser(description='Build nsResize plugin for NSIS') + parser.add_argument('--configs', nargs='+', + choices=list(CONFIGS.keys()) + ['all'], default=None) + parser.add_argument('--config', # singular alias used by CI + choices=list(CONFIGS.keys()) + ['all'], default=None) + parser.add_argument('--rebuild', action='store_true', default=True) + parser.add_argument('--no-rebuild', action='store_false', dest='rebuild') + parser.add_argument('--parallel', action='store_true', default=True) + parser.add_argument('--no-parallel', action='store_false', dest='parallel') + parser.add_argument('--verbosity', + choices=['quiet', 'minimal', 'normal', 'detailed', 'diagnostic'], + default='quiet') + parser.add_argument('--clean', action='store_true', default=False) + parser.add_argument('--list', action='store_true') + parser.add_argument('--list-project', action='store_true') + parser.add_argument('--no-optimizations', action='store_true') + parser.add_argument('--vs-version', choices=['auto', '2026', '2022'], default='auto') + parser.add_argument('--final', action='store_true', default=False, + help='Force rebuild + clean (pre-release build)') + parser.add_argument('--install-dir', type=Path, default=None, + help='Copy built DLLs to this directory (in / subdirs)') + args = parser.parse_args() + + # Merge --config (singular) into --configs list + if args.config and not args.configs: + args.configs = [args.config] + + if args.final: + args.rebuild = True + args.clean = True + + project_dir, project_file, dist_dir = get_project_paths() + + if args.list_project: + print_available_configurations(project_file) + return 0 + + if args.list: + for name, config in CONFIGS.items(): + print(f" {name:15s} -> {config.config} / {config.platform}") + return 0 + + if not project_file.exists(): + print(f"ERROR: project file not found: {project_file}", file=sys.stderr) + return 3 + + msbuild_result = find_msbuild(args.vs_version) + if not msbuild_result: + print("ERROR: MSBuild not found. Install Visual Studio 2022 or 2026.", file=sys.stderr) + return 2 + msbuild_path, platform_toolset, vs_version_name = msbuild_result + + version = read_version() + + if not args.configs: + configs_to_build = list(CONFIGS.values()) + elif 'all' in args.configs: + configs_to_build = list(CONFIGS.values()) + else: + configs_to_build = [CONFIGS[n] for n in args.configs] + + # Banner + print(f"{Colors.BOLD}{Colors.BRIGHT_CYAN}{'='*60}") + print(f"Building nsResize plugin v{version} - {Colors.BRIGHT_WHITE}{len(configs_to_build)}{Colors.RESET} {Colors.BOLD}{Colors.BRIGHT_CYAN}configuration(s)") + print(f"{'='*60}{Colors.RESET}") + print(f"{Colors.CYAN}VS: {Colors.RESET} {Colors.BRIGHT_WHITE}{vs_version_name}{Colors.RESET} {Colors.GRAY}({platform_toolset}){Colors.RESET}") + print(f"{Colors.CYAN}MSBuild: {Colors.RESET} {Colors.BRIGHT_WHITE}{msbuild_path}{Colors.RESET}") + print(f"{Colors.CYAN}Project: {Colors.RESET} {Colors.BRIGHT_WHITE}{project_file}{Colors.RESET}") + print(f"{Colors.CYAN}Dist: {Colors.RESET} {Colors.BRIGHT_WHITE}{dist_dir}{Colors.RESET}") + print(f"{Colors.CYAN}Rebuild: {Colors.RESET} {Colors.BRIGHT_WHITE}{args.rebuild}{Colors.RESET}") + print(f"{Colors.CYAN}Verbosity: {Colors.RESET} {Colors.BRIGHT_WHITE}{args.verbosity}{Colors.RESET}") + print() + + use_optimizations = not args.no_optimizations + print_cpu_info(args.parallel, use_optimizations) + + build_results = [] + total_start = time.time() + + if args.parallel and len(configs_to_build) > 1: + build_results = _build_configs_parallel( + msbuild_path, project_file, configs_to_build, platform_toolset, + rebuild=args.rebuild, verbosity=args.verbosity, + parallel=True, optimizations=use_optimizations, + project_dir=project_dir, dist_dir=dist_dir, + ) + else: + for i, config in enumerate(configs_to_build, 1): + success, b_time, _ = build_configuration( + msbuild_path, project_file, config, platform_toolset, + rebuild=args.rebuild, verbosity=args.verbosity, + parallel=args.parallel, optimizations=use_optimizations, + counter=f"{i}/{len(configs_to_build)}", + ) + if success: + ok, size, path = copy_output(project_dir, dist_dir, config) + build_results.append((config, ok, b_time, size, path)) + else: + build_results.append((config, False, b_time, 0, None)) + + total_time = time.time() - total_start + all_success = all(res[1] for res in build_results) + + print(f"\n{Colors.BOLD}{Colors.BRIGHT_GREEN if all_success else Colors.RED}{'='*50}") + print("ALL BUILDS SUCCESSFUL!" if all_success else "SOME BUILDS FAILED!") + print(f"{'='*50}{Colors.RESET}") + + # Copy to workspace plugins/ dir if present + plugins_dir = get_plugins_dir() + plugins_copied = [] + if plugins_dir and all_success: + for cfg, ok, _, _, _ in build_results: + if ok: + dst = copy_to_plugins(dist_dir, cfg, plugins_dir) + if dst: + plugins_copied.append(str(dst)) + if plugins_copied: + print(f"\n{Colors.CYAN}Installed to plugins/:{Colors.RESET}") + for p in plugins_copied: + print(f" {Colors.GRAY}->{Colors.RESET} {p}") + + if args.install_dir and all_success: + print(f"\n{Colors.CYAN}Installing to {args.install_dir}...{Colors.RESET}") + for cfg, ok, _, _, path in build_results: + if ok and path: + dest = args.install_dir / cfg.dest_dir + dest.mkdir(parents=True, exist_ok=True) + shutil.copy2(path, dest / DLL_NAME) + print(f" {Colors.GRAY}- Installed:{Colors.RESET} {dest / DLL_NAME}") + + print(f"\n{Colors.BOLD}{Colors.BRIGHT_CYAN}{'-'*50}") + print(f"Build Summary - VS {vs_version_name} ({platform_toolset}):") + for cfg, ok, b_time, size, _ in build_results: + color = Colors.GREEN if ok else Colors.RED + tag = "OK " if ok else "FAIL" + print(f" {color}{tag}{Colors.RESET} {cfg.name:15s} - {format_time(b_time):8s} - {size:11,d} bytes") + print(f"{Colors.BOLD}{Colors.BRIGHT_CYAN}{'-'*50}{Colors.RESET}") + print(f"Total time: {format_time(total_time)}\n") + + if args.clean: + clean_build_artifacts(project_dir, configs_to_build) + + return 0 if all_success else 1 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/dist/amd64-unicode/nsResize.dll b/dist/amd64-unicode/nsResize.dll new file mode 100644 index 0000000000000000000000000000000000000000..e38f8df83f8a448798e27f5ce74c249d118b8ff5 GIT binary patch literal 8192 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 literal 0 HcmV?d00001 diff --git a/dist/x86-ansi/nsResize.dll b/dist/x86-ansi/nsResize.dll new file mode 100644 index 0000000000000000000000000000000000000000..3fbbe1d3fc0109efe1e8763c2b5b551df92277bc GIT binary patch literal 6144 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 literal 0 HcmV?d00001 diff --git a/docs/Readme.txt b/docs/Readme.txt new file mode 100644 index 0000000..fe71746 --- /dev/null +++ b/docs/Readme.txt @@ -0,0 +1,108 @@ +nsResize NSIS plug-in + +Author: Stuart 'Afrow UK' Welch +Company: Afrow Soft Ltd +Date: 13th April 2013 +Version: 1.0.0.0 + +A small NSIS plug-in for moving or resizing windows/controls. + +See Examples\nsResize\*. + +------------------------------------------------------------------------ +Macros +------------------------------------------------------------------------ + +nsResize.nsh includes macros used by the LargeWindow example script. + +------------------------------------------------------------------------ +Functions +------------------------------------------------------------------------ + + nsResize::SetRTL $(^RTL) + + Sets right-to-left mode on or off. This is only needed when using the + nsResize::Set function. + +------------------------------------------------------------------------ + + nsResize::Set control_HWND x y width height + + Sets a control's new X and Y coordinates and a new width and height. + You may use the same unit values as nsDialogs. An empty string for x + or y will ensure the control is not moved. An empty string for width + or height will ensure the control is not resized. + +------------------------------------------------------------------------ + + nsResize::Add control_HWND x y width height + + Adds the given X, Y, width and height values. You may use the same + unit values as nsDialogs. Note that special treatment must be done for + right-to-left languages. + +------------------------------------------------------------------------ + + nsResize::Top control_HWND + + Moves a control to the top of the Z-order. + +------------------------------------------------------------------------ + + nsResize::GetPos control_HWND + Pop $X + Pop $Y + + Gets the control position in dialog units relative to its parent + window. + +------------------------------------------------------------------------ + + nsResize::GetPosPx control_HWND + Pop $X + Pop $Y + + Gets the control position in pixels relative to its parent window. + +------------------------------------------------------------------------ + + nsResize::GetSize control_HWND + Pop $Width + Pop $Height + + Gets the control size in dialog units. + +------------------------------------------------------------------------ + + nsResize::GetSizePx control_HWND + Pop $Width + Pop $Height + + Gets the control size in pixels. + +------------------------------------------------------------------------ +Change Log +------------------------------------------------------------------------ + +1.0.0.0 - 13th April 2013 +* First version. + +------------------------------------------------------------------------ +License +------------------------------------------------------------------------ + +This plug-in is provided 'as-is', without any express or implied +warranty. In no event will the author be held liable for any damages +arising from the use of this plug-in. + +Permission is granted to anyone to use this plug-in for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this plug-in must not be misrepresented; you must not + claim that you wrote the original plug-in. + If you use this plug-in in a product, an acknowledgment in the + product documentation would be appreciated but is not required. +2. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original plug-in. +3. This notice may not be removed or altered from any distribution. \ No newline at end of file diff --git a/examples/Circle.nsi b/examples/Circle.nsi new file mode 100644 index 0000000..a5896cb --- /dev/null +++ b/examples/Circle.nsi @@ -0,0 +1,44 @@ + +!addplugindir "n:\code\\Workspace\\Launchers\\plugins\\x86-unicode\\" +!include MUI2.nsh + +Name Circle +OutFile Circle.exe +RequestExecutionLevel user + +!insertmacro MUI_PAGE_WELCOME +Page Custom CirclePage_Show + +!insertmacro MUI_LANGUAGE English + +Var Label + +Function CirclePage_Show + + !insertmacro MUI_HEADER_TEXT `Circle` `Moves a label in a circle using a timer and absolute positioning` + + nsDialogs::Create 1018 + Pop $R0 + + ${NSD_CreateLabel} 100% 100% 20u 20u `` + Pop $Label + SetCtlColors $Label 0x000000 0xFF0000 + + StrCpy $R0 0 + ${NSD_CreateTimer} CirclePage_TimerTick 50 + + nsDialogs::Show + + ${NSD_KillTimer} CirclePage_TimerTick + +FunctionEnd + +Function CirclePage_TimerTick + + Math::Script `R0 = R0 + 0.1; R1 = 140 + sin(R0) * 140; R2 = 55 + cos(R0) * 50` + nsResize::Set $Label $R1u $R2u `` `` + +FunctionEnd + +Section +SectionEnd \ No newline at end of file diff --git a/examples/LargeWindow.nsi b/examples/LargeWindow.nsi new file mode 100644 index 0000000..7da47cb --- /dev/null +++ b/examples/LargeWindow.nsi @@ -0,0 +1,118 @@ +/* + This script uses the nsResize plug-in to resize your NSIS installer + and uninstaller windows. +*/ +!addplugindir "n:\code\\Workspace\\Launchers\\plugins\\x86-unicode\\" +!addincludedir "n:\code\\Workspace\\Launchers\\include\\" +!include MUI2.nsh +!include nsResize.nsh + +Name LargeWindow +OutFile LargeWindow.exe +RequestExecutionLevel user +InstallDir $EXEDIR +ShowInstDetails show + +; Increase the width and height by these units: +!define AddWidth 100u +!define AddHeight 75u + +; Resizing still works with MUI interface customisation. +!define MUI_LICENSEPAGE_RADIOBUTTONS +!define MUI_COMPONENTSPAGE_SMALLDESC +!define MUI_FINISHPAGE_NOAUTOCLOSE +!define MUI_FINISHPAGE_RUN $INSTDIR\Uninstall.exe + +; These will do the initial window resizing. +!define MUI_CUSTOMFUNCTION_GUIINIT myGUIInit +!define MUI_CUSTOMFUNCTION_UNGUIINIT un.myGUIInit + +; Each MUI install page must have a show function assigned. +!define MUI_PAGE_CUSTOMFUNCTION_SHOW WelcomePage_Show +!insertmacro MUI_PAGE_WELCOME +!define MUI_PAGE_CUSTOMFUNCTION_SHOW LicensePage_Show +!insertmacro MUI_PAGE_LICENSE `${__FILE__}` +!define MUI_PAGE_CUSTOMFUNCTION_SHOW DirectoryPage_Show +!insertmacro MUI_PAGE_DIRECTORY +!define MUI_PAGE_CUSTOMFUNCTION_SHOW ComponentsPage_Show +!insertmacro MUI_PAGE_COMPONENTS +!define MUI_PAGE_CUSTOMFUNCTION_SHOW InstFilesPage_Show +!insertmacro MUI_PAGE_INSTFILES +!define MUI_PAGE_CUSTOMFUNCTION_SHOW FinishPage_Show +!insertmacro MUI_PAGE_FINISH + +; Each MUI uninstall page must have a show function assigned. +!define MUI_PAGE_CUSTOMFUNCTION_SHOW un.WelcomePage_Show +!insertmacro MUI_UNPAGE_WELCOME +!define MUI_PAGE_CUSTOMFUNCTION_SHOW un.ConfirmPage_Show +!insertmacro MUI_UNPAGE_CONFIRM +!define MUI_PAGE_CUSTOMFUNCTION_SHOW un.InstFilesPage_Show +!insertmacro MUI_UNPAGE_INSTFILES +!define MUI_PAGE_CUSTOMFUNCTION_SHOW un.FinishPage_Show +!insertmacro MUI_UNPAGE_FINISH + +!insertmacro MUI_LANGUAGE English + +LangString SECTION_WRITE_UNINSTALLER ${LANG_ENGLISH} `This will write an uninstaller executable into the current installation directory. The uninstaller UI has also increased in size!` + +Function myGUIInit + ${nsResize_Window} ${AddWidth} ${AddHeight} +FunctionEnd + +Function WelcomePage_Show + ${nsResize_WelcomePage} ${AddWidth} ${AddHeight} +FunctionEnd + +Function LicensePage_Show + ${nsResize_LicensePage} ${AddWidth} ${AddHeight} +FunctionEnd + +Function DirectoryPage_Show + ${nsResize_DirectoryPage} ${AddWidth} ${AddHeight} +FunctionEnd + +Function ComponentsPage_Show + ${nsResize_ComponentsPage} ${AddWidth} ${AddHeight} +FunctionEnd + +Function InstFilesPage_Show + ${nsResize_InstFilesPage} ${AddWidth} ${AddHeight} +FunctionEnd + +Function FinishPage_Show + ${nsResize_FinishPage} ${AddWidth} ${AddHeight} +FunctionEnd + +Function un.myGUIInit + ${nsResize_Window} ${AddWidth} ${AddHeight} +FunctionEnd + +Function un.WelcomePage_Show + ${nsResize_WelcomePage} ${AddWidth} ${AddHeight} +FunctionEnd + +Function un.ConfirmPage_Show + ${nsResize_ConfirmPage} ${AddWidth} ${AddHeight} +FunctionEnd + +Function un.InstFilesPage_Show + ${nsResize_InstFilesPage} ${AddWidth} ${AddHeight} +FunctionEnd + +Function un.FinishPage_Show + ${nsResize_FinishPage} ${AddWidth} ${AddHeight} +FunctionEnd + +Section `Write uninstaller` SECTION_WRITE_UNINSTALLER + SectionIn RO + WriteUninstaller $INSTDIR\Uninstall.exe + MessageBox MB_OK|MB_ICONINFORMATION `Wrote: $INSTDIR\Uninstall.exe` +SectionEnd + +Section Uninstall + Delete $INSTDIR\Uninstall.exe +SectionEnd + +!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN + !insertmacro MUI_DESCRIPTION_TEXT ${SECTION_WRITE_UNINSTALLER} $(SECTION_WRITE_UNINSTALLER) +!insertmacro MUI_FUNCTION_DESCRIPTION_END \ No newline at end of file diff --git a/include/nsResize.nsh b/include/nsResize.nsh new file mode 100644 index 0000000..7573f72 --- /dev/null +++ b/include/nsResize.nsh @@ -0,0 +1,187 @@ +!ifndef __nsResize_NSH__ +!define __nsResize_NSH__ + +!macro __nsResize_AddParent Id X Y W H + GetDlgItem $R0 $HWNDPARENT ${Id} + nsResize::Add $R0 ${X} ${Y} ${W} ${H} +!macroend + +!macro __nsResize_AddChild Id X Y W H + GetDlgItem $R1 $R0 ${Id} + nsResize::Add $R1 ${X} ${Y} ${W} ${H} +!macroend + +!macro _nsResize_Window AddWidth AddHeight + + Push $R0 + nsResize::Add $HWNDPARENT 0 0 ${AddWidth} ${AddHeight} + ${If} $(^RTL) == 1 + !insertmacro __nsResize_AddParent 3 0 ${AddHeight} 0 0 + !insertmacro __nsResize_AddParent 1 0 ${AddHeight} 0 0 + !insertmacro __nsResize_AddParent 2 0 ${AddHeight} 0 0 + ${Else} + !insertmacro __nsResize_AddParent 3 ${AddWidth} ${AddHeight} 0 0 + !insertmacro __nsResize_AddParent 1 ${AddWidth} ${AddHeight} 0 0 + !insertmacro __nsResize_AddParent 2 ${AddWidth} ${AddHeight} 0 0 + !insertmacro __nsResize_AddParent 1039 ${AddWidth} 0 0 0 + ${EndIf} + !insertmacro __nsResize_AddParent 1018 0 0 ${AddWidth} ${AddHeight} + !insertmacro __nsResize_AddParent 1044 0 0 ${AddWidth} ${AddHeight} + !insertmacro __nsResize_AddParent 1035 0 ${AddHeight} ${AddWidth} 0 + !insertmacro __nsResize_AddParent 1036 0 0 ${AddWidth} 0 + !insertmacro __nsResize_AddParent 1045 0 ${AddHeight} ${AddWidth} 0 + !insertmacro __nsResize_AddParent 1256 0 ${AddHeight} ${AddWidth} 0 + !insertmacro __nsResize_AddParent 1028 0 ${AddHeight} ${AddWidth} 0 + !insertmacro __nsResize_AddParent 1034 0 0 ${AddWidth} 0 + !insertmacro __nsResize_AddParent 1037 0 0 ${AddWidth} 0 + !insertmacro __nsResize_AddParent 1038 0 0 ${AddWidth} 0 + Pop $R0 + +!macroend +!define nsResize_Window `!insertmacro _nsResize_Window` + +!macro _nsResize_WelcomePage AddWidth AddHeight + + nsResize::Add $mui.WelcomePage.Image 0 0 0 ${AddHeight} + ${If} $(^RTL) == 1 + nsResize::Add $mui.WelcomePage.Title -${AddWidth} 0 ${AddWidth} 0 + nsResize::Add $mui.WelcomePage.Text -${AddWidth} 0 ${AddWidth} ${AddHeight} + ${Else} + nsResize::Add $mui.WelcomePage.Title 0 0 ${AddWidth} 0 + nsResize::Add $mui.WelcomePage.Text 0 0 ${AddWidth} ${AddHeight} + ${EndIf} + +!macroend +!define nsResize_WelcomePage `!insertmacro _nsResize_WelcomePage` + +!macro _nsResize_LicensePage AddWidth AddHeight + + Push $R0 + Push $R1 + FindWindow $R0 `#32770` `` $HWNDPARENT + nsResize::Add $R0 0 0 ${AddWidth} ${AddHeight} + !insertmacro __nsResize_AddChild 1040 0 0 ${AddWidth} 0 + !insertmacro __nsResize_AddChild 1000 0 0 ${AddWidth} ${AddHeight} + !insertmacro __nsResize_AddChild 1006 0 ${AddHeight} ${AddWidth} 0 + !insertmacro __nsResize_AddChild 1034 0 ${AddHeight} ${AddWidth} 0 + !insertmacro __nsResize_AddChild 1035 0 ${AddHeight} ${AddWidth} 0 + Pop $R1 + Pop $R0 + +!macroend +!define nsResize_LicensePage `!insertmacro _nsResize_LicensePage` + +!macro _nsResize_DirectoryPage AddWidth AddHeight + + Push $R0 + Push $R1 + FindWindow $R0 `#32770` `` $HWNDPARENT + nsResize::Add $R0 0 0 ${AddWidth} ${AddHeight} + !insertmacro __nsResize_AddChild 1019 0 0 ${AddWidth} 0 + ${If} $(^RTL) == 1 + !insertmacro __nsResize_AddChild 1024 ${AddWidth} 0 0 0 + !insertmacro __nsResize_AddChild 1023 ${AddWidth} 0 0 0 + ${Else} + !insertmacro __nsResize_AddChild 1001 ${AddWidth} 0 0 0 + !insertmacro __nsResize_AddChild 1008 ${AddWidth} 0 0 0 + ${EndIf} + !insertmacro __nsResize_AddChild 1006 0 0 ${AddWidth} 0 + !insertmacro __nsResize_AddChild 1020 0 0 ${AddWidth} 0 + Pop $R1 + Pop $R0 + +!macroend +!define nsResize_DirectoryPage `!insertmacro _nsResize_DirectoryPage` + +!macro _nsResize_ComponentsPage AddWidth AddHeight + + Push $R0 + Push $R1 + FindWindow $R0 `#32770` `` $HWNDPARENT + nsResize::Add $R0 0 0 ${AddWidth} ${AddHeight} + !insertmacro __nsResize_AddChild 1017 0 0 ${AddWidth} ${AddHeight} + ${If} $(^RTL) == 1 + !insertmacro __nsResize_AddChild 1022 ${AddWidth} 0 0 ${AddHeight} + !insertmacro __nsResize_AddChild 1021 ${AddWidth} 0 0 0 + !insertmacro __nsResize_AddChild 1023 ${AddWidth} ${AddHeight} 0 0 + ${Else} + !insertmacro __nsResize_AddChild 1022 0 0 0 ${AddHeight} + !insertmacro __nsResize_AddChild 1023 0 ${AddHeight} 0 0 + ${EndIf} + !insertmacro __nsResize_AddChild 1006 0 0 ${AddWidth} 0 + !insertmacro __nsResize_AddChild 1032 0 0 ${AddWidth} ${AddHeight} + !ifndef MUI_COMPONENTSPAGE_NODESC + !ifdef MUI_COMPONENTSPAGE_SMALLDESC + !insertmacro __nsResize_AddChild 1042 0 ${AddHeight} ${AddWidth} 0 + !insertmacro __nsResize_AddChild 1043 0 ${AddHeight} ${AddWidth} 0 + !else + !insertmacro __nsResize_AddChild 1042 ${AddWidth} 0 0 ${AddHeight} + !insertmacro __nsResize_AddChild 1043 ${AddWidth} 0 0 ${AddHeight} + !endif + !endif + Pop $R1 + Pop $R0 + +!macroend +!define nsResize_ComponentsPage `!insertmacro _nsResize_ComponentsPage` + +!macro _nsResize_InstFilesPage AddWidth AddHeight + + Push $R0 + Push $R1 + FindWindow $R0 `#32770` `` $HWNDPARENT + nsResize::Add $R0 0 0 ${AddWidth} ${AddHeight} + !insertmacro __nsResize_AddChild 1004 0 0 ${AddWidth} 0 + !insertmacro __nsResize_AddChild 1006 0 0 ${AddWidth} 0 + !insertmacro __nsResize_AddChild 1016 0 0 ${AddWidth} ${AddHeight} + Pop $R1 + Pop $R0 + +!macroend +!define nsResize_InstFilesPage `!insertmacro _nsResize_InstFilesPage` + +!macro _nsResize_FinishPage AddWidth AddHeight + + nsResize::Add $mui.FinishPage.Image 0 0 0 ${AddHeight} + ${If} $(^RTL) == 1 + nsResize::Add $mui.FinishPage.Title -${AddWidth} 0 ${AddWidth} 0 + nsResize::Add $mui.FinishPage.Text -${AddWidth} 0 ${AddWidth} ${AddHeight} + ${Else} + nsResize::Add $mui.FinishPage.Title 0 0 ${AddWidth} 0 + nsResize::Add $mui.FinishPage.Text 0 0 ${AddWidth} ${AddHeight} + ${EndIf} + !ifdef MUI_FINISHPAGE_RUN_VARIABLES + nsResize::Add $mui.FinishPage.Run 0 ${AddHeight} 0 0 + !endif + !ifdef MUI_FINISHPAGE_SHOREADME_VARAIBLES ;not a typo! + nsResize::Add $mui.FinishPage.ShowReadme 0 ${AddHeight} 0 0 + !endif + !ifdef MUI_FINISHPAGE_LINK + nsResize::Add $mui.FinishPage.Link 0 ${AddHeight} 0 0 + !endif + !ifdef MUI_FINISHPAGE_REBOOT_VARIABLES + nsResize::Add $mui.FinishPage.RebootNow 0 ${AddHeight} 0 0 + nsResize::Add $mui.FinishPage.RebootLater 0 ${AddHeight} 0 0 + !endif + +!macroend +!define nsResize_FinishPage `!insertmacro _nsResize_FinishPage` + +!macro _nsResize_ConfirmPage AddWidth AddHeight + + Push $R0 + Push $R1 + FindWindow $R0 `#32770` `` $HWNDPARENT + nsResize::Add $R0 0 0 ${AddWidth} ${AddHeight} + ${If} $(^RTL) == 1 + !insertmacro __nsResize_AddChild 1029 ${AddWidth} 0 0 0 + ${EndIf} + !insertmacro __nsResize_AddChild 1000 0 0 ${AddWidth} 0 + !insertmacro __nsResize_AddChild 1006 0 0 ${AddWidth} 0 + Pop $R1 + Pop $R0 + +!macroend +!define nsResize_ConfirmPage `!insertmacro _nsResize_ConfirmPage` + +!endif \ No newline at end of file diff --git a/src/api.h b/src/api.h new file mode 100644 index 0000000..0d6b55e --- /dev/null +++ b/src/api.h @@ -0,0 +1,85 @@ +/* + * apih + * + * This file is a part of NSIS. + * + * Copyright (C) 1999-2019 Nullsoft and Contributors + * + * Licensed under the zlib/libpng license (the "License"); + * you may not use this file except in compliance with the License. + * + * Licence details can be found in the file COPYING. + * + * This software is provided 'as-is', without any express or implied + * warranty. + */ + +#ifndef _NSIS_EXEHEAD_API_H_ +#define _NSIS_EXEHEAD_API_H_ + +// Starting with NSIS 2.42, you can check the version of the plugin API in exec_flags->plugin_api_version +// The format is 0xXXXXYYYY where X is the major version and Y is the minor version (MAKELONG(y,x)) +// When doing version checks, always remember to use >=, ex: if (pX->exec_flags->plugin_api_version >= NSISPIAPIVER_1_0) {} + +#define NSISPIAPIVER_1_0 0x00010000 +#define NSISPIAPIVER_CURR NSISPIAPIVER_1_0 + +// NSIS Plug-In Callback Messages +enum NSPIM +{ + NSPIM_UNLOAD, // This is the last message a plugin gets, do final cleanup + NSPIM_GUIUNLOAD, // Called after .onGUIEnd +}; + +// Prototype for callbacks registered with extra_parameters->RegisterPluginCallback() +// Return NULL for unknown messages +// Should always be __cdecl for future expansion possibilities +typedef UINT_PTR (*NSISPLUGINCALLBACK)(enum NSPIM); + +// extra_parameters data structure containing other interesting stuff +// besides the stack, variables and HWND passed on to plug-ins. +typedef struct +{ + int autoclose; // SetAutoClose + int all_user_var; // SetShellVarContext: User context = 0, Machine context = 1 + int exec_error; // IfErrors + int abort; // IfAbort + int exec_reboot; // IfRebootFlag (NSIS_SUPPORT_REBOOT) + int reboot_called; // NSIS_SUPPORT_REBOOT + int XXX_cur_insttype; // Deprecated + int plugin_api_version; // Plug-in ABI. See NSISPIAPIVER_CURR (Note: used to be XXX_insttype_changed) + int silent; // IfSilent (NSIS_CONFIG_SILENT_SUPPORT) + int instdir_error; // GetInstDirError + int rtl; // 1 if $LANGUAGE is a RTL language + int errlvl; // SetErrorLevel + int alter_reg_view; // SetRegView: Default View = 0, Alternative View = (sizeof(void*) > 4 ? KEY_WOW64_32KEY : KEY_WOW64_64KEY) + int status_update; // SetDetailsPrint +} exec_flags_t; + +#ifndef NSISCALL +# define NSISCALL __stdcall +#endif +#if !defined(_WIN32) && !defined(LPTSTR) +# define LPTSTR TCHAR* +#endif + +typedef struct { + exec_flags_t *exec_flags; + int (NSISCALL *ExecuteCodeSegment)(int, HWND); + void (NSISCALL *validate_filename)(LPTSTR); + int (NSISCALL *RegisterPluginCallback)(HMODULE, NSISPLUGINCALLBACK); // returns 0 on success, 1 if already registered and < 0 on errors +} extra_parameters; + +// Definitions for page showing plug-ins +// See Ui.c to understand better how they're used + +// sent to the outer window to tell it to go to the next inner window +#define WM_NOTIFY_OUTER_NEXT (WM_USER+0x8) + +// custom pages should send this message to let NSIS know they're ready +#define WM_NOTIFY_CUSTOM_READY (WM_USER+0xd) + +// sent as wParam with WM_NOTIFY_OUTER_NEXT when user cancels - heed its warning +#define NOTIFY_BYE_BYE 'x' + +#endif /* _NSIS_EXEHEAD_API_H_ */ diff --git a/src/build_pluginapi_x64.cmd b/src/build_pluginapi_x64.cmd new file mode 100644 index 0000000..797b1eb --- /dev/null +++ b/src/build_pluginapi_x64.cmd @@ -0,0 +1,7 @@ +@echo off +call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" +cd /d %~dp0 +cl /c /O2 /DUNICODE /D_UNICODE /GS- /TC pluginapi.c /Fo:pluginapi_x64.obj +lib /OUT:nsis_unicode\pluginapi_x64.lib pluginapi_x64.obj +del pluginapi_x64.obj +echo Done! diff --git a/src/build_pluginapi_x86.cmd b/src/build_pluginapi_x86.cmd new file mode 100644 index 0000000..9334f83 --- /dev/null +++ b/src/build_pluginapi_x86.cmd @@ -0,0 +1,15 @@ +@echo off +call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars32.bat" +cd /d %~dp0 + +REM Build Unicode version +cl /c /O2 /DUNICODE /D_UNICODE /GS- /TC pluginapi.c /Fo:pluginapi_unicode.obj +lib /OUT:nsis_unicode\pluginapi.lib pluginapi_unicode.obj +del pluginapi_unicode.obj + +REM Build ANSI version +cl /c /O2 /GS- /TC pluginapi.c /Fo:pluginapi_ansi.obj +lib /OUT:nsis_ansi\pluginapi.lib pluginapi_ansi.obj +del pluginapi_ansi.obj + +echo Done! diff --git a/src/nsResize.c b/src/nsResize.c new file mode 100644 index 0000000..106b320 --- /dev/null +++ b/src/nsResize.c @@ -0,0 +1,257 @@ +/* + +nsResize NSIS plug-in + +Author: Stuart 'Afrow UK' Welch +Company: Afrow Soft Ltd. +Website: http://www.afrowsoft.co.uk +E-mail: afrowuk@afrowsoft.co.uk +Date: 13th April 2013 +Version: 1.0.0.0 + +A small NSIS plug-in for resizing windows. Uses code from nsDialogs for unit/RTL conversion. + +*/ + +#include +#include "nsResize.h" + +#include "pluginapi.h" + +HANDLE g_hInstance; +HWND g_hWndParent; +BOOL g_rtl; + +BOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) +{ + g_hInstance = hInst; + return TRUE; +} + +static UINT_PTR PluginCallback(enum NSPIM msg) +{ + return 0; +} + +static int ConvertPlacement(TCHAR* str, int total, BOOL height, BOOL add) +{ + TCHAR unit = *CharPrev(str, str + lstrlen(str)); + int x = myatoi(str); + + if (unit == TEXT('%')) + { + if (x < 0 && !add) + return MulDiv(total, 100 + x, 100); + return MulDiv(total, x, 100); + } + + if (unit == TEXT('u')) + { + RECT r; + r.left = r.top = x; + r.right = r.bottom = 0; + MapDialogRect(g_hWndParent, &r); + + if (height) + return x >= 0 || add ? r.top : total + r.top; + return x >= 0 || add ? r.left : total + r.left; + } + + if (x < 0 && !add) + return total + x; + return x; +} + +static void ConvertPosToRTL(int *x, int width, int dialogWidth) +{ + if (!g_rtl) + return; + + *x = dialogWidth - width - *x; +} + +static BOOL PopPlacement(HWND hWndControl, int* x, int* y, int* width, int* height, UINT* uFlags, BOOL add) +{ + RECT dialogRect; + int dialogWidth; + int dialogHeight; + TCHAR buf[1024]; + + GetClientRect(GetParent(hWndControl), &dialogRect); + dialogWidth = dialogRect.right; + dialogHeight = dialogRect.bottom; + + if (popstringn(buf, 1024)) + return FALSE; + + if (!*buf) + *uFlags |= SWP_NOMOVE; + else + *x = ConvertPlacement(buf, dialogWidth, FALSE, add); + + if (popstringn(buf, 1024)) + return FALSE; + + if (!*buf) + *uFlags |= SWP_NOMOVE; + else + *y = ConvertPlacement(buf, dialogHeight, TRUE, add); + + if (popstringn(buf, 1024)) + return FALSE; + + if (!*buf) + *uFlags |= SWP_NOSIZE; + else + *width = ConvertPlacement(buf, dialogWidth, FALSE, add); + + if (popstringn(buf, 1024)) + return FALSE; + + if (!*buf) + *uFlags |= SWP_NOSIZE; + else + *height = ConvertPlacement(buf, dialogHeight, TRUE, add); + + if (*uFlags & SWP_NOMOVE) + *x = *y = 0; + + if (*uFlags & SWP_NOSIZE) + *width = *height = 0; + + if (!add) + { + if (*uFlags & SWP_NOSIZE) + { + GetClientRect(hWndControl, &dialogRect); + ConvertPosToRTL(x, dialogRect.right, dialogWidth); + } + else + { + ConvertPosToRTL(x, *width, dialogWidth); + } + } + + return TRUE; +} + +static void GetRect(HWND hWnd, RECT* r) +{ + GetWindowRect(hWnd, r); + MapWindowPoints(NULL, GetParent(hWnd), (LPPOINT)r, 2); +} + +NSISFUNC(SetRTL) +{ + DLL_INIT(); + extra->RegisterPluginCallback((HMODULE)g_hInstance, PluginCallback); + g_rtl = (BOOL)popint(); +} + +NSISFUNC(Set) +{ + DLL_INIT(); + { + HWND hWndCtl = (HWND)popintptr(); + if (IsWindow(hWndCtl)) + { + int x, y, width, height; + UINT uFlags = SWP_NOZORDER; + if (PopPlacement(hWndCtl, &x, &y, &width, &height, &uFlags, FALSE)) + SetWindowPos(hWndCtl, NULL, x, y, width, height, uFlags); + } + } +} + +NSISFUNC(Add) +{ + DLL_INIT(); + { + HWND hWndCtl = (HWND)popintptr(); + if (IsWindow(hWndCtl)) + { + int x, y, width, height; + UINT uFlags = SWP_NOZORDER; + if (PopPlacement(hWndCtl, &x, &y, &width, &height, &uFlags, TRUE)) + { + RECT pos; + GetRect(hWndCtl, &pos); + SetWindowPos(hWndCtl, NULL, pos.left + x, pos.top + y, (pos.right - pos.left) + width, (pos.bottom - pos.top) + height, uFlags); + } + } + } +} + +NSISFUNC(Top) +{ + DLL_INIT(); + { + HWND hWndCtl = (HWND)popintptr(); + if (IsWindow(hWndCtl)) + { + SetWindowPos(hWndCtl, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE); + } + } +} + +NSISFUNC(GetPosPx) +{ + DLL_INIT(); + { + HWND hWndCtl = (HWND)popintptr(); + if (IsWindow(hWndCtl)) + { + RECT pos; + GetRect(hWndCtl, &pos); + MapDialogRect(g_hWndParent, &pos); + pushint(pos.top); + pushint(pos.left); + } + } +} + +NSISFUNC(GetPos) +{ + DLL_INIT(); + { + HWND hWndCtl = (HWND)popintptr(); + if (IsWindow(hWndCtl)) + { + RECT pos; + GetRect(hWndCtl, &pos); + pushint(pos.top); + pushint(pos.left); + } + } +} + +NSISFUNC(GetSizePx) +{ + DLL_INIT(); + { + HWND hWndCtl = (HWND)popintptr(); + if (IsWindow(hWndCtl)) + { + RECT pos; + GetRect(hWndCtl, &pos); + MapDialogRect(g_hWndParent, &pos); + pushint(pos.bottom); + pushint(pos.right); + } + } +} + +NSISFUNC(GetSize) +{ + DLL_INIT(); + { + HWND hWndCtl = (HWND)popintptr(); + if (IsWindow(hWndCtl)) + { + RECT pos; + GetRect(hWndCtl, &pos); + pushint(pos.bottom); + pushint(pos.right); + } + } +} \ No newline at end of file diff --git a/src/nsResize.h b/src/nsResize.h new file mode 100644 index 0000000..b02450f --- /dev/null +++ b/src/nsResize.h @@ -0,0 +1,7 @@ +#ifndef __NsThread_H__ +#define __NsThread_H__ + +#define NSISFUNC(name) void __declspec(dllexport) name(HWND hWndParent, int string_size, TCHAR* variables, stack_t** stacktop, extra_parameters* extra) +#define DLL_INIT() { EXDLL_INIT(); g_hWndParent = hWndParent; } + +#endif \ No newline at end of file diff --git a/src/nsResize.rc b/src/nsResize.rc new file mode 100644 index 0000000000000000000000000000000000000000..4781c3420c3513e5072f76c07fb834ce065f6c70 GIT binary patch literal 4644 zcmdUyTTdE66vxkVlYWOSzSPtzs1NOI2PWcXnpZ{WfQR|Jt%Gi|o|ec5Qv@*?^~ML-uOy4Qymr)?t;mH;l+yc4J+hKKn!F z277&G;f?sZ<~w2~YHhm(X478U3%G2#d5zsJryZah4mLA`ziD4B! z+{#w5svX#|m8@!_BHw|(WLUisqziLoN7!ZB#!al$vv24> z#5%G=1+72vK1S<2ddeOJ_P2SaJ@T{MjOKJT&$d3fS{*uol$l=YN27{s!w?JV{xkzEaF?%5%6<+G|x z235(gJiP0~SCJvQKN-vvkPFo$sUjk*gJgj>aRs_Fh_c zTz}<1Z!ux1vMj?Rt>$OAKbm$wu2jC_WbBglB{A)*|3u1p674K0(V~3TTt#k>U#c>G zs7j<&oY`m#K3c815|>62Y8__X{<`A_XxzgeVLxb6BR}iF6v*?kyM@nKOY=kWOxW?0 zurKTgh-31)Fa;A4!5hR~cs%Sm^h-owl?qh(D~v$>(AAD3q&uaCl*#=F>i4#T2a8T0 zMP>8h=1_GjP;shcNm9-UB~KyufPX_R9$=@(;j=M#-BjY5#3&)3r@f1e8oOhhiM1+p zH0Ma@;kIYaeZ5RV1j_R;Z3?GhuHi>@IkG_pxpl|gQboG#VyB?_@3po;fth zK0J%im0#5}^qJM&Pg`$Vn@z0NVJyO<2>q6$E>GR!BSlw>zGe8}rP&kpG + + + + Debug + Win32 + + + Release Unicode + Win32 + + + Release Unicode + x64 + + + Release + Win32 + + + + {C5988C24-9154-4DE8-92E3-DF89FBFA72EC} + nsResize + + + + DynamicLibrary + true + MultiByte + v110 + + + DynamicLibrary + false + true + MultiByte + v110 + + + DynamicLibrary + false + true + Unicode + v110 + + + DynamicLibrary + false + true + Unicode + v143 + + + + + + + + + + + + + + + + + + + $(SolutionDir)..\..\Unicode\Plugins\ + false + + + $(SolutionDir)..\..\Unicode\Plugins\x64\ + false + + + $(SolutionDir)..\..\Plugins\ + false + + + $(SolutionDir)..\..\Plugins\ + + + + Level3 + Disabled + + + true + nsis_ansi\pluginapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + DllMain + libc.lib + + + + + Level3 + MaxSpeed + true + true + false + + + true + true + nsis_ansi\pluginapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + true + libc.lib + false + DllMain + false + + + + + Level3 + MaxSpeed + true + true + false + + + false + true + true + nsis_unicode\pluginapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + true + libc.lib + DllMain + false + + + + + Level3 + MaxSpeed + true + true + false + + + false + true + true + nsis_unicode\pluginapi_x64.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + true + libc.lib + DllMain + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/nsResize.vcxproj.filters b/src/nsResize.vcxproj.filters new file mode 100644 index 0000000..864d061 --- /dev/null +++ b/src/nsResize.vcxproj.filters @@ -0,0 +1,35 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + + + Header Files + + + Header Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/src/nsis_tchar.h b/src/nsis_tchar.h new file mode 100644 index 0000000..cc861b7 --- /dev/null +++ b/src/nsis_tchar.h @@ -0,0 +1,229 @@ +/* + * nsis_tchar.h + * + * This file is a part of NSIS. + * + * Copyright (C) 1999-2019 Nullsoft and Contributors + * + * This software is provided 'as-is', without any express or implied + * warranty. + * + * For Unicode support by Jim Park -- 08/30/2007 + */ + +// Jim Park: Only those we use are listed here. + +#pragma once + +#ifdef _UNICODE + +#ifndef _T +#define __T(x) L ## x +#define _T(x) __T(x) +#define _TEXT(x) __T(x) +#endif + +#ifndef _TCHAR_DEFINED +#define _TCHAR_DEFINED +#if !defined(_NATIVE_WCHAR_T_DEFINED) && !defined(_WCHAR_T_DEFINED) +typedef unsigned short TCHAR; +#else +typedef wchar_t TCHAR; +#endif +#endif + + +// program +#define _tenviron _wenviron +#define __targv __wargv + +// printfs +#define _ftprintf fwprintf +#define _sntprintf _snwprintf +#if (defined(_MSC_VER) && (_MSC_VER<=1310||_MSC_FULL_VER<=140040310)) || defined(__MINGW32__) +# define _stprintf swprintf +#else +# define _stprintf _swprintf +#endif +#define _tprintf wprintf +#define _vftprintf vfwprintf +#define _vsntprintf _vsnwprintf +#if defined(_MSC_VER) && (_MSC_VER<=1310) +# define _vstprintf vswprintf +#else +# define _vstprintf _vswprintf +#endif + +// scanfs +#define _tscanf wscanf +#define _stscanf swscanf + +// string manipulations +#define _tcscat wcscat +#define _tcschr wcschr +#define _tcsclen wcslen +#define _tcscpy wcscpy +#define _tcsdup _wcsdup +#define _tcslen wcslen +#define _tcsnccpy wcsncpy +#define _tcsncpy wcsncpy +#define _tcsrchr wcsrchr +#define _tcsstr wcsstr +#define _tcstok wcstok + +// string comparisons +#define _tcscmp wcscmp +#define _tcsicmp _wcsicmp +#define _tcsncicmp _wcsnicmp +#define _tcsncmp wcsncmp +#define _tcsnicmp _wcsnicmp + +// upper / lower +#define _tcslwr _wcslwr +#define _tcsupr _wcsupr +#define _totlower towlower +#define _totupper towupper + +// conversions to numbers +#define _tcstoi64 _wcstoi64 +#define _tcstol wcstol +#define _tcstoul wcstoul +#define _tstof _wtof +#define _tstoi _wtoi +#define _tstoi64 _wtoi64 +#define _ttoi _wtoi +#define _ttoi64 _wtoi64 +#define _ttol _wtol + +// conversion from numbers to strings +#define _itot _itow +#define _ltot _ltow +#define _i64tot _i64tow +#define _ui64tot _ui64tow + +// file manipulations +#define _tfopen _wfopen +#define _topen _wopen +#define _tremove _wremove +#define _tunlink _wunlink + +// reading and writing to i/o +#define _fgettc fgetwc +#define _fgetts fgetws +#define _fputts fputws +#define _gettchar getwchar + +// directory +#define _tchdir _wchdir + +// environment +#define _tgetenv _wgetenv +#define _tsystem _wsystem + +// time +#define _tcsftime wcsftime + +#else // ANSI + +#ifndef _T +#define _T(x) x +#define _TEXT(x) x +#endif + +#ifndef _TCHAR_DEFINED +#define _TCHAR_DEFINED +typedef char TCHAR; +#endif + +// program +#define _tenviron environ +#define __targv __argv + +// printfs +#define _ftprintf fprintf +#define _sntprintf _snprintf +#define _stprintf sprintf +#define _tprintf printf +#define _vftprintf vfprintf +#define _vsntprintf _vsnprintf +#define _vstprintf vsprintf + +// scanfs +#define _tscanf scanf +#define _stscanf sscanf + +// string manipulations +#define _tcscat strcat +#define _tcschr strchr +#define _tcsclen strlen +#define _tcscnlen strnlen +#define _tcscpy strcpy +#define _tcsdup _strdup +#define _tcslen strlen +#define _tcsnccpy strncpy +#define _tcsrchr strrchr +#define _tcsstr strstr +#define _tcstok strtok + +// string comparisons +#define _tcscmp strcmp +#define _tcsicmp _stricmp +#define _tcsncmp strncmp +#define _tcsncicmp _strnicmp +#define _tcsnicmp _strnicmp + +// upper / lower +#define _tcslwr _strlwr +#define _tcsupr _strupr + +#define _totupper toupper +#define _totlower tolower + +// conversions to numbers +#define _tcstol strtol +#define _tcstoul strtoul +#define _tstof atof +#define _tstoi atoi +#define _tstoi64 _atoi64 +#define _tstoi64 _atoi64 +#define _ttoi atoi +#define _ttoi64 _atoi64 +#define _ttol atol + +// conversion from numbers to strings +#define _i64tot _i64toa +#define _itot _itoa +#define _ltot _ltoa +#define _ui64tot _ui64toa + +// file manipulations +#define _tfopen fopen +#define _topen _open +#define _tremove remove +#define _tunlink _unlink + +// reading and writing to i/o +#define _fgettc fgetc +#define _fgetts fgets +#define _fputts fputs +#define _gettchar getchar + +// directory +#define _tchdir _chdir + +// environment +#define _tgetenv getenv +#define _tsystem system + +// time +#define _tcsftime strftime + +#endif + +// is functions (the same in Unicode / ANSI) +#define _istgraph isgraph +#define _istascii __isascii + +#define __TFILE__ _T(__FILE__) +#define __TDATE__ _T(__DATE__) +#define __TTIME__ _T(__TIME__) diff --git a/src/pluginapi.c b/src/pluginapi.c new file mode 100644 index 0000000..110f70e --- /dev/null +++ b/src/pluginapi.c @@ -0,0 +1,305 @@ +#include +#include "pluginapi.h" + +#ifndef COUNTOF +#define COUNTOF(a) (sizeof(a)/sizeof(a[0])) +#endif + +// minimal tchar.h emulation +#ifndef _T +# define _T TEXT +#endif +#if !defined(TCHAR) && !defined(_TCHAR_DEFINED) +# ifdef UNICODE +# define TCHAR WCHAR +# else +# define TCHAR char +# endif +#endif + +#define isvalidnsisvarindex(varnum) ( ((unsigned int)(varnum)) < (__INST_LAST) ) + +unsigned int g_stringsize; +stack_t **g_stacktop; +LPTSTR g_variables; + +// utility functions (not required but often useful) + +int NSISCALL popstring(LPTSTR str) +{ + stack_t *th; + if (!g_stacktop || !*g_stacktop) return 1; + th=(*g_stacktop); + if (str) lstrcpy(str,th->text); + *g_stacktop = th->next; + GlobalFree((HGLOBAL)th); + return 0; +} + +int NSISCALL popstringn(LPTSTR str, int maxlen) +{ + stack_t *th; + if (!g_stacktop || !*g_stacktop) return 1; + th=(*g_stacktop); + if (str) lstrcpyn(str,th->text,maxlen?maxlen:g_stringsize); + *g_stacktop = th->next; + GlobalFree((HGLOBAL)th); + return 0; +} + +void NSISCALL pushstring(LPCTSTR str) +{ + stack_t *th; + if (!g_stacktop) return; + th=(stack_t*)GlobalAlloc(GPTR,(sizeof(stack_t)+(g_stringsize)*sizeof(*str))); + lstrcpyn(th->text,str,g_stringsize); + th->next=*g_stacktop; + *g_stacktop=th; +} + +LPTSTR NSISCALL getuservariable(const int varnum) +{ + if (!isvalidnsisvarindex(varnum)) return NULL; + return g_variables+varnum*g_stringsize; +} + +void NSISCALL setuservariable(const int varnum, LPCTSTR var) +{ + if (var && isvalidnsisvarindex(varnum)) + lstrcpy(g_variables + varnum*g_stringsize, var); +} + +#ifdef UNICODE +int NSISCALL PopStringA(LPSTR ansiStr) +{ + LPWSTR wideStr = (LPWSTR) GlobalAlloc(GPTR, g_stringsize*sizeof(WCHAR)); + int rval = popstring(wideStr); + WideCharToMultiByte(CP_ACP, 0, wideStr, -1, ansiStr, g_stringsize, NULL, NULL); + GlobalFree((HGLOBAL)wideStr); + return rval; +} + +int NSISCALL PopStringNA(LPSTR ansiStr, int maxlen) +{ + int realLen = maxlen ? maxlen : g_stringsize; + LPWSTR wideStr = (LPWSTR) GlobalAlloc(GPTR, realLen*sizeof(WCHAR)); + int rval = popstringn(wideStr, realLen); + WideCharToMultiByte(CP_ACP, 0, wideStr, -1, ansiStr, realLen, NULL, NULL); + GlobalFree((HGLOBAL)wideStr); + return rval; +} + +void NSISCALL PushStringA(LPCSTR ansiStr) +{ + LPWSTR wideStr = (LPWSTR) GlobalAlloc(GPTR, g_stringsize*sizeof(WCHAR)); + MultiByteToWideChar(CP_ACP, 0, ansiStr, -1, wideStr, g_stringsize); + pushstring(wideStr); + GlobalFree((HGLOBAL)wideStr); + return; +} + +void NSISCALL GetUserVariableW(const int varnum, LPWSTR wideStr) +{ + lstrcpyW(wideStr, getuservariable(varnum)); +} + +void NSISCALL GetUserVariableA(const int varnum, LPSTR ansiStr) +{ + LPWSTR wideStr = getuservariable(varnum); + WideCharToMultiByte(CP_ACP, 0, wideStr, -1, ansiStr, g_stringsize, NULL, NULL); +} + +void NSISCALL SetUserVariableA(const int varnum, LPCSTR ansiStr) +{ + if (ansiStr && isvalidnsisvarindex(varnum)) + { + LPWSTR wideStr = g_variables + varnum * g_stringsize; + MultiByteToWideChar(CP_ACP, 0, ansiStr, -1, wideStr, g_stringsize); + } +} + +#else +// ANSI defs +int NSISCALL PopStringW(LPWSTR wideStr) +{ + LPSTR ansiStr = (LPSTR) GlobalAlloc(GPTR, g_stringsize); + int rval = popstring(ansiStr); + MultiByteToWideChar(CP_ACP, 0, ansiStr, -1, wideStr, g_stringsize); + GlobalFree((HGLOBAL)ansiStr); + return rval; +} + +int NSISCALL PopStringNW(LPWSTR wideStr, int maxlen) +{ + int realLen = maxlen ? maxlen : g_stringsize; + LPSTR ansiStr = (LPSTR) GlobalAlloc(GPTR, realLen); + int rval = popstringn(ansiStr, realLen); + MultiByteToWideChar(CP_ACP, 0, ansiStr, -1, wideStr, realLen); + GlobalFree((HGLOBAL)ansiStr); + return rval; +} + +void NSISCALL PushStringW(LPWSTR wideStr) +{ + LPSTR ansiStr = (LPSTR) GlobalAlloc(GPTR, g_stringsize); + WideCharToMultiByte(CP_ACP, 0, wideStr, -1, ansiStr, g_stringsize, NULL, NULL); + pushstring(ansiStr); + GlobalFree((HGLOBAL)ansiStr); +} + +void NSISCALL GetUserVariableW(const int varnum, LPWSTR wideStr) +{ + LPSTR ansiStr = getuservariable(varnum); + MultiByteToWideChar(CP_ACP, 0, ansiStr, -1, wideStr, g_stringsize); +} + +void NSISCALL GetUserVariableA(const int varnum, LPSTR ansiStr) +{ + lstrcpyA(ansiStr, getuservariable(varnum)); +} + +void NSISCALL SetUserVariableW(const int varnum, LPCWSTR wideStr) +{ + if (wideStr && isvalidnsisvarindex(varnum)) + { + LPSTR ansiStr = g_variables + varnum * g_stringsize; + WideCharToMultiByte(CP_ACP, 0, wideStr, -1, ansiStr, g_stringsize, NULL, NULL); + } +} +#endif + +// playing with integers + +INT_PTR NSISCALL nsishelper_str_to_ptr(LPCTSTR s) +{ + INT_PTR v=0; + if (*s == _T('0') && (s[1] == _T('x') || s[1] == _T('X'))) + { + s++; + for (;;) + { + int c=*(++s); + if (c >= _T('0') && c <= _T('9')) c-=_T('0'); + else if (c >= _T('a') && c <= _T('f')) c-=_T('a')-10; + else if (c >= _T('A') && c <= _T('F')) c-=_T('A')-10; + else break; + v<<=4; + v+=c; + } + } + else if (*s == _T('0') && s[1] <= _T('7') && s[1] >= _T('0')) + { + for (;;) + { + int c=*(++s); + if (c >= _T('0') && c <= _T('7')) c-=_T('0'); + else break; + v<<=3; + v+=c; + } + } + else + { + int sign=0; + if (*s == _T('-')) sign++; else s--; + for (;;) + { + int c=*(++s) - _T('0'); + if (c < 0 || c > 9) break; + v*=10; + v+=c; + } + if (sign) v = -v; + } + + return v; +} + +unsigned int NSISCALL myatou(LPCTSTR s) +{ + unsigned int v=0; + + for (;;) + { + unsigned int c=*s++; + if (c >= _T('0') && c <= _T('9')) c-=_T('0'); + else break; + v*=10; + v+=c; + } + return v; +} + +int NSISCALL myatoi_or(LPCTSTR s) +{ + int v=0; + if (*s == _T('0') && (s[1] == _T('x') || s[1] == _T('X'))) + { + s++; + for (;;) + { + int c=*(++s); + if (c >= _T('0') && c <= _T('9')) c-=_T('0'); + else if (c >= _T('a') && c <= _T('f')) c-=_T('a')-10; + else if (c >= _T('A') && c <= _T('F')) c-=_T('A')-10; + else break; + v<<=4; + v+=c; + } + } + else if (*s == _T('0') && s[1] <= _T('7') && s[1] >= _T('0')) + { + for (;;) + { + int c=*(++s); + if (c >= _T('0') && c <= _T('7')) c-=_T('0'); + else break; + v<<=3; + v+=c; + } + } + else + { + int sign=0; + if (*s == _T('-')) sign++; else s--; + for (;;) + { + int c=*(++s) - _T('0'); + if (c < 0 || c > 9) break; + v*=10; + v+=c; + } + if (sign) v = -v; + } + + // Support for simple ORed expressions + if (*s == _T('|')) + { + v |= myatoi_or(s+1); + } + + return v; +} + +INT_PTR NSISCALL popintptr() +{ + TCHAR buf[128]; + if (popstringn(buf,COUNTOF(buf))) + return 0; + return nsishelper_str_to_ptr(buf); +} + +int NSISCALL popint_or() +{ + TCHAR buf[128]; + if (popstringn(buf,COUNTOF(buf))) + return 0; + return myatoi_or(buf); +} + +void NSISCALL pushintptr(INT_PTR value) +{ + TCHAR buffer[30]; + wsprintf(buffer, sizeof(void*) > 4 ? _T("%Id") : _T("%d"), value); + pushstring(buffer); +} diff --git a/src/pluginapi.h b/src/pluginapi.h new file mode 100644 index 0000000..11ccfeb --- /dev/null +++ b/src/pluginapi.h @@ -0,0 +1,109 @@ +#ifndef ___NSIS_PLUGIN__H___ +#define ___NSIS_PLUGIN__H___ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "api.h" +#include "nsis_tchar.h" // BUGBUG: Why cannot our plugins use the compilers tchar.h? + +#ifndef NSISCALL +# define NSISCALL WINAPI +#endif + +#define EXDLL_INIT() { \ + g_stringsize=string_size; \ + g_stacktop=stacktop; \ + g_variables=variables; } + +typedef struct _stack_t { + struct _stack_t *next; +#ifdef UNICODE + WCHAR text[1]; // this should be the length of g_stringsize when allocating +#else + char text[1]; +#endif +} stack_t; + +enum +{ +INST_0, // $0 +INST_1, // $1 +INST_2, // $2 +INST_3, // $3 +INST_4, // $4 +INST_5, // $5 +INST_6, // $6 +INST_7, // $7 +INST_8, // $8 +INST_9, // $9 +INST_R0, // $R0 +INST_R1, // $R1 +INST_R2, // $R2 +INST_R3, // $R3 +INST_R4, // $R4 +INST_R5, // $R5 +INST_R6, // $R6 +INST_R7, // $R7 +INST_R8, // $R8 +INST_R9, // $R9 +INST_CMDLINE, // $CMDLINE +INST_INSTDIR, // $INSTDIR +INST_OUTDIR, // $OUTDIR +INST_EXEDIR, // $EXEDIR +INST_LANG, // $LANGUAGE +__INST_LAST +}; + +extern unsigned int g_stringsize; +extern stack_t **g_stacktop; +extern LPTSTR g_variables; + +void NSISCALL pushstring(LPCTSTR str); +void NSISCALL pushintptr(INT_PTR value); +#define pushint(v) pushintptr((INT_PTR)(v)) +int NSISCALL popstring(LPTSTR str); // 0 on success, 1 on empty stack +int NSISCALL popstringn(LPTSTR str, int maxlen); // with length limit, pass 0 for g_stringsize +INT_PTR NSISCALL popintptr(); +#define popint() ( (int) popintptr() ) +int NSISCALL popint_or(); // with support for or'ing (2|4|8) +INT_PTR NSISCALL nsishelper_str_to_ptr(LPCTSTR s); +#define myatoi(s) ( (int) nsishelper_str_to_ptr(s) ) // converts a string to an integer +unsigned int NSISCALL myatou(LPCTSTR s); // converts a string to an unsigned integer, decimal only +int NSISCALL myatoi_or(LPCTSTR s); // with support for or'ing (2|4|8) +LPTSTR NSISCALL getuservariable(const int varnum); +void NSISCALL setuservariable(const int varnum, LPCTSTR var); + +#ifdef UNICODE +#define PopStringW(x) popstring(x) +#define PushStringW(x) pushstring(x) +#define SetUserVariableW(x,y) setuservariable(x,y) + +int NSISCALL PopStringA(LPSTR ansiStr); +int NSISCALL PopStringNA(LPSTR ansiStr, int maxlen); +void NSISCALL PushStringA(LPCSTR ansiStr); +void NSISCALL GetUserVariableW(const int varnum, LPWSTR wideStr); +void NSISCALL GetUserVariableA(const int varnum, LPSTR ansiStr); +void NSISCALL SetUserVariableA(const int varnum, LPCSTR ansiStr); + +#else +// ANSI defs + +#define PopStringA(x) popstring(x) +#define PushStringA(x) pushstring(x) +#define SetUserVariableA(x,y) setuservariable(x,y) + +int NSISCALL PopStringW(LPWSTR wideStr); +void NSISCALL PushStringW(LPWSTR wideStr); +void NSISCALL GetUserVariableW(const int varnum, LPWSTR wideStr); +void NSISCALL GetUserVariableA(const int varnum, LPSTR ansiStr); +void NSISCALL SetUserVariableW(const int varnum, LPCWSTR wideStr); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif//!___NSIS_PLUGIN__H___ diff --git a/src/resource.h b/src/resource.h new file mode 100644 index 0000000..147df92 --- /dev/null +++ b/src/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by nsResize.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif