Skip to content

Commit

Permalink
Added multiple keyboard layouts support for USB
Browse files Browse the repository at this point in the history
  • Loading branch information
VoidNoi committed May 1, 2024
1 parent 22e30cd commit 8abd7e8
Show file tree
Hide file tree
Showing 12 changed files with 2,039 additions and 457 deletions.
91 changes: 77 additions & 14 deletions BadCard.ino
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
#include <SD.h>

#include "src/Unicode/unicode.h"

#include "USB.h"

// https://gitlab.com/DJPX/advanced-keyboard-support-arduino
#include "src/USBHID-Keyboard/USBHIDKeyboard.h"
USBHIDKeyboard Keyboard;
#include "src/USBHID-Keyboard/KeyboardLayout_ES.h"
#include "src/USBHID-Keyboard/KeyboardLayout_DE.h"
#include "src/USBHID-Keyboard/KeyboardLayout_US.h"
#include "src/USBHID-Keyboard/KeyboardLayout_PT.h"
#include "src/USBHID-Keyboard/KeyboardLayout_FR.h"

#include "keys.h"

KeyboardLayout *layout = new KeyboardLayout_US();

int currentKBLayout = 0;
int kbLayoutsCursor = 0;

// https://github.com/T-vK/ESP32-BLE-Keyboard
#include "src/BLE-Keyboard/BleKeyboard.h"
BleKeyboard BLEKeyboard("GoodCard :)", "VoidNoi", 100);
Expand All @@ -23,9 +38,9 @@ int fileAmount;
int mainCursor = 0;
int scriptCursor = 0;

const int maxFiles = 50;
const int maxFiles = 100;

String sdFiles[maxFiles] = {"NEW SCRIPT", "ACTIVATE BLE"};
String sdFiles[maxFiles] = {"NEW SCRIPT", "ACTIVATE BLE", "KB LAYOUT"};

const int ELEMENT_COUNT_MAX = 500;
String fileText[ELEMENT_COUNT_MAX];
Expand All @@ -47,7 +62,7 @@ bool saveFile = false;
bool isBLE = false;

void getDirectory() {
fileAmount = 2;
fileAmount = 3;
File dir = SD.open(root);

while (true) {
Expand Down Expand Up @@ -126,7 +141,7 @@ void executeScript() {

if (!isBLE) {
USB.begin();
Keyboard.begin();
Keyboard.begin(layout);
}

display.println(fileName);
Expand Down Expand Up @@ -252,17 +267,30 @@ void processLine(String line) {
payload = "";
}
}

if (payload == "" && command != "") { // Command from (1)
processCommand(command); // Process command
} else if (command == "DELAY") { // Delay before the next commande
} else if (command == "DELAY") { // Delay before the next command
delay((int) payload.toInt()); // Convert payload to integer and make pause for 'payload' time
} else if (command == "STRING") {
} else if (command == "STRING") {
int payloadLen = payload.length();
unsigned char array[payloadLen];
for (int i = 0; i < payloadLen; i++) {
array[i] = payload[i];
}
array[payloadLen] = '\0';

char16_t uString[payloadLen];

utf8_to_utf16(&array[0], payloadLen, &uString[0], payloadLen);
uString[payloadLen] = '\0';
if (isBLE) {
BLEKeyboard.print(payload);
} else {
Keyboard.print(payload); // Type-in the payload
} // String processing
Keyboard.write(uString); // Type-in the payload
}
// String processing

} else if (command == "REM") { // Comment
} else if (command != "") { // Command from (2)
String remaining = line; // Prepare commands to run
Expand Down Expand Up @@ -523,14 +551,48 @@ void scriptOptions() {
}

void scriptMenu() {
char optionsList[3][20] = {"Execute script", "Edit script", "Delete script"};
int options = 3;
char optionsList[options][20] = {"Execute script", "Edit script", "Delete script"};

for (int i = 0; i < 3; i++) {
for (int i = 0; i < options; i++) {
display.setCursor(20, i * 20);
display.println(optionsList[i]);
}
}

void kbLayoutsOptions() {
currentKBLayout = kbLayoutsCursor;

switch (currentKBLayout) {
case 0:
layout = new KeyboardLayout_US();
break;
case 1:
layout = new KeyboardLayout_ES();
break;
case 2:
layout = new KeyboardLayout_DE();
break;
case 3:
layout = new KeyboardLayout_PT();
break;
case 4:
layout = new KeyboardLayout_FR();
break;
}
mainMenu();
}
int kbLayoutsLen = 5;

void kbLayoutsMenu() {
char kbLayouts[kbLayoutsLen][6] = {"en_US", "es_ES", "de_DE", "pt_PT", "fr_FR"};

for (int i = 0; i < kbLayoutsLen; i++) {
display.setCursor(20, i * 20);
display.println(kbLayouts[i]);
}
}

void mainOptions() {
if (mainCursor == 0) {
newFile();
Expand All @@ -545,8 +607,9 @@ void mainOptions() {
sdFiles[1] = "ACTIVATE BLE";
}
mainMenu();
}
else {
} else if (mainCursor == 2) {
handleMenus(kbLayoutsLen-1, &kbLayoutsOptions, kbLayoutsCursor, &kbLayoutsMenu);
} else {
handleMenus(2, &scriptOptions, scriptCursor, &scriptMenu);
}
}
Expand All @@ -565,7 +628,7 @@ void bootLogo(){
display.fillScreen(BLACK);

display.setTextSize(2);
String BCVersion = "BadCard v1.3.2";
String BCVersion = "BadCard v1.4.0";

display.setCursor(display.width()/2-(BCVersion.length()/2)*letterWidth, display.height()/2 - 50);
display.println(BCVersion);
Expand Down
92 changes: 92 additions & 0 deletions src/USBHID-Keyboard/KeyboardLayout.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
KeyboardLayout.h
This file is not part of the public API. It is meant to be included
only in Keyboard.cpp and the keyboard layout files. Layout files map
ASCII character codes to keyboard scan codes (technically, to USB HID
Usage codes), possibly altered by the SHIFT or ALT_GR modifiers.
Non-ACSII characters (anything outside the 7-bit range NUL..DEL) are
not supported.
== Creating your own layout ==
In order to create your own layout file, copy an existing layout that
is similar to yours, then modify it to use the correct keys. The
layout is an array in ASCII order. Each entry contains a scan code,
possibly modified by "|SHIFT" or "|ALT_GR", as in this excerpt from
the Italian layout:
0x35, // bslash
0x30|ALT_GR, // ]
0x2e|SHIFT, // ^
Do not change the control characters (those before scan code 0x2c,
corresponding to space). Do not attempt to grow the table past DEL. Do
not use both SHIFT and ALT_GR on the same character: this is not
supported. Unsupported characters should have 0x00 as scan code.
For a keyboard with an ISO physical layout, use the scan codes below:
+---+---+---+---+---+---+---+---+---+---+---+---+---+-------+
|35 |1e |1f |20 |21 |22 |23 |24 |25 |26 |27 |2d |2e |BackSp |
+---+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-----+
| Tab |14 |1a |08 |15 |17 |1c |18 |0c |12 |13 |2f |30 | Ret |
+-----++--++--++--++--++--++--++--++--++--++--++--++--++ |
|CapsL |04 |16 |07 |09 |0a |0b |0d |0e |0f |33 |34 |31 | |
+----+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+---+----+
|Shi.|32 |1d |1b |06 |19 |05 |11 |10 |36 |37 |38 | Shift |
+----+---++--+-+-+---+---+---+---+---+--++---+---++----+----+
|Ctrl|Win |Alt | |AlGr|Win |Menu|Ctrl|
+----+----+----+------------------------+----+----+----+----+
The ANSI layout is identical except that key 0x31 is above (rather
than next to) Return, and there is not key 0x32.
Give a unique name to the layout array, then declare it in Keyboard.h
with a line of the form:
extern const uint8_t KeyboardLayout_xx_YY[];
== Encoding details ==
All scan codes are less than 0x80, which makes bit 7 available to
signal that a modifier (Shift or AltGr) is needed to generate the
character. With only one exception, keys that are used with modifiers
have scan codes that are less than 0x40. This makes bit 6 available
to signal whether the modifier is Shift or AltGr. The exception is
0x64, the key next next to Left Shift on the ISO layout (and absent
from the ANSI layout). We handle it by replacing its value by 0x32 in
the layout arrays.
*/

#include <Arduino.h>
#pragma once

#define SHIFT 0x80
#define ALT_GR 0xc0
#define ISO_KEY 0x64
#define ISO_REPLACEMENT 0x32
#define U16SHIFT 0x8000

class KeyboardLayout
{
public:
KeyboardLayout(){};
~KeyboardLayout(){};
virtual const uint8_t* getKeymap()=0;
/**
* @brief Returns the keycode for the given key
*
* @param key Key value literal
* @return Key code if possible
*/
virtual uint16_t getKeycode(char16_t key)=0;
/**
* @brief Returns if the key is a keyboardlayout specific key
*
* @param key Key to check (represents a character)
* @return the keycode or 0 if it is not a special key
*/
virtual uint16_t isSpecialKey(char16_t key)=0;

};
Loading

0 comments on commit 8abd7e8

Please sign in to comment.