From 0e366091e521729ee0aea738be521461dcb5abe7 Mon Sep 17 00:00:00 2001 From: David Rose Date: Thu, 25 Dec 2003 16:15:14 +0000 Subject: [PATCH] beginnings of win-stats --- pandatool/src/pstatserver/pStatPianoRoll.cxx | 1 + pandatool/src/pstatserver/pStatStripChart.cxx | 54 +++ pandatool/src/pstatserver/pStatStripChart.h | 4 + pandatool/src/win-stats/Sources.pp | 23 ++ pandatool/src/win-stats/winStats.cxx | 126 +++++++ pandatool/src/win-stats/winStats.h | 25 ++ pandatool/src/win-stats/winStatsGraph.cxx | 209 +++++++++++ pandatool/src/win-stats/winStatsGraph.h | 71 ++++ pandatool/src/win-stats/winStatsMonitor.cxx | 333 +++++++++++++++++ pandatool/src/win-stats/winStatsMonitor.h | 77 ++++ pandatool/src/win-stats/winStatsServer.cxx | 30 ++ pandatool/src/win-stats/winStatsServer.h | 36 ++ .../src/win-stats/winStatsStripChart.cxx | 336 ++++++++++++++++++ pandatool/src/win-stats/winStatsStripChart.h | 67 ++++ 14 files changed, 1392 insertions(+) create mode 100644 pandatool/src/win-stats/Sources.pp create mode 100644 pandatool/src/win-stats/winStats.cxx create mode 100644 pandatool/src/win-stats/winStats.h create mode 100644 pandatool/src/win-stats/winStatsGraph.cxx create mode 100644 pandatool/src/win-stats/winStatsGraph.h create mode 100644 pandatool/src/win-stats/winStatsMonitor.cxx create mode 100644 pandatool/src/win-stats/winStatsMonitor.h create mode 100644 pandatool/src/win-stats/winStatsServer.cxx create mode 100644 pandatool/src/win-stats/winStatsServer.h create mode 100644 pandatool/src/win-stats/winStatsStripChart.cxx create mode 100644 pandatool/src/win-stats/winStatsStripChart.h diff --git a/pandatool/src/pstatserver/pStatPianoRoll.cxx b/pandatool/src/pstatserver/pStatPianoRoll.cxx index 8d02f6a0b8..ac0a149e05 100644 --- a/pandatool/src/pstatserver/pStatPianoRoll.cxx +++ b/pandatool/src/pstatserver/pStatPianoRoll.cxx @@ -260,6 +260,7 @@ end_draw() { //////////////////////////////////////////////////////////////////// void PStatPianoRoll:: idle() { + update(); } diff --git a/pandatool/src/pstatserver/pStatStripChart.cxx b/pandatool/src/pstatserver/pStatStripChart.cxx index 7339d04283..311b84442b 100644 --- a/pandatool/src/pstatserver/pStatStripChart.cxx +++ b/pandatool/src/pstatserver/pStatStripChart.cxx @@ -50,6 +50,7 @@ PStatStripChart(PStatMonitor *monitor, PStatView &view, _start_time = 0.0; _level_index = 0; + _title_unknown = true; const PStatClientData *client_data = _monitor->get_client_data(); if (client_data->has_collector(_collector_index)) { @@ -236,6 +237,59 @@ get_collector_under_pixel(int xpoint, int ypoint) { return -1; } +//////////////////////////////////////////////////////////////////// +// Function: PStatStripChart::get_title_text +// Access: Private +// Description: Returns the text suitable for the title label on the +// top line. +//////////////////////////////////////////////////////////////////// +string PStatStripChart:: +get_title_text() { + string text; + + _title_unknown = false; + bool _show_level = false; + int _thread_index = 0; + + const PStatClientData *client_data = _monitor->get_client_data(); + if (client_data->has_collector(_collector_index)) { + const PStatCollectorDef &def = client_data->get_collector_def(_collector_index); + if (_show_level) { + if (def._level_units.empty()) { + text = def._name; + } else { + text = def._name + " (" + def._level_units + ")"; + } + } else { + text = def._name + " time"; + } + } else { + _title_unknown = true; + } + + if (_thread_index != 0) { + if (client_data->has_thread(_thread_index)) { + text += "(" + client_data->get_thread_name(_thread_index) + " thread)"; + } else { + _title_unknown = true; + } + } + + return text; +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatStripChart::is_title_unknown +// Access: Public +// Description: Returns true if get_title_text() has never yet +// returned an answer, false if it has. +//////////////////////////////////////////////////////////////////// +bool PStatStripChart:: +is_title_unknown() const { + return _title_unknown; +} + + //////////////////////////////////////////////////////////////////// // Function: PStatStripChart::get_frame_data // Access: Protected diff --git a/pandatool/src/pstatserver/pStatStripChart.h b/pandatool/src/pstatserver/pStatStripChart.h index 14d0947885..fe39aed73a 100644 --- a/pandatool/src/pstatserver/pStatStripChart.h +++ b/pandatool/src/pstatserver/pStatStripChart.h @@ -72,6 +72,9 @@ public: INLINE int height_to_pixel(float value) const; INLINE float pixel_to_height(int y) const; + string get_title_text(); + bool is_title_unknown() const; + protected: class ColorData { public: @@ -117,6 +120,7 @@ private: float _time_width; float _start_time; float _value_height; + bool _title_unknown; }; #include "pStatStripChart.I" diff --git a/pandatool/src/win-stats/Sources.pp b/pandatool/src/win-stats/Sources.pp new file mode 100644 index 0000000000..8fcfd83e1a --- /dev/null +++ b/pandatool/src/win-stats/Sources.pp @@ -0,0 +1,23 @@ +#define BUILD_DIRECTORY $[and $[WINDOWS_PLATFORM],$[HAVE_NET]] +#define USE_PACKAGES net + +#begin bin_target + #define TARGET pstats + #define LOCAL_LIBS \ + progbase pstatserver + #define OTHER_LIBS \ + pstatclient:c linmath:c putil:c net:c express:c pandaexpress:m panda:m \ + dtoolutil:c dtoolbase:c dconfig:c dtoolconfig:m dtool:m \ + pystub + + #define SOURCES \ + winStats.cxx \ + winStatsGraph.cxx winStatsGraph.h \ + winStatsServer.cxx winStatsServer.h \ + winStatsMonitor.cxx winStatsMonitor.h \ + winStatsStripChart.cxx winStatsStripChart.h + + #define WIN_SYS_LIBS Imm32.lib winmm.lib kernel32.lib oldnames.lib user32.lib gdi32.lib + +#end bin_target + diff --git a/pandatool/src/win-stats/winStats.cxx b/pandatool/src/win-stats/winStats.cxx new file mode 100644 index 0000000000..d03504e8d2 --- /dev/null +++ b/pandatool/src/win-stats/winStats.cxx @@ -0,0 +1,126 @@ +// Filename: winStats.cxx +// Created by: drose (02Dec03) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "pandatoolbase.h" + +#include "winStatsServer.h" +#include "config_pstats.h" + +#include + +static const char *toplevel_class_name = "pstats"; +static WinStatsServer *server = NULL; + +//////////////////////////////////////////////////////////////////// +// Function: toplevel_window_proc +// Description: +//////////////////////////////////////////////////////////////////// +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); +} + + +//////////////////////////////////////////////////////////////////// +// Function: create_toplevel_window +// Description: 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; + + HWND toplevel_window = + CreateWindow(toplevel_class_name, "PStats", window_style, + CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, + NULL, NULL, application, 0); + if (!toplevel_window) { + nout << "Could not create toplevel window!\n"; + exit(1); + } + + return toplevel_window; +} + + +// 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. + +#ifdef USE_WINMAIN +int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) +#else +int main(int argc, char *argv[]) +#endif +{ + HINSTANCE application = GetModuleHandle(NULL); + HWND toplevel_window = create_toplevel_window(application); + + ShowWindow(toplevel_window, SW_SHOWMINIMIZED); + + // Create the server object. + server = new WinStatsServer; + if (!server->listen()) { + nout << "Unable to open port.\n"; + exit(1); + } + + // Set up a timer to poll the pstats every so often. + SetTimer(toplevel_window, 1, 200, NULL); + + // Now get lost in the Windows message loop. + MSG msg; + int retval; + retval = GetMessage(&msg, NULL, 0, 0); + while (retval != 0) { + if (retval == -1) { + nout << "Error processing message queue.\n"; + exit(1); + } + TranslateMessage(&msg); + DispatchMessage(&msg); + retval = GetMessage(&msg, NULL, 0, 0); + } + + return (0); +} diff --git a/pandatool/src/win-stats/winStats.h b/pandatool/src/win-stats/winStats.h new file mode 100644 index 0000000000..802a4bc284 --- /dev/null +++ b/pandatool/src/win-stats/winStats.h @@ -0,0 +1,25 @@ +// Filename: winStats.h +// Created by: drose (02Dec03) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef WINSTATS_H +#define WINSTATS_H + +#include "pStatServer.h" + +#endif + diff --git a/pandatool/src/win-stats/winStatsGraph.cxx b/pandatool/src/win-stats/winStatsGraph.cxx new file mode 100644 index 0000000000..f412bb296d --- /dev/null +++ b/pandatool/src/win-stats/winStatsGraph.cxx @@ -0,0 +1,209 @@ +// Filename: winStatsGraph.cxx +// Created by: drose (03Dec03) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "winStatsGraph.h" +#include "winStatsMonitor.h" + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsGraph::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +WinStatsGraph:: +WinStatsGraph(WinStatsMonitor *monitor) : + _monitor(monitor) +{ + _window = 0; + _bitmap = 0; + _bitmap_dc = 0; + _bitmap_xsize = 0; + _bitmap_ysize = 0; + _left_margin = 64; + _right_margin = 32; + _top_margin = 16; + _bottom_margin = 8; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsGraph::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +WinStatsGraph:: +~WinStatsGraph() { + _monitor = (WinStatsMonitor *)NULL; + release_bitmap(); + + Brushes::iterator bi; + for (bi = _brushes.begin(); bi != _brushes.end(); ++bi) { + HBRUSH brush = (*bi).second; + DeleteObject(brush); + } + + if (_window) { + DestroyWindow(_window); + _window = 0; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsGraph::new_collector +// Access: Public, Virtual +// Description: Called whenever a new Collector definition is +// received from the client. +//////////////////////////////////////////////////////////////////// +void WinStatsGraph:: +new_collector(int new_collector) { +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsGraph::new_data +// Access: Public, Virtual +// Description: Called whenever new data arrives. +//////////////////////////////////////////////////////////////////// +void WinStatsGraph:: +new_data(int thread_index, int frame_number) { +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsGraph::close +// Access: Protected +// Description: Should be called when the user closes the associated +// window. This tells the monitor to remove the graph. +//////////////////////////////////////////////////////////////////// +void WinStatsGraph:: +close() { + WinStatsMonitor *monitor = _monitor; + _monitor = (WinStatsMonitor *)NULL; + if (monitor != (WinStatsMonitor *)NULL) { + monitor->remove_graph(this); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsGraph::setup_bitmap +// Access: Protected +// Description: Sets up a backing-store bitmap of the indicated size. +//////////////////////////////////////////////////////////////////// +void WinStatsGraph:: +setup_bitmap(int xsize, int ysize) { + release_bitmap(); + _bitmap_xsize = max(xsize, 0); + _bitmap_ysize = max(ysize, 0); + + _frame_rect.left = _left_margin; + _frame_rect.top = _top_margin; + _frame_rect.right = _left_margin + _bitmap_xsize; + _frame_rect.bottom = _bottom_margin + _bitmap_ysize; + + HDC hdc = GetDC(_window); + _bitmap_dc = CreateCompatibleDC(hdc); + _bitmap = CreateCompatibleBitmap(hdc, _bitmap_xsize, _bitmap_ysize); + SelectObject(_bitmap_dc, _bitmap); + ReleaseDC(_window, hdc); +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsGraph::release_bitmap +// Access: Protected +// Description: Frees the backing-store bitmap created by +// setup_bitmap(). +//////////////////////////////////////////////////////////////////// +void WinStatsGraph:: +release_bitmap() { + if (_bitmap) { + DeleteObject(_bitmap); + _bitmap = 0; + } + if (_bitmap_dc) { + DeleteDC(_bitmap_dc); + _bitmap_dc = 0; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsGraph::get_collector_brush +// Access: Protected +// Description: Returns a brush suitable for drawing in the indicated +// collector's color. +//////////////////////////////////////////////////////////////////// +HBRUSH WinStatsGraph:: +get_collector_brush(int collector_index) { + Brushes::iterator bi; + bi = _brushes.find(collector_index); + if (bi != _brushes.end()) { + return (*bi).second; + } + + // Ask the monitor what color this guy should be. + RGBColorf rgb = _monitor->get_collector_color(collector_index); + int r = (int)(rgb[0] * 255.0f); + int g = (int)(rgb[1] * 255.0f); + int b = (int)(rgb[2] * 255.0f); + HBRUSH brush = CreateSolidBrush(RGB(r, g, b)); + + _brushes[collector_index] = brush; + return brush; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsGraph::draw_graph +// Access: Protected +// Description: Draws the graph into the window by blitting the +// backing-store bitmap in, along with a suitable frame. +//////////////////////////////////////////////////////////////////// +void WinStatsGraph:: +draw_graph(HDC hdc) { + if (_bitmap_xsize == 0 || _bitmap_ysize == 0) { + // Never mind: nothing to draw. + return; + } + + // First, draw a frame around the graph. + + // Windows doesn't seem to have an API to ask how big the outer + // frame will be before we draw it, only a way to draw the outer + // frame and return the size of the inner frame afterwards. + + // So we have to make our best guess about the correct size of the + // outer frame before we draw it, then examine the size of the + // resulting inner frame. If it didn't come out to the correct size + // (that is, exactly large enough to frame our graph), we expand the + // outer frame by the difference, and redraw it. + + RECT rect = _frame_rect; + DrawEdge(hdc, &rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); + + if (rect.left != _left_margin || + rect.top != _top_margin || + rect.right != _left_margin + _bitmap_xsize || + rect.bottom != _top_margin + _bitmap_ysize) { + _frame_rect.left = _left_margin - (rect.left - _frame_rect.left); + _frame_rect.top = _top_margin - (rect.top - _frame_rect.top); + _frame_rect.right = _left_margin + _bitmap_xsize + (_frame_rect.right - rect.right); + _frame_rect.bottom = _top_margin + _bitmap_ysize + (_frame_rect.bottom - rect.bottom); + + DrawEdge(hdc, &_frame_rect, EDGE_SUNKEN, BF_RECT); + } + + // Now fill in the graph. + BitBlt(hdc, _left_margin, _top_margin, + _bitmap_xsize, _bitmap_ysize, + _bitmap_dc, 0, 0, + SRCCOPY); +} diff --git a/pandatool/src/win-stats/winStatsGraph.h b/pandatool/src/win-stats/winStatsGraph.h new file mode 100644 index 0000000000..2e43b1a76c --- /dev/null +++ b/pandatool/src/win-stats/winStatsGraph.h @@ -0,0 +1,71 @@ +// Filename: winStatsGraph.h +// Created by: drose (03Dec03) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef WINSTATSGRAPH_H +#define WINSTATSGRAPH_H + +#include "pandatoolbase.h" +#include "pmap.h" + +#include + +class WinStatsMonitor; + +//////////////////////////////////////////////////////////////////// +// Class : WinStatsGraph +// Description : This is just an abstract base class to provide a +// common pointer type for the various kinds of graphs +// that may be created for a WinStatsMonitor. +//////////////////////////////////////////////////////////////////// +class WinStatsGraph { +public: + WinStatsGraph(WinStatsMonitor *monitor); + virtual ~WinStatsGraph(); + + virtual void new_collector(int collector_index); + virtual void new_data(int thread_index, int frame_number); + +protected: + void close(); + + void setup_bitmap(int xsize, int ysize); + void release_bitmap(); + + HBRUSH get_collector_brush(int collector_index); + void draw_graph(HDC hdc); + +protected: + // Table of brushes for our various collectors. + typedef pmap Brushes; + Brushes _brushes; + + WinStatsMonitor *_monitor; + HWND _window; + + HBITMAP _bitmap; + HDC _bitmap_dc; + + int _bitmap_xsize, _bitmap_ysize; + int _left_margin, _right_margin; + int _top_margin, _bottom_margin; + + RECT _frame_rect; +}; + +#endif + diff --git a/pandatool/src/win-stats/winStatsMonitor.cxx b/pandatool/src/win-stats/winStatsMonitor.cxx new file mode 100644 index 0000000000..639fc5db8f --- /dev/null +++ b/pandatool/src/win-stats/winStatsMonitor.cxx @@ -0,0 +1,333 @@ +// Filename: winStatsMonitor.cxx +// Created by: drose (02Dec03) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "winStatsMonitor.h" +#include "winStatsStripChart.h" + +#include "pStatCollectorDef.h" + +bool WinStatsMonitor::_window_class_registered = false; +const char * const WinStatsMonitor::_window_class_name = "monitor"; + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsMonitor::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +WinStatsMonitor:: +WinStatsMonitor() { + _window = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsMonitor::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +WinStatsMonitor:: +~WinStatsMonitor() { + cerr << "WinStatsMonitor destructor\n"; + Graphs::iterator gi; + for (gi = _graphs.begin(); gi != _graphs.end(); ++gi) { + delete (*gi); + } + _graphs.clear(); + + if (_window) { + DestroyWindow(_window); + _window = 0; + } + + // For now, exit when the first monitor closes. + exit(0); +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsMonitor::get_monitor_name +// Access: Public, Virtual +// Description: Should be redefined to return a descriptive name for +// the type of PStatsMonitor this is. +//////////////////////////////////////////////////////////////////// +string WinStatsMonitor:: +get_monitor_name() { + return "WinStats"; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsMonitor::initialized +// Access: Public, Virtual +// Description: Called after the monitor has been fully set up. At +// this time, it will have a valid _client_data pointer, +// and things like is_alive() and close() will be +// meaningful. However, we may not yet know who we're +// connected to (is_client_known() may return false), +// and we may not know anything about the threads or +// collectors we're about to get data on. +//////////////////////////////////////////////////////////////////// +void WinStatsMonitor:: +initialized() { + cerr << "Monitor initialized (refcount = " << get_ref_count() << ")\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsMonitor::got_hello +// Access: Public, Virtual +// Description: Called when the "hello" message has been received +// from the client. At this time, the client's hostname +// and program name will be known. +//////////////////////////////////////////////////////////////////// +void WinStatsMonitor:: +got_hello() { + create_window(); + + add_graph(new WinStatsStripChart(this, get_view(0), 0)); +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsMonitor::got_bad_version +// Access: Public, Virtual +// Description: Like got_hello(), this is called when the "hello" +// message has been received from the client. At this +// time, the client's hostname and program name will be +// known. However, the client appears to be an +// incompatible version and the connection will be +// terminated; the monitor should issue a message to +// that effect. +//////////////////////////////////////////////////////////////////// +void WinStatsMonitor:: +got_bad_version(int client_major, int client_minor, + int server_major, int server_minor) { + cerr << "Got bad version " << client_major << "." << client_minor + << " from " << get_client_progname() << " on " + << get_client_hostname() << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsMonitor::new_collector +// Access: Public, Virtual +// Description: Called whenever a new Collector definition is +// received from the client. Generally, the client will +// send all of its collectors over shortly after +// connecting, but there's no guarantee that they will +// all be received before the first frames are received. +// The monitor should be prepared to accept new Collector +// definitions midstream. +//////////////////////////////////////////////////////////////////// +void WinStatsMonitor:: +new_collector(int collector_index) { + cerr << "Got new collector " << get_collector_name(collector_index) + << "\n"; + Graphs::iterator gi; + for (gi = _graphs.begin(); gi != _graphs.end(); ++gi) { + WinStatsGraph *graph = (*gi); + graph->new_collector(collector_index); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsMonitor::new_data +// Access: Public, Virtual +// Description: Called as each frame's data is made available. There +// is no gurantee the frames will arrive in order, or +// that all of them will arrive at all. The monitor +// should be prepared to accept frames received +// out-of-order or missing. +//////////////////////////////////////////////////////////////////// +void WinStatsMonitor:: +new_data(int thread_index, int frame_number) { + Graphs::iterator gi; + for (gi = _graphs.begin(); gi != _graphs.end(); ++gi) { + WinStatsGraph *graph = (*gi); + graph->new_data(thread_index, frame_number); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsMonitor::lost_connection +// Access: Public, Virtual +// Description: Called whenever the connection to the client has been +// lost. This is a permanent state change. The monitor +// should update its display to represent this, and may +// choose to close down automatically. +//////////////////////////////////////////////////////////////////// +void WinStatsMonitor:: +lost_connection() { + cerr << "Lost connection to " << get_client_hostname() + << " (refcount = " << get_ref_count() << ")\n"; + + if (_window) { + DestroyWindow(_window); + _window = 0; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsMonitor::idle +// Access: Public, Virtual +// Description: If has_idle() returns true, this will be called +// periodically to allow the monitor to update its +// display or whatever it needs to do. +//////////////////////////////////////////////////////////////////// +void WinStatsMonitor:: +idle() { +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsMonitor::has_idle +// Access: Public, Virtual +// Description: Should be redefined to return true if you want to +// redefine idle() and expect it to be called. +//////////////////////////////////////////////////////////////////// +bool WinStatsMonitor:: +has_idle() { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsMonitor::get_window +// Access: Public +// Description: Returns the window handle to the monitor's window. +//////////////////////////////////////////////////////////////////// +HWND WinStatsMonitor:: +get_window() const { + return _window; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsMonitor::add_graph +// Access: Private +// Description: Adds the newly-created graph to the list of managed +// graphs. +//////////////////////////////////////////////////////////////////// +void WinStatsMonitor:: +add_graph(WinStatsGraph *graph) { + _graphs.insert(graph); +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsMonitor::remove_graph +// Access: Private +// Description: Deletes the indicated graph. +//////////////////////////////////////////////////////////////////// +void WinStatsMonitor:: +remove_graph(WinStatsGraph *graph) { + Graphs::iterator gi = _graphs.find(graph); + if (gi != _graphs.end()) { + _graphs.erase(gi); + delete graph; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsMonitor::create_window +// Access: Private +// Description: Creates the window for this monitor. +//////////////////////////////////////////////////////////////////// +void WinStatsMonitor:: +create_window() { + if (_window) { + return; + } + + HINSTANCE application = GetModuleHandle(NULL); + register_window_class(application); + + _window_title = get_client_progname() + " on " + get_client_hostname(); + DWORD window_style = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; + + _window = + CreateWindow(_window_class_name, _window_title.c_str(), window_style, + CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, + NULL, NULL, application, 0); + if (!_window) { + nout << "Could not create monitor window!\n"; + exit(1); + } + + SetWindowLongPtr(_window, 0, (LONG_PTR)this); + ShowWindow(_window, SW_SHOWNORMAL); +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsMonitor::register_window_class +// Access: Private, Static +// Description: Registers the window class for the monitor window, if +// it has not already been registered. +//////////////////////////////////////////////////////////////////// +void WinStatsMonitor:: +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(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)COLOR_BACKGROUND; + wc.lpszMenuName = NULL; + wc.lpszClassName = _window_class_name; + + // Reserve space to associate the this pointer with the window. + wc.cbWndExtra = sizeof(WinStatsMonitor *); + + if (!RegisterClass(&wc)) { + nout << "Could not register monitor window class!\n"; + exit(1); + } + + _window_class_registered = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsMonitor::static_window_proc +// Access: Private, Static +// Description: +//////////////////////////////////////////////////////////////////// +LONG WINAPI WinStatsMonitor:: +static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + WinStatsMonitor *self = (WinStatsMonitor *)GetWindowLongPtr(hwnd, 0); + if (self != (WinStatsMonitor *)NULL && self->_window == hwnd) { + return self->window_proc(hwnd, msg, wparam, lparam); + } else { + return DefWindowProc(hwnd, msg, wparam, lparam); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsMonitor::window_proc +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +LONG WinStatsMonitor:: +window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + switch (msg) { + case WM_DESTROY: + close(); + break; + + default: + break; + } + + return DefWindowProc(hwnd, msg, wparam, lparam); +} diff --git a/pandatool/src/win-stats/winStatsMonitor.h b/pandatool/src/win-stats/winStatsMonitor.h new file mode 100644 index 0000000000..13ca5359fd --- /dev/null +++ b/pandatool/src/win-stats/winStatsMonitor.h @@ -0,0 +1,77 @@ +// Filename: winStatsMonitor.h +// Created by: drose (02Dec03) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef WINSTATSMONITOR_H +#define WINSTATSMONITOR_H + +#include "pandatoolbase.h" + +#include "winStatsGraph.h" +#include "pStatMonitor.h" +#include "pointerTo.h" +#include "pset.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : WinStatsMonitor +// Description : This class represents a connection to a PStatsClient +// and manages the data exchange with the client. +//////////////////////////////////////////////////////////////////// +class WinStatsMonitor : public PStatMonitor { +public: + WinStatsMonitor(); + virtual ~WinStatsMonitor(); + + virtual string get_monitor_name(); + + virtual void initialized(); + virtual void got_hello(); + virtual void got_bad_version(int client_major, int client_minor, + int server_major, int server_minor); + virtual void new_collector(int collector_index); + virtual void new_data(int thread_index, int frame_number); + virtual void lost_connection(); + virtual void idle(); + virtual bool has_idle(); + + HWND get_window() const; + +private: + void add_graph(WinStatsGraph *graph); + void remove_graph(WinStatsGraph *graph); + + void create_window(); + static void register_window_class(HINSTANCE application); + + static LONG WINAPI static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); + LONG WINAPI window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); + + typedef pset Graphs; + Graphs _graphs; + + HWND _window; + string _window_title; + + static bool _window_class_registered; + static const char * const _window_class_name; + + friend class WinStatsGraph; +}; + +#endif diff --git a/pandatool/src/win-stats/winStatsServer.cxx b/pandatool/src/win-stats/winStatsServer.cxx new file mode 100644 index 0000000000..7ee085e8d4 --- /dev/null +++ b/pandatool/src/win-stats/winStatsServer.cxx @@ -0,0 +1,30 @@ +// Filename: winStatsServer.cxx +// Created by: drose (02Dec03) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "winStatsServer.h" +#include "winStatsMonitor.h" + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsServer::make_monitor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PStatMonitor *WinStatsServer:: +make_monitor() { + return new WinStatsMonitor; +} diff --git a/pandatool/src/win-stats/winStatsServer.h b/pandatool/src/win-stats/winStatsServer.h new file mode 100644 index 0000000000..d6258caed6 --- /dev/null +++ b/pandatool/src/win-stats/winStatsServer.h @@ -0,0 +1,36 @@ +// Filename: winStatsServer.h +// Created by: drose (02Dec03) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef WINSTATSSERVER_H +#define WINSTATSSERVER_H + +#include "pandatoolbase.h" +#include "pStatServer.h" + +//////////////////////////////////////////////////////////////////// +// Class : WinStatsServer +// Description : The class that owns the main loop, waiting for client +// connections. +//////////////////////////////////////////////////////////////////// +class WinStatsServer : public PStatServer { +public: + virtual PStatMonitor *make_monitor(); +}; + +#endif + diff --git a/pandatool/src/win-stats/winStatsStripChart.cxx b/pandatool/src/win-stats/winStatsStripChart.cxx new file mode 100644 index 0000000000..1105c8a2af --- /dev/null +++ b/pandatool/src/win-stats/winStatsStripChart.cxx @@ -0,0 +1,336 @@ +// Filename: winStatsStripChart.cxx +// Created by: drose (03Dec03) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "winStatsStripChart.h" +#include "winStatsMonitor.h" + +static const int default_strip_chart_width = 400; +static const int default_strip_chart_height = 100; + +bool WinStatsStripChart::_window_class_registered = false; +const char * const WinStatsStripChart::_window_class_name = "strip"; + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsStripChart::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +WinStatsStripChart:: +WinStatsStripChart(WinStatsMonitor *monitor, PStatView &view, + int collector_index) : + PStatStripChart(monitor, view, collector_index, + default_strip_chart_width, + default_strip_chart_height), + WinStatsGraph(monitor) +{ + cerr << "Constructing strip chart " << (void *)this << "\n"; + _brush_origin = 0; + + setup_bitmap(get_xsize(), get_ysize()); + clear_region(); + create_window(); +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsStripChart::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +WinStatsStripChart:: +~WinStatsStripChart() { + cerr << "Destructing strip chart " << (void *)this << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsStripChart::new_collector +// Access: Public, Virtual +// Description: Called whenever a new Collector definition is +// received from the client. +//////////////////////////////////////////////////////////////////// +void WinStatsStripChart:: +new_collector(int collector_index) { + if (is_title_unknown()) { + string window_title = get_title_text(); + if (!is_title_unknown()) { + SetWindowText(_window, window_title.c_str()); + } + } + + WinStatsGraph::new_collector(collector_index); +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsStripChart::idle +// Access: Public, Virtual +// Description: Called as each frame's data is made available. There +// is no gurantee the frames will arrive in order, or +// that all of them will arrive at all. The monitor +// should be prepared to accept frames received +// out-of-order or missing. +//////////////////////////////////////////////////////////////////// +void WinStatsStripChart:: +new_data(int thread_index, int frame_number) { + update(); +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsStripChart::clear_region +// Access: Protected, Virtual +// Description: Erases the chart area. +//////////////////////////////////////////////////////////////////// +void WinStatsStripChart:: +clear_region() { + RECT rect = { 0, 0, get_xsize(), get_ysize() }; + FillRect(_bitmap_dc, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH)); +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsStripChart::copy_region +// Access: Protected, Virtual +// Description: Should be overridden by the user class to copy a +// region of the chart from one part of the chart to +// another. This is used to implement scrolling. +//////////////////////////////////////////////////////////////////// +void WinStatsStripChart:: +copy_region(int start_x, int end_x, int dest_x) { + BitBlt(_bitmap_dc, dest_x, 0, + end_x - start_x, get_ysize(), + _bitmap_dc, start_x, 0, + SRCCOPY); + + // Also shift the brush origin over, so we still get proper + // dithering. + _brush_origin += (dest_x - start_x); + SetBrushOrgEx(_bitmap_dc, _brush_origin, 0, NULL); + + RECT rect = { + _left_margin + dest_x, _top_margin, + _left_margin + end_x - start_x, _top_margin + get_ysize() + }; + InvalidateRect(_window, &rect, FALSE); +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsStripChart::draw_slice +// Access: Protected, Virtual +// Description: Draws a single vertical slice of the strip chart, at +// the given pixel position, and corresponding to the +// indicated level data. +//////////////////////////////////////////////////////////////////// +void WinStatsStripChart:: +draw_slice(int x, int frame_number) { + const FrameData &frame = get_frame_data(frame_number); + + // Start by clearing the band first. + RECT rect = { x, 0, x + 1, get_ysize() }; + FillRect(_bitmap_dc, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH)); + + float overall_time = 0.0; + int y = get_ysize(); + + FrameData::const_iterator fi; + for (fi = frame.begin(); fi != frame.end(); ++fi) { + const ColorData &cd = (*fi); + overall_time += cd._net_value; + HBRUSH brush = get_collector_brush(cd._collector_index); + + if (overall_time > get_vertical_scale()) { + // Off the top. Go ahead and clamp it by hand, in case it's so + // far off the top we'd overflow the 16-bit pixel value. + rect.top = 0; + rect.bottom = y; + FillRect(_bitmap_dc, &rect, brush); + // And we can consider ourselves done now. + return; + } + + int top_y = height_to_pixel(overall_time); + rect.top = top_y; + rect.bottom = y; + FillRect(_bitmap_dc, &rect, brush); + y = top_y; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsStripChart::draw_empty +// Access: Protected, Virtual +// Description: Draws a single vertical slice of background color. +//////////////////////////////////////////////////////////////////// +void WinStatsStripChart:: +draw_empty(int x) { + RECT rect = { x, 0, x + 1, get_ysize() }; + FillRect(_bitmap_dc, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH)); +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsStripChart::draw_cursor +// Access: Protected, Virtual +// Description: Draws a single vertical slice of foreground color. +//////////////////////////////////////////////////////////////////// +void WinStatsStripChart:: +draw_cursor(int x) { + RECT rect = { x, 0, x + 1, get_ysize() }; + FillRect(_bitmap_dc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH)); +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsStripChart::end_draw +// Access: Protected, Virtual +// Description: Should be overridden by the user class. This hook +// will be called after drawing a series of color bars +// in the strip chart; it gives the pixel range that +// was just redrawn. +//////////////////////////////////////////////////////////////////// +void WinStatsStripChart:: +end_draw(int from_x, int to_x) { + RECT rect = { from_x, 0, to_x + 1, get_ysize() }; + InvalidateRect(_window, &rect, FALSE); +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsStripChart::create_window +// Access: Private +// Description: Creates the window for this strip chart. +//////////////////////////////////////////////////////////////////// +void WinStatsStripChart:: +create_window() { + if (_window) { + return; + } + + HINSTANCE application = GetModuleHandle(NULL); + register_window_class(application); + + string window_title = get_title_text(); + DWORD window_style = WS_CHILD | WS_OVERLAPPEDWINDOW; + + RECT win_rect = { + 0, 0, + _left_margin + get_xsize() + _right_margin, + _top_margin + get_ysize() + _bottom_margin + }; + + // compute window size based on desired client area size + AdjustWindowRect(&win_rect, window_style, FALSE); + + _window = + CreateWindow(_window_class_name, window_title.c_str(), window_style, + CW_USEDEFAULT, 0, + win_rect.right - win_rect.left, + win_rect.bottom - win_rect.top, + WinStatsGraph::_monitor->get_window(), NULL, application, 0); + if (!_window) { + nout << "Could not create StripChart window!\n"; + exit(1); + } + + SetWindowLongPtr(_window, 0, (LONG_PTR)this); + ShowWindow(_window, SW_SHOWNORMAL); +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsStripChart::register_window_class +// Access: Private, Static +// Description: Registers the window class for the stripChart window, if +// it has not already been registered. +//////////////////////////////////////////////////////////////////// +void WinStatsStripChart:: +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(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)COLOR_BACKGROUND; + wc.lpszMenuName = NULL; + wc.lpszClassName = _window_class_name; + + // Reserve space to associate the this pointer with the window. + wc.cbWndExtra = sizeof(WinStatsStripChart *); + + if (!RegisterClass(&wc)) { + nout << "Could not register StripChart window class!\n"; + exit(1); + } + + _window_class_registered = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsStripChart::static_window_proc +// Access: Private, Static +// Description: +//////////////////////////////////////////////////////////////////// +LONG WINAPI WinStatsStripChart:: +static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + WinStatsStripChart *self = (WinStatsStripChart *)GetWindowLongPtr(hwnd, 0); + if (self != (WinStatsStripChart *)NULL && self->_window == hwnd) { + return self->window_proc(hwnd, msg, wparam, lparam); + } else { + return DefWindowProc(hwnd, msg, wparam, lparam); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsStripChart::window_proc +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +LONG WinStatsStripChart:: +window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + switch (msg) { + case WM_DESTROY: + close(); + break; + + case WM_DISPLAYCHANGE: + setup_bitmap(get_xsize(), get_ysize()); + force_redraw(); + break; + + case WM_SIZE: + changed_size(LOWORD(lparam) - (_left_margin + _right_margin), + HIWORD(lparam) - (_top_margin + _bottom_margin)); + setup_bitmap(get_xsize(), get_ysize()); + force_redraw(); + InvalidateRect(hwnd, NULL, FALSE); + break; + + case WM_PAINT: + { + // Repaint the graph by copying the backing pixmap in. + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hwnd, &ps); + draw_graph(hdc); + EndPaint(hwnd, &ps); + return 0; + } + + default: + break; + } + + return DefWindowProc(hwnd, msg, wparam, lparam); +} diff --git a/pandatool/src/win-stats/winStatsStripChart.h b/pandatool/src/win-stats/winStatsStripChart.h new file mode 100644 index 0000000000..e86e0b8cb2 --- /dev/null +++ b/pandatool/src/win-stats/winStatsStripChart.h @@ -0,0 +1,67 @@ +// Filename: winStatsStripChart.h +// Created by: drose (03Dec03) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef WINSTATSSTRIPCHART_H +#define WINSTATSSTRIPCHART_H + +#include "pandatoolbase.h" + +#include "winStatsGraph.h" +#include "pStatStripChart.h" +#include "pointerTo.h" + +#include + +class WinStatsMonitor; + +//////////////////////////////////////////////////////////////////// +// Class : WinStatsStripChart +// Description : +//////////////////////////////////////////////////////////////////// +class WinStatsStripChart : public PStatStripChart, public WinStatsGraph { +public: + WinStatsStripChart(WinStatsMonitor *monitor, + PStatView &view, int collector_index); + virtual ~WinStatsStripChart(); + + virtual void new_collector(int collector_index); + virtual void new_data(int thread_index, int frame_number); + +protected: + virtual void clear_region(); + virtual void copy_region(int start_x, int end_x, int dest_x); + virtual void draw_slice(int x, int frame_number); + virtual void draw_empty(int x); + virtual void draw_cursor(int x); + virtual void end_draw(int from_x, int to_x); + +private: + void create_window(); + static void register_window_class(HINSTANCE application); + + static LONG WINAPI static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); + LONG WINAPI window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); + + int _brush_origin; + + static bool _window_class_registered; + static const char * const _window_class_name; +}; + +#endif +