Continuously sampling the keyboard matrix. Effectively undersampling with respect to most key bouncing but fast enough to detect changes originating from human interaction. Storing (shifting in) the sampling results in a bit-array, one per key. The lengths of such a bit-array defines the longest key-press history available for a key. Then checking for a continuous sequence of ones (indicating a long key press) in such an array, starting from what constitutes "now" or "latest" in the array, back to whatever would be the key press duration for which I want to check.
E.g. sampling with 50 ms requires 20 bits (make it 24 bits = 3 bytes, or a single 32 bit variable) per key to keep a > 1 second history for a key. A simple test
if((value & 0x000FFFFF) == 0x000FFFFF) ...
then tells me if a button has been pressed for at least 1s.
For larger durations I accumulate e.g. 1s press-durations in a separate counting byte.