From c68e596f32c5d450a714627871408407e9988ef7 Mon Sep 17 00:00:00 2001
From: Priyadi Iman Nurcahyo <priyadi@priyadi.net>
Date: Mon, 13 Feb 2017 08:03:07 +0700
Subject: [PATCH 1/2] Implement faux-clicky feature

---
 build_keyboard.mk                         |  5 ++
 keyboards/planck/keymaps/priyadi/Makefile |  3 +-
 keyboards/planck/keymaps/priyadi/keymap.c |  4 +-
 quantum/fauxclicky.c                      | 68 ++++++++++++++++++
 quantum/fauxclicky.h                      | 87 +++++++++++++++++++++++
 quantum/template/rules.mk                 |  1 +
 tmk_core/common/action.c                  | 13 ++++
 tmk_core/common/keyboard.c                |  6 ++
 8 files changed, 184 insertions(+), 3 deletions(-)
 create mode 100644 quantum/fauxclicky.c
 create mode 100644 quantum/fauxclicky.h

diff --git a/build_keyboard.mk b/build_keyboard.mk
index 2c64e93a2..c8e82cf0e 100644
--- a/build_keyboard.mk
+++ b/build_keyboard.mk
@@ -161,6 +161,11 @@ ifeq ($(strip $(AUDIO_ENABLE)), yes)
 	SRC += $(QUANTUM_DIR)/audio/luts.c
 endif
 
+ifeq ($(strip $(FAUXCLICKY_ENABLE)), yes)
+    OPT_DEFS += -DFAUXCLICKY_ENABLE
+	SRC += $(QUANTUM_DIR)/fauxclicky.c
+endif
+
 ifeq ($(strip $(UCIS_ENABLE)), yes)
 	OPT_DEFS += -DUCIS_ENABLE
 	UNICODE_ENABLE = yes
diff --git a/keyboards/planck/keymaps/priyadi/Makefile b/keyboards/planck/keymaps/priyadi/Makefile
index 336608b8c..27c2638e2 100644
--- a/keyboards/planck/keymaps/priyadi/Makefile
+++ b/keyboards/planck/keymaps/priyadi/Makefile
@@ -10,12 +10,13 @@ COMMAND_ENABLE = no         # 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 = yes      # Enable keyboard backlight functionality
 MIDI_ENABLE = no            # MIDI controls
-AUDIO_ENABLE = yes          # Audio output on port C6
+AUDIO_ENABLE = no           # Audio output on port C6
 UNICODE_ENABLE = no         # Unicode
 UNICODEMAP_ENABLE = yes     # Unicode map
 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.
 API_SYSEX_ENABLE = no
+FAUXCLICKY_ENABLE = yes
 
 # Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
 SLEEP_LED_ENABLE = no    # Breathing sleep LED during USB suspend
