blob: 0b3473c15cd85bb7f46923a9ef8297d2066aa629 [file] [log] [blame]
Austin Schuh812d0d12021-11-04 20:16:48 -07001// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
4
5#include <memory>
6
7#include <GLFW/glfw3.h>
8#include <fmt/format.h>
9#include <imgui.h>
10#include <ntcore_cpp.h>
James Kuszmaulb13e13f2023-11-22 20:44:04 -080011#include <wpi/StringExtras.h>
Austin Schuh812d0d12021-11-04 20:16:48 -070012#include <wpigui.h>
James Kuszmaulb13e13f2023-11-22 20:44:04 -080013#include <wpigui_openurl.h>
Austin Schuh812d0d12021-11-04 20:16:48 -070014
15#include "glass/Context.h"
Austin Schuh75263e32022-02-22 18:05:32 -080016#include "glass/MainMenuBar.h"
Austin Schuh812d0d12021-11-04 20:16:48 -070017#include "glass/Model.h"
Austin Schuh75263e32022-02-22 18:05:32 -080018#include "glass/Storage.h"
Austin Schuh812d0d12021-11-04 20:16:48 -070019#include "glass/View.h"
20#include "glass/networktables/NetworkTables.h"
21#include "glass/networktables/NetworkTablesProvider.h"
22#include "glass/networktables/NetworkTablesSettings.h"
23#include "glass/other/Log.h"
24#include "glass/other/Plot.h"
25
26namespace gui = wpi::gui;
27
28const char* GetWPILibVersion();
29
30namespace glass {
31std::string_view GetResource_glass_16_png();
32std::string_view GetResource_glass_32_png();
33std::string_view GetResource_glass_48_png();
34std::string_view GetResource_glass_64_png();
35std::string_view GetResource_glass_128_png();
36std::string_view GetResource_glass_256_png();
37std::string_view GetResource_glass_512_png();
38} // namespace glass
39
40static std::unique_ptr<glass::PlotProvider> gPlotProvider;
41static std::unique_ptr<glass::NetworkTablesProvider> gNtProvider;
42
43static std::unique_ptr<glass::NetworkTablesModel> gNetworkTablesModel;
44static std::unique_ptr<glass::NetworkTablesSettings> gNetworkTablesSettings;
45static glass::LogData gNetworkTablesLog;
Austin Schuh75263e32022-02-22 18:05:32 -080046static std::unique_ptr<glass::Window> gNetworkTablesWindow;
James Kuszmaulcf324122023-01-14 14:07:17 -080047static std::unique_ptr<glass::Window> gNetworkTablesInfoWindow;
Austin Schuh75263e32022-02-22 18:05:32 -080048static std::unique_ptr<glass::Window> gNetworkTablesSettingsWindow;
49static std::unique_ptr<glass::Window> gNetworkTablesLogWindow;
50
51static glass::MainMenuBar gMainMenu;
52static bool gAbout = false;
53static bool gSetEnterKey = false;
54static bool gKeyEdit = false;
James Kuszmaulcf324122023-01-14 14:07:17 -080055static int* gEnterKey;
56static void (*gPrevKeyCallback)(GLFWwindow*, int, int, int, int);
James Kuszmaulb13e13f2023-11-22 20:44:04 -080057static bool gNetworkTablesDebugLog = false;
58static unsigned int gPrevMode = NT_NET_MODE_NONE;
James Kuszmaulcf324122023-01-14 14:07:17 -080059
60static void RemapEnterKeyCallback(GLFWwindow* window, int key, int scancode,
61 int action, int mods) {
62 if (action == GLFW_PRESS || action == GLFW_RELEASE) {
63 if (gKeyEdit) {
64 *gEnterKey = key;
65 gKeyEdit = false;
66 } else if (*gEnterKey == key) {
67 key = GLFW_KEY_ENTER;
68 }
69 }
70
71 if (gPrevKeyCallback) {
72 gPrevKeyCallback(window, key, scancode, action, mods);
73 }
74}
Austin Schuh812d0d12021-11-04 20:16:48 -070075
James Kuszmaulb13e13f2023-11-22 20:44:04 -080076/**
77 * Generates the proper title bar title based on current instance state and
78 * event.
79 */
80static std::string MakeTitle(NT_Inst inst, nt::Event event) {
81 auto mode = nt::GetNetworkMode(inst);
82 if (mode & NT_NET_MODE_SERVER) {
83 auto numClients = nt::GetConnections(inst).size();
84 return fmt::format("Glass - {} Client{} Connected", numClients,
85 (numClients == 1 ? "" : "s"));
86 } else if (mode & NT_NET_MODE_CLIENT3 || mode & NT_NET_MODE_CLIENT4) {
87 if (event.Is(NT_EVENT_CONNECTED)) {
88 return fmt::format("Glass - Connected ({})",
89 event.GetConnectionInfo()->remote_ip);
90 }
91 }
92 return "Glass - DISCONNECTED";
93}
94
Austin Schuh812d0d12021-11-04 20:16:48 -070095static void NtInitialize() {
Austin Schuh812d0d12021-11-04 20:16:48 -070096 auto inst = nt::GetDefaultInstance();
James Kuszmaulcf324122023-01-14 14:07:17 -080097 auto poller = nt::CreateListenerPoller(inst);
James Kuszmaulb13e13f2023-11-22 20:44:04 -080098 nt::AddPolledListener(poller, inst, NT_EVENT_CONNECTION | NT_EVENT_IMMEDIATE);
99 nt::AddPolledLogger(poller, 0, 100);
100 gui::AddEarlyExecute([inst, poller] {
Austin Schuh812d0d12021-11-04 20:16:48 -0700101 auto win = gui::GetSystemWindow();
102 if (!win) {
103 return;
104 }
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800105 bool updateTitle = false;
106 nt::Event connectionEvent;
107 if (nt::GetNetworkMode(inst) != gPrevMode) {
108 gPrevMode = nt::GetNetworkMode(inst);
109 updateTitle = true;
110 }
111
James Kuszmaulcf324122023-01-14 14:07:17 -0800112 for (auto&& event : nt::ReadListenerQueue(poller)) {
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800113 if (event.Is(NT_EVENT_CONNECTION)) {
114 updateTitle = true;
115 connectionEvent = event;
James Kuszmaulcf324122023-01-14 14:07:17 -0800116 } else if (auto msg = event.GetLogMessage()) {
117 const char* level = "";
118 if (msg->level >= NT_LOG_CRITICAL) {
119 level = "CRITICAL: ";
120 } else if (msg->level >= NT_LOG_ERROR) {
121 level = "ERROR: ";
122 } else if (msg->level >= NT_LOG_WARNING) {
123 level = "WARNING: ";
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800124 } else if (msg->level < NT_LOG_INFO && !gNetworkTablesDebugLog) {
125 continue;
James Kuszmaulcf324122023-01-14 14:07:17 -0800126 }
127 gNetworkTablesLog.Append(fmt::format(
128 "{}{} ({}:{})\n", level, msg->message, msg->filename, msg->line));
Austin Schuh812d0d12021-11-04 20:16:48 -0700129 }
130 }
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800131
132 if (updateTitle) {
133 glfwSetWindowTitle(win, MakeTitle(inst, connectionEvent).c_str());
134 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700135 });
136
Austin Schuh75263e32022-02-22 18:05:32 -0800137 gNetworkTablesLogWindow = std::make_unique<glass::Window>(
138 glass::GetStorageRoot().GetChild("NetworkTables Log"),
139 "NetworkTables Log", glass::Window::kHide);
140 gNetworkTablesLogWindow->SetView(
Austin Schuh812d0d12021-11-04 20:16:48 -0700141 std::make_unique<glass::LogView>(&gNetworkTablesLog));
Austin Schuh75263e32022-02-22 18:05:32 -0800142 gNetworkTablesLogWindow->SetDefaultPos(250, 615);
143 gNetworkTablesLogWindow->SetDefaultSize(600, 130);
144 gNetworkTablesLogWindow->DisableRenamePopup();
145 gui::AddLateExecute([] { gNetworkTablesLogWindow->Display(); });
Austin Schuh812d0d12021-11-04 20:16:48 -0700146
147 // NetworkTables table window
148 gNetworkTablesModel = std::make_unique<glass::NetworkTablesModel>();
149 gui::AddEarlyExecute([] { gNetworkTablesModel->Update(); });
150
Austin Schuh75263e32022-02-22 18:05:32 -0800151 gNetworkTablesWindow = std::make_unique<glass::Window>(
152 glass::GetStorageRoot().GetChild("NetworkTables View"), "NetworkTables");
153 gNetworkTablesWindow->SetView(
Austin Schuh812d0d12021-11-04 20:16:48 -0700154 std::make_unique<glass::NetworkTablesView>(gNetworkTablesModel.get()));
Austin Schuh75263e32022-02-22 18:05:32 -0800155 gNetworkTablesWindow->SetDefaultPos(250, 277);
156 gNetworkTablesWindow->SetDefaultSize(750, 185);
157 gNetworkTablesWindow->DisableRenamePopup();
158 gui::AddLateExecute([] { gNetworkTablesWindow->Display(); });
Austin Schuh812d0d12021-11-04 20:16:48 -0700159
James Kuszmaulcf324122023-01-14 14:07:17 -0800160 // NetworkTables info window
161 gNetworkTablesInfoWindow = std::make_unique<glass::Window>(
162 glass::GetStorageRoot().GetChild("NetworkTables Info"),
163 "NetworkTables Info");
164 gNetworkTablesInfoWindow->SetView(glass::MakeFunctionView(
165 [&] { glass::DisplayNetworkTablesInfo(gNetworkTablesModel.get()); }));
166 gNetworkTablesInfoWindow->SetDefaultPos(250, 130);
167 gNetworkTablesInfoWindow->SetDefaultSize(750, 145);
168 gNetworkTablesInfoWindow->SetDefaultVisibility(glass::Window::kHide);
169 gNetworkTablesInfoWindow->DisableRenamePopup();
170 gui::AddLateExecute([] { gNetworkTablesInfoWindow->Display(); });
171
Austin Schuh812d0d12021-11-04 20:16:48 -0700172 // NetworkTables settings window
Austin Schuh75263e32022-02-22 18:05:32 -0800173 gNetworkTablesSettings = std::make_unique<glass::NetworkTablesSettings>(
James Kuszmaulcf324122023-01-14 14:07:17 -0800174 "glass", glass::GetStorageRoot().GetChild("NetworkTables Settings"));
Austin Schuh812d0d12021-11-04 20:16:48 -0700175 gui::AddEarlyExecute([] { gNetworkTablesSettings->Update(); });
176
Austin Schuh75263e32022-02-22 18:05:32 -0800177 gNetworkTablesSettingsWindow = std::make_unique<glass::Window>(
178 glass::GetStorageRoot().GetChild("NetworkTables Settings"),
179 "NetworkTables Settings");
180 gNetworkTablesSettingsWindow->SetView(
181 glass::MakeFunctionView([] { gNetworkTablesSettings->Display(); }));
182 gNetworkTablesSettingsWindow->SetDefaultPos(30, 30);
183 gNetworkTablesSettingsWindow->SetFlags(ImGuiWindowFlags_AlwaysAutoResize);
184 gNetworkTablesSettingsWindow->DisableRenamePopup();
185 gui::AddLateExecute([] { gNetworkTablesSettingsWindow->Display(); });
186
187 gui::AddWindowScaler([](float scale) {
188 // scale default window positions
189 gNetworkTablesLogWindow->ScaleDefault(scale);
190 gNetworkTablesWindow->ScaleDefault(scale);
191 gNetworkTablesSettingsWindow->ScaleDefault(scale);
192 });
Austin Schuh812d0d12021-11-04 20:16:48 -0700193}
194
195#ifdef _WIN32
196int __stdcall WinMain(void* hInstance, void* hPrevInstance, char* pCmdLine,
197 int nCmdShow) {
Austin Schuh75263e32022-02-22 18:05:32 -0800198 int argc = __argc;
199 char** argv = __argv;
Austin Schuh812d0d12021-11-04 20:16:48 -0700200#else
Austin Schuh75263e32022-02-22 18:05:32 -0800201int main(int argc, char** argv) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700202#endif
Austin Schuh75263e32022-02-22 18:05:32 -0800203 std::string_view saveDir;
204 if (argc == 2) {
205 saveDir = argv[1];
206 }
207
Austin Schuh812d0d12021-11-04 20:16:48 -0700208 gui::CreateContext();
209 glass::CreateContext();
210
211 gui::AddIcon(glass::GetResource_glass_16_png());
212 gui::AddIcon(glass::GetResource_glass_32_png());
213 gui::AddIcon(glass::GetResource_glass_48_png());
214 gui::AddIcon(glass::GetResource_glass_64_png());
215 gui::AddIcon(glass::GetResource_glass_128_png());
216 gui::AddIcon(glass::GetResource_glass_256_png());
217 gui::AddIcon(glass::GetResource_glass_512_png());
218
James Kuszmaulcf324122023-01-14 14:07:17 -0800219 gui::AddEarlyExecute(
220 [] { ImGui::DockSpaceOverViewport(ImGui::GetMainViewport()); });
221
222 gui::AddInit([] { ImGui::GetIO().ConfigDockingWithShift = true; });
223
Austin Schuh75263e32022-02-22 18:05:32 -0800224 gPlotProvider = std::make_unique<glass::PlotProvider>(
225 glass::GetStorageRoot().GetChild("Plots"));
226 gNtProvider = std::make_unique<glass::NetworkTablesProvider>(
227 glass::GetStorageRoot().GetChild("NetworkTables"));
Austin Schuh812d0d12021-11-04 20:16:48 -0700228
Austin Schuh75263e32022-02-22 18:05:32 -0800229 glass::SetStorageName("glass");
230 glass::SetStorageDir(saveDir.empty() ? gui::GetPlatformSaveFileDir()
231 : saveDir);
Austin Schuh812d0d12021-11-04 20:16:48 -0700232 gPlotProvider->GlobalInit();
233 gui::AddInit([] { glass::ResetTime(); });
234 gNtProvider->GlobalInit();
Austin Schuh75263e32022-02-22 18:05:32 -0800235 NtInitialize();
Austin Schuh812d0d12021-11-04 20:16:48 -0700236
237 glass::AddStandardNetworkTablesViews(*gNtProvider);
238
Austin Schuh75263e32022-02-22 18:05:32 -0800239 gui::AddLateExecute([] { gMainMenu.Display(); });
240
241 gMainMenu.AddMainMenu([] {
Austin Schuh812d0d12021-11-04 20:16:48 -0700242 if (ImGui::BeginMenu("View")) {
Austin Schuh75263e32022-02-22 18:05:32 -0800243 if (ImGui::MenuItem("Set Enter Key")) {
244 gSetEnterKey = true;
245 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700246 if (ImGui::MenuItem("Reset Time")) {
247 glass::ResetTime();
248 }
249 ImGui::EndMenu();
250 }
251 if (ImGui::BeginMenu("NetworkTables")) {
252 if (gNetworkTablesSettingsWindow) {
253 gNetworkTablesSettingsWindow->DisplayMenuItem("NetworkTables Settings");
254 }
255 if (gNetworkTablesWindow) {
256 gNetworkTablesWindow->DisplayMenuItem("NetworkTables View");
257 }
James Kuszmaulcf324122023-01-14 14:07:17 -0800258 if (gNetworkTablesInfoWindow) {
259 gNetworkTablesInfoWindow->DisplayMenuItem("NetworkTables Info");
260 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700261 if (gNetworkTablesLogWindow) {
262 gNetworkTablesLogWindow->DisplayMenuItem("NetworkTables Log");
263 }
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800264 ImGui::MenuItem("NetworkTables Debug Logging", nullptr,
265 &gNetworkTablesDebugLog);
Austin Schuh812d0d12021-11-04 20:16:48 -0700266 ImGui::Separator();
267 gNtProvider->DisplayMenu();
268 ImGui::EndMenu();
269 }
270 if (ImGui::BeginMenu("Plot")) {
271 bool paused = gPlotProvider->IsPaused();
272 if (ImGui::MenuItem("Pause All Plots", nullptr, &paused)) {
273 gPlotProvider->SetPaused(paused);
274 }
275 ImGui::Separator();
276 gPlotProvider->DisplayMenu();
277 ImGui::EndMenu();
278 }
279
Austin Schuh812d0d12021-11-04 20:16:48 -0700280 if (ImGui::BeginMenu("Info")) {
281 if (ImGui::MenuItem("About")) {
Austin Schuh75263e32022-02-22 18:05:32 -0800282 gAbout = true;
Austin Schuh812d0d12021-11-04 20:16:48 -0700283 }
284 ImGui::EndMenu();
285 }
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800286
287 if (ImGui::BeginMenu("Docs")) {
288 if (ImGui::MenuItem("Online documentation")) {
289 wpi::gui::OpenURL(
290 "https://docs.wpilib.org/en/stable/docs/software/dashboards/"
291 "glass/");
292 }
293 ImGui::EndMenu();
294 }
Austin Schuh75263e32022-02-22 18:05:32 -0800295 });
Austin Schuh812d0d12021-11-04 20:16:48 -0700296
Austin Schuh75263e32022-02-22 18:05:32 -0800297 gui::AddLateExecute([] {
298 if (gAbout) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700299 ImGui::OpenPopup("About");
Austin Schuh75263e32022-02-22 18:05:32 -0800300 gAbout = false;
Austin Schuh812d0d12021-11-04 20:16:48 -0700301 }
302 if (ImGui::BeginPopupModal("About")) {
303 ImGui::Text("Glass: A different kind of dashboard");
304 ImGui::Separator();
305 ImGui::Text("v%s", GetWPILibVersion());
Austin Schuh75263e32022-02-22 18:05:32 -0800306 ImGui::Separator();
307 ImGui::Text("Save location: %s", glass::GetStorageDir().c_str());
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800308 ImGui::Text("%.3f ms/frame (%.1f FPS)",
309 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
Austin Schuh812d0d12021-11-04 20:16:48 -0700310 if (ImGui::Button("Close")) {
311 ImGui::CloseCurrentPopup();
312 }
313 ImGui::EndPopup();
314 }
Austin Schuh75263e32022-02-22 18:05:32 -0800315
Austin Schuh75263e32022-02-22 18:05:32 -0800316 if (gSetEnterKey) {
317 ImGui::OpenPopup("Set Enter Key");
318 gSetEnterKey = false;
319 }
320 if (ImGui::BeginPopupModal("Set Enter Key")) {
321 ImGui::Text("Set the key to use to mean 'Enter'");
322 ImGui::Text("This is useful to edit values without the DS disabling");
323 ImGui::Separator();
324
Austin Schuh75263e32022-02-22 18:05:32 -0800325 ImGui::Text("Key:");
326 ImGui::SameLine();
327 char editLabel[40];
328 char nameBuf[32];
James Kuszmaulcf324122023-01-14 14:07:17 -0800329 const char* name = glfwGetKeyName(*gEnterKey, 0);
Austin Schuh75263e32022-02-22 18:05:32 -0800330 if (!name) {
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800331 wpi::format_to_n_c_str(nameBuf, sizeof(nameBuf), "{}", *gEnterKey);
332
Austin Schuh75263e32022-02-22 18:05:32 -0800333 name = nameBuf;
334 }
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800335 wpi::format_to_n_c_str(editLabel, sizeof(editLabel), "{}###edit",
336 gKeyEdit ? "(press key)" : name);
337
Austin Schuh75263e32022-02-22 18:05:32 -0800338 if (ImGui::SmallButton(editLabel)) {
339 gKeyEdit = true;
340 }
341 ImGui::SameLine();
342 if (ImGui::SmallButton("Reset")) {
James Kuszmaulcf324122023-01-14 14:07:17 -0800343 *gEnterKey = GLFW_KEY_ENTER;
Austin Schuh75263e32022-02-22 18:05:32 -0800344 }
345
346 if (ImGui::Button("Close")) {
347 ImGui::CloseCurrentPopup();
348 gKeyEdit = false;
349 }
350 ImGui::EndPopup();
351 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700352 });
353
James Kuszmaulcf324122023-01-14 14:07:17 -0800354 gui::Initialize("Glass - DISCONNECTED", 1024, 768,
355 ImGuiConfigFlags_DockingEnable);
356 gEnterKey = &glass::GetStorageRoot().GetInt("enterKey", GLFW_KEY_ENTER);
357 if (auto win = gui::GetSystemWindow()) {
358 gPrevKeyCallback = glfwSetKeyCallback(win, RemapEnterKeyCallback);
359 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700360 gui::Main();
361
Austin Schuh75263e32022-02-22 18:05:32 -0800362 gNetworkTablesSettingsWindow.reset();
363 gNetworkTablesLogWindow.reset();
364 gNetworkTablesWindow.reset();
Austin Schuh812d0d12021-11-04 20:16:48 -0700365 gNetworkTablesModel.reset();
Austin Schuh812d0d12021-11-04 20:16:48 -0700366 gNtProvider.reset();
367 gPlotProvider.reset();
368
369 glass::DestroyContext();
370 gui::DestroyContext();
Austin Schuh75263e32022-02-22 18:05:32 -0800371
372 return 0;
Austin Schuh812d0d12021-11-04 20:16:48 -0700373}