Mover janelas para telas específicas usando a linha de comando

5

Isto é similar a Coloque rapidamente uma janela em outra tela usando apenas o teclado , mas eu quero ser capaz de use a linha de comando (para que tudo que eu precise fazer é recuperar a linha de comando do histórico do bash).

Por exemplo, envie

  • todas as janelas do terminal do gnome para eDP1 ,
  • todas as janelas do Emacs para VGA1 e
  • todas as janelas do Chrome para HDMI1

(e maximizá-los depois de se mover - mas não a maneira louca F11 , a maximização normal do estilo window-manager).

Eu gostaria de especificar janelas pelo nome do executável.

    
por sds 24.11.2015 / 19:22

3 respostas

5

Mover todas as janelas de uma classe de janela específica para uma tela específica por nome (tela)

O script abaixo enviará janelas, pertencentes a um WM_CLASS (aplicativo) específico, para uma tela específica, pelo nome da tela. Como isso é feito é explicado no script e também abaixo.

O script pressupõe que as telas estão organizadas horizontalmente e mais ou menos alinhadas na parte superior (com uma diferença de < 100 PX).

O script

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

Como usar

  1. O script precisa dos dois wmctrl e xdotool :

    sudo apt-get install xdotool wmctrl
    
  2. Copie o script abaixo em um arquivo vazio, salve-o como move_wclass.py

  3. Execute-o pelo comando:

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

    por exemplo:

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

Para o WM_CLASS , você pode usar parte do WM_CLASS , como no exemplo. O nome da tela precisa ser o exato e o nome completo.

Como é feito (o conceito)

A explicação é principalmente sobre o conceito, não tanto sobre a codificação.

Na saída do xrandr, para cada tela conectada, há uma string / linha, parecida com:

VGA-1 connected 1280x1024+1680+0

Esta linha nos fornece informações sobre a posição da tela e seu nome , conforme explicado aqui .

O script lista as informações para todas as telas. Quando o script é executado com a tela e a classe da janela como argumentos, ele procura a posição (x-) da tela, procura todas as janelas (-id's) de uma determinada classe (com a ajuda de wmctrl -l e saída de xprop -id <window_id> .

Posteriormente, o script move todas as janelas, uma por uma, para uma posição na tela de destino (usando wmctrl -ir <window_id> -e 0,<x>,<y>,<width>,<height> ) e maximiza-a (com xdotool windowsize 100% 100% ).

Nota

O script funcionou bem nos testes que eu executei. Usar wmctrl , e até xdotool , em Unity pode ter algumas peculiaridades teimosas, no entanto, que às vezes precisam ser resolvidas pelo experimento, e não pelo raciocínio. Se você pode se deparar com exceções, por favor mencione.

    
por Jacob Vlijm 25.11.2015 / 11:18
1

Para o registro, aqui está o que eu uso para a combinação desta questão e Restaurar várias configurações do monitor :

# 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://sobrelinux.info/questions/70077/restore-multiple-monitor-settings" && ".join([
        "xrandr --output %s --pos %dx0  --rotate %s" % d for d in mydisplays]),
                          shell=True)

# https://sobrelinux.info/questions/34974/move-windows-to-specific-screens-using-the-command-line"/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()

você precisaria usar wmctrl versão 0.3 ou posterior (por causa do meu pull request ).

    
por sds 25.11.2015 / 16:43
1

Eu reescrevi o código python @jacobs para o bash simples e o fiz funcionar (eu testei isso no canela do Ubuntu 16).

Eu tive que adicionar remove,maximized_vert, remove,maximized_horz sem que as janelas não se movessem.

#!/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"
por 22.10.2018 / 09:20