1{***************************************************************************
2                  GtkMsgQueue - Messagequeue for Gtk interface
3                  --------------------------------------------
4
5                   Initial Revision  : Thu Aug 16, 2003
6
7
8 ***************************************************************************/
9
10 *****************************************************************************
11  This file is part of the Lazarus Component Library (LCL)
12
13  See the file COPYING.modifiedLGPL.txt, included in this distribution,
14  for details about the license.
15 *****************************************************************************
16}
17unit Gtk2MsgQueue;
18
19{$mode objfpc}{$H+}
20
21interface
22
23uses
24  // RTL
25  Classes,
26  // LCL
27  LazLinkedList, LCLType, LMessages, Gtk2Globals, Gtk2Proc,
28  // LazUtils
29  DynHashArray
30{$IFNDEF USE_GTK_MAIN_OLD_ITERATION}
31, glib2
32{$ENDIF}
33;
34
35type
36  TFinalPaintMessageFlag=(FPMF_None,FPMF_Internal,FPMF_All);
37
38  TGtkMessageQueueItem=class(TLinkListitem)
39  private
40    fMsg : PMsg;
41  public
42    property Msg: PMsg read fMsg write fMsg;
43    function IsPaintMessage: Boolean;
44    procedure DestroyMessage(ParFinalInternalOnly: TFinalPaintMessageFlag;
45                             DisposeMessage: boolean);
46    constructor Create;
47  end;
48
49  { TGtkMessageQueue }
50
51  TGtkMessageQueue=class(TLinkList)
52  private
53    FPaintMessages: TDynHashArray; // Hash for paint messages
54    FCritSec: TRTLCriticalSection;
55    {$IFNDEF USE_GTK_MAIN_OLD_ITERATION}
56    FMainContext: PGMainContext;
57    {$ELSE}
58    fLock: integer;
59    {$ENDIF}
60  protected
61    function CreateItem : TLinkListItem;override;
62    function CalculateHash(ParWnd : Hwnd):integer;
63    function HashPaintMessage(p: pointer): integer;
64  public
65    constructor Create;
66    destructor destroy;override;
67    procedure  Lock;
68    procedure  UnLock;
69    function   FirstMessageItem: TGtkMessageQueueItem;
70    function   LastMessageItem: TGtkMessageQueueItem;
71    function   FirstMessage: PMsg;
72    function   LastMessage: PMsg;
73    procedure  AddMessage(ParMsg: PMsg);
74    procedure  RemoveMessage(ParItem: TGtkMessageQueueItem;
75                             ParFinalOnlyInternal: TFinalPaintMessageFlag;
76                             DisposeMessage: boolean);
77    function   FindPaintMessage(HandleWnd: HWnd): TGtkMessageQueueItem;
78    function   HasPaintMessages:boolean;
79    function   HasNonPaintMessages:boolean;
80    function   NumberOfPaintMessages:integer;
81    function   PopFirstMessage: PMsg;
82    {$IFNDEF USE_GTK_MAIN_OLD_ITERATION}
83    property   MainContext: PGMainContext read FMainContext;
84    {$ENDIF}
85  end;
86
87
88implementation
89
90{---(TGtkMessageQueueItem)----------------------}
91
92function TGtkMessageQueueItem.IsPaintMessage: Boolean;
93begin
94  if fMsg <> nil then
95    Result := (Msg^.Message = LM_PAINT) or (Msg^.Message = LM_GTKPAINT)
96  else
97    Result := False;
98end;
99
100constructor TGtkMessageQueueItem.Create;
101begin
102  inherited Create;
103  fMsg := nil;
104end;
105
106procedure TGtkMessageQueueItem.DestroyMessage(
107  ParFinalInternalOnly: TFinalPaintMessageFlag; DisposeMessage: boolean);
108begin
109  if (ParFinalInternalOnly in [FPMF_All, FPMF_Internal])
110  and (fMsg^.message = LM_GTKPAINT)
111  then
112    FinalizePaintTagMsg(fMsg);
113  if DisposeMessage then
114    Dispose(fMsg);
115  fMsg := nil;
116end;
117
118{---(TGtkMessageQueue )---------------------------}
119
120constructor TGtkMessageQueue.Create;
121begin
122  inherited Create;
123  FPaintMessages := TDynHashArray.Create(-1);
124  FPaintMessages.OwnerHashFunction := @HashPaintMessage;
125  InitCriticalSection(FCritSec);
126  {$IFNDEF USE_GTK_MAIN_OLD_ITERATION}
127  FMainContext := g_main_context_new;
128  g_main_context_ref(FMainContext);
129  {$ENDIF}
130end;
131
132destructor TGtkMessageQueue.destroy;
133begin
134  inherited Destroy;
135  fPaintMessages.destroy;
136  {$IFNDEF USE_GTK_MAIN_OLD_ITERATION}
137  g_main_context_unref(FMainContext);
138  FMainContext := nil;
139  {$ENDIF}
140  DoneCriticalsection(FCritSec);
141end;
142
143procedure TGtkMessageQueue.Lock;
144begin
145  {$IFDEF USE_GTK_MAIN_OLD_ITERATION}
146  if InterlockedIncrement(fLock)=1 then
147    EnterCriticalsection(FCritSec);
148  {$ELSE}
149  if GetCurrentThreadId = MainThreadID then
150    repeat
151    until g_main_context_acquire(FMainContext)  // This can return False.
152  else
153    EnterCriticalsection(FCritSec);
154  {$ENDIF}
155end;
156
157procedure TGtkMessageQueue.UnLock;
158begin
159  {$IFDEF USE_GTK_MAIN_OLD_ITERATION}
160  if InterlockedDecrement(fLock)=0 then
161    LeaveCriticalsection(FCritSec);
162  {$ELSE}
163  if GetCurrentThreadId = MainThreadID then
164    g_main_context_release(FMainContext)
165  else
166    LeaveCriticalsection(FCritSec)
167  {$ENDIF}
168end;
169
170{------------------------------------------------------------------------------
171  Function: FindPaintMessage
172  Params: a window handle
173  Returns: nil or a Paint Message to the widget
174
175  Searches in FPaintMessages for a LM_PAINT message with HandleWnd.
176 ------------------------------------------------------------------------------}
177function TGtkMessageQueue.FindPaintMessage(HandleWnd: HWnd): TGtkMessageQueueItem;
178var h: integer;
179  HashItem: PDynHashArrayItem;
180begin
181  h:= CalculateHash(HandleWnd);
182  HashItem:=FPaintMessages.GetHashItem(h);
183  if HashItem<>nil then begin
184    Result:=TGtkMessageQueueItem(HashItem^.Item);
185    if Result.Msg^.hWnd=HandleWnd then
186      exit;
187    HashItem:=HashItem^.Next;
188    while (HashItem<>nil) and (HashItem^.IsOverflow) do begin
189
190      Result:=TGtkMessageQueueItem(HashItem^.Item);
191      if Result.Msg^.hWnd=HandleWnd then
192        exit;
193      HashItem:=HashItem^.Next;
194
195    end;
196  end;
197  Result:=nil;
198end;
199
200
201function TGtkMessageQueue.HashPaintMessage(p: pointer): integer;
202begin
203  result := CalculateHash(TGtkMessageQueueItem(p).Msg^.Hwnd);
204end;
205
206function TGtkMessageQueue.CalculateHash(ParWnd : Hwnd):integer;
207var
208  h:integer;
209begin
210  h :=ParWnd;
211  if h<0 then h:=-h;
212  Result:=((h mod 5364329)+(h mod 17)) mod FPaintMessages.Capacity;
213end;
214
215function TGtkMessageQueue.CreateItem : TLinkListItem;
216begin
217  result := TGtkMessageQueueItem.Create;
218  result.ResetItem;
219end;
220
221procedure TGtkMessageQueue.AddMessage(ParMsg : PMsg);
222var
223  vLItem : TGtkMessageQueueItem;
224begin
225  vlItem := TGtkMessageQueueItem(GetNewItem);
226  vlItem.fMsg := ParMsg;
227  AddAsLast(vlItem);
228  if vlItem.IsPaintMessage then fPaintMessages.Add(vlitem);
229end;
230
231function TGtkMessageQueue.FirstMessageItem : TGtkMessageQueueItem;
232begin
233  Lock;
234  try
235    Result :=TGtkMessageQueueItem(First);
236  finally
237    UnLock;
238  end;
239end;
240
241function TGtkMessageQueue.FirstMessage : PMsg;
242begin
243  Result := nil;
244  Lock;
245  try
246    if FirstMessageItem <> nil then  Result := FirstMessageItem.fMsg;
247  finally
248    UnLock;
249  end;
250end;
251
252function TGtkMessageQueue.LastMessageItem : TGtkMessageQueueItem;
253begin
254  Lock;
255  try
256    Result:=TGtkMessageQueueItem(Last);
257  finally
258    UnLock;
259  end;
260end;
261
262function TGtkMessageQueue.LastMessage : PMsg;
263begin
264  Lock;
265  try
266    Result := nil;
267    if LastMessageItem <> nil then   result := LastMessageItem.fMsg;
268  finally
269    UnLock;
270  end;
271end;
272
273{ Remove from queue and destroy message
274  ParItem         : Queue Item for removel
275  ParFinalOnlyInterl : finalyze message only for LM_GtkPaint }
276procedure  TGtkMessageQueue.RemoveMessage(ParItem: TGtkMessageQueueItem;
277  ParFinalOnlyInternal: TFinalPaintMessageFlag; DisposeMessage: boolean);
278begin
279  Lock;
280  try
281    if (ParItem.IsPaintMessage) then
282      fPaintMessages.Remove(ParItem);
283    ParItem.DestroyMessage(ParFinalOnlyInternal, DisposeMessage);
284    Delete(ParItem);
285  finally
286    UnLock;
287  end;
288end;
289
290function TGtkMessageQueue.HasPaintMessages:boolean;
291begin
292  Lock;
293  try
294    Result := fPaintMessages.Count > 0;
295  finally
296    UnLock;
297  end;
298end;
299
300function TGtkMessageQueue.NumberOfPaintMessages:integer;
301begin
302  Lock;
303  try
304    Result := fPaintMessages.Count;
305  finally
306    UnLock;
307  end;
308end;
309
310function TGtkMessageQueue.HasNonPaintMessages:boolean;
311begin
312  Lock;
313  try
314    Result := fPaintMessages.Count <> count;
315  finally
316    UnLock;
317  end;
318end;
319
320function TGtkMessageQueue.PopFirstMessage: PMsg;
321var
322  vlItem : TGtkMessageQueueItem;
323begin
324  Lock;
325  try
326    vlItem := FirstMessageItem;
327    if vlItem <> nil then
328    begin
329      Result := vlItem.Msg;
330      RemoveMessage(vlItem,FPMF_none,false);
331    end else
332      Result := nil;
333  finally
334    UnLock;
335  end;
336end;
337
338end.
339
340