WAŻNA EDYCJA
Poniżej przepisana wersja skryptu z pierwszej odpowiedzi (poniżej). Różnice:
Scenariusz
#!/usr/bin/env python3
import subprocess
import sys
import time
import math
app_class = sys.argv[1]
ws_lock = [int(n)-1 for n in sys.argv[2].split(",")]
def check_wlist():
# get the current list of windows
try:
raw_list = [
l.split() for l in subprocess.check_output(
["wmctrl", "-lG"]
).decode("utf-8").splitlines()
]
ids = [l[0] for l in raw_list]
return (raw_list, ids)
except subprocess.CalledProcessError:
pass
def get_wssize():
# get workspace size
resdata = subprocess.check_output(["xrandr"]).decode("utf-8").split()
i = resdata.index("current")
return [int(n) for n in [resdata[i+1], resdata[i+3].replace(",", "")]]
def get_current(ws_size):
# vector of the current workspace to origin of the spanning desktop
dt_data = subprocess.check_output(
["wmctrl", "-d"]
).decode("utf-8").split()
curr = [int(n) for n in dt_data[5].split(",")]
return (int(curr[0]/ws_size[0]), int(curr[1]/ws_size[1]))
def get_relativewinpos(ws_size, w_data):
# vector to the application window, relative to the current workspace
xpos = int(w_data[2]); ypos = int(w_data[3])
xw = ws_size[0]; yw = ws_size[1]
return (math.ceil((xpos-xw)/xw), math.ceil((ypos-yw)/yw))
def get_abswindowpos(ws_size, w_data):
# vector from the origin to the current window's workspace (flipped y-axis)
curr_pos = get_current(ws_size)
w_pos = get_relativewinpos(ws_size, w_data)
return (curr_pos[0]+w_pos[0], curr_pos[1]+w_pos[1])
def wm_class(w_id):
# get the WM_CLASS of new windows
return subprocess.check_output(
["xprop", "-id", w_id.strip(), "WM_CLASS"]
).decode("utf-8").split("=")[-1].strip()
ws_size = get_wssize()
wlist1 = []
subprocess.Popen(["notify-send", 'workspace lock is running for '+app_class])
while True:
# check focussed window ('except' for errors during "wild" workspace change)
try:
focus = subprocess.check_output(
["xdotool", "getwindowfocus"]
).decode("utf-8")
except subprocess.CalledProcessError:
pass
time.sleep(1)
wdata = check_wlist()
if wdata != None:
# compare existing window- ids, checking for new ones
wlist2 = wdata[1]
if wlist2 != wlist1:
# if so, check the new window's class
newlist = [[w, wm_class(w)] for w in wlist2 if not w in wlist1]
valids = sum([[l for l in wdata[0] if l[0] == w[0]] \
for w in newlist if app_class in w[1]], [])
# for matching windows, check if they need to be moved (check workspace)
for w in valids:
abspos = list(get_abswindowpos(ws_size, w))
if not abspos == ws_lock:
current = get_current(ws_size)
move = (
(ws_lock[0]-current[0])*ws_size[0],
(ws_lock[1]-current[1])*ws_size[1]-56
)
new_w = "wmctrl -ir "+w[0]+" -e "+(",").join(
["0", str(int(w[2])+move[0]),
str(int(w[2])+move[1]), w[4], w[5]]
)
subprocess.call(["/bin/bash", "-c", new_w])
# re- focus on the window that was focussed
if not app_class in wm_class(focus):
subprocess.Popen(["wmctrl", "-ia", focus])
wlist1 = wlist2
Jak używać
Skrypt potrzebuje zarówno wmctrl
a xdotool
:
sudo apt-get install wmctrl xdotool
Skopiuj powyższy skrypt do pustego pliku i zapisz go jako lock_towspace.py
O konkretnej aplikacji dowiedz się WM_CLASS
: otwórz aplikację, uruchom w terminalu:
xprop WM_CLASS and click on the window of the application
Wynik będzie wyglądał następująco (w twoim przypadku):
WM_CLASS: WM_CLASS(STRING) = "sun-awt-X11-XFramePeer", "MATLAB R2015a - academic use"
Użyj pierwszej lub drugiej części polecenia, aby uruchomić skrypt.
Polecenie uruchomienia skryptu to:
python3 /path/to/lock_towspace.py "sun-awt-X11-XFramePeer" 2,2
W poleceniu ostatnia sekcja; 2,2
to obszar roboczy, w którym chcesz zablokować aplikację (bez spacji: (!) kolumna, wiersz ), w formacie „ludzkim”; pierwsza kolumna / wiersz to1,1
- Przetestuj skrypt, uruchamiając go. Podczas pracy otwórz aplikację i pozwól, aby generowała okna jak zwykle. Wszystkie okna powinny pojawić się w docelowym obszarze roboczym, zgodnie z ustawieniem w poleceniu.
NIEAKTUALIZOWANA ODPOWIEDŹ:
(drugi) WERSJA TESTOWA
Poniższy skrypt blokuje określoną aplikację w jej początkowym obszarze roboczym. Jeśli skrypt zostanie uruchomiony, określa, w którym obszarze roboczym znajduje się aplikacja. Wszystkie dodatkowe okna tworzone przez aplikację zostaną przeniesione do tego samego obszaru roboczego w ułamku sekundy.
Problem fokusu jest rozwiązany przez automatyczne ponowne ustawienie ostrości na oknie, które było fokusowane przed utworzeniem dodatkowego okna.
Scenariusz
#!/usr/bin/env python3
import subprocess
import time
import math
app_class = '"gedit", "Gedit"'
def get_wssize():
# get workspace size
resdata = subprocess.check_output(["xrandr"]).decode("utf-8").split()
i = resdata.index("current")
return [int(n) for n in [resdata[i+1], resdata[i+3].replace(",", "")]]
def get_current(ws_size):
# get vector of the current workspace to the origin of the spanning desktop (flipped y-axis)
dt_data = subprocess.check_output(["wmctrl", "-d"]).decode("utf-8").split(); curr = [int(n) for n in dt_data[5].split(",")]
return (int(curr[0]/ws_size[0]), int(curr[1]/ws_size[1]))
def get_relativewinpos(ws_size, w_data):
# vector to the application window, relative to the current workspace
xw = ws_size[0]; yw = ws_size[1]
return (math.ceil((w_data[1]-xw)/xw), math.ceil((w_data[2]-yw)/yw))
def get_abswindowpos(ws_size, w_data):
curr_pos = get_current(ws_size)
w_pos = get_relativewinpos(ws_size, w_data)
return (curr_pos[0]+w_pos[0], curr_pos[1]+w_pos[1])
def wm_class(w_id):
return subprocess.check_output(["xprop", "-id", w_id, "WM_CLASS"]).decode("utf-8").split("=")[-1].strip()
def filter_windows(app_class):
# find windows (id, x_pos, y_pos) of app_class
try:
raw_list = [l.split() for l in subprocess.check_output(["wmctrl", "-lG"]).decode("utf-8").splitlines()]
return [(l[0], int(l[2]), int(l[3]), l[4], l[5]) for l in raw_list if wm_class(l[0]) == app_class]
except subprocess.CalledProcessError:
pass
ws_size = get_wssize()
init_window = get_abswindowpos(ws_size, filter_windows(app_class)[0])
valid_windows1 = filter_windows(app_class)
while True:
focus = subprocess.check_output(["xdotool", "getwindowfocus"]).decode("utf-8")
time.sleep(1)
valid_windows2 = filter_windows(app_class)
if all([valid_windows2 != None, valid_windows2 != valid_windows1]):
for t in [t for t in valid_windows2 if not t[0] in [w[0] for w in valid_windows1]]:
absolute = get_abswindowpos(ws_size, t)
if not absolute == init_window:
current = get_current(ws_size)
move = ((init_window[0]-current[0])*ws_size[0], (init_window[1]-current[1])*ws_size[1]-56)
new_w = "wmctrl -ir "+t[0]+" -e "+(",").join(["0", str(t[1]+move[0]), str(t[2]+move[1]), t[3], t[4]])
subprocess.call(["/bin/bash", "-c", new_w])
focus = str(hex(int(focus)))
z = 10-len(focus); focus = focus[:2]+z*"0"+focus[2:]
if not wm_class(focus) == app_class:
subprocess.Popen(["wmctrl", "-ia", focus])
valid_windows1 = valid_windows2
Jak używać
Skrypt potrzebuje zarówno, jak wmctrl
ixdotool
sudo apt-get install wmctrl xdotool
Skopiuj skrypt do pustego pliku i zapisz go jako keep_workspace.py
określ `WM_CLASS 'swojej aplikacji, otwierając aplikację, a następnie otwórz terminal i uruchom polecenie:
xprop WM_CLASS
Następnie kliknij okno aplikacji. Skopiuj wynik, wyglądając jak "sun-awt-X11-XFramePeer", "MATLAB R2015a - academic use"
w twoim przypadku, i umieść go między pojedynczymi cudzysłowami w sekcji head skryptu, jak wskazano.
Uruchom skrypt za pomocą polecenia:
python3 /path/to/keep_workspace.py
Jeśli działa tak, jak chcesz, dodam funkcję przełączania. Chociaż działa w moim systemie już od kilku godzin, jednak najpierw może wymagać drobnych poprawek.
Notatki
Chociaż nie należy zauważyć, skrypt robi dodać trochę obciążenia procesora w systemie. W moim starszym systemie zauważyłem wzrost o 3-10%. Jeśli podoba ci się, jak to działa, prawdopodobnie poprawię go jeszcze bardziej, aby zmniejszyć obciążenie.
Skrypt, jak to jest, zakłada, że dodatkowe okna są tej samej klasy co główne okno, jak wskazano w komentarzu. Z (bardzo) prostą zmianą okna pomocnicze mogą jednak należeć do innej klasy.
Wyjaśnienie
Chociaż prawdopodobnie nie jest zbyt interesujący dla przeciętnego czytelnika, skrypt działa poprzez obliczanie wektorów. Podczas uruchamiania skrypt oblicza:
- wektor od początku do bieżącego obszaru roboczego z wynikiem
wmctrl -d
- wektor do okna aplikacji, względem bieżącego obszaru roboczego, według danych wyjściowych
wmctrl -lG
- Na podstawie tych dwóch skrypt oblicza bezwzględną pozycję okna aplikacji na łączącym pulpicie (wszystkie obszary robocze w jednej macierzy)
Od tego momentu skrypt szuka nowych okien tej samej aplikacji, z wyjściem xprop WM_CLASS
, sprawdza ich pozycję w taki sam sposób jak powyżej i przenosi je do „oryginalnego” obszaru roboczego.
Ponieważ nowo utworzone okno „ukradło” fokus z ostatnio używanego okna, nad którym pracował użytkownik, fokus jest następnie ustawiany na okno, które miało fokus wcześniej.