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,5,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=callback, .MythMediaType=mediaType,
656 .destination=destination, .description=description };
657 QString msg = MythMediaDevice::MediaTypeString((MythMediaType)mediaType);
658
659 if (!extensions.isEmpty())
660 msg += QString(", ext(%1)").arg(extensions);
661
662 LOG(VB_MEDIA, LOG_INFO,
663 "Registering '" + destination + "' as a media handler for " +
664 msg);
665
666 m_handlerMap[destination] = mhd;
667
668 if (!extensions.isEmpty())
669 MythMediaDevice::RegisterMediaExtensions(mediaType, extensions);
670 }
671 else
672 {
673 LOG(VB_GENERAL, LOG_INFO,
674 destination + " is already registered as a media handler.");
675 }
676}
677
685void MediaMonitor::JumpToMediaHandler(MythMediaDevice* pMedia, bool forcePlayback)
686{
687 QVector<MHData> handlers;
688 QMap<QString, MHData>::Iterator itr = m_handlerMap.begin();
689
690 while (itr != m_handlerMap.end())
691 {
692 if (((*itr).MythMediaType & (int)pMedia->getMediaType()))
693 {
694 LOG(VB_GENERAL, LOG_NOTICE,
695 QString("Found a handler for %1 - '%2'")
696 .arg(pMedia->MediaTypeString(), itr.key()));
697 handlers.append(*itr);
698 }
699 itr++;
700 }
701
702 if (handlers.empty())
703 {
704 LOG(VB_MEDIA, LOG_INFO, "No media handler found for event type");
705 return;
706 }
707
708
709 // TODO - Generate a dialog, add buttons for each description,
710 // if user didn't cancel, selected = handlers.at(choice);
711 int selected = 0;
712
713 handlers.at(selected).callback(pMedia, forcePlayback);
714}
715
721 MythMediaDevice* pMedia) const
722{
723 // If we're not active then ignore signal.
724 if (!m_active)
725 return;
726
727 MythMediaStatus stat = pMedia->getStatus();
728 QString msg = QString(" (%1, %2 -> %3)")
729 .arg(pMedia->MediaTypeString(),
732
733 // This gets called from outside the main thread so we need
734 // to post an event back to the main thread.
735 // We now send events for all non-error statuses, so plugins get ejects
736 if (stat != MEDIASTAT_ERROR && stat != MEDIASTAT_UNKNOWN &&
737 // Don't send an event for a new device that's not mounted
738 (oldStatus != MEDIASTAT_UNPLUGGED || stat != MEDIASTAT_NOTMOUNTED))
739 {
740 // Should we ValidateAndLock() first?
741 QEvent *e = new MythMediaEvent(stat, pMedia);
742
743 LOG(VB_MEDIA, LOG_INFO, "Posting MediaEvent" + msg);
744
745 // sendEvent() is needed here - it waits for the event to be used.
746 // postEvent() would result in pDevice's media type changing
747 // ... before the plugin's event chain would process it.
748 // Another way would be to send an exact copy of pDevice instead.
749 QCoreApplication::sendEvent((QObject*)GetMythMainWindow(), e);
750 delete e;
751 }
752 else
753 {
754 LOG(VB_MEDIA, LOG_INFO,
755 "Media status changed, but not sending event" + msg);
756 }
757
758
759 if (stat == MEDIASTAT_OPEN || stat == MEDIASTAT_NODISK
760 || stat == MEDIASTAT_UNPLUGGED)
761 {
762 pMedia->clearData();
763 }
764}
765
770{
771 if (m_ignoreList.contains(device->getMountPath()) ||
772 m_ignoreList.contains(device->getRealDevice())||
773 m_ignoreList.contains(device->getDevicePath()) )
774 {
775 LOG(VB_MEDIA, LOG_INFO,
776 "Ignoring device: " + device->getDevicePath());
777 return true;
778 }
779#if 0
780 else
781 {
782 LOG(VB_MEDIA, LOG_DEBUG,
783 "Not ignoring: " + device->getDevicePath() + " / " +
784 device->getMountPath());
785 LOG(VB_MEDIA, LOG_DEBUG,
786 "Paths not in: " + m_ignoreList.join(", "));
787 }
788#endif
789
790 return false;
791}
792
797bool MediaMonitor::eventFilter(QObject *obj, QEvent *event)
798{
799 if (event->type() == MythMediaEvent::kEventType)
800 {
801 auto *me = dynamic_cast<MythMediaEvent *>(event);
802 if (me == nullptr)
803 {
804 LOG(VB_GENERAL, LOG_ALERT,
805 "MediaMonitor::eventFilter() couldn't cast event");
806 return true;
807 }
808
809 MythMediaDevice *pDev = me->getDevice();
810 if (!pDev)
811 {
812 LOG(VB_GENERAL, LOG_ALERT,
813 "MediaMonitor::eventFilter() got a bad media event?");
814 return true;
815 }
816
817 if (pDev->isUsable())
818 {
819 JumpToMediaHandler(pDev);
820 }
821 else
822 {
823 // We don't want to jump around in the menus, but should
824 // call each plugin's callback so it can track this change.
825
826 QMap<QString, MHData>::Iterator itr = m_handlerMap.begin();
827 while (itr != m_handlerMap.end())
828 {
829 if ((*itr).MythMediaType & (int)pDev->getMediaType() ||
830 pDev->getStatus() == MEDIASTAT_OPEN)
831 (*itr).callback(pDev, false);
832 itr++;
833 }
834 }
835
836 return false; // Don't eat the event
837 }
838
839 // standard event processing
840 return QObject::eventFilter(obj, event);
841}
842
843/*
844 * These methods return the user's preferred devices for playing and burning
845 * CDs and DVDs. Traditionally we had a database setting to remember this,
846 * but that is a bit wasteful when most users only have one drive.
847 *
848 * To make it a bit more beginner friendly, if no database default exists,
849 * or if it contains "default", the code tries to find a monitored drive.
850 * If, still, nothing is suitable, a caller hard-coded default is used.
851 *
852 * Ideally, these would return a MythMediaDevice * instead of a QString
853 */
854
855QString MediaMonitor::defaultDevice(const QString &dbSetting,
856 const QString &label,
857 const char *hardCodedDefault)
858{
859 QString device = gCoreContext->GetSetting(dbSetting);
860
861 LOG(VB_MEDIA, LOG_DEBUG,
862 QString("MediaMonitor::defaultDevice(%1,..,%2) dbSetting='%3'")
863 .arg(dbSetting, hardCodedDefault, device));
864
865 // No settings database defaults? Try to choose one:
866 if (device.isEmpty() || device == "default")
867 {
868 device = hardCodedDefault;
869
870 if (!s_monitor)
872
873 if (s_monitor)
874 {
875 bool canceled { false };
876 MythMediaDevice *d = s_monitor->selectDrivePopup(label, canceled, false, true);
877
878 if (canceled)
879 {
880 device.clear(); // If user has explicitly cancelled return empty string
881 d = nullptr;
882 }
883
884 if (d && s_monitor->ValidateAndLock(d))
885 {
886 device = d->getDevicePath();
888 }
889 }
890 }
891
892 LOG(VB_MEDIA, LOG_DEBUG,
893 "MediaMonitor::defaultDevice() returning " + device);
894 return device;
895}
896
901{
902 return defaultDevice("CDDevice", tr("Select a CD drive"), DEFAULT_CD);
903}
904
909{
910 return defaultDevice("VCDDeviceLocation",
911 tr("Select a VCD drive"), DEFAULT_CD);
912}
913
918{
919 return defaultDevice("DVDDeviceLocation",
920 tr("Select a DVD drive"), DEFAULT_DVD);
921}
922
927{
928 return defaultDevice("CDWriterDeviceLocation",
929 tr("Select a CD writer"), DEFAULT_CD);
930}
931
939{
940 QString device = defaultDevice("MythArchiveDVDLocation",
941 tr("Select a DVD writer"), DEFAULT_DVD);
942
943 return device;
944}
945
946
951{
952 QStringList list;
953
954 for (const auto *dev : std::as_const(m_devices))
955 {
956 QString devStr;
957 QString model = dev->getDeviceModel();
958 const QString& path = dev->getDevicePath();
959 const QString& real = dev->getRealDevice();
960
961 if (path != real)
962 devStr += path + "->";
963 devStr += real;
964
965 if (model.isEmpty())
966 model = "unknown";
967 devStr += " (" + model + ")";
968
969 list += devStr;
970 }
971
972 return list.join(", ");
973}
974
982{
984 if (mon)
985 {
986 mon->ChooseAndEjectMedia();
987 }
988 else
989 {
990 LOG(VB_MEDIA, LOG_INFO, "CD/DVD Monitor isn't enabled.");
991#ifdef Q_OS_LINUX
992 LOG(VB_MEDIA, LOG_INFO, "Trying Linux 'eject -T' command");
993 myth_system("eject -T");
994#elif defined(Q_OS_DARWIN)
995 QString def = DEFAULT_CD;
996 LOG(VB_MEDIA, LOG_INFO, "Trying 'diskutil eject " + def);
997 myth_system("diskutil eject " + def);
998#endif
999 }
1000}
1001
1002/*
1003 * vim:ts=4:sw=4:ai:et:si:sts=4
1004 */
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:180
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:267
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:193
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:284
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:134
void JumpToMediaHandler(MythMediaDevice *pMedia, bool forcePlayback=false)
Find a relevant jump point for this type of media.
virtual void CheckDeviceNotifications(void)
Definition: mediamonitor.h:107
static void AttemptEject(MythMediaDevice *device)
friend class MonitorThread
Definition: mediamonitor.h:51
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:133
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:123
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:125
QWaitCondition m_wait
Definition: mediamonitor.h:132
void CheckDevices(void)
Poll the devices in our list.
static MediaMonitor * s_monitor
Definition: mediamonitor.h:139
static QString defaultDevice(const QString &setting, const QString &label, const char *hardCodedDefault)
QStringList m_ignoreList
Definition: mediamonitor.h:129
static void ejectOpticalDisc(void)
Eject a disk, unmount a drive, open a tray.
bool volatile m_active
Was MonitorThread started?
Definition: mediamonitor.h:131
void StopMonitoring(void)
Stop the monitoring thread if needed.
QMap< QString, MHData > m_handlerMap
Registered Media Handlers.
Definition: mediamonitor.h:137
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:124
virtual void deleteLater(void)
static QString defaultCDdevice()
CDDevice, user-selected drive, or /dev/cdrom.
QMap< MythMediaDevice *, int > m_useCount
Definition: mediamonitor.h:126
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:45
unsigned long m_interval
Definition: mediamonitor.h:44
QPointer< MediaMonitor > m_monitor
Definition: mediamonitor.h:43
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:522
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:307
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:356
MythMediaType getMediaType() const
Definition: mythmedia.h:91
bool findMountPath()
Try to find a mount of m_devicePath in the mounts file.
Definition: mythmedia.cpp:372
virtual MythMediaError eject(bool open_close=true)
Definition: mythmedia.cpp:315
virtual void setSpeed(int speed)
Definition: mythmedia.cpp:338
bool isMounted(bool bVerify=true)
Tells us if m_devicePath is a mounted device.
Definition: mythmedia.cpp:364
static const Type kEventType
Definition: mythmedia.h:193
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
static QList< GameHandler * > * handlers
Definition: gamehandler.cpp:29
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
MediaCallback callback
Definition: mediamonitor.h:24