blob: 2c2debc1979b481d418215f7101609c569e5405e [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>
11#include <wpigui.h>
12
13#include "glass/Context.h"
Austin Schuh75263e32022-02-22 18:05:32 -080014#include "glass/MainMenuBar.h"
Austin Schuh812d0d12021-11-04 20:16:48 -070015#include "glass/Model.h"
Austin Schuh75263e32022-02-22 18:05:32 -080016#include "glass/Storage.h"
Austin Schuh812d0d12021-11-04 20:16:48 -070017#include "glass/networktables/NetworkTables.h"
18#include "glass/networktables/NetworkTablesSettings.h"
19#include "glass/other/Log.h"
20
21namespace gui = wpi::gui;
22
23const char* GetWPILibVersion();
24
25namespace ov {
26std::string_view GetResource_ov_16_png();
27std::string_view GetResource_ov_32_png();
28std::string_view GetResource_ov_48_png();
29std::string_view GetResource_ov_64_png();
30std::string_view GetResource_ov_128_png();
31std::string_view GetResource_ov_256_png();
32std::string_view GetResource_ov_512_png();
33} // namespace ov
34
35static std::unique_ptr<glass::NetworkTablesModel> gModel;
36static std::unique_ptr<glass::NetworkTablesSettings> gSettings;
37static glass::LogData gLog;
38static glass::NetworkTablesFlagsSettings gFlagsSettings;
Austin Schuh75263e32022-02-22 18:05:32 -080039static glass::MainMenuBar gMainMenu;
Austin Schuh812d0d12021-11-04 20:16:48 -070040
41static void NtInitialize() {
42 // update window title when connection status changes
43 auto inst = nt::GetDefaultInstance();
44 auto poller = nt::CreateConnectionListenerPoller(inst);
45 nt::AddPolledConnectionListener(poller, true);
46 gui::AddEarlyExecute([inst, poller] {
47 auto win = gui::GetSystemWindow();
48 if (!win) {
49 return;
50 }
51 bool timedOut;
52 for (auto&& event : nt::PollConnectionListener(poller, 0, &timedOut)) {
53 if ((nt::GetNetworkMode(inst) & NT_NET_MODE_SERVER) != 0) {
54 // for server mode, just print number of clients connected
55 glfwSetWindowTitle(win,
56 fmt::format("OutlineViewer - {} Clients Connected",
57 nt::GetConnections(inst).size())
58 .c_str());
59 } else if (event.connected) {
60 glfwSetWindowTitle(win, fmt::format("OutlineViewer - Connected ({})",
61 event.conn.remote_ip)
62 .c_str());
63 } else {
64 glfwSetWindowTitle(win, "OutlineViewer - DISCONNECTED");
65 }
66 }
67 });
68
69 // handle NetworkTables log messages
70 auto logPoller = nt::CreateLoggerPoller(inst);
71 nt::AddPolledLogger(logPoller, NT_LOG_INFO, 100);
72 gui::AddEarlyExecute([logPoller] {
73 bool timedOut;
74 for (auto&& msg : nt::PollLogger(logPoller, 0, &timedOut)) {
75 const char* level = "";
76 if (msg.level >= NT_LOG_CRITICAL) {
77 level = "CRITICAL: ";
78 } else if (msg.level >= NT_LOG_ERROR) {
79 level = "ERROR: ";
80 } else if (msg.level >= NT_LOG_WARNING) {
81 level = "WARNING: ";
82 }
83 gLog.Append(fmt::format("{}{} ({}:{})\n", level, msg.message,
84 msg.filename, msg.line));
85 }
86 });
87
88 // NetworkTables table window
89 gModel = std::make_unique<glass::NetworkTablesModel>();
90 gui::AddEarlyExecute([] { gModel->Update(); });
91
92 // NetworkTables settings window
Austin Schuh75263e32022-02-22 18:05:32 -080093 gSettings = std::make_unique<glass::NetworkTablesSettings>(
94 glass::GetStorageRoot().GetChild("NetworkTables Settings"));
Austin Schuh812d0d12021-11-04 20:16:48 -070095 gui::AddEarlyExecute([] { gSettings->Update(); });
96}
97
98static void DisplayGui() {
99 ImGui::GetStyle().WindowRounding = 0;
100
101 // fill entire OS window with this window
102 ImGui::SetNextWindowPos(ImVec2(0, 0));
103 int width, height;
104 glfwGetWindowSize(gui::GetSystemWindow(), &width, &height);
105 ImGui::SetNextWindowSize(ImVec2(width, height));
106
107 ImGui::Begin("Entries", nullptr,
108 ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_MenuBar |
109 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
110 ImGuiWindowFlags_NoCollapse);
111
112 gFlagsSettings.Update();
113
114 // can't create popups from within menu, so use flags
115 bool settings = false;
116 bool log = false;
117 bool about = false;
118
119 // main menu
120 ImGui::BeginMenuBar();
Austin Schuh75263e32022-02-22 18:05:32 -0800121 gMainMenu.WorkspaceMenu();
Austin Schuh812d0d12021-11-04 20:16:48 -0700122 gui::EmitViewMenu();
123 if (ImGui::BeginMenu("View")) {
124 gFlagsSettings.DisplayMenu();
125 ImGui::EndMenu();
126 }
127
128 if (ImGui::BeginMenu("Options")) {
129 if (ImGui::MenuItem("Settings")) {
130 settings = true;
131 }
132 ImGui::EndMenu();
133 }
134
135 if (ImGui::BeginMenu("Info")) {
136 if (ImGui::MenuItem("Log")) {
137 log = true;
138 }
139 ImGui::Separator();
140 if (ImGui::MenuItem("About")) {
141 about = true;
142 }
143 ImGui::EndMenu();
144 }
145 ImGui::EndMenuBar();
146
147 // settings popup
148 if (settings) {
149 ImGui::OpenPopup("Settings");
150 }
151 if (ImGui::BeginPopupModal("Settings", nullptr,
152 ImGuiWindowFlags_AlwaysAutoResize)) {
153 if (gSettings->Display()) {
154 ImGui::CloseCurrentPopup();
155 }
156 ImGui::SameLine();
157 if (ImGui::Button("Cancel")) {
158 ImGui::CloseCurrentPopup();
159 }
160 ImGui::EndPopup();
161 }
162
163 // log popup
164 if (log) {
165 ImGui::OpenPopup("Log");
166 }
167 if (ImGui::BeginPopupModal("Log", nullptr,
168 ImGuiWindowFlags_AlwaysAutoResize)) {
169 if (ImGui::Button("Close")) {
170 ImGui::CloseCurrentPopup();
171 }
172 ImGui::BeginChild("Lines", ImVec2(width * 0.75f, height * 0.75f));
173 glass::DisplayLog(&gLog, true);
174 ImGui::EndChild();
175 ImGui::EndPopup();
176 }
177
178 // about popup
179 if (about) {
180 ImGui::OpenPopup("About");
181 }
182 if (ImGui::BeginPopupModal("About", nullptr,
183 ImGuiWindowFlags_AlwaysAutoResize)) {
184 ImGui::Text("OutlineViewer");
185 ImGui::Separator();
186 ImGui::Text("v%s", GetWPILibVersion());
Austin Schuh75263e32022-02-22 18:05:32 -0800187 ImGui::Separator();
188 ImGui::Text("Save location: %s", glass::GetStorageDir().c_str());
Austin Schuh812d0d12021-11-04 20:16:48 -0700189 if (ImGui::Button("Close")) {
190 ImGui::CloseCurrentPopup();
191 }
192 ImGui::EndPopup();
193 }
194
195 // display table view
196 glass::DisplayNetworkTables(gModel.get(), gFlagsSettings.GetFlags());
197
198 ImGui::End();
199}
200
201#ifdef _WIN32
202int __stdcall WinMain(void* hInstance, void* hPrevInstance, char* pCmdLine,
203 int nCmdShow) {
Austin Schuh75263e32022-02-22 18:05:32 -0800204 int argc = __argc;
205 char** argv = __argv;
Austin Schuh812d0d12021-11-04 20:16:48 -0700206#else
Austin Schuh75263e32022-02-22 18:05:32 -0800207int main(int argc, char** argv) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700208#endif
Austin Schuh75263e32022-02-22 18:05:32 -0800209 std::string_view saveDir;
210 if (argc == 2) {
211 saveDir = argv[1];
212 }
213
Austin Schuh812d0d12021-11-04 20:16:48 -0700214 gui::CreateContext();
215 glass::CreateContext();
216
217 gui::AddIcon(ov::GetResource_ov_16_png());
218 gui::AddIcon(ov::GetResource_ov_32_png());
219 gui::AddIcon(ov::GetResource_ov_48_png());
220 gui::AddIcon(ov::GetResource_ov_64_png());
221 gui::AddIcon(ov::GetResource_ov_128_png());
222 gui::AddIcon(ov::GetResource_ov_256_png());
223 gui::AddIcon(ov::GetResource_ov_512_png());
224
Austin Schuh75263e32022-02-22 18:05:32 -0800225 glass::SetStorageName("outlineviewer");
226 glass::SetStorageDir(saveDir.empty() ? gui::GetPlatformSaveFileDir()
227 : saveDir);
228
Austin Schuh812d0d12021-11-04 20:16:48 -0700229 gui::AddInit(NtInitialize);
230
231 gui::AddLateExecute(DisplayGui);
232
233 gui::Initialize("OutlineViewer - DISCONNECTED", 600, 400);
234 gui::Main();
235
236 gModel.reset();
237 gSettings.reset();
238
239 glass::DestroyContext();
240 gui::DestroyContext();
Austin Schuh75263e32022-02-22 18:05:32 -0800241
242 return 0;
Austin Schuh812d0d12021-11-04 20:16:48 -0700243}