Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 58 additions & 1 deletion bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from fabric.widgets.box import Box
from fabric.widgets.centerbox import CenterBox
from fabric.widgets.eventbox import EventBox
from snippets import HackedRevealer, enable_blur, set_blur_regions_from_widget, disable_blur, free_blur, AppletReveal
from snippets import HackedRevealer, enable_blur, set_blur_regions_from_widget, disable_blur, free_blur, AppletReveal, UnifiedPopoutManager
from gi.repository import Gdk, Gtk, GLib, GtkLayerShell
from bar_widgets import (
LauncherButton, BluetoothButton, BatteryButton, CalendarButton, ClockButton,
Expand Down Expand Up @@ -547,6 +547,11 @@ def _on_enter(self, widget, event):
GLib.source_remove(self._leave_timer)
self._leave_timer = None

bar = self._get_bar()
if bar and hasattr(bar, "popout_manager") and bar.popout_manager.is_open and self.widget_key in APPLET_WIDGETS:
bar.popout_manager.open(self.widget_key, self.event_box)
return False

if not _is_hover_enabled_for_key(self.widget_key):
return False
if self._hover_timer is not None:
Expand Down Expand Up @@ -602,6 +607,11 @@ def _trigger_hover_open(self):
singletons.bar_manager._dash.toggle(active_monitor)
self._hook_dash_hover_leave()
elif self.widget_key in APPLET_WIDGETS:
bar = self._get_bar()
if bar and hasattr(bar, "popout_manager"):
self._opened_by_hover = True
bar.popout_manager.open(self.widget_key, self.event_box)
return False
popup = self._ensure_popup()
if popup and not popup.is_visible():
self._opened_by_hover = True
Expand Down Expand Up @@ -735,6 +745,10 @@ def _on_click(self, _widget, event: Gdk.EventButton):
return False
if event.button != 1:
return False
bar = self._get_bar()
if bar and hasattr(bar, "popout_manager") and self.widget_key in APPLET_WIDGETS:
bar.popout_manager.toggle(self.widget_key, self.event_box)
return True
popup = self._ensure_popup()
if popup is None:
return False
Expand Down Expand Up @@ -1215,6 +1229,19 @@ def _on_child_click(self, _widget, event: Gdk.EventButton):
return False
if event.button != 1:
return False

bar = self._get_bar()
if bar and hasattr(bar, "popout_manager"):
try:
idx = self._event_boxes.index(_widget)
if 0 <= idx < len(self.widget_keys):
key = self.widget_keys[idx]
if key in APPLET_WIDGETS:
bar.popout_manager.toggle(key, _widget)
return True
except ValueError:
pass

popup = self._ensure_popup()
if popup is None:
return False
Expand All @@ -1234,6 +1261,13 @@ def _on_outer_click(self, widget, event: Gdk.EventButton):
if event.button != 1:
return False

bar = self._get_bar()
if bar and hasattr(bar, "popout_manager"):
valid_keys = [k for k in self.widget_keys if k in APPLET_WIDGETS]
if valid_keys:
bar.popout_manager.toggle(valid_keys[0], self._outer_eb)
return True

popup = self._ensure_popup()
if popup is None:
return False
Expand Down Expand Up @@ -1774,6 +1808,8 @@ def __init__(self, monitor_id: int = 0, bar_index: int = 0, bar_cfg: dict = None
except Exception:
pass

self.popout_manager = UnifiedPopoutManager(bar=self, applet_factories=APPLET_WIDGETS)

if not self.auto_hide:
GLib.idle_add(self._revealer.set_reveal_child, True)
else:
Expand Down Expand Up @@ -2455,11 +2491,17 @@ def toggle(self, key: str):
for section in bar.sections.values():
for child in section.get_children():
if isinstance(child, WidgetWrapper) and child.widget_key == key:
if hasattr(bar, "popout_manager") and bar.popout_manager:
bar.popout_manager.toggle(key, child.event_box)
return
popup = child._ensure_popup()
if popup:
popup.toggle()
return
elif isinstance(child, GroupWrapper) and key in child.widget_keys:
if hasattr(bar, "popout_manager") and bar.popout_manager:
bar.popout_manager.toggle(key, child._outer_eb)
return
popup = child._ensure_popup()
if popup:
popup.toggle()
Expand All @@ -2468,6 +2510,12 @@ def toggle(self, key: str):
if key not in APPLET_WIDGETS:
return

for (monitor, _), bar in self._bars.items():
if get_connector_from_monitor_id(bar.monitor_id) == active_output:
if hasattr(bar, "popout_manager") and bar.popout_manager:
bar.popout_manager.toggle(key, bar._centerbox)
return

if key not in self._fallback_popups:
widget_class = APPLET_WIDGETS[key]
self._fallback_popups[key] = AppletWindow(
Expand Down Expand Up @@ -2545,6 +2593,15 @@ def set_bar_alignment(self, alignment: str) -> None:
for bar in self._bars.values():
bar._set_alignment(alignment)

def toggle_min_width(self) -> None:
for bar in self._bars.values():
bar._toggle_min_width()

def set_min_width(self, min_width: bool) -> None:
for bar in self._bars.values():
if bar.min_width != min_width:
bar._toggle_min_width()

def reload_bars(self) -> None:
for bar in list(self._bars.values()):
bar.destroy()
Expand Down
120 changes: 116 additions & 4 deletions bin/agl
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ show_help() {
echo -e " ${CYAN}uninstall${RESET} Uninstall Agility Shell (--purge to wipe user configs)"
echo -e " ${CYAN}suits${RESET} Manage desktop suites (list, next, prev, toggle, switch <id>)"
echo -e " ${CYAN}profile${RESET} Switch/view performance profile (optimized, balanced, dedicated)"
echo -e " ${CYAN}bar${RESET} Adjust bar dimensions and thickness (agl bar height <26-48>)"
echo -e " ${CYAN}bar${RESET} Adjust bar dimensions (agl bar height <26-48> | agl bar width [toggle|min|full])"
echo -e " ${CYAN}toggle${RESET} Toggle shell surfaces (dash, settings, launcher, suits, widgets)"
echo -e ""
echo -e "${BOLD}OPTIONS:${RESET}"
Expand Down Expand Up @@ -387,12 +387,30 @@ cmd_toggle() {
dash)
fabric-cli exec agility-shell "bar_manager.toggle('Dash')" 2>/dev/null || info "Shell not active."
;;
settings)
fabric-cli exec agility-shell "bar_manager.open_settings()" 2>/dev/null || info "Shell not active."
settings|control-center|cc|quicksettings|quick-settings)
fabric-cli exec agility-shell "bar_manager.toggle('Settings')" 2>/dev/null || info "Shell not active."
;;
launcher)
fabric-cli exec agility-shell "bar_manager.toggle('Launcher')" 2>/dev/null || info "Shell not active."
;;
calendar)
fabric-cli exec agility-shell "bar_manager.toggle('Calendar')" 2>/dev/null || info "Shell not active."
;;
audio|volume)
fabric-cli exec agility-shell "bar_manager.toggle('Volume')" 2>/dev/null || info "Shell not active."
;;
network|wifi)
fabric-cli exec agility-shell "bar_manager.toggle('Wifi')" 2>/dev/null || info "Shell not active."
;;
bluetooth)
fabric-cli exec agility-shell "bar_manager.toggle('Bluetooth')" 2>/dev/null || info "Shell not active."
;;
media)
fabric-cli exec agility-shell "bar_manager.toggle('Media')" 2>/dev/null || info "Shell not active."
;;
weather)
fabric-cli exec agility-shell "bar_manager.toggle('Weather')" 2>/dev/null || info "Shell not active."
;;
suits)
fabric-cli exec agility-shell "bar_manager.toggle('Suits')" 2>/dev/null || info "Shell not active."
;;
Expand All @@ -408,6 +426,41 @@ cmd_toggle() {
esac
}

cmd_volume() {
local action="${1:-up}"
local step="${2:-0.05}"
case "${action,,}" in
up|raise|+)
fabric-cli exec agility-shell "import services.singletons as s; s.audio.speaker.volume = min(1.0, round(s.audio.speaker.volume + float($step), 2)) if s.audio and s.audio.speaker else None" 2>/dev/null || info "Shell not active."
;;
down|lower|-)
fabric-cli exec agility-shell "import services.singletons as s; s.audio.speaker.volume = max(0.0, round(s.audio.speaker.volume - float($step), 2)) if s.audio and s.audio.speaker else None" 2>/dev/null || info "Shell not active."
;;
mute|toggle-mute)
fabric-cli exec agility-shell "import services.singletons as s; s.audio.speaker.muted = not s.audio.speaker.muted if s.audio and s.audio.speaker else None" 2>/dev/null || info "Shell not active."
;;
*)
echo "Usage: agl volume <up|down|mute> [step]"
;;
esac
}

cmd_brightness() {
local action="${1:-up}"
local step="${2:-5}"
case "${action,,}" in
up|raise|+)
fabric-cli exec agility-shell "import services.singletons as s; s.brightness.screen_brightness = min(100, int(s.brightness.screen_brightness + int($step))) if s.brightness and s.brightness.backend else None" 2>/dev/null || info "Shell not active."
;;
down|lower|-)
fabric-cli exec agility-shell "import services.singletons as s; s.brightness.screen_brightness = max(0, int(s.brightness.screen_brightness - int($step))) if s.brightness and s.brightness.backend else None" 2>/dev/null || info "Shell not active."
;;
*)
echo "Usage: agl brightness <up|down> [step]"
;;
esac
}

cmd_bar() {
local action="${1:-height}"
shift || true
Expand Down Expand Up @@ -466,8 +519,55 @@ if not found:
echo -e "Usage: agl bar height <26-48>"
fi
;;
width|min_width|min-width)
local mode="${1:-toggle}"
case "${mode,,}" in
min|minimal|compact)
if command -v fabric-cli &>/dev/null; then
fabric-cli exec agility-shell "
import services.singletons as s
try:
bm = s.bar_manager
if bm and hasattr(bm, 'set_min_width'):
bm.set_min_width(True)
except Exception:
pass
" 2>/dev/null || true
fi
success "Bar width set to minimal."
;;
full|expand|max)
if command -v fabric-cli &>/dev/null; then
fabric-cli exec agility-shell "
import services.singletons as s
try:
bm = s.bar_manager
if bm and hasattr(bm, 'set_min_width'):
bm.set_min_width(False)
except Exception:
pass
" 2>/dev/null || true
fi
success "Bar width set to full width."
;;
toggle|*)
if command -v fabric-cli &>/dev/null; then
fabric-cli exec agility-shell "
import services.singletons as s
try:
bm = s.bar_manager
if bm and hasattr(bm, 'toggle_min_width'):
bm.toggle_min_width()
except Exception:
pass
" 2>/dev/null || true
fi
success "Bar width toggled."
;;
esac
;;
*)
echo -e "${BOLD}Usage:${RESET} agl bar height <26-48>"
echo -e "${BOLD}Usage:${RESET} agl bar <height <26-48> | width [toggle|min|full]>"
;;
esac
}
Expand Down Expand Up @@ -515,6 +615,18 @@ case "$COMMAND" in
toggle)
cmd_toggle "$@"
;;
volume)
cmd_volume "$@"
;;
brightness)
cmd_brightness "$@"
;;
launcher)
cmd_toggle "launcher"
;;
cc|control-center)
cmd_toggle "control-center"
;;
-v|--version|version)
echo -e "Agility Shell CLI (agl) v$VERSION"
;;
Expand Down
15 changes: 11 additions & 4 deletions config/niri.kdl
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,17 @@ layer-rule {
}

