mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-21 22:15:40 +00:00
fix: player bar CSS + Windows media keys HWND (v1.17.2)
- layout: overflow:hidden on .app-shell + min-height:0 on sidebar/main/queue prevents player bar from being pushed off-screen when window is resized below OS minHeight constraint (ignored by some Linux WMs) - lib.rs: pass main window HWND to souvlaki PlatformConfig on Windows so SMTC hooks into the existing Win32 message loop instead of creating its own — fixes media keys on Windows without crashing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.17.2] - 2026-03-26
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Player bar disappears when window is resized small**: On Linux (and some Windows configurations), the window manager ignores the `minHeight` constraint, allowing the window to be dragged smaller than intended. The CSS grid's `1fr` row has an implicit `min-height: auto`, meaning it refuses to shrink below the min-content height of the sidebar/main/queue children — this pushed the total grid height beyond `100vh` and scrolled the player bar out of view. Fixed by adding `min-height: 0` to `.sidebar`, `.main-content`, and `.queue-panel`, and `overflow: hidden` to `.app-shell` as a safety net.
|
||||
- **Media keys on Windows (SMTC)**: souvlaki's Windows backend requires a valid Win32 HWND to hook into the existing message loop rather than spinning up its own. Passing `hwnd: None` caused a crash on startup (v1.17.0). Now retrieves the main window's HWND via `app.get_webview_window("main").hwnd()` and passes it to `PlatformConfig`. Falls back to disabled gracefully if the HWND cannot be obtained.
|
||||
|
||||
---
|
||||
|
||||
## [1.17.1] - 2026-03-25
|
||||
|
||||
### Fixed
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "psysonic",
|
||||
"version": "1.17.1",
|
||||
"version": "1.17.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Maintainer: Psychotoxic <psychotoxic@gmx.de>
|
||||
pkgname=psysonic
|
||||
pkgver=1.17.1
|
||||
pkgver=1.17.2
|
||||
pkgrel=1
|
||||
pkgdesc="Desktop music player for Subsonic API-compatible servers (Navidrome, Gonic, etc.)"
|
||||
arch=('x86_64')
|
||||
|
||||
+63
-48
@@ -252,61 +252,76 @@ pub fn run() {
|
||||
.build(app)?;
|
||||
|
||||
// ── MPRIS2 / OS media controls via souvlaki ──────────────────
|
||||
// Windows: souvlaki SMTC init requires a valid HWND and a running
|
||||
// COM message loop, neither of which is available this early in
|
||||
// setup(). Disabled on Windows until we can defer init post-window.
|
||||
// mpris_set_metadata / mpris_set_playback no-op via the None branch.
|
||||
#[cfg(target_os = "windows")]
|
||||
app.manage(MprisControls::new(None));
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
use souvlaki::{MediaControlEvent, MediaControls, PlatformConfig};
|
||||
|
||||
// On Linux, souvlaki requires a live D-Bus session.
|
||||
#[cfg(target_os = "linux")]
|
||||
let dbus_ok = std::env::var("DBUS_SESSION_BUS_ADDRESS")
|
||||
.map(|v| !v.is_empty())
|
||||
.unwrap_or(false);
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
let dbus_ok = true;
|
||||
|
||||
if !dbus_ok {
|
||||
eprintln!("[Psysonic] No D-Bus session — MPRIS media controls disabled");
|
||||
app.manage(MprisControls::new(None));
|
||||
} else {
|
||||
|
||||
let config = PlatformConfig {
|
||||
dbus_name: "psysonic",
|
||||
display_name: "Psysonic",
|
||||
hwnd: None,
|
||||
};
|
||||
|
||||
let maybe_controls = match MediaControls::new(config) {
|
||||
Ok(mut controls) => {
|
||||
let app_handle = app.handle().clone();
|
||||
if let Err(e) = controls.attach(move |event: MediaControlEvent| {
|
||||
let event_name = match event {
|
||||
MediaControlEvent::Toggle => "media:play-pause",
|
||||
MediaControlEvent::Play => "media:play-pause",
|
||||
MediaControlEvent::Pause => "media:play-pause",
|
||||
MediaControlEvent::Next => "media:next",
|
||||
MediaControlEvent::Previous => "media:prev",
|
||||
_ => return,
|
||||
};
|
||||
let _ = app_handle.emit(event_name, ());
|
||||
}) {
|
||||
eprintln!("[Psysonic] Failed to attach media controls: {e:?}");
|
||||
// Collect pre-conditions and the platform-specific HWND.
|
||||
// Returns None early (with a log) on any unrecoverable condition
|
||||
// so app.manage() always executes exactly once at the bottom.
|
||||
let maybe_controls: Option<MediaControls> = (|| {
|
||||
// Linux: requires a live D-Bus session.
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let dbus_ok = std::env::var("DBUS_SESSION_BUS_ADDRESS")
|
||||
.map(|v| !v.is_empty())
|
||||
.unwrap_or(false);
|
||||
if !dbus_ok {
|
||||
eprintln!("[Psysonic] No D-Bus session — MPRIS media controls disabled");
|
||||
return None;
|
||||
}
|
||||
Some(controls)
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("[Psysonic] Could not create media controls: {e:?}");
|
||||
None
|
||||
|
||||
// Windows: souvlaki SMTC must hook into the existing Win32
|
||||
// message loop rather than spinning up its own. Pass the
|
||||
// main window's HWND so it can do so. If we can't get one,
|
||||
// skip init (no crash, just no media overlay).
|
||||
#[cfg(target_os = "windows")]
|
||||
let hwnd = {
|
||||
use tauri::Manager;
|
||||
let h = app.get_webview_window("main")
|
||||
.and_then(|w| w.hwnd().ok())
|
||||
.map(|h| h.0 as *mut std::ffi::c_void);
|
||||
if h.is_none() {
|
||||
eprintln!("[Psysonic] Could not get HWND — Windows media controls disabled");
|
||||
return None;
|
||||
}
|
||||
h
|
||||
};
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let hwnd: Option<*mut std::ffi::c_void> = None;
|
||||
|
||||
let config = PlatformConfig {
|
||||
dbus_name: "psysonic",
|
||||
display_name: "Psysonic",
|
||||
hwnd,
|
||||
};
|
||||
|
||||
match MediaControls::new(config) {
|
||||
Ok(mut controls) => {
|
||||
let app_handle = app.handle().clone();
|
||||
if let Err(e) = controls.attach(move |event: MediaControlEvent| {
|
||||
let event_name = match event {
|
||||
MediaControlEvent::Toggle => "media:play-pause",
|
||||
MediaControlEvent::Play => "media:play-pause",
|
||||
MediaControlEvent::Pause => "media:play-pause",
|
||||
MediaControlEvent::Next => "media:next",
|
||||
MediaControlEvent::Previous => "media:prev",
|
||||
_ => return,
|
||||
};
|
||||
let _ = app_handle.emit(event_name, ());
|
||||
}) {
|
||||
eprintln!("[Psysonic] Failed to attach media controls: {e:?}");
|
||||
}
|
||||
Some(controls)
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("[Psysonic] Could not create media controls: {e:?}");
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
app.manage(MprisControls::new(maybe_controls));
|
||||
} // end dbus_ok
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "Psysonic",
|
||||
"version": "1.17.1",
|
||||
"version": "1.17.2",
|
||||
"identifier": "dev.psysonic.player",
|
||||
"build": {
|
||||
"beforeDevCommand": "npm run dev",
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
"sidebar main queue"
|
||||
"player player player";
|
||||
height: 100vh;
|
||||
/* overflow: hidden keeps the player bar pinned at the bottom even when the
|
||||
window is dragged below the OS minHeight constraint (ignored on some
|
||||
Linux WMs/compositors). Without this, the 1fr row's implicit auto
|
||||
min-height can push the grid taller than 100vh, scrolling the player
|
||||
bar out of view. */
|
||||
overflow: hidden;
|
||||
background: var(--bg-app);
|
||||
}
|
||||
|
||||
@@ -39,6 +45,7 @@
|
||||
border-right: 1px solid var(--border-subtle);
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
min-height: 0; /* allow 1fr row to shrink freely */
|
||||
}
|
||||
|
||||
.sidebar-brand {
|
||||
@@ -301,6 +308,7 @@
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
min-width: 0;
|
||||
min-height: 0; /* allow 1fr row to shrink freely */
|
||||
background: var(--bg-app);
|
||||
z-index: 1;
|
||||
}
|
||||
@@ -635,6 +643,7 @@
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
min-width: 0;
|
||||
min-height: 0; /* allow 1fr row to shrink freely */
|
||||
}
|
||||
|
||||
.queue-header {
|
||||
|
||||
Reference in New Issue
Block a user