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
Terminal, Linux Mint 10
Under Linux, you should see something like this:
X11 demo under Linux Mint 10
And under Mac OS X, you should see something like this:
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:
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?
XNextEvent is blocking like GetMessage (see here).
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?
while (true) {
while (XPending(dpy)) {
XNextEvent(dpy, &e);
// process event here…
}
DrawAndUpdate();
}
@Wan: yep I’m developing for Linux (and Mac 🙁 ) and GeeXLab is planned but later. Currently I work on smaller projects…
I really thank you very much for your simple example which works on MacBook Mountain Lion like the demo you gave.
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!