~ / software / glut-macosx /

GLUT under Mac OS X

Copyright © 2006—2017, Renaud Blanch.
last updates:
- changed recommended installation directory to /Library/Frameworks because SIP forbids writing to /System/Library/Frameworks (and because that's better anyway) (oct. 2017).
- added horizontal wheel support, added support for modifiers while handling wheel events, slightly changed wheel event semantic (march, 23 2015).
- added homebrew formula for automated source install (february, 25 2015).
- fixed compilation for XCode 5.1 (may, 27 2014).
- added OpenGL 3.2 core profile support (january, 14 2014).
- scale added to window state (september, 19 2013).
- added HiDPI/retina support (april, 23 2013).
- updated to new glut sources from apple (march, 6 2008).
- added intel mac support (september, 27 2007).

What?

I often use GLUT to prototype software using OpenGL. While GLUT is convenient, it lacks features like the support for mouse scroll wheel events or HiDPI (aka retina display). You can find below a binary version of GLUT that support those features, as well as the patches and instructions to build it by your self.

Mouse wheel support

The support for mouse wheel is available for Linux and for GLUT/Win32 using a patched version of GLUT. It mimics the Linux behaviour, where the vertical scroll wheel is mapped on 2 pseudo-buttons. It even adds horizontal scroll wheel support with two other pseudo-buttons. The GLUT_DOWN and GLUT_UP state encodes two slightly different pieces of information: one GLUT_DOWN event is send per unit of scrolling (scroll events have a delta value encoding the amount of scrolling), whereas one GLUT_UP event is send per wheel event. So a mouse wheel event with a delta of 3 will emit this sequence of events: GLUT_DOWN, GLUT_DOWN, GLUT_DOWN, GLUT_UP.

The scroll wheel events are thus received in the traditional mouse callback:

// compatibility with original GLUT

#if !defined(GLUT_WHEEL_UP)
#  define GLUT_WHEEL_UP    3
#  define GLUT_WHEEL_DOWN  4
#  define GLUT_WHEEL_LEFT  5
#  define GLUT_WHEEL_RIGHT 6
#endif


void mouse(int button, int state, int x, int y);


int main(int argc, char *argv[]) {
   // ...
   glutMouseFunc(mouse);
   // ...
}


void mouse(int button, int state, int x, int y) {
   switch(button) {
   case GLUT_LEFT_BUTTON:
   case GLUT_RIGHT_BUTTON:
      // ...
      break;

   case GLUT_WHEEL_UP:
   case GLUT_WHEEL_DOWN:
      if(state == GLUT_DOWN) {
         // multiple down events: one for each unit of scrolling
      } else {
         assert(state == GLUT_UP);
         // single up event: one per wheel tick
      }
      
      // ...
      break;
   }
}

The same code should compile and run unchanged. However, you should be aware that replacing the vendor provided version of GLUT can potentially change the behaviour of existing programs under some circumstances. The following mouse event handler:


void mouse(int button, int state, int x, int y) {
   switch(button) {
   case GLUT_LEFT_BUTTON:
   case GLUT_MIDDLE_BUTTON:
   case GLUT_RIGHT_BUTTON:
      // ...
      break;

   default:
      // never reached with original GLUT
      assert(false);
      break;
   }
}

will work as expected. With the original GLUT, the default clause is never reached whereas with this modified version, it is.

HiDPI support

HiDPI support enables GLUT applications to handle an OpenGL context at the backing store resolution (i.e. in real screen pixels) rather than at the screen resolution (i.e. in screen points). You can find an explanation of the difference between pixels and points in "High Resolution Explained: Features and Benefits". To enable HiDPI for your windows, a capability has been added to the string accepted by glutInitDisplayString:

glutInitDisplayString("rgba stencil double samples=8 hidpi");

When the hidpi capability is enabled, the GLUT functions that deal with coordinates and sizes of the OpenGL context are interpreted as real pixels rather than as screen points. The functionalities touched are:

This as some consequences that can be surprising but are logical. First, all pixels are not reachable with the mouse cursor: since the cursor jumps from screen point to screen point, some pixels can not be hovered. This can be annoying if you want to make single pixels clickable (probably a bad idea though), but is not avoidable since the display has then a higher resolution than the input device. Second, the size of the windows will change when dragged from one display that supports HiDPI to another that does not. In fact, the size of the window will remain the same in pixels, but since the ratio between pixels and points (aka backingScaleFactor) changes, its size in screen points changes.

Finally, it can be necessary to set or know the size of a window in points (e.g. to do some window layout on screen). To suit this use case, we added the scale of each window to its state exported by glutGet. For example, an application consisting of two windows (one standard, and one with HiDPI enabled), each occupying half of the screen can be achieved like that:

#include <stdlib.h>
#include <stdio.h>

#include <GLUT/glut.h>


// compatibility with original GLUT
#if !defined(GLUT_WINDOW_SCALE)
#	define GLUT_WINDOW_SCALE 199
#endif

void display() {
	glutSwapBuffers();
}

void reshape(int width, int height) {
	printf("window: %i, (%ix%i at scale %i)\n",
	       glutGetWindow(),
	       glutGet(GLUT_WINDOW_WIDTH), glutGet(GLUT_WINDOW_HEIGHT),
	       glutGet(GLUT_WINDOW_SCALE)
	);
}

int main(int argc, char *argv[]) {
	glutInit(&argc, argv);
	int W = glutGet(GLUT_SCREEN_WIDTH);
	int H = glutGet(GLUT_SCREEN_HEIGHT);
	
	glutInitDisplayString("rgba double hidpi");
	int w0 = glutCreateWindow("hidpi");
	glutReshapeFunc(reshape);
	glutDisplayFunc(display);
	int s0 = glutGet(GLUT_WINDOW_SCALE);
	if(s0 < 0) // unsupported by original GLUT
		s0 = 1;
	glutReshapeWindow(W/2*s0, H*s0);
	glutPositionWindow(0, 0);
	
	glutInitDisplayString("rgba double");
	int w1 = glutCreateWindow("non hidpi");
	glutReshapeFunc(reshape);
	glutDisplayFunc(display);
	int s1 = glutGet(GLUT_WINDOW_SCALE);
	if(s1 < 0) // unsupported by original GLUT
		s1 = 1;
	glutReshapeWindow(W/2*s1, H*s1);
	glutPositionWindow(W/2, 0);

	glutMainLoop();
	
	return EXIT_SUCCESS;
}

OpenGL 3.2 core profile support

The GLUT framework shipped with Mac OS X 10.8 and 10.9 supports OpenGL 3.2 core profile by the way of an additional capability (specified by the GLUT_3_2_CORE_PROFILE mask). Unfortunately, Apple did not release the sources of those GLUT versions. We have reimplemented this support (as well as the GLUT_NO_RECOVERY support) in this version of the fork from the latest published GLUT source code. So, if you want to do some code targeting OpenGL 3.2 core profile with GLUT, you can by specifying:

int main(int argc, char *argv[]) {
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGBA|GLUT_DOUBLE|GLUT_3_2_CORE_PROFILE);
	...

or using the display string (addition by our implementation):

int main(int argc, char *argv[]) {
	glutInit(&argc, argv);
	glutInitDisplayString("rgba double core");
	...

Getting and installing

Binaries

I provide a build of the modified GLUT framework. Putting the framework in the right location (/Library/Frameworks/) will make the new feature available to all GLUT applications. Be careful to preserve the symbolic links of the framework while putting it there (either use the finder to drag-and-drop the framework to the right location, use the ditto command line, or decompress the archive at the right location):

[esperluet:~] blanch% cd /Library/Frameworks
[esperluet:/Library/Frameworks] blanch% sudo tar xzvf ~/Downloads/GLUT.framework.tgz
Password:
x GLUT.framework/
[...]
[esperluet:/Library/Frameworks] blanch% 

If you want to go back to the original framework, simply remove /Library/Frameworks/GLUT.framework, the system will then fallbak to the Apple provided /System/Library/Frameworks/GLUT.framework.

The framework has been compiled on an intel MacBook Pro running Mac OS X Snow Lion (10.8.3), using the MacOSX10.5.sdk deployment target. The mouse wheel support is still available for older platforms.

Building yourself

If you want (or need) to compile the modified GLUT yourself, you'll need the GLUT-3.4.0 sources (the last version published by Apple is labelled "Version 3.3.2, 2008-02-08") (local mirror), and the following patches:

  1. glut-3.3.2-0-compilation.patch which fixes compilation issues (adapted from another fork of GLUT);
  2. glut-3.3.2-1-mousewheel.patch which adds support for the mouse wheel; and
  3. glut-3.3.2-2-hidpi.patch which adds support for HiDPI.
  4. glut-3.3.2-3-foreground.patch which brings GLUT application to foreground on startup.
  5. glut-3.3.2-4-3.2core.patch which restores support for OpenGL 3.2 core profile.

[esperluet:~] blanch% cd temp/
[esperluet:~/temp] blanch% curl -sO http://iihm.imag.fr/blanch/software/glut-macosx/glut-3.3.2.zip
[esperluet:~/temp] blanch% unzip ~/Downloads/glut-3.3.2.zip
Archive:  /Users/blanch/Downloads/glut-3.3.2.zip
   creating: glut/
[...]
[esperluet:~/temp] blanch% cd glut/
[esperluet:~/temp/glut] blanch% curl -s http://iihm.imag.fr/blanch/software/glut-macosx/patches/glut-3.3.2-0-compilation.patch | patch -p 1
patching file GLUTApplication.h
patching file GLUTClipboardController.h
patching file GLUTClipboardController.m
patching file GLUTWindow.h
patching file GLUTWindow.m
patching file macx_util.m
[esperluet:~/temp/glut] blanch% curl -s http://iihm.imag.fr/blanch/software/glut-macosx/patches/glut-3.3.2-1-mousewheel.patch | patch -p 1
patching file GLUTView.m
patching file glut.h
[esperluet:~/temp/glut] blanch% curl -s http://iihm.imag.fr/blanch/software/glut-macosx/patches/glut-3.3.2-2-hidpi.patch | patch -p 1
patching file GLUTView.m
patching file GLUTWindow.m
patching file macx_dstr.m
[esperluet:~/temp/glut] blanch% curl -s http://iihm.imag.fr/blanch/software/glut-macosx/patches/glut-3.3.2-3-foreground.patch | patch -p 1
patching file macx_init.m
[esperluet:~/temp/glut] blanch% curl -s http://iihm.imag.fr/blanch/software/glut-macosx/patches/glut-3.3.2-4-3.2core.patch | patch -p 1
patching file glut.h
patching file macx_dstr.m
patching file macx_glut.h
patching file macx_win.m

You can now build the GLUT framework using the XCode tools:

[esperluet:~/temp/glut] blanch% xcodebuild -list
Information about project "GLUT_External":
    Targets:
        GLUT

    Build Configurations:
        Debug
        Release

    If no build configuration is specified and -scheme is not passed then "Debug" is used.

    This project contains no schemes.
[esperluet:~/temp/glut] blanch% xcodebuild -configuration Release
=== BUILD NATIVE TARGET GLUT OF PROJECT GLUT_External WITH CONFIGURATION Release ===
...

** BUILD SUCCEEDED **

[esperluet:~/temp/glut] blanch% ls -l build/Release/
total 0
drwxr-xr-x  6 blanch  staff  204 29 avr 13:18 GLUT.framework
[esperluet:~/temp/glut] blanch% 
 

Installing using homebrew

Stéphane Conversy contributed a homebrew formula to automate the build from source and installation process. Thus, you should be able to perform a build and install by just running:

brew install http://iihm.imag.fr/blanch/software/glut-macosx/contribs/glut.rb