2023-11-29 18:47:11 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "UbaVisualizer.h"
2024-07-10 18:24:10 -04:00
# include "UbaConfig.h"
2023-11-29 18:47:11 -05:00
# include <algorithm>
# include <uxtheme.h>
# include <dwmapi.h>
2024-08-06 15:31:53 -04:00
# include <shellscalingapi.h>
2024-07-10 18:24:10 -04:00
2024-08-06 15:31:53 -04:00
# pragma comment (lib, "Shcore.lib")
2023-11-29 18:47:11 -05:00
# pragma comment (lib, "UxTheme.lib")
# pragma comment (lib, "Dwmapi.lib")
# define WM_NEWTRACE WM_USER+1
2024-08-25 02:00:53 -04:00
# define WM_SETTITLE WM_USER+2
2023-11-29 18:47:11 -05:00
namespace uba
{
2024-07-10 18:24:10 -04:00
enum
{
Popup_CopySessionInfo = 3 ,
Popup_CopyProcessInfo ,
Popup_CopyProcessLog ,
Popup_Replay ,
Popup_Pause ,
Popup_Play ,
Popup_JumpToEnd ,
# define UBA_VISUALIZER_FLAG(name, defaultValue, desc) Popup_##name,
UBA_VISUALIZER_FLAGS2
# undef UBA_VISUALIZER_FLAG
2024-07-15 17:13:18 -04:00
Popup_IncreaseFontSize ,
Popup_DecreaseFontSize ,
2024-07-10 18:24:10 -04:00
Popup_SaveAs ,
Popup_SaveSettings ,
2024-07-15 17:13:18 -04:00
Popup_OpenSettings ,
2024-07-10 18:24:10 -04:00
Popup_Quit ,
} ;
VisualizerConfig : : VisualizerConfig ( const tchar * fn ) : filename ( fn )
{
2024-07-15 17:13:18 -04:00
fontName = TC ( " Arial " ) ;
2024-07-10 18:24:10 -04:00
}
bool VisualizerConfig : : Load ( Logger & logger )
{
Config config ;
if ( ! config . LoadFromFile ( logger , filename . c_str ( ) ) )
{
DWORD value = 1 ;
DWORD valueSize = sizeof ( value ) ;
if ( RegGetValueW ( HKEY_CURRENT_USER , L " Software \\ Microsoft \\ Windows \\ CurrentVersion \\ Themes \\ Personalize " , L " AppsUseLightTheme " , RRF_RT_REG_DWORD , NULL , & value , & valueSize ) = = ERROR_SUCCESS )
DarkMode = value = = 0 ;
return false ;
}
config . GetValueAsInt ( x , L " X " ) ;
config . GetValueAsInt ( y , L " Y " ) ;
2024-07-15 17:13:18 -04:00
config . GetValueAsU32 ( width , L " Width " ) ;
config . GetValueAsU32 ( height , L " Height " ) ;
config . GetValueAsU32 ( fontSize , L " FontSize " ) ;
config . GetValueAsString ( fontName , L " FontName " ) ;
config . GetValueAsU32 ( maxActiveVisible , L " MaxActiveVisible " ) ;
2024-08-06 15:31:53 -04:00
config . GetValueAsU32 ( maxActiveProcessHeight , L " MaxActiveProcessHeight " ) ;
2024-07-10 18:24:10 -04:00
# define UBA_VISUALIZER_FLAG(name, defaultValue, desc) config.GetValueAsBool(show##name, L"Show" #name);
UBA_VISUALIZER_FLAGS1
# undef UBA_VISUALIZER_FLAG
# define UBA_VISUALIZER_FLAG(name, defaultValue, desc) config.GetValueAsBool(name, TC(#name));
UBA_VISUALIZER_FLAGS2
# undef UBA_VISUALIZER_FLAG
return true ;
}
bool VisualizerConfig : : Save ( Logger & logger )
{
Config config ;
config . AddValue ( L " X " , x ) ;
config . AddValue ( L " Y " , y ) ;
config . AddValue ( L " Width " , width ) ;
config . AddValue ( L " Height " , height ) ;
2024-07-15 17:13:18 -04:00
config . AddValue ( L " FontSize " , fontSize ) ;
config . AddValue ( L " FontName " , fontName . c_str ( ) ) ;
config . AddValue ( L " MaxActiveVisible " , maxActiveVisible ) ;
2024-08-06 15:31:53 -04:00
config . AddValue ( L " MaxActiveProcessHeight " , maxActiveProcessHeight ) ;
2024-07-10 18:24:10 -04:00
# define UBA_VISUALIZER_FLAG(name, defaultValue, desc) config.AddValue(L"Show" #name, show##name);
UBA_VISUALIZER_FLAGS1
# undef UBA_VISUALIZER_FLAG
# define UBA_VISUALIZER_FLAG(name, defaultValue, desc) config.AddValue(TC(#name), name);
UBA_VISUALIZER_FLAGS2
# undef UBA_VISUALIZER_FLAG
return config . SaveToFile ( logger , filename . c_str ( ) ) ;
}
enum VisualizerFlag
{
# define UBA_VISUALIZER_FLAG(name, defaultValue, desc) VisualizerFlag_##name,
UBA_VISUALIZER_FLAGS1
# undef UBA_VISUALIZER_FLAG
VisualizerFlag_Count
} ;
2023-11-29 18:47:11 -05:00
class DrawTextLogger : public Logger
{
public :
2024-04-12 01:05:04 -04:00
DrawTextLogger ( HWND hw , HDC h , int fh , HBRUSH bb )
: hwnd ( hw )
, hdc ( h )
, fontHeight ( fh )
, backgroundBrush ( bb )
{
textColor = GetTextColor ( hdc ) ;
}
2023-11-29 18:47:11 -05:00
virtual void BeginScope ( ) override { }
virtual void EndScope ( ) override { }
virtual void Log ( LogEntryType type , const wchar_t * str , u32 strLen ) override
{
2024-04-12 01:05:04 -04:00
RECT textRect { 0 , 0 , 0 , 0 } ;
DrawTextW ( hdc , str , strLen , & textRect , DT_CALCRECT ) ;
lines . emplace_back ( TString ( str , strLen ) , textOffset , height , textColor ) ;
width = Max ( width , int ( textRect . right + textOffset ) ) ;
height + = fontHeight ;
2023-11-29 18:47:11 -05:00
}
2024-04-12 01:05:04 -04:00
void AddSpace ( int space = 5 )
{
height + = space ;
}
2023-11-29 18:47:11 -05:00
2024-04-12 01:05:04 -04:00
void AddTextOffset ( int offset )
{
textOffset + = offset ;
}
void AddWidth ( int extra )
{
extraWidth + = extra ;
}
void DrawAtPos ( int x , int y )
{
RECT r ;
r . left = x ;
r . top = y ;
r . right = r . left + width ;
r . bottom = r . top + height ;
RECT clientRect ;
GetClientRect ( hwnd , & clientRect ) ;
if ( r . right > clientRect . right )
2024-07-15 17:13:18 -04:00
OffsetRect ( & r , - width - 15 , 0 ) ;
2024-04-12 01:05:04 -04:00
if ( r . bottom > clientRect . bottom )
{
OffsetRect ( & r , 0 , clientRect . bottom - r . bottom ) ;
if ( r . top < 0 )
OffsetRect ( & r , 0 , - r . top ) ;
}
RECT fillRect = r ;
fillRect . right + = 2 + extraWidth ;
FillRect ( hdc , & fillRect , backgroundBrush ) ;
for ( auto & line : lines )
{
RECT tr = r ;
tr . left + = line . left ;
tr . top + = line . top ;
SetTextColor ( hdc , line . color ) ;
DrawTextW ( hdc , line . str . data ( ) , u32 ( line . str . size ( ) ) , & tr , DT_SINGLELINE ) ;
}
}
void DrawAtCursor ( )
{
POINT p ;
GetCursorPos ( & p ) ;
ScreenToClient ( hwnd , & p ) ;
p . x + = 3 ;
p . y + = 3 ;
DrawAtPos ( p . x , p . y ) ;
}
DrawTextLogger & SetColor ( COLORREF c ) { textColor = c ; return * this ; }
int width = 0 ;
int height = 0 ;
int textOffset = 2 ;
int extraWidth = 0 ;
struct Line { TString str ; int left ; int top ; COLORREF color ; } ;
Vector < Line > lines ;
HWND hwnd ;
2023-11-29 18:47:11 -05:00
HDC hdc ;
int fontHeight ;
2024-04-12 01:05:04 -04:00
HBRUSH backgroundBrush ;
COLORREF textColor ;
bool isFirst = true ;
2023-11-29 18:47:11 -05:00
} ;
2024-01-09 12:43:47 -05:00
class WriteTextLogger : public Logger
{
public :
WriteTextLogger ( TString & out ) : m_out ( out ) { }
virtual void BeginScope ( ) override { }
virtual void EndScope ( ) override { }
virtual void Log ( LogEntryType type , const wchar_t * str , u32 strLen ) override { m_out . append ( str , strLen ) . append ( TC ( " \n " ) ) ; }
TString & m_out ;
} ;
2023-11-29 18:47:11 -05:00
2024-07-10 18:24:10 -04:00
Visualizer : : Visualizer ( VisualizerConfig & config , Logger & logger )
2023-11-29 18:47:11 -05:00
: m_logger ( logger )
2024-07-10 18:24:10 -04:00
, m_config ( config )
2023-11-29 18:47:11 -05:00
, m_trace ( logger )
{
2024-07-30 01:49:32 -04:00
memset ( m_activeProcessFont , 0 , sizeof ( m_activeProcessFont ) ) ;
memset ( m_activeProcessCountHistory , 0 , sizeof ( m_activeProcessCountHistory ) ) ;
2023-11-29 18:47:11 -05:00
}
Visualizer : : ~ Visualizer ( )
{
m_looping = false ;
// Make sure GetMessage is triggered out of its slumber
PostMessage ( m_hwnd , WM_QUIT , 0 , 0 ) ;
m_thread . Wait ( ) ;
delete m_client ;
}
bool Visualizer : : ShowUsingListener ( const wchar_t * channelName )
{
TraceChannel channel ( m_logger ) ;
if ( ! channel . Init ( channelName ) )
{
m_logger . Error ( L " TODO " ) ;
return false ;
}
2024-06-07 16:29:55 -04:00
m_listenTimeout . Create ( false ) ;
2023-11-29 18:47:11 -05:00
m_listenChannel . Append ( channelName ) ;
m_looping = true ;
m_autoScroll = false ;
2024-06-07 16:29:55 -04:00
if ( ! StartHwndThread ( ) )
return true ;
2023-11-29 18:47:11 -05:00
2024-06-07 16:29:55 -04:00
{
StringBuffer < > title ;
2024-09-10 10:26:02 -04:00
PostNewTitle ( GetTitlePrefix ( title ) . Appendf ( L " Listening for new sessions on channel '%s' " , m_listenChannel . data ) ) ;
2024-06-07 16:29:55 -04:00
}
2023-11-29 18:47:11 -05:00
StringBuffer < 256 > traceName ;
while ( m_hwnd )
{
2024-07-17 17:48:42 -04:00
if ( m_locked )
{
m_listenTimeout . IsSet ( 1000 ) ;
continue ;
}
2024-07-19 19:12:12 -04:00
if ( m_parentHwnd & & ! IsWindow ( m_parentHwnd ) )
PostQuit ( ) ;
2023-11-29 18:47:11 -05:00
traceName . Clear ( ) ;
if ( ! channel . Read ( traceName ) )
{
m_logger . Error ( L " TODO2 " ) ;
return false ;
}
2024-06-07 16:29:55 -04:00
if ( traceName . count )
2023-11-29 18:47:11 -05:00
{
2024-07-16 16:27:53 -04:00
StringBuffer < 128 > filter ;
if ( ! m_config . ShowAllTraces )
{
OwnerInfo ownerInfo = GetOwnerInfo ( ) ;
if ( ownerInfo . pid )
filter . Appendf ( L " _%s%u " , ownerInfo . id , ownerInfo . pid ) ;
}
if ( ! traceName . Equals ( m_newTraceName . data ) & & traceName . EndsWith ( filter . data ) )
2024-06-07 16:29:55 -04:00
{
m_newTraceName . Clear ( ) . Append ( traceName ) ;
2024-07-15 17:13:18 -04:00
PostNewTrace ( 0 , false ) ;
2024-06-07 16:29:55 -04:00
}
2023-11-29 18:47:11 -05:00
}
2024-06-07 16:29:55 -04:00
else
m_newTraceName . Clear ( ) ;
m_listenTimeout . IsSet ( 1000 ) ;
2023-11-29 18:47:11 -05:00
}
return true ;
}
bool Visualizer : : ShowUsingNamedTrace ( const wchar_t * namedTrace )
{
m_looping = true ;
2024-06-07 16:29:55 -04:00
if ( ! StartHwndThread ( ) )
return true ;
m_newTraceName . Append ( namedTrace ) ;
2024-07-15 17:13:18 -04:00
PostNewTrace ( 0 , false ) ;
2023-11-29 18:47:11 -05:00
return true ;
}
bool Visualizer : : ShowUsingSocket ( NetworkBackend & backend , const wchar_t * host , u16 port )
{
auto destroyClient = MakeGuard ( [ & ] ( ) { delete m_client ; m_client = nullptr ; } ) ;
m_looping = true ;
m_autoScroll = false ;
2024-06-07 16:29:55 -04:00
if ( ! StartHwndThread ( ) )
return true ;
2023-11-29 18:47:11 -05:00
2024-09-05 03:09:28 -04:00
m_clientDisconnect . Create ( true ) ;
2023-11-29 18:47:11 -05:00
wchar_t dots [ ] = TC ( " .... " ) ;
u32 dotsCounter = 0 ;
StringBuffer < 256 > traceName ;
while ( m_hwnd )
{
if ( ! m_client )
{
bool ctorSuccess = true ;
2024-09-05 03:09:28 -04:00
NetworkClientCreateInfo ncci ;
ncci . workerCount = 1 ;
m_client = new NetworkClient ( ctorSuccess , ncci ) ;
2023-11-29 18:47:11 -05:00
if ( ! ctorSuccess )
return false ;
}
StringBuffer < > title ;
2024-09-10 10:26:02 -04:00
PostNewTitle ( GetTitlePrefix ( title ) . Appendf ( L " Trying to connect to %s:%u%s " , host , port , dots + ( ( dotsCounter - - ) % 4 ) ) ) ;
2023-11-29 18:47:11 -05:00
if ( ! m_client - > Connect ( backend , host , port ) )
continue ;
2024-09-10 10:26:02 -04:00
PostNewTitle ( GetTitlePrefix ( title ) . Appendf ( L " Connected to %s:%u " , host , port ) ) ;
2024-07-15 17:13:18 -04:00
PostNewTrace ( 0 , false ) ;
2023-11-29 18:47:11 -05:00
2024-09-05 03:09:28 -04:00
while ( m_hwnd & & m_client - > IsConnected ( ) & & ! m_clientDisconnect . IsSet ( 1000 ) )
;
2024-09-10 10:26:02 -04:00
PostNewTitle ( GetTitlePrefix ( title ) . Appendf ( L " Disconnected... " ) ) ;
2023-11-29 18:47:11 -05:00
2024-03-08 18:31:48 -05:00
m_client - > Disconnect ( ) ;
2023-11-29 18:47:11 -05:00
delete m_client ;
m_client = nullptr ;
2024-09-05 03:09:28 -04:00
m_clientDisconnect . Reset ( ) ;
Sleep ( 4000 ) ; // To prevent it from reconnecting to the same thing again and get thrown out (since it will post a WM_NEWTRACE and clean everything
2023-11-29 18:47:11 -05:00
}
return true ;
}
bool Visualizer : : ShowUsingFile ( const wchar_t * fileName , u32 replay )
{
2024-01-16 15:35:42 -05:00
m_looping = true ;
m_autoScroll = false ;
2024-06-07 16:29:55 -04:00
if ( ! StartHwndThread ( ) )
return true ;
2023-11-29 18:47:11 -05:00
m_fileName . Append ( fileName ) ;
2024-07-15 17:13:18 -04:00
PostNewTrace ( replay , 0 ) ;
2023-11-29 18:47:11 -05:00
return true ;
}
2024-06-07 16:29:55 -04:00
bool Visualizer : : StartHwndThread ( )
{
m_thread . Start ( [ this ] ( ) { ThreadLoop ( ) ; return 0 ; } ) ;
while ( ! m_hwnd )
if ( m_thread . Wait ( 10 ) )
return false ;
return true ;
}
2023-11-29 18:47:11 -05:00
bool Visualizer : : HasWindow ( )
{
return m_looping = = true ;
}
HWND Visualizer : : GetHwnd ( )
{
return m_hwnd ;
}
2024-07-17 17:48:42 -04:00
void Visualizer : : Lock ( bool lock )
{
m_locked = lock ;
}
2024-09-10 10:26:02 -04:00
StringBufferBase & Visualizer : : GetTitlePrefix ( StringBufferBase & out )
2023-11-29 18:47:11 -05:00
{
2024-09-10 10:26:02 -04:00
out . Clear ( ) ;
2023-11-29 18:47:11 -05:00
out . Append ( L " UbaVisualizer " ) ;
# if UBA_DEBUG
out . Append ( L " (DEBUG) " ) ;
# endif
out . Append ( L " - " ) ;
2024-09-10 10:26:02 -04:00
return out ;
2023-11-29 18:47:11 -05:00
}
2024-04-27 20:06:34 -04:00
bool Visualizer : : Unselect ( )
{
2024-07-15 17:13:18 -04:00
if ( m_processSelected | | m_sessionSelectedIndex ! = ~ 0u | | m_statsSelected | | m_timelineSelected | | m_fetchedFilesSelected ! = ~ 0u | | m_workSelected | | ! m_hyperLinkSelected . empty ( ) )
2024-04-27 20:06:34 -04:00
{
m_processSelected = false ;
m_sessionSelectedIndex = ~ 0u ;
m_statsSelected = false ;
m_buttonSelected = ~ 0u ;
m_timelineSelected = 0 ;
m_fetchedFilesSelected = ~ 0u ;
m_workSelected = false ;
2024-07-15 17:13:18 -04:00
m_hyperLinkSelected . clear ( ) ;
2024-04-27 20:06:34 -04:00
return true ;
}
return false ;
}
2023-11-29 18:47:11 -05:00
void Visualizer : : Reset ( )
{
for ( HBITMAP bm : m_textBitmaps )
DeleteObject ( bm ) ;
DeleteObject ( m_lastBitmap ) ;
m_contentWidth = 0 ;
m_contentHeight = 0 ;
m_textBitmaps . clear ( ) ;
m_lastBitmap = 0 ;
m_lastBitmapOffset = BitmapCacheHeight ;
2024-07-16 16:27:53 -04:00
//m_autoScroll = true;
//m_scrollPosX = 0;
//m_scrollPosY = 0;
2023-11-29 18:47:11 -05:00
//m_zoomValue = 0.75f;
//m_horizontalScaleValue = 1.0f;
m_startTime = GetTime ( ) ;
m_pauseTime = 0 ;
2024-04-27 20:06:34 -04:00
Unselect ( ) ;
2023-11-29 18:47:11 -05:00
}
2024-07-10 18:24:10 -04:00
void Visualizer : : InitBrushes ( )
2023-11-29 18:47:11 -05:00
{
2024-07-10 18:24:10 -04:00
if ( m_config . DarkMode )
2023-11-29 18:47:11 -05:00
{
2024-02-02 00:30:28 -05:00
m_textColor = RGB ( 190 , 190 , 190 ) ;
m_textWarningColor = RGB ( 190 , 190 , 0 ) ;
m_textErrorColor = RGB ( 190 , 0 , 0 ) ;
2023-11-29 18:47:11 -05:00
m_processBrushes [ 0 ] . inProgress = CreateSolidBrush ( RGB ( 70 , 70 , 70 ) ) ;
m_processBrushes [ 1 ] . inProgress = CreateSolidBrush ( RGB ( 130 , 130 , 130 ) ) ;
m_processBrushes [ 0 ] . error = CreateSolidBrush ( RGB ( 140 , 0 , 0 ) ) ;
m_processBrushes [ 1 ] . error = CreateSolidBrush ( RGB ( 190 , 0 , 0 ) ) ;
m_processBrushes [ 0 ] . returned = CreateSolidBrush ( RGB ( 50 , 50 , 120 ) ) ;
m_processBrushes [ 1 ] . returned = CreateSolidBrush ( RGB ( 70 , 70 , 160 ) ) ;
m_processBrushes [ 0 ] . recv = CreateSolidBrush ( RGB ( 10 , 92 , 10 ) ) ;
m_processBrushes [ 1 ] . recv = CreateSolidBrush ( RGB ( 10 , 130 , 10 ) ) ;
m_processBrushes [ 0 ] . success = CreateSolidBrush ( RGB ( 10 , 100 , 10 ) ) ;
m_processBrushes [ 1 ] . success = CreateSolidBrush ( RGB ( 10 , 140 , 10 ) ) ;
m_processBrushes [ 0 ] . send = CreateSolidBrush ( RGB ( 10 , 115 , 10 ) ) ;
m_processBrushes [ 1 ] . send = CreateSolidBrush ( RGB ( 10 , 145 , 10 ) ) ;
2024-04-13 19:21:42 -04:00
m_processBrushes [ 0 ] . cacheFetch = CreateSolidBrush ( RGB ( 24 , 112 , 110 ) ) ;
m_processBrushes [ 1 ] . cacheFetch = CreateSolidBrush ( RGB ( 31 , 143 , 138 ) ) ;
2023-11-29 18:47:11 -05:00
m_workBrush = CreateSolidBrush ( RGB ( 70 , 70 , 100 ) ) ;
m_backgroundBrush = CreateSolidBrush ( 0x00252526 ) ;
m_separatorPen = CreatePen ( PS_SOLID , 1 , RGB ( 50 , 50 , 50 ) ) ;
m_tooltipBackgroundBrush = CreateSolidBrush ( 0x00404040 ) ;
m_checkboxPen = CreatePen ( PS_SOLID , 1 , RGB ( 130 , 130 , 130 ) ) ;
m_sendColor = RGB ( 0 , 170 , 0 ) ;
m_recvColor = RGB ( 0 , 170 , 255 ) ;
m_cpuColor = RGB ( 170 , 170 , 0 ) ;
m_memColor = RGB ( 170 , 0 , 255 ) ;
}
else
{
2024-02-02 00:30:28 -05:00
m_textColor = GetSysColor ( COLOR_INFOTEXT ) ;
m_textWarningColor = RGB ( 170 , 130 , 0 ) ;
m_textErrorColor = RGB ( 190 , 0 , 0 ) ;
2023-11-29 18:47:11 -05:00
m_processBrushes [ 0 ] . inProgress = CreateSolidBrush ( RGB ( 150 , 150 , 150 ) ) ;
m_processBrushes [ 1 ] . inProgress = CreateSolidBrush ( RGB ( 180 , 180 , 180 ) ) ;
m_processBrushes [ 0 ] . error = CreateSolidBrush ( RGB ( 255 , 70 , 70 ) ) ;
m_processBrushes [ 1 ] . error = CreateSolidBrush ( RGB ( 255 , 100 , 70 ) ) ;
m_processBrushes [ 0 ] . returned = CreateSolidBrush ( RGB ( 150 , 150 , 200 ) ) ;
m_processBrushes [ 1 ] . returned = CreateSolidBrush ( RGB ( 170 , 170 , 200 ) ) ;
m_processBrushes [ 0 ] . recv = CreateSolidBrush ( RGB ( 10 , 190 , 10 ) ) ;
m_processBrushes [ 1 ] . recv = CreateSolidBrush ( RGB ( 20 , 210 , 20 ) ) ;
m_processBrushes [ 0 ] . success = CreateSolidBrush ( RGB ( 10 , 200 , 10 ) ) ;
m_processBrushes [ 1 ] . success = CreateSolidBrush ( RGB ( 20 , 220 , 20 ) ) ;
m_processBrushes [ 0 ] . send = CreateSolidBrush ( RGB ( 80 , 210 , 80 ) ) ;
m_processBrushes [ 1 ] . send = CreateSolidBrush ( RGB ( 90 , 250 , 90 ) ) ;
2024-04-13 19:21:42 -04:00
m_processBrushes [ 0 ] . cacheFetch = CreateSolidBrush ( RGB ( 150 , 150 , 200 ) ) ;
m_processBrushes [ 1 ] . cacheFetch = CreateSolidBrush ( RGB ( 170 , 170 , 200 ) ) ;
2023-11-29 18:47:11 -05:00
m_workBrush = CreateSolidBrush ( RGB ( 150 , 150 , 200 ) ) ;
m_backgroundBrush = GetSysColorBrush ( 0 ) ;
m_separatorPen = CreatePen ( PS_SOLID , 1 , RGB ( 180 , 180 , 180 ) ) ;
m_tooltipBackgroundBrush = GetSysColorBrush ( COLOR_INFOBK ) ;
m_checkboxPen = CreatePen ( PS_SOLID , 1 , RGB ( 130 , 130 , 130 ) ) ;
m_sendColor = RGB ( 0 , 170 , 0 ) ; // Green
m_recvColor = RGB ( 63 , 72 , 204 ) ; // Blue
m_cpuColor = RGB ( 200 , 130 , 0 ) ; // Orange
m_memColor = RGB ( 170 , 0 , 255 ) ; // Purple
}
m_textPen = CreatePen ( PS_SOLID , 1 , m_textColor ) ;
m_sendPen = CreatePen ( PS_SOLID , 1 , m_sendColor ) ;
m_recvPen = CreatePen ( PS_SOLID , 1 , m_recvColor ) ;
m_cpuPen = CreatePen ( PS_SOLID , 1 , m_cpuColor ) ;
m_memPen = CreatePen ( PS_SOLID , 1 , m_memColor ) ;
2024-07-10 18:24:10 -04:00
}
void Visualizer : : ThreadLoop ( )
{
2024-08-06 15:31:53 -04:00
if ( m_config . parent )
{
SetProcessDpiAwareness ( PROCESS_SYSTEM_DPI_AWARE ) ;
}
2024-07-10 18:24:10 -04:00
InitBrushes ( ) ;
2023-11-29 18:47:11 -05:00
LOGBRUSH br = { 0 } ;
GetObject ( m_backgroundBrush , sizeof ( br ) , & br ) ;
m_processUpdatePen = CreatePen ( PS_SOLID , 2 , RGB ( GetRValue ( br . lbColor ) , GetGValue ( br . lbColor ) , GetBValue ( br . lbColor ) ) ) ;
HINSTANCE hInstance = GetModuleHandle ( NULL ) ;
2024-08-06 15:31:53 -04:00
int winPosX = m_config . x ;
int winPosY = m_config . y ;
int winWidth = m_config . width ;
int winHeight = m_config . height ;
RECT rectCombined ;
SetRectEmpty ( & rectCombined ) ;
EnumDisplayMonitors ( 0 , 0 , [ ] ( HMONITOR hMon , HDC hdc , LPRECT lprcMonitor , LPARAM pData )
{
RECT * rectCombined = reinterpret_cast < RECT * > ( pData ) ;
UnionRect ( rectCombined , rectCombined , lprcMonitor ) ;
return TRUE ;
} , ( LPARAM ) & rectCombined ) ;
winPosX = Max ( int ( rectCombined . left ) , winPosX ) ;
winPosY = Max ( int ( rectCombined . top ) , winPosY ) ;
winPosX = Min ( int ( rectCombined . right ) - winWidth , winPosX ) ;
winPosY = Min ( int ( rectCombined . bottom ) - winHeight , winPosY ) ;
2023-11-29 18:47:11 -05:00
WNDCLASSEX wndClassEx ;
ZeroMemory ( & wndClassEx , sizeof ( wndClassEx ) ) ;
wndClassEx . cbSize = sizeof ( wndClassEx ) ;
wndClassEx . style = CS_HREDRAW | CS_VREDRAW ;
wndClassEx . lpfnWndProc = & StaticWinProc ;
2024-05-03 16:23:21 -04:00
wndClassEx . hIcon = LoadIcon ( hInstance , MAKEINTRESOURCE ( 123 ) ) ;
2024-07-15 17:13:18 -04:00
wndClassEx . hCursor = 0 ;
2023-11-29 18:47:11 -05:00
wndClassEx . hInstance = hInstance ;
wndClassEx . hbrBackground = NULL ;
wndClassEx . lpszClassName = TEXT ( " UbaVisualizer " ) ;
ATOM wndClassAtom = RegisterClassEx ( & wndClassEx ) ;
2024-07-30 01:49:32 -04:00
UpdateDefaultFont ( ) ;
2023-11-29 18:47:11 -05:00
2024-07-17 17:48:42 -04:00
UpdateProcessFont ( ) ;
2023-11-29 18:47:11 -05:00
const TCHAR * fontName = TEXT ( " Consolas " ) ;
2024-08-06 15:31:53 -04:00
m_popupFont . handle = ( HFONT ) CreateFontW ( - 12 , 0 , 0 , 0 , FW_NORMAL , false , false , false , ANSI_CHARSET , OUT_DEFAULT_PRECIS , CLIP_DEFAULT_PRECIS , CLEARTYPE_QUALITY , FIXED_PITCH | FF_MODERN , fontName ) ;
2024-07-30 01:49:32 -04:00
m_popupFont . height = 14 ;
2023-11-29 18:47:11 -05:00
//m_popupFont = (HFONT)GetStockObject(SYSTEM_FIXED_FONT);
DWORD windowStyle = WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL ;
const TCHAR * windowClassName = MAKEINTATOM ( wndClassAtom ) ;
2024-07-19 15:56:16 -04:00
DWORD exStyle = 0 ;
if ( m_config . parent )
windowStyle = WS_POPUP | WS_VSCROLL | WS_HSCROLL ; // | WS_VISIBLE;
2023-11-29 18:47:11 -05:00
StringBuffer < > title ;
2024-09-10 10:26:02 -04:00
GetTitlePrefix ( title ) . Append ( L " Initializing... " ) ;
2023-11-29 18:47:11 -05:00
2024-07-19 15:56:16 -04:00
HWND hwnd = CreateWindowEx ( exStyle , windowClassName , title . data , windowStyle , winPosX , winPosY , winWidth , winHeight , NULL , NULL , hInstance , this ) ;
SetWindowLongPtr ( hwnd , GWLP_USERDATA , ( LONG_PTR ) this ) ;
2023-11-29 18:47:11 -05:00
BOOL cloak = TRUE ;
2024-07-19 15:56:16 -04:00
DwmSetWindowAttribute ( hwnd , DWMWA_CLOAK , & cloak , sizeof ( cloak ) ) ;
2023-11-29 18:47:11 -05:00
2024-07-10 18:24:10 -04:00
//if (m_config.DarkMode)
2023-11-29 18:47:11 -05:00
{
2024-07-19 15:56:16 -04:00
SetWindowTheme ( hwnd , L " DarkMode_Explorer " , NULL ) ;
SendMessageW ( hwnd , WM_THEMECHANGED , 0 , 0 ) ;
2023-11-29 18:47:11 -05:00
BOOL useDarkMode = true ;
u32 attribute = 20 ; // DWMWA_USE_IMMERSIVE_DARK_MODE
2024-07-19 15:56:16 -04:00
DwmSetWindowAttribute ( hwnd , attribute , & useDarkMode , sizeof ( useDarkMode ) ) ;
2023-11-29 18:47:11 -05:00
}
HitTestResult res ;
HitTest ( res , { - 1 , - 1 } ) ;
2024-07-19 15:56:16 -04:00
if ( m_config . parent )
{
m_parentHwnd = ( HWND ) ( uintptr_t ) m_config . parent ;
if ( ! SetParent ( hwnd , m_parentHwnd ) )
m_logger . Error ( L " SetParent failed using parentHwnd 0x%llx " , m_parentHwnd ) ;
PostMessage ( m_parentHwnd , 0x0444 , 0 , ( LPARAM ) hwnd ) ;
}
m_hwnd = hwnd ;
if ( ! m_parentHwnd )
ShowWindow ( m_hwnd , SW_SHOW ) ;
2023-11-29 18:47:11 -05:00
UpdateWindow ( m_hwnd ) ;
UpdateScrollbars ( true ) ;
cloak = FALSE ;
DwmSetWindowAttribute ( m_hwnd , DWMWA_CLOAK , & cloak , sizeof ( cloak ) ) ;
m_startTime = GetTime ( ) ;
while ( m_looping )
{
MSG msg ;
while ( GetMessage ( & msg , NULL , 0 , 0 ) )
{
if ( m_hwnd & & ! IsDialogMessage ( m_hwnd , & msg ) )
{
TranslateMessage ( & msg ) ;
DispatchMessage ( & msg ) ;
}
// It may happen that we receive the WM_DESTOY message from within the DistpachMessage above and handle it directly in WndProc.
// So before trying to call GetMessage again, we just need to validate that m_looping is still true otherwise we could end
// up waiting forever for this thread to exit.
if ( ! m_looping | | msg . message = = WM_QUIT | | msg . message = = WM_DESTROY | | msg . message = = WM_CLOSE )
{
if ( m_hwnd )
2024-07-15 17:13:18 -04:00
{
if ( m_config . AutoSaveSettings )
SaveSettings ( ) ;
2023-11-29 18:47:11 -05:00
DestroyWindow ( m_hwnd ) ;
2024-07-15 17:13:18 -04:00
}
2023-11-29 18:47:11 -05:00
UnregisterClass ( windowClassName , hInstance ) ;
m_hwnd = 0 ;
m_looping = false ;
2024-06-07 16:29:55 -04:00
m_listenTimeout . Set ( ) ;
2023-11-29 18:47:11 -05:00
break ;
}
}
}
}
2024-02-02 18:30:47 -05:00
void Visualizer : : Pause ( bool pause )
{
if ( m_paused = = pause )
return ;
m_paused = pause ;
if ( pause )
{
m_pauseStart = GetTime ( ) ;
}
else
{
m_replay = 1 ;
m_pauseTime + = GetTime ( ) - m_pauseStart ;
m_traceView . finished = false ;
SetTimer ( m_hwnd , 0 , 200 , NULL ) ;
}
}
2024-05-22 17:27:49 -04:00
void Visualizer : : StartDragToScroll ( const POINT & anchor )
{
// Uses reference-counter method since multiple input events (left and middle mouse button) can trigger the drag-to-scroll mechansim
if ( m_dragToScrollCounter = = 0 )
{
m_processSelected = false ;
m_sessionSelectedIndex = ~ 0u ;
m_statsSelected = false ;
m_buttonSelected = ~ 0u ;
m_timelineSelected = 0 ;
m_fetchedFilesSelected = ~ 0u ;
m_workSelected = false ;
2024-07-15 17:13:18 -04:00
m_hyperLinkSelected . clear ( ) ;
2024-05-22 17:27:49 -04:00
m_autoScroll = false ;
m_mouseAnchor = { anchor . x , anchor . y } ;
m_scrollAtAnchorX = m_scrollPosX ;
m_scrollAtAnchorY = m_scrollPosY ;
SetCapture ( m_hwnd ) ;
2024-08-06 15:31:53 -04:00
Redraw ( false ) ;
2024-05-22 17:27:49 -04:00
}
+ + m_dragToScrollCounter ;
}
void Visualizer : : StopDragToScroll ( )
{
2024-05-26 18:07:20 -04:00
if ( m_dragToScrollCounter > 0 )
- - m_dragToScrollCounter ;
if ( m_dragToScrollCounter ! = 0 )
return ;
ReleaseCapture ( ) ;
if ( UpdateSelection ( ) )
2024-08-06 15:31:53 -04:00
Redraw ( false ) ;
2024-05-22 17:27:49 -04:00
}
2024-07-10 18:24:10 -04:00
void Visualizer : : SaveSettings ( )
{
RECT rect ;
GetWindowRect ( m_hwnd , & rect ) ;
m_config . x = rect . left ;
m_config . y = rect . top ;
m_config . width = rect . right - rect . left ;
m_config . height = rect . bottom - rect . top ;
m_config . Save ( m_logger ) ;
}
2024-07-17 17:48:42 -04:00
void Visualizer : : DirtyBitmaps ( bool full )
2024-07-10 18:24:10 -04:00
{
for ( auto & session : m_traceView . sessions )
for ( auto & processor : session . processors )
for ( auto & process : processor . processes )
2024-07-17 17:48:42 -04:00
{
process . bitmapDirty = true ;
if ( full )
process . bitmap = 0 ;
}
if ( ! full )
return ;
for ( HBITMAP bm : m_textBitmaps )
DeleteObject ( bm ) ;
DeleteObject ( m_lastBitmap ) ;
m_textBitmaps . clear ( ) ;
m_lastBitmapOffset = BitmapCacheHeight ;
m_lastBitmap = 0 ;
2024-07-10 18:24:10 -04:00
}
2024-07-30 01:49:32 -04:00
void Visualizer : : UpdateFont ( Font & font , int height , bool createUnderline )
2024-07-15 17:13:18 -04:00
{
2024-07-30 01:49:32 -04:00
font . height = height ;
2024-08-06 15:31:53 -04:00
int fh = height ;
font . offset = 0 ;
if ( height < = 13 )
{
+ + fh ;
- - font . offset ;
}
if ( height < = 11 )
+ + fh ;
if ( height < = 9 )
+ + fh ;
if ( height < = 8 )
+ + fh ;
if ( height < = 6 )
+ + fh ;
if ( height < = 4 )
- - font . offset ;
2024-07-30 01:49:32 -04:00
if ( font . handle )
DeleteObject ( font . handle ) ;
if ( font . handleUnderlined )
DeleteObject ( font . handleUnderlined ) ;
2024-08-06 15:31:53 -04:00
//NONCLIENTMETRICS nonClientMetrics;
//nonClientMetrics.cbSize = sizeof(nonClientMetrics);
//SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(nonClientMetrics), &nonClientMetrics, 0);
//m_font = (HFONT)CreateFontIndirect(&nonClientMetrics.lfMessageFont);
font . handle = ( HFONT ) CreateFontW ( 4 - fh , 0 , 0 , 0 , FW_NORMAL , 0 , 0 , 0 , ANSI_CHARSET , OUT_DEFAULT_PRECIS , CLIP_DEFAULT_PRECIS , CLEARTYPE_QUALITY , DEFAULT_PITCH , m_config . fontName . c_str ( ) ) ;
2024-07-30 01:49:32 -04:00
if ( createUnderline )
2024-08-06 15:31:53 -04:00
font . handleUnderlined = ( HFONT ) CreateFontW ( 4 - fh , 0 , 0 , 0 , FW_NORMAL , 0 , 1 , 0 , ANSI_CHARSET , OUT_DEFAULT_PRECIS , CLIP_DEFAULT_PRECIS , CLEARTYPE_QUALITY , DEFAULT_PITCH , m_config . fontName . c_str ( ) ) ;
2024-07-30 01:49:32 -04:00
}
void Visualizer : : UpdateDefaultFont ( )
{
UpdateFont ( m_defaultFont , m_config . fontSize , true ) ;
m_sessionStepY = m_defaultFont . height + 4 ;
m_timelineFont = m_defaultFont ;
2024-07-17 17:48:42 -04:00
}
2024-07-15 17:13:18 -04:00
2024-07-17 17:48:42 -04:00
void Visualizer : : UpdateProcessFont ( )
{
2024-08-09 17:06:11 -04:00
m_zoomValue = 1.0f + m_boxHeight / 30.0f ;
int fontHeight = Max ( m_boxHeight - 2 , 1 ) ;
2024-07-30 01:49:32 -04:00
UpdateFont ( m_processFont , fontHeight , false ) ;
m_progressRectLeft = int ( 5 + float ( m_processFont . height ) * 1.8f ) ;
2024-07-17 17:48:42 -04:00
DirtyBitmaps ( true ) ;
2024-07-15 17:13:18 -04:00
}
void Visualizer : : ChangeFontSize ( int offset )
{
m_config . fontSize + = offset ;
m_config . fontSize = Max ( m_config . fontSize , 10u ) ;
2024-07-30 01:49:32 -04:00
UpdateDefaultFont ( ) ;
2024-08-06 15:31:53 -04:00
Redraw ( true ) ;
2024-07-15 17:13:18 -04:00
}
2024-08-06 15:31:53 -04:00
void Visualizer : : Redraw ( bool now )
2024-07-15 17:13:18 -04:00
{
2024-08-06 15:31:53 -04:00
u32 flags = RDW_INVALIDATE ;
if ( now )
flags | = RDW_UPDATENOW ;
RedrawWindow ( m_hwnd , NULL , NULL , flags ) ;
u32 activeProcessCount = u32 ( m_trace . m_activeProcesses . size ( ) ) ;
for ( u32 i = 0 ; i ! = sizeof_array ( m_activeProcessCountHistory ) ; + + i )
m_activeProcessCountHistory [ i ] = activeProcessCount ;
2024-07-15 17:13:18 -04:00
}
2023-11-29 18:47:11 -05:00
void Visualizer : : PaintClient ( const Function < void ( HDC hdc , HDC memDC , RECT & clientRect ) > & paintFunc )
{
HDC hdc = GetDC ( m_hwnd ) ;
RECT rect ;
GetClientRect ( m_hwnd , & rect ) ;
HDC memDC = CreateCompatibleDC ( hdc ) ;
if ( ! EqualRect ( & m_cachedBitmapRect , & rect ) )
{
if ( m_cachedBitmap )
DeleteObject ( m_cachedBitmap ) ;
m_cachedBitmap = CreateCompatibleBitmap ( hdc , rect . right - rect . left , rect . bottom - rect . top ) ;
m_cachedBitmapRect = rect ;
}
HGDIOBJ oldBmp = SelectObject ( memDC , m_cachedBitmap ) ;
paintFunc ( hdc , memDC , rect ) ;
SelectObject ( memDC , oldBmp ) ;
DeleteDC ( memDC ) ;
ReleaseDC ( m_hwnd , hdc ) ;
}
constexpr int GraphHeight = 30 ;
2024-03-08 18:31:48 -05:00
struct SessionRec
{
TraceView : : Session * session ;
u32 index ;
} ;
2024-07-17 17:48:42 -04:00
void Populate ( SessionRec * recs , TraceView & traceView , bool sort )
2024-03-08 18:31:48 -05:00
{
u32 count = u32 ( traceView . sessions . size ( ) ) ;
for ( u32 i = 0 , e = count ; i ! = e ; + + i )
recs [ i ] = { & traceView . sessions [ i ] , i } ;
2024-07-17 17:48:42 -04:00
if ( count < = 1 | | ! sort )
2024-03-08 18:31:48 -05:00
return ;
std : : sort ( recs + 1 , recs + traceView . sessions . size ( ) , [ ] ( SessionRec & a , SessionRec & b )
{
auto & as = * a . session ;
auto & bs = * b . session ;
if ( ( as . processActiveCount ! = 0 ) ! = ( bs . processActiveCount ! = 0 ) )
return as . processActiveCount > bs . processActiveCount ;
if ( as . processActiveCount & & as . proxyCreated ! = bs . proxyCreated )
return int ( as . proxyCreated ) > int ( bs . proxyCreated ) ;
return a . index < b . index ;
} ) ;
}
2023-11-29 18:47:11 -05:00
void Visualizer : : PaintAll ( HDC hdc , const RECT & clientRect )
{
2024-07-16 16:27:53 -04:00
u64 playTime = GetPlayTime ( ) ;
2023-11-29 18:47:11 -05:00
int posY = int ( m_scrollPosY ) ;
float scaleX = 50.0f * m_zoomValue * m_horizontalScaleValue ;
RECT progressRect = clientRect ;
2024-07-17 17:48:42 -04:00
progressRect . left + = m_progressRectLeft ;
2023-11-29 18:47:11 -05:00
2024-07-17 17:48:42 -04:00
if ( m_config . showTimeline )
{
2024-07-30 01:49:32 -04:00
progressRect . bottom - = m_defaultFont . height + 10 ;
2024-07-17 17:48:42 -04:00
}
2023-11-29 18:47:11 -05:00
SetBkMode ( hdc , TRANSPARENT ) ;
SetTextColor ( hdc , m_textColor ) ;
HDC textDC = CreateCompatibleDC ( hdc ) ;
SetTextColor ( textDC , m_textColor ) ;
2024-07-30 01:49:32 -04:00
SelectObject ( textDC , m_processFont . handle ) ;
2023-11-29 18:47:11 -05:00
SelectObject ( textDC , GetStockObject ( NULL_BRUSH ) ) ;
SetBkMode ( textDC , TRANSPARENT ) ;
2024-07-10 18:24:10 -04:00
SetBkColor ( hdc , m_config . DarkMode ? RGB ( 70 , 70 , 70 ) : RGB ( 180 , 180 , 180 ) ) ;
2023-11-29 18:47:11 -05:00
//TEXTMETRIC metric;
//GetTextMetrics(textDC, &metric);
HBITMAP nullBmp = CreateCompatibleBitmap ( hdc , 1 , 1 ) ;
HBITMAP oldBmp = ( HBITMAP ) SelectObject ( textDC , nullBmp ) ;
HBITMAP lastSelectedBitmap = 0 ;
HBRUSH lastSelectedBrush = 0 ;
u64 lastStop = 0 ;
2024-07-30 01:49:32 -04:00
SetActiveFont ( m_defaultFont ) ;
auto drawStatusText = [ & ] ( const StringView & text , LogEntryType type , int posX , int endX , bool moveY , bool underlined = false )
2024-07-15 17:13:18 -04:00
{
RECT rect ;
2024-07-30 01:49:32 -04:00
rect . left = posX ;
rect . right = endX ;
2024-08-06 15:31:53 -04:00
rect . top = posY + m_activeFont . offset ;
2024-07-30 01:49:32 -04:00
rect . bottom = posY + m_activeFont . height + 2 ;
2024-07-15 17:13:18 -04:00
SetTextColor ( hdc , type = = LogEntryType_Info ? m_textColor : ( type = = LogEntryType_Error ? m_textErrorColor : m_textWarningColor ) ) ;
if ( underlined )
2024-07-30 01:49:32 -04:00
SelectObject ( m_activeHdc , m_activeFont . handleUnderlined ) ;
2024-07-15 17:13:18 -04:00
ExtTextOutW ( hdc , rect . left , posY , ETO_CLIPPED , & rect , text . data , text . count , NULL ) ;
if ( underlined )
2024-07-30 01:49:32 -04:00
SelectObject ( m_activeHdc , m_activeFont . handle ) ;
2024-07-15 17:13:18 -04:00
if ( moveY )
posY = rect . bottom ;
} ;
2024-07-30 01:49:32 -04:00
auto drawIndentedText = [ & ] ( const StringView & text , LogEntryType type , int indent , bool moveY , bool underlined = false )
{
int posX = 5 + indent * m_defaultFont . height ;
drawStatusText ( text , type , posX , clientRect . right , moveY , underlined ) ;
} ;
2024-07-15 17:13:18 -04:00
if ( m_config . showProgress & & m_traceView . progressProcessesTotal )
{
2024-07-30 01:49:32 -04:00
drawIndentedText ( ToView ( L " Progress " ) , LogEntryType_Info , 1 , false ) ;
2024-07-15 17:13:18 -04:00
float progress = float ( m_traceView . progressProcessesDone ) / m_traceView . progressProcessesTotal ;
2024-07-30 01:49:32 -04:00
u32 width = m_activeFont . height * 18 ;
2024-07-15 17:13:18 -04:00
RECT rect ;
2024-07-30 01:49:32 -04:00
rect . left = 3 + 6 * m_activeFont . height ;
2024-07-15 17:13:18 -04:00
rect . right = rect . left + width ;
rect . top = posY ;
2024-07-30 01:49:32 -04:00
rect . bottom = posY + m_activeFont . height ;
2024-07-15 17:13:18 -04:00
FillRect ( hdc , & rect , m_processBrushes [ 0 ] . inProgress ) ;
rect . right = rect . left + int ( progress * width ) ; //durationMs/100;
FillRect ( hdc , & rect , m_traceView . progressErrorCount ? m_processBrushes [ 0 ] . error : m_processBrushes [ 0 ] . success ) ;
2024-08-09 17:06:11 -04:00
const tchar * remoteDisabled = TC ( " " ) ; // TODO: Only show this in "advanced mode" m_traceView.remoteExecutionDisabled ? TC(" (Remote spawn disabled)") : TC("");
2024-07-15 17:13:18 -04:00
StringBuffer < > str ;
2024-07-30 01:49:32 -04:00
str . Appendf ( L " %u%% %u / %u %s " , u32 ( progress * 100.0f ) , m_traceView . progressProcessesDone , m_traceView . progressProcessesTotal , remoteDisabled ) ;
drawIndentedText ( str , LogEntryType_Info , 6 , true ) ;
2024-07-15 17:13:18 -04:00
}
2024-07-16 16:27:53 -04:00
if ( m_config . showStatus & & ! m_traceView . statusMap . empty ( ) )
2024-02-02 00:30:28 -05:00
{
2024-07-15 17:13:18 -04:00
u32 lastRow = ~ 0u ;
u32 row = ~ 0u ;
2024-02-02 00:30:28 -05:00
for ( auto & kv : m_traceView . statusMap )
{
auto & status = kv . second ;
2024-07-15 17:13:18 -04:00
if ( status . text . empty ( ) )
2024-02-02 00:30:28 -05:00
continue ;
2024-07-15 17:13:18 -04:00
row = u32 ( kv . first > > 32 ) ;
if ( lastRow ! = ~ 0u & & lastRow ! = row )
2024-07-30 01:49:32 -04:00
posY + = m_activeFont . height + 2 ;
2024-07-15 17:13:18 -04:00
lastRow = row ;
u32 column = u32 ( kv . first & ~ 0u ) ;
2024-07-30 01:49:32 -04:00
drawIndentedText ( status . text , status . type , column , false , ! status . link . empty ( ) ) ;
2024-02-02 00:30:28 -05:00
}
2024-07-15 17:13:18 -04:00
if ( row ! = ~ 0u )
2024-07-30 01:49:32 -04:00
posY + = m_activeFont . height + 2 ;
2024-07-15 17:13:18 -04:00
2024-02-02 00:30:28 -05:00
SetTextColor ( hdc , m_textColor ) ;
2024-07-16 16:27:53 -04:00
posY + = 3 ;
2024-02-02 00:30:28 -05:00
}
2024-07-16 16:27:53 -04:00
2024-07-15 17:13:18 -04:00
if ( m_config . showActiveProcesses & & ! m_trace . m_activeProcesses . empty ( ) )
{
2024-08-06 15:31:53 -04:00
auto drawBox = [ & ] ( u64 start , u64 stop , int height , bool selected , bool inProgress )
2024-07-15 17:13:18 -04:00
{
2024-07-30 01:49:32 -04:00
//int left = 3 + 6*m_activeFont.height;
//int right = rect.left + durationMs/100;
int posX = int ( m_scrollPosX ) + progressRect . left ;
bool done = stop ! = ~ u64 ( 0 ) ;
if ( ! done )
stop = playTime ;
int left = int ( posX + TimeToS ( start ) * scaleX ) ;
int right = int ( posX + TimeToS ( stop ) * scaleX ) - 1 ;
2024-07-15 17:13:18 -04:00
2024-07-30 01:49:32 -04:00
RECT rect ;
rect . left = left ;
rect . right = right ;
rect . top = posY ;
rect . bottom = posY + height ;
2024-08-06 15:31:53 -04:00
FillRect ( hdc , & rect , inProgress ? m_processBrushes [ selected ] . inProgress : m_processBrushes [ selected ] . success ) ;
2024-07-30 01:49:32 -04:00
return rect ;
} ;
m_activeProcessCountHistory [ m_activeProcessCountHistoryIterator + + % sizeof_array ( m_activeProcessCountHistory ) ] = u32 ( m_trace . m_activeProcesses . size ( ) ) ;
PaintActiveProcesses ( posY , clientRect , [ & ] ( TraceView : : ProcessLocation & processLocation , u32 boxHeight , bool firstWithHeight )
{
auto & session = m_trace . GetSession ( m_traceView , processLocation . sessionIndex ) ;
TraceView : : Process & process = session . processors [ processLocation . processorIndex ] . processes [ processLocation . processIndex ] ;
bool selected = m_processSelected & & m_processSelectedLocation = = processLocation ;
2024-08-06 15:31:53 -04:00
if ( m_config . showFinishedProcesses )
{
u32 index = processLocation . processIndex ;
while ( index > 0 )
{
- - index ;
TraceView : : Process & process2 = session . processors [ processLocation . processorIndex ] . processes [ index ] ;
drawBox ( process2 . start , process2 . stop , boxHeight , false , false ) ;
}
}
RECT boxRect = drawBox ( process . start , process . stop , boxHeight , selected , true ) ;
2024-07-30 01:49:32 -04:00
u32 v = boxHeight - 1 ;
2024-08-06 15:31:53 -04:00
if ( v > 4 )
2024-07-30 01:49:32 -04:00
{
u32 fontIndex = Min ( v , u32 ( sizeof_array ( m_activeProcessFont ) - 1 ) ) ;
if ( ! m_activeProcessFont [ fontIndex ] . handle )
2024-08-06 15:31:53 -04:00
{
UpdateFont ( m_activeProcessFont [ fontIndex ] , fontIndex - 1 , false ) ;
m_activeProcessFont [ fontIndex ] . offset + = 1 ;
}
2024-07-30 01:49:32 -04:00
if ( firstWithHeight )
SetActiveFont ( m_activeProcessFont [ fontIndex ] ) ;
StringBuffer < > str ;
auto firstPar = - 1 ; //process.description.find_first_of('(');
if ( firstPar ! = - 1 & & * process . description . rbegin ( ) = = ' ) ' )
{
str . Append ( process . description . c_str ( ) + firstPar + 1 ) . Resize ( str . count - 1 ) ;
str . Append ( L " " ) . Append ( process . description . c_str ( ) , firstPar ) ;
}
else
str . Append ( process . description ) ;
if ( process . isRemote )
str . Append ( L " [ " ) . Append ( session . name ) . Append ( ' ] ' ) ;
else if ( process . cacheFetch )
str . Append ( L " [cache] " ) ;
if ( boxRect . left < 0 )
str . Appendf ( L " %s " , TimeToText ( playTime - process . start , true ) . str ) ;
//drawIndentedText(str, LogEntryType_Info, 1, true);
drawStatusText ( str , LogEntryType_Info , Max ( int ( boxRect . left + 1 ) , 1 ) , boxRect . right , false ) ;
}
} ) ;
2024-07-15 17:13:18 -04:00
}
2024-08-09 17:06:11 -04:00
int boxHeight = m_boxHeight ;
2024-07-30 01:49:32 -04:00
int stepY = int ( boxHeight ) + 2 ;
2024-08-09 17:06:11 -04:00
int processStepY = boxHeight + 1 ;
2024-07-30 01:49:32 -04:00
2024-03-03 20:42:41 -05:00
TraceView : : WorkRecord selectedWork ;
2024-02-02 00:30:28 -05:00
2024-03-08 18:31:48 -05:00
SessionRec sortedSessions [ 1024 ] ;
2024-07-17 17:48:42 -04:00
Populate ( sortedSessions , m_traceView , m_config . SortActiveRemoteSessions ) ;
2023-11-29 18:47:11 -05:00
TraceView : : ProcessLocation processLocation { 0 , 0 , 0 } ;
for ( u64 i = 0 , e = m_traceView . sessions . size ( ) ; i ! = e ; + + i )
{
2024-07-15 17:13:18 -04:00
bool isFirst = i = = 0 ;
2023-11-29 18:47:11 -05:00
auto & session = * sortedSessions [ i ] . session ;
2024-01-10 03:05:23 -05:00
bool hasUpdates = ! session . updates . empty ( ) ;
2024-07-19 19:12:12 -04:00
if ( ! isFirst )
{
if ( ! hasUpdates & & session . processors . empty ( ) )
continue ;
if ( ! m_config . showFinishedProcesses & & session . disconnectTime ! = ~ u64 ( 0 ) )
continue ;
}
2024-01-10 03:05:23 -05:00
2023-11-29 18:47:11 -05:00
processLocation . sessionIndex = sortedSessions [ i ] . index ;
if ( ! isFirst )
posY + = 3 ;
2024-07-15 17:13:18 -04:00
if ( m_config . showTitleBars )
2023-11-29 18:47:11 -05:00
{
2024-07-15 17:13:18 -04:00
if ( posY + stepY > = progressRect . top & & posY < = progressRect . bottom )
2023-11-29 18:47:11 -05:00
{
2024-07-15 17:13:18 -04:00
SelectObject ( hdc , m_separatorPen ) ;
MoveToEx ( hdc , 0 , posY , NULL ) ;
LineTo ( hdc , clientRect . right , posY ) ;
2023-11-29 18:47:11 -05:00
2024-07-15 17:13:18 -04:00
StringBuffer < > text ;
2024-07-30 01:49:32 -04:00
text . Append ( session . fullName ) ;
2023-11-29 18:47:11 -05:00
2024-07-15 17:13:18 -04:00
if ( hasUpdates & & session . disconnectTime = = ~ u64 ( 0 ) )
{
u64 ping = session . updates . back ( ) . ping ;
u64 memAvail = session . updates . back ( ) . memAvail ;
float cpuLoad = session . updates . back ( ) . cpuLoad ;
text . Appendf ( L " - Cpu: %.1f%% " , cpuLoad * 100.0f ) ;
if ( memAvail )
text . Appendf ( L " Mem: %ls/%ls " , BytesToText ( session . memTotal - memAvail ) . str , BytesToText ( session . memTotal ) . str ) ;
if ( ping )
text . Appendf ( L " Ping: %ls " , TimeToText ( ping , false , m_traceView . frequency ) . str ) ;
if ( ! session . notification . empty ( ) )
text . Append ( L " - " ) . Append ( session . notification ) ;
}
else if ( ! isFirst )
{
text . Append ( L " - Disconnected " ) ;
if ( ! session . notification . empty ( ) )
text . Append ( L " ( " ) . Append ( session . notification ) . Append ( ' ) ' ) ;
}
bool selected = m_sessionSelectedIndex = = processLocation . sessionIndex ;
int textBottom = Min ( posY + m_sessionStepY , int ( progressRect . bottom ) ) ;
2023-11-29 18:47:11 -05:00
RECT rect ;
rect . left = 5 ;
rect . right = clientRect . right ;
rect . top = posY ;
rect . bottom = textBottom ;
if ( selected )
SetBkMode ( hdc , OPAQUE ) ;
ExtTextOutW ( hdc , 5 , posY + 2 , ETO_CLIPPED , & rect , text . data , text . count , NULL ) ;
if ( selected )
SetBkMode ( hdc , TRANSPARENT ) ;
}
2024-07-15 17:13:18 -04:00
posY + = m_sessionStepY ;
2023-11-29 18:47:11 -05:00
}
2024-07-10 18:24:10 -04:00
bool showGraph = m_config . showNetworkStats | | m_config . showCpuMemStats ;
2024-01-10 03:05:23 -05:00
if ( showGraph & & hasUpdates )
2023-11-29 18:47:11 -05:00
{
if ( posY + GraphHeight > = progressRect . top & & posY + GraphHeight - 5 < progressRect . bottom )
{
int posX = int ( m_scrollPosX ) + progressRect . left ;
bool isFirstUpdate = true ;
u64 prevTime = 0 ;
u64 prevSend = 0 ;
u64 prevRecv = 0 ;
float prevCupLoad = 0 ;
int graphBaseY = posY + GraphHeight - 4 ;
int prevX = 0 ;
int prevSendY = 0 ;
int prevRecvY = 0 ;
int prevCpuY = 0 ;
int prevMemY = 0 ;
double sendScale = double ( session . highestSendPerS ) / ( double ( GraphHeight ) - 2 ) ;
double recvScale = double ( session . highestRecvPerS ) / ( double ( GraphHeight ) - 2 ) ;
2024-01-16 15:35:42 -05:00
2023-11-29 18:47:11 -05:00
for ( auto & update : session . updates )
{
float cpuLoad = update . cpuLoad ;
if ( cpuLoad < 0 | | cpuLoad > 1.0f )
cpuLoad = prevCupLoad ;
else
prevCupLoad = cpuLoad ;
2024-01-16 15:35:42 -05:00
auto updateSend = update . send ;
auto updateRecv = update . recv ;
2023-11-29 18:47:11 -05:00
int x = int ( posX + TimeToS ( update . time ) * scaleX ) ;
int sendY = graphBaseY ;
int recvY = graphBaseY ;
int cpuY = graphBaseY - int ( cpuLoad * ( GraphHeight - 2 ) ) ;
int memY = graphBaseY - int ( double ( session . memTotal - update . memAvail ) * ( GraphHeight - 2 ) / session . memTotal ) ;
double duration = TimeToS ( update . time - prevTime ) ;
if ( update . time = = 0 )
isFirstUpdate = true ;
2024-01-16 15:35:42 -05:00
else if ( prevSend > updateSend | | prevRecv > updateRecv )
2024-01-10 03:05:23 -05:00
isFirstUpdate = true ;
2023-11-29 18:47:11 -05:00
if ( double sendInvScaleY = duration * sendScale )
2024-01-16 15:35:42 -05:00
sendY = graphBaseY - int ( double ( updateSend - prevSend ) / sendInvScaleY ) ;
2023-11-29 18:47:11 -05:00
if ( double recvInvScaleY = duration * recvScale )
2024-01-16 15:35:42 -05:00
recvY = graphBaseY - int ( double ( updateRecv - prevRecv ) / recvInvScaleY ) - 1 ;
2023-11-29 18:47:11 -05:00
if ( ! isFirstUpdate & & x > clientRect . left & & prevX < = clientRect . right )
{
2024-07-10 18:24:10 -04:00
if ( m_config . showNetworkStats & & updateSend ! = 0 & & updateRecv ! = 0 )
2023-11-29 18:47:11 -05:00
{
2024-03-03 20:42:41 -05:00
SelectObject ( hdc , m_sendPen ) ;
2023-11-29 18:47:11 -05:00
MoveToEx ( hdc , prevX , prevSendY , NULL ) ;
LineTo ( hdc , x , sendY ) ;
2024-03-03 20:42:41 -05:00
SelectObject ( hdc , m_recvPen ) ;
2023-11-29 18:47:11 -05:00
MoveToEx ( hdc , prevX , prevRecvY , NULL ) ;
LineTo ( hdc , x , recvY ) ;
}
2024-07-10 18:24:10 -04:00
if ( m_config . showCpuMemStats )
2023-11-29 18:47:11 -05:00
{
SelectObject ( hdc , m_cpuPen ) ;
MoveToEx ( hdc , prevX , prevCpuY , NULL ) ;
LineTo ( hdc , x , cpuY ) ;
SelectObject ( hdc , m_memPen ) ;
MoveToEx ( hdc , prevX , prevMemY , NULL ) ;
LineTo ( hdc , x , memY ) ;
}
}
isFirstUpdate = false ;
prevX = x ;
prevSendY = sendY ;
prevRecvY = recvY ;
prevCpuY = cpuY ;
prevMemY = memY ;
prevTime = update . time ;
2024-01-16 15:35:42 -05:00
prevSend = updateSend ;
prevRecv = updateRecv ;
2023-11-29 18:47:11 -05:00
}
}
posY + = GraphHeight ;
}
2024-07-10 18:24:10 -04:00
if ( m_config . showDetailedData )
2023-12-22 02:41:43 -05:00
{
2024-01-02 17:42:42 -05:00
auto drawText = [ & ] ( const StringBufferBase & text , RECT & rect )
{
bool selected = m_fetchedFilesSelected = = processLocation . sessionIndex & & text . StartsWith ( TC ( " Fetched Files " ) ) ;
if ( selected )
SetBkMode ( hdc , OPAQUE ) ;
DrawTextW ( hdc , text . data , text . count , & rect , DT_SINGLELINE ) ;
if ( selected )
SetBkMode ( hdc , TRANSPARENT ) ;
} ;
2024-03-03 20:42:41 -05:00
bool isRemote = processLocation . sessionIndex ! = 0 ;
2024-01-16 15:35:42 -05:00
PaintDetailedStats ( posY , progressRect , session , isRemote , playTime , drawText ) ;
2023-12-22 02:41:43 -05:00
}
2023-11-29 18:47:11 -05:00
2024-07-30 01:49:32 -04:00
SetActiveFont ( m_processFont ) ;
2024-07-17 17:48:42 -04:00
2024-08-09 17:06:11 -04:00
bool shouldDrawText = m_processFont . height > 4 ;
2024-08-06 15:31:53 -04:00
2024-07-10 18:24:10 -04:00
if ( m_config . showProcessBars )
2023-11-29 18:47:11 -05:00
{
processLocation . processorIndex = 0 ;
for ( auto & processor : session . processors )
{
2024-07-19 19:12:12 -04:00
bool drawProcessorIndex = m_config . showFinishedProcesses ;
2024-07-15 17:13:18 -04:00
if ( posY + m_sessionStepY > = progressRect . top & & posY < progressRect . bottom )
2023-11-29 18:47:11 -05:00
{
2024-08-09 17:06:11 -04:00
int barHeight = boxHeight ;
2023-11-29 18:47:11 -05:00
int textOffsetY = 0 ;
2024-08-09 17:06:11 -04:00
if ( posY + boxHeight > progressRect . bottom )
2023-11-29 18:47:11 -05:00
{
2024-08-09 17:06:11 -04:00
int newBarHeight = Min ( barHeight , int ( progressRect . bottom - posY ) ) ;
2023-11-29 18:47:11 -05:00
textOffsetY = int ( barHeight - newBarHeight ) ;
barHeight = newBarHeight ;
}
2024-08-09 17:06:11 -04:00
const int textHeight = barHeight ;
2023-11-29 18:47:11 -05:00
const int rectBottom = posY + textHeight ;
2024-07-30 01:49:32 -04:00
const int offsetY = ( textHeight - m_processFont . height + textOffsetY ) / 2 ;
2023-11-29 18:47:11 -05:00
processLocation . processIndex = 0 ;
int posX = int ( m_scrollPosX ) + progressRect . left ;
for ( auto & process : processor . processes )
{
int left = int ( posX + TimeToS ( process . start ) * scaleX ) ;
2024-07-19 19:12:12 -04:00
auto pig = MakeGuard ( [ & ] ( ) { + + processLocation . processIndex ; } ) ;
2023-11-29 18:47:11 -05:00
if ( left > = progressRect . right )
continue ;
u64 stop = process . stop ;
bool done = stop ! = ~ u64 ( 0 ) ;
if ( ! done )
stop = playTime ;
2024-07-19 19:12:12 -04:00
else if ( ! m_config . showFinishedProcesses )
continue ;
drawProcessorIndex = true ;
2023-11-29 18:47:11 -05:00
RECT rect ;
rect . left = left ;
rect . right = int ( posX + TimeToS ( stop ) * scaleX ) - 1 ;
rect . top = posY ;
2024-08-09 17:06:11 -04:00
rect . bottom = rectBottom - 1 ;
2023-11-29 18:47:11 -05:00
if ( rect . right < = progressRect . left )
continue ;
rect . right = Max ( int ( rect . right ) , left + 1 ) ;
bool selected = m_processSelected & & m_processSelectedLocation = = processLocation ;
if ( selected )
process . bitmapDirty = true ;
- - rect . top ;
PaintProcessRect ( process , hdc , rect , progressRect , selected , false ) ;
+ + rect . top ;
int processWidth = rect . right - rect . left ;
2024-07-10 18:24:10 -04:00
if ( shouldDrawText & & m_config . ShowProcessText & & processWidth > 3 )
2023-11-29 18:47:11 -05:00
{
if ( ! process . bitmap | | process . bitmapDirty )
{
if ( ! process . bitmap )
{
if ( m_lastBitmapOffset = = BitmapCacheHeight )
{
if ( m_lastBitmap )
m_textBitmaps . push_back ( m_lastBitmap ) ;
m_lastBitmapOffset = 0 ;
m_lastBitmap = CreateCompatibleBitmap ( hdc , 256 , BitmapCacheHeight ) ;
}
process . bitmap = m_lastBitmap ;
process . bitmapOffset = m_lastBitmapOffset ;
2024-07-30 01:49:32 -04:00
m_lastBitmapOffset + = m_processFont . height ;
2023-11-29 18:47:11 -05:00
}
if ( lastSelectedBitmap ! = process . bitmap )
{
SelectObject ( textDC , process . bitmap ) ;
lastSelectedBitmap = process . bitmap ;
}
2024-07-30 01:49:32 -04:00
RECT rect2 { 0 , int ( process . bitmapOffset ) , 256 , int ( process . bitmapOffset ) + m_processFont . height } ;
RECT rect3 { 0 , int ( process . bitmapOffset ) , processWidth , int ( process . bitmapOffset ) + m_processFont . height } ;
2023-11-29 18:47:11 -05:00
if ( ! done )
rect3 . right = 256 ;
PaintProcessRect ( process , textDC , rect3 , rect2 , selected , true ) ;
rect2 . left + = 3 ; // Move in text a bit
2024-08-09 17:06:11 -04:00
int textY = rect2 . top + m_processFont . offset ;
2024-07-17 17:48:42 -04:00
2024-07-10 18:24:10 -04:00
bool dropShadow = m_config . DarkMode ;
2023-11-29 18:47:11 -05:00
if ( dropShadow )
{
SetTextColor ( textDC , RGB ( 5 , 60 , 5 ) ) ;
+ + rect2 . left ;
2024-07-17 17:48:42 -04:00
+ + textY ;
ExtTextOutW ( textDC , rect2 . left , textY , ETO_CLIPPED , & rect2 , process . description . c_str ( ) , int ( process . description . size ( ) ) , NULL ) ;
2023-11-29 18:47:11 -05:00
- - rect2 . left ;
2024-07-17 17:48:42 -04:00
- - textY ;
2023-11-29 18:47:11 -05:00
}
SetTextColor ( textDC , m_textColor ) ;
2024-07-17 17:48:42 -04:00
ExtTextOutW ( textDC , rect2 . left , textY , ETO_CLIPPED , & rect2 , process . description . c_str ( ) , int ( process . description . size ( ) ) , NULL ) ;
2023-11-29 18:47:11 -05:00
if ( ! selected )
process . bitmapDirty = false ;
}
if ( lastSelectedBitmap ! = process . bitmap )
{
SelectObject ( textDC , process . bitmap ) ;
lastSelectedBitmap = process . bitmap ;
}
int width = Min ( processWidth , 256 ) ;
int bitmapOffsetY = process . bitmapOffset ;
int bltOffsetY = offsetY ;
if ( bltOffsetY < 0 )
{
bitmapOffsetY - = bltOffsetY ;
bltOffsetY = 0 ;
}
2024-07-30 01:49:32 -04:00
int height = Min ( textHeight , m_processFont . height ) ;
2023-11-29 18:47:11 -05:00
if ( bltOffsetY + height > textHeight )
height = textHeight - bltOffsetY ;
if ( left > - 256 & & height > = 0 )
{
int bitmapOffsetX = rect . left - left ;
if ( left < progressRect . left )
{
int diff = progressRect . left - left ;
rect . left = progressRect . left ;
width - = diff ;
bitmapOffsetX + = diff ;
}
BitBlt ( hdc , rect . left , rect . top + bltOffsetY , width , height , textDC , bitmapOffsetX , bitmapOffsetY , SRCCOPY ) ;
}
}
2024-07-19 19:12:12 -04:00
}
if ( drawProcessorIndex )
{
RECT rect ;
rect . left = 5 ;
rect . right = progressRect . left - 5 ;
rect . top = posY ;
rect . bottom = rectBottom ;
StringBuffer < > buf ;
buf . AppendValue ( u64 ( processLocation . processorIndex ) + 1 ) ;
ExtTextOutW ( hdc , 5 , posY + offsetY , ETO_CLIPPED , & rect , buf . data , buf . count , NULL ) ;
2023-11-29 18:47:11 -05:00
}
}
lastStop = Max ( lastStop , processor . processes . rbegin ( ) - > stop ) ;
+ + processLocation . processorIndex ;
2024-07-19 19:12:12 -04:00
if ( drawProcessorIndex )
posY + = processStepY ;
2023-11-29 18:47:11 -05:00
}
}
else
{
for ( auto & processor : session . processors )
if ( ! processor . processes . empty ( ) )
lastStop = Max ( lastStop , processor . processes . rbegin ( ) - > stop ) ;
}
2024-07-10 18:24:10 -04:00
if ( m_config . showWorkers & & isFirst )
2023-11-29 18:47:11 -05:00
{
2024-03-03 20:42:41 -05:00
u32 trackIndex = 0 ;
2023-11-29 18:47:11 -05:00
for ( auto & workTrack : m_traceView . workTracks )
{
2024-07-15 17:13:18 -04:00
if ( posY + m_sessionStepY > = progressRect . top & & posY < = progressRect . bottom )
2023-11-29 18:47:11 -05:00
{
int textOffsetY = 0 ;
2024-08-09 17:06:11 -04:00
int barHeight = boxHeight ;
2023-11-29 18:47:11 -05:00
if ( posY + int ( boxHeight ) > progressRect . bottom )
{
2024-08-09 17:06:11 -04:00
int newBarHeight = Min ( barHeight , int ( progressRect . bottom - posY ) ) ;
textOffsetY = barHeight - newBarHeight ;
2023-11-29 18:47:11 -05:00
barHeight = newBarHeight ;
}
2024-08-09 17:06:11 -04:00
const int textHeight = barHeight ;
2023-11-29 18:47:11 -05:00
const int rectBottom = posY + textHeight ;
2024-07-30 01:49:32 -04:00
const int offsetY = ( textHeight - m_processFont . height + textOffsetY ) / 2 ;
2023-11-29 18:47:11 -05:00
if ( shouldDrawText )
{
RECT rect ;
rect . left = 5 ;
rect . right = progressRect . left - 5 ;
rect . top = posY ;
rect . bottom = rectBottom ;
StringBuffer < > buf ;
buf . AppendValue ( u64 ( trackIndex ) + 1 ) ;
ExtTextOutW ( hdc , 5 , posY + offsetY , ETO_CLIPPED , & rect , buf . data , buf . count , NULL ) ;
}
2024-03-03 20:42:41 -05:00
u32 workIndex = 0 ;
2023-11-29 18:47:11 -05:00
int posX = int ( m_scrollPosX ) + progressRect . left ;
2024-03-03 20:42:41 -05:00
for ( auto & work : workTrack . records )
2023-11-29 18:47:11 -05:00
{
2024-03-03 20:42:41 -05:00
if ( work . start = = work . stop )
2023-11-29 18:47:11 -05:00
{
2024-03-03 20:42:41 -05:00
+ + workIndex ;
2023-11-29 18:47:11 -05:00
continue ;
}
2024-03-03 20:42:41 -05:00
float startTime = TimeToS ( work . start ) ;
2023-11-29 18:47:11 -05:00
int left = int ( posX + startTime * scaleX ) ;
if ( left > = progressRect . right )
{
2024-03-03 20:42:41 -05:00
+ + workIndex ;
2023-11-29 18:47:11 -05:00
continue ;
}
HBRUSH brush ;
2024-03-03 20:42:41 -05:00
u64 stop = work . stop ;
2023-11-29 18:47:11 -05:00
bool done = stop ! = ~ u64 ( 0 ) ;
if ( done )
{
brush = m_workBrush ;
}
else
{
stop = playTime ;
brush = m_processBrushes [ 0 ] . inProgress ;
}
float stopTime = TimeToS ( stop ) ;
if ( ( stopTime - startTime ) * scaleX < 0.05f )
{
2024-03-03 20:42:41 -05:00
+ + workIndex ;
2023-11-29 18:47:11 -05:00
continue ;
}
RECT rect ;
rect . left = left ;
rect . right = int ( posX + stopTime * scaleX ) - 1 ;
rect . top = posY ;
rect . bottom = rectBottom ;
if ( rect . right < = progressRect . left )
{
2024-03-03 20:42:41 -05:00
+ + workIndex ;
2023-11-29 18:47:11 -05:00
continue ;
}
rect . right = Max ( int ( rect . right ) , left + 1 ) ;
2024-03-03 20:42:41 -05:00
bool selected = m_workSelected & & m_workTrack = = trackIndex & & m_workIndex = = workIndex ;
if ( selected )
selectedWork = work ;
2023-11-29 18:47:11 -05:00
if ( lastSelectedBrush ! = brush )
{
SelectObject ( hdc , brush ) ;
lastSelectedBrush = brush ;
}
- - rect . top ;
auto clampRect = [ & ] ( RECT & r ) { r . left = Min ( Max ( r . left , progressRect . left ) , progressRect . right ) ; r . right = Max ( Min ( r . right , progressRect . right ) , progressRect . left ) ; } ;
clampRect ( rect ) ;
FillRect ( hdc , & rect , brush ) ;
+ + rect . top ;
int processWidth = rect . right - rect . left ;
2024-07-10 18:24:10 -04:00
if ( shouldDrawText & & m_config . ShowProcessText & & processWidth > 3 )
2023-11-29 18:47:11 -05:00
{
2024-03-03 20:42:41 -05:00
if ( ! work . bitmap )
2023-11-29 18:47:11 -05:00
{
if ( m_lastBitmapOffset = = BitmapCacheHeight )
{
if ( m_lastBitmap )
m_textBitmaps . push_back ( m_lastBitmap ) ;
m_lastBitmapOffset = 0 ;
m_lastBitmap = CreateCompatibleBitmap ( hdc , 256 , BitmapCacheHeight ) ;
}
SelectObject ( textDC , m_lastBitmap ) ;
2024-07-30 01:49:32 -04:00
RECT rect2 { 0 , m_lastBitmapOffset , 256 , m_lastBitmapOffset + m_processFont . height } ;
2023-11-29 18:47:11 -05:00
FillRect ( textDC , & rect2 , m_workBrush ) ;
2024-03-03 20:42:41 -05:00
ExtTextOutW ( textDC , rect2 . left , rect2 . top , ETO_CLIPPED , & rect2 , work . description , int ( wcslen ( work . description ) ) , NULL ) ;
work . bitmap = m_lastBitmap ;
work . bitmapOffset = m_lastBitmapOffset ;
2024-07-30 01:49:32 -04:00
m_lastBitmapOffset + = m_processFont . height ;
2023-11-29 18:47:11 -05:00
}
2024-03-03 20:42:41 -05:00
if ( lastSelectedBitmap ! = work . bitmap )
2023-11-29 18:47:11 -05:00
{
2024-03-03 20:42:41 -05:00
SelectObject ( textDC , work . bitmap ) ;
lastSelectedBitmap = work . bitmap ;
2023-11-29 18:47:11 -05:00
}
int width = Min ( processWidth , 256 ) ;
2024-03-03 20:42:41 -05:00
int bitmapOffsetY = work . bitmapOffset ;
2023-11-29 18:47:11 -05:00
int bltOffsetY = offsetY ;
if ( bltOffsetY < 0 )
{
bitmapOffsetY - = bltOffsetY ;
bltOffsetY = 0 ;
}
2024-07-30 01:49:32 -04:00
int height = Min ( textHeight , m_processFont . height ) ;
2023-11-29 18:47:11 -05:00
if ( bltOffsetY + height > textHeight )
height = textHeight - bltOffsetY ;
if ( left > - 256 & & height > = 0 )
{
int bitmapOffsetX = rect . left - left ;
if ( left < progressRect . left )
{
int diff = progressRect . left - left ;
rect . left = progressRect . left ;
width - = diff ;
bitmapOffsetX + = diff ;
}
BitBlt ( hdc , rect . left , rect . top + bltOffsetY , width , height , textDC , bitmapOffsetX , bitmapOffsetY , SRCCOPY ) ;
}
}
2024-03-03 20:42:41 -05:00
+ + workIndex ;
2023-11-29 18:47:11 -05:00
}
}
+ + trackIndex ;
posY + = stepY ;
}
}
2024-07-17 17:48:42 -04:00
2024-07-30 01:49:32 -04:00
SetActiveFont ( m_defaultFont ) ;
2023-11-29 18:47:11 -05:00
}
SelectObject ( textDC , oldBmp ) ;
DeleteObject ( nullBmp ) ;
DeleteDC ( textDC ) ;
2024-07-17 17:48:42 -04:00
m_contentWidth = m_progressRectLeft + Max ( 0 , int ( TimeToS ( ( lastStop ! = 0 & & lastStop ! = ~ u64 ( 0 ) ) ? lastStop : playTime ) * scaleX ) ) ;
2024-07-30 01:49:32 -04:00
2023-11-29 18:47:11 -05:00
m_contentHeight = posY - int ( m_scrollPosY ) + stepY + 14 ;
2024-05-23 19:16:15 -04:00
float timelineSelected = m_timelineSelected ;
2024-07-10 18:24:10 -04:00
if ( m_config . showTimeline & & ! m_traceView . sessions . empty ( ) )
2023-11-29 18:47:11 -05:00
PaintTimeline ( hdc , clientRect ) ;
2024-05-23 19:16:15 -04:00
2024-07-10 18:24:10 -04:00
if ( m_config . showCursorLine & & m_mouseOverWindow )
2024-05-23 19:16:15 -04:00
{
float timeScale = ( m_horizontalScaleValue * m_zoomValue ) * 50.0f ;
float startOffset = - ( m_scrollPosX / timeScale ) ;
POINT pos ;
GetCursorPos ( & pos ) ;
ScreenToClient ( m_hwnd , & pos ) ;
2024-07-17 17:48:42 -04:00
timelineSelected = startOffset + ( pos . x - m_progressRectLeft ) / timeScale ;
2024-05-23 19:16:15 -04:00
}
if ( timelineSelected )
{
int posX = int ( m_scrollPosX ) + progressRect . left ;
int left = int ( posX + timelineSelected * scaleX ) ;
2024-07-19 19:12:12 -04:00
int timelineTop = GetTimelineTop ( clientRect ) ;
2024-05-23 19:16:15 -04:00
// TODO: Draw line up
MoveToEx ( hdc , left , 2 , NULL ) ;
LineTo ( hdc , left , timelineTop ) ;
if ( timelineSelected > = 0 )
{
StringBuffer < > b ;
u32 milliseconds = u32 ( timelineSelected * 1000.0f ) ;
u32 seconds = milliseconds / 1000 ;
milliseconds - = seconds * 1000 ;
u32 minutes = seconds / 60 ;
seconds - = minutes * 60 ;
u32 hours = minutes / 60 ;
minutes - = hours * 60 ;
if ( hours )
{
b . AppendValue ( hours ) . Append ( ' h ' ) ;
if ( minutes < 10 )
b . Append ( ' 0 ' ) ;
}
if ( minutes | | hours )
{
b . AppendValue ( minutes ) . Append ( ' m ' ) ;
if ( seconds < 10 )
b . Append ( ' 0 ' ) ;
}
b . AppendValue ( seconds ) . Append ( ' . ' ) ;
if ( milliseconds < 100 )
b . Append ( ' 0 ' ) ;
if ( milliseconds < 10 )
b . Append ( ' 0 ' ) ;
b . AppendValue ( milliseconds ) ;
2024-07-30 01:49:32 -04:00
SetActiveFont ( m_popupFont ) ;
DrawTextLogger logger ( m_hwnd , hdc , m_popupFont . height , m_tooltipBackgroundBrush ) ;
2024-05-23 19:16:15 -04:00
logger . Info ( L " %s " , b . data ) ;
logger . DrawAtPos ( left + 4 , timelineTop - 20 ) ;
}
}
2023-11-29 18:47:11 -05:00
{
2024-07-15 17:13:18 -04:00
int boxSide = 8 ;
int boxStride = boxSide + 2 ;
2023-11-29 18:47:11 -05:00
int top = 5 ;
2024-07-15 17:13:18 -04:00
int bottom = top + boxSide ;
int left = progressRect . right - 7 - boxSide ;
2023-11-29 18:47:11 -05:00
int right = progressRect . right - 7 ;
2024-07-15 17:13:18 -04:00
bool * values = & m_config . showProgress ;
2024-07-10 18:24:10 -04:00
for ( int i = VisualizerFlag_Count - 1 ; i > = 0 ; - - i )
2023-11-29 18:47:11 -05:00
{
SelectObject ( hdc , m_buttonSelected = = u32 ( i ) ? m_textPen : m_checkboxPen ) ;
SelectObject ( hdc , GetStockObject ( NULL_BRUSH ) ) ;
Rectangle ( hdc , left , top , right , bottom ) ;
2024-07-10 18:24:10 -04:00
if ( values [ i ] )
2023-11-29 18:47:11 -05:00
{
MoveToEx ( hdc , left + 2 , top + 2 , NULL ) ;
LineTo ( hdc , right - 2 , bottom - 2 ) ;
MoveToEx ( hdc , right - 3 , top + 2 , NULL ) ;
LineTo ( hdc , left + 1 , bottom - 2 ) ;
}
2024-07-15 17:13:18 -04:00
left - = boxStride ;
right - = boxStride ;
2023-11-29 18:47:11 -05:00
}
top - = 2 ;
2024-08-06 15:31:53 -04:00
auto drawText = [ & ] ( const tchar * text , COLORREF color )
{
SetTextColor ( hdc , color ) ;
RECT r { left , top , left + 200 , top + 200 } ;
auto strLen = TStrlen ( text ) ;
DrawTextW ( hdc , text , strLen , & r , DT_SINGLELINE | DT_NOCLIP | DT_CALCRECT ) ;
left - = ( r . right - r . left ) + 5 ;
r . left = left ;
DrawTextW ( hdc , text , strLen , & r , DT_SINGLELINE | DT_NOCLIP ) ;
} ;
2024-07-10 18:24:10 -04:00
if ( m_config . showCpuMemStats )
2023-11-29 18:47:11 -05:00
{
2024-07-30 01:49:32 -04:00
SetActiveFont ( m_defaultFont ) ;
2024-08-06 15:31:53 -04:00
drawText ( L " CPU " , m_cpuColor ) ;
drawText ( L " MEM " , m_memColor ) ;
2023-11-29 18:47:11 -05:00
}
2024-07-10 18:24:10 -04:00
if ( m_config . showNetworkStats )
2023-11-29 18:47:11 -05:00
{
2024-07-30 01:49:32 -04:00
SetActiveFont ( m_defaultFont ) ;
2024-08-06 15:31:53 -04:00
drawText ( L " SND " , m_sendColor ) ;
drawText ( L " RCV " , m_recvColor ) ;
2023-11-29 18:47:11 -05:00
}
SetTextColor ( hdc , m_textColor ) ;
}
if ( m_processSelected )
{
2024-08-06 15:31:53 -04:00
const TraceView : : Process & process = m_traceView . GetProcess ( m_processSelectedLocation ) ;
2023-11-29 18:47:11 -05:00
u64 duration = 0 ;
2024-01-17 16:49:50 -05:00
Vector < TString > logLines ;
u32 maxCharCount = 50u ;
2023-11-29 18:47:11 -05:00
bool hasExited = process . stop ! = ~ u64 ( 0 ) ;
if ( hasExited )
{
duration = process . stop - process . start ;
2024-01-17 16:49:50 -05:00
if ( ! process . logLines . empty ( ) )
{
u32 lineMaxCount = 0 ;
for ( auto & line : process . logLines )
{
u32 offset = 0 ;
u32 left = u32 ( line . text . size ( ) ) ;
while ( left )
{
u32 toCopy = Min ( left , maxCharCount ) ;
lineMaxCount = Max ( lineMaxCount , toCopy ) ;
logLines . push_back ( line . text . substr ( offset , toCopy ) ) ;
offset + = toCopy ;
left - = toCopy ;
}
}
}
2023-11-29 18:47:11 -05:00
}
else
{
duration = playTime - process . start ;
}
2024-07-30 01:49:32 -04:00
SetActiveFont ( m_popupFont ) ;
DrawTextLogger logger ( m_hwnd , hdc , m_popupFont . height , m_tooltipBackgroundBrush ) ;
2023-11-29 18:47:11 -05:00
2024-04-12 01:05:04 -04:00
logger . AddTextOffset ( - 10 ) ; // Remove spaces in the front
logger . AddWidth ( 3 ) ;
logger . AddSpace ( 2 ) ;
2023-11-29 18:47:11 -05:00
logger . Info ( L " %ls " , process . description . c_str ( ) ) ;
2024-08-16 02:16:46 -04:00
logger . Info ( L " Host: %ls " , m_processSelectedLocation . sessionIndex = = 0 ? TC ( " local " ) : m_traceView . GetSession ( m_processSelectedLocation ) . name . c_str ( ) ) ;
logger . Info ( L " ProcessId: %6u " , process . id ) ;
logger . Info ( L " Start: %7ls " , TimeToText ( process . start , true ) . str ) ;
logger . Info ( L " Duration: %7ls " , TimeToText ( duration , true ) . str ) ;
2024-08-09 18:50:03 -04:00
if ( ! process . returnedReason . empty ( ) )
logger . Info ( L " Returned: %7s " , process . returnedReason . data ( ) ) ;
2023-11-29 18:47:11 -05:00
if ( hasExited & & process . exitCode ! = 0 )
2024-05-23 19:16:15 -04:00
logger . Info ( L " ExitCode: %7u " , process . exitCode ) ;
2023-11-29 18:47:11 -05:00
2024-09-17 15:08:58 -04:00
const auto & breadcrumbs = process . breadcrumbs ;
if ( ! breadcrumbs . empty ( ) )
{
constexpr TString : : size_type maxLineLen = 37 ;
logger . Info ( L " " ) ;
logger . Info ( L " ------------ Breadcrumbs ------------ " ) ;
for ( TString : : size_type lineStart = 0 , lineEnd = 0 ; lineEnd < breadcrumbs . size ( ) ; lineStart = lineEnd + 1 )
{
// Log each individual line
lineEnd = breadcrumbs . find ( L ' \n ' , lineStart ) ;
TString line = ( lineEnd = = TString : : npos ? breadcrumbs . substr ( lineStart ) : breadcrumbs . substr ( lineStart , lineEnd - lineStart ) ) ;
// Break each line down into smaller section if they are longer than the maximum allowed length
if ( line . size ( ) > maxLineLen )
{
for ( TString : : size_type sectionStart = 0 , sectionEnd = 0 ; sectionStart < line . size ( ) ; sectionStart = sectionEnd )
{
const TString : : size_type maxSectionLen = sectionStart = = 0 ? maxLineLen : maxLineLen - 2 ;
sectionEnd = std : : min < TString : : size_type > ( sectionEnd + maxSectionLen , line . size ( ) ) ;
TString section = ( sectionStart = = 0 ? L " " : L " " ) + line . substr ( sectionStart , sectionEnd - sectionStart ) ;
logger . Info ( section . c_str ( ) ) ;
}
}
else
{
line = L " " + line ;
logger . Info ( line . c_str ( ) ) ;
}
}
}
2024-04-18 01:10:16 -04:00
if ( process . stop ! = ~ u64 ( 0 ) & & ! process . stats . empty ( ) )
2023-11-29 18:47:11 -05:00
{
2024-04-12 01:05:04 -04:00
BinaryReader reader ( process . stats . data ( ) , 0 , process . stats . size ( ) ) ;
ProcessStats processStats ;
SessionStats sessionStats ;
StorageStats storageStats ;
2024-06-02 18:14:50 -04:00
KernelStats kernelStats ;
2024-04-12 01:05:04 -04:00
CacheStats cacheStats ;
if ( process . cacheFetch )
{
2024-08-09 18:50:03 -04:00
if ( ! process . returnedReason . empty ( ) )
2024-05-23 19:16:15 -04:00
logger . Info ( L " Cache: Miss " ) ;
2024-04-24 15:26:58 -04:00
else
2024-05-23 19:16:15 -04:00
logger . Info ( L " Cache: Hit " ) ;
2024-04-12 01:05:04 -04:00
cacheStats . Read ( reader , m_traceView . version ) ;
2024-05-23 19:16:15 -04:00
if ( reader . GetLeft ( ) )
2024-04-12 01:05:04 -04:00
{
2024-06-02 18:14:50 -04:00
storageStats . Read ( reader , m_traceView . version ) ;
kernelStats . Read ( reader , m_traceView . version ) ;
2024-04-12 01:05:04 -04:00
}
}
else
{
processStats . Read ( reader , m_traceView . version ) ;
if ( reader . GetLeft ( ) )
{
2024-06-02 18:14:50 -04:00
if ( process . isRemote )
sessionStats . Read ( reader , m_traceView . version ) ;
storageStats . Read ( reader , m_traceView . version ) ;
kernelStats . Read ( reader , m_traceView . version ) ;
2024-04-12 01:05:04 -04:00
}
}
if ( processStats . hostTotalTime )
{
logger . Info ( L " " ) ;
2024-06-02 18:14:50 -04:00
logger . Info ( L " ----------- Detours stats ----------- " ) ;
2024-04-12 01:05:04 -04:00
processStats . Print ( logger , m_traceView . frequency ) ;
}
if ( ! sessionStats . IsEmpty ( ) )
2023-11-29 18:47:11 -05:00
{
logger . Info ( L " " ) ;
logger . Info ( L " ----------- Session stats ----------- " ) ;
2024-04-12 01:05:04 -04:00
sessionStats . Print ( logger , m_traceView . frequency ) ;
}
if ( ! cacheStats . IsEmpty ( ) )
{
logger . Info ( L " " ) ;
logger . Info ( L " ------------ Cache stats ------------ " ) ;
cacheStats . Print ( logger , m_traceView . frequency ) ;
}
2024-05-30 19:43:04 -04:00
if ( ! storageStats . IsEmpty ( ) )
{
logger . Info ( L " " ) ;
logger . Info ( L " ----------- Storage stats ----------- " ) ;
storageStats . Print ( logger , m_traceView . frequency ) ;
}
2024-06-02 18:14:50 -04:00
if ( ! kernelStats . IsEmpty ( ) )
2024-04-12 01:05:04 -04:00
{
2023-11-29 18:47:11 -05:00
logger . Info ( L " " ) ;
2024-06-02 18:14:50 -04:00
logger . Info ( L " ----------- Kernel stats ------------ " ) ;
kernelStats . Print ( logger , false , m_traceView . frequency ) ;
2023-11-29 18:47:11 -05:00
}
2024-01-17 16:49:50 -05:00
2024-05-23 19:16:15 -04:00
auto findIt = m_traceView . cacheWrites . find ( process . id ) ;
if ( findIt ! = m_traceView . cacheWrites . end ( ) )
{
TraceView : : CacheWrite & write = findIt - > second ;
logger . Info ( L " " ) ;
logger . Info ( L " -------- Cache write stats ---------- " ) ;
logger . Info ( L " Duration %9s " , TimeToText ( write . end - write . start ) . str ) ;
logger . Info ( L " Success %9s " , write . success ? L " true " : L " false " ) ;
logger . Info ( L " Bytes sent %9s " , BytesToText ( write . bytesSent ) . str ) ;
}
2024-01-17 16:49:50 -05:00
if ( ! logLines . empty ( ) )
{
2024-04-12 01:05:04 -04:00
logger . Info ( L " " ) ;
2024-01-17 16:49:50 -05:00
logger . Info ( L " ---------------- Log ---------------- " ) ;
2024-04-12 01:05:04 -04:00
logger . AddTextOffset ( 14 ) ;
2024-01-17 16:49:50 -05:00
for ( auto & line : logLines )
logger . Log ( LogEntryType_Info , line . c_str ( ) , u32 ( line . size ( ) ) ) ;
}
2023-11-29 18:47:11 -05:00
}
2024-04-12 01:05:04 -04:00
logger . AddSpace ( 3 ) ;
logger . DrawAtCursor ( ) ;
2023-11-29 18:47:11 -05:00
}
2024-03-03 20:42:41 -05:00
else if ( m_workSelected & & selectedWork . description )
{
u64 duration ;
if ( selectedWork . stop ! = ~ u64 ( 0 ) )
duration = selectedWork . stop - selectedWork . start ;
else
duration = playTime - selectedWork . start ;
2024-07-30 01:49:32 -04:00
SetActiveFont ( m_popupFont ) ;
DrawTextLogger logger ( m_hwnd , hdc , m_popupFont . height , m_tooltipBackgroundBrush ) ;
2024-04-12 01:05:04 -04:00
logger . AddSpace ( ) ;
2024-03-03 20:42:41 -05:00
logger . Info ( L " %ls " , selectedWork . description ) ;
logger . Info ( L " Start: %ls " , TimeToText ( selectedWork . start , true ) . str ) ;
logger . Info ( L " Duration: %ls " , TimeToText ( duration , true ) . str ) ;
2024-04-12 01:05:04 -04:00
logger . AddSpace ( ) ;
logger . DrawAtCursor ( ) ;
2024-03-03 20:42:41 -05:00
}
2023-11-29 18:47:11 -05:00
else if ( m_sessionSelectedIndex ! = ~ 0u )
{
int width = 290 ;
Vector < TString > summary = m_traceView . sessions [ m_sessionSelectedIndex ] . summary ;
if ( summary . empty ( ) )
{
if ( m_traceView . finished )
summary . push_back ( L " Session summary not available on this trace version " ) ;
else
summary . push_back ( L " Session summary not available until session is done " ) ;
summary . push_back ( L " " ) ;
width = 380 ;
}
2024-07-30 01:49:32 -04:00
int height = int ( summary . size ( ) ) * m_popupFont . height ;
2023-11-29 18:47:11 -05:00
POINT p ;
GetCursorPos ( & p ) ;
ScreenToClient ( m_hwnd , & p ) ;
RECT r ;
r . left = p . x ;
r . top = p . y ;
r . right = r . left + width ;
r . bottom = r . top + height ;
if ( r . right > clientRect . right )
OffsetRect ( & r , - width , 0 ) ;
if ( r . bottom > clientRect . bottom )
{
OffsetRect ( & r , 0 , - height ) ;
if ( r . top < 0 )
OffsetRect ( & r , 0 , - r . top ) ;
}
FillRect ( hdc , & r , m_tooltipBackgroundBrush ) ;
r . top + = 5 ;
2024-07-30 01:49:32 -04:00
SetActiveFont ( m_popupFont ) ;
2023-11-29 18:47:11 -05:00
for ( auto & line : summary )
{
DrawTextW ( hdc , line . c_str ( ) , int ( line . size ( ) ) , & r , DT_SINGLELINE ) ;
2024-07-30 01:49:32 -04:00
r . top + = m_popupFont . height ;
2023-11-29 18:47:11 -05:00
}
}
else if ( m_statsSelected )
{
2024-07-30 01:49:32 -04:00
SetActiveFont ( m_popupFont ) ;
DrawTextLogger logger ( m_hwnd , hdc , m_popupFont . height , m_tooltipBackgroundBrush ) ;
2024-04-12 01:05:04 -04:00
logger . AddSpace ( 3 ) ;
2023-11-29 18:47:11 -05:00
logger . SetColor ( m_cpuColor ) . Info ( L " Cpu: %.1f%% " , m_stats . cpuLoad * 100.0f ) ;
logger . SetColor ( m_memColor ) . Info ( L " Mem: %ls/%ls " , BytesToText ( m_stats . memTotal - m_stats . memAvail ) . str , BytesToText ( m_stats . memTotal ) . str ) ;
2024-09-04 18:54:49 -04:00
logger . SetColor ( m_recvColor ) . Info ( L " Recv: %lsps " , BytesToText ( m_stats . recvBytesPerSecond * 8 ) . str ) ;
logger . SetColor ( m_sendColor ) . Info ( L " Send: %lsps " , BytesToText ( m_stats . sendBytesPerSecond * 8 ) . str ) ;
2023-11-29 18:47:11 -05:00
if ( m_stats . ping )
2023-12-03 02:50:01 -05:00
logger . Info ( L " Ping: %ls " , TimeToText ( m_stats . ping , false , m_traceView . frequency ) . str ) ;
2024-04-12 01:05:04 -04:00
logger . AddSpace ( 3 ) ;
logger . DrawAtCursor ( ) ;
2023-11-29 18:47:11 -05:00
}
else if ( m_buttonSelected ! = ~ 0u )
{
const wchar_t * tooltip [ ] =
{
2024-07-10 18:24:10 -04:00
# define UBA_VISUALIZER_FLAG(name, defaultValue, desc) desc,
UBA_VISUALIZER_FLAGS1
# undef UBA_VISUALIZER_FLAG
2023-11-29 18:47:11 -05:00
} ;
2024-07-30 01:49:32 -04:00
SetActiveFont ( m_popupFont ) ;
DrawTextLogger logger ( m_hwnd , hdc , m_popupFont . height , m_tooltipBackgroundBrush ) ;
2024-07-15 17:13:18 -04:00
logger . Info ( L " %ls %ls " , L " Show " , tooltip [ m_buttonSelected ] ) ;
2024-04-12 01:05:04 -04:00
logger . DrawAtCursor ( ) ;
2023-11-29 18:47:11 -05:00
}
2023-12-22 02:41:43 -05:00
else if ( m_fetchedFilesSelected ! = ~ 0u )
{
auto & session = m_traceView . sessions [ m_fetchedFilesSelected ] ;
auto & fetchedFiles = session . fetchedFiles ;
2024-01-02 17:42:42 -05:00
if ( ! fetchedFiles . empty ( ) & & ! fetchedFiles [ 0 ] . hint . empty ( ) )
2023-12-22 02:41:43 -05:00
{
2024-04-12 01:05:04 -04:00
/*
2024-01-02 17:42:42 -05:00
int colWidth = 500 ;
int width = colWidth * 2 ;
2024-07-30 01:49:32 -04:00
int height = Min ( int ( clientRect . bottom ) , int ( fetchedFiles . size ( ) * m_popupFont . height ) ) ;
2023-12-22 02:41:43 -05:00
2024-07-30 01:49:32 -04:00
SetActiveFont ( m_defaultFont ) ;
DrawTextLogger logger ( m_hwnd , hdc , r , m_font . height , m_tooltipBackgroundBrush ) ;
2024-01-02 17:42:42 -05:00
for ( auto & f : fetchedFiles )
{
if ( f . hint = = TC ( " KnownInput " ) )
continue ;
2024-07-30 01:49:32 -04:00
if ( logger . rect . top > = r . bottom - m_font . height )
2024-01-02 17:42:42 -05:00
{
if ( logger . rect . left + colWidth > = r . right )
{
logger . Info ( L " ... " ) ;
break ;
}
logger . rect . top = r . top ;
logger . rect . left + = colWidth ;
}
logger . Info ( L " %s " , f . hint . c_str ( ) ) ;
2023-12-22 02:41:43 -05:00
}
2024-04-12 01:05:04 -04:00
logger . DrawAtCursor ( ) ;
*/
2023-12-22 02:41:43 -05:00
}
}
2023-11-29 18:47:11 -05:00
}
2024-03-03 20:42:41 -05:00
u64 ConvertTime ( const TraceView & view , u64 time ) ;
2024-07-30 01:49:32 -04:00
void Visualizer : : PaintActiveProcesses ( int & posY , const RECT & clientRect , const Function < void ( TraceView : : ProcessLocation & , u32 , bool ) > & drawProcess )
{
SetActiveFont ( m_processFont ) ;
int startPosY = posY ;
Map < u64 , TraceView : : ProcessLocation * > activeProcesses ;
u32 remoteCount = 0 ;
for ( auto & kv : m_trace . m_activeProcesses )
{
auto & active = kv . second ;
TraceView : : Process & process = m_trace . GetSession ( m_traceView , active . sessionIndex ) . processors [ active . processorIndex ] . processes [ active . processIndex ] ;
u64 start = process . start ; //~0llu - process.start;
activeProcesses . try_emplace ( start , & active ) ;
if ( process . isRemote )
+ + remoteCount ;
}
u32 maxHeight = clientRect . bottom ;
bool fillHeight = ! m_config . showDetailedData & & ! m_config . showTitleBars & & ! m_config . showCpuMemStats & & ! m_config . showNetworkStats & & ! m_config . showProcessBars ;
if ( fillHeight )
{
maxHeight = clientRect . bottom - posY ;
if ( m_config . showTimeline )
maxHeight - = GetTimelineHeight ( ) ;
}
else
{
u32 maxHeight2 = m_config . maxActiveVisible * ( m_activeFont . height + 2 ) ;
maxHeight = Min ( maxHeight , maxHeight2 ) ;
}
2024-08-06 15:31:53 -04:00
u32 maxSize = Min ( m_config . maxActiveProcessHeight , 32u ) ;
2024-07-30 01:49:32 -04:00
u32 maxSizeMinusOne = maxSize - 1 ;
u32 counts [ 128 ] = { 0 } ;
u32 highestHistoryCount = 0 ;
for ( u32 i = 0 ; i ! = sizeof_array ( m_activeProcessCountHistory ) - 1 ; + + i )
highestHistoryCount = Max ( highestHistoryCount , m_activeProcessCountHistory [ i ] ) ;
u32 activeProcessCount = highestHistoryCount ;
counts [ 0 ] = activeProcessCount ;
u32 totalHeight = counts [ 0 ] * 2 ;
while ( totalHeight < maxHeight & & counts [ maxSizeMinusOne ] ! = activeProcessCount )
{
bool changed = false ;
for ( u32 i = 0 ; i ! = maxSizeMinusOne ; + + i )
{
if ( counts [ i ] & & counts [ i ] > counts [ i + 1 ] * 2 + 1 )
{
- - counts [ i ] ;
+ + counts [ i + 1 ] ;
+ + totalHeight ;
changed = true ;
}
}
if ( ! changed )
{
for ( u32 j = 0 ; j ! = maxSizeMinusOne ; + + j )
{
if ( ! counts [ j ] )
continue ;
+ + counts [ j + 1 ] ;
- - counts [ j ] ;
+ + totalHeight ;
break ;
}
}
}
auto it = activeProcesses . begin ( ) ;
auto itEnd = activeProcesses . end ( ) ;
int endY = int ( startPosY + maxHeight ) ;
for ( u32 i = 0 ; i ! = maxSize ; + + i )
{
u32 v = maxSizeMinusOne - i ;
u32 boxHeight = v + 1 ;
for ( u32 j = 0 ; j ! = counts [ v ] ; + + j )
{
if ( it = = itEnd | | posY > = endY )
break ;
auto & active = * it - > second ;
+ + it ;
drawProcess ( active , boxHeight , j = = 0 ) ;
posY + = boxHeight + 1 ;
}
}
2024-08-06 15:31:53 -04:00
//if (fillHeight)
// posY = clientRect.bottom-1;//startPosY; // To prevent vertical scrollbar
if ( fillHeight | | counts [ maxSizeMinusOne ] ! = activeProcessCount )
2024-07-30 01:49:32 -04:00
posY = startPosY + maxHeight ;
else
posY + = 3 ;
SetActiveFont ( m_defaultFont ) ;
}
2023-11-29 18:47:11 -05:00
void Visualizer : : PaintProcessRect ( TraceView : : Process & process , HDC hdc , RECT rect , const RECT & progressRect , bool selected , bool writingBitmap )
{
auto clampRect = [ & ] ( RECT & r ) { r . left = Min ( Max ( r . left , progressRect . left ) , progressRect . right ) ; r . right = Max ( Min ( r . right , progressRect . right ) , progressRect . left ) ; } ;
bool done = process . stop ! = ~ u64 ( 0 ) ;
HBRUSH brush = m_processBrushes [ selected ] . success ;
2024-08-09 18:50:03 -04:00
if ( ! process . returnedReason . empty ( ) )
2023-11-29 18:47:11 -05:00
brush = m_processBrushes [ selected ] . returned ;
else if ( ! done )
brush = m_processBrushes [ selected ] . inProgress ;
2024-04-13 19:21:42 -04:00
else if ( process . cacheFetch )
brush = m_processBrushes [ selected ] . cacheFetch ;
2023-11-29 18:47:11 -05:00
else if ( process . exitCode ! = 0 )
brush = m_processBrushes [ selected ] . error ;
2024-04-12 01:05:04 -04:00
u64 writeFilesTime = process . writeFilesTime ;
2024-03-08 18:31:48 -05:00
2024-07-10 18:24:10 -04:00
if ( ! done | | process . exitCode ! = 0 | | ! m_config . ShowReadWriteColors | | ( TimeToMs ( writeFilesTime , m_traceView . frequency ) < 300 & & TimeToMs ( process . createFilesTime , m_traceView . frequency ) < 300 ) )
2023-11-29 18:47:11 -05:00
{
if ( writingBitmap )
rect . right = 256 ;
clampRect ( rect ) ;
FillRect ( hdc , & rect , brush ) ;
return ;
}
double duration = double ( process . stop - process . start ) ;
RECT main = rect ;
int width = rect . right - rect . left ;
2024-04-12 01:05:04 -04:00
double recvPart = ( double ( ConvertTime ( m_traceView , process . createFilesTime ) ) / duration ) ;
2023-11-29 18:47:11 -05:00
if ( int headSize = int ( recvPart * width ) )
{
UBA_ASSERT ( headSize > 0 ) ;
main . left + = headSize ;
RECT r2 = rect ;
r2 . right = r2 . left + headSize ;
clampRect ( r2 ) ;
if ( r2 . left ! = r2 . right )
FillRect ( hdc , & r2 , m_processBrushes [ selected ] . recv ) ;
}
2024-03-08 18:31:48 -05:00
double sendPart = ( double ( ConvertTime ( m_traceView , writeFilesTime ) ) / duration ) ;
2023-11-29 18:47:11 -05:00
if ( int tailSize = int ( sendPart * width ) )
{
UBA_ASSERT ( tailSize > 0 ) ;
main . right - = tailSize ;
RECT r2 = rect ;
r2 . left = r2 . right - tailSize ;
clampRect ( r2 ) ;
if ( r2 . left ! = r2 . right )
FillRect ( hdc , & r2 , m_processBrushes [ selected ] . send ) ;
}
clampRect ( main ) ;
if ( main . left ! = main . right )
FillRect ( hdc , & main , brush ) ;
//clampRect(rect);
/*
if ( ! process . updates . empty ( ) )
{
shouldDrawText = false ;
int prevUpdateX = rect . left ;
for ( auto & update : process . updates )
{
int updateX = int ( posX + TimeToS ( update . time ) * scaleX ) - 1 ;
RECT textRect { prevUpdateX + 5 , rect . top , updateX , rect . bottom } ;
DrawTextW ( hdc , update . reason . c_str ( ) , u32 ( update . reason . size ( ) ) , & textRect , DT_SINGLELINE ) ;
SelectObject ( hdc , m_processUpdatePen ) ;
MoveToEx ( hdc , updateX , rect . top , NULL ) ;
LineTo ( hdc , updateX , rect . bottom - 1 ) ;
prevUpdateX = updateX ;
}
}
*/
}
void Visualizer : : PaintTimeline ( HDC hdc , const RECT & clientRect )
{
2024-07-30 01:49:32 -04:00
SetActiveFont ( m_timelineFont ) ;
2024-07-19 19:12:12 -04:00
int top = GetTimelineTop ( clientRect ) ;
2023-11-29 18:47:11 -05:00
float timeScale = ( m_horizontalScaleValue * m_zoomValue ) * 50.0f ;
float startOffset = ( ( m_scrollPosX / timeScale ) - int ( m_scrollPosX / timeScale ) ) * timeScale ;
int index = - int ( startOffset / timeScale ) ;
int number = - int ( float ( m_scrollPosX ) / timeScale ) ;
int textStepSize = int ( ( 5.0f / timeScale ) + 1 ) * 5 ;
if ( textStepSize > 150 )
textStepSize = 600 ;
else if ( textStepSize > 120 )
textStepSize = 300 ;
else if ( textStepSize > 90 )
textStepSize = 240 ;
else if ( textStepSize > 45 )
textStepSize = 120 ;
else if ( textStepSize > 30 )
textStepSize = 60 ;
else if ( textStepSize > 10 )
textStepSize = 30 ;
int lineStepSize = textStepSize / 5 ;
RECT progressRect = clientRect ;
2024-07-17 17:48:42 -04:00
progressRect . left + = m_progressRectLeft ;
2023-11-29 18:47:11 -05:00
SelectObject ( hdc , m_textPen ) ;
while ( true )
{
int pos = progressRect . left + int ( startOffset + index * timeScale ) ;
if ( pos > = clientRect . right )
break ;
int lineBottom = top + 5 ;
if ( ! ( number % textStepSize ) )
{
bool shouldDraw = true ;
int seconds = number ;
StringBuffer < > buffer ;
if ( seconds > = 60 )
{
int min = seconds / 60 ;
seconds - = min * 60 ;
if ( ! seconds )
{
buffer . Appendf ( L " %um " , min ) ;
lineBottom + = 4 ;
}
}
if ( ! number | | seconds )
buffer . Appendf ( L " %u " , seconds ) ;
if ( shouldDraw )
{
RECT textRect ;
textRect . top = top + 8 ;
2024-07-30 01:49:32 -04:00
textRect . bottom = textRect . top + m_activeFont . height ;
2023-11-29 18:47:11 -05:00
textRect . right = pos + 20 ;
textRect . left = pos - 20 ;
DrawTextW ( hdc , buffer . data , buffer . count , & textRect , DT_SINGLELINE | DT_CENTER ) ;
}
}
if ( ! ( number % lineStepSize ) )
{
MoveToEx ( hdc , pos , top , NULL ) ;
LineTo ( hdc , pos , lineBottom ) ;
}
+ + number ;
+ + index ;
}
MoveToEx ( hdc , m_contentWidth , top - 25 , NULL ) ;
LineTo ( hdc , m_contentWidth , top ) ;
/*
POINT cursorPos ;
GetCursorPos ( & cursorPos ) ;
ScreenToClient ( m_hwnd , & cursorPos ) ;
RECT lineRect ;
lineRect . left = cursorPos . x ;
lineRect . right = cursorPos . x + 1 ;
lineRect . top = top ;
lineRect . bottom = top + 15 ;
FillRect ( hdc , & lineRect , m_lineBrush ) ;
*/
}
2023-12-22 02:41:43 -05:00
void Visualizer : : PaintDetailedStats ( int & posY , const RECT & progressRect , TraceView : : Session & session , bool isRemote , u64 playTime , const DrawTextFunc & drawTextFunc )
2023-11-29 18:47:11 -05:00
{
2024-07-30 01:49:32 -04:00
int stepY = m_activeFont . height ;
2023-11-29 18:47:11 -05:00
int startPosY = posY ;
int posX = progressRect . left + 5 ;
RECT textRect ;
textRect . top = posY ;
textRect . bottom = posY + 20 ;
textRect . left = posX ;
textRect . right = posX + 1000 ;
auto drawText = [ & ] ( const wchar_t * format , . . . )
{
textRect . top = posY ;
textRect . bottom = posY + stepY ;
posY + = stepY ;
StringBuffer < > str ;
va_list arg ;
va_start ( arg , format ) ;
str . Append ( format , arg ) ;
va_end ( arg ) ;
2023-12-22 02:41:43 -05:00
drawTextFunc ( str , textRect ) ;
2023-11-29 18:47:11 -05:00
} ;
if ( isRemote )
{
2024-01-16 15:35:42 -05:00
drawText ( L " Finished Processes: %u " , session . processExitedCount ) ;
drawText ( L " Active Processes: %u " , session . processActiveCount ) ;
2023-11-29 18:47:11 -05:00
if ( ! session . updates . empty ( ) )
{
auto & u = session . updates . back ( ) ;
u64 sendPerS = 0 ;
u64 recvPerS = 0 ;
if ( float duration = TimeToS ( u . time - session . prevUpdateTime ) )
{
sendPerS = u64 ( ( u . send - session . prevSend ) / duration ) ;
recvPerS = u64 ( ( u . recv - session . prevRecv ) / duration ) ;
}
drawText ( L " ClientId: %u TcpCount: %u " , session . clientUid . data1 , u . connectionCount ) ;
2024-09-04 18:54:49 -04:00
drawText ( L " Recv: %ls (%sps) " , BytesToText ( u . recv ) , BytesToText ( recvPerS * 8 ) ) ;
drawText ( L " Send: %ls (%sps) " , BytesToText ( u . send ) , BytesToText ( sendPerS * 8 ) ) ;
2023-11-29 18:47:11 -05:00
}
if ( session . disconnectTime = = ~ u64 ( 0 ) )
{
if ( session . proxyCreated )
drawText ( L " Proxy(HOSTED): %ls " , session . proxyName . c_str ( ) ) ;
else if ( ! session . proxyName . empty ( ) )
drawText ( L " Proxy: %ls " , session . proxyName . c_str ( ) ) ;
else
drawText ( L " Proxy: None " ) ;
}
int posY1 = posY ;
int fileWidth = 700 ;
auto drawFiles = [ & ] ( const wchar_t * fileType , Vector < TraceView : : FileTransfer > & files , u64 bytes , u32 & maxVisibleFiles )
{
textRect . left = posX ;
textRect . right = posX + fileWidth ;
drawText ( L " %ls Files: %u (%s) " , fileType , u32 ( files . size ( ) ) , BytesToText ( bytes ) ) ;
u32 fileCount = 0 ;
for ( auto rit = files . rbegin ( ) , rend = files . rend ( ) ; rit ! = rend ; + + rit )
{
TraceView : : FileTransfer & file = * rit ;
if ( file . stop ! = ~ u64 ( 0 ) )
continue ;
u64 time = 0 ;
if ( file . start < playTime )
time = playTime - file . start ;
drawText ( L " %s - %s, (%ls) " , file . hint . c_str ( ) , BytesToText ( file . size ) . str , TimeToText ( time , true ) . str ) ;
if ( fileCount + + > 5 )
break ;
}
posY + = stepY * ( maxVisibleFiles - fileCount ) ;
maxVisibleFiles = Max ( maxVisibleFiles , fileCount ) ;
} ;
posY = startPosY ;
posX + = 150 ;
drawFiles ( L " Fetched " , session . fetchedFiles , session . fetchedFilesBytes , session . maxVisibleFiles ) ;
int posY2 = posY ;
posY = startPosY ;
posX + = fileWidth ;
drawFiles ( L " Stored " , session . storedFiles , session . storedFilesBytes , session . maxVisibleFiles ) ;
posY = Max ( posY , Max ( posY1 , posY2 ) ) ;
}
2024-01-16 15:35:42 -05:00
else
{
drawText ( L " Finished Processes: %u (local: %u) " , m_traceView . totalProcessExitedCount , session . processExitedCount ) ;
drawText ( L " Active Processes: %u (local: %u) " , m_traceView . totalProcessActiveCount , session . processActiveCount ) ;
2024-09-05 03:09:28 -04:00
drawText ( L " Active Helpers: %u " , Max ( 1u , m_traceView . activeSessionCount ) - 1 ) ;
2024-01-16 15:35:42 -05:00
if ( ! session . updates . empty ( ) )
{
auto & u = session . updates . back ( ) ;
if ( u . send | | u . recv )
{
u64 sendPerS = 0 ;
u64 recvPerS = 0 ;
if ( float duration = TimeToS ( u . time - session . prevUpdateTime ) )
{
sendPerS = u64 ( ( u . send - session . prevSend ) / duration ) ;
recvPerS = u64 ( ( u . recv - session . prevRecv ) / duration ) ;
}
2024-09-04 18:54:49 -04:00
drawText ( L " Recv: %ls (%sps) " , BytesToText ( u . recv ) , BytesToText ( recvPerS ) ) ;
drawText ( L " Send: %ls (%sps) " , BytesToText ( u . send ) , BytesToText ( sendPerS ) ) ;
2024-01-16 15:35:42 -05:00
}
}
}
2023-11-29 18:47:11 -05:00
}
2024-07-16 16:27:53 -04:00
u64 Visualizer : : GetPlayTime ( )
2023-11-29 18:47:11 -05:00
{
u64 currentTime = m_paused ? m_pauseStart : GetTime ( ) ;
2024-07-15 17:13:18 -04:00
u64 playTime = 0 ;
if ( m_traceView . startTime )
playTime = currentTime - m_traceView . startTime - m_pauseTime ;
2023-11-29 18:47:11 -05:00
if ( m_replay )
playTime * = m_replay ;
2024-07-16 16:27:53 -04:00
return playTime ;
}
2024-07-30 01:49:32 -04:00
int Visualizer : : GetTimelineHeight ( )
{
return m_timelineFont . height + 8 ;
}
2024-07-19 19:12:12 -04:00
int Visualizer : : GetTimelineTop ( const RECT & clientRect )
{
2024-07-30 01:49:32 -04:00
int timelineHeight = GetTimelineHeight ( ) ;
int posY = m_contentHeight - timelineHeight ;
int maxY = int ( clientRect . bottom - timelineHeight ) ;
2024-07-19 19:12:12 -04:00
return m_config . LockTimelineToBottom ? maxY : Min ( posY , maxY ) ;
}
2024-07-16 16:27:53 -04:00
void Visualizer : : HitTest ( HitTestResult & outResult , const POINT & pos )
{
2024-07-30 01:49:32 -04:00
SetActiveFont ( m_defaultFont ) ;
2024-07-16 16:27:53 -04:00
u64 playTime = GetPlayTime ( ) ;
2023-11-29 18:47:11 -05:00
RECT clientRect ;
GetClientRect ( m_hwnd , & clientRect ) ;
int posY = int ( m_scrollPosY ) ;
2024-08-09 17:06:11 -04:00
int boxHeight = m_boxHeight ;
int processStepY = boxHeight + 1 ;
2023-11-29 18:47:11 -05:00
float scaleX = 50.0f * m_zoomValue * m_horizontalScaleValue ;
RECT progressRect = clientRect ;
2024-07-17 17:48:42 -04:00
progressRect . left + = m_progressRectLeft ;
2023-11-29 18:47:11 -05:00
progressRect . bottom - = 30 ;
{
2024-07-15 17:13:18 -04:00
int boxSide = 8 ;
int boxStride = boxSide + 2 ;
2023-11-29 18:47:11 -05:00
int top = 5 ;
2024-07-15 17:13:18 -04:00
int bottom = top + boxSide ;
int left = progressRect . right - 7 - boxSide ;
2023-11-29 18:47:11 -05:00
int right = progressRect . right - 7 ;
2024-07-10 18:24:10 -04:00
for ( int i = VisualizerFlag_Count - 1 ; i > = 0 ; - - i )
2023-11-29 18:47:11 -05:00
{
if ( pos . x > = left & & pos . x < = right & & pos . y > = top & & pos . y < = bottom )
{
outResult . buttonSelected = i ;
return ;
}
2024-07-15 17:13:18 -04:00
left - = boxStride ;
right - = boxStride ;
2023-11-29 18:47:11 -05:00
}
}
2024-07-30 01:49:32 -04:00
outResult . section = 0 ;
2023-11-29 18:47:11 -05:00
u64 lastStop = 0 ;
2024-07-15 17:13:18 -04:00
if ( m_config . showProgress & & m_traceView . progressProcessesTotal )
2024-07-30 01:49:32 -04:00
posY + = m_activeFont . height + 2 ;
2024-07-15 17:13:18 -04:00
2024-07-16 16:27:53 -04:00
if ( m_config . showStatus & & ! m_traceView . statusMap . empty ( ) )
2024-02-02 00:30:28 -05:00
{
2024-07-15 17:13:18 -04:00
u32 lastRow = ~ 0u ;
u32 row = ~ 0u ;
2024-02-02 00:30:28 -05:00
for ( auto & kv : m_traceView . statusMap )
2024-07-15 17:13:18 -04:00
{
if ( kv . second . text . empty ( ) )
continue ;
2024-07-16 16:27:53 -04:00
row = u32 ( kv . first > > 32 ) ;
if ( lastRow ! = ~ 0u & & lastRow ! = row )
2024-07-30 01:49:32 -04:00
posY + = m_activeFont . height + 2 ;
2024-07-16 16:27:53 -04:00
lastRow = row ;
2024-07-15 17:13:18 -04:00
if ( ! kv . second . link . empty ( ) )
{
2024-07-30 01:49:32 -04:00
if ( pos . y > = posY & & pos . y < posY + m_activeFont . height & & pos . x > 20 & & pos . x < 80 ) // TODO: x is just hard coded to fit horde for now
2024-07-15 17:13:18 -04:00
{
outResult . hyperLink = kv . second . link ;
return ;
}
}
}
if ( row ! = ~ 0u )
2024-07-30 01:49:32 -04:00
posY + = m_activeFont . height + 2 ;
2024-07-16 16:27:53 -04:00
posY + = 3 ;
2024-02-02 00:30:28 -05:00
}
2024-08-09 17:06:11 -04:00
if ( pos . y < posY )
2024-07-30 01:49:32 -04:00
return ;
outResult . section = 1 ;
2024-07-15 17:13:18 -04:00
2023-11-29 18:47:11 -05:00
TraceView : : ProcessLocation & outLocation = outResult . processLocation ;
2024-07-30 01:49:32 -04:00
if ( m_config . showActiveProcesses & & ! m_trace . m_activeProcesses . empty ( ) )
{
PaintActiveProcesses ( posY , clientRect , [ & ] ( TraceView : : ProcessLocation & processLocation , u32 boxHeight , bool firstWithHeight )
{
if ( pos . y < posY | | pos . y > posY + int ( boxHeight ) )
return ;
auto & session = m_trace . GetSession ( m_traceView , processLocation . sessionIndex ) ;
TraceView : : Process & process = session . processors [ processLocation . processorIndex ] . processes [ processLocation . processIndex ] ;
int posX = int ( m_scrollPosX ) + progressRect . left ;
u64 stop = process . stop ;
bool done = stop ! = ~ u64 ( 0 ) ;
if ( ! done )
stop = playTime ;
int left = int ( posX + TimeToS ( process . start ) * scaleX ) ;
int right = int ( posX + TimeToS ( stop ) * scaleX ) - 1 ;
if ( pos . x > = left & & pos . x < = right )
{
outLocation . sessionIndex = processLocation . sessionIndex ;
outLocation . processorIndex = processLocation . processorIndex ;
outLocation . processIndex = processLocation . processIndex ;
outResult . processSelected = true ;
return ;
}
} ) ;
if ( outResult . processSelected )
return ;
}
2024-08-09 17:06:11 -04:00
if ( pos . y < posY )
2024-08-06 15:31:53 -04:00
return ;
outResult . section = 2 ;
2024-03-08 18:31:48 -05:00
SessionRec sortedSessions [ 1024 ] ;
2024-07-17 17:48:42 -04:00
Populate ( sortedSessions , m_traceView , m_config . SortActiveRemoteSessions ) ;
2023-11-29 18:47:11 -05:00
for ( u64 i = 0 , e = m_traceView . sessions . size ( ) ; i ! = e ; + + i )
{
2024-07-15 17:13:18 -04:00
bool isFirst = i = = 0 ;
2023-11-29 18:47:11 -05:00
auto & session = * sortedSessions [ i ] . session ;
2024-01-10 03:05:23 -05:00
bool hasUpdates = ! session . updates . empty ( ) ;
2024-07-19 19:12:12 -04:00
if ( ! isFirst )
{
if ( ! hasUpdates & & session . processors . empty ( ) )
continue ;
if ( ! m_config . showFinishedProcesses & & session . disconnectTime ! = ~ u64 ( 0 ) )
continue ;
}
2024-01-10 03:05:23 -05:00
2023-11-29 18:47:11 -05:00
u32 sessionIndex = sortedSessions [ i ] . index ;
if ( ! isFirst )
posY + = 3 ;
2024-07-15 17:13:18 -04:00
if ( m_config . showTitleBars )
2023-11-29 18:47:11 -05:00
{
2024-07-15 17:13:18 -04:00
if ( pos . y > = posY & & pos . y < posY + m_sessionStepY )
2023-11-29 18:47:11 -05:00
{
2024-07-15 17:13:18 -04:00
if ( pos . x < 500 )
{
outResult . sessionSelectedIndex = sessionIndex ;
return ;
}
2023-11-29 18:47:11 -05:00
}
2024-07-15 17:13:18 -04:00
posY + = m_sessionStepY ;
}
2023-11-29 18:47:11 -05:00
2024-07-10 18:24:10 -04:00
bool showGraph = m_config . showNetworkStats | | m_config . showCpuMemStats ;
2023-11-29 18:47:11 -05:00
if ( showGraph & & ! session . updates . empty ( ) )
{
if ( pos . y > = posY & & pos . y < posY + GraphHeight )
{
int posX = int ( m_scrollPosX ) + progressRect . left ;
u64 prevTime = 0 ;
u64 prevSend = 0 ;
u64 prevRecv = 0 ;
int prevX = 100000 ;
for ( auto & update : session . updates )
{
int x = int ( posX + TimeToS ( update . time ) * scaleX ) ;
2024-01-10 03:05:23 -05:00
if ( prevSend > update . send | | prevRecv > update . recv )
{
prevSend = update . send ;
prevRecv = update . recv ;
prevX = x ;
continue ;
}
2023-11-29 18:47:11 -05:00
int hitOffset = ( prevX - x ) / 2 ;
if ( pos . x + hitOffset > = prevX & & pos . x + hitOffset < = x )
{
double duration = TimeToS ( update . time - prevTime ) ;
2024-03-03 20:42:41 -05:00
outResult . stats . recvBytesPerSecond = u64 ( ( update . recv - prevRecv ) / duration ) ;
outResult . stats . sendBytesPerSecond = u64 ( ( update . send - prevSend ) / duration ) ;
2023-11-29 18:47:11 -05:00
outResult . stats . ping = update . ping ;
outResult . stats . memAvail = update . memAvail ;
outResult . stats . cpuLoad = update . cpuLoad ;
outResult . stats . memTotal = session . memTotal ;
outResult . statsSelected = true ;
return ;
}
prevX = x ;
prevTime = update . time ;
prevSend = update . send ;
prevRecv = update . recv ;
}
posY + = GraphHeight ;
}
posY + = GraphHeight ;
}
2024-07-10 18:24:10 -04:00
if ( m_config . showDetailedData )
2023-12-22 02:41:43 -05:00
{
auto drawText = [ & ] ( const StringBufferBase & text , RECT & rect )
{
//DrawTextW(hdc, text.data, text.count, &rect, DT_SINGLELINE);
if ( pos . x > = rect . left & & pos . x < rect . right & & pos . y > = rect . top & & pos . y < rect . bottom & & text . StartsWith ( TC ( " Fetched Files " ) ) )
outResult . fetchedFilesSelected = sessionIndex ;
} ;
PaintDetailedStats ( posY , progressRect , session , i ! = 0 , playTime , drawText ) ;
}
2023-11-29 18:47:11 -05:00
2024-07-10 18:24:10 -04:00
if ( m_config . showProcessBars )
2023-11-29 18:47:11 -05:00
{
u32 processorIndex = 0 ;
for ( auto & processor : session . processors )
{
2024-07-19 19:12:12 -04:00
bool drawProcessorIndex = m_config . showFinishedProcesses ;
2024-07-17 17:48:42 -04:00
if ( pos . y < progressRect . bottom & & posY + processStepY > = progressRect . top & & posY < = progressRect . bottom & & pos . y > = posY - 1 & & pos . y < posY - 1 + processStepY )
2023-11-29 18:47:11 -05:00
{
u32 processIndex = 0 ;
int posX = int ( m_scrollPosX ) + progressRect . left ;
for ( auto & process : processor . processes )
{
int left = int ( posX + TimeToS ( process . start ) * scaleX ) ;
2024-07-19 19:12:12 -04:00
auto pig = MakeGuard ( [ & ] ( ) { + + processIndex ; } ) ;
2023-11-29 18:47:11 -05:00
if ( left > = progressRect . right )
continue ;
2024-07-19 19:12:12 -04:00
2023-11-29 18:47:11 -05:00
if ( left < progressRect . left )
left = progressRect . left ;
u64 stopTime = process . stop ;
bool done = stopTime ! = ~ u64 ( 0 ) ;
if ( ! done )
stopTime = playTime ;
2024-07-19 19:12:12 -04:00
else if ( ! m_config . showFinishedProcesses )
continue ;
drawProcessorIndex = true ;
2023-11-29 18:47:11 -05:00
RECT rect ;
rect . left = left ;
rect . right = int ( posX + TimeToS ( stopTime ) * scaleX ) ;
if ( rect . right < = progressRect . left )
continue ;
2024-07-19 19:12:12 -04:00
2023-11-29 18:47:11 -05:00
rect . right = Max ( int ( rect . right ) , left + 1 ) ;
rect . top = posY ;
rect . bottom = posY + int ( float ( 18 ) * m_zoomValue ) ;
if ( pos . x > = rect . left & & pos . x < = rect . right )
{
outLocation . sessionIndex = sessionIndex ;
outLocation . processorIndex = processorIndex ;
outLocation . processIndex = processIndex ;
outResult . processSelected = true ;
return ;
}
}
}
2023-12-12 21:27:20 -05:00
if ( ! processor . processes . empty ( ) )
lastStop = Max ( lastStop , processor . processes . rbegin ( ) - > stop ) ;
2023-11-29 18:47:11 -05:00
2024-07-19 19:12:12 -04:00
if ( drawProcessorIndex )
posY + = processStepY ;
2023-11-29 18:47:11 -05:00
+ + processorIndex ;
}
}
else
{
for ( auto & processor : session . processors )
if ( ! processor . processes . empty ( ) )
lastStop = Max ( lastStop , processor . processes . rbegin ( ) - > stop ) ;
}
2024-07-10 18:24:10 -04:00
if ( m_config . showWorkers & & isFirst )
2024-03-03 20:42:41 -05:00
{
int trackIndex = 0 ;
for ( auto & workTrack : m_traceView . workTracks )
{
2024-07-17 17:48:42 -04:00
if ( pos . y < progressRect . bottom & & posY + processStepY > = progressRect . top & & posY < = progressRect . bottom & & pos . y > = posY - 1 & & pos . y < posY - 1 + processStepY )
2024-03-03 20:42:41 -05:00
{
u32 workIndex = 0 ;
int posX = int ( m_scrollPosX ) + progressRect . left ;
for ( auto & work : workTrack . records )
{
int left = int ( posX + TimeToS ( work . start ) * scaleX ) ;
if ( left > = progressRect . right )
{
+ + workIndex ;
continue ;
}
if ( left < progressRect . left )
left = progressRect . left ;
u64 stopTime = work . stop ;
bool done = stopTime ! = ~ u64 ( 0 ) ;
if ( ! done )
stopTime = playTime ;
RECT rect ;
rect . left = left ;
rect . right = int ( posX + TimeToS ( stopTime ) * scaleX ) ;
if ( rect . right < = progressRect . left )
{
+ + workIndex ;
continue ;
}
rect . right = Max ( int ( rect . right ) , left + 1 ) ;
rect . top = posY ;
rect . bottom = posY + int ( float ( 18 ) * m_zoomValue ) ;
if ( pos . x > = rect . left & & pos . x < = rect . right )
{
outResult . workTrack = trackIndex ;
outResult . workIndex = workIndex ;
outResult . workSelected = true ;
return ;
}
+ + workIndex ;
}
}
+ + trackIndex ;
2024-07-17 17:48:42 -04:00
posY + = processStepY ;
2024-03-03 20:42:41 -05:00
}
}
2023-11-29 18:47:11 -05:00
}
2024-07-17 17:48:42 -04:00
m_contentWidth = m_progressRectLeft + Max ( 0 , int ( TimeToS ( ( lastStop ! = 0 & & lastStop ! = ~ u64 ( 0 ) ) ? lastStop : playTime ) * scaleX ) ) ;
m_contentHeight = posY - int ( m_scrollPosY ) + processStepY + 14 ;
2024-07-19 19:12:12 -04:00
if ( m_config . showTimeline & & ! m_traceView . sessions . empty ( ) )
{
int timelineTop = GetTimelineTop ( clientRect ) ;
2024-07-30 01:49:32 -04:00
if ( pos . y > = timelineTop ) // && pos.y < timelineTop + 40)
2024-07-19 19:12:12 -04:00
{
2024-08-06 15:31:53 -04:00
outResult . section = 3 ;
2024-07-19 19:12:12 -04:00
float timeScale = ( m_horizontalScaleValue * m_zoomValue ) * 50.0f ;
float startOffset = - ( m_scrollPosX / timeScale ) ;
outResult . timelineSelected = startOffset + ( pos . x - m_progressRectLeft ) / timeScale ;
}
}
2023-11-29 18:47:11 -05:00
}
2024-08-06 15:31:53 -04:00
void Visualizer : : WriteProcessStats ( Logger & out , const TraceView : : Process & process )
2024-01-09 12:43:47 -05:00
{
bool hasExited = process . stop ! = ~ u64 ( 0 ) ;
out . Info ( L " %ls " , process . description . c_str ( ) ) ;
2024-06-14 02:35:27 -04:00
out . Info ( L " ProcessId: %u " , process . id ) ;
2024-01-09 12:43:47 -05:00
out . Info ( L " Start: %ls " , TimeToText ( process . start , true ) . str ) ;
if ( hasExited )
out . Info ( L " Duration: %ls " , TimeToText ( process . stop - process . start , true ) . str ) ;
if ( hasExited & & process . exitCode ! = 0 )
out . Info ( L " ExitCode: %u " , process . exitCode ) ;
if ( process . stop ! = ~ u64 ( 0 ) )
{
2024-04-12 01:05:04 -04:00
out . Info ( L " " ) ;
BinaryReader reader ( process . stats . data ( ) , 0 , process . stats . size ( ) ) ;
ProcessStats processStats ;
SessionStats sessionStats ;
StorageStats storageStats ;
2024-06-02 18:14:50 -04:00
KernelStats kernelStats ;
2024-04-12 01:05:04 -04:00
processStats . Read ( reader , m_traceView . version ) ;
if ( reader . GetLeft ( ) )
{
2024-06-02 18:14:50 -04:00
if ( process . isRemote )
sessionStats . Read ( reader , m_traceView . version ) ;
storageStats . Read ( reader , m_traceView . version ) ;
kernelStats . Read ( reader , m_traceView . version ) ;
2024-04-12 01:05:04 -04:00
}
2024-06-02 18:14:50 -04:00
out . Info ( L " ----------- Detours stats ----------- " ) ;
2024-04-12 01:05:04 -04:00
processStats . Print ( out , m_traceView . frequency ) ;
2024-06-02 18:14:50 -04:00
if ( ! sessionStats . IsEmpty ( ) )
2024-01-09 12:43:47 -05:00
{
out . Info ( L " " ) ;
out . Info ( L " ----------- Session stats ----------- " ) ;
2024-04-12 01:05:04 -04:00
sessionStats . Print ( out , m_traceView . frequency ) ;
2024-06-02 18:14:50 -04:00
}
if ( ! storageStats . IsEmpty ( ) )
{
2024-01-09 12:43:47 -05:00
out . Info ( L " " ) ;
out . Info ( L " ----------- Storage stats ----------- " ) ;
2024-04-12 01:05:04 -04:00
storageStats . Print ( out , m_traceView . frequency ) ;
2024-06-02 18:14:50 -04:00
}
if ( ! kernelStats . IsEmpty ( ) )
{
2024-01-09 12:43:47 -05:00
out . Info ( L " " ) ;
2024-06-02 18:14:50 -04:00
out . Info ( L " ----------- Kernel stats ------------ " ) ;
kernelStats . Print ( out , false , m_traceView . frequency ) ;
2024-01-09 12:43:47 -05:00
}
}
}
void Visualizer : : CopyTextToClipboard ( const TString & str )
{
if ( ! OpenClipboard ( m_hwnd ) )
return ;
if ( auto hglbCopy = GlobalAlloc ( GMEM_MOVEABLE , ( str . size ( ) + 1 ) * sizeof ( TCHAR ) ) )
{
if ( auto lptstrCopy = GlobalLock ( hglbCopy ) )
{
memcpy ( lptstrCopy , str . data ( ) , ( str . size ( ) + 1 ) * sizeof ( TCHAR ) ) ;
GlobalUnlock ( hglbCopy ) ;
EmptyClipboard ( ) ;
SetClipboardData ( CF_UNICODETEXT , hglbCopy ) ;
}
}
CloseClipboard ( ) ;
}
void Visualizer : : UnselectAndRedraw ( )
{
2024-07-10 18:24:10 -04:00
if ( Unselect ( ) | | m_config . showCursorLine )
2024-08-06 15:31:53 -04:00
Redraw ( false ) ;
2024-01-09 12:43:47 -05:00
}
2023-11-29 18:47:11 -05:00
bool Visualizer : : UpdateAutoscroll ( )
{
if ( ! m_autoScroll )
return false ;
2024-07-16 16:27:53 -04:00
u64 playTime = GetPlayTime ( ) ;
2023-11-29 18:47:11 -05:00
2024-07-19 19:12:12 -04:00
2023-11-29 18:47:11 -05:00
RECT rect ;
GetClientRect ( m_hwnd , & rect ) ;
2024-07-31 12:34:53 -04:00
if ( rect . right = = 0 )
return false ;
2023-11-29 18:47:11 -05:00
float timeS = TimeToS ( playTime ) ;
2024-07-19 19:12:12 -04:00
if ( m_config . AutoScaleHorizontal )
{
m_scrollPosX = 0 ;
timeS = Max ( timeS , 20.0f / m_zoomValue ) ;
2024-07-31 12:34:53 -04:00
m_horizontalScaleValue = Max ( float ( rect . right - m_progressRectLeft - 2 ) / ( m_zoomValue * timeS * 50.0f ) , 0.001f ) ;
2024-07-19 19:12:12 -04:00
return true ;
}
else
{
float oldScrollPosX = m_scrollPosX ;
m_scrollPosX = Min ( 0.0f , ( float ) rect . right - timeS * 50.0f * m_horizontalScaleValue * m_zoomValue - m_progressRectLeft ) ;
return oldScrollPosX ! = m_scrollPosX ;
}
2023-11-29 18:47:11 -05:00
}
bool Visualizer : : UpdateSelection ( )
{
2024-05-22 17:27:49 -04:00
if ( ! m_mouseOverWindow | | m_dragToScrollCounter > 0 )
2023-11-29 18:47:11 -05:00
return false ;
POINT pos ;
GetCursorPos ( & pos ) ;
ScreenToClient ( m_hwnd , & pos ) ;
HitTestResult res ;
HitTest ( res , pos ) ;
2024-07-30 01:49:32 -04:00
m_activeSection = res . section ;
2023-11-29 18:47:11 -05:00
if ( res . processSelected = = m_processSelected & & res . processLocation = = m_processSelectedLocation & &
res . sessionSelectedIndex = = m_sessionSelectedIndex & &
res . statsSelected = = m_statsSelected & & memcmp ( & res . stats , & m_stats , sizeof ( Stats ) ) = = 0 & &
2023-12-22 02:41:43 -05:00
res . buttonSelected = = m_buttonSelected & & res . timelineSelected = = m_timelineSelected & &
2024-03-03 20:42:41 -05:00
res . fetchedFilesSelected = = m_fetchedFilesSelected & &
2024-07-15 17:13:18 -04:00
res . workSelected = = m_workSelected & & res . workTrack = = m_workTrack & & res . workIndex = = m_workIndex & &
res . hyperLink = = m_hyperLinkSelected )
2023-11-29 18:47:11 -05:00
return false ;
m_processSelected = res . processSelected ;
m_processSelectedLocation = res . processLocation ;
m_sessionSelectedIndex = res . sessionSelectedIndex ;
m_statsSelected = res . statsSelected ;
m_stats = res . stats ;
m_buttonSelected = res . buttonSelected ;
m_timelineSelected = res . timelineSelected ;
2023-12-22 02:41:43 -05:00
m_fetchedFilesSelected = res . fetchedFilesSelected ;
2024-03-03 20:42:41 -05:00
m_workSelected = res . workSelected ;
m_workTrack = res . workTrack ;
m_workIndex = res . workIndex ;
2024-07-15 17:13:18 -04:00
m_hyperLinkSelected = res . hyperLink ;
2023-11-29 18:47:11 -05:00
return true ;
}
void Visualizer : : UpdateScrollbars ( bool redraw )
{
RECT rect ;
GetClientRect ( m_hwnd , & rect ) ;
SCROLLINFO si ;
si . cbSize = sizeof ( SCROLLINFO ) ;
2024-07-15 17:13:18 -04:00
si . fMask = SIF_ALL | SIF_DISABLENOSCROLL ;
2023-11-29 18:47:11 -05:00
si . nMin = 0 ;
si . nMax = m_contentHeight ;
2024-07-30 01:49:32 -04:00
si . nPage = int ( rect . bottom ) ;
2023-11-29 18:47:11 -05:00
si . nPos = - int ( m_scrollPosY ) ;
si . nTrackPos = 0 ;
SetScrollInfo ( m_hwnd , SB_VERT , & si , redraw ) ;
si . nMax = m_contentWidth ;
si . nPage = rect . right ;
si . nPos = - int ( m_scrollPosX ) ;
SetScrollInfo ( m_hwnd , SB_HORZ , & si , redraw ) ;
}
2024-07-30 01:49:32 -04:00
void Visualizer : : SetActiveFont ( const Font & font )
{
m_activeFont = font ;
if ( m_activeHdc )
SelectObject ( m_activeHdc , font . handle ) ;
}
2024-07-15 17:13:18 -04:00
void Visualizer : : PostNewTrace ( u32 replay , bool paused )
{
KillTimer ( m_hwnd , 0 ) ;
PostMessage ( m_hwnd , WM_NEWTRACE , replay , paused ) ;
}
2024-08-25 02:00:53 -04:00
void Visualizer : : PostNewTitle ( const StringView & title )
{
PostMessage ( m_hwnd , WM_SETTITLE , 0 , ( LPARAM ) _wcsdup ( title . data ) ) ;
}
2024-07-19 15:56:16 -04:00
void Visualizer : : PostQuit ( )
{
m_looping = false ;
PostMessage ( m_hwnd , WM_USER + 666 , 0 , 0 ) ;
}
2023-11-29 18:47:11 -05:00
LRESULT Visualizer : : WinProc ( HWND hWnd , UINT Msg , WPARAM wParam , LPARAM lParam )
{
switch ( Msg )
{
2024-08-25 02:00:53 -04:00
case WM_SETTITLE :
{
auto title = ( wchar_t * ) lParam ;
SetWindowTextW ( hWnd , title ) ;
free ( title ) ;
break ;
}
2023-11-29 18:47:11 -05:00
case WM_NEWTRACE :
{
2024-07-15 17:13:18 -04:00
m_replay = u32 ( wParam ) ;
m_paused = lParam ;
2024-07-16 16:27:53 -04:00
m_autoScroll = true ;
m_scrollPosX = 0 ;
m_scrollPosY = 0 ;
2023-11-29 18:47:11 -05:00
Reset ( ) ;
StringBuffer < > title ;
GetTitlePrefix ( title ) ;
2024-03-08 18:31:48 -05:00
auto g = MakeGuard ( [ & ] ( )
{
2024-08-06 15:31:53 -04:00
Redraw ( true ) ;
2024-03-08 18:31:48 -05:00
UpdateScrollbars ( true ) ;
} ) ;
2023-11-29 18:47:11 -05:00
if ( m_client )
{
if ( ! m_trace . StartReadClient ( m_traceView , * m_client ) )
2024-09-05 03:09:28 -04:00
{
m_clientDisconnect . Set ( ) ;
2023-11-29 18:47:11 -05:00
return false ;
2024-09-05 03:09:28 -04:00
}
2023-11-29 18:47:11 -05:00
m_namedTrace . Clear ( ) . Append ( m_newTraceName ) ;
2024-01-16 15:35:42 -05:00
m_traceView . finished = false ;
}
else if ( ! m_fileName . IsEmpty ( ) )
{
2024-06-07 16:29:55 -04:00
m_trace . ReadFile ( m_traceView , m_fileName . data , m_replay ! = 0 ) ;
2024-01-16 15:35:42 -05:00
m_traceView . finished = m_replay = = 0 ;
2024-09-17 15:08:58 -04:00
PostNewTitle ( GetTitlePrefix ( title ) . Appendf ( L " %s (v%u) " , m_fileName . data , m_traceView . version ) ) ;
2023-11-29 18:47:11 -05:00
}
else
{
2024-07-15 17:13:18 -04:00
if ( ! m_trace . StartReadNamed ( m_traceView , m_newTraceName . data , true , m_replay ! = 0 ) )
2023-11-29 18:47:11 -05:00
return false ;
m_namedTrace . Clear ( ) . Append ( m_newTraceName ) ;
2024-01-16 15:35:42 -05:00
m_traceView . finished = false ;
2024-09-10 10:26:02 -04:00
PostNewTitle ( GetTitlePrefix ( title ) . Appendf ( L " %s (Listening for new sessions on channel '%s') " , m_namedTrace . data , m_listenChannel . data ) ) ;
2023-11-29 18:47:11 -05:00
}
2023-12-19 19:50:04 -05:00
SetTimer ( m_hwnd , 0 , 200 , NULL ) ;
2023-11-29 18:47:11 -05:00
return 0 ;
}
case WM_SYSCOMMAND :
// Don't send this message through DefWindowProc as it will destroy the window
// and GetMessage will stay stuck indefinitely.
if ( wParam = = SC_CLOSE )
{
2024-07-19 15:56:16 -04:00
PostQuit ( ) ;
2023-11-29 18:47:11 -05:00
return 0 ;
}
break ;
case WM_DESTROY :
2024-07-19 15:56:16 -04:00
PostQuit ( ) ;
2023-11-29 18:47:11 -05:00
return 0 ;
case WM_ERASEBKGND :
return 1 ;
case WM_PAINT :
{
PaintClient ( [ & ] ( HDC hdc , HDC memDC , RECT & rect )
{
FillRect ( memDC , & rect , m_backgroundBrush ) ;
2024-07-30 01:49:32 -04:00
m_activeHdc = memDC ;
2023-11-29 18:47:11 -05:00
PaintAll ( memDC , rect ) ;
2024-07-30 01:49:32 -04:00
m_activeHdc = 0 ;
2023-11-29 18:47:11 -05:00
BitBlt ( hdc , 0 , 0 , rect . right - rect . left , rect . bottom - rect . top , memDC , 0 , 0 , SRCCOPY ) ;
} ) ;
break ;
}
case WM_SIZE :
{
int height = HIWORD ( lParam ) ;
if ( m_contentHeight & & m_contentHeight + m_scrollPosY < height )
m_scrollPosY = float ( Min ( 0 , height - m_contentHeight ) ) ;
int width = LOWORD ( lParam ) ;
if ( m_contentWidth & & m_contentWidth + m_scrollPosX < width )
m_scrollPosX = float ( Min ( 0 , width - m_contentWidth ) ) ;
2024-07-30 01:49:32 -04:00
UpdateScrollbars ( true ) ;
2023-11-29 18:47:11 -05:00
break ;
}
case WM_TIMER :
{
bool changed = false ;
if ( ! m_paused )
{
u64 timeOffset = ( GetTime ( ) - m_startTime - m_pauseTime ) * m_replay ;
2024-07-15 17:13:18 -04:00
if ( ! m_fileName . IsEmpty ( ) )
2024-06-07 16:29:55 -04:00
{
2024-07-15 17:13:18 -04:00
if ( m_replay )
m_trace . UpdateReadFile ( m_traceView , timeOffset , changed ) ;
2024-06-07 16:29:55 -04:00
}
2023-11-29 18:47:11 -05:00
else if ( m_client )
2024-09-05 03:09:28 -04:00
{
if ( ! m_trace . UpdateReadClient ( m_traceView , * m_client , changed ) )
m_clientDisconnect . Set ( ) ;
}
2024-07-15 17:13:18 -04:00
else
{
if ( ! m_trace . UpdateReadNamed ( m_traceView , m_replay ? timeOffset : ~ u64 ( 0 ) , changed ) )
m_listenTimeout . Set ( ) ;
}
2023-11-29 18:47:11 -05:00
}
if ( m_traceView . finished )
{
m_autoScroll = false ;
KillTimer ( m_hwnd , 0 ) ;
2024-08-06 15:31:53 -04:00
changed = true ;
2023-11-29 18:47:11 -05:00
}
changed = UpdateAutoscroll ( ) | | changed ;
changed = UpdateSelection ( ) | | changed ;
if ( changed & & ! IsIconic ( m_hwnd ) )
{
UpdateScrollbars ( true ) ;
u64 startTime = GetTime ( ) ;
2024-08-06 15:31:53 -04:00
RedrawWindow ( m_hwnd , NULL , NULL , RDW_INVALIDATE | RDW_UPDATENOW ) ;
2023-11-29 18:47:11 -05:00
u64 paintTimeMs = TimeToMs ( GetTime ( ) - startTime ) ;
u32 waitTime = u32 ( Min ( paintTimeMs * 5 , 200ull ) ) ;
2023-12-19 19:50:04 -05:00
if ( ! m_traceView . finished )
SetTimer ( m_hwnd , 0 , waitTime , NULL ) ;
2023-11-29 18:47:11 -05:00
}
break ;
}
case WM_MOUSEWHEEL :
{
2024-05-22 17:27:49 -04:00
if ( m_dragToScrollCounter > 0 )
2023-11-29 18:47:11 -05:00
break ;
2024-07-17 17:48:42 -04:00
int delta = GET_WHEEL_DELTA_WPARAM ( wParam ) ;
2024-07-10 18:24:10 -04:00
bool controlDown = GetAsyncKeyState ( VK_CONTROL ) & ( 1 < < 15 ) ;
2024-07-17 17:48:42 -04:00
bool shiftDown = GetAsyncKeyState ( VK_LSHIFT ) & ( 1 < < 15 ) ;
2023-11-29 18:47:11 -05:00
2024-07-17 17:48:42 -04:00
if ( m_config . ScaleHorizontalWithScrollWheel | | controlDown | | shiftDown )
2023-11-29 18:47:11 -05:00
{
2024-08-06 15:31:53 -04:00
if ( m_activeSection = = 2 | | ! controlDown ) // process bars
2024-07-10 18:24:10 -04:00
{
2024-07-30 01:49:32 -04:00
RECT r ;
GetClientRect ( hWnd , & r ) ;
// Use mouse cursor as scroll anchor point
POINT cursorPos = { } ;
GetCursorPos ( & cursorPos ) ;
ScreenToClient ( m_hwnd , & cursorPos ) ;
float newScaleValue = m_horizontalScaleValue ;
2024-08-09 17:06:11 -04:00
int newBoxHeight = m_boxHeight ;
2024-07-30 01:49:32 -04:00
if ( controlDown )
2024-07-17 17:48:42 -04:00
{
2024-07-30 01:49:32 -04:00
if ( delta < 0 )
{
2024-08-09 17:06:11 -04:00
if ( newBoxHeight > 1 )
- - newBoxHeight ;
2024-07-30 01:49:32 -04:00
}
else if ( delta > 0 )
2024-08-09 17:06:11 -04:00
+ + newBoxHeight ;
}
2024-07-30 01:49:32 -04:00
else
2024-07-31 12:34:53 -04:00
newScaleValue = Max ( m_horizontalScaleValue + m_horizontalScaleValue * float ( delta ) * 0.0006f , 0.001f ) ;
2024-07-17 17:48:42 -04:00
2024-07-30 01:49:32 -04:00
// TODO: m_progressRectLeft changes with zoom so anchor logic is wrong
const float scrollAnchorOffsetX = float ( cursorPos . x ) - m_progressRectLeft ;
const float scrollAnchorOffsetY = 0 ; //float(cursorPos.y)*m_zoomValue;// - m_progressRectLeft;
2024-07-17 17:48:42 -04:00
2024-08-09 17:06:11 -04:00
float oldZoomValue = m_zoomValue ;
if ( newBoxHeight ! = m_boxHeight )
{
m_boxHeight = newBoxHeight ;
UpdateProcessFont ( ) ;
}
m_scrollPosY = Min ( 0.0f , float ( m_scrollPosY - scrollAnchorOffsetY ) * ( m_zoomValue / oldZoomValue ) + scrollAnchorOffsetY ) ;
m_scrollPosX = Min ( 0.0f , float ( m_scrollPosX - scrollAnchorOffsetX ) * ( m_zoomValue / oldZoomValue ) * ( newScaleValue / m_horizontalScaleValue ) + scrollAnchorOffsetX ) ; //LOWORD(lParam);
2024-07-17 17:48:42 -04:00
2024-07-30 01:49:32 -04:00
if ( m_horizontalScaleValue ! = newScaleValue )
m_horizontalScaleValue = newScaleValue ;
2024-07-17 17:48:42 -04:00
2024-07-30 01:49:32 -04:00
UpdateAutoscroll ( ) ;
UpdateSelection ( ) ;
2024-07-10 18:24:10 -04:00
2024-07-30 01:49:32 -04:00
int minScroll = r . right - m_contentWidth ;
m_scrollPosX = Min ( 0.0f , Max ( m_scrollPosX , float ( minScroll ) ) ) ;
m_scrollPosY = Min ( 0.0f , Max ( m_scrollPosY , float ( r . bottom - m_contentHeight ) ) ) ;
2024-07-10 18:24:10 -04:00
2024-07-30 01:49:32 -04:00
//if (!m_traceView.finished && m_scrollPosX <= minScroll)
// m_autoScroll = true;
2024-07-10 18:24:10 -04:00
2024-07-30 01:49:32 -04:00
if ( m_config . ShowReadWriteColors )
for ( auto & session : m_traceView . sessions )
for ( auto & processor : session . processors )
for ( auto & process : processor . processes )
//if (TimeToMs(process.writeFilesTime, m_traceView.frequency) >= 300 || TimeToMs(process.createFilesTime, m_traceView.frequency) >= 300)
process . bitmapDirty = true ;
}
2024-08-06 15:31:53 -04:00
else if ( m_activeSection = = 1 ) // active processes
2024-07-30 01:49:32 -04:00
{
2024-08-06 15:31:53 -04:00
if ( delta < 0 )
m_config . maxActiveProcessHeight = Max ( m_config . maxActiveProcessHeight - 1u , 5u ) ;
else if ( delta > 0 )
m_config . maxActiveProcessHeight = Min ( m_config . maxActiveProcessHeight + 1u , 32u ) ;
}
else if ( m_activeSection = = 0 | | m_activeSection = = 3 ) // status/timeline
{
if ( delta < 0 )
m_config . fontSize - = 1 ;
else if ( delta > 0 )
m_config . fontSize + = 1 ;
UpdateDefaultFont ( ) ;
2024-07-30 01:49:32 -04:00
}
2024-07-10 18:24:10 -04:00
UpdateScrollbars ( true ) ;
2024-08-06 15:31:53 -04:00
Redraw ( false ) ;
2023-11-29 18:47:11 -05:00
}
else
{
2024-07-10 18:24:10 -04:00
RECT r ;
GetClientRect ( hWnd , & r ) ;
float oldScrollY = m_scrollPosY ;
m_scrollPosY = m_scrollPosY + delta ;
m_scrollPosY = Min ( Max ( m_scrollPosY , float ( r . bottom - m_contentHeight ) ) , 0.0f ) ;
if ( oldScrollY ! = m_scrollPosY )
{
UpdateScrollbars ( true ) ;
2024-08-06 15:31:53 -04:00
Redraw ( false ) ;
2024-07-10 18:24:10 -04:00
}
2023-11-29 18:47:11 -05:00
}
break ;
}
case WM_MOUSEMOVE :
{
2024-07-30 01:49:32 -04:00
//m_logger.Info(TC("Section: %u"), m_activeSection);
2023-11-29 18:47:11 -05:00
POINTS p = MAKEPOINTS ( lParam ) ;
POINT pos { p . x , p . y } ;
2024-05-22 17:27:49 -04:00
if ( m_dragToScrollCounter > 0 )
2023-11-29 18:47:11 -05:00
{
RECT r ;
GetClientRect ( hWnd , & r ) ;
if ( m_contentHeight < = r . bottom )
m_scrollPosY = 0 ;
else
m_scrollPosY = Max ( Min ( m_scrollAtAnchorY + pos . y - m_mouseAnchor . y , 0.0f ) , float ( r . bottom - m_contentHeight ) ) ;
if ( m_contentWidth < = r . right )
m_scrollPosX = 0 ;
else
{
int minScroll = r . right - m_contentWidth ;
m_scrollPosX = Max ( Min ( m_scrollAtAnchorX + pos . x - m_mouseAnchor . x , 0.0f ) , float ( minScroll ) ) ;
if ( ! m_traceView . finished & & m_scrollPosX < = minScroll )
m_autoScroll = true ;
}
UpdateScrollbars ( true ) ;
2024-08-06 15:31:53 -04:00
Redraw ( false ) ;
2023-11-29 18:47:11 -05:00
}
else
{
2024-07-10 18:24:10 -04:00
if ( UpdateSelection ( ) | | m_config . showCursorLine )
2024-08-06 15:31:53 -04:00
Redraw ( false ) ;
2023-11-29 18:47:11 -05:00
/*
else
{
PaintClient ( [ & ] ( HDC hdc , HDC memDC , RECT & rect )
{
RECT timelineRect = rect ;
rect . top = Min ( rect . bottom , m_contentHeight ) - 28 ;
rect . bottom = Min ( rect . bottom , rect . top + 28 ) ;
FillRect ( memDC , & rect , m_backgroundBrush ) ;
SetBkMode ( memDC , TRANSPARENT ) ;
SetTextColor ( memDC , m_textColor ) ;
SelectObject ( memDC , m_font ) ;
PaintTimeline ( memDC , timelineRect ) ;
BitBlt ( hdc , 0 , rect . top , rect . right - rect . left , rect . bottom - rect . top , memDC , 0 , rect . top , SRCCOPY ) ;
} ) ;
}
*/
}
TRACKMOUSEEVENT tme ;
tme . cbSize = sizeof ( tme ) ;
tme . dwFlags = TME_LEAVE ;
tme . hwndTrack = hWnd ;
TrackMouseEvent ( & tme ) ;
m_mouseOverWindow = true ;
break ;
}
case WM_MOUSELEAVE :
m_mouseOverWindow = false ;
TRACKMOUSEEVENT tme ;
tme . cbSize = sizeof ( tme ) ;
tme . dwFlags = TME_CANCEL ;
tme . hwndTrack = hWnd ;
TrackMouseEvent ( & tme ) ;
2024-01-09 12:43:47 -05:00
if ( ! m_showPopup )
UnselectAndRedraw ( ) ;
2023-11-29 18:47:11 -05:00
break ;
case WM_MBUTTONDOWN :
{
POINTS p = MAKEPOINTS ( lParam ) ;
2024-05-22 17:27:49 -04:00
StartDragToScroll ( POINT { p . x , p . y } ) ;
2023-11-29 18:47:11 -05:00
break ;
}
case WM_MOUSEACTIVATE :
{
2024-07-30 01:49:32 -04:00
if ( LOWORD ( lParam ) ! = HTCLIENT )
break ;
if ( m_parentHwnd )
{
//PostMessage(wParam, WM_MOUSEACTIVATE, wParam, lParam);
//SetForegroundWindow(m_parentHwnd);
//SetFocus(m_parentHwnd);
//SetWindowPos((HWND)wParam, HWND_TOP, 0, 0, 0, 0, SWP_ASYNCWINDOWPOS | SWP_NOMOVE | SWP_NOSIZE);
PostMessage ( m_parentHwnd , 0x0445 , 0 , 0 ) ;
//return MA_NOACTIVATEANDEAT;
}
return MA_ACTIVATEANDEAT ;
2023-11-29 18:47:11 -05:00
}
case WM_LBUTTONDOWN :
{
if ( m_buttonSelected ! = ~ 0u )
{
2024-07-15 17:13:18 -04:00
bool * values = & m_config . showProgress ;
2024-07-10 18:24:10 -04:00
values [ m_buttonSelected ] = ! values [ m_buttonSelected ] ;
2023-11-29 18:47:11 -05:00
HitTestResult res ;
HitTest ( res , { - 1 , - 1 } ) ;
UpdateScrollbars ( true ) ;
2024-08-06 15:31:53 -04:00
Redraw ( false ) ;
2023-11-29 18:47:11 -05:00
}
else if ( m_timelineSelected )
{
2024-07-15 17:13:18 -04:00
if ( ! m_client ) // Does not work for network streams
2023-11-29 18:47:11 -05:00
{
2024-07-15 17:13:18 -04:00
float timelineSelected = Max ( m_timelineSelected , 0.0f ) ;
2023-11-29 18:47:11 -05:00
Reset ( ) ;
2024-07-30 01:49:32 -04:00
bool changed ;
u64 time = MsToTime ( u64 ( timelineSelected * 1000.0 ) ) ;
2024-07-15 17:13:18 -04:00
if ( ! m_fileName . IsEmpty ( ) )
{
if ( ! m_trace . ReadFile ( m_traceView , m_fileName . data , true ) )
return false ;
}
else
{
if ( ! m_trace . StartReadNamed ( m_traceView , nullptr , true , true ) )
return false ;
2024-07-30 01:49:32 -04:00
if ( m_traceView . realStartTime + time > m_startTime )
time = m_startTime - m_traceView . realStartTime ;
2024-07-15 17:13:18 -04:00
}
2024-02-02 18:30:47 -05:00
m_traceView . finished = false ;
2024-07-15 17:13:18 -04:00
if ( ! m_fileName . IsEmpty ( ) )
m_trace . UpdateReadFile ( m_traceView , time , changed ) ;
else
m_trace . UpdateReadNamed ( m_traceView , time , changed ) ;
2023-11-29 18:47:11 -05:00
m_pauseStart = m_startTime + time ;
2024-07-16 16:27:53 -04:00
m_pauseTime = m_startTime - m_pauseStart ;
if ( ! m_paused )
2024-02-02 18:30:47 -05:00
{
2024-07-16 16:27:53 -04:00
m_autoScroll = true ;
2024-05-31 12:40:24 -04:00
m_replay = 1 ;
2024-07-16 16:27:53 -04:00
SetTimer ( m_hwnd , 0 , 200 , NULL ) ;
2024-02-02 18:30:47 -05:00
}
else
{
2024-07-16 16:27:53 -04:00
m_pauseTime = 0 ;
2024-02-02 18:30:47 -05:00
}
2023-11-29 18:47:11 -05:00
HitTestResult res ;
HitTest ( res , { - 1 , - 1 } ) ;
2024-07-16 16:27:53 -04:00
RECT r ;
GetClientRect ( hWnd , & r ) ;
m_scrollPosX = Min ( Max ( m_scrollPosX , float ( r . right - m_contentWidth ) ) , 0.0f ) ;
m_scrollPosY = Min ( 0.0f , Max ( m_scrollPosY , float ( r . bottom - m_contentHeight ) ) ) ;
2023-11-29 18:47:11 -05:00
UpdateScrollbars ( true ) ;
2024-08-06 15:31:53 -04:00
Redraw ( true ) ;
2023-11-29 18:47:11 -05:00
}
}
2024-07-15 17:13:18 -04:00
else if ( ! m_hyperLinkSelected . empty ( ) )
{
ShellExecuteW ( NULL , L " open " , m_hyperLinkSelected . c_str ( ) , NULL , NULL , SW_SHOW ) ;
}
2024-05-22 17:27:49 -04:00
else
{
POINTS p = MAKEPOINTS ( lParam ) ;
StartDragToScroll ( POINT { p . x , p . y } ) ;
}
break ;
}
2024-07-15 17:13:18 -04:00
case WM_SETCURSOR :
{
static HCURSOR arrow = LoadCursorW ( NULL , IDC_ARROW ) ;
static HCURSOR hand = LoadCursorW ( NULL , IDC_HAND ) ;
if ( ! m_hyperLinkSelected . empty ( ) )
SetCursor ( hand ) ;
else
SetCursor ( arrow ) ;
break ;
}
2024-05-22 17:27:49 -04:00
case WM_LBUTTONUP :
{
if ( ! ( m_buttonSelected ! = ~ 0u | | m_timelineSelected ) )
{
StopDragToScroll ( ) ;
}
2023-11-29 18:47:11 -05:00
break ;
}
2024-01-09 12:43:47 -05:00
case WM_RBUTTONUP :
2023-11-29 18:47:11 -05:00
{
2024-01-09 12:43:47 -05:00
POINT point ;
point . x = LOWORD ( lParam ) ;
point . y = HIWORD ( lParam ) ;
HMENU hMenu = CreatePopupMenu ( ) ;
ClientToScreen ( hWnd , & point ) ;
2024-07-10 18:24:10 -04:00
# define UBA_VISUALIZER_FLAG(name, defaultValue, desc) \
AppendMenuW ( hMenu , MF_STRING | ( m_config . name ? MF_CHECKED : 0 ) , Popup_ # # name , desc ) ;
UBA_VISUALIZER_FLAGS2
# undef UBA_VISUALIZER_FLAG
2024-07-15 17:13:18 -04:00
AppendMenuW ( hMenu , MF_STRING , Popup_IncreaseFontSize , L " &Increase Font Size " ) ;
AppendMenuW ( hMenu , MF_STRING , Popup_DecreaseFontSize , L " &Decrease Font Size " ) ;
2024-03-03 20:42:41 -05:00
AppendMenuW ( hMenu , MF_SEPARATOR , 0 , NULL ) ;
2024-01-09 12:43:47 -05:00
if ( m_sessionSelectedIndex ! = ~ 0u )
{
AppendMenuW ( hMenu , MF_STRING , Popup_CopySessionInfo , L " &Copy Session Info " ) ;
AppendMenuW ( hMenu , MF_SEPARATOR , 0 , NULL ) ;
}
else if ( m_processSelected )
{
2024-08-06 15:31:53 -04:00
const TraceView : : Process & process = m_traceView . GetProcess ( m_processSelectedLocation ) ;
2024-01-17 16:49:50 -05:00
2024-01-09 12:43:47 -05:00
AppendMenuW ( hMenu , MF_STRING , Popup_CopyProcessInfo , L " &Copy Process Info " ) ;
2024-01-17 16:49:50 -05:00
if ( ! process . logLines . empty ( ) )
2024-01-30 02:19:51 -05:00
AppendMenuW ( hMenu , MF_STRING , Popup_CopyProcessLog , L " Copy Process &Log " ) ;
2024-01-09 12:43:47 -05:00
AppendMenuW ( hMenu , MF_SEPARATOR , 0 , NULL ) ;
}
2024-07-15 17:13:18 -04:00
if ( ! m_traceView . sessions . empty ( ) )
2024-01-30 02:19:51 -05:00
{
2024-07-15 17:13:18 -04:00
if ( ! m_client )
2024-01-30 02:19:51 -05:00
{
2024-07-15 17:13:18 -04:00
if ( ! m_replay | | m_traceView . finished )
AppendMenuW ( hMenu , MF_STRING , Popup_Replay , L " &Replay Trace " ) ;
2024-01-30 02:19:51 -05:00
else
2024-07-15 17:13:18 -04:00
{
if ( m_paused )
AppendMenuW ( hMenu , MF_STRING , Popup_Play , L " &Play " ) ;
else
AppendMenuW ( hMenu , MF_STRING , Popup_Pause , L " &Pause " ) ;
AppendMenuW ( hMenu , MF_STRING , Popup_JumpToEnd , L " &Jump To End " ) ;
}
2024-01-30 02:19:51 -05:00
}
2024-07-15 17:13:18 -04:00
if ( m_fileName . IsEmpty ( ) )
AppendMenuW ( hMenu , MF_STRING , Popup_SaveAs , L " &Save Trace " ) ;
AppendMenuW ( hMenu , MF_SEPARATOR , 0 , NULL ) ;
2024-01-30 02:19:51 -05:00
}
2024-07-10 18:24:10 -04:00
AppendMenuW ( hMenu , MF_STRING , Popup_SaveSettings , L " Save Position/Settings " ) ;
2024-07-15 17:13:18 -04:00
AppendMenuW ( hMenu , MF_STRING , Popup_OpenSettings , L " Open Settings file " ) ;
2024-01-09 12:43:47 -05:00
AppendMenuW ( hMenu , MF_STRING , Popup_Quit , L " &Quit " ) ;
m_showPopup = true ;
switch ( TrackPopupMenu ( hMenu , TPM_RETURNCMD | TPM_RIGHTBUTTON , point . x , point . y , 0 , hWnd , NULL ) )
{
case Popup_SaveAs :
{
2024-07-10 18:24:10 -04:00
OPENFILENAME ofn ;
TCHAR szFile [ 260 ] = { 0 } ;
2024-01-09 12:43:47 -05:00
// Initialize OPENFILENAME
ZeroMemory ( & ofn , sizeof ( ofn ) ) ;
ofn . lStructSize = sizeof ( ofn ) ;
ofn . hwndOwner = hWnd ;
ofn . lpstrFile = szFile ;
ofn . nMaxFile = sizeof ( szFile ) ;
ofn . lpstrDefExt = TC ( " uba " ) ;
ofn . lpstrFilter = TC ( " Uba \0 *.uba \0 All \0 *.* \0 " ) ;
ofn . nFilterIndex = 1 ;
ofn . lpstrFileTitle = NULL ;
ofn . nMaxFileTitle = 0 ;
ofn . lpstrInitialDir = NULL ;
//ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
if ( GetSaveFileName ( & ofn ) )
m_trace . SaveAs ( ofn . lpstrFile ) ;
break ;
}
2024-07-10 18:24:10 -04:00
case Popup_ShowProcessText :
m_config . ShowProcessText = ! m_config . ShowProcessText ;
2024-08-06 15:31:53 -04:00
Redraw ( true ) ;
2024-03-03 20:42:41 -05:00
break ;
2024-07-10 18:24:10 -04:00
case Popup_ShowReadWriteColors :
m_config . ShowReadWriteColors = ! m_config . ShowReadWriteColors ;
2024-07-17 17:48:42 -04:00
DirtyBitmaps ( false ) ;
2024-08-06 15:31:53 -04:00
Redraw ( true ) ;
2024-03-08 18:31:48 -05:00
break ;
2024-07-10 18:24:10 -04:00
case Popup_ScaleHorizontalWithScrollWheel :
m_config . ScaleHorizontalWithScrollWheel = ! m_config . ScaleHorizontalWithScrollWheel ;
break ;
2024-07-16 16:27:53 -04:00
case Popup_ShowAllTraces :
m_config . ShowAllTraces = ! m_config . ShowAllTraces ;
break ;
2024-07-17 17:48:42 -04:00
case Popup_SortActiveRemoteSessions :
m_config . SortActiveRemoteSessions = ! m_config . SortActiveRemoteSessions ;
2024-08-06 15:31:53 -04:00
Redraw ( true ) ;
2024-07-19 19:12:12 -04:00
break ;
case Popup_AutoScaleHorizontal :
m_config . AutoScaleHorizontal = ! m_config . AutoScaleHorizontal ;
2024-08-06 15:31:53 -04:00
Redraw ( true ) ;
2024-07-19 19:12:12 -04:00
break ;
case Popup_LockTimelineToBottom :
m_config . LockTimelineToBottom = ! m_config . LockTimelineToBottom ;
2024-08-06 15:31:53 -04:00
Redraw ( true ) ;
2024-07-17 17:48:42 -04:00
break ;
2024-07-10 18:24:10 -04:00
case Popup_DarkMode :
{
m_config . DarkMode = ! m_config . DarkMode ;
2024-07-17 17:48:42 -04:00
DirtyBitmaps ( false ) ;
2024-07-10 18:24:10 -04:00
InitBrushes ( ) ;
SetWindowTheme ( m_hwnd , m_config . DarkMode ? L " DarkMode_Explorer " : L " Explorer " , NULL ) ;
SendMessageW ( m_hwnd , WM_THEMECHANGED , 0 , 0 ) ;
BOOL useDarkMode = m_config . DarkMode ;
u32 attribute = 20 ; // DWMWA_USE_IMMERSIVE_DARK_MODE
DwmSetWindowAttribute ( m_hwnd , attribute , & useDarkMode , sizeof ( useDarkMode ) ) ;
2024-08-06 15:31:53 -04:00
Redraw ( true ) ;
2024-07-10 18:24:10 -04:00
break ;
}
2024-07-15 17:13:18 -04:00
case Popup_AutoSaveSettings :
m_config . AutoSaveSettings = ! m_config . AutoSaveSettings ;
break ;
2024-01-30 02:19:51 -05:00
case Popup_Replay :
2024-07-15 17:13:18 -04:00
PostNewTrace ( 1 , false ) ;
2024-01-30 02:19:51 -05:00
break ;
case Popup_Play :
2024-02-02 18:30:47 -05:00
Pause ( false ) ;
2024-01-30 02:19:51 -05:00
break ;
case Popup_Pause :
2024-02-02 18:30:47 -05:00
Pause ( true ) ;
2024-01-30 02:19:51 -05:00
break ;
case Popup_JumpToEnd :
m_traceView . finished = true ;
2024-07-15 17:13:18 -04:00
PostNewTrace ( 0 , false ) ;
2024-01-30 02:19:51 -05:00
break ;
2024-01-09 12:43:47 -05:00
2024-07-10 18:24:10 -04:00
case Popup_SaveSettings :
SaveSettings ( ) ;
break ;
2024-07-15 17:13:18 -04:00
case Popup_OpenSettings :
ShellExecuteW ( NULL , L " open " , m_config . filename . c_str ( ) , NULL , NULL , SW_SHOW ) ;
break ;
2024-01-09 12:43:47 -05:00
case Popup_Quit : // Quit
2024-07-19 15:56:16 -04:00
PostQuit ( ) ;
2024-01-09 12:43:47 -05:00
break ;
2024-07-15 17:13:18 -04:00
case Popup_IncreaseFontSize :
ChangeFontSize ( 1 ) ;
break ;
case Popup_DecreaseFontSize :
ChangeFontSize ( - 1 ) ;
break ;
2024-01-09 12:43:47 -05:00
case Popup_CopySessionInfo :
{
TString str ;
auto & session = m_traceView . sessions [ m_sessionSelectedIndex ] ;
2024-07-30 01:49:32 -04:00
str . append ( session . fullName ) . append ( TC ( " \n " ) ) ;
2024-01-09 12:43:47 -05:00
for ( auto & line : session . summary )
str . append ( line ) . append ( TC ( " \n " ) ) ;
CopyTextToClipboard ( str ) ;
break ;
}
case Popup_CopyProcessInfo :
{
TString str ;
WriteTextLogger logger ( str ) ;
2024-08-06 15:31:53 -04:00
const TraceView : : Process & process = m_traceView . GetProcess ( m_processSelectedLocation ) ;
2024-01-09 12:43:47 -05:00
WriteProcessStats ( logger , process ) ;
CopyTextToClipboard ( str ) ;
2024-01-17 16:49:50 -05:00
break ;
}
case Popup_CopyProcessLog :
{
TString str ;
2024-08-06 15:31:53 -04:00
const TraceView : : Process & process = m_traceView . GetProcess ( m_processSelectedLocation ) ;
2024-01-17 16:49:50 -05:00
bool isFirst = true ;
for ( auto line : process . logLines )
{
if ( ! isFirst )
str + = ' \n ' ;
isFirst = false ;
str + = line . text ;
}
CopyTextToClipboard ( str ) ;
2024-01-09 12:43:47 -05:00
break ;
}
}
DestroyMenu ( hMenu ) ;
m_showPopup = false ;
UnselectAndRedraw ( ) ;
2023-11-29 18:47:11 -05:00
break ;
}
case WM_MBUTTONUP :
{
2024-05-22 17:27:49 -04:00
StopDragToScroll ( ) ;
2023-11-29 18:47:11 -05:00
//m_processSelected = false;
break ;
}
case WM_KEYDOWN :
{
if ( wParam = = VK_SPACE )
2024-02-02 18:30:47 -05:00
Pause ( ! m_paused ) ;
2024-07-15 17:13:18 -04:00
if ( wParam = = VK_ADD )
2024-07-30 01:49:32 -04:00
+ + m_replay ;
2024-07-15 17:13:18 -04:00
if ( wParam = = VK_SUBTRACT )
2024-07-30 01:49:32 -04:00
- - m_replay ;
2023-11-29 18:47:11 -05:00
break ;
}
case WM_VSCROLL :
{
RECT r ;
GetClientRect ( hWnd , & r ) ;
float oldScrollY = m_scrollPosY ;
2024-05-22 17:12:32 -04:00
// HIWORD(wParam) only carries 16-bits, so use GetScrollInfo for larger scroll areas
SCROLLINFO scrollInfo = { } ;
scrollInfo . cbSize = sizeof ( scrollInfo ) ;
scrollInfo . fMask = SIF_TRACKPOS ;
GetScrollInfo ( m_hwnd , SB_VERT , & scrollInfo ) ;
2023-11-29 18:47:11 -05:00
switch ( LOWORD ( wParam ) )
{
case SB_THUMBTRACK :
case SB_THUMBPOSITION :
2024-05-22 17:12:32 -04:00
m_scrollPosY = - float ( scrollInfo . nTrackPos ) ;
2023-11-29 18:47:11 -05:00
break ;
case SB_PAGEDOWN :
m_scrollPosY = m_scrollPosY - r . bottom ;
break ;
case SB_PAGEUP :
m_scrollPosY = m_scrollPosY + r . bottom ;
break ;
case SB_LINEDOWN :
m_scrollPosY - = 30 ;
break ;
case SB_LINEUP :
m_scrollPosY + = 30 ;
break ;
}
m_scrollPosY = Min ( Max ( m_scrollPosY , float ( r . bottom - m_contentHeight ) ) , 0.0f ) ;
if ( oldScrollY ! = m_scrollPosY )
{
UpdateScrollbars ( true ) ;
2024-08-06 15:31:53 -04:00
Redraw ( false ) ;
2023-11-29 18:47:11 -05:00
}
return 0 ;
}
case WM_HSCROLL :
{
RECT r ;
GetClientRect ( hWnd , & r ) ;
float oldScrollX = m_scrollPosX ;
bool autoScroll = false ;
2024-05-22 17:12:32 -04:00
// HIWORD(wParam) only carries 16-bits, so use GetScrollInfo for larger scroll areas
SCROLLINFO scrollInfo = { } ;
scrollInfo . cbSize = sizeof ( scrollInfo ) ;
scrollInfo . fMask = SIF_TRACKPOS ;
GetScrollInfo ( m_hwnd , SB_HORZ , & scrollInfo ) ;
2023-11-29 18:47:11 -05:00
switch ( LOWORD ( wParam ) )
{
case SB_THUMBTRACK :
2024-05-22 17:12:32 -04:00
m_scrollPosX = - float ( scrollInfo . nTrackPos ) ;
2023-11-29 18:47:11 -05:00
if ( m_contentWidthWhenThumbTrack = = 0 )
m_contentWidthWhenThumbTrack = m_contentWidth ;
break ;
case SB_THUMBPOSITION :
autoScroll = m_contentWidthWhenThumbTrack - r . right < = HIWORD ( wParam ) + 10 ;
m_contentWidthWhenThumbTrack = 0 ;
2024-05-22 17:12:32 -04:00
m_scrollPosX = - float ( scrollInfo . nTrackPos ) ;
2023-11-29 18:47:11 -05:00
break ;
case SB_PAGEDOWN :
m_scrollPosX = m_scrollPosX - r . right ;
break ;
case SB_PAGEUP :
m_scrollPosX = m_scrollPosX + r . right ;
break ;
case SB_LINEDOWN :
m_scrollPosX - = 30 ;
break ;
case SB_LINEUP :
m_scrollPosX + = 30 ;
break ;
case SB_ENDSCROLL :
return 0 ;
}
int minScroll = r . right - m_contentWidth ;
m_autoScroll = ! m_traceView . finished & & ( m_scrollPosX < = minScroll | | autoScroll ) ;
m_scrollPosX = Min ( Max ( m_scrollPosX , float ( r . right - m_contentWidth ) ) , 0.0f ) ;
if ( oldScrollX ! = m_scrollPosX )
{
UpdateScrollbars ( true ) ;
2024-08-06 15:31:53 -04:00
Redraw ( false ) ;
2023-11-29 18:47:11 -05:00
}
return 0 ;
}
}
return DefWindowProc ( hWnd , Msg , wParam , lParam ) ;
}
LRESULT CALLBACK Visualizer : : StaticWinProc ( HWND hWnd , UINT Msg , WPARAM wParam , LPARAM lParam )
{
auto thisPtr = ( Visualizer * ) GetWindowLongPtr ( hWnd , GWLP_USERDATA ) ;
if ( ! thisPtr & & Msg = = WM_CREATE )
{
thisPtr = ( Visualizer * ) lParam ;
SetWindowLongPtr ( hWnd , GWLP_USERDATA , lParam ) ;
2024-08-06 15:31:53 -04:00
//EnableNonClientDpiScaling(hWnd);
2023-11-29 18:47:11 -05:00
}
if ( thisPtr & & hWnd = = thisPtr - > m_hwnd )
return thisPtr - > WinProc ( hWnd , Msg , wParam , lParam ) ;
else
return DefWindowProc ( hWnd , Msg , wParam , lParam ) ;
}
}