(********************************************************************)
(*                                                                  *)
(*  gkbd.sd7      Keyboard test program for graphic keyboard        *)
(*  Copyright (C) 2004, 2011 - 2013, 2020, 2021  Thomas Mertes      *)
(*                                                                  *)
(*  This program is free software; you can redistribute it and/or   *)
(*  modify it under the terms of the GNU General Public License as  *)
(*  published by the Free Software Foundation; either version 2 of  *)
(*  the License, or (at your option) any later version.             *)
(*                                                                  *)
(*  This program 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 General Public License for more details.                    *)
(*                                                                  *)
(*  You should have received a copy of the GNU General Public       *)
(*  License along with this program; if not, write to the           *)
(*  Free Software Foundation, Inc., 51 Franklin Street,             *)
(*  Fifth Floor, Boston, MA  02110-1301, USA.                       *)
(*                                                                  *)
(********************************************************************)


$ include "seed7_05.s7i";
  include "keybd.s7i";
  include "keydescr.s7i";
  include "float.s7i";
  include "draw.s7i";
  include "time.s7i";
  include "duration.s7i";
  include "console.s7i";
  include "stdfont12.s7i";
  include "window.s7i";
  include "tee.s7i";


const integer: xSpeed is 1;

const array [boolean] color: buttonColor is [boolean] (light_blue, light_red);

const type: keyActiveHashType is hash[char] boolean;

const set of char: mouseKeys is
    {KEY_MOUSE1, KEY_MOUSE2, KEY_MOUSE3, KEY_MOUSE4, KEY_MOUSE5, KEY_MOUSE_FWD, KEY_MOUSE_BACK,
     KEY_SFT_MOUSE1, KEY_SFT_MOUSE2, KEY_SFT_MOUSE3, KEY_SFT_MOUSE4, KEY_SFT_MOUSE5, KEY_SFT_MOUSE_FWD, KEY_SFT_MOUSE_BACK,
     KEY_CTL_MOUSE1, KEY_CTL_MOUSE2, KEY_CTL_MOUSE3, KEY_CTL_MOUSE4, KEY_CTL_MOUSE5, KEY_CTL_MOUSE_FWD, KEY_CTL_MOUSE_BACK,
     KEY_ALT_MOUSE1, KEY_ALT_MOUSE2, KEY_ALT_MOUSE3, KEY_ALT_MOUSE4, KEY_ALT_MOUSE5, KEY_ALT_MOUSE_FWD, KEY_ALT_MOUSE_BACK};


