From 63be2586d7dc47d0aa395f76c475968ee6f489d0 Mon Sep 17 00:00:00 2001 From: zeljko Date: Tue, 14 Apr 2020 12:45:56 +0000 Subject: [PATCH] Qt: added qtx11dummywidget unit, will be used to get accurate frame - eg GetWindowRect() before widget is mapped. part of issue #36897 git-svn-id: trunk@62969 - --- .gitattributes | 1 + lcl/interfaces/qt/qtx11dummywidget.pas | 167 +++++++++++++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 lcl/interfaces/qt/qtx11dummywidget.pas diff --git a/.gitattributes b/.gitattributes index 0c244e666d..78b73b5e8b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -10264,6 +10264,7 @@ lcl/interfaces/qt/qtwspairsplitter.pp svneol=native#text/pascal lcl/interfaces/qt/qtwsspin.pp svneol=native#text/pascal lcl/interfaces/qt/qtwsstdctrls.pp svneol=native#text/pascal lcl/interfaces/qt/qtx11.inc svneol=native#text/plain +lcl/interfaces/qt/qtx11dummywidget.pas svneol=native#text/plain lcl/interfaces/qt5/Makefile svneol=native#text/plain lcl/interfaces/qt5/Makefile.compiled svneol=native#text/plain lcl/interfaces/qt5/Makefile.fpc svneol=native#text/plain diff --git a/lcl/interfaces/qt/qtx11dummywidget.pas b/lcl/interfaces/qt/qtx11dummywidget.pas new file mode 100644 index 0000000000..7457bd66ed --- /dev/null +++ b/lcl/interfaces/qt/qtx11dummywidget.pas @@ -0,0 +1,167 @@ +unit qtx11dummywidget; + +{$mode objfpc}{$H+} + +{we use this unit only under x11 to get accurate frame size in our aps} + +interface + +uses + Classes, SysUtils, Types, qtobjects, qt4; + +type + + { TDummyWidget } + + TDummyWidget = class(TQtObject) + private + FFrameRect: TRect; + FFirstPaintEvent: boolean; + function GetWidget: QWidgetH; + procedure SetWidget(AValue: QWidgetH); + public + constructor Create; override; overload; + function GetWidgetFrame: TRect; + function EventFilter(Sender: QObjectH; Event: QEventH): Boolean; cdecl; override; + function ShowDummyWidget(const ALeft, ATop, AWidth, + AHeight: integer): boolean; + procedure SendToBack; + procedure HideWidget; + property Widget: QWidgetH read GetWidget write SetWidget; + end; + +implementation +uses LCLProc, qtint; + +{ TDummyWidget } + +function TDummyWidget.GetWidget: QWidgetH; +begin + Result := QWidgetH(TheObject); +end; + +procedure TDummyWidget.SetWidget(AValue: QWidgetH); +begin + TheObject := AValue; +end; + +{$DEFINE DEBUGQTFRAMESIZE} +function TDummyWidget.ShowDummyWidget(const ALeft, ATop, AWidth, + AHeight: integer): boolean; +var + R: TRect; + {$IFDEF DEBUGQTFRAMESIZE} + ATicks: QWord; + {$ENDIF} + ALoop: integer; + AMaxLoops: integer; +begin + Result := Assigned(Widget); + if Result then + begin + if not IsWayland and QX11Info_isCompositingManagerRunning then + {it is possible that we need 50-100 msec to get frame when running under compositing manager. + Measured composition managers: kwin 89 msec} + AMaxLoops := 200000 + else + AMaxLoops := 20000; + {$IFDEF DEBUGQTFRAMESIZE} + writeln('ShowDummyWidget(start) WindowManager="',GetWindowManager,'" Compositing enabled="',QX11Info_isCompositingManagerRunning,'" IsWayland="',IsWayland,'" MaxLoops=',AMaxLoops); + ATicks := GetTickCount64; + {$ENDIF} + if (ALeft <= 0) and (ATop <= 0) and (AWidth <= 0) and (AHeight <= 0) then + begin + //move off visible screen, some wm's does not allow such construct. + QWidget_move(Widget, -200, 0); + //set some reasonable size + QWidget_resize(Widget, 100, 75); + end else + begin + QWidget_move(Widget, ALeft + 1, ATop + 1); + QWidget_resize(Widget, AWidth - 1, AHeight - 1); + end; + QWidget_setAttribute(Widget, QtWA_X11DoNotAcceptFocus, True); + QWidget_show(Widget); + + {We are waiting until dummy window is laid out on screen by window manager + ALoop variable is needed to avoid infinite loop. + Usually we get result in about 20-30msec on modern X11 without compositing, + but 30-100 msec on wm with compositing enabled. + Older X11 or slower machine might need more loops to get result, + but it won't be over 200 msec in any case.} + + ALoop := 0; // avoid infinite loop + while not FFirstPaintEvent do + begin + inc(ALoop); + QCoreApplication_processEvents(); + if ALoop > AMaxLoops then + break; + end; + {$IFDEF DEBUGQTFRAMESIZE} + writeln('ShowDummyWidget: 1st LOOP=',ALoop); + {$ENDIF} + R := Rect(0 ,0, 0, 0); + ALoop := 0; // avoid infinite loop + //Qt4 sets QtWA_Mapped before first paint event and that's wrong since x11 did not update decoration yet. + //so we MUST wait infinite for the first paint event. + while (R.Top <= 0) do // qt4 sets QtWA_Mapped before first paint event :(, maybe we should add eventFilter to this widget and wait for paint event + begin + inc(ALoop); + R := GetWidgetFrame; + QCoreApplication_processEvents(); + if ALoop > AMaxLoops then + break; + end; + + {$IFDEF DEBUGQTFRAMESIZE} + writeln('ShowDummyWidget: 2nd LOOP=',ALoop,' LAST R=',dbgs(R)); + writeln('ShowDummyWidget: *finished* FRAME=',dbgs(GetWidgetFrame),' in ',GetTickCount64 - ATicks,' msec '); + {$ENDIF} + end; +end; + +constructor TDummyWidget.Create; +begin + inherited Create; + FFrameRect := Rect(0, 0, 0, 0); + Widget := QWidget_create(nil, QtWindow); + QWidget_setAttribute(Widget, QtWA_ShowWithoutActivating); + QWidget_setFocusPolicy(Widget, QtNoFocus); + AttachEvents; +end; + +function TDummyWidget.GetWidgetFrame: TRect; +var + AFrame, AGeometry: TRect; +begin + Result := FFrameRect; + if not Assigned(Widget) then + exit; + QWidget_frameGeometry(Widget, @AFrame); + QWidget_geometry(Widget, @AGeometry); + FFrameRect := Rect(AGeometry.Left - AFrame.Left, AGeometry.Top - AFrame.Top, AFrame.Right - AGeometry.Right, AFrame.Bottom - AGeometry.Bottom); + Result := FFrameRect; +end; + +function TDummyWidget.EventFilter(Sender: QObjectH; Event: QEventH): Boolean; + cdecl; +begin + Result := False; + if not FFirstPaintEvent and (QEvent_type(Event) = QEventPaint) then + FFirstPaintEvent := True; +end; + +procedure TDummyWidget.SendToBack; +begin + if Assigned(Widget) then + QWidget_lower(Widget); +end; + +procedure TDummyWidget.HideWidget; +begin + if Assigned(Widget) then + QWidget_hide(Widget); +end; + +end.