f



Resizing Motif/Xaw/Xlib apps vs. resizing GTK/Qt apps, and how to speed up an Xlib app

I'm toying with Xlib at the moment (for the experience mostly) and am 
experiencing difficulties with resize events. From what I've heard on IRC 
and the Web, it appears that my application is currently processing and 
acting upon every ConfigureNotify event I'm recieving (basically a few 
XResizeWindow calls), causing /something/ inside X to get majorly 
spammed, lag to bits, and in turn cause my application to lag really 
badly. And the contents of my window are lagging up really badly because 
of this too.

The biggest difference/discrepancy in resizing I've found with something 
written in Xlib, Xaw or Motif is that these typically don't seem to have 
control of the window they're in and will allow the WM to freely resize 
the window while they try to (almost comically) catch up to the WM's 
movements and lag majorly. elinks -g also exhibits this issue, and was 
the first app I found the problem in, and I was even on Win32, heh. Okay, 
so conversely, GTK or Qt seem to "grab" the window they're in, so the 
window resizes rather jerkily/jumpily in small increments, but the window 
contents "keep up" on each resize jump.

I've also found that my code and assumedly code of the same type (Xaw/
Motif/etc) uses 100% CPU while it resizes, but GTK/Qt code doesn't. :/

So I just wanted advice on (and possibly example code implementing :D) a 
way to go about fixing this up. I'm not very experienced with C or X so 
while I do my best to learn quickly I'm not all that fast at times with 
new concepts so do bear with me. :P Also, I #1 might have something 
wrong, and #2 am learning the right and wrong way to do things. If 
there's absolutely anything you think I could/should do differently, 
please do say. :P

Below is a barebones X/Xlib app that, when compiled, creates a little 
child window inside a top-level window. When you resize the window the 
inner window (which I've set to black so you can actually see it) should 
update its size as well, and hopefully lag so you can see what I'm 
talking about. Note that I color the child window black when I 
XCreateWindow it, so I don't need to use Expose events to manage its 
color upon resize. This proves that this problem isn't Expose-event-
related.

Sorry the code looks cramped, I wanted to be sure no servers or clients 
would incorrectly wrap it wrongly causing it to fail to compile. I set it 
to use double-space tabbing so converting '  ' back to '\t' shouldn't be 
a problem if you like "real" tabbing. :p

-dav7

-----

// gcc -lX11 xdrawsquare.c -o xdrawsquare

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

int main() {
  
  int windowwidth = 300, windowheight = 300, run = 1;
  XEvent event;
  
  Display *display = XOpenDisplay(NULL);
  int screen = DefaultScreen(display);
  
  XSizeHints hints;
  hints.flags = PSize;
  
  Window window = XCreateSimpleWindow(display, RootWindow(display,
    screen), 100, 100, windowwidth, windowheight, 1, 10,
    WhitePixel(display, screen));
  
  Atom wmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW",
    False);
  XSetWMProtocols(display, window, &wmDeleteMessage, 1);
  
  XSetStandardProperties(display, window, "X draw/resize demo",
    "xdrawsquare", None, (char **)"", 0, &hints);
  
  Window square = XCreateSimpleWindow(display, window, 10, 10,
    windowwidth - 20, windowheight - 20, 0, 0,
    BlackPixel(display, screen));
  
  XSelectInput(display, window, ExposureMask | KeyPressMask |\
    KeyReleaseMask | StructureNotifyMask);
  
  XMapWindow(display, square);
  XMapWindow(display, window);
  
  while (run == 1) {
    
    XNextEvent(display, &event);
    
    switch (event.type) {
      
      case DestroyNotify:
      case ClientMessage:
        if (event.xclient.data.l[0] ==
          wmDeleteMessage) run = 0;
        break;
      
      case ConfigureNotify:
        
        if (event.xconfigure.window == window) {
          
          windowwidth =
            event.xconfigure.width;
          windowheight =
            event.xconfigure.height;
          
          XResizeWindow(display, square,
            windowwidth - 20,
            windowheight - 20);
          
        }
        
        break;
      
      default: 
        break;
      
    }
    
  }
  
  XDestroyWindow(display, square);
  XDestroyWindow(display, window);
  
  XCloseDisplay(display);
  
}
0
David
11/8/2008 2:28:32 PM
comp.windows.x 1388 articles. 1 followers. Post Follow

4 Replies
1317 Views

Similar Articles

[PageSpeed] 13

David Lindsay wrote:

