Popup auf SNI-Desktops: Activate-Koordinaten (Anchor::Click) + Cinnamon-Extension

- SNI-Activate-Koordinaten gehen als Anchor::Click durch; Cinnamon liefert
  echte Werte, bei (0,0) bleibt der Cursor-Fallback (KDE/XFCE).
- Neue Cinnamon-Extension ai-control-popup@local: platziert das Popup unter
  Wayland am Zeiger (Work-Area-geklemmt, set_position ist dort wirkungslos)
  und gibt ihm unter X11 den initialen Fokus (Muffins Focus-Stealing-
  Prevention verwirft das present() der App).
- Extension in deb/rpm nach /usr/share/cinnamon/extensions paketiert.
- README: Cinnamon-Abschnitte und Installer-Teststand aktualisiert.

Verifiziert in der VM-Flotte: Cinnamon Wayland + X11, XFCE, KDE (Wayland),
GNOME (Wayland).
This commit is contained in:
marcus hinz
2026-07-21 11:23:45 +02:00
parent 1fa9ffcf1b
commit bbb06e0814
7 changed files with 105 additions and 16 deletions
+14 -7
View File
@@ -61,19 +61,26 @@ pub(crate) fn show_popup(app: &tauri::AppHandle, anchor: Anchor) {
};
let _ = w.set_position(tauri::PhysicalPosition::new(x, y));
}
// SNI (KDE/XFCE/Cinnamon): Activate liefert keine brauchbaren
// Koordinaten, aber der Zeiger sitzt beim Klick auf dem Icon.
Anchor::Cursor => {
// SNI (KDE/XFCE/Cinnamon): Activate-Koordinaten, wenn der Host welche
// mitgibt (Cinnamon); sonst Zeigerposition — der Zeiger sitzt beim
// Klick auf dem Icon. Unter Wayland liefert cursor_position (0,0).
Anchor::Click { x, y } => {
#[cfg(not(target_os = "linux"))]
let _ = (x, y);
#[cfg(target_os = "linux")]
{
// Gelöst wird die Sperre vom ersten Focused(true) im Event-Handler.
if let Some(g) = win.try_state::<PopupBlurGuard>() {
g.0.store(true, std::sync::atomic::Ordering::SeqCst);
}
match win.cursor_position() {
Ok(p) => position_popup(&w, p.x as i32, p.y as i32),
Err(_) => {
let _ = w.center();
if (x, y) != (0, 0) {
position_popup(&w, x, y);
} else {
match win.cursor_position() {
Ok(p) => position_popup(&w, p.x as i32, p.y as i32),
Err(_) => {
let _ = w.center();
}
}
}
}
+4 -4
View File
@@ -1,7 +1,7 @@
//! Eigenes StatusNotifierItem (ksni) statt Tauris libappindicator-Tray:
//! libappindicator kann nur ein Menü, wir wollen Linksklick → Popup-Fenster.
//! Activate liefert keine brauchbaren Koordinaten — der Zeiger sitzt beim
//! Klick auf dem Icon, daher Anchor::Cursor.
//! Die Activate-Koordinaten gehen als Anchor::Click durch; ob sie brauchbar
//! sind, hängt vom SNI-Host ab (Cinnamon ja, KDE 0,0).
use crate::platform::{Anchor, TrayCallbacks};
@@ -21,8 +21,8 @@ impl ksni::Tray for AiControlTray {
fn icon_pixmap(&self) -> Vec<ksni::Icon> {
self.icon.clone()
}
fn activate(&mut self, _x: i32, _y: i32) {
(self.cb.show)(&self.app, Anchor::Cursor);
fn activate(&mut self, x: i32, y: i32) {
(self.cb.show)(&self.app, Anchor::Click { x, y });
}
fn menu(&self) -> Vec<ksni::MenuItem<Self>> {
use ksni::menu::StandardItem;
+4 -3
View File
@@ -55,9 +55,10 @@ pub(crate) enum Anchor {
/// entscheidet die Plattform: Menüleiste oben → Popup darunter (macOS),
/// Taskbar unten → Popup darüber (Windows).
IconRect { rect: tauri::Rect, popup_below: bool },
/// SNI/ksni liefert keine Koordinaten — der Zeiger steht beim Klick auf dem
/// Icon (KDE/XFCE/Cinnamon).
Cursor,
/// SNI/ksni (KDE/XFCE/Cinnamon): Klick-Koordinaten aus Activate. Cinnamon
/// liefert echte Werte; wo der Host keine mitgibt (0,0), fällt app.rs auf
/// die Zeigerposition zurück — der Zeiger steht beim Klick auf dem Icon.
Click { x: i32, y: i32 },
/// Positionierung übernimmt der Compositor bzw. die Shell-Extension
/// (GNOME, KDE-Wayland/KWin).
Managed,