aboutsummaryrefslogtreecommitdiff
path: root/src/locale.c
blob: afe7ec14a4950f036d43dbf847e092d6272ddf93 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include "locale.h"
#include "hashtable.h"
#include <Carbon/Carbon.h>
#include <IOKit/hidsystem/ev_keymap.h>

#define internal static
#define local_persist static

internal struct table keymap_table;

internal char *
copy_cf_string_to_c(CFStringRef string)
{
    CFStringEncoding encoding = kCFStringEncodingUTF8;
    CFIndex length = CFStringGetLength(string);
    CFIndex bytes = CFStringGetMaximumSizeForEncoding(length, encoding);
    char *result = malloc(bytes + 1);

    // NOTE(koekeishiya): Boolean: typedef -> unsigned char; false = 0, true != 0
    Boolean success = CFStringGetCString(string, result, bytes + 1, encoding);
    if (!success) {
        free(result);
        result = NULL;
    }

    return result;
}

internal int
hash_keymap(const char *a)
{
    unsigned long hash = 0, high;
    while (*a) {
        hash = (hash << 4) + *a++;
        high = hash & 0xF0000000;
        if (high) {
            hash ^= (high >> 24);
        }
        hash &= ~high;
    }
    return hash;
}

internal bool
same_keymap(const char *a, const char *b)
{
    while (*a && *b && *a == *b) {
        ++a;
        ++b;
    }
    return *a == '\0' && *b == '\0';
}

internal CFStringRef
cfstring_from_keycode(CGKeyCode keycode)
{
    TISInputSourceRef keyboard = TISCopyCurrentASCIICapableKeyboardLayoutInputSource();
    CFDataRef uchr = (CFDataRef) TISGetInputSourceProperty(keyboard, kTISPropertyUnicodeKeyLayoutData);
    CFRelease(keyboard);

    UCKeyboardLayout *keyboard_layout = (UCKeyboardLayout *) CFDataGetBytePtr(uchr);
    if (keyboard_layout) {
        UInt32 dead_key_state = 0;
        UniCharCount max_string_length = 255;
        UniCharCount string_length = 0;
        UniChar unicode_string[max_string_length];

        OSStatus status = UCKeyTranslate(keyboard_layout, keycode,
                                         kUCKeyActionDown, 0,
                                         LMGetKbdType(), 0,
                                         &dead_key_state,
                                         max_string_length,
                                         &string_length,
                                         unicode_string);

        if (string_length == 0 && dead_key_state) {
            status = UCKeyTranslate(keyboard_layout, kVK_Space,
                                    kUCKeyActionDown, 0,
                                    LMGetKbdType(), 0,
                                    &dead_key_state,
                                    max_string_length,
                                    &string_length,
                                    unicode_string);
        }

        if (string_length > 0 && status == noErr) {
            return CFStringCreateWithCharacters(NULL, unicode_string, string_length);
        }
    }

    return NULL;
}

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast"
internal void
initialize_keycode_map()
{
    table_init(&keymap_table,
               131,
               (table_hash_func) hash_keymap,
               (table_compare_func) same_keymap);

    for (unsigned index = 0; index < 128; ++index) {
        CFStringRef key_string = cfstring_from_keycode(index);
        if (!key_string) continue;

        char *c_key_string = copy_cf_string_to_c(key_string);
        CFRelease(key_string);
        if (!c_key_string) continue;

        table_add(&keymap_table, c_key_string, (void *)index);
    }
}
#pragma clang diagnostic pop

uint32_t keycode_from_char(char key)
{
    uint32_t keycode = 0;
    char lookup_key[2];

    if (!keymap_table.count) {
        initialize_keycode_map();
    }

    snprintf(lookup_key, 2, "%c", key);
    keycode = (uint32_t) table_find(&keymap_table, &lookup_key);

    return keycode;
}