Beispiel #1
0
class ScanDevice(object):
    """
    Input device manager class.

    Manages input devices on the system and deals with
    PnP related event. The actual press/release events
    are forwarded to a ScanMode instance.
    """
    """ Default device name (virtual core pointer) """
    DEFAULT_NAME = "Default"
    """ Device id's of the primary masters """
    DEFAULT_VCP_ID = 2
    DEFAULT_VCK_ID = 3
    """ Device name blacklist """
    blacklist = [
        "Virtual core pointer", "Virtual core keyboard",
        "Virtual core XTEST pointer", "Virtual core XTEST keyboard",
        "Power Button"
    ]

    def __init__(self, event_handler):
        logger.debug("ScanDevice.__init__()")
        """ Selected device tuple (device id, master id) """
        self._active_device_ids = None
        """ Whether the active device is detached """
        self._floating = False
        """ Event handler for device events """
        self._event_handler = event_handler
        """ The manager for osk XInput devices """
        self._device_manager = XIDeviceManager()  # singleton
        self._device_manager.connect("device-event",
                                     self._device_event_handler)

        config.scanner.device_name_notify_add(self._device_name_notify)
        config.scanner.device_detach_notify_add(self._device_detach_notify)

        self._device_name_notify(config.scanner.device_name)

    def __del__(self):
        logger.debug("ScanDevice.__del__()")

    def _device_event_handler(self, event):
        """
        Handler for XI2 events.
        """
        event_type = event.xi_type
        device_id = event.device_id

        if event_type == XIEventType.DeviceAdded:
            device = self._device_manager.lookup_device_id(device_id)
            show_new_device_dialog(device.name, device.get_config_string(),
                                   device.is_pointer(),
                                   self._on_new_device_accepted)

        elif event_type == XIEventType.DeviceRemoved:
            # If we are currently using this device,
            # close it and fall back to 'Default'
            if self._active_device_ids and \
               self._active_device_ids[0] == device_id:
                self._active_device_ids = None
                self._floating = False
                config.scanner.device_detach = False
                config.scanner.device_name = self.DEFAULT_NAME

        else:
            # Never handle VCK events.
            if device_id != self.DEFAULT_VCK_ID:
                # Forward VCP events only if 'Default' is selected.
                # Else only handle devices we selected.
                if (device_id == self.DEFAULT_VCP_ID and \
                    config.scanner.device_name == self.DEFAULT_NAME) or \
                   (self._active_device_ids and \
                    device_id == self._active_device_ids[0]):

                    self._event_handler(event)

    def _on_new_device_accepted(self, config_string):
        """
        Callback for the 'New device' dialog.
        Called only if 'Use device' was chosen.
        """
        config.scanner.device_name = config_string
        config.scanner.device_detach = True

    def _device_detach_notify(self, detach):
        """
        Callback for the scanner.device_detach configuration changes.
        """
        if self._active_device_ids is None:
            return

        if detach:
            if not self._floating:
                self.detach(self._active_device_ids[0])
        else:
            if self._floating:
                self.attach(*self._active_device_ids)

    def _device_name_notify(self, name):
        """
        Callback for the scanner.device_name configuration changes.
        """
        self.close()

        if name == self.DEFAULT_NAME:
            return

        for device in self._device_manager.get_devices():
            if self.is_useable(device) and \
               name == device.get_config_string():
                self.open(device)
                break

        if self._active_device_ids is None:
            logger.debug("Unknown device-name in configuration.")
            config.scanner.device_detach = False
            config.scanner.device_name = self.DEFAULT_NAME

    def open(self, device):
        """
        Select for events and optionally detach the device.
        """
        if device.is_pointer():
            event_mask = XIEventMask.ButtonPressMask | \
                         XIEventMask.ButtonReleaseMask
        else:
            event_mask = XIEventMask.KeyPressMask | \
                         XIEventMask.KeyReleaseMask
        try:
            self._device_manager.select_events(None, device, event_mask)
            self._active_device_ids = (device.id, device.master)
        except Exception as ex:
            logger.warning("Failed to open device {id}: {ex}".format(
                id=device.id, ex=ex))

        if config.scanner.device_detach and not device.is_master():
            self.detach(device.id)

    def close(self):
        """
        Stop using the current device.
        """
        if self._floating:
            self.attach(*self._active_device_ids)

        if self._active_device_ids:
            device = self._device_manager.lookup_device_id( \
                                            self._active_device_ids[0])
            try:
                self._device_manager.unselect_events(None, device)
                self._active_device_ids = None
            except Exception as ex:
                logger.warning("Failed to close device {id}: {ex}".format(
                    id=self._active_device_ids[0], ex=ex))

    def attach(self, dev_id, master):
        """
        Attach the device to a master.
        """
        try:
            self._device_manager.attach_device_id(dev_id, master)
            self._floating = False
        except:
            logger.warning("Failed to attach device {id} to {master}".format(
                id=dev_id, master=master))

    def detach(self, dev_id):
        """
        Detach the device from its master.
        """
        try:
            self._device_manager.detach_device_id(dev_id)
            self._floating = True
        except:
            logger.warning("Failed to detach device {id}".format(id=dev_id))

    def finalize(self):
        """
        Clean up the ScanDevice instance.
        """
        self._device_manager.disconnect("device-event",
                                        self._device_event_handler)
        config.scanner.device_name_notify_remove(self._device_name_notify)
        config.scanner.device_detach_notify_remove(self._device_detach_notify)
        self.close()
        self._event_handler = None
        self.devices = None

    @staticmethod
    def is_useable(device):
        """
        Check whether this device is useable for scanning.
        """
        return device.name not in ScanDevice.blacklist \
               and device.enabled \
               and not device.is_floating()
