00001
00002
00003 #include <cstdio>
00004
00005
00006 #include <iostream>
00007 #include <typeinfo>
00008 using namespace std;
00009
00010
00011 #include <qapplication.h>
00012 #include <qprocess.h>
00013 #include <qdir.h>
00014 #include <qfile.h>
00015
00016
00017 #include "mythmediamonitor.h"
00018 #include "mythcdrom.h"
00019 #include "mythcontext.h"
00020 #include "mythdialogs.h"
00021 #include "mythconfig.h"
00022
00023 #ifdef USING_DARWIN_DA
00024 #include "mediamonitor-darwin.h"
00025 #endif
00026 #if defined(CONFIG_CYGWIN) || defined(_WIN32)
00027 #include "mediamonitor-windows.h"
00028 #else
00029 #include "mediamonitor-unix.h"
00030 #endif
00031
00032 MediaMonitor *MediaMonitor::c_monitor = NULL;
00033
00034
00035 MonitorThread::MonitorThread(MediaMonitor* pMon, unsigned long interval)
00036 : QThread()
00037 {
00038 m_Monitor = pMon;
00039 m_Interval = interval;
00040 }
00041
00042
00043
00044 void MonitorThread::run(void)
00045 {
00046 while (m_Monitor && m_Monitor->IsActive())
00047 {
00048 m_Monitor->CheckDevices();
00049 msleep(m_Interval);
00050 }
00051 }
00052
00054
00055
00056
00057 MediaMonitor* MediaMonitor::GetMediaMonitor(void)
00058 {
00059 if (c_monitor)
00060 return c_monitor;
00061
00062 #ifdef USING_DARWIN_DA
00063 c_monitor = new MediaMonitorDarwin(NULL, 500, true);
00064 #else
00065 #if defined(CONFIG_CYGWIN) || defined(_WIN32)
00066 c_monitor = new MediaMonitorWindows(NULL, 500, true);
00067 #else
00068 c_monitor = new MediaMonitorUnix(NULL, 500, true);
00069 #endif
00070 #endif
00071
00072 return c_monitor;
00073 }
00074
00075 void MediaMonitor::SetCDSpeed(const char *device, int speed)
00076 {
00077 MediaMonitor *mon = GetMediaMonitor();
00078 if (mon)
00079 {
00080 MythMediaDevice *pMedia = mon->GetMedia(device);
00081 if (pMedia && mon->ValidateAndLock(pMedia))
00082 {
00083 pMedia->setSpeed(speed);
00084 mon->Unlock(pMedia);
00085 return;
00086 }
00087 }
00088
00089 MythCDROM *cd = MythCDROM::get(NULL, device, false, false);
00090 if (cd)
00091 {
00092 cd->setSpeed(device, speed);
00093 delete cd;
00094 }
00095
00096 VERBOSE(VB_MEDIA, QString("MediaMonitor::setSpeed(%1) "
00097 "- Cannot find/create CDROM?") + device);
00098 }
00099
00100
00101 static const QString DevName(MythMediaDevice *d)
00102 {
00103 QString str = d->getVolumeID();
00104
00105 if (str == "")
00106 {
00107 str = d->getDeviceModel();
00108
00109 if (str)
00110 str += " (" + d->getDevicePath() + ')';
00111 else
00112 str = d->getDevicePath();
00113 }
00114
00115
00116
00117
00118
00119 return str;
00120 }
00121
00128 MythMediaDevice * MediaMonitor::selectDrivePopup(const QString label,
00129 bool showMounted)
00130 {
00131 QValueList <MythMediaDevice *> drives;
00132 QValueList <MythMediaDevice *>::iterator it = m_Devices.begin();
00133 QMutexLocker locker(&m_DevicesLock);
00134
00135 for (it = m_Devices.begin(); it != m_Devices.end(); ++it)
00136 {
00137
00138
00139
00140 if (QString(typeid(**it).name()).contains("MythCDROM") ||
00141 (showMounted && (*it)->isMounted()))
00142 drives.append(*it);
00143 }
00144
00145 if (drives.count() == 0)
00146 {
00147 VERBOSE(VB_MEDIA, "MediaMonitor::selectDrivePopup("
00148 + label + ") - No suitable devices");
00149 return NULL;
00150 }
00151
00152 if (drives.count() == 1)
00153 {
00154 VERBOSE(VB_MEDIA, "MediaMonitor::selectDrivePopup("
00155 + label + ") - One suitable device");
00156 return drives.front();
00157 }
00158
00159 QStringList buttonmsgs;
00160 for (it = drives.begin(); it != drives.end(); ++it)
00161 buttonmsgs += DevName(*it);
00162 buttonmsgs += tr("Cancel");
00163 const DialogCode cancelbtn = (DialogCode)
00164 (((int)kDialogCodeButton0) + buttonmsgs.size() - 1);
00165
00166 DialogCode ret = MythPopupBox::ShowButtonPopup(
00167 gContext->GetMainWindow(), "select drive", label,
00168 buttonmsgs, cancelbtn);
00169
00170
00171 if ((kDialogCodeRejected == ret) || (cancelbtn == ret))
00172 return (MythMediaDevice *)-1;
00173
00174 uint idx = MythDialog::CalcItemIndex(ret);
00175 if (idx < drives.count())
00176 return drives[idx];
00177
00178 return NULL;
00179 }
00180
00181
00190 void MediaMonitor::ChooseAndEjectMedia(void)
00191 {
00192 MythMediaDevice *selected;
00193
00194
00195 selected = selectDrivePopup(tr("Select removable media"
00196 " to eject or insert"), true);
00197
00198
00199 if (selected == (MythMediaDevice *) -1)
00200 return;
00201
00202 if (!selected)
00203 {
00204 MythPopupBox::showOkPopup(gContext->GetMainWindow(),
00205 "nothing to eject ",
00206 tr("No devices to eject"));
00207 return;
00208 }
00209
00210 QString dev = DevName(selected);
00211
00212 if (selected->getStatus() == MEDIASTAT_OPEN)
00213 {
00214 VERBOSE(VB_MEDIA,
00215 QString("Disk %1's tray is OPEN. Closing tray").arg(dev));
00216
00217 if (selected->eject(false) != MEDIAERR_OK)
00218 {
00219 QString msg = "Unable to open or close the empty drive %1.\n\n";
00220 msg += "You may have to use the eject button under its tray.";
00221 MythPopupBox::showOkPopup(gContext->GetMainWindow(),
00222 "eject close-tray fail",
00223 tr(msg).arg(dev));
00224 }
00225
00226 return;
00227 }
00228
00229 if (selected->isMounted(true))
00230 {
00231 VERBOSE(VB_MEDIA, QString("Disk %1 is mounted? Unmounting").arg(dev));
00232 selected->unmount();
00233
00234 if (selected->isMounted(true))
00235 {
00236 MythPopupBox::showOkPopup(gContext->GetMainWindow(),
00237 "eject unmount fail",
00238 tr("Failed to unmount %1").arg(dev));
00239 return;
00240 }
00241 }
00242
00243 VERBOSE(VB_MEDIA, QString("Unlocking disk %1, then eject()ing").arg(dev));
00244 selected->unlock();
00245
00246 MediaError err = selected->eject();
00247
00248 if (err == MEDIAERR_UNSUPPORTED)
00249 {
00250 MythPopupBox::showOkPopup(gContext->GetMainWindow(), "eject success",
00251 tr("You may safely remove %1").arg(dev));
00252 }
00253 else if (err == MEDIAERR_FAILED)
00254 {
00255 MythPopupBox::showOkPopup(gContext->GetMainWindow(), "eject fail",
00256 tr("Failed to eject %1").arg(dev));
00257 }
00258 }
00259
00266 MediaMonitor::MediaMonitor(QObject* par, unsigned long interval,
00267 bool allowEject)
00268 : QObject(par), m_Active(false), m_Thread(NULL),
00269 m_MonitorPollingInterval(interval), m_AllowEject(allowEject)
00270 {
00271
00272
00273 m_StartThread = gContext->GetNumSetting("MonitorDrives");
00274
00275
00276 m_SendEvent = gContext->GetNumSetting("MediaChangeEvents");
00277
00278
00279 QString ignore = gContext->GetSetting("IgnoreDevices", "");
00280
00281 if (ignore.length())
00282 m_IgnoreList = QStringList::split(',', ignore);
00283 else
00284 m_IgnoreList = QStringList::QStringList();
00285
00286 if (m_StartThread)
00287 VERBOSE(VB_MEDIA, "Creating MediaMonitor, SendEvents="
00288 + (m_SendEvent?QString("true"):QString("false")));
00289 else
00290 #ifdef USING_DARWIN_DA
00291 VERBOSE(VB_MEDIA, "MediaMonitor is disabled. Eject will not work");
00292 #else
00293 VERBOSE(VB_MEDIA,
00294 "Creating inactive MediaMonitor and static device list");
00295 #endif
00296 VERBOSE(VB_MEDIA, "IgnoreDevices=" + ignore);
00297
00298
00299 QStringList::Iterator dev;
00300 for (dev = m_IgnoreList.begin(); dev != m_IgnoreList.end(); ++dev)
00301 {
00302 QFileInfo *fi = new QFileInfo(*dev);
00303
00304 if (fi && fi->isSymLink())
00305 {
00306 QString target = fi->readLink();
00307
00308 if (m_IgnoreList.grep(target).isEmpty())
00309 {
00310 VERBOSE(VB_MEDIA, "Also ignoring " + target +
00311 " (symlinked from " + *dev + ").");
00312 m_IgnoreList += target;
00313 }
00314 }
00315 }
00316 }
00317
00318 MediaMonitor::~MediaMonitor()
00319 {
00320 delete m_Thread;
00321 }
00322
00331 bool MediaMonitor::RemoveDevice(const QString &dev)
00332 {
00333 QMutexLocker locker(&m_DevicesLock);
00334
00335 QValueList<MythMediaDevice*>::iterator it;
00336 for (it = m_Devices.begin(); it != m_Devices.end(); it++)
00337 {
00338 if ((*it)->getDevicePath() == dev)
00339 {
00340 if (m_UseCount[*it] == 0)
00341 {
00342 (*it)->deleteLater();
00343 m_Devices.remove(it);
00344 m_UseCount.remove(*it);
00345 }
00346 else
00347 {
00348
00349
00350 disconnect(*it);
00351 m_RemovedDevices.append(*it);
00352 m_Devices.remove(it);
00353 }
00354
00355 return true;
00356 }
00357 }
00358 return false;
00359 }
00360
00364 void MediaMonitor::CheckDevices(void)
00365 {
00366
00367 CheckDeviceNotifications();
00368
00369 QValueList<MythMediaDevice*>::iterator itr = m_Devices.begin();
00370 MythMediaDevice* pDev;
00371 while (itr != m_Devices.end())
00372 {
00373 pDev = *itr;
00374 if (pDev)
00375 pDev->checkMedia();
00376 itr++;
00377 }
00378 }
00379
00383 void MediaMonitor::StartMonitoring(void)
00384 {
00385
00386 if (m_Active)
00387 return;
00388
00389 if (!m_StartThread)
00390 return;
00391
00392 if (!m_Thread)
00393 m_Thread = new MonitorThread(this, m_MonitorPollingInterval);
00394
00395 VERBOSE(VB_MEDIA, "Starting MediaMonitor");
00396 m_Active = true;
00397 m_Thread->start();
00398 }
00399
00403 void MediaMonitor::StopMonitoring(void)
00404 {
00405
00406 if (!m_Active)
00407 return;
00408
00409 VERBOSE(VB_MEDIA, "Stopping MediaMonitor");
00410 m_Active = false;
00411 m_Thread->wait();
00412 }
00413
00424 bool MediaMonitor::ValidateAndLock(MythMediaDevice *pMedia)
00425 {
00426 QMutexLocker locker(&m_DevicesLock);
00427
00428 if (!m_Devices.contains(pMedia))
00429 return false;
00430
00431 m_UseCount[pMedia]++;
00432
00433 return true;
00434 }
00435
00441 void MediaMonitor::Unlock(MythMediaDevice *pMedia)
00442 {
00443 QMutexLocker locker(&m_DevicesLock);
00444
00445 if (!m_UseCount.contains(pMedia))
00446 return;
00447
00448 m_UseCount[pMedia]--;
00449
00450 if (m_UseCount[pMedia] == 0 && m_RemovedDevices.contains(pMedia))
00451 {
00452 m_RemovedDevices.remove(pMedia);
00453 m_UseCount.remove(pMedia);
00454 pMedia->deleteLater();
00455 }
00456 }
00457
00464 MythMediaDevice* MediaMonitor::GetMedia(const QString& path)
00465 {
00466 QMutexLocker locker(&m_DevicesLock);
00467
00468 QValueList<MythMediaDevice*>::iterator it = m_Devices.begin();
00469 for (;it != m_Devices.end(); it++)
00470 {
00471 if ((*it)->isSameDevice(path) &&
00472 (((*it)->getStatus() == MEDIASTAT_USEABLE) ||
00473 ((*it)->getStatus() == MEDIASTAT_MOUNTED) ||
00474 ((*it)->getStatus() == MEDIASTAT_NOTMOUNTED)))
00475 {
00476 return(*it);
00477 }
00478 }
00479
00480 return NULL;
00481 }
00482
00489 QString MediaMonitor::GetMountPath(const QString& devPath)
00490 {
00491 QString mountPath;
00492
00493 if (c_monitor)
00494 {
00495 MythMediaDevice *pMedia = c_monitor->GetMedia(devPath);
00496 if (pMedia && c_monitor->ValidateAndLock(pMedia))
00497 {
00498 mountPath = pMedia->getMountPath();
00499 c_monitor->Unlock(pMedia);
00500 }
00501 }
00502
00503 return mountPath;
00504 }
00505
00524 QValueList<MythMediaDevice*> MediaMonitor::GetMedias(MediaType mediatype)
00525 {
00526 QMutexLocker locker(&m_DevicesLock);
00527
00528 QValueList<MythMediaDevice*> medias;
00529
00530 QValueList<MythMediaDevice*>::iterator it = m_Devices.begin();
00531 for (;it != m_Devices.end(); it++)
00532 {
00533 if (((*it)->getMediaType() == mediatype) &&
00534 (((*it)->getStatus() == MEDIASTAT_USEABLE) ||
00535 ((*it)->getStatus() == MEDIASTAT_MOUNTED) ||
00536 ((*it)->getStatus() == MEDIASTAT_NOTMOUNTED)))
00537 {
00538 medias.push_back(*it);
00539 }
00540 }
00541
00542 return medias;
00543 }
00544
00548 void MediaMonitor::MonitorRegisterExtensions(uint mediatype,
00549 const QString &extensions)
00550 {
00551 VERBOSE(VB_IMPORTANT, QString("MonitorRegisterExtensions(0x%1, %2)")
00552 .arg(mediatype, 0, 16).arg(extensions));
00553
00554 QValueList<MythMediaDevice*>::iterator it = m_Devices.begin();
00555 for (; it != m_Devices.end(); ++it)
00556 {
00557 if (*it)
00558 (*it)->RegisterMediaExtensions(mediatype, extensions);
00559 }
00560 }
00561
00562
00563 void MediaMonitor::mediaStatusChanged(MediaStatus oldStatus,
00564 MythMediaDevice* pMedia)
00565 {
00566
00567 if (!m_Active)
00568 return;
00569
00570 MediaStatus stat = pMedia->getStatus();
00571 QString msg = QString(" (%1, %2 -> %3)")
00572 .arg(pMedia->MediaTypeString())
00573 .arg(MythMediaDevice::MediaStatusStrings[oldStatus])
00574 .arg(MythMediaDevice::MediaStatusStrings[stat]);
00575
00576
00577
00578
00579 if (m_SendEvent && stat != MEDIASTAT_ERROR && stat != MEDIASTAT_UNKNOWN)
00580 {
00581 VERBOSE(VB_MEDIA, "Posting MediaEvent" + msg);
00582
00583
00584
00585
00586
00587 QApplication::sendEvent((QObject*)gContext->GetMainWindow(),
00588 new MediaEvent(stat, pMedia));
00589 }
00590 else
00591 VERBOSE(VB_MEDIA, "Media status changed, but not sending event" + msg);
00592
00593
00594 if (stat == MEDIASTAT_OPEN || stat == MEDIASTAT_NODISK
00595 || stat == MEDIASTAT_UNPLUGGED)
00596 {
00597 pMedia->clearData();
00598 }
00599 }
00600
00604 bool MediaMonitor::shouldIgnore(MythMediaDevice* device)
00605 {
00606 if (m_IgnoreList.contains(device->getMountPath()) ||
00607 m_IgnoreList.contains(device->getRealDevice())||
00608 m_IgnoreList.contains(device->getDevicePath()) )
00609 {
00610 VERBOSE(VB_MEDIA, "Ignoring device: " + device->getDevicePath());
00611 return true;
00612 }
00613 #if 0
00614 else
00615 {
00616 VERBOSE(VB_MEDIA, "Not ignoring: " + device->getDevicePath()
00617 + " / " + device->getMountPath());
00618 VERBOSE(VB_MEDIA, "Paths not in: " + m_IgnoreList.join(", "));
00619 }
00620 #endif
00621
00622 return false;
00623 }
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636
00637
00638 QString MediaMonitor::defaultDevice(QString dbSetting,
00639 QString label, char *hardCodedDefault)
00640 {
00641 QString device = gContext->GetSetting(dbSetting);
00642
00643
00644 if (device.isEmpty() || device == "default")
00645 {
00646 device = hardCodedDefault;
00647
00648 if (!c_monitor)
00649 c_monitor = GetMediaMonitor();
00650
00651 if (c_monitor)
00652 {
00653 MythMediaDevice *d = c_monitor->selectDrivePopup(label);
00654
00655 if (d == (MythMediaDevice *) -1)
00656 d = NULL;
00657
00658 if (d)
00659 device = d->getDevicePath();
00660 }
00661 }
00662
00663 #if 0
00664
00665 extern unsigned int print_verbose_messages;
00666 if (print_verbose_messages & 0x00800000)
00667 {
00668 QDateTime dtmp = QDateTime::currentDateTime();
00669 QString dtime = dtmp.toString("yyyy-MM-dd hh:mm:ss.zzz");
00670 cout << dtime << " MediaMonitor::defaultDevice('" << dbSetting.ascii()
00671 << "', '" << label.ascii() << "'') returning '"
00672 << device.ascii() << "'" << endl;
00673 }
00674 #endif
00675
00676 return device;
00677 }
00678
00682 QString MediaMonitor::defaultCDdevice()
00683 {
00684 return defaultDevice("CDDevice", tr("Select a CD drive"), "/dev/cdrom");
00685 }
00686
00690 QString MediaMonitor::defaultVCDdevice()
00691 {
00692 return defaultDevice("VCDDeviceLocation",
00693 tr("Select a VCD drive"), "/dev/cdrom");
00694 }
00695
00699 QString MediaMonitor::defaultDVDdevice()
00700 {
00701 return defaultDevice("DVDDeviceLocation",
00702 tr("Select a DVD drive"), "/dev/dvd");
00703 }
00704
00708 QString MediaMonitor::defaultCDWriter()
00709 {
00710 return defaultDevice("CDWriterDeviceLocation",
00711 tr("Select a CD writer"), "/dev/cdrom");
00712 }
00713
00720 QString MediaMonitor::defaultDVDWriter()
00721 {
00722 QString device = defaultDevice("MythArchiveDVDLocation",
00723 tr("Select a DVD writer"), "/dev/dvd");
00724
00725 return device;
00726 }
00727
00728
00732 const QString MediaMonitor::listDevices(void)
00733 {
00734 QValueList<MythMediaDevice*>::const_iterator dev;
00735 QStringList list;
00736
00737 for (dev = m_Devices.begin(); dev != m_Devices.end(); ++dev)
00738 {
00739 QString devStr;
00740 QString model = (*dev)->getDeviceModel();
00741 QString path = (*dev)->getDevicePath();
00742 QString real = (*dev)->getRealDevice();
00743
00744 if (path != real)
00745 devStr += path + "->";
00746 devStr += real;
00747
00748 if (!model.length())
00749 model = "unknown";
00750 devStr += " (" + model + ")";
00751
00752 list += devStr;
00753 }
00754
00755 return list.join(", ");
00756 }