Browse Source

Merged SDL2 changes from bartbes/love-experiments/SDL2 into default.

- Added love.system module, including clipboard functions.

- Added custom hardware cursors (love.mouse.newCursor, love.mouse.setCursor.)

- Revamped love.joystick:
-- added Joystick objects and moved love.joystick functions which operated on individual joysticks to methods on the Joystick objects.
-- Added love.joystick.getJoystick and love.joystick.getJoystickCount.
-- Added events for when joysticks are connected and disconnected.
-- Added 'Gamepad' methods to Joystick objects: Joystick:isGamepad, Joystick:getGamepadAxis, and Joystick:isGamepadDown. They're a common abstraction for xbox controller-like joystick across operating systems.

- Added monitor choosing support and "fullscreen desktop" mode to love.window.

- Moved unicode text input events to their own callback function (love.textinput), removed the key repeat functions from love.keyboard and made the second argument of love.keypressed a boolean saying whether the keypress was a repeat.

- liblove now works with love.window and love.graphics in OS X
Alex Szpakowski 12 years ago
parent
commit
113ed1cfe7
67 changed files with 4645 additions and 1619 deletions
  1. 47 0
      platform/macosx/OSX.h
  2. 72 0
      platform/macosx/OSX.mm
  3. 0 12
      platform/macosx/SDLMain.h
  4. 0 464
      platform/macosx/SDLMain.m
  5. 108 24
      platform/macosx/love-framework.xcodeproj/project.pbxproj
  6. 14 36
      platform/macosx/love.xcodeproj/project.pbxproj
  7. 0 7
      platform/macosx/love_Prefix.pch
  8. 4 2
      src/common/Reference.cpp
  9. 1 0
      src/common/config.h
  10. 6 0
      src/common/runtime.cpp
  11. 12 0
      src/common/types.h
  12. 62 2
      src/love.cpp
  13. 446 239
      src/modules/event/sdl/Event.cpp
  14. 10 6
      src/modules/event/sdl/Event.h
  15. 2 2
      src/modules/event/sdl/wrap_Event.cpp
  16. 5 0
      src/modules/graphics/Graphics.h
  17. 26 13
      src/modules/graphics/opengl/Graphics.cpp
  18. 1 0
      src/modules/graphics/opengl/Graphics.h
  19. 85 2
      src/modules/joystick/Joystick.cpp
  20. 133 5
      src/modules/joystick/Joystick.h
  21. 99 0
      src/modules/joystick/JoystickModule.h
  22. 205 121
      src/modules/joystick/sdl/Joystick.cpp
  23. 59 22
      src/modules/joystick/sdl/Joystick.h
  24. 450 0
      src/modules/joystick/sdl/JoystickModule.cpp
  25. 81 0
      src/modules/joystick/sdl/JoystickModule.h
  26. 124 82
      src/modules/joystick/sdl/wrap_Joystick.cpp
  27. 17 11
      src/modules/joystick/sdl/wrap_Joystick.h
  28. 244 0
      src/modules/joystick/sdl/wrap_JoystickModule.cpp
  29. 47 0
      src/modules/joystick/sdl/wrap_JoystickModule.h
  30. 100 51
      src/modules/keyboard/Keyboard.cpp
  31. 103 72
      src/modules/keyboard/Keyboard.h
  32. 199 166
      src/modules/keyboard/sdl/Keyboard.cpp
  33. 5 5
      src/modules/keyboard/sdl/Keyboard.h
  34. 0 24
      src/modules/keyboard/wrap_Keyboard.cpp
  35. 0 2
      src/modules/keyboard/wrap_Keyboard.h
  36. 2 0
      src/modules/love/love.cpp
  37. 79 0
      src/modules/mouse/Cursor.cpp
  38. 100 0
      src/modules/mouse/Cursor.h
  39. 10 0
      src/modules/mouse/Mouse.h
  40. 122 0
      src/modules/mouse/sdl/Cursor.cpp
  41. 64 0
      src/modules/mouse/sdl/Cursor.h
  42. 63 5
      src/modules/mouse/sdl/Mouse.cpp
  43. 17 0
      src/modules/mouse/sdl/Mouse.h
  44. 66 0
      src/modules/mouse/wrap_Cursor.cpp
  45. 39 0
      src/modules/mouse/wrap_Cursor.h
  46. 86 3
      src/modules/mouse/wrap_Mouse.cpp
  47. 5 1
      src/modules/mouse/wrap_Mouse.h
  48. 63 0
      src/modules/system/System.cpp
  49. 101 0
      src/modules/system/System.h
  50. 91 0
      src/modules/system/sdl/System.cpp
  51. 66 0
      src/modules/system/sdl/System.h
  52. 112 0
      src/modules/system/wrap_System.cpp
  53. 43 0
      src/modules/system/wrap_System.h
  54. 6 4
      src/modules/thread/ThreadModule.cpp
  55. 16 10
      src/modules/thread/ThreadModule.h
  56. 1 1
      src/modules/thread/sdl/Thread.cpp
  57. 2 2
      src/modules/thread/sdl/threads.cpp
  58. 10 5
      src/modules/thread/wrap_ThreadModule.cpp
  59. 2 1
      src/modules/thread/wrap_ThreadModule.h
  60. 22 0
      src/modules/window/Window.cpp
  61. 61 18
      src/modules/window/Window.h
  62. 382 145
      src/modules/window/sdl/Window.cpp
  63. 34 5
      src/modules/window/sdl/Window.h
  64. 104 28
      src/modules/window/wrap_Window.cpp
  65. 4 1
      src/modules/window/wrap_Window.h
  66. 34 7
      src/scripts/boot.lua
  67. 71 13
      src/scripts/boot.lua.h

+ 47 - 0
platform/macosx/OSX.h

@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2006-2013 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#ifndef LOVE_OSX_H
+#define LOVE_OSX_H
+
+#include <string>
+
+namespace love
+{
+namespace osx
+{
+
+/**
+ * Returns the filepath of the first detected love file in the Resources folder
+ * in love.app.
+ * Returns an empty string if no love file is found.
+ **/
+std::string getLoveInResources();
+
+/**
+ * Checks for drop-file events. Returns the filepath if an event occurred, or
+ * an empty string otherwise.
+ **/
+std::string checkDropEvents();
+
+} // osx
+} // love
+
+#endif // LOVE_OSX_H

+ 72 - 0
platform/macosx/OSX.mm

@@ -0,0 +1,72 @@
+/**
+ * Copyright (c) 2006-2013 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#import "osx.h"
+#import <Foundation/Foundation.h>
+
+#include <SDL2/SDL.h>
+
+namespace love
+{
+namespace osx
+{
+
+std::string getLoveInResources()
+{
+	std::string path;
+
+	// check to see if there are any .love files in Resources - props to stevejohnson/diordna
+	NSArray *lovePaths = [[NSBundle mainBundle] pathsForResourcesOfType:@"love" inDirectory:nil];
+	if ([lovePaths count] > 0)
+	{
+		NSString *firstLovePath = [lovePaths objectAtIndex:0];
+		path = std::string([firstLovePath UTF8String]);
+	}
+
+	return path;
+}
+
+std::string checkDropEvents()
+{
+	std::string dropstr;
+	SDL_Event event;
+
+	bool initvideo = SDL_WasInit(SDL_INIT_VIDEO) != 0;
+	if (!initvideo)
+		SDL_InitSubSystem(SDL_INIT_VIDEO);
+
+	SDL_PumpEvents();
+	if (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_DROPFILE, SDL_DROPFILE) > 0)
+	{
+		if (event.type == SDL_DROPFILE)
+		{
+			dropstr = std::string(event.drop.file);
+			SDL_free(event.drop.file);
+		}
+	}
+
+	if (!initvideo)
+		SDL_QuitSubSystem(SDL_INIT_VIDEO);
+
+	return dropstr;
+}
+
+} // osx
+} // love

+ 0 - 12
platform/macosx/SDLMain.h

@@ -1,12 +0,0 @@
-/*   SDLMain.m - main entry point for our Cocoa-ized SDL app
-       Initial Version: Darrell Walisser <[email protected]>
-       Non-NIB-Code & other changes: Max Horn <[email protected]>
-
-    Feel free to customize this file to suit your needs
-*/
-
-#import <Cocoa/Cocoa.h>
-#include "SDL/SDL.h"
-
-@interface SDLMain : NSObject
-@end

+ 0 - 464
platform/macosx/SDLMain.m

@@ -1,464 +0,0 @@
-/*   SDLMain.m - main entry point for our Cocoa-ized SDL app
-	   Initial Version: Darrell Walisser <[email protected]>
-	   Non-NIB-Code & other changes: Max Horn <[email protected]>
-
-	Feel free to customize this file to suit your needs
-*/
-
-//#import "SDL.h"
-#import "SDLMain.h"
-#import <sys/param.h> /* for MAXPATHLEN */
-#import <unistd.h>
-
-/* Use this flag to determine whether we use SDLMain.nib or not */
-#define		SDL_USE_NIB_FILE	0
-
-/* Use this flag to determine whether we use CPS (docking) or not */
-/* We can't submit to the Mac App Store with this enabled: */
-/* http://www.philhassey.com/blog/2011/02/ */
-/*#define		SDL_USE_CPS		1 */
-#ifdef SDL_USE_CPS
-/* Portions of CPS.h */
-typedef struct CPSProcessSerNum
-{
-	UInt32		lo;
-	UInt32		hi;
-} CPSProcessSerNum;
-
-extern OSErr	CPSGetCurrentProcess( CPSProcessSerNum *psn);
-extern OSErr 	CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
-extern OSErr	CPSSetFrontProcess( CPSProcessSerNum *psn);
-
-#endif /* SDL_USE_CPS */
-
-static int    gArgc;
-static char  **gArgv;
-static BOOL   gFinderLaunch;
-static BOOL   gCalledAppMainline = FALSE;
-
-static NSString *getApplicationName(void)
-{
-	NSDictionary *dict;
-	NSString *appName = 0;
-
-	/* Determine the application name */
-	dict = (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle());
-	if (dict)
-		appName = [dict objectForKey: @"CFBundleName"];
-
-	if (![appName length])
-		appName = [[NSProcessInfo processInfo] processName];
-
-	return appName;
-}
-
-#if SDL_USE_NIB_FILE
-/* A helper category for NSString */
-@interface NSString (ReplaceSubString)
-- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString;
-@end
-#endif
-
-@interface SDLApplication : NSApplication
-@end
-
-@implementation SDLApplication
-/* Invoked from the Quit menu item */
-- (void)terminate:(id)sender
-{
-	/* Post a SDL_QUIT event */
-	SDL_Event event;
-	event.type = SDL_QUIT;
-	SDL_PushEvent(&event);
-}
-
-/* Hack to make Cocoa ignore keystrokes that aren't shortcuts, otherwise it beeps on every key press */
-- (void)sendEvent:(NSEvent *)theEvent {
-	if (NSKeyDown == [theEvent type] || NSKeyUp == [theEvent type]) {
-		if ([theEvent modifierFlags] & NSCommandKeyMask)
-			[super sendEvent: theEvent];
-	} else {
-		[super sendEvent: theEvent];
-	}
-}
-@end
-
-/* The main class of the application, the application's delegate */
-@implementation SDLMain
-
-/* Set the working directory to the .app's parent directory */
-/*CHANGED to Bundle's Resource Directory. */
-- (void) setupWorkingDirectory:(BOOL)shouldChdir
-{
-	if (shouldChdir)
-	{
-		/*
-		char parentdir[PATH_MAX];
-		//CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
-		CFURLRef url = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle());
-		CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url);
-		if (CFURLGetFileSystemRepresentation(url2, true, (UInt8 *)parentdir, PATH_MAX)) {
-			assert ( chdir (parentdir) == 0 );   // chdir to the binary app's parent //
-		}
-		CFRelease(url);
-		CFRelease(url2);
-		*/
-		CFBundleRef mainBundle = CFBundleGetMainBundle();
-		CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle);
-		char path[PATH_MAX];
-		if (!CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)path, PATH_MAX))
-		{
-			// error!
-		}
-		CFRelease(resourcesURL);
-		chdir(path);
-	}
-
-}
-
-#if SDL_USE_NIB_FILE
-
-/* Fix menu to contain the real app name instead of "SDL App" */
-- (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName
-{
-	NSRange aRange;
-	NSEnumerator *enumerator;
-	NSMenuItem *menuItem;
-
-	aRange = [[aMenu title] rangeOfString:@"SDL App"];
-	if (aRange.length != 0)
-		[aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]];
-
-	enumerator = [[aMenu itemArray] objectEnumerator];
-	while ((menuItem = [enumerator nextObject]))
-	{
-		aRange = [[menuItem title] rangeOfString:@"SDL App"];
-		if (aRange.length != 0)
-			[menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]];
-		if ([menuItem hasSubmenu])
-			[self fixMenu:[menuItem submenu] withAppName:appName];
-	}
-	[ aMenu sizeToFit ];
-}
-
-#else
-
-static void setApplicationMenu(void)
-{
-	/* warning: this code is very odd */
-	NSMenu *appleMenu;
-	NSMenuItem *menuItem;
-	NSString *title;
-	NSString *appName;
-
-	appName = getApplicationName();
-	appleMenu = [[NSMenu alloc] initWithTitle:@""];
-
-	/* Add menu items */
-	title = [@"About " stringByAppendingString:appName];
-	[appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
-
-	[appleMenu addItem:[NSMenuItem separatorItem]];
-
-	title = [@"Hide " stringByAppendingString:appName];
-	[appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
-
-	menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
-	[menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
-
-	[appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
-
-	[appleMenu addItem:[NSMenuItem separatorItem]];
-
-	title = [@"Quit " stringByAppendingString:appName];
-	[appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
-
-
-	/* Put menu into the menubar */
-	menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
-	[menuItem setSubmenu:appleMenu];
-	[[NSApp mainMenu] addItem:menuItem];
-
-	/* Finally give up our references to the objects */
-	[appleMenu release];
-	[menuItem release];
-}
-
-/* Create a window menu */
-static void setupWindowMenu(void)
-{
-	NSMenu      *windowMenu;
-	NSMenuItem  *windowMenuItem;
-	NSMenuItem  *menuItem;
-
-	windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
-
-	/* "Minimize" item */
-	menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
-	[windowMenu addItem:menuItem];
-	[menuItem release];
-
-	/* Put menu into the menubar */
-	windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
-	[windowMenuItem setSubmenu:windowMenu];
-	[[NSApp mainMenu] addItem:windowMenuItem];
-
-	/* Tell the application object that this is now the window menu */
-	[NSApp setWindowsMenu:windowMenu];
-
-	/* Finally give up our references to the objects */
-	[windowMenu release];
-	[windowMenuItem release];
-}
-
-/* Replacement for NSApplicationMain */
-static void CustomApplicationMain (int argc, char **argv)
-{
-	NSAutoreleasePool	*pool = [[NSAutoreleasePool alloc] init];
-	SDLMain				*sdlMain;
-
-	/* Ensure the application object is initialised */
-	[SDLApplication sharedApplication];
-	
-#ifdef SDL_USE_CPS
-	{
-		CPSProcessSerNum PSN;
-		/* Tell the dock about us */
-		if (!CPSGetCurrentProcess(&PSN))
-			if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
-				if (!CPSSetFrontProcess(&PSN))
-					[SDLApplication sharedApplication];
-	}
-#else
-	{
-		ProcessSerialNumber psn;
-
-		if (!GetCurrentProcess(&psn)) {
-			TransformProcessType(&psn, kProcessTransformToForegroundApplication);
-			SetFrontProcess(&psn);
-		}
-	}
-#endif /* SDL_USE_CPS */
-
-	/* Set up the menubar */
-	[NSApp setMainMenu:[[[NSMenu alloc] init] autorelease]];
-	setApplicationMenu();
-	setupWindowMenu();
-
-	/* Create SDLMain and make it the app delegate */
-	sdlMain = [[SDLMain alloc] init];
-	[NSApp setDelegate:sdlMain];
-
-	/* Start the main event loop */
-	[NSApp run];
-
-	[sdlMain release];
-	[pool release];
-}
-
-#endif
-
-
-/*
- * Catch document open requests...this lets us notice files when the app
- *  was launched by double-clicking a document, or when a document was
- *  dragged/dropped on the app's icon. You need to have a
- *  CFBundleDocumentsType section in your Info.plist to get this message,
- *  apparently.
- *
- * Files are added to gArgv, so to the app, they'll look like command line
- *  arguments. Previously, apps launched from the finder had nothing but
- *  an argv[0].
- *
- * This message may be received multiple times to open several docs on launch.
- *
- * This message is ignored once the app's mainline has been called.
- */
-- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
-{
-	const char *temparg;
-	size_t arglen;
-	char *arg;
-	char **newargv;
-
-	// Disabled because the method for detecting this doesn't work in OS 10.9.
-//	if (!gFinderLaunch)  /* MacOS is passing command line args. */
-//		return FALSE;
-
-	if (gCalledAppMainline)  /* app has started, ignore this document. */
-		return FALSE;
-
-	temparg = [filename UTF8String];
-	arglen = SDL_strlen(temparg) + 1;
-	arg = (char *) SDL_malloc(arglen);
-	if (arg == NULL)
-		return FALSE;
-
-	newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2));
-	if (newargv == NULL)
-	{
-		SDL_free(arg);
-		return FALSE;
-	}
-	gArgv = newargv;
-
-	SDL_strlcpy(arg, temparg, arglen);
-	gArgv[gArgc++] = arg;
-	gArgv[gArgc] = NULL;
-	return TRUE;
-}
-
-
-/* Called when the internal event loop has just started running */
-- (void) applicationDidFinishLaunching: (NSNotification *) note
-{
-	int status;
-
-	/* Set the working directory to the .app's Resources folder */
-	[self setupWorkingDirectory:gCalledAppMainline];
-
-#if SDL_USE_NIB_FILE
-	/* Set the main menu to contain the real app name instead of "SDL App" */
-	[self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()];
-#endif
-
-	/* Set up the app to receive menu events via keyboard shortcut */
-	setenv("SDL_ENABLEAPPEVENTS", "1", 1);
-
-	/* Hand off to main application code */
-	gCalledAppMainline = TRUE;
-	status = SDL_main (gArgc, gArgv);
-
-	/* We're done, thank you for playing */
-	exit(status);
-}
-@end
-
-
-@implementation NSString (ReplaceSubString)
-
-- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString
-{
-	unsigned int bufferSize;
-	unsigned int selfLen = [self length];
-	unsigned int aStringLen = [aString length];
-	unichar *buffer;
-	NSRange localRange;
-	NSString *result;
-
-	bufferSize = selfLen + aStringLen - aRange.length;
-	buffer = NSAllocateMemoryPages(bufferSize*sizeof(unichar));
-
-	/* Get first part into buffer */
-	localRange.location = 0;
-	localRange.length = aRange.location;
-	[self getCharacters:buffer range:localRange];
-
-	/* Get middle part into buffer */
-	localRange.location = 0;
-	localRange.length = aStringLen;
-	[aString getCharacters:(buffer+aRange.location) range:localRange];
-
-	/* Get last part into buffer */
-	localRange.location = aRange.location + aRange.length;
-	localRange.length = selfLen - localRange.location;
-	[self getCharacters:(buffer+aRange.location+aStringLen) range:localRange];
-
-	/* Build output string */
-	result = [NSString stringWithCharacters:buffer length:bufferSize];
-
-	NSDeallocateMemoryPages(buffer, bufferSize);
-
-	return result;
-}
-
-@end
-
-
-
-#ifdef main
-#  undef main
-#endif
-
-
-/* Main entry point to executable - should *not* be SDL_main! */
-int main (int argc, char **argv)
-{
-	/* Copy the arguments into a global variable */
-	/* This is passed if we are launched by double-clicking */
-	if (argc >= 2 && strncmp (argv[1], "-psn", 4) == 0)
-	{
-		gArgv = (char **) SDL_malloc(sizeof (char *) * 2);
-		gArgv[0] = argv[0];
-		gArgv[1] = NULL;
-		gArgc = 1;
-		gFinderLaunch = YES;
-	}
-	else
-	{
-		int i;
-		gArgc = argc;
-		gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1));
-		for (i = 0; i <= argc; i++)
-			gArgv[i] = argv[i];
-		gFinderLaunch = NO;
-	}
-
-	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
-	/* check to see if there are any .love files in Resources - props to stevejohnson/diordna */
-	NSArray *lovePaths = [[NSBundle mainBundle] pathsForResourcesOfType:@"love" inDirectory:nil];
-
-	if ([lovePaths count] > 0) /* there are, load the first one we found and run it */
-	{
-		NSString *firstLovePath = [lovePaths objectAtIndex:0];
-		gCalledAppMainline = YES;
-
-		const char *temparg;
-		size_t arglen;
-		char *arg;
-		char **newargv;
-
-		temparg = [firstLovePath UTF8String];
-		arglen = SDL_strlen(temparg) + 1;
-
-		arg = (char *)SDL_malloc(arglen);
-		if (arg == NULL)
-			return FALSE;
-
-		const char *fusedstr = "--fused";
-		size_t fusedarglen = SDL_strlen(fusedstr) + 1;
-
-		char *fusedarg = (char *) SDL_malloc(fusedarglen);
-		if (fusedarg == NULL)
-		{
-			SDL_free(arg);
-			return FALSE;
-		}
-
-		newargv = (char **) realloc(gArgv, sizeof(char *) * (gArgc + 3));
-		if (newargv == NULL)
-		{
-			SDL_free(arg);
-			SDL_free(fusedarg);
-			return FALSE;
-		}
-
-		gArgv = newargv;
-		SDL_strlcpy(arg, temparg, arglen);
-		SDL_strlcpy(fusedarg, fusedstr, fusedarglen);
-		gArgv[gArgc++] = arg;
-		gArgv[gArgc++] = fusedarg; // run in pseudo-fused mode
-		gArgv[gArgc] = NULL;
-	}
-
-	[pool drain];
-
-#if SDL_USE_NIB_FILE
-	[SDLApplication poseAsClass:[NSApplication class]];
-	NSApplicationMain (argc, argv);
-#else
-	CustomApplicationMain (argc, argv);
-#endif
-	return 0;
-}

+ 108 - 24
platform/macosx/love-framework.xcodeproj/project.pbxproj

@@ -135,9 +135,8 @@
 		FA08F63016C7542600F007B5 /* DevilHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1AA7781A230065F346E2313A /* DevilHandler.cpp */; };
 		FA08F63116C7542600F007B5 /* wrap_Image.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0B0728FA73B107B37A956A09 /* wrap_Image.cpp */; };
 		FA08F63216C7542600F007B5 /* wrap_ImageData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 076840774B0B6E721D0C18D0 /* wrap_ImageData.cpp */; };
-		FA08F63316C7542D00F007B5 /* Joystick.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 677F545C76EA3B247329358D /* Joystick.cpp */; };
-		FA08F63416C7542D00F007B5 /* Joystick.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55B425307C0C1C4B3EFC3A5F /* Joystick.cpp */; };
-		FA08F63516C7542D00F007B5 /* wrap_Joystick.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 139411436818381E493F00F5 /* wrap_Joystick.cpp */; };
+		FA08F63416C7542D00F007B5 /* JoystickModule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 55B425307C0C1C4B3EFC3A5F /* JoystickModule.cpp */; };
+		FA08F63516C7542D00F007B5 /* wrap_JoystickModule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 139411436818381E493F00F5 /* wrap_JoystickModule.cpp */; };
 		FA08F63616C7543400F007B5 /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 524741891BB93848039F4174 /* Keyboard.cpp */; };
 		FA08F63716C7543400F007B5 /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 43BC2B1C505E5EFF650C31E3 /* Keyboard.cpp */; };
 		FA08F63816C7543400F007B5 /* wrap_Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 19F40DF6507028212FEB1D77 /* wrap_Keyboard.cpp */; };
@@ -208,6 +207,18 @@
 		FA08F67B16C754BA00F007B5 /* Window.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 351B09E51FDC338622F44624 /* Window.cpp */; };
 		FA08F67C16C754BA00F007B5 /* Window.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6CDD4F3320303D222C180CD0 /* Window.cpp */; };
 		FA0CDE3D1710F9A50056E8D7 /* FormatHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0CDE3B1710F9A50056E8D7 /* FormatHandler.h */; };
+		FA1EF7C41799FEAC00FF380C /* wrap_System.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA34E7D4174B55AF00E04D3F /* wrap_System.cpp */; };
+		FA1EF7C51799FEB200FF380C /* wrap_System.h in Headers */ = {isa = PBXBuildFile; fileRef = FA34E7D5174B55AF00E04D3F /* wrap_System.h */; };
+		FA34E7CD174B513F00E04D3F /* System.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA34E7CB174B513F00E04D3F /* System.cpp */; };
+		FA34E7CE174B513F00E04D3F /* System.h in Headers */ = {isa = PBXBuildFile; fileRef = FA34E7CC174B513F00E04D3F /* System.h */; };
+		FA34E7D2174B515D00E04D3F /* System.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA34E7D0174B515D00E04D3F /* System.cpp */; };
+		FA34E7D3174B515D00E04D3F /* System.h in Headers */ = {isa = PBXBuildFile; fileRef = FA34E7D1174B515D00E04D3F /* System.h */; };
+		FA3D9E0D16E68DE600CA6630 /* Cursor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA3D9E0B16E68DE600CA6630 /* Cursor.cpp */; };
+		FA3D9E0E16E68DE600CA6630 /* Cursor.h in Headers */ = {isa = PBXBuildFile; fileRef = FA3D9E0C16E68DE600CA6630 /* Cursor.h */; };
+		FA3D9E1116E68EAE00CA6630 /* Cursor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA3D9E0F16E68EAE00CA6630 /* Cursor.cpp */; };
+		FA3D9E1216E68EAE00CA6630 /* Cursor.h in Headers */ = {isa = PBXBuildFile; fileRef = FA3D9E1016E68EAE00CA6630 /* Cursor.h */; };
+		FA3D9E1516E6D41100CA6630 /* wrap_Cursor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA3D9E1316E6D41000CA6630 /* wrap_Cursor.cpp */; };
+		FA3D9E1616E6D41100CA6630 /* wrap_Cursor.h in Headers */ = {isa = PBXBuildFile; fileRef = FA3D9E1416E6D41000CA6630 /* wrap_Cursor.h */; };
 		FA435EA317B36E9C004C3F22 /* Polyline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA435EA117B36E9C004C3F22 /* Polyline.cpp */; };
 		FA435EA417B36E9C004C3F22 /* Polyline.h in Headers */ = {isa = PBXBuildFile; fileRef = FA435EA217B36E9C004C3F22 /* Polyline.h */; };
 		FA5454C216F1310000D30303 /* MathModule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA5454C016F1310000D30303 /* MathModule.cpp */; };
@@ -222,7 +233,6 @@
 		FA577AC816C7513C00860150 /* Ogg.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA577A7116C719F400860150 /* Ogg.framework */; };
 		FA577ACA16C7514100860150 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA577A7C16C71A2600860150 /* OpenGL.framework */; };
 		FA577ACB16C7514400860150 /* physfs.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA577A7316C719F900860150 /* physfs.framework */; };
-		FA577ACC16C7514700860150 /* SDL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA577A7516C719FF00860150 /* SDL.framework */; };
 		FA577ACD16C7514C00860150 /* Vorbis.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA577A7716C71A0800860150 /* Vorbis.framework */; };
 		FA5FDC791788D548002F0ED2 /* enet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA5FDC5F1788D548002F0ED2 /* enet.cpp */; };
 		FA5FDC7A1788D548002F0ED2 /* callbacks.c in Sources */ = {isa = PBXBuildFile; fileRef = FA5FDC611788D548002F0ED2 /* callbacks.c */; };
@@ -251,11 +261,18 @@
 		FA7175AA178E8418001FE7FE /* lua.h in Headers */ = {isa = PBXBuildFile; fileRef = FA7175A9178E8418001FE7FE /* lua.h */; };
 		FA7C937A16DCC6C2006F2BEE /* wrap_Math.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA7C937516DCC6C2006F2BEE /* wrap_Math.cpp */; };
 		FA7C937B16DCC6C2006F2BEE /* wrap_Math.h in Headers */ = {isa = PBXBuildFile; fileRef = FA7C937616DCC6C2006F2BEE /* wrap_Math.h */; };
+		FA9B4A0816E1578300074F42 /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA9B4A0716E1578300074F42 /* SDL2.framework */; };
 		FA9FC0B0173D6E3E005027FF /* wrap_Window.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA9FC0AE173D6E3E005027FF /* wrap_Window.cpp */; };
 		FA9FC0B1173D6E3E005027FF /* wrap_Window.h in Headers */ = {isa = PBXBuildFile; fileRef = FA9FC0AF173D6E3E005027FF /* wrap_Window.h */; };
 		FAAC6B02170A373B008A61C5 /* CompressedData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAAC6B00170A373A008A61C5 /* CompressedData.cpp */; };
 		FAAC6B03170A373B008A61C5 /* CompressedData.h in Headers */ = {isa = PBXBuildFile; fileRef = FAAC6B01170A373A008A61C5 /* CompressedData.h */; };
 		FAAFF04416CB11C700CCDE45 /* OpenAL-Soft.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FAAFF04316CB11C700CCDE45 /* OpenAL-Soft.framework */; };
+		FAB0078F1740C12D00A9664D /* Joystick.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAB0078D1740C12D00A9664D /* Joystick.cpp */; };
+		FAB007901740C12D00A9664D /* Joystick.h in Headers */ = {isa = PBXBuildFile; fileRef = FAB0078E1740C12D00A9664D /* Joystick.h */; };
+		FAB007931740C28900A9664D /* Joystick.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAB007911740C28900A9664D /* Joystick.cpp */; };
+		FAB007941740C28900A9664D /* Joystick.h in Headers */ = {isa = PBXBuildFile; fileRef = FAB007921740C28900A9664D /* Joystick.h */; };
+		FAB007971740C87D00A9664D /* wrap_Joystick.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAB007951740C87D00A9664D /* wrap_Joystick.cpp */; };
+		FAB007981740C87D00A9664D /* wrap_Joystick.h in Headers */ = {isa = PBXBuildFile; fileRef = FAB007961740C87D00A9664D /* wrap_Joystick.h */; };
 		FAC5710017402D1100D147E4 /* BezierCurve.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAC570FC17402D1100D147E4 /* BezierCurve.cpp */; };
 		FAC5710117402D1100D147E4 /* BezierCurve.h in Headers */ = {isa = PBXBuildFile; fileRef = FAC570FD17402D1100D147E4 /* BezierCurve.h */; };
 		FAC5710217402D1100D147E4 /* wrap_BezierCurve.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAC570FE17402D1100D147E4 /* wrap_BezierCurve.cpp */; };
@@ -373,7 +390,7 @@
 		131F69C3368C4B8A55EE0DAD /* wrap_WeldJoint.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = wrap_WeldJoint.h; sourceTree = "<group>"; };
 		135801A6483528800C676492 /* io.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = io.c; sourceTree = "<group>"; };
 		138913BE5126483748FA43D0 /* Joint.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Joint.h; sourceTree = "<group>"; };
-		139411436818381E493F00F5 /* wrap_Joystick.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Joystick.cpp; sourceTree = "<group>"; };
+		139411436818381E493F00F5 /* wrap_JoystickModule.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_JoystickModule.cpp; sourceTree = "<group>"; };
 		14AE68E14C2C74526A612FA0 /* wrap_Image.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Image.cpp; sourceTree = "<group>"; };
 		15093E1B1A14176374C81299 /* wrap_CircleShape.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = wrap_CircleShape.h; sourceTree = "<group>"; };
 		153957EB332E1269671E7F4A /* b2CircleShape.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = b2CircleShape.h; sourceTree = "<group>"; };
@@ -453,7 +470,7 @@
 		2CAE75B079B828FE6892684A /* udp.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = udp.h; sourceTree = "<group>"; };
 		2CEC5ED5211174C7583849AD /* Reference.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Reference.h; sourceTree = "<group>"; };
 		2D290E902C451D6849051FEF /* b2WeldJoint.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = b2WeldJoint.h; sourceTree = "<group>"; };
-		2D7B7DEC4FC87878332E41B3 /* wrap_Joystick.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = wrap_Joystick.h; sourceTree = "<group>"; };
+		2D7B7DEC4FC87878332E41B3 /* wrap_JoystickModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = wrap_JoystickModule.h; sourceTree = "<group>"; };
 		2D8E6FAE2A100B5866E96BFF /* runtime.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = runtime.h; sourceTree = "<group>"; };
 		2D9475890CDA3D3776435622 /* Timer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Timer.h; sourceTree = "<group>"; };
 		2DC90F3C6160198256795C75 /* luasocket.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = luasocket.cpp; sourceTree = "<group>"; };
@@ -523,7 +540,7 @@
 		415E1438178736BE0EA908D5 /* select.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = select.c; sourceTree = "<group>"; };
 		426B1C4475DC54505B824B7F /* VertexBuffer.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = VertexBuffer.cpp; sourceTree = "<group>"; };
 		427B4B2517C0516844370E3D /* b2CollidePolygon.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = b2CollidePolygon.cpp; sourceTree = "<group>"; };
-		439E46D768A266780E894800 /* Joystick.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Joystick.h; sourceTree = "<group>"; };
+		439E46D768A266780E894800 /* JoystickModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JoystickModule.h; sourceTree = "<group>"; };
 		43A258C229C75C15238E520C /* Decoder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Decoder.h; sourceTree = "<group>"; };
 		43BC2B1C505E5EFF650C31E3 /* Keyboard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Keyboard.cpp; sourceTree = "<group>"; };
 		448C492C7AEB7840504F1C9D /* b2EdgeShape.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = b2EdgeShape.h; sourceTree = "<group>"; };
@@ -554,7 +571,7 @@
 		4B41232F7AF7793540F46C58 /* timeout.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = timeout.c; sourceTree = "<group>"; };
 		4B5F4DF8110020A96B5D3EAB /* b2BroadPhase.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = b2BroadPhase.cpp; sourceTree = "<group>"; };
 		4B731754147B27AF73AC5358 /* Volatile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Volatile.cpp; sourceTree = "<group>"; };
-		4C606AEC342457600C3F0741 /* Joystick.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Joystick.h; sourceTree = "<group>"; };
+		4C606AEC342457600C3F0741 /* JoystickModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JoystickModule.h; sourceTree = "<group>"; };
 		4D700D182EAA46273D1E2CC4 /* SpriteBatch.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SpriteBatch.cpp; sourceTree = "<group>"; };
 		4D81102E7ABD1C282BE42CE3 /* wrap_MouseJoint.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_MouseJoint.cpp; sourceTree = "<group>"; };
 		4DAB28A9235E2CBE75F56848 /* CircleShape.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CircleShape.cpp; sourceTree = "<group>"; };
@@ -585,7 +602,7 @@
 		54E85987318206E93DC8189F /* PolygonShape.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = PolygonShape.cpp; sourceTree = "<group>"; };
 		55A759CE711E157339930E58 /* b2BlockAllocator.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = b2BlockAllocator.cpp; sourceTree = "<group>"; };
 		55AE226405CC1A55462E1D89 /* threads.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = threads.h; sourceTree = "<group>"; };
-		55B425307C0C1C4B3EFC3A5F /* Joystick.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Joystick.cpp; sourceTree = "<group>"; };
+		55B425307C0C1C4B3EFC3A5F /* JoystickModule.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = JoystickModule.cpp; sourceTree = "<group>"; };
 		561D4A4722E36BAA16D17CC8 /* EdgeShape.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EdgeShape.h; sourceTree = "<group>"; };
 		567C0A0C58931DE54733011B /* b2StackAllocator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = b2StackAllocator.h; sourceTree = "<group>"; };
 		56D6030A0B8F7397715062B9 /* Image.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Image.cpp; sourceTree = "<group>"; };
@@ -641,7 +658,6 @@
 		6590063A6E4B3AEF4550443C /* b2GearJoint.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = b2GearJoint.h; sourceTree = "<group>"; };
 		66EC3C0463A703A97445193B /* b2PulleyJoint.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = b2PulleyJoint.cpp; sourceTree = "<group>"; };
 		66F8479E6D2D587A592F2024 /* socket.lua.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = socket.lua.h; sourceTree = "<group>"; };
-		677F545C76EA3B247329358D /* Joystick.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Joystick.cpp; sourceTree = "<group>"; };
 		678E42771C9B415628A3234D /* wrap_ParticleSystem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = wrap_ParticleSystem.h; sourceTree = "<group>"; };
 		68616BD516DB124312B47EB3 /* Image.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Image.h; sourceTree = "<group>"; };
 		691C5C5828550E2F60754EF2 /* wrap_Event.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Event.cpp; sourceTree = "<group>"; };
@@ -722,6 +738,18 @@
 		FA03546B1731F3A700284828 /* simplexnoise1234.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = simplexnoise1234.h; sourceTree = "<group>"; };
 		FA08F5AE16C7525600F007B5 /* Info-Framework.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-Framework.plist"; sourceTree = "<group>"; };
 		FA0CDE3B1710F9A50056E8D7 /* FormatHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FormatHandler.h; sourceTree = "<group>"; };
+		FA34E7CB174B513F00E04D3F /* System.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = System.cpp; sourceTree = "<group>"; };
+		FA34E7CC174B513F00E04D3F /* System.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = System.h; sourceTree = "<group>"; };
+		FA34E7D0174B515D00E04D3F /* System.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = System.cpp; sourceTree = "<group>"; };
+		FA34E7D1174B515D00E04D3F /* System.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = System.h; sourceTree = "<group>"; };
+		FA34E7D4174B55AF00E04D3F /* wrap_System.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_System.cpp; sourceTree = "<group>"; };
+		FA34E7D5174B55AF00E04D3F /* wrap_System.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_System.h; sourceTree = "<group>"; };
+		FA3D9E0B16E68DE600CA6630 /* Cursor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Cursor.cpp; sourceTree = "<group>"; };
+		FA3D9E0C16E68DE600CA6630 /* Cursor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Cursor.h; sourceTree = "<group>"; };
+		FA3D9E0F16E68EAE00CA6630 /* Cursor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Cursor.cpp; sourceTree = "<group>"; };
+		FA3D9E1016E68EAE00CA6630 /* Cursor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Cursor.h; sourceTree = "<group>"; };
+		FA3D9E1316E6D41000CA6630 /* wrap_Cursor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Cursor.cpp; sourceTree = "<group>"; };
+		FA3D9E1416E6D41000CA6630 /* wrap_Cursor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Cursor.h; sourceTree = "<group>"; };
 		FA435EA117B36E9C004C3F22 /* Polyline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Polyline.cpp; sourceTree = "<group>"; };
 		FA435EA217B36E9C004C3F22 /* Polyline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Polyline.h; sourceTree = "<group>"; };
 		FA5454C016F1310000D30303 /* MathModule.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MathModule.cpp; sourceTree = "<group>"; };
@@ -733,7 +761,6 @@
 		FA577A6F16C719F000860150 /* mpg123.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = mpg123.framework; path = /Library/Frameworks/mpg123.framework; sourceTree = "<absolute>"; };
 		FA577A7116C719F400860150 /* Ogg.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Ogg.framework; path = /Library/Frameworks/Ogg.framework; sourceTree = "<absolute>"; };
 		FA577A7316C719F900860150 /* physfs.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = physfs.framework; path = /Library/Frameworks/physfs.framework; sourceTree = "<absolute>"; };
-		FA577A7516C719FF00860150 /* SDL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL.framework; path = /Library/Frameworks/SDL.framework; sourceTree = "<absolute>"; };
 		FA577A7716C71A0800860150 /* Vorbis.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Vorbis.framework; path = /Library/Frameworks/Vorbis.framework; sourceTree = "<absolute>"; };
 		FA577A7916C71A1700860150 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
 		FA577A7C16C71A2600860150 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = /System/Library/Frameworks/OpenGL.framework; sourceTree = "<absolute>"; };
@@ -773,11 +800,18 @@
 		FA7175A9178E8418001FE7FE /* lua.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lua.h; sourceTree = "<group>"; };
 		FA7C937516DCC6C2006F2BEE /* wrap_Math.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Math.cpp; sourceTree = "<group>"; };
 		FA7C937616DCC6C2006F2BEE /* wrap_Math.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Math.h; sourceTree = "<group>"; };
+		FA9B4A0716E1578300074F42 /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL2.framework; path = /Library/Frameworks/SDL2.framework; sourceTree = "<absolute>"; };
 		FA9FC0AE173D6E3E005027FF /* wrap_Window.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Window.cpp; sourceTree = "<group>"; };
 		FA9FC0AF173D6E3E005027FF /* wrap_Window.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Window.h; sourceTree = "<group>"; };
 		FAAC6B00170A373A008A61C5 /* CompressedData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CompressedData.cpp; sourceTree = "<group>"; };
 		FAAC6B01170A373A008A61C5 /* CompressedData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CompressedData.h; sourceTree = "<group>"; };
 		FAAFF04316CB11C700CCDE45 /* OpenAL-Soft.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = "OpenAL-Soft.framework"; path = "/Library/Frameworks/OpenAL-Soft.framework"; sourceTree = "<absolute>"; };
+		FAB0078D1740C12D00A9664D /* Joystick.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Joystick.cpp; sourceTree = "<group>"; };
+		FAB0078E1740C12D00A9664D /* Joystick.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Joystick.h; sourceTree = "<group>"; };
+		FAB007911740C28900A9664D /* Joystick.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Joystick.cpp; sourceTree = "<group>"; };
+		FAB007921740C28900A9664D /* Joystick.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Joystick.h; sourceTree = "<group>"; };
+		FAB007951740C87D00A9664D /* wrap_Joystick.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Joystick.cpp; sourceTree = "<group>"; };
+		FAB007961740C87D00A9664D /* wrap_Joystick.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Joystick.h; sourceTree = "<group>"; };
 		FAC570FC17402D1100D147E4 /* BezierCurve.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BezierCurve.cpp; sourceTree = "<group>"; };
 		FAC570FD17402D1100D147E4 /* BezierCurve.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BezierCurve.h; sourceTree = "<group>"; };
 		FAC570FE17402D1100D147E4 /* wrap_BezierCurve.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_BezierCurve.cpp; sourceTree = "<group>"; };
@@ -822,6 +856,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				FA9B4A0816E1578300074F42 /* SDL2.framework in Frameworks */,
 				FAAFF04416CB11C700CCDE45 /* OpenAL-Soft.framework in Frameworks */,
 				FA577AB016C7507900860150 /* Cocoa.framework in Frameworks */,
 				FA577AC216C7512D00860150 /* FreeType.framework in Frameworks */,
@@ -833,7 +868,6 @@
 				FA577AC816C7513C00860150 /* Ogg.framework in Frameworks */,
 				FA577ACA16C7514100860150 /* OpenGL.framework in Frameworks */,
 				FA577ACB16C7514400860150 /* physfs.framework in Frameworks */,
-				FA577ACC16C7514700860150 /* SDL.framework in Frameworks */,
 				FA577ACD16C7514C00860150 /* Vorbis.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -952,9 +986,13 @@
 		130737AF4BD12D0356A65C87 /* mouse */ = {
 			isa = PBXGroup;
 			children = (
+				FA3D9E0B16E68DE600CA6630 /* Cursor.cpp */,
+				FA3D9E0C16E68DE600CA6630 /* Cursor.h */,
 				31E0110E5797041465FF5F95 /* Mouse.cpp */,
 				63A63DFB339266AC401B545A /* Mouse.h */,
 				1E017A8673D531394B5A3B16 /* sdl */,
+				FA3D9E1316E6D41000CA6630 /* wrap_Cursor.cpp */,
+				FA3D9E1416E6D41000CA6630 /* wrap_Cursor.h */,
 				22EF17981EBD442773FE41B6 /* wrap_Mouse.cpp */,
 				4E972C114A6C25A63B5B6EF2 /* wrap_Mouse.h */,
 			);
@@ -1080,6 +1118,8 @@
 		1E017A8673D531394B5A3B16 /* sdl */ = {
 			isa = PBXGroup;
 			children = (
+				FA3D9E0F16E68EAE00CA6630 /* Cursor.cpp */,
+				FA3D9E1016E68EAE00CA6630 /* Cursor.h */,
 				584E16AE09E12536206C46FE /* Mouse.cpp */,
 				198A44BD71BB61EE517C2A39 /* Mouse.h */,
 			);
@@ -1344,8 +1384,9 @@
 		501126AD67DC2A4B527654EA /* joystick */ = {
 			isa = PBXGroup;
 			children = (
-				677F545C76EA3B247329358D /* Joystick.cpp */,
-				4C606AEC342457600C3F0741 /* Joystick.h */,
+				FAB0078D1740C12D00A9664D /* Joystick.cpp */,
+				FAB0078E1740C12D00A9664D /* Joystick.h */,
+				4C606AEC342457600C3F0741 /* JoystickModule.h */,
 				687216AD6FA406C838284B91 /* sdl */,
 			);
 			path = joystick;
@@ -1381,6 +1422,7 @@
 				130737AF4BD12D0356A65C87 /* mouse */,
 				67F10C1D58A96C1C53563B5C /* physics */,
 				4BAB08EE4A6B4A1A01DA50A4 /* sound */,
+				FA34E7CA174B512200E04D3F /* system */,
 				02E0744773D13AD65E7C49DC /* thread */,
 				6D590DDD41E72A60262E4A4F /* timer */,
 				153D76205F7A4ACD12FB4C0E /* window */,
@@ -1522,10 +1564,14 @@
 		687216AD6FA406C838284B91 /* sdl */ = {
 			isa = PBXGroup;
 			children = (
-				55B425307C0C1C4B3EFC3A5F /* Joystick.cpp */,
-				439E46D768A266780E894800 /* Joystick.h */,
-				139411436818381E493F00F5 /* wrap_Joystick.cpp */,
-				2D7B7DEC4FC87878332E41B3 /* wrap_Joystick.h */,
+				FAB007911740C28900A9664D /* Joystick.cpp */,
+				FAB007921740C28900A9664D /* Joystick.h */,
+				55B425307C0C1C4B3EFC3A5F /* JoystickModule.cpp */,
+				439E46D768A266780E894800 /* JoystickModule.h */,
+				FAB007951740C87D00A9664D /* wrap_Joystick.cpp */,
+				FAB007961740C87D00A9664D /* wrap_Joystick.h */,
+				139411436818381E493F00F5 /* wrap_JoystickModule.cpp */,
+				2D7B7DEC4FC87878332E41B3 /* wrap_JoystickModule.h */,
 			);
 			path = sdl;
 			sourceTree = "<group>";
@@ -1708,9 +1754,31 @@
 			name = Resources;
 			sourceTree = "<group>";
 		};
+		FA34E7CA174B512200E04D3F /* system */ = {
+			isa = PBXGroup;
+			children = (
+				FA34E7CF174B514F00E04D3F /* sdl */,
+				FA34E7CB174B513F00E04D3F /* System.cpp */,
+				FA34E7CC174B513F00E04D3F /* System.h */,
+				FA34E7D4174B55AF00E04D3F /* wrap_System.cpp */,
+				FA34E7D5174B55AF00E04D3F /* wrap_System.h */,
+			);
+			path = system;
+			sourceTree = "<group>";
+		};
+		FA34E7CF174B514F00E04D3F /* sdl */ = {
+			isa = PBXGroup;
+			children = (
+				FA34E7D0174B515D00E04D3F /* System.cpp */,
+				FA34E7D1174B515D00E04D3F /* System.h */,
+			);
+			path = sdl;
+			sourceTree = "<group>";
+		};
 		FA577A6616C7199700860150 /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
+				FA9B4A0716E1578300074F42 /* SDL2.framework */,
 				FA577A7916C71A1700860150 /* Cocoa.framework */,
 				FA577A6716C719D900860150 /* FreeType.framework */,
 				FA577A6B16C719E400860150 /* Game_Music_Emu.framework */,
@@ -1722,7 +1790,6 @@
 				FAAFF04316CB11C700CCDE45 /* OpenAL-Soft.framework */,
 				FA577A7C16C71A2600860150 /* OpenGL.framework */,
 				FA577A7316C719F900860150 /* physfs.framework */,
-				FA577A7516C719FF00860150 /* SDL.framework */,
 				FA577A7716C71A0800860150 /* Vorbis.framework */,
 			);
 			name = Frameworks;
@@ -1850,7 +1917,13 @@
 				FAC86E6A1724555D00EED715 /* DrawGable.h in Headers */,
 				FAC86E6C1724555D00EED715 /* Geometry.h in Headers */,
 				FA03546D1731F3A700284828 /* simplexnoise1234.h in Headers */,
+				FA3D9E0E16E68DE600CA6630 /* Cursor.h in Headers */,
+				FA3D9E1216E68EAE00CA6630 /* Cursor.h in Headers */,
+				FA3D9E1616E6D41100CA6630 /* wrap_Cursor.h in Headers */,
 				FA9FC0B1173D6E3E005027FF /* wrap_Window.h in Headers */,
+				FAB007901740C12D00A9664D /* Joystick.h in Headers */,
+				FAB007941740C28900A9664D /* Joystick.h in Headers */,
+				FAB007981740C87D00A9664D /* wrap_Joystick.h in Headers */,
 				FAC5710117402D1100D147E4 /* BezierCurve.h in Headers */,
 				FAC5710317402D1100D147E4 /* wrap_BezierCurve.h in Headers */,
 				FA5FDC7E1788D548002F0ED2 /* callbacks.h in Headers */,
@@ -1864,6 +1937,9 @@
 				FA5FDC861788D548002F0ED2 /* win32.h in Headers */,
 				FA5FDC8F1788D548002F0ED2 /* lua-enet.h in Headers */,
 				FA7175AA178E8418001FE7FE /* lua.h in Headers */,
+				FA34E7CE174B513F00E04D3F /* System.h in Headers */,
+				FA34E7D3174B515D00E04D3F /* System.h in Headers */,
+				FA1EF7C51799FEB200FF380C /* wrap_System.h in Headers */,
 				FA435EA417B36E9C004C3F22 /* Polyline.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -2054,9 +2130,8 @@
 				FA08F63016C7542600F007B5 /* DevilHandler.cpp in Sources */,
 				FA08F63116C7542600F007B5 /* wrap_Image.cpp in Sources */,
 				FA08F63216C7542600F007B5 /* wrap_ImageData.cpp in Sources */,
-				FA08F63316C7542D00F007B5 /* Joystick.cpp in Sources */,
-				FA08F63416C7542D00F007B5 /* Joystick.cpp in Sources */,
-				FA08F63516C7542D00F007B5 /* wrap_Joystick.cpp in Sources */,
+				FA08F63416C7542D00F007B5 /* JoystickModule.cpp in Sources */,
+				FA08F63516C7542D00F007B5 /* wrap_JoystickModule.cpp in Sources */,
 				FA08F63616C7543400F007B5 /* Keyboard.cpp in Sources */,
 				FA08F63716C7543400F007B5 /* Keyboard.cpp in Sources */,
 				FA08F63816C7543400F007B5 /* wrap_Keyboard.cpp in Sources */,
@@ -2148,7 +2223,13 @@
 				FAC86E691724555D00EED715 /* DrawGable.cpp in Sources */,
 				FAC86E6B1724555D00EED715 /* Geometry.cpp in Sources */,
 				FA03546C1731F3A700284828 /* simplexnoise1234.cpp in Sources */,
+				FA3D9E0D16E68DE600CA6630 /* Cursor.cpp in Sources */,
+				FA3D9E1116E68EAE00CA6630 /* Cursor.cpp in Sources */,
+				FA3D9E1516E6D41100CA6630 /* wrap_Cursor.cpp in Sources */,
 				FA9FC0B0173D6E3E005027FF /* wrap_Window.cpp in Sources */,
+				FAB0078F1740C12D00A9664D /* Joystick.cpp in Sources */,
+				FAB007931740C28900A9664D /* Joystick.cpp in Sources */,
+				FAB007971740C87D00A9664D /* wrap_Joystick.cpp in Sources */,
 				FAC5710017402D1100D147E4 /* BezierCurve.cpp in Sources */,
 				FAC5710217402D1100D147E4 /* wrap_BezierCurve.cpp in Sources */,
 				FA5FDC791788D548002F0ED2 /* enet.cpp in Sources */,
@@ -2161,6 +2242,9 @@
 				FA5FDC8B1788D548002F0ED2 /* protocol.c in Sources */,
 				FA5FDC8D1788D548002F0ED2 /* unix.c in Sources */,
 				FA5FDC8E1788D548002F0ED2 /* win32.c in Sources */,
+				FA34E7CD174B513F00E04D3F /* System.cpp in Sources */,
+				FA34E7D2174B515D00E04D3F /* System.cpp in Sources */,
+				FA1EF7C41799FEAC00FF380C /* wrap_System.cpp in Sources */,
 				FA435EA317B36E9C004C3F22 /* Polyline.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -2190,7 +2274,7 @@
 					"\"$(SRCROOT)/../../src/libraries/enet/libenet/include\"",
 					/Library/Frameworks/FreeType.framework/Headers,
 					/Library/Frameworks/Lua.framework/Headers,
-					/Library/Frameworks/SDL.framework/Headers,
+					/Library/Frameworks/SDL2.framework/Headers,
 				);
 				LD_RUNPATH_SEARCH_PATHS = "@rpath";
 				LIBRARY_SEARCH_PATHS = "";
@@ -2223,7 +2307,7 @@
 					"\"$(SRCROOT)/../../src/libraries/enet/libenet/include\"",
 					/Library/Frameworks/FreeType.framework/Headers,
 					/Library/Frameworks/Lua.framework/Headers,
-					/Library/Frameworks/SDL.framework/Headers,
+					/Library/Frameworks/SDL2.framework/Headers,
 				);
 				LD_RUNPATH_SEARCH_PATHS = "@rpath";
 				LIBRARY_SEARCH_PATHS = "";

+ 14 - 36
platform/macosx/love.xcodeproj/project.pbxproj

@@ -10,14 +10,11 @@
 		8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
 		A911D3C915DFF25D005B7EB8 /* Game_Music_Emu.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = A911D3C715DFF24D005B7EB8 /* Game_Music_Emu.framework */; };
 		A9255DD11043183600BA1496 /* FreeType.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = A93E6E4810420B4A007D418B /* FreeType.framework */; };
-		A9255DD21043183600BA1496 /* SDL.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = A93E6E5210420B57007D418B /* SDL.framework */; };
 		A9255DD31043183600BA1496 /* Lua.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = A93E6E5310420B57007D418B /* Lua.framework */; };
-		A9255DEC1043188D00BA1496 /* SDLMain.m in Sources */ = {isa = PBXBuildFile; fileRef = A9255DEA1043188D00BA1496 /* SDLMain.m */; };
 		A9255E031043195A00BA1496 /* Vorbis.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = A9255E021043195A00BA1496 /* Vorbis.framework */; };
 		A9255F431043240F00BA1496 /* IL.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = A9255F421043240F00BA1496 /* IL.framework */; };
 		A9255F58104324E100BA1496 /* Ogg.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = A9255F51104324D700BA1496 /* Ogg.framework */; };
 		A93E6E4A10420B4A007D418B /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A93E6E4710420B4A007D418B /* OpenGL.framework */; };
-		A93E6E5410420B57007D418B /* SDL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A93E6E5210420B57007D418B /* SDL.framework */; };
 		A93E6E5510420B57007D418B /* Lua.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A93E6E5310420B57007D418B /* Lua.framework */; };
 		A93E6EED10420BA8007D418B /* love.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A93E6A3410420AC0007D418B /* love.cpp */; };
 		A9D307F2106635D3004FEDF8 /* physfs.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = A9D307E9106635C3004FEDF8 /* physfs.framework */; };
@@ -27,7 +24,10 @@
 		A9F169AD109E825000FC83D1 /* libmodplug.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = A9F16926109E7BAD00FC83D1 /* libmodplug.framework */; };
 		FA08F69616C766E000F007B5 /* love.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA08F69116C765A200F007B5 /* love.framework */; };
 		FA08F69716C766E700F007B5 /* love.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = FA08F69116C765A200F007B5 /* love.framework */; };
+		FA9B4A0A16E1579F00074F42 /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA9B4A0916E1579F00074F42 /* SDL2.framework */; };
+		FA9B4A0B16E157B500074F42 /* SDL2.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = FA9B4A0916E1579F00074F42 /* SDL2.framework */; };
 		FAAFF04716CB120000CCDE45 /* OpenAL-Soft.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = FAAFF04616CB120000CCDE45 /* OpenAL-Soft.framework */; };
+		FAC8E8D416F3C468004DADF3 /* osx.mm in Sources */ = {isa = PBXBuildFile; fileRef = FAC8E8D316F3C468004DADF3 /* osx.mm */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -48,6 +48,7 @@
 			dstSubfolderSpec = 10;
 			files = (
 				FA08F69716C766E700F007B5 /* love.framework in Copy Frameworks */,
+				FA9B4A0B16E157B500074F42 /* SDL2.framework in Copy Frameworks */,
 				FAAFF04716CB120000CCDE45 /* OpenAL-Soft.framework in Copy Frameworks */,
 				A9F169AC109E825000FC83D1 /* mpg123.framework in Copy Frameworks */,
 				A9F169AD109E825000FC83D1 /* libmodplug.framework in Copy Frameworks */,
@@ -56,7 +57,6 @@
 				A9255F431043240F00BA1496 /* IL.framework in Copy Frameworks */,
 				A9255E031043195A00BA1496 /* Vorbis.framework in Copy Frameworks */,
 				A9255DD11043183600BA1496 /* FreeType.framework in Copy Frameworks */,
-				A9255DD21043183600BA1496 /* SDL.framework in Copy Frameworks */,
 				A9255DD31043183600BA1496 /* Lua.framework in Copy Frameworks */,
 				A911D3C915DFF25D005B7EB8 /* Game_Music_Emu.framework in Copy Frameworks */,
 			);
@@ -69,18 +69,14 @@
 		1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
 		8D1107320486CEB800E47090 /* love.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = love.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		A911D3C715DFF24D005B7EB8 /* Game_Music_Emu.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Game_Music_Emu.framework; path = /Library/Frameworks/Game_Music_Emu.framework; sourceTree = "<absolute>"; };
-		A9255DEA1043188D00BA1496 /* SDLMain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLMain.m; sourceTree = "<group>"; };
-		A9255DEB1043188D00BA1496 /* SDLMain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDLMain.h; sourceTree = "<group>"; };
 		A9255E021043195A00BA1496 /* Vorbis.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Vorbis.framework; path = /Library/Frameworks/Vorbis.framework; sourceTree = "<absolute>"; };
 		A9255F421043240F00BA1496 /* IL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IL.framework; path = /Library/Frameworks/IL.framework; sourceTree = "<absolute>"; };
 		A9255F51104324D700BA1496 /* Ogg.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Ogg.framework; path = /Library/Frameworks/Ogg.framework; sourceTree = "<absolute>"; };
 		A93E6A3410420AC0007D418B /* love.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = love.cpp; path = ../../src/love.cpp; sourceTree = "<group>"; };
 		A93E6E4710420B4A007D418B /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = /System/Library/Frameworks/OpenGL.framework; sourceTree = "<absolute>"; };
 		A93E6E4810420B4A007D418B /* FreeType.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FreeType.framework; path = /Library/Frameworks/FreeType.framework; sourceTree = "<absolute>"; };
-		A93E6E5210420B57007D418B /* SDL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL.framework; path = /Library/Frameworks/SDL.framework; sourceTree = "<absolute>"; };
 		A93E6E5310420B57007D418B /* Lua.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Lua.framework; path = /Library/Frameworks/Lua.framework; sourceTree = "<absolute>"; };
 		A97E3842132A9EDE00198A2F /* love-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "love-Info.plist"; sourceTree = "<group>"; };
-		A9B1AE451197293000D496EB /* love_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = love_Prefix.pch; sourceTree = "<group>"; };
 		A9D307E9106635C3004FEDF8 /* physfs.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = physfs.framework; path = /Library/Frameworks/physfs.framework; sourceTree = "<absolute>"; };
 		A9DEC1BF1046EFA60049C70C /* Love.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = Love.icns; path = icons/Love.icns; sourceTree = "<group>"; };
 		A9DEC1C01046EFA70049C70C /* LoveDocument.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = LoveDocument.icns; path = icons/LoveDocument.icns; sourceTree = "<group>"; };
@@ -88,7 +84,10 @@
 		A9F169A6109E824900FC83D1 /* mpg123.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = mpg123.framework; path = /Library/Frameworks/mpg123.framework; sourceTree = "<absolute>"; };
 		FA08F69116C765A200F007B5 /* love.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = love.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		FA577A9316C7217800860150 /* love-framework.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = "love-framework.xcodeproj"; sourceTree = "<group>"; };
+		FA9B4A0916E1579F00074F42 /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL2.framework; path = /Library/Frameworks/SDL2.framework; sourceTree = "<absolute>"; };
 		FAAFF04616CB120000CCDE45 /* OpenAL-Soft.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = "OpenAL-Soft.framework"; path = "/Library/Frameworks/OpenAL-Soft.framework"; sourceTree = "<absolute>"; };
+		FAC8E8D316F3C468004DADF3 /* osx.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = osx.mm; sourceTree = "<group>"; };
+		FAC8E8D616F3C46E004DADF3 /* osx.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = osx.h; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -99,8 +98,8 @@
 				FA08F69616C766E000F007B5 /* love.framework in Frameworks */,
 				8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
 				A93E6E4A10420B4A007D418B /* OpenGL.framework in Frameworks */,
-				A93E6E5410420B57007D418B /* SDL.framework in Frameworks */,
 				A93E6E5510420B57007D418B /* Lua.framework in Frameworks */,
+				FA9B4A0A16E1579F00074F42 /* SDL2.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -110,6 +109,7 @@
 		1058C7A0FEA54F0111CA2CBB /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
+				FA9B4A0916E1579F00074F42 /* SDL2.framework */,
 				1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */,
 				A93E6E4810420B4A007D418B /* FreeType.framework */,
 				A911D3C715DFF24D005B7EB8 /* Game_Music_Emu.framework */,
@@ -122,7 +122,6 @@
 				FAAFF04616CB120000CCDE45 /* OpenAL-Soft.framework */,
 				A93E6E4710420B4A007D418B /* OpenGL.framework */,
 				A9D307E9106635C3004FEDF8 /* physfs.framework */,
-				A93E6E5210420B57007D418B /* SDL.framework */,
 				A9255E021043195A00BA1496 /* Vorbis.framework */,
 			);
 			name = Frameworks;
@@ -171,9 +170,8 @@
 			isa = PBXGroup;
 			children = (
 				A93E6A3410420AC0007D418B /* love.cpp */,
-				A9255DEA1043188D00BA1496 /* SDLMain.m */,
-				A9255DEB1043188D00BA1496 /* SDLMain.h */,
-				A9B1AE451197293000D496EB /* love_Prefix.pch */,
+				FAC8E8D616F3C46E004DADF3 /* osx.h */,
+				FAC8E8D316F3C468004DADF3 /* osx.mm */,
 			);
 			name = Source;
 			sourceTree = "<group>";
@@ -261,7 +259,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				A93E6EED10420BA8007D418B /* love.cpp in Sources */,
-				A9255DEC1043188D00BA1496 /* SDLMain.m in Sources */,
+				FAC8E8D416F3C468004DADF3 /* osx.mm in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -279,15 +277,6 @@
 				);
 				GCC_DYNAMIC_NO_PIC = NO;
 				GCC_OPTIMIZATION_LEVEL = 0;
-				GCC_PRECOMPILE_PREFIX_HEADER = YES;
-				GCC_PREFIX_HEADER = love_Prefix.pch;
-				HEADER_SEARCH_PATHS = (
-					"\"$(SRCROOT)/../../src\"",
-					"\"$(SRCROOT)/../../src/libraries\"",
-					"\"$(SRCROOT)/../../src/modules\"",
-					/Library/Frameworks/Lua.framework/Headers,
-					/Library/Frameworks/SDL.framework/Headers,
-				);
 				INSTALL_PATH = /Applications;
 				PRODUCT_NAME = love;
 			};
@@ -303,15 +292,6 @@
 					"\"$(SRCROOT)/build/Release\"",
 					"\"$(SRCROOT)/build/Debug\"",
 				);
-				GCC_PRECOMPILE_PREFIX_HEADER = YES;
-				GCC_PREFIX_HEADER = love_Prefix.pch;
-				HEADER_SEARCH_PATHS = (
-					"\"$(SRCROOT)/../../src\"",
-					"\"$(SRCROOT)/../../src/libraries\"",
-					"\"$(SRCROOT)/../../src/modules\"",
-					/Library/Frameworks/Lua.framework/Headers,
-					/Library/Frameworks/SDL.framework/Headers,
-				);
 				INSTALL_PATH = /Applications;
 				PRODUCT_NAME = love;
 			};
@@ -346,8 +326,7 @@
 					"\"$(SRCROOT)/../../src/libraries\"",
 					"\"$(SRCROOT)/../../src/modules\"",
 					/Library/Frameworks/Lua.framework/Headers,
-					/Library/Frameworks/FreeType.framework/Headers,
-					/Library/Frameworks/SDL.framework/Headers,
+					/Library/Frameworks/SDL2.framework/Headers,
 				);
 				INFOPLIST_FILE = "love-Info.plist";
 				LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks";
@@ -400,8 +379,7 @@
 					"\"$(SRCROOT)/../../src/libraries\"",
 					"\"$(SRCROOT)/../../src/modules\"",
 					/Library/Frameworks/Lua.framework/Headers,
-					/Library/Frameworks/FreeType.framework/Headers,
-					/Library/Frameworks/SDL.framework/Headers,
+					/Library/Frameworks/SDL2.framework/Headers,
 				);
 				INFOPLIST_FILE = "love-Info.plist";
 				LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks";

+ 0 - 7
platform/macosx/love_Prefix.pch

@@ -1,7 +0,0 @@
-//
-// Prefix header for all source files of the 'love' target in the 'love' project
-//
-
-#ifdef __OBJC__
-    #import <Cocoa/Cocoa.h>
-#endif

+ 4 - 2
src/common/Reference.cpp

@@ -26,12 +26,14 @@ namespace love
 const char REFERENCE_TABLE_NAME[] = "love-references";
 
 Reference::Reference()
-	: L(0), idx(LUA_REFNIL)
+	: L(0)
+	, idx(LUA_REFNIL)
 {
 }
 
 Reference::Reference(lua_State *L)
-	: L(0), idx(LUA_REFNIL)
+	: L(L)
+	, idx(LUA_REFNIL)
 {
 	ref(L);
 }

+ 1 - 0
src/common/config.h

@@ -72,6 +72,7 @@
 
 #if defined(LOVE_MACOSX)
 #	define LOVE_LEGENDARY_LIBSTDCXX_HACK
+#	define LOVE_LEGENDARY_APP_ARGV_HACK
 #endif
 
 // Autotools config.h

+ 6 - 0
src/common/runtime.cpp

@@ -571,6 +571,9 @@ StringMap<Type, TYPE_MAX_ENUM>::Entry typeEntries[] =
 	{"ImageData", IMAGE_IMAGE_DATA_ID},
 	{"CompressedData", IMAGE_COMPRESSED_DATA_ID},
 
+	// Joystick
+	{"Joystick", JOYSTICK_JOYSTICK_ID},
+
 	// Math
 	{"RandomGenerator", MATH_RANDOM_GENERATOR_ID},
 	{"BezierCurve", MATH_BEZIER_CURVE_ID},
@@ -582,6 +585,9 @@ StringMap<Type, TYPE_MAX_ENUM>::Entry typeEntries[] =
 	{"SoundData", SOUND_SOUND_DATA_ID},
 	{"Decoder", SOUND_DECODER_ID},
 
+	// Mouse
+	{"Cursor", MOUSE_CURSOR_ID},
+
 	// Physics
 	{"World", PHYSICS_WORLD_ID},
 	{"Contact", PHYSICS_CONTACT_ID},

+ 12 - 0
src/common/types.h

@@ -58,6 +58,9 @@ enum Type
 	IMAGE_IMAGE_DATA_ID,
 	IMAGE_COMPRESSED_DATA_ID,
 
+	// Joystick
+	JOYSTICK_JOYSTICK_ID,
+
 	// Math
 	MATH_RANDOM_GENERATOR_ID,
 	MATH_BEZIER_CURVE_ID,
@@ -69,6 +72,9 @@ enum Type
 	SOUND_SOUND_DATA_ID,
 	SOUND_DECODER_ID,
 
+	// Mouse
+	MOUSE_CURSOR_ID,
+
 	// Physics
 	PHYSICS_WORLD_ID,
 	PHYSICS_CONTACT_ID,
@@ -135,6 +141,9 @@ const bits GRAPHICS_SHADER_T = (bits(1) << GRAPHICS_SHADER_ID) | OBJECT_T;
 const bits IMAGE_IMAGE_DATA_T = (bits(1) << IMAGE_IMAGE_DATA_ID) | DATA_T;
 const bits IMAGE_COMPRESSED_DATA_T = (bits(1) << IMAGE_COMPRESSED_DATA_ID) | DATA_T;
 
+// Joystick.
+const bits JOYSTICK_JOYSTICK_T = (bits(1) << JOYSTICK_JOYSTICK_ID) | OBJECT_T;
+
 // Math.
 const bits MATH_RANDOM_GENERATOR_T = (bits(1) << MATH_RANDOM_GENERATOR_ID) | OBJECT_T;
 const bits MATH_BEZIER_CURVE_T = (bits(1) << MATH_BEZIER_CURVE_ID) | OBJECT_T;
@@ -146,6 +155,9 @@ const bits AUDIO_SOURCE_T = (bits(1) << AUDIO_SOURCE_ID) | OBJECT_T;
 const bits SOUND_SOUND_DATA_T = (bits(1) << SOUND_SOUND_DATA_ID) | DATA_T;
 const bits SOUND_DECODER_T = bits(1) << SOUND_DECODER_ID;
 
+// Mouse.
+const bits MOUSE_CURSOR_T = (bits(1) << MOUSE_CURSOR_ID) | OBJECT_T;
+
 // Physics.
 const bits PHYSICS_WORLD_T = (bits(1) << PHYSICS_WORLD_ID) | OBJECT_T;
 const bits PHYSICS_CONTACT_T = (bits(1) << PHYSICS_CONTACT_ID) | OBJECT_T;

+ 62 - 2
src/love.cpp

@@ -34,6 +34,10 @@ extern "C" {
 #include <windows.h>
 #endif // LOVE_WINDOWS
 
+#ifdef LOVE_MACOSX
+#include "osx.h"
+#endif // LOVE_MACOSX
+
 #ifdef LOVE_LEGENDARY_UTF8_ARGV_HACK
 
 void get_utf8_arguments(int &argc, char **&argv)
@@ -71,6 +75,54 @@ void get_utf8_arguments(int &argc, char **&argv)
 
 #endif // LOVE_LEGENDARY_UTF8_ARGV_HACK
 
+#ifdef LOVE_LEGENDARY_APP_ARGV_HACK
+
+#include <vector>
+
+static void get_app_arguments(int argc, char **argv, int &new_argc, char **&new_argv)
+{
+	std::vector<std::string> temp_argv;
+	for (int i = 0; i < argc; i++)
+	{
+		// Don't copy -psn_xxx argument from argv.
+		if (i == 0 || strncmp(argv[i], "-psn_", 5) != 0)
+			temp_argv.push_back(std::string(argv[i]));
+	}
+
+	// Check for a drop file string.
+	std::string dropfilestr = love::osx::checkDropEvents();
+	if (!dropfilestr.empty())
+	{
+		temp_argv.insert(temp_argv.begin() + 1, dropfilestr);
+	}
+	else
+	{
+		// If it exists, add the love file in love.app/Contents/Resources/ to argv.
+		std::string loveResourcesPath = love::osx::getLoveInResources();
+		if (!loveResourcesPath.empty())
+		{
+			// Run in pseudo-fused mode.
+			std::vector<std::string>::iterator it = temp_argv.begin();
+			it = temp_argv.insert(it + 1, loveResourcesPath);
+			temp_argv.insert(it + 1, std::string("--fused"));
+		}
+	}
+
+	// Copy temp argv vector to new argv array.
+	new_argc = (int) temp_argv.size();
+	new_argv = new char *[new_argc+1];
+
+	for (int i = 0; i < new_argc; i++)
+	{
+		new_argv[i] = new char[temp_argv[i].length() + 1];
+		strcpy(new_argv[i], temp_argv[i].c_str());
+	}
+
+	new_argv[new_argc+1] = NULL;
+}
+
+#endif // LOVE_LEGENDARY_APP_ARGV_HACK
+
 static int love_preload(lua_State *L, lua_CFunction f, const char *name)
 {
 	lua_getglobal(L, "package");
@@ -90,6 +142,14 @@ int main(int argc, char **argv)
 	argv = hack_argv;
 #endif // LOVE_LEGENDARY_UTF8_ARGV_HACK
 
+#ifdef LOVE_LEGENDARY_APP_ARGV_HACK
+	int hack_argc = 0;
+	char **hack_argv = 0;
+	get_app_arguments(argc, argv, hack_argc, hack_argv);
+	argc = hack_argc;
+	argv = hack_argv;
+#endif // LOVE_LEGENDARY_APP_ARGV_HACK
+
 	// Oh, you just want the version? Okay!
 	if (argc > 1 && strcmp(argv[1],"--version") == 0)
 	{
@@ -148,14 +208,14 @@ int main(int argc, char **argv)
 
 	lua_close(L);
 
-#ifdef LOVE_LEGENDARY_UTF8_ARGV_HACK
+#if defined(LOVE_LEGENDARY_UTF8_ARGV_HACK) || defined(LOVE_LEGENDARY_APP_ARGV_HACK)
 	if (hack_argv)
 	{
 		for (int i = 0; i<hack_argc; ++i)
 			delete [] hack_argv[i];
 		delete [] hack_argv;
 	}
-#endif // LOVE_LEGENDARY_UTF8_ARGV_HACK
+#endif // LOVE_LEGENDARY_UTF8_ARGV_HACK || LOVE_LEGENDARY_APP_ARGV_HACK
 	return retval;
 }
 

+ 446 - 239
src/modules/event/sdl/Event.cpp

@@ -22,11 +22,13 @@
 
 #include "keyboard/Keyboard.h"
 #include "mouse/Mouse.h"
-
-#include "libraries/utf8/utf8.h"
+#include "joystick/JoystickModule.h"
+#include "joystick/sdl/Joystick.h"
+#include "graphics/Graphics.h"
+#include "window/Window.h"
+#include "common/Exception.h"
 
 #include <cmath>
-#include <cstddef>
 
 namespace love
 {
@@ -42,7 +44,13 @@ const char *Event::getName() const
 
 Event::Event()
 {
-	SDL_EnableUNICODE(1);
+	if (SDL_InitSubSystem(SDL_INIT_EVENTS) < 0)
+		throw love::Exception("%s", SDL_GetError());
+}
+
+Event::~Event()
+{
+	SDL_QuitSubSystem(SDL_INIT_EVENTS);
 }
 
 void Event::pump()
@@ -50,7 +58,6 @@ void Event::pump()
 	SDL_PumpEvents();
 
 	static SDL_Event e;
-	SDL_EnableUNICODE(1);
 
 	Message *msg;
 
@@ -86,46 +93,65 @@ void Event::clear()
 	love::event::Event::clear();
 }
 
-Message *Event::convert(SDL_Event &e)
+Message *Event::convert(const SDL_Event &e) const
 {
-	Message *msg = 0;
+	Message *msg = NULL;
+
 	love::keyboard::Keyboard::Key key;
 	love::mouse::Mouse::Button button;
-	love::joystick::Joystick::Hat hat;
-	Variant *arg1 = 0, *arg2 = 0, *arg3 = 0;
+	Variant *arg1, *arg2, *arg3;
 	const char *txt;
+	std::map<SDL_Keycode, love::keyboard::Keyboard::Key>::const_iterator keyit;
+
 	switch (e.type)
 	{
 	case SDL_KEYDOWN:
-		if (!keys.find(e.key.keysym.sym, key))
+		keyit = keys.find(e.key.keysym.sym);
+		if (keyit != keys.end())
+			key = keyit->second;
+		else
 			key = love::keyboard::Keyboard::KEY_UNKNOWN;
+
 		if (!love::keyboard::Keyboard::getConstant(key, txt))
 			txt = "unknown";
 		arg1 = new Variant(txt, strlen(txt));
-		txt = convertUnicode(e.key.keysym.unicode);
-		// We don't want to send unprintable unicode text.
-		if (txt && (txt[0] >= ' ' && txt[0] != 127))
-		{
-			arg2 = new Variant(txt, strlen(txt));
-			msg = new Message("keypressed", arg1, arg2);
-			arg2->release();
-		}
-		else
-			msg = new Message("keypressed", arg1);
+		arg2 = new Variant(e.key.repeat == SDL_TRUE);
+		msg = new Message("keypressed", arg1, arg2);
 		arg1->release();
+		arg2->release();
 		break;
 	case SDL_KEYUP:
-		if (!keys.find(e.key.keysym.sym, key))
+		keyit = keys.find(e.key.keysym.sym);
+		if (keyit != keys.end())
+			key = keyit->second;
+		else
 			key = love::keyboard::Keyboard::KEY_UNKNOWN;
+
 		if (!love::keyboard::Keyboard::getConstant(key, txt))
 			txt = "unknown";
 		arg1 = new Variant(txt, strlen(txt));
 		msg = new Message("keyreleased", arg1);
 		arg1->release();
 		break;
+	case SDL_TEXTINPUT:
+		txt = e.text.text;
+		arg1 = new Variant(txt, strlen(txt));
+		msg = new Message("textinput", arg1);
+		arg1->release();
+		break;
+	case SDL_TEXTEDITING:
+		txt = e.edit.text;
+		arg1 = new Variant(txt, strlen(txt));
+		arg2 = new Variant((double) e.edit.start);
+		arg3 = new Variant((double) e.edit.length);
+		msg = new Message("textedit", arg1, arg2, arg3);
+		arg1->release();
+		arg2->release();
+		arg3->release();
+		break;
 	case SDL_MOUSEBUTTONDOWN:
 	case SDL_MOUSEBUTTONUP:
-		if (buttons.find(e.button.button, button) && love::mouse::Mouse::getConstant(button, txt))
+		if (buttons.find(e.button.button, button) && mouse::Mouse::getConstant(button, txt))
 		{
 			arg1 = new Variant((double) e.button.x);
 			arg2 = new Variant((double) e.button.y);
@@ -138,9 +164,77 @@ Message *Event::convert(SDL_Event &e)
 			arg3->release();
 		}
 		break;
+	case SDL_MOUSEWHEEL:
+		if (e.wheel.y != 0)
+		{
+			button = (e.wheel.y > 0) ? mouse::Mouse::BUTTON_WHEELUP : mouse::Mouse::BUTTON_WHEELDOWN;
+			if (!love::mouse::Mouse::getConstant(button, txt))
+				break;
+
+			int mx, my;
+			SDL_GetMouseState(&mx, &my);
+
+			arg1 = new Variant((double) mx);
+			arg2 = new Variant((double) my);
+			arg3 = new Variant(txt, strlen(txt));
+			msg = new Message("mousepressed", arg1, arg2, arg3);
+			arg1->release();
+			arg2->release();
+			arg3->release();
+		}
+		break;
 	case SDL_JOYBUTTONDOWN:
 	case SDL_JOYBUTTONUP:
-		arg1 = new Variant((double)(e.jbutton.which+1));
+	case SDL_JOYAXISMOTION:
+	case SDL_JOYBALLMOTION:
+	case SDL_JOYHATMOTION:
+	case SDL_JOYDEVICEADDED:
+	case SDL_JOYDEVICEREMOVED:
+	case SDL_CONTROLLERBUTTONDOWN:
+	case SDL_CONTROLLERBUTTONUP:
+	case SDL_CONTROLLERAXISMOTION:
+		msg = convertJoystickEvent(e);
+		break;
+	case SDL_WINDOWEVENT:
+		msg = convertWindowEvent(e);
+		break;
+	case SDL_DROPFILE:
+		SDL_free(e.drop.file);
+		break;
+	case SDL_QUIT:
+		msg = new Message("quit");
+		break;
+	default:
+		break;
+	}
+
+	return msg;
+}
+
+Message *Event::convertJoystickEvent(const SDL_Event &e) const
+{
+	joystick::JoystickModule *joymodule = (joystick::JoystickModule *) Module::findInstance("love.joystick.");
+	if (!joymodule)
+		return 0;
+
+	Message *msg = 0;
+	Proxy proxy;
+	love::joystick::Joystick::Hat hat;
+	love::joystick::Joystick::GamepadButton padbutton;
+	love::joystick::Joystick::GamepadAxis padaxis;
+	Variant *arg1, *arg2, *arg3;
+	const char *txt;
+
+	switch (e.type)
+	{
+	case SDL_JOYBUTTONDOWN:
+	case SDL_JOYBUTTONUP:
+		proxy.flags = JOYSTICK_JOYSTICK_T;
+		proxy.data = joymodule->getJoystickFromID(e.jbutton.which);
+		if (!proxy.data)
+			break;
+
+		arg1 = new Variant(JOYSTICK_JOYSTICK_ID, (void *) &proxy);
 		arg2 = new Variant((double)(e.jbutton.button+1));
 		msg = new Message((e.type == SDL_JOYBUTTONDOWN) ?
 						  "joystickpressed" : "joystickreleased",
@@ -150,7 +244,12 @@ Message *Event::convert(SDL_Event &e)
 		break;
 	case SDL_JOYAXISMOTION:
 		{
-			arg1 = new Variant((double)(e.jaxis.which+1));
+			proxy.flags = JOYSTICK_JOYSTICK_T;
+			proxy.data = joymodule->getJoystickFromID(e.jaxis.which);
+			if (!proxy.data)
+				break;
+
+			arg1 = new Variant(JOYSTICK_JOYSTICK_ID, (void *) &proxy);
 			arg2 = new Variant((double)(e.jaxis.axis+1));
 			float value = e.jaxis.value / 32768.0f;
 			if (fabsf(value) < 0.001f) value = 0.0f;
@@ -164,260 +263,368 @@ Message *Event::convert(SDL_Event &e)
 		}
 		break;
 	case SDL_JOYHATMOTION:
-		if (hats.find(e.jhat.value, hat) && love::joystick::Joystick::getConstant(hat, txt))
+		if (!joystick::sdl::Joystick::getConstant(e.jhat.value, hat) || !joystick::Joystick::getConstant(hat, txt))
+			break;
+
+		proxy.flags = JOYSTICK_JOYSTICK_T;
+		proxy.data = joymodule->getJoystickFromID(e.jhat.which);
+		if (!proxy.data)
+			break;
+
+		arg1 = new Variant(JOYSTICK_JOYSTICK_ID, (void *) &proxy);
+		arg2 = new Variant((double)(e.jhat.hat+1));
+		arg3 = new Variant(txt, strlen(txt));
+		msg = new Message("joystickhat", arg1, arg2, arg3);
+		arg1->release();
+		arg2->release();
+		arg3->release();
+		break;
+	case SDL_CONTROLLERBUTTONDOWN:
+	case SDL_CONTROLLERBUTTONUP:
+		if (!joystick::sdl::Joystick::getConstant((SDL_GameControllerButton) e.cbutton.button, padbutton))
+			break;
+
+		if (!joystick::Joystick::getConstant(padbutton, txt))
+			break;
+
+		proxy.flags = JOYSTICK_JOYSTICK_T;
+		proxy.data = joymodule->getJoystickFromID(e.cbutton.which);
+		if (!proxy.data)
+			break;
+
+		arg1 = new Variant(JOYSTICK_JOYSTICK_ID, (void *) &proxy);
+		arg2 = new Variant(txt, strlen(txt));
+		msg = new Message(e.type == SDL_CONTROLLERBUTTONDOWN ?
+						  "gamepadpressed" : "gamepadreleased", arg1, arg2);
+		arg1->release();
+		arg2->release();
+		break;
+	case SDL_CONTROLLERAXISMOTION:
+		if (joystick::sdl::Joystick::getConstant((SDL_GameControllerAxis) e.caxis.axis, padaxis))
 		{
-			arg1 = new Variant((double)(e.jhat.which+1));
-			arg2 = new Variant((double)(e.jhat.hat+1));
-			arg3 = new Variant(txt, strlen(txt));
-			msg = new Message("joystickhat", arg1, arg2, arg3);
+			if (!joystick::Joystick::getConstant(padaxis, txt))
+				break;
+
+			proxy.flags = JOYSTICK_JOYSTICK_T;
+			proxy.data = joymodule->getJoystickFromID(e.caxis.which);
+			if (!proxy.data)
+				break;
+
+			arg1 = new Variant(JOYSTICK_JOYSTICK_ID, (void *) &proxy);
+
+			arg2 = new Variant(txt, strlen(txt));
+			float value = e.jaxis.value / 32768.0f;
+			if (fabsf(value) < 0.001f) value = 0.0f;
+			if (value < -0.99f) value = -1.0f;
+			if (value > 0.99f) value = 1.0f;
+			arg3 = new Variant((double) value);
+			msg = new Message("gamepadaxis", arg1, arg2, arg3);
 			arg1->release();
 			arg2->release();
 			arg3->release();
 		}
 		break;
-	case SDL_ACTIVEEVENT:
+	case SDL_JOYDEVICEADDED:
+		// jdevice.which is the joystick device index.
+		proxy.data = joymodule->addJoystick(e.jdevice.which);
+		proxy.flags = JOYSTICK_JOYSTICK_T;
+		if (proxy.data)
 		{
-			// SDL can send multiple ACTIVEEVENTS in a single SDL_event... we
-			// have to work around that by re-sending the ones we miss.
-			SDL_Event e2 = e;
-
-			arg1 = new Variant(e.active.gain != 0);
-			if (e.active.state & SDL_APPINPUTFOCUS)
-			{
-				msg = new Message("focus", arg1);
-				e2.active.state &= ~SDL_APPINPUTFOCUS;
-			}
-			else if (e.active.state & SDL_APPMOUSEFOCUS)
-			{
-				msg = new Message("mousefocus", arg1);
-				e2.active.state &= ~SDL_APPMOUSEFOCUS;
-			}
-			else if (e.active.state & SDL_APPACTIVE)
-			{
-				msg = new Message("visible", arg1);
-				e2.active.state &= ~SDL_APPACTIVE;
-			}
+			arg1 = new Variant(JOYSTICK_JOYSTICK_ID, (void *) &proxy);
+			msg = new Message("joystickadded", arg1);
 			arg1->release();
-
-			// Put any missing active events back in SDL's queue.
-			if (e2.active.state != 0)
-				SDL_PushEvent(&e2);
 		}
 		break;
-	case SDL_QUIT:
-		msg = new Message("quit");
+	case SDL_JOYDEVICEREMOVED:
+		// jdevice.which is the joystick instance ID now.
+		proxy.data = joymodule->getJoystickFromID(e.jdevice.which);
+		proxy.flags = JOYSTICK_JOYSTICK_T;
+		if (proxy.data)
+		{
+			joymodule->removeJoystick((joystick::Joystick *) proxy.data);
+			arg1 = new Variant(JOYSTICK_JOYSTICK_ID, (void *) &proxy);
+			msg = new Message("joystickremoved", arg1);
+			arg1->release();
+		}
 		break;
-	case SDL_VIDEORESIZE:
-		arg1 = new Variant((double) e.resize.w);
-		arg2 = new Variant((double) e.resize.h);
-		msg = new Message("resize", arg1, arg2);
-		arg1->release();
-		arg2->release();
+	default:
 		break;
 	}
 
 	return msg;
 }
 
-const char *Event::convertUnicode(Uint16 codepoint) const
+Message *Event::convertWindowEvent(const SDL_Event &e) const
 {
-	// A single unicode character can use up to 4 bytes.
-	static char str[5] = {'\0'};
-	ptrdiff_t length = 0;
+	Message *msg = 0;
+	Variant *arg1, *arg2;
+	window::Window *win = 0;
 
-	if (codepoint == 0)
+	if (e.type != SDL_WINDOWEVENT)
 		return 0;
 
-	char *end = utf8::unchecked::append(codepoint, str);
-	length = end - str;
-
-	if (length <= 0 || !utf8::is_valid(str, str + length))
-		return 0;
+	switch (e.window.event)
+	{
+	case SDL_WINDOWEVENT_FOCUS_GAINED:
+	case SDL_WINDOWEVENT_FOCUS_LOST:
+		// Users won't expect the screensaver to activate if a game is in
+		// focus. Also, joystick input may not delay the screensaver timer.
+		if (e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED)
+			SDL_DisableScreenSaver();
+		else
+			SDL_EnableScreenSaver();
+		arg1 = new Variant(e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED);
+		msg = new Message("focus", arg1);
+		arg1->release();
+		break;
+	case SDL_WINDOWEVENT_ENTER:
+	case SDL_WINDOWEVENT_LEAVE:
+		arg1 = new Variant(e.window.event == SDL_WINDOWEVENT_ENTER);
+		msg = new Message("mousefocus", arg1);
+		arg1->release();
+		break;
+	case SDL_WINDOWEVENT_SHOWN:
+	case SDL_WINDOWEVENT_HIDDEN:
+		arg1 = new Variant(e.window.event == SDL_WINDOWEVENT_SHOWN);
+		msg = new Message("visible", arg1);
+		arg1->release();
+		break;
+	case SDL_WINDOWEVENT_RESIZED:
+		win = (window::Window *) Module::findInstance("love.window.");
+		if (win)
+		{
+			win->onWindowResize(e.window.data1, e.window.data2);
 
-	// Zero out the rest of the string.
-	for (ptrdiff_t i = length; i < 5; i++)
-		str[i] = '\0';
+			graphics::Graphics *gfx = (graphics::Graphics *) Module::findInstance("love.graphics.");
+			if (gfx)
+				gfx->setViewportSize(e.window.data1, e.window.data2);
+		}
+		arg1 = new Variant((double) e.window.data1);
+		arg2 = new Variant((double) e.window.data2);
+		msg = new Message("resize", arg1, arg2);
+		arg1->release();
+		arg2->release();
+		break;
+	}
 
-	return str;
+	return msg;
 }
 
-EnumMap<love::keyboard::Keyboard::Key, SDLKey, love::keyboard::Keyboard::KEY_MAX_ENUM>::Entry Event::keyEntries[] =
+std::map<SDL_Keycode, love::keyboard::Keyboard::Key> Event::createKeyMap()
 {
-	{ love::keyboard::Keyboard::KEY_BACKSPACE, SDLK_BACKSPACE},
-	{ love::keyboard::Keyboard::KEY_TAB, SDLK_TAB},
-	{ love::keyboard::Keyboard::KEY_CLEAR, SDLK_CLEAR},
-	{ love::keyboard::Keyboard::KEY_RETURN, SDLK_RETURN},
-	{ love::keyboard::Keyboard::KEY_PAUSE, SDLK_PAUSE},
-	{ love::keyboard::Keyboard::KEY_ESCAPE, SDLK_ESCAPE },
-	{ love::keyboard::Keyboard::KEY_SPACE, SDLK_SPACE },
-	{ love::keyboard::Keyboard::KEY_EXCLAIM, SDLK_EXCLAIM },
-	{ love::keyboard::Keyboard::KEY_QUOTEDBL, SDLK_QUOTEDBL },
-	{ love::keyboard::Keyboard::KEY_HASH, SDLK_HASH },
-	{ love::keyboard::Keyboard::KEY_DOLLAR, SDLK_DOLLAR },
-	{ love::keyboard::Keyboard::KEY_AMPERSAND, SDLK_AMPERSAND },
-	{ love::keyboard::Keyboard::KEY_QUOTE, SDLK_QUOTE },
-	{ love::keyboard::Keyboard::KEY_LEFTPAREN, SDLK_LEFTPAREN },
-	{ love::keyboard::Keyboard::KEY_RIGHTPAREN, SDLK_RIGHTPAREN },
-	{ love::keyboard::Keyboard::KEY_ASTERISK, SDLK_ASTERISK },
-	{ love::keyboard::Keyboard::KEY_PLUS, SDLK_PLUS },
-	{ love::keyboard::Keyboard::KEY_COMMA, SDLK_COMMA },
-	{ love::keyboard::Keyboard::KEY_MINUS, SDLK_MINUS },
-	{ love::keyboard::Keyboard::KEY_PERIOD, SDLK_PERIOD },
-	{ love::keyboard::Keyboard::KEY_SLASH, SDLK_SLASH },
-	{ love::keyboard::Keyboard::KEY_0, SDLK_0 },
-	{ love::keyboard::Keyboard::KEY_1, SDLK_1 },
-	{ love::keyboard::Keyboard::KEY_2, SDLK_2 },
-	{ love::keyboard::Keyboard::KEY_3, SDLK_3 },
-	{ love::keyboard::Keyboard::KEY_4, SDLK_4 },
-	{ love::keyboard::Keyboard::KEY_5, SDLK_5 },
-	{ love::keyboard::Keyboard::KEY_6, SDLK_6 },
-	{ love::keyboard::Keyboard::KEY_7, SDLK_7 },
-	{ love::keyboard::Keyboard::KEY_8, SDLK_8 },
-	{ love::keyboard::Keyboard::KEY_9, SDLK_9 },
-	{ love::keyboard::Keyboard::KEY_COLON, SDLK_COLON },
-	{ love::keyboard::Keyboard::KEY_SEMICOLON, SDLK_SEMICOLON },
-	{ love::keyboard::Keyboard::KEY_LESS, SDLK_LESS },
-	{ love::keyboard::Keyboard::KEY_EQUALS, SDLK_EQUALS },
-	{ love::keyboard::Keyboard::KEY_GREATER, SDLK_GREATER },
-	{ love::keyboard::Keyboard::KEY_QUESTION, SDLK_QUESTION },
-	{ love::keyboard::Keyboard::KEY_AT, SDLK_AT },
-
-	{ love::keyboard::Keyboard::KEY_LEFTBRACKET, SDLK_LEFTBRACKET },
-	{ love::keyboard::Keyboard::KEY_BACKSLASH, SDLK_BACKSLASH },
-	{ love::keyboard::Keyboard::KEY_RIGHTBRACKET, SDLK_RIGHTBRACKET },
-	{ love::keyboard::Keyboard::KEY_CARET, SDLK_CARET },
-	{ love::keyboard::Keyboard::KEY_UNDERSCORE, SDLK_UNDERSCORE },
-	{ love::keyboard::Keyboard::KEY_BACKQUOTE, SDLK_BACKQUOTE },
-	{ love::keyboard::Keyboard::KEY_A, SDLK_a },
-	{ love::keyboard::Keyboard::KEY_B, SDLK_b },
-	{ love::keyboard::Keyboard::KEY_C, SDLK_c },
-	{ love::keyboard::Keyboard::KEY_D, SDLK_d },
-	{ love::keyboard::Keyboard::KEY_E, SDLK_e },
-	{ love::keyboard::Keyboard::KEY_F, SDLK_f },
-	{ love::keyboard::Keyboard::KEY_G, SDLK_g },
-	{ love::keyboard::Keyboard::KEY_H, SDLK_h },
-	{ love::keyboard::Keyboard::KEY_I, SDLK_i },
-	{ love::keyboard::Keyboard::KEY_J, SDLK_j },
-	{ love::keyboard::Keyboard::KEY_K, SDLK_k },
-	{ love::keyboard::Keyboard::KEY_L, SDLK_l },
-	{ love::keyboard::Keyboard::KEY_M, SDLK_m },
-	{ love::keyboard::Keyboard::KEY_N, SDLK_n },
-	{ love::keyboard::Keyboard::KEY_O, SDLK_o },
-	{ love::keyboard::Keyboard::KEY_P, SDLK_p },
-	{ love::keyboard::Keyboard::KEY_Q, SDLK_q },
-	{ love::keyboard::Keyboard::KEY_R, SDLK_r },
-	{ love::keyboard::Keyboard::KEY_S, SDLK_s },
-	{ love::keyboard::Keyboard::KEY_T, SDLK_t },
-	{ love::keyboard::Keyboard::KEY_U, SDLK_u },
-	{ love::keyboard::Keyboard::KEY_V, SDLK_v },
-	{ love::keyboard::Keyboard::KEY_W, SDLK_w },
-	{ love::keyboard::Keyboard::KEY_X, SDLK_x },
-	{ love::keyboard::Keyboard::KEY_Y, SDLK_y },
-	{ love::keyboard::Keyboard::KEY_Z, SDLK_z },
-	{ love::keyboard::Keyboard::KEY_DELETE, SDLK_DELETE },
-
-	{ love::keyboard::Keyboard::KEY_KP0, SDLK_KP0 },
-	{ love::keyboard::Keyboard::KEY_KP1, SDLK_KP1 },
-	{ love::keyboard::Keyboard::KEY_KP2, SDLK_KP2 },
-	{ love::keyboard::Keyboard::KEY_KP3, SDLK_KP3 },
-	{ love::keyboard::Keyboard::KEY_KP4, SDLK_KP4 },
-	{ love::keyboard::Keyboard::KEY_KP5, SDLK_KP5 },
-	{ love::keyboard::Keyboard::KEY_KP6, SDLK_KP6 },
-	{ love::keyboard::Keyboard::KEY_KP7, SDLK_KP7 },
-	{ love::keyboard::Keyboard::KEY_KP8, SDLK_KP8 },
-	{ love::keyboard::Keyboard::KEY_KP9, SDLK_KP9 },
-	{ love::keyboard::Keyboard::KEY_KP_PERIOD, SDLK_KP_PERIOD },
-	{ love::keyboard::Keyboard::KEY_KP_DIVIDE, SDLK_KP_DIVIDE },
-	{ love::keyboard::Keyboard::KEY_KP_MULTIPLY, SDLK_KP_MULTIPLY},
-	{ love::keyboard::Keyboard::KEY_KP_MINUS, SDLK_KP_MINUS },
-	{ love::keyboard::Keyboard::KEY_KP_PLUS, SDLK_KP_PLUS },
-	{ love::keyboard::Keyboard::KEY_KP_ENTER, SDLK_KP_ENTER },
-	{ love::keyboard::Keyboard::KEY_KP_EQUALS, SDLK_KP_EQUALS },
-
-	{ love::keyboard::Keyboard::KEY_UP, SDLK_UP },
-	{ love::keyboard::Keyboard::KEY_DOWN, SDLK_DOWN },
-	{ love::keyboard::Keyboard::KEY_RIGHT, SDLK_RIGHT },
-	{ love::keyboard::Keyboard::KEY_LEFT, SDLK_LEFT },
-	{ love::keyboard::Keyboard::KEY_INSERT, SDLK_INSERT },
-	{ love::keyboard::Keyboard::KEY_HOME, SDLK_HOME },
-	{ love::keyboard::Keyboard::KEY_END, SDLK_END },
-	{ love::keyboard::Keyboard::KEY_PAGEUP, SDLK_PAGEUP },
-	{ love::keyboard::Keyboard::KEY_PAGEDOWN, SDLK_PAGEDOWN },
-
-	{ love::keyboard::Keyboard::KEY_F1, SDLK_F1 },
-	{ love::keyboard::Keyboard::KEY_F2, SDLK_F2 },
-	{ love::keyboard::Keyboard::KEY_F3, SDLK_F3 },
-	{ love::keyboard::Keyboard::KEY_F4, SDLK_F4 },
-	{ love::keyboard::Keyboard::KEY_F5, SDLK_F5 },
-	{ love::keyboard::Keyboard::KEY_F6, SDLK_F6 },
-	{ love::keyboard::Keyboard::KEY_F7, SDLK_F7 },
-	{ love::keyboard::Keyboard::KEY_F8, SDLK_F8 },
-	{ love::keyboard::Keyboard::KEY_F9, SDLK_F9 },
-	{ love::keyboard::Keyboard::KEY_F10, SDLK_F10 },
-	{ love::keyboard::Keyboard::KEY_F11, SDLK_F11 },
-	{ love::keyboard::Keyboard::KEY_F12, SDLK_F12 },
-	{ love::keyboard::Keyboard::KEY_F13, SDLK_F13 },
-	{ love::keyboard::Keyboard::KEY_F14, SDLK_F14 },
-	{ love::keyboard::Keyboard::KEY_F15, SDLK_F15 },
-
-	{ love::keyboard::Keyboard::KEY_NUMLOCK, SDLK_NUMLOCK },
-	{ love::keyboard::Keyboard::KEY_CAPSLOCK, SDLK_CAPSLOCK },
-	{ love::keyboard::Keyboard::KEY_SCROLLOCK, SDLK_SCROLLOCK },
-	{ love::keyboard::Keyboard::KEY_RSHIFT, SDLK_RSHIFT },
-	{ love::keyboard::Keyboard::KEY_LSHIFT, SDLK_LSHIFT },
-	{ love::keyboard::Keyboard::KEY_RCTRL, SDLK_RCTRL },
-	{ love::keyboard::Keyboard::KEY_LCTRL, SDLK_LCTRL },
-	{ love::keyboard::Keyboard::KEY_RALT, SDLK_RALT },
-	{ love::keyboard::Keyboard::KEY_LALT, SDLK_LALT },
-	{ love::keyboard::Keyboard::KEY_RMETA, SDLK_RMETA },
-	{ love::keyboard::Keyboard::KEY_LMETA, SDLK_LMETA },
-	{ love::keyboard::Keyboard::KEY_LSUPER, SDLK_LSUPER },
-	{ love::keyboard::Keyboard::KEY_RSUPER, SDLK_RSUPER },
-	{ love::keyboard::Keyboard::KEY_MODE, SDLK_MODE },
-	{ love::keyboard::Keyboard::KEY_COMPOSE, SDLK_COMPOSE },
-
-	{ love::keyboard::Keyboard::KEY_HELP, SDLK_HELP },
-	{ love::keyboard::Keyboard::KEY_PRINT, SDLK_PRINT },
-	{ love::keyboard::Keyboard::KEY_SYSREQ, SDLK_SYSREQ },
-	{ love::keyboard::Keyboard::KEY_BREAK, SDLK_BREAK },
-	{ love::keyboard::Keyboard::KEY_MENU, SDLK_MENU },
-	{ love::keyboard::Keyboard::KEY_POWER, SDLK_POWER },
-	{ love::keyboard::Keyboard::KEY_EURO, SDLK_EURO },
-	{ love::keyboard::Keyboard::KEY_UNDO, SDLK_UNDO },
-
-	{ love::keyboard::Keyboard::KEY_UNKNOWN, SDLK_UNKNOWN },
-};
+	using love::keyboard::Keyboard;
+
+	std::map<SDL_Keycode, Keyboard::Key> k;
+
+	k[SDLK_UNKNOWN] = Keyboard::KEY_UNKNOWN;
+
+	k[SDLK_RETURN] = Keyboard::KEY_RETURN;
+	k[SDLK_ESCAPE] = Keyboard::KEY_ESCAPE;
+	k[SDLK_BACKSPACE] = Keyboard::KEY_BACKSPACE;
+	k[SDLK_TAB] = Keyboard::KEY_TAB;
+	k[SDLK_SPACE] = Keyboard::KEY_SPACE;
+	k[SDLK_EXCLAIM] = Keyboard::KEY_EXCLAIM;
+	k[SDLK_QUOTEDBL] = Keyboard::KEY_QUOTEDBL;
+	k[SDLK_HASH] = Keyboard::KEY_HASH;
+	k[SDLK_DOLLAR] = Keyboard::KEY_DOLLAR;
+	k[SDLK_AMPERSAND] = Keyboard::KEY_AMPERSAND;
+	k[SDLK_QUOTE] = Keyboard::KEY_QUOTE;
+	k[SDLK_LEFTPAREN] = Keyboard::KEY_LEFTPAREN;
+	k[SDLK_RIGHTPAREN] = Keyboard::KEY_RIGHTPAREN;
+	k[SDLK_ASTERISK] = Keyboard::KEY_ASTERISK;
+	k[SDLK_PLUS] = Keyboard::KEY_PLUS;
+	k[SDLK_COMMA] = Keyboard::KEY_COMMA;
+	k[SDLK_MINUS] = Keyboard::KEY_MINUS;
+	k[SDLK_PERIOD] = Keyboard::KEY_PERIOD;
+	k[SDLK_SLASH] = Keyboard::KEY_SLASH;
+	k[SDLK_0] = Keyboard::KEY_0;
+	k[SDLK_1] = Keyboard::KEY_1;
+	k[SDLK_2] = Keyboard::KEY_2;
+	k[SDLK_3] = Keyboard::KEY_3;
+	k[SDLK_4] = Keyboard::KEY_4;
+	k[SDLK_5] = Keyboard::KEY_5;
+	k[SDLK_6] = Keyboard::KEY_6;
+	k[SDLK_7] = Keyboard::KEY_7;
+	k[SDLK_8] = Keyboard::KEY_8;
+	k[SDLK_9] = Keyboard::KEY_9;
+	k[SDLK_COLON] = Keyboard::KEY_COLON;
+	k[SDLK_SEMICOLON] = Keyboard::KEY_SEMICOLON;
+	k[SDLK_LESS] = Keyboard::KEY_LESS;
+	k[SDLK_EQUALS] = Keyboard::KEY_EQUALS;
+	k[SDLK_GREATER] = Keyboard::KEY_GREATER;
+	k[SDLK_QUESTION] = Keyboard::KEY_QUESTION;
+	k[SDLK_AT] = Keyboard::KEY_AT;
+
+	k[SDLK_LEFTBRACKET] = Keyboard::KEY_LEFTBRACKET;
+	k[SDLK_BACKSLASH] = Keyboard::KEY_BACKSLASH;
+	k[SDLK_RIGHTBRACKET] = Keyboard::KEY_RIGHTBRACKET;
+	k[SDLK_CARET] = Keyboard::KEY_CARET;
+	k[SDLK_UNDERSCORE] = Keyboard::KEY_UNDERSCORE;
+	k[SDLK_BACKQUOTE] = Keyboard::KEY_BACKQUOTE;
+	k[SDLK_a] = Keyboard::KEY_A;
+	k[SDLK_b] = Keyboard::KEY_B;
+	k[SDLK_c] = Keyboard::KEY_C;
+	k[SDLK_d] = Keyboard::KEY_D;
+	k[SDLK_e] = Keyboard::KEY_E;
+	k[SDLK_f] = Keyboard::KEY_F;
+	k[SDLK_g] = Keyboard::KEY_G;
+	k[SDLK_h] = Keyboard::KEY_H;
+	k[SDLK_i] = Keyboard::KEY_I;
+	k[SDLK_j] = Keyboard::KEY_J;
+	k[SDLK_k] = Keyboard::KEY_K;
+	k[SDLK_l] = Keyboard::KEY_L;
+	k[SDLK_m] = Keyboard::KEY_M;
+	k[SDLK_n] = Keyboard::KEY_N;
+	k[SDLK_o] = Keyboard::KEY_O;
+	k[SDLK_p] = Keyboard::KEY_P;
+	k[SDLK_q] = Keyboard::KEY_Q;
+	k[SDLK_r] = Keyboard::KEY_R;
+	k[SDLK_s] = Keyboard::KEY_S;
+	k[SDLK_t] = Keyboard::KEY_T;
+	k[SDLK_u] = Keyboard::KEY_U;
+	k[SDLK_v] = Keyboard::KEY_V;
+	k[SDLK_w] = Keyboard::KEY_W;
+	k[SDLK_x] = Keyboard::KEY_X;
+	k[SDLK_y] = Keyboard::KEY_Y;
+	k[SDLK_z] = Keyboard::KEY_Z;
+
+	k[SDLK_CAPSLOCK] = Keyboard::KEY_CAPSLOCK;
+
+	k[SDLK_F1] = Keyboard::KEY_F1;
+	k[SDLK_F2] = Keyboard::KEY_F2;
+	k[SDLK_F3] = Keyboard::KEY_F3;
+	k[SDLK_F4] = Keyboard::KEY_F4;
+	k[SDLK_F5] = Keyboard::KEY_F5;
+	k[SDLK_F6] = Keyboard::KEY_F6;
+	k[SDLK_F7] = Keyboard::KEY_F7;
+	k[SDLK_F8] = Keyboard::KEY_F8;
+	k[SDLK_F9] = Keyboard::KEY_F9;
+	k[SDLK_F10] = Keyboard::KEY_F10;
+	k[SDLK_F11] = Keyboard::KEY_F11;
+	k[SDLK_F12] = Keyboard::KEY_F12;
+
+	k[SDLK_PRINTSCREEN] = Keyboard::KEY_PRINTSCREEN;
+	k[SDLK_SCROLLLOCK] = Keyboard::KEY_SCROLLLOCK;
+	k[SDLK_PAUSE] = Keyboard::KEY_PAUSE;
+	k[SDLK_INSERT] = Keyboard::KEY_INSERT;
+	k[SDLK_HOME] = Keyboard::KEY_HOME;
+	k[SDLK_PAGEUP] = Keyboard::KEY_PAGEUP;
+	k[SDLK_DELETE] = Keyboard::KEY_DELETE;
+	k[SDLK_END] = Keyboard::KEY_END;
+	k[SDLK_PAGEDOWN] = Keyboard::KEY_PAGEDOWN;
+	k[SDLK_RIGHT] = Keyboard::KEY_RIGHT;
+	k[SDLK_LEFT] = Keyboard::KEY_LEFT;
+	k[SDLK_DOWN] = Keyboard::KEY_DOWN;
+	k[SDLK_UP] = Keyboard::KEY_UP;
+
+	k[SDLK_NUMLOCKCLEAR] = Keyboard::KEY_NUMLOCKCLEAR;
+	k[SDLK_KP_DIVIDE] = Keyboard::KEY_KP_DIVIDE;
+	k[SDLK_KP_MULTIPLY] = Keyboard::KEY_KP_MULTIPLY;
+	k[SDLK_KP_MINUS] = Keyboard::KEY_KP_MINUS;
+	k[SDLK_KP_PLUS] = Keyboard::KEY_KP_PLUS;
+	k[SDLK_KP_ENTER] = Keyboard::KEY_KP_ENTER;
+	k[SDLK_KP_0] = Keyboard::KEY_KP_0;
+	k[SDLK_KP_1] = Keyboard::KEY_KP_1;
+	k[SDLK_KP_2] = Keyboard::KEY_KP_2;
+	k[SDLK_KP_3] = Keyboard::KEY_KP_3;
+	k[SDLK_KP_4] = Keyboard::KEY_KP_4;
+	k[SDLK_KP_5] = Keyboard::KEY_KP_5;
+	k[SDLK_KP_6] = Keyboard::KEY_KP_6;
+	k[SDLK_KP_7] = Keyboard::KEY_KP_7;
+	k[SDLK_KP_8] = Keyboard::KEY_KP_8;
+	k[SDLK_KP_9] = Keyboard::KEY_KP_9;
+	k[SDLK_KP_PERIOD] = Keyboard::KEY_KP_PERIOD;
+	k[SDLK_KP_COMMA] = Keyboard::KEY_KP_COMMA;
+	k[SDLK_KP_EQUALS] = Keyboard::KEY_KP_EQUALS;
+
+	k[SDLK_APPLICATION] = Keyboard::KEY_APPLICATION;
+	k[SDLK_POWER] = Keyboard::KEY_POWER;
+	k[SDLK_F13] = Keyboard::KEY_F13;
+	k[SDLK_F14] = Keyboard::KEY_F14;
+	k[SDLK_F15] = Keyboard::KEY_F15;
+	k[SDLK_F16] = Keyboard::KEY_F16;
+	k[SDLK_F17] = Keyboard::KEY_F17;
+	k[SDLK_F18] = Keyboard::KEY_F18;
+	k[SDLK_F19] = Keyboard::KEY_F19;
+	k[SDLK_F20] = Keyboard::KEY_F20;
+	k[SDLK_F21] = Keyboard::KEY_F21;
+	k[SDLK_F22] = Keyboard::KEY_F22;
+	k[SDLK_F23] = Keyboard::KEY_F23;
+	k[SDLK_F24] = Keyboard::KEY_F24;
+	k[SDLK_EXECUTE] = Keyboard::KEY_EXECUTE;
+	k[SDLK_HELP] = Keyboard::KEY_HELP;
+	k[SDLK_MENU] = Keyboard::KEY_MENU;
+	k[SDLK_SELECT] = Keyboard::KEY_SELECT;
+	k[SDLK_STOP] = Keyboard::KEY_STOP;
+	k[SDLK_AGAIN] = Keyboard::KEY_AGAIN;
+	k[SDLK_UNDO] = Keyboard::KEY_UNDO;
+	k[SDLK_CUT] = Keyboard::KEY_CUT;
+	k[SDLK_COPY] = Keyboard::KEY_COPY;
+	k[SDLK_PASTE] = Keyboard::KEY_PASTE;
+	k[SDLK_FIND] = Keyboard::KEY_FIND;
+	k[SDLK_MUTE] = Keyboard::KEY_MUTE;
+	k[SDLK_VOLUMEUP] = Keyboard::KEY_VOLUMEUP;
+	k[SDLK_VOLUMEDOWN] = Keyboard::KEY_VOLUMEDOWN;
+
+	k[SDLK_ALTERASE] = Keyboard::KEY_ALTERASE;
+	k[SDLK_SYSREQ] = Keyboard::KEY_SYSREQ;
+	k[SDLK_CANCEL] = Keyboard::KEY_CANCEL;
+	k[SDLK_CLEAR] = Keyboard::KEY_CLEAR;
+	k[SDLK_PRIOR] = Keyboard::KEY_PRIOR;
+	k[SDLK_RETURN2] = Keyboard::KEY_RETURN2;
+	k[SDLK_SEPARATOR] = Keyboard::KEY_SEPARATOR;
+	k[SDLK_OUT] = Keyboard::KEY_OUT;
+	k[SDLK_OPER] = Keyboard::KEY_OPER;
+	k[SDLK_CLEARAGAIN] = Keyboard::KEY_CLEARAGAIN;
+
+	k[SDLK_THOUSANDSSEPARATOR] = Keyboard::KEY_THOUSANDSSEPARATOR;
+	k[SDLK_DECIMALSEPARATOR] = Keyboard::KEY_DECIMALSEPARATOR;
+	k[SDLK_CURRENCYUNIT] = Keyboard::KEY_CURRENCYUNIT;
+	k[SDLK_CURRENCYSUBUNIT] = Keyboard::KEY_CURRENCYSUBUNIT;
+
+	k[SDLK_LCTRL] = Keyboard::KEY_LCTRL;
+	k[SDLK_LSHIFT] = Keyboard::KEY_LSHIFT;
+	k[SDLK_LALT] = Keyboard::KEY_LALT;
+	k[SDLK_LGUI] = Keyboard::KEY_LGUI;
+	k[SDLK_RCTRL] = Keyboard::KEY_RCTRL;
+	k[SDLK_RSHIFT] = Keyboard::KEY_RSHIFT;
+	k[SDLK_RALT] = Keyboard::KEY_RALT;
+	k[SDLK_RGUI] = Keyboard::KEY_RGUI;
+
+	k[SDLK_MODE] = Keyboard::KEY_MODE;
+
+	k[SDLK_AUDIONEXT] = Keyboard::KEY_AUDIONEXT;
+	k[SDLK_AUDIOPREV] = Keyboard::KEY_AUDIOPREV;
+	k[SDLK_AUDIOSTOP] = Keyboard::KEY_AUDIOSTOP;
+	k[SDLK_AUDIOPLAY] = Keyboard::KEY_AUDIOPLAY;
+	k[SDLK_AUDIOMUTE] = Keyboard::KEY_AUDIOMUTE;
+	k[SDLK_MEDIASELECT] = Keyboard::KEY_MEDIASELECT;
+
+	k[SDLK_BRIGHTNESSDOWN] = Keyboard::KEY_BRIGHTNESSDOWN;
+	k[SDLK_BRIGHTNESSUP] = Keyboard::KEY_BRIGHTNESSUP;
+	k[SDLK_DISPLAYSWITCH] = Keyboard::KEY_DISPLAYSWITCH;
+	k[SDLK_KBDILLUMTOGGLE] = Keyboard::KEY_KBDILLUMTOGGLE;
+	k[SDLK_KBDILLUMDOWN] = Keyboard::KEY_KBDILLUMDOWN;
+	k[SDLK_KBDILLUMUP] = Keyboard::KEY_KBDILLUMUP;
+	k[SDLK_EJECT] = Keyboard::KEY_EJECT;
+	k[SDLK_SLEEP] = Keyboard::KEY_SLEEP;
+
+	return k;
+}
 
-EnumMap<love::keyboard::Keyboard::Key, SDLKey, love::keyboard::Keyboard::KEY_MAX_ENUM> Event::keys(Event::keyEntries, sizeof(Event::keyEntries));
+std::map<SDL_Keycode, love::keyboard::Keyboard::Key> Event::keys = Event::createKeyMap();
 
 EnumMap<love::mouse::Mouse::Button, Uint8, love::mouse::Mouse::BUTTON_MAX_ENUM>::Entry Event::buttonEntries[] =
 {
 	{ love::mouse::Mouse::BUTTON_LEFT, SDL_BUTTON_LEFT},
 	{ love::mouse::Mouse::BUTTON_MIDDLE, SDL_BUTTON_MIDDLE},
 	{ love::mouse::Mouse::BUTTON_RIGHT, SDL_BUTTON_RIGHT},
-	{ love::mouse::Mouse::BUTTON_WHEELUP, SDL_BUTTON_WHEELUP},
-	{ love::mouse::Mouse::BUTTON_WHEELDOWN, SDL_BUTTON_WHEELDOWN},
 	{ love::mouse::Mouse::BUTTON_X1, SDL_BUTTON_X1},
 	{ love::mouse::Mouse::BUTTON_X2, SDL_BUTTON_X2},
 };
 
 EnumMap<love::mouse::Mouse::Button, Uint8, love::mouse::Mouse::BUTTON_MAX_ENUM> Event::buttons(Event::buttonEntries, sizeof(Event::buttonEntries));
 
-EnumMap<love::joystick::Joystick::Hat, Uint8, love::joystick::Joystick::HAT_MAX_ENUM>::Entry Event::hatEntries[] =
-{
-	{love::joystick::Joystick::HAT_CENTERED, SDL_HAT_CENTERED},
-	{love::joystick::Joystick::HAT_UP, SDL_HAT_UP},
-	{love::joystick::Joystick::HAT_RIGHT, SDL_HAT_RIGHT},
-	{love::joystick::Joystick::HAT_DOWN, SDL_HAT_DOWN},
-	{love::joystick::Joystick::HAT_LEFT, SDL_HAT_LEFT},
-	{love::joystick::Joystick::HAT_RIGHTUP, SDL_HAT_RIGHTUP},
-	{love::joystick::Joystick::HAT_RIGHTDOWN, SDL_HAT_RIGHTDOWN},
-	{love::joystick::Joystick::HAT_LEFTUP, SDL_HAT_LEFTUP},
-	{love::joystick::Joystick::HAT_LEFTDOWN, SDL_HAT_LEFTDOWN},
-};
-
-EnumMap<love::joystick::Joystick::Hat, Uint8, love::joystick::Joystick::HAT_MAX_ENUM> Event::hats(Event::hatEntries, sizeof(Event::hatEntries));
-
 } // sdl
 } // event
 } // love

+ 10 - 6
src/modules/event/sdl/Event.h

@@ -29,6 +29,9 @@
 // SDL
 #include <SDL.h>
 
+// STL
+#include <map>
+
 namespace love
 {
 namespace event
@@ -44,6 +47,7 @@ public:
 	const char *getName() const;
 
 	Event();
+	virtual ~Event();
 
 	/**
 	 * Pumps the event queue. This function gathers all the pending input information
@@ -66,15 +70,15 @@ public:
 
 private:
 
-	Message *convert(SDL_Event &e);
-	const char *convertUnicode(Uint16 codepoint) const;
+	Message *convert(const SDL_Event &e) const;
+	Message *convertJoystickEvent(const SDL_Event &e) const;
+	Message *convertWindowEvent(const SDL_Event &e) const;
+
+	static std::map<SDL_Keycode, love::keyboard::Keyboard::Key> createKeyMap();
+	static std::map<SDL_Keycode, love::keyboard::Keyboard::Key> keys;
 
-	static EnumMap<love::keyboard::Keyboard::Key, SDLKey, love::keyboard::Keyboard::KEY_MAX_ENUM>::Entry keyEntries[];
-	static EnumMap<love::keyboard::Keyboard::Key, SDLKey, love::keyboard::Keyboard::KEY_MAX_ENUM> keys;
 	static EnumMap<love::mouse::Mouse::Button, Uint8, love::mouse::Mouse::BUTTON_MAX_ENUM>::Entry buttonEntries[];
 	static EnumMap<love::mouse::Mouse::Button, Uint8, love::mouse::Mouse::BUTTON_MAX_ENUM> buttons;
-	static EnumMap<love::joystick::Joystick::Hat, Uint8, love::joystick::Joystick::HAT_MAX_ENUM>::Entry hatEntries[];
-	static EnumMap<love::joystick::Joystick::Hat, Uint8, love::joystick::Joystick::HAT_MAX_ENUM> hats;
 
 }; // System
 

+ 2 - 2
src/modules/event/sdl/wrap_Event.cpp

@@ -127,9 +127,9 @@ extern "C" int luaopen_love_event(lua_State *L)
 		{
 			instance = new Event();
 		}
-		catch(Exception &e)
+		catch (love::Exception &e)
 		{
-			return luaL_error(L, e.what());
+			return luaL_error(L, "%s", e.what());
 		}
 	}
 	else

+ 5 - 0
src/modules/graphics/Graphics.h

@@ -108,6 +108,11 @@ public:
 
 	virtual ~Graphics();
 
+	/**
+	 * Sets the current graphics display viewport dimensions.
+	 **/
+	virtual void setViewportSize(int width, int height) = 0;
+
 	/**
 	 * Sets the current graphics display viewport and initializes the renderer.
 	 * @param width The viewport width.

+ 26 - 13
src/modules/graphics/opengl/Graphics.cpp

@@ -52,7 +52,7 @@ Graphics::Graphics()
 	, created(false)
 	, savedState()
 {
-	currentWindow = love::window::sdl::Window::getSingleton();
+	currentWindow = love::window::sdl::Window::createSingleton();
 
 	if (currentWindow->isCreated())
 		setMode(currentWindow->getWidth(), currentWindow->getHeight());
@@ -113,6 +113,27 @@ void Graphics::restoreState(const DisplayState &s)
 	setColorMask(s.colorMask[0], s.colorMask[1], s.colorMask[2], s.colorMask[3]);
 }
 
+void Graphics::setViewportSize(int width, int height)
+{
+	this->width = width;
+	this->height = height;
+
+	if (!isCreated())
+		return;
+
+	// Set the viewport to top-left corner
+	gl.setViewport(OpenGL::Viewport(0, 0, width, height));
+
+	// Reset the projection matrix
+	glMatrixMode(GL_PROJECTION);
+	glLoadIdentity();
+
+	// Set up orthographic view (no depth)
+	glOrtho(0.0, width, height, 0.0, -1.0, 1.0);
+
+	glMatrixMode(GL_MODELVIEW);
+}
+
 bool Graphics::setMode(int width, int height)
 {
 	this->width = width;
@@ -121,6 +142,10 @@ bool Graphics::setMode(int width, int height)
 	// Okay, setup OpenGL.
 	gl.initContext();
 
+	created = true;
+
+	setViewportSize(width, height);
+
 	// Make sure antialiasing works when set elsewhere
 	if (GLEE_VERSION_1_3 || GLEE_ARB_multisample)
 		glEnable(GL_MULTISAMPLE);
@@ -147,16 +172,6 @@ bool Graphics::setMode(int width, int height)
 	glEnable(GL_TEXTURE_2D);
 	gl.setTextureUnit(0);
 
-	// Set the viewport to top-left corner
-	gl.setViewport(OpenGL::Viewport(0, 0, width, height));
-
-	// Reset the projection matrix
-	glMatrixMode(GL_PROJECTION);
-	glLoadIdentity();
-
-	// Set up orthographic view (no depth)
-	glOrtho(0.0, width, height, 0.0, -1.0, 1.0);
-
 	// Reset modelview matrix
 	glMatrixMode(GL_MODELVIEW);
 	glLoadIdentity();
@@ -179,8 +194,6 @@ bool Graphics::setMode(int width, int height)
 	glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, &matrixLimit);
 	matrixLimit -= 5;
 
-	created = true;
-
 	return true;
 }
 

+ 1 - 0
src/modules/graphics/opengl/Graphics.h

@@ -112,6 +112,7 @@ public:
 
 	void restoreState(const DisplayState &s);
 
+	virtual void setViewportSize(int width, int height);
 	virtual bool setMode(int width, int height);
 	virtual void unSetMode();
 

+ 85 - 2
src/modules/joystick/Joystick.cpp

@@ -18,15 +18,26 @@
  * 3. This notice may not be removed or altered from any source distribution.
  **/
 
+// LOVE
 #include "Joystick.h"
 
+// STL
+#include <cmath>
+
 namespace love
 {
 namespace joystick
 {
 
-Joystick::~Joystick()
+float Joystick::clampval(float x) const
 {
+	if (fabsf(x) < 0.01)
+		return 0.0f;
+
+	if (x < -0.99f) return -1.0f;
+	if (x > 0.99f) return 1.0f;
+
+	return x;
 }
 
 bool Joystick::getConstant(const char *in, Joystick::Hat &out)
@@ -34,11 +45,41 @@ bool Joystick::getConstant(const char *in, Joystick::Hat &out)
 	return hats.find(in, out);
 }
 
-bool Joystick::getConstant(Joystick::Hat in, const char  *&out)
+bool Joystick::getConstant(Joystick::Hat in, const char *&out)
 {
 	return hats.find(in, out);
 }
 
+bool Joystick::getConstant(const char *in, Joystick::GamepadAxis &out)
+{
+	return gpAxes.find(in, out);
+}
+
+bool Joystick::getConstant(Joystick::GamepadAxis in, const char *&out)
+{
+	return gpAxes.find(in, out);
+}
+
+bool Joystick::getConstant(const char *in, Joystick::GamepadButton &out)
+{
+	return gpButtons.find(in, out);
+}
+
+bool Joystick::getConstant(Joystick::GamepadButton in, const char *&out)
+{
+	return gpButtons.find(in, out);
+}
+
+bool Joystick::getConstant(const char *in, Joystick::InputType &out)
+{
+	return inputTypes.find(in, out);
+}
+
+bool Joystick::getConstant(Joystick::InputType in, const char *&out)
+{
+	return inputTypes.find(in, out);
+}
+
 StringMap<Joystick::Hat, Joystick::HAT_MAX_ENUM>::Entry Joystick::hatEntries[] =
 {
 	{"c", Joystick::HAT_CENTERED},
@@ -54,5 +95,47 @@ StringMap<Joystick::Hat, Joystick::HAT_MAX_ENUM>::Entry Joystick::hatEntries[] =
 
 StringMap<Joystick::Hat, Joystick::HAT_MAX_ENUM> Joystick::hats(Joystick::hatEntries, sizeof(Joystick::hatEntries));
 
+StringMap<Joystick::GamepadAxis, Joystick::GAMEPAD_AXIS_MAX_ENUM>::Entry Joystick::gpAxisEntries[] =
+{
+	{"leftx", GAMEPAD_AXIS_LEFTX},
+	{"lefty", GAMEPAD_AXIS_LEFTY},
+	{"rightx", GAMEPAD_AXIS_RIGHTX},
+	{"righty", GAMEPAD_AXIS_RIGHTY},
+	{"triggerleft", GAMEPAD_AXIS_TRIGGERLEFT},
+	{"triggerright", GAMEPAD_AXIS_TRIGGERRIGHT},
+};
+
+StringMap<Joystick::GamepadAxis, Joystick::GAMEPAD_AXIS_MAX_ENUM> Joystick::gpAxes(Joystick::gpAxisEntries, sizeof(Joystick::gpAxisEntries));
+
+StringMap<Joystick::GamepadButton, Joystick::GAMEPAD_BUTTON_MAX_ENUM>::Entry Joystick::gpButtonEntries[] =
+{
+	{"a", GAMEPAD_BUTTON_A},
+	{"b", GAMEPAD_BUTTON_B},
+	{"x", GAMEPAD_BUTTON_X},
+	{"y", GAMEPAD_BUTTON_Y},
+	{"back", GAMEPAD_BUTTON_BACK},
+	{"guide", GAMEPAD_BUTTON_GUIDE},
+	{"start", GAMEPAD_BUTTON_START},
+	{"leftstick", GAMEPAD_BUTTON_LEFTSTICK},
+	{"rightstick", GAMEPAD_BUTTON_RIGHTSTICK},
+	{"leftshoulder", GAMEPAD_BUTTON_LEFTSHOULDER},
+	{"rightshoulder", GAMEPAD_BUTTON_RIGHTSHOULDER},
+	{"dpup", GAMEPAD_BUTTON_DPAD_UP},
+	{"dpdown", GAMEPAD_BUTTON_DPAD_DOWN},
+	{"dpleft", GAMEPAD_BUTTON_DPAD_LEFT},
+	{"dpright", GAMEPAD_BUTTON_DPAD_RIGHT},
+};
+
+StringMap<Joystick::GamepadButton, Joystick::GAMEPAD_BUTTON_MAX_ENUM> Joystick::gpButtons(Joystick::gpButtonEntries, sizeof(Joystick::gpButtonEntries));
+
+StringMap<Joystick::InputType, Joystick::INPUT_TYPE_MAX_ENUM>::Entry Joystick::inputTypeEntries[] =
+{
+	{"axis", Joystick::INPUT_TYPE_AXIS},
+	{"button", Joystick::INPUT_TYPE_BUTTON},
+	{"hat", Joystick::INPUT_TYPE_HAT},
+};
+
+StringMap<Joystick::InputType, Joystick::INPUT_TYPE_MAX_ENUM> Joystick::inputTypes(Joystick::inputTypeEntries, sizeof(Joystick::inputTypeEntries));
+
 } // joystick
 } // love

+ 133 - 5
src/modules/joystick/Joystick.h

@@ -22,18 +22,23 @@
 #define LOVE_JOYSTICK_JOYSTICK_H
 
 // LOVE
-#include "common/Module.h"
+#include "common/Object.h"
 #include "common/StringMap.h"
 
+// stdlib
+#include <vector>
+#include <string>
+
 namespace love
 {
 namespace joystick
 {
 
-class Joystick : public Module
+class Joystick : public Object
 {
 public:
 
+	// Joystick hat values.
 	enum Hat
 	{
 		HAT_INVALID,
@@ -49,19 +54,142 @@ public:
 		HAT_MAX_ENUM = 16
 	};
 
-	virtual ~Joystick();
+	// Valid Gamepad axes.
+	enum GamepadAxis
+	{
+		GAMEPAD_AXIS_INVALID,
+		GAMEPAD_AXIS_LEFTX,
+		GAMEPAD_AXIS_LEFTY,
+		GAMEPAD_AXIS_RIGHTX,
+		GAMEPAD_AXIS_RIGHTY,
+		GAMEPAD_AXIS_TRIGGERLEFT,
+		GAMEPAD_AXIS_TRIGGERRIGHT,
+		GAMEPAD_AXIS_MAX_ENUM
+	};
+
+	// Valid Gamepad buttons.
+	enum GamepadButton
+	{
+		GAMEPAD_BUTTON_INVALID,
+		GAMEPAD_BUTTON_A,
+		GAMEPAD_BUTTON_B,
+		GAMEPAD_BUTTON_X,
+		GAMEPAD_BUTTON_Y,
+		GAMEPAD_BUTTON_BACK,
+		GAMEPAD_BUTTON_GUIDE,
+		GAMEPAD_BUTTON_START,
+		GAMEPAD_BUTTON_LEFTSTICK,
+		GAMEPAD_BUTTON_RIGHTSTICK,
+		GAMEPAD_BUTTON_LEFTSHOULDER,
+		GAMEPAD_BUTTON_RIGHTSHOULDER,
+		GAMEPAD_BUTTON_DPAD_UP,
+		GAMEPAD_BUTTON_DPAD_DOWN,
+		GAMEPAD_BUTTON_DPAD_LEFT,
+		GAMEPAD_BUTTON_DPAD_RIGHT,
+		GAMEPAD_BUTTON_MAX_ENUM
+	};
+
+	// Different types of inputs for a joystick.
+	enum InputType
+	{
+		INPUT_TYPE_AXIS,
+		INPUT_TYPE_BUTTON,
+		INPUT_TYPE_HAT,
+		INPUT_TYPE_MAX_ENUM
+	};
+
+	// Represents a gamepad input value, e.g. the "x" button or the left trigger.
+	struct GamepadInput
+	{
+		InputType type;
+		union
+		{
+			GamepadAxis axis;
+			GamepadButton button;
+		};
+	};
+
+	// Represents a joystick input value, e.g. button 6 or axis 1.
+	struct JoystickInput
+	{
+		InputType type;
+		union
+		{
+			int axis;
+			int button;
+			struct
+			{
+				int index;
+				Hat value;
+			} hat;
+		};
+	};
+
+	virtual ~Joystick() {}
+
+	virtual bool open(int deviceindex) = 0;
+	virtual void close() = 0;
+
+	virtual bool isConnected() const = 0;
+
+	virtual const char *getName() const = 0;
+
+	virtual int getAxisCount() const = 0;
+	virtual int getButtonCount() const = 0;
+	virtual int getHatCount() const = 0;
+
+	virtual float getAxis(int axisindex) const = 0;
+	virtual std::vector<float> getAxes() const = 0;
+	virtual Hat getHat(int hatindex) const = 0;
+
+	virtual bool isDown(const std::vector<int> &buttonlist) const = 0;
+
+	virtual bool openGamepad(int deviceindex) = 0;
+	virtual bool isGamepad() const = 0;
+
+	virtual float getGamepadAxis(GamepadAxis axis) const = 0;
+	virtual bool isGamepadDown(const std::vector<GamepadButton> &blist) const = 0;
+
+	virtual void *getHandle() const = 0;
+
+	virtual std::string getGUID() const = 0;
+	virtual int getInstanceID() const = 0;
+	virtual int getID() const = 0;
 
 	static bool getConstant(const char *in, Hat &out);
-	static bool getConstant(Hat in, const char  *&out);
+	static bool getConstant(Hat in, const char *&out);
+
+	static bool getConstant(const char *in, GamepadAxis &out);
+	static bool getConstant(GamepadAxis in, const char *&out);
+
+	static bool getConstant(const char *in, GamepadButton &out);
+	static bool getConstant(GamepadButton in, const char *&out);
+
+	static bool getConstant(const char *in, InputType &out);
+	static bool getConstant(InputType in, const char *&out);
+
+protected:
+
+	float clampval(float x) const;
 
 private:
 
 	static StringMap<Hat, HAT_MAX_ENUM>::Entry hatEntries[];
 	static StringMap<Hat, HAT_MAX_ENUM> hats;
 
+	static StringMap<GamepadAxis, GAMEPAD_AXIS_MAX_ENUM>::Entry gpAxisEntries[];
+	static StringMap<GamepadAxis, GAMEPAD_AXIS_MAX_ENUM> gpAxes;
+
+	static StringMap<GamepadButton, GAMEPAD_BUTTON_MAX_ENUM>::Entry gpButtonEntries[];
+	static StringMap<GamepadButton, GAMEPAD_BUTTON_MAX_ENUM> gpButtons;
+
+	static StringMap<InputType, INPUT_TYPE_MAX_ENUM>::Entry inputTypeEntries[];
+	static StringMap<InputType, INPUT_TYPE_MAX_ENUM> inputTypes;
+
 }; // Joystick
 
 } // joystick
 } // love
 
-#endif // LOVE_JOYSTICK_JOYSTICK_H
+
+#endif // LOVE_JOYSTICK_JOYSTICK_H

+ 99 - 0
src/modules/joystick/JoystickModule.h

@@ -0,0 +1,99 @@
+/**
+ * Copyright (c) 2006-2013 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#ifndef LOVE_JOYSTICK_JOYSTICK_MODULE_H
+#define LOVE_JOYSTICK_JOYSTICK_MODULE_H
+
+// LOVE
+#include "common/Module.h"
+#include "Joystick.h"
+
+namespace love
+{
+namespace joystick
+{
+
+class JoystickModule : public Module
+{
+public:
+
+	virtual ~JoystickModule() {}
+
+	/**
+	 * Adds a connected Joystick device and opens it for use.
+	 * Returns NULL if the Joystick could not be added.
+	 **/
+	virtual Joystick *addJoystick(int deviceindex) = 0;
+
+	/**
+	 * Removes a disconnected Joystick device.
+	 **/
+	virtual void removeJoystick(Joystick *joystick) = 0;
+
+	/**
+	 * Gets a connected Joystick from its unique Instance ID.
+	 * Returns NULL if the instance ID does not correspond to a connected stick.
+	 **/
+	virtual Joystick *getJoystickFromID(int instanceid) = 0;
+
+	/**
+	 * Gets a connected Joystick.
+	 * Returns NULL if joyindex is not in the range of [0, getJoystickCount()).
+	 **/
+	virtual Joystick *getJoystick(int joyindex) = 0;
+
+	/**
+	 * Gets the index of a connected joystick.
+	 * Returns -1 if the joystick is not connected.
+	 **/
+	virtual int getIndex(const Joystick *joystick) = 0;
+
+	/**
+	 * Gets the number of currently connected Joysticks.
+	 **/
+	virtual int getJoystickCount() const = 0;
+
+	/**
+	 * Determines whether the Joystick at the specified index is a recognized
+	 * Gamepad.
+	 **/
+	virtual bool isGamepad(int joyindex) const = 0;
+
+	/**
+	 * Sets the virtual Gamepad mapping for a joystick input value for all
+	 * joystick devices with the specified joystick GUID.
+	 * If any joysticks with the specified GUID are connected, they will be
+	 * added as Gamepads if they aren't already, otherwise their Gamepad mapping
+	 * will be updated.
+	 **/
+	virtual bool setGamepadMapping(const std::string &guid, Joystick::GamepadInput gpinput, Joystick::JoystickInput joyinput) = 0;
+
+	/**
+	 * Gets the joystick input value the gamepad input value is bound to for the
+	 * specified joystick GUID.
+	 **/
+	virtual Joystick::JoystickInput getGamepadMapping(const std::string &guid, Joystick::GamepadInput gpinput) = 0;
+
+}; // JoystickModule
+
+} // joystick
+} // love
+
+#endif // LOVE_JOYSTICK_JOYSTICK_MODULE_H

+ 205 - 121
src/modules/joystick/sdl/Joystick.cpp

@@ -20,9 +20,6 @@
 
 #include "Joystick.h"
 
-// STD
-#include <cmath>
-
 namespace love
 {
 namespace joystick
@@ -30,206 +27,260 @@ namespace joystick
 namespace sdl
 {
 
-Joystick::Joystick()
-	: joysticks(0)
+Joystick::Joystick(int id)
+	: joyhandle(0)
+	, controller(0)
+	, instanceid(-1)
+	, id(id)
 {
-	// Init the SDL joystick system.
-	if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
-		throw love::Exception("%s", SDL_GetError());
-
-	// Start joystick event watching.
-	SDL_JoystickEventState(SDL_ENABLE);
-
-	// Open all connected joysticks.
-	int numjoysticks = getJoystickCount();
-	if (numjoysticks > 0)
-		this->joysticks = (SDL_Joystick **)calloc(numjoysticks, sizeof(SDL_Joystick *));
+}
 
-	for (int i = 0; i<numjoysticks; i++)
-		this->open(i);
+Joystick::Joystick(int id, int joyindex)
+	: joyhandle(0)
+	, controller(0)
+	, instanceid(-1)
+	, id(id)
+{
+	open(joyindex);
 }
 
 Joystick::~Joystick()
 {
-	// Closes any open joysticks.
-	for (int i = 0; i != getJoystickCount(); i++)
-	{
-		if (isOpen(i))
-			close(i);
-	}
-
-	free(joysticks);
-
-	SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
+	close();
 }
 
-void Joystick::reload()
+bool Joystick::open(int deviceindex)
 {
-	// Closes any open joysticks.
-	for (int i = 0; i != getJoystickCount(); i++)
+	close();
+
+	joyhandle = SDL_JoystickOpen(deviceindex);
+
+	if (joyhandle)
 	{
-		if (isOpen(i))
-			close(i);
+		instanceid = SDL_JoystickInstanceID(joyhandle);
+
+		// SDL_JoystickGetGUIDString uses 32 bytes plus the null terminator.
+		char cstr[33];
+
+		SDL_JoystickGUID sdlguid = SDL_JoystickGetGUID(joyhandle);
+		SDL_JoystickGetGUIDString(sdlguid, cstr, (int) sizeof(cstr));
+
+		guid = std::string(cstr);
+
+		// See if SDL thinks this is a Game Controller.
+		openGamepad(deviceindex);
+
+		// Prefer the GameController name.
+		if (controller)
+			name = SDL_GameControllerName(controller);
+		else
+			name = SDL_JoystickName(joyhandle);
 	}
 
-	free(joysticks);
+	return isConnected();
+}
 
-	SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
+void Joystick::close()
+{
+	if (controller)
+		SDL_GameControllerClose(controller);
 
-	if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
-		throw love::Exception("%s", SDL_GetError());
+	if (joyhandle)
+		SDL_JoystickClose(joyhandle);
 
-	int numjoysticks = this->getJoystickCount();
-	if (numjoysticks > 0)
-		this->joysticks = (SDL_Joystick **)calloc(numjoysticks, sizeof(SDL_Joystick *));
+	joyhandle = 0;
+	controller = 0;
+	instanceid = -1;
+}
 
-	for (int i = 0; i<numjoysticks; i++)
-		this->open(i);
+bool Joystick::isConnected() const
+{
+	return joyhandle != 0 && SDL_JoystickGetAttached(joyhandle);
 }
 
 const char *Joystick::getName() const
 {
-	return "love.joystick.sdl";
+	// Use the saved name if this Joystick isn't connected anymore.
+	if (!isConnected())
+		return name.c_str();
+
+	// Prefer SDL's GameController name, if possible.
+	if (isGamepad())
+		return SDL_GameControllerName(controller);
+
+	return SDL_JoystickName(joyhandle);
 }
 
-bool Joystick::checkIndex(int index)
+int Joystick::getAxisCount() const
 {
-	return index >= 0 && index < getJoystickCount();
+	return isConnected() ? SDL_JoystickNumAxes(joyhandle) : 0;
 }
 
-int Joystick::getJoystickCount()
+int Joystick::getButtonCount() const
 {
-	int num = SDL_NumJoysticks();
-	return num < 0 ? 0 : num;
+	return isConnected() ? SDL_JoystickNumButtons(joyhandle) : 0;
 }
 
-const char *Joystick::getName(int index)
+int Joystick::getHatCount() const
 {
-	return SDL_JoystickName(index);
+	return isConnected() ? SDL_JoystickNumHats(joyhandle) : 0;
 }
 
-bool Joystick::open(int index)
+float Joystick::getAxis(int axisindex) const
 {
-	if (isOpen(index))
-		return true;
+	if (!isConnected() || axisindex < 0 || axisindex >= getAxisCount())
+		return 0;
 
-	if (!checkIndex(index))
-		return false;
+	return clampval(((float) SDL_JoystickGetAxis(joyhandle, axisindex))/32768.0f);
+}
 
-	if (!(joysticks[index] = SDL_JoystickOpen(index)))
-		return false;
+std::vector<float> Joystick::getAxes() const
+{
+	std::vector<float> axes;
+	int count = getAxisCount();
+
+	if (!isConnected() || count <= 0)
+		return axes;
+
+	axes.reserve(count);
 
-	return true;
+	for (int i = 0; i < count; i++)
+		axes.push_back(clampval(((float) SDL_JoystickGetAxis(joyhandle, i))/32768.0f));
+
+	return axes;
 }
 
-bool Joystick::isOpen(int index)
+Joystick::Hat Joystick::getHat(int hatindex) const
 {
-	if (!checkIndex(index))
-		return false;
+	Hat h = HAT_INVALID;
+
+	if (!isConnected() || hatindex < 0 || hatindex >= getHatCount())
+		return h;
 
-	return joysticks[index] != 0 ? true : false;
+	getConstant(SDL_JoystickGetHat(joyhandle, hatindex), h);
+
+	return h;
 }
 
-bool Joystick::verifyJoystick(int index)
+bool Joystick::isDown(const std::vector<int> &buttonlist) const
 {
-	if (!checkIndex(index))
+	if (!isConnected())
 		return false;
 
-	if (!isOpen(index))
-		return false;
+	int num = getButtonCount();
 
-	return true;
-}
+	for (size_t i = 0; i < buttonlist.size(); i++)
+	{
+		int button = buttonlist[i];
+		if (button >= 0 && button < num && SDL_JoystickGetButton(joyhandle, button) == 1)
+			return true;
+	}
 
-int Joystick::getAxisCount(int index)
-{
-	return verifyJoystick(index) ? SDL_JoystickNumAxes(joysticks[index]) : 0;
+	return false;
 }
 
-int Joystick::getButtonCount(int index)
+bool Joystick::openGamepad(int deviceindex)
 {
-	return verifyJoystick(index) ? SDL_JoystickNumButtons(joysticks[index]) : 0;
-}
+	if (!SDL_IsGameController(deviceindex))
+		return false;
 
-int Joystick::getHatCount(int index)
-{
-	return verifyJoystick(index) ? SDL_JoystickNumHats(joysticks[index]) : 0;
-}
+	if (isGamepad())
+	{
+		SDL_GameControllerClose(controller);
+		controller = 0;
+	}
 
-float Joystick::clampval(float x)
-{
-	if (fabs((double)x) < 0.01) return 0.0f;
-	if (x < -0.99f) return -1.0f;
-	if (x > 0.99f) return 1.0f;
-	return x;
+	controller = SDL_GameControllerOpen(deviceindex);
+	return isGamepad();
 }
 
-float Joystick::getAxis(int index, int axis)
+bool Joystick::isGamepad() const
 {
-	if (!verifyJoystick(index))
-		return 0;
-
-	if (axis >= getAxisCount(index))
-		return 0;
-
-	return clampval(((float)SDL_JoystickGetAxis(joysticks[index], axis))/32768.0f);
+	return controller != 0;
 }
 
-int Joystick::getAxes(lua_State *L)
+float Joystick::getGamepadAxis(love::joystick::Joystick::GamepadAxis axis) const
 {
-	int index = luaL_checkint(L, 1) - 1;
+	if (!isConnected() || !isGamepad())
+		return 0.f;
 
-	if (!verifyJoystick(index))
-		return 0;
+	SDL_GameControllerAxis sdlaxis;
+	if (!getConstant(axis, sdlaxis))
+		return 0.f;
 
-	int num = getAxisCount(index);
+	Sint16 value = SDL_GameControllerGetAxis(controller, sdlaxis);
 
-	for (int i = 0; i<num; i++)
-		lua_pushnumber(L, clampval(((float)SDL_JoystickGetAxis(joysticks[index], i))/32768.0f));
-	return num;
+	return clampval((float) value / 32768.0f);
 }
 
-bool Joystick::isDown(int index, int *buttonlist)
+bool Joystick::isGamepadDown(const std::vector<GamepadButton> &blist) const
 {
-	if (!verifyJoystick(index))
+	if (!isConnected() || !isGamepad())
 		return false;
 
-	int num = getButtonCount(index);
+	SDL_GameControllerButton sdlbutton;
 
-	for (int button = *buttonlist; button != -1; button = *(++buttonlist))
+	for (size_t i = 0; i < blist.size(); i++)
 	{
-		if (button >= 0 && button < num && SDL_JoystickGetButton(joysticks[index], button) == 1)
+		if (!getConstant(blist[i], sdlbutton))
+			continue;
+
+		if (SDL_GameControllerGetButton(controller, sdlbutton) == 1)
 			return true;
 	}
 
 	return false;
 }
 
-Joystick::Hat Joystick::getHat(int index, int hat)
+void *Joystick::getHandle() const
 {
-	Hat h = HAT_INVALID;
+	return joyhandle;
+}
 
-	if (!verifyJoystick(index))
-		return h;
+std::string Joystick::getGUID() const
+{
+	// SDL2's GUIDs identify *classes* of devices, instead of unique devices.
+	return guid;
+}
 
-	if (hat >= getHatCount(index))
-		return h;
+int Joystick::getInstanceID() const
+{
+	return instanceid;
+}
 
-	hats.find(SDL_JoystickGetHat(joysticks[index], hat), h);
+int Joystick::getID() const
+{
+	return id;
+}
 
-	return h;
+bool Joystick::getConstant(Uint8 in, Joystick::Hat &out)
+{
+	return hats.find(in, out);
 }
 
-void Joystick::close(int index)
+bool Joystick::getConstant(Joystick::Hat in, Uint8 &out)
 {
-	if (!checkIndex(index))
-		return;
+	return hats.find(in, out);
+}
 
-	if (joysticks[index]!=0)
-	{
-		SDL_JoystickClose(joysticks[index]);
-		joysticks[index] = 0;
-	}
+bool Joystick::getConstant(SDL_GameControllerAxis in, Joystick::GamepadAxis &out)
+{
+	return gpAxes.find(in, out);
+}
+
+bool Joystick::getConstant(Joystick::GamepadAxis in, SDL_GameControllerAxis &out)
+{
+	return gpAxes.find(in, out);
+}
+
+bool Joystick::getConstant(SDL_GameControllerButton in, Joystick::GamepadButton &out)
+{
+	return gpButtons.find(in, out);
+}
+
+bool Joystick::getConstant(Joystick::GamepadButton in, SDL_GameControllerButton &out)
+{
+	return gpButtons.find(in, out);
 }
 
 EnumMap<Joystick::Hat, Uint8, Joystick::HAT_MAX_ENUM>::Entry Joystick::hatEntries[] =
@@ -247,6 +298,39 @@ EnumMap<Joystick::Hat, Uint8, Joystick::HAT_MAX_ENUM>::Entry Joystick::hatEntrie
 
 EnumMap<Joystick::Hat, Uint8, Joystick::HAT_MAX_ENUM> Joystick::hats(Joystick::hatEntries, sizeof(Joystick::hatEntries));
 
+EnumMap<Joystick::GamepadAxis, SDL_GameControllerAxis, Joystick::GAMEPAD_AXIS_MAX_ENUM>::Entry Joystick::gpAxisEntries[] =
+{
+	{Joystick::GAMEPAD_AXIS_LEFTX, SDL_CONTROLLER_AXIS_LEFTX},
+	{Joystick::GAMEPAD_AXIS_LEFTY, SDL_CONTROLLER_AXIS_LEFTY},
+	{Joystick::GAMEPAD_AXIS_RIGHTX, SDL_CONTROLLER_AXIS_RIGHTX},
+	{Joystick::GAMEPAD_AXIS_RIGHTY, SDL_CONTROLLER_AXIS_RIGHTY},
+	{Joystick::GAMEPAD_AXIS_TRIGGERLEFT, SDL_CONTROLLER_AXIS_TRIGGERLEFT},
+	{Joystick::GAMEPAD_AXIS_TRIGGERRIGHT, SDL_CONTROLLER_AXIS_TRIGGERRIGHT},
+};
+
+EnumMap<Joystick::GamepadAxis, SDL_GameControllerAxis, Joystick::GAMEPAD_AXIS_MAX_ENUM> Joystick::gpAxes(Joystick::gpAxisEntries, sizeof(Joystick::gpAxisEntries));
+
+EnumMap<Joystick::GamepadButton, SDL_GameControllerButton, Joystick::GAMEPAD_BUTTON_MAX_ENUM>::Entry Joystick::gpButtonEntries[] =
+{
+	{Joystick::GAMEPAD_BUTTON_A, SDL_CONTROLLER_BUTTON_A},
+	{Joystick::GAMEPAD_BUTTON_B, SDL_CONTROLLER_BUTTON_B},
+	{Joystick::GAMEPAD_BUTTON_X, SDL_CONTROLLER_BUTTON_X},
+	{Joystick::GAMEPAD_BUTTON_Y, SDL_CONTROLLER_BUTTON_Y},
+	{Joystick::GAMEPAD_BUTTON_BACK, SDL_CONTROLLER_BUTTON_BACK},
+	{Joystick::GAMEPAD_BUTTON_GUIDE, SDL_CONTROLLER_BUTTON_GUIDE},
+	{Joystick::GAMEPAD_BUTTON_START, SDL_CONTROLLER_BUTTON_START},
+	{Joystick::GAMEPAD_BUTTON_LEFTSTICK, SDL_CONTROLLER_BUTTON_LEFTSTICK},
+	{Joystick::GAMEPAD_BUTTON_RIGHTSTICK, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
+	{Joystick::GAMEPAD_BUTTON_LEFTSHOULDER, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
+	{Joystick::GAMEPAD_BUTTON_RIGHTSHOULDER, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
+	{Joystick::GAMEPAD_BUTTON_DPAD_UP, SDL_CONTROLLER_BUTTON_DPAD_UP},
+	{Joystick::GAMEPAD_BUTTON_DPAD_DOWN, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
+	{Joystick::GAMEPAD_BUTTON_DPAD_LEFT, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
+	{Joystick::GAMEPAD_BUTTON_DPAD_RIGHT, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
+};
+
+EnumMap<Joystick::GamepadButton, SDL_GameControllerButton, Joystick::GAMEPAD_BUTTON_MAX_ENUM> Joystick::gpButtons(Joystick::gpButtonEntries, sizeof(Joystick::gpButtonEntries));
+
 } // sdl
 } // joystick
 } // love

+ 59 - 22
src/modules/joystick/sdl/Joystick.h

@@ -26,7 +26,8 @@
 #include "common/EnumMap.h"
 
 // SDL
-#include <SDL.h>
+#include <SDL_joystick.h>
+#include <SDL_gamecontroller.h>
 
 namespace love
 {
@@ -37,38 +38,74 @@ namespace sdl
 
 class Joystick : public love::joystick::Joystick
 {
-private:
-	SDL_Joystick **joysticks;
 public:
-	Joystick();
+
+	Joystick(int id);
+	Joystick(int id, int joyindex);
+
 	virtual ~Joystick();
 
-	// Implements Module.
+	bool open(int deviceindex);
+	void close();
+
+	bool isConnected() const;
+
 	const char *getName() const;
 
-	void reload();
-	bool checkIndex(int index);
-	int getJoystickCount();
-	const char *getName(int index);
-	bool open(int index);
-	bool isOpen(int index);
-	bool verifyJoystick(int index);
-	int getAxisCount(int index);
-	int getButtonCount(int index);
-	int getHatCount(int index);
-	float clampval(float x);
-	float getAxis(int index, int axis);
-	int getAxes(lua_State *L);
-	bool isDown(int index, int *buttonlist);
-	Hat getHat(int index, int hat);
-	void close(int index);
+	int getAxisCount() const;
+	int getButtonCount() const;
+	int getHatCount() const;
+
+	float getAxis(int axisindex) const;
+	std::vector<float> getAxes() const;
+	Hat getHat(int hatindex) const;
+
+	bool isDown(const std::vector<int> &buttonlist) const;
+
+	bool openGamepad(int deviceindex);
+	bool isGamepad() const;
+
+	float getGamepadAxis(GamepadAxis axis) const;
+	bool isGamepadDown(const std::vector<GamepadButton> &blist) const;
+
+	void *getHandle() const;
+
+	std::string getGUID() const;
+	int getInstanceID() const;
+	int getID() const;
+
+	static bool getConstant(Hat in, Uint8 &out);
+	static bool getConstant(Uint8 in, Hat &out);
+
+	static bool getConstant(SDL_GameControllerAxis in, GamepadAxis &out);
+	static bool getConstant(GamepadAxis in, SDL_GameControllerAxis &out);
+
+	static bool getConstant(SDL_GameControllerButton in, GamepadButton &out);
+	static bool getConstant(GamepadButton in, SDL_GameControllerButton &out);
 
 private:
 
+	Joystick() {}
+
+	SDL_Joystick *joyhandle;
+	SDL_GameController *controller;
+
+	SDL_JoystickID instanceid;
+	std::string guid;
+	int id;
+
+	std::string name;
+
 	static EnumMap<Hat, Uint8, Joystick::HAT_MAX_ENUM>::Entry hatEntries[];
 	static EnumMap<Hat, Uint8, Joystick::HAT_MAX_ENUM> hats;
 
-}; // Joystick
+	static EnumMap<GamepadAxis, SDL_GameControllerAxis, GAMEPAD_AXIS_MAX_ENUM>::Entry gpAxisEntries[];
+	static EnumMap<GamepadAxis, SDL_GameControllerAxis, GAMEPAD_AXIS_MAX_ENUM> gpAxes;
+
+	static EnumMap<GamepadButton, SDL_GameControllerButton, GAMEPAD_BUTTON_MAX_ENUM>::Entry gpButtonEntries[];
+	static EnumMap<GamepadButton, SDL_GameControllerButton, GAMEPAD_BUTTON_MAX_ENUM> gpButtons;
+	
+};
 
 } // sdl
 } // joystick

+ 450 - 0
src/modules/joystick/sdl/JoystickModule.cpp

@@ -0,0 +1,450 @@
+/**
+ * Copyright (c) 2006-2013 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#include "common/config.h"
+#include "JoystickModule.h"
+#include "Joystick.h"
+
+// SDL
+#include <SDL.h>
+
+// C++
+#include <sstream>
+#include <algorithm>
+
+// C
+#include <cstdlib>
+
+namespace love
+{
+namespace joystick
+{
+namespace sdl
+{
+
+JoystickModule::JoystickModule()
+{
+	// Init the SDL Joystick and Game Controller systems.
+	if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0)
+		throw love::Exception("%s", SDL_GetError());
+
+	// Start joystick event watching. Joysticks are automatically added and
+	// removed via love.event.
+	SDL_JoystickEventState(SDL_ENABLE);
+	SDL_GameControllerEventState(SDL_ENABLE);
+}
+
+JoystickModule::~JoystickModule()
+{
+	// Close any open Joysticks.
+	for (size_t i = 0; i < joysticks.size(); i++)
+	{
+		joysticks[i]->close();
+		joysticks[i]->release();
+	}
+
+	SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER);
+}
+
+const char *JoystickModule::getName() const
+{
+	return "love.joystick.sdl";
+}
+
+love::joystick::Joystick *JoystickModule::getJoystick(int joyindex)
+{
+	if (joyindex < 0 || (size_t) joyindex >= activeSticks.size())
+		return 0;
+
+	return activeSticks[joyindex];
+}
+
+int JoystickModule::getIndex(const love::joystick::Joystick *joystick)
+{
+	for (size_t i = 0; i < activeSticks.size(); i++)
+	{
+		if (activeSticks[i] == joystick)
+			return i;
+	}
+
+	// Joystick is not connected.
+	return -1;
+}
+
+int JoystickModule::getJoystickCount() const
+{
+	return (int) activeSticks.size();
+}
+
+bool JoystickModule::isGamepad(int joyindex) const
+{
+	if (joyindex < 0 || (size_t) joyindex >= activeSticks.size())
+		return false;
+
+	return activeSticks[joyindex]->isGamepad();
+}
+
+love::joystick::Joystick *JoystickModule::getJoystickFromID(int instanceid)
+{
+	for (size_t i = 0; i < activeSticks.size(); i++)
+	{
+		if (instanceid == activeSticks[i]->getInstanceID())
+			return activeSticks[i];
+	}
+
+	return 0;
+}
+
+love::joystick::Joystick *JoystickModule::addJoystick(int deviceindex)
+{
+	if (deviceindex < 0 || deviceindex >= SDL_NumJoysticks())
+		return 0;
+
+	std::string guidstr = getDeviceGUID(deviceindex);
+
+	joystick::Joystick *joystick = 0;
+
+	for (size_t i = 0; i < joysticks.size(); i++)
+	{
+		// Try to re-use a disconnected Joystick with the same GUID.
+		if (!joysticks[i]->isConnected() && joysticks[i]->getGUID() == guidstr)
+		{
+			joystick = joysticks[i];
+			joystick->open(deviceindex);
+			break;
+		}
+	}
+
+	if (!joystick)
+	{
+		// Make a new Joystick and add it to the persistent list, if we can't
+		// re-use one.
+		joystick = new Joystick(joysticks.size(), deviceindex);
+		joysticks.push_back(joystick);
+	}
+
+	// Add the Joystick to the connected list, if it's not there already.
+	if (std::find(activeSticks.begin(), activeSticks.end(), joystick) == activeSticks.end())
+		activeSticks.push_back(joystick);
+
+	return joystick;
+}
+
+void JoystickModule::removeJoystick(love::joystick::Joystick *joystick)
+{
+	if (!joystick)
+		return;
+
+	// Close the Joystick and remove it from the active joystick list.
+	std::vector<joystick::Joystick *>::iterator it = std::find(activeSticks.begin(), activeSticks.end(), joystick);
+	if (it != activeSticks.end())
+	{
+		(*it)->close();
+		activeSticks.erase(it);
+	}
+}
+
+bool JoystickModule::setGamepadMapping(const std::string &guid, Joystick::GamepadInput gpinput, Joystick::JoystickInput joyinput)
+{
+	// All SDL joystick GUID strings are 32 characters.
+	if (guid.length() != 32)
+		throw love::Exception("Invalid joystick GUID: %s", guid.c_str());
+
+	SDL_JoystickGUID sdlguid = SDL_JoystickGetGUIDFromString(guid.c_str());
+	std::string mapstr;
+
+	char *sdlmapstr = SDL_GameControllerMappingForGUID(sdlguid);
+	if (sdlmapstr)
+	{
+		mapstr = sdlmapstr;
+		SDL_free(sdlmapstr);
+	}
+	else
+	{
+		// Use a generic name if we have to create a new mapping string.
+		mapstr = guid + ",Controller,";
+	}
+
+	std::stringstream joyinputstream;
+	Uint8 sdlhat;
+
+	// We can't have negative int values in the bind string.
+	switch (joyinput.type)
+	{
+	case Joystick::INPUT_TYPE_AXIS:
+		if (joyinput.axis >= 0)
+			joyinputstream << "a" << joyinput.axis;
+		break;
+	case Joystick::INPUT_TYPE_BUTTON:
+		if (joyinput.button >= 0)
+			joyinputstream << "b" << joyinput.button;
+		break;
+	case Joystick::INPUT_TYPE_HAT:
+		if (joyinput.hat.value >= 0 && Joystick::getConstant(joyinput.hat.value, sdlhat))
+			joyinputstream << "h" << joyinput.hat.value << "." << int(sdlhat);
+		break;
+	default:
+		break;
+	}
+
+	std::string joyinputstr = joyinputstream.str();
+
+	if (joyinputstr.length() == 0)
+		throw love::Exception("Invalid joystick input value.");
+
+	// SDL's name for the gamepad input value, e.g. "guide".
+	std::string gpinputname = stringFromGamepadInput(gpinput);
+
+	// We should remove any existing joystick bind for this gamepad buttton/axis
+	// so SDL's parser doesn't get mixed up.
+	removeBindFromMapString(mapstr, joyinputstr);
+
+	// The string we'll be adding to the mapping string, e.g. "guide:b10,"
+	std::string insertstr = gpinputname + ":" + joyinputstr + ",";
+
+	// We should replace any existing gamepad bind.
+	size_t findpos = mapstr.find(gpinputname + ":");
+	if (findpos != std::string::npos)
+	{
+		// The bind string ends at the next comma, or the end of the string.
+		size_t endpos = mapstr.find_first_of(',', findpos);
+		if (endpos == std::string::npos)
+			endpos = mapstr.length() - 1;
+
+		mapstr.replace(findpos, endpos - findpos + 1, insertstr);
+	}
+	else
+	{
+		// Just append to the end if we don't need to replace anything.
+		mapstr += insertstr;
+	}
+
+	// 1 == added, 0 == updated, -1 == error.
+	int status = SDL_GameControllerAddMapping(mapstr.c_str());
+
+	// FIXME: massive hack until missing APIs are added to SDL 2:
+	// https://bugzilla.libsdl.org/show_bug.cgi?id=1975
+	if (status == 1)
+		checkGamepads(guid);
+
+	return  status >= 0;
+}
+
+Joystick::JoystickInput JoystickModule::getGamepadMapping(const std::string &guid, Joystick::GamepadInput gpinput)
+{
+	// All SDL joystick GUID strings are 32 characters.
+	if (guid.length() != 32)
+		throw love::Exception("Invalid joystick GUID: %s", guid.c_str());
+
+	Joystick::JoystickInput jinput;
+	jinput.type = Joystick::INPUT_TYPE_MAX_ENUM;
+
+	SDL_JoystickGUID sdlguid = SDL_JoystickGetGUIDFromString(guid.c_str());
+
+	std::string mapstr;
+
+	char *sdlmapstr = SDL_GameControllerMappingForGUID(sdlguid);
+	if (!sdlmapstr)
+		return jinput;
+
+	mapstr = sdlmapstr;
+	SDL_free(sdlmapstr);
+
+	std::string gpbindname = stringFromGamepadInput(gpinput);
+
+	size_t findpos = mapstr.find(std::string(",") + gpbindname + ":");
+	if (findpos == std::string::npos)
+		return jinput;
+
+	size_t endpos = mapstr.find_first_of(',', findpos);
+	if (endpos == std::string::npos)
+	{
+		// Assume end-of-string if we can't find the next comma.
+		endpos = mapstr.length() - 1;
+	}
+
+	if (endpos >= mapstr.length())
+		return jinput; // Something went wrong.
+
+	// Strip out the trailing comma from our search position, if it exists.
+	if (mapstr[endpos] == ',')
+		endpos--;
+
+	// New start position: comma + gamepadinputlength + ":".
+	findpos += 1 + gpbindname.length() + 1;
+	std::string jbindstr = mapstr.substr(findpos, endpos - findpos + 1);
+
+	jinput = JoystickInputFromString(jbindstr);
+	return jinput;
+}
+
+std::string JoystickModule::stringFromGamepadInput(Joystick::GamepadInput gpinput) const
+{
+	SDL_GameControllerAxis sdlaxis;
+	SDL_GameControllerButton sdlbutton;
+
+	const char *gpinputname = 0;
+
+	switch (gpinput.type)
+	{
+	case Joystick::INPUT_TYPE_AXIS:
+		if (Joystick::getConstant(gpinput.axis, sdlaxis))
+			gpinputname = SDL_GameControllerGetStringForAxis(sdlaxis);
+		break;
+	case Joystick::INPUT_TYPE_BUTTON:
+		if (Joystick::getConstant(gpinput.button, sdlbutton))
+			gpinputname = SDL_GameControllerGetStringForButton(sdlbutton);
+		break;
+	default:
+		break;
+	}
+
+	if (!gpinputname)
+		throw love::Exception("Invalid gamepad axis/button.");
+
+	return std::string(gpinputname);
+}
+
+Joystick::JoystickInput JoystickModule::JoystickInputFromString(const std::string &str) const
+{
+	Joystick::JoystickInput jinput;
+	jinput.type = Joystick::INPUT_TYPE_MAX_ENUM;
+
+	// Return an invalid value rather than throwing an exception.
+	if (str.length() < 2)
+		return jinput;
+
+	// The input type will always be the first character in the string.
+	char inputtype = str[0];
+	std::string bindvalues = str.substr(1);
+
+	Uint8 sdlhat;
+	switch (inputtype)
+	{
+	case 'a':
+		jinput.type = Joystick::INPUT_TYPE_AXIS;
+		jinput.axis = atoi(bindvalues.c_str());
+		break;
+	case 'b':
+		jinput.type = Joystick::INPUT_TYPE_BUTTON;
+		jinput.button = atoi(bindvalues.c_str());
+		break;
+	case 'h':
+		// Hat string syntax is "index.value".
+		if (bindvalues.length() < 3)
+			break;
+		jinput.type = Joystick::INPUT_TYPE_HAT;
+		jinput.hat.index = atoi(bindvalues.substr(0, 1).c_str());
+		sdlhat = (Uint8) atoi(bindvalues.substr(2, 1).c_str());
+		if (!Joystick::getConstant(sdlhat, jinput.hat.value))
+		{
+			// Return an invalid value if we can't find the hat constant.
+			jinput.type = Joystick::INPUT_TYPE_MAX_ENUM;
+			return jinput;
+		}
+		break;
+	default:
+		break;
+	}
+
+	return jinput;
+}
+
+void JoystickModule::removeBindFromMapString(std::string &mapstr, const std::string &joybindstr) const
+{
+	// Find the joystick part of the bind in the string.
+	size_t joybindpos = mapstr.find(joybindstr + ",");
+	if (joybindpos == std::string::npos)
+	{
+		joybindpos = mapstr.rfind(joybindstr);
+		if (joybindpos != mapstr.length() - joybindstr.length())
+			return;
+	}
+
+	if (joybindpos == std::string::npos)
+		return;
+
+	// Find the start of the entire bind.
+	size_t bindstart = mapstr.rfind(',', joybindpos);
+	if (bindstart != std::string::npos && bindstart < mapstr.length() - 1)
+	{
+		size_t bindend = mapstr.find(',', bindstart + 1);
+		if (bindend == std::string::npos)
+			bindend = mapstr.length() - 1;
+
+		// Replace it with an empty string (remove it.)
+		mapstr.replace(bindstart, bindend - bindstart + 1, "");
+	}
+}
+
+void JoystickModule::checkGamepads(const std::string &guid) const
+{
+	// FIXME: massive hack until missing APIs are added to SDL 2:
+	// https://bugzilla.libsdl.org/show_bug.cgi?id=1975
+
+	// Make sure all connected joysticks of a certain guid that are
+	// gamepad-capable are opened as such.
+	for (int d_index = 0; d_index < SDL_NumJoysticks(); d_index++)
+	{
+		if (!SDL_IsGameController(d_index))
+			continue;
+
+		if (guid.compare(getDeviceGUID(d_index)) != 0)
+			continue;
+
+		std::vector<love::joystick::Joystick *>::const_iterator it;
+		for (it = activeSticks.begin(); it != activeSticks.end(); ++it)
+		{
+			if ((*it)->isGamepad() || guid.compare((*it)->getGUID()) != 0)
+				continue;
+
+			// Big hack time: open the index as a game controller and compare
+			// the underlying joystick handle to the active stick's.
+			SDL_GameController *ctrl = SDL_GameControllerOpen(d_index);
+			if (ctrl == NULL)
+				continue;
+
+			SDL_Joystick *stick = SDL_GameControllerGetJoystick(ctrl);
+			if (stick == (SDL_Joystick *) (*it)->getHandle())
+				(*it)->openGamepad(d_index);
+
+			SDL_GameControllerClose(ctrl);
+		}
+	}
+}
+
+std::string JoystickModule::getDeviceGUID(int deviceindex) const
+{
+	if (deviceindex < 0 || deviceindex >= SDL_NumJoysticks())
+		return std::string("");
+
+	// SDL_JoystickGetGUIDString uses 32 bytes plus the null terminator.
+	char guidstr[33] = {'\0'};
+
+	// SDL2's GUIDs identify *classes* of devices, instead of unique devices.
+	SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(deviceindex);
+	SDL_JoystickGetGUIDString(guid, guidstr, sizeof(guidstr));
+
+	return std::string(guidstr);
+}
+
+} // sdl
+} // joystick
+} // love

+ 81 - 0
src/modules/joystick/sdl/JoystickModule.h

@@ -0,0 +1,81 @@
+/**
+ * Copyright (c) 2006-2013 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#ifndef LOVE_JOYSTICK_SDL_JOYSTICK_MODULE_H
+#define LOVE_JOYSTICK_SDL_JOYSTICK_MODULE_H
+
+// LOVE
+#include "joystick/JoystickModule.h"
+
+// STL
+#include <string>
+#include <map>
+
+namespace love
+{
+namespace joystick
+{
+namespace sdl
+{
+
+class JoystickModule : public love::joystick::JoystickModule
+{
+public:
+
+	JoystickModule();
+	virtual ~JoystickModule();
+
+	// Implements Module.
+	const char *getName() const;
+
+	// Implements JoystickModule.
+	love::joystick::Joystick *addJoystick(int deviceindex);
+	void removeJoystick(love::joystick::Joystick *joystick);
+	love::joystick::Joystick *getJoystickFromID(int instanceid);
+	love::joystick::Joystick *getJoystick(int joyindex);
+	int getIndex(const love::joystick::Joystick *joystick);
+	int getJoystickCount() const;
+	bool isGamepad(int joyindex) const;
+	bool setGamepadMapping(const std::string &guid, Joystick::GamepadInput gpinput, Joystick::JoystickInput joyinput);
+	Joystick::JoystickInput getGamepadMapping(const std::string &guid, Joystick::GamepadInput gpinput);
+
+private:
+
+	std::string stringFromGamepadInput(Joystick::GamepadInput gpinput) const;
+	Joystick::JoystickInput JoystickInputFromString(const std::string &str) const;
+	void removeBindFromMapString(std::string &mapstr, const std::string &joybindstr) const;
+	void checkGamepads(const std::string &guid) const;
+
+	// SDL2's GUIDs identify *classes* of devices, instead of unique devices.
+	std::string getDeviceGUID(int deviceindex) const;
+
+	// Lists of currently connected Joysticks.
+	std::vector<Joystick *> activeSticks;
+
+	// Persistent list of all Joysticks which have been connected at some point.
+	std::vector<Joystick *> joysticks;
+
+}; // JoystickModule
+
+} // sdl
+} // joystick
+} // love
+
+#endif // LOVE_JOYSTICK_SDL_JOYSTICK_MODULE_H

+ 124 - 82
src/modules/joystick/sdl/wrap_Joystick.cpp

@@ -18,7 +18,11 @@
  * 3. This notice may not be removed or altered from any source distribution.
  **/
 
+// LOVE
 #include "wrap_Joystick.h"
+#include "wrap_JoystickModule.h"
+
+#include <vector>
 
 namespace love
 {
@@ -27,141 +31,179 @@ namespace joystick
 namespace sdl
 {
 
-static Joystick *instance = 0;
-
-int w_reload(lua_State *L)
+Joystick *luax_checkjoystick(lua_State *L, int idx)
 {
-	try
-	{
-		instance->reload();
-	}
-	catch(love::Exception &e)
-	{
-		return luaL_error(L, "%s", e.what());
-	}
-	return 0;
+	return luax_checktype<Joystick>(L, idx, "Joystick", JOYSTICK_JOYSTICK_T);
 }
 
-int w_getJoystickCount(lua_State *L)
+int w_Joystick_isConnected(lua_State *L)
 {
-	lua_pushinteger(L, instance->getJoystickCount());
+	Joystick *j = luax_checkjoystick(L, 1);
+	luax_pushboolean(L, j->isConnected());
 	return 1;
 }
 
-int w_getName(lua_State *L)
+int w_Joystick_getName(lua_State *L)
 {
-	int index = luaL_checkint(L, 1) - 1;
-	lua_pushstring(L, instance->getName(index));
+	Joystick *j = luax_checkjoystick(L, 1);
+	lua_pushstring(L, j->getName());
 	return 1;
 }
 
-int w_getAxisCount(lua_State *L)
+int w_Joystick_getID(lua_State *L)
 {
-	int index = luaL_checkint(L, 1) - 1;
-	lua_pushinteger(L, instance->getAxisCount(index));
+	Joystick *j = luax_checkjoystick(L, 1);
+	lua_pushinteger(L, j->getID());
 	return 1;
 }
 
-int w_getButtonCount(lua_State *L)
+int w_Joystick_getGUID(lua_State *L)
 {
-	int index = luaL_checkint(L, 1) - 1;
-	lua_pushinteger(L, instance->getButtonCount(index));
+	Joystick *j = luax_checkjoystick(L, 1);
+	luax_pushstring(L, j->getGUID());
 	return 1;
 }
 
-int w_getHatCount(lua_State *L)
+int w_Joystick_getAxisCount(lua_State *L)
 {
-	int index = luaL_checkint(L, 1) - 1;
-	lua_pushinteger(L, instance->getHatCount(index));
+	Joystick *j = luax_checkjoystick(L, 1);
+	lua_pushinteger(L, j->getAxisCount());
 	return 1;
 }
 
-int w_getAxis(lua_State *L)
+int w_Joystick_getButtonCount(lua_State *L)
 {
-	int index = luaL_checkint(L, 1) - 1;
-	int axis = luaL_checkint(L, 2) - 1;
-	lua_pushnumber(L, instance->getAxis(index, axis));
+	Joystick *j = luax_checkjoystick(L, 1);
+	lua_pushinteger(L, j->getButtonCount());
 	return 1;
 }
 
-int w_getAxes(lua_State *L)
+int w_Joystick_getHatCount(lua_State *L)
 {
-	return instance->getAxes(L);
+	Joystick *j = luax_checkjoystick(L, 1);
+	lua_pushinteger(L, j->getHatCount());
+	return 1;
 }
 
-int w_isDown(lua_State *L)
+int w_Joystick_getAxis(lua_State *L)
 {
-	int index = luaL_checkint(L, 1) - 1;
-	int num = lua_gettop(L);
-	
-	int *buttonlist = new int[num];
-	int counter = 0;
+	Joystick *j = luax_checkjoystick(L, 1);
+	int axisindex = luaL_checkint(L, 2) - 1;
+	lua_pushnumber(L, j->getAxis(axisindex));
+	return 1;
+}
 
-	for (int i = 1; i < num; i++)
-		buttonlist[counter++] = luaL_checkint(L, i + 1) - 1;
+int w_Joystick_getAxes(lua_State *L)
+{
+	Joystick *j = luax_checkjoystick(L, 1);
+	std::vector<float> axes = j->getAxes();
 
-	buttonlist[counter] = -1;
+	for (size_t i = 0; i < axes.size(); i++)
+		lua_pushnumber(L, axes[i]);
 
-	luax_pushboolean(L, instance->isDown(index, buttonlist));
-	delete[] buttonlist;
-	return 1;
+	return (int) axes.size();
 }
 
-int w_getHat(lua_State *L)
+int w_Joystick_getHat(lua_State *L)
 {
-	int index = luaL_checkint(L, 1)-1;
-	int hat = luaL_checkint(L, 2)-1;
+	Joystick *j = luax_checkjoystick(L, 1);
+	int hatindex = luaL_checkint(L, 2) - 1;
 
-	Joystick::Hat h = instance->getHat(index, hat);
+	Joystick::Hat h = j->getHat(hatindex);
 
 	const char *direction = "";
-	Joystick::getConstant(h, direction);
+	love::joystick::Joystick::getConstant(h, direction);
+
 	lua_pushstring(L, direction);
+	return 1;
+}
+
+int w_Joystick_isDown(lua_State *L)
+{
+	Joystick *j = luax_checkjoystick(L, 1);
 
+	luaL_checkinteger(L, 2);
+
+	std::vector<int> buttons;
+	for (int i = 2; i <= lua_gettop(L); i++)
+		buttons.push_back(luaL_checkint(L, i));
+
+	luax_pushboolean(L, j->isDown(buttons));
 	return 1;
 }
 
-// List of functions to wrap.
-static const luaL_Reg functions[] =
+int w_Joystick_isGamepad(lua_State *L)
 {
-	{ "reload", w_reload },
-	{ "getJoystickCount", w_getJoystickCount },
-	{ "getName", w_getName },
-	{ "getAxisCount", w_getAxisCount },
-	{ "getButtonCount", w_getButtonCount },
-	{ "getHatCount", w_getHatCount },
-	{ "getAxis", w_getAxis },
-	{ "getAxes", w_getAxes },
-	{ "isDown", w_isDown },
-	{ "getHat", w_getHat },
-	{ 0, 0 }
-};
+	Joystick *j = luax_checkjoystick(L, 1);
+	luax_pushboolean(L, j->isGamepad());
+	return 1;
+}
 
-extern "C" int luaopen_love_joystick(lua_State *L)
+int w_Joystick_getGamepadAxis(lua_State *L)
 {
-	if (instance == 0)
+	Joystick *j = luax_checkjoystick(L, 1);
+
+	const char *str = luaL_checkstring(L, 2);
+	Joystick::GamepadAxis axis;
+
+	if (!joystick::Joystick::getConstant(str, axis))
+		return luaL_error(L, "Invalid gamepad axis: %s", str);
+
+	lua_pushnumber(L, j->getGamepadAxis(axis));
+	return 1;
+}
+
+int w_Joystick_isGamepadDown(lua_State *L)
+{
+	Joystick *j = luax_checkjoystick(L, 1);
+
+	std::vector<Joystick::GamepadButton> buttons;
+	buttons.reserve(lua_gettop(L) - 1);
+
+	luaL_checkstring(L, 2);
+
+	for (int i = 2; i <= lua_gettop(L); i++)
 	{
-		try
-		{
-			instance = new Joystick();
-		}
-		catch(Exception &e)
-		{
-			return luaL_error(L, e.what());
-		}
+		const char *str = luaL_checkstring(L, i);
+		Joystick::GamepadButton button;
+
+		if (!joystick::Joystick::getConstant(str, button))
+			return luaL_error(L, "Invalid gamepad button: %s", str);
+
+		buttons.push_back(button);
 	}
-	else
-		instance->retain();
 
+	luax_pushboolean(L, j->isGamepadDown(buttons));
+	return 1;
+}
 
-	WrappedModule w;
-	w.module = instance;
-	w.name = "joystick";
-	w.flags = MODULE_T;
-	w.functions = functions;
-	w.types = 0;
+// List of functions to wrap.
+static const luaL_Reg functions[] =
+{
+	{ "isConnected", w_Joystick_isConnected },
+	{ "getName", w_Joystick_getName },
+	{ "getID", w_Joystick_getID },
+	{ "getGUID", w_Joystick_getGUID },
+	{ "getAxisCount", w_Joystick_getAxisCount },
+	{ "getButtonCount", w_Joystick_getButtonCount },
+	{ "getHatCount", w_Joystick_getHatCount },
+	{ "getAxis", w_Joystick_getAxis },
+	{ "getAxes", w_Joystick_getAxes },
+	{ "getHat", w_Joystick_getHat },
+	{ "isDown", w_Joystick_isDown },
+	{ "isGamepad", w_Joystick_isGamepad },
+	{ "getGamepadAxis", w_Joystick_getGamepadAxis },
+	{ "isGamepadDown", w_Joystick_isGamepadDown },
+
+	// From wrap_JoystickModule.
+	{ "getIndex", w_getIndex },
+	{ "getGamepadMapping", w_getGamepadMapping },
+	{ 0, 0 },
+};
 
-	return luax_register_module(L, w);
+extern "C" int luaopen_joystick(lua_State *L)
+{
+	return luax_register_type(L, "Joystick", functions);
 }
 
 } // sdl

+ 17 - 11
src/modules/joystick/sdl/wrap_Joystick.h

@@ -24,6 +24,7 @@
 // LOVE
 #include "common/config.h"
 #include "Joystick.h"
+#include "common/runtime.h"
 
 namespace love
 {
@@ -32,17 +33,22 @@ namespace joystick
 namespace sdl
 {
 
-int w_reload(lua_State *L);
-int w_getJoystickCount(lua_State *L);
-int w_getName(lua_State *L);
-int w_getAxisCount(lua_State *L);
-int w_getButtonCount(lua_State *L);
-int w_getHatCount(lua_State *L);
-int w_getAxis(lua_State *L);
-int w_getAxes(lua_State *L);
-int w_isDown(lua_State *L);
-int w_getHat(lua_State *L);
-extern "C" LOVE_EXPORT int luaopen_love_joystick(lua_State *L);
+Joystick *luax_checkjoystick(lua_State *L, int idx);
+int w_Joystick_isConnected(lua_State *L);
+int w_Joystick_getName(lua_State *L);
+int w_Joystick_getID(lua_State *L);
+int w_Joystick_getGUID(lua_State *L);
+int w_Joystick_getAxisCount(lua_State *L);
+int w_Joystick_getButtonCount(lua_State *L);
+int w_Joystick_getHatCount(lua_State *L);
+int w_Joystick_getAxis(lua_State *L);
+int w_Joystick_getAxes(lua_State *L);
+int w_Joystick_getHat(lua_State *L);
+int w_Joystick_isDown(lua_State *L);
+int w_Joystick_isGamepad(lua_State *L);
+int w_Joystick_getGamepadAxis(lua_State *L);
+int w_Joystick_isGamepadDown(lua_State *L);
+extern "C" int luaopen_joystick(lua_State *L);
 
 } // sdl
 } // joystick

+ 244 - 0
src/modules/joystick/sdl/wrap_JoystickModule.cpp

@@ -0,0 +1,244 @@
+/**
+ * Copyright (c) 2006-2013 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#include "wrap_JoystickModule.h"
+#include "wrap_Joystick.h"
+
+namespace love
+{
+namespace joystick
+{
+namespace sdl
+{
+
+static JoystickModule *instance = 0;
+
+int w_getJoystick(lua_State *L)
+{
+	int index = luaL_checkint(L, 1) - 1;
+	love::joystick::Joystick *stick = instance->getJoystick(index);
+
+	if (!stick)
+		return luaL_error(L, "Invalid joystick index");
+
+	stick->retain();
+	luax_newtype(L, "Joystick", JOYSTICK_JOYSTICK_T, (void *) stick);
+	
+	return 1;
+}
+
+int w_getIndex(lua_State *L)
+{
+	love::joystick::Joystick *j = luax_checkjoystick(L, 1);
+	int index = instance->getIndex(j);
+	if (index >= 0)
+		lua_pushinteger(L, index + 1);
+	else
+		lua_pushinteger(L, -1);
+	return 1;
+}
+
+int w_getJoystickCount(lua_State *L)
+{
+	lua_pushinteger(L, instance->getJoystickCount());
+	return 1;
+}
+
+int w_isGamepad(lua_State *L)
+{
+	int index = luaL_checkint(L, 1) - 1;
+	luax_pushboolean(L, instance->isGamepad(index));
+	return 1;
+}
+
+int w_setGamepadMapping(lua_State *L)
+{
+	// Only accept a GUID string. We don't accept a Joystick object because
+	// the gamepad mapping applies to all joysticks with the same GUID (e.g. all
+	// Xbox 360 controllers on the system), rather than individual objects.
+	std::string guid = luax_checkstring(L, 1);
+
+	const char *gpbindstr = luaL_checkstring(L, 2);
+	Joystick::GamepadInput gpinput;
+
+	if (love::joystick::Joystick::getConstant(gpbindstr, gpinput.axis))
+		gpinput.type = Joystick::INPUT_TYPE_AXIS;
+	else if (love::joystick::Joystick::getConstant(gpbindstr, gpinput.button))
+		gpinput.type = Joystick::INPUT_TYPE_BUTTON;
+	else
+		return luaL_error(L, "Invalid gamepad axis/button: %s", gpbindstr);
+
+	const char *jinputtypestr = luaL_checkstring(L, 3);
+	Joystick::JoystickInput jinput;
+
+	if (!love::joystick::Joystick::getConstant(jinputtypestr, jinput.type))
+		return luaL_error(L, "Invalid joystick input type: %s", jinputtypestr);
+
+	const char *hatstr;
+	switch (jinput.type)
+	{
+	case Joystick::INPUT_TYPE_AXIS:
+		jinput.axis = luaL_checkint(L, 4) - 1;
+		break;
+	case Joystick::INPUT_TYPE_BUTTON:
+		jinput.button = luaL_checkint(L, 4) - 1;
+		break;
+	case Joystick::INPUT_TYPE_HAT:
+		// Hats need both a hat index and a hat value.
+		jinput.hat.index = luaL_checkint(L, 4) - 1;
+		hatstr = luaL_checkstring(L, 5);
+		if (!love::joystick::Joystick::getConstant(hatstr, jinput.hat.value))
+			return luaL_error(L, "Invalid joystick hat: %s", hatstr);
+		break;
+	default:
+		return luaL_error(L, "Invalid joystick input type: %s", jinputtypestr);
+	}
+
+	bool success = false;
+	try
+	{
+		success = instance->setGamepadMapping(guid, gpinput, jinput);
+	}
+	catch (love::Exception &e)
+	{
+		return luaL_error(L, "%s", e.what());
+	}
+	luax_pushboolean(L, success);
+	return 1;
+}
+
+int w_getGamepadMapping(lua_State *L)
+{
+	std::string guid;
+
+	// Accept either a GUID string or a Joystick object. This way we can re-use
+	// the function for Joystick:getGamepadMapping.
+	if (lua_type(L, 1) == LUA_TSTRING)
+		guid = luax_checkstring(L, 1);
+	else
+	{
+		love::joystick::Joystick *stick = luax_checkjoystick(L, 1);
+		guid = stick->getGUID();
+	}
+
+	const char *gpbindstr = luaL_checkstring(L, 2);
+	Joystick::GamepadInput gpinput;
+
+	if (love::joystick::Joystick::getConstant(gpbindstr, gpinput.axis))
+		gpinput.type = Joystick::INPUT_TYPE_AXIS;
+	else if (love::joystick::Joystick::getConstant(gpbindstr, gpinput.button))
+		gpinput.type = Joystick::INPUT_TYPE_BUTTON;
+	else
+		return luaL_error(L, "Invalid gamepad axis/button: %s", gpbindstr);
+
+	Joystick::JoystickInput jinput;
+	jinput.type = Joystick::INPUT_TYPE_MAX_ENUM;
+
+	try
+	{
+		jinput = instance->getGamepadMapping(guid, gpinput);
+	}
+	catch (love::Exception &e)
+	{
+		return luaL_error(L, "%s", e.what());
+	}
+
+	if (jinput.type == Joystick::INPUT_TYPE_MAX_ENUM)
+		return 0;
+
+	const char *inputtypestr;
+	if (!love::joystick::Joystick::getConstant(jinput.type, inputtypestr))
+		return luaL_error(L, "Unknown joystick input type.");
+
+	lua_pushstring(L, inputtypestr);
+
+	const char *hatstr;
+	switch (jinput.type)
+	{
+	case Joystick::INPUT_TYPE_AXIS:
+		lua_pushinteger(L, jinput.axis + 1);
+		return 2;
+	case Joystick::INPUT_TYPE_BUTTON:
+		lua_pushinteger(L, jinput.button + 1);
+		return 2;
+	case Joystick::INPUT_TYPE_HAT:
+		lua_pushinteger(L, jinput.hat.index + 1);
+		if (love::joystick::Joystick::getConstant(jinput.hat.value, hatstr))
+		{
+			lua_pushstring(L, hatstr);
+			return 3;
+		}
+		else
+			return luaL_error(L, "Unknown joystick hat.");
+	default:
+		break; // ?
+	}
+
+	return 1;
+}
+
+// List of functions to wrap.
+static const luaL_Reg functions[] =
+{
+	{ "getJoystick", w_getJoystick },
+	{ "getIndex", w_getIndex },
+	{ "getJoystickCount", w_getJoystickCount },
+	{ "isGamepad", w_isGamepad },
+	{ "setGamepadMapping", w_setGamepadMapping },
+	{ "getGamepadMapping", w_getGamepadMapping },
+	{ 0, 0 }
+};
+
+static const lua_CFunction types[] =
+{
+	luaopen_joystick,
+	0,
+};
+
+extern "C" int luaopen_love_joystick(lua_State *L)
+{
+	if (instance == 0)
+	{
+		try
+		{
+			instance = new JoystickModule();
+		}
+		catch (Exception &e)
+		{
+			return luaL_error(L, e.what());
+		}
+	}
+	else
+		instance->retain();
+
+
+	WrappedModule w;
+	w.module = instance;
+	w.name = "joystick";
+	w.flags = MODULE_T;
+	w.functions = functions;
+	w.types = types;
+
+	return luax_register_module(L, w);
+}
+
+} // sdl
+} // joystick
+} // love

+ 47 - 0
src/modules/joystick/sdl/wrap_JoystickModule.h

@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2006-2013 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#ifndef LOVE_JOYSTICK_SDL_WRAP_JOYSTICK_MODULE_H
+#define LOVE_JOYSTICK_SDL_WRAP_JOYSTICK_MODULE_H
+
+// LOVE
+#include "common/config.h"
+#include "JoystickModule.h"
+
+namespace love
+{
+namespace joystick
+{
+namespace sdl
+{
+
+int w_getJoystick(lua_State *L);
+int w_getIndex(lua_State *L);
+int w_getJoystickCount(lua_State *L);
+int w_isGamepad(lua_State *L);
+int w_setGamepadMapping(lua_State *L);
+int w_getGamepadMapping(lua_State *L);
+extern "C" LOVE_EXPORT int luaopen_love_joystick(lua_State *L);
+
+} // sdl
+} // joystick
+} // love
+
+#endif // LOVE_JOYSTICK_SDL_WRAP_JOYSTICK_MODULE_H

+ 100 - 51
src/modules/keyboard/Keyboard.cpp

@@ -39,12 +39,10 @@ bool Keyboard::getConstant(Keyboard::Key in, const char  *&out)
 
 StringMap<Keyboard::Key, Keyboard::KEY_MAX_ENUM>::Entry Keyboard::keyEntries[] =
 {
-	{"backspace", Keyboard::KEY_BACKSPACE},
-	{"tab", Keyboard::KEY_TAB},
-	{"clear", Keyboard::KEY_CLEAR},
 	{"return", Keyboard::KEY_RETURN},
-	{"pause", Keyboard::KEY_PAUSE},
 	{"escape", Keyboard::KEY_ESCAPE},
+	{"backspace", Keyboard::KEY_BACKSPACE},
+	{"tab", Keyboard::KEY_TAB},
 	{" ", Keyboard::KEY_SPACE},
 	{"!", Keyboard::KEY_EXCLAIM},
 	{"\"", Keyboard::KEY_QUOTEDBL},
@@ -110,35 +108,8 @@ StringMap<Keyboard::Key, Keyboard::KEY_MAX_ENUM>::Entry Keyboard::keyEntries[] =
 	{"x", Keyboard::KEY_X},
 	{"y", Keyboard::KEY_Y},
 	{"z", Keyboard::KEY_Z},
-	{"delete", Keyboard::KEY_DELETE},
 
-	{"kp0", Keyboard::KEY_KP0},
-	{"kp1", Keyboard::KEY_KP1},
-	{"kp2", Keyboard::KEY_KP2},
-	{"kp3", Keyboard::KEY_KP3},
-	{"kp4", Keyboard::KEY_KP4},
-	{"kp5", Keyboard::KEY_KP5},
-	{"kp6", Keyboard::KEY_KP6},
-	{"kp7", Keyboard::KEY_KP7},
-	{"kp8", Keyboard::KEY_KP8},
-	{"kp9", Keyboard::KEY_KP9},
-	{"kp.", Keyboard::KEY_KP_PERIOD},
-	{"kp/", Keyboard::KEY_KP_DIVIDE},
-	{"kp*", Keyboard::KEY_KP_MULTIPLY},
-	{"kp-", Keyboard::KEY_KP_MINUS},
-	{"kp+", Keyboard::KEY_KP_PLUS},
-	{"kpenter", Keyboard::KEY_KP_ENTER},
-	{"kp=", Keyboard::KEY_KP_EQUALS},
-
-	{"up", Keyboard::KEY_UP},
-	{"down", Keyboard::KEY_DOWN},
-	{"right", Keyboard::KEY_RIGHT},
-	{"left", Keyboard::KEY_LEFT},
-	{"insert", Keyboard::KEY_INSERT},
-	{"home", Keyboard::KEY_HOME},
-	{"end", Keyboard::KEY_END},
-	{"pageup", Keyboard::KEY_PAGEUP},
-	{"pagedown", Keyboard::KEY_PAGEDOWN},
+	{"capslock", Keyboard::KEY_CAPSLOCK},
 
 	{"f1", Keyboard::KEY_F1},
 	{"f2", Keyboard::KEY_F2},
@@ -152,34 +123,112 @@ StringMap<Keyboard::Key, Keyboard::KEY_MAX_ENUM>::Entry Keyboard::keyEntries[] =
 	{"f10", Keyboard::KEY_F10},
 	{"f11", Keyboard::KEY_F11},
 	{"f12", Keyboard::KEY_F12},
+
+	{"printscreen", Keyboard::KEY_PRINTSCREEN},
+	{"scrolllock", Keyboard::KEY_SCROLLLOCK},
+	{"pause", Keyboard::KEY_PAUSE},
+	{"insert", Keyboard::KEY_INSERT},
+	{"home", Keyboard::KEY_HOME},
+	{"pageup", Keyboard::KEY_PAGEUP},
+	{"delete", Keyboard::KEY_DELETE},
+	{"end", Keyboard::KEY_END},
+	{"pagedown", Keyboard::KEY_PAGEDOWN},
+	{"right", Keyboard::KEY_RIGHT},
+	{"left", Keyboard::KEY_LEFT},
+	{"down", Keyboard::KEY_DOWN},
+	{"up", Keyboard::KEY_UP},
+
+	{"numlock", Keyboard::KEY_NUMLOCKCLEAR},
+	{"kp/", Keyboard::KEY_KP_DIVIDE},
+	{"kp*", Keyboard::KEY_KP_MULTIPLY},
+	{"kp-", Keyboard::KEY_KP_MINUS},
+	{"kp+", Keyboard::KEY_KP_PLUS},
+	{"kpenter", Keyboard::KEY_KP_ENTER},
+	{"kp0", Keyboard::KEY_KP_0},
+	{"kp1", Keyboard::KEY_KP_1},
+	{"kp2", Keyboard::KEY_KP_2},
+	{"kp3", Keyboard::KEY_KP_3},
+	{"kp4", Keyboard::KEY_KP_4},
+	{"kp5", Keyboard::KEY_KP_5},
+	{"kp6", Keyboard::KEY_KP_6},
+	{"kp7", Keyboard::KEY_KP_7},
+	{"kp8", Keyboard::KEY_KP_8},
+	{"kp9", Keyboard::KEY_KP_9},
+	{"kp.", Keyboard::KEY_KP_PERIOD},
+	{"kp,", Keyboard::KEY_KP_COMMA},
+	{"kp=", Keyboard::KEY_KP_EQUALS},
+
+	{"application", Keyboard::KEY_APPLICATION},
+	{"power", Keyboard::KEY_POWER},
 	{"f13", Keyboard::KEY_F13},
 	{"f14", Keyboard::KEY_F14},
 	{"f15", Keyboard::KEY_F15},
+	{"f16", Keyboard::KEY_F16},
+	{"f17", Keyboard::KEY_F17},
+	{"f18", Keyboard::KEY_F18},
+	{"f19", Keyboard::KEY_F19},
+	{"f20", Keyboard::KEY_F20},
+	{"f21", Keyboard::KEY_F21},
+	{"f22", Keyboard::KEY_F22},
+	{"f23", Keyboard::KEY_F23},
+	{"f24", Keyboard::KEY_F24},
+	{"execute", Keyboard::KEY_EXECUTE},
+	{"help", Keyboard::KEY_HELP},
+	{"menu", Keyboard::KEY_MENU},
+	{"select", Keyboard::KEY_SELECT},
+	{"stop", Keyboard::KEY_STOP},
+	{"again", Keyboard::KEY_AGAIN},
+	{"undo", Keyboard::KEY_UNDO},
+	{"cut", Keyboard::KEY_CUT},
+	{"copy", Keyboard::KEY_COPY},
+	{"paste", Keyboard::KEY_PASTE},
+	{"find", Keyboard::KEY_FIND},
+	{"mute", Keyboard::KEY_MUTE},
+	{"volumeup", Keyboard::KEY_VOLUMEUP},
+	{"volumedown", Keyboard::KEY_VOLUMEDOWN},
 
-	{"numlock", Keyboard::KEY_NUMLOCK},
-	{"capslock", Keyboard::KEY_CAPSLOCK},
-	{"scrollock", Keyboard::KEY_SCROLLOCK},
-	{"rshift", Keyboard::KEY_RSHIFT},
+	{"alterase", Keyboard::KEY_ALTERASE},
+	{"sysreq", Keyboard::KEY_SYSREQ},
+	{"cancel", Keyboard::KEY_CANCEL},
+	{"clear", Keyboard::KEY_CLEAR},
+	{"prior", Keyboard::KEY_PRIOR},
+	{"return2", Keyboard::KEY_RETURN2},
+	{"separator", Keyboard::KEY_SEPARATOR},
+	{"out", Keyboard::KEY_OUT},
+	{"oper", Keyboard::KEY_OPER},
+	{"clearagain", Keyboard::KEY_CLEARAGAIN},
+
+	{"thsousandsseparator", Keyboard::KEY_THOUSANDSSEPARATOR},
+	{"decimalseparator", Keyboard::KEY_DECIMALSEPARATOR},
+	{"currencyunit", Keyboard::KEY_CURRENCYUNIT},
+	{"currencysubunit", Keyboard::KEY_CURRENCYSUBUNIT},
+
+	{"lctrl", Keyboard::KEY_LCTRL},
 	{"lshift", Keyboard::KEY_LSHIFT},
+	{"lalt", Keyboard::KEY_LALT},
+	{"lgui", Keyboard::KEY_LGUI},
 	{"rctrl", Keyboard::KEY_RCTRL},
-	{"lctrl", Keyboard::KEY_LCTRL},
+	{"rshift", Keyboard::KEY_RSHIFT},
 	{"ralt", Keyboard::KEY_RALT},
-	{"lalt", Keyboard::KEY_LALT},
-	{"rmeta", Keyboard::KEY_RMETA},
-	{"lmeta", Keyboard::KEY_LMETA},
-	{"lsuper", Keyboard::KEY_LSUPER},
-	{"rsuper", Keyboard::KEY_RSUPER},
+	{"rgui", Keyboard::KEY_RGUI},
+
 	{"mode", Keyboard::KEY_MODE},
-	{"compose", Keyboard::KEY_COMPOSE},
 
-	{"help", Keyboard::KEY_HELP},
-	{"print", Keyboard::KEY_PRINT},
-	{"sysreq", Keyboard::KEY_SYSREQ},
-	{"break", Keyboard::KEY_BREAK},
-	{"menu", Keyboard::KEY_MENU},
-	{"power", Keyboard::KEY_POWER},
-	{"euro", Keyboard::KEY_EURO},
-	{"undo", Keyboard::KEY_UNDO},
+	{"audionext", Keyboard::KEY_AUDIONEXT},
+	{"audioprev", Keyboard::KEY_AUDIOPREV},
+	{"audiostop", Keyboard::KEY_AUDIOSTOP},
+	{"audioplay", Keyboard::KEY_AUDIOPLAY},
+	{"audiomute", Keyboard::KEY_AUDIOMUTE},
+	{"mediaselect", Keyboard::KEY_MEDIASELECT},
+
+	{"brightnessdown", Keyboard::KEY_BRIGHTNESSDOWN},
+	{"brightnessup", Keyboard::KEY_BRIGHTNESSUP},
+	{"displayswitch", Keyboard::KEY_DISPLAYSWITCH},
+	{"kbdillumtoggle", Keyboard::KEY_KBDILLUMTOGGLE},
+	{"kbdillumdown", Keyboard::KEY_KBDILLUMDOWN},
+	{"kbdillumup", Keyboard::KEY_KBDILLUMUP},
+	{"eject", Keyboard::KEY_EJECT},
+	{"sleep", Keyboard::KEY_SLEEP},
 };
 
 StringMap<Keyboard::Key, Keyboard::KEY_MAX_ENUM> Keyboard::keys(Keyboard::keyEntries, sizeof(Keyboard::keyEntries));

+ 103 - 72
src/modules/keyboard/Keyboard.h

@@ -37,16 +37,16 @@ public:
 	enum Key
 	{
 		KEY_UNKNOWN,
-		KEY_BACKSPACE,
-		KEY_TAB,
-		KEY_CLEAR,
+
 		KEY_RETURN,
-		KEY_PAUSE,
 		KEY_ESCAPE,
+		KEY_BACKSPACE,
+		KEY_TAB,
 		KEY_SPACE,
 		KEY_EXCLAIM,
 		KEY_QUOTEDBL,
 		KEY_HASH,
+		KEY_PERCENT,
 		KEY_DOLLAR,
 		KEY_AMPERSAND,
 		KEY_QUOTE,
@@ -108,35 +108,8 @@ public:
 		KEY_X,
 		KEY_Y,
 		KEY_Z,
-		KEY_DELETE,
-
-		KEY_KP0,
-		KEY_KP1,
-		KEY_KP2,
-		KEY_KP3,
-		KEY_KP4,
-		KEY_KP5,
-		KEY_KP6,
-		KEY_KP7,
-		KEY_KP8,
-		KEY_KP9,
-		KEY_KP_PERIOD,
-		KEY_KP_DIVIDE,
-		KEY_KP_MULTIPLY,
-		KEY_KP_MINUS,
-		KEY_KP_PLUS,
-		KEY_KP_ENTER,
-		KEY_KP_EQUALS,
 
-		KEY_UP,
-		KEY_DOWN,
-		KEY_RIGHT,
-		KEY_LEFT,
-		KEY_INSERT,
-		KEY_HOME,
-		KEY_END,
-		KEY_PAGEUP,
-		KEY_PAGEDOWN,
+		KEY_CAPSLOCK,
 
 		KEY_F1,
 		KEY_F2,
@@ -150,39 +123,116 @@ public:
 		KEY_F10,
 		KEY_F11,
 		KEY_F12,
+
+		KEY_PRINTSCREEN,
+		KEY_SCROLLLOCK,
+		KEY_PAUSE,
+		KEY_INSERT,
+		KEY_HOME,
+		KEY_PAGEUP,
+		KEY_DELETE,
+		KEY_END,
+		KEY_PAGEDOWN,
+		KEY_RIGHT,
+		KEY_LEFT,
+		KEY_DOWN,
+		KEY_UP,
+
+		KEY_NUMLOCKCLEAR,
+		KEY_KP_DIVIDE,
+		KEY_KP_MULTIPLY,
+		KEY_KP_MINUS,
+		KEY_KP_PLUS,
+		KEY_KP_ENTER,
+		KEY_KP_1,
+		KEY_KP_2,
+		KEY_KP_3,
+		KEY_KP_4,
+		KEY_KP_5,
+		KEY_KP_6,
+		KEY_KP_7,
+		KEY_KP_8,
+		KEY_KP_9,
+		KEY_KP_0,
+		KEY_KP_PERIOD,
+		KEY_KP_COMMA,
+		KEY_KP_EQUALS,
+
+		KEY_APPLICATION,
+		KEY_POWER,
 		KEY_F13,
 		KEY_F14,
 		KEY_F15,
+		KEY_F16,
+		KEY_F17,
+		KEY_F18,
+		KEY_F19,
+		KEY_F20,
+		KEY_F21,
+		KEY_F22,
+		KEY_F23,
+		KEY_F24,
+		KEY_EXECUTE,
+		KEY_HELP,
+		KEY_MENU,
+		KEY_SELECT,
+		KEY_STOP,
+		KEY_AGAIN,
+		KEY_UNDO,
+		KEY_CUT,
+		KEY_COPY,
+		KEY_PASTE,
+		KEY_FIND,
+		KEY_MUTE,
+		KEY_VOLUMEUP,
+		KEY_VOLUMEDOWN,
 
-		KEY_NUMLOCK,
-		KEY_CAPSLOCK,
-		KEY_SCROLLOCK,
-		KEY_RSHIFT,
+		KEY_ALTERASE,
+		KEY_SYSREQ,
+		KEY_CANCEL,
+		KEY_CLEAR,
+		KEY_PRIOR,
+		KEY_RETURN2,
+		KEY_SEPARATOR,
+		KEY_OUT,
+		KEY_OPER,
+		KEY_CLEARAGAIN,
+
+		KEY_THOUSANDSSEPARATOR,
+		KEY_DECIMALSEPARATOR,
+		KEY_CURRENCYUNIT,
+		KEY_CURRENCYSUBUNIT,
+
+		KEY_LCTRL,
 		KEY_LSHIFT,
+		KEY_LALT,
+		KEY_LGUI,
 		KEY_RCTRL,
-		KEY_LCTRL,
+		KEY_RSHIFT,
 		KEY_RALT,
-		KEY_LALT,
-		KEY_RMETA,
-		KEY_LMETA,
-		KEY_LSUPER,
-		KEY_RSUPER,
+		KEY_RGUI,
+
 		KEY_MODE,
-		KEY_COMPOSE,
 
-		KEY_HELP,
-		KEY_PRINT,
-		KEY_SYSREQ,
-		KEY_BREAK,
-		KEY_MENU,
-		KEY_POWER,
-		KEY_EURO,
-		KEY_UNDO,
+		KEY_AUDIONEXT,
+		KEY_AUDIOPREV,
+		KEY_AUDIOSTOP,
+		KEY_AUDIOPLAY,
+		KEY_AUDIOMUTE,
+		KEY_MEDIASELECT,
+
+		KEY_BRIGHTNESSDOWN,
+		KEY_BRIGHTNESSUP,
+		KEY_DISPLAYSWITCH,
+		KEY_KBDILLUMTOGGLE,
+		KEY_KBDILLUMDOWN,
+		KEY_KBDILLUMUP,
+		KEY_EJECT,
+		KEY_SLEEP,
+
 		KEY_MAX_ENUM = 512
 	};
 
-	static const int DEFAULT = -1;
-
 	virtual ~Keyboard() {}
 
 	/**
@@ -192,25 +242,6 @@ public:
 	 **/
 	virtual bool isDown(Key *keylist) const = 0;
 
-	/**
-	 * Enables key repeating.
-	 * @param delay The amount of delay before repeating the key (in milliseconds)
-	 * @param interval Specifies the amount of time between repeats (in milliseconds)
-	 **/
-	virtual void setKeyRepeat(int delay, int interval) const = 0;
-
-	/**
-	 * Gets the specified delay for the key repeat.
-	 * @return int
-	 **/
-	virtual int getKeyRepeatDelay() const = 0;
-
-	/**
-	 * Gets the specified interval for the key repeat.
-	 * @return int
-	 **/
-	virtual int getKeyRepeatInterval() const = 0;
-
 	static bool getConstant(const char *in, Key &out);
 	static bool getConstant(Key in, const char  *&out);
 

+ 199 - 166
src/modules/keyboard/sdl/Keyboard.cpp

@@ -36,187 +36,220 @@ const char *Keyboard::getName() const
 
 bool Keyboard::isDown(Key *keylist) const
 {
-	SDLKey k;
-	Uint8 *keystate = SDL_GetKeyState(0);
+	const Uint8 *keystate = SDL_GetKeyboardState(0);
+	std::map<Key, SDL_Keycode>::const_iterator it;
 
 	for (Key key = *keylist; key != KEY_MAX_ENUM; key = *(++keylist))
 	{
-		if (keys.find(key, k) && keystate[(unsigned)k] == 1)
+		it = keys.find(key);
+		if (it != keys.end() && keystate[SDL_GetScancodeFromKey(it->second)])
 			return true;
 	}
 
 	return false;
 }
 
-void Keyboard::setKeyRepeat(int delay, int interval) const
+std::map<Keyboard::Key, SDL_Keycode> Keyboard::createKeyMap()
 {
-	delay = (delay == DEFAULT) ? SDL_DEFAULT_REPEAT_DELAY : delay;
-	interval = (interval == DEFAULT) ? SDL_DEFAULT_REPEAT_INTERVAL : interval;
+	std::map<Keyboard::Key, SDL_Keycode> k;
 
-	SDL_EnableKeyRepeat(delay, interval);
-}
+	k[Keyboard::KEY_UNKNOWN] = SDLK_UNKNOWN;
 
-int Keyboard::getKeyRepeatDelay() const
-{
-	int delay, interval;
-	SDL_GetKeyRepeat(&delay, &interval);
-	return delay;
-}
+	k[Keyboard::KEY_RETURN] = SDLK_RETURN;
+	k[Keyboard::KEY_ESCAPE] = SDLK_ESCAPE;
+	k[Keyboard::KEY_BACKSPACE] = SDLK_BACKSPACE;
+	k[Keyboard::KEY_TAB] = SDLK_TAB;
+	k[Keyboard::KEY_SPACE] = SDLK_SPACE;
+	k[Keyboard::KEY_EXCLAIM] = SDLK_EXCLAIM;
+	k[Keyboard::KEY_QUOTEDBL] = SDLK_QUOTEDBL;
+	k[Keyboard::KEY_HASH] = SDLK_HASH;
+	k[Keyboard::KEY_DOLLAR] = SDLK_DOLLAR;
+	k[Keyboard::KEY_AMPERSAND] = SDLK_AMPERSAND;
+	k[Keyboard::KEY_QUOTE] = SDLK_QUOTE;
+	k[Keyboard::KEY_LEFTPAREN] = SDLK_LEFTPAREN;
+	k[Keyboard::KEY_RIGHTPAREN] = SDLK_RIGHTPAREN;
+	k[Keyboard::KEY_ASTERISK] = SDLK_ASTERISK;
+	k[Keyboard::KEY_PLUS] = SDLK_PLUS;
+	k[Keyboard::KEY_COMMA] = SDLK_COMMA;
+	k[Keyboard::KEY_MINUS] = SDLK_MINUS;
+	k[Keyboard::KEY_PERIOD] = SDLK_PERIOD;
+	k[Keyboard::KEY_SLASH] = SDLK_SLASH;
+	k[Keyboard::KEY_0] = SDLK_0;
+	k[Keyboard::KEY_1] = SDLK_1;
+	k[Keyboard::KEY_2] = SDLK_2;
+	k[Keyboard::KEY_3] = SDLK_3;
+	k[Keyboard::KEY_4] = SDLK_4;
+	k[Keyboard::KEY_5] = SDLK_5;
+	k[Keyboard::KEY_6] = SDLK_6;
+	k[Keyboard::KEY_7] = SDLK_7;
+	k[Keyboard::KEY_8] = SDLK_8;
+	k[Keyboard::KEY_9] = SDLK_9;
+	k[Keyboard::KEY_COLON] = SDLK_COLON;
+	k[Keyboard::KEY_SEMICOLON] = SDLK_SEMICOLON;
+	k[Keyboard::KEY_LESS] = SDLK_LESS;
+	k[Keyboard::KEY_EQUALS] = SDLK_EQUALS;
+	k[Keyboard::KEY_GREATER] = SDLK_GREATER;
+	k[Keyboard::KEY_QUESTION] = SDLK_QUESTION;
+	k[Keyboard::KEY_AT] = SDLK_AT;
 
-int Keyboard::getKeyRepeatInterval() const
-{
-	int delay, interval;
-	SDL_GetKeyRepeat(&delay, &interval);
-	return interval;
-}
+	k[Keyboard::KEY_LEFTBRACKET] = SDLK_LEFTBRACKET;
+	k[Keyboard::KEY_BACKSLASH] = SDLK_BACKSLASH;
+	k[Keyboard::KEY_RIGHTBRACKET] = SDLK_RIGHTBRACKET;
+	k[Keyboard::KEY_CARET] = SDLK_CARET;
+	k[Keyboard::KEY_UNDERSCORE] = SDLK_UNDERSCORE;
+	k[Keyboard::KEY_BACKQUOTE] = SDLK_BACKQUOTE;
+	k[Keyboard::KEY_A] = SDLK_a;
+	k[Keyboard::KEY_B] = SDLK_b;
+	k[Keyboard::KEY_C] = SDLK_c;
+	k[Keyboard::KEY_D] = SDLK_d;
+	k[Keyboard::KEY_E] = SDLK_e;
+	k[Keyboard::KEY_F] = SDLK_f;
+	k[Keyboard::KEY_G] = SDLK_g;
+	k[Keyboard::KEY_H] = SDLK_h;
+	k[Keyboard::KEY_I] = SDLK_i;
+	k[Keyboard::KEY_J] = SDLK_j;
+	k[Keyboard::KEY_K] = SDLK_k;
+	k[Keyboard::KEY_L] = SDLK_l;
+	k[Keyboard::KEY_M] = SDLK_m;
+	k[Keyboard::KEY_N] = SDLK_n;
+	k[Keyboard::KEY_O] = SDLK_o;
+	k[Keyboard::KEY_P] = SDLK_p;
+	k[Keyboard::KEY_Q] = SDLK_q;
+	k[Keyboard::KEY_R] = SDLK_r;
+	k[Keyboard::KEY_S] = SDLK_s;
+	k[Keyboard::KEY_T] = SDLK_t;
+	k[Keyboard::KEY_U] = SDLK_u;
+	k[Keyboard::KEY_V] = SDLK_v;
+	k[Keyboard::KEY_W] = SDLK_w;
+	k[Keyboard::KEY_X] = SDLK_x;
+	k[Keyboard::KEY_Y] = SDLK_y;
+	k[Keyboard::KEY_Z] = SDLK_z;
 
-EnumMap<Keyboard::Key, SDLKey, Keyboard::KEY_MAX_ENUM>::Entry Keyboard::keyEntries[] =
-{
-	{ Keyboard::KEY_BACKSPACE, SDLK_BACKSPACE},
-	{ Keyboard::KEY_TAB, SDLK_TAB},
-	{ Keyboard::KEY_CLEAR, SDLK_CLEAR},
-	{ Keyboard::KEY_RETURN, SDLK_RETURN},
-	{ Keyboard::KEY_PAUSE, SDLK_PAUSE},
-	{ Keyboard::KEY_ESCAPE, SDLK_ESCAPE },
-	{ Keyboard::KEY_SPACE, SDLK_SPACE },
-	{ Keyboard::KEY_EXCLAIM, SDLK_EXCLAIM },
-	{ Keyboard::KEY_QUOTEDBL, SDLK_QUOTEDBL },
-	{ Keyboard::KEY_HASH, SDLK_HASH },
-	{ Keyboard::KEY_DOLLAR, SDLK_DOLLAR },
-	{ Keyboard::KEY_AMPERSAND, SDLK_AMPERSAND },
-	{ Keyboard::KEY_QUOTE, SDLK_QUOTE },
-	{ Keyboard::KEY_LEFTPAREN, SDLK_LEFTPAREN },
-	{ Keyboard::KEY_RIGHTPAREN, SDLK_RIGHTPAREN },
-	{ Keyboard::KEY_ASTERISK, SDLK_ASTERISK },
-	{ Keyboard::KEY_PLUS, SDLK_PLUS },
-	{ Keyboard::KEY_COMMA, SDLK_COMMA },
-	{ Keyboard::KEY_MINUS, SDLK_MINUS },
-	{ Keyboard::KEY_PERIOD, SDLK_PERIOD },
-	{ Keyboard::KEY_SLASH, SDLK_SLASH },
-	{ Keyboard::KEY_0, SDLK_0 },
-	{ Keyboard::KEY_1, SDLK_1 },
-	{ Keyboard::KEY_2, SDLK_2 },
-	{ Keyboard::KEY_3, SDLK_3 },
-	{ Keyboard::KEY_4, SDLK_4 },
-	{ Keyboard::KEY_5, SDLK_5 },
-	{ Keyboard::KEY_6, SDLK_6 },
-	{ Keyboard::KEY_7, SDLK_7 },
-	{ Keyboard::KEY_8, SDLK_8 },
-	{ Keyboard::KEY_9, SDLK_9 },
-	{ Keyboard::KEY_COLON, SDLK_COLON },
-	{ Keyboard::KEY_SEMICOLON, SDLK_SEMICOLON },
-	{ Keyboard::KEY_LESS, SDLK_LESS },
-	{ Keyboard::KEY_EQUALS, SDLK_EQUALS },
-	{ Keyboard::KEY_GREATER, SDLK_GREATER },
-	{ Keyboard::KEY_QUESTION, SDLK_QUESTION },
-	{ Keyboard::KEY_AT, SDLK_AT },
-
-	{ Keyboard::KEY_LEFTBRACKET, SDLK_LEFTBRACKET },
-	{ Keyboard::KEY_BACKSLASH, SDLK_BACKSLASH },
-	{ Keyboard::KEY_RIGHTBRACKET, SDLK_RIGHTBRACKET },
-	{ Keyboard::KEY_CARET, SDLK_CARET },
-	{ Keyboard::KEY_UNDERSCORE, SDLK_UNDERSCORE },
-	{ Keyboard::KEY_BACKQUOTE, SDLK_BACKQUOTE },
-	{ Keyboard::KEY_A, SDLK_a },
-	{ Keyboard::KEY_B, SDLK_b },
-	{ Keyboard::KEY_C, SDLK_c },
-	{ Keyboard::KEY_D, SDLK_d },
-	{ Keyboard::KEY_E, SDLK_e },
-	{ Keyboard::KEY_F, SDLK_f },
-	{ Keyboard::KEY_G, SDLK_g },
-	{ Keyboard::KEY_H, SDLK_h },
-	{ Keyboard::KEY_I, SDLK_i },
-	{ Keyboard::KEY_J, SDLK_j },
-	{ Keyboard::KEY_K, SDLK_k },
-	{ Keyboard::KEY_L, SDLK_l },
-	{ Keyboard::KEY_M, SDLK_m },
-	{ Keyboard::KEY_N, SDLK_n },
-	{ Keyboard::KEY_O, SDLK_o },
-	{ Keyboard::KEY_P, SDLK_p },
-	{ Keyboard::KEY_Q, SDLK_q },
-	{ Keyboard::KEY_R, SDLK_r },
-	{ Keyboard::KEY_S, SDLK_s },
-	{ Keyboard::KEY_T, SDLK_t },
-	{ Keyboard::KEY_U, SDLK_u },
-	{ Keyboard::KEY_V, SDLK_v },
-	{ Keyboard::KEY_W, SDLK_w },
-	{ Keyboard::KEY_X, SDLK_x },
-	{ Keyboard::KEY_Y, SDLK_y },
-	{ Keyboard::KEY_Z, SDLK_z },
-	{ Keyboard::KEY_DELETE, SDLK_DELETE },
-
-	{ Keyboard::KEY_KP0, SDLK_KP0 },
-	{ Keyboard::KEY_KP1, SDLK_KP1 },
-	{ Keyboard::KEY_KP2, SDLK_KP2 },
-	{ Keyboard::KEY_KP3, SDLK_KP3 },
-	{ Keyboard::KEY_KP4, SDLK_KP4 },
-	{ Keyboard::KEY_KP5, SDLK_KP5 },
-	{ Keyboard::KEY_KP6, SDLK_KP6 },
-	{ Keyboard::KEY_KP7, SDLK_KP7 },
-	{ Keyboard::KEY_KP8, SDLK_KP8 },
-	{ Keyboard::KEY_KP9, SDLK_KP9 },
-	{ Keyboard::KEY_KP_PERIOD, SDLK_KP_PERIOD },
-	{ Keyboard::KEY_KP_DIVIDE, SDLK_KP_DIVIDE },
-	{ Keyboard::KEY_KP_MULTIPLY, SDLK_KP_MULTIPLY},
-	{ Keyboard::KEY_KP_MINUS, SDLK_KP_MINUS },
-	{ Keyboard::KEY_KP_PLUS, SDLK_KP_PLUS },
-	{ Keyboard::KEY_KP_ENTER, SDLK_KP_ENTER },
-	{ Keyboard::KEY_KP_EQUALS, SDLK_KP_EQUALS },
-
-	{ Keyboard::KEY_UP, SDLK_UP },
-	{ Keyboard::KEY_DOWN, SDLK_DOWN },
-	{ Keyboard::KEY_RIGHT, SDLK_RIGHT },
-	{ Keyboard::KEY_LEFT, SDLK_LEFT },
-	{ Keyboard::KEY_INSERT, SDLK_INSERT },
-	{ Keyboard::KEY_HOME, SDLK_HOME },
-	{ Keyboard::KEY_END, SDLK_END },
-	{ Keyboard::KEY_PAGEUP, SDLK_PAGEUP },
-	{ Keyboard::KEY_PAGEDOWN, SDLK_PAGEDOWN },
-
-	{ Keyboard::KEY_F1, SDLK_F1 },
-	{ Keyboard::KEY_F2, SDLK_F2 },
-	{ Keyboard::KEY_F3, SDLK_F3 },
-	{ Keyboard::KEY_F4, SDLK_F4 },
-	{ Keyboard::KEY_F5, SDLK_F5 },
-	{ Keyboard::KEY_F6, SDLK_F6 },
-	{ Keyboard::KEY_F7, SDLK_F7 },
-	{ Keyboard::KEY_F8, SDLK_F8 },
-	{ Keyboard::KEY_F9, SDLK_F9 },
-	{ Keyboard::KEY_F10, SDLK_F10 },
-	{ Keyboard::KEY_F11, SDLK_F11 },
-	{ Keyboard::KEY_F12, SDLK_F12 },
-	{ Keyboard::KEY_F13, SDLK_F13 },
-	{ Keyboard::KEY_F14, SDLK_F14 },
-	{ Keyboard::KEY_F15, SDLK_F15 },
-
-	{ Keyboard::KEY_NUMLOCK, SDLK_NUMLOCK },
-	{ Keyboard::KEY_CAPSLOCK, SDLK_CAPSLOCK },
-	{ Keyboard::KEY_SCROLLOCK, SDLK_SCROLLOCK },
-	{ Keyboard::KEY_RSHIFT, SDLK_RSHIFT },
-	{ Keyboard::KEY_LSHIFT, SDLK_LSHIFT },
-	{ Keyboard::KEY_RCTRL, SDLK_RCTRL },
-	{ Keyboard::KEY_LCTRL, SDLK_LCTRL },
-	{ Keyboard::KEY_RALT, SDLK_RALT },
-	{ Keyboard::KEY_LALT, SDLK_LALT },
-	{ Keyboard::KEY_RMETA, SDLK_RMETA },
-	{ Keyboard::KEY_LMETA, SDLK_LMETA },
-	{ Keyboard::KEY_LSUPER, SDLK_LSUPER },
-	{ Keyboard::KEY_RSUPER, SDLK_RSUPER },
-	{ Keyboard::KEY_MODE, SDLK_MODE },
-	{ Keyboard::KEY_COMPOSE, SDLK_COMPOSE },
-
-	{ Keyboard::KEY_HELP, SDLK_HELP },
-	{ Keyboard::KEY_PRINT, SDLK_PRINT },
-	{ Keyboard::KEY_SYSREQ, SDLK_SYSREQ },
-	{ Keyboard::KEY_BREAK, SDLK_BREAK },
-	{ Keyboard::KEY_MENU, SDLK_MENU },
-	{ Keyboard::KEY_POWER, SDLK_POWER },
-	{ Keyboard::KEY_EURO, SDLK_EURO },
-	{ Keyboard::KEY_UNDO, SDLK_UNDO },
-};
-
-EnumMap<Keyboard::Key, SDLKey, Keyboard::KEY_MAX_ENUM> Keyboard::keys(Keyboard::keyEntries, sizeof(Keyboard::keyEntries));
+	k[Keyboard::KEY_CAPSLOCK] = SDLK_CAPSLOCK;
+
+	k[Keyboard::KEY_F1] = SDLK_F1;
+	k[Keyboard::KEY_F2] = SDLK_F2;
+	k[Keyboard::KEY_F3] = SDLK_F3;
+	k[Keyboard::KEY_F4] = SDLK_F4;
+	k[Keyboard::KEY_F5] = SDLK_F5;
+	k[Keyboard::KEY_F6] = SDLK_F6;
+	k[Keyboard::KEY_F7] = SDLK_F7;
+	k[Keyboard::KEY_F8] = SDLK_F8;
+	k[Keyboard::KEY_F9] = SDLK_F9;
+	k[Keyboard::KEY_F10] = SDLK_F10;
+	k[Keyboard::KEY_F11] = SDLK_F11;
+	k[Keyboard::KEY_F12] = SDLK_F12;
+
+	k[Keyboard::KEY_PRINTSCREEN] = SDLK_PRINTSCREEN;
+	k[Keyboard::KEY_SCROLLLOCK] = SDLK_SCROLLLOCK;
+	k[Keyboard::KEY_PAUSE] = SDLK_PAUSE;
+	k[Keyboard::KEY_INSERT] = SDLK_INSERT;
+	k[Keyboard::KEY_HOME] = SDLK_HOME;
+	k[Keyboard::KEY_PAGEUP] = SDLK_PAGEUP;
+	k[Keyboard::KEY_DELETE] = SDLK_DELETE;
+	k[Keyboard::KEY_END] = SDLK_END;
+	k[Keyboard::KEY_PAGEDOWN] = SDLK_PAGEDOWN;
+	k[Keyboard::KEY_RIGHT] = SDLK_RIGHT;
+	k[Keyboard::KEY_LEFT] = SDLK_LEFT;
+	k[Keyboard::KEY_DOWN] = SDLK_DOWN;
+	k[Keyboard::KEY_UP] = SDLK_UP;
+
+	k[Keyboard::KEY_NUMLOCKCLEAR] = SDLK_NUMLOCKCLEAR;
+	k[Keyboard::KEY_KP_DIVIDE] = SDLK_KP_DIVIDE;
+	k[Keyboard::KEY_KP_MULTIPLY] = SDLK_KP_MULTIPLY;
+	k[Keyboard::KEY_KP_MINUS] = SDLK_KP_MINUS;
+	k[Keyboard::KEY_KP_PLUS] = SDLK_KP_PLUS;
+	k[Keyboard::KEY_KP_ENTER] = SDLK_KP_ENTER;
+	k[Keyboard::KEY_KP_0] = SDLK_KP_0;
+	k[Keyboard::KEY_KP_1] = SDLK_KP_1;
+	k[Keyboard::KEY_KP_2] = SDLK_KP_2;
+	k[Keyboard::KEY_KP_3] = SDLK_KP_3;
+	k[Keyboard::KEY_KP_4] = SDLK_KP_4;
+	k[Keyboard::KEY_KP_5] = SDLK_KP_5;
+	k[Keyboard::KEY_KP_6] = SDLK_KP_6;
+	k[Keyboard::KEY_KP_7] = SDLK_KP_7;
+	k[Keyboard::KEY_KP_8] = SDLK_KP_8;
+	k[Keyboard::KEY_KP_9] = SDLK_KP_9;
+	k[Keyboard::KEY_KP_PERIOD] = SDLK_KP_PERIOD;
+	k[Keyboard::KEY_KP_COMMA] = SDLK_KP_COMMA;
+	k[Keyboard::KEY_KP_EQUALS] = SDLK_KP_EQUALS;
+
+	k[Keyboard::KEY_APPLICATION] = SDLK_APPLICATION;
+	k[Keyboard::KEY_POWER] = SDLK_POWER;
+	k[Keyboard::KEY_F13] = SDLK_F13;
+	k[Keyboard::KEY_F14] = SDLK_F14;
+	k[Keyboard::KEY_F15] = SDLK_F15;
+	k[Keyboard::KEY_F16] = SDLK_F16;
+	k[Keyboard::KEY_F17] = SDLK_F17;
+	k[Keyboard::KEY_F18] = SDLK_F18;
+	k[Keyboard::KEY_F19] = SDLK_F19;
+	k[Keyboard::KEY_F20] = SDLK_F20;
+	k[Keyboard::KEY_F21] = SDLK_F21;
+	k[Keyboard::KEY_F22] = SDLK_F22;
+	k[Keyboard::KEY_F23] = SDLK_F23;
+	k[Keyboard::KEY_F24] = SDLK_F24;
+	k[Keyboard::KEY_EXECUTE] = SDLK_EXECUTE;
+	k[Keyboard::KEY_HELP] = SDLK_HELP;
+	k[Keyboard::KEY_MENU] = SDLK_MENU;
+	k[Keyboard::KEY_SELECT] = SDLK_SELECT;
+	k[Keyboard::KEY_STOP] = SDLK_STOP;
+	k[Keyboard::KEY_AGAIN] = SDLK_AGAIN;
+	k[Keyboard::KEY_UNDO] = SDLK_UNDO;
+	k[Keyboard::KEY_CUT] = SDLK_CUT;
+	k[Keyboard::KEY_COPY] = SDLK_COPY;
+	k[Keyboard::KEY_PASTE] = SDLK_PASTE;
+	k[Keyboard::KEY_FIND] = SDLK_FIND;
+	k[Keyboard::KEY_MUTE] = SDLK_MUTE;
+	k[Keyboard::KEY_VOLUMEUP] = SDLK_VOLUMEUP;
+	k[Keyboard::KEY_VOLUMEDOWN] = SDLK_VOLUMEDOWN;
+
+	k[Keyboard::KEY_ALTERASE] = SDLK_ALTERASE;
+	k[Keyboard::KEY_SYSREQ] = SDLK_SYSREQ;
+	k[Keyboard::KEY_CANCEL] = SDLK_CANCEL;
+	k[Keyboard::KEY_CLEAR] = SDLK_CLEAR;
+	k[Keyboard::KEY_PRIOR] = SDLK_PRIOR;
+	k[Keyboard::KEY_RETURN2] = SDLK_RETURN2;
+	k[Keyboard::KEY_SEPARATOR] = SDLK_SEPARATOR;
+	k[Keyboard::KEY_OUT] = SDLK_OUT;
+	k[Keyboard::KEY_OPER] = SDLK_OPER;
+	k[Keyboard::KEY_CLEARAGAIN] = SDLK_CLEARAGAIN;
+
+	k[Keyboard::KEY_THOUSANDSSEPARATOR] = SDLK_THOUSANDSSEPARATOR;
+	k[Keyboard::KEY_DECIMALSEPARATOR] = SDLK_DECIMALSEPARATOR;
+	k[Keyboard::KEY_CURRENCYUNIT] = SDLK_CURRENCYUNIT;
+	k[Keyboard::KEY_CURRENCYSUBUNIT] = SDLK_CURRENCYSUBUNIT;
+
+	k[Keyboard::KEY_LCTRL] = SDLK_LCTRL;
+	k[Keyboard::KEY_LSHIFT] = SDLK_LSHIFT;
+	k[Keyboard::KEY_LALT] = SDLK_LALT;
+	k[Keyboard::KEY_LGUI] = SDLK_LGUI;
+	k[Keyboard::KEY_RCTRL] = SDLK_RCTRL;
+	k[Keyboard::KEY_RSHIFT] = SDLK_RSHIFT;
+	k[Keyboard::KEY_RALT] = SDLK_RALT;
+	k[Keyboard::KEY_RGUI] = SDLK_RGUI;
+
+	k[Keyboard::KEY_MODE] = SDLK_MODE;
+
+	k[Keyboard::KEY_AUDIONEXT] = SDLK_AUDIONEXT;
+	k[Keyboard::KEY_AUDIOPREV] = SDLK_AUDIOPREV;
+	k[Keyboard::KEY_AUDIOSTOP] = SDLK_AUDIOSTOP;
+	k[Keyboard::KEY_AUDIOPLAY] = SDLK_AUDIOPLAY;
+	k[Keyboard::KEY_AUDIOMUTE] = SDLK_AUDIOMUTE;
+	k[Keyboard::KEY_MEDIASELECT] = SDLK_MEDIASELECT;
+
+	k[Keyboard::KEY_BRIGHTNESSDOWN] = SDLK_BRIGHTNESSDOWN;
+	k[Keyboard::KEY_BRIGHTNESSUP] = SDLK_BRIGHTNESSUP;
+	k[Keyboard::KEY_DISPLAYSWITCH] = SDLK_DISPLAYSWITCH;
+	k[Keyboard::KEY_KBDILLUMTOGGLE] = SDLK_KBDILLUMTOGGLE;
+	k[Keyboard::KEY_KBDILLUMDOWN] = SDLK_KBDILLUMDOWN;
+	k[Keyboard::KEY_KBDILLUMUP] = SDLK_KBDILLUMUP;
+	k[Keyboard::KEY_EJECT] = SDLK_EJECT;
+	k[Keyboard::KEY_SLEEP] = SDLK_SLEEP;
+
+	return k;
+}
 
+std::map<Keyboard::Key, SDL_Keycode> Keyboard::keys = Keyboard::createKeyMap();
 
 } // sdl
 } // keyboard

+ 5 - 5
src/modules/keyboard/sdl/Keyboard.h

@@ -28,6 +28,9 @@
 // SDL
 #include <SDL.h>
 
+// STL
+#include <map>
+
 namespace love
 {
 namespace keyboard
@@ -42,14 +45,11 @@ public:
 	// Implements Module.
 	const char *getName() const;
 	bool isDown(Key *keylist) const;
-	void setKeyRepeat(int delay, int interval) const;
-	int getKeyRepeatDelay() const;
-	int getKeyRepeatInterval() const;
 
 private:
 
-	static EnumMap<Key, SDLKey, Keyboard::KEY_MAX_ENUM>::Entry keyEntries[];
-	static EnumMap<Key, SDLKey, Keyboard::KEY_MAX_ENUM> keys;
+	static std::map<Key, SDL_Keycode> createKeyMap();
+	static std::map<Key, SDL_Keycode> keys;
 
 }; // Keyboard
 

+ 0 - 24
src/modules/keyboard/wrap_Keyboard.cpp

@@ -50,34 +50,10 @@ int w_isDown(lua_State *L)
 	return 1;
 }
 
-int w_setKeyRepeat(lua_State *L)
-{
-	if (lua_gettop(L) == 0)
-	{
-		// Disables key repeat.
-		instance->setKeyRepeat(0, 0);
-		return 0;
-	}
-
-	int delay = lua_isnumber(L, 1) ? (int)(lua_tonumber(L, 1) * 1000 + 0.5) : Keyboard::DEFAULT;
-	int interval = lua_isnumber(L, 2) ? (int)(lua_tonumber(L, 2) * 1000 + 0.5) : Keyboard::DEFAULT;
-	instance->setKeyRepeat(delay, interval);
-	return 0;
-}
-
-int w_getKeyRepeat(lua_State *L)
-{
-	lua_pushnumber(L, (lua_Number) instance->getKeyRepeatDelay() * 0.001);
-	lua_pushnumber(L, (lua_Number) instance->getKeyRepeatInterval() * 0.001);
-	return 2;
-}
-
 // List of functions to wrap.
 static const luaL_Reg functions[] =
 {
 	{ "isDown", w_isDown },
-	{ "setKeyRepeat", w_setKeyRepeat },
-	{ "getKeyRepeat", w_getKeyRepeat },
 	{ 0, 0 }
 };
 

+ 0 - 2
src/modules/keyboard/wrap_Keyboard.h

@@ -30,8 +30,6 @@ namespace keyboard
 {
 
 int w_isDown(lua_State *L);
-int w_setKeyRepeat(lua_State *L);
-int w_getKeyRepeat(lua_State *L);
 extern "C" LOVE_EXPORT int luaopen_love_keyboard(lua_State *L);
 
 } // keyboard

+ 2 - 0
src/modules/love/love.cpp

@@ -103,6 +103,7 @@ extern "C"
 	extern int luaopen_love_mouse(lua_State*);
 	extern int luaopen_love_physics(lua_State*);
 	extern int luaopen_love_sound(lua_State*);
+	extern int luaopen_love_system(lua_State*);
 	extern int luaopen_love_timer(lua_State*);
 	extern int luaopen_love_thread(lua_State*);
 	extern int luaopen_love_window(lua_State*);
@@ -122,6 +123,7 @@ static const luaL_Reg modules[] = {
 	{ "love.mouse", luaopen_love_mouse },
 	{ "love.physics", luaopen_love_physics },
 	{ "love.sound", luaopen_love_sound },
+	{ "love.system", luaopen_love_system },
 	{ "love.timer", luaopen_love_timer },
 	{ "love.thread", luaopen_love_thread },
 	{ "love.window", luaopen_love_window },

+ 79 - 0
src/modules/mouse/Cursor.cpp

@@ -0,0 +1,79 @@
+/**
+ * Copyright (c) 2006-2013 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#include "Cursor.h"
+
+namespace love
+{
+namespace mouse
+{
+
+Cursor::~Cursor()
+{
+}
+
+bool Cursor::getConstant(const char *in, SystemCursor &out)
+{
+	return systemCursors.find(in, out);
+}
+
+bool Cursor::getConstant(SystemCursor in, const char *&out)
+{
+	return systemCursors.find(in, out);
+}
+
+bool Cursor::getConstant(const char *in, CursorType &out)
+{
+	return types.find(in, out);
+}
+
+bool Cursor::getConstant(CursorType in, const char *&out)
+{
+	return types.find(in, out);
+}
+
+StringMap<Cursor::SystemCursor, Cursor::CURSOR_MAX_ENUM>::Entry Cursor::systemCursorEntries[] =
+{
+	{"arrow", Cursor::CURSOR_ARROW},
+	{"ibeam", Cursor::CURSOR_IBEAM},
+	{"wait", Cursor::CURSOR_WAIT},
+	{"crosshair", Cursor::CURSOR_CROSSHAIR},
+	{"waitarrow", Cursor::CURSOR_WAITARROW},
+	{"sizenwse", Cursor::CURSOR_SIZENWSE},
+	{"sizenesw", Cursor::CURSOR_SIZENESW},
+	{"sizewe", Cursor::CURSOR_SIZEWE},
+	{"sizens", Cursor::CURSOR_SIZENS},
+	{"sizeall", Cursor::CURSOR_SIZEALL},
+	{"no", Cursor::CURSOR_NO},
+	{"hand", Cursor::CURSOR_HAND},
+};
+
+StringMap<Cursor::SystemCursor, Cursor::CURSOR_MAX_ENUM> Cursor::systemCursors(Cursor::systemCursorEntries, sizeof(Cursor::systemCursorEntries));
+
+StringMap<Cursor::CursorType, Cursor::CURSORTYPE_MAX_ENUM>::Entry Cursor::typeEntries[] =
+{
+	{"system", Cursor::CURSORTYPE_SYSTEM},
+	{"image", Cursor::CURSORTYPE_IMAGE},
+};
+
+StringMap<Cursor::CursorType, Cursor::CURSORTYPE_MAX_ENUM> Cursor::types(Cursor::typeEntries, sizeof(Cursor::typeEntries));
+
+} // mouse
+} // love

+ 100 - 0
src/modules/mouse/Cursor.h

@@ -0,0 +1,100 @@
+/**
+ * Copyright (c) 2006-2013 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#ifndef LOVE_MOUSE_CURSOR_H
+#define LOVE_MOUSE_CURSOR_H
+
+// LOVE
+#include "image/ImageData.h"
+#include "common/Object.h"
+#include "common/StringMap.h"
+
+namespace love
+{
+namespace mouse
+{
+
+class Cursor : public Object
+{
+public:
+
+	// Types of system cursors.
+	enum SystemCursor
+	{
+		CURSOR_ARROW,
+		CURSOR_IBEAM,
+		CURSOR_WAIT,
+		CURSOR_CROSSHAIR,
+		CURSOR_WAITARROW,
+		CURSOR_SIZENWSE,
+		CURSOR_SIZENESW,
+		CURSOR_SIZEWE,
+		CURSOR_SIZENS,
+		CURSOR_SIZEALL,
+		CURSOR_NO,
+		CURSOR_HAND,
+		CURSOR_MAX_ENUM
+	};
+
+	enum CursorType
+	{
+		CURSORTYPE_SYSTEM,
+		CURSORTYPE_IMAGE,
+		CURSORTYPE_MAX_ENUM
+	};
+
+	virtual ~Cursor();
+
+	/**
+	 * Returns a pointer to the implementation-dependent handle of this Cursor.
+	 **/
+	virtual void *getHandle() const = 0;
+
+	/**
+	 * Returns whether this Cursor is system-defined or a custom image.
+	 **/
+	virtual CursorType getType() const = 0;
+
+	/**
+	 * Returns the type type of system cursor used, if this Cursor is using a
+	 * system-defined image.
+	 **/
+	virtual SystemCursor getSystemType() const = 0;
+
+	static bool getConstant(const char *in, SystemCursor &out);
+	static bool getConstant(SystemCursor in, const char *&out);
+
+	static bool getConstant(const char *in, CursorType &out);
+	static bool getConstant(CursorType in, const char *&out);
+
+private:
+
+	static StringMap<SystemCursor, CURSOR_MAX_ENUM>::Entry systemCursorEntries[];
+	static StringMap<SystemCursor, CURSOR_MAX_ENUM> systemCursors;
+
+	static StringMap<CursorType, CURSORTYPE_MAX_ENUM>::Entry typeEntries[];
+	static StringMap<CursorType, CURSORTYPE_MAX_ENUM> types;
+
+};
+
+} // mouse
+} // love
+
+#endif // LOVE_MOUSE_CURSOR_H

+ 10 - 0
src/modules/mouse/Mouse.h

@@ -22,8 +22,10 @@
 #define LOVE_MOUSE_MOUSE_H
 
 // LOVE
+#include "Cursor.h"
 #include "common/Module.h"
 #include "common/StringMap.h"
+#include "image/ImageData.h"
 
 namespace love
 {
@@ -49,6 +51,14 @@ public:
 
 	virtual ~Mouse() {};
 
+	virtual Cursor *newCursor(love::image::ImageData *data, int hotx, int hoty) = 0;
+	virtual Cursor *newCursor(Cursor::SystemCursor cursortype) = 0;
+
+	virtual void setCursor(Cursor *cursor) = 0;
+	virtual void setCursor() = 0;
+
+	virtual Cursor *getCursor() const = 0;
+
 	virtual int getX() const = 0;
 	virtual int getY() const = 0;
 	virtual void getPosition(int &x, int &y) const = 0;

+ 122 - 0
src/modules/mouse/sdl/Cursor.cpp

@@ -0,0 +1,122 @@
+/**
+ * Copyright (c) 2006-2013 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+// LOVE
+#include "Cursor.h"
+#include "common/config.h"
+
+namespace love
+{
+namespace mouse
+{
+namespace sdl
+{
+
+Cursor::Cursor(image::ImageData *data, int hotx, int hoty)
+	: cursor(0)
+	, type(CURSORTYPE_IMAGE)
+	, systemType(CURSOR_MAX_ENUM)
+{
+	Uint32 rmask, gmask, bmask, amask;
+#ifdef LOVE_BIG_ENDIAN
+	rmask = 0xFF000000;
+	gmask = 0x00FF0000;
+	bmask = 0x0000FF00;
+	amask = 0x000000FF;
+#else
+	rmask = 0x000000FF;
+	gmask = 0x0000FF00;
+	bmask = 0x00FF0000;
+	amask = 0xFF000000;
+#endif
+
+	int w = data->getWidth();
+	int h = data->getHeight();
+	int pitch = w * 4;
+
+	SDL_Surface *surface = SDL_CreateRGBSurfaceFrom(data->getData(), w, h, 32, pitch, rmask, gmask, bmask, amask);
+	if (!surface)
+		throw love::Exception("Cannot create cursor: out of memory!");
+
+	cursor = SDL_CreateColorCursor(surface, hotx, hoty);
+	SDL_FreeSurface(surface);
+
+	if (!cursor)
+		throw love::Exception("Cannot create cursor: %s", SDL_GetError());
+}
+
+Cursor::Cursor(mouse::Cursor::SystemCursor cursortype)
+	: cursor(0)
+	, type(CURSORTYPE_SYSTEM)
+	, systemType(cursortype)
+{
+	SDL_SystemCursor sdlcursortype;
+
+	if (systemCursors.find(cursortype, sdlcursortype))
+		cursor = SDL_CreateSystemCursor(sdlcursortype);
+	else
+		throw love::Exception("Cannot create system cursor: invalid type.");
+
+	if (!cursor)
+		throw love::Exception("Cannot create system cursor: %s", SDL_GetError());
+}
+
+Cursor::~Cursor()
+{
+	if (cursor)
+		SDL_FreeCursor(cursor);
+}
+
+void *Cursor::getHandle() const
+{
+	return cursor;
+}
+
+Cursor::CursorType Cursor::getType() const
+{
+	return type;
+}
+
+Cursor::SystemCursor Cursor::getSystemType() const
+{
+	return systemType;
+}
+
+EnumMap<Cursor::SystemCursor, SDL_SystemCursor, Cursor::CURSOR_MAX_ENUM>::Entry Cursor::systemCursorEntries[] =
+{
+	{Cursor::CURSOR_ARROW, SDL_SYSTEM_CURSOR_ARROW},
+	{Cursor::CURSOR_IBEAM, SDL_SYSTEM_CURSOR_IBEAM},
+	{Cursor::CURSOR_WAIT, SDL_SYSTEM_CURSOR_WAIT},
+	{Cursor::CURSOR_CROSSHAIR, SDL_SYSTEM_CURSOR_CROSSHAIR},
+	{Cursor::CURSOR_WAITARROW, SDL_SYSTEM_CURSOR_WAITARROW},
+	{Cursor::CURSOR_SIZENWSE, SDL_SYSTEM_CURSOR_SIZENWSE},
+	{Cursor::CURSOR_SIZENESW, SDL_SYSTEM_CURSOR_SIZENESW},
+	{Cursor::CURSOR_SIZEWE, SDL_SYSTEM_CURSOR_SIZEWE},
+	{Cursor::CURSOR_SIZENS, SDL_SYSTEM_CURSOR_SIZENS},
+	{Cursor::CURSOR_SIZEALL, SDL_SYSTEM_CURSOR_SIZEALL},
+	{Cursor::CURSOR_NO, SDL_SYSTEM_CURSOR_NO},
+	{Cursor::CURSOR_HAND, SDL_SYSTEM_CURSOR_HAND},
+};
+
+EnumMap<Cursor::SystemCursor, SDL_SystemCursor, Cursor::CURSOR_MAX_ENUM> Cursor::systemCursors(Cursor::systemCursorEntries, sizeof(Cursor::systemCursorEntries));
+
+} // sdl
+} // mouse
+} // love

+ 64 - 0
src/modules/mouse/sdl/Cursor.h

@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2006-2013 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#ifndef LOVE_MOUSE_SDL_CURSOR_H
+#define LOVE_MOUSE_SDL_CURSOR_H
+
+// LOVE
+#include "mouse/Cursor.h"
+#include "common/EnumMap.h"
+
+// SDL
+#include <SDL_mouse.h>
+
+namespace love
+{
+namespace mouse
+{
+namespace sdl
+{
+
+class Cursor : public love::mouse::Cursor
+{
+public:
+
+	Cursor(image::ImageData *imageData, int hotx, int hoty);
+	Cursor(SystemCursor cursortype);
+	~Cursor();
+
+	void *getHandle() const;
+	CursorType getType() const;
+	SystemCursor getSystemType() const;
+
+private:
+
+	SDL_Cursor *cursor;
+	CursorType type;
+	SystemCursor systemType;
+
+	static EnumMap<SystemCursor, SDL_SystemCursor, CURSOR_MAX_ENUM>::Entry systemCursorEntries[];
+	static EnumMap<SystemCursor, SDL_SystemCursor, CURSOR_MAX_ENUM> systemCursors;
+};
+
+} // sdl
+} // mouse
+} // love
+
+#endif // LOVE_MOUSE_SDL_CURSOR_H

+ 63 - 5
src/modules/mouse/sdl/Mouse.cpp

@@ -18,10 +18,12 @@
  * 3. This notice may not be removed or altered from any source distribution.
  **/
 
+// LOVE
 #include "Mouse.h"
+#include "window/sdl/Window.h"
 
 // SDL
-#include <SDL.h>
+#include <SDL_mouse.h>
 
 namespace love
 {
@@ -35,6 +37,50 @@ const char *Mouse::getName() const
 	return "love.mouse.sdl";
 }
 
+Mouse::Mouse()
+	: curCursor(0)
+{
+}
+
+Mouse::~Mouse()
+{
+	if (curCursor)
+		setCursor();
+}
+
+love::mouse::Cursor *Mouse::newCursor(love::image::ImageData *data, int hotx, int hoty)
+{
+	return new Cursor(data, hotx, hoty);
+}
+
+love::mouse::Cursor *Mouse::newCursor(love::mouse::Cursor::SystemCursor cursortype)
+{
+	return new Cursor(cursortype);
+}
+
+void Mouse::setCursor(love::mouse::Cursor *cursor)
+{
+	Object::AutoRelease cursorrelease(curCursor);
+
+	curCursor = cursor;
+	curCursor->retain();
+
+	SDL_SetCursor((SDL_Cursor *) cursor->getHandle());
+}
+
+void Mouse::setCursor()
+{
+	Object::AutoRelease cursorrelease(curCursor);
+	curCursor = NULL;
+
+	SDL_SetCursor(SDL_GetDefaultCursor());
+}
+
+love::mouse::Cursor *Mouse::getCursor() const
+{
+	return curCursor;
+}
+
 int Mouse::getX() const
 {
 	int x;
@@ -56,7 +102,13 @@ void Mouse::getPosition(int &x, int &y) const
 
 void Mouse::setPosition(int x, int y)
 {
-	SDL_WarpMouse(x, y);
+	love::window::Window *window = love::window::sdl::Window::getSingleton();
+
+	SDL_Window *handle = NULL;
+	if (window)
+		handle = (SDL_Window *) window->getHandle();
+
+	SDL_WarpMouseInWindow(handle, x, y);
 }
 
 void Mouse::setX(int x)
@@ -91,17 +143,23 @@ bool Mouse::isDown(Button *buttonlist) const
 
 bool Mouse::isVisible() const
 {
-	return (SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE) ? true : false;
+	return SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE;
 }
 
 void Mouse::setGrab(bool grab)
 {
-	SDL_WM_GrabInput(grab ? SDL_GRAB_ON : SDL_GRAB_OFF);
+	love::window::Window *window = love::window::sdl::Window::getSingleton();
+	if (window)
+		window->setMouseGrab(grab);
 }
 
 bool Mouse::isGrabbed() const
 {
-	return (SDL_WM_GrabInput(SDL_GRAB_QUERY) ==  SDL_GRAB_ON ? true : false);
+	love::window::Window *window = love::window::sdl::Window::getSingleton();
+	if (window)
+		return window->isMouseGrabbed();
+	else
+		return false;
 }
 
 } // sdl

+ 17 - 0
src/modules/mouse/sdl/Mouse.h

@@ -23,6 +23,7 @@
 
 // LOVE
 #include "mouse/Mouse.h"
+#include "Cursor.h"
 
 namespace love
 {
@@ -38,6 +39,17 @@ public:
 	// Implements Module.
 	const char *getName() const;
 
+	Mouse();
+	~Mouse();
+
+	love::mouse::Cursor *newCursor(love::image::ImageData *data, int hotx, int hoty);
+	love::mouse::Cursor *newCursor(love::mouse::Cursor::SystemCursor cursortype);
+
+	void setCursor(love::mouse::Cursor *cursor);
+	void setCursor();
+
+	love::mouse::Cursor *getCursor() const;
+
 	int getX() const;
 	int getY() const;
 	void getPosition(int &x, int &y) const;
@@ -49,6 +61,11 @@ public:
 	bool isVisible() const;
 	void setGrab(bool grab);
 	bool isGrabbed() const;
+
+private:
+
+	love::mouse::Cursor *curCursor;
+
 }; // Mouse
 
 } // sdl

+ 66 - 0
src/modules/mouse/wrap_Cursor.cpp

@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2006-2013 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+// LOVE
+#include "wrap_Cursor.h"
+#include "Cursor.h"
+
+namespace love
+{
+namespace mouse
+{
+
+int w_getType(lua_State *L)
+{
+	mouse::Cursor *cursor = luax_checktype<mouse::Cursor>(L, 1, "Cursor", MOUSE_CURSOR_T);
+
+	Cursor::CursorType ctype = cursor->getType();
+	const char *ctypestr;
+
+	if (!mouse::Cursor::getConstant(ctype, ctypestr))
+		return luaL_error(L, "Unknown cursor type.");
+
+	lua_pushstring(L, ctypestr);
+
+	Cursor::SystemCursor systype = cursor->getSystemType();
+	const char *systypestr;
+
+	if (mouse::Cursor::getConstant(systype, systypestr))
+	{
+		lua_pushstring(L, systypestr);
+		return 2;
+	}
+
+	return 1;
+};
+
+static const luaL_Reg functions[] =
+{
+	{ "getType", w_getType },
+	{ 0, 0 },
+};
+
+extern "C" int luaopen_cursor(lua_State *L)
+{
+	return luax_register_type(L, "Cursor", functions);
+}
+
+} // mouse
+} // love

+ 39 - 0
src/modules/mouse/wrap_Cursor.h

@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2006-2013 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#ifndef LOVE_MOUSE_WRAP_CURSOR_H
+#define LOVE_MOUSE_WRAP_CURSOR_H
+
+// LOVE
+#include "common/runtime.h"
+
+namespace love
+{
+namespace mouse
+{
+
+int w_getType(lua_State *L);
+extern "C" int luaopen_cursor(lua_State *L);
+
+} // mouse
+} // love
+
+
+#endif // LOVE_MOUSE_WRAP_CURSOR_H

+ 86 - 3
src/modules/mouse/wrap_Mouse.cpp

@@ -18,12 +18,13 @@
  * 3. This notice may not be removed or altered from any source distribution.
  **/
 
+// LOVE
+#include "wrap_Mouse.h"
+#include "wrap_Cursor.h"
 #include "common/config.h"
 
 #include "sdl/Mouse.h"
 
-#include "wrap_Mouse.h"
-
 namespace love
 {
 namespace mouse
@@ -31,6 +32,78 @@ namespace mouse
 
 static Mouse *instance = 0;
 
+int w_newCursor(lua_State *L)
+{
+	Cursor *cursor = 0;
+
+	if (lua_isstring(L, 1))
+	{
+		// System cursor type.
+		const char *str = luaL_checkstring(L, 1);
+		Cursor::SystemCursor systemCursor;
+
+		if (!Cursor::getConstant(str, systemCursor))
+			return luaL_error(L, "Invalid cursor type %s", str);
+
+		try
+		{
+			cursor = instance->newCursor(systemCursor);
+		}
+		catch (love::Exception &e)
+		{
+			return luaL_error(L, "%s", e.what());
+		}
+	}
+	else
+	{
+		// Custom image.
+		love::image::ImageData *data = luax_checktype<love::image::ImageData>(L, 1, "ImageData", IMAGE_IMAGE_DATA_T);
+		int hotx = luaL_optint(L, 2, 0);
+		int hoty = luaL_optint(L, 3, 0);
+
+		try
+		{
+			cursor = instance->newCursor(data, hotx, hoty);
+		}
+		catch (love::Exception &e)
+		{
+			return luaL_error(L, "%s", e.what());
+		}
+	}
+
+	luax_newtype(L, "Cursor", MOUSE_CURSOR_T, (void *) cursor);
+	return 1;
+}
+
+int w_setCursor(lua_State *L)
+{
+	// Revert to the default system cursor if no argument is given.
+	if (lua_isnoneornil(L, 1))
+	{
+		instance->setCursor();
+		return 0;
+	}
+
+	Cursor *cursor = luax_checktype<Cursor>(L, 1, "Cursor", MOUSE_CURSOR_T);
+	instance->setCursor(cursor);
+	return 0;
+}
+
+int w_getCursor(lua_State *L)
+{
+	Cursor *cursor = instance->getCursor();
+
+	if (cursor)
+	{
+		cursor->retain();
+		luax_newtype(L, "Cursor", MOUSE_CURSOR_T, (void *) cursor);
+	}
+	else
+		lua_pushnil(L);
+
+	return 1;
+}
+
 int w_getX(lua_State *L)
 {
 	lua_pushnumber(L, instance->getX());
@@ -122,6 +195,9 @@ int w_isGrabbed(lua_State *L)
 // List of functions to wrap.
 static const luaL_Reg functions[] =
 {
+	{ "newCursor", w_newCursor },
+	{ "setCursor", w_setCursor },
+	{ "getCursor", w_getCursor },
 	{ "getX", w_getX },
 	{ "getY", w_getY },
 	{ "setX", w_setX },
@@ -136,6 +212,13 @@ static const luaL_Reg functions[] =
 	{ 0, 0 }
 };
 
+// Types for this module.
+static const lua_CFunction types[] =
+{
+	luaopen_cursor,
+	0,
+};
+
 extern "C" int luaopen_love_mouse(lua_State *L)
 {
 	if (instance == 0)
@@ -157,7 +240,7 @@ extern "C" int luaopen_love_mouse(lua_State *L)
 	w.name = "mouse";
 	w.flags = MODULE_T;
 	w.functions = functions;
-	w.types = 0;
+	w.types = types;
 
 	return luax_register_module(L, w);
 }

+ 5 - 1
src/modules/mouse/wrap_Mouse.h

@@ -22,13 +22,17 @@
 #define LOVE_MOUSE_WRAP_MOUSE_H
 
 // LOVE
-#include "Mouse.h"
+#include "common/runtime.h"
+#include "common/config.h"
 
 namespace love
 {
 namespace mouse
 {
 
+int w_newCursor(lua_State *L);
+int w_setCursor(lua_State *L);
+int w_getCursor(lua_State *L);
 int w_getX(lua_State *L);
 int w_getY(lua_State *L);
 int w_getPosition(lua_State *L);

+ 63 - 0
src/modules/system/System.cpp

@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) 2006-2013 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#include "System.h"
+
+namespace love
+{
+namespace system
+{
+
+std::string System::getOS() const
+{
+#ifdef LOVE_MACOSX
+	return "OS X";
+#elif LOVE_WINDOWS
+	return "Windows";
+#elif LOVE_LINUX
+	return "Linux";
+#else
+	return "Unknown";
+#endif
+}
+
+bool System::getConstant(const char *in, System::PowerState &out)
+{
+	return powerStates.find(in, out);
+}
+
+bool System::getConstant(System::PowerState in, const char *&out)
+{
+	return powerStates.find(in, out);
+}
+
+StringMap<System::PowerState, System::POWER_MAX_ENUM>::Entry System::powerEntries[] =
+{
+	{"unknown", System::POWER_UNKNOWN},
+	{"battery", System::POWER_BATTERY},
+	{"nobattery", System::POWER_NO_BATTERY},
+	{"charging", System::POWER_CHARGING},
+	{"charged", System::POWER_CHARGED},
+};
+
+StringMap<System::PowerState, System::POWER_MAX_ENUM> System::powerStates(System::powerEntries, sizeof(System::powerEntries));
+
+} // system
+} // love

+ 101 - 0
src/modules/system/System.h

@@ -0,0 +1,101 @@
+/**
+ * Copyright (c) 2006-2013 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#ifndef LOVE_SYSTEM_H
+#define LOVE_SYSTEM_H
+
+// LOVE
+#include "common/config.h"
+#include "common/Module.h"
+#include "common/StringMap.h"
+
+// stdlib
+#include <string>
+
+namespace love
+{
+namespace system
+{
+
+class System : public Module
+{
+public:
+
+	enum PowerState
+	{
+		POWER_UNKNOWN,
+		POWER_BATTERY,
+		POWER_NO_BATTERY,
+		POWER_CHARGING,
+		POWER_CHARGED,
+		POWER_MAX_ENUM
+	};
+
+	virtual ~System() {}
+
+	/**
+	 * Gets the current operating system.
+	 **/
+	std::string getOS() const;
+
+	/**
+	 * Gets the number of reported CPU cores on the current system.
+	 * Does not account for technologies such as Hyperthreading: a 4-core
+	 * Hyperthreading-enabled Intel CPU will report 8, instead of 4.
+	 **/
+	virtual int getProcessorCount() const = 0;
+
+	/**
+	 * Replaces the contents of the system's text clipboard with a string.
+	 * @param text The clipboard text to set.
+	 **/
+	virtual void setClipboardText(const std::string &text) const = 0;
+
+	/**
+	 * Gets the contents of the system's text clipboard.
+	 **/
+	virtual std::string getClipboardText() const = 0;
+
+	/**
+	 * Gets information about the system's power supply.
+	 *
+	 * @param[out] seconds Time in seconds of battery life left.
+	 *             -1 if a value can't be determined.
+	 * @param[out] percent The percentage of battery life left (0-100.)
+	 *             -1 if a value can't be determined.
+	 *
+	 * @return The current state of the battery.
+	 **/
+	virtual PowerState getPowerInfo(int &seconds, int &percent) const = 0;
+
+	static bool getConstant(const char *in, PowerState &out);
+	static bool getConstant(PowerState in, const char *&out);
+
+private:
+
+	static StringMap<PowerState, POWER_MAX_ENUM>::Entry powerEntries[];
+	static StringMap<PowerState, POWER_MAX_ENUM> powerStates;
+
+}; // System
+
+} // system
+} // love
+
+#endif // LOVE_SYSTEM_H

+ 91 - 0
src/modules/system/sdl/System.cpp

@@ -0,0 +1,91 @@
+/**
+ * Copyright (c) 2006-2013 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+// LOVE
+#include "System.h"
+
+// SDL
+#include <SDL_clipboard.h>
+#include <SDL_cpuinfo.h>
+
+namespace love
+{
+namespace system
+{
+namespace sdl
+{
+
+System::System()
+{
+}
+
+const char *System::getName() const
+{
+	return "love.system.sdl";
+}
+
+int System::getProcessorCount() const
+{
+	return SDL_GetCPUCount();
+}
+
+void System::setClipboardText(const std::string &text) const
+{
+	SDL_SetClipboardText(text.c_str());
+}
+
+std::string System::getClipboardText() const
+{
+	std::string text("");
+
+	char *ctext = SDL_GetClipboardText();
+	if (ctext)
+	{
+		text = std::string(ctext);
+		SDL_free(ctext);
+	}
+
+	return text;
+}
+
+love::system::System::PowerState System::getPowerInfo(int &seconds, int &percent) const
+{
+	SDL_PowerState sdlstate = SDL_GetPowerInfo(&seconds, &percent);
+
+	PowerState state = POWER_UNKNOWN;
+	powerStates.find(sdlstate, state);
+
+	return state;
+}
+
+EnumMap<System::PowerState, SDL_PowerState, System::POWER_MAX_ENUM>::Entry System::powerEntries[] =
+{
+	{System::POWER_UNKNOWN, SDL_POWERSTATE_UNKNOWN},
+	{System::POWER_BATTERY, SDL_POWERSTATE_ON_BATTERY},
+	{System::POWER_NO_BATTERY, SDL_POWERSTATE_NO_BATTERY},
+	{System::POWER_CHARGING, SDL_POWERSTATE_CHARGING},
+	{System::POWER_CHARGED, SDL_POWERSTATE_CHARGED},
+};
+
+EnumMap<System::PowerState, SDL_PowerState, System::POWER_MAX_ENUM> System::powerStates(System::powerEntries, sizeof(System::powerEntries));
+
+} // sdl
+} // system
+} // love

+ 66 - 0
src/modules/system/sdl/System.h

@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2006-2013 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#ifndef LOVE_SYSTEM_SDL_SYSTEM_H
+#define LOVE_SYSTEM_SDL_SYSTEM_H
+
+// LOVE
+#include "system/System.h"
+#include "common/EnumMap.h"
+
+// SDL
+#include <SDL_power.h>
+
+namespace love
+{
+namespace system
+{
+namespace sdl
+{
+
+class System : public love::system::System
+{
+public:
+
+	System();
+	virtual ~System() {}
+
+	// Implements Module.
+	const char *getName() const;
+
+	int getProcessorCount() const;
+
+	void setClipboardText(const std::string &text) const;
+	std::string getClipboardText() const;
+
+	PowerState getPowerInfo(int &seconds, int &percent) const;
+
+private:
+
+	static EnumMap<PowerState, SDL_PowerState, POWER_MAX_ENUM>::Entry powerEntries[];
+	static EnumMap<PowerState, SDL_PowerState, POWER_MAX_ENUM> powerStates;
+
+}; // System
+
+} // sdl
+} // system
+} // love
+
+#endif // LOVE_SYSTEM_SDL_SYSTEM_H

+ 112 - 0
src/modules/system/wrap_System.cpp

@@ -0,0 +1,112 @@
+/**
+ * Copyright (c) 2006-2013 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+// LOVE
+#include "wrap_System.h"
+#include "sdl/System.h"
+
+namespace love
+{
+namespace system
+{
+
+static System *instance = 0;
+
+int w_getOS(lua_State *L)
+{
+	luax_pushstring(L, instance->getOS());
+	return 1;
+}
+
+int w_getProcessorCount(lua_State *L)
+{
+	lua_pushinteger(L, instance->getProcessorCount());
+	return 1;
+}
+
+int w_setClipboardText(lua_State *L)
+{
+	const char *text = luaL_checkstring(L, 1);
+	instance->setClipboardText(text);
+	return 0;
+}
+
+int w_getClipboardText(lua_State *L)
+{
+	luax_pushstring(L, instance->getClipboardText());
+	return 1;
+}
+
+int w_getPowerInfo(lua_State *L)
+{
+	int seconds = -1, percent = -1;
+	const char *str;
+
+	System::PowerState state = instance->getPowerInfo(seconds, percent);
+
+	if (!System::getConstant(state, str))
+		str = "unknown";
+
+	lua_pushstring(L, str);
+
+	if (percent >= 0)
+		lua_pushinteger(L, percent);
+	else
+		lua_pushnil(L);
+
+	if (seconds >= 0)
+		lua_pushinteger(L, seconds);
+	else
+		lua_pushnil(L);
+
+	return 3;
+}
+
+static const luaL_Reg functions[] =
+{
+	{ "getOS", w_getOS },
+	{ "getProcessorCount", w_getProcessorCount },
+	{ "setClipboardText", w_setClipboardText },
+	{ "getClipboardText", w_getClipboardText },
+	{ "getPowerInfo", w_getPowerInfo },
+	{ 0, 0 }
+};
+
+extern "C" int luaopen_love_system(lua_State *L)
+{
+	if (instance == 0)
+	{
+		instance = new love::system::sdl::System();
+	}
+	else
+		instance->retain();
+
+	WrappedModule w;
+	w.module = instance;
+	w.name = "system";
+	w.flags = MODULE_T;
+	w.functions = functions;
+	w.types = 0;
+
+	return luax_register_module(L, w);
+}
+
+} // system
+} // love

+ 43 - 0
src/modules/system/wrap_System.h

@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2006-2013 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#ifndef LOVE_WRAP_SYSTEM_H
+#define LOVE_WRAP_SYSTEM_H
+
+// LOVE
+#include "System.h"
+#include "common/runtime.h"
+
+namespace love
+{
+namespace system
+{
+
+int w_getOS(lua_State *L);
+int w_getProcessorCount(lua_State *L);
+int w_setClipboardText(lua_State *L);
+int w_getClipboardText(lua_State *L);
+int w_getPowerInfo(lua_State *L);
+extern "C" LOVE_EXPORT int luaopen_love_system(lua_State *L);
+
+} // system
+} // love
+
+#endif // LOVE_WRAP_SYSTEM_H

+ 6 - 4
src/modules/thread/ThreadModule.cpp

@@ -24,10 +24,6 @@ namespace love
 {
 namespace thread
 {
-const char *ThreadModule::getName() const
-{
-	return "love.thread.sdl";
-}
 
 LuaThread *ThreadModule::newThread(const std::string &name, love::Data *data)
 {
@@ -44,5 +40,11 @@ Channel *ThreadModule::getChannel(const std::string &name)
 {
 	return Channel::getChannel(name);
 }
+
+const char *ThreadModule::getName() const
+{
+	return "love.thread.sdl";
+}
+
 } // thread
 } // love

+ 16 - 10
src/modules/thread/ThreadModule.h

@@ -25,12 +25,14 @@
 #include <string>
 
 // LOVE
-#include <common/Data.h>
-#include <common/Module.h>
-#include <thread/Thread.h>
-#include <thread/Channel.h>
-#include <thread/LuaThread.h>
-#include <thread/threads.h>
+#include "common/Data.h"
+#include "common/Module.h"
+#include "common/StringMap.h"
+
+#include "Thread.h"
+#include "Channel.h"
+#include "LuaThread.h"
+#include "threads.h"
 
 namespace love
 {
@@ -39,13 +41,17 @@ namespace thread
 class ThreadModule : public love::Module
 {
 public:
+
 	virtual ~ThreadModule() {}
-	LuaThread *newThread(const std::string &name, love::Data *data);
-	Channel *newChannel();
-	Channel *getChannel(const std::string &name);
+	virtual LuaThread *newThread(const std::string &name, love::Data *data);
+	virtual Channel *newChannel();
+	virtual Channel *getChannel(const std::string &name);
+
+	// Implements Module.
+	virtual const char *getName() const;
 
-	const char *getName() const;
 }; // ThreadModule
+
 } // thread
 } // love
 

+ 1 - 1
src/modules/thread/sdl/Thread.cpp

@@ -51,7 +51,7 @@ bool Thread::start()
 		return false;
 	if (thread) // Clean old handle up
 		SDL_WaitThread(thread, 0);
-	thread = SDL_CreateThread(thread_runner, this);
+	thread = SDL_CreateThread(thread_runner, NULL, this);
 	running = (thread != 0);
 	return running;
 }

+ 2 - 2
src/modules/thread/sdl/threads.cpp

@@ -39,12 +39,12 @@ Mutex::~Mutex()
 
 void Mutex::lock()
 {
-	SDL_mutexP(mutex);
+	SDL_LockMutex(mutex);
 }
 
 void Mutex::unlock()
 {
-	SDL_mutexV(mutex);
+	SDL_UnlockMutex(mutex);
 }
 
 Conditional::Conditional()

+ 10 - 5
src/modules/thread/wrap_ThreadModule.cpp

@@ -18,16 +18,20 @@
 * 3. This notice may not be removed or altered from any source distribution.
 **/
 
+// LOVE
 #include "wrap_ThreadModule.h"
 #include "wrap_LuaThread.h"
 #include "wrap_Channel.h"
-#include <filesystem/File.h>
-#include <filesystem/FileData.h>
+#include "ThreadModule.h"
+
+#include "filesystem/File.h"
+#include "filesystem/FileData.h"
 
 namespace love
 {
 namespace thread
 {
+
 static ThreadModule *instance = 0;
 
 int w_newThread(lua_State *L)
@@ -89,7 +93,7 @@ extern "C" int luaopen_love_thread(lua_State *L)
 	{
 		try
 		{
-			instance = new ThreadModule();
+			instance = new love::thread::ThreadModule();
 		}
 		catch (Exception & e)
 		{
@@ -108,5 +112,6 @@ extern "C" int luaopen_love_thread(lua_State *L)
 
 	return luax_register_module(L, w);
 }
-}
-}
+
+} // thread
+} // love

+ 2 - 1
src/modules/thread/wrap_ThreadModule.h

@@ -22,7 +22,8 @@
 #define LOVE_THREAD_WRAP_THREADMODULE_H
 
 // LOVE
-#include "ThreadModule.h"
+#include "common/config.h"
+#include "common/runtime.h"
 
 namespace love
 {

+ 22 - 0
src/modules/window/Window.cpp

@@ -40,13 +40,35 @@ void Window::swapBuffers()
 
 WindowFlags::WindowFlags()
 	: fullscreen(false)
+	, fstype(Window::FULLSCREEN_TYPE_NORMAL)
 	, vsync(true)
 	, fsaa(0)
 	, resizable(false)
+	, minwidth(100)
+	, minheight(100)
 	, borderless(false)
 	, centered(true)
+	, display(0)
 {
 }
 
+bool Window::getConstant(const char *in, Window::FullscreenType &out)
+{
+	return fullscreenTypes.find(in, out);
+}
+
+bool Window::getConstant(Window::FullscreenType in, const char *&out)
+{
+	return fullscreenTypes.find(in, out);
+}
+
+StringMap<Window::FullscreenType, Window::FULLSCREEN_TYPE_MAX_ENUM>::Entry Window::fullscreenTypeEntries[] =
+{
+	{"normal", Window::FULLSCREEN_TYPE_NORMAL},
+	{"desktop", Window::FULLSCREEN_TYPE_DESKTOP},
+};
+
+StringMap<Window::FullscreenType, Window::FULLSCREEN_TYPE_MAX_ENUM> Window::fullscreenTypes(Window::fullscreenTypeEntries, sizeof(Window::fullscreenTypeEntries));
+
 } // window
 } // love

+ 61 - 18
src/modules/window/Window.h

@@ -21,32 +21,35 @@
 #ifndef LOVE_WINDOW_WINDOW_H
 #define LOVE_WINDOW_WINDOW_H
 
-// STL
-#include <string>
-
 // LOVE
 #include "common/Module.h"
+#include "common/StringMap.h"
 #include "image/ImageData.h"
 
+// C++
+#include <string>
+#include <vector>
+
 namespace love
 {
 namespace window
 {
 
-struct WindowFlags
-{
-	WindowFlags();
-	bool fullscreen; // = false
-	bool vsync; // = true
-	int fsaa; // = 0
-	bool resizable; // = false
-	bool borderless; // = false
-	bool centered; // = true
-}; // WindowFlags
+// Forward-declared so it can be used in the class methods. We can't define the
+// whole thing here because it uses the Window::Type enum.
+struct WindowFlags;
 
 class Window : public Module
 {
 public:
+
+	enum FullscreenType
+	{
+		FULLSCREEN_TYPE_NORMAL,
+		FULLSCREEN_TYPE_DESKTOP,
+		FULLSCREEN_TYPE_MAX_ENUM
+	};
+
 	struct WindowSize
 	{
 		int width;
@@ -56,18 +59,27 @@ public:
 	virtual ~Window();
 
 	virtual bool setWindow(int width = 800, int height = 600, WindowFlags *flags = 0) = 0;
-	virtual void getWindow(int &width, int &height, WindowFlags &flags) const = 0;
+	virtual void getWindow(int &width, int &height, WindowFlags &flags) = 0;
+
+	virtual bool setFullscreen(bool fullscreen, FullscreenType fstype) = 0;
+	virtual bool setFullscreen(bool fullscreen) = 0;
+
+	virtual bool onWindowResize(int width, int height) = 0;
 
-	virtual bool checkWindowSize(int width, int height, bool fullscreen) const = 0;
-	virtual WindowSize *getFullscreenSizes(int &n) const = 0;
+	virtual int getDisplayCount() const = 0;
+
+	virtual bool checkWindowSize(int width, int height, bool fullscreen, int displayindex) const = 0;
+	virtual std::vector<WindowSize> getFullscreenSizes(int displayindex) const = 0;
 
 	virtual int getWidth() const = 0;
 	virtual int getHeight() const = 0;
 
+	virtual void getDesktopDimensions(int displayindex, int &width, int &height) const = 0;
+
 	virtual bool isCreated() const = 0;
 
 	virtual void setWindowTitle(const std::string &title) = 0;
-	virtual std::string getWindowTitle() const = 0;
+	virtual const std::string &getWindowTitle() const = 0;
 
 	virtual bool setIcon(love::image::ImageData *imgd) = 0;
 	virtual love::image::ImageData *getIcon() = 0;
@@ -83,13 +95,44 @@ public:
 	virtual void setMouseVisible(bool visible) = 0;
 	virtual bool getMouseVisible() const = 0;
 
+	virtual void setMouseGrab(bool grab) = 0;
+	virtual bool isMouseGrabbed() const = 0;
+
+	virtual const void *getHandle() const = 0;
+
+	//virtual static Window *createSingleton() = 0;
 	//virtual static Window *getSingleton() = 0;
-	// No virtual statics, of course, but you are supposed to implement this static.
+	// No virtual statics, of course, but you are supposed to implement these statics.
+
+	static bool getConstant(const char *in, FullscreenType &out);
+	static bool getConstant(FullscreenType in, const char *&out);
 
 protected:
+
 	static Window *singleton;
 
+private:
+
+	static StringMap<FullscreenType, FULLSCREEN_TYPE_MAX_ENUM>::Entry fullscreenTypeEntries[];
+	static StringMap<FullscreenType, FULLSCREEN_TYPE_MAX_ENUM> fullscreenTypes;
+
 }; // Window
+
+struct WindowFlags
+{
+	WindowFlags();
+	bool fullscreen; // = false
+	Window::FullscreenType fstype; // = FULLSCREEN_TYPE_NORMAL
+	bool vsync; // = true
+	int fsaa; // = 0
+	bool resizable; // = false
+	int minwidth; // = 100
+	int minheight; // = 100
+	bool borderless; // = false
+	bool centered; // = true
+	int display; // = 0
+}; // WindowFlags
+
 } // window
 } // love
 

+ 382 - 145
src/modules/window/sdl/Window.cpp

@@ -23,11 +23,10 @@
 #include "graphics/Graphics.h"
 #include "Window.h"
 
-// SDL
-#include <SDL.h>
-
-// STL
+// C++
 #include <iostream>
+#include <vector>
+#include <algorithm>
 
 namespace love
 {
@@ -39,15 +38,30 @@ namespace sdl
 Window::Window()
 	: windowTitle("")
 	, created(false)
+	, mouseGrabbed(false)
+	, window(0)
+	, context(0)
 {
+#ifdef LOVE_MACOSX
+	// SDL 2 minimizes the window at weird times on OSX if this isn't disabled.
+	// TODO: do Linux and/or Windows need this as well (multi-monitor)?
+	SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
+#endif
+
 	if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
-		throw love::Exception(SDL_GetError());
+		throw love::Exception("%s", SDL_GetError());
 }
 
 Window::~Window()
 {
-	if (currentMode.icon)
-		currentMode.icon->release();
+	if (curMode.icon)
+		curMode.icon->release();
+
+	if (window)
+		SDL_DestroyWindow(window);
+
+	if (context)
+		SDL_GL_DeleteContext(context);
 
 	SDL_QuitSubSystem(SDL_INIT_VIDEO);
 }
@@ -66,109 +80,181 @@ bool Window::setWindow(int width, int height, WindowFlags *flags)
 	if (gfx)
 		gfx->unSetMode();
 
-	bool fullscreen = false;
-	bool vsync = true;
-	int fsaa = 0;
-	bool resizable = false;
-	bool borderless = false;
-	bool centered = true;
+	WindowFlags f;
 
 	if (flags)
+		f = *flags;
+
+	f.minwidth = std::max(f.minwidth, 0);
+	f.minheight = std::max(f.minheight, 0);
+
+	f.display = std::min(std::max(f.display, 0), getDisplayCount());
+
+	// Use the desktop resolution if a width or height of 0 is specified.
+	if (width == 0 || height == 0)
 	{
-		fullscreen = flags->fullscreen;
-		vsync = flags->vsync;
-		fsaa = flags->fsaa;
-		resizable = flags->resizable;
-		borderless = flags->borderless;
-		centered = flags->centered;
+		SDL_DisplayMode mode = {};
+		SDL_GetDesktopDisplayMode(f.display, &mode);
+		width = mode.w;
+		height = mode.h;
 	}
 
-	bool mouseVisible = getMouseVisible();
-	int keyrepeatDelay, keyrepeatInterval;
-	SDL_GetKeyRepeat(&keyrepeatDelay, &keyrepeatInterval);
-
-	// We need to restart the subsystem for two reasons:
-	// 1) Special case for fullscreen -> windowed. Windows XP did not
-	//    work well with "normal" display mode change in this case.
-	//    The application window does leave fullscreen, but the desktop
-	//    resolution does not revert to the correct one. Restarting the
-	//    SDL video subsystem does the trick, though.
-	// 2) Restart the event system (for whatever reason the event system
-	//    started and stopped with SDL_INIT_VIDEO, see:
-	//    http://sdl.beuc.net/sdl.wiki/Introduction_to_Events)
-	//    because the mouse position will not be able to exceed
-	//    the previous' video mode window size (i.e. alway
-	//    love.mouse.getX() < 800 when switching from 800x600 to a
-	//    higher resolution)
-	SDL_QuitSubSystem(SDL_INIT_VIDEO);
-
-	if (centered) // Window should be centered.
-		SDL_putenv(const_cast<char *>("SDL_VIDEO_CENTERED=center"));
-	else
-		SDL_putenv(const_cast<char *>("SDL_VIDEO_CENTERED="));
+	Uint32 sdlflags = SDL_WINDOW_OPENGL;
 
-	if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
+	if (f.fullscreen)
 	{
-		std::cout << "Could not init SDL_VIDEO: " << SDL_GetError() << std::endl;
-		return false;
+		switch (f.fstype)
+		{
+		case FULLSCREEN_TYPE_NORMAL:
+		default:
+			sdlflags |= SDL_WINDOW_FULLSCREEN;
+			break;
+		case FULLSCREEN_TYPE_DESKTOP:
+			sdlflags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
+			break;
+		}
 	}
 
-	// Set caption.
-	setWindowTitle(windowTitle);
-	setMouseVisible(mouseVisible);
-	setIcon(currentMode.icon);
-	SDL_EnableKeyRepeat(keyrepeatDelay, keyrepeatInterval);
+	if (f.resizable)
+		sdlflags |= SDL_WINDOW_RESIZABLE;
 
-	// Set GL attributes
-	SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
-	SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
-	SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
-	SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
-	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
-	SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, (vsync ? 1 : 0));
-	SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1);
+	if (f.borderless)
+		sdlflags |= SDL_WINDOW_BORDERLESS;
 
-	// FSAA
-	SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, (fsaa > 0) ? 1 : 0);
-	SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, (fsaa > 0) ? fsaa : 0);
+	// Destroy and recreate the window if the dimensions or flags have changed.
+	if (window)
+	{
+		int curdisplay = SDL_GetWindowDisplayIndex(window);
+		Uint32 wflags = SDL_GetWindowFlags(window);
+		wflags &= (SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_RESIZABLE | SDL_WINDOW_BORDERLESS);
 
-	Uint32 sdlflags = SDL_OPENGL;
-	// Flags
-	if (fullscreen)
-		sdlflags |= SDL_FULLSCREEN;
-	if (resizable)
-		sdlflags |= SDL_RESIZABLE;
-	if (borderless)
-		sdlflags |= SDL_NOFRAME;
-
-	// Have SDL set the video mode.
-	SDL_Surface *surface;
-	if ((surface = SDL_SetVideoMode(width, height, 32, sdlflags)) == 0)
+		if (sdlflags != wflags || width != curMode.width || height != curMode.height
+			|| f.display != curdisplay || f.fsaa != curMode.flags.fsaa)
+		{
+			SDL_DestroyWindow(window);
+			window = 0;
+
+			// The old window may have generated pending events which are no
+			// longer relevant. Destroy them all!
+			SDL_FlushEvent(SDL_WINDOWEVENT);
+		}
+	}
+
+	int centeredpos = SDL_WINDOWPOS_CENTERED_DISPLAY(f.display);
+	int uncenteredpos = SDL_WINDOWPOS_UNDEFINED_DISPLAY(f.display);
+
+	if (!window)
 	{
-		bool failed = true;
-		if (fsaa > 0)
+		// In Windows and Linux, some GL attributes are set on window creation.
+		setWindowGLAttributes(f.fsaa);
+
+		const char *title = windowTitle.c_str();
+		int pos = f.centered ? centeredpos : uncenteredpos;
+
+		window = SDL_CreateWindow(title, pos, pos, width, height, sdlflags);
+
+		if (!window && f.fsaa > 0)
 		{
-			// FSAA might have caused the failure, disable it and try again
+			// FSAA might have caused the failure, disable it and try again.
 			SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
 			SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
-			failed = (surface = SDL_SetVideoMode(width, height, 32, sdlflags)) == 0;
+
+			window = SDL_CreateWindow(title, pos, pos, width, height, sdlflags);
+			f.fsaa = 0;
 		}
-		if (failed)
+
+		// Make sure the window keeps any previously set icon.
+		if (window && curMode.icon)
+			setIcon(curMode.icon);
+	}
+
+	if (!window)
+	{
+		std::cerr << "Could not set video mode: " << SDL_GetError() << std::endl;
+		return false;
+	}
+
+	// Enforce minimum window dimensions.
+	SDL_SetWindowMinimumSize(window, f.minwidth, f.minheight);
+
+	if (f.centered && !f.fullscreen)
+		SDL_SetWindowPosition(window, centeredpos, centeredpos);
+
+	SDL_RaiseWindow(window);
+
+	if (!setContext(f.fsaa, f.vsync))
+		return false;
+
+	created = true;
+
+	updateWindowFlags(f);
+
+	if (gfx)
+		gfx->setMode(curMode.width, curMode.height);
+
+	// Make sure the mouse keeps its previous grab setting.
+	setMouseGrab(mouseGrabbed);
+
+	return true;
+}
+
+bool Window::onWindowResize(int width, int height)
+{
+	if (!window)
+		return false;
+
+	curMode.width = width;
+	curMode.height = height;
+
+	return true;
+}
+
+bool Window::setContext(int fsaa, bool vsync)
+{
+	// We need to recreate the context if FSAA changes,
+	// or if the existing context has been invalidated.
+	if (context && (fsaa != curMode.flags.fsaa || SDL_GL_MakeCurrent(window, context) < 0))
+	{
+		SDL_GL_DeleteContext(context);
+		context = 0;
+	}
+
+	// Create a new OpenGL context.
+	if (!context)
+	{
+		setWindowGLAttributes(fsaa);
+
+		context = SDL_GL_CreateContext(window);
+
+		if (!context && fsaa > 0)
 		{
-			std::cerr << "Could not set video mode: "  << SDL_GetError() << std::endl;
-			return false;
+			// FSAA might have caused the failure, disable it and try again.
+			SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
+			SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
+			context = SDL_GL_CreateContext(window);
 		}
 	}
 
-	created = true;
+	if (!context)
+	{
+		std::cerr << "Could not set video mode: " << SDL_GetError() << std::endl;
+		return false;
+	}
 
-	const SDL_VideoInfo *videoinfo = SDL_GetVideoInfo();
-	width = videoinfo->current_w;
-	height = videoinfo->current_h;
+	// Set vertical synchronization.
+	if (vsync)
+	{
+		// Prefer EXT_swap_control_tear (late swaps happen immediately),
+		// otherwise fall back to regular vsync.
+		if (SDL_GL_SetSwapInterval(-1) < 0 || SDL_GL_GetSwapInterval() != -1)
+			SDL_GL_SetSwapInterval(1);
+	}
+	else
+		SDL_GL_SetSwapInterval(0);
 
+	// Verify FSAA setting.
 	int buffers;
 	int samples;
-
 	SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &buffers);
 	SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &samples);
 
@@ -179,65 +265,173 @@ bool Window::setWindow(int width, int height, WindowFlags *flags)
 		fsaa = (buffers > 0) ? samples : 0;
 	}
 
-	// Get the actual vsync status
-	int real_vsync;
-	SDL_GL_GetAttribute(SDL_GL_SWAP_CONTROL, &real_vsync);
+	curMode.flags.fsaa = fsaa;
+	curMode.flags.vsync = SDL_GL_GetSwapInterval() != 0;
+
+	return true;
+}
+
+void Window::setWindowGLAttributes(int fsaa) const
+{
+	// Set GL window attributes.
+	SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
+	SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
+	SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
+	SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
+	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+	SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1);
+
+	// FSAA.
+	SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, (fsaa > 0) ? 1 : 0);
+	SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, (fsaa > 0) ? fsaa : 0);
+}
+
+void Window::updateWindowFlags(const WindowFlags &newflags)
+{
+	Uint32 wflags = SDL_GetWindowFlags(window);
 
 	// Set the new display mode as the current display mode.
-	currentMode.width = width;
-	currentMode.height = height;
-	currentMode.flags.fsaa = fsaa;
-	currentMode.flags.fullscreen = fullscreen;
-	currentMode.flags.vsync = (real_vsync != 0);
-	currentMode.flags.resizable = ((surface->flags & SDL_RESIZABLE) != 0);
-	currentMode.flags.borderless = ((surface->flags & SDL_NOFRAME) != 0);
-	currentMode.flags.centered = centered;
+	SDL_GetWindowSize(window, &curMode.width, &curMode.height);
 
-	if (gfx)
-		gfx->setMode(width, height);
+	if ((wflags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP)
+	{
+		curMode.flags.fullscreen = true;
+		curMode.flags.fstype = FULLSCREEN_TYPE_DESKTOP;
+	}
+	else if ((wflags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)
+	{
+		curMode.flags.fullscreen = true;
+		curMode.flags.fstype = FULLSCREEN_TYPE_NORMAL;
+	}
+	else
+	{
+		curMode.flags.fullscreen = false;
+		curMode.flags.fstype = newflags.fstype;
+	}
 
-	return true;
+	// The min width/height is set to 0 internally in SDL when in fullscreen.
+	if (curMode.flags.fullscreen)
+	{
+		curMode.flags.minwidth = newflags.minwidth;
+		curMode.flags.minheight = newflags.minheight;
+	}
+	else
+		SDL_GetWindowMinimumSize(window, &curMode.flags.minwidth, &curMode.flags.minheight);
+
+	curMode.flags.resizable = (wflags & SDL_WINDOW_RESIZABLE) != 0;
+	curMode.flags.borderless = (wflags & SDL_WINDOW_BORDERLESS) != 0;
+	curMode.flags.centered = newflags.centered;
+	curMode.flags.display = std::max(SDL_GetWindowDisplayIndex(window), 0);
 }
 
-void Window::getWindow(int &width, int &height, WindowFlags &flags) const
+void Window::getWindow(int &width, int &height, WindowFlags &flags)
 {
-	width = currentMode.width;
-	height = currentMode.height;
-	flags = currentMode.flags;
+	// Window position may be different from creation - update display index.
+	if (window)
+		curMode.flags.display = std::max(SDL_GetWindowDisplayIndex(window), 0);
+
+	width = curMode.width;
+	height = curMode.height;
+	flags = curMode.flags;
 }
 
-bool Window::checkWindowSize(int width, int height, bool fullscreen) const
+bool Window::setFullscreen(bool fullscreen, Window::FullscreenType fstype)
 {
-	Uint32 sdlflags = fullscreen ? (SDL_OPENGL | SDL_FULLSCREEN) : SDL_OPENGL;
+	if (!window)
+		return false;
+
+	WindowFlags newflags = curMode.flags;
+	newflags.fullscreen = fullscreen;
+	newflags.fstype = fstype;
+
+	Uint32 sdlflags = 0;
+
+	if (fullscreen)
+	{
+		if (fstype == FULLSCREEN_TYPE_DESKTOP)
+			sdlflags = SDL_WINDOW_FULLSCREEN_DESKTOP;
+		else
+		{
+			sdlflags = SDL_WINDOW_FULLSCREEN;
+
+			SDL_DisplayMode mode = {};
+			mode.w = curMode.width;
+			mode.h = curMode.height;
+
+			SDL_GetClosestDisplayMode(SDL_GetWindowDisplayIndex(window), &mode, &mode);
+			SDL_SetWindowDisplayMode(window, &mode);
+		}
+	}
 
-	// Check if mode is supported
-	int bpp = SDL_VideoModeOK(width, height, 32, sdlflags);
+	if (SDL_SetWindowFullscreen(window, sdlflags) == 0)
+	{
+		SDL_GL_MakeCurrent(window, context);
+		updateWindowFlags(newflags);
+		return true;
+	}
 
-	return (bpp >= 16);
+	return false;
 }
 
-typedef Window::WindowSize WindowSize;
+bool Window::setFullscreen(bool fullscreen)
+{
+	return setFullscreen(fullscreen, curMode.flags.fstype);
+}
 
-WindowSize *Window::getFullscreenSizes(int &n) const
+int Window::getDisplayCount() const
 {
-	SDL_Rect **modes = SDL_ListModes(0, SDL_OPENGL | SDL_FULLSCREEN);
+	return SDL_GetNumVideoDisplays();
+}
 
-	if (modes == (SDL_Rect **)0 || modes == (SDL_Rect **)-1)
+bool Window::checkWindowSize(int width, int height, bool fullscreen, int displayindex) const
+{
+	if (fullscreen)
 	{
-		n = 0;
-		return NULL;
+		SDL_DisplayMode mode = {}, closest = {};
+		mode.w = width;
+		mode.h = height;
+		SDL_GetClosestDisplayMode(displayindex, &mode, &closest);
+
+		return (mode.w == closest.w && mode.h == closest.h);
 	}
+	else
+	{
+		SDL_DisplayMode mode = {};
+		SDL_GetDesktopDisplayMode(displayindex, &mode);
+
+		return (width <= mode.w && height <= mode.h);
+	}
+}
 
-	n = 0;
-	for (int i = 0; modes[i]; i++)
-		n++;
+typedef Window::WindowSize WindowSize;
 
-	WindowSize *sizes = new WindowSize[n];
+std::vector<WindowSize> Window::getFullscreenSizes(int displayindex) const
+{
+	std::vector<WindowSize> sizes;
 
-	for (int i = 0; i < n; i++)
+	SDL_DisplayMode mode = {};
+	std::vector<WindowSize>::const_iterator it;
+	for (int i = 0; i < SDL_GetNumDisplayModes(displayindex); i++)
 	{
-		WindowSize w = {modes[i]->w, modes[i]->h};
-		sizes[i] = w;
+		SDL_GetDisplayMode(displayindex, i, &mode);
+
+		// SDL2's display mode list has multiple entries for modes of the same
+		// size with different bits per pixel, so we need to filter those out.
+		bool alreadyhassize = false;
+		for (it = sizes.begin(); it != sizes.end(); ++it)
+		{
+			if (it->width == mode.w && it->height == mode.h)
+			{
+				alreadyhassize = true;
+				break;
+			}
+		}
+
+		if (!alreadyhassize)
+		{
+			WindowSize w = {mode.w, mode.h};
+			sizes.push_back(w);
+		}
 	}
 
 	return sizes;
@@ -245,12 +439,28 @@ WindowSize *Window::getFullscreenSizes(int &n) const
 
 int Window::getWidth() const
 {
-	return currentMode.width;
+	return curMode.width;
 }
 
 int Window::getHeight() const
 {
-	return currentMode.height;
+	return curMode.height;
+}
+
+void Window::getDesktopDimensions(int displayindex, int &width, int &height) const
+{
+	if (displayindex >= 0 && displayindex < getDisplayCount())
+	{
+		SDL_DisplayMode mode = {};
+		SDL_GetDesktopDisplayMode(displayindex, &mode);
+		width = mode.w;
+		height = mode.h;
+	}
+	else
+	{
+		width = 0;
+		height = 0;
+	}
 }
 
 bool Window::isCreated() const
@@ -261,14 +471,13 @@ bool Window::isCreated() const
 void Window::setWindowTitle(const std::string &title)
 {
 	windowTitle = title;
-	SDL_WM_SetCaption(windowTitle.c_str(), 0);
+
+	if (window)
+		SDL_SetWindowTitle(window, title.c_str());
 }
 
-std::string Window::getWindowTitle() const
+const std::string &Window::getWindowTitle() const
 {
-	// not a reference
-	// because we want this untouched
-	// const std::string& might be an option
 	return windowTitle;
 }
 
@@ -277,6 +486,14 @@ bool Window::setIcon(love::image::ImageData *imgd)
 	if (!imgd)
 		return false;
 
+	imgd->retain();
+	if (curMode.icon)
+		curMode.icon->release();
+	curMode.icon = imgd;
+
+	if (!window)
+		return false;
+
 	Uint32 rmask, gmask, bmask, amask;
 #ifdef LOVE_BIG_ENDIAN
 	rmask = 0xFF000000;
@@ -294,51 +511,46 @@ bool Window::setIcon(love::image::ImageData *imgd)
 	int h = imgd->getHeight();
 	int pitch = imgd->getWidth() * 4;
 
-	SDL_Surface *icon = 0;
+	SDL_Surface *sdlicon = 0;
 
 	{
 		// We don't want another thread modifying the ImageData mid-copy.
 		love::thread::Lock lock(imgd->getMutex());
-		icon = SDL_CreateRGBSurfaceFrom(imgd->getData(), w, h, 32, pitch, rmask, gmask, bmask, amask);
+		sdlicon = SDL_CreateRGBSurfaceFrom(imgd->getData(), w, h, 32, pitch, rmask, gmask, bmask, amask);
 	}
 
-	if (!icon)
+	if (!sdlicon)
 		return false;
 
-	SDL_WM_SetIcon(icon, NULL);
-	SDL_FreeSurface(icon);
-
-	imgd->retain();
-	if (currentMode.icon)
-		currentMode.icon->release();
-	currentMode.icon = imgd;
+	SDL_SetWindowIcon(window, sdlicon);
+	SDL_FreeSurface(sdlicon);
 
 	return true;
 }
 
 love::image::ImageData *Window::getIcon()
 {
-	return currentMode.icon;
+	return curMode.icon;
 }
 
 void Window::swapBuffers()
 {
-	SDL_GL_SwapBuffers();
+	SDL_GL_SwapWindow(window);
 }
 
 bool Window::hasFocus() const
 {
-	return (SDL_GetAppState() & SDL_APPINPUTFOCUS) != 0;
+	return (window && SDL_GetKeyboardFocus() == window);
 }
 
 bool Window::hasMouseFocus() const
 {
-	return (SDL_GetAppState() & SDL_APPMOUSEFOCUS) != 0;
+	return (window && SDL_GetMouseFocus() == window);
 }
 
 bool Window::isVisible() const
 {
-	return (SDL_GetAppState() & SDL_APPACTIVE) != 0;
+	return window && (SDL_GetWindowFlags(window) & SDL_WINDOW_SHOWN) != 0;
 }
 
 void Window::setMouseVisible(bool visible)
@@ -348,10 +560,30 @@ void Window::setMouseVisible(bool visible)
 
 bool Window::getMouseVisible() const
 {
-	return (SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE) ? true : false;
+	return (SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE);
 }
 
-love::window::Window *Window::getSingleton()
+void Window::setMouseGrab(bool grab)
+{
+	mouseGrabbed = grab;
+	if (window)
+		SDL_SetWindowGrab(window, (SDL_bool) grab);
+}
+
+bool Window::isMouseGrabbed() const
+{
+	if (window)
+		return (bool) SDL_GetWindowGrab(window);
+	else
+		return mouseGrabbed;
+}
+
+const void *Window::getHandle() const
+{
+	return window;
+}
+
+love::window::Window *Window::createSingleton()
 {
 	if (!singleton)
 		singleton = new Window();
@@ -361,6 +593,11 @@ love::window::Window *Window::getSingleton()
 	return singleton;
 }
 
+love::window::Window *Window::getSingleton()
+{
+	return singleton;
+}
+
 const char *Window::getName() const
 {
 	return "love.window.sdl";

+ 34 - 5
src/modules/window/sdl/Window.h

@@ -24,6 +24,9 @@
 // LOVE
 #include "window/Window.h"
 
+// SDL
+#include <SDL.h>
+
 namespace love
 {
 namespace window
@@ -39,18 +42,27 @@ public:
 	~Window();
 
 	bool setWindow(int width = 800, int height = 600, WindowFlags *flags = 0);
-	void getWindow(int &width, int &height, WindowFlags &flags) const;
+	void getWindow(int &width, int &height, WindowFlags &flags);
+
+	bool setFullscreen(bool fullscreen, FullscreenType fstype);
+	bool setFullscreen(bool fullscreen);
+
+	bool onWindowResize(int width, int height);
 
-	bool checkWindowSize(int width, int height, bool fullscreen) const;
-	WindowSize *getFullscreenSizes(int &n) const;
+	int getDisplayCount() const;
+
+	bool checkWindowSize(int width, int height, bool fullscreen, int displayindex) const;
+	std::vector<WindowSize> getFullscreenSizes(int displayindex) const;
 
 	int getWidth() const;
 	int getHeight() const;
 
+	void getDesktopDimensions(int displayindex, int &width, int &height) const;
+
 	bool isCreated() const;
 
 	void setWindowTitle(const std::string &title);
-	std::string getWindowTitle() const;
+	const std::string &getWindowTitle() const;
 
 	bool setIcon(love::image::ImageData *imgd);
 	love::image::ImageData *getIcon();
@@ -65,12 +77,24 @@ public:
 	void setMouseVisible(bool visible);
 	bool getMouseVisible() const;
 
+	void setMouseGrab(bool grab);
+	bool isMouseGrabbed() const;
+
+	const void *getHandle() const;
+
+	static love::window::Window *createSingleton();
 	static love::window::Window *getSingleton();
 
 	const char *getName() const;
 
 private:
 
+	bool setContext(int fsaa, bool vsync);
+	void setWindowGLAttributes(int fsaa) const;
+
+	// Update the window flags based on the window's actual state.
+	void updateWindowFlags(const WindowFlags &newflags);
+
 	std::string windowTitle;
 
 	struct _currentMode
@@ -82,10 +106,15 @@ private:
 		WindowFlags flags;
 		love::image::ImageData *icon;
 
-	} currentMode;
+	} curMode;
 
 	bool created;
 
+	bool mouseGrabbed;
+
+	SDL_Window *window;
+	SDL_GLContext context;
+
 }; // Window
 
 } // sdl

+ 104 - 28
src/modules/window/wrap_Window.cpp

@@ -28,23 +28,37 @@ namespace window
 
 static Window *instance = 0;
 
+int w_getDisplayCount(lua_State *L)
+{
+	lua_pushinteger(L, instance->getDisplayCount());
+	return 1;
+}
+
 int w_checkMode(lua_State *L)
 {
 	int w = luaL_checkint(L, 1);
 	int h = luaL_checkint(L, 2);
 
 	bool fs = false;
+	int displayindex = 0;
 
 	if (lua_istable(L, 3))
 	{
 		lua_getfield(L, 3, "fullscreen");
 		fs = luax_toboolean(L, -1);
-		lua_pop(L, 1);
+
+		lua_getfield(L, 3, "display");
+		displayindex = luaL_optint(L, -1, 1) - 1;
+
+		lua_pop(L, 2);
 	}
 	else
+	{
 		fs = luax_toboolean(L, 3);
+		displayindex = luaL_optint(L, 4, 1) - 1;
+	}
 
-	luax_pushboolean(L, instance->checkWindowSize(w, h, fs));
+	luax_pushboolean(L, instance->checkWindowSize(w, h, fs, displayindex));
 	return 1;
 }
 
@@ -63,12 +77,33 @@ int w_setMode(lua_State *L)
 
 	WindowFlags flags;
 
+	lua_getfield(L, 3, "fullscreentype");
+	if (!lua_isnoneornil(L, -1))
+	{
+		const char *typestr = luaL_checkstring(L, -1);
+
+		if (!Window::getConstant(typestr, flags.fstype))
+			return luaL_error(L, "Invalid fullscreen type: %s", typestr);
+	}
+	else
+	{
+		// Default to "normal" fullscreen.
+		flags.fstype = Window::FULLSCREEN_TYPE_NORMAL;
+	}
+	lua_pop(L, 1);
+
 	flags.fullscreen = luax_boolflag(L, 3, "fullscreen", false);
 	flags.vsync = luax_boolflag(L, 3, "vsync", true);
 	flags.fsaa = luax_intflag(L, 3, "fsaa", 0);
 	flags.resizable = luax_boolflag(L, 3, "resizable", false);
+	flags.minwidth = luax_intflag(L, 3, "minwidth", 100);
+	flags.minheight = luax_intflag(L, 3, "minheight", 100);
 	flags.borderless = luax_boolflag(L, 3, "borderless", false);
 	flags.centered = luax_boolflag(L, 3, "centered", true);
+	flags.display = luax_intflag(L, 3, "display", 1);
+
+	// Display index is 1-based in Lua and 0-based internally.
+	flags.display--;
 
 	try
 	{
@@ -92,38 +127,52 @@ int w_getMode(lua_State *L)
 
 	lua_newtable(L);
 
+	const char *fstypestr = "normal";
+	Window::getConstant(flags.fstype, fstypestr);
+
+	lua_pushstring(L, fstypestr);
+	lua_setfield(L, -2, "fullscreentype");
+
 	luax_pushboolean(L, flags.fullscreen);
 	lua_setfield(L, -2, "fullscreen");
 
 	luax_pushboolean(L, flags.vsync);
 	lua_setfield(L, -2, "vsync");
 
-	lua_pushnumber(L, flags.fsaa);
+	lua_pushinteger(L, flags.fsaa);
 	lua_setfield(L, -2, "fsaa");
 
 	luax_pushboolean(L, flags.resizable);
 	lua_setfield(L, -2, "resizable");
 
+	lua_pushinteger(L, flags.minwidth);
+	lua_setfield(L, -2, "minwidth");
+
+	lua_pushinteger(L, flags.minheight);
+	lua_setfield(L, -2, "minheight");
+
 	luax_pushboolean(L, flags.borderless);
 	lua_setfield(L, -2, "borderless");
 
 	luax_pushboolean(L, flags.centered);
 	lua_setfield(L, -2, "centered");
-	
+
+	// Display index is 0-based internally and 1-based in Lua.
+	lua_pushinteger(L, flags.display + 1);
+	lua_setfield(L, -2, "display");
+
 	return 3;
 }
 
 int w_getModes(lua_State *L)
 {
-	int n;
-	Window::WindowSize *modes = instance->getFullscreenSizes(n);
+	int displayindex = luaL_optint(L, 1, 1);
 
-	if (modes == 0)
-		return 0;
+	std::vector<Window::WindowSize> modes = instance->getFullscreenSizes(displayindex - 1);
 
-	lua_createtable(L, n, 0);
+	lua_createtable(L, modes.size(), 0);
 
-	for (int i = 0; i < n ; i++)
+	for (size_t i = 0; i < modes.size(); i++)
 	{
 		lua_pushinteger(L, i+1);
 		lua_createtable(L, 0, 2);
@@ -141,29 +190,43 @@ int w_getModes(lua_State *L)
 		lua_settable(L, -3);
 	}
 
-	delete[] modes;
 	return 1;
 }
 
-int w_toggleFullscreen(lua_State *L)
+int w_setFullscreen(lua_State *L)
 {
-	int width, height;
-	WindowFlags flags;
-	instance->getWindow(width, height, flags);
-	flags.fullscreen = !flags.fullscreen;
+	bool fullscreen = luax_toboolean(L, 1);
+	Window::FullscreenType fstype = Window::FULLSCREEN_TYPE_MAX_ENUM;
 
-	try
-	{
-		luax_pushboolean(L, instance->setWindow(width, height, &flags));
-	}
-	catch (love::Exception &e)
-	{
-		return luaL_error(L, "%s", e.what());
-	}
+	const char *typestr = lua_isnoneornil(L, 2) ? 0 : lua_tostring(L, 2);
+	if (typestr && !Window::getConstant(typestr, fstype))
+		return luaL_error(L, "Invalid fullscreen type: %s", typestr);
 
+	bool success = false;
+	if (fstype == Window::FULLSCREEN_TYPE_MAX_ENUM)
+		success = instance->setFullscreen(fullscreen);
+	else
+		success = instance->setFullscreen(fullscreen, fstype);
+
+	luax_pushboolean(L, success);
 	return 1;
 }
 
+int w_isFullscreen(lua_State *L)
+{
+	int w, h;
+	WindowFlags flags;
+	instance->getWindow(w, h, flags);
+
+	const char *typestr;
+	if (!Window::getConstant(flags.fstype, typestr))
+		luaL_error(L, "Unknown fullscreen type.");
+
+	luax_pushboolean(L, flags.fullscreen);
+	lua_pushstring(L, typestr);
+	return 2;
+}
+
 int w_isCreated(lua_State *L)
 {
 	luax_pushboolean(L, instance->isCreated());
@@ -189,11 +252,21 @@ int w_getDimensions(lua_State *L)
 	return 2;
 }
 
+int w_getDesktopDimensions(lua_State *L)
+{
+	int width = 0, height = 0;
+	int displayindex = luaL_optint(L, 1, 1) - 1;
+	instance->getDesktopDimensions(displayindex, width, height);
+	lua_pushinteger(L, width);
+	lua_pushinteger(L, height);
+	return 2;
+}
+
 int w_setIcon(lua_State *L)
 {
 	image::ImageData *i = luax_checktype<image::ImageData>(L, 1, "ImageData", IMAGE_IMAGE_DATA_T);
-	instance->setIcon(i);
-	return 0;
+	luax_pushboolean(L, instance->setIcon(i));
+	return 1;
 }
 
 int w_getIcon(lua_State *L)
@@ -242,15 +315,18 @@ int w_isVisible(lua_State *L)
 
 static const luaL_Reg functions[] =
 {
+	{ "getDisplayCount", w_getDisplayCount },
 	{ "checkMode", w_checkMode },
 	{ "setMode", w_setMode },
 	{ "getMode", w_getMode },
 	{ "getModes", w_getModes },
-	{ "toggleFullscreen", w_toggleFullscreen },
+	{ "setFullscreen", w_setFullscreen },
+	{ "isFullscreen", w_isFullscreen },
 	{ "isCreated", w_isCreated },
 	{ "getWidth", w_getWidth },
 	{ "getHeight", w_getHeight },
 	{ "getDimensions", w_getDimensions },
+	{ "getDesktopDimensions", w_getDesktopDimensions },
 	{ "setIcon", w_setIcon },
 	{ "getIcon", w_getIcon },
 	{ "setTitle", w_setTitle },
@@ -265,7 +341,7 @@ extern "C" int luaopen_love_window(lua_State *L)
 {
 	try
 	{
-		instance = sdl::Window::getSingleton();
+		instance = sdl::Window::createSingleton();
 	}
 	catch (love::Exception &e)
 	{

+ 4 - 1
src/modules/window/wrap_Window.h

@@ -29,15 +29,18 @@ namespace love
 namespace window
 {
 
+int w_getDisplayCount(lua_State *L);
 int w_checkMode(lua_State *L);
 int w_setMode(lua_State *L);
 int w_getMode(lua_State *L);
 int w_getModes(lua_State *L);
-int w_toggleFullscreen(lua_State *L);
+int w_setFullscreen(lua_State *L);
+int w_isFullscreen(lua_State *L);
 int w_isCreated(lua_State *L);
 int w_getWidth(lua_State *L);
 int w_getHeight(lua_State *L);
 int w_getDimensions(lua_State *L);
+int w_getDesktopDimensions(lua_State *L);
 int w_setIcon(lua_State *L);
 int w_getIcon(lua_State *L);
 int w_setTitle(lua_State *L);

+ 34 - 7
src/scripts/boot.lua

@@ -158,6 +158,12 @@ function love.createhandlers()
 		keyreleased = function (b)
 			if love.keyreleased then return love.keyreleased(b) end
 		end,
+		textinput = function (t)
+			if love.textinput then return love.textinput(t) end
+		end,
+		textedit = function (t,s,l)
+			if love.textedit then return love.textedit(t,s,l) end
+		end,
 		mousepressed = function (x,y,b)
 			if love.mousepressed then return love.mousepressed(x,y,b) end
 		end,
@@ -173,9 +179,24 @@ function love.createhandlers()
 		joystickaxis = function (j,a,v)
 			if love.joystickaxis then return love.joystickaxis(j,a,v) end
 		end,
-		joystickhat = function(j,h,v)
+		joystickhat = function (j,h,v)
 			if love.joystickhat then return love.joystickhat(j,h,v) end
 		end,
+		gamepadpressed = function (j,b)
+			if love.gamepadpressed then return love.gamepadpressed(j,b) end
+		end,
+		gamepadreleased = function (j,b)
+			if love.gamepadreleased then return love.gamepadreleased(j,b) end
+		end,
+		gamepadaxis = function (j,a,v)
+			if love.gamepadaxis then return love.gamepadaxis(j,a,v) end
+		end,
+		joystickadded = function (j)
+			if love.joystickadded then return love.joystickadded(j) end
+		end,
+		joystickremoved = function(j)
+			if love.joystickremoved then return love.joystickremoved(j) end
+		end,
 		focus = function (f)
 			if love.focus then return love.focus(f) end
 		end,
@@ -192,11 +213,7 @@ function love.createhandlers()
 			if love.threaderror then return love.threaderror(t, err) end
 		end,
 		resize = function(w, h)
-			local ow, oh, flags = love.window.getMode()
-			if flags.resizable then
-				love.window.setMode(w, h, flags)
-				if love.resize then return love.resize(w, h) end
-			end
+			if love.resize then return love.resize(w, h) end
 		end,
 	}, {
 		__index = function(self, name)
@@ -266,7 +283,11 @@ function love.init()
 		window = {
 			width = 800,
 			height = 600,
+			minwidth = 100,
+			minheight = 100,
 			fullscreen = false,
+			fullscreentype = "normal",
+			display = 1,
 			vsync = true,
 			fsaa = 0,
 			borderless = false,
@@ -285,6 +306,7 @@ function love.init()
 			math = true,
 			physics = true,
 			sound = true,
+			system = true,
 			font = true,
 			thread = true,
 			window = true,
@@ -322,6 +344,7 @@ function love.init()
 		"joystick",
 		"mouse",
 		"sound",
+		"system",
 		"audio",
 		"image",
 		"font",
@@ -347,15 +370,19 @@ function love.init()
 	-- Setup window here.
 	local has_window = false
 	if c.window and c.modules.window then
-		if love.window.checkMode(c.window.width, c.window.height, c.window.fullscreen) or (c.window.width == 0 and c.window.height == 0) then
+		if love.window.checkMode(c.window.width, c.window.height, c.window.fullscreen, c.window.display) or (c.window.width == 0 and c.window.height == 0) then
 			assert(love.window.setMode(c.window.width, c.window.height,
 			{
 				fullscreen = c.window.fullscreen,
+				fullscreentype = c.window.fullscreentype,
 				vsync = c.window.vsync,
 				fsaa = c.window.fsaa,
 				resizable = c.window.resizable,
+				minwidth = c.window.minwidth,
+				minheight = c.window.minheight,
 				borderless = c.window.borderless,
 				centered = c.window.centered,
+				display = c.window.display,
 			}), "Could not set window mode")
 		else
 			error("Could not set window mode")

+ 71 - 13
src/scripts/boot.lua.h

@@ -270,6 +270,19 @@ const unsigned char boot_lua[] =
 	0x6f, 0x76, 0x65, 0x2e, 0x6b, 0x65, 0x79, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x64, 0x28, 0x62, 0x29, 
 	0x20, 0x65, 0x6e, 0x64, 0x0a,
 	0x09, 0x09, 0x65, 0x6e, 0x64, 0x2c, 0x0a,
+	0x09, 0x09, 0x74, 0x65, 0x78, 0x74, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 
+	0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x74, 0x29, 0x0a,
+	0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x69, 0x6e, 0x70, 
+	0x75, 0x74, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 
+	0x65, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x28, 0x74, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
+	0x09, 0x09, 0x65, 0x6e, 0x64, 0x2c, 0x0a,
+	0x09, 0x09, 0x74, 0x65, 0x78, 0x74, 0x65, 0x64, 0x69, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 
+	0x69, 0x6f, 0x6e, 0x20, 0x28, 0x74, 0x2c, 0x73, 0x2c, 0x6c, 0x29, 0x0a,
+	0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x65, 0x64, 0x69, 
+	0x74, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 
+	0x2e, 0x74, 0x65, 0x78, 0x74, 0x65, 0x64, 0x69, 0x74, 0x28, 0x74, 0x2c, 0x73, 0x2c, 0x6c, 0x29, 0x20, 0x65, 
+	0x6e, 0x64, 0x0a,
+	0x09, 0x09, 0x65, 0x6e, 0x64, 0x2c, 0x0a,
 	0x09, 0x09, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x66, 
 	0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x78, 0x2c, 0x79, 0x2c, 0x62, 0x29, 0x0a,
 	0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x70, 0x72, 
@@ -306,12 +319,47 @@ const unsigned char boot_lua[] =
 	0x6a, 0x2c, 0x61, 0x2c, 0x76, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
 	0x09, 0x09, 0x65, 0x6e, 0x64, 0x2c, 0x0a,
 	0x09, 0x09, 0x6a, 0x6f, 0x79, 0x73, 0x74, 0x69, 0x63, 0x6b, 0x68, 0x61, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 
-	0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6a, 0x2c, 0x68, 0x2c, 0x76, 0x29, 0x0a,
+	0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x6a, 0x2c, 0x68, 0x2c, 0x76, 0x29, 0x0a,
 	0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x6a, 0x6f, 0x79, 0x73, 0x74, 0x69, 0x63, 
 	0x6b, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6c, 
 	0x6f, 0x76, 0x65, 0x2e, 0x6a, 0x6f, 0x79, 0x73, 0x74, 0x69, 0x63, 0x6b, 0x68, 0x61, 0x74, 0x28, 0x6a, 0x2c, 
 	0x68, 0x2c, 0x76, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
 	0x09, 0x09, 0x65, 0x6e, 0x64, 0x2c, 0x0a,
+	0x09, 0x09, 0x67, 0x61, 0x6d, 0x65, 0x70, 0x61, 0x64, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x20, 0x3d, 
+	0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x6a, 0x2c, 0x62, 0x29, 0x0a,
+	0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x61, 0x6d, 0x65, 0x70, 0x61, 0x64, 
+	0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 
+	0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x61, 0x6d, 0x65, 0x70, 0x61, 0x64, 0x70, 0x72, 0x65, 0x73, 
+	0x73, 0x65, 0x64, 0x28, 0x6a, 0x2c, 0x62, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
+	0x09, 0x09, 0x65, 0x6e, 0x64, 0x2c, 0x0a,
+	0x09, 0x09, 0x67, 0x61, 0x6d, 0x65, 0x70, 0x61, 0x64, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x64, 0x20, 
+	0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x6a, 0x2c, 0x62, 0x29, 0x0a,
+	0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x61, 0x6d, 0x65, 0x70, 0x61, 0x64, 
+	0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 
+	0x72, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x61, 0x6d, 0x65, 0x70, 0x61, 0x64, 0x72, 0x65, 0x6c, 
+	0x65, 0x61, 0x73, 0x65, 0x64, 0x28, 0x6a, 0x2c, 0x62, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
+	0x09, 0x09, 0x65, 0x6e, 0x64, 0x2c, 0x0a,
+	0x09, 0x09, 0x67, 0x61, 0x6d, 0x65, 0x70, 0x61, 0x64, 0x61, 0x78, 0x69, 0x73, 0x20, 0x3d, 0x20, 0x66, 0x75, 
+	0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x6a, 0x2c, 0x61, 0x2c, 0x76, 0x29, 0x0a,
+	0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x61, 0x6d, 0x65, 0x70, 0x61, 0x64, 
+	0x61, 0x78, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6c, 
+	0x6f, 0x76, 0x65, 0x2e, 0x67, 0x61, 0x6d, 0x65, 0x70, 0x61, 0x64, 0x61, 0x78, 0x69, 0x73, 0x28, 0x6a, 0x2c, 
+	0x61, 0x2c, 0x76, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
+	0x09, 0x09, 0x65, 0x6e, 0x64, 0x2c, 0x0a,
+	0x09, 0x09, 0x6a, 0x6f, 0x79, 0x73, 0x74, 0x69, 0x63, 0x6b, 0x61, 0x64, 0x64, 0x65, 0x64, 0x20, 0x3d, 0x20, 
+	0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x6a, 0x29, 0x0a,
+	0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x6a, 0x6f, 0x79, 0x73, 0x74, 0x69, 0x63, 
+	0x6b, 0x61, 0x64, 0x64, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 
+	0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x6a, 0x6f, 0x79, 0x73, 0x74, 0x69, 0x63, 0x6b, 0x61, 0x64, 0x64, 0x65, 
+	0x64, 0x28, 0x6a, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
+	0x09, 0x09, 0x65, 0x6e, 0x64, 0x2c, 0x0a,
+	0x09, 0x09, 0x6a, 0x6f, 0x79, 0x73, 0x74, 0x69, 0x63, 0x6b, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x20, 
+	0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6a, 0x29, 0x0a,
+	0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x6a, 0x6f, 0x79, 0x73, 0x74, 0x69, 0x63, 
+	0x6b, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 
+	0x72, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x6a, 0x6f, 0x79, 0x73, 0x74, 0x69, 0x63, 0x6b, 0x72, 0x65, 
+	0x6d, 0x6f, 0x76, 0x65, 0x64, 0x28, 0x6a, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
+	0x09, 0x09, 0x65, 0x6e, 0x64, 0x2c, 0x0a,
 	0x09, 0x09, 0x66, 0x6f, 0x63, 0x75, 0x73, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 
 	0x20, 0x28, 0x66, 0x29, 0x0a,
 	0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x66, 0x6f, 0x63, 0x75, 0x73, 0x20, 0x74, 
@@ -344,17 +392,9 @@ const unsigned char boot_lua[] =
 	0x09, 0x09, 0x65, 0x6e, 0x64, 0x2c, 0x0a,
 	0x09, 0x09, 0x72, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 
 	0x6e, 0x28, 0x77, 0x2c, 0x20, 0x68, 0x29, 0x0a,
-	0x09, 0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6f, 0x77, 0x2c, 0x20, 0x6f, 0x68, 0x2c, 0x20, 0x66, 
-	0x6c, 0x61, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 
-	0x2e, 0x67, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x28, 0x29, 0x0a,
-	0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2e, 0x72, 0x65, 0x73, 0x69, 0x7a, 0x61, 
-	0x62, 0x6c, 0x65, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
-	0x09, 0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x73, 0x65, 
-	0x74, 0x4d, 0x6f, 0x64, 0x65, 0x28, 0x77, 0x2c, 0x20, 0x68, 0x2c, 0x20, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x29, 0x0a,
-	0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x72, 0x65, 0x73, 0x69, 0x7a, 0x65, 
-	0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 
-	0x72, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x28, 0x77, 0x2c, 0x20, 0x68, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
-	0x09, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
+	0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x72, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x20, 
+	0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x72, 
+	0x65, 0x73, 0x69, 0x7a, 0x65, 0x28, 0x77, 0x2c, 0x20, 0x68, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
 	0x09, 0x09, 0x65, 0x6e, 0x64, 0x2c, 0x0a,
 	0x09, 0x7d, 0x2c, 0x20, 0x7b, 0x0a,
 	0x09, 0x09, 0x5f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 
@@ -471,8 +511,14 @@ const unsigned char boot_lua[] =
 	0x09, 0x09, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x3d, 0x20, 0x7b, 0x0a,
 	0x09, 0x09, 0x09, 0x77, 0x69, 0x64, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x38, 0x30, 0x30, 0x2c, 0x0a,
 	0x09, 0x09, 0x09, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x20, 0x3d, 0x20, 0x36, 0x30, 0x30, 0x2c, 0x0a,
+	0x09, 0x09, 0x09, 0x6d, 0x69, 0x6e, 0x77, 0x69, 0x64, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x31, 0x30, 0x30, 0x2c, 0x0a,
+	0x09, 0x09, 0x09, 0x6d, 0x69, 0x6e, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x20, 0x3d, 0x20, 0x31, 0x30, 0x30, 
+	0x2c, 0x0a,
 	0x09, 0x09, 0x09, 0x66, 0x75, 0x6c, 0x6c, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x61, 
 	0x6c, 0x73, 0x65, 0x2c, 0x0a,
+	0x09, 0x09, 0x09, 0x66, 0x75, 0x6c, 0x6c, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x74, 0x79, 0x70, 0x65, 0x20, 
+	0x3d, 0x20, 0x22, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x22, 0x2c, 0x0a,
+	0x09, 0x09, 0x09, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x20, 0x3d, 0x20, 0x31, 0x2c, 0x0a,
 	0x09, 0x09, 0x09, 0x76, 0x73, 0x79, 0x6e, 0x63, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x0a,
 	0x09, 0x09, 0x09, 0x66, 0x73, 0x61, 0x61, 0x20, 0x3d, 0x20, 0x30, 0x2c, 0x0a,
 	0x09, 0x09, 0x09, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x20, 0x3d, 0x20, 0x66, 0x61, 
@@ -497,6 +543,7 @@ const unsigned char boot_lua[] =
 	0x09, 0x09, 0x09, 0x6d, 0x61, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x0a,
 	0x09, 0x09, 0x09, 0x70, 0x68, 0x79, 0x73, 0x69, 0x63, 0x73, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x0a,
 	0x09, 0x09, 0x09, 0x73, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x0a,
+	0x09, 0x09, 0x09, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x0a,
 	0x09, 0x09, 0x09, 0x66, 0x6f, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x0a,
 	0x09, 0x09, 0x09, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x0a,
 	0x09, 0x09, 0x09, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x0a,
@@ -553,6 +600,7 @@ const unsigned char boot_lua[] =
 	0x09, 0x09, 0x22, 0x6a, 0x6f, 0x79, 0x73, 0x74, 0x69, 0x63, 0x6b, 0x22, 0x2c, 0x0a,
 	0x09, 0x09, 0x22, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x22, 0x2c, 0x0a,
 	0x09, 0x09, 0x22, 0x73, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x2c, 0x0a,
+	0x09, 0x09, 0x22, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x22, 0x2c, 0x0a,
 	0x09, 0x09, 0x22, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x22, 0x2c, 0x0a,
 	0x09, 0x09, 0x22, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x22, 0x2c, 0x0a,
 	0x09, 0x09, 0x22, 0x66, 0x6f, 0x6e, 0x74, 0x22, 0x2c, 0x0a,
@@ -590,7 +638,8 @@ const unsigned char boot_lua[] =
 	0x68, 0x65, 0x63, 0x6b, 0x4d, 0x6f, 0x64, 0x65, 0x28, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 
 	0x77, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x68, 0x65, 
 	0x69, 0x67, 0x68, 0x74, 0x2c, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x66, 0x75, 0x6c, 
-	0x6c, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x29, 0x20, 0x6f, 0x72, 0x20, 0x28, 0x63, 0x2e, 0x77, 0x69, 0x6e, 
+	0x6c, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x2c, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 
+	0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x29, 0x20, 0x6f, 0x72, 0x20, 0x28, 0x63, 0x2e, 0x77, 0x69, 0x6e, 
 	0x64, 0x6f, 0x77, 0x2e, 0x77, 0x69, 0x64, 0x74, 0x68, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x20, 0x61, 0x6e, 0x64, 
 	0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x20, 0x3d, 
 	0x3d, 0x20, 0x30, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
@@ -602,17 +651,26 @@ const unsigned char boot_lua[] =
 	0x09, 0x09, 0x09, 0x09, 0x66, 0x75, 0x6c, 0x6c, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x3d, 0x20, 0x63, 
 	0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x66, 0x75, 0x6c, 0x6c, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 
 	0x2c, 0x0a,
+	0x09, 0x09, 0x09, 0x09, 0x66, 0x75, 0x6c, 0x6c, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x74, 0x79, 0x70, 0x65, 
+	0x20, 0x3d, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x66, 0x75, 0x6c, 0x6c, 0x73, 0x63, 
+	0x72, 0x65, 0x65, 0x6e, 0x74, 0x79, 0x70, 0x65, 0x2c, 0x0a,
 	0x09, 0x09, 0x09, 0x09, 0x76, 0x73, 0x79, 0x6e, 0x63, 0x20, 0x3d, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 
 	0x6f, 0x77, 0x2e, 0x76, 0x73, 0x79, 0x6e, 0x63, 0x2c, 0x0a,
 	0x09, 0x09, 0x09, 0x09, 0x66, 0x73, 0x61, 0x61, 0x20, 0x3d, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 
 	0x77, 0x2e, 0x66, 0x73, 0x61, 0x61, 0x2c, 0x0a,
 	0x09, 0x09, 0x09, 0x09, 0x72, 0x65, 0x73, 0x69, 0x7a, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x63, 0x2e, 
 	0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x72, 0x65, 0x73, 0x69, 0x7a, 0x61, 0x62, 0x6c, 0x65, 0x2c, 0x0a,
+	0x09, 0x09, 0x09, 0x09, 0x6d, 0x69, 0x6e, 0x77, 0x69, 0x64, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x63, 0x2e, 0x77, 
+	0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x6d, 0x69, 0x6e, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x0a,
+	0x09, 0x09, 0x09, 0x09, 0x6d, 0x69, 0x6e, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x20, 0x3d, 0x20, 0x63, 0x2e, 
+	0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x6d, 0x69, 0x6e, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2c, 0x0a,
 	0x09, 0x09, 0x09, 0x09, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x20, 0x3d, 0x20, 0x63, 
 	0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 
 	0x2c, 0x0a,
 	0x09, 0x09, 0x09, 0x09, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x63, 0x2e, 0x77, 
 	0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x65, 0x64, 0x2c, 0x0a,
+	0x09, 0x09, 0x09, 0x09, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x20, 0x3d, 0x20, 0x63, 0x2e, 0x77, 0x69, 
+	0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x2c, 0x0a,
 	0x09, 0x09, 0x09, 0x7d, 0x29, 0x2c, 0x20, 0x22, 0x43, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6e, 0x6f, 0x74, 0x20, 
 	0x73, 0x65, 0x74, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0x29, 0x0a,
 	0x09, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x0a,