class CSFloatingSlave(ClickSimulator):
    """
    Onboards built-in mouse click mapper.
    Maps secondary or middle button to the primary button.
    """
    def __init__(self, keyboard):
        ClickSimulator.__init__(self)

        self._keyboard = keyboard

        self._device_manager = XIDeviceManager()
        self._grabbed_device_ids = []
        self._num_clicks_detected = 0
        self._motion_position = None

        self._button = self.PRIMARY_BUTTON
        self._click_type = self.CLICK_TYPE_SINGLE

        self._click_done_notify_callbacks = []
        self._exclusion_rects = []

        self._osk_cm = osk.ClickMapper()

    def cleanup(self):
        self.end_mapping()
        self._click_done_notify_callbacks = []

    def is_valid(self):
        return self._device_manager.is_valid()

    def supports_click_params(self, button, click_type):
        return True

    def map_primary_click(self, event_source, button, click_type):
        if event_source and (button != self.PRIMARY_BUTTON or \
                             click_type != self.CLICK_TYPE_SINGLE):
            self._begin_mapping(event_source, button, click_type)
        else:
            self.end_mapping()

    def _begin_mapping(self, event_source, button, click_type):
        click_device = self._device_manager.get_last_click_device()
        motion_device = self._device_manager.get_last_motion_device()

        if self._register_xinput_events(click_device, motion_device):
            self._button = button
            self._click_type = click_type
            self._num_clicks_detected = 0
            self._motion_position = None

    def end_mapping(self):
        self._deregister_xinput_events()
        self._button = self.PRIMARY_BUTTON
        self._click_type = self.CLICK_TYPE_SINGLE

    def _register_xinput_events(self, click_device, motion_device):
        success = self._register_xinput_device(click_device, "primary",
                                          XIEventMask.ButtonPressMask | \
                                          XIEventMask.ButtonReleaseMask | \
                                          XIEventMask.MotionMask)

        if success and not motion_device is click_device:
            success = self._register_xinput_device(motion_device, "motion",
                                              XIEventMask.MotionMask)

        if success:
            self._device_manager.connect("device-event",
                                          self._on_device_event)
        else:
            self._deregister_xinput_events()

        return success

    def _register_xinput_device(self, device, description, event_mask):
        _logger.info("grab {} device {}".format(description, device))

        try:
            self._device_manager.grab_device(device)
            self._grabbed_device_ids.append(device.id)
        except osk.error as ex:
            _logger.error("grab device {id} '{name}': {ex}"
                          .format(id = device.id,
                                  name=device.name,
                                  ex = ex))
            return False

        try:
            self._device_manager.select_events(None, device, event_mask)
        except osk.error as ex:
            _logger.error("select root events for device "
                          "{id} '{name}': {ex}"
                          .format(id = device.id,
                                  name=device.name,
                                  ex = ex))
            return False

        return True

    def _deregister_xinput_events(self):
        if self._grabbed_device_ids:
            self._device_manager.disconnect("device-event",
                                            self._on_device_event)

            # unselect and ungrab all devices
            for device_id in self._grabbed_device_ids:
                device = self._device_manager.lookup_device_id(device_id)

                _logger.info("ungrab " + str(device))

                try:
                    self._device_manager.unselect_events(None, device)
                except osk.error as ex:
                    _logger.error("unselect root events for device "
                                  "{id} '{name}': {ex}"
                                  .format(id = device.id,
                                          name=device.name,
                                          ex = ex))

                try:
                    self._device_manager.ungrab_device(device)
                except osk.error as ex:
                    _logger.error("ungrab device {id} '{name}': {ex}"
                                  .format(id = device.id,
                                          name=device.name,
                                          ex = ex))

            self._grabbed_device_ids = []

    def _on_device_event(self, event):
        event_type = event.xi_type
        button = self._button
        click_type = self._click_type
        generate_button_event = self._osk_cm.generate_button_event

        if not event.device_id in self._grabbed_device_ids:
            return

        #print("device event:", event.device_id, event.xi_type, (event.x, event.y), (event.x_root, event.y_root), event.xid_event)

        # Get pointer position from motion events only to support clicking
        # with one device and pointing with another.
        position = self._motion_position
        if event_type == XIEventType.Motion:
            position = (int(event.x_root), int(event.y_root))
            self._motion_position = position

            # tell master pointer about our new position
            self._osk_cm.generate_motion_event(*position)

        if position is None and \
           (event_type == XIEventType.ButtonPress or \
            event_type == XIEventType.ButtonRelease):
            position = (int(event.x_root), int(event.y_root))

        if position is None:
            return

        # single click
        if click_type == self.CLICK_TYPE_SINGLE:
            if event_type == XIEventType.ButtonPress:
                self._keyboard.maybe_send_alt_press(None, button, 0)
                generate_button_event(button, True)

            elif event_type == XIEventType.ButtonRelease:
                generate_button_event(button, False)
                self._keyboard.maybe_send_alt_release(None, button, 0)

                if self._num_clicks_detected and \
                   not self._is_point_in_exclusion_rects(position):
                    self.end_mapped_click()

        # double click
        elif click_type == self.CLICK_TYPE_DOUBLE:
            if event_type == XIEventType.ButtonRelease:
                if self._num_clicks_detected:
                    if not self._is_point_in_exclusion_rects(position):
                        delay = 40
                        self._keyboard.maybe_send_alt_press(None, button, 0)
                        generate_button_event(button, True)
                        generate_button_event(button, False, delay)
                        generate_button_event(button, True, delay)
                        generate_button_event(button, False, delay)
                        self._keyboard.maybe_send_alt_release(None, button, 0)
                    self.end_mapped_click()

        # drag click
        elif click_type == self.CLICK_TYPE_DRAG:
            if event_type == XIEventType.ButtonRelease:
                if self._num_clicks_detected == 1:
                    if self._is_point_in_exclusion_rects(position):
                        self.end_mapped_click()
                    else:
                        self._keyboard.maybe_send_alt_press(None, button, 0)
                        generate_button_event(button, True)

                elif self._num_clicks_detected >= 2:
                    generate_button_event(button, False)
                    self._keyboard.maybe_send_alt_release(None, button, 0)
                    self.end_mapped_click()

        # count button presses
        if event_type == XIEventType.ButtonPress:
            self._num_clicks_detected += 1

    def _is_point_in_exclusion_rects(self, point):
        for rect in self._exclusion_rects:
            if rect.is_point_within(point):
                return True
        return False

    def is_mapping_active(self):
        return bool(self._grabbed_device_ids)

    def get_click_button(self):
        return self._button

    def get_click_type(self):
        return self._click_type

    def state_notify_add(self, callback):
        self._click_done_notify_callbacks.append(callback)

    def end_mapped_click(self):
        """ osk callback, outside click, xi button release """
        if self.is_mapping_active():
            self.end_mapping()

            self._keyboard.release_latched_sticky_keys()

            # update click type buttons
            for callback in self._click_done_notify_callbacks:
                callback(None)

    def set_exclusion_rects(self, rects):
        self._exclusion_rects = rects
