Compare commits

..

60 commits

Author SHA1 Message Date
8f864f3394 wip: icons 2026-01-31 18:09:02 +01:00
3239e60471 fixed synch issues + memleak @ name fetching 2025-01-25 17:27:36 +01:00
f1b734bea6 fixed visuals when adding epw/sw while window is visible 2025-01-18 18:51:32 +01:00
53f9506115 fixed newly arisen race cons (busywaits) 2025-01-18 18:01:35 +01:00
109330dbbd fixed lots of edge cases revolving role changes 2025-01-16 22:13:41 +01:00
0a301fb9bf fixed new endpoints not appering if UI open + no roles / deque 2025-01-16 20:29:02 +01:00
a3d00be3fe fixed callback not released + unneded env func param 2025-01-16 18:09:02 +01:00
c60b6f71ed updated build script & small installer name fix 2025-01-15 22:09:45 +01:00
f0d263d96e changed misleading log message 2025-01-15 21:49:41 +01:00
59a92fa34b fixed race cons ep/eph coll, unwanted session state update crash
+ some minor code cleanup
2025-01-15 21:47:35 +01:00
667482cfea marshalling on sessions, missing override tag 2025-01-14 19:19:49 +01:00
cdde1ce9b8 installer done, recipe cleanup 2025-01-13 17:42:10 +01:00
c87c8d0990 set: fix wrong save path / fixed null deref / more env refactor 2025-01-07 18:53:17 +01:00
047808c89f ref Env namespace for win32/reg stuff / startup toggle
Bigger than usual commit, but all of this had to be done simultaneously
2025-01-07 00:00:50 +01:00
94d2ebf1c2 wip: working installer. cleanup/bindir/logo missing 2024-12-24 00:29:23 +01:00
801adbb17e fixed 1px gaps/removed unnecesary com call 2024-12-18 00:44:33 +01:00
5e5365274c changed session module name retrieval + name when wnd not shown 2024-12-17 23:00:44 +01:00
2a1b30e166 fix: sessionmanager life expired under my feet 2024-12-17 00:01:02 +01:00
9f7e7e30e2 ini: fixed insufficient utf8->16 alloc 2024-12-12 20:44:27 +01:00
adca5111f6 finished basic ini skeleton 2024-12-06 19:59:09 +01:00
1ae324b68a wip: settings in effect 2024-12-06 17:55:46 +01:00
8e07b1efdd wip: ini parse baseline 2024-12-03 21:13:42 +01:00
8e93120555 correct size on all resolutions 2024-11-27 20:46:02 +01:00
60890cecad dark/light mode & accent color 2024-11-26 17:11:01 +01:00
13855c2e6f wip: dark/light mode adaptation 2024-11-20 19:21:37 +01:00
0bf8b321ae wip: endpoint add/remove/role change bugfixes 2024-08-15 18:07:13 +02:00
d4db24ed7d wip: no endpoint, role rework, visual ratio 2024-08-14 17:02:57 +02:00
e42dbaa194 session shown name parsing, pending refactor 2024-08-08 15:04:17 +02:00
517f117575 o0 & windownames 2024-08-06 14:49:52 +02:00
42b30b1bf8 Og & wip: names 2024-08-06 09:45:25 +02:00
544da49e32 test: fixed unnamed sessions #6 2024-06-06 20:11:12 +02:00
313a26c8e3 wip: customize scroll bar 2024-06-01 22:28:24 +02:00
f7de5ef803 Qt code refactor w/ functional style 2024-05-30 19:23:09 +02:00
f8171f12f3 wip: bad build config, unnecesary reimpl. Step by step 2024-05-30 18:48:04 +02:00
0e123f886d sliders value set to where clicked 2024-05-16 20:40:19 +02:00
b6b7e1c577 code cleanup: refactored ugly window width/scrheight cache 2024-05-14 19:48:11 +02:00
5d179bb91c changed qmake recipe (static c++/unwind link) 2024-05-14 01:55:29 +02:00
c1665b33e2 removed SVV.exe 2024-05-13 16:27:35 +02:00
6ebe2604e7 base for cruft removal 2024-05-11 22:39:49 +02:00
33f3d8216f change defaults poc 2024-05-10 19:51:48 +02:00
cb320da8cd fixed visual artifact when add/remove sessions while window open 2024-05-08 20:46:17 +02:00
4f3f8b3e56 normalized behaviour click trayIcon w/ wndow open + lim compose call 2024-05-08 19:22:33 +02:00
16604277fb disabled slider scroll 2024-05-07 19:38:17 +02:00
6c588d068f poc recompose 2024-05-07 19:16:24 +02:00
d75cf405f1 new layout every compose 2024-05-07 18:46:24 +02:00
071505d3fe added README/reimpl header 2024-05-06 19:32:22 +02:00
c7d77c30ab wip: slider draw algo 2024-05-02 23:25:06 +02:00
75fdfaa095 wip: peak meter visual ratio 2024-05-02 19:59:41 +02:00
dc8951776f wip: session meter 2024-04-30 23:40:52 +02:00
6bda4702df wip: endpoint meter 2024-04-30 21:30:58 +02:00
20a82b42d4 wip: meter bar 2024-04-28 18:26:44 +02:00
cdadee58fc minor code cleanup 2024-04-27 17:51:29 +02:00
2115cdf508 wip: detected width bug (channels) 2024-04-25 20:21:21 +02:00
46224d331c wip: file log 2024-04-25 15:43:58 +02:00
4756a00156 wip: fixed wrong index for epw creation 2024-04-24 02:03:07 +02:00
1b2ab191ca wip: dynamically updated endpoint name 2024-04-19 18:58:26 +02:00
0880305b23 dynamically updated session name 2024-04-19 13:40:50 +02:00
170d52067b wip: fixed multi monitor 2024-04-18 16:42:28 +02:00
4f637b4397 wip: reintroduced fake show 2024-04-18 13:18:29 +02:00
5b3b50a0d2 wip: fixed role setting 2024-04-17 20:18:29 +02:00
33 changed files with 4253 additions and 732 deletions

1
.gitignore vendored
View file

@ -2,6 +2,7 @@ build
*.rdbg *.rdbg
*.pdb *.pdb
*.ps1 *.ps1
*.exe
Makefile Makefile
Makefile.Debug Makefile.Debug
Makefile.Release Makefile.Release

15
README.md Normal file
View file

@ -0,0 +1,15 @@
# Mixer
## Build
* Toolchain: [llvm-mingw UCRT 20220906](https://github.com/mstorsjo/llvm-mingw/releases/tag/20220906).
* Clone [this](https://code.qt.io/cgit/qt/qt5.git/tag/?h=v6.3.2) Qt branch
* Build Qt from sources following [this](https://wiki.qt.io/Building_Qt_6_from_Git) guide and executing `configure.bat` as such:
```
..\qt6\configure.bat -prefix ..\install -static -debug -opensource -confirm-license -qt-zlib -qt-libpng -qt-webp -qt-libjpeg -qt-freetype -skip qt3d -skip qtactiveqt -skip qtandroidextras -skip qtcharts -skip qtconnectivity -skip qtdatavis3d -skip qtdoc -skip qtgamepad -skip qtlocation -skip qtlottie -skip qtmacextras -skip qtmultimedia -skip qtnetworkauth -skip qtpurchasing -skip qtquick3d -skip qtquick3dphysics -skip qtquicktimeline -skip qtremoteobjects -skip qtscript -skip qtsensors -skip qtwayland -skip qtwebglplugin -skip qtwebview -skip webengine -nomake examples -nomake tests
```
* Clone this repo and execute `bueno.bat`.

View file

@ -1,7 +1,10 @@
<!DOCTYPE RCC><RCC version="1.0"> <!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/"> <qresource prefix="/">
<file>assets/selawk.ttf</file>
<file>assets/notificationAreaIcon.png</file> <file>assets/notificationAreaIcon.png</file>
<file>assets/style.qss</file> <file>assets/style.qss</file>
<file>assets/logo.ico</file> <file>assets/logo.ico</file>
<file>assets/mute.svg</file>
<file>assets/unmute.svg</file>
</qresource> </qresource>
</RCC> </RCC>

Binary file not shown.

BIN
assets/installer.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

BIN
assets/installer.xcf Normal file

Binary file not shown.

BIN
assets/selawk.ttf Normal file

Binary file not shown.

BIN
assets/uninstaller.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

View file

@ -1,5 +1,7 @@
taskkill /F /IM "qtest.exe" taskkill /F /IM "MixerQ.exe"
taskkill /F /IM "MixerQd.exe"
qmake -o build\Makefile .\qtest.pro qmake -o build\Makefile .\qtest.pro
copy /Y /B .\assets\SoundVolumeView.exe .\build\debug REM mingw32-make.exe -C .\build -f Makefile.Release
copy /Y /B .\assets\SoundVolumeView.exe .\build\release mingw32-make.exe -C .\build -f Makefile.Debug
mingw32-make.exe -C .\build -f Makefile REM makensis /DBUILDTYPE=release install\installer.nsi
REM makensis /DBUILDTYPE=debug install\installer.nsi

265
install/installer.nsi Normal file
View file

@ -0,0 +1,265 @@
;Auto versioning-------------------------------
!makensis "/DBUILDTYPE=${BUILDTYPE} version.nsi"
!system "GetVersion.exe"
!include "Version.txt"
;optional cleanup
!delfile "GetVersion.exe"
!delfile "Version.txt"
;Includes--------------------------------
!include "MUI2.nsh"
!include "nsDialogs.nsh"
!include "LogicLib.nsh"
!include "${NSISDIR}\Contrib\Language files\English.nsh"
!include "${NSISDIR}\Contrib\Language files\Spanish.nsh"
;!include "${NSISDIR}\Contrib\Language files\English.nsh"
;Defines----------------------------------
!define MUI_LANGDLL_ALLLANGUAGES
!define MUI_UNICON "..\assets\uninstaller.ico"
!define MUI_ICON "..\assets\installer.ico"
;--------------------------------
;General
;Name and file
!if ${BUILDTYPE} == "release"
Name "MixerQ"
!else
Name "MixerQd"
!endif
OutFile "..\build\bin\MixerQ-installer-${version}.exe"
;Get installation folder from registry if available
;InstallDirRegKey HKCU "Software\Modern UI Test" ""
Unicode True
Var Is_Admin
Var Install_Type
;Request application privileges for UAC. If admin is not available, only user-level install will be available
RequestExecutionLevel highest
;--------------------------------
;Interface Settings
!define MUI_ABORTWARNING
;--------------------------------
;Pages
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "..\LICENSE.txt"
;!insertmacro MULTIUSER_PAGE_INSTALLMODE
Page Custom InstallTargetPage
;!insertmacro MUI_PAGE_COMPONENTS
!define MUI_PAGE_CUSTOMFUNCTION_PRE Skip_Directory_Func
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_WELCOME
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_UNPAGE_FINISH
;--------------------------------
;Languages
!insertmacro MUI_LANGUAGE "English"
!insertmacro MUI_LANGUAGE "Spanish"
!insertmacro MUI_LANGUAGE "SpanishInternational"
;English----------------------------
LangString Header_Title ${LANG_ENGLISH} "Configure Install"
LangString Header_Subtitle ${LANG_ENGLISH} "Customize install settings"
LangString Option_Scope ${LANG_ENGLISH} "Select for whom will $(^Name) be installed: "
LangString Scope_Machine ${LANG_ENGLISH} "All users"
LangString Scope_User ${LANG_ENGLISH} "Current user"
;Spanish/SpanishInternational----------------------------
LangString Header_Title ${LANG_SPANISH} "Configurar instalación"
LangString Header_Subtitle ${LANG_SPANISH} "Elija los ajustes de la instalación"
LangString Option_Scope ${LANG_SPANISH} "$(^Name) será instalado para: "
LangString Scope_Machine ${LANG_SPANISH} "Todos los usuarios"
LangString Scope_User ${LANG_SPANISH} "Usuario actual"
LangString Header_Title ${LANG_SPANISHINTERNATIONAL} "Configurar instalación"
LangString Header_Subtitle ${LANG_SPANISHINTERNATIONAL} "Elija los ajustes de la instalación"
LangString Option_Scope ${LANG_SPANISHINTERNATIONAL} "$(^Name) será instalado para: "
LangString Scope_Machine ${LANG_SPANISHINTERNATIONAL} "Todos los usuarios"
LangString Scope_User ${LANG_SPANISHINTERNATIONAL} "Usuario actual"
;Functions------------------------------
Function Skip_Directory_Func
;StrCmp $Install_Type "user" dontSkip
Abort # skip the page
;dontSkip:
FunctionEnd
!macro ONINIT un
Function ${un}.onInit
; The value of SetShellVarContext detetmines whether SHCTX is HKLM or HKCU
; and whether SMPROGRAMS refers to all users or just the current
!insertmacro MUI_LANGDLL_DISPLAY
UserInfo::GetAccountType
Pop $0
${If} $0 == "Admin"
; If we're an admin, default to installing to C:\Program Files
SetShellVarContext all
; StrCpy $INSTDIR "$PROGRAMFILES64\$(^Name)"
StrCpy $Is_Admin "true"
${Else}
; If we're just a user, default to installing to ~\AppData\Local
SetShellVarContext current
; StrCpy $INSTDIR "$LOCALAPPDATA\$(^Name)"
StrCpy $Is_Admin "false"
${EndIf}
; ${If} $INSTDIR == ""
; ; This only happens in the installer, because the uninstaller already knows INSTDIR
; ReadRegStr $0 SHCTX "Software\${PRODUCT_NAME}" ""
; ${If} $0 != ""
; ; If we're already installed, use the existing directory
; StrCpy $INSTDIR "$0"
; ${Else}
; StrCpy $INSTDIR "$INSTDIR_BASE\${PRODUCT_NAME}"
; ${Endif}
; ${Endif}
FunctionEnd
!macroend
!insertmacro ONINIT ""
!insertmacro ONINIT "un"
;NSDialog InstallTarget Page definition---------------------------------
Function InstallTargetPage
!insertmacro MUI_HEADER_TEXT $(Header_Title) $(Header_Subtitle)
;MessageBox MB_OK "Install type $Install_Type"
;MessageBox MB_OK "Build type ${BUILDTYPE}"
nsDialogs::Create 1018
Pop $0
FindWindow $0 "#32770"
GetDlgItem $1 $0 1 ;next/install button
SendMessage $1 ${WM_SETTEXT} 1 "STR:$(^InstallBtn)"
Pop $0
${NSD_CreateLabel} 0 0 100% 10% $(Option_Scope)
Pop $3
${NSD_CreateFirstRadioButton} 0 12% 40% 6% $(Scope_Machine)
Pop $1
${If} $Is_Admin == "false"
EnableWindow $1 0
StrCpy $INSTDIR "$LOCALAPPDATA\$(^Name)"
${Else}
SendMessage $1 ${BM_CLICK} "" "" ;Set default
StrCpy $INSTDIR "$PROGRAMFILES64\$(^Name)"
${EndIf}
${NSD_OnClick} $1 All_Users_Click
${NSD_CreateAdditionalRadioButton} 0 24% 40% 6% $(Scope_User)
Pop $2
${IfThen} $Is_Admin == "false" ${|} SendMessage $2 ${BM_CLICK} "" "" ${|}
${NSD_OnClick} $2 Current_User_Click
nsDialogs::Show
FunctionEnd
Function All_Users_Click
Pop $0
SetShellVarContext all
StrCpy $INSTDIR "$PROGRAMFILES64\$(^Name)"
StrCpy $Install_Type "machine"
;${NSD_SetText} $0 "machine"
;FindWindow $0 "#32770"
;GetDlgItem $1 $0 1 ;next/install button
;SendMessage $1 ${WM_SETTEXT} 1 "STR:$(^InstallBtn)"
FunctionEnd
Function Current_User_Click
Pop $0
SetShellVarContext current
StrCpy $INSTDIR "$LOCALAPPDATA\$(^Name)"
StrCpy $Install_Type "user"
;${NSD_SetText} $0 "user"
;FindWindow $0 "#32770"
;GetDlgItem $1 $0 1 ;next/install button
;SendMessage $1 ${WM_SETTEXT} 1 "STR:$(^NextBtn)"
FunctionEnd
;Default section----------------------
Section
SetRegView 64
SetOutPath $INSTDIR
!if ${BUILDTYPE} == "release"
File "..\build\bin\MixerQ.exe"
!else
File "..\build\bin\MixerQd.exe"
!endif
File "..\LICENSE.txt"
;Start menu shortcut
createDirectory "$SMPROGRAMS\$(^Name)"
createShortCut "$SMPROGRAMS\$(^Name)\$(^Name).lnk" "$INSTDIR\$(^Name).exe"
createShortCut "$SMPROGRAMS\$(^Name)\Uninstall$(^Name).lnk" "$INSTDIR\Uninstall$(^Name).exe"
;Store installation folder
WriteRegStr SHCTX "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" "DisplayName" "$(^Name)"
WriteRegStr SHCTX "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" "UninstallString" '"$INSTDIR\Uninstall$(^Name).exe"'
WriteRegDWORD SHCTX "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" "NoModify" 1
WriteRegDWORD SHCTX "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" "NoRepair" 1
WriteRegStr SHCTX "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" "$(^Name)" "$INSTDIR\$(^Name)"
;Create uninstaller
WriteUninstaller "$INSTDIR\Uninstall$(^Name).exe"
SectionEnd
;--------------------------------
;Descriptions
; ;Language strings
; LangString DESC_SecDummy ${LANG_ENGLISH} "A test section."
; ;Assign language strings to sections
; !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
; !insertmacro MUI_DESCRIPTION_TEXT ${SecDummy} $(DESC_SecDummy)
; !insertmacro MUI_FUNCTION_DESCRIPTION_END
;--------------------------------
;Uninstaller Section
Section "Uninstall"
SetRegView 64
SetShellVarContext current
DeleteRegValue HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" "$(^Name)"
DeleteRegKey HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)"
Delete "$SMPROGRAMS\$(^Name)\$(^Name).lnk"
Delete "$SMPROGRAMS\$(^Name)\Uninstall$(^Name).lnk"
RMDir "$SMPROGRAMS\$(^Name)"
${If} $Is_Admin == "true"
SetShellVarContext all
DeleteRegValue HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" "$(^Name)"
DeleteRegKey HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)"
Delete "$SMPROGRAMS\$(^Name)\$(^Name).lnk"
Delete "$SMPROGRAMS\$(^Name)\Uninstall$(^Name).lnk"
RMDir "$SMPROGRAMS\$(^Name)"
${EndIf}
Delete "$INSTDIR\$(^Name).exe"
Delete "$INSTDIR\LICENSE.txt"
Delete "$INSTDIR\Uninstall$(^Name).exe"
;!define PRODUCT_UNINST_ROOT_KEY "HKLM"
RMDir "$INSTDIR"
SectionEnd

26
install/version.nsi Normal file
View file

@ -0,0 +1,26 @@
!if ${BUILDTYPE} == "release"
!define File "..\build\bin\MixerQ.exe"
!else
!define File "..\build\bin\MixerQd.exe"
!endif
OutFile "GetVersion.exe"
SilentInstall silent
RequestExecutionLevel user ; don't write $EXEDIR\Version.txt with admin permissions and prevent invoking UAC
Section
## Get file version
GetDllVersion "${File}" $R0 $R1
IntOp $R2 $R0 / 0x00010000
IntOp $R3 $R0 & 0x0000FFFF
IntOp $R4 $R1 / 0x00010000
IntOp $R5 $R1 & 0x0000FFFF
StrCpy $R1 "$R2.$R3.$R4.$R5"
## Write it to a !define for use in main script
FileOpen $R0 "$EXEDIR\Version.txt" w
FileWrite $R0 '!define version "$R1"'
FileClose $R0
SectionEnd

View file

@ -1,18 +1,28 @@
QMAKE_CXXFLAGS += --target=x86_64-w64-mingw32 -g -gcodeview TEMPLATE = app
QMAKE_LFLAGS += --target=x86_64-w64-mingw32 -g -Wl,-pdb= -v QMAKE_CXXFLAGS += --target=x86_64-w64-mingw32 -Werror=return-type
LIBS += -LC:/capybara/libclang/x86_64-w64-mingw32/lib -lWinmm -lodbc32 -lodbccp32 -luuid -loleaut32 -lole32 -lshell32 -ladvapi32 -lcomdlg32 -lwinspool -lgdi32 -luser32 -lkernel32 QMAKE_LFLAGS += --target=x86_64-w64-mingw32 -v
#"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 CONFIG(release, debug|release) {
DEFINES += DEBUG QT_LOGGING_TO_CONSOLE=1 WIN32_LEAN_AND_MEAN TARGET = MixerQ
CONFIG += debug DESTDIR = bin
VERSION = 0.9.0.0
#QMAKE_CXXFLAGS += -O2 < Default. Modifying requires removing.
} else {
TARGET = MixerQd
DESTDIR = bin
VERSION = 0.9.0.1
QMAKE_CXXFLAGS += -g -gcodeview -O0
QMAKE_LFLAGS += -g -Wl,-pdb=
}
QT += widgets network LIBS += -lWinmm -lodbc32 -lodbccp32 -luuid -loleaut32 -lole32 -lshell32 -ladvapi32 -lcomdlg32 -lwinspool -lgdi32 -luser32 -lkernel32 -lpropsys -static -stdlib=libc++ -lunwind
INCLUDEPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\cont" DEFINES += QT_LOGGING_TO_CONSOLE=1 WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0602
DESTPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\cont" DEFINES_DEBUG += DEBUG
VPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\cont"
SOURCES += qtestmain.cpp qtclasses.cpp backlasses.cpp backsessionclasses.cpp contclasses.cpp contsessionclasses.cpp QT += widgets network svg
HEADERS += qtclasses.h backlasses.h backsessionclasses.h contclasses.h contsessionclasses.h global.h debug.h backfuncs.h ipolicyconfig.h msinclude.h INCLUDEPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\back\reimpl" "$$PWD\src\cont"
VPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\back\reimpl" "$$PWD\src\cont"
SOURCES += qtestmain.cpp qtclasses.cpp backlasses.cpp backsessionclasses.cpp contclasses.cpp contsessionclasses.cpp settings.cpp
HEADERS += qtclasses.h backlasses.h backsessionclasses.h contclasses.h contsessionclasses.h global.h debug.h backfuncs.h ipolicyconfig.h msinclude.h meterslider.h qtvisuals.h settings.h
RESOURCES = assets.qrc RESOURCES = assets.qrc
RC_ICONS += assets/logo.ico RC_ICONS += assets/logo.ico
#DESTDIR += "build"

View file

