00001
00002
00003 #include <cstdio>
00004
00005
00006 #include <iostream>
00007 #include <typeinfo>
00008 using namespace std;
00009
00010
00011 #include <QCoreApplication>
00012 #include <QFile>
00013 #include <QList>
00014 #include <QDir>
00015
00016
00017 #include "mythmediamonitor.h"
00018 #include "mythcdrom.h"
00019 #include "mythcorecontext.h"
00020 #include "mythdialogs.h"
00021 #include "mythconfig.h"
00022 #include "mythdialogbox.h"
00023 #include "mythmiscutil.h"
00024 #include "mythlogging.h"
00025 #include "mythmainwindow.h"
00026
00027 #ifdef USING_DARWIN_DA
00028 #include "mediamonitor-darwin.h"
00029 #elif CONFIG_CYGWIN || defined(_WIN32)
00030 #include "mediamonitor-windows.h"
00031 #else
00032 #include "mediamonitor-unix.h"
00033 #endif
00034
00035 MediaMonitor *MediaMonitor::c_monitor = NULL;
00036
00037
00038 MonitorThread::MonitorThread(MediaMonitor* pMon, unsigned long interval) :
00039 MThread("Monitor")
00040 {
00041 m_Monitor = pMon;
00042 m_Interval = interval;
00043 }
00044
00045
00046
00047 void MonitorThread::run(void)
00048 {
00049 RunProlog();
00050 while (m_Monitor && m_Monitor->IsActive())
00051 {
00052 m_Monitor->CheckDevices();
00053 msleep(m_Interval);
00054 }
00055 RunEpilog();
00056 }
00057
00059
00060
00061 #define MONITOR_INTERVAL 5000
00062
00063 MediaMonitor* MediaMonitor::GetMediaMonitor(void)
00064 {
00065 if (c_monitor)
00066 return c_monitor;
00067
00068 #ifdef USING_DARWIN_DA
00069 c_monitor = new MediaMonitorDarwin(NULL, MONITOR_INTERVAL, true);
00070 #else
00071 #if CONFIG_CYGWIN || defined(_WIN32)
00072 c_monitor = new MediaMonitorWindows(NULL, MONITOR_INTERVAL, true);
00073 #else
00074 c_monitor = new MediaMonitorUnix(NULL, MONITOR_INTERVAL, true);
00075 #endif
00076 #endif
00077
00078 return c_monitor;
00079 }
00080
00081 void MediaMonitor::SetCDSpeed(const char *device, int speed)
00082 {
00083 MediaMonitor *mon = GetMediaMonitor();
00084 if (mon)
00085 {
00086 MythMediaDevice *pMedia = mon->GetMedia(device);
00087 if (pMedia && mon->ValidateAndLock(pMedia))
00088 {
00089 pMedia->setSpeed(speed);
00090 mon->Unlock(pMedia);
00091 return;
00092 }
00093 }
00094
00095 MythCDROM *cd = MythCDROM::get(NULL, device, false, false);
00096 if (cd)
00097 {
00098 cd->setSpeed(device, speed);
00099 delete cd;
00100 return;
00101 }
00102
00103 LOG(VB_MEDIA, LOG_INFO,
00104 QString("MediaMonitor::setSpeed(%1) - Cannot find/create CDROM?")
00105 .arg(device));
00106 }
00107
00108
00109 static const QString DevName(MythMediaDevice *d)
00110 {
00111 QString str = d->getVolumeID();
00112
00113 if (str.isEmpty())
00114 {
00115 str = d->getDeviceModel();
00116
00117 if (!str.isEmpty())
00118 str += " (" + d->getDevicePath() + ')';
00119 else
00120 str = d->getDevicePath();
00121 }
00122
00123
00124
00125
00126
00127 return str;
00128 }
00129
00135 QList<MythMediaDevice*> MediaMonitor::GetRemovable(bool showMounted)
00136 {
00137 QList <MythMediaDevice *> drives;
00138 QList <MythMediaDevice *>::iterator it;
00139 QMutexLocker locker(&m_DevicesLock);
00140
00141 for (it = m_Devices.begin(); it != m_Devices.end(); ++it)
00142 {
00143
00144
00145
00146 if (QString(typeid(**it).name()).contains("MythCDROM") ||
00147 (showMounted && (*it)->isMounted(false)))
00148 drives.append(*it);
00149 }
00150
00151 return drives;
00152 }
00153
00159 MythMediaDevice * MediaMonitor::selectDrivePopup(const QString label,
00160 bool showMounted)
00161 {
00162 QList <MythMediaDevice *> drives = GetRemovable(showMounted);
00163
00164 if (drives.count() == 0)
00165 {
00166 QString msg = "MediaMonitor::selectDrivePopup() ";
00167 if (m_StartThread)
00168 msg += "- no removable devices";
00169 else
00170 msg += "MonitorDrives is disabled - no device list";
00171
00172 LOG(VB_MEDIA, LOG_INFO, msg);
00173 return NULL;
00174 }
00175
00176 if (drives.count() == 1)
00177 {
00178 LOG(VB_MEDIA, LOG_INFO,
00179 "MediaMonitor::selectDrivePopup(" + label +
00180 ") - One suitable device");
00181 return drives.front();
00182 }
00183
00184 QStringList buttonmsgs;
00185 QList <MythMediaDevice *>::iterator it;
00186 for (it = drives.begin(); it != drives.end(); ++it)
00187 buttonmsgs += DevName(*it);
00188 buttonmsgs += tr("Cancel");
00189 const DialogCode cancelbtn = (DialogCode)
00190 (((int)kDialogCodeButton0) + buttonmsgs.size() - 1);
00191
00192 DialogCode ret = MythPopupBox::ShowButtonPopup(
00193 GetMythMainWindow(), "select drive", label,
00194 buttonmsgs, cancelbtn);
00195
00196
00197 if ((kDialogCodeRejected == ret) || (cancelbtn == ret))
00198 return (MythMediaDevice *)-1;
00199
00200 int idx = MythDialog::CalcItemIndex(ret);
00201 if (idx < drives.count())
00202 return drives[idx];
00203
00204 return NULL;
00205 }
00206
00207
00216 void MediaMonitor::ChooseAndEjectMedia(void)
00217 {
00218 MythMediaDevice *selected;
00219
00220
00221 selected = selectDrivePopup(tr("Select removable media"
00222 " to eject or insert"), true);
00223
00224
00225 if (selected == (MythMediaDevice *) -1)
00226 return;
00227
00228 if (!selected)
00229 {
00230 ShowOkPopup(tr("No devices to eject"));
00231 return;
00232 }
00233
00234 AttemptEject(selected);
00235 }
00236
00237 void MediaMonitor::AttemptEject(MythMediaDevice *device)
00238 {
00239 QString dev = DevName(device);
00240
00241 if (device->getStatus() == MEDIASTAT_OPEN)
00242 {
00243 LOG(VB_MEDIA, LOG_INFO,
00244 QString("Disk %1's tray is OPEN. Closing tray").arg(dev));
00245
00246 if (device->eject(false) != MEDIAERR_OK)
00247 {
00248 QString msg = QObject::tr(
00249 "Unable to open or close the empty drive %1.\n\n"
00250 "You may have to use the eject button under its tray.");
00251 ShowOkPopup(msg.arg(dev));
00252 }
00253
00254 return;
00255 }
00256
00257 if (device->isMounted())
00258 {
00259 LOG(VB_MEDIA, LOG_INFO,
00260 QString("Disk %1 is mounted? Unmounting").arg(dev));
00261 device->unmount();
00262
00263 if (device->isMounted())
00264 {
00265 ShowOkPopup(tr("Failed to unmount %1").arg(dev));
00266 return;
00267 }
00268 }
00269
00270 LOG(VB_MEDIA, LOG_INFO,
00271 QString("Unlocking disk %1, then ejecting").arg(dev));
00272 device->unlock();
00273
00274 MythMediaError err = device->eject();
00275
00276 if (err == MEDIAERR_UNSUPPORTED)
00277 {
00278
00279
00280 ShowOkPopup(tr("You may safely remove %1").arg(dev));
00281 }
00282 else if (err == MEDIAERR_FAILED)
00283 {
00284 ShowOkPopup(tr("Failed to eject %1").arg(dev));
00285 }
00286 }
00287
00294 MediaMonitor::MediaMonitor(QObject* par, unsigned long interval,
00295 bool allowEject)
00296 : QObject(par), m_Active(false), m_Thread(NULL),
00297 m_MonitorPollingInterval(interval), m_AllowEject(allowEject)
00298 {
00299
00300
00301 m_StartThread = gCoreContext->GetNumSetting("MonitorDrives");
00302
00303
00304 QString ignore = gCoreContext->GetSetting("IgnoreDevices", "");
00305
00306 if (ignore.length())
00307 m_IgnoreList = ignore.split(',', QString::SkipEmptyParts);
00308 else
00309 m_IgnoreList = QStringList();
00310
00311 if (m_StartThread)
00312 LOG(VB_MEDIA, LOG_NOTICE, "Creating MediaMonitor");
00313 else
00314 #ifdef USING_DARWIN_DA
00315 LOG(VB_MEDIA, LOG_INFO,
00316 "MediaMonitor is disabled. Eject will not work");
00317 #else
00318 LOG(VB_MEDIA, LOG_INFO,
00319 "Creating inactive MediaMonitor and static device list");
00320 #endif
00321 LOG(VB_MEDIA, LOG_INFO, "IgnoreDevices=" + ignore);
00322
00323
00324 QStringList::Iterator dev;
00325 for (dev = m_IgnoreList.begin(); dev != m_IgnoreList.end(); ++dev)
00326 {
00327 QFileInfo *fi = new QFileInfo(*dev);
00328
00329 if (fi && fi->isSymLink())
00330 {
00331 QString target = getSymlinkTarget(*dev);
00332
00333 if (m_IgnoreList.filter(target).isEmpty())
00334 {
00335 LOG(VB_MEDIA, LOG_INFO,
00336 "Also ignoring " + target + " (symlinked from " +
00337 *dev + ").");
00338 m_IgnoreList += target;
00339 }
00340 }
00341 delete fi;
00342 }
00343 }
00344
00345 void MediaMonitor::deleteLater(void)
00346 {
00347 if (m_Thread)
00348 {
00349 StopMonitoring();
00350 delete m_Thread;
00351 m_Thread = NULL;
00352 }
00353 QObject::deleteLater();
00354 }
00355
00364 bool MediaMonitor::RemoveDevice(const QString &dev)
00365 {
00366 QMutexLocker locker(&m_DevicesLock);
00367
00368 QList<MythMediaDevice*>::iterator it;
00369 for (it = m_Devices.begin(); it != m_Devices.end(); ++it)
00370 {
00371 if ((*it)->getDevicePath() == dev)
00372 {
00373 if (m_UseCount[*it] == 0)
00374 {
00375 (*it)->deleteLater();
00376 m_Devices.erase(it);
00377 m_UseCount.remove(*it);
00378 }
00379 else
00380 {
00381
00382
00383 disconnect(*it);
00384 m_RemovedDevices.append(*it);
00385 m_Devices.erase(it);
00386 }
00387
00388 return true;
00389 }
00390 }
00391 return false;
00392 }
00393
00397 void MediaMonitor::CheckDevices(void)
00398 {
00399
00400 CheckDeviceNotifications();
00401
00402 QList<MythMediaDevice*>::iterator itr = m_Devices.begin();
00403 MythMediaDevice* pDev;
00404 while (itr != m_Devices.end())
00405 {
00406 pDev = *itr;
00407 if (pDev)
00408 pDev->checkMedia();
00409 ++itr;
00410 }
00411 }
00412
00416 void MediaMonitor::StartMonitoring(void)
00417 {
00418
00419 if (m_Active)
00420 return;
00421
00422 if (!m_StartThread)
00423 return;
00424
00425 if (!m_Thread)
00426 m_Thread = new MonitorThread(this, m_MonitorPollingInterval);
00427
00428 qRegisterMetaType<MythMediaStatus>("MythMediaStatus");
00429
00430 LOG(VB_MEDIA, LOG_NOTICE, "Starting MediaMonitor");
00431 m_Active = true;
00432 m_Thread->start();
00433 }
00434
00438 void MediaMonitor::StopMonitoring(void)
00439 {
00440
00441 if (!m_Active)
00442 return;
00443
00444 LOG(VB_MEDIA, LOG_NOTICE, "Stopping MediaMonitor");
00445 m_Active = false;
00446 m_Thread->wait();
00447 }
00448
00459 bool MediaMonitor::ValidateAndLock(MythMediaDevice *pMedia)
00460 {
00461 QMutexLocker locker(&m_DevicesLock);
00462
00463 if (!m_Devices.contains(pMedia))
00464 return false;
00465
00466 m_UseCount[pMedia]++;
00467
00468 return true;
00469 }
00470
00476 void MediaMonitor::Unlock(MythMediaDevice *pMedia)
00477 {
00478 QMutexLocker locker(&m_DevicesLock);
00479
00480 if (!m_UseCount.contains(pMedia))
00481 return;
00482
00483 m_UseCount[pMedia]--;
00484
00485 if (m_UseCount[pMedia] == 0 && m_RemovedDevices.contains(pMedia))
00486 {
00487 m_RemovedDevices.removeAll(pMedia);
00488 m_UseCount.remove(pMedia);
00489 pMedia->deleteLater();
00490 }
00491 }
00492
00499 MythMediaDevice* MediaMonitor::GetMedia(const QString& path)
00500 {
00501 QMutexLocker locker(&m_DevicesLock);
00502
00503 QList<MythMediaDevice*>::iterator it = m_Devices.begin();
00504 for (;it != m_Devices.end(); ++it)
00505 {
00506 if ((*it)->isSameDevice(path) &&
00507 (((*it)->getStatus() == MEDIASTAT_USEABLE) ||
00508 ((*it)->getStatus() == MEDIASTAT_MOUNTED) ||
00509 ((*it)->getStatus() == MEDIASTAT_NOTMOUNTED)))
00510 {
00511 return(*it);
00512 }
00513 }
00514
00515 return NULL;
00516 }
00517
00524 QString MediaMonitor::GetMountPath(const QString& devPath)
00525 {
00526 QString mountPath;
00527
00528 if (c_monitor)
00529 {
00530 MythMediaDevice *pMedia = c_monitor->GetMedia(devPath);
00531 if (pMedia && c_monitor->ValidateAndLock(pMedia))
00532 {
00533 mountPath = pMedia->getMountPath();
00534 c_monitor->Unlock(pMedia);
00535 }
00536
00537
00538 else
00539 {
00540 pMedia = MythCDROM::get(NULL, devPath.toAscii(), true, false);
00541 if (pMedia && pMedia->findMountPath())
00542 mountPath = pMedia->getMountPath();
00543 else
00544 LOG(VB_MEDIA, LOG_INFO,
00545 "MediaMonitor::GetMountPath() - failed");
00546
00547 }
00548 }
00549
00550 return mountPath;
00551 }
00552
00571 QList<MythMediaDevice*> MediaMonitor::GetMedias(MythMediaType mediatype)
00572 {
00573 QMutexLocker locker(&m_DevicesLock);
00574
00575 QList<MythMediaDevice*> medias;
00576
00577 QList<MythMediaDevice*>::iterator it = m_Devices.begin();
00578 for (;it != m_Devices.end(); ++it)
00579 {
00580 if (((*it)->getMediaType() & mediatype) &&
00581 (((*it)->getStatus() == MEDIASTAT_USEABLE) ||
00582 ((*it)->getStatus() == MEDIASTAT_MOUNTED) ||
00583 ((*it)->getStatus() == MEDIASTAT_NOTMOUNTED)))
00584 {
00585 medias.push_back(*it);
00586 }
00587 }
00588
00589 return medias;
00590 }
00591
00595 void MediaMonitor::MonitorRegisterExtensions(uint mediatype,
00596 const QString &extensions)
00597 {
00598 LOG(VB_GENERAL, LOG_DEBUG,
00599 QString("MonitorRegisterExtensions(0x%1, %2)")
00600 .arg(mediatype, 0, 16).arg(extensions));
00601
00602 QList<MythMediaDevice*>::iterator it = m_Devices.begin();
00603 for (; it != m_Devices.end(); ++it)
00604 {
00605 if (*it)
00606 (*it)->RegisterMediaExtensions(mediatype, extensions);
00607 }
00608 }
00609
00610 void MediaMonitor::RegisterMediaHandler(const QString &destination,
00611 const QString &description,
00612 const QString &key,
00613 void (*callback)
00614 (MythMediaDevice*),
00615 int mediaType,
00616 const QString &extensions)
00617 {
00618 if (m_handlerMap.count(destination) == 0)
00619 {
00620 MHData mhd = { callback, mediaType, destination, description };
00621 QString msg = MythMediaDevice::MediaTypeString((MythMediaType)mediaType);
00622
00623 if (extensions.length())
00624 msg += QString(", ext(%1)").arg(extensions);
00625
00626 LOG(VB_MEDIA, LOG_INFO,
00627 "Registering '" + destination + "' as a media handler for " +
00628 msg);
00629
00630 m_handlerMap[destination] = mhd;
00631
00632 if (extensions.length())
00633 MonitorRegisterExtensions(mediaType, extensions);
00634 }
00635 else
00636 {
00637 LOG(VB_GENERAL, LOG_INFO,
00638 destination + " is already registered as a media handler.");
00639 }
00640 }
00641
00649 void MediaMonitor::JumpToMediaHandler(MythMediaDevice* pMedia)
00650 {
00651 QList<MHData> handlers;
00652 QMap<QString, MHData>::Iterator itr = m_handlerMap.begin();
00653
00654 while (itr != m_handlerMap.end())
00655 {
00656 if (((*itr).MythMediaType & (int)pMedia->getMediaType()))
00657 {
00658 LOG(VB_GENERAL, LOG_NOTICE,
00659 QString("Found a handler for %1 - '%2'")
00660 .arg(pMedia->MediaTypeString()) .arg(itr.key()));
00661 handlers.append(*itr);
00662 }
00663 itr++;
00664 }
00665
00666 if (handlers.empty())
00667 {
00668 LOG(VB_MEDIA, LOG_INFO, "No media handler found for event type");
00669 return;
00670 }
00671
00672
00673
00674
00675 int selected = 0;
00676
00677 handlers.at(selected).callback(pMedia);
00678 }
00679
00684 void MediaMonitor::mediaStatusChanged(MythMediaStatus oldStatus,
00685 MythMediaDevice* pMedia)
00686 {
00687
00688 if (!m_Active)
00689 return;
00690
00691 MythMediaStatus stat = pMedia->getStatus();
00692 QString msg = QString(" (%1, %2 -> %3)")
00693 .arg(pMedia->MediaTypeString())
00694 .arg(MythMediaDevice::MediaStatusStrings[oldStatus])
00695 .arg(MythMediaDevice::MediaStatusStrings[stat]);
00696
00697
00698
00699
00700 if (stat != MEDIASTAT_ERROR && stat != MEDIASTAT_UNKNOWN)
00701 {
00702
00703 QEvent *e = new MythMediaEvent(stat, pMedia);
00704
00705 LOG(VB_MEDIA, LOG_INFO, "Posting MediaEvent" + msg);
00706
00707
00708
00709
00710
00711 QCoreApplication::sendEvent((QObject*)GetMythMainWindow(), e);
00712 delete e;
00713 }
00714 else
00715 LOG(VB_MEDIA, LOG_INFO,
00716 "Media status changed, but not sending event" + msg);
00717
00718
00719 if (stat == MEDIASTAT_OPEN || stat == MEDIASTAT_NODISK
00720 || stat == MEDIASTAT_UNPLUGGED)
00721 {
00722 pMedia->clearData();
00723 }
00724 }
00725
00729 bool MediaMonitor::shouldIgnore(const MythMediaDevice* device)
00730 {
00731 if (m_IgnoreList.contains(device->getMountPath()) ||
00732 m_IgnoreList.contains(device->getRealDevice())||
00733 m_IgnoreList.contains(device->getDevicePath()) )
00734 {
00735 LOG(VB_MEDIA, LOG_INFO,
00736 "Ignoring device: " + device->getDevicePath());
00737 return true;
00738 }
00739 #if 0
00740 else
00741 {
00742 LOG(VB_MEDIA, LOG_DEBUG,
00743 "Not ignoring: " + device->getDevicePath() + " / " +
00744 device->getMountPath());
00745 LOG(VB_MEDIA, LOG_DEBUG,
00746 "Paths not in: " + m_IgnoreList.join(", "));
00747 }
00748 #endif
00749
00750 return false;
00751 }
00752
00757 bool MediaMonitor::eventFilter(QObject *obj, QEvent *event)
00758 {
00759 if (event->type() == MythMediaEvent::kEventType)
00760 {
00761 MythMediaDevice *pDev = ((MythMediaEvent*)event)->getDevice();
00762
00763 if (!pDev)
00764 {
00765 LOG(VB_GENERAL, LOG_ALERT,
00766 "MediaMonitor::eventFilter() got a bad media event?");
00767 return true;
00768 }
00769
00770 if (pDev->isUsable())
00771 JumpToMediaHandler(pDev);
00772 else
00773 {
00774
00775
00776
00777 QMap<QString, MHData>::Iterator itr = m_handlerMap.begin();
00778 while (itr != m_handlerMap.end())
00779 {
00780 if ((*itr).MythMediaType & (int)pDev->getMediaType())
00781 (*itr).callback(pDev);
00782 itr++;
00783 }
00784 }
00785
00786 return false;
00787 }
00788
00789
00790 return QObject::eventFilter(obj, event);
00791 }
00792
00793
00794
00795
00796
00797
00798
00799
00800
00801
00802
00803
00804
00805 QString MediaMonitor::defaultDevice(QString dbSetting,
00806 QString label,
00807 const char *hardCodedDefault)
00808 {
00809 QString device = gCoreContext->GetSetting(dbSetting);
00810
00811 LOG(VB_MEDIA, LOG_DEBUG,
00812 QString("MediaMonitor::defaultDevice(%1,..,%2) dbSetting='%3'")
00813 .arg(dbSetting).arg(hardCodedDefault).arg(device));
00814
00815
00816 if (device.isEmpty() || device == "default")
00817 {
00818 device = hardCodedDefault;
00819
00820 if (!c_monitor)
00821 c_monitor = GetMediaMonitor();
00822
00823 if (c_monitor)
00824 {
00825 MythMediaDevice *d = c_monitor->selectDrivePopup(label);
00826
00827 if (d == (MythMediaDevice *) -1)
00828 d = NULL;
00829
00830 if (d && c_monitor->ValidateAndLock(d))
00831 {
00832 device = d->getDevicePath();
00833 c_monitor->Unlock(d);
00834 }
00835 }
00836 }
00837
00838 LOG(VB_MEDIA, LOG_DEBUG,
00839 "MediaMonitor::defaultDevice() returning " + device);
00840 return device;
00841 }
00842
00846 QString MediaMonitor::defaultCDdevice()
00847 {
00848 return defaultDevice("CDDevice", tr("Select a CD drive"), DEFAULT_CD);
00849 }
00850
00854 QString MediaMonitor::defaultVCDdevice()
00855 {
00856 return defaultDevice("VCDDeviceLocation",
00857 tr("Select a VCD drive"), DEFAULT_CD);
00858 }
00859
00863 QString MediaMonitor::defaultDVDdevice()
00864 {
00865 return defaultDevice("DVDDeviceLocation",
00866 tr("Select a DVD drive"), DEFAULT_DVD);
00867 }
00868
00872 QString MediaMonitor::defaultCDWriter()
00873 {
00874 return defaultDevice("CDWriterDeviceLocation",
00875 tr("Select a CD writer"), DEFAULT_CD);
00876 }
00877
00884 QString MediaMonitor::defaultDVDWriter()
00885 {
00886 QString device = defaultDevice("MythArchiveDVDLocation",
00887 tr("Select a DVD writer"), DEFAULT_DVD);
00888
00889 return device;
00890 }
00891
00892
00896 const QString MediaMonitor::listDevices(void)
00897 {
00898 QList<MythMediaDevice*>::const_iterator dev;
00899 QStringList list;
00900
00901 for (dev = m_Devices.begin(); dev != m_Devices.end(); ++dev)
00902 {
00903 QString devStr;
00904 QString model = (*dev)->getDeviceModel();
00905 QString path = (*dev)->getDevicePath();
00906 QString real = (*dev)->getRealDevice();
00907
00908 if (path != real)
00909 devStr += path + "->";
00910 devStr += real;
00911
00912 if (!model.length())
00913 model = "unknown";
00914 devStr += " (" + model + ")";
00915
00916 list += devStr;
00917 }
00918
00919 return list.join(", ");
00920 }
00921
00928 void MediaMonitor::ejectOpticalDisc()
00929 {
00930 MediaMonitor *mon = MediaMonitor::GetMediaMonitor();
00931 if (mon)
00932 mon->ChooseAndEjectMedia();
00933 else
00934 {
00935 LOG(VB_MEDIA, LOG_INFO, "CD/DVD Monitor isn't enabled.");
00936 #ifdef __linux__
00937 LOG(VB_MEDIA, LOG_INFO, "Trying Linux 'eject -T' command");
00938 myth_system("eject -T");
00939 #elif CONFIG_DARWIN
00940 QString def = DEFAULT_CD;
00941 LOG(VB_MEDIA, LOG_INFO, "Trying 'disktool -e " + def);
00942 myth_system("disktool -e " + def);
00943 #endif
00944 }
00945 }
00946
00947
00948
00949