명령줄을 사용하여 창을 특정 화면으로 이동

명령줄을 사용하여 창을 특정 화면으로 이동

이는 다음과 유사합니다.키보드만 사용하여 창을 다른 화면에 빠르게 배치, 그러나 나는 명령줄을 사용할 수 있기를 원합니다(그래서 내가 해야 할 일은 bash 기록에서 명령줄을 불러오는 것뿐입니다).

예를 들어, 보내다

  • 모든 그놈 터미널 창을 eDP1,
  • 모든 Emacs 창을 VGA1, 및
  • 모든 Chrome 창HDMI1

(그리고 이동한 후에 최대화합니다. 그러나 F11일반적인 창 관리자 스타일 최대화와 같은 이상한 방법은 아닙니다.)

실행 파일 이름으로 창을 지정하고 싶습니다.

답변1

특정 창 클래스의 모든 창을 (화면-) 이름별로 특정 화면으로 이동

WM_CLASS아래 스크립트는 특정 (응용 프로그램)에 속한 창을 특정 화면으로 보냅니다 .이름. 그 방법은 스크립트와 아래에 자세히 설명되어 있습니다.

스크립트는 화면이 수평으로 배열되어 있고 어느 정도 위쪽에 정렬되어 있다고 가정합니다(차이 < 100 PX).

스크립트

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

사용하는 방법

  1. 스크립트에는 wmctrl및 다음이 모두 필요합니다 xdotool.

    sudo apt-get install xdotool wmctrl
    
  2. 아래 스크립트를 빈 파일에 복사하고 다른 이름으로 저장하세요.move_wclass.py

  3. 다음 명령으로 실행하세요.

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

    예를 들어:

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

의 경우 다음을 WM_CLASS사용할 수 있습니다.부분WM_CLASS예와 같습니다. 화면 이름은 다음과 같아야 합니다.정확한그리고 완전한 이름.

수행 방법(개념)

설명은 대부분 개념에 관한 것이며 코딩에 대한 설명은 많지 않습니다.

xrandr의 출력에는 연결된 모든 화면에 대해 다음과 같은 문자열/줄이 있습니다.

VGA-1 connected 1280x1024+1680+0

이 줄은 화면의 정보를 제공합니다.위치그리고 그것의이름, 설명대로여기.

스크립트는 모든 화면에 대한 정보를 나열합니다. 스크립트가 화면과 창 클래스를 인수로 사용하여 실행되면 화면의 (x-) 위치를 조회하고 특정 클래스의 모든 창(-id's)을 조회합니다( wmctrl -l및 의 출력을 사용하여 xprop -id <window_id>.

그런 다음 스크립트는 모든 창을 대상 화면의 위치로 하나씩 이동하고( 사용 wmctrl -ir <window_id> -e 0,<x>,<y>,<width>,<height>) 이를 최대화합니다( 사용 xdotool windowsize 100% 100%).

메모

스크립트는 제가 실행한 테스트에서 잘 작동했습니다. Unity에서 wmctrl, 심지어 를 사용하면 xdotool몇 가지 완고한 특성이 있을 수 있지만 때로는 추론보다는 실험을 통해 해결해야 합니다. 예외가 발생할 수 있는 경우 언급해 주세요.

답변2

@jacobs Python 코드를 간단한 bash로 다시 작성하여 작동하도록 했습니다(우분투 16 계피에서 테스트했습니다).

remove,maximized_vert, remove,maximized_horz창문이 움직이지 않는 것을 추가해야했습니다 .

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

답변3

기록을 위해 이 질문과다중 모니터 설정 복원:

# 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()

당신은 사용해야 할 것입니다wmctrl버전 0.3 이상(내 때문에풀 리퀘스트).

답변4

@AndrzejPiszczek의 답변을 바탕으로 모든 창을 특정 화면으로 이동하는 방법은 다음과 같습니다.

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
}

관련 정보