IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

GTK+ avec C & C++ Discussion :

Intégration de la SFML dans Gtkmm, suppression/recréation de widgets


Sujet :

GTK+ avec C & C++

  1. #1
    Candidat au Club
    Profil pro
    Inscrit en
    Mars 2011
    Messages
    2
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2011
    Messages : 2
    Par défaut Intégration de la SFML dans Gtkmm, suppression/recréation de widgets
    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 :
    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.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
    #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
    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
    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.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
    #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
    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
    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 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
    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 le lancer, il suffit donc de faire ça :

    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
    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.)
    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)...

    Merci d'avance à ceux qui pourront m'aider... Tout est bon à prendre : une solution, une piste, ou une vague idée...

  2. #2
    Candidat au Club
    Profil pro
    Inscrit en
    Mars 2011
    Messages
    2
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2011
    Messages : 2
    Par défaut
    J'ai modifié ce programme de test en recréant totalement le SFMLWidget (un mélange de deux codes différents).
    L'ennui c'est que j'ai rajouté des bugs... Mais d'un autre côté c'est un peu mieux quand même.

    Je m'explique, dans le cadre de mon projet, lorsque je clique sur un des "boutons SFML", ces boutons sont bien supprimés et le SFMLWidget qui gère le jeu est bien créé (alors qu'avant, c'était plantage direct avec l'erreur de X donnée plus haut).
    Des fois ça plante quand même mais pas de la même façon (voir plus bas).

    Avant de parler sur ce nouveau programme test, je vous en donne les fichiers :

    main.cpp
    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;
    }
    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
    #ifndef HEADER_SFMLWIDGET
    #define HEADER_SFMLWIDGET
     
    #include <SFML/Graphics.hpp>
    #include <sigc++/functors/slot.h>
    #include <glibmm/objectbase.h>
    #include <glibmm/refptr.h>
    #include <gdkmm/window.h>
    #include <gtkmm/drawingarea.h>
    #include <gdk/gdkx.h>
     
    class SFMLWidget : public Gtk::DrawingArea, public sf::RenderWindow
    {
    	public:
    		// Constructeur
    		SFMLWidget(const sigc::slot<void>& on_idle);
     
    	protected:
    		// Fonctions de rappel
    		virtual void on_size_allocate(Gtk::Allocation& allocation);
    		virtual void on_map();
    		virtual void on_unmap();
    		virtual void on_realize();
    		virtual void on_unrealize();
     
    		// Stockage de la fenêtre GDK
    		Glib::RefPtr<Gdk::Window> m_gdk_window;
    };
     
    #endif
    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
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    #include "./SFMLWidget.hpp"
     
    SFMLWidget::SFMLWidget(const sigc::slot<void>& on_idle) :
    	Gtk::DrawingArea(), sf::RenderWindow()
    {
    	set_has_window(true);
    	set_can_focus(true);
     
    	Glib::signal_idle().connect(sigc::bind_return(on_idle, true));
     
    	UseVerticalSync(true);
    }
     
    void SFMLWidget::on_size_allocate(Gtk::Allocation& allocation)
    {
    	set_allocation(allocation);
     
    	if (m_gdk_window)
    	{
    		m_gdk_window->move_resize(allocation.get_x(), allocation.get_y(), allocation.get_width(), allocation.get_height());
    		Create(GDK_WINDOW_XID(m_gdk_window->gobj()));
    	}
    }
     
    void SFMLWidget::on_map()
    {
    	Gtk::Widget::on_map();
    }
     
    void SFMLWidget::on_unmap()
    {
    	Gtk::Widget::on_unmap();
    }
     
    void SFMLWidget::on_realize()
    {
    	set_realized();
     
    	if (!m_gdk_window)
    	{
    		GdkWindowAttr attributes;
    		memset(&attributes, 0, sizeof(attributes));
     
    		Gtk::Allocation allocation = get_allocation();
     
    		attributes.x = allocation.get_x();
    		attributes.y = allocation.get_y();
    		attributes.width = allocation.get_width();
    		attributes.height = allocation.get_height();
     
    		attributes.event_mask = get_events() | Gdk::EXPOSURE_MASK;
    		attributes.window_type = GDK_WINDOW_CHILD;
    		attributes.wclass = GDK_INPUT_OUTPUT;
     
    		m_gdk_window = Gdk::Window::create(get_parent_window(), &attributes, GDK_WA_X | GDK_WA_Y);
    		set_window(m_gdk_window);
     
    		m_gdk_window->set_user_data(gobj());
     
    		set_double_buffered(false);
     
    		Create(GDK_WINDOW_XID(m_gdk_window->gobj()));
    	}
    }
     
    void SFMLWidget::on_unrealize()
    {
    	m_gdk_window.reset();
    	Gtk::Widget::on_unrealize();
    }
    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
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    #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();
    		void OnIdle();
     
    	private:
    		Gtk::VBox* m_vbox;
    		Gtk::VBox* m_vbox_sfml;
    		SFMLWidget* m_sfml_widget;
    		Gtk::Button* m_button;
    		int m_r, m_g, m_b;
    };
     
    #endif
    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
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    #include <gdkmm/rectangle.h>
     
    #include "./MainWindow.hpp"
    #include "./SFMLWidget.hpp"
     
    MainWindow::MainWindow() :
    	Gtk::Window(), m_r(0), m_g(150), m_b(255)
    {
    	set_size_request(500, 300);
     
    	m_vbox = Gtk::manage(new Gtk::VBox(false, 10));
    	add(*m_vbox);
     
    	m_vbox_sfml = Gtk::manage(new Gtk::VBox(false, 0));
    	m_vbox->pack_start(*m_vbox_sfml, Gtk::PACK_EXPAND_WIDGET);
     
    	m_sfml_widget = new SFMLWidget(sigc::mem_fun(*this, &MainWindow::OnIdle));
    	m_vbox_sfml->pack_start(*m_sfml_widget, Gtk::PACK_EXPAND_WIDGET);
    	m_sfml_widget->show();
     
    	m_button = Gtk::manage(new Gtk::Button("Bonjour, je casse tout :)"));
    	m_button->signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::Test));
    	m_vbox->pack_start(*m_button, Gtk::PACK_SHRINK);
     
    	show_all();
    }
     
    MainWindow::~MainWindow()
    {
    	delete m_sfml_widget;
    }
     
    void MainWindow::Test()
    {
    	m_vbox_sfml->remove(*m_sfml_widget);
    	delete m_sfml_widget;
     
    	m_r = sf::Randomizer::Random(0, 255);
    	m_g = sf::Randomizer::Random(0, 255);
    	m_b = sf::Randomizer::Random(0, 255);
     
    	m_sfml_widget = new SFMLWidget(sigc::mem_fun(*this, &MainWindow::OnIdle));
    	m_vbox_sfml->pack_start(*m_sfml_widget, Gtk::PACK_EXPAND_WIDGET);
    	m_sfml_widget->show();
    }
     
    void MainWindow::OnIdle()
    {
    	m_r = sf::Randomizer::Random(0, 255);
    	m_g = sf::Randomizer::Random(0, 255);
    	m_b = sf::Randomizer::Random(0, 255);
    	m_sfml_widget->Clear(sf::Color(m_r, m_g, m_b));
    	m_sfml_widget->Display();
    }
    Donc je recense 4 bugs différents, dont un qui est un peu moins grave (mais qui me gène quand même).
    Commençons donc par le premier que j'ai remarqué (le moins grave). À chaque fois que je supprime un conteneur qui contient des SFMLWidget, il apparait et pour chaque SFMLWidget il me balance ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    (test_sfml:4615): Gtk-CRITICAL **: gtk_style_detach: assertion `style->attach_count > 0' failed
    Le programme ne plante pas, dans mon projet il continue à tourner normalement. La fenêtre étant un conteneur, ce bug apparait également quand on la ferme...

    Les 3 bugs suivants apparaissent quand on veut recréer un SFMLWidget (au clic sur le bouton).
    Le premier des trois, c'est évidemment la belle erreur de X déjà donnée plus haut, mais si elle vous manque la voila :
    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 2908 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.)
    Le deuxième bug, c'est ce qui m'intrigue le plus. Quand on clique sur le bouton (et qu'on évite le bug de X pour une raison inconnue), normalement le SFMLWidget présent est supprimé et un nouveau est recréé.
    Comme vous pouvez le voir dans le code, je connecte une fonction au signal idle qui me permet de gérer les animations. Cette fonction est la même pour le premier SFMLWidget et les autres, son comportement doit donc être aussi le même.
    Cependant, si vous avez lancé le programme vous constatez que le widget se fige, l'animation n'est plus... Et pourtant, un test avec std::cout m'a montré que la fonction MainWindow::OnIdle est bien appelée !

    Le troisième bug apparait lorsque l'on clique un certain nombre de fois sur le bouton (dans mon projet, des fois il apparait, des fois non). C'est une erreur qui stoppe tout affichage de la SFML (comme s'il n'y avait pas de widget) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    (test_sfml:4807): Gtk-CRITICAL **: gtk_style_detach: assertion `style->attach_count > 0' failed
    (test_sfml:4807): Gdk-WARNING **: GdkWindow 0x5000187 unexpectedly destroyed
    Et quand on ferme la fenêtre juste après, on obtient ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    (test_sfml:4807): Gdk-CRITICAL **: gdk_window_hide: assertion `GDK_IS_WINDOW (window)' failed
    (test_sfml:4807): Gtk-CRITICAL **: gtk_style_detach: assertion `style->attach_count > 0' failed
    (test_sfml:4807): Gdk-CRITICAL **: gdk_window_set_user_data: assertion `GDK_IS_WINDOW (window)' failed
    (test_sfml:4807): Gdk-CRITICAL **: _gdk_window_destroy_hierarchy: assertion `GDK_IS_WINDOW (window)' failed
    (test_sfml:4807): GLib-GObject-CRITICAL **: g_object_unref: assertion `G_IS_OBJECT (object)' failed
    (test_sfml:4807): Gtk-CRITICAL **: gtk_style_detach: assertion `style->attach_count > 0' failed
    (test_sfml:4807): Gtk-CRITICAL **: gtk_style_detach: assertion `style->attach_count > 0' failed
    (test_sfml:4807): Gtk-CRITICAL **: gtk_style_detach: assertion `style->attach_count > 0' failed
    La dernière ligne est répétée plusieurs fois, on reconnait le premier bug que j'ai cité et il est en fait répété autant de fois qu'on a créé de SFMLWidget... Alors que pourtant avec un delete le widget est censé ne plus exister non ?

    Encore une fois tout est bon à prendre... Même si vous pensez n'avoir une idée que pour un seul bug, c'est déjà ça ! Et si ça se trouve, ils sont liés...
    Merci d'avance, là je désespère... Google ne me donne rien... C'est à croire que personne n'a jamais lié la SFML à Gtkmm (alors que le wiki de la SFML anglais en parle pourtant)...

Discussions similaires

  1. Réponses: 5
    Dernier message: 28/02/2006, 15h12
  2. Réponses: 11
    Dernier message: 30/01/2006, 16h26
  3. Intégration des fichier XML dans une base de données MySQL
    Par bebemoundjou dans le forum XQUERY/SGBD
    Réponses: 8
    Dernier message: 25/11/2005, 22h41
  4. Intégration d'un SDK dans Visual Basic
    Par <-NicO-> dans le forum VB 6 et antérieur
    Réponses: 2
    Dernier message: 25/02/2005, 22h53
  5. Réponses: 7
    Dernier message: 30/11/2004, 14h54

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo