Test whether seq is a finished escape sequence.
complete false means “could still become one, keep accumulating”.
complete true with KEY_NONE means “definitely not one” and the
bytes are discarded, which is how an unknown sequence resynchronises.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=*), | intent(in) | :: | seq |
Bytes accumulated so far, starting with ESC. |
||
| logical, | intent(out) | :: | complete |
Whether a decision has been reached. |
||
| type(key_event_t), | intent(out) | :: | event |
The decoded key, or |
||
| integer(kind=default_int), | intent(out) | :: | consumed |
How many leading bytes of |
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| integer(kind=default_int), | private | :: | n |
pure subroutine match_sequence(seq, complete, event, consumed) !! Test whether `seq` is a finished escape sequence. !! !! `complete` false means "could still become one, keep accumulating". !! `complete` true with `KEY_NONE` means "definitely not one" and the !! bytes are discarded, which is how an unknown sequence resynchronises. character(len=*), intent(in) :: seq !! Bytes accumulated so far, starting with ESC. logical, intent(out) :: complete !! Whether a decision has been reached. type(key_event_t), intent(out) :: event !! The decoded key, or `KEY_NONE` if the sequence is unknown. integer(default_int), intent(out) :: consumed !! How many leading bytes of `seq` the decision used. integer(default_int) :: n complete = .false. event%code = KEY_NONE event%char_code = 0_int32 consumed = len(seq, kind=default_int) n = len(seq, kind=default_int) if (n == 1_default_int) return ! just ESC so far ! `ESC [` and `ESC O` both introduce the sequences below; xterm emits ! the first in normal mode and the second in application cursor mode, ! and a program that handles only one loses the arrow keys in the other. if (seq(2:2) /= "[" .and. seq(2:2) /= "O") then ! Nothing can continue from here, so the ESC was the Escape key. Only ! the ESC is consumed: the byte after it has not been classified yet ! and may itself start a sequence, as it does when the user presses ! Escape and then an arrow key inside one read. complete = .true. event%code = KEY_ESC consumed = 1_default_int return end if if (n == 2_default_int) return select case (seq(3:3)) case ("A") event%code = KEY_UP case ("B") event%code = KEY_DOWN case ("C") event%code = KEY_RIGHT case ("D") event%code = KEY_LEFT case ("H") event%code = KEY_HOME case ("F") event%code = KEY_END case ("1", "3", "4", "7", "8") ! numeric forms: ESC [ 3 ~ and friends, which need the tilde if (n == 3_default_int) return if (seq(4:4) /= "~") then complete = .true. return end if select case (seq(3:3)) case ("1", "7") event%code = KEY_HOME case ("4", "8") event%code = KEY_END case ("3") event%code = KEY_DELETE case default ! Unreachable: the outer select already narrowed seq(3:3) to these ! five. Spelled out so the reader, and the linter, do not have to ! hold the outer case list in mind to see that it is total. event%code = KEY_NONE end select complete = .true. consumed = 4_default_int return case default complete = .true. ! unknown; drop it return end select complete = .true. consumed = 3_default_int end subroutine match_sequence