const proc: displayKey (in char: ch, in boolean: keyActive) is func
  local
    var color: keyColor is color.value;
  begin
    keyColor := buttonColor[keyActive];
    case ch of
      when {KEY_SHIFT_LOCK}:     rect( 10, 150, 40, 20, keyColor);
      when {KEY_LEFT_SHIFT}:     rect( 10, 180, 50, 20, keyColor);
      when {KEY_LEFT_CONTROL}:   rect( 10, 210, 30, 20, keyColor);
      when {KEY_LEFT_SUPER}:     rect( 50, 210, 30, 20, keyColor);
      when {KEY_LEFT_ALT}:       rect( 90, 210, 30, 20, keyColor);
      when {KEY_RIGHT_SHIFT}:    rect(390, 180, 50, 20, keyColor);
      when {KEY_RIGHT_CONTROL}:  rect(410, 210, 30, 20, keyColor);
      when {KEY_RIGHT_SUPER}:    rect(370, 210, 30, 20, keyColor);
      when {KEY_RIGHT_ALT}:      rect(330, 210, 30, 20, keyColor);
      when {KEY_SHIFT}:          rect( 10, 250, 10, 10, keyColor);
      when {KEY_CONTROL}:        rect( 30, 250, 10, 10, keyColor);
      when {KEY_SUPER}:          rect( 50, 250, 10, 10, keyColor);
      when {KEY_ALT}:            rect( 70, 250, 10, 10, keyColor);
      when {KEY_NUM_LOCK_ON}:    rect(580,  30, 10, 10, keyColor);
      when {KEY_SHIFT_LOCK_ON}:  rect(600,  30, 10, 10, keyColor);
      when {KEY_SCROLL_LOCK_ON}: rect(620,  30, 10, 10, keyColor);

      when {KEY_ESC}:            rect( 10,  50, 20, 20, keyColor);
      when {KEY_F1}:             rect( 70,  50, 20, 20, keyColor);
      when {KEY_F2}:             rect(100,  50, 20, 20, keyColor);
      when {KEY_F3}:             rect(130,  50, 20, 20, keyColor);
      when {KEY_F4}:             rect(160,  50, 20, 20, keyColor);
      when {KEY_F5}:             rect(200,  50, 20, 20, keyColor);
      when {KEY_F6}:             rect(230,  50, 20, 20, keyColor);
      when {KEY_F7}:             rect(260,  50, 20, 20, keyColor);
      when {KEY_F8}:             rect(290,  50, 20, 20, keyColor);
      when {KEY_F9}:             rect(330,  50, 20, 20, keyColor);
      when {KEY_F10}:            rect(360,  50, 20, 20, keyColor);
      when {KEY_F11}:            rect(390,  50, 20, 20, keyColor);
      when {KEY_F12}:            rect(420,  50, 20, 20, keyColor);
      when {KEY_SCROLL_LOCK}:    rect(485,  50, 20, 20, keyColor);
      when {KEY_PAUSE}:          rect(515,  50, 20, 20, keyColor);

      when {'1'}:                rect( 40,  90, 20, 20, keyColor);
      when {'2'}:                rect( 70,  90, 20, 20, keyColor);
      when {'3'}:                rect(100,  90, 20, 20, keyColor);
      when {'4'}:                rect(130,  90, 20, 20, keyColor);
      when {'5'}:                rect(160,  90, 20, 20, keyColor);
      when {'6'}:                rect(190,  90, 20, 20, keyColor);
      when {'7'}:                rect(220,  90, 20, 20, keyColor);
      when {'8'}:                rect(250,  90, 20, 20, keyColor);
      when {'9'}:                rect(280,  90, 20, 20, keyColor);
      when {'0'}:                rect(310,  90, 20, 20, keyColor);
      when {KEY_BS}:             rect(400,  90, 40, 20, keyColor);

      when {KEY_TAB}:            rect( 10, 120, 35, 20, keyColor);
      when {'Q'}:                rect( 55, 120, 20, 20, keyColor);
      when {'W'}:                rect( 85, 120, 20, 20, keyColor);
      when {'E'}:                rect(115, 120, 20, 20, keyColor);
      when {'R'}:                rect(145, 120, 20, 20, keyColor);
      when {'T'}:                rect(175, 120, 20, 20, keyColor);
      when {'Y'}:                rect(205, 120, 20, 20, keyColor);
      when {'U'}:                rect(235, 120, 20, 20, keyColor);
      when {'I'}:                rect(265, 120, 20, 20, keyColor);
      when {'O'}:                rect(295, 120, 20, 20, keyColor);
      when {'P'}:                rect(325, 120, 20, 20, keyColor);

      when {'A'}:                rect( 65, 150, 20, 20, keyColor);
      when {'S'}:                rect( 95, 150, 20, 20, keyColor);
      when {'D'}:                rect(125, 150, 20, 20, keyColor);
      when {'F'}:                rect(155, 150, 20, 20, keyColor);
      when {'G'}:                rect(185, 150, 20, 20, keyColor);
      when {'H'}:                rect(215, 150, 20, 20, keyColor);
      when {'J'}:                rect(245, 150, 20, 20, keyColor);
      when {'K'}:                rect(275, 150, 20, 20, keyColor);
      when {'L'}:                rect(305, 150, 20, 20, keyColor);
      when {KEY_NL}:             rect(400, 150, 40, 20, keyColor);

      when {'Z'}:                rect( 80, 180, 20, 20, keyColor);
      when {'X'}:                rect(110, 180, 20, 20, keyColor);
      when {'C'}:                rect(140, 180, 20, 20, keyColor);
      when {'V'}:                rect(170, 180, 20, 20, keyColor);
      when {'B'}:                rect(200, 180, 20, 20, keyColor);
      when {'N'}:                rect(230, 180, 20, 20, keyColor);
      when {'M'}:                rect(260, 180, 20, 20, keyColor);

      when {' '}:                rect(145, 210, 160, 20, keyColor);

      when {KEY_NUM_LOCK}:       rect(550,  90, 20, 20, keyColor);
      when {KEY_HOME}:           rect(550, 120, 20, 20, keyColor);
                                 rect(485,  90, 20, 20, keyColor);
      when {KEY_UP}:             rect(580, 120, 20, 20, keyColor);
                                 rect(485, 180, 20, 20, keyColor);
      when {KEY_PGUP}:           rect(610, 120, 20, 20, keyColor);
                                 rect(515,  90, 20, 20, keyColor);
      when {KEY_LEFT}:           rect(550, 150, 20, 20, keyColor);
                                 rect(455, 210, 20, 20, keyColor);
      when {KEY_PAD_CENTER}:     rect(580, 150, 20, 20, keyColor);
      when {KEY_RIGHT}:          rect(610, 150, 20, 20, keyColor);
                                 rect(515, 210, 20, 20, keyColor);
      when {KEY_END}:            rect(550, 180, 20, 20, keyColor);
                                 rect(485, 120, 20, 20, keyColor);
      when {KEY_DOWN}:           rect(580, 180, 20, 20, keyColor);
                                 rect(485, 210, 20, 20, keyColor);
      when {KEY_PGDN}:           rect(610, 180, 20, 20, keyColor);
                                 rect(515, 120, 20, 20, keyColor);
      when {KEY_INS}:            rect(550, 210, 50, 20, keyColor);
                                 rect(455,  90, 20, 20, keyColor);
      when {KEY_DEL}:            rect(610, 210, 20, 20, keyColor);
                                 rect(455, 120, 20, 20, keyColor);

      when {KEY_MOUSE_FWD}:      rect(490, 240, 20, 20, keyColor);
      when {KEY_MOUSE_BACK}:     rect(520, 240, 20, 20, keyColor);
      when {KEY_MOUSE1}:         rect(550, 240, 20, 20, keyColor);
      when {KEY_MOUSE2}:         rect(580, 240, 20, 20, keyColor);
      when {KEY_MOUSE3}:         rect(610, 240, 20, 20, keyColor);
    end case;
  end func;


