File indexing completed on 2025-01-12 04:26:31
0001 /* 0002 SPDX-FileCopyrightText: 2008 Jean-Baptiste Mardelle <jb@kdenlive.org> 0003 Based on code by Arendt David <admin@prnet.org> 0004 0005 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0006 */ 0007 0008 #include "jogshuttle.h" 0009 0010 #include "kdenlive_debug.h" 0011 0012 #include <QApplication> 0013 #include <QDir> 0014 #include <cerrno> 0015 #include <cstring> 0016 #include <sys/select.h> 0017 #include <utility> 0018 // according to earlier standards 0019 #include <sys/time.h> 0020 #include <sys/types.h> 0021 #include <unistd.h> 0022 0023 // init media event type constants 0024 const QEvent::Type MediaCtrlEvent::Key = QEvent::Type(QEvent::registerEventType()); 0025 const QEvent::Type MediaCtrlEvent::Jog = QEvent::Type(QEvent::registerEventType()); 0026 const QEvent::Type MediaCtrlEvent::Shuttle = QEvent::Type(QEvent::registerEventType()); 0027 0028 ShuttleThread::ShuttleThread(QString device, QObject *parent) 0029 : m_device(std::move(device)) 0030 , m_parent(parent) 0031 , m_isRunning(false) 0032 { 0033 } 0034 0035 ShuttleThread::~ShuttleThread() = default; 0036 0037 QString ShuttleThread::device() 0038 { 0039 return m_device; 0040 } 0041 0042 void ShuttleThread::stop() 0043 { 0044 m_isRunning = false; 0045 } 0046 0047 void ShuttleThread::run() 0048 { 0049 media_ctrl mc; 0050 media_ctrl_open_dev(&mc, m_device.toUtf8().data()); 0051 if (mc.fd < 0) { 0052 return; 0053 } 0054 fd_set readset; 0055 struct timeval timeout; 0056 // enter thread loop 0057 m_isRunning = true; 0058 while (m_isRunning) { 0059 // reset the read set 0060 FD_ZERO(&readset); 0061 FD_SET(mc.fd, &readset); 0062 // reinit the timeout structure 0063 timeout.tv_sec = 0; 0064 timeout.tv_usec = 400000; 0065 // do select in blocked mode and wake up after timeout 0066 // for stop_me evaluation 0067 int result = select(mc.fd + 1, &readset, nullptr, nullptr, &timeout); 0068 // see if there was an error or timeout else process event 0069 if (result < 0 && errno == EINTR) { 0070 // EINTR event caught. This is not a problem - continue processing 0071 continue; 0072 } 0073 if (result < 0) { 0074 // stop thread 0075 m_isRunning = false; 0076 } else if (result > 0) { 0077 // we have input 0078 if (FD_ISSET(mc.fd, &readset)) { 0079 media_ctrl_event mev; 0080 mev.type = MEDIA_CTRL_EVENT_NONE; 0081 // read input 0082 media_ctrl_read_event(&mc, &mev); 0083 // process event 0084 handleEvent(mev); 0085 } 0086 } else if (result == 0) { 0087 // on timeout 0088 } 0089 } 0090 // close the handle and return thread 0091 media_ctrl_close(&mc); 0092 } 0093 0094 void ShuttleThread::handleEvent(const media_ctrl_event &ev) 0095 { 0096 if (ev.type == MEDIA_CTRL_EVENT_KEY) { 0097 key(ev); 0098 } else if (ev.type == MEDIA_CTRL_EVENT_JOG) { 0099 jog(ev); 0100 } else if (ev.type == MEDIA_CTRL_EVENT_SHUTTLE) { 0101 shuttle(ev); 0102 } 0103 } 0104 0105 void ShuttleThread::key(const media_ctrl_event &ev) 0106 { 0107 if (ev.value == KEY_PRESS) { 0108 QApplication::postEvent(m_parent, new MediaCtrlEvent(MediaCtrlEvent::Key, ev.index + 1)); 0109 } 0110 } 0111 0112 void ShuttleThread::shuttle(const media_ctrl_event &ev) 0113 { 0114 int value = ev.value / 2; 0115 0116 if (value > MaxShuttleRange || value < -MaxShuttleRange) { 0117 // qCDebug(KDENLIVE_LOG) << "Jog shuttle value is out of range: " << MaxShuttleRange; 0118 return; 0119 } 0120 QApplication::postEvent(m_parent, new MediaCtrlEvent(MediaCtrlEvent::Shuttle, value)); 0121 } 0122 0123 void ShuttleThread::jog(const media_ctrl_event &ev) 0124 { 0125 QApplication::postEvent(m_parent, new MediaCtrlEvent(MediaCtrlEvent::Jog, ev.value)); 0126 } 0127 0128 JogShuttle::JogShuttle(const QString &device, QObject *parent) 0129 : QObject(parent) 0130 , m_shuttleProcess(device, this) 0131 { 0132 m_shuttleProcess.start(); 0133 } 0134 0135 JogShuttle::~JogShuttle() 0136 { 0137 stopDevice(); 0138 } 0139 0140 void JogShuttle::stopDevice() 0141 { 0142 if (m_shuttleProcess.isRunning()) { 0143 // tell thread to stop 0144 m_shuttleProcess.stop(); 0145 m_shuttleProcess.quit(); 0146 // give the thread some time (ms) to shutdown 0147 m_shuttleProcess.wait(600); 0148 0149 // if still running - do it in the hardcore way 0150 if (m_shuttleProcess.isRunning()) { 0151 m_shuttleProcess.terminate(); 0152 qCWarning(KDENLIVE_LOG) << "Needed to force jogshuttle process termination"; 0153 } 0154 } 0155 } 0156 0157 void JogShuttle::customEvent(QEvent *e) 0158 { 0159 QEvent::Type type = e->type(); 0160 0161 if (type == MediaCtrlEvent::Key) { 0162 auto *mev = static_cast<MediaCtrlEvent *>(e); 0163 Q_EMIT button(mev->value()); 0164 } else if (type == MediaCtrlEvent::Jog) { 0165 auto *mev = static_cast<MediaCtrlEvent *>(e); 0166 int value = mev->value(); 0167 0168 if (value < 0) { 0169 Q_EMIT jogBack(); 0170 } else if (value > 0) { 0171 Q_EMIT jogForward(); 0172 } 0173 } else if (type == MediaCtrlEvent::Shuttle) { 0174 auto *mev = static_cast<MediaCtrlEvent *>(e); 0175 Q_EMIT shuttlePos(mev->value()); 0176 } 0177 } 0178 0179 QString JogShuttle::canonicalDevice(const QString &device) 0180 { 0181 return QDir(device).canonicalPath(); 0182 } 0183 0184 DeviceMap JogShuttle::enumerateDevices(const QString &devPath) 0185 { 0186 DeviceMap devs; 0187 QDir devDir(devPath); 0188 0189 if (!devDir.exists()) { 0190 return devs; 0191 } 0192 0193 const QStringList fileList = devDir.entryList(QDir::System | QDir::Files); 0194 for (const QString &fileName : fileList) { 0195 QString devFullPath = devDir.absoluteFilePath(fileName); 0196 QString fileLink = JogShuttle::canonicalDevice(devFullPath); 0197 // qCDebug(KDENLIVE_LOG) << QString(" [%1] ").arg(fileName); 0198 // qCDebug(KDENLIVE_LOG) << QString(" [%1] ").arg(fileLink); 0199 0200 media_ctrl mc; 0201 media_ctrl_open_dev(&mc, fileLink.toUtf8().data()); 0202 if (mc.fd > 0 && (mc.device != nullptr)) { 0203 devs.insert(QString(mc.device->name), devFullPath); 0204 qCDebug(KDENLIVE_LOG) << QStringLiteral(" [keys-count=%1] ").arg(media_ctrl_get_keys_count(&mc)); 0205 } 0206 media_ctrl_close(&mc); 0207 } 0208 0209 return devs; 0210 } 0211 0212 int JogShuttle::keysCount(const QString &devPath) 0213 { 0214 media_ctrl mc; 0215 int keysCount = 0; 0216 0217 QString fileLink = canonicalDevice(devPath); 0218 media_ctrl_open_dev(&mc, fileLink.toUtf8().data()); 0219 if (mc.fd > 0 && (mc.device != nullptr)) { 0220 keysCount = media_ctrl_get_keys_count(&mc); 0221 } 0222 0223 return keysCount; 0224 }