The ATUProcess_demo@SourceForge demonstrates the use case scenario for [atuProcess] unit from [ATU package] for [Lazarus IDE].
To checkout just the demo from SVN repo, from the command line run:
svn checkout https://svn.code.sf.net/p/all-things-ubuntu-library/code/trunk/examples/ATUprocess-demo ATUprocess-demo
The demo makes use of [TATUProcessManager] component, but it creates it at run-time programatically, so the component is not included in the project's main form LFM file. This means that the project does not require [ATU package] to be installed on your [Lazarus IDE], as long as the source files are on the search path.
On Ubuntu 18.04 (amg64) desktop, the demo looks like this:
After pressing the run button, the command will be executed, the top bar will dissapear, and a bottom bar will appear through which user can send input into the running process, in this case a sudo password:
When sudo prompts for password the input field automatically switches to password mode in which user cannot see characters being typed. But after input is sent, it resets this masking setting. When the process execution ends, the total output of the program is being shown in a modal dialog:
And after a process ends, you can start another:
The most important source code is in the project's main form unit (here).
The core of the demo is an instance of the [TATUProcessManager] class and an instance of [TStringFIFO] class:
private FAPA : TATUProcessManager; FFIFO : TStringFIFO;
These instances are both created in the main form's OnCreate event:
{ TForm1 } procedure TForm1.FormCreate(Sender: TObject); begin FFIFO := TStringFIFO.Create; FAPA := TATUProcessManager.Create(self); FAPA.OnProcessOutput := @self.OnOutput; FAPA.OnSudoPrompt := @self.OnSudoPrompt; FAPA.OnProcessExit := @self.OnExit; end;
The way a process is started, is via Button1Click event of a button on the main form, which executes command line specified by the user inside Edit1 control:
Procedure TForm1.Button1Click(Sender: TObject); begin Panel2.Hint := 'demo'; FAPA.Run(Panel2.Hint,Edit1.Text); Panel2.Visible := True; Panel1.Enabled := False; end;
Panel2 is the lower Panel which allows user to send input to the program via Edit2 control. The actual program execution happens via Run method of the [TATUProcessManager] class.
After that, the program will response to the program via one of its 3 events:
OnSudoPrompt will be triggered every time the ouptut of the program contains a sudo prompt for entering a password. This event is not thread-safe, but it also doesn't update any visual components, like the next event will. In this demo the only thing that happens is turning on the Edit2 password masking option, so when the user starts typing his password, the characters will not be seen:
Procedure TForm1.OnSudoPrompt(Sender: TObject; const Sudoer: string; var Password: string); begin Edit2.PasswordChar := '*'; end;
This masking mode will be turned off automatically after eeach time user actually sends input to the program (which is ok, because if the password is wrong, the OnSudoPrompt will be called again, and the maskign will be restored):
procedure TForm1.Button2Click(Sender: TObject); begin FAPA.SendInputLn(Panel2.Hint,Edit2.Text); Edit2.Text := ''; Edit2.PasswordChar := #0; end;
OnOutput will be triggered every time there is any output available from the program, either on the StdOut or StdErr pipe:
Procedure TForm1.OnOutput(Sender: TObject; ProcIO: TATUProcessIO); begin if(ProcIO=nil)then exit; if(ProcIO.StdOut<>'') then FFIFO.Put(ProcIO.StdOut); if(ProcIO.StdErr<>'') then FFIFO.Put(ProcIO.StdErr); end;
Notice that any available ouput is being sent into the instance of [TStringFIFO] inside this event. This is VERY important, because th OnOutput event of [TATUProcessManager] is asynchronous, and if you want to update any visual control from within an asynchronous event, it can lead to fatal runtime errors. The FIFO used in the demo is thread-safe, in the way that you can safely put/pop strings in/from it without worrying about thread symchronization. So in order to actually print the output in the main form's TMemo control, we don't update TMemo directly inside the OnOutput event. Insted, there is a TTimer on the main form, which keeps checking if there is anything available in the FIFO, and pops any strings to be printed:
procedure TForm1.Timer1Timer(Sender: TObject); var s: string; begin while FFIFO.Pop(s)do Memo1.Lines.Add(s); s := '' end;
The [TStringFIFO] and other thread-safe data exchange mechanisms are provided in the [atuExchange] unit.
OnExit event will be called at the end of the program execution, and unlike OnOutput and OnSudoPrompt, this event is thread-safe:
Procedure TForm1.OnExit(Sender: TObject; ProcIO: TATUProcessIO); begin Panel2.Visible := False; Panel1.Enabled := True; ShowMessage(ProcIO.StdOut); end;
It is here that the original Panel1 for starting programs will re-appear, and a modal dialog will be shown with the full ouput of the program.
Wiki: TATUProcessManager.Run
Wiki: TATUProcessManager
Wiki: atuProcess