This file is indexed.

/usr/src/castle-game-engine-4.1.1/window/gtk/why_not_using_gtk_idle.txt is in castle-game-engine-src 4.1.1-1.

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
This documents some history about GTK CastleWindow backend.
My initial approach was to use GTK/glib's mechanism for stuff like
PostRedisplay and Update, more precisely:

- PostRedisplay was doing
  if not Closed then gtk_widget_queue_draw(GTK_WIDGET(GLAreaGtk));

- FlushRedisplay was doing
  gdk_window_process_updates(GTK_WIDGET(WindowGtk)^.Window, g_true);

- signal_expose_event was calling DoDraw.

- GtkIdle (now called Application.ProcessUpdates)
  was registered as GTK's idle function,
  by gtk_idle_add_priority. GtkIdle was responsible for doing our
  idle / timer events (Application.OnIdle/Timer, TCastleWindow.OnIdle/Timer).

  At the beginning, it was registered with priority
  GTK_PRIORITY_REDRAW, then G_PRIORITY_HIGH_IDLE.

But, multiple reasons later, after many code rearrangements,
I dropped these ideas. Now I just implement redrawing and handling idle
by my own methods, called outside of GTK loop processing functions.

Some reasoning:

------------------------------------------------------------------------------

About idle priority:

    We would like to set it to GTK_PRIORITY_REDRAW (this is
    G_PRIORITY_HIGH_IDLE + 20, smaller than G_PRIORITY_DEFAULT_IDLE,
    which means it's more important). That's because our idle events
    (Application.OnIdle, TCastleWindow.OnIdle etc.)
    must be constantly called, even when AutoRedisplay = true
    (i.e. when we always have some redraw pending), and with
    ~approximately the same frequency as draw events.

    The default priority of gtk_idle_add, which is G_PRIORITY_DEFAULT_IDLE,
    would make our idle events (Application.OnIdle etc.) never called
    when AutoRedisplay = true.

    Unfortunately, with GTK 2 (GTK 1 didn't have this problem),
    GTK_PRIORITY_REDRAW causes a problem: after switching to alternative menu,
    key shortcuts to menu items do not work.
    I don't know what's the exact reason of this, I guess that I'm just
    blocking some other internal gtk idles and so I'm causing some
    mysterious problems.

    Simple test case with some comments is in
    ../../../examples/window/window_menu.lpr
    (and also gtk/tests/test_changing_menu/test_changing_menu.lpr
    on michalis.ii SVN, temporarily private (too obscure...)).

    G_PRIORITY_DEFAULT_IDLE doesn't have this problem, but it's leaving
    us with the first problem: Application.OnIdle will not be called
    continously if we redraw continously (like when AutoRedisplay = true).
     - Old solution: call WindowsIdleAndTimer manually
       after calling DoDraw in signal_expose_event.
     - Later solution: we just do not depend on GTK's expose events
       and gtk_widget_queue_draw for the job. We track RedisplayPosted
       ourselves, and realize it from our GtkIdle.

------------------------------------------------------------------------------

Sometimes we had to temporarily uninstall GtkIdle from GTK.

Reasoning in TGLApplication.ProcessAllMessages:

    GtkIdleInstalled must be temporarily turned off.
    That's because when GtkIdleInstalled = true gtk_events_pending()
    always returns true (because it thinks that it should call GtkIdle
    in a loop).

Reasoning in TCastleWindow.FileDialog when using GtkFileChooser:

    Temporarily set GtkIdleInstalled to false.
    Reason? Just like for ProcessAllMessages: when
    GtkIdleInstalled = true then gtk_events_pending()
    always returns true (because it thinks that it should call GtkIdle
    in a loop).

    In this case, gtk_events_pending may be called inside gtk_dialog_run.
    This seems to be the case for GTK starting from 2.14: on Ubuntu 8.10
    open dialog hangs strangely (you cannot see the entries in open dialog,
    and clicking on "file system" hangs etc.) without this
    GtkIdleInstalled := false fix.

This was done by this code:

  GtkIdleHandle: guint;
  FGtkIdleInstalled: boolean;
  procedure SetGtkIdleInstalled(value: boolean);
  { This controls whether GtkIdle is installed (i.e. registered to gtk).
    It is initialized to true in CreateImplDepend, finalized to false
    in DestroyImplDepend and for the most part stays as true.
    BUT it must be temporarily disabled in ProcessAllMesages. }
  property GtkIdleInstalled: boolean
    read FGtkIdleInstalled write SetGtkIdleInstalled;

procedure TGLApplication.SetGtkIdleInstalled(value: boolean);
begin
 if FGtkIdleInstalled <> value then
 begin
  FGtkIdleInstalled := value;
  if FGtkIdleInstalled then
  begin
   GtkIdleHandle := gtk_idle_add_priority(G_PRIORITY_DEFAULT_IDLE,
     @GtkIdle, nil);
  end else
  begin
   gtk_idle_remove(GtkIdleHandle);
  end;
 end;
end;

------------------------------------------------------------------------------

About using signal_expose_event:

    Looks like we cannot call here DoDraw, or any other callback.
    Reason: with newer GTK / glib (Debian gtk 2.18.3-1 and glib 2.22.3-1)
    signal "expose" is never delivered when another "expose" signal
    already works.

    (More info about this GTK change:

        I didn't found exact evidence for this,
        I suspect it's related to
        http://library.gnome.org/devel/gtk/2.18/gtk-migrating-ClientSideWindows.html,
        there's a text

        "One change that can cause problems for some applications is that
        GDK is more aggressive about optimizing away expose events.
        Code that does more than just repainting exposed areas in
        response to expose events may be affected by this.
        "

        If interested in digging more, probably looking at usage of
        G_SIGNAL_NO_RECURSE (for GSignalFlags) in newer GTK src could
        get something.
    )

    Which means that any event that could cause recursive
    event loop will not work correctly, i.e. will not get expose events
    from GTK. For example, calling GLWinMessages.MessageOk from OnDraw
    will fail badly --- during modal message window will not receive
    expose events when window is moved, resized etc.

    So just PostRedisplay. Nearest GtkIdle will handle redraw.

This also means that we do whole PostRedisplay tracking ourselves,
by RedisplayPosted boolean. No point in using gtk_widget_queue_draw /
gdk_window_process_updates if they don't behave like we expect.

------------------------------------------------------------------------------

Eventually, the final problem that broke the straw: it seems
that expose signal cannot be generated when we're inside GTK's registered
idle event.

    This concerns even the expose events that should be passed
    to us from obscuring + showing the window again. Strangely, expose
    after resizing the window still gets generated. But generally they don't work...

    This means that making MessageOk from OnDraw, or OnIdle, still doesn't
    work as expected...

Bottom line: it's not reliable to call any TCastleWindow events (that could
always call MessageOk and enter loop that will require more expose events
to run properly) from GTK's signal expose *or* GTK's registered idle.

------------------------------------------------------------------------------

Also, using GTK's signal expose was forcing us to use elaborate
AutoRedisplayAddToList hack. Reasoning and code below:

    DoDraw took care of AutoRedisplay:
    if AutoRedisplay then
    begin
     if Application.AutoRedisplayAddToList > 0 then

       make sure that Application.AutoRedisplayList contains Self
       (i.e. add Self to Application.AutoRedisplayList, unless
       Application.AutoRedisplayList already contains Self)

     else
       PostRedisplay;

    So specific CastleWindow implementations need not to worry about
    AutoRedisplay. They only have to implement PostRedisplay.

    Also, some implementations (currently this concerns gtk
    implementations) should disallow doing PostRedisplay caused
    by AutoRedisplay = true when they are inside ProcessAllMessages
    (since it can, in some situations, as with GTK bindings,
    mean that ProcessAllMessages would hang forever, since
    there would be always pending message to redisplay the window).
    Such implementations want to do

      if AutoRedisplayAddToList = 0 then AutoRedisplayList.Clear;
      Inc(AutoRedisplayAddToList);

    at the beginning and then

      Dec(AutoRedisplayAddToList);
      if AutoRedisplayAddToList = 0 then AutoRedisplayList.PostRedisplay;

    at the end, see castlewindow_gtk.inc ProcessAllMessages implementation
    for example (with proper try...finally clause around).
    Also such backends should do
      AutoRedisplayList := TCastleWindowsList.Create;
    and
      FreeAndNil(AutoRedisplayList);
    at appropriate places (e.g. in
    TGLApplication.Create/DestroyImplDependent)
    and
      Application.AutoRedisplayList.Delete(Self);
    in TCastleWindow.CloseImplDepend (to make sure that destroyed windows
    are not a memebers of AutoRedisplayList).

    Note that AutoRedisplayAddToList is an integer (not a simple boolean)
    to be safe in case someone calls ProcessAllMessages recursively
    (e.g. call ProcessAllMessages that calls OnMouseDown that calls
    ProcessAllMessages inside). }