async def monitor_start(self): """ Start watching for device changes Listen for relevant add/remove events from udev and fire callbacks. """ if self._monitor: return udev_monitor = Monitor.from_netlink(self._udev_context) udev_monitor.filter_by_tag('uchroma') udev_monitor.filter_by(subsystem='usb', device_type=u'usb_device') self._udev_observer = AsyncMonitorObserver(udev_monitor, callback=self._udev_event, name='uchroma-monitor') ensure_future(self._udev_observer.start()) self._monitor = True if self._callbacks: for device in self._devices.values(): ensure_future(self._fire_callbacks('add', device), loop=self._loop) self._logger.debug('Udev monitor started')
def _run(self): dm = UChromaDeviceManager() atexit.register(UChromaServer.exit, self._loop) dbus = DeviceManagerAPI(dm, self._logger) power = PowerMonitor() for sig in (signal.SIGINT, signal.SIGTERM): self._loop.add_signal_handler(sig, self._shutdown_callback) try: dbus.run() power.start() ensure_future(dm.monitor_start(), loop=self._loop) self._loop.run_forever() except KeyboardInterrupt: pass finally: for sig in (signal.SIGTERM, signal.SIGINT): self._loop.remove_signal_handler(sig) power.stop() self._loop.run_until_complete(asyncio.wait( \ [dm.close_devices(), dm.monitor_stop()], return_when=futures.ALL_COMPLETED))
def discover(self): """ Perform HID device discovery Iterates over all connected HID devices with RAZER_VENDOR_ID and checks the product ID against the Hardware descriptor. Interface endpoint restrictions are currently hard-coded. In the future this should be done by checking the HID report descriptor of the endpoint, however this functionality in HIDAPI is broken (report_descriptor returns garbage) on Linux in the current release. Discovery is automatically performed when the object is constructed, so this should only need to be called if the list of devices changes (monitoring for changes is beyond the scope of this API). """ devinfos = sorted(list(hidapi.enumerate(vendor_id=RAZER_VENDOR_ID)), key=lambda x: x.path) for devinfo in devinfos: parent = self._get_parent(devinfo.product_id) if parent is None: continue if self._key_for_path(parent.sys_path) is not None: continue hardware = Hardware.get_device(devinfo.product_id) if hardware is None: continue if hardware.type == Hardware.Type.HEADSET: if devinfo.interface_number != 3: continue elif hardware.type in (Hardware.Type.KEYBOARD, Hardware.Type.KEYPAD, Hardware.Type.LAPTOP): if devinfo.interface_number != 2: continue elif hardware.type in (Hardware.Type.MOUSE, Hardware.Type.MOUSEPAD): if devinfo.interface_number != 1: continue else: if devinfo.interface_number != 0: continue device = self._create_device(parent, hardware, devinfo) if device is not None: self._devices[device.key] = device if hardware.type == Hardware.Type.KEYBOARD: device.set_device_mode(0) if self._monitor and self._callbacks: ensure_future(self._fire_callbacks('add', device), loop=self._loop)
def stop(self): if not self._running: return self._running = False self._task.cancel() self._driver.input_manager.grab(False) ensure_future(self._queue.detach())
def remove_renderer(self, zindex: int) -> bool: if self._loop is None: return False if zindex is None or zindex < 0 or zindex > len(self._loop.layers): self._logger.error("Z-index out of range (requested %d max %d)", zindex, len(self._loop.layers)) return False ensure_future(self._loop.remove_layer(zindex)) return True
def start(self) -> bool: """ Start the AnimationLoop Initializes the renderers, zeros the buffers, and starts the loop. Requires an active asyncio event loop. :return: True if the loop was started """ if self.running: self._logger.error("Animation loop already running") return False if len(self.layers) == 0: self._logger.error("No renderers were configured") return False self._error = False self.running = True self._anim_task = ensure_future(self._animate()) self._anim_task.add_done_callback(self._renderer_done) return True
async def _get_layers(self): """ Wait for renderers to produce new layers, yields until at least one layer is active. """ # schedule tasks to wait on each renderer queue for r_idx in range(0, len(self.layers)): layer = self.layers[r_idx] if layer.waiter is None or layer.waiter.done(): layer.waiter = ensure_future(self._dequeue(r_idx)) # async wait for at least one completion waiters = [layer.waiter for layer in self.layers] if len(waiters) == 0: return await asyncio.wait(waiters, return_when=futures.FIRST_COMPLETED) # check the rest without waiting for r_idx in range(0, len(self.layers)): layer = self.layers[r_idx] if layer.waiter is not None and not layer.waiter.done(): self._dequeue_nowait(r_idx)
def _udev_event(self, device): self._logger.debug('Device event [%s]: %s', device.action, device) if device.action == 'remove': key = self._key_for_path(device.sys_path) if key is not None: removed = self._devices.pop(key, None) if removed is not None: removed.close() if self._callbacks: ensure_future( \ self._fire_callbacks('remove', removed), loop=self._loop) else: if self._key_for_path(device.sys_path) is None: self.discover()
def _open_input_devices(self): if self._opened: return True for input_device in self._input_devices: try: event_device = evdev.InputDevice(input_device) self._event_devices.append(event_device) task = ensure_future(self._evdev_callback(event_device)) task.add_done_callback( functools.partial(self._evdev_close, event_device)) self._tasks.append(task) self._logger.info('Opened event device %s', event_device) except Exception as err: self._logger.exception("Failed to open device: %s", input_device, exc_info=err) if len(self._event_devices) > 0: self._opened = True return self._opened
def stop(self, cb=None): if not self.running: return False task = ensure_future(self._stop()) if cb is not None: task.add_done_callback(cb) return True
def InputSignalEnabled(self, state): if dev_mode_enabled(): if state == self._signal_input: return if state: if self._input_queue is None: self._input_queue = InputQueue(self._driver) self._input_queue.attach() self._input_task = ensure_future(self._dev_mode_input()) else: ensure_future(self._input_queue.detach()) self._input_task.cancel() self._input_queue = None self._signal_input = state
def start(self): if self._running: return self._uinput = UInput.from_device(driver.input_manager.input_devices[0], \ name='UChroma Virtual Macro Device %d' % driver.device_index) self._queue = InputQueue(driver) self._queue.attach() self._driver.input_manager.grab(True) self._task = ensure_future(self._listen()) self._running = True
def start(self): if not self.renderer.running: self.task = ensure_future(self.renderer._run())