Re: Resizing Motif/Xaw/Xlib apps vs. resizing GTK/Qt apps, and how to speed up an Xlib app
- From: GPS <georgeps@xxxxxxxxxxxx>
- Date: Sat, 08 Nov 2008 22:40:26 -0700
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
.
- References:
- Prev by Date: Resizing Motif/Xaw/Xlib apps vs. resizing GTK/Qt apps, and how to speed up an Xlib app
- Next by Date: Re: Resizing Motif/Xaw/Xlib apps vs. resizing GTK/Qt apps, and how to speed up an Xlib app
- Previous by thread: Resizing Motif/Xaw/Xlib apps vs. resizing GTK/Qt apps, and how to speed up an Xlib app
- Next by thread: Re: Resizing Motif/Xaw/Xlib apps vs. resizing GTK/Qt apps, and how to speed up an Xlib app
- Index(es):
Relevant Pages
|