Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

borderWidth incorporated with window size hints (x11 or xmonad?) #198

Open
mneyrane opened this issue Aug 31, 2019 · 12 comments
Open

borderWidth incorporated with window size hints (x11 or xmonad?) #198

mneyrane opened this issue Aug 31, 2019 · 12 comments

Comments

@mneyrane
Copy link

mneyrane commented Aug 31, 2019

Problem Description

When running applications, some floating windows (e.g. dialog or save prompts) are set to have a fixed size. Setting borderWidth to some non-zero value in xmonad.hs means that such windows (including the border) respect the size hint. This results in a "squished" window.

squished window

I used LibreOffice to produce this prompt.

Configuration File

Here is a minimal configuration to reproduce the issue.

import XMonad

main = xmonad def
    { terminal    = "urxvt"
    , modMask     = mod4Mask
    , borderWidth = 2
    }

Additional information

xprop output of save prompt:

WM_STATE(WM_STATE):
                window state: Normal
                icon window: 0x0
_NET_WM_STATE(ATOM) = _NET_WM_STATE_MODAL, _NET_WM_STATE_SKIP_TASKBAR
WM_HINTS(WM_HINTS):
                Client accepts input or input focus: True
                Initial state is Normal State.
                window id # of group leader: 0x3200001
_GTK_THEME_VARIANT(UTF8_STRING) = 
XdndAware(ATOM) = BITMAP
_NET_WM_OPAQUE_REGION(CARDINAL) = 0, 0, 456, 98
WM_TRANSIENT_FOR(WINDOW): window id # 0x3200024
_NET_WM_WINDOW_TYPE(ATOM) = _NET_WM_WINDOW_TYPE_DIALOG
_NET_WM_SYNC_REQUEST_COUNTER(CARDINAL) = 52432147, 52432148
_NET_WM_USER_TIME(CARDINAL) = 4389460
_NET_WM_USER_TIME_WINDOW(WINDOW): window id # 0x3200d12
WM_CLIENT_LEADER(WINDOW): window id # 0x3200001
_NET_WM_PID(CARDINAL) = 4150
WM_LOCALE_NAME(STRING) = "en_US.UTF-8"
WM_CLIENT_MACHINE(STRING) = "localhost"
WM_NORMAL_HINTS(WM_SIZE_HINTS):
                program specified location: 0, 0
                program specified minimum size: 460 by 102
                program specified maximum size: 460 by 102
                program specified base size: 0 by 0
                window gravity: NorthWest
WM_PROTOCOLS(ATOM): protocols  WM_DELETE_WINDOW, WM_TAKE_FOCUS, _NET_WM_PING, _NET_WM_SYNC_REQUEST
WM_CLASS(STRING) = "soffice", "Soffice"
WM_ICON_NAME(STRING) = "Save Document?"
_NET_WM_ICON_NAME(UTF8_STRING) = "Save Document?"
WM_NAME(STRING) = "Save Document?"
_NET_WM_NAME(UTF8_STRING) = "Save Document?"

xwininfo output of the save prompt:

xwininfo: Window id: 0x3200d11 "Save Document?"

  Absolute upper-left X:  250
  Absolute upper-left Y:  489
  Relative upper-left X:  250
  Relative upper-left Y:  489
  Width: 456
  Height: 98
  Depth: 24
  Visual: 0x2b
  Visual Class: TrueColor
  Border width: 2
  Class: InputOutput
  Colormap: 0x3200002 (not installed)
  Bit Gravity State: NorthWestGravity
  Window Gravity State: NorthWestGravity
  Backing Store State: NotUseful
  Save Under State: no
  Map State: IsViewable
  Override Redirect State: no
  Corners:  +250+489  -1210+489  -1210-489  +250-489
  -geometry 456x98+250+489

Other

I don't see this as serious, it's more of an aesthetics thing. I apologize in advance if this is specific to X11, I'm not too familiar with the implementation of xmonad or X11. Otherwise, I'm very fond of this tiling window manager.

@liskin
Copy link
Member

liskin commented Sep 2, 2019

This is a bit weird as the code that computes float sizes does take border into consideration:

(fi (wa_width wa + bw*2) % fi (rect_width sr))
(fi (wa_height wa + bw*2) % fi (rect_height sr))

Also, I can't reproduce the issue here with neither soffice nor ssh-askpass, but my xmonad.hs is considerably longer than your minimal example, so I might have something somewhere that works around it, but I'm not aware of it.

@geekosaur
Copy link
Contributor

I'm not in a position to poke at this, but I'd like to remind people that this border is part of the window, not hanging around it as in reparenting window managers. This can cause confusion as to who "owns" it; people tend to expect it to belong to the window manager, but changing the border width reflects back into the client / program which may resize the window in response or etc. (This has been known to cause loops.)

Or put differently, setting the border width nonzero may take away from the drawing area the client expects to have, since the internal border is rarely used these days. It looks like this may be what's happening here, and if the client doesn't expect it then there may not be much to be done except use the manageHook to detect this particular window and resize it upward by twice the border width (see the doFloat* utility functions in XMonad.Hooks.ManageHelpers) so the client still gets the drawing area it expects.

@geekosaur
Copy link
Contributor

There's also at least two places that need to deal with float window sizes, and this causes problems sometimes. Floating dialogs tend to be withdrawn and represented later, rather than being rebuilt each time they're needed, and because we store relative instead of absolute sizes (RationalRect, so we can adapt to multiple screen sizes) we recalculate everything when it's remapped… and this sometimes goes wrong. I found at least one case where windows can either grow or shrink by the border width because it's getting modified on every mapWindow instead of only the once (because the border is fixed pixels, not scaled as the RationalRect expects; but we at least used to compute the RationalRect from the size with the border).

The whole thing probably needs to be reviewed and we need to make sure the right sizes are used in the right places. And if the above is still true, it needs to be fixed; but my recollection was it was hard to fix because we don't actually store the size, the withdrawn window still has its size and border and that's why it keeps growing as we reapply the appropriate-for-screen border each time while ignoring the existing one. We may have to catch the unmapEvent and remove and resize the border away again.

Note that the above means you can't reproduce this at all with only one monitor, and possibly not if all monitors are the same size, because it'll always compute the same sizes in those cases, IIRC.

@geekosaur
Copy link
Contributor

I should also note that soffice dialogs in general behave much better with EwmhDesktops enabled; the minimal config is more or less begging for problems.

Personally, I think we're well past the point where EwmhDesktops needs to be merged to core xmonad and made part of the default config; too many programs simply don't work right without it any more. We'll need to provide a minimalConfig without it to simplify backward compatibility for people who don't want it.

@mneyrane
Copy link
Author

mneyrane commented Sep 6, 2019

@geekosaur Thanks for the input. Also perhaps slightly off topic, how do soffice dialogs behave better with EwmhDesktops?

I've started using this module in my actual xmonad config not too long ago, though I'm not sure I've noticed a difference in this regard.

@geekosaur
Copy link
Contributor

I mentioned loops earlier? soffice was one of the main culprits there, absent EwmhDesktops it would resize the dialog after the border was added, then xmonad would resize it back. I don't think we ever figured out why, and it might have been a bug they fixed later. And this might be a side effect of "fixing" it, even.

This said, modern programs in general really want EwmhDesktops and you'll probably find things run better in general with it unless you stick to stuff like xterm. In particular, most browsers these days behave poorly without EwmhDesktops: no or broken fullscreen, tab dragging problems, etc.

@Zhern
Copy link

Zhern commented Dec 10, 2019

My setup is Arch + LightDM + XMonad (no DE).

I have the same issue with a custom python TK program. I use XMonad.Hooks.EwmhDesktops and basically want to have borders on all floating windows (thus in my xmonad.hs borderWidth = 4), but not non-floating windows (I use noBorders on every one of my layouts).

I can reproduce it with a python TK app, which basically does:

width = 300
height = 300
root = tk.Tk()
root.minsize(width, height)
root.maxsize(width, height)
... put stuff here ...
root.mainloop()

xprop outputs this:

_NET_WM_WINDOW_OPACITY(CARDINAL) = 2576980377
WM_STATE(WM_STATE):
        window state: Normal
        icon window: 0x0
_NET_WM_STATE(ATOM) =
WM_NORMAL_HINTS(WM_SIZE_HINTS):
        program specified minimum size: 300 by 300
        program specified maximum size: 300 by 300
WM_PROTOCOLS(ATOM): protocols  WM_DELETE_WINDOW
WM_HINTS(WM_HINTS):
        Client accepts input or input focus: True
        Initial state is Normal State.
_NET_WM_NAME(UTF8_STRING) = "tk"
WM_NAME(STRING) = "tk"
WM_CLASS(STRING) = "tk", "Tk"

and xwininfo outputs this:

