match_sequence Subroutine

private 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.

Arguments

Type IntentOptional 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 KEY_NONE if the sequence is unknown.

integer(kind=default_int), intent(out) :: consumed

How many leading bytes of seq the decision used.


Called by

proc~~match_sequence~~CalledByGraph proc~match_sequence match_sequence proc~decode_keys decode_keys proc~decode_keys->proc~match_sequence

Variables

Type Visibility Attributes Name Initial
integer(kind=default_int), private :: n

Source Code

   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