MythTV master
mediamonitor.cpp
Go to the documentation of this file.
1#include "mediamonitor.h"
2
3// Standard C headers
4#include <cstdio>
5
6// C++ headers
7#include <iostream>
8#include <typeinfo>
9
10// Qt headers
11#include <QtGlobal>
12#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
13#include <QtSystemDetection>
14#endif
15#include <QCoreApplication>
16#include <QFile>
17#include <QList>
18#include <QDir>
19
20// MythTV headers
22#include "libmythbase/mythconfig.h"
30
31#if CONFIG_DARWIN_DA
32#include "mediamonitor-darwin.h"
33#elif defined(Q_OS_WINDOWS)
35#else
36#include "mediamonitor-unix.h"
37#endif
38
39static const QString sLocation = QObject::tr("Media Monitor");
40
42
43// Nice and simple, as long as our monitor is valid and active,
44// loop and check it's devices.
46{
47 RunProlog();
48 QMutex mtx;
49 mtx.lock();
50 while (m_monitor && m_monitor->IsActive())
51 {
52 m_monitor->CheckDevices();
53 m_monitor->m_wait.wait(&mtx, m_interval);
54 QDateTime now(QDateTime::currentDateTimeUtc());
55 // if 10 seconds have elapsed instead of 5 seconds
56 // assume the system was suspended and reconnect
57 // sockets
58 if (m_lastCheckTime.secsTo(now) > 120)
59 {
62 {
63 LOG(VB_GENERAL, LOG_INFO, "Restarting LIRC handler");
65 }
66 }
67 m_lastCheckTime = now;
68 }
69 mtx.unlock();
70 RunEpilog();
71}
72
74// MediaMonitor
75
76static constexpr unsigned long MONITOR_INTERVAL { 5000 };
77
79{
80 if (s_monitor)
81 return s_monitor;
82
83#if CONFIG_DARWIN_DA
84 s_monitor = new MediaMonitorDarwin(nullptr, MONITOR_INTERVAL, true);
85#elif defined(Q_OS_WINDOWS)
87#else
88 s_monitor = new MediaMonitorUnix(nullptr, MONITOR_INTERVAL, true);
89#endif
90
91 return s_monitor;
92}
93
94void MediaMonitor::SetCDSpeed(const char *device, int speed)
95{
97 if (mon)
98 {
99 MythMediaDevice *pMedia = mon->GetMedia(device);
100 if (pMedia && mon->ValidateAndLock(pMedia))
101 {
102 pMedia->setSpeed(speed);
103 mon->Unlock(pMedia);
104 return;
105 }
106 }
107
108 MythCDROM *cd = MythCDROM::get(nullptr, device, false, false);
109 if (cd)
110 {
111 cd->setDeviceSpeed(device, speed);
112 delete cd;
113 return;
114 }
115
116 LOG(VB_MEDIA, LOG_INFO,
117 QString("MediaMonitor::setSpeed(%1) - Cannot find/create CDROM?")
118 .arg(device));
119}
120
121// When ejecting one of multiple devices, present a nice name to the user
122static QString DevName(MythMediaDevice *d)
123{
124 QString str = d->getVolumeID(); // First choice, the name of the media
125
126 if (str.isEmpty())
127 {
128 str = d->getDeviceModel(); // otherwise, the drive manufacturer/model
129
130 if (!str.isEmpty()) // and/or the device node
131 str += " (" + d->getDevicePath() + ')';
132 else
133 str = d->getDevicePath();
134 }
135 // We could add even more information here, but volume names
136 // are usually descriptively unique (i.e. usually good enough)
137 //else
138 // str += " (" + d->getDeviceModel() + ", " + d->getDevicePath() + ')';
139
140 return str;
141}
142
148QList<MythMediaDevice*> MediaMonitor::GetRemovable(bool showMounted,
149 bool showUsable)
150{
151 QList <MythMediaDevice *> drives;
152 QMutexLocker locker(&m_devicesLock);
153
154 for (MythMediaDevice *dev : std::as_const(m_devices))
155 {
156 // By default, we only list CD/DVD devices.
157 // Caller can also request mounted drives to be listed (e.g. USB flash)
158
159 if (showUsable && !dev->isUsable())
160 continue;
161
162 if (QString(typeid(*dev).name()).contains("MythCDROM") ||
163 (showMounted && dev->isMounted(false)))
164 drives.append(dev);
165 }
166
167 return drives;
168}
169
176 bool &canceled,
177 bool showMounted,
178 bool showUsable)
179{
180 canceled = false;
181 QList <MythMediaDevice *> drives = GetRemovable(showMounted,
182 showUsable);
183
184 if (drives.count() == 0)
185 {
186 QString msg = "MediaMonitor::selectDrivePopup() - no removable devices";
187
188 LOG(VB_MEDIA, LOG_INFO, msg);
189 return nullptr;
190 }
191
192 if (drives.count() == 1)
193 {
194 LOG(VB_MEDIA, LOG_INFO,
195 "MediaMonitor::selectDrivePopup(" + label +
196 ") - One suitable device");
197 return drives.front();
198 }
199
201 if (!win)
202 return nullptr;
203
204 MythScreenStack *stack = win->GetMainStack();
205 if (!stack)
206 return nullptr;
207
208 // Ignore MENU dialog actions
209 int btnIndex = -2;
210 while (btnIndex < -1)
211 {
212 auto *dlg = new MythDialogBox(label, stack, "select drive");
213 if (!dlg->Create())
214 {
215 delete dlg;
216 return nullptr;
217 }
218
219 // Add button for each drive
220 for (auto *drive : std::as_const(drives))
221 dlg->AddButton(DevName(drive));
222
223 dlg->AddButton(tr("Cancel"));
224
225 stack->AddScreen(dlg);
226
227 // Wait in local event loop so events are processed
228 QEventLoop block;
229 connect(dlg, &MythDialogBox::Closed,
230 &block, [&](const QString& /*resultId*/, int result) { block.exit(result); });
231
232 // Block until dialog closes
233 btnIndex = block.exec();
234 }
235
236 // If the user cancelled, return a special value
237 if (btnIndex < 0 || btnIndex >= drives.size())
238 {
239 canceled = true;
240 return nullptr;
241 }
242 return drives.at(btnIndex);
243}
244
245
255{
256 bool canceled { false };
257 MythMediaDevice *selected =
258 selectDrivePopup(tr("Select removable media to eject or insert"), canceled, true);
259
260 // If the user cancelled, no need to display or do anything more
261 if (canceled)
262 return;
263
264 if (!selected)
265 {
266 ShowNotification(tr("No devices to eject"), sLocation);
267 return;
268 }
269
270 AttemptEject(selected);
271}
272
273
274void MediaMonitor::EjectMedia(const QString &path)
275{
276 MythMediaDevice *device = GetMedia(path);
277 if (device)
278 AttemptEject(device);
279}
280
281
283{
284 QString dev = DevName(device);
285
286 if (device->getStatus() == MEDIASTAT_OPEN)
287 {
288 LOG(VB_MEDIA, LOG_INFO,
289 QString("Disk %1's tray is OPEN. Closing tray").arg(dev));
290
291 if (device->eject(false) != MEDIAERR_OK)
292 {
293 QString msg =
294 QObject::tr("Unable to open or close the empty drive %1");
295 QString extra =
296 QObject::tr("You may have to use the eject button under its tray");
297 ShowNotificationError(msg.arg(dev), sLocation, extra);
298 }
299 return;
300 }
301
302 if (device->isMounted())
303 {
304 LOG(VB_MEDIA, LOG_INFO,
305 QString("Disk %1 is mounted? Unmounting").arg(dev));
306 device->unmount();
307
308#ifndef Q_OS_DARWIN
309 if (device->isMounted())
310 {
311 ShowNotificationError(tr("Failed to unmount %1").arg(dev),
312 sLocation);
313 return;
314 }
315#endif
316 }
317
318 LOG(VB_MEDIA, LOG_INFO,
319 QString("Unlocking disk %1, then ejecting").arg(dev));
320 device->unlock();
321
322 MythMediaError err = device->eject();
323
324 if (err == MEDIAERR_UNSUPPORTED)
325 {
326 // Physical ejection isn't possible (there is no tray or slot),
327 // but logically the device is now ejected (ignored by the OS).
328 ShowNotification(tr("You may safely remove %1").arg(dev), sLocation);
329 }
330 else if (err == MEDIAERR_FAILED)
331 {
332 ShowNotificationError(tr("Failed to eject %1").arg(dev), sLocation);
333 }
334}
335
342MediaMonitor::MediaMonitor(QObject* par, unsigned long interval, bool allowEject)
343 : QObject(par),
344 m_monitorPollingInterval(interval),
345 m_allowEject(allowEject)
346{
347 // User can specify that some devices are not monitored
348 QString ignore = gCoreContext->GetSetting("IgnoreDevices", "");
349
350 if (!ignore.isEmpty())
351 {
352 m_ignoreList = ignore.split(',', Qt::SkipEmptyParts);
353 }
354
355 LOG(VB_MEDIA, LOG_NOTICE, "Creating MediaMonitor");
356 LOG(VB_MEDIA, LOG_INFO, "IgnoreDevices=" + ignore);
357
358 // If any of IgnoreDevices are symlinks, also add the real device
359 QStringList symlinked;
360 for (const auto & ignored : std::as_const(m_ignoreList))
361 {
362 if (auto fi = QFileInfo(ignored); fi.isSymLink())
363 {
364 if (auto target = getSymlinkTarget(ignored); m_ignoreList.filter(target).isEmpty())
365 {
366 symlinked += target;
367 LOG(VB_MEDIA, LOG_INFO, QString("Also ignoring %1 (symlinked from %2)")
368 .arg(target, ignored));
369 }
370 }
371 }
372
373 m_ignoreList += symlinked;
374}
375
377{
378 if (m_thread)
379 {
381 delete m_thread;
382 m_thread = nullptr;
383 }
384 QObject::deleteLater();
385}
386
395bool MediaMonitor::RemoveDevice(const QString &dev)
396{
397 QMutexLocker locker(&m_devicesLock);
398
399 QList<MythMediaDevice*>::iterator it;
400 for (it = m_devices.begin(); it != m_devices.end(); ++it)
401 {
402 if ((*it)->getDevicePath() == dev)
403 {
404 // Ensure device gets an unmount
405 (*it)->checkMedia();
406
407 if (m_useCount[*it] == 0)
408 {
409 m_useCount.remove(*it);
410 (*it)->deleteLater();
411 m_devices.erase(it);
412 }
413 else
414 {
415 // Other threads are still using this device
416 // postpone actual delete until they finish.
417 disconnect(*it);
418 m_removedDevices.append(*it);
419 m_devices.erase(it);
420 }
421
422 return true;
423 }
424 }
425 return false;
426}
427
432{
433 /* check if new devices have been plugged in */
435
436 QMutexLocker locker(&m_devicesLock);
437
438 QList<MythMediaDevice*>::iterator itr = m_devices.begin();
439 while (itr != m_devices.end())
440 {
441 MythMediaDevice* pDev = *itr;
442 if (pDev)
443 pDev->checkMedia();
444 ++itr;
445 }
446}
447
452{
453 // Sanity check
454 if (m_active)
455 return;
456 if (!gCoreContext->GetBoolSetting("MonitorDrives", false)) {
457 LOG(VB_MEDIA, LOG_NOTICE, "MediaMonitor disabled by user setting.");
458 return;
459 }
460
461 if (!m_thread)
463
464 qRegisterMetaType<MythMediaStatus>("MythMediaStatus");
465
466 LOG(VB_MEDIA, LOG_NOTICE, "Starting MediaMonitor");
467 m_active = true;
468 m_thread->start();
469}
470
475{
476 // Sanity check
477 if (!m_active)
478 return;
479
480 LOG(VB_MEDIA, LOG_NOTICE, "Stopping MediaMonitor");
481 m_active = false;
482 m_wait.wakeAll();
483 m_thread->wait();
484 LOG(VB_MEDIA, LOG_NOTICE, "Stopped MediaMonitor");
485}
486
498{
499 QMutexLocker locker(&m_devicesLock);
500
501 if (!m_devices.contains(pMedia))
502 return false;
503
504 m_useCount[pMedia]++;
505
506 return true;
507}
508
515{
516 QMutexLocker locker(&m_devicesLock);
517
518 if (!m_useCount.contains(pMedia))
519 return;
520
521 m_useCount[pMedia]--;
522
523 if (m_useCount[pMedia] == 0 && m_removedDevices.contains(pMedia))
524 {
525 m_removedDevices.removeAll(pMedia);
526 m_useCount.remove(pMedia);
527 pMedia->deleteLater();
528 }
529}
530
538{
539 QMutexLocker locker(&m_devicesLock);
540
541 for (auto *dev : std::as_const(m_devices))
542 {
543 if (dev->isSameDevice(path) &&
544 ((dev->getStatus() == MEDIASTAT_USEABLE) ||
545 (dev->getStatus() == MEDIASTAT_MOUNTED) ||
546 (dev->getStatus() == MEDIASTAT_NOTMOUNTED)))
547 {
548 return dev;
549 }
550 }
551
552 return nullptr;
553}
554
561QString MediaMonitor::GetMountPath(const QString& devPath)
562{
563 QString mountPath;
564
565 if (s_monitor)
566 {
567 MythMediaDevice *pMedia = s_monitor->GetMedia(devPath);
568 if (pMedia && s_monitor->ValidateAndLock(pMedia))
569 {
570 mountPath = pMedia->getMountPath();
571 s_monitor->Unlock(pMedia);
572 }
573 // The media monitor could be inactive.
574 // Create a fake media device just to lookup mount map:
575 else
576 {
577 pMedia = MythCDROM::get(nullptr, devPath.toLatin1(), true, false);
578 if (pMedia && pMedia->findMountPath())
579 mountPath = pMedia->getMountPath();
580 else
581 LOG(VB_MEDIA, LOG_INFO,
582 "MediaMonitor::GetMountPath() - failed");
583 // need some way to delete the media device.
584 }
585 }
586
587 return mountPath;
588}
589
608QList<MythMediaDevice*> MediaMonitor::GetMedias(unsigned mediatypes)
609{
610 QMutexLocker locker(&m_devicesLock);
611
612 QList<MythMediaDevice*> medias;
613
614 for (auto *dev : std::as_const(m_devices))
615 {
616 if ((dev->getMediaType() & mediatypes) &&
617 ((dev->getStatus() == MEDIASTAT_USEABLE) ||
618 (dev->getStatus() == MEDIASTAT_MOUNTED) ||
619 (dev->getStatus() == MEDIASTAT_NOTMOUNTED)))
620 {
621 medias.push_back(dev);
622 }
623 }
624
625 return medias;
626}
627
647void MediaMonitor::RegisterMediaHandler(const QString &destination,
648 const QString &description,
649 MediaCallback callback,
650 int mediaType,
651 const QString &extensions)
652{
653 if (!m_handlerMap.contains(destination))
654 {
655 MHData mhd = { callback, mediaType, destination, description };
656 QString msg = MythMediaDevice::MediaTypeString((MythMediaType)mediaType);
657
658 if (!extensions.isEmpty())
659 msg += QString(", ext(%1)").arg(extensions);
660
661 LOG(VB_MEDIA, LOG_INFO,
662 "Registering '" + destination + "' as a media handler for " +
663 msg);
664
665 m_handlerMap[destination] = mhd;
666
667 if (!extensions.isEmpty())
668 MythMediaDevice::RegisterMediaExtensions(mediaType, extensions);
669 }
670 else
671 {
672 LOG(VB_GENERAL, LOG_INFO,
673 destination + " is already registered as a media handler.");
674 }
675}
676
684void MediaMonitor::JumpToMediaHandler(MythMediaDevice* pMedia, bool forcePlayback)
685{
686 QVector<MHData> handlers;
687 QMap<QString, MHData>::Iterator itr = m_handlerMap.begin();
688
689 while (itr != m_handlerMap.end())
690 {
691 if (((*itr).MythMediaType & (int)pMedia->getMediaType()))
692 {
693 LOG(VB_GENERAL, LOG_NOTICE,
694 QString("Found a handler for %1 - '%2'")
695 .arg(pMedia->MediaTypeString(), itr.key()));
696 handlers.append(*itr);
697 }
698 itr++;
699 }
700
701 if (handlers.empty())
702 {
703 LOG(VB_MEDIA, LOG_INFO, "No media handler found for event type");
704 return;
705 }
706
707
708 // TODO - Generate a dialog, add buttons for each description,
709 // if user didn't cancel, selected = handlers.at(choice);
710 int selected = 0;
711
712 handlers.at(selected).callback(pMedia, forcePlayback);
713}
714
720 MythMediaDevice* pMedia) const
721{
722 // If we're not active then ignore signal.
723 if (!m_active)
724 return;
725
726 MythMediaStatus stat = pMedia->getStatus();
727 QString msg = QString(" (%1, %2 -> %3)")
728 .arg(pMedia->MediaTypeString(),
731
732 // This gets called from outside the main thread so we need
733 // to post an event back to the main thread.
734 // We now send events for all non-error statuses, so plugins get ejects
735 if (stat != MEDIASTAT_ERROR && stat != MEDIASTAT_UNKNOWN &&
736 // Don't send an event for a new device that's not mounted
737 (oldStatus != MEDIASTAT_UNPLUGGED || stat != MEDIASTAT_NOTMOUNTED))
738 {
739 // Should we ValidateAndLock() first?
740 QEvent *e = new MythMediaEvent(stat, pMedia);
741
742 LOG(VB_MEDIA, LOG_INFO, "Posting MediaEvent" + msg);
743
744 // sendEvent() is needed here - it waits for the event to be used.
745 // postEvent() would result in pDevice's media type changing
746 // ... before the plugin's event chain would process it.
747 // Another way would be to send an exact copy of pDevice instead.
748 QCoreApplication::sendEvent((QObject*)GetMythMainWindow(), e);
749 delete e;
750 }
751 else
752 {
753 LOG(VB_MEDIA, LOG_INFO,
754 "Media status changed, but not sending event" + msg);
755 }
756
757
758 if (stat == MEDIASTAT_OPEN || stat == MEDIASTAT_NODISK
759 || stat == MEDIASTAT_UNPLUGGED)
760 {
761 pMedia->clearData();
762 }
763}
764
769{
770 if (m_ignoreList.contains(device->getMountPath()) ||
771 m_ignoreList.contains(device->getRealDevice())||
772 m_ignoreList.contains(device->getDevicePath()) )
773 {
774 LOG(VB_MEDIA, LOG_INFO,
775 "Ignoring device: " + device->getDevicePath());
776 return true;
777 }
778#if 0
779 else
780 {
781 LOG(VB_MEDIA, LOG_DEBUG,
782 "Not ignoring: " + device->getDevicePath() + " / " +
783 device->getMountPath());
784 LOG(VB_MEDIA, LOG_DEBUG,
785 "Paths not in: " + m_ignoreList.join(", "));
786 }
787#endif
788
789 return false;
790}
791
796bool MediaMonitor::eventFilter(QObject *obj, QEvent *event)
797{
798 if (event->type() == MythMediaEvent::kEventType)
799 {
800 auto *me = dynamic_cast<MythMediaEvent *>(event);
801 if (me == nullptr)
802 {
803 LOG(VB_GENERAL, LOG_ALERT,
804 "MediaMonitor::eventFilter() couldn't cast event");
805 return true;
806 }
807
808 MythMediaDevice *pDev = me->getDevice();
809 if (!pDev)
810 {
811 LOG(VB_GENERAL, LOG_ALERT,
812 "MediaMonitor::eventFilter() got a bad media event?");
813 return true;
814 }
815
816 if (pDev->isUsable())
817 JumpToMediaHandler(pDev);
818 else
819 {
820 // We don't want to jump around in the menus, but should
821 // call each plugin's callback so it can track this change.
822
823 QMap<QString, MHData>::Iterator itr = m_handlerMap.begin();
824 while (itr != m_handlerMap.end())
825 {
826 if ((*itr).MythMediaType & (int)pDev->getMediaType() ||
827 pDev->getStatus() == MEDIASTAT_OPEN)
828 (*itr).callback(pDev, false);
829 itr++;
830 }
831 }
832
833 return false; // Don't eat the event
834 }
835
836 // standard event processing
837 return QObject::eventFilter(obj, event);
838}
839
840/*
841 * These methods return the user's preferred devices for playing and burning
842 * CDs and DVDs. Traditionally we had a database setting to remember this,
843 * but that is a bit wasteful when most users only have one drive.
844 *
845 * To make it a bit more beginner friendly, if no database default exists,
846 * or if it contains "default", the code tries to find a monitored drive.
847 * If, still, nothing is suitable, a caller hard-coded default is used.
848 *
849 * Ideally, these would return a MythMediaDevice * instead of a QString
850 */
851
852QString MediaMonitor::defaultDevice(const QString &dbSetting,
853 const QString &label,
854 const char *hardCodedDefault)
855{
856 QString device = gCoreContext->GetSetting(dbSetting);
857
858 LOG(VB_MEDIA, LOG_DEBUG,
859 QString("MediaMonitor::defaultDevice(%1,..,%2) dbSetting='%3'")
860 .arg(dbSetting, hardCodedDefault, device));
861
862 // No settings database defaults? Try to choose one:
863 if (device.isEmpty() || device == "default")
864 {
865 device = hardCodedDefault;
866
867 if (!s_monitor)
869
870 if (s_monitor)
871 {
872 bool canceled { false };
873 MythMediaDevice *d = s_monitor->selectDrivePopup(label, canceled, false, true);
874
875 if (canceled)
876 {
877 device.clear(); // If user has explicitly cancelled return empty string
878 d = nullptr;
879 }
880
881 if (d && s_monitor->ValidateAndLock(d))
882 {
883 device = d->getDevicePath();
885 }
886 }
887 }
888
889 LOG(VB_MEDIA, LOG_DEBUG,
890 "MediaMonitor::defaultDevice() returning " + device);
891 return device;
892}
893
898{
899 return defaultDevice("CDDevice", tr("Select a CD drive"), DEFAULT_CD);
900}
901
906{
907 return defaultDevice("VCDDeviceLocation",
908 tr("Select a VCD drive"), DEFAULT_CD);
909}
910
915{
916 return defaultDevice("DVDDeviceLocation",
917 tr("Select a DVD drive"), DEFAULT_DVD);
918}
919
924{
925 return defaultDevice("CDWriterDeviceLocation",
926 tr("Select a CD writer"), DEFAULT_CD);
927}
928
936{
937 QString device = defaultDevice("MythArchiveDVDLocation",
938 tr("Select a DVD writer"), DEFAULT_DVD);
939
940 return device;
941}
942
943
948{
949 QStringList list;
950
951 for (const auto *dev : std::as_const(m_devices))
952 {
953 QString devStr;
954 QString model = dev->getDeviceModel();
955 const QString& path = dev->getDevicePath();
956 const QString& real = dev->getRealDevice();
957
958 if (path != real)
959 devStr += path + "->";
960 devStr += real;
961
962 if (model.isEmpty())
963 model = "unknown";
964 devStr += " (" + model + ")";
965
966 list += devStr;
967 }
968
969 return list.join(", ");
970}
971
979{
981 if (mon)
982 mon->ChooseAndEjectMedia();
983 else
984 {
985 LOG(VB_MEDIA, LOG_INFO, "CD/DVD Monitor isn't enabled.");
986#ifdef Q_OS_LINUX
987 LOG(VB_MEDIA, LOG_INFO, "Trying Linux 'eject -T' command");
988 myth_system("eject -T");
989#elif defined(Q_OS_DARWIN)
990 QString def = DEFAULT_CD;
991 LOG(VB_MEDIA, LOG_INFO, "Trying 'diskutil eject " + def);
992 myth_system("diskutil eject " + def);
993#endif
994 }
995}
996
997/*
998 * vim:ts=4:sw=4:ai:et:si:sts=4
999 */
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:196
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:283
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:209
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:300
This currently depends on Apple's DiskArbitration framework.
I am assuming, for now, that everything on Windows uses drive letters (e.g.
QList< MythMediaDevice * > GetRemovable(bool showMounted=false, bool showUsable=false)
Generate a list of removable drives.
unsigned long m_monitorPollingInterval
Definition: mediamonitor.h:132
void JumpToMediaHandler(MythMediaDevice *pMedia, bool forcePlayback=false)
Find a relevant jump point for this type of media.
virtual void CheckDeviceNotifications(void)
Definition: mediamonitor.h:104
static void AttemptEject(MythMediaDevice *device)
friend class MonitorThread
Definition: mediamonitor.h:50
void mediaStatusChanged(MythMediaStatus oldStatus, MythMediaDevice *pMedia) const
Slot which is called when the device status changes and posts a media event to the mainwindow.
static MediaMonitor * GetMediaMonitor(void)
bool ValidateAndLock(MythMediaDevice *pMedia)
Validates the MythMediaDevice and increments its reference count.
MonitorThread * m_thread
Definition: mediamonitor.h:131
static void SetCDSpeed(const char *device, int speed)
static QString defaultDVDWriter()
MythArchiveDVDLocation, user-selected drive, or /dev/dvd.
bool RemoveDevice(const QString &dev)
Remove a device from the media monitor.
MythMediaDevice * GetMedia(const QString &path)
Get media device by pathname.
void Unlock(MythMediaDevice *pMedia)
decrements the MythMediaDevices reference count
MediaMonitor(QObject *par, unsigned long interval, bool allowEject)
Lookup some settings, and do OS-specific stuff in sub-classes.
void ChooseAndEjectMedia(void)
Unmounts and ejects removable media devices.
QRecursiveMutex m_devicesLock
Definition: mediamonitor.h:121
static QString defaultDVDdevice()
DVDDeviceLocation, user-selected drive, or /dev/dvd.
bool shouldIgnore(const MythMediaDevice *device)
Check user preferences to see if this device should be monitored.
QList< MythMediaDevice * > m_removedDevices
Definition: mediamonitor.h:123
QWaitCondition m_wait
Definition: mediamonitor.h:130
void CheckDevices(void)
Poll the devices in our list.
static MediaMonitor * s_monitor
Definition: mediamonitor.h:137
static QString defaultDevice(const QString &setting, const QString &label, const char *hardCodedDefault)
QStringList m_ignoreList
Definition: mediamonitor.h:127
static void ejectOpticalDisc(void)
Eject a disk, unmount a drive, open a tray.
bool volatile m_active
Was MonitorThread started?
Definition: mediamonitor.h:129
void StopMonitoring(void)
Stop the monitoring thread if needed.
QMap< QString, MHData > m_handlerMap
Registered Media Handlers.
Definition: mediamonitor.h:135
void RegisterMediaHandler(const QString &destination, const QString &description, MediaCallback callback, int mediaType, const QString &extensions)
Register a handler for media related events.
QList< MythMediaDevice * > m_devices
Definition: mediamonitor.h:122
virtual void deleteLater(void)
static QString defaultCDdevice()
CDDevice, user-selected drive, or /dev/cdrom.
QMap< MythMediaDevice *, int > m_useCount
Definition: mediamonitor.h:124
static QString GetMountPath(const QString &devPath)
If the device is being monitored, return its mountpoint.
static QString defaultVCDdevice()
VCDDeviceLocation, user-selected drive, or /dev/cdrom.
void EjectMedia(const QString &path)
virtual void StartMonitoring(void)
Start the monitoring thread if needed.
bool eventFilter(QObject *obj, QEvent *event) override
Installed into the main window's event chain, so that the main thread can safely jump to plugin code.
MythMediaDevice * selectDrivePopup(const QString &label, bool &canceled, bool showMounted=false, bool showUsable=false)
List removable drives, let the user select one.
QList< MythMediaDevice * > GetMedias(unsigned mediatypes)
Ask for available media.
QString listDevices(void)
A string summarising the current devices, for debugging.
static QString defaultCDWriter()
CDWriterDeviceLocation, user-selected drive, or /dev/cdrom.
void run(void) override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
QDateTime m_lastCheckTime
Definition: mediamonitor.h:44
unsigned long m_interval
Definition: mediamonitor.h:43
QPointer< MediaMonitor > m_monitor
Definition: mediamonitor.h:42
static MythCDROM * get(QObject *par, const QString &devicePath, bool SuperMount, bool AllowEject)
Definition: mythcdrom.cpp:44
void setDeviceSpeed(const char *devicePath, int speed) override
Definition: mythcdrom.cpp:141
QString GetSetting(const QString &key, const QString &defaultval="")
bool GetBoolSetting(const QString &key, bool defaultval=false)
Basic menu dialog, message and a list of options.
void Closed(QString, int)
MythScreenStack * GetMainStack()
const QString & getMountPath() const
Definition: mythmedia.h:58
MythMediaStatus getStatus() const
Definition: mythmedia.h:70
QString MediaTypeString()
Definition: mythmedia.cpp:521
bool unmount()
Definition: mythmedia.h:108
const QString & getRealDevice() const
Definition: mythmedia.h:63
bool isUsable() const
Is this device "ready", for a plugin to access?
Definition: mythmedia.h:84
static void RegisterMediaExtensions(uint mediatype, const QString &extensions)
Used to register media types with extensions.
Definition: mythmedia.cpp:306
const QString & getDevicePath() const
Definition: mythmedia.h:61
static const std::array< const QString, 9 > kMediaStatusStrings
Definition: mythmedia.h:117
virtual MythMediaStatus checkMedia()=0
virtual MythMediaError unlock()
Definition: mythmedia.cpp:355
MythMediaType getMediaType() const
Definition: mythmedia.h:91
bool findMountPath()
Try to find a mount of m_devicePath in the mounts file.
Definition: mythmedia.cpp:371
virtual MythMediaError eject(bool open_close=true)
Definition: mythmedia.cpp:314
virtual void setSpeed(int speed)
Definition: mythmedia.cpp:337
bool isMounted(bool bVerify=true)
Tells us if m_devicePath is a mounted device.
Definition: mythmedia.cpp:363
static const Type kEventType
Definition: mythmedia.h:193
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
static QList< GameHandler * > * handlers
Definition: gamehandler.cpp:28
static const iso6937table * d
#define DEFAULT_DVD
#define DEFAULT_CD
static constexpr unsigned long MONITOR_INTERVAL
static QString DevName(MythMediaDevice *d)
static const QString sLocation
void(*)(MythMediaDevice *, bool) MediaCallback
Definition: mediamonitor.h:15
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
bool HasMythMainWindow(void)
MythMainWindow * GetMythMainWindow(void)
MythMediaType
Definition: mythmedia.h:24
MythMediaError
Definition: mythmedia.h:39
@ MEDIAERR_UNSUPPORTED
Definition: mythmedia.h:42
@ MEDIAERR_OK
Definition: mythmedia.h:40
@ MEDIAERR_FAILED
Definition: mythmedia.h:41
MythMediaStatus
Definition: mythmedia.h:12
@ MEDIASTAT_UNKNOWN
Definition: mythmedia.h:14
@ MEDIASTAT_NODISK
CD/DVD tray closed but empty, device unusable.
Definition: mythmedia.h:17
@ MEDIASTAT_USEABLE
Definition: mythmedia.h:19
@ MEDIASTAT_UNPLUGGED
Definition: mythmedia.h:15
@ MEDIASTAT_OPEN
CD/DVD tray open (meaningless for non-CDs?)
Definition: mythmedia.h:16
@ MEDIASTAT_NOTMOUNTED
Definition: mythmedia.h:20
@ MEDIASTAT_MOUNTED
Definition: mythmedia.h:21
@ MEDIASTAT_ERROR
Unable to mount, but could be usable.
Definition: mythmedia.h:13
QString getSymlinkTarget(const QString &start_file, QStringList *intermediaries, unsigned maxLinks)
void ShowNotificationError(const QString &msg, const QString &from, const QString &detail, const VNMask visibility, const MythNotification::Priority priority)
convenience utility to display error message as notification
void ShowNotification(const QString &msg, const QString &from, const QString &detail, const VNMask visibility, const MythNotification::Priority priority)
uint myth_system(const QString &command, uint flags, std::chrono::seconds timeout)
Stores details of media handlers.
Definition: mediamonitor.h:23