xwininfo: Window id: 0x3000011 "tk"

  Absolute upper-left X:  0
  Absolute upper-left Y:  0
  Relative upper-left X:  0
  Relative upper-left Y:  0
  Width: 292
  Height: 292
  Depth: 24
  Visual: 0x21
  Visual Class: TrueColor
  Border width: 4
  Class: InputOutput
  Colormap: 0x20 (installed)
  Bit Gravity State: NorthWestGravity
  Window Gravity State: NorthWestGravity
  Backing Store State: NotUseful
  Save Under State: no
  Map State: IsViewable
  Override Redirect State: no
  Corners:  +0+0  -3140+0  -3140-1140  +0-1140
  -geometry 292x292+0+0

In the end, the whole window is 300x300, but the border is indeed inside the given size instead of outside (the content is 292x292 instead of 300x300, and the whole window is 300x300 instead of 308x308 with border = 4).

I tried with the @liskin 's xmonad public dotfile and I get the same behavior.

@geekosaur
Copy link
Contributor

xmonad uses the window's internal border (see bw in the X(7) manpage), not an external border. This would be difficult to change, since it would require every window to be wrapped in a frame window and then much code would need to be changed to be aware of the frame; X11 has no intrinsic way to attach an external border to a window.

@Zhern
Copy link

Zhern commented Dec 11, 2019

Without going to such complex rework, according to the liskin's answer, shouldn't the floating window size be already increased by twice the border width to workaround this internal-border-restriction?

@geekosaur
Copy link
Contributor

Looking back over this, a complication here is that when the window is initially sized, the noBorders is active and no space is reserved for the border (it's not recomputed after changing the borderWidth unless you do so manually in the manageHook). You may need to phrase things backwards, removing borders from the tiled windows instead of adding them to the floats.

@Zhern
Copy link

Zhern commented Dec 16, 2019

I think this is already what I did :

main = do
    toggleFadeSet <- newIORef S.empty
    xmonad $ myConfig toggleFadeSet

myConfig toggleFadeSet = ewmh def
    { borderWidth = 4                               -- default border = 4, this is disabled for layout-ed windows but kept for floating windows
    , layoutHook = myLayoutHook                     -- custom layout
    , ...

myLayoutHook = addToggle $ F.fullscreenFocus $ noFrillsDeco shrinkText myTheme $ three ||| binary ||| mirrorTiled  -- toggle fullscreen for all + apply theme on full width title + 3 layouts
    where addToggle = toggleLayouts (full)                                  -- add enable toggle full screen
          full = noSpacing $ noBorders Full                                 -- Fullscreen = no space + no border
          tiled = withSpacing $ noBorders $ Tall nmaster delta ratio        -- Main half left = space + no border
          mirrorTiled = Mirror tiled                                        -- Main half top
          binary = withSpacing $ noBorders $ emptyBSP                       -- Binary tree = space + no border
          three = withSpacing $ noBorders $ ThreeColMid nmaster delta ratio -- Main center = space + no border
          nmaster = 1
          delta = 3/100
          ratio = 1/2

I did some testing, and it appears that if I remove the line adjust r = r in src/XMonad/Operations.hs at line 65, then the fixed-sized and transient floating windows are (in my sense) correctly sized.
I neither really understand the purpose of this line (why the identity function here when a whole definition comes right before?), not did find any undesired side effect of this modification. However I think you are more relevant than me on this topic to tell. :)

@wygulmage
Copy link
Contributor

wygulmage commented Dec 30, 2019

I did some testing, and it appears that if I remove the line adjust r = r in src/XMonad/Operations.hs at line 65, then the fixed-sized and transient floating windows are (in my sense) correctly sized.
I neither really understand the purpose of this line (why the identity function here when a whole definition comes right before?), not did find any undesired side effect of this modification. However I think you are more relevant than me on this topic to tell. :)

The relevant code:

  adjust (W.RationalRect x y w h)
        | x + w > 1 || y + h > 1 || x < 0 || y < 0
        = W.RationalRect (0.5 - w/2) (0.5 - h/2) w h
  adjust r = r

In the source it's confusingly formatted and written, but the first part conditionally resizes the rectangle and the second leaves it unchanged. It could be written differently:

  adjust r@(W.RationalRect x y w h) =
      if x + w > 1 || y + h > 1 || x < 0 || y < 0
      then W.RationalRect (0.5 - w/2) (0.5 - h/2) w h
      else r

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants