gtkStatsMonitor.cxx 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850
  1. /**
  2. * PANDA 3D SOFTWARE
  3. * Copyright (c) Carnegie Mellon University. All rights reserved.
  4. *
  5. * All use of this software is subject to the terms of the revised BSD
  6. * license. You should have received a copy of this license along
  7. * with this source code in a file named "LICENSE."
  8. *
  9. * @file gtkStatsMonitor.cxx
  10. * @author drose
  11. * @date 2006-01-16
  12. */
  13. #include "gtkStatsMonitor.h"
  14. #include "gtkStats.h"
  15. #include "gtkStatsServer.h"
  16. #include "gtkStatsStripChart.h"
  17. #include "gtkStatsChartMenu.h"
  18. #include "gtkStatsPianoRoll.h"
  19. #include "gtkStatsFlameGraph.h"
  20. #include "gtkStatsTimeline.h"
  21. #include "pStatGraph.h"
  22. #include "pStatCollectorDef.h"
  23. #include "convert_srgb.h"
  24. /**
  25. *
  26. */
  27. GtkStatsMonitor::
  28. GtkStatsMonitor(GtkStatsServer *server) : PStatMonitor(server) {
  29. _window = server->get_window();
  30. _menu_bar = server->get_menu_bar();
  31. _status_bar = server->get_status_bar();
  32. // These will be filled in later when the menu is created.
  33. _scroll_speed = 0.0;
  34. _pause = false;
  35. _next_chart_index = 2;
  36. _resolution = gdk_screen_get_resolution(gdk_screen_get_default());
  37. setup_speed_menu();
  38. setup_frame_rate_label();
  39. }
  40. /**
  41. *
  42. */
  43. GtkStatsMonitor::
  44. ~GtkStatsMonitor() {
  45. close();
  46. }
  47. /**
  48. * Closes all the graphs associated with this monitor.
  49. */
  50. void GtkStatsMonitor::
  51. close() {
  52. PStatMonitor::close();
  53. remove_all_graphs();
  54. for (GtkWidget *label : _status_bar_labels) {
  55. gtk_container_remove(GTK_CONTAINER(_status_bar), label);
  56. }
  57. _status_bar_collectors.clear();
  58. _status_bar_labels.clear();
  59. if (_speed_menu_item != nullptr) {
  60. gtk_container_remove(GTK_CONTAINER(_menu_bar), _speed_menu_item);
  61. _speed_menu_item = nullptr;
  62. }
  63. for (GtkStatsChartMenu *chart_menu : _chart_menus) {
  64. chart_menu->remove_from_menu_bar(_menu_bar);
  65. delete chart_menu;
  66. }
  67. _chart_menus.clear();
  68. if (_frame_rate_menu_item != nullptr) {
  69. gtk_container_remove(GTK_CONTAINER(_menu_bar), _frame_rate_menu_item);
  70. _frame_rate_menu_item = nullptr;
  71. }
  72. _next_chart_index = 2;
  73. }
  74. /**
  75. * Should be redefined to return a descriptive name for the type of
  76. * PStatsMonitor this is.
  77. */
  78. std::string GtkStatsMonitor::
  79. get_monitor_name() {
  80. return "GtkStats";
  81. }
  82. /**
  83. * Called after the monitor has been fully set up. At this time, it will have
  84. * a valid _client_data pointer, and things like is_alive() and close() will
  85. * be meaningful. However, we may not yet know who we're connected to
  86. * (is_client_known() may return false), and we may not know anything about
  87. * the threads or collectors we're about to get data on.
  88. */
  89. void GtkStatsMonitor::
  90. initialized() {
  91. }
  92. /**
  93. * Called when the "hello" message has been received from the client. At this
  94. * time, the client's hostname and program name will be known.
  95. */
  96. void GtkStatsMonitor::
  97. got_hello() {
  98. }
  99. /**
  100. * Like got_hello(), this is called when the "hello" message has been received
  101. * from the client. At this time, the client's hostname and program name will
  102. * be known. However, the client appears to be an incompatible version and
  103. * the connection will be terminated; the monitor should issue a message to
  104. * that effect.
  105. */
  106. void GtkStatsMonitor::
  107. got_bad_version(int client_major, int client_minor,
  108. int server_major, int server_minor) {
  109. std::ostringstream str;
  110. str << "Unable to honor connection attempt from "
  111. << get_client_progname() << " on " << get_client_hostname()
  112. << ": unsupported PStats version "
  113. << client_major << "." << client_minor;
  114. if (server_minor == 0) {
  115. str << " (server understands version " << server_major
  116. << "." << server_minor << " only).";
  117. } else {
  118. str << " (server understands versions " << server_major
  119. << ".0 through " << server_major << "." << server_minor << ").";
  120. }
  121. std::string message = str.str();
  122. GtkWidget *dialog =
  123. gtk_message_dialog_new(GTK_WINDOW(_window),
  124. GTK_DIALOG_DESTROY_WITH_PARENT,
  125. GTK_MESSAGE_ERROR,
  126. GTK_BUTTONS_CLOSE,
  127. "%s", message.c_str());
  128. gtk_dialog_run(GTK_DIALOG(dialog));
  129. gtk_widget_destroy(dialog);
  130. }
  131. /**
  132. * Called whenever a new Collector definition is received from the client.
  133. * Generally, the client will send all of its collectors over shortly after
  134. * connecting, but there's no guarantee that they will all be received before
  135. * the first frames are received. The monitor should be prepared to accept
  136. * new Collector definitions midstream.
  137. */
  138. void GtkStatsMonitor::
  139. new_collector(int collector_index) {
  140. for (GtkStatsGraph *graph : _graphs) {
  141. graph->new_collector(collector_index);
  142. }
  143. // We might need to update our menus.
  144. for (GtkStatsChartMenu *chart_menu : _chart_menus) {
  145. chart_menu->check_update();
  146. }
  147. }
  148. /**
  149. * Called whenever a new Thread definition is received from the client.
  150. * Generally, the client will send all of its threads over shortly after
  151. * connecting, but there's no guarantee that they will all be received before
  152. * the first frames are received. The monitor should be prepared to accept
  153. * new Thread definitions midstream.
  154. */
  155. void GtkStatsMonitor::
  156. new_thread(int thread_index) {
  157. GtkStatsChartMenu *chart_menu = new GtkStatsChartMenu(this, thread_index);
  158. chart_menu->add_to_menu_bar(_menu_bar, _next_chart_index);
  159. ++_next_chart_index;
  160. _chart_menus.push_back(chart_menu);
  161. }
  162. /**
  163. * Called as each frame's data is made available. There is no guarantee the
  164. * frames will arrive in order, or that all of them will arrive at all. The
  165. * monitor should be prepared to accept frames received out-of-order or
  166. * missing.
  167. */
  168. void GtkStatsMonitor::
  169. new_data(int thread_index, int frame_number) {
  170. for (GtkStatsGraph *graph : _graphs) {
  171. graph->new_data(thread_index, frame_number);
  172. }
  173. if (thread_index == 0) {
  174. update_status_bar();
  175. }
  176. if (!_have_data) {
  177. open_default_graphs();
  178. _have_data = true;
  179. // Flash the window.
  180. gtk_window_set_urgency_hint(GTK_WINDOW(_window), TRUE);
  181. }
  182. }
  183. /**
  184. * Called whenever the connection to the client has been lost. This is a
  185. * permanent state change. The monitor should update its display to represent
  186. * this, and may choose to close down automatically.
  187. */
  188. void GtkStatsMonitor::
  189. lost_connection() {
  190. nout << "Lost connection to " << get_client_hostname() << "\n";
  191. }
  192. /**
  193. * If has_idle() returns true, this will be called periodically to allow the
  194. * monitor to update its display or whatever it needs to do.
  195. */
  196. void GtkStatsMonitor::
  197. idle() {
  198. // Check if any of our chart menus need updating.
  199. for (GtkStatsChartMenu *chart_menu : _chart_menus) {
  200. chart_menu->check_update();
  201. }
  202. // Update the frame rate label from the main thread (thread 0).
  203. const PStatThreadData *thread_data = get_client_data()->get_thread_data(0);
  204. double frame_rate = thread_data->get_frame_rate();
  205. if (frame_rate != 0.0f) {
  206. char buffer[128];
  207. sprintf(buffer, "%0.1f ms / %0.1f Hz", 1000.0f / frame_rate, frame_rate);
  208. gtk_label_set_text(GTK_LABEL(_frame_rate_label), buffer);
  209. if (!_status_bar_labels.empty()) {
  210. gtk_label_set_text(GTK_LABEL(_status_bar_labels[0]), buffer);
  211. }
  212. }
  213. }
  214. /**
  215. * Should be redefined to return true if you want to redefine idle() and
  216. * expect it to be called.
  217. */
  218. bool GtkStatsMonitor::
  219. has_idle() {
  220. return true;
  221. }
  222. /**
  223. * Called when the user guide bars have been changed.
  224. */
  225. void GtkStatsMonitor::
  226. user_guide_bars_changed() {
  227. for (GtkStatsGraph *graph : _graphs) {
  228. graph->user_guide_bars_changed();
  229. }
  230. }
  231. /**
  232. * Returns the window handle to the monitor's window.
  233. */
  234. GtkWidget *GtkStatsMonitor::
  235. get_window() const {
  236. return _window;
  237. }
  238. /**
  239. *
  240. */
  241. GtkAccelGroup *GtkStatsMonitor::
  242. get_accel_group() const {
  243. return ((GtkStatsServer *)_server)->get_accel_group();
  244. }
  245. /**
  246. * Returns the screen DPI.
  247. */
  248. double GtkStatsMonitor::
  249. get_resolution() const {
  250. return _resolution;
  251. }
  252. /**
  253. * Opens a new timeline.
  254. */
  255. PStatGraph *GtkStatsMonitor::
  256. open_timeline() {
  257. GtkStatsTimeline *graph = new GtkStatsTimeline(this);
  258. add_graph(graph);
  259. return graph;
  260. }
  261. /**
  262. * Opens a new flame graph showing the indicated data.
  263. */
  264. PStatGraph *GtkStatsMonitor::
  265. open_flame_graph(int thread_index, int collector_index) {
  266. GtkStatsFlameGraph *graph = new GtkStatsFlameGraph(this, thread_index, collector_index);
  267. add_graph(graph);
  268. return graph;
  269. }
  270. /**
  271. * Opens a new strip chart showing the indicated data.
  272. */
  273. PStatGraph *GtkStatsMonitor::
  274. open_strip_chart(int thread_index, int collector_index, bool show_level) {
  275. GtkStatsStripChart *graph =
  276. new GtkStatsStripChart(this, thread_index, collector_index, show_level);
  277. add_graph(graph);
  278. return graph;
  279. }
  280. /**
  281. * Opens a new piano roll showing the indicated data.
  282. */
  283. PStatGraph *GtkStatsMonitor::
  284. open_piano_roll(int thread_index) {
  285. GtkStatsPianoRoll *graph = new GtkStatsPianoRoll(this, thread_index);
  286. add_graph(graph);
  287. return graph;
  288. }
  289. /**
  290. * Opens a dialog to change the given collector color.
  291. */
  292. void GtkStatsMonitor::
  293. choose_collector_color(int collector_index) {
  294. const LRGBColor &current = get_collector_color(collector_index);
  295. GtkWidget *chooser = gtk_color_chooser_dialog_new(nullptr, GTK_WINDOW(_window));
  296. gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(chooser), FALSE);
  297. GdkRGBA rgba;
  298. rgba.red = encode_sRGB_float((float)current[0]);
  299. rgba.green = encode_sRGB_float((float)current[1]);
  300. rgba.blue = encode_sRGB_float((float)current[2]);
  301. rgba.alpha = 1.0;
  302. gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(chooser), &rgba);
  303. if (gtk_dialog_run(GTK_DIALOG(chooser)) == GTK_RESPONSE_OK) {
  304. gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(chooser), &rgba);
  305. LRGBColor result(
  306. decode_sRGB_float((float)rgba.red),
  307. decode_sRGB_float((float)rgba.green),
  308. decode_sRGB_float((float)rgba.blue));
  309. set_collector_color(collector_index, result);
  310. for (GtkStatsGraph *graph : _graphs) {
  311. graph->reset_collector_color(collector_index);
  312. }
  313. }
  314. gtk_widget_destroy(chooser);
  315. }
  316. /**
  317. * Resets the color of the given collector to the default.
  318. */
  319. void GtkStatsMonitor::
  320. reset_collector_color(int collector_index) {
  321. clear_collector_color(collector_index);
  322. for (GtkStatsGraph *graph : _graphs) {
  323. graph->reset_collector_color(collector_index);
  324. }
  325. }
  326. /**
  327. * Adds a new MenuDef to the monitor, or returns an existing one if there is
  328. * already one just like it.
  329. */
  330. const GtkStatsMonitor::MenuDef *GtkStatsMonitor::
  331. add_menu(const MenuDef &menu_def) {
  332. std::pair<Menus::iterator, bool> result = _menus.insert(menu_def);
  333. Menus::iterator mi = result.first;
  334. const GtkStatsMonitor::MenuDef &new_menu_def = (*mi);
  335. if (result.second) {
  336. // A new MenuDef was inserted.
  337. ((GtkStatsMonitor::MenuDef &)new_menu_def)._monitor = this;
  338. }
  339. return &new_menu_def;
  340. }
  341. /**
  342. * Called when the user selects a new time units from the monitor pulldown
  343. * menu, this should adjust the units for all graphs to the indicated mask if
  344. * it is a time-based graph.
  345. */
  346. void GtkStatsMonitor::
  347. set_time_units(int unit_mask) {
  348. for (GtkStatsGraph *graph : _graphs) {
  349. graph->set_time_units(unit_mask);
  350. }
  351. }
  352. /**
  353. * Called when the user selects a new scroll speed from the monitor pulldown
  354. * menu, this should adjust the speeds for all graphs to the indicated value.
  355. */
  356. void GtkStatsMonitor::
  357. set_scroll_speed(double scroll_speed) {
  358. _scroll_speed = scroll_speed;
  359. // First, change all of the open graphs appropriately.
  360. for (GtkStatsGraph *graph : _graphs) {
  361. graph->set_scroll_speed(_scroll_speed);
  362. }
  363. }
  364. /**
  365. * Called when the user selects a pause on or pause off option from the menu.
  366. */
  367. void GtkStatsMonitor::
  368. set_pause(bool pause) {
  369. _pause = pause;
  370. // First, change all of the open graphs appropriately.
  371. for (GtkStatsGraph *graph : _graphs) {
  372. graph->set_pause(_pause);
  373. }
  374. }
  375. /**
  376. * Adds the newly-created graph to the list of managed graphs.
  377. */
  378. void GtkStatsMonitor::
  379. add_graph(GtkStatsGraph *graph) {
  380. _graphs.insert(graph);
  381. graph->set_time_units(((GtkStatsServer *)_server)->get_time_units());
  382. graph->set_scroll_speed(_scroll_speed);
  383. graph->set_pause(_pause);
  384. }
  385. /**
  386. * Deletes the indicated graph.
  387. */
  388. void GtkStatsMonitor::
  389. remove_graph(GtkStatsGraph *graph) {
  390. Graphs::iterator gi = _graphs.find(graph);
  391. if (gi != _graphs.end()) {
  392. _graphs.erase(gi);
  393. delete graph;
  394. }
  395. }
  396. /**
  397. * Deletes all open graphs.
  398. */
  399. void GtkStatsMonitor::
  400. remove_all_graphs() {
  401. for (GtkStatsGraph *graph : _graphs) {
  402. delete graph;
  403. }
  404. _graphs.clear();
  405. }
  406. /**
  407. * Creates the "Speed" pulldown menu.
  408. */
  409. void GtkStatsMonitor::
  410. setup_speed_menu() {
  411. GtkWidget *menu = gtk_menu_new();
  412. _speed_menu_item = gtk_menu_item_new_with_label("Speed");
  413. gtk_menu_item_set_submenu(GTK_MENU_ITEM(_speed_menu_item), menu);
  414. gtk_menu_shell_append(GTK_MENU_SHELL(_menu_bar), _speed_menu_item);
  415. GSList *group = nullptr;
  416. GtkWidget *item;
  417. item = gtk_radio_menu_item_new_with_label(group, "1");
  418. gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
  419. g_signal_connect(G_OBJECT(item), "toggled",
  420. G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
  421. if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))) {
  422. GtkStatsMonitor *self = (GtkStatsMonitor *)data;
  423. self->set_scroll_speed(1);
  424. }
  425. }), this);
  426. group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
  427. item = gtk_radio_menu_item_new_with_label(group, "2");
  428. gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
  429. g_signal_connect(G_OBJECT(item), "toggled",
  430. G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
  431. if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))) {
  432. GtkStatsMonitor *self = (GtkStatsMonitor *)data;
  433. self->set_scroll_speed(2);
  434. }
  435. }), this);
  436. group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
  437. item = gtk_radio_menu_item_new_with_label(group, "3");
  438. gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
  439. gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
  440. g_signal_connect(G_OBJECT(item), "toggled",
  441. G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
  442. if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))) {
  443. GtkStatsMonitor *self = (GtkStatsMonitor *)data;
  444. self->set_scroll_speed(3);
  445. }
  446. }), this);
  447. group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
  448. item = gtk_radio_menu_item_new_with_label(group, "6");
  449. gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
  450. g_signal_connect(G_OBJECT(item), "toggled",
  451. G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
  452. if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))) {
  453. GtkStatsMonitor *self = (GtkStatsMonitor *)data;
  454. self->set_scroll_speed(6);
  455. }
  456. }), this);
  457. group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
  458. item = gtk_radio_menu_item_new_with_label(group, "12");
  459. gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
  460. g_signal_connect(G_OBJECT(item), "toggled",
  461. G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
  462. if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))) {
  463. GtkStatsMonitor *self = (GtkStatsMonitor *)data;
  464. self->set_scroll_speed(12);
  465. }
  466. }), this);
  467. group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
  468. item = gtk_separator_menu_item_new();
  469. gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
  470. item = gtk_check_menu_item_new_with_label("pause");
  471. gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
  472. g_signal_connect(G_OBJECT(item), "toggled",
  473. G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
  474. GtkStatsMonitor *self = (GtkStatsMonitor *)data;
  475. self->set_pause(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item)));
  476. }), this);
  477. set_scroll_speed(3);
  478. set_pause(false);
  479. gtk_widget_show_all(_speed_menu_item);
  480. ++_next_chart_index;
  481. }
  482. /**
  483. * Creates the frame rate label on the right end of the menu bar. This is
  484. * used as a text label to display the main thread's frame rate to the user,
  485. * although it is implemented as a right-justified toplevel menu item that
  486. * doesn't open to anything.
  487. */
  488. void GtkStatsMonitor::
  489. setup_frame_rate_label() {
  490. _frame_rate_menu_item = gtk_menu_item_new();
  491. _frame_rate_label = gtk_label_new("");
  492. gtk_container_add(GTK_CONTAINER(_frame_rate_menu_item), _frame_rate_label);
  493. gtk_widget_set_sensitive(_frame_rate_menu_item, FALSE);
  494. gtk_widget_show(_frame_rate_menu_item);
  495. gtk_widget_show(_frame_rate_label);
  496. gtk_menu_item_set_right_justified(GTK_MENU_ITEM(_frame_rate_menu_item), TRUE);
  497. gtk_menu_shell_append(GTK_MENU_SHELL(_menu_bar), _frame_rate_menu_item);
  498. }
  499. /**
  500. * Updates the status bar.
  501. */
  502. void GtkStatsMonitor::
  503. update_status_bar() {
  504. const PStatClientData *client_data = get_client_data();
  505. if (client_data == nullptr) {
  506. return;
  507. }
  508. const PStatThreadData *thread_data = get_client_data()->get_thread_data(0);
  509. if (thread_data == nullptr || thread_data->is_empty()) {
  510. return;
  511. }
  512. int frame_number = thread_data->get_latest_frame_number();
  513. const PStatFrameData &frame_data = thread_data->get_latest_frame();
  514. pvector<int> collectors;
  515. // The first label displays the frame rate.
  516. size_t li = 1;
  517. collectors.push_back(0);
  518. if (_status_bar_labels.empty()) {
  519. GtkWidget *label = gtk_label_new("");
  520. gtk_container_add(GTK_CONTAINER(_status_bar), label);
  521. _status_bar_labels.push_back(label);
  522. }
  523. // Gather the top-level collector list.
  524. int num_toplevel_collectors = client_data->get_num_toplevel_collectors();
  525. for (int tc = 0; tc < num_toplevel_collectors; tc++) {
  526. int collector = client_data->get_toplevel_collector(tc);
  527. if (client_data->has_collector(collector) &&
  528. client_data->get_collector_has_level(collector, 0)) {
  529. PStatView &view = get_level_view(collector, 0);
  530. view.set_to_frame(frame_data);
  531. double value = view.get_net_value();
  532. if (value == 0.0) {
  533. // Don't include it unless we've included it before.
  534. if (std::find(_status_bar_collectors.begin(), _status_bar_collectors.end(), collector) == _status_bar_collectors.end()) {
  535. continue;
  536. }
  537. }
  538. // Add the value for other threads that have this collector.
  539. for (int thread_index = 1; thread_index < client_data->get_num_threads(); ++thread_index) {
  540. PStatView &view = get_level_view(collector, thread_index);
  541. view.set_to_frame(frame_number);
  542. value += view.get_net_value();
  543. }
  544. const PStatCollectorDef &def = client_data->get_collector_def(collector);
  545. std::string text = def._name;
  546. text += ": " + PStatGraph::format_number(value, PStatGraph::GBU_named | PStatGraph::GBU_show_units, def._level_units);
  547. GtkWidget *label;
  548. if (li < _status_bar_labels.size()) {
  549. label = _status_bar_labels[li++];
  550. gtk_label_set_text(GTK_LABEL(label), text.c_str());
  551. }
  552. else {
  553. label = gtk_label_new(text.c_str());
  554. gtk_container_add(GTK_CONTAINER(_status_bar), label);
  555. _status_bar_labels.push_back(label);
  556. }
  557. collectors.push_back(collector);
  558. }
  559. }
  560. _status_bar_collectors = std::move(collectors);
  561. gtk_widget_show_all(_status_bar);
  562. }
  563. /**
  564. * Handles clicks on a partion of the status bar.
  565. */
  566. gboolean GtkStatsMonitor::
  567. status_bar_button_event(GtkWidget *widget, GdkEventButton *event, gpointer data) {
  568. GtkStatsMonitor *monitor = (GtkStatsMonitor *)data;
  569. GtkFlowBoxChild *child = gtk_flow_box_get_child_at_pos(
  570. GTK_FLOW_BOX(monitor->_status_bar), event->x, event->y);
  571. if (child == nullptr) {
  572. return FALSE;
  573. }
  574. // Which child is this?
  575. GList *children = gtk_container_get_children(GTK_CONTAINER(monitor->_status_bar));
  576. int index = g_list_index(children, child);
  577. g_list_free(children);
  578. if (index < 0 || (size_t)index >= monitor->_status_bar_labels.size()) {
  579. return FALSE;
  580. }
  581. const PStatClientData *client_data = monitor->get_client_data();
  582. if (client_data == nullptr) {
  583. return FALSE;
  584. }
  585. int collector = monitor->_status_bar_collectors[index];
  586. if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
  587. monitor->open_strip_chart(0, collector, collector != 0);
  588. // Also open a strip chart for other threads with data for this
  589. // collector.
  590. if (collector != 0) {
  591. for (int thread_index = 1; thread_index < client_data->get_num_threads(); ++thread_index) {
  592. PStatView &view = monitor->get_level_view(collector, thread_index);
  593. if (view.get_net_value() > 0.0) {
  594. monitor->open_strip_chart(thread_index, collector, true);
  595. }
  596. }
  597. }
  598. return TRUE;
  599. }
  600. else if (event->type == GDK_BUTTON_PRESS && event->button == 3 && index > 0) {
  601. PStatView &level_view = monitor->get_level_view(collector, 0);
  602. const PStatViewLevel *view_level = level_view.get_top_level();
  603. int num_children = view_level->get_num_children();
  604. if (num_children == 0) {
  605. return FALSE;
  606. }
  607. GtkWidget *menu = gtk_menu_new();
  608. // Reverse the order since the menus are listed from the top down; we want
  609. // to be visually consistent with the graphs, which list these labels from
  610. // the bottom up.
  611. for (int c = num_children - 1; c >= 0; c--) {
  612. const PStatViewLevel *child_level = view_level->get_child(c);
  613. int child_collector = child_level->get_collector();
  614. const MenuDef *menu_def = monitor->add_menu({0, child_collector, CT_strip_chart, true});
  615. double value = child_level->get_net_value();
  616. const PStatCollectorDef &def = client_data->get_collector_def(child_collector);
  617. std::string text = def._name;
  618. text += ": " + PStatGraph::format_number(value, PStatGraph::GBU_named | PStatGraph::GBU_show_units, def._level_units);
  619. GtkWidget *menu_item = gtk_menu_item_new_with_label(text.c_str());
  620. gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
  621. g_signal_connect(G_OBJECT(menu_item), "activate",
  622. G_CALLBACK(menu_activate),
  623. (void *)menu_def);
  624. }
  625. gtk_widget_show_all(menu);
  626. GtkWidget *label = monitor->_status_bar_labels[index];
  627. gtk_menu_popup_at_widget(GTK_MENU(menu), label,
  628. GDK_GRAVITY_NORTH_WEST,
  629. GDK_GRAVITY_SOUTH_WEST, nullptr);
  630. return TRUE;
  631. }
  632. return FALSE;
  633. }
  634. /**
  635. * Callback when a menu item is selected.
  636. */
  637. void GtkStatsMonitor::
  638. menu_activate(GtkWidget *widget, gpointer data) {
  639. const MenuDef &menu_def = *(const MenuDef *)data;
  640. GtkStatsMonitor *monitor = menu_def._monitor;
  641. if (monitor == nullptr) {
  642. return;
  643. }
  644. switch (menu_def._chart_type) {
  645. case CT_timeline:
  646. monitor->open_timeline();
  647. break;
  648. case CT_strip_chart:
  649. monitor->open_strip_chart(menu_def._thread_index,
  650. menu_def._collector_index,
  651. menu_def._show_level);
  652. break;
  653. case CT_flame_graph:
  654. monitor->open_flame_graph(menu_def._thread_index,
  655. menu_def._collector_index);
  656. break;
  657. case CT_piano_roll:
  658. monitor->open_piano_roll(menu_def._thread_index);
  659. break;
  660. case CT_choose_color:
  661. monitor->choose_collector_color(menu_def._collector_index);
  662. break;
  663. case CT_reset_color:
  664. monitor->reset_collector_color(menu_def._collector_index);
  665. break;
  666. }
  667. }
  668. /**
  669. * Called when a status bar item is double-clicked.
  670. */
  671. void GtkStatsMonitor::
  672. handle_status_bar_click(int item) {
  673. if (item == 0) {
  674. open_strip_chart(0, 0, false);
  675. }
  676. else if (item >= 1 && (size_t)item < _status_bar_collectors.size()) {
  677. int collector = _status_bar_collectors[item];
  678. open_strip_chart(0, collector, true);
  679. // Also open a strip chart for other threads with data for this
  680. // collector.
  681. const PStatClientData *client_data = get_client_data();
  682. for (int thread_index = 1; thread_index < client_data->get_num_threads(); ++thread_index) {
  683. PStatView &view = get_level_view(collector, thread_index);
  684. if (view.get_net_value() > 0.0) {
  685. open_strip_chart(thread_index, collector, true);
  686. }
  687. }
  688. }
  689. }
  690. /**
  691. * Called when a status bar item is right-clicked.
  692. */
  693. void GtkStatsMonitor::
  694. handle_status_bar_popup(int item) {
  695. if (item >= 0 && (size_t)item < _status_bar_collectors.size()) {
  696. int collector = _status_bar_collectors[item];
  697. PStatView &level_view = get_level_view(collector, 0);
  698. const PStatViewLevel *view_level = level_view.get_top_level();
  699. int num_children = view_level->get_num_children();
  700. if (num_children == 0) {
  701. return;
  702. }
  703. GtkWidget *menu = gtk_menu_new();
  704. // Reverse the order since the menus are listed from the top down; we want
  705. // to be visually consistent with the graphs, which list these labels from
  706. // the bottom up.
  707. const PStatClientData *client_data = get_client_data();
  708. for (int c = num_children - 1; c >= 0; c--) {
  709. const PStatViewLevel *child_level = view_level->get_child(c);
  710. int child_collector = child_level->get_collector();
  711. const MenuDef *menu_def = add_menu({0, child_collector, CT_strip_chart, true});
  712. double value = child_level->get_net_value();
  713. const PStatCollectorDef &def = client_data->get_collector_def(child_collector);
  714. std::string text = def._name;
  715. text += ": " + PStatGraph::format_number(value, PStatGraph::GBU_named | PStatGraph::GBU_show_units, def._level_units);
  716. GtkWidget *menu_item = gtk_menu_item_new_with_label(text.c_str());
  717. gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
  718. g_signal_connect(G_OBJECT(menu_item), "activate",
  719. G_CALLBACK(menu_activate),
  720. (void *)menu_def);
  721. }
  722. gtk_widget_show_all(menu);
  723. GtkWidget *label = _status_bar_labels[item];
  724. gtk_menu_popup_at_widget(GTK_MENU(menu), label,
  725. GDK_GRAVITY_NORTH_WEST,
  726. GDK_GRAVITY_SOUTH_WEST, nullptr);
  727. }
  728. }