diff --git a/pandatool/src/pstatserver/pStatClientData.cxx b/pandatool/src/pstatserver/pStatClientData.cxx index cf40a54894..06c8ff2070 100644 --- a/pandatool/src/pstatserver/pStatClientData.cxx +++ b/pandatool/src/pstatserver/pStatClientData.cxx @@ -156,12 +156,31 @@ get_collector_fullname(int index) const { // Description: Indicates whether the given collector has level data // (and consequently, whether it should appear on the // Levels menu). +// +// The return value is true if anything changed, false +// otherwise. //////////////////////////////////////////////////////////////////// -void PStatClientData:: +bool PStatClientData:: set_collector_has_level(int index, bool flag) { + bool any_changed = false; slot_collector(index); - nassertv(index >= 0 && index < (int)_collectors.size()); - _collectors[index]._is_level = flag; + nassertr(index >= 0 && index < (int)_collectors.size(), false); + + if (_collectors[index]._is_level != flag) { + any_changed = true; + _collectors[index]._is_level = flag; + + // Turning this on for a given collector also implicitly turns all + // of its ancestors. + if (flag) { + PStatCollectorDef *def = _collectors[index]._def; + if (def->_parent_index != 0) { + set_collector_has_level(def->_parent_index, flag); + } + } + } + + return any_changed; } @@ -178,6 +197,32 @@ get_collector_has_level(int index) const { _collectors[index]._is_level); } +//////////////////////////////////////////////////////////////////// +// Function: PStatClientData::get_num_toplevel_collectors +// Access: Public +// Description: Returns the total number of collectors that are +// toplevel collectors. These are the collectors that +// are the children of "Frame", which is collector 0. +//////////////////////////////////////////////////////////////////// +int PStatClientData:: +get_num_toplevel_collectors() const { + return _toplevel_collectors.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClientData::get_toplevel_collector +// Access: Public +// Description: Returns the collector index of the nth toplevel +// collector. Use this function to iterate through the +// n toplevel collectors indicated by +// get_num_toplevel_collectors(). +//////////////////////////////////////////////////////////////////// +int PStatClientData:: +get_toplevel_collector(int n) const { + nassertr(n >= 0 && n < (int)_toplevel_collectors.size(), 0); + return _toplevel_collectors[n]; +} + //////////////////////////////////////////////////////////////////// // Function: PStatClientData::get_num_threads // Access: Public @@ -278,6 +323,7 @@ add_collector(PStatCollectorDef *def) { } _collectors[def->_index]._def = def; + update_toplevel_collectors(); } //////////////////////////////////////////////////////////////////// @@ -343,3 +389,21 @@ slot_collector(int collector_index) { _collectors.push_back(collector); } } + +//////////////////////////////////////////////////////////////////// +// Function: PStatClientData::update_toplevel_collectors +// Access: Private +// Description: Rebuilds the list of toplevel collectors. +//////////////////////////////////////////////////////////////////// +void PStatClientData:: +update_toplevel_collectors() { + _toplevel_collectors.clear(); + + Collectors::const_iterator ci; + for (ci = _collectors.begin(); ci != _collectors.end(); ++ci) { + PStatCollectorDef *def = (*ci)._def; + if (def->_parent_index == 0) { + _toplevel_collectors.push_back(def->_index); + } + } +} diff --git a/pandatool/src/pstatserver/pStatClientData.h b/pandatool/src/pstatserver/pStatClientData.h index 272d162440..b0889fcc49 100644 --- a/pandatool/src/pstatserver/pStatClientData.h +++ b/pandatool/src/pstatserver/pStatClientData.h @@ -50,9 +50,12 @@ public: const PStatCollectorDef &get_collector_def(int index) const; string get_collector_name(int index) const; string get_collector_fullname(int index) const; - void set_collector_has_level(int index, bool flag); + bool set_collector_has_level(int index, bool flag); bool get_collector_has_level(int index) const; + int get_num_toplevel_collectors() const; + int get_toplevel_collector(int index) const; + int get_num_threads() const; bool has_thread(int index) const; string get_thread_name(int index) const; @@ -68,7 +71,7 @@ public: PStatFrameData *frame_data); private: void slot_collector(int collector_index); - + void update_toplevel_collectors(); private: bool _is_alive; @@ -83,6 +86,9 @@ private: typedef pvector Collectors; Collectors _collectors; + typedef pvector ToplevelCollectors; + ToplevelCollectors _toplevel_collectors; + class Thread { public: string _name; diff --git a/pandatool/src/win-stats/Sources.pp b/pandatool/src/win-stats/Sources.pp index 3726b6ddca..22baa0149f 100644 --- a/pandatool/src/win-stats/Sources.pp +++ b/pandatool/src/win-stats/Sources.pp @@ -12,6 +12,7 @@ #define SOURCES \ winStats.cxx \ + winStatsChartMenu.cxx winStatsChartMenu.h \ winStatsGraph.cxx winStatsGraph.h \ winStatsLabel.cxx winStatsLabel.h \ winStatsLabelStack.cxx winStatsLabelStack.h \ diff --git a/pandatool/src/win-stats/winStatsChartMenu.cxx b/pandatool/src/win-stats/winStatsChartMenu.cxx new file mode 100755 index 0000000000..89abeb2f81 --- /dev/null +++ b/pandatool/src/win-stats/winStatsChartMenu.cxx @@ -0,0 +1,185 @@ +// Filename: winStatsChartMenu.cxx +// Created by: drose (08Jan04) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "winStatsChartMenu.h" +#include "pStatMonitor.h" + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsChartMenu::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +WinStatsChartMenu:: +WinStatsChartMenu(PStatMonitor *monitor, int thread_index) : + _monitor(monitor), + _thread_index(thread_index) +{ + _menu = CreatePopupMenu(); + do_update(); +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsChartMenu::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +WinStatsChartMenu:: +~WinStatsChartMenu() { +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsChartMenu::get_menu_handle +// Access: Public +// Description: Returns the Windows menu handle for this particular +// menu. +//////////////////////////////////////////////////////////////////// +HMENU WinStatsChartMenu:: +get_menu_handle() { + return _menu; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsChartMenu::add_to_menu_bar +// Access: Public +// Description: Adds the menu to the end of the indicated menu bar. +//////////////////////////////////////////////////////////////////// +void WinStatsChartMenu:: +add_to_menu_bar(HMENU menu_bar) { + const PStatClientData *client_data = _monitor->get_client_data(); + string thread_name = client_data->get_thread_name(_thread_index); + + MENUITEMINFO mii; + memset(&mii, 0, sizeof(mii)); + mii.cbSize = sizeof(mii); + + mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_SUBMENU; + mii.fType = MFT_STRING; + mii.hSubMenu = _menu; + mii.dwTypeData = (char *)thread_name.c_str(); + InsertMenuItem(menu_bar, GetMenuItemCount(menu_bar), TRUE, &mii); +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsChartMenu::check_update +// Access: Public +// Description: Checks to see if the menu needs to be updated +// (e.g. because of new data from the client), and +// updates it if necessary. +//////////////////////////////////////////////////////////////////// +void WinStatsChartMenu:: +check_update() { + PStatView &view = _monitor->get_view(_thread_index); + if (view.get_level_index() != _last_level_index) { + do_update(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsChartMenu::do_update +// Access: Public +// Description: Unconditionally updates the menu with the latest data +// from the client. +//////////////////////////////////////////////////////////////////// +void WinStatsChartMenu:: +do_update() { + PStatView &view = _monitor->get_view(_thread_index); + _last_level_index = view.get_level_index(); + + // First, remove all of the old entries from the menu. + int num_items = GetMenuItemCount(_menu); + for (int i = num_items - 1; i >= 0; i--) { + DeleteMenu(_menu, i, MF_BYPOSITION); + } + + // Now rebuild the menu with the new set of entries. + + // The menu item(s) for the thread's frame time goes first. + add_view(_menu, view.get_top_level()); + + bool needs_separator = true; + + // And then the menu item(s) for each of the level values. + const PStatClientData *client_data = _monitor->get_client_data(); + int num_toplevel_collectors = client_data->get_num_toplevel_collectors(); + for (int tc = 0; tc < num_toplevel_collectors; tc++) { + int collector = client_data->get_toplevel_collector(tc); + if (client_data->has_collector(collector) && + client_data->get_collector_has_level(collector)) { + + // We put a separator between the above frame collector and the + // first level collector. + if (needs_separator) { + MENUITEMINFO mii; + memset(&mii, 0, sizeof(mii)); + mii.cbSize = sizeof(mii); + mii.fMask = MIIM_FTYPE; + mii.fType = MFT_SEPARATOR; + InsertMenuItem(_menu, GetMenuItemCount(_menu), TRUE, &mii); + + needs_separator = false; + } + + PStatView &level_view = _monitor->get_level_view(collector, _thread_index); + add_view(_menu, level_view.get_top_level()); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsChartMenu::add_view +// Access: Private +// Description: Adds a new entry or entries to the menu for the +// indicated view and its children. +//////////////////////////////////////////////////////////////////// +void WinStatsChartMenu:: +add_view(HMENU parent_menu, const PStatViewLevel *view_level) { + int collector = view_level->get_collector(); + + const PStatClientData *client_data = _monitor->get_client_data(); + string collector_name = client_data->get_collector_name(collector); + + MENUITEMINFO mii; + memset(&mii, 0, sizeof(mii)); + mii.cbSize = sizeof(mii); + + mii.fMask = MIIM_STRING | MIIM_FTYPE; + mii.fType = MFT_STRING; + mii.dwTypeData = (char *)collector_name.c_str(); + InsertMenuItem(parent_menu, GetMenuItemCount(parent_menu), TRUE, &mii); + + int num_children = view_level->get_num_children(); + if (num_children != 0) { + // If the collector has any children, add a menu entry to go + // directly to each of its children. + HMENU submenu = CreatePopupMenu(); + string submenu_name = collector_name + " components"; + + mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_SUBMENU; + mii.fType = MFT_STRING; + mii.hSubMenu = submenu; + mii.dwTypeData = (char *)submenu_name.c_str(); + InsertMenuItem(parent_menu, GetMenuItemCount(parent_menu), TRUE, &mii); + + // Reverse the order since the menus are listed from the top down; + // we want to be visually consistent with the graphs, which list + // these labels from the bottom up. + for (int c = num_children - 1; c >= 0; c--) { + add_view(submenu, view_level->get_child(c)); + } + } +} diff --git a/pandatool/src/win-stats/winStatsChartMenu.h b/pandatool/src/win-stats/winStatsChartMenu.h new file mode 100755 index 0000000000..daf8b0741f --- /dev/null +++ b/pandatool/src/win-stats/winStatsChartMenu.h @@ -0,0 +1,57 @@ +// Filename: winStatsChartMenu.h +// Created by: drose (08Jan04) +// +//////////////////////////////////////////////////////////////////// +// +// 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 WINSTATSCHARTMENU_H +#define WINSTATSCHARTMENU_H + +#include "pandatoolbase.h" + +#include + +class PStatMonitor; +class PStatView; +class PStatViewLevel; + +//////////////////////////////////////////////////////////////////// +// Class : WinStatsChartMenu +// Description : A pulldown menu of charts available for a particular +// thread. +//////////////////////////////////////////////////////////////////// +class WinStatsChartMenu { +public: + WinStatsChartMenu(PStatMonitor *monitor, int thread_index); + ~WinStatsChartMenu(); + + HMENU get_menu_handle(); + void add_to_menu_bar(HMENU menu_bar); + + void check_update(); + void do_update(); + +private: + void add_view(HMENU parent_menu, const PStatViewLevel *view_level); + + PStatMonitor *_monitor; + int _thread_index; + + int _last_level_index; + HMENU _menu; +}; + +#endif + diff --git a/pandatool/src/win-stats/winStatsLabel.cxx b/pandatool/src/win-stats/winStatsLabel.cxx new file mode 100755 index 0000000000..e42cd7e884 --- /dev/null +++ b/pandatool/src/win-stats/winStatsLabel.cxx @@ -0,0 +1,284 @@ +// Filename: winStatsLabel.cxx +// Created by: drose (07Jan04) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "winStatsLabel.h" +#include "winStatsMonitor.h" + +int WinStatsLabel::_left_margin = 2; +int WinStatsLabel::_right_margin = 2; +int WinStatsLabel::_top_margin = 2; +int WinStatsLabel::_bottom_margin = 2; + +bool WinStatsLabel::_window_class_registered = false; +const char * const WinStatsLabel::_window_class_name = "label"; + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabel::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +WinStatsLabel:: +WinStatsLabel(WinStatsMonitor *monitor, int collector_index) : + _collector_index(collector_index) +{ + _window = 0; + _text = monitor->get_client_data()->get_collector_name(_collector_index); + + 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); + _bg_color = RGB(r, g, b); + _bg_brush = CreateSolidBrush(RGB(r, g, b)); + + // Should our foreground be black or white? + float bright = + rgb[0] * 0.299 + + rgb[1] * 0.587 + + rgb[2] * 0.114; + + if (bright >= 0.5) { + _fg_color = RGB(0, 0, 0); + } else { + _fg_color = RGB(255, 255, 255); + } + + _x = 0; + _y = 0; + _width = 0; + _height = 0; + _ideal_width = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabel::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +WinStatsLabel:: +~WinStatsLabel() { + if (_window) { + DestroyWindow(_window); + _window = 0; + } + DeleteObject(_bg_brush); +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabel::setup +// Access: Public +// Description: Creates the actual window. +//////////////////////////////////////////////////////////////////// +void WinStatsLabel:: +setup(HWND parent_window) { + if (_window) { + DestroyWindow(_window); + _window = 0; + } + + create_window(parent_window); + + HDC hdc = GetDC(_window); + HGDIOBJ hfnt = GetStockObject(ANSI_VAR_FONT); + SelectObject(hdc, hfnt); + + SIZE size; + GetTextExtentPoint32(hdc, _text.data(), _text.length(), &size); + _height = size.cy + _top_margin + _bottom_margin; + _ideal_width = size.cx + _left_margin + _right_margin; + + ReleaseDC(_window, hdc); +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabel::set_pos +// Access: Public +// Description: Sets the position of the label on its parent. The +// position describes the lower-left corner of the +// rectangle, not the upper-left. +//////////////////////////////////////////////////////////////////// +void WinStatsLabel:: +set_pos(int x, int y, int width) { + _x = x; + _y = y; + _width = width; + SetWindowPos(_window, 0, x, y - _height, _width, _height, + SWP_NOZORDER | SWP_SHOWWINDOW); +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabel::get_x +// Access: Public +// Description: Returns the x position of the label on its parent. +//////////////////////////////////////////////////////////////////// +int WinStatsLabel:: +get_x() const { + return _x; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabel::get_y +// Access: Public +// Description: Returns the y position of the label on its parent. +//////////////////////////////////////////////////////////////////// +int WinStatsLabel:: +get_y() const { + return _y; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabel::get_width +// Access: Public +// Description: Returns the width of the label as we requested it. +//////////////////////////////////////////////////////////////////// +int WinStatsLabel:: +get_width() const { + return _width; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabel::get_height +// Access: Public +// Description: Returns the height of the label as we requested it. +//////////////////////////////////////////////////////////////////// +int WinStatsLabel:: +get_height() const { + return _height; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabel::get_ideal_width +// Access: Public +// Description: Returns the width the label would really prefer to be. +//////////////////////////////////////////////////////////////////// +int WinStatsLabel:: +get_ideal_width() const { + return _ideal_width; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabel::create_window +// Access: Private +// Description: Creates the window for this label. +//////////////////////////////////////////////////////////////////// +void WinStatsLabel:: +create_window(HWND parent_window) { + if (_window) { + return; + } + + HINSTANCE application = GetModuleHandle(NULL); + register_window_class(application); + + _window = + CreateWindow(_window_class_name, _text.c_str(), WS_CHILD, + 0, 0, 0, 0, + parent_window, NULL, application, 0); + if (!_window) { + nout << "Could not create Label window!\n"; + exit(1); + } + + SetWindowLongPtr(_window, 0, (LONG_PTR)this); +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabel::register_window_class +// Access: Private, Static +// Description: Registers the window class for the label window, if +// it has not already been registered. +//////////////////////////////////////////////////////////////////// +void WinStatsLabel:: +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 = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = _window_class_name; + + // Reserve space to associate the this pointer with the window. + wc.cbWndExtra = sizeof(WinStatsLabel *); + + if (!RegisterClass(&wc)) { + nout << "Could not register Label window class!\n"; + exit(1); + } + + _window_class_registered = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabel::static_window_proc +// Access: Private, Static +// Description: +//////////////////////////////////////////////////////////////////// +LONG WINAPI WinStatsLabel:: +static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + WinStatsLabel *self = (WinStatsLabel *)GetWindowLongPtr(hwnd, 0); + if (self != (WinStatsLabel *)NULL && self->_window == hwnd) { + return self->window_proc(hwnd, msg, wparam, lparam); + } else { + return DefWindowProc(hwnd, msg, wparam, lparam); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabel::window_proc +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +LONG WinStatsLabel:: +window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + switch (msg) { + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hwnd, &ps); + + RECT rect = { 0, 0, _width, _height }; + FillRect(hdc, &rect, _bg_brush); + + HGDIOBJ hfnt = GetStockObject(ANSI_VAR_FONT); + SelectObject(hdc, hfnt); + SetTextAlign(hdc, TA_RIGHT | TA_TOP); + + SetBkColor(hdc, _bg_color); + SetBkMode(hdc, OPAQUE); + SetTextColor(hdc, _fg_color); + + TextOut(hdc, _width - _right_margin, _top_margin, + _text.data(), _text.length()); + EndPaint(hwnd, &ps); + return 0; + } + + default: + break; + } + + return DefWindowProc(hwnd, msg, wparam, lparam); +} diff --git a/pandatool/src/win-stats/winStatsLabel.h b/pandatool/src/win-stats/winStatsLabel.h new file mode 100755 index 0000000000..40c0ec455c --- /dev/null +++ b/pandatool/src/win-stats/winStatsLabel.h @@ -0,0 +1,77 @@ +// Filename: winStatsLabel.h +// Created by: drose (07Jan04) +// +//////////////////////////////////////////////////////////////////// +// +// 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 WINSTATSLABEL_H +#define WINSTATSLABEL_H + +#include "pandatoolbase.h" + +#include + +class WinStatsMonitor; + +//////////////////////////////////////////////////////////////////// +// Class : WinStatsLabel +// Description : A text label that will draw in color appropriate for +// a particular collector. It also responds when the +// user double-clicks on it. This is handy for putting +// colored labels on strip charts. +//////////////////////////////////////////////////////////////////// +class WinStatsLabel { +public: + WinStatsLabel(WinStatsMonitor *monitor, int collector_index); + ~WinStatsLabel(); + + void setup(HWND parent_window); + void set_pos(int x, int y, int width); + + int get_x() const; + int get_y() const; + int get_width() const; + int get_height() const; + int get_ideal_width() const; + +private: + void create_window(HWND parent_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 _collector_index; + string _text; + HWND _window; + COLORREF _bg_color; + COLORREF _fg_color; + HBRUSH _bg_brush; + + int _x; + int _y; + int _width; + int _height; + int _ideal_width; + + static int _left_margin, _right_margin; + static int _top_margin, _bottom_margin; + + static bool _window_class_registered; + static const char * const _window_class_name; +}; + +#endif + diff --git a/pandatool/src/win-stats/winStatsLabelStack.cxx b/pandatool/src/win-stats/winStatsLabelStack.cxx new file mode 100755 index 0000000000..daa057b196 --- /dev/null +++ b/pandatool/src/win-stats/winStatsLabelStack.cxx @@ -0,0 +1,282 @@ +// Filename: winStatsLabelStack.cxx +// Created by: drose (07Jan04) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "winStatsLabelStack.h" +#include "winStatsLabel.h" + +bool WinStatsLabelStack::_window_class_registered = false; +const char * const WinStatsLabelStack::_window_class_name = "stack"; + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabelStack::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +WinStatsLabelStack:: +WinStatsLabelStack() { + _window = 0; + + _x = 0; + _y = 0; + _width = 0; + _height = 0; + _ideal_width = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabelStack::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +WinStatsLabelStack:: +~WinStatsLabelStack() { + clear_labels(); + if (_window) { + DestroyWindow(_window); + _window = 0; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabelStack::setup +// Access: Public +// Description: Creates the actual window object. +//////////////////////////////////////////////////////////////////// +void WinStatsLabelStack:: +setup(HWND parent_window) { + if (_window) { + DestroyWindow(_window); + _window = 0; + } + + create_window(parent_window); + + _ideal_width = 0; + Labels::iterator li; + for (li = _labels.begin(); li != _labels.end(); ++li) { + WinStatsLabel *label = (*li); + label->setup(_window); + _ideal_width = max(_ideal_width, label->get_ideal_width()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabelStack::is_setup +// Access: Public +// Description: Returns true if the label stack has been set up, +// false otherwise. +//////////////////////////////////////////////////////////////////// +bool WinStatsLabelStack:: +is_setup() const { + return (_window != 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabelStack::set_pos +// Access: Public +// Description: Sets the position and size of the label stack on its parent. +//////////////////////////////////////////////////////////////////// +void WinStatsLabelStack:: +set_pos(int x, int y, int width, int height) { + _x = x; + _y = y; + _width = width; + _height = height; + SetWindowPos(_window, 0, x, y, _width, _height, + SWP_NOZORDER | SWP_SHOWWINDOW); + + Labels::iterator li; + int yp = height; + for (li = _labels.begin(); li != _labels.end(); ++li) { + WinStatsLabel *label = (*li); + label->set_pos(0, yp, _width); + yp -= label->get_height(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabelStack::get_x +// Access: Public +// Description: Returns the x position of the stack on its parent. +//////////////////////////////////////////////////////////////////// +int WinStatsLabelStack:: +get_x() const { + return _x; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabelStack::get_y +// Access: Public +// Description: Returns the y position of the stack on its parent. +//////////////////////////////////////////////////////////////////// +int WinStatsLabelStack:: +get_y() const { + return _y; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabelStack::get_width +// Access: Public +// Description: Returns the width of the stack as we requested it. +//////////////////////////////////////////////////////////////////// +int WinStatsLabelStack:: +get_width() const { + return _width; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabelStack::get_height +// Access: Public +// Description: Returns the height of the stack as we requested it. +//////////////////////////////////////////////////////////////////// +int WinStatsLabelStack:: +get_height() const { + return _height; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabelStack::get_ideal_width +// Access: Public +// Description: Returns the width the stack would really prefer to be. +//////////////////////////////////////////////////////////////////// +int WinStatsLabelStack:: +get_ideal_width() const { + return _ideal_width; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabelStack::clear_labels +// Access: Public +// Description: Removes the set of labels and starts a new set. +//////////////////////////////////////////////////////////////////// +void WinStatsLabelStack:: +clear_labels() { + Labels::iterator li; + for (li = _labels.begin(); li != _labels.end(); ++li) { + delete (*li); + } + _labels.clear(); + _ideal_width = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabelStack::add_label +// Access: Public +// Description: Adds a new label to the top of the stack. +//////////////////////////////////////////////////////////////////// +void WinStatsLabelStack:: +add_label(WinStatsMonitor *monitor, int collector_index) { + int yp = _height; + if (!_labels.empty()) { + WinStatsLabel *top_label = _labels.back(); + yp = top_label->get_y() - top_label->get_height(); + } + WinStatsLabel *label = new WinStatsLabel(monitor, collector_index); + if (_window) { + label->setup(_window); + label->set_pos(0, yp, _width); + } + _ideal_width = max(_ideal_width, label->get_ideal_width()); + _labels.push_back(label); +} + + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabelStack::create_window +// Access: Private +// Description: Creates the window for this stack. +//////////////////////////////////////////////////////////////////// +void WinStatsLabelStack:: +create_window(HWND parent_window) { + if (_window) { + return; + } + + HINSTANCE application = GetModuleHandle(NULL); + register_window_class(application); + + _window = + CreateWindow(_window_class_name, "label stack", WS_CHILD | WS_CLIPCHILDREN, + 0, 0, 0, 0, + parent_window, NULL, application, 0); + if (!_window) { + nout << "Could not create Label Stack window!\n"; + exit(1); + } + + SetWindowLongPtr(_window, 0, (LONG_PTR)this); +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabelStack::register_window_class +// Access: Private, Static +// Description: Registers the window class for the label window, if +// it has not already been registered. +//////////////////////////////////////////////////////////////////// +void WinStatsLabelStack:: +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(WinStatsLabelStack *); + + if (!RegisterClass(&wc)) { + nout << "Could not register Label Stack window class!\n"; + exit(1); + } + + _window_class_registered = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabelStack::static_window_proc +// Access: Private, Static +// Description: +//////////////////////////////////////////////////////////////////// +LONG WINAPI WinStatsLabelStack:: +static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + WinStatsLabelStack *self = (WinStatsLabelStack *)GetWindowLongPtr(hwnd, 0); + if (self != (WinStatsLabelStack *)NULL && self->_window == hwnd) { + return self->window_proc(hwnd, msg, wparam, lparam); + } else { + return DefWindowProc(hwnd, msg, wparam, lparam); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsLabelStack::window_proc +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +LONG WinStatsLabelStack:: +window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + return DefWindowProc(hwnd, msg, wparam, lparam); +} diff --git a/pandatool/src/win-stats/winStatsLabelStack.h b/pandatool/src/win-stats/winStatsLabelStack.h new file mode 100755 index 0000000000..fdf7f7c54c --- /dev/null +++ b/pandatool/src/win-stats/winStatsLabelStack.h @@ -0,0 +1,75 @@ +// Filename: winStatsLabelStack.h +// Created by: drose (07Jan04) +// +//////////////////////////////////////////////////////////////////// +// +// 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 WINSTATSLABELSTACK_H +#define WINSTATSLABELSTACK_H + +#include "pandatoolbase.h" +#include "pvector.h" + +#include + +class WinStatsLabel; +class WinStatsMonitor; + +//////////////////////////////////////////////////////////////////// +// Class : WinStatsLabelStack +// Description : A window that contains a stack of labels from bottom +// to top. +//////////////////////////////////////////////////////////////////// +class WinStatsLabelStack { +public: + WinStatsLabelStack(); + ~WinStatsLabelStack(); + + void setup(HWND parent_window); + bool is_setup() const; + void set_pos(int x, int y, int width, int height); + + int get_x() const; + int get_y() const; + int get_width() const; + int get_height() const; + int get_ideal_width() const; + + void clear_labels(); + void add_label(WinStatsMonitor *monitor, int collector_index); + +private: + void create_window(HWND parent_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); + + HWND _window; + int _x; + int _y; + int _width; + int _height; + int _ideal_width; + + typedef pvector Labels; + Labels _labels; + + static bool _window_class_registered; + static const char * const _window_class_name; +}; + +#endif + diff --git a/pandatool/src/win-stats/winStatsMonitor.cxx b/pandatool/src/win-stats/winStatsMonitor.cxx index db74566419..0d19a6e0d4 100644 --- a/pandatool/src/win-stats/winStatsMonitor.cxx +++ b/pandatool/src/win-stats/winStatsMonitor.cxx @@ -18,8 +18,10 @@ #include "winStatsMonitor.h" #include "winStatsStripChart.h" +#include "winStatsChartMenu.h" #include "pStatCollectorDef.h" +#include "indent.h" bool WinStatsMonitor::_window_class_registered = false; const char * const WinStatsMonitor::_window_class_name = "monitor"; @@ -32,6 +34,7 @@ const char * const WinStatsMonitor::_window_class_name = "monitor"; WinStatsMonitor:: WinStatsMonitor() { _window = 0; + _menu_bar = 0; } //////////////////////////////////////////////////////////////////// @@ -48,6 +51,12 @@ WinStatsMonitor:: } _graphs.clear(); + ChartMenus::iterator mi; + for (mi = _chart_menus.begin(); mi != _chart_menus.end(); ++mi) { + delete (*mi); + } + _chart_menus.clear(); + if (_window) { DestroyWindow(_window); _window = 0; @@ -135,6 +144,30 @@ new_collector(int collector_index) { WinStatsGraph *graph = (*gi); graph->new_collector(collector_index); } + + // We might need to update our menus. + ChartMenus::iterator mi; + for (mi = _chart_menus.begin(); mi != _chart_menus.end(); ++mi) { + (*mi)->do_update(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: WinStatsMonitor::new_thread +// Access: Public, Virtual +// Description: Called whenever a new Thread definition is +// received from the client. Generally, the client will +// send all of its threads 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 Thread +// definitions midstream. +//////////////////////////////////////////////////////////////////// +void WinStatsMonitor:: +new_thread(int thread_index) { + WinStatsChartMenu *chart_menu = new WinStatsChartMenu(this, thread_index); + chart_menu->add_to_menu_bar(_menu_bar); + _chart_menus.push_back(chart_menu); } //////////////////////////////////////////////////////////////////// @@ -184,6 +217,11 @@ lost_connection() { //////////////////////////////////////////////////////////////////// void WinStatsMonitor:: idle() { + // Check if any of our chart menus need updating. + ChartMenus::iterator mi; + for (mi = _chart_menus.begin(); mi != _chart_menus.end(); ++mi) { + (*mi)->check_update(); + } } //////////////////////////////////////////////////////////////////// @@ -246,6 +284,13 @@ create_window() { HINSTANCE application = GetModuleHandle(NULL); register_window_class(application); + _menu_bar = CreateMenu(); + + ChartMenus::iterator mi; + for (mi = _chart_menus.begin(); mi != _chart_menus.end(); ++mi) { + (*mi)->add_to_menu_bar(_menu_bar); + } + _window_title = get_client_progname() + " on " + get_client_hostname(); DWORD window_style = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE; @@ -253,7 +298,7 @@ create_window() { _window = CreateWindow(_window_class_name, _window_title.c_str(), window_style, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, - NULL, NULL, application, 0); + NULL, _menu_bar, application, 0); if (!_window) { nout << "Could not create monitor window!\n"; exit(1); diff --git a/pandatool/src/win-stats/winStatsMonitor.h b/pandatool/src/win-stats/winStatsMonitor.h index 13ca5359fd..220ad7de59 100644 --- a/pandatool/src/win-stats/winStatsMonitor.h +++ b/pandatool/src/win-stats/winStatsMonitor.h @@ -28,6 +28,8 @@ #include +class WinStatsChartMenu; + //////////////////////////////////////////////////////////////////// // Class : WinStatsMonitor // Description : This class represents a connection to a PStatsClient @@ -45,6 +47,7 @@ public: 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_thread(int thread_index); virtual void new_data(int thread_index, int frame_number); virtual void lost_connection(); virtual void idle(); @@ -65,7 +68,11 @@ private: typedef pset Graphs; Graphs _graphs; + typedef pvector ChartMenus; + ChartMenus _chart_menus; + HWND _window; + HMENU _menu_bar; string _window_title; static bool _window_class_registered; diff --git a/pandatool/src/win-stats/winStatsStripChart.cxx b/pandatool/src/win-stats/winStatsStripChart.cxx index e1e3f52b72..bb32653199 100644 --- a/pandatool/src/win-stats/winStatsStripChart.cxx +++ b/pandatool/src/win-stats/winStatsStripChart.cxx @@ -337,10 +337,12 @@ static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { //////////////////////////////////////////////////////////////////// LONG WinStatsStripChart:: window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + /* switch (msg) { default: break; } + */ return WinStatsGraph::window_proc(hwnd, msg, wparam, lparam); }