After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 636904 - shadows not painting properly when losing focus to the shell
shadows not painting properly when losing focus to the shell
Status: RESOLVED FIXED
Product: mutter
Classification: Core
Component: general
git master
Other Linux
: Normal normal
: ---
Assigned To: mutter-maint
mutter-maint
: 644846 644848 (view as bug list)
Depends on:
Blocks:
 
 
Reported: 2010-12-09 19:47 UTC by William Jon McCann
Modified: 2011-03-28 20:01 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
screenshot (404.18 KB, image/png)
2010-12-09 19:47 UTC, William Jon McCann
  Details
window: add an appears-focused property, redraw shadows when it changes (7.69 KB, patch)
2011-03-25 19:24 UTC, Dan Winship
needs-work Details | Review
window: add an appears-focused property, redraw shadows when it changes (9.15 KB, patch)
2011-03-26 17:09 UTC, Dan Winship
needs-work Details | Review
test case changing the transient_for hint on the fly (261 bytes, text/plain)
2011-03-28 14:45 UTC, Owen Taylor
  Details
Test case with a focus chain loop (409 bytes, text/plain)
2011-03-28 14:53 UTC, Owen Taylor
  Details
Corrected version of the transient-for loop test case (409 bytes, text/plain)
2011-03-28 15:09 UTC, Owen Taylor
  Details
window: add an appears-focused property, redraw shadows when it changes (10.03 KB, patch)
2011-03-28 16:14 UTC, Dan Winship
committed Details | Review

Description William Jon McCann 2010-12-09 19:47:35 UTC
Created attachment 176151 [details]
screenshot

1. Start gnome-shell
2. Run test-markup from libnotify (or find another way to get a message source in the message tray)
3. Position a non-maximized window so that you can see the shadows (against a light background)
4. Ensure non-maximized window is focused
5. Move the mouse pointer into the summary area of the message tray
6. Click on an icon (not a status icon)
7. observe the change in the window shadows
Comment 1 Dan Winship 2011-03-01 15:40:03 UTC
Yeah, the MetaWindowActor needs to queue_redraw() on itself when meta_window_appears_focused(priv->window) changes.
Comment 2 Dan Winship 2011-03-15 18:35:10 UTC
*** Bug 644846 has been marked as a duplicate of this bug. ***
Comment 3 Dan Winship 2011-03-15 18:36:00 UTC
*** Bug 644848 has been marked as a duplicate of this bug. ***
Comment 4 Dan Winship 2011-03-25 19:24:27 UTC
Created attachment 184208 [details] [review]
window: add an appears-focused property, redraw shadows when it changes

We need to redraw a window's shadow any time the value of
meta_window_appears_focused() changes. So make that into a property so
we can get notifications on it.

====

Not necessarily for 3.0.0, just something I decided to fix while bugzilla
was down, because it annoyed me.

sync_focus_appearance() is perhaps overly cautious; it seems like only
one window should be able to have has_focus set at any given time (or at
least, any situation where multiple windows have has_focus set is very
transient and will probably disappear before the next redraw), and it
could be optimized a bit if that's true. But I wasn't sure if there was
some scenario where a window and its transient could both be considered
has_focus...
Comment 5 Owen Taylor 2011-03-26 05:24:08 UTC
Review of attachment 184208 [details] [review]:

Ah - didn't twig onto the fact that a change in focus would cause the frame to be redrawn, but that would go through XDamage and be a *clipped* redraw and not redraw the shadow. Now that you've pointed this out with clicking on notifications, it's going to annoy the heck out of me, at least in the rare case where I have non-maximized windows. Also easily reproducible with a window always on top - the reason it normally doesn't show up is that restacking causes a total repaint of the MetaWindowGroup.

Hmm, does this work right if a focusd dialog is unmanaged? I guess the X server reliably sends a focus out before unmapping or destroying a window, which are the only cases where we unmanage, so no real need to worry about that.

What it doesn't handle is the case where the transient_for hint is changed on the fly. A corner cases, but probably makes sense to handle rather than leaving for someone to debug late for some app which is tardy in setting the hint.

Undecided if this is 3.0.0 or 3.0.1 at this point - my feeling is that it's more like 3.0.0 - it's the kind of thing that catches the eye of someone testing the message tray and breaks the illusion, but see what you think is important in the issues I've pointed. If we can't converge pretty quickly on what's "good enough" then it will definitely be 3.0.1.

::: src/core/window.c
@@ +6394,3 @@
+  child = window;
+  parent = meta_window_get_transient_for (child);
+  while (child->type == META_WINDOW_MODAL_DIALOG && parent && parent != child)

Not a new problem but the parent != child doesn't keep us out of infinite loops. Add a tortoise variable updated every other loop cycle? I don't *obviously* see other places in Mutter that are vulnerable to infinite transient for loops, assuming that the stacking and placement constraint code is sound against contradictory constraints.

