--- qsampler/trunk/src/qsamplerMainForm.cpp 2017/10/17 21:44:20 3357 +++ qsampler/trunk/src/qsamplerMainForm.cpp 2020/01/02 19:03:34 3685 @@ -1,8 +1,8 @@ // qsamplerMainForm.cpp // /**************************************************************************** - Copyright (C) 2004-2017, rncbc aka Rui Nuno Capela. All rights reserved. - Copyright (C) 2007,2008,2015 Christian Schoenebeck + Copyright (C) 2004-2020, rncbc aka Rui Nuno Capela. All rights reserved. + Copyright (C) 2007-2019 Christian Schoenebeck This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -58,20 +58,18 @@ #include #include -#if QT_VERSION >= 0x050000 +#include + +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #endif -#if QT_VERSION < 0x040500 +#if QT_VERSION < QT_VERSION_CHECK(4, 5, 0) namespace Qt { const WindowFlags WindowCloseButtonHint = WindowFlags(0x08000000); } #endif -#ifdef HAVE_SIGNAL_H -#include -#endif - #ifdef CONFIG_LIBGIG #include #endif @@ -91,8 +89,9 @@ // All winsock apps needs this. -#if defined(_WIN32) +#if defined(__WIN32__) || defined(_WIN32) || defined(WIN32) static WSADATA _wsaData; +#undef HAVE_SIGNAL_H #endif @@ -103,20 +102,31 @@ #include +#include #include #include - #include // File descriptor for SIGUSR1 notifier. -static int g_fdUsr1[2]; +static int g_fdSigusr1[2] = { -1, -1 }; // Unix SIGUSR1 signal handler. static void qsampler_sigusr1_handler ( int /* signo */ ) { char c = 1; - (::write(g_fdUsr1[0], &c, sizeof(c)) > 0); + (::write(g_fdSigusr1[0], &c, sizeof(c)) > 0); +} + +// File descriptor for SIGTERM notifier. +static int g_fdSigterm[2] = { -1, -1 }; + +// Unix SIGTERM signal handler. +static void qsampler_sigterm_handler ( int /* signo */ ) +{ + char c = 1; + + (::write(g_fdSigterm[0], &c, sizeof(c)) > 0); } #endif // HAVE_SIGNAL_H @@ -194,7 +204,7 @@ // QSampler::MainForm -- Main window form implementation. // Kind of singleton reference. -MainForm *MainForm::g_pMainForm = NULL; +MainForm *MainForm::g_pMainForm = nullptr; MainForm::MainForm ( QWidget *pParent ) : QMainWindow(pParent) @@ -205,20 +215,20 @@ g_pMainForm = this; // Initialize some pointer references. - m_pOptions = NULL; + m_pOptions = nullptr; // All child forms are to be created later, not earlier than setup. - m_pMessages = NULL; - m_pInstrumentListForm = NULL; - m_pDeviceForm = NULL; + m_pMessages = nullptr; + m_pInstrumentListForm = nullptr; + m_pDeviceForm = nullptr; // We'll start clean. m_iUntitled = 0; m_iDirtySetup = 0; m_iDirtyCount = 0; - m_pServer = NULL; - m_pClient = NULL; + m_pServer = nullptr; + m_pClient = nullptr; m_iStartDelay = 0; m_iTimerDelay = 0; @@ -233,25 +243,48 @@ // LADISH Level 1 suport. // Initialize file descriptors for SIGUSR1 socket notifier. - ::socketpair(AF_UNIX, SOCK_STREAM, 0, g_fdUsr1); - m_pUsr1Notifier - = new QSocketNotifier(g_fdUsr1[1], QSocketNotifier::Read, this); + ::socketpair(AF_UNIX, SOCK_STREAM, 0, g_fdSigusr1); + m_pSigusr1Notifier + = new QSocketNotifier(g_fdSigusr1[1], QSocketNotifier::Read, this); - QObject::connect(m_pUsr1Notifier, + QObject::connect(m_pSigusr1Notifier, SIGNAL(activated(int)), SLOT(handle_sigusr1())); // Install SIGUSR1 signal handler. - struct sigaction usr1; - usr1.sa_handler = qsampler_sigusr1_handler; - sigemptyset(&usr1.sa_mask); - usr1.sa_flags = 0; - usr1.sa_flags |= SA_RESTART; - ::sigaction(SIGUSR1, &usr1, NULL); + struct sigaction sigusr1; + sigusr1.sa_handler = qsampler_sigusr1_handler; + sigemptyset(&sigusr1.sa_mask); + sigusr1.sa_flags = 0; + sigusr1.sa_flags |= SA_RESTART; + ::sigaction(SIGUSR1, &sigusr1, nullptr); + + // Initialize file descriptors for SIGTERM socket notifier. + ::socketpair(AF_UNIX, SOCK_STREAM, 0, g_fdSigterm); + m_pSigtermNotifier + = new QSocketNotifier(g_fdSigterm[1], QSocketNotifier::Read, this); + + QObject::connect(m_pSigtermNotifier, + SIGNAL(activated(int)), + SLOT(handle_sigterm())); + + // Install SIGTERM signal handler. + struct sigaction sigterm; + sigterm.sa_handler = qsampler_sigterm_handler; + sigemptyset(&sigterm.sa_mask); + sigterm.sa_flags = 0; + sigterm.sa_flags |= SA_RESTART; + ::sigaction(SIGTERM, &sigterm, nullptr); + ::sigaction(SIGQUIT, &sigterm, nullptr); + + // Ignore SIGHUP/SIGINT signals. + ::signal(SIGHUP, SIG_IGN); + ::signal(SIGINT, SIG_IGN); #else // HAVE_SIGNAL_H - m_pUsr1Notifier = NULL; + m_pSigusr1Notifier = nullptr; + m_pSigtermNotifier = nullptr; #endif // !HAVE_SIGNAL_H @@ -324,7 +357,7 @@ m_statusItem[QSAMPLER_STATUS_SESSION] = pLabel; statusBar()->addWidget(pLabel); -#if defined(_WIN32) +#if defined(__WIN32__) || defined(_WIN32) || defined(WIN32) WSAStartup(MAKEWORD(1, 1), &_wsaData); #endif @@ -425,13 +458,15 @@ // Do final processing anyway. processServerExit(); -#if defined(_WIN32) +#if defined(__WIN32__) || defined(_WIN32) || defined(WIN32) WSACleanup(); #endif #if defined(HAVE_SIGNAL_H) && defined(HAVE_SYS_SOCKET_H) - if (m_pUsr1Notifier) - delete m_pUsr1Notifier; + if (m_pSigusr1Notifier) + delete m_pSigusr1Notifier; + if (m_pSigtermNotifier) + delete m_pSigtermNotifier; #endif // Finally drop any widgets around... @@ -460,7 +495,7 @@ #endif // Pseudo-singleton reference shut-down. - g_pMainForm = NULL; + g_pMainForm = nullptr; } @@ -597,7 +632,7 @@ void MainForm::dragEnterEvent ( QDragEnterEvent* pDragEnterEvent ) { // Accept external drags only... - if (pDragEnterEvent->source() == NULL + if (pDragEnterEvent->source() == nullptr && pDragEnterEvent->mimeData()->hasUrls()) { pDragEnterEvent->accept(); } else { @@ -621,7 +656,7 @@ if (QFileInfo(sPath).exists()) { // Try to create a new channel from instrument file... Channel *pChannel = new Channel(); - if (pChannel == NULL) + if (pChannel == nullptr) return; // Start setting the instrument filename... pChannel->setInstrument(sPath, 0); @@ -721,13 +756,26 @@ char c; - if (::read(g_fdUsr1[1], &c, sizeof(c)) > 0) + if (::read(g_fdSigusr1[1], &c, sizeof(c)) > 0) saveSession(false); #endif } +void MainForm::handle_sigterm (void) +{ +#if defined(HAVE_SIGNAL_H) && defined(HAVE_SYS_SOCKET_H) + + char c; + + if (::read(g_fdSigterm[1], &c, sizeof(c)) > 0) + close(); + +#endif +} + + void MainForm::updateViewMidiDeviceStatusMenu (void) { m_ui.viewMidiDeviceStatusMenu->clear(); @@ -806,7 +854,7 @@ m_iUntitled++; // Stabilize form. - m_sFilename = QString::null; + m_sFilename = QString(); m_iDirtyCount = 0; appendMessages(tr("New session: \"%1\".").arg(sessionName(m_sFilename))); stabilizeForm(); @@ -818,12 +866,12 @@ // Open an existing sampler session. bool MainForm::openSession (void) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return false; // Ask for the filename to open... QString sFilename = QFileDialog::getOpenFileName(this, - QSAMPLER_TITLE ": " + tr("Open Session"), // Caption. + tr("Open Session"), // Caption. m_pOptions->sSessionDir, // Start here. tr("LSCP Session files") + " (*.lscp)" // Filter (LSCP files) ); @@ -844,7 +892,7 @@ // Save current sampler session with another name. bool MainForm::saveSession ( bool bPrompt ) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return false; QString sFilename = m_sFilename; @@ -856,7 +904,7 @@ sFilename = m_pOptions->sSessionDir; // Prompt the guy... sFilename = QFileDialog::getSaveFileName(this, - QSAMPLER_TITLE ": " + tr("Save Session"), // Caption. + tr("Save Session"), // Caption. sFilename, // Start here. tr("LSCP Session files") + " (*.lscp)" // Filter (LSCP files) ); @@ -866,10 +914,11 @@ // Enforce .lscp extension... if (QFileInfo(sFilename).suffix().isEmpty()) sFilename += ".lscp"; + #if 0 // Check if already exists... if (sFilename != m_sFilename && QFileInfo(sFilename).exists()) { if (QMessageBox::warning(this, - QSAMPLER_TITLE ": " + tr("Warning"), + tr("Warning"), tr("The file already exists:\n\n" "\"%1\"\n\n" "Do you want to replace it?") @@ -878,6 +927,7 @@ == QMessageBox::No) return false; } + #endif } // Save it right away. @@ -893,7 +943,7 @@ // Are we dirty enough to prompt it? if (m_iDirtyCount > 0) { switch (QMessageBox::warning(this, - QSAMPLER_TITLE ": " + tr("Warning"), + tr("Warning"), tr("The current session has been changed:\n\n" "\"%1\"\n\n" "Do you want to save the changes?") @@ -918,20 +968,16 @@ m_pWorkspace->setUpdatesEnabled(false); const QList& wlist = m_pWorkspace->subWindowList(); - const int iStripCount = wlist.count(); - for (int iStrip = 0; iStrip < iStripCount; ++iStrip) { - ChannelStrip *pChannelStrip = NULL; - QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); - if (pMdiSubWindow) - pChannelStrip = static_cast (pMdiSubWindow->widget()); + foreach (QMdiSubWindow *pMdiSubWindow, wlist) { + ChannelStrip *pChannelStrip + = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) { Channel *pChannel = pChannelStrip->channel(); if (bForce && pChannel) pChannel->removeChannel(); delete pChannelStrip; } - if (pMdiSubWindow) - delete pMdiSubWindow; + delete pMdiSubWindow; } m_pWorkspace->setUpdatesEnabled(true); // We're now clean, for sure. @@ -945,7 +991,7 @@ // Load a session from specific file path. bool MainForm::loadSessionFile ( const QString& sFilename ) { - if (m_pClient == NULL) + if (m_pClient == nullptr) return false; // Open and read from real file. @@ -1021,7 +1067,7 @@ // Save current session to specific file path. bool MainForm::saveSessionFile ( const QString& sFilename ) { - if (m_pClient == NULL) + if (m_pClient == nullptr) return false; // Check whether server is apparently OK... @@ -1060,16 +1106,19 @@ // It is assumed that this new kind of device+session file // will be loaded from a complete initialized server... int *piDeviceIDs; - int iDevice; + int i, iDevice; ts << "RESET" << endl; // Audio device mapping. - QMap audioDeviceMap; + QMap audioDeviceMap; iDevice = 0; piDeviceIDs = Device::getDevices(m_pClient, Device::Audio); - for (iDevice = 0; piDeviceIDs && piDeviceIDs[iDevice] >= 0; iDevice++) { - ts << endl; - Device device(Device::Audio, piDeviceIDs[iDevice]); + for (i = 0; piDeviceIDs && piDeviceIDs[i] >= 0; ++i) { + Device device(Device::Audio, piDeviceIDs[i]); + // Avoid plug-in driver devices... + if (device.driverName().toUpper() == "PLUGIN") + continue; // Audio device specification... + ts << endl; ts << "# " << device.deviceTypeName() << " " << device.driverName() << " " << tr("Device") << " " << iDevice << endl; ts << "CREATE AUDIO_OUTPUT_DEVICE " << device.driverName(); @@ -1100,18 +1149,21 @@ iPort++; } // Audio device index/id mapping. - audioDeviceMap[device.deviceID()] = iDevice; + audioDeviceMap.insert(device.deviceID(), iDevice++); // Try to keep it snappy :) QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); } // MIDI device mapping. - QMap midiDeviceMap; + QMap midiDeviceMap; iDevice = 0; piDeviceIDs = Device::getDevices(m_pClient, Device::Midi); - for (iDevice = 0; piDeviceIDs && piDeviceIDs[iDevice] >= 0; iDevice++) { - ts << endl; - Device device(Device::Midi, piDeviceIDs[iDevice]); + for (i = 0; piDeviceIDs && piDeviceIDs[i] >= 0; ++i) { + Device device(Device::Midi, piDeviceIDs[i]); + // Avoid plug-in driver devices... + if (device.driverName().toUpper() == "PLUGIN") + continue; // MIDI device specification... + ts << endl; ts << "# " << device.deviceTypeName() << " " << device.driverName() << " " << tr("Device") << " " << iDevice << endl; ts << "CREATE MIDI_INPUT_DEVICE " << device.driverName(); @@ -1142,7 +1194,7 @@ iPort++; } // MIDI device index/id mapping. - midiDeviceMap[device.deviceID()] = iDevice; + midiDeviceMap.insert(device.deviceID(), iDevice++); // Try to keep it snappy :) QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); } @@ -1205,33 +1257,38 @@ } ts << endl; // Check for errors... - if (pInstrs == NULL && ::lscp_client_get_errno(m_pClient)) { + if (pInstrs == nullptr && ::lscp_client_get_errno(m_pClient)) { appendMessagesClient("lscp_list_midi_instruments"); iErrors++; } // MIDI strument index/id mapping. - midiInstrumentMap[iMidiMap] = iMap; + midiInstrumentMap.insert(iMidiMap, iMap); } // Check for errors... - if (piMaps == NULL && ::lscp_client_get_errno(m_pClient)) { + if (piMaps == nullptr && ::lscp_client_get_errno(m_pClient)) { appendMessagesClient("lscp_list_midi_instrument_maps"); iErrors++; } #endif // CONFIG_MIDI_INSTRUMENT - // Sampler channel mapping. + // Sampler channel mapping... + int iChannelID = 0; const QList& wlist = m_pWorkspace->subWindowList(); - const int iStripCount = wlist.count(); - for (int iStrip = 0; iStrip < iStripCount; ++iStrip) { - ChannelStrip *pChannelStrip = NULL; - QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); - if (pMdiSubWindow) - pChannelStrip = static_cast (pMdiSubWindow->widget()); + foreach (QMdiSubWindow *pMdiSubWindow, wlist) { + ChannelStrip *pChannelStrip + = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) { Channel *pChannel = pChannelStrip->channel(); if (pChannel) { - const int iChannelID = pChannel->channelID(); + // Avoid "artifial" plug-in devices... + const int iAudioDevice = pChannel->audioDevice(); + if (!audioDeviceMap.contains(iAudioDevice)) + continue; + const int iMidiDevice = pChannel->midiDevice(); + if (!midiDeviceMap.contains(iMidiDevice)) + continue; + // Go for regular, canonical devices... ts << "# " << tr("Channel") << " " << iChannelID << endl; ts << "ADD CHANNEL" << endl; if (audioDeviceMap.isEmpty()) { @@ -1239,14 +1296,14 @@ << " " << pChannel->audioDriver() << endl; } else { ts << "SET CHANNEL AUDIO_OUTPUT_DEVICE " << iChannelID - << " " << audioDeviceMap[pChannel->audioDevice()] << endl; + << " " << audioDeviceMap.value(iAudioDevice) << endl; } if (midiDeviceMap.isEmpty()) { ts << "SET CHANNEL MIDI_INPUT_TYPE " << iChannelID << " " << pChannel->midiDriver() << endl; } else { ts << "SET CHANNEL MIDI_INPUT_DEVICE " << iChannelID - << " " << midiDeviceMap[pChannel->midiDevice()] << endl; + << " " << midiDeviceMap.value(iMidiDevice) << endl; } ts << "SET CHANNEL MIDI_INPUT_PORT " << iChannelID << " " << pChannel->midiPort() << endl; @@ -1277,9 +1334,10 @@ if (pChannel->channelSolo()) ts << "SET CHANNEL SOLO " << iChannelID << " 1" << endl; #ifdef CONFIG_MIDI_INSTRUMENT - if (pChannel->midiMap() >= 0) { + const int iMidiMap = pChannel->midiMap(); + if (midiInstrumentMap.contains(iMidiMap)) { ts << "SET CHANNEL MIDI_INSTRUMENT_MAP " << iChannelID - << " " << midiInstrumentMap[pChannel->midiMap()] << endl; + << " " << midiInstrumentMap.value(iMidiMap) << endl; } #endif #ifdef CONFIG_FXSEND @@ -1318,6 +1376,8 @@ } #endif ts << endl; + // Go for next channel... + ++iChannelID; } } // Try to keep it snappy :) @@ -1423,12 +1483,12 @@ // Reset the sampler instance. void MainForm::fileReset (void) { - if (m_pClient == NULL) + if (m_pClient == nullptr) return; // Ask user whether he/she want's an internal sampler reset... if (m_pOptions && m_pOptions->bConfirmReset) { - const QString& sTitle = QSAMPLER_TITLE ": " + tr("Warning"); + const QString& sTitle = tr("Warning"); const QString& sText = tr( "Resetting the sampler instance will close\n" "all device and channel configurations.\n\n" @@ -1479,7 +1539,7 @@ // Restart the client/server instance. void MainForm::fileRestart (void) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; bool bRestart = true; @@ -1487,7 +1547,7 @@ // Ask user whether he/she want's a complete restart... // (if we're currently up and running) if (m_pOptions && m_pOptions->bConfirmRestart) { - const QString& sTitle = QSAMPLER_TITLE ": " + tr("Warning"); + const QString& sTitle = tr("Warning"); const QString& sText = tr( "New settings will be effective after\n" "restarting the client/server connection.\n\n" @@ -1547,12 +1607,12 @@ void MainForm::addChannelStrip (void) { - if (m_pClient == NULL) + if (m_pClient == nullptr) return; // Just create the channel instance... Channel *pChannel = new Channel(); - if (pChannel == NULL) + if (pChannel == nullptr) return; // Before we show it up, may be we'll @@ -1588,20 +1648,20 @@ void MainForm::removeChannelStrip (void) { - if (m_pClient == NULL) + if (m_pClient == nullptr) return; ChannelStrip *pChannelStrip = activeChannelStrip(); - if (pChannelStrip == NULL) + if (pChannelStrip == nullptr) return; Channel *pChannel = pChannelStrip->channel(); - if (pChannel == NULL) + if (pChannel == nullptr) return; // Prompt user if he/she's sure about this... if (m_pOptions && m_pOptions->bConfirmRemove) { - const QString& sTitle = QSAMPLER_TITLE ": " + tr("Warning"); + const QString& sTitle = tr("Warning"); const QString& sText = tr( "About to remove channel:\n\n" "%1\n\n" @@ -1644,11 +1704,11 @@ // Setup current sampler channel. void MainForm::editSetupChannel (void) { - if (m_pClient == NULL) + if (m_pClient == nullptr) return; ChannelStrip *pChannelStrip = activeChannelStrip(); - if (pChannelStrip == NULL) + if (pChannelStrip == nullptr) return; // Just invoque the channel strip procedure. @@ -1659,11 +1719,11 @@ // Edit current sampler channel. void MainForm::editEditChannel (void) { - if (m_pClient == NULL) + if (m_pClient == nullptr) return; ChannelStrip *pChannelStrip = activeChannelStrip(); - if (pChannelStrip == NULL) + if (pChannelStrip == nullptr) return; // Just invoque the channel strip procedure. @@ -1674,11 +1734,11 @@ // Reset current sampler channel. void MainForm::editResetChannel (void) { - if (m_pClient == NULL) + if (m_pClient == nullptr) return; ChannelStrip *pChannelStrip = activeChannelStrip(); - if (pChannelStrip == NULL) + if (pChannelStrip == nullptr) return; // Just invoque the channel strip procedure. @@ -1689,7 +1749,7 @@ // Reset all sampler channels. void MainForm::editResetAllChannels (void) { - if (m_pClient == NULL) + if (m_pClient == nullptr) return; // Invoque the channel strip procedure, @@ -1697,12 +1757,9 @@ m_pWorkspace->setUpdatesEnabled(false); const QList& wlist = m_pWorkspace->subWindowList(); - const int iStripCount = wlist.count(); - for (int iStrip = 0; iStrip < iStripCount; ++iStrip) { - ChannelStrip *pChannelStrip = NULL; - QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); - if (pMdiSubWindow) - pChannelStrip = static_cast (pMdiSubWindow->widget()); + foreach (QMdiSubWindow *pMdiSubWindow, wlist) { + ChannelStrip *pChannelStrip + = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) pChannelStrip->channelReset(); } @@ -1761,7 +1818,7 @@ // Show/hide the MIDI instrument list-view form. void MainForm::viewInstruments (void) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; if (m_pInstrumentListForm) { @@ -1780,7 +1837,7 @@ // Show/hide the device configurator form. void MainForm::viewDevices (void) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; if (m_pDeviceForm) { @@ -1799,7 +1856,7 @@ // Show options dialog. void MainForm::viewOptions (void) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; OptionsForm* pOptionsForm = new OptionsForm(this); @@ -1841,7 +1898,7 @@ (!bOldKeepOnTop && m_pOptions->bKeepOnTop) || (iOldBaseFontSize != m_pOptions->iBaseFontSize)) { QMessageBox::information(this, - QSAMPLER_TITLE ": " + tr("Information"), + tr("Information"), tr("Some settings may be only effective\n" "next time you start this program.")); updateMessagesCapture(); @@ -1905,18 +1962,16 @@ m_pWorkspace->setUpdatesEnabled(false); int y = 0; - const int iStripCount = wlist.count(); - for (int iStrip = 0; iStrip < iStripCount; ++iStrip) { - QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); - if (pMdiSubWindow) { - pMdiSubWindow->adjustSize(); - int iWidth = m_pWorkspace->width(); - if (iWidth < pMdiSubWindow->width()) - iWidth = pMdiSubWindow->width(); - const int iHeight = pMdiSubWindow->frameGeometry().height(); - pMdiSubWindow->setGeometry(0, y, iWidth, iHeight); - y += iHeight; - } + foreach (QMdiSubWindow *pMdiSubWindow, wlist) { + pMdiSubWindow->adjustSize(); + const QRect& frameRect + = pMdiSubWindow->frameGeometry(); + int w = m_pWorkspace->width(); + if (w < frameRect.width()) + w = frameRect.width(); + const int h = frameRect.height(); + pMdiSubWindow->setGeometry(0, y, w, h); + y += h; } m_pWorkspace->setUpdatesEnabled(true); @@ -1927,7 +1982,7 @@ // Auto-arrange channel strips. void MainForm::channelsAutoArrange ( bool bOn ) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; // Toggle the auto-arrange flag. @@ -2031,7 +2086,7 @@ sText += ""; sText += "

\n"; - QMessageBox::about(this, tr("About") + " " QSAMPLER_TITLE, sText); + QMessageBox::about(this, tr("About"), sText); } @@ -2044,20 +2099,20 @@ QString sSessionName = sessionName(m_sFilename); if (m_iDirtyCount > 0) sSessionName += " *"; - setWindowTitle(tr(QSAMPLER_TITLE " - [%1]").arg(sSessionName)); + setWindowTitle(sSessionName); // Update the main menu state... ChannelStrip *pChannelStrip = activeChannelStrip(); const QList& wlist = m_pWorkspace->subWindowList(); - const bool bHasClient = (m_pOptions != NULL && m_pClient != NULL); - const bool bHasChannel = (bHasClient && pChannelStrip != NULL); + const bool bHasClient = (m_pOptions != nullptr && m_pClient != nullptr); + const bool bHasChannel = (bHasClient && pChannelStrip != nullptr); const bool bHasChannels = (bHasClient && wlist.count() > 0); m_ui.fileNewAction->setEnabled(bHasClient); m_ui.fileOpenAction->setEnabled(bHasClient); m_ui.fileSaveAction->setEnabled(bHasClient && m_iDirtyCount > 0); m_ui.fileSaveAsAction->setEnabled(bHasClient); m_ui.fileResetAction->setEnabled(bHasClient); - m_ui.fileRestartAction->setEnabled(bHasClient || m_pServer == NULL); + m_ui.fileRestartAction->setEnabled(bHasClient || m_pServer == nullptr); m_ui.editAddChannelAction->setEnabled(bHasClient); m_ui.editRemoveChannelAction->setEnabled(bHasChannel); m_ui.editSetupChannelAction->setEnabled(bHasChannel); @@ -2206,7 +2261,7 @@ // Retrieve the current channel list. int *piChannelIDs = ::lscp_list_channels(m_pClient); - if (piChannelIDs == NULL) { + if (piChannelIDs == nullptr) { if (::lscp_client_get_errno(m_pClient)) { appendMessagesClient("lscp_list_channels"); appendMessagesError( @@ -2226,17 +2281,14 @@ if (bRemoveDeadStrips) { const QList& wlist = m_pWorkspace->subWindowList(); - const int iStripCount = wlist.count(); - for (int iStrip = 0; iStrip < iStripCount; ++iStrip) { - ChannelStrip *pChannelStrip = NULL; - QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); - if (pMdiSubWindow) - pChannelStrip = static_cast (pMdiSubWindow->widget()); + foreach (QMdiSubWindow *pMdiSubWindow, wlist) { + ChannelStrip *pChannelStrip + = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) { bool bExists = false; for (int iChannel = 0; piChannelIDs[iChannel] >= 0; ++iChannel) { Channel *pChannel = pChannelStrip->channel(); - if (pChannel == NULL) + if (pChannel == nullptr) break; if (piChannelIDs[iChannel] == pChannel->channelID()) { // strip exists, don't touch it @@ -2259,7 +2311,7 @@ // Update the recent files list and menu. void MainForm::updateRecentFiles ( const QString& sFilename ) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; // Remove from list if already there (avoid duplicates) @@ -2274,7 +2326,7 @@ // Update the recent files list and menu. void MainForm::updateRecentFilesMenu (void) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; // Time to keep the list under limits. @@ -2308,12 +2360,9 @@ return; m_pWorkspace->setUpdatesEnabled(false); - const int iStripCount = wlist.count(); - for (int iStrip = 0; iStrip < iStripCount; ++iStrip) { - ChannelStrip *pChannelStrip = NULL; - QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); - if (pMdiSubWindow) - pChannelStrip = static_cast (pMdiSubWindow->widget()); + foreach (QMdiSubWindow *pMdiSubWindow, wlist) { + ChannelStrip *pChannelStrip + = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) pChannelStrip->updateInstrumentName(true); } @@ -2324,7 +2373,7 @@ // Force update of the channels display font. void MainForm::updateDisplayFont (void) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; // Check if display font is legal. @@ -2343,12 +2392,9 @@ return; m_pWorkspace->setUpdatesEnabled(false); - const int iStripCount = wlist.count(); - for (int iStrip = 0; iStrip < iStripCount; ++iStrip) { - ChannelStrip *pChannelStrip = NULL; - QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); - if (pMdiSubWindow) - pChannelStrip = static_cast (pMdiSubWindow->widget()); + foreach (QMdiSubWindow *pMdiSubWindow, wlist) { + ChannelStrip *pChannelStrip + = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) pChannelStrip->setDisplayFont(font); } @@ -2366,12 +2412,9 @@ return; m_pWorkspace->setUpdatesEnabled(false); - const int iStripCount = wlist.count(); - for (int iStrip = 0; iStrip < iStripCount; ++iStrip) { - ChannelStrip *pChannelStrip = NULL; - QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); - if (pMdiSubWindow) - pChannelStrip = static_cast (pMdiSubWindow->widget()); + foreach (QMdiSubWindow *pMdiSubWindow, wlist) { + ChannelStrip *pChannelStrip + = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect); } @@ -2382,7 +2425,7 @@ // Force update of the channels maximum volume setting. void MainForm::updateMaxVolume (void) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; #ifdef CONFIG_VOLUME @@ -2399,12 +2442,9 @@ return; m_pWorkspace->setUpdatesEnabled(false); - const int iStripCount = wlist.count(); - for (int iStrip = 0; iStrip < iStripCount; ++iStrip) { - ChannelStrip *pChannelStrip = NULL; - QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); - if (pMdiSubWindow) - pChannelStrip = static_cast (pMdiSubWindow->widget()); + foreach (QMdiSubWindow *pMdiSubWindow, wlist) { + ChannelStrip *pChannelStrip + = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume); } @@ -2449,7 +2489,7 @@ QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); if (m_pOptions && m_pOptions->bConfirmError) { - const QString& sTitle = QSAMPLER_TITLE ": " + tr("Error"); + const QString& sTitle = tr("Error"); #if 0 QMessageBox::critical(this, sTitle, sText, QMessageBox::Cancel); #else @@ -2472,7 +2512,7 @@ // This is a special message format, just for client results. void MainForm::appendMessagesClient( const QString& s ) { - if (m_pClient == NULL) + if (m_pClient == nullptr) return; appendMessagesColor(s + QString(": %1 (errno=%2)") @@ -2487,7 +2527,7 @@ // Force update of the messages font. void MainForm::updateMessagesFont (void) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; if (m_pMessages && !m_pOptions->sMessagesFont.isEmpty()) { @@ -2501,7 +2541,7 @@ // Update messages window line limit. void MainForm::updateMessagesLimit (void) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; if (m_pMessages) { @@ -2516,7 +2556,7 @@ // Enablement of the messages capture feature. void MainForm::updateMessagesCapture (void) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; if (m_pMessages) @@ -2530,13 +2570,13 @@ // The channel strip creation executive. ChannelStrip *MainForm::createChannelStrip ( Channel *pChannel ) { - if (m_pClient == NULL || pChannel == NULL) - return NULL; + if (m_pClient == nullptr || pChannel == nullptr) + return nullptr; // Add a new channel itema... ChannelStrip *pChannelStrip = new ChannelStrip(); - if (pChannelStrip == NULL) - return NULL; + if (pChannelStrip == nullptr) + return nullptr; // Set some initial channel strip options... if (m_pOptions) { @@ -2552,8 +2592,10 @@ } // Add it to workspace... - m_pWorkspace->addSubWindow(pChannelStrip, - Qt::SubWindow | Qt::FramelessWindowHint); + QMdiSubWindow *pMdiSubWindow + = m_pWorkspace->addSubWindow(pChannelStrip, + Qt::SubWindow | Qt::FramelessWindowHint); + pMdiSubWindow->setAttribute(Qt::WA_DeleteOnClose); // Actual channel strip setup... pChannelStrip->setup(pChannel); @@ -2577,7 +2619,7 @@ { QMdiSubWindow *pMdiSubWindow = static_cast (pChannelStrip->parentWidget()); - if (pMdiSubWindow == NULL) + if (pMdiSubWindow == nullptr) return; // Just delete the channel strip. @@ -2596,28 +2638,28 @@ if (pMdiSubWindow) return static_cast (pMdiSubWindow->widget()); else - return NULL; + return nullptr; } // Retrieve a channel strip by index. ChannelStrip *MainForm::channelStripAt ( int iStrip ) { - if (!m_pWorkspace) return NULL; + if (!m_pWorkspace) return nullptr; const QList& wlist = m_pWorkspace->subWindowList(); if (wlist.isEmpty()) - return NULL; + return nullptr; if (iStrip < 0 || iStrip >= wlist.count()) - return NULL; + return nullptr; QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); if (pMdiSubWindow) return static_cast (pMdiSubWindow->widget()); else - return NULL; + return nullptr; } @@ -2627,14 +2669,11 @@ const QList& wlist = m_pWorkspace->subWindowList(); if (wlist.isEmpty()) - return NULL; + return nullptr; - const int iStripCount = wlist.count(); - for (int iStrip = 0; iStrip < iStripCount; ++iStrip) { - ChannelStrip *pChannelStrip = NULL; - QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); - if (pMdiSubWindow) - pChannelStrip = static_cast (pMdiSubWindow->widget()); + foreach (QMdiSubWindow *pMdiSubWindow, wlist) { + ChannelStrip *pChannelStrip + = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) { Channel *pChannel = pChannelStrip->channel(); if (pChannel && pChannel->channelID() == iChannelID) @@ -2643,7 +2682,7 @@ } // Not found. - return NULL; + return nullptr; } @@ -2658,12 +2697,10 @@ = m_pWorkspace->subWindowList(); if (!wlist.isEmpty()) { m_ui.channelsMenu->addSeparator(); - const int iStripCount = wlist.count(); - for (int iStrip = 0; iStrip < iStripCount; ++iStrip) { - ChannelStrip *pChannelStrip = NULL; - QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); - if (pMdiSubWindow) - pChannelStrip = static_cast (pMdiSubWindow->widget()); + int iStrip = 0; + foreach (QMdiSubWindow *pMdiSubWindow, wlist) { + ChannelStrip *pChannelStrip + = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) { QAction *pAction = m_ui.channelsMenu->addAction( pChannelStrip->windowTitle(), @@ -2672,6 +2709,7 @@ pAction->setChecked(activeChannelStrip() == pChannelStrip); pAction->setData(iStrip); } + ++iStrip; } } } @@ -2682,7 +2720,7 @@ { // Retrive channel index from action data... QAction *pAction = qobject_cast (sender()); - if (pAction == NULL) + if (pAction == nullptr) return; ChannelStrip *pChannelStrip = channelStripAt(pAction->data().toInt()); @@ -2713,7 +2751,7 @@ // Timer slot funtion. void MainForm::timerSlot (void) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; // Is it the first shot on server start after a few delay? @@ -2748,17 +2786,25 @@ // Update the channel stream usage for each strip... const QList& wlist = m_pWorkspace->subWindowList(); - const int iStripCount = wlist.count(); - for (int iStrip = 0; iStrip < iStripCount; ++iStrip) { - ChannelStrip *pChannelStrip = NULL; - QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); - if (pMdiSubWindow) - pChannelStrip = static_cast (pMdiSubWindow->widget()); + foreach (QMdiSubWindow *pMdiSubWindow, wlist) { + ChannelStrip *pChannelStrip + = static_cast (pMdiSubWindow->widget()); if (pChannelStrip && pChannelStrip->isVisible()) pChannelStrip->updateChannelUsage(); } } } + + #if CONFIG_LSCP_CLIENT_CONNECTION_LOST + // If we lost connection to server: Try to automatically reconnect if we + // did not start the server. + // + // TODO: If we started the server, then we might inform the user that + // the server probably crashed and asking user ONCE whether we should + // restart the server. + if (lscp_client_connection_lost(m_pClient) && !m_pServer) + startAutoReconnectClient(); + #endif // CONFIG_LSCP_CLIENT_CONNECTION_LOST } // Register the next timer slot. @@ -2772,7 +2818,7 @@ // Start linuxsampler server... void MainForm::startServer (void) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; // Aren't already a client, are we? @@ -2782,7 +2828,7 @@ // Is the server process instance still here? if (m_pServer) { if (QMessageBox::warning(this, - QSAMPLER_TITLE ": " + tr("Warning"), + tr("Warning"), tr("Could not start the LinuxSampler server.\n\n" "Maybe it is already started."), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) { @@ -2852,7 +2898,7 @@ if (m_pServer && bInteractive) { if (QMessageBox::question(this, - QSAMPLER_TITLE ": " + tr("The backend's fate ..."), + tr("The backend's fate ..."), tr("You have the option to keep the sampler backend (LinuxSampler)\n" "running in the background. The sampler would continue to work\n" "according to your current sampler session and you could alter the\n" @@ -2864,26 +2910,32 @@ } } + bool bGraceWait = true; + // And try to stop server. if (m_pServer && m_bForceServerStop) { appendMessages(tr("Server is stopping...")); if (m_pServer->state() == QProcess::Running) { - #if defined(_WIN32) + #if defined(__WIN32__) || defined(_WIN32) || defined(WIN32) // Try harder... m_pServer->kill(); #else // Try softly... m_pServer->terminate(); + bool bFinished = m_pServer->waitForFinished(QSAMPLER_TIMER_MSECS * 1000); + if (bFinished) bGraceWait = false; #endif } } // Do final processing anyway. else processServerExit(); // Give it some time to terminate gracefully and stabilize... - QTime t; - t.start(); - while (t.elapsed() < QSAMPLER_TIMER_MSECS) - QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + if (bGraceWait) { + QElapsedTimer timer; + timer.start(); + while (timer.elapsed() < QSAMPLER_TIMER_MSECS) + QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + } } @@ -2911,9 +2963,9 @@ // Force final server shutdown... m_pServer->kill(); // Give it some time to terminate gracefully and stabilize... - QTime t; - t.start(); - while (t.elapsed() < QSAMPLER_TIMER_MSECS) + QElapsedTimer timer; + timer.start(); + while (timer.elapsed() < QSAMPLER_TIMER_MSECS) QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); } // Force final server shutdown... @@ -2921,7 +2973,7 @@ tr("Server was stopped with exit status %1.") .arg(m_pServer->exitStatus())); delete m_pServer; - m_pServer = NULL; + m_pServer = nullptr; } // Again, make status visible stable. @@ -2937,7 +2989,7 @@ lscp_event_t event, const char *pchData, int cchData, void *pvData ) { MainForm* pMainForm = (MainForm *) pvData; - if (pMainForm == NULL) + if (pMainForm == nullptr) return LSCP_FAILED; // ATTN: DO NOT EVER call any GUI code here, @@ -2951,10 +3003,10 @@ // Start our almighty client... -bool MainForm::startClient (void) +bool MainForm::startClient (bool bReconnectOnly) { // Have it a setup? - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return false; // Aren't we already started, are we? @@ -2968,13 +3020,19 @@ m_pClient = ::lscp_client_create( m_pOptions->sServerHost.toUtf8().constData(), m_pOptions->iServerPort, qsampler_client_callback, this); - if (m_pClient == NULL) { + if (m_pClient == nullptr) { // Is this the first try? // maybe we need to start a local server... if ((m_pServer && m_pServer->state() == QProcess::Running) - || !m_pOptions->bServerStart) { - appendMessagesError( - tr("Could not connect to server as client.\n\nSorry.")); + || !m_pOptions->bServerStart || bReconnectOnly) + { + // if this method is called from autoReconnectClient() + // then don't bother user with an error message... + if (!bReconnectOnly) { + appendMessagesError( + tr("Could not connect to server as client.\n\nSorry.") + ); + } } else { startServer(); } @@ -3038,7 +3096,7 @@ if (!m_pOptions->sSessionFile.isEmpty()) { // Just load the prabably startup session... if (loadSessionFile(m_pOptions->sSessionFile)) { - m_pOptions->sSessionFile = QString::null; + m_pOptions->sSessionFile = QString(); return true; } } @@ -3054,7 +3112,7 @@ // Stop client... void MainForm::stopClient (void) { - if (m_pClient == NULL) + if (m_pClient == nullptr) return; // Log prepare here. @@ -3086,7 +3144,7 @@ ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO); ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_COUNT); ::lscp_client_destroy(m_pClient); - m_pClient = NULL; + m_pClient = nullptr; // Hard-notify instrumnet and device configuration forms, // if visible, that we're running out... @@ -3103,10 +3161,26 @@ } +void MainForm::startAutoReconnectClient (void) +{ + stopClient(); + appendMessages(tr("Trying to reconnect...")); + QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(autoReconnectClient())); +} + + +void MainForm::autoReconnectClient (void) +{ + const bool bSuccess = startClient(true); + if (!bSuccess) + QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(autoReconnectClient())); +} + + // Channel strip activation/selection. void MainForm::activateStrip ( QMdiSubWindow *pMdiSubWindow ) { - ChannelStrip *pChannelStrip = NULL; + ChannelStrip *pChannelStrip = nullptr; if (pMdiSubWindow) pChannelStrip = static_cast (pMdiSubWindow->widget()); if (pChannelStrip)