@ -1,5 +1,7 @@
#include <backlasses.h> #include "backlasses.h"
#include <backfuncs.h> #include "backfuncs.h"
using namespace Environment;
EndpointNewSessionCallback::EndpointNewSessionCallback(EndpointHandler* eph){ EndpointNewSessionCallback::EndpointNewSessionCallback(EndpointHandler* eph){
this->eph = eph; this->eph = eph;
@ -39,23 +41,27 @@ HRESULT EndpointNewSessionCallback::QueryInterface(REFIID riid, VOID **ppvInterf
HRESULT EndpointNewSessionCallback::OnSessionCreated(IAudioSessionControl *NewSession) { HRESULT EndpointNewSessionCallback::OnSessionCreated(IAudioSessionControl *NewSession) {
if (eph->getFlow() == Flows::FLOW_CAPTURE) return S_OK; if (eph->getFlow() == Flows::FLOW_CAPTURE) return S_OK;
HRESULT result = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
IAudioSessionControl2* sessionControl; IAudioSessionControl2* sessionControl;
//ISimmpleAudioVolume* sessionVolume; //ISimmpleAudioVolume* sessionVolume;
if (FAILED(NewSession->QueryInterface(__uuidof(IAudioSessionControl2), (void**)&sessionControl))) { log_wdebugcpp(L"no nueva sesion......"); }; if (FAILED(NewSession->QueryInterface(__uuidof(IAudioSessionControl2), (void**)&sessionControl))) { log_wdebugcpp(L"no nueva sesion......"); };
if (sessionControl) { if (sessionControl) {
sessionControl->AddRef();
//sessionControl->QueryInterface(__uuidof(ISimpleAudioVolume), (void**)&sessionVolume);
Session* newSession = new Session(this->eph->getEndpoint(), sessionControl); Session* newSession = new Session(this->eph->getEndpoint(), sessionControl);
eph->addSessionSendFront(newSession);
SessionThreadParams tp = { .eph = this->eph, .session = newSession, .isDelete = false };
wait = true;
std::thread newSessionThread(&EndpointNewSessionCallback::createSessionThread, this, tp);
newSessionThread.detach();
while(wait);
} }
if (result == S_OK)
CoUninitialize();
return S_OK; return S_OK;
} }
void EndpointNewSessionCallback::createSessionThread(SessionThreadParams params) {
params.eph->addSessionSendFront(params.session);
this->wait = false;
}
EndpointVolumeCallback::EndpointVolumeCallback(Endpoint* ep){ EndpointVolumeCallback::EndpointVolumeCallback(Endpoint* ep){
this->ep = ep; this->ep = ep;
} }
@ -94,20 +100,37 @@ HRESULT EndpointVolumeCallback::QueryInterface(REFIID riid, VOID **ppvInterface)
HRESULT EndpointVolumeCallback::OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA pNotify) { HRESULT EndpointVolumeCallback::OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA pNotify) {
if (pNotify == NULL) return E_INVALIDARG; if (pNotify == NULL) return E_INVALIDARG;
AUDIO_VOLUME_NOTIFICATION_DATA paramCopy;
memcpy(&paramCopy, pNotify, sizeof(AUDIO_VOLUME_NOTIFICATION_DATA));
float* channelVolumes = (float*)malloc(pNotify->nChannels * sizeof(float));
for (int i = 0; i < pNotify->nChannels; i++) {
channelVolumes[i] = pNotify->afChannelVolumes[i];
}
wait = true;
std::thread updateVolumeThread(&EndpointVolumeCallback::updateVolumeInfo, this, paramCopy, channelVolumes);
updateVolumeThread.detach();
while(wait);
return S_OK;
}
void EndpointVolumeCallback::updateVolumeInfo(AUDIO_VOLUME_NOTIFICATION_DATA newVolume, float* channelVolumes) {
//delete osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller; //delete osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller;
//osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.freeData4(); //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. //Could've made a function or = override to hide this within Nguid, but back in cont = bad.
osh->handlersPlaybackMutex.lock();
osh->handlersCaptureMutex.lock();
osh->lockEndpoints();
osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.data1 \ osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.data1 \
= pNotify->guidEventContext.Data1; = newVolume.guidEventContext.Data1;
osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.data2 \ osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.data2 \
= pNotify->guidEventContext.Data2; = newVolume.guidEventContext.Data2;
osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.data3 \ osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.data3 \
= pNotify->guidEventContext.Data3; = newVolume.guidEventContext.Data3;
for(int i = 0; i < 8 /* Data4 size */; i++){ for(int i = 0; i < 8 /* Data4 size */; i++){
osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.data4[i] = pNotify->guidEventContext.Data4[i]; osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.data4[i] = newVolume.guidEventContext.Data4[i];
} }
//memcpy(&osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller, &pNotify->guidEventContext,sizeof(NGuid) ); //memcpy(&osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller, &newVolume.guidEventContext,sizeof(NGuid) );
Flows flow = this->ep->getFlow(); Flows flow = this->ep->getFlow();
EndpointHandler* eph = nullptr; EndpointHandler* eph = nullptr;
if (flow & Flows::FLOW_PLAYBACK) { if (flow & Flows::FLOW_PLAYBACK) {
@ -116,30 +139,35 @@ HRESULT EndpointVolumeCallback::OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA pNotify
eph = osh->getCaptureEndpointHandlers().at(this->ep->getIndex()); eph = osh->getCaptureEndpointHandlers().at(this->ep->getIndex());
} }
eph->getCallbackInfo()->muted = pNotify->bMuted; eph->getCallbackInfo()->muted = newVolume.bMuted;
eph->getCallbackInfo()->mainVolume = pNotify->fMasterVolume; eph->getCallbackInfo()->mainVolume = newVolume.fMasterVolume;
eph->getCallbackInfo()->channels = pNotify->nChannels; eph->getCallbackInfo()->channels = newVolume.nChannels;
UINT j = 0; UINT j = 0;
//todo: do while here caused stack corruption; sus //todo: do while here caused stack corruption; sus
while(j < pNotify->nChannels) { while(j < newVolume.nChannels) {
if (flow & Flows::FLOW_PLAYBACK) if (flow & Flows::FLOW_PLAYBACK)
eph->getCallbackInfo()->channelVolumes[j] = pNotify->afChannelVolumes[j]; eph->getCallbackInfo()->channelVolumes[j] = channelVolumes[j];
else else
eph->getCallbackInfo()->channelVolumes[j] = pNotify->afChannelVolumes[j]; eph->getCallbackInfo()->channelVolumes[j] = channelVolumes[j];
j++; j++;
} }
return S_OK; free(channelVolumes);
osh->unlockEndpoints();
osh->handlersPlaybackMutex.unlock();
osh->handlersCaptureMutex.unlock();
wait = false;
}
void EndpointVolumeCallback::reportFinished() {
this->wait = false;
}
EndpointSituationCallback::EndpointSituationCallback(Overseer* os){
this->os = os;
} }
/*
* EndpointSituationCallback::EndpointSituationCallback(IMMDeviceEnumerator *deviceEnumerator, std::vector<Endpoint*> playbackDevices){
* this->deviceEnumerator = deviceEnumerator;
* this->playbackDevices = playbackDevices;
* }
*
*/
ULONG EndpointSituationCallback::AddRef(){ ULONG EndpointSituationCallback::AddRef(){
return InterlockedIncrement(&ref); return InterlockedIncrement(&ref);
@ -174,6 +202,7 @@ HRESULT EndpointSituationCallback::QueryInterface(REFIID riid, VOID **ppvInterfa
HRESULT EndpointSituationCallback::OnDefaultDeviceChanged(EDataFlow flow, ERole role,LPCWSTR pwstrDeviceId) { HRESULT EndpointSituationCallback::OnDefaultDeviceChanged(EDataFlow flow, ERole role,LPCWSTR pwstrDeviceId) {
if (flow == EDataFlow::eCapture) return E_INVALIDARG; if (flow == EDataFlow::eCapture) return E_INVALIDARG;
if (!pwstrDeviceId) return E_INVALIDARG;
Roles nRole; Roles nRole;
switch (role) { switch (role) {
@ -189,8 +218,7 @@ HRESULT EndpointSituationCallback::OnDefaultDeviceChanged(EDataFlow flow, ERole
} }
std::wstring wstringEndpointId = pwstrDeviceId; std::wstring wstringEndpointId = pwstrDeviceId;
log_wdebugcpp(L"we got za defol 4 " + wstringEndpointId); log_wdebugcpp(L"we got za defol 4 " + wstringEndpointId);
osh->changeFrontDefaultsCallback(nRole, wstringEndpointId); osh->roleBucketEntryCallback(nRole, wstringEndpointId);
return S_OK; return S_OK;
} }
@ -206,62 +234,65 @@ HRESULT EndpointSituationCallback::OnDeviceRemoved(LPCWSTR pwstrDeviceId) {
HRESULT EndpointSituationCallback::OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) { HRESULT EndpointSituationCallback::OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) {
std::wstring endpointId = std::wstring(pwstrDeviceId); std::wstring endpointId = std::wstring(pwstrDeviceId);
log_wdebugcpp(L"Endpoint state change for " + endpointId);
EndpointState newState;
switch (dwNewState) { switch (dwNewState) {
case DEVICE_STATE_ACTIVE: case DEVICE_STATE_ACTIVE:
osh->reviseEndpointShowing(endpointId, EndpointState::ENDPOINT_ACTIVE); newState = EndpointState::ENDPOINT_ACTIVE;
break; break;
case DEVICE_STATE_DISABLED: case DEVICE_STATE_DISABLED:
osh->reviseEndpointShowing(endpointId, EndpointState::ENDPOINT_DISABLED); newState = EndpointState::ENDPOINT_DISABLED;
break; break;
case DEVICE_STATE_NOTPRESENT: case DEVICE_STATE_NOTPRESENT:
osh->reviseEndpointShowing(endpointId, EndpointState::ENDPOINT_NOTPRESENT); newState = EndpointState::ENDPOINT_NOTPRESENT;
break; break;
case DEVICE_STATE_UNPLUGGED: case DEVICE_STATE_UNPLUGGED:
osh->reviseEndpointShowing(endpointId, EndpointState::ENDPOINT_UNPLUGGED); newState = EndpointState::ENDPOINT_UNPLUGGED;
break; break;
} }
isEpStateChanging.exchange(true);
std::thread newEndpointThread(&OverseerHandler::reviseEndpointShowing, osh,
endpointId, newState);
newEndpointThread.detach();
while(isEpStateChanging);
return S_OK; return S_OK;
} }
HRESULT EndpointSituationCallback::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) { HRESULT EndpointSituationCallback::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) {
isEpStateChanging.exchange(true);
/* std::thread propertyThread(&Overseer::updateEndpointInfo, os, std::wstring(pwstrDeviceId));
* log_debugcpp(" -->Changed device property " + propertyThread.detach();
* key.fmtid.Data1 + key.fmtid.Data2 + key.fmtid.Data3 + "\n" + while(isEpStateChanging);
* 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; return S_OK;
} }
Endpoint::Endpoint(IMMDevice* ep, uint64_t idx){ void EndpointSituationCallback::reportFinishedStateChange() {
this->isEpStateChanging.exchange(false);
return;
}
Endpoint::Endpoint(IMMDevice* ep, IPolicyConfig7* policyConfig, uint64_t idx) {
this->endpoint = ep; this->endpoint = ep;
this->idx = idx; this->idx = idx;
this->policyConfig = policyConfig;
/* /*
* It can't multiflag, it's that stupid. MS momento. * It can't multiflag, it's that stupid. MS momento.
* Only shows most relevant flag according to MS, i.e. 0110 sends 0010 * 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 DWORD state;
if(FAILED(endpoint->GetState(&this->endpointState))) {exit(-2);}; if(FAILED(endpoint->GetState(&state))) {exit(-2);};
this->endpointState = (EndpointState)state;
if(this->endpointState == EndpointState::ENDPOINT_ACTIVE) { if(this->endpointState == EndpointState::ENDPOINT_ACTIVE) {
activateEndpointVolume(); activateEndpointVolume();
//if(FAILED(endpoint->Activate(__uuidof(IAudioClient),
// CLSCTX_ALL, NULL, (void**)&audioClient))) { log_debugcpp("audioclntbros..."); }
//audioClient->GetDevicePeriod(&defTime, &minTime);
} }
reloadEndpointChannels(); //todo: atexit into exit Gather ID
/* 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; LPWSTR tempString = nullptr;
if (FAILED(endpoint->GetId(&tempString))) { exit(-1); }; if (FAILED(endpoint->GetId(&tempString))) { exit(-1); };
endpointId = std::wstring(tempString); endpointId = std::wstring(tempString);
@ -269,36 +300,56 @@ Endpoint::Endpoint(IMMDevice* ep, uint64_t idx){
CoTaskMemFree(tempString); CoTaskMemFree(tempString);
endpoint->OpenPropertyStore(STGM_READ, &properties); endpoint->OpenPropertyStore(STGM_READ, &properties);
PROPVARIANT pv; this->updateName();
properties->GetValue(PKEY_Device_FriendlyName , &pv);
if (pv.pwszVal == nullptr)
friendlyName = L"Unnamed Not Present Endpoint";
else
friendlyName = std::wstring(pv.pwszVal);
this->setFlow(); this->setFlow();
if (this->flow == Flows::FLOW_PLAYBACK) {
activateEndpointSessions(); reloadEndpointChannels();
} }
void Endpoint::updateName() {
PROPVARIANT pv;
#define store_name(key, propvariant, wstr) do { \
properties->GetValue(key, &propvariant); \
if (pv.pwszVal == nullptr) wstr = L"Unnamed Not Present Endpoint"; \
else wstr = std::wstring(pv.pwszVal); \
} while (0)
store_name(PKEY_Device_FriendlyName, pv, friendlyName);
store_name(PKEY_Device_DeviceDesc, pv, descriptionName);
store_name(PKEY_DeviceInterface_FriendlyName, pv, deviceName);
#undef store_name
log_wdebugcpp(L"Endpoint name: " + friendlyName);
} }
void Endpoint::activateEndpointSessions() { void Endpoint::activateEndpointSessions() {
//sessionManager; if (this->flow != Flows::FLOW_PLAYBACK) {
if (FAILED(endpoint->Activate(__uuidof(IAudioSessionManager2), CLSCTX_ALL, NULL, (void**) &sessionManager))) { log_wdebugcpp(L"sesionbros..."); return; } log_debugcpp("recording. No seshes for u :(");
return;
}
if (!sessionManager) {
if (FAILED(endpoint->Activate(__uuidof(IAudioSessionManager2),
CLSCTX_ALL, NULL, (void**) &sessionManager))) {
log_debugcpp("Couldn't open session manager2, huh");
return;
}
}
IAudioSessionEnumerator* sessionEnumerator = nullptr; IAudioSessionEnumerator* sessionEnumerator = nullptr;
if (FAILED(sessionManager->GetSessionEnumerator(&sessionEnumerator))) { log_wdebugcpp(L"sesEnumeratorBros..."); return; } if (FAILED(sessionManager->GetSessionEnumerator(&sessionEnumerator))) { log_wdebugcpp(L"sesEnumeratorBros..."); exit(-5); return; }
endpointSessions.resize(1, nullptr); endpointSessions.resize(1, nullptr);
int sessionCount; int sessionCount;
sessionEnumerator->GetCount(&sessionCount); sessionEnumerator->GetCount(&sessionCount);
for (int i = 0; i < sessionCount; i++) { for (int i = 0; i < sessionCount; i++) {
IAudioSessionControl* sessionControlTmp; IAudioSessionControl* sessionControlTmp;
sessionEnumerator->GetSession(i, (IAudioSessionControl**)&sessionControlTmp); if (FAILED(sessionEnumerator->GetSession(i, (IAudioSessionControl**)&sessionControlTmp))) {
//todo:: asegurar lo del dynamic_cast exit(-6);
}
IAudioSessionControl2* sessionControl; IAudioSessionControl2* sessionControl;
sessionControlTmp->QueryInterface(__uuidof(IAudioSessionControl2), (void**)&sessionControl); if(FAILED(sessionControlTmp->QueryInterface(__uuidof(IAudioSessionControl2), (void**)&sessionControl))){
sessionControl->AddRef(); exit(-7);
}
sessionControlTmp->Release(); sessionControlTmp->Release();
Session* session = new Session(this, sessionControl, (size_t)i); Session* session = new Session(this, sessionControl, (size_t)i);
if (sessionControl->IsSystemSoundsSession() == S_OK) endpointSessions[0] = session; if (sessionControl->IsSystemSoundsSession() == S_OK) endpointSessions[0] = session;
@ -307,29 +358,39 @@ void Endpoint::activateEndpointSessions() {
sessionEnumerator->Release(); sessionEnumerator->Release();
} }
/*
* void Endpoint::deleteSessionManager() {
* sessionManager->Release();
* sessionManager = nullptr;
* }
*/
void Endpoint::addSession(Session* session) { void Endpoint::addSession(Session* session) {
session->setIndex(this->getSessionCount()); session->setIndex(this->getSessionCount());
endpointSessions.push_back(session); endpointSessions.push_back(session);
} }
void Endpoint::activateEndpointVolume() { void Endpoint::activateEndpointVolume() {
//bool extraThread = false; //If this EP is created after init, COM won't be initialized on the executing thread.
/* HRESULT result = CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
* Forgive me, for MS has sinned, and now I must too.
*/
if (this->endpointVolume == nullptr) { if (this->endpointVolume == nullptr) {
HRESULT result = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); if(FAILED(endpoint->Activate(
endpoint->Activate(IID_IAudioEndpointVolume, CLSCTX_ALL, NULL, (void**)&this->endpointVolume); IID_IAudioEndpointVolume,
//if (endpointVolume == nullptr) { //why they returning 0 after dealing with the error jfc CO_E_NOTINITIALIZED) { CLSCTX_ALL,
//CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE); NULL,
//extraThread = true; (void**)&this->endpointVolume))) {
//goto initialized; log_debugcpp("No volume, huh");
//} }
//log_debugcpp(std::string("no endpointVolume (IAudioEndpointVolume)"));
//todo: check header
if(FAILED(endpoint->Activate(__uuidof(IAudioMeterInformation),
CLSCTX_ALL, NULL, (void**)&endpointPeakMeter))) {
log_debugcpp("peakbros...");
}
}
if (result == S_OK) if (result == S_OK)
CoUninitialize(); CoUninitialize();
} }
}
void Endpoint::reloadEndpointChannels() { void Endpoint::reloadEndpointChannels() {
if (this->endpointState == DEVICE_STATE_ACTIVE) { if (this->endpointState == DEVICE_STATE_ACTIVE) {
@ -353,6 +414,13 @@ std::wstring Endpoint::getId(){
return endpointId; return endpointId;
} }
float Endpoint::getPeakVolume() {
float peakVol;
if(endpointPeakMeter) endpointPeakMeter->GetPeakValue(&peakVol);
else return 0;
return peakVol;
}
float Endpoint::getVolume(int channel){ float Endpoint::getVolume(int channel){
float volume; float volume;
if (channel == AudioChannel::CHANNEL_MAIN) { if (channel == AudioChannel::CHANNEL_MAIN) {
@ -374,7 +442,7 @@ bool Endpoint::getMute(){
return mute; return mute;
} }
void Endpoint::setState(uint8_t state){ void Endpoint::setState(EndpointState state){
this->endpointState = state; this->endpointState = state;
if(state == EndpointState::ENDPOINT_ACTIVE) { if(state == EndpointState::ENDPOINT_ACTIVE) {
this->activateEndpointVolume(); this->activateEndpointVolume();
@ -382,7 +450,7 @@ void Endpoint::setState(uint8_t state){
} }
} }
size_t Endpoint::getState(){ EndpointState Endpoint::getState(){
return this->endpointState; return this->endpointState;
} }
@ -390,9 +458,13 @@ void Endpoint::setVolume(NGuid guid, int channel, float volume) {
//TIP: There used to be log messages here. Now, it's a ghost town. //TIP: There used to be log messages here. Now, it's a ghost town.
GUID tempMsGuid = NGuidToGUID(guid); GUID tempMsGuid = NGuidToGUID(guid);
if (channel == AudioChannel::CHANNEL_MAIN) { if (channel == AudioChannel::CHANNEL_MAIN) {
if(FAILED(endpointVolume->SetMasterVolumeLevelScalar(volume, &tempMsGuid))) {}; if(FAILED(endpointVolume->SetMasterVolumeLevelScalar(volume, &tempMsGuid))) {
log_wdebugcpp(L"Master volume failed for endpoint: " + friendlyName);
};
} else { } else {
if(FAILED(endpointVolume->SetChannelVolumeLevelScalar(channel, volume, &tempMsGuid))) {}; if(FAILED(endpointVolume->SetChannelVolumeLevelScalar(channel, volume, &tempMsGuid))) {
log_wdebugcpp(L"Channel " + std::to_wstring(channel) + L" volume failed for endpoint: " + friendlyName);
};
} }
} }
@ -417,70 +489,29 @@ Roles Endpoint::getRoles(){
} }
void Endpoint::setRoles(Roles role){ void Endpoint::setRoles(Roles role){
//todo: otro exe momento if (!policyConfig) return;
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" "; bool allRoles = false;
std::wstring troublePair = L"0"; ERole val;
switch(role) { 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: case Roles::ROLE_CONSOLE:
command += std::to_wstring(0); val = eConsole;
break; break;
case Roles::ROLE_MULTIMEDIA: case Roles::ROLE_MULTIMEDIA:
command += std::to_wstring(1); val = eMultimedia;
break; break;
case Roles::ROLE_COMMUNICATIONS: case Roles::ROLE_COMMUNICATIONS:
command += std::to_wstring(2); val = eCommunications;
break;
default:
allRoles = true;
break; break;
} }
if (allRoles) {
if(CreateProcessW( policyConfig->SetDefaultEndpoint(endpointId.c_str(), eMultimedia);
NULL, //policyConfig->SetDefaultEndpoint(endpointId.c_str(), eConsole);
(wchar_t*)command.c_str(), policyConfig->SetDefaultEndpoint(endpointId.c_str(), eCommunications);
NULL, } else policyConfig->SetDefaultEndpoint(endpointId.c_str(), val);
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){ void Endpoint::assignRoles(Roles role){
@ -495,24 +526,12 @@ void Endpoint::removeRoles(Roles role){
void Endpoint::setFlow() { void Endpoint::setFlow() {
IMMEndpoint* flowGetter; IMMEndpoint* flowGetter;
//this should be as simple as writing IID_IMMEndpoint, but it just won't find the macro, so I copied it. Sad. if(FAILED(this->endpoint->QueryInterface(__uuidof(IMMEndpoint), (void**)&flowGetter)))
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..."); } { log_debugcpp("no flow..."); }
EDataFlow MSflow; EDataFlow MSflow;
HRESULT vafllar = flowGetter->GetDataFlow(&MSflow); flowGetter->GetDataFlow(&MSflow);
this->flow = (MSflow == EDataFlow::eRender ? Flows::FLOW_PLAYBACK : Flows::FLOW_CAPTURE); this->flow = (MSflow == EDataFlow::eRender ? Flows::FLOW_PLAYBACK : Flows::FLOW_CAPTURE);
log_debugcpp("Endpoint flow: " + std::to_string(flow));
flowGetter->Release(); flowGetter->Release();
} }
@ -526,7 +545,9 @@ std::vector<Session*> Endpoint::getSessions() {
} }
size_t Endpoint::getSessionCount() { size_t Endpoint::getSessionCount() {
return endpointSessions.size(); size_t sessionCount;
sessionCount = endpointSessions.size();
return sessionCount;
} }
void Endpoint::registerNewSessionNotification(EndpointNewSessionCallback* ensc){ void Endpoint::registerNewSessionNotification(EndpointNewSessionCallback* ensc){
@ -537,18 +558,28 @@ void Endpoint::unregisterNewSessionNotification(EndpointNewSessionCallback* ensc
sessionManager->UnregisterSessionNotification(ensc); sessionManager->UnregisterSessionNotification(ensc);
} }
void Endpoint::deleteSessions() {
for (auto session : endpointSessions) {
delete session;
}
endpointSessions.resize(0);
}
Endpoint::~Endpoint(){ Endpoint::~Endpoint(){
//EPs are never deleted.
log_wdebugcpp(L"murio endpoint-san uwu"); log_wdebugcpp(L"murio endpoint-san uwu");
properties->Release(); properties->Release();
endpointVolume->Release(); endpointVolume->Release();
endpoint->Release(); endpoint->Release();
sessionManager->Release(); sessionManager->Release();
for (auto session : endpointSessions) {
delete session;
}
} }
void Overseer::initCOMLibrary() { void Overseer::initCOMLibrary() {
OutputDebugStringW(L"EPWidget creation\n");
if(FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE))) { if(FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE))) {
log_debugcpp("si"); }; log_debugcpp("Not even COM?"); };
//Retrieving endpoint enumerator //Retrieving endpoint enumerator
@ -556,21 +587,23 @@ void Overseer::initCOMLibrary() {
if(FAILED(CoCreateInstance( __uuidof(MMDeviceEnumerator), NULL, if(FAILED(CoCreateInstance( __uuidof(MMDeviceEnumerator), NULL,
CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), CLSCTX_ALL, __uuidof(IMMDeviceEnumerator),
(void**)&deviceEnumerator)) ) (void**)&deviceEnumerator)) )
{ log_debugcpp("si"); }; { log_debugcpp("No MMDeviceEnum. Weird."); };
GUID tempGuid; GUID tempGuid;
if(FAILED(CoCreateGuid(&tempGuid))) { log_debugcpp("Failed to obtain GUID: " ); }; if(FAILED(CoCreateGuid(&tempGuid))) { log_debugcpp("Failed to obtain GUID: " ); };
//todo: wtf? why is it working? floats are ptrs... //todo: wtf? why is it working? floats are ptrs...
this->guid = GUIDToNGuid(&tempGuid); this->guid = GUIDToNGuid(&tempGuid);
//if(FAILED(CoCreateInstance(__uuidof(CPolicyConfigClient), HRESULT hre = CoCreateInstance(__uuidof(CPolicyConfigClient),
// NULL, CLSCTX_ALL, __uuidof(IPolicyConfig10), (LPVOID *)&policyConfig))) {exit(-1);} NULL, CLSCTX_ALL,
__uuidof(IPolicyConfig7), (LPVOID *)&policyConfig);
if (hre != S_OK) exit(-1);
//TODO: Release lpguid? //TODO: Release lpguid?
//TODO: Uninitialize COM //TODO: Uninitialize COM
} }
void Overseer::reloadEndpoints(Flows flow) { void Overseer::createEndpoints(Flows flow) {
IMMDeviceCollection *deviceCollection; IMMDeviceCollection *deviceCollection;
unsigned int numEndpoints; unsigned int numEndpoints;
EDataFlow MSflow = (flow == Flows::FLOW_PLAYBACK ? EDataFlow::eRender : EDataFlow::eCapture); EDataFlow MSflow = (flow == Flows::FLOW_PLAYBACK ? EDataFlow::eRender : EDataFlow::eCapture);
@ -586,13 +619,12 @@ void Overseer::reloadEndpoints(Flows flow) {
if(numEndpoints == 0) { log_debugcpp("si"); }; if(numEndpoints == 0) { log_debugcpp("si"); };
/* /*
* Retrieving actual endpoints and storing them on their own class * Retrieving actual endpoints and storing them on their own collection
*/ */
IMMDevice *temp; IMMDevice *temp;
for (unsigned int i = 0; i < numEndpoints; i++){ for (unsigned int i = 0; i < numEndpoints; i++){
if(deviceCollection->Item(i, &temp) != 0) { log_debugcpp("si"); }; if(deviceCollection->Item(i, &temp) != 0) { log_debugcpp("si"); };
Endpoint *endpoint = new Endpoint(temp, i); Endpoint *endpoint = new Endpoint(temp, policyConfig, i);
if (flow == Flows::FLOW_PLAYBACK) if (flow == Flows::FLOW_PLAYBACK)
this->playbackDevices.push_back(endpoint); this->playbackDevices.push_back(endpoint);
else else
@ -620,6 +652,8 @@ void Overseer::reloadEndpoints(Flows flow) {
break; break;
} }
deviceEnumerator->GetDefaultAudioEndpoint(MSflow, val, &temp); deviceEnumerator->GetDefaultAudioEndpoint(MSflow, val, &temp);
if (!temp) continue;
LPWSTR id = nullptr; LPWSTR id = nullptr;
if (flow == Flows::FLOW_PLAYBACK) { if (flow == Flows::FLOW_PLAYBACK) {
@ -628,8 +662,7 @@ void Overseer::reloadEndpoints(Flows flow) {
temp->GetId(&id); temp->GetId(&id);
int comparison = CompareStringEx(LOCALE_NAME_USER_DEFAULT, 0, eptId.c_str(), -987, id, -987, NULL, NULL, 0); int comparison = CompareStringEx(LOCALE_NAME_USER_DEFAULT, 0, eptId.c_str(), -987, id, -987, NULL, NULL, 0);
if (comparison - 2 == 0) { if (comparison - 2 == 0) {
log_wdebugcpp(L"ola defaul playback de " log_wdebugcpp(L"ola defaul playback de " + std::to_wstring(i) + L" es " + id);
+ std::to_wstring(i) + L" es " + id);
playbackDevices.at(j)->assignRoles((Roles)(1 << i)); playbackDevices.at(j)->assignRoles((Roles)(1 << i));
} }
} }
@ -650,11 +683,17 @@ void Overseer::reloadEndpoints(Flows flow) {
} }
Endpoint* Overseer::addEndpoint(std::wstring endpointId, /* out */Flows* flow = nullptr) { Endpoint* Overseer::addEndpoint(std::wstring endpointId, /* out */Flows* flow = nullptr) {
//This method is only called from the new endpoint callback and its subsequent thread,
//so another STA can be safely instantiated
if(FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE))) {
log_debugcpp("EP Callback Thread failed. Sad!");
return nullptr;
}
IMMDevice* newep; IMMDevice* newep;
if(FAILED(deviceEnumerator->GetDevice((LPCWSTR)endpointId.c_str(), &newep))) if(FAILED(deviceEnumerator->GetDevice((LPCWSTR)endpointId.c_str(), &newep)))
log_debugcpp("ay caramba con la hot metida."); log_debugcpp("ay caramba con la hot metida. Sad!");
Endpoint *endpoint = new Endpoint(newep); Endpoint *endpoint = new Endpoint(newep, policyConfig);
Flows getFlow = endpoint->getFlow(); Flows getFlow = endpoint->getFlow();
if (getFlow == Flows::FLOW_PLAYBACK) { if (getFlow == Flows::FLOW_PLAYBACK) {
@ -665,25 +704,179 @@ Endpoint* Overseer::addEndpoint(std::wstring endpointId, /* out */Flows* flow =
this->captureDevices.push_back(endpoint); this->captureDevices.push_back(endpoint);
} }
if (flow != nullptr) *flow = getFlow; if (flow != nullptr) *flow = getFlow;
CoUninitialize();
return endpoint; return endpoint;
} }
Overseer::Overseer() { //: epsc(deviceEnumerator, playbackDevices){ void Overseer::reportFinishedStateChange() {
//Initializing COM library epsc.reportFinishedStateChange();
}
Overseer::Overseer() : epsc(this) {
log_debugcpp("Initializing Overseer"); log_debugcpp("Initializing Overseer");
//Storing exe path for later use (mainly "Run on startup")
log_debugcpp("-Caching exe path");
uint32_t cPathLen = 0;
wchar_t* cPath = getExeAbsPath(&cPathLen);
exeAbsPath = cPath;
//Detecting install scope
wchar_t *machine = wcsstr(cPath, L"Program Files");
if (!machine)
Environment::scope = HKEY_CURRENT_USER;
else Environment::scope = HKEY_LOCAL_MACHINE;
free(cPath);
//Initializing COM library
log_debugcpp("-Initializing COM");
initCOMLibrary(); initCOMLibrary();
//Obtaining playback endpoint collection on this point in time //Obtaining playback endpoint collection
reloadEndpoints(Flows::FLOW_PLAYBACK); createEndpoints(Flows::FLOW_PLAYBACK);
//reloadEndpoints(Flows::FLOW_CAPTURE); //reloadEndpoints(Flows::FLOW_CAPTURE);
}
//Registering for endpoint information callback NGuid Overseer::getGuid() {
//this->epsc.fill(deviceEnumerator, playbackDevices, captureDevices); return guid;
//this->epsc.fill(deviceEnumerator); }
void Overseer::registerEndpointSituationCallback() {
if(FAILED(deviceEnumerator->RegisterEndpointNotificationCallback(((IMMNotificationClient*)&epsc)))) { log_debugcpp("when no enchufas......"); } if(FAILED(deviceEnumerator->RegisterEndpointNotificationCallback(((IMMNotificationClient*)&epsc)))) { log_debugcpp("when no enchufas......"); }
} }
void Overseer::openControlPanel() { std::vector<Endpoint*> Overseer::getPlaybackEndpoints() {
return playbackDevices;
}
std::vector<Endpoint*> Overseer::getCaptureEndpoints() {
return captureDevices;
}
void Overseer::updateEndpointInfo(std::wstring endpointId) {
//todo: reintroduce capture devices
playbackMutex.lock();
log_wdebugcpp(L"new name Endpoint id: " + endpointId);
for(auto ep : playbackDevices) {
if (ep->getId() == endpointId && ep->getState() == EndpointState::ENDPOINT_ACTIVE) {
ep->updateName();
osh->updateFrontEndpointName(ep);
break;
}
}
playbackMutex.unlock();
epsc.reportFinishedStateChange();
}
Overseer::~Overseer(){
//Overseer is never deleted. This is to annotate what would need to be taken care of.
log_debugcpp("jej");
deviceEnumerator->Release();
for(unsigned long long i = 0; i < playbackDevices.size(); i++){
delete(playbackDevices.at(i));
}
}
wchar_t* Environment::getExeAbsPath(uint32_t *exeAbsPathLength) {
wchar_t *exeAbsPath = (wchar_t*)calloc(UNICODE_STRING_MAX_CHARS, sizeof(wchar_t));
*exeAbsPathLength = GetModuleFileNameW(
NULL,
exeAbsPath,
UNICODE_STRING_MAX_CHARS
);
return exeAbsPath;
}
std::string Environment::createSettingsPath(SettingsTargetDirectory target) {
wchar_t* settingsPath = nullptr;
wchar_t settingsFile[] = L"\\settings.ini";
uint32_t settingsFileLen = (sizeof(settingsFile) / sizeof(wchar_t)) - 1;
wchar_t maxPathBypass[] = L"\\\\?\\";
uint32_t exePathLength = 0;
wchar_t folderPath[] = L"\\" LAPP_NAME;
uint32_t maxPathBypassLen = (sizeof(maxPathBypass)/ sizeof(wchar_t)) - 1;
uint32_t folderPathLen = (sizeof(folderPath) / sizeof(wchar_t)) - 1;
wchar_t* roamingPath = nullptr;
log_wdebugcpp(L"Bypass size: " + std::to_wstring((sizeof(maxPathBypass)/sizeof(maxPathBypass[0]))));
switch(target) {
case HOME_DIR:
{
if(SHGetKnownFolderPath(
FOLDERID_RoamingAppData,
0,
NULL,
&roamingPath)
== S_OK) {
//Retrieve path len
uint32_t pathLen = 0;
wchar_t currentChar = roamingPath[pathLen];
while(currentChar != '\0') {
pathLen++;
currentChar = roamingPath[pathLen];
}
settingsPath = (wchar_t*)calloc(pathLen +
maxPathBypassLen +
folderPathLen +
settingsFileLen,
sizeof(wchar_t));
memcpy(settingsPath, maxPathBypass, sizeof(wchar_t) * maxPathBypassLen);
memcpy(settingsPath + (maxPathBypassLen), roamingPath, sizeof(wchar_t) * pathLen);
CoTaskMemFree(roamingPath);
memcpy(settingsPath + (maxPathBypassLen + pathLen),
folderPath, sizeof(wchar_t) * folderPathLen);
log_wdebugcpp(L"Settings folder path: " + std::wstring(settingsPath));
if(CreateDirectoryW(settingsPath, NULL) || GetLastError() == ERROR_ALREADY_EXISTS) {
memcpy(settingsPath + (maxPathBypassLen + pathLen + folderPathLen),
settingsFile, sizeof(wchar_t) * settingsFileLen);
std::string utf8path = utf16ToUtf8(settingsPath);
free(settingsPath);
return utf8path;
}
}
}
return nullptr;
break;
case APP_PATH:
{
//Executable dir
settingsPath = getExeAbsPath(&exePathLength);
//reverse wcsstr
while(exePathLength >= 0) {
if(settingsPath[exePathLength] == '\\') {
memset(settingsPath + exePathLength,
0,
(UNICODE_STRING_MAX_CHARS - exePathLength) * sizeof(wchar_t));
break;
} else exePathLength--;
}
log_wdebugcpp(L"Exe folder: " + std::wstring(settingsPath));
if((UNICODE_STRING_MAX_CHARS - exePathLength) > (settingsFileLen + 1)) {
memcpy(settingsPath + exePathLength, settingsFile, sizeof(wchar_t) * settingsFileLen);
std::string utf8path = utf16ToUtf8(settingsPath);
free(settingsPath);
return utf8path;
}
}
return nullptr;
break;
default:
return nullptr;
break;
}
return nullptr;
}
void Environment::populateSystemValues() {
updateColors();
Environment::startup = checkStartup(scope);
}
void Environment::openControlPanel() {
STARTUPINFOEXW startupConfig; STARTUPINFOEXW startupConfig;
PROCESS_INFORMATION processInfo; PROCESS_INFORMATION processInfo;
SecureZeroMemory(&startupConfig, sizeof(STARTUPINFOEXW)); SecureZeroMemory(&startupConfig, sizeof(STARTUPINFOEXW));
@ -709,24 +902,146 @@ void Overseer::openControlPanel() {
} }
} }
NGuid Overseer::getGuid() { ProcessedNativeEvent Environment::processTopLevelWindowMessage(void* msg) {
return guid; #ifdef WIN32
MSG *message = static_cast<MSG *>(msg);
switch(message->message) {
case WM_SETTINGCHANGE:
//TODO: This looks like a future pain point. Ex handler would come in clutch
if(message->lParam && !wcscmp(((wchar_t*)message->lParam), L"ImmersiveColorSet"))
return updateColors();
break;
default:
return ProcessedNativeEvent::NONE;
break;
}
return ProcessedNativeEvent::NONE;
//if (message->message != WM_SETTINGCHANGE) {return false;}
#endif
} }
std::vector<Endpoint*> Overseer::getPlaybackEndpoints() { ProcessedNativeEvent Environment::updateColors() {
return playbackDevices; // DwmGetColorizationColor( WM_DWMCOLORIZATIONCOLORCHANGED
DWORD value = 0;
DWORD size = sizeof(DWORD);
LSTATUS result;
//Theme bg color
result = RegGetValueW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", L"AppsUseLightTheme", RRF_RT_REG_DWORD, nullptr, &value, &size);
lightMode = (bool)value;
//Accent color
result = RegGetValueW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\DWM", L"ColorizationColor", RRF_RT_REG_DWORD, nullptr, &value, &size);
if (result == ERROR_SUCCESS) {
accentColor = value;
} else accentColor = 0xffffffff;
return ProcessedNativeEvent::COLORS;
} }
std::vector<Endpoint*> Overseer::getCaptureEndpoints() { bool Environment::checkStartup(HKEY rootKeyFlags) {
return captureDevices; //LSTATUS result;
DWORD typeReturned;
//Checking if app entry exists
if(RegGetValueW(rootKeyFlags, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", LAPP_NAME, RRF_RT_REG_SZ, &typeReturned, nullptr, nullptr) != ERROR_SUCCESS && typeReturned != REG_SZ)
return false;
else return true;
} }
Overseer::~Overseer(){ void Environment::updateStartupConfig(bool onStartup) {
log_debugcpp("cum"); wchar_t regSubKey[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Run\\";
deviceEnumerator->Release();
for(unsigned long long i = 0; i < playbackDevices.size(); i++){ if(!onStartup) {
delete(playbackDevices.at(i)); HKEY runKey;
if (RegOpenKeyExW(
scope,
regSubKey,
0,
KEY_SET_VALUE,
&runKey
)
== ERROR_SUCCESS) {
RegDeleteValueW(runKey, LAPP_NAME);
RegCloseKey(runKey);
} }
} else {
LSTATUS result;
uint32_t cPathLen = 0;
wchar_t* cPath = getExeAbsPath(&cPathLen);
wchar_t* regPath = (wchar_t*)calloc(UNICODE_STRING_MAX_CHARS + 2, sizeof(wchar_t));
//char* v = 0xFF'00'00'00'00'00'00'21;
regPath[0] = L'"';
memcpy(regPath + 1, cPath, sizeof(wchar_t) * cPathLen);
memcpy(regPath + cPathLen + 1, L"\"", sizeof(wchar_t) * 2);
result = RegSetKeyValueW(
scope,
regSubKey,
LAPP_NAME,
REG_SZ,
(void*)regPath,
(cPathLen + 3) * sizeof(wchar_t));
/*
* if (result != ERROR_SUCCESS) {
* wchar_t* error;
* FormatMessageW(
* FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
* nullptr,
* (DWORD)result,
* LANG_USER_DEFAULT,
* error,
* 1,
* nullptr);
* LocalFree(error);
* }
*/
free(cPath);
}
return;
}
void Environment::setStartupConfig(bool onStartup) {
//TODO: Use the cache!!!! lol
uint32_t cPathLen = 0;
wchar_t* cPath = getExeAbsPath(&cPathLen);
wchar_t startupParam[] = L"--change-startup";
uint32_t startupParamLen = (sizeof(startupParam) / sizeof(wchar_t)) - 1;
wchar_t* completeParam = (wchar_t*)calloc(startupParamLen + 3, sizeof(wchar_t));
memcpy(completeParam, startupParam, sizeof(wchar_t) * startupParamLen);
if (onStartup)
memcpy(completeParam + startupParamLen, L" 1", sizeof(wchar_t) * 3);
else
memcpy(completeParam + startupParamLen, L" 0", sizeof(wchar_t) * 3);
if(scope == HKEY_LOCAL_MACHINE) {
ShellExecuteW(
NULL,
L"runas",
cPath,
completeParam,
NULL, // default dir
SW_SHOWNORMAL
);
} else {
Environment::updateStartupConfig(onStartup);
}
free(cPath);
free(completeParam);
return;
}
bool Environment::isLightMode() {
return lightMode;
}
bool Environment::isToRunAtStartup() {
return startup;
}
uint32_t Environment::getAccentColor() {
return accentColor;
} }
//int Overseer::getCaptureEndpoints(std::vector<Endpoint*> *captureEndpoints); //int Overseer::getCaptureEndpoints(std::vector<Endpoint*> *captureEndpoints);

View file

@ -4,14 +4,50 @@
#include "backsessionclasses.h" #include "backsessionclasses.h"
#include "global.h" #include "global.h"
#include "contclasses.h" #include "contclasses.h"
//#include "environment.h"
class EndpointVolumeCallback; class EndpointVolumeCallback;
class Session; class Session;
// Convert a wide UTF16LE string to an UTF8 string
static inline std::string utf16ToUtf8(const wchar_t* wstr) {
if(!wstr || wstr[0] == '\0') return std::string();
int size_needed = WideCharToMultiByte(CP_UTF8,
0,
wstr,
-1,
NULL,
0,
NULL,
NULL);
std::string str(size_needed, 0);
WideCharToMultiByte(CP_UTF8,
0,
wstr,
-1,
&str[0],
size_needed,
NULL,
NULL);
return str;
}
// Convert an UTF8 string to a wide UTF16LE String
/*
* std::wstring utf8_decode(const std::string &str)
* {
* if( str.empty() ) return std::wstring();
* int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
* std::wstring wstrTo( size_needed, 0 );
* MultiByteToWideChar (CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed);
* return wstrTo;
* }
*/
class Endpoint { class Endpoint {
public: public:
Endpoint(IMMDevice* endpoint, uint64_t idx = 0); Endpoint(IMMDevice* endpoint, IPolicyConfig7* policyConfig, uint64_t idx = 0);
//todo: how to forward declare delegate constructors? //todo: how to forward declare delegate constructors?
//Endpoint(IMMDevice* endpoint) : Endpoint(endpoint, 0) {}; //Endpoint(IMMDevice* endpoint) : Endpoint(endpoint, 0) {};
void reloadEndpointChannels(); void reloadEndpointChannels();
@ -22,16 +58,18 @@ class Endpoint {
float getVolume(int channel); float getVolume(int channel);
void setMute(NGuid guid, bool muted); void setMute(NGuid guid, bool muted);
bool getMute(); bool getMute();
void setState(uint8_t state); void setState(EndpointState state);
size_t getState(); EndpointState getState();
Roles getRoles(); Roles getRoles();
void setRoles(Roles role); void setRoles(Roles role);
void assignRoles(Roles role); void assignRoles(Roles role);
void removeRoles(Roles role); void removeRoles(Roles role);
void setFlow(); void setFlow();
Flows getFlow(); Flows getFlow();
float getPeakVolume();
std::wstring getId(); std::wstring getId();
std::wstring getName(); std::wstring getName();
void updateName();
void setVolumeCallback(EndpointVolumeCallback *epc); void setVolumeCallback(EndpointVolumeCallback *epc);
void removeVolumeCallback(EndpointVolumeCallback *epc); void removeVolumeCallback(EndpointVolumeCallback *epc);
@ -42,28 +80,34 @@ class Endpoint {
void addSession(Session* session); void addSession(Session* session);
void registerNewSessionNotification(EndpointNewSessionCallback* ensc); void registerNewSessionNotification(EndpointNewSessionCallback* ensc);
void unregisterNewSessionNotification(EndpointNewSessionCallback* ensc); void unregisterNewSessionNotification(EndpointNewSessionCallback* ensc);
void deleteSessions();
void activateEndpointSessions();
//void deleteSessionManager();
std::mutex endpointSessionsMutex;
~Endpoint(); ~Endpoint();
private: private:
void inline activateEndpointVolume(); void inline activateEndpointVolume();
void inline activateEndpointSessions();
std::vector<Session*> endpointSessions; std::vector<Session*> endpointSessions;
uint32_t channelCount = 0; uint32_t channelCount = 0;
IMMDevice *endpoint; IMMDevice *endpoint;
IAudioSessionManager2 *sessionManager;
Flows flow;
IAudioEndpointVolume *endpointVolume = nullptr; IAudioEndpointVolume *endpointVolume = nullptr;
IPropertyStore *properties; IPropertyStore *properties;
IAudioMeterInformation *endpointPeakMeter = nullptr;
//IAudioClient *audioClient;
int64_t defTime, minTime;
IAudioSessionManager2 *sessionManager = nullptr;
Flows flow;
std::wstring friendlyName; std::wstring friendlyName;
std::wstring descriptionName;
std::wstring deviceName;
std::wstring endpointId; std::wstring endpointId;
unsigned long endpointState; EndpointState endpointState;
Roles endpointRoles = (Roles)0; Roles endpointRoles = (Roles)0;
uint64_t idx; uint64_t idx;
/* Not implemented in llvm-mingw. Sad! //Not implemented in llvm-mingw. Sad! todo: mingw patch
* IAudioMeterInformation *endpointPeakMeter = nullptr; IPolicyConfig7* policyConfig;
*/
}; };
class EndpointVolumeCallback : public IAudioEndpointVolumeCallback { class EndpointVolumeCallback : public IAudioEndpointVolumeCallback {
@ -75,16 +119,19 @@ class EndpointVolumeCallback : public IAudioEndpointVolumeCallback {
ULONG Release(); ULONG Release();
HRESULT QueryInterface(REFIID riid, VOID **ppvInterface); HRESULT QueryInterface(REFIID riid, VOID **ppvInterface);
HRESULT OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA update); HRESULT OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA update);
void updateVolumeInfo(AUDIO_VOLUME_NOTIFICATION_DATA newVolume, float* channelVolumes);
void reportFinished();
//~EndpointVolumeCallback(); //~EndpointVolumeCallback();
private: private:
ULONG ref = 1; ULONG ref = 1;
Endpoint* ep; Endpoint* ep;
std::atomic<bool> wait = false;
}; };
class EndpointSituationCallback : public IMMNotificationClient { class EndpointSituationCallback : public IMMNotificationClient {
public: public:
//EndpointSituationCallback(IMMDeviceEnumerator *deviceEnumerator, std::vector<Endpoint*> playbackDevices, std::vector<Endpoint*> captureDevices); EndpointSituationCallback(Overseer* os);
ULONG AddRef(); ULONG AddRef();
ULONG Release(); ULONG Release();
HRESULT QueryInterface(REFIID riid, VOID **ppvInterface); HRESULT QueryInterface(REFIID riid, VOID **ppvInterface);
@ -93,25 +140,31 @@ class EndpointSituationCallback : public IMMNotificationClient {
HRESULT OnDeviceRemoved(LPCWSTR pwstrDeviceId); HRESULT OnDeviceRemoved(LPCWSTR pwstrDeviceId);
HRESULT OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState); HRESULT OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState);
HRESULT OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key); HRESULT OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key);
void reportFinishedStateChange();
private: private:
ULONG ref = 1; ULONG ref = 1;
//IMMDeviceEnumerator *deviceEnumerator; Overseer* os;
//std::vector<Endpoint*> playbackDevices; std::atomic<bool> isEpStateChanging = false;
//std::vector<Endpoint*> captureDevices;
}; };
class Overseer { class Overseer {
//TODO singleton?
public: public:
Overseer(); Overseer();
void openControlPanel(); void registerEndpointSituationCallback();
NGuid getGuid();
std::vector<Endpoint*> getPlaybackEndpoints(); std::vector<Endpoint*> getPlaybackEndpoints();
std::vector<Endpoint*> getCaptureEndpoints(); std::vector<Endpoint*> getCaptureEndpoints();
void updateEndpointInfo(std::wstring endpointId);
void reloadEndpoints(Flows flow); void createEndpoints(Flows flow);
Endpoint* addEndpoint(std::wstring endpointId, /* out */ Flows* flow); Endpoint* addEndpoint(std::wstring endpointId, /* out */ Flows* flow);
NGuid getGuid();
void reportFinishedStateChange();
std::mutex playbackMutex;
std::mutex captureMutex;
//void setEndpointStatusCallback(); //void setEndpointStatusCallback();
//void setEndpointStatusCallback(); //void setEndpointStatusCallback();
@ -123,28 +176,65 @@ class Overseer {
~Overseer(); ~Overseer();
private: private:
void initCOMLibrary();
NGuid guid; NGuid guid;
IMMDeviceEnumerator *deviceEnumerator; IMMDeviceEnumerator *deviceEnumerator;
EndpointSituationCallback epsc; EndpointSituationCallback epsc;
//IPolicyConfig *policyConfig;
std::vector<Endpoint*> playbackDevices; std::vector<Endpoint*> playbackDevices;
std::vector<Endpoint*> captureDevices; std::vector<Endpoint*> captureDevices;
void initCOMLibrary(); IPolicyConfig7* policyConfig;
friend class Endpoint;
//IMMDeviceCollection *deviceCollection; //IMMDeviceCollection *deviceCollection;
//int numCaptureEndpoints; //int numCaptureEndpoints;
//std::vector<Endpoint*> *captureDevices; //std::vector<Endpoint*> *captureDevices;
}; };
class EndpointNewSessionCallback : public IAudioSessionNotification { class EndpointNewSessionCallback : public IAudioSessionNotification {
private:
struct SessionThreadParams;
public: public:
EndpointNewSessionCallback(EndpointHandler *eph); EndpointNewSessionCallback(EndpointHandler *eph);
ULONG AddRef(); ULONG AddRef();
ULONG Release(); ULONG Release();
HRESULT QueryInterface(REFIID riid, VOID **ppvInterface); HRESULT QueryInterface(REFIID riid, VOID **ppvInterface);
HRESULT OnSessionCreated(IAudioSessionControl *NewSession); HRESULT OnSessionCreated(IAudioSessionControl *NewSession);
void createSessionThread(SessionThreadParams params);
private: private:
std::atomic<bool> wait = false;
ULONG ref = 1; ULONG ref = 1;
EndpointHandler *eph; EndpointHandler *eph;
struct SessionThreadParams {
EndpointHandler *eph;
Session *session;
bool isDelete;
};
};
namespace Environment {
wchar_t* getExeAbsPath(uint32_t *exeAbsPathLength);
std::string createSettingsPath(SettingsTargetDirectory target);
void populateSystemValues();
void openControlPanel();
ProcessedNativeEvent processTopLevelWindowMessage(void* msg);
ProcessedNativeEvent updateColors();
bool checkStartup(HKEY rootKeyFlags);
void updateStartupConfig(bool onStartup);
void setStartupConfig(bool onStartup);
bool isLightMode();
bool isToRunAtStartup();
uint32_t getAccentColor();
//todo: binary path cache unused
static std::wstring exeAbsPath;
static uint32_t exeAbsPathLen;
static bool lightMode;
static bool startup = false;
static HKEY scope;
static uint32_t accentColor;
}; };

View file

@ -37,6 +37,11 @@ HRESULT SessionStateCallback::QueryInterface(REFIID riid, VOID **ppvInterface) {
} }
HRESULT SessionStateCallback::OnDisplayNameChanged(LPCWSTR NewDisplayName, LPCGUID EventContext) { HRESULT SessionStateCallback::OnDisplayNameChanged(LPCWSTR NewDisplayName, LPCGUID EventContext) {
//TODO: Preguntar
while(sh->getVolumeInfo()->isNameChanged == true);
sh->setName(std::wstring(NewDisplayName));
sh->getVolumeInfo()->isNameChanged = true;
return S_OK; return S_OK;
} }
@ -89,8 +94,10 @@ HRESULT SessionStateCallback::OnStateChanged(AudioSessionState NewState) {
} }
HRESULT SessionStateCallback::OnSessionDisconnected(AudioSessionDisconnectReason DisconnectReason) { HRESULT SessionStateCallback::OnSessionDisconnected(AudioSessionDisconnectReason DisconnectReason) {
if (DisconnectReason != DisconnectReasonDeviceRemoval) {
sh->setState(SessionState::DISCONNECTED); sh->setState(SessionState::DISCONNECTED);
sh->reviseSessionShowing(SessionState::DISCONNECTED); sh->reviseSessionShowing(SessionState::DISCONNECTED);
}
return S_OK; return S_OK;
} }
@ -98,6 +105,9 @@ Session::Session(Endpoint* ep, IAudioSessionControl2* sessionControl, size_t idx
this->ep = ep; this->ep = ep;
this->sessionControl = sessionControl; this->sessionControl = sessionControl;
this->idx = idx; this->idx = idx;
//https://matthewvaneerde.wordpress.com/2012/06/08/getting-audio-peak-meter-values-for-all-active-audio-sessions/
if (FAILED(sessionControl->QueryInterface(__uuidof(IAudioMeterInformation), (void**)&meterInformation))) { log_wdebugcpp(L"sPeakbros......"); };
//meterInformation = (IAudioMeterInformation*)sessionControl;
AudioSessionState msState; AudioSessionState msState;
sessionControl->GetState(&msState); sessionControl->GetState(&msState);
@ -121,10 +131,19 @@ Session::Session(Endpoint* ep, IAudioSessionControl2* sessionControl, size_t idx
else { else {
LPWSTR sessionDisplayName; LPWSTR sessionDisplayName;
this->sessionControl->GetDisplayName(&sessionDisplayName); this->sessionControl->GetDisplayName(&sessionDisplayName);
if (!wcscmp(sessionDisplayName, L"")) if (!wcscmp(sessionDisplayName, L"")) {
this->sessionName = this->fetchProcessName(pid); std::wstring exePath;
else if (getExePath(pid, &exePath)) {
this->sessionName = exePath;
if (fetchName(exePath, pid)) goto nameFound;
}
if (fetchNameViaWindowName(pid, &this->sessionName)) goto nameFound;
} else {
this->sessionName = std::wstring(sessionDisplayName); this->sessionName = std::wstring(sessionDisplayName);
goto nameFound;
}
nameFound:
CoTaskMemFree(sessionDisplayName); CoTaskMemFree(sessionDisplayName);
} }
} }
@ -140,6 +159,13 @@ float Session::getVolume(int channel){
return volume; return volume;
} }
float Session::getPeakVolume() {
float peakVol;
if(meterInformation) meterInformation->GetPeakValue(&peakVol);
else return 0;
return peakVol;
}
/* /*
* uint32_t Endpoint::getChannelCount(){ * uint32_t Endpoint::getChannelCount(){
* return (uint32_t)channelCount; * return (uint32_t)channelCount;
@ -150,6 +176,10 @@ std::wstring Session::getName() {
return sessionName; return sessionName;
} }
void Session::setName(std::wstring newName) {
this->sessionName = newName;
}
bool Session::getMute() { bool Session::getMute() {
BOOL mut; BOOL mut;
if(FAILED(sessionVolume->GetMute(&mut))) { /* TIP: Below */ } if(FAILED(sessionVolume->GetMute(&mut))) { /* TIP: Below */ }
@ -176,87 +206,196 @@ void Session::setMute(NGuid guid, bool muted) {
if(FAILED(sessionVolume->SetMute(muted, &tempMsGuid))) { log_wdebugcpp(std::wstring(L"SessionVolume null?")); }; if(FAILED(sessionVolume->SetMute(muted, &tempMsGuid))) { log_wdebugcpp(std::wstring(L"SessionVolume null?")); };
} }
std::wstring Session::fetchProcessName(DWORD pid) { bool Session::getExePath(DWORD pid, std::wstring *exePath) {
/* //std::wstring msixName;
* https://learn.microsoft.com/en-us/windows/win32/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot HANDLE processHandle;
* https://stackoverflow.com/questions/11843368/how-to-get-process-description wchar_t fileName[UNICODE_STRING_MAX_CHARS];
*/ processHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
if (processHandle != NULL) {
/* Executable path retrieval */ if (GetModuleFileNameEx(processHandle, NULL, fileName, UNICODE_STRING_MAX_CHARS) == 0) {
std::wstring exePath = L""; CloseHandle(processHandle);
return false;
HANDLE processList = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid); }
if (processList == INVALID_HANDLE_VALUE) { } else {
log_wdebugcpp(L"aye no procname."); log_wdebugcpp(L"aye no procname. -> " + std::to_wstring(GetLastError()));
return exePath; return false;
} }
MODULEENTRY32W me32w; *exePath = std::wstring(fileName);
me32w.dwSize = sizeof(MODULEENTRY32W); return true;
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 */ bool Session::fetchNameViaFD(std::wstring exePath, DWORD pid, std::wstring *sessionName) {
/* File description retrieval: size and available lang-codepages */
struct LANGANDCODEPAGE { struct LANGANDCODEPAGE {
WORD wLanguage; WORD wLanguage;
WORD wCodePage; WORD wCodePage;
} *translationArray; } *translationArray;
DWORD filler; DWORD filler;
DWORD fileVersionInfoSize = GetFileVersionInfoSizeW(exePath.c_str(), &filler); DWORD fileVersionInfoSize = GetFileVersionInfoSizeExW
if (!fileVersionInfoSize) return exePath; (FILE_VER_GET_LOCALISED | FILE_VER_GET_NEUTRAL, exePath.c_str(), &filler);
if (!fileVersionInfoSize) return false;
void* fileVersionInfo = malloc(fileVersionInfoSize); void* fileVersionInfo = malloc(fileVersionInfoSize);
if(!GetFileVersionInfoW(exePath.c_str(),0,fileVersionInfoSize, fileVersionInfo)) if(!GetFileVersionInfoExW(FILE_VER_GET_LOCALISED | FILE_VER_GET_NEUTRAL,
return exePath; exePath.c_str(),0,fileVersionInfoSize, fileVersionInfo)) {
return false;
}
UINT translationArrayLen = 0; UINT translationArrayLen = 0;
if (!VerQueryValueW(fileVersionInfo, L"\\VarFileInfo\\Translation", (LPVOID*)&translationArray, &translationArrayLen)) if (!VerQueryValueW(fileVersionInfo, L"\\VarFileInfo\\Translation", (LPVOID*)&translationArray, &translationArrayLen)) {
return exePath; free(fileVersionInfo);
return false;
}
//File descriptor parsing
//TODO: https://learn.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-getuserpreferreduilanguages
/* It is possible to retrieve user languages and try to use one of those before falling back to whatever
* is available. Also possible to hardcode en-US or any other lang-codepage combo. When an actual translation
* sysem is put in place, I'll come finish this up.
*/
uint64_t availableLangs = (translationArrayLen / sizeof(LANGANDCODEPAGE));
if (!availableLangs) { free(fileVersionInfo); return false; }
bool match = false; int8_t syslangIdx = -1;
for (UINT i = 0; i < (translationArrayLen / sizeof(LANGANDCODEPAGE)); i++) { wchar_t metadataStringKey[256];
wchar_t fileDescriptionKey[256]; wchar_t* metadataString = NULL;
for (UINT i = 0; i < availableLangs; i++) {
LANGID defaultUILanguage = GetUserDefaultUILanguage(); LANGID defaultUILanguage = GetUserDefaultUILanguage();
if (defaultUILanguage != translationArray[i].wLanguage) if (defaultUILanguage != translationArray[i].wLanguage)
continue; continue;
match = true; syslangIdx = i;
wchar_t* fileDescription = NULL; break;
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);
}
} }
UINT metadataStringSize = 0;
swprintf(metadataStringKey, L"\\StringFileInfo\\%04x%04x\\FileDescription",
translationArray[(syslangIdx < 0 ? 0 : syslangIdx)].wLanguage,
translationArray[(syslangIdx < 0 ? 0 : syslangIdx)].wCodePage);
if (VerQueryValueW(fileVersionInfo, metadataStringKey, (LPVOID*)&metadataString, &metadataStringSize)
&& metadataString[0] != '\0') {
free(fileVersionInfo); free(fileVersionInfo);
return exePath; *sessionName = std::wstring(metadataString);
return true;
}
swprintf(metadataStringKey, L"\\StringFileInfo\\%04x%04x\\ProductName",
translationArray[(syslangIdx < 0 ? 0 : syslangIdx)].wLanguage,
translationArray[(syslangIdx < 0 ? 0 : syslangIdx)].wCodePage);
if (VerQueryValueW(fileVersionInfo, metadataStringKey, (LPVOID*)&metadataString, &metadataStringSize)
&& metadataString[0] != '\0') {
free(fileVersionInfo);
*sessionName = std::wstring(metadataString);
return true;
}
if(fileVersionInfo)
free(fileVersionInfo);
return false;
}
bool Session::fetchNameViaMSIX(std::wstring exePath, DWORD pid, std::wstring *sessionName) {
HANDLE process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
if(!process) return false;
//constant missing in mingw64. TB removed when I upgrade to a mingw64 ver that has it
#define APPLICATION_USER_MODEL_ID_MAX_LENGTH 130
uint32_t length = APPLICATION_USER_MODEL_ID_MAX_LENGTH;
PWSTR userModelId = (PWSTR)malloc(length * sizeof(wchar_t));
if(GetApplicationUserModelId(process, &length, userModelId) != ERROR_SUCCESS) {
CloseHandle(process);
return false;
}
CloseHandle(process);
static constexpr wchar_t* prefix = L"shell:appsfolder\\";
uint32_t prefixLen = wcslen(prefix);
uint32_t userModelIdLen = wcslen(userModelId);
wchar_t* fullName;
fullName = (prefixLen + userModelIdLen < length)
? (wchar_t*)malloc(length * sizeof(wchar_t))
: (wchar_t*)malloc((length * 2) * sizeof(wchar_t));
for (int32_t i = prefixLen - 1; i >= 0; i--) {
fullName[i] = prefix[i];
}
for (uint32_t i = 0; i < userModelIdLen + 1; i++) {
fullName[prefixLen + i] = userModelId[i];
}
IShellItem* si;
HRESULT hr = SHCreateItemFromParsingName(fullName,
nullptr,
IID_IShellItem,
(void**)&si
);
free(fullName);
LPWSTR humanName = nullptr;
si->GetDisplayName(SIGDN_NORMALDISPLAY, &humanName);
if(humanName && humanName[0] != '\0') {
*sessionName = std::wstring(humanName);
CoTaskMemFree(humanName);
}
if(si) si->Release();
if (sessionName->length() > 0)
return true;
else return false;
}
bool Session::fetchNameViaWindowName(DWORD pid, std::wstring *sessionName) {
//lParam is documented as in, so... Beware of future explosions, ig?
std::pair<HWND, DWORD> params = { 0, pid };
BOOL result = EnumWindows([](HWND hwnd, LPARAM lParam) -> BOOL {
auto pParams = (std::pair<HWND, DWORD>*)(lParam);
DWORD processId;
//IsWindowVisible(hwnd) &&&& GetWindow(hwnd, GW_OWNER) == 0
if ( GetWindowThreadProcessId(hwnd, &processId) && processId == pParams->second) {
int length = GetWindowTextLength(hwnd);
if (!length) return TRUE;
// Stop enumerating
SetLastError(-1);
pParams->first = hwnd;
return FALSE;
}
// Continue enumerating
return TRUE;
} , (LPARAM)&params);
if(!result && GetLastError() == -1 && params.first) {
//todo: double-dipping length... Not a fan
int length = GetWindowTextLength(params.first);
wchar_t* buffer = new wchar_t[length + 1];
GetWindowTextW(params.first, buffer, length + 1);
*sessionName = buffer;
delete[] buffer;
return true;
}
return false;
}
bool Session::fetchName(std::wstring exePath, DWORD pid) {
/*
* if(fetchNameViaWindowName(exePath, pid, &this->sessionName))
* return;
* else if(fetchNameViaMSIX(exePath, pid, &this->sessionName))
* return;
* else if(!fetchNameViaFD(exePath, pid, &this->sessionName))
* this->sessionName = exePath;
*/
if(fetchNameViaFD(exePath, pid, &this->sessionName))
return true;
return fetchNameViaMSIX(exePath, pid, &this->sessionName);
//else if(!fetchNameViaWindowName(exePath, pid, &this->sessionName))
/// this->sessionName = exePath;
} }
//todo: conflicting names. change callback name //todo: conflicting names. change callback name
@ -277,6 +416,10 @@ void Session::removeStateCallback(SessionStateCallback *ssc){
} }
Session::~Session() { Session::~Session() {
meterInformation->Release();
sessionControl->Release(); sessionControl->Release();
sessionVolume->Release(); sessionVolume->Release();
meterInformation = nullptr;
sessionControl = nullptr;
sessionVolume = nullptr;
} }

View file

@ -33,24 +33,31 @@ class Session {
Session(Endpoint* ep, IAudioSessionControl2* sessionControl) : Session(ep, sessionControl, SIZE_MAX) {}; Session(Endpoint* ep, IAudioSessionControl2* sessionControl) : Session(ep, sessionControl, SIZE_MAX) {};
void setVolume(NGuid guid, int channel, float volume); void setVolume(NGuid guid, int channel, float volume);
float getVolume(int channel); float getVolume(int channel);
float getPeakVolume();
void setMute(NGuid guid, bool muted); void setMute(NGuid guid, bool muted);
bool getMute(); bool getMute();
SessionState getState(); SessionState getState();
void setState(SessionState state); void setState(SessionState state);
void setIndex(size_t idx); void setIndex(size_t idx);
std::wstring getName(); std::wstring getName();
void setName(std::wstring newName);
void setStateCallback(SessionStateCallback *ssc); void setStateCallback(SessionStateCallback *ssc);
void removeStateCallback(SessionStateCallback *ssc); void removeStateCallback(SessionStateCallback *ssc);
~Session(); ~Session();
//uint32_t getChannelCount(); //uint32_t getChannelCount();
private: private:
std::wstring fetchProcessName(DWORD pid); bool getExePath(DWORD pid, /*out*/ std::wstring *exePath);
bool fetchName(std::wstring exePath, DWORD pid);
bool fetchNameViaFD(std::wstring exePath, DWORD pid, /*out*/ std::wstring *sessionName);
bool fetchNameViaMSIX(std::wstring exePath, DWORD pid, /*out*/ std::wstring *sessionName);
bool fetchNameViaWindowName(DWORD pid, /*out*/ std::wstring *sessionName);
/* std::wstring fetchProcessName(DWORD pid); */
std::wstring sessionName; std::wstring sessionName;
SessionState sessionState; SessionState sessionState;
Endpoint* ep; Endpoint* ep;
IAudioSessionControl2* sessionControl = nullptr; IAudioSessionControl2* sessionControl = nullptr;
IAudioMeterInformation* meterInformation = nullptr;
ISimpleAudioVolume* sessionVolume = nullptr; ISimpleAudioVolume* sessionVolume = nullptr;
size_t idx; size_t idx;
}; };

View file

@ -1,47 +1,42 @@
// ----------------------------------------------------------------------------
// PolicyConfig.h
// Undocumented COM-interface IPolicyConfig.
// Use for set default audio render endpoint
// @author EreTIk
// ----------------------------------------------------------------------------
#pragma once #pragma once
#ifndef __IPolicyConfig7_FWD_DEFINED__
#define __IPolicyConfig7_FWD_DEFINED__
typedef interface IPolicyConfig7 IPolicyConfig7;
#ifdef __cplusplus
interface IPolicyConfig7;
#endif
#endif /* __IPolicyConfig7_FWD_DEFINED__ */
interface DECLSPEC_UUID("CA286FC3-91FD-42C3-8E9B-CAAFA66242E3") #ifndef __CPolicyConfigClient_FWD_DEFINED__
IPolicyConfig10; #define __CPolicyConfigClient_FWD_DEFINED__
typedef class CPolicyConfigClient CPolicyConfigClient;
#endif /* __CPolicyConfigClient_FWD_DEFINED__ */
interface DECLSPEC_UUID("00000000-0000-0000-C000-000000000046") /*****************************************************************************
IPolicyConfig10_1; * CPolicyConfigClient coclass
*/
interface DECLSPEC_UUID("F8679F50-850A-41CF-9C72-430F290290C8") DEFINE_GUID(CLSID_CPolicyConfigClient, 0x870af99c, 0x171d, 0x4f9e, 0xaf,0x0d, 0xe6,0x3d,0xf4,0x0c,0x2b,0xc9);
IPolicyConfig7; #ifdef __cplusplus
class DECLSPEC_UUID("870af99c-171d-4f9e-af0d-e63df40c2bc9") CPolicyConfigClient;
#ifdef __CRT_UUID_DECL
__CRT_UUID_DECL(CPolicyConfigClient, 0x870af99c, 0x171d, 0x4f9e, 0xaf,0x0d, 0xe6,0x3d,0xf4,0x0c,0x2b,0xc9)
#endif
#endif
/* interface DECLSPEC_UUID("568B9108-44BF-40B4-9006-86AFE5B5A620") */
/* IPolicyConfigVista; */
interface DECLSPEC_UUID("f8679f50-850a-41cf-9c72-430f290290c8") /*****************************************************************************
IPolicyConfig; * IPolicyConfig7 interface
class DECLSPEC_UUID("870af99c-171d-4f9e-af0d-e63df40c2bc9") */
CPolicyConfigClient; #ifndef __IPolicyConfig7_INTERFACE_DEFINED__
// ---------------------------------------------------------------------------- #define __IPolicyConfig7_INTERFACE_DEFINED__
// class CPolicyConfigClient DEFINE_GUID(IID_IPolicyConfig7, 0xf8679f50, 0x850a, 0x41cf, 0x9c,0x72, 0x43,0x0f,0x29,0x02,0x90,0xc8);
// {870af99c-171d-4f9e-af0d-e63df40c2bc9} #if defined(__cplusplus) && !defined(CINTERFACE)
// MIDL_INTERFACE("f8679f50-850a-41cf-9c72-430f290290c8")
// interface IPolicyConfig IPolicyConfig7 : public IUnknown {
// {f8679f50-850a-41cf-9c72-430f290290c8}
//
// Query interface:
// CComPtr<IPolicyConfig> PolicyConfig;
// PolicyConfig.CoCreateInstance(__uuidof(CPolicyConfigClient));
//
// @compatible: Windows 7 and Later
// ----------------------------------------------------------------------------
interface IPolicyConfig : public IUnknown
{
public: public:
virtual HRESULT GetMixFormat( virtual HRESULT GetMixFormat(
PCWSTR, PCWSTR,
WAVEFORMATEX ** WAVEFORMATEX **
@ -107,32 +102,69 @@ public:
INT INT
); );
}; };
#ifdef __CRT_UUID_DECL
__CRT_UUID_DECL(IPolicyConfig7, 0xf8679f50, 0x850a, 0x41cf, 0x9c,0x72, 0x43,0x0f,0x29,0x02,0x90,0xc8)
#endif
#endif
#endif /* __IPolicyConfig7_INTERFACE_DEFINED__ */
/* __CRT_UUID_DECL(IPolicyConfig10, 0xca286fc3, 0x91fd, 0x42c3, 0x8e,0x9b, 0xca,0xaf,0xa6,0x62,0x42,0xe3) */
/* __CRT_UUID_DECL(CPolicyConfigClient, 0x870af99c, 0x171d, 0x4f9e, 0xaf,0x0d, 0xe6,0x3d,0xf4,0x0c,0x2b,0xc9) */
/* interface DECLSPEC_UUID("568b9108-44bf-40b4-9006-86afe5b5a620") */
/* IPolicyConfigVista; */
/* class DECLSPEC_UUID("294935CE-F637-4E7C-A41B-AB255460B862") */
/* CPolicyConfigVistaClient; */
/* // ---------------------------------------------------------------------------- */ /* // ---------------------------------------------------------------------------- */
/* // class CPolicyConfigVistaClient */ /* // PolicyConfig.h */
/* // {294935CE-F637-4E7C-A41B-AB255460B862} */ /* // 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 IPolicyConfigVista */ /* // interface IPolicyConfig */
/* // {568b9108-44bf-40b4-9006-86afe5b5a620} */ /* // {f8679f50-850a-41cf-9c72-430f290290c8} */
/* // */ /* // */
/* // Query interface: */ /* // Query interface: */
/* // CComPtr<IPolicyConfigVista> PolicyConfig; */ /* // CComPtr<IPolicyConfig> PolicyConfig; */
/* // PolicyConfig.CoCreateInstance(__uuidof(CPolicyConfigVistaClient)); */ /* // PolicyConfig.CoCreateInstance(__uuidof(CPolicyConfigClient)); */
/* // */ /* // */
/* // @compatible: Windows Vista and Later */ /* // @compatible: Windows 7 and Later */
/* // ---------------------------------------------------------------------------- */ /* // ---------------------------------------------------------------------------- */
/* interface IPolicyConfigVista : public IUnknown */ /* interface IPolicyConfig : public IUnknown */
/* { */ /* { */
/* public: */ /* public: */
/* virtual HRESULT GetMixFormat( */ /* virtual HRESULT GetMixFormat( */
/* PCWSTR, */ /* PCWSTR, */
/* WAVEFORMATEX ** */ /* WAVEFORMATEX ** */
/* ); // not available on Windows 7, use method from IPolicyConfig */ /* ); */
/* virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat( */ /* virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat( */
/* PCWSTR, */ /* PCWSTR, */
@ -140,6 +172,10 @@ public:
/* WAVEFORMATEX ** */ /* WAVEFORMATEX ** */
/* ); */ /* ); */
/* virtual HRESULT STDMETHODCALLTYPE ResetDeviceFormat( */
/* PCWSTR */
/* ); */
/* virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat( */ /* virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat( */
/* PCWSTR, */ /* PCWSTR, */
/* WAVEFORMATEX *, */ /* WAVEFORMATEX *, */
@ -151,22 +187,22 @@ public:
/* INT, */ /* INT, */
/* PINT64, */ /* PINT64, */
/* PINT64 */ /* PINT64 */
/* ); // not available on Windows 7, use method from IPolicyConfig */ /* ); */
/* virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod( */ /* virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod( */
/* PCWSTR, */ /* PCWSTR, */
/* PINT64 */ /* PINT64 */
/* ); // not available on Windows 7, use method from IPolicyConfig */ /* ); */
/* virtual HRESULT STDMETHODCALLTYPE GetShareMode( */ /* virtual HRESULT STDMETHODCALLTYPE GetShareMode( */
/* PCWSTR, */ /* PCWSTR, */
/* struct DeviceShareMode * */ /* struct DeviceShareMode * */
/* ); // not available on Windows 7, use method from IPolicyConfig */ /* ); */
/* virtual HRESULT STDMETHODCALLTYPE SetShareMode( */ /* virtual HRESULT STDMETHODCALLTYPE SetShareMode( */
/* PCWSTR, */ /* PCWSTR, */
/* struct DeviceShareMode * */ /* struct DeviceShareMode * */
/* ); // not available on Windows 7, use method from IPolicyConfig */ /* ); */
/* virtual HRESULT STDMETHODCALLTYPE GetPropertyValue( */ /* virtual HRESULT STDMETHODCALLTYPE GetPropertyValue( */
/* PCWSTR, */ /* PCWSTR, */
@ -181,12 +217,95 @@ public:
/* ); */ /* ); */
/* virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint( */ /* virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint( */
/* __in PCWSTR wszDeviceId, */ /* PCWSTR wszDeviceId, */
/* __in ERole eRole */ /* ERole eRole */
/* ); */ /* ); */
/* virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility( */ /* virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility( */
/* PCWSTR, */ /* PCWSTR, */
/* INT */ /* INT */
/* ); // not available on Windows 7, use method from IPolicyConfig */ /* ); */
/* }; */ /* }; */
/* /\* 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<IPolicyConfigVista> 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 *\/ */
/* /\* }; *\/ */

View file

@ -1,18 +1,23 @@
#pragma once #pragma once
#define _WIN32_WINNT 0x0A00
#include <sdkddkver.h> #include <sdkddkver.h>
//done by qt by def #define UNICODE //done by qt by def #define UNICODE
#include <Windows.h> #include <Windows.h>
#include <shellapi.h> #include <shellapi.h>
#include <Shobjidl.h>
#include <Shlobj.h>
#include <fileapi.h>
#include <appmodel.h>
#include <processthreadsapi.h> #include <processthreadsapi.h>
#include <mmdeviceapi.h> #include <mmdeviceapi.h>
#include <combaseapi.h> #include <combaseapi.h>
#include <initguid.h> #include <initguid.h>
#include <Propidl.h> #include <Propidl.h>
#include <propsys.h>
#include <functiondiscoverykeys_devpkey.h> #include <functiondiscoverykeys_devpkey.h>
#include <psapi.h>
//#include <debugapi.h> //#include <debugapi.h>
#include <endpointvolume.h> #include <endpointvolume.h>
@ -22,6 +27,38 @@
//#include <comip.h> //#include <comip.h>
#include <Winerror.h> #include <Winerror.h>
#include <stringapiset.h> #include <stringapiset.h>
#include "ipolicyconfig.h"
#include <Mmreg.h> #include <Mmreg.h>
#include <tlhelp32.h> #include <tlhelp32.h>
#include <Knownfolders.h>
#include "ipolicyconfig.h"
#include "audiometerinfo.h"
// IAudioMeterInformation
/* GUID manual; */
/* manual.Data1 = 0xc02216f6; */
/* manual.Data2 = 0x8c67; */
/* manual.Data3 = 0x4b5b; */
/* manual.Data4[0] = 0x9d; */
/* manual.Data4[1] = 0x00; */
/* manual.Data4[2] = 0xd0; */
/* manual.Data4[3] = 0x08; */
/* manual.Data4[4] = 0xe7; */
/* manual.Data4[5] = 0x3e; */
/* manual.Data4[6] = 0x00; */
/* manual.Data4[7] = 0x64; */
//if(FAILED(endpoint->Activate((const _GUID) manual,
//IMMEndpoint
/* 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; */

View file

@ -0,0 +1,130 @@
#ifndef __IAudioMeterInformation_FWD_DEFINED__
#define __IAudioMeterInformation_FWD_DEFINED__
typedef interface IAudioMeterInformation IAudioMeterInformation;
#endif /* __IAudioMeterInformation_FWD_DEFINED__ */
#ifdef __cplusplus
extern "C"{
#endif
/* interface __MIDL_itf_endpointvolume_0000_0003 */
/* [local] */
#ifndef __IAudioMeterInformation_INTERFACE_DEFINED__
#define __IAudioMeterInformation_INTERFACE_DEFINED__
/* interface IAudioMeterInformation */
/* [unique][helpstring][nonextensible][uuid][local][object] */
DEFINE_GUID(IID_IAudioMeterInformation, 0xc02216f6, 0x8c67, 0x4b5b, 0x9d,0x00, 0xd0,0x08,0xe7,0x3e,0x00,0x64);
#if defined(__cplusplus) && !defined(CINTERFACE)
MIDL_INTERFACE("c02216f6-8c67-4b5b-9d00-d008e73e0064")
IAudioMeterInformation : public IUnknown
{
public:
virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetPeakValue(
/* [out] */ float *pfPeak) = 0;
virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetMeteringChannelCount(
/* [annotation][out] */
_Out_ UINT *pnChannelCount) = 0;
virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetChannelsPeakValues(
/* [in] */ UINT32 u32ChannelCount,
/* [size_is][out] */ float *afPeakValues) = 0;
virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE QueryHardwareSupport(
/* [annotation][out] */
_Out_ DWORD *pdwHardwareSupportMask) = 0;
};
#ifdef __CRT_UUID_DECL
__CRT_UUID_DECL(IAudioMeterInformation, 0xc02216f6, 0x8c67, 0x4b5b, 0x9d,0x00, 0xd0,0x08,0xe7,0x3e,0x00,0x64)
#endif
#else /* C style interface */
typedef struct IAudioMeterInformationVtbl
{
BEGIN_INTERFACE
HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
IAudioMeterInformation * This,
/* [in] */ REFIID riid,
/* [annotation][iid_is][out] */
_COM_Outptr_ void **ppvObject);
ULONG ( STDMETHODCALLTYPE *AddRef )(
IAudioMeterInformation * This);
ULONG ( STDMETHODCALLTYPE *Release )(
IAudioMeterInformation * This);
/* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *GetPeakValue )(
IAudioMeterInformation * This,
/* [out] */ float *pfPeak);
/* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *GetMeteringChannelCount )(
IAudioMeterInformation * This,
/* [annotation][out] */
_Out_ UINT *pnChannelCount);
/* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *GetChannelsPeakValues )(
IAudioMeterInformation * This,
/* [in] */ UINT32 u32ChannelCount,
/* [size_is][out] */ float *afPeakValues);
/* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *QueryHardwareSupport )(
IAudioMeterInformation * This,
/* [annotation][out] */
_Out_ DWORD *pdwHardwareSupportMask);
END_INTERFACE
} IAudioMeterInformationVtbl;
interface IAudioMeterInformation
{
CONST_VTBL struct IAudioMeterInformationVtbl *lpVtbl;
};
#ifdef COBJMACROS
#define IAudioMeterInformation_QueryInterface(This,riid,ppvObject) \
( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
#define IAudioMeterInformation_AddRef(This) \
( (This)->lpVtbl -> AddRef(This) )
#define IAudioMeterInformation_Release(This) \
( (This)->lpVtbl -> Release(This) )
#define IAudioMeterInformation_GetPeakValue(This,pfPeak) \
( (This)->lpVtbl -> GetPeakValue(This,pfPeak) )
#define IAudioMeterInformation_GetMeteringChannelCount(This,pnChannelCount) \
( (This)->lpVtbl -> GetMeteringChannelCount(This,pnChannelCount) )
#define IAudioMeterInformation_GetChannelsPeakValues(This,u32ChannelCount,afPeakValues) \
( (This)->lpVtbl -> GetChannelsPeakValues(This,u32ChannelCount,afPeakValues) )
#define IAudioMeterInformation_QueryHardwareSupport(This,pdwHardwareSupportMask) \
( (This)->lpVtbl -> QueryHardwareSupport(This,pdwHardwareSupportMask) )
#endif /* COBJMACROS */
#endif /* C style interface */
#endif /* __IAudioMeterInformation_INTERFACE_DEFINED__ */
#ifdef __cplusplus
}
#endif

