My e-mail address is ColliM@europe.stortek.com

Folks,

I am writing a database program in C, that will be used in a critical 
situation, being updated in real time. For disaster recovery, I wrote
the code below, which causes two PCs to run the same software in parallel,
so that both PCs remain identical at all times. I have incorporated it into
my
software and tested it, and it appears to work fine. If anybody has the same
requirement, they may use it, hack it about, do whatever they want with it.
There are no guarantees with it, though, and I do not want to spend much time
supporting it.

The effect is that a PC is configured as MASTER and another is configured as
SLAVE. All keyboard characters on the Master go into the keyboard buffer as
usual, where your fantastic program goes looking for its input. If it uses
getch(), the character is input to the program, but when it uses kb_getch(),
besides going to the program, the character is also sent to the serial port.
Note that in these routines, only serial port COM-1 is used. If you don't
like
that, change the references to 0x3fa-0x3fe to something you like better.

Your fantastic program is also running on the Slave system, and just as on
any system, it goes looking in the keyboard buffer for its input. On the
Slave system, however, any character arriving at serial COM-1 port causes an
interrupt, and the interrupt routine puts the character into the keyboard
buffer, where your program finds it, just as if you had typed it there.

The routines in this source code come with a demonstration program which
will be compiled as long as TEST is defined. The demonstration program in
main() echoes any characters typed on the Master onto the screen of the
Slave, but this is not a simple echo. It is the result of the same program
running on both Master and Slave, and getting the same KEYBOARD input. The
demo program detects characters and function keys. It does not detect other 
two-character keys such as Arrow keys, although they are, in fact, sent to 
the slave system. If your fantastic program needs them, they are available.

Five routines are defined and described in kb.h (below). Read the
descriptions there.

The reason why I used the standard DOS 16-key keyboard buffer was so that I
could easily accomplish the following : The database program, for which I
wrote these shadowing routines, uses active help files, for which I use the
kb_ungetch() and kb_ungets(). For example, at a given point in the program, I
look for input. If the key pressed is A, B, C, D, F1 or F2, then the program
performs a corresponding action. If the key was F1, the action is to display
a help menu. The menu describes the action for each key other than F1, eg:

A   - Action 1
B   - Action 2
C   - Action 3
D   - Action 4
F2  - Action 5

If Escape is pressed, the menu goes away, and a goto (yes, that's right,
a GOTO!!) takes us back to the beginning of the keyboard input section,
and you can press the key of your choice. Instead of making the menu
go away, however, you can move the cursor down to the line of your choice
(let's say the description for the F2 key) and press Return to select it.
In that case, the help() routine which has just received a return from the
menu() routine, does a

kb_ungets("\0\x3d");

The "\0" tells kb_ungets that this is a function key (you could kb_ungets an
arrow key or delete key the same way). The "\x3d" is the second character
generated by the F2 key. Now when the goto is executed, the keyboard input
routine finds two characters in the keyboard buffer - a zero, saying that
the next character is the scancode of the key pressed, and a \x3d, exactly
the same as if it had been looking for keyboard input and we had pressed the
F2 key, so off it goes and does the F2 action.

Of course, if you are running two PCs in Master/Slave, then exactly the same
actions are happenning on the slave at all times.

To describe all of this in a program, see the source code below.
This is for explanation only - it will not compile and run!

my_prog()
{ char ch;

  input:
  ch = kb_getch();   // Read a character from the keyboard buffer.
                     // This may already have been put there by help()
(below),
                     // or if the keyboard buffer is empty, the program will
                     // wait for a key to be pressed.

  if(ch == '\0')     // first char is zero - this is a function key
  { ch = kb_getch(); // ... so get the second char = scan code
    if(ch == 0xc3)   // scan code for F1 key
    { help();
      goto input;
    }
    else if(ch == 0xc4) // scan code for F2 key
      action_5();
  }
  else if(ch == 'A') action_1();
  else if(ch == 'B') action_2();
  else if(ch == 'C') action_3();
  else if(ch == 'D') action_4();

  // ... other stuff
}

menu()
{ // menu routine returns the number of the option pressed, or
  // -1 (Minus 1) if ESC was pressed.
  // In this case, the following menu is presented.

  // A   - Action 1     - return = 1  if chosen
  // B   - Action 2     - return = 2      "
  // C   - Action 3     - return = 3      "
  // D   - Action 4     - return = 4      "
  // F2  - Action 5     - return = 5      "

}

void help() // puts psedo keypresses into key buffer before returning
{ menu(/* menu parameters */);
    if((choice = get_entry(result, NULL)) != -1) // if Escape not pressed
      switch(choice)
      { case 1:   /* 'A' was pressed */
        ungets("A");  // this character goes into the keybd buffer, where
        break;        // it will be found by input: routine in my_prog()

        case 2:   /* 'B' was pressed */
        ungets("B");
        break;
        case 3:   /* 'C' was pressed */
        ungets("C");
        break;
        case 4:   /* 'D' was pressed */
        ungets("D");
        break;
        case 5:   /* 'F2' was pressed */
        ungets("\x0\x3d"); // two character string. 0= first char of function
                           // key; \x3d= second character of F2 key.
        break;
      }
}

action_1()
{ // code for action 1
}

etc. for other "action_n()" routines

--------------------------------------

Prerequisites to using these routines are :
1) Your program must be in 32-bit protected mode C, compilable with DJGPP.
2) All keyboard input must be done via getch(). If you use scanf(), for
   example, you will have some work to do before you can use these routines.

Note that the "kb_" prefix (used below) stands for "keyboard_"

How to use these routines :

1) #include kb.h // in your program.c file
2) In the initiallisation phase of your program, insert the line
SetupInt();
3) Throughout your program, change all your getch() and ungetch() statements
   to kb_getch() and kb_ungetch(). This can be done with a couple of #defines
4) Put kb.c into your project.
5) Compile & Link.
6) Copy your program.exe file to the slave system.
7) Connect a serial cable (pins 2, 3 and 7) between the two machines' COM-1.
8) Run the program on both machines.
9) Configure the Master by pressing (Alt-Ctl-M).
10) Configure the Slave by pressing (Alt-Ctl-S).
11) Type on the Master only. Anything typed on the Slave will go only to the
    Slave, so the two programs may get out of step. You may wish to disable
    direct keyboard input (except for Alt_Ctl_1) on the slave.
12) A machine may be configured to Standalone by pressing Alt-Ctl-1.
    kb_getch() will then work the same as getch() would have.



