mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-05 01:20:42 +00:00
610b22f600
* In a couple of places I had to resort to if(IsHighContrast) but mostly this is just using system brushes consistently or not assuming black text. * The default DockPanel theme doesn't work well, so make a minimal high- contrast theme for it and assign it everywhere. * The pipeline flow was using fixed colours, use system brushes for the different elements and switch based on high-contrast to ensure active and inactive stages are visible (using ActiveCaption looks bad on normal themes because it's a big block of colour). * For some reason the flat toolstrip renderer doesn't handle white-on- black themes, but the system one does. It's a little clunkier but it shows up correctly without writing tons of custom painting code. * Range histogram uses a properly contrasting colour for the border. * Treelist views use a better system colour for selected rows when inactive and hovered rows (when high contrast). * Mesh view grids have a system background instead of white * Various things (pipeline state, mesh viewe) set text colour when colourising backgrounds of things instead of assuming black.
509 lines
16 KiB
C#
509 lines
16 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Data;
|
|
using System.Drawing;
|
|
using System.Linq;
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.Windows.Forms;
|
|
using WeifenLuo.WinFormsUI.Docking;
|
|
using renderdocui.Code;
|
|
using renderdoc;
|
|
using IronPython.Hosting;
|
|
using Microsoft.Scripting.Hosting;
|
|
using IronPython.Runtime.Exceptions;
|
|
using System.Threading;
|
|
|
|
namespace renderdocui.Windows.Dialogs
|
|
{
|
|
public partial class PythonShell : DockContent
|
|
{
|
|
Core m_Core = null;
|
|
|
|
private ScriptEngine pythonengine = null;
|
|
private ScriptScope shellscope = null;
|
|
|
|
ScintillaNET.Scintilla scriptEditor = null;
|
|
|
|
public PythonShell(Core core)
|
|
{
|
|
InitializeComponent();
|
|
|
|
if (SystemInformation.HighContrast)
|
|
{
|
|
toolStrip1.Renderer = new ToolStripSystemRenderer();
|
|
toolStrip2.Renderer = new ToolStripSystemRenderer();
|
|
}
|
|
|
|
shellTable.Dock = DockStyle.Fill;
|
|
scriptTable.Dock = DockStyle.Fill;
|
|
|
|
scriptEditor = new ScintillaNET.Scintilla();
|
|
((System.ComponentModel.ISupportInitialize)(scriptEditor)).BeginInit();
|
|
|
|
scriptEditor.Dock = System.Windows.Forms.DockStyle.Fill;
|
|
scriptEditor.Location = new System.Drawing.Point(3, 3);
|
|
scriptEditor.Name = "scripteditor";
|
|
scriptEditor.Font = new Font("Consolas", 8.25F, FontStyle.Regular, GraphicsUnit.Point, 0);
|
|
|
|
scriptEditor.Margins.Left = 4;
|
|
scriptEditor.Margins.Margin0.Width = 30;
|
|
scriptEditor.Margins.Margin1.Width = 0;
|
|
scriptEditor.Margins.Margin2.Width = 16;
|
|
|
|
scriptEditor.Markers[0].BackColor = System.Drawing.Color.LightCoral;
|
|
|
|
scriptEditor.ConfigurationManager.Language = "python";
|
|
|
|
((System.ComponentModel.ISupportInitialize)(scriptEditor)).EndInit();
|
|
|
|
scriptEditor.KeyDown += new KeyEventHandler(scriptEditor_KeyDown);
|
|
|
|
newScript.PerformClick();
|
|
|
|
scriptEditor.Scrolling.HorizontalWidth = 1;
|
|
|
|
const uint SCI_SETSCROLLWIDTHTRACKING = 2516;
|
|
scriptEditor.NativeInterface.SendMessageDirect(SCI_SETSCROLLWIDTHTRACKING, true);
|
|
|
|
scriptSplit.Panel1.Controls.Add(scriptEditor);
|
|
|
|
m_Core = core;
|
|
|
|
pythonengine = NewEngine();
|
|
|
|
mode_Changed(shellMode, null);
|
|
|
|
clearCmd_Click(null, null);
|
|
|
|
EnableButtons(true);
|
|
}
|
|
|
|
void scriptEditor_KeyDown(object sender, KeyEventArgs e)
|
|
{
|
|
if (e.Control)
|
|
{
|
|
if (e.KeyCode == Keys.O)
|
|
{
|
|
openButton.PerformClick();
|
|
e.Handled = true;
|
|
e.SuppressKeyPress = true;
|
|
}
|
|
if (e.KeyCode == Keys.S)
|
|
{
|
|
saveAs.PerformClick();
|
|
e.Handled = true;
|
|
e.SuppressKeyPress = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private ScriptEngine NewEngine()
|
|
{
|
|
var engine = Python.CreateEngine();
|
|
|
|
List<string> searches = new List<string>(engine.GetSearchPaths());
|
|
|
|
searches.Add(Directory.GetCurrentDirectory());
|
|
|
|
string libspath = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "pythonlibs.zip");
|
|
|
|
if (File.Exists(libspath))
|
|
searches.Add(libspath);
|
|
|
|
engine.SetSearchPaths(searches);
|
|
|
|
return engine;
|
|
}
|
|
|
|
private ScriptScope NewScope(ScriptEngine engine)
|
|
{
|
|
var scope = engine.CreateScope();
|
|
|
|
scope.SetVariable("renderdoc", m_Core);
|
|
|
|
return scope;
|
|
}
|
|
|
|
private Thread pythonThread = null;
|
|
|
|
private MemoryStream stdout = null;
|
|
private StreamWriter stdoutwriter = null;
|
|
private StreamReader stdoutreader = null;
|
|
private int linenum = -1;
|
|
|
|
private string Execute(ScriptEngine engine, ScriptScope scope, string script)
|
|
{
|
|
stdout = new MemoryStream();
|
|
stdoutwriter = new StreamWriter(stdout);
|
|
stdoutreader = new StreamReader(stdout);
|
|
|
|
engine.Runtime.IO.SetOutput(stdout, stdoutwriter);
|
|
|
|
try
|
|
{
|
|
m_Core.Renderer.SetExceptionCatching(true);
|
|
dynamic ret = engine.CreateScriptSourceFromString(script).Execute(scope);
|
|
m_Core.Renderer.SetExceptionCatching(false);
|
|
if (ret != null)
|
|
{
|
|
stdoutwriter.Write(ret.ToString() + Environment.NewLine);
|
|
stdoutwriter.Flush();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
m_Core.Renderer.SetExceptionCatching(false);
|
|
|
|
// IronPython throws so many exceptions, we don't want to kill the application
|
|
// so we just swallow Exception to cover all the bases
|
|
string exstr = engine.GetService<ExceptionOperations>().FormatException(ex);
|
|
stdoutwriter.Write(exstr);
|
|
stdoutwriter.Write(Environment.NewLine);
|
|
stdoutwriter.Flush();
|
|
}
|
|
|
|
stdout.Seek(0, SeekOrigin.Begin);
|
|
|
|
string output = stdoutreader.ReadToEnd();
|
|
|
|
stdoutreader.Dispose();
|
|
|
|
stdout = null;
|
|
stdoutreader = null;
|
|
stdoutwriter = null;
|
|
|
|
return output;
|
|
}
|
|
|
|
private void mode_Changed(object sender, EventArgs e)
|
|
{
|
|
if (sender == shellMode)
|
|
{
|
|
shellMode.Checked = true;
|
|
scriptMode.Checked = false;
|
|
scriptTable.Visible = false;
|
|
shellTable.Visible = true;
|
|
|
|
Text = "Interactive Python Shell";
|
|
|
|
interactiveInput_TextChanged(null, null);
|
|
}
|
|
else if (sender == scriptMode)
|
|
{
|
|
scriptMode.Checked = true;
|
|
shellMode.Checked = false;
|
|
shellTable.Visible = false;
|
|
scriptTable.Visible = true;
|
|
|
|
scriptOutput.Text = "";
|
|
|
|
Text = "Python Script Execute";
|
|
}
|
|
}
|
|
|
|
private List<string> history = new List<string>();
|
|
int historyidx = -1;
|
|
string workingtext = "";
|
|
|
|
private void interactiveInput_KeyDown(object sender, KeyEventArgs e)
|
|
{
|
|
if (!e.Shift && e.KeyCode == Keys.Return)
|
|
{
|
|
executeCmd_Click(null, null);
|
|
e.Handled = true;
|
|
e.SuppressKeyPress = true;
|
|
}
|
|
|
|
bool moved = false;
|
|
|
|
if (e.KeyCode == Keys.Down && historyidx > -1)
|
|
{
|
|
historyidx--;
|
|
|
|
moved = true;
|
|
}
|
|
if (e.KeyCode == Keys.Up && historyidx + 1 < history.Count)
|
|
{
|
|
if (historyidx == -1)
|
|
workingtext = interactiveInput.Text;
|
|
|
|
historyidx++;
|
|
|
|
moved = true;
|
|
}
|
|
|
|
if (moved)
|
|
{
|
|
if (historyidx == -1)
|
|
interactiveInput.Text = workingtext;
|
|
else
|
|
interactiveInput.Text = history[historyidx];
|
|
|
|
interactiveInput.Select(interactiveInput.Text.Length, 0);
|
|
}
|
|
}
|
|
|
|
private void interactiveInput_TextChanged(object sender, EventArgs e)
|
|
{
|
|
using (var g = interactiveInput.CreateGraphics())
|
|
{
|
|
SizeF MessageSize = g.MeasureString(interactiveInput.Text + "a", interactiveInput.Font,
|
|
interactiveInput.Width, new StringFormat(0));
|
|
|
|
const int maxHeight = 100;
|
|
|
|
if (MessageSize.Height <= maxHeight)
|
|
{
|
|
interactiveInput.Height = 20 + (int)MessageSize.Height;
|
|
interactiveInput.ScrollBars = ScrollBars.None;
|
|
}
|
|
else
|
|
{
|
|
interactiveInput.Height = 20 + maxHeight;
|
|
interactiveInput.ScrollBars = ScrollBars.Vertical;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void interactiveInput_Layout(object sender, LayoutEventArgs e)
|
|
{
|
|
interactiveInput_TextChanged(null, null);
|
|
}
|
|
|
|
private void executeCmd_Click(object sender, EventArgs e)
|
|
{
|
|
var nl = Environment.NewLine;
|
|
interactiveOutput.AppendText(">> " + interactiveInput.Text.Trim().Replace(nl, nl + ">> ") + nl);
|
|
interactiveOutput.AppendText(Execute(pythonengine, shellscope, interactiveInput.Text));
|
|
|
|
history.Insert(0, interactiveInput.Text);
|
|
historyidx = -1;
|
|
workingtext = "";
|
|
|
|
interactiveInput.Text = "";
|
|
}
|
|
|
|
private void clearCmd_Click(object sender, EventArgs e)
|
|
{
|
|
interactiveOutput.Text = String.Format("RenderDoc Python console, powered by IronPython {0}{1}" +
|
|
"The 'renderdoc' object is the Core class instance.{1}", IronPython.CurrentVersion.AssemblyFileVersion, Environment.NewLine);
|
|
|
|
shellscope = NewScope(pythonengine);
|
|
}
|
|
|
|
private static PythonShell me = null;
|
|
|
|
private static TracebackDelegate PythonTrace(TraceBackFrame frame, string result, object payload)
|
|
{
|
|
if(me != null)
|
|
me.TraceCallback(frame, result, payload);
|
|
return PythonTrace;
|
|
}
|
|
|
|
private void TraceCallback(TraceBackFrame frame, string result, object payload)
|
|
{
|
|
if (result == "exception")
|
|
{
|
|
System.Diagnostics.Trace.WriteLine("On line " + frame.f_lineno.ToString());
|
|
}
|
|
linenum = (int)frame.f_lineno - 1;
|
|
|
|
stdoutwriter.Flush();
|
|
stdout.Seek(0, SeekOrigin.Begin);
|
|
string output = stdoutreader.ReadToEnd();
|
|
stdout.Seek(0, SeekOrigin.Begin);
|
|
stdout.SetLength(0);
|
|
|
|
if (output.Length > 0)
|
|
{
|
|
this.BeginInvoke(new Action(() =>
|
|
{
|
|
scriptOutput.Text += output;
|
|
}));
|
|
}
|
|
}
|
|
|
|
private void SetLineNumber(int lineNum)
|
|
{
|
|
for (int i = 0; i < me.scriptEditor.Lines.Count; i++)
|
|
{
|
|
me.scriptEditor.Lines[i].DeleteMarker(0);
|
|
}
|
|
|
|
if (lineNum >= 0 && lineNum < me.scriptEditor.Lines.Count)
|
|
{
|
|
me.scriptEditor.Lines[lineNum].AddMarker(0);
|
|
}
|
|
}
|
|
|
|
private void EnableButtons(bool enable)
|
|
{
|
|
shellMode.Enabled = scriptMode.Enabled =
|
|
newScript.Enabled = openButton.Enabled = saveAs.Enabled =
|
|
runButton.Enabled = enable;
|
|
abortButton.Enabled = !enable;
|
|
}
|
|
|
|
private void abortButton_Click(object sender, EventArgs e)
|
|
{
|
|
if (pythonThread != null)
|
|
pythonThread.Abort();
|
|
}
|
|
|
|
private void runButton_Click(object sender, EventArgs e)
|
|
{
|
|
var scriptscope = NewScope(pythonengine);
|
|
|
|
me = this;
|
|
|
|
var script = scriptEditor.Text;
|
|
|
|
scriptOutput.Text = "";
|
|
linenum = -1;
|
|
|
|
EnableButtons(false);
|
|
|
|
linenumTimer.Enabled = true;
|
|
linenumTimer.Start();
|
|
|
|
pythonThread = Helpers.NewThread(new ThreadStart(() =>
|
|
{
|
|
pythonengine.SetTrace(PythonTrace);
|
|
|
|
string output = "";
|
|
// ignore output, the trace handler above will print output
|
|
try
|
|
{
|
|
output = Execute(pythonengine, scriptscope, script);
|
|
}
|
|
catch (ThreadAbortException)
|
|
{
|
|
// python was interrupted
|
|
Thread.ResetAbort();
|
|
}
|
|
|
|
linenumTimer.Stop();
|
|
pythonengine.SetTrace(null);
|
|
|
|
this.BeginInvoke(new Action(() =>
|
|
{
|
|
pythonThread = null;
|
|
|
|
scriptOutput.Text += output;
|
|
|
|
SetLineNumber(linenum);
|
|
|
|
EnableButtons(true);
|
|
}));
|
|
}));
|
|
|
|
pythonThread.Start();
|
|
}
|
|
|
|
private string ValidData(IDataObject d)
|
|
{
|
|
var fmts = new List<string>(d.GetFormats());
|
|
|
|
if (fmts.Contains("FileName"))
|
|
{
|
|
var data = d.GetData("FileName") as Array;
|
|
|
|
if (data != null && data.Length == 1 && data.GetValue(0) is string)
|
|
{
|
|
var filename = (string)data.GetValue(0);
|
|
|
|
try
|
|
{
|
|
if (File.Exists(filename) && Path.GetExtension(filename).ToUpperInvariant() == ".PY")
|
|
return Path.GetFullPath(filename);
|
|
}
|
|
catch (ArgumentException)
|
|
{
|
|
// invalid path etc
|
|
}
|
|
}
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
private void shell_DragEnter(object sender, DragEventArgs e)
|
|
{
|
|
if (ValidData(e.Data).Length > 0)
|
|
e.Effect = DragDropEffects.Copy;
|
|
else
|
|
e.Effect = DragDropEffects.None;
|
|
}
|
|
|
|
private void shell_DragDrop(object sender, DragEventArgs e)
|
|
{
|
|
string fn = ValidData(e.Data);
|
|
if (fn.Length > 0)
|
|
{
|
|
try
|
|
{
|
|
scriptEditor.Text = File.ReadAllText(fn);
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
MessageBox.Show("Couldn't open file " + saveDialog.FileName + Environment.NewLine + ex.ToString(), "Cannot open script",
|
|
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
|
|
mode_Changed(scriptMode, null);
|
|
}
|
|
}
|
|
|
|
private void openButton_Click(object sender, EventArgs e)
|
|
{
|
|
DialogResult res = openDialog.ShowDialog();
|
|
|
|
if (res == DialogResult.OK)
|
|
{
|
|
try
|
|
{
|
|
scriptEditor.Text = File.ReadAllText(openDialog.FileName);
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
MessageBox.Show("Couldn't load from " + openDialog.FileName + Environment.NewLine + ex.ToString(), "Cannot open script",
|
|
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void saveAs_Click(object sender, EventArgs e)
|
|
{
|
|
DialogResult res = saveDialog.ShowDialog();
|
|
|
|
if (res == DialogResult.OK)
|
|
{
|
|
try
|
|
{
|
|
File.WriteAllText(saveDialog.FileName, scriptEditor.Text);
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
MessageBox.Show("Couldn't save to " + saveDialog.FileName + Environment.NewLine + ex.ToString(), "Cannot save script",
|
|
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void newScript_Click(object sender, EventArgs e)
|
|
{
|
|
scriptEditor.Text = String.Format("# RenderDoc Python scripts, powered by IronPython {0}\n" +
|
|
"# The 'renderdoc' object is the Core class instance.\n\n", IronPython.CurrentVersion.AssemblyFileVersion);
|
|
|
|
scriptEditor.Text = scriptEditor.Text.Replace("\n", Environment.NewLine);
|
|
}
|
|
|
|
private void linenumTimer_Tick(object sender, EventArgs e)
|
|
{
|
|
SetLineNumber(linenum);
|
|
}
|
|
}
|
|
}
|