arduino-libraries / Keyboard

GNU Lesser General Public License v3.0
223 stars 157 forks source link

a library for the german keyboard-layout capable of sending keystrokes for ALL characters #52

Closed StefanL38 closed 2 years ago

StefanL38 commented 2 years ago

Hi,

I wrote a library for the german keyboard-layout that is capable of sending keystrokes for ALL characters It will send the correct keypress-codes for every character that is on a german keyboard. Including the characters ° ^ ² ³ { '[' ']' } \ ~ @ € | ä ö ü _ Ä Ö Ü µ ß which are special to the german keyboard-layout and all other characters where some of them have a different key on the keyboard compared to the US-keyboard-layout functional keys like "F1", "F2" backspace, cursor up/down etc. are not included. Except the Return-Key.

Some characters have a two-byte or even three-byte representation which needs a different analysis than a simple lookup of single character.

This library has one main-function called typetext where the name is what the function does.

I'm not familiar with creating repositaries on GitHub so I just post the .h and the .cpp-file here in this post. Adapting this library to other language specific keyboard-layouts just requires to change the values in the library

/*
  KeyboardGER2.h
  derived from Keyboard.h and modified by user StefanL38

  Copyright (c) 2015, Arduino LLC
  Original code (pre-library): Copyright (c) 2011, Peter Barrett

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library 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
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#ifndef KEYBOARDGER2_h  // this #ifndef #define prevents from re-declaration-errors 
#define KEYBOARDGER2_h  // through accidentically "double"-including KeyboardGER2.h

#include "Arduino.h"
#include "HID.h"

#if !defined(_USING_HID)

#warning "Using legacy HID core (non pluggable)"

#endif

const byte Ctrl  = 1;
const byte Shift = 2;
const byte Alt   = 4;

const byte CtrlAlt   = Ctrl + Alt;
const byte AltCtrl   = CtrlAlt;

const byte aeoeue_1stByte = 195;
const byte ae_2ndByte = 164;
const byte oe_2ndByte = 182;
const byte ue_2ndByte = 188;

const byte Ae_2ndByte = 132;
const byte Oe_2ndByte = 150;
const byte Ue_2ndByte = 156;
const byte SharpS_2ndByte = 159;

const byte mue23_1stByte = 194;
const byte mue_2ndByte   = 181;
const byte mue2_2ndByte  = 178;
const byte mue3_2ndByte  = 179;

const byte degree_2ndByte = 176;
const byte paragraph_2ndByte = 167;

const byte euro_1stByte = 226;
const byte euro_2ndByte = 130;
const byte euro_3rdByte = 172;

const int SendDelay = 5;

/* Modifiers */
enum MODIFIER_KEY {
    KEY_CTRL     = 1,
    KEY_SHIFT    = 2,
    KEY_ALT      = 4,
    KEY_CTRL_ALT = 5
};

typedef struct {
    unsigned char key;
    unsigned char modifier;
    } KEY_MOD_VALUE;

typedef struct {
  uint8_t modifiers;
  uint8_t reserved;
  uint8_t pressedKeys[6];
} KeyReport_t;

/* German keyboard (as HID standard) */
#define KEY_MOD_TABLE_SIZE (127)

