summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUltrabug <ultrabug@gentoo.org>2014-12-04 19:47:19 +0100
committerUltrabug <ultrabug@gentoo.org>2014-12-04 19:47:19 +0100
commit017e8b277c27aab56d582baaeefd6bdc8409dce4 (patch)
tree52b217acfdc3d2043d826fdf2d6be8cfb14e888b
parentef684aa27f4dc4d82f9814c7e1f84dde3a9549a6 (diff)
add handling of a special on_click parameter in i3status.conf allowing to send i3 messages and execute special py3status actions
-rwxr-xr-xpy3status/__init__.py118
1 files changed, 107 insertions, 11 deletions
diff --git a/py3status/__init__.py b/py3status/__init__.py
index 973dede..49bd97e 100755
--- a/py3status/__init__.py
+++ b/py3status/__init__.py
@@ -132,6 +132,7 @@ class I3status(Thread):
'output_format': 'i3bar'
},
'i3s_modules': [],
+ 'on_click': {},
'order': [],
'py3_modules': []
}
@@ -184,7 +185,30 @@ class I3status(Thread):
else:
config['i3s_modules'].append(value)
else:
- config[section_name][key] = value
+ if not key.startswith('on_click'):
+ config[section_name][key] = value
+ else:
+ # on_click special parameters
+ try:
+ button = int(key.split()[1])
+ if button not in range(1, 6):
+ raise ValueError(
+ 'should be 1, 2, 3, 4 or 5'
+ )
+ except IndexError as e:
+ raise IndexError(
+ 'missing "button id" for "on_click" '
+ 'parameter in section {}'.format(section_name)
+ )
+ except ValueError as e:
+ raise ValueError(
+ 'invalid "button id" '
+ 'for "on_click" parameter '
+ 'in section {} ({})'.format(section_name, e)
+ )
+ on_c = config['on_click']
+ on_c[section_name] = on_c.get(section_name, {})
+ on_c[section_name][button] = value
# override time format
if section_name in ['time', 'tztime'] and key == 'format':
@@ -372,15 +396,16 @@ class Events(Thread):
"""
This class is responsible for dispatching event JSONs sent by the i3bar.
"""
- def __init__(self, lock, config, modules):
+ def __init__(self, lock, config, modules, on_click):
"""
We need to poll stdin to receive i3bar messages.
"""
Thread.__init__(self)
self.config = config
self.lock = lock
- self.modules = modules.values()
+ self.modules = modules
self.poller_inp = IOPoller(sys.stdin)
+ self.on_click = on_click
def dispatch(self, module, obj, event):
"""
@@ -405,7 +430,7 @@ class Events(Thread):
it will dispatch i3status click events to this module so you can catch
them and trigger any function call based on the event.
"""
- for module in self.modules:
+ for module in self.modules.values():
if not module.click_events:
continue
if module.module_name == 'i3bar_click_events.py':
@@ -413,6 +438,57 @@ class Events(Thread):
else:
return False
+ def refresh(self, module_name):
+ """
+ Force a cache expiration for all the methods of the given module.
+ """
+ module = self.modules.get(module_name)
+ if module is not None:
+ if self.config['debug']:
+ syslog(LOG_INFO, 'refresh module {}'.format(module_name))
+ for obj in module.methods.values():
+ obj['cached_until'] = time()
+
+ @staticmethod
+ def refresh_all(module_name):
+ """
+ Force a full refresh of py3status and i3status modules by sending
+ a SIGUSR1 signal to py3status.
+ """
+ Popen(['killall', '-USR1', 'py3status'])
+
+ def on_click_dispatcher(self, module_name, command):
+ """
+ Dispatch on_click config parameters to either:
+ - Our own methods for special py3status commands (listed below)
+ - The i3-msg program which is part of i3wm
+ """
+ py3_commands = ['refresh', 'refresh_all']
+ if command is None:
+ return
+ elif command in py3_commands:
+ # this is a py3status command handled by this class
+ method = getattr(self, command)
+ method(module_name)
+ else:
+ # this is a i3 message
+ self.i3_msg(module_name, command)
+
+ @staticmethod
+ def i3_msg(module_name, command):
+ """
+ Execute the given i3 message and log its output.
+ """
+ i3_msg_pipe = Popen(['i3-msg', command], stdout=PIPE)
+ syslog(
+ LOG_INFO,
+ 'i3-msg module={} command="{}" stdout={}'.format(
+ module_name,
+ command,
+ i3_msg_pipe.stdout.read()
+ )
+ )
+
def run(self):
"""
Wait for an i3bar JSON event, then find the right module to dispatch
@@ -435,13 +511,28 @@ class Events(Thread):
if self.config['debug']:
syslog(LOG_INFO, 'received event {}'.format(event))
- # setup default action on button 2 press
+ # usage variables
+ button = event.get('button', 0)
default_event = False
dispatched = False
- if 'button' in event and event['button'] == 2:
+ instance = event.get('instance', '')
+ name = event.get('name', '')
+
+ # guess the module config name
+ module_name = '{} {}'.format(name, instance).strip()
+
+ # execute any configured i3-msg command
+ if self.on_click.get(module_name, {}).get(button):
+ self.on_click_dispatcher(
+ module_name,
+ self.on_click[module_name].get(button)
+ )
+ dispatched = True
+ # otherwise setup default action on button 2 press
+ elif button == 2:
default_event = True
- for module in self.modules:
+ for module in self.modules.values():
# skip modules not supporting click_events
# unless we have a default_event set
if not module.click_events and not default_event:
@@ -449,9 +540,9 @@ class Events(Thread):
# check for the method name/instance
for obj in module.methods.values():
- if event['name'] == obj['name']:
- if 'instance' in event:
- if event['instance'] == obj['instance']:
+ if name == obj['name']:
+ if instance:
+ if instance == obj['instance']:
self.dispatch(module, obj, event)
dispatched = True
break
@@ -869,7 +960,12 @@ class Py3statusWrapper():
)
# setup input events thread
- self.events_thread = Events(self.lock, self.config, self.modules)
+ self.events_thread = Events(
+ self.lock,
+ self.config,
+ self.modules,
+ self.i3status_thread.config['on_click']
+ )
self.events_thread.start()
if self.config['debug']:
syslog(LOG_INFO, 'events thread started')