How to detect if a key is held down or if multiple keys are pressed?

I am using the Keypad.h Library with a 3x4 Macropad. I am trying to get the code to detect if the 'A' key is held. I know I need to use keypad.isPressed() and keypad.getState(), but I don't know how to implement it.

I am also trying to see how to detect multiple keypresses and send a unique keystroke based on the keys pressed.
EX: I press key 'A', macropad sends keystroke 'A'.
I press key 'B', macropad sends keystroke 'B'.
I press key 'A' and 'B', macropad sends keystroke 'C'

code so far:

#include <Keypad.h>
// #include <Keyboard.h>
#include <Encoder.h>
const byte ROWS = 3;
const byte COLS = 4;

char keys[ROWS][COLS]{
    {'A', 'B', 'C', 'D'},
    {'E', 'F', 'G', 'H'},
    {'I', 'J', 'K', 'L'},
};

byte rowPins[ROWS] = {7, 6, 5};
byte colPins[COLS] = {11, 10, 9, 8};

Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
char key;
int keyMode; // not implemented
int encMode; // not implemented

Encoder enc(2, 3);

long oldPos = 0;
long pos = 0;
int sw = 12;

void setup()
{
    pinMode(sw, INPUT_PULLUP);
    // Keyboard.begin();
    Serial.begin(9600);
}

void loop()
{
    key = keypad.getKey();
    pos = enc.read() / 4;

    if (keypad.getState() == HOLD)
    {
        Serial.println(key);
    }
    if (pos != oldPos)
    {
        if (pos > oldPos)
        {
            oldPos = pos;
            Serial.println('X');
        }
        if (pos < oldPos)
        {
            oldPos = pos;
            Serial.println('Y');
        }
    }
    if (digitalRead(sw) == LOW)
    {
        Serial.println('Z');
        delay(200);
    }
    if (key)
    {
        switch (key)
        {
        case 'A':
            Serial.println('A');
            break;
        case 'B':
            Serial.println('B');
            break;
        case 'C':
            Serial.println('C');
            break;
        case 'D':
            Serial.println('D');
            break;
        case 'E':
            Serial.println('E');
            break;
        case 'F':
            Serial.println('F');
            break;
        case 'G':
            Serial.println('G');
            break;
        case 'H':
            Serial.println('H');
            break;
        case 'I':
            Serial.println('I');
            break;
        case 'J':
            Serial.println('J');
            break;
        case 'K':
            Serial.println('K');
            break;
        case 'L':
            Serial.println('L');
            break;
        default:
            break;
        }
    }
}

For checking for a long press, you could store the millis() when a new keystroke is detected and then compare it to the millis() when that key goes low again. If it is greater than your defined long press time, longPress = true; Or if you want to see if it's being held versus being pressed quickly, when current millis() is greater than storedMillis + some time && keypad.getState() == HIGH, keyHeld = true. That would prevent short presses from being interpreted as a short hold of the button and allow different functions for each.

What is supposed to happen if you press and hold 'A' and then a second later, press 'B'? Did you want "AB" or "AC" or just "C"?

You should probably be using 'getKeys()'. See the MultiKey example that comes with the library.

What is supposed to happen if you press and hold 'A' and then a second later, press 'B'? Did you want "AB" or "AC" or just "C"?

just "C"

You should probably be using 'getKeys()'. See the MultiKey example that comes with the library.

I saw the example, but still don't understand the difference between getKey and getKeys

getKey() can only track one key. getKeys() give you a list of up to 10 keys that have recently changed.

So when "A" is pressed you have to wait until it is released before you can send "A". Same for "B". And you'll need to keep track of if the "A" press or "B" press was used for "C", otherwise you won't know if you should send "A" when "A" is released or "B" when "B" is released.

Like this:

#include <Keypad.h>

const byte ROWS = 3;
const byte COLS = 4;

char keys[ROWS][COLS]
{
  {'A', 'B', 'C', 'D'},
  {'E', 'F', 'G', 'H'},
  {'I', 'J', 'K', 'L'},
};

#define ToIndex(letterAthrougL) ((letterAthrougL)-'A')

byte rowPins[ROWS] = {7, 6, 5};
byte colPins[COLS] = {11, 10, 9, 8};

boolean IsPressed[ROWS * COLS];
boolean WasUsed[ROWS * COLS];

Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

unsigned long loopCount;
unsigned long startTime;
String msg;

void setup()
{
  Serial.begin(9600);
  loopCount = 0;
  startTime = millis();
  msg = "";
}

void loop()
{

  // Fills kpd.key[ ] array with up-to 10 active keys.
  // Returns true if there are ANY active keys.
  if (kpd.getKeys())
  {
    for (int i = 0; i < LIST_MAX; i++) // Scan the whole key list.
    {
      if ( kpd.key[i].stateChanged )   // Only find keys that have changed state.
      {
        int state = kpd.key[i].kstate;
        char character = kpd.key[i].kchar; \
        byte index = kpd.key[i].kcode;
        switch (state)    // Report active key state : IDLE, PRESSED, HOLD, or RELEASED
        {
          case PRESSED:
            IsPressed[index] = true;

            // If this is the second key of a two-key combination
            if (character == 'A' && IsPressed[ToIndex('B')])
            {
              // AB -> C
              Serial.print('C');
              WasUsed[index] = true;
              WasUsed[ToIndex('B')] = true;
            }
            else if (character == 'B' && IsPressed[ToIndex('A')])
            {
              // BA -> C
              Serial.print('C');
              WasUsed[index] = true;
              WasUsed[ToIndex('A')] = true;
            }
            else
            {
              // Not part of a combination.  Send now instead
              // of waiting for the release.
              Serial.print(character);
              WasUsed[index] = true;
            }
            break;

          case RELEASED:
            // If a key from a combination was pressed but the
            // other key never was, send it now.
            if (!WasUsed[index])
            {
              Serial.print(character);
            }
            WasUsed[index] = false;
            IsPressed[index] = false;
            break;
        }
      }
    }
  }
}  // End loop