const KEY_MOD_VALUE key_mod_values[KEY_MOD_TABLE_SIZE] = {
   {0x00, 0 }, /* 0  */
   {0x00, 0 }, /* 1  */
   {0x00, 0 }, /* 2  */
   {0x00, 0 }, /* 3  */
   {0x00, 0 }, /* 4  */
   {0x00, 0 }, /* 5  */
   {0x00, 0 }, /* 6  */
   {0x00, 0 }, /* 7  */
   {0x00, 0 }, /* 8  */
   {0x00, 0 }, /* 9  */
   {0x58, 0 }, /* 10  */  //translate newline to returnkey
   {0x00, 0 }, /* 11  */
   {0x00, 0 }, /* 12  */
   {0x58, 0 }, /* 13  */  // translate return to returnkey
   {0x00, 0 }, /* 14  */
   {0x00, 0 }, /* 15  */
   {0x00, 0 }, /* 16  */
   {0x00, 0 }, /* 17  */
   {0x00, 0 }, /* 18  */
   {0x00, 0 }, /* 19  */
   {0x00, 0 }, /* 20  */
   {0x00, 0 }, /* 21  */
   {0x00, 0 }, /* 22  */
   {0x00, 0 }, /* 23  */
   {0x00, 0 }, /* 24  */
   {0x00, 0 }, /* 25  */
   {0x00, 0 }, /* 26  */
   {0x00, 0 }, /* 27  */
   {0x00, 0 }, /* 28  */
   {0x00, 0 }, /* 29  */
   {0x00, 0 }, /* 30  */
   {0x00, 0 }, /* 31  */
   {0x00, 0 }, /* 32  */
   {0x1E, KEY_SHIFT }, /* 33   !  */
   {0x1F, KEY_SHIFT }, /* 34   "  */
   {0x31, 0 }, /* 35   #  */
   {0x21, KEY_SHIFT }, /* 36   $  */
   {0x22, KEY_SHIFT }, /* 37   %  */
   {0x23, KEY_SHIFT }, /* 38   &  */
   {0x31, KEY_SHIFT }, /* 39   '  */
   {0x25, KEY_SHIFT }, /* 40   (  */
   {0x26, KEY_SHIFT }, /* 41   )  */
   {0x30, KEY_SHIFT }, /* 42   * */
   {0x30, 0 }, /* 43   +  */
   {0x36, 0 }, /* 44   ,  */
   {0x38, 0 }, /* 45   -  */
   {0x37, 0 }, /* 46   .  */
   {0x24, KEY_SHIFT }, /* 47   / */
   {0x27, 0 }, /* 48 0 */
   {0x1E, 0 }, /* 49 1 */
   {0x1F, 0 }, /* 50 2 */
   {0x20, 0 }, /* 51 3 */     // {0x20, 0 }, /* 51 3 */
   {0x21, 0 }, /* 52 4 */
   {0x22, 0 }, /* 53 5 */
   {0x23, 0 }, /* 54 6 */
   {0x24, 0 }, /* 55 7 */
   {0x25, 0 }, /* 56 8 */
   {0x26, 0 }, /* 57 9 */
   {0x37, KEY_SHIFT }, /* 58   :  */
   {0x36, KEY_SHIFT }, /* 59   ; */
   {0x64, 0 }, /* 60   <  */
   {0x27, KEY_SHIFT }, /* 61   =  */
   {0x64, KEY_SHIFT }, /* 62   >  */
   {0x2D, KEY_SHIFT }, /* 63   ?  */
   {0x14, KEY_CTRL_ALT }, /* 64   @ */
   {0x04, KEY_SHIFT }, /* 65   A  */
   {0x05, KEY_SHIFT }, /* 66   B  */
   {0x06, KEY_SHIFT }, /* 67   C  */
   {0x07, KEY_SHIFT }, /* 68   D  */
   {0x08, KEY_SHIFT }, /* 69   E */
   {0x09, KEY_SHIFT }, /* 70   F  */
   {0x0A, KEY_SHIFT }, /* 71   G  */
   {0x0B, KEY_SHIFT }, /* 72   H  */
   {0x0C, KEY_SHIFT }, /* 73   I */
   {0x0D, KEY_SHIFT }, /* 74   J  */
   {0x0E, KEY_SHIFT }, /* 75   K  */
   {0x0F, KEY_SHIFT }, /* 76   L  */
   {0x10, KEY_SHIFT }, /* 77   M  */
   {0x11, KEY_SHIFT }, /* 78   N  */
   {0x12, KEY_SHIFT }, /* 79   O */
   {0x13, KEY_SHIFT }, /* 80   P */
   {0x14, KEY_SHIFT }, /* 81   Q */
   {0x15, KEY_SHIFT }, /* 82   R */
   {0x16, KEY_SHIFT }, /* 83   S  */
   {0x17, KEY_SHIFT }, /* 84   T */
   {0x18, KEY_SHIFT }, /* 85   U */
   {0x19, KEY_SHIFT }, /* 86   V  */
   {0x1A, KEY_SHIFT }, /* 87   W */
   {0x1B, KEY_SHIFT }, /* 88   X  */
   {0x1D, KEY_SHIFT }, /* 89   Y  */
   {0x1C, KEY_SHIFT }, /* 90   Z */
   {0x25, KEY_CTRL_ALT }, /* 91   [ */
   {0x2D, KEY_CTRL_ALT }, /* 92   \ */
   {0x26, KEY_CTRL_ALT }, /* 93   ] */
   {0x35, 0 }, /* 94   ^  */
   {0x38, KEY_SHIFT }, /* 95   _  */
   {0x2E, KEY_SHIFT }, /* 96   `  */
   {0x04, 0 }, /* 97   a  */
   {0x05, 0 }, /* 98   b  */
   {0x06, 0 }, /* 99   c  */
   {0x07, 0 }, /* 100   d  */
   {0x08, 0 }, /* 101   e  */
   {0x09, 0 }, /* 102   f  */
   {0x0A, 0 }, /* 103   g  */
   {0x0B, 0 }, /* 104   h  */
   {0x0C, 0 }, /* 105   i  */
   {0x0D, 0 }, /* 106   j  */
   {0x0E, 0 }, /* 107   k  */
   {0x0F, 0 }, /* 108   l  */
   {0x10, 0 }, /* 109   m  */
   {0x11, 0 }, /* 110   n  */
   {0x12, 0 }, /* 111   o  */
   {0x13, 0 }, /* 112   p  */
   {0x14, 0 }, /* 113   q  */
   {0x15, 0 }, /* 114   r  */
   {0x16, 0 }, /* 115   s  */
   {0x17, 0 }, /* 116   t  */
   {0x18, 0 }, /* 117   u  */
   {0x19, 0 }, /* 118   v  */
   {0x1A, 0 }, /* 119   w  */
   {0x1B, 0 }, /* 120   x  */
   {0x1D, 0 }, /* 121   y  */
   {0x1C, 0 }, /* 122   z  */
   {0x0 , 0 }, /* 123   { */
   {0x64, KEY_CTRL_ALT }, /* 124   | */
   {0x0 , 0 }, /* 125   } */
   {0x30, KEY_CTRL_ALT }, /* 126   ~ */
};

