0

The following piece of code initiates substructure redirection on the root window and attempts to resize any new children:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/Xutil.h>

#define TERMINAL "urxvt"

void start(char* what)
{
     if(!fork())
     {
      char* const args[] = {"/bin/sh", "-c", what, NULL};
      execvp("/bin/sh", args);
      exit(1);
     }
}

int main()
{
     Display* dpy;
     Window root;
     XSetWindowAttributes setAttribs;
     XEvent ev;

     if(!(dpy = XOpenDisplay(NULL)))
      exit(1);

     root = XDefaultRootWindow(dpy);

     XSelectInput(dpy, root, SubstructureNotifyMask | SubstructureRedirectMask);
     XGrabKey(dpy, XKeysymToKeycode(dpy, XK_T), Mod4Mask, root, True, GrabModeAsync, GrabModeAsync);

     while(1)
     {
      XNextEvent(dpy, &ev);

      switch(ev.type)
      {
      case MapRequest:
           XMapWindow(dpy, ev.xmaprequest.window);
           XMoveResizeWindow(dpy, ev.xmaprequest.window, 0, 0, 800, 600);
           XSync(dpy, False);
           break;
      case KeyPress:
           start(TERMINAL);
           break;
      default:
           break;
      }
     }

     XUngrabKey(dpy, AnyKey, AnyModifier, root);
     XSync(dpy, False);

     return 0;
}

This seems to work fine for simple terminals (you can launch the one defined by the TERMINAL constant with Mod4+t ), but messes up various parts in more complex GUIs. For instance, emacs shows up without a toolbar, and the menu bar doesn't redraw itself when necessary. Firefox looks fine, but pressing the 'open menu' button has no visible effect. Changing the code to respect configuration requests fixes some of the problems ('open menu' button still won't work), but that, of course, defeats the entire purpose. All I want at this point is to be able to resize and reposition windows without breaking them. What am I missing?

[UPDATE]

As per Ingo's suggestion, here's an updated version that doesn't touch windows with override_redirect set:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/Xutil.h>

#define TERMINAL "urxvt"

void start(char* what)
{
     if(!fork())
     {
      char* const args[] = {"/bin/sh", "-c", what, NULL};
      execvp("/bin/sh", args);
      exit(1);
     }
}

int hasOverrideRedirect(Display* dpy, Window win)
{
     XWindowAttributes wa;

     if(!XGetWindowAttributes(dpy, win, &wa))
      return 0;
     return wa.override_redirect;
}

int main()
{
     Display* dpy;
     Window root;
     XSetWindowAttributes setAttribs;
     XEvent ev;

     if(!(dpy = XOpenDisplay(NULL)))
      exit(1);

     root = XDefaultRootWindow(dpy);

     XSelectInput(dpy, root, SubstructureNotifyMask | SubstructureRedirectMask);
     XGrabKey(dpy, XKeysymToKeycode(dpy, XK_T), Mod4Mask, root, True, GrabModeAsync, GrabModeAsync);

     while(1)
     {
      XNextEvent(dpy, &ev);

      switch(ev.type)
      {
      case MapRequest:
           XMapWindow(dpy, ev.xmaprequest.window);

           if(!hasOverrideRedirect(dpy, ev.xmaprequest.window))
           {
            XMoveResizeWindow(dpy, ev.xmaprequest.window, 0, 0, 800, 600);
            XSync(dpy, False);
           }
           break;
      case KeyPress:
           start(TERMINAL);
           break;

      case ConfigureRequest:
           if(hasOverrideRedirect(dpy, ev.xconfigurerequest.window))
           {
            XConfigureRequestEvent *e2 = &ev.xconfigurerequest;
            XWindowChanges wc;

            wc.x = e2->x;
            wc.y = e2->y;
            wc.width = e2->width;
            wc.height = e2->height;
            wc.border_width = e2->border_width;
            wc.sibling = e2->above;
            wc.stack_mode = e2->detail;
            XConfigureWindow(dpy, e2->window, e2->value_mask, &wc);
           }

      default:
           break;
      }
     }

     XUngrabKey(dpy, AnyKey, AnyModifier, root);
     XSync(dpy, False);

     return 0;
}

No noticeable difference, as far as the original problem is concerned.

user3026691
  • 497
  • 2
  • 11
  • One thing you for sure are doing wrong is ignoring override_redirect. I'd advise you to look at a simple window manager's code, something like dwm. – Ingo Bürk Feb 01 '16 at 15:15
  • I thought override_redirect was for the X server. I'll check this as soon as I can. dwm is far too cryptic for a beginner like me. – user3026691 Feb 01 '16 at 15:41
  • dwm is pretty simple, just check how it handles different types of events. You will receive map requests for all windows, but as the window manager you should not touch override redirect windows. In particular not resize them etc. – Ingo Bürk Feb 01 '16 at 17:22
  • Also on a general note I'd recommend you log all events, including ones you don't handle, so you can see what's going on. – Ingo Bürk Feb 01 '16 at 17:36
  • Respecting override_redirect seems to make no difference (see updated code). I have tried to understand the dwm source, but it does a bunch of things that seem redundant and make no sense to me, and duplicating them makes no difference. There's too much background noise for me to figure out what bits makes all the difference. I do log all events in the real code. There seem to be multiple map and configure requests coming from the same window ID immediately, which makes no sense to me either. I must be missing something fundamental. – user3026691 Feb 01 '16 at 17:51
  • I could imagine that firefox requires some basic EWMH / ICCCM support. For example, thunar menus open just fine. Also, firefox menus don't work in the total absence of a window manager either, which, frankly, I consider a bug in firefox. I wouldn't worry about firefox right now if you're trying to learn. EWMH / ICCCM definitely will be more complex to look into. – Ingo Bürk Feb 01 '16 at 17:53
  • Perhaps the problems with Firefox are related to the missing EWMH/ICCCM support, but the problems with emacs disappear if I simply grant all of its configuration requests and refrain from resizing it. What I want to understand is whether there's something wrong with emacs, or with the way I'm trying to resize it. – user3026691 Feb 01 '16 at 18:10

1 Answers1

-1

Well, I'm not entirely sure, but there are 3 things to consider:

  • Window Manager: normally it's the responsibility of the window manager to position and decorate windows. You're trying to subvert this now by resizing the window on the fly.

  • Complex GUIs: you're resizing every window, but what if programs create subwindows? Say, the toolbar is one subwindow, the browser panel is one, the scrollbar another. You are resizing them as well.

  • Unexpected behaviour: so, I have an X program and I create a fixed window of 50x50 pixels. Now suddenly someone else decides to make that window 800x600. What is my programma supposed to do?? Surely it gets confused...

You may be able to 'fix' point 2 by checking if the window is a direct descendent of the root window (e.g. it is the application's top level window), and only resize those.

JvO
  • 3,036
  • 2
  • 17
  • 32
  • This program _is_ the window manager in my case. If I understand correctly, only one client at a time can initiate substructure redirection on the root, so I thought it was clear. If the toolbar is indeed a subwindow of a child of the root window, then substructure redirection doesn't apply to it (i.e. the "WM" wouldn't intercept its configuration requests). Lastly, while it's true that I don't respect size hints, emacs and firefox should be able to handle a 800x600 size... – user3026691 Feb 01 '16 at 14:04
  • 1. As OP said, this *is* the window manager here. 2. The window manager doesn't care about subwindows, X takes care of that with the help from the application. 3. The specification is very clear on the fact that applications need to deal with these situations. – Ingo Bürk Feb 01 '16 at 14:52