UTM/Platform/iOS/Display/VMKeyboardView.m

464 lines
14 KiB
Objective-C

//
// Copyright © 2019 osy. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Parts taken from launcher-mobile
/*
* launcher-mobile: a multiplatform flexVDI/SPICE client
*
* Copyright (C) 2016 flexVDI (Flexible Software Solutions S.L.)
*
* This file is part of launcher-mobile.
*
* launcher-mobile is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* launcher-mobile is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with launcher-mobile. If not, see <http://www.gnu.org/licenses/>.
*/
#import "VMKeyboardView.h"
#import "UTMLogging.h"
#import "ctype.h"
typedef struct {
char tc;
int prekey;
int special_prekey;
int special_key;
int key;
} key_mapping_t;
typedef struct {
char tc;
char ext1;
char ext2;
int prekey;
int special_prekey;
int special_key;
int key;
} ext_key_mapping_t;
const ext_key_mapping_t pc104_es_ext[] = {
{194, 161, 0, 0x0, 0x0, 0x0, 0x0d}, // ¡
{194, 191, 0, 0x0, 0x0, 0x36, 0x0d}, // ¿
{194, 186, 0, 0x0, 0x0, 0x0, 0x29}, // º
{194, 170, 0, 0x0, 0x0, 0x36, 0x29}, // ª
{194, 169, 0, 0x0, 0x0, 0x1d, 0x2e}, // Ctrl + C
{194, 174, 0, 0x0, 0x0, 0x1d, 0x13},
{195, 177, 0, 0x0, 0x0, 0x0, 0x27}, // ñ
{195, 145, 0, 0x0, 0x0, 0x36, 0x27}, // ñ
{195, 167, 0, 0x0, 0x0, 0x0, 0x2b}, // ç
{195, 135, 0, 0x0, 0x0, 0x36, 0x2b}, // Ç
{195, 161, 0, 0x28, 0x0, 0x0, 0x1e}, // á
{195, 169, 0, 0x28, 0x0, 0x0, 0x12}, // é
{195, 173, 0, 0x28, 0x0, 0x0, 0x17}, // í
{195, 179, 0, 0x28, 0x0, 0x0, 0x18}, // ó
{195, 186, 0, 0x28, 0x0, 0x0, 0x16}, // ú
{195, 188, 0, 0x28, 0x36, 0x0, 0x16}, // ü
{195, 129, 0, 0x28, 0x0, 0x36, 0x1e}, // Á
{195, 137, 0, 0x28, 0x0, 0x36, 0x12}, // É
{195, 141, 0, 0x28, 0x0, 0x36, 0x17}, // Í
{195, 147, 0, 0x28, 0x0, 0x36, 0x18}, // Ó
{195, 154, 0, 0x28, 0x0, 0x36, 0x16}, // Ú
{195, 156, 0, 0x28, 0x36, 0x36, 0x16}, // Ü
{195, 159, 0, 0x0, 0x0, 0x1d, 0x30}, // Ctrl + B
{197, 147, 0, 0x0, 0x0, 0x38, 0x0f}, // Alt + Tab
{198, 146, 0, 0x0, 0x0, 0x1d, 0x21}, // Ctrl + F
{206, 169, 0, 0x0, 0x0, 0x1d, 0x2c}, // Ctrl + Z
{226, 130, 172, 0x0, 0x0, 0x138, 0x12}, // Euro
{226, 137, 164, 0x0, 0x0, 0x138, 0x29}, // Backslash
{226, 136, 145, 0x0, 0x0, 0x1d, 0x2d}, // Ctrl + X
{226, 136, 154, 0x0, 0x0, 0x1d, 0x2f}, // Ctrl + V
{226, 136, 130, 0x0, 0x0, 0x1d, 0x20} // Ctrl + D
};
const key_mapping_t pc104_es[] = {
{9, 0x0, 0x0, 0x0, 0xf}, // Tab
{'a', 0x0, 0x0, 0x0, 0x1e},
{'b', 0x0, 0x0, 0x0, 0x30},
{'c', 0x0, 0x0, 0x0, 0x2e},
{'d', 0x0, 0x0, 0x0, 0x20},
{'e', 0x0, 0x0, 0x0, 0x12},
{'f', 0x0, 0x0, 0x0, 0x21},
{'g', 0x0, 0x0, 0x0, 0x22},
{'h', 0x0, 0x0, 0x0, 0x23},
{'i', 0x0, 0x0, 0x0, 0x17},
{'j', 0x0, 0x0, 0x0, 0x24},
{'k', 0x0, 0x0, 0x0, 0x25},
{'l', 0x0, 0x0, 0x0, 0x26},
{'m', 0x0, 0x0, 0x0, 0x32},
{'n', 0x0, 0x0, 0x0, 0x31},
{'o', 0x0, 0x0, 0x0, 0x18},
{'p', 0x0, 0x0, 0x0, 0x19},
{'q', 0x0, 0x0, 0x0, 0x10},
{'r', 0x0, 0x0, 0x0, 0x13},
{'s', 0x0, 0x0, 0x0, 0x1f},
{'t', 0x0, 0x0, 0x0, 0x14},
{'u', 0x0, 0x0, 0x0, 0x16},
{'v', 0x0, 0x0, 0x0, 0x2f},
{'w', 0x0, 0x0, 0x0, 0x11},
{'x', 0x0, 0x0, 0x0, 0x2d},
{'y', 0x0, 0x0, 0x0, 0x15},
{'z', 0x0, 0x0, 0x0, 0x2c},
{'1', 0x0, 0x0, 0x0, 0x02},
{'2', 0x0, 0x0, 0x0, 0x03},
{'3', 0x0, 0x0, 0x0, 0x04},
{'4', 0x0, 0x0, 0x0, 0x05},
{'5', 0x0, 0x0, 0x0, 0x06},
{'6', 0x0, 0x0, 0x0, 0x07},
{'7', 0x0, 0x0, 0x0, 0x08},
{'8', 0x0, 0x0, 0x0, 0x09},
{'9', 0x0, 0x0, 0x0, 0x0a},
{'0', 0x0, 0x0, 0x0, 0x0b},
{' ', 0x0, 0x0, 0x0, 0x39},
{'!', 0x0, 0x0, 0x36, 0x02},
{'@', 0x0, 0x0, 0x138, 0x03},
{'"', 0x0, 0x0, 0x36, 0x03},
{'\'', 0x0, 0x0, 0x0, 0x0c},
{'#', 0x0, 0x0, 0x138, 0x04},
{'~', 0x0, 0x0, 0x138, 0x05},
{'$', 0x0, 0x0, 0x36, 0x05},
{'%', 0x0, 0x0, 0x36, 0x06},
{'&', 0x0, 0x0, 0x36, 0x07},
{'/', 0x0, 0x0, 0x36, 0x08},
{'(', 0x0, 0x0, 0x36, 0x09},
{')', 0x0, 0x0, 0x36, 0x0a},
{'=', 0x0, 0x0, 0x36, 0x0b},
{'?', 0x0, 0x0, 0x36, 0x0c},
{'-', 0x0, 0x0, 0x0, 0x35},
{'_', 0x0, 0x0, 0x36, 0x35},
{';', 0x0, 0x0, 0x36, 0x33},
{',', 0x0, 0x0, 0x0, 0x33},
{'.', 0x0, 0x0, 0x0, 0x34},
{':', 0x0, 0x0, 0x36, 0x34},
{'{', 0x0, 0x0, 0x138, 0x28},
{'}', 0x0, 0x0, 0x138, 0x2b},
{'[', 0x0, 0x0, 0x138, 0x1a},
{']', 0x0, 0x0, 0x138, 0x1b},
{'*', 0x0, 0x0, 0x36, 0x1b},
{'+', 0x0, 0x0, 0x0, 0x1b},
{'\\', 0x0, 0x0, 0x138, 0x29},
{'|', 0x0, 0x0, 0x138, 0x02},
{'^', 0x0, 0x0, 0x36, 0x1a},
{'`', 0x0, 0x0, 0x0, 0x1a},
{'<', 0x0, 0x0, 0x0, 0x56},
{'>', 0x0, 0x0, 0x36, 0x56},
{'\r', 0x0, 0x0, 0x0, 0x1c},
{'\n', 0x0, 0x0, 0x0, 0x1c},
{'\'', 0x0, 0x0, 0x0, 0x28},
{'"', 0x0, 0x0, 0x36, 0x28},
{'\t', 0x0, 0x0, 0x0, 0x0F},
{'\b', 0x0, 0x0, 0x0, 0x0E},
};
const ext_key_mapping_t pc104_us_ext[] = {
{195, 167, 0, 0x0, 0x0, 0x1d, 0x2e}, // Ctrl + C
{197, 147, 0, 0x0, 0x0, 0x38, 0x0f}, // Alt + Tab
{198, 146, 0, 0x0, 0x0, 0x1d, 0x21}, // Ctrl + F
{206, 169, 0, 0x0, 0x0, 0x1d, 0x2c}, // Ctrl + Z
{226, 137, 136, 0x0, 0x0, 0x1d, 0x2d}, // Ctrl + X
{226, 136, 154, 0x0, 0x0, 0x1d, 0x2f}, // Ctrl + V
{226, 136, 171, 0x0, 0x0, 0x1d, 0x30}, // Ctrl + B
{226, 136, 130, 0x0, 0x0, 0x1d, 0x20} // Ctrl + D
};
const key_mapping_t pc104_us[] = {
{9, 0x0, 0x0, 0x0, 0xf}, // Tab
{'a', 0x0, 0x0, 0x0, 0x1e},
{'b', 0x0, 0x0, 0x0, 0x30},
{'c', 0x0, 0x0, 0x0, 0x2e},
{'d', 0x0, 0x0, 0x0, 0x20},
{'e', 0x0, 0x0, 0x0, 0x12},
{'f', 0x0, 0x0, 0x0, 0x21},
{'g', 0x0, 0x0, 0x0, 0x22},
{'h', 0x0, 0x0, 0x0, 0x23},
{'i', 0x0, 0x0, 0x0, 0x17},
{'j', 0x0, 0x0, 0x0, 0x24},
{'k', 0x0, 0x0, 0x0, 0x25},
{'l', 0x0, 0x0, 0x0, 0x26},
{'m', 0x0, 0x0, 0x0, 0x32},
{'n', 0x0, 0x0, 0x0, 0x31},
{'o', 0x0, 0x0, 0x0, 0x18},
{'p', 0x0, 0x0, 0x0, 0x19},
{'q', 0x0, 0x0, 0x0, 0x10},
{'r', 0x0, 0x0, 0x0, 0x13},
{'s', 0x0, 0x0, 0x0, 0x1f},
{'t', 0x0, 0x0, 0x0, 0x14},
{'u', 0x0, 0x0, 0x0, 0x16},
{'v', 0x0, 0x0, 0x0, 0x2f},
{'w', 0x0, 0x0, 0x0, 0x11},
{'x', 0x0, 0x0, 0x0, 0x2d},
{'y', 0x0, 0x0, 0x0, 0x15},
{'z', 0x0, 0x0, 0x0, 0x2c},
{'1', 0x0, 0x0, 0x0, 0x02},
{'2', 0x0, 0x0, 0x0, 0x03},
{'3', 0x0, 0x0, 0x0, 0x04},
{'4', 0x0, 0x0, 0x0, 0x05},
{'5', 0x0, 0x0, 0x0, 0x06},
{'6', 0x0, 0x0, 0x0, 0x07},
{'7', 0x0, 0x0, 0x0, 0x08},
{'8', 0x0, 0x0, 0x0, 0x09},
{'9', 0x0, 0x0, 0x0, 0x0a},
{'0', 0x0, 0x0, 0x0, 0x0b},
{' ', 0x0, 0x0, 0x0, 0x39},
{'!', 0x0, 0x0, 0x36, 0x02},
{'@', 0x0, 0x0, 0x36, 0x03},
{'"', 0x0, 0x0, 0x36, 0x28},
{'\'', 0x0, 0x0, 0x0, 0x28},
{'#', 0x0, 0x0, 0x36, 0x04},
{'~', 0x0, 0x0, 0x36, 0x29},
{'$', 0x0, 0x0, 0x36, 0x05},
{'%', 0x0, 0x0, 0x36, 0x06},
{'&', 0x0, 0x0, 0x36, 0x08},
{'/', 0x0, 0x0, 0x0, 0x35},
{'(', 0x0, 0x0, 0x36, 0x0a},
{')', 0x0, 0x0, 0x36, 0x0b},
{'=', 0x0, 0x0, 0x0, 0x0d},
{'+', 0x0, 0x0, 0x36, 0x0d},
{'?', 0x0, 0x0, 0x36, 0x35},
{'-', 0x0, 0x0, 0x0, 0x0c},
{'_', 0x0, 0x0, 0x36, 0x0c},
{';', 0x0, 0x0, 0x0, 0x27},
{',', 0x0, 0x0, 0x0, 0x33},
{'.', 0x0, 0x0, 0x0, 0x34},
{':', 0x0, 0x0, 0x36, 0x27},
{'{', 0x0, 0x0, 0x36, 0x1a},
{'}', 0x0, 0x0, 0x36, 0x1b},
{'[', 0x0, 0x0, 0x0, 0x1a},
{']', 0x0, 0x0, 0x0, 0x1b},
{'*', 0x0, 0x0, 0x36, 0x09},
{'+', 0x0, 0x0, 0x0, 0x1b},
{'\\', 0x0, 0x0, 0x0, 0x2b},
{'|', 0x0, 0x0, 0x36, 0x2b},
{'^', 0x0, 0x0, 0x36, 0x07},
{'`', 0x0, 0x0, 0x0, 0x29},
{'<', 0x0, 0x0, 0x36, 0x33},
{'>', 0x0, 0x0, 0x36, 0x34},
{'\r', 0x0, 0x0, 0x0, 0x1c},
{'\n', 0x0, 0x0, 0x0, 0x1c},
{'\'', 0x0, 0x0, 0x0, 0x28},
{'"', 0x0, 0x0, 0x36, 0x28},
{'\t', 0x0, 0x0, 0x0, 0x0F},
{'\b', 0x0, 0x0, 0x0, 0x0E},
};
static int indexForChar(const key_mapping_t *table, size_t table_len, char tc) {
int i;
for (i = 0; i < table_len; i++) {
if (tc == table[i].tc) {
return i;
}
}
return -1;
}
static int indexForExtChar(const ext_key_mapping_t *table, size_t table_len, char tc, char ext1, char ext2) {
int i;
for (i = 0; i < table_len; i++) {
if (tc == table[i].tc &&
ext1 == table[i].ext1) {
if (ext2 != 0 && ext2 == table[i].ext2) {
return i;
} else if (ext2 == 0) {
return i;
}
}
}
return -1;
}
@implementation VMKeyboardView {
const key_mapping_t *_map;
size_t _map_len;
const ext_key_mapping_t *_ext_map;
size_t _ext_map_len;
}
- (UIKeyboardType)keyboardType {
return UIKeyboardTypeASCIICapable;
}
- (UITextAutocapitalizationType)autocapitalizationType {
return UITextAutocapitalizationTypeNone;
}
- (UITextAutocorrectionType)autocorrectionType {
return UITextAutocorrectionTypeNo;
}
- (UITextSpellCheckingType)spellCheckingType {
return UITextSpellCheckingTypeNo;
}
- (UITextSmartQuotesType)smartQuotesType {
return UITextSmartQuotesTypeNo;
}
- (UITextSmartDashesType)smartDashesType {
return UITextSmartDashesTypeNo;
}
- (UITextSmartInsertDeleteType)smartInsertDeleteType {
return UITextSmartInsertDeleteTypeNo;
}
- (void)configureTables {
NSString *language = [[NSLocale preferredLanguages] objectAtIndex:0];
if ([language isEqual:@"es-ES"]) {
_map = pc104_es;
_map_len = sizeof(pc104_es)/sizeof(pc104_es[0]);
_ext_map = pc104_es_ext;
_ext_map_len = sizeof(pc104_es_ext)/sizeof(pc104_es_ext[0]);
} else {
_map = pc104_us;
_map_len = sizeof(pc104_us)/sizeof(pc104_us[0]);
_ext_map = pc104_us_ext;
_ext_map_len = sizeof(pc104_us_ext)/sizeof(pc104_us_ext[0]);
}
}
- (BOOL)hasText {
return YES;
}
- (void)deleteBackward {
[self.delegate keyboardView:self didPressKeyDown:0x0E];
[NSThread sleepForTimeInterval:0.05f];
[self.delegate keyboardView:self didPressKeyUp:0x0E];
}
- (void)insertText:(nonnull NSString *)text {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
[text enumerateSubstringsInRange:NSMakeRange(0, text.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString * _Nullable substring, NSRange substringRange, NSRange enclosingRange, BOOL * _Nonnull stop) {
const char *seq = [substring UTF8String];
[self insertUTF8Sequence:seq];
// we need to pause a bit or the keypress will be too fast!
[NSThread sleepForTimeInterval:0.001f];
}];
});
}
- (void)insertUTF8Sequence:(const char *)ctext {
unsigned long ctext_len = strlen(ctext);
UTMLog(@"ctext length=%lu\n", ctext_len);
unsigned char tc = ctext[0];
int keycode = 0;
int special = 0;
int prekey = 0;
int prekey_special = 0;
int is_upper = false;
int index = -1;
if (!_map) {
[self configureTables];
}
if (isalpha(tc)) {
if (isupper(tc)) {
tc = tolower(tc);
is_upper = true;
}
}
switch (ctext_len) {
case 1:
UTMLog(@"char=%d\n", tc);
index = indexForChar(_map, _map_len, tc);
if (index != -1) {
keycode = _map[index].key;
special = _map[index].special_key;
}
break;
case 2:
UTMLog(@"char=%d\n", tc);
UTMLog(@"ext1=%d\n", (unsigned char) ctext[1]);
index = indexForExtChar(_ext_map, _ext_map_len, tc, ctext[1], 0);
if (index != -1) {
keycode = _ext_map[index].key;
special = _ext_map[index].special_key;
prekey = _ext_map[index].prekey;
prekey_special = _ext_map[index].special_prekey;
}
break;
case 3:
UTMLog(@"char=%d\n", tc);
UTMLog(@"ext1=%d\n", (unsigned char) ctext[1]);
UTMLog(@"ext2=%d\n", (unsigned char) ctext[2]);
index = indexForExtChar(_ext_map, _ext_map_len, tc, ctext[1], ctext[2]);
if (index != -1) {
keycode = _ext_map[index].key;
special = _ext_map[index].special_key;
prekey = _ext_map[index].prekey;
prekey_special = _ext_map[index].special_prekey;
}
break;
}
if (keycode) {
if (is_upper) {
special = 0x2A;
}
if (prekey) {
if (prekey_special) {
[self.delegate keyboardView:self didPressKeyDown:special];
}
[self.delegate keyboardView:self didPressKeyDown:prekey];
[NSThread sleepForTimeInterval:0.05f];
[self.delegate keyboardView:self didPressKeyUp:prekey];
if (prekey_special) {
[self.delegate keyboardView:self didPressKeyUp:special];
}
}
if (special) {
[self.delegate keyboardView:self didPressKeyDown:special];
}
[self.delegate keyboardView:self didPressKeyDown:keycode];
[NSThread sleepForTimeInterval:0.05f];
[self.delegate keyboardView:self didPressKeyUp:keycode];
if (special) {
[self.delegate keyboardView:self didPressKeyUp:special];
}
}
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
@end