11 Commits

Author SHA1 Message Date
ebdd35b1e1 1.1 2022-07-22 01:49:30 +02:00
b986bfde39 fix missing tray icon bug 2022-07-22 01:48:26 +02:00
ecfb2f206d 1.0 2022-07-21 17:48:52 +02:00
1ab7fd1129 more cleanup 2022-07-21 17:39:25 +02:00
d5d4521f58 autostart + more cleanup 2022-07-21 17:29:43 +02:00
27be3fb0d4 cleanup 2022-07-21 16:18:54 +02:00
df939555b9 load settings on start 2022-07-21 15:56:44 +02:00
4c73dba8ad docked mode 2022-07-15 19:14:59 +02:00
5c785ac4e7 volume slider 2022-07-15 18:46:44 +02:00
ab2bb6055c tray icon 2022-07-15 17:29:22 +02:00
4f804dc5c3 templates 2022-07-15 05:00:44 +02:00
16 changed files with 505 additions and 117 deletions

View File

@@ -39,12 +39,16 @@ public:
class ApplicationSettings { class ApplicationSettings {
public: public:
bool autostart = false;
bool docked = false;
bool showDisabledDevices = false; bool showDisabledDevices = false;
bool fitWindowHeight = true;
}; };
class ApplicationData { class ApplicationData {
public: public:
ApplicationSettings settings = {}; ApplicationSettings settings = {};
std::shared_ptr<AudioData> audioData = std::make_shared<AudioData>(); std::shared_ptr<AudioData> audioData = std::make_shared<AudioData>();
ApplicationData(const ApplicationData&) = delete;
ApplicationData& operator=(const ApplicationData&) = delete;
}; };

View File

