pstats: Third significant update to PStats server UI, including:
* Windows stay open after client disconnects, for further inspection * Ability to save session results to a file, and reopening those files * Ability to save the current graph window layout for new sessions * Ability to change colors (by right-clicking on bar) * SI prefixes for Hz units (kHz, MHz, etc.) * Ability to export session to Chrome Tracing JSON format * "Close All Graphs" menu option * Graphs now properly show data when opened while Pause is on * Some fixes for weird graph window minimize behavior on Windows
This commit is contained in:
parent
2a0da59dae
commit
bb6976d558
|
|
@ -613,6 +613,7 @@ if (COMPILER == "MSVC"):
|
|||
LibName("WINSOCK2", "ws2_32.lib")
|
||||
LibName("WINCOMCTL", "comctl32.lib")
|
||||
LibName("WINCOMDLG", "comdlg32.lib")
|
||||
LibName("UXTHEME", "uxtheme.lib")
|
||||
LibName("WINUSER", "user32.lib")
|
||||
LibName("WINMM", "winmm.lib")
|
||||
LibName("WINIMM", "imm32.lib")
|
||||
|
|
@ -5910,7 +5911,7 @@ if not PkgSkip("PANDATOOL") and (GetTarget() == 'windows' or not PkgSkip("GTK3")
|
|||
TargetAdd('pstats.exe', input='libp3progbase.lib')
|
||||
TargetAdd('pstats.exe', input='libp3pandatoolbase.lib')
|
||||
TargetAdd('pstats.exe', input=COMMON_PANDA_LIBS)
|
||||
TargetAdd('pstats.exe', opts=['SUBSYSTEM:WINDOWS', 'WINCOMCTL', 'WINSOCK', 'WINIMM', 'WINGDI', 'WINKERNEL', 'WINOLDNAMES', 'WINUSER', 'WINMM', 'GTK3'])
|
||||
TargetAdd('pstats.exe', opts=['SUBSYSTEM:WINDOWS', 'WINCOMCTL', 'WINCOMDLG', 'WINSOCK', 'WINIMM', 'WINGDI', 'WINKERNEL', 'WINOLDNAMES', 'WINUSER', 'WINMM', 'UXTHEME', 'GTK3'])
|
||||
|
||||
#
|
||||
# DIRECTORY: pandatool/src/xfileprogs/
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@ public:
|
|||
void set_parent(const PStatCollectorDef &parent);
|
||||
|
||||
void write_datagram(Datagram &destination) const;
|
||||
void read_datagram(DatagramIterator &source, PStatClientVersion *version);
|
||||
void read_datagram(DatagramIterator &source,
|
||||
PStatClientVersion *version = nullptr);
|
||||
|
||||
struct ColorDef {
|
||||
float r, g, b;
|
||||
|
|
|
|||
|
|
@ -81,5 +81,5 @@ read_datagram(DatagramIterator &source, PStatClientVersion *) {
|
|||
dp._value = source.get_float32();
|
||||
_level_data.push_back(dp);
|
||||
}
|
||||
nassertv(source.get_remaining_size() == 0);
|
||||
//nassertv(source.get_remaining_size() == 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,8 +57,8 @@ public:
|
|||
INLINE int get_level_collector(size_t n) const;
|
||||
INLINE double get_level(size_t n) const;
|
||||
|
||||
bool write_datagram(Datagram &destination, PStatClient *client) const;
|
||||
void read_datagram(DatagramIterator &source, PStatClientVersion *version);
|
||||
bool write_datagram(Datagram &destination, PStatClient *client = nullptr) const;
|
||||
void read_datagram(DatagramIterator &source, PStatClientVersion *version = nullptr);
|
||||
|
||||
private:
|
||||
class DataPoint {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ set(GTKSTATS_HEADERS
|
|||
gtkStatsGraph.h
|
||||
gtkStatsLabel.h
|
||||
gtkStatsLabelStack.h
|
||||
gtkStatsMenuId.h
|
||||
gtkStatsMonitor.h gtkStatsMonitor.I
|
||||
gtkStatsPianoRoll.h
|
||||
gtkStatsServer.h
|
||||
|
|
|
|||
|
|
@ -16,88 +16,13 @@
|
|||
#include "gtkStatsServer.h"
|
||||
#include "config_pstatclient.h"
|
||||
|
||||
GtkWidget *main_window;
|
||||
static GtkStatsServer *server = nullptr;
|
||||
|
||||
static gboolean
|
||||
delete_event(GtkWidget *widget,
|
||||
GdkEvent *event, gpointer data) {
|
||||
// Returning FALSE to indicate we should destroy the main window when the
|
||||
// user selects "close".
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
destroy(GtkWidget *widget, gpointer data) {
|
||||
gtk_main_quit();
|
||||
}
|
||||
|
||||
static gboolean
|
||||
timer(gpointer data) {
|
||||
static int count = 0;
|
||||
server->poll();
|
||||
|
||||
if (++count == 5) {
|
||||
count = 0;
|
||||
// Every once in a while, say once a second, we call this function, which
|
||||
// should force gdk to make all changes visible. We do this in case we
|
||||
// are getting starved and falling behind, so that the user still gets a
|
||||
// chance to see *something* happen onscreen, even if it's just
|
||||
// increasingly old data.
|
||||
//gdk_window_process_all_updates();
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[]) {
|
||||
gtk_init(&argc, &argv);
|
||||
|
||||
main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
|
||||
gtk_window_set_title(GTK_WINDOW(main_window), "PStats");
|
||||
|
||||
// Connect the delete and destroy events, so the user can exit the
|
||||
// application by closing the main window.
|
||||
g_signal_connect(G_OBJECT(main_window), "delete_event",
|
||||
G_CALLBACK(delete_event), nullptr);
|
||||
|
||||
g_signal_connect(G_OBJECT(main_window), "destroy",
|
||||
G_CALLBACK(destroy), nullptr);
|
||||
|
||||
std::ostringstream stream;
|
||||
stream << "Listening on port " << pstats_port;
|
||||
std::string str = stream.str();
|
||||
GtkWidget *label = gtk_label_new(str.c_str());
|
||||
gtk_container_add(GTK_CONTAINER(main_window), label);
|
||||
gtk_widget_show(label);
|
||||
|
||||
// Create the server object.
|
||||
server = new GtkStatsServer;
|
||||
if (!server->listen()) {
|
||||
std::ostringstream stream;
|
||||
stream
|
||||
<< "Unable to open port " << pstats_port
|
||||
<< ". Try specifying a different\n"
|
||||
<< "port number using pstats-port in your Config file.";
|
||||
std::string str = stream.str();
|
||||
|
||||
GtkWidget *dialog =
|
||||
gtk_message_dialog_new(GTK_WINDOW(main_window),
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||
GTK_MESSAGE_ERROR,
|
||||
GTK_BUTTONS_CLOSE,
|
||||
"%s", str.c_str());
|
||||
gtk_dialog_run(GTK_DIALOG(dialog));
|
||||
gtk_widget_destroy(dialog);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
gtk_widget_show(main_window);
|
||||
|
||||
// Set up a timer to poll the pstats every so often.
|
||||
g_timeout_add(200, timer, nullptr);
|
||||
// Create the server window.
|
||||
GtkStatsServer *server = new GtkStatsServer;
|
||||
server->new_session();
|
||||
|
||||
// Now get lost in the message loop.
|
||||
gtk_main();
|
||||
|
|
|
|||
|
|
@ -16,8 +16,4 @@
|
|||
|
||||
#include "pStatServer.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
extern GtkWidget *main_window;
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -56,11 +56,19 @@ add_to_menu_bar(GtkWidget *menu_bar, int position) {
|
|||
thread_name = client_data->get_thread_name(_thread_index);
|
||||
}
|
||||
|
||||
GtkWidget *menu_item = gtk_menu_item_new_with_label(thread_name.c_str());
|
||||
gtk_widget_show(menu_item);
|
||||
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), _menu);
|
||||
_menu_item = gtk_menu_item_new_with_label(thread_name.c_str());
|
||||
gtk_widget_show(_menu_item);
|
||||
gtk_menu_item_set_submenu(GTK_MENU_ITEM(_menu_item), _menu);
|
||||
|
||||
gtk_menu_shell_insert(GTK_MENU_SHELL(menu_bar), menu_item, position);
|
||||
gtk_menu_shell_insert(GTK_MENU_SHELL(menu_bar), _menu_item, position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the menu from the menu bar.
|
||||
*/
|
||||
void GtkStatsChartMenu::
|
||||
remove_from_menu_bar(GtkWidget *menu_bar) {
|
||||
gtk_container_remove(GTK_CONTAINER(menu_bar), _menu_item);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -90,16 +98,32 @@ do_update() {
|
|||
|
||||
if (_thread_index == 0) {
|
||||
// Timeline goes first.
|
||||
GtkStatsMonitor::MenuDef smd(_thread_index, -1, GtkStatsMonitor::CT_timeline, false);
|
||||
const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd);
|
||||
{
|
||||
GtkStatsMonitor::MenuDef smd(_thread_index, -1, GtkStatsMonitor::CT_timeline, false);
|
||||
const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd);
|
||||
|
||||
GtkWidget *menu_item = gtk_menu_item_new_with_label("Timeline");
|
||||
gtk_widget_show(menu_item);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_menu), menu_item);
|
||||
GtkWidget *menu_item = gtk_menu_item_new_with_label("Timeline");
|
||||
gtk_widget_show(menu_item);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_menu), menu_item);
|
||||
|
||||
g_signal_connect(G_OBJECT(menu_item), "activate",
|
||||
G_CALLBACK(GtkStatsMonitor::menu_activate),
|
||||
(void *)menu_def);
|
||||
g_signal_connect(G_OBJECT(menu_item), "activate",
|
||||
G_CALLBACK(GtkStatsMonitor::menu_activate),
|
||||
(void *)menu_def);
|
||||
}
|
||||
|
||||
// And the piano roll (even though it's not very useful nowadays)
|
||||
{
|
||||
GtkStatsMonitor::MenuDef smd(_thread_index, -1, GtkStatsMonitor::CT_piano_roll, false);
|
||||
const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd);
|
||||
|
||||
GtkWidget *menu_item = gtk_menu_item_new_with_label("Piano Roll");
|
||||
gtk_widget_show(menu_item);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_menu), menu_item);
|
||||
|
||||
g_signal_connect(G_OBJECT(menu_item), "activate",
|
||||
G_CALLBACK(GtkStatsMonitor::menu_activate),
|
||||
(void *)menu_def);
|
||||
}
|
||||
|
||||
GtkWidget *sep = gtk_separator_menu_item_new();
|
||||
gtk_widget_show(sep);
|
||||
|
|
@ -134,22 +158,41 @@ do_update() {
|
|||
}
|
||||
}
|
||||
|
||||
// Also menu item for piano roll (following a separator).
|
||||
GtkWidget *sep = gtk_separator_menu_item_new();
|
||||
gtk_widget_show(sep);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_menu), sep);
|
||||
// For the main thread menu, also some options relating to all graph windows.
|
||||
if (_thread_index == 0) {
|
||||
GtkWidget *sep = gtk_separator_menu_item_new();
|
||||
gtk_widget_show(sep);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_menu), sep);
|
||||
|
||||
{
|
||||
GtkStatsMonitor::MenuDef smd(_thread_index, -1, GtkStatsMonitor::CT_piano_roll, false);
|
||||
const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd);
|
||||
{
|
||||
GtkWidget *menu_item = gtk_menu_item_new_with_label("Close All Graphs");
|
||||
gtk_widget_show(menu_item);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_menu), menu_item);
|
||||
|
||||
GtkWidget *menu_item = gtk_menu_item_new_with_label("Piano Roll");
|
||||
gtk_widget_show(menu_item);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_menu), menu_item);
|
||||
g_signal_connect(G_OBJECT(menu_item), "activate",
|
||||
G_CALLBACK(activate_close_all),
|
||||
(void *)_monitor);
|
||||
}
|
||||
|
||||
g_signal_connect(G_OBJECT(menu_item), "activate",
|
||||
G_CALLBACK(GtkStatsMonitor::menu_activate),
|
||||
(void *)menu_def);
|
||||
{
|
||||
GtkWidget *menu_item = gtk_menu_item_new_with_label("Reopen Default Graphs");
|
||||
gtk_widget_show(menu_item);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_menu), menu_item);
|
||||
|
||||
g_signal_connect(G_OBJECT(menu_item), "activate",
|
||||
G_CALLBACK(activate_reopen_default),
|
||||
(void *)_monitor);
|
||||
}
|
||||
|
||||
{
|
||||
GtkWidget *menu_item = gtk_menu_item_new_with_label("Save Current Layout as Default");
|
||||
gtk_widget_show(menu_item);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_menu), menu_item);
|
||||
|
||||
g_signal_connect(G_OBJECT(menu_item), "activate",
|
||||
G_CALLBACK(activate_save_default),
|
||||
(void *)_monitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -250,3 +293,31 @@ remove_menu_child(GtkWidget *widget, gpointer data) {
|
|||
GtkWidget *menu = (GtkWidget *)data;
|
||||
gtk_container_remove(GTK_CONTAINER(menu), widget);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for Close All Graphs.
|
||||
*/
|
||||
void GtkStatsChartMenu::
|
||||
activate_close_all(GtkWidget *widget, gpointer data) {
|
||||
GtkStatsMonitor *monitor = (GtkStatsMonitor *)data;
|
||||
monitor->remove_all_graphs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for Reopen Default Graphs.
|
||||
*/
|
||||
void GtkStatsChartMenu::
|
||||
activate_reopen_default(GtkWidget *widget, gpointer data) {
|
||||
GtkStatsMonitor *monitor = (GtkStatsMonitor *)data;
|
||||
monitor->remove_all_graphs();
|
||||
monitor->open_default_graphs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for Save Current Layout as Default.
|
||||
*/
|
||||
void GtkStatsChartMenu::
|
||||
activate_save_default(GtkWidget *widget, gpointer data) {
|
||||
GtkStatsMonitor *monitor = (GtkStatsMonitor *)data;
|
||||
monitor->save_default_graphs();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ public:
|
|||
|
||||
GtkWidget *get_menu_widget();
|
||||
void add_to_menu_bar(GtkWidget *menu_bar, int position);
|
||||
void remove_from_menu_bar(GtkWidget *menu_bar);
|
||||
|
||||
void check_update();
|
||||
void do_update();
|
||||
|
|
@ -41,12 +42,16 @@ private:
|
|||
bool show_level);
|
||||
|
||||
static void remove_menu_child(GtkWidget *widget, gpointer data);
|
||||
static void activate_close_all(GtkWidget *widget, gpointer data);
|
||||
static void activate_reopen_default(GtkWidget *widget, gpointer data);
|
||||
static void activate_save_default(GtkWidget *widget, gpointer data);
|
||||
|
||||
GtkStatsMonitor *_monitor;
|
||||
int _thread_index;
|
||||
|
||||
int _last_level_index;
|
||||
GtkWidget *_menu;
|
||||
GtkWidget *_menu_item = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -306,6 +306,25 @@ animate(double time, double dt) {
|
|||
return PStatFlameGraph::animate(time, dt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current window dimensions.
|
||||
*/
|
||||
bool GtkStatsFlameGraph::
|
||||
get_window_state(int &x, int &y, int &width, int &height,
|
||||
bool &maximized, bool &minimized) const {
|
||||
GtkStatsGraph::get_window_state(x, y, width, height, maximized, minimized);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to restore the graph window to its previous dimensions.
|
||||
*/
|
||||
void GtkStatsFlameGraph::
|
||||
set_window_state(int x, int y, int width, int height,
|
||||
bool maximized, bool minimized) {
|
||||
GtkStatsGraph::set_window_state(x, y, width, height, maximized, minimized);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called during the servicing of the draw event; it gives a derived
|
||||
* class opportunity to do some further painting into the graph window.
|
||||
|
|
@ -417,6 +436,37 @@ handle_button_press(int graph_x, int graph_y, bool double_click, int button) {
|
|||
(void *)menu_def);
|
||||
}
|
||||
|
||||
{
|
||||
GtkWidget *menu_item = gtk_separator_menu_item_new();
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
|
||||
}
|
||||
|
||||
{
|
||||
const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
|
||||
-1, collector_index,
|
||||
GtkStatsMonitor::CT_choose_color,
|
||||
});
|
||||
|
||||
GtkWidget *menu_item = gtk_menu_item_new_with_label("Change Color...");
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
|
||||
g_signal_connect(G_OBJECT(menu_item), "activate",
|
||||
G_CALLBACK(GtkStatsMonitor::menu_activate),
|
||||
(void *)menu_def);
|
||||
}
|
||||
|
||||
{
|
||||
const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
|
||||
-1, collector_index,
|
||||
GtkStatsMonitor::CT_reset_color,
|
||||
});
|
||||
|
||||
GtkWidget *menu_item = gtk_menu_item_new_with_label("Reset Color");
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
|
||||
g_signal_connect(G_OBJECT(menu_item), "activate",
|
||||
G_CALLBACK(GtkStatsMonitor::menu_activate),
|
||||
(void *)menu_def);
|
||||
}
|
||||
|
||||
gtk_widget_show_all(menu);
|
||||
gtk_menu_popup_at_pointer(GTK_MENU(menu), nullptr);
|
||||
return TRUE;
|
||||
|
|
|
|||
|
|
@ -52,6 +52,11 @@ protected:
|
|||
|
||||
virtual bool animate(double time, double dt);
|
||||
|
||||
virtual bool get_window_state(int &x, int &y, int &width, int &height,
|
||||
bool &maximized, bool &minimized) const;
|
||||
virtual void set_window_state(int x, int y, int width, int height,
|
||||
bool maximized, bool minimized);
|
||||
|
||||
virtual void additional_graph_window_paint(cairo_t *cr);
|
||||
virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const;
|
||||
virtual DragMode consider_drag_start(int graph_x, int graph_y);
|
||||
|
|
|
|||
|
|
@ -309,6 +309,36 @@ animate(double time, double dt) {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current window dimensions.
|
||||
*/
|
||||
void GtkStatsGraph::
|
||||
get_window_state(int &x, int &y, int &width, int &height,
|
||||
bool &maximized, bool &minimized) const {
|
||||
GtkWindow *window = GTK_WINDOW(_window);
|
||||
gtk_window_get_position(window, &x, &y);
|
||||
gtk_window_get_size(window, &width, &height);
|
||||
maximized = gtk_window_is_maximized(window);
|
||||
minimized = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to restore the graph window to its previous dimensions.
|
||||
*/
|
||||
void GtkStatsGraph::
|
||||
set_window_state(int x, int y, int width, int height,
|
||||
bool maximized, bool minimized) {
|
||||
GtkWindow *window = GTK_WINDOW(_window);
|
||||
gtk_window_move(window, x, y);
|
||||
gtk_window_resize(window, width, height);
|
||||
if (maximized) {
|
||||
gtk_window_maximize(window);
|
||||
}
|
||||
if (minimized) {
|
||||
gtk_window_iconify(window);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pattern suitable for drawing in the indicated collector's color.
|
||||
*/
|
||||
|
|
@ -358,6 +388,17 @@ get_collector_text_color(int collector_index, bool highlight) {
|
|||
return highlight ? hcolor : color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the given collector has changed colors.
|
||||
*/
|
||||
void GtkStatsGraph::
|
||||
reset_collector_color(int collector_index) {
|
||||
_brushes.erase(collector_index);
|
||||
_text_colors.erase(collector_index);
|
||||
force_redraw();
|
||||
_label_stack.update_label_color(collector_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called during the servicing of the draw event; it gives a derived
|
||||
* class opportunity to do some further painting into the graph window.
|
||||
|
|
|
|||
|
|
@ -60,12 +60,19 @@ public:
|
|||
virtual void on_leave_label(int collector_index);
|
||||
virtual std::string get_label_tooltip(int collector_index) const;
|
||||
|
||||
void reset_collector_color(int collector_index);
|
||||
|
||||
protected:
|
||||
void close();
|
||||
|
||||
void start_animation();
|
||||
virtual bool animate(double time, double dt);
|
||||
|
||||
void get_window_state(int &x, int &y, int &width, int &height,
|
||||
bool &maximized, bool &minimized) const;
|
||||
void set_window_state(int x, int y, int width, int height,
|
||||
bool maximized, bool minimized);
|
||||
|
||||
cairo_pattern_t *get_collector_pattern(int collector_index, bool highlight = false);
|
||||
LRGBColor get_collector_text_color(int collector_index, bool highlight = false);
|
||||
|
||||
|
|
|
|||
|
|
@ -51,34 +51,10 @@ GtkStatsLabel(GtkStatsMonitor *monitor, GtkStatsGraph *graph,
|
|||
|
||||
gtk_widget_set_has_tooltip(_widget, TRUE);
|
||||
|
||||
// Set the fg and bg colors on the label.
|
||||
LRGBColor rgb = _monitor->get_collector_color(_collector_index);
|
||||
_bg_color = LRGBColor(
|
||||
encode_sRGB_float((float)rgb[0]),
|
||||
encode_sRGB_float((float)rgb[1]),
|
||||
encode_sRGB_float((float)rgb[2]));
|
||||
|
||||
_highlight_bg_color = LRGBColor(
|
||||
encode_sRGB_float((float)rgb[0] * 0.75f),
|
||||
encode_sRGB_float((float)rgb[1] * 0.75f),
|
||||
encode_sRGB_float((float)rgb[2] * 0.75f));
|
||||
|
||||
// Should our foreground be black or white?
|
||||
PN_stdfloat bright = _bg_color.dot(LRGBColor(0.2126, 0.7152, 0.0722));
|
||||
if (bright >= 0.5) {
|
||||
_fg_color = LRGBColor(0);
|
||||
} else {
|
||||
_fg_color = LRGBColor(1);
|
||||
}
|
||||
if (bright * 0.75 >= 0.5) {
|
||||
_highlight_fg_color = LRGBColor(0);
|
||||
} else {
|
||||
_highlight_fg_color = LRGBColor(1);
|
||||
}
|
||||
|
||||
_highlight = false;
|
||||
_mouse_within = false;
|
||||
|
||||
update_color();
|
||||
update_text(use_fullname);
|
||||
gtk_widget_show_all(_widget);
|
||||
}
|
||||
|
|
@ -145,6 +121,39 @@ get_highlight() const {
|
|||
return _highlight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the colors.
|
||||
*/
|
||||
void GtkStatsLabel::
|
||||
update_color() {
|
||||
// Set the fg and bg colors on the label.
|
||||
LRGBColor rgb = _monitor->get_collector_color(_collector_index);
|
||||
_bg_color = LRGBColor(
|
||||
encode_sRGB_float((float)rgb[0]),
|
||||
encode_sRGB_float((float)rgb[1]),
|
||||
encode_sRGB_float((float)rgb[2]));
|
||||
|
||||
_highlight_bg_color = LRGBColor(
|
||||
encode_sRGB_float((float)rgb[0] * 0.75f),
|
||||
encode_sRGB_float((float)rgb[1] * 0.75f),
|
||||
encode_sRGB_float((float)rgb[2] * 0.75f));
|
||||
|
||||
// Should our foreground be black or white?
|
||||
PN_stdfloat bright = _bg_color.dot(LRGBColor(0.2126, 0.7152, 0.0722));
|
||||
if (bright >= 0.5) {
|
||||
_fg_color = LRGBColor(0);
|
||||
} else {
|
||||
_fg_color = LRGBColor(1);
|
||||
}
|
||||
if (bright * 0.75 >= 0.5) {
|
||||
_highlight_fg_color = LRGBColor(0);
|
||||
} else {
|
||||
_highlight_fg_color = LRGBColor(1);
|
||||
}
|
||||
|
||||
gtk_widget_queue_draw(_widget);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if the full name of the collector should be shown.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ public:
|
|||
void set_highlight(bool highlight);
|
||||
bool get_highlight() const;
|
||||
|
||||
void update_color();
|
||||
void update_text(bool use_fullname);
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -128,10 +128,20 @@ void GtkStatsLabelStack::
|
|||
highlight_label(int collector_index) {
|
||||
if (_highlight_label != collector_index) {
|
||||
_highlight_label = collector_index;
|
||||
Labels::iterator li;
|
||||
for (li = _labels.begin(); li != _labels.end(); ++li) {
|
||||
GtkStatsLabel *label = (*li);
|
||||
for (GtkStatsLabel *label : _labels) {
|
||||
label->set_highlight(label->get_collector_index() == _highlight_label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the color of the label with the given index.
|
||||
*/
|
||||
void GtkStatsLabelStack::
|
||||
update_label_color(int collector_index) {
|
||||
for (GtkStatsLabel *label : _labels) {
|
||||
if (label->get_collector_index() == collector_index) {
|
||||
label->update_color();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ public:
|
|||
int get_num_labels() const;
|
||||
|
||||
void highlight_label(int collector_index);
|
||||
void update_label_color(int collector_index);
|
||||
|
||||
private:
|
||||
GtkWidget *_widget;
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file gtkStatsMenuId.h
|
||||
* @author drose
|
||||
* @date 2006-01-16
|
||||
*/
|
||||
|
||||
#ifndef GTKSTATSMENUID_H
|
||||
#define GTKSTATSMENUID_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
|
||||
/**
|
||||
* The enumerated values here are used for menu ID's for the various pulldown
|
||||
* menus in the application.
|
||||
*/
|
||||
enum GtkStatsMenuId {
|
||||
MI_none,
|
||||
MI_time_ms,
|
||||
MI_time_hz,
|
||||
MI_frame_rate_label,
|
||||
MI_speed_1,
|
||||
MI_speed_2,
|
||||
MI_speed_3,
|
||||
MI_speed_6,
|
||||
MI_speed_12,
|
||||
MI_pause,
|
||||
|
||||
// This one is last and represents the beginning of the range for the
|
||||
// various "new chart" menu options.
|
||||
MI_new_chart
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -19,23 +19,28 @@
|
|||
#include "gtkStatsPianoRoll.h"
|
||||
#include "gtkStatsFlameGraph.h"
|
||||
#include "gtkStatsTimeline.h"
|
||||
#include "gtkStatsMenuId.h"
|
||||
#include "pStatGraph.h"
|
||||
#include "pStatCollectorDef.h"
|
||||
|
||||
#include "convert_srgb.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
GtkStatsMonitor::
|
||||
GtkStatsMonitor(GtkStatsServer *server) : PStatMonitor(server) {
|
||||
_window = nullptr;
|
||||
_window = server->get_window();
|
||||
_menu_bar = server->get_menu_bar();
|
||||
_status_bar = server->get_status_bar();
|
||||
|
||||
// These will be filled in later when the menu is created.
|
||||
_time_units = 0;
|
||||
_scroll_speed = 0.0;
|
||||
_pause = false;
|
||||
_next_chart_index = 2;
|
||||
|
||||
_resolution = gdk_screen_get_resolution(gdk_screen_get_default());
|
||||
setup_speed_menu();
|
||||
setup_frame_rate_label();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -43,7 +48,41 @@ GtkStatsMonitor(GtkStatsServer *server) : PStatMonitor(server) {
|
|||
*/
|
||||
GtkStatsMonitor::
|
||||
~GtkStatsMonitor() {
|
||||
shutdown();
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes all the graphs associated with this monitor.
|
||||
*/
|
||||
void GtkStatsMonitor::
|
||||
close() {
|
||||
PStatMonitor::close();
|
||||
|
||||
remove_all_graphs();
|
||||
|
||||
for (GtkWidget *label : _status_bar_labels) {
|
||||
gtk_container_remove(GTK_CONTAINER(_status_bar), label);
|
||||
}
|
||||
_status_bar_collectors.clear();
|
||||
_status_bar_labels.clear();
|
||||
|
||||
if (_speed_menu_item != nullptr) {
|
||||
gtk_container_remove(GTK_CONTAINER(_menu_bar), _speed_menu_item);
|
||||
_speed_menu_item = nullptr;
|
||||
}
|
||||
|
||||
for (GtkStatsChartMenu *chart_menu : _chart_menus) {
|
||||
chart_menu->remove_from_menu_bar(_menu_bar);
|
||||
delete chart_menu;
|
||||
}
|
||||
_chart_menus.clear();
|
||||
|
||||
if (_frame_rate_menu_item != nullptr) {
|
||||
gtk_container_remove(GTK_CONTAINER(_menu_bar), _frame_rate_menu_item);
|
||||
_frame_rate_menu_item = nullptr;
|
||||
}
|
||||
|
||||
_next_chart_index = 2;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -72,8 +111,6 @@ initialized() {
|
|||
*/
|
||||
void GtkStatsMonitor::
|
||||
got_hello() {
|
||||
create_window();
|
||||
open_strip_chart(0, 0, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -102,7 +139,7 @@ got_bad_version(int client_major, int client_minor,
|
|||
|
||||
std::string message = str.str();
|
||||
GtkWidget *dialog =
|
||||
gtk_message_dialog_new(GTK_WINDOW(main_window),
|
||||
gtk_message_dialog_new(GTK_WINDOW(_window),
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||
GTK_MESSAGE_ERROR,
|
||||
GTK_BUTTONS_CLOSE,
|
||||
|
|
@ -120,16 +157,13 @@ got_bad_version(int client_major, int client_minor,
|
|||
*/
|
||||
void GtkStatsMonitor::
|
||||
new_collector(int collector_index) {
|
||||
Graphs::iterator gi;
|
||||
for (gi = _graphs.begin(); gi != _graphs.end(); ++gi) {
|
||||
GtkStatsGraph *graph = (*gi);
|
||||
for (GtkStatsGraph *graph : _graphs) {
|
||||
graph->new_collector(collector_index);
|
||||
}
|
||||
|
||||
// We might need to update our menus.
|
||||
ChartMenus::iterator mi;
|
||||
for (mi = _chart_menus.begin(); mi != _chart_menus.end(); ++mi) {
|
||||
(*mi)->do_update();
|
||||
for (GtkStatsChartMenu *chart_menu : _chart_menus) {
|
||||
chart_menu->do_update();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -163,6 +197,14 @@ new_data(int thread_index, int frame_number) {
|
|||
if (thread_index == 0) {
|
||||
update_status_bar();
|
||||
}
|
||||
|
||||
if (!_have_data) {
|
||||
open_default_graphs();
|
||||
_have_data = true;
|
||||
|
||||
// Flash the window.
|
||||
gtk_window_set_urgency_hint(GTK_WINDOW(_window), TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -173,8 +215,6 @@ new_data(int thread_index, int frame_number) {
|
|||
void GtkStatsMonitor::
|
||||
lost_connection() {
|
||||
nout << "Lost connection to " << get_client_hostname() << "\n";
|
||||
|
||||
shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -184,9 +224,8 @@ lost_connection() {
|
|||
void GtkStatsMonitor::
|
||||
idle() {
|
||||
// Check if any of our chart menus need updating.
|
||||
ChartMenus::iterator mi;
|
||||
for (mi = _chart_menus.begin(); mi != _chart_menus.end(); ++mi) {
|
||||
(*mi)->check_update();
|
||||
for (GtkStatsChartMenu *chart_menu : _chart_menus) {
|
||||
chart_menu->check_update();
|
||||
}
|
||||
|
||||
// Update the frame rate label from the main thread (thread 0).
|
||||
|
|
@ -218,9 +257,7 @@ has_idle() {
|
|||
*/
|
||||
void GtkStatsMonitor::
|
||||
user_guide_bars_changed() {
|
||||
Graphs::iterator gi;
|
||||
for (gi = _graphs.begin(); gi != _graphs.end(); ++gi) {
|
||||
GtkStatsGraph *graph = (*gi);
|
||||
for (GtkStatsGraph *graph : _graphs) {
|
||||
graph->user_guide_bars_changed();
|
||||
}
|
||||
}
|
||||
|
|
@ -242,56 +279,90 @@ get_resolution() const {
|
|||
}
|
||||
|
||||
/**
|
||||
* Opens a new strip chart showing the indicated data.
|
||||
* Opens a new timeline.
|
||||
*/
|
||||
void GtkStatsMonitor::
|
||||
open_strip_chart(int thread_index, int collector_index, bool show_level) {
|
||||
GtkStatsStripChart *graph =
|
||||
new GtkStatsStripChart(this, thread_index, collector_index, show_level);
|
||||
PStatGraph *GtkStatsMonitor::
|
||||
open_timeline() {
|
||||
GtkStatsTimeline *graph = new GtkStatsTimeline(this);
|
||||
add_graph(graph);
|
||||
|
||||
graph->set_time_units(_time_units);
|
||||
graph->set_scroll_speed(_scroll_speed);
|
||||
graph->set_pause(_pause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new piano roll showing the indicated data.
|
||||
*/
|
||||
void GtkStatsMonitor::
|
||||
open_piano_roll(int thread_index) {
|
||||
GtkStatsPianoRoll *graph = new GtkStatsPianoRoll(this, thread_index);
|
||||
add_graph(graph);
|
||||
|
||||
graph->set_time_units(_time_units);
|
||||
graph->set_scroll_speed(_scroll_speed);
|
||||
graph->set_pause(_pause);
|
||||
return graph;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new flame graph showing the indicated data.
|
||||
*/
|
||||
void GtkStatsMonitor::
|
||||
PStatGraph *GtkStatsMonitor::
|
||||
open_flame_graph(int thread_index, int collector_index) {
|
||||
GtkStatsFlameGraph *graph = new GtkStatsFlameGraph(this, thread_index, collector_index);
|
||||
add_graph(graph);
|
||||
|
||||
graph->set_time_units(_time_units);
|
||||
graph->set_scroll_speed(_scroll_speed);
|
||||
graph->set_pause(_pause);
|
||||
return graph;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new timeline.
|
||||
* Opens a new strip chart showing the indicated data.
|
||||
*/
|
||||
PStatGraph *GtkStatsMonitor::
|
||||
open_strip_chart(int thread_index, int collector_index, bool show_level) {
|
||||
GtkStatsStripChart *graph =
|
||||
new GtkStatsStripChart(this, thread_index, collector_index, show_level);
|
||||
add_graph(graph);
|
||||
return graph;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new piano roll showing the indicated data.
|
||||
*/
|
||||
PStatGraph *GtkStatsMonitor::
|
||||
open_piano_roll(int thread_index) {
|
||||
GtkStatsPianoRoll *graph = new GtkStatsPianoRoll(this, thread_index);
|
||||
add_graph(graph);
|
||||
return graph;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a dialog to change the given collector color.
|
||||
*/
|
||||
void GtkStatsMonitor::
|
||||
open_timeline() {
|
||||
GtkStatsTimeline *graph = new GtkStatsTimeline(this);
|
||||
add_graph(graph);
|
||||
choose_collector_color(int collector_index) {
|
||||
const LRGBColor ¤t = get_collector_color(collector_index);
|
||||
|
||||
graph->set_time_units(_time_units);
|
||||
graph->set_scroll_speed(_scroll_speed);
|
||||
graph->set_pause(_pause);
|
||||
GtkWidget *chooser = gtk_color_chooser_dialog_new(nullptr, GTK_WINDOW(_window));
|
||||
gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(chooser), FALSE);
|
||||
|
||||
GdkRGBA rgba;
|
||||
rgba.red = encode_sRGB_float((float)current[0]);
|
||||
rgba.green = encode_sRGB_float((float)current[1]);
|
||||
rgba.blue = encode_sRGB_float((float)current[2]);
|
||||
rgba.alpha = 1.0;
|
||||
gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(chooser), &rgba);
|
||||
|
||||
if (gtk_dialog_run(GTK_DIALOG(chooser)) == GTK_RESPONSE_OK) {
|
||||
gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(chooser), &rgba);
|
||||
LRGBColor result(
|
||||
decode_sRGB_float((float)rgba.red),
|
||||
decode_sRGB_float((float)rgba.green),
|
||||
decode_sRGB_float((float)rgba.blue));
|
||||
|
||||
set_collector_color(collector_index, result);
|
||||
|
||||
for (GtkStatsGraph *graph : _graphs) {
|
||||
graph->reset_collector_color(collector_index);
|
||||
}
|
||||
}
|
||||
|
||||
gtk_widget_destroy(chooser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the color of the given collector to the default.
|
||||
*/
|
||||
void GtkStatsMonitor::
|
||||
reset_collector_color(int collector_index) {
|
||||
clear_collector_color(collector_index);
|
||||
|
||||
for (GtkStatsGraph *graph : _graphs) {
|
||||
graph->reset_collector_color(collector_index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -317,13 +388,8 @@ add_menu(const MenuDef &menu_def) {
|
|||
*/
|
||||
void GtkStatsMonitor::
|
||||
set_time_units(int unit_mask) {
|
||||
_time_units = unit_mask;
|
||||
|
||||
// First, change all of the open graphs appropriately.
|
||||
Graphs::iterator gi;
|
||||
for (gi = _graphs.begin(); gi != _graphs.end(); ++gi) {
|
||||
GtkStatsGraph *graph = (*gi);
|
||||
graph->set_time_units(_time_units);
|
||||
for (GtkStatsGraph *graph : _graphs) {
|
||||
graph->set_time_units(unit_mask);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -336,9 +402,7 @@ set_scroll_speed(double scroll_speed) {
|
|||
_scroll_speed = scroll_speed;
|
||||
|
||||
// First, change all of the open graphs appropriately.
|
||||
Graphs::iterator gi;
|
||||
for (gi = _graphs.begin(); gi != _graphs.end(); ++gi) {
|
||||
GtkStatsGraph *graph = (*gi);
|
||||
for (GtkStatsGraph *graph : _graphs) {
|
||||
graph->set_scroll_speed(_scroll_speed);
|
||||
}
|
||||
}
|
||||
|
|
@ -351,9 +415,7 @@ set_pause(bool pause) {
|
|||
_pause = pause;
|
||||
|
||||
// First, change all of the open graphs appropriately.
|
||||
Graphs::iterator gi;
|
||||
for (gi = _graphs.begin(); gi != _graphs.end(); ++gi) {
|
||||
GtkStatsGraph *graph = (*gi);
|
||||
for (GtkStatsGraph *graph : _graphs) {
|
||||
graph->set_pause(_pause);
|
||||
}
|
||||
}
|
||||
|
|
@ -364,6 +426,10 @@ set_pause(bool pause) {
|
|||
void GtkStatsMonitor::
|
||||
add_graph(GtkStatsGraph *graph) {
|
||||
_graphs.insert(graph);
|
||||
|
||||
graph->set_time_units(((GtkStatsServer *)_server)->get_time_units());
|
||||
graph->set_scroll_speed(_scroll_speed);
|
||||
graph->set_pause(_pause);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -379,145 +445,14 @@ remove_graph(GtkStatsGraph *graph) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates the window for this monitor.
|
||||
* Deletes all open graphs.
|
||||
*/
|
||||
void GtkStatsMonitor::
|
||||
create_window() {
|
||||
if (_window != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
|
||||
g_signal_connect(G_OBJECT(_window), "delete_event",
|
||||
G_CALLBACK(window_delete_event), this);
|
||||
g_signal_connect(G_OBJECT(_window), "destroy",
|
||||
G_CALLBACK(window_destroy), this);
|
||||
|
||||
_window_title = get_client_progname() + " on " + get_client_hostname();
|
||||
gtk_window_set_title(GTK_WINDOW(_window), _window_title.c_str());
|
||||
|
||||
gtk_window_set_default_size(GTK_WINDOW(_window), 500, 360);
|
||||
|
||||
// Set up the menu.
|
||||
GtkAccelGroup *accel_group = gtk_accel_group_new();
|
||||
gtk_window_add_accel_group(GTK_WINDOW(_window), accel_group);
|
||||
_menu_bar = gtk_menu_bar_new();
|
||||
_next_chart_index = 2;
|
||||
|
||||
setup_options_menu();
|
||||
setup_speed_menu();
|
||||
setup_frame_rate_label();
|
||||
|
||||
for (GtkStatsChartMenu *chart_menu : _chart_menus) {
|
||||
chart_menu->add_to_menu_bar(_menu_bar, _next_chart_index);
|
||||
++_next_chart_index;
|
||||
}
|
||||
|
||||
// Pack the menu into the window.
|
||||
GtkWidget *main_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 1);
|
||||
gtk_container_add(GTK_CONTAINER(_window), main_vbox);
|
||||
gtk_box_pack_start(GTK_BOX(main_vbox), _menu_bar, FALSE, TRUE, 0);
|
||||
|
||||
// Create the status bar.
|
||||
_status_bar = gtk_flow_box_new();
|
||||
gtk_flow_box_set_activate_on_single_click(GTK_FLOW_BOX(_status_bar), FALSE);
|
||||
gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(_status_bar), GTK_SELECTION_NONE);
|
||||
g_signal_connect(G_OBJECT(_status_bar), "button_press_event",
|
||||
G_CALLBACK(status_bar_button_event), this);
|
||||
gtk_box_pack_end(GTK_BOX(main_vbox), _status_bar, FALSE, FALSE, 0);
|
||||
update_status_bar();
|
||||
|
||||
GtkWidget *sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
|
||||
gtk_box_pack_end(GTK_BOX(main_vbox), sep, FALSE, FALSE, 0);
|
||||
|
||||
gtk_widget_show_all(_window);
|
||||
gtk_widget_show(_window);
|
||||
gtk_widget_realize(_window);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes all the graphs associated with this monitor.
|
||||
*/
|
||||
void GtkStatsMonitor::
|
||||
shutdown() {
|
||||
Graphs::iterator gi;
|
||||
for (gi = _graphs.begin(); gi != _graphs.end(); ++gi) {
|
||||
delete (*gi);
|
||||
remove_all_graphs() {
|
||||
for (GtkStatsGraph *graph : _graphs) {
|
||||
delete graph;
|
||||
}
|
||||
_graphs.clear();
|
||||
|
||||
ChartMenus::iterator mi;
|
||||
for (mi = _chart_menus.begin(); mi != _chart_menus.end(); ++mi) {
|
||||
delete (*mi);
|
||||
}
|
||||
_chart_menus.clear();
|
||||
|
||||
if (_window != nullptr) {
|
||||
gtk_widget_destroy(_window);
|
||||
_window = nullptr;
|
||||
}
|
||||
|
||||
#ifdef DEVELOP_GTKSTATS
|
||||
// For GtkStats developers, exit when the first monitor closes.
|
||||
gtk_main_quit();
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback when the window is closed by the user.
|
||||
*/
|
||||
gboolean GtkStatsMonitor::
|
||||
window_delete_event(GtkWidget *widget, GdkEvent *event, gpointer data) {
|
||||
// Returning FALSE to indicate we should destroy the window when the user
|
||||
// selects "close".
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback when the window is destroyed by the system (or by delete_event).
|
||||
*/
|
||||
void GtkStatsMonitor::
|
||||
window_destroy(GtkWidget *widget, gpointer data) {
|
||||
GtkStatsMonitor *self = (GtkStatsMonitor *)data;
|
||||
self->close();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates the "Options" pulldown menu.
|
||||
*/
|
||||
void GtkStatsMonitor::
|
||||
setup_options_menu() {
|
||||
_options_menu = gtk_menu_new();
|
||||
|
||||
GtkWidget *item = gtk_menu_item_new_with_label("Options");
|
||||
gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), _options_menu);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_menu_bar), item);
|
||||
|
||||
GtkWidget *units_menu = gtk_menu_new();
|
||||
item = gtk_menu_item_new_with_label("Units");
|
||||
gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), units_menu);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_options_menu), item);
|
||||
|
||||
item = gtk_radio_menu_item_new_with_label(nullptr, "ms");
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(units_menu), item);
|
||||
g_signal_connect(G_OBJECT(item), "activate",
|
||||
G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
|
||||
GtkStatsMonitor *self = (GtkStatsMonitor *)data;
|
||||
self->set_time_units(PStatGraph::GBU_ms);
|
||||
}), this);
|
||||
|
||||
item = gtk_radio_menu_item_new_with_label(
|
||||
gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item)), "Hz");
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(units_menu), item);
|
||||
g_signal_connect(G_OBJECT(item), "activate",
|
||||
G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
|
||||
GtkStatsMonitor *self = (GtkStatsMonitor *)data;
|
||||
self->set_time_units(PStatGraph::GBU_hz);
|
||||
}), this);
|
||||
|
||||
set_time_units(PStatGraph::GBU_ms);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -525,15 +460,16 @@ setup_options_menu() {
|
|||
*/
|
||||
void GtkStatsMonitor::
|
||||
setup_speed_menu() {
|
||||
_speed_menu = gtk_menu_new();
|
||||
GtkWidget *menu = gtk_menu_new();
|
||||
|
||||
GtkWidget *item = gtk_menu_item_new_with_label("Speed");
|
||||
gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), _speed_menu);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_menu_bar), item);
|
||||
_speed_menu_item = gtk_menu_item_new_with_label("Speed");
|
||||
gtk_menu_item_set_submenu(GTK_MENU_ITEM(_speed_menu_item), menu);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_menu_bar), _speed_menu_item);
|
||||
|
||||
GSList *group = nullptr;
|
||||
GtkWidget *item;
|
||||
item = gtk_radio_menu_item_new_with_label(group, "1");
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_speed_menu), item);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
|
||||
g_signal_connect(G_OBJECT(item), "toggled",
|
||||
G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
|
||||
if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))) {
|
||||
|
|
@ -544,7 +480,7 @@ setup_speed_menu() {
|
|||
group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
|
||||
|
||||
item = gtk_radio_menu_item_new_with_label(group, "2");
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_speed_menu), item);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
|
||||
g_signal_connect(G_OBJECT(item), "toggled",
|
||||
G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
|
||||
if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))) {
|
||||
|
|
@ -556,7 +492,7 @@ setup_speed_menu() {
|
|||
|
||||
item = gtk_radio_menu_item_new_with_label(group, "3");
|
||||
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_speed_menu), item);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
|
||||
g_signal_connect(G_OBJECT(item), "toggled",
|
||||
G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
|
||||
if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))) {
|
||||
|
|
@ -567,7 +503,7 @@ setup_speed_menu() {
|
|||
group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
|
||||
|
||||
item = gtk_radio_menu_item_new_with_label(group, "6");
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_speed_menu), item);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
|
||||
g_signal_connect(G_OBJECT(item), "toggled",
|
||||
G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
|
||||
if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))) {
|
||||
|
|
@ -578,7 +514,7 @@ setup_speed_menu() {
|
|||
group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
|
||||
|
||||
item = gtk_radio_menu_item_new_with_label(group, "12");
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_speed_menu), item);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
|
||||
g_signal_connect(G_OBJECT(item), "toggled",
|
||||
G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
|
||||
if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))) {
|
||||
|
|
@ -589,10 +525,10 @@ setup_speed_menu() {
|
|||
group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
|
||||
|
||||
item = gtk_separator_menu_item_new();
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_speed_menu), item);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
|
||||
|
||||
item = gtk_check_menu_item_new_with_label("pause");
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_speed_menu), item);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
|
||||
g_signal_connect(G_OBJECT(item), "toggled",
|
||||
G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
|
||||
GtkStatsMonitor *self = (GtkStatsMonitor *)data;
|
||||
|
|
@ -601,6 +537,9 @@ setup_speed_menu() {
|
|||
|
||||
set_scroll_speed(3);
|
||||
set_pause(false);
|
||||
|
||||
gtk_widget_show_all(_speed_menu_item);
|
||||
++_next_chart_index;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -715,7 +654,7 @@ status_bar_button_event(GtkWidget *widget, GdkEventButton *event, gpointer data)
|
|||
GList *children = gtk_container_get_children(GTK_CONTAINER(monitor->_status_bar));
|
||||
int index = g_list_index(children, child);
|
||||
g_list_free(children);
|
||||
if (index < 0 || index >= monitor->_status_bar_labels.size()) {
|
||||
if (index < 0 || (size_t)index >= monitor->_status_bar_labels.size()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
|
@ -790,31 +729,114 @@ status_bar_button_event(GtkWidget *widget, GdkEventButton *event, gpointer data)
|
|||
*/
|
||||
void GtkStatsMonitor::
|
||||
menu_activate(GtkWidget *widget, gpointer data) {
|
||||
const MenuDef *menu_def = (const MenuDef *)data;
|
||||
GtkStatsMonitor *monitor = menu_def->_monitor;
|
||||
const MenuDef &menu_def = *(const MenuDef *)data;
|
||||
GtkStatsMonitor *monitor = menu_def._monitor;
|
||||
|
||||
if (monitor == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (menu_def->_chart_type) {
|
||||
switch (menu_def._chart_type) {
|
||||
case CT_timeline:
|
||||
monitor->open_timeline();
|
||||
break;
|
||||
|
||||
case CT_strip_chart:
|
||||
monitor->open_strip_chart(menu_def->_thread_index,
|
||||
menu_def->_collector_index,
|
||||
menu_def->_show_level);
|
||||
monitor->open_strip_chart(menu_def._thread_index,
|
||||
menu_def._collector_index,
|
||||
menu_def._show_level);
|
||||
break;
|
||||
|
||||
case CT_flame_graph:
|
||||
monitor->open_flame_graph(menu_def->_thread_index,
|
||||
menu_def->_collector_index);
|
||||
monitor->open_flame_graph(menu_def._thread_index,
|
||||
menu_def._collector_index);
|
||||
break;
|
||||
|
||||
case CT_piano_roll:
|
||||
monitor->open_piano_roll(menu_def->_thread_index);
|
||||
monitor->open_piano_roll(menu_def._thread_index);
|
||||
break;
|
||||
|
||||
case CT_choose_color:
|
||||
monitor->choose_collector_color(menu_def._collector_index);
|
||||
break;
|
||||
|
||||
case CT_reset_color:
|
||||
monitor->reset_collector_color(menu_def._collector_index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a status bar item is double-clicked.
|
||||
*/
|
||||
void GtkStatsMonitor::
|
||||
handle_status_bar_click(int item) {
|
||||
if (item == 0) {
|
||||
open_strip_chart(0, 0, false);
|
||||
}
|
||||
else if (item >= 1 && (size_t)item < _status_bar_collectors.size()) {
|
||||
int collector = _status_bar_collectors[item];
|
||||
open_strip_chart(0, collector, true);
|
||||
|
||||
// Also open a strip chart for other threads with data for this
|
||||
// collector.
|
||||
const PStatClientData *client_data = get_client_data();
|
||||
for (int thread_index = 1; thread_index < client_data->get_num_threads(); ++thread_index) {
|
||||
PStatView &view = get_level_view(collector, thread_index);
|
||||
if (view.get_net_value() > 0.0) {
|
||||
open_strip_chart(thread_index, collector, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a status bar item is right-clicked.
|
||||
*/
|
||||
void GtkStatsMonitor::
|
||||
handle_status_bar_popup(int item) {
|
||||
if (item >= 0 && (size_t)item < _status_bar_collectors.size()) {
|
||||
int collector = _status_bar_collectors[item];
|
||||
|
||||
PStatView &level_view = get_level_view(collector, 0);
|
||||
const PStatViewLevel *view_level = level_view.get_top_level();
|
||||
int num_children = view_level->get_num_children();
|
||||
if (num_children == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
GtkWidget *menu = gtk_menu_new();
|
||||
|
||||
// Reverse the order since the menus are listed from the top down; we want
|
||||
// to be visually consistent with the graphs, which list these labels from
|
||||
// the bottom up.
|
||||
const PStatClientData *client_data = get_client_data();
|
||||
for (int c = num_children - 1; c >= 0; c--) {
|
||||
const PStatViewLevel *child_level = view_level->get_child(c);
|
||||
|
||||
int child_collector = child_level->get_collector();
|
||||
const MenuDef *menu_def = add_menu({0, child_collector, CT_strip_chart, true});
|
||||
|
||||
double value = child_level->get_net_value();
|
||||
|
||||
const PStatCollectorDef &def = client_data->get_collector_def(child_collector);
|
||||
std::string text = def._name;
|
||||
text += ": " + PStatGraph::format_number(value, PStatGraph::GBU_named | PStatGraph::GBU_show_units, def._level_units);
|
||||
|
||||
GtkWidget *menu_item = gtk_menu_item_new_with_label(text.c_str());
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
|
||||
|
||||
g_signal_connect(G_OBJECT(menu_item), "activate",
|
||||
G_CALLBACK(menu_activate),
|
||||
(void *)menu_def);
|
||||
}
|
||||
|
||||
gtk_widget_show_all(menu);
|
||||
|
||||
GtkWidget *label = _status_bar_labels[item];
|
||||
gtk_menu_popup_at_widget(GTK_MENU(menu), label,
|
||||
GDK_GRAVITY_NORTH_WEST,
|
||||
GDK_GRAVITY_SOUTH_WEST, nullptr);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,9 @@ public:
|
|||
CT_strip_chart,
|
||||
CT_flame_graph,
|
||||
CT_piano_roll,
|
||||
|
||||
CT_choose_color,
|
||||
CT_reset_color,
|
||||
};
|
||||
|
||||
class MenuDef {
|
||||
|
|
@ -57,6 +60,8 @@ public:
|
|||
GtkStatsMonitor(GtkStatsServer *server);
|
||||
virtual ~GtkStatsMonitor();
|
||||
|
||||
void close();
|
||||
|
||||
virtual std::string get_monitor_name();
|
||||
|
||||
virtual void initialized();
|
||||
|
|
@ -75,10 +80,13 @@ public:
|
|||
GtkWidget *get_window() const;
|
||||
double get_resolution() const;
|
||||
|
||||
void open_strip_chart(int thread_index, int collector_index, bool show_level);
|
||||
void open_piano_roll(int thread_index);
|
||||
void open_flame_graph(int thread_index, int collector_index = -1);
|
||||
void open_timeline();
|
||||
PStatGraph *open_timeline();
|
||||
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_piano_roll(int thread_index);
|
||||
|
||||
void choose_collector_color(int collector_index);
|
||||
void reset_collector_color(int collector_index);
|
||||
|
||||
const MenuDef *add_menu(const MenuDef &menu_def);
|
||||
|
||||
|
|
@ -86,26 +94,22 @@ public:
|
|||
void set_scroll_speed(double scroll_speed);
|
||||
void set_pause(bool pause);
|
||||
|
||||
private:
|
||||
void add_graph(GtkStatsGraph *graph);
|
||||
void remove_graph(GtkStatsGraph *graph);
|
||||
void remove_all_graphs();
|
||||
|
||||
void create_window();
|
||||
void shutdown();
|
||||
static gboolean window_delete_event(GtkWidget *widget, GdkEvent *event,
|
||||
gpointer data);
|
||||
static void window_destroy(GtkWidget *widget, gpointer data);
|
||||
void setup_options_menu();
|
||||
private:
|
||||
void setup_speed_menu();
|
||||
void setup_frame_rate_label();
|
||||
void update_status_bar();
|
||||
bool show_popup_menu(int collector);
|
||||
|
||||
static gboolean status_bar_button_event(GtkWidget *widget,
|
||||
GdkEventButton *event,
|
||||
gpointer data);
|
||||
public:
|
||||
static void menu_activate(GtkWidget *widget, gpointer data);
|
||||
void handle_status_bar_click(int item);
|
||||
void handle_status_bar_popup(int item);
|
||||
|
||||
private:
|
||||
typedef pset<GtkStatsGraph *> Graphs;
|
||||
|
|
@ -119,21 +123,21 @@ private:
|
|||
|
||||
GtkWidget *_window;
|
||||
GtkWidget *_menu_bar;
|
||||
GtkWidget *_options_menu;
|
||||
GtkWidget *_speed_menu;
|
||||
GtkWidget *_speed_menu_item = nullptr;
|
||||
int _next_chart_index;
|
||||
GtkWidget *_frame_rate_menu_item;
|
||||
GtkWidget *_frame_rate_menu_item = nullptr;
|
||||
GtkWidget *_frame_rate_label;
|
||||
GtkWidget *_status_bar;
|
||||
pvector<int> _status_bar_collectors;
|
||||
pvector<GtkWidget *> _status_bar_labels;
|
||||
std::string _window_title;
|
||||
int _time_units;
|
||||
double _scroll_speed;
|
||||
bool _pause;
|
||||
bool _have_data = false;
|
||||
double _resolution;
|
||||
|
||||
friend class GtkStatsGraph;
|
||||
friend class GtkStatsServer;
|
||||
};
|
||||
|
||||
#include "gtkStatsMonitor.I"
|
||||
|
|
|
|||
|
|
@ -64,7 +64,8 @@ GtkStatsPianoRoll(GtkStatsMonitor *monitor, int thread_index) :
|
|||
// window's initial size.
|
||||
gtk_widget_set_size_request(_window, 0, 0);
|
||||
|
||||
clear_region();
|
||||
force_redraw();
|
||||
idle();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -170,6 +171,35 @@ on_popup_label(int collector_index) {
|
|||
(void *)menu_def);
|
||||
}
|
||||
|
||||
{
|
||||
GtkWidget *menu_item = gtk_separator_menu_item_new();
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
|
||||
}
|
||||
|
||||
{
|
||||
const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
|
||||
-1, collector_index, GtkStatsMonitor::CT_choose_color,
|
||||
});
|
||||
|
||||
GtkWidget *menu_item = gtk_menu_item_new_with_label("Change Color...");
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
|
||||
g_signal_connect(G_OBJECT(menu_item), "activate",
|
||||
G_CALLBACK(GtkStatsMonitor::menu_activate),
|
||||
(void *)menu_def);
|
||||
}
|
||||
|
||||
{
|
||||
const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
|
||||
-1, collector_index, GtkStatsMonitor::CT_reset_color,
|
||||
});
|
||||
|
||||
GtkWidget *menu_item = gtk_menu_item_new_with_label("Reset Color");
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
|
||||
g_signal_connect(G_OBJECT(menu_item), "activate",
|
||||
G_CALLBACK(GtkStatsMonitor::menu_activate),
|
||||
(void *)menu_def);
|
||||
}
|
||||
|
||||
gtk_widget_show_all(menu);
|
||||
gtk_menu_popup_at_pointer(GTK_MENU(menu), nullptr);
|
||||
}
|
||||
|
|
@ -263,6 +293,25 @@ idle() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current window dimensions.
|
||||
*/
|
||||
bool GtkStatsPianoRoll::
|
||||
get_window_state(int &x, int &y, int &width, int &height,
|
||||
bool &maximized, bool &minimized) const {
|
||||
GtkStatsGraph::get_window_state(x, y, width, height, maximized, minimized);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to restore the graph window to its previous dimensions.
|
||||
*/
|
||||
void GtkStatsPianoRoll::
|
||||
set_window_state(int x, int y, int width, int height,
|
||||
bool maximized, bool minimized) {
|
||||
GtkStatsGraph::set_window_state(x, y, width, height, maximized, minimized);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called during the servicing of the draw event; it gives a derived
|
||||
* class opportunity to do some further painting into the graph window.
|
||||
|
|
|
|||
|
|
@ -51,6 +51,11 @@ protected:
|
|||
virtual void end_draw();
|
||||
virtual void idle();
|
||||
|
||||
virtual bool get_window_state(int &x, int &y, int &width, int &height,
|
||||
bool &maximized, bool &minimized) const;
|
||||
virtual void set_window_state(int x, int y, int width, int height,
|
||||
bool maximized, bool minimized);
|
||||
|
||||
virtual void additional_graph_window_paint(cairo_t *cr);
|
||||
virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const;
|
||||
virtual DragMode consider_drag_start(int graph_x, int graph_y);
|
||||
|
|
|
|||
|
|
@ -13,11 +13,685 @@
|
|||
|
||||
#include "gtkStatsServer.h"
|
||||
#include "gtkStatsMonitor.h"
|
||||
#include "pandaVersion.h"
|
||||
#include "pStatGraph.h"
|
||||
#include "config_pstatclient.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
GtkStatsServer::
|
||||
GtkStatsServer() {
|
||||
#ifdef __APPLE__
|
||||
_last_session = Filename::expand_from(
|
||||
"$HOME/Library/Caches/Panda3D-" PANDA_ABI_VERSION_STR "/last-session.pstats");
|
||||
#else
|
||||
_last_session = Filename::expand_from("$XDG_CACHE_HOME/panda3d/last-session.pstats");
|
||||
#endif
|
||||
_last_session.set_binary();
|
||||
|
||||
_window = nullptr;
|
||||
_menu_bar = nullptr;
|
||||
_options_menu = nullptr;
|
||||
|
||||
_time_units = 0;
|
||||
|
||||
create_window();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
PStatMonitor *GtkStatsServer::
|
||||
make_monitor() {
|
||||
return new GtkStatsMonitor(this);
|
||||
make_monitor(const NetAddress &address) {
|
||||
// Enable the "New Session", "Save Session" and "Close Session" menu items.
|
||||
gtk_widget_set_sensitive(_new_session_menu_item, TRUE);
|
||||
gtk_widget_set_sensitive(_save_session_menu_item, TRUE);
|
||||
gtk_widget_set_sensitive(_close_session_menu_item, TRUE);
|
||||
gtk_widget_set_sensitive(_export_session_menu_item, TRUE);
|
||||
|
||||
std::ostringstream strm;
|
||||
strm << "PStats Server (connected to " << address << ")";
|
||||
std::string title = strm.str();
|
||||
gtk_window_set_title(GTK_WINDOW(_window), title.c_str());
|
||||
|
||||
if (_status_bar_label != nullptr) {
|
||||
gtk_container_remove(GTK_CONTAINER(_status_bar), _status_bar_label);
|
||||
_status_bar_label = nullptr;
|
||||
}
|
||||
|
||||
_monitor = new GtkStatsMonitor(this);
|
||||
return _monitor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when connection has been lost.
|
||||
*/
|
||||
void GtkStatsServer::
|
||||
lost_connection(PStatMonitor *monitor) {
|
||||
// Store a backup now, in case PStats crashes or something.
|
||||
_last_session.make_dir();
|
||||
if (monitor->write(_last_session)) {
|
||||
nout << "Wrote to " << _last_session << "\n";
|
||||
} else {
|
||||
nout << "Failed to write to " << _last_session << "\n";
|
||||
}
|
||||
|
||||
stop_listening();
|
||||
|
||||
gtk_window_set_title(GTK_WINDOW(_window), "PStats Server (disconnected)");
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a new session.
|
||||
*/
|
||||
bool GtkStatsServer::
|
||||
new_session() {
|
||||
if (!close_session()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (listen()) {
|
||||
{
|
||||
std::ostringstream strm;
|
||||
strm << "PStats Server (listening on port " << pstats_port << ")";
|
||||
std::string title = strm.str();
|
||||
gtk_window_set_title(GTK_WINDOW(_window), title.c_str());
|
||||
}
|
||||
{
|
||||
std::ostringstream strm;
|
||||
strm << "Waiting for client to connect on port " << pstats_port << "...";
|
||||
std::string title = strm.str();
|
||||
_status_bar_label = gtk_label_new(title.c_str());
|
||||
gtk_container_add(GTK_CONTAINER(_status_bar), _status_bar_label);
|
||||
gtk_widget_show(_status_bar_label);
|
||||
}
|
||||
|
||||
gtk_widget_set_sensitive(_new_session_menu_item, FALSE);
|
||||
gtk_widget_set_sensitive(_save_session_menu_item, FALSE);
|
||||
gtk_widget_set_sensitive(_close_session_menu_item, TRUE);
|
||||
gtk_widget_set_sensitive(_export_session_menu_item, FALSE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
gtk_window_set_title(GTK_WINDOW(_window), "PStats Server");
|
||||
|
||||
GtkWidget *dialog =
|
||||
gtk_message_dialog_new(GTK_WINDOW(_window),
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||
GTK_MESSAGE_ERROR,
|
||||
GTK_BUTTONS_CLOSE,
|
||||
"Unable to open port %d. Try specifying a different port number "
|
||||
"using pstats-port in your Config file.", pstats_port.get_value());
|
||||
gtk_dialog_run(GTK_DIALOG(dialog));
|
||||
gtk_widget_destroy(dialog);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offers to open an existing session.
|
||||
*/
|
||||
bool GtkStatsServer::
|
||||
open_session() {
|
||||
if (!close_session()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GtkFileChooserNative *native = gtk_file_chooser_native_new(
|
||||
"Open Session",
|
||||
GTK_WINDOW(_window),
|
||||
GTK_FILE_CHOOSER_ACTION_SAVE,
|
||||
"_Open", "Cancel");
|
||||
GtkFileChooser *chooser = GTK_FILE_CHOOSER(native);
|
||||
|
||||
GtkFileFilter *filter = gtk_file_filter_new();
|
||||
gtk_file_filter_set_name(filter, "PStats Session Files");
|
||||
gtk_file_filter_add_pattern(filter, "*.pstats");
|
||||
gtk_file_chooser_add_filter(chooser, filter);
|
||||
|
||||
gint res = gtk_native_dialog_run(GTK_NATIVE_DIALOG(native));
|
||||
if (res == GTK_RESPONSE_ACCEPT) {
|
||||
char *buffer = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(chooser));
|
||||
Filename fn = Filename::from_os_specific(buffer);
|
||||
fn.set_binary();
|
||||
g_free(buffer);
|
||||
g_object_unref(native);
|
||||
|
||||
GtkStatsMonitor *monitor = new GtkStatsMonitor(this);
|
||||
if (!monitor->read(fn)) {
|
||||
delete monitor;
|
||||
|
||||
GtkWidget *dialog =
|
||||
gtk_message_dialog_new(GTK_WINDOW(_window),
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||
GTK_MESSAGE_ERROR,
|
||||
GTK_BUTTONS_CLOSE,
|
||||
"Failed to load session file: %s", fn.c_str());
|
||||
gtk_dialog_run(GTK_DIALOG(dialog));
|
||||
gtk_widget_destroy(dialog);
|
||||
return false;
|
||||
}
|
||||
_save_filename = fn;
|
||||
|
||||
gtk_widget_set_sensitive(_new_session_menu_item, TRUE);
|
||||
gtk_widget_set_sensitive(_save_session_menu_item, TRUE);
|
||||
gtk_widget_set_sensitive(_close_session_menu_item, TRUE);
|
||||
gtk_widget_set_sensitive(_export_session_menu_item, TRUE);
|
||||
|
||||
_monitor = monitor;
|
||||
return true;
|
||||
}
|
||||
|
||||
g_object_unref(native);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the last session, if any.
|
||||
*/
|
||||
bool GtkStatsServer::
|
||||
open_last_session() {
|
||||
if (!close_session()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Filename fn = _last_session;
|
||||
GtkStatsMonitor *monitor = new GtkStatsMonitor(this);
|
||||
if (!monitor->read(fn)) {
|
||||
delete monitor;
|
||||
|
||||
GtkWidget *dialog =
|
||||
gtk_message_dialog_new(GTK_WINDOW(_window),
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||
GTK_MESSAGE_ERROR,
|
||||
GTK_BUTTONS_CLOSE,
|
||||
"Failed to load session file: %s", fn.c_str());
|
||||
gtk_dialog_run(GTK_DIALOG(dialog));
|
||||
gtk_widget_destroy(dialog);
|
||||
|
||||
return false;
|
||||
}
|
||||
_monitor = monitor;
|
||||
|
||||
// Enable the "New Session", "Save Session" and "Close Session" menu items.
|
||||
gtk_widget_set_sensitive(_new_session_menu_item, TRUE);
|
||||
gtk_widget_set_sensitive(_save_session_menu_item, TRUE);
|
||||
gtk_widget_set_sensitive(_close_session_menu_item, TRUE);
|
||||
gtk_widget_set_sensitive(_export_session_menu_item, TRUE);
|
||||
|
||||
// If the file contained no graphs, open the default graphs.
|
||||
if (monitor->_graphs.empty()) {
|
||||
monitor->open_default_graphs();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offers to save the current session.
|
||||
*/
|
||||
bool GtkStatsServer::
|
||||
save_session() {
|
||||
nassertr_always(_monitor != nullptr, true);
|
||||
|
||||
GtkFileChooserNative *native = gtk_file_chooser_native_new(
|
||||
"Save Session",
|
||||
GTK_WINDOW(_window),
|
||||
GTK_FILE_CHOOSER_ACTION_SAVE,
|
||||
"_Save", "Cancel");
|
||||
GtkFileChooser *chooser = GTK_FILE_CHOOSER(native);
|
||||
|
||||
gtk_file_chooser_set_do_overwrite_confirmation(chooser, TRUE);
|
||||
|
||||
GtkFileFilter *filter = gtk_file_filter_new();
|
||||
gtk_file_filter_set_name(filter, "PStats Session Files");
|
||||
gtk_file_filter_add_pattern(filter, "*.pstats");
|
||||
gtk_file_chooser_add_filter(chooser, filter);
|
||||
|
||||
if (_save_filename.empty()) {
|
||||
gtk_file_chooser_set_current_name(chooser, "session.pstats");
|
||||
}
|
||||
else {
|
||||
gtk_file_chooser_set_filename(chooser, _save_filename.c_str());
|
||||
}
|
||||
|
||||
gint res = gtk_native_dialog_run(GTK_NATIVE_DIALOG(native));
|
||||
if (res == GTK_RESPONSE_ACCEPT) {
|
||||
char *buffer = gtk_file_chooser_get_filename(chooser);
|
||||
Filename fn = Filename::from_os_specific(buffer);
|
||||
fn.set_binary();
|
||||
g_free(buffer);
|
||||
g_object_unref(native);
|
||||
|
||||
if (!_monitor->write(fn)) {
|
||||
GtkWidget *dialog =
|
||||
gtk_message_dialog_new(GTK_WINDOW(_window),
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||
GTK_MESSAGE_ERROR,
|
||||
GTK_BUTTONS_CLOSE,
|
||||
"Failed to save session file: %s", fn.c_str());
|
||||
gtk_dialog_run(GTK_DIALOG(dialog));
|
||||
gtk_widget_destroy(dialog);
|
||||
return false;
|
||||
}
|
||||
_save_filename = fn;
|
||||
_monitor->get_client_data()->clear_dirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
g_object_unref(native);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offers to export the current session as a JSON file.
|
||||
*/
|
||||
bool GtkStatsServer::
|
||||
export_session() {
|
||||
nassertr_always(_monitor != nullptr, true);
|
||||
|
||||
GtkFileChooserNative *native = gtk_file_chooser_native_new(
|
||||
"Export Session",
|
||||
GTK_WINDOW(_window),
|
||||
GTK_FILE_CHOOSER_ACTION_SAVE,
|
||||
"_Export", "Cancel");
|
||||
GtkFileChooser *chooser = GTK_FILE_CHOOSER(native);
|
||||
|
||||
gtk_file_chooser_set_do_overwrite_confirmation(chooser, TRUE);
|
||||
|
||||
GtkFileFilter *filter = gtk_file_filter_new();
|
||||
gtk_file_filter_set_name(filter, "JSON files");
|
||||
gtk_file_filter_add_pattern(filter, "*.json");
|
||||
gtk_file_chooser_add_filter(chooser, filter);
|
||||
|
||||
gtk_file_chooser_set_current_name(chooser, "session.json");
|
||||
|
||||
gint res = gtk_native_dialog_run(GTK_NATIVE_DIALOG(native));
|
||||
if (res == GTK_RESPONSE_ACCEPT) {
|
||||
char *buffer = gtk_file_chooser_get_filename(chooser);
|
||||
Filename fn = Filename::from_os_specific(buffer);
|
||||
fn.set_text();
|
||||
g_free(buffer);
|
||||
g_object_unref(native);
|
||||
|
||||
std::ofstream stream;
|
||||
if (!fn.open_write(stream)) {
|
||||
GtkWidget *dialog =
|
||||
gtk_message_dialog_new(GTK_WINDOW(_window),
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||
GTK_MESSAGE_ERROR,
|
||||
GTK_BUTTONS_CLOSE,
|
||||
"Failed to open file for export: %s", fn.c_str());
|
||||
gtk_dialog_run(GTK_DIALOG(dialog));
|
||||
gtk_widget_destroy(dialog);
|
||||
return false;
|
||||
}
|
||||
|
||||
int pid = _monitor->get_client_pid();
|
||||
_monitor->get_client_data()->write_json(stream, std::max(0, pid));
|
||||
stream.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
g_object_unref(native);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the current session.
|
||||
*/
|
||||
bool GtkStatsServer::
|
||||
close_session() {
|
||||
bool wrote_last_session = false;
|
||||
|
||||
if (_monitor != nullptr) {
|
||||
const PStatClientData *client_data = _monitor->get_client_data();
|
||||
if (client_data != nullptr && client_data->is_dirty()) {
|
||||
if (!_monitor->has_read_filename()) {
|
||||
_last_session.make_dir();
|
||||
if (_monitor->write(_last_session)) {
|
||||
nout << "Wrote to " << _last_session << "\n";
|
||||
wrote_last_session = true;
|
||||
}
|
||||
else {
|
||||
nout << "Failed to write to " << _last_session << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
GtkWidget *dialog =
|
||||
gtk_message_dialog_new(GTK_WINDOW(_window),
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||
GTK_MESSAGE_QUESTION,
|
||||
GTK_BUTTONS_YES_NO,
|
||||
"Would you like to save the currently open session?");
|
||||
gint response = gtk_dialog_run(GTK_DIALOG(dialog));
|
||||
gtk_widget_destroy(dialog);
|
||||
|
||||
if (response == GTK_RESPONSE_CANCEL ||
|
||||
(response == GTK_RESPONSE_YES && !save_session())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_monitor->close();
|
||||
_monitor = nullptr;
|
||||
}
|
||||
|
||||
_save_filename = Filename();
|
||||
stop_listening();
|
||||
|
||||
gtk_window_set_title(GTK_WINDOW(_window), "PStats Server");
|
||||
|
||||
if (_status_bar_label != nullptr) {
|
||||
gtk_container_remove(GTK_CONTAINER(_status_bar), _status_bar_label);
|
||||
_status_bar_label = nullptr;
|
||||
}
|
||||
|
||||
gtk_widget_set_sensitive(_new_session_menu_item, TRUE);
|
||||
if (wrote_last_session) {
|
||||
gtk_widget_set_sensitive(_open_last_session_menu_item, TRUE);
|
||||
}
|
||||
gtk_widget_set_sensitive(_save_session_menu_item, FALSE);
|
||||
gtk_widget_set_sensitive(_close_session_menu_item, FALSE);
|
||||
gtk_widget_set_sensitive(_export_session_menu_item, FALSE);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the window handle to the server's window.
|
||||
*/
|
||||
GtkWidget *GtkStatsServer::
|
||||
get_window() const {
|
||||
return _window;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the menu handle to the server's menu bar.
|
||||
*/
|
||||
GtkWidget *GtkStatsServer::
|
||||
get_menu_bar() const {
|
||||
return _menu_bar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the window handle to the server's status bar.
|
||||
*/
|
||||
GtkWidget *GtkStatsServer::
|
||||
get_status_bar() const {
|
||||
return _status_bar;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
int GtkStatsServer::
|
||||
get_time_units() const {
|
||||
return _time_units;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the user selects a new time units from the monitor pulldown
|
||||
* menu, this should adjust the units for all graphs to the indicated mask if
|
||||
* it is a time-based graph.
|
||||
*/
|
||||
void GtkStatsServer::
|
||||
set_time_units(int unit_mask) {
|
||||
_time_units = unit_mask;
|
||||
|
||||
if (_monitor != nullptr) {
|
||||
_monitor->set_time_units(unit_mask);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the window for this monitor.
|
||||
*/
|
||||
void GtkStatsServer::
|
||||
create_window() {
|
||||
_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
gtk_window_set_title(GTK_WINDOW(_window), "PStats Server");
|
||||
gtk_window_set_default_size(GTK_WINDOW(_window), 500, 360);
|
||||
|
||||
// Connect the delete and destroy events, so the user can exit the
|
||||
// application by closing the main window.
|
||||
g_signal_connect(G_OBJECT(_window), "delete_event",
|
||||
G_CALLBACK(+[](GtkWidget *widget, GdkEvent *event, gpointer data) -> gboolean {
|
||||
GtkStatsServer *self = (GtkStatsServer *)data;
|
||||
return self->close_session() ? FALSE : TRUE;
|
||||
}), this);
|
||||
|
||||
g_signal_connect(G_OBJECT(_window), "destroy",
|
||||
G_CALLBACK(+[](GtkWidget *widget, GdkEvent *event, gpointer data) -> gboolean {
|
||||
gtk_main_quit();
|
||||
return FALSE;
|
||||
}), this);
|
||||
|
||||
// Set up the menu.
|
||||
GtkAccelGroup *accel_group = gtk_accel_group_new();
|
||||
gtk_window_add_accel_group(GTK_WINDOW(_window), accel_group);
|
||||
_menu_bar = gtk_menu_bar_new();
|
||||
|
||||
setup_session_menu();
|
||||
setup_options_menu();
|
||||
|
||||
// Pack the menu into the window.
|
||||
GtkWidget *main_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 1);
|
||||
gtk_container_add(GTK_CONTAINER(_window), main_vbox);
|
||||
gtk_box_pack_start(GTK_BOX(main_vbox), _menu_bar, FALSE, TRUE, 0);
|
||||
|
||||
// Create the status bar.
|
||||
_status_bar = gtk_flow_box_new();
|
||||
gtk_flow_box_set_activate_on_single_click(GTK_FLOW_BOX(_status_bar), FALSE);
|
||||
gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(_status_bar), GTK_SELECTION_NONE);
|
||||
g_signal_connect(G_OBJECT(_status_bar), "button_press_event",
|
||||
G_CALLBACK(status_bar_button_event), this);
|
||||
gtk_box_pack_end(GTK_BOX(main_vbox), _status_bar, FALSE, FALSE, 0);
|
||||
|
||||
GtkWidget *sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
|
||||
gtk_box_pack_end(GTK_BOX(main_vbox), sep, FALSE, FALSE, 0);
|
||||
|
||||
gtk_widget_show_all(_window);
|
||||
gtk_widget_show(_window);
|
||||
gtk_widget_realize(_window);
|
||||
|
||||
// Set up a timer to poll the pstats every so often.
|
||||
g_timeout_add(200,
|
||||
G_SOURCE_FUNC(+[](gpointer data) -> gboolean {
|
||||
GtkStatsServer *self = (GtkStatsServer *)data;
|
||||
self->poll();
|
||||
return TRUE;
|
||||
}), this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the "Session" pulldown menu.
|
||||
*/
|
||||
void GtkStatsServer::
|
||||
setup_session_menu() {
|
||||
_session_menu = gtk_menu_new();
|
||||
|
||||
GtkWidget *item = gtk_menu_item_new_with_mnemonic("_Session");
|
||||
gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), _session_menu);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_menu_bar), item);
|
||||
|
||||
item = gtk_menu_item_new_with_mnemonic("_New Session");
|
||||
_new_session_menu_item = item;
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_session_menu), item);
|
||||
g_signal_connect(G_OBJECT(item), "activate",
|
||||
G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
|
||||
GtkStatsServer *self = (GtkStatsServer *)data;
|
||||
self->new_session();
|
||||
}), this);
|
||||
|
||||
item = gtk_menu_item_new_with_mnemonic("_Open Session...");
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_session_menu), item);
|
||||
g_signal_connect(G_OBJECT(item), "activate",
|
||||
G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
|
||||
GtkStatsServer *self = (GtkStatsServer *)data;
|
||||
self->open_session();
|
||||
}), this);
|
||||
|
||||
item = gtk_menu_item_new_with_mnemonic("Open _Last Session");
|
||||
_open_last_session_menu_item = item;
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_session_menu), item);
|
||||
g_signal_connect(G_OBJECT(item), "activate",
|
||||
G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
|
||||
GtkStatsServer *self = (GtkStatsServer *)data;
|
||||
self->open_last_session();
|
||||
}), this);
|
||||
|
||||
if (!_last_session.exists()) {
|
||||
gtk_widget_set_sensitive(item, FALSE);
|
||||
}
|
||||
|
||||
item = gtk_menu_item_new_with_mnemonic("_Save Session...");
|
||||
_save_session_menu_item = item;
|
||||
gtk_widget_set_sensitive(item, FALSE);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_session_menu), item);
|
||||
g_signal_connect(G_OBJECT(item), "activate",
|
||||
G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
|
||||
GtkStatsServer *self = (GtkStatsServer *)data;
|
||||
self->save_session();
|
||||
}), this);
|
||||
|
||||
item = gtk_menu_item_new_with_mnemonic("_Close Session");
|
||||
_close_session_menu_item = item;
|
||||
gtk_widget_set_sensitive(item, FALSE);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_session_menu), item);
|
||||
g_signal_connect(G_OBJECT(item), "activate",
|
||||
G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
|
||||
GtkStatsServer *self = (GtkStatsServer *)data;
|
||||
self->close_session();
|
||||
}), this);
|
||||
|
||||
GtkWidget *sep = gtk_separator_menu_item_new();
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_session_menu), sep);
|
||||
|
||||
item = gtk_menu_item_new_with_mnemonic("_Export as JSON...");
|
||||
_export_session_menu_item = item;
|
||||
gtk_widget_set_sensitive(item, FALSE);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_session_menu), item);
|
||||
g_signal_connect(G_OBJECT(item), "activate",
|
||||
G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
|
||||
GtkStatsServer *self = (GtkStatsServer *)data;
|
||||
self->export_session();
|
||||
}), this);
|
||||
|
||||
sep = gtk_separator_menu_item_new();
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_session_menu), sep);
|
||||
|
||||
item = gtk_menu_item_new_with_mnemonic("E_xit");
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_session_menu), item);
|
||||
g_signal_connect(G_OBJECT(item), "activate",
|
||||
G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
|
||||
GtkStatsServer *self = (GtkStatsServer *)data;
|
||||
if (self->close_session()) {
|
||||
gtk_main_quit();
|
||||
}
|
||||
}), this);
|
||||
|
||||
gtk_widget_show_all(_session_menu);
|
||||
|
||||
/*
|
||||
_session_menu = CreatePopupMenu();
|
||||
|
||||
MENUITEMINFO mii;
|
||||
memset(&mii, 0, sizeof(mii));
|
||||
mii.cbSize = sizeof(mii);
|
||||
|
||||
mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_SUBMENU;
|
||||
mii.fType = MFT_STRING;
|
||||
mii.hSubMenu = _session_menu;
|
||||
|
||||
mii.dwTypeData = "&Session";
|
||||
InsertMenuItem(_menu_bar, GetMenuItemCount(_menu_bar), TRUE, &mii);
|
||||
|
||||
AppendMenu(_session_menu, MF_STRING, MI_session_new, "&New Session\tCtrl+N");
|
||||
AppendMenu(_session_menu, MF_STRING, MI_session_open, "&Open Session...\tCtrl+O");
|
||||
|
||||
if (_last_session.exists()) {
|
||||
AppendMenu(_session_menu, MF_STRING, MI_session_open_last, "Open &Last Session");
|
||||
} else {
|
||||
AppendMenu(_session_menu, MF_STRING | MF_DISABLED, MI_session_open_last, "Open &Last Session");
|
||||
}
|
||||
|
||||
AppendMenu(_session_menu, MF_STRING | MF_DISABLED, MI_session_save, "&Save Session...\tCtrl+S");
|
||||
AppendMenu(_session_menu, MF_STRING | MF_DISABLED, MI_session_close, "&Close Session\tCtrl+W");
|
||||
|
||||
AppendMenu(_session_menu, MF_SEPARATOR, 0, nullptr);
|
||||
AppendMenu(_session_menu, MF_STRING | MF_DISABLED, MI_session_export_json, "&Export as JSON...");
|
||||
|
||||
AppendMenu(_session_menu, MF_SEPARATOR, 0, nullptr);
|
||||
AppendMenu(_session_menu, MF_STRING, MI_exit, "E&xit");*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the "Options" pulldown menu.
|
||||
*/
|
||||
void GtkStatsServer::
|
||||
setup_options_menu() {
|
||||
_options_menu = gtk_menu_new();
|
||||
|
||||
GtkWidget *item = gtk_menu_item_new_with_label("Options");
|
||||
gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), _options_menu);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_menu_bar), item);
|
||||
|
||||
GtkWidget *units_menu = gtk_menu_new();
|
||||
item = gtk_menu_item_new_with_label("Units");
|
||||
gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), units_menu);
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(_options_menu), item);
|
||||
|
||||
item = gtk_radio_menu_item_new_with_label(nullptr, "ms");
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(units_menu), item);
|
||||
g_signal_connect(G_OBJECT(item), "activate",
|
||||
G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
|
||||
GtkStatsServer *self = (GtkStatsServer *)data;
|
||||
self->set_time_units(PStatGraph::GBU_ms);
|
||||
}), this);
|
||||
|
||||
item = gtk_radio_menu_item_new_with_label(
|
||||
gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item)), "Hz");
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(units_menu), item);
|
||||
g_signal_connect(G_OBJECT(item), "activate",
|
||||
G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
|
||||
GtkStatsServer *self = (GtkStatsServer *)data;
|
||||
self->set_time_units(PStatGraph::GBU_hz);
|
||||
}), this);
|
||||
|
||||
set_time_units(PStatGraph::GBU_ms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicks on a partion of the status bar.
|
||||
*/
|
||||
gboolean GtkStatsServer::
|
||||
status_bar_button_event(GtkWidget *widget, GdkEventButton *event, gpointer data) {
|
||||
GtkStatsServer *server = (GtkStatsServer *)data;
|
||||
|
||||
GtkFlowBoxChild *child = gtk_flow_box_get_child_at_pos(
|
||||
GTK_FLOW_BOX(server->_status_bar), event->x, event->y);
|
||||
if (child == nullptr) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Which child is this?
|
||||
GList *children = gtk_container_get_children(GTK_CONTAINER(server->_status_bar));
|
||||
int index = g_list_index(children, child);
|
||||
g_list_free(children);
|
||||
if (index < 0) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
|
||||
server->_monitor->handle_status_bar_click(index);
|
||||
return TRUE;
|
||||
}
|
||||
else if (event->type == GDK_BUTTON_PRESS && event->button == 3 && index > 0) {
|
||||
server->_monitor->handle_status_bar_popup(index);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,13 +16,59 @@
|
|||
|
||||
#include "pandatoolbase.h"
|
||||
#include "pStatServer.h"
|
||||
#include "gtkStatsMonitor.h"
|
||||
|
||||
/**
|
||||
* The class that owns the main loop, waiting for client connections.
|
||||
*/
|
||||
class GtkStatsServer : public PStatServer {
|
||||
public:
|
||||
virtual PStatMonitor *make_monitor();
|
||||
GtkStatsServer();
|
||||
|
||||
virtual PStatMonitor *make_monitor(const NetAddress &address);
|
||||
virtual void lost_connection(PStatMonitor *monitor);
|
||||
|
||||
bool new_session();
|
||||
bool open_session();
|
||||
bool open_last_session();
|
||||
bool save_session();
|
||||
bool export_session();
|
||||
bool close_session();
|
||||
|
||||
GtkWidget *get_window() const;
|
||||
GtkWidget *get_menu_bar() const;
|
||||
GtkWidget *get_status_bar() const;
|
||||
|
||||
int get_time_units() const;
|
||||
void set_time_units(int unit_mask);
|
||||
|
||||
private:
|
||||
void create_window();
|
||||
void setup_session_menu();
|
||||
void setup_options_menu();
|
||||
|
||||
static gboolean status_bar_button_event(GtkWidget *widget,
|
||||
GdkEventButton *event,
|
||||
gpointer data);
|
||||
|
||||
private:
|
||||
PT(GtkStatsMonitor) _monitor;
|
||||
|
||||
Filename _last_session;
|
||||
Filename _save_filename;
|
||||
|
||||
GtkWidget *_window;
|
||||
GtkWidget *_menu_bar;
|
||||
GtkWidget *_session_menu;
|
||||
GtkWidget *_options_menu;
|
||||
GtkWidget *_status_bar;
|
||||
GtkWidget *_status_bar_label = nullptr;
|
||||
GtkWidget *_new_session_menu_item;
|
||||
GtkWidget *_open_last_session_menu_item;
|
||||
GtkWidget *_save_session_menu_item;
|
||||
GtkWidget *_close_session_menu_item;
|
||||
GtkWidget *_export_session_menu_item;
|
||||
int _time_units;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -25,9 +25,7 @@ static const int default_strip_chart_height = 100;
|
|||
GtkStatsStripChart::
|
||||
GtkStatsStripChart(GtkStatsMonitor *monitor, int thread_index,
|
||||
int collector_index, bool show_level) :
|
||||
PStatStripChart(monitor,
|
||||
show_level ? monitor->get_level_view(0, thread_index) : monitor->get_view(thread_index),
|
||||
thread_index, collector_index, 0, 0),
|
||||
PStatStripChart(monitor, thread_index, collector_index, show_level, 0, 0),
|
||||
GtkStatsGraph(monitor, true)
|
||||
{
|
||||
if (show_level) {
|
||||
|
|
@ -84,6 +82,8 @@ GtkStatsStripChart(GtkStatsMonitor *monitor, int thread_index,
|
|||
gtk_widget_set_size_request(_graph_window, 0, 0);
|
||||
|
||||
clear_region();
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -261,6 +261,35 @@ on_popup_label(int collector_index) {
|
|||
(void *)menu_def);
|
||||
}
|
||||
|
||||
{
|
||||
GtkWidget *menu_item = gtk_separator_menu_item_new();
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
|
||||
}
|
||||
|
||||
{
|
||||
const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
|
||||
-1, collector_index, GtkStatsMonitor::CT_choose_color,
|
||||
});
|
||||
|
||||
GtkWidget *menu_item = gtk_menu_item_new_with_label("Change Color...");
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
|
||||
g_signal_connect(G_OBJECT(menu_item), "activate",
|
||||
G_CALLBACK(GtkStatsMonitor::menu_activate),
|
||||
(void *)menu_def);
|
||||
}
|
||||
|
||||
{
|
||||
const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
|
||||
-1, collector_index, GtkStatsMonitor::CT_reset_color,
|
||||
});
|
||||
|
||||
GtkWidget *menu_item = gtk_menu_item_new_with_label("Reset Color");
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
|
||||
g_signal_connect(G_OBJECT(menu_item), "activate",
|
||||
G_CALLBACK(GtkStatsMonitor::menu_activate),
|
||||
(void *)menu_def);
|
||||
}
|
||||
|
||||
gtk_widget_show_all(menu);
|
||||
gtk_menu_popup_at_pointer(GTK_MENU(menu), nullptr);
|
||||
}
|
||||
|
|
@ -416,6 +445,25 @@ end_draw(int from_x, int to_x) {
|
|||
gdk_window_invalidate_rect(window, &rect, FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current window dimensions.
|
||||
*/
|
||||
bool GtkStatsStripChart::
|
||||
get_window_state(int &x, int &y, int &width, int &height,
|
||||
bool &maximized, bool &minimized) const {
|
||||
GtkStatsGraph::get_window_state(x, y, width, height, maximized, minimized);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to restore the graph window to its previous dimensions.
|
||||
*/
|
||||
void GtkStatsStripChart::
|
||||
set_window_state(int x, int y, int width, int height,
|
||||
bool maximized, bool minimized) {
|
||||
GtkStatsGraph::set_window_state(x, y, width, height, maximized, minimized);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called during the servicing of the draw event; it gives a derived
|
||||
* class opportunity to do some further painting into the graph window.
|
||||
|
|
|
|||
|
|
@ -56,6 +56,11 @@ protected:
|
|||
virtual void draw_cursor(int x);
|
||||
virtual void end_draw(int from_x, int to_x);
|
||||
|
||||
virtual bool get_window_state(int &x, int &y, int &width, int &height,
|
||||
bool &maximized, bool &minimized) const;
|
||||
virtual void set_window_state(int x, int y, int width, int height,
|
||||
bool maximized, bool minimized);
|
||||
|
||||
virtual void additional_graph_window_paint(cairo_t *cr);
|
||||
virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const;
|
||||
virtual DragMode consider_drag_start(int graph_x, int graph_y);
|
||||
|
|
|
|||
|
|
@ -312,6 +312,25 @@ animate(double time, double dt) {
|
|||
return PStatTimeline::animate(time, dt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current window dimensions.
|
||||
*/
|
||||
bool GtkStatsTimeline::
|
||||
get_window_state(int &x, int &y, int &width, int &height,
|
||||
bool &maximized, bool &minimized) const {
|
||||
GtkStatsGraph::get_window_state(x, y, width, height, maximized, minimized);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to restore the graph window to its previous dimensions.
|
||||
*/
|
||||
void GtkStatsTimeline::
|
||||
set_window_state(int x, int y, int width, int height,
|
||||
bool maximized, bool minimized) {
|
||||
GtkStatsGraph::set_window_state(x, y, width, height, maximized, minimized);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called during the servicing of the draw event; it gives a derived
|
||||
* class opportunity to do some further painting into the graph window.
|
||||
|
|
@ -414,6 +433,37 @@ handle_button_press(int graph_x, int graph_y, bool double_click, int button) {
|
|||
(void *)menu_def);
|
||||
}
|
||||
|
||||
{
|
||||
GtkWidget *menu_item = gtk_separator_menu_item_new();
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
|
||||
}
|
||||
|
||||
{
|
||||
const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
|
||||
-1, bar._collector_index,
|
||||
GtkStatsMonitor::CT_choose_color,
|
||||
});
|
||||
|
||||
GtkWidget *menu_item = gtk_menu_item_new_with_label("Change Color...");
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
|
||||
g_signal_connect(G_OBJECT(menu_item), "activate",
|
||||
G_CALLBACK(GtkStatsMonitor::menu_activate),
|
||||
(void *)menu_def);
|
||||
}
|
||||
|
||||
{
|
||||
const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
|
||||
-1, bar._collector_index,
|
||||
GtkStatsMonitor::CT_reset_color,
|
||||
});
|
||||
|
||||
GtkWidget *menu_item = gtk_menu_item_new_with_label("Reset Color");
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
|
||||
g_signal_connect(G_OBJECT(menu_item), "activate",
|
||||
G_CALLBACK(GtkStatsMonitor::menu_activate),
|
||||
(void *)menu_def);
|
||||
}
|
||||
|
||||
gtk_widget_show_all(menu);
|
||||
gtk_menu_popup_at_pointer(GTK_MENU(menu), nullptr);
|
||||
return TRUE;
|
||||
|
|
|
|||
|
|
@ -47,6 +47,11 @@ protected:
|
|||
|
||||
virtual bool animate(double time, double dt);
|
||||
|
||||
virtual bool get_window_state(int &x, int &y, int &width, int &height,
|
||||
bool &maximized, bool &minimized) const;
|
||||
virtual void set_window_state(int x, int y, int width, int height,
|
||||
bool maximized, bool minimized);
|
||||
|
||||
virtual void additional_graph_window_paint(cairo_t *cr);
|
||||
virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const;
|
||||
virtual DragMode consider_drag_start(int graph_x, int graph_y);
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
*/
|
||||
|
||||
#include "pStatClientData.h"
|
||||
#include "pStatFrameData.h"
|
||||
#include "pStatReader.h"
|
||||
|
||||
#include "pStatCollectorDef.h"
|
||||
|
|
@ -20,16 +21,15 @@ using std::string;
|
|||
|
||||
PStatCollectorDef PStatClientData::_null_collector(-1, "Unknown");
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
PStatClientData::
|
||||
PStatClientData(PStatReader *reader) :
|
||||
_is_alive(true),
|
||||
_is_dirty(false),
|
||||
_reader(reader)
|
||||
{
|
||||
_is_alive = true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -37,12 +37,28 @@ PStatClientData(PStatReader *reader) :
|
|||
*/
|
||||
PStatClientData::
|
||||
~PStatClientData() {
|
||||
Collectors::const_iterator ci;
|
||||
for (ci = _collectors.begin(); ci != _collectors.end(); ++ci) {
|
||||
delete (*ci)._def;
|
||||
for (Collector &collector : _collectors) {
|
||||
delete collector._def;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the is_dirty() flag.
|
||||
*/
|
||||
void PStatClientData::
|
||||
clear_dirty() const {
|
||||
_is_dirty = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the data was modified since the last time clear_dirty() was
|
||||
* called.
|
||||
*/
|
||||
bool PStatClientData::
|
||||
is_dirty() const {
|
||||
return _is_dirty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the data is actively getting filled by a connected client,
|
||||
* or false if the client has terminated.
|
||||
|
|
@ -83,6 +99,28 @@ has_collector(int index) const {
|
|||
_collectors[index]._def != nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the collector with the given full name, or -1 if no
|
||||
* such collector has been defined by the client.
|
||||
*/
|
||||
int PStatClientData::
|
||||
find_collector(const std::string &fullname) const {
|
||||
// Take the last bit, we can compare it more cheaply, only check the full
|
||||
// name if the basename matches.
|
||||
const char *colon = strrchr(fullname.c_str(), ':');
|
||||
std::string name(colon != nullptr ? colon + 1 : fullname.c_str());
|
||||
|
||||
for (int index = 0; index < get_num_collectors(); ++index) {
|
||||
const PStatCollectorDef *def = _collectors[index]._def;
|
||||
if (def != nullptr && def->_name == name &&
|
||||
get_collector_fullname(index) == fullname) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nth collector definition.
|
||||
*/
|
||||
|
|
@ -153,6 +191,10 @@ set_collector_has_level(int index, int thread_index, bool flag) {
|
|||
}
|
||||
}
|
||||
|
||||
if (any_changed) {
|
||||
_is_dirty = true;
|
||||
}
|
||||
|
||||
return any_changed;
|
||||
}
|
||||
|
||||
|
|
@ -206,6 +248,21 @@ has_thread(int index) const {
|
|||
!_threads[index]._name.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the thread with the given name, or -1 if no such thread
|
||||
* has yet been defined.
|
||||
*/
|
||||
int PStatClientData::
|
||||
find_thread(const std::string &name) const {
|
||||
for (int index = 0; index < get_num_threads(); ++index) {
|
||||
if (_threads[index]._name == name) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the indicated thread.
|
||||
*/
|
||||
|
|
@ -280,6 +337,8 @@ add_collector(PStatCollectorDef *def) {
|
|||
set_collector_has_level(def->_parent_index, thread_index, true);
|
||||
}
|
||||
}
|
||||
|
||||
_is_dirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -303,8 +362,9 @@ define_thread(int thread_index, const string &name) {
|
|||
if (_threads[thread_index]._data.is_null()) {
|
||||
_threads[thread_index]._data = new PStatThreadData(this);
|
||||
}
|
||||
}
|
||||
|
||||
_is_dirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes room for and stores a new frame's worth of data associated with some
|
||||
|
|
@ -319,6 +379,102 @@ record_new_frame(int thread_index, int frame_number,
|
|||
define_thread(thread_index);
|
||||
nassertv(thread_index >= 0 && thread_index < (int)_threads.size());
|
||||
_threads[thread_index]._data->record_new_frame(frame_number, frame_data);
|
||||
_is_dirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the client data in the form of a JSON output that can be loaded into
|
||||
* Chrome's event tracer.
|
||||
*/
|
||||
void PStatClientData::
|
||||
write_json(std::ostream &out, int pid) const {
|
||||
out << "[\n";
|
||||
|
||||
for (int thread_index = 0; thread_index < get_num_threads(); ++thread_index) {
|
||||
const Thread &thread = _threads[thread_index];
|
||||
|
||||
if (thread_index == 0) {
|
||||
out << "{\"name\":\"thread_name\",\"ph\":\"M\",\"pid\":" << pid
|
||||
<< ",\"tid\":0,\"args\":{\"name\":\"Main\"}}";
|
||||
}
|
||||
else if (!thread._name.empty()) {
|
||||
out
|
||||
<< ",\n{\"name\":\"thread_name\",\"ph\":\"M\",\"pid\":" << pid
|
||||
<< ",\"tid\":" << thread_index << ",\"args\":{\"name\":\""
|
||||
<< thread._name << "\"}}";
|
||||
}
|
||||
if (thread._data == nullptr || thread._data->is_empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int first_frame = thread._data->get_oldest_frame_number();
|
||||
int last_frame = thread._data->get_latest_frame_number();
|
||||
for (int frame_number = first_frame; frame_number <= last_frame; ++frame_number) {
|
||||
const PStatFrameData &frame_data = thread._data->get_frame(frame_number);
|
||||
size_t num_events = frame_data.get_num_events();
|
||||
for (size_t i = 0; i < num_events; ++i) {
|
||||
int collector_index = frame_data.get_time_collector(i);
|
||||
out
|
||||
<< ",\n{\"name\":\"" << get_collector_fullname(collector_index)
|
||||
<< "\",\"ts\":" << (uint64_t)(frame_data.get_time(i) * 1000000)
|
||||
<< ",\"ph\":\"" << (frame_data.is_start(i) ? 'B' : 'E') << "\""
|
||||
<< ",\"pid\":" << pid << ",\"tid\":" << thread_index << "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out << "\n]\n";
|
||||
out.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the client data to a datagram.
|
||||
*/
|
||||
void PStatClientData::
|
||||
write_datagram(Datagram &dg) const {
|
||||
for (const Collector &collector : _collectors) {
|
||||
PStatCollectorDef *def = collector._def;
|
||||
if (def != nullptr && def->_index != -1) {
|
||||
def->write_datagram(dg);
|
||||
collector._is_level.write_datagram(nullptr, dg);
|
||||
}
|
||||
}
|
||||
dg.add_int16(-1);
|
||||
|
||||
int thread_index = 0;
|
||||
for (const Thread &thread : _threads) {
|
||||
if (thread._data != nullptr) {
|
||||
dg.add_int16(thread_index);
|
||||
dg.add_string(thread._name);
|
||||
thread._data->write_datagram(dg);
|
||||
}
|
||||
++thread_index;
|
||||
}
|
||||
dg.add_int16(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the client data from a datagram.
|
||||
*/
|
||||
void PStatClientData::
|
||||
read_datagram(DatagramIterator &scan) {
|
||||
while (scan.peek_int16() != -1) {
|
||||
PStatCollectorDef *def = new PStatCollectorDef;
|
||||
def->read_datagram(scan);
|
||||
add_collector(def);
|
||||
_collectors[def->_index]._is_level.read_datagram(scan, nullptr);
|
||||
}
|
||||
scan.skip_bytes(2);
|
||||
|
||||
int thread_index;
|
||||
while ((thread_index = scan.get_int16()) != -1) {
|
||||
std::string name = scan.get_string();
|
||||
define_thread(thread_index, name);
|
||||
|
||||
_threads[thread_index]._data->read_datagram(scan);
|
||||
}
|
||||
|
||||
update_toplevel_collectors();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -344,9 +500,8 @@ void PStatClientData::
|
|||
update_toplevel_collectors() {
|
||||
_toplevel_collectors.clear();
|
||||
|
||||
Collectors::const_iterator ci;
|
||||
for (ci = _collectors.begin(); ci != _collectors.end(); ++ci) {
|
||||
PStatCollectorDef *def = (*ci)._def;
|
||||
for (Collector &collector : _collectors) {
|
||||
PStatCollectorDef *def = collector._def;
|
||||
if (def != nullptr && def->_parent_index == 0) {
|
||||
_toplevel_collectors.push_back(def->_index);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,14 +35,19 @@ class PStatReader;
|
|||
*/
|
||||
class PStatClientData : public PStatClientVersion {
|
||||
public:
|
||||
PStatClientData() = default;
|
||||
PStatClientData(PStatReader *reader);
|
||||
~PStatClientData();
|
||||
|
||||
void clear_dirty() const;
|
||||
bool is_dirty() const;
|
||||
|
||||
bool is_alive() const;
|
||||
void close();
|
||||
|
||||
int get_num_collectors() const;
|
||||
bool has_collector(int index) const;
|
||||
int find_collector(const std::string &fullname) const;
|
||||
const PStatCollectorDef &get_collector_def(int index) const;
|
||||
std::string get_collector_name(int index) const;
|
||||
std::string get_collector_fullname(int index) const;
|
||||
|
|
@ -54,6 +59,7 @@ public:
|
|||
|
||||
int get_num_threads() const;
|
||||
bool has_thread(int index) const;
|
||||
int find_thread(const std::string &name) const;
|
||||
std::string get_thread_name(int index) const;
|
||||
const PStatThreadData *get_thread_data(int index) const;
|
||||
|
||||
|
|
@ -65,13 +71,19 @@ public:
|
|||
|
||||
void record_new_frame(int thread_index, int frame_number,
|
||||
PStatFrameData *frame_data);
|
||||
|
||||
void write_json(std::ostream &out, int pid = 0) const;
|
||||
void write_datagram(Datagram &dg) const;
|
||||
void read_datagram(DatagramIterator &scan);
|
||||
|
||||
private:
|
||||
void slot_collector(int collector_index);
|
||||
void update_toplevel_collectors();
|
||||
|
||||
private:
|
||||
bool _is_alive;
|
||||
PStatReader *_reader;
|
||||
bool _is_alive = false;
|
||||
mutable bool _is_dirty = false;
|
||||
PStatReader *_reader = nullptr;
|
||||
|
||||
class Collector {
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -34,14 +34,22 @@ PStatFlameGraph(PStatMonitor *monitor,
|
|||
{
|
||||
_average_mode = true;
|
||||
_average_cursor = 0;
|
||||
|
||||
_time_width = 1.0 / pstats_target_frame_rate;
|
||||
_current_frame = -1;
|
||||
|
||||
_title_unknown = true;
|
||||
|
||||
// NB. This won't call force_redraw() (which we can't do yet) because average
|
||||
// mode is true
|
||||
update_data();
|
||||
_time_width = _stack.get_net_value(false);
|
||||
if (_time_width == 0.0) {
|
||||
_time_width = 1.0 / pstats_target_frame_rate;
|
||||
}
|
||||
|
||||
_guide_bar_units = GBU_ms | GBU_hz | GBU_show_units;
|
||||
normal_guide_bars();
|
||||
|
||||
monitor->_flame_graphs.insert(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -49,6 +57,7 @@ PStatFlameGraph(PStatMonitor *monitor,
|
|||
*/
|
||||
PStatFlameGraph::
|
||||
~PStatFlameGraph() {
|
||||
_monitor->_flame_graphs.erase(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -97,6 +106,15 @@ set_collector_index(int collector_index) {
|
|||
_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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -167,6 +185,34 @@ get_bar_collector(int depth, int x) const {
|
|||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the graph state to a datagram.
|
||||
*/
|
||||
void PStatFlameGraph::
|
||||
write_datagram(Datagram &dg) const {
|
||||
dg.add_int16(_orig_collector_index);
|
||||
dg.add_float64(_time_width);
|
||||
dg.add_bool(_average_mode);
|
||||
|
||||
PStatGraph::write_datagram(dg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the graph state from a datagram.
|
||||
*/
|
||||
void PStatFlameGraph::
|
||||
read_datagram(DatagramIterator &scan) {
|
||||
_orig_collector_index = scan.get_int16();
|
||||
_time_width = scan.get_float64();
|
||||
_average_mode = scan.get_bool();
|
||||
|
||||
PStatGraph::read_datagram(scan);
|
||||
|
||||
_current_frame = -1;
|
||||
normal_guide_bars();
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
@ -336,9 +382,11 @@ animate(double time, double dt) {
|
|||
_time_width = 1.0 / pstats_target_frame_rate;
|
||||
}
|
||||
normal_guide_bars();
|
||||
force_redraw();
|
||||
}
|
||||
|
||||
// Always use force_redraw, since the mouse position may have changed.
|
||||
force_redraw();
|
||||
|
||||
// Cycle through the ring buffers.
|
||||
_average_cursor = (_average_cursor + 1) % _num_average_frames;
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -60,6 +60,9 @@ public:
|
|||
std::string get_bar_tooltip(int depth, int x) const;
|
||||
int get_bar_collector(int depth, int x) const;
|
||||
|
||||
virtual void write_datagram(Datagram &dg) const final;
|
||||
virtual void read_datagram(DatagramIterator &scan) final;
|
||||
|
||||
protected:
|
||||
void update_data();
|
||||
void changed_size(int xsize, int ysize);
|
||||
|
|
|
|||
|
|
@ -189,8 +189,8 @@ format_number(double value, int guide_bar_units, const string &unit_name) {
|
|||
label += " ";
|
||||
label += unit_name;
|
||||
}
|
||||
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// Units are either milliseconds or hz, or both.
|
||||
if ((guide_bar_units & GBU_ms) != 0) {
|
||||
if ((guide_bar_units & GBU_show_units) != 0 &&
|
||||
|
|
@ -225,15 +225,36 @@ format_number(double value, int guide_bar_units, const string &unit_name) {
|
|||
if ((guide_bar_units & GBU_hz) != 0) {
|
||||
double hz = 1.0 / value;
|
||||
|
||||
if ((guide_bar_units & GBU_ms) != 0) {
|
||||
label += " (";
|
||||
if ((guide_bar_units & GBU_show_units) != 0 &&
|
||||
(guide_bar_units & GBU_ms) == 0) {
|
||||
if (hz >= 1000000000) {
|
||||
label += format_number(hz / 1000000000);
|
||||
label += " GHz";
|
||||
}
|
||||
else if (hz >= 1000000) {
|
||||
label += format_number(hz / 1000000);
|
||||
label += " MHz";
|
||||
}
|
||||
else if (hz >= 1000) {
|
||||
label += format_number(hz / 1000);
|
||||
label += " kHz";
|
||||
}
|
||||
else {
|
||||
label += format_number(hz);
|
||||
label += " Hz";
|
||||
}
|
||||
}
|
||||
label += format_number(hz);
|
||||
if ((guide_bar_units & GBU_show_units) != 0) {
|
||||
label += " Hz";
|
||||
}
|
||||
if ((guide_bar_units & GBU_ms) != 0) {
|
||||
label += ")";
|
||||
else {
|
||||
if ((guide_bar_units & GBU_ms) != 0) {
|
||||
label += " (";
|
||||
}
|
||||
label += format_number(hz);
|
||||
if ((guide_bar_units & GBU_show_units) != 0) {
|
||||
label += " Hz";
|
||||
}
|
||||
if ((guide_bar_units & GBU_ms) != 0) {
|
||||
label += ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -241,6 +262,43 @@ format_number(double value, int guide_bar_units, const string &unit_name) {
|
|||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the graph state to a datagram.
|
||||
*/
|
||||
void PStatGraph::
|
||||
write_datagram(Datagram &dg) const {
|
||||
int x, y, width, height;
|
||||
bool minimized, maximized;
|
||||
if (get_window_state(x, y, width, height, minimized, maximized)) {
|
||||
dg.add_bool(true);
|
||||
dg.add_int32(x);
|
||||
dg.add_int32(y);
|
||||
dg.add_int32(width);
|
||||
dg.add_int32(height);
|
||||
dg.add_bool(minimized);
|
||||
dg.add_bool(maximized);
|
||||
}
|
||||
else {
|
||||
dg.add_bool(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the graph state from a datagram.
|
||||
*/
|
||||
void PStatGraph::
|
||||
read_datagram(DatagramIterator &scan) {
|
||||
if (scan.get_bool()) {
|
||||
int x = scan.get_int32();
|
||||
int y = scan.get_int32();
|
||||
int width = scan.get_int32();
|
||||
int height = scan.get_int32();
|
||||
bool minimized = scan.get_bool();
|
||||
bool maximized = scan.get_bool();
|
||||
set_window_state(x, y, width, height, minimized, maximized);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the list of guide bars.
|
||||
*/
|
||||
|
|
@ -294,3 +352,20 @@ make_guide_bar(double value, PStatGraph::GuideBarStyle style) const {
|
|||
|
||||
return GuideBar(value, label, style);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current window dimensions.
|
||||
*/
|
||||
bool PStatGraph::
|
||||
get_window_state(int &x, int &y, int &width, int &height,
|
||||
bool &maximized, bool &minimized) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to restore the graph window to its previous dimensions.
|
||||
*/
|
||||
void PStatGraph::
|
||||
set_window_state(int x, int y, int width, int height,
|
||||
bool maximized, bool minimized) {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,6 +91,14 @@ public:
|
|||
static std::string format_number(double value, int guide_bar_units,
|
||||
const std::string &unit_name = std::string());
|
||||
|
||||
virtual void write_datagram(Datagram &dg) const;
|
||||
virtual void read_datagram(DatagramIterator &scan);
|
||||
|
||||
virtual bool get_window_state(int &x, int &y, int &width, int &height,
|
||||
bool &maximized, bool &minimized) const;
|
||||
virtual void set_window_state(int x, int y, int width, int height,
|
||||
bool maximized, bool minimized);
|
||||
|
||||
protected:
|
||||
virtual void normal_guide_bars()=0;
|
||||
void update_guide_bars(int num_bars, double scale);
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ void PStatListener::
|
|||
connection_opened(const PT(Connection) &,
|
||||
const NetAddress &address,
|
||||
const PT(Connection) &new_connection) {
|
||||
PStatMonitor *monitor = _manager->make_monitor();
|
||||
PStatMonitor *monitor = _manager->make_monitor(address);
|
||||
if (monitor == nullptr) {
|
||||
nout << "Couldn't create monitor!\n";
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -77,3 +77,19 @@ INLINE int PStatMonitor::
|
|||
get_client_pid() const {
|
||||
return _client_pid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the session data was loaded from a file.
|
||||
*/
|
||||
INLINE bool PStatMonitor::
|
||||
has_read_filename() const {
|
||||
return !_read_filename.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the filename this session data was loaded from.
|
||||
*/
|
||||
INLINE const Filename &PStatMonitor::
|
||||
get_read_filename() const {
|
||||
return _read_filename;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,10 +13,29 @@
|
|||
|
||||
#include "pStatMonitor.h"
|
||||
|
||||
#include "datagramInputFile.h"
|
||||
#include "datagramOutputFile.h"
|
||||
#include "pandaVersion.h"
|
||||
#include "pStatCollectorDef.h"
|
||||
#include "pStatTimeline.h"
|
||||
#include "pStatStripChart.h"
|
||||
#include "pStatFlameGraph.h"
|
||||
#include "pStatPianoRoll.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
static const string session_file_header("pstat\0\n\r", 8);
|
||||
static const string layout_file_header("pslyt\0\n\r", 8);
|
||||
|
||||
static const Filename layout_filename = Filename::binary_filename(
|
||||
#ifdef _WIN32
|
||||
Filename::expand_from("$USER_APPDATA/Panda3D-" PANDA_ABI_VERSION_STR "/pstats-layout")
|
||||
#elif defined(__APPLE__)
|
||||
Filename::expand_from("$HOME/Library/Caches/Panda3D-" PANDA_ABI_VERSION_STR "/pstats-layout")
|
||||
#else
|
||||
Filename::expand_from("$XDG_CACHE_HOME/panda3d/pstats-layout")
|
||||
#endif
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -31,6 +50,7 @@ PStatMonitor(PStatServer *server) : _server(server) {
|
|||
*/
|
||||
PStatMonitor::
|
||||
~PStatMonitor() {
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -74,6 +94,283 @@ set_client_data(PStatClientData *client_data) {
|
|||
initialized();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the data and the UI state to the given file.
|
||||
*/
|
||||
bool PStatMonitor::
|
||||
write(const Filename &fn) const {
|
||||
DatagramOutputFile dof;
|
||||
if (!dof.open(fn)) {
|
||||
return false;
|
||||
}
|
||||
if (!dof.write_header(session_file_header)) {
|
||||
return false;
|
||||
}
|
||||
Datagram dg;
|
||||
dg.set_stdfloat_double(false);
|
||||
dg.add_uint16(1);
|
||||
dg.add_uint16(1);
|
||||
write_datagram(dg);
|
||||
dof.put_datagram(dg);
|
||||
dof.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the data and the UI state from the given file.
|
||||
*/
|
||||
bool PStatMonitor::
|
||||
read(const Filename &fn) {
|
||||
close();
|
||||
|
||||
DatagramInputFile dif;
|
||||
if (!dif.open(fn)) {
|
||||
nout << "Failed to open " << fn << " for reading.\n";
|
||||
return false;
|
||||
}
|
||||
string header;
|
||||
if (!dif.read_header(header, 8) || header != session_file_header) {
|
||||
nout << "Session file contains invalid header.\n";
|
||||
return false;
|
||||
}
|
||||
Datagram dg;
|
||||
dg.set_stdfloat_double(false);
|
||||
|
||||
if (!dif.get_datagram(dg)) {
|
||||
nout << "Failed to read datagram from session file.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
DatagramIterator scan(dg);
|
||||
int version = scan.get_uint16();
|
||||
if (version != 1) {
|
||||
nout << "Unsupported session file version " << version << ".\n";
|
||||
return false;
|
||||
}
|
||||
// Room for a minor version number
|
||||
scan.get_uint16();
|
||||
|
||||
read_datagram(scan);
|
||||
dif.close();
|
||||
|
||||
idle();
|
||||
|
||||
_read_filename = fn;
|
||||
_client_data->clear_dirty();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the default set of graphs.
|
||||
*/
|
||||
void PStatMonitor::
|
||||
open_default_graphs() {
|
||||
DatagramInputFile dif;
|
||||
if (!dif.open(layout_filename)) {
|
||||
open_strip_chart(0, 0, false);
|
||||
return;
|
||||
}
|
||||
string header;
|
||||
if (!dif.read_header(header, 8) || header != layout_file_header) {
|
||||
nout << "Layout file contains invalid header.\n";
|
||||
open_strip_chart(0, 0, false);
|
||||
return;
|
||||
}
|
||||
Datagram dg;
|
||||
dg.set_stdfloat_double(false);
|
||||
|
||||
if (!dif.get_datagram(dg)) {
|
||||
nout << "Failed to read datagram from layout file.\n";
|
||||
open_strip_chart(0, 0, false);
|
||||
return;
|
||||
}
|
||||
|
||||
DatagramIterator scan(dg);
|
||||
int version = scan.get_uint16();
|
||||
if (version != 1) {
|
||||
nout << "Unsupported layout file version " << version << ".\n";
|
||||
open_strip_chart(0, 0, false);
|
||||
return;
|
||||
}
|
||||
// Room for a minor version number
|
||||
scan.get_uint16();
|
||||
|
||||
size_t num_colors = scan.get_uint32();
|
||||
for (size_t i = 0; i < num_colors; ++i) {
|
||||
int key = scan.get_int32();
|
||||
_colors[key].read_datagram(scan);
|
||||
}
|
||||
|
||||
PStatGraph *graph;
|
||||
|
||||
size_t num_timelines = scan.get_uint16();
|
||||
for (size_t i = 0; i < num_timelines; ++i) {
|
||||
int x = scan.get_int32();
|
||||
int y = scan.get_int32();
|
||||
int width = scan.get_int32();
|
||||
int height = scan.get_int32();
|
||||
bool minimized = scan.get_bool();
|
||||
bool maximized = scan.get_bool();
|
||||
graph = open_timeline();
|
||||
graph->set_window_state(x, y, width, height, minimized, maximized);
|
||||
}
|
||||
|
||||
size_t num_strip_charts = scan.get_uint16();
|
||||
for (size_t i = 0; i < num_strip_charts; ++i) {
|
||||
std::string thread_name = scan.get_string();
|
||||
std::string collector_name = scan.get_string();
|
||||
bool show_level = scan.get_bool();
|
||||
int thread_index = _client_data->find_thread(thread_name);
|
||||
int collector_index = _client_data->find_collector(collector_name);
|
||||
int x = scan.get_int32();
|
||||
int y = scan.get_int32();
|
||||
int width = scan.get_int32();
|
||||
int height = scan.get_int32();
|
||||
bool minimized = scan.get_bool();
|
||||
bool maximized = scan.get_bool();
|
||||
if (thread_index != -1) {
|
||||
graph = open_strip_chart(thread_index, collector_index, show_level);
|
||||
graph->set_window_state(x, y, width, height, minimized, maximized);
|
||||
}
|
||||
}
|
||||
|
||||
size_t num_flame_graphs = scan.get_uint16();
|
||||
for (size_t i = 0; i < num_flame_graphs; ++i) {
|
||||
std::string thread_name = scan.get_string();
|
||||
std::string collector_name = scan.get_string();
|
||||
int thread_index = _client_data->find_thread(thread_name);
|
||||
int collector_index = collector_name.empty() ? -1 : _client_data->find_collector(collector_name);
|
||||
int x = scan.get_int32();
|
||||
int y = scan.get_int32();
|
||||
int width = scan.get_int32();
|
||||
int height = scan.get_int32();
|
||||
bool minimized = scan.get_bool();
|
||||
bool maximized = scan.get_bool();
|
||||
if (thread_index != -1) {
|
||||
graph = open_flame_graph(thread_index, collector_index);
|
||||
graph->set_window_state(x, y, width, height, minimized, maximized);
|
||||
}
|
||||
}
|
||||
|
||||
size_t num_piano_rolls = scan.get_uint16();
|
||||
for (size_t i = 0; i < num_piano_rolls; ++i) {
|
||||
std::string thread_name = scan.get_string();
|
||||
int thread_index = _client_data->find_thread(thread_name);
|
||||
int x = scan.get_int32();
|
||||
int y = scan.get_int32();
|
||||
int width = scan.get_int32();
|
||||
int height = scan.get_int32();
|
||||
bool minimized = scan.get_bool();
|
||||
bool maximized = scan.get_bool();
|
||||
if (thread_index != -1) {
|
||||
graph = open_piano_roll(thread_index);
|
||||
graph->set_window_state(x, y, width, height, minimized, maximized);
|
||||
}
|
||||
}
|
||||
|
||||
dif.close();
|
||||
if (num_timelines + num_strip_charts + num_flame_graphs + num_piano_rolls == 0) {
|
||||
open_strip_chart(0, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the current graph layout as the default graph layout.
|
||||
*/
|
||||
bool PStatMonitor::
|
||||
save_default_graphs() const {
|
||||
layout_filename.make_dir();
|
||||
|
||||
DatagramOutputFile dof;
|
||||
if (!dof.open(layout_filename)) {
|
||||
return false;
|
||||
}
|
||||
if (!dof.write_header(layout_file_header)) {
|
||||
return false;
|
||||
}
|
||||
Datagram dg;
|
||||
dg.set_stdfloat_double(false);
|
||||
dg.add_uint16(1);
|
||||
dg.add_uint16(1);
|
||||
|
||||
dg.add_uint32((uint32_t)_colors.size());
|
||||
for (const auto &item : _colors) {
|
||||
dg.add_int32(item.first);
|
||||
item.second.write_datagram(dg);
|
||||
}
|
||||
|
||||
dg.add_uint16(_timelines.size());
|
||||
for (PStatGraph *graph : _timelines) {
|
||||
int x, y, width, height;
|
||||
bool minimized, maximized;
|
||||
if (graph->get_window_state(x, y, width, height, minimized, maximized)) {
|
||||
dg.add_int32(x);
|
||||
dg.add_int32(y);
|
||||
dg.add_int32(width);
|
||||
dg.add_int32(height);
|
||||
dg.add_bool(minimized);
|
||||
dg.add_bool(maximized);
|
||||
}
|
||||
}
|
||||
|
||||
dg.add_uint16(_strip_charts.size());
|
||||
for (PStatGraph *graph : _strip_charts) {
|
||||
int x, y, width, height;
|
||||
bool minimized, maximized;
|
||||
if (graph->get_window_state(x, y, width, height, minimized, maximized)) {
|
||||
dg.add_string(_client_data->get_thread_name(((PStatStripChart *)graph)->get_thread_index()));
|
||||
dg.add_string(_client_data->get_collector_fullname(((PStatStripChart *)graph)->get_collector_index()));
|
||||
dg.add_bool(((PStatStripChart *)graph)->get_view().get_show_level());
|
||||
dg.add_int32(x);
|
||||
dg.add_int32(y);
|
||||
dg.add_int32(width);
|
||||
dg.add_int32(height);
|
||||
dg.add_bool(minimized);
|
||||
dg.add_bool(maximized);
|
||||
}
|
||||
}
|
||||
|
||||
dg.add_uint16(_flame_graphs.size());
|
||||
for (PStatGraph *graph : _flame_graphs) {
|
||||
int x, y, width, height;
|
||||
bool minimized, maximized;
|
||||
if (graph->get_window_state(x, y, width, height, minimized, maximized)) {
|
||||
int collector_index = ((PStatFlameGraph *)graph)->get_collector_index();
|
||||
dg.add_string(_client_data->get_thread_name(((PStatFlameGraph *)graph)->get_thread_index()));
|
||||
dg.add_string(collector_index >= 0 ? _client_data->get_collector_fullname(collector_index) : "");
|
||||
dg.add_int32(x);
|
||||
dg.add_int32(y);
|
||||
dg.add_int32(width);
|
||||
dg.add_int32(height);
|
||||
dg.add_bool(minimized);
|
||||
dg.add_bool(maximized);
|
||||
}
|
||||
}
|
||||
|
||||
dg.add_uint16(_piano_rolls.size());
|
||||
for (PStatGraph *graph : _piano_rolls) {
|
||||
int x, y, width, height;
|
||||
bool minimized, maximized;
|
||||
if (graph->get_window_state(x, y, width, height, minimized, maximized)) {
|
||||
dg.add_string(_client_data->get_thread_name(((PStatPianoRoll *)graph)->get_thread_index()));
|
||||
dg.add_int32(x);
|
||||
dg.add_int32(y);
|
||||
dg.add_int32(width);
|
||||
dg.add_int32(height);
|
||||
dg.add_bool(minimized);
|
||||
dg.add_bool(maximized);
|
||||
}
|
||||
}
|
||||
|
||||
// Reserved for future graph type
|
||||
dg.add_uint16(0);
|
||||
|
||||
dof.put_datagram(dg);
|
||||
dof.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the client is alive and connected, false otherwise.
|
||||
*/
|
||||
|
|
@ -145,6 +442,22 @@ get_collector_color(int collector_index) {
|
|||
return (*ci).second;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a custom color associated with the given collector.
|
||||
*/
|
||||
void PStatMonitor::
|
||||
set_collector_color(int collector_index, const LRGBColor &color) {
|
||||
_colors[collector_index] = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears any custom custom color associated with the given collector.
|
||||
*/
|
||||
void PStatMonitor::
|
||||
clear_collector_color(int collector_index) {
|
||||
_colors.erase(collector_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view on the given thread index. If there is no such view already
|
||||
* for the indicated thread, this will create one. This view can be used to
|
||||
|
|
@ -293,3 +606,162 @@ is_thread_safe() {
|
|||
void PStatMonitor::
|
||||
user_guide_bars_changed() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new timeline.
|
||||
*/
|
||||
PStatGraph *PStatMonitor::
|
||||
open_timeline() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new strip chart showing the indicated data.
|
||||
*/
|
||||
PStatGraph *PStatMonitor::
|
||||
open_strip_chart(int thread_index, int collector_index, bool show_level) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new flame graph showing the indicated data.
|
||||
*/
|
||||
PStatGraph *PStatMonitor::
|
||||
open_flame_graph(int thread_index, int collector_index) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new piano roll showing the indicated data.
|
||||
*/
|
||||
PStatGraph *PStatMonitor::
|
||||
open_piano_roll(int thread_index) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the client data and open graphs to a datagram.
|
||||
*/
|
||||
void PStatMonitor::
|
||||
write_datagram(Datagram &dg) const {
|
||||
dg.add_bool(_client_known);
|
||||
dg.add_string(_client_hostname);
|
||||
dg.add_string(_client_progname);
|
||||
dg.add_int32(_client_pid);
|
||||
|
||||
get_client_data()->write_datagram(dg);
|
||||
|
||||
dg.add_uint32((uint32_t)_colors.size());
|
||||
for (const auto &item : _colors) {
|
||||
dg.add_int32(item.first);
|
||||
item.second.write_datagram(dg);
|
||||
}
|
||||
|
||||
dg.add_uint16(_timelines.size());
|
||||
for (PStatGraph *graph : _timelines) {
|
||||
graph->write_datagram(dg);
|
||||
}
|
||||
|
||||
dg.add_uint16(_strip_charts.size());
|
||||
for (PStatGraph *graph : _strip_charts) {
|
||||
dg.add_int16(((PStatStripChart *)graph)->get_thread_index());
|
||||
dg.add_int16(((PStatStripChart *)graph)->get_collector_index());
|
||||
dg.add_bool(((PStatStripChart *)graph)->get_view().get_show_level());
|
||||
graph->write_datagram(dg);
|
||||
}
|
||||
|
||||
dg.add_uint16(_flame_graphs.size());
|
||||
for (PStatGraph *graph : _flame_graphs) {
|
||||
dg.add_int16(((PStatFlameGraph *)graph)->get_thread_index());
|
||||
dg.add_int16(((PStatFlameGraph *)graph)->get_collector_index());
|
||||
graph->write_datagram(dg);
|
||||
}
|
||||
|
||||
dg.add_uint16(_piano_rolls.size());
|
||||
for (PStatGraph *graph : _piano_rolls) {
|
||||
dg.add_int16(((PStatPianoRoll *)graph)->get_thread_index());
|
||||
graph->write_datagram(dg);
|
||||
}
|
||||
|
||||
// Reserved for future graph type
|
||||
dg.add_uint16(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the client data and open graphs from a datagram.
|
||||
*/
|
||||
void PStatMonitor::
|
||||
read_datagram(DatagramIterator &scan) {
|
||||
_client_known = scan.get_bool();
|
||||
_client_hostname = scan.get_string();
|
||||
_client_progname = scan.get_string();
|
||||
_client_pid = scan.get_int32();
|
||||
|
||||
PStatClientData *client_data = new PStatClientData;
|
||||
client_data->read_datagram(scan);
|
||||
set_client_data(client_data);
|
||||
|
||||
size_t num_colors = scan.get_uint32();
|
||||
for (size_t i = 0; i < num_colors; ++i) {
|
||||
int key = scan.get_int32();
|
||||
_colors[key].read_datagram(scan);
|
||||
}
|
||||
|
||||
int num_collectors = client_data->get_num_collectors();
|
||||
for (int collector_index = 0; collector_index < num_collectors; ++collector_index) {
|
||||
if (client_data->has_collector(collector_index)) {
|
||||
new_collector(collector_index);
|
||||
}
|
||||
}
|
||||
|
||||
int num_threads = client_data->get_num_threads();
|
||||
for (int thread_index = 0; thread_index < num_threads; ++thread_index) {
|
||||
if (client_data->has_thread(thread_index)) {
|
||||
const PStatThreadData *thread_data = client_data->get_thread_data(thread_index);
|
||||
if (!thread_data->is_empty()) {
|
||||
const PStatFrameData &frame_data = thread_data->get_latest_frame();
|
||||
get_view(thread_index).set_to_frame(frame_data);
|
||||
|
||||
int num_collectors = client_data->get_num_toplevel_collectors();
|
||||
for (int i = 0; i < num_collectors; ++i) {
|
||||
int collector_index = client_data->get_toplevel_collector(i);
|
||||
if (client_data->has_collector(collector_index)) {
|
||||
get_level_view(collector_index, thread_index).set_to_frame(thread_data->get_latest_frame());
|
||||
}
|
||||
}
|
||||
}
|
||||
new_thread(thread_index);
|
||||
}
|
||||
}
|
||||
|
||||
PStatGraph *graph;
|
||||
|
||||
size_t num_timelines = scan.get_uint16();
|
||||
for (size_t i = 0; i < num_timelines; ++i) {
|
||||
graph = open_timeline();
|
||||
graph->read_datagram(scan);
|
||||
}
|
||||
|
||||
size_t num_strip_charts = scan.get_uint16();
|
||||
for (size_t i = 0; i < num_strip_charts; ++i) {
|
||||
int thread_index = scan.get_int16();
|
||||
int collector_index = scan.get_int16();
|
||||
graph = open_strip_chart(thread_index, collector_index, scan.get_bool());
|
||||
graph->read_datagram(scan);
|
||||
}
|
||||
|
||||
size_t num_flame_graphs = scan.get_uint16();
|
||||
for (size_t i = 0; i < num_flame_graphs; ++i) {
|
||||
int thread_index = scan.get_int16();
|
||||
int collector_index = scan.get_int16();
|
||||
graph = open_flame_graph(thread_index, collector_index);
|
||||
graph->read_datagram(scan);
|
||||
}
|
||||
|
||||
size_t num_piano_rolls = scan.get_uint16();
|
||||
for (size_t i = 0; i < num_piano_rolls; ++i) {
|
||||
int thread_index = scan.get_int16();
|
||||
graph = open_piano_roll(thread_index);
|
||||
graph->read_datagram(scan);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include "pmap.h"
|
||||
|
||||
class PStatCollectorDef;
|
||||
class PStatGraph;
|
||||
class PStatServer;
|
||||
|
||||
/**
|
||||
|
|
@ -40,7 +41,7 @@ class PStatMonitor : public ReferenceCount {
|
|||
public:
|
||||
// The following functions are primarily for use by internal classes to set
|
||||
// up the monitor.
|
||||
PStatMonitor(PStatServer *server);
|
||||
PStatMonitor(PStatServer *server = nullptr);
|
||||
virtual ~PStatMonitor();
|
||||
|
||||
void hello_from(const std::string &hostname, const std::string &progname,
|
||||
|
|
@ -51,6 +52,11 @@ public:
|
|||
int server_major, int server_minor);
|
||||
void set_client_data(PStatClientData *client_data);
|
||||
|
||||
bool write(const Filename &fn) const;
|
||||
bool read(const Filename &fn);
|
||||
|
||||
void open_default_graphs();
|
||||
bool save_default_graphs() const;
|
||||
|
||||
// The following functions are for use by user code to determine information
|
||||
// about the client data available.
|
||||
|
|
@ -61,16 +67,19 @@ public:
|
|||
INLINE const PStatClientData *get_client_data() const;
|
||||
INLINE std::string get_collector_name(int collector_index);
|
||||
const LRGBColor &get_collector_color(int collector_index);
|
||||
void set_collector_color(int collector_index, const LRGBColor &color);
|
||||
void clear_collector_color(int collector_index);
|
||||
|
||||
INLINE bool is_client_known() const;
|
||||
INLINE std::string get_client_hostname() const;
|
||||
INLINE std::string get_client_progname() const;
|
||||
INLINE int get_client_pid() const;
|
||||
INLINE bool has_read_filename() const;
|
||||
INLINE const Filename &get_read_filename() const;
|
||||
|
||||
PStatView &get_view(int thread_index);
|
||||
PStatView &get_level_view(int collector_index, int thread_index);
|
||||
|
||||
|
||||
// The following virtual methods may be overridden by a derived monitor
|
||||
// class to customize behavior.
|
||||
|
||||
|
|
@ -92,6 +101,14 @@ public:
|
|||
|
||||
virtual void user_guide_bars_changed();
|
||||
|
||||
virtual PStatGraph *open_timeline();
|
||||
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_piano_roll(int thread_index);
|
||||
|
||||
void write_datagram(Datagram &dg) const;
|
||||
void read_datagram(DatagramIterator &scan);
|
||||
|
||||
protected:
|
||||
PStatServer *_server;
|
||||
|
||||
|
|
@ -102,6 +119,7 @@ private:
|
|||
std::string _client_hostname;
|
||||
std::string _client_progname;
|
||||
int _client_pid;
|
||||
Filename _read_filename;
|
||||
|
||||
typedef pmap<int, PStatView> Views;
|
||||
Views _views;
|
||||
|
|
@ -110,6 +128,13 @@ private:
|
|||
|
||||
typedef pmap<int, LRGBColor> Colors;
|
||||
Colors _colors;
|
||||
|
||||
public:
|
||||
typedef pset<PStatGraph *> Graphs;
|
||||
Graphs _timelines;
|
||||
Graphs _strip_charts;
|
||||
Graphs _flame_graphs;
|
||||
Graphs _piano_rolls;
|
||||
};
|
||||
|
||||
#include "pStatMonitor.I"
|
||||
|
|
|
|||
|
|
@ -89,10 +89,24 @@ PStatPianoRoll(PStatMonitor *monitor, int thread_index, int xsize, int ysize) :
|
|||
{
|
||||
_time_width = 1.0 / pstats_target_frame_rate;
|
||||
_start_time = 0.0;
|
||||
|
||||
_current_frame = -1;
|
||||
|
||||
// If we already have data, load it in now.
|
||||
const PStatClientData *client_data = _monitor->get_client_data();
|
||||
if (client_data->get_num_collectors() != 0 &&
|
||||
client_data->get_num_threads() != 0) {
|
||||
const PStatThreadData *thread_data =
|
||||
client_data->get_thread_data(thread_index);
|
||||
if (!thread_data->is_empty()) {
|
||||
int frame_number = thread_data->get_latest_frame_number();
|
||||
compute_page(thread_data->get_frame(frame_number));
|
||||
}
|
||||
}
|
||||
|
||||
_guide_bar_units = GBU_ms | GBU_hz | GBU_show_units;
|
||||
normal_guide_bars();
|
||||
|
||||
monitor->_piano_rolls.insert(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -100,6 +114,7 @@ PStatPianoRoll(PStatMonitor *monitor, int thread_index, int xsize, int ysize) :
|
|||
*/
|
||||
PStatPianoRoll::
|
||||
~PStatPianoRoll() {
|
||||
_monitor->_piano_rolls.erase(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -142,6 +157,32 @@ get_label_tooltip(int collector_index) const {
|
|||
return client_data->get_collector_fullname(collector_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the graph state to a datagram.
|
||||
*/
|
||||
void PStatPianoRoll::
|
||||
write_datagram(Datagram &dg) const {
|
||||
dg.add_float64(_time_width);
|
||||
dg.add_float64(_start_time);
|
||||
|
||||
PStatGraph::write_datagram(dg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the graph state from a datagram.
|
||||
*/
|
||||
void PStatPianoRoll::
|
||||
read_datagram(DatagramIterator &scan) {
|
||||
_time_width = scan.get_float64();
|
||||
_start_time = scan.get_float64();
|
||||
|
||||
PStatGraph::read_datagram(scan);
|
||||
|
||||
_current_frame = -1;
|
||||
normal_guide_bars();
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* To be called by the user class when the widget size has changed. This
|
||||
* updates the chart's internal data and causes it to issue redraw commands to
|
||||
|
|
|
|||
|
|
@ -55,6 +55,9 @@ public:
|
|||
|
||||
std::string get_label_tooltip(int collector_index) const;
|
||||
|
||||
virtual void write_datagram(Datagram &dg) const final;
|
||||
virtual void read_datagram(DatagramIterator &scan) final;
|
||||
|
||||
protected:
|
||||
void changed_size(int xsize, int ysize);
|
||||
void force_redraw();
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ void PStatReader::
|
|||
lost_connection() {
|
||||
_client_data->_is_alive = false;
|
||||
_monitor->lost_connection();
|
||||
_manager->lost_connection(_monitor);
|
||||
_client_data.clear();
|
||||
|
||||
_manager->close_connection(_tcp_connection);
|
||||
|
|
|
|||
|
|
@ -30,10 +30,11 @@ PStatServer() {
|
|||
*/
|
||||
PStatServer::
|
||||
~PStatServer() {
|
||||
stop_listening();
|
||||
|
||||
delete _listener;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Establishes a port number that the manager will listen on for TCP
|
||||
* connections. This may be called more than once to listen simulataneously
|
||||
|
|
@ -47,20 +48,22 @@ PStatServer::
|
|||
*/
|
||||
bool PStatServer::
|
||||
listen(int port) {
|
||||
stop_listening();
|
||||
|
||||
if (port < 0) {
|
||||
port = pstats_port;
|
||||
}
|
||||
|
||||
// Now try to listen to the port.
|
||||
PT(Connection) rendezvous = open_TCP_server_rendezvous(port, 5);
|
||||
_rendezvous = open_TCP_server_rendezvous(port, 5);
|
||||
|
||||
if (rendezvous.is_null()) {
|
||||
if (_rendezvous.is_null()) {
|
||||
// Couldn't get it.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Tell the listener about the new port.
|
||||
_listener->add_connection(rendezvous);
|
||||
_listener->add_connection(_rendezvous);
|
||||
|
||||
if (_next_udp_port == 0) {
|
||||
_next_udp_port = port + 1;
|
||||
|
|
@ -68,6 +71,16 @@ listen(int port) {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops listening.
|
||||
*/
|
||||
void PStatServer::
|
||||
stop_listening() {
|
||||
if (_rendezvous != nullptr) {
|
||||
close_connection(_rendezvous);
|
||||
_rendezvous.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for any network activity and handles it, if appropriate, and then
|
||||
|
|
|
|||
|
|
@ -39,11 +39,14 @@ public:
|
|||
~PStatServer();
|
||||
|
||||
bool listen(int port = -1);
|
||||
void stop_listening();
|
||||
|
||||
void poll();
|
||||
void main_loop(bool *interrupt_flag = nullptr);
|
||||
|
||||
virtual PStatMonitor *make_monitor()=0;
|
||||
virtual PStatMonitor *make_monitor(const NetAddress &address)=0;
|
||||
virtual void lost_connection(PStatMonitor *monitor) {}
|
||||
|
||||
void add_reader(Connection *connection, PStatReader *reader);
|
||||
void remove_reader(Connection *connection, PStatReader *reader);
|
||||
|
||||
|
|
@ -67,6 +70,7 @@ private:
|
|||
void user_guide_bars_changed();
|
||||
|
||||
PStatListener *_listener;
|
||||
PT(Connection) _rendezvous;
|
||||
|
||||
typedef pmap<PT(Connection), PStatReader *> Readers;
|
||||
Readers _readers;
|
||||
|
|
|
|||
|
|
@ -29,11 +29,12 @@ using std::min;
|
|||
*
|
||||
*/
|
||||
PStatStripChart::
|
||||
PStatStripChart(PStatMonitor *monitor, PStatView &view,
|
||||
int thread_index, int collector_index, int xsize, int ysize) :
|
||||
PStatStripChart(PStatMonitor *monitor,
|
||||
int thread_index, int collector_index, bool show_level,
|
||||
int xsize, int ysize) :
|
||||
PStatGraph(monitor, xsize, ysize),
|
||||
_thread_index(thread_index),
|
||||
_view(view),
|
||||
_view(show_level ? monitor->get_level_view(0, thread_index) : monitor->get_view(thread_index)),
|
||||
_collector_index(collector_index)
|
||||
{
|
||||
_scroll_mode = pstats_scroll_mode;
|
||||
|
|
@ -57,6 +58,8 @@ PStatStripChart(PStatMonitor *monitor, PStatView &view,
|
|||
}
|
||||
|
||||
set_auto_vertical_scale();
|
||||
|
||||
monitor->_strip_charts.insert(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -64,6 +67,7 @@ PStatStripChart(PStatMonitor *monitor, PStatView &view,
|
|||
*/
|
||||
PStatStripChart::
|
||||
~PStatStripChart() {
|
||||
_monitor->_strip_charts.erase(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -370,6 +374,65 @@ get_label_tooltip(int collector_index) const {
|
|||
return text.str();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the graph state to a datagram.
|
||||
*/
|
||||
void PStatStripChart::
|
||||
write_datagram(Datagram &dg) const {
|
||||
dg.add_bool(_scroll_mode);
|
||||
dg.add_bool(_average_mode);
|
||||
dg.add_float64(_time_width);
|
||||
dg.add_float64(_start_time);
|
||||
dg.add_float64(_value_height);
|
||||
|
||||
// Not really necessary, we reconstructed this from the client data.
|
||||
//for (const auto &item : _data) {
|
||||
// dg.add_int32(item.first);
|
||||
// dg.add_uint32(item.second.size());
|
||||
//
|
||||
// for (const ColorData &cd : item.second) {
|
||||
// dg.add_uint16(cd._collector_index);
|
||||
// dg.add_uint16(cd._i);
|
||||
// dg.add_float64(cd._net_value);
|
||||
// }
|
||||
//}
|
||||
//dg.add_int32(-1);
|
||||
|
||||
PStatGraph::write_datagram(dg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the graph state from a datagram.
|
||||
*/
|
||||
void PStatStripChart::
|
||||
read_datagram(DatagramIterator &scan) {
|
||||
_next_frame = 0;
|
||||
force_reset();
|
||||
|
||||
_scroll_mode = scan.get_bool();
|
||||
_average_mode = scan.get_bool();
|
||||
_time_width = scan.get_float64();
|
||||
_start_time = scan.get_float64();
|
||||
_value_height = scan.get_float64();
|
||||
|
||||
//int key;
|
||||
//while ((key = scan.get_int32()) != -1) {
|
||||
// FrameData &fdata = _data[key];
|
||||
// fdata.resize(scan.get_uint32());
|
||||
//
|
||||
// for (ColorData &cd : fdata) {
|
||||
// cd._collector_index = scan.get_uint16();
|
||||
// cd._i = scan.get_uint16();
|
||||
// cd._net_value = scan.get_float64();
|
||||
// }
|
||||
//}
|
||||
|
||||
PStatGraph::read_datagram(scan);
|
||||
|
||||
normal_guide_bars();
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the data from additional into the data from fdata, after applying the
|
||||
* scale weight.
|
||||
|
|
@ -784,7 +847,6 @@ void PStatStripChart::
|
|||
idle() {
|
||||
}
|
||||
|
||||
|
||||
// STL function object for sorting labels in order by the collector's sort
|
||||
// index, used in update_labels(), below.
|
||||
class SortCollectorLabels2 {
|
||||
|
|
|
|||
|
|
@ -37,8 +37,9 @@ class PStatView;
|
|||
*/
|
||||
class PStatStripChart : public PStatGraph {
|
||||
public:
|
||||
PStatStripChart(PStatMonitor *monitor, PStatView &view,
|
||||
int thread_index, int collector_index, int xsize, int ysize);
|
||||
PStatStripChart(PStatMonitor *monitor,
|
||||
int thread_index, int collector_index, bool show_level,
|
||||
int xsize, int ysize);
|
||||
virtual ~PStatStripChart();
|
||||
|
||||
void new_data(int frame_number);
|
||||
|
|
@ -73,6 +74,9 @@ public:
|
|||
std::string get_title_text();
|
||||
std::string get_label_tooltip(int collector_index) const;
|
||||
|
||||
virtual void write_datagram(Datagram &dg) const final;
|
||||
virtual void read_datagram(DatagramIterator &scan) final;
|
||||
|
||||
protected:
|
||||
class ColorData {
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ get_latest_frame() const {
|
|||
bool PStatThreadData::
|
||||
get_elapsed_frames(int &then_i, int &now_i) const {
|
||||
if (!_computed_elapsed_frames) {
|
||||
((PStatThreadData *)this)->compute_elapsed_frames();
|
||||
compute_elapsed_frames();
|
||||
}
|
||||
|
||||
now_i = _now_i;
|
||||
|
|
@ -308,17 +308,48 @@ record_new_frame(int frame_number, PStatFrameData *frame_data) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Computes the frame numbers returned by get_elapsed_frames(). This is non-
|
||||
* const, but only updates cached values, so may safely be called from a const
|
||||
* method.
|
||||
* Writes the thread data to a datagram.
|
||||
*/
|
||||
void PStatThreadData::
|
||||
compute_elapsed_frames() {
|
||||
write_datagram(Datagram &dg) const {
|
||||
int frame_number = _first_frame_number;
|
||||
|
||||
for (PStatFrameData *frame_data : _frames) {
|
||||
if (frame_data != nullptr) {
|
||||
dg.add_int32(frame_number);
|
||||
frame_data->write_datagram(dg);
|
||||
}
|
||||
++frame_number;
|
||||
}
|
||||
dg.add_int32(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the thread data from a datagram.
|
||||
*/
|
||||
void PStatThreadData::
|
||||
read_datagram(DatagramIterator &scan) {
|
||||
int frame_number;
|
||||
while ((frame_number = scan.get_int32()) != -1) {
|
||||
PStatFrameData *frame_data = new PStatFrameData;
|
||||
frame_data->read_datagram(scan);
|
||||
|
||||
record_new_frame(frame_number, frame_data);
|
||||
}
|
||||
|
||||
compute_elapsed_frames();
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the frame numbers returned by get_elapsed_frames().
|
||||
*/
|
||||
void PStatThreadData::
|
||||
compute_elapsed_frames() const {
|
||||
if (_frames.empty()) {
|
||||
// No frames in the data at all.
|
||||
_got_elapsed_frames = false;
|
||||
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
_now_i = _frames.size() - 1;
|
||||
while (_now_i > 0 && _frames[_now_i] == nullptr) {
|
||||
_now_i--;
|
||||
|
|
@ -326,8 +357,8 @@ compute_elapsed_frames() {
|
|||
if (_now_i < 0) {
|
||||
// No frames have any real data.
|
||||
_got_elapsed_frames = false;
|
||||
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
nassertv(_frames[_now_i] != nullptr);
|
||||
|
||||
double now = _frames[_now_i]->get_end();
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@
|
|||
#define PSTATTHREADDATA_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
|
||||
#include "datagram.h"
|
||||
#include "datagramIterator.h"
|
||||
#include "referenceCount.h"
|
||||
|
||||
#include "pdeque.h"
|
||||
|
|
@ -61,8 +62,12 @@ public:
|
|||
|
||||
void record_new_frame(int frame_number, PStatFrameData *frame_data);
|
||||
|
||||
void write_datagram(Datagram &dg) const;
|
||||
void read_datagram(DatagramIterator &scan);
|
||||
|
||||
private:
|
||||
void compute_elapsed_frames();
|
||||
void compute_elapsed_frames() const;
|
||||
|
||||
const PStatClientData *_client_data;
|
||||
|
||||
typedef pdeque<PStatFrameData *> Frames;
|
||||
|
|
@ -70,10 +75,11 @@ private:
|
|||
int _first_frame_number;
|
||||
double _history;
|
||||
|
||||
bool _computed_elapsed_frames;
|
||||
bool _got_elapsed_frames;
|
||||
int _then_i;
|
||||
int _now_i;
|
||||
// Cached values, updated by compute_elapsed_frames().
|
||||
mutable bool _computed_elapsed_frames;
|
||||
mutable bool _got_elapsed_frames;
|
||||
mutable int _then_i;
|
||||
mutable int _now_i;
|
||||
|
||||
static PStatFrameData _null_frame;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@ PStatTimeline(PStatMonitor *monitor, int xsize, int ysize) :
|
|||
|
||||
_start_time = _lowest_start_time;
|
||||
_target_start_time = _start_time;
|
||||
|
||||
monitor->_timelines.insert(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -83,6 +85,7 @@ PStatTimeline(PStatMonitor *monitor, int xsize, int ysize) :
|
|||
*/
|
||||
PStatTimeline::
|
||||
~PStatTimeline() {
|
||||
_monitor->_timelines.erase(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -116,7 +119,7 @@ new_data(int thread_index, int frame_number) {
|
|||
_highest_end_time = frame_end;
|
||||
}
|
||||
|
||||
while (thread_index >= _threads.size()) {
|
||||
while (thread_index >= (int)_threads.size()) {
|
||||
_threads_changed = true;
|
||||
if (_threads.size() == 0) {
|
||||
_threads.resize(1);
|
||||
|
|
@ -261,6 +264,42 @@ get_bar_tooltip(int row, int x) const {
|
|||
return std::string();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the graph state to a datagram.
|
||||
*/
|
||||
void PStatTimeline::
|
||||
write_datagram(Datagram &dg) const {
|
||||
dg.add_float64(_time_scale);
|
||||
dg.add_float64(_start_time);
|
||||
dg.add_float64(_lowest_start_time);
|
||||
dg.add_float64(_highest_end_time);
|
||||
|
||||
PStatGraph::write_datagram(dg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the graph state from a datagram.
|
||||
*/
|
||||
void PStatTimeline::
|
||||
read_datagram(DatagramIterator &scan) {
|
||||
_time_scale = scan.get_float64();
|
||||
_start_time = scan.get_float64();
|
||||
_lowest_start_time = scan.get_float64();
|
||||
_highest_end_time = scan.get_float64();
|
||||
|
||||
_scroll_speed = 0.0;
|
||||
_zoom_speed = 0.0;
|
||||
|
||||
_have_start_time = true;
|
||||
_target_start_time = _start_time;
|
||||
_target_time_scale = _time_scale;
|
||||
|
||||
PStatGraph::read_datagram(scan);
|
||||
|
||||
normal_guide_bars();
|
||||
force_redraw();
|
||||
}
|
||||
|
||||
/**
|
||||
* To be called by the user class when the widget size has changed. This
|
||||
* updates the chart's internal data and causes it to issue redraw commands to
|
||||
|
|
@ -324,12 +363,12 @@ force_redraw(int row, int from_x, int to_x) {
|
|||
|
||||
for (size_t ti = 0; ti < _threads.size(); ++ti) {
|
||||
ThreadRow &thread_row = _threads[ti];
|
||||
if (thread_row._row_offset > row) {
|
||||
if ((int)thread_row._row_offset > row) {
|
||||
break;
|
||||
}
|
||||
|
||||
int row_index = row - (int)thread_row._row_offset;
|
||||
if (row_index < thread_row._rows.size()) {
|
||||
if (row_index < (int)thread_row._rows.size()) {
|
||||
draw_row((int)ti, row_index, start_time, end_time);
|
||||
}
|
||||
}
|
||||
|
|
@ -644,12 +683,12 @@ find_bar(int row, int x, ColorBar &bar) const {
|
|||
|
||||
for (size_t ti = 0; ti < _threads.size(); ++ti) {
|
||||
const ThreadRow &thread_row = _threads[ti];
|
||||
if (thread_row._row_offset > row) {
|
||||
if ((int)thread_row._row_offset > row) {
|
||||
break;
|
||||
}
|
||||
|
||||
int row_index = row - (int)thread_row._row_offset;
|
||||
if (row_index < thread_row._rows.size()) {
|
||||
if (row_index < (int)thread_row._rows.size()) {
|
||||
// Find the first element whose end time is larger than the given time.
|
||||
const Row &bars = thread_row._rows[row_index];
|
||||
Row::const_iterator it = std::lower_bound(bars.begin(), bars.end(), ColorBar {time, time});
|
||||
|
|
|
|||
|
|
@ -56,6 +56,9 @@ public:
|
|||
|
||||
std::string get_bar_tooltip(int row, int x) const;
|
||||
|
||||
virtual void write_datagram(Datagram &dg) const final;
|
||||
virtual void read_datagram(DatagramIterator &scan) final;
|
||||
|
||||
protected:
|
||||
void changed_size(int xsize, int ysize);
|
||||
void force_redraw();
|
||||
|
|
|
|||
|
|
@ -121,8 +121,8 @@ new_data(int thread_index, int frame_number) {
|
|||
}
|
||||
|
||||
const PStatFrameData &frame_data = thread_data->get_frame(frame_number);
|
||||
int num_events = frame_data.get_num_events();
|
||||
for (int i = 0; i < num_events; ++i) {
|
||||
size_t num_events = frame_data.get_num_events();
|
||||
for (size_t i = 0; i < num_events; ++i) {
|
||||
int collector_index = frame_data.get_time_collector(i);
|
||||
(*_outStream)
|
||||
<< "{\"name\":\"" << client_data->get_collector_fullname(collector_index)
|
||||
|
|
@ -141,8 +141,8 @@ new_data(int thread_index, int frame_number) {
|
|||
if (_show_raw_data) {
|
||||
const PStatFrameData &frame_data = thread_data->get_frame(frame_number);
|
||||
(*_outStream) << "raw data:\n";
|
||||
int num_events = frame_data.get_num_events();
|
||||
for (int i = 0; i < num_events; ++i) {
|
||||
size_t num_events = frame_data.get_num_events();
|
||||
for (size_t i = 0; i < num_events; ++i) {
|
||||
// The iomanipulators are much too clumsy.
|
||||
char formatted[32];
|
||||
sprintf(formatted, "%15.06lf", frame_data.get_time(i));
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ TextStats() {
|
|||
*
|
||||
*/
|
||||
PStatMonitor *TextStats::
|
||||
make_monitor() {
|
||||
make_monitor(const NetAddress &address) {
|
||||
|
||||
return new TextMonitor(this, _outFile, _show_raw_data, _json);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class TextStats : public ProgramBase, public PStatServer {
|
|||
public:
|
||||
TextStats();
|
||||
|
||||
virtual PStatMonitor *make_monitor();
|
||||
virtual PStatMonitor *make_monitor(const NetAddress &address);
|
||||
|
||||
void run();
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ set(WINSTATS_SOURCES
|
|||
|
||||
composite_sources(win-stats WINSTATS_SOURCES)
|
||||
add_executable(win-stats ${WINSTATS_HEADERS} ${WINSTATS_SOURCES})
|
||||
target_link_libraries(win-stats p3progbase p3pstatserver comctl32.lib)
|
||||
target_link_libraries(win-stats p3progbase p3pstatserver comctl32.lib uxtheme.lib)
|
||||
|
||||
# This program is NOT actually called win-stats. It's just pstats.exe
|
||||
set_target_properties(win-stats PROPERTIES OUTPUT_NAME "pstats")
|
||||
|
|
|
|||
|
|
@ -26,66 +26,6 @@
|
|||
// Enable common controls version 6, necessary for modern visual styles
|
||||
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
|
||||
|
||||
static const char *toplevel_class_name = "pstats";
|
||||
static WinStatsServer *server = nullptr;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static LONG WINAPI
|
||||
toplevel_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||
switch (msg) {
|
||||
case WM_TIMER:
|
||||
server->poll();
|
||||
break;
|
||||
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates the initial, toplevel window for the application.
|
||||
*/
|
||||
static HWND
|
||||
create_toplevel_window(HINSTANCE application) {
|
||||
WNDCLASS wc;
|
||||
|
||||
ZeroMemory(&wc, sizeof(WNDCLASS));
|
||||
wc.lpfnWndProc = (WNDPROC)toplevel_window_proc;
|
||||
wc.hInstance = application;
|
||||
wc.lpszClassName = toplevel_class_name;
|
||||
|
||||
if (!RegisterClass(&wc)) {
|
||||
nout << "Could not register window class!\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DWORD window_style = WS_POPUP | WS_SYSMENU | WS_ICONIC;
|
||||
|
||||
std::ostringstream strm;
|
||||
strm << "PStats " << pstats_port;
|
||||
std::string window_name = strm.str();
|
||||
|
||||
HWND toplevel_window =
|
||||
CreateWindow(toplevel_class_name, window_name.c_str(), window_style,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
nullptr, nullptr, application, 0);
|
||||
if (!toplevel_window) {
|
||||
nout << "Could not create toplevel window!\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return toplevel_window;
|
||||
}
|
||||
|
||||
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
|
||||
// Initialize commctl32.dll.
|
||||
INITCOMMONCONTROLSEX icc;
|
||||
|
|
@ -96,27 +36,9 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
|
|||
// Signal DPI awareness.
|
||||
SetProcessDPIAware();
|
||||
|
||||
HINSTANCE application = GetModuleHandle(nullptr);
|
||||
HWND toplevel_window = create_toplevel_window(application);
|
||||
|
||||
ShowWindow(toplevel_window, SW_SHOWMINIMIZED);
|
||||
|
||||
// Create the server object.
|
||||
server = new WinStatsServer;
|
||||
if (!server->listen()) {
|
||||
std::ostringstream stream;
|
||||
stream
|
||||
<< "Unable to open port " << pstats_port
|
||||
<< ". Try specifying a different\n"
|
||||
<< "port number using pstats-port in your Config file.";
|
||||
std::string str = stream.str();
|
||||
MessageBox(toplevel_window, str.c_str(), "PStats error",
|
||||
MB_OK | MB_ICONEXCLAMATION);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Set up a timer to poll the pstats every so often.
|
||||
SetTimer(toplevel_window, 1, 200, nullptr);
|
||||
// Create the server window.
|
||||
WinStatsServer *server = new WinStatsServer;
|
||||
server->new_session();
|
||||
|
||||
// Now get lost in the Windows message loop.
|
||||
MSG msg;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
*/
|
||||
|
||||
#include "winStatsChartMenu.h"
|
||||
#include "winStatsMenuId.h"
|
||||
#include "winStatsMonitor.h"
|
||||
|
||||
/**
|
||||
|
|
@ -31,6 +32,7 @@ WinStatsChartMenu(WinStatsMonitor *monitor, int thread_index) :
|
|||
*/
|
||||
WinStatsChartMenu::
|
||||
~WinStatsChartMenu() {
|
||||
DestroyMenu(_menu);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -99,14 +101,28 @@ do_update() {
|
|||
|
||||
if (_thread_index == 0) {
|
||||
// Timeline goes first.
|
||||
WinStatsMonitor::MenuDef menu_def(_thread_index, -1, WinStatsMonitor::CT_timeline, false);
|
||||
int menu_id = _monitor->get_menu_id(menu_def);
|
||||
{
|
||||
WinStatsMonitor::MenuDef menu_def(_thread_index, -1, WinStatsMonitor::CT_timeline, false);
|
||||
int menu_id = _monitor->get_menu_id(menu_def);
|
||||
|
||||
mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID;
|
||||
mii.fType = MFT_STRING;
|
||||
mii.wID = menu_id;
|
||||
mii.dwTypeData = "Timeline";
|
||||
InsertMenuItem(_menu, GetMenuItemCount(_menu), TRUE, &mii);
|
||||
mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID;
|
||||
mii.fType = MFT_STRING;
|
||||
mii.wID = menu_id;
|
||||
mii.dwTypeData = "Timeline";
|
||||
InsertMenuItem(_menu, GetMenuItemCount(_menu), TRUE, &mii);
|
||||
}
|
||||
|
||||
// And the piano roll (even though it's not very useful nowadays)
|
||||
{
|
||||
WinStatsMonitor::MenuDef menu_def(_thread_index, -1, WinStatsMonitor::CT_piano_roll, false);
|
||||
int menu_id = _monitor->get_menu_id(menu_def);
|
||||
|
||||
mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID;
|
||||
mii.fType = MFT_STRING;
|
||||
mii.wID = menu_id;
|
||||
mii.dwTypeData = "Piano Roll";
|
||||
InsertMenuItem(_menu, GetMenuItemCount(_menu), TRUE, &mii);
|
||||
}
|
||||
|
||||
mii.fMask = MIIM_FTYPE;
|
||||
mii.fType = MFT_SEPARATOR;
|
||||
|
|
@ -141,19 +157,28 @@ do_update() {
|
|||
}
|
||||
}
|
||||
|
||||
// Also menu item for piano roll (following a separator).
|
||||
mii.fMask = MIIM_FTYPE;
|
||||
mii.fType = MFT_SEPARATOR;
|
||||
InsertMenuItem(_menu, GetMenuItemCount(_menu), TRUE, &mii);
|
||||
|
||||
{
|
||||
WinStatsMonitor::MenuDef menu_def(_thread_index, -1, WinStatsMonitor::CT_piano_roll, false);
|
||||
int menu_id = _monitor->get_menu_id(menu_def);
|
||||
// For the main thread menu, also some options relating to all graph windows.
|
||||
if (_thread_index == 0) {
|
||||
mii.fMask = MIIM_FTYPE;
|
||||
mii.fType = MFT_SEPARATOR;
|
||||
InsertMenuItem(_menu, GetMenuItemCount(_menu), TRUE, &mii);
|
||||
|
||||
mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID;
|
||||
mii.fType = MFT_STRING;
|
||||
mii.wID = menu_id;
|
||||
mii.dwTypeData = "Piano Roll";
|
||||
mii.wID = MI_graphs_close_all;
|
||||
mii.dwTypeData = "Close All Graphs";
|
||||
InsertMenuItem(_menu, GetMenuItemCount(_menu), TRUE, &mii);
|
||||
|
||||
mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID;
|
||||
mii.fType = MFT_STRING;
|
||||
mii.wID = MI_graphs_reopen_default;
|
||||
mii.dwTypeData = "Reopen Default Graphs";
|
||||
InsertMenuItem(_menu, GetMenuItemCount(_menu), TRUE, &mii);
|
||||
|
||||
mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID;
|
||||
mii.fType = MFT_STRING;
|
||||
mii.wID = MI_graphs_save_default;
|
||||
mii.dwTypeData = "Save Current Layout as Default";
|
||||
InsertMenuItem(_menu, GetMenuItemCount(_menu), TRUE, &mii);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ WinStatsFlameGraph(WinStatsMonitor *monitor, int thread_index,
|
|||
_average_check_box = 0;
|
||||
|
||||
create_window();
|
||||
clear_region();
|
||||
force_redraw();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -117,7 +117,7 @@ set_time_units(int unit_mask) {
|
|||
|
||||
RECT rect;
|
||||
GetClientRect(_window, &rect);
|
||||
rect.left = _right_margin;
|
||||
rect.bottom = _top_margin;
|
||||
InvalidateRect(_window, &rect, TRUE);
|
||||
}
|
||||
}
|
||||
|
|
@ -159,6 +159,22 @@ on_leave_label(int collector_index) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the collector represented by this flame graph. This may force a
|
||||
* redraw.
|
||||
*/
|
||||
void WinStatsFlameGraph::
|
||||
set_collector_index(int collector_index) {
|
||||
PStatFlameGraph::set_collector_index(collector_index);
|
||||
|
||||
if (is_title_unknown()) {
|
||||
std::string window_title = get_title_text();
|
||||
if (!is_title_unknown()) {
|
||||
SetWindowText(_window, window_title.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls update_guide_bars with parameters suitable to this kind of graph.
|
||||
*/
|
||||
|
|
@ -176,6 +192,11 @@ normal_guide_bars() {
|
|||
}
|
||||
|
||||
_guide_bars_changed = true;
|
||||
|
||||
RECT rect;
|
||||
GetClientRect(_window, &rect);
|
||||
rect.bottom = _top_margin;
|
||||
InvalidateRect(_window, &rect, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -276,6 +297,28 @@ animate(double time, double dt) {
|
|||
return PStatFlameGraph::animate(time, dt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current window dimensions.
|
||||
*/
|
||||
bool WinStatsFlameGraph::
|
||||
get_window_state(int &x, int &y, int &width, int &height,
|
||||
bool &maximized, bool &minimized) const {
|
||||
WinStatsGraph::get_window_state(x, y, width, height, maximized, minimized);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to restore the graph window to its previous dimensions.
|
||||
*/
|
||||
void WinStatsFlameGraph::
|
||||
set_window_state(int x, int y, int width, int height,
|
||||
bool maximized, bool minimized) {
|
||||
WinStatsGraph::set_window_state(x, y, width, height, maximized, minimized);
|
||||
|
||||
// Set the state of the checkbox.
|
||||
SendMessage(_average_check_box, BM_SETCHECK, get_average_mode() ? BST_CHECKED : BST_UNCHECKED, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
@ -316,6 +359,14 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
|||
case 103:
|
||||
WinStatsGraph::_monitor->open_flame_graph(get_thread_index(), _popup_index);
|
||||
return 0;
|
||||
|
||||
case 104:
|
||||
WinStatsGraph::_monitor->choose_collector_color(_popup_index);
|
||||
return 0;
|
||||
|
||||
case 105:
|
||||
WinStatsGraph::_monitor->reset_collector_color(_popup_index);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -433,6 +484,9 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
|||
}
|
||||
AppendMenu(popup, MF_STRING, 102, "Open Strip Chart");
|
||||
AppendMenu(popup, MF_STRING, 103, "Open Flame Graph");
|
||||
AppendMenu(popup, MF_STRING | MF_SEPARATOR, 0, nullptr);
|
||||
AppendMenu(popup, MF_STRING, 104, "Change Color...");
|
||||
AppendMenu(popup, MF_STRING, 105, "Reset Color");
|
||||
TrackPopupMenu(popup, TPM_LEFTBUTTON, point.x, point.y, 0, _window, nullptr);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ public:
|
|||
virtual void on_enter_label(int collector_index);
|
||||
virtual void on_leave_label(int collector_index);
|
||||
|
||||
void set_collector_index(int collector_index);
|
||||
|
||||
protected:
|
||||
virtual void normal_guide_bars();
|
||||
|
||||
|
|
@ -51,6 +53,11 @@ protected:
|
|||
|
||||
virtual bool animate(double time, double dt);
|
||||
|
||||
virtual bool get_window_state(int &x, int &y, int &width, int &height,
|
||||
bool &maximized, bool &minimized) const;
|
||||
virtual void set_window_state(int x, int y, int width, int height,
|
||||
bool maximized, bool minimized);
|
||||
|
||||
LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
|
||||
virtual LONG graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
|
||||
virtual void additional_window_paint(HDC hdc);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include "winStatsGraph.h"
|
||||
#include "winStatsMonitor.h"
|
||||
#include "winStatsLabelStack.h"
|
||||
#include "winStatsServer.h"
|
||||
#include "trueClock.h"
|
||||
#include "convert_srgb.h"
|
||||
|
||||
|
|
@ -281,6 +282,57 @@ animate(double time, double dt) {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current window dimensions.
|
||||
*/
|
||||
void WinStatsGraph::
|
||||
get_window_state(int &x, int &y, int &width, int &height,
|
||||
bool &maximized, bool &minimized) const {
|
||||
WinStatsServer *server = (WinStatsServer *)_monitor->get_server();
|
||||
POINT client_origin = server->get_client_origin();
|
||||
WINDOWPLACEMENT wp;
|
||||
wp.length = sizeof(WINDOWPLACEMENT);
|
||||
GetWindowPlacement(_window, &wp);
|
||||
x = wp.rcNormalPosition.left - client_origin.x;
|
||||
y = wp.rcNormalPosition.top - client_origin.y;
|
||||
width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
|
||||
height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
|
||||
maximized = (wp.showCmd == SW_SHOWMAXIMIZED || (wp.flags & WPF_RESTORETOMAXIMIZED) != 0);
|
||||
minimized = (wp.showCmd == SW_SHOWMINIMIZED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to restore the graph window to its previous dimensions.
|
||||
*/
|
||||
void WinStatsGraph::
|
||||
set_window_state(int x, int y, int width, int height,
|
||||
bool maximized, bool minimized) {
|
||||
WinStatsServer *server = (WinStatsServer *)_monitor->get_server();
|
||||
POINT client_origin = server->get_client_origin();
|
||||
WINDOWPLACEMENT wp;
|
||||
wp.length = sizeof(WINDOWPLACEMENT);
|
||||
wp.flags = maximized ? WPF_RESTORETOMAXIMIZED : 0;
|
||||
wp.showCmd = minimized ? SW_SHOWMINIMIZED : (maximized ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL);
|
||||
wp.ptMinPosition.x = -1;
|
||||
wp.ptMinPosition.y = -1;
|
||||
wp.ptMaxPosition.x = -1;
|
||||
wp.ptMaxPosition.y = -1;
|
||||
wp.rcNormalPosition.left = client_origin.x + x;
|
||||
wp.rcNormalPosition.top = client_origin.y + y;
|
||||
wp.rcNormalPosition.right = wp.rcNormalPosition.left + width;
|
||||
wp.rcNormalPosition.bottom = wp.rcNormalPosition.top + height;
|
||||
|
||||
if (minimized) {
|
||||
int x, y;
|
||||
_monitor->calc_iconic_graph_window_pos(this, x, y);
|
||||
wp.ptMinPosition.x = x;
|
||||
wp.ptMinPosition.y = y;
|
||||
wp.flags |= WPF_SETMINPOSITION;
|
||||
}
|
||||
|
||||
SetWindowPlacement(_window, &wp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a brush suitable for drawing in the indicated collector's color.
|
||||
*/
|
||||
|
|
@ -331,6 +383,17 @@ get_collector_text_color(int collector_index, bool highlight) {
|
|||
return highlight ? hcolor : color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the given collector has changed colors.
|
||||
*/
|
||||
void WinStatsGraph::
|
||||
reset_collector_color(int collector_index) {
|
||||
_brushes.erase(collector_index);
|
||||
_text_colors.erase(collector_index);
|
||||
force_redraw();
|
||||
_label_stack.update_label_color(collector_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* This window_proc should be called up to by the derived classes for any
|
||||
* messages that are not specifically handled by the derived class.
|
||||
|
|
@ -356,6 +419,31 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
case WM_WINDOWPOSCHANGING:
|
||||
{
|
||||
WINDOWPOS &pos = *(WINDOWPOS *)lparam;
|
||||
if ((pos.flags & (SWP_NOMOVE | SWP_NOSIZE)) == 0 && IsIconic(hwnd)) {
|
||||
_monitor->calc_iconic_graph_window_pos(this, pos.x, pos.y);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_SYSCOMMAND:
|
||||
if (wparam == SC_MINIMIZE) {
|
||||
WINDOWPLACEMENT wp;
|
||||
if (GetWindowPlacement(hwnd, &wp)) {
|
||||
int x, y;
|
||||
_monitor->calc_iconic_graph_window_pos(this, x, y);
|
||||
wp.showCmd = SW_SHOWMINIMIZED;
|
||||
wp.flags |= WPF_SETMINPOSITION;
|
||||
wp.ptMinPosition.x = x;
|
||||
wp.ptMinPosition.y = y;
|
||||
SetWindowPlacement(hwnd, &wp);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_SIZE:
|
||||
move_label_stack();
|
||||
InvalidateRect(hwnd, nullptr, TRUE);
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
class PStatGraph;
|
||||
class WinStatsMonitor;
|
||||
|
||||
/**
|
||||
|
|
@ -67,6 +68,8 @@ public:
|
|||
|
||||
HWND get_window();
|
||||
|
||||
void reset_collector_color(int collector_index);
|
||||
|
||||
protected:
|
||||
void close();
|
||||
|
||||
|
|
@ -76,6 +79,11 @@ protected:
|
|||
void start_animation();
|
||||
virtual bool animate(double time, double dt);
|
||||
|
||||
void get_window_state(int &x, int &y, int &width, int &height,
|
||||
bool &maximized, bool &minimized) const;
|
||||
void set_window_state(int x, int y, int width, int height,
|
||||
bool maximized, bool minimized);
|
||||
|
||||
HBRUSH get_collector_brush(int collector_index, bool highlight = false);
|
||||
COLORREF get_collector_text_color(int collector_index, bool highlight = false);
|
||||
|
||||
|
|
|
|||
|
|
@ -42,35 +42,7 @@ WinStatsLabel(WinStatsMonitor *monitor, WinStatsGraph *graph,
|
|||
_tooltip_window(0)
|
||||
{
|
||||
update_text(use_fullname);
|
||||
|
||||
LRGBColor rgb = _monitor->get_collector_color(_collector_index);
|
||||
int r = (int)encode_sRGB_uchar((float)rgb[0]);
|
||||
int g = (int)encode_sRGB_uchar((float)rgb[1]);
|
||||
int b = (int)encode_sRGB_uchar((float)rgb[2]);
|
||||
_bg_brush = CreateSolidBrush(RGB(r, g, b));
|
||||
|
||||
// Calculate the color when it is highlighted.
|
||||
int hr = (int)encode_sRGB_uchar((float)rgb[0] * 0.75f);
|
||||
int hg = (int)encode_sRGB_uchar((float)rgb[1] * 0.75f);
|
||||
int hb = (int)encode_sRGB_uchar((float)rgb[2] * 0.75f);
|
||||
_highlight_bg_brush = CreateSolidBrush(RGB(hr, hg, hb));
|
||||
|
||||
// Should our foreground be black or white?
|
||||
double bright =
|
||||
rgb[0] * 0.2126 +
|
||||
rgb[1] * 0.7152 +
|
||||
rgb[2] * 0.0722;
|
||||
|
||||
if (bright >= 0.5) {
|
||||
_fg_color = RGB(0, 0, 0);
|
||||
} else {
|
||||
_fg_color = RGB(255, 255, 255);
|
||||
}
|
||||
if (bright * 0.75 >= 0.5) {
|
||||
_highlight_fg_color = RGB(0, 0, 0);
|
||||
} else {
|
||||
_highlight_fg_color = RGB(255, 255, 255);
|
||||
}
|
||||
update_color();
|
||||
|
||||
_x = 0;
|
||||
_y = 0;
|
||||
|
|
@ -95,6 +67,7 @@ WinStatsLabel::
|
|||
_window = 0;
|
||||
}
|
||||
DeleteObject(_bg_brush);
|
||||
DeleteObject(_highlight_bg_brush);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -147,6 +120,52 @@ set_highlight(bool highlight) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the colors.
|
||||
*/
|
||||
void WinStatsLabel::
|
||||
update_color() {
|
||||
if (_bg_brush != 0) {
|
||||
DeleteObject(_bg_brush);
|
||||
}
|
||||
if (_highlight_bg_brush != 0) {
|
||||
DeleteObject(_highlight_bg_brush);
|
||||
}
|
||||
|
||||
LRGBColor rgb = _monitor->get_collector_color(_collector_index);
|
||||
int r = (int)encode_sRGB_uchar((float)rgb[0]);
|
||||
int g = (int)encode_sRGB_uchar((float)rgb[1]);
|
||||
int b = (int)encode_sRGB_uchar((float)rgb[2]);
|
||||
_bg_brush = CreateSolidBrush(RGB(r, g, b));
|
||||
|
||||
// Calculate the color when it is highlighted.
|
||||
int hr = (int)encode_sRGB_uchar((float)rgb[0] * 0.75f);
|
||||
int hg = (int)encode_sRGB_uchar((float)rgb[1] * 0.75f);
|
||||
int hb = (int)encode_sRGB_uchar((float)rgb[2] * 0.75f);
|
||||
_highlight_bg_brush = CreateSolidBrush(RGB(hr, hg, hb));
|
||||
|
||||
// Should our foreground be black or white?
|
||||
double bright =
|
||||
rgb[0] * 0.2126 +
|
||||
rgb[1] * 0.7152 +
|
||||
rgb[2] * 0.0722;
|
||||
|
||||
if (bright >= 0.5) {
|
||||
_fg_color = RGB(0, 0, 0);
|
||||
} else {
|
||||
_fg_color = RGB(255, 255, 255);
|
||||
}
|
||||
if (bright * 0.75 >= 0.5) {
|
||||
_highlight_fg_color = RGB(0, 0, 0);
|
||||
} else {
|
||||
_highlight_fg_color = RGB(255, 255, 255);
|
||||
}
|
||||
|
||||
if (_window) {
|
||||
InvalidateRect(_window, nullptr, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if the full name of the collector should be shown.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ public:
|
|||
void set_highlight(bool highlight);
|
||||
INLINE bool get_highlight() const;
|
||||
|
||||
void update_color();
|
||||
void update_text(bool use_fullname);
|
||||
|
||||
private:
|
||||
|
|
@ -71,8 +72,8 @@ private:
|
|||
HWND _tooltip_window;
|
||||
COLORREF _fg_color;
|
||||
COLORREF _highlight_fg_color;
|
||||
HBRUSH _bg_brush;
|
||||
HBRUSH _highlight_bg_brush;
|
||||
HBRUSH _bg_brush = 0;
|
||||
HBRUSH _highlight_bg_brush = 0;
|
||||
|
||||
int _x;
|
||||
int _y;
|
||||
|
|
|
|||
|
|
@ -57,9 +57,7 @@ setup(HWND parent_window) {
|
|||
create_window(parent_window);
|
||||
|
||||
_ideal_width = 0;
|
||||
Labels::iterator li;
|
||||
for (li = _labels.begin(); li != _labels.end(); ++li) {
|
||||
WinStatsLabel *label = (*li);
|
||||
for (WinStatsLabel *label : _labels) {
|
||||
label->setup(_window);
|
||||
_ideal_width = std::max(_ideal_width, label->get_ideal_width());
|
||||
}
|
||||
|
|
@ -85,10 +83,8 @@ set_pos(int x, int y, int width, int height) {
|
|||
SetWindowPos(_window, 0, x, y, _width, _height,
|
||||
SWP_NOZORDER | SWP_SHOWWINDOW);
|
||||
|
||||
Labels::iterator li;
|
||||
int yp = height;
|
||||
for (li = _labels.begin(); li != _labels.end(); ++li) {
|
||||
WinStatsLabel *label = (*li);
|
||||
for (WinStatsLabel *label : _labels) {
|
||||
label->set_pos(0, yp, _width);
|
||||
yp -= label->get_height();
|
||||
}
|
||||
|
|
@ -167,9 +163,8 @@ get_label_collector_index(int label_index) const {
|
|||
*/
|
||||
void WinStatsLabelStack::
|
||||
clear_labels() {
|
||||
Labels::iterator li;
|
||||
for (li = _labels.begin(); li != _labels.end(); ++li) {
|
||||
delete (*li);
|
||||
for (WinStatsLabel *label : _labels) {
|
||||
delete label;
|
||||
}
|
||||
_labels.clear();
|
||||
_ideal_width = 0;
|
||||
|
|
@ -301,14 +296,25 @@ void WinStatsLabelStack::
|
|||
highlight_label(int collector_index) {
|
||||
if (_highlight_label != collector_index) {
|
||||
_highlight_label = collector_index;
|
||||
Labels::iterator li;
|
||||
for (li = _labels.begin(); li != _labels.end(); ++li) {
|
||||
WinStatsLabel *label = (*li);
|
||||
|
||||
for (WinStatsLabel *label : _labels) {
|
||||
label->set_highlight(label->get_collector_index() == _highlight_label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the color of the label with the given index.
|
||||
*/
|
||||
void WinStatsLabelStack::
|
||||
update_label_color(int collector_index) {
|
||||
for (WinStatsLabel *label : _labels) {
|
||||
if (label->get_collector_index() == collector_index) {
|
||||
label->update_color();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the window for this stack.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ public:
|
|||
int get_num_labels() const;
|
||||
|
||||
void highlight_label(int collector_index);
|
||||
void update_label_color(int collector_index);
|
||||
|
||||
private:
|
||||
void create_window(HWND parent_window);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,15 @@
|
|||
*/
|
||||
enum WinStatsMenuId {
|
||||
MI_none,
|
||||
|
||||
MI_session_new,
|
||||
MI_session_open,
|
||||
MI_session_open_last,
|
||||
MI_session_save,
|
||||
MI_session_close,
|
||||
MI_session_export_json,
|
||||
MI_exit,
|
||||
|
||||
MI_time_ms,
|
||||
MI_time_hz,
|
||||
MI_frame_rate_label,
|
||||
|
|
@ -32,6 +41,10 @@ enum WinStatsMenuId {
|
|||
MI_speed_12,
|
||||
MI_pause,
|
||||
|
||||
MI_graphs_close_all,
|
||||
MI_graphs_reopen_default,
|
||||
MI_graphs_save_default,
|
||||
|
||||
// This one is last and represents the beginning of the range for the
|
||||
// various "new chart" menu options.
|
||||
MI_new_chart
|
||||
|
|
|
|||
|
|
@ -23,45 +23,29 @@
|
|||
#include "pStatGraph.h"
|
||||
#include "pStatCollectorDef.h"
|
||||
|
||||
#include "convert_srgb.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <commctrl.h>
|
||||
|
||||
bool WinStatsMonitor::_window_class_registered = false;
|
||||
const char * const WinStatsMonitor::_window_class_name = "monitor";
|
||||
#include <commdlg.h>
|
||||
#include <uxtheme.h>
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
WinStatsMonitor::
|
||||
WinStatsMonitor(WinStatsServer *server) : PStatMonitor(server) {
|
||||
_window = 0;
|
||||
_menu_bar = 0;
|
||||
_options_menu = 0;
|
||||
_window = server->get_window();
|
||||
_menu_bar = server->get_menu_bar();
|
||||
_status_bar = server->get_status_bar();
|
||||
|
||||
// These will be filled in later when the menu is created.
|
||||
_time_units = 0;
|
||||
_scroll_speed = 0.0;
|
||||
_pause = false;
|
||||
|
||||
// Create the fonts used for rendering the UI.
|
||||
NONCLIENTMETRICS metrics = {0};
|
||||
metrics.cbSize = sizeof(NONCLIENTMETRICS);
|
||||
if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, 0)) {
|
||||
_font = CreateFontIndirect(&metrics.lfMenuFont);
|
||||
} else {
|
||||
_font = (HFONT)GetStockObject(ANSI_VAR_FONT);
|
||||
}
|
||||
|
||||
HDC dc = GetDC(nullptr);
|
||||
_pixel_scale = 0;
|
||||
if (dc) {
|
||||
_pixel_scale = GetDeviceCaps(dc, LOGPIXELSX) / (96 / 4);
|
||||
}
|
||||
if (_pixel_scale <= 0) {
|
||||
_pixel_scale = 4;
|
||||
}
|
||||
ReleaseDC(nullptr, dc);
|
||||
setup_speed_menu();
|
||||
setup_frame_rate_label();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -69,27 +53,28 @@ WinStatsMonitor(WinStatsServer *server) : PStatMonitor(server) {
|
|||
*/
|
||||
WinStatsMonitor::
|
||||
~WinStatsMonitor() {
|
||||
Graphs::iterator gi;
|
||||
for (gi = _graphs.begin(); gi != _graphs.end(); ++gi) {
|
||||
delete (*gi);
|
||||
}
|
||||
_graphs.clear();
|
||||
close();
|
||||
}
|
||||
|
||||
ChartMenus::iterator mi;
|
||||
for (mi = _chart_menus.begin(); mi != _chart_menus.end(); ++mi) {
|
||||
delete (*mi);
|
||||
/**
|
||||
* Closes the client connection if it is active.
|
||||
*/
|
||||
void WinStatsMonitor::
|
||||
close() {
|
||||
PStatMonitor::close();
|
||||
|
||||
remove_all_graphs();
|
||||
|
||||
RemoveMenu(_menu_bar, 2, MF_BYPOSITION);
|
||||
RemoveMenu(_menu_bar, 2, MF_BYPOSITION);
|
||||
|
||||
for (WinStatsChartMenu *chart_menu : _chart_menus) {
|
||||
RemoveMenu(_menu_bar, 2, MF_BYPOSITION);
|
||||
delete chart_menu;
|
||||
}
|
||||
_chart_menus.clear();
|
||||
|
||||
if (_window) {
|
||||
DestroyWindow(_window);
|
||||
_window = 0;
|
||||
}
|
||||
|
||||
#ifdef DEVELOP_WINSTATS
|
||||
// For Winstats developers, exit when the first monitor closes.
|
||||
exit(0);
|
||||
#endif
|
||||
DrawMenuBar(_window);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -118,8 +103,6 @@ initialized() {
|
|||
*/
|
||||
void WinStatsMonitor::
|
||||
got_hello() {
|
||||
create_window();
|
||||
open_strip_chart(0, 0, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -160,16 +143,13 @@ got_bad_version(int client_major, int client_minor,
|
|||
*/
|
||||
void WinStatsMonitor::
|
||||
new_collector(int collector_index) {
|
||||
Graphs::iterator gi;
|
||||
for (gi = _graphs.begin(); gi != _graphs.end(); ++gi) {
|
||||
WinStatsGraph *graph = (*gi);
|
||||
for (WinStatsGraph *graph : _graphs) {
|
||||
graph->new_collector(collector_index);
|
||||
}
|
||||
|
||||
// We might need to update our menus.
|
||||
ChartMenus::iterator mi;
|
||||
for (mi = _chart_menus.begin(); mi != _chart_menus.end(); ++mi) {
|
||||
(*mi)->do_update();
|
||||
for (WinStatsChartMenu *chart_menu : _chart_menus) {
|
||||
chart_menu->do_update();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -186,6 +166,10 @@ new_thread(int thread_index) {
|
|||
chart_menu->add_to_menu_bar(_menu_bar, MI_frame_rate_label);
|
||||
_chart_menus.push_back(chart_menu);
|
||||
DrawMenuBar(_window);
|
||||
|
||||
if (thread_index == 0) {
|
||||
update_status_bar();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -203,6 +187,11 @@ new_data(int thread_index, int frame_number) {
|
|||
if (thread_index == 0) {
|
||||
update_status_bar();
|
||||
}
|
||||
|
||||
if (!_have_data) {
|
||||
open_default_graphs();
|
||||
_have_data = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -213,11 +202,6 @@ new_data(int thread_index, int frame_number) {
|
|||
void WinStatsMonitor::
|
||||
lost_connection() {
|
||||
nout << "Lost connection to " << get_client_hostname() << "\n";
|
||||
|
||||
if (_window) {
|
||||
DestroyWindow(_window);
|
||||
_window = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -227,9 +211,8 @@ lost_connection() {
|
|||
void WinStatsMonitor::
|
||||
idle() {
|
||||
// Check if any of our chart menus need updating.
|
||||
ChartMenus::iterator mi;
|
||||
for (mi = _chart_menus.begin(); mi != _chart_menus.end(); ++mi) {
|
||||
(*mi)->check_update();
|
||||
for (WinStatsChartMenu *chart_menu : _chart_menus) {
|
||||
chart_menu->check_update();
|
||||
}
|
||||
|
||||
// Update the frame rate label from the main thread (thread 0).
|
||||
|
|
@ -268,9 +251,7 @@ has_idle() {
|
|||
*/
|
||||
void WinStatsMonitor::
|
||||
user_guide_bars_changed() {
|
||||
Graphs::iterator gi;
|
||||
for (gi = _graphs.begin(); gi != _graphs.end(); ++gi) {
|
||||
WinStatsGraph *graph = (*gi);
|
||||
for (WinStatsGraph *graph : _graphs) {
|
||||
graph->user_guide_bars_changed();
|
||||
}
|
||||
}
|
||||
|
|
@ -288,7 +269,7 @@ get_window() const {
|
|||
*/
|
||||
HFONT WinStatsMonitor::
|
||||
get_font() const {
|
||||
return _font;
|
||||
return ((WinStatsServer *)_server)->get_font();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -296,7 +277,7 @@ get_font() const {
|
|||
*/
|
||||
int WinStatsMonitor::
|
||||
get_pixel_scale() const {
|
||||
return _pixel_scale;
|
||||
return ((WinStatsServer *)_server)->get_pixel_scale();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -304,64 +285,100 @@ get_pixel_scale() const {
|
|||
*/
|
||||
POINT WinStatsMonitor::
|
||||
get_new_window_pos() {
|
||||
int offset = _graphs.size() * 10 * _pixel_scale;
|
||||
WinStatsServer *server = (WinStatsServer *)_server;
|
||||
int offset = _graphs.size() * 10 * server->get_pixel_scale();
|
||||
POINT client_origin = server->get_client_origin();
|
||||
POINT pt;
|
||||
pt.x = offset + _client_origin.x;
|
||||
pt.y = offset + _client_origin.y;
|
||||
pt.x = offset + client_origin.x;
|
||||
pt.y = offset + client_origin.y;
|
||||
return pt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new strip chart showing the indicated data.
|
||||
*/
|
||||
void WinStatsMonitor::
|
||||
open_strip_chart(int thread_index, int collector_index, bool show_level) {
|
||||
WinStatsStripChart *graph =
|
||||
new WinStatsStripChart(this, thread_index, collector_index, show_level);
|
||||
add_graph(graph);
|
||||
|
||||
graph->set_time_units(_time_units);
|
||||
graph->set_scroll_speed(_scroll_speed);
|
||||
graph->set_pause(_pause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new piano roll showing the indicated data.
|
||||
*/
|
||||
void WinStatsMonitor::
|
||||
open_piano_roll(int thread_index) {
|
||||
WinStatsPianoRoll *graph = new WinStatsPianoRoll(this, thread_index);
|
||||
add_graph(graph);
|
||||
|
||||
graph->set_time_units(_time_units);
|
||||
graph->set_scroll_speed(_scroll_speed);
|
||||
graph->set_pause(_pause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new flame graph showing the indicated data.
|
||||
*/
|
||||
void WinStatsMonitor::
|
||||
open_flame_graph(int thread_index, int collector_index) {
|
||||
WinStatsFlameGraph *graph = new WinStatsFlameGraph(this, thread_index, collector_index);
|
||||
add_graph(graph);
|
||||
|
||||
graph->set_time_units(_time_units);
|
||||
graph->set_scroll_speed(_scroll_speed);
|
||||
graph->set_pause(_pause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new timeline.
|
||||
*/
|
||||
void WinStatsMonitor::
|
||||
PStatGraph *WinStatsMonitor::
|
||||
open_timeline() {
|
||||
WinStatsTimeline *graph = new WinStatsTimeline(this);
|
||||
add_graph(graph);
|
||||
return graph;
|
||||
}
|
||||
|
||||
graph->set_time_units(_time_units);
|
||||
graph->set_scroll_speed(_scroll_speed);
|
||||
graph->set_pause(_pause);
|
||||
/**
|
||||
* Opens a new strip chart showing the indicated data.
|
||||
*/
|
||||
PStatGraph *WinStatsMonitor::
|
||||
open_strip_chart(int thread_index, int collector_index, bool show_level) {
|
||||
WinStatsStripChart *graph =
|
||||
new WinStatsStripChart(this, thread_index, collector_index, show_level);
|
||||
add_graph(graph);
|
||||
return graph;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new flame graph showing the indicated data.
|
||||
*/
|
||||
PStatGraph *WinStatsMonitor::
|
||||
open_flame_graph(int thread_index, int collector_index) {
|
||||
WinStatsFlameGraph *graph = new WinStatsFlameGraph(this, thread_index, collector_index);
|
||||
add_graph(graph);
|
||||
return graph;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new piano roll showing the indicated data.
|
||||
*/
|
||||
PStatGraph *WinStatsMonitor::
|
||||
open_piano_roll(int thread_index) {
|
||||
WinStatsPianoRoll *graph = new WinStatsPianoRoll(this, thread_index);
|
||||
add_graph(graph);
|
||||
return graph;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a dialog to change the given collector color.
|
||||
*/
|
||||
void WinStatsMonitor::
|
||||
choose_collector_color(int collector_index) {
|
||||
const LRGBColor ¤t = get_collector_color(collector_index);
|
||||
static COLORREF custom_colors[16] = {0};
|
||||
|
||||
CHOOSECOLORA cc = {
|
||||
sizeof(CHOOSECOLORA),
|
||||
_window,
|
||||
0,
|
||||
RGB(encode_sRGB_uchar(current[0]), encode_sRGB_uchar(current[1]), encode_sRGB_uchar(current[2])),
|
||||
(LPDWORD)custom_colors,
|
||||
CC_FULLOPEN | CC_RGBINIT,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
};
|
||||
|
||||
if (ChooseColorA(&cc)) {
|
||||
LRGBColor result(
|
||||
decode_sRGB_float(GetRValue(cc.rgbResult)),
|
||||
decode_sRGB_float(GetGValue(cc.rgbResult)),
|
||||
decode_sRGB_float(GetBValue(cc.rgbResult)));
|
||||
|
||||
set_collector_color(collector_index, result);
|
||||
|
||||
for (WinStatsGraph *graph : _graphs) {
|
||||
graph->reset_collector_color(collector_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the color of the given collector to the default.
|
||||
*/
|
||||
void WinStatsMonitor::
|
||||
reset_collector_color(int collector_index) {
|
||||
clear_collector_color(collector_index);
|
||||
|
||||
for (WinStatsGraph *graph : _graphs) {
|
||||
graph->reset_collector_color(collector_index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -404,28 +421,9 @@ get_menu_id(const MenuDef &menu_def) {
|
|||
*/
|
||||
void WinStatsMonitor::
|
||||
set_time_units(int unit_mask) {
|
||||
_time_units = unit_mask;
|
||||
|
||||
// First, change all of the open graphs appropriately.
|
||||
Graphs::iterator gi;
|
||||
for (gi = _graphs.begin(); gi != _graphs.end(); ++gi) {
|
||||
WinStatsGraph *graph = (*gi);
|
||||
graph->set_time_units(_time_units);
|
||||
for (WinStatsGraph *graph : _graphs) {
|
||||
graph->set_time_units(unit_mask);
|
||||
}
|
||||
|
||||
// Now change the checkmark on the pulldown menu.
|
||||
MENUITEMINFO mii;
|
||||
memset(&mii, 0, sizeof(mii));
|
||||
mii.cbSize = sizeof(mii);
|
||||
mii.fMask = MIIM_STATE;
|
||||
|
||||
mii.fState = ((_time_units & PStatGraph::GBU_ms) != 0) ?
|
||||
MFS_CHECKED : MFS_UNCHECKED;
|
||||
SetMenuItemInfo(_options_menu, MI_time_ms, FALSE, &mii);
|
||||
|
||||
mii.fState = ((_time_units & PStatGraph::GBU_hz) != 0) ?
|
||||
MFS_CHECKED : MFS_UNCHECKED;
|
||||
SetMenuItemInfo(_options_menu, MI_time_hz, FALSE, &mii);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -437,9 +435,7 @@ set_scroll_speed(double scroll_speed) {
|
|||
_scroll_speed = scroll_speed;
|
||||
|
||||
// First, change all of the open graphs appropriately.
|
||||
Graphs::iterator gi;
|
||||
for (gi = _graphs.begin(); gi != _graphs.end(); ++gi) {
|
||||
WinStatsGraph *graph = (*gi);
|
||||
for (WinStatsGraph *graph : _graphs) {
|
||||
graph->set_scroll_speed(_scroll_speed);
|
||||
}
|
||||
|
||||
|
|
@ -478,9 +474,7 @@ set_pause(bool pause) {
|
|||
_pause = pause;
|
||||
|
||||
// First, change all of the open graphs appropriately.
|
||||
Graphs::iterator gi;
|
||||
for (gi = _graphs.begin(); gi != _graphs.end(); ++gi) {
|
||||
WinStatsGraph *graph = (*gi);
|
||||
for (WinStatsGraph *graph : _graphs) {
|
||||
graph->set_pause(_pause);
|
||||
}
|
||||
|
||||
|
|
@ -500,6 +494,10 @@ set_pause(bool pause) {
|
|||
void WinStatsMonitor::
|
||||
add_graph(WinStatsGraph *graph) {
|
||||
_graphs.insert(graph);
|
||||
|
||||
graph->set_time_units(((WinStatsServer *)_server)->get_time_units());
|
||||
graph->set_scroll_speed(_scroll_speed);
|
||||
graph->set_pause(_pause);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -515,91 +513,14 @@ remove_graph(WinStatsGraph *graph) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates the window for this monitor.
|
||||
* Deletes all open graphs.
|
||||
*/
|
||||
void WinStatsMonitor::
|
||||
create_window() {
|
||||
if (_window) {
|
||||
return;
|
||||
remove_all_graphs() {
|
||||
for (WinStatsGraph *graph : _graphs) {
|
||||
delete graph;
|
||||
}
|
||||
|
||||
HINSTANCE application = GetModuleHandle(nullptr);
|
||||
register_window_class(application);
|
||||
|
||||
_menu_bar = CreateMenu();
|
||||
|
||||
setup_options_menu();
|
||||
setup_speed_menu();
|
||||
setup_frame_rate_label();
|
||||
|
||||
ChartMenus::iterator mi;
|
||||
for (mi = _chart_menus.begin(); mi != _chart_menus.end(); ++mi) {
|
||||
(*mi)->add_to_menu_bar(_menu_bar, MI_frame_rate_label);
|
||||
}
|
||||
|
||||
_window_title = get_client_progname() + " on " + get_client_hostname();
|
||||
DWORD window_style = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN |
|
||||
WS_CLIPSIBLINGS | WS_VISIBLE;
|
||||
|
||||
_window =
|
||||
CreateWindow(_window_class_name, _window_title.c_str(), window_style,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
nullptr, _menu_bar, application, 0);
|
||||
if (!_window) {
|
||||
nout << "Could not create monitor window!\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
SetWindowLongPtr(_window, 0, (LONG_PTR)this);
|
||||
|
||||
create_status_bar(application);
|
||||
|
||||
// For some reason, SW_SHOWNORMAL doesn't always work, but SW_RESTORE seems
|
||||
// to.
|
||||
ShowWindow(_window, SW_RESTORE);
|
||||
SetForegroundWindow(_window);
|
||||
|
||||
_client_origin.x = 0;
|
||||
_client_origin.y = 0;
|
||||
ClientToScreen(_window, &_client_origin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the "Options" pulldown menu.
|
||||
*/
|
||||
void WinStatsMonitor::
|
||||
setup_options_menu() {
|
||||
_options_menu = CreatePopupMenu();
|
||||
|
||||
MENUITEMINFO mii;
|
||||
memset(&mii, 0, sizeof(mii));
|
||||
mii.cbSize = sizeof(mii);
|
||||
|
||||
mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_SUBMENU;
|
||||
mii.fType = MFT_STRING;
|
||||
mii.hSubMenu = _options_menu;
|
||||
|
||||
// One day, when there is more than one option here, we will actually
|
||||
// present this to the user as the "Options" menu. For now, the only option
|
||||
// we have is time units. mii.dwTypeData = "Options";
|
||||
mii.dwTypeData = "Units";
|
||||
InsertMenuItem(_menu_bar, GetMenuItemCount(_menu_bar), TRUE, &mii);
|
||||
|
||||
|
||||
mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_CHECKMARKS | MIIM_STATE;
|
||||
mii.fType = MFT_STRING | MFT_RADIOCHECK;
|
||||
mii.hbmpChecked = nullptr;
|
||||
mii.hbmpUnchecked = nullptr;
|
||||
mii.fState = MFS_UNCHECKED;
|
||||
mii.wID = MI_time_ms;
|
||||
mii.dwTypeData = "ms";
|
||||
InsertMenuItem(_options_menu, GetMenuItemCount(_options_menu), TRUE, &mii);
|
||||
|
||||
mii.wID = MI_time_hz;
|
||||
mii.dwTypeData = "Hz";
|
||||
InsertMenuItem(_options_menu, GetMenuItemCount(_options_menu), TRUE, &mii);
|
||||
|
||||
set_time_units(PStatGraph::GBU_ms);
|
||||
_graphs.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -678,26 +599,6 @@ setup_frame_rate_label() {
|
|||
InsertMenuItem(_menu_bar, GetMenuItemCount(_menu_bar), TRUE, &mii);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a status bar at the bottom of the screen showing assorted level
|
||||
* values.
|
||||
*/
|
||||
void WinStatsMonitor::
|
||||
create_status_bar(HINSTANCE application) {
|
||||
_status_bar = CreateWindow(STATUSCLASSNAME, nullptr,
|
||||
SBARS_SIZEGRIP | WS_CHILD | WS_VISIBLE,
|
||||
0, 0, 0, 0,
|
||||
_window, (HMENU)0, application, nullptr);
|
||||
|
||||
update_status_bar();
|
||||
|
||||
|
||||
ShowWindow(_status_bar, SW_SHOW);
|
||||
UpdateWindow(_status_bar);
|
||||
|
||||
InvalidateRect(_status_bar, NULL, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the status bar.
|
||||
*/
|
||||
|
|
@ -758,8 +659,10 @@ update_status_bar() {
|
|||
HLOCAL hloc = LocalAlloc(LHND, sizeof(int) * (parts.size() + 1));
|
||||
PINT sizes = (PINT)LocalLock(hloc);
|
||||
|
||||
int pixel_scale = get_pixel_scale();
|
||||
|
||||
// Allocate the left-most slot for the framerate indicator.
|
||||
double offset = 28.0 * _pixel_scale;
|
||||
double offset = 28.0 * pixel_scale;
|
||||
sizes[0] = (int)(offset + 0.5);
|
||||
|
||||
if (!parts.empty()) {
|
||||
|
|
@ -769,11 +672,11 @@ update_status_bar() {
|
|||
double width_per_char = 0;
|
||||
GetClientRect(_status_bar, &rect);
|
||||
// Leave room for the grip.
|
||||
rect.right -= _pixel_scale * 4;
|
||||
rect.right -= pixel_scale * 4;
|
||||
width_per_char = (rect.right - rect.left - offset) / (double)total_chars;
|
||||
|
||||
// If we get below a minimum width, start chopping parts off.
|
||||
while (!parts.empty() && width_per_char < _pixel_scale * 1.5) {
|
||||
while (!parts.empty() && width_per_char < pixel_scale * 1.5) {
|
||||
total_chars -= parts.back().size();
|
||||
parts.pop_back();
|
||||
width_per_char = (rect.right - rect.left - offset) / (double)total_chars;
|
||||
|
|
@ -846,152 +749,89 @@ show_popup_menu(int collector) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Registers the window class for the monitor window, if it has not already
|
||||
* been registered.
|
||||
* Called when a graph window is iconified or moved in the iconified state.
|
||||
*/
|
||||
void WinStatsMonitor::
|
||||
register_window_class(HINSTANCE application) {
|
||||
if (_window_class_registered) {
|
||||
calc_iconic_graph_window_pos(WinStatsGraph *moved_graph, int &x, int &y) {
|
||||
MINIMIZEDMETRICS metrics;
|
||||
metrics.cbSize = sizeof(metrics);
|
||||
SystemParametersInfoA(SPI_GETMINIMIZEDMETRICS, 0, &metrics, 0);
|
||||
|
||||
int height = GetThemeSysSize(nullptr, SM_CYSIZE) + GetThemeSysSize(nullptr, SM_CXPADDEDBORDER) * 2;
|
||||
|
||||
RECT client_rect;
|
||||
GetClientRect(_window, &client_rect);
|
||||
MapWindowPoints(_window, nullptr, (POINT *)&client_rect, 2);
|
||||
|
||||
// Remove the status bar from the client rectangle.
|
||||
RECT status_bar_rect;
|
||||
GetWindowRect(_status_bar, &status_bar_rect);
|
||||
client_rect.bottom -= (status_bar_rect.bottom - status_bar_rect.top);
|
||||
|
||||
int iconic_offset = 0;
|
||||
|
||||
for (WinStatsGraph *graph : _graphs) {
|
||||
RECT child_rect;
|
||||
HWND window = graph->get_window();
|
||||
if (graph == moved_graph) {
|
||||
x = client_rect.left + iconic_offset;
|
||||
y = client_rect.bottom - height;
|
||||
iconic_offset += metrics.iWidth + metrics.iHorzGap;
|
||||
}
|
||||
else if (IsIconic(window) && GetWindowRect(window, &child_rect)) {
|
||||
// Keep it glued to the bottom-left corner of the parent window.
|
||||
child_rect.left = client_rect.left + iconic_offset;
|
||||
child_rect.top = client_rect.bottom - height;
|
||||
iconic_offset += metrics.iWidth + metrics.iHorzGap;
|
||||
|
||||
SetWindowPos(window, 0, child_rect.left, child_rect.top, 0, 0,
|
||||
SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the server window is moved.
|
||||
*/
|
||||
void WinStatsMonitor::
|
||||
handle_window_moved(const RECT &client_rect, int delta_x, int delta_y) {
|
||||
if (_graphs.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
WNDCLASS wc;
|
||||
MINIMIZEDMETRICS metrics;
|
||||
metrics.cbSize = sizeof(metrics);
|
||||
SystemParametersInfoA(SPI_GETMINIMIZEDMETRICS, 0, &metrics, 0);
|
||||
|
||||
ZeroMemory(&wc, sizeof(WNDCLASS));
|
||||
wc.style = 0;
|
||||
wc.lpfnWndProc = (WNDPROC)static_window_proc;
|
||||
wc.hInstance = application;
|
||||
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
|
||||
wc.lpszMenuName = nullptr;
|
||||
wc.lpszClassName = _window_class_name;
|
||||
int height = GetThemeSysSize(nullptr, SM_CYSIZE) + GetThemeSysSize(nullptr, SM_CXPADDEDBORDER) * 2;
|
||||
|
||||
// Reserve space to associate the this pointer with the window.
|
||||
wc.cbWndExtra = sizeof(WinStatsMonitor *);
|
||||
int iconic_offset = 0;
|
||||
|
||||
if (!RegisterClass(&wc)) {
|
||||
nout << "Could not register monitor window class!\n";
|
||||
exit(1);
|
||||
}
|
||||
for (WinStatsGraph *graph : _graphs) {
|
||||
WINDOWPLACEMENT wp;
|
||||
HWND window = graph->get_window();
|
||||
if (GetWindowPlacement(window, &wp)) {
|
||||
if (wp.showCmd == SW_SHOWMINIMIZED) {
|
||||
// Keep the minimized window title bar glued to the bottom-left
|
||||
// corner of the parent window.
|
||||
wp.flags |= WPF_SETMINPOSITION;
|
||||
wp.ptMinPosition.x = client_rect.left + iconic_offset;
|
||||
wp.ptMinPosition.y = client_rect.bottom - height;
|
||||
iconic_offset += metrics.iWidth + metrics.iHorzGap;
|
||||
}
|
||||
|
||||
_window_class_registered = true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
LONG WINAPI WinStatsMonitor::
|
||||
static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||
WinStatsMonitor *self = (WinStatsMonitor *)GetWindowLongPtr(hwnd, 0);
|
||||
if (self != nullptr && self->_window == hwnd) {
|
||||
return self->window_proc(hwnd, msg, wparam, lparam);
|
||||
} else {
|
||||
return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
// Move the "restored" window (even when it's currently min/maximized).
|
||||
wp.rcNormalPosition.left += delta_x;
|
||||
wp.rcNormalPosition.top += delta_y;
|
||||
wp.rcNormalPosition.right += delta_x;
|
||||
wp.rcNormalPosition.bottom += delta_y;
|
||||
SetWindowPlacement(window, &wp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
LONG WinStatsMonitor::
|
||||
window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||
switch (msg) {
|
||||
case WM_DESTROY:
|
||||
close();
|
||||
break;
|
||||
|
||||
case WM_WINDOWPOSCHANGED:
|
||||
if (!_graphs.empty()) {
|
||||
RECT status_bar_rect;
|
||||
GetWindowRect(_status_bar, &status_bar_rect);
|
||||
|
||||
RECT client_rect;
|
||||
GetClientRect(_window, &client_rect);
|
||||
MapWindowPoints(_window, nullptr, (POINT *)&client_rect, 2);
|
||||
|
||||
int delta_x = client_rect.left - _client_origin.x;
|
||||
int delta_y = client_rect.top - _client_origin.y;
|
||||
_client_origin.x = client_rect.left;
|
||||
_client_origin.y = client_rect.top;
|
||||
|
||||
int iconic_offset = 0;
|
||||
|
||||
for (WinStatsGraph *graph : _graphs) {
|
||||
RECT child_rect;
|
||||
HWND window = graph->get_window();
|
||||
if (GetWindowRect(window, &child_rect)) {
|
||||
if (IsIconic(window)) {
|
||||
// Keep it glued to the bottom-left corner of the parent window.
|
||||
child_rect.left = client_rect.left + iconic_offset;
|
||||
child_rect.top = client_rect.bottom - (child_rect.bottom - child_rect.top) - (status_bar_rect.bottom - status_bar_rect.top);
|
||||
iconic_offset += (child_rect.right - child_rect.left);
|
||||
} else {
|
||||
child_rect.left += delta_x;
|
||||
child_rect.top += delta_y;
|
||||
}
|
||||
SetWindowPos(window, 0, child_rect.left, child_rect.top, 0, 0,
|
||||
SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSIZE | SWP_NOREDRAW | SWP_NOACTIVATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_SIZE:
|
||||
if (_status_bar) {
|
||||
SendMessage(_status_bar, WM_SIZE, 0, 0);
|
||||
update_status_bar();
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_NOTIFY:
|
||||
if (((LPNMHDR)lparam)->code == NM_DBLCLK) {
|
||||
NMMOUSE &mouse = *(NMMOUSE *)lparam;
|
||||
if (mouse.dwItemSpec == 0) {
|
||||
open_strip_chart(0, 0, false);
|
||||
}
|
||||
else if (mouse.dwItemSpec >= 1 && mouse.dwItemSpec <= _status_bar_collectors.size()) {
|
||||
int collector = _status_bar_collectors[mouse.dwItemSpec - 1];
|
||||
open_strip_chart(0, collector, true);
|
||||
|
||||
// Also open a strip chart for other threads with data for this
|
||||
// collector.
|
||||
const PStatClientData *client_data = get_client_data();
|
||||
for (int thread_index = 1; thread_index < client_data->get_num_threads(); ++thread_index) {
|
||||
PStatView &view = get_level_view(collector, thread_index);
|
||||
if (view.get_net_value() > 0.0) {
|
||||
open_strip_chart(thread_index, collector, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
else if (((LPNMHDR)lparam)->code == NM_RCLICK) {
|
||||
NMMOUSE &mouse = *(NMMOUSE *)lparam;
|
||||
if (mouse.dwItemSpec >= 1 &&
|
||||
mouse.dwItemSpec <= _status_bar_collectors.size()) {
|
||||
int collector = _status_bar_collectors[mouse.dwItemSpec - 1];
|
||||
show_popup_menu(collector);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_COMMAND:
|
||||
if (HIWORD(wparam) <= 1) {
|
||||
int menu_id = LOWORD(wparam);
|
||||
handle_menu_command(menu_id);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Called when a menu item is clicked.
|
||||
*/
|
||||
void WinStatsMonitor::
|
||||
handle_menu_command(int menu_id) {
|
||||
|
|
@ -999,14 +839,6 @@ handle_menu_command(int menu_id) {
|
|||
case MI_none:
|
||||
break;
|
||||
|
||||
case MI_time_ms:
|
||||
set_time_units(PStatGraph::GBU_ms);
|
||||
break;
|
||||
|
||||
case MI_time_hz:
|
||||
set_time_units(PStatGraph::GBU_hz);
|
||||
break;
|
||||
|
||||
case MI_speed_1:
|
||||
set_scroll_speed(1);
|
||||
break;
|
||||
|
|
@ -1031,6 +863,19 @@ handle_menu_command(int menu_id) {
|
|||
set_pause(!_pause);
|
||||
break;
|
||||
|
||||
case MI_graphs_close_all:
|
||||
remove_all_graphs();
|
||||
break;
|
||||
|
||||
case MI_graphs_reopen_default:
|
||||
remove_all_graphs();
|
||||
open_default_graphs();
|
||||
break;
|
||||
|
||||
case MI_graphs_save_default:
|
||||
save_default_graphs();
|
||||
break;
|
||||
|
||||
default:
|
||||
if (menu_id >= MI_new_chart) {
|
||||
const MenuDef &menu_def = lookup_menu(menu_id);
|
||||
|
|
@ -1055,3 +900,38 @@ handle_menu_command(int menu_id) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a status bar item is double-clicked.
|
||||
*/
|
||||
void WinStatsMonitor::
|
||||
handle_status_bar_click(int item) {
|
||||
if (item == 0) {
|
||||
open_strip_chart(0, 0, false);
|
||||
}
|
||||
else if (item >= 1 && item <= _status_bar_collectors.size()) {
|
||||
int collector = _status_bar_collectors[item - 1];
|
||||
open_strip_chart(0, collector, true);
|
||||
|
||||
// Also open a strip chart for other threads with data for this
|
||||
// collector.
|
||||
const PStatClientData *client_data = get_client_data();
|
||||
for (int thread_index = 1; thread_index < client_data->get_num_threads(); ++thread_index) {
|
||||
PStatView &view = get_level_view(collector, thread_index);
|
||||
if (view.get_net_value() > 0.0) {
|
||||
open_strip_chart(thread_index, collector, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a status bar item is right-clicked.
|
||||
*/
|
||||
void WinStatsMonitor::
|
||||
handle_status_bar_popup(int item) {
|
||||
if (item >= 1 && item <= _status_bar_collectors.size()) {
|
||||
int collector = _status_bar_collectors[item - 1];
|
||||
show_popup_menu(collector);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,8 @@ public:
|
|||
WinStatsMonitor(WinStatsServer *server);
|
||||
virtual ~WinStatsMonitor();
|
||||
|
||||
void close();
|
||||
|
||||
virtual std::string get_monitor_name();
|
||||
|
||||
virtual void initialized();
|
||||
|
|
@ -79,10 +81,13 @@ public:
|
|||
int get_pixel_scale() const;
|
||||
POINT get_new_window_pos();
|
||||
|
||||
void open_strip_chart(int thread_index, int collector_index, bool show_level);
|
||||
void open_piano_roll(int thread_index);
|
||||
void open_flame_graph(int thread_index, int collector_index = -1);
|
||||
void open_timeline();
|
||||
PStatGraph *open_timeline();
|
||||
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_piano_roll(int thread_index);
|
||||
|
||||
void choose_collector_color(int collector_index);
|
||||
void reset_collector_color(int collector_index);
|
||||
|
||||
const MenuDef &lookup_menu(int menu_id) const;
|
||||
int get_menu_id(const MenuDef &menu_def);
|
||||
|
|
@ -91,22 +96,21 @@ public:
|
|||
void set_scroll_speed(double scroll_speed);
|
||||
void set_pause(bool pause);
|
||||
|
||||
private:
|
||||
void add_graph(WinStatsGraph *graph);
|
||||
void remove_graph(WinStatsGraph *graph);
|
||||
void remove_all_graphs();
|
||||
|
||||
void create_window();
|
||||
void setup_options_menu();
|
||||
private:
|
||||
void setup_speed_menu();
|
||||
void setup_frame_rate_label();
|
||||
void create_status_bar(HINSTANCE application);
|
||||
void update_status_bar();
|
||||
void show_popup_menu(int collector);
|
||||
static void register_window_class(HINSTANCE application);
|
||||
|
||||
static LONG WINAPI static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
|
||||
LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
|
||||
void calc_iconic_graph_window_pos(WinStatsGraph *graph, int &x, int &y);
|
||||
void handle_window_moved(const RECT &client_rect, int delta_x, int delta_y);
|
||||
void handle_menu_command(int menu_id);
|
||||
void handle_status_bar_click(int item);
|
||||
void handle_status_bar_popup(int item);
|
||||
|
||||
typedef pset<WinStatsGraph *> Graphs;
|
||||
Graphs _graphs;
|
||||
|
|
@ -121,23 +125,16 @@ private:
|
|||
|
||||
HWND _window;
|
||||
HMENU _menu_bar;
|
||||
HMENU _options_menu;
|
||||
HMENU _speed_menu;
|
||||
HWND _status_bar;
|
||||
POINT _client_origin;
|
||||
pvector<int> _status_bar_collectors;
|
||||
std::string _window_title;
|
||||
int _time_units;
|
||||
double _scroll_speed;
|
||||
bool _pause;
|
||||
int _pixel_scale;
|
||||
|
||||
HFONT _font;
|
||||
|
||||
static bool _window_class_registered;
|
||||
static const char * const _window_class_name;
|
||||
bool _have_data = false;
|
||||
|
||||
friend class WinStatsGraph;
|
||||
friend class WinStatsServer;
|
||||
};
|
||||
|
||||
#include "winStatsMonitor.I"
|
||||
|
|
|
|||
|
|
@ -40,7 +40,8 @@ WinStatsPianoRoll(WinStatsMonitor *monitor, int thread_index) :
|
|||
set_guide_bar_units(get_guide_bar_units() | GBU_show_units);
|
||||
|
||||
create_window();
|
||||
clear_region();
|
||||
force_redraw();
|
||||
idle();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -126,6 +127,9 @@ on_popup_label(int collector_index) {
|
|||
}
|
||||
AppendMenu(popup, MF_STRING, 102, "Open Strip Chart");
|
||||
AppendMenu(popup, MF_STRING, 103, "Open Flame Graph");
|
||||
AppendMenu(popup, MF_STRING | MF_SEPARATOR, 0, nullptr);
|
||||
AppendMenu(popup, MF_STRING, 104, "Change Color...");
|
||||
AppendMenu(popup, MF_STRING, 105, "Reset Color");
|
||||
TrackPopupMenu(popup, TPM_LEFTBUTTON, point.x, point.y, 0, _window, nullptr);
|
||||
}
|
||||
}
|
||||
|
|
@ -232,6 +236,25 @@ idle() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current window dimensions.
|
||||
*/
|
||||
bool WinStatsPianoRoll::
|
||||
get_window_state(int &x, int &y, int &width, int &height,
|
||||
bool &maximized, bool &minimized) const {
|
||||
WinStatsGraph::get_window_state(x, y, width, height, maximized, minimized);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to restore the graph window to its previous dimensions.
|
||||
*/
|
||||
void WinStatsPianoRoll::
|
||||
set_window_state(int x, int y, int width, int height,
|
||||
bool maximized, bool minimized) {
|
||||
WinStatsGraph::set_window_state(x, y, width, height, maximized, minimized);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
@ -255,6 +278,14 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
|||
case 103:
|
||||
WinStatsGraph::_monitor->open_flame_graph(get_thread_index(), _popup_index);
|
||||
return 0;
|
||||
|
||||
case 104:
|
||||
WinStatsGraph::_monitor->choose_collector_color(_popup_index);
|
||||
return 0;
|
||||
|
||||
case 105:
|
||||
WinStatsGraph::_monitor->reset_collector_color(_popup_index);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -55,6 +55,11 @@ protected:
|
|||
virtual void end_draw();
|
||||
virtual void idle();
|
||||
|
||||
virtual bool get_window_state(int &x, int &y, int &width, int &height,
|
||||
bool &maximized, bool &minimized) const;
|
||||
virtual void set_window_state(int x, int y, int width, int height,
|
||||
bool maximized, bool minimized);
|
||||
|
||||
LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
|
||||
virtual LONG graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
|
||||
virtual void additional_window_paint(HDC hdc);
|
||||
|
|
|
|||
|
|
@ -12,12 +12,863 @@
|
|||
*/
|
||||
|
||||
#include "winStatsServer.h"
|
||||
#include "winStatsMenuId.h"
|
||||
#include "winStatsMonitor.h"
|
||||
#include "pandaVersion.h"
|
||||
#include "pStatGraph.h"
|
||||
#include "config_pstatclient.h"
|
||||
|
||||
#include <commctrl.h>
|
||||
#include <commdlg.h>
|
||||
|
||||
bool WinStatsServer::_window_class_registered = false;
|
||||
const char *const WinStatsServer::_window_class_name = "server";
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
WinStatsServer::
|
||||
WinStatsServer() {
|
||||
_last_session = Filename::expand_from(
|
||||
"$USER_APPDATA/Panda3D-" PANDA_ABI_VERSION_STR "/last-session.pstats");
|
||||
_last_session.set_binary();
|
||||
|
||||
_window = 0;
|
||||
_menu_bar = 0;
|
||||
_options_menu = 0;
|
||||
|
||||
_time_units = 0;
|
||||
|
||||
// Create the fonts used for rendering the UI.
|
||||
NONCLIENTMETRICS metrics = {0};
|
||||
metrics.cbSize = sizeof(NONCLIENTMETRICS);
|
||||
if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, 0)) {
|
||||
_font = CreateFontIndirect(&metrics.lfMenuFont);
|
||||
} else {
|
||||
_font = (HFONT)GetStockObject(ANSI_VAR_FONT);
|
||||
}
|
||||
|
||||
create_window();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
PStatMonitor *WinStatsServer::
|
||||
make_monitor() {
|
||||
return new WinStatsMonitor(this);
|
||||
make_monitor(const NetAddress &address) {
|
||||
// Enable the "New Session", "Save Session" and "Close Session" menu items.
|
||||
MENUITEMINFO mii;
|
||||
memset(&mii, 0, sizeof(mii));
|
||||
mii.cbSize = sizeof(mii);
|
||||
mii.fMask = MIIM_STATE;
|
||||
mii.fState = MFS_ENABLED;
|
||||
SetMenuItemInfoA(_session_menu, MI_session_new, FALSE, &mii);
|
||||
SetMenuItemInfoA(_session_menu, MI_session_save, FALSE, &mii);
|
||||
SetMenuItemInfoA(_session_menu, MI_session_close, FALSE, &mii);
|
||||
SetMenuItemInfoA(_session_menu, MI_session_export_json, FALSE, &mii);
|
||||
|
||||
std::ostringstream strm;
|
||||
strm << "PStats Server (connected to " << address << ")";
|
||||
std::string title = strm.str();
|
||||
SetWindowTextA(_window, title.c_str());
|
||||
|
||||
_monitor = new WinStatsMonitor(this);
|
||||
return _monitor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when connection has been lost.
|
||||
*/
|
||||
void WinStatsServer::
|
||||
lost_connection(PStatMonitor *monitor) {
|
||||
// Store a backup now, in case PStats crashes or something.
|
||||
_last_session.make_dir();
|
||||
if (monitor->write(_last_session)) {
|
||||
nout << "Wrote to " << _last_session << "\n";
|
||||
} else {
|
||||
nout << "Failed to write to " << _last_session << "\n";
|
||||
}
|
||||
|
||||
stop_listening();
|
||||
|
||||
SetWindowTextA(_window, "PStats Server (disconnected)");
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a new session.
|
||||
*/
|
||||
bool WinStatsServer::
|
||||
new_session() {
|
||||
if (!close_session()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (listen()) {
|
||||
{
|
||||
std::ostringstream strm;
|
||||
strm << "PStats Server (listening on port " << pstats_port << ")";
|
||||
std::string title = strm.str();
|
||||
SetWindowTextA(_window, title.c_str());
|
||||
}
|
||||
{
|
||||
std::ostringstream strm;
|
||||
strm << "Waiting for client to connect on port " << pstats_port << "...";
|
||||
std::string title = strm.str();
|
||||
int part = -1;
|
||||
SendMessage(_status_bar, SB_SETPARTS, 1, (LPARAM)&part);
|
||||
SendMessage(_status_bar, WM_SETTEXT, 0, (LPARAM)title.c_str());
|
||||
}
|
||||
|
||||
// Disable the "New Session" menu item.
|
||||
MENUITEMINFO mii;
|
||||
memset(&mii, 0, sizeof(mii));
|
||||
mii.cbSize = sizeof(mii);
|
||||
mii.fMask = MIIM_STATE;
|
||||
mii.fState = MFS_DISABLED;
|
||||
SetMenuItemInfoA(_session_menu, MI_session_new, FALSE, &mii);
|
||||
|
||||
// Disable the "Save Session" menu item.
|
||||
mii.fState = MFS_DISABLED;
|
||||
SetMenuItemInfoA(_session_menu, MI_session_save, FALSE, &mii);
|
||||
|
||||
// Enable the "Close Session" menu item.
|
||||
mii.fState = MFS_ENABLED;
|
||||
SetMenuItemInfoA(_session_menu, MI_session_close, FALSE, &mii);
|
||||
|
||||
// Disable the "Export Session" menu item.
|
||||
mii.fState = MFS_DISABLED;
|
||||
SetMenuItemInfoA(_session_menu, MI_session_export_json, FALSE, &mii);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SetWindowTextA(_window, "PStats Server");
|
||||
|
||||
std::ostringstream stream;
|
||||
stream
|
||||
<< "Unable to open port " << pstats_port
|
||||
<< ". Try specifying a different\n"
|
||||
<< "port number using pstats-port in your Config file.";
|
||||
std::string str = stream.str();
|
||||
MessageBox(_window, str.c_str(), "PStats Error",
|
||||
MB_OK | MB_ICONEXCLAMATION);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offers to open an existing session.
|
||||
*/
|
||||
bool WinStatsServer::
|
||||
open_session() {
|
||||
if (!close_session()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char buffer[4096];
|
||||
buffer[0] = '\0';
|
||||
|
||||
OPENFILENAMEA ofn = {
|
||||
sizeof(OPENFILENAMEA),
|
||||
_window,
|
||||
nullptr,
|
||||
"PStats Session Files (*.pstats)\0*.pstats\0All Files (*.*)\0*.*\0",
|
||||
nullptr,
|
||||
0,
|
||||
0,
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
"Open Session",
|
||||
OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST,
|
||||
0,
|
||||
};
|
||||
|
||||
if (GetOpenFileNameA(&ofn)) {
|
||||
Filename fn = Filename::from_os_specific(buffer);
|
||||
fn.set_binary();
|
||||
|
||||
WinStatsMonitor *monitor = new WinStatsMonitor(this);
|
||||
if (!monitor->read(fn)) {
|
||||
delete monitor;
|
||||
|
||||
std::ostringstream stream;
|
||||
stream << "Failed to load session file: " << fn;
|
||||
std::string str = stream.str();
|
||||
MessageBox(_window, str.c_str(), "PStats Error",
|
||||
MB_OK | MB_ICONEXCLAMATION);
|
||||
return false;
|
||||
}
|
||||
_monitor = monitor;
|
||||
|
||||
// Enable the "New Session", "Save Session" and "Close Session" menu items.
|
||||
MENUITEMINFO mii;
|
||||
memset(&mii, 0, sizeof(mii));
|
||||
mii.cbSize = sizeof(mii);
|
||||
mii.fMask = MIIM_STATE;
|
||||
mii.fState = MFS_ENABLED;
|
||||
SetMenuItemInfoA(_session_menu, MI_session_new, FALSE, &mii);
|
||||
SetMenuItemInfoA(_session_menu, MI_session_save, FALSE, &mii);
|
||||
SetMenuItemInfoA(_session_menu, MI_session_close, FALSE, &mii);
|
||||
SetMenuItemInfoA(_session_menu, MI_session_export_json, FALSE, &mii);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the last session, if any.
|
||||
*/
|
||||
bool WinStatsServer::
|
||||
open_last_session() {
|
||||
if (!close_session()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Filename fn = _last_session;
|
||||
WinStatsMonitor *monitor = new WinStatsMonitor(this);
|
||||
if (!monitor->read(fn)) {
|
||||
delete monitor;
|
||||
|
||||
std::ostringstream stream;
|
||||
stream << "Failed to load session file: " << fn;
|
||||
std::string str = stream.str();
|
||||
MessageBox(_window, str.c_str(), "PStats Error",
|
||||
MB_OK | MB_ICONEXCLAMATION);
|
||||
return false;
|
||||
}
|
||||
_monitor = monitor;
|
||||
|
||||
// Enable the "New Session", "Save Session" and "Close Session" menu items.
|
||||
MENUITEMINFO mii;
|
||||
memset(&mii, 0, sizeof(mii));
|
||||
mii.cbSize = sizeof(mii);
|
||||
mii.fMask = MIIM_STATE;
|
||||
mii.fState = MFS_ENABLED;
|
||||
SetMenuItemInfoA(_session_menu, MI_session_new, FALSE, &mii);
|
||||
SetMenuItemInfoA(_session_menu, MI_session_save, FALSE, &mii);
|
||||
SetMenuItemInfoA(_session_menu, MI_session_close, FALSE, &mii);
|
||||
SetMenuItemInfoA(_session_menu, MI_session_export_json, FALSE, &mii);
|
||||
|
||||
// If the file contained no graphs, open the default graphs.
|
||||
if (monitor->_graphs.empty()) {
|
||||
monitor->open_default_graphs();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offers to save the current session.
|
||||
*/
|
||||
bool WinStatsServer::
|
||||
save_session() {
|
||||
nassertr_always(_monitor != nullptr, true);
|
||||
|
||||
char buffer[4096];
|
||||
buffer[0] = '\0';
|
||||
|
||||
OPENFILENAMEA ofn = {
|
||||
sizeof(OPENFILENAMEA),
|
||||
_window,
|
||||
0,
|
||||
"PStats Session Files\0*.pstats\0",
|
||||
nullptr,
|
||||
0,
|
||||
0,
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
"Save Session",
|
||||
OFN_OVERWRITEPROMPT,
|
||||
0,
|
||||
0,
|
||||
"pstats",
|
||||
0,
|
||||
};
|
||||
|
||||
if (GetSaveFileNameA(&ofn)) {
|
||||
Filename fn = Filename::from_os_specific(buffer);
|
||||
fn.set_binary();
|
||||
|
||||
if (!_monitor->write(fn)) {
|
||||
std::ostringstream stream;
|
||||
stream << "Failed to save session file: " << fn;
|
||||
std::string str = stream.str();
|
||||
MessageBox(_window, str.c_str(), "PStats Error",
|
||||
MB_OK | MB_ICONEXCLAMATION);
|
||||
return false;
|
||||
}
|
||||
_monitor->get_client_data()->clear_dirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offers to export the current session as a JSON file.
|
||||
*/
|
||||
bool WinStatsServer::
|
||||
export_session() {
|
||||
nassertr_always(_monitor != nullptr, true);
|
||||
|
||||
char buffer[4096];
|
||||
buffer[0] = '\0';
|
||||
|
||||
OPENFILENAMEA ofn = {
|
||||
sizeof(OPENFILENAMEA),
|
||||
_window,
|
||||
0,
|
||||
"JSON files\0*.json\0",
|
||||
nullptr,
|
||||
0,
|
||||
0,
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
"Export Session",
|
||||
OFN_OVERWRITEPROMPT,
|
||||
0,
|
||||
0,
|
||||
"json",
|
||||
0,
|
||||
};
|
||||
|
||||
if (GetSaveFileNameA(&ofn)) {
|
||||
Filename fn = Filename::from_os_specific(buffer);
|
||||
fn.set_text();
|
||||
|
||||
std::ofstream stream;
|
||||
if (!fn.open_write(stream)) {
|
||||
std::ostringstream stream;
|
||||
stream << "Failed to open file for export: " << fn;
|
||||
std::string str = stream.str();
|
||||
MessageBox(_window, str.c_str(), "PStats Error",
|
||||
MB_OK | MB_ICONEXCLAMATION);
|
||||
return false;
|
||||
}
|
||||
|
||||
int pid = _monitor->get_client_pid();
|
||||
_monitor->get_client_data()->write_json(stream, std::max(0, pid));
|
||||
stream.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the current session.
|
||||
*/
|
||||
bool WinStatsServer::
|
||||
close_session() {
|
||||
bool wrote_last_session = false;
|
||||
|
||||
if (_monitor != nullptr) {
|
||||
const PStatClientData *client_data = _monitor->get_client_data();
|
||||
if (client_data != nullptr && client_data->is_dirty()) {
|
||||
if (!_monitor->has_read_filename()) {
|
||||
_last_session.make_dir();
|
||||
if (_monitor->write(_last_session)) {
|
||||
nout << "Wrote to " << _last_session << "\n";
|
||||
wrote_last_session = true;
|
||||
}
|
||||
else {
|
||||
nout << "Failed to write to " << _last_session << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
int result = MessageBox(_window,
|
||||
"Would you like to save the currently open session?",
|
||||
"Unsaved Data", MB_YESNOCANCEL | MB_ICONQUESTION);
|
||||
if (result == IDCANCEL || (result == IDYES && !save_session())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_monitor->close();
|
||||
_monitor = nullptr;
|
||||
}
|
||||
|
||||
stop_listening();
|
||||
|
||||
SetWindowTextA(_window, "PStats Server");
|
||||
|
||||
int part = -1;
|
||||
SendMessage(_status_bar, SB_SETPARTS, 1, (LPARAM)&part);
|
||||
SendMessage(_status_bar, WM_SETTEXT, 0, (LPARAM)"");
|
||||
|
||||
// Enable the "New Session" menu item.
|
||||
MENUITEMINFO mii;
|
||||
memset(&mii, 0, sizeof(mii));
|
||||
mii.cbSize = sizeof(mii);
|
||||
mii.fMask = MIIM_STATE;
|
||||
mii.fState = MFS_ENABLED;
|
||||
SetMenuItemInfoA(_session_menu, MI_session_new, FALSE, &mii);
|
||||
|
||||
if (wrote_last_session) {
|
||||
// And the "Open Last Session" menu item.
|
||||
mii.fState = MFS_ENABLED;
|
||||
SetMenuItemInfoA(_session_menu, MI_session_open_last, FALSE, &mii);
|
||||
}
|
||||
|
||||
// Disable the "Save Session" menu item.
|
||||
mii.fState = MFS_DISABLED;
|
||||
SetMenuItemInfoA(_session_menu, MI_session_save, FALSE, &mii);
|
||||
|
||||
// Disable the "Close Session" menu item.
|
||||
mii.fState = MFS_DISABLED;
|
||||
SetMenuItemInfoA(_session_menu, MI_session_close, FALSE, &mii);
|
||||
|
||||
// Disable the "Export Session" menu item.
|
||||
mii.fState = MFS_DISABLED;
|
||||
SetMenuItemInfoA(_session_menu, MI_session_export_json, FALSE, &mii);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the window handle to the server's window.
|
||||
*/
|
||||
HWND WinStatsServer::
|
||||
get_window() const {
|
||||
return _window;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the menu handle to the server's menu bar.
|
||||
*/
|
||||
HMENU WinStatsServer::
|
||||
get_menu_bar() const {
|
||||
return _menu_bar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the window handle to the server's status bar.
|
||||
*/
|
||||
HWND WinStatsServer::
|
||||
get_status_bar() const {
|
||||
return _status_bar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the font that should be used for rendering text.
|
||||
*/
|
||||
HFONT WinStatsServer::
|
||||
get_font() const {
|
||||
return _font;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the system DPI scaling as a fraction where 4 = no scaling.
|
||||
*/
|
||||
int WinStatsServer::
|
||||
get_pixel_scale() const {
|
||||
return _pixel_scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the origin of the window's client area.
|
||||
*/
|
||||
POINT WinStatsServer::
|
||||
get_client_origin() const {
|
||||
return _client_origin;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
int WinStatsServer::
|
||||
get_time_units() const {
|
||||
return _time_units;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when the user selects a new time units from the monitor pulldown
|
||||
* menu, this should adjust the units for all graphs to the indicated mask if
|
||||
* it is a time-based graph.
|
||||
*/
|
||||
void WinStatsServer::
|
||||
set_time_units(int unit_mask) {
|
||||
_time_units = unit_mask;
|
||||
|
||||
if (_monitor != nullptr) {
|
||||
_monitor->set_time_units(unit_mask);
|
||||
}
|
||||
|
||||
// Now change the checkmark on the pulldown menu.
|
||||
MENUITEMINFO mii;
|
||||
memset(&mii, 0, sizeof(mii));
|
||||
mii.cbSize = sizeof(mii);
|
||||
mii.fMask = MIIM_STATE;
|
||||
|
||||
mii.fState = ((_time_units & PStatGraph::GBU_ms) != 0) ?
|
||||
MFS_CHECKED : MFS_UNCHECKED;
|
||||
SetMenuItemInfo(_options_menu, MI_time_ms, FALSE, &mii);
|
||||
|
||||
mii.fState = ((_time_units & PStatGraph::GBU_hz) != 0) ?
|
||||
MFS_CHECKED : MFS_UNCHECKED;
|
||||
SetMenuItemInfo(_options_menu, MI_time_hz, FALSE, &mii);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the window for this server.
|
||||
*/
|
||||
void WinStatsServer::
|
||||
create_window() {
|
||||
HINSTANCE application = GetModuleHandle(nullptr);
|
||||
register_window_class(application);
|
||||
|
||||
_menu_bar = CreateMenu();
|
||||
|
||||
setup_session_menu();
|
||||
setup_options_menu();
|
||||
|
||||
DWORD window_style = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN |
|
||||
WS_CLIPSIBLINGS | WS_VISIBLE;
|
||||
|
||||
_window =
|
||||
CreateWindow(_window_class_name, "PStats Server", window_style,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
nullptr, _menu_bar, application, 0);
|
||||
if (!_window) {
|
||||
nout << "Could not create server window!\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
SetWindowLongPtr(_window, 0, (LONG_PTR)this);
|
||||
|
||||
create_status_bar(application);
|
||||
|
||||
// For some reason, SW_SHOWNORMAL doesn't always work, but SW_RESTORE seems
|
||||
// to.
|
||||
ShowWindow(_window, SW_RESTORE);
|
||||
SetForegroundWindow(_window);
|
||||
|
||||
HDC dc = GetDC(_window);
|
||||
_pixel_scale = 0;
|
||||
if (dc) {
|
||||
_pixel_scale = GetDeviceCaps(dc, LOGPIXELSX) / (96 / 4);
|
||||
}
|
||||
if (_pixel_scale <= 0) {
|
||||
_pixel_scale = 4;
|
||||
}
|
||||
ReleaseDC(_window, dc);
|
||||
|
||||
_client_origin.x = 0;
|
||||
_client_origin.y = 0;
|
||||
ClientToScreen(_window, &_client_origin);
|
||||
|
||||
// Set up a timer to poll the pstats every so often.
|
||||
SetTimer(_window, 1, 200, nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the "Session" pulldown menu.
|
||||
*/
|
||||
void WinStatsServer::
|
||||
setup_session_menu() {
|
||||
_session_menu = CreatePopupMenu();
|
||||
|
||||
MENUITEMINFO mii;
|
||||
memset(&mii, 0, sizeof(mii));
|
||||
mii.cbSize = sizeof(mii);
|
||||
|
||||
mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_SUBMENU;
|
||||
mii.fType = MFT_STRING;
|
||||
mii.hSubMenu = _session_menu;
|
||||
|
||||
mii.dwTypeData = "&Session";
|
||||
InsertMenuItem(_menu_bar, GetMenuItemCount(_menu_bar), TRUE, &mii);
|
||||
|
||||
AppendMenu(_session_menu, MF_STRING, MI_session_new, "&New Session\tCtrl+N");
|
||||
AppendMenu(_session_menu, MF_STRING, MI_session_open, "&Open Session...\tCtrl+O");
|
||||
|
||||
if (_last_session.exists()) {
|
||||
AppendMenu(_session_menu, MF_STRING, MI_session_open_last, "Open &Last Session");
|
||||
} else {
|
||||
AppendMenu(_session_menu, MF_STRING | MF_DISABLED, MI_session_open_last, "Open &Last Session");
|
||||
}
|
||||
|
||||
AppendMenu(_session_menu, MF_STRING | MF_DISABLED, MI_session_save, "&Save Session...\tCtrl+S");
|
||||
AppendMenu(_session_menu, MF_STRING | MF_DISABLED, MI_session_close, "&Close Session\tCtrl+W");
|
||||
|
||||
AppendMenu(_session_menu, MF_SEPARATOR, 0, nullptr);
|
||||
AppendMenu(_session_menu, MF_STRING | MF_DISABLED, MI_session_export_json, "&Export as JSON...");
|
||||
|
||||
AppendMenu(_session_menu, MF_SEPARATOR, 0, nullptr);
|
||||
AppendMenu(_session_menu, MF_STRING, MI_exit, "E&xit");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the "Options" pulldown menu.
|
||||
*/
|
||||
void WinStatsServer::
|
||||
setup_options_menu() {
|
||||
_options_menu = CreatePopupMenu();
|
||||
|
||||
MENUITEMINFO mii;
|
||||
memset(&mii, 0, sizeof(mii));
|
||||
mii.cbSize = sizeof(mii);
|
||||
|
||||
mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_SUBMENU;
|
||||
mii.fType = MFT_STRING;
|
||||
mii.hSubMenu = _options_menu;
|
||||
|
||||
// One day, when there is more than one option here, we will actually
|
||||
// present this to the user as the "Options" menu. For now, the only option
|
||||
// we have is time units. mii.dwTypeData = "Options";
|
||||
mii.dwTypeData = "Units";
|
||||
InsertMenuItem(_menu_bar, GetMenuItemCount(_menu_bar), TRUE, &mii);
|
||||
|
||||
mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_CHECKMARKS | MIIM_STATE;
|
||||
mii.fType = MFT_STRING | MFT_RADIOCHECK;
|
||||
mii.hbmpChecked = nullptr;
|
||||
mii.hbmpUnchecked = nullptr;
|
||||
mii.fState = MFS_UNCHECKED;
|
||||
mii.wID = MI_time_ms;
|
||||
mii.dwTypeData = "ms";
|
||||
InsertMenuItem(_options_menu, GetMenuItemCount(_options_menu), TRUE, &mii);
|
||||
|
||||
mii.wID = MI_time_hz;
|
||||
mii.dwTypeData = "Hz";
|
||||
InsertMenuItem(_options_menu, GetMenuItemCount(_options_menu), TRUE, &mii);
|
||||
|
||||
set_time_units(PStatGraph::GBU_ms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a status bar at the bottom of the screen showing assorted level
|
||||
* values.
|
||||
*/
|
||||
void WinStatsServer::
|
||||
create_status_bar(HINSTANCE application) {
|
||||
_status_bar = CreateWindow(STATUSCLASSNAME, nullptr,
|
||||
SBARS_SIZEGRIP | WS_CHILD | WS_VISIBLE,
|
||||
0, 0, 0, 0,
|
||||
_window, (HMENU)0, application, nullptr);
|
||||
|
||||
ShowWindow(_status_bar, SW_SHOW);
|
||||
UpdateWindow(_status_bar);
|
||||
|
||||
InvalidateRect(_status_bar, NULL, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the window class for the server window, if it has not already
|
||||
* been registered.
|
||||
*/
|
||||
void WinStatsServer::
|
||||
register_window_class(HINSTANCE application) {
|
||||
if (_window_class_registered) {
|
||||
return;
|
||||
}
|
||||
|
||||
WNDCLASS wc;
|
||||
|
||||
ZeroMemory(&wc, sizeof(WNDCLASS));
|
||||
wc.style = 0;
|
||||
wc.lpfnWndProc = (WNDPROC)static_window_proc;
|
||||
wc.hInstance = application;
|
||||
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
|
||||
wc.lpszMenuName = nullptr;
|
||||
wc.lpszClassName = _window_class_name;
|
||||
|
||||
// Reserve space to associate the this pointer with the window.
|
||||
wc.cbWndExtra = sizeof(WinStatsServer *);
|
||||
|
||||
if (!RegisterClass(&wc)) {
|
||||
nout << "Could not register server window class!\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
_window_class_registered = true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
LONG WINAPI WinStatsServer::
|
||||
static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||
WinStatsServer *self = (WinStatsServer *)GetWindowLongPtr(hwnd, 0);
|
||||
if (self != nullptr && self->_window == hwnd) {
|
||||
return self->window_proc(hwnd, msg, wparam, lparam);
|
||||
} else {
|
||||
return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
LONG WinStatsServer::
|
||||
window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||
switch (msg) {
|
||||
case WM_TIMER:
|
||||
poll();
|
||||
break;
|
||||
|
||||
case WM_CLOSE:
|
||||
if (!close_session()) {
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
|
||||
case WM_WINDOWPOSCHANGED:
|
||||
{
|
||||
RECT client_rect;
|
||||
GetClientRect(_window, &client_rect);
|
||||
MapWindowPoints(_window, nullptr, (POINT *)&client_rect, 2);
|
||||
|
||||
if (_monitor != nullptr) {
|
||||
// Remove the status bar from the client rectangle.
|
||||
RECT status_bar_rect;
|
||||
GetWindowRect(_status_bar, &status_bar_rect);
|
||||
client_rect.bottom -= (status_bar_rect.bottom - status_bar_rect.top);
|
||||
|
||||
int delta_x = client_rect.left - _client_origin.x;
|
||||
int delta_y = client_rect.top - _client_origin.y;
|
||||
_client_origin.x = client_rect.left;
|
||||
_client_origin.y = client_rect.top;
|
||||
|
||||
_monitor->handle_window_moved(client_rect, delta_x, delta_y);
|
||||
}
|
||||
else {
|
||||
_client_origin.x = client_rect.left;
|
||||
_client_origin.y = client_rect.top;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_SIZE:
|
||||
if (_status_bar) {
|
||||
SendMessage(_status_bar, WM_SIZE, 0, 0);
|
||||
if (_monitor != nullptr) {
|
||||
_monitor->update_status_bar();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_NOTIFY:
|
||||
if (_monitor != nullptr) {
|
||||
if (((LPNMHDR)lparam)->code == NM_DBLCLK) {
|
||||
NMMOUSE &mouse = *(NMMOUSE *)lparam;
|
||||
_monitor->handle_status_bar_click(mouse.dwItemSpec);
|
||||
return TRUE;
|
||||
}
|
||||
else if (((LPNMHDR)lparam)->code == NM_RCLICK) {
|
||||
NMMOUSE &mouse = *(NMMOUSE *)lparam;
|
||||
_monitor->handle_status_bar_popup(mouse.dwItemSpec);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_KEYDOWN:
|
||||
if ((lparam & 0x40000000) == 0 && GetKeyState(VK_CONTROL) < 0) {
|
||||
switch (wparam) {
|
||||
case 'N':
|
||||
new_session();
|
||||
return 0;
|
||||
|
||||
case 'O':
|
||||
open_session();
|
||||
return 0;
|
||||
|
||||
case 'S':
|
||||
save_session();
|
||||
return 0;
|
||||
|
||||
case 'W':
|
||||
close_session();
|
||||
return 0;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_COMMAND:
|
||||
if (HIWORD(wparam) <= 1) {
|
||||
int menu_id = LOWORD(wparam);
|
||||
handle_menu_command(menu_id);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void WinStatsServer::
|
||||
handle_menu_command(int menu_id) {
|
||||
switch (menu_id) {
|
||||
case MI_none:
|
||||
break;
|
||||
|
||||
case MI_session_new:
|
||||
new_session();
|
||||
break;
|
||||
|
||||
case MI_session_open:
|
||||
open_session();
|
||||
break;
|
||||
|
||||
case MI_session_open_last:
|
||||
open_last_session();
|
||||
break;
|
||||
|
||||
case MI_session_save:
|
||||
save_session();
|
||||
break;
|
||||
|
||||
case MI_session_close:
|
||||
close_session();
|
||||
break;
|
||||
|
||||
case MI_session_export_json:
|
||||
export_session();
|
||||
break;
|
||||
|
||||
case MI_exit:
|
||||
if (close_session()) {
|
||||
exit(0);
|
||||
}
|
||||
break;
|
||||
|
||||
case MI_time_ms:
|
||||
set_time_units(PStatGraph::GBU_ms);
|
||||
break;
|
||||
|
||||
case MI_time_hz:
|
||||
set_time_units(PStatGraph::GBU_hz);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (_monitor != nullptr) {
|
||||
_monitor->handle_menu_command(menu_id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,13 +16,63 @@
|
|||
|
||||
#include "pandatoolbase.h"
|
||||
#include "pStatServer.h"
|
||||
#include "winStatsMonitor.h"
|
||||
|
||||
/**
|
||||
* The class that owns the main loop, waiting for client connections.
|
||||
*/
|
||||
class WinStatsServer : public PStatServer {
|
||||
public:
|
||||
virtual PStatMonitor *make_monitor();
|
||||
WinStatsServer();
|
||||
|
||||
virtual PStatMonitor *make_monitor(const NetAddress &address);
|
||||
virtual void lost_connection(PStatMonitor *monitor);
|
||||
|
||||
bool new_session();
|
||||
bool open_session();
|
||||
bool open_last_session();
|
||||
bool save_session();
|
||||
bool export_session();
|
||||
bool close_session();
|
||||
|
||||
HWND get_window() const;
|
||||
HMENU get_menu_bar() const;
|
||||
HWND get_status_bar() const;
|
||||
HFONT get_font() const;
|
||||
int get_pixel_scale() const;
|
||||
POINT get_client_origin() const;
|
||||
|
||||
int get_time_units() const;
|
||||
void set_time_units(int unit_mask);
|
||||
|
||||
private:
|
||||
void create_window();
|
||||
void setup_session_menu();
|
||||
void setup_options_menu();
|
||||
void create_status_bar(HINSTANCE application);
|
||||
static void register_window_class(HINSTANCE application);
|
||||
|
||||
static LONG WINAPI static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
|
||||
LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
|
||||
void handle_menu_command(int menu_id);
|
||||
|
||||
PT(WinStatsMonitor) _monitor;
|
||||
|
||||
Filename _last_session;
|
||||
|
||||
HWND _window = 0;
|
||||
HMENU _menu_bar = 0;
|
||||
HMENU _session_menu = 0;
|
||||
HMENU _options_menu = 0;
|
||||
HWND _status_bar;
|
||||
POINT _client_origin;
|
||||
int _time_units;
|
||||
int _pixel_scale;
|
||||
|
||||
HFONT _font;
|
||||
|
||||
static bool _window_class_registered;
|
||||
static const char * const _window_class_name;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -31,9 +31,7 @@ WinStatsStripChart::
|
|||
WinStatsStripChart(WinStatsMonitor *monitor, int thread_index,
|
||||
int collector_index, bool show_level) :
|
||||
PStatStripChart(monitor,
|
||||
show_level ? monitor->get_level_view(0, thread_index) : monitor->get_view(thread_index),
|
||||
thread_index,
|
||||
collector_index,
|
||||
thread_index, collector_index, show_level,
|
||||
monitor->get_pixel_scale() * default_strip_chart_width / 4,
|
||||
monitor->get_pixel_scale() * default_strip_chart_height / 4),
|
||||
WinStatsGraph(monitor)
|
||||
|
|
@ -62,6 +60,8 @@ WinStatsStripChart(WinStatsMonitor *monitor, int thread_index,
|
|||
|
||||
create_window();
|
||||
clear_region();
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -217,6 +217,9 @@ on_popup_label(int collector_index) {
|
|||
} else {
|
||||
AppendMenu(popup, MF_STRING, 103, "Open Flame Graph");
|
||||
}
|
||||
AppendMenu(popup, MF_STRING | MF_SEPARATOR, 0, nullptr);
|
||||
AppendMenu(popup, MF_STRING, 104, "Change Color...");
|
||||
AppendMenu(popup, MF_STRING, 105, "Reset Color");
|
||||
TrackPopupMenu(popup, TPM_LEFTBUTTON, point.x, point.y, 0, _window, nullptr);
|
||||
}
|
||||
}
|
||||
|
|
@ -239,6 +242,13 @@ set_collector_index(int collector_index) {
|
|||
if (get_collector_index() != collector_index) {
|
||||
PStatStripChart::set_collector_index(collector_index);
|
||||
|
||||
if (is_title_unknown()) {
|
||||
std::string window_title = get_title_text();
|
||||
if (!is_title_unknown()) {
|
||||
SetWindowText(_window, window_title.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Redraw the scale labels.
|
||||
RECT rect;
|
||||
GetClientRect(_window, &rect);
|
||||
|
|
@ -377,6 +387,28 @@ end_draw(int from_x, int to_x) {
|
|||
InvalidateRect(_graph_window, &rect, FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current window dimensions.
|
||||
*/
|
||||
bool WinStatsStripChart::
|
||||
get_window_state(int &x, int &y, int &width, int &height,
|
||||
bool &maximized, bool &minimized) const {
|
||||
WinStatsGraph::get_window_state(x, y, width, height, maximized, minimized);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to restore the graph window to its previous dimensions.
|
||||
*/
|
||||
void WinStatsStripChart::
|
||||
set_window_state(int x, int y, int width, int height,
|
||||
bool maximized, bool minimized) {
|
||||
WinStatsGraph::set_window_state(x, y, width, height, maximized, minimized);
|
||||
|
||||
// Set the state of the checkbox.
|
||||
SendMessage(_smooth_check_box, BM_SETCHECK, get_average_mode() ? BST_CHECKED : BST_UNCHECKED, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
@ -413,6 +445,14 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
|||
case 103:
|
||||
WinStatsGraph::_monitor->open_flame_graph(get_thread_index(), _popup_index);
|
||||
return 0;
|
||||
|
||||
case 104:
|
||||
WinStatsGraph::_monitor->choose_collector_color(_popup_index);
|
||||
return 0;
|
||||
|
||||
case 105:
|
||||
WinStatsGraph::_monitor->reset_collector_color(_popup_index);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -61,6 +61,11 @@ protected:
|
|||
virtual void draw_cursor(int x);
|
||||
virtual void end_draw(int from_x, int to_x);
|
||||
|
||||
virtual bool get_window_state(int &x, int &y, int &width, int &height,
|
||||
bool &maximized, bool &minimized) const;
|
||||
virtual void set_window_state(int x, int y, int width, int height,
|
||||
bool maximized, bool minimized);
|
||||
|
||||
LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
|
||||
virtual LONG graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
|
||||
virtual void additional_window_paint(HDC hdc);
|
||||
|
|
|
|||
|
|
@ -247,6 +247,25 @@ animate(double time, double dt) {
|
|||
return PStatTimeline::animate(time, dt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current window dimensions.
|
||||
*/
|
||||
bool WinStatsTimeline::
|
||||
get_window_state(int &x, int &y, int &width, int &height,
|
||||
bool &maximized, bool &minimized) const {
|
||||
WinStatsGraph::get_window_state(x, y, width, height, maximized, minimized);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to restore the graph window to its previous dimensions.
|
||||
*/
|
||||
void WinStatsTimeline::
|
||||
set_window_state(int x, int y, int width, int height,
|
||||
bool maximized, bool minimized) {
|
||||
WinStatsGraph::set_window_state(x, y, width, height, maximized, minimized);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
@ -417,6 +436,9 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
|||
AppendMenu(popup, MF_STRING, 102, "Open Strip Chart");
|
||||
AppendMenu(popup, MF_STRING, 103, "Open Flame Graph");
|
||||
AppendMenu(popup, MF_STRING, 104, "Open Piano Roll");
|
||||
AppendMenu(popup, MF_STRING | MF_SEPARATOR, 0, nullptr);
|
||||
AppendMenu(popup, MF_STRING, 105, "Change Color...");
|
||||
AppendMenu(popup, MF_STRING, 106, "Reset Color");
|
||||
TrackPopupMenu(popup, TPM_LEFTBUTTON, point.x, point.y, 0, _graph_window, nullptr);
|
||||
}
|
||||
}
|
||||
|
|
@ -447,6 +469,14 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
|||
case 104:
|
||||
WinStatsGraph::_monitor->open_piano_roll(_popup_bar._thread_index);
|
||||
return 0;
|
||||
|
||||
case 105:
|
||||
WinStatsGraph::_monitor->choose_collector_color(_popup_bar._collector_index);
|
||||
return 0;
|
||||
|
||||
case 106:
|
||||
WinStatsGraph::_monitor->reset_collector_color(_popup_bar._collector_index);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,11 @@ protected:
|
|||
|
||||
virtual bool animate(double time, double dt);
|
||||
|
||||
virtual bool get_window_state(int &x, int &y, int &width, int &height,
|
||||
bool &maximized, bool &minimized) const;
|
||||
virtual void set_window_state(int x, int y, int width, int height,
|
||||
bool maximized, bool minimized);
|
||||
|
||||
LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
|
||||
virtual LONG graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
|
||||
virtual void additional_window_paint(HDC hdc);
|
||||
|
|
|
|||
Loading…
Reference in New Issue