Переглянути джерело

pstats: Allow scrolling through frames in flame charts

Closes #1658
rdb 1 рік тому
батько
коміт
c48721493a

+ 2 - 2
pandatool/src/gtk-stats/gtkStatsChartMenu.cxx

@@ -226,7 +226,7 @@ add_view(GtkWidget *parent_menu, const PStatViewLevel *view_level,
   }
   }
   else if (menu_item != nullptr && menu == nullptr) {
   else if (menu_item != nullptr && menu == nullptr) {
     // Unhook the signal handler, we are creating a submenu.
     // Unhook the signal handler, we are creating a submenu.
-    GtkStatsMonitor::MenuDef smd(_thread_index, collector, GtkStatsMonitor::CT_strip_chart, show_level);
+    GtkStatsMonitor::MenuDef smd(GtkStatsMonitor::CT_strip_chart, _thread_index, collector, -1, show_level);
     const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd);
     const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd);
 
 
     g_signal_handlers_disconnect_by_data(G_OBJECT(menu_item), (void *)menu_def);
     g_signal_handlers_disconnect_by_data(G_OBJECT(menu_item), (void *)menu_def);
@@ -277,7 +277,7 @@ add_view(GtkWidget *parent_menu, const PStatViewLevel *view_level,
 GtkWidget *GtkStatsChartMenu::
 GtkWidget *GtkStatsChartMenu::
 make_menu_item(const char *label, int collector_index, ChartType chart_type,
 make_menu_item(const char *label, int collector_index, ChartType chart_type,
                bool show_level) {
                bool show_level) {
-  GtkStatsMonitor::MenuDef smd(_thread_index, collector_index, chart_type, show_level);
+  GtkStatsMonitor::MenuDef smd(chart_type, _thread_index, collector_index, -1, show_level);
   const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd);
   const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd);
 
 
   GtkWidget *menu_item = gtk_menu_item_new_with_label(label);
   GtkWidget *menu_item = gtk_menu_item_new_with_label(label);

+ 85 - 10
pandatool/src/gtk-stats/gtkStatsFlameGraph.cxx

@@ -24,13 +24,16 @@ static const int default_flame_graph_height = 150;
  */
  */
 GtkStatsFlameGraph::
 GtkStatsFlameGraph::
 GtkStatsFlameGraph(GtkStatsMonitor *monitor, int thread_index,
 GtkStatsFlameGraph(GtkStatsMonitor *monitor, int thread_index,
-                   int collector_index) :
-  PStatFlameGraph(monitor, thread_index, collector_index, 0, 0),
+                   int collector_index, int frame_number) :
+  PStatFlameGraph(monitor, thread_index, collector_index, frame_number, 0, 0),
   GtkStatsGraph(monitor, false)
   GtkStatsGraph(monitor, false)
 {
 {
   // Let's show the units on the guide bar labels.  There's room.
   // Let's show the units on the guide bar labels.  There's room.
   set_guide_bar_units(get_guide_bar_units() | GBU_show_units);
   set_guide_bar_units(get_guide_bar_units() | GBU_show_units);
 
 
+  std::string window_title = get_title_text();
+  gtk_window_set_title(GTK_WINDOW(_window), window_title.c_str());
+
   // Put some stuff on top of the graph.
   // Put some stuff on top of the graph.
   _top_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
   _top_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
   gtk_box_pack_start(GTK_BOX(_graph_vbox), _top_hbox,
   gtk_box_pack_start(GTK_BOX(_graph_vbox), _top_hbox,
@@ -51,6 +54,13 @@ GtkStatsFlameGraph(GtkStatsMonitor *monitor, int thread_index,
   gtk_box_pack_start(GTK_BOX(_top_hbox), _scale_area, TRUE, TRUE, 0);
   gtk_box_pack_start(GTK_BOX(_top_hbox), _scale_area, TRUE, TRUE, 0);
   gtk_box_pack_end(GTK_BOX(_top_hbox), _total_label, FALSE, FALSE, 0);
   gtk_box_pack_end(GTK_BOX(_top_hbox), _total_label, FALSE, FALSE, 0);
 
 
+  // Listen for mouse scroll and keyboard events.
+  gtk_widget_add_events(_window, GDK_SCROLL_MASK | GDK_KEY_PRESS_MASK);
+  g_signal_connect(G_OBJECT(_window), "scroll_event",
+                   G_CALLBACK(scroll_callback), this);
+  g_signal_connect(G_OBJECT(_window), "key_press_event",
+                   G_CALLBACK(key_press_callback), this);
+
   gtk_widget_set_size_request(_graph_window,
   gtk_widget_set_size_request(_graph_window,
     default_flame_graph_width * monitor->get_resolution() / 96,
     default_flame_graph_width * monitor->get_resolution() / 96,
     default_flame_graph_height * monitor->get_resolution() / 96);
     default_flame_graph_height * monitor->get_resolution() / 96);
@@ -284,6 +294,15 @@ draw_bar(int depth, int from_x, int to_x, int collector_index, int parent_index)
             pango_layout_set_text(layout, def._name.c_str(), def._name.size());
             pango_layout_set_text(layout, def._name.c_str(), def._name.size());
           }
           }
         }
         }
+        else if (collector_index == 0 && get_frame_number() >= 0) {
+          char text[32];
+          sprintf(text, "Frame %d", get_frame_number());
+          pango_layout_set_text(layout, text, -1);
+          if (pango_layout_is_ellipsized(layout)) {
+            // Nope, it's too long, go back.
+            pango_layout_set_text(layout, def._name.c_str(), def._name.size());
+          }
+        }
       }
       }
 
 
       int width, height;
       int width, height;
@@ -429,8 +448,7 @@ handle_button_press(int graph_x, int graph_y, bool double_click, int button) {
 
 
         {
         {
           const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
           const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
-            get_thread_index(), collector_index,
-            GtkStatsMonitor::CT_strip_chart, false,
+            GtkStatsMonitor::CT_strip_chart, get_thread_index(), collector_index,
           });
           });
 
 
           GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Strip Chart");
           GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Strip Chart");
@@ -442,8 +460,7 @@ handle_button_press(int graph_x, int graph_y, bool double_click, int button) {
 
 
         {
         {
           const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
           const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
-            get_thread_index(), collector_index,
-            GtkStatsMonitor::CT_flame_graph,
+            GtkStatsMonitor::CT_flame_graph, get_thread_index(), collector_index,
           });
           });
 
 
           GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Flame Graph");
           GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Flame Graph");
@@ -460,8 +477,7 @@ handle_button_press(int graph_x, int graph_y, bool double_click, int button) {
 
 
         {
         {
           const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
           const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
-            -1, collector_index,
-            GtkStatsMonitor::CT_choose_color,
+            GtkStatsMonitor::CT_choose_color, -1, collector_index,
           });
           });
 
 
           GtkWidget *menu_item = gtk_menu_item_new_with_label("Change Color...");
           GtkWidget *menu_item = gtk_menu_item_new_with_label("Change Color...");
@@ -473,8 +489,7 @@ handle_button_press(int graph_x, int graph_y, bool double_click, int button) {
 
 
         {
         {
           const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
           const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
-            -1, collector_index,
-            GtkStatsMonitor::CT_reset_color,
+            GtkStatsMonitor::CT_reset_color, -1, collector_index,
           });
           });
 
 
           GtkWidget *menu_item = gtk_menu_item_new_with_label("Reset Color");
           GtkWidget *menu_item = gtk_menu_item_new_with_label("Reset Color");
@@ -721,3 +736,63 @@ draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data) {
 
 
   return TRUE;
   return TRUE;
 }
 }
+
+/**
+ *
+ */
+gboolean GtkStatsFlameGraph::
+scroll_callback(GtkWidget *widget, GdkEventScroll *event, gpointer data) {
+  GtkStatsFlameGraph *self = (GtkStatsFlameGraph *)data;
+  bool changed = false;
+  switch (event->direction) {
+  case GDK_SCROLL_LEFT:
+    changed = self->prev_frame();
+    break;
+
+  case GDK_SCROLL_RIGHT:
+    changed = self->next_frame();
+    break;
+  }
+
+  if (changed) {
+    std::string window_title = self->get_title_text();
+    gtk_window_set_title(GTK_WINDOW(self->_window), window_title.c_str());
+    return TRUE;
+  } else {
+    return FALSE;
+  }
+}
+
+/**
+ *
+ */
+gboolean GtkStatsFlameGraph::
+key_press_callback(GtkWidget *widget, GdkEventKey *event, gpointer data) {
+  GtkStatsFlameGraph *self = (GtkStatsFlameGraph *)data;
+  bool changed = false;
+  switch (event->keyval) {
+  case GDK_KEY_Left:
+    changed = self->prev_frame();
+    break;
+
+  case GDK_KEY_Right:
+    changed = self->next_frame();
+    break;
+
+  case GDK_KEY_Home:
+    changed = self->first_frame();
+    break;
+
+  case GDK_KEY_End:
+    changed = self->last_frame();
+    break;
+  }
+
+  if (changed) {
+    std::string window_title = self->get_title_text();
+    gtk_window_set_title(GTK_WINDOW(self->_window), window_title.c_str());
+    return TRUE;
+  } else {
+    return FALSE;
+  }
+}

+ 3 - 1
pandatool/src/gtk-stats/gtkStatsFlameGraph.h

@@ -28,7 +28,7 @@ class GtkStatsLabel;
 class GtkStatsFlameGraph final : public PStatFlameGraph, public GtkStatsGraph {
 class GtkStatsFlameGraph final : public PStatFlameGraph, public GtkStatsGraph {
 public:
 public:
   GtkStatsFlameGraph(GtkStatsMonitor *monitor, int thread_index,
   GtkStatsFlameGraph(GtkStatsMonitor *monitor, int thread_index,
-                     int collector_index=-1);
+                     int collector_index=-1, int frame_number=-1);
   virtual ~GtkStatsFlameGraph();
   virtual ~GtkStatsFlameGraph();
 
 
   virtual void new_collector(int collector_index);
   virtual void new_collector(int collector_index);
@@ -76,6 +76,8 @@ private:
 
 
   static void toggled_callback(GtkToggleButton *button, gpointer data);
   static void toggled_callback(GtkToggleButton *button, gpointer data);
   static gboolean draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data);
   static gboolean draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data);
+  static gboolean scroll_callback(GtkWidget *widget, GdkEventScroll *event, gpointer data);
+  static gboolean key_press_callback(GtkWidget *widget, GdkEventKey *event, gpointer data);
 
 
 private:
 private:
   std::string _net_value_text;
   std::string _net_value_text;

+ 9 - 4
pandatool/src/gtk-stats/gtkStatsMonitor.I

@@ -15,10 +15,12 @@
  *
  *
  */
  */
 INLINE GtkStatsMonitor::MenuDef::
 INLINE GtkStatsMonitor::MenuDef::
-MenuDef(int thread_index, int collector_index, ChartType chart_type, bool show_level) :
+MenuDef(ChartType chart_type, int thread_index, int collector_index,
+        int frame_number, bool show_level) :
+  _chart_type(chart_type),
   _thread_index(thread_index),
   _thread_index(thread_index),
   _collector_index(collector_index),
   _collector_index(collector_index),
