From 79603022422cec39ea44686c84a1050acd0c145e Mon Sep 17 00:00:00 2001 From: Michael Terhar Date: Sat, 26 Aug 2017 23:33:33 -0400 Subject: Keymaps for several of my boards (#1641) * copied mjt keymaps from archive * All mjt boards now compile * fixed jd45-mjt breathing * Updates to fix SpaceFN but not tested yet. * Still missing either spacebar or an adjacent keypress. * Debugging rigged up for use with hid_listen. * Reverted the default keymap to use tap_layer_key rather than custom. Moved custom approach to keymap_debug.c * Fixed the lower-left side of the keymap, which needed more spacers due to the matrix being directly put into the array rather than using the keymap function. * Cleaned up JD45 keymap that uses tapkey. * Redid minivan keymap with numsym rather than raise/lower. Untested. * Created my MJT keymap for HHKB Enabled dynamic macros and moved somoe of the shortcuts around. * Minor keymap fixes to make them compile without errors. * Added home/end to right arrow cluster on DYN layer. * Added more keys to fn and dyn layers. * It wasn't using my custom layer last time somehow...? Now it will. * Compiled and installed at end of day on 8/23 * Moved macros to FKEY layer because Adjust was too hard to get into and out of without some sort of feedback. * Fixed volume controls... were reversed and disabled. * Added F13-F15 back to fkeys layer in Minivan * Created new Planck Keymap that uses the NumSym and FKeys layer approach like the Minivan. * Removed DYN layer. * Fixed diagram in planck numsym. * Cleanup for pull request. * Roadkit flip phone warning. * Replaced PLAY_NOTES_ARRAY to PLAY_SONG * reset the submodules * checked out specific commits for submodules * Removed debugging from JD45 shared config.h * Moved custom rules.mk to apropriate keymap Reset the shared rules.mk file. * Trailing return issue in rules.mk Gotta make for a smooth pull request :-) --- keyboards/roadkit/keymaps/flipphone/Makefile | 21 +++ keyboards/roadkit/keymaps/flipphone/config.h | 8 + keyboards/roadkit/keymaps/flipphone/keymap.c | 216 +++++++++++++++++++++++ keyboards/roadkit/keymaps/flipphone/readme.md | 26 +++ keyboards/roadkit/keymaps/mjt/Makefile | 21 +++ keyboards/roadkit/keymaps/mjt/config.h | 8 + keyboards/roadkit/keymaps/mjt/keymap.c | 236 ++++++++++++++++++++++++++ keyboards/roadkit/keymaps/mjt/readme.md | 22 +++ 8 files changed, 558 insertions(+) create mode 100644 keyboards/roadkit/keymaps/flipphone/Makefile create mode 100644 keyboards/roadkit/keymaps/flipphone/config.h create mode 100644 keyboards/roadkit/keymaps/flipphone/keymap.c create mode 100644 keyboards/roadkit/keymaps/flipphone/readme.md create mode 100644 keyboards/roadkit/keymaps/mjt/Makefile create mode 100644 keyboards/roadkit/keymaps/mjt/config.h create mode 100644 keyboards/roadkit/keymaps/mjt/keymap.c create mode 100644 keyboards/roadkit/keymaps/mjt/readme.md (limited to 'keyboards/roadkit') diff --git a/keyboards/roadkit/keymaps/flipphone/Makefile b/keyboards/roadkit/keymaps/flipphone/Makefile new file mode 100644 index 000000000..f4671a9d1 --- /dev/null +++ b/keyboards/roadkit/keymaps/flipphone/Makefile @@ -0,0 +1,21 @@ +# Build Options +# change to "no" to disable the options, or define them in the Makefile in +# the appropriate keymap folder that will get included automatically +# +BOOTMAGIC_ENABLE = no # Virtual DIP switch configuration(+1000) +MOUSEKEY_ENABLE = yes # Mouse keys(+4700) +EXTRAKEY_ENABLE = yes # Audio control and System control(+450) +CONSOLE_ENABLE = no # Console for debug(+400) +COMMAND_ENABLE = yes # Commands for debug and configuration +NKRO_ENABLE = yes # Nkey Rollover - if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work +BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality +MIDI_ENABLE = no # MIDI controls +AUDIO_ENABLE = no # Audio output on port C6 +UNICODE_ENABLE = no # Unicode +BLUETOOTH_ENABLE = no # Enable Bluetooth with the Adafruit EZ-Key HID +RGBLIGHT_ENABLE = no # Enable WS2812 RGB underlight. Do not enable this with audio at the same time. +SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend + +ifndef QUANTUM_DIR + include ../../../../Makefile +endif \ No newline at end of file diff --git a/keyboards/roadkit/keymaps/flipphone/config.h b/keyboards/roadkit/keymaps/flipphone/config.h new file mode 100644 index 000000000..df06a2620 --- /dev/null +++ b/keyboards/roadkit/keymaps/flipphone/config.h @@ -0,0 +1,8 @@ +#ifndef CONFIG_USER_H +#define CONFIG_USER_H + +#include "../../config.h" + +// place overrides here + +#endif \ No newline at end of file diff --git a/keyboards/roadkit/keymaps/flipphone/keymap.c b/keyboards/roadkit/keymaps/flipphone/keymap.c new file mode 100644 index 000000000..b48d72e9e --- /dev/null +++ b/keyboards/roadkit/keymaps/flipphone/keymap.c @@ -0,0 +1,216 @@ +#include "roadkit.h" +#include "action_layer.h" +#include "eeconfig.h" + +extern keymap_config_t keymap_config; + +#ifdef BACKLIGHT_ENABLE + #include "backlight.h" +#endif + +#ifdef AUDIO_ENABLE + #include "audio.h" +#endif + +void backlight_toggle(void){ +}; + +enum roadkit_layers { + _NUMPAD, + _FPH, + _FPHNOISY, + _ADJUST, + _DYN +}; + +enum roadkit_keycodes { + NUMPAD = SAFE_RANGE, + FPH_1, + FPH_2, + FPH_3, + FPH_4, + FPH_5, + FPH_6, + FPH_7, + FPH_8, + FPH_9, + FN_0, + BACKLIT, + MACSLEEP, + DYNAMIC_MACRO_RANGE, +}; + +#include "dynamic_macro.h" + +// Fillers to make keymaps cleaner looking +#define _______ KC_TRNS +#define XXXXXXX KC_NO + +const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + [_NUMPAD] = /* Numpad */ + KEYMAP(KC_KP_7, KC_KP_8, KC_KP_9, KC_KP_PLUS, \ + KC_KP_4, KC_KP_5, KC_KP_6, \ + KC_KP_1, KC_KP_2, KC_KP_3, KC_KP_ENTER, \ + LT(_ADJUST, KC_KP_0), KC_KP_DOT), + [_FPH] = /* Quiet T9 */ + KEYMAP(FPH_7, FPH_8, FPH_9, KC_KP_PLUS, \ + FPH_4, FPH_5, FPH_6, \ + FPH_1, FPH_2, FPH_3, SFT_T(KC_KP_ENTER), \ + LT(_ADJUST, KC_SPACE), KC_KP_DOT), + [_FPHNOISY] = /* Noisy T9 */ + KEYMAP(FPH_7, FPH_8, FPH_9, KC_KP_PLUS, \ + FPH_4, FPH_5, FPH_6, \ + FPH_1, FPH_2, FPH_3, SFT_T(KC_KP_ENTER), \ + LT(_ADJUST, KC_SPACE), KC_KP_DOT), + [_ADJUST] = /* Adjustments */ + KEYMAP(KC_NUMLOCK, TG(_FPHNOISY), TG(_FPH), TG(_NUMPAD), \ + KC_BSPC, BACKLIT, KC_DEL, \ + MACSLEEP, _______, _______, _______, \ + _______, MO(_DYN)), + [_DYN] = /* DYNAMIC MACRO */ + KEYMAP(DYN_REC_START1, DYN_REC_START2, _______, DYN_REC_STOP, \ + _______, _______, _______, \ + DYN_MACRO_PLAY1, DYN_MACRO_PLAY2, _______, _______, \ + _______, _______), +}; + +const uint16_t PROGMEM fn_actions[] = { + +}; + +void persistant_default_layer_set(uint16_t default_layer) { + eeconfig_update_default_layer(default_layer); + default_layer_set(default_layer); +} + +uint16_t fph_tap_qty = false; +uint16_t fph_keycode = 0; // stores which spacebar was used, either raise or lower... + +uint16_t get_render_keycode(uint16_t keycode, uint16_t tap_qty){ // maybe replace shift with "mods" + tap_qty--; // reduce by one to match array indexes. + switch(keycode){ + case FPH_2: + {uint16_t kc[] = {KC_A, KC_B, KC_C}; + if(tap_qty % 6 > 2) + return LSFT(kc[tap_qty % 3]); + return kc[tap_qty % 3];} + break; + case FPH_3: + {uint16_t kc[] = {KC_D, KC_E, KC_F}; + if(tap_qty % 6 > 2) + return LSFT(kc[tap_qty % 3]); + return kc[tap_qty % 3];} + break; + case FPH_4: + {uint16_t kc[] = {KC_G, KC_H, KC_I}; + if(tap_qty % 6 > 2) + return LSFT(kc[tap_qty % 3]); + return kc[tap_qty % 3];} + break; + case FPH_5: + {uint16_t kc[] = {KC_J, KC_K, KC_L}; + if(tap_qty % 6 > 2) + return LSFT(kc[tap_qty % 3]); + return kc[tap_qty % 3];} + break; + case FPH_6: + {uint16_t kc[] = {KC_M, KC_N, KC_O}; + if(tap_qty % 6 > 2) + return LSFT(kc[tap_qty % 3]); + return kc[tap_qty % 3];} + break; + case FPH_7: + {uint16_t kc[] = {KC_P, KC_Q, KC_R, KC_S}; + if(tap_qty % 8 > 2) + return LSFT(kc[tap_qty % 4]); + return kc[tap_qty % 4];} + break; + case FPH_8: + {uint16_t kc[] = {KC_T, KC_U, KC_V}; + if(tap_qty % 8 > 2) + return LSFT(kc[tap_qty % 4]); + return kc[tap_qty % 4];} + break; + case FPH_9: + {uint16_t kc[] = {KC_W, KC_X, KC_Y, KC_Z}; + if(tap_qty % 8 > 2) + return LSFT(kc[tap_qty % 4]); + return kc[tap_qty % 4];} + break; + case FPH_1: + {uint16_t kc[] = {KC_COMM, LSFT(KC_SLSH), KC_EXLM, KC_AT, KC_MINS, KC_UNDS, KC_PLUS, \ + KC_SCLN, LSFT(KC_SCLN), KC_QUOT, LSFT(KC_QUOT), KC_TILD, \ + KC_PIPE, KC_BSLS, KC_HASH, LSFT(KC_4), KC_PERC, KC_CIRC, KC_AMPR, KC_ASTR, \ + KC_LBRC, KC_RBRC, KC_LCBR, KC_RCBR, LSFT(KC_COMM), LSFT(KC_DOT)}; + return kc[tap_qty % 26];} + } + return KC_NO; +} + +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + if (!process_record_dynamic_macro(keycode, record)) { + return false; + } + uint8_t layer; + layer = biton32(layer_state); + + switch (keycode) { + case FPH_1 ... FPH_9: + if(layer == _FPHNOISY){ + if (record->event.pressed) { + if(fph_keycode != keycode) { // key change + fph_tap_qty = 0; + fph_keycode = keycode; + } else { // same key tapped again so erase and increment. + register_code(KC_BSPC); + unregister_code(KC_BSPC); + fph_tap_qty++; + } + uint16_t render_keycode = get_render_keycode(keycode, fph_tap_qty); + // find mods? Apply mods.. + register_code(render_keycode); + unregister_code(render_keycode); + } else { + // do we need to do anything on key-release? + } + } else { // not noisy, be demure . + if (record->event.pressed) { + if(fph_keycode != keycode) { // key change, kill everything. + uint16_t render_keycode = get_render_keycode(fph_keycode, fph_tap_qty); + // find mods? Apply mods.. + register_code(render_keycode); + unregister_code(render_keycode); + fph_keycode = keycode; + fph_tap_qty = 0; + } else { // same key tapped again so increment counter silently + fph_tap_qty++; + } + } else { + // do we need to do anything on key-release? + // maybe start a timer on this one? + } + } + return false; + break; + case BACKLIT: + if (record->event.pressed) { + #ifdef BACKLIGHT_ENABLE + backlight_step(); + #endif + } + return false; + break; + case MACSLEEP: + if (record->event.pressed) { + register_code(KC_RSFT); + register_code(KC_RCTL); + register_code(KC_POWER); + unregister_code(KC_POWER); + unregister_code(KC_RCTL); + unregister_code(KC_RSFT); + } + return false; + break; + } + return true; +} diff --git a/keyboards/roadkit/keymaps/flipphone/readme.md b/keyboards/roadkit/keymaps/flipphone/readme.md new file mode 100644 index 000000000..ad630be19 --- /dev/null +++ b/keyboards/roadkit/keymaps/flipphone/readme.md @@ -0,0 +1,26 @@ +# Flip Phone layout for Roadkit + +BEWARE, THIS DOES NOT WORK YET. + +Uses the numpad with classic cellular phone style letters assigned to the numbers. + +# LAYERS + +* Numpad - The layer that matches the caps mostly +* FPH - Base layer for text +* FPHNOISY - Text layer that outputs every character and then backspaces as they're replaced +* DYN - dynamic macro layer for recording and playing macros + +## FPH + +Poke the "2" key twice and it makes "B". + +## FPHNOISY + +Poke the "2" key twice and it makes "A"+backspace+"B" so you can see it cycle through. + +## MACROS + +May be able to record macros with the T9 layer because the "KC_*"s are only sent +at the end. Trying to do a macro with T9NOISY would probably fill up the buffer +in just a few characters. diff --git a/keyboards/roadkit/keymaps/mjt/Makefile b/keyboards/roadkit/keymaps/mjt/Makefile new file mode 100644 index 000000000..f4671a9d1 --- /dev/null +++ b/keyboards/roadkit/keymaps/mjt/Makefile @@ -0,0 +1,21 @@ +# Build Options +# change to "no" to disable the options, or define them in the Makefile in +# the appropriate keymap folder that will get included automatically +# +BOOTMAGIC_ENABLE = no # Virtual DIP switch configuration(+1000) +MOUSEKEY_ENABLE = yes # Mouse keys(+4700) +EXTRAKEY_ENABLE = yes # Audio control and System control(+450) +CONSOLE_ENABLE = no # Console for debug(+400) +COMMAND_ENABLE = yes # Commands for debug and configuration +NKRO_ENABLE = yes # Nkey Rollover - if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work +BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality +MIDI_ENABLE = no # MIDI controls +AUDIO_ENABLE = no # Audio output on port C6 +UNICODE_ENABLE = no # Unicode +BLUETOOTH_ENABLE = no # Enable Bluetooth with the Adafruit EZ-Key HID +RGBLIGHT_ENABLE = no # Enable WS2812 RGB underlight. Do not enable this with audio at the same time. +SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend + +ifndef QUANTUM_DIR + include ../../../../Makefile +endif \ No newline at end of file diff --git a/keyboards/roadkit/keymaps/mjt/config.h b/keyboards/roadkit/keymaps/mjt/config.h new file mode 100644 index 000000000..df06a2620 --- /dev/null +++ b/keyboards/roadkit/keymaps/mjt/config.h @@ -0,0 +1,8 @@ +#ifndef CONFIG_USER_H +#define CONFIG_USER_H + +#include "../../config.h" + +// place overrides here + +#endif \ No newline at end of file diff --git a/keyboards/roadkit/keymaps/mjt/keymap.c b/keyboards/roadkit/keymaps/mjt/keymap.c new file mode 100644 index 000000000..d3c82ac84 --- /dev/null +++ b/keyboards/roadkit/keymaps/mjt/keymap.c @@ -0,0 +1,236 @@ +#include "roadkit.h" +#include "action_layer.h" +#include "eeconfig.h" + +extern keymap_config_t keymap_config; + +#ifdef BACKLIGHT_ENABLE + #include "backlight.h" +#endif + +#ifdef AUDIO_ENABLE + #include "audio.h" +#endif + +void backlight_toggle(void){ +}; + +enum roadkit_layers { + _NUMPAD, + _NAVIGATION, + _EXCEL, + _ADJUST, + _DYN +}; + +enum minivan_keycodes { + NUMPAD = SAFE_RANGE, + NAVIGATION, + EXCEL, + NUMPAD_LOCK, + NAVIGATION_LOCK, + EXCEL_LOCK, + PARENS, + BRACKETS, + BRACES, + BACKLIT, + MACSLEEP, + DYNAMIC_MACRO_RANGE, +}; + +#include "dynamic_macro.h" + +// Fillers to make keymaps cleaner looking +#define _______ KC_TRNS +#define XXXXXXX KC_NO + + +const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + [_NUMPAD] = /* Numpad */ + KEYMAP(KC_KP_7, KC_KP_8, KC_KP_9, KC_KP_PLUS, \ + KC_KP_4, KC_KP_5, KC_KP_6, \ + KC_KP_1, KC_KP_2, KC_KP_3, NAVIGATION, \ + EXCEL, KC_KP_DOT), + [_EXCEL] = /* Excel related */ + KEYMAP(KC_KP_SLASH, KC_KP_ASTERISK, KC_MINS, KC_TAB, \ + KC_QUOT, KC_KP_EQUAL, PARENS , \ + KC_DQT, BRACKETS, BRACES, _______, \ + _______, MO(_DYN)), + [_NAVIGATION] = /* Navigation */ + KEYMAP(KC_HOME, _______, KC_PGUP, KC_TAB, \ + KC_END, KC_UP, KC_PGDN, \ + KC_LEFT, KC_DOWN, KC_RIGHT, _______, \ + _______, MO(_DYN)), + [_ADJUST] = /* Adjustments */ + KEYMAP(KC_NUMLOCK, MACSLEEP, BACKLIT, _______, \ + KC_BSPC, _______, KC_DEL, \ + EXCEL_LOCK, NUMPAD_LOCK, NAVIGATION_LOCK, _______, \ + _______, _______), + [_DYN] = /* DYNAMIC MACRO */ + KEYMAP(DYN_REC_START1, DYN_REC_START2, _______, _______, \ + _______, _______, _______, \ + DYN_MACRO_PLAY1, DYN_MACRO_PLAY2, _______, _______, \ + _______, _______), +}; + +const uint16_t PROGMEM fn_actions[] = { + +}; + +#ifdef AUDIO_ENABLE + +float tone_startup[][2] = SONG(STARTUP_SOUND); +float tone_numpad[][2] = SONG(QWERTY_SOUND); +float tone_excel[][2] = SONG(DVORAK_SOUND); +float tone_navigation[][2] = SONG(COLEMAK_SOUND); +float tone_plover[][2] = SONG(PLOVER_SOUND); +float tone_plover_gb[][2] = SONG(PLOVER_GOODBYE_SOUND); +float music_scale[][2] = SONG(MUSIC_SCALE_SOUND); + +float tone_goodbye[][2] = SONG(GOODBYE_SOUND); +#endif + +void persistant_default_layer_set(uint16_t default_layer) { + eeconfig_update_default_layer(default_layer); + default_layer_set(default_layer); +} + +bool fn_tap_used = false; // sets to false when space is pressed, + // sets to true when any other key is pressed, + //when space is lifted, if another key was prssed, don't send space. +uint16_t fn_keycode = 0; // stores which spacebar was used, either raise or lower... + +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + if (!process_record_dynamic_macro(keycode, record)) { + return false; + } + + switch (keycode) { + case NAVIGATION: // This key is enter if tapped, navigation if held + if (record->event.pressed) { + fn_keycode = NAVIGATION; // these two lines figure out if the next action is + fn_tap_used = false; + layer_on(_NAVIGATION); + update_tri_layer(_EXCEL, _NAVIGATION, _ADJUST); + } else { + layer_off(_NAVIGATION); + update_tri_layer(_EXCEL, _NAVIGATION, _ADJUST); + if(!fn_tap_used && fn_keycode == NAVIGATION ){ + register_code(KC_KP_ENTER); + unregister_code(KC_KP_ENTER); + fn_keycode = 0; + fn_tap_used = false; + } + } + return false; + break; + case EXCEL: + if (record->event.pressed) { + fn_keycode = EXCEL; + fn_tap_used = false; + layer_on(_EXCEL); + update_tri_layer(_EXCEL, _NAVIGATION, _ADJUST); + } else { + layer_off(_EXCEL); + update_tri_layer(_EXCEL, _NAVIGATION, _ADJUST); + if(!fn_tap_used && fn_keycode == EXCEL ){ + register_code(KC_KP_0); + unregister_code(KC_KP_0); + fn_keycode = 0; + fn_tap_used = false; + } + } + return false; + break; + default: + fn_tap_used = true; + break; // don't return because this just handles the spacebars. + } + // this is the normal keycode processing switch from Planck default layout. + switch (keycode) { + case NUMPAD_LOCK: + if (record->event.pressed) { + #ifdef AUDIO_ENABLE + PLAY_NOTE_ARRAY(tone_numpad, false, 0); + #endif + persistant_default_layer_set(1UL<<_NUMPAD); + } + return false; + break; + case NAVIGATION_LOCK: + if (record->event.pressed) { + #ifdef AUDIO_ENABLE + PLAY_NOTE_ARRAY(tone_navigation, false, 0); + #endif + persistant_default_layer_set(1UL<<_NAVIGATION); + } + return false; + break; + case EXCEL_LOCK: + if (record->event.pressed) { + #ifdef AUDIO_ENABLE + PLAY_NOTE_ARRAY(tone_excel, false, 0); + #endif + persistant_default_layer_set(1UL<<_EXCEL); + } + return false; + break; + case PARENS: + if (record->event.pressed) { + register_code(KC_RSFT); + register_code(KC_9); + unregister_code(KC_9); + register_code(KC_0); + unregister_code(KC_0); + register_code(KC_RSFT); + register_code(KC_LEFT); + unregister_code(KC_LEFT); + } + return false; + break; + case BRACKETS: + if (record->event.pressed) { + register_code(KC_LBRC); + unregister_code(KC_LBRC); + register_code(KC_RBRC); + unregister_code(KC_RBRC); + register_code(KC_LEFT); + unregister_code(KC_LEFT); + } + return false; + break; + case BRACES: + if (record->event.pressed) { + register_code(KC_RSFT); + register_code(KC_LBRC); + unregister_code(KC_LBRC); + register_code(KC_RBRC); + unregister_code(KC_RBRC); + unregister_code(KC_RSFT); + register_code(KC_LEFT); + unregister_code(KC_LEFT); + } + return false; + break; + case BACKLIT: + if (record->event.pressed) { + #ifdef BACKLIGHT_ENABLE + backlight_step(); + #endif + } + return false; + break; + case MACSLEEP: + if (record->event.pressed) { + register_code(KC_RSFT); + register_code(KC_RCTL); + register_code(KC_POWER); + unregister_code(KC_POWER); + unregister_code(KC_RCTL); + unregister_code(KC_RSFT); + } + return false; + break; + } + return true; +} diff --git a/keyboards/roadkit/keymaps/mjt/readme.md b/keyboards/roadkit/keymaps/mjt/readme.md new file mode 100644 index 000000000..80c0e5240 --- /dev/null +++ b/keyboards/roadkit/keymaps/mjt/readme.md @@ -0,0 +1,22 @@ +# Mike's custom keymap for roadkit + +Based on the 13 key layout. + +## LAYERS + +* Numpad - The layer that matches the caps +* Excel - / * - = and () [] {} macros +* Navigation - arrow keys and home/end +* ADJUST - just like planck adjust, lets you set default layer. +* DYN - dynamic macro layer for recording and playing macros + +## MACROS + +Sadly, the dynamic macros are probably going worthless because they keys that +they record have to be pressed on this keyboard. This keyboard doesn't have +most letters... + +## LEADER KEYS + +This board would probably be better with the user of leader keys instead of +complex layers with a raise/lower setup... Something to look into for later. -- cgit v1.2.3-24-g4f1b