FreeRDP
Loading...
Searching...
No Matches
RDPKeyboard.m
1/*
2 RDP Keyboard helper
3
4 Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
5
6 This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
7 If a copy of the MPL was not distributed with this file, You can obtain one at
8 http://mozilla.org/MPL/2.0/.
9 */
10
11#import "RDPKeyboard.h"
12#include <freerdp/locale/keyboard.h>
13
14@interface RDPKeyboard (Private)
15- (void)sendUnicodeKey:(int)character up:(BOOL)up;
16- (void)handleSpecialKey:(int)character;
17- (void)handleAlphaNumChar:(int)character;
18- (void)notifyDelegateModifiersChanged;
19@end
20
21@implementation RDPKeyboard
22
23@synthesize delegate = _delegate, ctrlPressed = _ctrl_pressed, altPressed = _alt_pressed,
24 shiftPressed = _shift_pressed, winPressed = _win_pressed;
25
26- (id)init
27{
28 if ((self = [super init]) != nil)
29 {
30 [self initWithSession:nil delegate:nil];
31
32 memset(_virtual_key_map, 0, sizeof(_virtual_key_map));
33 memset(_virtual_key_shift_map, 0, sizeof(_virtual_key_shift_map));
34
35 // init vkey map - used for alpha-num characters
36 _virtual_key_map['0'] = VK_KEY_0;
37 _virtual_key_map['1'] = VK_KEY_1;
38 _virtual_key_map['2'] = VK_KEY_2;
39 _virtual_key_map['3'] = VK_KEY_3;
40 _virtual_key_map['4'] = VK_KEY_4;
41 _virtual_key_map['5'] = VK_KEY_5;
42 _virtual_key_map['6'] = VK_KEY_6;
43 _virtual_key_map['7'] = VK_KEY_7;
44 _virtual_key_map['8'] = VK_KEY_8;
45 _virtual_key_map['9'] = VK_KEY_9;
46
47 _virtual_key_map['a'] = VK_KEY_A;
48 _virtual_key_map['b'] = VK_KEY_B;
49 _virtual_key_map['c'] = VK_KEY_C;
50 _virtual_key_map['d'] = VK_KEY_D;
51 _virtual_key_map['e'] = VK_KEY_E;
52 _virtual_key_map['f'] = VK_KEY_F;
53 _virtual_key_map['g'] = VK_KEY_G;
54 _virtual_key_map['h'] = VK_KEY_H;
55 _virtual_key_map['i'] = VK_KEY_I;
56 _virtual_key_map['j'] = VK_KEY_J;
57 _virtual_key_map['k'] = VK_KEY_K;
58 _virtual_key_map['l'] = VK_KEY_L;
59 _virtual_key_map['m'] = VK_KEY_M;
60 _virtual_key_map['n'] = VK_KEY_N;
61 _virtual_key_map['o'] = VK_KEY_O;
62 _virtual_key_map['p'] = VK_KEY_P;
63 _virtual_key_map['q'] = VK_KEY_Q;
64 _virtual_key_map['r'] = VK_KEY_R;
65 _virtual_key_map['s'] = VK_KEY_S;
66 _virtual_key_map['t'] = VK_KEY_T;
67 _virtual_key_map['u'] = VK_KEY_U;
68 _virtual_key_map['v'] = VK_KEY_V;
69 _virtual_key_map['w'] = VK_KEY_W;
70 _virtual_key_map['x'] = VK_KEY_X;
71 _virtual_key_map['y'] = VK_KEY_Y;
72 _virtual_key_map['z'] = VK_KEY_Z;
73
74 // some server does not accept unicode key event
75 // so, send physical key combinations
76 _virtual_key_map[' '] = VK_SPACE;
77 _virtual_key_map['`'] = _virtual_key_map['~'] = VK_OEM_3;
78 _virtual_key_map['-'] = _virtual_key_map['_'] = VK_OEM_MINUS;
79 _virtual_key_map['='] = _virtual_key_map['+'] = VK_OEM_PLUS;
80 _virtual_key_map['['] = _virtual_key_map['{'] = VK_OEM_4;
81 _virtual_key_map[']'] = _virtual_key_map['}'] = VK_OEM_6;
82 _virtual_key_map['\\'] = _virtual_key_map['|'] = VK_OEM_5;
83 _virtual_key_map[';'] = _virtual_key_map[':'] = VK_OEM_1;
84 _virtual_key_map['\''] = _virtual_key_map['\"'] = VK_OEM_7;
85 _virtual_key_map[','] = _virtual_key_map['<'] = VK_OEM_COMMA;
86 _virtual_key_map['.'] = _virtual_key_map['>'] = VK_OEM_PERIOD;
87 _virtual_key_map['/'] = _virtual_key_map['?'] = VK_OEM_2;
88
89 const char *shifted = "~_+{}|:\"<>?!@#$%^&*()";
90 for (const char *p = shifted; *p != '\0'; p++)
91 _virtual_key_shift_map[(unsigned char)*p] = YES;
92
93 _virtual_key_map['!'] = VK_KEY_1;
94 _virtual_key_map['@'] = VK_KEY_2;
95 _virtual_key_map['#'] = VK_KEY_3;
96 _virtual_key_map['] = VK_KEY_4;
97 _virtual_key_map['%'] = VK_KEY_5;
98 _virtual_key_map['^'] = VK_KEY_6;
99 _virtual_key_map['&'] = VK_KEY_7;
100 _virtual_key_map['*'] = VK_KEY_8;
101 _virtual_key_map['('] = VK_KEY_9;
102 _virtual_key_map[')'] = VK_KEY_0;
103 }
104 return self;
105}
106
107- (void)dealloc
108{
109 [super dealloc];
110}
111
112#pragma mark -
113#pragma mark class methods
114
115// return a keyboard instance
116+ (RDPKeyboard *)getSharedRDPKeyboard
117{
118 static RDPKeyboard *_shared_keyboard = nil;
119
120 if (_shared_keyboard == nil)
121 {
122 @synchronized(self)
123 {
124 if (_shared_keyboard == nil)
125 _shared_keyboard = [[RDPKeyboard alloc] init];
126 }
127 }
128
129 return _shared_keyboard;
130}
131
132// reset the keyboard instance and assign the given rdp instance
133- (void)initWithSession:(RDPSession *)session delegate:(NSObject<RDPKeyboardDelegate> *)delegate
134{
135 _alt_pressed = NO;
136 _ctrl_pressed = NO;
137 _shift_pressed = NO;
138 _win_pressed = NO;
139
140 _session = session;
141 _delegate = delegate;
142}
143
144- (void)reset
145{
146 // reset pressed ctrl, alt, shift or win key
147 if (_shift_pressed)
148 [self toggleShiftKey];
149 if (_alt_pressed)
150 [self toggleAltKey];
151 if (_ctrl_pressed)
152 [self toggleCtrlKey];
153 if (_win_pressed)
154 [self toggleWinKey];
155}
156
157// handles button pressed input event from the iOS keyboard
158// performs all conversions etc.
159- (void)sendUnicode:(int)character
160{
161 if ((character >= 0) && (character < 256) && isalnum((unsigned char)character))
162 [self handleAlphaNumChar:character];
163 else
164 [self handleSpecialKey:character];
165
166 [self reset];
167}
168
169// send a backspace key press
170- (void)sendVirtualKeyCode:(int)keyCode
171{
172 [self sendVirtualKey:keyCode up:NO];
173 [self sendVirtualKey:keyCode up:YES];
174}
175
176// sends the vk code to the session
177- (void)sendVirtualKey:(int)vKey up:(BOOL)up
178{
179 DWORD scancode = GetVirtualScanCodeFromVirtualKeyCode(vKey, 4);
180 int flags = (up ? KBD_FLAGS_RELEASE : KBD_FLAGS_DOWN);
181 flags |= ((scancode & KBDEXT) ? KBD_FLAGS_EXTENDED : 0);
182 [_session
183 sendInputEvent:[NSDictionary
184 dictionaryWithObjectsAndKeys:@"keyboard", @"type", @"scancode",
185 @"subtype",
186 [NSNumber numberWithUnsignedShort:flags],
187 @"flags",
188 [NSNumber
189 numberWithUnsignedShort:(scancode &
190 0xFF)],
191 @"scancode", nil]];
192}
193
194#pragma mark modifier key handling
195// toggle ctrl key, returns true if pressed, otherwise false
196- (void)toggleCtrlKey
197{
198 [self sendVirtualKey:VK_LCONTROL up:_ctrl_pressed];
199 _ctrl_pressed = !_ctrl_pressed;
200 [self notifyDelegateModifiersChanged];
201}
202
203// toggle alt key, returns true if pressed, otherwise false
204- (void)toggleAltKey
205{
206 [self sendVirtualKey:VK_LMENU up:_alt_pressed];
207 _alt_pressed = !_alt_pressed;
208 [self notifyDelegateModifiersChanged];
209}
210
211// toggle shift key, returns true if pressed, otherwise false
212- (void)toggleShiftKey
213{
214 [self sendVirtualKey:VK_LSHIFT up:_shift_pressed];
215 _shift_pressed = !_shift_pressed;
216 [self notifyDelegateModifiersChanged];
217}
218
219// toggle windows key, returns true if pressed, otherwise false
220- (void)toggleWinKey
221{
222 [self sendVirtualKey:(VK_LWIN | KBDEXT) up:_win_pressed];
223 _win_pressed = !_win_pressed;
224 [self notifyDelegateModifiersChanged];
225}
226
227#pragma mark Sending special key strokes
228
229- (void)sendEnterKeyStroke
230{
231 [self sendVirtualKeyCode:(VK_RETURN | KBDEXT)];
232}
233
234- (void)sendEscapeKeyStroke
235{
236 [self sendVirtualKeyCode:VK_ESCAPE];
237}
238
239- (void)sendBackspaceKeyStroke
240{
241 [self sendVirtualKeyCode:VK_BACK];
242}
243
244@end
245
246#pragma mark -
247@implementation RDPKeyboard (Private)
248
249- (void)handleAlphaNumChar:(int)character
250{
251 // if we receive an uppercase letter - make it lower and send an shift down event to server
252 BOOL shift_was_sent = NO;
253 if (isupper(character) && _shift_pressed == NO)
254 {
255 character = tolower(character);
256 [self sendVirtualKey:VK_LSHIFT up:NO];
257 shift_was_sent = YES;
258 }
259
260 // convert the character to a VK
261 int vk = _virtual_key_map[character];
262 if (vk != 0)
263 {
264 // send key pressed
265 [self sendVirtualKey:vk up:NO];
266 [self sendVirtualKey:vk up:YES];
267 }
268
269 // send the missing shift up if we had a shift down
270 if (shift_was_sent)
271 [self sendVirtualKey:VK_LSHIFT up:YES];
272}
273
274- (void)handleSpecialKey:(int)character
275{
276 if ((character >= 0) && (character < 256) && (_virtual_key_map[character] != 0))
277 {
278 BOOL shift_was_sent = NO;
279 if (_virtual_key_shift_map[character] && !_shift_pressed)
280 {
281 [self sendVirtualKey:VK_LSHIFT up:NO];
282 shift_was_sent = YES;
283 }
284
285 [self sendVirtualKey:_virtual_key_map[character] up:NO];
286 [self sendVirtualKey:_virtual_key_map[character] up:YES];
287
288 if (shift_was_sent)
289 [self sendVirtualKey:VK_LSHIFT up:YES];
290 return;
291 }
292
293 // Fall back for characters that have no US keyboard scancode representation.
294 [self sendUnicodeKey:character up:NO];
295 [self sendUnicodeKey:character up:YES];
296}
297
298- (void)sendUnicodeKey:(int)character up:(BOOL)up
299{
300 [_session
301 sendInputEvent:[NSDictionary
302 dictionaryWithObjectsAndKeys:
303 @"keyboard", @"type", @"unicode", @"subtype",
304 [NSNumber numberWithUnsignedShort:(up ? KBD_FLAGS_RELEASE : 0)],
305 @"flags", [NSNumber numberWithUnsignedShort:character],
306 @"unicode_char", nil]];
307}
308
309- (void)notifyDelegateModifiersChanged
310{
311 if ([[self delegate] respondsToSelector:@selector(modifiersChangedForKeyboard:)])
312 [[self delegate] modifiersChangedForKeyboard:self];
313}
314
315@end