Browse Source

beginnings of win-stats

David Rose 22 years ago
parent
commit
0e366091e5

+ 1 - 0
pandatool/src/pstatserver/pStatPianoRoll.cxx

@@ -260,6 +260,7 @@ end_draw() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PStatPianoRoll::
 void PStatPianoRoll::
 idle() {
 idle() {
+  update();
 }
 }
 
 
 
 

+ 54 - 0
pandatool/src/pstatserver/pStatStripChart.cxx

@@ -50,6 +50,7 @@ PStatStripChart(PStatMonitor *monitor, PStatView &view,
   _start_time = 0.0;
   _start_time = 0.0;
 
 
   _level_index = 0;
   _level_index = 0;
+  _title_unknown = true;
 
 
   const PStatClientData *client_data = _monitor->get_client_data();
   const PStatClientData *client_data = _monitor->get_client_data();
   if (client_data->has_collector(_collector_index)) {
   if (client_data->has_collector(_collector_index)) {
@@ -236,6 +237,59 @@ get_collector_under_pixel(int xpoint, int ypoint) {
   return -1;
   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
 //     Function: PStatStripChart::get_frame_data
 //       Access: Protected
 //       Access: Protected

+ 4 - 0
pandatool/src/pstatserver/pStatStripChart.h

@@ -72,6 +72,9 @@ public:
   INLINE int height_to_pixel(float value) const;
   INLINE int height_to_pixel(float value) const;
   INLINE float pixel_to_height(int y) const;
   INLINE float pixel_to_height(int y) const;
 
 
+  string get_title_text();
+  bool is_title_unknown() const;
+
 protected:
 protected:
   class ColorData {
   class ColorData {
   public:
   public:
@@ -117,6 +120,7 @@ private:
   float _time_width;
   float _time_width;
   float _start_time;
   float _start_time;
   float _value_height;
   float _value_height;
+  bool _title_unknown;
 };
 };
 
 
 #include "pStatStripChart.I"
 #include "pStatStripChart.I"

+ 23 - 0
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
+

+ 126 - 0
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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "pandatoolbase.h"
+
+#include "winStatsServer.h"
+#include "config_pstats.h"
+
+#include <windows.h>
+
+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);
+}

+ 25 - 0
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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef WINSTATS_H
+#define WINSTATS_H
+
+#include "pStatServer.h"
+
+#endif
+

+ 209 - 0
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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#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);
+}

+ 71 - 0
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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef WINSTATSGRAPH_H
+#define WINSTATSGRAPH_H
+
+#include "pandatoolbase.h"
+#include "pmap.h"
+
+#include <windows.h>
+
+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<int, HBRUSH> 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
+

+ 333 - 0
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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#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);
+}

+ 77 - 0
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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef WINSTATSMONITOR_H
+#define WINSTATSMONITOR_H
+
+#include "pandatoolbase.h"
+
+#include "winStatsGraph.h"
+#include "pStatMonitor.h"
+#include "pointerTo.h"
+#include "pset.h"
+
+#include <windows.h>
+
+////////////////////////////////////////////////////////////////////
+//       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<WinStatsGraph *> Graphs;
+  Graphs _graphs;
+
+  HWND _window;
+  string _window_title;
+
+  static bool _window_class_registered;
+  static const char * const _window_class_name;
+
+  friend class WinStatsGraph;
+};
+
+#endif

+ 30 - 0
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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "winStatsServer.h"
+#include "winStatsMonitor.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinStatsServer::make_monitor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+PStatMonitor *WinStatsServer::
+make_monitor() {
+  return new WinStatsMonitor;
+}

+ 36 - 0
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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#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
+

+ 336 - 0
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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#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);
+}

+ 67 - 0
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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef WINSTATSSTRIPCHART_H
+#define WINSTATSSTRIPCHART_H
+
+#include "pandatoolbase.h"
+
+#include "winStatsGraph.h"
+#include "pStatStripChart.h"
+#include "pointerTo.h"
+
+#include <windows.h>
+
+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
+