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