Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c0ea40a393 | |||
| 44f0915ba9 | |||
| a55567b422 | |||
| 707caefadf | |||
| 78895d34b5 | |||
| e6f217f19c | |||
| ff04675417 | |||
| b85f85c4da |
34
Cargo.lock
generated
34
Cargo.lock
generated
@@ -2373,7 +2373,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "owlry"
|
name = "owlry"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
@@ -2402,7 +2402,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "owlry-lua"
|
name = "owlry-lua"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"abi_stable",
|
"abi_stable",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -2420,7 +2420,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "owlry-plugin-api"
|
name = "owlry-plugin-api"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"abi_stable",
|
"abi_stable",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -2428,7 +2428,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "owlry-plugin-bookmarks"
|
name = "owlry-plugin-bookmarks"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"abi_stable",
|
"abi_stable",
|
||||||
"dirs",
|
"dirs",
|
||||||
@@ -2440,7 +2440,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "owlry-plugin-calculator"
|
name = "owlry-plugin-calculator"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"abi_stable",
|
"abi_stable",
|
||||||
"meval",
|
"meval",
|
||||||
@@ -2449,7 +2449,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "owlry-plugin-clipboard"
|
name = "owlry-plugin-clipboard"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"abi_stable",
|
"abi_stable",
|
||||||
"owlry-plugin-api",
|
"owlry-plugin-api",
|
||||||
@@ -2457,7 +2457,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "owlry-plugin-emoji"
|
name = "owlry-plugin-emoji"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"abi_stable",
|
"abi_stable",
|
||||||
"owlry-plugin-api",
|
"owlry-plugin-api",
|
||||||
@@ -2465,7 +2465,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "owlry-plugin-filesearch"
|
name = "owlry-plugin-filesearch"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"abi_stable",
|
"abi_stable",
|
||||||
"dirs",
|
"dirs",
|
||||||
@@ -2474,7 +2474,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "owlry-plugin-media"
|
name = "owlry-plugin-media"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"abi_stable",
|
"abi_stable",
|
||||||
"owlry-plugin-api",
|
"owlry-plugin-api",
|
||||||
@@ -2482,7 +2482,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "owlry-plugin-pomodoro"
|
name = "owlry-plugin-pomodoro"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"abi_stable",
|
"abi_stable",
|
||||||
"dirs",
|
"dirs",
|
||||||
@@ -2494,7 +2494,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "owlry-plugin-scripts"
|
name = "owlry-plugin-scripts"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"abi_stable",
|
"abi_stable",
|
||||||
"dirs",
|
"dirs",
|
||||||
@@ -2503,7 +2503,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "owlry-plugin-ssh"
|
name = "owlry-plugin-ssh"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"abi_stable",
|
"abi_stable",
|
||||||
"dirs",
|
"dirs",
|
||||||
@@ -2512,7 +2512,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "owlry-plugin-system"
|
name = "owlry-plugin-system"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"abi_stable",
|
"abi_stable",
|
||||||
"owlry-plugin-api",
|
"owlry-plugin-api",
|
||||||
@@ -2520,7 +2520,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "owlry-plugin-systemd"
|
name = "owlry-plugin-systemd"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"abi_stable",
|
"abi_stable",
|
||||||
"owlry-plugin-api",
|
"owlry-plugin-api",
|
||||||
@@ -2528,7 +2528,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "owlry-plugin-weather"
|
name = "owlry-plugin-weather"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"abi_stable",
|
"abi_stable",
|
||||||
"dirs",
|
"dirs",
|
||||||
@@ -2541,7 +2541,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "owlry-plugin-websearch"
|
name = "owlry-plugin-websearch"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"abi_stable",
|
"abi_stable",
|
||||||
"owlry-plugin-api",
|
"owlry-plugin-api",
|
||||||
@@ -2549,7 +2549,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "owlry-rune"
|
name = "owlry-rune"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"dirs",
|
"dirs",
|
||||||
|
|||||||
29
README.md
29
README.md
@@ -109,29 +109,34 @@ owlry --help # Show all options with examples
|
|||||||
|
|
||||||
### dmenu Mode
|
### dmenu Mode
|
||||||
|
|
||||||
Owlry is dmenu-compatible. Pipe input for interactive selection:
|
Owlry is dmenu-compatible. Pipe input for interactive selection - the selected item is printed to stdout (not executed), so you pipe the output to execute it:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Basic selection
|
# Screenshot menu (execute selected command)
|
||||||
echo -e "Option A\nOption B\nOption C" | owlry -m dmenu
|
printf '%s\n' \
|
||||||
|
"grimblast --notify copy screen" \
|
||||||
# Select from files
|
"grimblast --notify copy area" \
|
||||||
ls ~/Documents | owlry -m dmenu
|
"grimblast --notify edit screen" \
|
||||||
|
| owlry -m dmenu -p "Screenshot" \
|
||||||
|
| sh
|
||||||
|
|
||||||
# Git branch checkout
|
# Git branch checkout
|
||||||
git branch | owlry -m dmenu --prompt "checkout:" | xargs git checkout
|
git branch | owlry -m dmenu -p "checkout" | xargs git checkout
|
||||||
|
|
||||||
# Kill a process
|
# Kill a process
|
||||||
ps -eo comm | sort -u | owlry -m dmenu --prompt "kill:" | xargs pkill
|
ps -eo comm | sort -u | owlry -m dmenu -p "kill" | xargs pkill
|
||||||
|
|
||||||
# Select and open a project
|
# Select and open a project
|
||||||
find ~/projects -maxdepth 1 -type d | owlry -m dmenu | xargs code
|
find ~/projects -maxdepth 1 -type d | owlry -m dmenu | xargs code
|
||||||
|
|
||||||
# Package manager search
|
# Package manager search
|
||||||
pacman -Ssq | owlry -m dmenu --prompt "install:" | xargs sudo pacman -S
|
pacman -Ssq | owlry -m dmenu -p "install" | xargs sudo pacman -S
|
||||||
|
|
||||||
|
# Open selected file
|
||||||
|
ls ~/Documents | owlry -m dmenu | xargs xdg-open
|
||||||
```
|
```
|
||||||
|
|
||||||
The `--prompt` flag sets a custom label for the search input.
|
The `-p` / `--prompt` flag sets a custom label for the search input.
|
||||||
|
|
||||||
### Keyboard Shortcuts
|
### Keyboard Shortcuts
|
||||||
|
|
||||||
@@ -208,8 +213,8 @@ cp /usr/share/doc/owlry/config.example.toml ~/.config/owlry/config.toml
|
|||||||
show_icons = true
|
show_icons = true
|
||||||
max_results = 10
|
max_results = 10
|
||||||
tabs = ["app", "cmd", "uuctl"]
|
tabs = ["app", "cmd", "uuctl"]
|
||||||
# terminal_command = "kitty" # Auto-detected
|
# terminal_command = "kitty" # Auto-detected
|
||||||
# launch_wrapper = "uwsm app --" # Auto-detected
|
# use_uwsm = false # Enable for systemd session integration
|
||||||
|
|
||||||
[appearance]
|
[appearance]
|
||||||
width = 850
|
width = 850
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "owlry-lua"
|
name = "owlry-lua"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "owlry-plugin-api"
|
name = "owlry-plugin-api"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "owlry-plugin-bookmarks"
|
name = "owlry-plugin-bookmarks"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "owlry-plugin-calculator"
|
name = "owlry-plugin-calculator"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "owlry-plugin-clipboard"
|
name = "owlry-plugin-clipboard"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "owlry-plugin-emoji"
|
name = "owlry-plugin-emoji"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "owlry-plugin-filesearch"
|
name = "owlry-plugin-filesearch"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "owlry-plugin-media"
|
name = "owlry-plugin-media"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "owlry-plugin-pomodoro"
|
name = "owlry-plugin-pomodoro"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "owlry-plugin-scripts"
|
name = "owlry-plugin-scripts"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "owlry-plugin-ssh"
|
name = "owlry-plugin-ssh"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "owlry-plugin-system"
|
name = "owlry-plugin-system"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "owlry-plugin-systemd"
|
name = "owlry-plugin-systemd"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "owlry-plugin-weather"
|
name = "owlry-plugin-weather"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "owlry-plugin-websearch"
|
name = "owlry-plugin-websearch"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "owlry-rune"
|
name = "owlry-rune"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
rust-version = "1.90"
|
rust-version = "1.90"
|
||||||
description = "Rune scripting runtime for owlry plugins"
|
description = "Rune scripting runtime for owlry plugins"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "owlry"
|
name = "owlry"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
rust-version = "1.90"
|
rust-version = "1.90"
|
||||||
description = "A lightweight, owl-themed application launcher for Wayland"
|
description = "A lightweight, owl-themed application launcher for Wayland"
|
||||||
|
|||||||
@@ -27,11 +27,12 @@ pub struct GeneralConfig {
|
|||||||
/// Terminal command (auto-detected if not specified)
|
/// Terminal command (auto-detected if not specified)
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub terminal_command: Option<String>,
|
pub terminal_command: Option<String>,
|
||||||
/// Launch wrapper command for app execution.
|
/// Enable uwsm (Universal Wayland Session Manager) for launching apps.
|
||||||
/// Examples: "uwsm app --", "hyprctl dispatch exec --", "systemd-run --user --"
|
/// When enabled, desktop files are launched via `uwsm app -- <file>`
|
||||||
/// If None or empty, launches directly via sh -c
|
/// which starts apps in a proper systemd user session.
|
||||||
|
/// When disabled (default), apps are launched via `gio launch`.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub launch_wrapper: Option<String>,
|
pub use_uwsm: bool,
|
||||||
/// Provider tabs shown in the header bar.
|
/// Provider tabs shown in the header bar.
|
||||||
/// Valid values: app, cmd, uuctl, bookmark, calc, clip, dmenu, emoji, file, script, ssh, sys, web
|
/// Valid values: app, cmd, uuctl, bookmark, calc, clip, dmenu, emoji, file, script, ssh, sys, web
|
||||||
#[serde(default = "default_tabs")]
|
#[serde(default = "default_tabs")]
|
||||||
@@ -44,7 +45,7 @@ impl Default for GeneralConfig {
|
|||||||
show_icons: true,
|
show_icons: true,
|
||||||
max_results: 100,
|
max_results: 100,
|
||||||
terminal_command: None,
|
terminal_command: None,
|
||||||
launch_wrapper: None,
|
use_uwsm: false,
|
||||||
tabs: default_tabs(),
|
tabs: default_tabs(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -396,28 +397,6 @@ fn default_pomodoro_break() -> u32 {
|
|||||||
5
|
5
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Detect the best launch wrapper for the current session
|
|
||||||
/// Checks for uwsm (Universal Wayland Session Manager) and hyprland
|
|
||||||
fn detect_launch_wrapper() -> Option<String> {
|
|
||||||
// Check if running under uwsm (has UWSM_FINALIZE_VARNAMES or similar uwsm env vars)
|
|
||||||
if (std::env::var("UWSM_FINALIZE_VARNAMES").is_ok()
|
|
||||||
|| std::env::var("__UWSM_SELECT_TAG").is_ok())
|
|
||||||
&& command_exists("uwsm") {
|
|
||||||
debug!("Detected uwsm session, using 'uwsm app --' wrapper");
|
|
||||||
return Some("uwsm app --".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if running under Hyprland
|
|
||||||
if std::env::var("HYPRLAND_INSTANCE_SIGNATURE").is_ok()
|
|
||||||
&& command_exists("hyprctl") {
|
|
||||||
debug!("Detected Hyprland session, using 'hyprctl dispatch exec --' wrapper");
|
|
||||||
return Some("hyprctl dispatch exec --".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
// No wrapper needed for other environments
|
|
||||||
debug!("No launch wrapper detected, using direct execution");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Detect the best available terminal emulator
|
/// Detect the best available terminal emulator
|
||||||
/// Fallback chain:
|
/// Fallback chain:
|
||||||
@@ -578,11 +557,6 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-detect launch wrapper if not configured
|
|
||||||
if config.general.launch_wrapper.is_none() {
|
|
||||||
config.general.launch_wrapper = detect_launch_wrapper();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ pub struct MainWindow {
|
|||||||
lazy_state: Rc<RefCell<LazyLoadState>>,
|
lazy_state: Rc<RefCell<LazyLoadState>>,
|
||||||
/// Debounce source ID for cancelling pending searches
|
/// Debounce source ID for cancelling pending searches
|
||||||
debounce_source: Rc<RefCell<Option<gtk4::glib::SourceId>>>,
|
debounce_source: Rc<RefCell<Option<gtk4::glib::SourceId>>>,
|
||||||
|
/// Whether we're in dmenu mode (stdin pipe input)
|
||||||
|
is_dmenu_mode: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MainWindow {
|
impl MainWindow {
|
||||||
@@ -197,6 +199,9 @@ impl MainWindow {
|
|||||||
|
|
||||||
let lazy_state = Rc::new(RefCell::new(LazyLoadState::default()));
|
let lazy_state = Rc::new(RefCell::new(LazyLoadState::default()));
|
||||||
|
|
||||||
|
// Check if we're in dmenu mode (stdin pipe input)
|
||||||
|
let is_dmenu_mode = providers.borrow().is_dmenu_mode();
|
||||||
|
|
||||||
let main_window = Self {
|
let main_window = Self {
|
||||||
window,
|
window,
|
||||||
search_entry,
|
search_entry,
|
||||||
@@ -215,6 +220,7 @@ impl MainWindow {
|
|||||||
custom_prompt,
|
custom_prompt,
|
||||||
lazy_state,
|
lazy_state,
|
||||||
debounce_source: Rc::new(RefCell::new(None)),
|
debounce_source: Rc::new(RefCell::new(None)),
|
||||||
|
is_dmenu_mode,
|
||||||
};
|
};
|
||||||
|
|
||||||
main_window.setup_signals();
|
main_window.setup_signals();
|
||||||
@@ -735,12 +741,14 @@ impl MainWindow {
|
|||||||
let mode_label_for_activate = self.mode_label.clone();
|
let mode_label_for_activate = self.mode_label.clone();
|
||||||
let hints_label_for_activate = self.hints_label.clone();
|
let hints_label_for_activate = self.hints_label.clone();
|
||||||
let search_entry_for_activate = self.search_entry.clone();
|
let search_entry_for_activate = self.search_entry.clone();
|
||||||
|
let is_dmenu_mode_for_activate = self.is_dmenu_mode;
|
||||||
|
|
||||||
self.search_entry.connect_activate(move |entry| {
|
self.search_entry.connect_activate(move |entry| {
|
||||||
let selected = results_list_for_activate
|
let selected = results_list_for_activate
|
||||||
.selected_row()
|
.selected_row()
|
||||||
.or_else(|| results_list_for_activate.row_at_index(0));
|
.or_else(|| results_list_for_activate.row_at_index(0));
|
||||||
|
|
||||||
|
// Handle the case where we have a selected item
|
||||||
if let Some(row) = selected {
|
if let Some(row) = selected {
|
||||||
let index = row.index() as usize;
|
let index = row.index() as usize;
|
||||||
let results = current_results_for_activate.borrow();
|
let results = current_results_for_activate.borrow();
|
||||||
@@ -787,6 +795,10 @@ impl MainWindow {
|
|||||||
&providers_for_activate,
|
&providers_for_activate,
|
||||||
);
|
);
|
||||||
if should_close {
|
if should_close {
|
||||||
|
// In dmenu mode, exit with success code
|
||||||
|
if is_dmenu_mode_for_activate {
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
window_for_activate.close();
|
window_for_activate.close();
|
||||||
} else {
|
} else {
|
||||||
// Trigger search refresh for updated widget state
|
// Trigger search refresh for updated widget state
|
||||||
@@ -794,6 +806,16 @@ impl MainWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No item selected/matched - in dmenu mode, output the typed text
|
||||||
|
if is_dmenu_mode_for_activate {
|
||||||
|
let text = entry.text();
|
||||||
|
if !text.is_empty() {
|
||||||
|
println!("{}", text);
|
||||||
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -834,6 +856,7 @@ impl MainWindow {
|
|||||||
let hints_label = self.hints_label.clone();
|
let hints_label = self.hints_label.clone();
|
||||||
let submenu_state = self.submenu_state.clone();
|
let submenu_state = self.submenu_state.clone();
|
||||||
let tab_order = self.tab_order.clone();
|
let tab_order = self.tab_order.clone();
|
||||||
|
let is_dmenu_mode = self.is_dmenu_mode;
|
||||||
|
|
||||||
key_controller.connect_key_pressed(move |_, key, _, modifiers| {
|
key_controller.connect_key_pressed(move |_, key, _, modifiers| {
|
||||||
let ctrl = modifiers.contains(gtk4::gdk::ModifierType::CONTROL_MASK);
|
let ctrl = modifiers.contains(gtk4::gdk::ModifierType::CONTROL_MASK);
|
||||||
@@ -856,6 +879,10 @@ impl MainWindow {
|
|||||||
);
|
);
|
||||||
gtk4::glib::Propagation::Stop
|
gtk4::glib::Propagation::Stop
|
||||||
} else {
|
} else {
|
||||||
|
// In dmenu mode, exit with cancel code (1)
|
||||||
|
if is_dmenu_mode {
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
window.close();
|
window.close();
|
||||||
gtk4::glib::Propagation::Stop
|
gtk4::glib::Propagation::Stop
|
||||||
}
|
}
|
||||||
@@ -873,6 +900,10 @@ impl MainWindow {
|
|||||||
);
|
);
|
||||||
gtk4::glib::Propagation::Stop
|
gtk4::glib::Propagation::Stop
|
||||||
} else {
|
} else {
|
||||||
|
// In dmenu mode, exit with cancel code (1)
|
||||||
|
if is_dmenu_mode {
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
window.close();
|
window.close();
|
||||||
gtk4::glib::Propagation::Stop
|
gtk4::glib::Propagation::Stop
|
||||||
}
|
}
|
||||||
@@ -1310,14 +1341,13 @@ impl MainWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Launch a .desktop file using gio (GLib's desktop entry launcher).
|
/// Launch a .desktop file.
|
||||||
///
|
///
|
||||||
/// gio is always available as it's part of glib2, which is a hard dependency
|
/// When `use_uwsm` is enabled in config, launches via `uwsm app -- <file>`
|
||||||
/// of GTK4. It handles D-Bus activation, field codes, Terminal flag, etc.
|
/// which starts the app in a proper systemd user session.
|
||||||
/// per the freedesktop Desktop Entry specification.
|
|
||||||
///
|
///
|
||||||
/// Optionally wraps with a session manager (uwsm, hyprctl) for proper
|
/// Otherwise, uses `gio launch` which is always available (part of glib2/GTK4)
|
||||||
/// process tracking in Wayland compositors.
|
/// and handles D-Bus activation, field codes, Terminal flag, etc.
|
||||||
fn launch_desktop_file(desktop_path: &str, config: &Config) -> std::io::Result<std::process::Child> {
|
fn launch_desktop_file(desktop_path: &str, config: &Config) -> std::io::Result<std::process::Child> {
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
@@ -1329,25 +1359,32 @@ impl MainWindow {
|
|||||||
return Err(std::io::Error::new(std::io::ErrorKind::NotFound, msg));
|
return Err(std::io::Error::new(std::io::ErrorKind::NotFound, msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
match &config.general.launch_wrapper {
|
if config.general.use_uwsm {
|
||||||
// With wrapper: wrapper manages the process, gio handles the .desktop file
|
// Check if uwsm is available
|
||||||
Some(wrapper) if !wrapper.is_empty() => {
|
let uwsm_available = Command::new("which")
|
||||||
info!("Launching via {} + gio launch: {}", wrapper, desktop_path);
|
.arg("uwsm")
|
||||||
let mut parts: Vec<&str> = wrapper.split_whitespace().collect();
|
.stdout(std::process::Stdio::null())
|
||||||
let cmd = parts.remove(0);
|
.stderr(std::process::Stdio::null())
|
||||||
Command::new(cmd)
|
.status()
|
||||||
.args(&parts)
|
.map(|s| s.success())
|
||||||
.args(["gio", "launch"])
|
.unwrap_or(false);
|
||||||
.arg(desktop_path)
|
|
||||||
.spawn()
|
if !uwsm_available {
|
||||||
}
|
let msg = "uwsm is enabled in config but not installed";
|
||||||
// No wrapper: use gio directly
|
log::error!("{}", msg);
|
||||||
_ => {
|
crate::notify::notify("Launch failed", msg);
|
||||||
info!("Launching via gio launch: {}", desktop_path);
|
return Err(std::io::Error::new(std::io::ErrorKind::NotFound, msg));
|
||||||
Command::new("gio")
|
|
||||||
.args(["launch", desktop_path])
|
|
||||||
.spawn()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info!("Launching via uwsm: {}", desktop_path);
|
||||||
|
Command::new("uwsm")
|
||||||
|
.args(["app", "--", desktop_path])
|
||||||
|
.spawn()
|
||||||
|
} else {
|
||||||
|
info!("Launching via gio: {}", desktop_path);
|
||||||
|
Command::new("gio")
|
||||||
|
.args(["launch", desktop_path])
|
||||||
|
.spawn()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1360,8 +1397,9 @@ impl MainWindow {
|
|||||||
command.to_string()
|
command.to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Detect shell commands that shouldn't use a wrapper
|
// Shell/system commands run directly without uwsm wrapper
|
||||||
let is_shell_command = cmd.starts_with("playerctl ")
|
// (they're typically short-lived or system utilities)
|
||||||
|
let is_system_command = cmd.starts_with("playerctl ")
|
||||||
|| cmd.starts_with("dbus-send ")
|
|| cmd.starts_with("dbus-send ")
|
||||||
|| cmd.starts_with("systemctl ")
|
|| cmd.starts_with("systemctl ")
|
||||||
|| cmd.starts_with("journalctl ")
|
|| cmd.starts_with("journalctl ")
|
||||||
@@ -1371,19 +1409,14 @@ impl MainWindow {
|
|||||||
|| cmd.contains(" > ")
|
|| cmd.contains(" > ")
|
||||||
|| cmd.contains(" < ");
|
|| cmd.contains(" < ");
|
||||||
|
|
||||||
match &config.general.launch_wrapper {
|
// Use uwsm for regular commands if enabled (and not a system command)
|
||||||
Some(wrapper) if !wrapper.is_empty() && !is_shell_command => {
|
if config.general.use_uwsm && !is_system_command {
|
||||||
info!("Launching command via {}: {}", wrapper, cmd);
|
info!("Launching command via uwsm: {}", cmd);
|
||||||
let mut parts: Vec<&str> = wrapper.split_whitespace().collect();
|
Command::new("uwsm")
|
||||||
let wrapper_cmd = parts.remove(0);
|
.args(["app", "--", "sh", "-c", &cmd])
|
||||||
Command::new(wrapper_cmd)
|
.spawn()
|
||||||
.args(&parts)
|
} else {
|
||||||
.args(["sh", "-c", &cmd])
|
Command::new("sh").arg("-c").arg(&cmd).spawn()
|
||||||
.spawn()
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
Command::new("sh").arg("-c").arg(&cmd).spawn()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,22 +17,47 @@
|
|||||||
# │ Runtimes: /usr/lib/owlry/runtimes/*.so Lua/Rune runtimes │
|
# │ Runtimes: /usr/lib/owlry/runtimes/*.so Lua/Rune runtimes │
|
||||||
# └─────────────────────────────────────────────────────────────────────┘
|
# └─────────────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════
|
||||||
|
# DMENU MODE
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════
|
||||||
|
#
|
||||||
|
# Dmenu mode provides interactive selection from piped input.
|
||||||
|
# The selected item is printed to stdout (not executed), so pipe
|
||||||
|
# the output to execute it:
|
||||||
|
#
|
||||||
|
# ┌─────────────────────────────────────────────────────────────────────┐
|
||||||
|
# │ # Screenshot menu │
|
||||||
|
# │ printf '%s\n' \ │
|
||||||
|
# │ "grimblast --notify copy screen" \ │
|
||||||
|
# │ "grimblast --notify copy area" \ │
|
||||||
|
# │ | owlry -m dmenu -p "Screenshot" \ │
|
||||||
|
# │ | sh │
|
||||||
|
# │ │
|
||||||
|
# │ # Git branch checkout │
|
||||||
|
# │ git branch | owlry -m dmenu -p "checkout" | xargs git checkout │
|
||||||
|
# │ │
|
||||||
|
# │ # Package search │
|
||||||
|
# │ pacman -Ssq | owlry -m dmenu -p "install" | xargs sudo pacman -S │
|
||||||
|
# └─────────────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════
|
||||||
# GENERAL
|
# GENERAL
|
||||||
# ═══════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
[general]
|
[general]
|
||||||
show_icons = true
|
show_icons = true
|
||||||
max_results = 10
|
max_results = 100
|
||||||
|
|
||||||
# Terminal emulator for SSH, scripts, etc.
|
# Terminal emulator for SSH, scripts, etc.
|
||||||
# Auto-detection order: $TERMINAL → xdg-terminal-exec → DE-native → Wayland → X11 → xterm
|
# Auto-detection order: $TERMINAL → xdg-terminal-exec → DE-native → Wayland → X11 → xterm
|
||||||
# Uncomment to override:
|
# Uncomment to override:
|
||||||
# terminal_command = "kitty"
|
# terminal_command = "kitty"
|
||||||
|
|
||||||
# Launch wrapper for app execution (auto-detected for uwsm/Hyprland)
|
# Enable uwsm (Universal Wayland Session Manager) for launching apps.
|
||||||
# Examples: "uwsm app --", "hyprctl dispatch exec --", ""
|
# When enabled, apps are launched via "uwsm app --" which starts them
|
||||||
# launch_wrapper = "uwsm app --"
|
# in a proper systemd user session for better process management.
|
||||||
|
# Requires: uwsm to be installed
|
||||||
|
# use_uwsm = true
|
||||||
|
|
||||||
# Header tabs - providers shown as toggle buttons (Ctrl+1, Ctrl+2, etc.)
|
# Header tabs - providers shown as toggle buttons (Ctrl+1, Ctrl+2, etc.)
|
||||||
# Values: app, cmd, uuctl, bookmark, calc, clip, dmenu, emoji, file, script, ssh, sys, web
|
# Values: app, cmd, uuctl, bookmark, calc, clip, dmenu, emoji, file, script, ssh, sys, web
|
||||||
@@ -62,22 +87,54 @@ border_radius = 12
|
|||||||
# text_secondary = "#565f89"
|
# text_secondary = "#565f89"
|
||||||
# accent = "#7aa2f7"
|
# accent = "#7aa2f7"
|
||||||
# accent_bright = "#89b4fa"
|
# accent_bright = "#89b4fa"
|
||||||
|
#
|
||||||
|
# Provider badge colors (optional)
|
||||||
|
# badge_app = "#7aa2f7"
|
||||||
|
# badge_cmd = "#9ece6a"
|
||||||
|
# badge_bookmark = "#e0af68"
|
||||||
|
# badge_calc = "#bb9af7"
|
||||||
|
# badge_clip = "#7dcfff"
|
||||||
|
# badge_dmenu = "#c0caf5"
|
||||||
|
# badge_emoji = "#f7768e"
|
||||||
|
# badge_file = "#73daca"
|
||||||
|
# badge_script = "#ff9e64"
|
||||||
|
# badge_ssh = "#2ac3de"
|
||||||
|
# badge_sys = "#f7768e"
|
||||||
|
# badge_uuctl = "#9ece6a"
|
||||||
|
# badge_web = "#7aa2f7"
|
||||||
|
# badge_media = "#bb9af7"
|
||||||
|
# badge_weather = "#7dcfff"
|
||||||
|
# badge_pomo = "#f7768e"
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════
|
||||||
# PLUGINS
|
# PLUGINS
|
||||||
# ═══════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════
|
||||||
#
|
#
|
||||||
# All installed plugins are loaded by default. Use 'disabled' to blacklist.
|
# All installed plugins are loaded by default. Use 'disabled_plugins' to blacklist.
|
||||||
# Plugin IDs: calculator, system, ssh, clipboard, emoji, scripts, bookmarks,
|
# Plugin IDs: calculator, system, ssh, clipboard, emoji, scripts, bookmarks,
|
||||||
# websearch, filesearch, systemd, weather, media, pomodoro
|
# websearch, filesearch, systemd, weather, media, pomodoro
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
|
enabled = true # Master switch for all plugins
|
||||||
|
|
||||||
# Plugins to disable (by ID)
|
# Plugins to disable (by ID)
|
||||||
disabled = []
|
disabled_plugins = []
|
||||||
|
|
||||||
# Examples:
|
# Examples:
|
||||||
# disabled = ["emoji", "pomodoro"] # Disable specific plugins
|
# disabled_plugins = ["emoji", "pomodoro"] # Disable specific plugins
|
||||||
# disabled = ["weather", "media"] # Disable widget plugins
|
# disabled_plugins = ["weather", "media"] # Disable widget plugins
|
||||||
|
|
||||||
|
# Custom plugin registry URL (defaults to official registry)
|
||||||
|
# registry_url = "https://my-registry.example.com/plugins.json"
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────
|
||||||
|
# Sandbox settings (for Lua/Rune script plugins)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────
|
||||||
|
# [plugins.sandbox]
|
||||||
|
# allow_filesystem = false # Allow file system access beyond plugin dir
|
||||||
|
# allow_network = false # Allow network requests
|
||||||
|
# allow_commands = false # Allow shell command execution
|
||||||
|
# memory_limit = 67108864 # Memory limit in bytes (64 MB default)
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════
|
||||||
# PROVIDERS
|
# PROVIDERS
|
||||||
@@ -112,10 +169,26 @@ calculator = true # Calculator (= expression)
|
|||||||
websearch = true # Web search (? query)
|
websearch = true # Web search (? query)
|
||||||
|
|
||||||
# ─────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────
|
||||||
# Plugin settings
|
# Widget providers (displayed at top of results)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────
|
||||||
|
media = true # MPRIS media player controls
|
||||||
|
weather = false # Weather widget (disabled by default)
|
||||||
|
pomodoro = false # Pomodoro timer (disabled by default)
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────
|
||||||
|
# Provider settings
|
||||||
# ─────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
# Web search engine
|
# Web search engine
|
||||||
# Options: google, duckduckgo, bing, startpage, searxng, brave, ecosia
|
# Options: google, duckduckgo, bing, startpage, searxng, brave, ecosia
|
||||||
# Or custom URL: "https://search.example.com/?q={query}"
|
# Or custom URL: "https://search.example.com/?q={query}"
|
||||||
search_engine = "duckduckgo"
|
search_engine = "duckduckgo"
|
||||||
|
|
||||||
|
# Weather settings (when weather = true)
|
||||||
|
# weather_provider = "wttr.in" # Options: wttr.in, openweathermap, open-meteo
|
||||||
|
# weather_location = "Berlin" # City name or coordinates
|
||||||
|
# weather_api_key = "" # Required for openweathermap
|
||||||
|
|
||||||
|
# Pomodoro settings (when pomodoro = true)
|
||||||
|
# pomodoro_work_mins = 25 # Work session duration
|
||||||
|
# pomodoro_break_mins = 5 # Break duration
|
||||||
|
|||||||
Reference in New Issue
Block a user