| 1 | unit UKernel;
 | 
|---|
| 2 | 
 | 
|---|
| 3 | {$mode delphi}
 | 
|---|
| 4 | 
 | 
|---|
| 5 | interface
 | 
|---|
| 6 | 
 | 
|---|
| 7 | uses
 | 
|---|
| 8 |   Classes, SysUtils, Forms, Generics.Collections, UApp, UApi, UTextBuffer;
 | 
|---|
| 9 | 
 | 
|---|
| 10 | type
 | 
|---|
| 11 |   TKernel = class;
 | 
|---|
| 12 |   TAppContext = class;
 | 
|---|
| 13 | 
 | 
|---|
| 14 |   { TKernelApi }
 | 
|---|
| 15 | 
 | 
|---|
| 16 |   TKernelApi = class(TApi)
 | 
|---|
| 17 |     Kernel: TKernel;
 | 
|---|
| 18 |     AppContext: TAppContext;
 | 
|---|
| 19 |     procedure Sleep(Time: TDateTime); override;
 | 
|---|
| 20 |     function GetMessage: TApiMessage; override;
 | 
|---|
| 21 |     function InputText: string; override;
 | 
|---|
| 22 |     procedure OutputText(Text: string); override;
 | 
|---|
| 23 |     procedure Terminate; override;
 | 
|---|
| 24 |     procedure ExecuteApp(Name: string); override;
 | 
|---|
| 25 |   end;
 | 
|---|
| 26 | 
 | 
|---|
| 27 |   { TAppThread }
 | 
|---|
| 28 | 
 | 
|---|
| 29 |   TAppThread = class(TThread)
 | 
|---|
| 30 |     AppContext: TAppContext;
 | 
|---|
| 31 |     procedure Execute; override;
 | 
|---|
| 32 |   end;
 | 
|---|
| 33 | 
 | 
|---|
| 34 |   TAppState = (asSleep, asRunning, asTerminated);
 | 
|---|
| 35 | 
 | 
|---|
| 36 |   { TAppContext }
 | 
|---|
| 37 | 
 | 
|---|
| 38 |   TAppContext = class
 | 
|---|
| 39 |     AppState: TAppState;
 | 
|---|
| 40 |     App: TApp;
 | 
|---|
| 41 |     AppDescription: TAppDescription;
 | 
|---|
| 42 |     InputBuffer: TTextBuffer;
 | 
|---|
| 43 |     OutputBuffer: TTextBuffer;
 | 
|---|
| 44 |     Messages: TList<TApiMessage>;
 | 
|---|
| 45 |     Thread: TAppThread;
 | 
|---|
| 46 |     procedure Start;
 | 
|---|
| 47 |     procedure Stop;
 | 
|---|
| 48 |     constructor Create;
 | 
|---|
| 49 |     destructor Destroy; override;
 | 
|---|
| 50 |   end;
 | 
|---|
| 51 | 
 | 
|---|
| 52 |   TOutputTextEvent = procedure (Text: string) of object;
 | 
|---|
| 53 | 
 | 
|---|
| 54 |   { TKernel }
 | 
|---|
| 55 | 
 | 
|---|
| 56 |   TKernel = class
 | 
|---|
| 57 |   private
 | 
|---|
| 58 |     FOnOutputText: TOutputTextEvent;
 | 
|---|
| 59 |     FAppContexts: TObjectList<TAppContext>;
 | 
|---|
| 60 |     FCurrentApp: TAppContext;
 | 
|---|
| 61 |     FAppDescriptions: TAppDescriptions;
 | 
|---|
| 62 |     procedure Schedule;
 | 
|---|
| 63 |     function AddAppContext(AppDescription: TAppDescription): TAppContext;
 | 
|---|
| 64 |     procedure DoOutputText(Text: string);
 | 
|---|
| 65 |     procedure RemoveTerminated;
 | 
|---|
| 66 |   public
 | 
|---|
| 67 |     Terminated: Boolean;
 | 
|---|
| 68 |     function AddApp(Name: string; AppClass: TAppClass): TAppDescription;
 | 
|---|
| 69 |     procedure Start;
 | 
|---|
| 70 |     procedure Input(Text: string);
 | 
|---|
| 71 |     procedure InputKey(Key: Word);
 | 
|---|
| 72 |     constructor Create;
 | 
|---|
| 73 |     destructor Destroy; override;
 | 
|---|
| 74 |     property OnOutputText: TOutputTextEvent read FOnOutputText write FOnOutputText;
 | 
|---|
| 75 |   end;
 | 
|---|
| 76 | 
 | 
|---|
| 77 | 
 | 
|---|
| 78 | implementation
 | 
|---|
| 79 | 
 | 
|---|
| 80 | { TAppThread }
 | 
|---|
| 81 | 
 | 
|---|
| 82 | procedure TAppThread.Execute;
 | 
|---|
| 83 | begin
 | 
|---|
| 84 |   while not Terminated do begin
 | 
|---|
| 85 |     AppContext.App.Run;
 | 
|---|
| 86 |   end;
 | 
|---|
| 87 |   AppContext.AppState := asTerminated;
 | 
|---|
| 88 | end;
 | 
|---|
| 89 | 
 | 
|---|
| 90 | { TAppContext }
 | 
|---|
| 91 | 
 | 
|---|
| 92 | procedure TAppContext.Start;
 | 
|---|
| 93 | begin
 | 
|---|
| 94 |   Thread.Resume;
 | 
|---|
| 95 | end;
 | 
|---|
| 96 | 
 | 
|---|
| 97 | procedure TAppContext.Stop;
 | 
|---|
| 98 | begin
 | 
|---|
| 99 |   Thread.Terminate;
 | 
|---|
| 100 | end;
 | 
|---|
| 101 | 
 | 
|---|
| 102 | constructor TAppContext.Create;
 | 
|---|
| 103 | begin
 | 
|---|
| 104 |   InputBuffer := TTextBuffer.Create;
 | 
|---|
| 105 |   OutputBuffer := TTextBuffer.Create;
 | 
|---|
| 106 |   Messages := TList<TApiMessage>.Create;
 | 
|---|
| 107 |   Thread := TAppThread.Create(True);
 | 
|---|
| 108 |   Thread.AppContext := Self;
 | 
|---|
| 109 | end;
 | 
|---|
| 110 | 
 | 
|---|
| 111 | destructor TAppContext.Destroy;
 | 
|---|
| 112 | begin
 | 
|---|
| 113 |   FreeAndNil(Thread);
 | 
|---|
| 114 |   FreeAndNil(Messages);
 | 
|---|
| 115 |   FreeAndNil(InputBuffer);
 | 
|---|
| 116 |   FreeAndNil(OutputBuffer);
 | 
|---|
| 117 |   inherited;
 | 
|---|
| 118 | end;
 | 
|---|
| 119 | 
 | 
|---|
| 120 | { TKernelApi }
 | 
|---|
| 121 | 
 | 
|---|
| 122 | procedure TKernelApi.Sleep(Time: TDateTime);
 | 
|---|
| 123 | begin
 | 
|---|
| 124 | 
 | 
|---|
| 125 | end;
 | 
|---|
| 126 | 
 | 
|---|
| 127 | function TKernelApi.GetMessage: TApiMessage;
 | 
|---|
| 128 | begin
 | 
|---|
| 129 |   while AppContext.AppState <> asTerminated do begin
 | 
|---|
| 130 |     if AppContext.AppState = asTerminated then begin
 | 
|---|
| 131 |       Result := amTerminate;
 | 
|---|
| 132 |       Break;
 | 
|---|
| 133 |     end else begin
 | 
|---|
| 134 |       if AppContext.InputBuffer.Size > 0 then begin
 | 
|---|
| 135 |         Result := amInputText;
 | 
|---|
| 136 |         Break;
 | 
|---|
| 137 |       end;
 | 
|---|
| 138 |     end;
 | 
|---|
| 139 |     Sleep(1);
 | 
|---|
| 140 |   end;
 | 
|---|
| 141 | end;
 | 
|---|
| 142 | 
 | 
|---|
| 143 | function TKernelApi.InputText: string;
 | 
|---|
| 144 | begin
 | 
|---|
| 145 |   Result := AppContext.InputBuffer.Read;
 | 
|---|
| 146 | end;
 | 
|---|
| 147 | 
 | 
|---|
| 148 | procedure TKernelApi.OutputText(Text: string);
 | 
|---|
| 149 | begin
 | 
|---|
| 150 |   AppContext.OutputBuffer.Write(Text);
 | 
|---|
| 151 | end;
 | 
|---|
| 152 | 
 | 
|---|
| 153 | procedure TKernelApi.Terminate;
 | 
|---|
| 154 | begin
 | 
|---|
| 155 |   AppContext.AppState := asTerminated;
 | 
|---|
| 156 |   AppContext.Thread.Terminate;
 | 
|---|
| 157 | end;
 | 
|---|
| 158 | 
 | 
|---|
| 159 | procedure TKernelApi.ExecuteApp(Name: string);
 | 
|---|
| 160 | var
 | 
|---|
| 161 |   AppDescription: TAppDescription;
 | 
|---|
| 162 |   AppContext: TAppContext;
 | 
|---|
| 163 | begin
 | 
|---|
| 164 |   AppDescription := Kernel.FAppDescriptions.SearchByName(Name);
 | 
|---|
| 165 |   if Assigned(AppDescription) then begin
 | 
|---|
| 166 |     AppContext := Kernel.AddAppContext(AppDescription);
 | 
|---|
| 167 |     AppContext.Start;
 | 
|---|
| 168 |     Kernel.FCurrentApp := Kernel.FAppContexts[Kernel.FAppContexts.Count - 1];
 | 
|---|
| 169 |   end;
 | 
|---|
| 170 | end;
 | 
|---|
| 171 | 
 | 
|---|
| 172 | { TKernel }
 | 
|---|
| 173 | 
 | 
|---|
| 174 | procedure TKernel.Schedule;
 | 
|---|
| 175 | var
 | 
|---|
| 176 |   I: Integer;
 | 
|---|
| 177 | begin
 | 
|---|
| 178 |   I := FAppContexts.IndexOf(FCurrentApp);
 | 
|---|
| 179 |   if FAppContexts.Count > 0 then begin
 | 
|---|
| 180 |     I := (I + 1) mod FAppContexts.Count;
 | 
|---|
| 181 |     FCurrentApp := FAppContexts[I];
 | 
|---|
| 182 |   end else FCurrentApp := nil;
 | 
|---|
| 183 | end;
 | 
|---|
| 184 | 
 | 
|---|
| 185 | function TKernel.AddAppContext(AppDescription: TAppDescription): TAppContext;
 | 
|---|
| 186 | var
 | 
|---|
| 187 |   App: TApp;
 | 
|---|
| 188 | begin
 | 
|---|
| 189 |   App := AppDescription.AppClass.Create;
 | 
|---|
| 190 |   Result := TAppContext.Create;
 | 
|---|
| 191 |   Result.App := App;
 | 
|---|
| 192 |   Result.AppDescription := AppDescription;
 | 
|---|
| 193 |   App.Api.Parent := TKernelApi.Create;
 | 
|---|
| 194 |   TKernelApi(App.Api.Parent).AppContext := Result;
 | 
|---|
| 195 |   TKernelApi(App.Api.Parent).Kernel := Self;
 | 
|---|
| 196 |   FAppContexts.Add(Result);
 | 
|---|
| 197 | end;
 | 
|---|
| 198 | 
 | 
|---|
| 199 | procedure TKernel.DoOutputText(Text: string);
 | 
|---|
| 200 | begin
 | 
|---|
| 201 |   if Assigned(FOnOutputText) then FOnOutputText(Text);
 | 
|---|
| 202 | end;
 | 
|---|
| 203 | 
 | 
|---|
| 204 | procedure TKernel.RemoveTerminated;
 | 
|---|
| 205 | var
 | 
|---|
| 206 |   I: Integer;
 | 
|---|
| 207 | begin
 | 
|---|
| 208 |   for I := FAppContexts.Count - 1 downto 0 do begin
 | 
|---|
| 209 |     if FAppContexts[I].AppState = asTerminated then begin
 | 
|---|
| 210 |       FAppContexts.Delete(I);
 | 
|---|
| 211 |       if FAppContexts.Count > 0 then
 | 