View file

@ -1,27 +1,37 @@
#include "backlasses.h" #include "backlasses.h"
#include "contclasses.h" #include "contclasses.h"
//TODO: pragma once
void setConfigDirToDefaults() {
#define tryFileDir(dir, create) do { \
OverseerHandler::settingsPath = Environment::createSettingsPath(dir); \
set = ini::UserSettings::createSettings(OverseerHandler::settingsPath.c_str(), create); \
if(set) { \
return; \
} else OverseerHandler::settingsPath.clear(); \
} while(0)
#define tryOpenFileDir(dir) tryFileDir(dir, false)
#define tryCreateFileDir(dir) tryFileDir(dir, true)
tryOpenFileDir(SettingsTargetDirectory::APP_PATH);
tryOpenFileDir(SettingsTargetDirectory::HOME_DIR);
tryCreateFileDir(SettingsTargetDirectory::HOME_DIR);
tryCreateFileDir(SettingsTargetDirectory::APP_PATH);
return;
#undef tryOpenFileDir
#undef tryCreateFileDir
#undef tryFileDir
}
EndpointHandler::EndpointHandler(uint64_t idx, Flows flow) { EndpointHandler::EndpointHandler(uint64_t idx, Flows flow) {
//std::vector<Endpoint*> endpoints = osh->getPlaybackEndpoints().at(idx);
this->idx = idx; this->idx = idx;
this->flow = flow; this->flow = flow;
this->ep = (flow == Flows::FLOW_PLAYBACK ? osh->getPlaybackEndpoints().at(idx) : osh->getCaptureEndpoints().at(idx)); 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(); this->callbackInfo.caller = osh->getGuid();
ep->registerNewSessionNotification(ensc);
//epName = ep->getName(); //epName = ep->getName();
this->setBackEndpointVolumeCallbackInfoContent(this->getState()); this->setBackEndpointVolumeCallbackInfoContent(this->getState());
osh->pushBackEndpointHandler(this, flow); 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);
}
}
} }
void OverseerHandler::pushBackEndpointHandler(EndpointHandler* eph, Flows flow) { void OverseerHandler::pushBackEndpointHandler(EndpointHandler* eph, Flows flow) {
@ -50,16 +60,6 @@ Flows EndpointHandler::getFlow(){
return ep->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(){ BackEndpointVolumeCallbackInfo* EndpointHandler::getCallbackInfo(){
return &this->callbackInfo; return &this->callbackInfo;
} }
@ -114,7 +114,10 @@ void EndpointHandler::setBackEndpointVolumeCallbackInfoContent(uint8_t state) {
callbackInfo.muted = this->getMute(); callbackInfo.muted = this->getMute();
callbackInfo.mainVolume = this->getVolume(AudioChannel::CHANNEL_MAIN); callbackInfo.mainVolume = this->getVolume(AudioChannel::CHANNEL_MAIN);
callbackInfo.channels = this->getChannelCount(); callbackInfo.channels = this->getChannelCount();
if (!epc) {
epc = new EndpointVolumeCallback(ep);
ep->setVolumeCallback(epc); ep->setVolumeCallback(epc);
}
callbackInfo.channelVolumes.resize(this->callbackInfo.channels); callbackInfo.channelVolumes.resize(this->callbackInfo.channels);
for(uint32_t i = 0; i < this->getChannelCount(); i++){ for(uint32_t i = 0; i < this->getChannelCount(); i++){
callbackInfo.channelVolumes.at(i) = this->getVolume(i); callbackInfo.channelVolumes.at(i) = this->getVolume(i);
@ -122,17 +125,21 @@ void EndpointHandler::setBackEndpointVolumeCallbackInfoContent(uint8_t state) {
} }
} }
void EndpointHandler::setState(uint8_t state){ void EndpointHandler::setState(EndpointState state){
ep->setState(state); ep->setState(state);
this->setBackEndpointVolumeCallbackInfoContent(state); this->setBackEndpointVolumeCallbackInfoContent(state);
} }
void EndpointHandler::setState(uint8_t state, uint64_t index){ void EndpointHandler::setState(EndpointState state, uint64_t index){
ep->setState(state); ep->setState(state);
this->setFrontVisibilityInfo((EndpointState)state, index); this->setFrontVisibilityInfo((EndpointState)state, index);
this->setBackEndpointVolumeCallbackInfoContent(state); this->setBackEndpointVolumeCallbackInfoContent(state);
} }
float EndpointHandler::getPeakVolume() {
return ep->getPeakVolume();
}
uint8_t EndpointHandler::getRoles(){ uint8_t EndpointHandler::getRoles(){
return ep->getRoles(); return ep->getRoles();
} }
@ -175,10 +182,15 @@ Endpoint* EndpointHandler::getEndpoint() {
} }
void EndpointHandler::addSessionSendFront(Session* session) { void EndpointHandler::addSessionSendFront(Session* session) {
ep->addSession(session); if(!ep->endpointSessionsMutex.try_lock()) return;
sessionHandlersMutex.lock();
this->ep->addSession(session);
SessionHandler* sessionHandler = new SessionHandler(this, session, (getSessionCount() - 1)); SessionHandler* sessionHandler = new SessionHandler(this, session, (getSessionCount() - 1));
sessionHandlers.push_back(sessionHandler); sessionHandlers.push_back(sessionHandler);
ep->endpointSessionsMutex.unlock();
sessionHandlersMutex.unlock();
this->addSessionWidget(sessionHandler); this->addSessionWidget(sessionHandler);
} }
@ -190,6 +202,49 @@ void EndpointHandler::removeSessionFromFront(SessionHandler* sh) {
this->removeSessionWidget(sh); this->removeSessionWidget(sh);
} }
void EndpointHandler::deleteSessions() {
ep->unregisterNewSessionNotification(ensc);
ensc->Release();
ensc = nullptr;
for (auto sh : sessionHandlers) {
delete sh;
}
sessionHandlers.resize(0);
ep->deleteSessions();
//ep->deleteSessionManager();
}
void EndpointHandler::createSessionHandlers() {
ep->activateEndpointSessions();
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);
}
}
}
void EndpointHandler::createSessionHandlersCallback() {
ensc = new EndpointNewSessionCallback(this);
ep->registerNewSessionNotification(ensc);
}
void EndpointHandler::lockSessionCollections() {
this->sessionHandlersMutex.lock();
this->ep->endpointSessionsMutex.lock();
}
void EndpointHandler::unlockSessionCollections() {
this->sessionHandlersMutex.unlock();
this->ep->endpointSessionsMutex.unlock();
}
void EndpointHandler::removeVolumeCallback() {
ep->removeVolumeCallback(epc);
epc->Release();
epc = nullptr;
}
EndpointHandler::~EndpointHandler() { EndpointHandler::~EndpointHandler() {
ep->removeVolumeCallback(epc); ep->removeVolumeCallback(epc);
ep->unregisterNewSessionNotification(ensc); ep->unregisterNewSessionNotification(ensc);
@ -201,8 +256,44 @@ OverseerHandler::OverseerHandler() {
this->os = new Overseer(); this->os = new Overseer();
} }
void OverseerHandler::setSettingsPath(std::string path) {
OverseerHandler::settingsPath = path;
}
std::string OverseerHandler::getSettingsPath(){
return OverseerHandler::settingsPath;
}
void OverseerHandler::updateStartupConfig(bool onStartup) {
Environment::updateStartupConfig(onStartup);
}
void OverseerHandler::setStartupConfig(bool onStartup) {
Environment::setStartupConfig(onStartup);
}
void OverseerHandler::populateSystemValues() {
Environment::populateSystemValues();
}
void OverseerHandler::openControlPanel() { void OverseerHandler::openControlPanel() {
this->os->openControlPanel(); Environment::openControlPanel();
}
ProcessedNativeEvent OverseerHandler::processTopLevelWindowMessage(void* msg) {
return Environment::processTopLevelWindowMessage(msg);
}
bool OverseerHandler::isLightMode() {
return Environment::isLightMode();
}
bool OverseerHandler::isToRunAtStartup() {
return Environment::isToRunAtStartup();
}
uint32_t OverseerHandler::getAccentColor() {
return Environment::getAccentColor();
} }
std::vector<Endpoint*> OverseerHandler::getPlaybackEndpoints() { std::vector<Endpoint*> OverseerHandler::getPlaybackEndpoints() {
@ -229,7 +320,7 @@ uint64_t OverseerHandler::getCaptureEndpointsCount(){
return this->os->getCaptureEndpoints().size(); return this->os->getCaptureEndpoints().size();
} }
void OverseerHandler::reloadEndpointHandlers(){ void OverseerHandler::createEndpointHandlers(){
//todo: add capture //todo: add capture
//std::vector<EndpointHandler*>* ephs = new std::vector<EndpointHandler*>; //std::vector<EndpointHandler*>* ephs = new std::vector<EndpointHandler*>;
@ -237,9 +328,7 @@ void OverseerHandler::reloadEndpointHandlers(){
for(uint64_t i = 0; i < this->getPlaybackEndpointsCount(); i++){ for(uint64_t i = 0; i < this->getPlaybackEndpointsCount(); i++){
log_debugcpp("Creating Playback handler " + std::to_string(i)); log_debugcpp("Creating Playback handler " + std::to_string(i));
new EndpointHandler(i, Flows::FLOW_PLAYBACK);
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("Created Playback handler " + std::to_string(i) + ", adding to vector. " + " VSize: " + std::to_string(this->playbackEndpointHandlers.size()));
} }
@ -249,53 +338,69 @@ void OverseerHandler::reloadEndpointHandlers(){
for(uint64_t i = 0; i < this->getCaptureEndpointsCount(); i++) { for(uint64_t i = 0; i < this->getCaptureEndpointsCount(); i++) {
log_debugcpp("Creating Capture handler " + std::to_string(i)); log_debugcpp("Creating Capture handler " + std::to_string(i));
new EndpointHandler(i, Flows::FLOW_CAPTURE);
/*
* 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())); log_debugcpp("Created Capture handler " + std::to_string(i) + ", adding to vector. " + " VSize: " + std::to_string(this->captureEndpointHandlers.size()));
/* /*
* if (i >= this->captureEndpointHandlers.size()) * if (i >= this->captureEndpointHandlers.size())
* captureEndpointHandlers.push_back(eph); * captureEndpointHandlers.push_back(eph);
* else captureEndpointHandlers.at(i) = eph; * else captureEndpointHandlers.at(i) = eph;
*/ */
} }
//setEndpointHandlers(ephs); os->registerEndpointSituationCallback();
} }
EndpointHandler* OverseerHandler::addEndpoint(std::wstring endpointId, /* out */ Flows *flow = nullptr) { EndpointHandler* OverseerHandler::addEndpoint(std::wstring endpointId, /* out */ Flows *flow = nullptr) {
//This method is only called from the new endpoint callback and its subsequent thread
Flows localFlow; Flows localFlow;
Endpoint* newEp = this->os->addEndpoint(endpointId, &localFlow); Endpoint* newEp = this->os->addEndpoint(endpointId, &localFlow);
uint64_t ephIdx = (localFlow == Flows::FLOW_PLAYBACK ? this->getPlaybackEndpointsCount() : this->getCaptureEndpointsCount()) - 1; uint64_t ephIdx = (localFlow == Flows::FLOW_PLAYBACK ? this->getPlaybackEndpointsCount() : this->getCaptureEndpointsCount()) - 1;
EndpointHandler* newEph = new EndpointHandler(ephIdx, localFlow); EndpointHandler* newEph = new EndpointHandler(ephIdx, localFlow);
// std::vector<EndpointHandler*> getPlaybackEndpointHandlers();
//std::vector<EndpointHandler*> getCaptureEndpointHandlers();
if (flow != nullptr) *flow = localFlow; if (flow != nullptr) *flow = localFlow;
return newEph; return newEph;
} }
void OverseerHandler::reportFinishedStateChange() {
os->reportFinishedStateChange();
}
NGuid OverseerHandler::getGuid() { NGuid OverseerHandler::getGuid() {
return this->os->getGuid(); return this->os->getGuid();
} }
void OverseerHandler::setChangeFrontDefaultsFunction(std::function<void(Roles, std::wstring)> changeFrontDefaults){ /*
this->changeFrontDefaults = changeFrontDefaults; * void OverseerHandler::setChangeFrontDefaultsFunction(std::function<void(Roles, std::wstring)> changeFrontDefaults){
* this->changeFrontDefaults = changeFrontDefaults;
* }
*
* void OverseerHandler::changeFrontDefaultsCallback(Roles role, std::wstring endpointId) {
* this->changeFrontDefaults(role, endpointId);
* }
*/
void OverseerHandler::roleBucketEntryCallback(Roles role, std::wstring endpointId){
this->roleBucketEntry(role, endpointId);
} }
void OverseerHandler::changeFrontDefaultsCallback(Roles role, std::wstring endpointId) { void OverseerHandler::setRoleBucketEntryFunction(std::function<void(Roles, std::wstring)> roleBucketEntry) {
this->changeFrontDefaults(role, endpointId); this->roleBucketEntry = roleBucketEntry;
}
void OverseerHandler::updateFrontEndpointName(Endpoint* ep) {
//todo: reintroduce capture devices
for (auto eph : playbackEndpointHandlers) {
if (eph->getEndpoint() == ep) eph->getCallbackInfo()->updateName = true;
}
} }
void OverseerHandler::reviseEndpointShowing(std::wstring endpointId, EndpointState state) { void OverseerHandler::reviseEndpointShowing(std::wstring endpointId, EndpointState state) {
//TODO: Race condition!!!!!
std::vector<EndpointHandler*> allHandlers; std::vector<EndpointHandler*> allHandlers;
handlersPlaybackMutex.lock();
handlersCaptureMutex.lock();
os->playbackMutex.lock();
os->captureMutex.lock();
allHandlers.insert(allHandlers.end(), this->captureEndpointHandlers.begin(), this->captureEndpointHandlers.end()); allHandlers.insert(allHandlers.end(), this->captureEndpointHandlers.begin(), this->captureEndpointHandlers.end());
allHandlers.insert(allHandlers.end(), this->playbackEndpointHandlers.begin(), this->playbackEndpointHandlers.end()); allHandlers.insert(allHandlers.end(), this->playbackEndpointHandlers.begin(), this->playbackEndpointHandlers.end());
EndpointHandler* eph = nullptr; EndpointHandler* eph = nullptr;
@ -309,22 +414,29 @@ void OverseerHandler::reviseEndpointShowing(std::wstring endpointId, EndpointSta
//debug //debug
Flows flow; Flows flow;
if (!eph) { if (!eph) {
if (state ^ EndpointState::ENDPOINT_ACTIVE) return; if (state ^ EndpointState::ENDPOINT_ACTIVE) goto end;
//return;
//flow = Flows::FLOW_CAPTURE; //flow = Flows::FLOW_CAPTURE;
eph = osh->addEndpoint(endpointId, &flow); eph = osh->addEndpoint(endpointId, &flow);
} else } else
flow = eph->getFlow(); flow = eph->getFlow();
//todo: mic done but disabled. Tab-kun will come... //todo: mic done but disabled. Tab-kun will come...
if (flow == Flows::FLOW_CAPTURE) return; if (flow == Flows::FLOW_CAPTURE) goto end;
if (eph && EndpointState::ENDPOINT_ACTIVE & state) { if (eph && EndpointState::ENDPOINT_ACTIVE & state) {
eph->setState(EndpointState::ENDPOINT_ACTIVE);
this->addEndpointWidget(eph); this->addEndpointWidget(eph);
} else if (eph && eph->getFrontVisibilityState() == EndpointState::ENDPOINT_ACTIVE) { } else if (eph && eph->getFrontVisibilityState() == EndpointState::ENDPOINT_ACTIVE) {
eph->removeVolumeCallback();
this->removeEndpointWidget(eph->getFrontVisibilityIndex()); this->removeEndpointWidget(eph->getFrontVisibilityIndex());
} }
end:
handlersPlaybackMutex.unlock();
handlersCaptureMutex.unlock();
os->playbackMutex.unlock();
os->captureMutex.unlock();
os->reportFinishedStateChange();
return; return;
} }
@ -339,3 +451,14 @@ void OverseerHandler::setRemoveEndpointWidgetFunction(std::function<void(uint64_
void OverseerHandler::setEndpointHandlers(std::vector<EndpointHandler*> ephs){ void OverseerHandler::setEndpointHandlers(std::vector<EndpointHandler*> ephs){
this->playbackEndpointHandlers = ephs; this->playbackEndpointHandlers = ephs;
} }
void OverseerHandler::lockEndpoints() {
os->playbackMutex.lock();
os->captureMutex.lock();
}
void OverseerHandler::unlockEndpoints() {
os->playbackMutex.unlock();
os->captureMutex.unlock();
}

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "global.h" #include "global.h"
#include "settings.h"
#include "contsessionclasses.h" #include "contsessionclasses.h"
//#define invoke_mem_fn(object,ptrToMember) ((object).*(ptrToMember)) //#define invoke_mem_fn(object,ptrToMember) ((object).*(ptrToMember))
//#define pinvoke_mem_fn(object,ptrToMember) ((object)->*(ptrToMember)) //#define pinvoke_mem_fn(object,ptrToMember) ((object)->*(ptrToMember))
@ -18,19 +19,19 @@ struct BackEndpointVolumeCallbackInfo {
float mainVolume; float mainVolume;
size_t channels; size_t channels;
std::vector<float> channelVolumes; std::vector<float> channelVolumes;
bool updateName = false;
}; };
void setConfigDirToDefaults();
class EndpointHandler { class EndpointHandler {
public: public:
EndpointHandler(uint64_t idx, Flows flow); EndpointHandler(uint64_t idx, Flows flow);
void setBackEndpointVolumeCallbackInfoContent(uint8_t state); void setBackEndpointVolumeCallbackInfoContent(uint8_t state);
//these two, currently unused. If I use them, I should feel bad. //todo: replace all getEndpointHandler()
//EndpointVolumeCallback* getEndpointVolumeCallback(); //todo: name refactor
//Endpoint* getEndpoint();
//std::wstring epName;
BackEndpointVolumeCallbackInfo* getCallbackInfo(); BackEndpointVolumeCallbackInfo* getCallbackInfo();
uint32_t getChannelCount(); uint32_t getChannelCount();
@ -39,6 +40,7 @@ public:
void setVolume(int channel, float volume); void setVolume(int channel, float volume);
std::wstring getName(); std::wstring getName();
void setName(std::wstring newName);
std::wstring getId(); std::wstring getId();
void setFrontVisibilityInfo(EndpointState state, uint64_t frontIdx); void setFrontVisibilityInfo(EndpointState state, uint64_t frontIdx);
@ -56,9 +58,10 @@ public:
void setVolume(NGuid guid, int channel, int value); void setVolume(NGuid guid, int channel, int value);
void setMute(NGuid guid, bool muted); void setMute(NGuid guid, bool muted);
void setState(uint8_t state); void setState(EndpointState state);
void setState(uint8_t state, uint64_t idx); void setState(EndpointState state, uint64_t idx);
float getPeakVolume();
/* sessions */ /* sessions */
size_t getSessionCount(); size_t getSessionCount();
std::vector<SessionHandler*> getSessionHandlers(); std::vector<SessionHandler*> getSessionHandlers();
@ -71,6 +74,13 @@ public:
void setRemoveSessionWidgetFunction(std::function<void(SessionHandler*)> removeSessionWidget); void setRemoveSessionWidgetFunction(std::function<void(SessionHandler*)> removeSessionWidget);
void sendSessionToFront(SessionHandler* sh); void sendSessionToFront(SessionHandler* sh);
void removeSessionFromFront(SessionHandler* sh); void removeSessionFromFront(SessionHandler* sh);
void deleteSessions();
void createSessionHandlers();
void createSessionHandlersCallback();
std::mutex sessionHandlersMutex;
void lockSessionCollections();
void unlockSessionCollections();
void removeVolumeCallback();
~EndpointHandler(); ~EndpointHandler();
private: private:
@ -97,11 +107,25 @@ class OverseerHandler {
public: public:
OverseerHandler(); OverseerHandler();
static void setSettingsPath(std::string path);
static std::string getSettingsPath();
static inline std::string settingsPath;
void updateStartupConfig(bool onStartup);
void setStartupConfig(bool onStartup);
void populateSystemValues();
void openControlPanel(); void openControlPanel();
ProcessedNativeEvent processTopLevelWindowMessage(void* msg);
bool isLightMode();
bool isToRunAtStartup();
uint32_t getAccentColor();
void setChangeFrontDefaultsFunction(std::function<void(Roles, std::wstring)> changeFrontDefaults); //void setChangeFrontDefaultsFunction(std::function<void(Roles, std::wstring)> changeFrontDefaults);
void changeFrontDefaultsCallback(Roles role, std::wstring endpointId); //void changeFrontDefaultsCallback(Roles role, std::wstring endpointId);
void roleBucketEntryCallback(Roles role, std::wstring endpointId);
void setRoleBucketEntryFunction(std::function<void(Roles, std::wstring)> roleBucketEntry);
void updateFrontEndpointName(Endpoint* ep);
//void setReviseEndpointShowingFunction(std::function<void(std::wstring, EndpointState)> reviseEndpointShowing); //void setReviseEndpointShowingFunction(std::function<void(std::wstring, EndpointState)> reviseEndpointShowing);
void reviseEndpointShowing(std::wstring endpointId, EndpointState state); void reviseEndpointShowing(std::wstring endpointId, EndpointState state);
void setRemoveEndpointWidgetFunction(std::function<void(uint64_t)> removeEndpointWidget); void setRemoveEndpointWidgetFunction(std::function<void(uint64_t)> removeEndpointWidget);
@ -115,25 +139,29 @@ public:
void pushBackEndpointHandler(EndpointHandler* eph, Flows flow); void pushBackEndpointHandler(EndpointHandler* eph, Flows flow);
uint64_t getPlaybackEndpointsCount(); uint64_t getPlaybackEndpointsCount();
uint64_t getCaptureEndpointsCount(); uint64_t getCaptureEndpointsCount();
void reloadEndpointHandlers(); void createEndpointHandlers();
EndpointHandler* addEndpoint(std::wstring endpointId, Flows *flow); EndpointHandler* addEndpoint(std::wstring endpointId, Flows *flow);
void reportFinishedStateChange();
NGuid getGuid(); NGuid getGuid();
/* std::mutex handlersPlaybackMutex;
* void setSessionVolumeCallback(std::function<void(float)> changeSessionVolume); std::mutex handlersCaptureMutex;
* void setSessionVolume(float newValue, ); void lockEndpoints();
*/ void unlockEndpoints();
private: private:
Overseer *os; Overseer *os;
std::vector<EndpointHandler*> playbackEndpointHandlers; std::vector<EndpointHandler*> playbackEndpointHandlers;
std::vector<EndpointHandler*> captureEndpointHandlers; std::vector<EndpointHandler*> captureEndpointHandlers;
std::function<void(Roles, std::wstring /* endpointid */)> changeFrontDefaults; std::function<void(Roles, std::wstring /* endpointid */)> changeFrontDefaults;
std::function<void(uint64_t /* epw id */)> removeEndpointWidget; std::function<void(uint64_t /* epw id */)> removeEndpointWidget;
std::function<void(EndpointHandler*)> addEndpointWidget; std::function<void(EndpointHandler*)> addEndpointWidget;
std::function<void(Roles, std::wstring /* endpointid */)> roleBucketEntry;
/* Session's */ /* Session's */
std::function<void(float)> changeSessionVolume; std::function<void(float)> changeSessionVolume;
//std::function<void(uint64_t /* device */, uint32_t /* channel */, float /* value */)> updateFrontVolumeCallback; //std::function<void(uint64_t /* device */, uint32_t /* channel */, float /* value */)> updateFrontVolumeCallback;
//std::function<void(uint64_t /* device */, bool /* mute */)> updateFrontMuteCallback; //std::function<void(uint64_t /* device */, bool /* mute */)> updateFrontMuteCallback;

View file

@ -32,6 +32,14 @@ std::wstring SessionHandler::getName(){
return session->getName(); return session->getName();
} }
void SessionHandler::setName(std::wstring newName){
session->setName(newName);
}
float SessionHandler::getPeakVolume(){
return session->getPeakVolume();
}
bool SessionHandler::getMute(){ bool SessionHandler::getMute(){
return session->getMute(); return session->getMute();
} }
@ -77,3 +85,7 @@ void SessionHandler::reviseSessionShowing(SessionState state) {
} }
} }
SessionHandler::~SessionHandler() {
session->removeStateCallback(ssc);
ssc->Release();
}

View file

@ -12,7 +12,7 @@ struct SessionVolumeInfo {
bool muted; bool muted;
float mainVolume; float mainVolume;
NGuid caller; NGuid caller;
std::atomic<bool> isNameChanged = false;
//size_t channels; //size_t channels;
//std::vector<float> channelVolumes; //std::vector<float> channelVolumes;
}; };
@ -29,9 +29,11 @@ class SessionHandler {
void setState(SessionState state); void setState(SessionState state);
uint64_t getFrontIndex(); uint64_t getFrontIndex();
std::wstring getName(); std::wstring getName();
float getPeakVolume();
void setName(std::wstring newName);
void reviseSessionShowing(SessionState state); void reviseSessionShowing(SessionState state);
SessionVolumeInfo* getVolumeInfo(); SessionVolumeInfo* getVolumeInfo();
~SessionHandler();
private: private:
SessionVolumeInfo svi; SessionVolumeInfo svi;
EndpointHandler* eph; EndpointHandler* eph;

View file

@ -2,11 +2,38 @@
#if defined (QT_DEBUG) || defined (DEBUG) || defined (_DEBUG) #if defined (QT_DEBUG) || defined (DEBUG) || defined (_DEBUG)
template<size_t Y, typename T> #define PIPE_NAME "Mixerq-dev"
std::bitset<Y> varToBitset(T info) {
std::bitset<Y> content(info); #ifdef INIT_FILELOG
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
FILE* fileLog;
errno_t lfResult;
bool writable = false;
void inline initializeFileLogging() {
lfResult = fopen_s(&fileLog, "log.txt", "w");
if (!lfResult) writable = true;
else writable = false;
}
#else
extern std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
extern errno_t lfResult;
extern FILE* fileLog;
extern bool writable;
extern bool initializeFileLogging();
#endif
#define initialize_file_log() initializeFileLogging()
template<typename T>
std::bitset<sizeof(T) * 8> varToBitset(T info) {
std::bitset<sizeof(T) * 8> content(info);
return content; return content;
} }
#define print_as_binary(info) varToBitset<decltype(info)>(info).to_string()
#ifndef _WIN32 #ifndef _WIN32
#define log_debugcpp(str) do { \ #define log_debugcpp(str) do { \
std::cout << "[DEBUG]" << "(" << __FILE__ << ":" << __LINE__ << "): " << str << std::endl; \ std::cout << "[DEBUG]" << "(" << __FILE__ << ":" << __LINE__ << "): " << str << std::endl; \
@ -15,6 +42,7 @@ std::bitset<Y> varToBitset(T info) {
#define log_wdebugcpp(str) do { \ #define log_wdebugcpp(str) do { \
std::wcout << "[DEBUG]" << "(" << __FILE__ << ":" << __LINE__ << "): " << str << std::endl; \ std::wcout << "[DEBUG]" << "(" << __FILE__ << ":" << __LINE__ << "): " << str << std::endl; \
} while (0) } while (0)
#else #else
#include <Windows.h> #include <Windows.h>
@ -29,19 +57,30 @@ std::bitset<Y> varToBitset(T info) {
#define log_wdebugcpp(str) do { \ #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()); \ OutputDebugStringW(std::wstring(L"[DEBUG] (" + std::wstring(WFILE) + L":" + std::to_wstring(__LINE__) + L"): " + std::wstring(str) +L"\n").c_str()); \
} while (0) } while (0)
#endif
#define print_as_binary(len, type, info) varToBitset<len, type>(info) #endif //_WIN32
#define log_to_file(fmt, cnt...) do { \
if(writable) fprintf_s(fileLog, fmt,##cnt); \
} while (0)
#define close_file_log_buffer() do { \
if(writable) { fflush(fileLog); fclose(fileLog); } \
} while (0)
#else #else
#define log_debugcpp(str) #define log_debugcpp(str)
#define log_wdebugcpp(str) #define log_wdebugcpp(str)
#define print_as_binary(len, type, info) #define print_as_binary(info)
#endif #define log_to_file(fmt, cnt...)
#define initialize_file_log() false
#define close_file_log_buffer()
#define PIPE_NAME "Mixerq"
#endif //DEBUG
/* Here as a quick reference, in case smthn similar is needed again */ /* Here as a quick reference, in case smthn similar is needed again */
/* typedef void (EndpointWidget::*epwMuteFunc)(bool muted); */ /* typedef void (EndpointWidget::*epwMuteFunc)(bool muted); */
/* typedef void (EndpointWidget::*epwMainVolumeFunc)(float newValue); */ /* Typedef void (EndpointWidget::*epwMainVolumeFunc)(float newValue); */
/* typedef void (EndpointWidget::*epwChannelVolumeFunc)(uint32_t channel, float newValue); */ /* typedef void (EndpointWidget::*epwChannelVolumeFunc)(uint32_t channel, float newValue); */
/* typedef void (EndpointWidget::*epwToggleFrontFunc)(bool active); */ /* typedef void (EndpointWidget::*epwToggleFrontFunc)(bool active); */

View file

@ -1,15 +1,26 @@
#pragma once #pragma once
#define __STDC_WANT_LIB_EXT1__ 1
#include <stdio.h>
#include <errno.h>
#include <limits.h>
#include <codecvt>
#include <vector> #include <vector>
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <bitset> #include <bitset>
#include <climits>
#include <locale> #include <locale>
#include <map>
#include <thread>
#include <deque>
#include "debug.h" #include "debug.h"
//#include "settings.h"
//TODO: Use tr();? QTranslator //TODO: Use tr();? QTranslator
#define APP_NAME "MixerQ"
#define LAPP_NAME L"MixerQ"
#define STRING_MUTE "Mute" #define STRING_MUTE "Mute"
#define STRING_UNMUTE "Unmute" #define STRING_UNMUTE "Unmute"
#define STRING_QUIT "Quit" #define STRING_QUIT "Quit"
@ -26,8 +37,25 @@
#define STRING_CP "Open Control Panel" #define STRING_CP "Open Control Panel"
#define STRING_ABOUT "About" #define STRING_ABOUT "About"
#define STRING_STARTUP "Run at startup" #define STRING_STARTUP "Run at startup"
#define STRING_CHANNELS "Show endpoint channels"
#define STRING_NOENDPOINT "No active endpoints"
#define LSTRING_UNNAMED_SESSION L"Unnamed session"
//INIT BACK //INIT BACK
enum SettingsTargetDirectory {
HOME_DIR = 0,
APP_PATH = (1 << 0),
CUSTOM = (1 << 1),
};
enum ProcessedNativeEvent {
NONE = 0,
COLORS = (1 << 0),
};
enum AudioChannel { enum AudioChannel {
CHANNEL_LEFT = (1 << 0), CHANNEL_LEFT = (1 << 0),
CHANNEL_RIGHT = (1 << 1), CHANNEL_RIGHT = (1 << 1),
@ -79,7 +107,11 @@ struct NGuid {
/* }while (i < 8); */ /* }while (i < 8); */
/* } */ /* } */
}; };
namespace ini {
class UserSettings;
}
extern ini::UserSettings *set;
class OverseerHandler; class OverseerHandler;
extern OverseerHandler *osh; extern OverseerHandler *osh;

19
src/qt/meterslider.h Normal file
View file

@ -0,0 +1,19 @@
#include <QSlider>
class MeterSlider : public QSlider {
Q_OBJECT
private:
~MeterSlider();
float peakValue;
friend class MixerStyle;
protected:
bool event(QEvent* ev) override;
void paintEvent(QPaintEvent *event) override;
public:
//MeterSlider(Qt::Orientation orientation, QWidget *parent = nullptr);
//MeterSlider(QWidget* parent = nullptr) : MeterSlider(Qt::Vertical, parent){};
void setPeakValue(float peakValue);
using QSlider::QSlider;
};

File diff suppressed because it is too large Load diff

View file

@ -1,45 +1,10 @@
#pragma once #pragma once
//#ifndef MAINWINDOW_H #include "qtcommon.h"
//#define MAINWINDOW_H
#include <QMainWindow>
#include <QApplication>
#include <QCloseEvent>
#include <QIcon>
#include <QSystemTrayIcon>
#include <QMenu>
//#include <QMessageBox>
#include <QLabel>
#include <QSlider>
#include <QGridLayout>
#include <QPushButton>
#include <QCheckBox>
#include <QTimer>
#include <QScrollArea>
#include <QSize>
#include <QMenuBar>
#include <QMenu>
#include <QScreen>
#include <QToolBar>
#include <QWindow>
//#include <QScrollBar>
/*
* #else
* class QSlider;
* class QLabel;
* class QGridLayout;
* class QPushButton;
* class QWidget;
* class QMainWindow;
* #endif
*/
#include "global.h"
#include "contclasses.h" #include "contclasses.h"
#include "settings.h"
class MeterSlider;
enum SpawnPos { enum SpawnPos {
LEFT = (1 << 1), LEFT = (1 << 1),
@ -53,7 +18,15 @@ enum CustomQEvent {
EndpointWidgetCreated = 1002, EndpointWidgetCreated = 1002,
EndpointDefaultChange = 1003, EndpointDefaultChange = 1003,
SessionWidgetCreated = 1004, SessionWidgetCreated = 1004,
SessionWidgetObsolete = 1005 SessionWidgetObsolete = 1005,
RecomposeMainWindow = 1006,
EndpointRoleChange = 1007
};
class DarkModeEventFilter : public QAbstractNativeEventFilter {
public:
bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *) override;
}; };
template <typename T> template <typename T>
@ -62,40 +35,45 @@ class CustomWidgetEvent : public QEvent {
public: public:
CustomWidgetEvent(QEvent::Type type, T payload); CustomWidgetEvent(QEvent::Type type, T payload);
T payload; T payload;
}; };
//Q_DECLARE_METATYPE(EndpointWidgetEvent) //Q_DECLARE_METATYPE(EndpointWidgetEvent)
class ExtendedCheckBox : public QCheckBox { class ExtendedCheckBox : public QCheckBox {
Q_OBJECT Q_OBJECT
private:
QIcon icons;
protected: protected:
void customEvent(QEvent* ev) override; void customEvent(QEvent* ev) override;
void paintEvent(QPaintEvent* event) override;
public: public:
//c++11: this inherits all parent's constructors unconditionally //c++11: this inherits all parent's constructors unconditionally
using QCheckBox::QCheckBox; using QCheckBox::QCheckBox;
void addIcon(char* const path, QIcon::State state);
//alternative being calling parent ctor directly after declaring child ctor: //alternative being calling parent ctor directly after declaring child ctor:
//B(int x) : A(x) { } //B(int x) : A(x) { }
}; };
class SessionWidget : public QWidget { class SessionWidget : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent /* = nullptr */); SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent /* = nullptr */);
~SessionWidget(); ~SessionWidget();
void setSize(uint64_t width, uint64_t height); void calculateSize(uint64_t width, uint64_t height);
std::wstring getName();
public slots: public slots:
void updateMainVolume(int newValue); void updateMainVolume(int newValue);
void updateMute(int checked); void updateMute(int checked);
private: private:
QLabel *mainLabel = nullptr; QLabel *mainLabel = nullptr;
QSlider *mainSlider = nullptr; MeterSlider *mainSlider = nullptr;
uint64_t idx; uint64_t idx;
QHBoxLayout *widgetLayout = nullptr; QHBoxLayout *widgetLayout = nullptr;
QCheckBox *muteButton = nullptr; ExtendedCheckBox *muteButton = nullptr;
SessionHandler* sh; SessionHandler* sh;
QTimer* volumePoller = nullptr; QTimer* volumePoller = nullptr;
QSpacerItem* widthSpacer; QSpacerItem* widthSpacer;
@ -109,6 +87,7 @@ public:
//QSize minimumSizeHint() const override; //QSize minimumSizeHint() const override;
//void setMinimum(QSize minimum); //void setMinimum(QSize minimum);
void updateChannel(int channel); void updateChannel(int channel);
uint32_t getChannelCount() const;
private: private:
const double roundingFactor = 0.005; const double roundingFactor = 0.005;
@ -124,17 +103,17 @@ class EndpointWidget : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *parent = nullptr); EndpointWidget(EndpointHandler* eph, QWidget *parent = nullptr, uint64_t idx = INT_MAX);
//QSize minimumSizeHint() const override; //QSize minimumSizeHint() const override;
//void setMinimum(uint64_t height, double heightRatio); //void setMinimum(uint64_t height, double heightRatio);
void updateChannelsVisibility();
EndpointHandler* getEndpointHandler(); EndpointHandler* getEndpointHandler();
std::map<Roles, ExtendedCheckBox*> getDefaultRolesWidgets(); std::map<Roles, ExtendedCheckBox*> getDefaultRolesWidgets();
void setIndex(uint64_t idx); void setIndex(uint64_t idx);
uint64_t getIndex(); uint64_t getIndex();
//void setVolume(int channel, float volume); //void setVolume(int channel, float volume);
void setSize(uint64_t width, uint64_t height); void calculateSize(uint64_t width, uint64_t height);
~EndpointWidget(); ~EndpointWidget();
//void updateMainVolume(float newValue); //void updateMainVolume(float newValue);
@ -144,6 +123,9 @@ public:
//void populateEndpointWidget(EndpointHandler *eph); //void populateEndpointWidget(EndpointHandler *eph);
//void setEndpointHandlers(std::vector<EndpointHandler*> *ephs); //void setEndpointHandlers(std::vector<EndpointHandler*> *ephs);
//protected:
//bool event(QEvent* ev) override;
public slots: public slots:
void updateMainVolume(int newValue); void updateMainVolume(int newValue);
void updateMute(int checked); void updateMute(int checked);
@ -161,7 +143,7 @@ private:
QCheckBox *muteButton = nullptr; QCheckBox *muteButton = nullptr;
QLabel *mainLabel = nullptr; QLabel *mainLabel = nullptr;
QLabel *mainVolumeLabel = nullptr; QLabel *mainVolumeLabel = nullptr;
QSlider *mainSlider = nullptr; MeterSlider *mainSlider = nullptr;
std::vector<QSlider*> channelSliders; std::vector<QSlider*> channelSliders;
std::vector<QLabel*> channelLabels; std::vector<QLabel*> channelLabels;
QGridLayout *widgetLayout = nullptr; QGridLayout *widgetLayout = nullptr;
@ -172,7 +154,7 @@ private:
size_t defaultRolesVectorSize = 4; size_t defaultRolesVectorSize = 4;
QTimer* timer = nullptr; QTimer* timer = nullptr;
uint64_t idx; uint64_t idx;
ChannelWidget* cw; ChannelWidget* cw = nullptr;
std::vector<SessionWidget*> sessionWidgets; std::vector<SessionWidget*> sessionWidgets;
QSize minimum; QSize minimum;
//std::vector<EndpointHandler*> *ephs; //std::vector<EndpointHandler*> *ephs;
@ -191,10 +173,11 @@ public:
private: private:
QGridLayout *widgetLayout; QGridLayout *widgetLayout;
QPushButton *about; //QPushButton *about;
#ifdef WIN32 #ifdef WIN32
QPushButton *openCP; QPushButton *openCP;
QPushButton *startup; QCheckBox *startup;
QCheckBox *channels;
#endif #endif
}; };
@ -205,13 +188,15 @@ class MainWindow : public QMainWindow {
public: public:
MainWindow(QWidget *parent = nullptr); MainWindow(QWidget *parent = nullptr);
void reloadEndpointWidgets(); void reloadEndpointWidgets();
void compose(); void compose(bool isVisible);
void updateColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
protected: protected:
void closeEvent(QCloseEvent *event) override; void closeEvent(QCloseEvent *event) override;
void customEvent(QEvent* ev) override; void customEvent(QEvent* ev) override;
QRect setSizePosition(QScreen* screen, int width, int height); QRect setSizePosition(QScreen* screen, int width, int height);
QScreen* getCurrentScreen(); QScreen* getCurrentScreen();
void createLayout(QGridLayout *newLayout);
private slots: private slots:
void trayIconActivated(QSystemTrayIcon::ActivationReason reason); void trayIconActivated(QSystemTrayIcon::ActivationReason reason);
@ -223,8 +208,14 @@ private slots:
private: private:
//std::vector<EndpointHandler*> *ephs; //std::vector<EndpointHandler*> *ephs;
bool eventFilter(QObject *object, QEvent *event) override;
void flushRoleChanges();
void changeFrontDefaults(Roles role, EndpointWidget* newDef, EndpointWidget* oldDef);
std::vector<EndpointWidget*> ews; std::vector<EndpointWidget*> ews;
QWidget *widget; std::deque<std::pair<Roles, std::wstring>> roleBucketList;
std::mutex roleBucketMutex;
QWidget *containerWidget;
QGridLayout *widgetLayout; QGridLayout *widgetLayout;
QSystemTrayIcon *trayIcon; QSystemTrayIcon *trayIcon;
@ -234,13 +225,20 @@ private:
QTimer *ewsUpdateTimer; QTimer *ewsUpdateTimer;
static constexpr uint64_t ewsUpdateTimerFrequency = 500; static constexpr uint64_t ewsUpdateTimerFrequency = 500;
double widthRatio = 0.28; double widthRatio = 0.28;
double dpr = 1.0;
bool recentlyClosed = false;
uint8_t recentlyClosedTimerFrequency = 1000;
QTimer *recentlyClosedTimer;
QScrollArea *scrollArea; QScrollArea *scrollArea;
HeaderWidget* hw; HeaderWidget* hw;
QToolBar *mainMenuBar; QToolBar *mainMenuBar;
QScreen *screen; QScreen *screen;
QSpacerItem* lastRowSpacer; QSpacerItem* lastRowSpacer;
QLabel* noEndpoints = nullptr;
QFont font;
friend class EndpointWidget;
//public slots: //public slots:
// void setEndpointHandlers(std::vector<EndpointHandler*> *ephs); // void setEndpointHandlers(std::vector<EndpointHandler*> *ephs);
@ -248,5 +246,3 @@ private:
//void valueChanged(int value); //void valueChanged(int value);
}; };
//#endif

