Programming: Simple X11 Code Sample for Linux and Mac OS X

X Window System, Mac OS X

X Window (or X11) is the low level windowing system (the equivalent of the Win32 API under Windows) available with Unix / Linux and Mac OS X. According to this page, X11 is an option under Mac OS X (on my Lion-based system, X11 is available by default). Desktop environments like KDE or Gnome are built on top of X11. Then using X11 API can be an interesting solution if you need to quickly test an piece of code under Linux and Max OS X (it’s my case…).

And nice news, the compilation has the same syntax under both operating systems.

Here is the full source code of a simple X11 app that creates a X window and displays some basic information about the system. To quit the app, two ways are available:

  • 1 – ESC key. This is the simplest way to stop the app. Just catch the KeyPress event and test the presence of the XK_Escape code.
  • 2 – Window close button (the cross). Don’t know why, but it’s not obvious to manage the close button. Once you have found that the WM_DELETE_WINDOW
    message is the key of all your troubles, you can easily code the management of the close button.

To compile and link the code sample, just open a terminal in the folder of your source code (too easy under Linux, a bit less easy under Mac 😉 ) and type:

$ g++ xl01.cpp -o xl01 -I/usr/X11R6/include -I/usr/X11R6/include/X11 -L/usr/X11R6/lib 
-L/usr/X11R6/lib/X11 -lX11

Now to execute the demo:

$ ./xl01

X Window X11 app demo under Linux
Terminal, Linux Mint 10

Under Linux, you should see something like this:

X Window X11 app demo under Linux
X11 demo under Linux Mint 10

And under Mac OS X, you should see something like this:

X Window X11 app demo under Mac OS X
X11 demo under Mac OS X Lion

And now the full cross-platform source code that all of you are waiting for:

#include 
#include 
#include 

#include 
#include 
#include 

#include 

int main(int argc, char** argv)
{
  Display* dpy = XOpenDisplay(NULL);
  if (dpy == NULL) 
  {
    fprintf(stderr, "Cannot open display\n");
    exit(1);
  }
 
  int s = DefaultScreen(dpy);
  Window win = XCreateSimpleWindow(dpy, RootWindow(dpy, s), 10, 10, 660, 200, 1,
                                   BlackPixel(dpy, s), WhitePixel(dpy, s));
  XSelectInput(dpy, win, ExposureMask | KeyPressMask);
  XMapWindow(dpy, win);

#if defined(__APPLE_CC__)  
  XStoreName(dpy, win, "Geeks3D.com - X11 window under Mac OS X (Lion)");
#else
  XStoreName(dpy, win, "Geeks3D.com - X11 window under Linux (Mint 10)");
#endif  
  
  Atom WM_DELETE_WINDOW = XInternAtom(dpy, "WM_DELETE_WINDOW", False); 
  XSetWMProtocols(dpy, win, &WM_DELETE_WINDOW, 1);  

  bool uname_ok = false;
  struct utsname sname;  
  int ret = uname(&sname);
  if (ret != -1)
  {
    uname_ok = true;
  }
 
  XEvent e;
  while (1) 
  {
    XNextEvent(dpy, &e);
    if (e.type == Expose) 
    {
      int y_offset = 20;
 
#if defined(__APPLE_CC__)  
      const char* s1 = "X11 test app under Mac OS X Lion";
#else      
      const char* s1 = "X11 test app under Linux";
#endif      
      
      const char* s2 = "(C)2012 Geeks3D.com"; 
      XDrawString(dpy, win, DefaultGC(dpy, s), 10, y_offset, s1, strlen(s1));
      y_offset += 20;
      XDrawString(dpy, win, DefaultGC(dpy, s), 10, y_offset, s2, strlen(s2));
      y_offset += 20;

      if (uname_ok)
      {
        char buf[256] = {0};
        
        sprintf(buf, "System information:");
        XDrawString(dpy, win, DefaultGC(dpy, s), 10, y_offset, buf, strlen(buf));
        y_offset += 15;

        sprintf(buf, "- System: %s", sname.sysname);
        XDrawString(dpy, win, DefaultGC(dpy, s), 10, y_offset, buf, strlen(buf));
        y_offset += 15;

        sprintf(buf, "- Release: %s", sname.release);
        XDrawString(dpy, win, DefaultGC(dpy, s), 10, y_offset, buf, strlen(buf));
        y_offset += 15;

        sprintf(buf, "- Version: %s", sname.version);
        XDrawString(dpy, win, DefaultGC(dpy, s), 10, y_offset, buf, strlen(buf));
        y_offset += 15;
        
        sprintf(buf, "- Machine: %s", sname.machine);
        XDrawString(dpy, win, DefaultGC(dpy, s), 10, y_offset, buf, strlen(buf));
        y_offset += 20;
      }


      XWindowAttributes  wa;
      XGetWindowAttributes(dpy, win, &wa);
      int width = wa.width;
      int height = wa.height;
      char buf[128]={0};
      sprintf(buf, "Current window size: %dx%d", width, height);
      XDrawString(dpy, win, DefaultGC(dpy, s), 10, y_offset, buf, strlen(buf));
      y_offset += 20;
    }
    
    if (e.type == KeyPress)
    {
      char buf[128] = {0};
      KeySym keysym;
      int len = XLookupString(&e.xkey, buf, sizeof buf, &keysym, NULL);
      if (keysym == XK_Escape)
        break;
    }

    if ((e.type == ClientMessage) && 
        (static_cast(e.xclient.data.l[0]) == WM_DELETE_WINDOW))
    {
      break;
    }
  }
  
  XDestroyWindow(dpy, win);
  XCloseDisplay(dpy);
  return 0;
}

Some references:

7 thoughts on “Programming: Simple X11 Code Sample for Linux and Mac OS X”

  1. fpmore

    Hey JeGX thanks for the sample!
    Quick question: the XNextEvent function behaves like the WinAPI function GetMessage and suspends the current thread while there’s no new events or it behaves like the PeekMessage?

  2. Wan

    So how is the linux version of GeeXLab going? Come on, we know from the past blog posts that you are developping something for another platform than Windows, so what is it?

  3. Michal

    while (true) {
    while (XPending(dpy)) {
    XNextEvent(dpy, &e);
    // process event here…
    }
    DrawAndUpdate();
    }

  4. JeGX Post Author

    @Wan: yep I’m developing for Linux (and Mac 🙁 ) and GeeXLab is planned but later. Currently I work on smaller projects…

  5. did

    I really thank you very much for your simple example which works on MacBook Mountain Lion like the demo you gave.

  6. glasspusher

    Hey man,

    Thanks. This works under Mavericks too. One thing: there’s a typo in your command line instructions, you have “ell oh one” typed up there in the file names instead of “one oh one”, and for other newbies like me, be sure to enter that on the command line as one line, no carriage return, so it goes as one command!

Comments are closed.