From 740b7337b5834bbb088cf00485f914b9cbfc4fb1 Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Wed, 3 Dec 2014 14:46:22 +0100 Subject: [PATCH] 1) Change the wireless device driver to manage its own connections. 2) Add an API for devices to determine if some other device is connected. --- src/calibre/devices/interface.py | 22 ++++++++++ .../devices/smart_device_app/driver.py | 43 +++++++++++-------- src/calibre/gui2/device.py | 5 ++- 3 files changed, 52 insertions(+), 18 deletions(-) diff --git a/src/calibre/devices/interface.py b/src/calibre/devices/interface.py index 23de219b56..6932243b36 100644 --- a/src/calibre/devices/interface.py +++ b/src/calibre/devices/interface.py @@ -6,6 +6,28 @@ from collections import namedtuple from calibre.customize import Plugin from calibre.constants import iswindows +class CurrentlyConnectedDevice: + + def __init__(self): + self._connected_device = None + + def connected_device(self): + ''' + This method can be called by any device driver to determine if some other + device is already connected. It is threadsafe because of the python GIL. + ''' + return self._connected_device + + def set_connected_device(self, dev=None): + ''' + This method is called by the device manager whenever a device connects + or disconnects. It must not be called by anything else. It is called + on the device manager thread. It is threadsafe because of the python GIL. + ''' + self._connected_device = dev + +currently_connected_device = CurrentlyConnectedDevice() + class DevicePlugin(Plugin): """ Defines the interface that should be implemented by backends that diff --git a/src/calibre/devices/smart_device_app/driver.py b/src/calibre/devices/smart_device_app/driver.py index 8e27178c6f..d8d73dbc34 100644 --- a/src/calibre/devices/smart_device_app/driver.py +++ b/src/calibre/devices/smart_device_app/driver.py @@ -21,7 +21,7 @@ from calibre import prints from calibre.constants import numeric_version, DEBUG, cache_dir from calibre.devices.errors import (OpenFailed, OpenFeedback, ControlError, TimeoutError, InitialConnectionError, PacketError) -from calibre.devices.interface import DevicePlugin +from calibre.devices.interface import DevicePlugin, currently_connected_device from calibre.devices.usbms.books import Book, CollectionsBookList from calibre.devices.usbms.deviceconfig import DeviceConfig from calibre.devices.usbms.driver import USBMS @@ -54,8 +54,6 @@ def synchronous(tlockname): class ConnectionListener(Thread): - NOT_SERVICED_COUNT = 6 - def __init__(self, driver): Thread.__init__(self) self.daemon = True @@ -67,7 +65,6 @@ class ConnectionListener(Thread): self.keep_running = False def run(self): - queue_not_serviced_count = 0 device_socket = None get_all_ips(reinitialize=True) @@ -87,9 +84,9 @@ class ConnectionListener(Thread): self.driver._debug("All IP addresses", self.all_ip_addresses) if not self.driver.connection_queue.empty(): - queue_not_serviced_count += 1 - if queue_not_serviced_count >= self.NOT_SERVICED_COUNT: - self.driver._debug('queue not serviced', queue_not_serviced_count) + d = currently_connected_device.connected_device() + if d: + self.driver._debug('queue not serviced', d.__class__.__name__) try: sock = self.driver.connection_queue.get_nowait() s = self.driver._json_encode( @@ -98,9 +95,6 @@ class ConnectionListener(Thread): sock.close() except Queue.Empty: pass - queue_not_serviced_count = 0 - else: - queue_not_serviced_count = 0 if getattr(self.driver, 'broadcast_socket', None) is not None: while True: @@ -194,6 +188,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): NEWS_IN_FOLDER = True SUPPORTS_USE_AUTHOR_SORT = False WANTS_UPDATED_THUMBNAILS = True + MANAGES_DEVICE_PRESENCE = True # Guess about the max length on windows. This number will be reduced by # the length of the path on the client, and by the fudge factor below. We @@ -914,18 +909,17 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): # The public interface methods. @synchronous('sync_lock') - def is_usb_connected(self, devices_on_system, debug=False, only_presence=False): + def detect_managed_devices(self, devices_on_system, force_refresh=False): if getattr(self, 'listen_socket', None) is None: self.is_connected = False if self.is_connected: self.noop_counter += 1 - if (only_presence and - self.noop_counter > self.SEND_NOOP_EVERY_NTH_PROBE and + if (self.noop_counter > self.SEND_NOOP_EVERY_NTH_PROBE and (self.noop_counter % self.SEND_NOOP_EVERY_NTH_PROBE) != 1): try: ans = select.select((self.device_socket,), (), (), 0) if len(ans[0]) == 0: - return (True, self) + return self # The socket indicates that something is there. Given the # protocol, this can only be a disconnect notification. Fall # through and actually try to talk to the client. @@ -942,7 +936,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): self._close_device_socket() except: self._close_device_socket() - return (self.is_connected, self) + return self if self.is_connected else None if getattr(self, 'listen_socket', None) is not None: try: @@ -964,8 +958,23 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): pass except Queue.Empty: self.is_connected = False - return (self.is_connected, self) - return (False, None) + return self if self.is_connected else None + return None + + @synchronous('sync_lock') + def debug_managed_device_detection(self, devices_on_system, output): + from functools import partial + p = partial(prints, file=output) + if self.is_connected: + p("A wireless device is connected") + return True + all_ip_addresses = get_all_ips() + if all_ip_addresses: + p("All IP addresses", all_ip_addresses) + else: + p("No IP addresses found") + p("No device is connected") + return False @synchronous('sync_lock') def open(self, connected_device, library_uuid): diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index c6a2a20572..6e95a510b4 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -13,7 +13,7 @@ from PyQt5.Qt import ( from calibre.customize.ui import (available_input_formats, available_output_formats, device_plugins, disabled_device_plugins) -from calibre.devices.interface import DevicePlugin +from calibre.devices.interface import DevicePlugin, currently_connected_device from calibre.devices.errors import (UserFeedback, OpenFeedback, OpenFailed, InitialConnectionError) from calibre.gui2.dialogs.choose_format_device import ChooseFormatDeviceDialog @@ -231,6 +231,7 @@ class DeviceManager(Thread): # {{{ self.connected_device.specialize_global_preferences(device_prefs) self.connected_device_kind = device_kind self.connected_slot(True, device_kind) + currently_connected_device.set_connected_device(self.connected_device) def connected_device_removed(self): while True: @@ -254,6 +255,8 @@ class DeviceManager(Thread): # {{{ # is being shut down. self.connected_device.shutdown() self.call_shutdown_on_disconnect = False + + currently_connected_device.set_connected_device(None) device_prefs.set_overrides() self.connected_device = None self._device_information = None