> I'm toying with Xlib at the moment (for the experience mostly) and am
> experiencing difficulties with resize events. From what I've heard on IRC
> and the Web, it appears that my application is currently processing and
> acting upon every ConfigureNotify event I'm recieving (basically a few
> XResizeWindow calls), causing /something/ inside X to get majorly
> spammed, lag to bits, and in turn cause my application to lag really
> badly. And the contents of my window are lagging up really badly because
> of this too.
> 
> The biggest difference/discrepancy in resizing I've found with something
> written in Xlib, Xaw or Motif is that these typically don't seem to have
> control of the window they're in and will allow the WM to freely resize
> the window while they try to (almost comically) catch up to the WM's
> movements and lag majorly. elinks -g also exhibits this issue, and was
> the first app I found the problem in, and I was even on Win32, heh. Okay,
> so conversely, GTK or Qt seem to "grab" the window they're in, so the
> window resizes rather jerkily/jumpily in small increments, but the window
> contents "keep up" on each resize jump.

There are several things causing redraw performance issues in X11, and
there's not much you can do about some of it.  I know, because I wrote a
window system that wasn't based on X11, and I've worked with X11 for years.

X11 uses a mostly asynchronous drawing model, so flicker is one result of
that on resize.

Here's how a resize typically works.

1. user triggers an event that causes a button press event to be sent to a
particular app, such as the WM.
2. the WM responds by setting some state variable to dragging
3. the WM processes pointer motion events and calculates a delta that fits
the current hints, and thereby produces a new size for the application
window.
4. the WM calls XResizeWindow or XConfigureWindow
5. the application receives a ConfigureNotify due to step 4 and at some
point redraws, perhaps then, or perhaps when its event loop is idle.
6. graphics get drawn via X requests.
7. repeat steps 3 through 6 until a button release triggers the !dragging
state.

There are various positions in between where graphics may be
drawn/composited to the display memory before step 6, while the window
being resized hasn't fully drawn its contents, because X is serving drawing
requests from multiple clients.  The asynchronous model can't really solve
this, unless we assume that the new window contents are transparent, and
even then the titlebar width wouldn't match the content width, for a few
frames potentially.

The way it worked in my window system was different.  The WM was builtin to
the server via an API, and would actually smoothly resize, and provide a
more synchronous redraw system.  If you don't actually resize the parent
window until the client has responded, say with an update message, then the
contents never get out of sync with reality, and you wouldn't see flicker.

Resizing can often be costly, because it usually means reallocating a fairly
large RGB or RGBA buffer, and thus the old buffer may be unmapped with a
new buffer being mmaped in.  This means TLB updates, and cache misses, and
well, resizing is not a simple chore to optimize in the general case.  A
somewhat high-water-mark allocation scheme can help a little.

It works differently of course with a WM that uses the sort of "rubberband"
resize approach, which relies on grabbing the pointer until the user has
clicked, such as TWM.  Such a scheme doesn't generate nearly as many
events.

The flicker is not generally a serious problem for most people, but it's
sometimes annoying.

> I've also found that my code and assumedly code of the same type (Xaw/
> Motif/etc) uses 100% CPU while it resizes, but GTK/Qt code doesn't. :/

The lag/CPU usage could be from a variety of causes.  The geometry managers
may not be ideal, or as well optimized as Gtk+ and Qt.  Geometry managers
are difficult to write, at least for me.  Gtk+/Gdk and Qt use shared memory
for some of their graphics, Motif and Xaw generally do not.

