text: /* ----------------------------------------------------------------------------
   * key functions : basic : code
   * ----------------------------------------------------------------------------
   * Copyright (c) 2012 Ben Blazak <benblazak.dev@gmail.com>
   * Released under The MIT License (MIT) (see "license.md")
   * Project located at <https://github.com/benblazak/ergodox-firmware>
   * ------------------------------------------------------------------------- */
  
  
  #include "../../../main.h"
  #include "../../../keyboard/layout.h"
  #include "../public.h"
  #include "../private.h"
  
  // ----------------------------------------------------------------------------
  
  #define  MAX_LAYER_PUSH_POP_FUNCTIONS  10
  
  // ----------------------------------------------------------------------------
  
  // convenience macros
  #define  LAYER         main_arg_layer
  #define  LAYER_OFFSET  main_arg_layer_offset
  #define  ROW           main_arg_row
  #define  COL           main_arg_col
  #define  IS_PRESSED    main_arg_is_pressed
  #define  WAS_PRESSED   main_arg_was_pressed
  
  // ----------------------------------------------------------------------------
  
  /*
   * [name]
   *   Press|Release
   *
   * [description]
   *   Generate a normal keypress or keyrelease
   */
  void kbfun_press_release(void) {
  	if (!main_arg_trans_key_pressed)
  		main_arg_any_non_trans_key_pressed = true;
  	kbfun_press_release_preserve_sticky();
  }
  
  /*
   * [name]
   *   Press|Release and preserve top layer sticky key state
   *
   * [description]
   *   Generate a normal keypress or keyrelease
   *   While basing the sticky key state transition on whether
   *    kbfun_press_release() was called after kbfun_transparent() generally
   *    works in practice, it is not always the desired behavior. One of the
   *    benefits of sticky keys is avoiding key chording, so we want to make sure
   *    that standard modifiers do not interrupt the sticky key cycle. Use
   *    kbfun_press_release_preserve_sticky() if you want to define a standard
   *    modifier key (shift, control, alt, gui) on the sticky layer instead of
   *    defining the key to be transparent for the layer.
   */
  void kbfun_press_release_preserve_sticky(void) {
  	uint8_t keycode = kb_layout_get(LAYER, ROW, COL);
  	_kbfun_press_release(IS_PRESSED, keycode);
  }
  
  /*
   * [name]
   *   Toggle
   *
   * [description]
   *   Toggle the key pressed or unpressed
   */
  void kbfun_toggle(void) {
  	uint8_t keycode = kb_layout_get(LAYER, ROW, COL);
  
  	if (_kbfun_is_pressed(keycode))
  		_kbfun_press_release(false, keycode);
  	else
  		_kbfun_press_release(true, keycode);
  }
  
  /*
   * [name]
   *   Transparent
   *
   * [description]
   *   Execute the key that would have been executed if the current layer was not
   *   active
   */
  void kbfun_transparent(void) {
  	main_arg_trans_key_pressed = true;
  	LAYER_OFFSET++;
  	LAYER = main_layers_peek(LAYER_OFFSET);
  	main_layers_pressed[ROW][COL] = LAYER;
  	main_exec_key();
  }
  
  
  /* ----------------------------------------------------------------------------
   * layer push/pop functions
   * ------------------------------------------------------------------------- */
  
  // While there are only MAX_LAYER_PUSH_POP_FUNCTIONS number of layer functions,
  //  there are 1 + MAX_LAYER_PUSH_POP_FUNCTIONS layer ids because we still have
  //  layer 0 even if we will never have a push or pop function for it
  static uint8_t layer_ids[1 + MAX_LAYER_PUSH_POP_FUNCTIONS];
  
  static void layer_pop(uint8_t local_id) {
  	uint8_t id = layer_ids[local_id];
  	if (id != 0) {
  		main_layers_pop_id(id);
  		layer_ids[local_id] = 0;
  	}
  }
  
  static void layer_push(uint8_t local_id) {
  	uint8_t keycode = kb_layout_get(LAYER, ROW, COL);
  	layer_pop(local_id);
  	// Only the topmost layer on the stack should be in sticky once state, pop
  	//  the top layer if it is in sticky once state
  	uint8_t topSticky = main_layers_peek_sticky(0);
  	if (topSticky == eStickyOnceDown || topSticky == eStickyOnceUp) {
  		main_layers_pop_id(main_layers_peek(0));
  	}
  	layer_ids[local_id] = main_layers_push(keycode, eStickyNone);
  }
  
  static void layer_sticky(uint8_t local_id) {
  	uint8_t keycode = kb_layout_get(LAYER, ROW, COL);
  	if (IS_PRESSED) {
  		uint8_t topLayer = main_layers_peek(0);
  		uint8_t topSticky = main_layers_peek_sticky(0);
  		layer_pop(local_id);
  		if (topLayer == local_id) {
  			if (topSticky == eStickyOnceUp)
  				layer_ids[local_id] = main_layers_push(keycode, eStickyLock);
  		} else {
  			// only the topmost layer on the stack should be in sticky once state
  			if (topSticky == eStickyOnceDown || topSticky == eStickyOnceUp) {
  				layer_pop(topLayer);
  			}
  			layer_ids[local_id] = main_layers_push(keycode, eStickyOnceDown);
  			// this should be the only place we care about this flag being cleared
  			main_arg_any_non_trans_key_pressed = false;
  		}
  	} else {
  		uint8_t topLayer = main_layers_peek(0);
  		uint8_t topSticky = main_layers_peek_sticky(0);
  		if (topLayer == local_id) {
  			if (topSticky == eStickyOnceDown) {
  				// When releasing this sticky key, pop the layer always
  				layer_pop(local_id);
  				if (!main_arg_any_non_trans_key_pressed) {
  					// If no key defined for this layer (a non-transparent key)
  					//  was pressed, push the layer again, but in the
  					//  StickyOnceUp state
  					layer_ids[local_id] = main_layers_push(keycode, eStickyOnceUp);
  				}
  			}
  		}
  	}
  }
  
  static void layer_toggle(uint8_t local_id) {
  	if (layer_ids[local_id] != 0) {
  		layer_pop(local_id);
  	} else {
  		layer_push(local_id);
  	}
  }
  
  /*
   * [name]
   *   Layer push #1
   *
   * [description]
   *   Push a layer element containing the layer value specified in the keymap to
   *   the top of the stack, and record the id of that layer element
   */
  void kbfun_layer_push_1(void) {
  	layer_push(1);
  }
  
  /*
   * [name]
   *   Layer sticky cycle #1
   *
   * [description]
   *  This function gives similar behavior to sticky keys for modifiers available
   *   on most operating systems. It is considered an accessibility feature
   *   because it alleviates the user from having to hold down modifiers while
   *   pressing a key to produce the modified key function. It is useful for fast
   *   touch typing because you can avoid chording motions which both strain your
   *   hands and take your hands out of home-row position while pressing normal
   *   alpha keys.
   *  This function emulates the 3-state behavior which is default on OS X and
   *   optional in Windows where the modifier cycles between Off->Once->Locked
   *   states. This is particularly handy for symbol layers where you typically
   *   only type one symbol before you want to return to unmodified typing (layer
   *   0), e.g. 'if (condition) { a = "b" + "c"; }'. If you assign a symbol layer
   *   to a thumb key as a layer sticky cycle, you can type the entire line of
   *   code without taking your hands out of home row position and you do not need
   *   to toggle off the layer after each symbol is pressed, only immediately
   *   before keying the symbol.
   *  The exact behavior of the layer sticky cycle function is defined as follows
   *   for each state:
   *  1) One time down (set on key press) - The layer was not active and the key
   *      has been pressed but not yet released. The layer is pushed in the one
   *      time down state.
   *  2) One time up (set on key release) - The layer was active when the layer
   *      sticky key was released. If a key on this layer (not set to
   *      transparent) was pressed before the key was released, the layer will be
   *      popped. If a non-transparent key was not pressed, the layer is popped
   *      and pushed again in the one time up state.
   *  3) Locked (set on key press) - The layer was active and in the one time up
   *      state when the layer sticky key was pressed again. The layer will be
   *      popped if the function is invoked on a subsequent keypress.
   */
  void kbfun_layer_sticky_1  (void) {
  	layer_sticky(1);
  }
  
  /*
   * [name]
   *   Layer pop #1
   *
   * [description]
   *   Pop the layer element created by the corresponding "layer push" function
   *   out of the layer stack (no matter where it is in the stack, without
   *   touching any other elements)
   */
  void kbfun_layer_pop_1(void) {
  	layer_pop(1);
  }
  
  /*
   * [name]
   *   Layer toggle #1
   *
   * [description]
   *   If the layer element is already in the layer stack, pop it.  Otherwise,
   *   push the layer element to the top of the stack.
   */
  void kbfun_layer_toggle_1(void) {
  	layer_toggle(1);
  }
  
  /*
   * [name]
   *   Layer push #2
   *
   * [description]
   *   See the description of kbfun_layer_push_1()
   */
  void kbfun_layer_push_2(void) {
  	layer_push(2);
  }
  
  /*
   * [name]
   *   Layer sticky cycle #2
   *
   * [description]
   *   See the description of kbfun_layer_sticky_1()
   */
  void kbfun_layer_sticky_2  (void) {
  	layer_sticky(2);
  }
  
  /*
   * [name]
   *   Layer pop #2
   *
   * [description]
   *   See the description of kbfun_layer_pop_1()
   */
  void kbfun_layer_pop_2(void) {
  	layer_pop(2);
  }
  
  /*
   * [name]
   *   Layer toggle #2
   *
   * [description]
   *   See the description of kbfun_layer_toggle_1()
   */
  void kbfun_layer_toggle_2(void) {
  	layer_toggle(2);
  }
  
  /*
   * [name]
   *   Layer push #3
   *
   * [description]
   *   See the description of kbfun_layer_push_1()
   */
  void kbfun_layer_push_3(void) {
  	layer_push(3);
  }
  
  /*
   * [name]
   *   Layer sticky cycle #3
   *
   * [description]
   *   See the description of kbfun_layer_sticky_1()
   */
  void kbfun_layer_sticky_3  (void) {
  	layer_sticky(3);
  }
  
  /*
   * [name]
   *   Layer pop #3
   *
   * [description]
   *   See the description of kbfun_layer_pop_1()
   */
  void kbfun_layer_pop_3(void) {
  	layer_pop(3);
  }
  
  /*
   * [name]
   *   Layer toggle #3
   *
   * [description]
   *   See the description of kbfun_layer_toggle_1()
   */
  void kbfun_layer_toggle_3(void) {
  	layer_toggle(3);
  }
  
  /*
   * [name]
   *   Layer push #4
   *
   * [description]
   *   See the description of kbfun_layer_push_1()
   */
  void kbfun_layer_push_4(void) {
  	layer_push(4);
  }
  
  /*
   * [name]
   *   Layer sticky cycle #4
   *
   * [description]
   *   See the description of kbfun_layer_sticky_1()
   */
  void kbfun_layer_sticky_4  (void) {
  	layer_sticky(4);
  }
  
  /*
   * [name]
   *   Layer pop #4
   *
   * [description]
   *   See the description of kbfun_layer_pop_1()
   */
  void kbfun_layer_pop_4(void) {
  	layer_pop(4);
  }
  
  /*
   * [name]
   *   Layer toggle #4
   *
   * [description]
   *   See the description of kbfun_layer_toggle_1()
   */
  void kbfun_layer_toggle_4(void) {
  	layer_toggle(4);
  }
  
  /*
   * [name]
   *   Layer push #5
   *
   * [description]
   *   See the description of kbfun_layer_push_1()
   */
  void kbfun_layer_push_5(void) {
  	layer_push(5);
  }
  
  /*
   * [name]
   *   Layer sticky cycle #5
   *
   * [description]
   *   See the description of kbfun_layer_sticky_1()
   */
  void kbfun_layer_sticky_5  (void) {
  	layer_sticky(5);
  }
  
  /*
   * [name]
   *   Layer pop #5
   *
   * [description]
   *   See the description of kbfun_layer_pop_1()
   */
  void kbfun_layer_pop_5(void) {
  	layer_pop(5);
  }
  
  /*
   * [name]
   *   Layer toggle #5
   *
   * [description]
   *   See the description of kbfun_layer_toggle_1()
   */
  void kbfun_layer_toggle_5(void) {
  	layer_toggle(5);
  }
  
  /*
   * [name]
   *   Layer push #6
   *
   * [description]
   *   See the description of kbfun_layer_push_1()
   */
  void kbfun_layer_push_6(void) {
  	layer_push(6);
  }
  
  /*
   * [name]
   *   Layer sticky cycle #6
   *
   * [description]
   *   See the description of kbfun_layer_sticky_1()
   */
  void kbfun_layer_sticky_6  (void) {
  	layer_sticky(6);
  }
  
  /*
   * [name]
   *   Layer pop #6
   *
   * [description]
   *   See the description of kbfun_layer_pop_1()
   */
  void kbfun_layer_pop_6(void) {
  	layer_pop(6);
  }
  
  /*
   * [name]
   *   Layer toggle #6
   *
   * [description]
   *   See the description of kbfun_layer_toggle_1()
   */
  void kbfun_layer_toggle_6(void) {
  	layer_toggle(6);
  }
  
  /*
   * [name]
   *   Layer push #7
   *
   * [description]
   *   See the description of kbfun_layer_push_1()
   */
  void kbfun_layer_push_7(void) {
  	layer_push(7);
  }
  
  /*
   * [name]
   *   Layer sticky cycle #7
   *
   * [description]
   *   See the description of kbfun_layer_sticky_1()
   */
  void kbfun_layer_sticky_7  (void) {
  	layer_sticky(7);
  }
  
  /*
   * [name]
   *   Layer pop #7
   *
   * [description]
   *   See the description of kbfun_layer_pop_1()
   */
  void kbfun_layer_pop_7(void) {
  	layer_pop(7);
  }
  
  /*
   * [name]
   *   Layer toggle #7
   *
   * [description]
   *   See the description of kbfun_layer_toggle_1()
   */
  void kbfun_layer_toggle_7(void) {
  	layer_toggle(7);
  }
  
  /*
   * [name]
   *   Layer push #8
   *
   * [description]
   *   See the description of kbfun_layer_push_1()
   */
  void kbfun_layer_push_8(void) {
  	layer_push(8);
  }
  
  /*
   * [name]
   *   Layer sticky cycle #8
   *
   * [description]
   *   See the description of kbfun_layer_sticky_1()
   */
  void kbfun_layer_sticky_8  (void) {
  	layer_sticky(8);
  }
  
  /*
   * [name]
   *   Layer pop #8
   *
   * [description]
   *   See the description of kbfun_layer_pop_1()
   */
  void kbfun_layer_pop_8(void) {
  	layer_pop(8);
  }
  
  /*
   * [name]
   *   Layer toggle #8
   *
   * [description]
   *   See the description of kbfun_layer_toggle_1()
   */
  void kbfun_layer_toggle_8(void) {
  	layer_toggle(8);
  }
  
  /*
   * [name]
   *   Layer push #9
   *
   * [description]
   *   See the description of kbfun_layer_push_1()
   */
  void kbfun_layer_push_9(void) {
  	layer_push(9);
  }
  
  /*
   * [name]
   *   Layer sticky cycle #9
   *
   * [description]
   *   See the description of kbfun_layer_sticky_1()
   */
  void kbfun_layer_sticky_9  (void) {
  	layer_sticky(9);
  }
  
  /*
   * [name]
   *   Layer pop #9
   *
   * [description]
   *   See the description of kbfun_layer_pop_1()
   */
  void kbfun_layer_pop_9(void) {
  	layer_pop(9);
  }
  
  /*
   * [name]
   *   Layer toggle #9
   *
   * [description]
   *   See the description of kbfun_layer_toggle_1()
   */
  void kbfun_layer_toggle_9(void) {
  	layer_toggle(9);
  }
  
  /*
   * [name]
   *   Layer push #10
   *
   * [description]
   *   See the description of kbfun_layer_push_1()
   */
  void kbfun_layer_push_10(void) {
  	layer_push(10);
  }
  
  /*
   * [name]
   *   Layer sticky cycle #10
   *
   * [description]
   *   See the description of kbfun_layer_sticky_1()
   */
  void kbfun_layer_sticky_10  (void) {
  	layer_sticky(10);
  }
  
  /*
   * [name]
   *   Layer pop #10
   *
   * [description]
   *   See the description of kbfun_layer_pop_1()
   */
  void kbfun_layer_pop_10(void) {
  	layer_pop(10);
  }
  
  /*
   * [name]
   *   Layer toggle #10
   *
   * [description]
   *   See the description of kbfun_layer_toggle_1()
   */
  void kbfun_layer_toggle_10(void) {
  	layer_toggle(10);
  }
  
  /*
   * [name]
   *   Layer pop all
   *
   * [description]
   *   Pops all layers from the stack.  Resets the layout to the default state.
   */
  void kbfun_layer_pop_all(void) {
  	kbfun_layer_pop_1();
  	kbfun_layer_pop_2();
  	kbfun_layer_pop_3();
  	kbfun_layer_pop_4();
  	kbfun_layer_pop_5();
  	kbfun_layer_pop_6();
  	kbfun_layer_pop_7();
  	kbfun_layer_pop_8();
  	kbfun_layer_pop_9();
  	kbfun_layer_pop_10();
  }
  
  /* ----------------------------------------------------------------------------
   * ------------------------------------------------------------------------- */
  
  
  