ADD: Start to implement console under Windows

This commit is contained in:
Alexander Koblov 2009-06-27 20:43:37 +00:00
commit de5ab4781e
3 changed files with 361 additions and 0 deletions

106
src/platform/uterminal.pas Normal file
View file

@ -0,0 +1,106 @@
{
Double Commander
-------------------------------------------------------------------------
Terminal emulator abstract class
Copyright (C) 2009 Koblov Alexander (Alexx2000@mail.ru)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
}
unit uTerminal;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, uCmdBox;
type
Cint = Integer;
{ TTerminal }
TTerminal = class
protected
FChildPid: THandle;
Fpty: LongInt;
public
{en
Read info from pty
}
function Read_pty(var Output: UTF8String; const TimeOut: LongInt = 10): LongInt; virtual; abstract;
{en
Create new pty and start cmd
}
function Fork_pty(const RowCount, ColCount: Integer; const Command: UTF8String; const Params: UTF8String = ''): THandle; virtual; abstract;
{en
Write string to pty
}
function Write_pty(const Input: UTF8String): Boolean; virtual; abstract;
//---------------------
function SendBreak_pty(): Boolean; virtual; abstract; // ^C
function SendSignal_pty(Sig: Cint): Boolean; virtual; abstract;
function SetScreenSize(ColCount, RowCount: Integer): Boolean; virtual; abstract;
//---------------------
function KillShell: LongInt; virtual; abstract;
//---------------------}
property ShellPid: THandle read FChildPid;
property PtyPid: LongInt read Fpty;
end;
{ TConsoleThread }
TConsoleThread = class(TThread)
protected
FLock: System.TRTLCriticalSection;
FTerm: TTerminal;
FRowsCount,
FColsCount: Integer;
FOut: TCmdBox;
FShell: String;
public
property Terminal: TTerminal read FTerm;
property RowsCount: integer read FRowsCount write FRowsCount;
property ColsCount: integer read FColsCount write FColsCount;
property CmdBox: TCmdBox read FOut write FOut;
property Shell: String read FShell write FShell;
end;
function CreateConsoleThread: TConsoleThread;
implementation
uses
{$IF DEFINED(WINDOWS)}
uWinTerm;
{$ELSEIF DEFINED(UNIX)}
uTerm;
{$ENDIF}
function CreateConsoleThread: TConsoleThread;
{$IF DEFINED(WINDOWS)}
begin
Result:= TWinConThread.Create;
end;
{$ELSEIF DEFINED(UNIX)}
begin
Result:= TConThread.Create;
end;
{$ENDIF}
end.

View file

@ -0,0 +1,255 @@
unit uWinTerm;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Windows, uTerminal;
type
{ TWinTerm }
TWinTerm = class(TTerminal)
private
FSecurityAttributes: TSecurityAttributes;
FStartupInfo: TStartupInfo;
FProcessInformation: TProcessInformation;
PipeStdInRead,
PipeStdInWrite,
PipeStdOutRead,
PipeStdOutWrite: THandle;
public
constructor Create;
destructor Destroy; override;
//---------------------
function Read_Pty(var Output: UTF8String; const TimeOut: LongInt = 10): LongInt; override; // Read info from pty
function Fork_pty(const RowCount, ColCount: Integer; const Command: UTF8String; const Params: UTF8String=''): THandle; override;//Create new pty and start cmd
function Write_pty(const Input: UTF8String): Boolean; override; //write str to pty
//---------------------
function SendBreak_pty(): Boolean; override; // ^C
function SendSignal_pty(Sig: Cint): Boolean; override;
function SetScreenSize(ColCount, RowCount: Integer): Boolean; override;
//---------------------
function KillShell: LongInt;
end;
{ TWinConThread }
TWinConThread = class(TConsoleThread)
private
FBuffer: UTF8String;
procedure AddSymbol;
protected
procedure Execute; override;
public
constructor Create;
destructor Destroy; override;
end;
implementation
uses
FileUtil, uOSUtils;
function ConsoleToUTF8(const Str: AnsiString): UTF8String;
{$ifdef MSWindows}
var
Dst: PChar;
{$endif}
begin
Result:= Str;
{$ifdef MSWindows}
Dst:= AllocMem((Length(Result) + 1) * SizeOf(Char));
if OEMToChar(PChar(Result), Dst) then
Result:= SysToUTF8(Dst);
FreeMem(Dst);
{$endif}
end;
{ TWinTerm }
constructor TWinTerm.Create;
begin
end;
destructor TWinTerm.Destroy;
begin
KillShell;
inherited Destroy;
end;
function TWinTerm.Read_Pty(var Output: UTF8String; const timeout: LongInt): LongInt;
var
I: Integer;
dwRead, BufSize, DesBufSize: DWORD;
Res: Boolean;
pcOutput: PChar;
begin
try
BufSize:= 0;
dwRead:= 0;
Output:= EmptyStr;
repeat
for I:= 0 to 9 do
begin
Res:= PeekNamedPipe(PipeStdOutRead, nil, 0, nil, @DesBufSize, nil);
Res:= Res and (DesBufSize > 0);
if Res then Break;
Sleep(TimeOut);
end;
if Res then
begin
if DesBufSize > BufSize then
begin
GetMem(pcOutput, DesBufSize);
BufSize:= DesBufSize;
end;
Res:= ReadFile(PipeStdOutRead, pcOutput^, BufSize, dwRead, nil);
end;
until not Res;
except
end;
if dwRead > 0 then
begin
Output:= ConsoleToUTF8(pcOutput);
FreeMem(pcOutput);
end;
Result:= dwRead;
end;
function TWinTerm.Fork_pty(const rows, cols: Integer; const cmd: UTF8String;
const params: UTF8string): THandle;
var
hTmp1, hTmp2: THandle;
begin
ZeroMemory(@FSecurityAttributes, SizeOf(FSecurityAttributes));
FSecurityAttributes.nLength:= SizeOf(FSecurityAttributes);
FSecurityAttributes.bInheritHandle:= True;
FSecurityAttributes.lpSecurityDescriptor:= nil;
// create input/output pipes
CreatePipe(PipeStdInRead, PipeStdInWrite, @FSecurityAttributes, 0);
CreatePipe(PipeStdOutRead, PipeStdOutWrite, @FSecurityAttributes, 0);
DuplicateHandle(GetCurrentProcess(), PipeStdInWrite, GetCurrentProcess(), @hTmp1, 0, False, DUPLICATE_SAME_ACCESS);
DuplicateHandle(GetCurrentProcess(), PipeStdOutRead, GetCurrentProcess(), @hTmp2, 0, False, DUPLICATE_SAME_ACCESS);
CloseHandle(PipeStdInWrite);
CloseHandle(PipeStdOutRead);
PipeStdInWrite:= hTmp1;
PipeStdOutRead:= hTmp2;
ZeroMemory(@FStartupInfo, SizeOf(FStartupInfo));
FStartupInfo.cb:= SizeOf(FStartupInfo);
with FStartupInfo do
begin
dwFlags:= STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
wShowWindow:= SW_HIDE;
hStdInput:= PipeStdInRead;
hStdOutput:= PipeStdOutWrite;
end;
ZeroMemory(@FProcessInformation, SizeOf(FProcessInformation));
CreateProcessW(nil,
PWideChar(UTF8Decode(cmd)), // command line
nil, // process security attributes
nil, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
nil, // use parent's environment
nil, // use parent's current directory
FStartupInfo, // STARTUPINFO pointer
FProcessInformation); // receives PROCESS_INFORMATION
Result:= FProcessInformation.hProcess;
end;
function TWinTerm.Write_pty(const Input: UTF8String): Boolean;
var
dwWritten, BufSize: DWORD;
pcCommand: PChar;
begin
pcCommand:= PChar(UTF8ToConsole(Input));
BufSize:= Length(pcCommand);
Result:= WriteFile(PipeStdInWrite, pcCommand^, BufSize, dwWritten, nil);
Result:= Result and (BufSize = dwWritten);
end;
function TWinTerm.SendBreak_pty(): Boolean;
begin
Result:= False;
end;
function TWinTerm.SendSignal_pty(Sig: Cint): Boolean;
begin
Result:= False;
end;
function TWinTerm.SetScreenSize(aCols, aRows: Integer): Boolean;
begin
Result:= False;
end;
function TWinTerm.KillShell: LongInt;
begin
try
CloseHandle(PipeStdInRead);
CloseHandle(PipeStdInWrite);
CloseHandle(PipeStdOutRead);
CloseHandle(PipeStdOutWrite);
CloseHandle(FProcessInformation.hThread);
CloseHandle(FProcessInformation.hProcess);
except
end;
end;
{ TWinConThread }
procedure TWinConThread.AddSymbol;
begin
if Assigned(FOut) then
FOut.Write(FBuffer);
end;
procedure TWinConThread.Execute;
begin
FShell:= GetShell;
if Length(FShell) = 0 then
FShell:= RunTerm;
if Assigned(FTerm) then
FTerm.Fork_pty(FRowsCount, FColsCount, FShell);
while True do
begin
if Assigned(FTerm) then
begin
if FTerm.Read_pty(FBuffer, 0) > 0 then
Synchronize(@AddSymbol)
else
Sleep(1);
end else Break;
end;
end;
constructor TWinConThread.Create;
begin
inherited Create(True);
System.InitCriticalSection(FLock);
FTerm:= TWinTerm.Create;
FRowsCount:= 50;
FColsCount:= 100;
end;
destructor TWinConThread.Destroy;
begin
FreeAndNil(FTerm);
System.DoneCriticalSection(FLock);
inherited Destroy;
end;
end.