/* This file is part of Clementine. Clementine is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Clementine is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Clementine. If not, see . */ #include "wmdmlister.h" #include "wmdmthread.h" #include "core/utilities.h" #include #include #include #include #include #include #include #include QString WmdmLister::CanonicalNameToId(const QString& canonical_name) { return "wmdm/" + canonical_name; } QString WmdmLister::DeviceInfo::unique_id() const { return WmdmLister::CanonicalNameToId(canonical_name_); } WmdmLister::WmdmLister() : notification_cookie_(0) { } WmdmLister::~WmdmLister() { Q_ASSERT(!thread_); } void WmdmLister::Init() { thread_.reset(new WmdmThread); // Register for notifications IConnectionPointContainer* cp_container = NULL; thread_->manager()->QueryInterface(IID_IConnectionPointContainer, (void**)&cp_container); IConnectionPoint* cp = NULL; cp_container->FindConnectionPoint(IID_IWMDMNotification, &cp); cp->Advise(this, ¬ification_cookie_); cp->Release(); cp_container->Release(); // Fetch the initial list of devices IWMDMEnumDevice* device_it = NULL; if (thread_->manager()->EnumDevices2(&device_it)) { qWarning() << "Error querying WMDM devices"; return; } // Iterate through the devices QMap devices; forever { IWMDMDevice* device = NULL; IWMDMDevice2* device2 = NULL; ULONG fetched = 0; if (device_it->Next(1, &device, &fetched) || fetched != 1) break; if (device->QueryInterface(IID_IWMDMDevice2, (void**)&device2)) { qWarning() << "Error getting IWMDMDevice2 from device"; device->Release(); continue; } device->Release(); DeviceInfo info = ReadDeviceInfo(device2); if (info.is_suitable_) devices[info.unique_id()] = info; else device2->Release(); } device_it->Release(); // Update the internal cache { QMutexLocker l(&mutex_); devices_ = devices; } // Notify about the changes foreach (const QString& id, devices.keys()) { emit DeviceAdded(id); } } void WmdmLister::ReallyShutdown() { // Unregister for notifications IConnectionPointContainer* cp_container; thread_->manager()->QueryInterface(IID_IConnectionPointContainer, (void**)&cp_container); IConnectionPoint* cp; cp_container->FindConnectionPoint(IID_IWMDMNotification, &cp); cp->Release(); cp_container->Release(); thread_.reset(); } void WmdmLister::ShutDown() { // COM shutdown must be done in the original thread. metaObject()->invokeMethod(this, "ReallyShutdown", Qt::BlockingQueuedConnection); } template qint64 GetSpaceValue(F f) { DWORD low, high; f(&low, &high); return (qint64)high << 32 | (qint64)low; } WmdmLister::DeviceInfo WmdmLister::ReadDeviceInfo(IWMDMDevice2* device) { DeviceInfo ret; ret.device_ = device; // Get text strings const int max_size = 512; wchar_t buf[max_size]; device->GetName(buf, max_size); ret.name_ = QString::fromWCharArray(buf); device->GetManufacturer(buf, max_size); ret.manufacturer_ = QString::fromWCharArray(buf); device->GetCanonicalName(buf, max_size); ret.canonical_name_ = QString::fromWCharArray(buf).toLower(); // Get the type and check whether it has storage DWORD type = 0; device->GetType(&type); if (type & WMDM_DEVICE_TYPE_STORAGE) ret.is_suitable_ = true; // Get the icon HICON icon; device->GetDeviceIcon((ULONG*)&icon); ret.icon_ = QPixmap::fromWinHICON(icon); DestroyIcon(icon); // Get the main (first) storage for the device IWMDMEnumStorage* storage_it = NULL; if (device->EnumStorage(&storage_it) == S_OK && storage_it) { ULONG storage_fetched = 0; IWMDMStorage* storage; if (storage_it->Next(1, &storage, &storage_fetched) == S_OK) { if (storage->QueryInterface(IID_IWMDMStorage2, (void**)&ret.storage_)) { qWarning() << "Error getting IWMDMStorage2 from storage"; } else { // Get free space information IWMDMStorageGlobals* globals; ret.storage_->GetStorageGlobals(&globals); ret.total_bytes_ = GetSpaceValue(boost::bind(&IWMDMStorageGlobals::GetTotalSize, globals, _1, _2)); ret.free_bytes_ = GetSpaceValue(boost::bind(&IWMDMStorageGlobals::GetTotalFree, globals, _1, _2)); ret.free_bytes_ -= GetSpaceValue(boost::bind(&IWMDMStorageGlobals::GetTotalBad, globals, _1, _2)); globals->Release(); } storage->Release(); } storage_it->Release(); } return ret; } QStringList WmdmLister::DeviceUniqueIDs() { QMutexLocker l(&mutex_); return devices_.keys(); } QVariantList WmdmLister::DeviceIcons(const QString& id) { QPixmap pixmap = LockAndGetDeviceInfo(id, &DeviceInfo::icon_); if (pixmap.isNull()) return QVariantList(); return QVariantList() << pixmap; } QString WmdmLister::DeviceManufacturer(const QString& id) { return LockAndGetDeviceInfo(id, &DeviceInfo::manufacturer_); } QString WmdmLister::DeviceModel(const QString& id) { return LockAndGetDeviceInfo(id, &DeviceInfo::name_); } quint64 WmdmLister::DeviceCapacity(const QString& id) { return LockAndGetDeviceInfo(id, &DeviceInfo::total_bytes_); } quint64 WmdmLister::DeviceFreeSpace(const QString& id) { return LockAndGetDeviceInfo(id, &DeviceInfo::free_bytes_); } QVariantMap WmdmLister::DeviceHardwareInfo(const QString& id) { return QVariantMap(); } QString WmdmLister::MakeFriendlyName(const QString& id) { QMutexLocker l(&mutex_); if (!devices_.contains(id)) return QString(); const DeviceInfo& info = devices_[id]; if (info.manufacturer_.isEmpty() || info.manufacturer_ == "Unknown") return info.name_; return info.manufacturer_ + " " + info.name_; } QList WmdmLister::MakeDeviceUrls(const QString& id) { QUrl url; url.setScheme("wmdm"); url.setPath(id); return QList() << url; } void WmdmLister::UnmountDevice(const QString& id) { } void WmdmLister::UpdateDeviceFreeSpace(const QString& id) { } HRESULT WmdmLister::WMDMMessage(DWORD message_type, LPCWSTR name) { QString canonical_name = QString::fromWCharArray(name).toLower(); switch (message_type) { case WMDM_MSG_DEVICE_ARRIVAL: WMDMDeviceAdded(canonical_name); break; case WMDM_MSG_DEVICE_REMOVAL: WMDMDeviceRemoved(canonical_name); break; } return S_OK; } void WmdmLister::WMDMDeviceAdded(const QString& canonical_name) { ScopedWCharArray name(canonical_name); IWMDMDevice* device = NULL; if (thread_->manager()->GetDeviceFromCanonicalName(name, &device)) { qWarning() << "Error in GetDeviceFromCanonicalName for" << canonical_name; return; } IWMDMDevice2* device2 = NULL; if (device->QueryInterface(IID_IWMDMDevice2, (void**) &device2)) { qWarning() << "Error getting IWMDMDevice2 from device"; device->Release(); return; } device->Release(); DeviceInfo info = ReadDeviceInfo(device2); if (info.is_suitable_) { QString id = info.unique_id(); { QMutexLocker l(&mutex_); devices_[id] = info; } emit DeviceAdded(id); } else { device2->Release(); } } void WmdmLister::WMDMDeviceRemoved(const QString& canonical_name) { QString id = CanonicalNameToId(canonical_name); { QMutexLocker l(&mutex_); if (!devices_.contains(id)) return; devices_[id].device_->Release(); devices_[id].storage_->Release(); devices_.remove(id); } emit DeviceRemoved(id); } LONG WmdmLister::QueryInterface(REFIID riid, void** object) { *object = 0; if (riid == IID_IUnknown) *object = (IUnknown*) this; else if (riid == IID_IWMDMNotification) *object = (IWMDMNotification*) this; else return E_NOINTERFACE; return S_OK; } ULONG WmdmLister::AddRef() { return 0; } ULONG WmdmLister::Release() { return 0; } QString WmdmLister::DeviceCanonicalName(const QString& id) { return LockAndGetDeviceInfo(id, &DeviceInfo::canonical_name_); }