A MonoGame+GTK multiwindow sample application

 

---------------- Edited on 11/08/2012 ----------------

 

 

Solved the shutdown problem.

 

Updated both the article and MonoDevelop source code.

 

 

------------------------------------------------------

 

The premise

I am planning a lightweight, very specialized, multiplatform, isometric game modeler, that should share the rendering engine with a future game. At the moment I’m investigating the best technology to use and one option was obviously Unity, but I want to use screen space effects in rendering, possibly for free. Another possibility was XNA+WinForms, but the idea of making a MonoGame application from scratch, instead of porting an existing one, has been tickling me for a while, and I set out to test the MonoGame+GTK combination.

In the past week end I googled around a bit and it turns out that having MonoGame render in a GTK widget is fairly complex and a task still open. So I tried a Gimp styled multiwindow approach, with the MonoGame window separated from the GTK toolbox, and it seems to be a quite simple way to follow. Here are the results of my experiment (windows only, for now):

The MonoGame+GTK sample in action

The application shows a MonoGame window with a rotating cube, and a GTK panel with controls to change some aspects of the simple scene.

The setup

I made my experiments with MonoDevelop 3.0.4.7 and MonoGame 3.0 beta, on a Windows 7 laptop. The basis was a MonoGame application with GTK support enabled in the wizard.

At this point you can normally right click your project and add a GTK window or dialog to be set up with your controls (mine is called ToolsWindow). Next, I will show you the code to attach this window/dialog to the MonoGame main class.

The GTK window system management

The trick here is to have both the Gtk window and the gtk update cycle on a different thread from the MonoGame one. So, for sake of clarity, let’s define a separate class to handle this task:

public class GtkThread
{
    private GtkApp theApp;
    private Thread theThread;

    // The Gtk window
    private ToolsWindow toolsWindow;
    public ToolsWindow ToolsWindow
    {
        get { return toolsWindow; }
    }

    public bool IsInitialized { get; private set; }
    private bool wantsToStop = false;
		
    public GtkThread (GtkApp app)
    {
        theApp = app;
        IsInitialized = false;
    }

    // Create and start the thred object
    public void Start ()
    {
        theThread = new Thread(new ThreadStart(RunLoop));
        theThread.Start();
    }

    // Ask the main loop to exit gracefully
    public void Stop ()
    {
        wantsToStop = true;
    }

    // This is the Gtk thread's main loop
    public void RunLoop()
    {
        while (true) {
            // Check if the thread was asked to stop
            if(!wantsToStop)
            {
                if(!IsInitialized)
                {
                    // Initialize GTK application
                    Gtk.Application.Init();

                    // Create and show our GTK window
                    toolsWindow = new ToolsWindow(theApp);
                    toolsWindow.Show();

                    IsInitialized = true;
                }

                Gtk.Application.RunIteration (false);
            }
            else
            {
                // Exit the thread gracefully
                if(IsInitialized)
                {
                    // Close Gtk
                    Gtk.Application.Quit();
                    IsInitialized = false;
                }

                // Break out the main loop and end the thread
                break;
            }

            // Let's make the loop pause a bit
            System.Threading.Thread.Sleep(20);
        }
    }
}

The Gtk window is defined as a member and is accessible through a public property. The core of this class is the RunLoop method, which contains the actual code running on the thread. It’s a continuous loop interrupted with a brief Sleep to let the main MonoGame thread do it’s stuff. On each iteration we first check to see if the thread was asked to stop (you never have to stop a thread abruptly), in which case, if the thread had been able to initialize, we proceed to shut down the Gtk framework with a call to Gtk.Application.Quit(), then we break the main loop to actually end the thread. On the first run, the main loop initializes the framework with Gtk.Application.Init() and then creates and shows the Gtk window. Then, on every iteration, it calls Gtk.Application.RunIteration(false) which tells Gtk to have a turn and then return the control to the caller without blocking the execution.

Another thing we need to do is to create and launch the Gtk thread in our MonoGame class, and we do this in the constructor:

// Create and start the Gtk thread
gtkThread = new GtkThread(this);
gtkThread.Start();

Finally, you have to let Gtk do it’s cleanup, when exiting the game. To do this, override the OnExiting method of your Game class, and be sure to call the following lines in it:

// Ask the Gtk thread to stop...
gtkThread.Stop();
// ...and wait for the main loop to actually end.
while(gtkThread.IsInitialized);

The while loop is needed to avoid exiting the Game before the Gtk thread actually stops.
And that’s it. This is the minimum effort required to let Gtk run in parallel with your MonoGame app. Next we need to set up some sort of communication between the two, to have a real interaction.

The communications

At this point I want to use the Gtk widgets to actually influence the scene in the MonoGame window. We need to use the Gtk signals, and for my experiment I went for a simple and direct approach. We need a two way communication: the Game class must be able to set the values of the widgets at initialization time, for example, to set the toolbox to the default values. Most importantly, the Gtk widgets must change the scene parameters when the user interacts with them.

To simplify, I will show the code for the xRotation scrollbar, which influences the rotation speed of the cube around the X axis. First I will need a reference to the Game class in the ToolWindow class, to be set up at construction time. Then I have a property in my Game class named XRotationSpeed which is used in the update loop to let the cube spin around the X axis. Finally, in the ToolWindow class, I defined the following method:

public void SetXRotationSpeed (float rot)
{
    xRotation.Value = rot;
}

This can be used to set the scrollbar value directly, and I call it in the Initialize method of the Game class. At this point we only need a handler to deal with the update ov the scrollbar’s value. This is easily done in the properties box of the widget, in the GTK visual editor inside MonoDevelop. In the signals panel, write the handler’s name in the Range->ValueChanged signal box and the editor will create a default handler. Then go to the code and substitute the default handler with something like this:

protected void xRotationChanged (object sender, EventArgs e)
{
    theGame.XRotationSpeed = (float)xRotation.Value;
}

This will automatically update the Game class property whenever the user moves the corresponding scrollbar. The same procedure can be used for any kind of widget, thus potentially altering any kind of data in your scene.

The complete code

I have shown only the important bits of code in this post, and if you want a complete view of the experiment or if you are just curious to try this little toy I made, you can find the complete MonoDevelop solution at this link.

4 thoughts on “A MonoGame+GTK multiwindow sample application”

  1. Hello I was just wondering if you ever got Monogame working in one window with GTK#,
    Thank you

  2. Hello, Kevin.
    Sorry, but no. At the time of this experiment I found no way of doing this and then I never got back on this subject.

  3. Nice Idea but Gtk Sharp 3.22.6.4 has problem with wrong color.

    I really develop GLWidget ( using GLArea ) with OpenTK but i have tried to fix with real color but it still wrong color.

    Red, Green, Blue, Alpha ( Color4)

    Check out https://youtu.be/X_uYkNNZ0hw

    gtk-sharp-3 ( 3.22.6.4 ) from nuget
    OpenTK, ( latest OpenTK Development ) from myget
    GLWidget from Github – Reason OpenTK’s gtk sharp is wrong – i need copy manual GLWidget.cs to my project

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.