示例#1
0
class RemoteRelayNode:

    # websocket is an instance from the websockets library
    #
    # see https://websockets.readthedocs.io/en/stable/api.html
    def __init__(self, websocket):
        self.new_message_signal = AsyncSignal()
        self._websocket = websocket
        self._progress_signaler = ProgressSignaler(signals.outgoing_transfer)

    async def accept_relayed_message(self, message):
        self._progress_signaler.begin_transfer(message)
        async for chunk in message.chunks:
            await self._websocket.send(pickle.dumps(chunk))
            self._progress_signaler.on_chunk_transferred()

    def start_relaying_changes(self):
        asyncio.ensure_future(self._process_messages())

    async def _process_messages(self):
        async for message in self._chunked_message_receiver.received_messages:
            self.new_message_signal.send(message)

    @property
    def _chunked_message_receiver(self):
        return ChunkedMessageReceiver(self._depickled_socket_messages)

    @property
    async def _depickled_socket_messages(self):
        async for msg in self._websocket:
            yield pickle.loads(msg)

    def __repr__(self):
        return (f'<{type(self).__name__}: '
                f'{self._websocket.host}:{self._websocket.port}>')
示例#2
0
class AppSettings:

    on_change = AsyncSignal()

    @classproperty
    def get(cls):
        qsettings = cls._qsettings
        return Settings(is_server_enabled=qsettings.value('is_server_enabled',
                                                          type=bool),
                        server_listen_ip=qsettings.value('server_listen_ip',
                                                         type=str),
                        server_listen_port=qsettings.value(
                            'server_listen_port', type=str),
                        is_client_enabled=qsettings.value('is_client_enabled',
                                                          type=bool),
                        client_ws_url=qsettings.value('client_ws_url',
                                                      type=str))

    @classmethod
    def set(cls, settings):
        qsettings = cls._qsettings
        qsettings.setValue('is_server_enabled', settings.is_server_enabled)
        qsettings.setValue('server_listen_ip', settings.server_listen_ip)
        qsettings.setValue('server_listen_port', settings.server_listen_port)
        qsettings.setValue('is_client_enabled', settings.is_client_enabled)
        qsettings.setValue('client_ws_url', settings.client_ws_url)

        cls.on_change.send()

    _qsettings = QSettings('sumeet', 'clipshare')
示例#3
0
class LinuxClipboard:

    @classmethod
    def new(cls, qt_app):
        return cls(qt_app.clipboard())

    def __init__(self, qt_clipboard):
        self.new_clipboard_contents_signal = AsyncSignal()
        self._qt_clipboard = qt_clipboard

    def set(self, clipboard_contents):
        with self._stop_receiving_clipboard_updates():
            change_tiff_to_png(clipboard_contents)
            qmimedata_to_set = QMimeDataSerializer.deserialize(clipboard_contents)
            self._qt_clipboard.setMimeData(qmimedata_to_set)

    def clear(self):
        with self._stop_receiving_clipboard_updates():
            self._qt_clipboard.clear()

    def start_listening_for_changes(self):
        self._qt_clipboard.dataChanged.connect(self._grab_and_signal_clipboard_data)

    # temporarily stop listening to the clipboard while we set it, because we
    # don't want to detect our own updates
    @contextlib.contextmanager
    def _stop_receiving_clipboard_updates(self):
        try:
            self._qt_clipboard.dataChanged.disconnect(self._grab_and_signal_clipboard_data)
            yield
        finally:
            self.start_listening_for_changes()

    def _grab_and_signal_clipboard_data(self):
        mime_data = self._qt_clipboard.mimeData()
        clipboard_contents = QMimeDataSerializer.serialize(mime_data)
        logger.debug(f'detected change {log.format_obj(clipboard_contents)}')
        if self._contains_data(clipboard_contents):
            self.new_clipboard_contents_signal.send(clipboard_contents)
        else:
            logger.debug('nothing to update, backing out')

    def _contains_data(self, clipboard_contents):
        return any(clipboard_contents.values())

    def __repr__(self):
        return f'<{type(self).__name__}>'