class KeyboardGER2 
{
//private: {}
public:
  KeyReport_t _keyReport;
  void sendReport(KeyReport_t* keys);
  KeyboardGER2(void);
  void begin(void);
  void end(void);
  void sendKeyPress(uint8_t p_key, uint8_t p_modifiers);
  void sendKeyRelease();
  void SendReturn();
  void typeText(const char* text);

};

#endif
/*
  KeyboardGER2.cpp
  derived from Keyboard.cpp and modified by user StefanL38

  Copyright (c) 2015, Arduino LLC
  Original code (pre-library): Copyright (c) 2011, Peter Barrett

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library 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
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

  ATTENTION ! for easier writing down code there is a convention to give
  the class and the constructor-function the exact same name as the filenames *.h / *.cpp
  in this case this is "KeyboardGER2"
*/

#include "KeyboardGER2.h"

#if defined(_USING_HID)

static const uint8_t _hidReportDescriptor[] PROGMEM = {

  //  Keyboard
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)  // 47
    0x09, 0x06,                    // USAGE (Keyboard)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x85, 0x02,                    //   REPORT_ID (2)
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)

  0x19, 0xe0,                    //   USAGE_MINIMUM (Keyboard LeftControl)
    0x29, 0xe7,                    //   USAGE_MAXIMUM (Keyboard Right GUI)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x75, 0x01,                    //   REPORT_SIZE (1)

  0x95, 0x08,                    //   REPORT_COUNT (8)
    0x81, 0x02,                    //   INPUT (Data,Var,Abs)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x81, 0x03,                    //   INPUT (Cnst,Var,Abs)

  0x95, 0x06,                    //   REPORT_COUNT (6)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x73,                    //   LOGICAL_MAXIMUM (115)
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)

  0x19, 0x00,                    //   USAGE_MINIMUM (Reserved (no event indicated))
    0x29, 0x73,                    //   USAGE_MAXIMUM (Keyboard Application)
    0x81, 0x00,                    //   INPUT (Data,Ary,Abs)
    0xc0,                          // END_COLLECTION
};

KeyboardGER2::KeyboardGER2(void) { //<= the first "KeyboardGER2::" is the class-NAME the second "KeyboardGER2" is the functionname of the constructor
    static HIDSubDescriptor node(_hidReportDescriptor, sizeof(_hidReportDescriptor));
    HID().AppendDescriptor(&node);
}

void KeyboardGER2::begin(void){ } // insert class-Name:: between returntype and function-name 

void KeyboardGER2::end(void) { }

void KeyboardGER2::sendReport(KeyReport_t* keys){
    HID().SendReport(2,keys,sizeof(KeyReport_t));
}

