Change Screen Mode in GLFW

Monkey Programming Forums/User Modules/Change Screen Mode in GLFW

Skn3(Posted 2013) [#1]
Here is some code to allow screen mode changing in GLFW.

Please note that you must discard all images before changing screen mode and then you must reload them after.

You MUST discard images using image.Discard()

You MUST discard and reloads images used by 3rd party modules such as the mojo default font.

This isn't exactly a clean solution yet but I think in the new Monkey (v67) it will be far easier to implement this using a system similar to blitzmax where the image data is kept in ram as well as video memory. Reloads can then be automated. There is also a DiscardImages() event in the new monkeygame interface which could help.

Would anyone like to implement this code on other targets?

modules/skn3/screenmode.monkey
format_codebox('Strict

Import mojo

#if TARGET = "glfw"
Import "native/screenmode.${TARGET}.${LANG}"

Extern
Class GraphicsMode = "graphicsModeNative"
Field width:Int
Field height:Int
Field depth:Int
End
Extern Private
Function SetGraphicsNative:Void(width:Int, height:Int, depth:Int, fullSreen:Bool) = "SetGraphicsNative"
Function GetGraphicsModesNative:GraphicsMode[] () = "GetGraphicsModesNative"
Function GraphicsModeExistsNative:bool(width:Int, height:Int, depth:Int) = "GraphicsModeExistsNative"
Public
#end

'functions
Function SetGraphics:Void(width:Int, height:Int, depth:Int, fullScreen:Bool)
' --- this will change the resolution of the running app ---
#if TARGET = "glfw"
SetGraphicsNative(width, height, depth, fullScreen)
#end
End

Function GetGraphicsModes:GraphicsMode[] ()
' --- get list of all graphics modes ---
#if TARGET = "glfw"
Return GetGraphicsModesNative()
#else
Return New GraphicsMode[] ()
#end
End

Function GraphicsModeExists:Bool(width:Int, height:Int, depth:Int)
' --- return true if a graphics mode exists ---
#if TARGET = "glfw"
Return GraphicsModeExistsNative(width, height, depth)
#else
Return False
#end
End')

modules/skn3/screenmode/native/screenmode.glfw.cpp
format_codebox('#define MAX_NUM_MODES 400

//header
class graphicsModeNative;
static Array<graphicsModeNative* > GetGraphicsModesNative();

//classes
class graphicsModeNative : public Object{
public:
int width;
int height;
int depth;

graphicsModeNative(int width,int height,int depth);
};

graphicsModeNative::graphicsModeNative(int width,int height,int depth):width(width),height(height),depth(depth){}

//functions
static void SetGraphicsNative(int width, int height, int depth, bool fullScreen) {
// --- change the graphics resolution ---
int x,y;
int redBits,greenBits,blueBits,alphaBits,stencilBits;

//work out the bits
switch(depth) {
case 0:
//use desktop settings
fullScreen = false;
redBits = 0;
greenBits = 0;
blueBits = 0;
alphaBits = 0;
break;
case 16:
//16bit mode we give priority to green bit
redBits = 5;
greenBits = 6;
blueBits = 5;
alphaBits = 0;
break;
case 24:
case 32:
//fix 24bits for potential later cross compatability (faking alpha bits)
redBits = 8;
greenBits = 8;
blueBits = 8;
alphaBits = 0;
break;
}

//get position of new window based on mode
if (fullScreen == false) {
//windowed mode
GLFWvidmode desktopMode;
glfwGetDesktopMode(&desktopMode);
x = (desktopMode.Width-width)/2;
y = (desktopMode.Height-height)/2;
} else {
//desktop mode
x = 0;
y = 0;
}

//close old window
glfwCloseWindow();

//open new window
glfwOpenWindowHint(GLFW_WINDOW_NO_RESIZE,GL_TRUE);
if(!glfwOpenWindow(width,height,redBits,greenBits,blueBits,alphaBits,CFG_OPENGL_DEPTH_BUFFER_ENABLED ? 32 : 0,stencilBits,fullScreen ? GLFW_FULLSCREEN : GLFW_WINDOW)){ //width,height,redbits,greenbits,bluebits,alphabits,depthbits,stencilbits,mode
Error("glfwOpenWindow failed");
}

//put window position
glfwSetWindowPos(x,y);

//enable the cursor
glfwEnable(GLFW_MOUSE_CURSOR);

//modify the dimensions of the app
app->graphics->width = width;
app->graphics->height = height;

//setup new window
glfwSetWindowTitle(_STRINGIZE(CFG_GLFW_WINDOW_TITLE));

//setup glfw
glfwEnable(GLFW_KEY_REPEAT);
glfwDisable(GLFW_AUTO_POLL_EVENTS);
glfwSetKeyCallback(gxtkApp::OnKey);
glfwSetCharCallback(gxtkApp::OnChar);
glfwSetWindowSizeCallback(gxtkApp::OnWindowSize);
glfwSetWindowRefreshCallback(gxtkApp::OnWindowRefresh);
glfwSetMouseButtonCallback(gxtkApp::OnMouseButton);
}

static bool GraphicsModeExistsNative(int width,int height, int depth) {
// --- return true if a certain graphics mode exists ---
GLFWvidmode glfwModes[MAX_NUM_MODES];
int modeCount,index;

//fix 32 graphics mode so it matches glfw color bits
if (depth == 32) { depth = 24; }

//look for matching graphics mode
modeCount = glfwGetVideoModes(glfwModes,MAX_NUM_MODES);
for(index=0;index<modeCount;index++) {
if (glfwModes[index].Width == width && glfwModes[index].Height == height && glfwModes[index].RedBits + glfwModes[index].GreenBits + glfwModes[index].BlueBits == depth) {
return true;
}
}

//nope!
return false;
}

static Array<graphicsModeNative* > GetGraphicsModesNative() {
// --- get the available graphics resolutions ---
GLFWvidmode glfwModes[MAX_NUM_MODES];
int modeCount,index;

//get the video modes from glfw
modeCount = glfwGetVideoModes(glfwModes,MAX_NUM_MODES);

//convert into a format monkey can understand
Array<graphicsModeNative* > modes=Array<graphicsModeNative* >(modeCount);

//create all instance of native and assign to garbage collector
for(index=0;index<modeCount;index++) {
gc_assign(modes[index],new graphicsModeNative(glfwModes[index].Width,glfwModes[index].Height,glfwModes[index].RedBits + glfwModes[index].GreenBits + glfwModes[index].BlueBits));

//fix 24bits for potential later cross compatability (faking alpha bits)
if (modes[index]->depth == 24) { modes[index]->depth = 32; }
}

//return result
return modes;
}')


Skn3(Posted 2013) [#2]
Example
format_codebox('Import mojo
Import skn3.screenmode

Function Main:Int()
New Demo
Return 0
End

Class Demo Extends App
Field fullScreen:= False
Field image:Image

Method OnCreate:Int()
' --- app is created ---
SetUpdateRate(60)

image = LoadImage("monkey1.png")

Return 0
End Method

Method OnUpdate:Int()
' --- app is updated ---
If KeyHit(KEY_SPACE)
fullScreen = Not fullScreen

image.Discard()
image = Null

If fullScreen
If GraphicsModeExists(1024, 768, 32) SetGraphics(1024, 768, 32, fullScreen)
Else
SetGraphics(640, 480, 0, fullScreen)
EndIf

image = LoadImage("monkey1.png")
EndIf

Return 0
End Method

Method OnRender:Int()
' --- app is rendered ---
Cls(0, 0, 0)

DrawImage(image, 50, 50)

Return 0
End Method
End')


EdzUp(Posted 2013) [#3]
Excellent stuff :D


therevills(Posted 2013) [#4]
Skn3 what did I miss when I first looked into this? Looking at your code and what I originally did looks very similar... apart from the bits bit.


Skn3(Posted 2013) [#5]
Simply you and everyone else had missed the image.discard()Ii spent ages reading the posts on that thread, reading stack overflow and examining how monkey works under the hood. I had no idea monkey had picked up a command to manually discard().

So basically the reason it appeared to work once was just a coincidence. The images were referencing gl surfaces that had lost the context they belonged to when the window was closed.


Chroma(Posted 2013) [#6]
Yep, good job man!

Just test it...works as advertised.


John McCubbin(Posted 2013) [#7]
Good work, now if only we had it for XNA too :P


dopeyrulz(Posted 2013) [#8]
Skn3,

Hi been using your module in v66, but haven't yet managed to get it going in v68 with all the internal changes - having some trouble trying to get the callbacks referenced.

Did you have any plans for upgrading to v68 and beyond?


therevills(Posted 2013) [#9]
Try adding:
format_code('extern gxtkGraphics *bb_graphics_device;')
To the top of the C++.

Then change:
format_code(' //modify the dimensions of the app
app->graphics->width = width;
app->graphics->height = height;')
to:
format_code(' //modify the dimensions of the app
bb_graphics_device->width = width;
bb_graphics_device->height = height;')

Not tested ;)


dopeyrulz(Posted 2013) [#10]
@therevills
Thanks for your help - that's about where I got to!

It's the stuff referencing the glfw callbacks I can't get working as the keys etc stop working)
format_code('
glfwSetKeyCallback(gxtkApp::OnKey);
glfwSetCharCallback(gxtkApp::OnChar);
glfwSetWindowSizeCallback(gxtkApp::OnWindowSize);
glfwSetWindowRefreshCallback(gxtkApp::OnWindowRefresh);
glfwSetMouseButtonCallback(gxtkApp::OnMouseButton);

')
Also noted these callbacks are slightly changed in v68.

As the App setup stuff now appears to be Monkey code rather than native it can't seen to see how I can get access.

I've created a launch frontend for my Donkey Kong arcade cabinet that I've recently been building. Annoyingly when I launch Mame (via Execute) it sort-of turns the full-screen window into an almost full-screen window (it appears to size it without considering the window frame leaving the right/bottom part of the desktop visible) which I need to refresh to return back to full-screen. This code has been a lifesaver in allowing me to continue in monkey.


therevills(Posted 2013) [#11]
Ah, I didn't see those, sorry...

Try:
format_code('glfwSetKeyCallback(BBGlfwGame::OnKey);')

Or looking at the code (I hate C++ BTW), try getting the static reference of GlfwGame:
format_code('class BBGlfwGame : public BBGame{
public:
BBGlfwGame();

static BBGlfwGame *GlfwGame(){ return _glfwGame; }')


Skn3(Posted 2013) [#12]
Hey cheers for the fixes, Let me know if the fix works for now. I'll have another look into this soon and see if it can be improved with the new v68 stuff.


dopeyrulz(Posted 2013) [#13]
@therevills
No probs mate - nice of you to take a look. Yeah c++ not my thing either, but i'll have a play around - certainly maybe what we want is not publically exposed.


Trion(Posted 2013) [#14]
Hi! Did anyone test it on v70? And I want to know - can it use a current desktop user resolution? Does It hard to add this?

/sorry I am a newbie :) But I remember when I release my first game on BigFishGames (On BLitz3D)- change resolutions and other resolution stuff was a real hard issue. And I want to get it clear on Monkey GLFW...


SLotman(Posted 2013) [#15]
Alternating from windowed to fullscreen in Monkey is a pain. I've got it working on v70f - but a lot of changes had to be made, which I posted about here


jjsonick(Posted 2013) [#16]
SLotman, are you saying you made changes in Skn3's code to get it working in v70f/g? If so, could you post the changes (I didn't code or a link to code in the thread you mention)?


Skn3(Posted 2013) [#17]
Yeah if anyone has a definitive solution to this I could put it on a repo or update the code snippets in the first post?


SLotman(Posted 2013) [#18]
Not on Skn3's code - I code it myself, the changes I've made I posted on Monkey.70f thread. You need to expose a bunch of calls from monkeygame (the calls to read keys, mouse, etc) since they're private.

Then you destroy the glfw window, create a new one, discard all images and load them again.


ImmutableOctet(SKNG)(Posted 2013) [#19]
SLotman, I've messed with this sort of thing in the past, and I REALLY don't feel like doing it again. If you have working code, can you please just post it? If you've already done the work, there's no point in having every single one of us do it from scratch. I've already edited the GLFWGame code, so I shouldn't have any problems.


Why0Why(Posted 2013) [#20]
I REALLY think that being able to change screen res on the fly in GLFW should be an absolute priority. I was also disappointed that in the aforementioned thread that Mark didn't address that at all when Slot listed some relatively easy changes that would help.


ImmutableOctet(SKNG)(Posted 2013) [#21]

Why0Why: I REALLY think that being able to change screen res on the fly in GLFW should be an absolute priority. I was also disappointed that in the aforementioned thread that Mark didn't address that at all when Slot listed some relatively easy changes that would help.


Believe it or not, I was going to post about the same thing, but the post became huge and somewhat irrelevant, so I moved it to here.


SLotman(Posted 2013) [#22]
Actually, I don't have a screen *resolution* change. I set on config.target width and height to zero, and my code only switches from fullscreen to windowed mode and vice-versa.

With width/height set as 0 game starts at desktop resolution, and that's all I need.

This is the routine I call to create the windowed mode, should be all needed:
format_code('
void setWindowed(int w, int h)
{
glfwCloseWindow();
while(glfwGetWindowParam(GLFW_OPENED)) { glfwPollEvents(); }

GLFWvidmode desktopMode;
glfwGetDesktopMode( &desktopMode );

glfwOpenWindowHint( GLFW_WINDOW_NO_RESIZE, GL_FALSE);
if (w==0 && h==0)
{
if (CFG_GLFW_WINDOW_WIDTH==0) { w = desktopMode.Width; }
if (CFG_GLFW_WINDOW_HEIGHT==0){ h = desktopMode.Height; }
glfwOpenWindow(w,h, 0, 0, 0, 0, 0, 0, GLFW_WINDOW );
} else {
glfwOpenWindow(w, h, 0, 0, 0, 0, 0, 0, GLFW_WINDOW );
}
while(!glfwGetWindowParam(GLFW_OPENED)) { glfwPollEvents(); }

glfwSetWindowTitle(_STRINGIZE(CFG_GLFW_WINDOW_TITLE));

glfwEnable(GLFW_KEY_REPEAT);
glfwDisable(GLFW_AUTO_POLL_EVENTS);

glfwSetKeyCallback(BBGlfwGame::OnKey);
glfwSetCharCallback(BBGlfwGame::OnChar);
glfwSetMouseButtonCallback(BBGlfwGame::OnMouseButton);
glfwSetMousePosCallback(BBGlfwGame::OnMousePos);
glfwSetWindowCloseCallback(BBGlfwGame::OnWindowClose);

#if INIT_GL_EXTS
Init_GL_Exts();
#endif

}

')

As you can see, it's pretty simple and almost the same code you see on glfw code. I don't see what's the big fuzz of 'not posting the code' is all about.


ImmutableOctet(SKNG)(Posted 2013) [#23]
So, I finally got around to modifying your code SLotman, and here's the result:

format_codebox('

/*
All code is public-domain, and can be used in any way you see fit.
This is provided as-is, and I take no responsibility for any damages.

Credit would be appreciated, but it's not a requirement.
*/

// Credits:
// Original version by: SKN3
// Initial rewrite by: SLotman
// Current version by: Sonickidnextgen (Anthony Diamond)

// If we don't have these already, define them:
#ifndef _QUOTE
#define _QUOTE(X) #X
#endif

#ifndef _STRINGIZE
#define _STRINGIZE(X) _QUOTE(X)
#endif

#if !defined(_WIN32) //#if defined(__linux__)
#include <unistd.h>
#endif

// Everything related to the window:
// I was going to make this a namespace, but sadly they don't support 'private'.
class glfwWindow
{
// A couple of public prototypes:
public:
static inline GLFWvidmode* desktopVideoMode();
static inline void delay(unsigned int ms);

// Private members:
private:
// Global private variables:
static GLFWvidmode desktop;
static bool desktopChecked;
static float minTransitionSpeed;
static String windowTitle;
static const short transitionRate = 60;

// Private functions:
static void initTransition(float& transitionX, float& transitionY)
{
// This can be changed to any value you want, but it usually makes the window shake.
minTransitionSpeed = 0.0f;

// Set the initial window position:
transitionX = (float)((desktopVideoMode()->Width - CFG_GLFW_WINDOW_WIDTH)/2);
transitionY = (float)((desktopVideoMode()->Height - CFG_GLFW_WINDOW_HEIGHT)/2);

glfwSetWindowPos((int)transitionX, (int)transitionY);

// Delay for 100ms.
delay(100);

return;
}

static void updateTransition(bool& transition, float& transitionX, float& transitionY, int destX, int destY, int windowW, int windowH)
{
// Tell GLFW to poll for events.
glfwPollEvents();

// Check if we're going up or down, then move accordingly:
if (destX > transitionX)
transitionX += max((abs(transitionX-destX) * 0.095), minTransitionSpeed);
else
transitionX -= max((abs(transitionX-destX) * 0.095), minTransitionSpeed);

if (destY > transitionY)
transitionY += max((abs(transitionY-destY) * 0.095), minTransitionSpeed);
else
transitionY -= max((abs(transitionY-destY) * 0.095), minTransitionSpeed);

if (abs((int)transitionX - destX) < 5 && abs((((int)(transitionY) - destY)/2)) < 5)
transition = false;

glfwSetWindowPos((int)transitionX, (int)transitionY);

return;
}

// Public members:
public:
// Here's a simple function you might find useful, I use it for my own project.
static inline void appTitle(String s)
{
// Set the title of the window to the string specified:
glfwSetWindowTitle(s.ToCString<char>());
windowTitle = s;

return;
}

static inline String appTitle()
{
return windowTitle;
}

// And here's the XNA/C# version for those who need it:
/*
public static void AppTitle(String s)
{
BBXnaGame.XnaGame().GetXNAGame().Window.Title = s;

return;
}
*/

// The window-resize/screen-mode code:
static bool resize(int w, int h, int depth=0, bool fullscreen=false, bool resize=CFG_GLFW_WINDOW_RESIZABLE, bool transition=false, int x=-9999999, int y=-9999999)
{
// Temporary variables:
GLFWvidmode* desktopMode;
bool windowResponse;

// Transition related variables:
float transitionX;
float transitionY;

// Detect the desktop resolution.
desktopMode = desktopVideoMode();

// Reset the title of the window.
appTitle(String(_STRINGIZE(CFG_GLFW_WINDOW_TITLE)));

// Check if transitions are enabled.
if (transition == true)
{
// Initialize the transition.
initTransition(transitionX, transitionY);

// Repeat until the transition variable is false.
while (transition == true)
{
// Update the transition.
updateTransition
(
// Arguments:

// Transition variable. (Boolean)
transition,

// X and Y variables. (Floats)
transitionX,
transitionY,

// X and Y destination values. (Ints)
(desktopMode->Width-CFG_GLFW_WINDOW_WIDTH)/2,
desktopMode->Height,

// The width and height values for the current window. (Ints)
CFG_GLFW_WINDOW_WIDTH,
CFG_GLFW_WINDOW_HEIGHT
);

// Delay based on the frame-rate.
delay(1000/transitionRate);
}

// Set the transition variable back to true.
transition = true;
}

// Close the current window:
//glfwIconifyWindow();
glfwCloseWindow();

// Wait for GLFW to close everything.
while(glfwGetWindowParam(GLFW_OPENED))
glfwPollEvents();

// Check the screen-mode and make sure everything works out:
if (fullscreen == true)
{
if (CFG_GLFW_WINDOW_WIDTH == 0 || w == 0)
w = desktopMode->Width;

if (CFG_GLFW_WINDOW_HEIGHT == 0 || h == 0)
h = desktopMode->Height;
}
else
{
if (w == 0 && CFG_GLFW_WINDOW_WIDTH > 0)
w = CFG_GLFW_WINDOW_WIDTH;
else if (w == 0 && CFG_GLFW_WINDOW_WIDTH < 1)
w = desktopMode->Width;

if (h == 0 && CFG_GLFW_WINDOW_HEIGHT > 0)
h = CFG_GLFW_WINDOW_HEIGHT;
else if (h == 0 && CFG_GLFW_WINDOW_HEIGHT < 1)
h = desktopMode->Height;
}

// If we're in windowed mode, check to see if we should make the window resizable.
if (fullscreen == false)
glfwOpenWindowHint(GLFW_WINDOW_NO_RESIZE, (resize == true) ? GL_FALSE : GL_TRUE);

// Attempt to open the new window.
windowResponse = glfwOpenWindow
(
// Arguments:

// Width and Height.
w, h,

// Red, Greed, and Blue. (In bits)
max(depth/4, 0), max(depth/4, 0), max(depth/4, 0),

// Alpha, Depth, and Stencil-Depth. (In bits)
max(depth/4, 0), max(depth, 0), max(depth, 0),

// Window initialization mode. (GLFW_FULLSCREEN or GLFW_WINDOW)
(fullscreen == true) ? GLFW_FULLSCREEN : GLFW_WINDOW
);

if (windowTitle == "")
{
// Set the new window's title to the default.
appTitle(String(_STRINGIZE(CFG_GLFW_WINDOW_TITLE)));
}
else
{
// Set the new window's title to the old title.
glfwSetWindowTitle(windowTitle.ToCString<char>());
}

// In the event glfwOpenWindow was successful, do the following:
if (windowResponse == true)
{
// Just some code to make the position centered:
if (y == -9999999)
{
y = ((desktopMode->Height-h)/2);

if (h > ((unsigned int)(desktopMode->Height/1.5)))
y -= (abs(desktopMode->Height - h)/4);
}

if (x == -9999999)
x = (desktopMode->Width - w)/2;

// Check for the transition.
if (transition == false)
{
// If the transition is disabled,
// just set the position of the window and continue.
glfwSetWindowPos(x, y);
}
else
{
// If the transition is enabled, do the following:

// Set the transition's X variable to the value of 'x'.
transitionX = (float)x;

// Set the position of the window to the proper place.
glfwSetWindowPos((int)transitionX, (int)transitionY);

// Delay for 100ms.
delay(100);

// Repeat until the transition variable is false:
while (transition == true)
{
// Update the transition as usual.
updateTransition
(
// The arguments are listed above.
transition,
transitionX,
transitionY,
x,
y,
w,
h
);

// Delay based on the frame-rate.
delay(1000/transitionRate);
}

// Wait half a second, then continue.
delay(500);
}

// Wait for GLFW to initialize the window.
while(!glfwGetWindowParam(GLFW_OPENED))
glfwPollEvents();

// Setup some basic functionality:
glfwEnable(GLFW_KEY_REPEAT);
glfwDisable(GLFW_AUTO_POLL_EVENTS);

glfwSetKeyCallback(BBGlfwGame::OnKey);
glfwSetCharCallback(BBGlfwGame::OnChar);
glfwSetMouseButtonCallback(BBGlfwGame::OnMouseButton);
glfwSetMousePosCallback(BBGlfwGame::OnMousePos);
glfwSetWindowCloseCallback(BBGlfwGame::OnWindowClose);

// Initialize the GL extensions.
#if INIT_GL_EXTS
Init_GL_Exts();
#endif
}

// Clean up everything that we're handling manually:
// Nothing so far.

// Return our 'windowResponse' variable.
return windowResponse;
}
};

// Global private variables:
GLFWvidmode glfwWindow::desktop;
bool glfwWindow::desktopChecked;
float glfwWindow::minTransitionSpeed;
String glfwWindow::windowTitle = String(_STRINGIZE(CFG_GLFW_WINDOW_TITLE));

// Extra function definitions:

// A simple command that grabs the current desktop resolution:
inline GLFWvidmode* glfwWindow::desktopVideoMode()
{
if (desktopChecked == false)
{
glfwGetDesktopMode(&desktop);
desktopChecked = true;
}

// Return the desktop's video mode/info.
return &desktop;
}

// A basic cross-platform 'delay' command.
inline void glfwWindow::delay(unsigned int ms)
{
#if defined(_WIN32)
Sleep(ms);
#else
usleep(ms*1000);
#endif

return;
}

')

Here's the "Extern" code for Monkey:

format_codebox('
Extern

#If TARGET = "glfw"
Function ResizeWindow:Bool(Width:Int, Height:Int, Depth:Int=0, Fullscreen:Bool=False, Resize:Bool=False, Transition:Bool=False, X:Int=-9999999, Y:Int=-9999999)="glfwWindow::resize"
Function AppTitle:Void(Title:String)="glfwWindow::appTitle"
#End

Public

')

I think I went a bit overboard, and the transition option was done as a proof of concept, but regardless, that should all work.

I've tested all of this with V70e, I'll get around to testing it with V71 at some point. This should also work with Linux and Mac OS X, but I haven't tested it.

Please note that this was designed with 'OnCreate' in mind, so you generally should be calling 'ResizeWindow' there. And obviously, you shouldn't do anything graphics related with Mojo before using 'ResizeWindow' (Unless you want to reload/reset everything).

Also note that this requires the same edits to the 'BBGlfwGame' class, which can be located in "targets/glfw/modules/native/glfwgame.cpp".

I can't guarantee that this will work in the future; however, I may end up updating it if there's a problem with a future version of Monkey/Mojo. I'll be using this for my own project, so I'll at the very least have a working version.