示例#4
0
class ClientRelayNode:
    def __init__(self, clipboard):
        self.new_message_signal = AsyncSignal()
        self._clipboard = clipboard
        self._progress_signaler = ProgressSignaler(signals.incoming_transfer)

    async def accept_relayed_message(self, message):
        # we receive the message here after receiving the first chunk fully. up
        # to this point, we haven't gotten the entire message.
        #
        # there's a delay between when we first hear that there's a new message
        # and when we're ready to set clipboard contents, because we have to
        # wait for several chunks to download. transmitting an image may take
        # a few seconds to transfer, and in the meantime, if you try to paste,
        # you'll end up the previous item on the clipboard.

        # clearing the clipboard in anticipation of new contents being
        # downloaded will prevent accidental pasting.
        logger.debug('got wind of a message, clearing the clipboard')
        self._clipboard.clear()
        logger.debug('actually setting the clipboard')
        ensure_future(self._broadcast_incoming_transfer_progress(message))
        self._clipboard.set(await message.full_payload)

    def start_relaying_changes(self):
        self._clipboard.new_clipboard_contents_signal.connect(
            self._handle_new_clipboard_contents_signal)
        self._clipboard.start_listening_for_changes()

    def _handle_new_clipboard_contents_signal(self, clipboard_contents):
        message = Message(payload=clipboard_contents,
                          split_size=BYTES_PER_SPLIT)
        self.new_message_signal.send(message)

    def __repr__(self):
        return f'<{type(self).__name__}: {type(self._clipboard).__name__}>'

    async def _broadcast_incoming_transfer_progress(self, message):
        self._progress_signaler.begin_transfer(message)
        async for chunk in message.chunks:
            self._progress_signaler.on_chunk_transferred()
示例#5
0
 def __init__(self, ns_pasteboard):
     self.new_clipboard_contents_signal = AsyncSignal()
     self._ns_pasteboard = ns_pasteboard
     self._poller = Poller(self._ns_pasteboard)
示例#6
0
class MacClipboard:
    @classmethod
    def new(cls, qt_app):
        # we don't use qt to read for the mac clipboard, for now
        return cls(NSPasteboard.generalPasteboard())

    def __init__(self, ns_pasteboard):
        self.new_clipboard_contents_signal = AsyncSignal()
        self._ns_pasteboard = ns_pasteboard
        self._poller = Poller(self._ns_pasteboard)

    def set(self, clipboard_contents):
        object_to_set = self._extract_settable_nsobject(clipboard_contents)
        if not object_to_set:
            all_types = repr(clipboard_contents.keys())
            logger.debug('unsupported clipboard payload ' +
                         log.format_obj(clipboard_contents))
            return

        try:
            # pause the poller while we write to it, so we don't detect our own
            # changes
            self._poller.pause_polling()

            # for some reason you've gotta clear before writing or the write doesn't
            # have any effect
            self._ns_pasteboard.clearContents()
            self._ns_pasteboard.writeObjects_(
                NSArray.arrayWithObject_(object_to_set))

            # tell the poller to ignore the change we just made,
            self._poller.ignore_change_count(self._poller.current_change_count)
        finally:
            # and then when it resumes, it won't detect that change
            self._poller.resume_polling()

    def clear(self):
        logger.debug('clearing the clipboard')
        self._ns_pasteboard.clearContents()

    def start_listening_for_changes(self):
        asyncio.ensure_future(self._poll_forever())

    async def _poll_forever(self):
        while True:
            t = time.time()
            clipboard_contents = await self._poller.poll_for_new_clipboard_contents(
            )
            if clipboard_contents:
                logger.debug(
                    f'detected change {self._poller.current_change_count} ' +
                    log.format_obj(clipboard_contents))
                self.new_clipboard_contents_signal.send(clipboard_contents)
            await asyncio.sleep(CLIPBOARD_POLL_INTERVAL_SECONDS)

    def _extract_settable_nsobject(self, clipboard_contents):
        image_type = self._find_image_type(clipboard_contents)
        if image_type:
            image_data = clipboard_contents[image_type]
            return NSImage.alloc().initWithData_(image_data)
        elif 'text/plain' in clipboard_contents:
            bytes = clipboard_contents['text/plain']
            return NSString.alloc().initWithUTF8String_(bytes)
        # TODO: rich text, if i ever feel like i need it
        else:
            return None

    def _find_image_type(self, clipboard_contents):
        image_types = (t for t in clipboard_contents if t.startswith('image'))
        return next(image_types, None)

    def __repr__(self):
        return f'<{type(self).__name__}>'
示例#7
0
 def __init__(self, clipboard):
     self.new_message_signal = AsyncSignal()
     self._clipboard = clipboard
     self._progress_signaler = ProgressSignaler(signals.incoming_transfer)
示例#8
0
 def __init__(self, qt_clipboard):
     self.new_clipboard_contents_signal = AsyncSignal()
     self._qt_clipboard = qt_clipboard
示例#9
0
 def __init__(self, websocket):
     self.new_message_signal = AsyncSignal()
     self._websocket = websocket
     self._progress_signaler = ProgressSignaler(signals.outgoing_transfer)