├── .gitignore ├── eww └── hyprmsn │ ├── widgets │ ├── eww.scss │ ├── tab.scss │ ├── eww.yuck │ └── tab.yuck │ ├── scripts │ ├── move_to_window.sh │ └── get_windows.sh │ ├── eww.scss │ └── eww.yuck ├── config.ini ├── windows └── default.png ├── scripts ├── eww.sh ├── load_config.sh ├── clients.sh └── daemon.sh ├── install.sh ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | windows/*.jpeg 2 | -------------------------------------------------------------------------------- /eww/hyprmsn/widgets/eww.scss: -------------------------------------------------------------------------------- 1 | @import "./tab.scss"; -------------------------------------------------------------------------------- /config.ini: -------------------------------------------------------------------------------- 1 | [daemon] 2 | use_icons=false 3 | icon_pack=Papirus -------------------------------------------------------------------------------- /windows/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShakedGold/hyprmsn/HEAD/windows/default.png -------------------------------------------------------------------------------- /scripts/eww.sh: -------------------------------------------------------------------------------- 1 | eww --config $HOME/.config/eww/hyprmsn/widgets open hyprmsn --arg monitor=$(hyprctl -j monitors | jq -r '.[] | select(."focused"==true).id') 2 | -------------------------------------------------------------------------------- /eww/hyprmsn/scripts/move_to_window.sh: -------------------------------------------------------------------------------- 1 | window_id=$(hyprctl clients | rg 'Window ([A-Fa-f0-9]+)' | grep -F "$*" | sed -E 's/Window (.*) \->.*/\1/g') 2 | hyprctl dispatch focuswindow address:0x$window_id > /dev/null 3 | eww --config $HOME/.config/eww/hyprmsn/widgets close hyprmsn -------------------------------------------------------------------------------- /eww/hyprmsn/eww.scss: -------------------------------------------------------------------------------- 1 | *{ 2 | all: unset; //Unsets everything so you can style everything from scratch 3 | } 4 | .window-button { 5 | background-color: rgb(44, 44, 44); 6 | } 7 | 8 | .window-button:hover { 9 | background-color: rgb(27, 27, 27); 10 | } 11 | 12 | .window-button:active { 13 | background-color: rgb(17, 17, 17); 14 | } 15 | 16 | .window-box { 17 | border: rgb(61, 61, 61) solid 3px; 18 | } -------------------------------------------------------------------------------- /scripts/load_config.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | config=$HOME/.config/hyprmsn/config.ini 4 | 5 | section() { 6 | section_name=$1 7 | 8 | sed -n "/\[$section_name\]/,/^$/p" $config | sed -E "s/\[.*\]//g" | sed '/^[[:space:]]*$/d' 9 | } 10 | 11 | # daemon section 12 | section daemon > /dev/null 13 | 14 | # SOURCING 15 | # support comments 16 | sed "s/;/#/g" $config | grep -Ev "\[.*\]" | source /dev/stdin 17 | 18 | # remove spaces 19 | source <(grep = $config | sed 's/ *= */=/g') -------------------------------------------------------------------------------- /eww/hyprmsn/eww.yuck: -------------------------------------------------------------------------------- 1 | (defpoll windows :interval "1s" 2 | :initial "initial-value" ; optional, defaults to poll at startup 3 | :run-while time-visible ; optional, defaults to 'true' 4 | `scripts/windows.sh`) 5 | 6 | (defwidget windows_widget [] 7 | (box 8 | (literal :content windows) 9 | ) 10 | ) 11 | 12 | (defwindow hyprmsn 13 | :monitor 1 14 | :geometry (geometry 15 | :x "0%" 16 | :y "0%" 17 | :width "50%" 18 | :height "30%" 19 | :anchor "center center") 20 | :stacking "fg" 21 | :windowtype "dock" 22 | (windows_widget) 23 | ) -------------------------------------------------------------------------------- /eww/hyprmsn/widgets/tab.scss: -------------------------------------------------------------------------------- 1 | * { 2 | all: unset; //Unsets everything so you can style everything from scratch 3 | } 4 | 5 | .tab { 6 | padding: 10px; 7 | border: transparent solid 2px; 8 | background: rgba(0, 0, 0, 1); 9 | border-radius: 5px; 10 | } 11 | 12 | .tab:hover { 13 | border: white solid 2px; 14 | } 15 | 16 | .tab-close { 17 | background-color: rgba($color: #fff, $alpha: 0.2); 18 | padding-left: 10px; 19 | padding-right: 10px; 20 | border-radius: 5px; 21 | } 22 | 23 | .tab-close:hover { 24 | background-color: red; 25 | color: black; 26 | } -------------------------------------------------------------------------------- /eww/hyprmsn/widgets/eww.yuck: -------------------------------------------------------------------------------- 1 | (include "./tab.yuck") 2 | 3 | (defpoll windows-file :initial "[\"hi\"]" :interval "1s" 4 | `../scripts/get_windows.sh`) 5 | 6 | (defwindow hyprmsn [monitor] 7 | :monitor monitor 8 | :geometry (geometry 9 | :x "0%" 10 | :y "0%" 11 | :width "50%" 12 | :width "30%" 13 | :anchor "center center" 14 | ) 15 | :stacking "fg" 16 | :windowtype "dock" 17 | :class "container" 18 | (box :spacing 10 19 | (for window in windows-file 20 | (tab :title {window.name} :img {window.image} :icon {window.icon} :onclick {window.onclick}) 21 | ) 22 | ) 23 | ) -------------------------------------------------------------------------------- /eww/hyprmsn/widgets/tab.yuck: -------------------------------------------------------------------------------- 1 | (defwidget tab [title img ?icon onclick] 2 | (button :class "tab" :onclick onclick 3 | (box :class "tab-box" :orientation "v" :spacing -70 4 | (box :class "tab-labels" :orientation "h" :space-evenly false :valign "start" 5 | (box :class "tab-label-container" :space-evenly false :spacing 5 6 | (image :class "tab-label-icon" :path icon) 7 | (label :class "tab-label-title" :text title :unindent true :limit-width 20) 8 | ) 9 | ) 10 | (image :class "tab-image" :path img :image-width 192 :image-height 108) 11 | ) 12 | ) 13 | ) -------------------------------------------------------------------------------- /scripts/clients.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | hyprmsn=$HOME/.config/hyprmsn 3 | 4 | OLDIFS="$IFS" 5 | IFS=$'\n' # bash specific 6 | chosen=$(for line in $(hyprctl clients | grep "title: " | sed 's/title: //g' | tr -d '\t') 7 | do 8 | special_line=$line 9 | special_line=$(echo $special_line | sed 's/\[\([^]]*\)\]/\\[\1\\]/g') #add \ to [] 10 | special_line=$(echo $special_line | sed 's/'\''\.\*\\^//g') #add \ to special characters 11 | icon=$(ls "$hyprmsn/windows" | grep -F $special_line) 12 | echo -en "$line\x00icon\x1f$hyprmsn/windows/$icon\n" 13 | done | rofi -dmenu ) 14 | IFS="$OLDIFS" 15 | 16 | window_id=$(hyprctl clients | rg 'Window ([A-Fa-f0-9]+)' | grep -F "$chosen" | sed -E 's/Window (.*)->.*/\1/g') 17 | hyprctl dispatch focuswindow address:0x$window_id > /dev/null 18 | -------------------------------------------------------------------------------- /scripts/daemon.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | hyprmsn=$HOME/.config/hyprmsn 4 | mkdir $hyprmsn/windows 2> /dev/null 5 | window=$(hyprctl activewindow -j | jq --raw-output .title) 6 | rm -rf $hyprmsn/windows/*.jpeg 2> /dev/null 7 | 8 | while true; do 9 | new_window=$(hyprctl activewindow -j | jq --raw-output .title) 10 | 11 | if [[ "$window" == "$new_window" ]]; then 12 | continue 13 | fi 14 | window=$new_window 15 | 16 | window_str=$(echo $window | sed -e 's/[]\/$*.^[]/\\&/g') #| sed 's/\[\([^]]*\)\]/\\[\1\\]/g') #add \ to [] 17 | window_no_slash=$(echo $window | sed 's|/|⁄|g') 18 | 19 | at_size=$(hyprctl clients | sed -n "/$window_str/,/^\$/p" | grep -Ew 'at|size' | tr -d '[a-z]|:|\t| ') 20 | at=$(echo $at_size | tr ' ' '\n' | head -1 | tail -1) 21 | size=$(echo $at_size | tr ' ' '\n' | head -2 | tail -1 | tr ',' 'x') 22 | sleep 0.4 23 | grim -g "$at $size" -t jpeg -q 50 "$hyprmsn/windows/$window_no_slash.jpeg" 24 | done 25 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # prerequisites 4 | if ! command -v eww &> /dev/null 5 | then 6 | echo "eww could not be found" 7 | echo "install eww-wayland to continue" 8 | exit 9 | fi 10 | 11 | if ! command -v grim &> /dev/null 12 | then 13 | echo "grim could not be found" 14 | echo "install grim to continue" 15 | exit 16 | fi 17 | 18 | if ! command -v jq &> /dev/null 19 | then 20 | echo "jq could not be found" 21 | echo "install jq to continue" 22 | exit 23 | fi 24 | 25 | if ! command -v rg &> /dev/null 26 | then 27 | echo "rg could not be found" 28 | echo "install rg to continue" 29 | exit 30 | fi 31 | 32 | 33 | mkdir ~/.config/eww 2> /dev/null 34 | echo "created eww dir if it didn't existed" 35 | cp -r eww/* ~/.config/eww 36 | echo "copied the hyprmsn folder to the eww config" 37 | mkdir ~/.config/hyprmsn 2> /dev/null 38 | echo "created the hyprmsn dir" 39 | cp -r scripts windows ~/.config/hyprmsn/ 40 | echo "copied the scripts, windows to the hyprmsn dir" 41 | cp config.ini ~/.config/hyprmsn/ 42 | echo "copied the config.ini to the hyprmsn dir" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Shaked Gold 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 | -------------------------------------------------------------------------------- /eww/hyprmsn/scripts/get_windows.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | trun() { 3 | window=$1 4 | OLD_IFS="$IFS" 5 | IFS=$' ' 6 | for word in $window; do 7 | if [[ $count == $word_per_line ]]; then 8 | str="$str$word\n" 9 | count=0 10 | line_count=$(($line_count + 1)) 11 | else 12 | str="$str$word " 13 | count=$(($count + 1)) 14 | fi 15 | done 16 | IFS=$OLD_IFS 17 | echo $str 18 | } 19 | 20 | img() { 21 | window=$1 22 | windows_images_dir=$HOME/.config/hyprmsn/windows 23 | window_no_slash=$(echo $window | sed 's|/|⁄|g') 24 | window_img="$windows_images_dir/$(ls $windows_images_dir | grep -F $window_no_slash | tail -1)" 25 | if [[ $window_img == "$windows_images_dir/" ]]; then 26 | window_img="$windows_images_dir/default.png" 27 | fi 28 | echo "$window_img" 29 | } 30 | 31 | word_per_line=3 32 | total_lines=2 33 | OLD_IFS="$IFS" 34 | IFS=$'\n' 35 | windows=$(hyprctl clients | rg 'Window ([A-Fa-f0-9]+)' | sed -E 's/.*\-> (.*):/\1/g') 36 | classes=$(hyprctl clients | rg 'class: ' | sed "s/.*class: //g") 37 | initial_names=$(hyprctl clients | rg 'initialTitle: ' | cut -d ' ' -f2-) 38 | 39 | source $HOME/.config/hyprmsn/scripts/load_config.sh 40 | 41 | result="[" 42 | 43 | i=1 44 | for window in $windows; do 45 | window_class=$(echo $initial_names | tr ' ' '\n' | head -$i | tail -1) 46 | window_img=$(img $window) 47 | 48 | if [ $use_icons == true ]; then 49 | window_icon=$(find /usr/share/icons/$icon_pack | grep -E -i "$window_class\." | head -1 | tail -1) 50 | fi 51 | 52 | object="{\"name\": \"$window\", \"icon\": \"$window_icon\", \"image\": \"$window_img\", \"onclick\": \"../scripts/move_to_window.sh '$window'\"}" 53 | 54 | result=$(echo "$result$object,") 55 | 56 | i=$(( $i + 1 )) 57 | done 58 | IFS=$OLD_IFS 59 | 60 | result=$(echo "$result" | sed 's/,$/]/g') 61 | 62 | echo $result 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hyprmsn 2 | A "Mission Control" like in macos for Hyprland 3 | 4 | # Explanation 5 | ### ```daemon.sh``` 6 | this file is supposed to be running at all times and its purpose is to screenshot the focused window everytime you switch to a different window 7 | [Video Example](https://imgur.com/a/ExP51Ms) 8 | 9 | ### ```clients.sh``` 10 | This is a temporary file to use rofi for the UI but my plan is to switch this to eww to be able to display the images and control the appearance. 11 | the file works by calling ```hyprctl clients``` and grabbing the title for each one, after that it will loop through and find the corresponding screenshot to display, then it will display the rofi menu and wait for a selection to be made, after the chosen window is selected, I get the window ID/Adress and use that with: 12 | ``` 13 | hyprctl dispatch focuswindow address:0x$window_id > /dev/null 14 | ``` 15 | to switch to the window 16 | 17 | # Todo 18 | - [x] Integrate eww to dynamically create widgets 19 | - [X] make eww pretty 20 | - [ ] remove unsused screenshots to save space 21 | 22 | # Dependencies 23 | * Hyprland 24 | * grim 25 | * eww 26 | * jq 27 | * rg 28 | 29 | # New Features 30 | * you can now choose to use icons instead on the daemon as images! (so now you dont have to run the daemon at startup) 31 | just go into config.ini and set use_icons to true, it works best with the Papirus icon theme 32 | 33 | # Getting Started 34 | To test the program you will need to do the following: 35 | 1. `git clone https://github.com/ShakedGold/hyprmsn` 36 | 2. make sure `installer.sh` is executable (`chmod +x installer.sh`) 37 | 3. run `installer.sh` 38 | 4. run daemon.sh in `~/.config/hyrpmsn/scripts` 39 | 5. run `eww --config $HOME/.config/eww/hyprmsn/widgets open hyprmsn --arg monitor=0` 40 | 6. (Optional) I also set the monitor with this command: `eww --config $HOME/.config/eww/hyprmsn/widgets open hyprmsn --arg monitor=$(hyprctl -j monitors | jq -r '.[] | select(."focused"==true).id')` 41 | --------------------------------------------------------------------------------