void KeyboardGER2::sendKeyPress(uint8_t p_key, uint8_t p_modifiers) {

  KeyReport_t myKeyMessage;

  for (int j = 0; j < 6; j++) {
    myKeyMessage.pressedKeys[j] = 0;
  }

  myKeyMessage.pressedKeys[0] = p_key;
  myKeyMessage.modifiers = p_modifiers;
  myKeyMessage.reserved  = 0;

  sendReport(&myKeyMessage);  
  delay(SendDelay);  
}

void KeyboardGER2::sendKeyRelease() {

  KeyReport_t myKeyMessage;

  for (int j = 0; j < 6; j++) {
    myKeyMessage.pressedKeys[j] = 0;
  }

  myKeyMessage.pressedKeys[0] = 0;
  myKeyMessage.modifiers = 0;
  myKeyMessage.reserved  = 0;

  sendReport(&myKeyMessage);
}

void KeyboardGER2::SendReturn() {
  sendKeyPress(88,0);
  sendKeyRelease();  
}

// as some special characters like "@" "Ä", "€" have a two-byte or even three-byte-representation
// a simple write (uint8_t that_one_single_byte) is NOT possible 
// the multibyte characters need a more special analyses to set the correspnding key-press-value
void KeyboardGER2::typeText(const char* text) {

  uint8_t ASCII_Code;
  uint8_t SecondByte;
  uint8_t ThirdByte;

  uint8_t key;
  uint8_t modifiers;

  int len = strlen(text);
  int i = -1; // initialise with -1 to get i = 0 at first run with i++ at the top of the loop

  while (i < len) {
    i++;
    // translate character to key combination
    ASCII_Code = (uint8_t)text[i];

    if (ASCII_Code > KEY_MOD_TABLE_SIZE) { // character has a multibyte representation
      switch (ASCII_Code) {
        case aeoeue_1stByte: // if it's an äöü
          i++;
          SecondByte = text[i];

          if (SecondByte == ae_2ndByte) { // if it's an ä
            key = 52; 
            modifiers   = 0;
            break;
          }

          if (SecondByte == oe_2ndByte) { // if it's an ö
            key = 51; 
            modifiers   = 0;
            break;
          }

          if (SecondByte == ue_2ndByte) { // if it's an ü
            key = 47; 
            modifiers   = 0;
            break;
          }

          if (SecondByte == Ae_2ndByte) { // if it's an Ä
            key = 52; 
            modifiers   = Shift;
            break;
          }

          if (SecondByte == Oe_2ndByte) { // if it's an Ö
            key = 51; 
            modifiers   = Shift;
            break;
          }

          if (SecondByte == Ue_2ndByte) { // if it's an Ü
            key = 47; 
            modifiers   = Shift;
            break;
          }

          if (SecondByte == SharpS_2ndByte) { // if it's an ß
            key = 45; 
            modifiers   = 0;
            break;
          }

        case mue23_1stByte: // if it's a µ²³
          i++;
          SecondByte = text[i];

          if (SecondByte == mue_2ndByte) { // if it's an µ
            key = 16; 
            modifiers   = CtrlAlt;
            break;
          }

          if (SecondByte == mue2_2ndByte) { // if it's an ²
            key = 31; 
            modifiers   = CtrlAlt;
            break;
          }

          if (SecondByte == mue3_2ndByte) { // if it's an ³
            key = 32; 
            modifiers   = CtrlAlt;
            break;
          }

          if (SecondByte == degree_2ndByte) { // if it's an °
            key = 53; 
            modifiers   = Shift;
            break;
          }

          if (SecondByte == paragraph_2ndByte) { // if it's an §
            key = 32; 
            modifiers   = Shift;
            break;
          }

        case euro_1stByte: // if it's a €-symbol
          i++;
          SecondByte = text[i];
          if (SecondByte == euro_2ndByte) {
            i++;
            ThirdByte = text[i];
            if (ThirdByte == euro_3rdByte) {
              key = 8; 
              modifiers   = CtrlAlt;
              break;
            }
          }
        default:
          key = 0; 
          modifiers      = 0;
          //dbg("default", ASCII_Code);
          break;
      }
    } //if (ASCII_Code > KEY_MOD_TABLE_SIZE) multibyte representation

    else { // character has a single-byte representation
      KEY_MOD_VALUE myKeyAndMod = key_mod_values[ASCII_Code];
      modifiers = myKeyAndMod.modifier;

      key = myKeyAndMod.key; // myKeyMessage.pressedKeys[0] = myKeyAndMod.key;
    } //  else { // character has a single-byte representation

    sendKeyPress(key,modifiers);
    sendKeyRelease();  
  } //while (i < len)

} // void typeText(const char* text)

