text: // Copyright (C) 2002-2007 Nikolaus Gebhardt
  // Copyright (C) 2007-2011 Christian Stehno
  // This file is part of the "Irrlicht Engine".
  // For conditions of distribution and use, see copyright notice in irrlicht.h
  
  #include "CIrrDeviceAndroid.h"
  
  #ifdef _IRR_COMPILE_WITH_ANDROID_DEVICE_
  
  #include "os.h"
  #include "CFileSystem.h"
  #include "CAndroidAssetReader.h"
  #include "CAndroidAssetFileArchive.h"
  #include "CKeyEventWrapper.h"
  #include "CEGLManager.h"
  #include "ISceneManager.h"
  #include "IGUIEnvironment.h"
  #include "CEGLManager.h"
  
  namespace irr
  {
  	namespace video
  	{
  		IVideoDriver* createOGLES1Driver(const SIrrlichtCreationParameters& params,
  			io::IFileSystem* io, video::IContextManager* contextManager);
  
  		IVideoDriver* createOGLES2Driver(const SIrrlichtCreationParameters& params,
  			io::IFileSystem* io, video::IContextManager* contextManager);
  	}
  }
  
  namespace irr
  {
  
  CIrrDeviceAndroid::CIrrDeviceAndroid(const SIrrlichtCreationParameters& param)
  	: CIrrDeviceStub(param), Focused(false), Initialized(false), Paused(true), JNIEnvAttachedToVM(0)
  {
  #ifdef _DEBUG
  	setDebugName("CIrrDeviceAndroid");
  #endif
  
  	// Get the interface to the native Android activity.
  	Android = (android_app*)(param.PrivateData);
  
  	// Set the private data so we can use it in any static callbacks.
  	Android->userData = this;
  
  	// Set the default command handler. This is a callback function that the Android
  	// OS invokes to send the native activity messages.
  	Android->onAppCmd = handleAndroidCommand;
  	
  	createKeyMap();
  
  	// Create a sensor manager to receive touch screen events from the java activity.
  	SensorManager = ASensorManager_getInstance();
  	SensorEventQueue = ASensorManager_createEventQueue(SensorManager, Android->looper, LOOPER_ID_USER, 0, 0);
  	Android->onInputEvent = handleInput;
  
  	// Create EGL manager.
  	ContextManager = new video::CEGLManager();
  
  	os::Printer::log("Waiting for Android activity window to be created.", ELL_DEBUG);
  
  	do
  	{
  		s32 Events = 0;
  		android_poll_source* Source = 0;
  
  		while ((ALooper_pollAll(((Focused && !Paused) || !Initialized) ? 0 : -1, 0, &Events, (void**)&Source)) >= 0)
  		{
  			if(Source)
  				Source->process(Android, Source);
  		}
  	}
  	while(!Initialized);
  }
  
  
  CIrrDeviceAndroid::~CIrrDeviceAndroid()
  {
  	if (GUIEnvironment)
  	{
  		GUIEnvironment->drop();
  		GUIEnvironment = 0;
  	}
  
  	if (SceneManager)
  	{
  		SceneManager->drop();
  		SceneManager = 0;
  	}
  
  	if (VideoDriver)
  	{
  		VideoDriver->drop();
  		VideoDriver = 0;
  	}
  }
  
  bool CIrrDeviceAndroid::run()
  {
  	if (!Initialized)
  		return false;
  
  	os::Timer::tick();
  
  	s32 Events = 0;
  	android_poll_source* Source = 0;
  
  	while ((ALooper_pollAll(((Focused && !Paused) || !Initialized) ? 0 : -1, 0, &Events, (void**)&Source)) >= 0)
  	{
  		if(Source)
  			Source->process(Android, Source);
  
  		if(!Initialized)
  			break;
  	}
  
  	return Initialized;
  }
  
  void CIrrDeviceAndroid::yield()
  {
  	struct timespec ts = {0,1};
  	nanosleep(&ts, NULL);	
  }
  
  void CIrrDeviceAndroid::sleep(u32 timeMs, bool pauseTimer)
  {
  	const bool wasStopped = Timer ? Timer->isStopped() : true;
  
  	struct timespec ts;
  	ts.tv_sec = (time_t) (timeMs / 1000);
  	ts.tv_nsec = (long) (timeMs % 1000) * 1000000;
  
  	if (pauseTimer && !wasStopped)
  		Timer->stop();
  
  	nanosleep(&ts, NULL);
  
  	if (pauseTimer && !wasStopped)
  		Timer->start();	
  }
  
  void CIrrDeviceAndroid::setWindowCaption(const wchar_t* text)
  {
  }
  
  bool CIrrDeviceAndroid::present(video::IImage* surface, void* windowId, core::rect<s32>* srcClip)
  {
  	return true;
  }
  
  bool CIrrDeviceAndroid::isWindowActive() const
  {
  	return (Focused && !Paused);
  }
  
  bool CIrrDeviceAndroid::isWindowFocused() const
  {
  	return Focused;
  }
  
  bool CIrrDeviceAndroid::isWindowMinimized() const
  {
  	return !Focused;
  }
  
  void CIrrDeviceAndroid::closeDevice()
  {
  	ANativeActivity_finish(Android->activity);
  }
  
  void CIrrDeviceAndroid::setResizable(bool resize)
  {
  }
  
  void CIrrDeviceAndroid::minimizeWindow()
  {
  }
  
  void CIrrDeviceAndroid::maximizeWindow()
  {
  }
  
  void CIrrDeviceAndroid::restoreWindow()
  {
  }
  
  core::position2di CIrrDeviceAndroid::getWindowPosition()
  {
  	return core::position2di(0, 0);
  }
  
  E_DEVICE_TYPE CIrrDeviceAndroid::getType() const
  {
  	return EIDT_ANDROID;
  }
  
  void CIrrDeviceAndroid::handleAndroidCommand(android_app* app, int32_t cmd)
  {
  	CIrrDeviceAndroid* device = (CIrrDeviceAndroid*)app->userData;
  
  	switch (cmd)
  	{
  		case APP_CMD_SAVE_STATE:
  			os::Printer::log("Android command APP_CMD_SAVE_STATE", ELL_DEBUG);
  		break;
  		case APP_CMD_INIT_WINDOW:
  			char log_init_window[256];
  			os::Printer::log("Android command APP_CMD_INIT_WINDOW", ELL_DEBUG);
  			device->getExposedVideoData().OGLESAndroid.Window = app->window;
  
  			if (device->CreationParams.WindowSize.Width == 0 || device->CreationParams.WindowSize.Height == 0)
  			{
  				device->CreationParams.WindowSize.Width = ANativeWindow_getWidth(app->window);
  				device->CreationParams.WindowSize.Height = ANativeWindow_getHeight(app->window);
  			}
  			sprintf(log_init_window, "init window: width = %d, height = %d", ANativeWindow_getWidth(app->window), ANativeWindow_getHeight(app->window));
  			os::Printer::log(log_init_window);
  			device->getContextManager()->initialize(device->CreationParams, device->ExposedVideoData);
  			device->getContextManager()->generateSurface();
  			device->getContextManager()->generateContext();
  			device->getContextManager()->activateContext(device->getContextManager()->getContext());
  			if (device->CreationParams.WindowSize.Width != 1 && device->CreationParams.WindowSize.Height != 1)
  			{
  				if (!device->Initialized)
  		        {
  				    io::CAndroidAssetFileArchive* assets = new io::CAndroidAssetFileArchive( device->Android->activity->assetManager, false, false);
  				    assets->addDirectoryToFileList("");
  				    device->FileSystem->addFileArchive(assets);
  				    assets->drop();
  
  				    device->createDriver();
  
  				    if (device->VideoDriver)
  					    device->createGUIAndScene();
  			    }
  			    device->Initialized = true;
  			}
  		    break;
  		case APP_CMD_TERM_WINDOW:
  			os::Printer::log("Android command APP_CMD_TERM_WINDOW", ELL_DEBUG);
  			device->getContextManager()->destroySurface();
  		    break;
  		case APP_CMD_GAINED_FOCUS:
  		     os::Printer::log("Android command APP_CMD_GAINED_FOCUS", ELL_DEBUG);
  		     {
  		          s32 surfaceWidth = ANativeWindow_getWidth(app->window);
  		          s32 surfaceHeight = ANativeWindow_getHeight(app->window);
  				  char log_focus_window[256];
  				  sprintf(log_focus_window, "focus window: width = %d, height = %d", surfaceWidth, surfaceHeight);
  				  os::Printer::log(log_focus_window);
  		          if (device->CreationParams.WindowSize.Width != surfaceWidth || device->CreationParams.WindowSize.Height != surfaceHeight)
  		          {
  		               device->CreationParams.WindowSize.Width = surfaceWidth;
  		               device->CreationParams.WindowSize.Height = surfaceHeight;
  		               device->getContextManager()->destroySurface();
  		               device->getContextManager()->generateSurface();
  		               device->getContextManager()->activateContext(device->getContextManager()->getContext());
  					   if (!device->Initialized)
  					   {
  						   io::CAndroidAssetFileArchive* assets = new io::CAndroidAssetFileArchive( device->Android->activity->assetManager, false, false);
  						   assets->addDirectoryToFileList("");
  						   device->FileSystem->addFileArchive(assets);
  						   assets->drop();
  
  						   device->createDriver();
  
  						   if (device->VideoDriver)
  						   device->createGUIAndScene();
  					   }
  					   device->Initialized = true;
  		          }
  
  		     }
  		     device->Focused = true;
  		    break;
  		case APP_CMD_LOST_FOCUS:
  			os::Printer::log("Android command APP_CMD_LOST_FOCUS", ELL_DEBUG);
  			device->Focused = false;
  		break;
  		case APP_CMD_DESTROY:
  			os::Printer::log("Android command APP_CMD_DESTROY", ELL_DEBUG);
  			device->Initialized = false;
  			break;
  		case APP_CMD_PAUSE:
  			os::Printer::log("Android command APP_CMD_PAUSE", ELL_DEBUG);
  			device->Paused = true;
  			break;
  		case APP_CMD_STOP:
  			os::Printer::log("Android command APP_CMD_STOP", ELL_DEBUG);
  			break;
  		case APP_CMD_RESUME:
  			os::Printer::log("Android command APP_CMD_RESUME", ELL_DEBUG);
  			device->Paused = false;
  			break;
  		case APP_CMD_WINDOW_RESIZED:
  			os::Printer::log("Android command APP_CMD_WINDOW_RESIZED", ELL_DEBUG);
  			break;
  		default:
  			break;
  	}
  }
  
  s32 CIrrDeviceAndroid::handleInput(android_app* app, AInputEvent* androidEvent)
  {
  	CIrrDeviceAndroid* device = (CIrrDeviceAndroid*)app->userData;
  	s32 status = 0;
  
  	switch ( AInputEvent_getType(androidEvent) )
  	{
  		case AINPUT_EVENT_TYPE_MOTION:
  		{
  			SEvent event;
  			event.EventType = EET_TOUCH_INPUT_EVENT;
  
  			s32 eventAction = AMotionEvent_getAction(androidEvent);
  			s32 eventType =  eventAction & AMOTION_EVENT_ACTION_MASK;
  
  			bool touchReceived = true;
  
  			switch (eventType)
  			{
  			case AMOTION_EVENT_ACTION_DOWN:
  			case AMOTION_EVENT_ACTION_POINTER_DOWN:
  				event.TouchInput.Event = ETIE_PRESSED_DOWN;
  				break;
  			case AMOTION_EVENT_ACTION_MOVE:
  				event.TouchInput.Event = ETIE_MOVED;
  				break;
  			case AMOTION_EVENT_ACTION_UP:
  			case AMOTION_EVENT_ACTION_POINTER_UP:
  			case AMOTION_EVENT_ACTION_CANCEL:
  				event.TouchInput.Event = ETIE_LEFT_UP;
  				break;
  			default:
  				touchReceived = false;
  				break;
  			}
  
  			if (touchReceived)
  			{
  				// Process all touches for move action.
  				if (event.TouchInput.Event == ETIE_MOVED)
  				{
  					s32 pointerCount = AMotionEvent_getPointerCount(androidEvent);
  
  					for (s32 i = 0; i < pointerCount; ++i)
  					{
  						event.TouchInput.ID = AMotionEvent_getPointerId(androidEvent, i);
  						event.TouchInput.X = AMotionEvent_getX(androidEvent, i);
  						event.TouchInput.Y = AMotionEvent_getY(androidEvent, i);
  
  						device->postEventFromUser(event);
  					}
  				}
  				else // Process one touch for other actions.
  				{
  					s32 pointerIndex = (eventAction & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
  
  					event.TouchInput.ID = AMotionEvent_getPointerId(androidEvent, pointerIndex);
  					event.TouchInput.X = AMotionEvent_getX(androidEvent, pointerIndex);
  					event.TouchInput.Y = AMotionEvent_getY(androidEvent, pointerIndex);
  
  					device->postEventFromUser(event);
  				}
  
  				status = 1;
  			}
  		}
  		break;
  		case AINPUT_EVENT_TYPE_KEY:
  		{
  			SEvent event;
  			event.EventType = EET_KEY_INPUT_EVENT;
  
  			int32_t keyCode = AKeyEvent_getKeyCode(androidEvent);
  			int32_t keyAction = AKeyEvent_getAction(androidEvent);
  			int32_t keyMetaState = AKeyEvent_getMetaState(androidEvent);
  
  			if ( keyCode >= 0 && (u32)keyCode < device->KeyMap.size() )
  				event.KeyInput.Key = device->KeyMap[keyCode];
  			else
  				event.KeyInput.Key = KEY_UNKNOWN;
  			event.KeyInput.SystemKeyCode = (u32)keyCode;
  			if ( keyAction == AKEY_EVENT_ACTION_DOWN )
  				event.KeyInput.PressedDown = true;
  			else if ( keyAction == AKEY_EVENT_ACTION_UP )
  				event.KeyInput.PressedDown = false;
  			else if ( keyAction == AKEY_EVENT_ACTION_MULTIPLE )
  			{
  				// TODO: Multiple duplicate key events have occurred in a row,
  				// or a complex string is being delivered. The repeat_count
  				// property of the key event contains the number of times the
  				// given key code should be executed.
  				// I guess this might necessary for more complicated i18n key input,
  				// but don't see yet how to handle this correctly.
  			}
  
  			/* no use for meta keys so far.
  			if (   keyMetaState & AMETA_ALT_ON
  				|| keyMetaState & AMETA_ALT_LEFT_ON
  				|| keyMetaState & AMETA_ALT_RIGHT_ON )
  				;
  			// what is a sym?
  			if (   keyMetaState & AMETA_SYM_ON )
  				;
  			*/
  			if (   keyMetaState & AMETA_SHIFT_ON
  				|| keyMetaState & AMETA_SHIFT_LEFT_ON
  				|| keyMetaState & AMETA_SHIFT_RIGHT_ON )
  				event.KeyInput.Shift = true;
  			else
  				event.KeyInput.Shift = false;
  			event.KeyInput.Control = false;
  			
  			// Having memory allocations + going through JNI for each key-press is pretty bad (slow). 
  			// So we do it only for those keys which are likely text-characters and avoid it for all other keys.
  			// So it's fast for keys like game controller input and special keys. And text keys are typically 
  			// only used or entering text and not for gaming on Android, so speed likely doesn't matter there too much.
  			if ( event.KeyInput.Key > 0 )
  			{
  				// TODO:
  				// Not sure why we have to attach a JNIEnv here, but it won't work when doing that in the constructor or 
  				// trying to use the activity->env. My best guess is that the event-handling happens in an own thread.
  				// It means JNIEnvAttachedToVM will never get detached as I don't know a safe way where to do that 
  				// (we could attach & detach each time, but that would probably be slow)
  				// Also - it has to be each time as it get's invalid when the application mode changes.
  				if ( device->Android && device->Android->activity && device->Android->activity->vm )
  				{
  					JavaVMAttachArgs attachArgs;
  					attachArgs.version = JNI_VERSION_1_6;
  					attachArgs.name = 0;
  					attachArgs.group = NULL;
  
  					// Not a big problem calling it each time - it's a no-op when the thread already is attached
  					jint result = device->Android->activity->vm->AttachCurrentThread(&device->JNIEnvAttachedToVM, &attachArgs);
  					if(result == JNI_ERR)
  					{
  						os::Printer::log("AttachCurrentThread for the JNI environment failed.", ELL_WARNING);
  						device->JNIEnvAttachedToVM = 0;
  					}
  				}				
  				
  				if ( device->JNIEnvAttachedToVM )
  				{
  					jni::CKeyEventWrapper * keyEventWrapper = new jni::CKeyEventWrapper(device->JNIEnvAttachedToVM, keyAction, keyCode);
  					event.KeyInput.Char = keyEventWrapper->getUnicodeChar(keyMetaState);
  					delete keyEventWrapper;
  				}
  			}
  			else
  			{
  				// os::Printer::log("keyCode: ", core::stringc(keyCode).c_str(), ELL_DEBUG);
  				event.KeyInput.Char = 0;
  			}
  			
  			device->postEventFromUser(event);
  		}
  		break;
  		default:
  		break;
  	}
  
  	return status;
  }
  
  void CIrrDeviceAndroid::createDriver()
  {
  	switch(CreationParams.DriverType)
  	{
  	case video::EDT_OGLES1:
  #ifdef _IRR_COMPILE_WITH_OGLES1_
  		VideoDriver = video::createOGLES1Driver(CreationParams, FileSystem, ContextManager);
  #else
  		os::Printer::log("No OpenGL ES 1.0 support compiled in.", ELL_ERROR);
  #endif
  		break;
  	case video::EDT_OGLES2:
  #ifdef _IRR_COMPILE_WITH_OGLES2_
  		VideoDriver = video::createOGLES2Driver(CreationParams, FileSystem, ContextManager);
  #else
  		os::Printer::log("No OpenGL ES 2.0 support compiled in.", ELL_ERROR);
  #endif
  		break;
  	case video::EDT_NULL:
  		VideoDriver = video::createNullDriver(FileSystem, CreationParams.WindowSize);
  		break;
  	case video::EDT_SOFTWARE:
  	case video::EDT_BURNINGSVIDEO:
  	case video::EDT_OPENGL:
  	case video::EDT_DIRECT3D8:
  	case video::EDT_DIRECT3D9:
  		os::Printer::log("This driver is not available in Android. Try OpenGL ES 1.0 or ES 2.0.", ELL_ERROR);
  		break;
  	default:
  		os::Printer::log("Unable to create video driver of unknown type.", ELL_ERROR);
  		break;
  	}
  }
  
  video::SExposedVideoData& CIrrDeviceAndroid::getExposedVideoData()
  {
  	return ExposedVideoData;
  }
  
  void CIrrDeviceAndroid::createKeyMap()
  {
  	KeyMap.set_used(223);
  
      KeyMap[0] = KEY_UNKNOWN; // AKEYCODE_UNKNOWN
      KeyMap[1] = KEY_LBUTTON; // AKEYCODE_SOFT_LEFT
      KeyMap[2] = KEY_RBUTTON; // AKEYCODE_SOFT_RIGHT
      KeyMap[3] = KEY_HOME; // AKEYCODE_HOME
      KeyMap[4] = KEY_BACK; // AKEYCODE_BACK
      KeyMap[5] = KEY_UNKNOWN; // AKEYCODE_CALL
      KeyMap[6] = KEY_UNKNOWN; // AKEYCODE_ENDCALL
      KeyMap[7] = KEY_KEY_0; // AKEYCODE_0
      KeyMap[8] = KEY_KEY_1; // AKEYCODE_1
      KeyMap[9] = KEY_KEY_2; // AKEYCODE_2
      KeyMap[10] = KEY_KEY_3; // AKEYCODE_3
      KeyMap[11] = KEY_KEY_4; // AKEYCODE_4
      KeyMap[12] = KEY_KEY_5; // AKEYCODE_5
      KeyMap[13] = KEY_KEY_6; // AKEYCODE_6
      KeyMap[14] = KEY_KEY_7; // AKEYCODE_7
      KeyMap[15] = KEY_KEY_8; // AKEYCODE_8
      KeyMap[16] = KEY_KEY_9; // AKEYCODE_9
      KeyMap[17] = KEY_UNKNOWN; // AKEYCODE_STAR
      KeyMap[18] = KEY_UNKNOWN; // AKEYCODE_POUND
      KeyMap[19] = KEY_UP; // AKEYCODE_DPAD_UP
      KeyMap[20] = KEY_DOWN; // AKEYCODE_DPAD_DOWN
      KeyMap[21] = KEY_LEFT; // AKEYCODE_DPAD_LEFT
      KeyMap[22] = KEY_RIGHT; // AKEYCODE_DPAD_RIGHT
      KeyMap[23] = KEY_SELECT; // AKEYCODE_DPAD_CENTER
      KeyMap[24] = KEY_VOLUME_DOWN; // AKEYCODE_VOLUME_UP
      KeyMap[25] = KEY_VOLUME_UP; // AKEYCODE_VOLUME_DOWN
      KeyMap[26] = KEY_UNKNOWN; // AKEYCODE_POWER
      KeyMap[27] = KEY_UNKNOWN; // AKEYCODE_CAMERA
      KeyMap[28] = KEY_CLEAR; // AKEYCODE_CLEAR
      KeyMap[29] = KEY_KEY_A; // AKEYCODE_A
      KeyMap[30] = KEY_KEY_B; // AKEYCODE_B
      KeyMap[31] = KEY_KEY_C; // AKEYCODE_C
      KeyMap[32] = KEY_KEY_D; // AKEYCODE_D
      KeyMap[33] = KEY_KEY_E; // AKEYCODE_E
      KeyMap[34] = KEY_KEY_F; // AKEYCODE_F
      KeyMap[35] = KEY_KEY_G; // AKEYCODE_G
      KeyMap[36] = KEY_KEY_H; // AKEYCODE_H
      KeyMap[37] = KEY_KEY_I; // AKEYCODE_I
      KeyMap[38] = KEY_KEY_J; // AKEYCODE_J
      KeyMap[39] = KEY_KEY_K; // AKEYCODE_K
      KeyMap[40] = KEY_KEY_L; // AKEYCODE_L
      KeyMap[41] = KEY_KEY_M; // AKEYCODE_M
      KeyMap[42] = KEY_KEY_N; // AKEYCODE_N
      KeyMap[43] = KEY_KEY_O; // AKEYCODE_O
      KeyMap[44] = KEY_KEY_P; // AKEYCODE_P
      KeyMap[45] = KEY_KEY_Q; // AKEYCODE_Q
      KeyMap[46] = KEY_KEY_R; // AKEYCODE_R
      KeyMap[47] = KEY_KEY_S; // AKEYCODE_S
      KeyMap[48] = KEY_KEY_T; // AKEYCODE_T
      KeyMap[49] = KEY_KEY_U; // AKEYCODE_U
      KeyMap[50] = KEY_KEY_V; // AKEYCODE_V
      KeyMap[51] = KEY_KEY_W; // AKEYCODE_W
      KeyMap[52] = KEY_KEY_X; // AKEYCODE_X
      KeyMap[53] = KEY_KEY_Y; // AKEYCODE_Y
      KeyMap[54] = KEY_KEY_Z; // AKEYCODE_Z
      KeyMap[55] = KEY_COMMA; // AKEYCODE_COMMA
      KeyMap[56] = KEY_PERIOD; // AKEYCODE_PERIOD
      KeyMap[57] = KEY_MENU; // AKEYCODE_ALT_LEFT
      KeyMap[58] = KEY_MENU; // AKEYCODE_ALT_RIGHT
      KeyMap[59] = KEY_LSHIFT; // AKEYCODE_SHIFT_LEFT
      KeyMap[60] = KEY_RSHIFT; // AKEYCODE_SHIFT_RIGHT
      KeyMap[61] = KEY_TAB; // AKEYCODE_TAB
      KeyMap[62] = KEY_SPACE; // AKEYCODE_SPACE
      KeyMap[63] = KEY_UNKNOWN; // AKEYCODE_SYM
      KeyMap[64] = KEY_UNKNOWN; // AKEYCODE_EXPLORER
      KeyMap[65] = KEY_UNKNOWN; // AKEYCODE_ENVELOPE
      KeyMap[66] = KEY_RETURN; // AKEYCODE_ENTER
      KeyMap[67] = KEY_BACK; // AKEYCODE_DEL
      KeyMap[68] = KEY_OEM_3; // AKEYCODE_GRAVE
      KeyMap[69] = KEY_MINUS; // AKEYCODE_MINUS
      KeyMap[70] = KEY_UNKNOWN; // AKEYCODE_EQUALS
      KeyMap[71] = KEY_UNKNOWN; // AKEYCODE_LEFT_BRACKET
      KeyMap[72] = KEY_UNKNOWN; // AKEYCODE_RIGHT_BRACKET
      KeyMap[73] = KEY_UNKNOWN; // AKEYCODE_BACKSLASH
      KeyMap[74] = KEY_UNKNOWN; // AKEYCODE_SEMICOLON
      KeyMap[75] = KEY_UNKNOWN; // AKEYCODE_APOSTROPHE
      KeyMap[76] = KEY_UNKNOWN; // AKEYCODE_SLASH
      KeyMap[77] = KEY_UNKNOWN; // AKEYCODE_AT
      KeyMap[78] = KEY_UNKNOWN; // AKEYCODE_NUM
      KeyMap[79] = KEY_UNKNOWN; // AKEYCODE_HEADSETHOOK
      KeyMap[80] = KEY_UNKNOWN; // AKEYCODE_FOCUS (*Camera* focus)
      KeyMap[81] = KEY_PLUS; // AKEYCODE_PLUS
      KeyMap[82] = KEY_MENU; // AKEYCODE_MENU
      KeyMap[83] = KEY_UNKNOWN; // AKEYCODE_NOTIFICATION
      KeyMap[84] = KEY_UNKNOWN; // AKEYCODE_SEARCH
      KeyMap[85] = KEY_MEDIA_PLAY_PAUSE; // AKEYCODE_MEDIA_PLAY_PAUSE
      KeyMap[86] = KEY_MEDIA_STOP; // AKEYCODE_MEDIA_STOP
      KeyMap[87] = KEY_MEDIA_NEXT_TRACK; // AKEYCODE_MEDIA_NEXT
      KeyMap[88] = KEY_MEDIA_PREV_TRACK; // AKEYCODE_MEDIA_PREVIOUS
      KeyMap[89] = KEY_UNKNOWN; // AKEYCODE_MEDIA_REWIND
      KeyMap[90] = KEY_UNKNOWN; // AKEYCODE_MEDIA_FAST_FORWARD
      KeyMap[91] = KEY_VOLUME_MUTE; // AKEYCODE_MUTE
      KeyMap[92] = KEY_PRIOR; // AKEYCODE_PAGE_UP
      KeyMap[93] = KEY_NEXT; // AKEYCODE_PAGE_DOWN
      KeyMap[94] = KEY_UNKNOWN; // AKEYCODE_PICTSYMBOLS
      KeyMap[95] = KEY_UNKNOWN; // AKEYCODE_SWITCH_CHARSET
  
      // following look like controller inputs
      KeyMap[96] = KEY_UNKNOWN; // AKEYCODE_BUTTON_A
      KeyMap[97] = KEY_UNKNOWN; // AKEYCODE_BUTTON_B
      KeyMap[98] = KEY_UNKNOWN; // AKEYCODE_BUTTON_C
      KeyMap[99] = KEY_UNKNOWN; // AKEYCODE_BUTTON_X
      KeyMap[100] = KEY_UNKNOWN; // AKEYCODE_BUTTON_Y
      KeyMap[101] = KEY_UNKNOWN; // AKEYCODE_BUTTON_Z
      KeyMap[102] = KEY_UNKNOWN; // AKEYCODE_BUTTON_L1
      KeyMap[103] = KEY_UNKNOWN; // AKEYCODE_BUTTON_R1
      KeyMap[104] = KEY_UNKNOWN; // AKEYCODE_BUTTON_L2
      KeyMap[105] = KEY_UNKNOWN; // AKEYCODE_BUTTON_R2
      KeyMap[106] = KEY_UNKNOWN; // AKEYCODE_BUTTON_THUMBL
      KeyMap[107] = KEY_UNKNOWN; // AKEYCODE_BUTTON_THUMBR
      KeyMap[108] = KEY_UNKNOWN; // AKEYCODE_BUTTON_START
      KeyMap[109] = KEY_UNKNOWN; // AKEYCODE_BUTTON_SELECT
      KeyMap[110] = KEY_UNKNOWN; // AKEYCODE_BUTTON_MODE
  
      KeyMap[111] = KEY_ESCAPE; // AKEYCODE_ESCAPE
      KeyMap[112] = KEY_DELETE; // AKEYCODE_FORWARD_DEL
      KeyMap[113] = KEY_CONTROL; // AKEYCODE_CTRL_LEFT
      KeyMap[114] = KEY_CONTROL; // AKEYCODE_CTRL_RIGHT
      KeyMap[115] = KEY_CAPITAL; // AKEYCODE_CAPS_LOCK
      KeyMap[116] = KEY_SCROLL; // AKEYCODE_SCROLL_LOCK
      KeyMap[117] = KEY_UNKNOWN; // AKEYCODE_META_LEFT
      KeyMap[118] = KEY_UNKNOWN; // AKEYCODE_META_RIGHT
      KeyMap[119] = KEY_UNKNOWN; // AKEYCODE_FUNCTION
      KeyMap[120] = KEY_SNAPSHOT; // AKEYCODE_SYSRQ
      KeyMap[121] = KEY_PAUSE; // AKEYCODE_BREAK
      KeyMap[122] = KEY_HOME; // AKEYCODE_MOVE_HOME
      KeyMap[123] = KEY_END; // AKEYCODE_MOVE_END
      KeyMap[124] = KEY_INSERT; // AKEYCODE_INSERT
      KeyMap[125] = KEY_UNKNOWN; // AKEYCODE_FORWARD
      KeyMap[126] = KEY_PLAY; // AKEYCODE_MEDIA_PLAY
      KeyMap[127] = KEY_MEDIA_PLAY_PAUSE; // AKEYCODE_MEDIA_PAUSE
      KeyMap[128] = KEY_UNKNOWN; // AKEYCODE_MEDIA_CLOSE
      KeyMap[129] = KEY_UNKNOWN; // AKEYCODE_MEDIA_EJECT
      KeyMap[130] = KEY_UNKNOWN; // AKEYCODE_MEDIA_RECORD
      KeyMap[131] = KEY_F1; // AKEYCODE_F1
      KeyMap[132] = KEY_F2; // AKEYCODE_F2
      KeyMap[133] = KEY_F3; // AKEYCODE_F3
      KeyMap[134] = KEY_F4; // AKEYCODE_F4
      KeyMap[135] = KEY_F5; // AKEYCODE_F5
      KeyMap[136] = KEY_F6; // AKEYCODE_F6
      KeyMap[137] = KEY_F7; // AKEYCODE_F7
      KeyMap[138] = KEY_F8; // AKEYCODE_F8
      KeyMap[139] = KEY_F9; // AKEYCODE_F9
      KeyMap[140] = KEY_F10; // AKEYCODE_F10
      KeyMap[141] = KEY_F11; // AKEYCODE_F11
      KeyMap[142] = KEY_F12; // AKEYCODE_F12
      KeyMap[143] = KEY_NUMLOCK; // AKEYCODE_NUM_LOCK
      KeyMap[144] = KEY_NUMPAD0; // AKEYCODE_NUMPAD_0
      KeyMap[145] = KEY_NUMPAD1; // AKEYCODE_NUMPAD_1
      KeyMap[146] = KEY_NUMPAD2; // AKEYCODE_NUMPAD_2
      KeyMap[147] = KEY_NUMPAD3; // AKEYCODE_NUMPAD_3
      KeyMap[148] = KEY_NUMPAD4; // AKEYCODE_NUMPAD_4
      KeyMap[149] = KEY_NUMPAD5; // AKEYCODE_NUMPAD_5
      KeyMap[150] = KEY_NUMPAD6; // AKEYCODE_NUMPAD_6
      KeyMap[151] = KEY_NUMPAD7; // AKEYCODE_NUMPAD_7
      KeyMap[152] = KEY_NUMPAD8; // AKEYCODE_NUMPAD_8
      KeyMap[153] = KEY_NUMPAD9; // AKEYCODE_NUMPAD_9
      KeyMap[154] = KEY_DIVIDE; // AKEYCODE_NUMPAD_DIVIDE
      KeyMap[155] = KEY_MULTIPLY; // AKEYCODE_NUMPAD_MULTIPLY
      KeyMap[156] = KEY_SUBTRACT; // AKEYCODE_NUMPAD_SUBTRACT
      KeyMap[157] = KEY_ADD; // AKEYCODE_NUMPAD_ADD
      KeyMap[158] = KEY_UNKNOWN; // AKEYCODE_NUMPAD_DOT
      KeyMap[159] = KEY_COMMA; // AKEYCODE_NUMPAD_COMMA
      KeyMap[160] = KEY_RETURN; // AKEYCODE_NUMPAD_ENTER
      KeyMap[161] = KEY_UNKNOWN; // AKEYCODE_NUMPAD_EQUALS
      KeyMap[162] = KEY_UNKNOWN; // AKEYCODE_NUMPAD_LEFT_PAREN
      KeyMap[163] = KEY_UNKNOWN; // AKEYCODE_NUMPAD_RIGHT_PAREN
      KeyMap[164] = KEY_VOLUME_MUTE; // AKEYCODE_VOLUME_MUTE
      KeyMap[165] = KEY_UNKNOWN; // AKEYCODE_INFO
      KeyMap[166] = KEY_UNKNOWN; // AKEYCODE_CHANNEL_UP
      KeyMap[167] = KEY_UNKNOWN; // AKEYCODE_CHANNEL_DOWN
      KeyMap[168] = KEY_ZOOM; // AKEYCODE_ZOOM_IN
      KeyMap[169] = KEY_UNKNOWN; // AKEYCODE_ZOOM_OUT
      KeyMap[170] = KEY_UNKNOWN; // AKEYCODE_TV
      KeyMap[171] = KEY_UNKNOWN; // AKEYCODE_WINDOW
      KeyMap[172] = KEY_UNKNOWN; // AKEYCODE_GUIDE
      KeyMap[173] = KEY_UNKNOWN; // AKEYCODE_DVR
      KeyMap[174] = KEY_UNKNOWN; // AKEYCODE_BOOKMARK
      KeyMap[175] = KEY_UNKNOWN; // AKEYCODE_CAPTIONS
      KeyMap[176] = KEY_UNKNOWN; // AKEYCODE_SETTINGS
      KeyMap[177] = KEY_UNKNOWN; // AKEYCODE_TV_POWER
      KeyMap[178] = KEY_UNKNOWN; // AKEYCODE_TV_INPUT
      KeyMap[179] = KEY_UNKNOWN; // AKEYCODE_STB_POWER
      KeyMap[180] = KEY_UNKNOWN; // AKEYCODE_STB_INPUT
      KeyMap[181] = KEY_UNKNOWN; // AKEYCODE_AVR_POWER
      KeyMap[182] = KEY_UNKNOWN; // AKEYCODE_AVR_INPUT
      KeyMap[183] = KEY_UNKNOWN; // AKEYCODE_PROG_RED
      KeyMap[184] = KEY_UNKNOWN; // AKEYCODE_PROG_GREEN
      KeyMap[185] = KEY_UNKNOWN; // AKEYCODE_PROG_YELLOW
      KeyMap[186] = KEY_UNKNOWN; // AKEYCODE_PROG_BLUE
      KeyMap[187] = KEY_UNKNOWN; // AKEYCODE_APP_SWITCH
      KeyMap[188] = KEY_UNKNOWN; // AKEYCODE_BUTTON_1
      KeyMap[189] = KEY_UNKNOWN; // AKEYCODE_BUTTON_2
      KeyMap[190] = KEY_UNKNOWN; // AKEYCODE_BUTTON_3
      KeyMap[191] = KEY_UNKNOWN; // AKEYCODE_BUTTON_4
      KeyMap[192] = KEY_UNKNOWN; // AKEYCODE_BUTTON_5
      KeyMap[193] = KEY_UNKNOWN; // AKEYCODE_BUTTON_6
      KeyMap[194] = KEY_UNKNOWN; // AKEYCODE_BUTTON_7
      KeyMap[195] = KEY_UNKNOWN; // AKEYCODE_BUTTON_8
      KeyMap[196] = KEY_UNKNOWN; // AKEYCODE_BUTTON_9
      KeyMap[197] = KEY_UNKNOWN; // AKEYCODE_BUTTON_10
      KeyMap[198] = KEY_UNKNOWN; // AKEYCODE_BUTTON_11
      KeyMap[199] = KEY_UNKNOWN; // AKEYCODE_BUTTON_12
      KeyMap[200] = KEY_UNKNOWN; // AKEYCODE_BUTTON_13
      KeyMap[201] = KEY_UNKNOWN; // AKEYCODE_BUTTON_14
      KeyMap[202] = KEY_UNKNOWN; // AKEYCODE_BUTTON_15
      KeyMap[203] = KEY_UNKNOWN; // AKEYCODE_BUTTON_16
      KeyMap[204] = KEY_UNKNOWN; // AKEYCODE_LANGUAGE_SWITCH
      KeyMap[205] = KEY_UNKNOWN; // AKEYCODE_MANNER_MODE
      KeyMap[206] = KEY_UNKNOWN; // AKEYCODE_3D_MODE
      KeyMap[207] = KEY_UNKNOWN; // AKEYCODE_CONTACTS
      KeyMap[208] = KEY_UNKNOWN; // AKEYCODE_CALENDAR
      KeyMap[209] = KEY_UNKNOWN; // AKEYCODE_MUSIC
      KeyMap[210] = KEY_UNKNOWN; // AKEYCODE_CALCULATOR
      KeyMap[211] = KEY_UNKNOWN; // AKEYCODE_ZENKAKU_HANKAKU
      KeyMap[212] = KEY_UNKNOWN; // AKEYCODE_EISU
      KeyMap[213] = KEY_UNKNOWN; // AKEYCODE_MUHENKAN
      KeyMap[214] = KEY_UNKNOWN; // AKEYCODE_HENKAN
      KeyMap[215] = KEY_UNKNOWN; // AKEYCODE_KATAKANA_HIRAGANA
      KeyMap[216] = KEY_UNKNOWN; // AKEYCODE_YEN
      KeyMap[217] = KEY_UNKNOWN; // AKEYCODE_RO
      KeyMap[218] = KEY_UNKNOWN; // AKEYCODE_KANA
      KeyMap[219] = KEY_UNKNOWN; // AKEYCODE_ASSIST
      KeyMap[220] = KEY_UNKNOWN; // AKEYCODE_BRIGHTNESS_DOWN
      KeyMap[221] = KEY_UNKNOWN; // AKEYCODE_BRIGHTNESS_UP ,
      KeyMap[222] = KEY_UNKNOWN; // AKEYCODE_MEDIA_AUDIO_TRACK
  }
  
  } // end namespace irr
  
  #endif
  
  