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:
@@ -64,11 +64,11 @@ Releases carry a `.deb`, an `.rpm`, and an AppImage.
|
||||
- **deb / rpm** — recommended. Both also install the GNOME Shell extension and the KWin script that provide the tray/popup integration on GNOME and KDE Wayland (into `/usr/share/gnome-shell/extensions/` and `/usr/share/kwin/scripts/`).
|
||||
- **AppImage** — runs on desktops with a standard tray (KDE, XFCE, Cinnamon). On GNOME it refuses to start and points to the deb/rpm: the tray there needs the shell extension, which an installation-free AppImage cannot provide.
|
||||
|
||||
Tray integration by desktop: GNOME via the bundled shell extension, KDE Wayland via StatusNotifierItem plus the bundled KWin script for popup placement, KDE X11/XFCE/Cinnamon via StatusNotifierItem directly.
|
||||
Tray integration by desktop: GNOME via the bundled shell extension, KDE Wayland via StatusNotifierItem plus the bundled KWin script for popup placement, KDE X11/XFCE/Cinnamon via StatusNotifierItem directly. On Cinnamon **Wayland** sessions a bundled Cinnamon extension additionally places the popup at the tray icon (a Wayland client cannot position its own window); Cinnamon X11 needs no extension.
|
||||
|
||||
### Installer tests
|
||||
|
||||
The installers are hand-tested per release. Current state: Fedora 44 via rpm on GNOME, KDE Plasma, Cinnamon, and XFCE (VM test fleet); Ubuntu 26.04 (GNOME), Kubuntu (KDE Plasma, Wayland) and Linux Mint (Cinnamon) via deb in earlier rounds. Two known issues from the current round are open: on Cinnamon the tray popup opens at the top-left corner instead of at the tray icon, and on the X11 desktops (Cinnamon, XFCE) clicking outside the popup does not close it. macOS re-test of the current build is in progress.
|
||||
The installers are hand-tested per release. Current state: Fedora 44 via rpm on GNOME, KDE Plasma, Cinnamon, and XFCE (VM test fleet); Ubuntu 26.04 (GNOME), Kubuntu (KDE Plasma, Wayland) and Linux Mint (Cinnamon) via deb in earlier rounds. The two open findings from that round are resolved: popup placement on Cinnamon Wayland now comes from the bundled Cinnamon extension, and click-outside-to-close verified fine on XFCE and Cinnamon. macOS re-test of the current build is in progress.
|
||||
|
||||
### Windows
|
||||
|
||||
@@ -109,6 +109,7 @@ Deleting a project is scoped in three stages, each preceded by a preview of the
|
||||
│ ├── capabilities/ per-window ACL (main/popup/terminal/panel)
|
||||
│ └── icons/ app and tray icons
|
||||
├── gnome-shell-extension/ tray/popup integration for GNOME
|
||||
├── cinnamon-extension/ popup placement for Cinnamon Wayland
|
||||
├── kwin-script/ popup placement for KDE Wayland
|
||||
├── dev.sh development mode (tauri dev)
|
||||
├── build.sh release build for the current OS (see below)
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/* Cinnamon-Extension (CJS, Muffin/Meta-0)
|
||||
* ai-control-popup@local
|
||||
*
|
||||
* Das Tray-Icon kommt unter Cinnamon über SNI (xapp), die App zeigt das
|
||||
* Popup selbst. Die Extension ergänzt, was der Client nicht kann:
|
||||
* - Wayland: set_position aus dem Client ist wirkungslos — die Extension
|
||||
* schiebt das frisch gemappte Popup an den Mauszeiger (der beim Klick auf
|
||||
* dem Icon steht), mit derselben Klemm-Logik wie position_popup in app.rs.
|
||||
* - X11: Muffins Focus-Stealing-Prevention verwirft das present() der App —
|
||||
* ohne initialen Fokus schließt das Blur-Verstecken nie. Die Extension
|
||||
* fokussiert das Popup beim Map aus dem Compositor heraus (platziert wird
|
||||
* client-seitig über die SNI-Activate-Koordinaten).
|
||||
*/
|
||||
|
||||
const Meta = imports.gi.Meta;
|
||||
|
||||
const WIN_TITLE = 'ai-control-popup';
|
||||
|
||||
let mapId = 0;
|
||||
|
||||
function _place(win) {
|
||||
const [px, py] = global.get_pointer();
|
||||
const rect = win.get_frame_rect();
|
||||
// Work-Area statt Monitor: die Panel-Struts bleiben frei, das Popup sitzt
|
||||
// über der Leiste statt darauf.
|
||||
const wa = win.get_work_area_current_monitor();
|
||||
const maxX = wa.x + wa.width - rect.width;
|
||||
const maxY = wa.y + wa.height - rect.height;
|
||||
const x = Math.min(Math.max(px - rect.width, wa.x), Math.max(maxX, wa.x));
|
||||
const y = Math.min(Math.max(py, wa.y), Math.max(maxY, wa.y));
|
||||
win.move_frame(true, x, y);
|
||||
}
|
||||
|
||||
// Beim Mappen unsichtbar schalten und vor dem nächsten Redraw positionieren —
|
||||
// der erste sichtbare Frame sitzt schon am Zielort (Muster wie in der
|
||||
// GNOME-Extension, dort über global.compositor.get_laters()).
|
||||
function _placeBeforeRedraw(actor, win) {
|
||||
actor.remove_all_transitions();
|
||||
actor.opacity = 0;
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
|
||||
const rect = win.get_frame_rect();
|
||||
// Noch keine echte Größe -> nächster Redraw, bis positionierbar.
|
||||
if (!rect || rect.width <= 1) return true;
|
||||
_place(win);
|
||||
actor.opacity = 255;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function init(_metadata) {}
|
||||
|
||||
function enable() {
|
||||
mapId = global.window_manager.connect('map', (_wm, actor) => {
|
||||
const win = actor.meta_window;
|
||||
if (!win || win.get_title() !== WIN_TITLE) return;
|
||||
if (Meta.is_wayland_compositor()) {
|
||||
_placeBeforeRedraw(actor, win);
|
||||
} else {
|
||||
win.activate(global.get_current_time());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function disable() {
|
||||
if (mapId) {
|
||||
global.window_manager.disconnect(mapId);
|
||||
mapId = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"uuid": "ai-control-popup@local",
|
||||
"name": "ai-control popup placement",
|
||||
"description": "ai-control-Tray-Popup: platziert es unter Wayland am Mauszeiger und gibt ihm unter X11 den initialen Fokus (Focus-Stealing-Prevention).",
|
||||
"cinnamon-version": ["6.0", "6.2", "6.4", "6.6"],
|
||||
"version": 1
|
||||
}
|
||||
+14
-7
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
"/usr/share/gnome-shell/extensions/ai-control-popup@local/extension.js": "../gnome-shell-extension/ai-control-popup@local/extension.js",
|
||||
"/usr/share/gnome-shell/extensions/ai-control-popup@local/metadata.json": "../gnome-shell-extension/ai-control-popup@local/metadata.json",
|
||||
"/usr/share/gnome-shell/extensions/ai-control-popup@local/trayLinux.png": "icons/trayLinux.png",
|
||||
"/usr/share/cinnamon/extensions/ai-control-popup@local/extension.js": "../cinnamon-extension/ai-control-popup@local/extension.js",
|
||||
"/usr/share/cinnamon/extensions/ai-control-popup@local/metadata.json": "../cinnamon-extension/ai-control-popup@local/metadata.json",
|
||||
"/usr/share/kwin/scripts/ai-control-popup/metadata.json": "../kwin-script/ai-control-popup/metadata.json",
|
||||
"/usr/share/kwin/scripts/ai-control-popup/contents/code/main.js": "../kwin-script/ai-control-popup/contents/code/main.js"
|
||||
}
|
||||
@@ -42,6 +44,8 @@
|
||||
"/usr/share/gnome-shell/extensions/ai-control-popup@local/extension.js": "../gnome-shell-extension/ai-control-popup@local/extension.js",
|
||||
"/usr/share/gnome-shell/extensions/ai-control-popup@local/metadata.json": "../gnome-shell-extension/ai-control-popup@local/metadata.json",
|
||||
"/usr/share/gnome-shell/extensions/ai-control-popup@local/trayLinux.png": "icons/trayLinux.png",
|
||||
"/usr/share/cinnamon/extensions/ai-control-popup@local/extension.js": "../cinnamon-extension/ai-control-popup@local/extension.js",
|
||||
"/usr/share/cinnamon/extensions/ai-control-popup@local/metadata.json": "../cinnamon-extension/ai-control-popup@local/metadata.json",
|
||||
"/usr/share/kwin/scripts/ai-control-popup/metadata.json": "../kwin-script/ai-control-popup/metadata.json",
|
||||
"/usr/share/kwin/scripts/ai-control-popup/contents/code/main.js": "../kwin-script/ai-control-popup/contents/code/main.js"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user