254
src/qt/qtcommon.h Normal file
View file

@ -0,0 +1,254 @@
#pragma once
#include <QLocalSocket>
#include <QLocalServer>
#include <QString>
#include <QFile>
#include <QStringList>
#include <QStyleFactory>
#include <QPalette>
#include <QMainWindow>
#include <QApplication>
#include <QCloseEvent>
#include <QIcon>
#include <QSystemTrayIcon>
#include <QMenu>
//#include <QMessageBox>
#include <QLabel>
#include <QSlider>
#include <QGridLayout>
#include <QPushButton>
#include <QCheckBox>
#include <QTimer>
#include <QScrollArea>
#include <QScrollBar>
#include <QSize>
#include <QMenuBar>
#include <QMenu>
#include <QScreen>
#include <QToolBar>
#include <QWindow>
#include <QPainter>
#include <QStyle>
#include <QStyleOptionComplex>
#include <QStyleOptionSlider>
#include <QStylePainter>
#include <QStyleOptionSlider>
#include <QFontMetrics>
#include <QProxyStyle>
#include <QPixmapCache>
#include <QLatin1Char>
#include <QLatin1String>
#include <QFontDatabase>
#include <QAbstractNativeEventFilter>
#include <QAbstractEventDispatcher>
#include <QSvgRenderer>
//#include <QScrollbarStyleAnimation>
//#include <QScrollBar>
/*
* #else
* class QSlider;
* class QLabel;
* class QGridLayout;
* class QPushButton;
* class QWidget;
* class QMainWindow;
* #endif
*/
#include "global.h"
enum CustomComplexControl {
CC_MeterSlider = 0xf0000001
};
enum CustomControlElement {
CE_ExtendedCheckBox = 0xf0000001
};
namespace StylingHelper {
static inline void setBackgroundColor(bool lightMode) {
//QApplication* app = (QApplication*)QApplication::instance();
QPalette pal = QGuiApplication::palette();
if(lightMode) {
pal.setColor(QPalette::Window, Qt::white);
pal.setColor(QPalette::WindowText, Qt::black);
} else {
pal.setColor(QPalette::Window, Qt::black);
pal.setColor(QPalette::WindowText, Qt::white);
}
QGuiApplication::setPalette(pal);
}
static inline void setAccentColor(bool lightMode) {
//QApplication* app = (QApplication*)QApplication::instance();
QPalette pal = QGuiApplication::palette();
if(lightMode) {
pal.setColor(QPalette::Window, Qt::white);
pal.setColor(QPalette::WindowText, Qt::black);
} else {
pal.setColor(QPalette::Window, Qt::black);
pal.setColor(QPalette::WindowText, Qt::white);
}
QGuiApplication::setPalette(pal);
}
static inline bool argbToDiscreteValues(uint32_t color, uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *a) {
if(!r || !g || !b || !a) return false;
*a = (color >> 24) & 0xFF;
*r = (color >> 16) & 0xFF;
*g = (color >> 8) & 0xFF;
*b = color & 0xFF;
return true;
}
static inline QLatin1String operator""_L1(const char* ch, uint64_t) {
return QLatin1String(ch);
}
static inline qreal calculateDpi(const QStyleOption *option) {
#ifndef Q_OS_DARWIN
// Prioritize the application override, except for on macOS where
// we have historically not supported the AA_Use96Dpi flag.
if (QCoreApplication::testAttribute(Qt::AA_Use96Dpi))
return 96;
#endif
// Expect that QStyleOption::QFontMetrics::QFont has the correct DPI set
if (option)
return option->fontMetrics.fontDpi();
// Fall back to historical Qt behavior: hardcoded 72 DPI on mac,
// primary screen DPI on other platforms.
#ifdef Q_OS_DARWIN
return qstyleBaseDpi;
#else
return QGuiApplication::primaryScreen()->physicalDotsPerInch();
#endif
}
static inline qreal calculateDpi() {
return QFontMetrics(QApplication::font()).fontDpi();
}
/*
* #ifdef Q_OS_DARWIN
* static const qreal qstyleBaseDpi = 72;
* #else
* static const qreal qstyleBaseDpi = 96;
* #endif
*/
//Q_GUI_EXPORT int qt_defaultDpiX() const;
static inline qreal dpiScaled(qreal value, qreal dpi) {
static const qreal qstyleBaseDpi = 96;
return value * dpi / qstyleBaseDpi;
}
static inline qreal dpiScaled(qreal value, const QPaintDevice *device) {
return dpiScaled(value, device->logicalDpiX());
}
static inline qreal dpiScaled(qreal value, const QStyleOption *option) {
return dpiScaled(value, calculateDpi(option));
}
static inline bool isMacSystemPalette(const QPalette &pal) {
Q_UNUSED(pal);
#if defined(Q_OS_MACOS)
const QPalette *themePalette = QGuiApplicationPrivate::platformTheme()->palette();
if (themePalette && themePalette->color(QPalette::Normal, QPalette::Highlight) ==
pal.color(QPalette::Normal, QPalette::Highlight) &&
themePalette->color(QPalette::Normal, QPalette::HighlightedText) ==
pal.color(QPalette::Normal, QPalette::HighlightedText))
return true;
#endif
return false;
}
static inline QColor highlight(const QPalette &pal) {
if (isMacSystemPalette(pal))
return QColor(60, 140, 230);
return pal.color(QPalette::Highlight);
}
static inline QColor highlightedOutline(const QPalette &pal) {
QColor highlightedOutline = highlight(pal).darker(125);
if (highlightedOutline.value() > 160)
highlightedOutline.setHsl(highlightedOutline.hue(), highlightedOutline.saturation(), 160);
return highlightedOutline;
}
static inline QColor getOutline(const QPalette &pal) {
if (pal.window().style() == Qt::TexturePattern)
return QColor(0, 0, 0, 160);
return pal.window().color().darker(140);
}
static inline QColor getButtonColor(const QPalette &pal) {
QColor buttonColor = pal.button().color();
int val = qGray(buttonColor.rgb());
buttonColor = buttonColor.lighter(100 + qMax(1, (180 - val)/6));
buttonColor.setHsv(buttonColor.hue(), buttonColor.saturation() * 0.75, buttonColor.value());
return buttonColor;
}
static inline QColor innerContrastLine() {
return QColor(255, 255, 255, 30);
}
static inline QPixmap styleCachePixmap(const QSize &size, qreal pixelRatio) {
//not api
QPixmap cachePixmap = QPixmap(size * pixelRatio);
cachePixmap.setDevicePixelRatio(pixelRatio);
cachePixmap.fill(Qt::transparent);
return cachePixmap;
}
static inline QColor backgroundColor(const QPalette &pal, const QWidget* widget)
{
#if QT_CONFIG(scrollarea)
if (qobject_cast<const QScrollBar *>(widget) && widget->parent() &&
qobject_cast<const QAbstractScrollArea *>(widget->parent()->parent()))
return widget->parentWidget()->parentWidget()->palette().color(QPalette::Base);
#else
Q_UNUSED(widget);
#endif
return pal.color(QPalette::Base);
}
static inline QPixmap svg2Pixmap(const QString& svgContent,
const QSize& size,
QPainter::CompositionMode mode = QPainter::CompositionMode_SourceOver)
{
QSvgRenderer rr(svgContent);
QImage image(size.width(), size.height(), QImage::Format_ARGB32);
QPainter painter(&image);
painter.setCompositionMode(mode);
image.fill(Qt::transparent);
rr.render(&painter);
return QPixmap::fromImage(image);
}
static inline QScreen* getCurrentScreen() {
//note: Using cursor pos as screen detector. Flawed.
QPoint cursorPos = QCursor::pos();
for (QScreen *screen : QGuiApplication::screens()) {
QRect screenRect = screen->geometry();
if (screenRect.contains(cursorPos)) {
return screen;
}
}
return QGuiApplication::primaryScreen();
}
}

