EDICION IMPORTANTE
A continuación, una versión reescrita del guión de la primera respuesta (a continuación). Las diferencias:
La secuencia de comandos
#!/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
Cómo utilizar
El script necesita ambos wmctrl
y xdotool
:
sudo apt-get install wmctrl xdotool
Copie el script anterior en un archivo vacío, guárdelo como lock_towspace.py
De su aplicación específica, descubra WM_CLASS
: abra su aplicación, ejecute en una terminal:
xprop WM_CLASS and click on the window of the application
La salida se verá así (en su caso):
WM_CLASS: WM_CLASS(STRING) = "sun-awt-X11-XFramePeer", "MATLAB R2015a - academic use"
Utilice la primera o la segunda parte del comando para ejecutar el script.
El comando para ejecutar el script es:
python3 /path/to/lock_towspace.py "sun-awt-X11-XFramePeer" 2,2
En el comando, la última sección; 2,2
es el espacio de trabajo donde desea bloquear la aplicación (sin espacios: (!) columna, fila ), en formato "humano"; la primera columna / fila es1,1
- Pruebe el script ejecutándolo. Mientras se ejecuta, abra su aplicación y deje que produzca ventanas como de costumbre. Todas las ventanas deben aparecer en el espacio de trabajo de destino, como se establece en el comando.
RESPUESTA ACTUALIZADA:
(segundo) VERSIÓN DE PRUEBA
El siguiente script bloquea una aplicación específica en su espacio de trabajo inicial. Si se inicia el script, determina en qué espacio de trabajo reside la aplicación. Todas las ventanas adicionales que produce la aplicación se moverán al mismo espacio de trabajo en una fracción de segundo.
El problema de enfoque se resuelve reenfocando automáticamente la ventana que se enfocó antes de que se produjera la ventana adicional.
La secuencia de comandos
#!/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
Cómo utilizar
El script necesita ambos wmctrl
yxdotool
sudo apt-get install wmctrl xdotool
Copie el script en un archivo vacío, guárdelo como keep_workspace.py
determine el `WM_CLASS 'de su aplicación abriendo la aplicación, luego abra una terminal y ejecute el comando:
xprop WM_CLASS
Luego haga clic en la ventana de su aplicación. Copie el resultado, como "sun-awt-X11-XFramePeer", "MATLAB R2015a - academic use"
en su caso, y colóquelo entre comillas simples en la sección principal del script, como se indica.
Ejecute el script con el comando:
python3 /path/to/keep_workspace.py
Si funciona como desea, agregaré una función de alternar. Aunque ya funciona durante algunas horas en mi sistema, sin embargo, podría necesitar algunos ajustes primero.
Notas
Aunque no se debe notar que, el guión hace añadir un poco de carga del procesador al sistema. En mi sistema de ancianos noté un aumento del 3-10%. Si te gusta cómo funciona, probablemente lo ajustaré aún más para reducir la carga.
El script, tal como está, supone que las ventanas secundarias son de la misma clase que la ventana principal, como indicó en un comentario. Sin embargo, con un cambio (muy) simple, las ventanas secundarias pueden ser de otra clase.
Explicación
Aunque probablemente no sea muy interesante para un lector promedio, el script funciona calculando en vectores. En el inicio, el script calcula:
- el vector desde el origen hasta el espacio de trabajo actual con la salida de
wmctrl -d
- el vector a la ventana de la aplicación, relativo al espacio de trabajo actual, por la salida de
wmctrl -lG
- A partir de estos dos, el script calcula la posición absoluta de la ventana de la aplicación en el escritorio de expansión (todos los espacios de trabajo en una matriz)
A partir de ese momento, el script busca nuevas ventanas de la misma aplicación, con la salida de xprop WM_CLASS
, busca su posición de la misma manera que antes y las mueve al espacio de trabajo "original".
Dado que la ventana recién creada "robó" el foco de la última ventana utilizada en la que estaba trabajando el usuario, el foco se establece posteriormente en la ventana que tenía foco anteriormente.