#include "main.h" #include "DeviceView.h" #include "api__ml_pmp.h" #include "../nu/refcount.h" #include "..\..\General\gen_ml/ml_ipc_0313.h" #include "DeviceCommands.h" #include "resource1.h" #include // known commands static const char *DEVICE_CMD_VIEW_OPEN = "view_open"; static const char *DEVICE_CMD_SYNC = "sync"; static const char *DEVICE_CMD_AUTOFILL = "autofill"; static const char *DEVICE_CMD_PLAYLIST_CREATE = "playlist_create"; static const char *DEVICE_CMD_RENAME = "rename"; static const char *DEVICE_CMD_PREFERENCES = "preferences"; static const char *DEVICE_CMD_EJECT = "eject"; static const char *DEVICE_CMD_REMOVE = "remove"; static const char *DEVICE_CMD_TRANSFER = "transfer"; static const char *DEVICE_CMD_HIDE = "hide"; extern void UpdateDevicesListView(bool softUpdate); // we're going to share the command enum stuff for all devices class PortableDeviceType : public ifc_devicetype { public: PortableDeviceType() { } const char *GetName() { return "portable"; } HRESULT GetIcon(wchar_t *buffer, size_t bufferSize, int width, int height) { return E_NOTIMPL; } HRESULT GetDisplayName(wchar_t *buffer, size_t bufferSize) { if (NULL == buffer) return E_POINTER; WASABI_API_LNGSTRINGW_BUF(IDS_PORTABLE_DEVICE_TYPE, buffer, bufferSize); return S_OK; } protected: #define CBCLASS PortableDeviceType START_DISPATCH_INLINE; CB(API_GETNAME, GetName); CB(API_GETICON, GetIcon); CB(API_GETDISPLAYNAME, GetDisplayName); END_DISPATCH; #undef CBCLASS }; class USBDeviceConnection : public ifc_deviceconnection { public: USBDeviceConnection() { } const char *GetName() { return "usb"; } HRESULT GetIcon(wchar_t *buffer, size_t bufferSize, int width, int height) { if (FALSE == FormatResProtocol(MAKEINTRESOURCE(IDB_USB), L"PNG", buffer, bufferSize)) { return E_FAIL; } return S_OK; } HRESULT GetDisplayName(wchar_t *buffer, size_t bufferSize) { if (NULL == buffer) return E_POINTER; WASABI_API_LNGSTRINGW_BUF(IDS_DEVICE_CONNECTION_USB, buffer, bufferSize); return S_OK; } protected: #define CBCLASS USBDeviceConnection START_DISPATCH_INLINE; CB(API_GETNAME, GetName); CB(API_GETICON, GetIcon); CB(API_GETDISPLAYNAME, GetDisplayName); END_DISPATCH; #undef CBCLASS }; static PortableDeviceType portable_device_type; static USBDeviceConnection usb_connection; static PortableCommand registered_commands[] = { PortableCommand(DEVICE_CMD_VIEW_OPEN, IDS_DEVICE_CMD_VIEW_OPEN, IDS_DEVICE_CMD_VIEW_OPEN_DESC), PortableCommand(DEVICE_CMD_SYNC, IDS_DEVICE_CMD_SYNC, IDS_DEVICE_CMD_SYNC_DESC), PortableCommand(DEVICE_CMD_TRANSFER, IDS_DEVICE_CMD_TRANSFER, IDS_DEVICE_CMD_TRANSFER_DESC), PortableCommand(DEVICE_CMD_EJECT, IDS_DEVICE_CMD_EJECT, IDS_DEVICE_CMD_EJECT_DESC), PortableCommand(DEVICE_CMD_REMOVE, IDS_DEVICE_CMD_REMOVE, IDS_DEVICE_CMD_REMOVE_DESC), PortableCommand(DEVICE_CMD_RENAME, IDS_DEVICE_CMD_RENAME, IDS_DEVICE_CMD_RENAME_DESC), PortableCommand(DEVICE_CMD_AUTOFILL, IDS_DEVICE_CMD_AUTOFILL, IDS_DEVICE_CMD_AUTOFILL_DESC), PortableCommand(DEVICE_CMD_PLAYLIST_CREATE, IDS_DEVICE_CMD_PLAYLIST_CREATE, IDS_DEVICE_CMD_PLAYLIST_CREATE_DESC), PortableCommand(DEVICE_CMD_PREFERENCES, IDS_DEVICE_CMD_PREFERENCES, IDS_DEVICE_CMD_PREFERENCES_DESC), PortableCommand(DEVICE_CMD_HIDE, IDS_DEVICE_CMD_HIDE, IDS_DEVICE_CMD_HIDE), }; static ifc_devicecommand * _cdecl Devices_RegisterCommand(const char *name, void *user) { for(size_t i = 0; i < sizeof(registered_commands)/sizeof(*registered_commands); i++) { if (name == registered_commands[i].GetName()) { return ®istered_commands[i]; } } return NULL; } void Devices_Init() { if (AGAVE_API_DEVICEMANAGER) { /* register 'portable' device type */ ifc_devicetype *type = &portable_device_type; AGAVE_API_DEVICEMANAGER->TypeRegister(&type, 1); /* register 'usb' connection type */ ifc_deviceconnection *connection = &usb_connection; AGAVE_API_DEVICEMANAGER->ConnectionRegister(&connection, 1); /* register commands */ const char *commands[sizeof(registered_commands)/sizeof(*registered_commands)]; for(size_t i = 0; i < sizeof(registered_commands)/sizeof(*registered_commands); i++) { commands[i] = registered_commands[i].GetName(); } AGAVE_API_DEVICEMANAGER->CommandRegisterIndirect(commands, sizeof(registered_commands)/sizeof(*registered_commands), Devices_RegisterCommand, NULL); } } int DeviceView::QueryInterface(GUID interface_guid, void **object) { if (interface_guid == IFC_Device) { AddRef(); *object = (ifc_device *)this; return 0; } return 1; } const char *DeviceView::GetName() { return name; } HRESULT DeviceView::GetIcon(wchar_t *buffer, size_t bufferSize, int width, int height) { buffer[0]=0; dev->extraActions(DEVICE_GET_ICON, width, height, (intptr_t)buffer); if (buffer[0] == 0) return E_NOTIMPL; else return S_OK; return E_NOTIMPL; } HRESULT DeviceView::GetDisplayName(wchar_t *buffer, size_t bufferSize) { // TODO sometimes this is erroring on loading dev->getPlaylistName(0, buffer, bufferSize); return S_OK; } const char *DeviceView::GetType() { return "portable"; } const char *DeviceView::GetDisplayType() { return display_type; } const char *DeviceView::GetConnection() { return connection_type; } BOOL DeviceView::GetHidden() { return FALSE; } HRESULT DeviceView::GetTotalSpace(uint64_t *size) { UpdateSpaceInfo(FALSE, TRUE); *size = dev->getDeviceCapacityTotal(); return S_OK; } HRESULT DeviceView::GetUsedSpace(uint64_t *size) { if (NULL == size) return E_POINTER; UpdateSpaceInfo(TRUE, TRUE); *size = usedSpace; return S_OK; } BOOL DeviceView::GetAttached() { return TRUE; // ml_pmp devices are by default attached } HRESULT DeviceView::Attach(HWND hostWindow) { return E_NOTIMPL; } HRESULT DeviceView::Detach(HWND hostWindow) { return E_NOTIMPL; } HRESULT DeviceView::EnumerateCommands(ifc_devicesupportedcommandenum **enumerator, DeviceCommandContext context) { DeviceCommandInfo commands[32]; size_t count; if (NULL == enumerator) return E_POINTER; count = 0; LinkedQueue * txQueue = getTransferQueue(this); if (txQueue == NULL) return E_POINTER; // return E_NOTIMPL; if (context == DeviceCommandContext_View) { if (0 == txQueue->GetSize()) { SetDeviceCommandInfo(&commands[count++], DEVICE_CMD_SYNC, DeviceCommandFlag_Primary); SetDeviceCommandInfo(&commands[count++], DEVICE_CMD_AUTOFILL, DeviceCommandFlag_None); SetDeviceCommandInfo(&commands[count++], DEVICE_CMD_PLAYLIST_CREATE, DeviceCommandFlag_None); SetDeviceCommandInfo(&commands[count++], DEVICE_CMD_PREFERENCES, DeviceCommandFlag_None); SetDeviceCommandInfo(&commands[count++], DEVICE_CMD_EJECT, DeviceCommandFlag_None); SetDeviceCommandInfo(&commands[count++], DEVICE_CMD_REMOVE, DeviceCommandFlag_None); } } else { SetDeviceCommandInfo(&commands[count++], DEVICE_CMD_VIEW_OPEN, DeviceCommandFlag_Primary); DeviceCommandFlags flags = DeviceCommandFlag_None; if (0 != txQueue->GetSize()) flags |= DeviceCommandFlag_Disabled; if (0 != dev->extraActions(DEVICE_SYNC_UNSUPPORTED,0,0,0)) flags |= DeviceCommandFlag_Disabled; SetDeviceCommandInfo(&commands[count++], (!isCloudDevice ? DEVICE_CMD_SYNC : DEVICE_CMD_TRANSFER), flags | DeviceCommandFlag_Group); if (!isCloudDevice) SetDeviceCommandInfo(&commands[count++], DEVICE_CMD_AUTOFILL, flags); flags = DeviceCommandFlag_None; if (0 == dev->extraActions(DEVICE_PLAYLISTS_UNSUPPORTED,0,0,0)) { // TODO remove once we've got cloud playlists implemented if (!isCloudDevice) SetDeviceCommandInfo(&commands[count++], DEVICE_CMD_PLAYLIST_CREATE, flags | DeviceCommandFlag_Group); } // adds a specific menu item to hide the 'local library' source if (isCloudDevice) { char name[128] = {0}; if (dev->extraActions(DEVICE_GET_UNIQUE_ID, (intptr_t)name, sizeof(name), 0)) { if (!strcmp(name, "local_desktop")) { flags = DeviceCommandFlag_None | DeviceCommandFlag_Group; SetDeviceCommandInfo(&commands[count++], DEVICE_CMD_HIDE, flags); } } } bool has_rename = true; flags = DeviceCommandFlag_None; if (0 == dev->extraActions(DEVICE_CAN_RENAME_DEVICE,0,0,0)) has_rename = false; else SetDeviceCommandInfo(&commands[count++], DEVICE_CMD_RENAME, flags | DeviceCommandFlag_Group); flags = (!has_rename ? DeviceCommandFlag_Group : DeviceCommandFlag_None); SetDeviceCommandInfo(&commands[count++], DEVICE_CMD_PREFERENCES, flags); if (!dev->extraActions(DEVICE_DOES_NOT_SUPPORT_REMOVE,0,0,0)) SetDeviceCommandInfo(&commands[count++], (!isCloudDevice ? DEVICE_CMD_EJECT : DEVICE_CMD_REMOVE), flags | DeviceCommandFlag_Group); } *enumerator = new DeviceCommandEnumerator(commands, count); if (NULL == *enumerator) return E_OUTOFMEMORY; return S_OK; } HRESULT DeviceView::SendCommand(const char *command, HWND hostWindow, ULONG_PTR param) { if (!strcmp(command, DEVICE_CMD_EJECT) || !strcmp(command, DEVICE_CMD_REMOVE)) { Eject(); return S_OK; } else if (!strcmp(command, DEVICE_CMD_SYNC) || !strcmp(command, DEVICE_CMD_TRANSFER)) { if (!this->isCloudDevice) Sync(); else CloudSync(); return S_OK; } else if (!strcmp(command, DEVICE_CMD_AUTOFILL)) { Autofill(); return S_OK; } else if (!strcmp(command, DEVICE_CMD_RENAME)) { if (NULL != treeItem) MLNavItem_EditTitle(plugin.hwndLibraryParent, treeItem); else RenamePlaylist(0); return S_OK; } else if (!strcmp(command, DEVICE_CMD_PLAYLIST_CREATE)) { CreatePlaylist(); return S_OK; } else if (!strcmp(command, DEVICE_CMD_PREFERENCES)) { SENDWAIPC(plugin.hwndWinampParent, IPC_OPENPREFSTOPAGE,(WPARAM)&devPrefsPage); return S_OK; } else if (!strcmp(command, DEVICE_CMD_HIDE)) { static int IPC_CLOUD_HIDE_LOCAL = -1; if (IPC_CLOUD_HIDE_LOCAL == -1) IPC_CLOUD_HIDE_LOCAL = SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&"WinampCloudLocal", IPC_REGISTER_WINAMP_IPCMESSAGE); SENDWAIPC(plugin.hwndWinampParent, IPC_CLOUD_HIDE_LOCAL, 0); return S_OK; } return E_NOTIMPL; } HRESULT DeviceView::GetCommandFlags(const char *command, DeviceCommandFlags *flags) { return E_NOTIMPL; } HRESULT DeviceView::GetActivity(ifc_deviceactivity **activity) { LinkedQueue * txQueue = getTransferQueue(); if (txQueue == NULL || txQueue->GetSize() == 0) { *activity = 0; return S_FALSE; } AddRef(); *activity = this; return S_OK; } HRESULT DeviceView::Advise(ifc_deviceevent *handler) { event_handlers.push_back(handler); return S_OK; } HRESULT DeviceView::Unadvise(ifc_deviceevent *handler) { //event_handlers.eraseObject(handler); auto it = std::find(event_handlers.begin(), event_handlers.end(), handler); if (it != event_handlers.end()) { event_handlers.erase(it); } return S_OK; } extern C_ItemList devices; extern void UpdateDevicesListView(bool softupdate); void DeviceView::SetNavigationItem(void *navigationItem) { if (navigationItem) RegisterViews((HNAVITEM)navigationItem); } BOOL DeviceView::GetActive() { LinkedQueue * txQueue = getTransferQueue(); if (txQueue == NULL || txQueue->GetSize() == 0) return FALSE; return TRUE; } BOOL DeviceView::GetCancelable() { return FALSE; } HRESULT DeviceView::GetProgress(unsigned int *percentCompleted) { LinkedQueue * txQueue = getTransferQueue(); LinkedQueue * finishedTX = getFinishedTransferQueue(); int txProgress = getTransferProgress(); int size = (txQueue ? txQueue->GetSize() : 0); double num = (100.0 * (double)size) - (double)txProgress; double total = (double)100 * size + 100 * (finishedTX ? finishedTX->GetSize() : 0); double percent = (0 != total) ? (((total - num) * 100) / total) : 0; *percentCompleted = (unsigned int)percent; return S_OK; } HRESULT DeviceView::Activity_GetDisplayName(wchar_t *buffer, size_t bufferMax) { WASABI_API_LNGSTRINGW_BUF(IDS_TRANSFERRING, buffer, bufferMax); return S_OK; } HRESULT DeviceView::Activity_GetStatus(wchar_t *buffer, size_t bufferMax) { WASABI_API_LNGSTRINGW_BUF(IDS_TRANSFERRING_DESC, buffer, bufferMax); return S_OK; } HRESULT DeviceView::Cancel(HWND hostWindow) { // threadKillswitch = 1; // TODO: i think this is how to do it //transferContext.WaitForKill(); return S_OK; } HRESULT DeviceView::GetDropSupported(unsigned int dataType) { if (dataType == ML_TYPE_ITEMRECORDLISTW || dataType == ML_TYPE_ITEMRECORDLIST || dataType == ML_TYPE_PLAYLIST || dataType == ML_TYPE_PLAYLISTS || dataType == ML_TYPE_FILENAMES || dataType == ML_TYPE_FILENAMESW) return S_OK; return E_FAIL; } HRESULT DeviceView::Drop(void *data, unsigned int dataType) { return (HRESULT)TransferFromML(dataType,data,E_FAIL,S_OK); } HRESULT DeviceView::SetDisplayName(const wchar_t *displayName, bool force = 0) { if((0 == force && 0 == dev->extraActions(DEVICE_CAN_RENAME_DEVICE,0,0,0))) return E_FAIL; dev->setPlaylistName(0, displayName); free(devPrefsPage.name); devPrefsPage.name = _wcsdup(displayName); SENDWAIPC(plugin.hwndWinampParent, IPC_UPDATE_PREFS_DLGW, (WPARAM)&devPrefsPage); DevicePropertiesChanges(); UpdateDevicesListView(false); OnNameChanged(displayName); return S_OK; } HRESULT DeviceView::GetModel(wchar_t *buffer, size_t bufferSize) { if (NULL == buffer) return E_POINTER; buffer[0] = L'\0'; if(0 == dev->extraActions(DEVICE_GET_MODEL, (intptr_t)buffer, bufferSize, 0)) return E_NOTIMPL; return S_OK; } HRESULT DeviceView::GetStatus(wchar_t *buffer, size_t bufferSize) { return E_NOTIMPL; } #define CBCLASS DeviceView START_MULTIPATCH; START_PATCH(PATCH_IFC_DEVICE) M_CB(PATCH_IFC_DEVICE, ifc_device, QUERYINTERFACE, QueryInterface); M_CB(PATCH_IFC_DEVICE, ifc_device, API_GETNAME, GetName); M_CB(PATCH_IFC_DEVICE, ifc_device, ifc_deviceobject::API_GETDISPLAYNAME, GetDisplayName); M_CB(PATCH_IFC_DEVICE, ifc_device, API_GETICON, GetIcon); M_CB(PATCH_IFC_DEVICE, ifc_device, API_GETTYPE, GetType); M_CB(PATCH_IFC_DEVICE, ifc_device, API_GETDISPLAYTYPE, GetDisplayType); M_CB(PATCH_IFC_DEVICE, ifc_device, API_GETCONNECTION, GetConnection); M_CB(PATCH_IFC_DEVICE, ifc_device, API_GETTOTALSPACE, GetTotalSpace); M_CB(PATCH_IFC_DEVICE, ifc_device, API_GETUSEDSPACE, GetUsedSpace); M_CB(PATCH_IFC_DEVICE, ifc_device, API_ENUMERATECOMMANDS, EnumerateCommands); M_CB(PATCH_IFC_DEVICE, ifc_device, API_SENDCOMMAND, SendCommand); M_CB(PATCH_IFC_DEVICE, ifc_device, API_GETATTACHED, GetAttached); M_CB(PATCH_IFC_DEVICE, ifc_device, API_ATTACH, Attach); M_CB(PATCH_IFC_DEVICE, ifc_device, API_DETACH, Detach); M_CB(PATCH_IFC_DEVICE, ifc_device, API_ADVISE, Advise); M_CB(PATCH_IFC_DEVICE, ifc_device, API_UNADVISE, Unadvise); M_CB(PATCH_IFC_DEVICE, ifc_device, API_CREATEVIEW, CreateView); M_CB(PATCH_IFC_DEVICE, ifc_device, API_GETACTIVITY, GetActivity); M_VCB(PATCH_IFC_DEVICE, ifc_device, API_SETNAVIGATIONITEM, SetNavigationItem); M_CB(PATCH_IFC_DEVICE, ifc_device, API_GETDROPSUPPORTED, GetDropSupported); M_CB(PATCH_IFC_DEVICE, ifc_device, API_DROP, Drop); M_CB(PATCH_IFC_DEVICE, ifc_device, API_SETDISPLAYNAME, SetDisplayName); M_CB(PATCH_IFC_DEVICE, ifc_device, API_GETMODEL, GetModel); M_CB(PATCH_IFC_DEVICE, ifc_device, ifc_device::API_GETSTATUS, GetStatus); M_CB(PATCH_IFC_DEVICE, ifc_device, ADDREF, AddRef); M_CB(PATCH_IFC_DEVICE, ifc_device, RELEASE, Release); NEXT_PATCH(PATCH_IFC_DEVICEACTIVITY) M_CB(PATCH_IFC_DEVICEACTIVITY, ifc_deviceactivity, API_GETACTIVE, GetActive); M_CB(PATCH_IFC_DEVICEACTIVITY, ifc_deviceactivity, API_GETCANCELABLE, GetCancelable); M_CB(PATCH_IFC_DEVICEACTIVITY, ifc_deviceactivity, API_GETPROGRESS, GetProgress); M_CB(PATCH_IFC_DEVICEACTIVITY, ifc_deviceactivity, ifc_deviceactivity::API_GETDISPLAYNAME, Activity_GetDisplayName); M_CB(PATCH_IFC_DEVICEACTIVITY, ifc_deviceactivity, ifc_deviceactivity::API_GETSTATUS, Activity_GetStatus); M_CB(PATCH_IFC_DEVICEACTIVITY, ifc_deviceactivity, API_CANCEL, Cancel); M_CB(PATCH_IFC_DEVICEACTIVITY, ifc_deviceactivity, ADDREF, AddRef); M_CB(PATCH_IFC_DEVICEACTIVITY, ifc_deviceactivity, RELEASE, Release); END_PATCH END_MULTIPATCH; #undef CBCLASS