Commits

Anonymous committed c1c40d9

* UI pour configuration des inputs
* scrolling background pour les menus

  • Participants
  • Parent commits 2a31b9b

Comments (0)

Files changed (10)

 		<Unit filename="src/gui.d" />
 		<Unit filename="src/inputactions.d" />
 		<Unit filename="src/inputconfiguration.d" />
+		<Unit filename="src/inputconfigurationdialog.d" />
 		<Unit filename="src/inputmanager.d" />
 		<Unit filename="src/lexer.d" />
 		<Unit filename="src/main.d" />

data/scrollbg.png

Added
New image
 
-input {
+INPUT {
+   UP {
+      METHOD = key
+      KEYCODE = 273
+   }
 
-		up			{ method = key	keycode = 273 }
-		down 		{ method = key	keycode = 274 }
-		left 		{ method = key	keycode = 276 }
-		RIGHT 		{ method = key	keycode = 275 }
-		escape 		{ method = key	keycode = 27  }
-		crouch 		{ method = key	keycode = 99  } ; c
-		fire 		{ method = key	keycode = 120 } ; x
-		enter 		{ method = key	keycode = 13  } ; return
-		pause 		{ method = key	keycode = 112 } ; p
-		volume_up 	{ method = key	keycode = 280 } ; page up
-		volume_down { method = key	keycode = 281 } ; page down
-		editor 		{ method = key  keycode = 293 } ; F12
+   DOWN {
+      METHOD = key
+      KEYCODE = 274
+   }
+
+   LEFT {
+      METHOD = key
+      KEYCODE = 276
+   }
+
+   RIGHT {
+      METHOD = key
+      KEYCODE = 275
+   }
+
+   ESCAPE {
+      METHOD = key
+      KEYCODE = 27
+   }
+
+   CROUCH {
+      METHOD = key
+      KEYCODE = 99
+   }
+ ; c
+
+   FIRE {
+      METHOD = key
+      KEYCODE = 120
+   }
+ ; x
+
+   ENTER {
+      METHOD = key
+      KEYCODE = 13
+   }
+ ; return
+
+   PAUSE {
+      METHOD = key
+      KEYCODE = 19
+   }
+ ; p
+
+   VOLUME_UP {
+      METHOD = key
+      KEYCODE = 43
+   }
+ ; page up
+
+   VOLUME_DOWN {
+      METHOD = key
+      KEYCODE = 45
+   }
+ ; page down
+
+   EDITOR {
+      METHOD = key
+      KEYCODE = 293
+   }
+ ; F12
 
 }
-
-
-;    /* The keyboard syms have been cleverly chosen to map to ASCII */
-;    SDLK_BACKSPACE      = 8,
-;    SDLK_TAB        = 9,
-;    SDLK_CLEAR      = 12,
-;    SDLK_RETURN     = 13,
-;    SDLK_PAUSE      = 19,
-;    SDLK_ESCAPE     = 27,
-;    SDLK_SPACE      = 32,
-;    SDLK_EXCLAIM        = 33,
-;    SDLK_QUOTEDBL       = 34,
-;    SDLK_HASH       = 35,
-;    SDLK_DOLLAR     = 36,
-;    SDLK_AMPERSAND      = 38,
-;    SDLK_QUOTE      = 39,
-;    SDLK_LEFTPAREN      = 40,
-;    SDLK_RIGHTPAREN     = 41,
-;    SDLK_ASTERISK       = 42,
-;    SDLK_PLUS       = 43,
-;    SDLK_COMMA      = 44,
-;    SDLK_MINUS      = 45,
-;    SDLK_PERIOD     = 46,
-;    SDLK_SLASH      = 47,
-;    SDLK_0          = 48,
-;    SDLK_1          = 49,
-;    SDLK_2          = 50,
-;    SDLK_3          = 51,
-;    SDLK_4          = 52,
-;    SDLK_5          = 53,
-;    SDLK_6          = 54,
-;    SDLK_7          = 55,
-;    SDLK_8          = 56,
-;    SDLK_9          = 57,
-;    SDLK_COLON      = 58,
-;    SDLK_SEMICOLON      = 59,
-;    SDLK_LESS       = 60,
-;    SDLK_EQUALS     = 61,
-;    SDLK_GREATER        = 62,
-;    SDLK_QUESTION       = 63,
-;    SDLK_AT         = 64,
-;    /*
-;       Skip uppercase letters
-;     */
-;    SDLK_LEFTBRACKET    = 91,
-;    SDLK_BACKSLASH      = 92,
-;    SDLK_RIGHTBRACKET   = 93,
-;    SDLK_CARET      = 94,
-;    SDLK_UNDERSCORE     = 95,
-;    SDLK_BACKQUOTE      = 96,
-;    SDLK_a          = 97,
-;    SDLK_b          = 98,
-;    SDLK_c          = 99,
-;    SDLK_d          = 100,
-;    SDLK_e          = 101,
-;    SDLK_f          = 102,
-;    SDLK_g          = 103,
-;    SDLK_h          = 104,
-;    SDLK_i          = 105,
-;    SDLK_j          = 106,
-;    SDLK_k          = 107,
-;    SDLK_l          = 108,
-;    SDLK_m          = 109,
-;    SDLK_n          = 110,
-;    SDLK_o          = 111,
-;    SDLK_p          = 112,
-;    SDLK_q          = 113,
-;    SDLK_r          = 114,
-;    SDLK_s          = 115,
-;    SDLK_t          = 116,
-;    SDLK_u          = 117,
-;    SDLK_v          = 118,
-;    SDLK_w          = 119,
-;    SDLK_x          = 120,
-;    SDLK_y          = 121,
-;    SDLK_z          = 122,
-;    SDLK_DELETE     = 127,
-;    /* End of ASCII mapped keysyms */
-;
-;    /* Numeric keypad */
-;    SDLK_KP0        = 256,
-;    SDLK_KP1        = 257,
-;    SDLK_KP2        = 258,
-;    SDLK_KP3        = 259,
-;    SDLK_KP4        = 260,
-;    SDLK_KP5        = 261,
-;    SDLK_KP6        = 262,
-;    SDLK_KP7        = 263,
-;    SDLK_KP8        = 264,
-;    SDLK_KP9        = 265,
-;    SDLK_KP_PERIOD      = 266,
-;    SDLK_KP_DIVIDE      = 267,
-;    SDLK_KP_MULTIPLY    = 268,
-;    SDLK_KP_MINUS       = 269,
-;    SDLK_KP_PLUS        = 270,
-;    SDLK_KP_ENTER       = 271,
-;    SDLK_KP_EQUALS      = 272,
-;
-;    /* Arrows + Home/End pad */
-;    SDLK_UP         = 273,
-;    SDLK_DOWN       = 274,
-;    SDLK_RIGHT      = 275,
-;    SDLK_LEFT       = 276,
-;    SDLK_INSERT     = 277,
-;    SDLK_HOME       = 278,
-;    SDLK_END        = 279,
-;    SDLK_PAGEUP     = 280,
-;    SDLK_PAGEDOWN       = 281,
-;
-;    /* Function keys */
-;    SDLK_F1         = 282,
-;    SDLK_F2         = 283,
-;    SDLK_F3         = 284,
-;    SDLK_F4         = 285,
-;    SDLK_F5         = 286,
-;    SDLK_F6         = 287,
-;    SDLK_F7         = 288,
-;    SDLK_F8         = 289,
-;    SDLK_F9         = 290,
-;    SDLK_F10        = 291,
-;    SDLK_F11        = 292,
-;    SDLK_F12        = 293,
-;    SDLK_F13        = 294,
-;    SDLK_F14        = 295,
-;    SDLK_F15        = 296,
-;
-;    /* Key state modifier keys */
-;    SDLK_NUMLOCK        = 300,
-;    SDLK_CAPSLOCK       = 301,
-;    SDLK_SCROLLOCK      = 302,
-;    SDLK_RSHIFT     = 303,
-;    SDLK_LSHIFT     = 304,
-;    SDLK_RCTRL      = 305,
-;    SDLK_LCTRL      = 306,
-;    SDLK_RALT       = 307,
-;    SDLK_LALT       = 308,
-;    SDLK_RMETA      = 309,
-;    SDLK_LMETA      = 310,
-;    SDLK_LSUPER     = 311,      /* Left "Windows" key */
-;    SDLK_RSUPER     = 312,      /* Right "Windows" key */
-;    SDLK_MODE       = 313,      /* "Alt Gr" key */
-;    SDLK_COMPOSE        = 314,      /* Multi-key compose key */
-;
-;    /* Miscellaneous function keys */
-;    SDLK_HELP       = 315,
-;    SDLK_PRINT      = 316,
-;    SDLK_SYSREQ     = 317,
-;    SDLK_BREAK      = 318,
-;    SDLK_MENU       = 319,
-;    SDLK_POWER      = 320,      /* Power Macintosh power key */
-;    SDLK_EURO       = 321,      /* Some european keyboards */
-;    SDLK_UNDO       = 322,      /* Atari keyboard has Undo */
-;
+ ;    /* The keyboard syms have been cleverly chosen to map to ASCII */
+ ;    SDLK_BACKSPACE      = 8,
+ ;    SDLK_TAB        = 9,
+ ;    SDLK_CLEAR      = 12,
+ ;    SDLK_RETURN     = 13,
+ ;    SDLK_PAUSE      = 19,
+ ;    SDLK_ESCAPE     = 27,
+ ;    SDLK_SPACE      = 32,
+ ;    SDLK_EXCLAIM        = 33,
+ ;    SDLK_QUOTEDBL       = 34,
+ ;    SDLK_HASH       = 35,
+ ;    SDLK_DOLLAR     = 36,
+ ;    SDLK_AMPERSAND      = 38,
+ ;    SDLK_QUOTE      = 39,
+ ;    SDLK_LEFTPAREN      = 40,
+ ;    SDLK_RIGHTPAREN     = 41,
+ ;    SDLK_ASTERISK       = 42,
+ ;    SDLK_PLUS       = 43,
+ ;    SDLK_COMMA      = 44,
+ ;    SDLK_MINUS      = 45,
+ ;    SDLK_PERIOD     = 46,
+ ;    SDLK_SLASH      = 47,
+ ;    SDLK_0          = 48,
+ ;    SDLK_1          = 49,
+ ;    SDLK_2          = 50,
+ ;    SDLK_3          = 51,
+ ;    SDLK_4          = 52,
+ ;    SDLK_5          = 53,
+ ;    SDLK_6          = 54,
+ ;    SDLK_7          = 55,
+ ;    SDLK_8          = 56,
+ ;    SDLK_9          = 57,
+ ;    SDLK_COLON      = 58,
+ ;    SDLK_SEMICOLON      = 59,
+ ;    SDLK_LESS       = 60,
+ ;    SDLK_EQUALS     = 61,
+ ;    SDLK_GREATER        = 62,
+ ;    SDLK_QUESTION       = 63,
+ ;    SDLK_AT         = 64,
+ ;    /*
+ ;       Skip uppercase letters
+ ;     */
+ ;    SDLK_LEFTBRACKET    = 91,
+ ;    SDLK_BACKSLASH      = 92,
+ ;    SDLK_RIGHTBRACKET   = 93,
+ ;    SDLK_CARET      = 94,
+ ;    SDLK_UNDERSCORE     = 95,
+ ;    SDLK_BACKQUOTE      = 96,
+ ;    SDLK_a          = 97,
+ ;    SDLK_b          = 98,
+ ;    SDLK_c          = 99,
+ ;    SDLK_d          = 100,
+ ;    SDLK_e          = 101,
+ ;    SDLK_f          = 102,
+ ;    SDLK_g          = 103,
+ ;    SDLK_h          = 104,
+ ;    SDLK_i          = 105,
+ ;    SDLK_j          = 106,
+ ;    SDLK_k          = 107,
+ ;    SDLK_l          = 108,
+ ;    SDLK_m          = 109,
+ ;    SDLK_n          = 110,
+ ;    SDLK_o          = 111,
+ ;    SDLK_p          = 112,
+ ;    SDLK_q          = 113,
+ ;    SDLK_r          = 114,
+ ;    SDLK_s          = 115,
+ ;    SDLK_t          = 116,
+ ;    SDLK_u          = 117,
+ ;    SDLK_v          = 118,
+ ;    SDLK_w          = 119,
+ ;    SDLK_x          = 120,
+ ;    SDLK_y          = 121,
+ ;    SDLK_z          = 122,
+ ;    SDLK_DELETE     = 127,
+ ;    /* End of ASCII mapped keysyms */
+ ;
+ ;    /* Numeric keypad */
+ ;    SDLK_KP0        = 256,
+ ;    SDLK_KP1        = 257,
+ ;    SDLK_KP2        = 258,
+ ;    SDLK_KP3        = 259,
+ ;    SDLK_KP4        = 260,
+ ;    SDLK_KP5        = 261,
+ ;    SDLK_KP6        = 262,
+ ;    SDLK_KP7        = 263,
+ ;    SDLK_KP8        = 264,
+ ;    SDLK_KP9        = 265,
+ ;    SDLK_KP_PERIOD      = 266,
+ ;    SDLK_KP_DIVIDE      = 267,
+ ;    SDLK_KP_MULTIPLY    = 268,
+ ;    SDLK_KP_MINUS       = 269,
+ ;    SDLK_KP_PLUS        = 270,
+ ;    SDLK_KP_ENTER       = 271,
+ ;    SDLK_KP_EQUALS      = 272,
+ ;
+ ;    /* Arrows + Home/End pad */
+ ;    SDLK_UP         = 273,
+ ;    SDLK_DOWN       = 274,
+ ;    SDLK_RIGHT      = 275,
+ ;    SDLK_LEFT       = 276,
+ ;    SDLK_INSERT     = 277,
+ ;    SDLK_HOME       = 278,
+ ;    SDLK_END        = 279,
+ ;    SDLK_PAGEUP     = 280,
+ ;    SDLK_PAGEDOWN       = 281,
+ ;
+ ;    /* Function keys */
+ ;    SDLK_F1         = 282,
+ ;    SDLK_F2         = 283,
+ ;    SDLK_F3         = 284,
+ ;    SDLK_F4         = 285,
+ ;    SDLK_F5         = 286,
+ ;    SDLK_F6         = 287,
+ ;    SDLK_F7         = 288,
+ ;    SDLK_F8         = 289,
+ ;    SDLK_F9         = 290,
+ ;    SDLK_F10        = 291,
+ ;    SDLK_F11        = 292,
+ ;    SDLK_F12        = 293,
+ ;    SDLK_F13        = 294,
+ ;    SDLK_F14        = 295,
+ ;    SDLK_F15        = 296,
+ ;
+ ;    /* Key state modifier keys */
+ ;    SDLK_NUMLOCK        = 300,
+ ;    SDLK_CAPSLOCK       = 301,
+ ;    SDLK_SCROLLOCK      = 302,
+ ;    SDLK_RSHIFT     = 303,
+ ;    SDLK_LSHIFT     = 304,
+ ;    SDLK_RCTRL      = 305,
+ ;    SDLK_LCTRL      = 306,
+ ;    SDLK_RALT       = 307,
+ ;    SDLK_LALT       = 308,
+ ;    SDLK_RMETA      = 309,
+ ;    SDLK_LMETA      = 310,
+ ;    SDLK_LSUPER     = 311,      /* Left "Windows" key */
+ ;    SDLK_RSUPER     = 312,      /* Right "Windows" key */
+ ;    SDLK_MODE       = 313,      /* "Alt Gr" key */
+ ;    SDLK_COMPOSE        = 314,      /* Multi-key compose key */
+ ;
+ ;    /* Miscellaneous function keys */
+ ;    SDLK_HELP       = 315,
+ ;    SDLK_PRINT      = 316,
+ ;    SDLK_SYSREQ     = 317,
+ ;    SDLK_BREAK      = 318,
+ ;    SDLK_MENU       = 319,
+ ;    SDLK_POWER      = 320,      /* Power Macintosh power key */
+ ;    SDLK_EURO       = 321,      /* Some european keyboards */
+ ;    SDLK_UNDO       = 322,      /* Atari keyboard has Undo */
+ ;
 		writefln( getText() );
 	}
 
