|
|
|
|
@@ -94,6 +94,9 @@ void init(DrawData& drawData, ApplicationData& appData)
|
|
|
|
|
iconFontPath.append("\\remixicon.ttf");
|
|
|
|
|
io.Fonts->AddFontFromFileTTF(iconFontPath.c_str(), 14.0f, &icons_config, icons_ranges);
|
|
|
|
|
|
|
|
|
|
// style
|
|
|
|
|
loadUiStyle();
|
|
|
|
|
|
|
|
|
|
// Start timer thread
|
|
|
|
|
std::thread timerThread(updateTimer, std::ref(appData));
|
|
|
|
|
timerThread.detach();
|
|
|
|
|
@@ -108,6 +111,8 @@ void init(DrawData& drawData, ApplicationData& appData)
|
|
|
|
|
|
|
|
|
|
void draw(DrawData& drawData, ApplicationData& appData)
|
|
|
|
|
{
|
|
|
|
|
const float panelGap = 0.f;
|
|
|
|
|
|
|
|
|
|
justDocked = false;
|
|
|
|
|
appData.hoverTargetType = HoverTargetType::HOVER_TARGET_NONE;
|
|
|
|
|
|
|
|
|
|
@@ -127,7 +132,7 @@ 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.;
|
|
|
|
|
customYCursor += panelGap;
|
|
|
|
|
|
|
|
|
|
// Timer
|
|
|
|
|
ImGui::SetNextWindowPos(ImVec2(0, customYCursor));
|
|
|
|
|
@@ -138,13 +143,13 @@ void draw(DrawData& drawData, ApplicationData& appData)
|
|
|
|
|
ImGui::SetNextWindowPos(ImVec2(viewportSize.x / 2.f, customYCursor));
|
|
|
|
|
ImGui::SetNextWindowSize(ImVec2(viewportSize.x / 2.f, 0));
|
|
|
|
|
customYCursor += baseStationWindow(appData).y;
|
|
|
|
|
customYCursor += 5.;
|
|
|
|
|
customYCursor += panelGap;
|
|
|
|
|
|
|
|
|
|
// 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.;
|
|
|
|
|
customYCursor += panelGap;
|
|
|
|
|
|
|
|
|
|
// Recording devices
|
|
|
|
|
ImGui::SetNextWindowPos(ImVec2(0, customYCursor));
|
|
|
|
|
@@ -179,6 +184,12 @@ void cleanup(DrawData& drawData, ApplicationData& appData)
|
|
|
|
|
cleanupShell(drawData);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float getButtonWidth(const char* label)
|
|
|
|
|
{
|
|
|
|
|
ImVec2 labelSize = ImGui::CalcTextSize(label);
|
|
|
|
|
return labelSize.x + ImGui::GetStyle().FramePadding.x * 2.f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ImVec2 menuBar(DrawData& drawData, ApplicationData& appData)
|
|
|
|
|
{
|
|
|
|
|
ImVec2 size{};
|
|
|
|
|
@@ -219,7 +230,7 @@ ImVec2 menuBar(DrawData& drawData, ApplicationData& appData)
|
|
|
|
|
{
|
|
|
|
|
ImVec2 availableSpace = ImGui::GetContentRegionAvail();
|
|
|
|
|
ImVec2 cursorPos = ImGui::GetCursorPos();
|
|
|
|
|
ImGui::SetCursorPosX(cursorPos.x + availableSpace.x - 47.);
|
|
|
|
|
ImGui::SetCursorPosX(cursorPos.x + availableSpace.x - getButtonWidth(ICON_SUBTRACT_FILL) * 2.f);
|
|
|
|
|
|
|
|
|
|
if (ImGui::SmallButton(ICON_SUBTRACT_FILL))
|
|
|
|
|
{
|
|
|
|
|
@@ -270,7 +281,7 @@ bool windowHeaderButton(const char* id)
|
|
|
|
|
ImVec2 windowMax = ImGui::GetWindowContentRegionMax();
|
|
|
|
|
ImGui::PopClipRect();
|
|
|
|
|
|
|
|
|
|
ImGui::SetCursorPos({ windowMax.x - 20.f, tempPos.y - 28.f });
|
|
|
|
|
ImGui::SetCursorPos({ windowMax.x - getButtonWidth(ICON_CLOSE_FILL), tempPos.y - 30.f});
|
|
|
|
|
bool result = ImGui::SmallButton(id);
|
|
|
|
|
|
|
|
|
|
ImGui::PushClipRect(tempRect.Min, tempRect.Max, false);
|
|
|
|
|
@@ -441,11 +452,11 @@ ImVec2 timerWindow(DrawData& drawData, ApplicationData& appData)
|
|
|
|
|
{
|
|
|
|
|
if (timerData.timerHasNotified)
|
|
|
|
|
{
|
|
|
|
|
formatTime(timerData.timerStartTimestamp + std::chrono::seconds((int)appData.settings.timerDuration), timeStr, sizeof(timeStr));
|
|
|
|
|
timerText = std::format("Ended at {}", timeStr);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
formatTime(timerData.timerStartTimestamp + std::chrono::seconds((int)appData.settings.timerDuration), timeStr, sizeof(timeStr));
|
|
|
|
|
timerText = std::format("Ended at {}", timeStr);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
formatTime(appData.timerData.timerStartTimestamp + std::chrono::seconds((int)appData.settings.timerDuration), timeStr, sizeof(timeStr));
|
|
|
|
|
timerText = std::format("Ends at {}", timeStr);
|
|
|
|
|
}
|
|
|
|
|
@@ -531,12 +542,18 @@ 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 = false;
|
|
|
|
|
std::string hoverText = "";
|
|
|
|
|
|
|
|
|
|
if (taskDates.size() > 0)
|
|
|
|
|
{
|
|
|
|
|
time_t mostRecentDoneDate = *std::max_element(taskDates.begin(), taskDates.end());
|
|
|
|
|
double timeDiffSeconds = difftime(today, getDayStartOf(mostRecentDoneDate));
|
|
|
|
|
int timeDiffDays = timeDiffSeconds / (60 * 60 * 24);
|
|
|
|
|
highlightButton = timeDiffDays > appData.checklistHighlightDurationDays;
|
|
|
|
|
hoverText = std::format("{}", timeDiffDays);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 });
|
|
|
|
|
@@ -545,7 +562,8 @@ ImVec2 checklistWindow(ApplicationData& appData, const char* title)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ImVec2 beforeCheckbox = ImGui::GetCursorScreenPos();
|
|
|
|
|
if (ImGui::Checkbox(taskName.c_str(), &taskDone))
|
|
|
|
|
std::string checkboxText = std::format("{}##taskcheck{}", taskName.c_str(), taskName.c_str());
|
|
|
|
|
if (ImGui::Checkbox(checkboxText.c_str(), &taskDone))
|
|
|
|
|
{
|
|
|
|
|
if (taskDone)
|
|
|
|
|
{
|
|
|
|
|
@@ -563,11 +581,10 @@ ImVec2 checklistWindow(ApplicationData& appData, const char* title)
|
|
|
|
|
ImGui::PopStyleColor(3);
|
|
|
|
|
|
|
|
|
|
ImVec2 afterCheckbox = ImGui::GetCursorScreenPos();
|
|
|
|
|
std::string dayText = std::format("{}", timeDiffDays);
|
|
|
|
|
float textWidth = ImGui::CalcTextSize(dayText.c_str()).x;
|
|
|
|
|
float textWidth = ImGui::CalcTextSize(hoverText.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::Text(hoverText.c_str());
|
|
|
|
|
ImGui::PopStyleColor();
|
|
|
|
|
ImGui::SetCursorScreenPos(afterCheckbox);
|
|
|
|
|
}
|
|
|
|
|
@@ -575,9 +592,37 @@ ImVec2 checklistWindow(ApplicationData& appData, const char* title)
|
|
|
|
|
|
|
|
|
|
if (ImGui::BeginPopup(SETTINGS_POPUP_NAME))
|
|
|
|
|
{
|
|
|
|
|
ImGui::Text("Task Highlight Duration");
|
|
|
|
|
ImGui::SameLine();
|
|
|
|
|
|
|
|
|
|
ImGui::PushItemWidth(75.f);
|
|
|
|
|
ImGui::DragInt("Task highlight duration", &appData.checklistHighlightDurationDays, .05f, 1, 7, "%d days");
|
|
|
|
|
ImGui::DragInt("##taskduration", &appData.checklistHighlightDurationDays, .05f, 1, 7, "%d days");
|
|
|
|
|
ImGui::PopItemWidth();
|
|
|
|
|
|
|
|
|
|
ImGui::Text("Tasks");
|
|
|
|
|
auto taskIterator = appData.settings.taskNames.begin();
|
|
|
|
|
while (taskIterator != appData.settings.taskNames.end())
|
|
|
|
|
{
|
|
|
|
|
size_t taskIndex = taskIterator - appData.settings.taskNames.begin();
|
|
|
|
|
std::string& taskName = *taskIterator;
|
|
|
|
|
taskName.reserve(128);
|
|
|
|
|
ImGui::InputText(std::format("##taskinput{}", taskIndex).c_str(), taskName.data(), taskName.capacity());
|
|
|
|
|
ImGui::SameLine();
|
|
|
|
|
if (ImGui::SmallButton(std::format("{}##deltask{}", ICON_CLOSE_FILL, taskIndex).c_str()))
|
|
|
|
|
{
|
|
|
|
|
taskIterator = appData.settings.taskNames.erase(taskIterator);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
taskIterator++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ImGui::Button(ICON_ADD_FILL))
|
|
|
|
|
{
|
|
|
|
|
appData.settings.taskNames.push_back("");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ImGui::EndPopup();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -587,8 +632,21 @@ ImVec2 checklistWindow(ApplicationData& appData, const char* title)
|
|
|
|
|
return size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void startBaseStationProc(const char* args, DWORD flags = 0)
|
|
|
|
|
void startBaseStationProc(ApplicationData& appData, const char* args, DWORD flags = 0)
|
|
|
|
|
{
|
|
|
|
|
if (appData.lighthouseProcActive) return;
|
|
|
|
|
appData.lighthouseProcActive = true;
|
|
|
|
|
|
|
|
|
|
SECURITY_ATTRIBUTES saAttr;
|
|
|
|
|
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
|
|
|
saAttr.bInheritHandle = TRUE;
|
|
|
|
|
saAttr.lpSecurityDescriptor = NULL;
|
|
|
|
|
|
|
|
|
|
HANDLE exeOutWrite;
|
|
|
|
|
HANDLE exeOutRead;
|
|
|
|
|
CreatePipe(&exeOutRead, &exeOutWrite, &saAttr, 0);
|
|
|
|
|
SetHandleInformation(exeOutRead, HANDLE_FLAG_INHERIT, 0);
|
|
|
|
|
|
|
|
|
|
// additional information
|
|
|
|
|
STARTUPINFOA si;
|
|
|
|
|
PROCESS_INFORMATION pi;
|
|
|
|
|
@@ -596,14 +654,33 @@ void startBaseStationProc(const char* args, DWORD flags = 0)
|
|
|
|
|
// set the size of the structures
|
|
|
|
|
ZeroMemory(&si, sizeof(si));
|
|
|
|
|
si.cb = sizeof(si);
|
|
|
|
|
si.hStdOutput = exeOutWrite;
|
|
|
|
|
si.dwFlags = STARTF_USESTDHANDLES;
|
|
|
|
|
ZeroMemory(&pi, sizeof(pi));
|
|
|
|
|
|
|
|
|
|
// start the program up
|
|
|
|
|
CreateProcessA("lighthouse-v2-manager.exe", const_cast<LPSTR>(args), NULL, NULL, FALSE, flags, NULL, NULL, &si, &pi);
|
|
|
|
|
CreateProcessA("lighthouse-v2-manager.exe", const_cast<LPSTR>(args), NULL, NULL, true, flags, NULL, NULL, &si, &pi);
|
|
|
|
|
|
|
|
|
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
|
|
|
|
|
|
|
|
|
for (;;)
|
|
|
|
|
{
|
|
|
|
|
constexpr size_t BUFFER_SIZE = 4096;
|
|
|
|
|
char readBuffer[BUFFER_SIZE]{};
|
|
|
|
|
DWORD readCount = 0;
|
|
|
|
|
bool readSuccess = ReadFile(exeOutRead, readBuffer, BUFFER_SIZE, &readCount, NULL);
|
|
|
|
|
if (!readSuccess || readCount == 0) break;
|
|
|
|
|
|
|
|
|
|
std::cout << readBuffer << std::endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Close process and thread handles.
|
|
|
|
|
CloseHandle(pi.hProcess);
|
|
|
|
|
CloseHandle(pi.hThread);
|
|
|
|
|
CloseHandle(exeOutWrite);
|
|
|
|
|
CloseHandle(exeOutRead);
|
|
|
|
|
|
|
|
|
|
appData.lighthouseProcActive = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ImVec2 baseStationWindow(ApplicationData& appData)
|
|
|
|
|
@@ -623,7 +700,7 @@ ImVec2 baseStationWindow(ApplicationData& appData)
|
|
|
|
|
params.append(" ");
|
|
|
|
|
params.append(mac.c_str());
|
|
|
|
|
}
|
|
|
|
|
startBaseStationProc(params.c_str(), appData.settings.baseStationShowConsole ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW);
|
|
|
|
|
startBaseStationProc(appData, params.c_str(), appData.settings.baseStationShowConsole ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW);
|
|
|
|
|
}
|
|
|
|
|
ImGui::SameLine();
|
|
|
|
|
if (ImGui::Button("Shutdown"))
|
|
|
|
|
@@ -634,7 +711,7 @@ ImVec2 baseStationWindow(ApplicationData& appData)
|
|
|
|
|
params.append(" ");
|
|
|
|
|
params.append(mac.c_str());
|
|
|
|
|
}
|
|
|
|
|
startBaseStationProc(params.c_str(), appData.settings.baseStationShowConsole ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW);
|
|
|
|
|
startBaseStationProc(appData, params.c_str(), appData.settings.baseStationShowConsole ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -643,7 +720,7 @@ ImVec2 baseStationWindow(ApplicationData& appData)
|
|
|
|
|
ImGui::Checkbox("Show Console", &appData.settings.baseStationShowConsole);
|
|
|
|
|
if (ImGui::Button("Search"))
|
|
|
|
|
{
|
|
|
|
|
startBaseStationProc("discover");
|
|
|
|
|
startBaseStationProc(appData, "discover");
|
|
|
|
|
// TODO: parse stuff (annoying)
|
|
|
|
|
}
|
|
|
|
|
ImGui::Text(std::format("Known: {}", appData.settings.baseStationMacAdresses.size()).c_str());
|
|
|
|
|
@@ -664,11 +741,12 @@ ImVec2 audioDeviceWindow(ApplicationData& appData, std::vector<AudioDevice>& dev
|
|
|
|
|
ImGui::OpenPopup(SETTINGS_POPUP_NAME);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ImGui::BeginTable("DeviceTable", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_Borders | ImGuiTableFlags_NoSavedSettings))
|
|
|
|
|
if (ImGui::BeginTable("DeviceTable", 4, ImGuiTableFlags_Resizable | ImGuiTableFlags_Borders | ImGuiTableFlags_NoSavedSettings))
|
|
|
|
|
{
|
|
|
|
|
ImGui::TableSetupColumn("Devices", ImGuiTableColumnFlags_WidthStretch, 3.);
|
|
|
|
|
ImGui::TableSetupColumn("Volume", ImGuiTableColumnFlags_WidthStretch, 1.);
|
|
|
|
|
ImGui::TableSetupColumn("Defaults", ImGuiTableColumnFlags_WidthFixed, 55.);
|
|
|
|
|
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, getButtonWidth(ICON_MUSIC_2_FILL));
|
|
|
|
|
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, getButtonWidth(ICON_PHONE_FILL));
|
|
|
|
|
ImGui::TableHeadersRow();
|
|
|
|
|
|
|
|
|
|
for (auto& dev : deviceList)
|
|
|
|
|
@@ -756,22 +834,31 @@ ImVec2 audioDeviceWindow(ApplicationData& appData, std::vector<AudioDevice>& dev
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Defaults
|
|
|
|
|
const float circleSize = 5.f;
|
|
|
|
|
|
|
|
|
|
ImGui::TableNextColumn();
|
|
|
|
|
if (dev.state == DEVICE_STATE_ACTIVE)
|
|
|
|
|
{
|
|
|
|
|
if (dev.isDefaultConsole)
|
|
|
|
|
{
|
|
|
|
|
drawCircle(5, IM_COL32(50, 50, 222, 255));
|
|
|
|
|
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + getButtonWidth(ICON_MUSIC_2_FILL) / 2.f - circleSize);
|
|
|
|
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 4.f);
|
|
|
|
|
drawCircle(circleSize, IM_COL32(50, 50, 222, 255));
|
|
|
|
|
}
|
|
|
|
|
if (customButton("bn_d_", deviceIdUtf8.c_str(), ICON_MUSIC_2_FILL, !dev.isDefaultConsole))
|
|
|
|
|
{
|
|
|
|
|
setDefaultAudioDevice(*appData.audioData, dev.id.c_str(), ERole::eConsole);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ImGui::SameLine();
|
|
|
|
|
ImGui::TableNextColumn();
|
|
|
|
|
if (dev.state == DEVICE_STATE_ACTIVE)
|
|
|
|
|
{
|
|
|
|
|
if (dev.isDefaultCommunication)
|
|
|
|
|
{
|
|
|
|
|
drawCircle(5, IM_COL32(222, 50, 50, 255));
|
|
|
|
|
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + getButtonWidth(ICON_PHONE_FILL) / 2.f - circleSize);
|
|
|
|
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 4.f);
|
|
|
|
|
drawCircle(circleSize, IM_COL32(222, 50, 50, 255));
|
|
|
|
|
}
|
|
|
|
|
if (customButton("bn_c_", deviceIdUtf8.c_str(), ICON_PHONE_FILL, !dev.isDefaultCommunication))
|
|
|
|
|
{
|
|
|
|
|
|