Beispiel #3
0
class ScanDevice(object):
    """
    Input device manager class.

    Manages input devices on the system and deals with
    PnP related event. The actual press/release events
    are forwarded to a ScanMode instance.
    """

    """ Default device name (virtual core pointer) """
    DEFAULT_NAME = "Default"

    """ Device id's of the primary masters """
    DEFAULT_VCP_ID = 2
    DEFAULT_VCK_ID = 3

    """ Device name blacklist """
    blacklist = ["Virtual core pointer",
                 "Virtual core keyboard",
                 "Virtual core XTEST pointer",
                 "Virtual core XTEST keyboard",
                 "Power Button"]

    def __init__(self, event_handler):
        logger.debug("ScanDevice.__init__()")

        """ Selected device tuple (device id, master id) """
        self._active_device_ids = None

        """ Whether the active device is detached """
        self._floating = False

        """ Event handler for device events """
        self._event_handler = event_handler

        """ The manager for osk XInput devices """
        self._device_manager = XIDeviceManager()  # singleton
        self._device_manager.connect("device-event", self._device_event_handler)

        config.scanner.device_name_notify_add(self._device_name_notify)
        config.scanner.device_detach_notify_add(self._device_detach_notify)

        self._device_name_notify(config.scanner.device_name)

    def __del__(self):
        logger.debug("ScanDevice.__del__()")

    def _device_event_handler(self, event):
        """
        Handler for XI2 events.
        """
        event_type = event.xi_type
        device_id  = event.device_id

        if event_type == XIEventType.DeviceAdded:
            device = self._device_manager.lookup_device_id(device_id)
            show_new_device_dialog(device.name,
                                   device.get_config_string(),
                                   device.is_pointer(),
                                   self._on_new_device_accepted)

        elif event_type == XIEventType.DeviceRemoved:
            # If we are currently using this device,
            # close it and fall back to 'Default'
            if self._active_device_ids and \
               self._active_device_ids[0] == device_id:
                self._active_device_ids = None
                self._floating = False
                config.scanner.device_detach = False
                config.scanner.device_name = self.DEFAULT_NAME

        else:
            # Never handle VCK events.
            if device_id != self.DEFAULT_VCK_ID:
                # Forward VCP events only if 'Default' is selected.
                # Else only handle devices we selected.
                if (device_id == self.DEFAULT_VCP_ID and \
                    config.scanner.device_name == self.DEFAULT_NAME) or \
                   (self._active_device_ids and \
                    device_id == self._active_device_ids[0]):

                    self._event_handler(event)

    def _on_new_device_accepted(self, config_string):
        """
        Callback for the 'New device' dialog.
        Called only if 'Use device' was chosen.
        """
        config.scanner.device_name = config_string
        config.scanner.device_detach = True

    def _device_detach_notify(self, detach):
        """
        Callback for the scanner.device_detach configuration changes.
        """
        if self._active_device_ids is None:
            return

        if detach:
            if not self._floating:
                self.detach(self._active_device_ids[0])
        else:
            if self._floating:
                self.attach(*self._active_device_ids)

    def _device_name_notify(self, name):
        """
        Callback for the scanner.device_name configuration changes.
        """
        self.close()

        if name == self.DEFAULT_NAME:
            return

        for device in self._device_manager.get_devices():
            if self.is_useable(device) and \
               name == device.get_config_string():
                self.open(device)
                break

        if self._active_device_ids is None:
            logger.debug("Unknown device-name in configuration.")
            config.scanner.device_detach = False
            config.scanner.device_name = self.DEFAULT_NAME

    def open(self, device):
        """
        Select for events and optionally detach the device.
        """
        if device.is_pointer():
            event_mask = XIEventMask.ButtonPressMask | \
                         XIEventMask.ButtonReleaseMask
        else:
            event_mask = XIEventMask.KeyPressMask | \
                         XIEventMask.KeyReleaseMask
        try:
            self._device_manager.select_events(None, device, event_mask)
            self._active_device_ids = (device.id, device.master)
        except Exception as ex:
            logger.warning("Failed to open device {id}: {ex}"
                           .format(id = device.id, ex = ex))

        if config.scanner.device_detach and not device.is_master():
            self.detach(device.id)

    def close(self):
        """
        Stop using the current device.
        """
        if self._floating:
            self.attach(*self._active_device_ids)

        if self._active_device_ids:
            device = self._device_manager.lookup_device_id( \
                                            self._active_device_ids[0])
            try:
                self._device_manager.unselect_events(None, device)
                self._active_device_ids = None
            except Exception as ex:
                logger.warning("Failed to close device {id}: {ex}"
                               .format(id = self._active_device_ids[0],
                                       ex = ex))

    def attach(self, dev_id, master):
        """
        Attach the device to a master.
        """
        try:
            self._device_manager.attach_device_id(dev_id, master)
            self._floating = False
        except:
            logger.warning("Failed to attach device {id} to {master}"
                           .format(id = dev_id, master = master))

    def detach(self, dev_id):
        """
        Detach the device from its master.
        """
        try:
            self._device_manager.detach_device_id(dev_id)
            self._floating = True
        except:
            logger.warning("Failed to detach device {id}".format(id = dev_id))

    def finalize(self):
        """
        Clean up the ScanDevice instance.
        """
        self._device_manager.disconnect("device-event",
                                        self._device_event_handler)
        config.scanner.device_name_notify_remove(self._device_name_notify)
        config.scanner.device_detach_notify_remove(self._device_detach_notify)
        self.close()
        self._event_handler = None
        self.devices = None

    @staticmethod
    def is_useable(device):
        """
        Check whether this device is useable for scanning.
        """
        return device.name not in ScanDevice.blacklist \
               and device.enabled \
               and not device.is_floating()
