Menu

ATUProcess_demo

Krzysztof Kamil Jacewicz

Table of content


  • Synopsis
  • Code walkthrough

Synopsis

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:
mainform.png

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:
mainform2.png

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:
mainform3.png

And after a process ends, you can start another:
mainform4.png

Code walkthrough

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.


Related

Wiki: TATUProcessManager.Run
Wiki: TATUProcessManager
Wiki: atuProcess

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.