chore: add license file, add icons, images flow, update readme
@@ -614,6 +614,7 @@ dependencies = [
|
|||||||
"toml 1.1.4+spec-1.1.0",
|
"toml 1.1.4+spec-1.1.0",
|
||||||
"winit",
|
"winit",
|
||||||
"winreg",
|
"winreg",
|
||||||
|
"winresource",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5784,6 +5785,16 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winresource"
|
||||||
|
version = "0.1.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0986a8b1d586b7d3e4fe3d9ea39fb451ae22869dcea4aa109d287a374d866087"
|
||||||
|
dependencies = [
|
||||||
|
"toml 1.1.4+spec-1.1.0",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wit-bindgen"
|
name = "wit-bindgen"
|
||||||
version = "0.51.0"
|
version = "0.51.0"
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ winreg = "0.56.0"
|
|||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
slint-build = "1.16.1"
|
slint-build = "1.16.1"
|
||||||
|
winresource = "0.1.31"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Ku6epXBOCTuK
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -1,37 +1,84 @@
|
|||||||
## 🚀 Rust Browser Picker (Working Title)
|
<div align="center">
|
||||||
|
|
||||||
Легкий и мгновенный переключатель браузеров на Rust + Slint. Замена тяжеловесным аналогам, которые долго загружаются.
|
<img src="assets/icon.png" width="96" alt="bropicker">
|
||||||
|
|
||||||
## 🎯 Цель
|
# bropicker
|
||||||
|
|
||||||
Создать утилиту, которая перехватывает открытие ссылок и предлагает выбрать браузер в красивом UI. Главный приоритет — скорость запуска (Cold Start < 100ms).
|
Лёгкий переключатель браузеров: кликаешь ссылку — bropicker спрашивает, каким браузером её открыть.
|
||||||
|
|
||||||
## 🛠 Технологический стек
|
**Rust + Slint, нативный рендер без WebView, cold start ~110 ms.**
|
||||||
|
|
||||||
- Язык: [Rust](https://www.rust-lang.org/)
|
<a href="https://slint.dev"><img src="https://raw.githubusercontent.com/slint-ui/slint/master/logo/MadeWithSlint-logo-whitebg.png" alt="Made with Slint" height="28"></a>
|
||||||
- GUI: [Slint](https://slint.dev/) (Native-speed, Declarative UI)
|
|
||||||
- Запуск процессов: std::process::Command
|
|
||||||
|
|
||||||
## 📊 Текущий статус
|
<img src="docs/img/screenshot-dark.png" width="244" alt="Тёмная тема"> <img src="docs/img/screenshot-light.png" width="244" alt="Светлая тема">
|
||||||
|
|
||||||
- Frameless-окно с прозрачным фоном: карточка парит над рабочим столом со скруглёнными углами
|
</div>
|
||||||
- Тёмная и светлая темы (переключение кнопкой, env `BP_THEME=light`)
|
|
||||||
- Перетаскивание окна за хедер, Esc — закрыть, центрирование при старте
|
|
||||||
- Дизайн-макеты: [refs/dark.html](refs/dark.html) / [refs/light.html](refs/light.html)
|
|
||||||
|
|
||||||
## 🔧 Разработка
|
## Возможности
|
||||||
|
|
||||||
План работ и цикл визуальной разработки с ИИ-агентом — [docs/dev-workflow.md](docs/dev-workflow.md).
|
- Frameless-окно с прозрачностью, перетаскивание за хедер, Esc — закрыть
|
||||||
|
- Тёмная и светлая темы
|
||||||
|
- Автодетект установленных браузеров из реестра (Firefox, Chrome, Edge, Zen, Opera, Brave, Яндекс…)
|
||||||
|
- Запоминание выбора по домену: повторные ссылки открываются сразу, без вопроса
|
||||||
|
- Галочка «Всегда спрашивать» — пикер вызывается даже для запомненных доменов
|
||||||
|
- Несколько профилей одного браузера как отдельные записи (флаги + эмодзи-иконка)
|
||||||
|
- Конфиг в TOML, открывается кнопкой шестерёнки
|
||||||
|
|
||||||
|
## Сборка
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
cargo run # запуск
|
cargo build --release
|
||||||
tools/render-refs.ps1 # PNG-референсы из макетов
|
|
||||||
tools/run-and-shot.ps1 # собрать, снять скриншот окна
|
|
||||||
tools/run-and-shot.ps1 -Light # то же в светлой теме
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🗺 Дорожная карта
|
## Установка обработчиком ссылок
|
||||||
|
|
||||||
1. Список браузеров с логотипами, запуск выбранного браузера с URL (MVP)
|
```powershell
|
||||||
2. Регистрация в системе как браузер по умолчанию
|
tools\register.ps1 # скопирует сборку в %LOCALAPPDATA%\bropicker и откроет настройки Windows
|
||||||
3. config.toml + страница настроек
|
```
|
||||||
|
|
||||||
|
В открывшемся окне выбери **bropicker** для HTTP и HTTPS. Откат: `tools\register.ps1 -Uninstall`.
|
||||||
|
|
||||||
|
## Конфигурация
|
||||||
|
|
||||||
|
`%APPDATA%\bropicker\config.toml` (шестерёнка в пикере открывает его в редакторе).
|
||||||
|
Пример с профилями и эмодзи: [docs/config.example.toml](docs/config.example.toml).
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[[browsers]]
|
||||||
|
name = "Chrome — Work"
|
||||||
|
path = 'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe'
|
||||||
|
flags = '--profile-directory="Default"'
|
||||||
|
emoji = "💼"
|
||||||
|
|
||||||
|
[remembered]
|
||||||
|
"github.com" = "Chrome — Work"
|
||||||
|
|
||||||
|
[settings]
|
||||||
|
remember_choice = true
|
||||||
|
always_ask = true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Разработка
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
just check # fmt + clippy -D warnings + тесты
|
||||||
|
just screenshots # перегенерация скриншотов README (тёмная + светлая)
|
||||||
|
just icons # перегенерация иконок из assets/icon.svg
|
||||||
|
just release # release-сборка
|
||||||
|
just register # установка/обновление обработчика ссылок
|
||||||
|
```
|
||||||
|
|
||||||
|
То же напрямую: `tools\check.ps1`, `tools\screenshots.ps1`, `tools\register.ps1`.
|
||||||
|
Планы и процесс: [docs/dev-workflow.md](docs/dev-workflow.md).
|
||||||
|
|
||||||
|
## Лицензия
|
||||||
|
|
||||||
|
Код bropicker — [MIT](LICENSE).
|
||||||
|
|
||||||
|
Интерфейс построен на [Slint](https://slint.dev), который используется по
|
||||||
|
[Slint Royalty-free License 2.0](licenses/Slint-Royalty-free-2.0.md)
|
||||||
|
Текст лицензии также входит
|
||||||
|
в состав установки (`licenses/`).
|
||||||
|
|
||||||
|
Логотипы браузеров — [alrrr/browser-logos](https://github.com/alrrr/browser-logos), MIT
|
||||||
|
([LICENSE](logos/LICENSE-browser-logos.txt)).
|
||||||
|
|||||||
|
After Width: | Height: | Size: 364 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 192 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 24 KiB |
@@ -0,0 +1,20 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" viewBox="0 0 256 256">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
|
||||||
|
<stop offset="0" stop-color="#3b82f6"/>
|
||||||
|
<stop offset="1" stop-color="#06b6d4"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="256" height="256" rx="58" fill="url(#bg)"/>
|
||||||
|
|
||||||
|
<g stroke="#ffffff" stroke-width="13" fill="none" stroke-linecap="round">
|
||||||
|
<circle cx="128" cy="122" r="62"/>
|
||||||
|
<ellipse cx="128" cy="122" rx="27" ry="62"/>
|
||||||
|
<line x1="66" y1="122" x2="190" y2="122"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<circle cx="128" cy="212" r="11" fill="#ffffff"/>
|
||||||
|
<circle cx="88" cy="212" r="11" fill="#ffffff" opacity="0.45"/>
|
||||||
|
<circle cx="168" cy="212" r="11" fill="#ffffff" opacity="0.45"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 750 B |
@@ -1,6 +1,9 @@
|
|||||||
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
slint_build::compile("ui/main.slint").unwrap();
|
slint_build::compile("ui/main.slint").unwrap();
|
||||||
|
|
||||||
|
if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("windows") {
|
||||||
|
let mut res = winresource::WindowsResource::new();
|
||||||
|
res.set_icon("assets/app.ico");
|
||||||
|
res.compile().unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -169,10 +169,18 @@ HTML-макеты → PNG: **headless Edge** (`--headless --screenshot`), он
|
|||||||
Цель <100 ms чуть не достигнута; варианты оптимизации, если захочется:
|
Цель <100 ms чуть не достигнута; варианты оптимизации, если захочется:
|
||||||
renderer-femtovg вместо skia (быстрее init GPU), отложенная загрузка
|
renderer-femtovg вместо skia (быстрее init GPU), отложенная загрузка
|
||||||
конфига. Пока считаем приемлемым
|
конфига. Пока считаем приемлемым
|
||||||
- [ ] Иконка exe (.ico через build.rs winres)
|
- [x] Иконка exe: `assets/icon.svg` (градиент + глобус + точки «выбора»)
|
||||||
- [ ] Выбрать лицензию проекта: GPLv3 (опенсорс) или Royalty-free Slint (закрытый десктоп,
|
→ `magick` → `assets/icon.png` + `assets/app.ico` (16-256) → `winresource`
|
||||||
атрибуция AboutSlint) — зафиксировать в README и LICENSE
|
в build.rs; проверено ExtractAssociatedIcon
|
||||||
- [ ] Скриншоты в README из tools/out
|
- [x] Лицензия MIT (`LICENSE`); Slint — по Royalty-free License 2.0 (маршрут (b):
|
||||||
|
бейдж Made-with-Slint в README, текст лицензии в `licenses/` и в
|
||||||
|
составе установки `%LOCALAPPDATA%\bropicker\licenses\`).
|
||||||
|
ВАЖНО: маршрут (b) требует публичной страницы с бейджем; если репо
|
||||||
|
станет приватным — добавить виджет AboutSlint в окно настроек
|
||||||
|
- [x] Скриншоты в README: `docs/img/screenshot-{dark,light}.png`;
|
||||||
|
перегенерация — `just screenshots` (tools/screenshots.ps1 → shot.ps1 -Card:
|
||||||
|
DPI-aware обрезка до карточки, клип на 2px внутрь срезает AA-бахрому
|
||||||
|
скруглений, углы прозрачные — чужих пикселей нет)
|
||||||
- [ ] Ручной сценарий приёмки: register → клик ссылки в другом приложении →
|
- [ ] Ручной сценарий приёмки: register → клик ссылки в другом приложении →
|
||||||
пикер → выбор → вкладка открылась; повторный клик того же домена при
|
пикер → выбор → вкладка открылась; повторный клик того же домена при
|
||||||
always_ask=off → сразу браузер
|
always_ask=off → сразу браузер
|
||||||
|
|||||||
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 26 KiB |
@@ -0,0 +1,26 @@
|
|||||||
|
default:
|
||||||
|
@just --list
|
||||||
|
|
||||||
|
# fmt + clippy -D warnings + тесты
|
||||||
|
check:
|
||||||
|
powershell -ExecutionPolicy Bypass -File tools/check.ps1
|
||||||
|
|
||||||
|
# release-сборка
|
||||||
|
release:
|
||||||
|
cargo build --release
|
||||||
|
|
||||||
|
# перегенерация скриншотов для README (тёмная + светлая, только карточка)
|
||||||
|
screenshots:
|
||||||
|
powershell -ExecutionPolicy Bypass -File tools/screenshots.ps1
|
||||||
|
|
||||||
|
# перегенерация иконок из assets/icon.svg (icon.png, app.ico, PNG-лестница)
|
||||||
|
icons:
|
||||||
|
powershell -ExecutionPolicy Bypass -File tools/icons.ps1
|
||||||
|
|
||||||
|
# установка/обновление обработчика ссылок (+ открытие настроек Windows)
|
||||||
|
register:
|
||||||
|
powershell -ExecutionPolicy Bypass -File tools/register.ps1
|
||||||
|
|
||||||
|
# снять регистрацию и удалить %LOCALAPPDATA%\bropicker
|
||||||
|
unregister:
|
||||||
|
powershell -ExecutionPolicy Bypass -File tools/register.ps1 -Uninstall
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# Slint Royalty-free Desktop, Mobile, and Web Applications License
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
## Preamble
|
||||||
|
|
||||||
|
Slint is a toolkit that can be used to build user interfaces for applications. Slint (hereafter referred to as **Software**) is made available under different licenses by SixtyFPS GmbH incorporated at Oranienburger Str. 44, 16540 Hohen Neuendorf, Germany (**SixtyFPS**). The **Slint Royalty-free Desktop, Mobile, and Web Applications License** is suitable for those who develop desktop, mobile, or web applications and do not want to use open source components under copyleft licenses.
|
||||||
|
|
||||||
|
## 1. Grant of Rights
|
||||||
|
|
||||||
|
SixtyFPS hereby grants You a world-wide, royalty-free, non-exclusive license to use, reproduce, make available, modify, display, perform, distribute the Software as part of a Desktop, Mobile, or Web Application.
|
||||||
|
|
||||||
|
A **Desktop Application** is a computer program that is designed to run on a general-purpose computer (PC or notebook), typically installed and executed locally on the computer's operating system.
|
||||||
|
|
||||||
|
A **Mobile Application** is a computer program that is designed to run on a general-purpose mobile computer (mobile phone or tablet), typically installed and executed locally on the computer's operating system.
|
||||||
|
|
||||||
|
A **Web Application** is a computer program that is designed to run in the sandbox environment provided by a web browser.
|
||||||
|
|
||||||
|
Desktop Application, Mobile Application, and Web Application are hereafter referred to as **Application**.
|
||||||
|
|
||||||
|
## 2. License Conditions - Attribution
|
||||||
|
|
||||||
|
You may distribute the Software as part of an Application, modified or unmodified, provided that You do either of the following:
|
||||||
|
|
||||||
|
(a) Display the [`AboutSlint`](https://docs.slint.dev/latest/docs/slint/reference/std-widgets/misc/aboutslint/) widget in an "About" screen or dialog that is accessible from the top level menu of the Application. In the absence of such a screen or dialog, display the widget in the "Splash Screen" of the Application.
|
||||||
|
|
||||||
|
(b) Display the [Slint attribution badge](https://github.com/slint-ui/slint/tree/master/logo/MadeWithSlint-logo-whitebg.png) on a public webpage, preferably where the binaries of your Application can be downloaded from, in such a way that it can be easily found by any visitor to that page.
|
||||||
|
|
||||||
|
## 3. Limitations
|
||||||
|
|
||||||
|
The License does not permit to distribute or make the Software publicly available alone and without integration into an Application. For this purpose you may use the Software under the GNU General Public License, version 3.
|
||||||
|
|
||||||
|
The License does not permit the use of the Software within Embedded Systems. An **Embedded System** is a computer system designed to perform a specific task within a larger mechanical or electrical system.
|
||||||
|
|
||||||
|
The License does not permit the distribution of Application that exposes the APIs, in part or in total, of the Software.
|
||||||
|
|
||||||
|
You may not remove or alter any license notices (including copyright notices, disclaimers of warranty, or limitations of liability) contained within the source code form of the Software.
|
||||||
|
|
||||||
|
## 4. Warranty and Liability
|
||||||
|
|
||||||
|
SixtyFPS is only liable for conflicting rights of third parties if SixtyFPS was aware of these rights without informing you. Unless required by applicable law or agreed to in writing, SixtyFPS provides the Software on an "as is" basis, without warranties or conditions of any kind, either express or implied, including, without limitation, any warranties or conditions of merchantability, or fitness for a particular purpose.
|
||||||
|
|
||||||
|
Unless required by law, SixtyFPS won't be liable for any direct, indirect, incidental, or consequential damages arising in any way out of the use of the Software.
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
$svg = "assets/icon.svg"
|
||||||
|
|
||||||
|
magick -background none $svg -resize 256x256 assets/icon.png
|
||||||
|
magick -background none $svg -define icon:auto-resize=256,128,64,48,32,24,16 assets/app.ico
|
||||||
|
|
||||||
|
foreach ($size in @(16, 24, 32, 44, 48, 50, 64, 88, 128, 150, 256, 512)) {
|
||||||
|
magick -background none $svg -resize "${size}x${size}" ("assets/icon-{0}.png" -f $size)
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "icons regenerated: icon.png, app.ico, icon-{16..512}.png"
|
||||||
@@ -29,6 +29,11 @@ Copy-Item $src (Join-Path $installDir "bropicker.exe") -Force
|
|||||||
Copy-Item (Join-Path $PSScriptRoot "..\logos") (Join-Path $installDir "logos") -Recurse -Force
|
Copy-Item (Join-Path $PSScriptRoot "..\logos") (Join-Path $installDir "logos") -Recurse -Force
|
||||||
Copy-Item (Join-Path $PSScriptRoot "..\icons") (Join-Path $installDir "icons") -Recurse -Force
|
Copy-Item (Join-Path $PSScriptRoot "..\icons") (Join-Path $installDir "icons") -Recurse -Force
|
||||||
|
|
||||||
|
$licensesDir = Join-Path $installDir "licenses"
|
||||||
|
New-Item -ItemType Directory -Force -Path $licensesDir | Out-Null
|
||||||
|
Copy-Item (Join-Path $PSScriptRoot "..\licenses\Slint-Royalty-free-2.0.md") $licensesDir -Force
|
||||||
|
Copy-Item (Join-Path $PSScriptRoot "..\logos\LICENSE-browser-logos.txt") $licensesDir -Force
|
||||||
|
|
||||||
$exe = Join-Path $installDir "bropicker.exe"
|
$exe = Join-Path $installDir "bropicker.exe"
|
||||||
Write-Host "Installed to: $exe"
|
Write-Host "Installed to: $exe"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
cargo build
|
||||||
|
if ($LASTEXITCODE -ne 0) { exit 1 }
|
||||||
|
|
||||||
|
New-Item -ItemType Directory -Force -Path docs\img | Out-Null
|
||||||
|
$exe = Join-Path $PWD "target\debug\bropicker.exe"
|
||||||
|
|
||||||
|
$proc = Start-Process -FilePath $exe -WorkingDirectory $PWD -PassThru
|
||||||
|
Start-Sleep -Milliseconds 1800
|
||||||
|
powershell -ExecutionPolicy Bypass -File "$PSScriptRoot\shot.ps1" -ProcId $proc.Id -Out docs\img\screenshot-dark.png -Card
|
||||||
|
Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue
|
||||||
|
if ($LASTEXITCODE -ne 0) { exit 1 }
|
||||||
|
|
||||||
|
$env:BP_THEME = "light"
|
||||||
|
$proc = Start-Process -FilePath $exe -WorkingDirectory $PWD -PassThru
|
||||||
|
Start-Sleep -Milliseconds 1800
|
||||||
|
powershell -ExecutionPolicy Bypass -File "$PSScriptRoot\shot.ps1" -ProcId $proc.Id -Out docs\img\screenshot-light.png -Card
|
||||||
|
Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue
|
||||||
|
Remove-Item Env:\BP_THEME -ErrorAction SilentlyContinue
|
||||||
|
if ($LASTEXITCODE -ne 0) { exit 1 }
|
||||||
|
|
||||||
|
Write-Host "screenshots updated: docs\img\screenshot-{dark,light}.png"
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
param(
|
param(
|
||||||
[string]$ProcName = "bropicker",
|
[string]$ProcName = "bropicker",
|
||||||
[int]$ProcId = 0,
|
[int]$ProcId = 0,
|
||||||
[string]$Out = "tools/out/app.png"
|
[string]$Out = "tools/out/app.png",
|
||||||
|
[switch]$Card
|
||||||
)
|
)
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
@@ -18,6 +19,7 @@ public class BpWin {
|
|||||||
[DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr hWnd, out RECT rect);
|
[DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr hWnd, out RECT rect);
|
||||||
[DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd);
|
[DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd);
|
||||||
[DllImport("user32.dll")] public static extern bool SetWindowPos(IntPtr hWnd, IntPtr after, int x, int y, int cx, int cy, uint flags);
|
[DllImport("user32.dll")] public static extern bool SetWindowPos(IntPtr hWnd, IntPtr after, int x, int y, int cx, int cy, uint flags);
|
||||||
|
[DllImport("user32.dll")] public static extern uint GetDpiForWindow(IntPtr hWnd);
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
public struct RECT { public int Left; public int Top; public int Right; public int Bottom; }
|
public struct RECT { public int Left; public int Top; public int Right; public int Bottom; }
|
||||||
}
|
}
|
||||||
@@ -64,11 +66,50 @@ if ($w -le 0 -or $h -le 0) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Add-Type -AssemblyName System.Drawing
|
Add-Type -AssemblyName System.Drawing
|
||||||
$bmp = New-Object System.Drawing.Bitmap($w, $h)
|
$full = New-Object System.Drawing.Bitmap($w, $h)
|
||||||
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
$g = [System.Drawing.Graphics]::FromImage($full)
|
||||||
$g.CopyFromScreen($rect.Left, $rect.Top, 0, 0, (New-Object System.Drawing.Size($w, $h)))
|
$g.CopyFromScreen($rect.Left, $rect.Top, 0, 0, (New-Object System.Drawing.Size($w, $h)))
|
||||||
$g.Dispose()
|
$g.Dispose()
|
||||||
|
|
||||||
|
if ($Card) {
|
||||||
|
$dpi = [BpWin]::GetDpiForWindow($found)
|
||||||
|
$scale = if ($dpi -gt 0) { $dpi / 96.0 } else { 1.0 }
|
||||||
|
$inset = [int][Math]::Round(20 * $scale)
|
||||||
|
$radius = [int][Math]::Round(16 * $scale)
|
||||||
|
|
||||||
|
$clipInset = 2
|
||||||
|
$cw = $w - 2 * $inset - 2 * $clipInset
|
||||||
|
$ch = $h - 2 * $inset - 2 * $clipInset
|
||||||
|
$srcX = $inset + $clipInset
|
||||||
|
$srcY = $inset + $clipInset
|
||||||
|
$radius = [Math]::Max(1, $radius - $clipInset)
|
||||||
|
|
||||||
|
$bmp = New-Object System.Drawing.Bitmap($cw, $ch)
|
||||||
|
$g2 = [System.Drawing.Graphics]::FromImage($bmp)
|
||||||
|
$g2.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::AntiAlias
|
||||||
|
$g2.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
|
||||||
|
|
||||||
|
$cardPath = New-Object System.Drawing.Drawing2D.GraphicsPath
|
||||||
|
$cardPath.AddArc(0, 0, $radius, $radius, 180, 90)
|
||||||
|
$cardPath.AddArc($cw - $radius, 0, $radius, $radius, 270, 90)
|
||||||
|
$cardPath.AddArc($cw - $radius, $ch - $radius, $radius, $radius, 0, 90)
|
||||||
|
$cardPath.AddArc(0, $ch - $radius, $radius, $radius, 90, 90)
|
||||||
|
$cardPath.CloseFigure()
|
||||||
|
|
||||||
|
$g2.SetClip($cardPath)
|
||||||
|
$srcRect = New-Object System.Drawing.Rectangle($srcX, $srcY, $cw, $ch)
|
||||||
|
$dstRect = New-Object System.Drawing.Rectangle(0, 0, $cw, $ch)
|
||||||
|
$g2.DrawImage($full, $dstRect, $srcRect, [System.Drawing.GraphicsUnit]::Pixel)
|
||||||
|
$g2.Dispose()
|
||||||
|
$cardPath.Dispose()
|
||||||
|
$full.Dispose()
|
||||||
|
|
||||||
|
$w = $cw
|
||||||
|
$h = $ch
|
||||||
|
} else {
|
||||||
|
$bmp = $full
|
||||||
|
}
|
||||||
|
|
||||||
$outDir = Split-Path -Parent (Join-Path $PWD $Out)
|
$outDir = Split-Path -Parent (Join-Path $PWD $Out)
|
||||||
New-Item -ItemType Directory -Force -Path $outDir | Out-Null
|
New-Item -ItemType Directory -Force -Path $outDir | Out-Null
|
||||||
$outAbs = [System.IO.Path]::GetFullPath((Join-Path $PWD $Out))
|
$outAbs = [System.IO.Path]::GetFullPath((Join-Path $PWD $Out))
|
||||||
|
|||||||