web-dev-qa-db-fra.com

Déplacer des fenêtres vers des écrans spécifiques à l'aide de la ligne de commande

Ceci est similaire à placez rapidement une fenêtre sur un autre écran en utilisant uniquement le clavier , mais je veux pouvoir utiliser la ligne de commande (de sorte que tout ce que j'ai à faire est de rappeler la ligne de commande à partir du histoire de bash).

Par exemple, envoyer

  • toutes les fenêtres du terminal gnome à eDP1,
  • toutes les fenêtres Emacs vers VGA1, et
  • toutes les Chrome fenêtres vers HDMI1

(et les maximiser après avoir déménagé - mais pas les fous F11 manière, la maximisation normale du style de gestionnaire de fenêtre).

Je voudrais spécifier windows par le nom de l'exécutable.

5
sds

Déplacement de toutes les fenêtres d'une classe de fenêtres spécifique vers un écran spécifique par nom (écran)

Le script ci-dessous envoie des fenêtres, appartenant à un WM_CLASS (application) spécifique, à un écran spécifique, par le nom de l'écran . Comment cela est fait est expliqué dans le script et également plus loin.

Le script suppose que les écrans sont disposés horizontalement et plus ou moins alignés en haut (avec une différence <100 PX).

Le scénario

#!/usr/bin/env python3
import subprocess
import sys

# just a helper function, to reduce the amount of code
get = lambda cmd: subprocess.check_output(cmd).decode("utf-8")

# get the data on all currently connected screens, their x-resolution
screendata = [l.split() for l in get(["xrandr"]).splitlines() if " connected" in l]
screendata = sum([[(w[0], s.split("+")[-2]) for s in w if s.count("+") == 2] for w in screendata], [])

def get_class(classname):
    # function to get all windows that belong to a specific window class (application)
    w_list = [l.split()[0] for l in get(["wmctrl", "-l"]).splitlines()]
    return [w for w in w_list if classname in get(["xprop", "-id", w])]

scr = sys.argv[2]

try:
    # determine the left position of the targeted screen (x)
    pos = [sc for sc in screendata if sc[0] == scr][0]
except IndexError:
    # warning if the screen's name is incorrect (does not exist)
    print(scr, "does not exist. Check the screen name")
else:
    for w in get_class(sys.argv[1]):
        # first move and resize the window, to make sure it fits completely inside the targeted screen
        # else the next command will fail...
        subprocess.Popen(["wmctrl", "-ir", w, "-e", "0,"+str(int(pos[1])+100)+",100,300,300"])
        # maximize the window on its new screen
        subprocess.Popen(["xdotool", "windowsize", "-sync", w, "100%", "100%"])

Comment utiliser

  1. Le script nécessite à la fois wmctrl et xdotool:

    Sudo apt-get install xdotool wmctrl
    
  2. Copiez le script ci-dessous dans un fichier vide, enregistrez-le sous le nom move_wclass.py

  3. Exécutez-le à l'aide de la commande:

    python3 /path/to/move_wclass.py <WM_CLASS> <targeted_screen>
    

    par exemple:

    python3 /path/to/move_wclass.py gnome-terminal VGA-1
    

Pour le WM_CLASS, vous pouvez utiliser la partie du WM_CLASS, comme dans l'exemple. Le nom de l'écran doit être le exact et le nom complet.

Comment c'est fait (le concept)

L'explication porte principalement sur le concept, pas tellement sur le codage.

Dans la sortie de xrandr, pour chaque écran connecté, il y a une chaîne/ligne, ressemblant à:

VGA-1 connected 1280x1024+1680+0

Cette ligne nous donne des informations sur la position de l'écran et son nom , comme expliqué ici .

Le script répertorie les informations pour tous les écrans. Lorsque le script est exécuté avec l'écran et la classe window comme arguments, il recherche la position (x-) de l'écran, toutes les fenêtres (-id's) d'une certaine classe (à l'aide de wmctrl -l et la sortie de xprop -id <window_id>.

Par la suite, le script déplace toutes les fenêtres, une par une, vers une position sur l'écran ciblé (à l'aide de wmctrl -ir <window_id> -e 0,<x>,<y>,<width>,<height>) et l'agrandit (avec xdotool windowsize 100% 100%).

Remarque

Le script a bien fonctionné sur les tests avec lesquels je l'ai exécuté. Utiliser wmctrl, et même xdotool, sur Unity peut présenter certaines particularités tenaces, qui doivent parfois être résolues par l'expérience plutôt que par le raisonnement. Si vous rencontrez des exceptions, veuillez mentionner.

6
Jacob Vlijm

J'ai réécrit le code @jacobs python en bash simple et en le faisant fonctionner (j'ai testé cela sur ubuntu 16 cannelle).

Je devais ajouter remove,maximized_vert, remove,maximized_horz sans que Windows ne bouge pas.

#!/bin/bash

if [ ! -z "$1" ] || [ -z "$2" ]; then
    command=$(wmctrl -l | grep $1 | cut -d" " -f1)

    if [ ! -z "$command" ]; then
        position=$(xrandr | grep "^$2" | cut -d"+" -f2)

        if [ ! -z "$position" ]; then
            for window in $command; do 
               wmctrl -ir $window -b remove,maximized_vert
               wmctrl -ir $window -b remove,maximized_horz 
               wmctrl -ir $window -e 0,$position,0,1920,1080
               wmctrl -ir $window -b add,maximized_vert
               wmctrl -ir $window -b add,maximized_horz 
            done
        else
            echo -e "not found monitor with given name"
        fi
    else
        echo -e "not found windows with given name"
    fi
else
    echo -e "specify window and monitor name;\nmove.sh window-name monitor-name"
fi
  1. Sudo apt-get install xdotool wmctrl
  2. /path/to/script.sh "window-name" "monitor-name"
1
Andrzej Piszczek

Pour mémoire, voici ce que j’utilise pour combiner cette question et Restaurer plusieurs paramètres de moniteur :

# configure multiplr displays and
# move the windows to their appropriate displays

import subprocess
import os
import wmctrl
import re

mydisplays = [("VGA1",0,"left"),
              ("eDP1",1080,"normal"),
              ("HDMI1",3000,"left")]

# https://askubuntu.com/questions/702002/restore-multiple-monitor-settings
def set_displays ():
    subprocess.check_call(" && ".join([
        "xrandr --output %s --pos %dx0  --rotate %s" % d for d in mydisplays]),
                          Shell=True)

# https://askubuntu.com/questions/702071/move-windows-to-specific-screens-using-the-command-line
mywindows = [("/emacs$","VGA1"),
             ("/chrome$","HDMI1"),
             ("gnome-terminal","eDP1")]
def max_windows ():
    didi = dict([(d,x) for d,x,_ in mydisplays])
    for w in wmctrl.Window.list():
        try:
            exe = os.readlink("/proc/%d/exe" % (w.pid))
            for (r,d) in mywindows:
                if re.search(r,exe):
                    x = didi[d]
                    print "%s(%s) --> %s (%d)" % (r,exe,d,x)
                    w.set_properties(("remove","maximized_vert","maximized_horz"))
                    w.resize_and_move(x,0,w.w,w.h)
                    w.set_properties(("add","maximized_vert","maximized_horz"))
                    break
        except OSError:
            continue

def cmdlines (cmd):
    return subprocess.check_output(cmd).splitlines()

def show_displays ():
    for l in cmdlines(["xrandr"]):
        if " connected " in l:
            print l

if __== '__main__':
    show_displays()
    set_displays()
    show_displays()
    max_windows()

vous auriez besoin d'utiliser wmctrl version 0.3 ou ultérieure (à cause de ma requête d'extraction ).

1
sds