-	void update() {
-		write( mRoot.value, getText() );
+	bool update() {
+		try {
+			write( mRoot.value, getText() );
+		}
+		catch(FileException fe ) {
+			return false;
+		}
+		return true;
 	}
 
 	/// returns a root-level block node
 		return(true);
 	}
 
+	bool setValue( char[] valueid, int val ) {
+		return( setValue( valueid, std.string.toString( val )) );
+	}
+
 }
 
 /*

src/gamemanager.d

 import screen;
 import editor;
 
+/// technically : the GamePartManager
 class GameManager {
 
 	GamePart mGamePart;
 
 		switch( mGamePart.getState()  ) {
 
-			case GAMEPART_STATE.MENU_MAP:
-				delete( mGamePart );
-				mGamePart = new MapMenu();
-			break;
-
-			case GAMEPART_STATE.NEW_GAME:
-				delete( mGamePart );
-				mGamePart = new Game(false);
-			break;
-
-			case GAMEPART_STATE.GAME_WON:
-				delete( mGamePart );
-				mGamePart = new MainMenu(); // new GameWon();
-			break;
-
-			case GAMEPART_STATE.MAIN_MENU:
-				delete( mGamePart );
-				mGamePart = new MainMenu();
-			break;
-
-			case GAMEPART_STATE.EDITOR_INGAME:
-				delete( mGamePart );
-				mGamePart = new Editor(true);
-			break;
-
-			case GAMEPART_STATE.EDITOR:
-				delete( mGamePart );
-				mGamePart = new Editor(false);
-			break;
-
-			case GAMEPART_STATE.MENU_MAP_EDITOR:
-				delete( mGamePart );
-				mGamePart = new MapMenuEditor();
-			break;
-
-			case GAMEPART_STATE.CONTINUE:
-				delete( mGamePart );
-				mGamePart = new Game(true);
-			break;
-
-			case GAMEPART_STATE.MENU_OPTIONS:
-				delete( mGamePart );
-				mGamePart = new MenuOptions();
-			break;
-
+			case GAMEPART_STATE.MENU_MAP: changePart( new MapMenu() ); break;
+			case GAMEPART_STATE.NEW_GAME: changePart( new Game(false) ); break;
+			case GAMEPART_STATE.GAME_WON: changePart( new MainMenu() ); break;
+			case GAMEPART_STATE.MAIN_MENU: changePart( new MainMenu() ); break;
+			case GAMEPART_STATE.EDITOR_INGAME: changePart( new Editor(true) ); break;
+			case GAMEPART_STATE.EDITOR: changePart( new Editor(false) ); break;
+			case GAMEPART_STATE.MENU_MAP_EDITOR: changePart( new MapMenuEditor() );	break;
+			case GAMEPART_STATE.CONTINUE: changePart( new Game(true)); break;
+			case GAMEPART_STATE.MENU_OPTIONS: changePart( new MenuOptions() ); break;
+			//case GAMEPART_STATE.INPUT_CONFIG: changePart( new InputConfigurationDialog() ); break;
 			case GAMEPART_STATE.EXIT:
 				delete( mGamePart );
 				return( false );
 
 			default:
 			break;
-
 		}
 		gScreen.vsync();
 		return( true );
 	}
 
+	void changePart( GamePart gp ) {
+		delete(mGamePart);
+		mGamePart = gp;
+	}
+
 }
 		}
 
 		void render() {
-			if( mHighlighted) SDL_BlitSurface( mHl, null, gScreen.mSurface, &mRect );
+			if( mHl && mHighlighted) SDL_BlitSurface( mHl, null, gScreen.mSurface, &mRect );
 			//mGraphics.drawBox( cast(short)mX-2, cast(short)mY-2, cast(ushort)mW+4, cast(ushort)mH+4 );
 		}
 
 			mRect.y = cast(short)mY;
 		}
 
+		void changeWidth( uint width ) {
+			if( (cast(Panel)mParent) ) {
+				(cast(Panel)mParent).changeChildWidth( width - mW );
+				mW = width;
+
+				// rebuild the highlight bitmap and set the new width
+				mRect.w = cast(ushort)mW;
+				if( mHl ) SDL_FreeSurface( mHl );
+				buildHighlight();
+			}
+		}
+
 	}
 
 
 			mIcon = gTextureManager.get( imageName );
 			mW = mIcon.w;
 			mH = mIcon.h;
+
 			with( mRect ) {
 				w=cast(ushort)mW;
 				h=cast(ushort)mH;
 				x=cast(short)mX;
 				y=cast(short)mY;
 			}
+
 			buildHighlight(128);
 		}
 
 		SDL_Rect mRect;
 		Button mHighlighted;
 		uint mSelectedTextInput;
+		bool mHidden=false;
 
 		enum ALIGN { FREE, MIN, MAX, CENTERED };
 		ALIGN mHAlign = ALIGN.FREE;
 			foreach( p; mPanels ) delete( p );
 		}
 
+		void hide() { mHidden=true; }
+		void show() { mHidden=false; }
+		bool hidden() { return mHidden; }
+
+		void tab( uint size ) {
+			if( size == 0 ) return;
+			mXInsert = (mXInsert/size + 1) * size;
+		}
+
 		TextButton addTextButton( string caption, string fontid, ButtonListener listener=null ) {
 			TextButton b = new TextButton(this, mXInsert, mYInsert, caption, fontid, listener );
 			mButtons ~= b;
 			return( m );
 		}
 
+		void changeChildWidth( int width ) {
+			mXInsert += width;
+		}
+
 		void nl() {
 			mXInsert = mMargin;
 			mYInsert += mLineHeight + mLineGap;
 		if( mPy > 480 ) mPy = 480;
 
 		foreach( p; mPanels ) {
-			p.checkButtons( mPx, mPy, click );
-			p.injectKey( gInputManager.getKeyUp() );
-			p.render();
+			if( !p.hidden ) {
+				p.checkButtons( mPx, mPy, click );
+				p.injectKey( gInputManager.getKeyDown() );
+				p.render();
+			}
 		}
 
 		if( !mPointerHidden ) {

src/inputconfiguration.d

 	}
 
 
+	bool save() {
 
+		auto cf = new ConfigFile( gInputManager.mConfigFilename  );
+		if( !cf.load() ) return( false );
 
-	bool save() {
-		// TODO: implement saving : would need setting functions before !
+		foreach( action; mInputActions ) {
+
+			string key = "input." ~ action.name;
+
+			cf.setValue( key ~ ".method", getMethodName(action.method) );
+
+			switch( action.method ) {
+				case IAM_KEY:
+					cf.setValue( key ~ ".keycode", action.keycode );
+				break;
+				case IAM_BUTTON:
+					cf.setValue( key ~ ".joystick", action.joynum );
+					cf.setValue( key ~ ".button", action.button );
+				break;
+				case IAM_AXIS:
+					cf.setValue( key ~ ".joystick", action.joynum );
+					cf.setValue( key ~ ".axis", action.axis );
+					cf.setValue( key ~ ".dir", action.dir );
+					cf.setValue( key ~ ".threshold", action.threshold );
+				break;
+				default:
+				break;
+			}
+
+		}
+		//debug cf.display();
+		if( !cf.update() ) return( false );
+
 		return( true );
 	}
 
 
 	}
 
+	string getMethodName( int method ) {
+		switch( method ) {
+			case IAM_KEY: return( "key" );
+			case IAM_AXIS: return( "axis" );
+			case IAM_BUTTON: return( "button" );
+			default: return( "invalid" );
+		}
+	}
+
 }
 
 

src/inputconfigurationdialog.d

+
+module inputconfigurationdialog;
+
+import gui;
+import inputmanager;
+import inputconfiguration;
+import inputactions;
+debug import std.stdio;
+
+class InputConfigurationDialog: GUI.ButtonListener {
+
+	GUI.Panel mPanel;
+	GUI.Button mApplyButton;
+	GUI.Button mCancelButton;
+	GUI.Button[] mActionButtons;
+
+	InputConfiguration mInputConfig;
+
+	InputDetectDialog mDetect;
+	int mConfiguredAction;
+
+	bool mDone;
+
+	this() {
+		mInputConfig = new InputConfiguration();
+		mInputConfig.load( gInputManager.mConfigFilename );
+
+		recreatePanel();
+	}
+
+	~this() {
+		GUI.instance.removePanel( mPanel );
+		delete( mInputConfig );
+	}
+
+	void recreatePanel() {
+
+		if( mPanel ) GUI.instance.removePanel( mPanel );
+
+		mActionButtons.length = 0;
+
+		mPanel = GUI.instance.addPanel(0,0);
+		mPanel.setAlign( GUI.Panel.ALIGN.CENTERED, GUI.Panel.ALIGN.CENTERED );
+
+		foreach( ia; mInputConfig.mInputActions ) {
+			GUI.TextButton b = mPanel.addTextButton( ia.name, "", this );
+			b.changeWidth(180);
+			mActionButtons ~= b;
+			mPanel.tab(250);
+			switch( ia.method ) {
+				case IAM_KEY:
+					mPanel.addLabel( "key: " ~ gInputManager.getKeyName( ia.keycode ) );
+				break;
+				case IAM_BUTTON:
+					mPanel.addLabel( "joy#" ~ std.string.toString(ia.joynum) ~ " button#" ~ std.string.toString(ia.button) );
+				break;
+				case IAM_AXIS:
+					string dir = std.string.toString( ia.dir );
+					mPanel.addLabel( "joy#" ~ std.string.toString(ia.joynum) ~ " axis#" ~ std.string.toString(ia.axis) ~ " dir:" ~ dir );
+				break;
+				default:
+				break;
+			}
+			mPanel.nl();
+		}
+
+		mPanel.nl();
+
+		mApplyButton = mPanel.addTextButton( "Apply", "", this );
+		mCancelButton = mPanel.addTextButton( "Cancel", "", this );
+	}
+
+
+	bool done() {
+
+		if( mDetect && mDetect.done() ) {
+			if( mDetect.ok ) {
+				mInputConfig.mInputActions[mConfiguredAction] = mDetect.getInputAction();
+				mInputConfig.apply();
+				mInputConfig.save();
+			}
+			delete(mDetect);
+			recreatePanel();
+		}
+
+		return mDone;
+	};
+
+	void onButtonPressed( GUI.Button b ) {
+		if( b == mApplyButton ) {
+			mDone = true;
+			return;
+		}
+		if( b == mCancelButton ) {
+			mDone = true;
+			return;
+		}
+
+		foreach( n, button; mActionButtons ) {
+			if( b == button ) {
+				mPanel.hide;
+				mConfiguredAction = n;
+				mDetect = new InputDetectDialog( mInputConfig.mInputActions[n] );
+			}
+		}
+	}
+
+}
+
+
+class InputDetectDialog {
+
+	GUI.Panel mPanel;
+	SInputAction mInputAction;
+	bool mDone;
+	bool mOk;
+
+	this( inout SInputAction inputaction ) {
+		mPanel = GUI.instance.addPanel(0,0);
+		mPanel.setAlign( GUI.Panel.ALIGN.CENTERED, GUI.Panel.ALIGN.CENTERED );
+		mPanel.addLabel( "Press a key "); //, a joystick button or move a joystick axis" );
+		mPanel.nl();
+		mPanel.addLabel( "to configure <" ~ inputaction.name ~ ">" );
+		mPanel.nl();
+		mPanel.addLabel( "(or [Esc] to cancel.)" );
+
+		mInputAction = inputaction;
+	}
+
+	~this() {
+		GUI.instance.removePanel( mPanel );
+	}
+
+	bool done() {
+		probe();
+		return( mDone );
+	}
+
+	bool ok() { return mOk; }
+
+	void probe() {
+		int key = gInputManager.getKeyDownCode();
+
+		if( key != 0 ) {
+			mDone = true;
+			if( key == 27 ) return;
+
+			with( mInputAction ) { method = IAM_KEY; keycode = key; }
+			mOk = true;
+			return;
+		}
+		//TODO: probe joysticks
+	}
+
+	SInputAction getInputAction() {
+		return( mInputAction );
+	}
+}
+

src/inputmanager.d

     ubyte* mSDLKeys;
     // char[][] mPossibleActions;
     char[] mConfigFilename;
-    ubyte mLastPressedKey;
+    ubyte mLastPressedKeyChar;
+	uint mLastPressedKeySym;
 
     bool[8] mMouseButtonsUp;
     bool[8] mMouseButtonsDown;
         mMouseButtonsUp[] = false;
         mMouseButtonsDown[] = false;
 
-        mLastPressedKey = 0;
+        mLastPressedKeyChar = 0;
+		mLastPressedKeySym = 0;
 
         while ( SDL_PollEvent(&event) ) {
 
                 break;
 
             case SDL_KEYDOWN:
-                mLastPressedKey = getKeyValue( event.key.keysym.unicode);
+                mLastPressedKeyChar = getKeyValue( event.key.keysym.unicode);
+                mLastPressedKeySym = event.key.keysym.sym;
                 break;
 
             default:
 		}
 	}
 
-    char getUnicodeValue( inout SDL_KeyboardEvent key ) {
-        // magic numbers courtesy of SDL docs :)
-        const int INTERNATIONAL_MASK = 0xFF80, UNICODE_MASK = 0x7F;
-        int uni = key.keysym.unicode;
-        if ( uni == 0 ) {
-        	// not translatable key (like up or down arrows)
-            // probably not useful as string input
-            // we could optionally use this to get some value
-            // for it: SDL_GetKeyName( key );
-            return 0;
-        }
-        if ( ( uni & INTERNATIONAL_MASK ) == 0 ) {
-            if ( SDL_GetModState() & KMOD_SHIFT ) {
-                return cast(char)(/*toupper(*/ uni & UNICODE_MASK /*)*/ );
-            } else {
-                return cast(char)(uni & UNICODE_MASK);
-            }
-        }
-        else {  // we have a funky international character. one we can't read :(
-                // we could do nothing, or we can just show some sign of input, like so:
-            return '?';
-        }
+	string getKeyName( int sdlkeysym ) {
+		return( std.string.toString( SDL_GetKeyName( sdlkeysym ) ));
+	}
+
+
+    ubyte* getKeys() {
+        return( mSDLKeys );
     }
 
