/********************************************************************** Audacity: A Digital Audio Editor DeviceChange.cpp Leland Lucius *******************************************************************//*! \file DeviceChange.cpp \brief *//*******************************************************************/ #include "DeviceChange.h" #include "Experimental.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 final : 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 final : 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 final : 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