-  _chart_type(chart_type),
+  _frame_number(frame_number),
   _show_level(show_level),
   _show_level(show_level),
   _monitor(nullptr)
   _monitor(nullptr)
 {
 {
@@ -29,14 +31,17 @@ MenuDef(int thread_index, int collector_index, ChartType chart_type, bool show_l
  */
  */
 INLINE bool GtkStatsMonitor::MenuDef::
 INLINE bool GtkStatsMonitor::MenuDef::
 operator < (const MenuDef &other) const {
 operator < (const MenuDef &other) const {
+  if (_chart_type != other._chart_type) {
+    return _chart_type < other._chart_type;
+  }
   if (_thread_index != other._thread_index) {
   if (_thread_index != other._thread_index) {
     return _thread_index < other._thread_index;
     return _thread_index < other._thread_index;
   }
   }
   if (_collector_index != other._collector_index) {
   if (_collector_index != other._collector_index) {
     return _collector_index < other._collector_index;
     return _collector_index < other._collector_index;
   }
   }
-  if (_chart_type != other._chart_type) {
-    return _chart_type < other._chart_type;
+  if (_frame_number != other._frame_number) {
+    return _frame_number < other._frame_number;
   }
   }
   return (int)_show_level < (int)other._show_level;
   return (int)_show_level < (int)other._show_level;
 }
 }

+ 6 - 5
pandatool/src/gtk-stats/gtkStatsMonitor.cxx

@@ -316,8 +316,8 @@ open_timeline() {
  * Opens a new flame graph showing the indicated data.
  * Opens a new flame graph showing the indicated data.
  */
  */
 PStatGraph *GtkStatsMonitor::
 PStatGraph *GtkStatsMonitor::
-open_flame_graph(int thread_index, int collector_index) {
-  GtkStatsFlameGraph *graph = new GtkStatsFlameGraph(this, thread_index, collector_index);
+open_flame_graph(int thread_index, int collector_index, int frame_number) {
+  GtkStatsFlameGraph *graph = new GtkStatsFlameGraph(this, thread_index, collector_index, frame_number);
   add_graph(graph);
   add_graph(graph);
   return graph;
   return graph;
 }
 }
@@ -734,7 +734,7 @@ status_bar_button_event(GtkWidget *widget, GdkEventButton *event, gpointer data)
       const PStatViewLevel *child_level = view_level->get_child(c);
       const PStatViewLevel *child_level = view_level->get_child(c);
 
 
       int child_collector = child_level->get_collector();
       int child_collector = child_level->get_collector();
-      const MenuDef *menu_def = monitor->add_menu({0, child_collector, CT_strip_chart, true});
+      const MenuDef *menu_def = monitor->add_menu({CT_strip_chart, 0, child_collector, -1, true});
 
 
       double value = child_level->get_net_value();
       double value = child_level->get_net_value();
 
 
@@ -787,7 +787,8 @@ menu_activate(GtkWidget *widget, gpointer data) {
 
 
   case CT_flame_graph:
   case CT_flame_graph:
     monitor->open_flame_graph(menu_def._thread_index,
     monitor->open_flame_graph(menu_def._thread_index,
-                              menu_def._collector_index);
+                              menu_def._collector_index,
+                              menu_def._frame_number);
     break;
     break;
 
 
   case CT_piano_roll:
   case CT_piano_roll:
@@ -853,7 +854,7 @@ handle_status_bar_popup(int item) {
       const PStatViewLevel *child_level = view_level->get_child(c);
       const PStatViewLevel *child_level = view_level->get_child(c);
 
 
       int child_collector = child_level->get_collector();
       int child_collector = child_level->get_collector();
-      const MenuDef *menu_def = add_menu({0, child_collector, CT_strip_chart, true});
+      const MenuDef *menu_def = add_menu({CT_strip_chart, 0, child_collector, -1, true});
 
 
       double value = child_level->get_net_value();
       double value = child_level->get_net_value();
 
 

+ 5 - 4
pandatool/src/gtk-stats/gtkStatsMonitor.h

@@ -46,13 +46,14 @@ public:
 
 
   class MenuDef {
   class MenuDef {
   public:
   public:
-    INLINE MenuDef(int thread_index, int collector_index,
-                   ChartType chart_type, bool show_level = false);
+    INLINE MenuDef(ChartType chart_type, int thread_index, int collector_index,
+                   int frame_number = -1, bool show_level = false);
     INLINE bool operator < (const MenuDef &other) const;
     INLINE bool operator < (const MenuDef &other) const;
 
 
+    ChartType _chart_type;
     int _thread_index;
     int _thread_index;
     int _collector_index;
     int _collector_index;
-    ChartType _chart_type;
+    int _frame_number;
     bool _show_level;
     bool _show_level;
     GtkStatsMonitor *_monitor;
     GtkStatsMonitor *_monitor;
   };
   };
@@ -84,7 +85,7 @@ public:
 
 
   PStatGraph *open_timeline();
   PStatGraph *open_timeline();
   PStatGraph *open_strip_chart(int thread_index, int collector_index, bool show_level);
   PStatGraph *open_strip_chart(int thread_index, int collector_index, bool show_level);
-  PStatGraph *open_flame_graph(int thread_index, int collector_index = -1);
+  PStatGraph *open_flame_graph(int thread_index, int collector_index = -1, int frame_number = -1);
   PStatGraph *open_piano_roll(int thread_index);
   PStatGraph *open_piano_roll(int thread_index);
 
 
   void choose_collector_color(int collector_index);
   void choose_collector_color(int collector_index);

+ 4 - 4
pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx

@@ -149,7 +149,7 @@ on_popup_label(int collector_index) {
 
 
   {
   {
     const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
     const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
-      _thread_index, collector_index, GtkStatsMonitor::CT_strip_chart, false,
+      GtkStatsMonitor::CT_strip_chart, _thread_index, collector_index,
     });
     });
 
 
     GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Strip Chart");
     GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Strip Chart");
@@ -161,7 +161,7 @@ on_popup_label(int collector_index) {
 
 
   {
   {
     const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
     const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
-      _thread_index, collector_index, GtkStatsMonitor::CT_flame_graph,
+      GtkStatsMonitor::CT_flame_graph, _thread_index, collector_index,
     });
     });
 
 
     GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Flame Graph");
     GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Flame Graph");
@@ -178,7 +178,7 @@ on_popup_label(int collector_index) {
 
 
   {
   {
     const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
     const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
-      -1, collector_index, GtkStatsMonitor::CT_choose_color,
+      GtkStatsMonitor::CT_choose_color, -1, collector_index,
     });
     });
 
 
     GtkWidget *menu_item = gtk_menu_item_new_with_label("Change Color...");
     GtkWidget *menu_item = gtk_menu_item_new_with_label("Change Color...");
@@ -190,7 +190,7 @@ on_popup_label(int collector_index) {
 
 
   {
   {
     const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
     const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
-      -1, collector_index, GtkStatsMonitor::CT_reset_color,
+      GtkStatsMonitor::CT_reset_color, -1, collector_index,
     });
     });
 
 
     GtkWidget *menu_item = gtk_menu_item_new_with_label("Reset Color");
     GtkWidget *menu_item = gtk_menu_item_new_with_label("Reset Color");

+ 5 - 5
pandatool/src/gtk-stats/gtkStatsStripChart.cxx

@@ -243,8 +243,8 @@ on_popup_label(int collector_index) {
 
 
   {
   {
     const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
     const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
-      _thread_index, collector_index,
-      GtkStatsMonitor::CT_strip_chart, get_view().get_show_level(),
+      GtkStatsMonitor::CT_strip_chart, _thread_index, collector_index, -1,
+      get_view().get_show_level(),
     });
     });
 
 
     GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Strip Chart");
     GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Strip Chart");
@@ -256,7 +256,7 @@ on_popup_label(int collector_index) {
 
 
   if (!get_view().get_show_level()) {
   if (!get_view().get_show_level()) {
     const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
     const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
-      _thread_index, collector_index, GtkStatsMonitor::CT_flame_graph,
+      GtkStatsMonitor::CT_flame_graph, _thread_index, collector_index,
     });
     });
 
 
     GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Flame Graph");
     GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Flame Graph");
