ВАЖЛИВА РЕДАКЦІЯ
Нижче переписана версія сценарію з першої відповіді (нижче). Відмінності:
Сценарій
#!/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
Як використовувати
Сценарію потрібно як wmctrl
і xdotool
:
sudo apt-get install wmctrl xdotool
Скопіюйте сценарій вище в порожній файл, збережіть його як lock_towspace.py
З конкретної програми дізнайтеся WM_CLASS
: відкрийте програму, запустіть у терміналі:
xprop WM_CLASS and click on the window of the application
Вихід буде мати вигляд (у вашому випадку):
WM_CLASS: WM_CLASS(STRING) = "sun-awt-X11-XFramePeer", "MATLAB R2015a - academic use"
Для запуску сценарію використовуйте першу або другу частину команди.
Команда для запуску сценарію тоді:
python3 /path/to/lock_towspace.py "sun-awt-X11-XFramePeer" 2,2
У команді останній розділ; 2,2
- це робоча область, де ви хочете заблокувати додаток у (без пробілів: (!) стовпець, рядок ) у форматі "людський"; перший стовпчик / рядок1,1
- Перевірте сценарій, запустивши його. Під час роботи відкрийте додаток і дайте йому створювати вікна, як зазвичай. Усі вікна повинні з’являтися на цільовій робочій області, як встановлено в команді.
ВИРІШЕНИЙ ВІДПОВІДЬ:
(друга) ТЕСТОВА ВЕРСІЯ
Сценарій нижче блокує певну програму до її початкової робочої області. Якщо сценарій запущений, він визначає, в якій робочій області знаходиться програма. Усі додаткові вікна, які виробляє додаток, будуть переміщені до тієї ж робочої області за секунду.
Проблема фокусування вирішується автоматичним повторним фокусуванням на вікні, на яке було сфокусовано до створення додаткового вікна.
Сценарій
#!/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
Як використовувати
Сценарій потребує wmctrl
іxdotool
sudo apt-get install wmctrl xdotool
Скопіюйте скрипт у порожній файл, збережіть його як keep_workspace.py
визначте програму "WM_CLASS", відкривши програму, потім відкрийте термінал і виконайте команду:
xprop WM_CLASS
Потім натисніть на вікно програми. Скопіюйте висновок, схожий "sun-awt-X11-XFramePeer", "MATLAB R2015a - academic use"
на ваш випадок, і помістіть його між окремими цитатами в головному розділі сценарію, як зазначено.
Запустіть сценарій командою:
python3 /path/to/keep_workspace.py
Якщо він працює, як вам подобається, я додаю функцію перемикання. Хоча це працює вже на кілька годин у моїй системі, але, можливо, спочатку знадобиться певна настройка.
Примітки
Хоча ви не повинні цього помічати, скрипт дійсно додає деяку завантаження процесора в систему. На моїй літній системі я помітив збільшення на 3-10%. Якщо вам подобається, як це працює, я, ймовірно, ще більше налаштувати його, щоб зменшити навантаження.
Сценарій, як і є, передбачає, що окремі вікна є того ж класу, що і головне вікно, як ви вказали в коментарі. З (дуже) простою зміною, вторинні вікна можуть бути іншого класу, однак.
Пояснення
Хоча, мабуть, не дуже цікаво для пересічного читача, сценарій працює за допомогою обчислення у векторах. При запуску сценарій обчислює:
- вектор від джерела до поточної робочої області з виходом
wmctrl -d
- вектор до вікна програми, відносно поточної робочої області, на виході
wmctrl -lG
- З цих двох сценаріїв обчислюється абсолютне положення вікна програми на робочому столі (всі робочі простори в одній матриці)
З цього моменту сценарій шукає нові вікна того ж додатка, з виведенням xprop WM_CLASS
, шукає їх положення так само, як вище, і переміщує їх у "оригінальну" робочу область.
Оскільки новостворене вікно "вкрало" фокус з останнього використовуваного вікна, над яким працював користувач, згодом фокус встановлюється у вікно, на яке було раніше фокусування.