This file is indexed.

/usr/src/castle-game-engine-5.2.0/window/castlewindow_android.inc is in castle-game-engine-src 5.2.0-2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
{
  Copyright 2004-2014 Michalis Kamburelis.

  This file is part of "Castle Game Engine".

  "Castle Game Engine" is free software; see the file COPYING.txt,
  included in this distribution, for details about the copyright.

  "Castle Game Engine" 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.

  ----------------------------------------------------------------------------
}

{ Manage OpenGL ES context on Android using EGL,
  and manage message loop using Android NDK. }

{$I castlewindow_egl.inc}
{$I castlewindow_dialogs_by_messages.inc}

{$ifdef read_interface_uses}
CastleAndroidNativeWindow, CastleAndroidInput,
CastleAndroidNativeAppGlue, CastleGenericLists,
{$endif}

{$ifdef read_implementation_uses}
CastleAndroidCWString, CastleAndroidLooper,
CastleAndroidLog, CastleAndroidAssetStream, CastleConfig,
CastleOpenAL, CastleVorbisFile, CastleFreeTypeH, CastleZLib,
{$endif}

{$ifdef read_window_interface}
private
  NativeWindow: EGLNativeWindowType; //< must be set before Window.Open
{$endif read_window_interface}

{$ifdef read_application_interface}
private
  type
    { On Android, we have to track previous touch positions using our
      own structure, instead of relying on
      MainWindow.FTouches.FingerIndexPosition[FingerIndex].

      Reason: DoMotion is not immediately called when
      receiving AMOTION_EVENT_ACTION_MOVE. That is bacause we have to do
      our own event loop, that is emptied after native_app_glue is emptied,
      so we may process many AMOTION_EVENT_ACTION_MOVE before calling
      DoMotion for them.
      This is a consequence of native_app_glue weirdness, that forces us to process
      AMOTION_EVENT_ACTION_MOVE immediately, while calling DoMotion may take a while
      (esp. if it calls Application.ProcessMessage). }
    TTouchChange = object
      OldPosition, Position: TVector2Single;
    end;

    TQueuedEventType = (aeOpen, aeMotionDown, aeMotionUp, aeMotionMove);
    TQueuedEvent = object
      EventType: TQueuedEventType;
      Touch: TTouchChange;
      FingerIndex: TFingerIndex;
    end;
    TQueuedEventList = specialize TGenericStructList<TQueuedEvent>;

  const
    FingerCount = 20;

  var
    FScreenWidth, FScreenHeight: Integer;
    LastDoTimerTime: TMilisecTime;
    AndroidApp: PAndroid_App;
    QEvents: TQueuedEventList;
    TouchChanges: array [0..FingerCount - 1] of TTouchChange;

  function HandleInput(App: PAndroid_app; Event: PAInputEvent): boolean;
  procedure HandleCommand(App: PAndroid_app; Command: Integer);
{$endif read_application_interface}

{$ifdef read_implementation_uses}
{$endif}

{$ifdef read_implementation}

{ Android NDK integration ---------------------------------------------------- }

procedure OpenContext;
var
  Width, Height: Integer;
  Window: TCastleWindowCustom;
  NativeWindow: PANativeWindow;
begin
  Window := Application.MainWindow;
  NativeWindow := Window.NativeWindow; // NativeWindow was saved here by HandleCommand

  { get window size, use it }
  Width := ANativeWindow_getWidth(NativeWindow);
  Height := ANativeWindow_getHeight(NativeWindow);
  AndroidLog(alInfo, 'OpenContext (size: %d %d)', [Width, Height]);
  Application.FScreenWidth := Width;
  Application.FScreenHeight := Height;
  //Window.FullScreen := true; // TODO: setting fullscreen should work like that 4 lines below. Also, should be default?
  Window.Left := 0;
  Window.Top := 0;
  Window.Width := Width;
  Window.Height := Height;

  { create OpenGL context, run OnOpen events and so on }
  Window.Open;
end;

{ Whenever the context is lost, this is called.
  It's important that we release all OpenGL resources, to recreate them later. }
procedure CloseContext;
var
  Window: TCastleWindowCustom;
begin
  AndroidLog(alInfo, 'CloseContext');

  Window := Application.MainWindow;
  if Window <> nil then
  begin
    Window.Close;
    Window.NativeWindow := nil; // make sure to not access the NativeWindow anymore
  end;
end;

procedure TCastleApplication.HandleCommand(App: PAndroid_app; Command: Integer);
var
  QEvent: TQueuedEvent;
  ConfigState: TStringStream;
begin
  case Command of
    APP_CMD_INIT_WINDOW:
      begin
        { We will actually process this event (calling TCastleWindow.Open)
          later, to avoid ANRs when OnOpen does something time-consuming
          (like SceneManager.LoadLevel). }
        MainWindow.NativeWindow := App^.Window;
        QEvent.EventType := aeOpen;
        QEvents.Add(QEvent);
      end;
    APP_CMD_TERM_WINDOW:
      begin
        { Note that we cannot delay processing this event using
          QEvents. After CloseContext, window is really destroyed,
          see android_app_post_exec_cmd in native app glue. }
        QEvents.Clear;
        CloseContext;
      end;
    APP_CMD_SAVE_STATE:
      begin
        { We cannot delay processing this, onSaveInstanceState waits for us
          to fill App^.SavedState. }
        ConfigState := TStringStream.Create('');
        try
          { read Config to ConfigState }
          Config.Save(ConfigState);
          ConfigState.Position := 0;
          { read ConfigState to App^.SavedState }
          App^.SavedState := AllocateSavedState(ConfigState.Size);
          ConfigState.ReadBuffer(App^.SavedState^, ConfigState.Size);
          App^.SavedStateSize := ConfigState.Size;
          { log }
          WritelnLogMultiline('Config', 'Saved state:' + NL + ConfigState.DataString);
        finally FreeAndNil(ConfigState) end;
      end;
  end;
end;

function TCastleApplication.HandleInput(App: PAndroid_app; Event: PAInputEvent): boolean;

  function CurrentPosition(const PointerIndex: Integer): TVector2Single;
  begin
    Result := Vector2Single(
      AMotionEvent_getX(Event, PointerIndex),
      ScreenHeight - AMotionEvent_getY(Event, PointerIndex));
  end;

  { Note that MotionDown etc. do *not* call directly DoMouseDown and similar,
    as we *cannot* block HandleInput for indefinite amount of time
    (and DoMouseDown may call OnPress which may even do MessageOK
    that makes a modal dialog calling Application.ProcessMessage inside).
    The API of native_app_glue is somewhat stupid, it allows us to process
    messages in a loop, but still the HandleInput and HandleCommand
    cannot block making a modal dialog ---  the process_input and process_cmd
    implementations in native_app_glue depend on that. }

  procedure MotionDown(const FingerIndex: TFingerIndex; const PointerIndex: Integer);
  var
    QEvent: TQueuedEvent;
  begin
    if FingerIndex < FingerCount then
    begin
      { update TouchChanges }
      TouchChanges[FingerIndex].OldPosition := TouchChanges[FingerIndex].Position;
      TouchChanges[FingerIndex].Position := CurrentPosition(PointerIndex);

      QEvent.EventType := aeMotionDown;
      QEvent.FingerIndex := FingerIndex;
      QEvent.Touch := TouchChanges[FingerIndex];
      QEvents.Add(QEvent);
    end;
  end;

  procedure MotionUp(const FingerIndex: TFingerIndex; const PointerIndex: Integer);
  var
    QEvent: TQueuedEvent;
  begin
    if FingerIndex < FingerCount then
    begin
      { update TouchChanges }
      TouchChanges[FingerIndex].OldPosition := TouchChanges[FingerIndex].Position;
      TouchChanges[FingerIndex].Position := CurrentPosition(PointerIndex);

      QEvent.EventType := aeMotionUp;
      QEvent.FingerIndex := FingerIndex;
      QEvent.Touch := TouchChanges[FingerIndex];
      QEvents.Add(QEvent);
    end;
  end;

  procedure MotionMove(const FingerIndex: TFingerIndex; const PointerIndex: Integer);
  var
    QEvent: TQueuedEvent;
    NewPosition: TVector2Single;
  const
    MinDistanceToReportMove = 0.5;
  begin
    if FingerIndex < FingerCount then
    begin
      NewPosition := CurrentPosition(PointerIndex);
      if PointsDistanceSqr(NewPosition, TouchChanges[FingerIndex].Position) >
         Sqr(MinDistanceToReportMove) then
      begin
        { update TouchChanges }
        TouchChanges[FingerIndex].OldPosition := TouchChanges[FingerIndex].Position;
        TouchChanges[FingerIndex].Position := NewPosition;

        QEvent.EventType := aeMotionMove;
        QEvent.FingerIndex := FingerIndex;
        QEvent.Touch := TouchChanges[FingerIndex];
        QEvents.Add(QEvent);
      end;
    end;
  end;

var
  MotionAction, PointerIndex, ActionPointerIndex: Integer;
begin
  Result := false;
  if AInputEvent_getType(Event) = AINPUT_EVENT_TYPE_MOTION then
  begin
    MotionAction := AMotionEvent_getAction(Event) and AMOTION_EVENT_ACTION_MASK;
    ActionPointerIndex := (AMotionEvent_getAction(Event) and
      AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) shr
      AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;

    case MotionAction of
      AMOTION_EVENT_ACTION_DOWN:
        begin
          for PointerIndex := 0 to AMotionEvent_getPointerCount(Event) - 1 do
            MotionDown(AMotionEvent_getPointerId(Event, PointerIndex), PointerIndex);
          Result := true;
        end;
      AMOTION_EVENT_ACTION_UP:
        begin
          for PointerIndex := 0 to AMotionEvent_getPointerCount(Event) - 1 do
            MotionUp(AMotionEvent_getPointerId(Event, PointerIndex), PointerIndex);
          Result := true;
        end;
      AMOTION_EVENT_ACTION_CANCEL:
        WritelnLog('Android', 'Motion cancel event');
        { TODO: Handle this like AMOTION_EVENT_ACTION_UP? }
      AMOTION_EVENT_ACTION_POINTER_DOWN:
        MotionDown(AMotionEvent_getPointerId(Event, ActionPointerIndex), ActionPointerIndex);
      AMOTION_EVENT_ACTION_POINTER_UP:
        MotionUp(AMotionEvent_getPointerId(Event, ActionPointerIndex), ActionPointerIndex);
      AMOTION_EVENT_ACTION_MOVE:
        begin
          for PointerIndex := 0 to AMotionEvent_getPointerCount(Event) - 1 do
            MotionMove(AMotionEvent_getPointerId(Event, PointerIndex), PointerIndex);
          Result := true;
        end;
    end;
  end;
end;

procedure AndroidMainImplementation(App: PAndroid_App);
var
  ConfigState: TStringStream;
begin
  Application.AndroidApp := App;

  { Use SavedState as XML contents that should be loaded to global Config.
    Note that we directly use AndroidLog below, not WritelnLog or OnWarning,
    because user code had no chance to call InitializeLog yet. }
  if App^.SavedState <> nil then
  begin
    ConfigState := TStringStream.Create('');
    try
      { load ConfigState from App^.SavedState }
      ConfigState.WriteBuffer(App^.SavedState^, App^.SavedStateSize);
      ConfigState.Position := 0;
      { load Config from ConfigState }
      try
        Config.Load(ConfigState);
        { log }
        AndroidLog(alInfo, 'Config: Loaded state:' + NL + ConfigState.DataString);
      except
        on E: TObject do
          AndroidLog(alWarn, 'Config: Cannot read XML config from Android state: ' + ExceptMessage(E));
      end;
    finally FreeAndNil(ConfigState) end;
  end else
    AndroidLog(alInfo, 'Config: no saved state');

  Application.Run;
end;

{ TCastleWindowCustom ---------------------------------------------------------- }

procedure TCastleWindowCustom.CreateBackend;
begin
end;

{ It would be nice to update Android's app menu based on MainMenu contents.
  But we cannot do this from NDK, it would require implementing Java code
  connected to us. }

procedure TCastleWindowCustom.BackendMenuInitialize;
begin
end;

procedure TCastleWindowCustom.BackendMenuFinalize;
begin
end;

procedure TCastleWindowCustom.MenuUpdateCaption(Entry: TMenuEntryWithCaption);
begin
  MenuFinalize;
  MenuInitialize;
end;

procedure TCastleWindowCustom.MenuUpdateEnabled(Entry: TMenuEntryWithCaption);
begin
  MenuFinalize;
  MenuInitialize;
end;

procedure TCastleWindowCustom.MenuUpdateChecked(Entry: TMenuItemChecked);
begin
  MenuFinalize;
  MenuInitialize;
end;

function TCastleWindowCustom.MenuUpdateCheckedFast: boolean;
begin
  Result := false;
end;

procedure TCastleWindowCustom.MenuInsert(const Parent: TMenu;
  const ParentPosition: Integer; const Entry: TMenuEntry);
begin
  MenuFinalize;
  MenuInitialize;
end;

procedure TCastleWindowCustom.MenuDelete(const Parent: TMenu;
  const ParentPosition: Integer; const Entry: TMenuEntry);
begin
  MenuFinalize;
  MenuInitialize;
end;

procedure TCastleWindowCustom.OpenBackend;
begin
  ContextCreateEnd(NativeWindow);
  Application.OpenWindowsAdd(Self);
end;

procedure TCastleWindowCustom.CloseBackend;
begin
  ContextDestroy;
  { Note that we do not clear NativeWindow now, because we may need it
    if window is opened again. Although when Android system opens the window,
    NativeWindow is set by OpenContext,
    but when we manually reopen the window from
    code (like "reopen context" button in android_demo), we need to keep
    previous NativeWindow value. }
end;

procedure TCastleWindowCustom.SetCaption(const Part: TCaptionPart; const Value: string);
begin
  FCaption[Part] := Value;
  if not Closed then { TODO: use GetWholeCaption };
end;

procedure TCastleWindowCustom.SetCursor(const Value: TMouseCursor);
begin
  if FCursor <> Value then
  begin
    FCursor := Value;
    if not Closed then
      { TODO UpdateCursor };
  end;
end;

procedure TCastleWindowCustom.SetCustomCursor(const Value: TRGBAlphaImage);
begin
  { TODO }
  FCustomCursor := Value;
end;

function TCastleWindowCustom.RedirectKeyDownToMenuClick: boolean;
begin
  { Call menu shortcuts on key presses.
    Since we don't show MainMenu, this is the only way how we can at least
    support it's shortcuts. }
  Result := true;
end;

procedure TCastleWindowCustom.SetMousePosition(const Value: TVector2Single);
begin
  { There is no cursor position on Android devices.
    Just update MousePosition (although we're not really required to
    do this, code using SetMousePosition should not depend that
    it was always successfull). }
  if not Closed then
    FMousePosition := Value;
end;

procedure TCastleWindowCustom.SetFullScreen(const Value: boolean);
begin
  FFullScreen := Value; //< does nothing on Android
end;

{ TCastleApplication ---------------------------------------------------------- }

procedure TCastleApplication.CreateBackend;
begin
  AndroidMain := @AndroidMainImplementation;
end;

procedure TCastleApplication.DestroyBackend;
begin
end;

function TCastleApplication.ProcessAllMessages: boolean;
begin
  Result := ProcessMessage(false, false);
end;

function TCastleApplication.ProcessMessage(WaitForMessage, WaitToLimitFPS: boolean): boolean;

  { Call Update on Application and all open Windows, call OnTimer on Application }
  procedure WindowsUpdateAndTimer;
  begin
    DoSelfUpdate;
    FOpenWindows.DoUpdate;
    MaybeDoTimer(LastDoTimerTime);
  end;

  procedure Resize(Width, Height: Integer);
  begin
    AndroidLog(alInfo, 'Resize (size: %d %d)', [Width, Height]);

    FScreenWidth := Width;
    FScreenHeight := Height;

    if not MainWindow.Closed then
      MainWindow.DoResize(Width, Height, false);
  end;

var
  Ident, Events, NewWidth, NewHeight: Integer;
  Source: Pandroid_poll_source;
  QEvent: TQueuedEvent;
  MessageHandlingBegin: TTimerResult;
begin
  MessageHandlingBegin := Timer;
  repeat
    if WaitForMessage and AllowSuspendForInput and
      { Unfortunately, we have to be more conservative than
        AllowSuspendForInput to decide when we can suspend.

        Right now we cannot suspend on non-closed window.
        Otherwise we do not react to resize events soon enough,
        since we have to actually do a couple of loop passes until resize
        reaches us. Reproducible on drawing_toy: without this,
        we will not receive resize before drawing. }
      ((MainWindow = nil) or MainWindow.Closed) then
    begin
      WritelnLog('Android', 'Waiting for next event without consuming CPU ticks.');
      Ident := ALooper_pollAll(-1 { wait }, nil, @Events, @Source);
    end else
      Ident := ALooper_pollAll(0, nil, @Events, @Source);
    if Ident < 0 then Break;

    if Source <> nil then
      Source^.Process(AndroidApp, Source);

    { poll our own events quueue.
      Yes, we need our own events queue to shield from the native_app_glue
      weirdness... }
    if MainWindow <> nil then
      while QEvents.Count > 0 do
      begin
        QEvent := QEvents.First;
        QEvents.Delete(0);
        case QEvent.EventType of
          aeOpen: OpenContext;
          aeMotionDown:
            if not MainWindow.Closed then
              MainWindow.DoMouseDown(QEvent.Touch.Position, mbLeft, QEvent.FingerIndex);
          aeMotionUp  :
            if not MainWindow.Closed then
              MainWindow.DoMouseUp  (QEvent.Touch.Position, mbLeft, QEvent.FingerIndex, false);
          aeMotionMove:
            if not MainWindow.Closed then
              MainWindow.DoMotion   (InputMotion(QEvent.Touch.OldPosition, QEvent.Touch.Position, [mbLeft], QEvent.FingerIndex));
        end;
      end;

    // Check if we are exiting.
    if AndroidApp^.DestroyRequested = 1 then
    begin
      CloseContext;
      Exit(false);
    end;

  // loop condition avoids being clogged with motion events
  until (Timer - MessageHandlingBegin) / TimerFrequency > 1 / LimitFPS;

  if (MainWindow <> nil) and not MainWindow.Closed then
  begin
    { check for Resize. As there is no reliable event to capture it
      (ANativeWindow_getWidth and ANativeWindow_getheight are immediately
      updated, but for some time EGL sizes stay old) so we just watch
      for changes, and only fire our "Resize" when really EGL size changed. }
    MainWindow.QuerySize(NewWidth, NewHeight);
    if (NewWidth <> MainWindow.Width) or
       (NewHeight <> MainWindow.Height) then
      Resize(NewWidth, NewHeight);

    { we do not check Invalidated here, just redraw every frame when window is open }
    MainWindow.DoRender;

    WindowsUpdateAndTimer;
  end;

  { Note that we ignore WaitToLimitFPS here, right now.
    When redrawing (not MainWindow.Closed), it is always "on" in the main loop,
    we don't control it, Android throttles render speed anyway.
    When not redrawing, then there's not much point in WaitToLimitFPS,
    we should rather let WaitForMessage mechanism work (when all windows are
    closed, we should usually have WaitForMessage = true). }

  Result := true;
end;

procedure TCastleApplication.Run;
begin
  if MainWindow = nil then
    raise Exception.Create('For Android, you have to assign Application.MainWindow');

  if QEvents = nil then
    QEvents := TQueuedEventList.Create else
    QEvents.Clear;

  FillByte(TouchChanges, SizeOf(TouchChanges), 0);

  { since we have AndroidApp now, prepare it }
  AndroidApp^.OnAppCmd := @HandleCommand;
  AndroidApp^.OnInputEvent := @HandleInput;
  AssetManager := AndroidApp^.Activity^.AssetManager;

  { We could not do dlopen on Android before AndroidMain is called.
    Load necessary libraries now. Some of the below libraries are not really
    available on Android anyway, but we keep the list below for completeness
    (engine gracefully handles lack of most libraries, only OpenGLES
    is really required). }
  { Note that this may be called many times, because AndroidMainImplementation
    may be called many times, because ANativeActivity_onCreate may be called
    multple times. So things below must be implemented in a secure way,
    to work Ok when executed multiple times. }
  GLES20Initialization;
  OpenALInitialization;
  VorbisFileInitialization;
  InitializeAndroidCWString;
  LoadFreeTypeLibrary;
  {$ifndef CASTLE_PNG_USING_FCL_IMAGE}
  {$ifndef CASTLE_PNG_STATIC}
  PngInitialization;
  {$endif}
  {$endif}
  {$ifndef CASTLE_ZLIB_USING_PASZLIB}
  ZLibInitialization;
  {$endif}

  while ProcessMessage(true, true) do ;

  FreeAndNil(QEvents);
end;

procedure TCastleApplication.QuitWhenNoOpenWindows;
begin
  { TODO }
end;

function TCastleApplication.ScreenWidth: integer;
begin
  Result := FScreenWidth;
end;

function TCastleApplication.ScreenHeight: integer;
begin
  Result := FScreenHeight;
end;

function TCastleApplication.BackendName: string;
begin
  Result := 'Android';
end;

{ TCastleClipboard ----------------------------------------------------------- }

function TCastleClipboard.GetAsText: string;
begin
  { TODO }
  Result := '';
end;

procedure TCastleClipboard.SetAsText(const Value: string);
begin
  { TODO }
end;

{ TCastleWindow -------------------------------------------------------------- }

procedure TCastleWindow.NavigationInfoChanged;
begin
end;

{$endif read_implementation}