Compare commits

...

4 Commits

Author SHA1 Message Date
22f9a8a1a6 checklist stuff 2022-12-22 10:55:56 +01:00
7e76c4813d failed loudness eq experiment 2022-12-14 18:42:16 +01:00
8967846657 Separate windows shell interaction into it's own file 2022-08-10 14:17:50 +02:00
c58b6c5624 mute button 2022-07-26 20:34:40 +02:00
20 changed files with 2686 additions and 126 deletions

View File

@@ -3,7 +3,7 @@
#include "AudioApi.h"
#include <functiondiscoverykeys.h>
AudioDevice::AudioDevice(IMMDevice* device, LPCWSTR deviceId)
AudioDevice::AudioDevice(IMMDevice* device, LPCWSTR deviceId) : device(device)
{
id = std::wstring(deviceId);

View File

@@ -1,10 +1,12 @@
#pragma once
#include <mmdeviceapi.h>
#include "AudioNotificationListener.h"
#include <endpointvolume.h>
#include <string>
#include <vector>
#include "AudioNotificationListener.h"
#include <unordered_map>
class AudioDevice {
public:
@@ -44,13 +46,16 @@ public:
bool autostart = false;
bool docked = false;
bool showDisabledDevices = false;
std::vector<std::string> taskNames = {};
std::unordered_map<std::string, std::vector<time_t>> tasks = {};
};
class ApplicationData {
public:
ApplicationSettings settings = {};
size_t checklistLength = 0;
std::shared_ptr<AudioData> audioData = std::make_shared<AudioData>();
ApplicationData(const ApplicationData&) = delete;
ApplicationData& operator=(const ApplicationData&) = delete;
//ApplicationData(const ApplicationData&) = delete;
//ApplicationData& operator=(const ApplicationData&) = delete;
};

View File

@@ -7,26 +7,22 @@
// see https://github.com/microsoft/Windows-classic-samples/blob/main/Samples/Win7Samples/winui/shell/appshellintegration/NotificationIcon/NotificationIcon.cpp
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "rpcrt4.lib")
#include <windows.h>
#include "Util.h"
#include "AudioApi.h"
#include "Settings.h"
#include "AsuroTool.h"
#include "WindowsShell.h"
#include <array>
#include <functional>
#include <iostream>
#include <sstream>
#include <shellapi.h>
#include <commctrl.h>
#include <strsafe.h>
#include <format>
#include <ctime>
#include "Util.h"
#include "AudioApi.h"
#include "Settings.h"
#include "resource.h"
#include "AsuroTool.h"
const size_t MAX_FONT_PATH_LENGTH = 2048;
const UINT TRAY_ID = 420;
const UINT WMAPP_NOTIFYCALLBACK = WM_APP + 1;
#define MAX_FONT_PATH_LENGTH 2048
// Globals for use in callbacks
DrawData* gDrawData;
@@ -50,7 +46,7 @@ int main()
callbacks.drawFunc = std::bind(draw, std::placeholders::_1, std::ref(applicationData));
callbacks.cleanupFunc = std::bind(cleanup, std::placeholders::_1, std::ref(applicationData));
startImgui(callbacks, "Audio Thingy", 600, 400);
startImgui(callbacks, "Audio Thingy", 700, 400);
}
void init(DrawData& drawData, ApplicationData& appData)
@@ -76,63 +72,14 @@ void init(DrawData& drawData, ApplicationData& appData)
ImFontConfig icons_config;
icons_config.MergeMode = true;
icons_config.PixelSnapH = true;
icons_config.GlyphOffset = { 0., 2. };
std::string iconFontPath = std::string(appPathStr);
iconFontPath.append("\\remixicon.ttf");
io.Fonts->AddFontFromFileTTF(iconFontPath.c_str(), 14.0f, &icons_config, icons_ranges);
// Set window icon
HINSTANCE instance = GetModuleHandle(NULL);
LPWSTR iconId = MAKEINTRESOURCE(IDI_ICON1);
HANDLE iconLarge = LoadImageW(instance, iconId, IMAGE_ICON, 64, 64, 0);
HANDLE iconSmall = LoadImageW(instance, iconId, IMAGE_ICON, 32, 32, 0);
SendMessage(drawData.window_handle, WM_SETICON, ICON_BIG, (LPARAM)iconLarge);
SendMessage(drawData.window_handle, WM_SETICON, ICON_SMALL, (LPARAM)iconSmall);
SendMessage(drawData.window_handle, WM_SETICON, ICON_SMALL2, (LPARAM)iconSmall);
// Set tray icon
NOTIFYICONDATA nid = { sizeof(nid) };
nid.hWnd = drawData.window_handle;
nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE | NIF_SHOWTIP;
nid.uID = TRAY_ID;
nid.uCallbackMessage = WMAPP_NOTIFYCALLBACK;
HRESULT iconResult;
iconResult = LoadIconMetric(instance, iconId, LIM_SMALL, &nid.hIcon);
isError(iconResult, "Failed to load tray icon image: ");
iconResult = LoadString(instance, IDS_STRING_TOOLTIP, nid.szTip, ARRAYSIZE(nid.szTip));
isError(iconResult, "Failed to load tray icon text: ");
Shell_NotifyIcon(NIM_ADD, &nid);
nid.uVersion = NOTIFYICON_VERSION_4;
Shell_NotifyIcon(NIM_SETVERSION, &nid);
if (!SetWindowsHookEx(WH_CALLWNDPROC, trayIconEventHandler, instance, GetCurrentThreadId()))
{
std::cout << "Failed to hook tray icon events: " << std::hex << GetLastError() << std::endl;
}
// Set window minimize behavior
glfwSetWindowIconifyCallback(drawData.window, [](GLFWwindow* window, int isIconified) {
if (isIconified && gAppData->settings.docked)
{
glfwHideWindow(window);
}
isHidden = isIconified;
});
glfwSetWindowFocusCallback(drawData.window, [](GLFWwindow* window, int isFocused) {
if (!isFocused && gAppData->settings.docked && !justDocked)
{
glfwIconifyWindow(window);
}
});
// Load settings
initShell(drawData);
initSettings(drawData, appData);
// Set up audio device api
initAudio(appData);
}
@@ -140,7 +87,6 @@ void draw(DrawData& drawData, ApplicationData& appData)
{
justDocked = false;
// Actual Drawing
if (isHidden)
{
glfwWaitEvents();
@@ -153,17 +99,24 @@ void draw(DrawData& drawData, ApplicationData& appData)
// Menu Bar
customYCursor += menuBar(drawData, appData).y;
// Checklist
ImGui::SetNextWindowPos(ImVec2(0, customYCursor));
ImGui::SetNextWindowSize(ImVec2(viewportSize.x, 0));
customYCursor += checklistWindow(appData, std::format(" {} Checklist", ICON_CHECK_FILL).c_str()).y;
customYCursor += 5.;
// Playback Devices
ImGui::SetNextWindowPos(ImVec2(0, customYCursor));
ImGui::SetNextWindowSize(ImVec2(viewportSize.x, 0));
customYCursor += audioDeviceWindow(appData, appData.audioData->playbackDevices, " \xEE\xB8\x84 Playback").y;
customYCursor += audioDeviceWindow(appData, appData.audioData->playbackDevices, std::format(" {} Playback", ICON_HEADPHONE_FILL).c_str()).y;
customYCursor += 5.;
// Recording devices
ImGui::SetNextWindowPos(ImVec2(0, customYCursor));
ImGui::SetNextWindowSize(ImVec2(viewportSize.x, 0));
customYCursor += audioDeviceWindow(appData, appData.audioData->recordingDevices, " \xEE\xBD\x8F Recording").y;
customYCursor += audioDeviceWindow(appData, appData.audioData->recordingDevices, std::format(" {} Recording", ICON_MIC_FILL).c_str()).y;
// Resize viewport
drawData.window_size.y = customYCursor;
@@ -178,11 +131,7 @@ void draw(DrawData& drawData, ApplicationData& appData)
void cleanup(DrawData& drawData, ApplicationData& appData)
{
// Remove tray icon
NOTIFYICONDATA nid = { sizeof(nid) };
nid.hWnd = drawData.window_handle;
nid.uID = TRAY_ID;
Shell_NotifyIcon(NIM_DELETE, &nid);
cleanupShell(drawData);
}
ImVec2 menuBar(DrawData& drawData, ApplicationData& appData)
@@ -223,13 +172,13 @@ ImVec2 menuBar(DrawData& drawData, ApplicationData& appData)
{
ImVec2 availableSpace = ImGui::GetContentRegionAvail();
ImVec2 cursorPos = ImGui::GetCursorPos();
ImGui::SetCursorPosX(cursorPos.x + availableSpace.x - 33.);
ImGui::SetCursorPosX(cursorPos.x + availableSpace.x - 47.);
if (ImGui::SmallButton("-"))
if (ImGui::SmallButton(ICON_SUBTRACT_FILL))
{
glfwIconifyWindow(drawData.window);
}
if (ImGui::SmallButton("x"))
if (ImGui::SmallButton(ICON_CLOSE_FILL))
{
glfwSetWindowShouldClose(drawData.window, GLFW_TRUE);
}
@@ -267,6 +216,91 @@ bool customButton(const char* id_start, const char* id_end, const char* title, b
return result;
}
void drawCheclistDayLines(ApplicationData& appData, ImDrawList* drawList, float lineHeight, time_t day)
{
auto& tasks = appData.settings.tasks;
size_t totalTasks = appData.settings.taskNames.size();
size_t count = std::count_if(tasks.begin(), tasks.end(), [&](std::pair<std::string, std::vector<time_t>> t) { return std::any_of(t.second.begin(), t.second.end(), [&](time_t tt) { return tt == day; }); });
for (int i = 0; i < count; i++)
{
ImVec2 cursorPos = ImGui::GetCursorScreenPos();
drawList->AddLine({ cursorPos.x, cursorPos.y }, { cursorPos.x, cursorPos.y + lineHeight }, ImColor(.4f, .9f, .3f), 3.f);
ImGui::SetCursorScreenPos({ cursorPos.x + 5.f, cursorPos.y });
}
for (int i = 0; i < max(0, totalTasks - count); i++)
{
ImVec2 cursorPos = ImGui::GetCursorScreenPos();
drawList->AddLine({ cursorPos.x, cursorPos.y }, { cursorPos.x, cursorPos.y + lineHeight }, ImColor(.1f, .3f, .05f), 3.f);
ImGui::SetCursorScreenPos({ cursorPos.x + 5.f, cursorPos.y });
}
}
time_t getDayStartOf(time_t time)
{
tm localTime;
localtime_s(&localTime, &time);
localTime.tm_hour = 0;
localTime.tm_min = 0;
localTime.tm_sec = 0;
return mktime(&localTime);
}
ImVec2 checklistWindow(ApplicationData& appData, const char* title)
{
if (ImGui::Begin(title, 0, ImGuiWindowFlags_NoResize))
{
time_t today = getDayStartOf(std::time(nullptr));
ImDrawList* drawList = ImGui::GetWindowDrawList();
float lineHeight = ImGui::GetFontSize() + ImGui::GetStyle().FramePadding.y * 2;
for (int pastDay = 3; pastDay >= 1; pastDay--)
{
time_t date = today - (60 * 60 * 24 * pastDay);
drawCheclistDayLines(appData, drawList, lineHeight, date);
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5.f);
}
ImVec2 cursorPos = ImGui::GetCursorScreenPos();
drawList->AddLine({ cursorPos.x, cursorPos.y - 2.f }, { cursorPos.x, cursorPos.y + lineHeight + 2.f }, IM_COL32_WHITE, 1.f);
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 10.f);
drawCheclistDayLines(appData, drawList, lineHeight, today);
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 10.f);
auto todayMatcher = [&](time_t t) { return getDayStartOf(t) == today; };
for (std::string& taskName : appData.settings.taskNames)
{
if (!appData.settings.tasks.contains(taskName))
{
appData.settings.tasks.insert({ taskName, std::vector<time_t>{} });
}
std::vector<time_t>& taskDates = appData.settings.tasks[taskName];
bool taskDoneToday = std::any_of(taskDates.begin(), taskDates.end(), todayMatcher);
if (ImGui::Checkbox(taskName.c_str(), &taskDoneToday))
{
if (taskDoneToday)
{
taskDates.push_back(today);
}
else
{
std::erase_if(taskDates, todayMatcher);
}
}
ImGui::SameLine();
}
}
ImVec2 size = ImGui::GetWindowSize();
ImGui::End();
return size;
}
ImVec2 audioDeviceWindow(ApplicationData& appData, std::vector<AudioDevice>& deviceList, const char* title)
{
if (ImGui::Begin(title, 0, ImGuiWindowFlags_NoResize))
@@ -305,6 +339,21 @@ ImVec2 audioDeviceWindow(ApplicationData& appData, std::vector<AudioDevice>& dev
ImGui::TableNextColumn();
if (dev.state == DEVICE_STATE_ACTIVE)
{
// Mute button
ImGui::PushID(std::string("bn_mute_").append(deviceIdUtf8).c_str());
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
bool isDeviceMuted = isMuted(dev.volumeInterface);
if (ImGui::Button(isDeviceMuted ? ICON_VOLUME_MUTE_FILL : ICON_VOLUME_UP_FILL))
{
setMuted(dev.volumeInterface, !isDeviceMuted);
}
ImGui::PopStyleColor();
ImGui::PopID();
ImGui::SameLine(0, 2);
// Meter
static std::array<float, 2> meterValues{};
UINT channelCount = getMeterValues(dev.meterInterface, meterValues);
@@ -334,6 +383,7 @@ ImVec2 audioDeviceWindow(ApplicationData& appData, std::vector<AudioDevice>& dev
ImGui::SetNextItemWidth(space.x);
ImGui::PushID(&dev.id);
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0, 0, 0, 0));
if (isDeviceMuted) ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImVec4(.4, .4, .4, 1.));
float volume = getVolume(dev.volumeInterface);
if (ImGui::SliderFloat("", &volume, 0., 1., "", ImGuiSliderFlags_NoRoundToFormat | ImGuiSliderFlags_AlwaysClamp))
@@ -341,6 +391,7 @@ ImVec2 audioDeviceWindow(ApplicationData& appData, std::vector<AudioDevice>& dev
setVolume(dev.volumeInterface, volume);
}
if (isDeviceMuted) ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopID();
}
@@ -353,7 +404,7 @@ ImVec2 audioDeviceWindow(ApplicationData& appData, std::vector<AudioDevice>& dev
{
drawCircle(5, IM_COL32(50, 50, 222, 255));
}
if (customButton("bn_d_", deviceIdUtf8.c_str(), "\xEE\xBE\x82", !dev.isDefaultConsole))
if (customButton("bn_d_", deviceIdUtf8.c_str(), ICON_MUSIC_2_FILL, !dev.isDefaultConsole))
{
setDefaultAudioDevice(*appData.audioData, dev.id.c_str(), ERole::eConsole);
}
@@ -363,7 +414,7 @@ ImVec2 audioDeviceWindow(ApplicationData& appData, std::vector<AudioDevice>& dev
{
drawCircle(5, IM_COL32(222, 50, 50, 255));
}
if (customButton("bn_c_", deviceIdUtf8.c_str(), "\xEE\xBF\xA9", !dev.isDefaultCommunication))
if (customButton("bn_c_", deviceIdUtf8.c_str(), ICON_PHONE_FILL, !dev.isDefaultCommunication))
{
setDefaultAudioDevice(*appData.audioData, dev.id.c_str(), ERole::eCommunications);
}
@@ -391,25 +442,3 @@ void drawCircle(float radius, ImU32 color)
ImVec2 windowPos = ImGui::GetWindowPos();
drawList->AddCircleFilled(ImVec2(cursorPos.x + windowPos.x, cursorPos.y + windowPos.y + ImGui::GetTextLineHeight() / 2.), radius, color);
}
LRESULT CALLBACK trayIconEventHandler(int code, WPARAM wParam, LPARAM lParam)
{
if (code >= 0)
{
CWPSTRUCT* data = (CWPSTRUCT*)lParam;
if (data->message == WMAPP_NOTIFYCALLBACK)
{
auto id = HIWORD(data->lParam);
auto trayEvent = LOWORD(data->lParam);
if (id == TRAY_ID && trayEvent == WM_LBUTTONUP)
{
glfwShowWindow(gDrawData->window);
glfwRestoreWindow(gDrawData->window);
}
}
}
return CallNextHookEx(NULL, code, wParam, lParam);
}

View File

@@ -1,16 +1,15 @@
#pragma once
#include <vector>
#include "ImguiBase.h"
#include "ApplicationData.h"
#include <vector>
void init(DrawData& drawData, ApplicationData& customData);
void draw(DrawData& drawData, ApplicationData& customData);
void cleanup(DrawData& drawData, ApplicationData& appData);
ImVec2 menuBar(DrawData& drawData, ApplicationData& appData);
ImVec2 checklistWindow(ApplicationData& appData, const char* title);
ImVec2 audioDeviceWindow(ApplicationData& appData, std::vector<AudioDevice>& deviceList, const char* title);
void drawCircle(float radius, ImU32 color);
LRESULT trayIconEventHandler(int code, WPARAM wParam, LPARAM lParam);

View File

@@ -111,7 +111,7 @@ END
STRINGTABLE
BEGIN
IDS_STRING_TOOLTIP "uwu"
IDS_STRING_TOOLTIP "Audio Thingy"
END
#endif // English (United Kingdom) resources

View File

@@ -93,6 +93,7 @@
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<DisableSpecificWarnings>4305; 4244</DisableSpecificWarnings>
<LanguageStandard>stdcpp20</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@@ -111,6 +112,7 @@
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<DisableSpecificWarnings>4305; 4244</DisableSpecificWarnings>
<LanguageStandard>stdcpp20</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@@ -129,6 +131,7 @@
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<DisableSpecificWarnings>4305; 4244</DisableSpecificWarnings>
<LanguageStandard>stdcpp20</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@@ -147,6 +150,7 @@
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<DisableSpecificWarnings>4305; 4244</DisableSpecificWarnings>
<LanguageStandard>stdcpp20</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@@ -164,6 +168,7 @@
<ClCompile Include="AudioNotificationListener.cpp" />
<ClCompile Include="Settings.cpp" />
<ClCompile Include="Util.cpp" />
<ClCompile Include="WindowsShell.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="ApplicationData.h" />
@@ -175,6 +180,7 @@
<ClInclude Include="resource.h" />
<ClInclude Include="Settings.h" />
<ClInclude Include="Util.h" />
<ClInclude Include="WindowsShell.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ImguiBase\ImguiBase.vcxproj">

View File

@@ -39,6 +39,9 @@
<ClCompile Include="Settings.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="WindowsShell.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="AsuroTool.h">
@@ -65,6 +68,9 @@
<ClInclude Include="PolicyConfig.h">
<Filter>Header Files\Audio</Filter>
</ClInclude>
<ClInclude Include="WindowsShell.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CopyFileToFolders Include="Montserrat-Regular.ttf">

View File

@@ -1,15 +1,18 @@
#include "AudioApi.h"
#include "Util.h"
#include "PolicyConfig.h"
#include <windows.h>
#include <functiondiscoverykeys.h>
#include <endpointvolume.h>
#include <initguid.h>
#include <mmdeviceapi.h>
#include <array>
#include <vector>
#include <algorithm>
#include "Util.h"
#include "AudioApi.h"
#include "PolicyConfig.h"
#include <iostream>
void initAudio(ApplicationData& appData)
{
@@ -172,3 +175,70 @@ void getVolumeLimit(IAudioEndpointVolume* volumeInterface, float* outMin, float*
float dummy;
volumeInterface->GetVolumeRange(outMin, outMax, &dummy);
}
bool isMuted(IAudioEndpointVolume* volumeInterface)
{
BOOL result = false;
if (FAILED(volumeInterface->GetMute(&result))) return false;
return static_cast<bool>(result);
}
void setMuted(IAudioEndpointVolume* volumeInterface, bool newState)
{
volumeInterface->SetMute(static_cast<BOOL>(newState), NULL);
}
void CreateLoudnessEqualizationKey(PROPERTYKEY& key)
{
// Realtek: const wchar_t* guid = L"E0A941A0-88A2-4df5-8D6B-DD20BB06E8FB";
const wchar_t* guid = L"FC52A749-4BE9-4510-896E-966BA6525980";
UuidFromString((RPC_WSTR)guid, &key.fmtid);
key.pid = 3;
}
AudioProcessingState getAudioProcessing(AudioDevice& device)
{
return AudioProcessingState::Unknown;
LPWSTR pwstrEndpointId = NULL;
HRESULT hr = device.device->GetId(&pwstrEndpointId);
IPolicyConfig* pPolicyConfig;
hr = CoCreateInstance(__uuidof(CPolicyConfigClient), NULL, CLSCTX_ALL, IID_PPV_ARGS(&pPolicyConfig));
if (SUCCEEDED(hr))
{
PROPVARIANT var;
PropVariantInit(&var);
PROPERTYKEY key{};
CreateLoudnessEqualizationKey(key);
hr = pPolicyConfig->GetPropertyValue(pwstrEndpointId, TRUE, key, &var);
pPolicyConfig->Release();
return var.boolVal ? AudioProcessingState::Disabled : AudioProcessingState::Enabled;
}
return AudioProcessingState::Unknown;
}
void setAudioProcessing(AudioDevice& device, bool newVal)
{
return;
LPWSTR pwstrEndpointId = NULL;
HRESULT hr = device.device->GetId(&pwstrEndpointId);
IPolicyConfig* pPolicyConfig;
hr = CoCreateInstance(__uuidof(CPolicyConfigClient), NULL, CLSCTX_ALL, IID_PPV_ARGS(&pPolicyConfig));
if (SUCCEEDED(hr))
{
PROPVARIANT var;
PropVariantInit(&var);
var.vt = VT_UI4;
var.uintVal = newVal ? ENDPOINT_SYSFX_ENABLED : ENDPOINT_SYSFX_DISABLED;
PROPERTYKEY key{};
CreateLoudnessEqualizationKey(key);
hr = pPolicyConfig->SetPropertyValue(pwstrEndpointId, TRUE, key, &var);
pPolicyConfig->Release();
}
}

View File

@@ -1,9 +1,14 @@
#pragma once
#include <mmdeviceapi.h>
#include "ApplicationData.h"
enum class AudioProcessingState
{
Enabled,
Disabled,
Unknown
};
void initAudio(ApplicationData& appData);
void loadAudioDevices(AudioData& audioData, std::vector<AudioDevice>& deviceList, EDataFlow deviceType);
void reloadDeviceLists(AudioData& audioData);
@@ -16,3 +21,7 @@ float getVolume(IAudioEndpointVolume* volumeInterface);
void setVolume(IAudioEndpointVolume* volumeInterface, float newVolume);
UINT getMeterValues(IAudioMeterInformation* meterInterface, std::array<float, 2>& levels);
void getVolumeLimit(IAudioEndpointVolume* volumeInterface, float* outMin, float* outMax);
bool isMuted(IAudioEndpointVolume* volumeInterface);
void setMuted(IAudioEndpointVolume* volumeInterface, bool newState);
AudioProcessingState getAudioProcessing(AudioDevice& device);
void setAudioProcessing(AudioDevice& device, bool newVal);

View File

@@ -1,8 +1,10 @@
#pragma once
#include <memory>
#include <initguid.h>
#include <mmdeviceapi.h>
#include <memory>
class AudioData;
class ApplicationData;
class AudioNotificationListener : public IMMNotificationClient {

View File

@@ -115,6 +115,9 @@ public:
PCWSTR,
INT
);
virtual HRESULT STDMETHODCALLTYPE GetPropertyValue(PCWSTR pszDeviceName, BOOL bFxStore, const PROPERTYKEY& pKey, PROPVARIANT* pv) = 0;
virtual HRESULT STDMETHODCALLTYPE SetPropertyValue(PCWSTR pszDeviceName, BOOL bFxStore, const PROPERTYKEY& pKey, PROPVARIANT* pv) = 0;
};
interface DECLSPEC_UUID("568b9108-44bf-40b4-9006-86afe5b5a620") IPolicyConfigVista;

View File

@@ -3,6 +3,8 @@
#include "Util.h"
#include "Settings.h"
#include <chrono>
extern bool justDocked;
extern DrawData* gDrawData;
extern ApplicationData* gAppData;
@@ -10,8 +12,8 @@ extern ApplicationData* gAppData;
void initSettings(DrawData& drawData, ApplicationData& appData)
{
ImGuiSettingsHandler ini_handler;
ini_handler.TypeName = "ApplicationSettings";
ini_handler.TypeHash = ImHashStr("ApplicationSettings");
ini_handler.TypeName = APPLICATION_SETTINGS_GROUP;
ini_handler.TypeHash = ImHashStr(APPLICATION_SETTINGS_GROUP);
ini_handler.ReadOpenFn = settingsReadOpen;
ini_handler.ReadLineFn = settingsReadLine;
ini_handler.WriteAllFn = settingsWriteAll;
@@ -29,6 +31,8 @@ void* settingsReadOpen(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const c
void settingsReadLine(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line)
{
if (strlen(line) == 0) return;
ApplicationSettings* settings = (ApplicationSettings*)entry;
int docked;
@@ -36,19 +40,54 @@ void settingsReadLine(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* en
{
settings->docked = (bool)docked;
}
int autostart;
if (sscanf_s(line, "autostart=%i", &autostart))
{
settings->autostart = (bool)autostart;
}
std::string taskName{};
taskName.resize(MAX_TASK_NAME_LENGTH);
if (sscanf_s(line, "taskname=%s", &taskName[0], MAX_TASK_NAME_LENGTH))
{
settings->taskNames.push_back(taskName);
}
std::string task{};
task.resize(MAX_TASK_NAME_LENGTH);
time_t dayTimestamp;
if (sscanf_s(line, "task=%lld %s", &dayTimestamp, &task[0], MAX_TASK_NAME_LENGTH))
{
if (settings->tasks.contains(task))
{
settings->tasks[task].push_back(dayTimestamp);
}
else
{
settings->tasks.insert({ task, std::vector{ dayTimestamp } });
}
}
}
void settingsWriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* outBuf)
{
outBuf->appendf("[%s][%s]\n", "ApplicationSettings", "ApplicationSettings");
outBuf->appendf("[%s][%s]\n", APPLICATION_SETTINGS_GROUP, APPLICATION_SETTINGS_GROUP);
outBuf->appendf("docked=%i\n", (int)gAppData->settings.docked);
outBuf->appendf("autostart=%i\n", (int)gAppData->settings.autostart);
outBuf->append("\n");
for (std::string& taskName : gAppData->settings.taskNames)
{
outBuf->appendf("taskname=%s\n", taskName.c_str());
}
for (auto& task : gAppData->settings.tasks)
{
for (auto& date : task.second)
{
outBuf->appendf("task=%lld %s\n", date, task.first.c_str());
}
}
}
void applySettings(DrawData& drawData, ApplicationData& appData)

View File

@@ -1,8 +1,11 @@
#pragma once
#include "ApplicationData.h"
#include "ImguiBase.h"
#include <imgui_internal.h>
#include "ApplicationData.h"
#define APPLICATION_SETTINGS_GROUP "ApplicationSettings"
#define MAX_TASK_NAME_LENGTH 1024
void initSettings(DrawData& drawData, ApplicationData& appData);

View File

@@ -1,6 +1,7 @@
#pragma once
#include <windows.h>
#include <sstream>
bool isError(const HRESULT result, const std::stringstream message);

102
AsuroTool/WindowsShell.cpp Normal file
View File

@@ -0,0 +1,102 @@
#include "WindowsShell.h"
#include "resource.h"
#include "ApplicationData.h"
#include "Util.h"
#include <Windows.h>
#include <shellapi.h>
#include <commctrl.h>
#include <strsafe.h>
#include <iostream>
const UINT TRAY_ID = 420;
const UINT WMAPP_NOTIFYCALLBACK = WM_APP + 1;
extern DrawData* gDrawData;
extern ApplicationData* gAppData;
extern bool justDocked;
extern bool isHidden;
void initShell(DrawData& drawData)
{
// Set window icon
HINSTANCE instance = GetModuleHandle(NULL);
LPWSTR iconId = MAKEINTRESOURCE(IDI_ICON1);
HANDLE iconLarge = LoadImageW(instance, iconId, IMAGE_ICON, 64, 64, 0);
HANDLE iconSmall = LoadImageW(instance, iconId, IMAGE_ICON, 32, 32, 0);
SendMessage(drawData.window_handle, WM_SETICON, ICON_BIG, (LPARAM)iconLarge);
SendMessage(drawData.window_handle, WM_SETICON, ICON_SMALL, (LPARAM)iconSmall);
SendMessage(drawData.window_handle, WM_SETICON, ICON_SMALL2, (LPARAM)iconSmall);
// Set tray icon
NOTIFYICONDATA nid = { sizeof(nid) };
nid.hWnd = drawData.window_handle;
nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE | NIF_SHOWTIP;
nid.uID = TRAY_ID;
nid.uCallbackMessage = WMAPP_NOTIFYCALLBACK;
HRESULT iconResult;
iconResult = LoadIconMetric(instance, iconId, LIM_SMALL, &nid.hIcon);
isError(iconResult, "Failed to load tray icon image: ");
iconResult = LoadString(instance, IDS_STRING_TOOLTIP, nid.szTip, ARRAYSIZE(nid.szTip));
isError(iconResult, "Failed to load tray icon text: ");
Shell_NotifyIcon(NIM_ADD, &nid);
nid.uVersion = NOTIFYICON_VERSION_4;
Shell_NotifyIcon(NIM_SETVERSION, &nid);
if (!SetWindowsHookEx(WH_CALLWNDPROC, trayIconEventHandler, instance, GetCurrentThreadId()))
{
std::cout << "Failed to hook tray icon events: " << std::hex << GetLastError() << std::endl;
}
// Set window minimize behavior
glfwSetWindowIconifyCallback(drawData.window, [](GLFWwindow* window, int isIconified) {
if (isIconified && gAppData->settings.docked)
{
glfwHideWindow(window);
}
isHidden = isIconified;
});
glfwSetWindowFocusCallback(drawData.window, [](GLFWwindow* window, int isFocused) {
if (!isFocused && gAppData->settings.docked && !justDocked)
{
glfwIconifyWindow(window);
}
});
}
void cleanupShell(DrawData& drawData)
{
// Remove tray icon
NOTIFYICONDATA nid = { sizeof(nid) };
nid.hWnd = drawData.window_handle;
nid.uID = TRAY_ID;
Shell_NotifyIcon(NIM_DELETE, &nid);
}
LRESULT CALLBACK trayIconEventHandler(int code, WPARAM wParam, LPARAM lParam)
{
if (code >= 0)
{
CWPSTRUCT* data = (CWPSTRUCT*)lParam;
if (data->message == WMAPP_NOTIFYCALLBACK)
{
auto id = HIWORD(data->lParam);
auto trayEvent = LOWORD(data->lParam);
if (id == TRAY_ID && (trayEvent == WM_LBUTTONUP || trayEvent == WM_RBUTTONUP))
{
glfwShowWindow(gDrawData->window);
glfwRestoreWindow(gDrawData->window);
}
}
}
return CallNextHookEx(NULL, code, wParam, lParam);
}

8
AsuroTool/WindowsShell.h Normal file
View File

@@ -0,0 +1,8 @@
#pragma once
#include <ImguiBase.h>
void initShell(DrawData& drawData);
void cleanupShell(DrawData& drawData);
LRESULT trayIconEventHandler(int code, WPARAM wParam, LPARAM lParam);

View File

@@ -9,6 +9,7 @@
#include <GLFW/glfw3.h>
#include "imgui.h"
#include "remixicon.h"
class DrawData {
public:

View File

@@ -202,6 +202,7 @@
<ClInclude Include="imgui\headers\imstb_rectpack.h" />
<ClInclude Include="imgui\headers\imstb_textedit.h" />
<ClInclude Include="imgui\headers\imstb_truetype.h" />
<ClInclude Include="remixicon.h" />
</ItemGroup>
<ItemGroup>
<Font Include="Montserrat-Regular.ttf" />

View File

@@ -74,6 +74,9 @@
<ClInclude Include="ImguiBase.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="remixicon.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Font Include="Montserrat-Regular.ttf">

2273
ImguiBase/remixicon.h Normal file

File diff suppressed because it is too large Load Diff