|---|
| 212 |         FCurrentApp := FAppContexts[I mod FAppContexts.Count]
 | 
|---|
| 213 |         else FCurrentApp := nil;
 | 
|---|
| 214 |     end;
 | 
|---|
| 215 |   end;
 | 
|---|
| 216 |   if FAppContexts.Count = 0 then Terminated := True;
 | 
|---|
| 217 | end;
 | 
|---|
| 218 | 
 | 
|---|
| 219 | function TKernel.AddApp(Name: string; AppClass: TAppClass): TAppDescription;
 | 
|---|
| 220 | begin
 | 
|---|
| 221 |   Result := TAppDescription.Create;
 | 
|---|
| 222 |   Result.Name := Name;
 | 
|---|
| 223 |   Result.AppClass := AppClass;
 | 
|---|
| 224 |   FAppDescriptions.Add(Result);
 | 
|---|
| 225 | end;
 | 
|---|
| 226 | 
 | 
|---|
| 227 | procedure TKernel.Start;
 | 
|---|
| 228 | var
 | 
|---|
| 229 |   Text: string;
 | 
|---|
| 230 | begin
 | 
|---|
| 231 |   if FAppDescriptions.Count > 0 then begin
 | 
|---|
| 232 |     FCurrentApp := AddAppContext(FAppDescriptions[0]);
 | 
|---|
| 233 |     FCurrentApp.Start;
 | 
|---|
| 234 |     repeat
 | 
|---|
| 235 |       RemoveTerminated;
 | 
|---|
| 236 |       Application.ProcessMessages;
 | 
|---|
| 237 |       Sleep(1);
 | 
|---|
| 238 | 
 | 
|---|
| 239 |       if Assigned(FCurrentApp) then begin
 | 
|---|
| 240 |         Text := FCurrentApp.OutputBuffer.Read;
 | 
|---|
| 241 |         if Text <> '' then begin
 | 
|---|
| 242 |           DoOutputText(Text);
 | 
|---|
| 243 |         end;
 | 
|---|
| 244 |       end;
 | 
|---|
| 245 |     until Terminated;
 | 
|---|
| 246 |   end;
 | 
|---|
| 247 | end;
 | 
|---|
| 248 | 
 | 
|---|
| 249 | procedure TKernel.Input(Text: string);
 | 
|---|
| 250 | begin
 | 
|---|
| 251 |   if Assigned(FCurrentApp) then begin
 | 
|---|
| 252 |     FCurrentApp.InputBuffer.Write(Text);
 | 
|---|
| 253 |   end;
 | 
|---|
| 254 | end;
 | 
|---|
| 255 | 
 | 
|---|
| 256 | procedure TKernel.InputKey(Key: Word);
 | 
|---|
| 257 | begin
 | 
|---|
| 258 |   if Key = 9 then begin
 | 
|---|
| 259 |     if FAppContexts.Count > 1 then begin
 | 
|---|
| 260 |       FCurrentApp := FAppContexts[(FAppContexts.IndexOf(FCurrentApp) + 1 ) mod FAppContexts.Count];
 | 
|---|
| 261 |       DoOutputText('Switched to ' + FCurrentApp.AppDescription.Name + LineEnding);
 | 
|---|
| 262 |     end;
 | 
|---|
| 263 |   end;
 | 
|---|
| 264 | end;
 | 
|---|
| 265 | 
 | 
|---|
| 266 | constructor TKernel.Create;
 | 
|---|
| 267 | begin
 | 
|---|
| 268 |   FAppContexts := TObjectList<TAppContext>.Create;
 | 
|---|
| 269 |   FAppDescriptions := TAppDescriptions.Create;
 | 
|---|
| 270 | end;
 | 
|---|
| 271 | 
 | 
|---|
| 272 | destructor TKernel.Destroy;
 | 
|---|
| 273 | begin
 | 
|---|
| 274 |   FreeAndNil(FAppContexts);
 | 
|---|
| 275 |   FreeAndNil(FAppDescriptions);
 | 
|---|
| 276 |   inherited;
 | 
|---|
| 277 | end;
 | 
|---|
| 278 | 
 | 
|---|
| 279 | end.
 | 
|---|
| 280 | 
 | 
|---|