How to use buttons / menus / etc. with SDL?

There are 2 main solutions:

The following describe several solutions for embeddeding the SDL window in your application (point #2).

Manually copy from non-window SDL_Surface to the GUI toolkit

You manipulate a SDL_Surface as usual, but you manually copy it as a pixel array to your tookit widget. This is the only technique that allow you to use SDL in several windows.

There is a nice demo for wxWidgets at http://code.technoplaza.net/wx-sdl/part1/ . The demo has some limitations:

With Gtk+, there is a similar solution where you use gdk_draw_rgb_image on SDL_Surface->pixels. Similarly, it involves using (or converting to) a 24bits-RGB format with precise masks, usually not the same as the physical screen, so there will be one additional blit of the pixel data for conversion. Note that it only supports blitting the whole surface:

GdkDrawable* drawable = ...; /* eg. widget->window */
/* A GC is necessary */
GdkGC* ignored_gc = gdk_gc_new(drawable);

int width = -1, height = -1;
gdk_drawable_get_size(drawable, &width, &height);
/* gdk_draw_rgb_image uses hardcoded 24bit RGB (in that order),
   which is usually the reverse order than my x86 display */
Uint32 rmask, gmask, bmask, amask;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
rmask = 0xff000000;
gmask = 0x00ff0000;
bmask = 0x0000ff00;
#else
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
#endif
amask = 0x00000000;
SDL_Surface *screen = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height,
                                           24, rmask, gmask, bmask, amask);

SDL_BlitSurface(...., screen, NULL);
...

/* ~Flip */
gdk_draw_rgb_image(drawable, ignored_gc,
                   0, 0, screen->w, screen->h,
                   GDK_RGB_DITHER_NONE,
                   (guchar*)screen->pixels, screen->pitch);

/* Clean-up when quitting game: */
SDL_FreeSurface(screen);

g_object_unref(ignored_gc);

If you're ok with using other pixel formats, one more efficient way to draw in a Gdk window is to create a SDL_Surface from a GdkImage (this way you skip a pixel conversion from 24bit-RGB before each blit). It also supports partial blits. You'll use gdk_draw_image:

GdkDrawable* drawable = ...; /* eg. widget->window */
/* A GC is necessary */
GdkGC* ignored_gc = gdk_gc_new(drawable);

int width = -1, height = -1;
gdk_drawable_get_size(drawable, &width, &height);
GdkVisual* visual = gdk_drawable_get_visual(drawable);
GdkImage* image = gdk_image_new(GDK_IMAGE_FASTEST, visual, width, height);
SDL_Surface* screen = SDL_CreateRGBSurfaceFrom(image->mem, width, height, image->bits_per_pixel, image->bpl,
                                               visual->red_mask, visual->green_mask, visual->blue_mask, 0);

SDL_BlitSurface(...., screen, NULL);
...

/* ~Flip/UpdateRect */
gdk_draw_image(drawable, ignored_gc, image, 0, 0,
               0, 0, -1, -1);
...

/* Clean-up when quitting game: */
SDL_FreeSurface(screen);
g_object_unref(image);

g_object_unref(ignored_gc);

The mouse or keyboard events are caught by the GUI toolkit only, not by SDL (at least under X11).

The SDL_WINDOWID hack

This makes SDL use an existing window instead of creating a new one. This works under X11 and Win32. Basically you putenv("SDL_WINDOWID=XXX") where XXX is an existing window's ID. You can only use it on one existing window (not several).

Code can be found for various toolkits:

You do not receive the usual SDL events for this solution. With C, SDL still catches SIGTERM and converts it to a SDL_QUIT event; Python however catches SIGTERM first. The ticks also work.

Gtk socket

Using Gtk socket is mentioned in the SDL_WINDOWID section, because you can put SDL in a socket by passing the socket id to SDL_WINDOWID.

The other way around, does not involved SDL_WINDOWID, somewhat works too: passing the SDL window ID (pygame.display.get_wm_info().get("window")) to a Gtk socket (socket.add_id()). Since you "steal" SDL out of an existing window, you get a now-empty original SDL window lying around.

1 implies input can work with the Gtk socket technique. According to my tests, the window events are handled by the original empty window, and the mouse / keyboard events by the stolen graphics display in the socket window.

Use MDI instead of SDI

That is, Multiple Document Interface instead of Single Document Interface (think Gimp vs. Photoshop).

You initialize SDL as usual, and call your favorite toolkit to create toolbars around the main SDL window.

See the double loop section for examples and limitations.

Double event loop issue

When combining SDL and a GUI, and where your technique supports SDL events, there will be two event loops. Both need to run at the same time!

Work-arounds:

You'll need to read the events if you want SDL to refresh the window when it's repainted (hidden behind another window, etc.).

putenv or SDL_putenv?

Apparently SDL_putenv takes care of tricky situations where the environment is not the same in the application and in the library: http://lists.libsdl.org/htdig.cgi/sdl-libsdl.org/2005-September/051604.html

Not tested though.

FAQ_GUI (last edited 2009-03-13 19:48:59 by j38ju)