Verschieben Sie Fenster mithilfe der Befehlszeile auf bestimmte Bildschirme

Verschieben Sie Fenster mithilfe der Befehlszeile auf bestimmte Bildschirme

Das ist ähnlich wiePlatzieren Sie ein Fenster schnell auf einem anderen Bildschirm, indem Sie nur die Tastatur verwenden, aber ich möchte die Befehlszeile verwenden können (so dass ich nur die Befehlszeile aus dem Bash-Verlauf abrufen muss).

Senden Sie beispielsweise

  • alle Gnome-Terminalfenster zu eDP1,
  • alle Emacs-Fenster zu VGA1, und
  • alle Chrome-Fenster aufHDMI1

(und maximieren Sie sie nach dem Verschieben – aber nicht auf die verrückte F11Art, sondern die normale Maximierung im Fenstermanager-Stil).

Ich möchte Windows anhand des ausführbaren Namens angeben.

Antwort1

Verschieben aller Fenster einer bestimmten Fensterklasse auf einen bestimmten Bildschirm anhand des (Bildschirm-)Namens

Das folgende Skript sendet Fenster, die zu einer bestimmten WM_CLASS(Anwendung) gehören, an einen bestimmten Bildschirm, und zwar über denName. Wie das geht, wird im Skript und auch weiter unten erklärt.

Das Skript geht davon aus, dass die Bildschirme horizontal angeordnet und mehr oder weniger oben ausgerichtet sind (mit einem Unterschied von < 100 PX).

Das Drehbuch

#!/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%"])

Wie benutzt man

  1. Das Skript benötigt beides wmctrlund xdotool:

    sudo apt-get install xdotool wmctrl
    
  2. Kopieren Sie das folgende Skript in eine leere Datei und speichern Sie es untermove_wclass.py

  3. Führen Sie es mit dem folgenden Befehl aus:

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

    Zum Beispiel:

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

Für WM_CLASSkönnen Sie verwenden:Teildes WM_CLASS, wie im Beispiel. Der Name des Bildschirms muss dergenauund vollständiger Name.

Wie es gemacht wird (das Konzept)

Die Erklärung bezieht sich hauptsächlich auf das Konzept und nicht so sehr auf die Codierung.

In der Ausgabe von xrandr gibt es für jeden verbundenen Bildschirm eine Zeichenfolge/Zeile, die wie folgt aussieht:

VGA-1 connected 1280x1024+1680+0

Diese Zeile gibt uns Informationen über den BildschirmPositionund seinName, wie erklärtHier.

Das Skript listet die Informationen für alle Bildschirme auf. Wenn das Skript mit dem Bildschirm und der Fensterklasse als Argumente ausgeführt wird, sucht es die (x-)Position des Bildschirms, sucht alle Fenster (-id's) einer bestimmten Klasse (mithilfe von wmctrl -lund der Ausgabe von xprop -id <window_id>).

Anschließend verschiebt das Skript alle Fenster nacheinander an eine Position auf dem Zielbildschirm (mit wmctrl -ir <window_id> -e 0,<x>,<y>,<width>,<height>) und maximiert diese (mit xdotool windowsize 100% 100%).

Notiz

Das Skript funktionierte bei den Tests, mit denen ich es ausgeführt habe, einwandfrei. Die Verwendung von wmctrlund sogar xdotoolmit Unity kann jedoch einige hartnäckige Eigenheiten aufweisen, die manchmal eher durch Experimente als durch Argumentation gelöst werden müssen. Wenn Sie auf Ausnahmen stoßen, geben Sie dies bitte an.

Antwort2

Ich habe den Python-Code von @jacobs in einfaches Bash umgeschrieben und zum Laufen gebracht (ich habe dies auf Ubuntu 16 Cinnamon getestet).

Ich musste hinzufügen, remove,maximized_vert, remove,maximized_horzdass sich die Fenster ohne dieses nicht bewegten.

#!/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"

Antwort3

Fürs Protokoll: Hier ist, was ich für die Kombination dieser Frage verwende undWiederherstellen mehrerer Monitoreinstellungen:

# configure multiple 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 __name__ == '__main__':
    show_displays()
    set_displays()
    show_displays()
    max_windows()

Sie müssten verwendenwmctrlVersion 0.3 oder höher (aufgrund meinerPull-Anfrage).

Antwort4

Basierend auf der Antwort von @AndrzejPiszczek gibt es folgende Möglichkeit, alle Fenster auf einen bestimmten Bildschirm zu verschieben:

function move_win {
    if [ -z "$1" ]; then
        echo -e "Specify a screen, possible options: "
        echo -e $(xrandr | grep " connected " | cut -d'-' -f1)
        return
    fi

    MONITOR=$1

    # get all relevant windows on all screens
    windows=$(wmctrl -l | egrep -v " -1 " | cut -d" " -f1)

    if [ ! -z "$windows" ]; then
        # get the necessary metrics from the screen the windows should be moved to 
        # will contain: width, height, offsetX, offsetY
        screen_values=($(xrandr | grep "^$MONITOR-.* connected" | grep -Eo '[0-9]+x[0-9]+\+[0-9]+\+[0-9]+' | sed 's/x/ /g; s/+/ /g'))

        if (( ${#screen_values[@]} )); then
            # get the start/end position of the screen so we can later determine
            # if the window is already on the screen or not
            screen_start_pos=$(( ${screen_values[2]} ))
            screen_end_pos=$(( ${screen_values[2]} + ${screen_values[0]} ))

            for window in $windows; do
                # get the window name
                window_name=$(wmctrl -lG | grep "$window" | awk -F "$HOSTNAME " '{print $2}')
                # extract relevant window geometry values such as x, y, width, height
                window_values=($(wmctrl -lG | grep "$window" | awk -F " " '{print $3, $5, $6}'))

                # if the window's X origin position is already inside the screen's 
                # total width then don't move it (this won't work exactly for windows only partially on the screen)
                if (( ${window_values[0]} >= $screen_end_pos || ${window_values[0]} < $screen_start_pos )); then
                    echo -e "Moving to screen $MONITOR: $window_name"
                  
                    wmctrl -ir $window -b remove,maximized_vert
                    wmctrl -ir $window -b remove,maximized_horz
                    # the -e parameters are gradient,x,y,width,height
                    # move window to (X,Y) -> (0,0) of new screen and the same window dimensions
                    wmctrl -ir $window -e 0,$screen_start_pos,0,${window_values[1]},${window_values[2]}
                else
                    echo -e "Already on screen $MONITOR: $window_name"
                fi
            done
        else 
            echo -e "No screen found"
        fi
    else
        echo -e "No windows found"
    fi
}

verwandte Informationen