binds {
Mod+Space hotkey-overlay-title="Open Launcher" { spawn "fabric-cli" "exec" "agility-shell" "bar_manager.toggle('Launcher')"; }
Mod+D hotkey-overlay-title="Open Dash" { spawn "fabric-cli" "exec" "agility-shell" "bar_manager.toggle('Dash')"; }
Mod+Shift+W hotkey-overlay-title="Wallpaper Gallery" { spawn "fabric-cli" "exec" "agility-shell" "bar_manager.toggle('WallpaperPicker')"; }
Alt+C hotkey-overlay-title="Wallpaper Switcher" { spawn "fabric-cli" "exec" "agility-shell" "bar_manager.toggle('WallpaperDrawer')"; }
Mod+Space hotkey-overlay-title="Open Launcher" { spawn "agl" "toggle" "launcher"; }
Mod+C hotkey-overlay-title="Control Center" { spawn "agl" "toggle" "cc"; }
Mod+D hotkey-overlay-title="Open Dash" { spawn "agl" "toggle" "dash"; }
Mod+Shift+W hotkey-overlay-title="Wallpaper Gallery" { spawn "agl" "toggle" "WallpaperPicker"; }
Alt+C hotkey-overlay-title="Wallpaper Switcher" { spawn "agl" "toggle" "WallpaperDrawer"; }
Mod+Alt+X hotkey-overlay-title="Random Wallpaper" { spawn "fabric-cli" "exec" "agility-shell" "wallpaper_service.random_wallpaper()"; }
Alt+X hotkey-overlay-title="Random Wallpaper" { spawn "fabric-cli" "exec" "agility-shell" "wallpaper_service.random_wallpaper()"; }

XF86AudioRaiseVolume allow-when-locked=true { spawn "agl" "volume" "up"; }
XF86AudioLowerVolume allow-when-locked=true { spawn "agl" "volume" "down"; }
XF86AudioMute allow-when-locked=true { spawn "agl" "volume" "mute"; }
XF86MonBrightnessUp allow-when-locked=true { spawn "agl" "brightness" "up"; }
XF86MonBrightnessDown allow-when-locked=true { spawn "agl" "brightness" "down"; }
}
50 changes: 38 additions & 12 deletions services/caffeine.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,29 @@ def toggle(self):
self.enabled = not self._enabled

def _start_inhibit(self):
try:
from services.singletons import idle
if idle:
idle.stop()
except Exception as e:
logger.debug(f"[CaffeineService] Error stopping idle service: {e}")

try:
subprocess.run(["pkill", "-x", "swayidle"], check=False, capture_output=True)
except Exception:
pass

try:
from services.idle import _get_screen_commands
_, screen_on = _get_screen_commands()
if screen_on:
subprocess.Popen(screen_on, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
except Exception:
pass

try:
self._process = subprocess.Popen(
["systemd-inhibit", "--what=idle:sleep", "--why=Caffeine keep-awake", "--mode=block", "sleep", "infinity"],
["systemd-inhibit", "--what=idle:sleep:handle-lid-switch", "--why=Caffeine keep-awake", "--mode=block", "sleep", "infinity"],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
Expand All @@ -39,16 +59,22 @@ def _start_inhibit(self):
logger.warning(f"[CaffeineService] Failed to start inhibitor: {e}")

def _stop_inhibit(self):
if self._process is None:
return
try:
self._process.terminate()
self._process.wait(timeout=1)
except Exception:
if self._process is not None:
try:
self._process.kill()
self._process.terminate()
self._process.wait(timeout=1)
except Exception:
pass
finally:
self._process = None
logger.info("[CaffeineService] Inhibitor stopped")
try:
self._process.kill()
except Exception:
pass
finally:
self._process = None
logger.info("[CaffeineService] Inhibitor stopped")

try:
from services.singletons import idle
if idle:
idle.start()
except Exception as e:
logger.debug(f"[CaffeineService] Error starting idle service: {e}")
4 changes: 3 additions & 1 deletion snippets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from .applet_reveal import AppletReveal
from .dashreveal import DashReveal
from .entry import StyleAwareEntry
from .popout_manager import UnifiedPopoutManager
__all__ = [
"HackedRevealer",
"HackedStack",
Expand Down Expand Up @@ -50,5 +51,6 @@
"FlatScale",
"AppletReveal",
"DashReveal",
"StyleAwareEntry"
"StyleAwareEntry",
"UnifiedPopoutManager",
]
Loading
Loading