├── src ├── lib.rs ├── settings.rs ├── config.toml └── main.rs ├── .vim └── coc-settings.json ├── screenshots ├── after.png └── before.png ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── Cargo.lock └── LICENSE-APACHE-2.0 /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod settings; 2 | -------------------------------------------------------------------------------- /.vim/coc-settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "swaycons", 4 | "swayipc" 5 | ] 6 | } -------------------------------------------------------------------------------- /screenshots/after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allie-wake-up/swaycons/HEAD/screenshots/after.png -------------------------------------------------------------------------------- /screenshots/before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allie-wake-up/swaycons/HEAD/screenshots/before.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # These are backup files generated by rustfmt 6 | **/*.rs.bk 7 | 8 | 9 | # Added by cargo 10 | 11 | /target 12 | 13 | Session.vim 14 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "swaycons" 3 | version = "0.3.1" 4 | authors = ["Allie Stephan "] 5 | description = "swaycons adds nerd font icons to sway window titles" 6 | repository = "https://github.com/actuallyallie/swaycons" 7 | license = "MIT / Apache-2.0" 8 | keywords = ["sway", "swaywm", "icons"] 9 | edition = "2021" 10 | readme = "README.md" 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | [[bin]] 14 | name = "swaycons" 15 | path = "src/main.rs" 16 | 17 | [dependencies] 18 | config = "0.11.0" 19 | regex = "1.5.4" 20 | swayipc = "3.0.0" 21 | xdg = "2.4.0" 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Allie Stephan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/settings.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use config::{Config, ConfigError, File, FileFormat, Value}; 3 | use xdg::BaseDirectories; 4 | 5 | #[derive(Clone, Debug)] 6 | pub struct GlobalSettings { 7 | pub color: String, 8 | pub focused_color: Option, 9 | pub icon: String, 10 | pub size: String, 11 | pub separator: String, 12 | } 13 | 14 | impl GlobalSettings { 15 | pub fn new( 16 | color: String, 17 | focused_color: Option, 18 | icon: String, 19 | size: String, 20 | separator: String, 21 | ) -> GlobalSettings { 22 | GlobalSettings { 23 | color, 24 | focused_color, 25 | icon, 26 | size, 27 | separator, 28 | } 29 | } 30 | 31 | pub fn from(mut config: HashMap) -> GlobalSettings { 32 | let color = config.remove("color").unwrap(); 33 | let focused_color = match config.remove("focused_color") { 34 | Some(color) => if color.len() == 7 && color.starts_with("#") { 35 | Some(color) 36 | } else { 37 | None 38 | }, 39 | None => None 40 | }; 41 | let icon = config.remove("icon").unwrap(); 42 | let size = config.remove("size").unwrap(); 43 | let separator = config 44 | .remove("separator") 45 | .unwrap_or(String::from(" ")); 46 | GlobalSettings { 47 | color, 48 | focused_color, 49 | icon, 50 | size, 51 | separator, 52 | } 53 | } 54 | } 55 | 56 | #[derive(Clone, Debug)] 57 | pub struct BaseSettings { 58 | pub color: Option, 59 | pub icon: Option, 60 | pub size: Option, 61 | } 62 | 63 | impl BaseSettings { 64 | pub fn new(mut config: HashMap) -> BaseSettings { 65 | let color = match config.remove("color") { 66 | Some(color) => match color.into_str() { 67 | Ok(color) => Some(color), 68 | Err(_e) => None, 69 | }, 70 | None => None, 71 | }; 72 | let icon = match config.remove("icon") { 73 | Some(icon) => match icon.into_str() { 74 | Ok(icon) => Some(icon), 75 | Err(_e) => None, 76 | }, 77 | None => None, 78 | }; 79 | let size = match config.remove("size") { 80 | Some(size) => match size.into_str() { 81 | Ok(size) => Some(size), 82 | Err(_e) => None, 83 | }, 84 | None => None, 85 | }; 86 | BaseSettings { color, icon, size } 87 | } 88 | } 89 | 90 | #[derive(Clone, Debug)] 91 | pub struct TitleSettings { 92 | pub app_id: Option>, 93 | pub base: BaseSettings, 94 | } 95 | 96 | impl TitleSettings { 97 | pub fn new(mut config: HashMap) -> TitleSettings { 98 | let app_id: Option> = match config.remove("app_id") { 99 | Some(app_id) => { 100 | match app_id.try_into() { 101 | Ok(app_id) => Some(app_id), 102 | Err(_e) => None, 103 | } 104 | } 105 | None => None, 106 | }; 107 | let base = BaseSettings::new(config); 108 | TitleSettings { app_id, base } 109 | } 110 | } 111 | 112 | #[derive(Clone, Debug)] 113 | pub struct Settings { 114 | pub global: GlobalSettings, 115 | pub title: HashMap, 116 | pub app_id: HashMap, 117 | } 118 | 119 | static DEFAULT_SETTINGS: &str = include_str!(r"./config.toml"); 120 | 121 | pub fn get_settings() -> Result { 122 | let xdg_dirs = BaseDirectories::with_prefix("swaycons").unwrap(); 123 | let config_path = xdg_dirs 124 | .place_config_file("config.toml").unwrap(); 125 | let mut settings = Config::new(); 126 | settings.merge(File::from_str(DEFAULT_SETTINGS, FileFormat::Toml))?; 127 | settings.merge(File::from(config_path).required(false))?; 128 | let global_map: HashMap = settings.get("global").unwrap(); 129 | let global = GlobalSettings::from(global_map); 130 | let title_config: HashMap> = settings.get("title").unwrap(); 131 | let mut title = HashMap::new(); 132 | for (key, value) in title_config { 133 | title.insert(key, TitleSettings::new(value)); 134 | } 135 | let mut app_id = HashMap::new(); 136 | let app_id_config: HashMap> = settings.get("app_id").unwrap(); 137 | for (key, value) in app_id_config { 138 | app_id.insert(key, BaseSettings::new(value)); 139 | } 140 | Ok(Settings { 141 | global, 142 | title, 143 | app_id 144 | }) 145 | } 146 | -------------------------------------------------------------------------------- /src/config.toml: -------------------------------------------------------------------------------- 1 | [global] 2 | color = "#FFFFFF" 3 | focused_color = "#FFFFFF" 4 | icon = "󰖯" 5 | size = "14pt" 6 | separator = " " 7 | 8 | [app_id] 9 | "dev.alextren.Spot" = { icon = "󰓇", color = "#1ed760" } 10 | "org.daa.NeovimGtk" = { icon = "", color = "#8fff6d" } 11 | "org.gnome.Calculator" = { icon = "󰃬" } 12 | "org.gnome.clocks" = { icon = "󱑈" } 13 | "org.gnome.Nautilus" = { icon = "󰉖", color = "#6291d6" } 14 | 1Password = { icon = "󰍁", color = "#0572ec" } 15 | Alacritty = { icon = "" } 16 | Deezer = { icon = "󰋋", color = "#ef5466" } 17 | Insomnia = { icon = "", color = "#7100df" } 18 | Signal = { icon = "󰭹", color = "#3777f0" } 19 | Slack = { icon = "󰒱", color = "#2eb67d" } 20 | Spotify = { icon = "󰓇", color = "#1ed760" } 21 | beekeeper-studio = { icon = "󰆼", color = "#fad83b" } 22 | chromium = { icon = "", color = "#a1c2fa", size = "13pt" } 23 | discord = { icon = "󰙯", color = "#404eed" } 24 | firefox = { icon = "", color = "#ff8817", size = "13pt" } 25 | foot = { icon = "" } 26 | geary = { icon = "", color = "#f6d32d" } 27 | gnome-calendar = { icon = "", color = "#3584e4" } 28 | gnumeric = { icon = "󰈛", color = "#34a853" } 29 | google-chrome = { icon = "", color = "#4285f4" } 30 | neovide = { icon = "", color = "#8fff6d" } 31 | nvim-qt = { icon = "", color = "#8fff6d" } 32 | shotwell = { icon = "", color = "#4f7aaf" } 33 | zoom = { icon = "󰕧", color = "#2d8cff" } 34 | libreoffice-writer = { icon = "", color = "#00A500", size = "12pt" } 35 | libreoffice-calc = { icon = "󰱿", color = "#00A500" } 36 | 37 | [title] 38 | "(?i)vim" = { app_id = ["foot", "Alacritty"], icon = "", color = "#8fff6d" } 39 | "(cloud|developers)\\.google.com" = { icon = "", color = "#4285f4" } 40 | "192\\.168\\.0\\.1|192\\.168\\.86\\.1|ui\\.com" = { icon = "󰖩", color = "#004cb6" } 41 | "1password\\.com" = { icon = "󰍁", color = "#0572ec" } 42 | "amazon\\.com" = { icon = "", color = "#ff9900" } 43 | "angular\\.io" = { icon = "", color = "#dd0031" } 44 | "archlinux\\.org" = { icon = "", color = "#1793d1" } 45 | "atlassian\\.(net|com)" = { icon = "󰌃", color = "#0052cc" } 46 | "aws\\.amazon\\.com" = { icon = "", color = "#ff9900" } 47 | "bitbucket\\.org" = { icon = "󰂨", color = "#0052cc" } 48 | "calendar\\.google\\.com" = { icon = "󰃶", color = "#4285f4" } 49 | "coda\\.io" = { icon = "", color = "#f46a54" } 50 | "crates\\.io" = { icon = "󰏗", color = "#ffc933" } 51 | "deezer\\.com" = { icon = "󰋋", color = "#ef5466" } 52 | "discogs\\.com" = { icon = "󰀥" } 53 | "discord\\.com" = { icon = "󰙯", color = "#404eed" } 54 | "docs\\.google\\.com" = { icon = "", color = "#4285f4" } 55 | "docs\\.google\\.com/spreadsheets" = { icon = "󰈛", color = "#34a853" } 56 | "docs\\.rs|rust-lang\\.org" = { icon = "", size = "16pt" } 57 | "drive\\.google\\.com" = { icon = "󰊶", color = "#4285f4" } 58 | "dropbox\\.com" = { icon = "󰇣", color = "#0061fe" } 59 | "duckduckgo\\.com" = { icon = "󰇥", color = "#de5833" } 60 | "facebook\\.com" = { icon = "", color = "#1877f2" } 61 | "fastmail\\.com" = { icon = "󰗰", color = "#1565c0" } 62 | "fastmail\\.com/calendar" = { icon = "", color = "#1565c0" } 63 | "fastmail\\.com/contacts" = { size = "11pt", icon = "", color = "#1565c0" } 64 | "feedbin\\.com" = { icon = "" } 65 | "gitbook\\.com" = { icon = "", color = "#346ddb" } 66 | "github\\.com" = { icon = "" } 67 | "google\\.com" = { icon = "", color = "#4285f4" } 68 | "google\\.com/maps" = { icon = "󰗵", color = "#4caf50" } 69 | "insomnia\\.rest" = { icon = "", color = "#7100df" } 70 | "keep\\.google\\.com" = { icon = "󰛜", color = "#fbbc04" } 71 | "last\\.fm" = { icon = "", color = "#ba0000" } 72 | "mail\\.google\\.com" = { icon = "󰊫", color = "#ad1f1c" } 73 | "meet\\.google\\.com" = { icon = "", color = "#4285f4" } 74 | "messages\\.google\\.com" = { icon = "󰍩", color = "#4285f4" } 75 | "mozilla\\.org" = { icon = "" } 76 | "nerdfonts\\.com" = { icon = "", color = "#ffce3e" } 77 | "nytimes\\.com/games/wordle" = { icon = "󰈭", color = "#538d4e" } 78 | "photos\\.google\\.com" = { icon = "󰄄", color = "#4285f4" } 79 | "reactjs\\.org" = { icon = "", color = "#61dafb" } 80 | "reddit\\.com" = { icon = "󰑍", color = "#ff4500" } 81 | "slack\\.com" = { icon = "󰒱", color = "#2eb67d" } 82 | "sleeper\\.com" = { icon = "󰉟", color = "#7c8ef4" } 83 | "spotify\\.com" = { icon = "󰓇", color = "#1ed760" } 84 | "sr\\.ht|sourcehut\\.org" = { icon = "" } 85 | "stackoverflow\\.com" = { icon = "", color = "#f48225" } 86 | "startpage\\.com" = { icon = "", color = "#6573ff" } 87 | "stripe\\.com" = { icon = "", color = "#635bff" } 88 | "ticktick\\.com" = { icon = "󰄴", color = "#4774f9" } 89 | "todoist\\.com" = { icon = "󰄲", color = "#e44232" } 90 | "travis-ci\\.(com|org)" = { icon = "", color = "#cd324a" } 91 | "twitter\\.com" = { icon = "", color = "#1d9bf0" } 92 | "webpack\\.js\\.org" = { icon = "󰆧", color = "#8ed6fb" } 93 | "wikipedia\\.org" = { icon = "" } 94 | "youneedabudget\\.com" = { icon = "", color = "#4495d7" } 95 | "bandcamp\\.com" = { icon = "", color = "#1da0c3", size = "13pt" } 96 | "zoom\\.us" = { icon = "󰕧", color = "#2d8cff" } 97 | aerc = { app_id = ["foot", "Alacritty"], icon = "󰇰" } 98 | litecli = { app_id = ["foot", "Alacritty"], icon = "󰆼", color = "#c74451" } 99 | nnn = { app_id = ["foot", "Alacritty"], icon = "󰉖" } 100 | pgcli = { app_id = ["foot", "Alacritty"], icon = "󰆼", color = "#c74451" } 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # swaycons 2 | 3 | Window Icons in Sway with Nerd Fonts! 4 | 5 | ## Why? 6 | 7 | - I heavily use sway tabs and adding icons makes it a lot easier to visually pick out the window that I'm looking for. 8 | - Sway issue discussing window icons: https://github.com/swaywm/sway/issues/4882 9 | - Workarounds using just the sway config have significant limitations. Sway will only trigger changes for `for_window [title="firefox"] title_format {icon} %title` type configs once. That makes it less than ideal for icons in many cases. 10 | - Because swaycons has a default config included, it's a lot simpler to get running than configuring font icons manually in a sway config 11 | 12 | Go from 13 | ![before](/screenshots/before.png) 14 | 15 | to 16 | 17 | ![after](/screenshots/after.png) 18 | 19 | ## Installation 20 | 21 | ### Prerequisites 22 | 23 | 1. [Install Rust](https://www.rust-lang.org/tools/install) 24 | 2. Sway 25 | 3. A font with icons and [pango](https://docs.gtk.org/Pango/) enabled in your sway config. The default config assumes a nerd font 26 | is used. The config should look something like this: 27 | 28 | ``` 29 | font pango:FuraCode Nerd Font 11 30 | ``` 31 | 32 | ### Install Swaycons 33 | 34 | 1. `cargo install swaycons` 35 | 36 | 37 | ## Usage 38 | 39 | - run `swaycons` 40 | - recommend adding something like `exec swaycons` to sway config 41 | - swaycons must be restarted after config changes 42 | 43 | 44 | ## Config 45 | 46 | - by default the config file should be placed at `~/.config/swaycons/config.toml`, but it will look for `swaycons/config.toml` in whatever your configured XDG config folder is. 47 | - the default config is included in the binary and can be viewed [here](src/config.toml) 48 | - Your custom config will add to the default config 49 | - The best place to find icons for nerd fonts is the [nerd fonts cheat sheet](https://www.nerdfonts.com/cheat-sheet) 50 | - learn about pango Attributes [here](https://docs.gtk.org/Pango/pango_markup.html#the-span-attributes) 51 | - to find the `app_id` or `class` I recommend running `swaymsg -t get_tree | less` and using `/` to search for the app you're looking for 52 | - There are a lot of great resources to learn how to write regular expressions out there. The examples in the title section will cover most simple cases though. 53 | - Here is an example with comments: 54 | 55 | ```toml 56 | 57 | # global section. all windows will default to these settings 58 | [global] 59 | color = "#FFFFFF" # this must be a valid color 60 | focused_color = "#FFFFFF" # to disable a focused_color set this to "" 61 | icon = "󰖯" # to disable a default icon just set this to "" 62 | size = "14pt" # must be a valid pango size value 63 | separator = " " # anything between the icon and window title 64 | 65 | # app_id section. This does an exact string comparison to the app_id or 66 | # window_properties.class value reported in swaymsg -t get_tree for the window 67 | # It will be app_id for wayland apps and window_properties.class for X11 apps 68 | [app_id] 69 | chromium = { icon = "", color = "#a1c2fa", size = "13pt" } 70 | firefox = { icon = "", color = "#ff8817" } 71 | foot = { icon = "" } 72 | neovide = { icon = "", color = "#8fff6d" } 73 | 74 | # This does a regex match on the window title. Matches from this section 75 | # will take precedence over matches from the app_id section. A very basic 76 | # algorithm is used to select the more exact regex if there are multiple 77 | # matches. If 1 regex contains another it will choose the longer one. For 78 | # instance mail\\.google\\.com and google\\.com/maps will be chosen over 79 | # google\\.com 80 | [title] 81 | # escape . for an exact match. Normally . matches any character 82 | "crates\\.io" = { icon = "󰏗", color = "#ffc933" } 83 | "github\\.com" = { icon = "" } 84 | "google\\.com" = { icon = "", color = "#4285f4" } 85 | "google\\.com/maps" = { icon = "󰗵", color = "#4caf50" } 86 | "mail\\.google\\.com" = { icon = "󰊫", color = "#ad1f1c" } 87 | 88 | # use | for or 89 | "sr\\.ht|sourcehut\\.org" = { icon = "" } 90 | 91 | # can do an or around just a substring with (a|b) 92 | "travis-ci\\.(com|org)" = { icon = "", color = "#cd324a" } 93 | 94 | # The app_id setting means that this will only match if both the title matches 95 | # the regex and the app_id or window_properties.class equals one of the values 96 | # provided in the app_id array 97 | # For example this allows a vim logo in the terminal but keeps a github logo 98 | # when viewing a github page with vim in the repository name 99 | vim = { app_id = ["foot", "Alacritty"], icon = "", color = "#8fff6d" } 100 | ``` 101 | 102 | ## Replace Firefox Tabs with Sway Tabs 103 | 104 | This plugin is extremely useful when using sway tabs instead of browser tabs. To get this working properly with Firefox a few steps and plugins are necessary: 105 | 106 | 1. Open Firefox Settings and Disable `Open links in tabs instead of new windows` 107 | 2. Install [Tab-less](https://github.com/iainbeeston/tab-less) from [Firefox Add-ons](https://addons.mozilla.org/en-US/firefox/search/?q=tab-less) 108 | 3. Install [URL in Title](https://github.com/M-Gregoire/Firefox-AddUrlToWindowsTitle) from [Firefox Add-ons](https://addons.mozilla.org/en-US/firefox/addon/url-in-title-keepass/?utm_source=addons.mozilla.org&utm_medium=referral&utm_content=search) 109 | 4. Hide the tab bar - directions are in the [sidebery github wiki](https://github.com/mbnuqw/sidebery/wiki/Firefox-Styles-Snippets-(via-userChrome.css)#firefox-styles-snippets-via-userchromecss) 110 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use regex::RegexSet; 2 | use std::collections::HashMap; 3 | use swaycons::settings::{get_settings, BaseSettings, GlobalSettings, TitleSettings}; 4 | use swayipc::{Connection, Event, EventType, Fallible}; 5 | 6 | const DEBUG: bool = false; 7 | 8 | fn main() -> Fallible<()> { 9 | let settings = get_settings().unwrap(); 10 | let global_settings = settings.global; 11 | let global_base = BaseSettings { 12 | color: Some(global_settings.color.clone()), 13 | icon: Some(global_settings.icon.clone()), 14 | size: Some(global_settings.size.clone()), 15 | }; 16 | let title_settings = settings.title; 17 | let app_id_settings = settings.app_id; 18 | let title_set = RegexSet::new(title_settings.keys()).unwrap(); 19 | let mut ignore: HashMap> = HashMap::new(); 20 | let mut last_focused = None; 21 | let mut last_focused_settings: Option<&BaseSettings> = None; 22 | let mut connection = Connection::new()?; 23 | for event in Connection::new()?.subscribe([EventType::Window])? { 24 | if let Event::Window(w) = event? { 25 | let id = w.container.id; 26 | let app_id = match w.container.window_properties { 27 | Some(properties) => properties.class.unwrap_or_default(), 28 | None => w.container.app_id.unwrap_or_default(), 29 | }; 30 | let focused = w.container.focused; 31 | let name = w.container.name.unwrap_or_default(); 32 | let ignore_entry = ignore.entry(id).or_default(); 33 | if DEBUG { 34 | println!("id: {}, app_id: {}, name: {}", id, app_id, name); 35 | } 36 | let mut ignore_matcher: Option = None; 37 | let settings = match find_best_match(&title_set, &name, &title_settings, &app_id) { 38 | Some(index) => { 39 | let pattern = title_set.patterns().get(index); 40 | if pattern.is_some() { 41 | ignore_matcher = match pattern { 42 | Some(p) => Some(p.to_string()), 43 | None => None, 44 | }; 45 | let settings = title_settings.get(pattern.unwrap()).unwrap(); 46 | &settings.base 47 | } else { 48 | &global_base 49 | } 50 | } 51 | None => { 52 | let settings = app_id_settings.get(&app_id); 53 | ignore_matcher = Some(app_id); 54 | if settings.is_some() { 55 | settings.unwrap() 56 | } else { 57 | &global_base 58 | } 59 | } 60 | }; 61 | if ignore_matcher.is_some() && *ignore_entry != ignore_matcher 62 | || (focused && !(last_focused == Some(id))) 63 | { 64 | if DEBUG { 65 | println!("setting icon for id: {}, focused: {}", id, focused); 66 | } 67 | set_icon(id, &settings, &global_settings, &mut connection, focused)?; 68 | } 69 | if focused && Some(id) != last_focused { 70 | if let Some(last_id) = last_focused { 71 | if let Some(last_settings) = last_focused_settings { 72 | if DEBUG { 73 | println!("id: {} lost focus", last_id); 74 | } 75 | set_icon( 76 | last_id, 77 | last_settings, 78 | &global_settings, 79 | &mut connection, 80 | false, 81 | )?; 82 | } 83 | } 84 | last_focused = Some(id); 85 | } 86 | 87 | if focused { 88 | last_focused_settings = Some(&settings); 89 | } 90 | 91 | ignore.insert(id, ignore_matcher); 92 | } 93 | } 94 | Ok(()) 95 | } 96 | 97 | fn find_best_match( 98 | title_set: &RegexSet, 99 | name: &str, 100 | title_settings: &HashMap, 101 | app_id: &String, 102 | ) -> Option { 103 | let matches = title_set.matches(name); 104 | let mut best_match = None; 105 | for m in matches.into_iter() { 106 | let current = title_set.patterns().get(m).unwrap(); 107 | let settings = title_settings.get(current).unwrap(); 108 | if let Some(app_id_match) = settings.app_id.as_ref() { 109 | if !app_id_match.contains(app_id) { 110 | continue; 111 | } 112 | } 113 | 114 | if best_match == None { 115 | best_match = Some(m); 116 | } else { 117 | let best = title_set.patterns().get(best_match.unwrap()).unwrap(); 118 | let current = title_set.patterns().get(m).unwrap(); 119 | if current.contains(best) { 120 | best_match = Some(m); 121 | } 122 | } 123 | } 124 | best_match 125 | } 126 | 127 | fn set_icon( 128 | id: i64, 129 | settings: &BaseSettings, 130 | global_settings: &GlobalSettings, 131 | connection: &mut Connection, 132 | focused: bool, 133 | ) -> Fallible<()> { 134 | let color = if focused && global_settings.focused_color.is_some() { 135 | global_settings.focused_color.as_ref().unwrap() 136 | } else { 137 | settings.color.as_ref().unwrap_or(&global_settings.color) 138 | }; 139 | let icon = settings.icon.as_ref().unwrap_or(&global_settings.icon); 140 | let size = settings.size.as_ref().unwrap_or(&global_settings.size); 141 | let separator = &global_settings.separator; 142 | 143 | connection.run_command(format!( 144 | "[con_id={}] title_format \"{}{}%title\"", 145 | id, color, size, icon, separator 146 | ))?; 147 | Ok(()) 148 | } 149 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "arrayvec" 16 | version = "0.5.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" 19 | 20 | [[package]] 21 | name = "autocfg" 22 | version = "1.1.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 25 | 26 | [[package]] 27 | name = "bitflags" 28 | version = "1.3.2" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 31 | 32 | [[package]] 33 | name = "cfg-if" 34 | version = "1.0.0" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 37 | 38 | [[package]] 39 | name = "config" 40 | version = "0.11.0" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "1b1b9d958c2b1368a663f05538fc1b5975adce1e19f435acceae987aceeeb369" 43 | dependencies = [ 44 | "lazy_static", 45 | "nom", 46 | "rust-ini", 47 | "serde 1.0.193", 48 | "serde-hjson", 49 | "serde_json", 50 | "toml", 51 | "yaml-rust", 52 | ] 53 | 54 | [[package]] 55 | name = "itoa" 56 | version = "1.0.9" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" 59 | 60 | [[package]] 61 | name = "lazy_static" 62 | version = "1.4.0" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 65 | 66 | [[package]] 67 | name = "lexical-core" 68 | version = "0.7.6" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" 71 | dependencies = [ 72 | "arrayvec", 73 | "bitflags", 74 | "cfg-if", 75 | "ryu", 76 | "static_assertions", 77 | ] 78 | 79 | [[package]] 80 | name = "linked-hash-map" 81 | version = "0.5.6" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" 84 | 85 | [[package]] 86 | name = "memchr" 87 | version = "2.6.4" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" 90 | 91 | [[package]] 92 | name = "nom" 93 | version = "5.1.3" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "08959a387a676302eebf4ddbcbc611da04285579f76f88ee0506c63b1a61dd4b" 96 | dependencies = [ 97 | "lexical-core", 98 | "memchr", 99 | "version_check", 100 | ] 101 | 102 | [[package]] 103 | name = "num-traits" 104 | version = "0.1.43" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" 107 | dependencies = [ 108 | "num-traits 0.2.17", 109 | ] 110 | 111 | [[package]] 112 | name = "num-traits" 113 | version = "0.2.17" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" 116 | dependencies = [ 117 | "autocfg", 118 | ] 119 | 120 | [[package]] 121 | name = "proc-macro2" 122 | version = "1.0.70" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" 125 | dependencies = [ 126 | "unicode-ident", 127 | ] 128 | 129 | [[package]] 130 | name = "quote" 131 | version = "1.0.33" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" 134 | dependencies = [ 135 | "proc-macro2", 136 | ] 137 | 138 | [[package]] 139 | name = "regex" 140 | version = "1.10.2" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" 143 | dependencies = [ 144 | "aho-corasick", 145 | "memchr", 146 | "regex-automata", 147 | "regex-syntax", 148 | ] 149 | 150 | [[package]] 151 | name = "regex-automata" 152 | version = "0.4.3" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" 155 | dependencies = [ 156 | "aho-corasick", 157 | "memchr", 158 | "regex-syntax", 159 | ] 160 | 161 | [[package]] 162 | name = "regex-syntax" 163 | version = "0.8.2" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" 166 | 167 | [[package]] 168 | name = "rust-ini" 169 | version = "0.13.0" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" 172 | 173 | [[package]] 174 | name = "ryu" 175 | version = "1.0.15" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" 178 | 179 | [[package]] 180 | name = "serde" 181 | version = "0.8.23" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" 184 | 185 | [[package]] 186 | name = "serde" 187 | version = "1.0.193" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" 190 | dependencies = [ 191 | "serde_derive", 192 | ] 193 | 194 | [[package]] 195 | name = "serde-hjson" 196 | version = "0.9.1" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "6a3a4e0ea8a88553209f6cc6cfe8724ecad22e1acf372793c27d995290fe74f8" 199 | dependencies = [ 200 | "lazy_static", 201 | "num-traits 0.1.43", 202 | "regex", 203 | "serde 0.8.23", 204 | ] 205 | 206 | [[package]] 207 | name = "serde_derive" 208 | version = "1.0.193" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" 211 | dependencies = [ 212 | "proc-macro2", 213 | "quote", 214 | "syn", 215 | ] 216 | 217 | [[package]] 218 | name = "serde_json" 219 | version = "1.0.108" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" 222 | dependencies = [ 223 | "itoa", 224 | "ryu", 225 | "serde 1.0.193", 226 | ] 227 | 228 | [[package]] 229 | name = "static_assertions" 230 | version = "1.1.0" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 233 | 234 | [[package]] 235 | name = "swaycons" 236 | version = "0.3.1" 237 | dependencies = [ 238 | "config", 239 | "regex", 240 | "swayipc", 241 | "xdg", 242 | ] 243 | 244 | [[package]] 245 | name = "swayipc" 246 | version = "3.0.2" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "daa5d19f881f372e225095e297072e2e3ee1c4e9e3a46cafe5f5cf70f1313f29" 249 | dependencies = [ 250 | "serde 1.0.193", 251 | "serde_json", 252 | "swayipc-types", 253 | ] 254 | 255 | [[package]] 256 | name = "swayipc-types" 257 | version = "1.3.1" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "b1b62656428f36fd561f13aff70386b891fc6f67db24ecccf72a49655d6c9fbb" 260 | dependencies = [ 261 | "serde 1.0.193", 262 | "serde_json", 263 | "thiserror", 264 | ] 265 | 266 | [[package]] 267 | name = "syn" 268 | version = "2.0.39" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" 271 | dependencies = [ 272 | "proc-macro2", 273 | "quote", 274 | "unicode-ident", 275 | ] 276 | 277 | [[package]] 278 | name = "thiserror" 279 | version = "1.0.50" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" 282 | dependencies = [ 283 | "thiserror-impl", 284 | ] 285 | 286 | [[package]] 287 | name = "thiserror-impl" 288 | version = "1.0.50" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" 291 | dependencies = [ 292 | "proc-macro2", 293 | "quote", 294 | "syn", 295 | ] 296 | 297 | [[package]] 298 | name = "toml" 299 | version = "0.5.11" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" 302 | dependencies = [ 303 | "serde 1.0.193", 304 | ] 305 | 306 | [[package]] 307 | name = "unicode-ident" 308 | version = "1.0.12" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 311 | 312 | [[package]] 313 | name = "version_check" 314 | version = "0.9.4" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 317 | 318 | [[package]] 319 | name = "xdg" 320 | version = "2.5.2" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" 323 | 324 | [[package]] 325 | name = "yaml-rust" 326 | version = "0.4.5" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" 329 | dependencies = [ 330 | "linked-hash-map", 331 | ] 332 | -------------------------------------------------------------------------------- /LICENSE-APACHE-2.0: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2023 Allie Stephan 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | --------------------------------------------------------------------------------