@@ -3,26 +3,52 @@
#pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup") #pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")
#endif #endif
// we need commctrl v6 for LoadIconMetric()
// 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")
#include <windows.h> #include <windows.h>
#include <functional>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <shellapi.h>
#include <commctrl.h>
#include <strsafe.h>
#include "Util.h" #include "Util.h"
#include "AudioApi.h" #include "AudioApi.h"
#include "Settings.h"
#include "resource.h" #include "resource.h"
#include "AsuroTool.h" #include "AsuroTool.h"
const UINT TRAY_ID = 420;
const UINT WMAPP_NOTIFYCALLBACK = WM_APP + 1;
// Globals for use in callbacks
DrawData* gDrawData;
ApplicationData* gAppData;
bool justDocked = false;
bool isHidden = false;
int main() int main()
{ {
ApplicationData applicationData{}; ApplicationData applicationData{};
ImGuiCallbacks callbacks{};
startImgui(&applicationData, init, draw, "Asuro's Tool", 600, 400); callbacks.initFunc = std::bind(init, std::placeholders::_1, std::ref(applicationData));
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);
} }
void init(DrawData& drawData, void* customData) void init(DrawData& drawData, ApplicationData& appData)
{ {
ApplicationData* appData = static_cast<ApplicationData*>(customData); // sad :(
gDrawData = &drawData;
gAppData = &appData;
// Load text font // Load text font
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
@@ -35,67 +61,133 @@ void init(DrawData& drawData, void* customData)
icons_config.PixelSnapH = true; icons_config.PixelSnapH = true;
io.Fonts->AddFontFromFileTTF("remixicon.ttf", 14.0f, &icons_config, icons_ranges); io.Fonts->AddFontFromFileTTF("remixicon.ttf", 14.0f, &icons_config, icons_ranges);
// Set up audio device api
HRESULT audioResult;
audioResult = CoInitializeEx(NULL, COINIT_MULTITHREADED);
isError(audioResult, "Failed to initialize COM: ");
audioResult = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&appData->audioData->deviceEnumerator));
isError(audioResult, "Failed to set up audio device enumerator: ");
appData->audioData->audioNotificationListener = new AudioNotificationListener(appData->audioData);
audioResult = appData->audioData->deviceEnumerator->RegisterEndpointNotificationCallback(appData->audioData->audioNotificationListener);
isError(audioResult, "Failed to register audio notification listener: ");
reloadDeviceLists(*appData->audioData);
// Set window icon // Set window icon
HINSTANCE instance = GetModuleHandle(NULL);
LPWSTR iconId = MAKEINTRESOURCE(IDI_ICON1); LPWSTR iconId = MAKEINTRESOURCE(IDI_ICON1);
HANDLE iconLarge = LoadImageW(GetModuleHandle(NULL), iconId, IMAGE_ICON, 64, 64, 0); HANDLE iconLarge = LoadImageW(instance, iconId, IMAGE_ICON, 64, 64, 0);
HANDLE iconSmall = LoadImageW(GetModuleHandle(NULL), iconId, IMAGE_ICON, 32, 32, 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_BIG, (LPARAM)iconLarge);
SendMessage(drawData.window_handle, WM_SETICON, ICON_SMALL, (LPARAM)iconSmall); SendMessage(drawData.window_handle, WM_SETICON, ICON_SMALL, (LPARAM)iconSmall);
SendMessage(drawData.window_handle, WM_SETICON, ICON_SMALL2, (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
initSettings(drawData, appData);
// Set up audio device api
initAudio(appData);
} }
void draw(DrawData& drawData, void* customData) void draw(DrawData& drawData, ApplicationData& appData)
{ {
ApplicationData* appData = static_cast<ApplicationData*>(customData); justDocked = false;
// Actual Drawing
if (isHidden)
{
glfwWaitEvents();
return;
}
float customYCursor = 0; float customYCursor = 0;
ImVec2 viewportSize = ImGui::GetMainViewport()->Size; ImVec2 viewportSize = ImGui::GetMainViewport()->Size;
// Menu Bar // Menu Bar
customYCursor += menuBar(appData).y; customYCursor += menuBar(drawData, appData).y;
// Playback Devices // Playback Devices
ImGui::SetNextWindowPos(ImVec2(0, customYCursor)); ImGui::SetNextWindowPos(ImVec2(0, customYCursor));
ImGui::SetNextWindowSize(ImVec2(viewportSize.x, 0)); ImGui::SetNextWindowSize(ImVec2(viewportSize.x, 0));
customYCursor += audioDeviceWindow(appData, appData->audioData->playbackDevices, " \xEE\xB8\x84 Playback").y; customYCursor += audioDeviceWindow(appData, appData.audioData->playbackDevices, " \xEE\xB8\x84 Playback").y;
customYCursor += 5.; customYCursor += 5.;
// Recording devices // Recording devices
ImGui::SetNextWindowPos(ImVec2(0, customYCursor)); ImGui::SetNextWindowPos(ImVec2(0, customYCursor));
ImGui::SetNextWindowSize(ImVec2(viewportSize.x, 0)); ImGui::SetNextWindowSize(ImVec2(viewportSize.x, 0));
customYCursor += audioDeviceWindow(appData, appData->audioData->recordingDevices, " \xEE\xBD\x8F Recording").y; customYCursor += audioDeviceWindow(appData, appData.audioData->recordingDevices, " \xEE\xBD\x8F Recording").y;
// Resize viewport // Resize viewport
if (appData->settings.fitWindowHeight) drawData.window_size.y = customYCursor;
if (appData.settings.docked)
{ {
drawData.window_size.y = customYCursor; int monitorX, monitorY, monitorW, monitorH;
glfwGetMonitorWorkarea(glfwGetPrimaryMonitor(), &monitorX, &monitorY, &monitorW, &monitorH);
glfwSetWindowPos(drawData.window, monitorX + monitorW - drawData.window_size.x, monitorY + monitorH - drawData.window_size.y);
} }
} }
ImVec2 menuBar(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);
}
ImVec2 menuBar(DrawData& drawData, ApplicationData& appData)
{ {
ImVec2 size{}; ImVec2 size{};
bool closeMenu = false;
if (ImGui::BeginMainMenuBar()) if (ImGui::BeginMainMenuBar())
{ {
if (ImGui::BeginMenu("Settings")) if (ImGui::BeginMenu("Settings"))
{ {
ImGui::Checkbox("Show Disabled Devices", &appData->settings.showDisabledDevices); if (ImGui::Checkbox("Docked", &appData.settings.docked))
ImGui::Checkbox("Fit Window Height", &appData->settings.fitWindowHeight); {
closeMenu = true;
updateDocked(drawData, appData);
}
ImGui::Checkbox("Show Disabled Devices", &appData.settings.showDisabledDevices);
if (ImGui::Checkbox("Autostart", &appData.settings.autostart))
{
setAutostart(appData.settings.autostart);
}
ImGui::EndMenu(); ImGui::EndMenu();
} }
@@ -103,14 +195,36 @@ ImVec2 menuBar(ApplicationData* appData)
{ {
if (ImGui::Button("Manual Refresh")) if (ImGui::Button("Manual Refresh"))
{ {
reloadDeviceLists(*appData->audioData); reloadDeviceLists(*appData.audioData);
} }
ImGui::EndMenu(); ImGui::EndMenu();
} }
if (appData.settings.docked)
{
ImVec2 availableSpace = ImGui::GetContentRegionAvail();
ImVec2 cursorPos = ImGui::GetCursorPos();
ImGui::SetCursorPosX(cursorPos.x + availableSpace.x - 33.);
if (ImGui::SmallButton("-"))
{
glfwIconifyWindow(drawData.window);
}
if (ImGui::SmallButton("x"))
{
glfwSetWindowShouldClose(drawData.window, GLFW_TRUE);
}
}
size = ImGui::GetWindowSize(); size = ImGui::GetWindowSize();
ImGui::EndMainMenuBar(); ImGui::EndMainMenuBar();
} }
if (closeMenu)
{
ImGui::SetWindowFocus();
}
return size; return size;
} }
@@ -134,7 +248,7 @@ bool customButton(const char* id_start, const char* id_end, const char* title, b
return result; return result;
} }
ImVec2 audioDeviceWindow(ApplicationData* appData, std::vector<AudioDevice>& deviceList, const char* title) ImVec2 audioDeviceWindow(ApplicationData& appData, std::vector<AudioDevice>& deviceList, const char* title)
{ {
if (ImGui::Begin(title, 0, ImGuiWindowFlags_NoResize)) if (ImGui::Begin(title, 0, ImGuiWindowFlags_NoResize))
{ {
@@ -153,7 +267,7 @@ ImVec2 audioDeviceWindow(ApplicationData* appData, std::vector<AudioDevice>& dev
{ {
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1., 1., 1., 1.)); ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1., 1., 1., 1.));
} }
else if (!appData->settings.showDisabledDevices) else if (!appData.settings.showDisabledDevices)
{ {
continue; continue;
} }
@@ -172,18 +286,33 @@ ImVec2 audioDeviceWindow(ApplicationData* appData, std::vector<AudioDevice>& dev
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (dev.state == DEVICE_STATE_ACTIVE) if (dev.state == DEVICE_STATE_ACTIVE)
{ {
// Log scale because it looks better (no actual reason for these exact numbers)
float meterValue = log10f(getMeterValue(dev.meterInterface) * 9. + 1.);
float volume = log10f(getMeterValue(dev.meterInterface) * 9. + 1.);
auto drawList = ImGui::GetWindowDrawList(); auto drawList = ImGui::GetWindowDrawList();
ImVec2 windowPos = ImGui::GetWindowPos(); ImVec2 windowPos = ImGui::GetWindowPos();
ImVec2 cursorPos = ImGui::GetCursorScreenPos(); ImVec2 cursorPos = ImGui::GetCursorScreenPos();
ImVec2 space = ImGui::GetContentRegionAvail(); ImVec2 space = ImGui::GetContentRegionAvail();
float lineY = cursorPos.y + ImGui::GetTextLineHeight() / 2.; float lineY = cursorPos.y + ImGui::GetTextLineHeight() / 2. + 2.;
drawList->AddLine(ImVec2(cursorPos.x, lineY), ImVec2(cursorPos.x + space.x, lineY), IM_COL32(120, 120, 120, 255), 2.); const float linePaddingX = 3.;
drawList->AddLine(ImVec2(cursorPos.x, lineY), ImVec2(cursorPos.x + space.x * volume, lineY), IM_COL32(200, 200, 255, 255), 3.); cursorPos.x += linePaddingX;
drawList->AddLine(ImVec2(cursorPos.x, lineY), ImVec2(cursorPos.x + (space.x - 2. * linePaddingX), lineY), IM_COL32(120, 120, 120, 255), 2.);
drawList->AddLine(ImVec2(cursorPos.x, lineY), ImVec2(cursorPos.x + (space.x - 2. * linePaddingX) * meterValue, lineY), IM_COL32(200, 200, 255, 255), 3.);
float volume = getVolume(dev.volumeInterface);
float prevVolume = volume;
ImGui::SetNextItemWidth(space.x);
ImGui::PushID(dev.device);
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0, 0, 0, 0));
ImGui::SliderFloat("", &volume, 0., 1., "", ImGuiSliderFlags_NoRoundToFormat | ImGuiSliderFlags_AlwaysClamp);
ImGui::PopStyleColor();
ImGui::PopID();
if (prevVolume != volume)
{
setVolume(dev.volumeInterface, volume);
}
} }
// Defaults // Defaults
@@ -196,7 +325,7 @@ ImVec2 audioDeviceWindow(ApplicationData* appData, std::vector<AudioDevice>& dev
} }
if (customButton("bn_d_", deviceIdUtf8.c_str(), "\xEE\xBE\x82", !dev.isDefaultConsole)) if (customButton("bn_d_", deviceIdUtf8.c_str(), "\xEE\xBE\x82", !dev.isDefaultConsole))
{ {
setDefaultAudioDevice(*appData->audioData, dev.id.c_str(), ERole::eConsole); setDefaultAudioDevice(*appData.audioData, dev.id.c_str(), ERole::eConsole);
} }
ImGui::SameLine(); ImGui::SameLine();
@@ -206,7 +335,7 @@ ImVec2 audioDeviceWindow(ApplicationData* appData, std::vector<AudioDevice>& dev
} }
if (customButton("bn_c_", deviceIdUtf8.c_str(), "\xEE\xBF\xA9", !dev.isDefaultCommunication)) if (customButton("bn_c_", deviceIdUtf8.c_str(), "\xEE\xBF\xA9", !dev.isDefaultCommunication))
{ {
setDefaultAudioDevice(*appData->audioData, dev.id.c_str(), ERole::eCommunications); setDefaultAudioDevice(*appData.audioData, dev.id.c_str(), ERole::eCommunications);
} }
} }
@@ -232,3 +361,25 @@ void drawCircle(float radius, ImU32 color)
ImVec2 windowPos = ImGui::GetWindowPos(); ImVec2 windowPos = ImGui::GetWindowPos();
drawList->AddCircleFilled(ImVec2(cursorPos.x + windowPos.x, cursorPos.y + windowPos.y + ImGui::GetTextLineHeight() / 2.), radius, color); 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

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