@@ -273,7 +273,7 @@ on_popup_label(int collector_index) {
 
 
   {
   {
     const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
     const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
-      -1, collector_index, GtkStatsMonitor::CT_choose_color,
+      GtkStatsMonitor::CT_choose_color, -1, collector_index,
     });
     });
 
 
     GtkWidget *menu_item = gtk_menu_item_new_with_label("Change Color...");
     GtkWidget *menu_item = gtk_menu_item_new_with_label("Change Color...");
@@ -285,7 +285,7 @@ on_popup_label(int collector_index) {
 
 
   {
   {
     const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
     const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
-      -1, collector_index, GtkStatsMonitor::CT_reset_color,
+      GtkStatsMonitor::CT_reset_color, -1, collector_index,
     });
     });
 
 
     GtkWidget *menu_item = gtk_menu_item_new_with_label("Reset Color");
     GtkWidget *menu_item = gtk_menu_item_new_with_label("Reset Color");

+ 5 - 7
pandatool/src/gtk-stats/gtkStatsTimeline.cxx

@@ -409,8 +409,8 @@ handle_button_press(int graph_x, int graph_y, bool double_click, int button) {
 
 
         {
         {
           const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
           const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
+            GtkStatsMonitor::CT_strip_chart,
             bar._thread_index, bar._collector_index,
             bar._thread_index, bar._collector_index,
-            GtkStatsMonitor::CT_strip_chart, false,
           });
           });
 
 
           GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Strip Chart");
           GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Strip Chart");
@@ -422,8 +422,8 @@ handle_button_press(int graph_x, int graph_y, bool double_click, int button) {
 
 
         {
         {
           const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
           const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
-            bar._thread_index, bar._collector_index,
             GtkStatsMonitor::CT_flame_graph,
             GtkStatsMonitor::CT_flame_graph,
+            bar._thread_index, bar._collector_index, bar._frame_number,
           });
           });
 
 
           GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Flame Graph");
           GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Flame Graph");
@@ -435,7 +435,7 @@ handle_button_press(int graph_x, int graph_y, bool double_click, int button) {
 
 
         {
         {
           const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
           const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
-            bar._thread_index, -1, GtkStatsMonitor::CT_piano_roll,
+            GtkStatsMonitor::CT_piano_roll, bar._thread_index, -1,
           });
           });
 
 
           GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Piano Roll");
           GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Piano Roll");
@@ -452,8 +452,7 @@ handle_button_press(int graph_x, int graph_y, bool double_click, int button) {
 
 
         {
         {
           const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
           const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
-            -1, bar._collector_index,
-            GtkStatsMonitor::CT_choose_color,
+            GtkStatsMonitor::CT_choose_color, -1, bar._collector_index,
           });
           });
 
 
           GtkWidget *menu_item = gtk_menu_item_new_with_label("Change Color...");
           GtkWidget *menu_item = gtk_menu_item_new_with_label("Change Color...");
@@ -465,8 +464,7 @@ handle_button_press(int graph_x, int graph_y, bool double_click, int button) {
 
 
         {
         {
           const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
           const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
-            -1, bar._collector_index,
-            GtkStatsMonitor::CT_reset_color,
+            GtkStatsMonitor::CT_reset_color, -1, bar._collector_index,
           });
           });
 
 
           GtkWidget *menu_item = gtk_menu_item_new_with_label("Reset Color");
           GtkWidget *menu_item = gtk_menu_item_new_with_label("Reset Color");

+ 5 - 2
pandatool/src/mac-stats/macStatsFlameGraph.h

