diff --git a/README.md b/README.md
index 1da8bc2..417399a 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,8 @@
Swayr consists of a demon, and a client. The demon `swayrd` records
window/workspace creations, deletions, and focus changes using sway's JSON IPC
-interface. The client `swayr` offers subcommands, see `swayr --help`.
+interface. The client `swayr` offers subcommands, see `swayr --help`, and
+sends them to the demon which executes them.
Right now, there are these subcommands:
* `next-window` focuses the next window in depth-first iteration order of the
@@ -82,16 +83,72 @@ and logging are optional.
## Configuration
Swayr can be configured using the `~/.config/swayr/config.toml` config file.
+
If it doesn't exist, a simple default configuration will be created on the
first invocation for use with the [wofi](https://todo.sr.ht/~scoopta/wofi)
-launcher. It should be easy to adapt that default config for usage with other
-launchers such as [dmenu](https://tools.suckless.org/dmenu/),
+launcher.
+
+It should be easy to adapt that default config for usage with other launchers
+such as [dmenu](https://tools.suckless.org/dmenu/),
[bemenu](https://github.com/Cloudef/bemenu),
[rofi](https://github.com/davatorium/rofi), a script spawning a terminal with
[fzf](https://github.com/junegunn/fzf), or whatever. The only requirement is
-that the launcher needs to be able to read the items to choose from from stdin.
+that the launcher needs to be able to read the items to choose from from stdin,
+and spit out the selected item to stdout.
+
+The default config looks like this:
+
+```toml
+[launcher]
+executable = 'wofi'
+args = [
+ '--show=dmenu',
+ '--allow-markup',
+ '--allow-images',
+ '--insensitive',
+ '--cache-file=/dev/null',
+ '--parse-search',
+ '--prompt={prompt}',
+]
+
+[format]
+window_format = '{urgency_start}“{title}”{urgency_end} — {app_name} on workspace {workspace_name} ({id})'
+workspace_format = 'Workspace {name} ({id})'
+urgency_start = ''
+urgency_end = ''
+```
-TODO: Show default config and describe it.
+In the `[launcher]` section, you can specify the launchen/menu program using
+the `executable` name or full path, and the `args` (flags and options) it
+should get passed. If some argument contains the placeholder `{prompt}`, it is
+replaced with a prompt such as "Switch to window" depending on context.
+
+In the `[format]` section, format strings are specified defining how selection
+choises are to be layed out. `wofi` supports [pango
+markup](https://docs.gtk.org/Pango/pango_markup.html) which makes it possible
+to style the text using HTML and CSS. The following formats are supported
+right now.
+* `window_format` defines how windows are displayed. The placeholder `{title}`
+ is replaced with the window's title, `{app_name}` with the application name,
+ `{workspace_name}` with the name or number of the workspace the window is
+ shown, and `{id}` is the window's sway-internal con id. There are also the
+ placeholders `{urcency_start}` and `{urgency_end}` which get replaced by the
+ empty string if the window has no urgency flag, and with the values of the
+ same-named formats if the window has the urgency flag set. That makes it
+ possible to highlight urgent windows as shown in the default config.
+* `workspace_format` defines how workspaces are displayed. There are the
+ placeholders `{name}` which gets replaced by the workspace's number or name,
+ and `{id}` which gets replaced by the sway-internal con id of the workspace.
+* `urgency_start` is a string which replaces the `{urgency_start}` placeholder
+ in `window_format`.
+* `urgency_end` is a string which replaces the `{urgency_end}` placeholder in
+ `window_format`.
+
+It is crucial that during selection (using wofi or some other launcher) each
+window has a different display string. Therefore, it is highly recommended to
+include the `{id}` placeholder at least in `window_format`. Otherwise, e.g.,
+two terminals (of the same terminal app) with the same working directory (and
+therefore, the same title) wouldn't be distinguishable.
## Questions & Patches
diff --git a/src/con.rs b/src/con.rs
index 46d733e..794e82e 100644
--- a/src/con.rs
+++ b/src/con.rs
@@ -122,46 +122,22 @@ impl<'a> fmt::Display for Window<'a> {
impl<'a> DisplayFormat for Window<'a> {
fn format_for_display(&self, cfg: &cfg::Config) -> String {
- let default = cfg::Config::default();
+ let default_format = cfg::Format::default();
let fmt = cfg
.format
.as_ref()
.and_then(|f| f.window_format.as_ref())
- .unwrap_or_else(|| {
- default
- .format
- .as_ref()
- .unwrap()
- .window_format
- .as_ref()
- .unwrap()
- });
+ .unwrap_or_else(|| default_format.window_format.as_ref().unwrap());
let urgency_start = cfg
.format
.as_ref()
.and_then(|f| f.urgency_start.as_ref())
- .unwrap_or_else(|| {
- default
- .format
- .as_ref()
- .unwrap()
- .urgency_start
- .as_ref()
- .unwrap()
- });
+ .unwrap_or_else(|| default_format.urgency_start.as_ref().unwrap());
let urgency_end = cfg
.format
.as_ref()
.and_then(|f| f.urgency_end.as_ref())
- .unwrap_or_else(|| {
- default
- .format
- .as_ref()
- .unwrap()
- .urgency_end
- .as_ref()
- .unwrap()
- });
+ .unwrap_or_else(|| default_format.urgency_end.as_ref().unwrap());
fmt.replace("{id}", format!("{}", self.get_id()).as_str())
.replace(
@@ -391,19 +367,13 @@ impl<'a> fmt::Display for Workspace<'a> {
impl<'a> DisplayFormat for Workspace<'a> {
fn format_for_display(&self, cfg: &cfg::Config) -> String {
- let default = cfg::Config::default();
+ let default_format = cfg::Format::default();
let fmt = cfg
.format
.as_ref()
.and_then(|f| f.workspace_format.as_ref())
.unwrap_or_else(|| {
- default
- .format
- .as_ref()
- .unwrap()
- .workspace_format
- .as_ref()
- .unwrap()
+ default_format.workspace_format.as_ref().unwrap()
});
fmt.replace("{id}", format!("{}", self.get_id()).as_str())
diff --git a/src/config.rs b/src/config.rs
index f635a06..3e848ab 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -28,30 +28,51 @@ pub struct Config {
pub format: Option,
}
+impl Default for Launcher {
+ fn default() -> Self {
+ Launcher {
+ executable: Some("wofi".to_string()),
+ args: Some(vec![
+ "--show=dmenu".to_string(),
+ "--allow-markup".to_string(),
+ "--allow-images".to_string(),
+ "--insensitive".to_string(),
+ "--cache-file=/dev/null".to_string(),
+ "--parse-search".to_string(),
+ "--prompt={prompt}".to_string(),
+ ]),
+ }
+ }
+}
+
+impl Default for Format {
+ fn default() -> Self {
+ Format {
+ window_format: Some(
+ "{urgency_start}“{title}”{urgency_end} \
+ — {app_name} on workspace {workspace_name} \
+ ({id})"
+ .to_string(),
+ ),
+ workspace_format: Some(
+ "Workspace {name} \
+ ({id})"
+ .to_string(),
+ ),
+ urgency_start: Some(
+ ""
+ .to_string(),
+ ),
+ urgency_end: Some("".to_string()),
+ }
+ }
+}
+
impl Default for Config {
fn default() -> Self {
Config {
- launcher: Some(Launcher {
- executable: Some("wofi".to_string()),
- args: Some(vec![
- "--show=dmenu".to_string(),
- "--allow-markup".to_string(),
- "--allow-images".to_string(),
- "--insensitive".to_string(),
- "--cache-file=/dev/null".to_string(),
- "--parse-search".to_string(),
- "--prompt={prompt}".to_string(),
- ]),
- }),
- format: Some(Format {
- window_format: Some(
- "{urgency_start}“{title}”{urgency_end} — {app_name} on workspace {workspace_name} ({id})"
- .to_string(),
- ),
- workspace_format: Some("Workspace {name} ({id})".to_string()),
- urgency_start: Some("".to_string()),
- urgency_end: Some("".to_string())
- }),
+ launcher: Some(Launcher::default()),
+ format: Some(Format::default()),
}
}
}
diff --git a/src/util.rs b/src/util.rs
index 50d9634..5094f72 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -52,27 +52,17 @@ where
map.insert(s, c);
}
- let default = cfg::Config::default();
+ let launcher_default = cfg::Launcher::default();
let launcher_exec = cfg
.launcher
.as_ref()
.and_then(|l| l.executable.as_ref())
- .unwrap_or_else(|| {
- default
- .launcher
- .as_ref()
- .unwrap()
- .executable
- .as_ref()
- .unwrap()
- });
+ .unwrap_or_else(|| launcher_default.executable.as_ref().unwrap());
let args: Vec = cfg
.launcher
.as_ref()
.and_then(|l| l.args.as_ref())
- .unwrap_or_else(|| {
- default.launcher.as_ref().unwrap().args.as_ref().unwrap()
- })
+ .unwrap_or_else(|| launcher_default.args.as_ref().unwrap())
.iter()
.map(|a| a.replace("{prompt}", prompt))
.collect();