Re: [dev] [dwm] fix status bar cropping on screen resize

From: Mark Williams <markrwilliams_AT_gmail.com>
Date: Mon, 28 Feb 2011 16:07:08 -0800

On Sun, Feb 27, 2011 at 12:15:49PM +0100, Szabolcs Nagy wrote:
> > 2) The bar gets cut off because configurenotify() expects updategeom()
> > to return true only if the ConfigureEvent reports a change to the root
> > window's dimensions. While updategeom() does this correctly when
> i don't quite understand why the reported width is not the
> full display width there

Sorry. Here's what I hope is a clearer explanation:

dwm checks for resolution changes by passing ConfigureNotify events
through configurenotify, which checks to see if the event is
associated with the root window (not 'ConfigureEvent', which doesn't
even exist. Whoops!). If it is, the function sets sw and sh (its
display resolution variables) to the event's width and height and then
runs updategeom to check if the display resolution has actually
changed. updategeom returns True if it has, and then configurenotify
recreates the bar pixmap with sw as its width and redraws the bar (and
everything else). If the resolution's the same, sw and sh remain
updated but nothing else happens.

It turns out that several root window ConfigureNotify events are
generated when a resolution change occurs, but when using the older
RandR calls, some of the first ones occur before the root window is
resized. The non-Xinerama code responds correctly to this because it
checks to see if the new sw and sh values are different than the width
and height stored in mons; if they are, then it knows a resolution
change has occurred since this is the only place where mons->mw/ww and
->mh/wh are set. These values are updated, updategeom is made to
return True (lines 1851-7), and things proceed as described above.
Since the other ConfigureNotify events are ignored, the pixmap is
redrawn only when sw has been set to the newer horizontal resolution.

The Xinerama code in updategeom, however, doesn't check sw and sh when
it decides whether or not the resolution's changed (lines 1812-37).
Because the information it gets from Xinerama reflects the new screen
resolution, it has updategeom return True even when processing an
early ConfigureNotify event that occurred before the root window got
resized, and which therefore resulted in sw and sh being set to the
the previous resolution. This is mostly OK; since the Xinerama
information is accurate the members in the monitor struct(s) are set
to the correct values, and since the last ConfigureNotify event
generated has the new, accurate root window dimensions, sw and sh are
also set to correct values before they're used in applysizehints or
whereever.

However, sw has not been set to the correct value before XCreatePixmap
is called in configurenotify. When going from a smaller resolution to
a larger one, this means that the bar pixmap gets recreated with the
smaller resolution's width. The ConfigureNotify events that occur
after the root window has been resized do not cause the pixmap to be
recreated correctly because by then the Xinerama code has updated the
values it checks and thus does not detect any change.

In short: the Xinerama code does not keep track of sw the way the
non-Xinerama code does and the way the XCreatePixmap call in
configurenotify expects it to. This is a problem when the root window
generates a ConfigureNotify event before it's been resized, as occurs
when resizing the screen with the RandR < 1.2 calls. Again, this is
definitely an edge case!

One solution is to have the Xinerama code update sw and sh to the
right dimensions when it detects a change. I chose instead to compare
them to the sums of the widths and heights stored in the mons list
because a) this is closer to what the non-Xinerama code does and b)
looping through the mons list and adding everything up seemed less
expensive than the Xinerama checks.

> > 3) Interestingly, the current code and the previous solution create a
> > dc.drawable pixmap that's as wide as the aggregate horizontal
> > resolution across all monitors. That makes sense for the non-Xinerama
> > code, where the bar stretches from monitor to monitor, but it doesn't
> > when each has its own bar. In the latter case the pixmap only has to
> > be as wide as the largest monitor's horizontal resolution. The second
> this seems reasonable
>
> i'd use a max-monitor-width global instead of dc.dw
> (like sw and sh it's a display geometry related var,
> not a dc internal)
> imho updategeom shouldn't modify dc or if it does
> then do all the work there (createpixmap, updatebars)

I originally thought to use a global too, but when I couldn't think of
a use for the value aside from creating the bar pixmap, I stuck it in
the DC struct. To my mind it's not any different than updating the
various members of the Monitor structs, so modifying it in updategeom
didn't seem so bad. I'll be happy to rewrite the patch to use
max-monitor-width if I'm wrong.

Also, maybe it's a good idea to combine the sw and sh check from patch
one with the pixmap width adjustment from patch two?

> > Even though this happens because of other people's weak code, I think
> > 3 (or 2) is the best solution since dwm itself seems to be making some
> > incorrect assumptions; however, if the BUGS entry is the right
> > solution I'll be happy to write the explanation.
> thanks for looking into this

Thanks for such great software!
Received on Tue Mar 01 2011 - 01:07:08 CET

This archive was generated by hypermail 2.2.0 : Tue Mar 01 2011 - 01:12:03 CET