blob: a61b49397cb6325dc7d9f317f1e79abf9581c4cd [file] [log] [blame]
Adedeji Adebisi684ec912021-07-22 18:07:52 +00001// Copyright 2021 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "main.hpp"
16
17#include "analyzer.hpp"
18#include "bargraph.hpp"
19#include "dbus_capture.hpp"
20#include "histogram.hpp"
21#include "menu.hpp"
22#include "sensorhelper.hpp"
23#include "views.hpp"
24
25#include <fmt/printf.h>
26#include <ncurses.h>
27#include <stdio.h>
28#include <unistd.h>
29
30#include <iomanip>
31#include <sstream>
32#include <thread>
33
34DBusTopWindow* g_current_active_view;
35SummaryView* g_summary_window;
36SensorDetailView* g_sensor_detail_view;
37DBusStatListView* g_dbus_stat_list_view;
38FooterView* g_footer_view;
39BarGraph<float>* g_bargraph = nullptr;
40Histogram<float>* g_histogram;
41std::vector<DBusTopWindow*> g_views;
42int g_highlighted_view_index = INVALID;
43sd_bus* g_bus = nullptr;
44SensorSnapshot* g_sensor_snapshot;
45DBusConnectionSnapshot* g_connection_snapshot;
46DBusTopStatistics* g_dbus_statistics; // At every update interval,
47 // dbus_top_analyzer::g_dbus_statistics's
48 // value is copied to this one for display
49void ReinitializeUI();
50int maxx, maxy, halfx, halfy;
51
52void ActivateWindowA()
53{
nitroglycerine49dcde12023-03-14 07:24:39 -070054 g_views[0]->visible_ = true;
55 g_views[0]->maximize_ = true;
56 g_views[1]->visible_ = false;
57 g_views[2]->visible_ = false;
Adedeji Adebisi684ec912021-07-22 18:07:52 +000058}
59
60void ActivateWindowB()
61{
nitroglycerine49dcde12023-03-14 07:24:39 -070062 g_views[1]->visible_ = true;
63 g_views[1]->maximize_ = true;
64 g_views[0]->visible_ = false;
65 g_views[2]->visible_ = false;
Adedeji Adebisi684ec912021-07-22 18:07:52 +000066}
67void ActivateWindowC()
68{
nitroglycerine49dcde12023-03-14 07:24:39 -070069 g_views[2]->visible_ = true;
70 g_views[2]->maximize_ = true;
71 g_views[0]->visible_ = false;
72 g_views[1]->visible_ = false;
Adedeji Adebisi684ec912021-07-22 18:07:52 +000073}
74void ActivateAllWindows()
75{
76 g_views[0]->maximize_ = false;
nitroglycerine49dcde12023-03-14 07:24:39 -070077 g_views[1]->maximize_ = false;
78 g_views[2]->maximize_ = false;
79 g_views[0]->visible_ = true;
80 g_views[1]->visible_ = true;
81 g_views[2]->visible_ = true;
Adedeji Adebisi684ec912021-07-22 18:07:52 +000082}
83
84void InitColorPairs()
85{
86 init_pair(1, COLOR_WHITE, COLOR_BLACK); // Does not work on actual machine
87 init_pair(2, COLOR_BLACK, COLOR_WHITE);
88}
89
90// Returns number of lines drawn
91int DrawTextWithWidthLimit(WINDOW* win, std::string txt, int y, int x,
nitroglycerine49dcde12023-03-14 07:24:39 -070092 int width,
93 [[maybe_unused]] const std::string& delimiters)
Adedeji Adebisi684ec912021-07-22 18:07:52 +000094{
95 int ret = 0;
96 std::string curr_word, curr_line;
97 while (txt.empty() == false)
98 {
99 ret++;
100 if (static_cast<int>(txt.size()) > width)
101 {
Sui Chen8643b5d2022-08-14 11:56:30 -0700102 mvwaddstr(win, y, x, txt.substr(0, width).c_str());
Adedeji Adebisi684ec912021-07-22 18:07:52 +0000103 txt = txt.substr(width);
104 }
105 else
106 {
Sui Chen8643b5d2022-08-14 11:56:30 -0700107 mvwaddstr(win, y, x, txt.c_str());
Adedeji Adebisi684ec912021-07-22 18:07:52 +0000108 break;
109 }
110 y++;
111 }
112 return ret;
113}
114
115void UpdateWindowSizes()
116{
117 /* calculate window sizes and locations */
118 if (getenv("FIXED_TERMINAL_SIZE"))
119 {
120 maxx = 100;
121 maxy = 30;
122 }
123 else
124 {
125 getmaxyx(stdscr, maxy, maxx);
126 halfx = maxx >> 1;
127 halfy = maxy >> 1;
128 }
129 for (DBusTopWindow* v : g_views)
130 {
131 v->OnResize(maxx, maxy);
nitroglycerine49dcde12023-03-14 07:24:39 -0700132 if (v->maximize_)
133 {
134 v->rect = {0, 0, maxx, maxy - MARGIN_BOTTOM};
Adedeji Adebisi684ec912021-07-22 18:07:52 +0000135 v->UpdateWindowSizeAndPosition();
136 }
137 }
138}
139
140std::string FloatToString(float value)
141{
142 return fmt::sprintf("%.2f", value);
143}
144
145void DBusTopRefresh()
146{
147 UpdateWindowSizes();
148 for (DBusTopWindow* v : g_views)
149 {
150 v->Render();
151 }
152 DBusTopWindow* focused_view = g_current_active_view;
153 if (focused_view)
154 {
155 focused_view->DrawBorderIfNeeded(); // focused view border: on top
156 }
157 refresh();
158}
159
160// This function is called by the Capture thread
161void DBusTopStatisticsCallback(DBusTopStatistics* stat, Histogram<float>* hist)
162{
163 if (stat == nullptr)
164 return;
165 // Makes a copy for display
166 // TODO: Add a mutex here for safety
167 stat->Assign(g_dbus_statistics);
168 hist->Assign(g_histogram);
169 float interval_secs = stat->seconds_since_last_sample_;
170 if (interval_secs == 0)
171 {
172 interval_secs = GetSummaryIntervalInMillises() / 1000.0f;
173 }
174 g_summary_window->UpdateDBusTopStatistics(stat);
175 stat->SetSortFieldsAndReset(g_dbus_stat_list_view->GetSortFields());
176 // ReinitializeUI(); // Don't do it here, only when user presses [R]
177 DBusTopRefresh();
178}
179
180void CycleHighlightedView()
181{
182 int new_index = 0;
183 if (g_highlighted_view_index == INVALID)
184 {
185 new_index = 0;
186 }
187 else
188 {
189 new_index = g_highlighted_view_index + 1;
190 }
191 while (new_index < static_cast<int>(g_views.size()) &&
192 g_views[new_index]->selectable_ == false)
193 {
194 new_index++;
195 }
196 if (new_index >= static_cast<int>(g_views.size()))
197 {
198 new_index = INVALID;
199 }
200 // Un-highlight all
201 for (DBusTopWindow* v : g_views)
202 {
203 v->focused_ = false;
204 }
205 if (new_index != INVALID)
206 {
207 g_views[new_index]->focused_ = true;
208 g_current_active_view = g_views[new_index];
209 }
210 else
211 {
212 g_current_active_view = nullptr;
213 }
214 g_highlighted_view_index = new_index;
215 DBusTopRefresh();
216}
217
218int UserInputThread()
219{
220 while (true)
221 {
222 int c = getch();
223 DBusTopWindow* curr_view = g_current_active_view;
224 // If a view is currently focused on
225 if (curr_view)
226 {
227 switch (c)
228 {
nitroglycerine49dcde12023-03-14 07:24:39 -0700229 case 0x1B: // 27 in dec, 0x1B in hex, escape key
Adedeji Adebisi684ec912021-07-22 18:07:52 +0000230 {
231 getch();
232 c = getch();
233 switch (c)
234 {
235 case 'A':
236 curr_view->OnKeyDown("up");
237 break;
238 case 'B':
239 curr_view->OnKeyDown("down");
240 break;
241 case 'C':
242 curr_view->OnKeyDown("right");
243 break;
244 case 'D':
245 curr_view->OnKeyDown("left");
246 break;
nitroglycerine49dcde12023-03-14 07:24:39 -0700247 case 0x1B:
Adedeji Adebisi684ec912021-07-22 18:07:52 +0000248 curr_view->OnKeyDown("escape");
249 break;
250 }
251 break;
252 }
253 case '\n': // 10 in dec, 0x0A in hex, line feed
254 {
255 curr_view->OnKeyDown("enter");
256 break;
257 }
258 case 'q':
259 case 'Q': // Q key
260 {
261 curr_view->OnKeyDown("escape");
262 break;
263 }
264 case 'a':
265 case 'A': // A key
266 {
267 curr_view->OnKeyDown("a");
268 break;
269 }
270 case 'd':
271 case 'D': // D key
272 {
273 curr_view->OnKeyDown("d");
274 break;
275 }
276 case 33: // Page up
277 {
278 curr_view->OnKeyDown("pageup");
279 break;
280 }
281 case 34: // Page down
282 {
283 curr_view->OnKeyDown("pagedown");
284 break;
285 }
286 case ' ': // Spacebar
287 {
288 curr_view->OnKeyDown("space");
289 break;
290 }
291 }
292 }
293 // The following keys are registered both when a view is selected and
294 // when it is not
295 switch (c)
296 {
297 case '\t': // 9 in dec, 0x09 in hex, tab
298 {
299 CycleHighlightedView();
300 break;
301 }
302 case 'r':
303 case 'R':
304 {
305 ReinitializeUI();
306 DBusTopRefresh();
307 break;
308 }
309 case 'x':
310 case 'X':
311 {
312 clear();
313 ActivateWindowA();
314 break;
315 }
316 case 'y':
317 case 'Y':
318 {
319 clear();
320 ActivateWindowB();
321 break;
322 }
323 case 'z':
324 case 'Z':
325 {
326 clear();
327 ActivateWindowC();
328 break;
329 }
330 case 'h':
331 case 'H':
332 {
333 ActivateAllWindows();
334 DBusTopRefresh();
335 }
336 default:
337 break;
338 }
339 }
340 exit(0);
341}
342
343void ReinitializeUI()
344{
345 endwin();
346 initscr();
347 use_default_colors();
348 noecho();
349 for (int i = 0; i < static_cast<int>(g_views.size()); i++)
350 {
351 g_views[i]->RecreateWindow();
352 }
353}
354
nitroglycerine49dcde12023-03-14 07:24:39 -0700355int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv)
Adedeji Adebisi684ec912021-07-22 18:07:52 +0000356{
357 int r = AcquireBus(&g_bus);
358 if (r <= 0)
359 {
360 printf("Error acquiring bus for monitoring\n");
361 exit(0);
362 }
363
364 printf("Listing all sensors for display\n");
365 // ListAllSensors creates connection snapshot and sensor snapshot
366 dbus_top_analyzer::ListAllSensors();
367 g_bargraph = new BarGraph<float>(300);
368 g_histogram = new Histogram<float>();
369
370 initscr();
371 use_default_colors();
372 start_color();
373 noecho();
374
375 clear();
376 g_dbus_statistics = new DBusTopStatistics();
377 g_summary_window = new SummaryView();
378 g_sensor_detail_view = new SensorDetailView();
379 g_dbus_stat_list_view = new DBusStatListView();
380 g_footer_view = new FooterView();
381 g_views.push_back(g_summary_window);
382 g_views.push_back(g_sensor_detail_view);
383 g_views.push_back(g_dbus_stat_list_view);
384 g_views.push_back(g_footer_view);
385
386 g_sensor_detail_view->UpdateSensorSnapshot(g_sensor_snapshot);
387 UpdateWindowSizes();
388 dbus_top_analyzer::SetDBusTopStatisticsCallback(&DBusTopStatisticsCallback);
389 std::thread capture_thread(DbusCaptureThread);
390 std::thread user_input_thread(UserInputThread);
391 capture_thread.join();
392
393 return 0;
394}