]> git.eshelyaron.com Git - emacs.git/commitdiff
"Fix" iconification handling on PGTK
authorPo Lu <luangruo@yahoo.com>
Wed, 9 Nov 2022 12:41:17 +0000 (20:41 +0800)
committerPo Lu <luangruo@yahoo.com>
Wed, 9 Nov 2022 13:05:24 +0000 (21:05 +0800)
* src/pgtkterm.c (pgtk_iconify_frame): Write some more comments
and refrain from setting the frame as iconified here.
(map_event): Remove redundant braces.
(window_state_event): Remove useless code and manage frame
visibility as correctly as possible under Wayland while
iconified.  (bug#55836)

src/pgtkterm.c

index 491ba338821792851f86df7b75146a24118b2b17..3350676e3c6d1aafbef023d16510b98ea2b42afa 100644 (file)
@@ -714,40 +714,42 @@ pgtk_set_window_size (struct frame *f, bool change_gravity,
 
 void
 pgtk_iconify_frame (struct frame *f)
-/* --------------------------------------------------------------------------
-     External: Iconify window
-   -------------------------------------------------------------------------- */
 {
+  GtkWindow *window;
+
   /* Don't keep the highlight on an invisible frame.  */
+
   if (FRAME_DISPLAY_INFO (f)->highlight_frame == f)
-    FRAME_DISPLAY_INFO (f)->highlight_frame = 0;
+    FRAME_DISPLAY_INFO (f)->highlight_frame = NULL;
+
+  /* If the frame is already iconified, return.  */
 
   if (FRAME_ICONIFIED_P (f))
     return;
 
-  block_input ();
+  /* Child frames on PGTK have no outer widgets.  In that case, simply
+     refuse to iconify the frame.  */
 
   if (FRAME_GTK_OUTER_WIDGET (f))
     {
       if (!FRAME_VISIBLE_P (f))
        gtk_widget_show_all (FRAME_GTK_OUTER_WIDGET (f));
 
-      gtk_window_iconify (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
-      SET_FRAME_VISIBLE (f, 0);
-      SET_FRAME_ICONIFIED (f, true);
-      unblock_input ();
-      return;
-    }
+      window = GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f));
 
-  /* Make sure the X server knows where the window should be positioned,
-     in case the user deiconifies with the window manager.  */
-  if (!FRAME_VISIBLE_P (f) && !FRAME_ICONIFIED_P (f))
-    pgtk_set_offset (f, f->left_pos, f->top_pos, 0);
+      gtk_window_iconify (window);
 
-  SET_FRAME_ICONIFIED (f, true);
-  SET_FRAME_VISIBLE (f, 0);
+      /* Don't make the frame iconified here.  Doing so will cause it
+        to be skipped by redisplay, until GDK says it is deiconified
+        (see window_state_event for more details).  However, if the
+        window server rejects the iconification request, GDK will
+        never tell Emacs about the iconification not happening,
+        leading to the frame not being redisplayed until the next
+        window state change.  */
 
-  unblock_input ();
+      /* SET_FRAME_VISIBLE (f, 0);
+        SET_FRAME_ICONIFIED (f, true); */
+    }
 }
 
 static gboolean
@@ -5420,9 +5422,7 @@ map_event (GtkWidget *widget,
       /* Check if fullscreen was specified before we where mapped the
          first time, i.e. from the command line.  */
       if (!FRAME_X_OUTPUT (f)->has_been_visible)
-       {
-         set_fullscreen_state (f);
-       }
+       set_fullscreen_state (f);
 
       if (!iconified)
        {
@@ -5465,24 +5465,6 @@ window_state_event (GtkWidget *widget,
   inev.ie.kind = NO_EVENT;
   inev.ie.arg = Qnil;
 
-  if (f)
-    {
-      if (new_state & GDK_WINDOW_STATE_FOCUSED)
-       {
-         if (FRAME_ICONIFIED_P (f))
-           {
-             /* Gnome shell does not iconify us when C-z is pressed.
-                It hides the frame.  So if our state says we aren't
-                hidden anymore, treat it as deiconified.  */
-             SET_FRAME_VISIBLE (f, 1);
-             SET_FRAME_ICONIFIED (f, false);
-             FRAME_X_OUTPUT (f)->has_been_visible = true;
-             inev.ie.kind = DEICONIFY_EVENT;
-             XSETFRAME (inev.ie.frame_or_window, f);
-           }
-       }
-    }
-
   if (new_state & GDK_WINDOW_STATE_FULLSCREEN)
     store_frame_param (f, Qfullscreen, Qfullboth);
   else if (new_state & GDK_WINDOW_STATE_MAXIMIZED)
@@ -5500,14 +5482,37 @@ window_state_event (GtkWidget *widget,
   else
     store_frame_param (f, Qfullscreen, Qnil);
 
+  /* The Wayland protocol provides no way for the client to know
+     whether or not one of its toplevels has actually been
+     deiconified.  It only provides a request for clients to iconify a
+     toplevel, without even the ability to determine whether or not
+     the iconification request was rejected by the display server.
+
+     GDK computes the iconified state by sending a window state event
+     containing only GDK_WINDOW_STATE_ICONIFIED immediately after
+     gtk_window_iconify is called.  That is error-prone if the request
+     to iconify the frame was rejected by the display server, but is
+     not the main problem here, as Wayland compositors only rarely
+     reject such requests.  GDK also assumes that it can clear the
+     iconified state upon receiving the next toplevel configure event
+     from the display server.  Unfortunately, such events can be sent
+     by Wayland compositors while the frame is iconified, and may also
+     not be sent upon deiconification.  So, no matter what Emacs does,
+     the iconification state of a frame is likely to be wrong under
+     one situation or another.  */
+
   if (new_state & GDK_WINDOW_STATE_ICONIFIED)
-    SET_FRAME_ICONIFIED (f, true);
+    {
+      SET_FRAME_ICONIFIED (f, true);
+      SET_FRAME_VISIBLE (f, false);
+    }
   else
     {
       FRAME_X_OUTPUT (f)->has_been_visible = true;
       inev.ie.kind = DEICONIFY_EVENT;
       XSETFRAME (inev.ie.frame_or_window, f);
       SET_FRAME_ICONIFIED (f, false);
+      SET_FRAME_VISIBLE (f, true);
     }
 
   if (new_state & GDK_WINDOW_STATE_STICKY)