@@ -25,7 +25,7 @@
 class MacStatsFlameGraph final : public PStatFlameGraph, public MacStatsGraph {
 class MacStatsFlameGraph final : public PStatFlameGraph, public MacStatsGraph {
 public:
 public:
   MacStatsFlameGraph(MacStatsMonitor *monitor, int thread_index,
   MacStatsFlameGraph(MacStatsMonitor *monitor, int thread_index,
-                     int collector_index=-1);
+                     int collector_index=-1, int frame_number=-1);
   virtual ~MacStatsFlameGraph();
   virtual ~MacStatsFlameGraph();
 
 
   virtual void new_collector(int collector_index);
   virtual void new_collector(int collector_index);
@@ -60,11 +60,14 @@ protected:
   virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const;
   virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const;
   virtual DragMode consider_drag_start(int graph_x, int graph_y);
   virtual DragMode consider_drag_start(int graph_x, int graph_y);
 
 
+  virtual bool handle_key(int graph_x, int graph_y, bool pressed,
+                          UniChar c, unsigned short key_code);
   virtual void handle_button_press(int graph_x, int graph_y,
   virtual void handle_button_press(int graph_x, int graph_y,
-                                       bool double_click, int button);
+                                   bool double_click, int button);
   virtual void handle_button_release(int graph_x, int graph_y);
   virtual void handle_button_release(int graph_x, int graph_y);
   virtual void handle_motion(int graph_x, int graph_y);
   virtual void handle_motion(int graph_x, int graph_y);
   virtual void handle_leave();
   virtual void handle_leave();
+  virtual void handle_wheel(int graph_x, int graph_y, double dx, double dy);
   virtual void handle_draw_graph(CGContextRef ctx, NSRect rect);
   virtual void handle_draw_graph(CGContextRef ctx, NSRect rect);
   virtual void handle_back();
   virtual void handle_back();
 
 

+ 54 - 2
pandatool/src/mac-stats/macStatsFlameGraph.mm

@@ -29,8 +29,8 @@ static const int default_flame_graph_height = 250;
  */
  */
 MacStatsFlameGraph::
 MacStatsFlameGraph::
 MacStatsFlameGraph(MacStatsMonitor *monitor, int thread_index,
 MacStatsFlameGraph(MacStatsMonitor *monitor, int thread_index,
-                   int collector_index) :
-  PStatFlameGraph(monitor, thread_index, collector_index, 0, 0),
+                   int collector_index, int frame_number) :
+  PStatFlameGraph(monitor, thread_index, collector_index, frame_number, 0, 0),
   MacStatsGraph(monitor, [MacStatsFlameGraphViewController alloc])
   MacStatsGraph(monitor, [MacStatsFlameGraphViewController alloc])
 {
 {
   // Used for popup menus.
   // Used for popup menus.
@@ -538,6 +538,43 @@ consider_drag_start(int graph_x, int graph_y) {
   return DM_none;
   return DM_none;
 }
 }
 
 
+/**
+ *
+ */
+bool MacStatsFlameGraph::
+handle_key(int graph_x, int graph_y, bool pressed, UniChar c, unsigned short key_code) {
+  bool changed = false;
+
+  if (pressed) {
+    switch (c) {
+    case NSLeftArrowFunctionKey:
+      changed = prev_frame();
+      break;
+
+    case NSRightArrowFunctionKey:
+      changed = next_frame();
+      break;
+
+    case NSHomeFunctionKey:
+      changed = first_frame();
+      break;
+
+    case NSEndFunctionKey:
+      changed = last_frame();
+      break;
+    }
+  }
+
+  if (changed) {
+    std::string window_title = get_title_text();
+    if (!is_title_unknown()) {
+      _window.title = [NSString stringWithUTF8String:window_title.c_str()];
+    }
+  }
+
+  return changed;
+}
+
 /**
 /**
  * Called when the mouse button is depressed within the graph window.
  * Called when the mouse button is depressed within the graph window.
  */
  */
@@ -652,6 +689,21 @@ handle_leave() {
   return;
   return;
 }
 }
 
 
+/**
+ *
+ */
+void MacStatsFlameGraph::
+handle_wheel(int graph_x, int graph_y, double dx, double dy) {
+  if (dx != 0.0) {
+    if ((dx > 0.0) ? prev_frame() : next_frame()) {
+      std::string window_title = get_title_text();
+      if (!is_title_unknown()) {
+        _window.title = [NSString stringWithUTF8String:window_title.c_str()];
+      }
+    }
+  }
+}
+
 /**
 /**
  * Fills in the graph window.
  * Fills in the graph window.
  */
  */

+ 6 - 8
pandatool/src/mac-stats/macStatsGraphView.mm

@@ -45,15 +45,13 @@
 }
 }
 
 
 - (void)keyDown:(NSEvent *)event {
 - (void)keyDown:(NSEvent *)event {
-  if (!event.isARepeat) {
-    NSPoint pos = [self convertPoint:event.locationInWindow fromView:nil];
-    UniChar c = 0;
-    NSString *str = [event charactersIgnoringModifiers];
-    if (str != nil && str.length == 1) {
-      c = [str characterAtIndex:0];
-    }
-    _graph->handle_key(pos.x, pos.y, true, c, event.keyCode);
+  NSPoint pos = [self convertPoint:event.locationInWindow fromView:nil];
+  UniChar c = 0;
+  NSString *str = [event charactersIgnoringModifiers];
+  if (str != nil && str.length == 1) {
+    c = [str characterAtIndex:0];
   }
   }
+  _graph->handle_key(pos.x, pos.y, true, c, event.keyCode);
 }
 }
 
 
 - (void)keyUp:(NSEvent *)event {
 - (void)keyUp:(NSEvent *)event {

+ 1 - 1
pandatool/src/mac-stats/macStatsMonitor.h

@@ -57,7 +57,7 @@ public:
 
 
   PStatGraph *open_timeline();
   PStatGraph *open_timeline();
   PStatGraph *open_strip_chart(int thread_index, int collector_index, bool show_level);
   PStatGraph *open_strip_chart(int thread_index, int collector_index, bool show_level);
-  PStatGraph *open_flame_graph(int thread_index, int collector_index = -1);
+  PStatGraph *open_flame_graph(int thread_index, int collector_index = -1, int frame_number = -1);
   PStatGraph *open_piano_roll(int thread_index);
   PStatGraph *open_piano_roll(int thread_index);
 
 
   CGColorRef get_collector_color(int collector_index, bool highlight = false);
   CGColorRef get_collector_color(int collector_index, bool highlight = false);

+ 2 - 2
pandatool/src/mac-stats/macStatsMonitor.mm

@@ -312,9 +312,9 @@ open_timeline() {
  * Opens a new flame graph showing the indicated data.
  * Opens a new flame graph showing the indicated data.
  */
  */
 PStatGraph *MacStatsMonitor::
 PStatGraph *MacStatsMonitor::
-open_flame_graph(int thread_index, int collector_index) {
+open_flame_graph(int thread_index, int collector_index, int frame_number) {
   MacStatsFlameGraph *graph =
   MacStatsFlameGraph *graph =
-    new MacStatsFlameGraph(this, thread_index, collector_index);
+    new MacStatsFlameGraph(this, thread_index, collector_index, frame_number);
   add_graph(graph);
   add_graph(graph);
   return graph;
   return graph;
 }
 }

+ 1 - 1
pandatool/src/mac-stats/macStatsTimeline.mm

@@ -819,7 +819,7 @@ handle_open_strip_chart() {
 void MacStatsTimeline::
 void MacStatsTimeline::
 handle_open_flame_graph() {
 handle_open_flame_graph() {
   const ColorBar &bar = _popup_bar;
   const ColorBar &bar = _popup_bar;
-  MacStatsGraph::_monitor->open_flame_graph(bar._thread_index, bar._collector_index);
+  MacStatsGraph::_monitor->open_flame_graph(bar._thread_index, bar._collector_index, bar._frame_number);
 }
 }
 
 
 /**
 /**

+ 9 - 0
pandatool/src/pstatserver/pStatFlameGraph.I

@@ -27,6 +27,15 @@ get_collector_index() const {
   return _collector_index;
   return _collector_index;
 }
 }
 
 
+/**
+ * Returns the particular frame number whose data this flame graph reflects.
+ * Returns -1 if we're looking at a moving average instead.
+ */
+INLINE int PStatFlameGraph::
+get_frame_number() const {
+  return _frame_number;
+}
+
 /**
 /**
  * Returns the amount of total time the width of the horizontal axis
  * Returns the amount of total time the width of the horizontal axis
  * represents.
  * represents.

+ 157 - 7
pandatool/src/pstatserver/pStatFlameGraph.cxx

@@ -25,12 +25,13 @@
  *
  *
  */
  */
 PStatFlameGraph::
 PStatFlameGraph::
-PStatFlameGraph(PStatMonitor *monitor,
-                int thread_index, int collector_index, int xsize, int ysize) :
+PStatFlameGraph(PStatMonitor *monitor, int thread_index, int collector_index,
+                int frame_number, int xsize, int ysize) :
   PStatGraph(monitor, xsize, ysize),
   PStatGraph(monitor, xsize, ysize),
   _thread_index(thread_index),
   _thread_index(thread_index),
   _collector_index(collector_index),
   _collector_index(collector_index),
-  _orig_collector_index(collector_index)
+  _orig_collector_index(collector_index),
+  _frame_number(frame_number)
 {
 {
   _average_mode = true;
   _average_mode = true;
   _average_cursor = 0;
   _average_cursor = 0;
@@ -73,11 +74,20 @@ update() {
     const PStatThreadData *thread_data =
     const PStatThreadData *thread_data =
       client_data->get_thread_data(_thread_index);
       client_data->get_thread_data(_thread_index);
     if (!thread_data->is_empty()) {
     if (!thread_data->is_empty()) {
-      int frame_number = thread_data->get_latest_frame_number();
-      if (frame_number != _current_frame) {
-        _current_frame = frame_number;
+      if (_frame_number >= 0) {
+        if (thread_data->has_frame(_frame_number)) {
+          if (_current_frame != _frame_number) {
+            _current_frame = _frame_number;
+            update_data();
+          }
+        }
+      } else {
+        int frame_number = thread_data->get_latest_frame_number();
+        if (frame_number != _current_frame) {
+          _current_frame = frame_number;
 
 
-        update_data();
+          update_data();
+        }
       }
       }
     }
     }
   }
   }
@@ -117,6 +127,142 @@ set_collector_index(int collector_index) {
   }
   }
 }
 }
 
 
+/**
+ * Changes the frame number shown by this flame graph.  This may force a redraw.
+ */
+void PStatFlameGraph::
+set_frame_number(int frame_number) {
+  if (_frame_number != frame_number) {
+    _frame_number = frame_number;
+    _current_frame = frame_number;
+    _title_unknown = true;
+    _stack.clear();
+    update_data();
+
+    if (_average_mode) {
+      _stack.update_averages(_average_cursor);
+      _time_width = _stack.get_net_value(true);
+      if (_time_width == 0.0) {
+        _time_width = 1.0 / pstats_target_frame_rate;
+      }
+      normal_guide_bars();
+    }
+  }
+}
+
+/**
+ * Sets the frame number to the oldest available frame.
+ */
+bool PStatFlameGraph::
+first_frame() {
+  const PStatClientData *client_data = _monitor->get_client_data();
+  if (client_data == nullptr) {
+    return false;
+  }
+
+  const PStatThreadData *thread_data = client_data->get_thread_data(_thread_index);
+  if (thread_data == nullptr || thread_data->is_empty()) {
+    return false;
+  }
+
+  int oldest = thread_data->get_oldest_frame_number();
+  if (_frame_number != oldest && thread_data->has_frame(oldest)) {
+    set_frame_number(oldest);
+    return true;
+  }
+  return false;
+}
+
+/**
+ * Advances to the next available frame.  Returns true if the frame number was
+ * changed after a call to this method.
+ */
+bool PStatFlameGraph::
+next_frame() {
+  const PStatClientData *client_data = _monitor->get_client_data();
+  if (client_data == nullptr) {
+    return false;
+  }
+
+  const PStatThreadData *thread_data = client_data->get_thread_data(_thread_index);
+  if (thread_data == nullptr || thread_data->is_empty()) {
+    return false;
+  }
+
+  int latest = thread_data->get_latest_frame_number();
+  if (_frame_number < 0 || _frame_number > latest) {
+    set_frame_number(latest);
+    return true;
+  }
+
+  for (int i = _frame_number + 1; i <= latest; ++i) {
+    if (thread_data->has_frame(i)) {
+      set_frame_number(i);
+      return true;
+    }
+  }
+
+  return false;
+}
+
+/**
+ * Reverts to the previous frame.  Returns true if the frame number was changed
+ * after a call to this method.
+ */
+bool PStatFlameGraph::
+prev_frame() {
+  const PStatClientData *client_data = _monitor->get_client_data();
+  if (client_data == nullptr) {
+    return false;
+  }
+
+  const PStatThreadData *thread_data = client_data->get_thread_data(_thread_index);
+  if (thread_data == nullptr || thread_data->is_empty()) {
+    return false;
+  }
+
+  int oldest = thread_data->get_oldest_frame_number();
+
+  int i;
+  if (_frame_number < 0) {
+    i = thread_data->get_latest_frame_number();
+  } else {
+    i = _frame_number - 1;
+  }
+  while (i >= oldest) {
+    if (thread_data->has_frame(i)) {
+      set_frame_number(i);
+      return true;
+    }
+    --i;
+  }
+
+  return false;
+}
+
+/**
+ * Sets the frame number to the latest available frame.
+ */
+bool PStatFlameGraph::
+last_frame() {
+  const PStatClientData *client_data = _monitor->get_client_data();
+  if (client_data == nullptr) {
+    return false;
+  }
+
+  const PStatThreadData *thread_data = client_data->get_thread_data(_thread_index);
+  if (thread_data == nullptr || thread_data->is_empty()) {
+    return false;
+  }
+
+  int latest = thread_data->get_latest_frame_number();
+  if (_frame_number != latest && thread_data->has_frame(latest)) {
+    set_frame_number(latest);
+    return true;
+  }
+  return false;
+}
+
 /**
 /**
  * Returns the text suitable for the title label on the top line.
  * Returns the text suitable for the title label on the top line.
  */
  */
@@ -150,6 +296,10 @@ get_title_text() {
     _title_unknown = true;
     _title_unknown = true;
   }
   }
 
 
+  if (_frame_number >= 0) {
+    text += " (frame " + format_string(_frame_number) + ")";
+  }
+
   return text;
   return text;
 }
 }
 
 

