diff --git a/assets.qrc b/assets.qrc
new file mode 100644
index 0000000..8b63932
--- /dev/null
+++ b/assets.qrc
@@ -0,0 +1,5 @@
+
+
+ assets/notificationAreaIcon.png
+
+
diff --git a/assets/SoundVolumeView.exe b/assets/SoundVolumeView.exe
new file mode 100644
index 0000000..4c7a7d2
Binary files /dev/null and b/assets/SoundVolumeView.exe differ
diff --git a/assets/notificationAreaIcon.png b/assets/notificationAreaIcon.png
new file mode 100644
index 0000000..0252332
Binary files /dev/null and b/assets/notificationAreaIcon.png differ
diff --git a/bueno.bat b/bueno.bat
index cd190f0..2c8ac1c 100644
--- a/bueno.bat
+++ b/bueno.bat
@@ -1,2 +1,4 @@
qmake -o build\Makefile .\qtest.pro
-mingw32-make.exe -C .\build -f Makefile.Release
+copy /Y /B .\assets\SoundVolumeView.exe .\build\debug
+copy /Y /B .\assets\SoundVolumeView.exe .\build\release
+mingw32-make.exe -C .\build -f Makefile
diff --git a/qtest.pro b/qtest.pro
index 7f7dbb8..046d8d8 100644
--- a/qtest.pro
+++ b/qtest.pro
@@ -1,8 +1,16 @@
-CONFIG += debug console
-QT += widgets
+QMAKE_CXXFLAGS += --target=x86_64-w64-mingw32 -g -gcodeview
+QMAKE_LFLAGS += --target=x86_64-w64-mingw32 -g -Wl,-pdb= -v
+LIBS += -LC:/capybara/libclang/x86_64-w64-mingw32/lib -lWinmm -lodbc32 -lodbccp32 -luuid -loleaut32 -lole32 -lshell32 -ladvapi32 -lcomdlg32 -lwinspool -lgdi32 -luser32 -lkernel32
+#"kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" -luuid -loleaut32 -lole32 -lshell32 -ladvapi32 -lcomdlg32 -lwinspool -lgdi32 -luser32 -lkernel32
+DEFINES += DEBUG QT_LOGGING_TO_CONSOLE=1 WIN32_LEAN_AND_MEAN
+CONFIG += debug
+
+QT += widgets network
INCLUDEPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\cont"
DESTPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\cont"
VPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\cont"
-SOURCES += qtestmain.cpp qtclasses.cpp backlasses.cpp contclasses.cpp
-HEADERS += qtclasses.h backlasses.h contclasses.h global.h
+SOURCES += qtestmain.cpp qtclasses.cpp backlasses.cpp backsessionclasses.cpp contclasses.cpp contsessionclasses.cpp
+HEADERS += qtclasses.h backlasses.h backsessionclasses.h contclasses.h contsessionclasses.h global.h debug.h backfuncs.h ipolicyconfig.h msinclude.h
+RESOURCES = assets.qrc
+
#DESTDIR += "build"
diff --git a/src/back/backfuncs.h b/src/back/backfuncs.h
new file mode 100644
index 0000000..b8e1c6c
--- /dev/null
+++ b/src/back/backfuncs.h
@@ -0,0 +1,32 @@
+GUID inline NGuidToGUID(NGuid guid) {
+ GUID msGuid = GUID();
+ msGuid.Data1 = guid.data1;
+ msGuid.Data2 = guid.data2;
+ msGuid.Data3 = guid.data3;
+ for (int i = 0; i < 8; i++){
+ msGuid.Data4[i] = guid.data4[i];
+ //log_debugcpp("MSGUID DATA4 BYTE " << i << ": ");
+ //log_debugcpp(print_as_binary(8, uint32_t, msGuid.Data4[i]));
+ }
+ //log_debugcpp("MSGUID DATA1: " << msGuid.Data1);
+ //log_debugcpp("MSGUID DATA2: " << msGuid.Data2);
+ //log_debugcpp("MSGUID DATA3: " << msGuid.Data3);
+
+ return msGuid;
+}
+
+NGuid inline GUIDToNGuid(LPGUID msGuid){
+ NGuid guid = NGuid();
+ guid.data1 = msGuid->Data1;
+ guid.data2 = msGuid->Data2;
+ guid.data3 = msGuid->Data3;
+ for (int i = 0; i < 8; i++){
+ guid.data4[i] = msGuid->Data4[i];
+ //log_debugcpp("GUID DATA4 BYTE " << i << ": ");
+ //log_debugcpp(print_as_binary(8, uint32_t, guid.data4[i]));
+ }
+ //log_debugcpp("GUID DATA1: " << guid.data1);
+ //log_debugcpp("GUID DATA2: " << guid.data2);
+ //log_debugcpp("GUID DATA3: " << guid.data3);
+ return guid;
+}
diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp
index 61e3562..8960c49 100644
--- a/src/back/backlasses.cpp
+++ b/src/back/backlasses.cpp
@@ -1,40 +1,563 @@
#include
+#include
-Endpoint::Endpoint(IMMDevice* ep){
+EndpointNewSessionCallback::EndpointNewSessionCallback(EndpointHandler* eph){
+ this->eph = eph;
+}
+
+ULONG EndpointNewSessionCallback::AddRef(){
+ return InterlockedIncrement(&ref);
+}
+
+ULONG EndpointNewSessionCallback::Release(){
+ ULONG tempRef = InterlockedDecrement(&ref);
+ if (tempRef == 0) {
+ delete this;
+ }
+ return tempRef;
+}
+
+HRESULT EndpointNewSessionCallback::QueryInterface(REFIID riid, VOID **ppvInterface) {
+ if (IID_IUnknown == riid)
+ {
+ AddRef();
+ *ppvInterface = (IUnknown*)this;
+ }
+ else if (__uuidof(IAudioSessionNotification) == riid)
+ {
+ AddRef();
+ *ppvInterface = (IMMNotificationClient*)this;
+ }
+ else
+ {
+ *ppvInterface = NULL;
+ return E_NOINTERFACE;
+ }
+ return S_OK;
+}
+
+HRESULT EndpointNewSessionCallback::OnSessionCreated(IAudioSessionControl *NewSession) {
+ if (eph->getFlow() == Flows::FLOW_CAPTURE) return S_OK;
+
+ HRESULT result = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
+ IAudioSessionControl2* sessionControl;
+ //ISimmpleAudioVolume* sessionVolume;
+ if (FAILED(NewSession->QueryInterface(__uuidof(IAudioSessionControl2), (void**)&sessionControl))) { log_wdebugcpp(L"no nueva sesion......"); };
+ if (sessionControl) {
+ sessionControl->AddRef();
+ //sessionControl->QueryInterface(__uuidof(ISimpleAudioVolume), (void**)&sessionVolume);
+ Session* newSession = new Session(this->eph->getEndpoint(), sessionControl);
+ eph->addSessionSendFront(newSession);
+ }
+
+ if (result == S_OK)
+ CoUninitialize();
+
+ return S_OK;
+}
+
+EndpointVolumeCallback::EndpointVolumeCallback(Endpoint* ep){
+ this->ep = ep;
+}
+
+ULONG EndpointVolumeCallback::AddRef(){
+ return InterlockedIncrement(&ref);
+}
+
+ULONG EndpointVolumeCallback::Release(){
+ ULONG tempRef = InterlockedDecrement(&ref);
+ if (tempRef == 0) {
+ delete this;
+ }
+ return tempRef;
+}
+
+HRESULT EndpointVolumeCallback::QueryInterface(REFIID riid, VOID **ppvInterface) {
+ if (IID_IUnknown == riid)
+ {
+ AddRef();
+ *ppvInterface = (IUnknown*)this;
+ }
+ else if (__uuidof(IAudioEndpointVolumeCallback) == riid)
+ {
+ AddRef();
+ *ppvInterface = (IAudioEndpointVolumeCallback*)this;
+ }
+ else
+ {
+ *ppvInterface = NULL;
+ return E_NOINTERFACE;
+ }
+ return S_OK;
+}
+
+HRESULT EndpointVolumeCallback::OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA pNotify) {
+ if (pNotify == NULL) return E_INVALIDARG;
+
+ //delete osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller;
+ //osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.freeData4();
+ //Could've made a function or = override to hide this within Nguid, but back in cont = bad.
+ osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.data1 \
+ = pNotify->guidEventContext.Data1;
+ osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.data2 \
+ = pNotify->guidEventContext.Data2;
+ osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.data3 \
+ = pNotify->guidEventContext.Data3;
+ for(int i = 0; i < 8 /* Data4 size */; i++){
+ osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.data4[i] = pNotify->guidEventContext.Data4[i];
+ }
+
+ //memcpy(&osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller, &pNotify->guidEventContext,sizeof(NGuid) );
+ Flows flow = this->ep->getFlow();
+ EndpointHandler* eph = nullptr;
+ if (flow & Flows::FLOW_PLAYBACK) {
+ eph = osh->getPlaybackEndpointHandlers().at(this->ep->getIndex());
+ } else {
+ eph = osh->getCaptureEndpointHandlers().at(this->ep->getIndex());
+ }
+
+ eph->getCallbackInfo()->muted = pNotify->bMuted;
+ eph->getCallbackInfo()->mainVolume = pNotify->fMasterVolume;
+ eph->getCallbackInfo()->channels = pNotify->nChannels;
+
+
+ UINT j = 0;
+ //todo: do while here caused stack corruption; sus
+ while(j < pNotify->nChannels) {
+ if (flow & Flows::FLOW_PLAYBACK)
+ eph->getCallbackInfo()->channelVolumes[j] = pNotify->afChannelVolumes[j];
+ else
+ eph->getCallbackInfo()->channelVolumes[j] = pNotify->afChannelVolumes[j];
+ j++;
+ }
+ return S_OK;
+}
+
+/*
+ * EndpointSituationCallback::EndpointSituationCallback(IMMDeviceEnumerator *deviceEnumerator, std::vector playbackDevices){
+ * this->deviceEnumerator = deviceEnumerator;
+ * this->playbackDevices = playbackDevices;
+ * }
+ *
+ */
+//todo: not on construct since it expects them to already exist; smells like refactor!
+void EndpointSituationCallback::fill(IMMDeviceEnumerator *deviceEnumerator, std::vector playbackDevices, std::vector captureDevices){
+ this->deviceEnumerator = deviceEnumerator;
+ this->playbackDevices = playbackDevices;
+ this->captureDevices = captureDevices;
+}
+
+ULONG EndpointSituationCallback::AddRef(){
+ return InterlockedIncrement(&ref);
+}
+
+ULONG EndpointSituationCallback::Release(){
+ ULONG tempRef = InterlockedDecrement(&ref);
+ if (tempRef == 0) {
+ delete this;
+ }
+ return tempRef;
+}
+
+HRESULT EndpointSituationCallback::QueryInterface(REFIID riid, VOID **ppvInterface) {
+ if (IID_IUnknown == riid)
+ {
+ AddRef();
+ *ppvInterface = (IUnknown*)this;
+ }
+ else if (__uuidof(IMMNotificationClient) == riid)
+ {
+ AddRef();
+ *ppvInterface = (IMMNotificationClient*)this;
+ }
+ else
+ {
+ *ppvInterface = NULL;
+ return E_NOINTERFACE;
+ }
+ return S_OK;
+}
+
+HRESULT EndpointSituationCallback::OnDefaultDeviceChanged(EDataFlow flow, ERole role,LPCWSTR pwstrDeviceId) {
+ if (flow == EDataFlow::eCapture) return E_INVALIDARG;
+
+ Roles nRole;
+ switch (role) {
+ case ERole::eConsole:
+ nRole = Roles::ROLE_CONSOLE;
+ break;
+ case ERole::eMultimedia:
+ nRole = Roles::ROLE_MULTIMEDIA;
+ break;
+ case ERole::eCommunications:
+ nRole = Roles::ROLE_COMMUNICATIONS;
+ break;
+ }
+ std::wstring wstringEndpointId = pwstrDeviceId;
+ log_wdebugcpp(L"we got za defol 4 " + wstringEndpointId);
+ osh->changeFrontDefaultsCallback(nRole, wstringEndpointId);
+
+ return S_OK;
+}
+
+HRESULT EndpointSituationCallback::OnDeviceAdded(LPCWSTR pwstrDeviceId) {
+ log_wdebugcpp(L"ayo we eventing za adin " + std::wstring(pwstrDeviceId));
+ return S_OK;
+};
+
+HRESULT EndpointSituationCallback::OnDeviceRemoved(LPCWSTR pwstrDeviceId) {
+ log_wdebugcpp(L"ayo we eventing za rmovin " + std::wstring(pwstrDeviceId));
+ return S_OK;
+}
+
+HRESULT EndpointSituationCallback::OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) {
+ std::wstring endpointId = std::wstring(pwstrDeviceId);
+ switch (dwNewState){
+ case DEVICE_STATE_ACTIVE:
+ osh->reviseEndpointShowing(endpointId, EndpointState::ENDPOINT_ACTIVE);
+ break;
+ case DEVICE_STATE_DISABLED:
+ osh->reviseEndpointShowing(endpointId, EndpointState::ENDPOINT_DISABLED);
+ break;
+ case DEVICE_STATE_NOTPRESENT:
+ osh->reviseEndpointShowing(endpointId, EndpointState::ENDPOINT_NOTPRESENT);
+ break;
+ case DEVICE_STATE_UNPLUGGED:
+ osh->reviseEndpointShowing(endpointId, EndpointState::ENDPOINT_UNPLUGGED);
+ break;
+ }
+
+ return S_OK;
+}
+
+HRESULT EndpointSituationCallback::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) {
+
+ /*
+ * log_debugcpp(" -->Changed device property " +
+ * key.fmtid.Data1 + key.fmtid.Data2 + key.fmtid.Data3 + "\n" +
+ * key.fmtid.Data4[0]+ key.fmtid.Data4[1]+ "\n"+
+ * key.fmtid.Data4[2]+ key.fmtid.Data4[3] + "\n"+
+ * key.fmtid.Data4[4]+ key.fmtid.Data4[5] + "\n"+
+ * key.fmtid.Data4[6]+ key.fmtid.Data4[7]+ "\n"+
+ * " pid " + key.pid);
+ */
+
+ return S_OK;
+}
+
+Endpoint::Endpoint(IMMDevice* ep, uint64_t idx){
this->endpoint = ep;
- if(FAILED(endpoint->Activate(IID_IAudioEndpointVolume, CLSCTX_ALL, NULL, (void**)&endpointVolume))) { log_debugcpp("si"); };
- //Obtaining friendly name: IPropertyStore creates PROPVARIANT per field
- // hr = endpointPtr->GetId(&endpointID);
+ this->idx = idx;
+
+ /*
+ * It can't multiflag, it's that stupid. MS momento.
+ * Only shows most relevant flag according to MS, i.e. 0110 sends 0010
+ */
+ //todo: preguntitas owindows dword no es uint32_t even tho mingw mingas
+ if(FAILED(endpoint->GetState(&this->endpointState))) {exit(-1);};
+
+ if(this->endpointState == EndpointState::ENDPOINT_ACTIVE) {
+ activateEndpointVolume();
+ }
+
+ reloadEndpointChannels();
+
+ /* todo: check header
+ * if(FAILED(endpoint->Activate(__uuidof(IAudioMeterInformation),
+ * CLSCTX_ALL, NULL, (void**)&endpointPeakMeter))) { log_debugcpp("peakbros..."); }
+ */
+
+ //todo:: atexit into exit Gather ID
+ LPWSTR tempString = nullptr;
+ if (FAILED(endpoint->GetId(&tempString))) {exit(-1);};
+ endpointId = std::wstring(tempString);
+ log_wdebugcpp(endpointId);
+ CoTaskMemFree(tempString);
+
endpoint->OpenPropertyStore(STGM_READ, &properties);
PROPVARIANT pv;
properties->GetValue(PKEY_Device_FriendlyName , &pv);
- friendlyName = pv.pwszVal;
+ if (pv.pwszVal == nullptr)
+ friendlyName = L"Unnamed Not Present Endpoint";
+ else
+ friendlyName = std::wstring(pv.pwszVal);
+
+ this->setFlow();
+ if (this->flow == Flows::FLOW_PLAYBACK) {
+ activateEndpointSessions();
+ }
}
-LPWSTR Endpoint::getName(){
+/*
+ * Endpoint::Endpoint(IMMDevice* endpoint) : Endpoint(endpoint, 0) {};
+ */
+
+void Endpoint::activateEndpointSessions() {
+ //sessionManager;
+ if (FAILED(endpoint->Activate(__uuidof(IAudioSessionManager2), CLSCTX_ALL, NULL, (void**) &sessionManager))) { log_wdebugcpp(L"sesionbros..."); return; }
+
+ IAudioSessionEnumerator* sessionEnumerator = nullptr;
+ if (FAILED(sessionManager->GetSessionEnumerator(&sessionEnumerator))) { log_wdebugcpp(L"sesEnumeratorBros..."); return; }
+
+ int sessionCount;
+ sessionEnumerator->GetCount(&sessionCount);
+ for (int i = 0; i < sessionCount; i++) {
+ IAudioSessionControl* sessionControlTmp;
+ sessionEnumerator->GetSession(i, (IAudioSessionControl**)&sessionControlTmp);
+ //todo:: asegurar lo del dynamic_cast
+ IAudioSessionControl2* sessionControl;
+ sessionControlTmp->QueryInterface(__uuidof(IAudioSessionControl2), (void**)&sessionControl);
+ sessionControl->AddRef();
+ sessionControlTmp->Release();
+ Session* session = new Session(this, sessionControl, (size_t)i);
+ endpointSessions.push_back(session);
+ }
+ sessionEnumerator->Release();
+}
+
+void Endpoint::addSession(Session* session) {
+ session->setIndex(this->getSessionCount());
+ endpointSessions.push_back(session);
+}
+
+void Endpoint::activateEndpointVolume() {
+ //bool extraThread = false;
+ /*
+ * Forgive me, for MS has sinned, and now I must too.
+ */
+ if (this->endpointVolume == nullptr){
+ HRESULT result = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
+ endpoint->Activate(IID_IAudioEndpointVolume, CLSCTX_ALL, NULL, (void**)&this->endpointVolume);
+ //if (endpointVolume == nullptr) { //why they returning 0 after dealing with the error jfc CO_E_NOTINITIALIZED) {
+ //CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
+ //extraThread = true;
+ //goto initialized;
+ //}
+ //log_debugcpp(std::string("no endpointVolume (IAudioEndpointVolume)"));
+ if (result == S_OK)
+ CoUninitialize();
+ }
+}
+
+void Endpoint::reloadEndpointChannels() {
+ if (this->endpointState == DEVICE_STATE_ACTIVE) {
+ if (FAILED(endpointVolume->GetChannelCount(&channelCount))) {log_debugcpp("get channel count fail");};/* */
+ }
+}
+
+void Endpoint::setIndex(uint64_t idx){
+ this->idx = idx;
+}
+
+uint64_t Endpoint::getIndex(){
+ return idx;
+}
+
+std::wstring Endpoint::getName(){
return friendlyName;
}
-float Endpoint::getVolume(){
+std::wstring Endpoint::getId(){
+ return endpointId;
+}
+
+float Endpoint::getVolume(int channel){
float volume;
- if(FAILED(endpointVolume->GetMasterVolumeLevelScalar(&volume))) { log_debugcpp("si");}
+ if (channel == AudioChannel::CHANNEL_MAIN) {
+ if(FAILED(endpointVolume->GetMasterVolumeLevelScalar(&volume))) { /* log_debugcpp("si") */;}
+ } else {
+ if(FAILED(endpointVolume->GetChannelVolumeLevelScalar(channel, &volume))) { /* log_debugcpp("si"); */}
+ }
return volume;
}
-void Endpoint::setVolume(float volume) {
- if(FAILED(endpointVolume->SetMasterVolumeLevelScalar(volume, NULL))) { log_debugcpp("si"); };
+uint32_t Endpoint::getChannelCount(){
+ return (uint32_t)channelCount;
}
-//Endpoint::~Endpoint(){
-// free(friendlyName);
-// properties->Release();
-// endpointVolume->Release();
-// endpoint->Release();
-//}
+bool Endpoint::getMute(){
+ BOOL mut;
+ if(FAILED(endpointVolume->GetMute(&mut))) { /* TIP: Below */ }
+ bool mute = (bool)mut;
+ return mute;
+}
-void Overseer::initCOMLibrary(){
- if(FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED))) { log_debugcpp("si"); };
+void Endpoint::setState(uint8_t state){
+ this->endpointState = state;
+ if(state == EndpointState::ENDPOINT_ACTIVE) {
+ this->activateEndpointVolume();
+ this->reloadEndpointChannels();
+ }
+}
+
+size_t Endpoint::getState(){
+ return this->endpointState;
+}
+
+void Endpoint::setVolume(NGuid guid, int channel, float volume) {
+ //TIP: There used to be log messages here. Now, it's a ghost town.
+ GUID tempMsGuid = NGuidToGUID(guid);
+ if (channel == AudioChannel::CHANNEL_MAIN) {
+ if(FAILED(endpointVolume->SetMasterVolumeLevelScalar(volume, &tempMsGuid))) {};
+ } else {
+ if(FAILED(endpointVolume->SetChannelVolumeLevelScalar(channel, volume, &tempMsGuid))) {};
+ }
+}
+
+void Endpoint::setMute(NGuid guid, bool muted) {
+ GUID tempMsGuid = NGuidToGUID(guid);
+ if(FAILED(endpointVolume->SetMute(muted, &tempMsGuid))) { log_wdebugcpp(std::wstring(L"EndpointVolume null?")); };
+}
+
+void Endpoint::setVolumeCallback(EndpointVolumeCallback *epc){
+ if(endpointVolume == nullptr) {
+ this->activateEndpointVolume();
+ }
+ endpointVolume->RegisterControlChangeNotify((IAudioEndpointVolumeCallback*)epc);
+}
+
+void Endpoint::removeVolumeCallback(EndpointVolumeCallback *epc){
+ endpointVolume->UnregisterControlChangeNotify((IAudioEndpointVolumeCallback*)epc);
+}
+
+Roles Endpoint::getRoles(){
+ return this->endpointRoles;
+}
+
+void Endpoint::setRoles(Roles role){
+ //otro exe momento
+ STARTUPINFOEXW startupConfig;
+ PROCESS_INFORMATION processInfo;
+ SecureZeroMemory(&startupConfig, sizeof(STARTUPINFOEXW));
+ SecureZeroMemory(&startupConfig.StartupInfo, sizeof(STARTUPINFOW));
+ startupConfig.StartupInfo.cb = sizeof(STARTUPINFOEXW);
+ SecureZeroMemory(&processInfo, sizeof(PROCESS_INFORMATION));
+
+ std::wstring command = L"SoundVolumeView.exe /SetDefault " + endpointId + L" ";
+ std::wstring troublePair = L"0";
+ switch (role) {
+ case Roles::ROLE_ALL:
+ /*
+ * console or multimedia, one sends both, at least for now;
+ * either cos of ms or dis guy;
+ * no choice but to treat them as one for now.
+ * command += L"all"; and nothing else would've been nice...
+ */
+ troublePair = command + troublePair;
+ if(CreateProcessW(
+ NULL,
+ (wchar_t*)troublePair.c_str(),
+ NULL,
+ NULL,
+ false,
+ CREATE_UNICODE_ENVIRONMENT,
+ NULL,
+ NULL,
+ (LPSTARTUPINFOW)&startupConfig,
+ &processInfo
+ ) == true) {
+ WaitForSingleObject(processInfo.hProcess, INFINITE );
+ CloseHandle(processInfo.hProcess);
+ CloseHandle(processInfo.hThread);
+ }
+ command += L"2";
+ break;
+ case Roles::ROLE_CONSOLE:
+ command += std::to_wstring(0);
+ break;
+ case Roles::ROLE_MULTIMEDIA:
+ command += std::to_wstring(1);
+ break;
+ case Roles::ROLE_COMMUNICATIONS:
+ command += std::to_wstring(2);
+ break;
+ }
+
+ if(CreateProcessW(
+ NULL,
+ (wchar_t*)command.c_str(),
+ NULL,
+ NULL,
+ false,
+ CREATE_UNICODE_ENVIRONMENT,
+ NULL,
+ NULL,
+ (LPSTARTUPINFOW)&startupConfig,
+ &processInfo
+ ) == true) {
+ WaitForSingleObject(processInfo.hProcess, INFINITE );
+ CloseHandle(processInfo.hProcess);
+ CloseHandle(processInfo.hThread);
+ }
+}
+
+void Endpoint::assignRoles(Roles role){
+ Roles roles = (Roles)(endpointRoles | role);
+ this->endpointRoles = roles;
+}
+
+void Endpoint::removeRoles(Roles role){
+ Roles roles = (Roles)(endpointRoles ^ role);
+ this->endpointRoles = roles;
+}
+
+void Endpoint::setFlow() {
+ IMMEndpoint* flowGetter;
+ //this should be as simple as writing IID_IMMEndpoint, but it just won't find the macro, so I copied it. Sad.
+ GUID manual;
+ manual.Data1 = 0x1be09788;
+ manual.Data2 = 0x6894;
+ manual.Data3 = 0x4089;
+ manual.Data4[0] = 0x85;
+ manual.Data4[1] = 0x86;
+ manual.Data4[2] = 0x9a;
+ manual.Data4[3] = 0x2a;
+ manual.Data4[4] = 0x6c;
+ manual.Data4[5] = 0x26;
+ manual.Data4[6] = 0x5a;
+ manual.Data4[7] = 0xc5;
+ if(FAILED(this->endpoint->QueryInterface((const _GUID)manual, (void**)&flowGetter)))
+ { log_debugcpp("no flow..."); }
+ EDataFlow MSflow;
+ HRESULT vafllar = flowGetter->GetDataFlow(&MSflow);
+ this->flow = (MSflow == EDataFlow::eRender ? Flows::FLOW_PLAYBACK : Flows::FLOW_CAPTURE);
+ flowGetter->Release();
+}
+
+Flows Endpoint::getFlow() {
+ return this->flow;
+}
+
+/* sessions */
+std::vector Endpoint::getSessions() {
+ return endpointSessions;
+}
+
+size_t Endpoint::getSessionCount() {
+ return endpointSessions.size();
+}
+
+void Endpoint::registerNewSessionNotification(EndpointNewSessionCallback* ensc){
+ sessionManager->RegisterSessionNotification(ensc);
+}
+
+void Endpoint::unregisterNewSessionNotification(EndpointNewSessionCallback* ensc){
+ sessionManager->UnregisterSessionNotification(ensc);
+}
+
+Endpoint::~Endpoint(){
+ log_wdebugcpp(L"murio endpoint-san uwu");
+ properties->Release();
+ endpointVolume->Release();
+ endpoint->Release();
+ sessionManager->Release();
+}
+
+void Overseer::initCOMLibrary() {
+ OutputDebugStringW(L"EPWidget creation\n");
+ if(FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE))) {
+ log_debugcpp("si"); };
//Retrieving endpoint enumerator
@@ -44,58 +567,148 @@ void Overseer::initCOMLibrary(){
(void**)&deviceEnumerator)) )
{ log_debugcpp("si"); };
+ GUID tempGuid;
+ if(FAILED(CoCreateGuid(&tempGuid))) { log_debugcpp("Failed to obtain GUID: " ); };
+ //todo: wtf? why is it working? floats are ptrs...
+ this->guid = GUIDToNGuid(&tempGuid);
+
+ //if(FAILED(CoCreateInstance(__uuidof(CPolicyConfigClient),
+ // NULL, CLSCTX_ALL, __uuidof(IPolicyConfig10), (LPVOID *)&policyConfig))) {exit(-1);}
+
+ //TODO: Release lpguid?
+ //TODO: Uninitialize COM
}
-void Overseer::reloadEndpoints() {
+void Overseer::reloadEndpoints(Flows flow) {
IMMDeviceCollection *deviceCollection;
+ unsigned int numEndpoints;
+ EDataFlow MSflow = (flow == Flows::FLOW_PLAYBACK ? EDataFlow::eRender : EDataFlow::eCapture);
// | DEVICE_STATE_DISABLED | DEVICE_STATE_NOTPRESENT | DEVICE_STATE_UNPLUGGED
- if(FAILED(deviceEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &deviceCollection) ))
+ // NOTPRESENT shows a lot of garbage, unnamed devices.
+ if(FAILED(deviceEnumerator->EnumAudioEndpoints(MSflow, DEVICE_STATE_ACTIVE | DEVICE_STATE_DISABLED | DEVICE_STATE_NOTPRESENT | DEVICE_STATE_UNPLUGGED, &deviceCollection) ))
{ log_debugcpp("si"); };
-
- //Counting them
- if(FAILED(deviceCollection->GetCount(&numPlaybackEndpoints))) { log_debugcpp("si");};
- if(numPlaybackEndpoints == 0) { log_debugcpp("si"); };
-
+ /*
+ * Counting them
+ */
+ if(FAILED(deviceCollection->GetCount(&numEndpoints))) { log_debugcpp("si");};
+ if(numEndpoints == 0) { log_debugcpp("si"); };
- //Retrieving actual endpoints and storing them on their own class
- for (unsigned int i = 0; i < numPlaybackEndpoints; i++){
- IMMDevice *temp;
+ /*
+ * Retrieving actual endpoints and storing them on their own class
+ */
+ IMMDevice *temp;
+ for (unsigned int i = 0; i < numEndpoints; i++){
if(deviceCollection->Item(i, &temp) != 0) { log_debugcpp("si"); };
- Endpoint *endpoint = new Endpoint(temp);
- this->playbackDevices.push_back(endpoint);
- //TODO: le porblemx std::cout << "ola" << std::endl;
+ Endpoint *endpoint = new Endpoint(temp, i);
+
+ if (flow == Flows::FLOW_PLAYBACK)
+ this->playbackDevices.push_back(endpoint);
+ else
+ this->captureDevices.push_back(endpoint);
+ //TODO: le porblemx std::cout + "ola" + std::endl;
}
deviceCollection->Release();
+
+ /*
+ * Discerning default endpoints per role
+ * order: console, multimedia, communications
+ */
+ for(int i = 0; i < ERole_enum_count; i++){
+ ERole val;
+ switch(i) {
+ case 0:
+ val = eConsole;
+ break;
+ case 1:
+ val = eMultimedia;
+ break;
+ case 2:
+ val = eCommunications;
+ break;
+ }
+ deviceEnumerator->GetDefaultAudioEndpoint(MSflow, val, &temp);
+ LPWSTR id = nullptr;
+
+ if (flow == Flows::FLOW_PLAYBACK) {
+ for (unsigned int j = 0; j < numEndpoints; j++) {
+ std::wstring eptId = playbackDevices.at(j)->getId();
+ temp->GetId(&id);
+ int comparison = CompareStringEx(LOCALE_NAME_USER_DEFAULT, 0, eptId.c_str(), -987, id, -987, NULL, NULL, 0);
+ if (comparison - 2 == 0) {
+ log_wdebugcpp(L"ola defaul playback de "
+ + std::to_wstring(i) + L" es " + id);
+ playbackDevices.at(j)->assignRoles((Roles)(1 << i));
+ }
+ }
+ } else {
+ for (unsigned int j = 0; j < numEndpoints; j++){
+ std::wstring eptId = captureDevices.at(j)->getId();
+ temp->GetId(&id);
+ int comparison = CompareStringEx(LOCALE_NAME_USER_DEFAULT, 0, eptId.c_str(), -987, id, -987, NULL, NULL, 0);
+ if (comparison - 2 == 0) {
+ log_wdebugcpp(L"ola defaul capture de "
+ + std::to_wstring(i) + L" es " + id);
+ captureDevices.at(j)->assignRoles((Roles)(1 << i));
+ }
+ }
+ }
+ }
}
-Overseer::Overseer(){
+Endpoint* Overseer::addEndpoint(std::wstring endpointId, /* out */Flows* flow = nullptr) {
+ IMMDevice* newep;
+ if(FAILED(deviceEnumerator->GetDevice((LPCWSTR)endpointId.c_str(), &newep)))
+ log_debugcpp("ay caramba con la hot metida.");
+
+ Endpoint *endpoint = new Endpoint(newep);
+
+ Flows getFlow = endpoint->getFlow();
+ if (getFlow == Flows::FLOW_PLAYBACK) {
+ endpoint->setIndex(osh->getPlaybackEndpointsCount());
+ this->playbackDevices.push_back(endpoint);
+ } else {
+ endpoint->setIndex(osh->getCaptureEndpointsCount());
+ this->captureDevices.push_back(endpoint);
+ }
+ if (flow != nullptr) *flow = getFlow;
+ return endpoint;
+}
+
+Overseer::Overseer() { //: epsc(deviceEnumerator, playbackDevices){
//Initializing COM library
+ log_debugcpp("Initializing Overseer");
initCOMLibrary();
//Obtaining playback endpoint collection on this point in time
- reloadEndpoints();
+ reloadEndpoints(Flows::FLOW_PLAYBACK);
+ //reloadEndpoints(Flows::FLOW_CAPTURE);
+
+ //Registering for endpoint information callback
+ this->epsc.fill(deviceEnumerator, playbackDevices, captureDevices);
+ if(FAILED(deviceEnumerator->RegisterEndpointNotificationCallback(((IMMNotificationClient*)&epsc)))) { log_debugcpp("when no enchufas......"); }
}
-//Overseer::int getDefaultPlaybackEndpoint(Endpoint** defaultEndpoint){
-//if (FAILED(deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &endpointPtr)))
-// return 1;
-//return 0;
-//}
-
-//int Overseer::getDefaultCaptureEndpoint(Endpoint** defaultEndpoint);
-
+NGuid Overseer::getGuid() {
+ return guid;
+}
+
std::vector Overseer::getPlaybackEndpoints() {
return playbackDevices;
}
-//Overseer::~Overseer(){
- // deviceEnumerator->Release();
- // for(unsigned long long i = 0; i < playbackDevices.size(); i++){
- //delete(playbackDevices.at(i));
- //}
- //}
+std::vector Overseer::getCaptureEndpoints() {
+ return captureDevices;
+}
+
+Overseer::~Overseer(){
+ log_debugcpp("cum");
+ deviceEnumerator->Release();
+ for(unsigned long long i = 0; i < playbackDevices.size(); i++){
+ delete(playbackDevices.at(i));
+ }
+}
//int Overseer::getCaptureEndpoints(std::vector *captureEndpoints);
diff --git a/src/back/backlasses.h b/src/back/backlasses.h
index 90bc88b..f70e6d1 100644
--- a/src/back/backlasses.h
+++ b/src/back/backlasses.h
@@ -1,38 +1,105 @@
#pragma once
-#define WIN32_LEAN_AND_MEAN
+#include "msinclude.h"
+#include "backsessionclasses.h"
#include "global.h"
-#include
-#include
+#include "contclasses.h"
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-//#include
-//#include
-#include
+class EndpointVolumeCallback;
+class Session;
class Endpoint {
public:
- Endpoint(IMMDevice* endpoint);
- void setVolume(float volume);
- float getVolume();
- LPWSTR getName();
- //~Endpoint();
+ Endpoint(IMMDevice* endpoint, uint64_t idx);
+ //todo: how to forward declare delegate constructors?
+ Endpoint(IMMDevice* endpoint) : Endpoint(endpoint, 0) {};
+ void reloadEndpointChannels();
+ uint64_t getIndex();
+ void setIndex(uint64_t idx);
+ void setVolume(NGuid guid, int channel, float volume);
+ uint32_t getChannelCount();
+ float getVolume(int channel);
+ void setMute(NGuid guid, bool muted);
+ bool getMute();
+ void setState(uint8_t state);
+ size_t getState();
+ Roles getRoles();
+ void setRoles(Roles role);
+ void assignRoles(Roles role);
+ void removeRoles(Roles role);
+ void setFlow();
+ Flows getFlow();
+ std::wstring getId();
+ std::wstring getName();
+
+ void setVolumeCallback(EndpointVolumeCallback *epc);
+ void removeVolumeCallback(EndpointVolumeCallback *epc);
+
+ /* sessions */
+ std::vector getSessions();
+ size_t getSessionCount();
+ void addSession(Session* session);
+ void registerNewSessionNotification(EndpointNewSessionCallback* ensc);
+ void unregisterNewSessionNotification(EndpointNewSessionCallback* ensc);
+
+ ~Endpoint();
private:
+ void inline activateEndpointVolume();
+ void inline activateEndpointSessions();
+
+ std::vector endpointSessions;
+ uint32_t channelCount = 0;
IMMDevice* endpoint;
- IAudioEndpointVolume *endpointVolume ;
+ IAudioSessionManager2 *sessionManager;
+ Flows flow;
+ IAudioEndpointVolume *endpointVolume = nullptr;
IPropertyStore *properties;
- LPWSTR friendlyName;
- // LPWSTR endpointID = NULL;
+ std::wstring friendlyName;
+ std::wstring endpointId;
+ unsigned long endpointState;
+ Roles endpointRoles = (Roles)0;
+ uint64_t idx;
+ /* Not implemented in llvm-mingw. Sad!
+ * IAudioMeterInformation *endpointPeakMeter = nullptr;
+ */
+};
+
+class EndpointVolumeCallback : public IAudioEndpointVolumeCallback {
+
+ public:
+ EndpointVolumeCallback(Endpoint* ep);
+
+ ULONG AddRef();
+ ULONG Release();
+ HRESULT QueryInterface(REFIID riid, VOID **ppvInterface);
+ HRESULT OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA update);
+ //~EndpointVolumeCallback();
+
+ private:
+ ULONG ref = 1;
+ Endpoint* ep;
+};
+
+class EndpointSituationCallback : public IMMNotificationClient {
+ public:
+ //EndpointSituationCallback(IMMDeviceEnumerator *deviceEnumerator, std::vector playbackDevices);
+ ULONG AddRef();
+ ULONG Release();
+ HRESULT QueryInterface(REFIID riid, VOID **ppvInterface);
+ HRESULT OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId);
+ HRESULT OnDeviceAdded(LPCWSTR pwstrDeviceId);
+ HRESULT OnDeviceRemoved(LPCWSTR pwstrDeviceId);
+ HRESULT OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState);
+ HRESULT OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key);
+
+ void fill(IMMDeviceEnumerator *deviceEnumerator, std::vector playbackDevices, std::vector captureDevices);
+ private:
+ ULONG ref = 1;
+ IMMDeviceEnumerator *deviceEnumerator;
+ std::vector playbackDevices;
+ std::vector captureDevices;
};
class Overseer {
@@ -40,21 +107,44 @@ class Overseer {
public:
Overseer();
std::vector getPlaybackEndpoints();
- void reloadEndpoints();
+ std::vector getCaptureEndpoints();
+
+ void reloadEndpoints(Flows flow);
+ Endpoint* addEndpoint(std::wstring endpointId, /* out */ Flows* flow);
+ NGuid getGuid();
+ //void setEndpointStatusCallback();
+ //void setEndpointStatusCallback();
+
//~Overseer();
//int getDefaultPlaybackEndpoint(Endpoint** defaultEndpoint);
//int getDefaultCaptureEndpoint(Endpoint** defaultEndpoint);
//int getCaptureEndpoints(std::vector *captureEndpoints);
//IMMDeviceEnumerator** setOrigin();
- //~Overseer();
+ ~Overseer();
private:
- unsigned int numPlaybackEndpoints;
+ NGuid guid;
+
IMMDeviceEnumerator *deviceEnumerator;
+ EndpointSituationCallback epsc;
+ //IPolicyConfig *policyConfig;
std::vector playbackDevices;
+ std::vector captureDevices;
void initCOMLibrary();
//IMMDeviceCollection *deviceCollection;
//int numCaptureEndpoints;
//std::vector *captureDevices;
};
+class EndpointNewSessionCallback : public IAudioSessionNotification {
+ public:
+ EndpointNewSessionCallback(EndpointHandler *eph);
+ ULONG AddRef();
+ ULONG Release();
+ HRESULT QueryInterface(REFIID riid, VOID **ppvInterface);
+ HRESULT OnSessionCreated(IAudioSessionControl *NewSession);
+
+ private:
+ ULONG ref = 1;
+ EndpointHandler *eph;
+};
diff --git a/src/back/backsessionclasses.cpp b/src/back/backsessionclasses.cpp
new file mode 100644
index 0000000..ced7fb1
--- /dev/null
+++ b/src/back/backsessionclasses.cpp
@@ -0,0 +1,282 @@
+#include "backsessionclasses.h"
+#include "backfuncs.h"
+
+SessionStateCallback::SessionStateCallback(SessionHandler *sh) {
+ this->sh = sh;
+}
+
+ULONG SessionStateCallback::AddRef() {
+ return InterlockedIncrement(&ref);
+}
+
+ULONG SessionStateCallback::Release() {
+ ULONG tempRef = InterlockedDecrement(&ref);
+ if (tempRef == 0) {
+ delete this;
+ }
+ return tempRef;
+}
+
+HRESULT SessionStateCallback::QueryInterface(REFIID riid, VOID **ppvInterface) {
+ if (IID_IUnknown == riid)
+ {
+ AddRef();
+ *ppvInterface = (IUnknown*)this;
+ }
+ else if (__uuidof(IAudioSessionNotification) == riid)
+ {
+ AddRef();
+ *ppvInterface = (IMMNotificationClient*)this;
+ }
+ else
+ {
+ *ppvInterface = NULL;
+ return E_NOINTERFACE;
+ }
+ return S_OK;
+}
+
+HRESULT SessionStateCallback::OnDisplayNameChanged(LPCWSTR NewDisplayName, LPCGUID EventContext) {
+ return S_OK;
+}
+
+HRESULT SessionStateCallback::OnIconPathChanged(LPCWSTR NewIconPath, LPCGUID EventContex) {
+ return S_OK;
+}
+
+HRESULT SessionStateCallback::OnSimpleVolumeChanged(float NewVolume, BOOL NewMute, LPCGUID EventContext) {
+ sh->getVolumeInfo()->muted = NewMute;
+ sh->getVolumeInfo()->mainVolume = NewVolume;
+ sh->getVolumeInfo()->caller = GUIDToNGuid((LPGUID)EventContext);
+ /*
+ * if (NewMute)
+ * {
+ * printf("MUTE\n");
+ * }
+ * else
+ * {
+ * printf("Volume = %d percent\n",
+ * (UINT32)(100*NewVolume + 0.5));
+ * }
+ */
+ return S_OK;
+}
+
+HRESULT SessionStateCallback::OnChannelVolumeChanged(DWORD ChannelCount, float NewChannelVolumeArray[], DWORD ChangedChannel, LPCGUID EventContext) {
+ return S_OK;
+}
+
+HRESULT SessionStateCallback::OnGroupingParamChanged(LPCGUID NewGroupingParam, LPCGUID EventContext) {
+ return S_OK;
+}
+
+HRESULT SessionStateCallback::OnStateChanged(AudioSessionState NewState) {
+ SessionState newState;// = sh->getState();
+ switch (NewState) {
+ case AudioSessionStateActive:
+ newState = SessionState::ACTIVE;
+ break;
+ case AudioSessionStateInactive:
+ newState = SessionState::INACTIVE;
+ break;
+ case AudioSessionStateExpired:
+ newState = SessionState::EXPIRED;
+ break;
+ }
+
+ sh->reviseSessionShowing(newState);
+ return S_OK;
+}
+
+HRESULT SessionStateCallback::OnSessionDisconnected(AudioSessionDisconnectReason DisconnectReason) {
+ sh->setState(SessionState::DISCONNECTED);
+ sh->reviseSessionShowing(SessionState::DISCONNECTED);
+ return S_OK;
+}
+
+Session::Session(Endpoint* ep, IAudioSessionControl2* sessionControl, size_t idx) {
+ this->ep = ep;
+ this->sessionControl = sessionControl;
+ this->idx = idx;
+
+ AudioSessionState msState;
+ sessionControl->GetState(&msState);
+ switch (msState) {
+ case AudioSessionState::AudioSessionStateActive:
+ this->sessionState = SessionState::ACTIVE;
+ break;
+ case AudioSessionState::AudioSessionStateInactive:
+ this->sessionState = SessionState::INACTIVE;
+ break;
+ case AudioSessionState::AudioSessionStateExpired:
+ this->sessionState = SessionState::EXPIRED;
+ break;
+ }
+
+ sessionControl->QueryInterface(__uuidof(ISimpleAudioVolume), (void**)&sessionVolume);
+ DWORD pid;
+ sessionControl->GetProcessId(&pid);
+ if (sessionControl->IsSystemSoundsSession() == S_OK)
+ this->sessionName = std::wstring(LSTRING_SYSTEM_SOUNDS);
+ else {
+ LPWSTR sessionDisplayName;
+ this->sessionControl->GetDisplayName(&sessionDisplayName);
+ if (!wcscmp(sessionDisplayName, L""))
+ this->sessionName = this->fetchProcessName(pid);
+ else
+ this->sessionName = std::wstring(sessionDisplayName);
+ CoTaskMemFree(sessionDisplayName);
+ }
+}
+
+float Session::getVolume(int channel){
+ float volume;
+ if (channel == AudioChannel::CHANNEL_MAIN) {
+ if(FAILED(sessionVolume->GetMasterVolume(&volume))) { /* log_debugcpp("si") */;}
+ } else {
+ return 0.0;
+ //if(FAILED(endpointVolume->GetChannelVolumeLevelScalar(channel, &volume))) { /* log_debugcpp("si"); */}
+ }
+ return volume;
+}
+
+/*
+ * uint32_t Endpoint::getChannelCount(){
+ * return (uint32_t)channelCount;
+ * }
+ */
+
+std::wstring Session::getName() {
+ return sessionName;
+}
+
+bool Session::getMute() {
+ BOOL mut;
+ if(FAILED(sessionVolume->GetMute(&mut))) { /* TIP: Below */ }
+ bool mute = (bool)mut;
+ return mute;
+}
+
+void Session::setVolume(NGuid guid, int channel, float volume) {
+ //TIP: There used to be log messages here. Now, it's a ghost town.
+ GUID tempMsGuid = NGuidToGUID(guid);
+ if (channel == AudioChannel::CHANNEL_MAIN) {
+ if(FAILED(sessionVolume->SetMasterVolume(volume, &tempMsGuid))) {};
+ } else {
+ //if(FAILED(sessionVolume->SetChannelVolumeLevelScalar(channel, volume, &tempMsGuid))) {};
+ }
+}
+
+void Session::setIndex(size_t idx) {
+ this->idx = idx;
+}
+
+void Session::setMute(NGuid guid, bool muted) {
+ GUID tempMsGuid = NGuidToGUID(guid);
+ if(FAILED(sessionVolume->SetMute(muted, &tempMsGuid))) { log_wdebugcpp(std::wstring(L"SessionVolume null?")); };
+}
+
+std::wstring Session::fetchProcessName(DWORD pid) {
+ /*
+ * https://learn.microsoft.com/en-us/windows/win32/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot
+ * https://stackoverflow.com/questions/11843368/how-to-get-process-description
+ */
+
+ /* Executable path retrieval */
+ std::wstring exePath = L"";
+
+ HANDLE processList = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid);
+ if (processList == INVALID_HANDLE_VALUE) {
+ log_wdebugcpp(L"aye no procname.");
+ return exePath;
+ }
+
+ MODULEENTRY32W me32w;
+ me32w.dwSize = sizeof(MODULEENTRY32W);
+ if(Module32FirstW(processList, &me32w)) {
+ do {
+ if (me32w.th32ProcessID == pid) {
+ exePath = std::wstring(me32w.szExePath);
+ break;
+ /*
+ * However, if the calling process is a 32-bit process, you must call the
+ * QueryFullProcessImageName function to retrieve the full path of the
+ * executable file for a 64-bit process.
+ */
+ }
+ } while(Module32NextW(processList, &me32w));
+ }
+ CloseHandle(processList);
+
+ /* File description retrieval */
+ struct LANGANDCODEPAGE {
+ WORD wLanguage;
+ WORD wCodePage;
+ } *translationArray;
+
+ DWORD filler;
+ DWORD fileVersionInfoSize = GetFileVersionInfoSizeW(exePath.c_str(), &filler);
+ if (!fileVersionInfoSize) return exePath;
+
+ void* fileVersionInfo = malloc(fileVersionInfoSize);
+ if(!GetFileVersionInfoW(exePath.c_str(),0,fileVersionInfoSize, fileVersionInfo))
+ return exePath;
+
+ UINT translationArrayLen = 0;
+ if (!VerQueryValueW(fileVersionInfo, L"\\VarFileInfo\\Translation", (LPVOID*)&translationArray, &translationArrayLen))
+ return exePath;
+
+ bool match = false;
+ for (UINT i = 0; i < (translationArrayLen / sizeof(LANGANDCODEPAGE)); i++) {
+ wchar_t fileDescriptionKey[256];
+ LANGID defaultUILanguage = GetUserDefaultUILanguage();
+ if (defaultUILanguage != translationArray[i].wLanguage)
+ continue;
+
+ match = true;
+ wchar_t* fileDescription = NULL;
+ UINT fileDescriptionSize = 0;
+ swprintf(fileDescriptionKey, L"\\StringFileInfo\\%04x%04x\\FileDescription",
+ translationArray[i].wLanguage, translationArray[i].wCodePage);
+ if (VerQueryValueW(fileVersionInfo, fileDescriptionKey, (LPVOID*)&fileDescription, &fileDescriptionSize)) {
+ exePath = std::wstring(fileDescription);
+ }
+ }
+
+ if (!match && 1 <= (translationArrayLen / sizeof(LANGANDCODEPAGE))) {
+ wchar_t fileDescriptionKey[256];
+
+wchar_t* fileDescription = NULL;
+ UINT fileDescriptionSize = 0;
+ swprintf(fileDescriptionKey, L"\\StringFileInfo\\%04x%04x\\FileDescription",
+ translationArray[0].wLanguage, translationArray[0].wCodePage);
+ if (VerQueryValueW(fileVersionInfo, fileDescriptionKey, (LPVOID*)&fileDescription, &fileDescriptionSize)) {
+ exePath = std::wstring(fileDescription);
+ }
+ }
+
+ free(fileVersionInfo);
+ return exePath;
+}
+
+//todo: conflicting names. change callback name
+void Session::setState(SessionState state) {
+ sessionState = state;
+}
+
+SessionState Session::getState() {
+ return sessionState;
+}
+
+void Session::setStateCallback(SessionStateCallback *ssc){
+ sessionControl->RegisterAudioSessionNotification((IAudioSessionEvents*) ssc);
+}
+
+void Session::removeStateCallback(SessionStateCallback *ssc){
+ sessionControl->UnregisterAudioSessionNotification((IAudioSessionEvents*) ssc);
+}
+
+Session::~Session() {
+ sessionControl->Release();
+ sessionVolume->Release();
+}
diff --git a/src/back/backsessionclasses.h b/src/back/backsessionclasses.h
new file mode 100644
index 0000000..e6c8490
--- /dev/null
+++ b/src/back/backsessionclasses.h
@@ -0,0 +1,57 @@
+#pragma once
+#include "msinclude.h"
+
+#include "global.h"
+#include "contclasses.h"
+
+class Endpoint;
+
+class SessionStateCallback : public IAudioSessionEvents {
+ public:
+ SessionStateCallback(SessionHandler *sh);
+ ULONG AddRef();
+ ULONG Release();
+ HRESULT QueryInterface(REFIID riid, VOID **ppvInterface);
+ HRESULT OnSessionCreated(IAudioSessionControl *NewSession);
+ HRESULT OnChannelVolumeChanged(DWORD ChannelCount, float NewChannelVolumeArray[], DWORD ChangedChannel, LPCGUID EventContext);
+ HRESULT OnDisplayNameChanged(LPCWSTR NewDisplayName, LPCGUID EventContext);
+ HRESULT OnGroupingParamChanged( LPCGUID NewGroupingParam, LPCGUID EventContext);
+ HRESULT OnIconPathChanged(LPCWSTR NewIconPath, LPCGUID EventContext);
+ HRESULT OnSessionDisconnected(AudioSessionDisconnectReason DisconnectReason);
+ HRESULT OnSimpleVolumeChanged(float NewVolume, BOOL NewMute, LPCGUID EventContext);
+ HRESULT OnStateChanged(AudioSessionState NewState);
+
+ private:
+ ULONG ref = 1;
+ SessionHandler *sh;
+};
+
+class Session {
+
+ public:
+ Session(Endpoint* ep, IAudioSessionControl2* sessionControl, size_t idx);
+ Session(Endpoint* ep, IAudioSessionControl2* sessionControl) : Session(ep, sessionControl, SIZE_MAX) {};
+ void setVolume(NGuid guid, int channel, float volume);
+ float getVolume(int channel);
+ void setMute(NGuid guid, bool muted);
+ bool getMute();
+ SessionState getState();
+ void setState(SessionState state);
+ void setIndex(size_t idx);
+ std::wstring getName();
+
+ void setStateCallback(SessionStateCallback *ssc);
+ void removeStateCallback(SessionStateCallback *ssc);
+ ~Session();
+ //uint32_t getChannelCount();
+
+ private:
+ std::wstring fetchProcessName(DWORD pid);
+ std::wstring sessionName;
+ SessionState sessionState;
+ Endpoint* ep;
+ IAudioSessionControl2* sessionControl = nullptr;
+ ISimpleAudioVolume* sessionVolume = nullptr;
+ size_t idx;
+};
+
diff --git a/src/back/ipolicyconfig.h b/src/back/ipolicyconfig.h
new file mode 100644
index 0000000..2e10aab
--- /dev/null
+++ b/src/back/ipolicyconfig.h
@@ -0,0 +1,192 @@
+// ----------------------------------------------------------------------------
+// PolicyConfig.h
+// Undocumented COM-interface IPolicyConfig.
+// Use for set default audio render endpoint
+// @author EreTIk
+// ----------------------------------------------------------------------------
+
+
+#pragma once
+
+
+interface DECLSPEC_UUID("CA286FC3-91FD-42C3-8E9B-CAAFA66242E3")
+IPolicyConfig10;
+
+interface DECLSPEC_UUID("00000000-0000-0000-C000-000000000046")
+IPolicyConfig10_1;
+
+interface DECLSPEC_UUID("F8679F50-850A-41CF-9C72-430F290290C8")
+IPolicyConfig7;
+
+/* interface DECLSPEC_UUID("568B9108-44BF-40B4-9006-86AFE5B5A620") */
+/* IPolicyConfigVista; */
+
+interface DECLSPEC_UUID("f8679f50-850a-41cf-9c72-430f290290c8")
+IPolicyConfig;
+class DECLSPEC_UUID("870af99c-171d-4f9e-af0d-e63df40c2bc9")
+CPolicyConfigClient;
+// ----------------------------------------------------------------------------
+// class CPolicyConfigClient
+// {870af99c-171d-4f9e-af0d-e63df40c2bc9}
+//
+// interface IPolicyConfig
+// {f8679f50-850a-41cf-9c72-430f290290c8}
+//
+// Query interface:
+// CComPtr PolicyConfig;
+// PolicyConfig.CoCreateInstance(__uuidof(CPolicyConfigClient));
+//
+// @compatible: Windows 7 and Later
+// ----------------------------------------------------------------------------
+interface IPolicyConfig : public IUnknown
+{
+public:
+
+ virtual HRESULT GetMixFormat(
+ PCWSTR,
+ WAVEFORMATEX **
+ );
+
+ virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat(
+ PCWSTR,
+ INT,
+ WAVEFORMATEX **
+ );
+
+ virtual HRESULT STDMETHODCALLTYPE ResetDeviceFormat(
+ PCWSTR
+ );
+
+ virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat(
+ PCWSTR,
+ WAVEFORMATEX *,
+ WAVEFORMATEX *
+ );
+
+ virtual HRESULT STDMETHODCALLTYPE GetProcessingPeriod(
+ PCWSTR,
+ INT,
+ PINT64,
+ PINT64
+ );
+
+ virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod(
+ PCWSTR,
+ PINT64
+ );
+
+ virtual HRESULT STDMETHODCALLTYPE GetShareMode(
+ PCWSTR,
+ struct DeviceShareMode *
+ );
+
+ virtual HRESULT STDMETHODCALLTYPE SetShareMode(
+ PCWSTR,
+ struct DeviceShareMode *
+ );
+
+ virtual HRESULT STDMETHODCALLTYPE GetPropertyValue(
+ PCWSTR,
+ const PROPERTYKEY &,
+ PROPVARIANT *
+ );
+
+ virtual HRESULT STDMETHODCALLTYPE SetPropertyValue(
+ PCWSTR,
+ const PROPERTYKEY &,
+ PROPVARIANT *
+ );
+
+ virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint(
+ PCWSTR wszDeviceId,
+ ERole eRole
+ );
+
+ virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility(
+ PCWSTR,
+ INT
+ );
+};
+
+/* interface DECLSPEC_UUID("568b9108-44bf-40b4-9006-86afe5b5a620") */
+/* IPolicyConfigVista; */
+/* class DECLSPEC_UUID("294935CE-F637-4E7C-A41B-AB255460B862") */
+/* CPolicyConfigVistaClient; */
+/* // ---------------------------------------------------------------------------- */
+/* // class CPolicyConfigVistaClient */
+/* // {294935CE-F637-4E7C-A41B-AB255460B862} */
+/* // */
+/* // interface IPolicyConfigVista */
+/* // {568b9108-44bf-40b4-9006-86afe5b5a620} */
+/* // */
+/* // Query interface: */
+/* // CComPtr PolicyConfig; */
+/* // PolicyConfig.CoCreateInstance(__uuidof(CPolicyConfigVistaClient)); */
+/* // */
+/* // @compatible: Windows Vista and Later */
+/* // ---------------------------------------------------------------------------- */
+/* interface IPolicyConfigVista : public IUnknown */
+/* { */
+/* public: */
+
+/* virtual HRESULT GetMixFormat( */
+/* PCWSTR, */
+/* WAVEFORMATEX ** */
+/* ); // not available on Windows 7, use method from IPolicyConfig */
+
+/* virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat( */
+/* PCWSTR, */
+/* INT, */
+/* WAVEFORMATEX ** */
+/* ); */
+
+/* virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat( */
+/* PCWSTR, */
+/* WAVEFORMATEX *, */
+/* WAVEFORMATEX * */
+/* ); */
+
+/* virtual HRESULT STDMETHODCALLTYPE GetProcessingPeriod( */
+/* PCWSTR, */
+/* INT, */
+/* PINT64, */
+/* PINT64 */
+/* ); // not available on Windows 7, use method from IPolicyConfig */
+
+/* virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod( */
+/* PCWSTR, */
+/* PINT64 */
+/* ); // not available on Windows 7, use method from IPolicyConfig */
+
+/* virtual HRESULT STDMETHODCALLTYPE GetShareMode( */
+/* PCWSTR, */
+/* struct DeviceShareMode * */
+/* ); // not available on Windows 7, use method from IPolicyConfig */
+
+/* virtual HRESULT STDMETHODCALLTYPE SetShareMode( */
+/* PCWSTR, */
+/* struct DeviceShareMode * */
+/* ); // not available on Windows 7, use method from IPolicyConfig */
+
+/* virtual HRESULT STDMETHODCALLTYPE GetPropertyValue( */
+/* PCWSTR, */
+/* const PROPERTYKEY &, */
+/* PROPVARIANT * */
+/* ); */
+
+/* virtual HRESULT STDMETHODCALLTYPE SetPropertyValue( */
+/* PCWSTR, */
+/* const PROPERTYKEY &, */
+/* PROPVARIANT * */
+/* ); */
+
+/* virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint( */
+/* __in PCWSTR wszDeviceId, */
+/* __in ERole eRole */
+/* ); */
+
+/* virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility( */
+/* PCWSTR, */
+/* INT */
+/* ); // not available on Windows 7, use method from IPolicyConfig */
+/* }; */
diff --git a/src/back/msinclude.h b/src/back/msinclude.h
new file mode 100644
index 0000000..fc7beac
--- /dev/null
+++ b/src/back/msinclude.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#define _WIN32_WINNT 0x0A00
+#include
+
+//done by qt by def #define UNICODE
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+//#include
+
+#include
+#include
+#include
+//#include
+//#include
+#include
+#include
+#include "ipolicyconfig.h"
+#include
+#include
diff --git a/src/cont/contclasses.cpp b/src/cont/contclasses.cpp
index 30b2688..38c30b1 100644
--- a/src/cont/contclasses.cpp
+++ b/src/cont/contclasses.cpp
@@ -1,36 +1,337 @@
+#include "backlasses.h"
#include "contclasses.h"
+//TODO: pragma once
-Overseer OverseerHandler::os;
-
-EndpointHandler::EndpointHandler(Endpoint *ept, QObject *parent) : QObject(parent) {
- this->ept = ept;
- eptName = QString::fromStdWString(ept->getName());
-}
-
-void EndpointHandler::setValue(int value){
- ept->setVolume((float)value / 100);
-}
-
-QString EndpointHandler::getName(){
- return eptName;
-}
-
-float EndpointHandler::getVolume(){
- return ept->getVolume();
-}
-
-Overseer OverseerHandler::getOverseer(){
- return os;
-}
-
-OverseerHandler::OverseerHandler(QObject *parent) : QObject(parent) {
+EndpointHandler::EndpointHandler(uint64_t idx, Flows flow) {
+ //std::vector endpoints = osh->getPlaybackEndpoints().at(idx);
+ this->idx = idx;
+ this->flow = flow;
+ this->ep = (flow == Flows::FLOW_PLAYBACK ? osh->getPlaybackEndpoints().at(idx) : osh->getCaptureEndpoints().at(idx));
+
+ epc = new EndpointVolumeCallback(ep);
+ ensc = new EndpointNewSessionCallback(this);
+ this->callbackInfo.caller = osh->getGuid();
+ ep->registerNewSessionNotification(ensc);
+ //epName = ep->getName();
+ this->setBackEndpointVolumeCallbackInfoContent(this->getState());
+ osh->pushBackEndpointHandler(this, flow);
+ if (this->flow == Flows::FLOW_PLAYBACK) {
+ for (int i = 0; i < this->getSessionCount(); i++) {
+ SessionHandler* sessionHandler = new SessionHandler(this, this->getSessions().at(i),i);
+ sessionHandlers.push_back(sessionHandler);
+ }
+ }
}
-std::vector* OverseerHandler::getEndpointHandlers(){
- return endpointHandlers;
+void OverseerHandler::pushBackEndpointHandler(EndpointHandler* eph, Flows flow) {
+ if (eph == nullptr) return;
+ if (flow == Flows::FLOW_PLAYBACK)
+ this->playbackEndpointHandlers.push_back(eph);
+ else
+ this->captureEndpointHandlers.push_back(eph);
+ return;
}
-void OverseerHandler::setEndpointHandlers(std::vector *ephs){
- this->endpointHandlers = ephs;
+void EndpointHandler::setFrontVisibilityInfo(EndpointState state, uint64_t frontIdx){
+ ephfv.visibility = state;
+ ephfv.frontIdx = frontIdx;
+}
+
+uint64_t EndpointHandler::getFrontVisibilityIndex(){
+ return ephfv.frontIdx;
+}
+
+EndpointState EndpointHandler::getFrontVisibilityState(){
+ return ephfv.visibility;
+}
+
+Flows EndpointHandler::getFlow(){
+ return ep->getFlow();
+}
+
+/* these two, currently unused. If I use them, I should feel bad.
+ * Endpoint* EndpointHandler::getEndpoint() {
+ * return this->ep;
+ * }
+ *
+ * EndpointVolumeCallback* EndpointHandler::getEndpointVolumeCallback() {
+ * return this->epc;
+ * }
+ */
+
+BackEndpointVolumeCallbackInfo* EndpointHandler::getCallbackInfo(){
+ return &this->callbackInfo;
+}
+
+uint32_t EndpointHandler::getChannelCount(){
+ return ep->getChannelCount();
+}
+
+void EndpointHandler::setIndex(uint64_t idx){
+ this->idx = idx;
+}
+
+uint64_t EndpointHandler::getIndex(){
+ return idx;
+}
+
+/*
+ * -1 for master volume
+ */
+void EndpointHandler::setVolume(NGuid guid, int channel, int value){
+ if (channel == AudioChannel::CHANNEL_MAIN)
+ ep->setVolume(guid, channel, (float)value / 100);
+ else ep->setVolume(guid, channel, (float)value / 100);
+}
+
+void EndpointHandler::setMute(NGuid guid, bool muted){
+ ep->setMute(guid, muted);
+}
+
+std::wstring EndpointHandler::getName(){
+ return ep->getName();
+}
+
+std::wstring EndpointHandler::getId(){
+ return ep->getId();
+}
+
+float EndpointHandler::getVolume(int channel){
+ return ep->getVolume(channel);
+}
+
+bool EndpointHandler::getMute(){
+ return ep->getMute();
+}
+
+size_t EndpointHandler::getState(){
+ return ep->getState();
+}
+
+void EndpointHandler::setBackEndpointVolumeCallbackInfoContent(uint8_t state) {
+ if(state == EndpointState::ENDPOINT_ACTIVE) {
+ callbackInfo.muted = this->getMute();
+ callbackInfo.mainVolume = this->getVolume(AudioChannel::CHANNEL_MAIN);
+ callbackInfo.channels = this->getChannelCount();
+ ep->setVolumeCallback(epc);
+ callbackInfo.channelVolumes.resize(this->callbackInfo.channels);
+ for(uint32_t i = 0; i < this->getChannelCount(); i++){
+ callbackInfo.channelVolumes.at(i) = this->getVolume(i);
+ }
+ }
+}
+
+void EndpointHandler::setState(uint8_t state){
+ ep->setState(state);
+ this->setBackEndpointVolumeCallbackInfoContent(state);
+}
+
+void EndpointHandler::setState(uint8_t state, uint64_t index){
+ ep->setState(state);
+ this->setFrontVisibilityInfo((EndpointState)state, index);
+ this->setBackEndpointVolumeCallbackInfoContent(state);
+}
+
+uint8_t EndpointHandler::getRoles(){
+ return ep->getRoles();
+}
+
+void EndpointHandler::setRoles(Roles newRole){
+ ep->setRoles(newRole);
+}
+
+void EndpointHandler::assignRoles(Roles newRole){
+ ep->assignRoles(newRole);
+}
+
+void EndpointHandler::removeRoles(Roles newRole){
+ ep->removeRoles(newRole);
+}
+
+void EndpointHandler::setAddSessionWidgetFunction(std::function addSessionWidget) {
+ this->addSessionWidget = addSessionWidget;
+}
+
+void EndpointHandler::setRemoveSessionWidgetFunction(std::function removeSessionWidget) {
+ this->removeSessionWidget = removeSessionWidget;
+}
+
+/* sessions */
+size_t EndpointHandler::getSessionCount() {
+ return ep->getSessionCount();
+}
+
+std::vector EndpointHandler::getSessions(){
+ return ep->getSessions();
+}
+
+std::vector EndpointHandler::getSessionHandlers(){
+ return this->sessionHandlers;
+}
+
+Endpoint* EndpointHandler::getEndpoint() {
+ return this->ep;
+}
+
+void EndpointHandler::addSessionSendFront(Session* session) {
+ ep->addSession(session);
+
+ SessionHandler* sessionHandler = new SessionHandler(this, session, (getSessionCount() - 1));
+ sessionHandlers.push_back(sessionHandler);
+ this->addSessionWidget(sessionHandler);
+}
+
+void EndpointHandler::sendSessionToFront(SessionHandler* sh) {
+ this->addSessionWidget(sh);
+}
+
+void EndpointHandler::removeSessionFromFront(SessionHandler* sh) {
+ this->removeSessionWidget(sh);
+}
+
+EndpointHandler::~EndpointHandler() {
+ ep->removeVolumeCallback(epc);
+ ep->unregisterNewSessionNotification(ensc);
+ epc->Release();
+ delete ep;
+}
+
+OverseerHandler::OverseerHandler() {
+ this->os = new Overseer();
+}
+
+std::vector OverseerHandler::getPlaybackEndpoints() {
+ return this->os->getPlaybackEndpoints();
+}
+
+std::vector OverseerHandler::getCaptureEndpoints() {
+ return this->os->getCaptureEndpoints();
+}
+
+std::vector OverseerHandler::getPlaybackEndpointHandlers(){
+ return playbackEndpointHandlers;
+}
+
+std::vector OverseerHandler::getCaptureEndpointHandlers(){
+ return captureEndpointHandlers;
+}
+
+uint64_t OverseerHandler::getPlaybackEndpointsCount(){
+ return this->os->getPlaybackEndpoints().size();
+}
+
+uint64_t OverseerHandler::getCaptureEndpointsCount(){
+ return this->os->getCaptureEndpoints().size();
+}
+
+void OverseerHandler::reloadEndpointHandlers(){
+ //todo: add capture
+
+ //std::vector* ephs = new std::vector;
+ log_debugcpp("Playback VSize: " + std::to_string(this->getPlaybackEndpointsCount()));
+
+ for(uint64_t i = 0; i < this->getPlaybackEndpointsCount(); i++){
+ log_debugcpp("Creating Playback handler " + std::to_string(i));
+
+ EndpointHandler* ephexx = new EndpointHandler(i, Flows::FLOW_PLAYBACK);
+ //this->playbackEndpointHandlers.push_back(ephexx);
+ log_debugcpp("Created Playback handler " + std::to_string(i) + ", adding to vector. " + " VSize: " + std::to_string(this->playbackEndpointHandlers.size()));
+
+ }
+
+ log_debugcpp("Capture VSize: " +
+ std::to_string(this->getCaptureEndpointsCount()));
+
+ for(uint64_t i = 0; i < this->getCaptureEndpointsCount(); i++){
+ log_debugcpp("Creating Capture handler " + std::to_string(i));
+
+ /*
+ * if(i < (this->captureEndpointHandlers.size()) &&
+ * this->captureEndpointHandlers.at(i) != nullptr)
+ * delete captureEndpointHandlers.at(i);
+ */
+
+ EndpointHandler* ephoo = new EndpointHandler(i, Flows::FLOW_CAPTURE);
+ //this->captureEndpointHandlers.push_back(ephoo);
+ log_debugcpp("Created Capture handler " + std::to_string(i) + ", adding to vector. " + " VSize: " + std::to_string(this->captureEndpointHandlers.size()));
+
+ /*
+ * if (i >= this->captureEndpointHandlers.size())
+ * captureEndpointHandlers.push_back(eph);
+ * else captureEndpointHandlers.at(i) = eph;
+ */
+ }
+ //setEndpointHandlers(ephs);
+}
+
+EndpointHandler* OverseerHandler::addEndpoint(std::wstring endpointId, /* out */ Flows *flow = nullptr){
+ Flows localFlow;
+ Endpoint* newEp = this->os->addEndpoint(endpointId, &localFlow);
+
+ uint64_t ephIdx = (localFlow == Flows::FLOW_PLAYBACK ? this->getPlaybackEndpointsCount() : this->getCaptureEndpointsCount()) - 1;
+
+ EndpointHandler* newEph = new EndpointHandler(ephIdx, localFlow);
+ // std::vector getPlaybackEndpointHandlers();
+ //std::vector getCaptureEndpointHandlers();
+ if (flow != nullptr) *flow = localFlow;
+ return newEph;
+}
+
+NGuid OverseerHandler::getGuid() {
+ return this->os->getGuid();
+}
+
+void OverseerHandler::setChangeFrontDefaultsFunction(std::function changeFrontDefaults){
+ this->changeFrontDefaults = changeFrontDefaults;
+}
+
+void OverseerHandler::changeFrontDefaultsCallback(Roles role, std::wstring endpointId) {
+ this->changeFrontDefaults(role, endpointId);
+}
+
+void OverseerHandler::reviseEndpointShowing(std::wstring endpointId, EndpointState state) {
+ std::vector allHandlers;
+ allHandlers.insert(allHandlers.end(), this->captureEndpointHandlers.begin(), this->captureEndpointHandlers.end());
+ allHandlers.insert(allHandlers.end(), this->playbackEndpointHandlers.begin(), this->playbackEndpointHandlers.end());
+ EndpointHandler* eph = nullptr;
+ for (auto loopEph : allHandlers) {
+ if (loopEph->getId() == endpointId) {
+ eph = loopEph;
+ break;
+ }
+ }
+
+ //debug
+ Flows flow;
+ if (!eph) {
+ if (state ^ EndpointState::ENDPOINT_ACTIVE) return;
+ //return;
+ //flow = Flows::FLOW_CAPTURE;
+ eph = osh->addEndpoint(endpointId, &flow);
+ } else
+ flow = eph->getFlow();
+
+ //todo: mic done but disabled. Tab-kun will come...
+ if (flow == Flows::FLOW_CAPTURE) return;
+
+
+ if(eph && EndpointState::ENDPOINT_ACTIVE & state) {
+ this->addEndpointWidget(eph);
+ } else if (eph && eph->getFrontVisibilityState() == EndpointState::ENDPOINT_ACTIVE){
+ this->removeEndpointWidget(eph->getFrontVisibilityIndex());
+ }
+ return;
+}
+
+void OverseerHandler::setAddEndpointWidgetFunction(std::function addEndpointWidget){
+ this->addEndpointWidget = addEndpointWidget;
+}
+
+void OverseerHandler::setRemoveEndpointWidgetFunction(std::function removeEndpointWidget){
+ this->removeEndpointWidget = removeEndpointWidget;
+}
+
+void OverseerHandler::setEndpointHandlers(std::vector ephs){
+ this->playbackEndpointHandlers = ephs;
}
diff --git a/src/cont/contclasses.h b/src/cont/contclasses.h
index 647ca1d..25fcaea 100644
--- a/src/cont/contclasses.h
+++ b/src/cont/contclasses.h
@@ -1,49 +1,138 @@
#pragma once
-#include
+
#include "global.h"
-#include "backlasses.h"
-
-
-class EndpointHandler : public QObject {
- Q_OBJECT
-
-public:
- EndpointHandler(Endpoint *ept, QObject *parent = nullptr);
- QString getName();
- float getVolume();
-
-private:
- Endpoint *ept;
- QString eptName;
- //QSlider *slidy;
-
-public slots:
- void setValue(int value);
-
-
-//signals:
+#include "contsessionclasses.h"
+//#define invoke_mem_fn(object,ptrToMember) ((object).*(ptrToMember))
+//#define pinvoke_mem_fn(object,ptrToMember) ((object)->*(ptrToMember))
+class EndpointWidget;
+class Endpoint;
+class EndpointVolumeCallback;
+class Overseer;
+class SessionHandler;
+class EndpointNewSessionCallback;
+struct BackEndpointVolumeCallbackInfo {
+ NGuid caller;
+ bool muted;
+ float mainVolume;
+ size_t channels;
+ std::vector channelVolumes;
};
+class EndpointHandler {
+
+public:
+ EndpointHandler(uint64_t idx, Flows flow);
+ void setBackEndpointVolumeCallbackInfoContent(uint8_t state);
-class OverseerHandler : public QObject {
- Q_OBJECT
+ //these two, currently unused. If I use them, I should feel bad.
+ //EndpointVolumeCallback* getEndpointVolumeCallback();
+ //Endpoint* getEndpoint();
+
+ //std::wstring epName;
+ BackEndpointVolumeCallbackInfo* getCallbackInfo();
+ uint32_t getChannelCount();
+
+ void setIndex(uint64_t idx);
+ uint64_t getIndex();
+ void setVolume(int channel, float volume);
+
+ std::wstring getName();
+ std::wstring getId();
+
+ void setFrontVisibilityInfo(EndpointState state, uint64_t frontIdx);
+ uint64_t getFrontVisibilityIndex();
+ EndpointState getFrontVisibilityState();
+
+ Flows getFlow();
+ float getVolume(int channel);
+ bool getMute();
+ size_t getState();
+ uint8_t getRoles();
+ void setRoles(Roles newRole);
+ void assignRoles(Roles newRole);
+ void removeRoles(Roles newRole);
+
+ void setVolume(NGuid guid, int channel, int value);
+ void setMute(NGuid guid, bool muted);
+ void setState(uint8_t state);
+ void setState(uint8_t state, uint64_t idx);
+
+ /* sessions */
+ size_t getSessionCount();
+ std::vector getSessionHandlers();
+ void createNewSession();
+ Endpoint* getEndpoint();
+
+ /*Session*/
+ void addSessionSendFront(Session* session);
+ void setAddSessionWidgetFunction(std::function addSessionWidget);
+ void setRemoveSessionWidgetFunction(std::function removeSessionWidget);
+ void sendSessionToFront(SessionHandler* sh);
+ void removeSessionFromFront(SessionHandler* sh);
+
+ ~EndpointHandler();
+private:
+ std::vector getSessions();
+
+ uint64_t idx;
+ Endpoint *ep = nullptr;
+ EndpointVolumeCallback *epc = nullptr;
+ Flows flow;
+ BackEndpointVolumeCallbackInfo callbackInfo;
+ struct EndpointHandlerFrontVisibility {
+ EndpointState visibility = EndpointState::ENDPOINT_ALL;
+ uint64_t frontIdx = INT_MAX;
+ };
+ EndpointHandlerFrontVisibility ephfv;
+ EndpointNewSessionCallback* ensc;
+ std::vector sessionHandlers;
+ std::function addSessionWidget;
+ std::function removeSessionWidget;
+ //QSlider *slidy;
+};
+
+class OverseerHandler {
public:
- OverseerHandler(QObject *parent = nullptr);
- void setEndpointHandlers(std::vector *ephs);
- std::vector* getEndpointHandlers();
- static Overseer getOverseer();
+ OverseerHandler();
+ void setChangeFrontDefaultsFunction(std::function changeFrontDefaults);
+ void changeFrontDefaultsCallback(Roles role, std::wstring endpointId);
+
+ //void setReviseEndpointShowingFunction(std::function reviseEndpointShowing);
+ void reviseEndpointShowing(std::wstring endpointId, EndpointState state);
+ void setRemoveEndpointWidgetFunction(std::function removeEndpointWidget);
+ void setAddEndpointWidgetFunction(std::function addEndpointWidget);
+
+ void setEndpointHandlers(std::vector ephs);
+ std::vector getPlaybackEndpointHandlers();
+ std::vector getCaptureEndpointHandlers();
+ std::vector getPlaybackEndpoints();
+ std::vector getCaptureEndpoints();
+ void pushBackEndpointHandler(EndpointHandler* eph, Flows flow);
+ uint64_t getPlaybackEndpointsCount();
+ uint64_t getCaptureEndpointsCount();
+ void reloadEndpointHandlers();
+ EndpointHandler* addEndpoint(std::wstring endpointId, Flows *flow);
+ NGuid getGuid();
+
+ /*
+ * void setSessionVolumeCallback(std::function changeSessionVolume);
+ * void setSessionVolume(float newValue, );
+ */
private:
- static Overseer os;
- std::vector *endpointHandlers;
- //QSlider *slidy;
-
- //public slots:
- //void setValue(int value);
-
-
+ Overseer *os;
+ std::vector playbackEndpointHandlers;
+ std::vector captureEndpointHandlers;
+ std::function changeFrontDefaults;
+ std::function removeEndpointWidget;
+ std::function addEndpointWidget;
+ /* Session's */
+ std::function changeSessionVolume;
+ //std::function updateFrontVolumeCallback;
+ //std::function updateFrontMuteCallback;
+
};
diff --git a/src/cont/contsessionclasses.cpp b/src/cont/contsessionclasses.cpp
new file mode 100644
index 0000000..155c0e1
--- /dev/null
+++ b/src/cont/contsessionclasses.cpp
@@ -0,0 +1,79 @@
+#include "contsessionclasses.h"
+#include "backsessionclasses.h"
+
+SessionHandler::SessionHandler(EndpointHandler* eph, Session* session, size_t idx) {
+ this->eph = eph;
+ this->idx = idx;
+ this->session = session;
+
+ this->svi.mainVolume = session->getVolume(AudioChannel::CHANNEL_MAIN);
+ this->svi.muted = session->getMute();
+ this->svi.caller = osh->getGuid();
+
+ ssc = new SessionStateCallback(this);
+ this->session->setStateCallback(ssc);
+}
+
+void SessionHandler::setVolume(NGuid guid, int channel, int value){
+ if (channel == AudioChannel::CHANNEL_MAIN)
+ session->setVolume(guid, channel, (float)value / 100);
+ else session->setVolume(guid, channel, (float)value / 100);
+}
+
+float SessionHandler::getVolume(int channel){
+ return session->getVolume(channel);
+}
+
+void SessionHandler::setMute(NGuid guid, bool muted){
+ session->setMute(guid, muted);
+}
+
+std::wstring SessionHandler::getName(){
+ return session->getName();
+}
+
+bool SessionHandler::getMute(){
+ return session->getMute();
+}
+
+void SessionHandler::setFrontIndex(uint64_t frontIdx) {
+ this->frontIdx = frontIdx;
+}
+
+uint64_t SessionHandler::getFrontIndex() {
+ return frontIdx;
+}
+
+SessionVolumeInfo* SessionHandler::getVolumeInfo() {
+ return &svi;
+}
+
+SessionState SessionHandler::getState() {
+ return session->getState();
+}
+
+void SessionHandler::setState(SessionState state) {
+ session->setState(state);
+}
+
+void SessionHandler::reviseSessionShowing(SessionState state) {
+ SessionState currentState = this->getState();
+ switch (currentState) {
+ case SessionState::ACTIVE:
+ case SessionState::INACTIVE:
+ if (state == SessionState::EXPIRED) {
+ eph->removeSessionFromFront(this);
+ }
+ break;
+ case SessionState::EXPIRED:
+ if (state == SessionState::ACTIVE || INACTIVE) {
+ eph->sendSessionToFront(this);
+ }
+ break;
+ case SessionState::DISCONNECTED:
+ if (frontIdx != INT_MAX)
+ eph->removeSessionFromFront(this);
+ break;
+ }
+}
+
diff --git a/src/cont/contsessionclasses.h b/src/cont/contsessionclasses.h
new file mode 100644
index 0000000..51f620f
--- /dev/null
+++ b/src/cont/contsessionclasses.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include "global.h"
+//#include "contclasses.h"
+
+class EndpointHandler;
+class Session;
+class SessionStateCallback;
+
+struct SessionVolumeInfo {
+ //SessionVolumeInfo(bool muted, float mainVolume);
+ bool muted;
+ float mainVolume;
+ NGuid caller;
+
+ //size_t channels;
+ //std::vector channelVolumes;
+};
+
+class SessionHandler {
+ public:
+ SessionHandler(EndpointHandler* eph, Session* session, size_t idx);
+ void setVolume(NGuid guid, int channel, int value);
+ float getVolume(int channel);
+ void setMute(NGuid guid, bool muted);
+ bool getMute();
+ void setFrontIndex(uint64_t frontIdx);
+ SessionState getState();
+ void setState(SessionState state);
+ uint64_t getFrontIndex();
+ std::wstring getName();
+ void reviseSessionShowing(SessionState state);
+ SessionVolumeInfo* getVolumeInfo();
+
+ private:
+ SessionVolumeInfo svi;
+ EndpointHandler* eph;
+ Session* session;
+ SessionStateCallback* ssc;
+ size_t idx;
+ uint64_t frontIdx = INT_MAX;
+};
diff --git a/src/debug.h b/src/debug.h
new file mode 100644
index 0000000..da6285c
--- /dev/null
+++ b/src/debug.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#if defined (QT_DEBUG) || defined (DEBUG) || defined (_DEBUG)
+
+template
+std::bitset varToBitset(T info) {
+ std::bitset content(info);
+ return content;
+}
+#ifndef _WIN32
+#define log_debugcpp(str) do { \
+ std::cout << "[DEBUG]" << "(" << __FILE__ << ":" << __LINE__ << "): " << str << std::endl; \
+ } while (0)
+
+#define log_wdebugcpp(str) do { \
+ std::wcout << "[DEBUG]" << "(" << __FILE__ << ":" << __LINE__ << "): " << str << std::endl; \
+ } while (0)
+#else
+
+#include
+#include
+#define WIDE2(x) L##x
+#define WIDE1(x) WIDE2(x)
+#define WFILE WIDE1(__FILE__)
+#define log_debugcpp(str) { \
+ OutputDebugStringA(std::string("[DEBUG] (" + std::string(__FILE__) + ":" + std::to_string(__LINE__) + "): " + std::string(str) + "\n").c_str()); \
+ } while (0)
+
+#define log_wdebugcpp(str) do { \
+ OutputDebugStringW(std::wstring(L"[DEBUG] (" + std::wstring(WFILE) + L":" + std::to_wstring(__LINE__) + L"): " + std::wstring(str) +L"\n").c_str()); \
+ } while (0)
+#endif
+
+#define print_as_binary(len, type, info) varToBitset(info)
+
+#else
+#define log_debugcpp(str)
+#define log_wdebugcpp(str)
+#define print_as_binary(len, type, info)
+#endif
+
+/* Here as a quick reference, in case smthn similar is needed again */
+/* typedef void (EndpointWidget::*epwMuteFunc)(bool muted); */
+/* typedef void (EndpointWidget::*epwMainVolumeFunc)(float newValue); */
+/* typedef void (EndpointWidget::*epwChannelVolumeFunc)(uint32_t channel, float newValue); */
+/* typedef void (EndpointWidget::*epwToggleFrontFunc)(bool active); */
+
diff --git a/src/global.h b/src/global.h
index c44b7e5..292ec06 100644
--- a/src/global.h
+++ b/src/global.h
@@ -1,4 +1,82 @@
#pragma once
-#define log_debugcpp(str) do { \
- std::cout << "[DEBUG]" << "(" << __FILE__ << ":" << __LINE__ << "): " << str << std::endl; \
- } while (0)
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "debug.h"
+
+//TODO: Use tr();? QTranslator
+#define STRING_MUTE "Mute"
+#define STRING_UNMUTE "Unmute"
+#define STRING_QUIT "Quit"
+#define STRING_TITLE "Mixer Fachero"
+
+#define STRING_ROLE_CONSOLE "Console"
+#define STRING_ROLE_MULTIMEDIA "Multimedia"
+#define STRING_ROLE_COMMUNICATIONS "Communications"
+#define STRING_ROLE_ALL "All"
+
+#define STRING_SYSTEM_SOUNDS "System Sounds"
+#define LSTRING_SYSTEM_SOUNDS L"System Sounds"
+//INIT BACK
+
+enum AudioChannel {
+ CHANNEL_LEFT = (1 << 0),
+ CHANNEL_RIGHT = (1 << 1),
+ CHANNEL_MAIN = ~0,
+};
+
+enum EndpointState {
+ ENDPOINT_ACTIVE = (1 << 0),
+ ENDPOINT_DISABLED = (1 << 1),
+ ENDPOINT_NOTPRESENT = (1 << 2),
+ ENDPOINT_UNPLUGGED = (1 << 3),
+ ENDPOINT_ALL = 0x0F
+};
+
+enum SessionState {
+ ACTIVE = (1 << 0),
+ INACTIVE = (1 << 1),
+ EXPIRED = (1 << 2),
+ DISCONNECTED = (1 << 3),
+ ALL = 0x0F
+};
+
+enum Flows {
+ FLOW_PLAYBACK = (1 << 0),
+ FLOW_CAPTURE = (1 << 1),
+ FLOW_BOTH = (1 << 2),
+};
+
+enum Roles {
+ ROLE_CONSOLE = (1 << 0),
+ ROLE_MULTIMEDIA = (1 << 1),
+ ROLE_COMMUNICATIONS = (1 << 2),
+ ROLE_ALL = 0x07,
+};
+
+struct NGuid {
+ //todo: still leaking?
+ uint32_t data1;
+ uint16_t data2;
+ uint16_t data3;
+ unsigned char data4[8];
+
+
+ /* void freeData4(){ */
+ /* int i = 0; */
+ /* do{ */
+ /* if(this->data4 + i != nullptr) free(data4 + i); */
+ /* i++; */
+ /* }while (i < 8); */
+ /* } */
+};
+
+
+class OverseerHandler;
+extern OverseerHandler *osh;
+
diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp
index 38ae3f2..a9da261 100644
--- a/src/qt/qtclasses.cpp
+++ b/src/qt/qtclasses.cpp
@@ -1,53 +1,584 @@
#include "qtclasses.h"
-MainWindow::MainWindow(std::vector *ephs, QWidget *parent) : QMainWindow(parent) {
- // setWindowState(Qt::WindowFullScreen);
- // setCentralWidget(centralWidget);
- widget = new QWidget();
- layout = new QGridLayout();
-
- widget->setLayout(layout);
- setCentralWidget(widget);
- //layout->addWidget(pintas, 0, 0);
-
- setWindowTitle("slidea resbala nu c");
-
- setEndpointHandlers(ephs);
- for (unsigned int i = 0; i < this->ephs->size(); i++){
- QLabel *pintas = new QLabel(ephs->at(i)->getName());
- QSlider *teSlider = new QSlider(Qt::Horizontal);
- teSlider->setFocusPolicy(Qt::StrongFocus);
- teSlider->setTickPosition(QSlider::TicksBothSides);
- teSlider->setTickInterval(5);
- teSlider->setSingleStep(1);
- teSlider->setRange(0,100);
- float volume = ephs->at(i)->getVolume() * 100;
- teSlider->setValue((int)volume);
- log_debugcpp("ENDPOINT SET WITH VOLUME " << volume);
- layout->addWidget(pintas, i, 0);
- layout->addWidget(teSlider, i, 1);
- connect(teSlider, &QSlider::valueChanged, ephs->at(i), &EndpointHandler::setValue);
- }
+template
+CustomWidgetEvent::CustomWidgetEvent(QEvent::Type type, T payload) : QEvent(type){
+ this->payload = payload;
}
-void MainWindow::setEndpointHandlers(std::vector *ephs){
- this->ephs = ephs;
+void ExtendedCheckBox::customEvent(QEvent* ev) {
+ QEvent::Type tipo = ev->type();
+ if (ev->type() == (QEvent::Type)CustomQEvent::EndpointDefaultChange) {
+ //todo: still prone to bugs; whack-a-mole to come
+ ev->setAccepted(true);
+ this->blockSignals(true);
+ if (this->isEnabled()) {
+ this->setCheckState(Qt::Checked);
+ this->setDisabled(true);
+ } else {
+ this->setDisabled(false);
+ this->setCheckState(Qt::Unchecked);
+ }
+ this->blockSignals(false);
+ return;
+ }
+ // Make sure the rest of events are handled
+ QCheckBox::customEvent(ev);
+}
+
+SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) : QWidget(parent){
+ //todo: based on qgridlayout, name+mute should be its own widget, same with channels
+ this->idx = idx;
+ this->sh = sh;
+
+ layout = new QGridLayout(this);
+ //this->setLayout(
+
+ muteButton = new QCheckBox(this);
+ mainLabel = new QLabel(QString::fromStdWString(sh->getName()), this);
+ mainSlider = new QSlider(Qt::Horizontal, this);
+
+ mainSlider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ mainSlider->setFocusPolicy(Qt::StrongFocus);
+ mainSlider->setTickPosition(QSlider::TicksBothSides);
+ mainSlider->setTickInterval(5);
+ mainSlider->setSingleStep(1);
+ mainSlider->setRange(0,100);
+
+ muteButton->setCheckState((sh->getMute() == false ? Qt::Unchecked : Qt::Checked));
+ muteButton->setText(sh->getMute() ? STRING_UNMUTE : STRING_MUTE);
+ float volume = sh->getVolume(AudioChannel::CHANNEL_MAIN) * 100;
+ mainSlider->setValue((int)volume);
+ log_debugcpp("SESSION SET WITH VOLUME " + std::to_string(volume));
+
+ //tip: would need to be new widget with layout in it
+ //mainMuteLayout = new QGridLayout();
+ layout->addWidget(mainLabel, 0, 0, Qt::AlignLeft | Qt::AlignBottom);
+ layout->addWidget(muteButton, 0, 1, Qt::AlignLeft | Qt::AlignBottom);
+ layout->addWidget(mainSlider, 0, 2, 1, 2);
+
+ //TODO:0 = mute and muted, change volume = unmuted are client side tricks = 2 callbacks, one for volume, one for mute state. Implement as an user selectable option?
+ connect(mainSlider, &QSlider::valueChanged, this,&SessionWidget::updateMainVolume);
+ connect(muteButton, &QCheckBox::stateChanged, this, (&SessionWidget::updateMute));
+
+ /*
+ * Session Volume Polling
+ */
+ volumePoller = new QTimer();
+ connect(volumePoller, &QTimer::timeout, [this, sh](){
+ //if (memcmp(osh->callbackInfo[idx]->caller, osh->getGuid(), sizeof(NGuid)) == 0) return; CHECK IF THIS PROGRAM GENERATED THE FUNSIES IS NO LONGER IN USE FOR NOW.
+ //todo: global + constexpr + ratio
+ const float roundingFactor = 0.005;
+ mainSlider->blockSignals(true);
+ muteButton->blockSignals(true);
+ mainSlider->setValue((int)((sh->getVolumeInfo()->mainVolume + roundingFactor) * 100));
+ muteButton->setCheckState((sh->getVolumeInfo()->muted == false ? Qt::Unchecked : Qt::Checked));
+ muteButton->setText(sh->getVolumeInfo()->muted ? STRING_UNMUTE : STRING_MUTE);
+
+ //memcpy(osh->callbackInfo[idx]->caller, osh->getGuid(), sizeof(NGuid));
+
+ //TODO: el default = objcopy frees?
+ //Todo: like fr pregunta
+ sh->getVolumeInfo()->caller = osh->getGuid();
+ mainSlider->blockSignals(false);
+ muteButton->blockSignals(false);
+ });
+ volumePoller->start(10);
+}
+
+void SessionWidget::updateMute(int checked){
+ bool muted = (checked == 2 ? true : false);
+ this->sh->setMute(osh->getGuid(), muted);
+ this->muteButton->setText(this->sh->getMute() ? STRING_UNMUTE : STRING_MUTE);
+}
+
+void SessionWidget::updateMainVolume(int newValue){
+ this->sh->setVolume(osh->getGuid(), AudioChannel::CHANNEL_MAIN, newValue);
+}
+
+SessionWidget::~SessionWidget() {
+ volumePoller->stop();
+ delete volumePoller;
+}
+
+EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *parent) : QWidget(parent){
+ //todo: based on qgridlayout, name+mute should be its own widget, same with channels
+ row = 0;
+ this->idx = idx;
+ this->eph = eph;
+ //todo: sussy
+ this->eph->setState(EndpointState::ENDPOINT_ACTIVE, idx);
+
+ layout = new QGridLayout(this);
+ //this->setLayout(layout);
+ log_debugcpp("epw main layout parent: " + std::to_string((intptr_t)(layout->parent())));
+ if (parent == nullptr) { log_debugcpp("ayooooo?"); }
+
+ defaultRolesCheckBoxes = {
+ {Roles::ROLE_ALL, new ExtendedCheckBox(this)},
+ {Roles::ROLE_CONSOLE, new ExtendedCheckBox(this)},
+ {Roles::ROLE_MULTIMEDIA, new ExtendedCheckBox(this)},
+ {Roles::ROLE_COMMUNICATIONS, new ExtendedCheckBox(this)}
+ };
+
+ /*
+ * Mute, main slider and label setup
+ */
+ muteButton = new QCheckBox(this);
+ mainLabel = new QLabel(QString::fromStdWString(eph->getName()), this);
+ mainSlider = new QSlider(Qt::Horizontal, this);
+
+ if (this->eph->getState() != EndpointState::ENDPOINT_ACTIVE) {
+ layout->addWidget(mainLabel, row, 0);
+ layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Minimum), 1, 0);
+ return;
+ }
+
+ //muteButton->setStyleSheet("background-color: #A3C1DA; color: red");
+ mainSlider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ mainSlider->setFocusPolicy(Qt::StrongFocus);
+ mainSlider->setTickPosition(QSlider::TicksBothSides);
+ mainSlider->setTickInterval(5);
+ mainSlider->setSingleStep(1);
+ mainSlider->setRange(0,100);
+
+ muteButton->setCheckState((eph->getMute() == false ? Qt::Unchecked : Qt::Checked));
+ muteButton->setText(eph->getMute() ? STRING_UNMUTE : STRING_MUTE);
+ float volume = eph->getVolume(AudioChannel::CHANNEL_MAIN) * 100;
+ mainSlider->setValue((int)volume);
+ log_debugcpp("ENDPOINT SET WITH VOLUME " + std::to_string(volume));
+
+ //tip: would need to be new widget with layout in it
+ //mainMuteLayout = new QGridLayout();
+ layout->addWidget(mainLabel, row, 0, Qt::AlignLeft | Qt::AlignBottom);
+ layout->addWidget(muteButton, row, 1, Qt::AlignLeft | Qt::AlignBottom);
+ layout->addWidget(mainSlider, row, 2, 1, 2);
+ row++;
+
+ //TODO:0 = mute and muted, change volume = unmuted are client side tricks = 2 callbacks, one for volume, one for mute state. Implement as an user selectable option?
+ connect(mainSlider, &QSlider::valueChanged, this,&EndpointWidget::updateMainVolume);
+ connect(muteButton, &QCheckBox::stateChanged, this, (&EndpointWidget::updateMute));
+
+
+/*
+ * Channel sliders setup
+ */
+ uint32_t epChannelCount = eph->getChannelCount();
+ for(uint32_t i = 0; i < epChannelCount && epChannelCount > 1; i++){
+ QSlider* tmp = new QSlider(Qt::Horizontal);
+ QLabel* tmpLb = new QLabel("");
+ tmp->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ tmp->setTickInterval(5);
+ tmp->setSingleStep(1);
+ tmp->setRange(0,100);
+
+ volume = eph->getVolume(i) * 100;
+ tmp->setValue((int) volume);
+ tmpLb->setText(QString::number(volume));
+ this->channelSliders.push_back(tmp);
+ this->channelLabels.push_back(tmpLb);
+ layout->addWidget(tmp, row + 1, i);
+ layout->addWidget(tmpLb, row + 2, i);
+
+ //TODO: check if there's a need to prevent deadlocks; probably this will eventually turn into its own func
+ //this causes channel bar desync when back -> front. blocksignals below fix it. huh.
+ connect(tmp, &QSlider::valueChanged, [this, i](int newValue){
+ this->eph->setVolume(osh->getGuid(), i, newValue);
+ this->channelLabels.at(i)->setText(QString::number(newValue));
+ });
+ }
+ row += 3;
+
+ /*
+ * Role ExtendedCheckBoxes setup
+ */
+
+ uint8_t assignedRoles = eph->getRoles();
+ defaultRolesCheckBoxes.at(Roles::ROLE_ALL)->setCheckState(assignedRoles == Roles::ROLE_ALL ? Qt::Checked : Qt::Unchecked);
+ //todo duditas de &
+ defaultRolesCheckBoxes.at(Roles::ROLE_ALL)->setDisabled(assignedRoles == Roles::ROLE_ALL ? true : false);
+ defaultRolesCheckBoxes.at(Roles::ROLE_ALL)->setText(STRING_ROLE_ALL);
+
+ defaultRolesCheckBoxes.at(Roles::ROLE_CONSOLE)->setCheckState(assignedRoles & Roles::ROLE_CONSOLE ? Qt::Checked : Qt::Unchecked);
+ defaultRolesCheckBoxes.at(Roles::ROLE_CONSOLE)->setDisabled(assignedRoles & Roles::ROLE_CONSOLE ? true : false);
+ defaultRolesCheckBoxes.at(Roles::ROLE_CONSOLE)->setText(STRING_ROLE_CONSOLE);
+
+ defaultRolesCheckBoxes.at(Roles::ROLE_MULTIMEDIA)->setCheckState(assignedRoles & Roles::ROLE_MULTIMEDIA ? Qt::Checked : Qt::Unchecked);
+ defaultRolesCheckBoxes.at(Roles::ROLE_MULTIMEDIA)->setDisabled(assignedRoles & Roles::ROLE_MULTIMEDIA ? true : false);
+ defaultRolesCheckBoxes.at(Roles::ROLE_MULTIMEDIA)->setText(STRING_ROLE_MULTIMEDIA);
+
+ defaultRolesCheckBoxes.at(Roles::ROLE_COMMUNICATIONS)->setCheckState(assignedRoles & Roles::ROLE_COMMUNICATIONS ? Qt::Checked : Qt::Unchecked);
+ defaultRolesCheckBoxes.at(Roles::ROLE_COMMUNICATIONS)->setDisabled(assignedRoles & Roles::ROLE_COMMUNICATIONS ? true : false);
+ defaultRolesCheckBoxes.at(Roles::ROLE_COMMUNICATIONS)->setText(STRING_ROLE_COMMUNICATIONS);
+
+ connect(defaultRolesCheckBoxes.at(Roles::ROLE_ALL), &QCheckBox::stateChanged,[this] {
+ this->eph->setRoles(Roles::ROLE_ALL);
+ });
+ connect(defaultRolesCheckBoxes.at(Roles::ROLE_CONSOLE), &QCheckBox::stateChanged,[this] {
+ this->eph->setRoles(Roles::ROLE_CONSOLE);
+ });
+ connect(defaultRolesCheckBoxes.at(Roles::ROLE_MULTIMEDIA), &QCheckBox::stateChanged,[this] {
+ this->eph->setRoles(Roles::ROLE_MULTIMEDIA);
+ });
+
+ connect(defaultRolesCheckBoxes.at(Roles::ROLE_COMMUNICATIONS), &QCheckBox::stateChanged,[this] {
+ this->eph->setRoles(Roles::ROLE_COMMUNICATIONS);
+ });
+
+ layout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_ALL), row, 0);
+ layout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_CONSOLE), row, 1);
+ layout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_MULTIMEDIA), row, 2);
+ layout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_COMMUNICATIONS), row, 3);
+ row++;
+/* ----------------------------------------------------------- */
+
+ /*
+ * EndpointVolume Polling time
+ */
+ timer = new QTimer();
+ connect(timer, &QTimer::timeout, [this, eph](){
+ //if (memcmp(osh->callbackInfo[idx]->caller, osh->getGuid(), sizeof(NGuid)) == 0) return; CHECK IF THIS PROGRAM GENERATED THE FUNSIES IS NO LONGER IN USE FOR NOW.
+ //todo: global + constexpr + ratio
+ const float roundingFactor = 0.005;
+ mainSlider->blockSignals(true);
+ muteButton->blockSignals(true);
+ mainSlider->setValue((int)((eph->getCallbackInfo()->mainVolume + roundingFactor) * 100));
+ muteButton->setCheckState((eph->getCallbackInfo()->muted == false ? Qt::Unchecked : Qt::Checked));
+ muteButton->setText(eph->getCallbackInfo()->muted ? STRING_UNMUTE : STRING_MUTE);
+ for(uint32_t i = 0; i < eph->getCallbackInfo()->channels && eph->getChannelCount() > 1; i++){
+ this->channelSliders.at(i)->blockSignals(true);
+ this->channelSliders.at(i)->setValue((int)((eph->getCallbackInfo()->channelVolumes[i] + roundingFactor) * 100));
+ this->channelLabels.at(i)->setText(QString::number((int)((eph->getCallbackInfo()->channelVolumes[i] + roundingFactor) * 100)));
+ this->channelSliders.at(i)->blockSignals(false);
+ }
+ //memcpy(osh->callbackInfo[idx]->caller, osh->getGuid(), sizeof(NGuid));
+
+ //TODO: el default = objcopy frees?
+ //Todo: like fr pregunta
+ eph->getCallbackInfo()->caller = osh->getGuid();
+ mainSlider->blockSignals(false);
+ muteButton->blockSignals(false);
+ });
+ timer->start(10);
+
+ /* First SessionWidget batch */
+ for (size_t i = 0; i < eph->getSessionCount(); i++) {
+ SessionWidget* sessionWidget = new SessionWidget(i, eph->getSessionHandlers().at(i), this);
+ layout->addWidget(sessionWidget, row, 4);
+ row++;
+ sessionWidgets.push_back(sessionWidget);
+ eph->getSessionHandlers().at(i)->setFrontIndex(i);
+ }
+
+ /* Add/Remove SessionWidget callback */
+ eph->setAddSessionWidgetFunction([this](SessionHandler* sessionHandler) {
+ QCoreApplication::instance()->postEvent(this, new CustomWidgetEvent((QEvent::Type)CustomQEvent::SessionWidgetCreated, sessionHandler));
+ });
+
+ eph->setRemoveSessionWidgetFunction([this](SessionHandler* sessionHandler) {
+ QCoreApplication::instance()->postEvent(this, new CustomWidgetEvent((QEvent::Type)CustomQEvent::SessionWidgetObsolete, sessionHandler));
+ });
+
+ //todo parent?
+ layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Minimum), 1, 0);
+ layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Minimum), 4, 0);
+ layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 6, 0);
+ layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 6, 1);
+ log_debugcpp("ENDPOINT_WIDGETED");
+}
+
+void EndpointWidget::addSessionWidget(CustomWidgetEvent* ev){
+ uint64_t index = this->sessionWidgets.size();
+ SessionWidget* sw = new SessionWidget(index, ev->payload, this);
+ ev->payload->setFrontIndex(index);
+ this->layout->addWidget(sw, row, 4);
+ row++;
+ sessionWidgets.push_back(sw);
+ return;
+}
+
+void EndpointWidget::removeSessionWidget(CustomWidgetEvent* ev){
+ uint64_t i = ev->payload->getFrontIndex();
+ this->sessionWidgets.at(i)->setParent(nullptr);
+ this->layout->removeWidget(sessionWidgets.at(i));
+ delete sessionWidgets.at(i);
+ sessionWidgets.at(i) = nullptr;
+ ev->payload->setFrontIndex(INT_MAX);
+ //this->sessionWidgetsUpdateTimer->start();
+ return;
+}
+
+void EndpointWidget::customEvent(QEvent* ev) {
+ if (ev->type() == (QEvent::Type)CustomQEvent::SessionWidgetCreated) {
+ ev->setAccepted(true);
+ this->addSessionWidget((CustomWidgetEvent*) ev);
+ } else if (ev->type() == (QEvent::Type)CustomQEvent::SessionWidgetObsolete) {
+ ev->setAccepted(true);
+ this->removeSessionWidget((CustomWidgetEvent*) ev);
+ }
+ QWidget::customEvent(ev);
+}
+
+
+EndpointWidget::~EndpointWidget() {
+ timer->stop();
+ delete timer;
+ this->eph->setFrontVisibilityInfo(EndpointState::ENDPOINT_ALL, INT_MAX);
+}
+
+void MainWindow::customEvent(QEvent* ev) {
+ if (ev->type() == CustomQEvent::EndpointWidgetObsolete) {
+ ev->setAccepted(true);
+ this->removeEndpointWidget((CustomWidgetEvent*)ev);
+ } else if (ev->type() == (QEvent::Type)CustomQEvent::EndpointWidgetCreated) {
+ ev->setAccepted(true);
+ this->addEndpointWidget((CustomWidgetEvent*)ev);
+ }
+ QMainWindow::customEvent(ev);
+}
+
+void MainWindow::removeEndpointWidget(CustomWidgetEvent* ev){
+ uint64_t i = ev->payload;
+ this->ews.at(i)->setParent(nullptr);
+ this->layout->removeWidget(ews.at(i));
+ //uint64_t saisu = ews.size();
+ //delete ews.at(index);
+ delete ews.at(i);
+ ews.at(i) = nullptr;
+ this->ewsUpdateTimer->start();
+ return;
+}
+
+void MainWindow::addEndpointWidget(CustomWidgetEvent* ev){
+ EndpointWidget* epw = new EndpointWidget(this->ews.size(), ev->payload, widget);
+ this->layout->addWidget(epw);
+ ews.push_back(epw);
+ return;
+}
+
+void MainWindow::reorderEndpointWidgetCollection() {
+ /* Flatten */
+ size_t firstNullPosition = 0;
+ size_t ewsSize = ews.size();
+ bool breakSorting = false;
+
+ //todo: is all of gui really atomic by definition? im afraid of cutting through amazing add momentos, but I think I did my homework. Must check back.
+ for (size_t i = 0; i < ewsSize; i++) {
+ if (ews.at(i) == nullptr) {
+ for (size_t j = (i + 1); j < ewsSize; j++) {
+
+ if (ews.at(j) != nullptr) {
+ ews.at(i) = ews.at(j);
+ ews.at(i)->setIndex(i);
+ ews.at(j) = nullptr;
+ break;
+ }
+ if (j == ewsSize - 1) {
+ firstNullPosition = i;
+ breakSorting = true;
+ }
+
+ }
+ if (breakSorting) break;
+ }
+ }
+ ews.resize(firstNullPosition + 1);
+}
+
+void EndpointWidget::updateMute(int checked){
+ bool muted = (checked == 2 ? true : false);
+ this->eph->setMute(osh->getGuid(), muted);
+ this->muteButton->setText(this->eph->getMute() ? STRING_UNMUTE : STRING_MUTE);
+}
+
+void EndpointWidget::updateMainVolume(int newValue){
+ this->eph->setVolume(osh->getGuid(), AudioChannel::CHANNEL_MAIN, newValue);
}
/*
- * void MainWindow::setPlotButton() {
- * button = new QPushButton("push"),
- * button->setCheckable(true);
- * connect(button, SIGNAL(toggled(bool)), this, SLOT(toggled(bool)))
- * QHBoxLayout *plotsLayout = new QHBoxLayout;
- * plotsLayout->setSpacing(10);
- * plotsLayout->addWidget(funPlot);
- * QHBoxLayout *buttonsLayout = new QHBoxLayout ;
- * buttonsLayout->addWidget(button);
- * QVBoxLayout *widgetLayout = new QVBoxLayout;
- * widgetLayout->addLayout(plotsLayout);
- * widgetLayout->addLayout(buttonsLayout);
- * setLayout(widgetLayout);
- * ...
- */
+ * void EndpointWidget::updateVolume(uint32_t channel, float newValue){
+ * //this->blockSignals(true);
+ * int newVal = newValue * 100;
+ * if (channel == (uint32_t)AudioChannel::CHANNEL_MAIN) {
+ * //TIP: Above
+ * //this->mainSlider->blockSignals(true);
+ *
+ * if(this->mainSlider->value() != newVal) {
+ * this->mainSlider->blockSignals(true);
+ * this->mainSlider->setValue(newVal);
+ * this->mainSlider->blockSignals(false);
+ * }
+ * return;
+ * }
+ *
+ * for (size_t i = 0; i < sizeof(uint32_t) * 8 && i < channelSliders.size(); ++i) {
+ * if (((channel >> i) & 1) && this->channelSliders.at(i)->value() != newVal) {
+ * //this->channelSliders.at(i)->blockSignals(true);
+ *
+ * this->channelSliders.at(i)->setValue(newVal);
+ * this->channelLabels.at(i)->setText(QString::number((int)(newValue * 100)));
+ *
+ * //this->channelSliders.at(i)->blockSignals(false);
+ * }
+ * }
+ *
+ * //this->blockSignals(false);
+ * }
+ */
+
+EndpointHandler* EndpointWidget::getEndpointHandler(){
+ return this->eph;
+}
+
+/*
+ * void EndpointWidget::updateFrontIndex(uint64_t index){
+ * this->idx = index;
+ * }
+ */
+
+void EndpointWidget::setIndex(uint64_t idx){
+ this->idx = idx;
+ this->eph->setFrontVisibilityInfo(EndpointState::ENDPOINT_ACTIVE, this->idx);
+}
+
+uint64_t EndpointWidget::getIndex(){
+ return idx;
+}
+
+std::map EndpointWidget::getDefaultRolesWidgets() {
+ return defaultRolesCheckBoxes;
+}
+
+
+MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
+ // setWindowState(Qt::WindowFullScreen);
+ // setCentralWidget(centralWidget);
+
+ /*
+ * Registering needed custom events
+ */
+ QEvent::registerEventType(CustomQEvent::EndpointWidgetObsolete);
+ QEvent::registerEventType(CustomQEvent::EndpointWidgetCreated);
+ QEvent::registerEventType(CustomQEvent::EndpointDefaultChange);
+
+ ewsUpdateTimer = new QTimer(this);
+ widget = new QWidget();
+ layout = new QGridLayout();
+ trayIcon = new QSystemTrayIcon();
+ trayIconMenu = new QMenu();
+ trayIconMenuQuit = new QAction(STRING_QUIT);
+
+ ewsUpdateTimer->setSingleShot(true);
+ ewsUpdateTimer->setInterval(ewsUpdateTimerFrequency);
+ connect(ewsUpdateTimer, &QTimer::timeout, this, &MainWindow::reorderEndpointWidgetCollection);
+ widget->setLayout(layout);
+ setCentralWidget(widget);
+ //layout->addWidget(pintas, 0, 0);
+ setWindowTitle(STRING_TITLE);
+
+ reloadEndpointWidgets();
+
+ /*
+ * Tray Icon code
+ */
+ trayIconMenu->addSeparator();
+ trayIconMenu->addAction(trayIconMenuQuit);
+ connect(trayIconMenuQuit, &QAction::triggered, qApp, &QCoreApplication::quit);
+ trayIcon->setIcon(QIcon(":/assets/notificationAreaIcon.png"));
+ setWindowIcon(QIcon(":/assets/notificationAreaIcon.png"));
+ //TODO: Extend qsystemtrayicon to change mouse click?
+ //show before setting tooltip required; smells like bug to me!
+ trayIcon->show();
+ trayIcon->setToolTip(STRING_TITLE);
+ trayIcon->setContextMenu(trayIconMenu);
+ connect(trayIcon, &QSystemTrayIcon::activated, this, &MainWindow::trayIconActivated);
+
+ /*
+ * Set of function callback definitons for EndpointSituationCallback
+ */
+ osh->setChangeFrontDefaultsFunction([this](Roles role, std::wstring endpointId) {
+ for (auto epw : this->ews) {
+ /*
+ * Is this the new default endpoint?
+ */
+ if (epw->getEndpointHandler()->getId() == endpointId) {
+ //not necessary to keep endpointState flags up to date right now, but updating it will allow for later config files / profiles
+ epw->getDefaultRolesWidgets().at(role)->blockSignals(true);
+ epw->getEndpointHandler()->assignRoles(role);
+ epw->getDefaultRolesWidgets().at(role)->blockSignals(false);
+ QCoreApplication::instance()->postEvent(epw->getDefaultRolesWidgets().at(role), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange));
+ //epw->defaultRolesCheckBoxes.at(role)->postEnableChange();
+ /*
+ * And were you THE default?
+ */
+ if (epw->getEndpointHandler()->getRoles() == Roles::ROLE_ALL) {
+ QCoreApplication::instance()->postEvent(epw->getDefaultRolesWidgets().at(Roles::ROLE_ALL), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange));
+ }
+ /*
+ * Are you the dethroned king?
+ */
+ } else if (epw->getEndpointHandler()->getRoles() & role) {
+ /*
+ * And were you THE default up until now?
+ */
+ if (epw->getEndpointHandler()->getRoles() == Roles::ROLE_ALL) {
+ QCoreApplication::instance()->postEvent(epw->getDefaultRolesWidgets().at(Roles::ROLE_ALL), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange));
+ }
+
+ epw->getDefaultRolesWidgets().at(role)->blockSignals(true);
+ //Same as before. ini-san will come...
+ epw->getEndpointHandler()->removeRoles(role);
+ epw->getDefaultRolesWidgets().at(role)->blockSignals(false);
+ QCoreApplication::instance()->postEvent(epw->getDefaultRolesWidgets().at(role), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange));
+
+ }
+ }
+ });
+
+ osh->setRemoveEndpointWidgetFunction([this](uint64_t index) {
+ QCoreApplication::instance()->postEvent(this, new CustomWidgetEvent((QEvent::Type)CustomQEvent::EndpointWidgetObsolete, index));
+ });
+
+ osh->setAddEndpointWidgetFunction([this](EndpointHandler* eph) {
+ QCoreApplication::instance()->postEvent(this, new CustomWidgetEvent((QEvent::Type)CustomQEvent::EndpointWidgetCreated, eph));
+ });
+}
+
+void MainWindow::closeEvent(QCloseEvent *event) {
+ if (!event->spontaneous() || !isVisible()) return;
+
+ if (trayIcon->isVisible()) {
+ //todo: would be nice to show this to 1st time users; ini-san will come...
+ //this->trayIcon->showMessage("ini file calling","tratarte como un gilipollas la primera vez", QSystemTrayIcon::Information);
+
+ hide();
+ event->ignore();
+ }
+}
+
+void MainWindow::trayIconActivated(QSystemTrayIcon::ActivationReason reason) {
+ switch (reason) {
+ case QSystemTrayIcon::Trigger:
+ this->showNormal();
+ break;
+ default:
+ break;
+ }
+}
+
+void MainWindow::reloadEndpointWidgets() {
+ size_t i = 0;
+ for (size_t epwIndex = 0; i < (osh->getPlaybackEndpointHandlers().size()); i++) {
+ if (osh->getPlaybackEndpointHandlers().at(i)->getState() == EndpointState::ENDPOINT_ACTIVE){
+ log_debugcpp("EPWidget creation");
+ //osh->getPlaybackEndpointHandlers().at(i)->getCallbackInfo()->caller = osh->getGuid();
+ EndpointWidget *epw = new EndpointWidget(epwIndex, osh->getPlaybackEndpointHandlers().at(i), widget);
+ epwIndex++;
+ //alfinal estoes solopara inicializarlmao
+ ews.push_back(epw);
+ layout->addWidget(epw, i, 0);
+ }
+ }
+ //todo:: tas aqui tirao, no me gustas y probablemente yo a ti tampoco
+ layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), i, 0);
+}
diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h
index 6ef82b0..b9c4fa8 100644
--- a/src/qt/qtclasses.h
+++ b/src/qt/qtclasses.h
@@ -1,34 +1,200 @@
#pragma once
+
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
-#include
#include
#include
+#include
+
+#include
+#include
+#include
+//#include
+
#include
#include
#include
-//#include "global.h"
-#include "contclasses.h"
-//#include
-//#include
+#include
+#include
+#include
+/*
+ * #else
+ * class QSlider;
+ * class QLabel;
+ * class QGridLayout;
+ * class QPushButton;
+ * class QWidget;
+ * class QMainWindow;
+ * #endif
+ */
+#include "global.h"
+#include "contclasses.h"
+//class EndpointHandler;
+
+/*
+ * class ToggleButton : public QAbstractButton {
+ * Q_OBJECT
+ *
+ * public:
+ * ToggleButton(QWidget *parent = nullptr);
+ * void checkStateSet();
+ * bool hitButton(const QPoint &pos) const;
+ * void nextCheckState();
+ * void changeEvent(QEvent *e) override;
+ * bool event(QEvent *e) override;
+ * void focusInEvent(QFocusEvent *e) override;
+ * void focusOutEvent(QFocusEvent *e) override;
+ * void keyPressEvent(QKeyEvent *e) override;
+ * void keyReleaseEvent(QKeyEvent *e) override;
+ * void mouseMoveEvent(QMouseEvent *e) override;
+ * void mousePressEvent(QMouseEvent *e) override;
+ * void mouseReleaseEvent(QMouseEvent *e) override;
+ * void paintEvent(QPaintEvent *e) override = 0;
+ * void timerEvent(QTimerEvent *e) override;
+ * ToggleButton(QWidget *parent = nullptr);
+ * };
+ */
+enum CustomQEvent {
+ EndpointWidgetObsolete = 1001,
+ EndpointWidgetCreated = 1002,
+ EndpointDefaultChange = 1003,
+ SessionWidgetCreated = 1004,
+ SessionWidgetObsolete = 1005
+};
+
+template
+class CustomWidgetEvent : public QEvent {
+
+public:
+ CustomWidgetEvent(QEvent::Type type, T payload);
+ T payload;
+
+};
+//Q_DECLARE_METATYPE(EndpointWidgetEvent)
+
+class ExtendedCheckBox : public QCheckBox {
+ Q_OBJECT
+protected:
+ void customEvent(QEvent* ev) override;
+
+public:
+ //c++11: this inherits all parent's constructors unconditionally
+ using QCheckBox::QCheckBox;
+ //alternative being calling parent ctor directly after declaring child ctor:
+ //B(int x) : A(x) { }
+};
+
+
+class SessionWidget : public QWidget {
+Q_OBJECT
+
+public:
+ SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent /* = nullptr */);
+ ~SessionWidget();
+
+public slots:
+ void updateMainVolume(int newValue);
+ void updateMute(int checked);
+
+private:
+ QLabel *mainLabel = nullptr;
+ QSlider *mainSlider = nullptr;
+ uint64_t idx;
+ QGridLayout *layout = nullptr;
+ QCheckBox *muteButton = nullptr;
+ SessionHandler* sh;
+ QTimer* volumePoller = nullptr;
+};
+
+class EndpointWidget : public QWidget {
+Q_OBJECT
+
+public:
+ EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *parent = nullptr);
+
+ EndpointHandler* getEndpointHandler();
+ std::map getDefaultRolesWidgets();
+
+ void setIndex(uint64_t idx);
+ uint64_t getIndex();
+ void setVolume(int channel, float volume);
+
+ ~EndpointWidget();
+ //void updateMainVolume(float newValue);
+ //void updateVolume(uint32_t channel, float newValue);
+ //void updateMute(bool muted);
+
+ //void populateEndpointWidget(EndpointHandler *eph);
+ //void setEndpointHandlers(std::vector *ephs);
+
+public slots:
+ void updateMainVolume(int newValue);
+ void updateMute(int checked);
+
+protected:
+ void customEvent(QEvent* ev) override;
+
+private slots:
+ void addSessionWidget(CustomWidgetEvent* ev);
+ void removeSessionWidget(CustomWidgetEvent* ev);
+
+private:
+ int row;
+ QCheckBox *muteButton = nullptr;
+ QLabel *mainLabel = nullptr, *leftChannelLabel = nullptr, *rightChannelLabel = nullptr;
+ QSlider *mainSlider = nullptr;
+ std::vector channelSliders;
+ std::vector channelLabels;
+ QGridLayout *layout = nullptr;
+ QGridLayout *mainMuteLayout = nullptr;
+ std::map defaultRolesCheckBoxes;
+
+ EndpointHandler* eph;
+ size_t defaultRolesVectorSize = 4;
+ QTimer* timer = nullptr;
+ uint64_t idx;
+ std::vector sessionWidgets;
+ //std::vector *ephs;
+ //std::vector *sliders;
+
+ //signals:
+ //void valueChanged(int value);
+
+};
class MainWindow : public QMainWindow {
Q_OBJECT
//QWidget *centralWidget;
public:
- MainWindow(std::vector *ephs, QWidget *parent = nullptr);
- void setEndpointHandlers(std::vector *ephs);
+ MainWindow(QWidget *parent = nullptr);
+ void reloadEndpointWidgets();
+
+protected:
+ void closeEvent(QCloseEvent *event) override;
+ void customEvent(QEvent* ev) override;
+
+private slots:
+ void trayIconActivated(QSystemTrayIcon::ActivationReason reason);
+ void removeEndpointWidget(CustomWidgetEvent* ev);
+ void addEndpointWidget(CustomWidgetEvent* ev);
+ void reorderEndpointWidgetCollection();
+ //TODO: destroy/empty existing EndpointWidgets
+ //void setEndpointHandlers(std::vector *ephs);
private:
- std::vector *ephs;
- std::vector *sliders;
+ //std::vector *ephs;
+ std::vector ews;
QWidget *widget;
QGridLayout *layout;
- //QLabel *pintas;
+ QSystemTrayIcon *trayIcon;
+ QMenu *trayIconMenu;
+ QAction *trayIconMenuQuit;
+ QTimer *ewsUpdateTimer;
+ static constexpr uint64_t ewsUpdateTimerFrequency = 500;
//public slots:
// void setEndpointHandlers(std::vector *ephs);
diff --git a/src/qtestmain.cpp b/src/qtestmain.cpp
index 610f0b4..19cf627 100644
--- a/src/qtestmain.cpp
+++ b/src/qtestmain.cpp
@@ -1,42 +1,64 @@
-#include
-#include
-
//#include
//#include
//#include
+
+//#define QTBLESSED
#include
-#include
+#include
+#include
+#include
+#include
+//#include "contclasses.h"
#include "qtclasses.h"
-//TODO david #include "backlasses.h"
+#include "global.h"
-
-//INIT BACK
-OverseerHandler *osh = new OverseerHandler();
+OverseerHandler *osh = nullptr;
QApplication* createApplication(int &argc, char *argv[])
{
return new QApplication(argc, argv);
}
+bool isSingleInstanceRunning(QString appName) {
+ QLocalSocket socket;
+ socket.connectToServer(appName);
+ bool isOpen = socket.isOpen();
+ socket.close();
+ return isOpen;
+}
+
+QLocalServer* startSingleInstanceServer(QString appName) {
+ QLocalServer* server = new QLocalServer;
+ server->setSocketOptions(QLocalServer::WorldAccessOption);
+ server->listen(appName);
+ return server;
+}
int main (int argc, char* argv[]) {
//QApplication::setStyle("windowsvista");
- //INIT CONT
- std::vector epts = OverseerHandler::getOverseer().getPlaybackEndpoints();
- std::vector* ephs = new std::vector;
- for(unsigned int i = 0; i < epts.size(); i++){
- EndpointHandler *eph = new EndpointHandler(epts.at(i));
- ephs->push_back(eph);
- }
+ //Check if running
+ //https://stackoverflow.com/questions/48060989/qt-show-application-if-currently-running
+ if (!isSingleInstanceRunning("Mixer"))
+ startSingleInstanceServer("Mixer");
+ else exit(0);
+
+ osh = new OverseerHandler();
+ //qRegisterMetaType();
+
+ //INIT CONT
+ log_debugcpp("main init");
+ osh->reloadEndpointHandlers();
+ log_debugcpp("Reloaded endpoint handlers");
- osh->setEndpointHandlers(ephs);
//INIT FRONT
QScopedPointer app(createApplication(argc, argv));
- MainWindow window = MainWindow(ephs);
+ MainWindow window = MainWindow();
//window.setEndpointHandlers(ephs);
+ QApplication::setQuitOnLastWindowClosed(false);
app->setStyle("windowsvista");
window.show();
return app->exec();
}
+