mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-21 02:19:29 +02:00
Gtk2: changed the way how main event loop iterates BUT ONLY VIA -dUSE_GTK_MAIN_CONTEXT_ITERATION until it's stable enough.
Now PostMessage() work correct when messages arrives from other threads. fixes #17548 and probably more issues with gtk2 threads usage. git-svn-id: trunk@27829 -
This commit is contained in:
parent
293f373040
commit
1c4a0cc726
@ -68,6 +68,9 @@ type
|
|||||||
|
|
||||||
TGtk2WidgetSet = class(TWidgetSet)
|
TGtk2WidgetSet = class(TWidgetSet)
|
||||||
private
|
private
|
||||||
|
{$IFDEF USE_GTK_MAIN_CONTEXT_ITERATION}
|
||||||
|
FMainPoll: PGPollFD;
|
||||||
|
{$ENDIF}
|
||||||
FMultiThreadingEnabled: boolean;
|
FMultiThreadingEnabled: boolean;
|
||||||
FocusTimer: cardinal;
|
FocusTimer: cardinal;
|
||||||
FLastFocusIn: PGtkWidget;
|
FLastFocusIn: PGtkWidget;
|
||||||
|
@ -26,7 +26,11 @@ unit Gtk2MsgQueue;
|
|||||||
|
|
||||||
interface
|
interface
|
||||||
|
|
||||||
uses LazLinkedList, LCLType, LMessages, Gtk2Globals, DynHashArray, Gtk2Proc;
|
uses LazLinkedList, LCLType, LMessages, Gtk2Globals, DynHashArray, Gtk2Proc
|
||||||
|
{$IFDEF USE_GTK_MAIN_CONTEXT_ITERATION}
|
||||||
|
, glib2
|
||||||
|
{$ENDIF}
|
||||||
|
;
|
||||||
|
|
||||||
type
|
type
|
||||||
TFinalPaintMessageFlag=(FPMF_None,FPMF_Internal,FPMF_All);
|
TFinalPaintMessageFlag=(FPMF_None,FPMF_Internal,FPMF_All);
|
||||||
@ -47,7 +51,11 @@ type
|
|||||||
TGtkMessageQueue=class(TLinkList)
|
TGtkMessageQueue=class(TLinkList)
|
||||||
private
|
private
|
||||||
FPaintMessages: TDynHashArray; // Hash for paint messages
|
FPaintMessages: TDynHashArray; // Hash for paint messages
|
||||||
|
{$IFDEF USE_GTK_MAIN_CONTEXT_ITERATION}
|
||||||
|
FMainContext: PGMainContext;
|
||||||
|
{$ELSE}
|
||||||
FCritSec: TRTLCriticalSection;
|
FCritSec: TRTLCriticalSection;
|
||||||
|
{$ENDIF}
|
||||||
fLock: integer;
|
fLock: integer;
|
||||||
protected
|
protected
|
||||||
function CreateItem : TLinkListItem;override;
|
function CreateItem : TLinkListItem;override;
|
||||||
@ -71,6 +79,9 @@ type
|
|||||||
function HasNonPaintMessages:boolean;
|
function HasNonPaintMessages:boolean;
|
||||||
function NumberOfPaintMessages:integer;
|
function NumberOfPaintMessages:integer;
|
||||||
function PopFirstMessage: PMsg;
|
function PopFirstMessage: PMsg;
|
||||||
|
{$IFDEF USE_GTK_MAIN_CONTEXT_ITERATION}
|
||||||
|
property MainContext: PGMainContext read FMainContext;
|
||||||
|
{$ENDIF}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
@ -111,28 +122,46 @@ begin
|
|||||||
inherited Create;
|
inherited Create;
|
||||||
FPaintMessages := TDynHashArray.Create(-1);
|
FPaintMessages := TDynHashArray.Create(-1);
|
||||||
FPaintMessages.OwnerHashFunction := @HashPaintMessage;
|
FPaintMessages.OwnerHashFunction := @HashPaintMessage;
|
||||||
|
{$IFNDEF USE_GTK_MAIN_CONTEXT_ITERATION}
|
||||||
InitCriticalSection(FCritSec);
|
InitCriticalSection(FCritSec);
|
||||||
|
{$ELSE}
|
||||||
|
FMainContext := g_main_context_new;
|
||||||
|
g_main_context_ref(FMainContext);
|
||||||
|
{$ENDIF}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
destructor TGtkMessageQueue.destroy;
|
destructor TGtkMessageQueue.destroy;
|
||||||
begin
|
begin
|
||||||
inherited Destroy;
|
inherited Destroy;
|
||||||
fPaintMessages.destroy;
|
fPaintMessages.destroy;
|
||||||
|
{$IFNDEF USE_GTK_MAIN_CONTEXT_ITERATION}
|
||||||
DoneCriticalsection(FCritSec);
|
DoneCriticalsection(FCritSec);
|
||||||
|
{$ELSE}
|
||||||
|
g_main_context_unref(FMainContext);
|
||||||
|
FMainContext := nil;
|
||||||
|
{$ENDIF}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TGtkMessageQueue.Lock;
|
procedure TGtkMessageQueue.Lock;
|
||||||
begin
|
begin
|
||||||
inc(fLock);
|
inc(fLock);
|
||||||
if fLock=1 then
|
if fLock=1 then
|
||||||
|
{$IFNDEF USE_GTK_MAIN_CONTEXT_ITERATION}
|
||||||
EnterCriticalsection(FCritSec);
|
EnterCriticalsection(FCritSec);
|
||||||
|
{$ELSE}
|
||||||
|
g_main_context_acquire(FMainContext);
|
||||||
|
{$ENDIF}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TGtkMessageQueue.UnLock;
|
procedure TGtkMessageQueue.UnLock;
|
||||||
begin
|
begin
|
||||||
dec(fLock);
|
dec(fLock);
|
||||||
if fLock=0 then
|
if fLock=0 then
|
||||||
|
{$IFNDEF USE_GTK_MAIN_CONTEXT_ITERATION}
|
||||||
LeaveCriticalsection(FCritSec);
|
LeaveCriticalsection(FCritSec);
|
||||||
|
{$ELSE}
|
||||||
|
g_main_context_release(FMainContext);
|
||||||
|
{$ENDIF}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{------------------------------------------------------------------------------
|
{------------------------------------------------------------------------------
|
||||||
@ -292,8 +321,12 @@ begin
|
|||||||
Lock;
|
Lock;
|
||||||
try
|
try
|
||||||
vlItem := FirstMessageItem;
|
vlItem := FirstMessageItem;
|
||||||
|
if vlItem <> nil then
|
||||||
|
begin
|
||||||
Result := vlItem.Msg;
|
Result := vlItem.Msg;
|
||||||
RemoveMessage(vlItem,FPMF_none,false);
|
RemoveMessage(vlItem,FPMF_none,false);
|
||||||
|
end else
|
||||||
|
Result := nil;
|
||||||
finally
|
finally
|
||||||
UnLock;
|
UnLock;
|
||||||
end;
|
end;
|
||||||
|
@ -23,6 +23,37 @@
|
|||||||
// {$DEFINE ASSERT_IS_ON}
|
// {$DEFINE ASSERT_IS_ON}
|
||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
|
|
||||||
|
{$IFDEF USE_GTK_MAIN_CONTEXT_ITERATION}
|
||||||
|
var
|
||||||
|
Gtk2MPF: TGPollFunc;
|
||||||
|
|
||||||
|
function Gtk2PollFunction(ufds:PGPollFD; nfsd:guint; timeout:gint):gint;cdecl;
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
Result := nfsd;
|
||||||
|
if TimeOut = -1 then
|
||||||
|
Gtk2WidgetSet.FMainPoll := ufds
|
||||||
|
else
|
||||||
|
Gtk2WidgetSet.FMainPoll := nil;
|
||||||
|
if Gtk2MPF <> nil then
|
||||||
|
begin
|
||||||
|
if (glib_major_version = 2) and (glib_minor_version < 24) and
|
||||||
|
(Gtk2WidgetSet.FMainPoll <> nil) then
|
||||||
|
begin
|
||||||
|
while (Gtk2WidgetSet.FMainPoll <> nil) and
|
||||||
|
(Gtk2WidgetSet.FMainPoll^.revents = 0) do
|
||||||
|
begin
|
||||||
|
Gtk2MPF(ufds, nfsd, 1);
|
||||||
|
if Gtk2WidgetSet.FMessageQueue.Count > 0 then
|
||||||
|
break;
|
||||||
|
end;
|
||||||
|
end else
|
||||||
|
Gtk2MPF(ufds, nfsd, timeout);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
{$ENDIF}
|
||||||
|
|
||||||
function GTK2FocusCB( widget: PGtkWidget; event:PGdkEventFocus;
|
function GTK2FocusCB( widget: PGtkWidget; event:PGdkEventFocus;
|
||||||
data: gPointer) : GBoolean; cdecl;
|
data: gPointer) : GBoolean; cdecl;
|
||||||
var
|
var
|
||||||
@ -1076,6 +1107,11 @@ constructor TGtk2WidgetSet.Create;
|
|||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
Gtk1Create;
|
Gtk1Create;
|
||||||
|
{$IFDEF USE_GTK_MAIN_CONTEXT_ITERATION}
|
||||||
|
FMainPoll := nil;
|
||||||
|
Gtk2MPF := g_main_context_get_poll_func(g_main_context_default);
|
||||||
|
g_main_context_set_poll_func(g_main_context_default, @Gtk2PollFunction);
|
||||||
|
{$ENDIF}
|
||||||
StayOnTopList := nil;
|
StayOnTopList := nil;
|
||||||
im_context:=gtk_im_multicontext_new;
|
im_context:=gtk_im_multicontext_new;
|
||||||
g_signal_connect (G_OBJECT (im_context), 'commit',
|
g_signal_connect (G_OBJECT (im_context), 'commit',
|
||||||
@ -2342,7 +2378,12 @@ procedure TGtk2WidgetSet.AppProcessMessages;
|
|||||||
|
|
||||||
function PendingGtkMessagesExists: boolean;
|
function PendingGtkMessagesExists: boolean;
|
||||||
begin
|
begin
|
||||||
|
{$IFNDEF USE_GTK_MAIN_CONTEXT_ITERATION}
|
||||||
Result:=(gtk_events_pending<>0) or LCLtoGtkMessagePending;
|
Result:=(gtk_events_pending<>0) or LCLtoGtkMessagePending;
|
||||||
|
{$ELSE}
|
||||||
|
Result := g_main_context_pending(g_main_context_default) or
|
||||||
|
LCLtoGtkMessagePending;
|
||||||
|
{$ENDIF}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
var
|
var
|
||||||
@ -2357,10 +2398,20 @@ begin
|
|||||||
|
|
||||||
// let gtk handle up to 100 messages and call our callbacks
|
// let gtk handle up to 100 messages and call our callbacks
|
||||||
i:=100;
|
i:=100;
|
||||||
while (gtk_events_pending<>0) and (i>0) do begin
|
|
||||||
|
{$IFNDEF USE_GTK_MAIN_CONTEXT_ITERATION}
|
||||||
|
while (gtk_events_pending<>0) and (i>0) do
|
||||||
|
begin
|
||||||
gtk_main_iteration_do(False);
|
gtk_main_iteration_do(False);
|
||||||
dec(i);
|
dec(i);
|
||||||
end;
|
end;
|
||||||
|
{$ELSE}
|
||||||
|
while g_main_context_pending(g_main_context_default) and (i>0) do
|
||||||
|
begin
|
||||||
|
g_main_context_iteration(g_main_context_default, False);
|
||||||
|
dec(i);
|
||||||
|
end;
|
||||||
|
{$ENDIF}
|
||||||
|
|
||||||
//DebugLn(['TGtk2WidgetSet.AppProcessMessages SendCachedGtkMessages']);
|
//DebugLn(['TGtk2WidgetSet.AppProcessMessages SendCachedGtkMessages']);
|
||||||
// send cached gtk messages to the lcl
|
// send cached gtk messages to the lcl
|
||||||
@ -2404,12 +2455,15 @@ begin
|
|||||||
|
|
||||||
//debugln(['TGtk2WidgetSet.AppProcessMessages ',vlMsg^.Message,' ',LM_CHAR,' ',dbgsname(GetLCLObject(Pointer(vlMsg^.hwnd)))]);
|
//debugln(['TGtk2WidgetSet.AppProcessMessages ',vlMsg^.Message,' ',LM_CHAR,' ',dbgsname(GetLCLObject(Pointer(vlMsg^.hwnd)))]);
|
||||||
// Send message
|
// Send message
|
||||||
|
if vlMsg <> nil then
|
||||||
|
begin
|
||||||
try
|
try
|
||||||
with vlMsg^ do SendMessage(hWND, Message, WParam, LParam);
|
with vlMsg^ do SendMessage(hWND, Message, WParam, LParam);
|
||||||
finally
|
finally
|
||||||
Dispose(vlMsg);
|
Dispose(vlMsg);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
// proceed until all messages are handled
|
// proceed until all messages are handled
|
||||||
until (not PendingGtkMessagesExists) or Application.Terminated;
|
until (not PendingGtkMessagesExists) or Application.Terminated;
|
||||||
@ -2425,11 +2479,14 @@ end;
|
|||||||
procedure TGtk2WidgetSet.AppWaitMessage;
|
procedure TGtk2WidgetSet.AppWaitMessage;
|
||||||
begin
|
begin
|
||||||
WaitingForMessages:=true;
|
WaitingForMessages:=true;
|
||||||
|
{$IFNDEF USE_GTK_MAIN_CONTEXT_ITERATION}
|
||||||
gtk_main_iteration_do(True);
|
gtk_main_iteration_do(True);
|
||||||
|
{$ELSE}
|
||||||
|
g_main_context_iteration(g_main_context_default, True);
|
||||||
|
{$ENDIF}
|
||||||
WaitingForMessages:=false;
|
WaitingForMessages:=false;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
procedure TGtk2WidgetSet.FreeStockItems;
|
procedure TGtk2WidgetSet.FreeStockItems;
|
||||||
|
|
||||||
procedure DeleteAndNilObject(var h: HGDIOBJ);
|
procedure DeleteAndNilObject(var h: HGDIOBJ);
|
||||||
|
@ -6799,7 +6799,7 @@ begin
|
|||||||
AMessage^.WParam := WParam;
|
AMessage^.WParam := WParam;
|
||||||
AMessage^.LParam := LParam;
|
AMessage^.LParam := LParam;
|
||||||
|
|
||||||
fMessageQueue.Lock;
|
FMessageQueue.Lock;
|
||||||
try
|
try
|
||||||
if (AMessage^.Message = LM_PAINT) or (AMessage^.Message = LM_GTKPAINT) then
|
if (AMessage^.Message = LM_PAINT) or (AMessage^.Message = LM_GTKPAINT) then
|
||||||
begin
|
begin
|
||||||
@ -6822,6 +6822,7 @@ begin
|
|||||||
|
|
||||||
FMessageQueue.AddMessage(AMessage);
|
FMessageQueue.AddMessage(AMessage);
|
||||||
|
|
||||||
|
{$IFNDEF USE_GTK_MAIN_CONTEXT_ITERATION}
|
||||||
if GetCurrentThreadId <> MainThreadID then
|
if GetCurrentThreadId <> MainThreadID then
|
||||||
begin
|
begin
|
||||||
// awake gtk loop
|
// awake gtk loop
|
||||||
@ -6839,9 +6840,21 @@ begin
|
|||||||
DebugLn(['TGtk2WidgetSet.PostMessage ToDo: wake up gtk']);
|
DebugLn(['TGtk2WidgetSet.PostMessage ToDo: wake up gtk']);
|
||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
end;
|
end;
|
||||||
|
{$ENDIF}
|
||||||
finally
|
finally
|
||||||
fMessageQueue.UnLock;
|
FMessageQueue.UnLock;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{$IFDEF USE_GTK_MAIN_CONTEXT_ITERATION}
|
||||||
|
if GetCurrentThreadId <> MainThreadID then
|
||||||
|
begin
|
||||||
|
// old glib versions needs another way to wake up.
|
||||||
|
if (glib_major_version = 2) and
|
||||||
|
(glib_minor_version < 24) and (FMainPoll <> nil) then
|
||||||
|
FMainPoll^.revents := 1;
|
||||||
|
g_main_context_wakeup(g_main_context_default);
|
||||||
|
end;
|
||||||
|
{$ENDIF}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{------------------------------------------------------------------------------
|
{------------------------------------------------------------------------------
|
||||||
|
Loading…
Reference in New Issue
Block a user