XNA to MonoGame – ‘Full-Screen’ Window

The purpose of this post is to give an explanation of how to make your XNA and MonoGame game window faux full-screen, that is to say borderless window full-screen. The example codes used below are from a game called Space Salvager, which was originally created in XNA and has been ported to MonoGame.

XNA

The ‘full-screen‘ mode used in Space Salvager is actually a borderless window that’s the same size as the screen, this was done because toggling the game into full-screen always caused an exception to be thrown.  Moreover, I personally hate true full-screen because I like to alt-tab out into other windows while playing games.

The code for this originally came from another blog, unfortunately this was about two years ago and I can’t find the source anymore: sorry.  The first thing is to import some methods from external libraries:

using System.Runtime.InteropServices;

[DllImport("user32.dll")]
private extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndIntertAfter, 
    int X, int Y, int cx, int cy, int uFlags);

[DllImport("user32.dll")]
private extern int GetSystemMetrics(int Which);

private const int SM_CXSCREEN = 0;
private const int SM_CYSCREEN = 1;
private IntPtr HWND_TOP = IntPtr.Zero;
private const int SWP_SHOWWINDOW = 64;

public int ScreenX
{
	get { return GetSystemMetrics(SM_CXSCREEN); }
}
public int ScreenY
{
	get { return GetSystemMetrics(SM_CYSCREEN); }
}

The constants and accessors used could just be rolled into your full-screen function, but that really depends on your implementation and how you want to use it: that being said using constants is good practice.

The second thing to do is to modify the game window’s width and height, setting them to the same dimensions as the current screen:

public void FullScreen(SalvagerGame game)
{
    /* Set the resolution to the native resolution */
    Screen screen = Screen.FromHandle(game.Window.Handle);
    screen = screen == null ? Screen.PrimaryScreen : screen;
	
    SalvagerGame.GraphicsDeviceManager.PreferredBackBufferWidth = screen.Bounds.Width;
    SalvagerGame.GraphicsDeviceManager.PreferredBackBufferHeight = screen.Bounds.Height;
    SalvagerGame.GraphicsDeviceManager.ApplyChanges();

The game class used in Space Salvager is ‘SalvagerGame’ which is where the GraphicsDeviceManager is stored, but as long as you have access to the GraphicsDeviceManager where it may be it will work. If you want the option of toggling between ‘full-screen‘ and ‘windowed’ it might be prudent to store the current width and height now, so you can set the window back to ‘windowed’ with the same dimensions later.

The last thing to do is to change the window itself to be ‘full-screen‘, which in this case means borderless, maximised and in the left-hand corner.

    /* Set fullscreen */
    Form.FromHandle(game.Window.Handle).FindForm().WindowState = FormWindowState.Maximized;
    Form.FromHandle(game.Window.Handle).FindForm().FormBorderStyle = FormBorderStyle.None;
    Form.FromHandle(game.Window.Handle).FindForm().TopMost = true;
    SetWindowPos(game.Window.Handle, HWND_TOP, 0, 0, ScreenX, ScreenY, SWP_SHOWWINDOW);
}

Yes, I highlighted all the rows on for that example.

MonoGame

Running this code under MonoGame throws an exception, this happens because the OpenTK window doesn’t return the Form correctly. The solution to this I found fairly recently and therefore still have to link to it: voila. The fix requires you to use Reflection, which if you’ve not used before I highly recommend checking out because it’s incredibly powerful.

The workaround for this involves two changes, firstly extending the GameWindow class – not done any extensions before? They’re pretty cool, go practice them.

public static OpenTK.GameWindow GetForm(this GameWindow gameWindow)
{
    Type type = typeof(OpenTKGameWindow);
    FieldInfo field = type.GetField("window", 
        BindingFlags.NonPublic | BindingFlags.Instance);
    if (field == null)
    {
        return null;
    }
    else
    {
        return field.GetValue(gameWindow) as OpenTK.GameWindow;
    }
}

With this we can retrieve the OpenTK game window and use this without throwing an exception. The last thing to do is modify how we set the window ‘full-screen‘:

    /* Set fullscreen */
    game.Window.IsBorderless = true;
    game.Window.SetPosition(Microsoft.Xna.Framework.Point.Zero);
    SetWindowPos(game.Window.Handle, HWND_TOP, 0, 0, ScreenX, ScreenY, SWP_SHOWWINDOW);
}

Here we’re not changing the window state or form border style, but instead simply setting the window ‘IsBorderless’ to true and manually setting the position to the top-left. With those changes you should now be able to turn your MonoGame window ‘full-screen‘.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s