aboutsummaryrefslogtreecommitdiff
path: root/json_layer.py
blob: 4d4c69cdf78c68d9c3da10e8b2f7451ede4a7c10 (plain)
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#
# Read data in the json format from the serial connexion.
# The json shall hold in a single line, and the following commands are
# recognized :
# {"layer": layer_name} : change the keyboard to the given layer
# {"stack": layer_name} : stack a new layer on top on an existing one
# {"get_layer"}
#

from usb_cdc import data
import json

from kmk.modules import Module
from kmk.keys import Key, KC
#from kmk.handlers.sequences import simple_key_sequence
from kmk.modules.macros import Delay, Macros, Tap

# This dictionnary is a sort of point of function.
#
# It gives a way to apply a specific function in the json
#
# There is mutually recursive calls between
#   - operators
#   - key_of_json
#   - sequence
#
# I do not define it yet, only declare it.
operators = {}

def key_of_json(element, inside=False):
    """ Create the key from the key name.
        The value can be either a string or a list. If the parameter is a
        string, the associated key will be returned, otherwise this will be the
        chain of all of them.
    """
    if isinstance(element, int):
        return element
    if isinstance(element, str):
        return KC[element]
    if isinstance(element, list):
        hd, *tl = element
        key = key_of_json(hd)
        if len(tl) != 0:
            # Chainable key / recursive function
            return key( key_of_json(tl) )
        else:
            return key
    if isinstance(element, dict):
        for key, value in element.items():
            return operators[key](value)
    pass

def encode_macro(element):
    """ Encode the result of the function key_of_json in order to build a macro
        of this
    """
    key = key_of_json(element)
    if isinstance(key, int):
        return Delay(key)
    elif isinstance(key, Key):
        return Tap(key)
    return key

def sequence(element):
    """ Convert a list of keys into a sequence of them.
    """
    # Convert each element in the list as Tap function
    seq = list(map(encode_macro, element))
    # Send the list as arguments to the function
    return KC.MACRO(*seq)

def no_release(element):
    key = key_of_json(element)
    return key(no_release=True)

def no_press(element):
    key = key_of_json(element)
    return key(no_press=True)

# Now I can define the dictionnary
operators["key"]        = key_of_json
operators["chain"]      = key_of_json
operators["seq"]        = sequence
operators["no_release"] = no_release
operators["no_press"]   = no_press

class Layer(object):
    """ Layer as an object.
        This class gives the property name in addition of the keymap list
    """
    def __init__(self, jdata):
        self.load(jdata)

    def load(self, json_data):
        jdata = json.loads(json_data.strip())
        for name, keys in jdata.items():
            self.name = name
            self.keys = list(map(key_of_json, keys))

    def __getitem__(self, idx):
        """ When the layer is indexed, return the keymap associated with
        """
        return self.keys[idx]

    def __eq__(self, other):
        """ Compare two layer by the name only
        """
        return self.name == other.name

class JsonLayer(Module):

    def during_bootup(self, keyboard):
        try:
            # Do not set any timeout, we check before reading a string if there
            # is any content to read, but block to be sure to read everything.
            data.timeout = None
        except AttributeError:
            pass

    def before_matrix_scan(self, keyboard):
        pass

    def after_matrix_scan(self, keyboard):
        pass

    def process_key(self, keyboard, key, is_pressed, int_coord):
        return key

    def before_hid_send(self, keyboard):
        # Serial.data isn't initialized.
        if not data:
            return

        # Nothing to parse.
        if data.in_waiting <= 0:
            return
        line = data.readline()
        if not line:
            return

        try:
            keyboard.keymap[0].load(line.decode())
        except Exception as err:
            print(err)

    def after_hid_send(self, keyboard):
        pass

    def on_powersave_enable(self, keyboard):
        pass

    def on_powersave_disable(self, keyboard):
        pass