diff --git a/src/DeviceChange.cpp b/src/DeviceChange.cpp new file mode 100644 index 000000000..07cd0b3a1 --- /dev/null +++ b/src/DeviceChange.cpp @@ -0,0 +1,406 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + DeviceChange.cpp + + Leland Lucius + +*******************************************************************//*! + +\file DeviceChange.cpp +\brief + +*//*******************************************************************/ + +#include "DeviceChange.h" + +#if defined(EXPERIMENTAL_DEVICE_CHANGE_HANDLER) + +#if defined(HAVE_DEVICE_CHANGE) + +#include +#include +#include + +DECLARE_LOCAL_EVENT_TYPE(EVT_DEVICE_CHANGE, -1); +DEFINE_EVENT_TYPE(EVT_DEVICE_CHANGE); + +#if defined(__WXMSW__) + +#include +#include +#include +#include + +class DeviceChangeListener : public IMMNotificationClient, + public DeviceChangeInterface +{ +public: + DeviceChangeListener() + { + mRefCnt = 1; + mEnumerator = NULL; + mEnabled = false; + mHandler = NULL; + } + + virtual ~DeviceChangeListener() + { + if (mEnumerator) + { + mEnumerator->UnregisterEndpointNotificationCallback(this); + mEnumerator = NULL; + } + + if (mHandler) + { + CoInitialize(NULL); + } + } + + // IUnknown implementation + + ULONG STDMETHODCALLTYPE AddRef() + { + return InterlockedIncrement(&mRefCnt); + } + + ULONG STDMETHODCALLTYPE Release() + { + ULONG cnt = InterlockedDecrement(&mRefCnt); + if (cnt == 0) + { + delete this; + } + return cnt; + } + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID **ppvInterface) + { + if (riid == IID_IUnknown) + { + AddRef(); + *ppvInterface = (IUnknown *) this; + } + else if (riid == __uuidof(IMMNotificationClient)) + { + AddRef(); + *ppvInterface = (IMMNotificationClient *) this; + } + else + { + *ppvInterface = NULL; + return E_NOINTERFACE; + } + + return S_OK; + } + + // IMMDeviceChangeListener implementation + + HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged( EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) + { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId) + { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId) + { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) + { + if (mEnabled) + { + mEnabled = false; + wxMutexGuiEnter(); + wxCommandEvent e(EVT_DEVICE_CHANGE); + mHandler->AddPendingEvent(e); + wxMutexGuiLeave(); + } + + return S_OK; + } + + HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) + { + return S_OK; + } + + bool SetHandler(wxEvtHandler *handler) + { + mHandler = handler; + + CoInitialize(NULL); + + HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), + NULL, + CLSCTX_INPROC_SERVER, + __uuidof(IMMDeviceEnumerator), + (void**)&mEnumerator); + if (hr == S_OK && mEnumerator) + { + mEnumerator->RegisterEndpointNotificationCallback(this); + } + + return hr == S_OK && mEnumerator; + } + + void Enable(bool enable) + { + mEnabled = enable; + } + +private: + wxEvtHandler *mHandler; + bool mEnabled; + ULONG mRefCnt; + IMMDeviceEnumerator *mEnumerator; +}; + +#elif defined(__WXGTK__) + +#include +#include +#include +#include +#include + +class DeviceChangeListener : public DeviceChangeInterface +{ +public: + DeviceChangeListener() + { + mEnabled = false; + mHandler = NULL; + mThread = 0; + } + + virtual ~DeviceChangeListener() + { + if (mThread) + { + pthread_cancel(mThread); + mThread = 0; + } + } + + // IUnknown implementation + bool SetHandler(wxEvtHandler *handler) + { + mHandler = handler; + + return pthread_create(&mThread, NULL, DeviceChangeListener::Listener, this) == 0; + } + + void Enable(bool enable) + { + mEnabled = enable; + } + + static void *Listener(void *parm) + { + DeviceChangeListener *This = (DeviceChangeListener *) parm; + + // Instantiate the udev object + struct udev *udev = udev_new(); + if (!udev) + { + pthread_exit(NULL); + } + + // Instantiate the monitor object + struct udev_monitor *mon = udev_monitor_new_from_netlink(udev, "udev"); + + // Start receiving notifications + udev_monitor_enable_receiving(mon); + + // Get the file descriptor we'll wait on + int fd = udev_monitor_get_fd(mon); + + while (true) + { + fd_set set; + + FD_ZERO(&set); + FD_SET(fd, &set); + + if (select(fd + 1, &set, NULL, NULL, NULL) < 0) + { + break; + } + + if (FD_ISSET(fd, &set)) + { + struct udev_device *dev = udev_monitor_receive_device(mon); + if (dev) + { +#if 0 + printf("Got Device\n"); + printf(" Node: %s\n", udev_device_get_devnode(dev)); + printf(" Subsystem: %s\n", udev_device_get_subsystem(dev)); + printf(" Devtype: %s\n", udev_device_get_devtype(dev)); + printf(" Action: %s\n", udev_device_get_action(dev)); +#endif + if (This->mEnabled) + { + This->mEnabled = false; + wxMutexGuiEnter(); + wxCommandEvent e(EVT_DEVICE_CHANGE); + This->mHandler->AddPendingEvent(e); + wxMutexGuiLeave(); + } + + udev_device_unref(dev); + } + } + } + + udev_unref(udev); + + pthread_exit(NULL); + } + +private: + wxEvtHandler *mHandler; + bool mEnabled; + pthread_t mThread; +}; + +#elif defined(__WXMAC__) + +#include + +class DeviceChangeListener : public DeviceChangeInterface +{ +public: + DeviceChangeListener() + { + mEnabled = false; + mHandler = NULL; + mListening = false; + } + + virtual ~DeviceChangeListener() + { + if (mListening) + { + AudioObjectPropertyAddress property_address; + + property_address.mSelector = kAudioHardwarePropertyDevices; + property_address.mScope = kAudioObjectPropertyScopeGlobal; + property_address.mElement = kAudioObjectPropertyElementMaster; + + AudioObjectRemovePropertyListener(kAudioObjectSystemObject, + &property_address, + DeviceChangeListener::Listener, + this); + mListening = false; + } + } + + // IUnknown implementation + bool SetHandler(wxEvtHandler *handler) + { + mHandler = handler; + + AudioObjectPropertyAddress property_address; + + property_address.mSelector = kAudioHardwarePropertyDevices; + property_address.mScope = kAudioObjectPropertyScopeGlobal; + property_address.mElement = kAudioObjectPropertyElementMaster; + + mListening = AudioObjectAddPropertyListener(kAudioObjectSystemObject, + &property_address, + DeviceChangeListener::Listener, + this) == 0; + } + + void Enable(bool enable) + { + mEnabled = enable; + } + + static OSStatus Listener(AudioObjectID objectID, + UInt32 numberAddresses, + const AudioObjectPropertyAddress inAddresses[], + void *clientData) + { + DeviceChangeListener *This = (DeviceChangeListener *) clientData; + + for (int i = 0; i < numberAddresses; i++) + { +#if 0 + printf("address %d\n", i); + printf("selector %08x\n", inAddresses[i].mSelector); + printf("scope %08x\n", inAddresses[i].mScope); + printf("element %08x\n", inAddresses[i].mElement); +#endif + if (This->mEnabled) + { + This->mEnabled = false; + wxMutexGuiEnter(); + wxCommandEvent e(EVT_DEVICE_CHANGE); + This->mHandler->AddPendingEvent(e); + wxMutexGuiLeave(); + } + } + + return 0; + } + +private: + wxEvtHandler *mHandler; + bool mEnabled; + bool mListening; +}; +#endif + +BEGIN_EVENT_TABLE(DeviceChangeHandler, wxEvtHandler) + EVT_COMMAND(wxID_ANY, EVT_DEVICE_CHANGE, DeviceChangeHandler::OnChange) + EVT_TIMER(wxID_ANY, DeviceChangeHandler::OnTimer) +END_EVENT_TABLE() + +DeviceChangeHandler::DeviceChangeHandler() +: wxEvtHandler() +{ + mTimer.SetOwner(this); + mListener = new DeviceChangeListener(); + mListener->SetHandler(this); + mListener->Enable(true); +} + +DeviceChangeHandler::~DeviceChangeHandler() +{ + if (mListener) + { + mListener->Enable(false); + delete mListener; + } +} + +void DeviceChangeHandler::Enable(bool enable) +{ + mListener->Enable(enable); +} + +void DeviceChangeHandler::OnChange(wxCommandEvent & evt) +{ + mTimer.Start(500, true); +} + +void DeviceChangeHandler::OnTimer(wxTimerEvent & evt) +{ + DeviceChangeNotification(); + mListener->Enable(true); +} + +#endif + +#endif diff --git a/src/DeviceChange.h b/src/DeviceChange.h new file mode 100644 index 000000000..09d21c272 --- /dev/null +++ b/src/DeviceChange.h @@ -0,0 +1,61 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + DeviceChange.h + + Leland Lucius + +**********************************************************************/ + +#ifndef __AUDACITY_DEVICECHANGE_H__ +#define __AUDACITY_DEVICECHANGE_H__ + +#include "Audacity.h" +#include "Experimental.h" + +#if defined(EXPERIMENTAL_DEVICE_CHANGE_HANDLER) + +#if defined(__WXMSW__) || defined(__WXMAC__) || defined(HAVE_LIBUDEV_H) +#define HAVE_DEVICE_CHANGE +#endif + +#if defined(HAVE_DEVICE_CHANGE) + +#include +#include + +class DeviceChangeInterface +{ +public: + virtual ~DeviceChangeInterface() {}; + + virtual bool SetHandler(wxEvtHandler *handler) = 0; + virtual void Enable(bool enable = true) = 0; +}; + +class DeviceChangeHandler : public wxEvtHandler +{ +public: + DeviceChangeHandler(); + virtual ~DeviceChangeHandler(); + + void Enable(bool enable = true); + + virtual void DeviceChangeNotification() = 0; + +private: + void OnChange(wxCommandEvent & evt); + void OnTimer(wxTimerEvent & evt); + + DeviceChangeInterface *mListener; + wxTimer mTimer; + + DECLARE_EVENT_TABLE() +}; + +#endif + +#endif + +#endif