UnityView+Keyboard.mm 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. #import "UnityView.h"
  2. #include "UI/Keyboard.h"
  3. #include <sys/time.h>
  4. #include <map>
  5. #include <vector>
  6. static NSArray* keyboardCommands = nil;
  7. extern "C" int UnityGetAppleTVRemoteAllowExitToMenu();
  8. extern "C" void UnitySetAppleTVRemoteAllowExitToMenu(int val);
  9. @implementation UnityView (Keyboard)
  10. // Keyboard shortcuts don't provide events for key up
  11. // Keyboard shortcut callbacks are called with 0.4 (first time) and 0.1 (following times) seconds interval while pressing the key
  12. // Below we implement key expiration mechanism where key up event is generated if shortcut callback
  13. // is not called for specific key for more than <kKeyTimeoutInSeconds>
  14. typedef std::map<int, double> KeyMap;
  15. static const double kKeyTimeoutInSeconds = 0.5;
  16. static KeyMap& GetKeyMap()
  17. {
  18. static KeyMap s_Map;
  19. return s_Map;
  20. }
  21. static double GetTimeInSeconds()
  22. {
  23. timeval now;
  24. gettimeofday(&now, NULL);
  25. return now.tv_sec + now.tv_usec / 1000000.0;
  26. }
  27. - (void)createKeyboard
  28. {
  29. // only English keyboard layout is supported
  30. NSString* baseLayout = @"1234567890-=qwertyuiop[]asdfghjkl;'\\`zxcvbnm,./!@#$%^&*()_+{}:\"|<>?~ \t\r\b\\";
  31. NSString* numpadLayout = @"1234567890-=*+/.\r";
  32. NSString* upperCaseLetters = @"QWERTYUIOPASDFGHJKLZXCVBNM";
  33. size_t sizeOfKeyboardCommands = baseLayout.length + numpadLayout.length + upperCaseLetters.length + 11;
  34. NSMutableArray* commands = [NSMutableArray arrayWithCapacity: sizeOfKeyboardCommands];
  35. for (NSInteger i = 0; i < baseLayout.length; ++i)
  36. {
  37. NSString* input = [baseLayout substringWithRange: NSMakeRange(i, 1)];
  38. [commands addObject: [UIKeyCommand keyCommandWithInput: input modifierFlags: kNilOptions action: @selector(handleCommand:)]];
  39. }
  40. for (NSInteger i = 0; i < numpadLayout.length; ++i)
  41. {
  42. NSString* input = [numpadLayout substringWithRange: NSMakeRange(i, 1)];
  43. [commands addObject: [UIKeyCommand keyCommandWithInput: input modifierFlags: UIKeyModifierNumericPad action: @selector(handleCommand:)]];
  44. }
  45. for (NSInteger i = 0; i < upperCaseLetters.length; ++i)
  46. {
  47. NSString* input = [upperCaseLetters substringWithRange: NSMakeRange(i, 1)];
  48. [commands addObject: [UIKeyCommand keyCommandWithInput: input modifierFlags: UIKeyModifierShift action: @selector(handleCommand:)]];
  49. }
  50. // pageUp, pageDown
  51. [commands addObject: [UIKeyCommand keyCommandWithInput: @"UIKeyInputPageUp" modifierFlags: kNilOptions action: @selector(handleCommand:)]];
  52. [commands addObject: [UIKeyCommand keyCommandWithInput: @"UIKeyInputPageDown" modifierFlags: kNilOptions action: @selector(handleCommand:)]];
  53. // up, down, left, right, esc
  54. [commands addObject: [UIKeyCommand keyCommandWithInput: UIKeyInputUpArrow modifierFlags: kNilOptions action: @selector(handleCommand:)]];
  55. [commands addObject: [UIKeyCommand keyCommandWithInput: UIKeyInputDownArrow modifierFlags: kNilOptions action: @selector(handleCommand:)]];
  56. [commands addObject: [UIKeyCommand keyCommandWithInput: UIKeyInputLeftArrow modifierFlags: kNilOptions action: @selector(handleCommand:)]];
  57. [commands addObject: [UIKeyCommand keyCommandWithInput: UIKeyInputRightArrow modifierFlags: kNilOptions action: @selector(handleCommand:)]];
  58. [commands addObject: [UIKeyCommand keyCommandWithInput: UIKeyInputEscape modifierFlags: kNilOptions action: @selector(handleCommand:)]];
  59. // caps Lock, shift, control, option, command
  60. [commands addObject: [UIKeyCommand keyCommandWithInput: @"" modifierFlags: UIKeyModifierAlphaShift action: @selector(handleCommand:)]];
  61. [commands addObject: [UIKeyCommand keyCommandWithInput: @"" modifierFlags: UIKeyModifierShift action: @selector(handleCommand:)]];
  62. [commands addObject: [UIKeyCommand keyCommandWithInput: @"" modifierFlags: UIKeyModifierControl action: @selector(handleCommand:)]];
  63. [commands addObject: [UIKeyCommand keyCommandWithInput: @"" modifierFlags: UIKeyModifierAlternate action: @selector(handleCommand:)]];
  64. [commands addObject: [UIKeyCommand keyCommandWithInput: @"" modifierFlags: UIKeyModifierCommand action: @selector(handleCommand:)]];
  65. keyboardCommands = commands.copy;
  66. }
  67. - (NSArray*)keyCommands
  68. {
  69. //keyCommands take controll of buttons over UITextView, that's why need to return nil if text input field is active
  70. if ([[KeyboardDelegate Instance] status] == Visible)
  71. {
  72. return nil;
  73. }
  74. if (keyboardCommands == nil)
  75. {
  76. [self createKeyboard];
  77. }
  78. return keyboardCommands;
  79. }
  80. - (bool)isValidCodeForButton:(int)code
  81. {
  82. return (code > 0 && code < 128);
  83. }
  84. - (void)handleCommand:(UIKeyCommand *)command
  85. {
  86. NSString* input = command.input;
  87. UIKeyModifierFlags modifierFlags = command.modifierFlags;
  88. char inputChar = ([input length] > 0) ? [input characterAtIndex: 0] : 0;
  89. int code = (int)inputChar; // ASCII code
  90. UnitySendKeyboardCommand(command);
  91. if (![self isValidCodeForButton: code])
  92. {
  93. code = 0;
  94. }
  95. if ((modifierFlags & UIKeyModifierAlphaShift) != 0)
  96. code = UnityStringToKey("caps lock");
  97. if ((modifierFlags & UIKeyModifierShift) != 0)
  98. code = UnityStringToKey("left shift");
  99. if ((modifierFlags & UIKeyModifierControl) != 0)
  100. code = UnityStringToKey("left ctrl");
  101. if ((modifierFlags & UIKeyModifierAlternate) != 0)
  102. code = UnityStringToKey("left alt");
  103. if ((modifierFlags & UIKeyModifierCommand) != 0)
  104. code = UnityStringToKey("left cmd");
  105. if ((modifierFlags & UIKeyModifierNumericPad) != 0)
  106. {
  107. switch (inputChar)
  108. {
  109. case '0':
  110. code = UnityStringToKey("[0]");
  111. break;
  112. case '1':
  113. code = UnityStringToKey("[1]");
  114. break;
  115. case '2':
  116. code = UnityStringToKey("[2]");
  117. break;
  118. case '3':
  119. code = UnityStringToKey("[3]");
  120. break;
  121. case '4':
  122. code = UnityStringToKey("[4]");
  123. break;
  124. case '5':
  125. code = UnityStringToKey("[5]");
  126. break;
  127. case '6':
  128. code = UnityStringToKey("[6]");
  129. break;
  130. case '7':
  131. code = UnityStringToKey("[7]");
  132. break;
  133. case '8':
  134. code = UnityStringToKey("[8]");
  135. break;
  136. case '9':
  137. code = UnityStringToKey("[9]");
  138. break;
  139. case '-':
  140. code = UnityStringToKey("[-]");
  141. break;
  142. case '=':
  143. code = UnityStringToKey("equals");
  144. break;
  145. case '*':
  146. code = UnityStringToKey("[*]");
  147. break;
  148. case '+':
  149. code = UnityStringToKey("[+]");
  150. break;
  151. case '/':
  152. code = UnityStringToKey("[/]");
  153. break;
  154. case '.':
  155. code = UnityStringToKey("[.]");
  156. break;
  157. case '\r':
  158. code = UnityStringToKey("enter");
  159. break;
  160. default:
  161. break;
  162. }
  163. }
  164. if (input == UIKeyInputUpArrow)
  165. code = UnityStringToKey("up");
  166. else if (input == UIKeyInputDownArrow)
  167. code = UnityStringToKey("down");
  168. else if (input == UIKeyInputRightArrow)
  169. code = UnityStringToKey("right");
  170. else if (input == UIKeyInputLeftArrow)
  171. code = UnityStringToKey("left");
  172. else if (input == UIKeyInputEscape)
  173. code = UnityStringToKey("escape");
  174. else if ([input isEqualToString: @"UIKeyInputPageUp"])
  175. code = UnityStringToKey("page up");
  176. else if ([input isEqualToString: @"UIKeyInputPageDown"])
  177. code = UnityStringToKey("page down");
  178. KeyMap::iterator item = GetKeyMap().find(code);
  179. if (item == GetKeyMap().end())
  180. {
  181. // New key is down, register it and its time
  182. UnitySetKeyboardKeyState(code, true);
  183. GetKeyMap()[code] = GetTimeInSeconds();
  184. }
  185. else
  186. {
  187. // Still holding the key, update its time
  188. item->second = GetTimeInSeconds();
  189. }
  190. }
  191. - (void)processKeyboard
  192. {
  193. KeyMap& map = GetKeyMap();
  194. if (map.size() == 0)
  195. return;
  196. std::vector<int> keysToUnpress;
  197. double nowTime = GetTimeInSeconds();
  198. for (KeyMap::iterator item = map.begin();
  199. item != map.end();
  200. item++)
  201. {
  202. // Key has expired, register it for key up event
  203. if (nowTime - item->second > kKeyTimeoutInSeconds)
  204. keysToUnpress.push_back(item->first);
  205. }
  206. for (std::vector<int>::iterator item = keysToUnpress.begin();
  207. item != keysToUnpress.end();
  208. item++)
  209. {
  210. map.erase(*item);
  211. UnitySetKeyboardKeyState(*item, false);
  212. }
  213. }
  214. @end