@@ +6396,3 @@
+  while (child->type == META_WINDOW_MODAL_DIALOG && parent && parent != child)
+    {
+      gboolean parent_new_attached_focus = child->has_focus || child->has_attached_focus;

Doesn't deal with parents with multiple attached children - really need an attached focus count on the parent rather than a boolean. (If we care.. the two attached children will also be stacked over each other in a completely messy ugly fashion. Fine to just document not caring.)

@@ +6398,3 @@
+      gboolean parent_new_attached_focus = child->has_focus || child->has_attached_focus;
+
+      if (parent->has_attached_focus == parent_new_attached_focus)

Hmm, more accurate to break on:

 (parent->has_attached_focus && parent->has_focus) == (parent_new_attached_focus && parent->has_focus)

though you still have to update parent->has_attached_focus in that case. So, like

 if (parent->has_focus) {
    parent->has_attached_focus = parent_new_attached_focus;
    break;
 }

and then skip the later check.
Comment 6 Dan Winship 2011-03-26 17:09:45 UTC
Created attachment 184303 [details] [review]
window: add an appears-focused property, redraw shadows when it changes

We need to redraw a window's shadow any time the value of
meta_window_appears_focused() changes. So make that into a property so
we can get notifications on it.
Comment 7 Dan Winship 2011-03-26 17:19:45 UTC
(In reply to comment #5)
> Not a new problem but the parent != child doesn't keep us out of infinite
> loops.

> Doesn't deal with parents with multiple attached children

OK, new version should fix both bugs by keeping track of the specific focused descendant window that is causing us to be appears-focus.

    - When an attached child gains focus, it sets each
      parent->attached_focus_window to the focus window, until it reaches
      the top or finds a window that already has the right
      attached_focus_window, which will break any loops

    - When the attached child loses focus, it sets each
      parent->attached_focus_window to NULL, until it reaches the top
      of finds a window that didn't have this window as its
      attached_focus_window

In both cases, the second break condition will trigger if it sees the value it wrote before, so we can't get caught in a loop.

In the multiple-attached-children case, even if we get the focus-in before the focus-out, it still works, because the focus-in will overwrite parent->attached_focus_window to point to the new child, but the focus-out won't overwrite it with NULL, because it no longer matches the right child.
Comment 8 Owen Taylor 2011-03-28 14:45:14 UTC
Created attachment 184459 [details]
test case changing the transient_for hint on the fly

This test case shows that since we propagate focus appearance after updating the transient for hint, we can leave a parent dialog "stuck" with attached_modal_dialog set.
Comment 9 Owen Taylor 2011-03-28 14:51:15 UTC
Though I also get stuck focus appearance when I simply close an attached modal dialog - say like the gedit file save dialog.
Comment 10 Owen Taylor 2011-03-28 14:53:35 UTC
Created attachment 184462 [details]
Test case with a focus chain loop

Here's a test case with a loop of three attached modal dialogs - some things are definitely related to constraints/positioning, but no hang, which is the important thing.
Comment 11 Owen Taylor 2011-03-28 14:59:02 UTC
Review of attachment 184303 [details] [review]:

This feels like a robust approach to me, and the only bug I saw in the code was that meta_window_propagate_focus_appearance() is called from window-props.c after xtransient_for is already changed. But there seems to be another bug where closing an attached modal dialog leaves the parent with a stuck attached_focus_window, which isn't obvious to me without debugging.
Comment 12 Owen Taylor 2011-03-28 15:09:49 UTC
Created attachment 184463 [details]
Corrected version of the transient-for loop test case

Last version of the transient-for-loop test case didn't actually work - though it seems like a GTK+ or Mutter bug that it didn't:

w1 = gtk.Window()
w1.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
w1.set_modal(True)
w1.show_all()

w2 = gtk.Window()
w2.show_all()
w1.set_transient_for (w2)

Produces:

Invalid WM_TRANSIENT_FOR window 0x2800020 specified for 0x2800003 (transient.).

Changing the second show_all() to a show_now() [so wait until we get a MapEvent back from the window manager] makes it work. Here's the focus loop corrected, and it crashes mutter with your patch applied. Haven't checked yet if it crashes in the focus appearance code or not.
Comment 13 Owen Taylor 2011-03-28 15:14:59 UTC
(In reply to comment #12)
> Changing the second show_all() to a show_now() [so wait until we get a MapEvent
> back from the window manager] makes it work. Here's the focus loop corrected,
> and it crashes mutter with your patch applied. Haven't checked yet if it
> crashes in the focus appearance code or not.

Infinite recursion through:

 meta_window_move
 meta_window_move_resize_internal
 move_attached_dialog
 meta_window_move
 [...]

So unrelated to this patch and this bug.
Comment 14 Dan Winship 2011-03-28 16:14:57 UTC
Created attachment 184475 [details] [review]
window: add an appears-focused property, redraw shadows when it changes

- add a flag to propagate_focus_appearance() so we can control
    whether it propagate "focus" or "unfocus"

  - call propagate_focus_appearance() both at the start and the end of
    reload_transient_for(), to remove focus appearance from previous
    parent (if any), and add it to the new parent (if any)

  - call propagate_focus_appearance() from meta_window_unmanage(),
    because even if the window does get a FocusOut on destroy, we
    won't be paying attention to it any more at that point.
Comment 15 Owen Taylor 2011-03-28 16:43:07 UTC
Review of attachment 184475 [details] [review]:

Good for me pending release-team approval
http://mail.gnome.org/archives/release-team/2011-March/msg00475.html
Comment 16 Owen Taylor 2011-03-28 19:29:42 UTC
Review of attachment 184475 [details] [review]:

Has release team sign-off 

http://mail.gnome.org/archives/release-team/2011-March/msg00475.html
Comment 17 Dan Winship 2011-03-28 20:01:01 UTC
Attachment 184475 [details] pushed as f464b85 - window: add an appears-focused property, redraw shadows when it changes