timer
This commit is contained in:
@@ -1,12 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "AudioNotificationListener.h"
|
#include "AudioNotificationListener.h"
|
||||||
|
#include "WindowsShell.h"
|
||||||
|
|
||||||
#include <endpointvolume.h>
|
#include <endpointvolume.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
class AudioDevice {
|
class AudioDevice {
|
||||||
public:
|
public:
|
||||||
@@ -49,6 +51,10 @@ public:
|
|||||||
bool showChecklistExtras = false;
|
bool showChecklistExtras = false;
|
||||||
std::vector<std::string> taskNames = {};
|
std::vector<std::string> taskNames = {};
|
||||||
std::unordered_map<std::string, std::vector<time_t>> tasks = {};
|
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
|
enum class HoverTargetType
|
||||||
@@ -57,6 +63,14 @@ enum class HoverTargetType
|
|||||||
HOVER_TARGET_CHECKLIST_DAY = 1,
|
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 {
|
class ApplicationData {
|
||||||
public:
|
public:
|
||||||
ApplicationSettings settings = {};
|
ApplicationSettings settings = {};
|
||||||
@@ -65,6 +79,8 @@ public:
|
|||||||
std::shared_ptr<AudioData> audioData = std::make_shared<AudioData>();
|
std::shared_ptr<AudioData> audioData = std::make_shared<AudioData>();
|
||||||
HoverTargetType hoverTargetType = HoverTargetType::HOVER_TARGET_NONE;
|
HoverTargetType hoverTargetType = HoverTargetType::HOVER_TARGET_NONE;
|
||||||
time_t hoverTargetDay = 0;
|
time_t hoverTargetDay = 0;
|
||||||
|
TimerData timerData{};
|
||||||
|
std::mutex timerMutex{};
|
||||||
|
|
||||||
//ApplicationData(const ApplicationData&) = delete;
|
//ApplicationData(const ApplicationData&) = delete;
|
||||||
//ApplicationData& operator=(const ApplicationData&) = delete;
|
//ApplicationData& operator=(const ApplicationData&) = delete;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#include "AsuroTool.h"
|
#include "AsuroTool.h"
|
||||||
#include "WindowsShell.h"
|
#include "WindowsShell.h"
|
||||||
|
#include "Timer.h"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@@ -22,8 +23,10 @@
|
|||||||
#include <format>
|
#include <format>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
|
||||||
|
|
||||||
#define MAX_FONT_PATH_LENGTH 2048
|
#define MAX_FONT_PATH_LENGTH 2048
|
||||||
#define SETTINGS_POPUP_NAME "settings_popup"
|
#define SETTINGS_POPUP_NAME "settings_popup"
|
||||||
|
#define TIMER_WARNING_COLOR ImVec4(.7f, 0.2f, 0.2f, .5f)
|
||||||
|
|
||||||
// Globals for use in callbacks
|
// Globals for use in callbacks
|
||||||
DrawData* gDrawData;
|
DrawData* gDrawData;
|
||||||
@@ -34,6 +37,8 @@ time_t selectedDay = 0;
|
|||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
|
isError(CoInitializeEx(NULL, COINIT_MULTITHREADED), "Failed to initialize");
|
||||||
|
|
||||||
std::wstring appDir;
|
std::wstring appDir;
|
||||||
getAppDir(appDir);
|
getAppDir(appDir);
|
||||||
if (_wchdir(appDir.c_str()) != 0)
|
if (_wchdir(appDir.c_str()) != 0)
|
||||||
@@ -89,6 +94,10 @@ void init(DrawData& drawData, ApplicationData& appData)
|
|||||||
iconFontPath.append("\\remixicon.ttf");
|
iconFontPath.append("\\remixicon.ttf");
|
||||||
io.Fonts->AddFontFromFileTTF(iconFontPath.c_str(), 14.0f, &icons_config, icons_ranges);
|
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
|
// Time
|
||||||
selectedDay = getDayStartOf(std::time(nullptr));
|
selectedDay = getDayStartOf(std::time(nullptr));
|
||||||
|
|
||||||
@@ -118,14 +127,23 @@ void draw(DrawData& drawData, ApplicationData& appData)
|
|||||||
ImGui::SetNextWindowPos(ImVec2(0, customYCursor));
|
ImGui::SetNextWindowPos(ImVec2(0, customYCursor));
|
||||||
ImGui::SetNextWindowSize(ImVec2(viewportSize.x, 0));
|
ImGui::SetNextWindowSize(ImVec2(viewportSize.x, 0));
|
||||||
customYCursor += checklistWindow(appData, std::format(" {} Checklist", ICON_CHECK_FILL).c_str()).y;
|
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.;
|
customYCursor += 5.;
|
||||||
|
|
||||||
// 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, std::format(" {} Playback", ICON_HEADPHONE_FILL).c_str()).y;
|
customYCursor += audioDeviceWindow(appData, appData.audioData->playbackDevices, std::format(" {} Playback", ICON_HEADPHONE_FILL).c_str()).y;
|
||||||
|
|
||||||
customYCursor += 5.;
|
customYCursor += 5.;
|
||||||
|
|
||||||
// Recording devices
|
// 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)
|
ImVec2 checklistWindow(ApplicationData& appData, const char* title)
|
||||||
{
|
{
|
||||||
if (ImGui::Begin(title, 0, ImGuiWindowFlags_NoResize))
|
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];
|
std::vector<time_t>& taskDates = appData.settings.tasks[taskName];
|
||||||
bool taskDone = std::any_of(taskDates.begin(), taskDates.end(), selectedDayMatcher);
|
bool taskDone = std::any_of(taskDates.begin(), taskDates.end(), selectedDayMatcher);
|
||||||
|
|
||||||
bool highlightButton = std::all_of(taskDates.begin(), taskDates.end(), [&](time_t t) {
|
time_t mostRecentDoneDate = *std::max_element(taskDates.begin(), taskDates.end());
|
||||||
double timeDiffSeconds = difftime(today, getDayStartOf(t));
|
double timeDiffSeconds = difftime(today, getDayStartOf(mostRecentDoneDate));
|
||||||
return timeDiffSeconds >= appData.checklistHighlightDurationDays * 24 * 60 * 60 + 1;
|
int timeDiffDays = timeDiffSeconds / (60 * 60 * 24);
|
||||||
});
|
|
||||||
|
bool highlightButton = timeDiffDays >= appData.checklistHighlightDurationDays;
|
||||||
if (highlightButton)
|
if (highlightButton)
|
||||||
{
|
{
|
||||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, { .5f, .2f, .15f, 0.54f });
|
ImGui::PushStyleColor(ImGuiCol_FrameBg, { .5f, .2f, .15f, 0.54f });
|
||||||
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, { .6f, .3f, .2f, 0.40f });
|
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, { .6f, .3f, .2f, 0.40f });
|
||||||
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, { .6f, .3f, .2f, 0.67f });
|
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, { .6f, .3f, .2f, 0.67f });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImVec2 beforeCheckbox = ImGui::GetCursorScreenPos();
|
||||||
if (ImGui::Checkbox(taskName.c_str(), &taskDone))
|
if (ImGui::Checkbox(taskName.c_str(), &taskDone))
|
||||||
{
|
{
|
||||||
if (taskDone)
|
if (taskDone)
|
||||||
@@ -372,11 +549,21 @@ ImVec2 checklistWindow(ApplicationData& appData, const char* title)
|
|||||||
std::erase_if(taskDates, selectedDayMatcher);
|
std::erase_if(taskDates, selectedDayMatcher);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
if (highlightButton)
|
if (highlightButton)
|
||||||
{
|
{
|
||||||
ImGui::PopStyleColor(3);
|
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))
|
if (ImGui::BeginPopup(SETTINGS_POPUP_NAME))
|
||||||
@@ -393,6 +580,77 @@ ImVec2 checklistWindow(ApplicationData& appData, const char* title)
|
|||||||
return size;
|
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)
|
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))
|
||||||
|
|||||||
@@ -15,5 +15,7 @@ void cleanup(DrawData& drawData, ApplicationData& appData);
|
|||||||
|
|
||||||
ImVec2 menuBar(DrawData& drawData, ApplicationData& appData);
|
ImVec2 menuBar(DrawData& drawData, ApplicationData& appData);
|
||||||
ImVec2 checklistWindow(ApplicationData& appData, const char* title);
|
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);
|
ImVec2 audioDeviceWindow(ApplicationData& appData, std::vector<AudioDevice>& deviceList, const char* title);
|
||||||
void drawCircle(float radius, ImU32 color);
|
void drawCircle(float radius, ImU32 color);
|
||||||
|
|||||||
@@ -98,7 +98,7 @@
|
|||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Console</SubSystem>
|
<SubSystem>Console</SubSystem>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<AdditionalDependencies>pathcch.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>pathcch.lib;runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
<IgnoreSpecificDefaultLibraries>
|
<IgnoreSpecificDefaultLibraries>
|
||||||
</IgnoreSpecificDefaultLibraries>
|
</IgnoreSpecificDefaultLibraries>
|
||||||
</Link>
|
</Link>
|
||||||
@@ -119,7 +119,7 @@
|
|||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<AdditionalDependencies>pathcch.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>pathcch.lib;runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
<IgnoreSpecificDefaultLibraries>
|
<IgnoreSpecificDefaultLibraries>
|
||||||
</IgnoreSpecificDefaultLibraries>
|
</IgnoreSpecificDefaultLibraries>
|
||||||
</Link>
|
</Link>
|
||||||
@@ -136,7 +136,7 @@
|
|||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Console</SubSystem>
|
<SubSystem>Console</SubSystem>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<AdditionalDependencies>pathcch.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>pathcch.lib;runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
<IgnoreSpecificDefaultLibraries>
|
<IgnoreSpecificDefaultLibraries>
|
||||||
</IgnoreSpecificDefaultLibraries>
|
</IgnoreSpecificDefaultLibraries>
|
||||||
</Link>
|
</Link>
|
||||||
@@ -157,7 +157,7 @@
|
|||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<AdditionalDependencies>pathcch.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>pathcch.lib;runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
<IgnoreSpecificDefaultLibraries>
|
<IgnoreSpecificDefaultLibraries>
|
||||||
</IgnoreSpecificDefaultLibraries>
|
</IgnoreSpecificDefaultLibraries>
|
||||||
</Link>
|
</Link>
|
||||||
@@ -167,8 +167,10 @@
|
|||||||
<ClCompile Include="AsuroTool.cpp" />
|
<ClCompile Include="AsuroTool.cpp" />
|
||||||
<ClCompile Include="AudioNotificationListener.cpp" />
|
<ClCompile Include="AudioNotificationListener.cpp" />
|
||||||
<ClCompile Include="Settings.cpp" />
|
<ClCompile Include="Settings.cpp" />
|
||||||
|
<ClCompile Include="Timer.cpp" />
|
||||||
<ClCompile Include="Util.cpp" />
|
<ClCompile Include="Util.cpp" />
|
||||||
<ClCompile Include="WindowsShell.cpp" />
|
<ClCompile Include="WindowsShell.cpp" />
|
||||||
|
<ClCompile Include="WinToast\wintoastlib.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="ApplicationData.h" />
|
<ClInclude Include="ApplicationData.h" />
|
||||||
@@ -179,8 +181,10 @@
|
|||||||
<ClInclude Include="PolicyConfig.h" />
|
<ClInclude Include="PolicyConfig.h" />
|
||||||
<ClInclude Include="resource.h" />
|
<ClInclude Include="resource.h" />
|
||||||
<ClInclude Include="Settings.h" />
|
<ClInclude Include="Settings.h" />
|
||||||
|
<ClInclude Include="Timer.h" />
|
||||||
<ClInclude Include="Util.h" />
|
<ClInclude Include="Util.h" />
|
||||||
<ClInclude Include="WindowsShell.h" />
|
<ClInclude Include="WindowsShell.h" />
|
||||||
|
<ClInclude Include="WinToast\wintoastlib.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\ImguiBase\ImguiBase.vcxproj">
|
<ProjectReference Include="..\ImguiBase\ImguiBase.vcxproj">
|
||||||
@@ -215,12 +219,17 @@
|
|||||||
<ResourceCompile Include="AsuroTool.rc" />
|
<ResourceCompile Include="AsuroTool.rc" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Image Include="d:\nextcloud\dragon collection\profile pics\icon2.ico" />
|
<CopyFileToFolders Include="kaiju.ico">
|
||||||
<Image Include="d:\nextcloud\dragon collection\profile pics\icon3.ico" />
|
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</DeploymentContent>
|
||||||
<Image Include="D:\Nextcloud\Dragon Collection\Profile Pics\kaiju.ico" />
|
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</DeploymentContent>
|
||||||
<Image Include="kaiju.ico" />
|
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</DeploymentContent>
|
||||||
<Image Include="kaiju32.png" />
|
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</DeploymentContent>
|
||||||
<Image Include="kaiju64.png" />
|
</CopyFileToFolders>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<CopyFileToFolders Include="basestation\lighthouse-v2-manager.exe">
|
||||||
|
<FileType>Document</FileType>
|
||||||
|
</CopyFileToFolders>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
|||||||
@@ -19,6 +19,12 @@
|
|||||||
<Filter Include="Header Files\Audio">
|
<Filter Include="Header Files\Audio">
|
||||||
<UniqueIdentifier>{b65d213d-ddf6-4816-90d1-bf0811a51abf}</UniqueIdentifier>
|
<UniqueIdentifier>{b65d213d-ddf6-4816-90d1-bf0811a51abf}</UniqueIdentifier>
|
||||||
</Filter>
|
</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>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="AsuroTool.cpp">
|
<ClCompile Include="AsuroTool.cpp">
|
||||||
@@ -42,6 +48,12 @@
|
|||||||
<ClCompile Include="WindowsShell.cpp">
|
<ClCompile Include="WindowsShell.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="WinToast\wintoastlib.cpp">
|
||||||
|
<Filter>Source Files\lib</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="Timer.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="AsuroTool.h">
|
<ClInclude Include="AsuroTool.h">
|
||||||
@@ -71,6 +83,12 @@
|
|||||||
<ClInclude Include="WindowsShell.h">
|
<ClInclude Include="WindowsShell.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="WinToast\wintoastlib.h">
|
||||||
|
<Filter>Header Files\lib</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="Timer.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<CopyFileToFolders Include="Montserrat-Regular.ttf">
|
<CopyFileToFolders Include="Montserrat-Regular.ttf">
|
||||||
@@ -79,30 +97,14 @@
|
|||||||
<CopyFileToFolders Include="remixicon.ttf">
|
<CopyFileToFolders Include="remixicon.ttf">
|
||||||
<Filter>Resource Files</Filter>
|
<Filter>Resource Files</Filter>
|
||||||
</CopyFileToFolders>
|
</CopyFileToFolders>
|
||||||
|
<CopyFileToFolders Include="basestation\lighthouse-v2-manager.exe" />
|
||||||
|
<CopyFileToFolders Include="kaiju.ico">
|
||||||
|
<Filter>Resource Files</Filter>
|
||||||
|
</CopyFileToFolders>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="AsuroTool.rc">
|
<ResourceCompile Include="AsuroTool.rc">
|
||||||
<Filter>Resource Files</Filter>
|
<Filter>Resource Files</Filter>
|
||||||
</ResourceCompile>
|
</ResourceCompile>
|
||||||
</ItemGroup>
|
</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>
|
</Project>
|
||||||
@@ -16,11 +16,7 @@
|
|||||||
|
|
||||||
void initAudio(ApplicationData& appData)
|
void initAudio(ApplicationData& appData)
|
||||||
{
|
{
|
||||||
HRESULT audioResult;
|
HRESULT audioResult = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&appData.audioData->deviceEnumerator));
|
||||||
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: ");
|
isError(audioResult, "Failed to set up audio device enumerator: ");
|
||||||
|
|
||||||
appData.audioData->audioNotificationListener = new AudioNotificationListener(appData.audioData);
|
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 } });
|
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)
|
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());
|
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)
|
void applySettings(DrawData& drawData, ApplicationData& appData)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#define APPLICATION_SETTINGS_GROUP "ApplicationSettings"
|
#define APPLICATION_SETTINGS_GROUP "ApplicationSettings"
|
||||||
#define MAX_TASK_NAME_LENGTH 1024
|
#define MAX_TASK_NAME_LENGTH 1024
|
||||||
|
#define MAX_MAC_ADDRESS_LENGTH 128
|
||||||
|
|
||||||
void initSettings(DrawData& drawData, ApplicationData& appData);
|
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 "ApplicationData.h"
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
#include <commctrl.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;
|
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
|
// Set window minimize behavior
|
||||||
glfwSetWindowIconifyCallback(drawData.window, [](GLFWwindow* window, int isIconified) {
|
glfwSetWindowIconifyCallback(drawData.window, [](GLFWwindow* window, int isIconified) {
|
||||||
if (isIconified && gAppData->settings.docked)
|
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)
|
void cleanupShell(DrawData& drawData)
|
||||||
{
|
{
|
||||||
// Remove tray icon
|
// Remove tray icon
|
||||||
@@ -100,3 +124,21 @@ LRESULT CALLBACK trayIconEventHandler(int code, WPARAM wParam, LPARAM lParam)
|
|||||||
|
|
||||||
return CallNextHookEx(NULL, code, wParam, 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
|
#pragma once
|
||||||
#include <ImguiBase.h>
|
#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 initShell(DrawData& drawData);
|
||||||
|
|
||||||
|
void showToastNotification(TimerToastHandler* handler, const wchar_t* title, const wchar_t* text);
|
||||||
|
|
||||||
void cleanupShell(DrawData& drawData);
|
void cleanupShell(DrawData& drawData);
|
||||||
|
|
||||||
LRESULT trayIconEventHandler(int code, WPARAM wParam, LPARAM lParam);
|
LRESULT trayIconEventHandler(int code, WPARAM wParam, LPARAM lParam);
|
||||||
|
|||||||
Reference in New Issue
Block a user