forked from mirrors/qmk_userspace
		
	Add consumer/system usage support.
This commit is contained in:
		
					parent
					
						
							
								e9af482690
							
						
					
				
			
			
				commit
				
					
						16ba9bda56
					
				
			
		
					 5 changed files with 141 additions and 38 deletions
				
			
		| 
						 | 
				
			
			@ -69,6 +69,14 @@ void host_unregister_key(uint8_t key)
 | 
			
		|||
    host_send_keyboard_report();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void host_clear_all_keys_but_mods(void)
 | 
			
		||||
{
 | 
			
		||||
    for (int8_t i = 0; i < REPORT_KEYS; i++) {
 | 
			
		||||
        keyboard_report->keys[i] = 0;
 | 
			
		||||
    }
 | 
			
		||||
    host_send_keyboard_report();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* keyboard report operations */
 | 
			
		||||
void host_add_key(uint8_t key)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,6 +42,7 @@ uint8_t host_keyboard_leds(void);
 | 
			
		|||
/* new interface */
 | 
			
		||||
void host_register_key(uint8_t key);
 | 
			
		||||
void host_unregister_key(uint8_t key);
 | 
			
		||||
void host_clear_all_keys_but_mods(void);
 | 
			
		||||
 | 
			
		||||
/* keyboard report operations */
 | 
			
		||||
void host_add_key(uint8_t key);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,8 +41,6 @@ typedef enum keykind {
 | 
			
		|||
    FNK_DOWN, FNK_UP,
 | 
			
		||||
    KEY_DOWN, KEY_UP,
 | 
			
		||||
    MOD_DOWN, MOD_UP,
 | 
			
		||||
    MOUSEKEY_DOWN, MOUSEKEY_UP,
 | 
			
		||||
    DELAY
 | 
			
		||||
} keykind_t;
 | 
			
		||||
 | 
			
		||||
typedef enum { IDLE, DELAYING, WAITING, PRESSING } kbdstate_t;
 | 
			
		||||
| 
						 | 
				
			
			@ -77,7 +75,9 @@ static inline keykind_t get_keykind(uint8_t code, bool pressed)
 | 
			
		|||
        else
 | 
			
		||||
            return (pressed ? FN_DOWN : FN_UP);
 | 
			
		||||
    }
 | 
			
		||||
    if IS_MOUSEKEY(code)    return (pressed ? MOUSEKEY_DOWN : MOUSEKEY_UP);
 | 
			
		||||
    if IS_MOUSEKEY(code)    return (pressed ? KEY_DOWN : KEY_UP);
 | 
			
		||||
    if IS_SYSTEM(code)      return (pressed ? KEY_DOWN : KEY_UP);
 | 
			
		||||
    if IS_CONSUMER(code)    return (pressed ? KEY_DOWN : KEY_UP);
 | 
			
		||||
    return  NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -86,7 +86,13 @@ static void layer_switch_on(uint8_t code)
 | 
			
		|||
    if (!IS_FN(code)) return;
 | 
			
		||||
    fn_state_bits |= FN_BIT(code);
 | 
			
		||||
    if (current_layer != keymap_fn_layer(FN_INDEX(code))) {
 | 
			
		||||
        //TODO: clear all key execpt Mod key
 | 
			
		||||
        // clear all key execpt Mod key
 | 
			
		||||
        host_clear_all_keys_but_mods();
 | 
			
		||||
        host_system_send(0);
 | 
			
		||||
        host_consumer_send(0);
 | 
			
		||||
        mousekey_clear();
 | 
			
		||||
        mousekey_send();
 | 
			
		||||
 | 
			
		||||
        debug("Layer Switch(on): "); debug_hex(current_layer);
 | 
			
		||||
        current_layer = keymap_fn_layer(FN_INDEX(code));
 | 
			
		||||
        debug(" -> "); debug_hex(current_layer); debug("\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -98,7 +104,13 @@ static void layer_switch_off(uint8_t code)
 | 
			
		|||
    if (!IS_FN(code)) return;
 | 
			
		||||
    fn_state_bits &= ~FN_BIT(code);
 | 
			
		||||
    if (current_layer != keymap_fn_layer(biton(fn_state_bits))) {
 | 
			
		||||
        //TODO: clear all key execpt Mod key
 | 
			
		||||
        // clear all key execpt Mod key
 | 
			
		||||
        host_clear_all_keys_but_mods();
 | 
			
		||||
        host_system_send(0);
 | 
			
		||||
        host_consumer_send(0);
 | 
			
		||||
        mousekey_clear();
 | 
			
		||||
        mousekey_send();
 | 
			
		||||
 | 
			
		||||
        debug("Layer Switch(off): "); debug_hex(current_layer);
 | 
			
		||||
        current_layer = keymap_fn_layer(biton(fn_state_bits));
 | 
			
		||||
        debug(" -> "); debug_hex(current_layer); debug("\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -128,6 +140,7 @@ static inline bool is_anykey_down(void)
 | 
			
		|||
 | 
			
		||||
static void register_code(uint8_t code)
 | 
			
		||||
{
 | 
			
		||||
debug("register_code\n");
 | 
			
		||||
    if IS_KEY(code) {
 | 
			
		||||
        host_add_key(code);
 | 
			
		||||
        host_send_keyboard_report();
 | 
			
		||||
| 
						 | 
				
			
			@ -140,6 +153,84 @@ static void register_code(uint8_t code)
 | 
			
		|||
        mousekey_on(code);
 | 
			
		||||
        mousekey_send();
 | 
			
		||||
    }
 | 
			
		||||
    else if IS_CONSUMER(code) {
 | 
			
		||||
debug("consumer\n");
 | 
			
		||||
        uint16_t usage = 0;
 | 
			
		||||
        switch (code) {
 | 
			
		||||
            case KB_AUDIO_MUTE:
 | 
			
		||||
                usage = AUDIO_MUTE;
 | 
			
		||||
                break;
 | 
			
		||||
            case KB_AUDIO_VOL_UP:
 | 
			
		||||
                usage = AUDIO_VOL_UP;
 | 
			
		||||
                break;
 | 
			
		||||
            case KB_AUDIO_VOL_DOWN:
 | 
			
		||||
                usage = AUDIO_VOL_DOWN;
 | 
			
		||||
                break;
 | 
			
		||||
            case KB_MEDIA_NEXT_TRACK:
 | 
			
		||||
                usage = TRANSPORT_NEXT_TRACK;
 | 
			
		||||
                break;
 | 
			
		||||
            case KB_MEDIA_PREV_TRACK:
 | 
			
		||||
                usage = TRANSPORT_PREV_TRACK;
 | 
			
		||||
                break;
 | 
			
		||||
            case KB_MEDIA_STOP:
 | 
			
		||||
                usage = TRANSPORT_STOP;
 | 
			
		||||
                break;
 | 
			
		||||
            case KB_MEDIA_PLAY_PAUSE:
 | 
			
		||||
                usage = TRANSPORT_PLAY_PAUSE;
 | 
			
		||||
                break;
 | 
			
		||||
            case KB_MEDIA_SELECT:
 | 
			
		||||
                usage = AL_CC_CONFIG;
 | 
			
		||||
                break;
 | 
			
		||||
            case KB_MAIL:
 | 
			
		||||
                usage = AL_EMAIL;
 | 
			
		||||
                break;
 | 
			
		||||
            case KB_CALCULATOR:
 | 
			
		||||
                usage = AL_CALCULATOR;
 | 
			
		||||
                break;
 | 
			
		||||
            case KB_MY_COMPUTER:
 | 
			
		||||
                usage = AL_LOCAL_BROWSER;
 | 
			
		||||
                break;
 | 
			
		||||
            case KB_WWW_SEARCH:
 | 
			
		||||
                usage = AC_SEARCH;
 | 
			
		||||
                break;
 | 
			
		||||
            case KB_WWW_HOME:
 | 
			
		||||
                usage = AC_HOME;
 | 
			
		||||
                break;
 | 
			
		||||
            case KB_WWW_BACK:
 | 
			
		||||
                usage = AC_BACK;
 | 
			
		||||
                break;
 | 
			
		||||
            case KB_WWW_FORWARD:
 | 
			
		||||
                usage = AC_FORWARD;
 | 
			
		||||
                break;
 | 
			
		||||
            case KB_WWW_STOP:
 | 
			
		||||
                usage = AC_STOP;
 | 
			
		||||
                break;
 | 
			
		||||
            case KB_WWW_REFRESH:
 | 
			
		||||
                usage = AC_REFRESH;
 | 
			
		||||
                break;
 | 
			
		||||
            case KB_WWW_FAVORITES:
 | 
			
		||||
                usage = AC_BOOKMARKS;
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
debug("usage: "); phex16(usage); debug("\n");
 | 
			
		||||
        host_consumer_send(usage);
 | 
			
		||||
    }
 | 
			
		||||
    else if IS_SYSTEM(code) {
 | 
			
		||||
        uint16_t usage = 0;
 | 
			
		||||
        switch (code) {
 | 
			
		||||
            case KB_SYSTEM_POWER:
 | 
			
		||||
                usage = SYSTEM_POWER_DOWN;
 | 
			
		||||
                break;
 | 
			
		||||
            case KB_SYSTEM_SLEEP:
 | 
			
		||||
                usage = SYSTEM_SLEEP;
 | 
			
		||||
                break;
 | 
			
		||||
            case KB_SYSTEM_WAKE:
 | 
			
		||||
                usage = SYSTEM_WAKE_UP;
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        host_system_send(usage);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void unregister_code(uint8_t code)
 | 
			
		||||
| 
						 | 
				
			
			@ -156,6 +247,12 @@ static void unregister_code(uint8_t code)
 | 
			
		|||
        mousekey_off(code);
 | 
			
		||||
        mousekey_send();
 | 
			
		||||
    }
 | 
			
		||||
    else if IS_CONSUMER(code) {
 | 
			
		||||
        host_consumer_send(0x0000);
 | 
			
		||||
    }
 | 
			
		||||
    else if IS_SYSTEM(code) {
 | 
			
		||||
        host_system_send(0x0000);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -254,7 +351,6 @@ static inline void process_key(keyevent_t event)
 | 
			
		|||
                    layer_switch_off(code);
 | 
			
		||||
                    break;
 | 
			
		||||
                case KEY_DOWN:
 | 
			
		||||
                case MOUSEKEY_DOWN:
 | 
			
		||||
                    register_code(code);
 | 
			
		||||
                    NEXT(PRESSING);
 | 
			
		||||
                    break;
 | 
			
		||||
| 
						 | 
				
			
			@ -262,7 +358,6 @@ static inline void process_key(keyevent_t event)
 | 
			
		|||
                    register_code(code);
 | 
			
		||||
                    break;
 | 
			
		||||
                case KEY_UP:
 | 
			
		||||
                case MOUSEKEY_UP:
 | 
			
		||||
                case MOD_UP:
 | 
			
		||||
                    unregister_code(code);
 | 
			
		||||
                    break;
 | 
			
		||||
| 
						 | 
				
			
			@ -283,16 +378,16 @@ static inline void process_key(keyevent_t event)
 | 
			
		|||
                    register_code(keymap_fn_keycode(FN_INDEX(code)));
 | 
			
		||||
                    break;
 | 
			
		||||
                case FNK_UP:
 | 
			
		||||
                    // can't know whether layer switched or not
 | 
			
		||||
                    layer_switch_off(code);
 | 
			
		||||
                    unregister_code(keymap_fn_keycode(FN_INDEX(code)));
 | 
			
		||||
                    break;
 | 
			
		||||
                case KEY_DOWN:
 | 
			
		||||
                case MOD_DOWN:
 | 
			
		||||
                case MOUSEKEY_DOWN:
 | 
			
		||||
                    register_code(code);
 | 
			
		||||
                    break;
 | 
			
		||||
                case KEY_UP:
 | 
			
		||||
                case MOD_UP:
 | 
			
		||||
                case MOUSEKEY_UP:
 | 
			
		||||
                    unregister_code(code);
 | 
			
		||||
                    // no key registered? mousekey, mediakey, systemkey
 | 
			
		||||
                    if (!host_has_anykey())
 | 
			
		||||
| 
						 | 
				
			
			@ -307,7 +402,6 @@ static inline void process_key(keyevent_t event)
 | 
			
		|||
                case FN_DOWN:
 | 
			
		||||
                case FNK_DOWN:
 | 
			
		||||
                case KEY_DOWN:
 | 
			
		||||
                case MOUSEKEY_DOWN:
 | 
			
		||||
                    waiting_key = (keyrecord_t) {
 | 
			
		||||
                        .event = event,
 | 
			
		||||
                        .code = code,
 | 
			
		||||
| 
						 | 
				
			
			@ -339,7 +433,6 @@ static inline void process_key(keyevent_t event)
 | 
			
		|||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                case KEY_UP:
 | 
			
		||||
                case MOUSEKEY_UP:
 | 
			
		||||
                    unregister_code(code);
 | 
			
		||||
                    NEXT(IDLE);
 | 
			
		||||
                    break;
 | 
			
		||||
| 
						 | 
				
			
			@ -355,7 +448,6 @@ static inline void process_key(keyevent_t event)
 | 
			
		|||
                case FN_DOWN:
 | 
			
		||||
                case FNK_DOWN:
 | 
			
		||||
                case KEY_DOWN:
 | 
			
		||||
                case MOUSEKEY_DOWN:
 | 
			
		||||
                    tmp_mods = keyboard_report->mods;
 | 
			
		||||
                    host_set_mods(delayed_fn.mods);
 | 
			
		||||
                    register_code(keymap_fn_keycode(FN_INDEX(delayed_fn.code)));
 | 
			
		||||
| 
						 | 
				
			
			@ -389,7 +481,6 @@ static inline void process_key(keyevent_t event)
 | 
			
		|||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                case KEY_UP:
 | 
			
		||||
                case MOUSEKEY_UP:
 | 
			
		||||
                    if (code == waiting_key.code) {
 | 
			
		||||
                        layer_switch_on(delayed_fn.code);
 | 
			
		||||
                        NEXT(IDLE);
 | 
			
		||||
| 
						 | 
				
			
			@ -444,7 +535,6 @@ void keyboard_task(void)
 | 
			
		|||
        matrix_row = matrix_get_row(r);
 | 
			
		||||
        matrix_change = matrix_row ^ matrix_prev[r];
 | 
			
		||||
        if (matrix_change) {
 | 
			
		||||
            // TODO: print once per scan
 | 
			
		||||
            if (debug_matrix) matrix_print();
 | 
			
		||||
 | 
			
		||||
            for (int c = 0; c < MATRIX_COLS; c++) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -135,13 +135,6 @@ void mousekey_send(void)
 | 
			
		|||
void mousekey_clear(void)
 | 
			
		||||
{
 | 
			
		||||
    report = (report_mouse_t){};
 | 
			
		||||
/*
 | 
			
		||||
    report.buttons = 0;
 | 
			
		||||
    report.x = 0;
 | 
			
		||||
    report.y = 0;
 | 
			
		||||
    report.v = 0;
 | 
			
		||||
    report.h = 0;
 | 
			
		||||
*/
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mousekey_debug(void)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,15 +24,20 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
#define IS_ERROR(code)           (KB_ROLL_OVER <= (code) && (code) <= KB_UNDEFINED)
 | 
			
		||||
#define IS_ANY(code)             (KB_A         <= (code))
 | 
			
		||||
#define IS_ANY(code)             (KB_A         <= (code) && (code) <= 0xFF)
 | 
			
		||||
#define IS_KEY(code)             (KB_A         <= (code) && (code) <= KB_EXSEL)
 | 
			
		||||
#define IS_MOD(code)             (KB_LCTRL     <= (code) && (code) <= KB_RGUI)
 | 
			
		||||
 | 
			
		||||
#define IS_FN(code)              (KB_FN0       <= (code) && (code) <= KB_FN7)
 | 
			
		||||
#define IS_MOUSEKEY(code)        (KB_MS_UP     <= (code) && (code) <= KB_MS_WH_RIGHT)
 | 
			
		||||
#define IS_MOUSEKEY_MOVE(code)   (KB_MS_UP     <= (code) && (code) <= KB_MS_RIGHT)
 | 
			
		||||
#define IS_MOUSEKEY_BUTTON(code) (KB_MS_BTN1   <= (code) && (code) <= KB_MS_BTN5)
 | 
			
		||||
#define IS_MOUSEKEY_WHEEL(code)  (KB_MS_WH_UP  <= (code) && (code) <= KB_MS_WH_RIGHT)
 | 
			
		||||
 | 
			
		||||
#define IS_SPECIAL(code)         ((0xB0 <= (code) && (code) <= 0xDF) || (0xE8 <= (code) && (code) <= 0xFF))
 | 
			
		||||
#define IS_CONSUMER(code)        (KB_MUTE      <= (code) && (code) <= KB_WFAV)
 | 
			
		||||
#define IS_SYSTEM(code)          (KB_POWER     <= (code) && (code) <= KB_WAKE)
 | 
			
		||||
 | 
			
		||||
#define MOD_BIT(code)   (1<<((code) & 0x07))
 | 
			
		||||
#define FN_BIT(code)    (1<<((code) - KB_FN0))
 | 
			
		||||
#define FN_INDEX(code)  ((code) - KB_FN0)
 | 
			
		||||
| 
						 | 
				
			
			@ -137,14 +142,16 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
/* Special keycode */
 | 
			
		||||
/* NOTE: 0xA5-DF and 0xE8-FF can be used for internal special purpose */
 | 
			
		||||
enum special_keycodes {
 | 
			
		||||
    /* System Control */
 | 
			
		||||
    KB_SYSTEM_POWER = 0xB0,
 | 
			
		||||
    KB_SYSTEM_POWER = 0xA5,
 | 
			
		||||
    KB_SYSTEM_SLEEP,
 | 
			
		||||
    KB_SYSTEM_WAKE,
 | 
			
		||||
    KB_SYSTEM_WAKE,     /* 0xA7 */
 | 
			
		||||
                        /* 0xA8-AF */
 | 
			
		||||
 | 
			
		||||
    /* Consumer Page */
 | 
			
		||||
    KB_AUDIO_MUTE,
 | 
			
		||||
    KB_AUDIO_MUTE = 0xB0,
 | 
			
		||||
    KB_AUDIO_VOL_UP,
 | 
			
		||||
    KB_AUDIO_VOL_DOWN,
 | 
			
		||||
    KB_MEDIA_NEXT_TRACK,
 | 
			
		||||
| 
						 | 
				
			
			@ -157,13 +164,14 @@ enum special_keycodes {
 | 
			
		|||
    KB_MY_COMPUTER,
 | 
			
		||||
    KB_WWW_SEARCH,
 | 
			
		||||
    KB_WWW_HOME,
 | 
			
		||||
    KB_WWW_BACK,        /* 0xC0 */
 | 
			
		||||
    KB_WWW_BACK,
 | 
			
		||||
    KB_WWW_FORWARD,
 | 
			
		||||
    KB_WWW_STOP,
 | 
			
		||||
    KB_WWW_REFRESH,
 | 
			
		||||
    KB_WWW_FAVORITES,
 | 
			
		||||
    KB_WWW_REFRESH,     /* 0xC0 */
 | 
			
		||||
    KB_WWW_FAVORITES,   /* 0xC1 */
 | 
			
		||||
                        /* 0xC2-DF vacant for future use */
 | 
			
		||||
 | 
			
		||||
    /* reserve 0xE0-E7 for Modifiers */
 | 
			
		||||
    /* 0xE0-E7 for Modifiers. DO NOT USE. */
 | 
			
		||||
 | 
			
		||||
    /* Layer Switching */
 | 
			
		||||
    KB_FN0 = 0xE8,
 | 
			
		||||
| 
						 | 
				
			
			@ -173,7 +181,7 @@ enum special_keycodes {
 | 
			
		|||
    KB_FN4,
 | 
			
		||||
    KB_FN5,
 | 
			
		||||
    KB_FN6,
 | 
			
		||||
    KB_FN7,
 | 
			
		||||
    KB_FN7,             /* 0xEF */
 | 
			
		||||
 | 
			
		||||
    /* Mousekey */
 | 
			
		||||
    KB_MS_UP = 0xF0,
 | 
			
		||||
| 
						 | 
				
			
			@ -189,11 +197,13 @@ enum special_keycodes {
 | 
			
		|||
    KB_MS_WH_UP,
 | 
			
		||||
    KB_MS_WH_DOWN,
 | 
			
		||||
    KB_MS_WH_LEFT,
 | 
			
		||||
    KB_MS_WH_RIGHT,
 | 
			
		||||
    KB_MS_WH_RIGHT,     /* 0xFC */
 | 
			
		||||
                        /* 0xFD-FF vacant for future use */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* USB HID Keyboard/Keypad Usage(0x07) */
 | 
			
		||||
enum keycodes {
 | 
			
		||||
    KB_NO = 0,
 | 
			
		||||
    KB_NO               = 0x00,
 | 
			
		||||
    KB_ROLL_OVER,
 | 
			
		||||
    KB_POST_FAIL,
 | 
			
		||||
    KB_UNDEFINED,
 | 
			
		||||
| 
						 | 
				
			
			@ -357,9 +367,10 @@ enum keycodes {
 | 
			
		|||
    KB_OPER,
 | 
			
		||||
    KB_CLEAR_AGAIN,
 | 
			
		||||
    KB_CRSEL,
 | 
			
		||||
    KB_EXSEL,
 | 
			
		||||
    KB_EXSEL,           /* 0xA4 */
 | 
			
		||||
 | 
			
		||||
    /* NOTE: 0xA5-DF are used for internal special purpose */
 | 
			
		||||
 | 
			
		||||
    /* NOTE: 0xB0-DF are used as special_keycodes */
 | 
			
		||||
#if 0
 | 
			
		||||
    KB_KP_00 = 0xB0,
 | 
			
		||||
    KB_KP_000,
 | 
			
		||||
| 
						 | 
				
			
			@ -406,7 +417,7 @@ enum keycodes {
 | 
			
		|||
    KB_KP_BINARY,
 | 
			
		||||
    KB_KP_OCTAL,
 | 
			
		||||
    KB_KP_DECIMAL,
 | 
			
		||||
    KB_KP_HEXADECIMAL,
 | 
			
		||||
    KB_KP_HEXADECIMAL,  /* 0xDD */
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    /* Modifiers */
 | 
			
		||||
| 
						 | 
				
			
			@ -419,7 +430,7 @@ enum keycodes {
 | 
			
		|||
    KB_RALT,
 | 
			
		||||
    KB_RGUI,
 | 
			
		||||
 | 
			
		||||
    /* NOTE: 0xE8-FF are used as special_keycodes */
 | 
			
		||||
    /* NOTE: 0xE8-FF are used for internal special purpose */ 
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif /* USB_KEYCODES_H */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue