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 GtkMsgQueue;
18
19{$mode objfpc}{$H+}
20
21interface
22
23uses LazLinkedList, LCLType, LMessages, GtkGlobals, DynHashArray, GtkProc;
24
25type
26  TFinalPaintMessageFlag=(FPMF_None,FPMF_Internal,FPMF_All);
27
28  TGtkMessageQueueItem=class(TLinkListitem)
29  private
30    fMsg : PMsg;
31  public
32    property Msg: PMsg read fMsg write fMsg;
33    function IsPaintMessage: Boolean;
34    procedure DestroyMessage(ParFinalInternalOnly: TFinalPaintMessageFlag;
35                             DisposeMessage: boolean);
36    constructor Create;
37  end;
38
39  { TGtkMessageQueue }
40
41  TGtkMessageQueue=class(TLinkList)
42  private
43    FPaintMessages: TDynHashArray; // Hash for paint messages
44    FCritSec: TRTLCriticalSection;
45    fLock: integer;
46  protected
47    function CreateItem : TLinkListItem;override;
48    function CalculateHash(ParWnd : Hwnd):integer;
49    function HashPaintMessage(p: pointer): integer;
50  public
51    constructor Create;
52    destructor destroy;override;
53    procedure  Lock;
54    procedure  UnLock;
55    function   FirstMessageItem: TGtkMessageQueueItem;
56    function   LastMessageItem: TGtkMessageQueueItem;
57    function   FirstMessage: PMsg;
58    function   LastMessage: PMsg;
59    procedure  AddMessage(ParMsg: PMsg);
60    procedure  RemoveMessage(ParItem: TGtkMessageQueueItem;
61                             ParFinalOnlyInternal: TFinalPaintMessageFlag;
62                             DisposeMessage: boolean);
63    function   FindPaintMessage(HandleWnd: HWnd): TGtkMessageQueueItem;
64    function   HasPaintMessages:boolean;
65    function   HasNonPaintMessages:boolean;
66    function   NumberOfPaintMessages:integer;
67    function   PopFirstMessage: PMsg;
68  end;
69
70
71implementation
72
73{---(TGtkMessageQueueItem)----------------------}
74
75function TGtkMessageQueueItem.IsPaintMessage: Boolean;
76begin
77  if fMsg <> nil then
78    Result := (Msg^.Message = LM_PAINT) or (Msg^.Message = LM_GTKPAINT)
79  else
80    Result := False;
81end;
82
83constructor TGtkMessageQueueItem.Create;
84begin
85  inherited Create;
86  fMsg := nil;
87end;
88
89procedure TGtkMessageQueueItem.DestroyMessage(
90  ParFinalInternalOnly: TFinalPaintMessageFlag; DisposeMessage: boolean);
91begin
92  if (ParFinalInternalOnly in [FPMF_All, FPMF_Internal])
93  and (fMsg^.message = LM_GTKPAINT)
94  then
95    FinalizePaintTagMsg(fMsg);
96  if DisposeMessage then
97    Dispose(fMsg);
98  fMsg := nil;
99end;
100
101{---(TGtkMessageQueue )---------------------------}
102
103constructor TGtkMessageQueue.Create;
104begin
105  inherited Create;
106  FPaintMessages := TDynHashArray.Create(-1);
107  FPaintMessages.OwnerHashFunction := @HashPaintMessage;
108  InitCriticalSection(FCritSec);
109end;
110
111destructor TGtkMessageQueue.destroy;
112begin
113  inherited Destroy;
114  fPaintMessages.destroy;
115  DoneCriticalsection(FCritSec);
116end;
117
118procedure TGtkMessageQueue.Lock;
119begin
120  inc(fLock);
121  if fLock=1 then
122    EnterCriticalsection(FCritSec);
123end;
124
125procedure TGtkMessageQueue.UnLock;
126begin
127  dec(fLock);
128  if fLock=0 then
129    LeaveCriticalsection(FCritSec);
130end;
131
132{------------------------------------------------------------------------------
133  Function: FindPaintMessage
134  Params: a window handle
135  Returns: nil or a Paint Message to the widget
136
137  Searches in FPaintMessages for a LM_PAINT message with HandleWnd.
138 ------------------------------------------------------------------------------}
139function TGtkMessageQueue.FindPaintMessage(HandleWnd: HWnd): TGtkMessageQueueItem;
140var h: integer;
141  HashItem: PDynHashArrayItem;
142begin
143  h:= CalculateHash(HandleWnd);
144  HashItem:=FPaintMessages.GetHashItem(h);
145  if HashItem<>nil then begin
146    Result:=TGtkMessageQueueItem(HashItem^.Item);
147    if Result.Msg^.hWnd=HandleWnd then
148      exit;
149    HashItem:=HashItem^.Next;
150    while (HashItem<>nil) and (HashItem^.IsOverflow) do begin
151
152      Result:=TGtkMessageQueueItem(HashItem^.Item);
153      if Result.Msg^.hWnd=HandleWnd then
154        exit;
155      HashItem:=HashItem^.Next;
156
157    end;
158  end;
159  Result:=nil;
160end;
161
162
163function TGtkMessageQueue.HashPaintMessage(p: pointer): integer;
164begin
165  result := CalculateHash(TGtkMessageQueueItem(p).Msg^.Hwnd);
166end;
167
168function TGtkMessageQueue.CalculateHash(ParWnd : Hwnd):integer;
169var
170  h:integer;
171begin
172  h :=ParWnd;
173  if h<0 then h:=-h;
174  Result:=((h mod 5364329)+(h mod 17)) mod FPaintMessages.Capacity;
175end;
176
177function TGtkMessageQueue.CreateItem : TLinkListItem;
178begin
179  result := TGtkMessageQueueItem.Create;
180  result.ResetItem;
181end;
182
183procedure TGtkMessageQueue.AddMessage(ParMsg : PMsg);
184var
185  vLItem : TGtkMessageQueueItem;
186begin
187  vlItem := TGtkMessageQueueItem(GetNewItem);
188  vlItem.fMsg := ParMsg;
189  AddAsLast(vlItem);
190  if vlItem.IsPaintMessage then fPaintMessages.Add(vlitem);
191end;
192
193function TGtkMessageQueue.FirstMessageItem : TGtkMessageQueueItem;
194begin
195  Lock;
196  try
197    Result :=TGtkMessageQueueItem(First);
198  finally
199    UnLock;
200  end;
201end;
202
203function TGtkMessageQueue.FirstMessage : PMsg;
204begin
205  Result := nil;
206  Lock;
207  try
208    if FirstMessageItem <> nil then  Result := FirstMessageItem.fMsg;
209  finally
210    UnLock;
211  end;
212end;
213
214function TGtkMessageQueue.LastMessageItem : TGtkMessageQueueItem;
215begin
216  Lock;
217  try
218    Result:=TGtkMessageQueueItem(Last);
219  finally
220    UnLock;
221  end;
222end;
223
224function TGtkMessageQueue.LastMessage : PMsg;
225begin
226  Lock;
227  try
228    Result := nil;
229    if LastMessageItem <> nil then   result := LastMessageItem.fMsg;
230  finally
231    UnLock;
232  end;
233end;
234
235{ Remove from queue and destroy message
236  ParItem         : Queue Item for removel
237  ParFinalOnlyInterl : finalyze message only for LM_GtkPaint }
238procedure  TGtkMessageQueue.RemoveMessage(ParItem: TGtkMessageQueueItem;
239  ParFinalOnlyInternal: TFinalPaintMessageFlag; DisposeMessage: boolean);
240begin
241  Lock;
242  try
243    if (ParItem.IsPaintMessage) then
244      fPaintMessages.Remove(ParItem);
245    ParItem.DestroyMessage(ParFinalOnlyInternal, DisposeMessage);
246    Delete(ParItem);
247  finally
248    UnLock;
249  end;
250end;
251
252function TGtkMessageQueue.HasPaintMessages:boolean;
253begin
254  Lock;
255  try
256    Result := fPaintMessages.Count > 0;
257  finally
258    UnLock;
259  end;
260end;
261
262function TGtkMessageQueue.NumberOfPaintMessages:integer;
263begin
264  Lock;
265  try
266    Result := fPaintMessages.Count;
267  finally
268    UnLock;
269  end;
270end;
271
272function TGtkMessageQueue.HasNonPaintMessages:boolean;
273begin
274  Lock;
275  try
276    Result := fPaintMessages.Count <> count;
277  finally
278    UnLock;
279  end;
280end;
281
282function TGtkMessageQueue.PopFirstMessage: PMsg;
283var
284  vlItem : TGtkMessageQueueItem;
285begin
286  Lock;
287  try
288    vlItem := FirstMessageItem;
289    Result := vlItem.Msg;
290    RemoveMessage(vlItem,FPMF_none,false);
291  finally
292    UnLock;
293  end;
294end;
295
296end.
297
298