[core/input] Add callback deregistration

Enable components to unregister callbacks (i.e. for dynamic widgets).

see #23
This commit is contained in:
Tobi-wan Kenobi 2016-12-10 10:26:07 +01:00
parent 761b81970d
commit 918d7a6046
3 changed files with 51 additions and 3 deletions

View file

@ -108,7 +108,7 @@ class Engine(object):
self._output.flush() self._output.flush()
self._output.end() self._output.end()
if self.running(): if self.running():
time.sleep(1) self.input.wait(self._config.get("interval", 1))
self._output.stop() self._output.stop()
self.input.stop() self.input.stop()

View file

@ -18,6 +18,7 @@ def read_input(inp):
try: try:
event = json.loads(line) event = json.loads(line)
inp.callback(event) inp.callback(event)
inp.redraw()
except ValueError: except ValueError:
pass pass
inp.has_event = True inp.has_event = True
@ -32,18 +33,28 @@ class I3BarInput(object):
self.global_id = str(uuid.uuid4()) self.global_id = str(uuid.uuid4())
self.need_event = False self.need_event = False
self.has_event = False self.has_event = False
self._condition = threading.Condition()
def start(self): def start(self):
"""Start asynchronous input processing""" """Start asynchronous input processing"""
self.has_event = False self.has_event = False
self.running = True self.running = True
self._condition.acquire()
self._thread = threading.Thread(target=read_input, args=(self,)) self._thread = threading.Thread(target=read_input, args=(self,))
self._thread.start() self._thread.start()
def redraw(self):
self._condition.acquire()
self._condition.notify()
self._condition.release()
def alive(self): def alive(self):
"""Check whether the input processing is still active""" """Check whether the input processing is still active"""
return self._thread.is_alive() return self._thread.is_alive()
def wait(self, timeout):
self._condition.wait(timeout)
def _wait(self): def _wait(self):
while not self.has_event: while not self.has_event:
time.sleep(0.1) time.sleep(0.1)
@ -51,18 +62,27 @@ class I3BarInput(object):
def stop(self): def stop(self):
"""Stop asynchronous input processing""" """Stop asynchronous input processing"""
self._condition.release()
if self.need_event: if self.need_event:
self._wait() self._wait()
self.running = False self.running = False
self._thread.join() self._thread.join()
return self.clean_exit return self.clean_exit
def register_callback(self, obj, button, cmd): def _uid(self, obj):
"""Register a callback function or system call"""
uid = self.global_id uid = self.global_id
if obj: if obj:
uid = obj.id uid = obj.id
return uid
def deregister_callbacks(self, obj):
uid = self._uid(obj)
if uid in self._callbacks:
del self._callbacks[uid]
def register_callback(self, obj, button, cmd):
"""Register a callback function or system call"""
uid = self._uid(obj)
if uid not in self._callbacks: if uid not in self._callbacks:
self._callbacks[uid] = {} self._callbacks[uid] = {}
self._callbacks[uid][button] = cmd self._callbacks[uid][button] = cmd

View file

@ -61,6 +61,20 @@ class TestI3BarInput(unittest.TestCase):
mock_input.readline.assert_any_call() mock_input.readline.assert_any_call()
self.assertTrue(self._called > 0) self.assertTrue(self._called > 0)
@mock.patch("sys.stdin")
def test_remove_global_callback(self, mock_input):
mock_input.readline.return_value = json.dumps({
"name": "somename",
"instance": "someinstance",
"button": bumblebee.input.LEFT_MOUSE,
})
self.input.register_callback(None, button=1, cmd=self.callback)
self.input.deregister_callbacks(None)
self.input.start()
self.assertEquals(self.input.stop(), True)
mock_input.readline.assert_any_call()
self.assertTrue(self._called == 0)
@mock.patch("sys.stdin") @mock.patch("sys.stdin")
def test_global_callback_button_missmatch(self, mock_input): def test_global_callback_button_missmatch(self, mock_input):
mock_input.readline.return_value = json.dumps({ mock_input.readline.return_value = json.dumps({
@ -87,6 +101,20 @@ class TestI3BarInput(unittest.TestCase):
mock_input.readline.assert_any_call() mock_input.readline.assert_any_call()
self.assertTrue(self._called > 0) self.assertTrue(self._called > 0)
@mock.patch("sys.stdin")
def test_remove_module_callback(self, mock_input):
mock_input.readline.return_value = json.dumps({
"name": self.anyModule.id,
"instance": None,
"button": bumblebee.input.LEFT_MOUSE,
})
self.input.register_callback(self.anyModule, button=1, cmd=self.callback)
self.input.deregister_callbacks(self.anyModule)
self.input.start()
self.assertEquals(self.input.stop(), True)
mock_input.readline.assert_any_call()
self.assertTrue(self._called == 0)
@mock.patch("sys.stdin") @mock.patch("sys.stdin")
def test_widget_callback(self, mock_input): def test_widget_callback(self, mock_input):
mock_input.readline.return_value = json.dumps({ mock_input.readline.return_value = json.dumps({