2020-11-01 19:46:00 +01:00
/*
Copyright 2020 Seth Bonner < fl3tching101 @ 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 QMK_KEYBOARD_H
2021-09-11 09:32:29 +02:00
# include "keycode_lookup.h"
# include <string.h>
2020-11-01 19:46:00 +01:00
2021-09-11 09:32:29 +02:00
# ifdef CONSOLE_ENABLE
# include "print.h"
# endif
2020-11-01 19:46:00 +01:00
// Each layer gets a name for readability, which is then used in the keymap matrix below.
// The underscores don't mean anything - you can have a layer called STUFF or any other name.
// Layer names don't all need to be of the same length, obviously, and you can also skip them
// entirely and just use numbers.
# define _BASE 0
2021-09-11 09:32:29 +02:00
# define _SPEC 1 // Special layer
2020-11-01 19:46:00 +01:00
// Use the following format to create custom key codes to make macros out of and such
enum custom_keycodes {
2021-09-11 09:32:29 +02:00
KC_EXAM = SAFE_RANGE // "Examine" key code to show the keycode of a key pressed afterwards on the OLED
2020-11-01 19:46:00 +01:00
} ;
2021-09-11 09:32:29 +02:00
2020-11-01 19:46:00 +01:00
const uint16_t PROGMEM keymaps [ ] [ MATRIX_ROWS ] [ MATRIX_COLS ] = {
2021-11-03 02:00:27 +01:00
[ _BASE ] = LAYOUT (
2022-10-15 23:29:43 +02:00
RGB_MOD , KC_NUM ,
2021-11-03 02:00:27 +01:00
KC_P7 , KC_P8 , KC_P9 , KC_DEL ,
KC_P4 , KC_P5 , KC_P6 , KC_END ,
KC_P1 , KC_P2 , KC_P3 , KC_F13 ,
KC_P0 , MO ( 1 ) , KC_PDOT , KC_PENT
2021-09-11 09:32:29 +02:00
) ,
2021-11-03 02:00:27 +01:00
[ _SPEC ] = LAYOUT (
RGB_RMOD , KC_MUTE ,
KC_NO , KC_NO , KC_NO , KC_EXAM ,
KC_NO , KC_NO , KC_NO , KC_NO ,
2022-05-15 21:26:27 +02:00
QK_BOOT , RGB_TOG , RGB_SPI , RGB_SPD ,
2021-11-03 02:00:27 +01:00
KC_NO , _______ , KC_NO , KC_NO
2021-09-11 09:32:29 +02:00
)
2020-11-01 19:46:00 +01:00
} ;
2021-05-22 08:17:32 +02:00
bool encoder_update_user ( uint8_t index , bool clockwise ) {
2020-11-01 19:46:00 +01:00
if ( index = = 0 ) { // first encoder
if ( clockwise ) {
tap_code ( KC_AUDIO_VOL_UP ) ;
} else {
tap_code ( KC_AUDIO_VOL_DOWN ) ;
}
} else if ( index = = 1 ) { // second encoder
if ( clockwise ) {
rgblight_increase_val ( ) ;
} else {
rgblight_decrease_val ( ) ;
}
}
2021-05-22 08:17:32 +02:00
return true ;
2020-11-01 19:46:00 +01:00
}
2021-08-24 08:28:26 +02:00
# ifdef OLED_ENABLE
2020-11-01 19:46:00 +01:00
2021-09-11 09:32:29 +02:00
# define ANIM_FRAMES 3
# define ANIM_FRAME_DURATION 110 // Number of milliseconds per frame (no faster than 110ms, last line struggles)
# define BACKGROUND_FRAMES 21
# define ROCKET_CENTER_POS 3
# define SPLASH_DUR 100 // Measured in frames, see above for frame length (note, 231 is used as a key value later on, CTRL+F for uses of this to make sure everything is good)
uint32_t anim_timer = 0 ;
uint8_t current_frame = 0 ;
uint8_t rocket_y_position = 3 ;
uint8_t rocket_pos_change = 0 ;
uint8_t background_frame = 0 ;
uint8_t splash_dur_counter = 0 ;
bool examine_engaged = false ;
uint16_t examined_keycode = KC_NO ;
char lastKeycodeString [ 32 ] = { 0 } ;
const char star_background [ 8 ] [ 21 ] =
{
{ 0x88 , 0x00 , 0x00 , 0x00 , 0x90 , 0x00 , 0x00 , 0x8C , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x89 , 0x00 , 0x00 , 0x00 , 0x00 , 0x93 } ,
{ 0x00 , 0x00 , 0x89 , 0x00 , 0x00 , 0x00 , 0x8D , 0x00 , 0x00 , 0x00 , 0x00 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x8E , 0x00 , 0x00 } ,
{ 0x00 , 0x8F , 0x00 , 0x00 , 0x8A , 0x00 , 0x00 , 0x00 , 0x00 , 0x92 , 0x00 , 0x00 , 0x00 , 0x88 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ,
{ 0x00 , 0x00 , 0x00 , 0x8B , 0x00 , 0x00 , 0x00 , 0x8D , 0x00 , 0x00 , 0x00 , 0x00 , 0x8C , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x90 , 0x00 } ,
{ 0x8D , 0x00 , 0x89 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x8B , 0x00 , 0x00 , 0x00 , 0x00 , 0x8F , 0x8B , 0x00 , 0x00 , 0x00 , 0x00 } ,
{ 0x00 , 0x8A , 0x00 , 0x00 , 0x8E , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x91 , 0x00 , 0x00 , 0x8D , 0x00 , 0x00 , 0x00 , 0x8F , 0x00 , 0x00 } ,
{ 0x00 , 0x00 , 0x00 , 0x8C , 0x00 , 0x00 , 0x8F , 0x00 , 0x89 , 0x00 , 0x00 , 0x88 , 0x00 , 0x00 , 0x00 , 0x8D , 0x00 , 0x00 , 0x00 , 0x8F } ,
{ 0x00 , 0x8B , 0x00 , 0x00 , 0x91 , 0x00 , 0x00 , 0x92 , 0x00 , 0x00 , 0x00 , 0x8E , 0x00 , 0x00 , 0x90 , 0x00 , 0x00 , 0x8C , 0x00 , 0x00 } ,
} ;
static void oled_write_ln_centered ( const char * data , bool inverted )
{
if ( strlen ( data ) > = 21 ) // If more than 1 line of text is passed in, return without doing anything
{
return ;
}
// Character buffer to build up the string in
char line_buf [ 21 ] ;
// Amount to offset string from left side
2022-07-07 14:14:09 +02:00
uint8_t offset = ( 22 - strlen ( data ) ) / 2 ;
2021-09-11 09:32:29 +02:00
// Formatted string centering... look, it works, don't ask how...
2022-07-07 14:14:09 +02:00
snprintf ( line_buf , 21 , " %*s%s%*s " , offset , " " , data , offset , " " ) ; // Centers data within 21 character buffer
2021-09-11 09:32:29 +02:00
oled_write_ln ( line_buf , inverted ) ;
}
2020-11-01 19:46:00 +01:00
2021-09-11 09:32:29 +02:00
// Prints the exhaust characters in an order determined by the phase for animation purposes
// startX - The x axis starting point in characters for the exhaust (3 behind the rocket)
// startY - The y axis starting point in characters for the exhaust (middle of the rocket)
// phase - The "phase" of the animation, no real rhyme or reason to the exact number, but each frame move +1 to make the animation work
static void render_exhaust ( uint8_t startX , uint8_t startY , uint8_t phase )
{
oled_set_cursor ( startX , startY ) ;
oled_write_char ( 0x85 + ( phase % 3 ) , false ) ;
phase + + ;
oled_write_char ( 0x85 + ( phase % 3 ) , false ) ;
phase + + ;
oled_write_char ( 0x85 + ( phase % 3 ) , false ) ;
}
// Renders the "stars" behind the rocket
// startY - The starting Y location (in characters) of the rocket so that stars aren't rendered on top of the rocket
static void render_stars ( uint8_t startY , uint8_t phase )
{
// Line 0
oled_set_cursor ( 0 , 0 ) ;
for ( int i = 0 ; i < 21 ; i + + )
{
oled_write_char ( star_background [ 0 ] [ ( i + phase ) % 21 ] , false ) ;
}
// Line 1
oled_set_cursor ( 0 , 1 ) ;
for ( int i = 0 ; i < 21 ; i + + )
{
oled_write_char ( star_background [ 1 ] [ ( i + phase ) % 21 ] , false ) ;
}
// Line 2
oled_set_cursor ( 0 , 2 ) ;
for ( int i = 0 ; i < 21 ; i + + )
{
oled_write_char ( star_background [ 2 ] [ ( i + phase ) % 21 ] , false ) ;
}
// Line 3
oled_set_cursor ( 0 , 3 ) ;
for ( int i = 0 ; i < 21 ; i + + )
{
oled_write_char ( star_background [ 3 ] [ ( i + phase ) % 21 ] , false ) ;
}
// Line 4
oled_set_cursor ( 0 , 4 ) ;
for ( int i = 0 ; i < 21 ; i + + )
{
oled_write_char ( star_background [ 4 ] [ ( i + phase ) % 21 ] , false ) ;
}
// Line 5
oled_set_cursor ( 0 , 5 ) ;
for ( int i = 0 ; i < 21 ; i + + )
{
oled_write_char ( star_background [ 5 ] [ ( i + phase ) % 21 ] , false ) ;
}
// Line 6
oled_set_cursor ( 0 , 6 ) ;
for ( int i = 0 ; i < 21 ; i + + )
{
oled_write_char ( star_background [ 6 ] [ ( i + phase ) % 21 ] , false ) ;
}
// Line 7
oled_set_cursor ( 0 , 7 ) ;
for ( int i = 0 ; i < 21 ; i + + )
{
oled_write_char ( star_background [ 7 ] [ ( i + phase ) % 21 ] , false ) ;
}
}
static void render_logo ( uint8_t startX , uint8_t startY )
{
oled_set_cursor ( startX , startY ) ;
oled_write_char ( 0x80 , false ) ;
oled_write_char ( 0x81 , false ) ;
oled_write_char ( 0x82 , false ) ;
oled_write_char ( 0x83 , false ) ;
oled_write_char ( 0x84 , false ) ;
oled_set_cursor ( startX , startY + 1 ) ;
oled_write_char ( 0xA0 , false ) ;
oled_write_char ( 0xA1 , false ) ;
oled_write_char ( 0xA2 , false ) ;
oled_write_char ( 0xA3 , false ) ;
oled_write_char ( 0xA4 , false ) ;
oled_write_char ( 0xA5 , false ) ;
oled_set_cursor ( startX , startY + 2 ) ;
oled_write_char ( 0xC0 , false ) ;
oled_write_char ( 0xC1 , false ) ;
oled_write_char ( 0xC2 , false ) ;
oled_write_char ( 0xC3 , false ) ;
oled_write_char ( 0xC4 , false ) ;
2020-11-01 19:46:00 +01:00
}
oled_rotation_t oled_init_user ( oled_rotation_t rotation ) { return OLED_ROTATION_180 ; }
2021-11-01 23:42:50 +01:00
bool oled_task_user ( void )
2021-09-11 09:32:29 +02:00
{
// Playing the animation
if ( ( timer_elapsed32 ( anim_timer ) > ANIM_FRAME_DURATION ) & & ( splash_dur_counter < SPLASH_DUR ) )
{
anim_timer = timer_read32 ( ) ; // read the current timer value
current_frame = ( current_frame + 1 ) % ANIM_FRAMES ; // Frame in the exhaust animation
background_frame = ( background_frame + 1 ) % BACKGROUND_FRAMES ; // Frame in the star animation
// Move the rocket up and down
if ( ( rocket_pos_change / 9 ) = = 0 )
{
rocket_y_position = ROCKET_CENTER_POS ;
}
else if ( ( rocket_pos_change / 9 ) = = 1 )
{
rocket_y_position = ROCKET_CENTER_POS + 1 ;
}
else if ( ( rocket_pos_change / 9 ) = = 2 )
{
rocket_y_position = ROCKET_CENTER_POS ;
}
if ( ( rocket_pos_change / 9 ) = = 3 )
{
rocket_y_position = ROCKET_CENTER_POS - 1 ;
}
// Renders the scene piece by piece
render_stars ( 8 , background_frame ) ; // Render star background
render_exhaust ( 6 , rocket_y_position + 1 , current_frame ) ; // Render exhaust
render_logo ( 9 , rocket_y_position ) ; // Render the rocket
// Timing for rocket position change
if ( rocket_pos_change < 36 )
{
rocket_pos_change + + ;
}
else
{
rocket_pos_change = 0 ;
}
splash_dur_counter + + ;
}
else if ( ( splash_dur_counter > = SPLASH_DUR ) & & ( splash_dur_counter ! = 231 ) ) // Should only run once at end of splash screen duration
{
splash_dur_counter = 231 ; // Nice known value
oled_clear ( ) ; // Clear the screen
}
// After the splash screen
if ( splash_dur_counter = = 231 )
{
uint8_t light_level = rgblight_get_val ( ) ;
light_level = ( uint8_t ) ( 100.0 * ( ( float ) light_level / ( float ) RGBLIGHT_LIMIT_VAL ) ) ; // Convert to %
char c_light_level [ 3 ] ;
itoa ( light_level , c_light_level , 10 ) ;
// Display lock LED statuses
led_t led_state = host_keyboard_led_state ( ) ;
if ( led_state . num_lock )
{
oled_write ( PSTR ( " | " ) , false ) ;
oled_write ( PSTR ( " NUM " ) , true ) ;
oled_write ( PSTR ( " | " ) , false ) ;
}
else
{
oled_write ( PSTR ( " |NUM| " ) , false ) ;
}
if ( led_state . caps_lock )
{
oled_write ( PSTR ( " | " ) , false ) ;
oled_write ( PSTR ( " CAP " ) , true ) ;
oled_write ( PSTR ( " | " ) , false ) ;
}
else
{
oled_write ( PSTR ( " |CAP| " ) , false ) ;
}
if ( led_state . scroll_lock )
{
oled_write ( PSTR ( " | " ) , false ) ;
oled_write ( PSTR ( " SCR " ) , true ) ;
oled_write ( PSTR ( " | " ) , false ) ;
}
else
{
oled_write ( PSTR ( " |SCR| " ) , false ) ;
}
// Print the examine info
if ( examine_engaged = = true )
{
oled_set_cursor ( 0 , 2 ) ;
oled_write_ln ( PSTR ( " Keycode: " ) , false ) ;
oled_write_ln_centered ( lastKeycodeString , false ) ;
}
else
{
oled_set_cursor ( 0 , 2 ) ;
oled_write_ln ( PSTR ( " " ) , false ) ;
oled_write_ln ( PSTR ( " " ) , false ) ;
}
// Print the backlight % bottom right
oled_set_cursor ( 11 , 7 ) ;
oled_write ( PSTR ( " BKLT: " ) , false ) ;
oled_write ( c_light_level , false ) ;
oled_write ( PSTR ( " % " ) , false ) ;
// Print the layer number in bottom left
oled_set_cursor ( 0 , 7 ) ;
oled_write ( PSTR ( " L: " ) , false ) ;
switch ( get_highest_layer ( layer_state ) )
{
case 0 :
oled_write ( PSTR ( " 0 " ) , false ) ;
break ;
case 1 :
oled_write ( PSTR ( " 1 " ) , false ) ;
break ;
case 2 :
oled_write ( PSTR ( " 2 " ) , false ) ;
break ;
case 3 :
oled_write ( PSTR ( " 3 " ) , false ) ;
break ;
default :
oled_write ( PSTR ( " Und " ) , false ) ;
break ;
}
}
2021-11-01 23:42:50 +01:00
return false ;
2021-09-11 09:32:29 +02:00
}
// Process the extra/extended keycode functionality
bool process_record_user ( uint16_t keycode , keyrecord_t * record )
{
bool ret = true ; // True will allow QMK to process the key as usual after the function runs, false skips QMK processing after this function runs
switch ( keycode )
{
case KC_EXAM :
if ( record - > event . pressed ) // On pressed, flip bool examine_engaged
{
if ( examine_engaged = = false )
{
examine_engaged = true ;
}
else
{
examine_engaged = false ;
}
ret = false ;
}
else // On release do nothing
{
ret = false ;
}
break ;
default : // For any key other than EX, simply let QMK process after saving away what it was
memset ( lastKeycodeString , 0 , sizeof ( lastKeycodeString ) ) ;
memcpy ( lastKeycodeString , translate_keycode_to_string ( keycode ) , sizeof ( ( ( lookup_table_t * ) 0 ) - > key_string ) ) ;
ret = true ;
break ;
}
return ret ;
2020-11-01 19:46:00 +01:00
}
# endif