summaryrefslogtreecommitdiff
path: root/users/snowe
diff options
context:
space:
mode:
authorTyler Thrailkill <tylerbthrailkill@gmail.com>2021-06-23 20:21:40 -0600
committerGitHub <noreply@github.com>2021-06-24 12:21:40 +1000
commit4a15eb593d0d1447bf9bca87ef966f84b8077a4f (patch)
tree8b4bf29238334d71eca22d4687451f8c0430dab4 /users/snowe
parent6e1ed1c9d3f083e7dbb7b17375e437963cd70771 (diff)
downloadqmk_firmware-4a15eb593d0d1447bf9bca87ef966f84b8077a4f.tar.gz
qmk_firmware-4a15eb593d0d1447bf9bca87ef966f84b8077a4f.zip
snowe keymap/userspace and Ocean Dream animation (#12477)
* Add snowe keymap/userspace & Ocean Dream animation * Add snowe userspace with keymap wrappers and two animations * Add crkbd keymap * Add Ocean Dream animation, a cool full screen animation with: * * twinkling stars * * meteor showers * * ocean waves * * island with palm tree * * moon with phases * Disable Luna so travis build succeeds. * Add more copyrights * Add pragma once to keycode_aliases.h Co-authored-by: Drashna Jaelre <drashna@live.com> Co-authored-by: Drashna Jaelre <drashna@live.com>
Diffstat (limited to 'users/snowe')
-rw-r--r--users/snowe/luna.c229
-rw-r--r--users/snowe/luna.h31
-rw-r--r--users/snowe/ocean_dream.c555
-rw-r--r--users/snowe/ocean_dream.h103
-rw-r--r--users/snowe/oled_setup.c141
-rw-r--r--users/snowe/oled_setup.h30
-rw-r--r--users/snowe/readme.md10
-rw-r--r--users/snowe/readme_ocean_dream.md258
-rw-r--r--users/snowe/rules.mk27
-rw-r--r--users/snowe/snowe.h43
-rw-r--r--users/snowe/wrappers.h92
11 files changed, 1519 insertions, 0 deletions
diff --git a/users/snowe/luna.c b/users/snowe/luna.c
new file mode 100644
index 0000000000..4653abfaeb
--- /dev/null
+++ b/users/snowe/luna.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2021 QMK Community
+ * Copyright 2021 Tyler Thrailkill (@snowe/@snowe2010) <tyler.b.thrailkill@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 "quantum.h"
+#include "luna.h"
+
+// KEYBOARD PET START
+
+// settings
+#define MIN_WALK_SPEED 10
+#define MIN_RUN_SPEED 40
+
+// advanced settings
+#define ANIM_FRAME_DURATION 200 // how long each frame lasts in ms
+#define ANIM_SIZE 96 // number of bytes in array. If you change sprites, minimize for adequate firmware size. max is 1024
+
+bool isSneaking = false;
+bool isJumping = false;
+bool showedJump = true;
+
+// status variables
+int current_wpm = 0;
+led_t led_usb_state = {
+ .num_lock = false,
+ .caps_lock = false,
+ .scroll_lock = false
+};
+
+// current frame
+uint8_t current_frame = 0;
+
+// timers
+uint32_t anim_timer = 0;
+uint32_t anim_sleep = 0;
+
+// logic
+void render_luna(int LUNA_X, int LUNA_Y) {
+
+ // Sit
+ static const char PROGMEM sit[2][ANIM_SIZE] = {
+ // 'sit1', 32x22px
+ {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x1c,
+ 0x02, 0x05, 0x02, 0x24, 0x04, 0x04, 0x02, 0xa9, 0x1e, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x10, 0x08, 0x68, 0x10, 0x08, 0x04, 0x03, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x82, 0x7c, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x0c, 0x10, 0x10, 0x20, 0x20, 0x20, 0x28,
+ 0x3e, 0x1c, 0x20, 0x20, 0x3e, 0x0f, 0x11, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+
+ // 'sit2', 32x22px
+ {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x1c,
+ 0x02, 0x05, 0x02, 0x24, 0x04, 0x04, 0x02, 0xa9, 0x1e, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x90, 0x08, 0x18, 0x60, 0x10, 0x08, 0x04, 0x03, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0e, 0x82, 0x7c, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x0c, 0x10, 0x10, 0x20, 0x20, 0x20, 0x28,
+ 0x3e, 0x1c, 0x20, 0x20, 0x3e, 0x0f, 0x11, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ }
+ };
+
+ // Walk
+ static const char PROGMEM walk[2][ANIM_SIZE] = {
+ // 'walk1', 32x22px
+ {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x20, 0x10, 0x90, 0x90, 0x90, 0xa0, 0xc0, 0x80, 0x80,
+ 0x80, 0x70, 0x08, 0x14, 0x08, 0x90, 0x10, 0x10, 0x08, 0xa4, 0x78, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x08, 0xfc, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x18, 0xea, 0x10, 0x0f, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x1c, 0x20, 0x20, 0x3c, 0x0f, 0x11, 0x1f, 0x03,
+ 0x06, 0x18, 0x20, 0x20, 0x3c, 0x0c, 0x12, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+
+ // 'walk2', 32x22px
+ {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x20, 0x20, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0xe0, 0x10, 0x28, 0x10, 0x20, 0x20, 0x20, 0x10, 0x48, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x20, 0xf8, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x30, 0xd5, 0x20, 0x1f, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x20, 0x30, 0x0c, 0x02, 0x05, 0x09, 0x12, 0x1e,
+ 0x02, 0x1c, 0x14, 0x08, 0x10, 0x20, 0x2c, 0x32, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ }
+ };
+
+ // Run
+ static const char PROGMEM run[2][ANIM_SIZE] = {
+ // 'run1', 32x22px
+ {
+ 0x00, 0x00, 0x00, 0x00, 0xe0, 0x10, 0x08, 0x08, 0xc8, 0xb0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x40, 0x40, 0x3c, 0x14, 0x04, 0x08, 0x90, 0x18, 0x04, 0x08, 0xb0, 0x40, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0xc4, 0xa4, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc8, 0x58, 0x28, 0x2a, 0x10, 0x0f, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x09, 0x04, 0x04, 0x04, 0x04, 0x02, 0x03, 0x02, 0x01, 0x01,
+ 0x02, 0x02, 0x04, 0x08, 0x10, 0x26, 0x2b, 0x32, 0x04, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+
+ // 'run2', 32x22px
+ {
+ 0x00, 0x00, 0x00, 0xe0, 0x10, 0x10, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x80, 0x80, 0x78, 0x28, 0x08, 0x10, 0x20, 0x30, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x04, 0x08, 0x10, 0x11, 0xf9, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0xb0, 0x50, 0x55, 0x20, 0x1f, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x0c, 0x10, 0x20, 0x28, 0x37,
+ 0x02, 0x1e, 0x20, 0x20, 0x18, 0x0c, 0x14, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ }
+ };
+
+ // Bark
+ static const char PROGMEM bark[2][ANIM_SIZE] = {
+ // 'bark1', 32x22px
+ {
+ 0x00, 0xc0, 0x20, 0x10, 0xd0, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x40,
+ 0x3c, 0x14, 0x04, 0x08, 0x90, 0x18, 0x04, 0x08, 0xb0, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x04, 0x08, 0x10, 0x11, 0xf9, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0xc8, 0x48, 0x28, 0x2a, 0x10, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x0c, 0x10, 0x20, 0x28, 0x37, 0x02, 0x02,
+ 0x04, 0x08, 0x10, 0x26, 0x2b, 0x32, 0x04, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+
+ // 'bark2', 32x22px
+ {
+ 0x00, 0xe0, 0x10, 0x10, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x40,
+ 0x40, 0x2c, 0x14, 0x04, 0x08, 0x90, 0x18, 0x04, 0x08, 0xb0, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x04, 0x08, 0x10, 0x11, 0xf9, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0x48, 0x28, 0x2a, 0x10, 0x0f, 0x20, 0x4a, 0x09, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x0c, 0x10, 0x20, 0x28, 0x37, 0x02, 0x02,
+ 0x04, 0x08, 0x10, 0x26, 0x2b, 0x32, 0x04, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ }
+ };
+
+ // Sneak
+ static const char PROGMEM sneak[2][ANIM_SIZE] = {
+ // 'sneak1', 32x22px
+ {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc0, 0x40, 0x40, 0x80, 0x00, 0x80, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x1e, 0x21, 0xf0, 0x04, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, 0x04,
+ 0x04, 0x04, 0x03, 0x01, 0x00, 0x00, 0x09, 0x01, 0x80, 0x80, 0xab, 0x04, 0xf8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x1c, 0x20, 0x20, 0x3c, 0x0f, 0x11, 0x1f, 0x02, 0x06,
+ 0x18, 0x20, 0x20, 0x38, 0x08, 0x10, 0x18, 0x04, 0x04, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00,
+ },
+
+ // 'sneak2', 32x22px
+ {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x40, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xe0, 0xa0, 0x20, 0x40, 0x80, 0xc0, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x3e, 0x41, 0xf0, 0x04, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, 0x02, 0x04,
+ 0x04, 0x02, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x40, 0x40, 0x55, 0x82, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x20, 0x30, 0x0c, 0x02, 0x05, 0x09, 0x12, 0x1e, 0x04,
+ 0x18, 0x10, 0x08, 0x10, 0x20, 0x28, 0x34, 0x06, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ }
+ };
+
+ // animation
+ void animation_phase(void) {
+
+ // jump
+ if (isJumping || !showedJump) {
+
+ // clear
+ oled_set_cursor(LUNA_X,LUNA_Y +2);
+ oled_write(" ", false);
+
+ oled_set_cursor(LUNA_X,LUNA_Y -1);
+
+ showedJump = true;
+ } else {
+
+ // clear
+ oled_set_cursor(LUNA_X,LUNA_Y -1);
+ oled_write(" ", false);
+
+ oled_set_cursor(LUNA_X,LUNA_Y);
+ }
+
+ // switch frame
+ current_frame = (current_frame + 1) % 2;
+
+ // current status
+ if(led_usb_state.caps_lock) {
+ oled_write_raw_P(bark[abs(1 - current_frame)], ANIM_SIZE);
+
+ } else if(isSneaking) {
+ oled_write_raw_P(sneak[abs(1 - current_frame)], ANIM_SIZE);
+
+ } else if(current_wpm <= MIN_WALK_SPEED) {
+ oled_write_raw_P(sit[abs(1 - current_frame)], ANIM_SIZE);
+
+ } else if(current_wpm <= MIN_RUN_SPEED) {
+ oled_write_raw_P(walk[abs(1 - current_frame)], ANIM_SIZE);
+
+ } else {
+ oled_write_raw_P(run[abs(1 - current_frame)], ANIM_SIZE);
+ }
+ }
+
+ // animation timer
+ if(timer_elapsed32(anim_timer) > ANIM_FRAME_DURATION) {
+ anim_timer = timer_read32();
+ current_wpm = get_current_wpm();
+ animation_phase();
+ }
+
+ // this fixes the screen on and off bug
+ if (current_wpm > 0) {
+ oled_on();
+ anim_sleep = timer_read32();
+ } else if(timer_elapsed32(anim_sleep) > OLED_TIMEOUT) {
+ oled_off();
+ }
+
+}
+
+// KEYBOARD PET END \ No newline at end of file
diff --git a/users/snowe/luna.h b/users/snowe/luna.h
new file mode 100644
index 0000000000..c96d7a12df
--- /dev/null
+++ b/users/snowe/luna.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 QMK Community
+ * Copyright 2021 Tyler Thrailkill (@snowe/@snowe2010) <tyler.b.thrailkill@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/>.
+*/
+
+#pragma once
+
+extern bool isSneaking;
+extern bool isJumping;
+extern bool showedJump;
+
+// status variables
+extern led_t led_usb_state;
+//extern int current_wpm;
+
+
+void render_luna(int LUNA_X, int LUNA_Y);
+
diff --git a/users/snowe/ocean_dream.c b/users/snowe/ocean_dream.c
new file mode 100644
index 0000000000..2f372628da
--- /dev/null
+++ b/users/snowe/ocean_dream.c
@@ -0,0 +1,555 @@
+/*
+ * Copyright 2021 Tyler Thrailkill (@snowe/@snowe2010) <tyler.b.thrailkill@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 "ocean_dream.h"
+#include "quantum.h"
+#include "print.h"
+
+// Calculated Parameters
+#define TWINKLE_PROBABILITY_MODULATOR 100 / TWINKLE_PROBABILITY // CALCULATED: Don't Touch
+#define TOTAL_STARS STARS_PER_LINE *NUMBER_OF_STAR_LINES // CALCULATED: Don't Touch
+#define OCEAN_ANIMATION_MODULATOR NUMBER_OF_FRAMES / OCEAN_ANIMATION_SPEED // CALCULATED: Don't Touch
+#define SHOOTING_STAR_ANIMATION_MODULATOR NUMBER_OF_FRAMES / SHOOTING_STAR_ANIMATION_SPEED // CALCULATED: Don't Touch
+#define STAR_ANIMATION_MODULATOR NUMBER_OF_FRAMES / STAR_ANIMATION_SPEED // CALCULATED: Don't Touch
+
+uint8_t animation_counter = 0; // global animation counter.
+bool is_calm = false;
+uint32_t starry_night_anim_timer = 0;
+uint32_t starry_night_anim_sleep = 0;
+static int current_wpm = 0;
+
+static uint8_t increment_counter(uint8_t counter, uint8_t max) {
+ counter++;
+ if (counter >= max) {
+ return 0;
+ } else {
+ return counter;
+ }
+}
+
+#ifdef ENABLE_WAVE
+static uint8_t decrement_counter(uint8_t counter, uint8_t max) {
+ counter--;
+ if (counter < 0 || counter > max) {
+ return max;
+ } else {
+ return counter;
+ }
+}
+#endif
+
+#ifdef ENABLE_MOON // region
+# ifndef STATIC_MOON
+uint8_t moon_animation_frame = 0; // keeps track of current moon frame
+uint16_t moon_animation_counter = 0; // counts how many frames to wait before animating moon to next frame
+# endif
+
+# ifdef STATIC_MOON
+static const char PROGMEM moon[6] = {
+ 0x18, 0x7E, 0xFF, 0xC3, 0x81, 0x81,
+};
+# endif
+
+# ifndef STATIC_MOON
+static const char PROGMEM moon_animation[14][8] = {
+ // clang-format off
+ { 0x3C, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, },
+ { 0x3C, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0x42, 0x00, },
+ { 0x3C, 0x7E, 0xFF, 0xFF, 0xFF, 0xC3, 0x00, 0x00, },
+ { 0x3C, 0x7E, 0xFF, 0xFF, 0xC3, 0x81, 0x00, 0x00, },
+ { 0x3C, 0x7E, 0xFF, 0xC3, 0x81, 0x00, 0x00, 0x00, },
+ { 0x3C, 0x7E, 0xC3, 0x81, 0x81, 0x00, 0x00, 0x00, },
+ { 0x3C, 0x42, 0x81, 0x81, 0x00, 0x00, 0x00, 0x00, },
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
+ { 0x00, 0x00, 0x00, 0x00, 0x81, 0x81, 0x42, 0x3C, },
+ { 0x00, 0x00, 0x00, 0x81, 0x81, 0xC3, 0x7E, 0x3C, },
+ { 0x00, 0x00, 0x00, 0x81, 0xC3, 0xFF, 0x7E, 0x3C, },
+ { 0x00, 0x00, 0x81, 0xC3, 0xFF, 0xFF, 0x7E, 0x3C, },
+ { 0x00, 0x00, 0xC3, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, },
+ { 0x00, 0x42, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, },
+ // clang-format on
+};
+# endif
+
+static void draw_moon(void) {
+# ifdef STATIC_MOON
+ oled_set_cursor(MOON_COLUMN, MOON_LINE);
+ oled_write_raw_P(moon, 6);
+# endif
+# ifndef STATIC_MOON
+ moon_animation_counter = increment_counter(moon_animation_counter, ANIMATE_MOON_EVERY_N_FRAMES);
+ if (moon_animation_counter == 0) {
+ moon_animation_frame = increment_counter(moon_animation_frame, 14);
+ oled_set_cursor(MOON_COLUMN, MOON_LINE);
+ oled_write_raw_P(moon_animation[moon_animation_frame], 8);
+ }
+# endif
+}
+#endif // endregion
+
+#ifdef ENABLE_WAVE // region
+uint8_t starry_night_wave_frame_width_counter = 31;
+uint8_t rough_waves_frame_counter = 0;
+
+// clang-format off
+static const char PROGMEM ocean_top[8][32] = {
+ // still ocean
+ {
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ },
+ // small ripples
+ {
+ 0x20, 0x60, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x20, 0x60, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x20, 0x60, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x20, 0x60, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ },
+ // level 2 ripples
+ {
+ 0x20, 0x60, 0x40, 0x40, 0x20, 0x60, 0x40, 0x40,
+ 0x20, 0x60, 0x40, 0x40, 0x20, 0x60, 0x40, 0x40,
+ 0x20, 0x60, 0x40, 0x40, 0x20, 0x60, 0x40, 0x40,
+ 0x20, 0x60, 0x40, 0x40, 0x20, 0x60, 0x40, 0x40,
+ },
+ // level 3 waves
+ {
+ 0x40, 0x20, 0x10, 0x20, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x20, 0x10, 0x20, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x20, 0x10, 0x20, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x20, 0x10, 0x20, 0x40, 0x40, 0x40, 0x40,
+ },
+ {
+ 0x40, 0x40, 0x20, 0x10, 0x28, 0x50, 0x40, 0x40,
+ 0x40, 0x40, 0x20, 0x10, 0x28, 0x50, 0x40, 0x40,
+ 0x40, 0x40, 0x20, 0x10, 0x28, 0x50, 0x40, 0x40,
+ 0x40, 0x40, 0x20, 0x10, 0x28, 0x50, 0x40, 0x40,
+ },
+ {
+ 0x40, 0x40, 0x40, 0x20, 0x10, 0x30, 0x70, 0x60,
+ 0x40, 0x40, 0x40, 0x20, 0x10, 0x30, 0x70, 0x60,
+ 0x40, 0x40, 0x40, 0x20, 0x10, 0x30, 0x70, 0x60,
+ 0x40, 0x40, 0x40, 0x20, 0x10, 0x30, 0x70, 0x60,
+ },
+};
+static const char PROGMEM ocean_bottom[8][32] = {
+ // still ocean
+ {
+ 0x00, 0x40, 0x40, 0x41, 0x01, 0x01, 0x01, 0x21,
+ 0x20, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x44,
+ 0x44, 0x40, 0x40, 0x00, 0x00, 0x08, 0x08, 0x00,
+ 0x01, 0x01, 0x01, 0x00, 0x40, 0x40, 0x00, 0x00,
+ },
+ // small ripples
+ {
+ 0x00, 0x00, 0x40, 0x40, 0x01, 0x01, 0x01, 0x20,
+ 0x20, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04,
+ 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x01, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00,
+ },
+ // level 2 ripples
+ {
+ 0x00, 0x00, 0x40, 0x40, 0x01, 0x01, 0x01, 0x20,
+ 0x20, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04,
+ 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x01, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00,
+ },
+ // level 3 waves
+ {
+ 0x00, 0x40, 0x40, 0x42, 0x42, 0x03, 0x11, 0x11,
+ 0x20, 0x20, 0x00, 0x00, 0x08, 0x0C, 0x0C, 0x04,
+ 0x05, 0x41, 0x41, 0x21, 0x20, 0x00, 0x00, 0x08,
+ 0x0A, 0x0A, 0x0B, 0x41, 0x41, 0x41, 0x41, 0x00,
+ },
+ {
+ 0x10, 0x10, 0x00, 0x80, 0x84, 0xC4, 0x02, 0x06,
+ 0x84, 0x44, 0xC0, 0x80, 0x80, 0x20, 0x20, 0x10,
+ 0x08, 0x12, 0x91, 0x81, 0x42, 0x40, 0x00, 0x00,
+ 0x10, 0x12, 0x22, 0x22, 0x24, 0x04, 0x84, 0x80,
+ },
+ {
+ 0x08, 0x80, 0x80, 0x82, 0x82, 0x03, 0x21, 0x21,
+ 0x10, 0x10, 0x00, 0x00, 0x04, 0x04, 0x0C, 0x08,
+ 0x09, 0x41, 0x42, 0x22, 0x20, 0x00, 0x00, 0x08,
+ 0x0A, 0x0A, 0x0B, 0x41, 0x43, 0x42, 0x42, 0x00,
+ },
+};
+// clang-format on
+
+static void animate_waves(void) {
+ starry_night_wave_frame_width_counter = decrement_counter(starry_night_wave_frame_width_counter, WIDTH - 1); // only 3 frames for last wave type
+ rough_waves_frame_counter = increment_counter(rough_waves_frame_counter, 3); // only 3 frames for last wave type
+
+ void draw_ocean(uint8_t frame, uint16_t offset, uint8_t byte_index) {
+ oled_write_raw_byte(pgm_read_byte(ocean_top[frame] + byte_index), offset);
+ oled_write_raw_byte(pgm_read_byte(ocean_bottom[frame] + byte_index), offset + WIDTH);
+ }
+
+ for (int i = 0; i < WIDTH; ++i) {
+ uint16_t offset = OCEAN_LINE * WIDTH + i;
+ uint8_t byte_index = starry_night_wave_frame_width_counter + i;
+ if (byte_index >= WIDTH) {
+ byte_index = byte_index - WIDTH;
+ }
+ if (is_calm || current_wpm <= WAVE_CALM) {
+ draw_ocean(0, offset, byte_index);
+ } else if (current_wpm <= WAVE_HEAVY_STORM) {
+ draw_ocean(1, offset, byte_index);
+ } else if (current_wpm <= WAVE_HURRICANE) {
+ draw_ocean(2, offset, byte_index);
+ } else {
+ draw_ocean(3 + rough_waves_frame_counter, offset, byte_index);
+ }
+ }
+}
+#endif // endregion
+
+#ifdef ENABLE_ISLAND // region
+uint8_t island_frame_1 = 0;
+
+// clang-format off
+// only use 46 bytes (first 18 are blank, so we don't write them, makes it smaller and we can see the shooting stars properly!)
+
+// To save space and allow the shooting stars to be seen, only draw the tree on every frame.
+// Tree is only 14bytes wide so we save 108 bytes on just the first row. Second row, the
+// first 18 bytes is always the same piece of land, so only store that once, which saves 90 bytes
+static const char PROGMEM islandRightTop[6][14] = {
+ {0x84, 0xEC, 0x6C, 0x3C, 0xF8, 0xFE, 0x3F, 0x6B, 0xDB, 0xB9, 0x30, 0x40, 0x00, 0x00,},
+ {0x80, 0xC3, 0xEE, 0x7C, 0xB8, 0xFC, 0xFE, 0x6F, 0xDB, 0x9B, 0xB2, 0x30, 0x00, 0x00,},
+ {0x00, 0xC0, 0xEE, 0x7F, 0x3D, 0xF8, 0xFC, 0x7E, 0x57, 0xDB, 0xDB, 0x8A, 0x00, 0x00,},
+ {0x00, 0xC0, 0xE6, 0x7F, 0x3B, 0xF9, 0xFC, 0xFC, 0xB6, 0xB3, 0x33, 0x61, 0x00, 0x00,},
+ {0x00, 0x00, 0x00, 0x00, 0x80, 0xEE, 0xFF, 0xFB, 0xF9, 0xFC, 0xDE, 0xB6, 0xB6, 0x24,},
+ {0x00, 0x00, 0x00, 0x00, 0xC0, 0xEE, 0xFE, 0xFF, 0xFB, 0xFD, 0xEE, 0xB6, 0xB6, 0x92,},
+};
+static const char PROGMEM islandRightBottom[6][14] = {
+ {0x41, 0x40, 0x60, 0x3E, 0x3F, 0x23, 0x20, 0x60, 0x41, 0x43, 0x40, 0x40, 0x40, 0x80,},
+ {0x40, 0x41, 0x60, 0x3E, 0x3F, 0x23, 0x20, 0x60, 0x40, 0x40, 0x41, 0x41, 0x40, 0x80,},
+ {0x40, 0x40, 0x61, 0x3D, 0x3F, 0x27, 0x21, 0x60, 0x40, 0x40, 0x40, 0x40, 0x40, 0x80,},
+ {0x40, 0x43, 0x61, 0x3C, 0x3F, 0x27, 0x21, 0x60, 0x41, 0x43, 0x43, 0x42, 0x40, 0x80,},
+ {0x40, 0x40, 0x60, 0x3C, 0x3F, 0x27, 0x23, 0x63, 0x44, 0x40, 0x41, 0x41, 0x41, 0x81,},
+ {0x40, 0x40, 0x60, 0x3C, 0x3F, 0x27, 0x23, 0x63, 0x42, 0x42, 0x41, 0x41, 0x41, 0x80,},
+};
+static const char PROGMEM islandLeft[18] = {
+ 0x80, 0x40, 0x40, 0x40, 0x40, 0x60,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x60, 0x40, 0x40,
+};
+// clang-format on
+
+static void animate_island(void) {
+ if (animation_counter == 0) {
+ island_frame_1 = increment_counter(island_frame_1, 2);
+ }
+
+ void draw_island_parts(uint8_t frame) {
+ oled_set_cursor(ISLAND_COLUMN + 3, ISLAND_LINE);
+ oled_write_raw_P(islandRightTop[frame], 14);
+ oled_set_cursor(ISLAND_COLUMN + 0, ISLAND_LINE + 1);
+ oled_write_raw_P(islandLeft, 18);
+ oled_set_cursor(ISLAND_COLUMN + 3, ISLAND_LINE + 1);
+ oled_write_raw_P(islandRightBottom[frame], 14);
+ }
+
+ if (is_calm || current_wpm < ISLAND_CALM) {
+ draw_island_parts(0);
+ } else if (current_wpm >= ISLAND_CALM && current_wpm < ISLAND_HEAVY_STORM) {
+ draw_island_parts(island_frame_1 + 1);
+ } else if (current_wpm >= ISLAND_HEAVY_STORM && current_wpm < ISLAND_HURRICANE) {
+ draw_island_parts(island_frame_1 + 2);
+ } else {
+ draw_island_parts(island_frame_1 + 4);
+ }
+}
+#endif // endregion
+
+#ifdef ENABLE_STARS // region
+bool stars_setup = false; // only setup stars once, then we just twinkle them
+struct Coordinate {
+ int x;
+ int y;
+ bool exists;
+};
+
+struct Coordinate stars[TOTAL_STARS]; // tracks all stars/coordinates
+
+/**
+ * Setup all the initial stars on the screen
+ * This function divides the screen into regions based on STARS_PER_LINE and NUMBER_OF_STAR_LINES
+ * where each line is made up of 8x8 pixel groups, that are populated by a single star.
+ *
+ * Not sure how this function will work with larger or smaller screens.
+ * It should be fine, as long as the screen width is a multiple of 8
+ */
+static void setup_stars(void) {
+ // For every line, split the line into STARS_PER_LINE, find a random point in that region, and turn the pixel on
+ // 36% probability it will not be added
+ // (said another way, 80% chance it will start out lit in the x direction, then 80% chance it will start out lit in the y direction = 64% probability it will start out lit at all)
+ for (int line = 0; line < NUMBER_OF_STAR_LINES; ++line) {
+ for (int column_group = 0; column_group < STARS_PER_LINE; ++column_group) {
+ uint8_t rand_column = rand() % 10;
+ uint8_t rand_row = rand() % 10;
+ if (rand_column < 8 && rand_row < 8) {
+ int column_adder = column_group * 8;
+ int line_adder = line * 8;
+ int x = rand_column + column_adder;
+ int y = rand_row + line_adder;
+ oled_write_pixel(x, y, true);
+ stars[column_group + (line * STARS_PER_LINE)].x = x;
+ stars[column_group + (line * STARS_PER_LINE)].y = y;
+ stars[column_group + (line * STARS_PER_LINE)].exists = true;
+ } else {
+ stars[column_group + (line * STARS_PER_LINE)].exists = false;
+ }
+ }
+ }
+ stars_setup = true;
+}
+
+/**
+ * Twinkle the stars (move them one pixel in any direction) with a probability of 50% to twinkle any given star
+ */
+static void twinkle_stars(void) {
+ for (int line = 0; line < NUMBER_OF_STAR_LINES; ++line) {
+ for (int column_group = 0; column_group < STARS_PER_LINE; ++column_group) {
+ struct Coordinate star = stars[column_group + (line * STARS_PER_LINE)];
+
+ // skip stars that were never added
+ if (!star.exists) {
+ continue;
+ }
+ if (rand() % TWINKLE_PROBABILITY_MODULATOR == 0) {
+ oled_write_pixel(star.x, star.y, false); // black out pixel
+
+ // don't allow stars to leave their own region
+ if (star.x == (column_group * 8)) { // star is the farthest left it can go in its region
+ star.x++; // move it right immediately
+ } else if (star.x == (((column_group + 1) * 8) - 1)) { // star is farthest right it can go in its region
+ star.x--; // move it left immediately
+ }
+ if (star.y == (line * 8)) { // star is the farthest up it can go in its region
+ star.y++; // move it down immediately
+ } else if (star.y == (((line + 1) * 8) - 1)) { // star is farthest down it can go in its region
+ star.y--; // move it up immediately
+ }
+
+ // now decide direction
+ int new_x;
+ int x_choice = rand() % 3;
+ if (x_choice == 0) {
+ new_x = star.x - 1;
+ } else if (x_choice == 1) {
+ new_x = star.x + 1;
+ } else {
+ new_x = star.x;
+ }
+
+ int new_y;
+ int y_choice = rand() % 3;
+ if (y_choice == 0) {
+ new_y = star.y - 1;
+ } else if (y_choice == 1) {
+ new_y = star.y + 1;
+ } else {
+ new_y = star.y;
+ }
+
+ star.x = new_x;
+ star.y = new_y;
+ oled_write_pixel(new_x, new_y, true);
+ }
+
+ stars[column_group + (line * STARS_PER_LINE)] = star;
+ }
+ }
+}
+
+/**
+ * Setup the stars and then animate them on subsequent frames
+ */
+static void animate_stars(void) {
+ if (!stars_setup) {
+ setup_stars();
+ } else {
+ twinkle_stars();
+ }
+}
+#endif // endregion
+
+#ifdef ENABLE_SHOOTING_STARS // region
+bool shooting_stars_setup = false; // only setup shooting stars array once with defaults
+
+struct ShootingStar {
+ int x_1;
+ int y_1;
+ int x_2;
+ int y_2;
+ bool running;
+ int frame;
+ int delay;
+};
+
+struct ShootingStar shooting_stars[MAX_NUMBER_OF_SHOOTING_STARS]; // tracks all the shooting stars
+
+static void setup_shooting_star(struct ShootingStar *shooting_star) {
+ int column_to_start = rand() % (WIDTH / 2);
+ int row_to_start = rand() % (HEIGHT - 48); // shooting_stars travel diagonally 1 down, 1 across. So the lowest a shooting_star can start and not 'hit' the ocean is 32 above the ocean.
+
+ shooting_star->x_1 = column_to_start;
+ shooting_star->y_1 = row_to_start;
+ shooting_star->x_2 = column_to_start + 1;
+ shooting_star->y_2 = row_to_start + 1;
+ shooting_star->running = true;
+ shooting_star->frame++;
+ shooting_star->delay = rand() % SHOOTING_STAR_DELAY;
+}
+
+static void move_shooting_star(struct ShootingStar *shooting_star) {
+ oled_write_pixel(shooting_star->x_1, shooting_star->y_1, false);
+ oled_write_pixel(shooting_star->x_2, shooting_star->y_2, false);
+
+ shooting_star->x_1++;
+ shooting_star->y_1++;
+ shooting_star->x_2++;
+ shooting_star->y_2++;
+ shooting_star->frame++;
+
+ oled_write_pixel(shooting_star->x_1, shooting_star->y_1, true);
+ oled_write_pixel(shooting_star->x_2, shooting_star->y_2, true);
+}
+
+static void finish_shooting_star(struct ShootingStar *shooting_star) {
+ oled_write_pixel(shooting_star->x_1, shooting_star->y_1, false);
+ oled_write_pixel(shooting_star->x_2, shooting_star->y_2, false);
+ shooting_star->running = false;
+ shooting_star->frame = 0;
+}
+
+static void animate_shooting_star(struct ShootingStar *shooting_star) {
+ if (shooting_star->frame > SHOOTING_STAR_FRAMES) {
+ finish_shooting_star(shooting_star);
+ return;
+ } else if (!shooting_star->running) {
+ setup_shooting_star(shooting_star);
+ } else {
+ if (shooting_star->delay == 0) {
+ move_shooting_star(shooting_star);
+ } else {
+ shooting_star->delay--;
+ }
+ }
+}
+
+static void animate_shooting_stars(void) {
+ if (is_calm) {
+ return;
+ }
+ if (!shooting_stars_setup) {
+ for (int i = 0; i < MAX_NUMBER_OF_SHOOTING_STARS; ++i) {
+ shooting_stars[i].running = false;
+ }
+ shooting_stars_setup = true;
+ }
+ /**
+ * Fixes issue with stars that were falling _while_ the
+ * wpm dropped below the condition for them to keep falling
+ */
+ void end_extra_stars(uint8_t starting_index) {
+ for (int shooting_star_index = starting_index; shooting_star_index < MAX_NUMBER_OF_SHOOTING_STARS; ++shooting_star_index) {
+ struct ShootingStar shooting_star = shooting_stars[shooting_star_index];
+ if (shooting_star.running) {
+ finish_shooting_star(&shooting_star);
+ shooting_stars[shooting_star_index] = shooting_star;
+ }
+ }
+ }
+
+ int number_of_shooting_stars = current_wpm / SHOOTING_STAR_WPM_INCREMENT;
+ number_of_shooting_stars = (number_of_shooting_stars > MAX_NUMBER_OF_SHOOTING_STARS) ? MAX_NUMBER_OF_SHOOTING_STARS : number_of_shooting_stars;
+
+ if (number_of_shooting_stars == 0) {
+ // make sure all shooting_stars are ended
+ end_extra_stars(0);
+ } else {
+ for (int shooting_star_index = 0; shooting_star_index < number_of_shooting_stars; ++shooting_star_index) {
+ struct ShootingStar shooting_star = shooting_stars[shooting_star_index];
+ animate_shooting_star(&shooting_star);
+ shooting_stars[shooting_star_index] = shooting_star;
+ }
+ end_extra_stars(number_of_shooting_stars);
+ }
+}
+#endif // endregion
+
+/**
+ * Main rendering function
+ *
+ * Calls all different animations at different rates
+ */
+void render_stars(void) {
+ // // animation timer
+ if (timer_elapsed32(starry_night_anim_timer) > STARRY_NIGHT_ANIM_FRAME_DURATION) {
+ starry_night_anim_timer = timer_read32();
+ current_wpm = get_current_wpm();
+
+#ifdef ENABLE_ISLAND
+ animate_island();
+#endif
+
+#ifdef ENABLE_SHOOTING_STARS
+ if (animation_counter % SHOOTING_STAR_ANIMATION_MODULATOR == 0) {
+ animate_shooting_stars();
+ }
+#endif
+
+#ifdef ENABLE_STARS
+ // TODO offsetting the star animation from the wave animation would look better,
+ // but if I do that, then the stars appear in the water because
+ // the ocean animation has to wait a bunch of frames to overwrite it.
+ // Possible solutions:
+ // 1. Only draw stars to the top of the island/ocean.
+ // 2. Draw ocean every frame, only move ocean on frames matching modulus
+ // Problems:
+ // 1. What if someone wants to move the island up a bit, or they want to have the stars reflect in the water?
+ // 2. More cpu intensive. And I'm already running out of cpu as it is...
+ if (animation_counter % STAR_ANIMATION_MODULATOR == 0) {
+ animate_stars();
+ }
+#endif
+
+#ifdef ENABLE_WAVE
+ if (animation_counter % OCEAN_ANIMATION_MODULATOR == 0) {
+ animate_waves();
+ }
+#endif
+
+#ifdef ENABLE_MOON
+ draw_moon();
+#endif
+
+ animation_counter = increment_counter(animation_counter, NUMBER_OF_FRAMES);
+ }
+
+ // this fixes the screen on and off bug
+ if (current_wpm > 0) {
+ oled_on();
+ starry_night_anim_sleep = timer_read32();
+ } else if (timer_elapsed32(starry_night_anim_sleep) > OLED_TIMEOUT) {
+ oled_off();
+ }
+}
diff --git a/users/snowe/ocean_dream.h b/users/snowe/ocean_dream.h
new file mode 100644
index 0000000000..498375559a
--- /dev/null
+++ b/users/snowe/ocean_dream.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2021 Tyler Thrailkill (@snowe/@snowe2010) <tyler.b.thrailkill@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/>.
+ */
+
+#pragma once
+#include "quantum.h"
+
+/**
+ * Features:
+ * You can turn on and off features in this section
+ */
+#define ENABLE_MOON // Uses 182 bytes
+#define ENABLE_WAVE // Uses 844 bytes
+#define ENABLE_SHOOTING_STARS // Uses 872 bytes
+#define ENABLE_ISLAND
+#define ENABLE_STARS // Uses 606 bytes
+
+/**
+ * Global Settings
+ */
+#define STARRY_NIGHT_ANIM_FRAME_DURATION 30 // how long each frame lasts in ms
+#define NUMBER_OF_FRAMES 20 // Self explanatory. Probably shouldn't touch this, not sure how stuff will work if it's changed. If changed should be multiple of 1, 2, 3, 4, and 5
+#define WIDTH OLED_DISPLAY_HEIGHT // for vertical displays
+#define HEIGHT OLED_DISPLAY_WIDTH // for vertical displays
+
+/**
+ * Moon Parameters
+ */
+#define MOON_LINE 4 // the line you want the moon to appear at
+#define MOON_COLUMN 4 // the column you want the moon to appear at
+//#define STATIC_MOON // uncomment this to make the moon a static image, no animation
+#ifndef STATIC_MOON
+# define ANIMATE_MOON_EVERY_N_FRAMES 100 // animate the moon every n frames
+#endif
+
+/**
+ * Wave Parameters
+ */
+#define OCEAN_LINE 14 // Line you want to render the ocean starting at (best at oled_max_lines() - 2)
+#define WAVE_CALM 20 // render calm ocean under this WPM and ripple ocean at this WPM
+#define WAVE_HEAVY_STORM 40 // render medium ocean at this WPM
+#define WAVE_HURRICANE 60 // render heavy waves above this WPM
+// What number of frames you want to animate the ocean at.
+// Should be equal to or smaller than NUMBER_OF_FRAMES, e.g. 30, would animate on every other frame, 20, every third frame, etc
+// Don't set equal to 0.
+#define OCEAN_ANIMATION_SPEED 1
+
+/**
+ * Shooting Star Parameters
+ */
+#define SHOOTING_STAR_DELAY 12 // delay modulus for time between shooting stars. Decides number of frames to delay, e.g. 12 means 0-11 frames of delay between each shooting star
+#define SHOOTING_STAR_FRAMES 16 // how many 2 pixel frames per shooting star. Increment this for longer shooting stars
+#define MAX_NUMBER_OF_SHOOTING_STARS 12 // maximum number of shooting stars that can be on screen at the same time
+#define SHOOTING_STAR_WPM_INCREMENT 10 // every n WPM increase, add an extra star, up to MAX_NUMBER_OF_SHOOTING_STARS, e.g. an increment of 5 would result in 1 shooting star at 5-9wpm, 2 at 10-14, etc.
+// What number of frames you want to animate the shooting stars at.
+// Should be equal to or smaller than NUMBER_OF_FRAMES, e.g. 30, would animate on every other frame, 20, every third frame, etc
+// Don't set equal to 0.
+#define SHOOTING_STAR_ANIMATION_SPEED 30
+
+/**
+ * Star Parameters
+ */
+#define STARS_PER_LINE 4 // number of stars per line (for a display that is 128x32, this would be 4 stars spread out evenly over the 32byte width, one every 8 bytes)
+#define NUMBER_OF_STAR_LINES 16 // number of lines to fill up with stars (for a display that is 128x32, 16 bytes are filled with the ocean animation, so that leaves 112 pixels left over. The number of lines depends on your OLED_FONT_HEIGHT)
+#define TWINKLE_PROBABILITY 25 // probability that any star twinkles on a given frame
+// What number of frames you want to animate the stars at.
+// Should be equal to or smaller than NUMBER_OF_FRAMES, e.g. 20, would animate on every frame, 10, every other frame, etc
+// Don't set equal to 0.
+#define STAR_ANIMATION_SPEED 1
+
+/**
+ * Island Parameters
+ */
+#define ISLAND_LINE 12 // line that the island starts at. Island is 2 lines tall
+#define ISLAND_COLUMN 0 // column that the island starts at
+#define ISLAND_CALM 20 // WPM at which the palm tree calmly moves
+#define ISLAND_HEAVY_STORM 40 // WPM at which the heavy storm occurs
+#define ISLAND_HURRICANE 60 // WPM at which THE HURRICANE STARTS
+
+/*
+ * DON'T TOUCH
+ */
+
+extern bool is_calm;
+
+// timers
+extern uint32_t starry_night_anim_timer;
+extern uint32_t starry_night_anim_sleep;
+
+void render_stars(void);
diff --git a/users/snowe/oled_setup.c b/users/snowe/oled_setup.c
new file mode 100644
index 0000000000..b3e04df458
--- /dev/null
+++ b/users/snowe/oled_setup.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright QMK Community
+ * Copyright 2021 Tyler Thrailkill (@snowe/@snowe2010) <tyler.b.thrailkill@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/>.
+ */
+
+#ifdef OLED_DRIVER_ENABLE
+
+# include QMK_KEYBOARD_H
+# include "quantum.h"
+# include "snowe.h"
+
+# include <stdio.h> // for keylog?
+
+oled_rotation_t oled_init_user(oled_rotation_t rotation) {
+ if (!is_master) {
+ return OLED_ROTATION_270; // flips the display 180 degrees if offhand
+ }
+ return OLED_ROTATION_270;
+}
+
+# define L_BASE 0
+# define L_LOWER 2
+# define L_RAISE 4
+# define L_ADJUST 8
+
+void oled_render_layer_state(void) {
+ oled_write_P(PSTR("Layer"), false);
+ switch (layer_state) {
+ case L_BASE:
+ oled_write_ln_P(PSTR("Main"), false);
+ break;
+ case L_LOWER:
+ oled_write_ln_P(PSTR("Bot"), false);
+ break;
+ case L_RAISE:
+ oled_write_ln_P(PSTR("Top"), false);
+ break;
+ case L_ADJUST:
+ case L_ADJUST | L_LOWER:
+ case L_ADJUST | L_RAISE:
+ case L_ADJUST | L_LOWER | L_RAISE:
+ oled_write_ln_P(PSTR("Comb"), false);
+ break;
+ }
+}
+
+char keylog_str[24] = {};
+
+const char code_to_name[60] = {
+ ' ', ' ', ' ', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
+ 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
+ 'R', 'E', 'B', 'T', '_', '-', '=', '[', ']', '\\',
+ '#', ';', '\'', '`', ',', '.', '/', ' ', ' ', ' '};
+
+void set_keylog(uint16_t keycode, keyrecord_t *record) {
+ char name = ' ';
+ if ((keycode >= QK_MOD_TAP && keycode <= QK_MOD_TAP_MAX) || (keycode >= QK_LAYER_TAP && keycode <= QK_LAYER_TAP_MAX)) {
+ keycode = keycode & 0xFF;
+ }
+ if (keycode < 60) {
+ name = code_to_name[keycode];
+ }
+
+ // update keylog
+ snprintf(keylog_str, sizeof(keylog_str), "%dx%d, k%2d : %c", record->event.key.row, record->event.key.col, keycode, name);
+}
+
+void oled_render_keylog(void) { oled_write(keylog_str, false); }
+
+// void render_bootmagic_status(bool status) {
+// /* Show Ctrl-Gui Swap options */
+// static const char PROGMEM logo[][2][3] = {
+// {{0x97, 0x98, 0}, {0xb7, 0xb8, 0}},
+// {{0x95, 0x96, 0}, {0xb5, 0xb6, 0}},
+// };
+// if (status) {
+// oled_write_ln_P(logo[0][0], false);
+// oled_write_ln_P(logo[0][1], false);
+// } else {
+// oled_write_ln_P(logo[1][0], false);
+// oled_write_ln_P(logo[1][1], false);
+// }
+//}
+void render_bootmagic_status(void) {
+ /* Show Ctrl-Gui Swap options */
+ static const char PROGMEM logo[][2][3] = {
+ {{0x97, 0x98, 0}, {0xb7, 0xb8, 0}},
+ {{0x95, 0x96, 0}, {0xb5, 0xb6, 0}},
+ };
+ oled_write_P(PSTR("BTMGK"), false);
+ oled_write_P(PSTR(""), false);
+ if (!keymap_config.swap_lctl_lgui) {
+ oled_write_P(logo[1][0], false);
+ oled_write_P(PSTR(" "), false);
+ oled_write_P(logo[1][1], false);
+ } else {
+ oled_write_P(logo[0][0], false);
+ oled_write_P(PSTR(" "), false);
+ oled_write_P(logo[0][1], false);
+ }
+ oled_write_P(PSTR(" NKRO "), keymap_config.nkro);
+ oled_write_P(PSTR("WPM: "), false);
+
+ char wpm[6];
+ itoa(get_current_wpm(), wpm, 10);
+ oled_write_ln(wpm, false);
+}
+
+void oled_task_user(void) {
+ if (is_master) {
+ oled_render_layer_state();
+ oled_render_keylog();
+ render_bootmagic_status();
+
+# ifdef LUNA_ENABLE
+ led_usb_state = host_keyboard_led_state();
+ render_luna(0, 13);
+# endif
+ } else {
+# ifdef OCEAN_DREAM_ENABLE
+ render_stars();
+# endif
+ }
+}
+
+#endif // OLED_DRIVER_ENABLE
diff --git a/users/snowe/oled_setup.h b/users/snowe/oled_setup.h
new file mode 100644
index 0000000000..031ce6bd08
--- /dev/null
+++ b/users/snowe/oled_setup.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2021 Tyler Thrailkill (@snowe/@snowe2010) <tyler.b.thrailkill@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/>.
+ */
+
+#pragma once
+
+#include "quantum.h"
+#ifdef OLED_DRIVER_ENABLE
+# include "oled_driver.h"
+# define OLED_RENDER_WPM_COUNTER " WPM: "
+#endif
+#ifdef LUNA_ENABLE
+# include "luna.h"
+#endif
+#ifdef OCEAN_DREAM_ENABLE
+# include "ocean_dream.h"
+#endif \ No newline at end of file
diff --git a/users/snowe/readme.md b/users/snowe/readme.md
new file mode 100644
index 0000000000..ff45c9df29
--- /dev/null
+++ b/users/snowe/readme.md
@@ -0,0 +1,10 @@
+# Welcome
+
+Welcome to my userspace.
+
+There's nothing really outstanding here, except the [Ocean Dream](readme_ocean_dream.md)
+animation I made.
+
+A lot of the code for my wrappers/keymaps came from other users.
+
+I will probably update this with more in-depth information later.
diff --git a/users/snowe/readme_ocean_dream.md b/users/snowe/readme_ocean_dream.md
new file mode 100644
index 0000000000..ca15dd47cd
--- /dev/null
+++ b/users/snowe/readme_ocean_dream.md
@@ -0,0 +1,258 @@
+# Ocean Dream
+
+Tapping away at your IMX Corne with Box Jades, you feel yourself
+drifting off, into a soundscape of waves and rustling leaves.
+You open your eyes only to find yourself in an _Ocean Dream_.
+
+Introducing, **Ocean Dream**, an animation for keyboards with tall OLED
+screens. Built for 128x32 screens, this animation should work for 128x64
+at least, though it hasn't been tested there.
+
+Completely customizable, you can change everything about the animation,
+from the number of falling stars, to how likely a star is to twinkle, and
+even if the moon has phases or not.
+
+# Installation
+
+Installation is easy.
+
+1. Add `ocean.h` and `ocean.c` to your keyboard folder or userspace.
+2. In your `keymap.c` or wherever you handle your oled code, add
+```c
+# ifdef OCEAN_DREAM_ENABLE
+ render_stars();
+# endif
+```
+to `oled_task_user(void)`, where you would like (see [my keymap](../../keyboards/crkbd/keymaps/snowe/keymap.c) for an example)
+3. In your `keymap.c` or wherever you handle your process_record code,
+ add an event that sets `is_calm` when you press `ctrl`
+```c
+bool process_record_user(uint16_t keycode, keyrecord_t *record) {
+ switch (keycode) {
+ case KC_LCTL:
+ case KC_RCTL:
+#ifdef OCEAN_DREAM_ENABLE
+ is_calm = (record->event.pressed) ? true : false;
+#endif
+ break;
+ }
+ return true;
+}
+```
+4. In your `rules.mk` to make it easier to turn the animation on/off, add
+```makefile
+ifeq ($(strip $(OLED_DRIVER_ENABLE)), yes)
+ #... your code here...
+
+ ifdef OCEAN_DREAM_ENABLE
+ ifeq ($(strip $(OCEAN_DREAM_ENABLE)), yes)
+ SRC += ocean_dream.c
+ OPT_DEFS += -DOCEAN_DREAM_ENABLE
+ endif
+ endif
+ ifndef OCEAN_DREAM_ENABLE
+ SRC += ocean_dream.c
+ OPT_DEFS += -DOCEAN_DREAM_ENABLE
+ endif
+endif
+```
+
+You're done! Now you can enable **Ocean Dream** by simply turning on the OLED feature
+```makefile
+OLED_DRIVER_ENABLE = yes
+```
+
+And if you want to disable it without turning off the OLED Driver you can simply set
+```makefile
+OCEAN_DREAM_ENABLE = no
+```
+
+# Settings
+
+**Ocean Dream** comes with several different animations, all individually configurable:
+
+* 🌌 Stars that twinkle
+* 🌠 Meteor showers that get more vibrant the faster you type
+* 🌊 Waves that get rougher the faster you type
+* 🏝 An island with a palm tree that blows in the wind the faster you type
+* 🌗 A moon that goes through the moon phases (or not, your choice!)
+
+Each feature can be individually turned on and off, with a simple `#define`.
+
+You can see all the options and more documentation by looking at `ocean_dream.h`.
+
+All options come enabled by default.
+
+## Global Flags:
+
+### Toggles:
+
+You can toggle on/off any features using these flags:
+
+* `ENABLE_MOON` // Uses 182 bytes
+* `ENABLE_WAVE` // Uses 844 bytes
+* `ENABLE_SHOOTING_STARS` // Uses 872 bytes
+* `ENABLE_ISLAND`
+* `ENABLE_STARS` // Uses 606 bytes
+
+### Global Configuration:
+
+* `STARRY_NIGHT_ANIM_FRAME_DURATION` - configures how long each frame lasts in ms
+* `NUMBER_OF_FRAMES` - configures the number of frames that constitute a single 'round trip' of animation.
+ Enables keeping animations in sync/timed with each other.
+ Probably shouldn't touch this, not sure how stuff will work if it's changed.
+ If changed should probably be multiple of 1, 2, 3, 4, and 5
+* `WIDTH` - for vertical displays, configures the width (the shortest measurement of your display). defaults to `OLED_DISPLAY_HEIGHT`
+* `HEIGHT` - for vertical displays, configures the height (the longest measurement of your display). defaults to `OLED_DISPLAY_WIDTH`
+
+## Stars
+
+### Description
+
+The 🌌 stars animation is a background of slowly twinkling stars.
+The stars are built on a grid of sorts, where each cell of the grid
+is 8x8 pixels with 1 star per cell. There is a probability of any
+star twinkling on any given frame, decided by the corresponding flags.
+
+### Flags
+
+Enabled with the `#define ENABLE_STARS` directive.
+
+The stars come with several configuration options:
+
+* `STARS_PER_LINE` - configures the number of stars per line. Defaulting to 4, this would
+ mean that you have 4 stars across each line (8x32 on a 128x32 display), where each star
+ is in a 8x8 grid
+* `NUMBER_OF_STAR_LINES` - configures the number of lines to fill up with stars.
+ Defaults to 16, filling the whole display.
+* `TWINKLE_PROBABILITY` - configures the probability of a star twinkling on a given frame.
+* `STAR_ANIMATION_SPEED` - configures the number of frames you want to animate the star at.
+ Must be equal to or lower than `NUMBER_OF_FRAMES`.
+ Example:
+ ```c
+ #define NUMBER_OF_FRAMES 20
+ #define STAR_ANIMATION_SPEED 5
+ ```
+ would result in the star animation happening every 4 frames. 20 would result in every frame,
+ 1 would be once every 20 frames. This essentially changes how fast stars will twinkle, but
+ does not change the probability of the stars twinkling.
+
+## Moon
+
+### Description
+
+The 🌗 moon animation is an 8x8 animation of a moon, or, if you are running out of program memory, you
+can set it to just a static crescent moon, with no animation.
+
+### Flags
+
+Enabled with the `#define ENABLE_MOON` directive.
+
+The moon comes with only a few configuration options:
+
+* `STATIC_MOON` - include this directive if you want your moon to have no animation. It will simply be a static crescent
+ moon, only taking up 6 bytes of space. If you do not include this directive, then the moon will have an animation.
+ The default is a moon with animation.
+* `MOON_LINE` - defines the line that the moon sits on. Default is `4`. (see [reference](#reference))
+* `MOON_COLUMN` - defines the column that the moon sits at. Default is `4`. (see [reference](#reference))
+* `ANIMATE_MOON_EVERY_N_FRAMES` - only enabled when `STATIC_MOON` is disabled, this affects how often the moon changes phases.
+ Example:
+ ```c
+ #define STARRY_NIGHT_ANIM_FRAME_DURATION 30
+ #ifndef STATIC_MOON
+ # define ANIMATE_MOON_EVERY_N_FRAMES 100
+ #endif
+ ```
+ would result in the moon changing phases every 3000ms, or every 3 seconds.
+
+## Ocean
+
+### Description
+
+The 🌊 ocean animation is a full width animation of ocean waves, where the waves get rougher the faster you type.
+You can configure the boundaries for each degree of _wave ferocity_ as well as how fast the ocean/waves move.
+
+### Flags
+
+* `OCEAN_LINE` - where to render the ocean at. Defaults to `14`. (see [reference](#reference))
+* `OCEAN_ANIMATION_SPEED` - configures the number of frames you want to animate the ocean at.
+ Must be equal to or lower than `NUMBER_OF_FRAMES`.
+ Example:
+ ```c
+ #define NUMBER_OF_FRAMES 20
+ #define OCEAN_ANIMATION_SPEED 5
+ ```
+ would result in the ocean animation happening every 4 frames. 20 would result in every frame,
+ 1 would be once every 20 frames. This essentially changes how fast the waves will move.
+* `WAVE_CALM` - Defines the WPM at which the _Wave Ferocity_ kicks up.
+ At any WPM between `WAVE_CALM` and `WAVE_HEAVY_STORM`, the waves will be just tiny ripples.
+* `WAVE_HEAVY_STORM` - Defines the WPM at which the _Wave Ferocity_ kicks up to medium.
+ At any WPM between `WAVE_HEAVY_STORM` and `WAVE_HURRICANE`, the waves will be medium sized waves.
+* `WAVE_HURRICANE` - Defines the WPM at which the _Wave Ferocity_ kicks up to the last notch.
+ At any WPM above `WAVE_HURRICANE`, the waves will be torrential.
+
+## Shooting Stars
+
+The 🌠 shooting star animation is a full screen animation that renders a meteor shower based on your typing speed. The
+faster you type, the more shooting stars there are!
+
+You can configure many parts of the shooting stars, from shower size, to speed, to length of each trail, to how spaced
+out they are.
+
+Note: Each frame of a shooting star is only 2 pixels in length. This is a design decision, and could probably be changed
+with a decent amount of work, but was chosen for looks and memory constraints.
+
+### Flags
+
+* `SHOOTING_STAR_DELAY` - Decides number of frames to delay, based on modulus, e.g. 12 means 0-11 frames of delay between each shooting star
+* `SHOOTING_STAR_FRAMES` - how long each shooting star will be. A size of `16` will result in shooting stars that are 32 pixels long
+* `MAX_NUMBER_OF_SHOOTING_STARS` - maximum number of shooting stars that can be on screen at the same time
+* `SHOOTING_STAR_WPM_INCREMENT` - every n WPM increase, add an extra star, up to MAX_NUMBER_OF_SHOOTING_STARS
+ Example: an increment of 5 would result in 1 shooting star at 5-9wpm, 2 at 10-14, etc.
+* `SHOOTING_STAR_ANIMATION_SPEED` - configures the number of frames you want to animate the shooting stars at.
+ Must be equal to or lower than `NUMBER_OF_FRAMES`.
+ Example:
+ ```c
+ #define NUMBER_OF_FRAMES 20
+ #define SHOOTING_STAR_ANIMATION_SPEED 5
+ ```
+ would result in the shooting star animation happening every 4 frames. 20 would result in every frame,
+ 1 would be once every 20 frames. This essentially changes how fast the stars move through the sky.
+
+
+## Island
+
+The 🏝 island animation is a 32w x 16h animation of a small island with a single palm tree blowing in the wind. The faster
+you type the harder the palm tree blows in the wind!
+
+Since this animation has significant black space to the left side of the tree, certain design decisions were made to allow the
+shooting stars to still show up in the sky there. This animation should work on any size screen >=32 pixels wide, and you
+can place it anywhere on the screen, but above the ocean is recommended.
+
+### Flags
+
+* `ISLAND_LINE` - where to render the island at. Defaults to `12`. The island is 2 lines tall. (see [reference](#reference))
+* `ISLAND_COLUMN` - where to render the island at. Defaults to `0`. The island is 32 pixels wide. (see [reference](#reference))
+* `ISLAND_CALM` - Defines the WPM at which the _Storm Ferocity_ kicks up.
+ At any WPM between `ISLAND_CALM` and `ISLAND_HEAVY_STORM`, the palm tree will just calmly blow in the wind.
+* `ISLAND_HEAVY_STORM` - Defines the WPM at which the _Storm Ferocity_ kicks up.
+ At any WPM between `ISLAND_HEAVY_STORM` and `ISLAND_HURRICANE`, the palm tree will be blowing hard in the wind
+* `ISLAND_HURRICANE` - Defines the WPM at which the _Storm Ferocity_ kicks up.
+ At any WPM above `ISLAND_HURRICANE`, the palm tree will almost be torn from its roots!
+
+
+# Reference
+
+Any reference to `_LINE` or `COLUMN` refers to setting a cursor position using `oled_set_cursor()`, which uses
+`OLED_FONT_WIDTH` and `OLED_FONT_HEIGHT` internally.
+This will be the top-leftmost pixel of the image, so `_LINE 1` would start at the 9th pixel down and `_COLUMN 1`
+would be the 7th pixel to the right, starting from the topleftmost pixel on the screen.
+
+This code has been untested with different font heights/widths, so you might have to adjust a bit to make it work if you
+have modified those values.
+
+# ToDo
+
+- [ ] don't require `is_calm` as a keyboard event. Not sure if the code will work without it currently.
+- [ ] make all the stars twinkle brightly based on keypresses rather than timed. Not just a movement twinkle, but a larger
+ 'full' twinkle, where the star actually gets bigger. This will be quite difficult, thus is left out of the v1.
diff --git a/users/snowe/rules.mk b/users/snowe/rules.mk
new file mode 100644
index 0000000000..a6e152c1cf
--- /dev/null
+++ b/users/snowe/rules.mk
@@ -0,0 +1,27 @@
+
+
+ifeq ($(strip $(OLED_DRIVER_ENABLE)), yes)
+ SRC += oled_setup.c
+
+ ifdef OCEAN_DREAM_ENABLE
+ ifeq ($(strip $(OCEAN_DREAM_ENABLE)), yes)
+ SRC += ocean_dream.c
+ OPT_DEFS += -DOCEAN_DREAM_ENABLE
+ endif
+ endif
+ ifndef OCEAN_DREAM_ENABLE
+ SRC += ocean_dream.c
+ OPT_DEFS += -DOCEAN_DREAM_ENABLE
+ endif
+
+ ifdef LUNA_ENABLE
+ ifeq ($(strip $(LUNA_ENABLE)), yes)
+ SRC += luna.c
+ OPT_DEFS += -DLUNA_ENABLE
+ endif
+ endif
+ ifndef LUNA_ENABLE
+ SRC += luna.c
+ OPT_DEFS += -DLUNA_ENABLE
+ endif
+endif
diff --git a/users/snowe/snowe.h b/users/snowe/snowe.h
new file mode 100644
index 0000000000..4453b26469
--- /dev/null
+++ b/users/snowe/snowe.h
@@ -0,0 +1,43 @@
+/*
+Copyright 2021 Tyler Thrailkill <@snowe/@snowe2010>
+
+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/>.
+*/
+
+
+#pragma once
+#include QMK_KEYBOARD_H
+
+#ifndef QMK_FIRMWARE_SNOWE_H
+# define QMK_FIRMWARE_SNOWE_H ;
+#endif // QMK_FIRMWARE_SNOWE_H
+
+#include "wrappers.h"
+#include "keycode_aliases.h"
+
+#define IGNORE_MOD_TAP_INTERRUPT
+#undef PERMISSIVE_HOLD
+
+//#if defined(RGBLIGHT_ENABLE)
+//# include "rgb_stuff.h"
+//#endif
+//#if defined(RGB_MATRIX_ENABLE)
+//# include "rgb_matrix_stuff.h"
+//#endif
+#ifdef OLED_DRIVER_ENABLE
+# include "oled_setup.h"
+#endif
+
+
+enum layers { _MAIN, _LOWER, _UPPER, _ADJUST };
diff --git a/users/snowe/wrappers.h b/users/snowe/wrappers.h
new file mode 100644
index 0000000000..485f8de542
--- /dev/null
+++ b/users/snowe/wrappers.h
@@ -0,0 +1,92 @@
+/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
+ * 2021 Tyler Thrailkill (@snowe/@snowe2010) <tyler.b.thrailkill@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/>.
+ */
+
+#pragma once
+#include "snowe.h"
+
+/*
+Since our quirky block definitions are basically a list of comma separated
+arguments, we need a wrapper in order for these definitions to be
+expanded before being used as arguments to the LAYOUT_xxx macro.
+*/
+#if (!defined(LAYOUT) && defined(KEYMAP))
+# define LAYOUT KEYMAP
+#endif
+
+//#define LAYOUT_ergodox_wrapper(...) LAYOUT_ergodox(__VA_ARGS__)
+//#define LAYOUT_ergodox_pretty_wrapper(...) LAYOUT_ergodox_pretty(__VA_ARGS__)
+#define KEYMAP_wrapper(...) LAYOUT(__VA_ARGS__)
+#define LAYOUT_wrapper(...) LAYOUT(__VA_ARGS__)
+//#define LAYOUT_ortho_4x12_wrapper(...) LAYOUT_ortho_4x12(__VA_ARGS__)
+//#define LAYOUT_ortho_5x12_wrapper(...) LAYOUT_ortho_5x12(__VA_ARGS__)
+//#define LAYOUT_gergo_wrapper(...) LAYOUT_gergo(__VA_ARGS__)
+
+/*
+Blocks for each of the four major keyboard layouts
+Organized so we can quickly adapt and modify all of them
+at once, rather than for each keyboard, one at a time.
+And this allows for much cleaner blocks in the keymaps.
+For instance Tap/Hold for Control on all of the layouts
+
+NOTE: These are all the same length. If you do a search/replace
+ then you need to add/remove underscores to keep the
+ lengths consistent.
+*/
+
+#define _________________QWERTY_L1_________________ KC_Q, KC_W, KC_E, KC_R, KC_T
+#define _________________QWERTY_L2_________________ KC_A, KC_S, KC_D, KC_F, KC_G
+#define _________________QWERTY_L3_________________ KC_Z, KC_X, KC_C, KC_V, KC_B
+
+#define _________________QWERTY_R1_________________ KC_Y, KC_U, KC_I, KC_O, KC_P
+#define _________________QWERTY_R2_________________ KC_H, KC_J, KC_K, KC_L, KC_SCLN
+#define _________________QWERTY_R3_________________ KC_N, KC_M, KC_COMM, KC_DOT, KC_SLASH
+
+
+#define ________________NUMBER_LEFT________________ KC_1, KC_2, KC_3, KC_4, KC_5
+#define ________________NUMBER_RIGHT_______________ KC_6, KC_7, KC_8, KC_9, KC_0
+//#define ________________NUMBER_RIGHT_______________ KC_6, KC_7, TD(TD_8_UP), KC_9, KC_0
+#define _________________FUNC_LEFT_________________ KC_F1, KC_F2, KC_F3, KC_F4, KC_F5
+#define _________________FUNC_RIGHT________________ KC_F6, KC_F7, KC_F8, KC_F9, KC_F10
+
+#define ___________________BLANK___________________ _______, _______, _______, _______, _______
+
+
+#define _________________LOWER_L1__________________ KC_EXLM, KC_AT, KC_HASH, KC_DLR, KC_PERC
+#define _________________LOWER_L2__________________ _________________FUNC_LEFT_________________
+#define _________________LOWER_L3__________________ _________________FUNC_RIGHT________________
+
+#define _________________LOWER_R1__________________ KC_CIRC, KC_AMPR, KC_ASTR, KC_LPRN, KC_RPRN
+#define _________________LOWER_R2__________________ _______, KC_UNDS, KC_PLUS, KC_LCBR, KC_RCBR
+#define _________________LOWER_R3__________________ _______, KC_LEFT, KC_UP , KC_DOWN, KC_RGHT
+
+
+#define _________________RAISE_L1__________________ ________________NUMBER_LEFT________________
+#define _________________RAISE_L2__________________ _______, KC_MINS, KC_EQL, KC_LBRC, KC_RBRC
+#define _________________RAISE_L3__________________ ___________________BLANK___________________
+
+#define _________________RAISE_R1__________________ ________________NUMBER_RIGHT_______________
+#define _________________RAISE_R2__________________ KC_LEFT, KC_DOWN, KC_UP, KC_RGHT, _______
+#define _________________RAISE_R3__________________ _______, KC_HOME, KC_PGDN, KC_PGUP, KC_END
+
+
+
+#define _________________ADJUST_L1_________________ RGB_MOD, RGB_HUI, RGB_SAI, RGB_VAI, RGB_TOG
+#define _________________ADJUST_L2_________________ MU_TOG , CK_TOGG, AU_ON, AU_OFF, AG_NORM
+#define _________________ADJUST_L3_________________ RGB_RMOD,RGB_HUD,RGB_SAD, RGB_VAD, _______
+#define _________________ADJUST_R1_________________ _______, _______, _______, _______, _______
+#define _________________ADJUST_R2_________________ RESET, CG_TOGG, _______, _______, _______
+#define _________________ADJUST_R3_________________ _______, KC_MNXT, KC_VOLU, KC_VOLD, KC_MPLY