1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
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, qt5;
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
{.$DEFINE DEBUGQTFRAMESIZE}
uses {$IFDEF DEBUGQTFRAMESIZE}LCLProc,{$ENDIF} qtint;
{ TDummyWidget }
function TDummyWidget.GetWidget: QWidgetH;
begin
Result := QWidgetH(TheObject);
end;
procedure TDummyWidget.SetWidget(AValue: QWidgetH);
begin
TheObject := AValue;
end;
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) or (ATop <= 0) or (AWidth <= 0) or (AHeight <= 0) then
begin
//move off visible screen, some wm's does not allow such construct.
QScreen_geometry(QGuiApplication_primaryScreen(), @R);
QWidget_move(Widget, R.CenterPoint.x, R.CenterPoint.y);
//set some reasonable size
QWidget_resize(Widget, 75, 32);
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.
|