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