903
src/qt/qtvisuals.h Normal file
View file

@ -0,0 +1,903 @@
#pragma once
#include "qtcommon.h"
#include "meterslider.h"
using namespace StylingHelper;
class MixerStyle : public QProxyStyle {
public:
using QProxyStyle::QProxyStyle;
//void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *option,
// QPainter *painter, const QWidget *widget) const override;
//Click on slider = move handle to click pos
int styleHint(QStyle::StyleHint hint, const QStyleOption* option = 0, const QWidget* widget = 0,
QStyleHintReturn* returnData = 0) const {
if (hint == QStyle::SH_Slider_AbsoluteSetButtons)
return (Qt::LeftButton | Qt::MiddleButton | Qt::RightButton);
return baseStyle()->styleHint(hint, option, widget, returnData);
}
QRect subControlRect(ComplexControl control,
const QStyleOptionComplex *option,
SubControl subControl,
const QWidget *widget) const {
QRect rect = QCommonStyle::subControlRect(CC_Slider, option, subControl, widget);
switch (control) {
#if QT_CONFIG(slider)
case CC_MeterSlider:
if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
int tickSize = proxy()->pixelMetric(PM_SliderTickmarkOffset, option, widget);
switch (subControl) {
case SC_SliderHandle: {
const bool bothTicks = (slider->tickPosition & QSlider::TicksBothSides) == QSlider::TicksBothSides;
if (slider->orientation == Qt::Horizontal) {
rect.setHeight(baseStyle()->pixelMetric(PM_SliderThickness, option, widget));
rect.setWidth(baseStyle()->pixelMetric(PM_SliderLength, option, widget));
int centerY = slider->rect.center().y() - rect.height() / 2;
if (!bothTicks) {
if (slider->tickPosition & QSlider::TicksAbove)
centerY += tickSize;
if (slider->tickPosition & QSlider::TicksBelow)
centerY -= tickSize - 1;
}
rect.moveTop(centerY);
} else {
rect.setWidth(baseStyle()->pixelMetric(PM_SliderThickness, option, widget));
rect.setHeight(baseStyle()->pixelMetric(PM_SliderLength, option, widget));
int centerX = slider->rect.center().x() - rect.width() / 2;
if (!bothTicks) {
if (slider->tickPosition & QSlider::TicksAbove)
centerX += tickSize;
if (slider->tickPosition & QSlider::TicksBelow)
centerX -= tickSize - 1;
}
rect.moveLeft(centerX);
}
}
break;
case SC_SliderGroove: {
QPoint grooveCenter = slider->rect.center();
//rect.setWidth(std::abs(grooveCenter.x()) * 2);
const int grooveThickness = dpiScaled(7, option); //QStyleHelper::dpiScaled(7, option);
const bool bothTicks = (slider->tickPosition & QSlider::TicksBothSides) == QSlider::TicksBothSides;
if (slider->orientation == Qt::Horizontal) {
rect.setHeight(grooveThickness);
if (!bothTicks) {
if (slider->tickPosition & QSlider::TicksAbove)
grooveCenter.ry() += tickSize;
if (slider->tickPosition & QSlider::TicksBelow)
grooveCenter.ry() -= tickSize - 1;
}
} else {
rect.setWidth(grooveThickness);
if (!bothTicks) {
if (slider->tickPosition & QSlider::TicksAbove)
grooveCenter.rx() += tickSize;
if (slider->tickPosition & QSlider::TicksBelow)
grooveCenter.rx() -= tickSize - 1;
}
}
rect.moveCenter(grooveCenter);
break;
}
default:
break;
}
}
return rect;
break;
#endif // QT_CONFIG(slider)
#if QT_CONFIG(scrollbar)
case CC_ScrollBar:
if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
QRect scrollBarRect = scrollbar->rect;
const uint64_t offset = 7;
scrollBarRect.setWidth(scrollBarRect.width() / 2);
scrollBarRect.setHeight(scrollBarRect.height() - 15);
int maxlen = ((scrollbar->orientation == Qt::Horizontal) ?
scrollBarRect.width() : scrollBarRect.height());
int sliderlen;
// calculate slider length
if (scrollbar->maximum != scrollbar->minimum) {
uint range = scrollbar->maximum - scrollbar->minimum;
sliderlen = (qint64(scrollbar->pageStep) * maxlen) / (range + scrollbar->pageStep);
int slidermin = baseStyle()->pixelMetric(PM_ScrollBarSliderMin, scrollbar, widget);
if (sliderlen < slidermin || range > INT_MAX / 2)
sliderlen = slidermin;
if (sliderlen > maxlen)
sliderlen = maxlen;
} else {
sliderlen = maxlen;
}
int sliderstart = sliderPositionFromValue(scrollbar->minimum,
scrollbar->maximum,
scrollbar->sliderPosition,
maxlen - sliderlen,
scrollbar->upsideDown);
switch (subControl) {
/*
* case SC_ScrollBarSubLine: // top/left button
* if (scrollbar->orientation == Qt::Horizontal) {
* int buttonWidth = qMin(scrollBarRect.width() / 2, sbextent);
* rect.setRect(0, 0, buttonWidth, scrollBarRect.height());
* } else {
* int buttonHeight = qMin(scrollBarRect.height() / 2, sbextent);
* rect.setRect(0, 0, scrollBarRect.width(), buttonHeight);
* }
* break;
* case SC_ScrollBarAddLine: // bottom/right button
* if (scrollbar->orientation == Qt::Horizontal) {
* int buttonWidth = qMin(scrollBarRect.width()/2, sbextent);
* rect.setRect(scrollBarRect.width() - buttonWidth, 0, buttonWidth, scrollBarRect.height());
* } else {
* int buttonHeight = qMin(scrollBarRect.height()/2, sbextent);
* rect.setRect(0, scrollBarRect.height() - buttonHeight, scrollBarRect.width(), buttonHeight);
* }
* break;
*/
case SC_ScrollBarSubPage: // between top/left button and slider
if (scrollbar->orientation == Qt::Horizontal)
rect.setRect(0, 0, sliderstart, scrollBarRect.height());
else
rect.setRect(0, 0, scrollBarRect.width(), sliderstart);
break;
case SC_ScrollBarAddPage: // between bottom/right button and slider
if (scrollbar->orientation == Qt::Horizontal)
rect.setRect(sliderstart + sliderlen, 0,
maxlen - sliderstart - sliderlen, scrollBarRect.height());
else
rect.setRect(0, sliderstart + sliderlen, scrollBarRect.width(),
maxlen - sliderstart - sliderlen);
break;
case SC_ScrollBarGroove:
if (scrollbar->orientation == Qt::Horizontal)
rect.setRect(offset, 0, scrollBarRect.width(),
scrollBarRect.height());
else
rect.setRect(0, offset, scrollBarRect.width(),
scrollBarRect.height() );
break;
case SC_ScrollBarSlider:
if (scrollbar->orientation == Qt::Horizontal)
rect.setRect(sliderstart + offset, 0, sliderlen, scrollBarRect.height());
else
rect.setRect(0, sliderstart + offset, scrollBarRect.width(), sliderlen);
break;
default:
break;
}
rect = visualRect(scrollbar->direction, scrollBarRect, rect);
return rect;
}
break;
#endif // QT_CONFIG(scrollbar)
default:
return baseStyle()->subControlRect(control, option, subControl, widget);
break;
}
return QRect();
}
void drawControl(ControlElement element, const QStyleOption *opt,
QPainter *p, const QWidget *widget) const
{
switch(element) {
case CE_ExtendedCheckBox:
if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
QStyleOptionButton subopt = *btn;
subopt.rect = subElementRect(SE_CheckBoxIndicator, btn, widget);
//proxy()->drawPrimitive(PE_IndicatorCheckBox, &subopt, p, widget);
subopt.rect = subElementRect(SE_CheckBoxContents, btn, widget);
//proxy()->drawControl(CE_CheckBoxLabel, &subopt, p, widget);
int alignment = visualAlignment(btn->direction, Qt::AlignLeft | Qt::AlignVCenter);
if (!proxy()->styleHint(SH_UnderlineShortcut, btn, widget))
alignment |= Qt::TextHideMnemonic;
QPixmap pix;
QRect textRect = btn->rect;
if (!btn->icon.isNull()) {
pix = btn->icon.pixmap(btn->iconSize, StylingHelper::getCurrentScreen()->devicePixelRatio(),
QIcon::Mode::Normal, btn->state & State_On ? QIcon::On : QIcon::Off);
proxy()->drawItemPixmap(p, btn->rect, alignment, pix);
if (btn->direction == Qt::RightToLeft)
textRect.setRight(textRect.right() - btn->iconSize.width() - 4);
else
textRect.setLeft(textRect.left() + btn->iconSize.width() + 4);
}
if (!btn->text.isEmpty()){
proxy()->drawItemText(p, textRect, alignment | Qt::TextShowMnemonic,
btn->palette, btn->state & State_Enabled, btn->text, QPalette::WindowText);
}
//
if (btn->state & State_HasFocus) {
QStyleOptionFocusRect fropt;
fropt.QStyleOption::operator=(*btn);
fropt.rect = subElementRect(SE_CheckBoxFocusRect, btn, widget);
proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, p, widget);
}
}
default:
baseStyle()->drawControl(element, opt, p, widget);
break;
}
}
void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *option,
QPainter *painter, const QWidget *widget) const {
QColor outline;
switch (cc) {
#if QT_CONFIG(slider)
case CC_MeterSlider:
if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
const qreal dpr = painter->device()->devicePixelRatio();
const QColor buttonColor = getButtonColor(option->palette);
QRect groove = this->subControlRect(static_cast<QStyle::ComplexControl>(CC_MeterSlider), option, SC_SliderGroove, widget);
QRect handle = this->subControlRect(static_cast<QStyle::ComplexControl>(CC_MeterSlider), option, SC_SliderHandle, widget);
bool horizontal = slider->orientation == Qt::Horizontal;
bool ticksAbove = slider->tickPosition & QSlider::TicksAbove;
bool ticksBelow = slider->tickPosition & QSlider::TicksBelow;
QColor activeHighlight = highlight(option->palette);
QPixmap cache;
QBrush oldBrush = painter->brush();
QPen oldPen = painter->pen();
QColor shadowAlpha(Qt::black);
shadowAlpha.setAlpha(10);
if (option->state & State_HasFocus && option->state & State_KeyboardFocusChange)
outline = highlightedOutline(option->palette);
if ((option->subControls & SC_SliderGroove) && groove.isValid()) {
QColor grooveColor;
grooveColor.setHsv(buttonColor.hue(),
qMin(255, (int)(buttonColor.saturation())),
qMin(255, (int)(buttonColor.value()*0.9)));
QString groovePixmapName = "slider_groove" + QString::number(groove.width());//QStyleHelper::uniqueName("slider_groove"_L1, option, groove.size(), dpr);
QRect pixmapRect(0, 0, groove.width(), groove.height());
// draw background groove
if (!QPixmapCache::find(groovePixmapName, &cache)) {
cache = styleCachePixmap(pixmapRect.size(), dpr);
QPainter groovePainter(&cache);
groovePainter.setRenderHint(QPainter::Antialiasing, true);
groovePainter.translate(0.5, 0.5);
QLinearGradient gradient;
if (horizontal) {
gradient.setStart(pixmapRect.center().x(), pixmapRect.top());
gradient.setFinalStop(pixmapRect.center().x(), pixmapRect.bottom());
}
else {
gradient.setStart(pixmapRect.left(), pixmapRect.center().y());
gradient.setFinalStop(pixmapRect.right(), pixmapRect.center().y());
}
groovePainter.setPen(QPen(outline));
gradient.setColorAt(0, grooveColor.darker(110));
gradient.setColorAt(1, grooveColor.lighter(110));//palette.button().color().darker(115));
groovePainter.setBrush(gradient);
groovePainter.drawRoundedRect(pixmapRect.adjusted(1, 1, -2, -2), 1, 1);
groovePainter.end();
QPixmapCache::insert(groovePixmapName, cache);
}
painter->drawPixmap(groove.topLeft(), cache);
// draw groove lines (vol + unattenuated vol)
const MeterSlider* widgetCast = qobject_cast<const MeterSlider*>(widget);
if (widgetCast) {
QRect clipRect;
//if (!groovePixmapName.isEmpty()) groovePixmapName += QLatin1String("_unattenuated");
//groovePixmapName += "_unattenuated"_L1;
//lf (!QPixmapCache::find(groovePixmapName, &cache)) {
cache = styleCachePixmap(pixmapRect.size(), dpr);
QPainter groovePainter(&cache);
QLinearGradient gradient;
if (horizontal) {
gradient.setStart(pixmapRect.center().x(), pixmapRect.top());
gradient.setFinalStop(pixmapRect.center().x(), pixmapRect.bottom());
}
else {
gradient.setStart(pixmapRect.left(), pixmapRect.center().y());
gradient.setFinalStop(pixmapRect.right(), pixmapRect.center().y());
}
QColor highlight = QColor(30,30,30,100); //this->highlight(option->palette);
QColor highlightedoutline = highlight.darker(140);
QColor grooveOutline = outline;
if (qGray(grooveOutline.rgb()) > qGray(highlightedoutline.rgb()))
grooveOutline = highlightedoutline;
float peakValue = ((MeterSlider*)widget)->peakValue;
groovePainter.setRenderHint(QPainter::Antialiasing, true);
groovePainter.translate(0.5, 0.5);
groovePainter.setPen(QPen(grooveOutline));
gradient.setColorAt(0, highlight);
gradient.setColorAt(1, highlight.lighter(130));
groovePainter.setBrush(gradient);
groovePainter.drawRoundedRect(pixmapRect.adjusted(
1, 1,
-pixmapRect.width() + (pixmapRect.width() * peakValue),
-2), 1, 1);
groovePainter.setPen(innerContrastLine());
groovePainter.setBrush(Qt::green);
double stepWidth = (double)widgetCast->width() * ((double)widgetCast->singleStep() / (widgetCast->maximum() - widgetCast->minimum()));
groovePainter.drawRoundedRect(pixmapRect.adjusted(
1, 1,
-pixmapRect.width() + ((widgetCast->width() - ((widgetCast->maximum() - widgetCast->value()) * stepWidth)) * peakValue),
-2), 1, 1);
groovePainter.end();
//QPixmapCache::insert(groovePixmapName, cache);
//}
//}
if (horizontal) {
if (slider->upsideDown)
clipRect = QRect(handle.right(), groove.top(), groove.right() - handle.right(), groove.height());
else
clipRect = QRect(groove.left() + 2, groove.top() + 2,
groove.right() - 2, 2);
} else {
if (slider->upsideDown)
clipRect = QRect(groove.left(), handle.bottom(), groove.width(), groove.height() - (handle.bottom() - slider->rect.top()));
else
clipRect = QRect(groove.left(), groove.top(), groove.width(), handle.top() - groove.top());
}
painter->save();
painter->setClipRect(clipRect.adjusted(0, 0, 1, 1), Qt::IntersectClip);
painter->drawPixmap(groove.topLeft(), cache);
painter->restore();
}
}
if (option->subControls & SC_SliderTickmarks) {
painter->save();
painter->translate(slider->rect.x(), slider->rect.y());
painter->setPen(outline);
int tickSize = proxy()->pixelMetric(PM_SliderTickmarkOffset, option, widget);
int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget);
int interval = slider->tickInterval;
if (interval <= 0) {
interval = slider->singleStep;
if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval,
available)
- QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
0, available) < 3)
interval = slider->pageStep;
}
if (interval <= 0)
interval = 1;
int v = slider->minimum;
int len = proxy()->pixelMetric(PM_SliderLength, slider, widget);
QVector<QLine> lines;
while (v <= slider->maximum + 1) {
if (v == slider->maximum + 1 && interval == 1)
break;
const int v_ = qMin(v, slider->maximum);
int pos = sliderPositionFromValue(slider->minimum, slider->maximum,
v_, (horizontal
? slider->rect.width()
: slider->rect.height()) - len,
slider->upsideDown) + len / 2;
int extra = 2 - ((v_ == slider->minimum || v_ == slider->maximum) ? 1 : 0);
if (horizontal) {
if (ticksAbove) {
lines += QLine(pos, slider->rect.top() + extra,
pos, slider->rect.top() + tickSize);
}
if (ticksBelow) {
lines += QLine(pos, slider->rect.bottom() - extra,
pos, slider->rect.bottom() - tickSize);
}
} else {
if (ticksAbove) {
lines += QLine(slider->rect.left() + extra, pos,
slider->rect.left() + tickSize, pos);
}
if (ticksBelow) {
lines += QLine(slider->rect.right() - extra, pos,
slider->rect.right() - tickSize, pos);
}
}
// in the case where maximum is max int
int nextInterval = v + interval;
if (nextInterval < v)
break;
v = nextInterval;
}
painter->drawLines(lines);
painter->restore();
}
// draw handle
if ((option->subControls & SC_SliderHandle) ) {
QString handlePixmapName = "slider_handle" + QString::number(handle.height());//QStyleHelper::uniqueName("slider_handle"_L1, option,//handle.size(), dpr);
if (!QPixmapCache::find(handlePixmapName, &cache)) {
cache = styleCachePixmap(handle.size(), dpr);
QRect pixmapRect(0, 0, handle.width(), handle.height());
QPainter handlePainter(&cache);
QRect gradRect = pixmapRect.adjusted(2, 2, -2, -2);
// gradient fill
QRect r = pixmapRect.adjusted(1, 1, -2, -2);
QLinearGradient gradient = qt_fusion_gradient(gradRect, getButtonColor(option->palette),horizontal ? TopDown : FromLeft);
handlePainter.setRenderHint(QPainter::Antialiasing, true);
handlePainter.translate(0.5, 0.5);
handlePainter.setPen(Qt::NoPen);
handlePainter.setBrush(QColor(0, 0, 0, 40));
handlePainter.drawRect(horizontal ? r.adjusted(-1, 2, 1, -2) : r.adjusted(2, -1, -2, 1));
handlePainter.setPen(QPen(getOutline(option->palette)));
if (option->state & State_HasFocus && option->state & State_KeyboardFocusChange)
handlePainter.setPen(QPen(highlightedOutline(option->palette)));
handlePainter.setBrush(gradient);
handlePainter.drawRoundedRect(r, 2, 2);
handlePainter.setBrush(Qt::NoBrush);
handlePainter.setPen(innerContrastLine());
handlePainter.drawRoundedRect(r.adjusted(1, 1, -1, -1), 2, 2);
QColor cornerAlpha = outline.darker(120);
cornerAlpha.setAlpha(80);
//handle shadow
handlePainter.setPen(shadowAlpha);
handlePainter.drawLine(QPoint(r.left() + 2, r.bottom() + 1), QPoint(r.right() - 2, r.bottom() + 1));
handlePainter.drawLine(QPoint(r.right() + 1, r.bottom() - 3), QPoint(r.right() + 1, r.top() + 4));
handlePainter.drawLine(QPoint(r.right() - 1, r.bottom()), QPoint(r.right() + 1, r.bottom() - 2));
handlePainter.end();
QPixmapCache::insert(handlePixmapName, cache);
}
painter->drawPixmap(handle.topLeft(), cache);
}
painter->setBrush(oldBrush);
painter->setPen(oldPen);
}
break;
case CC_ScrollBar:
painter->save();
if (const QStyleOptionSlider *scrollBar = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
bool wasActive = false;
qreal expandScale = 1.0;
qreal expandOffset = -1.0;
QObject *styleObject = option->styleObject;
QColor buttonColor = calculateButtonColor(option->palette);
QColor gradientStartColor = buttonColor.lighter(104);
QColor gradientStopColor = buttonColor.darker(102);
bool transient = styleHint(SH_ScrollBar_Transient, option, widget);
bool horizontal = scrollBar->orientation == Qt::Horizontal;
bool sunken = scrollBar->state & State_Sunken;
QRect scrollBarSubLine = subControlRect(cc, scrollBar, SC_ScrollBarSubLine, widget);
QRect scrollBarAddLine = subControlRect(cc, scrollBar, SC_ScrollBarAddLine, widget);
QRect scrollBarSlider = subControlRect(cc, scrollBar, SC_ScrollBarSlider, widget);
QRect scrollBarGroove = subControlRect(cc, scrollBar, SC_ScrollBarGroove, widget);
QRect rect = option->rect;
outline = highlightedOutline(option->palette);
QColor alphaOutline = outline;
alphaOutline.setAlpha(180);
QColor arrowColor = option->palette.windowText().color();
//QColor arrowColor = QColor(188,143,143,100);
arrowColor.setAlpha(160);
const QColor appBgColor = QGuiApplication::palette().window().color();
const bool isDarkBg = appBgColor.red() < 128 && appBgColor.green() < 128 && appBgColor.blue() < 128;
if (transient) {
if (horizontal) {
rect.setY(rect.y() + 4.5 - expandOffset);
scrollBarSlider.setY(scrollBarSlider.y() + 4.5 - expandOffset);
scrollBarGroove.setY(scrollBarGroove.y() + 4.5 - expandOffset);
rect.setHeight(rect.height() * expandScale);
scrollBarGroove.setHeight(scrollBarGroove.height() * expandScale);
} else {
rect.setX(rect.x() + 4.5 - expandOffset);
scrollBarSlider.setX(scrollBarSlider.x() + 4.5 - expandOffset);
scrollBarGroove.setX(scrollBarGroove.x() + 4.5 - expandOffset);
rect.setWidth(rect.width() * expandScale);
scrollBarGroove.setWidth(scrollBarGroove.width() * expandScale);
}
}
// Paint groove
if ((!transient || scrollBar->activeSubControls || wasActive) && scrollBar->subControls & SC_ScrollBarGroove) {
QLinearGradient gradient(rect.center().x(), rect.top(),
rect.center().x(), rect.bottom());
if (!horizontal)
gradient = QLinearGradient(rect.left(), rect.center().y(),
rect.right(), rect.center().y());
if (!transient || !isDarkBg) {
gradient.setColorAt(0, buttonColor.darker(107));
gradient.setColorAt(0.1, buttonColor.darker(105));
gradient.setColorAt(0.9, buttonColor.darker(105));
gradient.setColorAt(1, buttonColor.darker(107));
} else {
gradient.setColorAt(0, appBgColor.lighter(157));
gradient.setColorAt(0.1, appBgColor.lighter(155));
gradient.setColorAt(0.9, appBgColor.lighter(155));
gradient.setColorAt(1, appBgColor.lighter(157));
}
painter->save();
//painter->fillRect(rect, gradient);
painter->setPen(Qt::NoPen);
painter->setPen(alphaOutline);
QColor subtleEdge = alphaOutline;
subtleEdge.setAlpha(40);
painter->setPen(subtleEdge);
//painter->setBrush(Qt::NoBrush);
painter->setOpacity(0.3);
painter->setBrush(Qt::gray);
painter->drawRoundedRect(scrollBarGroove, 5, 5);
//painter->drawRoundedRect(scrollBarGroove, 5, 5);
painter->restore();
}
QRect pixmapRect = scrollBarSlider;
QColor highlightColor = option->palette.highlight().color();
/*
* QLinearGradient gradient(pixmapRect.center().x(), pixmapRect.top(),
* pixmapRect.center().x(), pixmapRect.bottom());
* if (!horizontal)
* gradient = QLinearGradient(pixmapRect.left(), pixmapRect.center().y(),
* pixmapRect.right(), pixmapRect.center().y());
*/
//QLinearGradient highlightedGradient = gradient;
QColor heldColor = 0xc4e3d7e0;//Qt::gray;//osh->isLightMode() ? Qt::white : Qt::black;
//gradient.setColorAt(0, option->palette.highlight().color());
//gradient.setColorAt(1, option->palette.highlight().color());
//highlightedGradient.setColorAt(0, gradientStartColor.darker(102));
//highlightedGradient.setColorAt(1, gradientStopColor.lighter(102));
// Paint slider
if (scrollBar->subControls & SC_ScrollBarSlider) {
log_debugcpp("Final scrollbar paint if");
if (transient) {
QRect rect = scrollBarSlider.adjusted(horizontal ? 1 : 2, horizontal ? 2 : 1, -1, -1);
painter->setPen(Qt::NoPen);
painter->setBrush(isDarkBg ? lightShade() : darkShade());
int r = qMin(rect.width(), rect.height()) / 2;
painter->save();
painter->setRenderHint(QPainter::Antialiasing, true);
painter->drawRoundedRect(rect, r, r);
painter->restore();
} else {
QRect pixmapRect = scrollBarSlider;
painter->setPen(QPen(alphaOutline));
if (option->state & State_Sunken
&& scrollBar->activeSubControls & SC_ScrollBarSlider)
painter->setBrush(heldColor);
else if (option->state & State_MouseOver
&& scrollBar->activeSubControls & SC_ScrollBarSlider) {
painter->setBrush(highlightColor);
}
else //if (!isDarkBg)
if(!isDarkBg)
painter->setBrush(Qt::black);
else painter->setBrush(Qt::white);
//else
// painter->setBrush(heldColor);
painter->drawRoundedRect(pixmapRect.adjusted(horizontal ? -1 : 0, horizontal ? 0 : -1, horizontal ? 0 : -1, horizontal ? -1 : 0), 5, 5);
painter->setPen(innerContrastLine());
painter->drawRoundedRect(scrollBarSlider.adjusted(horizontal ? 0 : 1, horizontal ? 1 : 0, -1, -1), 5, 5);
// Outer shadow
// painter->setPen(subtleEdge);
// if (horizontal) {
//// painter->drawLine(scrollBarSlider.topLeft() + QPoint(-2, 0), scrollBarSlider.bottomLeft() + QPoint(2, 0));
//// painter->drawLine(scrollBarSlider.topRight() + QPoint(-2, 0), scrollBarSlider.bottomRight() + QPoint(2, 0));
// } else {
//// painter->drawLine(pixmapRect.topLeft() + QPoint(0, -2), pixmapRect.bottomLeft() + QPoint(0, -2));
//// painter->drawLine(pixmapRect.topRight() + QPoint(0, 2), pixmapRect.bottomRight() + QPoint(0, 2));
// }
}
}
/*
* // The SubLine (up/left) buttons
* if (!transient && scrollBar->subControls & SC_ScrollBarSubLine) {
* if ((scrollBar->activeSubControls & SC_ScrollBarSubLine) && sunken)
* painter->setBrush(gradientStopColor);
* else if ((scrollBar->activeSubControls & SC_ScrollBarSubLine))
* painter->setBrush(highlightedGradient);
* else
* painter->setBrush(gradient);
*
* painter->setPen(Qt::NoPen);
* painter->drawRoundedRect(scrollBarSubLine.adjusted(horizontal ? 0 : 1, horizontal ? 1 : 0, 0, 0), 1, 1);
* painter->setPen(QPen(alphaOutline));
* if (option->state & State_Horizontal) {
* painter->drawRect(scrollBarSubLine.adjusted(horizontal ? 0 : 1, 0, horizontal ? 1 : 0, horizontal ? -1 : 0));
* } else {
* painter->drawRoundedRect(scrollBarSubLine.adjusted(0, 0, horizontal ? 0 : -1, 0), 1, 1);
* }
*
* QRect upRect = scrollBarSubLine.adjusted(horizontal ? 0 : 1, horizontal ? 1 : 0, horizontal ? -2 : -1, horizontal ? -1 : -2);
* painter->setBrush(Qt::NoBrush);
* painter->setPen(innerContrastLine());
* painter->drawRect(upRect);
*
* // Arrows
* Qt::ArrowType arrowType = Qt::UpArrow;
* if (option->state & State_Horizontal)
* arrowType = option->direction == Qt::LeftToRight ? Qt::LeftArrow : Qt::RightArrow;
* qt_fusion_draw_arrow(arrowType, painter, option, upRect, arrowColor);
* }
*
* // The AddLine (down/right) button
* if (!transient && scrollBar->subControls & SC_ScrollBarAddLine) {
* if ((scrollBar->activeSubControls & SC_ScrollBarAddLine) && sunken)
* painter->setBrush(gradientStopColor);
* else if ((scrollBar->activeSubControls & SC_ScrollBarAddLine))
* painter->setBrush(midColor2);
* else
* painter->setBrush(gradient);
*
* painter->setPen(Qt::NoPen);
* painter->drawRect(scrollBarAddLine.adjusted(horizontal ? 0 : 1, horizontal ? 1 : 0, 0, 0));
* painter->setPen(QPen(alphaOutline, 1));
* if (option->state & State_Horizontal) {
* painter->drawRect(scrollBarAddLine.adjusted(horizontal ? -1 : 0, 0, horizontal ? -1 : 0, horizontal ? -1 : 0));
* } else {
* painter->drawRect(scrollBarAddLine.adjusted(0, horizontal ? 0 : -1, horizontal ? 0 : -1, horizontal ? 0 : -1));
* }
*
* QRect downRect = scrollBarAddLine.adjusted(1, 1, -1, -1);
* painter->setPen(innerContrastLine());
* painter->setBrush(Qt::NoBrush);
* painter->drawRect(downRect);
*
* Qt::ArrowType arrowType = Qt::DownArrow;
* if (option->state & State_Horizontal)
* arrowType = option->direction == Qt::LeftToRight ? Qt::RightArrow : Qt::LeftArrow;
* qt_fusion_draw_arrow(arrowType, painter, option, downRect, arrowColor);
* }
*/
}
painter->restore();
break;
default:
baseStyle()->drawComplexControl(cc, option, painter, widget);
#endif // QT_CONFIG(slider)
}
}
private:
enum Direction {
TopDown,
FromLeft,
BottomUp,
FromRight
};
static QLinearGradient qt_fusion_gradient(const QRect &rect, const QBrush &baseColor, Direction direction = TopDown)
{
int x = rect.center().x();
int y = rect.center().y();
QLinearGradient gradient;
switch (direction) {
case FromLeft:
gradient = QLinearGradient(rect.left(), y, rect.right(), y);
break;
case FromRight:
gradient = QLinearGradient(rect.right(), y, rect.left(), y);
break;
case BottomUp:
gradient = QLinearGradient(x, rect.bottom(), x, rect.top());
break;
case TopDown:
default:
gradient = QLinearGradient(x, rect.top(), x, rect.bottom());
break;
}
if (baseColor.gradient())
gradient.setStops(baseColor.gradient()->stops());
else {
QColor gradientStartColor = baseColor.color().lighter(124);
QColor gradientStopColor = baseColor.color().lighter(102);
gradient.setColorAt(0, gradientStartColor);
gradient.setColorAt(1, gradientStopColor);
// Uncomment for adding shiny shading
// QColor midColor1 = mergedColors(gradientStartColor, gradientStopColor, 55);
// QColor midColor2 = mergedColors(gradientStartColor, gradientStopColor, 45);
// gradient.setColorAt(0.5, midColor1);
// gradient.setColorAt(0.501, midColor2);
}
return gradient;
}
QColor highlightedOutline(const QPalette &pal) const {
QColor highlightedOutline = highlight(pal);//.darker(25);//QColor(Qt::green);
if (highlightedOutline.value() > 160)
highlightedOutline.setHsl(highlightedOutline.hue(), highlightedOutline.saturation(), 160);
return highlightedOutline;
}
QColor calculateButtonColor(const QPalette &pal) const {
QColor buttonColor = pal.button().color();
int val = qGray(buttonColor.rgb());
buttonColor = buttonColor.lighter(100 + qMax(1, (180 - val)/6));
buttonColor.setHsv(buttonColor.hue(), buttonColor.saturation() * 0.75, buttonColor.value());
return buttonColor;
}
// Used for grip handles
QColor lightShade() const {
return QColor(255, 255, 255, 90);
}
QColor darkShade() const {
return QColor(0, 0, 0, 60);
}
QColor innerContrastLine() const {
return QColor(255, 255, 255, 30);
}
static QColor mergedColors(const QColor &colorA, const QColor &colorB, int factor = 50) {
const int maxFactor = 100;
QColor tmp = colorA;
tmp.setRed((tmp.red() * factor) / maxFactor + (colorB.red() * (maxFactor - factor)) / maxFactor);
tmp.setGreen((tmp.green() * factor) / maxFactor + (colorB.green() * (maxFactor - factor)) / maxFactor);
tmp.setBlue((tmp.blue() * factor) / maxFactor + (colorB.blue() * (maxFactor - factor)) / maxFactor);
return tmp;
}
static void qt_fusion_draw_arrow(Qt::ArrowType type, QPainter *painter, const QStyleOption *option, const QRect &rect, const QColor &color)
{
if (rect.isEmpty())
return;
const qreal dpi = calculateDpi(option);
const qreal dpr = painter->device()->devicePixelRatio();
const int arrowWidth = int(dpiScaled(14, dpi));
const int arrowHeight = int(dpiScaled(8, dpi));
const int arrowMax = qMin(arrowHeight, arrowWidth);
const int rectMax = qMin(rect.height(), rect.width());
const int size = qMin(arrowMax, rectMax);
QPixmap cachePixmap;
const QString cacheKey = "fusion-arrow"_L1 + QString::number(rect.size().width()) + QString::number(rect.size().height()) + QString::number(type);
if (!QPixmapCache::find(cacheKey, &cachePixmap)) {
cachePixmap = styleCachePixmap(rect.size(), dpr);
QPainter cachePainter(&cachePixmap);
QRectF arrowRect;
arrowRect.setWidth(size);
arrowRect.setHeight(arrowHeight * size / arrowWidth);
if (type == Qt::LeftArrow || type == Qt::RightArrow)
arrowRect = arrowRect.transposed();
arrowRect.moveTo((rect.width() - arrowRect.width()) / 2.0,
(rect.height() - arrowRect.height()) / 2.0);
std::array<QPointF, 3> triangle;
switch (type) {
case Qt::DownArrow:
triangle = {arrowRect.topLeft(), arrowRect.topRight(), QPointF(arrowRect.center().x(), arrowRect.bottom())};
break;
case Qt::RightArrow:
triangle = {arrowRect.topLeft(), arrowRect.bottomLeft(), QPointF(arrowRect.right(), arrowRect.center().y())};
break;
case Qt::LeftArrow:
triangle = {arrowRect.topRight(), arrowRect.bottomRight(), QPointF(arrowRect.left(), arrowRect.center().y())};
break;
default:
triangle = {arrowRect.bottomLeft(), arrowRect.bottomRight(), QPointF(arrowRect.center().x(), arrowRect.top())};
break;
}
cachePainter.setPen(Qt::NoPen);
cachePainter.setBrush(color);
cachePainter.setRenderHint(QPainter::Antialiasing);
cachePainter.drawPolygon(triangle.data(), int(triangle.size()));
QPixmapCache::insert(cacheKey, cachePixmap);
}
painter->drawPixmap(rect, cachePixmap);
}
};
/*
* void MixerStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,
* QPainter *p, const QWidget *widget) const {
* switch (cc) {
* #if QT_CONFIG(slider)
* case CC_Slider:
* if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
* if (slider->subControls == SC_SliderTickmarks) {
* int tickOffset = proxy()->pixelMetric(PM_SliderTickmarkOffset, slider, widget);
* int ticks = slider->tickPosition;
* int thickness = proxy()->pixelMetric(PM_SliderControlThickness, slider, widget);
* int len = proxy()->pixelMetric(PM_SliderLength, slider, widget);
* int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget);
* int interval = slider->tickInterval;
* if (interval <= 0) {
* interval = slider->singleStep;
* if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval,
* available)
* - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
* 0, available) < 3)
* interval = slider->pageStep;
* }
* if (!interval)
* interval = 1;
* int fudge = len / 2;
* int pos;
* // Since there is no subrect for tickmarks do a translation here.
* p->save();
* p->translate(slider->rect.x(), slider->rect.y());
* p->setPen(slider->palette.windowText().color());
* int v = slider->minimum;
* while (v <= slider->maximum + 1) {
* if (v == slider->maximum + 1 && interval == 1)
* break;
* const int v_ = qMin(v, slider->maximum);
* pos = QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
* v_, available) + fudge;
* if (slider->orientation == Qt::Horizontal) {
* if (ticks & QSlider::TicksAbove)
* p->drawLine(pos, 0, pos, tickOffset - 2);
* if (ticks & QSlider::TicksBelow)
* p->drawLine(pos, tickOffset + thickness + 1, pos,
* slider->rect.height()-1);
* } else {
* if (ticks & QSlider::TicksAbove)
* p->drawLine(0, pos, tickOffset - 2, pos);
* if (ticks & QSlider::TicksBelow)
* p->drawLine(tickOffset + thickness + 1, pos,
* slider->rect.width()-1, pos);
* }
* // in the case where maximum is max int
* int nextInterval = v + interval;
* if (nextInterval < v)
* break;
* v = nextInterval;
* }
* }
* }
* p->restore();
* break;
* default:
* baseStyle()->drawComplexControl(cc, opt, p, widget);
* break;
* }
* #endif // QT_CONFIG(slider)
* }
*/
//void MixerStyle::

View file

@ -1,22 +1,22 @@
#include <QApplication>
#include <QMainWindow>
#include <QLocalSocket>
#include <QLocalServer>
#include <QString>
#include <QFile>
//#include "contclasses.h" //#include "contclasses.h"
#define INIT_FILELOG
#include "qtcommon.h"
#include "qtclasses.h" #include "qtclasses.h"
#include "global.h" #include "qtvisuals.h"
#include "settings.h"
OverseerHandler *osh = nullptr; OverseerHandler *osh = nullptr;
ini::UserSettings *set = nullptr;
QApplication* createApplication(int &argc, char *argv[]) bool startupRun = false;
{ bool onStartup = false;
char* userSettingsPath = nullptr;
QApplication* createApplication(int &argc, char *argv[]) {
return new QApplication(argc, argv); return new QApplication(argc, argv);
} }
bool isSingleInstanceRunning(QString appName) { bool isInstanceRunning(QString appName) {
QLocalSocket socket; QLocalSocket socket;
socket.connectToServer(appName); socket.connectToServer(appName);
bool isOpen = socket.isOpen(); bool isOpen = socket.isOpen();
@ -24,27 +24,104 @@ bool isSingleInstanceRunning(QString appName) {
return isOpen; return isOpen;
} }
QLocalServer* startSingleInstanceServer(QString appName) { QLocalServer* startInstanceServer(QString appName) {
QLocalServer* server = new QLocalServer; QLocalServer* server = new QLocalServer;
server->setSocketOptions(QLocalServer::WorldAccessOption); server->setSocketOptions(QLocalServer::WorldAccessOption);
server->listen(appName); server->listen(appName);
return server; return server;
} }
void closeDebugFileLog() {
close_file_log_buffer();
}
void parseCmdArgs(int argc, char* argv[]) {
if(argc == 1) return;
//char* configPath = nullptr;
char* arg[] = { (char*)"--config-path=", (char*)"--change-startup" };
for (int i = 1; i < argc; i++) {
if(strstr(argv[i], arg[0])) {
userSettingsPath = argv[i] + strlen(arg[0]);
}
if(strstr(argv[i], arg[1])) {
if (++i >= argc) return;
switch (argv[i][0]) {
case '0':
startupRun = true;
onStartup = false;
break;
case '1':
startupRun = true;
onStartup = true;
break;
default:
break;
}
return;
}
}
return;
}
/* set_terminate
* void closeDebugFileLog2() {
* close_file_log_buffer();
* abort();
* }
*/
int main (int argc, char* argv[]) { int main (int argc, char* argv[]) {
//QApplication::setStyle("windowsvista"); /*
* Debug: logging report file
*/
initialize_file_log();
atexit(closeDebugFileLog);
QApplication::setStyle(new MixerStyle(QStyleFactory::create("Fusion")));
//QApplication::setFont(font);
//QPalette palette = QGuiApplication::palette();
//palette.setColor(QPalette::Active, QPalette::Highlight, QColor(255, 192, 203, 200));
//QColor(30,30,30,100));
//QGuiApplication::setPalette(palette);
osh = new OverseerHandler();
osh->populateSystemValues();
/*
* Arg parsing: (admin?) startup change run
*/
parseCmdArgs(argc, argv);
if (startupRun) {
if (!isInstanceRunning(PIPE_NAME)) exit(-1);
//if (startupChangeInfo->permissionChangeScope == NONE) exit(-1);
osh->updateStartupConfig(onStartup);
exit(0);
}
//Check if running //Check if running
//https://stackoverflow.com/questions/48060989/qt-show-application-if-currently-running //https://stackoverflow.com/questions/48060989/qt-show-application-if-currently-running
if (!isSingleInstanceRunning("Mixer")) if (!isInstanceRunning(PIPE_NAME))
startSingleInstanceServer("Mixer"); startInstanceServer(PIPE_NAME);
else exit(0); else exit(0);
osh = new OverseerHandler(); /*
* Config file init
*/
if (userSettingsPath)
set = ini::UserSettings::createSettings(userSettingsPath, true);
if (set)
OverseerHandler::settingsPath = std::string(userSettingsPath);
else setConfigDirToDefaults();
StylingHelper::setBackgroundColor(osh->isLightMode());
//qRegisterMetaType<EndpointWidgetEvent>(); //qRegisterMetaType<EndpointWidgetEvent>();
//INIT CONT //INIT CONT
log_debugcpp("main init"); log_debugcpp("main init");
osh->reloadEndpointHandlers(); osh->createEndpointHandlers();
log_debugcpp("Reloaded endpoint handlers"); log_debugcpp("Reloaded endpoint handlers");
//INIT FRONT //INIT FRONT
@ -53,6 +130,10 @@ int main (int argc, char* argv[]) {
MainWindow window = MainWindow(); MainWindow window = MainWindow();
//window.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::QSizePolicy::MinimumExpanding) //window.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::QSizePolicy::MinimumExpanding)
QApplication::setQuitOnLastWindowClosed(false); QApplication::setQuitOnLastWindowClosed(false);
DarkModeEventFilter* darkMode = new DarkModeEventFilter();
QAbstractEventDispatcher::instance()->installNativeEventFilter(darkMode);
/* /*
* QFile styleFile(":/assets/style.qss"); * QFile styleFile(":/assets/style.qss");
* styleFile.open(QFile::ReadOnly); * styleFile.open(QFile::ReadOnly);

231
src/settings.cpp Normal file
View file

@ -0,0 +1,231 @@
#include "settings.h"
#include "msinclude.h"
namespace ini {
wchar_t* Utf8toUtf16(const char* str, uint64_t* size = nullptr) {
if(!str || str[0] == '\0') return nullptr;
int sizeNeeded = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
if(size) *size = sizeNeeded;
wchar_t* utf16 = (wchar_t*)calloc(sizeNeeded, sizeof(wchar_t));
MultiByteToWideChar(CP_UTF8, 0, str, -1, utf16, sizeNeeded);
return utf16;
}
UserSettings::UserSettings(char* textContents) {
//Parsing values
bool isCRLF = false;
char *curLine = textContents;
char *separator = nullptr, *key = nullptr, *value = nullptr;
while(curLine) {
char* nextLine = strchr(curLine, '\n');
if(nextLine == curLine + 1 || nextLine == curLine + 2)
goto nextIteration;
if (nextLine && (isCRLF || *(nextLine - 1) == '\r')) {
isCRLF = true;
*(nextLine - 1) = '\0';
} else if (nextLine) *nextLine = '\0'; // temporarily terminate the current line
log_debugcpp("[SET] curLine: " + std::string(curLine) + " ");
separator = strchr(curLine, '=');
if(!separator)
goto nextIteration;
*separator = '\0';
key = trimAndAllocate(curLine);
value = trimAndAllocate(separator + 1);
values.try_emplace(key, value);
log_debugcpp("[SET] ini Map size: " + std::to_string(values.size()));
*separator = '=';
nextIteration:
if (nextLine) { // then restore newline-char, just to be tidy
if (isCRLF)
*(nextLine - 1) = '\r';
else *nextLine = '\n';
}
curLine = nextLine ? (nextLine + 1) : NULL;
}
free(textContents);
}
char* const UserSettings::getValue(char* key, uint64_t len) {
if (auto search = values.find(key); search != values.end())
return (char* const) search->second;
return nullptr;
}
void UserSettings::setValue(char* key, char* value, uint64_t valueSize, uint64_t keySize) {
char *newValue, *newKey;
if (auto search = values.find(key); search != values.end()) {
if(!(strcmp(value, search->second))) return;
newValue = (char*)calloc(valueSize, sizeof(char));
if (!(search->second == pos || search->second == neg)) {
free(search->second);
}
search->second = newValue;
return;
}
newValue = (char*)calloc(valueSize, sizeof(char));
newKey = (char*)calloc(keySize, sizeof(char));
values.insert(std::make_pair(newKey, newValue));
return;
}
void UserSettings::setValue(char* key, bool value, uint64_t keySize) {
char *newKey;
log_debugcpp("[SET] Pos value: " + std::to_string((intptr_t)pos));
log_debugcpp("[SET] Neg value: " + std::to_string((intptr_t)neg));
if (auto search = values.find(key); search != values.end()) {
log_debugcpp("[SET] Previous value: " + std::to_string((intptr_t)values[key]));
if (!(search->second == pos || search->second == neg)) {
free(search->second);
}
if (value)
search->second = pos;
else search->second = neg;
return;
}
newKey = (char*)calloc(keySize, sizeof(char));
memcpy(newKey, key, keySize * sizeof(char));
values.insert(std::make_pair(newKey, value ? pos : neg));
return;
}
bool UserSettings::save(const char* path) {
if(!path) return false;
uint64_t convertedPathSize = 0;
wchar_t* utf16Path = Utf8toUtf16(path, &convertedPathSize);
if(!utf16Path) return false;
#define releaseBeforeReturn() do { \
CloseHandle(settingsHandle); \
settingsHandle = nullptr; \
free(utf16Path); \
free(text); \
} while(0)
//We initially reserve 1024B for flushing. If storage is exceeded, more same-size chunks are allocated
const uint64_t chunkSize = 1024;
char* text = (char*)calloc(chunkSize, sizeof(char));
uint64_t mapSize = values.size();
uint64_t chunks = 1;
uint64_t keySize = 0, valueSize = 0, totalSize = 0, previousStepSize = 0;
//for(std::pair<char*, char*> entry : values) {
std::unordered_map<char*, char*, Djb12Hasher, StrcmpEqual>::iterator it;
for (it = values.begin(); it != values.end(); it++) {
keySize = strlen(it->first);
valueSize = strlen(it->second);
totalSize += valueSize + keySize + (it == values.begin() ? 1 : 2); //newline and separator
if(totalSize > (chunkSize * chunks)) {
text = (char*)realloc(text, (++chunks * chunkSize));
}
if(it != values.begin())
memcpy(text + previousStepSize++, "\n", sizeof(char));
memcpy(text + previousStepSize, it->first, sizeof(char) * keySize);
memcpy(text + previousStepSize + keySize, "=", sizeof(char));
memcpy(text + previousStepSize + 1 + keySize, it->second, sizeof(char) * valueSize);
previousStepSize = totalSize;
}
HANDLE settingsHandle = nullptr;
settingsHandle = CreateFile2(
utf16Path,
GENERIC_READ | GENERIC_WRITE,
0,
CREATE_ALWAYS,
NULL);
if(settingsHandle == INVALID_HANDLE_VALUE) {
log_debugcpp("[SET] Can't save to file: " + std::to_string(GetLastError()));
releaseBeforeReturn();
return false;
}
DWORD bytesWritten;
BOOL writeSuccess = WriteFile(
settingsHandle,
text,
totalSize,
&bytesWritten,
nullptr
);
releaseBeforeReturn();
if (writeSuccess == TRUE) return true;
else return false;
return false;
#undef releaseBeforeReturn
}
UserSettings::~UserSettings() {
//if(textContents) free(textContents);
for(std::pair<char*, char*> entry : values) {
free(entry.first);
if (!(entry.second == pos || entry.second == neg))
free(entry.second);
}
}
UserSettings* UserSettings::createSettings(const char* path, bool create) {
if(!path) return nullptr;
wchar_t* utf16Path = Utf8toUtf16(path);
if(!utf16Path) return nullptr;
#define releaseBeforeReturn() do { \
CloseHandle(settingsHandle); \
settingsHandle = nullptr; \
free(utf16Path); \
} while(0)
char* textContents;
HANDLE settingsHandle = nullptr;
settingsHandle = CreateFile2(
utf16Path,
GENERIC_READ | GENERIC_WRITE,
0,
(create ? OPEN_ALWAYS : OPEN_EXISTING),
NULL);
if(settingsHandle == INVALID_HANDLE_VALUE) {
std::string createString = std::string(create ? (char*)"create" : (char*)"open");
log_debugcpp("[SET] Can't create settings file: " + std::to_string(GetLastError()));
log_debugcpp("[SET] Can't " + createString + \
" settings file on: " + std::string(path) + \
", error: " + std::to_string(GetLastError()));
releaseBeforeReturn();
return nullptr;
}
//Calculating file size and reading file
uint64_t fileSize;
LARGE_INTEGER fileSizeStruct;
if(!GetFileSizeEx(settingsHandle, &fileSizeStruct)) {
releaseBeforeReturn();
return nullptr;
}
fileSize = fileSizeStruct.QuadPart;
uint32_t bytesRead = 0;
uint64_t textContentsSize = fileSize + 1;
textContents = (char*)calloc(textContentsSize, sizeof(char));
if (ReadFile(settingsHandle, textContents, fileSize,
(LPDWORD)&bytesRead, NULL) != TRUE) {
releaseBeforeReturn();
return nullptr;
}
releaseBeforeReturn();
return new UserSettings(textContents);
//textContents.assign(tempTextContents);
//free(tempTextContents);
#undef releaseBeforeReturn
}
}

71
src/settings.h Normal file
View file

@ -0,0 +1,71 @@
#pragma once
#include "global.h"
namespace ini {
//Trims spaces, LF and CRLF
static inline char* trimAndAllocate(const char* in, uint64_t len = 0) {
if (!in) return nullptr;
uint64_t startingPos = 0, lastPos = 0;
bool foundStart = false;
for(int i = 0; ;i++) {
char c = in[i];
if ((len > 0 && startingPos == (len - 1)) || (len > 0 && lastPos == (len - 1))) return nullptr;
if ((c != ' ' || c != '\r' || c != '\n') && !foundStart) {
foundStart = true;
lastPos = startingPos;
}
if (foundStart && (c == ' ' || c == '\r' || c == '\n')) {
break;
}
if(!foundStart)
startingPos++;
else lastPos++;
}
if(!(lastPos - startingPos)) return nullptr;
char* trimmedString = (char*)calloc(lastPos - startingPos + 1, sizeof(char));
memcpy(trimmedString, in + startingPos, lastPos - startingPos);
return trimmedString;
}
struct Djb12Hasher {
size_t operator()(char* str) const {
unsigned long hash = 5381;
int c;
while (c = *str++)
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
return hash;
}
};
struct StrcmpEqual {
bool operator()(char* key1, char* key2) const {
return (!strcmp(key1, key2));
}
};
class UserSettings {
public:
static UserSettings* createSettings(const char* path = nullptr, bool create = false);
~UserSettings();
char* const getValue(char* key, uint64_t len = 0);
void setValue(char* key, char* value, uint64_t valueSize, uint64_t keySize); //'\0' included
void setValue(char* key, bool value, uint64_t keySize); //'\0' included
bool save(const char* path);
protected:
private:
UserSettings(char* text = nullptr);
std::unordered_map<char*, char*, Djb12Hasher, StrcmpEqual> values;
char* pos = "true";
char* neg = "false";
};
}