timer
This commit is contained in:
@@ -1,12 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "AudioNotificationListener.h"
|
||||
#include "WindowsShell.h"
|
||||
|
||||
#include <endpointvolume.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <chrono>
|
||||
|
||||
class AudioDevice {
|
||||
public:
|
||||
@@ -49,6 +51,10 @@ public:
|
||||
bool showChecklistExtras = false;
|
||||
std::vector<std::string> taskNames = {};
|
||||
std::unordered_map<std::string, std::vector<time_t>> tasks = {};
|
||||
std::vector<std::string> baseStationMacAdresses = {};
|
||||
float timerDuration = 5.f * 60.f;
|
||||
float timerRepeatDuration = 2.f * 60.f;
|
||||
bool timerRepeating = false;
|
||||
};
|
||||
|
||||
enum class HoverTargetType
|
||||
@@ -57,6 +63,14 @@ enum class HoverTargetType
|
||||
HOVER_TARGET_CHECKLIST_DAY = 1,
|
||||
};
|
||||
|
||||
struct TimerData {
|
||||
bool isTimerActive = false;
|
||||
std::chrono::system_clock::time_point timerStartTimestamp;
|
||||
bool timerHasNotified = false;
|
||||
float lastNotifySeconds;
|
||||
TimerToastHandler toastHandler = {};
|
||||
};
|
||||
|
||||
class ApplicationData {
|
||||
public:
|
||||
ApplicationSettings settings = {};
|
||||
@@ -65,6 +79,8 @@ public:
|
||||
std::shared_ptr<AudioData> audioData = std::make_shared<AudioData>();
|
||||
HoverTargetType hoverTargetType = HoverTargetType::HOVER_TARGET_NONE;
|
||||
time_t hoverTargetDay = 0;
|
||||
TimerData timerData{};
|
||||
std::mutex timerMutex{};
|
||||
|
||||
//ApplicationData(const ApplicationData&) = delete;
|
||||
//ApplicationData& operator=(const ApplicationData&) = delete;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "Settings.h"
|
||||
#include "AsuroTool.h"
|
||||
#include "WindowsShell.h"
|
||||
#include "Timer.h"
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
@@ -22,8 +23,10 @@
|
||||
#include <format>
|
||||
#include <ctime>
|
||||
|
||||
|
||||
#define MAX_FONT_PATH_LENGTH 2048
|
||||
#define SETTINGS_POPUP_NAME "settings_popup"
|
||||
#define TIMER_WARNING_COLOR ImVec4(.7f, 0.2f, 0.2f, .5f)
|
||||
|
||||
// Globals for use in callbacks
|
||||
DrawData* gDrawData;
|
||||
@@ -34,6 +37,8 @@ time_t selectedDay = 0;
|
||||
|
||||
int main()
|
||||
{
|
||||
isError(CoInitializeEx(NULL, COINIT_MULTITHREADED), "Failed to initialize");
|
||||
|
||||
std::wstring appDir;
|
||||
getAppDir(appDir);
|
||||
if (_wchdir(appDir.c_str()) != 0)
|
||||
@@ -89,6 +94,10 @@ void init(DrawData& drawData, ApplicationData& appData)
|
||||
iconFontPath.append("\\remixicon.ttf");
|
||||
io.Fonts->AddFontFromFileTTF(iconFontPath.c_str(), 14.0f, &icons_config, icons_ranges);
|
||||
|
||||
// Start timer thread
|
||||
std::thread timerThread(updateTimer, std::ref(appData));
|
||||
timerThread.detach();
|
||||
|
||||
// Time
|
||||
selectedDay = getDayStartOf(std::time(nullptr));
|
||||
|
||||
@@ -118,14 +127,23 @@ void draw(DrawData& drawData, ApplicationData& appData)
|
||||
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.;
|
||||
|
||||
// Timer
|
||||
ImGui::SetNextWindowPos(ImVec2(0, customYCursor));
|
||||
ImGui::SetNextWindowSize(ImVec2(viewportSize.x / 2.f, 0));
|
||||
timerWindow(drawData, appData);
|
||||
|
||||
// Base Stations
|
||||
ImGui::SetNextWindowPos(ImVec2(viewportSize.x / 2.f, customYCursor));
|
||||
ImGui::SetNextWindowSize(ImVec2(viewportSize.x / 2.f, 0));
|
||||
customYCursor += baseStationWindow(appData).y;
|
||||
customYCursor += 5.;
|
||||
|
||||
// Playback Devices
|
||||
ImGui::SetNextWindowPos(ImVec2(0, customYCursor));
|
||||
ImGui::SetNextWindowSize(ImVec2(viewportSize.x, 0));
|
||||
customYCursor += audioDeviceWindow(appData, appData.audioData->playbackDevices, std::format(" {} Playback", ICON_HEADPHONE_FILL).c_str()).y;
|
||||
|
||||
customYCursor += 5.;
|
||||
|
||||
// Recording devices
|
||||
@@ -306,6 +324,162 @@ void drawDayLineButton(ApplicationData& appData, ImDrawList* drawList, float lin
|
||||
}
|
||||
}
|
||||
|
||||
void formatTime(std::chrono::system_clock::time_point time, char* buffer, size_t bufferSize)
|
||||
{
|
||||
const time_t tTime = std::chrono::system_clock::to_time_t(time);
|
||||
tm timeInfo;
|
||||
localtime_s(&timeInfo, &tTime);
|
||||
std::strftime(buffer, bufferSize, "%H:%M", &timeInfo);
|
||||
}
|
||||
|
||||
ImVec2 timerWindow(DrawData& drawData, ApplicationData& appData)
|
||||
{
|
||||
appData.timerMutex.lock();
|
||||
TimerData& timerData = appData.timerData;
|
||||
|
||||
std::string title;
|
||||
if (timerData.isTimerActive)
|
||||
{
|
||||
if (timerData.timerHasNotified)
|
||||
{
|
||||
title = std::format("{} Timer done!", ICON_TIMER_FILL);
|
||||
}
|
||||
else
|
||||
{
|
||||
float elapsedMinutes = calcElapsedSeconds(timerData.timerStartTimestamp) / 60.f;
|
||||
title = std::format("{} Timer ({:.0f}/{:.0f} minutes)", ICON_TIMER_FILL, elapsedMinutes, appData.settings.timerDuration / 60.f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
title = std::format("{} Timer", ICON_TIMER_FILL);
|
||||
}
|
||||
|
||||
// Draw window
|
||||
if (ImGui::Begin(title.c_str(), 0, ImGuiWindowFlags_NoResize))
|
||||
{
|
||||
if (windowHeaderButton(ICON_SETTINGS_FILL))
|
||||
{
|
||||
ImGui::OpenPopup(SETTINGS_POPUP_NAME);
|
||||
}
|
||||
|
||||
|
||||
float timerDisplayMinutes = appData.settings.timerDuration / 60.f;
|
||||
|
||||
if (timerData.isTimerActive)
|
||||
{
|
||||
float elapsedSeconds = calcElapsedSeconds(timerData.timerStartTimestamp);
|
||||
float timerPercentComplete = elapsedSeconds / appData.settings.timerDuration;
|
||||
float timerDisplayPercent = timerPercentComplete;
|
||||
timerDisplayMinutes = (appData.settings.timerDuration - elapsedSeconds) / 60.f;
|
||||
if (timerDisplayMinutes < 0.f) timerDisplayMinutes = 0.f;
|
||||
|
||||
// Draw background rectangle
|
||||
ImVec2 padding = ImGui::GetStyle().WindowPadding;
|
||||
ImVec2 contentMin = ImGui::GetWindowContentRegionMin();
|
||||
ImVec2 contentMax = ImGui::GetWindowContentRegionMax();
|
||||
ImVec2 windowPos = ImGui::GetWindowPos();
|
||||
|
||||
ImVec2 rectMin = { contentMin.x + windowPos.x - padding.x, contentMin.y + windowPos.y - padding.y };
|
||||
ImVec2 rectMax = { contentMax.x + windowPos.x + padding.x, contentMax.y + windowPos.y + padding.y };
|
||||
if (timerDisplayMinutes > 0.f)
|
||||
{
|
||||
float contentWidth = rectMax.x - rectMin.x;
|
||||
rectMax = { rectMin.x + contentWidth * (1.f - timerDisplayPercent), rectMax.y };
|
||||
}
|
||||
|
||||
ImDrawList* drawList = ImGui::GetWindowDrawList();
|
||||
ImRect tempRect = ImGui::GetCurrentWindow()->ClipRect;
|
||||
ImGui::PopClipRect();
|
||||
drawList->AddRectFilled(rectMin, rectMax, ImColor{ timerDisplayMinutes > 0.f ? ImGui::GetStyleColorVec4(ImGuiCol_TabUnfocusedActive) : TIMER_WARNING_COLOR });
|
||||
ImGui::PushClipRect(tempRect.Min, tempRect.Max, false);
|
||||
}
|
||||
|
||||
// Play/Reset
|
||||
if (ImGui::Button(timerData.isTimerActive ? ICON_RESTART_LINE : ICON_PLAY_FILL))
|
||||
{
|
||||
if (!timerData.isTimerActive)
|
||||
{
|
||||
timerData.isTimerActive = true;
|
||||
timerData.timerHasNotified = false;
|
||||
timerData.timerStartTimestamp = std::chrono::system_clock::now();
|
||||
}
|
||||
else
|
||||
{
|
||||
timerData.isTimerActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Timer controls
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5.f);
|
||||
|
||||
if (ImGui::Button(ICON_SUBTRACT_FILL))
|
||||
{
|
||||
appData.settings.timerDuration -= 60.f;
|
||||
if (appData.settings.timerDuration < 0.) appData.settings.timerDuration = 0.f;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::PushItemWidth(35.f);
|
||||
if (ImGui::InputFloat("##timer", &timerDisplayMinutes, 0, 0, "%.0fm"))
|
||||
{
|
||||
appData.settings.timerDuration = timerDisplayMinutes * 60.f;
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_ADD_FILL))
|
||||
{
|
||||
appData.settings.timerDuration += 60.f;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
char timeStr[32];
|
||||
std::string timerText;
|
||||
if (timerData.isTimerActive && timerData.timerHasNotified)
|
||||
{
|
||||
formatTime(timerData.timerStartTimestamp + std::chrono::seconds((int)appData.settings.timerDuration), timeStr, sizeof(timeStr));
|
||||
timerText = std::format("Ended at {}", timeStr);
|
||||
}
|
||||
else
|
||||
{
|
||||
formatTime(std::chrono::system_clock::now() + std::chrono::seconds((int)appData.settings.timerDuration), timeStr, sizeof(timeStr));
|
||||
timerText = std::format("Ends at {}", timeStr);
|
||||
}
|
||||
|
||||
float availWidth = ImGui::GetContentRegionMax().x - ImGui::GetCursorPosX();
|
||||
float textWidth = ImGui::CalcTextSize(timerText.c_str()).x;
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + availWidth - textWidth);
|
||||
ImGui::Text(timerText.c_str());
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopup(SETTINGS_POPUP_NAME))
|
||||
{
|
||||
// Loop
|
||||
//float loopContentWidth = ImGui::GetFrameHeight() + 5.f + ImGui::CalcTextSize("Loop").x + 5.f + 30.f + 10.f;
|
||||
//ImGui::SetCursorPosX(ImGui::GetWindowWidth() - loopContentWidth);
|
||||
ImGui::Checkbox("Loop", &appData.settings.timerRepeating);
|
||||
|
||||
ImGui::SameLine();
|
||||
float loopTimerMinutes = appData.settings.timerRepeatDuration / 60.f;
|
||||
ImGui::PushItemWidth(35.f);
|
||||
if (ImGui::InputFloat("##timerloop", &loopTimerMinutes, 0, 0, "%.0fm"))
|
||||
{
|
||||
appData.settings.timerRepeatDuration = loopTimerMinutes * 60.f;
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
appData.timerMutex.unlock();
|
||||
|
||||
ImVec2 size = ImGui::GetWindowSize();
|
||||
ImGui::End();
|
||||
return size;
|
||||
}
|
||||
|
||||
ImVec2 checklistWindow(ApplicationData& appData, const char* title)
|
||||
{
|
||||
if (ImGui::Begin(title, 0, ImGuiWindowFlags_NoResize))
|
||||
@@ -351,16 +525,19 @@ ImVec2 checklistWindow(ApplicationData& appData, const char* title)
|
||||
std::vector<time_t>& taskDates = appData.settings.tasks[taskName];
|
||||
bool taskDone = std::any_of(taskDates.begin(), taskDates.end(), selectedDayMatcher);
|
||||
|
||||
bool highlightButton = std::all_of(taskDates.begin(), taskDates.end(), [&](time_t t) {
|
||||
double timeDiffSeconds = difftime(today, getDayStartOf(t));
|
||||
return timeDiffSeconds >= appData.checklistHighlightDurationDays * 24 * 60 * 60 + 1;
|
||||
});
|
||||
time_t mostRecentDoneDate = *std::max_element(taskDates.begin(), taskDates.end());
|
||||
double timeDiffSeconds = difftime(today, getDayStartOf(mostRecentDoneDate));
|
||||
int timeDiffDays = timeDiffSeconds / (60 * 60 * 24);
|
||||
|
||||
bool highlightButton = timeDiffDays >= appData.checklistHighlightDurationDays;
|
||||
if (highlightButton)
|
||||
{
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, { .5f, .2f, .15f, 0.54f });
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, { .6f, .3f, .2f, 0.40f });
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, { .6f, .3f, .2f, 0.67f });
|
||||
}
|
||||
|
||||
ImVec2 beforeCheckbox = ImGui::GetCursorScreenPos();
|
||||
if (ImGui::Checkbox(taskName.c_str(), &taskDone))
|
||||
{
|
||||
if (taskDone)
|
||||
@@ -372,11 +549,21 @@ ImVec2 checklistWindow(ApplicationData& appData, const char* title)
|
||||
std::erase_if(taskDates, selectedDayMatcher);
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
if (highlightButton)
|
||||
{
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
ImVec2 afterCheckbox = ImGui::GetCursorScreenPos();
|
||||
std::string dayText = std::format("{}", timeDiffDays);
|
||||
float textWidth = ImGui::CalcTextSize(dayText.c_str()).x;
|
||||
ImGui::SetCursorScreenPos({ beforeCheckbox.x + ImGui::GetFrameHeight() / 2.f - textWidth / 2.f, beforeCheckbox.y });
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, { 1.f, 1.f, 1.f, 1.f });
|
||||
ImGui::Text(dayText.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SetCursorScreenPos(afterCheckbox);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopup(SETTINGS_POPUP_NAME))
|
||||
@@ -393,6 +580,77 @@ ImVec2 checklistWindow(ApplicationData& appData, const char* title)
|
||||
return size;
|
||||
}
|
||||
|
||||
void startBaseStationProc(const char* args)
|
||||
{
|
||||
// additional information
|
||||
STARTUPINFOA si;
|
||||
PROCESS_INFORMATION pi;
|
||||
|
||||
// set the size of the structures
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
|
||||
// start the program up
|
||||
DWORD creationFlags = 0;
|
||||
#ifdef _DEBUG
|
||||
creationFlags = CREATE_NEW_CONSOLE;
|
||||
#endif
|
||||
CreateProcessA("lighthouse-v2-manager.exe", const_cast<LPSTR>(args), NULL, NULL, FALSE, creationFlags, NULL, NULL, &si, &pi);
|
||||
|
||||
// Close process and thread handles.
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
}
|
||||
|
||||
ImVec2 baseStationWindow(ApplicationData& appData)
|
||||
{
|
||||
if (ImGui::Begin(std::format("{} Base Stations", ICON_ALARM_WARNING_FILL).c_str(), 0, ImGuiWindowFlags_NoResize))
|
||||
{
|
||||
if (windowHeaderButton(ICON_SETTINGS_FILL))
|
||||
{
|
||||
ImGui::OpenPopup(SETTINGS_POPUP_NAME);
|
||||
}
|
||||
|
||||
if (ImGui::Button("Wake"))
|
||||
{
|
||||
std::string params{ " on" };
|
||||
for (std::string& mac : appData.settings.baseStationMacAdresses)
|
||||
{
|
||||
params.append(" ");
|
||||
params.append(mac.c_str());
|
||||
}
|
||||
startBaseStationProc(params.c_str());
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Shutdown"))
|
||||
{
|
||||
std::string params{ " off" };
|
||||
for (std::string& mac : appData.settings.baseStationMacAdresses)
|
||||
{
|
||||
params.append(" ");
|
||||
params.append(mac.c_str());
|
||||
}
|
||||
startBaseStationProc(params.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopup(SETTINGS_POPUP_NAME))
|
||||
{
|
||||
if (ImGui::Button("Search"))
|
||||
{
|
||||
startBaseStationProc("discover");
|
||||
// TODO: parse stuff (annoying)
|
||||
}
|
||||
ImGui::Text(std::format("Known: {}", appData.settings.baseStationMacAdresses.size()).c_str());
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
@@ -15,5 +15,7 @@ void cleanup(DrawData& drawData, ApplicationData& appData);
|
||||
|
||||
ImVec2 menuBar(DrawData& drawData, ApplicationData& appData);
|
||||
ImVec2 checklistWindow(ApplicationData& appData, const char* title);
|
||||
ImVec2 timerWindow(DrawData& drawData, ApplicationData& appData);
|
||||
ImVec2 baseStationWindow(ApplicationData& appData);
|
||||
ImVec2 audioDeviceWindow(ApplicationData& appData, std::vector<AudioDevice>& deviceList, const char* title);
|
||||
void drawCircle(float radius, ImU32 color);
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>pathcch.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>pathcch.lib;runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<IgnoreSpecificDefaultLibraries>
|
||||
</IgnoreSpecificDefaultLibraries>
|
||||
</Link>
|
||||
@@ -119,7 +119,7 @@
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>pathcch.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>pathcch.lib;runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<IgnoreSpecificDefaultLibraries>
|
||||
</IgnoreSpecificDefaultLibraries>
|
||||
</Link>
|
||||
@@ -136,7 +136,7 @@
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>pathcch.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>pathcch.lib;runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<IgnoreSpecificDefaultLibraries>
|
||||
</IgnoreSpecificDefaultLibraries>
|
||||
</Link>
|
||||
@@ -157,7 +157,7 @@
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>pathcch.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>pathcch.lib;runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<IgnoreSpecificDefaultLibraries>
|
||||
</IgnoreSpecificDefaultLibraries>
|
||||
</Link>
|
||||
@@ -167,8 +167,10 @@
|
||||
<ClCompile Include="AsuroTool.cpp" />
|
||||
<ClCompile Include="AudioNotificationListener.cpp" />
|
||||
<ClCompile Include="Settings.cpp" />
|
||||
<ClCompile Include="Timer.cpp" />
|
||||
<ClCompile Include="Util.cpp" />
|
||||
<ClCompile Include="WindowsShell.cpp" />
|
||||
<ClCompile Include="WinToast\wintoastlib.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ApplicationData.h" />
|
||||
@@ -179,8 +181,10 @@
|
||||
<ClInclude Include="PolicyConfig.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="Settings.h" />
|
||||
<ClInclude Include="Timer.h" />
|
||||
<ClInclude Include="Util.h" />
|
||||
<ClInclude Include="WindowsShell.h" />
|
||||
<ClInclude Include="WinToast\wintoastlib.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ImguiBase\ImguiBase.vcxproj">
|
||||
@@ -215,12 +219,17 @@
|
||||
<ResourceCompile Include="AsuroTool.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="d:\nextcloud\dragon collection\profile pics\icon2.ico" />
|
||||
<Image Include="d:\nextcloud\dragon collection\profile pics\icon3.ico" />
|
||||
<Image Include="D:\Nextcloud\Dragon Collection\Profile Pics\kaiju.ico" />
|
||||
<Image Include="kaiju.ico" />
|
||||
<Image Include="kaiju32.png" />
|
||||
<Image Include="kaiju64.png" />
|
||||
<CopyFileToFolders Include="kaiju.ico">
|
||||
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</DeploymentContent>
|
||||
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</DeploymentContent>
|
||||
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</DeploymentContent>
|
||||
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</DeploymentContent>
|
||||
</CopyFileToFolders>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CopyFileToFolders Include="basestation\lighthouse-v2-manager.exe">
|
||||
<FileType>Document</FileType>
|
||||
</CopyFileToFolders>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
||||
@@ -19,6 +19,12 @@
|
||||
<Filter Include="Header Files\Audio">
|
||||
<UniqueIdentifier>{b65d213d-ddf6-4816-90d1-bf0811a51abf}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\lib">
|
||||
<UniqueIdentifier>{7571e2e0-bbb5-4c09-a047-e1d3c2a0ed5e}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\lib">
|
||||
<UniqueIdentifier>{ced4b7a2-267d-4b67-a8f1-41b3af68a5f0}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AsuroTool.cpp">
|
||||
@@ -42,6 +48,12 @@
|
||||
<ClCompile Include="WindowsShell.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="WinToast\wintoastlib.cpp">
|
||||
<Filter>Source Files\lib</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Timer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="AsuroTool.h">
|
||||
@@ -71,6 +83,12 @@
|
||||
<ClInclude Include="WindowsShell.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WinToast\wintoastlib.h">
|
||||
<Filter>Header Files\lib</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Timer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CopyFileToFolders Include="Montserrat-Regular.ttf">
|
||||
@@ -79,30 +97,14 @@
|
||||
<CopyFileToFolders Include="remixicon.ttf">
|
||||
<Filter>Resource Files</Filter>
|
||||
</CopyFileToFolders>
|
||||
<CopyFileToFolders Include="basestation\lighthouse-v2-manager.exe" />
|
||||
<CopyFileToFolders Include="kaiju.ico">
|
||||
<Filter>Resource Files</Filter>
|
||||
</CopyFileToFolders>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="AsuroTool.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="d:\nextcloud\dragon collection\profile pics\icon2.ico">
|
||||
<Filter>Resource Files</Filter>
|
||||
</Image>
|
||||
<Image Include="kaiju64.png">
|
||||
<Filter>Resource Files</Filter>
|
||||
</Image>
|
||||
<Image Include="d:\nextcloud\dragon collection\profile pics\icon3.ico">
|
||||
<Filter>Resource Files</Filter>
|
||||
</Image>
|
||||
<Image Include="D:\Nextcloud\Dragon Collection\Profile Pics\kaiju.ico">
|
||||
<Filter>Resource Files</Filter>
|
||||
</Image>
|
||||
<Image Include="kaiju32.png">
|
||||
<Filter>Resource Files</Filter>
|
||||
</Image>
|
||||
<Image Include="kaiju.ico">
|
||||
<Filter>Resource Files</Filter>
|
||||
</Image>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -16,11 +16,7 @@
|
||||
|
||||
void initAudio(ApplicationData& appData)
|
||||
{
|
||||
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));
|
||||
HRESULT 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);
|
||||
|
||||
@@ -68,6 +68,31 @@ void settingsReadLine(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* en
|
||||
settings->tasks.insert({ task, std::vector{ dayTimestamp } });
|
||||
}
|
||||
}
|
||||
|
||||
std::string baseStationMac{};
|
||||
baseStationMac.resize(MAX_MAC_ADDRESS_LENGTH);
|
||||
if (sscanf_s(line, "baseStationMac=%s", &baseStationMac[0], MAX_MAC_ADDRESS_LENGTH))
|
||||
{
|
||||
settings->baseStationMacAdresses.push_back(baseStationMac);
|
||||
}
|
||||
|
||||
float timerDuration;
|
||||
if (sscanf_s(line, "timerDuration=%f", &timerDuration))
|
||||
{
|
||||
settings->timerDuration = timerDuration;
|
||||
}
|
||||
|
||||
float timerRepeatDuration;
|
||||
if (sscanf_s(line, "timerRepeatDuration=%f", &timerRepeatDuration))
|
||||
{
|
||||
settings->timerRepeatDuration = timerRepeatDuration;
|
||||
}
|
||||
|
||||
int timerRepeating;
|
||||
if (sscanf_s(line, "timerRepeating=%i", &timerRepeating))
|
||||
{
|
||||
settings->timerRepeating = (bool)timerRepeating;
|
||||
}
|
||||
}
|
||||
|
||||
void settingsWriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* outBuf)
|
||||
@@ -88,6 +113,15 @@ void settingsWriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTex
|
||||
outBuf->appendf("task=%lld %s\n", date, task.first.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
for (std::string& baseStationMac : gAppData->settings.baseStationMacAdresses)
|
||||
{
|
||||
outBuf->appendf("baseStationMac=%s\n", baseStationMac.c_str());
|
||||
}
|
||||
|
||||
outBuf->appendf("timerDuration=%f\n", gAppData->settings.timerDuration);
|
||||
outBuf->appendf("timerRepeatDuration=%f\n", gAppData->settings.timerRepeatDuration);
|
||||
outBuf->appendf("timerRepeating=%i\n", (int)gAppData->settings.timerRepeating);
|
||||
}
|
||||
|
||||
void applySettings(DrawData& drawData, ApplicationData& appData)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#define APPLICATION_SETTINGS_GROUP "ApplicationSettings"
|
||||
#define MAX_TASK_NAME_LENGTH 1024
|
||||
#define MAX_MAC_ADDRESS_LENGTH 128
|
||||
|
||||
void initSettings(DrawData& drawData, ApplicationData& appData);
|
||||
|
||||
|
||||
65
AsuroTool/Timer.cpp
Normal file
65
AsuroTool/Timer.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#include "Timer.h"
|
||||
|
||||
|
||||
float calcElapsedSeconds(std::chrono::system_clock::time_point startTime)
|
||||
{
|
||||
auto elapsedTime = std::chrono::system_clock::now() - startTime;
|
||||
return std::chrono::duration_cast<std::chrono::seconds>(elapsedTime).count();
|
||||
}
|
||||
|
||||
void updateTimer(ApplicationData& appData)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
appData.timerMutex.lock();
|
||||
TimerData& timerData = appData.timerData;
|
||||
|
||||
if (timerData.isTimerActive)
|
||||
{
|
||||
// Main timer
|
||||
float elapsedSeconds = calcElapsedSeconds(timerData.timerStartTimestamp);
|
||||
float timerPercentComplete = elapsedSeconds / appData.settings.timerDuration;
|
||||
|
||||
if (timerPercentComplete >= 1.f)
|
||||
{
|
||||
if (!timerData.timerHasNotified)
|
||||
{
|
||||
// Alert
|
||||
timerData.timerHasNotified = true;
|
||||
|
||||
std::wstring titleText = std::format(L"{} minutes over!", appData.settings.timerDuration / 60.f);
|
||||
|
||||
std::wstring messageText;
|
||||
if (appData.settings.timerRepeating)
|
||||
{
|
||||
messageText = std::format(L"Repeats every {} minutes.", appData.settings.timerRepeatDuration / 60.f);
|
||||
timerData.lastNotifySeconds = elapsedSeconds;
|
||||
}
|
||||
else
|
||||
{
|
||||
timerData.isTimerActive = false;
|
||||
messageText = L"Timer does not repeat.";
|
||||
}
|
||||
showToastNotification(&timerData.toastHandler, titleText.c_str(), messageText.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Loop timer
|
||||
if (elapsedSeconds - timerData.lastNotifySeconds >= appData.settings.timerRepeatDuration)
|
||||
{
|
||||
timerData.lastNotifySeconds = elapsedSeconds;
|
||||
|
||||
float elapsedSecondsSinceEnd = elapsedSeconds - appData.settings.timerDuration;
|
||||
std::wstring titleText = std::format(L"Timer ended {} minutes ago", elapsedSecondsSinceEnd / 60.f);
|
||||
std::wstring messageText = std::format(L"Repeats every {} minutes.", appData.settings.timerRepeatDuration / 60.f);
|
||||
|
||||
showToastNotification(&timerData.toastHandler, titleText.c_str(), messageText.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
appData.timerMutex.unlock();
|
||||
Sleep(1000);
|
||||
}
|
||||
}
|
||||
8
AsuroTool/Timer.h
Normal file
8
AsuroTool/Timer.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include "ApplicationData.h"
|
||||
|
||||
float calcElapsedSeconds(std::chrono::system_clock::time_point startTime);
|
||||
void updateTimer(ApplicationData& appData);
|
||||
1183
AsuroTool/WinToast/wintoastlib.cpp
Normal file
1183
AsuroTool/WinToast/wintoastlib.cpp
Normal file
File diff suppressed because it is too large
Load Diff
234
AsuroTool/WinToast/wintoastlib.h
Normal file
234
AsuroTool/WinToast/wintoastlib.h
Normal file
@@ -0,0 +1,234 @@
|
||||
/* * Copyright (C) 2016-2019 Mohammed Boujemaoui <mohabouje@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef WINTOASTLIB_H
|
||||
#define WINTOASTLIB_H
|
||||
#include <Windows.h>
|
||||
#include <sdkddkver.h>
|
||||
#include <WinUser.h>
|
||||
#include <ShObjIdl.h>
|
||||
#include <wrl/implements.h>
|
||||
#include <wrl/event.h>
|
||||
#include <windows.ui.notifications.h>
|
||||
#include <strsafe.h>
|
||||
#include <Psapi.h>
|
||||
#include <ShlObj.h>
|
||||
#include <roapi.h>
|
||||
#include <propvarutil.h>
|
||||
#include <functiondiscoverykeys.h>
|
||||
#include <iostream>
|
||||
#include <winstring.h>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace ABI::Windows::Data::Xml::Dom;
|
||||
using namespace ABI::Windows::Foundation;
|
||||
using namespace ABI::Windows::UI::Notifications;
|
||||
using namespace Windows::Foundation;
|
||||
|
||||
|
||||
namespace WinToastLib {
|
||||
|
||||
class IWinToastHandler {
|
||||
public:
|
||||
enum WinToastDismissalReason {
|
||||
UserCanceled = ToastDismissalReason::ToastDismissalReason_UserCanceled,
|
||||
ApplicationHidden = ToastDismissalReason::ToastDismissalReason_ApplicationHidden,
|
||||
TimedOut = ToastDismissalReason::ToastDismissalReason_TimedOut
|
||||
};
|
||||
virtual ~IWinToastHandler() = default;
|
||||
virtual void toastActivated() const = 0;
|
||||
virtual void toastActivated(int actionIndex) const = 0;
|
||||
virtual void toastDismissed(WinToastDismissalReason state) const = 0;
|
||||
virtual void toastFailed() const = 0;
|
||||
};
|
||||
|
||||
class WinToastTemplate {
|
||||
public:
|
||||
enum class Scenario { Default, Alarm, IncomingCall, Reminder };
|
||||
enum Duration { System, Short, Long };
|
||||
enum AudioOption { Default = 0, Silent, Loop };
|
||||
enum TextField { FirstLine = 0, SecondLine, ThirdLine };
|
||||
enum WinToastTemplateType {
|
||||
ImageAndText01 = ToastTemplateType::ToastTemplateType_ToastImageAndText01,
|
||||
ImageAndText02 = ToastTemplateType::ToastTemplateType_ToastImageAndText02,
|
||||
ImageAndText03 = ToastTemplateType::ToastTemplateType_ToastImageAndText03,
|
||||
ImageAndText04 = ToastTemplateType::ToastTemplateType_ToastImageAndText04,
|
||||
Text01 = ToastTemplateType::ToastTemplateType_ToastText01,
|
||||
Text02 = ToastTemplateType::ToastTemplateType_ToastText02,
|
||||
Text03 = ToastTemplateType::ToastTemplateType_ToastText03,
|
||||
Text04 = ToastTemplateType::ToastTemplateType_ToastText04,
|
||||
};
|
||||
|
||||
enum AudioSystemFile {
|
||||
DefaultSound,
|
||||
IM,
|
||||
Mail,
|
||||
Reminder,
|
||||
SMS,
|
||||
Alarm,
|
||||
Alarm2,
|
||||
Alarm3,
|
||||
Alarm4,
|
||||
Alarm5,
|
||||
Alarm6,
|
||||
Alarm7,
|
||||
Alarm8,
|
||||
Alarm9,
|
||||
Alarm10,
|
||||
Call,
|
||||
Call1,
|
||||
Call2,
|
||||
Call3,
|
||||
Call4,
|
||||
Call5,
|
||||
Call6,
|
||||
Call7,
|
||||
Call8,
|
||||
Call9,
|
||||
Call10,
|
||||
};
|
||||
|
||||
|
||||
WinToastTemplate(_In_ WinToastTemplateType type = WinToastTemplateType::ImageAndText02);
|
||||
~WinToastTemplate();
|
||||
|
||||
void setFirstLine(_In_ const std::wstring& text);
|
||||
void setSecondLine(_In_ const std::wstring& text);
|
||||
void setThirdLine(_In_ const std::wstring& text);
|
||||
void setTextField(_In_ const std::wstring& txt, _In_ TextField pos);
|
||||
void setAttributionText(_In_ const std::wstring& attributionText);
|
||||
void setImagePath(_In_ const std::wstring& imgPath);
|
||||
void setAudioPath(_In_ WinToastTemplate::AudioSystemFile audio);
|
||||
void setAudioPath(_In_ const std::wstring& audioPath);
|
||||
void setAudioOption(_In_ WinToastTemplate::AudioOption audioOption);
|
||||
void setDuration(_In_ Duration duration);
|
||||
void setExpiration(_In_ INT64 millisecondsFromNow);
|
||||
void setScenario(_In_ Scenario scenario);
|
||||
void addAction(_In_ const std::wstring& label);
|
||||
|
||||
std::size_t textFieldsCount() const;
|
||||
std::size_t actionsCount() const;
|
||||
bool hasImage() const;
|
||||
const std::vector<std::wstring>& textFields() const;
|
||||
const std::wstring& textField(_In_ TextField pos) const;
|
||||
const std::wstring& actionLabel(_In_ std::size_t pos) const;
|
||||
const std::wstring& imagePath() const;
|
||||
const std::wstring& audioPath() const;
|
||||
const std::wstring& attributionText() const;
|
||||
const std::wstring& scenario() const;
|
||||
INT64 expiration() const;
|
||||
WinToastTemplateType type() const;
|
||||
WinToastTemplate::AudioOption audioOption() const;
|
||||
Duration duration() const;
|
||||
private:
|
||||
std::vector<std::wstring> _textFields{};
|
||||
std::vector<std::wstring> _actions{};
|
||||
std::wstring _imagePath{};
|
||||
std::wstring _audioPath{};
|
||||
std::wstring _attributionText{};
|
||||
std::wstring _scenario{L"Default"};
|
||||
INT64 _expiration{0};
|
||||
AudioOption _audioOption{WinToastTemplate::AudioOption::Default};
|
||||
WinToastTemplateType _type{WinToastTemplateType::Text01};
|
||||
Duration _duration{Duration::System};
|
||||
};
|
||||
|
||||
class WinToast {
|
||||
public:
|
||||
enum WinToastError {
|
||||
NoError = 0,
|
||||
NotInitialized,
|
||||
SystemNotSupported,
|
||||
ShellLinkNotCreated,
|
||||
InvalidAppUserModelID,
|
||||
InvalidParameters,
|
||||
InvalidHandler,
|
||||
NotDisplayed,
|
||||
UnknownError
|
||||
};
|
||||
|
||||
enum ShortcutResult {
|
||||
SHORTCUT_UNCHANGED = 0,
|
||||
SHORTCUT_WAS_CHANGED = 1,
|
||||
SHORTCUT_WAS_CREATED = 2,
|
||||
|
||||
SHORTCUT_MISSING_PARAMETERS = -1,
|
||||
SHORTCUT_INCOMPATIBLE_OS = -2,
|
||||
SHORTCUT_COM_INIT_FAILURE = -3,
|
||||
SHORTCUT_CREATE_FAILED = -4
|
||||
};
|
||||
|
||||
enum ShortcutPolicy {
|
||||
/* Don't check, create, or modify a shortcut. */
|
||||
SHORTCUT_POLICY_IGNORE = 0,
|
||||
/* Require a shortcut with matching AUMI, don't create or modify an existing one. */
|
||||
SHORTCUT_POLICY_REQUIRE_NO_CREATE = 1,
|
||||
/* Require a shortcut with matching AUMI, create if missing, modify if not matching.
|
||||
* This is the default. */
|
||||
SHORTCUT_POLICY_REQUIRE_CREATE = 2,
|
||||
};
|
||||
|
||||
WinToast(void);
|
||||
virtual ~WinToast();
|
||||
static WinToast* instance();
|
||||
static bool isCompatible();
|
||||
static bool isSupportingModernFeatures();
|
||||
static std::wstring configureAUMI(_In_ const std::wstring& companyName,
|
||||
_In_ const std::wstring& productName,
|
||||
_In_ const std::wstring& subProduct = std::wstring(),
|
||||
_In_ const std::wstring& versionInformation = std::wstring());
|
||||
static const std::wstring& strerror(_In_ WinToastError error);
|
||||
virtual bool initialize(_Out_opt_ WinToastError* error = nullptr);
|
||||
virtual bool isInitialized() const;
|
||||
virtual bool hideToast(_In_ INT64 id);
|
||||
virtual INT64 showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHandler* handler, _Out_opt_ WinToastError* error = nullptr);
|
||||
virtual void clear();
|
||||
virtual enum ShortcutResult createShortcut();
|
||||
|
||||
const std::wstring& appName() const;
|
||||
const std::wstring& appUserModelId() const;
|
||||
void setAppUserModelId(_In_ const std::wstring& aumi);
|
||||
void setAppName(_In_ const std::wstring& appName);
|
||||
void setShortcutPolicy(_In_ ShortcutPolicy policy);
|
||||
|
||||
protected:
|
||||
bool _isInitialized{false};
|
||||
bool _hasCoInitialized{false};
|
||||
ShortcutPolicy _shortcutPolicy{SHORTCUT_POLICY_REQUIRE_CREATE};
|
||||
std::wstring _appName{};
|
||||
std::wstring _aumi{};
|
||||
std::map<INT64, ComPtr<IToastNotification>> _buffer{};
|
||||
|
||||
HRESULT validateShellLinkHelper(_Out_ bool& wasChanged);
|
||||
HRESULT createShellLinkHelper();
|
||||
HRESULT setImageFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path);
|
||||
HRESULT setAudioFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path, _In_opt_ WinToastTemplate::AudioOption option = WinToastTemplate::AudioOption::Default);
|
||||
HRESULT setTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text, _In_ UINT32 pos);
|
||||
HRESULT setAttributionTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text);
|
||||
HRESULT addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& action, _In_ const std::wstring& arguments);
|
||||
HRESULT addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& duration);
|
||||
HRESULT addScenarioHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& scenario);
|
||||
ComPtr<IToastNotifier> notifier(_In_ bool* succeded) const;
|
||||
void setError(_Out_opt_ WinToastError* error, _In_ WinToastError value);
|
||||
};
|
||||
}
|
||||
#endif // WINTOASTLIB_H
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "ApplicationData.h"
|
||||
#include "Util.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
#include <shellapi.h>
|
||||
#include <commctrl.h>
|
||||
@@ -53,6 +54,17 @@ void initShell(DrawData& drawData)
|
||||
std::cout << "Failed to hook tray icon events: " << std::hex << GetLastError() << std::endl;
|
||||
}
|
||||
|
||||
// Setup Toasts
|
||||
if (!WinToast::isCompatible()) {
|
||||
OutputDebugString(L"Error, your system in not supported!\n");
|
||||
}
|
||||
WinToast::instance()->setAppName(L"AudioThingy");
|
||||
const auto aumi = WinToast::configureAUMI(L"asuro", L"audiothingy");
|
||||
WinToast::instance()->setAppUserModelId(aumi);
|
||||
if (!WinToast::instance()->initialize()) {
|
||||
OutputDebugString(L"Error, could not initialize the lib!\n");
|
||||
}
|
||||
|
||||
// Set window minimize behavior
|
||||
glfwSetWindowIconifyCallback(drawData.window, [](GLFWwindow* window, int isIconified) {
|
||||
if (isIconified && gAppData->settings.docked)
|
||||
@@ -70,6 +82,18 @@ void initShell(DrawData& drawData)
|
||||
});
|
||||
}
|
||||
|
||||
void showToastNotification(TimerToastHandler* handler, const wchar_t* title, const wchar_t* text)
|
||||
{
|
||||
WinToastTemplate templ = WinToastTemplate(WinToastTemplate::ImageAndText02);
|
||||
templ.setImagePath(L"E:\\Code\\AsuroImgui\\x64\\Debug\\kaiju.ico");
|
||||
templ.setTextField(title, WinToastTemplate::FirstLine);
|
||||
templ.setTextField(text, WinToastTemplate::SecondLine);
|
||||
templ.addAction(L"Stop");
|
||||
if (!WinToast::instance()->showToast(templ, handler)) {
|
||||
OutputDebugString(L"Error: Could not launch your toast notification!\n");
|
||||
}
|
||||
}
|
||||
|
||||
void cleanupShell(DrawData& drawData)
|
||||
{
|
||||
// Remove tray icon
|
||||
@@ -100,3 +124,21 @@ LRESULT CALLBACK trayIconEventHandler(int code, WPARAM wParam, LPARAM lParam)
|
||||
|
||||
return CallNextHookEx(NULL, code, wParam, lParam);
|
||||
}
|
||||
|
||||
void TimerToastHandler::toastActivated() const
|
||||
{
|
||||
glfwShowWindow(gDrawData->window);
|
||||
glfwRestoreWindow(gDrawData->window);
|
||||
}
|
||||
|
||||
void TimerToastHandler::toastActivated(int actionIndex) const
|
||||
{
|
||||
}
|
||||
|
||||
void TimerToastHandler::toastFailed() const
|
||||
{
|
||||
}
|
||||
|
||||
void TimerToastHandler::toastDismissed(WinToastDismissalReason state) const
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,8 +1,22 @@
|
||||
#pragma once
|
||||
#include <ImguiBase.h>
|
||||
|
||||
#include "WinToast/wintoastlib.h"
|
||||
|
||||
using namespace WinToastLib;
|
||||
|
||||
struct TimerToastHandler : IWinToastHandler
|
||||
{
|
||||
void toastActivated() const override;
|
||||
void toastActivated(int actionIndex) const override;
|
||||
void toastFailed() const override;
|
||||
void toastDismissed(WinToastDismissalReason state) const override;
|
||||
};
|
||||
|
||||
void initShell(DrawData& drawData);
|
||||
|
||||
void showToastNotification(TimerToastHandler* handler, const wchar_t* title, const wchar_t* text);
|
||||
|
||||
void cleanupShell(DrawData& drawData);
|
||||
|
||||
LRESULT trayIconEventHandler(int code, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
Reference in New Issue
Block a user