> 
> So I just wanted advice on (and possibly example code implementing :D) a
> way to go about fixing this up. I'm not very experienced with C or X so
> while I do my best to learn quickly I'm not all that fast at times with
> new concepts so do bear with me. :P Also, I #1 might have something
> wrong, and #2 am learning the right and wrong way to do things. If
> there's absolutely anything you think I could/should do differently,
> please do say. :P
> 
> Below is a barebones X/Xlib app that, when compiled, creates a little
> child window inside a top-level window. When you resize the window the
> inner window (which I've set to black so you can actually see it) should
> update its size as well, and hopefully lag so you can see what I'm
> talking about. Note that I color the child window black when I
> XCreateWindow it, so I don't need to use Expose events to manage its
> color upon resize. This proves that this problem isn't Expose-event-
> related.

Generally you will probably want to redraw after a ConfigureNotify too,
IIRC.
 
> Sorry the code looks cramped, I wanted to be sure no servers or clients
> would incorrectly wrap it wrongly causing it to fail to compile. I set it
> to use double-space tabbing so converting '  ' back to '\t' shouldn't be
> a problem if you like "real" tabbing. :p
> 
> -dav7
> 
> -----
> 
> // gcc -lX11 xdrawsquare.c -o xdrawsquare
> 
> #include <X11/Xlib.h>
> #include <X11/Xutil.h>
> 
> int main() {
>   
>   int windowwidth = 300, windowheight = 300, run = 1;
>   XEvent event;
>   
>   Display *display = XOpenDisplay(NULL);
>   int screen = DefaultScreen(display);
>   
>   XSizeHints hints;
>   hints.flags = PSize;
>   
>   Window window = XCreateSimpleWindow(display, RootWindow(display,
>     screen), 100, 100, windowwidth, windowheight, 1, 10,
>     WhitePixel(display, screen));
>   
>   Atom wmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW",
>     False);
>   XSetWMProtocols(display, window, &wmDeleteMessage, 1);
>   
>   XSetStandardProperties(display, window, "X draw/resize demo",
>     "xdrawsquare", None, (char **)"", 0, &hints);
>   
>   Window square = XCreateSimpleWindow(display, window, 10, 10,
>     windowwidth - 20, windowheight - 20, 0, 0,
>     BlackPixel(display, screen));
>   
>   XSelectInput(display, window, ExposureMask | KeyPressMask |\
>     KeyReleaseMask | StructureNotifyMask);
>   
>   XMapWindow(display, square);
>   XMapWindow(display, window);
>   
>   while (run == 1) {
>     
>     XNextEvent(display, &event);
>     
>     switch (event.type) {
>       
>       case DestroyNotify:
>       case ClientMessage:
>         if (event.xclient.data.l[0] ==
>           wmDeleteMessage) run = 0;
>         break;
>       
>       case ConfigureNotify:
>         
>         if (event.xconfigure.window == window) {
>           
>           windowwidth =
>             event.xconfigure.width;
>           windowheight =
>             event.xconfigure.height;
>           
>           XResizeWindow(display, square,
>             windowwidth - 20,
>             windowheight - 20);
>           
>         }

This strikes me as odd.  Something else, such as the window manager (WM) has
notified you of the size it chose, and thus you get a ConfigureNotify.  By
doing another ConfigureRequest you can create a lot of unnecessary events. 
The XResizeWindow when a WM is redirecting Configure requests ends up
causing the WM to respond in some way.  Just accept what the WM tells you
the toplevel size is.

With regard to subwindows, you can choose any size you want, unless you have
redirection enabled for the parent.

--George
0
GPS
11/9/2008 5:40:26 AM
David Lindsay wrote:

> ...causing /something/ inside X to get majorly
> spammed, lag to bits, and in turn cause my application to lag really
> badly. And the contents of my window are lagging up really badly because
> of this too.

Qt handles this by processing only the final ConfigureNotify event in any
given series of events.  X11 generates a ConfigureNotify event for *every*
change in window size.  Fully processing all of the events is going to
cause major display lag.

0
Tony
11/9/2008 4:37:54 PM
On Nov 9, 8:37=A0am, Tony <donotre...@tonyobryan.com> wrote:
> David Lindsay wrote:
> > ...causing /something/ inside X to get majorly
> > spammed, lag to bits, and in turn cause my application to lag really
> > badly. And the contents of my window are lagging up really badly becaus=
e
> > of this too.
>
> Qt handles this by processing only the final ConfigureNotify event in any
> given series of events. =A0X11 generates a ConfigureNotify event for *eve=
ry*
> change in window size. =A0Fully processing all of the events is going to
> cause major display lag.

Tony hinted at the correct way to handle this. In your event loop,
whenever you find a ConfigureNotify for your window, you should check
the
rest of the event queue to see if it contains other ConfigureNotify
events
on your window.; Discard all but the last one, and process that one.
See XCheckTypedWindowEvent.

This same technique should be used for MotionNotify, Expose, Enter,
and Leave events.

Most Motif widgets do this automatically, by setting their
compress_motion,
compress_exposure, and compress_enterleave fields to TRUE.
--
Fred Kleinschmidt
0
Fred
11/10/2008 3:22:18 PM
David Lindsay wrote:
> I'm toying with Xlib at the moment (for the experience mostly) and am 
> experiencing difficulties with resize events. From what I've heard on IRC 
> and the Web, it appears that my application is currently processing and 
> acting upon every ConfigureNotify event I'm recieving (basically a few 
> XResizeWindow calls), causing /something/ inside X to get majorly 

X11 sends multiple Resize and Expose events for sections of your window.
It is not needed to react to all of those. Instead you can react to the
last one only. This seriously reduces the amount of work required. I am
yet to see an actual application that correctly/successfully handles the
sections of Expose etc... Anyway, the trick is the
XCheckTypedWindowEvent() call.

In my fpGUI Toolkit I wrote, there is almost zero flicker. I combine
this trick with double buffered drawing and a cached canvas for resize
events. This works fantastic. fpGUI Toolkit code is freely available on
SourceForge, though it is written in Object Pascal, but the XLib calls
are identical to what you would use in C language.

X.ConfigureNotify:
    begin
      repeat
        //
      until not XCheckTypedWindowEvent(display, ev.xconfigure.window,
          ConfigureNotify, @ev);
      // Only now do we process the resize or move events.
      ....


Regards,
  - Graeme -

_______________________________________________________
fpGUI - a cross-platform GUI toolkit using Free Pascal
http://opensoft.homeip.net/fpgui/

0
Graeme
11/12/2008 7:14:54 AM
Reply: