Find the answer to your Linux question:
Results 1 to 6 of 6
Hi. I have written a simple program in C that gives graphical output through X using the Xlib library. It does not receive any input from the user, all it ...
Enjoy an ad free experience by logging in. Not a member yet? Register.
  1. #1
    Linux User Daan's Avatar
    Join Date
    Aug 2005
    Location
    The Netherlands
    Posts
    323

    Question My Xlib program behaves differently every time I run it...


    Hi.

    I have written a simple program in C that gives graphical output through X using the Xlib library. It does not receive any input from the user, all it should do is make window and draw a black filled rectangle in it. However, after compiling, the program does so only once in while, the rest of the times I run it only shows the window, nothing is drawn in it.

    Can you help find my error?

    Here's the code:

    Code:
    /* XCM.c - displays a CA on the screen using xlibs. */
    
    #include <X11/Xlib.h>
    #include <unistd.h>
    
    Window makeCAwindow(Display* display,
    	int win_width, int win_height, int x, int y);
    	
    GC makeCAgc(Display* display, Window CAwin);
    
    void drawCA(Display* display, Window CAwin, GC gc);
    
    int height = 100;
    int width = 100;
    
    int main()
    {	Display* display;
    	Window CAwin;
    	GC CAgc;
    	int win_width, win_height, x, y;
    	int cellpix = 5;
    	win_width = cellpix * width;
    	win_height = cellpix * height;
    
    	/* Connect to X server on the local machine. */
    	display = XOpenDisplay(":0");
    	
    	/* Make a window at position x = 0, y = 0. */
    	x = y = 0;
    	CAwin = makeCAwindow(display, win_width, win_height, x, y);
    	
    	/* Make a Graphics Context for drawing in the window. */
    	CAgc = makeCAgc(display, CAwin);
    	
    	/* Draw in the window. */
    	drawCA(display, CAwin, CAgc);
    	
    	/* Send pending instructions to X server. */
    	XFlush(display);
    	
    	sleep(3);
    	
    	/* End connection to X server. */
    	XCloseDisplay(display);
    	
    }
    
    Window makeCAwindow(Display* display,
    		int win_width, int win_height, int x, int y)	
    {	
    	int screen_num = DefaultScreen(display);
    	int borderwidth = 5;	
    	Window win;
    	
    	win = XCreateSimpleWindow(display, RootWindow(display, screen_num),
                                x, y, win_width, win_height, borderwidth,
                                BlackPixel(display, screen_num),
                                WhitePixel(display, screen_num));
                                
        XMapWindow(display, win);
        
        XFlush(display);
        
        return win;
    } 
    
    GC makeCAgc(Display* display, Window CAwin)  
    {
    	GC gc;
    	unsigned long valuemask = 0;
    	XGCValues values;
    	int screen_num = DefaultScreen(display);
    	
    	gc = XCreateGC(display, CAwin, valuemask, &values);
    	
    	/* change the background color of this GC to white. */
    	XSetBackground(display, gc, WhitePixel(display, screen_num));
    
    	/* change the foreground color of this GC to black. */
    	XSetForeground(display, gc, BlackPixel(display, screen_num));
    
    	return gc;	
    }                   
    
    void drawCA(Display* display, Window CAwin, GC gc)  
    {
    	int x,y;
    	int a = 100;
    	x = 0;
    	y = 0;
    	XFillRectangle(display, CAwin, gc, x, y, a, a);
    	x = 100;
    	XFillRectangle(display, CAwin, gc, x, y, a, a);	
    }
    I compiled and ran like this:

    Code:
    daan@smeagol:~/science/code/CM/C++M$ gcc XCM.c -o XCM -Wall -L/usr/X11R6/lib -lX11
    XCM.c: In function ‘main’:
    XCM.c:46: warning: control reaches end of non-void function
    daan@smeagol:~/science/code/CM/C++M$ ./XCM
    daan@smeagol:~/science/code/CM/C++M$ ./XCM
    daan@smeagol:~/science/code/CM/C++M$ ./XCM
    daan@smeagol:~/science/code/CM/C++M$ ./XCM
    daan@smeagol:~/science/code/CM/C++M$ ./XCM
    daan@smeagol:~/science/code/CM/C++M$ ./XCM
    daan@smeagol:~/science/code/CM/C++M$ ./XCM
    daan@smeagol:~/science/code/CM/C++M$ ./XCM
    daan@smeagol:~/science/code/CM/C++M$ ./XCM
    daan@smeagol:~/science/code/CM/C++M$ ./XCM
    daan@smeagol:~/science/code/CM/C++M$ ./XCM
    daan@smeagol:~/science/code/CM/C++M$ ./XCM
    daan@smeagol:~/science/code/CM/C++M$ ./XCM
    daan@smeagol:~/science/code/CM/C++M$ ./XCM
    daan@smeagol:~/science/code/CM/C++M$ ./XCM
    daan@smeagol:~/science/code/CM/C++M$ ./XCM
    daan@smeagol:~/science/code/CM/C++M$ ./XCM
    daan@smeagol:~/science/code/CM/C++M$
    I got a black rectangle only once (the 10th time or so).

    Thanks!

  2. #2
    Linux User Daan's Avatar
    Join Date
    Aug 2005
    Location
    The Netherlands
    Posts
    323

    Red face

    I've added two line of code and now it works (if it doesn't hang). Still a bit magical to me...

    Code:
    /* XCM.c - displays a CA on the screen using xlibs. */
    
    #include <X11/Xlib.h>
    #include <unistd.h>
    
    Window makeCAwindow(Display* display,
    	int win_width, int win_height, int x, int y);
    	
    GC makeCAgc(Display* display, Window CAwin);
    
    void drawCA(Display* display, Window CAwin, GC gc);
    
    int height = 100;
    int width = 100;
    
    int main()
    {	Display* display;
    	Window CAwin;
    	GC CAgc;
    	int win_width, win_height, x, y;
    	int cellpix = 5;
    	win_width = cellpix * width;
    	win_height = cellpix * height;
    
    	/* Connect to X server on the local machine. */
    	display = XOpenDisplay(":0");
    	
    	/* Make a window at position x = 0, y = 0. */
    	x = y = 0;
    	CAwin = makeCAwindow(display, win_width, win_height, x, y);
    	
    	/************************************
    	 * We want to get MapNotify events. *
    	 ************************************/
    	XSelectInput(display, CAwin, StructureNotifyMask);
    	
    	/* Make a Graphics Context for drawing in the window. */
    	CAgc = makeCAgc(display, CAwin);
    	
    	/*************************************
    	 * Wait for MapNotify events.        *
    	 *************************************/
    	for(;;) {
    	    XEvent e;
    	    XNextEvent(display, &e);
    	    if (e.type == MapNotify)
    		  break;
          }
    
    	
    	/* Draw in the window. */
    	drawCA(display, CAwin, CAgc);
    	
    	/* Send pending instructions to X server. */
    	XFlush(display);
    	
    	sleep(3);
    	
    	/* End connection to X server. */
    	XCloseDisplay(display);
    	
    }
    
    Window makeCAwindow(Display* display,
    		int win_width, int win_height, int x, int y)	
    {	
    	int screen_num = DefaultScreen(display);
    	int borderwidth = 5;	
    	Window win;
    	
    	win = XCreateSimpleWindow(display, RootWindow(display, screen_num),
                                x, y, win_width, win_height, borderwidth,
                                BlackPixel(display, screen_num),
                                WhitePixel(display, screen_num));
                                
        XMapWindow(display, win);
        
        XFlush(display);
        
        return win;
    } 
    
    GC makeCAgc(Display* display, Window CAwin)  
    {
    	GC gc;
    	unsigned long valuemask = 0;
    	XGCValues values;
    	int screen_num = DefaultScreen(display);
    	
    	gc = XCreateGC(display, CAwin, valuemask, &values);
    	
    	/* change the background color of this GC to white. */
    	XSetBackground(display, gc, WhitePixel(display, screen_num));
    
    	/* change the foreground color of this GC to black. */
    	XSetForeground(display, gc, BlackPixel(display, screen_num));
    
    	return gc;	
    }                   
    
    void drawCA(Display* display, Window CAwin, GC gc)  
    {
    	int x,y;
    	int a = 100;
    	x = 0;
    	y = 0;
    	XFillRectangle(display, CAwin, gc, x, y, a, a);
    	x = 100;
    	XFillRectangle(display, CAwin, gc, x, y, a, a);	
    }

  3. #3
    Linux User
    Join Date
    Aug 2005
    Location
    Italy
    Posts
    401
    Try do draw like this:

    Code:
    for (;;) {
    drawCA(display, CAwin, CAgc);
    sleep(1);
    }
    But substitute you XFlush cal with a XSync(display, false); call... this is better for you.
    Using XSync you can avoid to check StrutureNotify events, making you code more stable.

    ...

    Normally a program like that is based on events: you application should not draw only once, but draw when needed. This should be done when an Expose event is received, because the X server wants to redraw the window. Because you draw only once, maybe the window context may be lost after you drawing.
    When using Windows, have you ever told "Ehi... do your business?"
    Linux user #396597 (http://counter.li.org)

  4. $spacer_open
    $spacer_close
  5. #4
    Linux User Daan's Avatar
    Join Date
    Aug 2005
    Location
    The Netherlands
    Posts
    323

    Smile

    Thanks for your reply, burnit!

    I have replaced Xflush(display) with XSync(display,False) in the main() and in the makeCAwindow() functions of the first version of my code I posted above. Now it doesn't hang (like it did in the second version), and it shows a rectangle 17 out of 20 times.

    Drawing like you suggested only gives an empty window, however, with an XSync() added, it draws a black rectangle every time, although sometimes with a delay of a second or so.

    Code:
    for (;;) {
    	drawCA(display, CAwin, CAgc);
    	XSync(display,False);
    	sleep(1);
    }
    I could alter the for loop so that it goes through it a couple of times only, enough to make chances very slight that a rectangle does not appear, so that it can go on with the program.

    Code:
    for (loop=0;loop<3;loop++) {
    	drawCA(display, CAwin, CAgc);
    	XSync(display,False);		
    }
    But this is not a very nice way, especially because I want to draw up to 10 000 squares for about 10 000 times...

  6. #5
    Linux User
    Join Date
    Aug 2005
    Location
    Italy
    Posts
    401
    Well... my code was only an example to see if everything works!

    Your application should provide a global Draw() routine, wich will be called whenever an Expose event is generated. This ensure the windows painting whenever is will be shadowed by another window or something else...

    If you want to make animations (moving your rectangles), you should implement a main loop wich call manually your Draw() routine... but this is a little more complex, because while you call Draw() in the main loop (to animate graphics), your X server can generate an Expose event wich calls the same Draw()!

    Keep in mind that X server programming is asynchronous: when you call Xlib routines, they are not executed immediately, but they are processed lately. That's because you need XSync: it force to process all pending Xlib calls and wait for their completion.

    My last consideration: a short loop without delay will burn the CPU to 100%, and this is not desirable. So you should implement a frame rate limiter using gettimeofday and nanosleep: in this way you can limit the number of draws (for example only 30/40 times in one second). This permit to have a resposive system, without loading CPu too much.
    When using Windows, have you ever told "Ehi... do your business?"
    Linux user #396597 (http://counter.li.org)

  7. #6
    Linux User Daan's Avatar
    Join Date
    Aug 2005
    Location
    The Netherlands
    Posts
    323

    Smile

    Hi burnit!

    I'm writing a program to simulate cellular automata (CA, e.g. Conway's Game of Life), so indeed I have a loop that updates the states of the cells (colors of the squares) in the window (according to the current states of the cells and the "next state rule"). It's working pretty well now, I have 100x100 grid on which nice patterns emerge at a high rate.

    If I get you right, it would be a good idea to have updates in the window only if the window is exposed. Once I understand the Expose functionality of the X system, that would not be so hard: the main loop will continously update the non graphical 2D array in the computer's memory, and only when the window is exposed, this array is translated into a X-graphical grid in the window. But then I would have to draw everything to the window each moment it is exposed, not just the changes... I'll see what I'll do...

    I'm not sure Xlib is the best way to do this thing, but I don't know a better way. It seems Xlib is designed for interaction with the user, at the moment my program is not interactive.

    Thanks for your thoughts,

    Daan

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •