--- linuxsampler/trunk/src/network/lscpserver.cpp 2004/08/22 14:46:47 225 +++ linuxsampler/trunk/src/network/lscpserver.cpp 2005/02/21 04:35:31 398 @@ -3,19 +3,20 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * + * Copyright (C) 2005 Christian Schoenebeck * * * - * This program is free software; you can redistribute it and/or modify * + * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * - * This program is distributed in the hope that it will be useful, * + * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software * + * along with this library; if not, write to the Free Software * * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * * MA 02111-1307 USA * ***************************************************************************/ @@ -23,6 +24,11 @@ #include "lscpserver.h" #include "lscpresultset.h" #include "lscpevent.h" +//#include "../common/global.h" + +#ifdef HAVE_SQLITE3 +#include "sqlite3.h" +#endif #include "../engines/gig/Engine.h" #include "../drivers/audio/AudioOutputDeviceFactory.h" @@ -50,8 +56,9 @@ Mutex LSCPServer::NotifyMutex = Mutex(); Mutex LSCPServer::NotifyBufferMutex = Mutex(); Mutex LSCPServer::SubscriptionMutex = Mutex(); +Mutex LSCPServer::RTNotifyMutex = Mutex(); -LSCPServer::LSCPServer(Sampler* pSampler) : Thread(false, 0, -4) { +LSCPServer::LSCPServer(Sampler* pSampler) : Thread(true, false, 0, -4) { this->pSampler = pSampler; LSCPEvent::RegisterEvent(LSCPEvent::event_channels, "CHANNELS"); LSCPEvent::RegisterEvent(LSCPEvent::event_voice_count, "VOICE_COUNT"); @@ -88,10 +95,19 @@ SocketAddress.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(hSocket, (sockaddr*) &SocketAddress, sizeof(sockaddr_in)) < 0) { - std::cerr << "LSCPServer: Could not bind server socket." << std::endl; - close(hSocket); - //return -1; - exit(EXIT_FAILURE); + std::cerr << "LSCPServer: Could not bind server socket, retrying for " << ToString(LSCP_SERVER_BIND_TIMEOUT) << " seconds..."; + for (int trial = 0; true; trial++) { // retry for LSCP_SERVER_BIND_TIMEOUT seconds + if (bind(hSocket, (sockaddr*) &SocketAddress, sizeof(sockaddr_in)) < 0) { + if (trial > LSCP_SERVER_BIND_TIMEOUT) { + std::cerr << "gave up!" << std::endl; + close(hSocket); + //return -1; + exit(EXIT_FAILURE); + } + else sleep(1); // sleep 1s + } + else break; // success + } } listen(hSocket, 1); @@ -194,6 +210,18 @@ NotifyMutex.Unlock(); } +int LSCPServer::EventSubscribers( std::list events ) { + int subs = 0; + SubscriptionMutex.Lock(); + for( std::list::iterator iter = events.begin(); + iter != events.end(); iter++) + { + subs += eventSubscriptions.count(*iter); + } + SubscriptionMutex.Unlock(); + return subs; +} + void LSCPServer::SendLSCPNotify( LSCPEvent event ) { SubscriptionMutex.Lock(); if (eventSubscriptions.count(event.GetType()) == 0) { @@ -423,16 +451,20 @@ LSCPResultSet result; try { SamplerChannel* pSamplerChannel = pSampler->GetSamplerChannel(uiSamplerChannel); - if (!pSamplerChannel) throw LinuxSamplerException("Index out of bounds"); + if (!pSamplerChannel) throw LinuxSamplerException("Invalid sampler channel number " + ToString(uiSamplerChannel)); Engine* pEngine = pSamplerChannel->GetEngine(); - if (!pEngine) throw LinuxSamplerException("No engine loaded on channel"); + if (!pEngine) throw LinuxSamplerException("No engine loaded on sampler channel"); if (!pSamplerChannel->GetAudioOutputDevice()) - throw LinuxSamplerException("No audio output device on channel"); + throw LinuxSamplerException("No audio output device connected to sampler channel"); if (bBackground) { - LSCPLoadInstrument *pLoadInstrument = new LSCPLoadInstrument(pEngine, Filename.c_str(), uiInstrument); - pLoadInstrument->StartThread(); + InstrumentLoader.StartNewLoad(Filename, uiInstrument, pEngine); + } + else { + // tell the engine which instrument to load + pEngine->PrepareLoadInstrument(Filename.c_str(), uiInstrument); + // actually start to load the instrument (blocks until completed) + pEngine->LoadInstrument(); } - else pEngine->LoadInstrument(Filename.c_str(), uiInstrument); } catch (LinuxSamplerException e) { result.Error(e); @@ -451,8 +483,10 @@ if ((EngineName == "GigEngine") || (EngineName == "gig")) type = Engine::type_gig; else throw LinuxSamplerException("Unknown engine type"); SamplerChannel* pSamplerChannel = pSampler->GetSamplerChannel(uiSamplerChannel); - if (!pSamplerChannel) throw LinuxSamplerException("Index out of bounds"); + if (!pSamplerChannel) throw LinuxSamplerException("Invalid sampler channel number " + ToString(uiSamplerChannel)); + LockRTNotify(); pSamplerChannel->LoadEngine(type); + UnlockRTNotify(); } catch (LinuxSamplerException e) { result.Error(e); @@ -503,7 +537,9 @@ String LSCPServer::RemoveChannel(uint uiSamplerChannel) { dmsg(2,("LSCPServer: RemoveChannel(SamplerChannel=%d)\n", uiSamplerChannel)); LSCPResultSet result; + LockRTNotify(); pSampler->RemoveSamplerChannel(uiSamplerChannel); + UnlockRTNotify(); return result.Produce(); } @@ -546,13 +582,14 @@ LSCPResultSet result; try { SamplerChannel* pSamplerChannel = pSampler->GetSamplerChannel(uiSamplerChannel); - if (!pSamplerChannel) throw LinuxSamplerException("Index out of bounds"); + if (!pSamplerChannel) throw LinuxSamplerException("Invalid sampler channel number " + ToString(uiSamplerChannel)); Engine* pEngine = pSamplerChannel->GetEngine(); //Defaults values String EngineName = "NONE"; float Volume = 0.0f; String InstrumentFileName = "NONE"; + String InstrumentName = "NONE"; int InstrumentIndex = -1; int InstrumentStatus = -1; int AudioOutputChannels = 0; @@ -565,7 +602,10 @@ InstrumentStatus = pEngine->InstrumentStatus(); InstrumentIndex = pEngine->InstrumentIndex(); if (InstrumentIndex != -1) + { InstrumentFileName = pEngine->InstrumentFileName(); + InstrumentName = pEngine->InstrumentName(); + } for (int chan = 0; chan < pEngine->Channels(); chan++) { if (AudioRouting != "") AudioRouting += ","; AudioRouting += ToString(pEngine->OutputChannel(chan)); @@ -582,11 +622,12 @@ result.Add("MIDI_INPUT_DEVICE", GetMidiInputDeviceIndex(pSamplerChannel->GetMidiInputDevice())); result.Add("MIDI_INPUT_PORT", pSamplerChannel->GetMidiInputPort()); - if (pSamplerChannel->GetMidiInputChannel()) result.Add("MIDI_INPUT_CHANNEL", pSamplerChannel->GetMidiInputChannel()); - else result.Add("MIDI_INPUT_CHANNEL", "ALL"); + if (pSamplerChannel->GetMidiInputChannel() == MidiInputPort::midi_chan_all) result.Add("MIDI_INPUT_CHANNEL", "ALL"); + else result.Add("MIDI_INPUT_CHANNEL", pSamplerChannel->GetMidiInputChannel()); result.Add("INSTRUMENT_FILE", InstrumentFileName); result.Add("INSTRUMENT_NR", InstrumentIndex); + result.Add("INSTRUMENT_NAME", InstrumentName); result.Add("INSTRUMENT_STATUS", InstrumentStatus); } catch (LinuxSamplerException e) { @@ -604,9 +645,9 @@ LSCPResultSet result; try { SamplerChannel* pSamplerChannel = pSampler->GetSamplerChannel(uiSamplerChannel); - if (!pSamplerChannel) throw LinuxSamplerException("Index out of bounds"); + if (!pSamplerChannel) throw LinuxSamplerException("Invalid sampler channel number " + ToString(uiSamplerChannel)); Engine* pEngine = pSamplerChannel->GetEngine(); - if (!pEngine) throw LinuxSamplerException("No engine loaded on channel"); + if (!pEngine) throw LinuxSamplerException("No engine loaded on sampler channel"); result.Add(pEngine->VoiceCount()); } catch (LinuxSamplerException e) { @@ -624,9 +665,9 @@ LSCPResultSet result; try { SamplerChannel* pSamplerChannel = pSampler->GetSamplerChannel(uiSamplerChannel); - if (!pSamplerChannel) throw LinuxSamplerException("Index out of bounds"); + if (!pSamplerChannel) throw LinuxSamplerException("Invalid sampler channel number " + ToString(uiSamplerChannel)); Engine* pEngine = pSamplerChannel->GetEngine(); - if (!pEngine) throw LinuxSamplerException("No engine loaded on channel"); + if (!pEngine) throw LinuxSamplerException("No engine loaded on sampler channel"); result.Add(pEngine->DiskStreamCount()); } catch (LinuxSamplerException e) { @@ -644,9 +685,9 @@ LSCPResultSet result; try { SamplerChannel* pSamplerChannel = pSampler->GetSamplerChannel(uiSamplerChannel); - if (!pSamplerChannel) throw LinuxSamplerException("Index out of bounds"); + if (!pSamplerChannel) throw LinuxSamplerException("Invalid sampler channel number " + ToString(uiSamplerChannel)); Engine* pEngine = pSamplerChannel->GetEngine(); - if (!pEngine) throw LinuxSamplerException("No engine loaded on channel"); + if (!pEngine) throw LinuxSamplerException("No engine loaded on sampler channel"); if (!pEngine->DiskStreamSupported()) result.Add("NA"); else { @@ -743,7 +784,7 @@ } String LSCPServer::GetMidiInputDriverParameterInfo(String Driver, String Parameter, std::map DependencyList) { - dmsg(2,("LSCPServer: GetMidiInputDriverParameterInfo(Driver=%s,Parameter=%s)\n",Driver.c_str(),Parameter.c_str())); + dmsg(2,("LSCPServer: GetMidiInputDriverParameterInfo(Driver=%s,Parameter=%s,DependencyListSize=%d)\n",Driver.c_str(),Parameter.c_str(),DependencyList.size())); LSCPResultSet result; try { DeviceCreationParameter* pParameter = MidiInputDeviceFactory::GetDriverParameter(Driver, Parameter); @@ -752,11 +793,16 @@ result.Add("MANDATORY", pParameter->Mandatory()); result.Add("FIX", pParameter->Fix()); result.Add("MULTIPLICITY", pParameter->Multiplicity()); - if (pParameter->Depends()) result.Add("DEPENDS", *pParameter->Depends()); - if (pParameter->Default()) result.Add("DEFAULT", *pParameter->Default()); - if (pParameter->RangeMin()) result.Add("RANGE_MIN", *pParameter->RangeMin()); - if (pParameter->RangeMax()) result.Add("RANGE_MAX", *pParameter->RangeMax()); - if (pParameter->Possibilities()) result.Add("POSSIBILITIES", *pParameter->Possibilities()); + optional oDepends = pParameter->Depends(); + optional oDefault = pParameter->Default(DependencyList); + optional oRangeMin = pParameter->RangeMin(DependencyList); + optional oRangeMax = pParameter->RangeMax(DependencyList); + optional oPossibilities = pParameter->Possibilities(DependencyList); + if (oDepends) result.Add("DEPENDS", *oDepends); + if (oDefault) result.Add("DEFAULT", *oDefault); + if (oRangeMin) result.Add("RANGE_MIN", *oRangeMin); + if (oRangeMax) result.Add("RANGE_MAX", *oRangeMax); + if (oPossibilities) result.Add("POSSIBILITIES", *oPossibilities); } catch (LinuxSamplerException e) { result.Error(e); @@ -765,7 +811,7 @@ } String LSCPServer::GetAudioOutputDriverParameterInfo(String Driver, String Parameter, std::map DependencyList) { - dmsg(2,("LSCPServer: GetAudioOutputDriverParameterInfo(Driver=%s,Parameter=%s)\n",Driver.c_str(),Parameter.c_str())); + dmsg(2,("LSCPServer: GetAudioOutputDriverParameterInfo(Driver=%s,Parameter=%s,DependencyListSize=%d)\n",Driver.c_str(),Parameter.c_str(),DependencyList.size())); LSCPResultSet result; try { DeviceCreationParameter* pParameter = AudioOutputDeviceFactory::GetDriverParameter(Driver, Parameter); @@ -774,11 +820,16 @@ result.Add("MANDATORY", pParameter->Mandatory()); result.Add("FIX", pParameter->Fix()); result.Add("MULTIPLICITY", pParameter->Multiplicity()); - if (pParameter->Depends()) result.Add("DEPENDS", *pParameter->Depends()); - if (pParameter->Default()) result.Add("DEFAULT", *pParameter->Default()); - if (pParameter->RangeMin()) result.Add("RANGE_MIN", *pParameter->RangeMin()); - if (pParameter->RangeMax()) result.Add("RANGE_MAX", *pParameter->RangeMax()); - if (pParameter->Possibilities()) result.Add("POSSIBILITIES", *pParameter->Possibilities()); + optional oDepends = pParameter->Depends(); + optional oDefault = pParameter->Default(DependencyList); + optional oRangeMin = pParameter->RangeMin(DependencyList); + optional oRangeMax = pParameter->RangeMax(DependencyList); + optional oPossibilities = pParameter->Possibilities(DependencyList); + if (oDepends) result.Add("DEPENDS", *oDepends); + if (oDefault) result.Add("DEFAULT", *oDefault); + if (oRangeMin) result.Add("RANGE_MIN", *oRangeMin); + if (oRangeMax) result.Add("RANGE_MAX", *oRangeMax); + if (oPossibilities) result.Add("POSSIBILITIES", *oPossibilities); } catch (LinuxSamplerException e) { result.Error(e); @@ -926,7 +977,7 @@ // get audio channel AudioChannel* pChannel = pDevice->Channel(ChannelId); - if (!pChannel) throw LinuxSamplerException("Audio ouotput device does not have channel " + ToString(ChannelId) + "."); + if (!pChannel) throw LinuxSamplerException("Audio output device does not have audio channel " + ToString(ChannelId) + "."); // return the values of all audio channel parameters std::map parameters = pChannel->ChannelParameters(); @@ -985,7 +1036,7 @@ // get audio channel AudioChannel* pChannel = pDevice->Channel(ChannelId); - if (!pChannel) throw LinuxSamplerException("Audio output device does not have channel " + ToString(ChannelId) + "."); + if (!pChannel) throw LinuxSamplerException("Audio output device does not have audio channel " + ToString(ChannelId) + "."); // get desired audio channel parameter std::map parameters = pChannel->ChannelParameters(); @@ -1018,7 +1069,7 @@ // get audio channel AudioChannel* pChannel = pDevice->Channel(ChannelId); - if (!pChannel) throw LinuxSamplerException("Audio output device does not have channel " + ToString(ChannelId) + "."); + if (!pChannel) throw LinuxSamplerException("Audio output device does not have audio channel " + ToString(ChannelId) + "."); // get desired audio channel parameter std::map parameters = pChannel->ChannelParameters(); @@ -1101,9 +1152,10 @@ LSCPResultSet result; try { SamplerChannel* pSamplerChannel = pSampler->GetSamplerChannel(uiSamplerChannel); - if (!pSamplerChannel) throw LinuxSamplerException("Invalid channel number " + ToString(uiSamplerChannel)); + if (!pSamplerChannel) throw LinuxSamplerException("Invalid sampler channel number " + ToString(uiSamplerChannel)); Engine* pEngine = pSamplerChannel->GetEngine(); if (!pEngine) throw LinuxSamplerException("No engine deployed on sampler channel " + ToString(uiSamplerChannel)); + if (!pSamplerChannel->GetAudioOutputDevice()) throw LinuxSamplerException("No audio output device connected to sampler channel " + ToString(uiSamplerChannel)); pEngine->SetOutputChannel(ChannelAudioOutputChannel, AudioOutputDeviceInputChannel); } catch (LinuxSamplerException e) { @@ -1117,7 +1169,7 @@ LSCPResultSet result; try { SamplerChannel* pSamplerChannel = pSampler->GetSamplerChannel(uiSamplerChannel); - if (!pSamplerChannel) throw LinuxSamplerException("Invalid channel number " + ToString(uiSamplerChannel)); + if (!pSamplerChannel) throw LinuxSamplerException("Invalid sampler channel number " + ToString(uiSamplerChannel)); std::map devices = pSampler->GetAudioOutputDevices(); if (!devices.count(AudioDeviceId)) throw LinuxSamplerException("There is no audio output device with index " + ToString(AudioDeviceId)); AudioOutputDevice* pDevice = devices[AudioDeviceId]; @@ -1134,10 +1186,10 @@ LSCPResultSet result; try { SamplerChannel* pSamplerChannel = pSampler->GetSamplerChannel(uiSamplerChannel); - if (!pSamplerChannel) throw LinuxSamplerException("Invalid channel number " + ToString(uiSamplerChannel)); + if (!pSamplerChannel) throw LinuxSamplerException("Invalid sampler channel number " + ToString(uiSamplerChannel)); // Driver type name aliasing... - if (AudioOutputDriver == "ALSA") AudioOutputDriver = "Alsa"; - if (AudioOutputDriver == "JACK") AudioOutputDriver = "Jack"; + if (AudioOutputDriver == "Alsa") AudioOutputDriver = "ALSA"; + if (AudioOutputDriver == "Jack") AudioOutputDriver = "JACK"; // Check if there's one audio output device already created // for the intended audio driver type (AudioOutputDriver)... AudioOutputDevice *pDevice = NULL; @@ -1171,7 +1223,7 @@ LSCPResultSet result; try { SamplerChannel* pSamplerChannel = pSampler->GetSamplerChannel(uiSamplerChannel); - if (!pSamplerChannel) throw LinuxSamplerException("Invalid channel number " + ToString(uiSamplerChannel)); + if (!pSamplerChannel) throw LinuxSamplerException("Invalid sampler channel number " + ToString(uiSamplerChannel)); pSamplerChannel->SetMidiInputPort(MIDIPort); } catch (LinuxSamplerException e) { @@ -1185,7 +1237,7 @@ LSCPResultSet result; try { SamplerChannel* pSamplerChannel = pSampler->GetSamplerChannel(uiSamplerChannel); - if (!pSamplerChannel) throw LinuxSamplerException("Invalid channel number " + ToString(uiSamplerChannel)); + if (!pSamplerChannel) throw LinuxSamplerException("Invalid sampler channel number " + ToString(uiSamplerChannel)); pSamplerChannel->SetMidiInputChannel((MidiInputPort::midi_chan_t) MIDIChannel); } catch (LinuxSamplerException e) { @@ -1199,7 +1251,7 @@ LSCPResultSet result; try { SamplerChannel* pSamplerChannel = pSampler->GetSamplerChannel(uiSamplerChannel); - if (!pSamplerChannel) throw LinuxSamplerException("Invalid channel number " + ToString(uiSamplerChannel)); + if (!pSamplerChannel) throw LinuxSamplerException("Invalid sampler channel number " + ToString(uiSamplerChannel)); std::map devices = pSampler->GetMidiInputDevices(); if (!devices.count(MIDIDeviceId)) throw LinuxSamplerException("There is no MIDI input device with index " + ToString(MIDIDeviceId)); MidiInputDevice* pDevice = devices[MIDIDeviceId]; @@ -1216,9 +1268,9 @@ LSCPResultSet result; try { SamplerChannel* pSamplerChannel = pSampler->GetSamplerChannel(uiSamplerChannel); - if (!pSamplerChannel) throw LinuxSamplerException("Invalid channel number " + ToString(uiSamplerChannel)); + if (!pSamplerChannel) throw LinuxSamplerException("Invalid sampler channel number " + ToString(uiSamplerChannel)); // Driver type name aliasing... - if (MidiInputDriver == "ALSA") MidiInputDriver = "Alsa"; + if (MidiInputDriver == "Alsa") MidiInputDriver = "ALSA"; // Check if there's one MIDI input device already created // for the intended MIDI driver type (MidiInputDriver)... MidiInputDevice *pDevice = NULL; @@ -1259,7 +1311,7 @@ LSCPResultSet result; try { SamplerChannel* pSamplerChannel = pSampler->GetSamplerChannel(uiSamplerChannel); - if (!pSamplerChannel) throw LinuxSamplerException("Invalid channel number " + ToString(uiSamplerChannel)); + if (!pSamplerChannel) throw LinuxSamplerException("Invalid sampler channel number " + ToString(uiSamplerChannel)); std::map devices = pSampler->GetMidiInputDevices(); if (!devices.count(MIDIDeviceId)) throw LinuxSamplerException("There is no MIDI input device with index " + ToString(MIDIDeviceId)); MidiInputDevice* pDevice = devices[MIDIDeviceId]; @@ -1280,9 +1332,9 @@ LSCPResultSet result; try { SamplerChannel* pSamplerChannel = pSampler->GetSamplerChannel(uiSamplerChannel); - if (!pSamplerChannel) throw LinuxSamplerException("Index out of bounds"); + if (!pSamplerChannel) throw LinuxSamplerException("Invalid sampler channel number " + ToString(uiSamplerChannel)); Engine* pEngine = pSamplerChannel->GetEngine(); - if (!pEngine) throw LinuxSamplerException("No engine loaded on channel"); + if (!pEngine) throw LinuxSamplerException("No engine loaded on sampler channel"); pEngine->Volume(dVolume); } catch (LinuxSamplerException e) { @@ -1299,9 +1351,9 @@ LSCPResultSet result; try { SamplerChannel* pSamplerChannel = pSampler->GetSamplerChannel(uiSamplerChannel); - if (!pSamplerChannel) throw LinuxSamplerException("Index out of bounds"); + if (!pSamplerChannel) throw LinuxSamplerException("Invalid sampler channel number " + ToString(uiSamplerChannel)); Engine* pEngine = pSamplerChannel->GetEngine(); - if (!pEngine) throw LinuxSamplerException("No engine loaded on channel"); + if (!pEngine) throw LinuxSamplerException("No engine loaded on sampler channel"); pEngine->Reset(); } catch (LinuxSamplerException e) { @@ -1346,6 +1398,37 @@ return result.Produce(); } +static int select_callback(void * lscpResultSet, int argc, + char **argv, char **azColName) +{ + LSCPResultSet* resultSet = (LSCPResultSet*) lscpResultSet; + resultSet->Add(argc, argv); + return 0; +} + +String LSCPServer::QueryDatabase(String query) { + LSCPResultSet result; +#ifdef HAVE_SQLITE3 + char* zErrMsg = NULL; + sqlite3 *db; + String selectStr = "SELECT " + query; + + int rc = sqlite3_open("linuxsampler.db", &db); + if (rc == SQLITE_OK) + { + rc = sqlite3_exec(db, selectStr.c_str(), select_callback, &result, &zErrMsg); + } + if ( rc != SQLITE_OK ) + { + result.Error(String(zErrMsg), rc); + } + sqlite3_close(db); +#else + result.Error(String("SQLITE3 was not installed when linuxsampler was built. SELECT statement is not available."), 0); +#endif + return result.Produce(); +} + /** * Will be called by the parser to enable or disable echo mode; if echo * mode is enabled, all commands from the client will (immediately) be @@ -1364,30 +1447,3 @@ } return result.Produce(); } - -// Instrument loader constructor. -LSCPLoadInstrument::LSCPLoadInstrument(Engine* pEngine, String Filename, uint uiInstrument) - : Thread(false, 0, -4) -{ - this->pEngine = pEngine; - this->Filename = Filename; - this->uiInstrument = uiInstrument; -} - -// Instrument loader process. -int LSCPLoadInstrument::Main() -{ - try { - pEngine->LoadInstrument(Filename.c_str(), uiInstrument); - } - - catch (LinuxSamplerException e) { - e.PrintMessage(); - } - - // Always re-enable the engine. - pEngine->Enable(); - - // FIXME: Shoot ourselves on the foot? - delete this; -}