Something went wrong. Try again.
Mixxx DJ software with MCP, Subsonic/Navidrome streaming library support — browse and load tracks from your self-hosted music server mixxx.org
subsonic navidrome streaming audio dj mac linux midi
Something went wrong. Try again.
C++
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256#include "mixxxapplication.h"
#include <QThreadPool>#include <QTouchEvent>#include <QtDebug>#include <QtGlobal>
#include "audio/frame.h"#include "audio/types.h"#include "control/controlproxy.h"#include "library/relocatedtrack.h"#include "library/trackset/crate/crateid.h"#include "moc_mixxxapplication.cpp"#include "soundio/soundmanagerutil.h"#include "track/track.h"#include "track/trackref.h"#include "util/assert.h"#include "util/cache.h"#include "util/cmdlineargs.h"#include "util/color/rgbcolor.h"#include "util/fileinfo.h"#include "util/math.h"
// When linking Qt statically, the Q_IMPORT_PLUGIN is needed for each linked plugin.// https://doc.qt.io/qt-5/plugins-howto.html#details-of-linking-static-plugins#ifdef QT_STATIC#include <QtPlugin>#if defined(Q_OS_WASM)Q_IMPORT_PLUGIN(QWasmIntegrationPlugin)#elif defined(Q_OS_WIN)Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)Q_IMPORT_PLUGIN(QModernWindowsStylePlugin)#elseQ_IMPORT_PLUGIN(QWindowsVistaStylePlugin)#endif#elif defined(Q_OS_IOS)Q_IMPORT_PLUGIN(QIOSIntegrationPlugin)#elif defined(Q_OS_MACOS)Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin)Q_IMPORT_PLUGIN(QMacStylePlugin)#elif defined(Q_OS_LINUX)Q_IMPORT_PLUGIN(QXcbIntegrationPlugin)#else#error "Q_IMPORT_PLUGIN() for the current patform is missing"#endif#if !defined(Q_OS_WASM)Q_IMPORT_PLUGIN(QOffscreenIntegrationPlugin)Q_IMPORT_PLUGIN(QMinimalIntegrationPlugin)#endif
Q_IMPORT_PLUGIN(QSQLiteDriverPlugin)#if !defined(Q_OS_IOS)Q_IMPORT_PLUGIN(QTlsBackendOpenSSL)#endifQ_IMPORT_PLUGIN(QSvgPlugin)Q_IMPORT_PLUGIN(QICOPlugin)Q_IMPORT_PLUGIN(QJpegPlugin)Q_IMPORT_PLUGIN(QGifPlugin)
#endif // QT_STATIC
namespace {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)/// This class allows to change the button of a mouse event on the fly./// This is required because we want to change the behaviour of Qts mouse/// buttony synthesizer without duplicate all the code.class QMouseEventEditable : public QMouseEvent { public: void setButton(Qt::MouseButton button) { b = button; }#if QT_VERSION <= QT_VERSION_CHECK(5, 12, 4) && defined(__APPLE__) // We also use this class to modify erroneous mouseState. See // MixxxApplication::notify(...) for details. void setButtons(Qt::MouseButtons mouseState) { this->mouseState = mouseState; }#endif};#endif
// kEventNotifyExecTimeWarningThreshold defines the threshold duration for event// processing warnings. If the processing time of an event exceeds this duration// in developer mode, a warning will be logged. This is used to identify// potentially slow event processing in the application, which could impact// performance. With a 60Hz waveform update rate, paint and swap events must be// processed through the event queue every 16.6ms, to ensure smooth rendering.// Exceeding this processing time can lead to visible delays, therefore 10ms is a// reasonable threshold.constexpr int kDefaultEventNotifyExecTimeWarningThreshold = 10;
} // anonymous namespace
MixxxApplication::MixxxApplication(int& argc, char** argv) : QApplication(argc, argv), m_rightPressedButtons(0), m_pTouchShift(nullptr), m_isDeveloper(CmdlineArgs::Instance().getDeveloper()), m_eventNotifyExecTimeWarningThreshold( mixxx::Duration::fromMillis(kDefaultEventNotifyExecTimeWarningThreshold)) { registerMetaTypes();
// Increase the size of the global thread pool to at least // 4 threads, even if less cores are available. These threads // will be used for loading external libraries and other tasks. QThreadPool::globalInstance()->setMaxThreadCount( math_max(4, QThreadPool::globalInstance()->maxThreadCount()));}
void MixxxApplication::registerMetaTypes() { // PCM audio types qRegisterMetaType<mixxx::audio::ChannelCount>("mixxx::audio::ChannelCount"); qRegisterMetaType<mixxx::audio::ChannelLayout>("mixxx::audio::ChannelLayout"); qRegisterMetaType<mixxx::audio::OptionalChannelLayout>("mixxx::audio::OptionalChannelLayout"); qRegisterMetaType<mixxx::audio::SampleRate>("mixxx::audio::SampleRate"); qRegisterMetaType<mixxx::audio::Bitrate>("mixxx::audio::Bitrate");
// Tracks qRegisterMetaType<TrackId>(); qRegisterMetaType<QSet<TrackId>>(); qRegisterMetaType<QList<TrackId>>(); qRegisterMetaType<TrackRef>(); qRegisterMetaType<QList<TrackRef>>(); qRegisterMetaType<QList<QPair<TrackRef, TrackRef>>>(); qRegisterMetaType<TrackPointer>();
// Crates qRegisterMetaType<CrateId>(); qRegisterMetaType<QSet<CrateId>>(); qRegisterMetaType<QList<CrateId>>();
// Sound devices qRegisterMetaType<SoundDeviceId>();#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QMetaType::registerComparators<SoundDeviceId>();#endif
// Library Scanner qRegisterMetaType<RelocatedTrack>(); qRegisterMetaType<QList<RelocatedTrack>>();
// Various custom data types qRegisterMetaType<mixxx::ReplayGain>("mixxx::ReplayGain"); qRegisterMetaType<mixxx::cache_key_t>("mixxx::cache_key_t"); qRegisterMetaType<mixxx::Bpm>("mixxx::Bpm"); qRegisterMetaType<mixxx::Duration>("mixxx::Duration"); qRegisterMetaType<mixxx::audio::FramePos>("mixxx::audio::FramePos"); qRegisterMetaType<std::optional<mixxx::RgbColor>>("std::optional<mixxx::RgbColor>"); qRegisterMetaType<mixxx::FileInfo>("mixxx::FileInfo");}
void MixxxApplication::setNotifyWarningThreshold(int threshold) { if (threshold > kDefaultEventNotifyExecTimeWarningThreshold) { m_eventNotifyExecTimeWarningThreshold = mixxx::Duration::fromMillis(threshold); }}
bool MixxxApplication::notify(QObject* pTarget, QEvent* pEvent) {#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // All touch events are translated into two simultaneous events: one for // the target QWidgetWindow and one for the target QWidget. // A second touch becomes a mouse move without additional press and release // events. switch (pEvent->type()) { case QEvent::MouseButtonPress: { QMouseEventEditable* pMouseEvent = static_cast<QMouseEventEditable*>(pEvent); // clazy:exclude=wrong-qevent-cast if (pMouseEvent->source() == Qt::MouseEventSynthesizedByQt && pMouseEvent->button() == Qt::LeftButton && touchIsRightButton()) { // Assert the assumption that QT synthesizes only one click at a time // = two events (see above) VERIFY_OR_DEBUG_ASSERT(m_rightPressedButtons < 2) { break; } pMouseEvent->setButton(Qt::RightButton); m_rightPressedButtons++; }#if QT_VERSION <= QT_VERSION_CHECK(5, 12, 4) && defined(__APPLE__) if (pMouseEvent->button() == Qt::RightButton && pMouseEvent->buttons() == Qt::LeftButton) { // Workaround for a bug in Qt 5.12 qnsview_mouse.mm, where the wrong value is // assigned to the event's mouseState for simulated rightbutton press events // (using ctrl+leftbotton), which results in a missing release event for that // press event. // // Fixed in Qt 5.12.5. See // https://github.com/qt/qtbase/commit/9a47768b46f5e5eed407b70dfa9183fa1d21e242 pMouseEvent->setButtons(Qt::RightButton); }#endif break; } case QEvent::MouseButtonRelease: { QMouseEventEditable* pMouseEvent = static_cast<QMouseEventEditable*>(pEvent); // clazy:exclude=wrong-qevent-cast if (pMouseEvent->source() == Qt::MouseEventSynthesizedByQt && pMouseEvent->button() == Qt::LeftButton && m_rightPressedButtons > 0) { pMouseEvent->setButton(Qt::RightButton); m_rightPressedButtons--; } break; } default: break; }#endif
PerformanceTimer time;
if (m_isDeveloper) { time.start(); }
bool ret = QApplication::notify(pTarget, pEvent);
VERIFY_OR_DEBUG_ASSERT(pTarget != nullptr) { qWarning() << "Processed" << pEvent->type() << "for null pointer, this is probably a bug!"; return ret; }
if (m_isDeveloper && time.elapsed() > m_eventNotifyExecTimeWarningThreshold) { QDebug debug = qDebug(); debug << "Processing" << pEvent->type() << "for object"; if (pEvent->type() == QEvent::DeferredDelete || pEvent->type() == QEvent::ChildRemoved || pEvent->type() == QEvent::Timer) { // pTarget can be already dangling in case of DeferredDelete debug << static_cast<void*>(pTarget); // will print dangling address } else { debug << pTarget // will print address, class and object name << "running in thread:" << pTarget->thread()->objectName(); } debug << "took" << time.elapsed().debugMillisWithUnit(); }
return ret;}
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)bool MixxxApplication::touchIsRightButton() { if (!m_pTouchShift) { m_pTouchShift = new ControlProxy( "[Controls]", "touch_shift", this); } return m_pTouchShift->toBool();}#endif