-
Notifications
You must be signed in to change notification settings - Fork 0
/
focus-last.py
executable file
·105 lines (88 loc) · 3.51 KB
/
focus-last.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#!/usr/bin/env python3
import os
import socket
import selectors
import tempfile
import threading
from argparse import ArgumentParser
import i3ipc
SOCKET_DIR = '{}/i3_focus_last.{}{}'.format(tempfile.gettempdir(), os.geteuid(),
os.getenv("DISPLAY"))
SOCKET_FILE = '{}/socket'.format(SOCKET_DIR)
MAX_WIN_HISTORY = 15
class FocusWatcher:
def __init__(self):
self.i3 = i3ipc.Connection()
self.i3.on('window::focus', self.on_window_focus)
# Make a directory with permissions that restrict access to
# the user only.
os.makedirs(SOCKET_DIR, mode=0o700, exist_ok=True)
self.listening_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
if os.path.exists(SOCKET_FILE):
os.remove(SOCKET_FILE)
self.listening_socket.bind(SOCKET_FILE)
self.listening_socket.listen(1)
self.window_list = []
self.window_list_lock = threading.RLock()
def on_window_focus(self, i3conn, event):
with self.window_list_lock:
window_id = event.container.id
if window_id in self.window_list:
self.window_list.remove(window_id)
self.window_list.insert(0, window_id)
if len(self.window_list) > MAX_WIN_HISTORY:
del self.window_list[MAX_WIN_HISTORY:]
def launch_i3(self):
self.i3.main()
def launch_server(self):
selector = selectors.DefaultSelector()
def accept(sock):
conn, addr = sock.accept()
selector.register(conn, selectors.EVENT_READ, read)
def read(conn):
data = conn.recv(1024)
if data == b'switch':
with self.window_list_lock:
tree = self.i3.get_tree()
windows = set(w.id for w in tree.leaves())
for window_id in self.window_list[1:]:
if window_id not in windows:
self.window_list.remove(window_id)
else:
self.i3.command('[con_id=%s] focus' % window_id)
break
elif not data:
selector.unregister(conn)
conn.close()
selector.register(self.listening_socket, selectors.EVENT_READ, accept)
while True:
for key, event in selector.select():
callback = key.data
callback(key.fileobj)
def run(self):
t_i3 = threading.Thread(target=self.launch_i3)
t_server = threading.Thread(target=self.launch_server)
for t in (t_i3, t_server):
t.start()
if __name__ == '__main__':
parser = ArgumentParser(prog='focus-last.py',
description='''
Focus last focused window.
This script should be launch from the .xsessionrc without argument.
Then you can bind this script with the `--switch` option to one of your
i3 keybinding.
''')
parser.add_argument('--switch',
dest='switch',
action='store_true',
help='Switch to the previous window',
default=False)
args = parser.parse_args()
if not args.switch:
focus_watcher = FocusWatcher()
focus_watcher.run()
else:
client_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
client_socket.connect(SOCKET_FILE)
client_socket.send(b'switch')
client_socket.close()