Bonjour !
Je développe actuellement un petit jeu. La SFML me plaît plutôt bien mais dans le même temps, des menus avec Gtkmm est un peu mieux.
C'est pourquoi je me suis lancé dans l'intégration de la SFML dans Gtkmm. Après pas mal de temps, j'ai fini par réussir à créer un widget dérivant de Gtk::DrawingArea et de sf::RenderWindow. Ce widget fonctionne très bien, il m'affiche mon dessin SFML et gère très bien les animations que j'y fait (je n'ai pas encore testé du côté de la gestion du clavier... Ça ne fait que commencer).
Bref, le problème n'est donc pas là a priori.
En fait, dans ce projet, j'ai 4 widgets SFML dans 4 boutons. Lorsque l'on clique sur un bouton, une méthode est lancée (bon ça c'est un peu normal... :-°). Cette dernière est censée effacer le menu actuel en le supprimant de la Gtk::VBox qui le contient. Après l'avoir effacé, c'est l'écran de jeu qui doit s'afficher (lancé par la même méthode) à la place du menu.
Jusque là, tout va bien, le menu est effacé, le widget est bien créé mais... Dès que je souhaite l'afficher avec show(), boum ! Une belle erreur (que je donnerai plus bas)...
J'ai beau chercher, je ne trouve rien de bien intéressant (déjà que pour lier la SFML à Gtkmm la doc est quasi-absente alors pour résoudre une erreur...).
C'est pourquoi j'ai écrit un petit programme qui recrée le problème, voici les différents fichiers qui le composent :
main.cpp :
MainWindow.hpp :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13 #include <cstdlib> #include <gtkmm/main.h> #include "./MainWindow.hpp" int main(int argc, char* argv[]) { Gtk::Main app(argc, argv); MainWindow window; Gtk::Main::run(window); return EXIT_SUCCESS; }
MainWindow.cpp :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 #ifndef HEADER_MAINWINDOW #define HEADER_MAINWINDOW #include <gtkmm/window.h> #include <gtkmm/box.h> #include <gtkmm/button.h> class SFMLWidget; class MainWindow : public Gtk::Window { public: MainWindow(); ~MainWindow(); void Test(); private: Gtk::VBox* m_vbox; SFMLWidget* m_sfml_widget; Gtk::Button* m_button; }; #endif
SFMLWidget.hpp :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 #include "./MainWindow.hpp" #include "./SFMLWidget.hpp" MainWindow::MainWindow() : Gtk::Window() { set_border_width(10); m_vbox = Gtk::manage(new Gtk::VBox(false, 10)); add(*m_vbox); m_sfml_widget = new SFMLWidget(150, 150); m_vbox->pack_start(*m_sfml_widget); m_button = Gtk::manage(new Gtk::Button("Test")); m_button->signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::Test)); m_vbox->pack_start(*m_button); show_all(); } MainWindow::~MainWindow() { delete m_sfml_widget; } void MainWindow::Test() { m_vbox->remove(*m_sfml_widget); delete m_sfml_widget; m_sfml_widget = new SFMLWidget(150, 150); m_vbox->pack_start(*m_sfml_widget); m_sfml_widget->show(); }
SFMLWidget.cpp :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 #ifndef HEADER_SFMLWIDGET #define HEADER_SFMLWIDGET #include <SFML/Graphics.hpp> #include <gtkmm/drawingarea.h> #include <gdk/gdkx.h> class SFMLWidget : public Gtk::DrawingArea, public sf::RenderWindow { public: SFMLWidget(const unsigned int width, const unsigned int height); protected: void OnIdle(); virtual void on_realize(); virtual void on_unrealize(); virtual bool on_expose_event(GdkEventExpose* event); }; #endif
Pour ceux qui voudraient tester, voici ce qu'il faut pour compiler tout ça :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 #include "./SFMLWidget.hpp" SFMLWidget::SFMLWidget(const unsigned int width, const unsigned int height) : Gtk::DrawingArea(), sf::RenderWindow() { add_events(Gdk::ALL_EVENTS_MASK); set_can_focus(); set_size_request(width, height); Glib::signal_idle().connect(sigc::bind_return(sigc::mem_fun(*this, &SFMLWidget::OnIdle), true)); } void SFMLWidget::OnIdle() { Clear(sf::Color(0, 150, 255)); Display(); } void SFMLWidget::on_realize() { Gtk::Widget::on_realize(); set_double_buffered(false); Create(GDK_WINDOW_XID(get_window()->gobj())); } void SFMLWidget::on_unrealize() { Close(); Gtk::Widget::on_unrealize(); } bool SFMLWidget::on_expose_event(GdkEventExpose* event) { Display(); return Gtk::Widget::on_expose_event(event); }
Pour le lancer, il suffit donc de faire ça :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 g++ -c main.cpp -o main.o -Wall -g `pkg-config --cflags gtkmm-2.4` g++ -c SFMLWidget.cpp -o SFMLWidget.o -Wall -g `pkg-config --cflags gtkmm-2.4` g++ -c MainWindow.cpp -o MainWindow.o -Wall -g `pkg-config --cflags gtkmm-2.4` g++ main.o SFMLWidget.o MainWindow.o -o test_sfml `pkg-config --libs gtkmm-2.4` -lsfml-graphics -lsfml-window -lsfml-system
Pour provoquer l'erreur, il faut cliquer sur le bouton (il se retrouve en haut du coup, mais c'est normal et on s'en fout un peu, ce n'est qu'un test). Comme vous pourrez le voir, le résultat est assez bizarre... Bref, l'erreur est là mais n'est pas affichée forcément à tous les coups (des fois il faut fermer, relancer, cliquer plusieurs fois sur le bouton...). Mais quand elle est là, elle est exactement la même que celle que j'ai pour mon jeu :
Code : Sélectionner tout - Visualiser dans une fenêtre à part ./test_sfml
Comme elle le conseille, j'ai évidemment cherché du côté de gdb avec la backtrace et cela me mène à la ligne 22 de SFMLWidget.cpp (dans mon programme test). L'ennui c'est que cette même ligne fonctionne parfaitement du premier coup (que ce soit dans mon jeu ou dans ce programme test)...
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9 The program 'test_sfml' received an X Window System error. This probably reflects a bug in the program. The error was 'BadWindow (invalid Window parameter)'. (Details: serial 123811 error_code 3 request_code 2 minor_code 0) (Note to programmers: normally, X errors are reported asynchronously; that is, you will receive the error a while after causing it. To debug your program, run it with the --sync command line option to change this behavior. You can then get a meaningful backtrace from your debugger if you break on the gdk_x_error() function.)
Merci d'avance à ceux qui pourront m'aider... Tout est bon à prendre : une solution, une piste, ou une vague idée...
Partager