diff --git a/keyboards/planck/keymaps/priyadi/keymap.c b/keyboards/planck/keymaps/priyadi/keymap.c
index 2e979221a..13668fd10 100644
--- a/keyboards/planck/keymaps/priyadi/keymap.c
+++ b/keyboards/planck/keymaps/priyadi/keymap.c
@@ -268,8 +268,8 @@ const uint32_t PROGMEM unicode_map[] = {
 
 
 // hybrid right-gui & scroll lock (mapped to Compose in OS)
-#undef KC_RCTL
-#define KC_RCTL MT(MOD_LCTL, KC_SLCK)
+#undef KC_RALT
+#define KC_RALT MT(MOD_RALT, KC_SLCK)
 
 // keymaps
 
diff --git a/quantum/fauxclicky.c b/quantum/fauxclicky.c
new file mode 100644
index 000000000..13273e705
--- /dev/null
+++ b/quantum/fauxclicky.c
@@ -0,0 +1,68 @@
+/*
+Copyright 2017 Priyadi Iman Nurcahyo
+
+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 <avr/interrupt.h>
+#include <avr/io.h>
+#include <timer.h>
+#include <fauxclicky.h>
+#include <stdbool.h>
+#include <musical_notes.h>
+
+__attribute__ ((weak))
+float fauxclicky_pressed_note[2] = MUSICAL_NOTE(_F3, 2);
+__attribute__ ((weak))
+float fauxclicky_released_note[2] = MUSICAL_NOTE(_A3, 2);
+__attribute__ ((weak))
+float fauxclicky_beep_note[2] = MUSICAL_NOTE(_C3, 2);
+
+bool fauxclicky_enabled = true;
+uint16_t note_start = 0;
+bool note_playing = false;
+uint16_t note_period = 0;
+
+void fauxclicky_init()
+{
+    // Set port PC6 (OC3A and /OC4A) as output
+    DDRC |= _BV(PORTC6);
+
+    // TCCR3A / TCCR3B: Timer/Counter #3 Control Registers
+    TCCR3A = (0 << COM3A1) | (0 << COM3A0) | (1 << WGM31) | (0 << WGM30);
+    TCCR3B = (1 << WGM33)  | (1 << WGM32)  | (0 << CS32)  | (1 << CS31) | (0 << CS30);
+}
+
+void fauxclicky_stop()
+{
+    FAUXCLICKY_DISABLE_OUTPUT;
+    note_playing = false;
+}
+
+void fauxclicky_play(float note[2]) {
+    if (!fauxclicky_enabled) return;
+    if (note_playing) fauxclicky_stop();
+    FAUXCLICKY_TIMER_PERIOD = (uint16_t)(((float)F_CPU) / (note[0] * FAUXCLICKY_CPU_PRESCALER));
+    FAUXCLICKY_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (note[0] * FAUXCLICKY_CPU_PRESCALER)) / 2);
+    note_playing = true;
+    note_period = (note[1] / 16) * (60 / (float)FAUXCLICKY_TEMPO) * 100;   // check this
+    note_start = timer_read();
+    FAUXCLICKY_ENABLE_OUTPUT;
+}
+
+void fauxclicky_check() {
+    if (!note_playing) return;
+
+    if (timer_elapsed(note_start) > note_period) {
+        fauxclicky_stop();
+    }
+}
diff --git a/quantum/fauxclicky.h b/quantum/fauxclicky.h
new file mode 100644
index 000000000..6cfc291c0
--- /dev/null
+++ b/quantum/fauxclicky.h
@@ -0,0 +1,87 @@
+/*
+Copyright 2017 Priyadi Iman Nurcahyo
+
+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 AUDIO_ENABLE
+#error "AUDIO_ENABLE and FAUXCLICKY_ENABLE cannot be both enabled"
+#endif
+
+#include "musical_notes.h"
+
+__attribute__ ((weak))
+float fauxclicky_pressed_note[2];
+__attribute__ ((weak))
+float fauxclicky_released_note[2];
+__attribute__ ((weak))
+float fauxclicky_beep_note[2];
+
+//
+// tempo in BPM
+//
+
+#ifndef FAUXCLICKY_TEMPO
+#define FAUXCLICKY_TEMPO TEMPO_DEFAULT
+#endif
+
+// beep on press
+#define FAUXCLICKY_ACTION_PRESS fauxclicky_play(fauxclicky_pressed_note)
+
+// beep on release
+#define FAUXCLICKY_ACTION_RELEASE fauxclicky_play(fauxclicky_released_note)
+
+// general purpose beep
+#define FAUXCLICKY_BEEP fauxclicky_play(fauxclicky_beep_note)
+
+// enable
+#define FAUXCLICKY_ON fauxclicky_enabled = true
+
+// disable
+#define FAUXCLICKY_OFF do { \
+    fauxclicky_enabled = false; \
+    fauxclicky_stop(); \
+} while (0)
+
+//
+// pin configuration
+//
+
+#ifndef FAUXCLICKY_CPU_PRESCALER
+#define FAUXCLICKY_CPU_PRESCALER 8
+#endif
+
+#ifndef FAUXCLICKY_ENABLE_OUTPUT
+#define FAUXCLICKY_ENABLE_OUTPUT TCCR3A |= _BV(COM3A1);
+#endif
+
+#ifndef FAUXCLICKY_DISABLE_OUTPUT
+#define FAUXCLICKY_DISABLE_OUTPUT TCCR3A &= ~(_BV(COM3A1) | _BV(COM3A0));
+#endif
+
+#ifndef FAUXCLICKY_TIMER_PERIOD
+#define FAUXCLICKY_TIMER_PERIOD ICR3
+#endif
+
+#ifndef FAUXCLICKY_DUTY_CYCLE
+#define FAUXCLICKY_DUTY_CYCLE OCR3A
+#endif
+
+//
+// definitions
+//
+
+void fauxclicky_init(void);
+void fauxclicky_stop(void);
+void fauxclicky_play(float note[2]);
+void fauxclicky_check(void);
+
diff --git a/quantum/template/rules.mk b/quantum/template/rules.mk
index 55898147d..bad3387bf 100644
--- a/quantum/template/rules.mk
+++ b/quantum/template/rules.mk
@@ -65,3 +65,4 @@ MIDI_ENABLE ?= no            # MIDI controls
 UNICODE_ENABLE ?= no         # Unicode
 BLUETOOTH_ENABLE ?= no       # Enable Bluetooth with the Adafruit EZ-Key HID
 AUDIO_ENABLE ?= no           # Audio output on port C6
+FAUXCLICKY_ENABLE ?= no      # Use buzzer to emulate clicky switches
diff --git a/tmk_core/common/action.c b/tmk_core/common/action.c
index f03670a7f..94de36918 100644
--- a/tmk_core/common/action.c
+++ b/tmk_core/common/action.c
@@ -33,6 +33,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include "nodebug.h"
 #endif
 
+#ifdef FAUXCLICKY_ENABLE
+#include <fauxclicky.h>
+#endif
 
 void action_exec(keyevent_t event)
 {
@@ -41,6 +44,16 @@ void action_exec(keyevent_t event)
         dprint("EVENT: "); debug_event(event); dprintln();
     }
 
+#ifdef FAUXCLICKY_ENABLE
+    if (IS_PRESSED(event)) {
+        FAUXCLICKY_ACTION_PRESS;
+    }
+    if (IS_RELEASED(event)) {
+        FAUXCLICKY_ACTION_RELEASE;
+    }
+    fauxclicky_check();
+#endif
+
 #ifdef ONEHAND_ENABLE
     if (!IS_NOEVENT(event)) {
         process_hand_swap(&event);
diff --git a/tmk_core/common/keyboard.c b/tmk_core/common/keyboard.c
index 3aa82231b..eac1f1dd8 100644
--- a/tmk_core/common/keyboard.c
+++ b/tmk_core/common/keyboard.c
@@ -51,6 +51,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #ifdef RGBLIGHT_ENABLE
 #   include "rgblight.h"
 #endif
+#ifdef FAUXCLICKY_ENABLE
+#   include "fauxclicky.h"
+#endif
 #ifdef SERIAL_LINK_ENABLE
 #   include "serial_link/system/serial_link.h"
 #endif
@@ -108,6 +111,9 @@ void keyboard_init(void) {
 #ifdef RGBLIGHT_ENABLE
     rgblight_init();
 #endif
+#ifdef FAUXCLICKY_ENABLE
+    fauxclicky_init();
+#endif
 #if defined(NKRO_ENABLE) && defined(FORCE_NKRO)
     keymap_config.nkro = 1;
 #endif

From 8c93c5d9ab8a0a69d84f707db71f417b66402693 Mon Sep 17 00:00:00 2001
From: Priyadi Iman Nurcahyo <priyadi@priyadi.net>
Date: Mon, 13 Feb 2017 14:55:35 +0700
Subject: [PATCH 2/2] Add keycodes to turn on, turn off and toggle faux clicky

---
 quantum/fauxclicky.h       | 12 ++++++++++++
 quantum/quantum.c          | 24 ++++++++++++++++++++++++
 quantum/quantum_keycodes.h |  7 +++++++
 3 files changed, 43 insertions(+)

diff --git a/quantum/fauxclicky.h b/quantum/fauxclicky.h
index 6cfc291c0..109bd0d83 100644
--- a/quantum/fauxclicky.h
+++ b/quantum/fauxclicky.h
@@ -18,6 +18,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #endif
 
 #include "musical_notes.h"
+#include "stdbool.h"
 
 __attribute__ ((weak))
 float fauxclicky_pressed_note[2];
@@ -26,6 +27,8 @@ float fauxclicky_released_note[2];
 __attribute__ ((weak))
 float fauxclicky_beep_note[2];
 
+bool fauxclicky_enabled;
+
 //
 // tempo in BPM
 //
@@ -52,6 +55,15 @@ float fauxclicky_beep_note[2];
     fauxclicky_stop(); \
 } while (0)
 
+// toggle
+#define FAUXCLICKY_TOGGLE do { \
+    if (fauxclicky_enabled) { \
+        FAUXCLICKY_OFF; \
+    } else { \
+        FAUXCLICKY_ON; \
+    } \
+} while (0)
+
 //
 // pin configuration
 //
diff --git a/quantum/quantum.c b/quantum/quantum.c
index 45ea8cb73..2088c10c9 100644
--- a/quantum/quantum.c
+++ b/quantum/quantum.c
@@ -7,6 +7,10 @@
 #define TAPPING_TERM 200
 #endif
 
+#ifdef FAUXCLICKY_ENABLE
+#include "fauxclicky.h"
+#endif
+
 static void do_code16 (uint16_t code, void (*f) (uint8_t)) {
   switch (code) {
   case QK_MODS ... QK_MODS_MAX:
@@ -196,6 +200,26 @@ bool process_record_quantum(keyrecord_t *record) {
       }
 	  return false;
       break;
+  #ifdef FAUXCLICKY_ENABLE
+  case FC_TOG:
+    if (record->event.pressed) {
+      FAUXCLICKY_TOGGLE;
+    }
+    return false;
+    break;
+  case FC_ON:
+    if (record->event.pressed) {
+      FAUXCLICKY_ON;
+    }
+    return false;
+    break;
+  case FC_OFF:
+    if (record->event.pressed) {
+      FAUXCLICKY_OFF;
+    }
+    return false;
+    break;
+  #endif
 	#ifdef RGBLIGHT_ENABLE
 	case RGB_TOG:
 		if (record->event.pressed) {
diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h
index ab2e79026..cc7a5013f 100644
--- a/quantum/quantum_keycodes.h
+++ b/quantum/quantum_keycodes.h
@@ -86,6 +86,13 @@ enum quantum_keycodes {
     AU_OFF,
     AU_TOG,
 
+#ifdef FAUXCLICKY_ENABLE
+    // Faux clicky
+    FC_ON,
+    FC_OFF,
+    FC_TOG,
+#endif
+
     // Music mode on/off/toggle
     MU_ON,
     MU_OFF,