mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-08 19:48:20 +02:00
Embedded console / terminal support
This commit is contained in:
parent
60817eec48
commit
20a8e3327c
28 changed files with 2317 additions and 690 deletions
|
@ -10,6 +10,7 @@ namespace System.Diagnostics
|
|||
bool mRedirectStandardOutput = false;
|
||||
bool mRedirectStandardError = false;
|
||||
bool mCreateNoWindow = false;
|
||||
bool mActivateWindow = false;
|
||||
public bool ErrorDialog;
|
||||
//public Windows.Handle ErrorDialogParentHandle;
|
||||
//public ProcessWindowStyle WindowStyle;
|
||||
|
@ -26,6 +27,7 @@ namespace System.Diagnostics
|
|||
public bool RedirectStandardOutput { get { return mRedirectStandardOutput; } set { mRedirectStandardOutput = value; } };
|
||||
public bool RedirectStandardError { get { return mRedirectStandardError; } set { mRedirectStandardError = value; } };
|
||||
public bool CreateNoWindow { get { return mCreateNoWindow; } set { mCreateNoWindow = value; } };
|
||||
public bool ActivateWindow { get { return mActivateWindow; } set { mActivateWindow = value; } };
|
||||
|
||||
Encoding StandardOutputEncoding;
|
||||
Encoding StandardErrorEncoding;
|
||||
|
|
|
@ -70,6 +70,8 @@ namespace System.Diagnostics
|
|||
}
|
||||
if (startInfo.CreateNoWindow)
|
||||
spawnFlags |= .NoWindow;
|
||||
if (!startInfo.ActivateWindow)
|
||||
spawnFlags |= .NoActivateWindow;
|
||||
if (startInfo.RedirectStandardInput)
|
||||
spawnFlags |= .RedirectStdInput;
|
||||
if (startInfo.RedirectStandardOutput)
|
||||
|
|
|
@ -24,6 +24,7 @@ namespace System.IO
|
|||
case OpenError(FileOpenError);
|
||||
case ReadError(FileReadError);
|
||||
case SeekError;
|
||||
case PipeListening;
|
||||
}
|
||||
|
||||
static class File
|
||||
|
|
|
@ -72,6 +72,8 @@ namespace System.IO
|
|||
{
|
||||
case .Timeout:
|
||||
return .Err(.ReadError(.Timeout));
|
||||
case .PipeListening:
|
||||
return .Err(.PipeListening);
|
||||
default:
|
||||
return .Err(.ReadError(.Unknown));
|
||||
}
|
||||
|
|
|
@ -109,5 +109,11 @@ namespace System.IO
|
|||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
mMemory.Clear();
|
||||
mPosition = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ namespace System.IO
|
|||
|
||||
public Result<void, FileOpenError> Create(StringView machineName, StringView pipeName, PipeOptions options)
|
||||
{
|
||||
Close();
|
||||
|
||||
Runtime.Assert(mBfpFile == null);
|
||||
|
||||
String path = scope String();
|
||||
|
@ -64,6 +66,8 @@ namespace System.IO
|
|||
|
||||
public Result<void, FileOpenError> Open(StringView machineName, StringView pipeName, PipeOptions options)
|
||||
{
|
||||
Close();
|
||||
|
||||
Runtime.Assert(mBfpFile == null);
|
||||
|
||||
String path = scope String();
|
||||
|
|
|
@ -31,7 +31,8 @@ namespace System
|
|||
PartialData,
|
||||
TempFileError,
|
||||
Timeout,
|
||||
NotEmpty
|
||||
NotEmpty,
|
||||
PipeListening
|
||||
};
|
||||
|
||||
public struct BfpSpawn {}
|
||||
|
@ -263,6 +264,7 @@ namespace System
|
|||
ErrorDialog = 0x400,
|
||||
Window_Hide = 0x800,
|
||||
Window_Maximized = 0x1000,
|
||||
NoActivateWindow = 0x2000
|
||||
};
|
||||
|
||||
public enum BfpKillFlags : int32
|
||||
|
@ -455,6 +457,7 @@ namespace System
|
|||
InsufficientBuffer = (int)Result.InsufficientBuffer,
|
||||
Timeout = (int)Result.Timeout,
|
||||
NotEmpty = (int)Result.NotEmpty,
|
||||
PipeListening = (int)Result.PipeListening,
|
||||
};
|
||||
|
||||
#if !BF_RUNTIME_DISABLE
|
||||
|
|
21
BeefTools/BeefCon/BeefProj.toml
Normal file
21
BeefTools/BeefCon/BeefProj.toml
Normal file
|
@ -0,0 +1,21 @@
|
|||
FileVersion = 1
|
||||
Dependencies = {corlib = "*", Beefy2D = "*"}
|
||||
|
||||
[Project]
|
||||
Name = "BeefCon"
|
||||
TargetType = "BeefGUIApplication"
|
||||
StartupObject = "BeefCon.Program"
|
||||
|
||||
[Configs.Debug.Win64]
|
||||
TargetDirectory = "$(WorkspaceDir)\\..\\..\\IDE\\dist"
|
||||
TargetName = "$(ProjectName)_d"
|
||||
BeefLibType = "DynamicDebug"
|
||||
DebugCommandArguments = "123 1 Powershell"
|
||||
|
||||
[Configs.Release.Win64]
|
||||
TargetDirectory = "$(WorkspaceDir)\\..\\..\\IDE\\dist"
|
||||
DebugCommandArguments = "123 1 Powershell"
|
||||
|
||||
[[ProjectFolder.Items]]
|
||||
Type = "Source"
|
||||
Path = "../../IDE/src/util/ConsoleProvider.bf"
|
8
BeefTools/BeefCon/BeefSpace.toml
Normal file
8
BeefTools/BeefCon/BeefSpace.toml
Normal file
|
@ -0,0 +1,8 @@
|
|||
FileVersion = 1
|
||||
Projects = {BeefCon = {Path = "."}, Beefy2D = "*"}
|
||||
|
||||
[Workspace]
|
||||
StartupProject = "BeefCon"
|
||||
|
||||
[Configs.Release.Win64]
|
||||
BfOptimizationLevel = "OgPlus"
|
167
BeefTools/BeefCon/src/Program.bf
Normal file
167
BeefTools/BeefCon/src/Program.bf
Normal file
|
@ -0,0 +1,167 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.IO;
|
||||
using IDE.util;
|
||||
using System.Diagnostics;
|
||||
using Beefy.widgets;
|
||||
|
||||
namespace BeefCon;
|
||||
|
||||
class Program
|
||||
{
|
||||
BeefConConsoleProvider.Pipe mPipe ~ delete _;
|
||||
WinNativeConsoleProvider mProvider ~ delete _;
|
||||
int32 mPid;
|
||||
int32 mConid;
|
||||
String mExecStr = new .() ~ delete _;
|
||||
SpawnedProcess mSpawnedProcess ~ delete _;
|
||||
|
||||
public ~this()
|
||||
{
|
||||
mSpawnedProcess.Kill();
|
||||
mSpawnedProcess.WaitFor();
|
||||
}
|
||||
|
||||
static mixin GET<T>(var ptr)
|
||||
{
|
||||
*((T*)(ptr += sizeof(T)) - 1)
|
||||
}
|
||||
|
||||
public void MessageLoop()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
switch (mPipe.ReadMessage(-1))
|
||||
{
|
||||
case .Ok(let msg):
|
||||
uint8* ptr = msg.Ptr + 1;
|
||||
switch (*(BeefConConsoleProvider.Message*)msg.Ptr)
|
||||
{
|
||||
case .GetData:
|
||||
mPipe.StartMessage(BeefConConsoleProvider.Message.Data);
|
||||
mPipe.Stream.Write((int32)mProvider.Width);
|
||||
mPipe.Stream.Write((int32)mProvider.Height);
|
||||
mPipe.Stream.Write((int32)mProvider.BufferHeight);
|
||||
mPipe.Stream.Write((int32)mProvider.ScrollTop);
|
||||
mPipe.Stream.Write(mProvider.CursorVisible);
|
||||
mPipe.Stream.Write(mProvider.CursorHeight);
|
||||
mPipe.Stream.Write(mProvider.CursorPos);
|
||||
for (int i < 16)
|
||||
mPipe.Stream.Write(mProvider.GetColor(i));
|
||||
|
||||
for (int row < mProvider.Height)
|
||||
{
|
||||
for (int col < mProvider.Width)
|
||||
{
|
||||
var cell = mProvider.GetCell(col, row);
|
||||
mPipe.Stream.Write(cell.mChar);
|
||||
mPipe.Stream.Write(cell.mAttributes);
|
||||
}
|
||||
}
|
||||
mPipe.EndMessage();
|
||||
case .Resize:
|
||||
int32 cols = GET!<int32>(ptr);
|
||||
int32 rows = GET!<int32>(ptr);
|
||||
bool resizeContent = GET!<bool>(ptr);
|
||||
mProvider.Resize(cols, rows, resizeContent);
|
||||
case .KeyDown:
|
||||
KeyCode keyCode = GET!<KeyCode>(ptr);
|
||||
KeyFlags keyFlags = GET!<KeyFlags>(ptr);
|
||||
mProvider.KeyDown(keyCode, keyFlags);
|
||||
case .KeyUp:
|
||||
KeyCode keyCode = GET!<KeyCode>(ptr);
|
||||
mProvider.KeyUp(keyCode);
|
||||
case .InputString:
|
||||
int32 strLen = GET!<int32>(ptr);
|
||||
StringView str = .((.)ptr, strLen);
|
||||
mProvider.SendInput(str);
|
||||
case .MouseDown:
|
||||
int32 col = GET!<int32>(ptr);
|
||||
int32 row = GET!<int32>(ptr);
|
||||
int32 btnState = GET!<int32>(ptr);
|
||||
int32 btnCount = GET!<int32>(ptr);
|
||||
KeyFlags keyFlags = GET!<KeyFlags>(ptr);
|
||||
mProvider.MouseDown(col, row, btnState, btnCount, keyFlags);
|
||||
case .MouseMove:
|
||||
int32 col = GET!<int32>(ptr);
|
||||
int32 row = GET!<int32>(ptr);
|
||||
int32 btnState = GET!<int32>(ptr);
|
||||
KeyFlags keyFlags = GET!<KeyFlags>(ptr);
|
||||
mProvider.MouseMove(col, row, btnState, keyFlags);
|
||||
case .MouseUp:
|
||||
int32 col = GET!<int32>(ptr);
|
||||
int32 row = GET!<int32>(ptr);
|
||||
int32 btnState = GET!<int32>(ptr);
|
||||
KeyFlags keyFlags = GET!<KeyFlags>(ptr);
|
||||
mProvider.MouseUp(col, row, btnState, keyFlags);
|
||||
case .MouseWheel:
|
||||
int32 col = GET!<int32>(ptr);
|
||||
int32 row = GET!<int32>(ptr);
|
||||
int32 dy = GET!<int32>(ptr);
|
||||
mProvider.MouseWheel(col, row, dy);
|
||||
default:
|
||||
}
|
||||
case .Err(let err):
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
mPipe = new .();
|
||||
mPipe.Listen(mPid, mConid);
|
||||
|
||||
mProvider = new .();
|
||||
//mProvider.mHideNativeConsole = false;
|
||||
mProvider.Attach();
|
||||
|
||||
ProcessStartInfo procInfo = scope ProcessStartInfo();
|
||||
procInfo.UseShellExecute = false;
|
||||
procInfo.SetFileName(mExecStr);
|
||||
|
||||
mSpawnedProcess = new SpawnedProcess();
|
||||
if (mSpawnedProcess.Start(procInfo) case .Err)
|
||||
return;
|
||||
|
||||
while (true)
|
||||
{
|
||||
mProvider.Update();
|
||||
|
||||
var process = Platform.BfpProcess_GetById(null, mPid, null);
|
||||
if (process == null)
|
||||
{
|
||||
Console.Error.WriteLine("Process closed");
|
||||
return;
|
||||
}
|
||||
Platform.BfpProcess_Release(process);
|
||||
MessageLoop();
|
||||
|
||||
if (mPipe.mFailed)
|
||||
return;
|
||||
|
||||
if (!mPipe.mConnected)
|
||||
Thread.Sleep(20);
|
||||
|
||||
if (mSpawnedProcess.WaitFor(0))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public static int Main(String[] args)
|
||||
{
|
||||
if (args.Count < 2)
|
||||
{
|
||||
Console.Error.WriteLine("Usage: BeefCon <pid> <conid> <exe>");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Program pg = scope .();
|
||||
pg.mPid = int32.Parse(args[0]);
|
||||
pg.mConid = int32.Parse(args[1]);
|
||||
pg.mExecStr.Set(args[2]);
|
||||
pg.Run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -54,7 +54,8 @@ enum BfpResult
|
|||
BfpResult_PartialData,
|
||||
BfpResult_TempFileError,
|
||||
BfpResult_Timeout,
|
||||
BfpResult_NotEmpty
|
||||
BfpResult_NotEmpty,
|
||||
BfpResult_PipeListening
|
||||
};
|
||||
|
||||
enum BfpSystemResult
|
||||
|
@ -77,7 +78,8 @@ enum BfpFileResult
|
|||
BfpFileResult_PartialData = BfpResult_PartialData,
|
||||
BfpFileResult_InsufficientBuffer = BfpResult_InsufficientBuffer,
|
||||
BfpFileResult_Timeout = BfpResult_Timeout,
|
||||
BfpFileResult_NotEmpty = BfpResult_NotEmpty
|
||||
BfpFileResult_NotEmpty = BfpResult_NotEmpty,
|
||||
BfpFileResult_PipeListening = BfpResult_PipeListening
|
||||
};
|
||||
|
||||
typedef void(*BfpCrashInfoFunc)();
|
||||
|
@ -197,6 +199,7 @@ enum BfpSpawnFlags
|
|||
BfpSpawnFlag_ErrorDialog = 0x400,
|
||||
BfpSpawnFlag_Window_Hide = 0x800,
|
||||
BfpSpawnFlag_Window_Maximized = 0x1000,
|
||||
BfpSpawnFlag_NoActivateWindow = 0x2000,
|
||||
};
|
||||
|
||||
enum BfpSpawnResult
|
||||
|
@ -420,7 +423,7 @@ enum BfpFileStdKind
|
|||
|
||||
BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_Create(const char* name, BfpFileCreateKind createKind, BfpFileCreateFlags createFlags, BfpFileAttributes createdFileAttr, BfpFileResult* outResult);
|
||||
BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_GetStd(BfpFileStdKind kind, BfpFileResult* outResult);
|
||||
BFP_EXPORT intptr BFP_CALLTYPE BfpFile_GetSystemHandle(BfpFile* file);
|
||||
BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_GetFromHandle(intptr handle, BfpFileResult* outResult);
|
||||
BFP_EXPORT void BFP_CALLTYPE BfpFile_Release(BfpFile* file);
|
||||
BFP_EXPORT void BFP_CALLTYPE BfpFile_Close(BfpFile* file, BfpFileResult* outResult);
|
||||
BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Write(BfpFile* file, const void* buffer, intptr size, int timeoutMS, BfpFileResult* outResult);
|
||||
|
|
|
@ -1689,6 +1689,13 @@ public:
|
|||
creationFlags |= CREATE_NO_WINDOW;
|
||||
// set up the environment block parameter
|
||||
|
||||
if ((flags & BfpSpawnFlag_NoActivateWindow) != 0)
|
||||
{
|
||||
startupInfo.dwFlags |= STARTF_USESHOWWINDOW;
|
||||
startupInfo.wShowWindow = SW_SHOWNOACTIVATE;
|
||||
}
|
||||
// set up the environment block parameter
|
||||
|
||||
WCHAR* targetStrPtr = NULL;
|
||||
UTF16String targetStrW;
|
||||
if ((flags & BfpSpawnFlag_ArgsIncludesTarget) != 0)
|
||||
|
@ -1726,7 +1733,7 @@ public:
|
|||
String str8(env, envSize);
|
||||
envW = UTF8Decode(str8);
|
||||
envVoidPtr = (void*)envW.c_str();
|
||||
startupInfo.dwFlags |= CREATE_UNICODE_ENVIRONMENT;
|
||||
creationFlags |= CREATE_UNICODE_ENVIRONMENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2998,6 +3005,11 @@ BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_Create(const char* path, BfpFileCreateK
|
|||
return bfpFile;
|
||||
}
|
||||
|
||||
BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_GetFromHandle(intptr handle, BfpFileResult* outResult)
|
||||
{
|
||||
return new BfpFile((HANDLE)handle);
|
||||
}
|
||||
|
||||
BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_GetStd(BfpFileStdKind kind, BfpFileResult* outResult)
|
||||
{
|
||||
HANDLE h = INVALID_HANDLE_VALUE;
|
||||
|
@ -3235,6 +3247,9 @@ BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Read(BfpFile* file, void* buffer, intptr
|
|||
int lastError = ::GetLastError();
|
||||
switch (lastError)
|
||||
{
|
||||
case ERROR_PIPE_LISTENING:
|
||||
OUTRESULT(BfpFileResult_PipeListening);
|
||||
break;
|
||||
case ERROR_BROKEN_PIPE: // Just an EOF
|
||||
OUTRESULT(BfpFileResult_Ok);
|
||||
break;
|
||||
|
|
|
@ -138,6 +138,14 @@ namespace IDE.Debugger
|
|||
Allocations = 2
|
||||
}
|
||||
|
||||
public enum OpenFileFlags
|
||||
{
|
||||
None,
|
||||
RedirectStdInput = 1,
|
||||
RedirectStdOutput = 2,
|
||||
RedirectStdError = 4
|
||||
}
|
||||
|
||||
public List<Breakpoint> mBreakpointList = new List<Breakpoint>();
|
||||
public Dictionary<String, StepFilter> mStepFilterList = new Dictionary<String, StepFilter>();
|
||||
|
||||
|
@ -157,7 +165,7 @@ namespace IDE.Debugger
|
|||
static extern bool Debugger_OpenMiniDump(char8* filename);
|
||||
|
||||
[CallingConvention(.Stdcall),CLink]
|
||||
static extern bool Debugger_OpenFile(char8* launchPath, char8* targetPath, char8* args, char8* workingDir, void* envBlockPtr, int32 envBlockLen, bool hotSwapEnabled);
|
||||
static extern bool Debugger_OpenFile(char8* launchPath, char8* targetPath, char8* args, char8* workingDir, void* envBlockPtr, int32 envBlockLen, bool hotSwapEnabled, OpenFileFlags openFileFlags);
|
||||
|
||||
[CallingConvention(.Stdcall),CLink]
|
||||
static extern bool Debugger_ComptimeAttach(void* bfCompiler);
|
||||
|
@ -165,6 +173,9 @@ namespace IDE.Debugger
|
|||
[CallingConvention(.Stdcall),CLink]
|
||||
static extern bool Debugger_Attach(int32 processId, AttachFlags attachFlags);
|
||||
|
||||
[CallingConvention(.Stdcall),CLink]
|
||||
public static extern void Debugger_GetStdHandles(Platform.BfpFile** outStdIn, Platform.BfpFile** outStdOut, Platform.BfpFile** outStdErr);
|
||||
|
||||
[CallingConvention(.Stdcall),CLink]
|
||||
static extern void Debugger_Run();
|
||||
|
||||
|
@ -473,7 +484,7 @@ namespace IDE.Debugger
|
|||
Debugger_FullReportMemory();
|
||||
}
|
||||
|
||||
public bool OpenFile(String launchPath, String targetPath, String args, String workingDir, Span<char8> envBlock, bool isCompiled, bool hotSwapEnabled)
|
||||
public bool OpenFile(String launchPath, String targetPath, String args, String workingDir, Span<char8> envBlock, bool isCompiled, bool hotSwapEnabled, OpenFileFlags openFileFlags)
|
||||
{
|
||||
DeleteAndNullify!(mRunningPath);
|
||||
mRunningPath = new String(launchPath);
|
||||
|
@ -481,7 +492,7 @@ namespace IDE.Debugger
|
|||
mIsComptimeDebug = false;
|
||||
mIsRunningCompiled = isCompiled;
|
||||
mIsRunningWithHotSwap = hotSwapEnabled;
|
||||
return Debugger_OpenFile(launchPath, targetPath, args, workingDir, envBlock.Ptr, (int32)envBlock.Length, hotSwapEnabled);
|
||||
return Debugger_OpenFile(launchPath, targetPath, args, workingDir, envBlock.Ptr, (int32)envBlock.Length, hotSwapEnabled, openFileFlags);
|
||||
}
|
||||
|
||||
public bool ComptimeAttach(BfCompiler compiler)
|
||||
|
@ -1175,6 +1186,11 @@ namespace IDE.Debugger
|
|||
return Debugger_Attach(process.Id, attachFlags);
|
||||
}
|
||||
|
||||
public void GetStdHandles(Platform.BfpFile** outStdIn, Platform.BfpFile** outStdOut, Platform.BfpFile** outStdErr)
|
||||
{
|
||||
Debugger_GetStdHandles(outStdIn, outStdOut, outStdErr);
|
||||
}
|
||||
|
||||
public DbgProfiler StartProfiling(int threadId, String desc, int sampleRate)
|
||||
{
|
||||
DbgProfiler profiler = new DbgProfiler(Debugger_StartProfiling(threadId, desc, (.)sampleRate));
|
||||
|
|
|
@ -345,6 +345,9 @@ namespace IDE
|
|||
bool mProfileCompile = false;
|
||||
ProfileInstance mProfileCompileProfileId;
|
||||
|
||||
Monitor mDebugOutputMonitor = new .() ~ delete _;
|
||||
String mDebugOutput = new .() ~ delete _;
|
||||
|
||||
#if !CLI
|
||||
public IPCHelper mIPCHelper ~ delete _;
|
||||
public bool mIPCHadFocus;
|
||||
|
@ -3779,7 +3782,7 @@ namespace IDE
|
|||
}
|
||||
|
||||
// Always write to STDOUT even if we're running as a GUI, allowing cases like RunAndWait to pass us a stdout handle
|
||||
Console.Error.WriteLine("ERROR: {0}", text);
|
||||
Console.Error.WriteLine("ERROR: {0}", text).IgnoreError();
|
||||
|
||||
#if CLI
|
||||
mFailed = true;
|
||||
|
@ -3806,7 +3809,7 @@ namespace IDE
|
|||
|
||||
mFailed = true;
|
||||
OutputLineSmart("ERROR: {0}", text);
|
||||
Console.Error.WriteLine("ERROR: {0}", text);
|
||||
Console.Error.WriteLine("ERROR: {0}", text).IgnoreError();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@ -8250,8 +8253,11 @@ namespace IDE
|
|||
NOP!();
|
||||
}
|
||||
|
||||
if (!evt.mKeyFlags.HeldKeys.HasFlag(.Alt))
|
||||
{
|
||||
mConsolePanel.SysKeyDown(evt);
|
||||
//mTerminalPanel.SysKeyDown(evt);
|
||||
mTerminalPanel.SysKeyDown(evt);
|
||||
}
|
||||
|
||||
if (evt.mHandled)
|
||||
return;
|
||||
|
@ -8426,8 +8432,8 @@ namespace IDE
|
|||
|
||||
void SysKeyUp(KeyCode keyCode)
|
||||
{
|
||||
//mTerminalPanel.SysKeyUp(keyCode);
|
||||
mConsolePanel.SysKeyUp(keyCode);
|
||||
mTerminalPanel.SysKeyUp(keyCode);
|
||||
}
|
||||
|
||||
void ShowOpenFileInSolutionDialog()
|
||||
|
@ -8786,8 +8792,10 @@ namespace IDE
|
|||
return;
|
||||
StreamReader streamReader = scope StreamReader(fileStream, null, false, 4096);
|
||||
|
||||
int count = 0;
|
||||
while (true)
|
||||
{
|
||||
count++;
|
||||
var buffer = scope String();
|
||||
if (streamReader.ReadLine(buffer) case .Err)
|
||||
break;
|
||||
|
@ -8816,6 +8824,60 @@ namespace IDE
|
|||
}
|
||||
}
|
||||
|
||||
void ReadDebugOutputThread(Object obj)
|
||||
{
|
||||
FileStream fileStream = (.)obj;
|
||||
|
||||
int count = 0;
|
||||
Loop: while (true)
|
||||
{
|
||||
uint8[4096] data = ?;
|
||||
switch (fileStream.TryRead(data, -1))
|
||||
{
|
||||
case .Ok(let len):
|
||||
if (len == 0)
|
||||
break Loop;
|
||||
using (mDebugOutputMonitor.Enter())
|
||||
{
|
||||
for (int i < len)
|
||||
mDebugOutput.Append((char8)data[i]);
|
||||
}
|
||||
case .Err:
|
||||
break Loop;
|
||||
}
|
||||
|
||||
/*var buffer = scope String();
|
||||
if (streamReader.Read(buffer) case .Err)
|
||||
break;
|
||||
using (mDebugOutputMonitor.Enter())
|
||||
mDebugOutput.Add(new String(buffer));*/
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
delete fileStream;
|
||||
}
|
||||
|
||||
/*static void ReadDebugErrorThread(Object obj)
|
||||
{
|
||||
ExecutionInstance executionInstance = (ExecutionInstance)obj;
|
||||
|
||||
FileStream fileStream = scope FileStream();
|
||||
if (executionInstance.mProcess.AttachStandardError(fileStream) case .Err)
|
||||
return;
|
||||
StreamReader streamReader = scope StreamReader(fileStream, null, false, 4096);
|
||||
|
||||
while (true)
|
||||
{
|
||||
var buffer = scope String();
|
||||
if (streamReader.ReadLine(buffer) case .Err)
|
||||
break;
|
||||
|
||||
using (IDEApp.sApp.mMonitor.Enter())
|
||||
executionInstance.mDeferredOutput.Add(new String(buffer));
|
||||
}
|
||||
}*/
|
||||
|
||||
public enum RunFlags
|
||||
{
|
||||
None,
|
||||
|
@ -11943,8 +12005,26 @@ namespace IDE
|
|||
return true;
|
||||
}
|
||||
|
||||
if (!mDebugger.OpenFile(launchPath, targetPath, arguments, workingDir, envBlock, wasCompiled, workspaceOptions.mAllowHotSwapping))
|
||||
if (mSettings.mDebugConsoleKind == .Embedded)
|
||||
{
|
||||
ShowConsole();
|
||||
mConsolePanel.Attach();
|
||||
}
|
||||
|
||||
if (mSettings.mDebugConsoleKind == .RedirectToImmediate)
|
||||
{
|
||||
ShowImmediatePanel();
|
||||
}
|
||||
|
||||
DebugManager.OpenFileFlags openFileFlags = .None;
|
||||
|
||||
if ((mSettings.mDebugConsoleKind == .RedirectToImmediate) || (mSettings.mDebugConsoleKind == .RedirectToOutput))
|
||||
openFileFlags |= .RedirectStdOutput | .RedirectStdError;
|
||||
|
||||
if (!mDebugger.OpenFile(launchPath, targetPath, arguments, workingDir, envBlock, wasCompiled, workspaceOptions.mAllowHotSwapping, openFileFlags))
|
||||
{
|
||||
if (!mSettings.mAlwaysEnableConsole)
|
||||
mConsolePanel.Detach();
|
||||
DeleteAndNullify!(mCompileAndRunStopwatch);
|
||||
return false;
|
||||
}
|
||||
|
@ -12301,8 +12381,10 @@ namespace IDE
|
|||
mOutputPanel = new OutputPanel(true);
|
||||
mOutputPanel.mAutoDelete = false;
|
||||
mTerminalPanel = new TerminalPanel();
|
||||
mTerminalPanel .Init();
|
||||
mTerminalPanel.mAutoDelete = false;
|
||||
mConsolePanel = new ConsolePanel();
|
||||
mConsolePanel.Init();
|
||||
mConsolePanel.mAutoDelete = false;
|
||||
mImmediatePanel = new ImmediatePanel();
|
||||
mImmediatePanel.mAutoDelete = false;
|
||||
|
@ -12963,6 +13045,37 @@ namespace IDE
|
|||
{
|
||||
mDebugger.Update();
|
||||
|
||||
Platform.BfpFile* stdOut = null;
|
||||
Platform.BfpFile* stdError = null;
|
||||
mDebugger.GetStdHandles(null, &stdOut, &stdError);
|
||||
if (stdOut != null)
|
||||
{
|
||||
FileStream fileStream = new FileStream();
|
||||
fileStream.Attach(stdOut);
|
||||
Thread thread = new Thread(new => ReadDebugOutputThread);
|
||||
thread.Start(fileStream, true);
|
||||
}
|
||||
if (stdError != null)
|
||||
{
|
||||
FileStream fileStream = new FileStream();
|
||||
fileStream.Attach(stdError);
|
||||
Thread thread = new Thread(new => ReadDebugOutputThread);
|
||||
thread.Start(fileStream, true);
|
||||
}
|
||||
|
||||
using (mDebugOutputMonitor.Enter())
|
||||
{
|
||||
if (!mDebugOutput.IsEmpty)
|
||||
{
|
||||
mDebugOutput.Replace("\r", "");
|
||||
if (mSettings.mDebugConsoleKind == .RedirectToOutput)
|
||||
mOutputPanel.Write(mDebugOutput);
|
||||
if (mSettings.mDebugConsoleKind == .RedirectToImmediate)
|
||||
mImmediatePanel.Write(mDebugOutput);
|
||||
mDebugOutput.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
runState = mDebugger.GetRunState();
|
||||
mDebuggerPerformingTask = (runState == .DebugEval) || (runState == .DebugEval_Done) || (runState == .SearchingSymSrv);
|
||||
|
||||
|
@ -13514,6 +13627,8 @@ namespace IDE
|
|||
var disassemblyPanel = TryGetDisassemblyPanel(false);
|
||||
if (disassemblyPanel != null)
|
||||
disassemblyPanel.Disable();
|
||||
if (!mSettings.mAlwaysEnableConsole)
|
||||
mConsolePanel.Detach();
|
||||
mDebugger.DisposeNativeBreakpoints();
|
||||
mDebugger.Detach();
|
||||
mDebugger.mIsRunning = false;
|
||||
|
@ -14539,6 +14654,16 @@ namespace IDE
|
|||
RefreshRate = 60;
|
||||
}
|
||||
|
||||
if (mTerminalPanel != null)
|
||||
{
|
||||
// Detach terminal if the panel is closed
|
||||
var terminalTabButton = GetTab(mTerminalPanel);
|
||||
if (terminalTabButton == null)
|
||||
{
|
||||
mTerminalPanel.Detach();
|
||||
}
|
||||
}
|
||||
|
||||
bool hasFocus = false;
|
||||
for (let window in mWindows)
|
||||
{
|
||||
|
@ -14622,6 +14747,15 @@ namespace IDE
|
|||
if (mScriptManager != null)
|
||||
mScriptManager.Update();
|
||||
|
||||
if (mConsolePanel != null)
|
||||
{
|
||||
if ((mSettings.mAlwaysEnableConsole) ||
|
||||
((mSettings.mDebugConsoleKind == .Embedded) && (mDebugger.mIsRunning)))
|
||||
mConsolePanel.Attach();
|
||||
else
|
||||
mConsolePanel.Detach();
|
||||
}
|
||||
|
||||
if (mTestManager != null)
|
||||
{
|
||||
mTestManager.Update();
|
||||
|
|
|
@ -898,8 +898,10 @@ namespace IDE
|
|||
Add("Show Output", "Ctrl+Alt+O");
|
||||
Add("Show Profiler", "Ctrl+Alt+P");
|
||||
Add("Show QuickWatch", "Shift+Alt+W");
|
||||
Add("Show Threads", "Ctrl+Alt+T");
|
||||
Add("Show Threads", "Ctrl+Alt+H");
|
||||
Add("Show Watches", "Ctrl+Alt+W");
|
||||
Add("Show Console", "Ctrl+Alt+N");
|
||||
Add("Show Terminal", "Ctrl+Alt+T");
|
||||
Add("Show Workspace Explorer", "Ctrl+Alt+S");
|
||||
Add("Start Debugging", "F5");
|
||||
Add("Start Without Debugging", "Ctrl+F5");
|
||||
|
@ -1097,6 +1099,14 @@ namespace IDE
|
|||
public bool mDependencies;
|
||||
}
|
||||
|
||||
public enum ConsoleKind
|
||||
{
|
||||
Native,
|
||||
Embedded,
|
||||
RedirectToOutput,
|
||||
RedirectToImmediate,
|
||||
}
|
||||
|
||||
public bool mLoadedSettings;
|
||||
public String mSettingFileText ~ delete _;
|
||||
public DateTime mSettingFileDateTime;
|
||||
|
@ -1110,6 +1120,8 @@ namespace IDE
|
|||
public RecentFiles mRecentFiles = new RecentFiles() ~ delete _;
|
||||
public String mWakaTimeKey = new .() ~ delete _;
|
||||
public String mWindowsTerminal = new .("Powershell") ~ delete _;
|
||||
public ConsoleKind mDebugConsoleKind;
|
||||
public bool mAlwaysEnableConsole;
|
||||
public String mEmscriptenPath = new .() ~ delete _;
|
||||
public bool mEnableDevMode;
|
||||
public TutorialsFinished mTutorialsFinished = .();
|
||||
|
@ -1167,8 +1179,12 @@ namespace IDE
|
|||
mDebuggerSettings.Serialize(sd);
|
||||
using (sd.CreateObject("VisualStudio"))
|
||||
mVSSettings.Serialize(sd);
|
||||
using (sd.CreateObject("Terminal"))
|
||||
using (sd.CreateObject("Console"))
|
||||
{
|
||||
sd.Add("WindowsTerminal", mWindowsTerminal);
|
||||
sd.Add("DebugConsole", mDebugConsoleKind);
|
||||
sd.Add("AlwaysEnableConsole", mAlwaysEnableConsole);
|
||||
}
|
||||
using (sd.CreateObject("Wasm"))
|
||||
sd.Add("EmscriptenPath", mEmscriptenPath);
|
||||
|
||||
|
@ -1258,8 +1274,12 @@ namespace IDE
|
|||
mDebuggerSettings.Deserialize(sd);
|
||||
using (sd.Open("VisualStudio"))
|
||||
mVSSettings.Deserialize(sd);
|
||||
using (sd.Open("Terminal"))
|
||||
using (sd.Open("Console"))
|
||||
{
|
||||
sd.Get("WindowsTerminal", mWindowsTerminal);
|
||||
mDebugConsoleKind = sd.GetEnum<ConsoleKind>("DebugConsole", .Native);
|
||||
mAlwaysEnableConsole = sd.GetBool("AlwaysEnableConsole");
|
||||
}
|
||||
using (sd.Open("Wasm"))
|
||||
sd.Get("EmscriptenPath", mEmscriptenPath);
|
||||
|
||||
|
|
|
@ -614,7 +614,7 @@ namespace IDE
|
|||
|
||||
var envBlock = scope List<char8>();
|
||||
Environment.EncodeEnvironmentVariables(envVars, envBlock);
|
||||
if (!gApp.mDebugger.OpenFile(curProjectInfo.mTestExePath, curProjectInfo.mTestExePath, mTestInstance.mArgs, mTestInstance.mWorkingDir, envBlock, true, false))
|
||||
if (!gApp.mDebugger.OpenFile(curProjectInfo.mTestExePath, curProjectInfo.mTestExePath, mTestInstance.mArgs, mTestInstance.mWorkingDir, envBlock, true, false, .None))
|
||||
{
|
||||
QueueOutputLine("ERROR: Failed debug '{0}'", curProjectInfo.mTestExePath);
|
||||
TestFailed();
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -332,7 +332,7 @@ namespace IDE.ui
|
|||
var envBlock = scope List<char8>();
|
||||
Environment.EncodeEnvironmentVariables(envVars, envBlock);
|
||||
|
||||
if (!gApp.mDebugger.OpenFile(targetPath, targetPath, arguments, workingDir, envBlock, false, false))
|
||||
if (!gApp.mDebugger.OpenFile(targetPath, targetPath, arguments, workingDir, envBlock, false, false, .None))
|
||||
{
|
||||
gApp.Fail(scope String()..AppendF("Unable to open executable for debugging: {0}", targetPath));
|
||||
return;
|
||||
|
|
|
@ -2991,7 +2991,7 @@ namespace IDE.ui
|
|||
}
|
||||
});
|
||||
|
||||
item = folderItem.AddItem("Terminal");
|
||||
item = folderItem.AddItem("External Terminal");
|
||||
item.mOnMenuItemSelected.Add(new (menu) =>
|
||||
{
|
||||
let projectItem = GetSelectedProjectItem();
|
||||
|
@ -3023,6 +3023,34 @@ namespace IDE.ui
|
|||
process.Start(psi).IgnoreError();
|
||||
}
|
||||
});
|
||||
|
||||
item = folderItem.AddItem("Embedded Terminal");
|
||||
item.mOnMenuItemSelected.Add(new (menu) =>
|
||||
{
|
||||
let projectItem = GetSelectedProjectItem();
|
||||
String path = scope String();
|
||||
if (projectItem == null)
|
||||
{
|
||||
path.Set(gApp.mWorkspace.mDir);
|
||||
}
|
||||
else if (let projectFolder = projectItem as ProjectFolder)
|
||||
{
|
||||
if (projectFolder.mParentFolder == null)
|
||||
{
|
||||
path.Set(projectFolder.mProject.mProjectDir);
|
||||
}
|
||||
else
|
||||
projectFolder.GetFullImportPath(path);
|
||||
}
|
||||
else
|
||||
projectItem.mParentFolder.GetFullImportPath(path);
|
||||
|
||||
if (!path.IsWhiteSpace)
|
||||
{
|
||||
gApp.ShowTerminal();
|
||||
gApp.mTerminalPanel.OpenDirectory(path);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (projectItem == null)
|
||||
|
|
|
@ -62,7 +62,7 @@ namespace IDE.ui
|
|||
AddCategoryItem(root, "Compiler");
|
||||
AddCategoryItem(root, "Debugger");
|
||||
AddCategoryItem(root, "Visual Studio");
|
||||
AddCategoryItem(root, "Terminal");
|
||||
AddCategoryItem(root, "Console");
|
||||
AddCategoryItem(root, "Wasm");
|
||||
|
||||
if (!gApp.mSettings.mVSSettings.IsConfigured())
|
||||
|
@ -170,7 +170,7 @@ namespace IDE.ui
|
|||
category.Open(true, true);
|
||||
}
|
||||
|
||||
void PopulateTerminalOptions()
|
||||
void PopulateConsoleOptions()
|
||||
{
|
||||
mCurPropertiesTarget = gApp.mSettings;
|
||||
|
||||
|
@ -179,6 +179,8 @@ namespace IDE.ui
|
|||
category.mIsBold = true;
|
||||
category.mTextColor = Color.Mult(DarkTheme.COLOR_TEXT, cHeaderColor);
|
||||
AddPropertiesItem(category, "Windows Terminal", "mWindowsTerminal");
|
||||
AddPropertiesItem(category, "Debug Console", "mDebugConsoleKind");
|
||||
AddPropertiesItem(category, "Always Enable Console", "mAlwaysEnableConsole");
|
||||
category.Open(true, true);
|
||||
}
|
||||
|
||||
|
@ -438,7 +440,7 @@ namespace IDE.ui
|
|||
case .VisualStudio:
|
||||
PopulateVSOptions();
|
||||
case .Terminal:
|
||||
PopulateTerminalOptions();
|
||||
PopulateConsoleOptions();
|
||||
case .Wasm:
|
||||
PopulateWasmOptions();
|
||||
default:
|
||||
|
|
|
@ -10,15 +10,51 @@ using Beefy.widgets;
|
|||
using Beefy.events;
|
||||
using System.Diagnostics;
|
||||
using Beefy.utils;
|
||||
using IDE.util;
|
||||
|
||||
namespace IDE.ui;
|
||||
|
||||
class TerminalPanel : Panel
|
||||
class TerminalPanel : ConsolePanel
|
||||
{
|
||||
public override void Serialize(StructuredData data)
|
||||
{
|
||||
base.Serialize(data);
|
||||
|
||||
data.Add("Type", "TerminalPanel");
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
var consoleProvider = new BeefConConsoleProvider();
|
||||
consoleProvider.mBeefConExePath = new $"{gApp.mInstallDir}/BeefCon.exe";
|
||||
consoleProvider.mTerminalExe = new .(gApp.mSettings.mWindowsTerminal);
|
||||
|
||||
mConsoleProvider = consoleProvider;
|
||||
}
|
||||
|
||||
public override void AddedToParent()
|
||||
{
|
||||
var consoleProvider = (BeefConConsoleProvider)mConsoleProvider;
|
||||
consoleProvider.mTerminalExe.Set(gApp.mSettings.mWindowsTerminal);
|
||||
consoleProvider.mWorkingDir.Set(gApp.mWorkspace.mDir);
|
||||
mConsoleProvider.Attach();
|
||||
}
|
||||
|
||||
public override void RemovedFromParent(Widget previousParent, WidgetWindow window)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void OpenDirectory(StringView path)
|
||||
{
|
||||
var consoleProvider = (BeefConConsoleProvider)mConsoleProvider;
|
||||
consoleProvider.mWorkingDir.Set(path);
|
||||
consoleProvider.Detach();
|
||||
consoleProvider.Attach();
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -328,7 +328,7 @@ bool CeDebugger::CanOpen(const StringImpl& fileName, DebuggerResult* outResult)
|
|||
return false;
|
||||
}
|
||||
|
||||
void CeDebugger::OpenFile(const StringImpl& launchPath, const StringImpl& targetPath, const StringImpl& args, const StringImpl& workingDir, const Array<uint8>& envBlock, bool hotSwapEnabled)
|
||||
void CeDebugger::OpenFile(const StringImpl& launchPath, const StringImpl& targetPath, const StringImpl& args, const StringImpl& workingDir, const Array<uint8>& envBlock, bool hotSwapEnabled, DbgOpenFileFlags openFileFlags)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -337,6 +337,10 @@ bool CeDebugger::Attach(int processId, BfDbgAttachFlags attachFlags)
|
|||
return false;
|
||||
}
|
||||
|
||||
void CeDebugger::GetStdHandles(BfpFile** outStdIn, BfpFile** outStdOut, BfpFile** outStdErr)
|
||||
{
|
||||
}
|
||||
|
||||
void CeDebugger::Run()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -314,8 +314,9 @@ public:
|
|||
virtual void OutputRawMessage(const StringImpl& msg) override;
|
||||
virtual int GetAddrSize() override;
|
||||
virtual bool CanOpen(const StringImpl& fileName, DebuggerResult* outResult) override;
|
||||
virtual void OpenFile(const StringImpl& launchPath, const StringImpl& targetPath, const StringImpl& args, const StringImpl& workingDir, const Array<uint8>& envBlock, bool hotSwapEnabled) override;
|
||||
virtual void OpenFile(const StringImpl& launchPath, const StringImpl& targetPath, const StringImpl& args, const StringImpl& workingDir, const Array<uint8>& envBlock, bool hotSwapEnabled, DbgOpenFileFlags openFileFlags) override;
|
||||
virtual bool Attach(int processId, BfDbgAttachFlags attachFlags) override;
|
||||
virtual void GetStdHandles(BfpFile** outStdIn, BfpFile** outStdOut, BfpFile** outStdErr) override;
|
||||
virtual void Run() override;
|
||||
virtual void HotLoad(const Array<String>& objectFiles, int hotIdx) override;
|
||||
virtual void InitiateHotResolve(DbgHotResolveFlags flags) override;
|
||||
|
|
|
@ -754,7 +754,7 @@ BF_EXPORT int BF_CALLTYPE Debugger_GetAddrSize()
|
|||
return gDebugger->GetAddrSize();
|
||||
}
|
||||
|
||||
BF_EXPORT bool BF_CALLTYPE Debugger_OpenFile(const char* launchPath, const char* targetPath, const char* args, const char* workingDir, void* envBlockPtr, int envBlockSize, bool hotSwapEnabled)
|
||||
BF_EXPORT bool BF_CALLTYPE Debugger_OpenFile(const char* launchPath, const char* targetPath, const char* args, const char* workingDir, void* envBlockPtr, int envBlockSize, bool hotSwapEnabled, DbgOpenFileFlags openFileFlags)
|
||||
{
|
||||
BF_ASSERT(gDebugger == NULL);
|
||||
|
||||
|
@ -784,7 +784,7 @@ BF_EXPORT bool BF_CALLTYPE Debugger_OpenFile(const char* launchPath, const char*
|
|||
envBlock.Insert(0, (uint8*)envBlockPtr, envBlockSize);
|
||||
}
|
||||
|
||||
gDebugger->OpenFile(launchPath, targetPath, args, workingDir, envBlock, hotSwapEnabled);
|
||||
gDebugger->OpenFile(launchPath, targetPath, args, workingDir, envBlock, hotSwapEnabled, openFileFlags);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -911,6 +911,12 @@ BF_EXPORT bool BF_CALLTYPE Debugger_Attach(int processId, BfDbgAttachFlags attac
|
|||
return false;
|
||||
}
|
||||
|
||||
BF_EXPORT void Debugger_GetStdHandles(BfpFile** outStdIn, BfpFile** outStdOut, BfpFile** outStdErr)
|
||||
{
|
||||
if (gDebugger != NULL)
|
||||
gDebugger->GetStdHandles(outStdIn, outStdOut, outStdErr);
|
||||
}
|
||||
|
||||
BF_EXPORT void BF_CALLTYPE Debugger_Run()
|
||||
{
|
||||
gDebugger->Run();
|
||||
|
|
|
@ -200,6 +200,14 @@ enum DbgMemoryFlags : uint8
|
|||
DbgMemoryFlags_Execute = 4
|
||||
};
|
||||
|
||||
enum DbgOpenFileFlags : uint8
|
||||
{
|
||||
DbgOpenFileFlag_None = 0,
|
||||
DbgOpenFileFlag_RedirectStdInput = 1,
|
||||
DbgOpenFileFlag_RedirectStdOutput = 2,
|
||||
DbgOpenFileFlag_RedirectStdError = 4
|
||||
};
|
||||
|
||||
class DbgModuleMemoryCache
|
||||
{
|
||||
public:
|
||||
|
@ -266,8 +274,9 @@ public:
|
|||
virtual void OutputRawMessage(const StringImpl& msg) = 0;
|
||||
virtual int GetAddrSize() = 0;
|
||||
virtual bool CanOpen(const StringImpl& fileName, DebuggerResult* outResult) = 0;
|
||||
virtual void OpenFile(const StringImpl& launchPath, const StringImpl& targetPath, const StringImpl& args, const StringImpl& workingDir, const Array<uint8>& envBlock, bool hotSwapEnabled) = 0;
|
||||
virtual void OpenFile(const StringImpl& launchPath, const StringImpl& targetPath, const StringImpl& args, const StringImpl& workingDir, const Array<uint8>& envBlock, bool hotSwapEnabled, DbgOpenFileFlags openFileFlags) = 0;
|
||||
virtual bool Attach(int processId, BfDbgAttachFlags attachFlags) = 0;
|
||||
virtual void GetStdHandles(BfpFile** outStdIn, BfpFile** outStdOut, BfpFile** outStdErr) = 0;
|
||||
virtual void Run() = 0;
|
||||
virtual bool HasLoadedTargetBinary() { return true; }
|
||||
virtual void HotLoad(const Array<String>& objectFiles, int hotIdx) = 0;
|
||||
|
|
|
@ -79,6 +79,11 @@ static void FilterThreadName(String& name)
|
|||
}
|
||||
}
|
||||
|
||||
static bool IsHandleValid(HANDLE handle)
|
||||
{
|
||||
return (handle != NULL) && (handle != INVALID_HANDLE_VALUE);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
WdBreakpointCondition::~WdBreakpointCondition()
|
||||
|
@ -508,6 +513,9 @@ WinDebugger::WinDebugger(DebugManager* debugManager) : mDbgSymSrv(this)
|
|||
mOrigStepType = StepType_None;
|
||||
mLastValidStepIntoPC = 0;
|
||||
mActiveSymSrvRequest = NULL;
|
||||
mStdInputPipe = INVALID_HANDLE_VALUE;
|
||||
mStdOutputPipe = INVALID_HANDLE_VALUE;
|
||||
mStdErrorPipe = INVALID_HANDLE_VALUE;
|
||||
|
||||
mStoredReturnValueAddr = 0;
|
||||
#ifdef BF_DBG_32
|
||||
|
@ -528,6 +536,8 @@ WinDebugger::WinDebugger(DebugManager* debugManager) : mDbgSymSrv(this)
|
|||
mDbgProcessId = 0;
|
||||
mDbgHeapData = NULL;
|
||||
mIsPartialCallStack = true;
|
||||
mHotSwapEnabled = false;
|
||||
mOpenFileFlags = DbgOpenFileFlag_None;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
|
@ -919,7 +929,7 @@ void WinDebugger::DebugThreadProc()
|
|||
|
||||
if (!IsMiniDumpDebugger())
|
||||
{
|
||||
if (!DoOpenFile(mLaunchPath, mArgs, mWorkingDir, mEnvBlock))
|
||||
if (!DoOpenFile(mLaunchPath, mArgs, mWorkingDir, mEnvBlock, mOpenFileFlags))
|
||||
{
|
||||
if (mDbgProcessId != 0)
|
||||
OutputRawMessage("error Unable to attach to process");
|
||||
|
@ -1005,7 +1015,7 @@ bool WinDebugger::CanOpen(const StringImpl& fileName, DebuggerResult* outResult)
|
|||
return canRead;
|
||||
}
|
||||
|
||||
void WinDebugger::OpenFile(const StringImpl& launchPath, const StringImpl& targetPath, const StringImpl& args, const StringImpl& workingDir, const Array<uint8>& envBlock, bool hotSwapEnabled)
|
||||
void WinDebugger::OpenFile(const StringImpl& launchPath, const StringImpl& targetPath, const StringImpl& args, const StringImpl& workingDir, const Array<uint8>& envBlock, bool hotSwapEnabled, DbgOpenFileFlags openFileFlags)
|
||||
{
|
||||
BF_ASSERT(!mIsRunning);
|
||||
mLaunchPath = launchPath;
|
||||
|
@ -1014,6 +1024,7 @@ void WinDebugger::OpenFile(const StringImpl& launchPath, const StringImpl& targe
|
|||
mWorkingDir = workingDir;
|
||||
mEnvBlock = envBlock;
|
||||
mHotSwapEnabled = hotSwapEnabled;
|
||||
mOpenFileFlags = openFileFlags;
|
||||
mDebugTarget = new DebugTarget(this);
|
||||
}
|
||||
|
||||
|
@ -1059,6 +1070,29 @@ bool WinDebugger::Attach(int processId, BfDbgAttachFlags attachFlags)
|
|||
return true;
|
||||
}
|
||||
|
||||
void WinDebugger::GetStdHandles(BfpFile** outStdIn, BfpFile** outStdOut, BfpFile** outStdErr)
|
||||
{
|
||||
AutoCrit autoCrit(mDebugManager->mCritSect);
|
||||
|
||||
if ((outStdIn != NULL) && (IsHandleValid(mStdInputPipe)))
|
||||
{
|
||||
*outStdIn = BfpFile_GetFromHandle((intptr)mStdInputPipe, NULL);
|
||||
mStdInputPipe = 0;
|
||||
}
|
||||
|
||||
if ((outStdOut != NULL) && (IsHandleValid(mStdOutputPipe)))
|
||||
{
|
||||
*outStdOut = BfpFile_GetFromHandle((intptr)mStdOutputPipe, NULL);
|
||||
mStdOutputPipe = 0;
|
||||
}
|
||||
|
||||
if ((outStdErr != NULL) && (IsHandleValid(mStdErrorPipe)))
|
||||
{
|
||||
*outStdErr = BfpFile_GetFromHandle((intptr)mStdErrorPipe, NULL);
|
||||
mStdErrorPipe = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void WinDebugger::Run()
|
||||
{
|
||||
mIsRunning = true;
|
||||
|
@ -1262,7 +1296,50 @@ String WinDebugger::GetDbgAllocInfo()
|
|||
return result;
|
||||
}
|
||||
|
||||
bool WinDebugger::DoOpenFile(const StringImpl& fileName, const StringImpl& args, const StringImpl& workingDir, const Array<uint8>& envBlock)
|
||||
static bool CreatePipeWithSecurityAttributes(HANDLE& hReadPipe, HANDLE& hWritePipe, SECURITY_ATTRIBUTES* lpPipeAttributes, int32 nSize)
|
||||
{
|
||||
hReadPipe = 0;
|
||||
hWritePipe = 0;
|
||||
bool ret = ::CreatePipe(&hReadPipe, &hWritePipe, lpPipeAttributes, nSize);
|
||||
if (!ret || (hReadPipe == INVALID_HANDLE_VALUE) || (hWritePipe == INVALID_HANDLE_VALUE))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CreatePipe(HANDLE& parentHandle, HANDLE& childHandle, bool parentInputs)
|
||||
{
|
||||
SECURITY_ATTRIBUTES securityAttributesParent = { 0 };
|
||||
securityAttributesParent.bInheritHandle = 1;
|
||||
|
||||
HANDLE hTmp = INVALID_HANDLE_VALUE;
|
||||
if (parentInputs)
|
||||
CreatePipeWithSecurityAttributes(childHandle, hTmp, &securityAttributesParent, 0);
|
||||
else
|
||||
CreatePipeWithSecurityAttributes(hTmp, childHandle, &securityAttributesParent, 0);
|
||||
|
||||
HANDLE dupHandle = 0;
|
||||
|
||||
// Duplicate the parent handle to be non-inheritable so that the child process
|
||||
// doesn't have access. This is done for correctness sake, exact reason is unclear.
|
||||
// One potential theory is that child process can do something brain dead like
|
||||
// closing the parent end of the pipe and there by getting into a blocking situation
|
||||
// as parent will not be draining the pipe at the other end anymore.
|
||||
if (!::DuplicateHandle(GetCurrentProcess(), hTmp,
|
||||
GetCurrentProcess(), &dupHandle,
|
||||
0, false, DUPLICATE_SAME_ACCESS))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
parentHandle = dupHandle;
|
||||
|
||||
if (hTmp != INVALID_HANDLE_VALUE)
|
||||
::CloseHandle(hTmp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WinDebugger::DoOpenFile(const StringImpl& fileName, const StringImpl& args, const StringImpl& workingDir, const Array<uint8>& envBlock, DbgOpenFileFlags openFileFlags)
|
||||
{
|
||||
BP_ZONE("WinDebugger::DoOpenFile");
|
||||
|
||||
|
@ -1274,6 +1351,33 @@ bool WinDebugger::DoOpenFile(const StringImpl& fileName, const StringImpl& args,
|
|||
si.cb = sizeof(si);
|
||||
ZeroMemory(&mProcessInfo, sizeof(mProcessInfo));
|
||||
|
||||
DWORD flags = DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS | CREATE_DEFAULT_ERROR_MODE;
|
||||
BOOL inheritHandles = false;
|
||||
|
||||
// set up the streams
|
||||
if ((openFileFlags & (DbgOpenFileFlag_RedirectStdInput | DbgOpenFileFlag_RedirectStdOutput | DbgOpenFileFlag_RedirectStdError)) != 0)
|
||||
{
|
||||
if ((openFileFlags & DbgOpenFileFlag_RedirectStdInput) != 0)
|
||||
CreatePipe(mStdInputPipe, si.hStdInput, true);
|
||||
else if (::GetConsoleWindow() != NULL)
|
||||
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
||||
else
|
||||
si.hStdInput = INVALID_HANDLE_VALUE;
|
||||
|
||||
if ((openFileFlags & DbgOpenFileFlag_RedirectStdOutput) != 0)
|
||||
CreatePipe(mStdOutputPipe, si.hStdOutput, false);
|
||||
else
|
||||
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
if ((openFileFlags & DbgOpenFileFlag_RedirectStdError) != 0)
|
||||
CreatePipe(mStdErrorPipe, si.hStdError, false);
|
||||
else
|
||||
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
||||
flags |= CREATE_NO_WINDOW;
|
||||
si.dwFlags = STARTF_USESTDHANDLES;
|
||||
inheritHandles = true;
|
||||
}
|
||||
|
||||
if (mDbgProcessId != 0)
|
||||
{
|
||||
BOOL success = ::DebugActiveProcess(mDbgProcessId);
|
||||
|
@ -1288,7 +1392,6 @@ bool WinDebugger::DoOpenFile(const StringImpl& fileName, const StringImpl& args,
|
|||
|
||||
UTF16String envW;
|
||||
|
||||
DWORD flags = DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS | CREATE_DEFAULT_ERROR_MODE;
|
||||
void* envPtr = NULL;
|
||||
if (!envBlock.IsEmpty())
|
||||
{
|
||||
|
@ -1318,11 +1421,34 @@ bool WinDebugger::DoOpenFile(const StringImpl& fileName, const StringImpl& args,
|
|||
cmdLine += args;
|
||||
}
|
||||
|
||||
BOOL worked = CreateProcessW(NULL, (WCHAR*)UTF8Decode(cmdLine).c_str(), NULL, NULL, FALSE,
|
||||
BOOL worked = CreateProcessW(NULL, (WCHAR*)UTF8Decode(cmdLine).c_str(), NULL, NULL, inheritHandles,
|
||||
flags, envPtr, (WCHAR*)UTF8Decode(workingDir).c_str(), &si, &mProcessInfo);
|
||||
|
||||
if ((openFileFlags & DbgOpenFileFlag_RedirectStdInput) != 0)
|
||||
::CloseHandle(si.hStdInput);
|
||||
if ((openFileFlags & DbgOpenFileFlag_RedirectStdOutput) != 0)
|
||||
::CloseHandle(si.hStdOutput);
|
||||
if ((openFileFlags & DbgOpenFileFlag_RedirectStdError) != 0)
|
||||
::CloseHandle(si.hStdError);
|
||||
|
||||
if (!worked)
|
||||
{
|
||||
if (IsHandleValid(mStdInputPipe))
|
||||
{
|
||||
::CloseHandle(mStdInputPipe);
|
||||
mStdInputPipe = 0;
|
||||
}
|
||||
if (IsHandleValid(mStdOutputPipe))
|
||||
{
|
||||
::CloseHandle(mStdOutputPipe);
|
||||
mStdOutputPipe = 0;
|
||||
}
|
||||
if (IsHandleValid(mStdErrorPipe))
|
||||
{
|
||||
::CloseHandle(mStdErrorPipe);
|
||||
mStdErrorPipe = 0;
|
||||
}
|
||||
|
||||
auto lastError = ::GetLastError();
|
||||
if (lastError == ERROR_DIRECTORY)
|
||||
{
|
||||
|
@ -1503,6 +1629,18 @@ void WinDebugger::Detach()
|
|||
mBreakpointAddrMap.Clear();
|
||||
|
||||
gDebugUpdateCnt = 0;
|
||||
|
||||
if (IsHandleValid(mStdInputPipe))
|
||||
::CloseHandle(mStdInputPipe);
|
||||
mStdInputPipe = INVALID_HANDLE_VALUE;
|
||||
|
||||
if (IsHandleValid(mStdOutputPipe))
|
||||
::CloseHandle(mStdOutputPipe);
|
||||
mStdOutputPipe = INVALID_HANDLE_VALUE;
|
||||
|
||||
if (IsHandleValid(mStdErrorPipe))
|
||||
::CloseHandle(mStdErrorPipe);
|
||||
mStdErrorPipe = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
Profiler* WinDebugger::StartProfiling()
|
||||
|
@ -2927,12 +3065,32 @@ static BOOL CALLBACK WdEnumWindowsProc(HWND hwnd, LPARAM lParam)
|
|||
if (processId != ((WinDebugger*)gDebugger)->mProcessInfo.dwProcessId)
|
||||
return TRUE;
|
||||
|
||||
while (true)
|
||||
{
|
||||
HWND parentHWnd = GetParent(hwnd);
|
||||
if (parentHWnd != NULL)
|
||||
{
|
||||
hwnd = parentHWnd;
|
||||
continue;
|
||||
}
|
||||
SetForegroundWindow(hwnd);
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void WinDebugger::ForegroundTarget()
|
||||
{
|
||||
HWND hwnd = ::GetForegroundWindow();
|
||||
if (hwnd != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DWORD processId = 0;
|
||||
GetWindowThreadProcessId(hwnd, &processId);
|
||||
if (processId == ((WinDebugger*)gDebugger)->mProcessInfo.dwProcessId)
|
||||
return; // Already good
|
||||
}
|
||||
|
||||
EnumWindows(WdEnumWindowsProc, 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -389,6 +389,7 @@ public:
|
|||
String mArgs;
|
||||
String mWorkingDir;
|
||||
bool mHotSwapEnabled;
|
||||
DbgOpenFileFlags mOpenFileFlags;
|
||||
Array<uint8> mEnvBlock;
|
||||
DebugTarget* mEmptyDebugTarget;
|
||||
DebugTarget* mDebugTarget;
|
||||
|
@ -400,6 +401,11 @@ public:
|
|||
DWORD mDbgProcessId;
|
||||
HANDLE mDbgProcessHandle;
|
||||
HANDLE mDbgThreadHandle;
|
||||
|
||||
HANDLE mStdInputPipe;
|
||||
HANDLE mStdOutputPipe;
|
||||
HANDLE mStdErrorPipe;
|
||||
|
||||
bool mIsDebuggerWaiting;
|
||||
bool mWantsDebugContinue;
|
||||
bool mNeedsRehupBreakpoints;
|
||||
|
@ -553,7 +559,7 @@ public:
|
|||
void ModuleChanged(DbgModule* dbgModule);
|
||||
bool DoUpdate();
|
||||
void DebugThreadProc();
|
||||
bool DoOpenFile(const StringImpl& fileName, const StringImpl& args, const StringImpl& workingDir, const Array<uint8>& envBlock);
|
||||
bool DoOpenFile(const StringImpl& fileName, const StringImpl& args, const StringImpl& workingDir, const Array<uint8>& envBlock, DbgOpenFileFlags openFileFlags);
|
||||
|
||||
DbgTypedValue GetRegister(const StringImpl& regName, DbgLanguage language, CPURegisters* registers, Array<RegForm>* regForms = NULL);
|
||||
void FixupLineData(DbgCompileUnit* compileUnit);
|
||||
|
@ -581,8 +587,9 @@ public:
|
|||
virtual void OutputRawMessage(const StringImpl& msg) override;
|
||||
virtual int GetAddrSize() override;
|
||||
virtual bool CanOpen(const StringImpl& fileName, DebuggerResult* outResult) override;
|
||||
virtual void OpenFile(const StringImpl& launchPath, const StringImpl& targetPath, const StringImpl& args, const StringImpl& workingDir, const Array<uint8>& envBlock, bool hotSwapEnabled) override;
|
||||
virtual void OpenFile(const StringImpl& launchPath, const StringImpl& targetPath, const StringImpl& args, const StringImpl& workingDir, const Array<uint8>& envBlock, bool hotSwapEnabled, DbgOpenFileFlags openFileFlags) override;
|
||||
virtual bool Attach(int processId, BfDbgAttachFlags attachFlags) override;
|
||||
virtual void GetStdHandles(BfpFile** outStdIn, BfpFile** outStdOut, BfpFile** outStdErr) override;
|
||||
virtual void Run() override;
|
||||
virtual bool HasLoadedTargetBinary() override;
|
||||
virtual void HotLoad(const Array<String>& objectFiles, int hotIdx) override;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue