Fix mod stuck of MODS_KEY when leaving layer #62
- Add action_util.c and remove action_oneshot.c - Add oneshot_mods for MODS_ONESHOT - Add weak_mods for MODS_KEY and MACRO - weak_mods is cleared when layer switching
This commit is contained in:
		
							parent
							
								
									cb434cfebc
								
							
						
					
					
						commit
						d52d554360
					
				
					 12 changed files with 330 additions and 323 deletions
				
			
		
							
								
								
									
										138
									
								
								common/action.c
									
										
									
									
									
								
							
							
						
						
									
										138
									
								
								common/action.c
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -23,8 +23,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		|||
#include "backlight.h"
 | 
			
		||||
#include "action_layer.h"
 | 
			
		||||
#include "action_tapping.h"
 | 
			
		||||
#include "action_oneshot.h"
 | 
			
		||||
#include "action_macro.h"
 | 
			
		||||
#include "action_util.h"
 | 
			
		||||
#include "action.h"
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG_ACTION
 | 
			
		||||
| 
						 | 
				
			
			@ -79,15 +79,15 @@ void process_action(keyrecord_t *record)
 | 
			
		|||
                                                                action.key.mods<<4;
 | 
			
		||||
                if (event.pressed) {
 | 
			
		||||
                    if (mods) {
 | 
			
		||||
                        host_add_mods(mods);
 | 
			
		||||
                        host_send_keyboard_report();
 | 
			
		||||
                        add_weak_mods(mods);
 | 
			
		||||
                        send_keyboard_report();
 | 
			
		||||
                    }
 | 
			
		||||
                    register_code(action.key.code);
 | 
			
		||||
                } else {
 | 
			
		||||
                    unregister_code(action.key.code);
 | 
			
		||||
                    if (mods) {
 | 
			
		||||
                        host_del_mods(mods);
 | 
			
		||||
                        host_send_keyboard_report();
 | 
			
		||||
                        del_weak_mods(mods);
 | 
			
		||||
                        send_keyboard_report();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -105,11 +105,11 @@ void process_action(keyrecord_t *record)
 | 
			
		|||
                        if (event.pressed) {
 | 
			
		||||
                            if (tap_count == 0) {
 | 
			
		||||
                                dprint("MODS_TAP: Oneshot: add_mods\n");
 | 
			
		||||
                                add_mods(mods);
 | 
			
		||||
                                register_mods(mods);
 | 
			
		||||
                            }
 | 
			
		||||
                            else if (tap_count == 1) {
 | 
			
		||||
                                dprint("MODS_TAP: Oneshot: start\n");
 | 
			
		||||
                                oneshot_start(mods);
 | 
			
		||||
                                set_oneshot_mods(mods);
 | 
			
		||||
                            }
 | 
			
		||||
                            else if (tap_count == TAPPING_TOGGLE) {
 | 
			
		||||
                                dprint("MODS_TAP: Oneshot: toggle\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -118,25 +118,23 @@ void process_action(keyrecord_t *record)
 | 
			
		|||
                            else {
 | 
			
		||||
                                dprint("MODS_TAP: Oneshot: cancel&add_mods\n");
 | 
			
		||||
                                // double tap cancels oneshot and works as normal modifier.
 | 
			
		||||
                                oneshot_cancel();
 | 
			
		||||
                                add_mods(mods);
 | 
			
		||||
                                clear_oneshot_mods();
 | 
			
		||||
                                register_mods(mods);
 | 
			
		||||
                            }
 | 
			
		||||
                        } else {
 | 
			
		||||
                            if (tap_count == 0) {
 | 
			
		||||
                                dprint("MODS_TAP: Oneshot: cancel/del_mods\n");
 | 
			
		||||
                                // cancel oneshot on hold
 | 
			
		||||
                                oneshot_cancel();
 | 
			
		||||
                                del_mods(mods);
 | 
			
		||||
                                clear_oneshot_mods();
 | 
			
		||||
                                unregister_mods(mods);
 | 
			
		||||
                            }
 | 
			
		||||
                            else if (tap_count == 1) {
 | 
			
		||||
                                dprint("MODS_TAP: Oneshot: del_mods\n");
 | 
			
		||||
                                // retain Oneshot
 | 
			
		||||
                                del_mods(mods);
 | 
			
		||||
                                // Oneshot
 | 
			
		||||
                            }
 | 
			
		||||
                            else {
 | 
			
		||||
                                dprint("MODS_TAP: Oneshot: del_mods\n");
 | 
			
		||||
                                // cancel Mods
 | 
			
		||||
                                del_mods(mods);
 | 
			
		||||
                                unregister_mods(mods);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
| 
						 | 
				
			
			@ -148,14 +146,14 @@ void process_action(keyrecord_t *record)
 | 
			
		|||
                                    dprint("MODS_TAP: Tap: Cancel: add_mods\n");
 | 
			
		||||
                                    // ad hoc: set 0 to cancel tap
 | 
			
		||||
                                    record->tap.count = 0;
 | 
			
		||||
                                    add_mods(mods);
 | 
			
		||||
                                    register_mods(mods);
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    dprint("MODS_TAP: Tap: register_code\n");
 | 
			
		||||
                                    register_code(action.key.code);
 | 
			
		||||
                                }
 | 
			
		||||
                            } else {
 | 
			
		||||
                                dprint("MODS_TAP: No tap: add_mods\n");
 | 
			
		||||
                                add_mods(mods);
 | 
			
		||||
                                register_mods(mods);
 | 
			
		||||
                            }
 | 
			
		||||
                        } else {
 | 
			
		||||
                            if (tap_count > 0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -163,7 +161,7 @@ void process_action(keyrecord_t *record)
 | 
			
		|||
                                unregister_code(action.key.code);
 | 
			
		||||
                            } else {
 | 
			
		||||
                                dprint("MODS_TAP: No tap: add_mods\n");
 | 
			
		||||
                                del_mods(mods);
 | 
			
		||||
                                unregister_mods(mods);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
| 
						 | 
				
			
			@ -343,30 +341,30 @@ void register_code(uint8_t code)
 | 
			
		|||
        // Resync: ignore if caps lock already is on
 | 
			
		||||
        if (host_keyboard_leds() & (1<<USB_LED_CAPS_LOCK)) return;
 | 
			
		||||
#endif
 | 
			
		||||
        host_add_key(KC_CAPSLOCK);
 | 
			
		||||
        host_send_keyboard_report();
 | 
			
		||||
        host_del_key(KC_CAPSLOCK);
 | 
			
		||||
        host_send_keyboard_report();
 | 
			
		||||
        add_key(KC_CAPSLOCK);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
        del_key(KC_CAPSLOCK);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    else if (KC_LOCKING_NUM == code) {
 | 
			
		||||
#ifdef LOCKING_RESYNC_ENABLE
 | 
			
		||||
        if (host_keyboard_leds() & (1<<USB_LED_NUM_LOCK)) return;
 | 
			
		||||
#endif
 | 
			
		||||
        host_add_key(KC_NUMLOCK);
 | 
			
		||||
        host_send_keyboard_report();
 | 
			
		||||
        host_del_key(KC_NUMLOCK);
 | 
			
		||||
        host_send_keyboard_report();
 | 
			
		||||
        add_key(KC_NUMLOCK);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
        del_key(KC_NUMLOCK);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    else if (KC_LOCKING_SCROLL == code) {
 | 
			
		||||
#ifdef LOCKING_RESYNC_ENABLE
 | 
			
		||||
        if (host_keyboard_leds() & (1<<USB_LED_SCROLL_LOCK)) return;
 | 
			
		||||
#endif
 | 
			
		||||
        host_add_key(KC_SCROLLLOCK);
 | 
			
		||||
        host_send_keyboard_report();
 | 
			
		||||
        host_del_key(KC_SCROLLLOCK);
 | 
			
		||||
        host_send_keyboard_report();
 | 
			
		||||
        add_key(KC_SCROLLLOCK);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
        del_key(KC_SCROLLLOCK);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -375,25 +373,28 @@ void register_code(uint8_t code)
 | 
			
		|||
        if (command_proc(code)) return;
 | 
			
		||||
 | 
			
		||||
#ifndef NO_ACTION_ONESHOT
 | 
			
		||||
/* TODO: remove
 | 
			
		||||
        if (oneshot_state.mods && !oneshot_state.disabled) {
 | 
			
		||||
            uint8_t tmp_mods = host_get_mods();
 | 
			
		||||
            host_add_mods(oneshot_state.mods);
 | 
			
		||||
            uint8_t tmp_mods = get_mods();
 | 
			
		||||
            add_mods(oneshot_state.mods);
 | 
			
		||||
 | 
			
		||||
            host_add_key(code);
 | 
			
		||||
            host_send_keyboard_report();
 | 
			
		||||
            add_key(code);
 | 
			
		||||
            send_keyboard_report();
 | 
			
		||||
 | 
			
		||||
            host_set_mods(tmp_mods);
 | 
			
		||||
            set_mods(tmp_mods);
 | 
			
		||||
            send_keyboard_report();
 | 
			
		||||
            oneshot_cancel();
 | 
			
		||||
        } else 
 | 
			
		||||
*/
 | 
			
		||||
#endif
 | 
			
		||||
        {
 | 
			
		||||
            host_add_key(code);
 | 
			
		||||
            host_send_keyboard_report();
 | 
			
		||||
            add_key(code);
 | 
			
		||||
            send_keyboard_report();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else if IS_MOD(code) {
 | 
			
		||||
        host_add_mods(MOD_BIT(code));
 | 
			
		||||
        host_send_keyboard_report();
 | 
			
		||||
        add_mods(MOD_BIT(code));
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
    }
 | 
			
		||||
    else if IS_SYSTEM(code) {
 | 
			
		||||
        host_system_send(KEYCODE2SYSTEM(code));
 | 
			
		||||
| 
						 | 
				
			
			@ -415,40 +416,40 @@ void unregister_code(uint8_t code)
 | 
			
		|||
        // Resync: ignore if caps lock already is off
 | 
			
		||||
        if (!(host_keyboard_leds() & (1<<USB_LED_CAPS_LOCK))) return;
 | 
			
		||||
#endif
 | 
			
		||||
        host_add_key(KC_CAPSLOCK);
 | 
			
		||||
        host_send_keyboard_report();
 | 
			
		||||
        host_del_key(KC_CAPSLOCK);
 | 
			
		||||
        host_send_keyboard_report();
 | 
			
		||||
        add_key(KC_CAPSLOCK);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
        del_key(KC_CAPSLOCK);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    else if (KC_LOCKING_NUM == code) {
 | 
			
		||||
#ifdef LOCKING_RESYNC_ENABLE
 | 
			
		||||
        if (!(host_keyboard_leds() & (1<<USB_LED_NUM_LOCK))) return;
 | 
			
		||||
#endif
 | 
			
		||||
        host_add_key(KC_NUMLOCK);
 | 
			
		||||
        host_send_keyboard_report();
 | 
			
		||||
        host_del_key(KC_NUMLOCK);
 | 
			
		||||
        host_send_keyboard_report();
 | 
			
		||||
        add_key(KC_NUMLOCK);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
        del_key(KC_NUMLOCK);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    else if (KC_LOCKING_SCROLL == code) {
 | 
			
		||||
#ifdef LOCKING_RESYNC_ENABLE
 | 
			
		||||
        if (!(host_keyboard_leds() & (1<<USB_LED_SCROLL_LOCK))) return;
 | 
			
		||||
#endif
 | 
			
		||||
        host_add_key(KC_SCROLLLOCK);
 | 
			
		||||
        host_send_keyboard_report();
 | 
			
		||||
        host_del_key(KC_SCROLLLOCK);
 | 
			
		||||
        host_send_keyboard_report();
 | 
			
		||||
        add_key(KC_SCROLLLOCK);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
        del_key(KC_SCROLLLOCK);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    else if IS_KEY(code) {
 | 
			
		||||
        host_del_key(code);
 | 
			
		||||
        host_send_keyboard_report();
 | 
			
		||||
        del_key(code);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
    }
 | 
			
		||||
    else if IS_MOD(code) {
 | 
			
		||||
        host_del_mods(MOD_BIT(code));
 | 
			
		||||
        host_send_keyboard_report();
 | 
			
		||||
        del_mods(MOD_BIT(code));
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
    }
 | 
			
		||||
    else if IS_SYSTEM(code) {
 | 
			
		||||
        host_system_send(0);
 | 
			
		||||
| 
						 | 
				
			
			@ -458,38 +459,33 @@ void unregister_code(uint8_t code)
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void add_mods(uint8_t mods)
 | 
			
		||||
void register_mods(uint8_t mods)
 | 
			
		||||
{
 | 
			
		||||
    if (mods) {
 | 
			
		||||
        host_add_mods(mods);
 | 
			
		||||
        host_send_keyboard_report();
 | 
			
		||||
        add_mods(mods);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void del_mods(uint8_t mods)
 | 
			
		||||
void unregister_mods(uint8_t mods)
 | 
			
		||||
{
 | 
			
		||||
    if (mods) {
 | 
			
		||||
        host_del_mods(mods);
 | 
			
		||||
        host_send_keyboard_report();
 | 
			
		||||
        del_mods(mods);
 | 
			
		||||
        send_keyboard_report();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void set_mods(uint8_t mods)
 | 
			
		||||
{
 | 
			
		||||
    host_set_mods(mods);
 | 
			
		||||
    host_send_keyboard_report();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void clear_keyboard(void)
 | 
			
		||||
{
 | 
			
		||||
    host_clear_mods();
 | 
			
		||||
    clear_mods();
 | 
			
		||||
    clear_keyboard_but_mods();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void clear_keyboard_but_mods(void)
 | 
			
		||||
{
 | 
			
		||||
    host_clear_keys();
 | 
			
		||||
    host_send_keyboard_report();
 | 
			
		||||
    clear_weak_mods();
 | 
			
		||||
    clear_keys();
 | 
			
		||||
    send_keyboard_report();
 | 
			
		||||
#ifdef MOUSEKEY_ENABLE
 | 
			
		||||
    mousekey_clear();
 | 
			
		||||
    mousekey_send();
 | 
			
		||||
| 
						 | 
				
			
			@ -502,7 +498,7 @@ void clear_keyboard_but_mods(void)
 | 
			
		|||
 | 
			
		||||
bool sending_anykey(void)
 | 
			
		||||
{
 | 
			
		||||
    return (host_has_anykey() || host_mouse_in_use() ||
 | 
			
		||||
    return (has_anykey() || host_mouse_in_use() ||
 | 
			
		||||
            host_last_sysytem_report() || host_last_consumer_report());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -59,9 +59,9 @@ void action_function(keyrecord_t *record, uint8_t id, uint8_t opt);
 | 
			
		|||
void process_action(keyrecord_t *record);
 | 
			
		||||
void register_code(uint8_t code);
 | 
			
		||||
void unregister_code(uint8_t code);
 | 
			
		||||
void add_mods(uint8_t mods);
 | 
			
		||||
void del_mods(uint8_t mods);
 | 
			
		||||
void set_mods(uint8_t mods);
 | 
			
		||||
void register_mods(uint8_t mods);
 | 
			
		||||
void unregister_mods(uint8_t mods);
 | 
			
		||||
//void set_mods(uint8_t mods);
 | 
			
		||||
void clear_keyboard(void);
 | 
			
		||||
void clear_keyboard_but_mods(void);
 | 
			
		||||
bool sending_anykey(void);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,13 +29,13 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		|||
 * 000r|0000|0000 0001    Transparent code
 | 
			
		||||
 * 000r|0000| keycode     Key
 | 
			
		||||
 * 000r|mods|0000 0000    Modifiers
 | 
			
		||||
 * 000r|mods| keycode     Key and Modifiers
 | 
			
		||||
 * 000r|mods| keycode     Modifiers+Key(Modified key)
 | 
			
		||||
 *   r: Left/Right flag(Left:0, Right:1)
 | 
			
		||||
 *
 | 
			
		||||
 * ACT_MODS_TAP(001r):
 | 
			
		||||
 * 001r|mods|0000 0000    Modifiers with OneShot
 | 
			
		||||
 * 001r|mods|0000 00xx    (reserved)
 | 
			
		||||
 * 001r|mods| keycode     Modifiers with Tap Key
 | 
			
		||||
 * 001r|mods| keycode     Modifiers with Tap Key(Dual role)
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 * Other Keys(01xx)
 | 
			
		||||
| 
						 | 
				
			
			@ -69,7 +69,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		|||
 * 1001|oopp|BBBB BBBB   8-bit Bitwise Operation???
 | 
			
		||||
 *
 | 
			
		||||
 * ACT_LAYER_TAP(101x):
 | 
			
		||||
 * 101E|LLLL| keycode    Invert with tap key
 | 
			
		||||
 * 101E|LLLL| keycode    On/Off with tap key
 | 
			
		||||
 * 101E|LLLL|1110 xxxx   Reserved(0xE0-EF)
 | 
			
		||||
 * 101E|LLLL|1111 0000   Invert with tap toggle(0xF0)
 | 
			
		||||
 * 101E|LLLL|1111 0001   On/Off
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,21 +0,0 @@
 | 
			
		|||
#include "action_oneshot.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifndef NO_ACTION_ONESHOT
 | 
			
		||||
oneshot_state_t oneshot_state;
 | 
			
		||||
 | 
			
		||||
void oneshot_start(uint8_t mods)
 | 
			
		||||
{
 | 
			
		||||
    oneshot_state.mods = mods;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void oneshot_cancel(void)
 | 
			
		||||
{
 | 
			
		||||
    oneshot_state.mods = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void oneshot_toggle(void)
 | 
			
		||||
{
 | 
			
		||||
    oneshot_state.disabled = !oneshot_state.disabled;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -1,52 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2013 Jun Wako <wakojun@gmail.com>
 | 
			
		||||
 | 
			
		||||
This program 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.
 | 
			
		||||
 | 
			
		||||
This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
#ifndef ACTION_ONESHOT_H
 | 
			
		||||
#define ACTION_ONESHOT_H
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
#ifdef NO_ACTION_TAPPING
 | 
			
		||||
    #define NO_ACTION_ONESHOT
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef NO_ACTION_ONESHOT
 | 
			
		||||
/* Oneshot modifier
 | 
			
		||||
 *
 | 
			
		||||
 * Problem: Want to capitalize like 'The' but the result tends to be 'THe'.
 | 
			
		||||
 * Solution: Oneshot modifier have its effect on only one key coming next.
 | 
			
		||||
 *           Tap Shift, then type 't', 'h' and 'e'. Not need to hold Shift key.
 | 
			
		||||
 *
 | 
			
		||||
 *  Hold:       works as normal modifier.
 | 
			
		||||
 *  Tap:        one shot modifier.
 | 
			
		||||
 *  2 Tap:      cancel one shot modifier.
 | 
			
		||||
 *  5-Tap:      toggles enable/disable oneshot feature.
 | 
			
		||||
 */
 | 
			
		||||
typedef struct {
 | 
			
		||||
    uint8_t mods;
 | 
			
		||||
    bool    disabled;
 | 
			
		||||
}   oneshot_state_t;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
oneshot_state_t oneshot_state;
 | 
			
		||||
 | 
			
		||||
void oneshot_start(uint8_t mods);
 | 
			
		||||
void oneshot_cancel(void);
 | 
			
		||||
void oneshot_toggle(void);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										192
									
								
								common/action_util.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								common/action_util.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,192 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2013 Jun Wako <wakojun@gmail.com>
 | 
			
		||||
 | 
			
		||||
This program 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.
 | 
			
		||||
 | 
			
		||||
This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
#include "host.h"
 | 
			
		||||
#include "report.h"
 | 
			
		||||
#include "debug.h"
 | 
			
		||||
#include "action_util.h"
 | 
			
		||||
 | 
			
		||||
static inline void add_key_byte(uint8_t code);
 | 
			
		||||
static inline void del_key_byte(uint8_t code);
 | 
			
		||||
#ifdef NKRO_ENABLE
 | 
			
		||||
static inline void add_key_bit(uint8_t code);
 | 
			
		||||
static inline void del_key_bit(uint8_t code);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static uint8_t real_mods = 0;
 | 
			
		||||
static uint8_t weak_mods = 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// TODO: pointer variable is not needed
 | 
			
		||||
//report_keyboard_t keyboard_report = {};
 | 
			
		||||
report_keyboard_t *keyboard_report = &(report_keyboard_t){};
 | 
			
		||||
 | 
			
		||||
#ifndef NO_ACTION_ONESHOT
 | 
			
		||||
static bool oneshot_enabled = true;
 | 
			
		||||
static int8_t oneshot_mods = 0;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void send_keyboard_report(void) {
 | 
			
		||||
    keyboard_report->mods  = real_mods;
 | 
			
		||||
    keyboard_report->mods |= weak_mods;
 | 
			
		||||
#ifndef NO_ACTION_ONESHOT
 | 
			
		||||
    keyboard_report->mods |= oneshot_mods;
 | 
			
		||||
    if (has_anykey()) {
 | 
			
		||||
        clear_oneshot_mods();
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    host_keyboard_send(keyboard_report);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* key */
 | 
			
		||||
void add_key(uint8_t key)
 | 
			
		||||
{
 | 
			
		||||
#ifdef NKRO_ENABLE
 | 
			
		||||
    if (keyboard_nkro) {
 | 
			
		||||
        add_key_bit(key);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    add_key_byte(key);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void del_key(uint8_t key)
 | 
			
		||||
{
 | 
			
		||||
#ifdef NKRO_ENABLE
 | 
			
		||||
    if (keyboard_nkro) {
 | 
			
		||||
        del_key_bit(key);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    del_key_byte(key);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void clear_keys(void)
 | 
			
		||||
{
 | 
			
		||||
    // not clear mods
 | 
			
		||||
    for (int8_t i = 1; i < REPORT_SIZE; i++) {
 | 
			
		||||
        keyboard_report->raw[i] = 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* modifier */
 | 
			
		||||
uint8_t get_mods(void) { return real_mods; }
 | 
			
		||||
void add_mods(uint8_t mods) { real_mods |= mods; }
 | 
			
		||||
void del_mods(uint8_t mods) { real_mods &= ~mods; }
 | 
			
		||||
void set_mods(uint8_t mods) { real_mods = mods; }
 | 
			
		||||
void clear_mods(void) { real_mods = 0; }
 | 
			
		||||
 | 
			
		||||
/* weak modifier */
 | 
			
		||||
uint8_t get_weak_mods(void) { return weak_mods; }
 | 
			
		||||
void add_weak_mods(uint8_t mods) { weak_mods |= mods; }
 | 
			
		||||
void del_weak_mods(uint8_t mods) { weak_mods &= ~mods; }
 | 
			
		||||
void set_weak_mods(uint8_t mods) { weak_mods = mods; }
 | 
			
		||||
void clear_weak_mods(void) { weak_mods = 0; }
 | 
			
		||||
 | 
			
		||||
/* Oneshot modifier */
 | 
			
		||||
#ifndef NO_ACTION_ONESHOT
 | 
			
		||||
void set_oneshot_mods(uint8_t mods) { oneshot_mods = mods; }
 | 
			
		||||
void clear_oneshot_mods(void) { oneshot_mods = 0; }
 | 
			
		||||
void oneshot_toggle(void) { oneshot_enabled = !oneshot_enabled; }
 | 
			
		||||
void oneshot_enable(void) { oneshot_enabled = true; }
 | 
			
		||||
void oneshot_disable(void) { oneshot_enabled = false; }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * inspect keyboard state
 | 
			
		||||
 */
 | 
			
		||||
uint8_t has_anykey(void)
 | 
			
		||||
{
 | 
			
		||||
    uint8_t cnt = 0;
 | 
			
		||||
    for (uint8_t i = 1; i < REPORT_SIZE; i++) {
 | 
			
		||||
        if (keyboard_report->raw[i])
 | 
			
		||||
            cnt++;
 | 
			
		||||
    }
 | 
			
		||||
    return cnt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t has_anymod(void)
 | 
			
		||||
{
 | 
			
		||||
    return bitpop(real_mods);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t get_first_key(void)
 | 
			
		||||
{
 | 
			
		||||
#ifdef NKRO_ENABLE
 | 
			
		||||
    if (keyboard_nkro) {
 | 
			
		||||
        uint8_t i = 0;
 | 
			
		||||
        for (; i < REPORT_BITS && !keyboard_report->nkro.bits[i]; i++)
 | 
			
		||||
            ;
 | 
			
		||||
        return i<<3 | biton(keyboard_report->nkro.bits[i]);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    return keyboard_report->keys[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* local functions */
 | 
			
		||||
static inline void add_key_byte(uint8_t code)
 | 
			
		||||
{
 | 
			
		||||
    int8_t i = 0;
 | 
			
		||||
    int8_t empty = -1;
 | 
			
		||||
    for (; i < REPORT_KEYS; i++) {
 | 
			
		||||
        if (keyboard_report->keys[i] == code) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        if (empty == -1 && keyboard_report->keys[i] == 0) {
 | 
			
		||||
            empty = i;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (i == REPORT_KEYS) {
 | 
			
		||||
        if (empty != -1) {
 | 
			
		||||
            keyboard_report->keys[empty] = code;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void del_key_byte(uint8_t code)
 | 
			
		||||
{
 | 
			
		||||
    for (uint8_t i = 0; i < REPORT_KEYS; i++) {
 | 
			
		||||
        if (keyboard_report->keys[i] == code) {
 | 
			
		||||
            keyboard_report->keys[i] = 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef NKRO_ENABLE
 | 
			
		||||
static inline void add_key_bit(uint8_t code)
 | 
			
		||||
{
 | 
			
		||||
    if ((code>>3) < REPORT_BITS) {
 | 
			
		||||
        keyboard_report->nkro.bits[code>>3] |= 1<<(code&7);
 | 
			
		||||
    } else {
 | 
			
		||||
        dprintf("add_key_bit: can't add: %02X\n", code);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void del_key_bit(uint8_t code)
 | 
			
		||||
{
 | 
			
		||||
    if ((code>>3) < REPORT_BITS) {
 | 
			
		||||
        keyboard_report->nkro.bits[code>>3] &= ~(1<<(code&7));
 | 
			
		||||
    } else {
 | 
			
		||||
        dprintf("del_key_bit: can't del: %02X\n", code);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										56
									
								
								common/action_util.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								common/action_util.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,56 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2013 Jun Wako <wakojun@gmail.com>
 | 
			
		||||
 | 
			
		||||
This program 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.
 | 
			
		||||
 | 
			
		||||
This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
#ifndef ACTION_UTIL_H
 | 
			
		||||
#define ACTION_UTIL_H
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
extern report_keyboard_t *keyboard_report;
 | 
			
		||||
 | 
			
		||||
void send_keyboard_report(void);
 | 
			
		||||
 | 
			
		||||
/* key */
 | 
			
		||||
void add_key(uint8_t key);
 | 
			
		||||
void del_key(uint8_t key);
 | 
			
		||||
void clear_keys(void);
 | 
			
		||||
 | 
			
		||||
/* modifier */
 | 
			
		||||
uint8_t get_mods(void);
 | 
			
		||||
void add_mods(uint8_t mods);
 | 
			
		||||
void del_mods(uint8_t mods);
 | 
			
		||||
void set_mods(uint8_t mods);
 | 
			
		||||
void clear_mods(void);
 | 
			
		||||
 | 
			
		||||
/* weak modifier */
 | 
			
		||||
uint8_t get_weak_mods(void);
 | 
			
		||||
void add_weak_mods(uint8_t mods);
 | 
			
		||||
void del_weak_mods(uint8_t mods);
 | 
			
		||||
void set_weak_mods(uint8_t mods);
 | 
			
		||||
void clear_weak_mods(void);
 | 
			
		||||
 | 
			
		||||
/* oneshot modifier */
 | 
			
		||||
void set_oneshot_mods(uint8_t mods);
 | 
			
		||||
void clear_oneshot_mods(void);
 | 
			
		||||
void oneshot_toggle(void);
 | 
			
		||||
void oneshot_enable(void);
 | 
			
		||||
void oneshot_disable(void);
 | 
			
		||||
 | 
			
		||||
/* inspect */
 | 
			
		||||
uint8_t has_anykey(void);
 | 
			
		||||
uint8_t has_anymod(void);
 | 
			
		||||
uint8_t get_first_key(void);
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -27,6 +27,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		|||
#include "keyboard.h"
 | 
			
		||||
#include "bootloader.h"
 | 
			
		||||
#include "action_layer.h"
 | 
			
		||||
#include "action_util.h"
 | 
			
		||||
#include "eeconfig.h"
 | 
			
		||||
#include "sleep_led.h"
 | 
			
		||||
#include "led.h"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										150
									
								
								common/host.c
									
										
									
									
									
								
							
							
						
						
									
										150
									
								
								common/host.c
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -27,7 +27,6 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		|||
bool keyboard_nkro = false;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
report_keyboard_t *keyboard_report = &(report_keyboard_t){};
 | 
			
		||||
report_mouse_t mouse_report = {};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -35,13 +34,6 @@ static host_driver_t *driver;
 | 
			
		|||
static uint16_t last_system_report = 0;
 | 
			
		||||
static uint16_t last_consumer_report = 0;
 | 
			
		||||
 | 
			
		||||
static inline void add_key_byte(uint8_t code);
 | 
			
		||||
static inline void del_key_byte(uint8_t code);
 | 
			
		||||
#ifdef NKRO_ENABLE
 | 
			
		||||
static inline void add_key_bit(uint8_t code);
 | 
			
		||||
static inline void del_key_bit(uint8_t code);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void host_set_driver(host_driver_t *d)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -67,7 +59,7 @@ void host_keyboard_send(report_keyboard_t *report)
 | 
			
		|||
    if (debug_keyboard) {
 | 
			
		||||
        dprint("keyboard_report: ");
 | 
			
		||||
        for (uint8_t i = 0; i < REPORT_SIZE; i++) {
 | 
			
		||||
            dprintf("%02X ", keyboard_report->raw[i]);
 | 
			
		||||
            dprintf("%02X ", report->raw[i]);
 | 
			
		||||
        }
 | 
			
		||||
        dprint("\n");
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -97,98 +89,6 @@ void host_consumer_send(uint16_t report)
 | 
			
		|||
    (*driver->send_consumer)(report);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* keyboard report utils */
 | 
			
		||||
void host_add_key(uint8_t key)
 | 
			
		||||
{
 | 
			
		||||
#ifdef NKRO_ENABLE
 | 
			
		||||
    if (keyboard_nkro) {
 | 
			
		||||
        add_key_bit(key);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    add_key_byte(key);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void host_del_key(uint8_t key)
 | 
			
		||||
{
 | 
			
		||||
#ifdef NKRO_ENABLE
 | 
			
		||||
    if (keyboard_nkro) {
 | 
			
		||||
        del_key_bit(key);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    del_key_byte(key);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void host_clear_keys(void)
 | 
			
		||||
{
 | 
			
		||||
    // not clea  mods
 | 
			
		||||
    for (int8_t i = 1; i < REPORT_SIZE; i++) {
 | 
			
		||||
        keyboard_report->raw[i] = 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t host_get_mods(void)
 | 
			
		||||
{
 | 
			
		||||
    return keyboard_report->mods;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void host_add_mods(uint8_t mods)
 | 
			
		||||
{
 | 
			
		||||
    keyboard_report->mods |= mods;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void host_del_mods(uint8_t mods)
 | 
			
		||||
{
 | 
			
		||||
    keyboard_report->mods &= ~mods;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void host_set_mods(uint8_t mods)
 | 
			
		||||
{
 | 
			
		||||
    keyboard_report->mods = mods;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void host_clear_mods(void)
 | 
			
		||||
{
 | 
			
		||||
    keyboard_report->mods = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t host_has_anykey(void)
 | 
			
		||||
{
 | 
			
		||||
    uint8_t cnt = 0;
 | 
			
		||||
    for (uint8_t i = 1; i < REPORT_SIZE; i++) {
 | 
			
		||||
        if (keyboard_report->raw[i])
 | 
			
		||||
            cnt++;
 | 
			
		||||
    }
 | 
			
		||||
    return cnt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t host_has_anymod(void)
 | 
			
		||||
{
 | 
			
		||||
    return bitpop(keyboard_report->mods);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t host_get_first_key(void)
 | 
			
		||||
{
 | 
			
		||||
#ifdef NKRO_ENABLE
 | 
			
		||||
    if (keyboard_nkro) {
 | 
			
		||||
        uint8_t i = 0;
 | 
			
		||||
        for (; i < REPORT_BITS && !keyboard_report->nkro.bits[i]; i++)
 | 
			
		||||
            ;
 | 
			
		||||
        return i<<3 | biton(keyboard_report->nkro.bits[i]);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    return keyboard_report->keys[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void host_send_keyboard_report(void)
 | 
			
		||||
{
 | 
			
		||||
    if (!driver) return;
 | 
			
		||||
    host_keyboard_send(keyboard_report);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t host_mouse_in_use(void)
 | 
			
		||||
{
 | 
			
		||||
    return (mouse_report.buttons | mouse_report.x | mouse_report.y | mouse_report.v | mouse_report.h);
 | 
			
		||||
| 
						 | 
				
			
			@ -203,51 +103,3 @@ uint16_t host_last_consumer_report(void)
 | 
			
		|||
{
 | 
			
		||||
    return last_consumer_report;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void add_key_byte(uint8_t code)
 | 
			
		||||
{
 | 
			
		||||
    int8_t i = 0;
 | 
			
		||||
    int8_t empty = -1;
 | 
			
		||||
    for (; i < REPORT_KEYS; i++) {
 | 
			
		||||
        if (keyboard_report->keys[i] == code) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        if (empty == -1 && keyboard_report->keys[i] == 0) {
 | 
			
		||||
            empty = i;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (i == REPORT_KEYS) {
 | 
			
		||||
        if (empty != -1) {
 | 
			
		||||
            keyboard_report->keys[empty] = code;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void del_key_byte(uint8_t code)
 | 
			
		||||
{
 | 
			
		||||
    for (uint8_t i = 0; i < REPORT_KEYS; i++) {
 | 
			
		||||
        if (keyboard_report->keys[i] == code) {
 | 
			
		||||
            keyboard_report->keys[i] = 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef NKRO_ENABLE
 | 
			
		||||
static inline void add_key_bit(uint8_t code)
 | 
			
		||||
{
 | 
			
		||||
    if ((code>>3) < REPORT_BITS) {
 | 
			
		||||
        keyboard_report->nkro.bits[code>>3] |= 1<<(code&7);
 | 
			
		||||
    } else {
 | 
			
		||||
        dprintf("add_key_bit: can't add: %02X\n", code);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void del_key_bit(uint8_t code)
 | 
			
		||||
{
 | 
			
		||||
    if ((code>>3) < REPORT_BITS) {
 | 
			
		||||
        keyboard_report->nkro.bits[code>>3] &= ~(1<<(code&7));
 | 
			
		||||
    } else {
 | 
			
		||||
        dprintf("del_key_bit: can't del: %02X\n", code);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,7 +33,6 @@ extern bool keyboard_nkro;
 | 
			
		|||
#endif
 | 
			
		||||
 | 
			
		||||
/* report */
 | 
			
		||||
extern report_keyboard_t *keyboard_report;
 | 
			
		||||
extern report_mouse_t mouse_report;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -48,22 +47,6 @@ void host_mouse_send(report_mouse_t *report);
 | 
			
		|||
void host_system_send(uint16_t data);
 | 
			
		||||
void host_consumer_send(uint16_t data);
 | 
			
		||||
 | 
			
		||||
/* keyboard report utils */
 | 
			
		||||
void host_add_key(uint8_t key);
 | 
			
		||||
void host_del_key(uint8_t key);
 | 
			
		||||
void host_clear_keys(void);
 | 
			
		||||
 | 
			
		||||
uint8_t host_get_mods(void);
 | 
			
		||||
void host_add_mods(uint8_t mods);
 | 
			
		||||
void host_del_mods(uint8_t mods);
 | 
			
		||||
void host_set_mods(uint8_t mods);
 | 
			
		||||
void host_clear_mods(void);
 | 
			
		||||
 | 
			
		||||
uint8_t host_has_anykey(void);
 | 
			
		||||
uint8_t host_has_anymod(void);
 | 
			
		||||
uint8_t host_get_first_key(void);
 | 
			
		||||
void host_send_keyboard_report(void);
 | 
			
		||||
 | 
			
		||||
/* mouse report utils */
 | 
			
		||||
uint8_t host_mouse_in_use(void);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue