16
16
import time
17
17
import Xlib
18
18
import Xlib .display
19
+ from Xlib .error import DisplayError
20
+ import logging
21
+ from subprocess import run , PIPE
22
+
23
+ logger = logging .getLogger (__name__ )
19
24
20
25
21
26
class Connection :
@@ -49,20 +54,11 @@ class Connection:
49
54
_struct_header_size = struct .calcsize (_struct_header )
50
55
51
56
def __init__ (self , socket_path = None , auto_reconnect = False ):
52
- if not socket_path :
53
- socket_path = os .environ .get ("I3SOCK" )
54
-
55
- if not socket_path :
56
- socket_path = os .environ .get ("SWAYSOCK" )
57
57
58
- if not socket_path :
59
- try :
60
- disp = Xlib .display .Display ()
61
- root = disp .screen ().root
62
- i3atom = disp .intern_atom ("I3_SOCKET_PATH" )
63
- socket_path = root .get_full_property (i3atom , Xlib .X .AnyPropertyType ).value .decode ()
64
- except Exception :
65
- pass
58
+ if socket_path :
59
+ logger .info ('using user provided socket path: %s' , socket_path )
60
+ else :
61
+ socket_path = self ._find_socket_path ()
66
62
67
63
if not socket_path :
68
64
raise Exception ('Failed to retrieve the i3 or sway IPC socket path' )
@@ -79,6 +75,50 @@ def __init__(self, socket_path=None, auto_reconnect=False):
79
75
self ._quitting = False
80
76
self ._synchronizer = None
81
77
78
+ def _find_socket_path (self ):
79
+ socket_path = os .environ .get ("I3SOCK" )
80
+ if socket_path :
81
+ logger .info ('got socket path from I3SOCK env variable: %s' , socket_path )
82
+ return socket_path
83
+
84
+ socket_path = os .environ .get ("SWAYSOCK" )
85
+ if socket_path :
86
+ logger .info ('got socket path from SWAYSOCK env variable: %s' , socket_path )
87
+ return socket_path
88
+
89
+ try :
90
+ disp = Xlib .display .Display ()
91
+ root = disp .screen ().root
92
+ i3atom = disp .intern_atom ("I3_SOCKET_PATH" )
93
+ prop = root .get_full_property (i3atom , Xlib .X .AnyPropertyType )
94
+ if prop and prop .value :
95
+ socket_path = prop .value .decode ()
96
+ except DisplayError as e :
97
+ logger .info ('could not get i3 socket path from root atom' , exc_info = e )
98
+
99
+ if socket_path :
100
+ logger .info ('got socket path from root atom: %s' , socket_path )
101
+ return socket_path
102
+
103
+ for binary in ('i3' , 'sway' ):
104
+ try :
105
+ process = run ([binary , '--get-socketpath' ], stdout = PIPE , stderr = PIPE )
106
+ if process .returncode == 0 and process .stdout :
107
+ socket_path = process .stdout .decode ().strip ()
108
+ logger .info ('got socket path from `%s` binary: %s' , binary , socket_path )
109
+ return socket_path
110
+ else :
111
+ logger .info (
112
+ 'could not get socket path from `%s` binary: returncode=%d, stdout=%s, stderr=%s' ,
113
+ process .returncode , process .stdout , process .stderr )
114
+
115
+ except Exception as e :
116
+ logger .info ('could not get i3 socket path from `%s` binary' , binary , exc_info = e )
117
+ continue
118
+
119
+ logger .info ('could not find i3 socket path' )
120
+ return None
121
+
82
122
def _sync (self ):
83
123
if self ._synchronizer is None :
84
124
self ._synchronizer = Synchronizer ()
@@ -129,19 +169,23 @@ def _ipc_recv(self, sock):
129
169
data = sock .recv (14 )
130
170
131
171
if len (data ) == 0 :
132
- # EOF
172
+ logger . info ( 'got EOF from ipc socket' )
133
173
return '' , 0
134
174
135
175
msg_magic , msg_length , msg_type = self ._unpack_header (data )
176
+ logger .info ('reading ipc message: type=%s, length=%s' , msg_type , msg_length )
136
177
msg_size = self ._struct_header_size + msg_length
137
178
while len (data ) < msg_size :
138
179
data += sock .recv (msg_length )
139
- return self ._unpack (data ), msg_type
180
+ payload = self ._unpack (data )
181
+ logger .info ('message payload: %s' , payload )
182
+ return payload , msg_type
140
183
141
184
def _ipc_send (self , sock , message_type , payload ):
142
185
"""Send and receive a message from the ipc. NOTE: this is not thread
143
186
safe
144
187
"""
188
+ logger .info ('sending to ipc socket: type=%s, payload=%s' , message_type , payload )
145
189
sock .sendall (self ._pack (message_type , payload ))
146
190
data , msg_type = self ._ipc_recv (sock )
147
191
return data
@@ -165,8 +209,10 @@ def _message(self, message_type, payload):
165
209
if not self .auto_reconnect :
166
210
raise e
167
211
212
+ logger .info ('got a connection error, reconnecting' , exc_info = e )
168
213
# XXX: can the socket path change between restarts?
169
214
if not self ._wait_for_socket ():
215
+ logger .info ('could not reconnect' )
170
216
raise e
171
217
172
218
self ._cmd_socket = socket .socket (socket .AF_UNIX , socket .SOCK_STREAM )
@@ -427,10 +473,11 @@ def _event_socket_poll(self):
427
473
if self ._sub_socket is None :
428
474
return True
429
475
476
+ logger .info ('getting ipc event from subscription socket' )
430
477
data , msg_type = self ._ipc_recv (self ._sub_socket )
431
478
432
479
if len (data ) == 0 :
433
- # EOF
480
+ logger . info ( 'subscription socket got EOF, shutting down' )
434
481
self ._pubsub .emit ('ipc_shutdown' , None )
435
482
return True
436
483
@@ -486,6 +533,8 @@ def main(self, timeout: float = 0.0):
486
533
self ._quitting = False
487
534
timer = None
488
535
536
+ logger .info ('starting the main loop' )
537
+
489
538
while True :
490
539
try :
491
540
self ._event_socket_setup ()
@@ -515,5 +564,6 @@ def main(self, timeout: float = 0.0):
515
564
516
565
def main_quit (self ):
517
566
"""Quits the running main loop for this connection."""
567
+ logger .info ('shutting down the main loop' )
518
568
self ._quitting = True
519
569
self ._event_socket_teardown ()
0 commit comments