| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773 |
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Linq;
- using System.Text.RegularExpressions;
- using OpenTK;
- using OpenTK.Graphics;
- using OpenTK.Graphics.OpenGL;
- using OpenTK.Input;
- using Ryujinx.Common;
- using Ryujinx.Profiler.UI.SharpFontHelpers;
- namespace Ryujinx.Profiler.UI
- {
- public partial class ProfileWindow : GameWindow
- {
- // List all buttons for index in button array
- private enum ButtonIndex
- {
- TagTitle = 0,
- InstantTitle = 1,
- AverageTitle = 2,
- TotalTitle = 3,
- FilterBar = 4,
- ShowHideInactive = 5,
- Pause = 6,
- ChangeDisplay = 7,
- // Don't automatically draw after here
- ToggleFlags = 8,
- Step = 9,
- // Update this when new buttons are added.
- // These are indexes to the enum list
- Autodraw = 8,
- Count = 10,
- }
- // Font service
- private FontService _fontService;
- // UI variables
- private ProfileButton[] _buttons;
- private bool _initComplete = false;
- private bool _visible = true;
- private bool _visibleChanged = true;
- private bool _viewportUpdated = true;
- private bool _redrawPending = true;
- private bool _displayGraph = true;
- private bool _displayFlags = true;
- private bool _showInactive = true;
- private bool _paused = false;
- private bool _doStep = false;
- // Layout
- private const int LineHeight = 16;
- private const int TitleHeight = 24;
- private const int TitleFontHeight = 16;
- private const int LinePadding = 2;
- private const int ColumnSpacing = 15;
- private const int FilterHeight = 24;
- private const int BottomBarHeight = FilterHeight + LineHeight;
- // Sorting
- private List<KeyValuePair<ProfileConfig, TimingInfo>> _unsortedProfileData;
- private IComparer<KeyValuePair<ProfileConfig, TimingInfo>> _sortAction = new ProfileSorters.TagAscending();
- // Flag data
- private long[] _timingFlagsAverages;
- private long[] _timingFlagsLast;
- // Filtering
- private string _filterText = "";
- private bool _regexEnabled = false;
- // Scrolling
- private float _scrollPos = 0;
- private float _minScroll = 0;
- private float _maxScroll = 0;
- // Profile data storage
- private List<KeyValuePair<ProfileConfig, TimingInfo>> _sortedProfileData;
- private long _captureTime;
- // Input
- private bool _backspaceDown = false;
- private bool _prevBackspaceDown = false;
- private double _backspaceDownTime = 0;
- // F35 used as no key
- private Key _graphControlKey = Key.F35;
- // Event management
- private double _updateTimer;
- private double _processEventTimer;
- private bool _profileUpdated = false;
- private readonly object _profileDataLock = new object();
- public ProfileWindow()
- // Graphics mode enables 2xAA
- : base(1280, 720, new GraphicsMode(new ColorFormat(8, 8, 8, 8), 1, 1, 2))
- {
- Title = "Profiler";
- Location = new Point(DisplayDevice.Default.Width - 1280,
- (DisplayDevice.Default.Height - 720) - 50);
- if (Profile.UpdateRate <= 0)
- {
- // Perform step regardless of flag type
- Profile.RegisterFlagReceiver((t) =>
- {
- if (!_paused)
- {
- _doStep = true;
- }
- });
- }
- // Large number to force an update on first update
- _updateTimer = 0xFFFF;
- Init();
- // Release context for render thread
- Context.MakeCurrent(null);
- }
-
- public void ToggleVisible()
- {
- _visible = !_visible;
- _visibleChanged = true;
- }
- private void SetSort(IComparer<KeyValuePair<ProfileConfig, TimingInfo>> filter)
- {
- _sortAction = filter;
- _profileUpdated = true;
- }
- #region OnLoad
- /// <summary>
- /// Setup OpenGL and load resources
- /// </summary>
- public void Init()
- {
- GL.ClearColor(Color.Black);
- _fontService = new FontService();
- _fontService.InitializeTextures();
- _fontService.UpdateScreenHeight(Height);
- _buttons = new ProfileButton[(int)ButtonIndex.Count];
- _buttons[(int)ButtonIndex.TagTitle] = new ProfileButton(_fontService, () => SetSort(new ProfileSorters.TagAscending()));
- _buttons[(int)ButtonIndex.InstantTitle] = new ProfileButton(_fontService, () => SetSort(new ProfileSorters.InstantAscending()));
- _buttons[(int)ButtonIndex.AverageTitle] = new ProfileButton(_fontService, () => SetSort(new ProfileSorters.AverageAscending()));
- _buttons[(int)ButtonIndex.TotalTitle] = new ProfileButton(_fontService, () => SetSort(new ProfileSorters.TotalAscending()));
- _buttons[(int)ButtonIndex.Step] = new ProfileButton(_fontService, () => _doStep = true);
- _buttons[(int)ButtonIndex.FilterBar] = new ProfileButton(_fontService, () =>
- {
- _profileUpdated = true;
- _regexEnabled = !_regexEnabled;
- });
- _buttons[(int)ButtonIndex.ShowHideInactive] = new ProfileButton(_fontService, () =>
- {
- _profileUpdated = true;
- _showInactive = !_showInactive;
- });
- _buttons[(int)ButtonIndex.Pause] = new ProfileButton(_fontService, () =>
- {
- _profileUpdated = true;
- _paused = !_paused;
- });
- _buttons[(int)ButtonIndex.ToggleFlags] = new ProfileButton(_fontService, () =>
- {
- _displayFlags = !_displayFlags;
- _redrawPending = true;
- });
- _buttons[(int)ButtonIndex.ChangeDisplay] = new ProfileButton(_fontService, () =>
- {
- _displayGraph = !_displayGraph;
- _redrawPending = true;
- });
- Visible = _visible;
- }
- #endregion
- #region OnResize
- /// <summary>
- /// Respond to resize events
- /// </summary>
- /// <param name="e">Contains information on the new GameWindow size.</param>
- /// <remarks>There is no need to call the base implementation.</remarks>
- protected override void OnResize(EventArgs e)
- {
- _viewportUpdated = true;
- }
- #endregion
- #region OnClose
- /// <summary>
- /// Intercept close event and hide instead
- /// </summary>
- protected override void OnClosing(CancelEventArgs e)
- {
- // Hide window
- _visible = false;
- _visibleChanged = true;
- // Cancel close
- e.Cancel = true;
- base.OnClosing(e);
- }
- #endregion
- #region OnUpdateFrame
- /// <summary>
- /// Profile Update Loop
- /// </summary>
- /// <param name="e">Contains timing information.</param>
- /// <remarks>There is no need to call the base implementation.</remarks>
- public void Update(FrameEventArgs e)
- {
- if (_visibleChanged)
- {
- Visible = _visible;
- _visibleChanged = false;
- }
- // Backspace handling
- if (_backspaceDown)
- {
- if (!_prevBackspaceDown)
- {
- _backspaceDownTime = 0;
- FilterBackspace();
- }
- else
- {
- _backspaceDownTime += e.Time;
- if (_backspaceDownTime > 0.3)
- {
- _backspaceDownTime -= 0.05;
- FilterBackspace();
- }
- }
- }
- _prevBackspaceDown = _backspaceDown;
- // Get timing data if enough time has passed
- _updateTimer += e.Time;
- if (_doStep || ((Profile.UpdateRate > 0) && (!_paused && (_updateTimer > Profile.UpdateRate))))
- {
- _updateTimer = 0;
- _captureTime = PerformanceCounter.ElapsedTicks;
- _timingFlags = Profile.GetTimingFlags();
- _doStep = false;
- _profileUpdated = true;
- _unsortedProfileData = Profile.GetProfilingData();
- (_timingFlagsAverages, _timingFlagsLast) = Profile.GetTimingAveragesAndLast();
-
- }
-
- // Filtering
- if (_profileUpdated)
- {
- lock (_profileDataLock)
- {
- _sortedProfileData = _showInactive ? _unsortedProfileData : _unsortedProfileData.FindAll(kvp => kvp.Value.IsActive);
- if (_sortAction != null)
- {
- _sortedProfileData.Sort(_sortAction);
- }
- if (_regexEnabled)
- {
- try
- {
- Regex filterRegex = new Regex(_filterText, RegexOptions.IgnoreCase);
- if (_filterText != "")
- {
- _sortedProfileData = _sortedProfileData.Where((pair => filterRegex.IsMatch(pair.Key.Search))).ToList();
- }
- }
- catch (ArgumentException argException)
- {
- // Skip filtering for invalid regex
- }
- }
- else
- {
- // Regular filtering
- _sortedProfileData = _sortedProfileData.Where((pair => pair.Key.Search.ToLower().Contains(_filterText.ToLower()))).ToList();
- }
- }
- _profileUpdated = false;
- _redrawPending = true;
- _initComplete = true;
- }
- // Check for events 20 times a second
- _processEventTimer += e.Time;
- if (_processEventTimer > 0.05)
- {
- ProcessEvents();
- if (_graphControlKey != Key.F35)
- {
- switch (_graphControlKey)
- {
- case Key.Left:
- _graphPosition += (long) (GraphMoveSpeed * e.Time);
- break;
- case Key.Right:
- _graphPosition = Math.Max(_graphPosition - (long) (GraphMoveSpeed * e.Time), 0);
- break;
- case Key.Up:
- _graphZoom = MathF.Min(_graphZoom + (float) (GraphZoomSpeed * e.Time), 100.0f);
- break;
- case Key.Down:
- _graphZoom = MathF.Max(_graphZoom - (float) (GraphZoomSpeed * e.Time), 1f);
- break;
- }
- _redrawPending = true;
- }
- _processEventTimer = 0;
- }
- }
- #endregion
- #region OnRenderFrame
- /// <summary>
- /// Profile Render Loop
- /// </summary>
- /// <remarks>There is no need to call the base implementation.</remarks>
- public void Draw()
- {
- if (!_visible || !_initComplete)
- {
- return;
- }
-
- // Update viewport
- if (_viewportUpdated)
- {
- GL.Viewport(0, 0, Width, Height);
- GL.MatrixMode(MatrixMode.Projection);
- GL.LoadIdentity();
- GL.Ortho(0, Width, 0, Height, 0.0, 4.0);
- _fontService.UpdateScreenHeight(Height);
- _viewportUpdated = false;
- _redrawPending = true;
- }
- if (!_redrawPending)
- {
- return;
- }
- // Frame setup
- GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
- GL.ClearColor(Color.Black);
- _fontService.fontColor = Color.White;
- int verticalIndex = 0;
- float width;
- float maxWidth = 0;
- float yOffset = _scrollPos - TitleHeight;
- float xOffset = 10;
- float timingDataLeft;
- float timingWidth;
- // Background lines to make reading easier
- #region Background Lines
- GL.Enable(EnableCap.ScissorTest);
- GL.Scissor(0, BottomBarHeight, Width, Height - TitleHeight - BottomBarHeight);
- GL.Begin(PrimitiveType.Triangles);
- GL.Color3(0.2f, 0.2f, 0.2f);
- for (int i = 0; i < _sortedProfileData.Count; i += 2)
- {
- float top = GetLineY(yOffset, LineHeight, LinePadding, false, i - 1);
- float bottom = GetLineY(yOffset, LineHeight, LinePadding, false, i);
- // Skip rendering out of bounds bars
- if (top < 0 || bottom > Height)
- continue;
- GL.Vertex2(0, bottom);
- GL.Vertex2(0, top);
- GL.Vertex2(Width, top);
- GL.Vertex2(Width, top);
- GL.Vertex2(Width, bottom);
- GL.Vertex2(0, bottom);
- }
- GL.End();
- _maxScroll = (LineHeight + LinePadding) * (_sortedProfileData.Count - 1);
- #endregion
-
- lock (_profileDataLock)
- {
- // Display category
- #region Category
- verticalIndex = 0;
- foreach (var entry in _sortedProfileData)
- {
- if (entry.Key.Category == null)
- {
- verticalIndex++;
- continue;
- }
- float y = GetLineY(yOffset, LineHeight, LinePadding, true, verticalIndex++);
- width = _fontService.DrawText(entry.Key.Category, xOffset, y, LineHeight);
- if (width > maxWidth)
- {
- maxWidth = width;
- }
- }
- GL.Disable(EnableCap.ScissorTest);
- width = _fontService.DrawText("Category", xOffset, Height - TitleFontHeight, TitleFontHeight);
- if (width > maxWidth)
- maxWidth = width;
- xOffset += maxWidth + ColumnSpacing;
- #endregion
- // Display session group
- #region Session Group
- maxWidth = 0;
- verticalIndex = 0;
- GL.Enable(EnableCap.ScissorTest);
- foreach (var entry in _sortedProfileData)
- {
- if (entry.Key.SessionGroup == null)
- {
- verticalIndex++;
- continue;
- }
- float y = GetLineY(yOffset, LineHeight, LinePadding, true, verticalIndex++);
- width = _fontService.DrawText(entry.Key.SessionGroup, xOffset, y, LineHeight);
- if (width > maxWidth)
- {
- maxWidth = width;
- }
- }
- GL.Disable(EnableCap.ScissorTest);
- width = _fontService.DrawText("Group", xOffset, Height - TitleFontHeight, TitleFontHeight);
- if (width > maxWidth)
- maxWidth = width;
- xOffset += maxWidth + ColumnSpacing;
- #endregion
- // Display session item
- #region Session Item
- maxWidth = 0;
- verticalIndex = 0;
- GL.Enable(EnableCap.ScissorTest);
- foreach (var entry in _sortedProfileData)
- {
- if (entry.Key.SessionItem == null)
- {
- verticalIndex++;
- continue;
- }
- float y = GetLineY(yOffset, LineHeight, LinePadding, true, verticalIndex++);
- width = _fontService.DrawText(entry.Key.SessionItem, xOffset, y, LineHeight);
- if (width > maxWidth)
- {
- maxWidth = width;
- }
- }
- GL.Disable(EnableCap.ScissorTest);
- width = _fontService.DrawText("Item", xOffset, Height - TitleFontHeight, TitleFontHeight);
- if (width > maxWidth)
- maxWidth = width;
- xOffset += maxWidth + ColumnSpacing;
- _buttons[(int)ButtonIndex.TagTitle].UpdateSize(0, Height - TitleFontHeight, 0, (int)xOffset, TitleFontHeight);
- #endregion
- // Timing data
- timingWidth = Width - xOffset - 370;
- timingDataLeft = xOffset;
- GL.Scissor((int)xOffset, BottomBarHeight, (int)timingWidth, Height - TitleHeight - BottomBarHeight);
- if (_displayGraph)
- {
- DrawGraph(xOffset, yOffset, timingWidth);
- }
- else
- {
- DrawBars(xOffset, yOffset, timingWidth);
- }
- GL.Scissor(0, BottomBarHeight, Width, Height - TitleHeight - BottomBarHeight);
- if (!_displayGraph)
- {
- _fontService.DrawText("Blue: Instant, Green: Avg, Red: Total", xOffset, Height - TitleFontHeight, TitleFontHeight);
- }
- xOffset = Width - 360;
- // Display timestamps
- #region Timestamps
- verticalIndex = 0;
- long totalInstant = 0;
- long totalAverage = 0;
- long totalTime = 0;
- long totalCount = 0;
- GL.Enable(EnableCap.ScissorTest);
- foreach (var entry in _sortedProfileData)
- {
- float y = GetLineY(yOffset, LineHeight, LinePadding, true, verticalIndex++);
- _fontService.DrawText($"{GetTimeString(entry.Value.Instant)} ({entry.Value.InstantCount})", xOffset, y, LineHeight);
- _fontService.DrawText(GetTimeString(entry.Value.AverageTime), 150 + xOffset, y, LineHeight);
- _fontService.DrawText(GetTimeString(entry.Value.TotalTime), 260 + xOffset, y, LineHeight);
- totalInstant += entry.Value.Instant;
- totalAverage += entry.Value.AverageTime;
- totalTime += entry.Value.TotalTime;
- totalCount += entry.Value.InstantCount;
- }
- GL.Disable(EnableCap.ScissorTest);
- float yHeight = Height - TitleFontHeight;
- _fontService.DrawText("Instant (Count)", xOffset, yHeight, TitleFontHeight);
- _buttons[(int)ButtonIndex.InstantTitle].UpdateSize((int)xOffset, (int)yHeight, 0, 130, TitleFontHeight);
- _fontService.DrawText("Average", 150 + xOffset, yHeight, TitleFontHeight);
- _buttons[(int)ButtonIndex.AverageTitle].UpdateSize((int)(150 + xOffset), (int)yHeight, 0, 130, TitleFontHeight);
- _fontService.DrawText("Total (ms)", 260 + xOffset, yHeight, TitleFontHeight);
- _buttons[(int)ButtonIndex.TotalTitle].UpdateSize((int)(260 + xOffset), (int)yHeight, 0, Width, TitleFontHeight);
- // Totals
- yHeight = FilterHeight + 3;
- int textHeight = LineHeight - 2;
- _fontService.fontColor = new Color(100, 100, 255, 255);
- float tempWidth = _fontService.DrawText($"Host {GetTimeString(_timingFlagsLast[(int)TimingFlagType.SystemFrame])} " +
- $"({GetTimeString(_timingFlagsAverages[(int)TimingFlagType.SystemFrame])})", 5, yHeight, textHeight);
- _fontService.fontColor = Color.Red;
- _fontService.DrawText($"Game {GetTimeString(_timingFlagsLast[(int)TimingFlagType.FrameSwap])} " +
- $"({GetTimeString(_timingFlagsAverages[(int)TimingFlagType.FrameSwap])})", 15 + tempWidth, yHeight, textHeight);
- _fontService.fontColor = Color.White;
-
- _fontService.DrawText($"{GetTimeString(totalInstant)} ({totalCount})", xOffset, yHeight, textHeight);
- _fontService.DrawText(GetTimeString(totalAverage), 150 + xOffset, yHeight, textHeight);
- _fontService.DrawText(GetTimeString(totalTime), 260 + xOffset, yHeight, textHeight);
- #endregion
- }
- #region Bottom bar
- // Show/Hide Inactive
- float widthShowHideButton = _buttons[(int)ButtonIndex.ShowHideInactive].UpdateSize($"{(_showInactive ? "Hide" : "Show")} Inactive", 5, 5, 4, 16);
- // Play/Pause
- float widthPlayPauseButton = _buttons[(int)ButtonIndex.Pause].UpdateSize(_paused ? "Play" : "Pause", 15 + (int)widthShowHideButton, 5, 4, 16) + widthShowHideButton;
- // Step
- float widthStepButton = widthPlayPauseButton;
- if (_paused)
- {
- widthStepButton += _buttons[(int)ButtonIndex.Step].UpdateSize("Step", (int)(25 + widthPlayPauseButton), 5, 4, 16) + 10;
- _buttons[(int)ButtonIndex.Step].Draw();
- }
- // Change display
- float widthChangeDisplay = _buttons[(int)ButtonIndex.ChangeDisplay].UpdateSize($"View: {(_displayGraph ? "Graph" : "Bars")}", 25 + (int)widthStepButton, 5, 4, 16) + widthStepButton;
- width = widthChangeDisplay;
- if (_displayGraph)
- {
- width += _buttons[(int) ButtonIndex.ToggleFlags].UpdateSize($"{(_displayFlags ? "Hide" : "Show")} Flags", 35 + (int)widthChangeDisplay, 5, 4, 16) + 10;
- _buttons[(int)ButtonIndex.ToggleFlags].Draw();
- }
- // Filter bar
- _fontService.DrawText($"{(_regexEnabled ? "Regex " : "Filter")}: {_filterText}", 35 + width, 7, 16);
- _buttons[(int)ButtonIndex.FilterBar].UpdateSize((int)(45 + width), 0, 0, Width, FilterHeight);
- #endregion
- // Draw buttons
- for (int i = 0; i < (int)ButtonIndex.Autodraw; i++)
- {
- _buttons[i].Draw();
- }
-
- // Dividing lines
- #region Dividing lines
- GL.Color3(Color.White);
- GL.Begin(PrimitiveType.Lines);
- // Top divider
- GL.Vertex2(0, Height -TitleHeight);
- GL.Vertex2(Width, Height - TitleHeight);
- // Bottom divider
- GL.Vertex2(0, FilterHeight);
- GL.Vertex2(Width, FilterHeight);
- GL.Vertex2(0, BottomBarHeight);
- GL.Vertex2(Width, BottomBarHeight);
- // Bottom vertical dividers
- GL.Vertex2(widthShowHideButton + 10, 0);
- GL.Vertex2(widthShowHideButton + 10, FilterHeight);
- GL.Vertex2(widthPlayPauseButton + 20, 0);
- GL.Vertex2(widthPlayPauseButton + 20, FilterHeight);
- if (_paused)
- {
- GL.Vertex2(widthStepButton + 20, 0);
- GL.Vertex2(widthStepButton + 20, FilterHeight);
- }
- if (_displayGraph)
- {
- GL.Vertex2(widthChangeDisplay + 30, 0);
- GL.Vertex2(widthChangeDisplay + 30, FilterHeight);
- }
- GL.Vertex2(width + 30, 0);
- GL.Vertex2(width + 30, FilterHeight);
- // Column dividers
- float timingDataTop = Height - TitleHeight;
- GL.Vertex2(timingDataLeft, FilterHeight);
- GL.Vertex2(timingDataLeft, timingDataTop);
-
- GL.Vertex2(timingWidth + timingDataLeft, FilterHeight);
- GL.Vertex2(timingWidth + timingDataLeft, timingDataTop);
- GL.End();
- #endregion
- _redrawPending = false;
- SwapBuffers();
- }
- #endregion
- private string GetTimeString(long timestamp)
- {
- float time = (float)timestamp / PerformanceCounter.TicksPerMillisecond;
- return (time < 1) ? $"{time * 1000:F3}us" : $"{time:F3}ms";
- }
- private void FilterBackspace()
- {
- if (_filterText.Length <= 1)
- {
- _filterText = "";
- }
- else
- {
- _filterText = _filterText.Remove(_filterText.Length - 1, 1);
- }
- }
- private float GetLineY(float offset, float lineHeight, float padding, bool centre, int line)
- {
- return Height + offset - lineHeight - padding - ((lineHeight + padding) * line) + ((centre) ? padding : 0);
- }
- protected override void OnKeyPress(KeyPressEventArgs e)
- {
- _filterText += e.KeyChar;
- _profileUpdated = true;
- }
- protected override void OnKeyDown(KeyboardKeyEventArgs e)
- {
- switch (e.Key)
- {
- case Key.BackSpace:
- _profileUpdated = _backspaceDown = true;
- return;
- case Key.Left:
- case Key.Right:
- case Key.Up:
- case Key.Down:
- _graphControlKey = e.Key;
- return;
- }
- base.OnKeyUp(e);
- }
- protected override void OnKeyUp(KeyboardKeyEventArgs e)
- {
- // Can't go into switch as value isn't constant
- if (e.Key == Profile.Controls.Buttons.ToggleProfiler)
- {
- ToggleVisible();
- return;
- }
- switch (e.Key)
- {
- case Key.BackSpace:
- _backspaceDown = false;
- return;
- case Key.Left:
- case Key.Right:
- case Key.Up:
- case Key.Down:
- _graphControlKey = Key.F35;
- return;
- }
- base.OnKeyUp(e);
- }
- protected override void OnMouseUp(MouseButtonEventArgs e)
- {
- foreach (ProfileButton button in _buttons)
- {
- if (button.ProcessClick(e.X, Height - e.Y))
- return;
- }
- }
- protected override void OnMouseWheel(MouseWheelEventArgs e)
- {
- _scrollPos += e.Delta * -30;
- if (_scrollPos < _minScroll)
- _scrollPos = _minScroll;
- if (_scrollPos > _maxScroll)
- _scrollPos = _maxScroll;
- _redrawPending = true;
- }
- }
- }
|