common/Windows/MicroGlut.c
a721e321
 // Micro-GLUT, bare essentials
 
 // By Ingemar Ragnemalm 2012
 // I wrote this since GLUT seems not to have been updated to support
 // creation of a 3.2 context on the Mac. You can use FreeGLUT
 // which has this support, but I felt I wanted something without the old-style
 // material, and that is small enough to include as a single source file.
 
 // 120309 First Win32 version. Incomplete, lacks timers and more. Also, most settings are not supported.
 // 130204 Tried to add GL3 and GLEW. No success.
 // Forgotten for too long...
 // 1508013 Running with all current utities and GLEW, with the psychedelic teapot demo!
 // Timers and rescaling enabled, needs testing.
 // 150817: Timers and rescaling runs fine!
 // Menus and warp pointer are missing, but this looks good enough for "first beta version"!
 // Tested mainly with the Psychedelic Teapot example.
 // 150919 Added the very useful glutKeyIsDown plus support for key up events.
 // 150920: glutInitWindowPosition and glutInitWindowSize are now working
 // 150923: Keyboard events now report ASCII values instead of virtual codes. Also, various special keys like arrow keys should work.
 // Finally, I have taken some steps to make special key callbacks obsolete by smarter mapping of special keys.
 // 151203: Added a stdio window.
 // 160222: Fixed a bug affecting glutKeyIsDown (a very important call which works better now).
 // 1602??: Added glutWarpPointer, glutHideCursor, glutShowCursor.
 // 160309: Added glutFullScreen, glutExitFullScreen, glutToggleFullScreen.
 // 170221: Added glutPositionWindow, glutReshapeWindow. Changed default behavior on resize.
 // 170913: Added glutMouseIsDown, corrected support for glutMotionFunc (dragging).
 // 200405: Added BeginPaint etc as response to WM_PAINT to make Windows stop sending PAINT repeatedly.
 // Also avoided having events blocking timers in the event loop.
 // 210524: Important overhaul! After removing WinMain and moving its functionality to glutInit,
 // MicroGlut now works with MinGW, and outputs printf's properly! I also moved glewInit into MicroGlut
 // to educe/remove system dependencies from your code. This means MinGW (DevC++ etc) in, Visual Studio OUT!
 // Visual Studio's inability to printf is unbearable and MinGW is a lot better.
 // 210524: Added glutMotionFunc in the header file.
 // 210530: Temporary fix for the window size. It is not how I want it to be but it looks OK and will have to do for now.
 // 220112: Added support for modifier keys
 
 #include <windows.h>
 #include "glew.h"
 #include <gl/gl.h>
 #include "MicroGlut.h"
 #include <stdio.h>
 #include <io.h>
 #include <fcntl.h>
 
 #ifndef _WIN32
 	This line causes an error if you are not using Windows. It means that you are accidentally compiling the Windows version.
 	Have a look at your paths.
 #endif
 
 // Vital internal variables
 
 void (*gDisplay)(void);
 void (*gReshape)(int width, int height);
 void (*gKey)(unsigned char key, int x, int y);
 void (*gKeyUp)(unsigned char key, int x, int y);
 void (*gSpecialKey)(unsigned char key, int x, int y); // I consider this obsolete!
 void (*gSpecialKeyUp)(unsigned char key, int x, int y); // I consider this obsolete!
 void (*gMouseMoved)(int x, int y);
 void (*gMouseDragged)(int x, int y);
 void (*gMouseFunc)(int button, int state, int x, int y);
 unsigned int gContextInitMode = GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH;
 void (*gIdle)(void);
 char updatePending = 1;
 char gRunning = 1;
 int gContextVersionMajor = 0;
 int gContextVersionMinor = 0;
 char gKeymap[256];
 char gButtonPressed[10] = {0,0,0,0,0,0,0,0,0,0};
 
 // Prototype
 static void checktimers();
 
 // -----------
 
 // Globals (was in GLViewDataPtr)
 //NSOpenGLContext	*m_context;
 float lastWidth, lastHeight;
 //NSView *theView;
 
 HWND hWnd;
 HDC hDC;
 HGLRC hRC;
 HINSTANCE gInstance;
 
 // Enable OpenGL
 
 void EnableOpenGL(HWND hWnd, HDC * hDC, HGLRC * hRC)
 {
 	PIXELFORMATDESCRIPTOR pfd;
 	int format;
 	int zdepth, sdepth;
 #if defined WGL_CONTEXT_MAJOR_VERSION_ARB
 // NOT tested because WGL_CONTEXT_MAJOR_VERSION_ARB is undefined on my computer
 	int attribs[] =
 	{
 		WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
 		WGL_CONTEXT_MINOR_VERSION_ARB, 2,
 		WGL_CONTEXT_FLAGS_ARB, 0,
 		0
 	};
 #endif
 	// get the device context (DC)
 	*hDC = GetDC( hWnd );
 
 	if (gContextInitMode & GLUT_DEPTH)
 		zdepth = 32;
 	else
 		zdepth = 0;
 
 	if (gContextInitMode & GLUT_STENCIL)
 		sdepth = 32;
 	else
 		sdepth = 0;
 
 	// set the pixel format for the DC
 	// MUCH OF THIS SHOULD BE OPTIONAL (like depth and stencil above)!
 	ZeroMemory( &pfd, sizeof( pfd ) );
 	pfd.nSize = sizeof( pfd );
 	pfd.nVersion = 1;
 	pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
 	pfd.iPixelType = PFD_TYPE_RGBA;
 	pfd.cColorBits = 24;
 	pfd.cDepthBits = zdepth;
 	pfd.iLayerType = PFD_MAIN_PLANE;
 	format = ChoosePixelFormat( *hDC, &pfd );
 	SetPixelFormat( *hDC, format, &pfd );
 
 #if defined WGL_CONTEXT_MAJOR_VERSION_ARB
 // NOT tested because WGL_CONTEXT_MAJOR_VERSION_ARB is undefined on my computer
 // Try to support new OpenGL!
 	attribs[1] = gContextVersionMajor;
 	attribs[3] = gContextVersionMinor;
 
     if(wglewIsSupported("WGL_ARB_create_context") == 1)
     {
 		*hRC = wglCreateContextAttribsARB(*hDC,0, attribs);
 		wglMakeCurrent(*hDC, *hRC);
 	}
 	else
 	{	//It's not possible to make a GL 3.x context. Use the old style context (GL 2.1 and before)
 		// create and enable the render context (RC)
 		*hRC = wglCreateContext( *hDC );
 		wglMakeCurrent( *hDC, *hRC );
 	}
 #else
 		*hRC = wglCreateContext( *hDC );
 		wglMakeCurrent( *hDC, *hRC );
 #endif
 
 
 	glewInit();
 }
 
 // Disable OpenGL
 
 void DisableOpenGL(HWND hWnd, HDC hDC, HGLRC hRC)
 {
 	wglMakeCurrent( NULL, NULL );
 	wglDeleteContext( hRC );
 	ReleaseDC( hWnd, hDC );
 }
 
 
 
 
 
 void glutPostRedisplay()
 {
 	updatePending = 1;
 }
 
 // ------------------ Main program ---------------------
 
 //MGApplication *myApp;
 //NSView *view;
 //NSWindow *window;
 static struct timeval timeStart;
 
 int gWindowPosX = 10;
 int gWindowPosY = 50;
 int gWindowWidth = 400;
 int gWindowHeight = 400;
 
 void glutInitWindowPosition (int x, int y)
 {
 	gWindowPosX = x;
 	gWindowPosY = y;
 }
 void glutInitWindowSize (int width, int height)
 {
 	gWindowWidth = width;
 	gWindowHeight = height;
 }
 
 void glutCreateWindow(char *title)
 {
 /*	// Convert title to szTitle!
 	#define MAX_LOADSTRING 100
 	TCHAR szTitle[MAX_LOADSTRING];					// The title bar text
     int size_needed = MultiByteToWideChar(CP_UTF8, 0, &title[0], strlen(title), NULL, 0);
     MultiByteToWideChar(CP_UTF8, 0, &title[0], strlen(title), &szTitle[0], size_needed);
 	szTitle[size_needed] = 0; // Zero terminate
 
 	// Convert class name to szCn!
 	#define MAX_LOADSTRING 100
 	char cn[] = "MicroGlutWC";
 	TCHAR szCn[MAX_LOADSTRING];					// The title bar text
     size_needed = MultiByteToWideChar(CP_UTF8, 0, &cn[0], strlen(cn), NULL, 0);
     MultiByteToWideChar(CP_UTF8, 0, &cn[0], strlen(cn), &szCn[0], size_needed);
 	szCn[size_needed] = 0; // Zero terminate
 */
 
 /*	RECT r;
 	r.left = gWindowPosX;
 	r.top = gWindowPosY;
 	r.right = gWindowWidth+gWindowPosX;
 	r.bottom = gWindowHeight+gWindowPosY;
 	r.left = 0;
 	r.top = 0;
 	r.right = gWindowWidth;
 	r.bottom = gWindowHeight;
 
 	AdjustWindowRect(&r, WS_CAPTION | WS_POPUPWINDOW, 0);
 */
 
 // Grabbed from FreeGLUT since AdjustWindowRect did not work for me
 	// create main window
     int h = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CXDLGFRAME)*2 + gWindowHeight;
     int w = GetSystemMetrics(SM_CXDLGFRAME)*2 + gWindowWidth;
 
     // Same window settings as QuakeII
     // WS_BORDER WS_DLGFRAME WS_CLIPSIBLINGS
 //    hWnd = CreateWindowEx(WS_EX_APPWINDOW,
 //      g_appName, title,
 //      WS_BORDER | WS_DLGFRAME | WS_CLIPSIBLINGS,
 //      CW_USEDEFAULT, CW_USEDEFAULT, w, h,
 //      NULL, NULL, g_hMainInst, NULL);
 
 // This is with a +10 offset which gives the proper size. I am not happy with this but it will have to do for now since none of
 // the "real" methods gave the right result.
     hWnd = CreateWindow("MicroGlutWC", title,
      WS_CAPTION | WS_POPUPWINDOW | WS_VISIBLE | WS_OVERLAPPEDWINDOW,
       gWindowPosX, gWindowPosY, w+10, h+10,
       NULL, NULL, gInstance, NULL);
 
 /*
 	hWnd = CreateWindow(
 //		szCn, szTitle,
 		"MicroGlutWC", title,
 		WS_CAPTION | WS_POPUPWINDOW | WS_VISIBLE | WS_OVERLAPPEDWINDOW, // WS_OVERLAPPEDWINDOW gives rescalable window
 //		0, 0, 256, 256,
 //		r.left, r.top, r.right-r.left, r.bottom-r.top,
 //		gWindowPosX, gWindowPosY, r.right-r.left, r.bottom-r.top,
 		gWindowPosX, gWindowPosY, gWindowWidth + 16, gWindowHeight + 39, // Hard-coded because I get AdjustWindowRect wrong
 		NULL, NULL, gInstance, NULL );
 	if (hWnd == NULL)
 		printf("No window\n");
 */
 	// enable OpenGL for the window
 	EnableOpenGL( hWnd, &hDC, &hRC );
 }
 
 
 void glutMainLoop()
 {
 	BOOL quit = 0;
 	MSG msg;
 
 	// program main loop
 	while ( !quit )
 	{
 
 		// check for messages
 		if ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE )  )
 		{
 			// handle or dispatch messages
 			if ( msg.message == WM_QUIT )
 			{
 				quit = TRUE;
 			}
 			else
 			{
 				TranslateMessage( &msg );
 				DispatchMessage( &msg );
 			}
 		}
 //		else  - SKIPPED in order not to have events blocking timers
 		{
 			if (updatePending)
 			{
 				gDisplay();
 				updatePending = 0;
 			}
 			if (gIdle)
 				gIdle();
 			// TIMERS!
 			checktimers();
 		}
 	}
 }
 
 // This won't work yet
 //void glutCheckLoop()
 //{
 //}
 
 void glutDisplayFunc(void (*func)(void))
 {
 	gDisplay = func;
 }
 
 void glutReshapeFunc(void (*func)(int width, int height))
 {
 	gReshape = func;
 	if (func != NULL)
 		gReshape(gWindowWidth, gWindowHeight);
 }
 
 void glutKeyboardFunc(void (*func)(unsigned char key, int x, int y))
 {
 	gKey = func;
 }
 
 void glutKeyboardUpFunc(void (*func)(unsigned char key, int x, int y))
 {
 	gKeyUp = func;
 }
 
 // I consider this obsolete!
 void glutSpecialFunc(void (*func)(unsigned char key, int x, int y))
 {
 	gSpecialKey = func;
 }
 
 // I consider this obsolete!
 void glutSpecialUpFunc(void (*func)(unsigned char key, int x, int y))
 {
 	gSpecialKeyUp = func;
 }
 
 void glutPassiveMotionFunc(void (*func)(int x, int y))
 {
 	gMouseMoved = func;
 }
 
 void glutMotionFunc(void (*func)(int x, int y))
 {
 	gMouseDragged = func;
 }
 
 void glutMouseFunc(void (*func)(int button, int state, int x, int y))
 {
 	gMouseFunc = func;
 }
 
 // You can safely skip this
 void glutSwapBuffers()
 {
  	SwapBuffers(hDC);
 }
 
 int glutGet(int type)
 {
 //	struct timeval tv;
 
 //	gettimeofday(&tv, NULL);
 //	return (tv.tv_usec - timeStart.tv_usec) / 1000 + (tv.tv_sec - timeStart.tv_sec)*1000;
 
 	return GetTickCount();
 }
 
 void glutInitDisplayMode(unsigned int mode)
 {
 	gContextInitMode = mode;
 }
 
 void glutIdleFunc(void (*func)(void))
 {
 	gIdle = func;
 }
 
 char glutKeyIsDown(unsigned char c)
 {
     if (c == GLUT_KEY_SHIFT)
         return ( ( ( GetKeyState( VK_LSHIFT   ) < 0 ) ||
             ( GetKeyState( VK_RSHIFT   ) < 0 ) ) );
     if (c == GLUT_KEY_CTRL)
          return ( ( ( GetKeyState( VK_LCONTROL   ) < 0 ) ||
             ( GetKeyState( VK_RCONTROL   ) < 0 ) ) );
     if (c == GLUT_KEY_ALT)
          return ( ( ( GetKeyState( VK_LMENU   ) < 0 ) ||
             ( GetKeyState( VK_RMENU   ) < 0 ) ) );
 
 	return gKeymap[c];
 }
 
 static unsigned char scan2ascii(WPARAM vk, LPARAM scancode)
 {
    static HKL layout;
    static unsigned char State[256];
    static unsigned char chars[2];
    int count;
    static short unsigned int chars1[2];
 
    layout = GetKeyboardLayout(0);
    if (GetKeyboardState(State)==FALSE)
       return 0;
    count = ToAsciiEx(vk,scancode,State,&chars1[0],0,layout);
    if (count > 0) return (char)chars1[0];
    else return 0;
 }
 
 static void doKeyboardEvent(WPARAM wParam, LPARAM lParam, void (*keyFunc)(unsigned char key, int x, int y), void (specialKeyFunc)(unsigned char key, int x, int y), char keyMapValue)
 {
 	unsigned char c;
 
 		switch(wParam)
 		{
 			case VK_F1:
 				c = GLUT_KEY_F1; break;
 			case VK_F2:
 				c = GLUT_KEY_F2; break;
 			case VK_F3:
 				c = GLUT_KEY_F3; break;
 			case VK_F4:
 				c = GLUT_KEY_F4; break;
 			case VK_F5:
 				c = GLUT_KEY_F5; break;
 			case VK_F6:
 				c = GLUT_KEY_F6; break;
 			case VK_F7:
 				c = GLUT_KEY_F7; break;
 // F8 and up ignored since they are not possible on some keyboards - like mine
 
 			case VK_LEFT:
 				c = GLUT_KEY_LEFT; break;
 			case VK_UP:
 				c = GLUT_KEY_UP; break;
 			case VK_RIGHT:
 				c = GLUT_KEY_RIGHT; break;
 			case VK_DOWN:
 				c = GLUT_KEY_DOWN; break;
 
 			case VK_ESCAPE:
 				c = GLUT_KEY_ESC; break;
 			case VK_PRIOR:
 				c = GLUT_KEY_PAGE_UP; break;
 			case VK_NEXT:
 				c = GLUT_KEY_PAGE_DOWN; break;
 			case VK_HOME:
 				c = GLUT_KEY_HOME; break;
 			case VK_END:
 				c = GLUT_KEY_END; break;
 			case VK_INSERT:
 				c = GLUT_KEY_INSERT; break;
 			default:
 				c = scan2ascii(wParam,lParam);
 				if (c == 0) return;
 		}
 		if (keyFunc != NULL)
 		{
 			keyFunc(c, 0, 0); // TO DO: x and y
 		}
 		else
 		if (specialKeyFunc != NULL && c < 32)
 		{
 			specialKeyFunc(c, 0, 0); // TO DO: x and y
 		}
 		gKeymap[c] = keyMapValue;
 		//printf("key %i %i\n", c, keyMapValue);
 }
 
 
 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
 	int x = LOWORD(lParam);
 	int y = HIWORD(lParam);
 //	static char firstTime = 1;
 //
 //	if (firstTime)
 //	{
 //		if (gReshape != NULL)
 //			gReshape(gWindowWidth, gWindowHeight);
 //		firstTime = 0;
 //	}
 
 	switch (message)
 	{
     case WM_LBUTTONUP:
 		if (gMouseFunc != NULL)
 			gMouseFunc(GLUT_LEFT_BUTTON, GLUT_UP, x, y);
 		gButtonPressed[0] = 0;
         break;
 
     case WM_LBUTTONDOWN:
 		if (gMouseFunc != NULL)
 			gMouseFunc(GLUT_LEFT_BUTTON, GLUT_DOWN, x, y);
 		gButtonPressed[0] = 1;
      break;
 
     case WM_RBUTTONUP:
 		if (gMouseFunc != NULL)
 			gMouseFunc(GLUT_RIGHT_BUTTON, GLUT_UP, x, y);
 		gButtonPressed[1] = 0;
         break;
 
     case WM_RBUTTONDOWN:
 		if (gMouseFunc != NULL)
 			gMouseFunc(GLUT_RIGHT_BUTTON, GLUT_DOWN, x, y);
 		gButtonPressed[1] = 1;
      break;
 
     case WM_MOUSEMOVE:
 		if (gMouseMoved != NULL)
 			gMouseMoved(x, y);
 		if (gButtonPressed[0] || gButtonPressed[1])
 			if (gMouseDragged != NULL)
 			gMouseDragged(x, y);
     break;
 
 	case WM_CREATE:
 		return 0;
 
 	case WM_CLOSE:
 		PostQuitMessage( 0 );
 		return 0;
 
 	case WM_DESTROY:
 		return 0;
 
 	case WM_KEYDOWN:
 		doKeyboardEvent(wParam, lParam, gKey, gSpecialKey, 1);
 		return 0;
 	case WM_KEYUP:
 		doKeyboardEvent(wParam, lParam, gKeyUp, gSpecialKeyUp, 0);
 		return 0;
 
 	case WM_SIZE:
 		if (gReshape != NULL)
 			gReshape(LOWORD(lParam), HIWORD(lParam));
 		else
 		{	glViewport(0,0,LOWORD(lParam), HIWORD(lParam));
 			glutPostRedisplay();
 		}
 		break;
 	case WM_PAINT: // Don't have Windows fighting us while resize!
 		if (gDisplay)
 		{
 			PAINTSTRUCT ps;
 			HDC hdc;
 			hdc = BeginPaint(hWnd, &ps); // Added to make Windows stop sending PAINT repeatedly.
 			OutputDebugStringA("paint\n");
 			gDisplay();
 			EndPaint(hWnd, &ps);
 			updatePending = 0;
 		}
 		break;
 	case WM_ERASEBKGND:
 		return 1;
 		break;
 	default:
 		return DefWindowProc( hWnd, message, wParam, lParam );
 	}
 
 	return 0;
 }
 
 void glutInit(int *argcp, char **argv)
 {
 	int i;
 	int hCrt;
 	FILE *hf;
 
 	for (i = 0; i < 256; i++) gKeymap[i] = 0;
 
 	WNDCLASS wc;
 
 	gInstance = GetModuleHandle(NULL);
 	// register window class
 	wc.style = CS_OWNDC;
 	wc.lpfnWndProc = WndProc;
 	wc.cbClsExtra = 0;
 	wc.cbWndExtra = 0;
 	wc.hInstance = gInstance;
 	wc.hIcon = LoadIcon( NULL, IDI_APPLICATION );
 	wc.hCursor = LoadCursor( NULL, IDC_ARROW );
 	wc.hbrBackground = (HBRUSH)GetStockObject( BLACK_BRUSH );
 	wc.lpszMenuName = NULL;
 	wc.lpszClassName = "MicroGlutWC";
 	if (!RegisterClass( &wc ))
 		MessageBox(NULL, "RegisterClass failed", "?", MB_ICONEXCLAMATION | MB_OK);
 
 	// Make printf work!
 	AllocConsole();
 	hCrt = _open_osfhandle(
 		//(long)
 		(long long)GetStdHandle(STD_OUTPUT_HANDLE), // This gives a warning but it works.
 		_O_TEXT
 		);
 	hf = _fdopen( hCrt, "w" );
 	*stdout = *hf;
 	i = setvbuf( stdout, NULL, _IONBF, 0 );
 	*stderr = *hf;
 
 //	printf("allocated console and window class\n");
 }
 
 void glutClose()
 {    /* shutdown OpenGL */
     DisableOpenGL (hWnd, hDC, hRC);
 
     /* destroy the window explicitly */
     DestroyWindow (hWnd);
 
  //   return 0; // msg.wParam;
 }
 
 
 
 
 
 
 
 // NOTE: The timer is not designed with any multithreading in mind!
 typedef struct TimerRec
 {
 	int arg;
 	int time;
 	int repeatTime;
 	void (*func)(int arg);
 	char repeating;
 	struct TimerRec *next;
 	struct TimerRec *prev;
 } TimerRec;
 
 TimerRec *gTimers = NULL;
 
 void glutTimerFunc(int millis, void (*func)(int arg), int arg)
 {
 	TimerRec *t	= (TimerRec *)malloc(sizeof(TimerRec));
 	t->arg = arg;
 	t->time = millis + glutGet(GLUT_ELAPSED_TIME);
 	t->repeatTime = 0;
 	t->repeating = 0;
 	t->func = func;
 	t->next = gTimers;
 	t->prev = NULL;
 	if (gTimers != NULL)
 		gTimers->prev = t;
 	gTimers = t;
 }
 
 // Added by Ingemar
 void glutRepeatingTimer(int millis)
 {
 	TimerRec *t	= (TimerRec *)malloc(sizeof(TimerRec));
 	t->arg = 0;
 	t->time = millis + glutGet(GLUT_ELAPSED_TIME);
 	t->repeatTime = millis;
 	t->repeating = 1;
 	t->func = NULL;
 	t->next = gTimers;
 	t->prev = NULL;
 	if (gTimers != NULL)
 		gTimers->prev = t;
 	gTimers = t;
 }
 
 // From http://stackoverflow.com/questions/5801813/c-usleep-is-obsolete-workarounds-for-windows-mingw
 void usleep(__int64 usec)
 {
     HANDLE timer;
     LARGE_INTEGER ft;
 
     ft.QuadPart = -(10*usec); // Convert to 100 nanosecond interval, negative value indicates relative time
 
     timer = CreateWaitableTimer(NULL, TRUE, NULL);
     SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
     WaitForSingleObject(timer, INFINITE);
     CloseHandle(timer);
 }
 
 static void checktimers()
 {
 	if (gTimers != NULL)
 	{
 		TimerRec *t, *firethis = NULL;
 		int now = glutGet(GLUT_ELAPSED_TIME);
 		int nextTime = now + 1000; // Distant future, 1 second
 		t = gTimers;
 		for (t = gTimers; t != NULL; t = t->next)
 		{
 			if (t->time < nextTime) nextTime = t->time; // Time for the next one
 			if (t->time < now) // See if this is due to fire
 			{
 				firethis = t;
 			}
 		}
 		if (firethis != NULL)
 		{
 		// Fire the timer
 			if (firethis->func != NULL)
 				firethis->func(firethis->arg);
 			else
 				glutPostRedisplay();
 		// Remove the timer if it was one-shot, otherwise update the time
 			if (firethis->repeating)
 			{
 				firethis->time = now + firethis->repeatTime;
 			}
 			else
 			{
 				if (firethis->prev != NULL)
 					firethis->prev->next = firethis->next;
 				else
 					gTimers = firethis->next;
                 if (firethis->next != NULL)
 					firethis->next->prev = firethis->prev;
 				free(firethis);
 			}
 		}
 		// Otherwise, sleep until any timer should fire
         if (!updatePending)
 			if (nextTime > now)
             {
 		usleep((nextTime - now)*1000);
             }
 	}
     else
 // If no timer and no update, sleep a little to keep CPU load low
         if (!updatePending)
             usleep(10);
 }
 
 void glutInitContextVersion(int major, int minor)
 {
 	gContextVersionMajor = major;
 	gContextVersionMinor = minor;
 }
 
 
 void glutHideCursor()
 {
 	ShowCursor(0);
 }
 
 void glutShowCursor()
 {
 	ShowCursor(1);
 }
 
 void glutWarpPointer(int x, int y)
 {
 	POINT coords;
 	coords.x = x;
 	coords.y = y;
 	ClientToScreen(hWnd, &coords);
 	SetCursorPos(coords.x, coords.y);
 }
 
 
 
 // ----------------------------- Full-screen mode support! ---------------------------------
 
 static int savedWidth;
 static int savedHeight;
 static int savedColourBits;
 static int savedRefreshRate;
 static int savedXPosition;
 static int savedYPosition;
 
 // This can be useful if you want to change the resolution.
 static int enterFullscreenExt(HWND hwnd, int fullscreenWidth, int fullscreenHeight, int colourBits, int refreshRate)
 {
     DEVMODE fullscreenSettings;
     int isChangeSuccessful=0;
     RECT windowBoundary;
 	HDC windowHDC;
 
 	GetWindowRect(hwnd, &windowBoundary);
 	savedWidth  = windowBoundary.right - windowBoundary.left;
 	savedHeight = windowBoundary.bottom - windowBoundary.top;
 	savedXPosition = windowBoundary.left;
 	savedYPosition = windowBoundary.top;
 
     EnumDisplaySettings(NULL, 0, &fullscreenSettings);
     fullscreenSettings.dmPelsWidth        = fullscreenWidth;
     fullscreenSettings.dmPelsHeight       = fullscreenHeight;
     fullscreenSettings.dmBitsPerPel       = colourBits;
     fullscreenSettings.dmDisplayFrequency = refreshRate;
     fullscreenSettings.dmFields           = DM_PELSWIDTH |
                                             DM_PELSHEIGHT |
                                             DM_BITSPERPEL |
                                             DM_DISPLAYFREQUENCY;
 
     SetWindowLongPtr(hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW | WS_EX_TOPMOST);
     SetWindowLongPtr(hwnd, GWL_STYLE, WS_POPUP | WS_VISIBLE);
     SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, fullscreenWidth, fullscreenHeight, SWP_SHOWWINDOW);
 //    isChangeSuccessful = ChangeDisplaySettings(&fullscreenSettings, CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL;
     ShowWindow(hwnd, SW_MAXIMIZE);
 
     return isChangeSuccessful;
 }
 
 static int enterFullscreen(HWND hwnd)
 {
 	int fullscreenWidth;
 	int fullscreenHeight;
 	int colourBits;
 	int refreshRate;
 	HDC windowHDC;
 
 	windowHDC = GetDC(hwnd);
 	fullscreenWidth  = GetDeviceCaps(windowHDC, HORZRES);
 	fullscreenHeight = GetDeviceCaps(windowHDC, VERTRES);
 	colourBits       = GetDeviceCaps(windowHDC, BITSPIXEL);
 	refreshRate      = GetDeviceCaps(windowHDC, VREFRESH);
 
 	return enterFullscreenExt(hwnd, fullscreenWidth, fullscreenHeight, colourBits, refreshRate);
 }
 
 static int exitFullscreen(HWND hwnd)
 {
     int isChangeSuccessful;
 
     SetWindowLongPtr(hwnd, GWL_EXSTYLE, WS_EX_LEFT);
     SetWindowLongPtr(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE);
     isChangeSuccessful = ChangeDisplaySettings(NULL, CDS_RESET) == DISP_CHANGE_SUCCESSFUL;
     SetWindowPos(hwnd, HWND_NOTOPMOST, savedXPosition, savedYPosition, savedWidth, savedHeight, SWP_SHOWWINDOW);
     ShowWindow(hwnd, SW_RESTORE);
 
     return isChangeSuccessful;
 }
 
 int isFullScreen = 0;
 
 void glutFullScreen()
 {
 	if (!isFullScreen)
 		enterFullscreen(hWnd);
 	isFullScreen = 1;
 }
 
 void glutExitFullScreen()
 {
 	if (isFullScreen)
 		exitFullscreen(hWnd);
 	isFullScreen = 0;
 }
 
 void glutToggleFullScreen()
 {
 	if (!isFullScreen)
 		enterFullscreen(hWnd);
 	else
 		exitFullscreen(hWnd);
 	isFullScreen = !isFullScreen;
 }
 
 // Added 2017-02-21
 void glutPositionWindow(int x, int y)
 {
 	RECT r;
 	GetWindowRect(hWnd, &r);
 	MoveWindow(hWnd, x, y, r.right-r.left, r.bottom-r.top, TRUE);
 }
 
 void glutReshapeWindow(int width, int height)
 {
 	RECT r;
 	GetWindowRect(hWnd, &r);
 	MoveWindow(hWnd, r.left, r.top, width, height, TRUE);
 }
 
 char glutMouseIsDown(unsigned int c)
 {
 	return gButtonPressed[c];
 }