#endif

I have tested this library with an Seeeduino-XIAO which is a SAMD21 based microcontroller

best regards Stefan KeyboardGER2.zip

CLAassistant commented 2 years ago

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
0 out of 4 committers have signed the CLA.

:x: Léo Martin
:x: Annubis45
:x: martin-leo
:x: barchstien


Léo Martin seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

per1234 commented 2 years ago

Thanks for sharing your library @StefanL38.

I will suggest that you publish it to a fork in order to make it easy for everyone in the community to use and contribute to.

You can learn about that here: https://docs.github.com/en/get-started/quickstart/fork-a-repo

StefanL38 commented 2 years ago

Hi per1234,

nice gesture to invite me to "fork" the repo. Thank you for that.

I took a look into this link https://docs.github.com/en/get-started/quickstart/fork-a-repo which does NOT explain very much so I looked into https://docs.github.com/en/github/collaborating-with-pull-requests/working-with-forks

SAME thing there: NO explanation on how it is done in DETAIL with step by step screenshots

The times when I had fun learning things through a lot of try and error are definitely OVER

If the GitHUB-documenting-Team is unable to provide a STEP-BY-STEP tutorial with SCREENSHOTS how "fork" a repo I will NOT do it!

Man this really makes me upset!!! NOT everybody is a NERD which has the greatest fun on trying to find out by looking up dozens of links and asking a lot of questions in forums until he/she is able to fork a repo. GRRRRRRRRRRRRRRR!!!!!!!!!!!!!!!!!!!!!!!

If you can't post a link to such a step-by-step-screenshot tutorial it stays like it is. If you want to do me a favor forward this comment to the GitHUB-documenting-Team

PaulStoffregen commented 2 years ago

If the GitHUB-documenting-Team is unable to provide a STEP-BY-STEP tutorial with SCREENSHOTS how "fork" a repo I will NOT do it!

Man this really makes me upset!!!

Woah, no need to get so upset. Just scroll up to the top of this page and click this button.

fork

Clicking will create a copy of this repository which is owned by your github account. Because it's your copy, you will have permission to change it any way you wish. Github will know it is a fork, so when you've made changes to your copy, Github will offer you buttons to easily create a pull request to contribute your changes back to this original.

Github and the git software are powerful tools. They offer a lot of features (far more than I know) and indeed it gets very complex if you dive deep into the details. But the truth is most people use only a subset of all that stuff. Like most tools, and especially very powerful ones, some learning is involved, but that knowledge & experience is (probably) well worth investing some of your time.

StefanL38 commented 2 years ago

Hi Paul,

thank you for answering with a screenhot how to fork.

Github and the git software are powerful tools. They offer a lot of features (far more than I know) and indeed it gets very complex if you dive deep into the details. But the truth is most people use only a subset of all that stuff. Like most tools, and especially very powerful ones, some learning is involved, but that knowledge & experience is (probably) well worth investing some of your time.

Yes and it becomes even more powerful if the documenting-Team creates a step-by-step tutorial with screenshots to show each step until a pullrequest is finished. Creating this tutorial means investing time once to enable saving time for all newcomers in the future. One purpose of Github is to serve code and knowledge to the community. So it would be just consequent to serve the knowledge on how to use Github istelf in a way that makes it efficient for newcomers to learn it.

And this is with step-by-step-tutorials with screenshots. IMHO Text and Screenhots are even better than videos because it is much easier to scroll back and forth on a website than dragging back and forth the timeindicator-point of a video

best regards Stefan

StefanL38 commented 2 years ago

So in lack of such a tutorial I did some quick tries which ended in a mess. added the new library files to the old ones. hm how to delete the other files? no idea! ok delete the whole subfolder src - done how to create a new one ? no idea!

searching the screen clicked on "commit to master-branch" or something similar I guess trying to commit with the src-folder deleted is not useful. too late clicking did work.

reading this maybe makes it better understandable why a step-by-step-tutorial is needed best regards Stefan