feat: app icon + Arch package (PKGBUILD + .desktop)
Add a desktop/taskbar identity and a system package for PeerSpeak. - assets/icons/peerspeak.svg: master app icon — the in-app mic glyph over a P2P mesh of peer nodes, Catppuccin Mocha palette. Rendered to the hicolor raster sizes (16..512) committed alongside it. - Window/taskbar icon: embed a 128x128 straight-RGBA blob and load it via iced from_rgba (keeps us off iced's heavy `image` feature). Set the Wayland/X11 app_id to "peerspeak" so compositors match the window to the .desktop launcher and show the icon natively. - packaging/peerspeak.desktop: launcher (StartupWMClass=peerspeak). - packaging/PKGBUILD: peerspeak-git VCS package — cargo --frozen build, --lib check, installs the binary, .desktop, and hicolor icons. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 606 B |
|
After Width: | Height: | Size: 994 B |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="256" height="256" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- PeerSpeak app icon: in-app mic glyph + P2P mesh nodes, Catppuccin Mocha. -->
|
||||
<defs>
|
||||
<linearGradient id="tile" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" stop-color="#1e1e2e"/>
|
||||
<stop offset="1" stop-color="#181825"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Rounded-square tile -->
|
||||
<rect x="20" y="20" width="216" height="216" rx="48" fill="url(#tile)"
|
||||
stroke="#313244" stroke-width="3"/>
|
||||
|
||||
<!-- P2P mesh: edges (under nodes + mic) -->
|
||||
<g stroke="#45475a" stroke-width="6" stroke-linecap="round" fill="none">
|
||||
<line x1="74" y1="74" x2="128" y2="128"/>
|
||||
<line x1="182" y1="74" x2="128" y2="128"/>
|
||||
<line x1="74" y1="182" x2="128" y2="128"/>
|
||||
<line x1="182" y1="182" x2="128" y2="128"/>
|
||||
<line x1="74" y1="74" x2="182" y2="74"/>
|
||||
<line x1="74" y1="182" x2="182" y2="182"/>
|
||||
</g>
|
||||
|
||||
<!-- P2P mesh: peer nodes -->
|
||||
<g fill="#b4befe">
|
||||
<circle cx="74" cy="74" r="11"/>
|
||||
<circle cx="182" cy="74" r="11"/>
|
||||
<circle cx="74" cy="182" r="11"/>
|
||||
<circle cx="182" cy="182" r="11"/>
|
||||
</g>
|
||||
|
||||
<!-- Microphone (hero) — same geometry as the in-app Mic icon, scaled 6.4x -->
|
||||
<g fill="none" stroke="#89b4fa" stroke-width="13"
|
||||
stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="108.8" y="68.8" width="38.4" height="70.4" rx="19.2"/>
|
||||
<path d="M 169.6 123.2 A 41.6 41.6 0 0 0 86.4 123.2"/>
|
||||
<line x1="128" y1="164.8" x2="128" y2="187.2"/>
|
||||
<line x1="105.6" y1="187.2" x2="150.4" y2="187.2"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,68 @@
|
||||
# Maintainer: mollusk <jitty+lc1iz0dc@protonmail.com>
|
||||
pkgname=peerspeak-git
|
||||
_pkgname=peerspeak
|
||||
pkgver=0.1.0
|
||||
pkgrel=1
|
||||
pkgdesc="Decentralized peer-to-peer voice chat (Rust/iroh/PipeWire/Opus/iced)"
|
||||
arch=('x86_64')
|
||||
url="https://gitbutter.xyz/mollusk/peerspeak"
|
||||
license=('custom')
|
||||
depends=('pipewire' 'opus')
|
||||
makedepends=('git' 'cargo' 'pkgconf')
|
||||
optdepends=('pixelpass: screen sharing inside a room'
|
||||
'mpv: screen-share viewer (vlc is used as a fallback)')
|
||||
provides=('peerspeak')
|
||||
conflicts=('peerspeak')
|
||||
options=('!lto' '!debug')
|
||||
source=("$_pkgname::git+ssh://git@gitbutter.xyz/mollusk/peerspeak.git")
|
||||
sha256sums=('SKIP')
|
||||
|
||||
pkgver() {
|
||||
cd "$srcdir/$_pkgname"
|
||||
# 0.1.0.r<commits>.g<short-sha> — sorts monotonically across rebuilds.
|
||||
printf '%s.r%s.g%s' \
|
||||
"$(awk -F'\"' '/^version =/{print $2; exit}' Cargo.toml)" \
|
||||
"$(git rev-list --count HEAD)" \
|
||||
"$(git rev-parse --short HEAD)"
|
||||
}
|
||||
|
||||
prepare() {
|
||||
cd "$srcdir/$_pkgname"
|
||||
# Vendor deps up front so build() can run --frozen (no surprise network).
|
||||
export CARGO_HOME="$srcdir/cargo-home"
|
||||
cargo fetch --locked --target "$(rustc -vV | sed -n 's/host: //p')"
|
||||
}
|
||||
|
||||
build() {
|
||||
cd "$srcdir/$_pkgname"
|
||||
export CARGO_HOME="$srcdir/cargo-home"
|
||||
export RUSTUP_TOOLCHAIN=stable
|
||||
export CARGO_TARGET_DIR=target
|
||||
cargo build --frozen --release --bin "$_pkgname"
|
||||
}
|
||||
|
||||
check() {
|
||||
cd "$srcdir/$_pkgname"
|
||||
export CARGO_HOME="$srcdir/cargo-home"
|
||||
export RUSTUP_TOOLCHAIN=stable
|
||||
# Library unit tests only — the integration suites bind real iroh/QUIC
|
||||
# endpoints and fail in a sandboxed/offline build environment.
|
||||
cargo test --frozen --release --lib
|
||||
}
|
||||
|
||||
package() {
|
||||
cd "$srcdir/$_pkgname"
|
||||
|
||||
install -Dm755 "target/release/$_pkgname" "$pkgdir/usr/bin/$_pkgname"
|
||||
install -Dm644 "packaging/$_pkgname.desktop" \
|
||||
"$pkgdir/usr/share/applications/$_pkgname.desktop"
|
||||
|
||||
# Hicolor icon theme (scalable SVG + the rendered raster sizes).
|
||||
install -Dm644 "assets/icons/$_pkgname.svg" \
|
||||
"$pkgdir/usr/share/icons/hicolor/scalable/apps/$_pkgname.svg"
|
||||
local s
|
||||
for s in 16 24 32 48 64 128 256 512; do
|
||||
install -Dm644 "assets/icons/$_pkgname-$s.png" \
|
||||
"$pkgdir/usr/share/icons/hicolor/${s}x${s}/apps/$_pkgname.png"
|
||||
done
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=PeerSpeak
|
||||
GenericName=P2P Voice Chat
|
||||
Comment=Decentralized peer-to-peer voice chat
|
||||
Exec=peerspeak
|
||||
Icon=peerspeak
|
||||
Terminal=false
|
||||
Categories=Network;Telephony;Chat;
|
||||
Keywords=voice;chat;voip;p2p;peer;
|
||||
StartupWMClass=peerspeak
|
||||
@@ -336,6 +336,15 @@ pub fn run_gui() -> iced::Result {
|
||||
// dock doesn't squeeze the controls column). Layout is responsive.
|
||||
size: init_size,
|
||||
position: iced::window::Position::Centered,
|
||||
// App/taskbar icon (mainly used on X11/XWayland; native Wayland takes
|
||||
// the icon from the .desktop file matched by app_id instead).
|
||||
icon: window_icon(),
|
||||
// app_id must match the .desktop basename so Wayland compositors
|
||||
// (e.g. KWin) attach our launcher icon to the window.
|
||||
platform_specific: iced::window::settings::PlatformSpecific {
|
||||
application_id: "peerspeak".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
// We save the final size ourselves on CloseRequested, then exit.
|
||||
exit_on_close_request: false,
|
||||
..Default::default()
|
||||
@@ -343,6 +352,14 @@ pub fn run_gui() -> iced::Result {
|
||||
.run()
|
||||
}
|
||||
|
||||
/// Build the window icon from an embedded 128×128 straight-RGBA blob rendered
|
||||
/// from `assets/icons/peerspeak.svg`. Using `from_rgba` (always available) keeps
|
||||
/// us off iced's heavy `image` feature — the blob is raw pixels, no decoder.
|
||||
fn window_icon() -> Option<iced::window::Icon> {
|
||||
const RGBA: &[u8] = include_bytes!("../../assets/icons/peerspeak-128.rgba");
|
||||
iced::window::icon::from_rgba(RGBA.to_vec(), 128, 128).ok()
|
||||
}
|
||||
|
||||
fn subscription(_state: &AppState) -> Subscription<AppMessage> {
|
||||
let core_sub = Subscription::run(core_subscription).map(AppMessage::UiEventReceived);
|
||||
let event_sub = iced::event::listen().map(AppMessage::EventOccurred);
|
||||
|
||||