+    ubyte getKeyDown() {
+        return( mLastPressedKeyChar );
+    }
+
+	uint getKeyDownCode() {
+		return( mLastPressedKeySym );
+	}
 
     void getMousePositionRelative( out int x, out int y ) {
         SDL_GetRelativeMouseState( &x, &y );
             break;
         }
     }
-
-    ubyte* getKeys() {
-        return( mSDLKeys );
-    }
-
-    ubyte getKeyUp() {
-        return( mLastPressedKey );
-    }
 }
 
 import textsurface;
 import texturemanager;
 import gui;
+import inputconfigurationdialog;
 
 // ============ base Menu class ===============
 
 
 // ============================================================
 
+class MenuBackground {
+	static short mX, mY; // static to avoid graphical "jump" between menus
+	short mDx, mDy;
+	SDL_Surface* mBg;
+	SDL_Rect mRect;
+
+	this() {
+		mBg = gTextureManager.get("scrollbg.png");
+		mDx = 1; //cast(short)(std.random.rand()%2);
+		mDy = 1; //cast(short)(std.random.rand()%2);
+		with(mRect) { w=cast(ushort)mBg.w; h=cast(ushort)mBg.h; }
+	}
+
+	void draw() {
+		mX = cast(short)(( mX + mDx ) % 32);
+		mY = cast(short)(( mY + mDy ) % 32);
+		with(mRect) {
+			x = -mX;
+			y = -mY;
+		}
+		SDL_BlitSurface( mBg, null, gScreen.mSurface, &mRect );
+	}
+}
+
+// ============================================================
+
 class MenuBase: GamePart, GUI.MenuListener {
 	GUI.Menu mMenu;
 	GAMEPART_STATE mState = GAMEPART_STATE.GAME;
-	SDL_Surface* mBg;
+	//SDL_Surface* mBg;
+	MenuBackground mBg;
+
 	this() {
 		mMenu = GUI.instance.addMenu( 20, 20 );
 		mMenu.setListener(this);
 		mMenu.setAlign( GUI.Panel.ALIGN.CENTERED, GUI.Panel.ALIGN.CENTERED );
-		mBg = gTextureManager.get("background.png");
+		//mBg = gTextureManager.get("background.png");
+		mBg = new MenuBackground();
 	}
 
 	~this() {
 
 	void update() {
 
-		SDL_BlitSurface( mBg, null, gScreen.mSurface, null );
+		//SDL_BlitSurface( mBg, null, gScreen.mSurface, null );
+		mBg.draw();
 
 		int x, y;
 		gInputManager.getMousePositionRelative( x,y );
 
 	string[] mOptions = ["Toggle Fulscreen","(Input configuration)", "Cancel" ];
 
+	InputConfigurationDialog mInputConfigurationDialog;
+
 	this() {
 		mMenu.addItems( mOptions );
 	}
 
 	void onSelectedItem( int item ) {
 
+		// cancel button is the same as pressing escape
 		if( item == mOptions.length-1 ) item = -1;
 
 		switch( item ) {
 
 			case 1:
 				//mState = GAMEPART_STATE.INPUT_CONFIG;
+				mInputConfigurationDialog = new InputConfigurationDialog();
+				mMenu.hide();
 			break;
 
 			default: break;
 		}
 	}
+
+	void update() {
+		if( mInputConfigurationDialog !is null ) {
+			if( mInputConfigurationDialog.done() ) {
+				delete( mInputConfigurationDialog );
+				mMenu.show();
+			}
+		}
+		super.update();
+	}
 }