Trois posts ça fait flood :mad:
Je veux bien poster mon script ici, mais ça risque d'être difficilement lisible.
Il faut 140 colonnes (caractères) de large pour bien avoir la mise en forme, sinon les retours à la ligne vont gêner la lecture.
De plus, il fait pas loin de 300 lignes tout compris (lignes vides et commentaires ...)
Vous me dites et j'édite ce post.
Autre chose encore, on a parlé de Christophe C.
Déjà je lui passe mon bonjour et je lui dois une fière chandelle car c'est grâce à son post sur le même sujet que j'ai découvert 'yad'.
Autant dire que j'ai grave appris au travers de ses exemples fonctionnels avec yad.
De plus, je me suis inspiré de son script pour la liste des bureaux (j'ai quand même fait différemment mais j'utilise aussi wmctrl).
Donc MERCI et BRAVO pour ton boulot Christophe C.
Ensuite, mon script comporte certainement d'énormes "bashismes" et donc je ne suis pas sûr de sa portabilité (sujet que j'ai pas trop creusé encore). Et il y a sûrement d'autres trucs à simplifier et aussi ... et ... et peut être ... :lol:
Vous me dites comment il serait mieux que je procède ...
Le voilà :
#!/bin/bash
###############################################################################
#
# Nom : DevilsPie-IGEL - Devil's Pie Interface Graphique Extra Légère
#
# Commande : DevilsPie-IGEL
#
# Arguments : Aucuns
# Options : Aucunes
#
# Nécéssite : devilspie wmctrl xdotool yad zenity
# par Tawal.
###############################################################################
RETOUR=0 # Code de retour si tout se passe bien
ERR_SORTIE_YAD=64 # Sortie utilisateur 1ere boite (attente_appli)
ERR_ABANDON_YAD=65 # Sortie utilisateur 2eme boite (yad_inf_fen)
ERR_PARSING_CONTINUE=70 # Retour zenity erreur parsing (ou split fichier) et utilisateur continue
ERR_PARSING_STOP=71 # Retour zenity erreur parsing (ou split fichier) des fichiers de règles et abandon utilisateur
ERR_CASE_ZENITY=81 # Code retour si case "reponse zenity erreur parsing" a échoue - Très peu probable mais bon
ERR_CASE_YAD=82 # Idem qu'au dessus mais pour yad_inf_fen - Très peu probable aussi
ERR_INCONNUE_FIN_SCRIPT=113 # Exit à la fin du script où il ne devrait jamais passer !
# Création du dossier temporaire Devil et des fichiers temporaires devil_tmp et yad_datas
Devil=$(mktemp -d /tmp/Devil-XXXXXXXXXXXXXXXX) #+ (du moins les chemins sont contenus dans ces variables)
devil_tmp=$(mktemp -p $Devil devil_tmp-XXX) # Gestion de l'effacement du dossier et des fichiers temporaires
yad_datas=$(mktemp -p $Devil yad_datas-XXX) #+ par le 'trap rm' ci-après : un peu trop de signaux trapés ?
trap "rm -rf $Devil $devil_tmp $yad_datas" TERM INT ABRT HUP QUIT EXIT # TERM=15 INT=Ctrl+C ABRT=Abort HUP=Par.Proc.Mort QUIT=Crtl+D
DOSS="${HOME}/.devilspie/ds-FE" # Création du dossier récepteur (si inexistant) des fichiers finaux.
[ ! -d "$DOSS" ] && mkdir "$DOSS" #+ On va pas mettre en prod direct non plus !
#+ Non, c'est pour éviter d'écraser des fichiers existants
ORIGIN=$(pwd) # Sauvegarde de l'endroit d'où on vient, on va se ballader un peu
sep='|' # Definition du separateur pour 'yad' et pour les champs de 'awk' et ...
export icon=/usr/share/icons/Tango/32x32/apps/xfce4-session.png
######## Trois Fonctions à exporter pour éviter un script extérieur - Appelées par les boutons 'yad' et par la fonction 'xdoyad'
howtodo() {
zenity --info --width 450 --window-icon $icon --title "Devil's Pie - IGEL" \
--text "\t\t\t\t <u><b>HOW TO DO</b></u>\n\n\t- Ouvrez l'application à gérer et cliquez sur <b>OK</b>.\n\n\t- Redimensionnez et positionnez la fenêtre.\n\n\t- Puis cliquez sur <b>Attraper les données de la fenêtre</b>.\n\n\t- Sélectionnez le Bureau, puis\n\n\t- <u>Ouvrez une nouvelle fenêtre</u> à gérer.\n\n\t- Et cliquez sur <b>Nouvelle Fenêtre OK</b>.\n\n\t- Recommencez l'opération etc ...\n\n\t- Puis cliquez sur <b>Créer Règles</b> quand vous avez fini.\n\n\n<i>Le bouton <b>Attraper les données de la fenêtre</b> ne sauvegarde pas la règle qui s'affiche, celle-ci l'est lorsque vous passez à la fenêtre suivante ou que vous créez les règles.\n\nParcourrez une à une <u>toutes</u> les fenêtres de l'application en suivant la procédure ci-dessus.\n\nToutes les données sont modifiables !\nIl est conseillé de ne changer ni le nom ni la classe d'application.\nSurveillez bien les titres de fenêtres, car ils sont très restrictifs !\n\nChoisisez '<u>is</u>' pour un titre de fenêtre inchangeant.\nOptez pour '<u>contains</u>' pour un titre modifié de fenêtre.\n\nVous pouvez garder cette fenêtre ouverte pendant les opérations.</i>"
}
xdoyad() { # Capture des données fenêtre après redimensionnement et retour à 'yad' des données par les 'echo'
local geom="" pos="" title="" totwinstack chope
totwinstack=$(xdotool search --onlyvisible --classname "$1" getwindowname %@ 2>/dev/null | wc -l)
chope=$(xdotool search --onlyvisible --classname "$1" getwindowgeometry %$totwinstack getwindowname %$totwinstack 2>/dev/null)
posxdo=( $(echo $chope | awk '{print $4}' | sed 's/,/ /g') )
X=$((${posxdo[0]}-10)); Y=$((${posxdo[1]}-38)) # Rattrapage de la différence entre Devilspie et Xdotool (approx. quoique)
pos="+"$X"+"$Y
geom=$(echo $chope | awk '{print $8}')
title=$(echo "$chope" | awk 'BEGIN {$1=""; $2=""; $3=""; $4=""; $5=""; $6=""; $7=""; $8=""}; END {print $N}' | sed 's/^ *//g')
[ -z "$title" ] && zenity_pas_de_fen && xdoyad $1 # Appel de la fonction zenity_pas_de_fen + récursivité de 'xdoyad'
echo "3:$title"
echo "4:${geom}${pos}"
}
zenity_pas_de_fen() { # Gestion par la fonction 'xdoyad' en cas si la fenêtre n'est pas trouvée. Appel zenity_pas_de_fen
zenity --info --width 350 --window-icon $icon --title "Devil's Pie - IGEL" \
--title "ERREUR ! Fenêtre Non Trouvée !" \
--text "Réouvrez la fenêtre, cliquez sur <b>Valider</b>\n\npuis sur <b>Attraper les données de la fenêtre</b>"
}
export -f howtodo zenity_pas_de_fen xdoyad # Export des trois fonctions (elles agissent dans un|des sous-shell|s)
######### Fin Export #################################################################################
############# PARTIE FONCTIONS #####################################################################
sortie() {
activ_regles
killall devilspie
cd $ORIGIN
exit $1
}
err_parsing() { # Gestion de l'erreur dans le parsing ...
mess=""; mess2=""; mess3="" #+ N'arrivera pas à mon avis, j'ai du créer les erreurs à la main
[ "$3" -gt "1" ] && { #+ sur les fichiers temporaires.
mess="<u>sans importance</u>"
mess2="dans une sous-règle."
mess3="Elle ne 'matchera' pas, mais laissera le reste fonctionner."
} || {
mess="<u><b>GRAVE</b></u>"
mess2="dans l'entête de fichier !"
mess3="Ce fichier de règles ne fonctionnera pas.\nMais il peut être récupéré manuellement, il sera créé en continuant."
}
zenity --question --title "Devil's Pie - IGEL" \
--window-icon $icon --width 450 \
--ok-label "OUI" --cancel-label "NON" \
--text "\nUne erreur $mess est survenue lors de la création d'un fichier .ds\nLe fichier <b>$nom_fich_reel.ds</b> comporte le nom <u>$nom_fich_lu</u> en classe d'application $mess2\n\n$mess3\n\nVoulez-vous tout de même créer le fichier\Cliquer sur 'NON' et aucun fichier ne sera créé !"
res=$?
case $res in
0 ) RETOUR=$ERR_PARSING_CONTINUE ;;
1 ) sortie $ERR_PARSING_STOP ;;
* ) RETOUR=$ERR_CASE_ZENITY ;;
esac
}
sauve_ds() { # On transfère les fichiers finaux du dossier temporaire au dossier DOSS
local fich n=0 tab
for fich in *.ds; do # Création d'un tableau ( fich1 fich2... DossierDest )
tab[$n]=$fich
n=$((n+1 ))
done
tab[$n]=$DOSS
cp -f ${tab[@]} & # Copie des fichiers définitifs des règles (1 fichier par classe d'application)
for n in `seq 1 100`; do #+ en forcing et à la volée !!!
echo $n # DE LA POUDRE AUX YEUX
sleep 0.005 # ce zenity là !
done | zenity --progress --window-icon $icon --auto-close --no-cancel --title "Devil's Pie - IGEL" --text "\nCopie des fichiers ..."
cd $OLDPWD # Je l'avais dit : de la balade suivez le chemin ^^
}
parse_fich() { # On crée le fichier final de règles. On extrait les données dim + loi ligne par ligne.
local num_lign app title geom class gui='"' # + Gestion structure fichier final
for num_lign in `seq 1 $(wc -l $1 | awk '{print $1}')`; do
nom_fich_reel=$1
nom_fich_reel=${nom_fich_reel%*-} # On ôte le tret '-' à nom_fich_reel pour bien matcher avec la class d'appli.
nom_fich_lu=$(sed -n "$num_lign p" $1 | awk -v var="$sep" 'BEGIN {FS=var}; END {print $1}' | sed 's/ *//g')
[ ! "$nom_fich_lu" = "$nom_fich_reel" ] && err_parsing $nom_fich_lu $nom_fich_reel $num_lign # Teste si le nom du fichier
class='"'$(sed -n "$num_lign p" $1 | awk -v var="$sep" 'BEGIN {FS=var}; END {print $1}')'"' #+ lu dans la ligne
app='"'$(sed -n "$num_lign p" $1 | awk -v var="$sep" 'BEGIN {FS=var}; END {print $2}')'"' #+ correspond à celui du fichier réel
title='"'$(sed -n "$num_lign p" $1 | awk -v var="$sep" 'BEGIN {FS=var}; END {print $3}')'"' #+ Appel de la fonction err_parsing
geom='"'$(sed -n "$num_lign p" $1 | awk -v var="$sep" 'BEGIN {FS=var}; END {print $4}')'"'
bur=$(sed -n "$num_lign p" $1 | awk -v var="$sep" 'BEGIN {FS=var}; END {print $5}')
loi=$(sed -n "$num_lign p" $1 | awk -v var="$sep" 'BEGIN {FS=var}; END {print $6}')
[ "$num_lign" = "1" ] && { # C'est ici qu'on écrit vraiment les règles, 1 tour boucle = 1 ligne lue et éclatée = une règle
echo -e "(if (and (is (window_class) $class) (is (application_name) $app))" > "$Devil/$nom_fich_reel.ds"
echo -e " (begin\n" >> "$Devil/$nom_fich_reel.ds" #
[ ! "$bur" = "$defaut" ] && { echo -e " (set_workspace $bur)\n" >> "$Devil/$nom_fich_reel.ds"; set_bur=1; }
echo -e " (if ($loi (window_name) $title)" >> "$Devil/$nom_fich_reel.ds" #+ Entète et
echo -e " (geometry $geom))\n" >> "$Devil/$nom_fich_reel.ds" #+ 1ère règle
}
[ "$num_lign" -gt "1" ] && { #
echo -e " (if ($loi (window_name) $title)" >> "$Devil/$nom_fich_reel.ds" # Sous-règles
[ ! "$bur" = "$defaut" ] && echo -e " (set_workspace $bur)" >> "$Devil/$nom_fich_reel.ds" #+
echo -e " (geometry $geom))\n" >> "$Devil/$nom_fich_reel.ds" #+ suivantes #
}
done
echo " )" >> "$Devil/$nom_fich_reel.ds"
echo ")" >> "$Devil/$nom_fich_reel.ds"
[ "$num_lign" = "1" ] && { # Si qu'une seule règle pour l'application alors on efface les lignes génantes
[ "$set_bur" = "1" ] && sed -i '2d; 9d' "$Devil/$nom_fich_reel.ds" || sed -i '2d; 7d' "$Devil/$nom_fich_reel.ds"
} #+ et la ')'
}
creer_ds() { # On envoie chaque fichier 'classe d'app' contenant les données de dim + règles
local i # + à la création du fichier .ds final correspondant.
cd $Devil # On va travailler dans le dossier temporaire
for i in *-; do
parse_fich $i
done
}
fich_split() { # On éclate le fichier de récupération des données utilisateur, on concatène les données règles par application/fichier
local i line nom_fich #+ le 1er champ de chaque ligne est la classe d'application
for i in `seq 1 $(wc -l $yad_datas | awk '{print $1}')`; do #+ qui sera le nom du fichier .ds final
line=$(sed -n "$i p" $yad_datas)
nom_fich=$(echo $line | awk -v var="$sep" 'BEGIN {FS=var}; END {print $1}' | sed 's/ *//g')
echo $line >> "$Devil/$nom_fich-"
done
}
creer_fichs_regles() { # Alors là, je me demande bien ce que c'est :D
fich_split
creer_ds
sauve_ds
}
enleve_o() { # on enlève l'appendice '-o' pour réactiver les règles. Voir plus bas
local a=$1
a=${a%*-o}
mv $1 $a
}
met_o() { # On met un appendice '-o' à l'extension du fichier règle pour le désactiver
local a=$1 #+ (plus de reconnaissance de l'extension .ds par devilspie)
a="$a-o"
mv $1 $a
}
activ_regles() { # On enlève l'appendice '-o' aux fichiers ~/.devilspie/*.ds
cd ${HOME}/.devilspie # Il faut le 'cd' en cas d'abandon aprés erreur parsing
fich_ds_o=( $(ls *.ds-o) )
for i in `seq 0 $(( ${#fich_ds_o[@]}-1 ))`; do
enleve_o ${fich_ds_o[$i]}
done
met_o debug.ds # Et on met le '-o' à debug.ds qui devient debug.ds-o
} #+ debug.ds désactivé du coup ;)
desactiv_regles() { # On met un appendice '-o' aux fichiers ~/.devilspie/*.ds
[ ! -e "debug.ds" ] && [ ! -e "debug.ds-o" ] && echo "(debug)" > debug.ds #+ sauf à debug.ds et si debug.ds l'a dejà, on l'enlève
fich_ds=( $(ls *.ds | grep -v "debug.ds") ) #+ bref l'inverse d'au dessus.
debug=( $(ls debug.*) )
for i in `seq 0 $(( ${#fich_ds[@]}-1 ))`; do
met_o ${fich_ds[$i]}
done
[ "$debug" = "debug.ds-o" ] && enleve_o $debug
}
inf_chop() { # On chope les informations des fenêtres que 'devilspie -d' nous laisse dans devil_tmp (on ne lit que la dernière ligne)
line=$(tail -n 1 $devil_tmp) #+ et on éclate les données
win_class=$(echo $line | awk 'BEGIN {FS=";"}; END {print $3}' | awk 'BEGIN {FS=":"}; END {print $2}' | sed "s/^ '//g; s/'$//g")
win_name=$(echo $line | awk 'BEGIN {FS=";"}; END {print $2}' | awk 'BEGIN {FS=":"}; END {print $2}' | sed "s/^ '//g; s/'$//g")
win_title=$(echo $line | awk 'BEGIN {FS=";"}; END {print $1}' | awk 'BEGIN {FS=":"}; END {print $2}' | sed "s/^ '//g; s/'$//g")
win_geom=$(echo $line | awk 'BEGIN {FS=";"}; END {print $4}' | awk 'BEGIN {FS=":"}; END {print $2}' | sed "s/^ //g")
}
yad_attente_appli() { # Pour attendre que l'application à gérer soit ouverte + accès HOW TO DO (1ére fenêtre du script à apparaître)
yad --form --width 350 --window-icon $icon --title="Devil's Pie - IGEL" \
--align=center \
--buttons-layout=center \
--field=" ":LBL "" \
--field="<b>Attente de l'application à gérer ...</b>\n\nDémarrez l'application à configurer.\nEt cliquez sur <b>OK</b>":LBL "" \
--field=" ":LBL "" \
--button="How To Do":'bash -c "howtodo"' \
--button="OK":0 \
--button="Sortie":1
[ "$?" -eq 0 ] || sortie $ERR_SORTIE_YAD # Cette boite a une commande sous le bouton How To Do,
} #+ appel de 'howtodo' cf. Export Fonction
yad_inf_fen() { # La boite de récupération des données fenêtres à gérer - Interactive grâce au --field=" ":FBTN commande
inf_chop # On va chercher les infos avant d'afficher
yad --form --window-icon $icon --title="Devil's Pie - IGEL" \
--item-separator="$sep" \
--buttons-layout=edge \
--align=right \
--field="<b>CLASSE</b>" "$win_class" \
--field="<b>NOM</b>" "$win_name" \
--field="<b>TITRE DE FENÊTRE</b>" "$win_title" \
--field="<b>GÉMOTRIE</b>" "$win_geom" \
--field="<b>BUREAU</b>":CB "$list_bur" \
--field="<b>Consistance Règle</b>":CB "is$sep^contains" \
--field=" ":LBL "" \
--field="*-*-*-* <b>Attraper les données de la fenêtre</b> *-*-*-*":FBTN '@bash -c "xdoyad %1"' \
--button="<b>Nouvelle Fenêtre OK</b>":0 \
--button="<b>Créer Règles</b>":2 \
--button="<b>Abandon</b>":1 \
--field="Un clic sur '<u>Nouvelle Fenêtre OK</u>' confirme la règle et passe à la suivante. ":LBL "" \
--field="La nouvelle fenêtre doit être ouverte ":LBL "" >> $yad_datas
# Pareil une cmd sous un bouton, appel de la fonction 'xdoyad'
res=$?
case $res in
0 ) yad_inf_fen ;; # Récursivité de yad_inf_fen
1 ) sortie $ERR_ABANDON_YAD ;; #+
2 ) creer_fichs_regles ;; #+ le reste sort en créant ou pas les fichiers
* ) RETOUR=$ERR_CASE_YAD ;; #+ selon le choix utilisateur ou les erreurs possibles
esac
}
########## Fin Partie Fonction
########## PROGRAMME PRINCIPAL ##########################################
#
cd ${HOME}/.devilspie # On travaille dans ~/.devilspie pour désactiver les règles en cours.
defaut="Sur le Bureau où démarre l'application" # Création liste bureaux pour yad : de la forme 1|2|...|^Defaut
nb_bur=$(wmctrl -d | wc -l) # Comptage du nombre de bureaux avec sep=|
for i in `seq 1 $nb_bur`; do
[ "$i" = "1" ] && list_bur=$i$sep || list_bur=$list_bur$i$sep # Création de la liste - Pour 'yad', le séparateur par défaut est !
[ "$i" = "$nb_bur" ] && list_bur=$list_bur"^"$defaut #+ c'est un peu génant comme caractère d'où la varaible "sep"
done
desactiv_regles # On désactive toutes les règles sauf le fichier debug.ds
#+ il est créé s'il le faut
[ -n "$(pgrep -f devilspie)" ] && killall devilspie # On tue devilspie si il est présent (pour être tranquille)
#+ ça vaudra un redémarrage de session (needed to activate new laws)
devilspie -d 2>/dev/null >> $devil_tmp & # On relance devilspie en mode 'debug' et on log stdout dans devil_tmp
yad_attente_appli # On attend que l'application soit lancée,
#+ l'utilsateur le signale par un clic
yad_inf_fen # On affiche les données récupérées dans la fenêtre
#+ de création de règle.
sortie $RETOUR # Et on sort par la petite porte après multiples
#+ boucles dans les fonctions.
exit $ERR_INCONNUE_FIN_SCRIPT # Le script ne doit jamais passer par là !
Bonne déco-lecture 😃
Mouais, le manque de largeur cache les commentaires, va falloir jouer de la glissière :/
Et la mise en page des coms à sauté (surement les tabs)
lol où se trouve le "par Tawal" ^^
Voici comment la mise en page rend chez moi (screenshot du début du script) :
Mise en page screenshot
Évidemment, si vous avez des questions sur le fonctionnement, les commandes etc ... je vous y répondrais 😉
Edit: Rajout d'une éloge de plus pour Christophe C 😉
Edit2: Ajout du script.
Edit3etDernier: Ajout de la dernière invitation (heu c'est douteux ça ...)
Edit4: Menteur que je suis ! Bon j'ai remanié à peu près les commentaires, c'est toujours pas beau mais il n'y a plus de retour à la ligne.
J'espère que j'ai pas fait sauter une " ou autre petit caractère ...
Edit5: Bah si, il manqait une accolade fermante }. J'ai copié collé chez moi testé en terminal. Heureusement, j'en ai pété qu'une !
Edit6: Repost du script, remanié avec MousePad, c'est bien plus joli. Et j'ai retesté la mouture ici présente.
Edit7: Hmmm ... Une petite aide pour la lecture : descendez en lisant les commentaires,
et remontez le script fonction par fonction (quasiment) pour l'exécution ....
Edit8: Correction erreur légère : la définition de ERR_CASE devient ERR_CASE_ZENITY=81, ça concerne un retour d'erreur très peu probable.