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

Add option to create borders externally #81

Closed
2 tasks done
incertia opened this issue Feb 15, 2017 · 9 comments
Closed
2 tasks done

Add option to create borders externally #81

incertia opened this issue Feb 15, 2017 · 9 comments

Comments

@incertia
Copy link

incertia commented Feb 15, 2017

Problem Description

Borders are created internally, so if borderWidth = 2, a call to xcb_create_window() with width=1280 and height=720 and borderwidth = 0 ends up creating a client area measuring 1276 x 716. For graphics applications that require a fixed window size, this can be particularly annoying if you have to wait for the window to become exposed to resize the window.

An .xinitrc file consisting of precisely

#! /bin/bash
exec xterm

can create borders that go outward like so, confirmed via xcb_get_geometry having the correct client area.

Configuration File

module Main (main) where

import XMonad

main :: IO ()
main = xmonad def

Checklist

@pjones
Copy link
Contributor

pjones commented Mar 14, 2017

@incertia You seem to know way more about this than I do. Any chance you want to submit a patch?

@erthalion
Copy link
Member

@pjones @incertia if you don't mind I'll try to tackle this question.

@pjones
Copy link
Contributor

pjones commented Apr 14, 2017

@erthalion Got for it!

@erthalion
Copy link
Member

erthalion commented Apr 23, 2017

An .xinitrc file consisting of precisely ... can create borders that go outward like so

Well, I'm really confused by this statement. I couldn't find any mention of
external/outside borders in libx11 source code, xcb_get_geometry_reply_t
(a structure related to the xcb_get_geometry) also doesn't contain any
information, that may help to detect whether borders are external or internal.

I couldn't also reproduce a situation when borders are outside of a window
using provided .xinitrc and the xmonad config. So it would be nice if
@incertia will explain this point in more details.

For graphics applications that require a fixed window size

So, at this point I see following options:

  • I'm wrong and it's possible to create borders outside of a window, so we can
    use this feature.

  • "Emulate" external borders by creating a window with dimensions window_width + 2 * border_width x window_heigth + 2 * border_width. But it means some overhead
    for the operations with window sizes, because it's necessary to calculate
    it since we're pretending that dimensions are still window_width x window_heigth.

  • It really makes sense only for graphics applications with fixes window size
    (I assume it's mostly about games), so it would be great to be able to
    differentiate those apps and just remove borders for them (but I almost sure
    that it's impossible).

I think it also worth to mention that I see similar problems when I toggle
panels (e.g. xmobar).

@incertia
Copy link
Author

incertia commented Dec 26, 2019

Super late reply, but here's another attempt at explaining what I'm trying to get at:

Other window managers/desktop sessions will draw window decorators around the client area. Specifically, in the case of cinnamon, when a window is displayed and then unmapped/destroyed, I check its geometry with xcb_get_geometry and it reports 1600 x 900 with border=0. When I run the same application within xmonad it reports 1596 x 896 with border=2. Under the basic xterm xinitrc, it reports as 1600 x 900 with border=0. Creating the X11 window with the border width set to 2 yields client areas of size 1600 x 900 in all 3 sessions.

My xmonad config is mostly just

 defaults xmb_input xmb_notif = defaultConfig
      { modMask               = mod4Mask
      , manageHook            = myManageHook <+> manageDocks
      , layoutHook            = avoidStruts $ myLayoutHook
      , startupHook           = myStartupHook
      , logHook               = myLogHook xmb_input xmb_notif
      , handleEventHook       = docksEventHook
      , terminal              = "urxvtc"
      , normalBorderColor     = "#63c0f5"
      , focusedBorderColor    = "#00d000"
      , borderWidth           = 2 -- <-- the important line here
      , workspaces            = myWorkspaces }

The window creation code is nothing fancy:

  xcb_create_window(app->xc,
                    XCB_COPY_FROM_PARENT,
                    app->xw,
                    screen->root,
                    (screen->width_in_pixels - w) / 2,
                    (screen->height_in_pixels - h) / 2,
                    w, h, // width, height
                    0,  // border
                    XCB_WINDOW_CLASS_INPUT_OUTPUT,
                    screen->root_visual,
                    mask, values);
  xcb_change_property(app->xc, XCB_PROP_MODE_REPLACE, app->xw,
                      app->ewmh->_NET_WM_WINDOW_TYPE, XCB_ATOM_ATOM,
                      32, 1, &app->ewmh->_NET_WM_WINDOW_TYPE_NORMAL);
  memset(&size_hints, 0, sizeof(xcb_size_hints_t));
  memset(&wm_hints, 0, sizeof(xcb_icccm_wm_hints_t));
  xcb_icccm_size_hints_set_base_size(&size_hints, w, h);
  xcb_icccm_size_hints_set_min_size(&size_hints, w, h);
  xcb_icccm_size_hints_set_max_size(&size_hints, w, h);
  xcb_icccm_size_hints_set_win_gravity(&size_hints, XCB_GRAVITY_CENTER);
  xcb_icccm_set_wm_size_hints(app->xc, app->xw, XCB_ATOM_WM_NORMAL_HINTS,
                              &size_hints);
  xcb_icccm_set_wm_transient_for(app->xc, app->xw, screen->root);
  xcb_icccm_wm_hints_set_normal(&wm_hints);
  xcb_icccm_set_wm_hints(app->xc, app->xw, &wm_hints);

and we report geometry during the event loop as follows:

          case XCB_INPUT_RAW_KEY_RELEASE:
            keysym = get_keysym(app, ev.kr->detail, mods);
            if (keysym == XK_Escape) {
              printf("escape released\n");
              ggc = xcb_get_geometry(app->xc, app->xw);
              ggr = xcb_get_geometry_reply(app->xc, ggc, NULL);
              printf("window size: %u x %u\n", ggr->width, ggr->height);
              printf("border size: %u\n", ggr->border_width);
              xcb_destroy_window(app->xc, app->xw);
              xcb_flush(app->xc);
            }
            break;

tl;dr it seems to me that the default behavior of xmonad borders is to grow into the client area whereas the default behavior from other window managers/desktop sessions do not touch the client area and grow outwards around the client area.

EDIT: this could be a problem inherent to XSetWindowBorderWidth in which case I'm not too sure what the default option should be.

EDIT (again): I have updated my program to use an additional call to xcb_configure_window to set the border width to 2 after initially setting it to 0. xmonad reports 1596 x 896, 2, cinnamon reports 1600 x 900, 0, and xterm reports 1600 x 900, 2. Note that cinnamon reports 0 border width for every configuration.

EDIT (yet again): I took a look at floatLocation and the code seems correct to me, so I am completely clueless as to why XMonad gets a smaller client area.

@aij
Copy link

aij commented Dec 26, 2019

I think "external" borders are usually what you get with reparenting window managers. Rather than drawing outside the window, a new window is created for the border and WM controls, and the client window is embedded inside that.

@incertia
Copy link
Author

incertia commented Dec 26, 2019

Aha, I think I tracked down the issue. xmonad is the only window manager which issues a resize request to my application, taking off the border width and resizing it to 1596 x 896. Does it make sense to only do this for tiled windows instead of all visible windows?

@incertia
Copy link
Author

incertia commented Dec 26, 2019

Here's a hack I put together to theoretically address this issue, I'm not sure if there are any other changes that need to be made. I am also able to make a PR with these changes @pjones @erthalion.

diff --git a/src/XMonad/Operations.hs b/src/XMonad/Operations.hs
index 2845a6e..b25dd07 100644
--- a/src/XMonad/Operations.hs
+++ b/src/XMonad/Operations.hs
@@ -128,7 +128,7 @@ windows f = do
     -- for each workspace, layout the currently visible workspaces
     let allscreens     = W.screens ws
         summed_visible = scanl (++) [] $ map (W.integrate' . W.stack . W.workspace) allscreens
-    rects <- fmap concat $ forM (zip allscreens summed_visible) $ \ (w, vis) -> do
+    rects <- fmap (foldl (\(a, b) (c, d) -> (a ++ c, b ++ d)) ([], [])) $ forM (zip allscreens summed_visible) $ \ (w, vis) -> do
         let wsp   = W.workspace w
             this  = W.view n ws
             n     = W.tag wsp
@@ -151,11 +151,11 @@ windows f = do
 
         io $ restackWindows d (map fst vs)
         -- return the visible windows for this workspace:
-        return vs
+        return (flt, rs)
 
-    let visible = map fst rects
+    let visible = let (floating, tiled) = rects in map fst floating ++ map fst tiled
 
-    mapM_ (uncurry tileWindow) rects
+    mapM_ (uncurry tileWindow) (snd rects)
 
     whenJust (W.peek ws) $ \w -> do
       fbs <- asks (focusedBorderColor . config)

@liskin
Copy link
Member

liskin commented May 7, 2024

I don't think xmonad will become a reparenting window manager any time soon, so let's close this in favor of #198 (which is just the issue of floating windows having incorrect size) and #207 (which tries to address that issue but needs additional work or a different approach).

@liskin liskin closed this as not planned Won't fix, can't repro, duplicate, stale May 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants