From 706c354b02617cd1715a38e5fdb77dac124822db Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 1 Dec 2022 15:05:34 +0100 Subject: [PATCH] pstats: Support for command-line options (session file, port number) --- pandatool/src/gtk-stats/gtkStats.cxx | 2 +- pandatool/src/gtk-stats/gtkStatsServer.cxx | 87 +++++++++++++++++++--- pandatool/src/gtk-stats/gtkStatsServer.h | 12 ++- pandatool/src/win-stats/winStats.cxx | 18 ++--- pandatool/src/win-stats/winStatsServer.cxx | 69 +++++++++++++---- pandatool/src/win-stats/winStatsServer.h | 12 ++- 6 files changed, 160 insertions(+), 40 deletions(-) diff --git a/pandatool/src/gtk-stats/gtkStats.cxx b/pandatool/src/gtk-stats/gtkStats.cxx index 6e8e8b1dc9..344af2016b 100644 --- a/pandatool/src/gtk-stats/gtkStats.cxx +++ b/pandatool/src/gtk-stats/gtkStats.cxx @@ -22,7 +22,7 @@ main(int argc, char *argv[]) { // Create the server window. GtkStatsServer *server = new GtkStatsServer; - server->new_session(); + server->parse_command_line(argc, argv); // Now get lost in the message loop. gtk_main(); diff --git a/pandatool/src/gtk-stats/gtkStatsServer.cxx b/pandatool/src/gtk-stats/gtkStatsServer.cxx index ccf78fb9c3..ce7f99e1e9 100644 --- a/pandatool/src/gtk-stats/gtkStatsServer.cxx +++ b/pandatool/src/gtk-stats/gtkStatsServer.cxx @@ -17,11 +17,36 @@ #include "pStatGraph.h" #include "config_pstatclient.h" +#include + /** * */ GtkStatsServer:: -GtkStatsServer() { +GtkStatsServer() : _port(pstats_port) { + set_program_brief("GTK+3-based PStats client"); + set_program_description + ("This is a GUI-based PStats server that listens on a TCP port for a " + "connection from a PStatClient in a Panda3D application. It offers " + "various graphs for showing the timing information sent by the client." + "\n\n" + "The full documentation is available online:\n " +#ifdef HAVE_PYTHON + "https://docs.panda3d.org/" PANDA_ABI_VERSION_STR "/python/optimization/pstats" +#else + "https://docs.panda3d.org/" PANDA_ABI_VERSION_STR "/cpp/optimization/pstats" +#endif + ""); + + add_option + ("p", "port", 0, + "Specify the TCP port to listen for connections on. By default, this " + "is taken from the pstats-port Config variable.", + &ProgramBase::dispatch_int, nullptr, &_port); + + add_runline("[-p 5185]"); + add_runline("session.pstats"); + #ifdef __APPLE__ _last_session = Filename::expand_from( "$HOME/Library/Caches/Panda3D-" PANDA_ABI_VERSION_STR "/last-session.pstats"); @@ -30,12 +55,56 @@ GtkStatsServer() { #endif _last_session.set_binary(); - - _time_units = 0; - create_window(); } +/** + * Does something with the additional arguments on the command line (after all + * the -options have been parsed). Returns true if the arguments are good, + * false otherwise. + */ +bool GtkStatsServer:: +handle_args(ProgramBase::Args &args) { + if (args.empty()) { + new_session(); + return true; + } + else if (args.size() == 1) { + Filename fn = Filename::from_os_specific(args[0]); + fn.set_binary(); + GtkStatsMonitor *monitor = new GtkStatsMonitor(this); + if (!monitor->read(fn)) { + delete monitor; + + // If we're not running from the terminal, show a GUI message box. + if (!isatty(STDERR_FILENO)) { + 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; + } + else { + nout << "At most one filename may be specified on the command-line.\n"; + return false; + } +} + /** * */ @@ -97,16 +166,16 @@ new_session() { return false; } - if (listen()) { + if (listen(_port)) { { std::ostringstream strm; - strm << "PStats Server (listening on port " << pstats_port << ")"; + strm << "PStats Server (listening on port " << _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 << "..."; + strm << "Waiting for client to connect on port " << _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); @@ -129,7 +198,8 @@ new_session() { 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()); + "using pstats-port in your Config file or the -p option on the " + "command-line.", _port); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); @@ -405,7 +475,6 @@ close_session() { return true; } - /** * Returns the window handle to the server's window. */ diff --git a/pandatool/src/gtk-stats/gtkStatsServer.h b/pandatool/src/gtk-stats/gtkStatsServer.h index a027ddecfe..9aa3507a7d 100644 --- a/pandatool/src/gtk-stats/gtkStatsServer.h +++ b/pandatool/src/gtk-stats/gtkStatsServer.h @@ -15,18 +15,21 @@ #define GTKSTATSSERVER_H #include "pandatoolbase.h" +#include "programBase.h" #include "pStatServer.h" #include "gtkStatsMonitor.h" /** * The class that owns the main loop, waiting for client connections. */ -class GtkStatsServer : public PStatServer { +class GtkStatsServer : public PStatServer, public ProgramBase { public: GtkStatsServer(); - virtual PStatMonitor *make_monitor(const NetAddress &address); - virtual void lost_connection(PStatMonitor *monitor); + virtual bool handle_args(Args &args) override; + + virtual PStatMonitor *make_monitor(const NetAddress &address) override; + virtual void lost_connection(PStatMonitor *monitor) override; bool new_session(); bool open_session(); @@ -58,6 +61,7 @@ private: Filename _last_session; Filename _save_filename; + int _port = -1; GtkWidget *_window = nullptr; GtkAccelGroup *_accel_group = nullptr; GtkWidget *_menu_bar = nullptr; @@ -70,7 +74,7 @@ private: GtkWidget *_save_session_menu_item; GtkWidget *_close_session_menu_item; GtkWidget *_export_session_menu_item; - int _time_units; + int _time_units = 0; }; #endif diff --git a/pandatool/src/win-stats/winStats.cxx b/pandatool/src/win-stats/winStats.cxx index 74add4b083..a98d20631c 100644 --- a/pandatool/src/win-stats/winStats.cxx +++ b/pandatool/src/win-stats/winStats.cxx @@ -26,7 +26,7 @@ // 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='*'\"") -int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { +int main(int argc, char *argv[]) { // Initialize commctl32.dll. INITCOMMONCONTROLSEX icc; icc.dwICC = ICC_WIN95_CLASSES | ICC_STANDARD_CLASSES; @@ -38,7 +38,11 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { // Create the server window. WinStatsServer *server = new WinStatsServer; - server->new_session(); + if (server->parse_command_line(argc, argv, false) == ProgramBase::EC_failure) { + MessageBox(nullptr, "Failed to parse command-line options.", + "PStats Error", MB_OK | MB_ICONEXCLAMATION); + return 1; + } // Now get lost in the Windows message loop. MSG msg; @@ -54,13 +58,9 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { retval = GetMessage(&msg, nullptr, 0, 0); } - return (0); + return 0; } -// WinMain() is the correct way to start a Windows-only application, but it is -// sometimes more convenient during development to use main() instead, which -// doesn't squelch the stderr output. - -int main(int argc, char *argv[]) { - return WinMain(nullptr, nullptr, nullptr, 0); +int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { + return main(__argc, __argv); } diff --git a/pandatool/src/win-stats/winStatsServer.cxx b/pandatool/src/win-stats/winStatsServer.cxx index 63834a2565..bad24e87b8 100644 --- a/pandatool/src/win-stats/winStatsServer.cxx +++ b/pandatool/src/win-stats/winStatsServer.cxx @@ -28,17 +28,14 @@ const char *const WinStatsServer::_window_class_name = "server"; * */ WinStatsServer:: -WinStatsServer() { +WinStatsServer() : _port(pstats_port) { + set_program_brief("Windows PStats client"); + add_option("p", "port", 0, "", &ProgramBase::dispatch_int, nullptr, &_port); + _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); @@ -51,6 +48,52 @@ WinStatsServer() { create_window(); } +/** + * Does something with the additional arguments on the command line (after all + * the -options have been parsed). Returns true if the arguments are good, + * false otherwise. + */ +bool WinStatsServer:: +handle_args(ProgramBase::Args &args) { + if (args.empty()) { + new_session(); + return true; + } + else if (args.size() == 1) { + Filename fn = Filename::from_os_specific(args[0]); + 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 true; + } + + // 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); + + _monitor = monitor; + return true; + } + else { + nout << "At most one filename may be specified on the command-line.\n"; + return false; + } +} + /** * */ @@ -112,16 +155,16 @@ new_session() { return false; } - if (listen()) { + if (listen(_port)) { { std::ostringstream strm; - strm << "PStats Server (listening on port " << pstats_port << ")"; + strm << "PStats Server (listening on port " << _port << ")"; std::string title = strm.str(); SetWindowTextA(_window, title.c_str()); } { std::ostringstream strm; - strm << "Waiting for client to connect on port " << pstats_port << "..."; + strm << "Waiting for client to connect on port " << _port << "..."; std::string title = strm.str(); int part = -1; SendMessage(_status_bar, SB_SETPARTS, 1, (LPARAM)&part); @@ -155,9 +198,9 @@ new_session() { std::ostringstream stream; stream - << "Unable to open port " << pstats_port - << ". Try specifying a different\n" - << "port number using pstats-port in your Config file."; + << "Unable to open port " << _port << ". Try specifying a different " + << "port number using pstats-port in your Config file or the -p option on " + << "the command-line."; std::string str = stream.str(); MessageBox(_window, str.c_str(), "PStats Error", MB_OK | MB_ICONEXCLAMATION); diff --git a/pandatool/src/win-stats/winStatsServer.h b/pandatool/src/win-stats/winStatsServer.h index cb069f46e2..1c1e1341c7 100644 --- a/pandatool/src/win-stats/winStatsServer.h +++ b/pandatool/src/win-stats/winStatsServer.h @@ -15,18 +15,21 @@ #define WINSTATSSERVER_H #include "pandatoolbase.h" +#include "programBase.h" #include "pStatServer.h" #include "winStatsMonitor.h" /** * The class that owns the main loop, waiting for client connections. */ -class WinStatsServer : public PStatServer { +class WinStatsServer : public PStatServer, public ProgramBase { public: WinStatsServer(); - virtual PStatMonitor *make_monitor(const NetAddress &address); - virtual void lost_connection(PStatMonitor *monitor); + virtual bool handle_args(Args &args) override; + + virtual PStatMonitor *make_monitor(const NetAddress &address) override; + virtual void lost_connection(PStatMonitor *monitor) override; bool new_session(); bool open_session(); @@ -60,13 +63,14 @@ private: Filename _last_session; + int _port = -1; 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 _time_units = 0; int _pixel_scale; HFONT _font;