MODIFICA IMPORTANTE
Di seguito una versione riscritta dello script dalla prima risposta (sotto). Le differenze:
Il copione
#!/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
Come usare
Lo script ha bisogno di entrambi wmctrl
e xdotool
:
sudo apt-get install wmctrl xdotool
Copia lo script sopra in un file vuoto, salvalo come lock_towspace.py
Della tua applicazione specifica, scopri WM_CLASS
: apri l'applicazione, esegui in un terminale:
xprop WM_CLASS and click on the window of the application
L'output sarà simile (nel tuo caso):
WM_CLASS: WM_CLASS(STRING) = "sun-awt-X11-XFramePeer", "MATLAB R2015a - academic use"
Utilizzare la prima o la seconda parte nel comando per eseguire lo script.
Il comando per eseguire lo script è quindi:
python3 /path/to/lock_towspace.py "sun-awt-X11-XFramePeer" 2,2
Nel comando, l'ultima sezione; 2,2
è l'area di lavoro in cui si desidera bloccare l'applicazione (senza spazi: colonna (!) , riga ), in formato "umano"; la prima colonna / riga è1,1
- Prova lo script eseguendolo. Durante l'esecuzione, apri l'applicazione e lascia che produca Windows come al solito. Tutte le finestre dovrebbero apparire nell'area di lavoro di destinazione, come impostato nel comando.
RISPOSTA AGGIORNATA:
(secondo) VERSIONE DI PROVA
Lo script seguente blocca un'applicazione specifica nel suo spazio di lavoro iniziale. Se lo script viene avviato, determina su quale area di lavoro risiede l'applicazione. Tutte le finestre aggiuntive prodotte dall'applicazione verranno spostate nello stesso spazio di lavoro in una frazione di secondo.
Il problema di messa a fuoco viene risolto focalizzandosi automaticamente sulla finestra focalizzata prima della produzione della finestra aggiuntiva.
Il copione
#!/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
Come usare
La sceneggiatura ha bisogno di entrambi wmctrl
exdotool
sudo apt-get install wmctrl xdotool
Copia lo script in un file vuoto, salvalo come keep_workspace.py
determinare il `WM_CLASS 'dell'applicazione aprendo l'applicazione, quindi aprire un terminale ed eseguire il comando:
xprop WM_CLASS
Quindi fare clic sulla finestra dell'applicazione. Copia l'output, come "sun-awt-X11-XFramePeer", "MATLAB R2015a - academic use"
nel tuo caso, e posizionalo tra virgolette singole nella sezione head dello script, come indicato.
Esegui lo script con il comando:
python3 /path/to/keep_workspace.py
Se funziona come preferisci, aggiungerò una funzione di attivazione / disattivazione. Sebbene funzioni già da alcune ore sul mio sistema, potrebbe essere necessario prima apportare alcune modifiche.
Appunti
Anche se non si dovrebbe notare che, lo script non aggiungere un po 'carico del processore al sistema. Sul mio sistema anziano ho notato un aumento del 3-10%. Se ti piace come funziona, probabilmente lo modificherò ulteriormente per ridurre il carico.
Lo script, così com'è, presuppone che le finestre secondarie siano della stessa classe della finestra principale, come indicato in un commento. Con una (molto) semplice modifica, le finestre secondarie possono essere di un'altra classe.
Spiegazione
Sebbene probabilmente non sia molto interessante per un lettore medio, la sceneggiatura funziona calcolando in vettori. All'avvio, lo script calcola:
- il vettore dall'origine allo spazio di lavoro corrente con l'output di
wmctrl -d
- il vettore alla finestra dell'applicazione, rispetto allo spazio di lavoro corrente, dall'output di
wmctrl -lG
- Da questi due, lo script calcola la posizione assoluta della finestra dell'applicazione sul desktop di spanning (tutte le aree di lavoro in una matrice)
Da quel momento in poi, lo script cerca nuove finestre della stessa applicazione, con l'output di xprop WM_CLASS
, cerca la loro posizione come sopra e le sposta nell'area di lavoro "originale".
Poiché la finestra appena creata "rubava" lo stato attivo dall'ultima finestra utilizzata su cui l'utente stava lavorando, lo stato attivo viene successivamente impostato sulla finestra che aveva lo stato attivo in precedenza.