所以我有两个显示器,主显示器在右侧(由于 HDMI 电缆长度),辅助显示器在左侧。我启用了“仅在主显示器上切换工作区”选项。
问题是,当我切换到另一个工作区时,辅助显示器上的窗口会窃取焦点,直到我手动 alt tab 到必要的窗口,然后再次切换到该工作区。
问题是:切换到任何工作区后,如何使 GNOME 焦点窗口(即左上角)位于主显示屏上?
答案1
先决条件
- 已
xdotool
安装在您的系统中- 您可能需要在 Xorg (X11) 中运行您的 gnome 系统,因为此工具在 Wayland 中不起作用
- 已
python3
安装在您的系统中- 我不需要安装任何软件包,
pip
因为这些模块大多是内置的,并且随 python 一起提供
- 我不需要安装任何软件包,
战略
- 创建一个脚本
activewindow
每次桌面/工作区发生变化时都会强制进行新的操作。 - 自动运行此脚本以在启动时运行
Gnome Focus 自动化脚本
#!/usr/bin/env python3
import os
import time
import subprocess
import atexit
import signal
import sys
# DESCRIPTION:
#
# Checks out every "REFRESH_RATE" seconds if the workspace/desktop changed
# If so, correct the focus to the last screen you were focusing in that workspace/desktop
#
# In the case that we have no history of you focusing a screen in the current desktop,
# attempt to focus on the topmost window by searching and filtering all the windows
# in the current desktop
#
# TWEAK VARIABLES
REFRESH_RATE = 0.01 # seconds
LOCKFILE = "/tmp/gnome_focus_automation_lockfile"
history_desktop_last_active_window = {}
def main():
global history_desktop_last_active_window
# This SCRIPT can't run duplicated on our system
# So let's create a lockfile to prevent that
create_lockfile_or_quit()
current_desktop = get_desktop()
while True:
time.sleep(REFRESH_RATE)
update_window_history()
previous_desktop = current_desktop
current_desktop = get_desktop()
did_desktop_change = current_desktop != previous_desktop
if did_desktop_change:
focus_best_window(current_desktop)
def update_window_history():
global history_desktop_last_active_window
active_window = get_active_window()
active_w_desktop = get_desktop_for_window(active_window)
if active_w_desktop != "":
history_desktop_last_active_window[active_w_desktop] = active_window
def focus_best_window(current_desktop):
global history_desktop_last_active_window
if current_desktop in history_desktop_last_active_window:
target_window = history_desktop_last_active_window[current_desktop]
is_target_valid = get_desktop_for_window(target_window) == current_desktop
if is_target_valid:
did_history_focus_succeded = try_activate_window(target_window)
else:
did_history_focus_succeded = False
else:
did_history_focus_succeded = False
if not did_history_focus_succeded:
# Fallback Focus
topmost_window = unreliably_get_topmost_window_of_desktop(current_desktop)
if topmost_window != "":
try_activate_window(topmost_window)
# ----------------------------------------
# LOCKFILE helper functions
# Make sure the lock file is removed when we exit and when we receive a signal
def cleanup():
os.unlink(LOCKFILE)
# If lockfile exists, then exit
def create_lockfile_or_quit():
if os.path.exists(LOCKFILE):
pid = open(LOCKFILE).read()
try:
os.kill(int(pid), 0)
print("Already running...")
print("")
print("If you are sure that's not the case you can manually delete")
print(f"the lockfile located at {LOCKFILE} and try again.")
sys.exit()
except OSError:
pass
atexit.register(cleanup)
signal.signal(signal.SIGINT, lambda signal, frame: sys.exit(0))
signal.signal(signal.SIGTERM, lambda signal, frame: sys.exit(0))
with open(LOCKFILE, "w") as f:
f.write(str(os.getpid()))
# ---------------------------------------------
# xdotool helper FUNCTIONS
def run_command_that_might_fail(command, fail_return_value):
try:
process = subprocess.Popen(
command, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL
)
stdout, _ = process.communicate()
return stdout.decode().strip()
except subprocess.CalledProcessError:
return fail_return_value
def get_desktop():
return run_command_that_might_fail(["xdotool", "get_desktop"], fail_return_value=-1)
def get_active_window():
return run_command_that_might_fail(
["xdotool", "getactivewindow"], fail_return_value=-1
)
def get_desktop_for_window(window):
return run_command_that_might_fail(
["xdotool", "get_desktop_for_window", window], fail_return_value=""
)
def unreliably_get_topmost_window_of_desktop(desktop):
# The output from "xdotool search" is reverse-ordered according to how windows appear on the screen
# So getting the last window from the current desktop is the way to go
# This sometimes doesn't work and a window that is on the bottom appears on the top
return (
run_command_that_might_fail(
["xdotool", "search", "--desktop", str(desktop), "."], fail_return_value=""
)
.strip()
.split("\n")[-1]
)
def try_activate_window(window):
output = run_command_that_might_fail(
["xdotool", "windowactivate", window], fail_return_value=-1
)
# Boolean return
return output != -1
if __name__ == "__main__":
main()
将此脚本保存在系统中的某个位置,并使用以下命令使其可执行
chmod +x {path_to_your_script}
现在您只需让它在您登录时自动启动即可。为此,请创建一个gnome_focus_automation.desktop
位于的文件,~/.config/autostart
其内容如下:
[Desktop Entry]
Type=Application
Exec=/home/username/path/to/your/script.py
Hidden=false
NoDisplay=false
X-GNOME-Autostart-enabled=true
Name=GnomeFocusAutomation
Comment=Everytime the workspace changes, the focus is corrected to the topmost window
重要的:/home/username/path/to/your/script.py
用脚本的完整路径替换。不要将路径替换为~
类似~/path/to/your/script.py
完毕!
您应该注销并重新登录才能查看效果。
改进空间
目前,我的脚本每 0.01 秒检查一次工作区是否发生变化并采取相应措施...在我的实验中,这个数字对我来说很好,但我知道按时间间隔做出反应并不理想,最好在系统中进行回调以触发操作。
归根结底,这是一个“黑客”解决方案。如果有人有更优雅的解决方案,请评论...