const proc: checkKey (inout keyActiveHashType: keyActive, in char: ch) is func
  local
    var boolean: buttonPressed is FALSE;
  begin
    buttonPressed := buttonPressed(KEYBOARD, ch);
    if ch not in keyActive or keyActive[ch] <> buttonPressed then
      keyActive @:= [ch] buttonPressed;
      displayKey(ch, buttonPressed);
    end if;
  end func;


const proc: main is func
  local
    var text: screen is text.value;
    var file: logFile is STD_NULL;
    var keyActiveHashType: keyActive is keyActiveHashType.value;
    var char: ch is ' ';
    var integer: xPos is 10;
    var integer: xIncr is xSpeed;
    var time: waitTime is time.value;
  begin
    screen(640, 480);
    # Enable the program to get KEY_CLOSE without closing the window.
    selectInput(curr_win, KEY_CLOSE, TRUE);
    # Enable the program to get KEY_RESIZE.
    selectInput(curr_win, KEY_RESIZE, TRUE);
    screen := openPixmapFontFile(curr_win, 10, 26);
    setFont(screen, stdFont12);
    writeln(screen, "Keyboard test program. Type ! to terminate the program");
    screen := openPixmapFontFile(curr_win, 20, 270);
    setFont(screen, stdFont12);
    OUT := openWindow(screen, 1, 1, 11, 500);
    logFile := STD_CONSOLE;
    OUT := openTee(OUT, logFile);
    KEYBOARD := GRAPH_KEYBOARD;
    flushGraphic;
    repeat
      waitTime := time(NOW) + 30000 . MICRO_SECONDS;
      while inputReady(KEYBOARD) do
        ch := getc(KEYBOARD);
        write(ord(ch) <& " ");
        if ch in keyDescription then
          if ch = KEY_RESIZE then
            writeln("KEY_RESIZE (" <& width(curr_win) <& ", " <& height(curr_win) <& ")");
          elsif ch in mouseKeys then
            writeln(keyDescription[ch] <& " (" <& clickedXPos(KEYBOARD) <&
                    ", " <& clickedYPos(KEYBOARD) <& ")");
          else
            writeln(keyDescription[ch]);
          end if;
        elsif ch >= ' ' and ch <= '~' or
            ch >= '\160;' and ch <= char.last then
          writeln(ch);
        else
          writeln;
        end if;
      end while;
      checkKey(keyActive, KEY_SHIFT_LOCK);
      checkKey(keyActive, KEY_LEFT_SHIFT);
      checkKey(keyActive, KEY_LEFT_CONTROL);
      checkKey(keyActive, KEY_LEFT_SUPER);
      checkKey(keyActive, KEY_LEFT_ALT);
      checkKey(keyActive, KEY_RIGHT_SHIFT);
      checkKey(keyActive, KEY_RIGHT_CONTROL);
      checkKey(keyActive, KEY_RIGHT_SUPER);
      checkKey(keyActive, KEY_RIGHT_ALT);
      checkKey(keyActive, KEY_SHIFT);
      checkKey(keyActive, KEY_CONTROL);
      checkKey(keyActive, KEY_SUPER);
      checkKey(keyActive, KEY_ALT);
      checkKey(keyActive, KEY_NUM_LOCK_ON);
      checkKey(keyActive, KEY_SHIFT_LOCK_ON);
      checkKey(keyActive, KEY_SCROLL_LOCK_ON);

      checkKey(keyActive, KEY_ESC);
      checkKey(keyActive, KEY_F1);
      checkKey(keyActive, KEY_F2);
      checkKey(keyActive, KEY_F3);
      checkKey(keyActive, KEY_F4);
      checkKey(keyActive, KEY_F5);
      checkKey(keyActive, KEY_F6);
      checkKey(keyActive, KEY_F7);
      checkKey(keyActive, KEY_F8);
      checkKey(keyActive, KEY_F9);
      checkKey(keyActive, KEY_F10);
      checkKey(keyActive, KEY_F11);
      checkKey(keyActive, KEY_F12);
      checkKey(keyActive, KEY_SCROLL_LOCK);
      checkKey(keyActive, KEY_PAUSE);

      checkKey(keyActive, '1');
      checkKey(keyActive, '2');
      checkKey(keyActive, '3');
      checkKey(keyActive, '4');
      checkKey(keyActive, '5');
      checkKey(keyActive, '6');
      checkKey(keyActive, '7');
      checkKey(keyActive, '8');
      checkKey(keyActive, '9');
      checkKey(keyActive, '0');
      checkKey(keyActive, KEY_BS);

      checkKey(keyActive, KEY_TAB);
      checkKey(keyActive, 'Q');
      checkKey(keyActive, 'W');
      checkKey(keyActive, 'E');
      checkKey(keyActive, 'R');
      checkKey(keyActive, 'T');
      checkKey(keyActive, 'Y');
      checkKey(keyActive, 'U');
      checkKey(keyActive, 'I');
      checkKey(keyActive, 'O');
      checkKey(keyActive, 'P');

      checkKey(keyActive, 'A');
      checkKey(keyActive, 'S');
      checkKey(keyActive, 'D');
      checkKey(keyActive, 'F');
      checkKey(keyActive, 'G');
      checkKey(keyActive, 'H');
      checkKey(keyActive, 'J');
      checkKey(keyActive, 'K');
      checkKey(keyActive, 'L');
      checkKey(keyActive, KEY_NL);

      checkKey(keyActive, 'Z');
      checkKey(keyActive, 'X');
      checkKey(keyActive, 'C');
      checkKey(keyActive, 'V');
      checkKey(keyActive, 'B');
      checkKey(keyActive, 'N');
      checkKey(keyActive, 'M');

      checkKey(keyActive, ' ');

      checkKey(keyActive, KEY_NUM_LOCK);
      checkKey(keyActive, KEY_HOME);
      checkKey(keyActive, KEY_UP);
      checkKey(keyActive, KEY_PGUP);
      checkKey(keyActive, KEY_LEFT);
      checkKey(keyActive, KEY_PAD_CENTER);
      checkKey(keyActive, KEY_RIGHT);
      checkKey(keyActive, KEY_END);
      checkKey(keyActive, KEY_DOWN);
      checkKey(keyActive, KEY_PGDN);
      checkKey(keyActive, KEY_INS);
      checkKey(keyActive, KEY_DEL);

      checkKey(keyActive, KEY_MOUSE_FWD);
      checkKey(keyActive, KEY_MOUSE_BACK);
      checkKey(keyActive, KEY_MOUSE1);
      checkKey(keyActive, KEY_MOUSE2);
      checkKey(keyActive, KEY_MOUSE3);

      if xPos > 620 then
        xIncr := -xSpeed;
      elsif xPos < 10 then
        xIncr := xSpeed;
      end if;
      if xIncr > 0 then
        rect(xPos, 10, xIncr, 10, black);
      else # xIncr < 0
        rect(xPos + 10 + xIncr, 10, -xIncr, 10, black);
      end if;
      xPos +:= xIncr;
      rect(xPos, 10, 10, 10, buttonColor[buttonPressed(KEYBOARD, ch)]);
      flushGraphic;
      await(waitTime);
    until ch = '!';
  end func;