UnityView+Keyboard.mm 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  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. // up, down, left, right, esc
  51. [commands addObject: [UIKeyCommand keyCommandWithInput: UIKeyInputUpArrow modifierFlags: kNilOptions action: @selector(handleCommand:)]];
  52. [commands addObject: [UIKeyCommand keyCommandWithInput: UIKeyInputDownArrow modifierFlags: kNilOptions action: @selector(handleCommand:)]];
  53. [commands addObject: [UIKeyCommand keyCommandWithInput: UIKeyInputLeftArrow modifierFlags: kNilOptions action: @selector(handleCommand:)]];
  54. [commands addObject: [UIKeyCommand keyCommandWithInput: UIKeyInputRightArrow modifierFlags: kNilOptions action: @selector(handleCommand:)]];
  55. [commands addObject: [UIKeyCommand keyCommandWithInput: UIKeyInputEscape modifierFlags: kNilOptions action: @selector(handleCommand:)]];
  56. // caps Lock, shift, control, option, command
  57. [commands addObject: [UIKeyCommand keyCommandWithInput: @"" modifierFlags: UIKeyModifierAlphaShift action: @selector(handleCommand:)]];
  58. [commands addObject: [UIKeyCommand keyCommandWithInput: @"" modifierFlags: UIKeyModifierShift action: @selector(handleCommand:)]];
  59. [commands addObject: [UIKeyCommand keyCommandWithInput: @"" modifierFlags: UIKeyModifierControl action: @selector(handleCommand:)]];
  60. [commands addObject: [UIKeyCommand keyCommandWithInput: @"" modifierFlags: UIKeyModifierAlternate action: @selector(handleCommand:)]];
  61. [commands addObject: [UIKeyCommand keyCommandWithInput: @"" modifierFlags: UIKeyModifierCommand action: @selector(handleCommand:)]];
  62. keyboardCommands = commands.copy;
  63. }
  64. - (NSArray*)keyCommands
  65. {
  66. //keyCommands take controll of buttons over UITextView, that's why need to return nil if text input field is active
  67. if ([[KeyboardDelegate Instance] status] == Visible)
  68. {
  69. return nil;
  70. }
  71. if (keyboardCommands == nil)
  72. {
  73. [self createKeyboard];
  74. }
  75. return keyboardCommands;
  76. }
  77. - (bool)isValidCodeForButton:(int)code
  78. {
  79. return (code > 0 && code < 128);
  80. }
  81. - (void)handleCommand:(UIKeyCommand *)command
  82. {
  83. NSString* input = command.input;
  84. UIKeyModifierFlags modifierFlags = command.modifierFlags;
  85. char inputChar = ([input length] > 0) ? [input characterAtIndex: 0] : 0;
  86. int code = (int)inputChar; // ASCII code
  87. UnitySendKeyboardCommand(command);
  88. if (![self isValidCodeForButton: code])
  89. {
  90. code = 0;
  91. }
  92. if ((modifierFlags & UIKeyModifierAlphaShift) != 0)
  93. code = UnityStringToKey("caps lock");
  94. if ((modifierFlags & UIKeyModifierShift) != 0)
  95. code = UnityStringToKey("left shift");
  96. if ((modifierFlags & UIKeyModifierControl) != 0)
  97. code = UnityStringToKey("left ctrl");
  98. if ((modifierFlags & UIKeyModifierAlternate) != 0)
  99. code = UnityStringToKey("left alt");
  100. if ((modifierFlags & UIKeyModifierCommand) != 0)
  101. code = UnityStringToKey("left cmd");
  102. if ((modifierFlags & UIKeyModifierNumericPad) != 0)
  103. {
  104. switch (inputChar)
  105. {
  106. case '0':
  107. code = UnityStringToKey("[0]");
  108. break;
  109. case '1':
  110. code = UnityStringToKey("[1]");
  111. break;
  112. case '2':
  113. code = UnityStringToKey("[2]");
  114. break;
  115. case '3':
  116. code = UnityStringToKey("[3]");
  117. break;
  118. case '4':
  119. code = UnityStringToKey("[4]");
  120. break;
  121. case '5':
  122. code = UnityStringToKey("[5]");
  123. break;
  124. case '6':
  125. code = UnityStringToKey("[6]");
  126. break;
  127. case '7':
  128. code = UnityStringToKey("[7]");
  129. break;
  130. case '8':
  131. code = UnityStringToKey("[8]");
  132. break;
  133. case '9':
  134. code = UnityStringToKey("[9]");
  135. break;
  136. case '-':
  137. code = UnityStringToKey("[-]");
  138. break;
  139. case '=':
  140. code = UnityStringToKey("equals");
  141. break;
  142. case '*':
  143. code = UnityStringToKey("[*]");
  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 '\r':
  155. code = UnityStringToKey("enter");
  156. break;
  157. default:
  158. break;
  159. }
  160. }
  161. if (input == UIKeyInputUpArrow)
  162. code = UnityStringToKey("up");
  163. else if (input == UIKeyInputDownArrow)
  164. code = UnityStringToKey("down");
  165. else if (input == UIKeyInputRightArrow)
  166. code = UnityStringToKey("right");
  167. else if (input == UIKeyInputLeftArrow)
  168. code = UnityStringToKey("left");
  169. else if (input == UIKeyInputEscape)
  170. code = UnityStringToKey("escape");
  171. KeyMap::iterator item = GetKeyMap().find(code);
  172. if (item == GetKeyMap().end())
  173. {
  174. // New key is down, register it and its time
  175. UnitySetKeyboardKeyState(code, true);
  176. GetKeyMap()[code] = GetTimeInSeconds();
  177. }
  178. else
  179. {
  180. // Still holding the key, update its time
  181. item->second = GetTimeInSeconds();
  182. }
  183. }
  184. - (void)processKeyboard
  185. {
  186. KeyMap& map = GetKeyMap();
  187. if (map.size() == 0)
  188. return;
  189. std::vector<int> keysToUnpress;
  190. double nowTime = GetTimeInSeconds();
  191. for (KeyMap::iterator item = map.begin();
  192. item != map.end();
  193. item++)
  194. {
  195. // Key has expired, register it for key up event
  196. if (nowTime - item->second > kKeyTimeoutInSeconds)
  197. keysToUnpress.push_back(item->first);
  198. }
  199. for (std::vector<int>::iterator item = keysToUnpress.begin();
  200. item != keysToUnpress.end();
  201. item++)
  202. {
  203. map.erase(*item);
  204. UnitySetKeyboardKeyState(*item, false);
  205. }
  206. }
  207. @end