+ 9 - 1
pandatool/src/pstatserver/pStatFlameGraph.h

@@ -36,7 +36,7 @@ class PStatFrameData;
 class PStatFlameGraph : public PStatGraph {
 class PStatFlameGraph : public PStatGraph {
 public:
 public:
   PStatFlameGraph(PStatMonitor *monitor,
   PStatFlameGraph(PStatMonitor *monitor,
-                  int thread_index, int collector_index,
+                  int thread_index, int collector_index, int frame_number,
                   int xsize, int ysize);
                   int xsize, int ysize);
   virtual ~PStatFlameGraph();
   virtual ~PStatFlameGraph();
 
 
@@ -46,6 +46,13 @@ public:
   INLINE int get_collector_index() const;
   INLINE int get_collector_index() const;
   void set_collector_index(int collector_index);
   void set_collector_index(int collector_index);
 
 
+  INLINE int get_frame_number() const;
+  void set_frame_number(int collector_index);
+  bool first_frame();
+  bool next_frame();
+  bool prev_frame();
+  bool last_frame();
+
   INLINE double get_horizontal_scale() const;
   INLINE double get_horizontal_scale() const;
 
 
   INLINE void set_average_mode(bool average_mode);
   INLINE void set_average_mode(bool average_mode);
@@ -123,6 +130,7 @@ private:
   int _thread_index;
   int _thread_index;
   int _collector_index;
   int _collector_index;
   int _orig_collector_index;
   int _orig_collector_index;
+  int _frame_number;
   bool _average_mode;
   bool _average_mode;
   size_t _average_cursor;
   size_t _average_cursor;
 
 

+ 1 - 1
pandatool/src/pstatserver/pStatMonitor.cxx

@@ -654,7 +654,7 @@ open_strip_chart(int thread_index, int collector_index, bool show_level) {
  * Opens a new flame graph showing the indicated data.
  * Opens a new flame graph showing the indicated data.
  */
  */
 PStatGraph *PStatMonitor::
 PStatGraph *PStatMonitor::
-open_flame_graph(int thread_index, int collector_index) {
+open_flame_graph(int thread_index, int collector_index, int frame_number) {
   return nullptr;
   return nullptr;
 }
 }
 
 

+ 1 - 1
pandatool/src/pstatserver/pStatMonitor.h

@@ -104,7 +104,7 @@ public:
 
 
   virtual PStatGraph *open_timeline();
   virtual PStatGraph *open_timeline();
   virtual PStatGraph *open_strip_chart(int thread_index, int collector_index, bool show_level);
   virtual PStatGraph *open_strip_chart(int thread_index, int collector_index, bool show_level);
-  virtual PStatGraph *open_flame_graph(int thread_index, int collector_index = -1);
+  virtual PStatGraph *open_flame_graph(int thread_index, int collector_index = -1, int frame_number = -1);
   virtual PStatGraph *open_piano_roll(int thread_index);
   virtual PStatGraph *open_piano_roll(int thread_index);
 
 
   void write_datagram(Datagram &dg) const;
   void write_datagram(Datagram &dg) const;

+ 44 - 2
pandatool/src/win-stats/winStatsFlameGraph.cxx

@@ -29,9 +29,9 @@ const char * const WinStatsFlameGraph::_window_class_name = "flame";
  */
  */
 WinStatsFlameGraph::
 WinStatsFlameGraph::
 WinStatsFlameGraph(WinStatsMonitor *monitor, int thread_index,
 WinStatsFlameGraph(WinStatsMonitor *monitor, int thread_index,
-                   int collector_index) :
+                   int collector_index, int frame_number) :
   PStatFlameGraph(monitor,
   PStatFlameGraph(monitor,
-                  thread_index, collector_index,
+                  thread_index, collector_index, frame_number,
                   monitor->get_pixel_scale() * default_flame_graph_width / 4,
                   monitor->get_pixel_scale() * default_flame_graph_width / 4,
                   monitor->get_pixel_scale() * default_flame_graph_height / 4),
                   monitor->get_pixel_scale() * default_flame_graph_height / 4),
   WinStatsGraph(monitor)
   WinStatsGraph(monitor)
@@ -349,6 +349,7 @@ LONG WinStatsFlameGraph::
 window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
 window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
   switch (msg) {
   switch (msg) {
   case WM_LBUTTONDOWN:
   case WM_LBUTTONDOWN:
+    SetFocus(_window);
     if (_potential_drag_mode == DM_new_guide_bar) {
     if (_potential_drag_mode == DM_new_guide_bar) {
       set_drag_mode(DM_new_guide_bar);
       set_drag_mode(DM_new_guide_bar);
       SetCapture(_graph_window);
       SetCapture(_graph_window);
@@ -393,6 +394,31 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
     }
     }
     break;
     break;
 
 
+  case WM_KEYDOWN:
+    {
+      bool changed = false;
+      switch (wparam) {
+      case VK_LEFT:
+        changed = prev_frame();
+        break;
+      case VK_RIGHT:
+        changed = next_frame();
+        break;
+      case VK_HOME:
+        changed = first_frame();
+        break;
+      case VK_END:
+        changed = last_frame();
+        break;
+      }
+      if (changed) {
+        std::string window_title = get_title_text();
+        SetWindowText(_window, window_title.c_str());
+        return 0;
+      }
+    }
+    break;
+
   default:
   default:
     break;
     break;
   }
   }
@@ -407,6 +433,7 @@ LONG WinStatsFlameGraph::
 graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
 graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
   switch (msg) {
   switch (msg) {
   case WM_LBUTTONDOWN:
   case WM_LBUTTONDOWN:
+    SetFocus(_window);
     if (_potential_drag_mode == DM_guide_bar && _drag_guide_bar >= 0) {
     if (_potential_drag_mode == DM_guide_bar && _drag_guide_bar >= 0) {
       set_drag_mode(DM_guide_bar);
       set_drag_mode(DM_guide_bar);
       int16_t x = LOWORD(lparam);
       int16_t x = LOWORD(lparam);
@@ -518,6 +545,19 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
     }
     }
     break;
     break;
 
 
+  case WM_MOUSEHWHEEL:
+    {
+      int delta = GET_WHEEL_DELTA_WPARAM(wparam);
+      if (delta != 0) {
+        if (delta > 0 ? next_frame() : prev_frame()) {
+          std::string window_title = get_title_text();
+          SetWindowText(_window, window_title.c_str());
+        }
+      }
+      return 0;
+    }
+    break;
+
   default:
   default:
     break;
     break;
   }
   }
@@ -760,6 +800,8 @@ create_window() {
   // Ensure that the window is on top of the stack.
   // Ensure that the window is on top of the stack.
   SetWindowPos(_window, HWND_TOP, 0, 0, 0, 0,
   SetWindowPos(_window, HWND_TOP, 0, 0, 0, 0,
                SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
                SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
+
+  SetFocus(_window);
 }
 }
 
 
 /**
 /**

+ 1 - 1
pandatool/src/win-stats/winStatsFlameGraph.h

@@ -28,7 +28,7 @@ class WinStatsLabel;
 class WinStatsFlameGraph : public PStatFlameGraph, public WinStatsGraph {
 class WinStatsFlameGraph : public PStatFlameGraph, public WinStatsGraph {
 public:
 public:
   WinStatsFlameGraph(WinStatsMonitor *monitor, int thread_index,
   WinStatsFlameGraph(WinStatsMonitor *monitor, int thread_index,
-                     int collector_index=-1);
+                     int collector_index=-1, int frame_number=-1);
   virtual ~WinStatsFlameGraph();
   virtual ~WinStatsFlameGraph();
 
 
   virtual void new_data(int thread_index, int frame_number);
   virtual void new_data(int thread_index, int frame_number);

+ 2 - 2
pandatool/src/win-stats/winStatsMonitor.cxx

@@ -332,8 +332,8 @@ open_strip_chart(int thread_index, int collector_index, bool show_level) {
  * Opens a new flame graph showing the indicated data.
  * Opens a new flame graph showing the indicated data.
  */
  */
 PStatGraph *WinStatsMonitor::
 PStatGraph *WinStatsMonitor::
-open_flame_graph(int thread_index, int collector_index) {
-  WinStatsFlameGraph *graph = new WinStatsFlameGraph(this, thread_index, collector_index);
+open_flame_graph(int thread_index, int collector_index, int frame_number) {
+  WinStatsFlameGraph *graph = new WinStatsFlameGraph(this, thread_index, collector_index, frame_number);
   add_graph(graph);
   add_graph(graph);
   return graph;
   return graph;
 }
 }

+ 1 - 1
pandatool/src/win-stats/winStatsMonitor.h

@@ -84,7 +84,7 @@ public:
 
 
   PStatGraph *open_timeline();
   PStatGraph *open_timeline();
   PStatGraph *open_strip_chart(int thread_index, int collector_index, bool show_level);
   PStatGraph *open_strip_chart(int thread_index, int collector_index, bool show_level);
-  PStatGraph *open_flame_graph(int thread_index, int collector_index = -1);
+  PStatGraph *open_flame_graph(int thread_index, int collector_index = -1, int frame_number = -1);
   PStatGraph *open_piano_roll(int thread_index);
   PStatGraph *open_piano_roll(int thread_index);
 
 
   void choose_collector_color(int collector_index);
   void choose_collector_color(int collector_index);

+ 1 - 1
pandatool/src/win-stats/winStatsTimeline.cxx

@@ -463,7 +463,7 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
       return 0;
       return 0;
 
 
     case 103:
     case 103:
-      WinStatsGraph::_monitor->open_flame_graph(_popup_bar._thread_index, _popup_bar._collector_index);
+      WinStatsGraph::_monitor->open_flame_graph(_popup_bar._thread_index, _popup_bar._collector_index, _popup_bar._frame_number);
       return 0;
       return 0;
 
 
     case 104:
     case 104: