View Issue Details

IDProjectCategoryView StatusLast Update
0031832LazarusLCLpublic2017-05-18 19:30
ReporterKostas Michalopoulos Assigned ToZeljan Rikalo  
PrioritynormalSeverityminorReproducibilityalways
Status resolvedResolutionfixed 
PlatformLinuxOSDebian 
Product Version1.9 (SVN) 
Summary0031832: SW_SHOWNORMAL on Gtk2 widgetset shrinks the window in some window managers
DescriptionCalling ShowWindow(Handle, SW_SHOWNORMAL) on Gtk2 widgetset will shrink the window under some window managers. This call is made at several places during design time, including changing components and loading forms. The video below shows the issue with Window Maker:

https://webmshare.com/q78rb

The bug seems to be in Gtk2's gtk_window_unmaximize which attempts to resize the window even if it is maximized. While it ideally the bug should be fixed in Gtk2, the development seems to be almost halted (mainly build and documentation fixes seem to happen) and with Lazarus targeting earlier versions like 2.8 (from 2005) it is a better idea to address this on LCL side.

The attached patch contains a workaround for TCustomForm descendants that checks the maximized state (Gtk2 doesn't provide any way to query the current state, but LCL already tracks it for forms) before calling gtk_window_unmaximize, which fixes the issue shown in the video above. The patch was made against revision 54956, but should apply to 1.8RC1 and earlier versions (maybe by hand - it is a small patch after all).
Steps To Reproduce1. Install Window Maker
2. Build Lazarus with the Gtk2 widgetset
3. Run Lazarus
4. Try to select any component from the palette
5. Notice how the window shrinks

alternatively check the video
Tagscomponent palette, designer, gtk2, lcl, widgetset
Fixed in Revision54958,54975
LazTarget-
WidgetsetGTK 2
Attached Files

Activities

Kostas Michalopoulos

2017-05-17 23:02

reporter  

gtk2-window-unmaximize-workaround-for-sw_shownormal-patch.diff (1,476 bytes)   
Index: lcl/interfaces/gtk2/gtk2winapi.inc
===================================================================
--- lcl/interfaces/gtk2/gtk2winapi.inc	(revision 54956)
+++ lcl/interfaces/gtk2/gtk2winapi.inc	(working copy)
@@ -9400,6 +9400,7 @@
   GtkWindow: PGtkWindow;
   B: Boolean;
   Widget: PGtkWidget;
+  Control: TWinControl;
 begin
   Result := False;
 
@@ -9446,7 +9447,19 @@
         if not GTK_WIDGET_VISIBLE(PGtkWidget(GtkWindow)) then
           gtk_widget_show(PGtkWidget(GtkWindow));
         gtk_window_deiconify(GtkWindow);
-        gtk_window_unmaximize(GtkWindow);
+        // Workaround for gtk2 bug with some window managers:
+        // calling gtk_window_unmaximize when the window is not maximized
+        // will cause window to shrink.  Here we check if the window we are
+        // resizing is a form and use its WindowState property to make sure
+        // it is maximized before trying to unmaximize it which avoids the
+        // issue (custom code that uses the Win32 API will still have the
+        // problem though, but gtk2 does not offer a is_maximized method)
+        Control:=TWinControl(GetLCLObject({%H-}Pointer(hWnd)));
+        if Assigned(Control) and (Control is TCustomForm) then begin
+          if TCustomForm(Control).WindowState=wsMaximized then
+            gtk_window_unmaximize(GtkWindow);
+        end else
+          gtk_window_unmaximize(GtkWindow);
         gtk_window_unfullscreen(GtkWindow);
       end;
     end;

Kostas Michalopoulos

2017-05-17 23:04

reporter  

Kostas Michalopoulos

2017-05-17 23:05

reporter   ~0100374

I attached the video for future reference in case the URL disappears.

CudaText man

2017-05-17 23:30

reporter   ~0100377

+ if Assigned(Control) and (Control is TCustomForm) then begin
+ if TCustomForm(Control).WindowState=wsMaximized then
+ gtk_window_unmaximize(GtkWindow);
+ end else
+ gtk_window_unmaximize(GtkWindow);


better to do

  change := true;
  if Assigned(Control) and (Control is TCustomForm) then
    change:= TCustomForm(Control).WindowState=wsMaximized;
  if change then
    gtk_window_unmaximize(GtkWindow);

Mattias Gaertner

2017-05-18 01:12

manager   ~0100380

Mighty thanks!

I simplified the condition.

Zeljan Rikalo

2017-05-18 13:41

developer  

issue31832.diff (1,821 bytes)   
Index: gtk2winapi.inc
===================================================================
--- gtk2winapi.inc	(revision 54966)
+++ gtk2winapi.inc	(working copy)
@@ -9446,20 +9446,16 @@
       begin
         if not GTK_WIDGET_VISIBLE(PGtkWidget(GtkWindow)) then
           gtk_widget_show(PGtkWidget(GtkWindow));
-        gtk_window_deiconify(GtkWindow);
-        // Workaround for gtk2 bug with some window managers:
-        // calling gtk_window_unmaximize when the window is not maximized
-        // will cause window to shrink.  Here we check if the window we are
-        // resizing is a form and use its WindowState property to make sure
-        // it is maximized before trying to unmaximize it which avoids the
-        // issue (custom code that uses the Win32 API will still have the
-        // problem though, but gtk2 does not offer a is_maximized method)
-        // see https://bugs.freepascal.org/view.php?id=31832
-        Control:=TWinControl(GetLCLObject({%H-}Pointer(hWnd)));
-        if (not (Control is TCustomForm)) or
-            (TCustomForm(Control).WindowState=wsMaximized) then
-          gtk_window_unmaximize(GtkWindow);
-        gtk_window_unfullscreen(GtkWindow);
+        if GDK_IS_WINDOW(PGtkWidget(GtkWindow)^.window) then
+        begin
+          // see https://bugs.freepascal.org/view.php?id=31832
+          if gdk_window_get_state(PGtkWidget(GtkWindow)^.window) = GDK_WINDOW_STATE_ICONIFIED then
+            gtk_window_deiconify(GtkWindow);
+          if gdk_window_get_state(PGtkWidget(GtkWindow)^.window) = GDK_WINDOW_STATE_MAXIMIZED then
+            gtk_window_unmaximize(GtkWindow);
+          if gdk_window_get_state(PGtkWidget(GtkWindow)^.window) = GDK_WINDOW_STATE_FULLSCREEN then
+            gtk_window_unfullscreen(GtkWindow);
+        end;
       end;
     end;
 
issue31832.diff (1,821 bytes)   

Zeljan Rikalo

2017-05-18 13:42

developer   ~0100434

I've attached possibly better solution, so just decide to commit it or not.
Above that code is check with B:boolean which check for window validity, so my patch is clean gtk patch without involving lcl.

Zeljan Rikalo

2017-05-18 14:08

developer  

issue31832_2.diff (3,622 bytes)   
Index: gtk2winapi.inc
===================================================================
--- gtk2winapi.inc	(revision 54969)
+++ gtk2winapi.inc	(working copy)
@@ -9446,20 +9446,20 @@
       begin
         if not GTK_WIDGET_VISIBLE(PGtkWidget(GtkWindow)) then
           gtk_widget_show(PGtkWidget(GtkWindow));
-        gtk_window_deiconify(GtkWindow);
-        // Workaround for gtk2 bug with some window managers:
-        // calling gtk_window_unmaximize when the window is not maximized
-        // will cause window to shrink.  Here we check if the window we are
-        // resizing is a form and use its WindowState property to make sure
-        // it is maximized before trying to unmaximize it which avoids the
-        // issue (custom code that uses the Win32 API will still have the
-        // problem though, but gtk2 does not offer a is_maximized method)
+
         // see https://bugs.freepascal.org/view.php?id=31832
-        Control:=TWinControl(GetLCLObject({%H-}Pointer(hWnd)));
-        if (not (Control is TCustomForm)) or
-            (TCustomForm(Control).WindowState=wsMaximized) then
-          gtk_window_unmaximize(GtkWindow);
-        gtk_window_unfullscreen(GtkWindow);
+        if GDK_IS_WINDOW(PGtkWidget(GtkWindow)^.window) and
+          (gdk_window_get_window_type(PGtkWidget(GtkWindow)^.window) <> GDK_WINDOW_CHILD) then
+        begin
+          if gdk_window_get_state(PGtkWidget(GtkWindow)^.window) = GDK_WINDOW_STATE_ICONIFIED then
+            gtk_window_deiconify(GtkWindow)
+          else
+          if gdk_window_get_state(PGtkWidget(GtkWindow)^.window) = GDK_WINDOW_STATE_MAXIMIZED then
+            gtk_window_unmaximize(GtkWindow)
+          else
+          if gdk_window_get_state(PGtkWidget(GtkWindow)^.window) = GDK_WINDOW_STATE_FULLSCREEN then
+            gtk_window_unfullscreen(GtkWindow);
+        end;
       end;
     end;
 
@@ -9475,9 +9475,17 @@
       gtk_widget_show(PGtkWidget(GtkWindow))
     else
     begin
-      gtk_window_deiconify(GtkWindow);
-      gtk_window_unfullscreen(GtkWindow);
-      gtk_window_maximize(GtkWindow);
+      // see https://bugs.freepascal.org/view.php?id=31832
+      if GDK_IS_WINDOW(PGtkWidget(GtkWindow)^.window) and
+        (gdk_window_get_window_type(PGtkWidget(GtkWindow)^.window) <> GDK_WINDOW_CHILD) then
+      begin
+        if gdk_window_get_state(PGtkWidget(GtkWindow)^.window) = GDK_WINDOW_STATE_ICONIFIED then
+          gtk_window_deiconify(GtkWindow)
+        else
+        if gdk_window_get_state(PGtkWidget(GtkWindow)^.window) = GDK_WINDOW_STATE_FULLSCREEN then
+          gtk_window_unfullscreen(GtkWindow);
+        gtk_window_maximize(GtkWindow);
+      end;
     end;
 
   SW_SHOWFULLSCREEN:
@@ -9488,11 +9496,20 @@
 
   SW_RESTORE:
     begin
-      gtk_window_deiconify(GtkWindow);
-      gtk_window_unmaximize(GtkWindow);
-      gtk_window_unfullscreen(GtkWindow);
+      // see https://bugs.freepascal.org/view.php?id=31832
+      if GDK_IS_WINDOW(PGtkWidget(GtkWindow)^.window) and
+        (gdk_window_get_window_type(PGtkWidget(GtkWindow)^.window) <> GDK_WINDOW_CHILD) then
+      begin
+        if gdk_window_get_state(PGtkWidget(GtkWindow)^.window) = GDK_WINDOW_STATE_ICONIFIED then
+          gtk_window_deiconify(GtkWindow)
+        else
+        if gdk_window_get_state(PGtkWidget(GtkWindow)^.window) = GDK_WINDOW_STATE_MAXIMIZED then
+          gtk_window_unmaximize(GtkWindow)
+        else
+        if gdk_window_get_state(PGtkWidget(GtkWindow)^.window) = GDK_WINDOW_STATE_FULLSCREEN then
+          gtk_window_unfullscreen(GtkWindow);
+      end;
     end;
-
   end;
 
   Result := True;
issue31832_2.diff (3,622 bytes)   

Zeljan Rikalo

2017-05-18 14:09

developer   ~0100435

Added new patch issue31832_2.diff which checks for level, but also checks other conditions in SW_RESTORE and others.

Zeljan Rikalo

2017-05-18 14:32

developer   ~0100437

Pls, do not apply issue31832_2.diff since gdk_window_get_state() does not return correct results. Must check why it is so. Since gtk2 2.22 there's gtk_window_state() which returns correct results, but we are supporting all since gtk2-2.8 and such statement cannot be used for gtk2 < 2.22

Zeljan Rikalo

2017-05-18 16:07

developer   ~0100441

Ok, found what happens:
gtk_window_maximize(PGtkWindow(MyWindow));
if gdk_window_get_state(PGtkWindow(MyWindow)^.window) and GDK_WINDOW_STATE_MAXIMIZED <> 0 then
  writeln('OK')
else
  writeln('BAD');
gtk_window_unmaximize(PGtkWindow(MyWindow));

1.Note that code above is written from head, so maybe won't compile.
2.Such thing does not work under gtk2 since gdk window flag isn't updated
imediatelly, but after events processing, under qt and win32 this works fine.

My real test was:
with TForm2.Create(Self) do
begin
  Show;
  LCLIntf.ShowWindow(Handle, SW_SHOWMAXIMIZED);
  LCLIntf.ShowWindow(Handle, SW_RESTORE);
end;
So, my patch doesn't work correct in such scenario because gdk_window_get_state() does not return correct value in this case.

Conclusion: Do not close this issue, leave workaround inside for now, this need to be rewritten in another way by using only gtk2/gdk code.

Zeljan Rikalo

2017-05-18 16:08

developer   ~0100442

Let's keep it "resolved" for now, but pls leave bug unclosed

Zeljan Rikalo

2017-05-18 19:30

developer   ~0100451

Please test with r54975. I'll add it to the merge list for 1.8RC2, but will not merge until someone confirm that this bug is fixed with r54975.

Issue History

Date Modified Username Field Change
2017-05-17 23:02 Kostas Michalopoulos New Issue
2017-05-17 23:02 Kostas Michalopoulos File Added: gtk2-window-unmaximize-workaround-for-sw_shownormal-patch.diff
2017-05-17 23:04 Kostas Michalopoulos Tag Attached: component palette
2017-05-17 23:04 Kostas Michalopoulos Tag Attached: designer
2017-05-17 23:04 Kostas Michalopoulos Tag Attached: gtk2
2017-05-17 23:04 Kostas Michalopoulos Tag Attached: lcl
2017-05-17 23:04 Kostas Michalopoulos Tag Attached: widgetset
2017-05-17 23:04 Kostas Michalopoulos File Added: webmshare_q78rb.webm
2017-05-17 23:05 Kostas Michalopoulos Note Added: 0100374
2017-05-17 23:30 CudaText man Note Added: 0100377
2017-05-18 01:12 Mattias Gaertner Fixed in Revision => 54958
2017-05-18 01:12 Mattias Gaertner LazTarget => -
2017-05-18 01:12 Mattias Gaertner Note Added: 0100380
2017-05-18 01:12 Mattias Gaertner Status new => resolved
2017-05-18 01:12 Mattias Gaertner Resolution open => fixed
2017-05-18 01:12 Mattias Gaertner Assigned To => Mattias Gaertner
2017-05-18 13:41 Zeljan Rikalo File Added: issue31832.diff
2017-05-18 13:42 Zeljan Rikalo Note Added: 0100434
2017-05-18 14:08 Zeljan Rikalo File Added: issue31832_2.diff
2017-05-18 14:09 Zeljan Rikalo Note Added: 0100435
2017-05-18 14:32 Zeljan Rikalo Note Added: 0100437
2017-05-18 14:39 Mattias Gaertner Assigned To Mattias Gaertner => Zeljan Rikalo
2017-05-18 14:39 Mattias Gaertner Status resolved => assigned
2017-05-18 16:07 Zeljan Rikalo Note Added: 0100441
2017-05-18 16:08 Zeljan Rikalo Note Added: 0100442
2017-05-18 16:08 Zeljan Rikalo Status assigned => resolved
2017-05-18 19:29 Zeljan Rikalo Status resolved => assigned
2017-05-18 19:29 Zeljan Rikalo Resolution fixed => reopened
2017-05-18 19:30 Zeljan Rikalo Fixed in Revision 54958 => 54958,54975
2017-05-18 19:30 Zeljan Rikalo Note Added: 0100451
2017-05-18 19:30 Zeljan Rikalo Status assigned => resolved
2017-05-18 19:30 Zeljan Rikalo Resolution reopened => fixed