Beispiel #4
0
class CSFloatingSlave(ClickSimulator):
    """
    Onboards built-in mouse click mapper.
    Maps secondary or middle button to the primary button.
    """
    def __init__(self, keyboard):
        ClickSimulator.__init__(self)

        self._keyboard = keyboard

        self._device_manager = XIDeviceManager()
        self._grabbed_device_ids = []
        self._num_clicks_detected = 0
        self._motion_position = None

        self._button = self.PRIMARY_BUTTON
        self._click_type = self.CLICK_TYPE_SINGLE

        self._click_done_notify_callbacks = []
        self._exclusion_rects = []

        self._osk_cm = osk.ClickMapper()

    def cleanup(self):
        self.end_mapping()
        self._click_done_notify_callbacks = []

    def is_valid(self):
        return self._device_manager.is_valid()

    def supports_click_params(self, button, click_type):
        return True

    def map_primary_click(self, event_source, button, click_type):
        if event_source and (button != self.PRIMARY_BUTTON or \
                             click_type != self.CLICK_TYPE_SINGLE):
            self._begin_mapping(event_source, button, click_type)
        else:
            self.end_mapping()

    def _begin_mapping(self, event_source, button, click_type):
        click_device = self._device_manager.get_last_click_device()
        motion_device = self._device_manager.get_last_motion_device()

        if self._register_xinput_events(click_device, motion_device):
            self._button = button
            self._click_type = click_type
            self._num_clicks_detected = 0
            self._motion_position = None

    def end_mapping(self):
        self._deregister_xinput_events()
        self._button = self.PRIMARY_BUTTON
        self._click_type = self.CLICK_TYPE_SINGLE

    def _register_xinput_events(self, click_device, motion_device):
        success = self._register_xinput_device(click_device, "primary",
                                          XIEventMask.ButtonPressMask | \
                                          XIEventMask.ButtonReleaseMask | \
                                          XIEventMask.MotionMask)

        if success and not motion_device is click_device:
            success = self._register_xinput_device(motion_device, "motion",
                                              XIEventMask.MotionMask)

        if success:
            self._device_manager.connect("device-event",
                                          self._on_device_event)
        else:
            self._deregister_xinput_events()

        return success

    def _register_xinput_device(self, device, description, event_mask):
        _logger.info("grab {} device {}".format(description, device))

        try:
            self._device_manager.grab_device(device)
            self._grabbed_device_ids.append(device.id)
        except osk.error as ex:
            _logger.error("grab device {id} '{name}': {ex}"
                          .format(id = device.id,
                                  name=device.name,
                                  ex = ex))
            return False

        try:
            self._device_manager.select_events(None, device, event_mask)
        except osk.error as ex:
            _logger.error("select root events for device "
                          "{id} '{name}': {ex}"
                          .format(id = device.id,
                                  name=device.name,
                                  ex = ex))
            return False

        return True

    def _deregister_xinput_events(self):
        if self._grabbed_device_ids:
            self._device_manager.disconnect("device-event",
                                            self._on_device_event)

            # unselect and ungrab all devices
            for device_id in self._grabbed_device_ids:
                device = self._device_manager.lookup_device_id(device_id)

                _logger.info("ungrab " + str(device))

                try:
                    self._device_manager.unselect_events(None, device)
                except osk.error as ex:
                    _logger.error("unselect root events for device "
                                  "{id} '{name}': {ex}"
                                  .format(id = device.id,
                                          name=device.name,
                                          ex = ex))

                try:
                    self._device_manager.ungrab_device(device)
                except osk.error as ex:
                    _logger.error("ungrab device {id} '{name}': {ex}"
                                  .format(id = device.id,
                                          name=device.name,
                                          ex = ex))

            self._grabbed_device_ids = []

    def _on_device_event(self, event):
        event_type = event.xi_type
        button = self._button
        click_type = self._click_type
        generate_button_event = self._osk_cm.generate_button_event

        if not event.device_id in self._grabbed_device_ids:
            return

        #print("device event:", event.device_id, event.xi_type, (event.x, event.y), (event.x_root, event.y_root), event.xid_event)

        # Get pointer position from motion events only to support clicking
        # with one device and pointing with another.
        position = self._motion_position
        if event_type == XIEventType.Motion:
            position = (int(event.x_root), int(event.y_root))
            self._motion_position = position

            # tell master pointer about our new position
            self._osk_cm.generate_motion_event(*position)

        if position is None and \
           (event_type == XIEventType.ButtonPress or \
            event_type == XIEventType.ButtonRelease):
            position = (int(event.x_root), int(event.y_root))

        if position is None:
            return

        # single click
        if click_type == self.CLICK_TYPE_SINGLE:
            if event_type == XIEventType.ButtonPress:
                self._keyboard.maybe_send_alt_press(None, button, 0)
                generate_button_event(button, True)

            elif event_type == XIEventType.ButtonRelease:
                generate_button_event(button, False)
                self._keyboard.maybe_send_alt_release(None, button, 0)

                if self._num_clicks_detected and \
                   not self._is_point_in_exclusion_rects(position):
                    self.end_mapped_click()

        # double click
        elif click_type == self.CLICK_TYPE_DOUBLE:
            if event_type == XIEventType.ButtonRelease:
                if self._num_clicks_detected:
                    if not self._is_point_in_exclusion_rects(position):
                        delay = 40
                        self._keyboard.maybe_send_alt_press(None, button, 0)
                        generate_button_event(button, True)
                        generate_button_event(button, False, delay)
                        generate_button_event(button, True, delay)
                        generate_button_event(button, False, delay)
                        self._keyboard.maybe_send_alt_release(None, button, 0)
                    self.end_mapped_click()

        # drag click
        elif click_type == self.CLICK_TYPE_DRAG:
            if event_type == XIEventType.ButtonRelease:
                if self._num_clicks_detected == 1:
                    if self._is_point_in_exclusion_rects(position):
                        self.end_mapped_click()
                    else:
                        self._keyboard.maybe_send_alt_press(None, button, 0)
                        generate_button_event(button, True)

                elif self._num_clicks_detected >= 2:
                    generate_button_event(button, False)
                    self._keyboard.maybe_send_alt_release(None, button, 0)
                    self.end_mapped_click()

        # count button presses
        if event_type == XIEventType.ButtonPress:
            self._num_clicks_detected += 1

    def _is_point_in_exclusion_rects(self, point):
        for rect in self._exclusion_rects:
            if rect.is_point_within(point):
                return True
        return False

    def is_mapping_active(self):
        return bool(self._grabbed_device_ids)

    def get_click_button(self):
        return self._button

    def get_click_type(self):
        return self._click_type

    def state_notify_add(self, callback):
        self._click_done_notify_callbacks.append(callback)

    def end_mapped_click(self):
        """ osk callback, outside click, xi button release """
        if self.is_mapping_active():
            self.end_mapping()

            self._keyboard.release_latched_sticky_keys()

            # update click type buttons
            for callback in self._click_done_notify_callbacks:
                callback(None)

    def set_exclusion_rects(self, rects):
        self._exclusion_rects = rects