Преглед изворни кода

review Stage/System (split per platform) -- might need .cpp version for lime

ncannasse пре 8 година
родитељ
комит
61d6c0067f
14 измењених фајлова са 1280 додато и 1255 уклоњено
  1. 1 1
      h2d/Interactive.hx
  2. 2 2
      h3d/Engine.hx
  3. 1 1
      h3d/impl/Benchmark.hx
  4. 1 1
      h3d/scene/Interactive.hx
  5. 54 0
      hxd/Cursor.hx
  6. 291 0
      hxd/Stage.flash.hx
  7. 266 0
      hxd/Stage.hl.hx
  8. 26 438
      hxd/Stage.hx
  9. 187 0
      hxd/Stage.js.hx
  10. 166 0
      hxd/System.flash.hx
  11. 152 0
      hxd/System.hl.hx
  12. 31 811
      hxd/System.hx
  13. 94 0
      hxd/System.js.hx
  14. 8 1
      samples/GpuParticles.hx

+ 1 - 1
h2d/Interactive.hx

@@ -4,7 +4,7 @@ class Interactive extends Drawable implements hxd.SceneEvents.Interactive {
 
 	public var width : Float;
 	public var height : Float;
-	public var cursor(default,set) : hxd.System.Cursor;
+	public var cursor(default,set) : hxd.Cursor;
 	public var isEllipse : Bool;
 	/**
 		Set the default `cancel` mode (see `hxd.Event`), default to false.

+ 2 - 2
h3d/Engine.hx

@@ -48,7 +48,7 @@ class Engine {
 		this.hardware = hardware;
 		this.antiAlias = aa;
 		this.autoResize = true;
-		fullScreen = !hxd.System.isWindowed;
+		fullScreen = !hxd.System.getValue(IsWindowed);
 		var stage = hxd.Stage.getInstance();
 		realFps = hxd.System.getDefaultFrameRate();
 		lastTime = haxe.Timer.stamp();
@@ -238,7 +238,7 @@ class Engine {
 
 	function set_fullScreen(v) {
 		fullScreen = v;
-		if( mem != null && hxd.System.isWindowed )
+		if( mem != null && hxd.System.getValue(IsWindowed) )
 			hxd.Stage.getInstance().setFullScreen(v);
 		return v;
 	}

+ 1 - 1
h3d/impl/Benchmark.hx

@@ -215,7 +215,7 @@ class Benchmark extends h2d.Graphics {
 			if( estimateWait ) {
 				var waitT = frameTime - totalTime;
 				if( waitT > 0 ) {
-					if( hxd.System.isVSync() ) {
+					if( hxd.Stage.getInstance().vsync ) {
 						var vst = 1e9 / hxd.System.getDefaultFrameRate() - totalTime;
 						if( vst > waitT ) vst = waitT;
 						if( vst > 0 ) {

+ 1 - 1
h3d/scene/Interactive.hx

@@ -3,7 +3,7 @@ package h3d.scene;
 class Interactive extends Object implements hxd.SceneEvents.Interactive {
 
 	public var shape : h3d.col.Collider;
-	public var cursor(default,set) : hxd.System.Cursor;
+	public var cursor(default,set) : hxd.Cursor;
 	/**
 		Set the default `cancel` mode (see `hxd.Event`), default to false.
 	**/

+ 54 - 0
hxd/Cursor.hx

@@ -0,0 +1,54 @@
+package hxd;
+
+enum Cursor {
+	Default;
+	Button;
+	Move;
+	TextInput;
+	Hide;
+	Custom( custom : CustomCursor );
+}
+
+@:allow(hxd.System)
+class CustomCursor {
+
+	var frames : Array<hxd.BitmapData>;
+	var speed : Float;
+	var offsetX : Int;
+	var offsetY : Int;
+	#if hlsdl
+	var alloc : sdl.Cursor;
+	#elseif flash
+	static var UID = 0;
+	var name : String;
+	var alloc : flash.ui.MouseCursorData;
+	#end
+
+	public function new( frames, speed, offsetX, offsetY ) {
+		this.frames = frames;
+		this.speed = speed;
+		this.offsetX = offsetX;
+		this.offsetY = offsetY;
+		#if flash
+		name = "custom_" + UID++;
+		#end
+	}
+
+	public function dispose() {
+		for( f in frames )
+			f.dispose();
+		frames = [];
+		#if hlsdl
+		if( alloc != null ) {
+			alloc.free();
+			alloc = null;
+		}
+		#elseif flash
+		if( alloc != null ) {
+			flash.ui.Mouse.unregisterCursor(name);
+			alloc = null;
+		}
+		#end
+	}
+
+}

+ 291 - 0
hxd/Stage.flash.hx

@@ -0,0 +1,291 @@
+package hxd;
+
+class Stage {
+
+	var resizeEvents : List<Void -> Void>;
+	var eventTargets : List<Event -> Void>;
+
+	public var width(get, null) : Int;
+	public var height(get, null) : Int;
+	public var mouseX(get, null) : Int;
+	public var mouseY(get, null) : Int;
+	public var mouseLock(get, set) : Bool;
+	public var vsync(get, set) : Bool;
+
+	// FLASH
+	var stage : flash.display.Stage;
+	var fsDelayed : Bool;
+
+	function new() : Void {
+		eventTargets = new List();
+		resizeEvents = new List();
+
+		stage = flash.Lib.current.stage;
+		stage.scaleMode = flash.display.StageScaleMode.NO_SCALE;
+		stage.addEventListener(flash.events.Event.RESIZE, onResize);
+		initGesture(false);
+		if( isAir() )
+			setupOnCloseEvent();
+	}
+
+	inline function isAir() {
+		return @:privateAccess hxd.System.isAir();
+	}
+
+	function initGesture(b) {
+		if( hxd.System.getValue(IsMobile) ) {
+			if( b )  {
+				flash.ui.Multitouch.inputMode = flash.ui.MultitouchInputMode.GESTURE;
+				stage.removeEventListener(flash.events.TouchEvent.TOUCH_BEGIN, onTouchDown);
+				stage.removeEventListener(flash.events.TouchEvent.TOUCH_MOVE, onTouchMove);
+				stage.removeEventListener(flash.events.TouchEvent.TOUCH_END, onTouchUp);
+				stage.addEventListener(flash.events.MouseEvent.MOUSE_DOWN, onMouseDown);
+				stage.addEventListener(flash.events.MouseEvent.MOUSE_MOVE, onMouseMove);
+				stage.addEventListener(flash.events.MouseEvent.MOUSE_UP, onMouseUp);
+			} else {
+				flash.ui.Multitouch.inputMode = flash.ui.MultitouchInputMode.TOUCH_POINT;
+				stage.addEventListener(flash.events.TouchEvent.TOUCH_BEGIN, onTouchDown);
+				stage.addEventListener(flash.events.TouchEvent.TOUCH_MOVE, onTouchMove);
+				stage.addEventListener(flash.events.TouchEvent.TOUCH_END, onTouchUp);
+				stage.removeEventListener(flash.events.MouseEvent.MOUSE_DOWN, onMouseDown);
+				stage.removeEventListener(flash.events.MouseEvent.MOUSE_MOVE, onMouseMove);
+				stage.removeEventListener(flash.events.MouseEvent.MOUSE_UP, onMouseUp);
+			}
+		} else {
+			stage.addEventListener(flash.events.MouseEvent.MOUSE_DOWN, onMouseDown);
+			stage.addEventListener(flash.events.MouseEvent.MOUSE_MOVE, onMouseMove);
+			stage.addEventListener(flash.events.MouseEvent.MOUSE_UP, onMouseUp);
+			stage.addEventListener(flash.events.MouseEvent.MOUSE_WHEEL, onMouseWheel);
+			stage.addEventListener(flash.events.KeyboardEvent.KEY_DOWN, onKeyDown);
+			stage.addEventListener(flash.events.KeyboardEvent.KEY_UP, onKeyUp);
+			stage.addEventListener(flash.events.MouseEvent.RIGHT_MOUSE_DOWN, onRMouseDown);
+			stage.addEventListener(flash.events.MouseEvent.RIGHT_MOUSE_UP, onRMouseUp);
+		}
+	}
+
+	public dynamic function onClose() : Bool {
+		return true;
+	}
+
+	public function event( e : hxd.Event ) : Void {
+		for( et in eventTargets )
+			et(e);
+	}
+
+	public function addEventTarget( et : Event->Void ) : Void {
+		eventTargets.add(et);
+	}
+
+	public function removeEventTarget( et : Event->Void ) : Void {
+		for( e in eventTargets )
+			if( Reflect.compareMethods(e,et) ) {
+				eventTargets.remove(e);
+				break;
+			}
+	}
+
+	public function addResizeEvent( f : Void -> Void ) : Void {
+		resizeEvents.push(f);
+	}
+
+	public function removeResizeEvent( f : Void -> Void ) : Void {
+		for( e in resizeEvents )
+			if( Reflect.compareMethods(e,f) ) {
+				resizeEvents.remove(f);
+				break;
+			}
+	}
+
+	function onResize(e:Dynamic) : Void {
+		for( r in resizeEvents )
+			r();
+	}
+
+	public function resize( width : Int, height : Int ) : Void {
+	}
+
+	public function setFullScreen( v : Bool ) : Void {
+		var isAir = isAir();
+		var state = v ? (isAir ? flash.display.StageDisplayState.FULL_SCREEN_INTERACTIVE : flash.display.StageDisplayState.FULL_SCREEN) : flash.display.StageDisplayState.NORMAL;
+		if( stage.displayState != state ) {
+			var t = flash.Lib.getTimer();
+			// delay first fullsrceen toggle on OSX/Air to prevent the command window to spawn over
+			if( v && isAir && t < 5000 && !fsDelayed && flash.system.Capabilities.os.indexOf("Mac") != -1 ) {
+				fsDelayed = true;
+				haxe.Timer.delay(function() this.setFullScreen(v), 1000);
+				return;
+			}
+			stage.displayState = state;
+		}
+	}
+
+	static var inst = null;
+	public static function getInstance() : Stage {
+		if( inst == null ) inst = new Stage();
+		return inst;
+	}
+
+	function setupOnCloseEvent() {
+		var nw : flash.events.EventDispatcher = Reflect.field(stage, "nativeWindow");
+		if( nw == null ) return;
+		nw.addEventListener("closing", function(e:flash.events.Event) {
+			if( !onClose() )
+				e.preventDefault();
+		});
+	}
+
+	#if !air3 static inline #end
+	var multipleWindowsSupport = false;
+	var lastX : Float = -1.;
+	var lastY : Float = -1.;
+
+	inline function get_mouseX() {
+		return Std.int( multipleWindowsSupport ? lastX : stage.mouseX );
+	}
+
+	inline function get_mouseY() {
+		return Std.int( multipleWindowsSupport ? lastY : stage.mouseY );
+	}
+
+	inline function get_width() {
+		return stage.stageWidth;
+	}
+
+	inline function get_height() {
+		return stage.stageHeight;
+	}
+
+	inline function get_mouseLock() {
+		return stage.mouseLock;
+	}
+
+	inline function set_mouseLock(v) {
+		return stage.mouseLock = v;
+	}
+
+	function get_vsync() : Bool return true;
+
+	function set_vsync( b : Bool ) : Bool {
+		if( !b ) throw "Can't disable vsync on this platform";
+		return true;
+	}
+
+	function onMouseDown(e:Dynamic) {
+		event(new Event(EPush, mouseX, mouseY));
+	}
+
+	function onRMouseDown(e:Dynamic) {
+		var e = new Event(EPush, mouseX, mouseY);
+		e.button = 1;
+		event(e);
+	}
+
+	function onMouseUp(e:Dynamic) {
+		event(new Event(ERelease, mouseX, mouseY));
+	}
+
+	function onRMouseUp(e:Dynamic) {
+		var e = new Event(ERelease, mouseX, mouseY);
+		e.button = 1;
+		event(e);
+	}
+
+	function onMouseMove(e:flash.events.MouseEvent) {
+		if( multipleWindowsSupport ) {
+			lastX = e.stageX;
+			lastY = e.stageY;
+		}
+		event(new Event(EMove, mouseX, mouseY));
+	}
+
+	function onMouseWheel(e:flash.events.MouseEvent) {
+		var ev = new Event(EWheel, mouseX, mouseY);
+		ev.wheelDelta = -e.delta / 3.0;
+		event(ev);
+	}
+
+	function onKeyUp(e:flash.events.KeyboardEvent) {
+		var ev = new Event(EKeyUp, mouseX, mouseY);
+		ev.keyCode = e.keyCode;
+		ev.charCode = getCharCode(e);
+		event(ev);
+	}
+
+	function onKeyDown(e:flash.events.KeyboardEvent) {
+		var ev = new Event(EKeyDown, mouseX, mouseY);
+		ev.keyCode = e.keyCode;
+		ev.charCode = getCharCode(e);
+		event(ev);
+
+		// prevent escaping fullscreen in air
+		if( e.keyCode == flash.ui.Keyboard.ESCAPE ) e.preventDefault();
+		if( e.keyCode == "S".code && e.ctrlKey ) e.preventDefault();
+		// prevent ALT menu (sadly DONT WORK)
+		if( e.keyCode == 18 ) {
+			e.preventDefault();
+			e.stopImmediatePropagation();
+			e.stopPropagation();
+		}
+		// prevent back exiting app in mobile
+		if( e.keyCode == flash.ui.Keyboard.BACK ) {
+			e.preventDefault();
+			e.stopImmediatePropagation();
+		}
+	}
+
+	function getCharCode( e : flash.events.KeyboardEvent ) {
+		// disable some invalid charcodes
+		if( e.keyCode == 27 ) e.charCode = 0;
+		// Flash charCode are not valid, they assume an english keyboard. Let's do some manual translation here (to complete with command keyboards)
+		switch( flash.system.Capabilities.language ) {
+		case "fr":
+			return switch( e.keyCode ) {
+			case 49: if( e.altKey ) 0 else if( e.shiftKey ) '1'.code else e.charCode;
+			case 50: if( e.altKey ) '~'.code else if( e.shiftKey ) '2'.code else e.charCode;
+			case 51: if( e.altKey ) '#'.code else if( e.shiftKey ) '3'.code else e.charCode;
+			case 52: if( e.altKey ) '{'.code else if( e.shiftKey ) '4'.code else e.charCode;
+			case 53: if( e.altKey ) '['.code else if( e.shiftKey ) '5'.code else e.charCode;
+			case 54: if( e.altKey ) '|'.code else if( e.shiftKey ) '6'.code else e.charCode;
+			case 55: if( e.altKey ) '`'.code else if( e.shiftKey ) '7'.code else e.charCode;
+			case 56: if( e.altKey ) '\\'.code else if( e.shiftKey ) '8'.code else e.charCode;
+			case 57: if( e.altKey ) '^'.code else if( e.shiftKey ) '9'.code else e.charCode;
+			case 48: if( e.altKey ) '@'.code else if( e.shiftKey ) '0'.code else e.charCode;
+			case 219: if( e.altKey ) ']'.code else if( e.shiftKey ) '°'.code else e.charCode;
+			case 187: if( e.altKey ) '}'.code else if( e.shiftKey ) '+'.code else e.charCode;
+			case 188: if( e.altKey ) 0 else if( e.shiftKey ) '?'.code else e.charCode;
+			case 190: if( e.altKey ) 0 else if( e.shiftKey ) '.'.code else e.charCode;
+			case 191: if( e.altKey ) 0 else if( e.shiftKey ) '/'.code else e.charCode;
+			case 223: if( e.altKey ) 0 else if( e.shiftKey ) '§'.code else e.charCode;
+			case 192: if( e.altKey ) 0 else if( e.shiftKey ) '%'.code else e.charCode;
+			case 220: if( e.altKey ) 0 else if( e.shiftKey ) 'µ'.code else e.charCode;
+			case 221: if( e.altKey ) 0 else if( e.shiftKey ) '¨'.code else '^'.code;
+			case 186: if( e.altKey ) '¤'.code else if( e.shiftKey ) '£'.code else e.charCode;
+			default:
+				e.charCode;
+			}
+		default:
+			return e.charCode;
+		}
+	}
+
+	function onTouchDown(e:flash.events.TouchEvent) {
+		var ev = new Event(EPush, e.localX, e.localY);
+		ev.touchId = e.touchPointID;
+		event(ev);
+	}
+
+	function onTouchUp(e:flash.events.TouchEvent) {
+		var ev = new Event(ERelease, e.localX, e.localY);
+		ev.touchId = e.touchPointID;
+		event(ev);
+	}
+
+	function onTouchMove(e:flash.events.TouchEvent) {
+		var ev = new Event(EMove, e.localX, e.localY);
+		ev.touchId = e.touchPointID;
+		event(ev);
+	}
+
+
+}
+

+ 266 - 0
hxd/Stage.hl.hx

@@ -0,0 +1,266 @@
+package hxd;
+import hxd.Key in K;
+
+//@:coreApi
+class Stage {
+
+	var resizeEvents : List<Void -> Void>;
+	var eventTargets : List<Event -> Void>;
+
+	public var width(get, null) : Int;
+	public var height(get, null) : Int;
+	public var mouseX(get, null) : Int;
+	public var mouseY(get, null) : Int;
+	public var mouseLock(get, set) : Bool;
+	public var vsync(get, set) : Bool;
+
+	var window : sdl.Window;
+	var fullScreenMode : sdl.Window.DisplayMode = Borderless;
+	var windowWidth = 800;
+	var windowHeight = 600;
+	var curMouseX = 0;
+	var curMouseY = 0;
+	var shiftDown : Bool;
+
+	static var CODEMAP = [for( i in 0...2048 ) i];
+	static var CHARMAP = [for( i in 0...2048 ) 0];
+
+	function new(title:String, width:Int, height:Int) {
+		this.windowWidth = width;
+		this.windowHeight = height;
+		eventTargets = new List();
+		resizeEvents = new List();
+		window = new sdl.Window(title, width, height);
+	}
+
+	public dynamic function onClose() : Bool {
+		return true;
+	}
+
+	public function event( e : hxd.Event ) : Void {
+		for( et in eventTargets )
+			et(e);
+	}
+
+	public function addEventTarget(et : Event -> Void) : Void {
+		eventTargets.add(et);
+	}
+
+	public function removeEventTarget(et : Event -> Void) : Void {
+		for( e in eventTargets )
+			if( Reflect.compareMethods(e,et) ) {
+				eventTargets.remove(e);
+				break;
+			}
+	}
+
+	public function addResizeEvent( f : Void -> Void ) : Void {
+		resizeEvents.push(f);
+	}
+
+	public function removeResizeEvent( f : Void -> Void ) : Void {
+		for( e in resizeEvents )
+			if( Reflect.compareMethods(e,f) ) {
+				resizeEvents.remove(f);
+				break;
+			}
+	}
+
+	function onResize(e:Dynamic) : Void {
+		for( r in resizeEvents )
+			r();
+	}
+
+	public function resize( width : Int, height : Int ) : Void {
+		window.resize(width, height);
+	}
+
+	public function setFullScreen( v : Bool ) : Void {
+		window.displayMode = v ? fullScreenMode : Windowed;
+	}
+
+	function get_mouseX() : Int {
+		return curMouseX;
+	}
+
+	function get_mouseY() : Int {
+		return curMouseY;
+	}
+
+	function get_width() : Int {
+		return windowWidth;
+	}
+
+	function get_height() : Int {
+		return windowHeight;
+	}
+
+	function get_mouseLock() : Bool {
+		return false;
+	}
+
+	function get_vsync() : Bool return window.vsync;
+
+	function set_vsync( b : Bool ) : Bool {
+		window.vsync = b;
+		return b;
+	}
+
+	function onEvent( e : sdl.Event ) : Void {
+		var eh = null;
+		switch( e.type ) {
+		case WindowState:
+			switch( e.state ) {
+			case Resize:
+				windowWidth = window.width;
+				windowHeight = window.height;
+				onResize(null);
+			default:
+			}
+		case MouseDown:
+			mouseX = e.mouseX;
+			mouseY = e.mouseY;
+			eh = new Event(EPush, e.mouseX, e.mouseY);
+			// middle button -> 2 / right button -> 1
+			eh.button = switch( e.button - 1 ) {
+			case 0: 0;
+			case 1: 2;
+			case 2: 1;
+			case x: x;
+			}
+		case MouseUp:
+			mouseX = e.mouseX;
+			mouseY = e.mouseY;
+			eh = new Event(ERelease, e.mouseX, e.mouseY);
+			eh.button = switch( e.button - 1 ) {
+			case 0: 0;
+			case 1: 2;
+			case 2: 1;
+			case x: x;
+			};
+		case MouseMove:
+			mouseX = e.mouseX;
+			mouseY = e.mouseY;
+			eh = new Event(EMove, e.mouseX, e.mouseY);
+		case KeyDown:
+			eh = new Event(EKeyDown);
+			if( e.keyCode & (1 << 30) != 0 ) e.keyCode = (e.keyCode & ((1 << 30) - 1)) + 1000;
+			eh.keyCode = CODEMAP[e.keyCode];
+			eh.charCode = CHARMAP[e.keyCode];
+			if( eh.charCode == ":".code && shiftDown )
+				eh.charCode = "/".code;
+			if( eh.charCode >= 'a'.code && eh.charCode <= 'z'.code && shiftDown )
+				eh.charCode += 'A'.code - 'a'.code;
+			if( eh.keyCode & (K.LOC_LEFT | K.LOC_RIGHT) != 0 ) {
+				e.keyCode = eh.keyCode & 0xFF;
+				if( e.keyCode == K.SHIFT ) shiftDown = true;
+				onEvent(e);
+			}
+		case KeyUp:
+			eh = new Event(EKeyUp);
+			if( e.keyCode & (1 << 30) != 0 ) e.keyCode = (e.keyCode & ((1 << 30) - 1)) + 1000;
+			eh.keyCode = CODEMAP[e.keyCode];
+			eh.charCode = CHARMAP[e.keyCode];
+			if( eh.charCode == ":".code && shiftDown )
+				eh.charCode = "/".code;
+			if( eh.charCode >= 'a'.code && eh.charCode <= 'z'.code && shiftDown )
+				eh.charCode += 'A'.code - 'a'.code;
+			if( eh.keyCode & (K.LOC_LEFT | K.LOC_RIGHT) != 0 ) {
+				e.keyCode = eh.keyCode & 0xFF;
+				if( e.keyCode == K.SHIFT ) shiftDown = false;
+				onEvent(e);
+			}
+		case MouseWheel:
+			eh = new Event(EWheel, mouseX, mouseY);
+			eh.wheelDelta = -e.wheelDelta;
+		case GControllerAdded, GControllerRemoved, GControllerUp, GControllerDown, GControllerAxis:
+			@:privateAccess hxd.Pad.onEvent( e );
+		default:
+		}
+		if( eh != null ) event(eh);
+	}
+
+
+	function set_mouseLock(v:Bool) : Bool {
+		if( v ) throw "Not implemented";
+		return false;
+	}
+
+
+	static function initChars() : Void {
+
+		inline function addKey(sdl, keyCode, charCode=0) {
+			CODEMAP[sdl] = keyCode;
+			if( charCode != 0 ) CHARMAP[sdl] = charCode;
+		}
+
+		/*
+			SDL 2.0 does not allow to get the charCode from a key event.
+			let's for now do a simple mapping, even if not very efficient
+		*/
+
+		// ASCII
+		CHARMAP[K.BACKSPACE] = 8;
+		CHARMAP[K.TAB] = 9;
+		CHARMAP[K.ENTER] = 13;
+		for( i in 32...127 )
+			CHARMAP[i] = i;
+		for( i in 0...26 )
+			addKey(97 + i, K.A + i);
+		for( i in 0...12 )
+			addKey(1058 + i, K.F1 + i);
+
+		// NUMPAD
+
+		addKey(1084, K.NUMPAD_DIV, "/".code);
+		addKey(1085, K.NUMPAD_MULT, "*".code);
+		addKey(1086, K.NUMPAD_SUB, "-".code);
+		addKey(1087, K.NUMPAD_ADD, "+".code);
+		addKey(1088, K.NUMPAD_ENTER, 13);
+		for( i in 0...9 )
+			addKey(1089 + i, K.NUMPAD_1 + i, "1".code + i);
+		addKey(1098, K.NUMPAD_0, "0".code);
+		addKey(1099, K.NUMPAD_DOT, ".".code);
+
+		// EXTRA
+		var keys = [
+			//K.BACKSPACE
+			//K.TAB
+			//K.ENTER
+			1225 => K.LSHIFT,
+			1229 => K.RSHIFT,
+			1224 => K.LCTRL,
+			1228 => K.RCTRL,
+			1226 => K.LALT,
+			1230 => K.RALT,
+			// K.ESCAPE
+			// K.SPACE
+			1075 => K.PGUP,
+			1078 => K.PGDOWN,
+			1077 => K.END,
+			1074 => K.HOME,
+			1080 => K.LEFT,
+			1082 => K.UP,
+			1079 => K.RIGHT,
+			1081 => K.DOWN,
+			1073 => K.INSERT,
+			127 => K.DELETE,
+			//K.NUMPAD_0-9
+			//K.A-Z
+			//K.F1-F12
+			1085 => K.NUMPAD_MULT,
+			1087 => K.NUMPAD_ADD,
+			1088 => K.NUMPAD_ENTER,
+			1086 => K.NUMPAD_SUB,
+			1099 => K.NUMPAD_DOT,
+			1084 => K.NUMPAD_DIV,
+		];
+		for( sdl in keys.keys() )
+			addKey(sdl, keys.get(sdl));
+	}
+
+	static var inst = null;
+	public static function getInstance() : Stage {
+		return inst;
+	}
+}

+ 26 - 438
hxd/Stage.hx

@@ -2,143 +2,35 @@ package hxd;
 
 class Stage {
 
-	/**
-		Touch is disabled by default and will be activated only if the screen has touch capacities (see hxd.System.isTouch).
-		For Flash/AIR Desktop, enabling touch has the effect of disabling normal mouse/keys events.
-	**/
-	public static var ENABLE_TOUCH = false;
-
-	#if (flash || openfl || nme)
-	var stage : flash.display.Stage;
-	var fsDelayed : Bool;
-	#elseif lime
-	var limeStage : hxd.impl.LimeStage;
-	#end
 	var resizeEvents : List<Void -> Void>;
 	var eventTargets : List<Event -> Void>;
 
-	#if (js && !lime)
-	@:allow(hxd)
-	static function getCanvas() {
-		var canvas : js.html.CanvasElement = cast js.Browser.document.getElementById("webgl");
-		if( canvas == null ) throw "Missing canvas#webgl";
-		return canvas;
-	}
-	var canvas : js.html.CanvasElement;
-	var canvasPos : { var width(default, never) : Float; var height(default, never) : Float; var left(default, never) : Float; var top(default, never) : Float; };
-	#end
-
 	public var width(get, null) : Int;
 	public var height(get, null) : Int;
 	public var mouseX(get, null) : Int;
 	public var mouseY(get, null) : Int;
 	public var mouseLock(get, set) : Bool;
+	public var vsync(get, set) : Bool;
 
-	function new() {
+	function new() : Void {
 		eventTargets = new List();
 		resizeEvents = new List();
-		#if (flash || openfl || nme)
-		stage = flash.Lib.current.stage;
-		stage.scaleMode = flash.display.StageScaleMode.NO_SCALE;
-		stage.addEventListener(flash.events.Event.RESIZE, onResize);
-		initGesture(false);
-		#elseif lime
-		limeStage = new hxd.impl.LimeStage( this );
-		lime.app.Application.current.addModule( limeStage );
-		#elseif js
-		canvas = getCanvas();
-		canvasPos = canvas.getBoundingClientRect();
-		js.Browser.window.addEventListener("mousedown", onMouseDown);
-		js.Browser.window.addEventListener("mousemove", onMouseMove);
-		js.Browser.window.addEventListener("mouseup", onMouseUp);
-		js.Browser.window.addEventListener("mousewheel", onMouseWheel);
-		js.Browser.window.addEventListener("keydown", onKeyDown);
-		js.Browser.window.addEventListener("keyup", onKeyUp);
-		canvas.addEventListener("mousedown", function(e) {
-			onMouseDown(e);
-			e.stopPropagation();
-			e.preventDefault();
-		});
-		canvas.oncontextmenu = function(e){
-			e.stopPropagation();
-			e.preventDefault();
-			return false;
-		};
-		var curW = this.width, curH = this.height;
-		var t0 = new haxe.Timer(100);
-		t0.run = function() {
-			canvasPos = canvas.getBoundingClientRect();
-			var cw = this.width, ch = this.height;
-			if( curW != cw || curH != ch ) {
-				curW = cw;
-				curH = ch;
-				onResize(null);
-			}
-		};
-		#end
-		#if flash
-		if( untyped hxd.System.isAir() )
-			setupOnCloseEvent();
-		#end
-	}
-
-	#if (flash || openfl)
-
-	function initGesture(b) {
-		if( hxd.System.isTouch && ENABLE_TOUCH ) {
-			if( b )  {
-				flash.ui.Multitouch.inputMode = flash.ui.MultitouchInputMode.GESTURE;
-				stage.removeEventListener(flash.events.TouchEvent.TOUCH_BEGIN, onTouchDown);
-				stage.removeEventListener(flash.events.TouchEvent.TOUCH_MOVE, onTouchMove);
-				stage.removeEventListener(flash.events.TouchEvent.TOUCH_END, onTouchUp);
-				stage.addEventListener(flash.events.MouseEvent.MOUSE_DOWN, onMouseDown);
-				stage.addEventListener(flash.events.MouseEvent.MOUSE_MOVE, onMouseMove);
-				stage.addEventListener(flash.events.MouseEvent.MOUSE_UP, onMouseUp);
-			} else {
-				flash.ui.Multitouch.inputMode = flash.ui.MultitouchInputMode.TOUCH_POINT;
-				stage.addEventListener(flash.events.TouchEvent.TOUCH_BEGIN, onTouchDown);
-				stage.addEventListener(flash.events.TouchEvent.TOUCH_MOVE, onTouchMove);
-				stage.addEventListener(flash.events.TouchEvent.TOUCH_END, onTouchUp);
-				stage.removeEventListener(flash.events.MouseEvent.MOUSE_DOWN, onMouseDown);
-				stage.removeEventListener(flash.events.MouseEvent.MOUSE_MOVE, onMouseMove);
-				stage.removeEventListener(flash.events.MouseEvent.MOUSE_UP, onMouseUp);
-			}
-		} else {
-			stage.addEventListener(flash.events.MouseEvent.MOUSE_DOWN, onMouseDown);
-			stage.addEventListener(flash.events.MouseEvent.MOUSE_MOVE, onMouseMove);
-			stage.addEventListener(flash.events.MouseEvent.MOUSE_UP, onMouseUp);
-			stage.addEventListener(flash.events.MouseEvent.MOUSE_WHEEL, onMouseWheel);
-			stage.addEventListener(flash.events.KeyboardEvent.KEY_DOWN, onKeyDown);
-			stage.addEventListener(flash.events.KeyboardEvent.KEY_UP, onKeyUp);
-			stage.addEventListener(flash.events.MouseEvent.RIGHT_MOUSE_DOWN, onRMouseDown);
-			stage.addEventListener(flash.events.MouseEvent.RIGHT_MOUSE_UP, onRMouseUp);
-		}
 	}
 
-	function setupOnCloseEvent() {
-		var nw : flash.events.EventDispatcher = Reflect.field(stage, "nativeWindow");
-		if( nw == null ) return;
-		nw.addEventListener("closing", function(e:flash.events.Event) {
-			if( !onClose() )
-				e.preventDefault();
-		});
-	}
-	#end
-
-	public dynamic function onClose() {
+	public dynamic function onClose() : Bool {
 		return true;
 	}
 
-	public function event( e : hxd.Event ) {
+	public function event( e : hxd.Event ) : Void {
 		for( et in eventTargets )
 			et(e);
 	}
 
-	public function addEventTarget(et) {
+	public function addEventTarget( et : Event->Void ) : Void {
 		eventTargets.add(et);
 	}
 
-	public function removeEventTarget(et) {
+	public function removeEventTarget( et : Event->Void ) : Void {
 		for( e in eventTargets )
 			if( Reflect.compareMethods(e,et) ) {
 				eventTargets.remove(e);
@@ -146,11 +38,11 @@ class Stage {
 			}
 	}
 
-	public function addResizeEvent( f : Void -> Void ) {
+	public function addResizeEvent( f : Void -> Void ) : Void {
 		resizeEvents.push(f);
 	}
 
-	public function removeResizeEvent( f : Void -> Void ) {
+	public function removeResizeEvent( f : Void -> Void ) : Void {
 		for( e in resizeEvents )
 			if( Reflect.compareMethods(e,f) ) {
 				resizeEvents.remove(f);
@@ -158,357 +50,53 @@ class Stage {
 			}
 	}
 
-	function onResize(e:Dynamic) {
+	function onResize(e:Dynamic) : Void {
 		for( r in resizeEvents )
 			r();
 	}
 
-	public function resize( width : Int, height : Int ) {
-		#if hxsdl
-		var win = @:privateAccess System.win;
-		win.resize(width, height);
-		#end
+	public function resize( width : Int, height : Int ) : Void {
 	}
 
-	#if hxsdl
-	var fullScreenMode : sdl.Window.DisplayMode = Borderless;
-	#end
-
-	public function setFullScreen( v : Bool ) {
-		#if flash
-		var isAir = flash.system.Capabilities.playerType == "Desktop";
-		var state = v ? (isAir ? flash.display.StageDisplayState.FULL_SCREEN_INTERACTIVE : flash.display.StageDisplayState.FULL_SCREEN) : flash.display.StageDisplayState.NORMAL;
-		if( stage.displayState != state ) {
-			var t = flash.Lib.getTimer();
-			// delay first fullsrceen toggle on OSX/Air to prevent the command window to spawn over
-			if( v && isAir && t < 5000 && !fsDelayed && flash.system.Capabilities.os.indexOf("Mac") != -1 ) {
-				fsDelayed = true;
-				haxe.Timer.delay(function() this.setFullScreen(v), 1000);
-				return;
-			}
-			stage.displayState = state;
-		}
-		#elseif hxsdl
-		var win = @:privateAccess System.win;
-		win.displayMode = v ? fullScreenMode : Windowed;
-		#end
+	public function setFullScreen( v : Bool ) : Void {
 	}
 
 	static var inst = null;
-	public static function getInstance() {
+	public static function getInstance() : Stage {
 		if( inst == null ) inst = new Stage();
 		return inst;
 	}
 
-#if (flash || openfl || nme)
-
-	#if !air3 static inline #end
-	var multipleWindowsSupport = false;
-	var lastX : Float = -1.;
-	var lastY : Float = -1.;
-
-	inline function get_mouseX() {
-		return Std.int( multipleWindowsSupport ? lastX : stage.mouseX );
-	}
-
-	inline function get_mouseY() {
-		return Std.int( multipleWindowsSupport ? lastY : stage.mouseY );
-	}
-
-	inline function get_width() {
-		return stage.stageWidth;
-	}
-
-	inline function get_height() {
-		return stage.stageHeight;
-	}
-
-	inline function get_mouseLock() {
-		#if cpp
-		return false;
-		#else
-		return stage.mouseLock;
-		#end
-	}
-
-	inline function set_mouseLock(v) {
-		#if cpp
-		return false;
-		#else
-		return stage.mouseLock = v;
-		#end
-	}
-
-	function onMouseDown(e:Dynamic) {
-		event(new Event(EPush, mouseX, mouseY));
-	}
-
-	function onRMouseDown(e:Dynamic) {
-		var e = new Event(EPush, mouseX, mouseY);
-		e.button = 1;
-		event(e);
-	}
-
-	function onMouseUp(e:Dynamic) {
-		event(new Event(ERelease, mouseX, mouseY));
-	}
-
-	function onRMouseUp(e:Dynamic) {
-		var e = new Event(ERelease, mouseX, mouseY);
-		e.button = 1;
-		event(e);
-	}
-
-	function onMouseMove(e:flash.events.MouseEvent) {
-		if( multipleWindowsSupport ) {
-			lastX = e.stageX;
-			lastY = e.stageY;
-		}
-		event(new Event(EMove, mouseX, mouseY));
-	}
-
-	function onMouseWheel(e:flash.events.MouseEvent) {
-		var ev = new Event(EWheel, mouseX, mouseY);
-		ev.wheelDelta = -e.delta / 3.0;
-		event(ev);
-	}
-
-	function onKeyUp(e:flash.events.KeyboardEvent) {
-		var ev = new Event(EKeyUp, mouseX, mouseY);
-		ev.keyCode = e.keyCode;
-		ev.charCode = getCharCode(e);
-		event(ev);
-	}
-
-	function onKeyDown(e:flash.events.KeyboardEvent) {
-		var ev = new Event(EKeyDown, mouseX, mouseY);
-		ev.keyCode = e.keyCode;
-		ev.charCode = getCharCode(e);
-		event(ev);
-		#if flash
-		// prevent escaping fullscreen in air
-		if( e.keyCode == flash.ui.Keyboard.ESCAPE ) e.preventDefault();
-		if( e.keyCode == "S".code && e.ctrlKey ) e.preventDefault();
-		// prevent ALT menu (sadly DONT WORK)
-		if( e.keyCode == 18 ) {
-			e.preventDefault();
-			e.stopImmediatePropagation();
-			e.stopPropagation();
-		}
-		// prevent back exiting app in mobile
-		if( e.keyCode == flash.ui.Keyboard.BACK ) {
-			e.preventDefault();
-			e.stopImmediatePropagation();
-		}
-		#end
-	}
-
-	function getCharCode( e : flash.events.KeyboardEvent ) {
-		#if cpp
-		return e.charCode;
-		#else
-		// disable some invalid charcodes
-		if( e.keyCode == 27 ) e.charCode = 0;
-		// Flash charCode are not valid, they assume an english keyboard. Let's do some manual translation here (to complete with command keyboards)
-		switch( flash.system.Capabilities.language ) {
-		case "fr":
-			return switch( e.keyCode ) {
-			case 49: if( e.altKey ) 0 else if( e.shiftKey ) '1'.code else e.charCode;
-			case 50: if( e.altKey ) '~'.code else if( e.shiftKey ) '2'.code else e.charCode;
-			case 51: if( e.altKey ) '#'.code else if( e.shiftKey ) '3'.code else e.charCode;
-			case 52: if( e.altKey ) '{'.code else if( e.shiftKey ) '4'.code else e.charCode;
-			case 53: if( e.altKey ) '['.code else if( e.shiftKey ) '5'.code else e.charCode;
-			case 54: if( e.altKey ) '|'.code else if( e.shiftKey ) '6'.code else e.charCode;
-			case 55: if( e.altKey ) '`'.code else if( e.shiftKey ) '7'.code else e.charCode;
-			case 56: if( e.altKey ) '\\'.code else if( e.shiftKey ) '8'.code else e.charCode;
-			case 57: if( e.altKey ) '^'.code else if( e.shiftKey ) '9'.code else e.charCode;
-			case 48: if( e.altKey ) '@'.code else if( e.shiftKey ) '0'.code else e.charCode;
-			case 219: if( e.altKey ) ']'.code else if( e.shiftKey ) '°'.code else e.charCode;
-			case 187: if( e.altKey ) '}'.code else if( e.shiftKey ) '+'.code else e.charCode;
-			case 188: if( e.altKey ) 0 else if( e.shiftKey ) '?'.code else e.charCode;
-			case 190: if( e.altKey ) 0 else if( e.shiftKey ) '.'.code else e.charCode;
-			case 191: if( e.altKey ) 0 else if( e.shiftKey ) '/'.code else e.charCode;
-			case 223: if( e.altKey ) 0 else if( e.shiftKey ) '§'.code else e.charCode;
-			case 192: if( e.altKey ) 0 else if( e.shiftKey ) '%'.code else e.charCode;
-			case 220: if( e.altKey ) 0 else if( e.shiftKey ) 'µ'.code else e.charCode;
-			case 221: if( e.altKey ) 0 else if( e.shiftKey ) '¨'.code else '^'.code;
-			case 186: if( e.altKey ) '¤'.code else if( e.shiftKey ) '£'.code else e.charCode;
-			default:
-				e.charCode;
-			}
-		default:
-			return e.charCode;
-		}
-		#end
-	}
-
-	function onTouchDown(e:flash.events.TouchEvent) {
-		var ev = new Event(EPush, e.localX, e.localY);
-		ev.touchId = e.touchPointID;
-		event(ev);
-	}
-
-	function onTouchUp(e:flash.events.TouchEvent) {
-		var ev = new Event(ERelease, e.localX, e.localY);
-		ev.touchId = e.touchPointID;
-		event(ev);
-	}
-
-	function onTouchMove(e:flash.events.TouchEvent) {
-		var ev = new Event(EMove, e.localX, e.localY);
-		ev.touchId = e.touchPointID;
-		event(ev);
-	}
-
-#elseif (js && !lime)
-
-	var curMouseX : Float = 0.;
-	var curMouseY : Float = 0.;
-
-	function get_width() {
-		return Math.round(canvasPos.width * js.Browser.window.devicePixelRatio);
-	}
-
-	function get_height() {
-		return Math.round(canvasPos.height * js.Browser.window.devicePixelRatio);
-	}
-
-	function get_mouseX() {
-		return Math.round((curMouseX - canvasPos.left) * js.Browser.window.devicePixelRatio);
-	}
-
-	function get_mouseY() {
-		return Math.round((curMouseY - canvasPos.top) * js.Browser.window.devicePixelRatio);
-	}
-
-	function get_mouseLock() {
-		return false;
-	}
-
-	function set_mouseLock(b) {
-		throw "Mouse lock not supported";
-		return false;
-	}
-
-	function onMouseDown(e:js.html.MouseEvent) {
-		var ev = new Event(EPush, mouseX, mouseY);
-		if (e.button == 2) ev.button = 1;
-		event(ev);
-	}
-
-	function onMouseUp(e:js.html.MouseEvent) {
-		var ev = new Event(ERelease, mouseX, mouseY);
-		if (e.button == 2) ev.button = 1;
-		event(ev);
-	}
-
-	function onMouseMove(e:js.html.MouseEvent) {
-		curMouseX = e.clientX;
-		curMouseY = e.clientY;
-		event(new Event(EMove, mouseX, mouseY));
-	}
-
-	function onMouseWheel(e:js.html.MouseEvent) {
-		var ev = new Event(EWheel, mouseX, mouseY);
-		ev.wheelDelta = untyped -e.wheelDelta / 30.0;
-		event(ev);
-	}
-
-	function onKeyUp(e:js.html.KeyboardEvent) {
-		var ev = new Event(EKeyUp, mouseX, mouseY);
-		ev.keyCode = e.keyCode;
-		ev.charCode = e.charCode;
-		event(ev);
-	}
-
-	function onKeyDown(e:js.html.KeyboardEvent) {
-		var ev = new Event(EKeyDown, mouseX, mouseY);
-		ev.keyCode = e.keyCode;
-		ev.charCode = e.charCode;
-		event(ev);
-	}
-
-#elseif hxsdl
-
-	function get_mouseX() {
-		return @:privateAccess System.mouseX;
-	}
-
-	function get_mouseY() {
-		return @:privateAccess System.mouseY;
-	}
-
-	function get_width() {
-		return @:privateAccess System.windowWidth;
-	}
-
-	function get_height() {
-		return @:privateAccess System.windowHeight;
-	}
-
-	function get_mouseLock() {
-		return false;
-	}
-
-	function set_mouseLock(b) {
-		if( b ) throw "Not implemented";
-		return b;
-	}
-
-#elseif lime
-
-	inline function get_mouseX() {
-		return limeStage.mouseX;
-	}
-
-	inline function get_mouseY() {
-		return limeStage.mouseY;
-	}
-
-	inline function get_width() {
-		return limeStage.width;
-	}
-
-	inline function get_height() {
-		return limeStage.height;
-	}
-
-	function get_mouseLock() {
-		return false;
-	}
-
-	function set_mouseLock(b) {
-		if( b ) throw "Not implemented";
-		return b;
-	}
-
-#else
-
-	function get_mouseX() {
+	function get_mouseX() : Int {
 		return 0;
 	}
 
-	function get_mouseY() {
+	function get_mouseY() : Int {
 		return 0;
 	}
 
-	function get_width() {
+	function get_width() : Int {
 		return 0;
 	}
 
-	function get_height() {
+	function get_height() : Int {
 		return 0;
 	}
 
-	function get_mouseLock() {
+	function get_mouseLock() : Bool {
 		return false;
 	}
 
-	function set_mouseLock(b) {
-		if( b ) throw "Not implemented";
-		return b;
+	function set_mouseLock( v : Bool ) : Bool {
+		if( v ) throw "Not implemented";
+		return false;
 	}
 
-#end
+	function get_vsync() : Bool return true;
+
+	function set_vsync( b : Bool ) : Bool {
+		if( !b ) throw "Can't disable vsync on this platform";
+		return true;
+	}
 
 }

+ 187 - 0
hxd/Stage.js.hx

@@ -0,0 +1,187 @@
+package hxd;
+
+class Stage {
+
+	var resizeEvents : List<Void -> Void>;
+	var eventTargets : List<Event -> Void>;
+
+	public var width(get, null) : Int;
+	public var height(get, null) : Int;
+	public var mouseX(get, null) : Int;
+	public var mouseY(get, null) : Int;
+	public var mouseLock(get, set) : Bool;
+	public var vsync(get, set) : Bool;
+
+	var curMouseX : Float = 0.;
+	var curMouseY : Float = 0.;
+
+	@:allow(hxd)
+	static function getCanvas() {
+		var canvas : js.html.CanvasElement = cast js.Browser.document.getElementById("webgl");
+		if( canvas == null ) throw "Missing canvas#webgl";
+		return canvas;
+	}
+	var canvas : js.html.CanvasElement;
+	var canvasPos : { var width(default, never) : Float; var height(default, never) : Float; var left(default, never) : Float; var top(default, never) : Float; };
+
+	function new() : Void {
+		eventTargets = new List();
+		resizeEvents = new List();
+
+		canvas = getCanvas();
+		canvasPos = canvas.getBoundingClientRect();
+		js.Browser.window.addEventListener("mousedown", onMouseDown);
+		js.Browser.window.addEventListener("mousemove", onMouseMove);
+		js.Browser.window.addEventListener("mouseup", onMouseUp);
+		js.Browser.window.addEventListener("mousewheel", onMouseWheel);
+		js.Browser.window.addEventListener("keydown", onKeyDown);
+		js.Browser.window.addEventListener("keyup", onKeyUp);
+		canvas.addEventListener("mousedown", function(e) {
+			onMouseDown(e);
+			e.stopPropagation();
+			e.preventDefault();
+		});
+		canvas.oncontextmenu = function(e){
+			e.stopPropagation();
+			e.preventDefault();
+			return false;
+		};
+		var curW = this.width, curH = this.height;
+		var t0 = new haxe.Timer(100);
+		t0.run = function() {
+			canvasPos = canvas.getBoundingClientRect();
+			var cw = this.width, ch = this.height;
+			if( curW != cw || curH != ch ) {
+				curW = cw;
+				curH = ch;
+				onResize(null);
+			}
+		};
+	}
+
+	public dynamic function onClose() : Bool {
+		return true;
+	}
+
+	public function event( e : hxd.Event ) : Void {
+		for( et in eventTargets )
+			et(e);
+	}
+
+	public function addEventTarget( et : Event->Void ) : Void {
+		eventTargets.add(et);
+	}
+
+	public function removeEventTarget( et : Event->Void ) : Void {
+		for( e in eventTargets )
+			if( Reflect.compareMethods(e,et) ) {
+				eventTargets.remove(e);
+				break;
+			}
+	}
+
+	public function addResizeEvent( f : Void -> Void ) : Void {
+		resizeEvents.push(f);
+	}
+
+	public function removeResizeEvent( f : Void -> Void ) : Void {
+		for( e in resizeEvents )
+			if( Reflect.compareMethods(e,f) ) {
+				resizeEvents.remove(f);
+				break;
+			}
+	}
+
+	function onResize(e:Dynamic) : Void {
+		for( r in resizeEvents )
+			r();
+	}
+
+	public function resize( width : Int, height : Int ) : Void {
+	}
+
+	public function setFullScreen( v : Bool ) : Void {
+	}
+
+	static var inst = null;
+	public static function getInstance() : Stage {
+		if( inst == null ) inst = new Stage();
+		return inst;
+	}
+
+	function get_width() {
+		return Math.round(canvasPos.width * js.Browser.window.devicePixelRatio);
+	}
+
+	function get_height() {
+		return Math.round(canvasPos.height * js.Browser.window.devicePixelRatio);
+	}
+
+	function get_mouseX() {
+		return Math.round((curMouseX - canvasPos.left) * js.Browser.window.devicePixelRatio);
+	}
+
+	function get_mouseY() {
+		return Math.round((curMouseY - canvasPos.top) * js.Browser.window.devicePixelRatio);
+	}
+
+	function get_mouseLock() : Bool {
+		return false;
+	}
+
+	function set_mouseLock( v : Bool ) : Bool {
+		if( v ) throw "Not implemented";
+		return false;
+	}
+
+	function get_vsync() : Bool return true;
+
+	function set_vsync( b : Bool ) : Bool {
+		if( !b ) throw "Can't disable vsync on this platform";
+		return true;
+	}
+
+	function onMouseDown(e:js.html.MouseEvent) {
+		var ev = new Event(EPush, mouseX, mouseY);
+		if (e.button == 2) ev.button = 1;
+		event(ev);
+	}
+
+	function onMouseUp(e:js.html.MouseEvent) {
+		var ev = new Event(ERelease, mouseX, mouseY);
+		if (e.button == 2) ev.button = 1;
+		event(ev);
+	}
+
+	function onMouseMove(e:js.html.MouseEvent) {
+		curMouseX = e.clientX;
+		curMouseY = e.clientY;
+		event(new Event(EMove, mouseX, mouseY));
+	}
+
+	function onMouseWheel(e:js.html.MouseEvent) {
+		var ev = new Event(EWheel, mouseX, mouseY);
+		ev.wheelDelta = untyped -e.wheelDelta / 30.0;
+		event(ev);
+	}
+
+	function onKeyUp(e:js.html.KeyboardEvent) {
+		var ev = new Event(EKeyUp, mouseX, mouseY);
+		ev.keyCode = e.keyCode;
+		ev.charCode = e.charCode;
+		event(ev);
+	}
+
+	function onKeyDown(e:js.html.KeyboardEvent) {
+		var ev = new Event(EKeyDown, mouseX, mouseY);
+		ev.keyCode = e.keyCode;
+		ev.charCode = e.charCode;
+		event(ev);
+	}
+
+}
+
+
+
+
+

+ 166 - 0
hxd/System.flash.hx

@@ -0,0 +1,166 @@
+package hxd;
+
+enum Platform {
+	IOS;
+	Android;
+	WebGL;
+	PC;
+	Console;
+	FlashPlayer;
+}
+
+enum SystemValue {
+	IsTouch;
+	IsWindowed;
+	IsMobile;
+}
+
+class System {
+
+	public static var width(get,never) : Int;
+	public static var height(get, never) : Int;
+	public static var lang(get, never) : String;
+	public static var platform(get, never) : Platform;
+	public static var screenDPI(get,never) : Float;
+	public static var setCursor = setNativeCursor;
+
+	static var loopFunc : Void -> Void;
+
+	// FLASH
+	static var loopVar : flash.events.Event -> Void;
+
+
+	public static function getCurrentLoop() : Void -> Void {
+		return loopFunc;
+	}
+
+	public static function setLoop( f : Void -> Void ) : Void {
+		loopFunc = f;
+		if( loopVar != null )
+			flash.Lib.current.removeEventListener(flash.events.Event.ENTER_FRAME, loopVar);
+		if( f == null )
+			loopVar = null;
+		else {
+			loopVar = function(_) f();
+			flash.Lib.current.addEventListener(flash.events.Event.ENTER_FRAME, loopVar);
+		}
+	}
+
+	public static function start( callb : Void -> Void ) : Void {
+		callb();
+	}
+
+	public static function setNativeCursor( c : Cursor ) : Void {
+		flash.ui.Mouse.cursor = switch( c ) {
+		case Default: "auto";
+		case Button: "button";
+		case Move: "hand";
+		case TextInput: "ibeam";
+		case Hide: "auto";
+		case Custom(cursor):
+			if( cursor.alloc == null ) {
+				var c = new flash.ui.MouseCursorData();
+				var v = new flash.Vector();
+				for( f in cursor.frames ) v.push(f.toNative());
+				c.data = v;
+				c.frameRate = cursor.speed;
+				c.hotSpot = new flash.geom.Point(cursor.offsetX, cursor.offsetY);
+				cursor.alloc = c;
+				flash.ui.Mouse.registerCursor(cursor.name, cursor.alloc);
+			}
+			cursor.name;
+		}
+		if( c == Hide ) flash.ui.Mouse.hide() else flash.ui.Mouse.show();
+	}
+
+	static var CACHED_NAME = null;
+	public static function getDeviceName() : String {
+		if( CACHED_NAME != null )
+			return CACHED_NAME;
+		var name;
+		switch( platform ) {
+		case Android if( isAir() ):
+			try {
+				var f : Dynamic = Type.createInstance(flash.Lib.current.loaderInfo.applicationDomain.getDefinition("flash.filesystem.File"), ["/system/build.prop"]);
+				var fs : flash.utils.IDataInput = Type.createInstance(flash.Lib.current.loaderInfo.applicationDomain.getDefinition("flash.filesystem.FileStream"), []);
+				Reflect.callMethod(fs, Reflect.field(fs, "open"), [f, "read"]);
+				var content = fs.readUTFBytes(fs.bytesAvailable);
+				name = StringTools.trim(content.split("ro.product.model=")[1].split("\n")[0]);
+			} catch( e : Dynamic ) {
+				name = "Android";
+			}
+		case IOS:
+			name = switch( [width, height, screenDPI] ) {
+			case [960, 640, 326]: "iPhone4";
+			case [1136, 640, 326]: "iPhone5";
+			case [1334, 750, 326]: "iPhone6";
+			case [1920, 1080, 401]: "iPhone6+";
+			case [2048, 1536, 264]: "iPad"; // 3/4/Air
+			case [2048, 1536, 326]: "iPadMini2";
+			case [1024, 768, 163]: "iPadMini";
+			case [w, h, dpi]: "IOS Unknown " + w + "x" + h + "@" + dpi;
+			}
+		default:
+			name = "PC";
+		}
+		return name;
+	}
+
+	public static function getDefaultFrameRate() : Float {
+		return flash.Lib.current.stage.frameRate;
+	}
+
+	public static function getValue( s : SystemValue ) : Bool {
+		switch( s ) {
+		case IsWindowed:
+			var p = flash.system.Capabilities.playerType;
+			return p == "ActiveX" || p == "PlugIn" || p == "StandAlone" || p == "Desktop";
+		case IsMobile:
+			return platform == IOS || platform == Android;
+		case IsTouch:
+			return flash.system.Capabilities.touchscreenType == flash.system.TouchscreenType.FINGER;
+		}
+	}
+
+	public static function exit() : Void {
+		if( isAir() ) {
+			var d : Dynamic = flash.Lib.current.loaderInfo.applicationDomain.getDefinition("flash.desktop.NativeApplication");
+			Reflect.field(Reflect.field(d,"nativeApplication"),"exit")();
+		} else
+			flash.system.System.exit(0);
+	}
+
+	// getters
+
+	static function get_width() {
+		var Cap = flash.system.Capabilities;
+		return getValue(IsWindowed) ? flash.Lib.current.stage.stageWidth : Std.int(Cap.screenResolutionX > Cap.screenResolutionY ? Cap.screenResolutionX : Cap.screenResolutionY);
+	}
+
+	static function get_height() {
+		var Cap = flash.system.Capabilities;
+		return getValue(IsWindowed) ? flash.Lib.current.stage.stageHeight : Std.int(Cap.screenResolutionX > Cap.screenResolutionY ? Cap.screenResolutionY : Cap.screenResolutionX);
+	}
+
+	static function get_lang() : String {
+		return flash.system.Capabilities.language;
+	}
+
+	static function get_platform() : Platform {
+		if( flash.system.Capabilities.manufacturer.indexOf('Android') != -1 )
+			return Android;
+		if( flash.system.Capabilities.manufacturer.indexOf('iOS') != -1 )
+			return IOS;
+		return FlashPlayer;
+	}
+
+	static function isAir() : Bool {
+		return flash.system.Capabilities.playerType == "Desktop";
+	}
+
+	static function get_screenDPI() : Int {
+		return Std.int(flash.system.Capabilities.screenDPI);
+	}
+
+}
+

+ 152 - 0
hxd/System.hl.hx

@@ -0,0 +1,152 @@
+package hxd;
+
+enum Platform {
+	IOS;
+	Android;
+	WebGL;
+	PC;
+	Console;
+	FlashPlayer;
+}
+
+enum SystemValue {
+	IsTouch;
+	IsWindowed;
+	IsMobile;
+}
+
+//@:coreApi
+class System {
+
+	public static var width(get,never) : Int;
+	public static var height(get, never) : Int;
+	public static var lang(get, never) : String;
+	public static var platform(get, never) : Platform;
+	public static var screenDPI(get,never) : Float;
+	public static var setCursor = setNativeCursor;
+
+	static var loopFunc : Void -> Void;
+
+	// -- HL
+	static var currentNativeCursor : Cursor = Default;
+	static var cursorVisible = true;
+
+	public static function getCurrentLoop() : Void -> Void {
+		return loopFunc;
+	}
+
+	public static function setLoop( f : Void -> Void ) : Void {
+		loopFunc = f;
+	}
+
+	static function mainLoop() : Void {
+		if( loopFunc != null ) loopFunc();
+		@:privateAccess hxd.Stage.inst.window.present();
+	}
+
+	public static function start( init : Void -> Void ) : Void {
+		sdl.Sdl.tick();
+		sdl.Sdl.init();
+		var width = 800;
+		var height = 600;
+		var size = haxe.macro.Compiler.getDefine("windowSize");
+		var title = haxe.macro.Compiler.getDefine("windowTitle");
+		if( title == null )
+			title = "";
+		if( size != null ) {
+			var p = size.split("x");
+			width = Std.parseInt(p[0]);
+			height = Std.parseInt(p[1]);
+		}
+		@:privateAccess Stage.initChars();
+		@:privateAccess Stage.inst = new Stage(title, width, height);
+		init();
+		sdl.Sdl.defaultEventHandler = @:privateAccess Stage.inst.onEvent;
+		haxe.MainLoop.add(mainLoop);
+	}
+
+	public static function setNativeCursor( c : Cursor ) : Void {
+		if( c.equals(currentNativeCursor) )
+			return;
+		currentNativeCursor = c;
+		if( c == Hide ) {
+			cursorVisible = false;
+			sdl.Cursor.show(false);
+			return;
+		}
+		var cur : sdl.Cursor;
+		switch( c ) {
+		case Default:
+			cur = sdl.Cursor.createSystem(Arrow);
+		case Button:
+			cur = sdl.Cursor.createSystem(Hand);
+		case Move:
+			throw "Cursor not supported";
+		case TextInput:
+			cur = sdl.Cursor.createSystem(IBeam);
+		case Hide:
+			throw "assert";
+		case Custom(c):
+			if( c.alloc == null ) {
+				if( c.frames.length > 1 ) throw "Animated cursor not supported";
+				var pixels = c.frames[0].getPixels();
+				pixels.convert(BGRA);
+				var surf = sdl.Surface.fromBGRA(pixels.bytes, pixels.width, pixels.height);
+				c.alloc = sdl.Cursor.create(surf, c.offsetX, c.offsetY);
+				surf.free();
+				pixels.dispose();
+			}
+			cur = c.alloc;
+		}
+		cur.set();
+		if( !cursorVisible ) {
+			cursorVisible = true;
+			sdl.Cursor.show(true);
+		}
+	}
+
+	public static function getDeviceName() : String {
+		return "PC/" + sdl.Sdl.getDevices()[0];
+	}
+
+	public static function getDefaultFrameRate() : Float {
+		return 60.;
+	}
+
+	public static function getValue( s : SystemValue ) : Bool {
+		return switch( s ) {
+		case IsWindowed:
+			return true;
+		default:
+			return false;
+		}
+	}
+
+	public static function exit() : Void {
+		try {
+			Sys.exit(0);
+		} catch( e : Dynamic ) {
+			// access violation sometimes ?
+			exit();
+		}
+	}
+
+	@:hlNative("std","sys_locale") static function getLocale() : hl.Bytes { return null; }
+
+	static var _lang : String;
+	static function get_lang() : String {
+		if( _lang == null ) {
+			var str = @:privateAccess String.fromUCS2(getLocale());
+			_lang = ~/[.@_-]/g.split(str)[0];
+		}
+		return _lang;
+	}
+
+	// getters
+
+	static function get_width() : Int return sdl.Sdl.getScreenWidth();
+	static function get_height() : Int return sdl.Sdl.getScreenHeight();
+	static function get_platform() : Platform return PC;
+	static function get_screenDPI() : Int return 72; // TODO : SDL ?
+
+}

+ 31 - 811
hxd/System.hx

@@ -1,846 +1,66 @@
 package hxd;
 
-#if hxsdl
-import hxd.Key in K;
-#end
-
-enum Cursor {
-	Default;
-	Button;
-	Move;
-	TextInput;
-	Hide;
-	Custom( custom : CustomCursor );
+enum Platform {
+	IOS;
+	Android;
+	WebGL;
+	PC;
+	Console;
+	FlashPlayer;
 }
 
-@:allow(hxd.System)
-class CustomCursor {
-
-	var frames : Array<hxd.BitmapData>;
-	var speed : Float;
-	var offsetX : Int;
-	var offsetY : Int;
-	#if hlsdl
-	var alloc : sdl.Cursor;
-	#elseif flash
-	static var UID = 0;
-	var name : String;
-	var alloc : flash.ui.MouseCursorData;
-	#end
-
-	public function new( frames, speed, offsetX, offsetY ) {
-		this.frames = frames;
-		this.speed = speed;
-		this.offsetX = offsetX;
-		this.offsetY = offsetY;
-		#if flash
-		name = "custom_" + UID++;
-		#end
-	}
-
-	public function dispose() {
-		for( f in frames )
-			f.dispose();
-		frames = [];
-		#if hlsdl
-		if( alloc != null ) {
-			alloc.free();
-			alloc = null;
-		}
-		#elseif flash
-		if( alloc != null ) {
-			flash.ui.Mouse.unregisterCursor(name);
-			alloc = null;
-		}
-		#end
-	}
-
+enum SystemValue {
+	IsTouch;
+	IsWindowed;
+	IsMobile;
 }
 
 class System {
 
 	public static var width(get,never) : Int;
-	public static var height(get,never) : Int;
-	public static var isTouch(get,never) : Bool;
-	public static var isWindowed(get,never) : Bool;
-	public static var lang(get,never) : String;
-	public static var isAndroid(get, never) : Bool;
-	public static var isIOS(get, never) : Bool;
-
+	public static var height(get, never) : Int;
+	public static var lang(get, never) : String;
+	public static var platform(get, never) : Platform;
 	public static var screenDPI(get,never) : Float;
-
 	public static var setCursor = setNativeCursor;
 
-	#if (flash || nme || openfl)
-
-	static function get_isWindowed() {
-		#if cpp
-		return true;
-		#else
-		var p = flash.system.Capabilities.playerType;
-		return p == "ActiveX" || p == "PlugIn" || p == "StandAlone" || p == "Desktop";
-		#end
-	}
-
-	static function get_isTouch() {
-		#if cpp
-		return false;
-		#else
-		return flash.system.Capabilities.touchscreenType == flash.system.TouchscreenType.FINGER;
-		#end
-	}
-
-	static function get_width() {
-		var Cap = flash.system.Capabilities;
-		return isWindowed ? flash.Lib.current.stage.stageWidth : Std.int(Cap.screenResolutionX > Cap.screenResolutionY ? Cap.screenResolutionX : Cap.screenResolutionY);
-	}
-
-	static function get_height() {
-		var Cap = flash.system.Capabilities;
-		return isWindowed ? flash.Lib.current.stage.stageHeight : Std.int(Cap.screenResolutionX > Cap.screenResolutionY ? Cap.screenResolutionY : Cap.screenResolutionX);
-	}
-
-	static function get_isAndroid() {
-		#if cpp
-		return #if android true #else false #end;
-		#else
-		return flash.system.Capabilities.manufacturer.indexOf('Android') != -1;
-		#end
-	}
-
-	static function get_isIOS() {
-		#if cpp
-		return #if ios true #else false #end;
-		#else
-		return flash.system.Capabilities.manufacturer.indexOf('iOS') != -1;
-		#end
-	}
-
-	static function get_screenDPI() {
-		return flash.system.Capabilities.screenDPI;
-	}
-
-	static var loop = null;
-	static var loopFunc = null;
-	#if (nme || openfl)
-	static var VIEW = null;
-	#end
+	static var loopFunc : Void -> Void;
 
-	public static function getCurrentLoop() {
+	public static function getCurrentLoop() : Void -> Void {
 		return loopFunc;
 	}
 
-	public static function setLoop( f : Void -> Void ) {
+	public static function setLoop( f : Void -> Void ) : Void {
 		loopFunc = f;
-		#if nme
-		if( VIEW == null ) {
-			VIEW = new nme.display.OpenGLView();
-			VIEW.name = "glView";
-			flash.Lib.current.addChildAt(VIEW,0);
-		}
-		VIEW.render = function(_) if ( f != null ) f();
-		#elseif openfl
-		if( openfl.display.OpenGLView.isSupported ){
-			if( VIEW == null ) {
-				VIEW = new openfl.display.OpenGLView();
-				VIEW.name = "glView";
-				flash.Lib.current.addChildAt(VIEW, 0);
-			}
-			VIEW.render = function(_) if ( f != null ) f();
-		}
-		#else
-		if( loop != null )
-			flash.Lib.current.removeEventListener(flash.events.Event.ENTER_FRAME, loop);
-		if( f == null )
-			loop = null;
-		else {
-			loop = function(_) f();
-			flash.Lib.current.addEventListener(flash.events.Event.ENTER_FRAME, loop);
-		}
-		#end
-	}
-
-	public static function start(callb) {
-		#if nme
-		var windowSize = haxe.macro.Compiler.getDefine("window");
-		if( windowSize == null ) windowSize = "800x600";
-		var windowSize = windowSize.split("x");
-		var width = Std.parseInt(windowSize[0]), height = Std.parseInt(windowSize[1]);
-		if( width < 100 ) width = 100;
-		if( height < 100 ) height = 100;
-		nme.Lib.create(function() {
-            nme.Lib.current.stage.align = nme.display.StageAlign.TOP_LEFT;
-            nme.Lib.current.stage.scaleMode = nme.display.StageScaleMode.NO_SCALE;
-            nme.Lib.current.loaderInfo = nme.display.LoaderInfo.create(null);
-			try {
-				callb();
-			} catch( e : Dynamic ) {
-				Sys.println(e);
-				#if debug
-				Sys.println(haxe.CallStack.toString(haxe.CallStack.exceptionStack()));
-				#end
-			}
-         },
-         width, height,
-         120, // using 60 FPS with no vsync gives a fps ~= 50
-         0xFFFFFF,
-         (true ? nme.Lib.HARDWARE : 0) |
-         nme.Lib.ALLOW_SHADERS | nme.Lib.REQUIRE_SHADERS |
-         (true ? nme.Lib.DEPTH_BUFFER : 0) |
-         (false ? nme.Lib.STENCIL_BUFFER : 0) |
-         (true ? nme.Lib.RESIZABLE : 0) |
-         (false ? nme.Lib.BORDERLESS : 0) |
-         (true ? nme.Lib.VSYNC : 0) |
-         (false ? nme.Lib.FULLSCREEN : 0) |
-         (0 == 4 ? nme.Lib.HW_AA_HIRES : 0) |
-         (0 == 2 ? nme.Lib.HW_AA : 0),
-         "Heaps Application"
-		);
-		#else
-		callb();
-		#end
-	}
-
-	#if flash
-	static function isAir() {
-		return flash.system.Capabilities.playerType == "Desktop";
-	}
-	#end
-
-	public static function getClipboard() : String {
-		#if flash
-		return flash.desktop.Clipboard.generalClipboard.getData(flash.desktop.ClipboardFormats.TEXT_FORMAT);
-		#else
-		return "";
-		#end
-	}
-
-	public static function exit() {
-		#if flash
-		if( isAir() ) {
-			var d : Dynamic = flash.Lib.current.loaderInfo.applicationDomain.getDefinition("flash.desktop.NativeApplication");
-			Reflect.field(Reflect.field(d,"nativeApplication"),"exit")();
-		} else
-		#end
-			flash.system.System.exit(0);
-	}
-
-	public static function setNativeCursor( c : Cursor ) {
-		#if cpp
-		// TODO
-		#else
-		flash.ui.Mouse.cursor = switch( c ) {
-		case Default: "auto";
-		case Button: "button";
-		case Move: "hand";
-		case TextInput: "ibeam";
-		case Hide: "auto";
-		case Custom(cursor):
-			#if cpp
-				throw "not supported on openFL for now";
-			#else
-				if( cursor.alloc == null ) {
-					var c = new flash.ui.MouseCursorData();
-					var v = new flash.Vector();
-					for( f in cursor.frames ) v.push(f.toNative());
-					c.data = v;
-					c.frameRate = cursor.speed;
-					c.hotSpot = new flash.geom.Point(cursor.offsetX, cursor.offsetY);
-					cursor.alloc = c;
-					flash.ui.Mouse.registerCursor(cursor.name, cursor.alloc);
-				}
-				cursor.name;
-			#end
-		}
-		#end
-		if( c == Hide ) flash.ui.Mouse.hide() else flash.ui.Mouse.show();
-	}
-
-
-	/**
-		Returns the device name:
-			"PC" for a desktop computer
-			Or the android device name
-			(will add iPad/iPhone/iPod soon)
-	**/
-	static var CACHED_NAME = null;
-	public static function getDeviceName() {
-		if( CACHED_NAME != null )
-			return CACHED_NAME;
-		var name;
-		#if flash
-		if( isAndroid && isAir() ) {
-			try {
-				var f : Dynamic = Type.createInstance(flash.Lib.current.loaderInfo.applicationDomain.getDefinition("flash.filesystem.File"), ["/system/build.prop"]);
-				var fs : flash.utils.IDataInput = Type.createInstance(flash.Lib.current.loaderInfo.applicationDomain.getDefinition("flash.filesystem.FileStream"), []);
-				Reflect.callMethod(fs, Reflect.field(fs, "open"), [f, "read"]);
-				var content = fs.readUTFBytes(fs.bytesAvailable);
-				name = StringTools.trim(content.split("ro.product.model=")[1].split("\n")[0]);
-			} catch( e : Dynamic ) {
-				name = "Android";
-			}
-		} else
-		#end
-		if( isIOS ) {
-			name = switch( [width, height, screenDPI] ) {
-			case [960, 640, 326]: "iPhone4";
-			case [1136, 640, 326]: "iPhone5";
-			case [1334, 750, 326]: "iPhone6";
-			case [1920, 1080, 401]: "iPhone6+";
-			case [2048, 1536, 264]: "iPad"; // 3/4/Air
-			case [2048, 1536, 326]: "iPadMini2";
-			case [1024, 768, 163]: "iPadMini";
-			case [w, h, dpi]: "IOS Unknown " + w + "x" + h + "@" + dpi;
-			}
-		} else
-			name = "PC";
-		CACHED_NAME = name;
-		return name;
-	}
-
-	static function get_lang() {
-		return flash.system.Capabilities.language;
-	}
-
-	#elseif lime
-
-	static function get_isWindowed() {
-		return true;
-	}
-
-	static function get_isTouch() {
-		#if desktop
-		return false;
-		#else
-		return true;
-		#end
-	}
-
-	static function get_width() {
-		var win = lime.app.Application.current.window;
-		return Std.int(win.width * win.scale);
-	}
-
-	static function get_height() {
-		var win = lime.app.Application.current.window;
-		return Std.int(win.height * win.scale);
-	}
-
-	static function get_isAndroid() {
-		return #if android true #else false #end;
-	}
-
-	static function get_isIOS() {
-		return #if ios true #else false #end;
-	}
-
-	static function get_screenDPI() {
-		return 0; // TODO
-	}
-
-	@:allow(hxd.impl.LimeStage)
-	static var loopFunc = null;
-
-	public static function getCurrentLoop() {
-		return loopFunc;
-	}
-
-	public static function setLoop( f : Void -> Void ) {
-		loopFunc = f;
-	}
-
-	public static function start(callb) {
-		callb();
-	}
-
-	public static function getClipboard() : String {
-		return lime.system.Clipboard.text;
-	}
-
-	public static function exit() {
-		return lime.system.System.exit( 0 );
-	}
-
-	public static function setNativeCursor( c : Cursor ) {
-		lime.ui.Mouse.cursor = switch( c ){
-		case Default: DEFAULT;
-		case Button: POINTER;
-		case Move: MOVE;
-		case TextInput: TEXT;
-		case Hide: DEFAULT;
-		case Custom(_,_,_,_):
-			throw "not supported";
-		}
-		if( c == Hide ) lime.ui.Mouse.hide() else lime.ui.Mouse.show();
-	}
-
-
-	/**
-		Returns the device name:
-			"PC" for a desktop computer
-			Or the android device name
-			(will add iPad/iPhone/iPod soon)
-	**/
-	static var CACHED_NAME = null;
-	public static function getDeviceName() {
-		if( CACHED_NAME != null )
-			return CACHED_NAME;
-		var name;
-		name = "Unknown"; // TODO
-		CACHED_NAME = name;
-		return name;
-	}
-
-	static function get_lang() {
-		return null; // TODO
-	}
-
-	#elseif js
-
-	static var LOOP = null;
-	static var LOOP_INIT = false;
-
-	static function loopFunc() {
-		var window : Dynamic = js.Browser.window;
-		var rqf : Dynamic = window.requestAnimationFrame ||
-			window.webkitRequestAnimationFrame ||
-			window.mozRequestAnimationFrame;
-		rqf(loopFunc);
-		if( LOOP != null ) LOOP();
-	}
-
-	public static function getCurrentLoop() {
-		return LOOP;
-	}
-
-	public static function setLoop( f : Void -> Void ) {
-		if( !LOOP_INIT ) {
-			LOOP_INIT = true;
-			loopFunc();
-		}
-		LOOP = f;
-	}
-
-	public static function start( callb ) {
-		callb();
-	}
-
-	public static function getClipboard() : String {
-		return "";
-	}
-
-	public static function setNativeCursor( c : Cursor ) {
-		var canvas = js.Browser.document.getElementById("webgl");
-		if( canvas != null ) {
-			canvas.style.cursor = switch( c ) {
-			case Default: "default";
-			case Button: "pointer";
-			case Move: "move";
-			case TextInput: "text";
-			case Hide: "none";
-			case Custom(_): throw "Custom cursor not supported";
-			};
-		}
-	}
-
-	static function get_lang() {
-		return "en";
-	}
-
-	static function get_screenDPI() {
-		return 72.;
-	}
-
-	static function get_isAndroid() {
-		return false;
-	}
-
-	static function get_isIOS() {
-		return false;
-	}
-
-	static function get_isWindowed() {
-		return true;
-	}
-
-	static function get_isTouch() {
-		return false;
-	}
-
-	static function get_width() {
-		return Math.round(js.Browser.document.body.clientWidth * js.Browser.window.devicePixelRatio);
-	}
-
-	static function get_height() {
-		return Math.round(js.Browser.document.body.clientHeight  * js.Browser.window.devicePixelRatio);
-	}
-
-	#elseif hxsdl
-
-	static var currentNativeCursor : Cursor = Default;
-	static var cursorVisible = true;
-
-	public static function setNativeCursor( c : Cursor ) {
-		if( c.equals(currentNativeCursor) )
-			return;
-		currentNativeCursor = c;
-		if( c == Hide ) {
-			cursorVisible = false;
-			sdl.Cursor.show(false);
-			return;
-		}
-		var cur : sdl.Cursor;
-		switch( c ) {
-		case Default:
-			cur = sdl.Cursor.createSystem(Arrow);
-		case Button:
-			cur = sdl.Cursor.createSystem(Hand);
-		case Move:
-			throw "Cursor not supported";
-		case TextInput:
-			cur = sdl.Cursor.createSystem(IBeam);
-		case Hide:
-			throw "assert";
-		case Custom(c):
-			if( c.alloc == null ) {
-				if( c.frames.length > 1 ) throw "Animated cursor not supported";
-				var pixels = c.frames[0].getPixels();
-				pixels.convert(BGRA);
-				var surf = sdl.Surface.fromBGRA(pixels.bytes, pixels.width, pixels.height);
-				c.alloc = sdl.Cursor.create(surf, c.offsetX, c.offsetY);
-				surf.free();
-				pixels.dispose();
-			}
-			cur = c.alloc;
-		}
-		cur.set();
-		if( !cursorVisible ) {
-			cursorVisible = true;
-			sdl.Cursor.show(true);
-		}
-	}
-
-	static function get_screenDPI() {
-		return 72; // not implemented in SDL ???
-	}
-
-	static function get_isIOS() {
-		return false;
-	}
-
-	static function get_isAndroid() {
-		return false;
-	}
-
-	static function get_isWindowed() {
-		return true;
 	}
 
-	static function get_isTouch() {
-		return false;
+	public static function start( callb : Void -> Void ) : Void {
 	}
 
-	@:hlNative("std","sys_locale") static function getLocale() : hl.Bytes { return null; }
-
-	static var _lang : String;
-	static function get_lang() {
-		if( _lang == null ) {
-			var str = @:privateAccess String.fromUCS2(getLocale());
-			_lang = ~/[.@_-]/g.split(str)[0];
-		}
-		return _lang;
+	public static function setNativeCursor( c : Cursor ) : Void {
 	}
 
-	static function get_width() {
-		return sdl.Sdl.getScreenWidth();
+	public static function getDeviceName() : String {
+		return "Unknown";
 	}
 
-	static function get_height() {
-		return sdl.Sdl.getScreenHeight();
-	}
-
-	public static function exit() {
-		try {
-			Sys.exit(0);
-		} catch( e : Dynamic ) {
-			// access violation sometimes ?
-			exit();
-		}
-	}
-
-	public static function getClipboard() : String {
-		return "";
-	}
-
-	public static function getCurrentLoop() {
-		return currentLoop;
-	}
-
-	static var win : sdl.Window;
-	static var windowWidth = 800;
-	static var windowHeight = 600;
-	static var mouseX = 0;
-	static var mouseY = 0;
-	static var shiftDown : Bool;
-	static var currentLoop = null;
-	static var CODEMAP = [for( i in 0...2048 ) i];
-	static var CHARMAP = [for( i in 0...2048 ) 0];
-
-	public static function setLoop( f : Void -> Void ) {
-		currentLoop = f;
-	}
-
-	static function mainLoop() {
-		if( currentLoop != null ) currentLoop();
-		win.present();
-	}
-
-	static function onEvent( e : sdl.Event ) {
-		var eh = null;
-		switch( e.type ) {
-		case WindowState:
-			switch( e.state ) {
-			case Resize:
-				windowWidth = win.width;
-				windowHeight = win.height;
-				@:privateAccess Stage.getInstance().onResize(null);
-			default:
-			}
-		case MouseDown:
-			mouseX = e.mouseX;
-			mouseY = e.mouseY;
-			eh = new Event(EPush, e.mouseX, e.mouseY);
-			// middle button -> 2 / right button -> 1
-			eh.button = switch( e.button - 1 ) {
-			case 0: 0;
-			case 1: 2;
-			case 2: 1;
-			case x: x;
-			}
-		case MouseUp:
-			mouseX = e.mouseX;
-			mouseY = e.mouseY;
-			eh = new Event(ERelease, e.mouseX, e.mouseY);
-			eh.button = switch( e.button - 1 ) {
-			case 0: 0;
-			case 1: 2;
-			case 2: 1;
-			case x: x;
-			};
-		case MouseMove:
-			mouseX = e.mouseX;
-			mouseY = e.mouseY;
-			eh = new Event(EMove, e.mouseX, e.mouseY);
-		case KeyDown:
-			eh = new Event(EKeyDown);
-			if( e.keyCode & (1 << 30) != 0 ) e.keyCode = (e.keyCode & ((1 << 30) - 1)) + 1000;
-			eh.keyCode = CODEMAP[e.keyCode];
-			eh.charCode = CHARMAP[e.keyCode];
-			if( eh.charCode == ":".code && shiftDown )
-				eh.charCode = "/".code;
-			if( eh.charCode >= 'a'.code && eh.charCode <= 'z'.code && shiftDown )
-				eh.charCode += 'A'.code - 'a'.code;
-			if( eh.keyCode & (K.LOC_LEFT | K.LOC_RIGHT) != 0 ) {
-				e.keyCode = eh.keyCode & 0xFF;
-				if( e.keyCode == K.SHIFT ) shiftDown = true;
-				onEvent(e);
-			}
-		case KeyUp:
-			eh = new Event(EKeyUp);
-			if( e.keyCode & (1 << 30) != 0 ) e.keyCode = (e.keyCode & ((1 << 30) - 1)) + 1000;
-			eh.keyCode = CODEMAP[e.keyCode];
-			eh.charCode = CHARMAP[e.keyCode];
-			if( eh.charCode == ":".code && shiftDown )
-				eh.charCode = "/".code;
-			if( eh.charCode >= 'a'.code && eh.charCode <= 'z'.code && shiftDown )
-				eh.charCode += 'A'.code - 'a'.code;
-			if( eh.keyCode & (K.LOC_LEFT | K.LOC_RIGHT) != 0 ) {
-				e.keyCode = eh.keyCode & 0xFF;
-				if( e.keyCode == K.SHIFT ) shiftDown = false;
-				onEvent(e);
-			}
-		case MouseWheel:
-			eh = new Event(EWheel, mouseX, mouseY);
-			eh.wheelDelta = -e.wheelDelta;
-		case GControllerAdded, GControllerRemoved, GControllerUp, GControllerDown, GControllerAxis:
-			@:privateAccess hxd.Pad.onEvent( e );
-		default:
-		}
-		if( eh != null ) Stage.getInstance().event(eh);
-	}
-
-	public static function start( init : Void -> Void ) {
-		inline function addKey(sdl, keyCode, charCode=0) {
-			CODEMAP[sdl] = keyCode;
-			if( charCode != 0 ) CHARMAP[sdl] = charCode;
-		}
-
-		/*
-			SDL 2.0 does not allow to get the charCode from a key event.
-			let's for now do a simple mapping, even if not very efficient
-		*/
-
-		// ASCII
-		CHARMAP[K.BACKSPACE] = 8;
-		CHARMAP[K.TAB] = 9;
-		CHARMAP[K.ENTER] = 13;
-		for( i in 32...127 )
-			CHARMAP[i] = i;
-		for( i in 0...26 )
-			addKey(97 + i, K.A + i);
-		for( i in 0...12 )
-			addKey(1058 + i, K.F1 + i);
-
-		// NUMPAD
-
-		addKey(1084, K.NUMPAD_DIV, "/".code);
-		addKey(1085, K.NUMPAD_MULT, "*".code);
-		addKey(1086, K.NUMPAD_SUB, "-".code);
-		addKey(1087, K.NUMPAD_ADD, "+".code);
-		addKey(1088, K.NUMPAD_ENTER, 13);
-		for( i in 0...9 )
-			addKey(1089 + i, K.NUMPAD_1 + i, "1".code + i);
-		addKey(1098, K.NUMPAD_0, "0".code);
-		addKey(1099, K.NUMPAD_DOT, ".".code);
-
-		// EXTRA
-		var keys = [
-			//K.BACKSPACE
-			//K.TAB
-			//K.ENTER
-			1225 => K.LSHIFT,
-			1229 => K.RSHIFT,
-			1224 => K.LCTRL,
-			1228 => K.RCTRL,
-			1226 => K.LALT,
-			1230 => K.RALT,
-			// K.ESCAPE
-			// K.SPACE
-			1075 => K.PGUP,
-			1078 => K.PGDOWN,
-			1077 => K.END,
-			1074 => K.HOME,
-			1080 => K.LEFT,
-			1082 => K.UP,
-			1079 => K.RIGHT,
-			1081 => K.DOWN,
-			1073 => K.INSERT,
-			127 => K.DELETE,
-			//K.NUMPAD_0-9
-			//K.A-Z
-			//K.F1-F12
-			1085 => K.NUMPAD_MULT,
-			1087 => K.NUMPAD_ADD,
-			1088 => K.NUMPAD_ENTER,
-			1086 => K.NUMPAD_SUB,
-			1099 => K.NUMPAD_DOT,
-			1084 => K.NUMPAD_DIV,
-		];
-		for( sdl in keys.keys() )
-			addKey(sdl, keys.get(sdl));
-
-		sdl.Sdl.tick();
-		sdl.Sdl.init();
-		var size = haxe.macro.Compiler.getDefine("windowSize");
-		if( size != null ) {
-			var p = size.split("x");
-			windowWidth = Std.parseInt(p[0]);
-			windowHeight = Std.parseInt(p[1]);
-		}
-		win = new sdl.Window("", windowWidth, windowHeight);
-		init();
-		#if hl
-		sdl.Sdl.defaultEventHandler = onEvent;
-		haxe.MainLoop.add(mainLoop);
-		#else
-		sdl.Sdl.loop(mainLoop,onEvent);
-		sdl.Sdl.quit();
-		#end
-	}
-
-	#elseif hl
-
-	static var LOOP = null;
-
-	public static function exit() {
-		Sys.exit(0);
-	}
-
-	public static function start( init : Void -> Void ) {
-		init();
-		while( LOOP != null ) LOOP();
-	}
-
-	public static function setLoop( f : Void -> Void ) {
-		LOOP = f;
-	}
-
-	public static function getCurrentLoop() {
-		return LOOP;
-	}
-
-	public static function setNativeCursor( c : Cursor ) {
-	}
-
-	static function get_screenDPI() {
-		return 72.;
-	}
-
-	@:hlNative("std","sys_locale") static function getLocale() : hl.Bytes { return null; }
-
-	static var _lang : String;
-	static function get_lang() {
-		if( _lang == null ) {
-			var str = @:privateAccess String.fromUCS2(getLocale());
-			_lang = ~/[.@_-]/g.split(str)[0];
-		}
-		return _lang;
-	}
-
-	static function get_isAndroid() {
-		return false;
-	}
-
-	public static function getClipboard() {
-		return "";
-	}
-
-	static function get_isIOS() {
-		return false;
-	}
-
-	static function get_isWindowed() {
-		return true;
+	public static function getDefaultFrameRate() : Float {
+		return 60.;
 	}
 
-	static function get_isTouch() {
+	public static function getValue( s : SystemValue ) : Bool {
 		return false;
 	}
 
-	static function get_width() {
-		return 800;
-	}
-
-	static function get_height() {
-		return 600;
+	public static function exit() : Void {
 	}
 
-	#end
+	// getters
 
-	public static function getDefaultFrameRate() : Float {
-		#if (flash || openfl)
-		return flash.Lib.current.stage.frameRate;
-		#else
-		return 60.;
-		#end
-	}
-
-	public static function isVSync() {
-		#if hlsdl
-		return win.vsync;
-		#else
-		return true;
-		#end
-	}
+	static function get_width() : Int return 0;
+	static function get_height() : Int return 0;
+	static function get_lang() : String return "en";
+	static function get_platform() : Platform return PC;
+	static function get_screenDPI() : Int return 72;
 
 }

+ 94 - 0
hxd/System.js.hx

@@ -0,0 +1,94 @@
+package hxd;
+
+enum Platform {
+	IOS;
+	Android;
+	WebGL;
+	PC;
+	Console;
+	FlashPlayer;
+}
+
+enum SystemValue {
+	IsTouch;
+	IsWindowed;
+	IsMobile;
+}
+
+class System {
+
+	public static var width(get,never) : Int;
+	public static var height(get, never) : Int;
+	public static var lang(get, never) : String;
+	public static var platform(get, never) : Platform;
+	public static var screenDPI(get,never) : Float;
+	public static var setCursor = setNativeCursor;
+
+	static var loopFunc : Void -> Void;
+
+	// JS
+	static var loopInit = false;
+
+	public static function getCurrentLoop() : Void -> Void {
+		return loopFunc;
+	}
+
+	public static function setLoop( f : Void -> Void ) : Void {
+		if( !loopInit ) {
+			loopInit = true;
+			browserLoop();
+		}
+		loopFunc = f;
+	}
+
+	static function browserLoop() {
+		var window : Dynamic = js.Browser.window;
+		var rqf : Dynamic = window.requestAnimationFrame ||
+			window.webkitRequestAnimationFrame ||
+			window.mozRequestAnimationFrame;
+		rqf(browserLoop);
+		if( loopFunc != null ) loopFunc();
+	}
+
+	public static function start( callb : Void -> Void ) : Void {
+		callb();
+	}
+
+	public static function setNativeCursor( c : Cursor ) : Void {
+		var canvas = Stage.getCanvas();
+		if( canvas != null ) {
+			canvas.style.cursor = switch( c ) {
+			case Default: "default";
+			case Button: "pointer";
+			case Move: "move";
+			case TextInput: "text";
+			case Hide: "none";
+			case Custom(_): throw "Custom cursor not supported";
+			};
+		}
+	}
+
+	public static function getDeviceName() : String {
+		return "Unknown";
+	}
+
+	public static function getDefaultFrameRate() : Float {
+		return 60.;
+	}
+
+	public static function getValue( s : SystemValue ) : Bool {
+		return false;
+	}
+
+	public static function exit() : Void {
+	}
+
+	// getters
+
+	static function get_width() : Int return Math.round(js.Browser.document.body.clientWidth * js.Browser.window.devicePixelRatio);
+	static function get_height() : Int return Math.round(js.Browser.document.body.clientHeight  * js.Browser.window.devicePixelRatio);
+	static function get_lang() : String return "en";
+	static function get_platform() : Platform return PC;
+	static function get_screenDPI() : Int return 72;
+
+}

+ 8 - 1
samples/GpuParticles.hx

@@ -4,7 +4,8 @@ class GpuParticles extends SampleApp {
 	var group : h3d.parts.GpuParticles.GpuPartGroup;
 	var box : h3d.scene.Box;
 	var tf : h2d.Text;
-	var prevParts = 0;
+	var moving = false;
+	var time = 0.;
 
 	override function init() {
 		super.init();
@@ -36,6 +37,7 @@ class GpuParticles extends SampleApp {
 		addSlider("Gravity", function() return g.gravity, function(v) g.gravity = v, 0, 5);
 		addCheck("Sort", function() return g.sortMode == Dynamic, function(v) g.sortMode = v ? Dynamic : None);
 		addCheck("Loop", function() return g.emitLoop, function(v) { g.emitLoop = v; if( !v ) parts.currentTime = 0; });
+		addCheck("Move", function() return moving, function(v) moving = v);
 
 		parts.onEnd = function() {
 			engine.backgroundColor = 0xFF000080;
@@ -54,6 +56,11 @@ class GpuParticles extends SampleApp {
 
 	override function update(dt:Float) {
 
+		if( moving ) {
+			time += dt * 0.01;
+			parts.x = Math.cos(time) * 5;
+			parts.y = Math.sin(time) * 5;
+		}
 
 		if( engine.backgroundColor&0xFFFFFF > 0 )
 			engine.backgroundColor -= 8;