I’m trying to embed SDL in a wxWidgets panel. I had a perfect working
application in Windows XP.
However, it failed to work at all in Linux/GTK+ 2. It would always exit
with BadWindow error. I couldn’t find anything that definitively said
that the SDL_WINDOWID trick couldn’t be used with non top-level windows,
but there were some questions about whether this was possible or not.
I decided to abandon the panel for now and simply draw on the frame. I
got rid of the panel. The application once again works perfectly in
Windows, but is useless in Linux/GTK+ 2. Although the application
doesn’t crash and starts up, it never draws on the frame.
The gtk-demo works just fine. I had to change one line to get it to
compile under GTK+ 2.0, but it works fine after that:
// this no longer seems to exist in GTK+ 2.0
// gtk_accel_group_attach(accel_group, GTK_OBJECT(window));
gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);
Here is my source. My application in Linux with wxWidgets is using GTK,
so I do not understand why the gtk-demo works, but my program does not.
If you comment out the putenv(s) line, the program will run, but the SDL
output will be in another window. I want it to co-opt my frame. The
wxFrame is a gtk top-level window.
/*******************************************************************************
// Headers
*******************************************************************************/
#include
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include “SDL.h”
#if defined(WXGTK)
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#endif
/*******************************************************************************
// Global Declarations
*******************************************************************************/
enum {
ID_FRAME = 10000,
IDM_FILE_EXIT,
IDM_HELP_ABOUT
};
/*******************************************************************************
// SDLFrame Class
*******************************************************************************/
DECLARE_EVENT_TYPE(EVT_PAINT_SDL_FRAME, 0)
class SDLFrame : public wxFrame {
DECLARE_CLASS(SDLFrame)
DECLARE_EVENT_TABLE()
private:
SDL_Surface *screen;
/**
* Called when exit from the file menu is selected.*
* @param event The associated wxCommandEvent (unused).
*/
void onFileExit(wxCommandEvent &event);
/**
* Called when about from the help menu is selected.
*
* @param event The associated wxCommandEvent (unused).
*/
void onHelpAbout(wxCommandEvent &event);
/**
* Called to paint the frame.
*
* @param event The associated wxCommandEvent (unused).
*/
void onPaint(wxCommandEvent &event);
public:
/**
* Creates a new SDLFrame.
*/
SDLFrame();
/**
* Gets the native window ID for this SDLFrame.
*
* @return The native window ID.
*/
long getWindowID() const;
/**
* Sets the SDL_Surface to use with this SDLFrame.
*
* @param screen The new SDL_Surface.
*/
void setScreen(SDL_Surface *screen);
};
inline void SDLFrame::onFileExit(wxCommandEvent &) { Close(); }
inline long SDLFrame::getWindowID() const {
#if defined(WXMSW)
return reinterpret_cast(GetHandle());
#elif defined(WXGTK)
return GDK_WINDOW_XWINDOW(
(static_cast<GtkWidget*>(GetHandle()))->window
);
#elif defined(WXOSX)
#error “We don’t yet work on OS-X. Sorry.”
#else
#error “SDLFrame only works on Windows, GTK, and OS-X.”
#endif
}
inline void SDLFrame::setScreen(SDL_Surface *screen) {
this->screen = screen;
}
IMPLEMENT_CLASS(SDLFrame, wxFrame)
BEGIN_EVENT_TABLE(SDLFrame, wxFrame)
EVT_MENU(IDM_FILE_EXIT, SDLFrame::onFileExit)
EVT_MENU(IDM_HELP_ABOUT, SDLFrame::onHelpAbout)
EVT_COMMAND(ID_FRAME, EVT_PAINT_SDL_FRAME, SDLFrame::onPaint)
END_EVENT_TABLE()
DEFINE_EVENT_TYPE(EVT_PAINT_SDL_FRAME)
SDLFrame::SDLFrame() : screen(NULL) {
// Create the SDLFrame
Create(NULL, ID_FRAME, wxT(“Frame Title”), wxDefaultPosition,
wxSize(640, 480), wxCAPTION | wxSYSTEM_MENU |
wxMINIMIZE_BOX | wxCLOSE_BOX);
// create the main menubar
wxMenuBar *mb = new wxMenuBar;
// create the file menu
wxMenu *fileMenu = new wxMenu;
fileMenu->Append(IDM_FILE_EXIT, wxT("E&xit"));
// add the file menu to the menu bar
mb->Append(fileMenu, wxT("&File"));
// create the help menu
wxMenu *helpMenu = new wxMenu;
helpMenu->Append(IDM_HELP_ABOUT, wxT("About"));
// add the help menu to the menu bar
mb->Append(helpMenu, wxT("&Help"));
// add the menu bar to the SDLFrame
SetMenuBar(mb);
}
void SDLFrame::onHelpAbout(wxCommandEvent &) {
wxMessageBox(wxT(“wx-sdl tutorial\nCopyright © 2005 John Ratliff”),
wxT(“about wx-sdl tutorial”),
wxOK | wxICON_INFORMATION);
}
void SDLFrame::onPaint(wxCommandEvent &) {
printf(“calling onPaint()…\n”);
// Lock surface if needed
if (SDL_MUSTLOCK(screen)) {
if (SDL_LockSurface(screen) < 0) {
return;
}
}
printf("locked!\n");
// Ask SDL for the time in milliseconds
int tick = SDL_GetTicks();
// Declare y offset variable
int yofs = 0;
for (int i = 0; i < 480; i++) {
for (int j = 0, ofs = yofs; j < 640; j++, ofs++) {
((unsigned int *)screen->pixels)[ofs] =
i * i + j * j + tick;
}
yofs += screen->pitch / 4;
}
printf("drawing...\n");
// Unlock if needed
if (SDL_MUSTLOCK(screen)) {
SDL_UnlockSurface(screen);
}
printf("unlock!\n");
// Tell SDL to update the whole screen
SDL_UpdateRect(screen, 0, 0, 640, 480);
printf("goodbye!\n");
fflush(stdout);
}
/*******************************************************************************
// DisplayThread Class
*******************************************************************************/
class DisplayThread : public wxThread {
private:
SDLFrame &frame;
bool running;
/**
* The thread code. Called by wxThread::Run().
*/
void *Entry();
public:
/**
* Creates a new DisplayThread.
*
* @param frame The SDLFrame to talk to.
*/
DisplayThread(SDLFrame &frame);
/**
* Stops this thread.
*/
void stop();
};
inline DisplayThread::DisplayThread(SDLFrame &frame) : wxThread(),
frame(frame),
running(true) {}
inline void DisplayThread::stop() { running = false; }
void *DisplayThread::Entry() {
// event used to tell the frame to paint itself
wxCommandEvent event(EVT_PAINT_SDL_FRAME, frame.GetId());
event.SetEventObject(&frame);
// while we're still going
while (running) {
printf("running...\n");
// ask the frame to paint itself
frame.AddPendingEvent(event);
// throttle to avoid flooding the SDLFrame with paint events
wxMilliSleep(33);
}
// unused thread return value
return NULL;
}
/*******************************************************************************
// SDLApp Class
*******************************************************************************/
class SDLApp : public wxApp {
DECLARE_CLASS(SDLApp)
private:
SDLFrame *frame;
DisplayThread *thread;
public:
/**
* Called to initialize this SDLApp.
*
* @return true if initialization succeeded; false otherwise.
*/
bool OnInit();
/**
* Called to run this SDLApp.
*
* @return The status code (0 if good, non-0 if bad).
*/
int OnRun();
/**
* Called when this SDLApp is ready to exit.
*
* @return The exit code.
*/
int OnExit();
};
bool SDLApp::OnInit() {
// create the SDLFrame
frame = new SDLFrame;
frame->Show();
// Our SDLFrame is the Top Window
SetTopWindow(frame);
// initialization should always succeed
return true;
}
int SDLApp::OnRun() {
{
// hack to make SDL use our SDLFrame for drawing
char s[81];
sprintf(s, "SDL_WINDOWID=%ld", frame->getWindowID());
putenv(s);
}
// initialize SDL
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
std::cerr << "unable to init SDL: " << SDL_GetError() << '\n';
return -1;
}
// create the SDL_Surface for our SDLFrame
SDL_Surface *screen;
if ((screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE)) == NULL) {
std::cerr << "unable to set video mode (640x480): " <<
SDL_GetError() << ‘\n’;
return -1;
}
frame->setScreen(screen);
// start the DisplayThread to tell the SDLFrame to paint
thread = new DisplayThread(*frame);
thread->Create();
thread->Run();
// start the main loop
return wxApp::OnRun();
}
int SDLApp::OnExit() {
// stop the DisplayThread
thread->stop();
// cleanup SDL
SDL_Quit();
// return the standard exit code
return wxApp::OnExit();
}
IMPLEMENT_CLASS(SDLApp, wxApp)
IMPLEMENT_APP(SDLApp)
Thanks,
–John Ratliff