View File

@@ -64,6 +64,56 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
// remains consistent on all systems. // remains consistent on all systems.
IDI_ICON1 ICON "kaiju.ico" IDI_ICON1 ICON "kaiju.ico"
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,1,1
PRODUCTVERSION 1,0,1,1
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "200004b0"
BEGIN
VALUE "CompanyName", "Asuro"
VALUE "FileDescription", "Audio Thingy"
VALUE "FileVersion", "1.0.1.1"
VALUE "InternalName", "AsuroTool.exe"
VALUE "LegalCopyright", "Copyright (C) 2022"
VALUE "OriginalFilename", "AsuroTool.exe"
VALUE "ProductName", "Audio Thingy"
VALUE "ProductVersion", "1.0.1.1"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x2000, 1200
END
END
/////////////////////////////////////////////////////////////////////////////
//
// String Table
//
STRINGTABLE
BEGIN
IDS_STRING_TOOLTIP "uwu"
END
#endif // English (United Kingdom) resources #endif // English (United Kingdom) resources
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////

View File

@@ -92,6 +92,7 @@
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<DisableSpecificWarnings>4305; 4244</DisableSpecificWarnings>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
@@ -106,6 +107,7 @@
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<DisableSpecificWarnings>4305; 4244</DisableSpecificWarnings>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
@@ -120,6 +122,7 @@
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<DisableSpecificWarnings>4305; 4244</DisableSpecificWarnings>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
@@ -134,6 +137,7 @@
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<DisableSpecificWarnings>4305; 4244</DisableSpecificWarnings>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
@@ -146,6 +150,7 @@
<ClCompile Include="ApplicationData.cpp" /> <ClCompile Include="ApplicationData.cpp" />
<ClCompile Include="AsuroTool.cpp" /> <ClCompile Include="AsuroTool.cpp" />
<ClCompile Include="AudioNotificationListener.cpp" /> <ClCompile Include="AudioNotificationListener.cpp" />
<ClCompile Include="Settings.cpp" />
<ClCompile Include="Util.cpp" /> <ClCompile Include="Util.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -156,6 +161,7 @@
<ClInclude Include="AudioNotificationListener.h" /> <ClInclude Include="AudioNotificationListener.h" />
<ClInclude Include="PolicyConfig.h" /> <ClInclude Include="PolicyConfig.h" />
<ClInclude Include="resource.h" /> <ClInclude Include="resource.h" />
<ClInclude Include="Settings.h" />
<ClInclude Include="Util.h" /> <ClInclude Include="Util.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -36,6 +36,9 @@
<ClCompile Include="AudioNotificationListener.cpp"> <ClCompile Include="AudioNotificationListener.cpp">
<Filter>Source Files\Audio</Filter> <Filter>Source Files\Audio</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Settings.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="AsuroTool.h"> <ClInclude Include="AsuroTool.h">
@@ -44,9 +47,6 @@
<ClInclude Include="Util.h"> <ClInclude Include="Util.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="PolicyConfig.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ApplicationData.h"> <ClInclude Include="ApplicationData.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
@@ -59,6 +59,12 @@
<ClInclude Include="AudioNotificationListener.h"> <ClInclude Include="AudioNotificationListener.h">
<Filter>Header Files\Audio</Filter> <Filter>Header Files\Audio</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Settings.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="PolicyConfig.h">
<Filter>Header Files\Audio</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<CopyFileToFolders Include="Montserrat-Regular.ttf"> <CopyFileToFolders Include="Montserrat-Regular.ttf">

View File

@@ -10,31 +10,20 @@
#include "AudioApi.h" #include "AudioApi.h"
#include "PolicyConfig.h" #include "PolicyConfig.h"
HRESULT getDeviceProperty(IPropertyStore* propertyStore, const PROPERTYKEY propertyKey, PROPVARIANT* outData) void initAudio(ApplicationData& appData)
{ {
PropVariantInit(outData); HRESULT audioResult;
HRESULT nameResult = propertyStore->GetValue(propertyKey, outData); audioResult = CoInitializeEx(NULL, COINIT_MULTITHREADED);
return nameResult; isError(audioResult, "Failed to initialize COM: ");
}
HRESULT getDevicePropertyString(IPropertyStore* propertyStore, const PROPERTYKEY propertyKey, PROPVARIANT* outData, const wchar_t*& outString, const wchar_t* defaultStr) audioResult = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&appData.audioData->deviceEnumerator));
{ isError(audioResult, "Failed to set up audio device enumerator: ");
HRESULT result = getDeviceProperty(propertyStore, propertyKey, outData);
outString = outData->vt != VT_LPWSTR ? defaultStr : outData->pwszVal;
return result;
}
void setDefaultAudioDevice(AudioData& audioData, const wchar_t* deviceId, ERole role) appData.audioData->audioNotificationListener = new AudioNotificationListener(appData.audioData);
{ audioResult = appData.audioData->deviceEnumerator->RegisterEndpointNotificationCallback(appData.audioData->audioNotificationListener);
IPolicyConfigVista* pPolicyConfig; isError(audioResult, "Failed to register audio notification listener: ");
HRESULT hr = CoCreateInstance(__uuidof(CPolicyConfigVistaClient), NULL, CLSCTX_ALL, __uuidof(IPolicyConfigVista), (LPVOID*)&pPolicyConfig); reloadDeviceLists(*appData.audioData);
if (!isError(hr, "Failed to set default audio device: "))
{
hr = pPolicyConfig->SetDefaultEndpoint(deviceId, role);
pPolicyConfig->Release();
reloadDeviceLists(audioData);
}
} }
void loadAudioDevices(AudioData& audioData, std::vector<AudioDevice>& deviceList, EDataFlow deviceType) void loadAudioDevices(AudioData& audioData, std::vector<AudioDevice>& deviceList, EDataFlow deviceType)
@@ -42,7 +31,7 @@ void loadAudioDevices(AudioData& audioData, std::vector<AudioDevice>& deviceList
deviceList.clear(); deviceList.clear();
HRESULT err; HRESULT err;
IMMDeviceCollection* deviceCollection = NULL; IMMDeviceCollection* deviceCollection = nullptr;
err = audioData.deviceEnumerator->EnumAudioEndpoints(deviceType, DEVICE_STATE_ACTIVE | DEVICE_STATE_DISABLED, &deviceCollection); err = audioData.deviceEnumerator->EnumAudioEndpoints(deviceType, DEVICE_STATE_ACTIVE | DEVICE_STATE_DISABLED, &deviceCollection);
if (isError(err, "Failed to enumerate audio devices: ")) return; if (isError(err, "Failed to enumerate audio devices: ")) return;
@@ -51,7 +40,7 @@ void loadAudioDevices(AudioData& audioData, std::vector<AudioDevice>& deviceList
err = deviceCollection->GetCount(&deviceCount); err = deviceCollection->GetCount(&deviceCount);
if (isError(err, "Failed to count audio devices: ")) return; if (isError(err, "Failed to count audio devices: ")) return;
IMMDevice* defaultConsoleDevice = NULL; IMMDevice* defaultConsoleDevice = nullptr;
LPWSTR defaultConsoleId = nullptr; LPWSTR defaultConsoleId = nullptr;
err = audioData.deviceEnumerator->GetDefaultAudioEndpoint(deviceType, ERole::eConsole, &defaultConsoleDevice); err = audioData.deviceEnumerator->GetDefaultAudioEndpoint(deviceType, ERole::eConsole, &defaultConsoleDevice);
if (!FAILED(err)) if (!FAILED(err))
@@ -59,7 +48,7 @@ void loadAudioDevices(AudioData& audioData, std::vector<AudioDevice>& deviceList
defaultConsoleDevice->GetId(&defaultConsoleId); defaultConsoleDevice->GetId(&defaultConsoleId);
} }
IMMDevice* defaultMediaOutput = NULL; IMMDevice* defaultMediaOutput = nullptr;
LPWSTR defaultMediaId = nullptr; LPWSTR defaultMediaId = nullptr;
err = audioData.deviceEnumerator->GetDefaultAudioEndpoint(deviceType, ERole::eMultimedia, &defaultMediaOutput); err = audioData.deviceEnumerator->GetDefaultAudioEndpoint(deviceType, ERole::eMultimedia, &defaultMediaOutput);
if (!FAILED(err)) if (!FAILED(err))
@@ -67,7 +56,7 @@ void loadAudioDevices(AudioData& audioData, std::vector<AudioDevice>& deviceList
defaultMediaOutput->GetId(&defaultMediaId); defaultMediaOutput->GetId(&defaultMediaId);
} }
IMMDevice* defaultCommunicationOutput = NULL; IMMDevice* defaultCommunicationOutput = nullptr;
LPWSTR defaultCommunicationId = nullptr; LPWSTR defaultCommunicationId = nullptr;
err = audioData.deviceEnumerator->GetDefaultAudioEndpoint(deviceType, ERole::eCommunications, &defaultCommunicationOutput); err = audioData.deviceEnumerator->GetDefaultAudioEndpoint(deviceType, ERole::eCommunications, &defaultCommunicationOutput);
if (!FAILED(err)) if (!FAILED(err))
@@ -106,7 +95,6 @@ void loadAudioDevices(AudioData& audioData, std::vector<AudioDevice>& deviceList
err = deviceData.device->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (LPVOID*)&deviceData.volumeInterface); err = deviceData.device->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (LPVOID*)&deviceData.volumeInterface);
isError(err, "Failed to get audio endpoint volume interface: "); isError(err, "Failed to get audio endpoint volume interface: ");
IAudioMeterInformation* meterInterface;
err = deviceData.device->Activate(__uuidof(IAudioMeterInformation), CLSCTX_INPROC_SERVER, NULL, (LPVOID*)&deviceData.meterInterface); err = deviceData.device->Activate(__uuidof(IAudioMeterInformation), CLSCTX_INPROC_SERVER, NULL, (LPVOID*)&deviceData.meterInterface);
isError(err, "Failed to get audio meter interface: "); isError(err, "Failed to get audio meter interface: ");
@@ -146,10 +134,37 @@ void reloadDeviceLists(AudioData& audioData)
loadAudioDevices(audioData, audioData.recordingDevices, EDataFlow::eCapture); loadAudioDevices(audioData, audioData.recordingDevices, EDataFlow::eCapture);
} }
HRESULT getDeviceProperty(IPropertyStore* propertyStore, const PROPERTYKEY propertyKey, PROPVARIANT* outData)
{
PropVariantInit(outData);
HRESULT nameResult = propertyStore->GetValue(propertyKey, outData);
return nameResult;
}
HRESULT getDevicePropertyString(IPropertyStore* propertyStore, const PROPERTYKEY propertyKey, PROPVARIANT* outData, const wchar_t*& outString, const wchar_t* defaultStr)
{
HRESULT result = getDeviceProperty(propertyStore, propertyKey, outData);
outString = outData->vt != VT_LPWSTR ? defaultStr : outData->pwszVal;
return result;
}
void setDefaultAudioDevice(AudioData& audioData, const wchar_t* deviceId, ERole role)
{
IPolicyConfigVista* pPolicyConfig = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(CPolicyConfigVistaClient), NULL, CLSCTX_ALL, __uuidof(IPolicyConfigVista), (LPVOID*)&pPolicyConfig);
if (!isError(hr, "Failed to set default audio device: "))
{
hr = pPolicyConfig->SetDefaultEndpoint(deviceId, role);
pPolicyConfig->Release();
reloadDeviceLists(audioData);
}
}
float getVolume(IAudioEndpointVolume* volumeInterface) float getVolume(IAudioEndpointVolume* volumeInterface)
{ {
float volume; float volume;
if (FAILED(volumeInterface->GetChannelVolumeLevel(0, &volume))) if (FAILED(volumeInterface->GetMasterVolumeLevelScalar(&volume)))
{ {
volume = 0.; volume = 0.;
} }
@@ -157,6 +172,12 @@ float getVolume(IAudioEndpointVolume* volumeInterface)
return volume; return volume;
} }
void setVolume(IAudioEndpointVolume* volumeInterface, float newVolume)
{
HRESULT hr = volumeInterface->SetMasterVolumeLevelScalar(newVolume, NULL);
isError(hr, "Failed to set volume level: ");
}
float getMeterValue(IAudioMeterInformation* meterInterface) float getMeterValue(IAudioMeterInformation* meterInterface)
{ {
float volume; float volume;

View File

@@ -4,10 +4,14 @@
#include "ApplicationData.h" #include "ApplicationData.h"
void initAudio(ApplicationData& appData);
void loadAudioDevices(AudioData& audioData, std::vector<AudioDevice>& deviceList, EDataFlow deviceType);
void reloadDeviceLists(AudioData& audioData);
HRESULT getDeviceProperty(IPropertyStore* propertyStore, const PROPERTYKEY propertyKey, PROPVARIANT* outData); HRESULT getDeviceProperty(IPropertyStore* propertyStore, const PROPERTYKEY propertyKey, PROPVARIANT* outData);
HRESULT getDevicePropertyString(IPropertyStore* propertyStore, const PROPERTYKEY propertyKey, PROPVARIANT* outData, const wchar_t*& outString, const wchar_t* defaultStr = L"Unknown"); HRESULT getDevicePropertyString(IPropertyStore* propertyStore, const PROPERTYKEY propertyKey, PROPVARIANT* outData, const wchar_t*& outString, const wchar_t* defaultStr = L"Unknown");
void setDefaultAudioDevice(AudioData& audioData, const wchar_t* deviceId, ERole role); void setDefaultAudioDevice(AudioData& audioData, const wchar_t* deviceId, ERole role);
void loadAudioDevices(AudioData& audioData, std::vector<AudioDevice>& deviceList, EDataFlow deviceType);
void reloadDeviceLists(AudioData& audioData);
float getVolume(IAudioEndpointVolume* volumeInterface); float getVolume(IAudioEndpointVolume* volumeInterface);
void setVolume(IAudioEndpointVolume* volumeInterface, float newVolume);
float getMeterValue(IAudioMeterInformation* meterInterface); float getMeterValue(IAudioMeterInformation* meterInterface);

View File

@@ -126,7 +126,7 @@ HRESULT __stdcall AudioNotificationListener::QueryInterface(REFIID riid, void**
} }
else else
{ {
*ppvObject = NULL; *ppvObject = nullptr;
return E_NOINTERFACE; return E_NOINTERFACE;
} }

97
AsuroTool/Settings.cpp Normal file
View File

@@ -0,0 +1,97 @@
#include "ApplicationData.h"
#include "ImguiBase.h"
#include "Util.h"
#include "Settings.h"
extern bool justDocked;
extern DrawData* gDrawData;
extern ApplicationData* gAppData;
void initSettings(DrawData& drawData, ApplicationData& appData)
{
ImGuiSettingsHandler ini_handler;
ini_handler.TypeName = "ApplicationSettings";
ini_handler.TypeHash = ImHashStr("ApplicationSettings");
ini_handler.ReadOpenFn = settingsReadOpen;
ini_handler.ReadLineFn = settingsReadLine;
ini_handler.WriteAllFn = settingsWriteAll;
GImGui->SettingsHandlers.push_back(ini_handler);
ImGui::LoadIniSettingsFromDisk("imgui.ini");
applySettings(drawData, appData);
}
void* settingsReadOpen(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name)
{
ApplicationSettings* settings = &gAppData->settings;
*settings = ApplicationSettings();
return (void*)settings;
}
void settingsReadLine(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line)
{
ApplicationSettings* settings = (ApplicationSettings*)entry;
int docked;
if (sscanf_s(line, "docked=%i", &docked))
{
settings->docked = (bool)docked;
}
int autostart;
if (sscanf_s(line, "autostart=%i", &autostart))
{
settings->autostart = (bool)autostart;
}
}
void settingsWriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* outBuf)
{
outBuf->appendf("[%s][%s]\n", "ApplicationSettings", "ApplicationSettings");
outBuf->appendf("docked=%i\n", (int)gAppData->settings.docked);
outBuf->appendf("autostart=%i\n", (int)gAppData->settings.autostart);
outBuf->append("\n");
}
void applySettings(DrawData& drawData, ApplicationData& appData)
{
updateDocked(drawData, appData);
setAutostart(appData.settings.autostart);
}
void updateDocked(DrawData& drawData, ApplicationData& appData)
{
justDocked = true;
glfwSetWindowAttrib(drawData.window, GLFW_DECORATED, !appData.settings.docked);
ShowWindow(drawData.window_handle, SW_HIDE);
SetWindowLongPtr(drawData.window_handle, GWL_EXSTYLE, appData.settings.docked ? WS_EX_TOOLWINDOW : 0);
ShowWindow(drawData.window_handle, SW_SHOW);
}
void setAutostart(bool newValue)
{
const size_t MAX_PATH_LENGTH = 2048;
const wchar_t* KEY_APP_NAME = L"AsuroTool";
HRESULT hr;
HKEY runKey = nullptr;
hr = RegCreateKey(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", &runKey);
if (isError(hr, "Failed to find/create autostart run key: ")) return;
if (newValue)
{
std::wstring appPath;
appPath.resize(MAX_PATH_LENGTH);
hr = GetModuleFileName(NULL, &appPath[0], static_cast<DWORD>(appPath.size()));
if (isError(hr, "Failed to get executable name: ")) return;
appPath.resize(wcslen(appPath.data()));
hr = RegSetValueEx(runKey, KEY_APP_NAME, 0, REG_SZ, (BYTE*)appPath.c_str(), (appPath.size() + 1) * sizeof(wchar_t));
if (isError(hr, "Failed to write autostart key: ")) return;
}
else
{
hr = RegDeleteValue(runKey, KEY_APP_NAME);
if (isError(hr, "Failed to delete autostart key: ")) return;
}
}

15
AsuroTool/Settings.h Normal file
View File

@@ -0,0 +1,15 @@
#pragma once
#include "ImguiBase.h"
#include <imgui_internal.h>
#include "ApplicationData.h"
void initSettings(DrawData& drawData, ApplicationData& appData);
void* settingsReadOpen(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name);
void settingsReadLine(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line);
void settingsWriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* outBuf);
void applySettings(DrawData& drawData, ApplicationData& appData);
void updateDocked(DrawData& drawData, ApplicationData& appData);
void setAutostart(bool newValue);

View File

@@ -3,12 +3,14 @@
// Used by AsuroTool.rc // Used by AsuroTool.rc
// //
#define IDI_ICON1 109 #define IDI_ICON1 109
#define IDS_STRING112 112
#define IDS_STRING_TOOLTIP 112
// Next default values for new objects // Next default values for new objects
// //
#ifdef APSTUDIO_INVOKED #ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS #ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 112 #define _APS_NEXT_RESOURCE_VALUE 113
#define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101 #define _APS_NEXT_SYMED_VALUE 101

View File

@@ -1,12 +1,10 @@
#include "imgui.h"
#include "ImguiBase.h"
#include "imgui_impl_glfw.h" #include "imgui_impl_glfw.h"
#include "imgui_impl_vulkan.h" #include "imgui_impl_vulkan.h"
#include "ImguiBase.h"
#include <iostream> #include <iostream>
#define GLFW_INCLUDE_NONE
#define GLFW_INCLUDE_VULKAN
#define GLFW_EXPOSE_NATIVE_WIN32
#include <GLFW/glfw3.h>
#include <GLFW/glfw3native.h> #include <GLFW/glfw3native.h>
#include <vulkan/vulkan.h> #include <vulkan/vulkan.h>
@@ -331,7 +329,7 @@ static void glfw_error_callback(int error, const char* description)
fprintf(stderr, "Glfw Error %d: %s\n", error, description); fprintf(stderr, "Glfw Error %d: %s\n", error, description);
} }
int startImgui(void* customState, void (*const initFunc)(DrawData&, void*), void (*const drawFunc)(DrawData&, void*), const char* title, int windowWidth, int windowHeight) int startImgui(ImGuiCallbacks& callbacks, const char* title, int windowWidth, int windowHeight)
{ {
// Setup GLFW window // Setup GLFW window
glfwSetErrorCallback(glfw_error_callback); glfwSetErrorCallback(glfw_error_callback);
@@ -339,6 +337,7 @@ int startImgui(void* customState, void (*const initFunc)(DrawData&, void*), void
return 1; return 1;
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
GLFWwindow* window = glfwCreateWindow(windowWidth, windowHeight, title, NULL, NULL); GLFWwindow* window = glfwCreateWindow(windowWidth, windowHeight, title, NULL, NULL);
// Setup Vulkan // Setup Vulkan
@@ -365,6 +364,7 @@ int startImgui(void* customState, void (*const initFunc)(DrawData&, void*), void
// Setup Dear ImGui context // Setup Dear ImGui context
IMGUI_CHECKVERSION(); IMGUI_CHECKVERSION();
ImGui::CreateContext(); ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io; ImGuiIO& io = ImGui::GetIO(); (void)io;
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
@@ -408,10 +408,15 @@ int startImgui(void* customState, void (*const initFunc)(DrawData&, void*), void
// Our state // Our state
DrawData drawData{}; DrawData drawData{};
drawData.window = window;
drawData.window_handle = glfwGetWin32Window(window); drawData.window_handle = glfwGetWin32Window(window);
drawData.window_size = getWindowSize(window); drawData.window_size = getWindowSize(window);
initFunc(drawData, customState); if (callbacks.initFunc)
{
callbacks.initFunc(drawData);
}
glfwShowWindow(window);
// Upload Fonts // Upload Fonts
{ {
@@ -475,7 +480,10 @@ int startImgui(void* customState, void (*const initFunc)(DrawData&, void*), void
ImVec2 prevWindowSize = getWindowSize(window); ImVec2 prevWindowSize = getWindowSize(window);
drawData.window_size = prevWindowSize; drawData.window_size = prevWindowSize;
drawFunc(drawData, customState); if (callbacks.drawFunc)
{
callbacks.drawFunc(drawData);
}
if (drawData.window_size.x != prevWindowSize.x || drawData.window_size.y != prevWindowSize.y) if (drawData.window_size.x != prevWindowSize.x || drawData.window_size.y != prevWindowSize.y)
{ {
@@ -498,6 +506,10 @@ int startImgui(void* customState, void (*const initFunc)(DrawData&, void*), void
} }
// Cleanup // Cleanup
if (callbacks.cleanupFunc)
{
callbacks.cleanupFunc(drawData);
}
err = vkDeviceWaitIdle(g_Device); err = vkDeviceWaitIdle(g_Device);
check_vk_result(err); check_vk_result(err);
ImGui_ImplVulkan_Shutdown(); ImGui_ImplVulkan_Shutdown();

View File

@@ -1,16 +1,30 @@
#pragma once #pragma once
#include <Windows.h> #include <Windows.h>
#include "imgui_impl_glfw.h"
#include "imgui/headers/imgui.h" #include <functional>
#define GLFW_INCLUDE_NONE
#define GLFW_INCLUDE_VULKAN
#define GLFW_EXPOSE_NATIVE_WIN32
#include <GLFW/glfw3.h>
#include "imgui.h"
class DrawData { class DrawData {
public: public:
HWND window_handle = nullptr; GLFWwindow* window = nullptr;
ImVec4 clear_color = {}; HWND window_handle = nullptr;
ImVec2 window_size = {}; ImVec4 clear_color = {};
ImVec2 window_size = {};
}; };
int startImgui(void* customState, void (*const initFunc)(DrawData&, void*), void (* const drawFunc)(DrawData&, void*), const char* title, int windowWidth, int windowHeight); class ImGuiCallbacks {
public:
std::function<void(DrawData&)> initFunc = nullptr;
std::function<void(DrawData&)> drawFunc = nullptr;
std::function<void(DrawData&)> cleanupFunc = nullptr;
};
int startImgui(ImGuiCallbacks& callbacks, const char* title, int windowWidth, int windowHeight);
ImVec2 getWindowSize(GLFWwindow* window); ImVec2 getWindowSize(GLFWwindow* window);

View File

@@ -11,14 +11,17 @@
int main() int main()
{ {
ApplicationData applicationData{}; ApplicationData applicationData{};
ImGuiCallbacks callbacks{};
startImgui(&applicationData, init, draw, "Node Test", 1280, 720); callbacks.initFunc = std::bind(init, std::placeholders::_1, applicationData);
callbacks.drawFunc = std::bind(draw, std::placeholders::_1, applicationData);
startImgui(callbacks, "Node Test", 1280, 720);
} }
/// <summary> /// <summary>
/// Setup before first draw. /// Setup before first draw.
/// </summary> /// </summary>
void init(DrawData& drawData, void* customData) void init(DrawData& drawData, ApplicationData& customData)
{ {
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
io.Fonts->AddFontFromFileTTF("Montserrat-Regular.ttf", 16.0f); io.Fonts->AddFontFromFileTTF("Montserrat-Regular.ttf", 16.0f);
@@ -27,17 +30,15 @@ void init(DrawData& drawData, void* customData)
/// <summary> /// <summary>
/// Draw main application content. /// Draw main application content.
/// </summary> /// </summary>
void draw(DrawData& drawData, void* customData) void draw(DrawData& drawData, ApplicationData& applicationData)
{ {
ApplicationData* applicationData = static_cast<ApplicationData*>(customData);
// Top menu bar // Top menu bar
ImGui::BeginMainMenuBar(); ImGui::BeginMainMenuBar();
if (ImGui::Button("Add Node")) if (ImGui::Button("Add Node"))
{ {
std::string name{ "Node " }; std::string name{ "Node " };
name.append(std::to_string(applicationData->nodes.size() + 1)); name.append(std::to_string(applicationData.nodes.size() + 1));
applicationData->nodes.push_back(NodeWindow(name)); applicationData.nodes.push_back(NodeWindow(name));
} }
ImGui::EndMainMenuBar(); ImGui::EndMainMenuBar();
@@ -45,8 +46,8 @@ void draw(DrawData& drawData, void* customData)
bool isDraggingLine = false; bool isDraggingLine = false;
ImVec2 lineStartPos = ImVec2{}; ImVec2 lineStartPos = ImVec2{};
auto nodeIterator = applicationData->nodes.begin(); auto nodeIterator = applicationData.nodes.begin();
while (nodeIterator != applicationData->nodes.end()) while (nodeIterator != applicationData.nodes.end())
{ {
NodeWindow& node = *nodeIterator; NodeWindow& node = *nodeIterator;
@@ -56,7 +57,7 @@ void draw(DrawData& drawData, void* customData)
if (!nodeOpen) if (!nodeOpen)
{ {
cleanEreaseNodeElements(*nodeIterator, applicationData); cleanEreaseNodeElements(*nodeIterator, applicationData);
nodeIterator = applicationData->nodes.erase(nodeIterator); nodeIterator = applicationData.nodes.erase(nodeIterator);
ImGui::End(); ImGui::End();
break; // TODO: contine creates bug with closing too many windows??? break; // TODO: contine creates bug with closing too many windows???
} }
@@ -112,7 +113,7 @@ void draw(DrawData& drawData, void* customData)
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("NODE_CONNECTION")) if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("NODE_CONNECTION"))
{ {
ConnectionPayload* payloadData = static_cast<ConnectionPayload*>(payload->Data); ConnectionPayload* payloadData = static_cast<ConnectionPayload*>(payload->Data);
applicationData->addConnection(payloadData->sourceID, triggerIterator->id); applicationData.addConnection(payloadData->sourceID, triggerIterator->id);
} }
ImGui::EndDragDropTarget(); ImGui::EndDragDropTarget();
} }
@@ -153,11 +154,11 @@ void draw(DrawData& drawData, void* customData)
// Draw connected lines in background // Draw connected lines in background
ImDrawList* bgDrawList = ImGui::GetBackgroundDrawList(); ImDrawList* bgDrawList = ImGui::GetBackgroundDrawList();
for (NodeConnection& nodeConnection : applicationData->connections) for (NodeConnection& nodeConnection : applicationData.connections)
{ {
ImVec2 sourcePos = ImVec2{ 0, 0 }; ImVec2 sourcePos = ImVec2{ 0, 0 };
ImVec2 targetPos = ImVec2{ 0, 0 }; ImVec2 targetPos = ImVec2{ 0, 0 };
for (auto node : applicationData->nodes) for (auto node : applicationData.nodes)
{ {
for (auto alert : node.alerts) for (auto alert : node.alerts)
{ {
@@ -237,14 +238,14 @@ bool invisibleInlineButton(const char* title, const char* id)
/// Removes any attached connections before erasing this NodeElement from a vector. /// Removes any attached connections before erasing this NodeElement from a vector.
/// </summary> /// </summary>
/// <returns>Continued iterator</returns> /// <returns>Continued iterator</returns>
std::vector<NodeElement>::iterator cleanEraseElement(std::vector<NodeElement>& elements, std::vector<NodeElement>::iterator toErase, ApplicationData* data) std::vector<NodeElement>::iterator cleanEraseElement(std::vector<NodeElement>& elements, std::vector<NodeElement>::iterator toErase, ApplicationData& data)
{ {
auto connectionIterator = data->connections.begin(); auto connectionIterator = data.connections.begin();
while (connectionIterator != data->connections.end()) while (connectionIterator != data.connections.end())
{ {
if (connectionIterator->sourceID == toErase->id || connectionIterator->targetID == toErase->id) if (connectionIterator->sourceID == toErase->id || connectionIterator->targetID == toErase->id)
{ {
connectionIterator = data->connections.erase(connectionIterator); connectionIterator = data.connections.erase(connectionIterator);
continue; continue;
} }
connectionIterator++; connectionIterator++;
@@ -252,7 +253,7 @@ std::vector<NodeElement>::iterator cleanEraseElement(std::vector<NodeElement>& e
return elements.erase(toErase); return elements.erase(toErase);
} }
void cleanEreaseNodeElements(NodeWindow& node, ApplicationData* data) void cleanEreaseNodeElements(NodeWindow& node, ApplicationData& data)
{ {
auto triggerIterator = node.triggers.begin(); auto triggerIterator = node.triggers.begin();
while (triggerIterator != node.triggers.end()) while (triggerIterator != node.triggers.end())

View File

@@ -46,12 +46,12 @@ enum class CircleDragType
Target Target
}; };
void init(DrawData& drawData, void* customData); void init(DrawData& drawData, ApplicationData& customData);
void draw(DrawData& drawData, void* customDataVoid); void draw(DrawData& drawData, ApplicationData& customDataVoid);
void invisibleDragArea(const void* id, ImVec2& size); void invisibleDragArea(const void* id, ImVec2& size);
void drawCircle(ImVec2* outCircleCenter); void drawCircle(ImVec2* outCircleCenter);
bool inlineButton(const char* title, const void* id); bool inlineButton(const char* title, const void* id);
bool invisibleInlineButton(const char* title, const char* id); bool invisibleInlineButton(const char* title, const char* id);
std::vector<NodeElement>::iterator cleanEraseElement(std::vector<NodeElement>& vector, std::vector<NodeElement>::iterator toErase, ApplicationData* data); std::vector<NodeElement>::iterator cleanEraseElement(std::vector<NodeElement>& vector, std::vector<NodeElement>::iterator toErase, ApplicationData& data);
void cleanEreaseNodeElements(NodeWindow& node, ApplicationData* data); void cleanEreaseNodeElements(NodeWindow& node, ApplicationData& data);