简体   繁体   中英

SFML crashes when closing with wxWidgets

I am using an SFML view integrated inside a wxWidgets frame. I used the code sample on the SFML website (which is quite old but I got it to work making a few tweaks) to do this. And then started fleshing out my project from that base class. However, I am at the stage now where I need to create and delete many SFML+wxWidgets windows based on user actions, however SFML crashes whenever I close its parent wxWidgets window. I get the following error:

Cannot close SFML area when SFML is integrated in a NSView.

All the SFML+wxWidgets examples on the web I found face this error when I run it after closing. This error should not be an issue if the user only needs to close the window once, but I am managing many windows over the user session, so if it crashes once, it brings down the whole app with it.

Here is the section of the header file code for the base class combining wxWidgets and sfml, everything else is specific to my app, and not to the error:

class ChessWidgetBase : public wxControl, public sf::RenderWindow {
  public:
    ChessWidgetBase(wxWindow* parent, wxSize size); 
    virtual ~ChessWidgetBase() {};
  private:
    DECLARE_EVENT_TABLE()
    virtual void HandleLeftDown(wxMouseEvent&) {}
    virtual void HandleLeftUp(wxMouseEvent&) {}
    virtual void OnUpdate() {};
    void OnIdle(wxIdleEvent&);
    void OnPaint(wxPaintEvent&);
    void OnEraseBackground(wxEraseEvent&);
};

This code is based off this minimum reproducable example I used from the internet to make the above class:

#include <iostream>
#include <wx/wx.h>
#include <memory>
#include <SFML/Graphics.hpp>

#ifdef __WXGTK__
#include <gdk/gdkx.h>
#include <gtk/gtk.h>
#endif

using namespace std;

static const int kDefaultWindowWidth = 1280;
static const int kDefaultWindowHeight = 720;
static const int kCanvasMargin = 50;

struct wxSfmlCanvas : public wxControl, public sf::RenderWindow
{
  wxSfmlCanvas(
    wxWindow *parent = nullptr,
    wxWindowID windowId = -1,
    const wxPoint &position = wxDefaultPosition,
    const wxSize &size = wxDefaultSize,
    long style = 0) :
  wxControl(parent, windowId, position, size, style)
  {
    createRenderWindow();
  }

  virtual void onUpdate(){};

  void onIdle(wxIdleEvent& event)
  {
    // Send a paint message when control is idle, to ensure max framerate
    Refresh();
  }

  void onPaint(wxPaintEvent& event)
  {
    wxPaintDC dc(this);     // Prepare control to be repainted
    onUpdate();             // Tick update
    display();              // Draw
  }

  // Explicitly overriding prevents wxWidgets from drawing, which could result in flicker
  void onEraseBackground(wxEraseEvent& event){}

  void createRenderWindow()
  {
#ifdef __WXGTK__
    gtk_widget_realize(m_wxwindow);
    gtk_widget_set_double_buffered(m_wxwindow, false);

    GdkWindow *gdkWindow = gtk_widget_get_window((GtkWidget*)GetHandle());
    XFlush(GDK_WINDOW_XDISPLAY(gdkWindow));

    sf::RenderWindow::create(GDK_WINDOW_XWINDOW(gdkWindow));
#else
    sf::RenderWindow::create(GetHandle());
#endif
  }

  void setwxWindowSize(const wxSize& size)
  {
    this->SetSize(size);
  }

  void setRenderWindowSize(const sf::Vector2u& size)
  {
    this->setSize(size);
  }

  virtual ~wxSfmlCanvas(){};

wxDECLARE_EVENT_TABLE();
};

struct Canvas : public wxSfmlCanvas
{
  Canvas(
    wxWindow* parent,
    wxWindowID id,
    wxPoint position,
    wxSize size,
    long style = 0) :
  wxSfmlCanvas(parent, id, position, size, style)
  {
  }

  virtual void onUpdate()
  {
    clear(sf::Color::Yellow);

    // TODO: Do some sprite drawing or whatever
  }

  void onResize(wxSizeEvent& event)
  {
    auto size = event.GetSize();

    auto newCanvasWidth = size.x - (2 * kCanvasMargin);
    auto newCanvasHeight = size.y - (2 * kCanvasMargin);

    // Resize Canvas window
    this->setwxWindowSize({newCanvasWidth, newCanvasHeight});
    this->setRenderWindowSize({(unsigned int)newCanvasWidth, (unsigned int)newCanvasHeight});
  }
};

struct AppFrame : public wxFrame
{
  AppFrame(const wxString& title, const wxPoint& pos, const wxSize& size) :
    wxFrame(NULL, wxID_ANY, title, pos, size),
    _panel(new wxPanel(this)),
    _canvas(new Canvas(
      _panel.get(),
      wxID_ANY,
      wxPoint(kCanvasMargin, kCanvasMargin),
      wxSize(kDefaultWindowWidth - (2 * kCanvasMargin), kDefaultWindowHeight - (2 * kCanvasMargin))
    ))
  {
    _panel->SetBackgroundColour(*wxCYAN);

    ////////////////////////////////////////////////////////////////////////////////
    // Probably due to some RTTI, IDE is getting confused by this dynamic call
    // and doesn't understand the correct Bind overload. Causes non sequitur errors
    // to display in the inspector. Suppress.
    //
    // Dynamic binding is cleanest here, since we don't want to hook up our resize
    // handler until our dependencies (Canvas namely) have finished their initialization
    ////////////////////////////////////////////////////////////////////////////////
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wint-conversion"
    Bind(wxEVT_SIZE, &AppFrame::onResize, this);
    #pragma clang diagnostic pop
    ////////////////////////////////////////////////////////////////////////////////

  }

  void onResize(wxSizeEvent& event)
  {
    _canvas->onResize(event);
    event.Skip();
  }

  unique_ptr<wxPanel> _panel;
  unique_ptr<Canvas> _canvas;
};

struct App : public wxApp
{
  virtual bool OnInit()
  {
    auto frame = new AppFrame("SFML Canvas Demo", wxPoint(100, 100),
                              wxSize(kDefaultWindowWidth, kDefaultWindowHeight));
    frame->Show(true);
    return true;
  }
};

wxBEGIN_EVENT_TABLE(wxSfmlCanvas, wxControl)
  EVT_IDLE(wxSfmlCanvas::onIdle)
  EVT_PAINT(wxSfmlCanvas::onPaint)
  EVT_ERASE_BACKGROUND(wxSfmlCanvas::onEraseBackground)
wxEND_EVENT_TABLE()

wxIMPLEMENT_APP(App);

(if you want to run it you might have to install sfml+wxwidgets) Any ways to handle closing a window that prevents a crash with wxWidgets+SFML? Just need some ideas and a couple lines of code to show them, no need for complete examples...

To fix this error upgrade SFML to version >= 2.6.0. You cannot find this in the downloads site for SFML as it hasn't been released yet, so you must install from the Github directly over here: https://github.com/SFML/SFML/tree/2.6.x .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM